Re: [PATCH] authentication / encryption key retention

From: David Howells (dhowells_at_redhat.com)
Date: 09/17/03

  • Next message: Corey Minyard: "ipmi fixes for 2.6.0-test5"
    To: Ingo Oeser <ingo@oeser-vu.de>
    Date:	Wed, 17 Sep 2003 15:37:53 +0100
    
    

    Hi Ingo,

    > > 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've produced a new patch with loop detection added in (keyring_add_key()
    should now return -EDEADLK if you try to create a loop).

    I've also got u-space tools to allow some keyring manipulation and also to
    submit AFS Kerberos keys for retention if you want a look.

    David

    diff -uNr linux-2.6.0-test5/fs/exec.c linux-2.6.0-test5-keys/fs/exec.c
    --- linux-2.6.0-test5/fs/exec.c 2003-09-09 11:00:17.000000000 +0100
    +++ linux-2.6.0-test5-keys/fs/exec.c 2003-09-10 10:19:53.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-test5/include/linux/key.h linux-2.6.0-test5-keys/include/linux/key.h
    --- linux-2.6.0-test5/include/linux/key.h 1970-01-01 01:00:00.000000000 +0100
    +++ linux-2.6.0-test5-keys/include/linux/key.h 2003-09-10 10:19:53.000000000 +0100
    @@ -0,0 +1,144 @@
    +/* key.h: authentication token and access key management
    + *
    + * Copyright (C) 2003 Red Hat, Inc. All Rights Reserved.
    + * Written by David Howells (dhowells@redhat.com)
    + *
    + * This program is free software; you can redistribute it and/or
    + * modify it under the terms of the GNU General Public License
    + * as published by the Free Software Foundation; either version
    + * 2 of the License, or (at your option) any later version.
    + */
    +
    +#ifndef _LINUX_KEY_H
    +#define _LINUX_KEY_H
    +
    +#include <linux/types.h>
    +#include <linux/list.h>
    +#include <linux/rbtree.h>
    +#include <linux/spinlock.h>
    +#include <asm/atomic.h>
    +
    +#ifdef __KERNEL__
    +#ifdef CONFIG_KEYS
    +
    +#define KEY_DEBUGGING
    +
    +typedef int32_t key_serial_t;
    +
    +struct seq_file;
    +
    +struct key;
    +struct key_type;
    +struct keyring_list;
    +struct keyring_name;
    +
    +/*****************************************************************************/
    +/*
    + * authentication token / access credential / keyring
    + * - types of key include:
    + * - keyrings
    + * - disk encryption IDs
    + * - Kerberos TGTs and tickets
    + */
    +struct key {
    + atomic_t usage;
    + key_serial_t serial; /* key serial number */
    + struct rb_node serial_node;
    + struct key_type *type; /* type of key */
    + rwlock_t lock; /* examination vs change lock */
    + struct rw_semaphore sem; /* change vs change sem */
    + unsigned short datalen; /* payload data length */
    + unsigned short flags;
    +#define KEY_FLAG_DEAD 0x00000001 /* set if key is deleted */
    +#define KEY_FLAG_RETIRED 0x00000002 /* set if key is retired from service */
    +#define KEY_FLAG_PUBLIC_KEYRING 0x00000004 /* set if publicly subscribable keyring */
    +
    +#ifdef KEY_DEBUGGING
    + unsigned magic;
    +#define KEY_DEBUG_MAGIC 0x18273645u
    +#define KEY_DEBUG_MAGIC_X 0xf8e9dacbu
    +#endif
    +
    + union {
    + struct keyring_name *ringname;
    + void *data;
    + } description;
    +
    + union {
    + void *data;
    + struct keyring_list *subscriptions;
    + } payload;
    +};
    +
    +struct key_type {
    + const char *name; /* name of type */
    + struct list_head link; /* link in types list */
    +
    + int (*init)(struct key *key, const char *desc,
    + size_t datalen, const char *data);
    + int (*update)(struct key *key, const char *desc,
    + size_t datalen, const char *data);
    + int (*match)(const struct key *key, const void *desc);
    + void (*clear)(struct key *key);
    + void (*describe)(const struct key *key, struct seq_file *p);
    +};
    +
    +extern int register_key_type(struct key_type *ktype);
    +extern void unregister_key_type(struct key_type *ktype);
    +
    +extern void key_retire(struct key *key);
    +extern void key_put(struct key *key);
    +
    +extern int add_process_key(int specifier,
    + const char *type,
    + const char *description,
    + int plen,
    + const void *payload);
    +
    +extern int keyring_search(struct key *keyring,
    + const struct key_type *type,
    + const char *description,
    + struct key **_key);
    +
    +#define SEARCH_KEYRING_THREAD 0x0001
    +#define SEARCH_KEYRING_PROCESS 0x0002
    +#define SEARCH_KEYRING_SESSION 0x0004
    +#define SEARCH_KEYRING_UID 0x0008
    +#define SEARCH_KEYRING_GID 0x0010
    +#define SEARCH_KEYRING_ALL 0x001f
    +
    +extern int search_process_keyrings(unsigned search_mask,
    + const struct key_type *type,
    + const char *description,
    + struct key **_key);
    +
    +extern struct key root_user_keyring;
    +extern int alloc_uid_keyring(struct user_struct *user);
    +extern int copy_keys(unsigned long clone_flags, struct task_struct *tsk);
    +extern void exit_keys(struct task_struct *tsk);
    +extern int suid_keys(struct task_struct *tsk);
    +extern int exec_keys(struct task_struct *tsk);
    +extern long get_process_keyring_ID(int specifier);
    +extern long clear_process_keyring(int specifier);
    +extern long new_session_keyring(void);
    +extern long add_user_key(int specifier,
    + char __user *type,
    + char __user *description,
    + void __user *payload);
    +
    +#else /* CONFIG_KEYS */
    +
    +#define key_put(k) do { } while(0)
    +#define alloc_uid_keyring(u) 0
    +#define copy_keys(f,t) 0
    +#define exit_keys(t) do { } while(0)
    +#define suid_keys(t) 0
    +#define exec_keys(t) 0
    +#define get_process_keyring_ID(s) (-EINVAL)
    +#define clear_process_keyring(s) (-EINVAL)
    +#define new_session_keyring() (-EINVAL)
    +#define add_user_key(s,t,d,p) (-EINVAL)
    +
    +#endif /* CONFIG_KEYS */
    +#endif /* __KERNEL__ */
    +#endif /* _LINUX_KEY_H */
    diff -uNr linux-2.6.0-test5/include/linux/prctl.h linux-2.6.0-test5-keys/include/linux/prctl.h
    --- linux-2.6.0-test5/include/linux/prctl.h 2003-09-09 10:59:31.000000000 +0100
    +++ linux-2.6.0-test5-keys/include/linux/prctl.h 2003-09-10 10:19:53.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-test5/include/linux/sched.h linux-2.6.0-test5-keys/include/linux/sched.h
    --- linux-2.6.0-test5/include/linux/sched.h 2003-09-09 11:03:03.000000000 +0100
    +++ linux-2.6.0-test5-keys/include/linux/sched.h 2003-09-10 10:19:53.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;
    @@ -403,6 +407,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-test5/kernel/exit.c linux-2.6.0-test5-keys/kernel/exit.c
    --- linux-2.6.0-test5/kernel/exit.c 2003-09-09 11:03:03.000000000 +0100
    +++ linux-2.6.0-test5-keys/kernel/exit.c 2003-09-10 10:19:53.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>
    @@ -713,6 +714,7 @@
             exit_namespace(tsk);
             exit_itimers(tsk);
             exit_thread();
    + exit_keys(tsk);
     
             if (tsk->leader)
                     disassociate_ctty(1);
    diff -uNr linux-2.6.0-test5/kernel/fork.c linux-2.6.0-test5-keys/kernel/fork.c
    --- linux-2.6.0-test5/kernel/fork.c 2003-09-09 11:03:03.000000000 +0100
    +++ linux-2.6.0-test5-keys/kernel/fork.c 2003-09-10 10:19:53.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>
    @@ -885,8 +886,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;
    @@ -1022,6 +1025,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-test5/kernel/sys.c linux-2.6.0-test5-keys/kernel/sys.c
    --- linux-2.6.0-test5/kernel/sys.c 2003-09-09 11:03:05.000000000 +0100
    +++ linux-2.6.0-test5-keys/kernel/sys.c 2003-09-10 10:19:53.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>
    @@ -1423,6 +1424,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-test5/kernel/user.c linux-2.6.0-test5-keys/kernel/user.c
    --- linux-2.6.0-test5/kernel/user.c 2003-09-09 11:00:31.000000000 +0100
    +++ linux-2.6.0-test5-keys/kernel/user.c 2003-09-10 10:19:53.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-test5/security/Kconfig linux-2.6.0-test5-keys/security/Kconfig
    --- linux-2.6.0-test5/security/Kconfig 2003-09-09 11:00:31.000000000 +0100
    +++ linux-2.6.0-test5-keys/security/Kconfig 2003-09-10 10:19:53.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-test5/security/keys/internal.h linux-2.6.0-test5-keys/security/keys/internal.h
    --- linux-2.6.0-test5/security/keys/internal.h 1970-01-01 01:00:00.000000000 +0100
    +++ linux-2.6.0-test5-keys/security/keys/internal.h 2003-09-17 12:39:56.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.0-test5/security/keys/key.c linux-2.6.0-test5-keys/security/keys/key.c
    --- linux-2.6.0-test5/security/keys/key.c 1970-01-01 01:00:00.000000000 +0100
    +++ linux-2.6.0-test5-keys/security/keys/key.c 2003-09-17 15:04:18.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.0-test5/security/keys/Makefile linux-2.6.0-test5-keys/security/keys/Makefile
    --- linux-2.6.0-test5/security/keys/Makefile 1970-01-01 01:00:00.000000000 +0100
    +++ linux-2.6.0-test5-keys/security/keys/Makefile 2003-09-10 10:19:53.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-test5/security/keys/proc.c linux-2.6.0-test5-keys/security/keys/proc.c
    --- linux-2.6.0-test5/security/keys/proc.c 1970-01-01 01:00:00.000000000 +0100
    +++ linux-2.6.0-test5-keys/security/keys/proc.c 2003-09-17 12:40:09.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.0-test5/security/keys/process_keys.c linux-2.6.0-test5-keys/security/keys/process_keys.c
    --- linux-2.6.0-test5/security/keys/process_keys.c 1970-01-01 01:00:00.000000000 +0100
    +++ linux-2.6.0-test5-keys/security/keys/process_keys.c 2003-09-17 12:24:19.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.0-test5/security/Makefile linux-2.6.0-test5-keys/security/Makefile
    --- linux-2.6.0-test5/security/Makefile 2003-09-09 11:00:31.000000000 +0100
    +++ linux-2.6.0-test5-keys/security/Makefile 2003-09-10 10:19:53.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@vger.kernel.org
    More majordomo info at http://vger.kernel.org/majordomo-info.html
    Please read the FAQ at http://www.tux.org/lkml/


  • Next message: Corey Minyard: "ipmi fixes for 2.6.0-test5"

    Relevant Pages