aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEric W. Biederman <ebiederm@xmission.com>2016-11-14 19:48:07 -0500
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2017-01-06 04:40:13 -0500
commite747b4ae3b6bca205d82e86366e140cdcbfb7731 (patch)
treeeb37a1d3d431363778fde364f726e49571ba8ec6
parent48466c4772d2cfeb331395043f7edd8c5324ef1b (diff)
ptrace: Capture the ptracer's creds not PT_PTRACE_CAP
commit 64b875f7ac8a5d60a4e191479299e931ee949b67 upstream. 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. Fixes: 4214e42f96d4 ("v2.4.9.11 -> v2.4.9.12") Signed-off-by: "Eric W. Biederman" <ebiederm@xmission.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-rw-r--r--fs/exec.c2
-rw-r--r--include/linux/capability.h1
-rw-r--r--include/linux/ptrace.h1
-rw-r--r--include/linux/sched.h1
-rw-r--r--kernel/capability.c20
-rw-r--r--kernel/ptrace.c12
6 files changed, 30 insertions, 7 deletions
diff --git a/fs/exec.c b/fs/exec.c
index a9dec2f540dc..67e86571685a 100644
--- a/fs/exec.c
+++ b/fs/exec.c
@@ -1425,7 +1425,7 @@ static void check_unsafe_exec(struct linux_binprm *bprm)
1425 unsigned n_fs; 1425 unsigned n_fs;
1426 1426
1427 if (p->ptrace) { 1427 if (p->ptrace) {
1428 if (p->ptrace & PT_PTRACE_CAP) 1428 if (ptracer_capable(p, current_user_ns()))
1429 bprm->unsafe |= LSM_UNSAFE_PTRACE_CAP; 1429 bprm->unsafe |= LSM_UNSAFE_PTRACE_CAP;
1430 else 1430 else
1431 bprm->unsafe |= LSM_UNSAFE_PTRACE; 1431 bprm->unsafe |= LSM_UNSAFE_PTRACE;
diff --git a/include/linux/capability.h b/include/linux/capability.h
index 6ce3eefe2769..6ffb67e10c06 100644
--- a/include/linux/capability.h
+++ b/include/linux/capability.h
@@ -243,6 +243,7 @@ static inline bool ns_capable_noaudit(struct user_namespace *ns, int cap)
243extern bool privileged_wrt_inode_uidgid(struct user_namespace *ns, const struct inode *inode); 243extern bool privileged_wrt_inode_uidgid(struct user_namespace *ns, const struct inode *inode);
244extern bool capable_wrt_inode_uidgid(const struct inode *inode, int cap); 244extern bool capable_wrt_inode_uidgid(const struct inode *inode, int cap);
245extern bool file_ns_capable(const struct file *file, struct user_namespace *ns, int cap); 245extern bool file_ns_capable(const struct file *file, struct user_namespace *ns, int cap);
246extern bool ptracer_capable(struct task_struct *tsk, struct user_namespace *ns);
246 247
247/* audit system wants to get cap info from files as well */ 248/* audit system wants to get cap info from files as well */
248extern int get_vfs_caps_from_disk(const struct dentry *dentry, struct cpu_vfs_cap_data *cpu_caps); 249extern int get_vfs_caps_from_disk(const struct dentry *dentry, struct cpu_vfs_cap_data *cpu_caps);
diff --git a/include/linux/ptrace.h b/include/linux/ptrace.h
index 504c98a278d4..e13bfdf7f314 100644
--- a/include/linux/ptrace.h
+++ b/include/linux/ptrace.h
@@ -19,7 +19,6 @@
19#define PT_SEIZED 0x00010000 /* SEIZE used, enable new behavior */ 19#define PT_SEIZED 0x00010000 /* SEIZE used, enable new behavior */
20#define PT_PTRACED 0x00000001 20#define PT_PTRACED 0x00000001
21#define PT_DTRACE 0x00000002 /* delayed trace (used on m68k, i386) */ 21#define PT_DTRACE 0x00000002 /* delayed trace (used on m68k, i386) */
22#define PT_PTRACE_CAP 0x00000004 /* ptracer can follow suid-exec */
23 22
24#define PT_OPT_FLAG_SHIFT 3 23#define PT_OPT_FLAG_SHIFT 3
25/* PT_TRACE_* event enable flags */ 24/* PT_TRACE_* event enable flags */
diff --git a/include/linux/sched.h b/include/linux/sched.h
index e9c009dc3a4a..75d9a57e212e 100644
--- a/include/linux/sched.h
+++ b/include/linux/sched.h
@@ -1656,6 +1656,7 @@ struct task_struct {
1656 struct list_head cpu_timers[3]; 1656 struct list_head cpu_timers[3];
1657 1657
1658/* process credentials */ 1658/* process credentials */
1659 const struct cred __rcu *ptracer_cred; /* Tracer's credentials at attach */
1659 const struct cred __rcu *real_cred; /* objective and real subjective task 1660 const struct cred __rcu *real_cred; /* objective and real subjective task
1660 * credentials (COW) */ 1661 * credentials (COW) */
1661 const struct cred __rcu *cred; /* effective (overridable) subjective task 1662 const struct cred __rcu *cred; /* effective (overridable) subjective task
diff --git a/kernel/capability.c b/kernel/capability.c
index c020c0047983..4984e1f552eb 100644
--- a/kernel/capability.c
+++ b/kernel/capability.c
@@ -485,3 +485,23 @@ bool capable_wrt_inode_uidgid(const struct inode *inode, int cap)
485 return ns_capable(ns, cap) && privileged_wrt_inode_uidgid(ns, inode); 485 return ns_capable(ns, cap) && privileged_wrt_inode_uidgid(ns, inode);
486} 486}
487EXPORT_SYMBOL(capable_wrt_inode_uidgid); 487EXPORT_SYMBOL(capable_wrt_inode_uidgid);
488
489/**
490 * ptracer_capable - Determine if the ptracer holds CAP_SYS_PTRACE in the namespace
491 * @tsk: The task that may be ptraced
492 * @ns: The user namespace to search for CAP_SYS_PTRACE in
493 *
494 * Return true if the task that is ptracing the current task had CAP_SYS_PTRACE
495 * in the specified user namespace.
496 */
497bool ptracer_capable(struct task_struct *tsk, struct user_namespace *ns)
498{
499 int ret = 0; /* An absent tracer adds no restrictions */
500 const struct cred *cred;
501 rcu_read_lock();
502 cred = rcu_dereference(tsk->ptracer_cred);
503 if (cred)
504 ret = security_capable_noaudit(cred, ns, CAP_SYS_PTRACE);
505 rcu_read_unlock();
506 return (ret == 0);
507}
diff --git a/kernel/ptrace.c b/kernel/ptrace.c
index 282821557183..e82c15cadd6d 100644
--- a/kernel/ptrace.c
+++ b/kernel/ptrace.c
@@ -39,6 +39,9 @@ void __ptrace_link(struct task_struct *child, struct task_struct *new_parent)
39 BUG_ON(!list_empty(&child->ptrace_entry)); 39 BUG_ON(!list_empty(&child->ptrace_entry));
40 list_add(&child->ptrace_entry, &new_parent->ptraced); 40 list_add(&child->ptrace_entry, &new_parent->ptraced);
41 child->parent = new_parent; 41 child->parent = new_parent;
42 rcu_read_lock();
43 child->ptracer_cred = get_cred(__task_cred(new_parent));
44 rcu_read_unlock();
42} 45}
43 46
44/** 47/**
@@ -71,12 +74,16 @@ void __ptrace_link(struct task_struct *child, struct task_struct *new_parent)
71 */ 74 */
72void __ptrace_unlink(struct task_struct *child) 75void __ptrace_unlink(struct task_struct *child)
73{ 76{
77 const struct cred *old_cred;
74 BUG_ON(!child->ptrace); 78 BUG_ON(!child->ptrace);
75 79
76 clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE); 80 clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
77 81
78 child->parent = child->real_parent; 82 child->parent = child->real_parent;
79 list_del_init(&child->ptrace_entry); 83 list_del_init(&child->ptrace_entry);
84 old_cred = child->ptracer_cred;
85 child->ptracer_cred = NULL;
86 put_cred(old_cred);
80 87
81 spin_lock(&child->sighand->siglock); 88 spin_lock(&child->sighand->siglock);
82 child->ptrace = 0; 89 child->ptrace = 0;
@@ -326,11 +333,6 @@ static int ptrace_attach(struct task_struct *task, long request,
326 333
327 task_lock(task); 334 task_lock(task);
328 retval = __ptrace_may_access(task, PTRACE_MODE_ATTACH_REALCREDS); 335 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 }
334 task_unlock(task); 336 task_unlock(task);
335 if (retval) 337 if (retval)
336 goto unlock_creds; 338 goto unlock_creds;