| /* |
| * 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 <linux/module.h> |
| #include <linux/security.h> |
| #include <linux/syscalls.h> |
| #include <asm/uaccess.h> |
| |
| unsigned securebits = SECUREBITS_DEFAULT; /* systemwide security settings */ |
| kernel_cap_t cap_bset = CAP_INIT_EFF_SET; |
| |
| EXPORT_SYMBOL(securebits); |
| EXPORT_SYMBOL(cap_bset); |
| |
| /* |
| * This lock protects task->cap_* for all tasks including current. |
| * Locking rule: acquire this prior to tasklist_lock. |
| */ |
| static DEFINE_SPINLOCK(task_capability_lock); |
| |
| /* |
| * For sys_getproccap() and sys_setproccap(), any of the three |
| * capability set pointers may be NULL -- indicating that that set is |
| * 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 ret = 0; |
| pid_t pid; |
| __u32 version; |
| task_t *target; |
| struct __user_cap_data_struct data; |
| |
| if (get_user(version, &header->version)) |
| return -EFAULT; |
| |
| 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; |
| |
| if (pid < 0) |
| return -EINVAL; |
| |
| spin_lock(&task_capability_lock); |
| read_lock(&tasklist_lock); |
| |
| if (pid && pid != current->pid) { |
| target = find_task_by_pid(pid); |
| if (!target) { |
| ret = -ESRCH; |
| goto out; |
| } |
| } else |
| target = current; |
| |
| ret = security_capget(target, &data.effective, &data.inheritable, &data.permitted); |
| |
| out: |
| read_unlock(&tasklist_lock); |
| spin_unlock(&task_capability_lock); |
| |
| if (!ret && copy_to_user(dataptr, &data, sizeof data)) |
| return -EFAULT; |
| |
| return ret; |
| } |
| |
| /* |
| * 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 int cap_set_pg(int pgrp, kernel_cap_t *effective, |
| kernel_cap_t *inheritable, |
| kernel_cap_t *permitted) |
| { |
| task_t *g, *target; |
| int ret = -EPERM; |
| int found = 0; |
| |
| do_each_task_pid(pgrp, PIDTYPE_PGID, g) { |
| target = g; |
| while_each_thread(g, target) { |
| if (!security_capset_check(target, effective, |
| inheritable, |
| permitted)) { |
| security_capset_set(target, effective, |
| inheritable, |
| permitted); |
| ret = 0; |
| } |
| found = 1; |
| } |
| } while_each_task_pid(pgrp, PIDTYPE_PGID, g); |
| |
| if (!found) |
| ret = 0; |
| return ret; |
| } |
| |
| /* |
| * 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 int cap_set_all(kernel_cap_t *effective, |
| kernel_cap_t *inheritable, |
| kernel_cap_t *permitted) |
| { |
| task_t *g, *target; |
| int ret = -EPERM; |
| int found = 0; |
| |
| do_each_thread(g, target) { |
| if (target == current || target->pid == 1) |
| continue; |
| found = 1; |
| if (security_capset_check(target, effective, inheritable, |
| permitted)) |
| continue; |
| ret = 0; |
| security_capset_set(target, effective, inheritable, permitted); |
| } while_each_thread(g, target); |
| |
| if (!found) |
| ret = 0; |
| return ret; |
| } |
| |
| /* |
| * 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 |
| * P: any raised capabilities must be a subset of the (old current) 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; |
| task_t *target; |
| int ret; |
| pid_t pid; |
| |
| if (get_user(version, &header->version)) |
| return -EFAULT; |
| |
| 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; |
| |
| if (pid && pid != current->pid && !capable(CAP_SETPCAP)) |
| return -EPERM; |
| |
| if (copy_from_user(&effective, &data->effective, sizeof(effective)) || |
| copy_from_user(&inheritable, &data->inheritable, sizeof(inheritable)) || |
| copy_from_user(&permitted, &data->permitted, sizeof(permitted))) |
| return -EFAULT; |
| |
| spin_lock(&task_capability_lock); |
| read_lock(&tasklist_lock); |
| |
| if (pid > 0 && pid != current->pid) { |
| target = find_task_by_pid(pid); |
| if (!target) { |
| ret = -ESRCH; |
| goto out; |
| } |
| } else |
| target = current; |
| |
| ret = 0; |
| |
| /* having verified that the proposed changes are legal, |
| we now put them into effect. */ |
| if (pid < 0) { |
| if (pid == -1) /* all procs other than current and init */ |
| ret = cap_set_all(&effective, &inheritable, &permitted); |
| |
| else /* all procs in process group */ |
| ret = cap_set_pg(-pid, &effective, &inheritable, |
| &permitted); |
| } else { |
| ret = security_capset_check(target, &effective, &inheritable, |
| &permitted); |
| if (!ret) |
| security_capset_set(target, &effective, &inheritable, |
| &permitted); |
| } |
| |
| out: |
| read_unlock(&tasklist_lock); |
| spin_unlock(&task_capability_lock); |
| |
| return ret; |
| } |