From: David Howells 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 Signed-off-by: Andrew Morton --- 25-akpm/Documentation/keys.txt | 28 +++++++++--- 25-akpm/fs/keyfs/rootfile.c | 14 +++++- 25-akpm/include/linux/key.h | 2 25-akpm/security/keys/keyctl.c | 80 +++++++++++++++++++++++++---------- 25-akpm/security/keys/process_keys.c | 36 ++++++++------- 25-akpm/security/keys/request_key.c | 40 ++++++++++------- 6 files changed, 136 insertions(+), 64 deletions(-) diff -puN Documentation/keys.txt~support-supplementary-information-for-request-key Documentation/keys.txt --- 25/Documentation/keys.txt~support-supplementary-information-for-request-key 2004-09-02 15:58:23.647871536 -0700 +++ 25-akpm/Documentation/keys.txt 2004-09-02 15:58:23.694864392 -0700 @@ -515,6 +515,7 @@ The new keyctl syscall functions are: 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 @@ The new keyctl syscall functions are: 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 @@ locked, or else the data may be changed 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 @@ To create a new key, the kernel will att line: /sbin/request-key create \ - + 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 @@ hand the request off to (perhaps a path 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 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 -puN fs/keyfs/rootfile.c~support-supplementary-information-for-request-key fs/keyfs/rootfile.c --- 25/fs/keyfs/rootfile.c~support-supplementary-information-for-request-key 2004-09-02 15:58:23.649871232 -0700 +++ 25-akpm/fs/keyfs/rootfile.c 2004-09-02 15:58:23.699863632 -0700 @@ -198,7 +198,7 @@ static ssize_t keyfs_request_key_write(s 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 @@ static ssize_t keyfs_request_key_write(s /* parse the parameter string */ ret = -EINVAL; + info = NULL; type = buffer; @@ -234,6 +235,15 @@ static ssize_t keyfs_request_key_write(s 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 @@ static ssize_t keyfs_request_key_write(s } /* 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; diff -puN include/linux/key.h~support-supplementary-information-for-request-key include/linux/key.h --- 25/include/linux/key.h~support-supplementary-information-for-request-key 2004-09-02 15:58:23.651870928 -0700 +++ 25-akpm/include/linux/key.h 2004-09-02 15:58:23.695864240 -0700 @@ -200,7 +200,7 @@ extern void key_put(struct key *key); 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 -puN security/keys/keyctl.c~support-supplementary-information-for-request-key security/keys/keyctl.c --- 25/security/keys/keyctl.c~support-supplementary-information-for-request-key 2004-09-02 15:58:23.652870776 -0700 +++ 25-akpm/security/keys/keyctl.c 2004-09-02 15:58:23.696864088 -0700 @@ -207,7 +207,7 @@ static long user_keyring_clear(key_seria 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 @@ static long user_keyring_unlink(key_seri 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 @@ static long user_keyring_search(key_seri /* 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 @@ static long user_setperm_key(key_serial_ 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 @@ static long user_request_key(const char 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 @@ static long user_instantiate_key(key_ser * 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 @@ static long user_negate_key(key_serial_t * 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 @@ asmlinkage long sys_keyctl(int option, u 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 -puN security/keys/process_keys.c~support-supplementary-information-for-request-key security/keys/process_keys.c --- 25/security/keys/process_keys.c~support-supplementary-information-for-request-key 2004-09-02 15:58:23.654870472 -0700 +++ 25-akpm/security/keys/process_keys.c 2004-09-02 15:58:23.697863936 -0700 @@ -373,7 +373,7 @@ struct key *search_process_keyrings_aux( 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 @@ struct key *search_process_keyrings_aux( } /* 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 -puN security/keys/request_key.c~support-supplementary-information-for-request-key security/keys/request_key.c --- 25/security/keys/request_key.c~support-supplementary-information-for-request-key 2004-09-02 15:58:23.690865000 -0700 +++ 25-akpm/security/keys/request_key.c 2004-09-02 15:58:23.698863784 -0700 @@ -26,12 +26,15 @@ DECLARE_WAIT_QUEUE_HEAD(request_key_cons /*****************************************************************************/ /* * request userspace finish the construction of a key - * - execute "/sbin/request-key " + * - execute "/sbin/request-key " + * - 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 @@ static int call_request_key(struct key * 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 @@ static int call_request_key(struct key * /* 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 @@ static int call_request_key(struct key * * - 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 @@ static struct key *__request_key_constru 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_constru */ 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 @@ static struct key *request_key_construct } /* 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 @@ static struct key *request_key_construct * 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 @@ struct key *request_key(struct key_type /* 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; } _