diff options
Diffstat (limited to 'kernel/capability.c')
| -rw-r--r-- | kernel/capability.c | 166 |
1 files changed, 74 insertions, 92 deletions
diff --git a/kernel/capability.c b/kernel/capability.c index 8fd0d451069d..1911791268fb 100644 --- a/kernel/capability.c +++ b/kernel/capability.c @@ -2,17 +2,21 @@ * linux/kernel/capability.c * * Copyright (C) 1997 Andrew Main <zefram@fysh.org> + * * Integrated into 2.1.97+, Andrew G. Morgan <morgan@transmeta.com> + * 30 May 2002: Cleanup, Robert M. Love <rml@tech9.net> */ #include <linux/mm.h> #include <asm/uaccess.h> unsigned securebits = SECUREBITS_DEFAULT; /* systemwide security settings */ - kernel_cap_t cap_bset = CAP_INIT_EFF_SET; -/* Note: never hold tasklist_lock while spinning for this one */ +/* + * This global lock protects task->cap_* for all tasks including current. + * Locking rule: acquire this prior to tasklist_lock. + */ spinlock_t task_capability_lock = SPIN_LOCK_UNLOCKED; /* @@ -21,23 +25,24 @@ spinlock_t task_capability_lock = SPIN_LOCK_UNLOCKED; * uninteresting and/or not to be changed. */ +/* + * sys_capget - get the capabilities of a given process. + */ asmlinkage long sys_capget(cap_user_header_t header, cap_user_data_t dataptr) { - int error, pid; + int ret = 0; + pid_t pid; __u32 version; - struct task_struct *target; + task_t *target; struct __user_cap_data_struct data; if (get_user(version, &header->version)) return -EFAULT; - - error = -EINVAL; - if (version != _LINUX_CAPABILITY_VERSION) { - version = _LINUX_CAPABILITY_VERSION; - if (put_user(version, &header->version)) - error = -EFAULT; - return error; - } + + if (version != _LINUX_CAPABILITY_VERSION) + if (put_user(_LINUX_CAPABILITY_VERSION, &header->version)) + return -EFAULT; + return -EINVAL; if (get_user(pid, &header->pid)) return -EFAULT; @@ -45,48 +50,39 @@ asmlinkage long sys_capget(cap_user_header_t header, cap_user_data_t dataptr) if (pid < 0) return -EINVAL; - error = 0; - spin_lock(&task_capability_lock); + read_lock(&tasklist_lock); - if (pid && pid != current->pid) { - read_lock(&tasklist_lock); - target = find_task_by_pid(pid); /* identify target of query */ - if (!target) - error = -ESRCH; - } else { - target = current; + target = find_task_by_pid(pid); + if (!target) { + ret = -ESRCH; + goto out; } - if (!error) { - data.permitted = cap_t(target->cap_permitted); - data.inheritable = cap_t(target->cap_inheritable); - data.effective = cap_t(target->cap_effective); - } + data.permitted = cap_t(target->cap_permitted); + data.inheritable = cap_t(target->cap_inheritable); + data.effective = cap_t(target->cap_effective); - if (target != current) - read_unlock(&tasklist_lock); +out: + read_unlock(&tasklist_lock); spin_unlock(&task_capability_lock); - if (!error) { - if (copy_to_user(dataptr, &data, sizeof data)) - return -EFAULT; - } + if (!ret && copy_to_user(dataptr, &data, sizeof data)) + return -EFAULT; - return error; + return ret; } -/* set capabilities for all processes in a given process group */ - -static void cap_set_pg(int pgrp, - kernel_cap_t *effective, - kernel_cap_t *inheritable, - kernel_cap_t *permitted) +/* + * cap_set_pg - set capabilities for all processes in a given process + * group. We call this holding task_capability_lock and tasklist_lock. + */ +static inline void cap_set_pg(int pgrp, kernel_cap_t *effective, + kernel_cap_t *inheritable, + kernel_cap_t *permitted) { - struct task_struct *target; + task_t *target; - /* FIXME: do we need to have a write lock here..? */ - read_lock(&tasklist_lock); for_each_task(target) { if (target->pgrp != pgrp) continue; @@ -94,20 +90,18 @@ static void cap_set_pg(int pgrp, target->cap_inheritable = *inheritable; target->cap_permitted = *permitted; } - read_unlock(&tasklist_lock); } -/* set capabilities for all processes other than 1 and self */ - -static void cap_set_all(kernel_cap_t *effective, - kernel_cap_t *inheritable, - kernel_cap_t *permitted) +/* + * cap_set_all - set capabilities for all processes other than init + * and self. We call this holding task_capability_lock and tasklist_lock. + */ +static inline void cap_set_all(kernel_cap_t *effective, + kernel_cap_t *inheritable, + kernel_cap_t *permitted) { - struct task_struct *target; + task_t *target; - /* FIXME: do we need to have a write lock here..? */ - read_lock(&tasklist_lock); - /* ALL means everyone other than self or 'init' */ for_each_task(target) { if (target == current || target->pid == 1) continue; @@ -115,35 +109,35 @@ static void cap_set_all(kernel_cap_t *effective, target->cap_inheritable = *inheritable; target->cap_permitted = *permitted; } - read_unlock(&tasklist_lock); } /* + * sys_capset - set capabilities for a given process, all processes, or all + * processes in a given process group. + * * The restrictions on setting capabilities are specified as: * * [pid is for the 'target' task. 'current' is the calling task.] * - * I: any raised capabilities must be a subset of the (old current) Permitted + * I: any raised capabilities must be a subset of the (old current) permitted * P: any raised capabilities must be a subset of the (old current) permitted - * E: must be set to a subset of (new target) Permitted + * E: must be set to a subset of (new target) permitted */ - asmlinkage long sys_capset(cap_user_header_t header, const cap_user_data_t data) { kernel_cap_t inheritable, permitted, effective; __u32 version; - struct task_struct *target; - int error, pid; + task_t *target; + int ret; + pid_t pid; if (get_user(version, &header->version)) return -EFAULT; - if (version != _LINUX_CAPABILITY_VERSION) { - version = _LINUX_CAPABILITY_VERSION; - if (put_user(version, &header->version)) + if (version != _LINUX_CAPABILITY_VERSION) + if (put_user(_LINUX_CAPABILITY_VERSION, &header->version)) return -EFAULT; return -EINVAL; - } if (get_user(pid, &header->pid)) return -EFAULT; @@ -156,43 +150,35 @@ asmlinkage long sys_capset(cap_user_header_t header, const cap_user_data_t data) copy_from_user(&permitted, &data->permitted, sizeof(permitted))) return -EFAULT; - error = -EPERM; spin_lock(&task_capability_lock); + read_lock(&tasklist_lock); if (pid > 0 && pid != current->pid) { - read_lock(&tasklist_lock); - target = find_task_by_pid(pid); /* identify target of query */ - if (!target) { - error = -ESRCH; - goto out; - } - } else { - target = current; - } + target = find_task_by_pid(pid); + if (!target) { + ret = -ESRCH; + goto out; + } + } else + target = current; + ret = -EPERM; /* verify restrictions on target's new Inheritable set */ - if (!cap_issubset(inheritable, - cap_combine(target->cap_inheritable, - current->cap_permitted))) { + if (!cap_issubset(inheritable, cap_combine(target->cap_inheritable, + current->cap_permitted))) goto out; - } /* verify restrictions on target's new Permitted set */ - if (!cap_issubset(permitted, - cap_combine(target->cap_permitted, - current->cap_permitted))) { + if (!cap_issubset(permitted, cap_combine(target->cap_permitted, + current->cap_permitted))) goto out; - } /* verify the _new_Effective_ is a subset of the _new_Permitted_ */ - if (!cap_issubset(effective, permitted)) { + if (!cap_issubset(effective, permitted)) goto out; - } - /* having verified that the proposed changes are legal, - we now put them into effect. */ - error = 0; + ret = 0; if (pid < 0) { if (pid == -1) /* all procs other than current and init */ @@ -200,19 +186,15 @@ asmlinkage long sys_capset(cap_user_header_t header, const cap_user_data_t data) else /* all procs in process group */ cap_set_pg(-pid, &effective, &inheritable, &permitted); - goto spin_out; } else { - /* FIXME: do we need to have a write lock here..? */ target->cap_effective = effective; target->cap_inheritable = inheritable; target->cap_permitted = permitted; } out: - if (target != current) { - read_unlock(&tasklist_lock); - } -spin_out: + read_unlock(&tasklist_lock); spin_unlock(&task_capability_lock); - return error; + + return ret; } |
