]>
Commit | Line | Data |
---|---|---|
e96a76db TL |
1 | From: Jann Horn <jann@thejh.net> |
2 | Date: Sat, 26 Dec 2015 03:52:31 +0100 | |
3 | Subject: [PATCH] ptrace: being capable wrt a process requires mapped uids/gids | |
4 | ||
5 | ptrace_has_cap() checks whether the current process should be | |
6 | treated as having a certain capability for ptrace checks | |
7 | against another process. Until now, this was equivalent to | |
8 | has_ns_capability(current, target_ns, CAP_SYS_PTRACE). | |
9 | ||
10 | However, if a root-owned process wants to enter a user | |
11 | namespace for some reason without knowing who owns it and | |
12 | therefore can't change to the namespace owner's uid and gid | |
13 | before entering, as soon as it has entered the namespace, | |
14 | the namespace owner can attach to it via ptrace and thereby | |
15 | gain access to its uid and gid. | |
16 | ||
17 | While it is possible for the entering process to switch to | |
18 | the uid of a claimed namespace owner before entering, | |
19 | causing the attempt to enter to fail if the claimed uid is | |
20 | wrong, this doesn't solve the problem of determining an | |
21 | appropriate gid. | |
22 | ||
23 | With this change, the entering process can first enter the | |
24 | namespace and then safely inspect the namespace's | |
25 | properties, e.g. through /proc/self/{uid_map,gid_map}, | |
26 | assuming that the namespace owner doesn't have access to | |
27 | uid 0. | |
28 | ||
29 | Changed in v2: The caller needs to be capable in the | |
30 | namespace into which tcred's uids/gids can be mapped. | |
31 | ||
32 | Signed-off-by: Jann Horn <jann@thejh.net> | |
33 | Acked-by: Serge Hallyn <serge.hallyn@ubuntu.com> | |
34 | Acked-by: Andy Lutomirski <luto@kernel.org> | |
35 | --- | |
36 | kernel/ptrace.c | 33 ++++++++++++++++++++++++++++----- | |
37 | 1 file changed, 28 insertions(+), 5 deletions(-) | |
38 | ||
39 | diff --git a/kernel/ptrace.c b/kernel/ptrace.c | |
40 | index b760bae..260a08d 100644 | |
41 | --- a/kernel/ptrace.c | |
42 | +++ b/kernel/ptrace.c | |
43 | @@ -20,6 +20,7 @@ | |
44 | #include <linux/uio.h> | |
45 | #include <linux/audit.h> | |
46 | #include <linux/pid_namespace.h> | |
47 | +#include <linux/user_namespace.h> | |
48 | #include <linux/syscalls.h> | |
49 | #include <linux/uaccess.h> | |
50 | #include <linux/regset.h> | |
51 | @@ -207,12 +208,34 @@ static int ptrace_check_attach(struct task_struct *child, bool ignore_state) | |
52 | return ret; | |
53 | } | |
54 | ||
55 | -static int ptrace_has_cap(struct user_namespace *ns, unsigned int mode) | |
56 | +static bool ptrace_has_cap(const struct cred *tcred, unsigned int mode) | |
57 | { | |
58 | + struct user_namespace *tns = tcred->user_ns; | |
59 | + | |
60 | + /* When a root-owned process enters a user namespace created by a | |
61 | + * malicious user, the user shouldn't be able to execute code under | |
62 | + * uid 0 by attaching to the root-owned process via ptrace. | |
63 | + * Therefore, similar to the capable_wrt_inode_uidgid() check, | |
64 | + * verify that all the uids and gids of the target process are | |
65 | + * mapped into a namespace below the current one in which the caller | |
66 | + * is capable. | |
67 | + * No fsuid/fsgid check because __ptrace_may_access doesn't do it | |
68 | + * either. | |
69 | + */ | |
70 | + while ( | |
71 | + !kuid_has_mapping(tns, tcred->euid) || | |
72 | + !kuid_has_mapping(tns, tcred->suid) || | |
73 | + !kuid_has_mapping(tns, tcred->uid) || | |
74 | + !kgid_has_mapping(tns, tcred->egid) || | |
75 | + !kgid_has_mapping(tns, tcred->sgid) || | |
76 | + !kgid_has_mapping(tns, tcred->gid)) { | |
77 | + tns = tns->parent; | |
78 | + } | |
79 | + | |
80 | if (mode & PTRACE_MODE_NOAUDIT) | |
81 | - return has_ns_capability_noaudit(current, ns, CAP_SYS_PTRACE); | |
82 | + return has_ns_capability_noaudit(current, tns, CAP_SYS_PTRACE); | |
83 | else | |
84 | - return has_ns_capability(current, ns, CAP_SYS_PTRACE); | |
85 | + return has_ns_capability(current, tns, CAP_SYS_PTRACE); | |
86 | } | |
87 | ||
88 | /* Returns 0 on success, -errno on denial. */ | |
89 | @@ -241,7 +264,7 @@ static int __ptrace_may_access(struct task_struct *task, unsigned int mode) | |
90 | gid_eq(cred->gid, tcred->sgid) && | |
91 | gid_eq(cred->gid, tcred->gid)) | |
92 | goto ok; | |
93 | - if (ptrace_has_cap(tcred->user_ns, mode)) | |
94 | + if (ptrace_has_cap(tcred, mode)) | |
95 | goto ok; | |
96 | rcu_read_unlock(); | |
97 | return -EPERM; | |
98 | @@ -252,7 +275,7 @@ ok: | |
99 | dumpable = get_dumpable(task->mm); | |
100 | rcu_read_lock(); | |
101 | if (dumpable != SUID_DUMP_USER && | |
102 | - !ptrace_has_cap(__task_cred(task)->user_ns, mode)) { | |
103 | + !ptrace_has_cap(__task_cred(task), mode)) { | |
104 | rcu_read_unlock(); | |
105 | return -EPERM; | |
106 | } | |
107 | -- | |
108 | 2.1.4 |