From patchwork Sun Sep 18 15:05:11 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jann Horn X-Patchwork-Id: 9337905 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork.web.codeaurora.org (Postfix) with ESMTP id 306266089F for ; Sun, 18 Sep 2016 15:05:42 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 2053328CDF for ; Sun, 18 Sep 2016 15:05:42 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 1362028CEB; Sun, 18 Sep 2016 15:05:42 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-6.9 required=2.0 tests=BAYES_00,RCVD_IN_DNSWL_HI autolearn=unavailable version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id B2F4428CEB for ; Sun, 18 Sep 2016 15:05:40 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S936253AbcIRPFi (ORCPT ); Sun, 18 Sep 2016 11:05:38 -0400 Received: from thejh.net ([37.221.195.125]:46280 "EHLO thejh.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S934338AbcIRPFe (ORCPT ); Sun, 18 Sep 2016 11:05:34 -0400 Received: from pc.thejh.net (pc.vpn [192.168.44.2]) by thejh.net (Postfix) with ESMTPSA id DE0F2182691; Sun, 18 Sep 2016 17:05:31 +0200 (CEST) From: Jann Horn To: Alexander Viro , Roland McGrath , Oleg Nesterov , John Johansen , James Morris , "Serge E. Hallyn" , Paul Moore , Stephen Smalley , Eric Paris , Casey Schaufler , Kees Cook , Andrew Morton , Janis Danisevskis , Seth Forshee , "Eric . Biederman" , Thomas Gleixner , Benjamin LaHaise Cc: linux-fsdevel@vger.kernel.org, linux-security-module@vger.kernel.org, security@kernel.org Subject: [PATCH 3/9] proc: use open()-time creds for ptrace checks Date: Sun, 18 Sep 2016 17:05:11 +0200 Message-Id: <1474211117-16674-4-git-send-email-jann@thejh.net> X-Mailer: git-send-email 2.1.4 In-Reply-To: <1474211117-16674-1-git-send-email-jann@thejh.net> References: <1474211117-16674-1-git-send-email-jann@thejh.net> Sender: linux-fsdevel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-fsdevel@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP This adds a new ptrace_may_access_noncurrent() method that uses the supplied credentials instead of those of the current task. (However, the current task may still be inspected for auditing purposes, e.g. by the Smack LSM.) procfs used the caller's creds for a few ptrace_may_access() checks at read() time, which made a confused deputy attack by passing an FD to a procfs file to a setuid program possible. Therefore, the following was a local userspace ASLR bypass: rm -f /tmp/foobar procmail <(echo 'DEFAULT=/tmp/foobar') --- fs/proc/array.c | 3 +- fs/proc/base.c | 63 ++++++++++++++++++++++++++++++++++------- fs/proc/internal.h | 14 +++++++++ include/linux/lsm_hooks.h | 3 +- include/linux/ptrace.h | 5 ++++ include/linux/security.h | 10 ++++--- kernel/ptrace.c | 40 +++++++++++++++++++------- security/apparmor/include/ipc.h | 2 +- security/apparmor/ipc.c | 4 +-- security/apparmor/lsm.c | 14 +++++++-- security/commoncap.c | 8 +++--- security/security.c | 5 ++-- security/selinux/hooks.c | 4 +-- security/smack/smack_lsm.c | 18 ++++++++---- security/yama/yama_lsm.c | 9 ++++-- 15 files changed, 150 insertions(+), 52 deletions(-) diff --git a/fs/proc/array.c b/fs/proc/array.c index 88c7de1..3349742 100644 --- a/fs/proc/array.c +++ b/fs/proc/array.c @@ -413,7 +413,8 @@ static int do_task_stat(struct seq_file *m, struct pid_namespace *ns, state = *get_task_state(task); vsize = eip = esp = 0; - permitted = ptrace_may_access(task, PTRACE_MODE_READ_FSCREDS | PTRACE_MODE_NOAUDIT); + permitted = proc_ptrace_may_access_seq(task, + PTRACE_MODE_READ_FSCREDS | PTRACE_MODE_NOAUDIT, m); mm = get_task_mm(task); if (mm) { vsize = task_vsize(mm); diff --git a/fs/proc/base.c b/fs/proc/base.c index ac0df4d..771199b 100644 --- a/fs/proc/base.c +++ b/fs/proc/base.c @@ -430,8 +430,9 @@ static int proc_pid_wchan(struct seq_file *m, struct pid_namespace *ns, wchan = get_wchan(task); - if (wchan && ptrace_may_access(task, PTRACE_MODE_READ_FSCREDS) - && !lookup_symbol_name(wchan, symname)) + if (wchan && + proc_ptrace_may_access_seq(task, PTRACE_MODE_READ_FSCREDS, m) && + !lookup_symbol_name(wchan, symname)) seq_printf(m, "%s", symname); else seq_putc(m, '0'); @@ -440,12 +441,12 @@ static int proc_pid_wchan(struct seq_file *m, struct pid_namespace *ns, } #endif /* CONFIG_KALLSYMS */ -static int lock_trace(struct task_struct *task) +static int lock_trace(struct seq_file *m, struct task_struct *task) { int err = mutex_lock_killable(&task->signal->cred_guard_mutex); if (err) return err; - if (!ptrace_may_access(task, PTRACE_MODE_ATTACH_FSCREDS)) { + if (!proc_ptrace_may_access_seq(task, PTRACE_MODE_ATTACH_FSCREDS, m)) { mutex_unlock(&task->signal->cred_guard_mutex); return -EPERM; } @@ -478,7 +479,7 @@ static int proc_pid_stack(struct seq_file *m, struct pid_namespace *ns, trace.entries = entries; trace.skip = 0; - err = lock_trace(task); + err = lock_trace(m, task); if (!err) { save_stack_trace_tsk(task, &trace); @@ -660,7 +661,7 @@ static int proc_pid_syscall(struct seq_file *m, struct pid_namespace *ns, unsigned long args[6], sp, pc; int res; - res = lock_trace(task); + res = lock_trace(m, task); if (res) return res; @@ -770,7 +771,7 @@ static const struct inode_operations proc_def_inode_operations = { static int proc_single_show(struct seq_file *m, void *v) { - struct inode *inode = m->private; + struct inode *inode = m->file->f_inode; struct pid_namespace *ns; struct pid *pid; struct task_struct *task; @@ -790,14 +791,35 @@ static int proc_single_show(struct seq_file *m, void *v) static int proc_single_open(struct inode *inode, struct file *filp) { - return single_open(filp, proc_single_show, inode); + int ret; + u64 *privunit_id = kmalloc(sizeof(u64), GFP_KERNEL); + + if (privunit_id == NULL) + return -ENOMEM; + *privunit_id = current->self_privunit_id; + + ret = single_open(filp, proc_single_show, privunit_id); + + if (ret) + kfree(privunit_id); + return ret; +} + +static int proc_single_release(struct inode *inode, struct file *filp) +{ + struct seq_file *seq = filp->private_data; + u64 *privunit_id = seq->private; + + kfree(privunit_id); + + return single_release(inode, filp); } static const struct file_operations proc_single_file_operations = { .open = proc_single_open, .read = seq_read, .llseek = seq_lseek, - .release = single_release, + .release = proc_single_release, }; @@ -2157,10 +2179,29 @@ out: return ret; } +int proc_open_get_privunit_id(struct inode *inode, struct file *filp) +{ + u64 *privunit_id = kmalloc(sizeof(u64), GFP_KERNEL); + + if (privunit_id == NULL) + return -ENOMEM; + *privunit_id = current->self_privunit_id; + filp->private_data = privunit_id; + return 0; +} + +int proc_release_put_privunit_id(struct inode *inode, struct file *filp) +{ + kfree((u64 *)filp->private_data); + return 0; +} + static const struct file_operations proc_map_files_operations = { .read = generic_read_dir, .iterate_shared = proc_map_files_readdir, .llseek = generic_file_llseek, + .open = proc_open_get_privunit_id, + .release = proc_release_put_privunit_id, }; #ifdef CONFIG_CHECKPOINT_RESTORE @@ -2614,7 +2655,7 @@ static int do_io_accounting(struct task_struct *task, struct seq_file *m, int wh if (result) return result; - if (!ptrace_may_access(task, PTRACE_MODE_READ_FSCREDS)) { + if (!proc_ptrace_may_access_seq(task, PTRACE_MODE_READ_FSCREDS, m)) { result = -EACCES; goto out_unlock; } @@ -2798,7 +2839,7 @@ static const struct file_operations proc_setgroups_operations = { static int proc_pid_personality(struct seq_file *m, struct pid_namespace *ns, struct pid *pid, struct task_struct *task) { - int err = lock_trace(task); + int err = lock_trace(m, task); if (!err) { seq_printf(m, "%08x\n", task->personality); unlock_trace(task); diff --git a/fs/proc/internal.h b/fs/proc/internal.h index 7931c55..9498879 100644 --- a/fs/proc/internal.h +++ b/fs/proc/internal.h @@ -14,6 +14,9 @@ #include #include #include +#include +#include +#include struct ctl_table_header; struct mempolicy; @@ -134,6 +137,15 @@ out: return ~0U; } +static inline bool proc_ptrace_may_access_seq(struct task_struct *task, + unsigned int mode, struct seq_file *m) +{ + const u64 *privunit_id = m->private; + + return ptrace_may_access_noncurrent(task, mode, m->file->f_cred, + privunit_id); +} + /* * Offset of the first process in the /proc root directory.. */ @@ -168,6 +180,8 @@ extern int pid_delete_dentry(const struct dentry *); extern int proc_pid_readdir(struct file *, struct dir_context *); extern struct dentry *proc_pid_lookup(struct inode *, struct dentry *, unsigned int); extern loff_t mem_lseek(struct file *, loff_t, int); +extern int proc_open_get_privunit_id(struct inode *, struct file *); +extern int proc_release_put_privunit_id(struct inode *, struct file *); /* Lookups */ typedef int instantiate_t(struct inode *, struct dentry *, diff --git a/include/linux/lsm_hooks.h b/include/linux/lsm_hooks.h index 101bf19..f33ef61 100644 --- a/include/linux/lsm_hooks.h +++ b/include/linux/lsm_hooks.h @@ -1147,6 +1147,7 @@ * attributes would be changed by the execve. * @child contains the task_struct structure for the target process. * @mode contains the PTRACE_MODE flags indicating the form of access. + * @cred contains the caller's credentials * Return 0 if permission is granted. * @ptrace_traceme: * Check that the @parent process has sufficient permission to trace the @@ -1315,7 +1316,7 @@ union security_list_options { struct file *file); int (*ptrace_access_check)(struct task_struct *child, - unsigned int mode); + unsigned int mode, const struct cred *cred); int (*ptrace_traceme)(struct task_struct *parent); int (*capget)(struct task_struct *target, kernel_cap_t *effective, kernel_cap_t *inheritable, kernel_cap_t *permitted); diff --git a/include/linux/ptrace.h b/include/linux/ptrace.h index 504c98a..d48d953 100644 --- a/include/linux/ptrace.h +++ b/include/linux/ptrace.h @@ -66,6 +66,9 @@ extern void exit_ptrace(struct task_struct *tracer, struct list_head *dead); #define PTRACE_MODE_ATTACH_FSCREDS (PTRACE_MODE_ATTACH | PTRACE_MODE_FSCREDS) #define PTRACE_MODE_ATTACH_REALCREDS (PTRACE_MODE_ATTACH | PTRACE_MODE_REALCREDS) +/* ignore relationship between current and target task */ +#define PTRACE_MODE_NON_CURRENT 0x20 + /** * ptrace_may_access - check whether the caller is permitted to access * a target task. @@ -81,6 +84,8 @@ extern void exit_ptrace(struct task_struct *tracer, struct list_head *dead); * process_vm_writev or ptrace (and should use the real credentials). */ extern bool ptrace_may_access(struct task_struct *task, unsigned int mode); +bool ptrace_may_access_noncurrent(struct task_struct *task, unsigned int mode, + const struct cred *cred, const u64 *privunit_id); static inline int ptrace_reparented(struct task_struct *child) { diff --git a/include/linux/security.h b/include/linux/security.h index 7831cd5..e2cca1d 100644 --- a/include/linux/security.h +++ b/include/linux/security.h @@ -72,7 +72,8 @@ struct timezone; extern int cap_capable(const struct cred *cred, struct user_namespace *ns, int cap, int audit); extern int cap_settime(const struct timespec64 *ts, const struct timezone *tz); -extern int cap_ptrace_access_check(struct task_struct *child, unsigned int mode); +extern int cap_ptrace_access_check(struct task_struct *child, unsigned int mode, + const struct cred *cred); extern int cap_ptrace_traceme(struct task_struct *parent); extern int cap_capget(struct task_struct *target, kernel_cap_t *effective, kernel_cap_t *inheritable, kernel_cap_t *permitted); extern int cap_capset(struct cred *new, const struct cred *old, @@ -191,7 +192,8 @@ int security_binder_transfer_binder(struct task_struct *from, struct task_struct *to); int security_binder_transfer_file(struct task_struct *from, struct task_struct *to, struct file *file); -int security_ptrace_access_check(struct task_struct *child, unsigned int mode); +int security_ptrace_access_check(struct task_struct *child, unsigned int mode, + const struct cred *cred); int security_ptrace_traceme(struct task_struct *parent); int security_capget(struct task_struct *target, kernel_cap_t *effective, @@ -414,9 +416,9 @@ static inline int security_binder_transfer_file(struct task_struct *from, } static inline int security_ptrace_access_check(struct task_struct *child, - unsigned int mode) + unsigned int mode, const struct cred *cred) { - return cap_ptrace_access_check(child, mode); + return cap_ptrace_access_check(child, mode, cred); } static inline int security_ptrace_traceme(struct task_struct *parent) diff --git a/kernel/ptrace.c b/kernel/ptrace.c index b5120ec..dc7594a 100644 --- a/kernel/ptrace.c +++ b/kernel/ptrace.c @@ -206,18 +206,20 @@ static int ptrace_check_attach(struct task_struct *child, bool ignore_state) return ret; } -static int ptrace_has_cap(struct user_namespace *ns, unsigned int mode) +static int ptrace_has_cap(const struct cred *cred, struct user_namespace *ns, + unsigned int mode) { if (mode & PTRACE_MODE_NOAUDIT) - return has_ns_capability_noaudit(current, ns, CAP_SYS_PTRACE); + return security_capable_noaudit(cred, ns, CAP_SYS_PTRACE) == 0; else - return has_ns_capability(current, ns, CAP_SYS_PTRACE); + return security_capable(cred, ns, CAP_SYS_PTRACE) == 0; } /* Returns 0 on success, -errno on denial. */ -static int __ptrace_may_access(struct task_struct *task, unsigned int mode) +static int __ptrace_may_access(struct task_struct *task, unsigned int mode, + const struct cred *cred, const u64 *privunit_id) { - const struct cred *cred = current_cred(), *tcred; + const struct cred *tcred; int dumpable = 0; kuid_t caller_uid; kgid_t caller_gid; @@ -237,8 +239,9 @@ static int __ptrace_may_access(struct task_struct *task, unsigned int mode) */ /* Don't let security modules deny introspection */ - if (same_thread_group(task, current)) + if (*privunit_id == task->self_privunit_id) return 0; + rcu_read_lock(); if (mode & PTRACE_MODE_FSCREDS) { caller_uid = cred->fsuid; @@ -263,7 +266,7 @@ static int __ptrace_may_access(struct task_struct *task, unsigned int mode) gid_eq(caller_gid, tcred->sgid) && gid_eq(caller_gid, tcred->gid)) goto ok; - if (ptrace_has_cap(tcred->user_ns, mode)) + if (ptrace_has_cap(cred, tcred->user_ns, mode)) goto ok; rcu_read_unlock(); return -EPERM; @@ -274,13 +277,13 @@ ok: dumpable = get_dumpable(task->mm); rcu_read_lock(); if (dumpable != SUID_DUMP_USER && - !ptrace_has_cap(__task_cred(task)->user_ns, mode)) { + !ptrace_has_cap(cred, __task_cred(task)->user_ns, mode)) { rcu_read_unlock(); return -EPERM; } rcu_read_unlock(); - return security_ptrace_access_check(task, mode); + return security_ptrace_access_check(task, mode, cred); } /* @@ -297,7 +300,21 @@ bool ptrace_may_access(struct task_struct *task, unsigned int mode) { int err; task_lock(task); - err = __ptrace_may_access(task, mode); + err = __ptrace_may_access(task, mode, current_cred(), + ¤t->self_privunit_id); + task_unlock(task); + return !err; +} + +bool ptrace_may_access_noncurrent(struct task_struct *task, unsigned int mode, + const struct cred *cred, + const u64 *privunit_id) +{ + int err; + + task_lock(task); + err = __ptrace_may_access(task, mode | PTRACE_MODE_NON_CURRENT, cred, + privunit_id); task_unlock(task); return !err; } @@ -338,7 +355,8 @@ static int ptrace_attach(struct task_struct *task, long request, goto out; task_lock(task); - retval = __ptrace_may_access(task, PTRACE_MODE_ATTACH_REALCREDS); + retval = __ptrace_may_access(task, PTRACE_MODE_ATTACH_REALCREDS, + current_cred(), ¤t->self_privunit_id); task_unlock(task); if (retval) goto unlock_creds; diff --git a/security/apparmor/include/ipc.h b/security/apparmor/include/ipc.h index 288ca76..eb685c1 100644 --- a/security/apparmor/include/ipc.h +++ b/security/apparmor/include/ipc.h @@ -22,7 +22,7 @@ struct aa_profile; int aa_may_ptrace(struct aa_profile *tracer, struct aa_profile *tracee, unsigned int mode); -int aa_ptrace(struct task_struct *tracer, struct task_struct *tracee, +int aa_ptrace(struct aa_profile *tracer_p, struct task_struct *tracee, unsigned int mode); #endif /* __AA_IPC_H */ diff --git a/security/apparmor/ipc.c b/security/apparmor/ipc.c index 777ac1c..c60b374 100644 --- a/security/apparmor/ipc.c +++ b/security/apparmor/ipc.c @@ -82,7 +82,7 @@ int aa_may_ptrace(struct aa_profile *tracer, struct aa_profile *tracee, * * Returns: %0 else error code if permission denied or error */ -int aa_ptrace(struct task_struct *tracer, struct task_struct *tracee, +int aa_ptrace(struct aa_profile *tracer_p, struct task_struct *tracee, unsigned int mode) { /* @@ -94,7 +94,6 @@ int aa_ptrace(struct task_struct *tracer, struct task_struct *tracee, * - tracer profile has CAP_SYS_PTRACE */ - struct aa_profile *tracer_p = aa_get_task_profile(tracer); int error = 0; if (!unconfined(tracer_p)) { @@ -105,7 +104,6 @@ int aa_ptrace(struct task_struct *tracer, struct task_struct *tracee, aa_put_profile(tracee_p); } - aa_put_profile(tracer_p); return error; } diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c index 41b8cb1..dd94df2 100644 --- a/security/apparmor/lsm.c +++ b/security/apparmor/lsm.c @@ -94,14 +94,22 @@ static void apparmor_cred_transfer(struct cred *new, const struct cred *old) } static int apparmor_ptrace_access_check(struct task_struct *child, - unsigned int mode) + unsigned int mode, const struct cred *cred) { - return aa_ptrace(current, child, mode); + struct aa_profile *tracer_p = aa_get_profile(aa_cred_profile(cred)); + int res = aa_ptrace(tracer_p, child, mode); + + aa_put_profile(tracer_p); + return res; } static int apparmor_ptrace_traceme(struct task_struct *parent) { - return aa_ptrace(parent, current, PTRACE_MODE_ATTACH); + struct aa_profile *tracer_p = aa_get_task_profile(parent); + int res = aa_ptrace(tracer_p, current, PTRACE_MODE_ATTACH); + + aa_put_profile(tracer_p); + return res; } /* Derived from security/commoncap.c:cap_capget */ diff --git a/security/commoncap.c b/security/commoncap.c index 14540bd..fe0a73a 100644 --- a/security/commoncap.c +++ b/security/commoncap.c @@ -133,14 +133,14 @@ int cap_settime(const struct timespec64 *ts, const struct timezone *tz) * Determine whether a process may access another, returning 0 if permission * granted, -ve if denied. */ -int cap_ptrace_access_check(struct task_struct *child, unsigned int mode) +int cap_ptrace_access_check(struct task_struct *child, unsigned int mode, + const struct cred *cred) { int ret = 0; - const struct cred *cred, *child_cred; + const struct cred *child_cred; const kernel_cap_t *caller_caps; rcu_read_lock(); - cred = current_cred(); child_cred = __task_cred(child); if (mode & PTRACE_MODE_FSCREDS) caller_caps = &cred->cap_effective; @@ -149,7 +149,7 @@ int cap_ptrace_access_check(struct task_struct *child, unsigned int mode) if (cred->user_ns == child_cred->user_ns && cap_issubset(child_cred->cap_permitted, *caller_caps)) goto out; - if (ns_capable(child_cred->user_ns, CAP_SYS_PTRACE)) + if (security_capable(cred, child_cred->user_ns, CAP_SYS_PTRACE) == 0) goto out; ret = -EPERM; out: diff --git a/security/security.c b/security/security.c index 4838e7f..70c021a 100644 --- a/security/security.c +++ b/security/security.c @@ -154,9 +154,10 @@ int security_binder_transfer_file(struct task_struct *from, return call_int_hook(binder_transfer_file, 0, from, to, file); } -int security_ptrace_access_check(struct task_struct *child, unsigned int mode) +int security_ptrace_access_check(struct task_struct *child, unsigned int mode, + const struct cred *cred) { - return call_int_hook(ptrace_access_check, 0, child, mode); + return call_int_hook(ptrace_access_check, 0, child, mode, cred); } int security_ptrace_traceme(struct task_struct *parent) diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 13185a6..f9a0be7 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -2133,10 +2133,10 @@ static int selinux_binder_transfer_file(struct task_struct *from, } static int selinux_ptrace_access_check(struct task_struct *child, - unsigned int mode) + unsigned int mode, const struct cred *cred) { if (mode & PTRACE_MODE_READ) { - u32 sid = current_sid(); + u32 sid = cred_sid(cred); u32 csid = task_sid(child); return avc_has_perm(sid, csid, SECCLASS_FILE, FILE__READ, NULL); } diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c index 87a9741..7616742 100644 --- a/security/smack/smack_lsm.c +++ b/security/smack/smack_lsm.c @@ -408,7 +408,8 @@ static inline unsigned int smk_ptrace_mode(unsigned int mode) /** * smk_ptrace_rule_check - helper for ptrace access - * @tracer: tracer process + * @tracer: tracer process - only used for auditing if @tracer_cred is set + * @tracer_cred: creds for checks, may be different from @tracer's creds * @tracee_known: label entry of the process that's about to be traced * @mode: ptrace attachment mode (PTRACE_MODE_*) * @func: name of the function that called us, used for audit @@ -416,6 +417,7 @@ static inline unsigned int smk_ptrace_mode(unsigned int mode) * Returns 0 on access granted, -error on error */ static int smk_ptrace_rule_check(struct task_struct *tracer, + const struct cred *tracer_cred, struct smack_known *tracee_known, unsigned int mode, const char *func) { @@ -431,7 +433,9 @@ static int smk_ptrace_rule_check(struct task_struct *tracer, } rcu_read_lock(); - tsp = __task_cred(tracer)->security; + if (tracer_cred == NULL) + tracer_cred = __task_cred(tracer); + tsp = tracer_cred->security; tracer_known = smk_of_task(tsp); if ((mode & PTRACE_MODE_ATTACH) && @@ -476,13 +480,14 @@ static int smk_ptrace_rule_check(struct task_struct *tracer, * * Do the capability checks. */ -static int smack_ptrace_access_check(struct task_struct *ctp, unsigned int mode) +static int smack_ptrace_access_check(struct task_struct *ctp, unsigned int mode, + const struct cred *cred) { struct smack_known *skp; skp = smk_of_task_struct(ctp); - return smk_ptrace_rule_check(current, skp, mode, __func__); + return smk_ptrace_rule_check(current, cred, skp, mode, __func__); } /** @@ -500,7 +505,8 @@ static int smack_ptrace_traceme(struct task_struct *ptp) skp = smk_of_task(current_security()); - rc = smk_ptrace_rule_check(ptp, skp, PTRACE_MODE_ATTACH, __func__); + rc = smk_ptrace_rule_check(ptp, NULL, skp, PTRACE_MODE_ATTACH, + __func__); return rc; } @@ -941,7 +947,7 @@ static int smack_bprm_set_creds(struct linux_binprm *bprm) rcu_read_lock(); tracer = ptrace_parent(current); if (likely(tracer != NULL)) - rc = smk_ptrace_rule_check(tracer, + rc = smk_ptrace_rule_check(tracer, NULL, isp->smk_task, PTRACE_MODE_ATTACH, __func__); diff --git a/security/yama/yama_lsm.c b/security/yama/yama_lsm.c index 0309f21..32a2962 100644 --- a/security/yama/yama_lsm.c +++ b/security/yama/yama_lsm.c @@ -347,7 +347,7 @@ static int ptracer_exception_found(struct task_struct *tracer, * Returns 0 if following the ptrace is allowed, -ve on error. */ static int yama_ptrace_access_check(struct task_struct *child, - unsigned int mode) + unsigned int mode, const struct cred *cred) { int rc = 0; @@ -359,7 +359,9 @@ static int yama_ptrace_access_check(struct task_struct *child, break; case YAMA_SCOPE_RELATIONAL: rcu_read_lock(); - if (!task_is_descendant(current, child) && + /* fail open for some procfs-based ATTACH accesses! */ + if ((mode & PTRACE_MODE_NON_CURRENT) == 0 && + !task_is_descendant(current, child) && !ptracer_exception_found(current, child) && !ns_capable(__task_cred(child)->user_ns, CAP_SYS_PTRACE)) rc = -EPERM; @@ -367,7 +369,8 @@ static int yama_ptrace_access_check(struct task_struct *child, break; case YAMA_SCOPE_CAPABILITY: rcu_read_lock(); - if (!ns_capable(__task_cred(child)->user_ns, CAP_SYS_PTRACE)) + if (security_capable(cred, __task_cred(child)->user_ns, + CAP_SYS_PTRACE) != 0) rc = -EPERM; rcu_read_unlock(); break;