Skip to content

Commit 7743c48

Browse files
committed
keys: Cache result of request_key*() temporarily in task_struct
If a filesystem uses keys to hold authentication tokens, then it needs a token for each VFS operation that might perform an authentication check - either by passing it to the server, or using to perform a check based on authentication data cached locally. For open files this isn't a problem, since the key should be cached in the file struct since it represents the subject performing operations on that file descriptor. During pathwalk, however, there isn't anywhere to cache the key, except perhaps in the nameidata struct - but that isn't exposed to the filesystems. Further, a pathwalk can incur a lot of operations, calling one or more of the following, for instance: ->lookup() ->permission() ->d_revalidate() ->d_automount() ->get_acl() ->getxattr() on each dentry/inode it encounters - and each one may need to call request_key(). And then, at the end of pathwalk, it will call the actual operation: ->mkdir() ->mknod() ->getattr() ->open() ... which may need to go and get the token again. However, it is very likely that all of the operations on a single dentry/inode - and quite possibly a sequence of them - will all want to use the same authentication token, which suggests that caching it would be a good idea. To this end: (1) Make it so that a positive result of request_key() and co. that didn't require upcalling to userspace is cached temporarily in task_struct. (2) The cache is 1 deep, so a new result displaces the old one. (3) The key is released by exit and by notify-resume. (4) The cache is cleared in a newly forked process. Signed-off-by: David Howells <[email protected]>
1 parent 896f195 commit 7743c48

File tree

6 files changed

+82
-1
lines changed

6 files changed

+82
-1
lines changed

Documentation/security/keys/request-key.rst

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,9 @@ The process stops immediately a valid key is found with permission granted to
176176
use it. Any error from a previous match attempt is discarded and the key is
177177
returned.
178178

179+
When request_key() is invoked, if CONFIG_KEYS_REQUEST_CACHE=y, a per-task
180+
one-key cache is first checked for a match.
181+
179182
When search_process_keyrings() is invoked, it performs the following searches
180183
until one succeeds:
181184

@@ -195,7 +198,9 @@ until one succeeds:
195198
c) The calling process's session keyring is searched.
196199

197200
The moment one succeeds, all pending errors are discarded and the found key is
198-
returned.
201+
returned. If CONFIG_KEYS_REQUEST_CACHE=y, then that key is placed in the
202+
per-task cache, displacing the previous key. The cache is cleared on exit or
203+
just prior to resumption of userspace.
199204

200205
Only if all these fail does the whole thing fail with the highest priority
201206
error. Note that several errors may have come from LSM.

include/linux/sched.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -831,6 +831,11 @@ struct task_struct {
831831
/* Effective (overridable) subjective task credentials (COW): */
832832
const struct cred __rcu *cred;
833833

834+
#ifdef CONFIG_KEYS
835+
/* Cached requested key. */
836+
struct key *cached_requested_key;
837+
#endif
838+
834839
/*
835840
* executable name, excluding path.
836841
*

include/linux/tracehook.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,13 @@ static inline void tracehook_notify_resume(struct pt_regs *regs)
187187
if (unlikely(current->task_works))
188188
task_work_run();
189189

190+
#ifdef CONFIG_KEYS_REQUEST_CACHE
191+
if (unlikely(current->cached_requested_key)) {
192+
key_put(current->cached_requested_key);
193+
current->cached_requested_key = NULL;
194+
}
195+
#endif
196+
190197
mem_cgroup_handle_over_high();
191198
blkcg_maybe_throttle_current();
192199
}

kernel/cred.c

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,11 @@ void exit_creds(struct task_struct *tsk)
174174
validate_creds(cred);
175175
alter_cred_subscribers(cred, -1);
176176
put_cred(cred);
177+
178+
#ifdef CONFIG_KEYS_REQUEST_CACHE
179+
key_put(current->cached_requested_key);
180+
current->cached_requested_key = NULL;
181+
#endif
177182
}
178183

179184
/**
@@ -327,6 +332,10 @@ int copy_creds(struct task_struct *p, unsigned long clone_flags)
327332
struct cred *new;
328333
int ret;
329334

335+
#ifdef CONFIG_KEYS_REQUEST_CACHE
336+
p->cached_requested_key = NULL;
337+
#endif
338+
330339
if (
331340
#ifdef CONFIG_KEYS
332341
!p->cred->thread_keyring &&

security/keys/Kconfig

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,24 @@ config KEYS_COMPAT
2424
def_bool y
2525
depends on COMPAT && KEYS
2626

27+
config KEYS_REQUEST_CACHE
28+
bool "Enable temporary caching of the last request_key() result"
29+
depends on KEYS
30+
help
31+
This option causes the result of the last successful request_key()
32+
call that didn't upcall to the kernel to be cached temporarily in the
33+
task_struct. The cache is cleared by exit and just prior to the
34+
resumption of userspace.
35+
36+
This allows the key used for multiple step processes where each step
37+
wants to request a key that is likely the same as the one requested
38+
by the last step to save on the searching.
39+
40+
An example of such a process is a pathwalk through a network
41+
filesystem in which each method needs to request an authentication
42+
key. Pathwalk will call multiple methods for each dentry traversed
43+
(permission, d_revalidate, lookup, getxattr, getacl, ...).
44+
2745
config PERSISTENT_KEYRINGS
2846
bool "Enable register of persistent per-UID keyrings"
2947
depends on KEYS

security/keys/request_key.c

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,31 @@
2222

2323
#define key_negative_timeout 60 /* default timeout on a negative key's existence */
2424

25+
static struct key *check_cached_key(struct keyring_search_context *ctx)
26+
{
27+
#ifdef CONFIG_KEYS_REQUEST_CACHE
28+
struct key *key = current->cached_requested_key;
29+
30+
if (key &&
31+
ctx->match_data.cmp(key, &ctx->match_data) &&
32+
!(key->flags & ((1 << KEY_FLAG_INVALIDATED) |
33+
(1 << KEY_FLAG_REVOKED))))
34+
return key_get(key);
35+
#endif
36+
return NULL;
37+
}
38+
39+
static void cache_requested_key(struct key *key)
40+
{
41+
#ifdef CONFIG_KEYS_REQUEST_CACHE
42+
struct task_struct *t = current;
43+
44+
key_put(t->cached_requested_key);
45+
t->cached_requested_key = key_get(key);
46+
set_tsk_thread_flag(t, TIF_NOTIFY_RESUME);
47+
#endif
48+
}
49+
2550
/**
2651
* complete_request_key - Complete the construction of a key.
2752
* @authkey: The authorisation key.
@@ -562,6 +587,10 @@ struct key *request_key_and_link(struct key_type *type,
562587
}
563588
}
564589

590+
key = check_cached_key(&ctx);
591+
if (key)
592+
return key;
593+
565594
/* search all the process keyrings for a key */
566595
rcu_read_lock();
567596
key_ref = search_process_keyrings_rcu(&ctx);
@@ -587,6 +616,9 @@ struct key *request_key_and_link(struct key_type *type,
587616
goto error_free;
588617
}
589618
}
619+
620+
/* Only cache the key on immediate success */
621+
cache_requested_key(key);
590622
} else if (PTR_ERR(key_ref) != -EAGAIN) {
591623
key = ERR_CAST(key_ref);
592624
} else {
@@ -786,6 +818,10 @@ struct key *request_key_rcu(struct key_type *type, const char *description)
786818

787819
kenter("%s,%s", type->name, description);
788820

821+
key = check_cached_key(&ctx);
822+
if (key)
823+
return key;
824+
789825
/* search all the process keyrings for a key */
790826
key_ref = search_process_keyrings_rcu(&ctx);
791827
if (IS_ERR(key_ref)) {
@@ -794,6 +830,7 @@ struct key *request_key_rcu(struct key_type *type, const char *description)
794830
key = ERR_PTR(-ENOKEY);
795831
} else {
796832
key = key_ref_to_ptr(key_ref);
833+
cache_requested_key(key);
797834
}
798835

799836
kleave(" = %p", key);

0 commit comments

Comments
 (0)