diff options
| author | David Howells <dhowells@redhat.com> | 2011-06-17 06:25:59 -0400 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2011-06-17 12:40:48 -0400 |
| commit | 879669961b11e7f40b518784863a259f735a72bf (patch) | |
| tree | 9bff5392e365caf656c9dd9be38f7471c182278c | |
| parent | eb96c925152fc289311e5d7e956b919e9b60ab53 (diff) | |
KEYS/DNS: Fix ____call_usermodehelper() to not lose the session keyring
____call_usermodehelper() now erases any credentials set by the
subprocess_inf::init() function. The problem is that commit
17f60a7da150 ("capabilites: allow the application of capability limits
to usermode helpers") creates and commits new credentials with
prepare_kernel_cred() after the call to the init() function. This wipes
all keyrings after umh_keys_init() is called.
The best way to deal with this is to put the init() call just prior to
the commit_creds() call, and pass the cred pointer to init(). That
means that umh_keys_init() and suchlike can modify the credentials
_before_ they are published and potentially in use by the rest of the
system.
This prevents request_key() from working as it is prevented from passing
the session keyring it set up with the authorisation token to
/sbin/request-key, and so the latter can't assume the authority to
instantiate the key. This causes the in-kernel DNS resolver to fail
with ENOKEY unconditionally.
Signed-off-by: David Howells <dhowells@redhat.com>
Acked-by: Eric Paris <eparis@redhat.com>
Tested-by: Jeff Layton <jlayton@redhat.com>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
| -rw-r--r-- | fs/exec.c | 2 | ||||
| -rw-r--r-- | include/linux/kmod.h | 8 | ||||
| -rw-r--r-- | kernel/kmod.c | 16 | ||||
| -rw-r--r-- | security/keys/request_key.c | 3 |
4 files changed, 15 insertions, 14 deletions
| @@ -1996,7 +1996,7 @@ static void wait_for_dump_helpers(struct file *file) | |||
| 1996 | * is a special value that we use to trap recursive | 1996 | * is a special value that we use to trap recursive |
| 1997 | * core dumps | 1997 | * core dumps |
| 1998 | */ | 1998 | */ |
| 1999 | static int umh_pipe_setup(struct subprocess_info *info) | 1999 | static int umh_pipe_setup(struct subprocess_info *info, struct cred *new) |
| 2000 | { | 2000 | { |
| 2001 | struct file *rp, *wp; | 2001 | struct file *rp, *wp; |
| 2002 | struct fdtable *fdt; | 2002 | struct fdtable *fdt; |
diff --git a/include/linux/kmod.h b/include/linux/kmod.h index d4a5c84c503d..0da38cf7db7b 100644 --- a/include/linux/kmod.h +++ b/include/linux/kmod.h | |||
| @@ -45,7 +45,7 @@ static inline int request_module_nowait(const char *name, ...) { return -ENOSYS; | |||
| 45 | #endif | 45 | #endif |
| 46 | 46 | ||
| 47 | 47 | ||
| 48 | struct key; | 48 | struct cred; |
| 49 | struct file; | 49 | struct file; |
| 50 | 50 | ||
| 51 | enum umh_wait { | 51 | enum umh_wait { |
| @@ -62,7 +62,7 @@ struct subprocess_info { | |||
| 62 | char **envp; | 62 | char **envp; |
| 63 | enum umh_wait wait; | 63 | enum umh_wait wait; |
| 64 | int retval; | 64 | int retval; |
| 65 | int (*init)(struct subprocess_info *info); | 65 | int (*init)(struct subprocess_info *info, struct cred *new); |
| 66 | void (*cleanup)(struct subprocess_info *info); | 66 | void (*cleanup)(struct subprocess_info *info); |
| 67 | void *data; | 67 | void *data; |
| 68 | }; | 68 | }; |
| @@ -73,7 +73,7 @@ struct subprocess_info *call_usermodehelper_setup(char *path, char **argv, | |||
| 73 | 73 | ||
| 74 | /* Set various pieces of state into the subprocess_info structure */ | 74 | /* Set various pieces of state into the subprocess_info structure */ |
| 75 | void call_usermodehelper_setfns(struct subprocess_info *info, | 75 | void call_usermodehelper_setfns(struct subprocess_info *info, |
| 76 | int (*init)(struct subprocess_info *info), | 76 | int (*init)(struct subprocess_info *info, struct cred *new), |
| 77 | void (*cleanup)(struct subprocess_info *info), | 77 | void (*cleanup)(struct subprocess_info *info), |
| 78 | void *data); | 78 | void *data); |
| 79 | 79 | ||
| @@ -87,7 +87,7 @@ void call_usermodehelper_freeinfo(struct subprocess_info *info); | |||
| 87 | static inline int | 87 | static inline int |
| 88 | call_usermodehelper_fns(char *path, char **argv, char **envp, | 88 | call_usermodehelper_fns(char *path, char **argv, char **envp, |
| 89 | enum umh_wait wait, | 89 | enum umh_wait wait, |
| 90 | int (*init)(struct subprocess_info *info), | 90 | int (*init)(struct subprocess_info *info, struct cred *new), |
| 91 | void (*cleanup)(struct subprocess_info *), void *data) | 91 | void (*cleanup)(struct subprocess_info *), void *data) |
| 92 | { | 92 | { |
| 93 | struct subprocess_info *info; | 93 | struct subprocess_info *info; |
diff --git a/kernel/kmod.c b/kernel/kmod.c index ad6a81c58b44..47613dfb7b28 100644 --- a/kernel/kmod.c +++ b/kernel/kmod.c | |||
| @@ -156,12 +156,6 @@ static int ____call_usermodehelper(void *data) | |||
| 156 | */ | 156 | */ |
| 157 | set_user_nice(current, 0); | 157 | set_user_nice(current, 0); |
| 158 | 158 | ||
| 159 | if (sub_info->init) { | ||
| 160 | retval = sub_info->init(sub_info); | ||
| 161 | if (retval) | ||
| 162 | goto fail; | ||
| 163 | } | ||
| 164 | |||
| 165 | retval = -ENOMEM; | 159 | retval = -ENOMEM; |
| 166 | new = prepare_kernel_cred(current); | 160 | new = prepare_kernel_cred(current); |
| 167 | if (!new) | 161 | if (!new) |
| @@ -173,6 +167,14 @@ static int ____call_usermodehelper(void *data) | |||
| 173 | new->cap_inheritable); | 167 | new->cap_inheritable); |
| 174 | spin_unlock(&umh_sysctl_lock); | 168 | spin_unlock(&umh_sysctl_lock); |
| 175 | 169 | ||
| 170 | if (sub_info->init) { | ||
| 171 | retval = sub_info->init(sub_info, new); | ||
| 172 | if (retval) { | ||
| 173 | abort_creds(new); | ||
| 174 | goto fail; | ||
| 175 | } | ||
| 176 | } | ||
| 177 | |||
| 176 | commit_creds(new); | 178 | commit_creds(new); |
| 177 | 179 | ||
| 178 | retval = kernel_execve(sub_info->path, | 180 | retval = kernel_execve(sub_info->path, |
| @@ -388,7 +390,7 @@ EXPORT_SYMBOL(call_usermodehelper_setup); | |||
| 388 | * context in which call_usermodehelper_exec is called. | 390 | * context in which call_usermodehelper_exec is called. |
| 389 | */ | 391 | */ |
| 390 | void call_usermodehelper_setfns(struct subprocess_info *info, | 392 | void call_usermodehelper_setfns(struct subprocess_info *info, |
| 391 | int (*init)(struct subprocess_info *info), | 393 | int (*init)(struct subprocess_info *info, struct cred *new), |
| 392 | void (*cleanup)(struct subprocess_info *info), | 394 | void (*cleanup)(struct subprocess_info *info), |
| 393 | void *data) | 395 | void *data) |
| 394 | { | 396 | { |
diff --git a/security/keys/request_key.c b/security/keys/request_key.c index d31862e0aa1c..8e319a416eec 100644 --- a/security/keys/request_key.c +++ b/security/keys/request_key.c | |||
| @@ -71,9 +71,8 @@ EXPORT_SYMBOL(complete_request_key); | |||
| 71 | * This is called in context of freshly forked kthread before kernel_execve(), | 71 | * This is called in context of freshly forked kthread before kernel_execve(), |
| 72 | * so we can simply install the desired session_keyring at this point. | 72 | * so we can simply install the desired session_keyring at this point. |
| 73 | */ | 73 | */ |
| 74 | static int umh_keys_init(struct subprocess_info *info) | 74 | static int umh_keys_init(struct subprocess_info *info, struct cred *cred) |
| 75 | { | 75 | { |
| 76 | struct cred *cred = (struct cred*)current_cred(); | ||
| 77 | struct key *keyring = info->data; | 76 | struct key *keyring = info->data; |
| 78 | 77 | ||
| 79 | return install_session_keyring_to_cred(cred, keyring); | 78 | return install_session_keyring_to_cred(cred, keyring); |
