ptrace: Capture the ptracer's creds not PT_PTRACE_CAP
authorEric W. Biederman <ebiederm@xmission.com>
Tue, 15 Nov 2016 00:48:07 +0000 (18:48 -0600)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Fri, 6 Jan 2017 10:16:11 +0000 (11:16 +0100)
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>
fs/exec.c
include/linux/capability.h
include/linux/ptrace.h
include/linux/sched.h
kernel/capability.c
kernel/ptrace.c

index b06623a9347f4f206fb466c80574ec4bdd7d13fa..4040d7b173c04462f0f8740c9dbd66d7e9b0f3be 100644 (file)
--- a/fs/exec.c
+++ b/fs/exec.c
@@ -1254,7 +1254,7 @@ static void check_unsafe_exec(struct linux_binprm *bprm)
        unsigned n_fs;
 
        if (p->ptrace) {
-               if (p->ptrace & PT_PTRACE_CAP)
+               if (ptracer_capable(p, current_user_ns()))
                        bprm->unsafe |= LSM_UNSAFE_PTRACE_CAP;
                else
                        bprm->unsafe |= LSM_UNSAFE_PTRACE;
index 5f8249d378a237445cdf678a2fc3e567cbc00217..7d38697c7e63b06b0c7c70cfdb5d4146f9f24e98 100644 (file)
@@ -249,6 +249,7 @@ static inline bool ns_capable_noaudit(struct user_namespace *ns, int cap)
 #endif /* CONFIG_MULTIUSER */
 extern bool capable_wrt_inode_uidgid(const struct inode *inode, int cap);
 extern bool file_ns_capable(const struct file *file, struct user_namespace *ns, int cap);
+extern bool ptracer_capable(struct task_struct *tsk, struct user_namespace *ns);
 
 /* audit system wants to get cap info from files as well */
 extern int get_vfs_caps_from_disk(const struct dentry *dentry, struct cpu_vfs_cap_data *cpu_caps);
index 504c98a278d46606d27f09d109589e0a1e2263d8..e13bfdf7f314784e326b245e1f1ab5465da9e7dc 100644 (file)
@@ -19,7 +19,6 @@
 #define PT_SEIZED      0x00010000      /* SEIZE used, enable new behavior */
 #define PT_PTRACED     0x00000001
 #define PT_DTRACE      0x00000002      /* delayed trace (used on m68k, i386) */
-#define PT_PTRACE_CAP  0x00000004      /* ptracer can follow suid-exec */
 
 #define PT_OPT_FLAG_SHIFT      3
 /* PT_TRACE_* event enable flags */
index 1c0193baea2a57a53ed41a650df3717f4a0148ba..ce0f61dcd887ee7e2694ec6c641ec1b567345dad 100644 (file)
@@ -1540,6 +1540,7 @@ struct task_struct {
        struct list_head cpu_timers[3];
 
 /* process credentials */
+       const struct cred __rcu *ptracer_cred; /* Tracer's credentials at attach */
        const struct cred __rcu *real_cred; /* objective and real subjective task
                                         * credentials (COW) */
        const struct cred __rcu *cred;  /* effective (overridable) subjective task
index 00411c82dac57c4cac0254c823f9adcb2a951549..dfa0e4528b0b6d887dca7f75a2e71ace761c9dea 100644 (file)
@@ -473,3 +473,23 @@ bool capable_wrt_inode_uidgid(const struct inode *inode, int cap)
                kgid_has_mapping(ns, inode->i_gid);
 }
 EXPORT_SYMBOL(capable_wrt_inode_uidgid);
+
+/**
+ * ptracer_capable - Determine if the ptracer holds CAP_SYS_PTRACE in the namespace
+ * @tsk: The task that may be ptraced
+ * @ns: The user namespace to search for CAP_SYS_PTRACE in
+ *
+ * Return true if the task that is ptracing the current task had CAP_SYS_PTRACE
+ * in the specified user namespace.
+ */
+bool ptracer_capable(struct task_struct *tsk, struct user_namespace *ns)
+{
+       int ret = 0;  /* An absent tracer adds no restrictions */
+       const struct cred *cred;
+       rcu_read_lock();
+       cred = rcu_dereference(tsk->ptracer_cred);
+       if (cred)
+               ret = security_capable_noaudit(cred, ns, CAP_SYS_PTRACE);
+       rcu_read_unlock();
+       return (ret == 0);
+}
index 21b60e0a30560a26c47a169582cebbc48bb5bd7f..a46c40bfb5f669f04f6b42c8c06ea0c61ba63360 100644 (file)
@@ -39,6 +39,9 @@ void __ptrace_link(struct task_struct *child, struct task_struct *new_parent)
        BUG_ON(!list_empty(&child->ptrace_entry));
        list_add(&child->ptrace_entry, &new_parent->ptraced);
        child->parent = new_parent;
+       rcu_read_lock();
+       child->ptracer_cred = get_cred(__task_cred(new_parent));
+       rcu_read_unlock();
 }
 
 /**
@@ -71,11 +74,15 @@ void __ptrace_link(struct task_struct *child, struct task_struct *new_parent)
  */
 void __ptrace_unlink(struct task_struct *child)
 {
+       const struct cred *old_cred;
        BUG_ON(!child->ptrace);
 
        child->ptrace = 0;
        child->parent = child->real_parent;
        list_del_init(&child->ptrace_entry);
+       old_cred = child->ptracer_cred;
+       child->ptracer_cred = NULL;
+       put_cred(old_cred);
 
        spin_lock(&child->sighand->siglock);
 
@@ -325,11 +332,6 @@ static int ptrace_attach(struct task_struct *task, long request,
 
        task_lock(task);
        retval = __ptrace_may_access(task, PTRACE_MODE_ATTACH_REALCREDS);
-       if (!retval) {
-               struct mm_struct *mm = task->mm;
-               if (mm && ns_capable(mm->user_ns, CAP_SYS_PTRACE))
-                       flags |= PT_PTRACE_CAP;
-       }
        task_unlock(task);
        if (retval)
                goto unlock_creds;