Re: [PATCH] authentication / encryption key retention

From: David Howells
Date: Mon Sep 08 2003 - 12:38:44 EST



Hi Ingo,

> some suggestions to improve your patch. First: It is very elegant.

Thanks:-)

> > + kmem_cache_free(uid_cachep, up);
> this should be 'new' instead of 'up'

Fixed.

> Maybe the keyword "keyring" should be put here somehow, since many
> security people know this concept by exactly this name.

Done:

+ Furthermore, a special type of key is available that acts as keyring:
+ a searchable sequence of keys. Each process is equipped with access
+ to five standard keyrings: UID-specific, GID-specific, session,
+ process and thread.

> Maybe this allocator should be separated. It will be useful outside this
> function.

By "this allocator" do you mean the algorithm that generates a unique serial
number?

> You should take the semaphore always to prevent the tiny race, where you
> allocate the first subscription, while you check this.

I don't think there's a race there. We do a spot check, and if there's nothing
there at that time, so be it. True, the subscriber may be inside the
semaphore, preparatory to adding a ring, but they haven't done so yet, so it
isn't much of a problem.

However, I'll make the change anyway... it's probably not going to be called
much, so it shouldn't hurt.

> > + retired:
> > + read_unlock(&keyring->lock);
> > +
> > + if (sp > 0) {
> > + sp--;
> > + keyring = stack[sp].keyring;
> > + keylist = stack[sp].keylist;
> > + kix = stack[sp].kix + 1;
> > + goto ascend;
> > + }
>
> Why do you do the iteration without taking the spinlock here?

What do you mean? I read_lock() every node as I deal with it. All the nodes
stored on the stack are still read_locked().

And do you mean iteration or recursion?

> That whole function looks a little hackish and is difficult to follow. I
> would at least like to see
> for (; kix < keyring->payload.subscriptions->nkeys; kix++) {
> key = keyring->payload.subscriptions->keys[kix];
> }
>
> Now the goto next becomes continue.

Erm... what about the recursion element? The set of keyrings is a graph not a
one dimensional list (some of the keys may actually be keyrings themselves).

> You also never need to store keylist on the stack, because of the identity
> stack[sp].keyring->payload.subscriptions == stack[sp].keylist

Good point. Fixed.

> This should reduce stack usage or allow deeper nesting.

True. But 6 levels is probably enough.

> Loop detection should better be done on insert, not on search.
> With the above code you allow loops, but ignore them, right?

Yes, loop detection is a pain whenever it is done, especially as loops can
cause recursive deadlocks in keyring_search().

I'm not sure what'd be the best way to avoid loop detection. What if the user
sets up three keyrings and then subscribes A to B, B to C and C to A? On the
other hand what if they subscribe A to B, B to C and A to C?

I think the only way to do it would be to add in the new subscription and then
walk the new graph in its entirety, marking keyrings as you go down and then
unmarking them as you rise back up, and giving an error if we try to walk over
a node that's already marked.

The problem again is evading recursive deadlock - particularly against other
algorithms also recursively walking the graph at the same time.

My initial thought was just to say that a user can only subscribe keyrings to
one of the five process keyrings, and those can't be subscribed elsewhere.

> > + vsnprintf(buf, 32, fmt, va);
>
> you should check the return value of vsnprintf here end fail for
> ret >= sizeof(buf)

But that can't happen. The kernel's vsnprintf() returns the number of chars
written into the buffer (exclusive of the NUL char), and so if its return
value is >= sizeof(buf) then your stack is stuffed:

[lib/vsprintf.c]
int vsnprintf(char *buf, size_t size, const char *fmt, va_list args)
{
...
/* the trailing null byte doesn't count towards the total
* ++str;
*/
return str-buf;
}

However, I've changed things to eliminate the strlen() call and use the result
of vsnprintf() instead.

> > + seq_printf(m, "%s", kname->name);
>
> What about seq_puts(kname->name) here?

Done. It used to say something else as well, but no longer...

> > +static int install_session_keyring(struct task_struct *tsk)
> > +{
> ...
> Why is this one implemented differently from the others above?

At one point the function did something else as well, but that has been taken
out. I've normalised the process & thread versions to be the same, and I've
made them release the newly allocated keyring if the naming function fails.

> > + case PR_SPEC_GROUP_KEYRING:
> ...
> Not implemented, yet? ;-)

No. I don't want to start mucking around adding "struct group" to match
"struct user" just yet. I'll consider that after Linus adds this key
management stuff to the kernel.

I'd also like to add "login session" and "process" / "thread group" structs
too.

> > +long new_session_keyring(void)
> ...
> You evaluate current three times here. Evaluating it is expensive. You
> might consider using a variable holding current instead.

It's not that expensive, at least not on the x86 arch. It's probably more
expensive to save an extra value on the stack so as to have the value to
hand. Besides, the get_current() function probably ought to be
__attribute__((const)).

> > + ret = get_user(plen, (uint16_t *) _payload);
>
> Why this cast?

get_user() looks at the pointer type to determine the fetch size. _payload is
void *.

> > +int copy_keys(unsigned long clone_flags, struct task_struct *tsk)
> ...
> What about the per thread keyring?
> Is there an atomic_inc(tsk->thread_keyring) missing?

No. Note the following:

tsk->thread_keyring = NULL;

> So all in all, it looks like a very nice and clean patch.

I've attached a revised patch:-)

> I'm looking forward to have this included.

So am I.

Thanks for your comments,
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-09-08 17:32:59.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, new);
+ 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-09-08 17:36:42.000000000 +0100
@@ -4,6 +4,23 @@

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.
+
+ Furthermore, a special type of key is available that acts as keyring:
+ a searchable sequence of keys. Each process is equipped with access
+ to five standard keyrings: UID-specific, GID-specific, session,
+ process and thread.
+
+ If you are unsure as to whether this is required, 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-09-08 18:04:51.000000000 +0100
@@ -0,0 +1,590 @@
+/* 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(source);
+
+ down_read(&source->sem);
+
+ /* duplicate the list of subscribed keys */
+ if (source->payload.subscriptions) {
+ const unsigned limit =
+ (PAGE_SIZE - sizeof(*klist)) / sizeof(struct key);
+
+ 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;
+ 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].kix = kix;
+ sp++;
+ goto descend;
+ }
+
+ next:
+ kix++;
+ }
+
+ retired:
+ read_unlock(&keyring->lock);
+
+ if (sp > 0) {
+ sp--;
+ keyring = stack[sp].keyring;
+ keylist = keyring->payload.subscriptions;
+ 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);
+ len = vsnprintf(buf, 32, fmt, va);
+ va_end(va);
+
+ 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_puts(m, 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-09-08 18:16:59.000000000 +0100
@@ -0,0 +1,506 @@
+/* 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)
+{
+ struct key *keyring;
+ int ret;
+
+ ret = keyring_alloc(NULL, &keyring);
+ if (ret < 0)
+ return ret;
+
+ ret = keyring_set_name(keyring, "_tid.%d", tsk->pid);
+ if (ret < 0) {
+ key_put(keyring);
+ return ret;
+ }
+
+ key_put(xchg(&tsk->thread_keyring, keyring));
+
+ return 0;
+} /* end install_thread_keyring() */
+
+/*****************************************************************************/
+/*
+ * install a fresh process keyring, discarding the old one
+ */
+static int install_process_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, "_pid.%d", tsk->tgid);
+ if (ret < 0) {
+ key_put(keyring);
+ return ret;
+ }
+
+ key_put(xchg(&tsk->process_keyring, keyring));
+
+ return 0;
+} /* 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) {
+ key_put(keyring);
+ 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)
+{
+ struct task_struct *tsk = current;
+ int ret;
+
+ ret = install_session_keyring(tsk);
+ if (ret < 0)
+ return ret;
+
+ return tsk->session_keyring ? tsk->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-09-05 14:56:36.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/