diff options
| -rw-r--r-- | fs/exec.c | 2 | ||||
| -rw-r--r-- | include/linux/capability.h | 1 | ||||
| -rw-r--r-- | include/linux/ptrace.h | 1 | ||||
| -rw-r--r-- | include/linux/sched.h | 1 | ||||
| -rw-r--r-- | kernel/capability.c | 20 | ||||
| -rw-r--r-- | kernel/ptrace.c | 12 |
6 files changed, 30 insertions, 7 deletions
| @@ -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 */ |
| 243 | extern bool capable_wrt_inode_uidgid(const struct inode *inode, int cap); | 243 | extern bool capable_wrt_inode_uidgid(const struct inode *inode, int cap); |
| 244 | extern bool file_ns_capable(const struct file *file, struct user_namespace *ns, int cap); | 244 | 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); | ||
| 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 */ |
| 247 | extern int get_vfs_caps_from_disk(const struct dentry *dentry, struct cpu_vfs_cap_data *cpu_caps); | 248 | extern 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 | } |
| 475 | EXPORT_SYMBOL(capable_wrt_inode_uidgid); | 475 | 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 | } | ||
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 | */ |
| 72 | void __ptrace_unlink(struct task_struct *child) | 75 | void __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; |
