diff --git a/Documentation/security/keys/core.rst b/Documentation/security/keys/core.rst index 82dd457ff78d..003f1452a5b7 100644 --- a/Documentation/security/keys/core.rst +++ b/Documentation/security/keys/core.rst @@ -1147,6 +1147,16 @@ payload contents" for more information. case error ERESTARTSYS will be returned. + * To search for a key under RCU conditions, call:: + + struct key *request_key_rcu(const struct key_type *type, + const char *description); + + which is similar to request_key() except that it does not check for keys + that are under construction and it will not call out to userspace to + construct a key if it can't find a match. + + * When it is no longer required, the key should be released using:: void key_put(struct key *key); diff --git a/Documentation/security/keys/request-key.rst b/Documentation/security/keys/request-key.rst index 07af991463b5..7caedc4d29f1 100644 --- a/Documentation/security/keys/request-key.rst +++ b/Documentation/security/keys/request-key.rst @@ -36,6 +36,11 @@ or:: size_t callout_len, void *aux); +or:: + + struct key *request_key_rcu(const struct key_type *type, + const char *description); + Or by userspace invoking the request_key system call:: key_serial_t request_key(const char *type, @@ -57,6 +62,10 @@ The two async in-kernel calls may return keys that are still in the process of being constructed. The two non-async ones will wait for construction to complete first. +The request_key_rcu() call is like the in-kernel request_key() call, except +that it doesn't check for keys that are under construction and doesn't attempt +to construct missing keys. + The userspace interface links the key to a keyring associated with the process to prevent the key from going away, and returns the serial number of the key to the caller. diff --git a/include/linux/key.h b/include/linux/key.h index 612e1cf84049..3604a554df99 100644 --- a/include/linux/key.h +++ b/include/linux/key.h @@ -274,6 +274,9 @@ extern struct key *request_key(struct key_type *type, const char *description, const char *callout_info); +extern struct key *request_key_rcu(struct key_type *type, + const char *description); + extern struct key *request_key_with_auxdata(struct key_type *type, const char *description, const void *callout_info, diff --git a/security/keys/request_key.c b/security/keys/request_key.c index bf1d223ec21c..b4b3677657d6 100644 --- a/security/keys/request_key.c +++ b/security/keys/request_key.c @@ -756,3 +756,47 @@ struct key *request_key_async_with_auxdata(struct key_type *type, callout_len, aux, NULL, KEY_ALLOC_IN_QUOTA); } EXPORT_SYMBOL(request_key_async_with_auxdata); + +/** + * request_key_rcu - Request key from RCU-read-locked context + * @type: The type of key we want. + * @description: The name of the key we want. + * + * Request a key from a context that we may not sleep in (such as RCU-mode + * pathwalk). Keys under construction are ignored. + * + * Return a pointer to the found key if successful, -ENOKEY if we couldn't find + * a key or some other error if the key found was unsuitable or inaccessible. + */ +struct key *request_key_rcu(struct key_type *type, const char *description) +{ + struct keyring_search_context ctx = { + .index_key.type = type, + .index_key.description = description, + .index_key.desc_len = strlen(description), + .cred = current_cred(), + .match_data.cmp = key_default_cmp, + .match_data.raw_data = description, + .match_data.lookup_type = KEYRING_SEARCH_LOOKUP_DIRECT, + .flags = (KEYRING_SEARCH_DO_STATE_CHECK | + KEYRING_SEARCH_SKIP_EXPIRED), + }; + struct key *key; + key_ref_t key_ref; + + kenter("%s,%s", type->name, description); + + /* search all the process keyrings for a key */ + key_ref = search_process_keyrings_rcu(&ctx); + if (IS_ERR(key_ref)) { + key = ERR_CAST(key_ref); + if (PTR_ERR(key_ref) == -EAGAIN) + key = ERR_PTR(-ENOKEY); + } else { + key = key_ref_to_ptr(key_ref); + } + + kleave(" = %p", key); + return key; +} +EXPORT_SYMBOL(request_key_rcu);