Skip to content

Commit 8409cca

Browse files
hallyntorvalds
authored andcommitted
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 <[email protected]> Cc: "Eric W. Biederman" <[email protected]> Acked-by: Daniel Lezcano <[email protected]> Acked-by: David Howells <[email protected]> Cc: James Morris <[email protected]> Signed-off-by: Andrew Morton <[email protected]> Signed-off-by: Linus Torvalds <[email protected]>
1 parent 39fd339 commit 8409cca

3 files changed

Lines changed: 49 additions & 20 deletions

File tree

include/linux/capability.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -553,6 +553,8 @@ static inline kernel_cap_t cap_raise_nfsd_set(const kernel_cap_t a,
553553
*/
554554
#define has_capability(t, cap) (security_real_capable((t), &init_user_ns, (cap)) == 0)
555555

556+
#define has_ns_capability(t, ns, cap) (security_real_capable((t), (ns), (cap)) == 0)
557+
556558
/**
557559
* has_capability_noaudit - Determine if a task has a superior capability available (unaudited)
558560
* @t: The task in question

kernel/ptrace.c

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -134,21 +134,24 @@ int __ptrace_may_access(struct task_struct *task, unsigned int mode)
134134
return 0;
135135
rcu_read_lock();
136136
tcred = __task_cred(task);
137-
if ((cred->uid != tcred->euid ||
138-
cred->uid != tcred->suid ||
139-
cred->uid != tcred->uid ||
140-
cred->gid != tcred->egid ||
141-
cred->gid != tcred->sgid ||
142-
cred->gid != tcred->gid) &&
143-
!capable(CAP_SYS_PTRACE)) {
144-
rcu_read_unlock();
145-
return -EPERM;
146-
}
137+
if (cred->user->user_ns == tcred->user->user_ns &&
138+
(cred->uid == tcred->euid &&
139+
cred->uid == tcred->suid &&
140+
cred->uid == tcred->uid &&
141+
cred->gid == tcred->egid &&
142+
cred->gid == tcred->sgid &&
143+
cred->gid == tcred->gid))
144+
goto ok;
145+
if (ns_capable(tcred->user->user_ns, CAP_SYS_PTRACE))
146+
goto ok;
147+
rcu_read_unlock();
148+
return -EPERM;
149+
ok:
147150
rcu_read_unlock();
148151
smp_rmb();
149152
if (task->mm)
150153
dumpable = get_dumpable(task->mm);
151-
if (!dumpable && !capable(CAP_SYS_PTRACE))
154+
if (!dumpable && !task_ns_capable(task, CAP_SYS_PTRACE))
152155
return -EPERM;
153156

154157
return security_ptrace_access_check(task, mode);
@@ -198,7 +201,7 @@ static int ptrace_attach(struct task_struct *task)
198201
goto unlock_tasklist;
199202

200203
task->ptrace = PT_PTRACED;
201-
if (capable(CAP_SYS_PTRACE))
204+
if (task_ns_capable(task, CAP_SYS_PTRACE))
202205
task->ptrace |= PT_PTRACE_CAP;
203206

204207
__ptrace_link(task, current);

security/commoncap.c

Lines changed: 32 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -127,18 +127,30 @@ int cap_settime(const struct timespec *ts, const struct timezone *tz)
127127
* @child: The process to be accessed
128128
* @mode: The mode of attachment.
129129
*
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+
*
130136
* Determine whether a process may access another, returning 0 if permission
131137
* granted, -ve if denied.
132138
*/
133139
int cap_ptrace_access_check(struct task_struct *child, unsigned int mode)
134140
{
135141
int ret = 0;
142+
const struct cred *cred, *child_cred;
136143

137144
rcu_read_lock();
138-
if (!cap_issubset(__task_cred(child)->cap_permitted,
139-
current_cred()->cap_permitted) &&
140-
!capable(CAP_SYS_PTRACE))
141-
ret = -EPERM;
145+
cred = current_cred();
146+
child_cred = __task_cred(child);
147+
if (cred->user->user_ns == child_cred->user->user_ns &&
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;
153+
out:
142154
rcu_read_unlock();
143155
return ret;
144156
}
@@ -147,18 +159,30 @@ int cap_ptrace_access_check(struct task_struct *child, unsigned int mode)
147159
* cap_ptrace_traceme - Determine whether another process may trace the current
148160
* @parent: The task proposed to be the tracer
149161
*
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+
*
150168
* Determine whether the nominated task is permitted to trace the current
151169
* process, returning 0 if permission is granted, -ve if denied.
152170
*/
153171
int cap_ptrace_traceme(struct task_struct *parent)
154172
{
155173
int ret = 0;
174+
const struct cred *cred, *child_cred;
156175

157176
rcu_read_lock();
158-
if (!cap_issubset(current_cred()->cap_permitted,
159-
__task_cred(parent)->cap_permitted) &&
160-
!has_capability(parent, CAP_SYS_PTRACE))
161-
ret = -EPERM;
177+
cred = __task_cred(parent);
178+
child_cred = current_cred();
179+
if (cred->user->user_ns == child_cred->user->user_ns &&
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;
185+
out:
162186
rcu_read_unlock();
163187
return ret;
164188
}

0 commit comments

Comments
 (0)