diff options
author | Ralf Baechle <ralf@linux-mips.org> | 1998-06-30 00:21:34 +0000 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 1998-06-30 00:21:34 +0000 |
commit | 3917ac5846dd0f9ad1238166f90caab9912052e6 (patch) | |
tree | 1c298935def4f29edb39192365a65d73de999155 /kernel/capability.c | |
parent | af2f803c8b2d469fe38e4a7ce952658dfcb6681a (diff) |
o Merge with Linux 2.1.100.
o Cleanup the machine dependencies of floppy and rtc. The driver for
the Dallas thingy in the Indy is still missing.
o Handle allocation of zero'd pages correct for R4000SC / R4400SC.
o Page colouring shit to match the virtual and physical colour of all
mapped pages. This tends to produce extreme fragmentation problems,
so it's deactivated for now. Users of R4000SC / R4400SC may re-enable
the code in arch/mips/mm/init.c by removing the definition of
CONF_GIVE_A_SHIT_ABOUT_COLOURS. Should get them somewhat further -
but don't shake to hard ...
o Fixed ptrace(2)-ing of syscalls, strace is now working again.
o Fix the interrupt forwarding from the keyboard driver to the psaux
driver, PS/2 mice are now working on the Indy. The fix is somewhat
broken as it prevents generic kernels for Indy and machines which handle
things different.
o Things I can't remember.
Diffstat (limited to 'kernel/capability.c')
-rw-r--r-- | kernel/capability.c | 252 |
1 files changed, 252 insertions, 0 deletions
diff --git a/kernel/capability.c b/kernel/capability.c new file mode 100644 index 000000000..ddbfaa87b --- /dev/null +++ b/kernel/capability.c @@ -0,0 +1,252 @@ +/* + * linux/kernel/capability.c + * + * Copyright (C) 1997 Andrew Main <zefram@fysh.org> + * Integrated into 2.1.97+, Andrew G. Morgan <morgan@transmeta.com> + */ + +#include <linux/config.h> +#include <linux/errno.h> +#include <linux/kernel.h> +#include <linux/capability.h> +#include <linux/mm.h> +#include <linux/sched.h> +#include <linux/string.h> + +#include <asm/uaccess.h> + +static inline void cap_fromuser(kernel_cap_t *k, __u32 *u) +{ + copy_from_user(k, u, sizeof(*k)); +} + + +static inline void cap_touser(__u32 *u, const kernel_cap_t *k) +{ + copy_to_user(u, k, sizeof(*k)); +} + +#ifdef __SMP__ +static spinlock_t task_capability_lock; +#endif + +/* + * 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. + */ + +asmlinkage int sys_capget(cap_user_header_t header, cap_user_data_t data) +{ + int error = -EINVAL, pid; + __u32 version; + struct task_struct *target; + + if (!access_ok(VERIFY_WRITE, &header->version, sizeof(*header))) { + /* not large enough for current header so indicate error */ + if (access_ok(VERIFY_WRITE, &header->version, + sizeof(header->version))) { + return error; + } + goto all_done; + } + + copy_from_user(&version, &header->version, sizeof(header->version)); + if (version != _LINUX_CAPABILITY_VERSION) { + /* if enough space for kernel version, write that */ + + all_done: + version = _LINUX_CAPABILITY_VERSION; + copy_to_user(&header->version, &version, + sizeof(header->version)); + return error; + } + + if (!access_ok(VERIFY_WRITE, data, sizeof(*data))) { + return error; + } + + copy_from_user(&pid, &header->pid, sizeof(header->pid)); + if (pid < 0) { + return error; + } + + spin_lock(&task_capability_lock); + + if (pid && 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; + } + + cap_touser(&data->permitted, &target->cap_permitted); + cap_touser(&data->inheritable, &target->cap_inheritable); + cap_touser(&data->effective, &target->cap_effective); + + error = 0; + +out: + if (target != current) { + read_unlock(&tasklist_lock); + } + spin_unlock(&task_capability_lock); + return error; +} + +/* 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) +{ + struct task_struct *target; + + /* FIXME: do we need to have a write lock here..? */ + read_lock(&tasklist_lock); + for_each_task(target) { + if (target->pgrp != pgrp) + continue; + target->cap_effective = *effective; + 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) +{ + struct task_struct *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; + target->cap_effective = *effective; + target->cap_inheritable = *inheritable; + target->cap_permitted = *permitted; + } + read_unlock(&tasklist_lock); +} + +/* + * 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 int 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 = -EINVAL, pid; + + if (!access_ok(VERIFY_WRITE, &header->version, sizeof(*header))) { + /* not large enough for current header so indicate error */ + if (!access_ok(VERIFY_WRITE, &header->version, + sizeof(header->version))) { + return error; + } + goto all_done; + } + + copy_from_user(&version, &header->version, sizeof(header->version)); + if (version != _LINUX_CAPABILITY_VERSION) { + + all_done: + version = _LINUX_CAPABILITY_VERSION; + copy_to_user(&header->version, &version, + sizeof(header->version)); + return error; + } + + if (!access_ok(VERIFY_READ, data, sizeof(*data))) { + return error; + } + + /* may want to set other processes at some point -- for now demand 0 */ + copy_from_user(&pid, &header->pid, sizeof(pid)); + + error = -EPERM; + if (pid && !capable(CAP_SETPCAP)) + return error; + + spin_lock(&task_capability_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; + } + + /* copy from userspace */ + cap_fromuser(&effective, &data->effective); + cap_fromuser(&inheritable, &data->inheritable); + cap_fromuser(&permitted, &data->permitted); + + /* verify restrictions on target's new Inheritable set */ + 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))) { + goto out; + } + + /* verify the _new_Effective_ is a subset of the _new_Permitted_ */ + if (!cap_issubset(effective, permitted)) { + goto out; + } + + /* having verified that the proposed changes are legal, + we now put them into effect. */ + error = 0; + + if (pid < 0) { + if (pid == -1) /* all procs other than current and init */ + cap_set_all(&effective, &inheritable, &permitted); + + 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: + spin_unlock(&task_capability_lock); + return error; +} |