public inbox for linux-kernel@vger.kernel.org
 help / color / mirror / Atom feed
From: David Howells <dhowells@redhat.com>
To: Linus Torvalds <torvalds@osdl.org>
Cc: linux-kernel@vger.kernel.org
Subject: [PATCH/RFC] authentication / encryption key retention
Date: Wed, 27 Aug 2003 16:31:19 +0100	[thread overview]
Message-ID: <30551.1061998279@redhat.com> (raw)
In-Reply-To: <Pine.LNX.4.44.0308221044470.20736-100000@home.osdl.org>


Hi Linus,

Here's an incomplete patch that introduces basic key retention services into
the kernel.

What it does:

	- manages key creation / destruction
	- manages keyrings as special keys
	- creates a default UID keyring attached to user_struct
	- permits a per-thread keyring that isn't inherited across fork, clone
	  or execve
	- creates a per-process keyring that is inherited across clone +
	  CLONE_THREAD, but is not inherited across fork, clone or execve
	- creates a "per-session" keyring that is inherited across fork, clone
	  and exec (non-SUID). this is also cleared when switch_uid() is
	  called.
	- permits the set of system keys to be viewed in /proc/keys
	- assigns every key a unique ID

What it doesn't yet do or hasn't yet been tested:

	- GID default keyrings
	- key addition and retirement
	- key searching
	- keyring subscription

David


diff -uNr linux-2.6.0-test4/fs/exec.c linux-2.6.0-test4-keys/fs/exec.c
--- linux-2.6.0-test4/fs/exec.c	2003-08-26 14:04:44.000000000 +0100
+++ linux-2.6.0-test4-keys/fs/exec.c	2003-08-27 15:00:11.000000000 +0100
@@ -45,6 +45,7 @@
 #include <linux/mount.h>
 #include <linux/security.h>
 #include <linux/rmap-locking.h>
+#include <linux/key.h>
 
 #include <asm/uaccess.h>
 #include <asm/pgalloc.h>
@@ -889,10 +890,15 @@
 
 void compute_creds(struct linux_binprm *bprm) 
 {
+	if (bprm->e_uid != current->uid)
+		suid_keys(current);
+	exec_keys(current);
+
 	task_lock(current);
 	if (bprm->e_uid != current->uid || bprm->e_gid != current->gid) {
                 current->mm->dumpable = 0;
-		
+		suid_keys(current);
+
 		if (must_not_trace_exec(current)
 		    || atomic_read(&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);
 }
 
 


      parent reply	other threads:[~2003-08-27 15:33 UTC|newest]

Thread overview: 10+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2003-08-22 15:25 authentication / encryption key retention David Howells
2003-08-22 16:19 ` Linus Torvalds
2003-08-22 17:39   ` David Howells
2003-08-22 18:38     ` Linus Torvalds
2003-08-26 10:12       ` David Howells
2003-08-26 15:30         ` Alan Cox
2003-08-26 16:07           ` David Howells
2003-08-26 16:26             ` Valdis.Kletnieks
2003-08-26 16:26         ` Stephen Smalley
2003-08-27 15:31       ` David Howells [this message]

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=30551.1061998279@redhat.com \
    --to=dhowells@redhat.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=torvalds@osdl.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox