[PATCH] Make key management use syscalls not prctls

David Howells
2004-09-06T12:16:28Z

The attached patch makes the key management patch use syscalls instead of
prctls, though only the i386 arch is so furnished by this patch.

This patch creates three new syscalls: add_key(), request_key() and keyctl(),
the last of which is provides all the other key and keyring manipulation
operations that used to be prctls.

Doing this allows simplification of the interface... those calls that take a
payload no longer look for the payload length in the first two bytes of the
buffer but take it instead as a syscall argument.

http://people.redhat.com/~dhowells/keys/keyctl.c has been updated to match.

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

warthog>diffstat keys-syscalls-269rc1mm2.diff
Documentation/keys.txt | 146 ++++++-----
arch/i386/kernel/entry.S | 5
include/asm-generic/errno.h | 2
include/asm-i386/unistd.h | 9
include/linux/key.h | 21 +
include/linux/keyctl.h | 39 +++
include/linux/prctl.h | 26 --
kernel/sys.c | 14 -
security/keys/keyctl.c | 531 +++++++++++++++++++++++--------------------
security/keys/process_keys.c | 80 ------
10 files changed, 440 insertions(+), 433 deletions(-)

diff -uNr linux-2.6.9-rc1-mm2-keys-reqkey/arch/i386/kernel/entry.S linux-2.6.9-rc1-mm2-keys-syscalls/arch/i386/kernel/entry.S
--- linux-2.6.9-rc1-mm2-keys-reqkey/arch/i386/kernel/entry.S 2004-08-31 16:52:17.000000000 +0100
+++ linux-2.6.9-rc1-mm2-keys-syscalls/arch/i386/kernel/entry.S 2004-09-04 11:35:55.000000000 +0100
@@ -943,6 +943,9 @@
.long sys_vperfctr_unlink
.long sys_vperfctr_iresume
.long sys_vperfctr_read
- .long sys_waitid
+ .long sys_waitid /* 290 */
+ .long sys_add_key
+ .long sys_request_key
+ .long sys_keyctl

syscall_table_size=(.-sys_call_table)
diff -uNr linux-2.6.9-rc1-mm2-keys-reqkey/Documentation/keys.txt linux-2.6.9-rc1-mm2-keys-syscalls/Documentation/keys.txt
--- linux-2.6.9-rc1-mm2-keys-reqkey/Documentation/keys.txt 2004-09-01 21:29:38.000000000 +0100
+++ linux-2.6.9-rc1-mm2-keys-syscalls/Documentation/keys.txt 2004-09-06 11:29:59.912867321 +0100
@@ -287,65 +287,33 @@
USERSPACE SYSTEM CALL INTERFACE
===============================

-Userspace can manipulate keys directly through a number of prctl functions,
-and through a new syscall, keyctl, that also provides a number of
-functions. Currently the keyctl syscall is provided through prctl.
+Userspace can manipulate keys directly through three new syscalls: add_key,
+request_key and keyctl. The latter provides a number of functions for
+manipulating keys.

When referring to a key directly, userspace programs should use the key's
serial number (a positive 32-bit integer). However, there are some special
values available for referring to special keys and keyrings that relate to the
process making the call:

- CONSTANT VALUE KEY REFERENCED
- ============================== ====== ===========================
- PR_SPEC_THREAD_KEYRING -1 thread-specific keyring
- PR_SPEC_PROCESS_KEYRING -2 process-specific keyring
- PR_SPEC_SESSION_KEYRING -3 session-specific keyring
- PR_SPEC_USER_KEYRING -4 UID-specific keyring
- PR_SPEC_USER_SESSION_KEYRING -5 UID-session keyring
- PR_SPEC_GROUP_KEYRING -6 GID-specific keyring
+ CONSTANT VALUE KEY REFERENCED
+ ============================== ====== ===========================
+ KEY_SPEC_THREAD_KEYRING -1 thread-specific keyring
+ KEY_SPEC_PROCESS_KEYRING -2 process-specific keyring
+ KEY_SPEC_SESSION_KEYRING -3 session-specific keyring
+ KEY_SPEC_USER_KEYRING -4 UID-specific keyring
+ KEY_SPEC_USER_SESSION_KEYRING -5 UID-session keyring
+ KEY_SPEC_GROUP_KEYRING -6 GID-specific keyring

-The additional prctl syscall functions are:
-
- (*) Map a special key ID to a real key ID for this process:
-
- key_serial_t prctl(PR_GET_KEYID, key_serial_t id, int create);
-
- The special key specified by "id" is looked up (with the key being
- created if necessary) and the ID of the key or keyring thus found is
- returned if it exists.
-
- If the key does not yet exist, the key will be created if "create" is
- non-zero; and the error ENOKEY will be returned if "create" is zero.
-
-
- (*) Replace the session keyring this process subscribes to with a new one:
-
- key_serial_t prctl(PR_JOIN_SESSION_KEYRING, const char *name);
-
- If name is NULL, an anonymous keyring is created attached to the process
- as its session keyring, displacing the old session keyring.
-
- If name is not NULL, if a keyring of that name exists, the process
- attempts to attach it as the session keyring, returning an error if that
- is not permitted; otherwise a new keyring of that name is created and
- attached as the session keyring.
-
- To attach to a named keyring, the keyring must have search permission for
- the process's ownership.
-
- The ID of the new session keyring is returned if successful.
-
-
-The new keyctl syscall functions are:
+The main syscalls are:

(*) Create a new key of given type, description and payload and add it to the
nominated keyring:

- key_serial_t keyctl(KEYCTL_NEW, key_serial_t keyring,
- const char *type, const char *desc,
- const void *payload);
+ key_serial_t add_key(const char *type, const char *desc,
+ const void *payload, size_t plen,
+ key_serial_t keyring);

If a key of the same type and description as that proposed already exists
in the keyring, this will try to update it with the given payload, or it
@@ -359,9 +327,9 @@
attach it to the keyring. In this case, an error will be generated if the
process does not have permission to write to the keyring.

- The first two bytes in the payload buffer should be the length of the
- data (in CPU byte order) followed by the data. The payload is optional,
- and the pointer can be NULL if not required by the type.
+ The payload is optional, and the pointer can be NULL if not required by
+ the type. The payload is plen in size, and plen can be zero for an empty
+ payload.

A new keyring can be generated by setting type "keyring", the keyring
name as the description (or NULL) and setting the payload to NULL.
@@ -377,16 +345,68 @@
The ID of the new or updated key is returned if successful.

+ (*) Search the process's keyrings for a key, potentially calling out to
+ userspace to create it.
+
+ key_serial_t request_key(const char *type, const char *description,
+ const char *callout_info,
+ key_serial_t dest_keyring);
+
+ This function searches all the process's keyrings in the order thread,
+ process, session for a matching key. This works very much like
+ KEYCTL_SEARCH, including the optional attachment of the discovered key to
+ a keyring.
+
+ If a key cannot be found, and if callout_info is not NULL, then
+ /sbin/request-key will be invoked in an attempt to obtain a key. The
+ callout_info string will be passed as an argument to the program.
+
+
+The keyctl syscall functions are:
+
+ (*) Map a special key ID to a real key ID for this process:
+
+ key_serial_t keyctl(KEYCTL_GET_KEYRING_ID, key_serial_t id,
+ int create);
+
+ The special key specified by "id" is looked up (with the key being
+ created if necessary) and the ID of the key or keyring thus found is
+ returned if it exists.
+
+ If the key does not yet exist, the key will be created if "create" is
+ non-zero; and the error ENOKEY will be returned if "create" is zero.
+
+
+ (*) Replace the session keyring this process subscribes to with a new one:
+
+ key_serial_t keyctl(KEYCTL_JOIN_SESSION_KEYRING, const char *name);
+
+ If name is NULL, an anonymous keyring is created attached to the process
+ as its session keyring, displacing the old session keyring.
+
+ If name is not NULL, if a keyring of that name exists, the process
+ attempts to attach it as the session keyring, returning an error if that
+ is not permitted; otherwise a new keyring of that name is created and
+ attached as the session keyring.
+
+ To attach to a named keyring, the keyring must have search permission for
+ the process's ownership.
+
+ The ID of the new session keyring is returned if successful.
+
+
(*) Update the specified key:

- long keyctl(KEYCTL_UPDATE, key_serial_t key, const void *payload);
+ long keyctl(KEYCTL_UPDATE, key_serial_t key, const void *payload,
+ size_t plen);

This will try to update the specified key with the given payload, or it
will return error EOPNOTSUPP if that function is not supported by the key
type. The process must also have permission to write to the key to be
able to update it.

- The payload must be in the same format as for KEYCTL_NEW.
+ The payload is of length plen, and may be absent or empty as for
+ add_key().

(*) Revoke a key:
@@ -511,23 +531,6 @@
fails. On success, the resulting key ID will be returned.

- (*) Search the process's keyrings for a key:
-
- key_serial_t keyctl(KEYCTL_REQUEST_KEY,
- const char *type, const char *description,
- const char *callout_info,
- key_serial_t dest_keyring);
-
- This function searches all the process's keyrings in the order thread,
- process, session for a matching key. This works very much like
- KEYCTL_SEARCH, including the optional attachment of the discovered key to
- a keyring.
-
- If a key cannot be found, and if callout_info is not NULL, then
- /sbin/request-key will be invoked in an attempt to obtain a key. The
- callout_info string will be passed as an argument to the program.
-
-
(*) Read the payload data from a key:

key_serial_t keyctl(KEYCTL_READ, key_serial_t keyring, char *buffer,
@@ -553,7 +556,8 @@
(*) Instantiate a partially constructed key.

key_serial_t keyctl(KEYCTL_INSTANTIATE, key_serial_t key,
- const void *payload, key_serial_t keyring);
+ const void *payload, size_t plen,
+ key_serial_t keyring);

If the kernel calls back to userspace to complete the instantiation of a
key, userspace should use this call to supply data for the key before the
@@ -567,6 +571,8 @@
that keyring, however all the constraints applying in KEYCTL_LINK apply
in this case too.

+ The payload and plen arguments describe the payload data as for add_key().
+

(*) Negatively instantiate a partially constructed key.

diff -uNr linux-2.6.9-rc1-mm2-keys-reqkey/include/asm-generic/errno.h linux-2.6.9-rc1-mm2-keys-syscalls/include/asm-generic/errno.h
--- linux-2.6.9-rc1-mm2-keys-reqkey/include/asm-generic/errno.h 2004-08-31 16:52:35.000000000 +0100
+++ linux-2.6.9-rc1-mm2-keys-syscalls/include/asm-generic/errno.h 2004-09-05 10:57:26.000000000 +0100
@@ -101,4 +101,6 @@
#define EKEYREVOKED 127 /* Key has been revoked */
#define EKEYREJECTED 128 /* Key was rejected by service */

+#define __ERRNO_LAST 128
+
#endif
diff -uNr linux-2.6.9-rc1-mm2-keys-reqkey/include/asm-i386/unistd.h linux-2.6.9-rc1-mm2-keys-syscalls/include/asm-i386/unistd.h
--- linux-2.6.9-rc1-mm2-keys-reqkey/include/asm-i386/unistd.h 2004-08-31 16:52:37.000000000 +0100
+++ linux-2.6.9-rc1-mm2-keys-syscalls/include/asm-i386/unistd.h 2004-09-05 10:56:53.000000000 +0100
@@ -296,14 +296,17 @@
#define __NR_vperfctr_iresume (__NR_perfctr_info+4)
#define __NR_vperfctr_read (__NR_perfctr_info+5)
#define __NR_waitid 290
+#define __NR_add_key 291
+#define __NR_request_key 292
+#define __NR_keyctl 293

-#define NR_syscalls 291
+#define NR_syscalls 294

-/* user-visible error numbers are in the range -1 - -124: see <asm-i386/errno.h> */
+/* user-visible error numbers are in the range -1 - -128: see <asm-i386/errno.h> */

#define __syscall_return(type, res) \
do { \
- if ((unsigned long)(res) >= (unsigned long)(-125)) { \
+ if ((unsigned long)(res) >= (unsigned long)(-(__ERRNO_LAST + 1))) { \
errno = -(res); \
res = -1; \
} \
diff -uNr linux-2.6.9-rc1-mm2-keys-reqkey/include/linux/keyctl.h linux-2.6.9-rc1-mm2-keys-syscalls/include/linux/keyctl.h
--- linux-2.6.9-rc1-mm2-keys-reqkey/include/linux/keyctl.h 1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.9-rc1-mm2-keys-syscalls/include/linux/keyctl.h 2004-09-05 10:28:07.000000000 +0100
@@ -0,0 +1,39 @@
+/* keyctl.h: keyctl command IDs
+ *
+ * Copyright (C) 2004 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_KEYCTL_H
+#define _LINUX_KEYCTL_H
+
+/* special process keyring shortcut IDs */
+#define KEY_SPEC_THREAD_KEYRING -1 /* - key ID for thread-specific keyring */
+#define KEY_SPEC_PROCESS_KEYRING -2 /* - key ID for process-specific keyring */
+#define KEY_SPEC_SESSION_KEYRING -3 /* - key ID for session-specific keyring */
+#define KEY_SPEC_USER_KEYRING -4 /* - key ID for UID-specific keyring */
+#define KEY_SPEC_USER_SESSION_KEYRING -5 /* - key ID for UID-session keyring */
+#define KEY_SPEC_GROUP_KEYRING -6 /* - key ID for GID-specific keyring */
+
+/* keyctl commands */
+#define KEYCTL_GET_KEYRING_ID 0 /* ask for a keyring's ID */
+#define KEYCTL_JOIN_SESSION_KEYRING 1 /* join or start named session keyring */
+#define KEYCTL_UPDATE 2 /* update a key */
+#define KEYCTL_REVOKE 3 /* revoke a key */
+#define KEYCTL_CHOWN 4 /* set ownership of a key */
+#define KEYCTL_SETPERM 5 /* set perms on a key */
+#define KEYCTL_DESCRIBE 6 /* describe a key */
+#define KEYCTL_CLEAR 7 /* clear contents of a keyring */
+#define KEYCTL_LINK 8 /* link a key into a keyring */
+#define KEYCTL_UNLINK 9 /* unlink a key from a keyring */
+#define KEYCTL_SEARCH 10 /* search for a key in a keyring */
+#define KEYCTL_READ 11 /* read a key or keyring's contents */
+#define KEYCTL_INSTANTIATE 12 /* instantiate a partially constructed key */
+#define KEYCTL_NEGATE 13 /* negate a partially constructed key */
+
+#endif /* _LINUX_KEYCTL_H */
diff -uNr linux-2.6.9-rc1-mm2-keys-reqkey/include/linux/key.h linux-2.6.9-rc1-mm2-keys-syscalls/include/linux/key.h
--- linux-2.6.9-rc1-mm2-keys-reqkey/include/linux/key.h 2004-09-01 20:27:15.000000000 +0100
+++ linux-2.6.9-rc1-mm2-keys-syscalls/include/linux/key.h 2004-09-04 11:34:32.000000000 +0100
@@ -250,10 +250,19 @@
extern int exec_keys(struct task_struct *tsk);
extern void key_fsuid_changed(struct task_struct *tsk);
extern void key_fsgid_changed(struct task_struct *tsk);
-extern long get_process_keyring_ID(key_serial_t id, int create);
-extern long join_session_keyring_user(const char __user *name);

-asmlinkage long sys_keyctl(int option, unsigned long arg2, unsigned long arg3,
+asmlinkage long sys_add_key(const char __user *_type,
+ const char __user *_description,
+ const void __user *_payload,
+ size_t plen,
+ key_serial_t destringid);
+
+asmlinkage long sys_request_key(const char __user *_type,
+ const char __user *_description,
+ const char __user *_callout_info,
+ key_serial_t destringid);
+
+asmlinkage long sys_keyctl(int cmd, unsigned long arg2, unsigned long arg3,
unsigned long arg4, unsigned long arg5);

#else /* CONFIG_KEYS */
@@ -267,9 +276,9 @@
#define exec_keys(t) do { } while(0)
#define key_fsuid_changed(t) do { } while(0)
#define key_fsgid_changed(t) do { } while(0)
-#define get_process_keyring_ID(s,c) (-EINVAL)
-#define join_session_keyring_user(n) (-EINVAL)
-#define sys_keyctl(o,b,c,d,e) (-EINVAL)
+#define sys_addkey(a,b,c,d,e) (-ENOSYS)
+#define sys_request_key(a,b,c,d,e) (-ENOSYS)
+#define sys_keyctl(a,b,c,d,e) (-ENOSYS)

#endif /* CONFIG_KEYS */
#endif /* __KERNEL__ */
diff -uNr linux-2.6.9-rc1-mm2-keys-reqkey/include/linux/prctl.h linux-2.6.9-rc1-mm2-keys-syscalls/include/linux/prctl.h
--- linux-2.6.9-rc1-mm2-keys-reqkey/include/linux/prctl.h 2004-08-31 16:52:39.000000000 +0100
+++ linux-2.6.9-rc1-mm2-keys-syscalls/include/linux/prctl.h 2004-09-04 11:28:34.000000000 +0100
@@ -49,30 +49,4 @@
# define PR_TIMING_TIMESTAMP 1 /* Accurate timestamp based
process timing */

-/* Manage a process's keyrings */
-#define PR_SPEC_THREAD_KEYRING -1 /* - key ID for thread-specific keyring */
-#define PR_SPEC_PROCESS_KEYRING -2 /* - key ID for process-specific keyring */
-#define PR_SPEC_SESSION_KEYRING -3 /* - key ID for session-specific keyring */
-#define PR_SPEC_USER_KEYRING -4 /* - key ID for UID-specific keyring */
-#define PR_SPEC_USER_SESSION_KEYRING -5 /* - key ID for UID-session keyring */
-#define PR_SPEC_GROUP_KEYRING -6 /* - key ID for GID-specific keyring */
-
-#define PR_GET_KEYRING_ID 15 /* ask for a keyring's ID */
-#define PR_JOIN_SESSION_KEYRING 16 /* join or start named session keyring */
-#define KEYCTL_NEW 0x100 /* create a key and add to specified keyring */
-#define KEYCTL_UPDATE 0x101 /* update a key */
-#define KEYCTL_REVOKE 0x102 /* revoke a key */
-#define KEYCTL_CHOWN 0x103 /* set ownership of a key */
-#define KEYCTL_SETPERM 0x104 /* set perms on a key */
-#define KEYCTL_DESCRIBE 0x105 /* describe a key */
-#define KEYCTL_CLEAR 0x106 /* clear contents of a keyring */
-#define KEYCTL_LINK 0x107 /* link a key into a keyring */
-#define KEYCTL_UNLINK 0x108 /* unlink a key from a keyring */
-#define KEYCTL_SEARCH 0x109 /* search for a key in a keyring */
-#define KEYCTL_READ 0x10a /* read a key or keyring's contents */
-#define KEYCTL_REQUEST_KEY 0x10b /* request a key from the process's keyrings */
-#define KEYCTL_INSTANTIATE 0x10c /* instantiate a partially constructed key */
-#define KEYCTL_NEGATE 0x10d /* negate a partially constructed key */
-#define __KEYCTL_LAST 0x10d
-
#endif /* _LINUX_PRCTL_H */
diff -uNr linux-2.6.9-rc1-mm2-keys-reqkey/kernel/sys.c linux-2.6.9-rc1-mm2-keys-syscalls/kernel/sys.c
--- linux-2.6.9-rc1-mm2-keys-reqkey/kernel/sys.c 2004-08-31 16:52:40.000000000 +0100
+++ linux-2.6.9-rc1-mm2-keys-syscalls/kernel/sys.c 2004-09-06 10:16:46.075079772 +0100
@@ -279,6 +279,9 @@
cond_syscall(sys_get_mempolicy)
cond_syscall(sys_set_mempolicy)
cond_syscall(compat_get_mempolicy)
+cond_syscall(sys_add_key)
+cond_syscall(sys_request_key)
+cond_syscall(sys_keyctl)

/* arch-specific weak syscall entries */
cond_syscall(sys_pciconfig_read)
@@ -1783,17 +1786,6 @@
current->keep_capabilities = arg2;
break;

- case PR_GET_KEYRING_ID:
- error = get_process_keyring_ID((key_serial_t) arg2, arg3);
- break;
- case PR_JOIN_SESSION_KEYRING:
- error = join_session_keyring_user((const char __user *) arg2);
- break;
-
- case KEYCTL_NEW ... __KEYCTL_LAST:
- error = sys_keyctl(option, arg2, arg3, arg4, arg5);
- break;
-
default:
error = -EINVAL;
break;
diff -uNr linux-2.6.9-rc1-mm2-keys-reqkey/security/keys/keyctl.c linux-2.6.9-rc1-mm2-keys-syscalls/security/keys/keyctl.c
--- linux-2.6.9-rc1-mm2-keys-reqkey/security/keys/keyctl.c 2004-09-01 21:58:48.000000000 +0100
+++ linux-2.6.9-rc1-mm2-keys-syscalls/security/keys/keyctl.c 2004-09-06 11:20:31.166815319 +0100
@@ -13,7 +13,7 @@
#include <linux/init.h>
#include <linux/sched.h>
#include <linux/slab.h>
-#include <linux/prctl.h>
+#include <linux/keyctl.h>
#include <linux/fs.h>
#include <linux/err.h>
#include <asm/uaccess.h>
@@ -25,19 +25,23 @@
* new key to the specified keyring or update a matching key in that keyring
* - the keyring must be writable
* - returns the new key's serial number
- * - implements keyctl(KEYCTL_NEW)
+ * - implements add_key()
*/
-static long user_add_key(key_serial_t ringid,
- const char __user *_type,
- const char __user *_description,
- const void __user *_payload)
+asmlinkage long sys_add_key(const char __user *_type,
+ const char __user *_description,
+ const void __user *_payload,
+ size_t plen,
+ key_serial_t ringid)
{
struct key *keyring, *key;
- size_t plen;
char type[32], *description;
void *payload;
long dlen, ret;

+ ret = -EINVAL;
+ if (plen > 32767)
+ goto error;
+
/* draw all the data into kernel space */
ret = strncpy_from_user(type, _type, sizeof(type) - 1);
if (ret < 0)
@@ -64,20 +68,10 @@

/* pull the payload in if one was supplied */
payload = NULL;
- plen = 0;

if (_payload) {
- ret = get_user(plen, (uint16_t *) _payload);
- if (ret < 0)
- goto error2;
- _payload += 2;
-
- ret = -EINVAL;
- if (plen < 0 || plen > PAGE_SIZE)
- goto error2;
-
ret = -ENOMEM;
- payload = (void *) get_zeroed_page(GFP_KERNEL);
+ payload = kmalloc(plen, GFP_KERNEL);
if (!payload)
goto error2;

@@ -107,13 +101,195 @@

key_put(keyring);
error3:
- free_page((unsigned long) payload);
+ kfree(payload);
+ error2:
+ kfree(description);
+ error:
+ return ret;
+
+} /* end sys_add_key() */
+
+/*****************************************************************************/
+/*
+ * search the process keyrings for a matching key
+ * - nested keyrings may also be searched if they have Search permission
+ * - if a key is found, it will be attached to the destination keyring if
+ * there's one specified
+ * - /sbin/request-key will be invoked if _callout_info is non-NULL
+ * - the _callout_info string will be passed to /sbin/request-key
+ * - if the _callout_info string is empty, it will be rendered as "-"
+ * - implements request_key()
+ */
+asmlinkage long sys_request_key(const char __user *_type,
+ const char __user *_description,
+ const char __user *_callout_info,
+ key_serial_t destringid)
+{
+ struct key_type *ktype;
+ struct key *key, *dest;
+ char type[32], *description, *callout_info;
+ long dlen, ret;
+
+ /* pull the type into kernel space */
+ ret = strncpy_from_user(type, _type, sizeof(type) - 1);
+ if (ret < 0)
+ goto error;
+ type[31] = '\0';
+
+ /* pull the description into kernel space */
+ ret = -EFAULT;
+ dlen = strnlen_user(_description, PAGE_SIZE - 1);
+ if (dlen <= 0)
+ goto error;
+
+ ret = -EINVAL;
+ if (dlen > PAGE_SIZE - 1)
+ goto error;
+
+ ret = -ENOMEM;
+ description = kmalloc(dlen + 1, GFP_KERNEL);
+ if (!description)
+ goto error;
+
+ ret = -EFAULT;
+ if (copy_from_user(description, _description, dlen + 1) != 0)
+ goto error2;
+
+ /* pull the callout info into kernel space */
+ callout_info = NULL;
+ if (_callout_info) {
+ ret = -EFAULT;
+ dlen = strnlen_user(_callout_info, PAGE_SIZE - 1);
+ if (dlen <= 0)
+ goto error2;
+
+ ret = -EINVAL;
+ if (dlen > PAGE_SIZE - 1)
+ goto error2;
+
+ ret = -ENOMEM;
+ callout_info = kmalloc(dlen + 1, GFP_KERNEL);
+ if (!callout_info)
+ goto error2;
+
+ ret = -EFAULT;
+ if (copy_from_user(callout_info, _callout_info, dlen + 1) != 0)
+ goto error3;
+ }
+
+ /* get the destination keyring if specified */
+ dest = NULL;
+ if (destringid) {
+ dest = lookup_user_key(destringid, 1, 0, KEY_WRITE);
+ if (IS_ERR(dest)) {
+ ret = PTR_ERR(dest);
+ goto error3;
+ }
+ }
+
+ /* find the key type */
+ ktype = key_type_lookup(type);
+ if (IS_ERR(ktype)) {
+ ret = PTR_ERR(ktype);
+ goto error4;
+ }
+
+ /* do the search */
+ key = request_key(ktype, description, callout_info);
+ if (IS_ERR(key)) {
+ ret = PTR_ERR(key);
+ goto error5;
+ }
+
+ /* link the resulting key to the destination keyring */
+ if (dest) {
+ ret = key_link(dest, key);
+ if (ret < 0)
+ goto error6;
+ }
+
+ ret = key->serial;
+
+ error6:
+ key_put(key);
+ error5:
+ key_type_put(ktype);
+ error4:
+ key_put(dest);
+ error3:
+ kfree(callout_info);
error2:
kfree(description);
error:
return ret;

-} /* end user_add_key() */
+} /* end sys_request_key() */
+
+/*****************************************************************************/
+/*
+ * get the ID of the specified process keyring
+ * - the keyring must have search permission to be found
+ * - implements keyctl(KEYCTL_GET_KEYRING_ID)
+ */
+static long keyctl_get_keyring_ID(key_serial_t id, int create)
+{
+ struct key *key;
+ long ret;
+
+ key = lookup_user_key(id, create, 0, KEY_SEARCH);
+ if (IS_ERR(key)) {
+ ret = PTR_ERR(key);
+ goto error;
+ }
+
+ ret = key->serial;
+ key_put(key);
+ error:
+ return ret;
+
+} /* end keyctl_get_keyring_ID() */
+
+/*****************************************************************************/
+/*
+ * join the session keyring
+ * - implements keyctl(KEYCTL_JOIN_SESSION_KEYRING)
+ */
+static long keyctl_join_session_keyring(const char __user *_name)
+{
+ char *name;
+ long nlen, ret;
+
+ /* fetch the name from userspace */
+ name = NULL;
+ if (_name) {
+ ret = -EFAULT;
+ nlen = strnlen_user(_name, PAGE_SIZE - 1);
+ if (nlen <= 0)
+ goto error;
+
+ ret = -EINVAL;
+ if (nlen > PAGE_SIZE - 1)
+ goto error;
+
+ ret = -ENOMEM;
+ name = kmalloc(nlen + 1, GFP_KERNEL);
+ if (!name)
+ goto error;
+
+ ret = -EFAULT;
+ if (copy_from_user(name, _name, nlen + 1) != 0)
+ goto error2;
+ }
+
+ /* join the session */
+ ret = join_session_keyring(name);
+
+ error2:
+ kfree(name);
+ error:
+ return ret;
+
+} /* end keyctl_join_session_keyring() */

/*****************************************************************************/
/*
@@ -121,29 +297,23 @@
* - the key must be writable
* - implements keyctl(KEYCTL_UPDATE)
*/
-static long user_update_key(key_serial_t id, const void __user *_payload)
+static long keyctl_update_key(key_serial_t id,
+ const void __user *_payload,
+ size_t plen)
{
struct key *key;
- size_t plen;
void *payload;
long ret;

+ ret = -EINVAL;
+ if (plen > PAGE_SIZE)
+ goto error;
+
/* pull the payload in if one was supplied */
payload = NULL;
- plen = 0;
-
if (_payload) {
- 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);
+ payload = kmalloc(plen, GFP_KERNEL);
if (!payload)
goto error;

@@ -164,11 +334,11 @@

key_put(key);
error2:
- free_page((unsigned long) payload);
+ kfree(payload);
error:
return ret;

-} /* end user_update_key() */
+} /* end keyctl_update_key() */

/*****************************************************************************/
/*
@@ -176,7 +346,7 @@
* - the key must be writable
* - implements keyctl(KEYCTL_REVOKE)
*/
-static long user_revoke_key(key_serial_t id)
+static long keyctl_revoke_key(key_serial_t id)
{
struct key *key;
long ret;
@@ -194,7 +364,7 @@
error:
return 0;

-} /* end user_revoke_key() */
+} /* end keyctl_revoke_key() */

/*****************************************************************************/
/*
@@ -202,7 +372,7 @@
* - the keyring must be writable
* - implements keyctl(KEYCTL_CLEAR)
*/
-static long user_keyring_clear(key_serial_t ringid)
+static long keyctl_keyring_clear(key_serial_t ringid)
{
struct key *keyring;
long ret;
@@ -219,7 +389,7 @@
error:
return ret;

-} /* end user_keyring_clear() */
+} /* end keyctl_keyring_clear() */

/*****************************************************************************/
/*
@@ -228,7 +398,7 @@
* - the key must be linkable
* - implements keyctl(KEYCTL_LINK)
*/
-static long user_keyring_link(key_serial_t ringid, key_serial_t id)
+static long keyctl_keyring_link(key_serial_t id, key_serial_t ringid)
{
struct key *keyring, *key;
long ret;
@@ -253,7 +423,7 @@
error:
return ret;

-} /* end user_keyring_link() */
+} /* end keyctl_keyring_link() */

/*****************************************************************************/
/*
@@ -262,7 +432,7 @@
* - we don't need any permissions on the key
* - implements keyctl(KEYCTL_UNLINK)
*/
-static long user_keyring_unlink(key_serial_t ringid, key_serial_t id)
+static long keyctl_keyring_unlink(key_serial_t id, key_serial_t ringid)
{
struct key *keyring, *key;
long ret;
@@ -287,7 +457,7 @@
error:
return ret;

-} /* end user_keyring_unlink() */
+} /* end keyctl_keyring_unlink() */

/*****************************************************************************/
/*
@@ -300,9 +470,9 @@
* type;uid;gid;perm;description<NUL>
* - implements keyctl(KEYCTL_DESCRIBE)
*/
-static long user_describe_key(key_serial_t keyid,
- char __user *buffer,
- size_t buflen)
+static long keyctl_describe_key(key_serial_t keyid,
+ char __user *buffer,
+ size_t buflen)
{
struct key *key;
char *tmpbuf;
@@ -350,7 +520,7 @@
error:
return ret;

-} /* end user_describe_key() */
+} /* end keyctl_describe_key() */

/*****************************************************************************/
/*
@@ -362,10 +532,10 @@
* there's one specified
* - implements keyctl(KEYCTL_SEARCH)
*/
-static long user_keyring_search(key_serial_t ringid,
- const char __user *_type,
- const char __user *_description,
- key_serial_t destringid)
+static long keyctl_keyring_search(key_serial_t ringid,
+ const char __user *_type,
+ const char __user *_description,
+ key_serial_t destringid)
{
struct key_type *ktype;
struct key *keyring, *key, *dest;
@@ -457,17 +627,17 @@
error:
return ret;

-} /* end user_keyring_search() */
+} /* end keyctl_keyring_search() */

/*****************************************************************************/
/*
* see if the key we're looking at is the target key
*/
-static int user_read_key_same(const struct key *key, const void *target)
+static int keyctl_read_key_same(const struct key *key, const void *target)
{
return key == target;

-} /* end user_read_key_same() */
+} /* end keyctl_read_key_same() */

/*****************************************************************************/
/*
@@ -479,9 +649,9 @@
* irrespective of how much we may have copied
* - implements keyctl(KEYCTL_READ)
*/
-static long user_read_key(key_serial_t keyid,
- char __user *buffer,
- size_t buflen)
+static long keyctl_read_key(key_serial_t keyid,
+ char __user *buffer,
+ size_t buflen)
{
struct key *key, *skey;
long ret;
@@ -500,7 +670,7 @@
/* okay - we do have search permission on the key
* itself, but do we have the key? */
skey = search_process_keyrings_aux(key->type, key,
- user_read_key_same);
+ keyctl_read_key_same);
if (!IS_ERR(skey))
goto can_read_key2;
}
@@ -532,7 +702,7 @@
error:
return ret;

-} /* end user_read_key() */
+} /* end keyctl_read_key() */

/*****************************************************************************/
/*
@@ -541,7 +711,7 @@
* - if the uid or gid is -1, then that parameter is not changed
* - implements keyctl(KEYCTL_CHOWN)
*/
-static long user_chown_key(key_serial_t id, uid_t uid, gid_t gid)
+static long keyctl_chown_key(key_serial_t id, uid_t uid, gid_t gid)
{
struct key *key;
long ret;
@@ -592,7 +762,7 @@
error:
return ret;

-} /* end user_chown_key() */
+} /* end keyctl_chown_key() */

/*****************************************************************************/
/*
@@ -600,7 +770,7 @@
* - the keyring owned by the changer
* - implements keyctl(KEYCTL_SETPERM)
*/
-static long user_setperm_key(key_serial_t id, key_perm_t perm)
+static long keyctl_setperm_key(key_serial_t id, key_perm_t perm)
{
struct key *key;
long ret;
@@ -637,154 +807,32 @@
error:
return ret;

-} /* end user_setperm_key() */
-
-/*****************************************************************************/
-/*
- * search the process keyrings for a matching key
- * - nested keyrings may also be searched if they have Search permission
- * - if a key is found, it will be attached to the destination keyring if
- * there's one specified
- * - /sbin/request-key will be invoked if _callout_info is non-NULL
- * - the _callout_info string will be passed to /sbin/request-key
- * - if the _callout_info string is empty, it will be rendered as "-"
- * - implements keyctl(KEYCTL_REQUEST_KEY)
- */
-static long user_request_key(const char __user *_type,
- const char __user *_description,
- const char __user *_callout_info,
- key_serial_t destringid)
-{
- struct key_type *ktype;
- struct key *key, *dest;
- char type[32], *description, *callout_info;
- long dlen, ret;
-
- /* pull the type into kernel space */
- ret = strncpy_from_user(type, _type, sizeof(type) - 1);
- if (ret < 0)
- goto error;
- type[31] = '\0';
-
- /* pull the description into kernel space */
- ret = -EFAULT;
- dlen = strnlen_user(_description, PAGE_SIZE - 1);
- if (dlen <= 0)
- goto error;
-
- ret = -EINVAL;
- if (dlen > PAGE_SIZE - 1)
- goto error;
-
- ret = -ENOMEM;
- description = kmalloc(dlen + 1, GFP_KERNEL);
- if (!description)
- goto error;
-
- ret = -EFAULT;
- if (copy_from_user(description, _description, dlen + 1) != 0)
- goto error2;
-
- /* pull the callout info into kernel space */
- callout_info = NULL;
- if (_callout_info) {
- ret = -EFAULT;
- dlen = strnlen_user(_callout_info, PAGE_SIZE - 1);
- if (dlen <= 0)
- goto error2;
-
- ret = -EINVAL;
- if (dlen > PAGE_SIZE - 1)
- goto error2;
-
- ret = -ENOMEM;
- callout_info = kmalloc(dlen + 1, GFP_KERNEL);
- if (!callout_info)
- goto error2;
-
- ret = -EFAULT;
- if (copy_from_user(callout_info, _callout_info, dlen + 1) != 0)
- goto error3;
- }
-
- /* get the destination keyring if specified */
- dest = NULL;
- if (destringid) {
- dest = lookup_user_key(destringid, 1, 0, KEY_WRITE);
- if (IS_ERR(dest)) {
- ret = PTR_ERR(dest);
- goto error3;
- }
- }
-
- /* find the key type */
- ktype = key_type_lookup(type);
- if (IS_ERR(ktype)) {
- ret = PTR_ERR(ktype);
- goto error4;
- }
-
- /* do the search */
- key = request_key(ktype, description, callout_info);
- if (IS_ERR(key)) {
- ret = PTR_ERR(key);
- goto error5;
- }
-
- /* link the resulting key to the destination keyring */
- if (dest) {
- ret = key_link(dest, key);
- if (ret < 0)
- goto error6;
- }
-
- ret = key->serial;
-
- error6:
- key_put(key);
- error5:
- key_type_put(ktype);
- error4:
- key_put(dest);
- error3:
- kfree(callout_info);
- error2:
- kfree(description);
- error:
- return ret;
-
-} /* end user_request_key() */
+} /* end keyctl_setperm_key() */

/*****************************************************************************/
/*
* instantiate the key with the specified payload, and, if one is given, link
* the key into the keyring
*/
-static long user_instantiate_key(key_serial_t id,
- const void __user *_payload,
- key_serial_t ringid)
+static long keyctl_instantiate_key(key_serial_t id,
+ const void __user *_payload,
+ size_t plen,
+ key_serial_t ringid)
{
struct key *key, *keyring;
- size_t plen;
void *payload;
long ret;

+ ret = -EINVAL;
+ if (plen > 32767)
+ goto error;
+
/* pull the payload in if one was supplied */
payload = NULL;
- plen = 0;

if (_payload) {
- 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);
+ payload = kmalloc(plen, GFP_KERNEL);
if (!payload)
goto error;

@@ -818,20 +866,20 @@
error3:
key_put(key);
error2:
- free_page((unsigned long) payload);
+ kfree(payload);
error:
return ret;

-} /* end user_instantiate_key() */
+} /* end keyctl_instantiate_key() */

/*****************************************************************************/
/*
* negatively instantiate the key with the given timeout (in seconds), and, if
* one is given, link the key into the keyring
*/
-static long user_negate_key(key_serial_t id,
- unsigned timeout,
- key_serial_t ringid)
+static long keyctl_negate_key(key_serial_t id,
+ unsigned timeout,
+ key_serial_t ringid)
{
struct key *key, *keyring;
long ret;
@@ -863,7 +911,7 @@
error:
return ret;

-} /* end user_negate_key() */
+} /* end keyctl_negate_key() */

/*****************************************************************************/
/*
@@ -874,73 +922,70 @@
unsigned long arg4, unsigned long arg5)
{
switch (option) {
- case KEYCTL_NEW:
- return user_add_key((key_serial_t) arg2,
- (const char __user *) arg3,
- (const char __user *) arg4,
- (const void __user *) arg5);
-
+ case KEYCTL_GET_KEYRING_ID:
+ return keyctl_get_keyring_ID((key_serial_t) arg2,
+ (int) arg3);
+
+ case KEYCTL_JOIN_SESSION_KEYRING:
+ return keyctl_join_session_keyring((const char __user *) arg3);
+
case KEYCTL_UPDATE:
- return user_update_key((key_serial_t) arg2,
- (const void __user *) arg3);
+ return keyctl_update_key((key_serial_t) arg2,
+ (const void __user *) arg3,
+ (size_t) arg4);

case KEYCTL_REVOKE:
- return user_revoke_key((key_serial_t) arg2);
+ return keyctl_revoke_key((key_serial_t) arg2);

case KEYCTL_DESCRIBE:
- return user_describe_key((key_serial_t) arg2,
- (char __user *) arg3,
- (unsigned) arg4);
+ return keyctl_describe_key((key_serial_t) arg2,
+ (char __user *) arg3,
+ (unsigned) arg4);

case KEYCTL_CLEAR:
- return user_keyring_clear((key_serial_t) arg2);
+ return keyctl_keyring_clear((key_serial_t) arg2);

case KEYCTL_LINK:
- return user_keyring_link((key_serial_t) arg2,
- (key_serial_t) arg3);
+ return keyctl_keyring_link((key_serial_t) arg2,
+ (key_serial_t) arg3);

case KEYCTL_UNLINK:
- return user_keyring_unlink((key_serial_t) arg2,
- (key_serial_t) arg3);
+ return keyctl_keyring_unlink((key_serial_t) arg2,
+ (key_serial_t) arg3);

case KEYCTL_SEARCH:
- return user_keyring_search((key_serial_t) arg2,
- (const char __user *) arg3,
- (const char __user *) arg4,
- (key_serial_t) arg5);
+ return keyctl_keyring_search((key_serial_t) arg2,
+ (const char __user *) arg3,
+ (const char __user *) arg4,
+ (key_serial_t) arg5);

case KEYCTL_READ:
- return user_read_key((key_serial_t) arg2,
- (char __user *) arg3,
- (size_t) arg4);
+ return keyctl_read_key((key_serial_t) arg2,
+ (char __user *) arg3,
+ (size_t) arg4);

case KEYCTL_CHOWN:
- return user_chown_key((key_serial_t) arg2,
- (uid_t) arg3,
- (gid_t) arg4);
+ return keyctl_chown_key((key_serial_t) arg2,
+ (uid_t) arg3,
+ (gid_t) arg4);

case KEYCTL_SETPERM:
- return user_setperm_key((key_serial_t) arg2,
- (key_perm_t) arg3);
-
- case KEYCTL_REQUEST_KEY:
- return user_request_key((const char __user *) arg2,
- (const char __user *) arg3,
- (const char __user *) arg4,
- (key_serial_t) arg5);
+ return keyctl_setperm_key((key_serial_t) arg2,
+ (key_perm_t) arg3);

case KEYCTL_INSTANTIATE:
- return user_instantiate_key((key_serial_t) arg2,
- (const void __user *) arg3,
- (key_serial_t) arg4);
+ return keyctl_instantiate_key((key_serial_t) arg2,
+ (const void __user *) arg3,
+ (size_t) arg4,
+ (key_serial_t) arg5);

case KEYCTL_NEGATE:
- return user_negate_key((key_serial_t) arg2,
- (unsigned) arg3,
- (key_serial_t) arg4);
+ return keyctl_negate_key((key_serial_t) arg2,
+ (unsigned) arg3,
+ (key_serial_t) arg4);

default:
- return -EINVAL;
+ return -EOPNOTSUPP;
}

} /* end sys_keyctl() */
diff -uNr linux-2.6.9-rc1-mm2-keys-reqkey/security/keys/process_keys.c linux-2.6.9-rc1-mm2-keys-syscalls/security/keys/process_keys.c
--- linux-2.6.9-rc1-mm2-keys-reqkey/security/keys/process_keys.c 2004-09-02 09:21:46.000000000 +0100
+++ linux-2.6.9-rc1-mm2-keys-syscalls/security/keys/process_keys.c 2004-09-06 10:10:46.000000000 +0100
@@ -13,7 +13,7 @@
#include <linux/init.h>
#include <linux/sched.h>
#include <linux/slab.h>
-#include <linux/prctl.h>
+#include <linux/keyctl.h>
#include <linux/fs.h>
#include <linux/err.h>
#include <asm/uaccess.h>
@@ -485,7 +485,7 @@
key = ERR_PTR(-ENOKEY);

switch (id) {
- case PR_SPEC_THREAD_KEYRING:
+ case KEY_SPEC_THREAD_KEYRING:
if (!tsk->thread_keyring) {
if (!create)
goto error;
@@ -501,7 +501,7 @@
atomic_inc(&key->usage);
break;

- case PR_SPEC_PROCESS_KEYRING:
+ case KEY_SPEC_PROCESS_KEYRING:
if (!tsk->process_keyring) {
if (!create)
goto error;
@@ -517,7 +517,7 @@
atomic_inc(&key->usage);
break;

- case PR_SPEC_SESSION_KEYRING:
+ case KEY_SPEC_SESSION_KEYRING:
if (!tsk->session_keyring) {
/* always install a session keyring upon access if one
* doesn't exist yet */
@@ -531,17 +531,17 @@
atomic_inc(&key->usage);
break;

- case PR_SPEC_USER_KEYRING:
+ case KEY_SPEC_USER_KEYRING:
key = tsk->user->uid_keyring;
atomic_inc(&key->usage);
break;

- case PR_SPEC_USER_SESSION_KEYRING:
+ case KEY_SPEC_USER_SESSION_KEYRING:
key = tsk->user->session_keyring;
atomic_inc(&key->usage);
break;

- case PR_SPEC_GROUP_KEYRING:
+ case KEY_SPEC_GROUP_KEYRING:
/* group keyrings are not yet supported */
key = ERR_PTR(-EINVAL);
goto error;
@@ -584,30 +584,6 @@

/*****************************************************************************/
/*
- * get the ID of the specified process keyring
- * - the keyring must have search permission to be found
- * - implements prctl(PR_GET_KEYRING_ID)
- */
-long get_process_keyring_ID(key_serial_t id, int create)
-{
- struct key *key;
- long ret;
-
- key = lookup_user_key(id, create, 0, KEY_SEARCH);
- if (IS_ERR(key)) {
- ret = PTR_ERR(key);
- goto error;
- }
-
- ret = key->serial;
- key_put(key);
- error:
- return ret;
-
-} /* end get_process_keyring_ID() */
-
-/*****************************************************************************/
-/*
* join the named keyring as the session keyring if possible, or attempt to
* create a new one of that name if not
* - if the name is NULL, an empty anonymous keyring is installed instead
@@ -662,45 +638,3 @@
return ret;

} /* end join_session_keyring() */
-
-/*****************************************************************************/
-/*
- * join the session keyring
- * - implements prctl(PR_JOIN_SESSION_KEYRING)
- */
-long join_session_keyring_user(const char __user *_name)
-{
- char *name;
- long nlen, ret;
-
- /* fetch the name from userspace */
- name = NULL;
- if (_name) {
- ret = -EFAULT;
- nlen = strnlen_user(_name, PAGE_SIZE - 1);
- if (nlen <= 0)
- goto error;
-
- ret = -EINVAL;
- if (nlen > PAGE_SIZE - 1)
- goto error;
-
- ret = -ENOMEM;
- name = kmalloc(nlen + 1, GFP_KERNEL);
- if (!name)
- goto error;
-
- ret = -EFAULT;
- if (copy_from_user(name, _name, nlen + 1) != 0)
- goto error2;
- }
-
- /* join the session */
- ret = join_session_keyring(name);
-
- error2:
- kfree(name);
- error:
- return ret;
-
-} /* end join_session_keyring_user() */