diff options
Diffstat (limited to 'security/keys')
-rw-r--r-- | security/keys/Kconfig | 18 | ||||
-rw-r--r-- | security/keys/request_key.c | 37 |
2 files changed, 55 insertions, 0 deletions
diff --git a/security/keys/Kconfig b/security/keys/Kconfig index 6462e6654ccf..12f70b556d09 100644 --- a/security/keys/Kconfig +++ b/security/keys/Kconfig @@ -24,6 +24,24 @@ config KEYS_COMPAT def_bool y depends on COMPAT && KEYS +config KEYS_REQUEST_CACHE + bool "Enable temporary caching of the last request_key() result" + depends on KEYS + help + This option causes the result of the last successful request_key() + call that didn't upcall to the kernel to be cached temporarily in the + task_struct. The cache is cleared by exit and just prior to the + resumption of userspace. + + This allows the key used for multiple step processes where each step + wants to request a key that is likely the same as the one requested + by the last step to save on the searching. + + An example of such a process is a pathwalk through a network + filesystem in which each method needs to request an authentication + key. Pathwalk will call multiple methods for each dentry traversed + (permission, d_revalidate, lookup, getxattr, getacl, ...). + config PERSISTENT_KEYRINGS bool "Enable register of persistent per-UID keyrings" depends on KEYS diff --git a/security/keys/request_key.c b/security/keys/request_key.c index b4b3677657d6..f289982cb5db 100644 --- a/security/keys/request_key.c +++ b/security/keys/request_key.c @@ -22,6 +22,31 @@ #define key_negative_timeout 60 /* default timeout on a negative key's existence */ +static struct key *check_cached_key(struct keyring_search_context *ctx) +{ +#ifdef CONFIG_KEYS_REQUEST_CACHE + struct key *key = current->cached_requested_key; + + if (key && + ctx->match_data.cmp(key, &ctx->match_data) && + !(key->flags & ((1 << KEY_FLAG_INVALIDATED) | + (1 << KEY_FLAG_REVOKED)))) + return key_get(key); +#endif + return NULL; +} + +static void cache_requested_key(struct key *key) +{ +#ifdef CONFIG_KEYS_REQUEST_CACHE + struct task_struct *t = current; + + key_put(t->cached_requested_key); + t->cached_requested_key = key_get(key); + set_tsk_thread_flag(t, TIF_NOTIFY_RESUME); +#endif +} + /** * complete_request_key - Complete the construction of a key. * @authkey: The authorisation key. @@ -562,6 +587,10 @@ struct key *request_key_and_link(struct key_type *type, } } + key = check_cached_key(&ctx); + if (key) + return key; + /* search all the process keyrings for a key */ rcu_read_lock(); key_ref = search_process_keyrings_rcu(&ctx); @@ -587,6 +616,9 @@ struct key *request_key_and_link(struct key_type *type, goto error_free; } } + + /* Only cache the key on immediate success */ + cache_requested_key(key); } else if (PTR_ERR(key_ref) != -EAGAIN) { key = ERR_CAST(key_ref); } else { @@ -786,6 +818,10 @@ struct key *request_key_rcu(struct key_type *type, const char *description) kenter("%s,%s", type->name, description); + key = check_cached_key(&ctx); + if (key) + return key; + /* search all the process keyrings for a key */ key_ref = search_process_keyrings_rcu(&ctx); if (IS_ERR(key_ref)) { @@ -794,6 +830,7 @@ struct key *request_key_rcu(struct key_type *type, const char *description) key = ERR_PTR(-ENOKEY); } else { key = key_ref_to_ptr(key_ref); + cache_requested_key(key); } kleave(" = %p", key); |