From: David Howells <dhowells@redhat.com>
To: Linus Torvalds <torvalds@osdl.org>
Cc: linux-kernel@vger.kernel.org
Subject: [PATCH/RFC] authentication / encryption key retention
Date: Wed, 27 Aug 2003 16:31:19 +0100 [thread overview]
Message-ID: <30551.1061998279@redhat.com> (raw)
In-Reply-To: <Pine.LNX.4.44.0308221044470.20736-100000@home.osdl.org>
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 <linux/mount.h>
#include <linux/security.h>
#include <linux/rmap-locking.h>
+#include <linux/key.h>
#include <asm/uaccess.h>
#include <asm/pgalloc.h>
@@ -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 <linux/types.h>
+#include <linux/rbtree.h>
+#include <linux/spinlock.h>
+#include <asm/atomic.h>
+
+#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 <linux/profile.h>
#include <linux/mount.h>
#include <linux/proc_fs.h>
+#include <linux/key.h>
#include <asm/uaccess.h>
#include <asm/pgtable.h>
@@ -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 <linux/futex.h>
#include <linux/ptrace.h>
#include <linux/mount.h>
+#include <linux/key.h>
#include <asm/pgtable.h>
#include <asm/pgalloc.h>
@@ -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 <linux/module.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/key.h>
+#include <linux/fs.h>
+#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
+#include <asm/errno.h>
+
+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 <linux/sched.h>
#include <linux/slab.h>
#include <linux/bitops.h>
+#include <linux/key.h>
/*
* 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);
}
prev parent reply other threads:[~2003-08-27 15:33 UTC|newest]
Thread overview: 10+ messages / expand[flat|nested] mbox.gz Atom feed top
2003-08-22 15:25 authentication / encryption key retention David Howells
2003-08-22 16:19 ` Linus Torvalds
2003-08-22 17:39 ` David Howells
2003-08-22 18:38 ` Linus Torvalds
2003-08-26 10:12 ` David Howells
2003-08-26 15:30 ` Alan Cox
2003-08-26 16:07 ` David Howells
2003-08-26 16:26 ` Valdis.Kletnieks
2003-08-26 16:26 ` Stephen Smalley
2003-08-27 15:31 ` David Howells [this message]
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=30551.1061998279@redhat.com \
--to=dhowells@redhat.com \
--cc=linux-kernel@vger.kernel.org \
--cc=torvalds@osdl.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.