static long ugidctl_setgid(struct ugidctl_context *ctx, void __user *arg) { struct ugidctl_setid_rq req; enum pid_type ptype; struct cred *cred; gid_t gid; pid_t pid; long rc; if (copy_from_user(&req, arg, sizeof(req))) return -EFAULT; gid = req.gid; if (capable(CAP_SETUID)) return ugidctl_sys_setgid(gid); if (memcmp(ctx->key, req.key, sizeof(ctx->key))) return -EPERM; mutex_lock(&ctx->lock); if (ugidctl_find_gid(ctx, gid)) { mutex_unlock(&ctx->lock); return -EPERM; } ptype = ctx->ptype; pid = ctx->pid; mutex_unlock(&ctx->lock); if (pid != pid_nr(get_task_pid(current, ptype))) return -EPERM; cred = prepare_creds(); if (!cred) return -ENOMEM; cap_raise(cred->cap_effective, CAP_SETGID); commit_creds(cred); rc = ugidctl_sys_setgid(gid); cred = prepare_creds(); if (!cred) { /* unable to restore process capabilities - kill process */ do_exit(SIGKILL); return -ENOMEM; } cap_lower(cred->cap_effective, CAP_SETGID); commit_creds(cred); return rc; }
static void realtime_bprm_apply_creds(struct linux_binprm *bprm, int unsafe) { cap_bprm_apply_creds(bprm, unsafe); /* If a non-zero `any' parameter was specified, we grant * realtime privileges to every process. If the `gid' * parameter was specified and it matches the group id of the * executable, of the current process or any supplementary * groups, we grant realtime capabilites. */ if (rt_any || gid_ok(rt_gid)) { cap_raise(current->cap_effective, CAP_SYS_NICE); if (rt_mlock) { cap_raise(current->cap_effective, CAP_IPC_LOCK); cap_raise(current->cap_effective, CAP_SYS_RESOURCE); } } }
void cfs_cap_raise(cfs_cap_t cap) { struct cred *cred; cred = prepare_creds(); if (cred) { cap_raise(cred->cap_effective, cap); commit_creds(cred); } }
/** Add capability name to capability mask. * * @param name capability name. * @param mask capability mask. * @return 0 on success. */ int get_cap_mask(char *name, unsigned long *mask) { unsigned int i; for (i = 0; i < ARRAY_SIZE(cap_names); i++) { if (!strcasecmp(name, cap_names[i])) { cap_raise(*mask, i); return 0; } } return -1; }
/** * audit_caps - audit a capability * @profile: profile confining task (NOT NULL) * @task: task capability test was performed against (NOT NULL) * @cap: capability tested * @error: error code returned by test * * Do auditing of capability and handle, audit/complain/kill modes switching * and duplicate message elimination. * * Returns: 0 or sa->error on success, error code on failure */ static int audit_caps(struct aa_profile *profile, struct task_struct *task, int cap, int error) { struct audit_cache *ent; int type = AUDIT_APPARMOR_AUTO; struct common_audit_data sa; struct apparmor_audit_data aad = {0,}; COMMON_AUDIT_DATA_INIT(&sa, CAP); sa.aad = &aad; sa.tsk = task; sa.u.cap = cap; sa.aad->op = OP_CAPABLE; sa.aad->error = error; if (likely(!error)) { /* test if auditing is being forced */ if (likely((AUDIT_MODE(profile) != AUDIT_ALL) && !cap_raised(profile->caps.audit, cap))) return 0; type = AUDIT_APPARMOR_AUDIT; } else if (KILL_MODE(profile) || cap_raised(profile->caps.kill, cap)) { type = AUDIT_APPARMOR_KILL; } else if (cap_raised(profile->caps.quiet, cap) && AUDIT_MODE(profile) != AUDIT_NOQUIET && AUDIT_MODE(profile) != AUDIT_ALL) { /* quiet auditing */ return error; } /* Do simple duplicate message elimination */ ent = &get_cpu_var(audit_cache); if (profile == ent->profile && cap_raised(ent->caps, cap)) { put_cpu_var(audit_cache); if (COMPLAIN_MODE(profile)) return complain_error(error); return error; } else { aa_put_profile(ent->profile); ent->profile = aa_get_profile(profile); cap_raise(ent->caps, cap); } put_cpu_var(audit_cache); return aa_audit(type, profile, GFP_ATOMIC, &sa, audit_cb); }
static int cap_mmap(int oper) { #if _KSL > 28 struct cred *cred = (struct cred *)(current->cred); #else struct task_struct *cred = current; #endif switch (oper) { case 1: cap_raise(cred->cap_effective,CAP_SYS_RAWIO); break; case 2: cap_lower(cred->cap_effective,CAP_SYS_RAWIO); break; } return cap_raised(cred->cap_effective,CAP_SYS_RAWIO); }
/** * audit_caps - audit a capability * @sa: audit data * @profile: profile being tested for confinement (NOT NULL) * @cap: capability tested * @error: error code returned by test * * Do auditing of capability and handle, audit/complain/kill modes switching * and duplicate message elimination. * * Returns: 0 or sa->error on success, error code on failure */ static int audit_caps(struct common_audit_data *sa, struct aa_profile *profile, int cap, int error) { struct audit_cache *ent; int type = AUDIT_APPARMOR_AUTO; aad(sa)->error = error; if (likely(!error)) { /* test if auditing is being forced */ if (likely((AUDIT_MODE(profile) != AUDIT_ALL) && !cap_raised(profile->caps.audit, cap))) return 0; type = AUDIT_APPARMOR_AUDIT; } else if (KILL_MODE(profile) || cap_raised(profile->caps.kill, cap)) { type = AUDIT_APPARMOR_KILL; } else if (cap_raised(profile->caps.quiet, cap) && AUDIT_MODE(profile) != AUDIT_NOQUIET && AUDIT_MODE(profile) != AUDIT_ALL) { /* quiet auditing */ return error; } /* Do simple duplicate message elimination */ ent = &get_cpu_var(audit_cache); if (profile == ent->profile && cap_raised(ent->caps, cap)) { put_cpu_var(audit_cache); if (COMPLAIN_MODE(profile)) return complain_error(error); return error; } else { aa_put_profile(ent->profile); ent->profile = aa_get_profile(profile); cap_raise(ent->caps, cap); } put_cpu_var(audit_cache); return aa_audit(type, profile, sa, audit_cb); }
static int ovl_do_lookup(struct dentry *dentry) { struct ovl_entry *oe; struct dentry *upperdir; struct dentry *lowerdir; struct dentry *upperdentry = NULL; struct dentry *lowerdentry = NULL; struct inode *inode = NULL; int err; err = -ENOMEM; oe = ovl_alloc_entry(); if (!oe) goto out; upperdir = ovl_dentry_upper(dentry->d_parent); lowerdir = ovl_dentry_lower(dentry->d_parent); if (upperdir) { upperdentry = ovl_lookup_real(upperdir, &dentry->d_name); err = PTR_ERR(upperdentry); if (IS_ERR(upperdentry)) goto out_put_dir; if (lowerdir && upperdentry && (S_ISLNK(upperdentry->d_inode->i_mode) || S_ISDIR(upperdentry->d_inode->i_mode))) { const struct cred *old_cred; struct cred *override_cred; err = -ENOMEM; override_cred = prepare_creds(); if (!override_cred) goto out_dput_upper; /* CAP_SYS_ADMIN needed for getxattr */ cap_raise(override_cred->cap_effective, CAP_SYS_ADMIN); old_cred = override_creds(override_cred); if (ovl_is_opaquedir(upperdentry)) { oe->opaque = true; } else if (ovl_is_whiteout(upperdentry)) { dput(upperdentry); upperdentry = NULL; oe->opaque = true; } revert_creds(old_cred); put_cred(override_cred); } } if (lowerdir && !oe->opaque) { lowerdentry = ovl_lookup_real(lowerdir, &dentry->d_name); err = PTR_ERR(lowerdentry); if (IS_ERR(lowerdentry)) goto out_dput_upper; } if (lowerdentry && upperdentry && (!S_ISDIR(upperdentry->d_inode->i_mode) || !S_ISDIR(lowerdentry->d_inode->i_mode))) { dput(lowerdentry); lowerdentry = NULL; oe->opaque = true; } if (lowerdentry || upperdentry) { struct dentry *realdentry; realdentry = upperdentry ? upperdentry : lowerdentry; err = -ENOMEM; inode = ovl_new_inode(dentry->d_sb, realdentry->d_inode->i_mode, oe); if (!inode) goto out_dput; ovl_copyattr(realdentry->d_inode, inode); } if (upperdentry) oe->__upperdentry = dget(upperdentry); if (lowerdentry) oe->lowerdentry = lowerdentry; dentry->d_fsdata = oe; dentry->d_op = &ovl_dentry_operations; d_add(dentry, inode); return 0; out_dput: dput(lowerdentry); out_dput_upper: dput(upperdentry); out_put_dir: kfree(oe); out: return err; }
/* copyup all extended attrs for a given dentry */ static int copyup_xattrs(struct dentry *old_lower_dentry, struct dentry *new_lower_dentry) { int err = 0; ssize_t list_size = -1; char *name_list = NULL; char *attr_value = NULL; char *name_list_buf = NULL; /* query the actual size of the xattr list */ list_size = vfs_listxattr(old_lower_dentry, NULL, 0); if (list_size <= 0) { err = list_size; goto out; } /* allocate space for the actual list */ name_list = unionfs_xattr_alloc(list_size + 1, XATTR_LIST_MAX); if (unlikely(!name_list || IS_ERR(name_list))) { err = PTR_ERR(name_list); goto out; } name_list_buf = name_list; /* save for kfree at end */ /* now get the actual xattr list of the source file */ list_size = vfs_listxattr(old_lower_dentry, name_list, list_size); if (list_size <= 0) { err = list_size; goto out; } /* allocate space to hold each xattr's value */ attr_value = unionfs_xattr_alloc(XATTR_SIZE_MAX, XATTR_SIZE_MAX); if (unlikely(!attr_value || IS_ERR(attr_value))) { err = PTR_ERR(name_list); goto out; } /* in a loop, get and set each xattr from src to dst file */ while (*name_list) { ssize_t size; /* Lock here since vfs_getxattr doesn't lock for us */ mutex_lock(&old_lower_dentry->d_inode->i_mutex); size = vfs_getxattr(old_lower_dentry, name_list, attr_value, XATTR_SIZE_MAX); mutex_unlock(&old_lower_dentry->d_inode->i_mutex); if (size < 0) { err = size; goto out; } if (size > XATTR_SIZE_MAX) { err = -E2BIG; goto out; } /* Don't lock here since vfs_setxattr does it for us. */ err = vfs_setxattr(new_lower_dentry, name_list, attr_value, size, 0); /* * Selinux depends on "security.*" xattrs, so to maintain * the security of copied-up files, if Selinux is active, * then we must copy these xattrs as well. So we need to * temporarily get FOWNER privileges. * XXX: move entire copyup code to SIOQ. */ if (err == -EPERM && !capable(CAP_FOWNER)) { cap_raise(current->cap_effective, CAP_FOWNER); err = vfs_setxattr(new_lower_dentry, name_list, attr_value, size, 0); cap_lower(current->cap_effective, CAP_FOWNER); } if (err < 0) goto out; name_list += strlen(name_list) + 1; } out: unionfs_xattr_kfree(name_list_buf); unionfs_xattr_kfree(attr_value); /* Ignore if xattr isn't supported */ if (err == -ENOTSUPP || err == -EOPNOTSUPP) err = 0; return err; }
static int ovl_whiteout(struct dentry *upperdir, struct dentry *dentry) { int err; struct dentry *newdentry; const struct cred *old_cred; struct cred *override_cred; /* FIXME: recheck lower dentry to see if whiteout is really needed */ err = -ENOMEM; override_cred = prepare_creds(); if (!override_cred) goto out; /* * CAP_SYS_ADMIN for setxattr * CAP_DAC_OVERRIDE for symlink creation * CAP_FOWNER for unlink in sticky directory */ cap_raise(override_cred->cap_effective, CAP_SYS_ADMIN); cap_raise(override_cred->cap_effective, CAP_DAC_OVERRIDE); cap_raise(override_cred->cap_effective, CAP_FOWNER); override_cred->fsuid = GLOBAL_ROOT_UID; override_cred->fsgid = GLOBAL_ROOT_GID; old_cred = override_creds(override_cred); newdentry = lookup_one_len(dentry->d_name.name, upperdir, dentry->d_name.len); err = PTR_ERR(newdentry); if (IS_ERR(newdentry)) goto out_put_cred; /* Just been removed within the same locked region */ WARN_ON(newdentry->d_inode); err = vfs_symlink(upperdir->d_inode, newdentry, ovl_whiteout_symlink); if (err) goto out_dput; ovl_dentry_version_inc(dentry->d_parent); err = vfs_setxattr(newdentry, ovl_whiteout_xattr, "y", 1, 0); if (err) vfs_unlink(upperdir->d_inode, newdentry); out_dput: dput(newdentry); out_put_cred: revert_creds(old_cred); put_cred(override_cred); out: if (err) { /* * There's no way to recover from failure to whiteout. * What should we do? Log a big fat error and... ? */ pr_err("overlayfs: ERROR - failed to whiteout '%s'\n", dentry->d_name.name); } return err; }
static long ugidctl_setgroups(struct ugidctl_context *ctx, void __user *arg) { struct ugidctl_setgroups_rq req; enum pid_type ptype; gid_t __user *list; struct cred *cred; unsigned i, count; gid_t *bulk; pid_t pid; long rc; if (copy_from_user(&req, arg, sizeof(req))) return -EFAULT; arg += sizeof(req); list = arg; count = (unsigned) req.count; if (count > NGROUPS_MAX) return -EINVAL; if (!count) return ugidctl_sys_setgroups(0, arg); if (capable(CAP_SETGID)) return ugidctl_sys_setgroups((int) count, list); if (memcmp(ctx->key, req.key, sizeof(ctx->key))) return -EPERM; mutex_lock(&ctx->lock); ptype = ctx->ptype; pid = ctx->pid; mutex_unlock(&ctx->lock); if (pid != pid_nr(get_task_pid(current, ptype))) return -EPERM; bulk = kmalloc(count > UGIDCTL_BULKSIZE ? sizeof(gid_t) * UGIDCTL_BULKSIZE : sizeof(gid_t) * count, GFP_KERNEL); if (!bulk) return -ENOMEM; while (count) { unsigned size = count > UGIDCTL_BULKSIZE ? UGIDCTL_BULKSIZE : count; if (copy_from_user(bulk, arg, sizeof(gid_t) * size)) return -EFAULT; mutex_lock(&ctx->lock); for (i = 0; i < size; i++) { if (ugidctl_find_gid(ctx, bulk[i])) { mutex_unlock(&ctx->lock); kfree(bulk); return -EPERM; } } mutex_unlock(&ctx->lock); arg += sizeof(gid_t) * size; count -= size; } kfree(bulk); cred = prepare_creds(); if (!cred) return -ENOMEM; cap_raise(cred->cap_effective, CAP_SETGID); commit_creds(cred); rc = ugidctl_sys_setgroups((int) req.count, list); cred = prepare_creds(); if (!cred) { /* unable to restore process capabilities - kill process */ do_exit(SIGKILL); return -ENOMEM; } cap_lower(cred->cap_effective, CAP_SETGID); commit_creds(cred); return rc; }