From patchwork Wed Nov 9 03:47:03 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Seth Forshee X-Patchwork-Id: 692589 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from huckleberry.canonical.com (huckleberry.canonical.com [91.189.94.19]) by ozlabs.org (Postfix) with ESMTP id 3tDBvz4tVlz9t1T; Wed, 9 Nov 2016 14:47:43 +1100 (AEDT) Authentication-Results: ozlabs.org; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=canonical-com.20150623.gappssmtp.com header.i=@canonical-com.20150623.gappssmtp.com header.b="pQOXe4iG"; dkim-atps=neutral Received: from localhost ([127.0.0.1] helo=huckleberry.canonical.com) by huckleberry.canonical.com with esmtp (Exim 4.76) (envelope-from ) id 1c4JrQ-0002KY-A9; Wed, 09 Nov 2016 03:47:40 +0000 Received: from mail-it0-f54.google.com ([209.85.214.54]) by huckleberry.canonical.com with esmtps (TLS1.0:RSA_AES_128_CBC_SHA1:16) (Exim 4.76) (envelope-from ) id 1c4Jqz-00027W-Bw for kernel-team@lists.ubuntu.com; Wed, 09 Nov 2016 03:47:13 +0000 Received: by mail-it0-f54.google.com with SMTP id e187so187309608itc.0 for ; Tue, 08 Nov 2016 19:47:13 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=canonical-com.20150623.gappssmtp.com; s=20150623; h=from:to:subject:date:message-id:in-reply-to:references; bh=IGpRr7cjHAj1nNMwlFH5ZoOBRs05OOqx5iOhOby/rFk=; b=pQOXe4iGGKWtvwxQeag+jVyqK8MTTm9l+AzAH6XvID0xKySHn8lvPBenQJCwNs9mcV gQA10xT4BoDNkXBemvfRMw36EpJo2PFagwLPGxUXgnWUhYVss3fQ5Wi1jsodw9+ny3Sp kaxU053A6BjbqPADW9Bs+kKeoiH2WHbQPlVt+kqztAFix3I180Jge2SQ3O0qxcRVYoXq 4XGEh7NfZzyvZ5x1TXTRYfGAl+UuZMQ3ZBQBBGoxM2LTxOumdmCI54ajtY1IZYxg8llK mRy9ZgpIKJT30MDlrRgSDBLw6GcVRv8KbOUxluFhx5qkBmfhGWg6e6CWtqoJxCG8dkNx EvJg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:from:to:subject:date:message-id:in-reply-to :references; bh=IGpRr7cjHAj1nNMwlFH5ZoOBRs05OOqx5iOhOby/rFk=; b=WSNx4hlYGk9BDc9+B3Wol0Ueyuylis6wYcMSjQ8MFNzvYzTDqE0tL3k13edeZqst80 Wnd4qwIbmiv2u46JaWT3vEXQ49e8AwSdfTWjbMhWWMUC3GC64L3qeGjuR3yyHqtwyRm3 594hyYPi3yKZhoaVywNlXBVetOGF89x3QWnDJTKX77+1M5pKs3WBh52PwC/A8tW05ERK 2fGPeZJQfA/cdVRHb4X2yolHJOzGkwLfrbLQH2UCI0eVLmiHX2/rWD9D2QfKfJBy1I0R 3mxDjSiXg928OKzRrwAQoA4ICV5J6NPFXiFEYRDYOIk7l30tFtRh3lqxc3gKBcAcq4dZ braw== X-Gm-Message-State: ABUngvcT3gD3Ys60vXxgFqJ0kdT8WCxHAnA0DBB0e6AAmkYi8r1mMLlIgtmK1eP1wpKtEqOf X-Received: by 10.107.6.160 with SMTP id f32mr15239151ioi.201.1478663232105; Tue, 08 Nov 2016 19:47:12 -0800 (PST) Received: from localhost ([2605:a601:aaf:a920:cd0b:269a:552f:c442]) by smtp.gmail.com with ESMTPSA id b134sm13422767ioa.13.2016.11.08.19.47.11 for (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Tue, 08 Nov 2016 19:47:11 -0800 (PST) From: Seth Forshee To: kernel-team@lists.ubuntu.com Subject: [PATCH 2/2][xenial] UBUNTU: SAUCE: (no-up) mm: Add a user_ns owner to mm_struct and fix ptrace permission checks Date: Tue, 8 Nov 2016 21:47:03 -0600 Message-Id: <1478663225-108884-7-git-send-email-seth.forshee@canonical.com> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1478663225-108884-1-git-send-email-seth.forshee@canonical.com> References: <1478663225-108884-1-git-send-email-seth.forshee@canonical.com> X-BeenThere: kernel-team@lists.ubuntu.com X-Mailman-Version: 2.1.14 Precedence: list List-Id: Kernel team discussions List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Errors-To: kernel-team-bounces@lists.ubuntu.com Sender: kernel-team-bounces@lists.ubuntu.com From: "Eric W. Biederman" During exec dumpable is cleared if the file that is being executed is not readable by the user executing the file. A bug in ptrace_may_access allows reading the file if the executable happens to enter into a subordinate user namespace (aka clone(CLONE_NEWUSER), unshare(CLONE_NEWUSER), or setns(fd, CLONE_NEWUSER). This problem is fixed with only necessary userspace breakage by adding a user namespace owner to mm_struct, captured at the time of exec, so it is clear in which user namespace CAP_SYS_PTRACE must be present in to be able to safely give read permission to the executable. The function ptrace_may_access is modified to verify that the ptracer has CAP_SYS_ADMIN in task->mm->user_ns instead of task->cred->user_ns. This ensures that if the task changes it's cred into a subordinate user namespace it does not become ptraceable. The function ptrace_attach is modified to only set PT_PTRACE_CAP when CAP_SYS_PTRACE is held over task->mm->user_ns. The intent of PT_PTRACE_CAP is to be a flag to note that whatever permission changes the task might go through the tracer has sufficient permissions for it not to be an issue. task->cred->user_ns is always the same as or descendent of mm->user_ns. Which guarantees that having CAP_SYS_PTRACE over mm->user_ns is the worst case for the tasks credentials. To prevent regressions mm->dumpable and mm->user_ns are not considered when a task has no mm. As simply failing ptrace_may_attach causes regressions in privileged applications attempting to read things such as /proc//stat Cc: stable@vger.kernel.org Acked-by: Kees Cook Tested-by: Cyrill Gorcunov Fixes: 8409cca70561 ("userns: allow ptrace from non-init user namespaces") Signed-off-by: "Eric W. Biederman" (cherry picked from commit 2e41414828bb0b066bde2f156cfa848c38531edf linux-next) CVE-2015-8709 Signed-off-by: Seth Forshee --- include/linux/mm_types.h | 1 + kernel/fork.c | 9 ++++++--- kernel/ptrace.c | 26 +++++++++++--------------- mm/init-mm.c | 2 ++ 4 files changed, 20 insertions(+), 18 deletions(-) diff --git a/include/linux/mm_types.h b/include/linux/mm_types.h index c3a3760622af..c0052c644687 100644 --- a/include/linux/mm_types.h +++ b/include/linux/mm_types.h @@ -471,6 +471,7 @@ struct mm_struct { */ struct task_struct __rcu *owner; #endif + struct user_namespace *user_ns; /* store ref to file /proc//exe symlink points to */ struct file __rcu *exe_file; diff --git a/kernel/fork.c b/kernel/fork.c index 06949d8da922..c735d440d5ee 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -591,7 +591,8 @@ static void mm_init_owner(struct mm_struct *mm, struct task_struct *p) #endif } -static struct mm_struct *mm_init(struct mm_struct *mm, struct task_struct *p) +static struct mm_struct *mm_init(struct mm_struct *mm, struct task_struct *p, + struct user_namespace *user_ns) { mm->mmap = NULL; mm->mm_rb = RB_ROOT; @@ -631,6 +632,7 @@ static struct mm_struct *mm_init(struct mm_struct *mm, struct task_struct *p) if (init_new_context(p, mm)) goto fail_nocontext; + mm->user_ns = get_user_ns(user_ns); return mm; fail_nocontext: @@ -676,7 +678,7 @@ struct mm_struct *mm_alloc(void) return NULL; memset(mm, 0, sizeof(*mm)); - return mm_init(mm, current); + return mm_init(mm, current, current_user_ns()); } /* @@ -691,6 +693,7 @@ void __mmdrop(struct mm_struct *mm) destroy_context(mm); mmu_notifier_mm_destroy(mm); check_mm(mm); + put_user_ns(mm->user_ns); free_mm(mm); } EXPORT_SYMBOL_GPL(__mmdrop); @@ -948,7 +951,7 @@ static struct mm_struct *dup_mm(struct task_struct *tsk) memcpy(mm, oldmm, sizeof(*mm)); - if (!mm_init(mm, tsk)) + if (!mm_init(mm, tsk, mm->user_ns)) goto fail_nomem; err = dup_mmap(mm, oldmm); diff --git a/kernel/ptrace.c b/kernel/ptrace.c index 3189e51db7e8..21b60e0a3056 100644 --- a/kernel/ptrace.c +++ b/kernel/ptrace.c @@ -219,7 +219,7 @@ static int ptrace_has_cap(struct user_namespace *ns, unsigned int mode) static int __ptrace_may_access(struct task_struct *task, unsigned int mode) { const struct cred *cred = current_cred(), *tcred; - int dumpable = 0; + struct mm_struct *mm; kuid_t caller_uid; kgid_t caller_gid; @@ -270,16 +270,11 @@ static int __ptrace_may_access(struct task_struct *task, unsigned int mode) return -EPERM; ok: rcu_read_unlock(); - smp_rmb(); - if (task->mm) - dumpable = get_dumpable(task->mm); - rcu_read_lock(); - if (dumpable != SUID_DUMP_USER && - !ptrace_has_cap(__task_cred(task)->user_ns, mode)) { - rcu_read_unlock(); - return -EPERM; - } - rcu_read_unlock(); + mm = task->mm; + if (mm && + ((get_dumpable(mm) != SUID_DUMP_USER) && + !ptrace_has_cap(mm->user_ns, mode))) + return -EPERM; return security_ptrace_access_check(task, mode); } @@ -330,6 +325,11 @@ 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; @@ -343,10 +343,6 @@ static int ptrace_attach(struct task_struct *task, long request, if (seize) flags |= PT_SEIZED; - rcu_read_lock(); - if (ns_capable(__task_cred(task)->user_ns, CAP_SYS_PTRACE)) - flags |= PT_PTRACE_CAP; - rcu_read_unlock(); task->ptrace = flags; __ptrace_link(task, current); diff --git a/mm/init-mm.c b/mm/init-mm.c index a56a851908d2..975e49f00f34 100644 --- a/mm/init-mm.c +++ b/mm/init-mm.c @@ -6,6 +6,7 @@ #include #include +#include #include #include @@ -21,5 +22,6 @@ struct mm_struct init_mm = { .mmap_sem = __RWSEM_INITIALIZER(init_mm.mmap_sem), .page_table_lock = __SPIN_LOCK_UNLOCKED(init_mm.page_table_lock), .mmlist = LIST_HEAD_INIT(init_mm.mmlist), + .user_ns = &init_user_ns, INIT_MM_CONTEXT(init_mm) };