[OpenAFS-devel] linux kernel token/key repository patch for 2.6

Troy Benjegerdes hozer@hozed.org
Thu, 4 Mar 2004 11:23:14 -0600


This is a MIME-formatted message.  If you see this text it means that your
E-mail software does not support MIME-formatted messages.

--=_kalmia.hozed.org-19977-1078420994-0001-2
Content-Type: text/plain; charset=us-ascii
Content-Transfer-Encoding: 7bit
Content-Disposition: inline

It was suggested that I send this patch here. Any comments?

-- 
--------------------------------------------------------------------------
Troy Benjegerdes                'da hozer'                hozer@hozed.org  

Somone asked my why I work on this free (http://www.fsf.org/philosophy/)
software stuff and not get a real job. Charles Shultz had the best answer:

"Why do musicians compose symphonies and poets write poems? They do it
because life wouldn't have any meaning for them if they didn't. That's why
I draw cartoons. It's my life." -- Charles Shultz

--=_kalmia.hozed.org-19977-1078420994-0001-2
Content-Type: message/rfc822
Content-Disposition: inline

Delivered-To: hozer@hozed.org
Return-Path: <dhowells@redhat.com>
Received: from warthog.cambridge.redhat.com (softdnserr [::ffff:213.86.99.237])
  by kalmia.hozed.org with esmtp; Tue, 24 Feb 2004 05:04:54 -0600
Received: from warthog.cambridge.redhat.com (localhost.localdomain [127.0.0.1])
	by warthog.cambridge.redhat.com (8.12.10/8.12.8) with ESMTP id i1OB4qDV002871
	for <hozer@hozed.org>; Tue, 24 Feb 2004 11:04:52 GMT
Received: from redhat.com (dhowells@localhost)
	by warthog.cambridge.redhat.com (8.12.10/8.12.10/Submit) with ESMTP id i1OB4pqX002867
	for <hozer@hozed.org>; Tue, 24 Feb 2004 11:04:52 GMT
X-Authentication-Warning: warthog.cambridge.redhat.com: dhowells owned process doing -bs
From: David Howells <dhowells@redhat.com>
In-Reply-To: <20040223031712.GE29343@kalmia.hozed.org> 
References: <20040223031712.GE29343@kalmia.hozed.org> 
To: Troy Benjegerdes <hozer@hozed.org>
Subject: Re: [shadow@dementia.org: Re: [OpenAFS] Linux kernel 2.6 & AFS] 
User-Agent: EMH/1.14.1 SEMI/1.14.4 (Hosorogi) FLIM/1.14.5 (Demachiyanagi) APEL/10.6 Emacs/21.3 (i386-redhat-linux-gnu) MULE/5.0 (SAKAKI)
Mime-Version: 1.0
Content-Type: multipart/mixed; boundary="=_kalmia.hozed.org-19977-1078420994-0001-3"
Date: Tue, 24 Feb 2004 11:04:51 +0000
Message-ID: <2866.1077620691@redhat.com>
Sender: dhowells@redhat.com
X-Spam-Checker-Version: SpamAssassin 2.61 (1.212.2.1-2003-12-09-exp) on 
	kalmia.hozed.org
X-Spam-Level: 
X-Spam-Status: No, hits=-4.9 required=5.0 tests=BAYES_00 autolearn=no 
	version=2.61

This is a MIME-formatted message.  If you see this text it means that your
E-mail software does not support MIME-formatted messages.

--=_kalmia.hozed.org-19977-1078420994-0001-3
Content-Type: text/plain; charset=us-ascii
Content-Transfer-Encoding: 7bit


Hi Troy,

> What's the status of the 2.6 kernel afs implementation, and do you have
> any newer versions that haven't been merged?

There's extra features pending, but Linus refuses to take them until 2.7
because of the feature freeze.

> I'd like to see *something* make it into the kernel that gives a common
> interface for authentication for both AFS (kafs and/or openafs) and
> NFSv4.

I have a patch to provide facilities for the retention of tokens within the
kernel such that they're automatically deleted at the right times (written to
incorporate Linus's ideas). However, that's not going to get in till 2.7
either.

I've attached the patch should you want to peruse it, an example key
definition module (for kerberos) and three files that make up a simple aklog
client that can pass kerberos tokens to the kernel.

This also provides a way to provide PAG numbers:-)

> Thanks, and let me know if there's anything I can do to help.

Um. Not a lot. Not unless you can persuade Linus to open 2.7. Once Linus
accepts features, they'll be quite easy to backport to 2.6.

David


--=_kalmia.hozed.org-19977-1078420994-0001-3
Content-Type: application/octet-stream; type=patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment; filename="keys-260t5.diff"

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

--=_kalmia.hozed.org-19977-1078420994-0001-3
Content-Type: application/octet-stream
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment; filename="key_krb.c"

/* key_krb.c: Kerberos authentication token management
 *
 * Copyright (C) 2003 Red Hat, Inc. All Rights Reserved.
 * Written by David Howells (dhowells@redhat.com)
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version
 * 2 of the License, or (at your option) any later version.
 */

#include <linux/module.h>
#include <linux/init.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/key.h>
#include <linux/seq_file.h>
#include <asm/errno.h>

static int krb_key_init(struct key *key, const char *desc,
			 size_t datalen, const char *data);
static int krb_key_match(const struct key *key, const void *desc);
static void krb_key_clear(struct key *key);
static void krb_key_describe(const struct key *keyring, struct seq_file *m);

/* Kerberos ticket
 * - the description must be in "<vers>;<principle>@<realm>" format
 */
static struct key_type key_type_krb = {
	.name		= "krb",
	.link		= LIST_HEAD_INIT(key_type_krb.link),
	.init		= krb_key_init,
	.match		= krb_key_match,
	.clear		= krb_key_clear,
	.describe	= krb_key_describe,
};

static int krb_key_init(struct key *key, const char *desc,
			 size_t datalen, const char *data)
{
	size_t dlen;

	if (!strchr(desc, '@'))
		return -EINVAL;

	dlen = strlen(desc) + 1;
	key->description.data = kmalloc(dlen, GFP_KERNEL);
	if (!key->description.data)
		return -ENOMEM;
	memcpy(key->description.data, desc, dlen);

	key->payload.data = kmalloc(datalen, GFP_KERNEL);
	if (!key->payload.data)
		return -ENOMEM;

	key->datalen = datalen;
	memcpy(key->payload.data, data, datalen);

	return 0;
}

static int krb_key_match(const struct key *key, const void *desc)
{
	if (!key->description.data)
		return 0;

	return strcmp(key->description.data, desc) == 0 ? 1 : 0;
}

static void krb_key_clear(struct key *key)
{
	if (key->description.data)
		kfree(key->description.data);
	if (key->payload.data)
		kfree(key->payload.data);
}

static void krb_key_describe(const struct key *key, struct seq_file *m)
{
	if (key->description.data)
		seq_puts(m, key->description.data);
	else
		seq_puts(m, "[anon]");
}

static int __init krb_init(void)
{
	return register_key_type(&key_type_krb);
}

__initcall(krb_init);

--=_kalmia.hozed.org-19977-1078420994-0001-3
Content-Type: application/octet-stream
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment; filename="afsutil.h"

/* afsutil.h: AFS client utility library
 *
 * 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 _AFS_UTIL_H
#define _AFS_UTIL_H

#include <stdlib.h>
#include <stdint.h>

extern int afsutil_authorise_krb5(const char *cell,
				  const char *realm,
				  time_t expiry,
				  size_t session_key_size,
				  const void *session_key,
				  int ticket_kvno,
				  size_t ticket_size,
				  const void *ticket);
				  

#endif /* _AFS_UTIL_H */

--=_kalmia.hozed.org-19977-1078420994-0001-3
Content-Type: application/octet-stream
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment; filename="kernel.c"

/* kernel.c: routines for talking to the kernel
 *
 * 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 <stdlib.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <alloca.h>
#include <sys/prctl.h>

/* 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 */

typedef int32_t key_serial_t;

struct afs_key_data {
	uint16_t	session_key_size;
	uint16_t	ticket_size;
	int32_t		kvno;
	time_t		expiry;
	uint8_t		data[0];
};

/*****************************************************************************/
/*
 * pass authorisation information to the kernel indicating that we have a valid
 * kerberos4 ticket
 */
int afsutil_authorise_krb5(const char *cell,
			   const char *realm,
			   time_t expiry,
			   size_t session_key_size,
			   const void *session_key,
			   int ticket_kvno,
			   size_t ticket_size,
			   const void *ticket)
{
	struct afs_key_data *keydata;
	uint16_t payload_size;
	void *buffer;
	int ret;

	payload_size =
		sizeof(struct afs_key_data) +
		session_key_size +
		ticket_size;

	buffer = alloca(sizeof(uint16_t) + payload_size);

	*(uint16_t *) buffer = payload_size;

	keydata = buffer + sizeof(uint16_t);
	keydata->session_key_size	= session_key_size;
	keydata->ticket_size		= ticket_size;
	keydata->kvno			= ticket_kvno;
	keydata->expiry			= expiry;

	memcpy(keydata->data, session_key, session_key_size);
	memcpy(keydata->data + session_key_size, ticket, ticket_size);

	if (prctl(PR_ADD_NEW_KEY, PR_SPEC_SESSION_KEYRING,
		  "afs", cell, buffer) < 0)
		return -1;

	return 0;
} /* end afsutil_authorise_krb5() */

--=_kalmia.hozed.org-19977-1078420994-0001-3
Content-Type: application/octet-stream
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment; filename="aklog.c"

/* aklog.c: request a Kerberos ticket grant for an AFS cell
 *
 * 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 <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <krb5.h>
#include <com_err.h>
#include <kerberosIV/krb.h>
#include <afsutil.h>

extern int krb524_convert_creds_kdc(krb5_context, krb5_creds *, CREDENTIALS *);

char *realm = "CAMBRIDGE.REDHAT.COM"; //NULL;
char *cell = "cambridge.redhat.com"; //NULL;
int debug = 0;

krb5_principal	princ;
krb5_context	krb5context;
krb5_ccache	tktcache;

static void format(void) __attribute__((noreturn));
static void format(void)
{
	fprintf(stderr,
		"aklog [-d] [-c] [<cell>] [-k <realm>]\n");

	exit(2);
}

#define KRBERR(X,M) do { if ((X) != KSUCCESS) krberr((X),(M)); } while(0)

void krberr(errcode_t kerr, const char *where) __attribute__((noreturn));
void krberr(errcode_t kerr, const char *where)
{
	fprintf(stderr, "%s: %s\n", where, error_message(kerr));
	exit(1);
}

/*****************************************************************************/
/*
 * parse the argument list
 */
void parse_args(char **argv)
{
	if (!*argv)
		return;

	if (strcmp(argv[0], "-help") == 0)
		format();

	if (strcmp(argv[0], "-d") == 0) {
		debug++;
		argv++;
	}

	if (strcmp(argv[0], "-c") == 0)
		argv++;

	if (!*argv)
		format();

	if (argv[0][0] == '-')
		format();
	cell = *argv;
	argv++;

	if (!*argv)
		return;

	if (strcmp(argv[0], "-k") != 0)
		format();
	argv++;

	if (!*argv)
		return;

	if (argv[0][0] == '-')
		format();
	realm = *argv;
	argv++;

	if (*argv)
		format();

} /* end parse_args() */

/*****************************************************************************/
/*
 * try to obtain a kerberos ticket
 */
int obtain_ticket(krb5_creds **creds, const char *service, const char *cell)
{
	krb5_error_code kerr;
	krb5_creds request;

	/* set up a description of what we actually want */
	memset(&request, 0, sizeof(request));

	kerr = krb5_build_principal(krb5context, &request.server,
				    strlen(realm), realm,
				    service, cell,
				    NULL);
	KRBERR(kerr, "failed to construct request");

	request.client			= princ;
	request.times.endtime		= 0;
	request.keyblock.enctype	= ENCTYPE_DES_CBC_CRC;

	/* go and prod the Kerberos servers */
	kerr = krb5_get_credentials(krb5context, 0, tktcache, &request, creds);
	if (kerr != KSUCCESS && kerr != KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN)
		KRBERR(kerr, "error talking to KDC");

	return kerr == KSUCCESS;
} /* end obtain_ticket() */

/*****************************************************************************/
/*
 *
 */
int main(int argc, char *argv[])
{
	krb5_error_code kerr;
	unsigned char *cp;
	krb5_creds *creds5;
	CREDENTIALS creds4;
	time_t time;
	char buf1[100];
	int loop;

	parse_args(argv + 1);

	kerr = krb5_init_context(&krb5context);
	KRBERR(kerr, "failed to initialise the Kerberos5 context");

	/* may need to find the default realm */
	if (!realm) {
		kerr = krb5_get_default_realm(krb5context, &realm);
		KRBERR(kerr, "failed to get the default realm");
	}

	/* may need to find the default AFS cell */
	if (!cell) {
	}

	/* open the appropriate Kerberos ticket cache */
	kerr = krb5_cc_default(krb5context, &tktcache);
	KRBERR(kerr, "unable to resolve default cred cache");

	kerr = krb5_cc_get_principal(krb5context, tktcache, &princ);
	KRBERR(kerr, "unable to extract the principal from the cache");

	/* ask the KDC to give us an AFS ticket */
	if (!cell || !obtain_ticket(&creds5, "afs", cell)) {
		if (!obtain_ticket(&creds5, "afs", NULL)) {
			fprintf(stderr, "couldn't obtain AFS ticket\n");
			exit(2);
		}
	}

	kerr = krb5_cc_close(krb5context, tktcache);
	KRBERR(kerr, "error closing cache");

	/* ask the KDC to turn the Kerberos 5 ticket into a Kerberos 4
	 * ticket */
	kerr = krb524_convert_creds_kdc(krb5context, creds5, &creds4);
	KRBERR(kerr, "unable to convert to a kerberos V4 ticket");

	/* dump the credential data obtained */
	if (debug) {
		printf("SERVICE  : %s%s%s@%s\n",
		       creds4.service,
		       creds4.instance[0] ? "/" : "",
		       creds4.instance,
		       creds4.realm);
		printf("PRINCIPAL: %s%s%s@%s\n",
		       creds4.pname,
		       creds4.pinst[0] ? "/" : "", creds4.pinst,
		       creds4.realm);

		time = creds4.issue_date;
		printf("ISSUED   : %s", ctime_r(&time, buf1));

		time = creds5->times.endtime;
		printf("EXPIRES  : %s", ctime_r(&time, buf1));

		printf("SESSION  : key=[");
		cp = (unsigned char *) &creds4.session;
		for (loop = 0; loop < sizeof(creds4.session); loop++)
			printf("%02x", *cp++);
		printf("]\n");

		printf("TICKET   : version %d, length %d:",
		       creds4.kvno, creds4.ticket_st.length);

		cp = (unsigned char *) &creds4.ticket_st.dat;
		for (loop = 0; loop < creds4.ticket_st.length; loop++) {
			if (loop % (76 / 2) == 0)
				printf("\n    ");
			printf("%02x", *cp++);
		}
		printf("\n");
	}

	/* pass the ticket to the AFS filesystem */
	if (afsutil_authorise_krb5(cell,
				   realm,
				   creds5->times.endtime,
				   sizeof(creds4.session),
				   &creds4.session,
				   creds4.kvno,
				   creds4.ticket_st.length,
				   creds4.ticket_st.dat) < 0
	    ) {
		fprintf(stderr, "unable to pass token to kernel: %m\n");
		exit(1);
	}

	krb5_free_creds(krb5context, creds5);

	krb5_free_context(krb5context);

	return 0;
} /* end main() */

--=_kalmia.hozed.org-19977-1078420994-0001-3--

--=_kalmia.hozed.org-19977-1078420994-0001-2--