Received: by 2002:a05:6a10:9848:0:0:0:0 with SMTP id x8csp4467967pxf; Tue, 16 Mar 2021 14:26:53 -0700 (PDT) X-Google-Smtp-Source: ABdhPJzZRzkIdsTK+OFbT4D2npT+4owF+uUb05Ljkfik8qqgZE1O3H+TMgjW5Z1E4jw/6vES23mq X-Received: by 2002:a05:6402:1c86:: with SMTP id cy6mr37798890edb.276.1615930013547; Tue, 16 Mar 2021 14:26:53 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1615930013; cv=none; d=google.com; s=arc-20160816; b=OWbXhnM3EnXHEIV8bycPS6/2nJeO98NxreSdiCVBXs0uVBtvX9VhILbAP627jvejLC IBsqkAR7ZizSdAYXkGysdmJVANdy6isycymkgO1hRJV+Jdv5JZR5zX4NrhCrR5mo/RvI EVcia/HkHhAqnJYz0F81kjtGb0FKz7bIigOcZH0Y6f8yAYGLDjQLMA9XchQnGCntMAvA T8eVwfOtB94JBNABWaLot7g8BilTlPMhBRx6uzA12ybXQ1mza0FSO+q0+meYNlUtvX+m os+DIedAHnUpJJTYLhJihWSmTMWqwrNEWUqXjn+m5+UyH1/rLR+iZqDMCYNNoCW0cHB1 HR/A== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:content-transfer-encoding:mime-version :references:in-reply-to:message-id:date:subject:cc:to:from; bh=AI3/SHrc8JvW1PPaC7L826pvMc6SSkvN75NKYxsKH1I=; b=q+tNiXUNq2jQDJV6bh/1o2EJAz4TlUdEIBo9U4Ld2q5PQCv1P7Vmqd5v3PW6S4q0Xl fGJUMv7/5fmJBOjBsYjrakKtoPZOPt0iaNq5zG5LJ+lpzHp1z6WuCSdJqg5xFiuF9hKh a5kcl+7ZbsHmEhuyCGYNBMnzO4IeRU83wHx0CNuVeCFD4l98CMChCBEose3wivLTnSse 84gft/1s4WkTVq1dqSy6U24NoPvZyX4KbOvbUL5Do6oLeZsrcD7NS4fG1bRjP1q59Fm6 tLep2Sq5ng/eQDKhYtcyQ47bfK/Ft2sAhOGfxKq1nMRMKII6K/yZIJmL8NQwOiM3ZyK2 f9kA== ARC-Authentication-Results: i=1; mx.google.com; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Return-Path: Received: from vger.kernel.org (vger.kernel.org. [23.128.96.18]) by mx.google.com with ESMTP id b13si14868630edt.436.2021.03.16.14.26.31; Tue, 16 Mar 2021 14:26:53 -0700 (PDT) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) client-ip=23.128.96.18; Authentication-Results: mx.google.com; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S231400AbhCPUg7 (ORCPT + 99 others); Tue, 16 Mar 2021 16:36:59 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:46984 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S231398AbhCPUgo (ORCPT ); Tue, 16 Mar 2021 16:36:44 -0400 Received: from smtp-190b.mail.infomaniak.ch (smtp-190b.mail.infomaniak.ch [IPv6:2001:1600:3:17::190b]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 314CFC061756 for ; Tue, 16 Mar 2021 13:36:43 -0700 (PDT) Received: from smtp-3-0001.mail.infomaniak.ch (unknown [10.4.36.108]) by smtp-2-3000.mail.infomaniak.ch (Postfix) with ESMTPS id 4F0Q7F14SVzMppGb; Tue, 16 Mar 2021 21:36:41 +0100 (CET) Received: from localhost (unknown [23.97.221.149]) by smtp-3-0001.mail.infomaniak.ch (Postfix) with ESMTPA id 4F0Q7C0M97zlh8T9; Tue, 16 Mar 2021 21:36:37 +0100 (CET) From: =?UTF-8?q?Micka=C3=ABl=20Sala=C3=BCn?= To: Al Viro , James Morris , Serge Hallyn Cc: =?UTF-8?q?Micka=C3=ABl=20Sala=C3=BCn?= , Andy Lutomirski , Casey Schaufler , Christian Brauner , Christoph Hellwig , David Howells , Dominik Brodowski , "Eric W . Biederman" , Jann Horn , John Johansen , Kees Cook , Kentaro Takeda , Tetsuo Handa , kernel-hardening@lists.openwall.com, linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org, linux-security-module@vger.kernel.org, =?UTF-8?q?Micka=C3=ABl=20Sala=C3=BCn?= Subject: [PATCH v5 1/1] fs: Allow no_new_privs tasks to call chroot(2) Date: Tue, 16 Mar 2021 21:36:33 +0100 Message-Id: <20210316203633.424794-2-mic@digikod.net> X-Mailer: git-send-email 2.30.2 In-Reply-To: <20210316203633.424794-1-mic@digikod.net> References: <20210316203633.424794-1-mic@digikod.net> MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org From: Mickaël Salaün Being able to easily change root directories enables to ease some development workflow and can be used as a tool to strengthen unprivileged security sandboxes. chroot(2) is not an access-control mechanism per se, but it can be used to limit the absolute view of the filesystem, and then limit ways to access data and kernel interfaces (e.g. /proc, /sys, /dev, etc.). Users may not wish to expose namespace complexity to potentially malicious processes, or limit their use because of limited resources. The chroot feature is much more simple (and limited) than the mount namespace, but can still be useful. As for containers, users of chroot(2) should take care of file descriptors or data accessible by other means (e.g. current working directory, leaked FDs, passed FDs, devices, mount points, etc.). There is a lot of literature that discuss the limitations of chroot, and users of this feature should be aware of the multiple ways to bypass it. Using chroot(2) for security purposes can make sense if it is combined with other features (e.g. dedicated user, seccomp, LSM access-controls, etc.). One could argue that chroot(2) is useless without a properly populated root hierarchy (i.e. without /dev and /proc). However, there are multiple use cases that don't require the chrooting process to create file hierarchies with special files nor mount points, e.g.: * A process sandboxing itself, once all its libraries are loaded, may not need files other than regular files, or even no file at all. * Some pre-populated root hierarchies could be used to chroot into, provided for instance by development environments or tailored distributions. * Processes executed in a chroot may not require access to these special files (e.g. with minimal runtimes, or by emulating some special files with a LD_PRELOADed library or seccomp). Allowing a task to change its own root directory is not a threat to the system if we can prevent confused deputy attacks, which could be performed through execution of SUID-like binaries. This can be prevented if the calling task sets PR_SET_NO_NEW_PRIVS on itself with prctl(2). To only affect this task, its filesystem information must not be shared with other tasks, which can be achieved by not passing CLONE_FS to clone(2). A similar no_new_privs check is already used by seccomp to avoid the same kind of security issues. Furthermore, because of its security use and to avoid giving a new way for attackers to get out of a chroot (e.g. using /proc//root, or chroot/chdir), an unprivileged chroot is only allowed if the calling process is not already chrooted. This limitation is the same as for creating user namespaces. This change may not impact systems relying on other permission models than POSIX capabilities (e.g. Tomoyo). Being able to use chroot(2) on such systems may require to update their security policies. Only the chroot system call is relaxed with this no_new_privs check; the init_chroot() helper doesn't require such change. Allowing unprivileged users to use chroot(2) is one of the initial objectives of no_new_privs: https://www.kernel.org/doc/html/latest/userspace-api/no_new_privs.html This patch is a follow-up of a previous one sent by Andy Lutomirski: https://lore.kernel.org/lkml/0e2f0f54e19bff53a3739ecfddb4ffa9a6dbde4d.1327858005.git.luto@amacapital.net/ Cc: Al Viro Cc: Andy Lutomirski Cc: Christian Brauner Cc: Christoph Hellwig Cc: David Howells Cc: Dominik Brodowski Cc: Eric W. Biederman Cc: James Morris Cc: Jann Horn Cc: John Johansen Cc: Kentaro Takeda Cc: Serge Hallyn Cc: Tetsuo Handa Signed-off-by: Mickaël Salaün Reviewed-by: Kees Cook Link: https://lore.kernel.org/r/20210316203633.424794-2-mic@digikod.net --- Changes since v4: * Use READ_ONCE(current->fs->users) (found by Jann Horn). * Remove ambiguous example in commit description. * Add Reviewed-by Kees Cook. Changes since v3: * Move the new permission checks to a dedicated helper current_chroot_allowed() to make the code easier to read and align with user_path_at(), path_permission() and security_path_chroot() calls (suggested by Kees Cook). * Remove now useless included file. * Extend commit description. * Rebase on v5.12-rc3 . Changes since v2: * Replace path_is_under() check with current_chrooted() to gain the same protection as create_user_ns() (suggested by Jann Horn). See commit 3151527ee007 ("userns: Don't allow creation if the user is chrooted") Changes since v1: * Replace custom is_path_beneath() with existing path_is_under(). --- fs/open.c | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/fs/open.c b/fs/open.c index e53af13b5835..480010a551b2 100644 --- a/fs/open.c +++ b/fs/open.c @@ -532,6 +532,24 @@ SYSCALL_DEFINE1(fchdir, unsigned int, fd) return error; } +static inline int current_chroot_allowed(void) +{ + /* + * Changing the root directory for the calling task (and its future + * children) requires that this task has CAP_SYS_CHROOT in its + * namespace, or be running with no_new_privs and not sharing its + * fs_struct and not escaping its current root (cf. create_user_ns()). + * As for seccomp, checking no_new_privs avoids scenarios where + * unprivileged tasks can affect the behavior of privileged children. + */ + if (task_no_new_privs(current) && READ_ONCE(current->fs->users) == 1 && + !current_chrooted()) + return 0; + if (ns_capable(current_user_ns(), CAP_SYS_CHROOT)) + return 0; + return -EPERM; +} + SYSCALL_DEFINE1(chroot, const char __user *, filename) { struct path path; @@ -546,9 +564,10 @@ SYSCALL_DEFINE1(chroot, const char __user *, filename) if (error) goto dput_and_out; - error = -EPERM; - if (!ns_capable(current_user_ns(), CAP_SYS_CHROOT)) + error = current_chroot_allowed(); + if (error) goto dput_and_out; + error = security_path_chroot(&path); if (error) goto dput_and_out; -- 2.30.2