From 70f2817a43c89b784dc2ec3d06ba5bf3064f8235 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Fri, 29 Apr 2005 01:27:34 -0500 Subject: [PATCH] sysfs: (rest) if show/store is missing return -EIO sysfs: fix the rest of the kernel so if an attribute doesn't implement show or store method read/write will return -EIO instead of 0 or -EINVAL or -EPERM. Signed-off-by: Dmitry Torokhov Signed-off-by: Greg Kroah-Hartman --- security/seclvl.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'security') diff --git a/security/seclvl.c b/security/seclvl.c index 8a0ab0d7949e..c8e87b22c9bd 100644 --- a/security/seclvl.c +++ b/security/seclvl.c @@ -155,7 +155,7 @@ seclvl_attr_store(struct kobject *kobj, struct seclvl_obj *obj = container_of(kobj, struct seclvl_obj, kobj); struct seclvl_attribute *attribute = container_of(attr, struct seclvl_attribute, attr); - return (attribute->store ? attribute->store(obj, buf, len) : 0); + return attribute->store ? attribute->store(obj, buf, len) : -EIO; } static ssize_t @@ -164,7 +164,7 @@ seclvl_attr_show(struct kobject *kobj, struct attribute *attr, char *buf) struct seclvl_obj *obj = container_of(kobj, struct seclvl_obj, kobj); struct seclvl_attribute *attribute = container_of(attr, struct seclvl_attribute, attr); - return (attribute->show ? attribute->show(obj, buf) : 0); + return attribute->show ? attribute->show(obj, buf) : -EIO; } /** -- cgit v1.2.3 From da3caa204ca40c32dcb751ebead2a6835b83e8d1 Mon Sep 17 00:00:00 2001 From: Gerald Schaefer Date: Tue, 21 Jun 2005 17:15:18 -0700 Subject: [PATCH] SELinux: memory leak in selinux_sb_copy_data() There is a memory leak during mount when SELinux is active and mount options are specified. Signed-off-by: Gerald Schaefer Acked-by: Stephen Smalley Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- security/selinux/hooks.c | 1 + 1 file changed, 1 insertion(+) (limited to 'security') diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index db845cbd5841..87302a49067b 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -1945,6 +1945,7 @@ static int selinux_sb_copy_data(struct file_system_type *type, void *orig, void } while (*in_end++); copy_page(in_save, nosec_save); + free_page((unsigned long)nosec_save); out: return rc; } -- cgit v1.2.3 From d6e711448137ca3301512cec41a2c2ce852b3d0a Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Thu, 23 Jun 2005 00:09:43 -0700 Subject: [PATCH] setuid core dump Add a new `suid_dumpable' sysctl: This value can be used to query and set the core dump mode for setuid or otherwise protected/tainted binaries. The modes are 0 - (default) - traditional behaviour. Any process which has changed privilege levels or is execute only will not be dumped 1 - (debug) - all processes dump core when possible. The core dump is owned by the current user and no security is applied. This is intended for system debugging situations only. Ptrace is unchecked. 2 - (suidsafe) - any binary which normally would not be dumped is dumped readable by root only. This allows the end user to remove such a dump but not access it directly. For security reasons core dumps in this mode will not overwrite one another or other files. This mode is appropriate when adminstrators are attempting to debug problems in a normal environment. (akpm: > > +EXPORT_SYMBOL(suid_dumpable); > > EXPORT_SYMBOL_GPL? No problem to me. > > if (current->euid == current->uid && current->egid == current->gid) > > current->mm->dumpable = 1; > > Should this be SUID_DUMP_USER? Actually the feedback I had from last time was that the SUID_ defines should go because its clearer to follow the numbers. They can go everywhere (and there are lots of places where dumpable is tested/used as a bool in untouched code) > Maybe this should be renamed to `dump_policy' or something. Doing that > would help us catch any code which isn't using the #defines, too. Fair comment. The patch was designed to be easy to maintain for Red Hat rather than for merging. Changing that field would create a gigantic diff because it is used all over the place. ) Signed-off-by: Alan Cox Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- Documentation/sysctl/kernel.txt | 20 ++++++++++++++++++++ fs/exec.c | 23 +++++++++++++++++++++-- fs/proc/base.c | 6 ++++-- include/linux/binfmts.h | 5 +++++ include/linux/sched.h | 2 +- include/linux/sysctl.h | 1 + kernel/sys.c | 22 +++++++++++----------- kernel/sysctl.c | 9 +++++++++ security/commoncap.c | 2 +- security/dummy.c | 2 +- 10 files changed, 74 insertions(+), 18 deletions(-) (limited to 'security') diff --git a/Documentation/sysctl/kernel.txt b/Documentation/sysctl/kernel.txt index 35159176997b..9f11d36a8c10 100644 --- a/Documentation/sysctl/kernel.txt +++ b/Documentation/sysctl/kernel.txt @@ -49,6 +49,7 @@ show up in /proc/sys/kernel: - shmmax [ sysv ipc ] - shmmni - stop-a [ SPARC only ] +- suid_dumpable - sysrq ==> Documentation/sysrq.txt - tainted - threads-max @@ -300,6 +301,25 @@ kernel. This value defaults to SHMMAX. ============================================================== +suid_dumpable: + +This value can be used to query and set the core dump mode for setuid +or otherwise protected/tainted binaries. The modes are + +0 - (default) - traditional behaviour. Any process which has changed + privilege levels or is execute only will not be dumped +1 - (debug) - all processes dump core when possible. The core dump is + owned by the current user and no security is applied. This is + intended for system debugging situations only. Ptrace is unchecked. +2 - (suidsafe) - any binary which normally would not be dumped is dumped + readable by root only. This allows the end user to remove + such a dump but not access it directly. For security reasons + core dumps in this mode will not overwrite one another or + other files. This mode is appropriate when adminstrators are + attempting to debug problems in a normal environment. + +============================================================== + tainted: Non-zero if the kernel has been tainted. Numeric values, which diff --git a/fs/exec.c b/fs/exec.c index 3a4b35a14c0d..48871917d363 100644 --- a/fs/exec.c +++ b/fs/exec.c @@ -58,6 +58,9 @@ int core_uses_pid; char core_pattern[65] = "core"; +int suid_dumpable = 0; + +EXPORT_SYMBOL(suid_dumpable); /* The maximal length of core_pattern is also specified in sysctl.c */ static struct linux_binfmt *formats; @@ -864,6 +867,9 @@ int flush_old_exec(struct linux_binprm * bprm) if (current->euid == current->uid && current->egid == current->gid) current->mm->dumpable = 1; + else + current->mm->dumpable = suid_dumpable; + name = bprm->filename; /* Copies the binary name from after last slash */ @@ -884,7 +890,7 @@ int flush_old_exec(struct linux_binprm * bprm) permission(bprm->file->f_dentry->d_inode,MAY_READ, NULL) || (bprm->interp_flags & BINPRM_FLAGS_ENFORCE_NONDUMP)) { suid_keys(current); - current->mm->dumpable = 0; + current->mm->dumpable = suid_dumpable; } /* An exec changes our domain. We are no longer part of the thread @@ -1432,6 +1438,8 @@ int do_coredump(long signr, int exit_code, struct pt_regs * regs) struct inode * inode; struct file * file; int retval = 0; + int fsuid = current->fsuid; + int flag = 0; binfmt = current->binfmt; if (!binfmt || !binfmt->core_dump) @@ -1441,6 +1449,16 @@ int do_coredump(long signr, int exit_code, struct pt_regs * regs) up_write(&mm->mmap_sem); goto fail; } + + /* + * We cannot trust fsuid as being the "true" uid of the + * process nor do we know its entire history. We only know it + * was tainted so we dump it as root in mode 2. + */ + if (mm->dumpable == 2) { /* Setuid core dump mode */ + flag = O_EXCL; /* Stop rewrite attacks */ + current->fsuid = 0; /* Dump root private */ + } mm->dumpable = 0; init_completion(&mm->core_done); spin_lock_irq(¤t->sighand->siglock); @@ -1466,7 +1484,7 @@ int do_coredump(long signr, int exit_code, struct pt_regs * regs) lock_kernel(); format_corename(corename, core_pattern, signr); unlock_kernel(); - file = filp_open(corename, O_CREAT | 2 | O_NOFOLLOW | O_LARGEFILE, 0600); + file = filp_open(corename, O_CREAT | 2 | O_NOFOLLOW | O_LARGEFILE | flag, 0600); if (IS_ERR(file)) goto fail_unlock; inode = file->f_dentry->d_inode; @@ -1491,6 +1509,7 @@ int do_coredump(long signr, int exit_code, struct pt_regs * regs) close_fail: filp_close(file, NULL); fail_unlock: + current->fsuid = fsuid; complete_all(&mm->core_done); fail: return retval; diff --git a/fs/proc/base.c b/fs/proc/base.c index e31903aadd96..ace151fa4878 100644 --- a/fs/proc/base.c +++ b/fs/proc/base.c @@ -314,7 +314,7 @@ static int may_ptrace_attach(struct task_struct *task) (current->gid != task->gid)) && !capable(CAP_SYS_PTRACE)) goto out; rmb(); - if (!task->mm->dumpable && !capable(CAP_SYS_PTRACE)) + if (task->mm->dumpable != 1 && !capable(CAP_SYS_PTRACE)) goto out; if (security_ptrace(current, task)) goto out; @@ -1113,7 +1113,9 @@ static int task_dumpable(struct task_struct *task) if (mm) dumpable = mm->dumpable; task_unlock(task); - return dumpable; + if(dumpable == 1) + return 1; + return 0; } diff --git a/include/linux/binfmts.h b/include/linux/binfmts.h index 7e736e201c46..c1e82c514443 100644 --- a/include/linux/binfmts.h +++ b/include/linux/binfmts.h @@ -69,6 +69,11 @@ extern void remove_arg_zero(struct linux_binprm *); extern int search_binary_handler(struct linux_binprm *,struct pt_regs *); extern int flush_old_exec(struct linux_binprm * bprm); +extern int suid_dumpable; +#define SUID_DUMP_DISABLE 0 /* No setuid dumping */ +#define SUID_DUMP_USER 1 /* Dump as user of process */ +#define SUID_DUMP_ROOT 2 /* Dump as root */ + /* Stack area protections */ #define EXSTACK_DEFAULT 0 /* Whatever the arch defaults to */ #define EXSTACK_DISABLE_X 1 /* Disable executable stacks */ diff --git a/include/linux/sched.h b/include/linux/sched.h index b58afd97a180..901742f92389 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -246,7 +246,7 @@ struct mm_struct { unsigned long saved_auxv[42]; /* for /proc/PID/auxv */ - unsigned dumpable:1; + unsigned dumpable:2; cpumask_t cpu_vm_mask; /* Architecture-specific MM context */ diff --git a/include/linux/sysctl.h b/include/linux/sysctl.h index a17745c80a91..614e939c78a4 100644 --- a/include/linux/sysctl.h +++ b/include/linux/sysctl.h @@ -136,6 +136,7 @@ enum KERN_UNKNOWN_NMI_PANIC=66, /* int: unknown nmi panic flag */ KERN_BOOTLOADER_TYPE=67, /* int: boot loader type */ KERN_RANDOMIZE=68, /* int: randomize virtual address space */ + KERN_SETUID_DUMPABLE=69, /* int: behaviour of dumps for setuid core */ }; diff --git a/kernel/sys.c b/kernel/sys.c index f006632c2ba7..0a2c8cda9638 100644 --- a/kernel/sys.c +++ b/kernel/sys.c @@ -525,7 +525,7 @@ asmlinkage long sys_setregid(gid_t rgid, gid_t egid) } if (new_egid != old_egid) { - current->mm->dumpable = 0; + current->mm->dumpable = suid_dumpable; smp_wmb(); } if (rgid != (gid_t) -1 || @@ -556,7 +556,7 @@ asmlinkage long sys_setgid(gid_t gid) { if(old_egid != gid) { - current->mm->dumpable=0; + current->mm->dumpable = suid_dumpable; smp_wmb(); } current->gid = current->egid = current->sgid = current->fsgid = gid; @@ -565,7 +565,7 @@ asmlinkage long sys_setgid(gid_t gid) { if(old_egid != gid) { - current->mm->dumpable=0; + current->mm->dumpable = suid_dumpable; smp_wmb(); } current->egid = current->fsgid = gid; @@ -596,7 +596,7 @@ static int set_user(uid_t new_ruid, int dumpclear) if(dumpclear) { - current->mm->dumpable = 0; + current->mm->dumpable = suid_dumpable; smp_wmb(); } current->uid = new_ruid; @@ -653,7 +653,7 @@ asmlinkage long sys_setreuid(uid_t ruid, uid_t euid) if (new_euid != old_euid) { - current->mm->dumpable=0; + current->mm->dumpable = suid_dumpable; smp_wmb(); } current->fsuid = current->euid = new_euid; @@ -703,7 +703,7 @@ asmlinkage long sys_setuid(uid_t uid) if (old_euid != uid) { - current->mm->dumpable = 0; + current->mm->dumpable = suid_dumpable; smp_wmb(); } current->fsuid = current->euid = uid; @@ -748,7 +748,7 @@ asmlinkage long sys_setresuid(uid_t ruid, uid_t euid, uid_t suid) if (euid != (uid_t) -1) { if (euid != current->euid) { - current->mm->dumpable = 0; + current->mm->dumpable = suid_dumpable; smp_wmb(); } current->euid = euid; @@ -798,7 +798,7 @@ asmlinkage long sys_setresgid(gid_t rgid, gid_t egid, gid_t sgid) if (egid != (gid_t) -1) { if (egid != current->egid) { - current->mm->dumpable = 0; + current->mm->dumpable = suid_dumpable; smp_wmb(); } current->egid = egid; @@ -845,7 +845,7 @@ asmlinkage long sys_setfsuid(uid_t uid) { if (uid != old_fsuid) { - current->mm->dumpable = 0; + current->mm->dumpable = suid_dumpable; smp_wmb(); } current->fsuid = uid; @@ -875,7 +875,7 @@ asmlinkage long sys_setfsgid(gid_t gid) { if (gid != old_fsgid) { - current->mm->dumpable = 0; + current->mm->dumpable = suid_dumpable; smp_wmb(); } current->fsgid = gid; @@ -1652,7 +1652,7 @@ asmlinkage long sys_prctl(int option, unsigned long arg2, unsigned long arg3, error = 1; break; case PR_SET_DUMPABLE: - if (arg2 != 0 && arg2 != 1) { + if (arg2 < 0 || arg2 > 2) { error = -EINVAL; break; } diff --git a/kernel/sysctl.c b/kernel/sysctl.c index 701d12c63068..24a4d12d5aa9 100644 --- a/kernel/sysctl.c +++ b/kernel/sysctl.c @@ -58,6 +58,7 @@ extern int sysctl_overcommit_ratio; extern int max_threads; extern int sysrq_enabled; extern int core_uses_pid; +extern int suid_dumpable; extern char core_pattern[]; extern int cad_pid; extern int pid_max; @@ -950,6 +951,14 @@ static ctl_table fs_table[] = { .proc_handler = &proc_dointvec, }, #endif + { + .ctl_name = KERN_SETUID_DUMPABLE, + .procname = "suid_dumpable", + .data = &suid_dumpable, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = &proc_dointvec, + }, { .ctl_name = 0 } }; diff --git a/security/commoncap.c b/security/commoncap.c index 849b8c338ee8..04c12f58d656 100644 --- a/security/commoncap.c +++ b/security/commoncap.c @@ -149,7 +149,7 @@ void cap_bprm_apply_creds (struct linux_binprm *bprm, int unsafe) if (bprm->e_uid != current->uid || bprm->e_gid != current->gid || !cap_issubset (new_permitted, current->cap_permitted)) { - current->mm->dumpable = 0; + current->mm->dumpable = suid_dumpable; if (unsafe & ~LSM_UNSAFE_PTRACE_CAP) { if (!capable(CAP_SETUID)) { diff --git a/security/dummy.c b/security/dummy.c index b32eff146547..6ff887586479 100644 --- a/security/dummy.c +++ b/security/dummy.c @@ -130,7 +130,7 @@ static void dummy_bprm_free_security (struct linux_binprm *bprm) static void dummy_bprm_apply_creds (struct linux_binprm *bprm, int unsafe) { if (bprm->e_uid != current->uid || bprm->e_gid != current->gid) { - current->mm->dumpable = 0; + current->mm->dumpable = suid_dumpable; if ((unsafe & ~LSM_UNSAFE_PTRACE_CAP) && !capable(CAP_SETUID)) { bprm->e_uid = current->uid; -- cgit v1.2.3 From 76d8aeabfeb1c42641a81c44280177b9a08670d8 Mon Sep 17 00:00:00 2001 From: David Howells Date: Thu, 23 Jun 2005 22:00:49 -0700 Subject: [PATCH] keys: Discard key spinlock and use RCU for key payload The attached patch changes the key implementation in a number of ways: (1) It removes the spinlock from the key structure. (2) The key flags are now accessed using atomic bitops instead of write-locking the key spinlock and using C bitwise operators. The three instantiation flags are dealt with with the construction semaphore held during the request_key/instantiate/negate sequence, thus rendering the spinlock superfluous. The key flags are also now bit numbers not bit masks. (3) The key payload is now accessed using RCU. This permits the recursive keyring search algorithm to be simplified greatly since no locks need be taken other than the usual RCU preemption disablement. Searching now does not require any locks or semaphores to be held; merely that the starting keyring be pinned. (4) The keyring payload now includes an RCU head so that it can be disposed of by call_rcu(). This requires that the payload be copied on unlink to prevent introducing races in copy-down vs search-up. (5) The user key payload is now a structure with the data following it. It includes an RCU head like the keyring payload and for the same reason. It also contains a data length because the data length in the key may be changed on another CPU whilst an RCU protected read is in progress on the payload. This would then see the supposed RCU payload and the on-key data length getting out of sync. I'm tempted to drop the key's datalen entirely, except that it's used in conjunction with quota management and so is a little tricky to get rid of. (6) Update the keys documentation. Signed-Off-By: David Howells Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- Documentation/keys.txt | 285 ++++++++++++++++++++++++++----------------- include/linux/key-ui.h | 6 +- include/linux/key.h | 25 ++-- security/keys/key.c | 94 +++++++------- security/keys/keyctl.c | 23 ++-- security/keys/keyring.c | 245 ++++++++++++++++++++++--------------- security/keys/proc.c | 21 ++-- security/keys/process_keys.c | 12 +- security/keys/request_key.c | 32 +++-- security/keys/user_defined.c | 85 +++++++++---- 10 files changed, 480 insertions(+), 348 deletions(-) (limited to 'security') diff --git a/Documentation/keys.txt b/Documentation/keys.txt index 36d80aeeaf28..3df40c1fe15a 100644 --- a/Documentation/keys.txt +++ b/Documentation/keys.txt @@ -22,6 +22,7 @@ This document has the following sections: - New procfs files - Userspace system call interface - Kernel services + - Notes on accessing payload contents - Defining a key type - Request-key callback service - Key access filesystem @@ -45,27 +46,26 @@ Each key has a number of attributes: - State. - (*) Each key is issued a serial number of type key_serial_t that is unique - for the lifetime of that key. All serial numbers are positive non-zero - 32-bit integers. + (*) Each key is issued a serial number of type key_serial_t that is unique for + the lifetime of that key. All serial numbers are positive non-zero 32-bit + integers. Userspace programs can use a key's serial numbers as a way to gain access to it, subject to permission checking. (*) Each key is of a defined "type". Types must be registered inside the - kernel by a kernel service (such as a filesystem) before keys of that - type can be added or used. Userspace programs cannot define new types - directly. + kernel by a kernel service (such as a filesystem) before keys of that type + can be added or used. Userspace programs cannot define new types directly. - Key types are represented in the kernel by struct key_type. This defines - a number of operations that can be performed on a key of that type. + Key types are represented in the kernel by struct key_type. This defines a + number of operations that can be performed on a key of that type. Should a type be removed from the system, all the keys of that type will be invalidated. (*) Each key has a description. This should be a printable string. The key - type provides an operation to perform a match between the description on - a key and a criterion string. + type provides an operation to perform a match between the description on a + key and a criterion string. (*) Each key has an owner user ID, a group ID and a permissions mask. These are used to control what a process may do to a key from userspace, and @@ -74,10 +74,10 @@ Each key has a number of attributes: (*) Each key can be set to expire at a specific time by the key type's instantiation function. Keys can also be immortal. - (*) Each key can have a payload. This is a quantity of data that represent - the actual "key". In the case of a keyring, this is a list of keys to - which the keyring links; in the case of a user-defined key, it's an - arbitrary blob of data. + (*) Each key can have a payload. This is a quantity of data that represent the + actual "key". In the case of a keyring, this is a list of keys to which + the keyring links; in the case of a user-defined key, it's an arbitrary + blob of data. Having a payload is not required; and the payload can, in fact, just be a value stored in the struct key itself. @@ -92,8 +92,8 @@ Each key has a number of attributes: (*) Each key can be in one of a number of basic states: - (*) Uninstantiated. The key exists, but does not have any data - attached. Keys being requested from userspace will be in this state. + (*) Uninstantiated. The key exists, but does not have any data attached. + Keys being requested from userspace will be in this state. (*) Instantiated. This is the normal state. The key is fully formed, and has data attached. @@ -140,10 +140,10 @@ The key service provides a number of features besides keys: clone, fork, vfork or execve occurs. A new keyring is created only when required. - The process-specific keyring is replaced with an empty one in the child - on clone, fork, vfork unless CLONE_THREAD is supplied, in which case it - is shared. execve also discards the process's process keyring and creates - a new one. + The process-specific keyring is replaced with an empty one in the child on + clone, fork, vfork unless CLONE_THREAD is supplied, in which case it is + shared. execve also discards the process's process keyring and creates a + new one. The session-specific keyring is persistent across clone, fork, vfork and execve, even when the latter executes a set-UID or set-GID binary. A @@ -177,11 +177,11 @@ The key service provides a number of features besides keys: If a system call that modifies a key or keyring in some way would put the user over quota, the operation is refused and error EDQUOT is returned. - (*) There's a system call interface by which userspace programs can create - and manipulate keys and keyrings. + (*) There's a system call interface by which userspace programs can create and + manipulate keys and keyrings. - (*) There's a kernel interface by which services can register types and - search for keys. + (*) There's a kernel interface by which services can register types and search + for keys. (*) There's a way for the a search done from the kernel to call back to userspace to request a key that can't be found in a process's keyrings. @@ -194,9 +194,9 @@ The key service provides a number of features besides keys: KEY ACCESS PERMISSIONS ====================== -Keys have an owner user ID, a group access ID, and a permissions mask. The -mask has up to eight bits each for user, group and other access. Only five of -each set of eight bits are defined. These permissions granted are: +Keys have an owner user ID, a group access ID, and a permissions mask. The mask +has up to eight bits each for user, group and other access. Only five of each +set of eight bits are defined. These permissions granted are: (*) View @@ -210,8 +210,8 @@ each set of eight bits are defined. These permissions granted are: (*) Write - This permits a key's payload to be instantiated or updated, or it allows - a link to be added to or removed from a keyring. + This permits a key's payload to be instantiated or updated, or it allows a + link to be added to or removed from a keyring. (*) Search @@ -238,8 +238,8 @@ about the status of the key service: (*) /proc/keys This lists all the keys on the system, giving information about their - type, description and permissions. The payload of the key is not - available this way: + type, description and permissions. The payload of the key is not available + this way: SERIAL FLAGS USAGE EXPY PERM UID GID TYPE DESCRIPTION: SUMMARY 00000001 I----- 39 perm 1f0000 0 0 keyring _uid_ses.0: 1/4 @@ -318,21 +318,21 @@ The main syscalls are: If a key of the same type and description as that proposed already exists in the keyring, this will try to update it with the given payload, or it will return error EEXIST if that function is not supported by the key - type. The process must also have permission to write to the key to be - able to update it. The new key will have all user permissions granted and - no group or third party permissions. + type. The process must also have permission to write to the key to be able + to update it. The new key will have all user permissions granted and no + group or third party permissions. - Otherwise, this will attempt to create a new key of the specified type - and description, and to instantiate it with the supplied payload and - attach it to the keyring. In this case, an error will be generated if the - process does not have permission to write to the keyring. + Otherwise, this will attempt to create a new key of the specified type and + description, and to instantiate it with the supplied payload and attach it + to the keyring. In this case, an error will be generated if the process + does not have permission to write to the keyring. The payload is optional, and the pointer can be NULL if not required by the type. The payload is plen in size, and plen can be zero for an empty payload. - A new keyring can be generated by setting type "keyring", the keyring - name as the description (or NULL) and setting the payload to NULL. + A new keyring can be generated by setting type "keyring", the keyring name + as the description (or NULL) and setting the payload to NULL. User defined keys can be created by specifying type "user". It is recommended that a user defined key's description by prefixed with a type @@ -369,9 +369,9 @@ The keyctl syscall functions are: key_serial_t keyctl(KEYCTL_GET_KEYRING_ID, key_serial_t id, int create); - The special key specified by "id" is looked up (with the key being - created if necessary) and the ID of the key or keyring thus found is - returned if it exists. + The special key specified by "id" is looked up (with the key being created + if necessary) and the ID of the key or keyring thus found is returned if + it exists. If the key does not yet exist, the key will be created if "create" is non-zero; and the error ENOKEY will be returned if "create" is zero. @@ -402,8 +402,8 @@ The keyctl syscall functions are: This will try to update the specified key with the given payload, or it will return error EOPNOTSUPP if that function is not supported by the key - type. The process must also have permission to write to the key to be - able to update it. + type. The process must also have permission to write to the key to be able + to update it. The payload is of length plen, and may be absent or empty as for add_key(). @@ -422,8 +422,8 @@ The keyctl syscall functions are: long keyctl(KEYCTL_CHOWN, key_serial_t key, uid_t uid, gid_t gid); - This function permits a key's owner and group ID to be changed. Either - one of uid or gid can be set to -1 to suppress that change. + This function permits a key's owner and group ID to be changed. Either one + of uid or gid can be set to -1 to suppress that change. Only the superuser can change a key's owner to something other than the key's current owner. Similarly, only the superuser can change a key's @@ -484,12 +484,12 @@ The keyctl syscall functions are: long keyctl(KEYCTL_LINK, key_serial_t keyring, key_serial_t key); - This function creates a link from the keyring to the key. The process - must have write permission on the keyring and must have link permission - on the key. + This function creates a link from the keyring to the key. The process must + have write permission on the keyring and must have link permission on the + key. - Should the keyring not be a keyring, error ENOTDIR will result; and if - the keyring is full, error ENFILE will result. + Should the keyring not be a keyring, error ENOTDIR will result; and if the + keyring is full, error ENFILE will result. The link procedure checks the nesting of the keyrings, returning ELOOP if it appears to deep or EDEADLK if the link would introduce a cycle. @@ -503,8 +503,8 @@ The keyctl syscall functions are: specified key, and removes it if found. Subsequent links to that key are ignored. The process must have write permission on the keyring. - If the keyring is not a keyring, error ENOTDIR will result; and if the - key is not present, error ENOENT will be the result. + If the keyring is not a keyring, error ENOTDIR will result; and if the key + is not present, error ENOENT will be the result. (*) Search a keyring tree for a key: @@ -513,9 +513,9 @@ The keyctl syscall functions are: const char *type, const char *description, key_serial_t dest_keyring); - This searches the keyring tree headed by the specified keyring until a - key is found that matches the type and description criteria. Each keyring - is checked for keys before recursion into its children occurs. + This searches the keyring tree headed by the specified keyring until a key + is found that matches the type and description criteria. Each keyring is + checked for keys before recursion into its children occurs. The process must have search permission on the top level keyring, or else error EACCES will result. Only keyrings that the process has search @@ -549,8 +549,8 @@ The keyctl syscall functions are: As much of the data as can be fitted into the buffer will be copied to userspace if the buffer pointer is not NULL. - On a successful return, the function will always return the amount of - data available rather than the amount copied. + On a successful return, the function will always return the amount of data + available rather than the amount copied. (*) Instantiate a partially constructed key. @@ -568,8 +568,8 @@ The keyctl syscall functions are: it, and the key must be uninstantiated. If a keyring is specified (non-zero), the key will also be linked into - that keyring, however all the constraints applying in KEYCTL_LINK apply - in this case too. + that keyring, however all the constraints applying in KEYCTL_LINK apply in + this case too. The payload and plen arguments describe the payload data as for add_key(). @@ -587,8 +587,8 @@ The keyctl syscall functions are: it, and the key must be uninstantiated. If a keyring is specified (non-zero), the key will also be linked into - that keyring, however all the constraints applying in KEYCTL_LINK apply - in this case too. + that keyring, however all the constraints applying in KEYCTL_LINK apply in + this case too. =============== @@ -601,17 +601,14 @@ be broken down into two areas: keys and key types. Dealing with keys is fairly straightforward. Firstly, the kernel service registers its type, then it searches for a key of that type. It should retain the key as long as it has need of it, and then it should release it. For a -filesystem or device file, a search would probably be performed during the -open call, and the key released upon close. How to deal with conflicting keys -due to two different users opening the same file is left to the filesystem -author to solve. - -When accessing a key's payload data, key->lock should be at least read locked, -or else the data may be changed by an update being performed from userspace -whilst the driver or filesystem is trying to access it. If no update method is -supplied, then the key's payload may be accessed without holding a lock as -there is no way to change it, provided it can be guaranteed that the key's -type definition won't go away. +filesystem or device file, a search would probably be performed during the open +call, and the key released upon close. How to deal with conflicting keys due to +two different users opening the same file is left to the filesystem author to +solve. + +When accessing a key's payload contents, certain precautions must be taken to +prevent access vs modification races. See the section "Notes on accessing +payload contents" for more information. (*) To search for a key, call: @@ -690,6 +687,54 @@ type definition won't go away. void unregister_key_type(struct key_type *type); +=================================== +NOTES ON ACCESSING PAYLOAD CONTENTS +=================================== + +The simplest payload is just a number in key->payload.value. In this case, +there's no need to indulge in RCU or locking when accessing the payload. + +More complex payload contents must be allocated and a pointer to them set in +key->payload.data. One of the following ways must be selected to access the +data: + + (1) Unmodifyable key type. + + If the key type does not have a modify method, then the key's payload can + be accessed without any form of locking, provided that it's known to be + instantiated (uninstantiated keys cannot be "found"). + + (2) The key's semaphore. + + The semaphore could be used to govern access to the payload and to control + the payload pointer. It must be write-locked for modifications and would + have to be read-locked for general access. The disadvantage of doing this + is that the accessor may be required to sleep. + + (3) RCU. + + RCU must be used when the semaphore isn't already held; if the semaphore + is held then the contents can't change under you unexpectedly as the + semaphore must still be used to serialise modifications to the key. The + key management code takes care of this for the key type. + + However, this means using: + + rcu_read_lock() ... rcu_dereference() ... rcu_read_unlock() + + to read the pointer, and: + + rcu_dereference() ... rcu_assign_pointer() ... call_rcu() + + to set the pointer and dispose of the old contents after a grace period. + Note that only the key type should ever modify a key's payload. + + Furthermore, an RCU controlled payload must hold a struct rcu_head for the + use of call_rcu() and, if the payload is of variable size, the length of + the payload. key->datalen cannot be relied upon to be consistent with the + payload just dereferenced if the key's semaphore is not held. + + =================== DEFINING A KEY TYPE =================== @@ -717,15 +762,15 @@ The structure has a number of fields, some of which are mandatory: int key_payload_reserve(struct key *key, size_t datalen); - With the revised data length. Error EDQUOT will be returned if this is - not viable. + With the revised data length. Error EDQUOT will be returned if this is not + viable. (*) int (*instantiate)(struct key *key, const void *data, size_t datalen); This method is called to attach a payload to a key during construction. - The payload attached need not bear any relation to the data passed to - this function. + The payload attached need not bear any relation to the data passed to this + function. If the amount of data attached to the key differs from the size in keytype->def_datalen, then key_payload_reserve() should be called. @@ -734,38 +779,47 @@ The structure has a number of fields, some of which are mandatory: The fact that KEY_FLAG_INSTANTIATED is not set in key->flags prevents anything else from gaining access to the key. - This method may sleep if it wishes. + It is safe to sleep in this method. (*) int (*duplicate)(struct key *key, const struct key *source); If this type of key can be duplicated, then this method should be - provided. It is called to copy the payload attached to the source into - the new key. The data length on the new key will have been updated and - the quota adjusted already. + provided. It is called to copy the payload attached to the source into the + new key. The data length on the new key will have been updated and the + quota adjusted already. This method will be called with the source key's semaphore read-locked to - prevent its payload from being changed. It is safe to sleep here. + prevent its payload from being changed, thus RCU constraints need not be + applied to the source key. + + This method does not have to lock the destination key in order to attach a + payload. The fact that KEY_FLAG_INSTANTIATED is not set in key->flags + prevents anything else from gaining access to the key. + + It is safe to sleep in this method. (*) int (*update)(struct key *key, const void *data, size_t datalen); - If this type of key can be updated, then this method should be - provided. It is called to update a key's payload from the blob of data - provided. + If this type of key can be updated, then this method should be provided. + It is called to update a key's payload from the blob of data provided. key_payload_reserve() should be called if the data length might change - before any changes are actually made. Note that if this succeeds, the - type is committed to changing the key because it's already been altered, - so all memory allocation must be done first. + before any changes are actually made. Note that if this succeeds, the type + is committed to changing the key because it's already been altered, so all + memory allocation must be done first. + + The key will have its semaphore write-locked before this method is called, + but this only deters other writers; any changes to the key's payload must + be made under RCU conditions, and call_rcu() must be used to dispose of + the old payload. - key_payload_reserve() should be called with the key->lock write locked, - and the changes to the key's attached payload should be made before the - key is locked. + key_payload_reserve() should be called before the changes are made, but + after all allocations and other potentially failing function calls are + made. - The key will have its semaphore write-locked before this method is - called. Any changes to the key should be made with the key's rwlock - write-locked also. It is safe to sleep here. + It is safe to sleep in this method. (*) int (*match)(const struct key *key, const void *desc); @@ -782,12 +836,12 @@ The structure has a number of fields, some of which are mandatory: (*) void (*destroy)(struct key *key); - This method is optional. It is called to discard the payload data on a - key when it is being destroyed. + This method is optional. It is called to discard the payload data on a key + when it is being destroyed. - This method does not need to lock the key; it can consider the key as - being inaccessible. Note that the key's type may have changed before this - function is called. + This method does not need to lock the key to access the payload; it can + consider the key as being inaccessible at this time. Note that the key's + type may have been changed before this function is called. It is not safe to sleep in this method; the caller may hold spinlocks. @@ -797,26 +851,31 @@ The structure has a number of fields, some of which are mandatory: This method is optional. It is called during /proc/keys reading to summarise a key's description and payload in text form. - This method will be called with the key's rwlock read-locked. This will - prevent the key's payload and state changing; also the description should - not change. This also means it is not safe to sleep in this method. + This method will be called with the RCU read lock held. rcu_dereference() + should be used to read the payload pointer if the payload is to be + accessed. key->datalen cannot be trusted to stay consistent with the + contents of the payload. + + The description will not change, though the key's state may. + + It is not safe to sleep in this method; the RCU read lock is held by the + caller. (*) long (*read)(const struct key *key, char __user *buffer, size_t buflen); This method is optional. It is called by KEYCTL_READ to translate the - key's payload into something a blob of data for userspace to deal - with. Ideally, the blob should be in the same format as that passed in to - the instantiate and update methods. + key's payload into something a blob of data for userspace to deal with. + Ideally, the blob should be in the same format as that passed in to the + instantiate and update methods. If successful, the blob size that could be produced should be returned rather than the size copied. - This method will be called with the key's semaphore read-locked. This - will prevent the key's payload changing. It is not necessary to also - read-lock key->lock when accessing the key's payload. It is safe to sleep - in this method, such as might happen when the userspace buffer is - accessed. + This method will be called with the key's semaphore read-locked. This will + prevent the key's payload changing. It is not necessary to use RCU locking + when accessing the key's payload. It is safe to sleep in this method, such + as might happen when the userspace buffer is accessed. ============================ @@ -853,8 +912,8 @@ If it returns with the key remaining in the unconstructed state, the key will be marked as being negative, it will be added to the session keyring, and an error will be returned to the key requestor. -Supplementary information may be provided from whoever or whatever invoked -this service. This will be passed as the parameter. If no such +Supplementary information may be provided from whoever or whatever invoked this +service. This will be passed as the parameter. If no such information was made available, then "-" will be passed as this parameter instead. diff --git a/include/linux/key-ui.h b/include/linux/key-ui.h index 60cc7b762e78..159ca8d54e9a 100644 --- a/include/linux/key-ui.h +++ b/include/linux/key-ui.h @@ -31,8 +31,10 @@ extern spinlock_t key_serial_lock; * subscribed */ struct keyring_list { - unsigned maxkeys; /* max keys this list can hold */ - unsigned nkeys; /* number of keys currently held */ + struct rcu_head rcu; /* RCU deletion hook */ + unsigned short maxkeys; /* max keys this list can hold */ + unsigned short nkeys; /* number of keys currently held */ + unsigned short delkey; /* key to be unlinked by RCU */ struct key *keys[0]; }; diff --git a/include/linux/key.h b/include/linux/key.h index 6aa46d0e812f..2c24ffaca86f 100644 --- a/include/linux/key.h +++ b/include/linux/key.h @@ -18,7 +18,7 @@ #include #include #include -#include +#include #include #ifdef __KERNEL__ @@ -78,7 +78,6 @@ struct key { key_serial_t serial; /* key serial number */ struct rb_node serial_node; struct key_type *type; /* type of key */ - rwlock_t lock; /* examination vs change lock */ struct rw_semaphore sem; /* change vs change sem */ struct key_user *user; /* owner of this key */ time_t expiry; /* time at which key expires (or 0) */ @@ -86,14 +85,10 @@ struct key { gid_t gid; key_perm_t perm; /* access permissions */ unsigned short quotalen; /* length added to quota */ - unsigned short datalen; /* payload data length */ - unsigned short flags; /* status flags (change with lock writelocked) */ -#define KEY_FLAG_INSTANTIATED 0x00000001 /* set if key has been instantiated */ -#define KEY_FLAG_DEAD 0x00000002 /* set if key type has been deleted */ -#define KEY_FLAG_REVOKED 0x00000004 /* set if key had been revoked */ -#define KEY_FLAG_IN_QUOTA 0x00000008 /* set if key consumes quota */ -#define KEY_FLAG_USER_CONSTRUCT 0x00000010 /* set if key is being constructed in userspace */ -#define KEY_FLAG_NEGATIVE 0x00000020 /* set if key is negative */ + unsigned short datalen; /* payload data length + * - may not match RCU dereferenced payload + * - payload should contain own length + */ #ifdef KEY_DEBUGGING unsigned magic; @@ -101,6 +96,14 @@ struct key { #define KEY_DEBUG_MAGIC_X 0xf8e9dacbu #endif + unsigned long flags; /* status flags (change with bitops) */ +#define KEY_FLAG_INSTANTIATED 0 /* set if key has been instantiated */ +#define KEY_FLAG_DEAD 1 /* set if key type has been deleted */ +#define KEY_FLAG_REVOKED 2 /* set if key had been revoked */ +#define KEY_FLAG_IN_QUOTA 3 /* set if key consumes quota */ +#define KEY_FLAG_USER_CONSTRUCT 4 /* set if key is being constructed in userspace */ +#define KEY_FLAG_NEGATIVE 5 /* set if key is negative */ + /* the description string * - this is used to match a key against search criteria * - this should be a printable string @@ -250,6 +253,8 @@ extern int keyring_add_key(struct key *keyring, extern struct key *key_lookup(key_serial_t id); +extern void keyring_replace_payload(struct key *key, void *replacement); + #define key_serial(key) ((key) ? (key)->serial : 0) /* diff --git a/security/keys/key.c b/security/keys/key.c index 59402c843203..1fdfccb3fe43 100644 --- a/security/keys/key.c +++ b/security/keys/key.c @@ -294,7 +294,6 @@ struct key *key_alloc(struct key_type *type, const char *desc, } atomic_set(&key->usage, 1); - rwlock_init(&key->lock); init_rwsem(&key->sem); key->type = type; key->user = user; @@ -308,7 +307,7 @@ struct key *key_alloc(struct key_type *type, const char *desc, key->payload.data = NULL; if (!not_in_quota) - key->flags |= KEY_FLAG_IN_QUOTA; + key->flags |= 1 << KEY_FLAG_IN_QUOTA; memset(&key->type_data, 0, sizeof(key->type_data)); @@ -359,7 +358,7 @@ int key_payload_reserve(struct key *key, size_t datalen) key_check(key); /* contemplate the quota adjustment */ - if (delta != 0 && key->flags & KEY_FLAG_IN_QUOTA) { + if (delta != 0 && test_bit(KEY_FLAG_IN_QUOTA, &key->flags)) { spin_lock(&key->user->lock); if (delta > 0 && @@ -405,23 +404,17 @@ static int __key_instantiate_and_link(struct key *key, down_write(&key_construction_sem); /* can't instantiate twice */ - if (!(key->flags & KEY_FLAG_INSTANTIATED)) { + if (!test_bit(KEY_FLAG_INSTANTIATED, &key->flags)) { /* instantiate the key */ ret = key->type->instantiate(key, data, datalen); if (ret == 0) { /* mark the key as being instantiated */ - write_lock(&key->lock); - atomic_inc(&key->user->nikeys); - key->flags |= KEY_FLAG_INSTANTIATED; + set_bit(KEY_FLAG_INSTANTIATED, &key->flags); - if (key->flags & KEY_FLAG_USER_CONSTRUCT) { - key->flags &= ~KEY_FLAG_USER_CONSTRUCT; + if (test_and_clear_bit(KEY_FLAG_USER_CONSTRUCT, &key->flags)) awaken = 1; - } - - write_unlock(&key->lock); /* and link it into the destination keyring */ if (keyring) @@ -486,21 +479,17 @@ int key_negate_and_link(struct key *key, down_write(&key_construction_sem); /* can't instantiate twice */ - if (!(key->flags & KEY_FLAG_INSTANTIATED)) { + if (!test_bit(KEY_FLAG_INSTANTIATED, &key->flags)) { /* mark the key as being negatively instantiated */ - write_lock(&key->lock); - atomic_inc(&key->user->nikeys); - key->flags |= KEY_FLAG_INSTANTIATED | KEY_FLAG_NEGATIVE; + set_bit(KEY_FLAG_NEGATIVE, &key->flags); + set_bit(KEY_FLAG_INSTANTIATED, &key->flags); now = current_kernel_time(); key->expiry = now.tv_sec + timeout; - if (key->flags & KEY_FLAG_USER_CONSTRUCT) { - key->flags &= ~KEY_FLAG_USER_CONSTRUCT; + if (test_and_clear_bit(KEY_FLAG_USER_CONSTRUCT, &key->flags)) awaken = 1; - } - write_unlock(&key->lock); ret = 0; /* and link it into the destination keyring */ @@ -553,8 +542,10 @@ static void key_cleanup(void *data) rb_erase(&key->serial_node, &key_serial_tree); spin_unlock(&key_serial_lock); + key_check(key); + /* deal with the user's key tracking and quota */ - if (key->flags & KEY_FLAG_IN_QUOTA) { + if (test_bit(KEY_FLAG_IN_QUOTA, &key->flags)) { spin_lock(&key->user->lock); key->user->qnkeys--; key->user->qnbytes -= key->quotalen; @@ -562,7 +553,7 @@ static void key_cleanup(void *data) } atomic_dec(&key->user->nkeys); - if (key->flags & KEY_FLAG_INSTANTIATED) + if (test_bit(KEY_FLAG_INSTANTIATED, &key->flags)) atomic_dec(&key->user->nikeys); key_user_put(key->user); @@ -631,9 +622,9 @@ struct key *key_lookup(key_serial_t id) goto error; found: - /* pretent doesn't exist if it's dead */ + /* pretend it doesn't exist if it's dead */ if (atomic_read(&key->usage) == 0 || - (key->flags & KEY_FLAG_DEAD) || + test_bit(KEY_FLAG_DEAD, &key->flags) || key->type == &key_type_dead) goto not_found; @@ -708,12 +699,9 @@ static inline struct key *__key_update(struct key *key, const void *payload, ret = key->type->update(key, payload, plen); - if (ret == 0) { + if (ret == 0) /* updating a negative key instantiates it */ - write_lock(&key->lock); - key->flags &= ~KEY_FLAG_NEGATIVE; - write_unlock(&key->lock); - } + clear_bit(KEY_FLAG_NEGATIVE, &key->flags); up_write(&key->sem); @@ -841,12 +829,9 @@ int key_update(struct key *key, const void *payload, size_t plen) down_write(&key->sem); ret = key->type->update(key, payload, plen); - if (ret == 0) { + if (ret == 0) /* updating a negative key instantiates it */ - write_lock(&key->lock); - key->flags &= ~KEY_FLAG_NEGATIVE; - write_unlock(&key->lock); - } + clear_bit(KEY_FLAG_NEGATIVE, &key->flags); up_write(&key->sem); } @@ -892,10 +877,7 @@ struct key *key_duplicate(struct key *source, const char *desc) goto error2; atomic_inc(&key->user->nikeys); - - write_lock(&key->lock); - key->flags |= KEY_FLAG_INSTANTIATED; - write_unlock(&key->lock); + set_bit(KEY_FLAG_INSTANTIATED, &key->flags); error_k: up_read(&key_types_sem); @@ -922,9 +904,7 @@ void key_revoke(struct key *key) /* make sure no one's trying to change or use the key when we mark * it */ down_write(&key->sem); - write_lock(&key->lock); - key->flags |= KEY_FLAG_REVOKED; - write_unlock(&key->lock); + set_bit(KEY_FLAG_REVOKED, &key->flags); up_write(&key->sem); } /* end key_revoke() */ @@ -975,24 +955,33 @@ void unregister_key_type(struct key_type *ktype) /* withdraw the key type */ list_del_init(&ktype->link); - /* need to withdraw all keys of this type */ + /* mark all the keys of this type dead */ spin_lock(&key_serial_lock); for (_n = rb_first(&key_serial_tree); _n; _n = rb_next(_n)) { key = rb_entry(_n, struct key, serial_node); - if (key->type != ktype) - continue; + if (key->type == ktype) + key->type = &key_type_dead; + } + + spin_unlock(&key_serial_lock); + + /* make sure everyone revalidates their keys */ + synchronize_kernel(); + + /* we should now be able to destroy the payloads of all the keys of + * this type with impunity */ + spin_lock(&key_serial_lock); - write_lock(&key->lock); - key->type = &key_type_dead; - write_unlock(&key->lock); + for (_n = rb_first(&key_serial_tree); _n; _n = rb_next(_n)) { + key = rb_entry(_n, struct key, serial_node); - /* there shouldn't be anyone looking at the description or - * payload now */ - if (ktype->destroy) - ktype->destroy(key); - memset(&key->payload, 0xbd, sizeof(key->payload)); + if (key->type == ktype) { + if (ktype->destroy) + ktype->destroy(key); + memset(&key->payload, 0xbd, sizeof(key->payload)); + } } spin_unlock(&key_serial_lock); @@ -1037,4 +1026,5 @@ void __init key_init(void) /* link the two root keyrings together */ key_link(&root_session_keyring, &root_user_keyring); + } /* end key_init() */ diff --git a/security/keys/keyctl.c b/security/keys/keyctl.c index dc0011b3fac9..cedb7326de29 100644 --- a/security/keys/keyctl.c +++ b/security/keys/keyctl.c @@ -728,7 +728,6 @@ long keyctl_chown_key(key_serial_t id, uid_t uid, gid_t gid) /* make the changes with the locks held to prevent chown/chown races */ ret = -EACCES; down_write(&key->sem); - write_lock(&key->lock); if (!capable(CAP_SYS_ADMIN)) { /* only the sysadmin can chown a key to some other UID */ @@ -755,7 +754,6 @@ long keyctl_chown_key(key_serial_t id, uid_t uid, gid_t gid) ret = 0; no_access: - write_unlock(&key->lock); up_write(&key->sem); key_put(key); error: @@ -784,26 +782,19 @@ long keyctl_setperm_key(key_serial_t id, key_perm_t perm) goto error; } - /* make the changes with the locks held to prevent chown/chmod - * races */ + /* make the changes with the locks held to prevent chown/chmod races */ ret = -EACCES; down_write(&key->sem); - write_lock(&key->lock); - /* if we're not the sysadmin, we can only chmod a key that we - * own */ - if (!capable(CAP_SYS_ADMIN) && key->uid != current->fsuid) - goto no_access; - - /* changing the permissions mask */ - key->perm = perm; - ret = 0; + /* if we're not the sysadmin, we can only change a key that we own */ + if (capable(CAP_SYS_ADMIN) || key->uid == current->fsuid) { + key->perm = perm; + ret = 0; + } - no_access: - write_unlock(&key->lock); up_write(&key->sem); key_put(key); - error: +error: return ret; } /* end keyctl_setperm_key() */ diff --git a/security/keys/keyring.c b/security/keys/keyring.c index e2ab4f8e7481..c9a5de197487 100644 --- a/security/keys/keyring.c +++ b/security/keys/keyring.c @@ -132,10 +132,17 @@ static int keyring_duplicate(struct key *keyring, const struct key *source) (PAGE_SIZE - sizeof(*klist)) / sizeof(struct key); ret = 0; - sklist = source->payload.subscriptions; - if (sklist && sklist->nkeys > 0) { + /* find out how many keys are currently linked */ + rcu_read_lock(); + sklist = rcu_dereference(source->payload.subscriptions); + max = 0; + if (sklist) max = sklist->nkeys; + rcu_read_unlock(); + + /* allocate a new payload and stuff load with key links */ + if (max > 0) { BUG_ON(max > limit); max = (max + 3) & ~3; @@ -148,6 +155,10 @@ static int keyring_duplicate(struct key *keyring, const struct key *source) if (!klist) goto error; + /* set links */ + rcu_read_lock(); + sklist = rcu_dereference(source->payload.subscriptions); + klist->maxkeys = max; klist->nkeys = sklist->nkeys; memcpy(klist->keys, @@ -157,7 +168,9 @@ static int keyring_duplicate(struct key *keyring, const struct key *source) for (loop = klist->nkeys - 1; loop >= 0; loop--) atomic_inc(&klist->keys[loop]->usage); - keyring->payload.subscriptions = klist; + rcu_read_unlock(); + + rcu_assign_pointer(keyring->payload.subscriptions, klist); ret = 0; } @@ -192,7 +205,7 @@ static void keyring_destroy(struct key *keyring) write_unlock(&keyring_name_lock); } - klist = keyring->payload.subscriptions; + klist = rcu_dereference(keyring->payload.subscriptions); if (klist) { for (loop = klist->nkeys - 1; loop >= 0; loop--) key_put(klist->keys[loop]); @@ -216,17 +229,20 @@ static void keyring_describe(const struct key *keyring, struct seq_file *m) seq_puts(m, "[anon]"); } - klist = keyring->payload.subscriptions; + rcu_read_lock(); + klist = rcu_dereference(keyring->payload.subscriptions); if (klist) seq_printf(m, ": %u/%u", klist->nkeys, klist->maxkeys); else seq_puts(m, ": empty"); + rcu_read_unlock(); } /* end keyring_describe() */ /*****************************************************************************/ /* * read a list of key IDs from the keyring's contents + * - the keyring's semaphore is read-locked */ static long keyring_read(const struct key *keyring, char __user *buffer, size_t buflen) @@ -237,7 +253,7 @@ static long keyring_read(const struct key *keyring, int loop, ret; ret = 0; - klist = keyring->payload.subscriptions; + klist = rcu_dereference(keyring->payload.subscriptions); if (klist) { /* calculate how much data we could return */ @@ -320,7 +336,7 @@ struct key *keyring_search_aux(struct key *keyring, key_match_func_t match) { struct { - struct key *keyring; + struct keyring_list *keylist; int kix; } stack[KEYRING_SEARCH_MAX_DEPTH]; @@ -328,10 +344,12 @@ struct key *keyring_search_aux(struct key *keyring, struct timespec now; struct key *key; long err; - int sp, psp, kix; + int sp, kix; key_check(keyring); + rcu_read_lock(); + /* top keyring must have search permission to begin the search */ key = ERR_PTR(-EACCES); if (!key_permission(keyring, KEY_SEARCH)) @@ -347,11 +365,10 @@ struct key *keyring_search_aux(struct key *keyring, /* start processing a new keyring */ descend: - read_lock(&keyring->lock); - if (keyring->flags & KEY_FLAG_REVOKED) + if (test_bit(KEY_FLAG_REVOKED, &keyring->flags)) goto not_this_keyring; - keylist = keyring->payload.subscriptions; + keylist = rcu_dereference(keyring->payload.subscriptions); if (!keylist) goto not_this_keyring; @@ -364,7 +381,7 @@ struct key *keyring_search_aux(struct key *keyring, continue; /* skip revoked keys and expired keys */ - if (key->flags & KEY_FLAG_REVOKED) + if (test_bit(KEY_FLAG_REVOKED, &key->flags)) continue; if (key->expiry && now.tv_sec >= key->expiry) @@ -379,7 +396,7 @@ struct key *keyring_search_aux(struct key *keyring, continue; /* we set a different error code if we find a negative key */ - if (key->flags & KEY_FLAG_NEGATIVE) { + if (test_bit(KEY_FLAG_NEGATIVE, &key->flags)) { err = -ENOKEY; continue; } @@ -390,48 +407,37 @@ struct key *keyring_search_aux(struct key *keyring, /* search through the keyrings nested in this one */ kix = 0; ascend: - while (kix < keylist->nkeys) { + for (; kix < keylist->nkeys; kix++) { key = keylist->keys[kix]; if (key->type != &key_type_keyring) - goto next; + continue; /* recursively search nested keyrings * - only search keyrings for which we have search permission */ if (sp >= KEYRING_SEARCH_MAX_DEPTH) - goto next; + continue; if (!key_permission(key, KEY_SEARCH)) - goto next; - - /* evade loops in the keyring tree */ - for (psp = 0; psp < sp; psp++) - if (stack[psp].keyring == keyring) - goto next; + continue; /* stack the current position */ - stack[sp].keyring = keyring; + stack[sp].keylist = keylist; stack[sp].kix = kix; sp++; /* begin again with the new keyring */ keyring = key; goto descend; - - next: - kix++; } /* the keyring we're looking at was disqualified or didn't contain a * matching key */ not_this_keyring: - read_unlock(&keyring->lock); - if (sp > 0) { /* resume the processing of a keyring higher up in the tree */ sp--; - keyring = stack[sp].keyring; - keylist = keyring->payload.subscriptions; + keylist = stack[sp].keylist; kix = stack[sp].kix + 1; goto ascend; } @@ -442,16 +448,9 @@ struct key *keyring_search_aux(struct key *keyring, /* we found a viable match */ found: atomic_inc(&key->usage); - read_unlock(&keyring->lock); - - /* unwind the keyring stack */ - while (sp > 0) { - sp--; - read_unlock(&stack[sp].keyring->lock); - } - key_check(key); error: + rcu_read_unlock(); return key; } /* end keyring_search_aux() */ @@ -489,7 +488,9 @@ struct key *__keyring_search_one(struct key *keyring, struct key *key; int loop; - klist = keyring->payload.subscriptions; + rcu_read_lock(); + + klist = rcu_dereference(keyring->payload.subscriptions); if (klist) { for (loop = 0; loop < klist->nkeys; loop++) { key = klist->keys[loop]; @@ -497,7 +498,7 @@ struct key *__keyring_search_one(struct key *keyring, if (key->type == ktype && key->type->match(key, description) && key_permission(key, perm) && - !(key->flags & KEY_FLAG_REVOKED) + !test_bit(KEY_FLAG_REVOKED, &key->flags) ) goto found; } @@ -509,6 +510,7 @@ struct key *__keyring_search_one(struct key *keyring, found: atomic_inc(&key->usage); error: + rcu_read_unlock(); return key; } /* end __keyring_search_one() */ @@ -540,7 +542,7 @@ struct key *find_keyring_by_name(const char *name, key_serial_t bound) &keyring_name_hash[bucket], type_data.link ) { - if (keyring->flags & KEY_FLAG_REVOKED) + if (test_bit(KEY_FLAG_REVOKED, &keyring->flags)) continue; if (strcmp(keyring->description, name) != 0) @@ -579,7 +581,7 @@ struct key *find_keyring_by_name(const char *name, key_serial_t bound) static int keyring_detect_cycle(struct key *A, struct key *B) { struct { - struct key *subtree; + struct keyring_list *keylist; int kix; } stack[KEYRING_SEARCH_MAX_DEPTH]; @@ -587,20 +589,21 @@ static int keyring_detect_cycle(struct key *A, struct key *B) struct key *subtree, *key; int sp, kix, ret; + rcu_read_lock(); + ret = -EDEADLK; if (A == B) - goto error; + goto cycle_detected; subtree = B; sp = 0; /* start processing a new keyring */ descend: - read_lock(&subtree->lock); - if (subtree->flags & KEY_FLAG_REVOKED) + if (test_bit(KEY_FLAG_REVOKED, &subtree->flags)) goto not_this_keyring; - keylist = subtree->payload.subscriptions; + keylist = rcu_dereference(subtree->payload.subscriptions); if (!keylist) goto not_this_keyring; kix = 0; @@ -619,7 +622,7 @@ static int keyring_detect_cycle(struct key *A, struct key *B) goto too_deep; /* stack the current position */ - stack[sp].subtree = subtree; + stack[sp].keylist = keylist; stack[sp].kix = kix; sp++; @@ -632,13 +635,10 @@ static int keyring_detect_cycle(struct key *A, struct key *B) /* the keyring we're looking at was disqualified or didn't contain a * matching key */ not_this_keyring: - read_unlock(&subtree->lock); - if (sp > 0) { /* resume the checking of a keyring higher up in the tree */ sp--; - subtree = stack[sp].subtree; - keylist = subtree->payload.subscriptions; + keylist = stack[sp].keylist; kix = stack[sp].kix + 1; goto ascend; } @@ -646,30 +646,36 @@ static int keyring_detect_cycle(struct key *A, struct key *B) ret = 0; /* no cycles detected */ error: + rcu_read_unlock(); return ret; too_deep: ret = -ELOOP; - goto error_unwind; + goto error; + cycle_detected: ret = -EDEADLK; - error_unwind: - read_unlock(&subtree->lock); - - /* unwind the keyring stack */ - while (sp > 0) { - sp--; - read_unlock(&stack[sp].subtree->lock); - } - goto error; } /* end keyring_detect_cycle() */ +/*****************************************************************************/ +/* + * dispose of a keyring list after the RCU grace period + */ +static void keyring_link_rcu_disposal(struct rcu_head *rcu) +{ + struct keyring_list *klist = + container_of(rcu, struct keyring_list, rcu); + + kfree(klist); + +} /* end keyring_link_rcu_disposal() */ + /*****************************************************************************/ /* * link a key into to a keyring - * - must be called with the keyring's semaphore held + * - must be called with the keyring's semaphore write-locked */ int __key_link(struct key *keyring, struct key *key) { @@ -679,7 +685,7 @@ int __key_link(struct key *keyring, struct key *key) int ret; ret = -EKEYREVOKED; - if (keyring->flags & KEY_FLAG_REVOKED) + if (test_bit(KEY_FLAG_REVOKED, &keyring->flags)) goto error; ret = -ENOTDIR; @@ -710,9 +716,10 @@ int __key_link(struct key *keyring, struct key *key) /* there's sufficient slack space to add directly */ atomic_inc(&key->usage); - write_lock(&keyring->lock); - klist->keys[klist->nkeys++] = key; - write_unlock(&keyring->lock); + klist->keys[klist->nkeys] = key; + smp_wmb(); + klist->nkeys++; + smp_wmb(); ret = 0; } @@ -723,6 +730,8 @@ int __key_link(struct key *keyring, struct key *key) max += klist->maxkeys; ret = -ENFILE; + if (max > 65535) + goto error3; size = sizeof(*klist) + sizeof(*key) * max; if (size > PAGE_SIZE) goto error3; @@ -743,14 +752,13 @@ int __key_link(struct key *keyring, struct key *key) /* add the key into the new space */ atomic_inc(&key->usage); - - write_lock(&keyring->lock); - keyring->payload.subscriptions = nklist; nklist->keys[nklist->nkeys++] = key; - write_unlock(&keyring->lock); + + rcu_assign_pointer(keyring->payload.subscriptions, nklist); /* dispose of the old keyring list */ - kfree(klist); + if (klist) + call_rcu(&klist->rcu, keyring_link_rcu_disposal); ret = 0; } @@ -789,13 +797,28 @@ int key_link(struct key *keyring, struct key *key) EXPORT_SYMBOL(key_link); +/*****************************************************************************/ +/* + * dispose of a keyring list after the RCU grace period, freeing the unlinked + * key + */ +static void keyring_unlink_rcu_disposal(struct rcu_head *rcu) +{ + struct keyring_list *klist = + container_of(rcu, struct keyring_list, rcu); + + key_put(klist->keys[klist->delkey]); + kfree(klist); + +} /* end keyring_unlink_rcu_disposal() */ + /*****************************************************************************/ /* * unlink the first link to a key from a keyring */ int key_unlink(struct key *keyring, struct key *key) { - struct keyring_list *klist; + struct keyring_list *klist, *nklist; int loop, ret; key_check(keyring); @@ -819,36 +842,69 @@ int key_unlink(struct key *keyring, struct key *key) ret = -ENOENT; goto error; - key_is_present: +key_is_present: + /* we need to copy the key list for RCU purposes */ + nklist = kmalloc(sizeof(*klist) + sizeof(*key) * klist->maxkeys, + GFP_KERNEL); + if (!nklist) + goto nomem; + nklist->maxkeys = klist->maxkeys; + nklist->nkeys = klist->nkeys - 1; + + if (loop > 0) + memcpy(&nklist->keys[0], + &klist->keys[0], + loop * sizeof(klist->keys[0])); + + if (loop < nklist->nkeys) + memcpy(&nklist->keys[loop], + &klist->keys[loop + 1], + (nklist->nkeys - loop) * sizeof(klist->keys[0])); + /* adjust the user's quota */ key_payload_reserve(keyring, keyring->datalen - KEYQUOTA_LINK_BYTES); - /* shuffle down the key pointers - * - it might be worth shrinking the allocated memory, but that runs - * the risk of ENOMEM as we would have to copy - */ - write_lock(&keyring->lock); + rcu_assign_pointer(keyring->payload.subscriptions, nklist); - klist->nkeys--; - if (loop < klist->nkeys) - memcpy(&klist->keys[loop], - &klist->keys[loop + 1], - (klist->nkeys - loop) * sizeof(struct key *)); + up_write(&keyring->sem); - write_unlock(&keyring->lock); + /* schedule for later cleanup */ + klist->delkey = loop; + call_rcu(&klist->rcu, keyring_unlink_rcu_disposal); - up_write(&keyring->sem); - key_put(key); ret = 0; - error: +error: return ret; +nomem: + ret = -ENOMEM; + up_write(&keyring->sem); + goto error; } /* end key_unlink() */ EXPORT_SYMBOL(key_unlink); +/*****************************************************************************/ +/* + * dispose of a keyring list after the RCU grace period, releasing the keys it + * links to + */ +static void keyring_clear_rcu_disposal(struct rcu_head *rcu) +{ + struct keyring_list *klist; + int loop; + + klist = container_of(rcu, struct keyring_list, rcu); + + for (loop = klist->nkeys - 1; loop >= 0; loop--) + key_put(klist->keys[loop]); + + kfree(klist); + +} /* end keyring_clear_rcu_disposal() */ + /*****************************************************************************/ /* * clear the specified process keyring @@ -857,7 +913,7 @@ EXPORT_SYMBOL(key_unlink); int keyring_clear(struct key *keyring) { struct keyring_list *klist; - int loop, ret; + int ret; ret = -ENOTDIR; if (keyring->type == &key_type_keyring) { @@ -870,20 +926,15 @@ int keyring_clear(struct key *keyring) key_payload_reserve(keyring, sizeof(struct keyring_list)); - write_lock(&keyring->lock); - keyring->payload.subscriptions = NULL; - write_unlock(&keyring->lock); + rcu_assign_pointer(keyring->payload.subscriptions, + NULL); } up_write(&keyring->sem); /* free the keys after the locks have been dropped */ - if (klist) { - for (loop = klist->nkeys - 1; loop >= 0; loop--) - key_put(klist->keys[loop]); - - kfree(klist); - } + if (klist) + call_rcu(&klist->rcu, keyring_clear_rcu_disposal); ret = 0; } diff --git a/security/keys/proc.c b/security/keys/proc.c index 91343b85c39c..c55cf1fd0826 100644 --- a/security/keys/proc.c +++ b/security/keys/proc.c @@ -140,7 +140,7 @@ static int proc_keys_show(struct seq_file *m, void *v) now = current_kernel_time(); - read_lock(&key->lock); + rcu_read_lock(); /* come up with a suitable timeout value */ if (key->expiry == 0) { @@ -164,14 +164,17 @@ static int proc_keys_show(struct seq_file *m, void *v) sprintf(xbuf, "%luw", timo / (60*60*24*7)); } +#define showflag(KEY, LETTER, FLAG) \ + (test_bit(FLAG, &(KEY)->flags) ? LETTER : '-') + seq_printf(m, "%08x %c%c%c%c%c%c %5d %4s %06x %5d %5d %-9.9s ", key->serial, - key->flags & KEY_FLAG_INSTANTIATED ? 'I' : '-', - key->flags & KEY_FLAG_REVOKED ? 'R' : '-', - key->flags & KEY_FLAG_DEAD ? 'D' : '-', - key->flags & KEY_FLAG_IN_QUOTA ? 'Q' : '-', - key->flags & KEY_FLAG_USER_CONSTRUCT ? 'U' : '-', - key->flags & KEY_FLAG_NEGATIVE ? 'N' : '-', + showflag(key, 'I', KEY_FLAG_INSTANTIATED), + showflag(key, 'R', KEY_FLAG_REVOKED), + showflag(key, 'D', KEY_FLAG_DEAD), + showflag(key, 'Q', KEY_FLAG_IN_QUOTA), + showflag(key, 'U', KEY_FLAG_USER_CONSTRUCT), + showflag(key, 'N', KEY_FLAG_NEGATIVE), atomic_read(&key->usage), xbuf, key->perm, @@ -179,11 +182,13 @@ static int proc_keys_show(struct seq_file *m, void *v) key->gid, key->type->name); +#undef showflag + if (key->type->describe) key->type->describe(key, m); seq_putc(m, '\n'); - read_unlock(&key->lock); + rcu_read_unlock(); return 0; diff --git a/security/keys/process_keys.c b/security/keys/process_keys.c index 2eb0e471cd40..059c350cac46 100644 --- a/security/keys/process_keys.c +++ b/security/keys/process_keys.c @@ -38,10 +38,9 @@ struct key root_user_keyring = { .serial = 2, .type = &key_type_keyring, .user = &root_key_user, - .lock = RW_LOCK_UNLOCKED, .sem = __RWSEM_INITIALIZER(root_user_keyring.sem), .perm = KEY_USR_ALL, - .flags = KEY_FLAG_INSTANTIATED, + .flags = 1 << KEY_FLAG_INSTANTIATED, .description = "_uid.0", #ifdef KEY_DEBUGGING .magic = KEY_DEBUG_MAGIC, @@ -54,10 +53,9 @@ struct key root_session_keyring = { .serial = 1, .type = &key_type_keyring, .user = &root_key_user, - .lock = RW_LOCK_UNLOCKED, .sem = __RWSEM_INITIALIZER(root_session_keyring.sem), .perm = KEY_USR_ALL, - .flags = KEY_FLAG_INSTANTIATED, + .flags = 1 << KEY_FLAG_INSTANTIATED, .description = "_uid_ses.0", #ifdef KEY_DEBUGGING .magic = KEY_DEBUG_MAGIC, @@ -349,9 +347,7 @@ void key_fsuid_changed(struct task_struct *tsk) /* update the ownership of the thread keyring */ if (tsk->thread_keyring) { down_write(&tsk->thread_keyring->sem); - write_lock(&tsk->thread_keyring->lock); tsk->thread_keyring->uid = tsk->fsuid; - write_unlock(&tsk->thread_keyring->lock); up_write(&tsk->thread_keyring->sem); } @@ -366,9 +362,7 @@ void key_fsgid_changed(struct task_struct *tsk) /* update the ownership of the thread keyring */ if (tsk->thread_keyring) { down_write(&tsk->thread_keyring->sem); - write_lock(&tsk->thread_keyring->lock); tsk->thread_keyring->gid = tsk->fsgid; - write_unlock(&tsk->thread_keyring->lock); up_write(&tsk->thread_keyring->sem); } @@ -588,7 +582,7 @@ struct key *lookup_user_key(key_serial_t id, int create, int partial, } ret = -EIO; - if (!partial && !(key->flags & KEY_FLAG_INSTANTIATED)) + if (!partial && !test_bit(KEY_FLAG_INSTANTIATED, &key->flags)) goto invalid_key; ret = -EACCES; diff --git a/security/keys/request_key.c b/security/keys/request_key.c index 9705b1aeba5d..1f6c0940297f 100644 --- a/security/keys/request_key.c +++ b/security/keys/request_key.c @@ -105,7 +105,7 @@ static struct key *__request_key_construction(struct key_type *type, struct key_construction cons; struct timespec now; struct key *key; - int ret, negative; + int ret, negated; /* create a key and add it to the queue */ key = key_alloc(type, description, @@ -113,9 +113,7 @@ static struct key *__request_key_construction(struct key_type *type, if (IS_ERR(key)) goto alloc_failed; - write_lock(&key->lock); - key->flags |= KEY_FLAG_USER_CONSTRUCT; - write_unlock(&key->lock); + set_bit(KEY_FLAG_USER_CONSTRUCT, &key->flags); cons.key = key; list_add_tail(&cons.link, &key->user->consq); @@ -130,7 +128,7 @@ static struct key *__request_key_construction(struct key_type *type, /* if the key wasn't instantiated, then we want to give an error */ ret = -ENOKEY; - if (!(key->flags & KEY_FLAG_INSTANTIATED)) + if (!test_bit(KEY_FLAG_INSTANTIATED, &key->flags)) goto request_failed; down_write(&key_construction_sem); @@ -139,7 +137,7 @@ static struct key *__request_key_construction(struct key_type *type, /* also give an error if the key was negatively instantiated */ check_not_negative: - if (key->flags & KEY_FLAG_NEGATIVE) { + if (test_bit(KEY_FLAG_NEGATIVE, &key->flags)) { key_put(key); key = ERR_PTR(-ENOKEY); } @@ -152,24 +150,23 @@ static struct key *__request_key_construction(struct key_type *type, * - remove from construction queue * - mark the key as dead */ - negative = 0; + negated = 0; down_write(&key_construction_sem); list_del(&cons.link); - write_lock(&key->lock); - key->flags &= ~KEY_FLAG_USER_CONSTRUCT; - /* check it didn't get instantiated between the check and the down */ - if (!(key->flags & KEY_FLAG_INSTANTIATED)) { - key->flags |= KEY_FLAG_INSTANTIATED | KEY_FLAG_NEGATIVE; - negative = 1; + if (!test_bit(KEY_FLAG_INSTANTIATED, &key->flags)) { + set_bit(KEY_FLAG_NEGATIVE, &key->flags); + set_bit(KEY_FLAG_INSTANTIATED, &key->flags); + negated = 1; } - write_unlock(&key->lock); + clear_bit(KEY_FLAG_USER_CONSTRUCT, &key->flags); + up_write(&key_construction_sem); - if (!negative) + if (!negated) goto check_not_negative; /* surprisingly, the key got * instantiated */ @@ -250,7 +247,7 @@ static struct key *request_key_construction(struct key_type *type, for (;;) { set_current_state(TASK_UNINTERRUPTIBLE); - if (!(ckey->flags & KEY_FLAG_USER_CONSTRUCT)) + if (!test_bit(KEY_FLAG_USER_CONSTRUCT, &ckey->flags)) break; schedule(); } @@ -339,7 +336,8 @@ int key_validate(struct key *key) if (key) { /* check it's still accessible */ ret = -EKEYREVOKED; - if (key->flags & (KEY_FLAG_REVOKED | KEY_FLAG_DEAD)) + if (test_bit(KEY_FLAG_REVOKED, &key->flags) || + test_bit(KEY_FLAG_DEAD, &key->flags)) goto error; /* check it hasn't expired */ diff --git a/security/keys/user_defined.c b/security/keys/user_defined.c index 8d65b3a28129..c33d3614a0db 100644 --- a/security/keys/user_defined.c +++ b/security/keys/user_defined.c @@ -42,12 +42,19 @@ struct key_type key_type_user = { .read = user_read, }; +struct user_key_payload { + struct rcu_head rcu; /* RCU destructor */ + unsigned short datalen; /* length of this data */ + char data[0]; /* actual data */ +}; + /*****************************************************************************/ /* * instantiate a user defined key */ static int user_instantiate(struct key *key, const void *data, size_t datalen) { + struct user_key_payload *upayload; int ret; ret = -EINVAL; @@ -58,13 +65,15 @@ static int user_instantiate(struct key *key, const void *data, size_t datalen) if (ret < 0) goto error; - /* attach the data */ ret = -ENOMEM; - key->payload.data = kmalloc(datalen, GFP_KERNEL); - if (!key->payload.data) + upayload = kmalloc(sizeof(*upayload) + datalen, GFP_KERNEL); + if (!upayload) goto error; - memcpy(key->payload.data, data, datalen); + /* attach the data */ + upayload->datalen = datalen; + memcpy(upayload->data, data, datalen); + rcu_assign_pointer(key->payload.data, upayload); ret = 0; error: @@ -75,18 +84,25 @@ static int user_instantiate(struct key *key, const void *data, size_t datalen) /*****************************************************************************/ /* * duplicate a user defined key + * - both keys' semaphores are locked against further modification + * - the new key cannot yet be accessed */ static int user_duplicate(struct key *key, const struct key *source) { + struct user_key_payload *upayload, *spayload; int ret; /* just copy the payload */ ret = -ENOMEM; - key->payload.data = kmalloc(source->datalen, GFP_KERNEL); + upayload = kmalloc(sizeof(*upayload) + source->datalen, GFP_KERNEL); + if (upayload) { + spayload = rcu_dereference(source->payload.data); + BUG_ON(source->datalen != spayload->datalen); - if (key->payload.data) { - key->datalen = source->datalen; - memcpy(key->payload.data, source->payload.data, source->datalen); + upayload->datalen = key->datalen = spayload->datalen; + memcpy(upayload->data, spayload->data, key->datalen); + + key->payload.data = upayload; ret = 0; } @@ -94,42 +110,56 @@ static int user_duplicate(struct key *key, const struct key *source) } /* end user_duplicate() */ +/*****************************************************************************/ +/* + * dispose of the old data from an updated user defined key + */ +static void user_update_rcu_disposal(struct rcu_head *rcu) +{ + struct user_key_payload *upayload; + + upayload = container_of(rcu, struct user_key_payload, rcu); + + kfree(upayload); + +} /* end user_update_rcu_disposal() */ + /*****************************************************************************/ /* * update a user defined key + * - the key's semaphore is write-locked */ static int user_update(struct key *key, const void *data, size_t datalen) { - void *new, *zap; + struct user_key_payload *upayload, *zap; int ret; ret = -EINVAL; if (datalen <= 0 || datalen > 32767 || !data) goto error; - /* copy the data */ + /* construct a replacement payload */ ret = -ENOMEM; - new = kmalloc(datalen, GFP_KERNEL); - if (!new) + upayload = kmalloc(sizeof(*upayload) + datalen, GFP_KERNEL); + if (!upayload) goto error; - memcpy(new, data, datalen); + upayload->datalen = datalen; + memcpy(upayload->data, data, datalen); /* check the quota and attach the new data */ - zap = new; - write_lock(&key->lock); + zap = upayload; ret = key_payload_reserve(key, datalen); if (ret == 0) { /* attach the new data, displacing the old */ zap = key->payload.data; - key->payload.data = new; + rcu_assign_pointer(key->payload.data, upayload); key->expiry = 0; } - write_unlock(&key->lock); - kfree(zap); + call_rcu(&zap->rcu, user_update_rcu_disposal); error: return ret; @@ -152,13 +182,15 @@ static int user_match(const struct key *key, const void *description) */ static void user_destroy(struct key *key) { - kfree(key->payload.data); + struct user_key_payload *upayload = key->payload.data; + + kfree(upayload); } /* end user_destroy() */ /*****************************************************************************/ /* - * describe the user + * describe the user key */ static void user_describe(const struct key *key, struct seq_file *m) { @@ -171,18 +203,23 @@ static void user_describe(const struct key *key, struct seq_file *m) /*****************************************************************************/ /* * read the key data + * - the key's semaphore is read-locked */ static long user_read(const struct key *key, char __user *buffer, size_t buflen) { - long ret = key->datalen; + struct user_key_payload *upayload; + long ret; + + upayload = rcu_dereference(key->payload.data); + ret = upayload->datalen; /* we can return the data as is */ if (buffer && buflen > 0) { - if (buflen > key->datalen) - buflen = key->datalen; + if (buflen > upayload->datalen) + buflen = upayload->datalen; - if (copy_to_user(buffer, key->payload.data, buflen) != 0) + if (copy_to_user(buffer, upayload->data, buflen) != 0) ret = -EFAULT; } -- cgit v1.2.3 From 7888e7ff4ee579442128d7d12a9c9dbf2cf7de6a Mon Sep 17 00:00:00 2001 From: David Howells Date: Thu, 23 Jun 2005 22:00:51 -0700 Subject: [PATCH] Keys: Pass session keyring to call_usermodehelper() The attached patch makes it possible to pass a session keyring through to the process spawned by call_usermodehelper(). This allows patch 3/3 to pass an authorisation key through to /sbin/request-key, thus permitting better access controls when doing just-in-time key creation. Signed-Off-By: David Howells Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/key.h | 10 +++++++++- include/linux/kmod.h | 13 ++++++++++++- kernel/kmod.c | 17 +++++++++++++---- security/keys/request_key.c | 2 +- 4 files changed, 35 insertions(+), 7 deletions(-) (limited to 'security') diff --git a/include/linux/key.h b/include/linux/key.h index 2c24ffaca86f..2bfbf88d2740 100644 --- a/include/linux/key.h +++ b/include/linux/key.h @@ -273,14 +273,22 @@ extern void key_fsuid_changed(struct task_struct *tsk); extern void key_fsgid_changed(struct task_struct *tsk); extern void key_init(void); +#define __install_session_keyring(tsk, keyring) \ +({ \ + struct key *old_session = tsk->signal->session_keyring; \ + tsk->signal->session_keyring = keyring; \ + old_session; \ +}) + #else /* CONFIG_KEYS */ #define key_validate(k) 0 #define key_serial(k) 0 -#define key_get(k) NULL +#define key_get(k) ({ NULL; }) #define key_put(k) do { } while(0) #define alloc_uid_keyring(u) 0 #define switch_uid_keyring(u) do { } while(0) +#define __install_session_keyring(t, k) ({ NULL; }) #define copy_keys(f,t) 0 #define copy_thread_group_keys(t) 0 #define exit_keys(t) do { } while(0) diff --git a/include/linux/kmod.h b/include/linux/kmod.h index 95d0e4b0814d..e4a231549407 100644 --- a/include/linux/kmod.h +++ b/include/linux/kmod.h @@ -19,6 +19,7 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ +#include #include #include #include @@ -34,7 +35,17 @@ static inline int request_module(const char * name, ...) { return -ENOSYS; } #endif #define try_then_request_module(x, mod...) ((x) ?: (request_module(mod), (x))) -extern int call_usermodehelper(char *path, char *argv[], char *envp[], int wait); + +struct key; +extern int call_usermodehelper_keys(char *path, char *argv[], char *envp[], + struct key *session_keyring, int wait); + +static inline int +call_usermodehelper(char *path, char **argv, char **envp, int wait) +{ + return call_usermodehelper_keys(path, argv, envp, NULL, wait); +} + extern void usermodehelper_init(void); #endif /* __LINUX_KMOD_H__ */ diff --git a/kernel/kmod.c b/kernel/kmod.c index eed53d4f5230..44166e3bb8af 100644 --- a/kernel/kmod.c +++ b/kernel/kmod.c @@ -120,6 +120,7 @@ struct subprocess_info { char *path; char **argv; char **envp; + struct key *ring; int wait; int retval; }; @@ -130,16 +131,21 @@ struct subprocess_info { static int ____call_usermodehelper(void *data) { struct subprocess_info *sub_info = data; + struct key *old_session; int retval; - /* Unblock all signals. */ + /* Unblock all signals and set the session keyring. */ + key_get(sub_info->ring); flush_signals(current); spin_lock_irq(¤t->sighand->siglock); + old_session = __install_session_keyring(current, sub_info->ring); flush_signal_handlers(current, 1); sigemptyset(¤t->blocked); recalc_sigpending(); spin_unlock_irq(¤t->sighand->siglock); + key_put(old_session); + /* We can run anywhere, unlike our parent keventd(). */ set_cpus_allowed(current, CPU_MASK_ALL); @@ -211,10 +217,11 @@ static void __call_usermodehelper(void *data) } /** - * call_usermodehelper - start a usermode application + * call_usermodehelper_keys - start a usermode application * @path: pathname for the application * @argv: null-terminated argument list * @envp: null-terminated environment list + * @session_keyring: session keyring for process (NULL for an empty keyring) * @wait: wait for the application to finish and return status. * * Runs a user-space application. The application is started @@ -224,7 +231,8 @@ static void __call_usermodehelper(void *data) * Must be called from process context. Returns a negative error code * if program was not execed successfully, or 0. */ -int call_usermodehelper(char *path, char **argv, char **envp, int wait) +int call_usermodehelper_keys(char *path, char **argv, char **envp, + struct key *session_keyring, int wait) { DECLARE_COMPLETION(done); struct subprocess_info sub_info = { @@ -232,6 +240,7 @@ int call_usermodehelper(char *path, char **argv, char **envp, int wait) .path = path, .argv = argv, .envp = envp, + .ring = session_keyring, .wait = wait, .retval = 0, }; @@ -247,7 +256,7 @@ int call_usermodehelper(char *path, char **argv, char **envp, int wait) wait_for_completion(&done); return sub_info.retval; } -EXPORT_SYMBOL(call_usermodehelper); +EXPORT_SYMBOL(call_usermodehelper_keys); void __init usermodehelper_init(void) { diff --git a/security/keys/request_key.c b/security/keys/request_key.c index 1f6c0940297f..1919540f047d 100644 --- a/security/keys/request_key.c +++ b/security/keys/request_key.c @@ -88,7 +88,7 @@ static int call_request_key(struct key *key, argv[i] = NULL; /* do it */ - return call_usermodehelper(argv[0], argv, envp, 1); + return call_usermodehelper_keys(argv[0], argv, envp, NULL, 1); } /* end call_request_key() */ -- cgit v1.2.3 From 8589b4e00e352f983259140f25a262d973be6bc5 Mon Sep 17 00:00:00 2001 From: David Howells Date: Thu, 23 Jun 2005 22:00:53 -0700 Subject: [PATCH] Keys: Use RCU to manage session keyring pointer The attached patch uses RCU to manage the session keyring pointer in struct signal_struct. This means that searching need not disable interrupts and get a the sighand spinlock to access this pointer. Furthermore, by judicious use of rcu_read_(un)lock(), this patch also avoids the need to take and put refcounts on the session keyring itself, thus saving on even more atomic ops. Signed-Off-By: David Howells Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- security/keys/process_keys.c | 42 +++++++++++++++++++++--------------------- security/keys/request_key.c | 7 +++---- 2 files changed, 24 insertions(+), 25 deletions(-) (limited to 'security') diff --git a/security/keys/process_keys.c b/security/keys/process_keys.c index 059c350cac46..972e30172687 100644 --- a/security/keys/process_keys.c +++ b/security/keys/process_keys.c @@ -1,6 +1,6 @@ /* process_keys.c: management of a process's keyrings * - * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved. + * Copyright (C) 2004-5 Red Hat, Inc. All Rights Reserved. * Written by David Howells (dhowells@redhat.com) * * This program is free software; you can redistribute it and/or @@ -181,7 +181,7 @@ static int install_process_keyring(struct task_struct *tsk) goto error; } - /* attach or swap keyrings */ + /* attach keyring */ spin_lock_irqsave(&tsk->sighand->siglock, flags); if (!tsk->signal->process_keyring) { tsk->signal->process_keyring = keyring; @@ -227,12 +227,14 @@ static int install_session_keyring(struct task_struct *tsk, /* install the keyring */ spin_lock_irqsave(&tsk->sighand->siglock, flags); - old = tsk->signal->session_keyring; - tsk->signal->session_keyring = keyring; + old = rcu_dereference(tsk->signal->session_keyring); + rcu_assign_pointer(tsk->signal->session_keyring, keyring); spin_unlock_irqrestore(&tsk->sighand->siglock, flags); ret = 0; + /* we're using RCU on the pointer */ + synchronize_kernel(); key_put(old); error: return ret; @@ -245,8 +247,6 @@ static int install_session_keyring(struct task_struct *tsk, */ int copy_thread_group_keys(struct task_struct *tsk) { - unsigned long flags; - key_check(current->thread_group->session_keyring); key_check(current->thread_group->process_keyring); @@ -254,10 +254,10 @@ int copy_thread_group_keys(struct task_struct *tsk) tsk->signal->process_keyring = NULL; /* same session keyring */ - spin_lock_irqsave(¤t->sighand->siglock, flags); + rcu_read_lock(); tsk->signal->session_keyring = - key_get(current->signal->session_keyring); - spin_unlock_irqrestore(¤t->sighand->siglock, flags); + key_get(rcu_dereference(current->signal->session_keyring)); + rcu_read_unlock(); return 0; @@ -381,8 +381,7 @@ struct key *search_process_keyrings_aux(struct key_type *type, key_match_func_t match) { struct task_struct *tsk = current; - unsigned long flags; - struct key *key, *ret, *err, *tmp; + struct key *key, *ret, *err; /* we want to return -EAGAIN or -ENOKEY if any of the keyrings were * searchable, but we failed to find a key or we found a negative key; @@ -436,17 +435,18 @@ struct key *search_process_keyrings_aux(struct key_type *type, } /* search the session keyring last */ - spin_lock_irqsave(&tsk->sighand->siglock, flags); - - tmp = tsk->signal->session_keyring; - if (!tmp) - tmp = tsk->user->session_keyring; - atomic_inc(&tmp->usage); - - spin_unlock_irqrestore(&tsk->sighand->siglock, flags); + if (tsk->signal->session_keyring) { + rcu_read_lock(); + key = keyring_search_aux( + rcu_dereference(tsk->signal->session_keyring), + type, description, match); + rcu_read_unlock(); + } + else { + key = keyring_search_aux(tsk->user->session_keyring, + type, description, match); + } - key = keyring_search_aux(tmp, type, description, match); - key_put(tmp); if (!IS_ERR(key)) goto found; diff --git a/security/keys/request_key.c b/security/keys/request_key.c index 1919540f047d..54aa7b70e63b 100644 --- a/security/keys/request_key.c +++ b/security/keys/request_key.c @@ -175,13 +175,12 @@ static struct key *__request_key_construction(struct key_type *type, key->expiry = now.tv_sec + key_negative_timeout; if (current->signal->session_keyring) { - unsigned long flags; struct key *keyring; - spin_lock_irqsave(¤t->sighand->siglock, flags); - keyring = current->signal->session_keyring; + rcu_read_lock(); + keyring = rcu_dereference(current->signal->session_keyring); atomic_inc(&keyring->usage); - spin_unlock_irqrestore(¤t->sighand->siglock, flags); + rcu_read_unlock(); key_link(keyring, key); key_put(keyring); -- cgit v1.2.3 From 3e30148c3d524a9c1c63ca28261bc24c457eb07a Mon Sep 17 00:00:00 2001 From: David Howells Date: Thu, 23 Jun 2005 22:00:56 -0700 Subject: [PATCH] Keys: Make request-key create an authorisation key The attached patch makes the following changes: (1) There's a new special key type called ".request_key_auth". This is an authorisation key for when one process requests a key and another process is started to construct it. This type of key cannot be created by the user; nor can it be requested by kernel services. Authorisation keys hold two references: (a) Each refers to a key being constructed. When the key being constructed is instantiated the authorisation key is revoked, rendering it of no further use. (b) The "authorising process". This is either: (i) the process that called request_key(), or: (ii) if the process that called request_key() itself had an authorisation key in its session keyring, then the authorising process referred to by that authorisation key will also be referred to by the new authorisation key. This means that the process that initiated a chain of key requests will authorise the lot of them, and will, by default, wind up with the keys obtained from them in its keyrings. (2) request_key() creates an authorisation key which is then passed to /sbin/request-key in as part of a new session keyring. (3) When request_key() is searching for a key to hand back to the caller, if it comes across an authorisation key in the session keyring of the calling process, it will also search the keyrings of the process specified therein and it will use the specified process's credentials (fsuid, fsgid, groups) to do that rather than the calling process's credentials. This allows a process started by /sbin/request-key to find keys belonging to the authorising process. (4) A key can be read, even if the process executing KEYCTL_READ doesn't have direct read or search permission if that key is contained within the keyrings of a process specified by an authorisation key found within the calling process's session keyring, and is searchable using the credentials of the authorising process. This allows a process started by /sbin/request-key to read keys belonging to the authorising process. (5) The magic KEY_SPEC_*_KEYRING key IDs when passed to KEYCTL_INSTANTIATE or KEYCTL_NEGATE will specify a keyring of the authorising process, rather than the process doing the instantiation. (6) One of the process keyrings can be nominated as the default to which request_key() should attach new keys if not otherwise specified. This is done with KEYCTL_SET_REQKEY_KEYRING and one of the KEY_REQKEY_DEFL_* constants. The current setting can also be read using this call. (7) request_key() is partially interruptible. If it is waiting for another process to finish constructing a key, it can be interrupted. This permits a request-key cycle to be broken without recourse to rebooting. Signed-Off-By: David Howells Signed-Off-By: Benoit Boissinot Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- Documentation/keys.txt | 34 ++++++++ include/linux/key-ui.h | 41 ++++++++- include/linux/key.h | 9 +- include/linux/keyctl.h | 11 +++ include/linux/sched.h | 8 +- kernel/sys.c | 2 +- security/keys/Makefile | 5 +- security/keys/compat.c | 7 +- security/keys/internal.h | 45 +++++++++- security/keys/key.c | 24 ++++-- security/keys/keyctl.c | 176 ++++++++++++++++++++++++------------- security/keys/keyring.c | 67 ++++++++++++-- security/keys/process_keys.c | 179 +++++++++++++++++++++++--------------- security/keys/request_key.c | 182 ++++++++++++++++++++++++++++++++------- security/keys/request_key_auth.c | 180 ++++++++++++++++++++++++++++++++++++++ 15 files changed, 779 insertions(+), 191 deletions(-) create mode 100644 security/keys/request_key_auth.c (limited to 'security') diff --git a/Documentation/keys.txt b/Documentation/keys.txt index 3df40c1fe15a..0321ded4b9ae 100644 --- a/Documentation/keys.txt +++ b/Documentation/keys.txt @@ -591,6 +591,37 @@ The keyctl syscall functions are: this case too. + (*) Set the default request-key destination keyring. + + long keyctl(KEYCTL_SET_REQKEY_KEYRING, int reqkey_defl); + + This sets the default keyring to which implicitly requested keys will be + attached for this thread. reqkey_defl should be one of these constants: + + CONSTANT VALUE NEW DEFAULT KEYRING + ====================================== ====== ======================= + KEY_REQKEY_DEFL_NO_CHANGE -1 No change + KEY_REQKEY_DEFL_DEFAULT 0 Default[1] + KEY_REQKEY_DEFL_THREAD_KEYRING 1 Thread keyring + KEY_REQKEY_DEFL_PROCESS_KEYRING 2 Process keyring + KEY_REQKEY_DEFL_SESSION_KEYRING 3 Session keyring + KEY_REQKEY_DEFL_USER_KEYRING 4 User keyring + KEY_REQKEY_DEFL_USER_SESSION_KEYRING 5 User session keyring + KEY_REQKEY_DEFL_GROUP_KEYRING 6 Group keyring + + The old default will be returned if successful and error EINVAL will be + returned if reqkey_defl is not one of the above values. + + The default keyring can be overridden by the keyring indicated to the + request_key() system call. + + Note that this setting is inherited across fork/exec. + + [1] The default default is: the thread keyring if there is one, otherwise + the process keyring if there is one, otherwise the session keyring if + there is one, otherwise the user default session keyring. + + =============== KERNEL SERVICES =============== @@ -626,6 +657,9 @@ payload contents" for more information. Should the function fail error ENOKEY, EKEYEXPIRED or EKEYREVOKED will be returned. + If successful, the key will have been attached to the default keyring for + implicitly obtained request-key keys, as set by KEYCTL_SET_REQKEY_KEYRING. + (*) When it is no longer required, the key should be released using: diff --git a/include/linux/key-ui.h b/include/linux/key-ui.h index 159ca8d54e9a..cc326174a808 100644 --- a/include/linux/key-ui.h +++ b/include/linux/key-ui.h @@ -1,4 +1,4 @@ -/* key-ui.h: key userspace interface stuff for use by keyfs +/* key-ui.h: key userspace interface stuff * * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved. * Written by David Howells (dhowells@redhat.com) @@ -84,8 +84,45 @@ static inline int key_any_permission(const struct key *key, key_perm_t perm) return kperm != 0; } +static inline int key_task_groups_search(struct task_struct *tsk, gid_t gid) +{ + int ret; + + task_lock(tsk); + ret = groups_search(tsk->group_info, gid); + task_unlock(tsk); + return ret; +} + +static inline int key_task_permission(const struct key *key, + struct task_struct *context, + key_perm_t perm) +{ + key_perm_t kperm; + + if (key->uid == context->fsuid) { + kperm = key->perm >> 16; + } + else if (key->gid != -1 && + key->perm & KEY_GRP_ALL && ( + key->gid == context->fsgid || + key_task_groups_search(context, key->gid) + ) + ) { + kperm = key->perm >> 8; + } + else { + kperm = key->perm; + } + + kperm = kperm & perm & KEY_ALL; + + return kperm == perm; + +} -extern struct key *lookup_user_key(key_serial_t id, int create, int part, +extern struct key *lookup_user_key(struct task_struct *context, + key_serial_t id, int create, int partial, key_perm_t perm); extern long join_session_keyring(const char *name); diff --git a/include/linux/key.h b/include/linux/key.h index 2bfbf88d2740..970bbd916cf4 100644 --- a/include/linux/key.h +++ b/include/linux/key.h @@ -199,10 +199,12 @@ extern int key_payload_reserve(struct key *key, size_t datalen); extern int key_instantiate_and_link(struct key *key, const void *data, size_t datalen, - struct key *keyring); + struct key *keyring, + struct key *instkey); extern int key_negate_and_link(struct key *key, unsigned timeout, - struct key *keyring); + struct key *keyring, + struct key *instkey); extern void key_revoke(struct key *key); extern void key_put(struct key *key); @@ -245,9 +247,6 @@ extern struct key *keyring_search(struct key *keyring, struct key_type *type, const char *description); -extern struct key *search_process_keyrings(struct key_type *type, - const char *description); - extern int keyring_add_key(struct key *keyring, struct key *key); diff --git a/include/linux/keyctl.h b/include/linux/keyctl.h index 381dedc370a3..8d7c59a29e09 100644 --- a/include/linux/keyctl.h +++ b/include/linux/keyctl.h @@ -20,6 +20,16 @@ #define KEY_SPEC_USER_SESSION_KEYRING -5 /* - key ID for UID-session keyring */ #define KEY_SPEC_GROUP_KEYRING -6 /* - key ID for GID-specific keyring */ +/* request-key default keyrings */ +#define KEY_REQKEY_DEFL_NO_CHANGE -1 +#define KEY_REQKEY_DEFL_DEFAULT 0 +#define KEY_REQKEY_DEFL_THREAD_KEYRING 1 +#define KEY_REQKEY_DEFL_PROCESS_KEYRING 2 +#define KEY_REQKEY_DEFL_SESSION_KEYRING 3 +#define KEY_REQKEY_DEFL_USER_KEYRING 4 +#define KEY_REQKEY_DEFL_USER_SESSION_KEYRING 5 +#define KEY_REQKEY_DEFL_GROUP_KEYRING 6 + /* keyctl commands */ #define KEYCTL_GET_KEYRING_ID 0 /* ask for a keyring's ID */ #define KEYCTL_JOIN_SESSION_KEYRING 1 /* join or start named session keyring */ @@ -35,5 +45,6 @@ #define KEYCTL_READ 11 /* read a key or keyring's contents */ #define KEYCTL_INSTANTIATE 12 /* instantiate a partially constructed key */ #define KEYCTL_NEGATE 13 /* negate a partially constructed key */ +#define KEYCTL_SET_REQKEY_KEYRING 14 /* set default request-key keyring */ #endif /* _LINUX_KEYCTL_H */ diff --git a/include/linux/sched.h b/include/linux/sched.h index 901742f92389..2c69682b0444 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -561,9 +561,10 @@ struct group_info { groups_free(group_info); \ } while (0) -struct group_info *groups_alloc(int gidsetsize); -void groups_free(struct group_info *group_info); -int set_current_groups(struct group_info *group_info); +extern struct group_info *groups_alloc(int gidsetsize); +extern void groups_free(struct group_info *group_info); +extern int set_current_groups(struct group_info *group_info); +extern int groups_search(struct group_info *group_info, gid_t grp); /* access the groups "array" with this macro */ #define GROUP_AT(gi, i) \ ((gi)->blocks[(i)/NGROUPS_PER_BLOCK][(i)%NGROUPS_PER_BLOCK]) @@ -660,6 +661,7 @@ struct task_struct { struct user_struct *user; #ifdef CONFIG_KEYS struct key *thread_keyring; /* keyring private to this thread */ + unsigned char jit_keyring; /* default keyring to attach requested keys to */ #endif int oomkilladj; /* OOM kill score adjustment (bit shift). */ char comm[TASK_COMM_LEN]; /* executable name excluding path diff --git a/kernel/sys.c b/kernel/sys.c index 5a9d6b075016..da24bc1292db 100644 --- a/kernel/sys.c +++ b/kernel/sys.c @@ -1259,7 +1259,7 @@ static void groups_sort(struct group_info *group_info) } /* a simple bsearch */ -static int groups_search(struct group_info *group_info, gid_t grp) +int groups_search(struct group_info *group_info, gid_t grp) { int left, right; diff --git a/security/keys/Makefile b/security/keys/Makefile index ddb495d65062..c392d750b208 100644 --- a/security/keys/Makefile +++ b/security/keys/Makefile @@ -7,8 +7,9 @@ obj-y := \ keyring.o \ keyctl.o \ process_keys.o \ - user_defined.o \ - request_key.o + request_key.o \ + request_key_auth.o \ + user_defined.o obj-$(CONFIG_KEYS_COMPAT) += compat.o obj-$(CONFIG_PROC_FS) += proc.o diff --git a/security/keys/compat.c b/security/keys/compat.c index aff8b22dcb5c..3303673c636e 100644 --- a/security/keys/compat.c +++ b/security/keys/compat.c @@ -1,6 +1,6 @@ /* compat.c: 32-bit compatibility syscall for 64-bit systems * - * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved. + * Copyright (C) 2004-5 Red Hat, Inc. All Rights Reserved. * Written by David Howells (dhowells@redhat.com) * * This program is free software; you can redistribute it and/or @@ -24,7 +24,7 @@ * - if you can, you should call sys_keyctl directly */ asmlinkage long compat_sys_keyctl(u32 option, - u32 arg2, u32 arg3, u32 arg4, u32 arg5) + u32 arg2, u32 arg3, u32 arg4, u32 arg5) { switch (option) { case KEYCTL_GET_KEYRING_ID: @@ -71,6 +71,9 @@ asmlinkage long compat_sys_keyctl(u32 option, case KEYCTL_NEGATE: return keyctl_negate_key(arg2, arg3, arg4); + case KEYCTL_SET_REQKEY_KEYRING: + return keyctl_set_reqkey_keyring(arg2); + default: return -EOPNOTSUPP; } diff --git a/security/keys/internal.h b/security/keys/internal.h index 67b2b93a7489..46c8602661c9 100644 --- a/security/keys/internal.h +++ b/security/keys/internal.h @@ -1,6 +1,6 @@ /* internal.h: authentication token and access key management internal defs * - * Copyright (C) 2003 Red Hat, Inc. All Rights Reserved. + * Copyright (C) 2003-5 Red Hat, Inc. All Rights Reserved. * Written by David Howells (dhowells@redhat.com) * * This program is free software; you can redistribute it and/or @@ -15,6 +15,16 @@ #include #include +#if 0 +#define kenter(FMT, a...) printk("==> %s("FMT")\n",__FUNCTION__ , ## a) +#define kleave(FMT, a...) printk("<== %s()"FMT"\n",__FUNCTION__ , ## a) +#define kdebug(FMT, a...) printk(FMT"\n" , ## a) +#else +#define kenter(FMT, a...) do {} while(0) +#define kleave(FMT, a...) do {} while(0) +#define kdebug(FMT, a...) do {} while(0) +#endif + extern struct key_type key_type_dead; extern struct key_type key_type_user; @@ -66,20 +76,46 @@ extern struct key *__keyring_search_one(struct key *keyring, const char *description, key_perm_t perm); +extern struct key *keyring_search_instkey(struct key *keyring, + key_serial_t target_id); + typedef int (*key_match_func_t)(const struct key *, const void *); extern struct key *keyring_search_aux(struct key *keyring, + struct task_struct *tsk, struct key_type *type, const void *description, key_match_func_t match); -extern struct key *search_process_keyrings_aux(struct key_type *type, - const void *description, - key_match_func_t match); +extern struct key *search_process_keyrings(struct key_type *type, + const void *description, + key_match_func_t match, + struct task_struct *tsk); extern struct key *find_keyring_by_name(const char *name, key_serial_t bound); extern int install_thread_keyring(struct task_struct *tsk); +extern int install_process_keyring(struct task_struct *tsk); + +extern struct key *request_key_and_link(struct key_type *type, + const char *description, + const char *callout_info, + struct key *dest_keyring); + +/* + * request_key authorisation + */ +struct request_key_auth { + struct key *target_key; + struct task_struct *context; + pid_t pid; +}; + +extern struct key_type key_type_request_key_auth; +extern struct key *request_key_auth_new(struct key *target, + struct key **_rkakey); + +extern struct key *key_get_instantiation_authkey(key_serial_t target_id); /* * keyctl functions @@ -100,6 +136,7 @@ extern long keyctl_setperm_key(key_serial_t, key_perm_t); extern long keyctl_instantiate_key(key_serial_t, const void __user *, size_t, key_serial_t); extern long keyctl_negate_key(key_serial_t, unsigned, key_serial_t); +extern long keyctl_set_reqkey_keyring(int); /* diff --git a/security/keys/key.c b/security/keys/key.c index 1fdfccb3fe43..3304d37bb379 100644 --- a/security/keys/key.c +++ b/security/keys/key.c @@ -1,6 +1,6 @@ /* key.c: basic authentication token and access key management * - * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved. + * Copyright (C) 2004-5 Red Hat, Inc. All Rights Reserved. * Written by David Howells (dhowells@redhat.com) * * This program is free software; you can redistribute it and/or @@ -391,7 +391,8 @@ EXPORT_SYMBOL(key_payload_reserve); static int __key_instantiate_and_link(struct key *key, const void *data, size_t datalen, - struct key *keyring) + struct key *keyring, + struct key *instkey) { int ret, awaken; @@ -419,6 +420,10 @@ static int __key_instantiate_and_link(struct key *key, /* and link it into the destination keyring */ if (keyring) ret = __key_link(keyring, key); + + /* disable the authorisation key */ + if (instkey) + key_revoke(instkey); } } @@ -439,19 +444,21 @@ static int __key_instantiate_and_link(struct key *key, int key_instantiate_and_link(struct key *key, const void *data, size_t datalen, - struct key *keyring) + struct key *keyring, + struct key *instkey) { int ret; if (keyring) down_write(&keyring->sem); - ret = __key_instantiate_and_link(key, data, datalen, keyring); + ret = __key_instantiate_and_link(key, data, datalen, keyring, instkey); if (keyring) up_write(&keyring->sem); return ret; + } /* end key_instantiate_and_link() */ EXPORT_SYMBOL(key_instantiate_and_link); @@ -462,7 +469,8 @@ EXPORT_SYMBOL(key_instantiate_and_link); */ int key_negate_and_link(struct key *key, unsigned timeout, - struct key *keyring) + struct key *keyring, + struct key *instkey) { struct timespec now; int ret, awaken; @@ -495,6 +503,10 @@ int key_negate_and_link(struct key *key, /* and link it into the destination keyring */ if (keyring) ret = __key_link(keyring, key); + + /* disable the authorisation key */ + if (instkey) + key_revoke(instkey); } up_write(&key_construction_sem); @@ -781,7 +793,7 @@ struct key *key_create_or_update(struct key *keyring, } /* instantiate it and link it into the target keyring */ - ret = __key_instantiate_and_link(key, payload, plen, keyring); + ret = __key_instantiate_and_link(key, payload, plen, keyring, NULL); if (ret < 0) { key_put(key); key = ERR_PTR(ret); diff --git a/security/keys/keyctl.c b/security/keys/keyctl.c index cedb7326de29..fea262860ea0 100644 --- a/security/keys/keyctl.c +++ b/security/keys/keyctl.c @@ -1,6 +1,6 @@ /* keyctl.c: userspace keyctl operations * - * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved. + * Copyright (C) 2004-5 Red Hat, Inc. All Rights Reserved. * Written by David Howells (dhowells@redhat.com) * * This program is free software; you can redistribute it and/or @@ -49,6 +49,13 @@ asmlinkage long sys_add_key(const char __user *_type, goto error; type[31] = '\0'; + if (!type[0]) + goto error; + + ret = -EPERM; + if (type[0] == '.') + goto error; + ret = -EFAULT; dlen = strnlen_user(_description, PAGE_SIZE - 1); if (dlen <= 0) @@ -82,7 +89,7 @@ asmlinkage long sys_add_key(const char __user *_type, } /* find the target keyring (which must be writable) */ - keyring = lookup_user_key(ringid, 1, 0, KEY_WRITE); + keyring = lookup_user_key(NULL, ringid, 1, 0, KEY_WRITE); if (IS_ERR(keyring)) { ret = PTR_ERR(keyring); goto error3; @@ -181,7 +188,7 @@ asmlinkage long sys_request_key(const char __user *_type, /* get the destination keyring if specified */ dest = NULL; if (destringid) { - dest = lookup_user_key(destringid, 1, 0, KEY_WRITE); + dest = lookup_user_key(NULL, destringid, 1, 0, KEY_WRITE); if (IS_ERR(dest)) { ret = PTR_ERR(dest); goto error3; @@ -196,23 +203,15 @@ asmlinkage long sys_request_key(const char __user *_type, } /* do the search */ - key = request_key(ktype, description, callout_info); + key = request_key_and_link(ktype, description, callout_info, dest); if (IS_ERR(key)) { ret = PTR_ERR(key); goto error5; } - /* link the resulting key to the destination keyring */ - if (dest) { - ret = key_link(dest, key); - if (ret < 0) - goto error6; - } - ret = key->serial; - error6: - key_put(key); + key_put(key); error5: key_type_put(ktype); error4: @@ -237,7 +236,7 @@ long keyctl_get_keyring_ID(key_serial_t id, int create) struct key *key; long ret; - key = lookup_user_key(id, create, 0, KEY_SEARCH); + key = lookup_user_key(NULL, id, create, 0, KEY_SEARCH); if (IS_ERR(key)) { ret = PTR_ERR(key); goto error; @@ -324,7 +323,7 @@ long keyctl_update_key(key_serial_t id, } /* find the target key (which must be writable) */ - key = lookup_user_key(id, 0, 0, KEY_WRITE); + key = lookup_user_key(NULL, id, 0, 0, KEY_WRITE); if (IS_ERR(key)) { ret = PTR_ERR(key); goto error2; @@ -352,7 +351,7 @@ long keyctl_revoke_key(key_serial_t id) struct key *key; long ret; - key = lookup_user_key(id, 0, 0, KEY_WRITE); + key = lookup_user_key(NULL, id, 0, 0, KEY_WRITE); if (IS_ERR(key)) { ret = PTR_ERR(key); goto error; @@ -378,7 +377,7 @@ long keyctl_keyring_clear(key_serial_t ringid) struct key *keyring; long ret; - keyring = lookup_user_key(ringid, 1, 0, KEY_WRITE); + keyring = lookup_user_key(NULL, ringid, 1, 0, KEY_WRITE); if (IS_ERR(keyring)) { ret = PTR_ERR(keyring); goto error; @@ -404,13 +403,13 @@ long keyctl_keyring_link(key_serial_t id, key_serial_t ringid) struct key *keyring, *key; long ret; - keyring = lookup_user_key(ringid, 1, 0, KEY_WRITE); + keyring = lookup_user_key(NULL, ringid, 1, 0, KEY_WRITE); if (IS_ERR(keyring)) { ret = PTR_ERR(keyring); goto error; } - key = lookup_user_key(id, 1, 0, KEY_LINK); + key = lookup_user_key(NULL, id, 1, 0, KEY_LINK); if (IS_ERR(key)) { ret = PTR_ERR(key); goto error2; @@ -438,13 +437,13 @@ long keyctl_keyring_unlink(key_serial_t id, key_serial_t ringid) struct key *keyring, *key; long ret; - keyring = lookup_user_key(ringid, 0, 0, KEY_WRITE); + keyring = lookup_user_key(NULL, ringid, 0, 0, KEY_WRITE); if (IS_ERR(keyring)) { ret = PTR_ERR(keyring); goto error; } - key = lookup_user_key(id, 0, 0, 0); + key = lookup_user_key(NULL, id, 0, 0, 0); if (IS_ERR(key)) { ret = PTR_ERR(key); goto error2; @@ -475,16 +474,29 @@ long keyctl_describe_key(key_serial_t keyid, char __user *buffer, size_t buflen) { - struct key *key; + struct key *key, *instkey; char *tmpbuf; long ret; - key = lookup_user_key(keyid, 0, 1, KEY_VIEW); + key = lookup_user_key(NULL, keyid, 0, 1, KEY_VIEW); if (IS_ERR(key)) { + /* viewing a key under construction is permitted if we have the + * authorisation token handy */ + if (PTR_ERR(key) == -EACCES) { + instkey = key_get_instantiation_authkey(keyid); + if (!IS_ERR(instkey)) { + key_put(instkey); + key = lookup_user_key(NULL, keyid, 0, 1, 0); + if (!IS_ERR(key)) + goto okay; + } + } + ret = PTR_ERR(key); goto error; } +okay: /* calculate how much description we're going to return */ ret = -ENOMEM; tmpbuf = kmalloc(PAGE_SIZE, GFP_KERNEL); @@ -568,7 +580,7 @@ long keyctl_keyring_search(key_serial_t ringid, goto error2; /* get the keyring at which to begin the search */ - keyring = lookup_user_key(ringid, 0, 0, KEY_SEARCH); + keyring = lookup_user_key(NULL, ringid, 0, 0, KEY_SEARCH); if (IS_ERR(keyring)) { ret = PTR_ERR(keyring); goto error2; @@ -577,7 +589,7 @@ long keyctl_keyring_search(key_serial_t ringid, /* get the destination keyring if specified */ dest = NULL; if (destringid) { - dest = lookup_user_key(destringid, 1, 0, KEY_WRITE); + dest = lookup_user_key(NULL, destringid, 1, 0, KEY_WRITE); if (IS_ERR(dest)) { ret = PTR_ERR(dest); goto error3; @@ -656,24 +668,23 @@ long keyctl_read_key(key_serial_t keyid, char __user *buffer, size_t buflen) long ret; /* find the key first */ - key = lookup_user_key(keyid, 0, 0, 0); + key = lookup_user_key(NULL, keyid, 0, 0, 0); if (!IS_ERR(key)) { /* see if we can read it directly */ if (key_permission(key, KEY_READ)) goto can_read_key; - /* can't; see if it's searchable from this process's - * keyrings */ - ret = -ENOKEY; - if (key_permission(key, KEY_SEARCH)) { - /* okay - we do have search permission on the key - * itself, but do we have the key? */ - skey = search_process_keyrings_aux(key->type, key, - keyctl_read_key_same); - if (!IS_ERR(skey)) - goto can_read_key2; - } - + /* we can't; see if it's searchable from this process's + * keyrings + * - we automatically take account of the fact that it may be + * dangling off an instantiation key + */ + skey = search_process_keyrings(key->type, key, + keyctl_read_key_same, current); + if (!IS_ERR(skey)) + goto can_read_key2; + + ret = PTR_ERR(skey); goto error2; } @@ -719,7 +730,7 @@ long keyctl_chown_key(key_serial_t id, uid_t uid, gid_t gid) if (uid == (uid_t) -1 && gid == (gid_t) -1) goto error; - key = lookup_user_key(id, 1, 1, 0); + key = lookup_user_key(NULL, id, 1, 1, 0); if (IS_ERR(key)) { ret = PTR_ERR(key); goto error; @@ -776,7 +787,7 @@ long keyctl_setperm_key(key_serial_t id, key_perm_t perm) if (perm & ~(KEY_USR_ALL | KEY_GRP_ALL | KEY_OTH_ALL)) goto error; - key = lookup_user_key(id, 1, 1, 0); + key = lookup_user_key(NULL, id, 1, 1, 0); if (IS_ERR(key)) { ret = PTR_ERR(key); goto error; @@ -809,7 +820,8 @@ long keyctl_instantiate_key(key_serial_t id, size_t plen, key_serial_t ringid) { - struct key *key, *keyring; + struct request_key_auth *rka; + struct key *instkey, *keyring; void *payload; long ret; @@ -831,18 +843,21 @@ long keyctl_instantiate_key(key_serial_t id, goto error2; } - /* find the target key (which must be writable) */ - key = lookup_user_key(id, 0, 1, KEY_WRITE); - if (IS_ERR(key)) { - ret = PTR_ERR(key); + /* find the instantiation authorisation key */ + instkey = key_get_instantiation_authkey(id); + if (IS_ERR(instkey)) { + ret = PTR_ERR(instkey); goto error2; } - /* find the destination keyring if present (which must also be - * writable) */ + rka = instkey->payload.data; + + /* find the destination keyring amongst those belonging to the + * requesting task */ keyring = NULL; if (ringid) { - keyring = lookup_user_key(ringid, 1, 0, KEY_WRITE); + keyring = lookup_user_key(rka->context, ringid, 1, 0, + KEY_WRITE); if (IS_ERR(keyring)) { ret = PTR_ERR(keyring); goto error3; @@ -850,11 +865,12 @@ long keyctl_instantiate_key(key_serial_t id, } /* instantiate the key and link it into a keyring */ - ret = key_instantiate_and_link(key, payload, plen, keyring); + ret = key_instantiate_and_link(rka->target_key, payload, plen, + keyring, instkey); key_put(keyring); error3: - key_put(key); + key_put(instkey); error2: kfree(payload); error: @@ -869,21 +885,24 @@ long keyctl_instantiate_key(key_serial_t id, */ long keyctl_negate_key(key_serial_t id, unsigned timeout, key_serial_t ringid) { - struct key *key, *keyring; + struct request_key_auth *rka; + struct key *instkey, *keyring; long ret; - /* find the target key (which must be writable) */ - key = lookup_user_key(id, 0, 1, KEY_WRITE); - if (IS_ERR(key)) { - ret = PTR_ERR(key); + /* find the instantiation authorisation key */ + instkey = key_get_instantiation_authkey(id); + if (IS_ERR(instkey)) { + ret = PTR_ERR(instkey); goto error; } + rka = instkey->payload.data; + /* find the destination keyring if present (which must also be * writable) */ keyring = NULL; if (ringid) { - keyring = lookup_user_key(ringid, 1, 0, KEY_WRITE); + keyring = lookup_user_key(NULL, ringid, 1, 0, KEY_WRITE); if (IS_ERR(keyring)) { ret = PTR_ERR(keyring); goto error2; @@ -891,16 +910,54 @@ long keyctl_negate_key(key_serial_t id, unsigned timeout, key_serial_t ringid) } /* instantiate the key and link it into a keyring */ - ret = key_negate_and_link(key, timeout, keyring); + ret = key_negate_and_link(rka->target_key, timeout, keyring, instkey); key_put(keyring); error2: - key_put(key); + key_put(instkey); error: return ret; } /* end keyctl_negate_key() */ +/*****************************************************************************/ +/* + * set the default keyring in which request_key() will cache keys + * - return the old setting + */ +long keyctl_set_reqkey_keyring(int reqkey_defl) +{ + int ret; + + switch (reqkey_defl) { + case KEY_REQKEY_DEFL_THREAD_KEYRING: + ret = install_thread_keyring(current); + if (ret < 0) + return ret; + goto set; + + case KEY_REQKEY_DEFL_PROCESS_KEYRING: + ret = install_process_keyring(current); + if (ret < 0) + return ret; + + case KEY_REQKEY_DEFL_DEFAULT: + case KEY_REQKEY_DEFL_SESSION_KEYRING: + case KEY_REQKEY_DEFL_USER_KEYRING: + case KEY_REQKEY_DEFL_USER_SESSION_KEYRING: + set: + current->jit_keyring = reqkey_defl; + + case KEY_REQKEY_DEFL_NO_CHANGE: + return current->jit_keyring; + + case KEY_REQKEY_DEFL_GROUP_KEYRING: + default: + return -EINVAL; + } + +} /* end keyctl_set_reqkey_keyring() */ + /*****************************************************************************/ /* * the key control system call @@ -971,6 +1028,9 @@ asmlinkage long sys_keyctl(int option, unsigned long arg2, unsigned long arg3, (unsigned) arg3, (key_serial_t) arg4); + case KEYCTL_SET_REQKEY_KEYRING: + return keyctl_set_reqkey_keyring(arg2); + default: return -EOPNOTSUPP; } diff --git a/security/keys/keyring.c b/security/keys/keyring.c index c9a5de197487..90a551e4da66 100644 --- a/security/keys/keyring.c +++ b/security/keys/keyring.c @@ -1,6 +1,6 @@ /* keyring.c: keyring handling * - * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved. + * Copyright (C) 2004-5 Red Hat, Inc. All Rights Reserved. * Written by David Howells (dhowells@redhat.com) * * This program is free software; you can redistribute it and/or @@ -308,7 +308,7 @@ struct key *keyring_alloc(const char *description, uid_t uid, gid_t gid, uid, gid, KEY_USR_ALL, not_in_quota); if (!IS_ERR(keyring)) { - ret = key_instantiate_and_link(keyring, NULL, 0, dest); + ret = key_instantiate_and_link(keyring, NULL, 0, dest, NULL); if (ret < 0) { key_put(keyring); keyring = ERR_PTR(ret); @@ -326,11 +326,12 @@ struct key *keyring_alloc(const char *description, uid_t uid, gid_t gid, * - we only find keys on which we have search permission * - we use the supplied match function to see if the description (or other * feature of interest) matches - * - we readlock the keyrings as we search down the tree + * - we rely on RCU to prevent the keyring lists from disappearing on us * - we return -EAGAIN if we didn't find any matching key * - we return -ENOKEY if we only found negative matching keys */ struct key *keyring_search_aux(struct key *keyring, + struct task_struct *context, struct key_type *type, const void *description, key_match_func_t match) @@ -352,7 +353,7 @@ struct key *keyring_search_aux(struct key *keyring, /* top keyring must have search permission to begin the search */ key = ERR_PTR(-EACCES); - if (!key_permission(keyring, KEY_SEARCH)) + if (!key_task_permission(keyring, context, KEY_SEARCH)) goto error; key = ERR_PTR(-ENOTDIR); @@ -392,7 +393,7 @@ struct key *keyring_search_aux(struct key *keyring, continue; /* key must have search permissions */ - if (!key_permission(key, KEY_SEARCH)) + if (!key_task_permission(key, context, KEY_SEARCH)) continue; /* we set a different error code if we find a negative key */ @@ -418,7 +419,7 @@ struct key *keyring_search_aux(struct key *keyring, if (sp >= KEYRING_SEARCH_MAX_DEPTH) continue; - if (!key_permission(key, KEY_SEARCH)) + if (!key_task_permission(key, context, KEY_SEARCH)) continue; /* stack the current position */ @@ -468,7 +469,11 @@ struct key *keyring_search(struct key *keyring, struct key_type *type, const char *description) { - return keyring_search_aux(keyring, type, description, type->match); + if (!type->match) + return ERR_PTR(-ENOKEY); + + return keyring_search_aux(keyring, current, + type, description, type->match); } /* end keyring_search() */ @@ -496,7 +501,8 @@ struct key *__keyring_search_one(struct key *keyring, key = klist->keys[loop]; if (key->type == ktype && - key->type->match(key, description) && + (!key->type->match || + key->type->match(key, description)) && key_permission(key, perm) && !test_bit(KEY_FLAG_REVOKED, &key->flags) ) @@ -515,6 +521,51 @@ struct key *__keyring_search_one(struct key *keyring, } /* end __keyring_search_one() */ +/*****************************************************************************/ +/* + * search for an instantiation authorisation key matching a target key + * - the RCU read lock must be held by the caller + * - a target_id of zero specifies any valid token + */ +struct key *keyring_search_instkey(struct key *keyring, + key_serial_t target_id) +{ + struct request_key_auth *rka; + struct keyring_list *klist; + struct key *instkey; + int loop; + + klist = rcu_dereference(keyring->payload.subscriptions); + if (klist) { + for (loop = 0; loop < klist->nkeys; loop++) { + instkey = klist->keys[loop]; + + if (instkey->type != &key_type_request_key_auth) + continue; + + rka = instkey->payload.data; + if (target_id && rka->target_key->serial != target_id) + continue; + + /* the auth key is revoked during instantiation */ + if (!test_bit(KEY_FLAG_REVOKED, &instkey->flags)) + goto found; + + instkey = ERR_PTR(-EKEYREVOKED); + goto error; + } + } + + instkey = ERR_PTR(-EACCES); + goto error; + +found: + atomic_inc(&instkey->usage); +error: + return instkey; + +} /* end keyring_search_instkey() */ + /*****************************************************************************/ /* * find a keyring with the specified name diff --git a/security/keys/process_keys.c b/security/keys/process_keys.c index 972e30172687..34db087bbcc7 100644 --- a/security/keys/process_keys.c +++ b/security/keys/process_keys.c @@ -165,7 +165,7 @@ int install_thread_keyring(struct task_struct *tsk) /* * make sure a process keyring is installed */ -static int install_process_keyring(struct task_struct *tsk) +int install_process_keyring(struct task_struct *tsk) { unsigned long flags; struct key *keyring; @@ -376,12 +376,13 @@ void key_fsgid_changed(struct task_struct *tsk) * - we return -EAGAIN if we didn't find any matching key * - we return -ENOKEY if we found only negative matching keys */ -struct key *search_process_keyrings_aux(struct key_type *type, - const void *description, - key_match_func_t match) +struct key *search_process_keyrings(struct key_type *type, + const void *description, + key_match_func_t match, + struct task_struct *context) { - struct task_struct *tsk = current; - struct key *key, *ret, *err; + struct request_key_auth *rka; + struct key *key, *ret, *err, *instkey; /* we want to return -EAGAIN or -ENOKEY if any of the keyrings were * searchable, but we failed to find a key or we found a negative key; @@ -395,9 +396,9 @@ struct key *search_process_keyrings_aux(struct key_type *type, err = ERR_PTR(-EAGAIN); /* search the thread keyring first */ - if (tsk->thread_keyring) { - key = keyring_search_aux(tsk->thread_keyring, type, - description, match); + if (context->thread_keyring) { + key = keyring_search_aux(context->thread_keyring, + context, type, description, match); if (!IS_ERR(key)) goto found; @@ -415,9 +416,9 @@ struct key *search_process_keyrings_aux(struct key_type *type, } /* search the process keyring second */ - if (tsk->signal->process_keyring) { - key = keyring_search_aux(tsk->signal->process_keyring, - type, description, match); + if (context->signal->process_keyring) { + key = keyring_search_aux(context->signal->process_keyring, + context, type, description, match); if (!IS_ERR(key)) goto found; @@ -434,53 +435,93 @@ struct key *search_process_keyrings_aux(struct key_type *type, } } - /* search the session keyring last */ - if (tsk->signal->session_keyring) { + /* search the session keyring */ + if (context->signal->session_keyring) { rcu_read_lock(); key = keyring_search_aux( - rcu_dereference(tsk->signal->session_keyring), - type, description, match); + rcu_dereference(context->signal->session_keyring), + context, type, description, match); rcu_read_unlock(); + + if (!IS_ERR(key)) + goto found; + + switch (PTR_ERR(key)) { + case -EAGAIN: /* no key */ + if (ret) + break; + case -ENOKEY: /* negative key */ + ret = key; + break; + default: + err = key; + break; + } + + /* if this process has a session keyring and that has an + * instantiation authorisation key in the bottom level, then we + * also search the keyrings of the process mentioned there */ + if (context != current) + goto no_key; + + rcu_read_lock(); + instkey = __keyring_search_one( + rcu_dereference(context->signal->session_keyring), + &key_type_request_key_auth, NULL, 0); + rcu_read_unlock(); + + if (IS_ERR(instkey)) + goto no_key; + + rka = instkey->payload.data; + + key = search_process_keyrings(type, description, match, + rka->context); + key_put(instkey); + + if (!IS_ERR(key)) + goto found; + + switch (PTR_ERR(key)) { + case -EAGAIN: /* no key */ + if (ret) + break; + case -ENOKEY: /* negative key */ + ret = key; + break; + default: + err = key; + break; + } } + /* or search the user-session keyring */ else { - key = keyring_search_aux(tsk->user->session_keyring, - type, description, match); - } - - if (!IS_ERR(key)) - goto found; + key = keyring_search_aux(context->user->session_keyring, + context, type, description, match); + if (!IS_ERR(key)) + goto found; - switch (PTR_ERR(key)) { - case -EAGAIN: /* no key */ - if (ret) + switch (PTR_ERR(key)) { + case -EAGAIN: /* no key */ + if (ret) + break; + case -ENOKEY: /* negative key */ + ret = key; break; - case -ENOKEY: /* negative key */ - ret = key; - break; - default: - err = key; - break; + default: + err = key; + break; + } } + +no_key: /* no key - decide on the error we're going to go for */ key = ret ? ret : err; - found: +found: return key; -} /* end search_process_keyrings_aux() */ - -/*****************************************************************************/ -/* - * search the process keyrings for the first matching key - * - we return -EAGAIN if we didn't find any matching key - * - we return -ENOKEY if we found only negative matching keys - */ -struct key *search_process_keyrings(struct key_type *type, - const char *description) -{ - return search_process_keyrings_aux(type, description, type->match); - } /* end search_process_keyrings() */ /*****************************************************************************/ @@ -489,72 +530,73 @@ struct key *search_process_keyrings(struct key_type *type, * - don't create special keyrings unless so requested * - partially constructed keys aren't found unless requested */ -struct key *lookup_user_key(key_serial_t id, int create, int partial, - key_perm_t perm) +struct key *lookup_user_key(struct task_struct *context, key_serial_t id, + int create, int partial, key_perm_t perm) { - struct task_struct *tsk = current; - unsigned long flags; struct key *key; int ret; + if (!context) + context = current; + key = ERR_PTR(-ENOKEY); switch (id) { case KEY_SPEC_THREAD_KEYRING: - if (!tsk->thread_keyring) { + if (!context->thread_keyring) { if (!create) goto error; - ret = install_thread_keyring(tsk); + ret = install_thread_keyring(context); if (ret < 0) { key = ERR_PTR(ret); goto error; } } - key = tsk->thread_keyring; + key = context->thread_keyring; atomic_inc(&key->usage); break; case KEY_SPEC_PROCESS_KEYRING: - if (!tsk->signal->process_keyring) { + if (!context->signal->process_keyring) { if (!create) goto error; - ret = install_process_keyring(tsk); + ret = install_process_keyring(context); if (ret < 0) { key = ERR_PTR(ret); goto error; } } - key = tsk->signal->process_keyring; + key = context->signal->process_keyring; atomic_inc(&key->usage); break; case KEY_SPEC_SESSION_KEYRING: - if (!tsk->signal->session_keyring) { + if (!context->signal->session_keyring) { /* always install a session keyring upon access if one * doesn't exist yet */ ret = install_session_keyring( - tsk, tsk->user->session_keyring); + context, context->user->session_keyring); if (ret < 0) goto error; } - spin_lock_irqsave(&tsk->sighand->siglock, flags); - key = tsk->signal->session_keyring; + rcu_read_lock(); + key = rcu_dereference(context->signal->session_keyring); atomic_inc(&key->usage); - spin_unlock_irqrestore(&tsk->sighand->siglock, flags); + rcu_read_unlock(); break; case KEY_SPEC_USER_KEYRING: - key = tsk->user->uid_keyring; + key = context->user->uid_keyring; atomic_inc(&key->usage); break; case KEY_SPEC_USER_SESSION_KEYRING: - key = tsk->user->session_keyring; + key = context->user->session_keyring; atomic_inc(&key->usage); break; @@ -574,7 +616,7 @@ struct key *lookup_user_key(key_serial_t id, int create, int partial, break; } - /* check the status and permissions */ + /* check the status */ if (perm) { ret = key_validate(key); if (ret < 0) @@ -585,8 +627,10 @@ struct key *lookup_user_key(key_serial_t id, int create, int partial, if (!partial && !test_bit(KEY_FLAG_INSTANTIATED, &key->flags)) goto invalid_key; + /* check the permissions */ ret = -EACCES; - if (!key_permission(key, perm)) + + if (!key_task_permission(key, context, perm)) goto invalid_key; error: @@ -609,7 +653,6 @@ struct key *lookup_user_key(key_serial_t id, int create, int partial, long join_session_keyring(const char *name) { struct task_struct *tsk = current; - unsigned long flags; struct key *keyring; long ret; @@ -619,9 +662,9 @@ long join_session_keyring(const char *name) if (ret < 0) goto error; - spin_lock_irqsave(&tsk->sighand->siglock, flags); - ret = tsk->signal->session_keyring->serial; - spin_unlock_irqrestore(&tsk->sighand->siglock, flags); + rcu_read_lock(); + ret = rcu_dereference(tsk->signal->session_keyring)->serial; + rcu_read_unlock(); goto error; } diff --git a/security/keys/request_key.c b/security/keys/request_key.c index 54aa7b70e63b..dfcd983af1fd 100644 --- a/security/keys/request_key.c +++ b/security/keys/request_key.c @@ -1,6 +1,6 @@ /* request_key.c: request a key from userspace * - * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved. + * Copyright (C) 2004-5 Red Hat, Inc. All Rights Reserved. * Written by David Howells (dhowells@redhat.com) * * This program is free software; you can redistribute it and/or @@ -13,6 +13,7 @@ #include #include #include +#include #include "internal.h" struct key_construction { @@ -27,18 +28,26 @@ DECLARE_WAIT_QUEUE_HEAD(request_key_conswq); /* * request userspace finish the construction of a key * - execute "/sbin/request-key " - * - if callout_info is an empty string, it'll be rendered as a "-" instead */ static int call_request_key(struct key *key, const char *op, const char *callout_info) { struct task_struct *tsk = current; - unsigned long flags; key_serial_t prkey, sskey; + struct key *session_keyring, *rkakey; char *argv[10], *envp[3], uid_str[12], gid_str[12]; char key_str[12], keyring_str[3][12]; - int i; + int ret, i; + + kenter("{%d},%s,%s", key->serial, op, callout_info); + + /* generate a new session keyring with an auth key in it */ + session_keyring = request_key_auth_new(key, &rkakey); + if (IS_ERR(session_keyring)) { + ret = PTR_ERR(session_keyring); + goto error; + } /* record the UID and GID */ sprintf(uid_str, "%d", current->fsuid); @@ -55,17 +64,17 @@ static int call_request_key(struct key *key, if (tsk->signal->process_keyring) prkey = tsk->signal->process_keyring->serial; - sskey = 0; - spin_lock_irqsave(&tsk->sighand->siglock, flags); - if (tsk->signal->session_keyring) - sskey = tsk->signal->session_keyring->serial; - spin_unlock_irqrestore(&tsk->sighand->siglock, flags); - + sprintf(keyring_str[1], "%d", prkey); - if (!sskey) + if (tsk->signal->session_keyring) { + rcu_read_lock(); + sskey = rcu_dereference(tsk->signal->session_keyring)->serial; + rcu_read_unlock(); + } + else { sskey = tsk->user->session_keyring->serial; + } - sprintf(keyring_str[1], "%d", prkey); sprintf(keyring_str[2], "%d", sskey); /* set up a minimal environment */ @@ -84,11 +93,20 @@ static int call_request_key(struct key *key, argv[i++] = keyring_str[0]; argv[i++] = keyring_str[1]; argv[i++] = keyring_str[2]; - argv[i++] = callout_info[0] ? (char *) callout_info : "-"; + argv[i++] = (char *) callout_info; argv[i] = NULL; /* do it */ - return call_usermodehelper_keys(argv[0], argv, envp, NULL, 1); + ret = call_usermodehelper_keys(argv[0], argv, envp, session_keyring, 1); + + /* dispose of the special keys */ + key_revoke(rkakey); + key_put(rkakey); + key_put(session_keyring); + + error: + kleave(" = %d", ret); + return ret; } /* end call_request_key() */ @@ -107,6 +125,8 @@ static struct key *__request_key_construction(struct key_type *type, struct key *key; int ret, negated; + kenter("%s,%s,%s", type->name, description, callout_info); + /* create a key and add it to the queue */ key = key_alloc(type, description, current->fsuid, current->fsgid, KEY_USR_ALL, 0); @@ -143,6 +163,7 @@ static struct key *__request_key_construction(struct key_type *type, } out: + kleave(" = %p", key); return key; request_failed: @@ -216,6 +237,9 @@ static struct key *request_key_construction(struct key_type *type, DECLARE_WAITQUEUE(myself, current); + kenter("%s,%s,{%d},%s", + type->name, description, user->uid, callout_info); + /* see if there's such a key under construction already */ down_write(&key_construction_sem); @@ -232,6 +256,7 @@ static struct key *request_key_construction(struct key_type *type, /* see about getting userspace to construct the key */ key = __request_key_construction(type, description, callout_info); error: + kleave(" = %p", key); return key; /* someone else has the same key under construction @@ -245,9 +270,11 @@ static struct key *request_key_construction(struct key_type *type, add_wait_queue(&request_key_conswq, &myself); for (;;) { - set_current_state(TASK_UNINTERRUPTIBLE); + set_current_state(TASK_INTERRUPTIBLE); if (!test_bit(KEY_FLAG_USER_CONSTRUCT, &ckey->flags)) break; + if (signal_pending(current)) + break; schedule(); } @@ -265,23 +292,85 @@ static struct key *request_key_construction(struct key_type *type, } /* end request_key_construction() */ +/*****************************************************************************/ +/* + * link a freshly minted key to an appropriate destination keyring + */ +static void request_key_link(struct key *key, struct key *dest_keyring) +{ + struct task_struct *tsk = current; + struct key *drop = NULL; + + kenter("{%d},%p", key->serial, dest_keyring); + + /* find the appropriate keyring */ + if (!dest_keyring) { + switch (tsk->jit_keyring) { + case KEY_REQKEY_DEFL_DEFAULT: + case KEY_REQKEY_DEFL_THREAD_KEYRING: + dest_keyring = tsk->thread_keyring; + if (dest_keyring) + break; + + case KEY_REQKEY_DEFL_PROCESS_KEYRING: + dest_keyring = tsk->signal->process_keyring; + if (dest_keyring) + break; + + case KEY_REQKEY_DEFL_SESSION_KEYRING: + rcu_read_lock(); + dest_keyring = key_get( + rcu_dereference(tsk->signal->session_keyring)); + rcu_read_unlock(); + drop = dest_keyring; + + if (dest_keyring) + break; + + case KEY_REQKEY_DEFL_USER_SESSION_KEYRING: + dest_keyring = current->user->session_keyring; + break; + + case KEY_REQKEY_DEFL_USER_KEYRING: + dest_keyring = current->user->uid_keyring; + break; + + case KEY_REQKEY_DEFL_GROUP_KEYRING: + default: + BUG(); + } + } + + /* and attach the key to it */ + key_link(dest_keyring, key); + + key_put(drop); + + kleave(""); + +} /* end request_key_link() */ + /*****************************************************************************/ /* * request a key * - search the process's keyrings * - check the list of keys being created or updated - * - call out to userspace for a key if requested (supplementary info can be - * passed) + * - call out to userspace for a key if supplementary info was provided + * - cache the key in an appropriate keyring */ -struct key *request_key(struct key_type *type, - const char *description, - const char *callout_info) +struct key *request_key_and_link(struct key_type *type, + const char *description, + const char *callout_info, + struct key *dest_keyring) { struct key_user *user; struct key *key; + kenter("%s,%s,%s,%p", + type->name, description, callout_info, dest_keyring); + /* search all the process keyrings for a key */ - key = search_process_keyrings_aux(type, description, type->match); + key = search_process_keyrings(type, description, type->match, current); if (PTR_ERR(key) == -EAGAIN) { /* the search failed, but the keyrings were searchable, so we @@ -292,12 +381,13 @@ struct key *request_key(struct key_type *type, /* - get hold of the user's construction queue */ user = key_user_lookup(current->fsuid); - if (!user) { - key = ERR_PTR(-ENOMEM); - goto error; - } + if (!user) + goto nomem; + + do { + if (signal_pending(current)) + goto interrupted; - for (;;) { /* ask userspace (returns NULL if it waited on a key * being constructed) */ key = request_key_construction(type, description, @@ -307,18 +397,46 @@ struct key *request_key(struct key_type *type, /* someone else made the key we want, so we need to * search again as it might now be available to us */ - key = search_process_keyrings_aux(type, description, - type->match); - if (PTR_ERR(key) != -EAGAIN) - break; - } + key = search_process_keyrings(type, description, + type->match, current); + + } while (PTR_ERR(key) == -EAGAIN); key_user_put(user); + + /* link the new key into the appropriate keyring */ + if (!PTR_ERR(key)) + request_key_link(key, dest_keyring); } - error: +error: + kleave(" = %p", key); return key; +nomem: + key = ERR_PTR(-ENOMEM); + goto error; + +interrupted: + key_user_put(user); + key = ERR_PTR(-EINTR); + goto error; + +} /* end request_key_and_link() */ + +/*****************************************************************************/ +/* + * request a key + * - search the process's keyrings + * - check the list of keys being created or updated + * - call out to userspace for a key if supplementary info was provided + */ +struct key *request_key(struct key_type *type, + const char *description, + const char *callout_info) +{ + return request_key_and_link(type, description, callout_info, NULL); + } /* end request_key() */ EXPORT_SYMBOL(request_key); diff --git a/security/keys/request_key_auth.c b/security/keys/request_key_auth.c new file mode 100644 index 000000000000..f22264632229 --- /dev/null +++ b/security/keys/request_key_auth.c @@ -0,0 +1,180 @@ +/* request_key_auth.c: request key authorisation controlling key def + * + * Copyright (C) 2005 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include +#include "internal.h" + +static int request_key_auth_instantiate(struct key *, const void *, size_t); +static void request_key_auth_describe(const struct key *, struct seq_file *); +static void request_key_auth_destroy(struct key *); + +/* + * the request-key authorisation key type definition + */ +struct key_type key_type_request_key_auth = { + .name = ".request_key_auth", + .def_datalen = sizeof(struct request_key_auth), + .instantiate = request_key_auth_instantiate, + .describe = request_key_auth_describe, + .destroy = request_key_auth_destroy, +}; + +/*****************************************************************************/ +/* + * instantiate a request-key authorisation record + */ +static int request_key_auth_instantiate(struct key *key, + const void *data, + size_t datalen) +{ + struct request_key_auth *rka, *irka; + struct key *instkey; + int ret; + + ret = -ENOMEM; + rka = kmalloc(sizeof(*rka), GFP_KERNEL); + if (rka) { + /* see if the calling process is already servicing the key + * request of another process */ + instkey = key_get_instantiation_authkey(0); + if (!IS_ERR(instkey)) { + /* it is - use that instantiation context here too */ + irka = instkey->payload.data; + rka->context = irka->context; + rka->pid = irka->pid; + key_put(instkey); + } + else { + /* it isn't - use this process as the context */ + rka->context = current; + rka->pid = current->pid; + } + + rka->target_key = key_get((struct key *) data); + key->payload.data = rka; + ret = 0; + } + + return ret; + +} /* end request_key_auth_instantiate() */ + +/*****************************************************************************/ +/* + * + */ +static void request_key_auth_describe(const struct key *key, + struct seq_file *m) +{ + struct request_key_auth *rka = key->payload.data; + + seq_puts(m, "key:"); + seq_puts(m, key->description); + seq_printf(m, " pid:%d", rka->pid); + +} /* end request_key_auth_describe() */ + +/*****************************************************************************/ +/* + * destroy an instantiation authorisation token key + */ +static void request_key_auth_destroy(struct key *key) +{ + struct request_key_auth *rka = key->payload.data; + + kenter("{%d}", key->serial); + + key_put(rka->target_key); + +} /* end request_key_auth_destroy() */ + +/*****************************************************************************/ +/* + * create a session keyring to be for the invokation of /sbin/request-key and + * stick an authorisation token in it + */ +struct key *request_key_auth_new(struct key *target, struct key **_rkakey) +{ + struct key *keyring, *rkakey = NULL; + char desc[20]; + int ret; + + kenter("%d,", target->serial); + + /* allocate a new session keyring */ + sprintf(desc, "_req.%u", target->serial); + + keyring = keyring_alloc(desc, current->fsuid, current->fsgid, 1, NULL); + if (IS_ERR(keyring)) { + kleave("= %ld", PTR_ERR(keyring)); + return keyring; + } + + /* allocate the auth key */ + sprintf(desc, "%x", target->serial); + + rkakey = key_alloc(&key_type_request_key_auth, desc, + current->fsuid, current->fsgid, + KEY_USR_VIEW, 1); + if (IS_ERR(rkakey)) { + key_put(keyring); + kleave("= %ld", PTR_ERR(rkakey)); + return rkakey; + } + + /* construct and attach to the keyring */ + ret = key_instantiate_and_link(rkakey, target, 0, keyring, NULL); + if (ret < 0) { + key_revoke(rkakey); + key_put(rkakey); + key_put(keyring); + kleave("= %d", ret); + return ERR_PTR(ret); + } + + *_rkakey = rkakey; + kleave(" = {%d} ({%d})", keyring->serial, rkakey->serial); + return keyring; + +} /* end request_key_auth_new() */ + +/*****************************************************************************/ +/* + * get the authorisation key for instantiation of a specific key if attached to + * the current process's keyrings + * - this key is inserted into a keyring and that is set as /sbin/request-key's + * session keyring + * - a target_id of zero specifies any valid token + */ +struct key *key_get_instantiation_authkey(key_serial_t target_id) +{ + struct task_struct *tsk = current; + struct key *instkey; + + /* we must have our own personal session keyring */ + if (!tsk->signal->session_keyring) + return ERR_PTR(-EACCES); + + /* and it must contain a suitable request authorisation key + * - lock RCU against session keyring changing + */ + rcu_read_lock(); + + instkey = keyring_search_instkey( + rcu_dereference(tsk->signal->session_keyring), target_id); + + rcu_read_unlock(); + return instkey; + +} /* end key_get_instantiation_authkey() */ -- cgit v1.2.3 From 16c29b67fb3bbacfc2a71f9e5f7d85728ef45efa Mon Sep 17 00:00:00 2001 From: Michael Halcrow Date: Thu, 23 Jun 2005 22:00:58 -0700 Subject: [PATCH] eCryptfs: export user key type Export this symbol to GPL modules for eCryptfs: an out-of-tree GPL'ed filesystem. Signed off by: Michael Halcrow Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- security/keys/user_defined.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'security') diff --git a/security/keys/user_defined.c b/security/keys/user_defined.c index c33d3614a0db..e446acba73d3 100644 --- a/security/keys/user_defined.c +++ b/security/keys/user_defined.c @@ -48,6 +48,8 @@ struct user_key_payload { char data[0]; /* actual data */ }; +EXPORT_SYMBOL_GPL(key_type_user); + /*****************************************************************************/ /* * instantiate a user defined key -- cgit v1.2.3 From 6b9921976f0861e04828b3aff66696c1f3fd900d Mon Sep 17 00:00:00 2001 From: Lorenzo Hernandez García-Hierro Date: Sat, 25 Jun 2005 14:54:34 -0700 Subject: [PATCH] selinux: add executable stack check This patch adds an execstack permission check that controls the ability to make the main process stack executable so that attempts to make the stack executable can still be prevented even if the process is allowed the existing execmem permission in order to e.g. perform runtime code generation. Note that this does not yet address thread stacks. Note also that unlike the execmem check, the execstack check is only applied on mprotect calls, not mmap calls, as the current security_file_mmap hook is not passed the necessary information presently. The original author of the code that makes the distinction of the stack region, is Ingo Molnar, who wrote it within his patch for /proc//maps markers. (http://marc.theaimsgroup.com/?l=linux-kernel&m=110719881508591&w=2) The patches also can be found at: http://pearls.tuxedo-es.org/patches/selinux/policy-execstack.patch http://pearls.tuxedo-es.org/patches/selinux/kernel-execstack.patch policy-execstack.patch is the patch that needs to be applied to the policy in order to support the execstack permission and exclude it from general_domain_access within macros/core_macros.te. kernel-execstack.patch adds such permission to the SELinux code within the kernel and adds the proper permission check to the selinux_file_mprotect() hook. Signed-off-by: Lorenzo Hernandez Garcia-Hierro Acked-by: James Morris Acked-by: Stephen Smalley Cc: Ingo Molnar Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- security/selinux/hooks.c | 10 ++++++++++ security/selinux/include/av_perm_to_string.h | 1 + security/selinux/include/av_permissions.h | 1 + 3 files changed, 12 insertions(+) (limited to 'security') diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 87302a49067b..ad725213f568 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -2488,6 +2488,16 @@ static int selinux_file_mprotect(struct vm_area_struct *vma, if (rc) return rc; } + if (!vma->vm_file && (prot & PROT_EXEC) && + vma->vm_start <= vma->vm_mm->start_stack && + vma->vm_end >= vma->vm_mm->start_stack) { + /* Attempt to make the process stack executable. + * This has an additional execstack check. + */ + rc = task_has_perm(current, current, PROCESS__EXECSTACK); + if (rc) + return rc; + } #endif return file_map_prot_check(vma->vm_file, prot, vma->vm_flags&VM_SHARED); diff --git a/security/selinux/include/av_perm_to_string.h b/security/selinux/include/av_perm_to_string.h index 8928bb4d3c53..e81f0226c37d 100644 --- a/security/selinux/include/av_perm_to_string.h +++ b/security/selinux/include/av_perm_to_string.h @@ -70,6 +70,7 @@ S_(SECCLASS_PROCESS, PROCESS__DYNTRANSITION, "dyntransition") S_(SECCLASS_PROCESS, PROCESS__SETCURRENT, "setcurrent") S_(SECCLASS_PROCESS, PROCESS__EXECMEM, "execmem") + S_(SECCLASS_PROCESS, PROCESS__EXECSTACK, "execstack") S_(SECCLASS_MSGQ, MSGQ__ENQUEUE, "enqueue") S_(SECCLASS_MSG, MSG__SEND, "send") S_(SECCLASS_MSG, MSG__RECEIVE, "receive") diff --git a/security/selinux/include/av_permissions.h b/security/selinux/include/av_permissions.h index bdfce4ca8f8e..38ce18b33284 100644 --- a/security/selinux/include/av_permissions.h +++ b/security/selinux/include/av_permissions.h @@ -465,6 +465,7 @@ #define PROCESS__DYNTRANSITION 0x00800000UL #define PROCESS__SETCURRENT 0x01000000UL #define PROCESS__EXECMEM 0x02000000UL +#define PROCESS__EXECSTACK 0x04000000UL #define IPC__CREATE 0x00000001UL #define IPC__DESTROY 0x00000002UL -- cgit v1.2.3 From 09ffd94fb15d85fbf9eebb8180f50264b264d6fe Mon Sep 17 00:00:00 2001 From: Lorenzo Hernández García-Hierro Date: Sat, 25 Jun 2005 14:54:35 -0700 Subject: [PATCH] selinux: add executable heap check This patch,based on sample code by Roland McGrath, adds an execheap permission check that controls the ability to make the heap executable so that this can be prevented in almost all cases (the X server is presently an exception, but this will hopefully be resolved in the future) so that even programs with execmem permission will need to have the anonymous memory mapped in order to make it executable. The only reason that we use a permission check for such restriction (vs. making it unconditional) is that the X module loader presently needs it; it could possibly be made unconditional in the future when X is changed. The policy patch for the execheap permission is available at: http://pearls.tuxedo-es.org/patches/selinux/policy-execheap.patch Signed-off-by: Lorenzo Hernandez Garcia-Hierro Acked-by: James Morris Acked-by: Stephen Smalley Cc: Ingo Molnar Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- security/selinux/hooks.c | 11 +++++++++++ security/selinux/include/av_perm_to_string.h | 1 + security/selinux/include/av_permissions.h | 1 + 3 files changed, 13 insertions(+) (limited to 'security') diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index ad725213f568..932eef18db33 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -2477,6 +2477,17 @@ static int selinux_file_mprotect(struct vm_area_struct *vma, prot = reqprot; #ifndef CONFIG_PPC32 + if ((prot & PROT_EXEC) && !(vma->vm_flags & VM_EXECUTABLE) && + (vma->vm_start >= vma->vm_mm->start_brk && + vma->vm_end <= vma->vm_mm->brk)) { + /* + * We are making an executable mapping in the brk region. + * This has an additional execheap check. + */ + rc = task_has_perm(current, current, PROCESS__EXECHEAP); + if (rc) + return rc; + } if (vma->vm_file != NULL && vma->anon_vma != NULL && (prot & PROT_EXEC)) { /* * We are making executable a file mapping that has diff --git a/security/selinux/include/av_perm_to_string.h b/security/selinux/include/av_perm_to_string.h index e81f0226c37d..1deb59e1b762 100644 --- a/security/selinux/include/av_perm_to_string.h +++ b/security/selinux/include/av_perm_to_string.h @@ -71,6 +71,7 @@ S_(SECCLASS_PROCESS, PROCESS__SETCURRENT, "setcurrent") S_(SECCLASS_PROCESS, PROCESS__EXECMEM, "execmem") S_(SECCLASS_PROCESS, PROCESS__EXECSTACK, "execstack") + S_(SECCLASS_PROCESS, PROCESS__EXECHEAP, "execheap") S_(SECCLASS_MSGQ, MSGQ__ENQUEUE, "enqueue") S_(SECCLASS_MSG, MSG__SEND, "send") S_(SECCLASS_MSG, MSG__RECEIVE, "receive") diff --git a/security/selinux/include/av_permissions.h b/security/selinux/include/av_permissions.h index 38ce18b33284..a78b5d59c9fc 100644 --- a/security/selinux/include/av_permissions.h +++ b/security/selinux/include/av_permissions.h @@ -466,6 +466,7 @@ #define PROCESS__SETCURRENT 0x01000000UL #define PROCESS__EXECMEM 0x02000000UL #define PROCESS__EXECSTACK 0x04000000UL +#define PROCESS__EXECHEAP 0x08000000UL #define IPC__CREATE 0x00000001UL #define IPC__DESTROY 0x00000002UL -- cgit v1.2.3 From b2b18660066997420b716c1881a6be8b82700d97 Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Sat, 25 Jun 2005 14:55:38 -0700 Subject: [PATCH] RCU: clean up a few remaining synchronize_kernel() calls 2.6.12-rc6-mm1 has a few remaining synchronize_kernel()s, some (but not all) in comments. This patch changes these synchronize_kernel() calls (and comments) to synchronize_rcu() or synchronize_sched() as follows: - arch/x86_64/kernel/mce.c mce_read(): change to synchronize_sched() to handle races with machine-check exceptions (synchronize_rcu() would not cut it given RCU implementations intended for hardcore realtime use. - drivers/input/serio/i8042.c i8042_stop(): change to synchronize_sched() to handle races with i8042_interrupt() interrupt handler. Again, synchronize_rcu() would not cut it given RCU implementations intended for hardcore realtime use. - include/*/kdebug.h comments: change to synchronize_sched() to handle races with NMIs. As before, synchronize_rcu() would not cut it... - include/linux/list.h comment: change to synchronize_rcu(), since this comment is for list_del_rcu(). - security/keys/key.c unregister_key_type(): change to synchronize_rcu(), since this is interacting with RCU read side. - security/keys/process_keys.c install_session_keyring(): change to synchronize_rcu(), since this is interacting with RCU read side. Signed-off-by: "Paul E. McKenney" Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/x86_64/kernel/mce.c | 2 +- drivers/input/serio/i8042.c | 2 +- include/asm-i386/kdebug.h | 2 +- include/asm-ppc64/kdebug.h | 2 +- include/asm-sparc64/kdebug.h | 2 +- include/asm-x86_64/kdebug.h | 2 +- include/linux/list.h | 2 +- security/keys/key.c | 2 +- security/keys/process_keys.c | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) (limited to 'security') diff --git a/arch/x86_64/kernel/mce.c b/arch/x86_64/kernel/mce.c index 7ab15c8ab95f..21e70625a495 100644 --- a/arch/x86_64/kernel/mce.c +++ b/arch/x86_64/kernel/mce.c @@ -411,7 +411,7 @@ static ssize_t mce_read(struct file *filp, char __user *ubuf, size_t usize, loff memset(mcelog.entry, 0, next * sizeof(struct mce)); mcelog.next = 0; - synchronize_kernel(); + synchronize_sched(); /* Collect entries that were still getting written before the synchronize. */ diff --git a/drivers/input/serio/i8042.c b/drivers/input/serio/i8042.c index 5900de3c3f4f..a9bf549c8dc5 100644 --- a/drivers/input/serio/i8042.c +++ b/drivers/input/serio/i8042.c @@ -396,7 +396,7 @@ static void i8042_stop(struct serio *serio) struct i8042_port *port = serio->port_data; port->exists = 0; - synchronize_kernel(); + synchronize_sched(); port->serio = NULL; } diff --git a/include/asm-i386/kdebug.h b/include/asm-i386/kdebug.h index de6498b0d493..b3f8d5f59d5d 100644 --- a/include/asm-i386/kdebug.h +++ b/include/asm-i386/kdebug.h @@ -18,7 +18,7 @@ struct die_args { }; /* Note - you should never unregister because that can race with NMIs. - If you really want to do it first unregister - then synchronize_kernel - then free. + If you really want to do it first unregister - then synchronize_sched - then free. */ int register_die_notifier(struct notifier_block *nb); extern struct notifier_block *i386die_chain; diff --git a/include/asm-ppc64/kdebug.h b/include/asm-ppc64/kdebug.h index 488634258a72..d383d161cf8d 100644 --- a/include/asm-ppc64/kdebug.h +++ b/include/asm-ppc64/kdebug.h @@ -17,7 +17,7 @@ struct die_args { /* Note - you should never unregister because that can race with NMIs. - If you really want to do it first unregister - then synchronize_kernel - + If you really want to do it first unregister - then synchronize_sched - then free. */ int register_die_notifier(struct notifier_block *nb); diff --git a/include/asm-sparc64/kdebug.h b/include/asm-sparc64/kdebug.h index f70d3dad01f9..6321f5a0198d 100644 --- a/include/asm-sparc64/kdebug.h +++ b/include/asm-sparc64/kdebug.h @@ -16,7 +16,7 @@ struct die_args { }; /* Note - you should never unregister because that can race with NMIs. - * If you really want to do it first unregister - then synchronize_kernel + * If you really want to do it first unregister - then synchronize_sched * - then free. */ int register_die_notifier(struct notifier_block *nb); diff --git a/include/asm-x86_64/kdebug.h b/include/asm-x86_64/kdebug.h index 6277f75cbb4b..b90341994d80 100644 --- a/include/asm-x86_64/kdebug.h +++ b/include/asm-x86_64/kdebug.h @@ -14,7 +14,7 @@ struct die_args { }; /* Note - you should never unregister because that can race with NMIs. - If you really want to do it first unregister - then synchronize_kernel - then free. + If you really want to do it first unregister - then synchronize_sched - then free. */ int register_die_notifier(struct notifier_block *nb); extern struct notifier_block *die_chain; diff --git a/include/linux/list.h b/include/linux/list.h index 399b51d17218..aab2db21b013 100644 --- a/include/linux/list.h +++ b/include/linux/list.h @@ -185,7 +185,7 @@ static inline void list_del(struct list_head *entry) * list_for_each_entry_rcu(). * * Note that the caller is not permitted to immediately free - * the newly deleted entry. Instead, either synchronize_kernel() + * the newly deleted entry. Instead, either synchronize_rcu() * or call_rcu() must be used to defer freeing until an RCU * grace period has elapsed. */ diff --git a/security/keys/key.c b/security/keys/key.c index 3304d37bb379..fb89f9844465 100644 --- a/security/keys/key.c +++ b/security/keys/key.c @@ -980,7 +980,7 @@ void unregister_key_type(struct key_type *ktype) spin_unlock(&key_serial_lock); /* make sure everyone revalidates their keys */ - synchronize_kernel(); + synchronize_rcu(); /* we should now be able to destroy the payloads of all the keys of * this type with impunity */ diff --git a/security/keys/process_keys.c b/security/keys/process_keys.c index 34db087bbcc7..9b0369c5a223 100644 --- a/security/keys/process_keys.c +++ b/security/keys/process_keys.c @@ -234,7 +234,7 @@ static int install_session_keyring(struct task_struct *tsk, ret = 0; /* we're using RCU on the pointer */ - synchronize_kernel(); + synchronize_rcu(); key_put(old); error: return ret; -- cgit v1.2.3 From 9a5f04bf798254390f89445ecf0b6f4c70ddc1f8 Mon Sep 17 00:00:00 2001 From: Jesper Juhl Date: Sat, 25 Jun 2005 14:58:51 -0700 Subject: [PATCH] selinux: kfree cleanup kfree(NULL) is legal. Signed-off-by: Jesper Juhl Acked-by: Stephen Smalley Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- security/selinux/hooks.c | 3 +-- security/selinux/selinuxfs.c | 9 +++------ security/selinux/ss/conditional.c | 9 +++------ security/selinux/ss/policydb.c | 15 +++++---------- security/selinux/ss/services.c | 6 ++---- 5 files changed, 14 insertions(+), 28 deletions(-) (limited to 'security') diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 932eef18db33..17a1189f1ff8 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -1658,9 +1658,8 @@ static int selinux_bprm_secureexec (struct linux_binprm *bprm) static void selinux_bprm_free_security(struct linux_binprm *bprm) { - struct bprm_security_struct *bsec = bprm->security; + kfree(bprm->security); bprm->security = NULL; - kfree(bsec); } extern struct vfsmount *selinuxfs_mount; diff --git a/security/selinux/selinuxfs.c b/security/selinux/selinuxfs.c index 07221568b505..8eb140dd2e4b 100644 --- a/security/selinux/selinuxfs.c +++ b/security/selinux/selinuxfs.c @@ -951,8 +951,7 @@ static int sel_make_bools(void) u32 sid; /* remove any existing files */ - if (bool_pending_values) - kfree(bool_pending_values); + kfree(bool_pending_values); sel_remove_bools(dir); @@ -997,10 +996,8 @@ static int sel_make_bools(void) out: free_page((unsigned long)page); if (names) { - for (i = 0; i < num; i++) { - if (names[i]) - kfree(names[i]); - } + for (i = 0; i < num; i++) + kfree(names[i]); kfree(names); } return ret; diff --git a/security/selinux/ss/conditional.c b/security/selinux/ss/conditional.c index b53441184aca..e2057f5a411a 100644 --- a/security/selinux/ss/conditional.c +++ b/security/selinux/ss/conditional.c @@ -166,16 +166,14 @@ static void cond_list_destroy(struct cond_node *list) void cond_policydb_destroy(struct policydb *p) { - if (p->bool_val_to_struct != NULL) - kfree(p->bool_val_to_struct); + kfree(p->bool_val_to_struct); avtab_destroy(&p->te_cond_avtab); cond_list_destroy(p->cond_list); } int cond_init_bool_indexes(struct policydb *p) { - if (p->bool_val_to_struct) - kfree(p->bool_val_to_struct); + kfree(p->bool_val_to_struct); p->bool_val_to_struct = (struct cond_bool_datum**) kmalloc(p->p_bools.nprim * sizeof(struct cond_bool_datum*), GFP_KERNEL); if (!p->bool_val_to_struct) @@ -185,8 +183,7 @@ int cond_init_bool_indexes(struct policydb *p) int cond_destroy_bool(void *key, void *datum, void *p) { - if (key) - kfree(key); + kfree(key); kfree(datum); return 0; } diff --git a/security/selinux/ss/policydb.c b/security/selinux/ss/policydb.c index 14190efbf333..785c33cf4864 100644 --- a/security/selinux/ss/policydb.c +++ b/security/selinux/ss/policydb.c @@ -590,17 +590,12 @@ void policydb_destroy(struct policydb *p) hashtab_destroy(p->symtab[i].table); } - for (i = 0; i < SYM_NUM; i++) { - if (p->sym_val_to_name[i]) - kfree(p->sym_val_to_name[i]); - } + for (i = 0; i < SYM_NUM; i++) + kfree(p->sym_val_to_name[i]); - if (p->class_val_to_struct) - kfree(p->class_val_to_struct); - if (p->role_val_to_struct) - kfree(p->role_val_to_struct); - if (p->user_val_to_struct) - kfree(p->user_val_to_struct); + kfree(p->class_val_to_struct); + kfree(p->role_val_to_struct); + kfree(p->user_val_to_struct); avtab_destroy(&p->te_avtab); diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c index b6149147d5cb..922bb45054aa 100644 --- a/security/selinux/ss/services.c +++ b/security/selinux/ss/services.c @@ -1705,11 +1705,9 @@ out: err: if (*names) { for (i = 0; i < *len; i++) - if ((*names)[i]) - kfree((*names)[i]); + kfree((*names)[i]); } - if (*values) - kfree(*values); + kfree(*values); goto out; } -- cgit v1.2.3 From 6931dfc9f3f81d148b7ed0ab3fd796f8b986a995 Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Thu, 30 Jun 2005 02:58:51 -0700 Subject: [PATCH] selinux_sb_copy_data() should not require a whole page Currently selinux_sb_copy_data requires an entire page be allocated to *orig when the function is called. This "requirement" is based on the fact that we call copy_page(in_save, nosec_save) and in_save = orig when the data is not FS_BINARY_MOUNTDATA. This means that if a caller were to call do_kern_mount with only about 10 bytes of options, they would get passed here and then we would corrupt PAGE_SIZE - 10 bytes of memory (with all zeros.) Currently it appears all in kernel FS's use one page of data so this has not been a problem. An out of kernel FS did just what is described above and it would almost always panic shortly after they tried to mount. From looking else where in the kernel it is obvious that this string of data must always be null terminated. (See example in do_mount where it always zeros the last byte.) Thus I suggest we use strcpy in place of copy_page. In this way we make sure the amount we copy is always less than or equal to the amount we received and since do_mount is zeroing the last byte this should be safe for all. Signed-off-by: Eric Paris Cc: Stephen Smalley Acked-by: James Morris Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- security/selinux/hooks.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'security') diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 17a1189f1ff8..6be273851144 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -68,6 +68,7 @@ #include #include #include +#include #include "avc.h" #include "objsec.h" @@ -1943,7 +1944,7 @@ static int selinux_sb_copy_data(struct file_system_type *type, void *orig, void } } while (*in_end++); - copy_page(in_save, nosec_save); + strcpy(in_save, nosec_save); free_page((unsigned long)nosec_save); out: return rc; -- cgit v1.2.3