Skip to content

Commit 64b875f

Browse files
committed
ptrace: Capture the ptracer's creds not PT_PTRACE_CAP
When the flag PT_PTRACE_CAP was added the PTRACE_TRACEME path was overlooked. This can result in incorrect behavior when an application like strace traces an exec of a setuid executable. Further PT_PTRACE_CAP does not have enough information for making good security decisions as it does not report which user namespace the capability is in. This has already allowed one mistake through insufficient granulariy. I found this issue when I was testing another corner case of exec and discovered that I could not get strace to set PT_PTRACE_CAP even when running strace as root with a full set of caps. This change fixes the above issue with strace allowing stracing as root a setuid executable without disabling setuid. More fundamentaly this change allows what is allowable at all times, by using the correct information in it's decision. Cc: [email protected] Fixes: 4214e42f96d4 ("v2.4.9.11 -> v2.4.9.12") Signed-off-by: "Eric W. Biederman" <[email protected]>
1 parent bfedb58 commit 64b875f

File tree

6 files changed

+30
-7
lines changed

6 files changed

+30
-7
lines changed

fs/exec.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1406,7 +1406,7 @@ static void check_unsafe_exec(struct linux_binprm *bprm)
14061406
unsigned n_fs;
14071407

14081408
if (p->ptrace) {
1409-
if (p->ptrace & PT_PTRACE_CAP)
1409+
if (ptracer_capable(p, current_user_ns()))
14101410
bprm->unsafe |= LSM_UNSAFE_PTRACE_CAP;
14111411
else
14121412
bprm->unsafe |= LSM_UNSAFE_PTRACE;

include/linux/capability.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -242,6 +242,7 @@ static inline bool ns_capable_noaudit(struct user_namespace *ns, int cap)
242242
#endif /* CONFIG_MULTIUSER */
243243
extern bool capable_wrt_inode_uidgid(const struct inode *inode, int cap);
244244
extern bool file_ns_capable(const struct file *file, struct user_namespace *ns, int cap);
245+
extern bool ptracer_capable(struct task_struct *tsk, struct user_namespace *ns);
245246

246247
/* audit system wants to get cap info from files as well */
247248
extern int get_vfs_caps_from_disk(const struct dentry *dentry, struct cpu_vfs_cap_data *cpu_caps);

include/linux/ptrace.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@
1919
#define PT_SEIZED 0x00010000 /* SEIZE used, enable new behavior */
2020
#define PT_PTRACED 0x00000001
2121
#define PT_DTRACE 0x00000002 /* delayed trace (used on m68k, i386) */
22-
#define PT_PTRACE_CAP 0x00000004 /* ptracer can follow suid-exec */
2322

2423
#define PT_OPT_FLAG_SHIFT 3
2524
/* PT_TRACE_* event enable flags */

include/linux/sched.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1656,6 +1656,7 @@ struct task_struct {
16561656
struct list_head cpu_timers[3];
16571657

16581658
/* process credentials */
1659+
const struct cred __rcu *ptracer_cred; /* Tracer's credentials at attach */
16591660
const struct cred __rcu *real_cred; /* objective and real subjective task
16601661
* credentials (COW) */
16611662
const struct cred __rcu *cred; /* effective (overridable) subjective task

kernel/capability.c

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -473,3 +473,23 @@ bool capable_wrt_inode_uidgid(const struct inode *inode, int cap)
473473
kgid_has_mapping(ns, inode->i_gid);
474474
}
475475
EXPORT_SYMBOL(capable_wrt_inode_uidgid);
476+
477+
/**
478+
* ptracer_capable - Determine if the ptracer holds CAP_SYS_PTRACE in the namespace
479+
* @tsk: The task that may be ptraced
480+
* @ns: The user namespace to search for CAP_SYS_PTRACE in
481+
*
482+
* Return true if the task that is ptracing the current task had CAP_SYS_PTRACE
483+
* in the specified user namespace.
484+
*/
485+
bool ptracer_capable(struct task_struct *tsk, struct user_namespace *ns)
486+
{
487+
int ret = 0; /* An absent tracer adds no restrictions */
488+
const struct cred *cred;
489+
rcu_read_lock();
490+
cred = rcu_dereference(tsk->ptracer_cred);
491+
if (cred)
492+
ret = security_capable_noaudit(cred, ns, CAP_SYS_PTRACE);
493+
rcu_read_unlock();
494+
return (ret == 0);
495+
}

kernel/ptrace.c

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,9 @@ void __ptrace_link(struct task_struct *child, struct task_struct *new_parent)
3939
BUG_ON(!list_empty(&child->ptrace_entry));
4040
list_add(&child->ptrace_entry, &new_parent->ptraced);
4141
child->parent = new_parent;
42+
rcu_read_lock();
43+
child->ptracer_cred = get_cred(__task_cred(new_parent));
44+
rcu_read_unlock();
4245
}
4346

4447
/**
@@ -71,12 +74,16 @@ void __ptrace_link(struct task_struct *child, struct task_struct *new_parent)
7174
*/
7275
void __ptrace_unlink(struct task_struct *child)
7376
{
77+
const struct cred *old_cred;
7478
BUG_ON(!child->ptrace);
7579

7680
clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
7781

7882
child->parent = child->real_parent;
7983
list_del_init(&child->ptrace_entry);
84+
old_cred = child->ptracer_cred;
85+
child->ptracer_cred = NULL;
86+
put_cred(old_cred);
8087

8188
spin_lock(&child->sighand->siglock);
8289
child->ptrace = 0;
@@ -326,11 +333,6 @@ static int ptrace_attach(struct task_struct *task, long request,
326333

327334
task_lock(task);
328335
retval = __ptrace_may_access(task, PTRACE_MODE_ATTACH_REALCREDS);
329-
if (!retval) {
330-
struct mm_struct *mm = task->mm;
331-
if (mm && ns_capable(mm->user_ns, CAP_SYS_PTRACE))
332-
flags |= PT_PTRACE_CAP;
333-
}
334336
task_unlock(task);
335337
if (retval)
336338
goto unlock_creds;

0 commit comments

Comments
 (0)