public inbox for linux-kernel@vger.kernel.org
 help / color / mirror / Atom feed
* [PATCH] authentication / encryption key retention
@ 2003-09-05 14:19 David Howells
  2003-09-08 15:30 ` Ingo Oeser
  0 siblings, 1 reply; 4+ messages in thread
From: David Howells @ 2003-09-05 14:19 UTC (permalink / raw)
  To: torvalds; +Cc: linux-kernel


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

^ permalink raw reply	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2003-09-17 14:38 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2003-09-05 14:19 [PATCH] authentication / encryption key retention David Howells
2003-09-08 15:30 ` Ingo Oeser
2003-09-08 17:34   ` David Howells
2003-09-17 14:37     ` David Howells

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox