aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSerge E. Hallyn <serge@hallyn.com>2011-03-23 19:43:20 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2011-03-23 22:47:05 -0400
commit8409cca7056113bee3236cb6a8e4d8d4d1eef102 (patch)
treed9f1ced0d47070fcdf8b399021f33770c150b1ec
parent39fd33933b0209e4b6254743f2cede07c5ad4c52 (diff)
userns: allow ptrace from non-init user namespaces
ptrace is allowed to tasks in the same user namespace according to the usual rules (i.e. the same rules as for two tasks in the init user namespace). ptrace is also allowed to a user namespace to which the current task the has CAP_SYS_PTRACE capability. Changelog: Dec 31: Address feedback by Eric: . Correct ptrace uid check . Rename may_ptrace_ns to ptrace_capable . Also fix the cap_ptrace checks. Jan 1: Use const cred struct Jan 11: use task_ns_capable() in place of ptrace_capable(). Feb 23: same_or_ancestore_user_ns() was not an appropriate check to constrain cap_issubset. Rather, cap_issubset() only is meaningful when both capsets are in the same user_ns. Signed-off-by: Serge E. Hallyn <serge.hallyn@canonical.com> Cc: "Eric W. Biederman" <ebiederm@xmission.com> Acked-by: Daniel Lezcano <daniel.lezcano@free.fr> Acked-by: David Howells <dhowells@redhat.com> Cc: James Morris <jmorris@namei.org> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
-rw-r--r--include/linux/capability.h2
-rw-r--r--kernel/ptrace.c27
-rw-r--r--security/commoncap.c40
3 files changed, 49 insertions, 20 deletions
diff --git a/include/linux/capability.h b/include/linux/capability.h
index 7c9c82903012..2ec4a8cc86a5 100644
--- a/include/linux/capability.h
+++ b/include/linux/capability.h
@@ -553,6 +553,8 @@ static inline kernel_cap_t cap_raise_nfsd_set(const kernel_cap_t a,
553 */ 553 */
554#define has_capability(t, cap) (security_real_capable((t), &init_user_ns, (cap)) == 0) 554#define has_capability(t, cap) (security_real_capable((t), &init_user_ns, (cap)) == 0)
555 555
556#define has_ns_capability(t, ns, cap) (security_real_capable((t), (ns), (cap)) == 0)
557
556/** 558/**
557 * has_capability_noaudit - Determine if a task has a superior capability available (unaudited) 559 * has_capability_noaudit - Determine if a task has a superior capability available (unaudited)
558 * @t: The task in question 560 * @t: The task in question
diff --git a/kernel/ptrace.c b/kernel/ptrace.c
index e2302e40b360..0fc1eed28d27 100644
--- a/kernel/ptrace.c
+++ b/kernel/ptrace.c
@@ -134,21 +134,24 @@ int __ptrace_may_access(struct task_struct *task, unsigned int mode)
134 return 0; 134 return 0;
135 rcu_read_lock(); 135 rcu_read_lock();
136 tcred = __task_cred(task); 136 tcred = __task_cred(task);
137 if ((cred->uid != tcred->euid || 137 if (cred->user->user_ns == tcred->user->user_ns &&
138 cred->uid != tcred->suid || 138 (cred->uid == tcred->euid &&
139 cred->uid != tcred->uid || 139 cred->uid == tcred->suid &&
140 cred->gid != tcred->egid || 140 cred->uid == tcred->uid &&
141 cred->gid != tcred->sgid || 141 cred->gid == tcred->egid &&
142 cred->gid != tcred->gid) && 142 cred->gid == tcred->sgid &&
143 !capable(CAP_SYS_PTRACE)) { 143 cred->gid == tcred->gid))
144 rcu_read_unlock(); 144 goto ok;
145 return -EPERM; 145 if (ns_capable(tcred->user->user_ns, CAP_SYS_PTRACE))
146 } 146 goto ok;
147 rcu_read_unlock();
148 return -EPERM;
149ok:
147 rcu_read_unlock(); 150 rcu_read_unlock();
148 smp_rmb(); 151 smp_rmb();
149 if (task->mm) 152 if (task->mm)
150 dumpable = get_dumpable(task->mm); 153 dumpable = get_dumpable(task->mm);
151 if (!dumpable && !capable(CAP_SYS_PTRACE)) 154 if (!dumpable && !task_ns_capable(task, CAP_SYS_PTRACE))
152 return -EPERM; 155 return -EPERM;
153 156
154 return security_ptrace_access_check(task, mode); 157 return security_ptrace_access_check(task, mode);
@@ -198,7 +201,7 @@ static int ptrace_attach(struct task_struct *task)
198 goto unlock_tasklist; 201 goto unlock_tasklist;
199 202
200 task->ptrace = PT_PTRACED; 203 task->ptrace = PT_PTRACED;
201 if (capable(CAP_SYS_PTRACE)) 204 if (task_ns_capable(task, CAP_SYS_PTRACE))
202 task->ptrace |= PT_PTRACE_CAP; 205 task->ptrace |= PT_PTRACE_CAP;
203 206
204 __ptrace_link(task, current); 207 __ptrace_link(task, current);
diff --git a/security/commoncap.c b/security/commoncap.c
index 43a205bc7d7c..f20e984ccfb4 100644
--- a/security/commoncap.c
+++ b/security/commoncap.c
@@ -127,18 +127,30 @@ int cap_settime(const struct timespec *ts, const struct timezone *tz)
127 * @child: The process to be accessed 127 * @child: The process to be accessed
128 * @mode: The mode of attachment. 128 * @mode: The mode of attachment.
129 * 129 *
130 * If we are in the same or an ancestor user_ns and have all the target
131 * task's capabilities, then ptrace access is allowed.
132 * If we have the ptrace capability to the target user_ns, then ptrace
133 * access is allowed.
134 * Else denied.
135 *
130 * Determine whether a process may access another, returning 0 if permission 136 * Determine whether a process may access another, returning 0 if permission
131 * granted, -ve if denied. 137 * granted, -ve if denied.
132 */ 138 */
133int cap_ptrace_access_check(struct task_struct *child, unsigned int mode) 139int cap_ptrace_access_check(struct task_struct *child, unsigned int mode)
134{ 140{
135 int ret = 0; 141 int ret = 0;
142 const struct cred *cred, *child_cred;
136 143
137 rcu_read_lock(); 144 rcu_read_lock();
138 if (!cap_issubset(__task_cred(child)->cap_permitted, 145 cred = current_cred();
139 current_cred()->cap_permitted) && 146 child_cred = __task_cred(child);
140 !capable(CAP_SYS_PTRACE)) 147 if (cred->user->user_ns == child_cred->user->user_ns &&
141 ret = -EPERM; 148 cap_issubset(child_cred->cap_permitted, cred->cap_permitted))
149 goto out;
150 if (ns_capable(child_cred->user->user_ns, CAP_SYS_PTRACE))
151 goto out;
152 ret = -EPERM;
153out:
142 rcu_read_unlock(); 154 rcu_read_unlock();
143 return ret; 155 return ret;
144} 156}
@@ -147,18 +159,30 @@ int cap_ptrace_access_check(struct task_struct *child, unsigned int mode)
147 * cap_ptrace_traceme - Determine whether another process may trace the current 159 * cap_ptrace_traceme - Determine whether another process may trace the current
148 * @parent: The task proposed to be the tracer 160 * @parent: The task proposed to be the tracer
149 * 161 *
162 * If parent is in the same or an ancestor user_ns and has all current's
163 * capabilities, then ptrace access is allowed.
164 * If parent has the ptrace capability to current's user_ns, then ptrace
165 * access is allowed.
166 * Else denied.
167 *
150 * Determine whether the nominated task is permitted to trace the current 168 * Determine whether the nominated task is permitted to trace the current
151 * process, returning 0 if permission is granted, -ve if denied. 169 * process, returning 0 if permission is granted, -ve if denied.
152 */ 170 */
153int cap_ptrace_traceme(struct task_struct *parent) 171int cap_ptrace_traceme(struct task_struct *parent)
154{ 172{
155 int ret = 0; 173 int ret = 0;
174 const struct cred *cred, *child_cred;
156 175
157 rcu_read_lock(); 176 rcu_read_lock();
158 if (!cap_issubset(current_cred()->cap_permitted, 177 cred = __task_cred(parent);
159 __task_cred(parent)->cap_permitted) && 178 child_cred = current_cred();
160 !has_capability(parent, CAP_SYS_PTRACE)) 179 if (cred->user->user_ns == child_cred->user->user_ns &&
161 ret = -EPERM; 180 cap_issubset(child_cred->cap_permitted, cred->cap_permitted))
181 goto out;
182 if (has_ns_capability(parent, child_cred->user->user_ns, CAP_SYS_PTRACE))
183 goto out;
184 ret = -EPERM;
185out:
162 rcu_read_unlock(); 186 rcu_read_unlock();
163 return ret; 187 return ret;
164} 188}