[PATCH] authentication / encryption key retention

From: David Howells
Date: Fri Sep 05 2003 - 09:23:41 EST



Hi Linus,

Here's a patch to the kernel that can provide authentication token and
encryption key retention at various levels. Could you have a look at it and
see if it's along the lines of what you want?

Note that it uses the prctl() syscall to propose keys to the kernel.

One thing I think it needs is some way of limiting the number of extant keys a
user is permitted to have.

I can now use it to retain AFS keys obtained from Kerberos.

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-09-01 14:24:18.000000000 +0100
@@ -0,0 +1,144 @@
+/* key.h: authentication token and access key management
+ *
+ * Copyright (C) 2003 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@xxxxxxxxxx)
+ *
+ * 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/list.h>
+#include <linux/rbtree.h>
+#include <linux/spinlock.h>
+#include <asm/atomic.h>
+
+#ifdef __KERNEL__
+#ifdef CONFIG_KEYS
+
+#define KEY_DEBUGGING
+
+typedef int32_t key_serial_t;
+
+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 and tickets
+ */
+struct key {
+ atomic_t usage;
+ 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 */
+ unsigned short datalen; /* payload data length */
+ unsigned short 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 */
+ struct list_head link; /* link in types list */
+
+ int (*init)(struct key *key, const char *desc,
+ size_t datalen, const char *data);
+ int (*update)(struct key *key, const char *desc,
+ size_t datalen, const char *data);
+ int (*match)(const struct key *key, const void *desc);
+ void (*clear)(struct key *key);
+ void (*describe)(const struct key *key, struct seq_file *p);
+};
+
+extern int register_key_type(struct key_type *ktype);
+extern void unregister_key_type(struct key_type *ktype);
+
+extern void key_retire(struct key *key);
+extern void key_put(struct key *key);
+
+extern int add_process_key(int specifier,
+ const char *type,
+ const char *description,
+ int plen,
+ const void *payload);
+
+extern int keyring_search(struct key *keyring,
+ const struct key_type *type,
+ const char *description,
+ struct key **_key);
+
+#define SEARCH_KEYRING_THREAD 0x0001
+#define SEARCH_KEYRING_PROCESS 0x0002
+#define SEARCH_KEYRING_SESSION 0x0004
+#define SEARCH_KEYRING_UID 0x0008
+#define SEARCH_KEYRING_GID 0x0010
+#define SEARCH_KEYRING_ALL 0x001f
+
+extern int search_process_keyrings(unsigned search_mask,
+ const struct key_type *type,
+ const char *description,
+ struct key **_key);
+
+extern struct key root_user_keyring;
+extern int alloc_uid_keyring(struct user_struct *user);
+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);
+extern long get_process_keyring_ID(int specifier);
+extern long clear_process_keyring(int specifier);
+extern long new_session_keyring(void);
+extern long add_user_key(int specifier,
+ char __user *type,
+ char __user *description,
+ void __user *payload);
+
+#else /* CONFIG_KEYS */
+
+#define key_put(k) do { } while(0)
+#define alloc_uid_keyring(u) 0
+#define copy_keys(f,t) 0
+#define exit_keys(t) do { } while(0)
+#define suid_keys(t) 0
+#define exec_keys(t) 0
+#define get_process_keyring_ID(s) (-EINVAL)
+#define clear_process_keyring(s) (-EINVAL)
+#define new_session_keyring() (-EINVAL)
+#define add_user_key(s,t,d,p) (-EINVAL)
+
+#endif /* CONFIG_KEYS */
+#endif /* __KERNEL__ */
+#endif /* _LINUX_KEY_H */
diff -uNr linux-2.6.0-test4/include/linux/prctl.h linux-2.6.0-test4-keys/include/linux/prctl.h
--- linux-2.6.0-test4/include/linux/prctl.h 2003-08-26 14:04:47.000000000 +0100
+++ linux-2.6.0-test4-keys/include/linux/prctl.h 2003-08-28 09:55:22.000000000 +0100
@@ -43,5 +43,16 @@
# define PR_TIMING_TIMESTAMP 1 /* Accurate timestamp based
process timing */

+/* Manage a process's keyrings */
+#define PR_SPEC_THREAD_KEYRING 0 /* - specifier for thread-specific keyring */
+#define PR_SPEC_PROCESS_KEYRING 1 /* - specifier for process-specific keyring */
+#define PR_SPEC_SESSION_KEYRING 2 /* - specifier for session-specific keyring */
+#define PR_SPEC_USER_KEYRING 3 /* - specifier for UID-specific keyring */
+#define PR_SPEC_GROUP_KEYRING 4 /* - specifier for GID-specific keyring */
+
+#define PR_GET_KEYRING_ID 15 /* ask for specified keyring's ID */
+#define PR_CLEAR_KEYRING 16 /* clear contents of specified keyring */
+#define PR_NEW_SESSION_KEYRING 17 /* start a new session keyring */
+#define PR_ADD_NEW_KEY 18 /* add a key to specified keyring */

#endif /* _LINUX_PRCTL_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-29 10:42:20.000000000 +0100
@@ -290,6 +290,10 @@
atomic_t processes; /* How many processes does this user have? */
atomic_t files; /* How many open files does this user have? */

+#ifdef CONFIG_KEYS
+ struct key *keyring; /* UID specific keyring */
+#endif
+
/* Hash table maintenance information */
struct list_head uidhash_list;
uid_t uid;
@@ -402,6 +406,11 @@
kernel_cap_t cap_effective, cap_inheritable, cap_permitted;
int keep_capabilities:1;
struct user_struct *user;
+#ifdef CONFIG_KEYS
+ 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 */
+#endif
/* 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/sys.c linux-2.6.0-test4-keys/kernel/sys.c
--- linux-2.6.0-test4/kernel/sys.c 2003-08-26 14:04:48.000000000 +0100
+++ linux-2.6.0-test4-keys/kernel/sys.c 2003-08-28 10:43:08.000000000 +0100
@@ -23,6 +23,7 @@
#include <linux/security.h>
#include <linux/dcookies.h>
#include <linux/suspend.h>
+#include <linux/key.h>

#include <asm/uaccess.h>
#include <asm/io.h>
@@ -1417,6 +1418,21 @@
}
current->keep_capabilities = arg2;
break;
+ case PR_GET_KEYRING_ID:
+ error = get_process_keyring_ID(arg2);
+ break;
+ case PR_CLEAR_KEYRING:
+ error = clear_process_keyring(arg2);
+ break;
+ case PR_NEW_SESSION_KEYRING:
+ error = new_session_keyring();
+ break;
+ case PR_ADD_NEW_KEY:
+ error = add_user_key(arg2,
+ (char __user *) arg3,
+ (char __user *) arg4,
+ (void __user *) arg5);
+ break;
default:
error = -EINVAL;
break;
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-29 11:03:52.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,10 @@
struct user_struct root_user = {
.__count = ATOMIC_INIT(1),
.processes = ATOMIC_INIT(1),
- .files = ATOMIC_INIT(0)
+ .files = ATOMIC_INIT(0),
+#ifdef CONFIG_KEYS
+ .keyring = &root_user_keyring,
+#endif
};

/*
@@ -73,6 +77,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 +103,11 @@
atomic_set(&new->processes, 0);
atomic_set(&new->files, 0);

+ if (alloc_uid_keyring(new) < 0) {
+ kmem_cache_free(uid_cachep, up);
+ return NULL;
+ }
+
/*
* Before adding this, check whether we raced
* on adding the same user already..
@@ -130,6 +140,7 @@
atomic_dec(&old_user->processes);
current->user = new_user;
free_uid(old_user);
+ suid_keys(current);
}


diff -uNr linux-2.6.0-test4/security/Kconfig linux-2.6.0-test4-keys/security/Kconfig
--- linux-2.6.0-test4/security/Kconfig 2003-08-26 13:27:21.000000000 +0100
+++ linux-2.6.0-test4-keys/security/Kconfig 2003-08-29 10:13:32.000000000 +0100
@@ -4,6 +4,18 @@

menu "Security options"

+config KEYS
+ bool "Enable access key retention support"
+ help
+ This option provides support for retaining authentication tokens and
+ access keys in the kernel.
+
+ It also includes provision of methods by which such keys might be
+ associated with a process so that network filesystems, encryption
+ support and the like can find them.
+
+ If you are unsure how to answer this question, answer N.
+
config SECURITY
bool "Enable different security models"
help
diff -uNr linux-2.6.0-test4/security/keys/internal.h linux-2.6.0-test4-keys/security/keys/internal.h
--- linux-2.6.0-test4/security/keys/internal.h 1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.0-test4-keys/security/keys/internal.h 2003-08-29 11:06:37.000000000 +0100
@@ -0,0 +1,75 @@
+/* internal.h: authentication token and access key management internal defs
+ *
+ * Copyright (C) 2003 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@xxxxxxxxxx)
+ *
+ * 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 _INTERNAL_H
+#define _INTERNAL_H
+
+#include <linux/key.h>
+
+/*****************************************************************************/
+/*
+ * 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 struct rb_root key_serial_tree;
+extern spinlock_t key_serial_lock;
+extern struct list_head key_types_list;
+extern struct rw_semaphore key_types_sem;
+
+extern int key_alloc(struct key_type *type, struct key **_key);
+extern int keyring_alloc(struct key *source, struct key **_key);
+
+extern int __keyring_add_key(struct key *keyring, struct key *key);
+
+extern int keyring_set_name(struct key *keyring, const char *fmt, ...)
+__attribute__((format(printf, 2, 3)));
+
+
+#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
+
+#endif /* _INTERNAL_H */
diff -uNr linux-2.6.0-test4/security/keys/key.c linux-2.6.0-test4-keys/security/keys/key.c
--- linux-2.6.0-test4/security/keys/key.c 1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.0-test4-keys/security/keys/key.c 2003-08-29 11:07:39.000000000 +0100
@@ -0,0 +1,595 @@
+/* key.c: authentication token and access key management
+ *
+ * Copyright (C) 2003 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@xxxxxxxxxx)
+ *
+ * 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/seq_file.h>
+#include <asm/errno.h>
+#include "internal.h"
+
+static kmem_cache_t *key_jar;
+static key_serial_t key_serial_next = 2;
+struct rb_root key_serial_tree;
+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",
+ .link = { &key_types_list, &key_types_list },
+ .match = keyring_match,
+ .clear = keyring_clear,
+ .describe = keyring_describe,
+};
+
+struct list_head key_types_list = {
+ .next = &key_type_keyring.link,
+ .prev = &key_type_keyring.link,
+};
+DECLARE_RWSEM(key_types_sem);
+
+struct key root_user_keyring = {
+ .usage = ATOMIC_INIT(1),
+ .serial = 1,
+ .type = &key_type_keyring,
+ .lock = RW_LOCK_UNLOCKED,
+ .sem = __RWSEM_INITIALIZER(root_user_keyring.sem),
+#ifdef KEY_DEBUGGING
+ .magic = KEY_DEBUG_MAGIC,
+#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);
+ init_rwsem(&key->sem);
+ 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 */
+ key->serial = key_serial_next;
+ if (key->serial < 2)
+ key->serial = 2;
+ key_serial_next = key->serial + 1;
+
+ 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 (;;) {
+ key->serial = key_serial_next;
+ if (key->serial < 2)
+ key->serial = 2;
+ key_serial_next = key->serial + 1;
+
+ 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() */
+
+/*****************************************************************************/
+/*
+ * 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);
+
+ down_write(&key->sem);
+ write_lock(&key->lock);
+ key->flags |= KEY_FLAG_RETIRED;
+ write_unlock(&key->lock);
+ up_write(&key->sem);
+} /* 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);
+
+ /* prevent the source keyring from being altered */
+ down_read(&source->sem);
+
+ sklist = source->payload.subscriptions;
+ if (sklist && sklist->nkeys > 0) {
+ max = sklist->nkeys;
+ 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;
+
+ klist->maxkeys = max;
+ 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;
+ }
+
+ up_read(&source->sem);
+ }
+
+ return ret;
+
+ nomem:
+ up_read(&source->sem);
+ 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 char *description,
+ 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, description)) {
+ if (key->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, *nklist;
+ unsigned max;
+ size_t size;
+
+ if (keyring->flags & KEY_FLAG_RETIRED)
+ return -EINVAL;
+
+ klist = keyring->payload.subscriptions;
+ if (klist && klist->nkeys < klist->maxkeys) {
+ atomic_inc(&key->usage);
+
+ write_lock(&keyring->lock);
+ klist->keys[klist->nkeys++] = key;
+ write_unlock(&keyring->lock);
+
+ return 0;
+ }
+
+ /* grow the key list */
+ max = 4;
+ if (klist)
+ max += klist->maxkeys;
+
+ size = sizeof(*klist) + sizeof(*key) * max;
+ if (size > PAGE_SIZE)
+ return -ENFILE;
+
+ nklist = kmalloc(size, GFP_KERNEL);
+ if (!nklist)
+ return -ENOMEM;
+ nklist->maxkeys = max;
+ nklist->nkeys = 0;
+
+ if (klist) {
+ nklist->nkeys = klist->nkeys;
+ memcpy(nklist->keys, klist->keys, sizeof(*key) * klist->nkeys);
+ }
+
+ atomic_inc(&key->usage);
+
+ write_lock(&keyring->lock);
+ keyring->payload.subscriptions = nklist;
+ nklist->keys[nklist->nkeys++] = key;
+ write_unlock(&keyring->lock);
+
+ if (klist)
+ kfree(klist);
+ return 0;
+
+} /* end __keyring_add_key() */
+
+/*****************************************************************************/
+/*
+ * add a key to a keyring
+ */
+int keyring_add_key(struct key *keyring, struct key *key)
+{
+ int ret;
+
+ key_validate(keyring);
+ key_validate(key);
+
+ down_write(&keyring->sem);
+ ret = __keyring_add_key(keyring, key);
+ up_write(&keyring->sem);
+
+ return ret;
+} /* 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);
+
+ if (keyring->type != &key_type_keyring)
+ return -EINVAL;
+
+ 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;
+
+ down_write(&keyring->sem);
+ write_lock(&keyring->lock);
+ keyring->description.ringname = kname;
+ write_unlock(&keyring->lock);
+ up_write(&keyring->sem);
+
+ return 0;
+} /* end keyring_set_name() */
+
+EXPORT_SYMBOL(keyring_set_name);
+
+/*****************************************************************************/
+/*
+ * match keyrings on their name
+ */
+static int keyring_match(const struct key *keyring, const void *description)
+{
+ struct keyring_name *kname;
+
+ kname = keyring->description.ringname;
+ if (kname)
+ if (strcmp(kname->name, description) == 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() */
+
+/*****************************************************************************/
+/*
+ * register a type of key
+ */
+int register_key_type(struct key_type *ktype)
+{
+ struct key_type *p;
+ int ret;
+
+ ret = -EEXIST;
+ down_write(&key_types_sem);
+
+ list_for_each_entry(p, &key_types_list, link) {
+ if (strcmp(p->name, ktype->name) == 0)
+ goto out;
+ }
+
+ list_add(&ktype->link, &key_types_list);
+ ret = 0;
+
+ out:
+ up_write(&key_types_sem);
+ return ret;
+} /* end register_key_type() */
+
+EXPORT_SYMBOL(register_key_type);
+
+/*****************************************************************************/
+/*
+ * unregister a type of key
+ */
+void unregister_key_type(struct key_type *ktype)
+{
+ down_write(&key_types_sem);
+
+ list_del_init(&ktype->link);
+
+ up_write(&key_types_sem);
+} /* end unregister_key_type() */
+
+EXPORT_SYMBOL(unregister_key_type);
+
+/*****************************************************************************/
+/*
+ * initialise the key management stuff
+ */
+static int __init key_init(void)
+{
+ 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");
+
+ 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);
diff -uNr linux-2.6.0-test4/security/keys/Makefile linux-2.6.0-test4-keys/security/keys/Makefile
--- linux-2.6.0-test4/security/keys/Makefile 1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.0-test4-keys/security/keys/Makefile 2003-09-05 14:47:55.000000000 +0100
@@ -0,0 +1,9 @@
+#
+# Makefile for key management
+#
+
+obj-y := \
+ key.o \
+ process_keys.o
+
+obj-$(CONFIG_PROC_FS) += proc.o
diff -uNr linux-2.6.0-test4/security/keys/proc.c linux-2.6.0-test4-keys/security/keys/proc.c
--- linux-2.6.0-test4/security/keys/proc.c 1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.0-test4-keys/security/keys/proc.c 2003-08-29 10:20:14.000000000 +0100
@@ -0,0 +1,119 @@
+/* proc.c: proc files for key database enumeration
+ *
+ * Copyright (C) 2003 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@xxxxxxxxxx)
+ *
+ * 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/fs.h>
+#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
+#include <asm/errno.h>
+#include "internal.h"
+
+static int proc_keys_open(struct inode *inode, struct file *file);
+static void *proc_keys_start(struct seq_file *p, loff_t *_pos);
+static void *proc_keys_next(struct seq_file *p, void *v, loff_t *_pos);
+static void proc_keys_stop(struct seq_file *p, void *v);
+static int proc_keys_show(struct seq_file *m, void *v);
+
+static struct seq_operations proc_keys_ops = {
+ .start = proc_keys_start,
+ .next = proc_keys_next,
+ .stop = proc_keys_stop,
+ .show = proc_keys_show,
+};
+
+static struct file_operations proc_keys_fops = {
+ .open = proc_keys_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = seq_release,
+};
+
+/*****************************************************************************/
+/*
+ * implement "/proc/keys" to provides a list of the keys on the system
+ */
+static int proc_keys_open(struct inode *inode, struct file *file)
+{
+ return seq_open(file, &proc_keys_ops);
+
+}
+
+static void *proc_keys_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 *proc_keys_next(struct seq_file *p, void *v, loff_t *_pos)
+{
+ (*_pos)++;
+ return rb_next((struct rb_node *) v);
+}
+
+static void proc_keys_stop(struct seq_file *p, void *v)
+{
+ spin_unlock(&key_serial_lock);
+}
+
+static int proc_keys_show(struct seq_file *m, void *v)
+{
+ struct rb_tree *_p = v;
+ struct key *key = rb_entry(_p, struct key, serial_node);
+
+ read_lock(&key->lock);
+
+ 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');
+
+ read_unlock(&key->lock);
+
+ return 0;
+}
+
+/*****************************************************************************/
+/*
+ * declare the /proc files
+ */
+static int __init key_proc_init(void)
+{
+ struct proc_dir_entry *p;
+
+ p = create_proc_entry("keys", 0, NULL);
+ if (!p)
+ panic("Cannot create /proc/keys\n");
+
+ p->proc_fops = &proc_keys_fops;
+
+ return 0;
+} /* end key_proc_init() */
+
+__initcall(key_proc_init);
diff -uNr linux-2.6.0-test4/security/keys/process_keys.c linux-2.6.0-test4-keys/security/keys/process_keys.c
--- linux-2.6.0-test4/security/keys/process_keys.c 1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.0-test4-keys/security/keys/process_keys.c 2003-08-29 11:16:53.000000000 +0100
@@ -0,0 +1,491 @@
+/* process_keys.c: management of process keyrings
+ *
+ * Copyright (C) 2003 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@xxxxxxxxxx)
+ *
+ * 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/prctl.h>
+#include <linux/fs.h>
+#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
+#include <asm/errno.h>
+#include <asm/uaccess.h>
+#include "internal.h"
+
+/*****************************************************************************/
+/*
+ * allocate a keyring to associate with a UID
+ */
+int alloc_uid_keyring(struct user_struct *user)
+{
+ struct key *keyring;
+ int ret;
+
+ ret = keyring_alloc(NULL, &keyring);
+ if (ret < 0)
+ return ret;
+
+ ret = keyring_set_name(keyring, "_uid.%u", user->uid);
+ if (ret < 0) {
+ key_put(keyring);
+ return ret;
+ }
+
+ user->keyring = keyring;
+ return 0;
+} /* end alloc_uid_keyring() */
+
+/*****************************************************************************/
+/*
+ * install a fresh thread keyring, discarding the old one
+ */
+static int install_thread_keyring(struct task_struct *tsk)
+{
+ int ret;
+
+ key_put(xchg(&tsk->thread_keyring, NULL));
+
+ ret = keyring_alloc(NULL, &tsk->thread_keyring);
+ if (ret < 0)
+ return ret;
+
+ return keyring_set_name(tsk->thread_keyring, "_tid.%d", tsk->pid);
+
+} /* end install_thread_keyring() */
+
+/*****************************************************************************/
+/*
+ * install a fresh process keyring, discarding the old one
+ */
+static int install_process_keyring(struct task_struct *tsk)
+{
+ int ret;
+
+ key_put(xchg(&tsk->process_keyring, NULL));
+
+ ret = keyring_alloc(NULL, &tsk->process_keyring);
+ if (ret < 0)
+ return ret;
+
+ return keyring_set_name(tsk->process_keyring, "_pid.%d", tsk->tgid);
+
+} /* end install_process_keyring() */
+
+/*****************************************************************************/
+/*
+ * install a fresh session keyring, discarding the old one
+ */
+static int install_session_keyring(struct task_struct *tsk)
+{
+ struct key *keyring;
+ int ret;
+
+ ret = keyring_alloc(NULL, &keyring);
+ if (ret < 0)
+ return ret;
+
+ ret = keyring_set_name(keyring, "_ses.%u", keyring->serial);
+ if (ret < 0)
+ return ret;
+
+ key_put(xchg(&tsk->session_keyring, keyring));
+
+ return 0;
+} /* end install_session_keyring() */
+
+/*****************************************************************************/
+/*
+ * select the keyring specified by the user, making sure it exists
+ */
+static int select_keyring(int specifier, struct key **_keyring)
+{
+ struct task_struct *tsk = current;
+ int ret;
+
+ switch (specifier) {
+ case PR_SPEC_THREAD_KEYRING:
+ if (!tsk->thread_keyring) {
+ ret = install_thread_keyring(tsk);
+ if (ret < 0)
+ return ret;
+ }
+ *_keyring = tsk->thread_keyring;
+ return 0;
+
+ case PR_SPEC_PROCESS_KEYRING:
+ if (!tsk->process_keyring) {
+ ret = install_process_keyring(tsk);
+ if (ret < 0)
+ return ret;
+ }
+ *_keyring = tsk->process_keyring;
+ return 0;
+
+ case PR_SPEC_SESSION_KEYRING:
+ if (!tsk->session_keyring) {
+ ret = install_session_keyring(tsk);
+ if (ret < 0)
+ return ret;
+ }
+ *_keyring = tsk->session_keyring;
+ return 0;
+
+ case PR_SPEC_USER_KEYRING:
+ *_keyring = tsk->user->keyring;
+ return 0;
+
+ case PR_SPEC_GROUP_KEYRING:
+ default:
+ return -EINVAL;
+ }
+} /* end select_keyring() */
+
+/*****************************************************************************/
+/*
+ * get the ID of the specified process keyring
+ * - implements prctl(PR_GET_KEYRING_ID)
+ */
+long get_process_keyring_ID(int specifier)
+{
+ struct key *keyring;
+ int ret;
+
+ ret = select_keyring(specifier, &keyring);
+
+ return ret < 0 ? ret : keyring->serial;
+
+} /* end get_process_keyring_ID() */
+
+/*****************************************************************************/
+/*
+ * clear the specified process keyring
+ * - implements prctl(PR_CLEAR_KEYRING)
+ */
+long clear_process_keyring(int specifier)
+{
+ struct keyring_list *klist;
+ struct task_struct *tsk = current;
+ struct key *keyring;
+ int loop;
+
+ switch (specifier) {
+ case PR_SPEC_THREAD_KEYRING:
+ if (!tsk->thread_keyring)
+ return 0;
+ keyring = tsk->thread_keyring;
+ break;
+
+ case PR_SPEC_PROCESS_KEYRING:
+ if (!tsk->process_keyring)
+ return 0;
+ keyring = tsk->process_keyring;
+ break;
+
+ case PR_SPEC_SESSION_KEYRING:
+ if (!tsk->session_keyring)
+ return 0;
+ keyring = tsk->session_keyring;
+ break;
+
+ case PR_SPEC_USER_KEYRING:
+ keyring = tsk->user->keyring;
+
+ case PR_SPEC_GROUP_KEYRING:
+ default:
+ return -EINVAL;
+ }
+
+ down_write(&keyring->sem);
+ klist = keyring->payload.subscriptions;
+ if (klist) {
+ write_lock(&keyring->lock);
+ keyring->payload.subscriptions = NULL;
+ write_unlock(&keyring->lock);
+ }
+ up_write(&keyring->sem);
+
+ if (klist) {
+ for (loop = klist->nkeys - 1; loop >= 0; loop--)
+ key_put(klist->keys[loop]);
+
+ kfree(klist);
+ }
+
+ return 0;
+} /* end clear_process_keyring() */
+
+/*****************************************************************************/
+/*
+ * install a new session keyring, discarding the old one
+ * - implements prctl(PR_NEW_SESSION_KEYRING)
+ */
+long new_session_keyring(void)
+{
+ int ret;
+
+ ret = install_session_keyring(current);
+ if (ret < 0)
+ return ret;
+
+ return current->session_keyring ? current->session_keyring->serial : 0;
+
+} /* end new_session_keyring() */
+
+/*****************************************************************************/
+/*
+ * add a new key to the specified process keyring
+ */
+int add_process_key(int specifier,
+ const char *type,
+ const char *description,
+ int plen,
+ const void *payload)
+{
+ struct keyring_list *klist;
+ struct key_type *ktype;
+ struct key *keyring, *key;
+ int loop, ret;
+
+ ret = select_keyring(specifier, &keyring);
+ if (ret < 0)
+ return ret;
+
+ down_read(&key_types_sem);
+
+ /* look up the type */
+ ret = -ENOENT;
+ list_for_each_entry(ktype, &key_types_list, link) {
+ if (strcmp(ktype->name, type) == 0)
+ goto found_type;
+ }
+ goto error;
+
+ found_type:
+ ret = -EISDIR;
+ if (!ktype->init)
+ goto error;
+
+ /* search for an existing key of the same type and description */
+ down_write(&keyring->sem);
+
+ klist = keyring->payload.subscriptions;
+ if (klist) {
+ for (loop = 0; loop < klist->nkeys; loop++) {
+ key = klist->keys[loop];
+ if (key->type == ktype &&
+ key->type->match(key, description) &&
+ !key->flags & KEY_FLAG_RETIRED)
+ goto update;
+ }
+ }
+
+ /* generate a new key and initialise it */
+ ret = key_alloc(ktype, &key);
+ if (ret < 0)
+ goto error2;
+
+ ret = ktype->init(key, description, plen, payload);
+ if (ret < 0)
+ goto error3;
+
+ ret = __keyring_add_key(keyring, key);
+
+ error3:
+ key_put(key);
+ error2:
+ up_write(&keyring->sem);
+ error:
+ up_read(&key_types_sem);
+ return ret;
+
+ /* update an existing key */
+ update:
+ ret = -EEXIST;
+ if (ktype->update)
+ ret = ktype->update(key, description, plen, payload);
+ goto error2;
+
+} /* end add_process_key() */
+
+EXPORT_SYMBOL(add_process_key);
+
+/*****************************************************************************/
+/*
+ * extract the description of a new key from userspace and add it as a key to
+ * one of the process's keyrings
+ * - implements prctl(PR_ADD_NEW_KEY)
+ */
+long add_user_key(int specifier,
+ char __user *_type,
+ char __user *_description,
+ void __user *_payload)
+{
+ char type[32], *description;
+ void *payload;
+ long dlen;
+ int ret, plen;
+
+ ret = strncpy_from_user(type, _type, sizeof(type) - 1);
+ if (ret < 0)
+ return ret;
+ type[31] = '\0';
+
+ dlen = strnlen_user(_description, PAGE_SIZE - 1);
+ if (dlen <= 0)
+ return -EFAULT;
+ if (dlen > PAGE_SIZE - 1)
+ return -EINVAL;
+
+ description = kmalloc(dlen, GFP_KERNEL);
+ if (!description)
+ return -ENOMEM;
+ ret = -EFAULT;
+ if (copy_from_user(description, _description, dlen) != 0)
+ goto error;
+
+ ret = get_user(plen, (uint16_t *) _payload);
+ if (ret < 0)
+ goto error;
+ _payload += 2;
+
+ ret = -EINVAL;
+ if (plen < 0 || plen > PAGE_SIZE)
+ goto error;
+
+ ret = -ENOMEM;
+ payload = (void *) get_zeroed_page(GFP_KERNEL);
+ if (!payload)
+ goto error;
+
+ ret = -EFAULT;
+ if (copy_from_user(payload, _payload, plen) != 0)
+ goto error2;
+
+ ret = add_process_key(specifier, type, description, plen, payload);
+
+ error2:
+ free_page((unsigned long) payload);
+ error:
+ kfree(description);
+ return ret;
+} /* end add_user_key() */
+
+/*****************************************************************************/
+/*
+ * 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 {
+ tsk->process_keyring = NULL;
+ ret = install_process_keyring(tsk);
+ }
+ }
+
+ 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)
+{
+ return tsk->session_keyring ? install_session_keyring(tsk) : 0;
+
+} /* end suid_keys() */
+
+/*****************************************************************************/
+/*
+ * deal with execve()
+ */
+int exec_keys(struct task_struct *tsk)
+{
+ key_put(xchg(&tsk->thread_keyring, NULL));
+
+ if (!tsk->session_keyring)
+ if (install_session_keyring(tsk) < 0)
+ return -ENOMEM;
+
+ return install_process_keyring(tsk);
+
+} /* end exec_keys() */
+
+/*****************************************************************************/
+/*
+ * search selected process keyrings for the first matching key
+ */
+int search_process_keyrings(unsigned search_mask,
+ const struct key_type *type,
+ const char *description,
+ struct key **_key)
+{
+ struct task_struct *tsk = current;
+ int ret;
+
+ if (search_mask & SEARCH_KEYRING_THREAD && !tsk->thread_keyring) {
+ ret = keyring_search(tsk->thread_keyring,
+ type, description, _key);
+ if (ret == 0)
+ return ret;
+ }
+
+ if (search_mask & SEARCH_KEYRING_PROCESS && !tsk->process_keyring) {
+ ret = keyring_search(tsk->process_keyring,
+ type, description, _key);
+ if (ret == 0)
+ return ret;
+ }
+
+ if (search_mask & SEARCH_KEYRING_SESSION && !tsk->session_keyring) {
+ ret = keyring_search(tsk->session_keyring,
+ type, description, _key);
+ if (ret == 0)
+ return ret;
+ }
+
+ if (search_mask & SEARCH_KEYRING_UID && !tsk->user->keyring) {
+ ret = keyring_search(tsk->user->keyring,
+ type, description, _key);
+ if (ret == 0)
+ return ret;
+ }
+
+ return -ENOENT;
+} /* end search_process_keyrings() */
diff -uNr linux-2.6.0-test4/security/Makefile linux-2.6.0-test4-keys/security/Makefile
--- linux-2.6.0-test4/security/Makefile 2003-08-26 13:27:21.000000000 +0100
+++ linux-2.6.0-test4-keys/security/Makefile 2003-08-29 10:31:49.000000000 +0100
@@ -2,6 +2,7 @@
# Makefile for the kernel security code
#

+obj-$(CONFIG_KEYS) += keys/
subdir-$(CONFIG_SECURITY_SELINUX) += selinux

# if we don't select a security model, use the default capabilities
-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/