Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S263394AbTH0PdC (ORCPT ); Wed, 27 Aug 2003 11:33:02 -0400 Received: (majordomo@vger.kernel.org) by vger.kernel.org id S263343AbTH0PdB (ORCPT ); Wed, 27 Aug 2003 11:33:01 -0400 Received: from pub237.cambridge.redhat.com ([213.86.99.237]:26834 "EHLO warthog.warthog") by vger.kernel.org with ESMTP id S263503AbTH0Pb3 (ORCPT ); Wed, 27 Aug 2003 11:31:29 -0400 From: David Howells In-Reply-To: References: To: Linus Torvalds Cc: linux-kernel@vger.kernel.org Subject: [PATCH/RFC] authentication / encryption key retention User-Agent: EMH/1.14.1 SEMI/1.14.4 (Hosorogi) FLIM/1.14.4 (=?ISO-8859-4?Q?Kashiharajing=FE-mae?=) APEL/10.4 Emacs/21.2 (i386-redhat-linux-gnu) MULE/5.0 (SAKAKI) MIME-Version: 1.0 (generated by SEMI 1.14.4 - "Hosorogi") Content-Type: text/plain; charset=US-ASCII Date: Wed, 27 Aug 2003 16:31:19 +0100 Message-ID: <30551.1061998279@redhat.com> Sender: linux-kernel-owner@vger.kernel.org X-Mailing-List: linux-kernel@vger.kernel.org Content-Length: 28492 Lines: 1090 Hi Linus, Here's an incomplete patch that introduces basic key retention services into the kernel. What it does: - manages key creation / destruction - manages keyrings as special keys - creates a default UID keyring attached to user_struct - permits a per-thread keyring that isn't inherited across fork, clone or execve - creates a per-process keyring that is inherited across clone + CLONE_THREAD, but is not inherited across fork, clone or execve - creates a "per-session" keyring that is inherited across fork, clone and exec (non-SUID). this is also cleared when switch_uid() is called. - permits the set of system keys to be viewed in /proc/keys - assigns every key a unique ID What it doesn't yet do or hasn't yet been tested: - GID default keyrings - key addition and retirement - key searching - keyring subscription David diff -uNr linux-2.6.0-test4/fs/exec.c linux-2.6.0-test4-keys/fs/exec.c --- linux-2.6.0-test4/fs/exec.c 2003-08-26 14:04:44.000000000 +0100 +++ linux-2.6.0-test4-keys/fs/exec.c 2003-08-27 15:00:11.000000000 +0100 @@ -45,6 +45,7 @@ #include #include #include +#include #include #include @@ -889,10 +890,15 @@ void compute_creds(struct linux_binprm *bprm) { + if (bprm->e_uid != current->uid) + suid_keys(current); + exec_keys(current); + task_lock(current); if (bprm->e_uid != current->uid || bprm->e_gid != current->gid) { current->mm->dumpable = 0; - + suid_keys(current); + if (must_not_trace_exec(current) || atomic_read(¤t->fs->count) > 1 || atomic_read(¤t->files->count) > 1 diff -uNr linux-2.6.0-test4/include/linux/key.h linux-2.6.0-test4-keys/include/linux/key.h --- linux-2.6.0-test4/include/linux/key.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.0-test4-keys/include/linux/key.h 2003-08-27 15:35:49.000000000 +0100 @@ -0,0 +1,123 @@ +/* key.h: authentication token and access key management + * + * Copyright (C) 2003 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. + */ + +#ifndef _LINUX_KEY_H +#define _LINUX_KEY_H + +#include +#include +#include +#include + +#define KEY_DEBUGGING + +struct seq_file; + +struct key; +struct key_type; +struct keyring_list; +struct keyring_name; + +/*****************************************************************************/ +/* + * authentication token / access credential / keyring + * - types of key include: + * - keyrings + * - disk encryption IDs + * - Kerberos TGTs + */ +struct key { + atomic_t usage; + unsigned serial; /* key serial number (0 if retired) */ + struct rb_node serial_node; + struct key_type *type; /* type of key */ + rwlock_t lock; + unsigned flags; +#define KEY_FLAG_DEAD 0x00000001 /* set if key is deleted */ +#define KEY_FLAG_RETIRED 0x00000002 /* set if key is retired from service */ +#define KEY_FLAG_PUBLIC_KEYRING 0x00000004 /* set if publicly subscribable keyring */ + +#ifdef KEY_DEBUGGING + unsigned magic; +#define KEY_DEBUG_MAGIC 0x18273645u +#define KEY_DEBUG_MAGIC_X 0xf8e9dacbu +#endif + + union { + struct keyring_name *ringname; + void *data; + } description; + + union { + void *data; + struct keyring_list *subscriptions; + } payload; +}; + +struct key_type { + const char *name; /* name of type */ + + int (*match)(const struct key *key, const void *criterion); + void (*clear)(struct key *key); + void (*describe)(const struct key *key, struct seq_file *p); +}; + +extern int key_alloc(struct key_type *type, struct key **_key); +extern void key_retire(struct key *key); +extern void key_put(struct key *key); + +extern int keyring_alloc(struct key *source, struct key **_key); + +/*****************************************************************************/ +/* + * list of subscribed keys + * - when plumbing the depths of the key tree, there's a hard limit set on the + * depth to deal with cycles in the tree + */ +struct keyring_list { + unsigned maxkeys; /* max keys this list can hold */ + unsigned nkeys; /* number of keys this list currently holds */ + struct key *keys[0]; +}; + +#define KEYRING_SEARCH_MAX_DEPTH 6 + +/*****************************************************************************/ +/* + * name of keyring + * - publicly available keyrings must be labelled uniquely + */ +struct keyring_name { + struct key *keyring; /* keyring key */ + struct rb_node name_node; /* node in tree of public names */ + char name[0]; /* label for this keyring */ +}; + +extern int keyring_search(struct key *keyring, + const struct key_type *type, + const void *criterion, + struct key **_key); + +extern int keyring_filter(struct key *source, + int (*func)(struct key *key, void *criterion), + void *criterion, + struct key **_subset); + +extern int keyring_set_name(struct key *keyring, const char *fmt, ...) +__attribute__((format(printf, 2, 3))); + +extern struct key root_user_keyring; +extern int copy_keys(unsigned long clone_flags, struct task_struct *tsk); +extern void exit_keys(struct task_struct *tsk); +extern int suid_keys(struct task_struct *tsk); +extern int exec_keys(struct task_struct *tsk); + +#endif /* _LINUX_KEY_H */ diff -uNr linux-2.6.0-test4/include/linux/sched.h linux-2.6.0-test4-keys/include/linux/sched.h --- linux-2.6.0-test4/include/linux/sched.h 2003-08-26 14:04:47.000000000 +0100 +++ linux-2.6.0-test4-keys/include/linux/sched.h 2003-08-27 13:05:43.000000000 +0100 @@ -290,6 +290,8 @@ atomic_t processes; /* How many processes does this user have? */ atomic_t files; /* How many open files does this user have? */ + struct key *keyring; /* UID specific keyring */ + /* Hash table maintenance information */ struct list_head uidhash_list; uid_t uid; @@ -402,6 +404,9 @@ kernel_cap_t cap_effective, cap_inheritable, cap_permitted; int keep_capabilities:1; struct user_struct *user; + struct key *session_keyring; /* keyring inherited over fork */ + struct key *process_keyring; /* keyring private to this process (CLONE_THREAD) */ + struct key *thread_keyring; /* keyring private to this thread */ /* limits */ struct rlimit rlim[RLIM_NLIMITS]; unsigned short used_math; diff -uNr linux-2.6.0-test4/kernel/exit.c linux-2.6.0-test4-keys/kernel/exit.c --- linux-2.6.0-test4/kernel/exit.c 2003-08-26 13:27:21.000000000 +0100 +++ linux-2.6.0-test4-keys/kernel/exit.c 2003-08-27 13:09:01.000000000 +0100 @@ -22,6 +22,7 @@ #include #include #include +#include #include #include @@ -711,6 +712,7 @@ exit_namespace(tsk); exit_itimers(tsk); exit_thread(); + exit_keys(tsk); if (tsk->leader) disassociate_ctty(1); diff -uNr linux-2.6.0-test4/kernel/fork.c linux-2.6.0-test4-keys/kernel/fork.c --- linux-2.6.0-test4/kernel/fork.c 2003-08-26 14:04:48.000000000 +0100 +++ linux-2.6.0-test4-keys/kernel/fork.c 2003-08-27 13:08:54.000000000 +0100 @@ -30,6 +30,7 @@ #include #include #include +#include #include #include @@ -884,8 +885,10 @@ goto bad_fork_cleanup_sighand; if ((retval = copy_mm(clone_flags, p))) goto bad_fork_cleanup_signal; - if ((retval = copy_namespace(clone_flags, p))) + if ((retval = copy_keys(clone_flags, p))) goto bad_fork_cleanup_mm; + if ((retval = copy_namespace(clone_flags, p))) + goto bad_fork_cleanup_keys; retval = copy_thread(0, clone_flags, stack_start, stack_size, p, regs); if (retval) goto bad_fork_cleanup_namespace; @@ -1021,6 +1024,8 @@ bad_fork_cleanup_namespace: exit_namespace(p); +bad_fork_cleanup_keys: + exit_keys(p); bad_fork_cleanup_mm: exit_mm(p); bad_fork_cleanup_signal: diff -uNr linux-2.6.0-test4/kernel/key.c linux-2.6.0-test4-keys/kernel/key.c --- linux-2.6.0-test4/kernel/key.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.0-test4-keys/kernel/key.c 2003-08-27 16:12:59.000000000 +0100 @@ -0,0 +1,754 @@ +/* key.c: authentication token and access key management + * + * Copyright (C) 2003 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 +#include +#include +#include +#include + +static kmem_cache_t *key_jar; +static struct rb_root key_serial_tree; +static unsigned key_serial_next = 2; +static spinlock_t key_serial_lock = SPIN_LOCK_UNLOCKED; + +static struct rb_root keyring_name_tree; +static rwlock_t keyring_name_lock = RW_LOCK_UNLOCKED; + +static int keyring_match(const struct key *key, const void *criterion); +static void keyring_clear(struct key *key); +static void keyring_describe(const struct key *keyring, struct seq_file *m); + +static struct key_type key_type_keyring = { + .name = "keyring", + .match = keyring_match, + .clear = keyring_clear, + .describe = keyring_describe, +}; + +struct key root_user_keyring = { + .usage = ATOMIC_INIT(1), + .serial = 1, + .type = &key_type_keyring, + .lock = RW_LOCK_UNLOCKED, +#ifdef KEY_DEBUGGING + .magic = KEY_DEBUG_MAGIC, +#endif +}; + +#ifdef CONFIG_PROC_FS +static int key_proc_open(struct inode *inode, struct file *file); +static void *key_proc_start(struct seq_file *p, loff_t *_pos); +static void *key_proc_next(struct seq_file *p, void *v, loff_t *_pos); +static void key_proc_stop(struct seq_file *p, void *v); +static int key_proc_show(struct seq_file *m, void *v); + +static struct seq_operations key_proc_ops = { + .start = key_proc_start, + .next = key_proc_next, + .stop = key_proc_stop, + .show = key_proc_show, +}; + +static struct file_operations key_proc_fops = { + .open = key_proc_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release, +}; +#endif + +#ifdef KEY_DEBUGGING +static void __key_validate(const struct key *key) +{ + printk("__key_validate: key %p {%08x} should be {%08x}\n", + key, key->magic, KEY_DEBUG_MAGIC); + BUG(); +} +static inline void key_validate(const struct key *key) +{ + if (key && key->magic != KEY_DEBUG_MAGIC) + __key_validate(key); +} +#else +static inline void key_validate(const struct key *key) {} +#endif + +/*****************************************************************************/ +/* + * allocate a key of the specified type + * - the key is provided with a unique serial number + */ +int key_alloc(struct key_type *type, struct key **_key) +{ + struct rb_node *parent, **p; + struct key *key, *xkey; + + *_key = NULL; + + key = kmem_cache_alloc(key_jar, SLAB_KERNEL); + if (!key) + return -ENOMEM; + + memset(key, 0, sizeof(*key)); + + atomic_set(&key->usage, 1); + rwlock_init(&key->lock); + key->type = type; +#ifdef KEY_DEBUGGING + key->magic = KEY_DEBUG_MAGIC; +#endif + + spin_lock(&key_serial_lock); + + /* propose a serial number and try to insert it into the tree */ + if (key_serial_next < 1) + key_serial_next = 1; + key->serial = key_serial_next++; + + parent = NULL; + p = &key_serial_tree.rb_node; + + while (*p) { + parent = *p; + xkey = rb_entry(parent, struct key, serial_node); + + if (key->serial < xkey->serial) + p = &(*p)->rb_left; + else if (key->serial > xkey->serial) + p = &(*p)->rb_right; + else + goto serial_exists; + } + goto insert_here; + + /* we found a key with the proposed serial number - walk the tree from + * that point looking for the next unused serial number */ + serial_exists: + for (;;) { + if (key_serial_next < 1) + key_serial_next = 1; + key->serial = key_serial_next++; + + if (!parent->rb_parent) + p = &key_serial_tree.rb_node; + else if (parent->rb_parent->rb_left == parent) + p = &parent->rb_parent->rb_left; + else + p = &parent->rb_parent->rb_right; + + parent = rb_next(parent); + if (!parent) + break; + + xkey = rb_entry(parent, struct key, serial_node); + if (key->serial < xkey->serial) + goto insert_here; + } + + insert_here: + rb_link_node(&key->serial_node, parent, p); + rb_insert_color(&key->serial_node, &key_serial_tree); + spin_unlock(&key_serial_lock); + + *_key = key; + return 0; +} /* end key_alloc() */ + +EXPORT_SYMBOL(key_alloc); + +/*****************************************************************************/ +/* + * dispose of a key + */ +void key_put(struct key *key) +{ + if (!key) + return; + + key_validate(key); + if (atomic_dec_and_lock(&key->usage, &key_serial_lock)) { + key->flags |= KEY_FLAG_DEAD; + rb_erase(&key->serial_node, &key_serial_tree); + spin_unlock(&key_serial_lock); + + key->type->clear(key); +#ifdef KEY_DEBUGGING + key->magic = KEY_DEBUG_MAGIC_X; +#endif + kmem_cache_free(key_jar, key); + } + +} /* end key_put() */ + +EXPORT_SYMBOL(key_put); + +/*****************************************************************************/ +/* + * retire a key from service + */ +void key_retire(struct key *key) +{ + key_validate(key); + write_lock(&key->lock); + key->flags |= KEY_FLAG_RETIRED; + write_unlock(&key->lock); +} /* end key_retire() */ + +EXPORT_SYMBOL(key_retire); + +/*****************************************************************************/ +/* + * allocate or duplicate a keyring + * - the new keyring does not get a name attached, even if duplicated + */ +int keyring_alloc(struct key *source, struct key **_key) +{ + struct keyring_list *sklist, *klist; + struct key *keyring; + unsigned max; + size_t size; + int ret, loop; + + ret = key_alloc(&key_type_keyring, _key); + if (ret < 0 || !source) + return ret; + + keyring = *_key; + key_validate(keyring); + + /* duplicate the list of subscribed keys */ + if (source->payload.subscriptions) { + const unsigned limit = + (PAGE_SIZE - sizeof(*klist)) / sizeof(struct key); + + max = 0; + klist = NULL; + + read_lock(&source->lock); + + /* we need to take care here as some other process may + * resize the list under us when we drop the spinlock + * to perform the allocation + */ + while (sklist = source->payload.subscriptions, + sklist && sklist->nkeys > 0 && max < sklist->nkeys + ) { + max = sklist->nkeys; + read_unlock(&source->lock); + + BUG_ON(max > limit); + + max = (max + 3) & ~3; + if (max > limit) + max = limit; + + size = sizeof(*klist) + sizeof(struct key) * max; + klist = kmalloc(size, GFP_KERNEL); + if (!klist) + goto nomem; + memset(klist, 0, size); + klist->maxkeys = max; + + read_lock(&source->lock); + } + + if (sklist && sklist->nkeys) { + klist->nkeys = sklist->nkeys; + memcpy(klist->keys, sklist->keys, + sklist->nkeys * sizeof(struct key)); + + for (loop = klist->nkeys - 1; loop >= 0; loop--) + atomic_inc(&klist->keys[loop]->usage); + + keyring->payload.subscriptions = klist; + } + else if (klist) { + kfree(klist); + } + + read_unlock(&source->lock); + } + + return ret; + + nomem: + key_put(keyring); + *_key = NULL; + return -ENOMEM; +} /* end keyring_alloc() */ + +EXPORT_SYMBOL(keyring_alloc); + +/*****************************************************************************/ +/* + * search the supplied keyring tree for a key that matches the criterion + * - perform a depth-first search up to the prescribed limit + */ +int keyring_search(struct key *keyring, + const struct key_type *type, + const void *criterion, + struct key **_key) +{ + struct { + struct key *keyring; + struct keyring_list *keylist; + int kix; + } stack[KEYRING_SEARCH_MAX_DEPTH]; + + struct keyring_list *keylist; + struct key *key; + int sp, psp, kix; + + key_validate(keyring); + + *_key = NULL; + + if (keyring->type != &key_type_keyring) + return -EINVAL; + + sp = 0; + + descend: + read_lock(&keyring->lock); + if (keyring->flags & KEY_FLAG_RETIRED) + goto retired; + + keylist = keyring->payload.subscriptions; + kix = 0; + + ascend: + while (kix < keylist->nkeys) { + key = keylist->keys[kix]; + + if (key->type == type && key->type->match(key, criterion)) { + if (keyring->flags & KEY_FLAG_RETIRED) + goto next; + atomic_inc(&key->usage); + goto found; + } + + if (key->type == &key_type_keyring) { + if (sp >= KEYRING_SEARCH_MAX_DEPTH) + goto next; + + /* evade loops in the keyring tree */ + for (psp = 0; psp < sp; psp++) + if (stack[psp].keyring == keyring) + goto next; + + stack[sp].keyring = keyring; + stack[sp].keylist = keylist; + stack[sp].kix = kix; + sp++; + goto descend; + } + + next: + kix++; + } + + retired: + read_unlock(&keyring->lock); + + if (sp > 0) { + sp--; + keyring = stack[sp].keyring; + keylist = stack[sp].keylist; + kix = stack[sp].kix + 1; + goto ascend; + } + + return -ENOENT; + + found: + read_unlock(&keyring->lock); + + for (; sp > 0; sp--) + read_unlock(&stack[sp].keyring->lock); + + key_validate(key); + + *_key = key; + return 0; +} /* end keyring_search() */ + +EXPORT_SYMBOL(keyring_search); + +/*****************************************************************************/ +/* + * add a key to a keyring + */ +int keyring_add_key(struct key *keyring, struct key *key) +{ + struct keyring_list *klist, *xklist; + unsigned max; + size_t size; + + key_validate(keyring); + key_validate(key); + + max = 0; + xklist = NULL; + write_lock(&keyring->lock); + + if (keyring->flags & KEY_FLAG_RETIRED) + goto retired; + + klist = keyring->payload.subscriptions; + if (klist) { + if (klist->maxkeys > klist->nkeys) { + atomic_inc(&key->usage); + klist->keys[klist->nkeys++] = key; + write_unlock(&keyring->lock); + return 0; + } + + max = klist->maxkeys; + } + + write_unlock(&keyring->lock); + + /* try to grow the key list */ + again: + max += 4; + size = sizeof(*klist) + sizeof(*key) * max; + if (size > PAGE_SIZE) + return -ENFILE; + + xklist = kmalloc(size, GFP_KERNEL); + if (!xklist) + return -ENOMEM; + memset(xklist, 0, size); + xklist->maxkeys = max; + + write_lock(&keyring->lock); + + if (keyring->flags & KEY_FLAG_RETIRED) + goto retired; + + klist = keyring->payload.subscriptions; + if (klist) { + if (max <= klist->nkeys) + goto keyring_unexpectedly_resized; + + xklist->nkeys = klist->nkeys; + memcpy(xklist->keys, klist->keys, sizeof(*key) * klist->nkeys); + } + + atomic_inc(&key->usage); + xklist->keys[xklist->nkeys++] = key; + keyring->payload.subscriptions = xklist; + write_unlock(&keyring->lock); + + if (klist) + kfree(klist); + return 0; + + /* some other process changed the number of keys in the list */ + keyring_unexpectedly_resized: + max = klist->maxkeys; + write_unlock(&keyring->lock); + kfree(xklist); + goto again; + + retired: + write_unlock(&keyring->lock); + if (xklist) + kfree(xklist); + return -EINVAL; + +} /* end keyring_add_key() */ + +EXPORT_SYMBOL(keyring_add_key); + +/*****************************************************************************/ +/* + * set the name of a keyring + */ +int keyring_set_name(struct key *keyring, const char *fmt, ...) +{ + struct keyring_name *kname; + va_list va; + size_t len; + char buf[32]; + + key_validate(keyring); + + va_start(va, fmt); + vsnprintf(buf, 32, fmt, va); + va_end(va); + len = strlen(buf); + + kname = kmalloc(sizeof(*kname) + len + 1, GFP_KERNEL); + if (!kname) + return -ENOMEM; + memset(kname, 0, sizeof(*kname)); + memcpy(kname->name, buf, len + 1); + kname->keyring = keyring; + keyring->description.ringname = kname; + + return 0; +} /* end keyring_set_name() */ + +EXPORT_SYMBOL(keyring_set_name); + +/*****************************************************************************/ +/* + * keyrings aren't currently matchable + */ +static int keyring_match(const struct key *keyring, const void *criterion) +{ + struct keyring_name *kname; + + kname = keyring->description.ringname; + if (kname) + if (strcmp(kname->name, criterion) == 0) + return 1; + + return 0; +} /* end keyring_match() */ + +/*****************************************************************************/ +/* + * dispose of the data dangling from the corpse of a keyring + */ +static void keyring_clear(struct key *keyring) +{ + struct keyring_name *kname; + struct keyring_list *klist; + int loop; + + kname = keyring->description.ringname; + if (kname) { + if (keyring->flags & KEY_FLAG_PUBLIC_KEYRING) { + write_lock(&keyring_name_lock); + rb_erase(&kname->name_node, &keyring_name_tree); + write_unlock(&keyring_name_lock); + } + + kfree(kname); + } + + klist = keyring->payload.subscriptions; + if (klist) { + for (loop = klist->nkeys - 1; loop >= 0; loop--) + key_put(klist->keys[loop]); + kfree(klist); + } + +} /* end keyring_clear() */ + +/*****************************************************************************/ +/* + * describe the keyring + */ +static void keyring_describe(const struct key *keyring, struct seq_file *m) +{ + struct keyring_name *kname; + struct keyring_list *klist; + + kname = keyring->description.ringname; + if (kname) { + seq_printf(m, "%s", kname->name); + } + else { + seq_puts(m, "[anon]"); + } + + klist = keyring->payload.subscriptions; + if (klist) + seq_printf(m, ": %u/%u", klist->nkeys, klist->maxkeys); + else + seq_puts(m, ": empty"); + +} /* end keyring_describe() */ + +/*****************************************************************************/ +/* + * initialise the key management stuff + */ +static int __init key_init(void) +{ + struct proc_dir_entry *p; + + key_jar = kmem_cache_create("key_jar", sizeof(struct key), + 0, + SLAB_HWCACHE_ALIGN, NULL, NULL); + if (!key_jar) + panic("Cannot create key jar\n"); + + p = create_proc_entry("keys", 0, NULL); + if (!p) + panic("Cannot create /proc/keys\n"); + + p->proc_fops = &key_proc_fops; + + key_validate(&root_user_keyring); + rb_link_node(&root_user_keyring.serial_node, NULL, &key_serial_tree.rb_node); + rb_insert_color(&root_user_keyring.serial_node, &key_serial_tree); + keyring_set_name(&root_user_keyring, "_uid.0"); + + return 0; +} /* end key_init() */ + +subsys_initcall(key_init); + +/*****************************************************************************/ +/* + * copy the keys for fork + */ +int copy_keys(unsigned long clone_flags, struct task_struct *tsk) +{ + int ret = 0; + + key_validate(tsk->session_keyring); + key_validate(tsk->process_keyring); + key_validate(tsk->thread_keyring); + + if (tsk->session_keyring) + atomic_inc(&tsk->session_keyring->usage); + + if (tsk->process_keyring) { + if (clone_flags & CLONE_THREAD) { + atomic_inc(&tsk->process_keyring->usage); + } + else { + ret = keyring_alloc(NULL, &tsk->process_keyring); + if (ret == 0) + ret = keyring_set_name(tsk->process_keyring, + "_pid.%d", tsk->tgid); + } + } + + tsk->thread_keyring = NULL; + + return ret; +} /* end copy_keys() */ + +/*****************************************************************************/ +/* + * dispose of keys upon exit + */ +void exit_keys(struct task_struct *tsk) +{ + key_put(tsk->session_keyring); + key_put(tsk->process_keyring); + key_put(tsk->thread_keyring); + +} /* end exit_keys() */ + +/*****************************************************************************/ +/* + * deal with SUID programs and setuid()/setreuid()/setresuid() + */ +int suid_keys(struct task_struct *tsk) +{ + struct key *keyring = xchg(&tsk->session_keyring, NULL); + if (keyring) { + key_put(keyring); + + if (keyring_alloc(NULL, &tsk->session_keyring) < 0) + return -ENOMEM; + return keyring_set_name(tsk->session_keyring, + "_ses.%u", + tsk->session_keyring->serial); + } + + return 0; +} /* end suid_keys() */ + +/*****************************************************************************/ +/* + * deal with execve() + */ +int exec_keys(struct task_struct *tsk) +{ + key_put(xchg(&tsk->process_keyring, NULL)); + key_put(xchg(&tsk->thread_keyring, NULL)); + + if (!tsk->session_keyring) { + if (keyring_alloc(NULL, &tsk->session_keyring) < 0) + return -ENOMEM; + if (keyring_set_name(tsk->session_keyring, + "_ses.%u", tsk->session_keyring->serial + ) < 0) + return -ENOMEM; + } + + if (keyring_alloc(NULL, &tsk->process_keyring) < 0) + return -ENOMEM; + + return keyring_set_name(tsk->process_keyring, "_pid.%d", tsk->tgid); + +} /* end exec_keys() */ + +/*****************************************************************************/ +/* + * implement "/proc/keys" to provides a list of the keys on the system + */ +#ifdef CONFIG_PROC_FS +static int key_proc_open(struct inode *inode, struct file *file) +{ + return seq_open(file, &key_proc_ops); + +} + +static void *key_proc_start(struct seq_file *p, loff_t *_pos) +{ + struct rb_node *_p; + loff_t pos = *_pos; + + spin_lock(&key_serial_lock); + + _p = rb_first(&key_serial_tree); + while (pos > 0 && _p) { + pos--; + _p = rb_next(_p); + } + + return _p; +} + +static void *key_proc_next(struct seq_file *p, void *v, loff_t *_pos) +{ + (*_pos)++; + return rb_next((struct rb_node *) v); +} + +static void key_proc_stop(struct seq_file *p, void *v) +{ + spin_unlock(&key_serial_lock); +} + +static int key_proc_show(struct seq_file *m, void *v) +{ + struct rb_tree *_p = v; + const struct key *key = rb_entry(_p, struct key, serial_node); + + seq_printf(m, "%08x %c%c%c %5d %-9.9s ", + key->serial, + key->flags & KEY_FLAG_PUBLIC_KEYRING ? 'p' : '-', + key->flags & KEY_FLAG_RETIRED ? 'r' : '-', + key->flags & KEY_FLAG_DEAD ? 'd' : '-', + atomic_read(&key->usage), + key->type->name); + + key->type->describe(key, m); + seq_putc(m, '\n'); + + return 0; +} +#endif /* CONFIG_PROC_FS */ diff -uNr linux-2.6.0-test4/kernel/Makefile linux-2.6.0-test4-keys/kernel/Makefile --- linux-2.6.0-test4/kernel/Makefile 2003-08-26 14:04:48.000000000 +0100 +++ linux-2.6.0-test4-keys/kernel/Makefile 2003-08-26 18:09:44.000000000 +0100 @@ -6,7 +6,7 @@ exit.o itimer.o time.o softirq.o resource.o \ sysctl.o capability.o ptrace.o timer.o user.o \ signal.o sys.o kmod.o workqueue.o pid.o \ - rcupdate.o intermodule.o extable.o params.o posix-timers.o + rcupdate.o intermodule.o extable.o params.o posix-timers.o key.o obj-$(CONFIG_FUTEX) += futex.o obj-$(CONFIG_GENERIC_ISA_DMA) += dma.o diff -uNr linux-2.6.0-test4/kernel/user.c linux-2.6.0-test4-keys/kernel/user.c --- linux-2.6.0-test4/kernel/user.c 2003-08-26 13:27:21.000000000 +0100 +++ linux-2.6.0-test4-keys/kernel/user.c 2003-08-27 16:18:26.000000000 +0100 @@ -12,6 +12,7 @@ #include #include #include +#include /* * UID task count cache, to get fast user lookup in "alloc_uid" @@ -30,7 +31,8 @@ struct user_struct root_user = { .__count = ATOMIC_INIT(1), .processes = ATOMIC_INIT(1), - .files = ATOMIC_INIT(0) + .files = ATOMIC_INIT(0), + .keyring = &root_user_keyring, }; /* @@ -73,6 +75,7 @@ { if (up && atomic_dec_and_lock(&up->__count, &uidhash_lock)) { uid_hash_remove(up); + key_put(up->keyring); kmem_cache_free(uid_cachep, up); spin_unlock(&uidhash_lock); } @@ -98,6 +101,17 @@ atomic_set(&new->processes, 0); atomic_set(&new->files, 0); + if (keyring_alloc(NULL, &new->keyring) < 0) { + kmem_cache_free(uid_cachep, up); + return NULL; + } + + if (keyring_set_name(new->keyring, "_uid.%u", uid) < 0) { + key_put(new->keyring); + kmem_cache_free(uid_cachep, up); + return NULL; + } + /* * Before adding this, check whether we raced * on adding the same user already.. @@ -130,6 +144,7 @@ atomic_dec(&old_user->processes); current->user = new_user; free_uid(old_user); + suid_keys(current); } - To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/