[PATCH] Support supplementary information for request-key

From: David Howells
Date: Thu Sep 02 2004 - 04:23:07 EST



The attached patch adds support to the request key interface such that
supplementary information can be passed to the /sbin/request-key program. This
argument also controls whether or not that program will be invoked.

The attached patch also fixes some places where the session keyring was being
dealt with incorrectly and some places where the error returns from function
calls that return pointers weren't being transferred into the return integer
variable.

This has been tested with revised copies of keyctl.c, request-key.c and
request-key-dhowells.sh that have now been uploaded to:

http://people.redhat.com/~dhowells/keys/

Signed-Off-By: David Howells <dhowells@xxxxxxxxxx>
---

Documentation/keys.txt | 28 +++++++++++----
fs/keyfs/rootfile.c | 14 ++++++-
include/linux/key.h | 2 -
security/keys/keyctl.c | 80 +++++++++++++++++++++++++++++++------------
security/keys/process_keys.c | 36 ++++++++++---------
security/keys/request_key.c | 40 +++++++++++++--------
6 files changed, 136 insertions(+), 64 deletions(-)

diff -uNr linux-2.6.9-rc1-mm2-keys/Documentation/keys.txt linux-2.6.9-rc1-mm2-keys-reqkey/Documentation/keys.txt
--- linux-2.6.9-rc1-mm2-keys/Documentation/keys.txt 2004-08-31 16:52:24.000000000 +0100
+++ linux-2.6.9-rc1-mm2-keys-reqkey/Documentation/keys.txt 2004-09-01 21:29:38.000000000 +0100
@@ -515,6 +515,7 @@

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,
@@ -522,6 +523,10 @@
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:

@@ -602,12 +607,14 @@

struct key *request_key(const struct key_type *type,
const char *description,
- int callout);
+ const char *callout_string);

This is used to request a key or keyring with a description that matches
the description specified according to the key type's match function. This
- permits approximate matching to occur. If callout is non-zero, then the key
- service is allowed to ask userspace to create or update the key.
+ permits approximate matching to occur. If callout_string is not NULL, then
+ /sbin/request-key will be invoked in an attempt to obtain the key from
+ userspace. In that case, callout_string will be passed as an argument to
+ the program.

Should the function fail error ENOKEY, EKEYEXPIRED or EKEYREVOKED will be
returned.
@@ -761,7 +768,7 @@
line:

/sbin/request-key create <key> <uid> <gid> \
- <threadring> <processring> <sessionring>
+ <threadring> <processring> <sessionring> <callout_info>

<key> is the key being constructed, and the three keyrings are the process
keyrings from the process that caused the search to be issued. These are
@@ -778,12 +785,19 @@
example, the KDE desktop manager).

The program (or whatever it calls) should finish construction of the key by
-calling KEYCTL_INSTANTIATE and then call KEYCTL_LINK to cache the key in one
-of the keyrings (probably the session ring) before returning.
+calling KEYCTL_INSTANTIATE, which also permits it to cache the key in one of
+the keyrings (probably the session ring) before returning. Alternatively, the
+key can be marked as negative with KEYCTL_NEGATE; this also permits the key to
+be cached in one of the keyrings.

If it returns with the key remaining in the unconstructed state, the key will
be marked as being negative, it will be added to the session keyring, and an
-error will be returned to the key requester.
+error will be returned to the key requestor.
+
+Supplementary information may be provided from whoever or whatever invoked
+this service. This will be passed as the <callout_info> parameter. If no such
+information was made available, then "-" will be passed as this parameter
+instead.


Similarly, the kernel may attempt to update an expired or a soon to expire key
diff -uNr linux-2.6.9-rc1-mm2-keys/include/linux/key.h linux-2.6.9-rc1-mm2-keys-reqkey/include/linux/key.h
--- linux-2.6.9-rc1-mm2-keys/include/linux/key.h 2004-08-31 16:52:38.000000000 +0100
+++ linux-2.6.9-rc1-mm2-keys-reqkey/include/linux/key.h 2004-09-01 20:27:15.000000000 +0100
@@ -200,7 +200,7 @@

extern struct key *request_key(struct key_type *type,
const char *description,
- int callout);
+ const char *callout_info);

extern int key_validate(struct key *key);

diff -uNr linux-2.6.9-rc1-mm2-keys/security/keys/keyctl.c linux-2.6.9-rc1-mm2-keys-reqkey/security/keys/keyctl.c
--- linux-2.6.9-rc1-mm2-keys/security/keys/keyctl.c 2004-08-31 17:12:12.000000000 +0100
+++ linux-2.6.9-rc1-mm2-keys-reqkey/security/keys/keyctl.c 2004-09-01 21:58:48.000000000 +0100
@@ -207,7 +207,7 @@
struct key *keyring;
long ret;

- keyring = lookup_user_key(ringid, 0, 0, KEY_WRITE);
+ keyring = lookup_user_key(ringid, 1, 0, KEY_WRITE);
if (IS_ERR(keyring)) {
ret = PTR_ERR(keyring);
goto error;
@@ -267,7 +267,7 @@
struct key *keyring, *key;
long ret;

- keyring = lookup_user_key(ringid, 1, 0, KEY_WRITE);
+ keyring = lookup_user_key(ringid, 0, 0, KEY_WRITE);
if (IS_ERR(keyring)) {
ret = PTR_ERR(keyring);
goto error;
@@ -398,15 +398,19 @@

/* get the keyring at which to begin the search */
keyring = lookup_user_key(ringid, 0, 0, KEY_SEARCH);
- if (IS_ERR(keyring))
+ if (IS_ERR(keyring)) {
+ ret = PTR_ERR(keyring);
goto error2;
+ }

/* get the destination keyring if specified */
dest = NULL;
if (destringid) {
- dest = lookup_user_key(destringid, 0, 0, KEY_WRITE);
- if (IS_ERR(dest))
+ dest = lookup_user_key(destringid, 1, 0, KEY_WRITE);
+ if (IS_ERR(dest)) {
+ ret = PTR_ERR(dest);
goto error3;
+ }
}

/* find the key type */
@@ -633,31 +637,36 @@
error:
return ret;

-} /* end user_serperm_key() */
+} /* end user_setperm_key() */

/*****************************************************************************/
/*
* search the process keyrings for a matching key
- * - nested keyrings may also be searched if they are searchable
+ * - 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;
+ char type[32], *description, *callout_info;
long dlen, ret;

- /* pull the type and description into kernel space */
+ /* 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)
@@ -676,43 +685,69 @@
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, 0, 0, KEY_WRITE);
- if (IS_ERR(dest))
- goto error2;
+ 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 error3;
+ goto error4;
}

/* do the search */
- key = request_key(ktype, description, 1);
+ key = request_key(ktype, description, callout_info);
if (IS_ERR(key)) {
ret = PTR_ERR(key);
- goto error4;
+ goto error5;
}

/* link the resulting key to the destination keyring */
if (dest) {
ret = key_link(dest, key);
if (ret < 0)
- goto error5;
+ goto error6;
}

ret = key->serial;

- error5:
+ error6:
key_put(key);
- error4:
+ error5:
key_type_put(ktype);
- error3:
+ error4:
key_put(dest);
+ error3:
+ kfree(callout_info);
error2:
kfree(description);
error:
@@ -769,7 +804,7 @@
* writable) */
keyring = NULL;
if (ringid) {
- keyring = lookup_user_key(ringid, 0, 0, KEY_WRITE);
+ keyring = lookup_user_key(ringid, 1, 0, KEY_WRITE);
if (IS_ERR(keyring)) {
ret = PTR_ERR(keyring);
goto error3;
@@ -812,7 +847,7 @@
* writable) */
keyring = NULL;
if (ringid) {
- keyring = lookup_user_key(ringid, 0, 0, KEY_WRITE);
+ keyring = lookup_user_key(ringid, 1, 0, KEY_WRITE);
if (IS_ERR(keyring)) {
ret = PTR_ERR(keyring);
goto error2;
@@ -891,7 +926,8 @@
case KEYCTL_REQUEST_KEY:
return user_request_key((const char __user *) arg2,
(const char __user *) arg3,
- (key_serial_t) arg4);
+ (const char __user *) arg4,
+ (key_serial_t) arg5);

case KEYCTL_INSTANTIATE:
return user_instantiate_key((key_serial_t) arg2,
diff -uNr linux-2.6.9-rc1-mm2-keys/security/keys/process_keys.c linux-2.6.9-rc1-mm2-keys-reqkey/security/keys/process_keys.c
--- linux-2.6.9-rc1-mm2-keys/security/keys/process_keys.c 2004-08-31 17:12:12.000000000 +0100
+++ linux-2.6.9-rc1-mm2-keys-reqkey/security/keys/process_keys.c 2004-09-02 09:21:46.937113725 +0100
@@ -373,7 +373,7 @@
key_match_func_t match)
{
struct task_struct *tsk = current;
- struct key *key, *ret, *err;
+ struct key *key, *ret, *err, *session;

/* we want to return -EAGAIN or -ENOKEY if any of the keyrings were
* searchable, but we failed to find a key or we found a negative key;
@@ -427,23 +427,25 @@
}

/* search the session keyring last */
- if (tsk->session_keyring) {
- key = keyring_search_aux(tsk->session_keyring, type,
- description, match);
- if (!IS_ERR(key))
- goto found;
-
- switch (PTR_ERR(key)) {
- case -EAGAIN: /* no key */
- if (ret)
- break;
- case -ENOKEY: /* negative key */
- ret = key;
- break;
- default:
- err = key;
+ session = tsk->session_keyring;
+ if (!session)
+ session = tsk->user->session_keyring;
+
+ key = keyring_search_aux(session, type,
+ description, match);
+ if (!IS_ERR(key))
+ goto found;
+
+ switch (PTR_ERR(key)) {
+ case -EAGAIN: /* no key */
+ if (ret)
break;
- }
+ case -ENOKEY: /* negative key */
+ ret = key;
+ break;
+ default:
+ err = key;
+ break;
}

/* no key - decide on the error we're going to go for */
diff -uNr linux-2.6.9-rc1-mm2-keys/security/keys/request_key.c linux-2.6.9-rc1-mm2-keys-reqkey/security/keys/request_key.c
--- linux-2.6.9-rc1-mm2-keys/security/keys/request_key.c 2004-08-31 16:52:41.000000000 +0100
+++ linux-2.6.9-rc1-mm2-keys-reqkey/security/keys/request_key.c 2004-09-02 09:49:09.065232756 +0100
@@ -26,12 +26,15 @@
/*****************************************************************************/
/*
* request userspace finish the construction of a key
- * - execute "/sbin/request-key <op> <key> <uid> <gid> <keyring> <keyring> <keyring>"
+ * - execute "/sbin/request-key <op> <key> <uid> <gid> <keyring> <keyring> <keyring> <info>"
+ * - if callout_info is an empty string, it'll be rendered as a "-" instead
*/
-static int call_request_key(struct key *key, char *op)
+static int call_request_key(struct key *key,
+ const char *op,
+ const char *callout_info)
{
struct task_struct *tsk = current;
- char *argv[9], *envp[3], uid_str[12], gid_str[12];
+ char *argv[10], *envp[3], uid_str[12], gid_str[12];
char key_str[12], keyring_str[3][12];
int i;

@@ -49,7 +52,9 @@
sprintf(keyring_str[1], "%d",
tsk->process_keyring ? tsk->process_keyring->serial : 0);
sprintf(keyring_str[2], "%d",
- tsk->session_keyring ? tsk->session_keyring->serial : 0);
+ (tsk->session_keyring ?
+ tsk->session_keyring->serial :
+ tsk->user->session_keyring->serial));
task_unlock(tsk);

/* set up a minimal environment */
@@ -61,13 +66,14 @@
/* set up the argument list */
i = 0;
argv[i++] = "/sbin/request-key";
- argv[i++] = op;
+ argv[i++] = (char *) op;
argv[i++] = key_str;
argv[i++] = uid_str;
argv[i++] = gid_str;
argv[i++] = keyring_str[0];
argv[i++] = keyring_str[1];
argv[i++] = keyring_str[2];
+ argv[i++] = callout_info[0] ? (char *) callout_info : "-";
argv[i] = NULL;

/* do it */
@@ -82,7 +88,8 @@
* - we ignore program failure and go on key status instead
*/
static struct key *__request_key_construction(struct key_type *type,
- const char *description)
+ const char *description,
+ const char *callout_info)
{
struct key_construction cons;
struct timespec now;
@@ -106,7 +113,7 @@
up_write(&key_construction_sem);

/* make the call */
- ret = call_request_key(key, "create");
+ ret = call_request_key(key, "create", callout_info);
if (ret < 0)
goto request_failed;

@@ -183,7 +190,8 @@
*/
static struct key *request_key_construction(struct key_type *type,
const char *description,
- struct key_user *user)
+ struct key_user *user,
+ const char *callout_info)
{
struct key_construction *pcons;
struct key *key, *ckey;
@@ -204,7 +212,7 @@
}

/* see about getting userspace to construct the key */
- key = __request_key_construction(type, description);
+ key = __request_key_construction(type, description, callout_info);
error:
return key;

@@ -244,23 +252,24 @@
* request a key
* - search the process's keyrings
* - check the list of keys being created or updated
- * - call out to userspace for a key
+ * - call out to userspace for a key if requested (supplementary info can be
+ * passed)
*/
struct key *request_key(struct key_type *type,
const char *description,
- int callout)
+ const char *callout_info)
{
struct key_user *user;
struct key *key;

/* search all the process keyrings for a key */
- key = search_process_keyrings(type, description);
+ key = search_process_keyrings_aux(type, description, type->match);

if (PTR_ERR(key) == -EAGAIN) {
/* the search failed, but the keyrings were searchable, so we
* should consult userspace if we can */
key = ERR_PTR(-ENOKEY);
- if (!callout)
+ if (!callout_info)
goto error;

/* - get hold of the user's construction queue */
@@ -274,13 +283,14 @@
/* ask userspace (returns NULL if it waited on a key
* being constructed) */
key = request_key_construction(type, description,
- user);
+ user, callout_info);
if (key)
break;

/* someone else made the key we want, so we need to
* search again as it might now be available to us */
- key = search_process_keyrings(type, description);
+ key = search_process_keyrings_aux(type, description,
+ type->match);
if (PTR_ERR(key) != -EAGAIN)
break;
}
diff -uNr linux-2.6.9-rc1-mm2-keys/fs/keyfs/rootfile.c linux-2.6.9-rc1-mm2-keys-reqkey/fs/keyfs/rootfile.c
--- linux-2.6.9-rc1-mm2-keys/fs/keyfs/rootfile.c 2004-08-31 16:52:33.000000000 +0100
+++ linux-2.6.9-rc1-mm2-keys-reqkey/fs/keyfs/rootfile.c 2004-09-01 20:44:39.000000000 +0100
@@ -198,7 +198,7 @@
struct key_type *ktype;
struct key *key;
ssize_t ret;
- char *buffer, *bend, *p, *type, *desc;
+ char *buffer, *bend, *p, *type, *desc, *info;

ret = -EINVAL;
if (rec->state != KEYFS_BIFILE_WANT_PARAMS || buflen > 40000)
@@ -220,6 +220,7 @@

/* parse the parameter string */
ret = -EINVAL;
+ info = NULL;

type = buffer;

@@ -234,6 +235,15 @@
goto error2;
*p++ = 0;

+ if (p != bend) {
+ info = p;
+
+ p = memchr(p, '\n', bend - p);
+ if (!p)
+ goto error2;
+ *p++ = 0;
+ }
+
if (p != bend)
goto error2;

@@ -245,7 +255,7 @@
}

/* request the key */
- key = request_key(ktype, desc, 1);
+ key = request_key(ktype, desc, info);
if (IS_ERR(key)) {
ret = PTR_ERR(key);
goto error3;
-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/