lkml.org 
[lkml]   [2004]   [Jun]   [15]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
Patch in this message
/
From
SubjectRe: In-kernel Authentication Tokens (PAGs)
Date

> > Surely the only logical reason to tag a process with extra security
> > information /in the kernel/ is because that information is going to be
> > used /by the kernel/. I can't think of a good reason to put a
> > generalised keystore in the kernel.
>
> Here are three good reasons.
>
> - You want the key lifetime to be the same as your process lifetime
> - You want the key to be readable ONLY by that one process.
> - The kernel wants to supports multiple security realms and mechanisms.
> Not everybody is happy with just kerberosV credentials, and we already
> have beta code for the SPKM mechanism in RPCSEC_GSS.
>
>
> As for the AFS PAG idea: it's already been shot down. See the
> linux-fsdevel thread I referred to earlier.

You might want to look at this patch. It's what I've come up with to support
kafs, but it's general, and should work for anything. It's been built along
Linus's guidelines, and has Linus's approval, contingent on something actually
using it fully.

You can use the session keyring number as a PAG ID if you wish.

I've a sample aklog program (key submission) should you be interested.

David

Signed-Off-By: David Howells <dhowells@redhat.com>

diff -uNr linux-2.6.6/fs/exec.c linux-2.6.6-keys/fs/exec.c
--- linux-2.6.6/fs/exec.c 2004-05-11 11:28:55.000000000 +0100
+++ linux-2.6.6-keys/fs/exec.c 2004-05-11 11:35:49.000000000 +0100
@@ -46,6 +46,7 @@
#include <linux/security.h>
#include <linux/syscalls.h>
#include <linux/rmap.h>
+#include <linux/key.h>

#include <asm/uaccess.h>
#include <asm/pgalloc.h>
@@ -848,8 +849,10 @@
flush_thread();

if (bprm->e_uid != current->euid || bprm->e_gid != current->egid ||
- permission(bprm->file->f_dentry->d_inode,MAY_READ, NULL))
+ permission(bprm->file->f_dentry->d_inode,MAY_READ, NULL)) {
+ suid_keys(current);
current->mm->dumpable = 0;
+ }

/* An exec changes our domain. We are no longer part of the thread
group */
@@ -939,6 +942,11 @@
void compute_creds(struct linux_binprm *bprm)
{
int unsafe;
+
+ if (bprm->e_uid != current->uid)
+ suid_keys(current);
+ exec_keys(current);
+
task_lock(current);
unsafe = unsafe_exec(current);
security_bprm_apply_creds(bprm, unsafe);
diff -uNr linux-2.6.6/include/linux/key.h linux-2.6.6-keys/include/linux/key.h
--- linux-2.6.6/include/linux/key.h 1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.6-keys/include/linux/key.h 2004-05-11 11:32:06.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.6/include/linux/prctl.h linux-2.6.6-keys/include/linux/prctl.h
--- linux-2.6.6/include/linux/prctl.h 2004-05-11 11:26:37.000000000 +0100
+++ linux-2.6.6-keys/include/linux/prctl.h 2004-05-11 11:32:06.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.6/include/linux/sched.h linux-2.6.6-keys/include/linux/sched.h
--- linux-2.6.6/include/linux/sched.h 2004-05-11 11:28:57.000000000 +0100
+++ linux-2.6.6-keys/include/linux/sched.h 2004-05-11 11:32:06.000000000 +0100
@@ -312,6 +312,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;
@@ -449,6 +453,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.6/kernel/exit.c linux-2.6.6-keys/kernel/exit.c
--- linux-2.6.6/kernel/exit.c 2004-05-11 11:28:57.000000000 +0100
+++ linux-2.6.6-keys/kernel/exit.c 2004-05-11 11:32:06.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>
@@ -790,6 +791,7 @@
__exit_fs(tsk);
exit_namespace(tsk);
exit_thread();
+ exit_keys(tsk);

if (tsk->signal->leader)
disassociate_ctty(1);
diff -uNr linux-2.6.6/kernel/fork.c linux-2.6.6-keys/kernel/fork.c
--- linux-2.6.6/kernel/fork.c 2004-05-11 11:28:57.000000000 +0100
+++ linux-2.6.6-keys/kernel/fork.c 2004-05-11 13:36:27.000000000 +0100
@@ -33,6 +33,7 @@
#include <linux/ptrace.h>
#include <linux/mount.h>
#include <linux/audit.h>
+#include <linux/key.h>

#include <asm/pgtable.h>
#include <asm/pgalloc.h>
@@ -972,8 +973,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;
@@ -1084,6 +1087,8 @@

bad_fork_cleanup_namespace:
exit_namespace(p);
+bad_fork_cleanup_keys:
+ exit_keys(p);
bad_fork_cleanup_mm:
exit_mm(p);
if (p->active_mm)
diff -uNr linux-2.6.6/kernel/sys.c linux-2.6.6-keys/kernel/sys.c
--- linux-2.6.6/kernel/sys.c 2004-05-11 11:28:57.000000000 +0100
+++ linux-2.6.6-keys/kernel/sys.c 2004-05-11 11:32:06.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>
@@ -1639,6 +1640,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.6/kernel/user.c linux-2.6.6-keys/kernel/user.c
--- linux-2.6.6/kernel/user.c 2004-05-11 11:27:00.000000000 +0100
+++ linux-2.6.6-keys/kernel/user.c 2004-05-11 11:32:06.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.6/security/Kconfig linux-2.6.6-keys/security/Kconfig
--- linux-2.6.6/security/Kconfig 2004-05-11 11:27:01.000000000 +0100
+++ linux-2.6.6-keys/security/Kconfig 2004-05-11 11:32:06.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.6/security/keys/internal.h linux-2.6.6-keys/security/keys/internal.h
--- linux-2.6.6/security/keys/internal.h 1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.6-keys/security/keys/internal.h 2004-05-11 11:32:06.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 struct semaphore key_serial_sem;
+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.6/security/keys/key.c linux-2.6.6-keys/security/keys/key.c
--- linux-2.6.6/security/keys/key.c 1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.6-keys/security/keys/key.c 2004-05-11 11:32:06.000000000 +0100
@@ -0,0 +1,714 @@
+/* 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;
+DECLARE_MUTEX(key_serial_sem);
+
+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_dead;
+
+static struct key_type key_type_keyring = {
+ .name = "keyring",
+ .link = { &key_type_dead.link, &key_types_list },
+ .match = keyring_match,
+ .clear = keyring_clear,
+ .describe = keyring_describe,
+};
+
+static struct key_type key_type_dead = {
+ .name = "dead",
+ .link = { &key_types_list, &key_type_keyring.link },
+ .match = NULL,
+ .clear = NULL,
+ .describe = NULL,
+};
+
+struct list_head key_types_list = {
+ .next = &key_type_keyring.link,
+ .prev = &key_type_dead.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
+
+ down(&key_serial_sem);
+
+ /* 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);
+ up(&key_serial_sem);
+
+ *_key = key;
+ return 0;
+} /* end key_alloc() */
+
+/*****************************************************************************/
+/*
+ * dispose of a key
+ */
+void key_put(struct key *key)
+{
+ if (!key)
+ return;
+
+ key_validate(key);
+ down(&key_serial_sem);
+ if (atomic_dec_and_test(&key->usage)) {
+ key->flags |= KEY_FLAG_DEAD;
+ rb_erase(&key->serial_node, &key_serial_tree);
+
+ if (key->type->clear)
+ key->type->clear(key);
+#ifdef KEY_DEBUGGING
+ key->magic = KEY_DEBUG_MAGIC_X;
+#endif
+ kmem_cache_free(key_jar, key);
+ }
+
+ up(&key_serial_sem);
+
+} /* 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 ||
+ keyring->type != &key_type_dead)
+ 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);
+
+/*****************************************************************************/
+/*
+ * see if a cycle will will be created by inserting acyclic tree B in acyclic
+ * tree A at the topmost level (ie: as a direct child of A)
+ * - since we are adding B to A at the top level, checking for cycles should
+ * just be a matter of seeing if node A is somewhere in tree B
+ */
+static int keyring_detect_cycle(struct key *A, struct key *B)
+{
+ struct {
+ struct key *subtree;
+ int kix;
+ } stack[KEYRING_SEARCH_MAX_DEPTH];
+
+ struct keyring_list *keylist;
+ struct key *subtree, *key;
+ int sp, kix, ret;
+
+ if (A == B)
+ return -EDEADLK;
+
+ subtree = B;
+ sp = 0;
+
+ descend:
+ read_lock(&subtree->lock);
+ if (subtree->flags & KEY_FLAG_RETIRED)
+ goto retired;
+
+ keylist = subtree->payload.subscriptions;
+ kix = 0;
+
+ ascend:
+ for (; kix < keylist->nkeys; kix++) {
+ key = keylist->keys[kix];
+
+ if (key == A)
+ goto cycle_detected;
+
+ if (key->type == &key_type_keyring) {
+ if (sp >= KEYRING_SEARCH_MAX_DEPTH)
+ goto too_deep;
+
+ stack[sp].subtree = subtree;
+ stack[sp].kix = kix;
+ sp++;
+ goto descend;
+ }
+ }
+
+ retired:
+ read_unlock(&subtree->lock);
+
+ if (sp > 0) {
+ sp--;
+ subtree = stack[sp].subtree;
+ keylist = subtree->payload.subscriptions;
+ kix = stack[sp].kix + 1;
+ goto ascend;
+ }
+
+ return 0;
+
+ too_deep:
+ ret = -ELOOP;
+ goto error;
+ cycle_detected:
+ ret = -EDEADLK;
+ error:
+ read_unlock(&subtree->lock);
+
+ for (; sp > 0; sp--)
+ read_unlock(&stack[sp].subtree->lock);
+ return ret;
+} /* end keyring_detect_cycle() */
+
+/*****************************************************************************/
+/*
+ * 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;
+ int ret;
+
+ if (keyring->flags & KEY_FLAG_RETIRED)
+ return -EINVAL;
+
+ /* check that we aren't going to create a cycle adding one keyring to
+ * another */
+ if (key->type == &key_type_keyring) {
+ ret = keyring_detect_cycle(keyring, key);
+ if (ret < 0)
+ return ret;
+ }
+
+ /* add directly if sufficient slack space */
+ 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)
+{
+ struct rb_node *_n;
+ struct key *key;
+
+ down_write(&key_types_sem);
+
+ list_del_init(&ktype->link);
+
+ /* need to withdraw all keys of this type */
+ down(&key_serial_sem);
+
+ for (_n = rb_first(&key_serial_tree); _n; _n = rb_next(_n)) {
+ key = rb_entry(_n, struct key, serial_node);
+
+ if (key->type != ktype)
+ continue;
+
+ write_lock(&key->lock);
+ key->type = &key_type_dead;
+ write_unlock(&key->lock);
+
+ /* nobody is going to look at description or payload anymore */
+ ktype->clear(key);
+ memset(&key->description, 0xbd, sizeof(key->description));
+ memset(&key->payload, 0xbd, sizeof(key->payload));
+ }
+
+ up(&key_serial_sem);
+ 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.6/security/keys/Makefile linux-2.6.6-keys/security/keys/Makefile
--- linux-2.6.6/security/keys/Makefile 1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.6-keys/security/keys/Makefile 2004-05-11 11:32:06.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.6/security/keys/proc.c linux-2.6.6-keys/security/keys/proc.c
--- linux-2.6.6/security/keys/proc.c 1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.6-keys/security/keys/proc.c 2004-05-11 11:32:06.000000000 +0100
@@ -0,0 +1,120 @@
+/* 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;
+
+ down(&key_serial_sem);
+
+ _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)
+{
+ up(&key_serial_sem);
+}
+
+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);
+
+ if (key->type->describe)
+ 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.6/security/keys/process_keys.c linux-2.6.6-keys/security/keys/process_keys.c
--- linux-2.6.6/security/keys/process_keys.c 1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.6-keys/security/keys/process_keys.c 2004-05-11 11:32:06.000000000 +0100
@@ -0,0 +1,510 @@
+/* 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)
+{
+ 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 = -EINVAL;
+ if (!ktype->match)
+ goto error;
+
+ 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.6/security/Makefile linux-2.6.6-keys/security/Makefile
--- linux-2.6.6/security/Makefile 2004-05-11 11:27:01.000000000 +0100
+++ linux-2.6.6-keys/security/Makefile 2004-05-11 11:32:06.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
\
 
 \ /
  Last update: 2005-03-22 14:03    [W:0.137 / U:0.624 seconds]
©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site