aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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 4e497b9ee71e..3cf2cfced97a 100644
--- a/fs/exec.c
+++ b/fs/exec.c
@@ -1406,7 +1406,7 @@ static void check_unsafe_exec(struct linux_binprm *bprm)
1406 unsigned n_fs; 1406 unsigned n_fs;
1407 1407
1408 if (p->ptrace) { 1408 if (p->ptrace) {
1409 if (p->ptrace & PT_PTRACE_CAP) 1409 if (ptracer_capable(p, current_user_ns()))
1410 bprm->unsafe |= LSM_UNSAFE_PTRACE_CAP; 1410 bprm->unsafe |= LSM_UNSAFE_PTRACE_CAP;
1411 else 1411 else
1412 bprm->unsafe |= LSM_UNSAFE_PTRACE; 1412 bprm->unsafe |= LSM_UNSAFE_PTRACE;
diff --git a/include/linux/capability.h b/include/linux/capability.h
index dbc21c719ce6..d6088e2a7668 100644
--- a/include/linux/capability.h
+++ b/include/linux/capability.h
@@ -242,6 +242,7 @@ static inline bool ns_capable_noaudit(struct user_namespace *ns, int cap)
242#endif /* CONFIG_MULTIUSER */ 242#endif /* CONFIG_MULTIUSER */
243extern bool capable_wrt_inode_uidgid(const struct inode *inode, int cap); 243extern bool capable_wrt_inode_uidgid(const struct inode *inode, int cap);
244extern bool file_ns_capable(const struct file *file, struct user_namespace *ns, int cap); 244extern bool file_ns_capable(const struct file *file, struct user_namespace *ns, int cap);
245extern bool ptracer_capable(struct task_struct *tsk, struct user_namespace *ns);
245 246
246/* audit system wants to get cap info from files as well */ 247/* audit system wants to get cap info from files as well */
247extern int get_vfs_caps_from_disk(const struct dentry *dentry, struct cpu_vfs_cap_data *cpu_caps); 248extern 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 348f51b0ec92..e9f693598e15 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 00411c82dac5..dfa0e4528b0b 100644
--- a/kernel/capability.c
+++ b/kernel/capability.c
@@ -473,3 +473,23 @@ bool capable_wrt_inode_uidgid(const struct inode *inode, int cap)
473 kgid_has_mapping(ns, inode->i_gid); 473 kgid_has_mapping(ns, inode->i_gid);
474} 474}
475EXPORT_SYMBOL(capable_wrt_inode_uidgid); 475EXPORT_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 */
485bool 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}
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;