From patchwork Fri Aug 7 22:49:39 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Lokesh Gidra X-Patchwork-Id: 11706167 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 1C450138A for ; Fri, 7 Aug 2020 22:50:40 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id EE69522CA1 for ; Fri, 7 Aug 2020 22:50:39 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="VbkTLTWQ" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726238AbgHGWuV (ORCPT ); Fri, 7 Aug 2020 18:50:21 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:37648 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726224AbgHGWuU (ORCPT ); Fri, 7 Aug 2020 18:50:20 -0400 Received: from mail-yb1-xb4a.google.com (mail-yb1-xb4a.google.com [IPv6:2607:f8b0:4864:20::b4a]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 6BE25C061757 for ; Fri, 7 Aug 2020 15:50:20 -0700 (PDT) Received: by mail-yb1-xb4a.google.com with SMTP id 7so4622606ybl.5 for ; Fri, 07 Aug 2020 15:50:20 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20161025; h=date:in-reply-to:message-id:mime-version:references:subject:from:to :cc; bh=QIWXbljuhkUfYg9+OdRLfSQFfEGInZZcRka+NBb2Kkg=; b=VbkTLTWQbKV4Psh4Xlau61AL5QWBilATI1P+KBI78kIIzKJeQezay/DERqyrqeAo5C fWEY2y/HGex8q0aNBo16JRTo9iLJrgGd2hxEok5YR2bp793CU/vc8G0rHuRIQU5G4csk dzzcH0MZGaNoKSlOhw6sYtzkUJywECa9BFlef4FxWXUEfixhl8UoD/FxLjylGIGbDMyM 2vtwVu+ZwIAc24ntA2p6uABPGsU1ftFhvI8uhlE6UHfEWsOZMDAR7ssPer01wvvBWHG9 bGdWVF15z6pWpEcZBpm8cIl+hn4Y9MbJZ2DJLL55V3Ay61cKsAnvRbPH3ywEofQEQRI7 0qaQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:date:in-reply-to:message-id:mime-version :references:subject:from:to:cc; bh=QIWXbljuhkUfYg9+OdRLfSQFfEGInZZcRka+NBb2Kkg=; b=KZ02lg5KiZt48/SaHjrJnJsvlqDmswqJ+/6nJmpQQZz9TTJkr87rEUPhKYQTu+ogRY N8lnC/nmIzwsSN93N+e/TdE0ycEL2uaMMSPwEk+2Lx9eCTDZqZqpC/ZvqSr+1XlRN7N3 ZItOiwAaNDiHBRG+JOYxmFC4IHJIZTm7LN+awo71tSuI5jIRWAGQiiuZpxI5SgykVrhi +ly8j2Eh+eLrS3Hy8Lan3UhZHU8YZ/d7jk/wsvF4EjMo2Ur6twwX77bOXthsetF84xDR 7LwRlQyvRokKaW0nPmTjiWeSqX1dqQmVTnFhyBBmIcGJ1SKqe0T8i8YF7emKag8vkI4G To+Q== X-Gm-Message-State: AOAM5300aw4Uj5GXTm+ariZLNE0aZ/vZ6expjS9IL2X0MXL2Vr2l87fN hzu2BOHhtf365vu7L6WW5ZXfV+xWXUpbqV64iQ== X-Google-Smtp-Source: ABdhPJyX658AkGnB52cUAPwrcm3lbJOcGrf4zsA2JIlKJmwIqkPtaSnKxTYtFsJD7nQ7KVWEFu1XPddnDs6RAfW82A== X-Received: by 2002:a25:cbd6:: with SMTP id b205mr25022420ybg.137.1596840619485; Fri, 07 Aug 2020 15:50:19 -0700 (PDT) Date: Fri, 7 Aug 2020 15:49:39 -0700 In-Reply-To: <20200807224941.3440722-1-lokeshgidra@google.com> Message-Id: <20200807224941.3440722-2-lokeshgidra@google.com> Mime-Version: 1.0 References: <20200807224941.3440722-1-lokeshgidra@google.com> X-Mailer: git-send-email 2.28.0.236.gb10cc79966-goog Subject: [PATCH v6 1/3] Add a new LSM-supporting anonymous inode interface From: Lokesh Gidra To: Alexander Viro , James Morris , Stephen Smalley , Casey Schaufler , Eric Biggers Cc: "Serge E. Hallyn" , Paul Moore , Eric Paris , Lokesh Gidra , Daniel Colascione , Kees Cook , "Eric W. Biederman" , KP Singh , David Howells , Thomas Cedeno , Anders Roxell , Sami Tolvanen , Matthew Garrett , Aaron Goidel , Randy Dunlap , "Joel Fernandes (Google)" , YueHaibing , Christian Brauner , Alexei Starovoitov , Alexey Budankov , Adrian Reber , Aleksa Sarai , linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org, linux-security-module@vger.kernel.org, selinux@vger.kernel.org, kaleshsingh@google.com, calin@google.com, surenb@google.com, nnk@google.com, jeffv@google.com, kernel-team@android.com, Daniel Colascione Sender: selinux-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: selinux@vger.kernel.org From: Daniel Colascione This change adds two new functions, anon_inode_getfile_secure and anon_inode_getfd_secure, that create anonymous-node files with individual non-S_PRIVATE inodes to which security modules can apply policy. Existing callers continue using the original singleton-inode kind of anonymous-inode file. We can transition anonymous inode users to the new kind of anonymous inode in individual patches for the sake of bisection and review. The new functions accept an optional context_inode parameter that callers can use to provide additional contextual information to security modules, e.g., indicating that one anonymous struct file is a logical child of another, allowing a security model to propagate security information from one to the other. Signed-off-by: Daniel Colascione [Fix comment documenting return values of inode_init_security_anon()] [Add context_inode description in comments to anon_inode_getfd_secure() and anon_inode_getfile_secure()] [Make _anon_inode_getfile() static] [Use correct error cast in _anon_inode_getfile()] Signed-off-by: Lokesh Gidra --- fs/anon_inodes.c | 193 ++++++++++++++++++++++++++-------- include/linux/anon_inodes.h | 13 +++ include/linux/lsm_hook_defs.h | 2 + include/linux/lsm_hooks.h | 7 ++ include/linux/security.h | 3 + security/security.c | 9 ++ 6 files changed, 186 insertions(+), 41 deletions(-) diff --git a/fs/anon_inodes.c b/fs/anon_inodes.c index 89714308c25b..4e09915fe847 100644 --- a/fs/anon_inodes.c +++ b/fs/anon_inodes.c @@ -55,61 +55,109 @@ static struct file_system_type anon_inode_fs_type = { .kill_sb = kill_anon_super, }; -/** - * anon_inode_getfile - creates a new file instance by hooking it up to an - * anonymous inode, and a dentry that describe the "class" - * of the file - * - * @name: [in] name of the "class" of the new file - * @fops: [in] file operations for the new file - * @priv: [in] private data for the new file (will be file's private_data) - * @flags: [in] flags - * - * Creates a new file by hooking it on a single inode. This is useful for files - * that do not need to have a full-fledged inode in order to operate correctly. - * All the files created with anon_inode_getfile() will share a single inode, - * hence saving memory and avoiding code duplication for the file/inode/dentry - * setup. Returns the newly created file* or an error pointer. - */ -struct file *anon_inode_getfile(const char *name, - const struct file_operations *fops, - void *priv, int flags) +static struct inode *anon_inode_make_secure_inode( + const char *name, + const struct inode *context_inode) { + struct inode *inode; + const struct qstr qname = QSTR_INIT(name, strlen(name)); + int error; + + inode = alloc_anon_inode(anon_inode_mnt->mnt_sb); + if (IS_ERR(inode)) + return inode; + inode->i_flags &= ~S_PRIVATE; + error = security_inode_init_security_anon( + inode, &qname, context_inode); + if (error) { + iput(inode); + return ERR_PTR(error); + } + return inode; +} + +static struct file *_anon_inode_getfile(const char *name, + const struct file_operations *fops, + void *priv, int flags, + const struct inode *context_inode, + bool secure) +{ + struct inode *inode; struct file *file; - if (IS_ERR(anon_inode_inode)) - return ERR_PTR(-ENODEV); + if (secure) { + inode = anon_inode_make_secure_inode( + name, context_inode); + if (IS_ERR(inode)) + return ERR_CAST(inode); + } else { + inode = anon_inode_inode; + if (IS_ERR(inode)) + return ERR_PTR(-ENODEV); + /* + * We know the anon_inode inode count is always + * greater than zero, so ihold() is safe. + */ + ihold(inode); + } - if (fops->owner && !try_module_get(fops->owner)) - return ERR_PTR(-ENOENT); + if (fops->owner && !try_module_get(fops->owner)) { + file = ERR_PTR(-ENOENT); + goto err; + } - /* - * We know the anon_inode inode count is always greater than zero, - * so ihold() is safe. - */ - ihold(anon_inode_inode); - file = alloc_file_pseudo(anon_inode_inode, anon_inode_mnt, name, + file = alloc_file_pseudo(inode, anon_inode_mnt, name, flags & (O_ACCMODE | O_NONBLOCK), fops); if (IS_ERR(file)) goto err; - file->f_mapping = anon_inode_inode->i_mapping; + file->f_mapping = inode->i_mapping; file->private_data = priv; return file; err: - iput(anon_inode_inode); + iput(inode); module_put(fops->owner); return file; } -EXPORT_SYMBOL_GPL(anon_inode_getfile); /** - * anon_inode_getfd - creates a new file instance by hooking it up to an - * anonymous inode, and a dentry that describe the "class" - * of the file + * anon_inode_getfile_secure - creates a new file instance by hooking + * it up to a new anonymous inode and a + * dentry that describe the "class" of the + * file. Make it possible to use security + * modules to control access to the + * new file. + * + * @name: [in] name of the "class" of the new file + * @fops: [in] file operations for the new file + * @priv: [in] private data for the new file (will be file's private_data) + * @flags: [in] flags + * @context_inode [in] inode for additional contextual info to security modules + * + * Creates a new file by hooking it on an unspecified inode. This is + * useful for files that do not need to have a full-fledged filesystem + * to operate correctly. All the files created with + * anon_inode_getfile_secure() will have distinct inodes, avoiding + * code duplication for the file/inode/dentry setup. Returns the + * newly created file* or an error pointer. + */ +struct file *anon_inode_getfile_secure(const char *name, + const struct file_operations *fops, + void *priv, int flags, + const struct inode *context_inode) +{ + return _anon_inode_getfile( + name, fops, priv, flags, context_inode, true); +} +EXPORT_SYMBOL_GPL(anon_inode_getfile_secure); + +/** + * anon_inode_getfile - creates a new file instance by hooking it up to an + * anonymous inode, and a dentry that describe the "class" + * of the file * * @name: [in] name of the "class" of the new file * @fops: [in] file operations for the new file @@ -118,12 +166,23 @@ EXPORT_SYMBOL_GPL(anon_inode_getfile); * * Creates a new file by hooking it on a single inode. This is useful for files * that do not need to have a full-fledged inode in order to operate correctly. - * All the files created with anon_inode_getfd() will share a single inode, + * All the files created with anon_inode_getfile() will share a single inode, * hence saving memory and avoiding code duplication for the file/inode/dentry - * setup. Returns new descriptor or an error code. + * setup. Returns the newly created file* or an error pointer. */ -int anon_inode_getfd(const char *name, const struct file_operations *fops, - void *priv, int flags) +struct file *anon_inode_getfile(const char *name, + const struct file_operations *fops, + void *priv, int flags) +{ + return _anon_inode_getfile(name, fops, priv, flags, NULL, false); +} +EXPORT_SYMBOL_GPL(anon_inode_getfile); + +static int _anon_inode_getfd(const char *name, + const struct file_operations *fops, + void *priv, int flags, + const struct inode *context_inode, + bool secure) { int error, fd; struct file *file; @@ -133,7 +192,8 @@ int anon_inode_getfd(const char *name, const struct file_operations *fops, return error; fd = error; - file = anon_inode_getfile(name, fops, priv, flags); + file = _anon_inode_getfile(name, fops, priv, flags, context_inode, + secure); if (IS_ERR(file)) { error = PTR_ERR(file); goto err_put_unused_fd; @@ -146,6 +206,58 @@ int anon_inode_getfd(const char *name, const struct file_operations *fops, put_unused_fd(fd); return error; } + +/** + * anon_inode_getfd_secure - creates a new file instance by hooking it + * up to a new anonymous inode and a dentry + * that describe the "class" of the file. + * Make it possible to use security modules + * to control access to the new file. + * + * @name: [in] name of the "class" of the new file + * @fops: [in] file operations for the new file + * @priv: [in] private data for the new file (will be file's private_data) + * @flags: [in] flags + * @context_inode [in] inode for additional contextual info to security modules + * + * Creates a new file by hooking it on an unspecified inode. This is + * useful for files that do not need to have a full-fledged filesystem + * to operate correctly. All the files created with + * anon_inode_getfile_secure() will have distinct inodes, avoiding + * code duplication for the file/inode/dentry setup. Returns a newly + * created file descriptor or an error code. + */ +int anon_inode_getfd_secure(const char *name, const struct file_operations *fops, + void *priv, int flags, + const struct inode *context_inode) +{ + return _anon_inode_getfd(name, fops, priv, flags, + context_inode, true); +} +EXPORT_SYMBOL_GPL(anon_inode_getfd_secure); + +/** + * anon_inode_getfd - creates a new file instance by hooking it up to + * an anonymous inode and a dentry that describe + * the "class" of the file + * + * @name: [in] name of the "class" of the new file + * @fops: [in] file operations for the new file + * @priv: [in] private data for the new file (will be file's private_data) + * @flags: [in] flags + * + * Creates a new file by hooking it on a single inode. This is + * useful for files that do not need to have a full-fledged inode in + * order to operate correctly. All the files created with + * anon_inode_getfile() will use the same singleton inode, reducing + * memory use and avoiding code duplication for the file/inode/dentry + * setup. Returns a newly created file descriptor or an error code. + */ +int anon_inode_getfd(const char *name, const struct file_operations *fops, + void *priv, int flags) +{ + return _anon_inode_getfd(name, fops, priv, flags, NULL, false); +} EXPORT_SYMBOL_GPL(anon_inode_getfd); static int __init anon_inode_init(void) @@ -162,4 +274,3 @@ static int __init anon_inode_init(void) } fs_initcall(anon_inode_init); - diff --git a/include/linux/anon_inodes.h b/include/linux/anon_inodes.h index d0d7d96261ad..67bd85d92dca 100644 --- a/include/linux/anon_inodes.h +++ b/include/linux/anon_inodes.h @@ -10,12 +10,25 @@ #define _LINUX_ANON_INODES_H struct file_operations; +struct inode; + +struct file *anon_inode_getfile_secure(const char *name, + const struct file_operations *fops, + void *priv, int flags, + const struct inode *context_inode); struct file *anon_inode_getfile(const char *name, const struct file_operations *fops, void *priv, int flags); + +int anon_inode_getfd_secure(const char *name, + const struct file_operations *fops, + void *priv, int flags, + const struct inode *context_inode); + int anon_inode_getfd(const char *name, const struct file_operations *fops, void *priv, int flags); + #endif /* _LINUX_ANON_INODES_H */ diff --git a/include/linux/lsm_hook_defs.h b/include/linux/lsm_hook_defs.h index af998f93d256..613a3d6306c2 100644 --- a/include/linux/lsm_hook_defs.h +++ b/include/linux/lsm_hook_defs.h @@ -113,6 +113,8 @@ LSM_HOOK(void, LSM_RET_VOID, inode_free_security, struct inode *inode) LSM_HOOK(int, 0, inode_init_security, struct inode *inode, struct inode *dir, const struct qstr *qstr, const char **name, void **value, size_t *len) +LSM_HOOK(int, 0, inode_init_security_anon, struct inode *inode, + const struct qstr *name, const struct inode *context_inode) LSM_HOOK(int, 0, inode_create, struct inode *dir, struct dentry *dentry, umode_t mode) LSM_HOOK(int, 0, inode_link, struct dentry *old_dentry, struct inode *dir, diff --git a/include/linux/lsm_hooks.h b/include/linux/lsm_hooks.h index 95b7c1d32062..22847380c26c 100644 --- a/include/linux/lsm_hooks.h +++ b/include/linux/lsm_hooks.h @@ -233,6 +233,13 @@ * Returns 0 if @name and @value have been successfully set, * -EOPNOTSUPP if no security attribute is needed, or * -ENOMEM on memory allocation failure. + * @inode_init_security_anon: + * Set up a secure anonymous inode. + * @inode contains the inode structure + * @name name of the anonymous inode class + * @context_inode optional related inode + * Returns 0 on success, -EACCESS if the security module denies the + * creation of this inode, or another -errno upon other errors. * @inode_create: * Check permission to create a regular file. * @dir contains inode structure of the parent of the new file. diff --git a/include/linux/security.h b/include/linux/security.h index 0a0a03b36a3b..95c133a8f8bb 100644 --- a/include/linux/security.h +++ b/include/linux/security.h @@ -322,6 +322,9 @@ void security_inode_free(struct inode *inode); int security_inode_init_security(struct inode *inode, struct inode *dir, const struct qstr *qstr, initxattrs initxattrs, void *fs_data); +int security_inode_init_security_anon(struct inode *inode, + const struct qstr *name, + const struct inode *context_inode); int security_old_inode_init_security(struct inode *inode, struct inode *dir, const struct qstr *qstr, const char **name, void **value, size_t *len); diff --git a/security/security.c b/security/security.c index 70a7ad357bc6..149b3f024e2d 100644 --- a/security/security.c +++ b/security/security.c @@ -1057,6 +1057,15 @@ int security_inode_init_security(struct inode *inode, struct inode *dir, } EXPORT_SYMBOL(security_inode_init_security); +int +security_inode_init_security_anon(struct inode *inode, + const struct qstr *name, + const struct inode *context_inode) +{ + return call_int_hook(inode_init_security_anon, 0, inode, name, + context_inode); +} + int security_old_inode_init_security(struct inode *inode, struct inode *dir, const struct qstr *qstr, const char **name, void **value, size_t *len) From patchwork Fri Aug 7 22:49:40 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Lokesh Gidra X-Patchwork-Id: 11706155 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 8A3F314B7 for ; Fri, 7 Aug 2020 22:50:31 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 6AE3822CBB for ; Fri, 7 Aug 2020 22:50:31 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="SZFP3fsZ" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726352AbgHGWu0 (ORCPT ); Fri, 7 Aug 2020 18:50:26 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:37668 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726316AbgHGWuY (ORCPT ); Fri, 7 Aug 2020 18:50:24 -0400 Received: from mail-qt1-x849.google.com (mail-qt1-x849.google.com [IPv6:2607:f8b0:4864:20::849]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id F0C8AC061757 for ; Fri, 7 Aug 2020 15:50:23 -0700 (PDT) Received: by mail-qt1-x849.google.com with SMTP id b18so2746738qte.18 for ; Fri, 07 Aug 2020 15:50:23 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20161025; h=date:in-reply-to:message-id:mime-version:references:subject:from:to :cc; bh=5LUPe8BT+jHh/tZlanYghI1SrJggIU5nJyl+FoUY+zw=; b=SZFP3fsZ2jQjHSSnMI+Z8i/5NI90k34O3AvqbW76psCndf30+itZlYlkH6wYGU2eQ+ UZgQhQmVZQ5Dw01H1xwXtPGwvxMhqcENTM3IFDc+4tgm2eln9Oa0+F2DQLUFVoZAmkFL 4yAqkSB8i45M+hEZ4VheqrsQf6zjEib8GMM2TIQpo+a6Oxlmled6yX5X0jDpQ1sqz2bd w+5f0XoFNB/0/IKFQM/69VGaaI/Aji0zwITUHJdzeKzsvSVe6CUM3bFZL+9PtZP5tmcl x+4dJpKNpBZQdmC8lh1JTHPEbzbwyoGFOlz+cpmLUekQsWUSEnlMmcre9w4x+5gq05NA Eluw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:date:in-reply-to:message-id:mime-version :references:subject:from:to:cc; bh=5LUPe8BT+jHh/tZlanYghI1SrJggIU5nJyl+FoUY+zw=; b=Q7KC25SxZlRhm4E9LTUAAg3xjwVENj0aZLKT1zimUqaee7r5eDegx5efy+LX9D5Ogt lDsv6e6g/1Uu0TOzUNaXknolCkb/LV8ZMVdfqQv1zDuRZAyqzbrouIML0LfDS7DwRqLu COj2Ys6JGI4mpOsxQlZLeX3Spz7Hk3i9QDSj7yxNiL6LfKRDWWt/PWU8BVQkIGt8aaQS JSe9DImB0UvAZGKthHvZVVH0+ZUPFKLiv/Bvh2xIVpF2NgqP/9W328J3etBsQ4BW7JKw gNb4jlpuryFNW2VpFF2foGHkHGTTgRD+IuA8tlRWM6nW759yHJI/MhAGCL9CiH0XjgaQ nuqA== X-Gm-Message-State: AOAM533MflFO0jJTtoivTRk5J0ruW9AbkQL+Bc17VIl00MB8TGknyJC0 yQ7rvHXXlXKKKbLQhE0BCgcAoblG8EbHcmSyhw== X-Google-Smtp-Source: ABdhPJytkJJ1Im9EoucEn8GQ70mfklaLWFe9apw9X6eW8WrUOXqtG852zHNYsyhsZg0X1RHy8lmIHoP6OW3uJ5F2tw== X-Received: by 2002:a0c:fdeb:: with SMTP id m11mr17231144qvu.103.1596840623013; Fri, 07 Aug 2020 15:50:23 -0700 (PDT) Date: Fri, 7 Aug 2020 15:49:40 -0700 In-Reply-To: <20200807224941.3440722-1-lokeshgidra@google.com> Message-Id: <20200807224941.3440722-3-lokeshgidra@google.com> Mime-Version: 1.0 References: <20200807224941.3440722-1-lokeshgidra@google.com> X-Mailer: git-send-email 2.28.0.236.gb10cc79966-goog Subject: [PATCH v6 2/3] Teach SELinux about anonymous inodes From: Lokesh Gidra To: Alexander Viro , James Morris , Stephen Smalley , Casey Schaufler , Eric Biggers Cc: "Serge E. Hallyn" , Paul Moore , Eric Paris , Lokesh Gidra , Daniel Colascione , Kees Cook , "Eric W. Biederman" , KP Singh , David Howells , Thomas Cedeno , Anders Roxell , Sami Tolvanen , Matthew Garrett , Aaron Goidel , Randy Dunlap , "Joel Fernandes (Google)" , YueHaibing , Christian Brauner , Alexei Starovoitov , Alexey Budankov , Adrian Reber , Aleksa Sarai , linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org, linux-security-module@vger.kernel.org, selinux@vger.kernel.org, kaleshsingh@google.com, calin@google.com, surenb@google.com, nnk@google.com, jeffv@google.com, kernel-team@android.com, Daniel Colascione , Andrew Morton Sender: selinux-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: selinux@vger.kernel.org From: Daniel Colascione This change uses the anon_inodes and LSM infrastructure introduced in the previous patch to give SELinux the ability to control anonymous-inode files that are created using the new _secure() anon_inodes functions. A SELinux policy author detects and controls these anonymous inodes by adding a name-based type_transition rule that assigns a new security type to anonymous-inode files created in some domain. The name used for the name-based transition is the name associated with the anonymous inode for file listings --- e.g., "[userfaultfd]" or "[perf_event]". Example: type uffd_t; type_transition sysadm_t sysadm_t : anon_inode uffd_t "[userfaultfd]"; allow sysadm_t uffd_t:anon_inode { create }; (The next patch in this series is necessary for making userfaultfd support this new interface. The example above is just for exposition.) Signed-off-by: Daniel Colascione Acked-by: Casey Schaufler Acked-by: Stephen Smalley Cc: Al Viro Cc: Andrew Morton Signed-off-by: James Morris --- security/selinux/hooks.c | 53 +++++++++++++++++++++++++++++ security/selinux/include/classmap.h | 2 ++ 2 files changed, 55 insertions(+) diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index ca901025802a..5b403ad44aad 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -2926,6 +2926,58 @@ static int selinux_inode_init_security(struct inode *inode, struct inode *dir, return 0; } +static int selinux_inode_init_security_anon(struct inode *inode, + const struct qstr *name, + const struct inode *context_inode) +{ + const struct task_security_struct *tsec = selinux_cred(current_cred()); + struct common_audit_data ad; + struct inode_security_struct *isec; + int rc; + + if (unlikely(!selinux_state.initialized)) + return 0; + + isec = selinux_inode(inode); + + /* + * We only get here once per ephemeral inode. The inode has + * been initialized via inode_alloc_security but is otherwise + * untouched. + */ + + if (context_inode) { + struct inode_security_struct *context_isec = + selinux_inode(context_inode); + isec->sclass = context_isec->sclass; + isec->sid = context_isec->sid; + } else { + isec->sclass = SECCLASS_ANON_INODE; + rc = security_transition_sid( + &selinux_state, tsec->sid, tsec->sid, + isec->sclass, name, &isec->sid); + if (rc) + return rc; + } + + isec->initialized = LABEL_INITIALIZED; + + /* + * Now that we've initialized security, check whether we're + * allowed to actually create this type of anonymous inode. + */ + + ad.type = LSM_AUDIT_DATA_INODE; + ad.u.inode = inode; + + return avc_has_perm(&selinux_state, + tsec->sid, + isec->sid, + isec->sclass, + FILE__CREATE, + &ad); +} + static int selinux_inode_create(struct inode *dir, struct dentry *dentry, umode_t mode) { return may_create(dir, dentry, SECCLASS_FILE); @@ -6993,6 +7045,7 @@ static struct security_hook_list selinux_hooks[] __lsm_ro_after_init = { LSM_HOOK_INIT(inode_free_security, selinux_inode_free_security), LSM_HOOK_INIT(inode_init_security, selinux_inode_init_security), + LSM_HOOK_INIT(inode_init_security_anon, selinux_inode_init_security_anon), LSM_HOOK_INIT(inode_create, selinux_inode_create), LSM_HOOK_INIT(inode_link, selinux_inode_link), LSM_HOOK_INIT(inode_unlink, selinux_inode_unlink), diff --git a/security/selinux/include/classmap.h b/security/selinux/include/classmap.h index 40cebde62856..ba2e01a6955c 100644 --- a/security/selinux/include/classmap.h +++ b/security/selinux/include/classmap.h @@ -249,6 +249,8 @@ struct security_class_mapping secclass_map[] = { {"open", "cpu", "kernel", "tracepoint", "read", "write"} }, { "lockdown", { "integrity", "confidentiality", NULL } }, + { "anon_inode", + { COMMON_FILE_PERMS, NULL } }, { NULL } }; From patchwork Fri Aug 7 22:49:41 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Lokesh Gidra X-Patchwork-Id: 11706159 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id CD62E138A for ; Fri, 7 Aug 2020 22:50:31 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id B47B822CA1 for ; Fri, 7 Aug 2020 22:50:31 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="bF2xJUIl" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726335AbgHGWua (ORCPT ); Fri, 7 Aug 2020 18:50:30 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:37688 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726382AbgHGWu1 (ORCPT ); Fri, 7 Aug 2020 18:50:27 -0400 Received: from mail-pj1-x1049.google.com (mail-pj1-x1049.google.com [IPv6:2607:f8b0:4864:20::1049]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 2CECFC061A28 for ; Fri, 7 Aug 2020 15:50:27 -0700 (PDT) Received: by mail-pj1-x1049.google.com with SMTP id ei10so2655167pjb.2 for ; Fri, 07 Aug 2020 15:50:27 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20161025; h=date:in-reply-to:message-id:mime-version:references:subject:from:to :cc; bh=3/owlDjeUst/VUedKaOlenkrtf0ajk2+sX2cL13a7vo=; b=bF2xJUIl3GYrWMv7joU4DxGYXczwSg+carS7d5v9vBaVDCqwtrswyCeuZi2vDCcn1I VfxkmmJsSr5GAGY4Io7yRwj1aQEOarVNDrLpe4iRBtm5RPlnT84dETARkWpF+dQaQCMN 5T8vnTYMrgJL9sC33YSONqohFwtuf6X7xukqpNxQUOfDrs0fBb4hpKYXBNg6umB1IKjS 14csky7oxpKje8br+jEYf2jnlTjk8+dToAc31ep9at/aQX8aw1chNbLU28aszzE1DtuZ T2j+ZD8fJ4FjC/jVIcjQeG+sTNq4rzL6UtFg4Ii1dyaOWZND0gaDs+vw6ScUJlHmewbQ m5qA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:date:in-reply-to:message-id:mime-version :references:subject:from:to:cc; bh=3/owlDjeUst/VUedKaOlenkrtf0ajk2+sX2cL13a7vo=; b=GpbdHCr3VDSLXMTZviM09EnOBgh2ycJWMFe7B4l2lsmgK22mWzg2Z1wJLdhGCAD9dG oLcUINLCpaeKhXAcXJQTZFulwWt//+r+hy0tRNL2ZQ/SzupqZJ/0uzt/g/SFyKAPWosn J8OMfZVrDrbRPiMz+aZBGrIO74xLd9v7tLEFs2tW65tMXM8ifnEhG2BNRKfvoddGk3X6 DDgFjGUfsSPktFl3SZJXUIeuN2ZKiERj7BlNP11ZiyvPap47/A3XtWx4guiH+SHFLdSp QepUk1U/GVd2mZf8iNYI4icO//RZ6Fps1cw4YQcCsW/NbhLwYWzFNUB7uiYIVDW4pzae btuA== X-Gm-Message-State: AOAM530qUkLMywmYPQEpz0mItugt9Q8hD+E++rtI2xTtkWaWVORkd2Tb wvPZZKR0aLhwQV873TQpAu3gGT1oLkHqej44Yw== X-Google-Smtp-Source: ABdhPJzxKLo54fvW6D1u9bvhmmERMULl1i/OzpJh+BMKFG/junntW18h4IfaT+t8b+Ofl6BrLf0D1SQ/CC6dzKxqyw== X-Received: by 2002:a17:90b:3807:: with SMTP id mq7mr16155471pjb.13.1596840626464; Fri, 07 Aug 2020 15:50:26 -0700 (PDT) Date: Fri, 7 Aug 2020 15:49:41 -0700 In-Reply-To: <20200807224941.3440722-1-lokeshgidra@google.com> Message-Id: <20200807224941.3440722-4-lokeshgidra@google.com> Mime-Version: 1.0 References: <20200807224941.3440722-1-lokeshgidra@google.com> X-Mailer: git-send-email 2.28.0.236.gb10cc79966-goog Subject: [PATCH v6 3/3] Wire UFFD up to SELinux From: Lokesh Gidra To: Alexander Viro , James Morris , Stephen Smalley , Casey Schaufler , Eric Biggers Cc: "Serge E. Hallyn" , Paul Moore , Eric Paris , Lokesh Gidra , Daniel Colascione , Kees Cook , "Eric W. Biederman" , KP Singh , David Howells , Thomas Cedeno , Anders Roxell , Sami Tolvanen , Matthew Garrett , Aaron Goidel , Randy Dunlap , "Joel Fernandes (Google)" , YueHaibing , Christian Brauner , Alexei Starovoitov , Alexey Budankov , Adrian Reber , Aleksa Sarai , linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org, linux-security-module@vger.kernel.org, selinux@vger.kernel.org, kaleshsingh@google.com, calin@google.com, surenb@google.com, nnk@google.com, jeffv@google.com, kernel-team@android.com, Daniel Colascione Sender: selinux-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: selinux@vger.kernel.org From: Daniel Colascione This change gives userfaultfd file descriptors a real security context, allowing policy to act on them. Signed-off-by: Daniel Colascione [Remove owner inode from userfaultfd_ctx] [Use anon_inode_getfd_secure() instead of anon_inode_getfile_secure() in userfaultfd syscall] [Use inode of file in userfaultfd_read() in resolve_userfault_fork()] Signed-off-by: Lokesh Gidra --- fs/userfaultfd.c | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/fs/userfaultfd.c b/fs/userfaultfd.c index 6e264dded46e..23c8618ebe35 100644 --- a/fs/userfaultfd.c +++ b/fs/userfaultfd.c @@ -978,14 +978,16 @@ static __poll_t userfaultfd_poll(struct file *file, poll_table *wait) static const struct file_operations userfaultfd_fops; -static int resolve_userfault_fork(struct userfaultfd_ctx *ctx, - struct userfaultfd_ctx *new, +static int resolve_userfault_fork(struct userfaultfd_ctx *new, + struct inode *inode, struct uffd_msg *msg) { int fd; - fd = anon_inode_getfd("[userfaultfd]", &userfaultfd_fops, new, - O_RDWR | (new->flags & UFFD_SHARED_FCNTL_FLAGS)); + fd = anon_inode_getfd_secure( + "[userfaultfd]", &userfaultfd_fops, new, + O_RDWR | (new->flags & UFFD_SHARED_FCNTL_FLAGS), + inode); if (fd < 0) return fd; @@ -995,7 +997,7 @@ static int resolve_userfault_fork(struct userfaultfd_ctx *ctx, } static ssize_t userfaultfd_ctx_read(struct userfaultfd_ctx *ctx, int no_wait, - struct uffd_msg *msg) + struct uffd_msg *msg, struct inode *inode) { ssize_t ret; DECLARE_WAITQUEUE(wait, current); @@ -1106,7 +1108,7 @@ static ssize_t userfaultfd_ctx_read(struct userfaultfd_ctx *ctx, int no_wait, spin_unlock_irq(&ctx->fd_wqh.lock); if (!ret && msg->event == UFFD_EVENT_FORK) { - ret = resolve_userfault_fork(ctx, fork_nctx, msg); + ret = resolve_userfault_fork(fork_nctx, inode, msg); spin_lock_irq(&ctx->event_wqh.lock); if (!list_empty(&fork_event)) { /* @@ -1166,6 +1168,7 @@ static ssize_t userfaultfd_read(struct file *file, char __user *buf, ssize_t _ret, ret = 0; struct uffd_msg msg; int no_wait = file->f_flags & O_NONBLOCK; + struct inode *inode = file_inode(file); if (ctx->state == UFFD_STATE_WAIT_API) return -EINVAL; @@ -1173,7 +1176,7 @@ static ssize_t userfaultfd_read(struct file *file, char __user *buf, for (;;) { if (count < sizeof(msg)) return ret ? ret : -EINVAL; - _ret = userfaultfd_ctx_read(ctx, no_wait, &msg); + _ret = userfaultfd_ctx_read(ctx, no_wait, &msg, inode); if (_ret < 0) return ret ? ret : _ret; if (copy_to_user((__u64 __user *) buf, &msg, sizeof(msg))) @@ -1995,8 +1998,10 @@ SYSCALL_DEFINE1(userfaultfd, int, flags) /* prevent the mm struct to be freed */ mmgrab(ctx->mm); - fd = anon_inode_getfd("[userfaultfd]", &userfaultfd_fops, ctx, - O_RDWR | (flags & UFFD_SHARED_FCNTL_FLAGS)); + fd = anon_inode_getfd_secure("[userfaultfd]", + &userfaultfd_fops, ctx, + O_RDWR | (flags & UFFD_SHARED_FCNTL_FLAGS), + NULL); if (fd < 0) { mmdrop(ctx->mm); kmem_cache_free(userfaultfd_ctx_cachep, ctx);