lkml.org 
[lkml]   [2003]   [Aug]   [27]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
Patch in this message
/
From
Subject[PATCH/RFC] authentication / encryption key retention
Date

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(&current->fs->count) > 1
|| atomic_read(&current->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);
}


-
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/

\
 
 \ /
  Last update: 2005-03-22 13:48    [W:0.102 / U:1.544 seconds]
©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site