From patchwork Fri Jan 8 05:32:56 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Lokesh Gidra X-Patchwork-Id: 12005805 X-Patchwork-Delegate: paul@paul-moore.com Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-26.6 required=3.0 tests=BAYES_00,DKIMWL_WL_MED, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_CR_TRAILER,INCLUDES_PATCH,MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS, USER_AGENT_GIT,USER_IN_DEF_DKIM_WL autolearn=unavailable autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 3EF47C433E9 for ; Fri, 8 Jan 2021 05:34:38 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 1FCA82368A for ; Fri, 8 Jan 2021 05:34:38 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727946AbhAHFeJ (ORCPT ); Fri, 8 Jan 2021 00:34:09 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:56558 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1727934AbhAHFeF (ORCPT ); Fri, 8 Jan 2021 00:34:05 -0500 Received: from mail-qt1-x84a.google.com (mail-qt1-x84a.google.com [IPv6:2607:f8b0:4864:20::84a]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 1AC86C0612FE for ; Thu, 7 Jan 2021 21:33:04 -0800 (PST) Received: by mail-qt1-x84a.google.com with SMTP id b8so7435387qtr.18 for ; Thu, 07 Jan 2021 21:33:04 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20161025; h=sender:date:in-reply-to:message-id:mime-version:references:subject :from:to:cc; bh=4KH1yzDb8snTvZnWPZ6fdUzRnCevlmVN32CkdvE/ol0=; b=OooG9vnAq2ysyycbcX08KiXI5S0HJbbgcA9Z9Oe2Ez2suyGyVQeZttS0HO9/+UCX/V PjCh7xJBw+TdGVoIZS/5qgRIm1qmo97LfRBqabAIBI1eElk2H8vl7Iq2/mJF/OvpbxZv S2DHBKn5hwyOvzL+cFt33QWpegpu0/4RIQXbtBugszlnBsVDw6iZPwE3EwdLtZnuE4jb iXk8Ya9/XnKUJPuZ9NKZuMg0V/wwMZIaYhIfU5f7QeWo9mpEC+S7/MxUfwosRALlsS7z VyixOvAGTgm5BqbxTBEz6lPVVpEjwtub+alQLQhkEdpqshrKURlxVPoAjGWDT9RlyPXj UuXg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:sender:date:in-reply-to:message-id:mime-version :references:subject:from:to:cc; bh=4KH1yzDb8snTvZnWPZ6fdUzRnCevlmVN32CkdvE/ol0=; b=JBbShHKowKLIgK3fLgrIFozdJ2fU922BkHLeF9jlB5hp3+dzRCivNGaQEdmEKF4uqe 2CMg+Zikd0DnZPOly4cgaUOpcyLOXs/x52uhIKWWuTHkBQM3SybD0myqZFjH81elLbZH T3E25KZrVp9rvFtmI05ywq40JdYsB6Btp0ILmQkGoxoyMIi73a2w6+XF0EdHxY6SeOAa KsgLm5xVd3SwtQjKcrVDpbac/xctlE8YF3o8O192+/skh4Ki4SHyg4S8MEJQEoPzPQC0 Un9bVodKc3Xuczltv4mweyx8eOTqZK2u/BbDPxV7/6XfolC6vCalVwubP33BxemXiSg7 njxA== X-Gm-Message-State: AOAM530S6zxIpN3DFPm2ZApR5px5cTQwUf7sVVZBvixKZPPDuEXqzzlL lShZzmaj/Ah6SUYmuVhuxYJkMcVyfPeKcKE8JQ== X-Google-Smtp-Source: ABdhPJxe5Xp0qHN/Ia6xoJMR+ESmUkHT1cbbplCD/GFyfD/HcPhZbdQJMkeXAwcx0bM8pckO4liRgJmF06RFMLCLzQ== Sender: "lokeshgidra via sendgmr" X-Received: from lg.mtv.corp.google.com ([2620:15c:211:202:f693:9fff:fef4:29dd]) (user=lokeshgidra job=sendgmr) by 2002:a0c:bd2b:: with SMTP id m43mr2274696qvg.32.1610083983055; Thu, 07 Jan 2021 21:33:03 -0800 (PST) Date: Thu, 7 Jan 2021 21:32:56 -0800 In-Reply-To: <20210108053259.726613-1-lokeshgidra@google.com> Message-Id: <20210108053259.726613-2-lokeshgidra@google.com> Mime-Version: 1.0 References: <20210108053259.726613-1-lokeshgidra@google.com> X-Mailer: git-send-email 2.30.0.284.gd98b1dd5eaa7-goog Subject: [PATCH v14 1/4] security: add inode_init_security_anon() LSM hook From: Lokesh Gidra To: Andrea Arcangeli , Alexander Viro , James Morris , Stephen Smalley , Casey Schaufler , Eric Biggers , Paul Moore Cc: "Serge E. Hallyn" , Eric Paris , Lokesh Gidra , Daniel Colascione , Kees Cook , "Eric W. Biederman" , KP Singh , David Howells , Anders Roxell , Sami Tolvanen , Matthew Garrett , Randy Dunlap , "Joel Fernandes (Google)" , YueHaibing , Christian Brauner , Alexei Starovoitov , 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, jeffv@google.com, kernel-team@android.com, linux-mm@kvack.org, Andrew Morton , hch@infradead.org Precedence: bulk List-ID: X-Mailing-List: selinux@vger.kernel.org This change adds a new LSM hook, inode_init_security_anon(), that will be used while creating secure anonymous inodes. The hook allows/denies its creation and assigns a security context to the inode. The new hook accepts an optional context_inode parameter that callers can use to provide additional contextual information to security modules for granting/denying permission to create an anon-inode of the same type. This context_inode's security_context can also be used to initialize the newly created anon-inode's security_context. Signed-off-by: Lokesh Gidra Reviewed-by: Eric Biggers --- include/linux/lsm_hook_defs.h | 2 ++ include/linux/lsm_hooks.h | 9 +++++++++ include/linux/security.h | 10 ++++++++++ security/security.c | 8 ++++++++ 4 files changed, 29 insertions(+) diff --git a/include/linux/lsm_hook_defs.h b/include/linux/lsm_hook_defs.h index 7aaa753b8608..dfd261dcbcb0 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 a19adef1f088..bdfc8a76a4f7 100644 --- a/include/linux/lsm_hooks.h +++ b/include/linux/lsm_hooks.h @@ -233,6 +233,15 @@ * 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 the incore security field for the new anonymous inode + * and return whether the inode creation is permitted by the security + * module or not. + * @inode contains the inode structure + * @name name of the anonymous inode class + * @context_inode optional related inode + * Returns 0 on success, -EACCES 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 c35ea0ffccd9..b0d14f04b16d 100644 --- a/include/linux/security.h +++ b/include/linux/security.h @@ -324,6 +324,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); @@ -738,6 +741,13 @@ static inline int security_inode_init_security(struct inode *inode, return 0; } +static inline int security_inode_init_security_anon(struct inode *inode, + const struct qstr *name, + const struct inode *context_inode) +{ + return 0; +} + static inline int security_old_inode_init_security(struct inode *inode, struct inode *dir, const struct qstr *qstr, diff --git a/security/security.c b/security/security.c index 7b09cfbae94f..401663b5b70e 100644 --- a/security/security.c +++ b/security/security.c @@ -1059,6 +1059,14 @@ 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 Jan 8 05:32:57 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Lokesh Gidra X-Patchwork-Id: 12005809 X-Patchwork-Delegate: paul@paul-moore.com Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-26.6 required=3.0 tests=BAYES_00,DKIMWL_WL_MED, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_CR_TRAILER,INCLUDES_PATCH,MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS, USER_AGENT_GIT,USER_IN_DEF_DKIM_WL autolearn=unavailable autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id E10D0C43331 for ; Fri, 8 Jan 2021 05:34:38 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id C178F233EE for ; Fri, 8 Jan 2021 05:34:38 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728034AbhAHFeX (ORCPT ); Fri, 8 Jan 2021 00:34:23 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:56608 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1728024AbhAHFeW (ORCPT ); Fri, 8 Jan 2021 00:34:22 -0500 Received: from mail-pf1-x44a.google.com (mail-pf1-x44a.google.com [IPv6:2607:f8b0:4864:20::44a]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id A4B22C061290 for ; Thu, 7 Jan 2021 21:33:05 -0800 (PST) Received: by mail-pf1-x44a.google.com with SMTP id q22so5822671pfj.20 for ; Thu, 07 Jan 2021 21:33:05 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20161025; h=sender:date:in-reply-to:message-id:mime-version:references:subject :from:to:cc; bh=98Z4ectXH1REB58k02Mn1amE7f3KfVW7xatcwz+e4fE=; b=Gr8ToxvYwY22MpaSPnvv+xNTlF/MwvGK1jay3/KQp6rUMoMTvKtxUxD+fc0e3NyuJ8 o+cKsoUpxojpTgUHAomzIjQbNqfHVgzRePkld2+cs3rxMTsLKygVVuDBTGTVTkv/7RUH A8cpF9U6c3PRllLLLAn3+rq892JeKIN7FGREU7e9y/RdFftBINpJBgXteA+zKow1enF5 LiYwKQ9Yqv9yh2E1d/xtg7uonuUqM6Ob/TcInfIIyoUzagaRIPp12hiiGc16g7fPqtGc /UI3GE1GT62eFxi/gDJtYKPs+7OXz85Vsmzqzg6OykKSj1TQbbo+WMZTNm0z45ZRAoIs T7Og== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:sender:date:in-reply-to:message-id:mime-version :references:subject:from:to:cc; bh=98Z4ectXH1REB58k02Mn1amE7f3KfVW7xatcwz+e4fE=; b=A00mZu512Mc2+VD7e7YVAVEO8tx7mJTrhuxrRAxhGq6t/8Ieq4uPl/UcyNgNY6wM1q YJxDcT/06pCE8jef9gTx77GwWcDjLioa2dRz1qltkDCKF/AD1hCnkUX4PJjlhVfD+4Cm kSrar2WxoTSzlmLzMOO2Y8Lw8bhve3JMhk0It3zgTEWqGhx6+vX4/eh7EM57BYgsYeeH XvBWc8ZHSkHbFAgkjBBWsL5SIrY91Ha0X9fJa+wSTWfnNMYlJhGxtaEH1CA1BtlUiesO rkpC7XYHwmehbe8sr30Msq/GkhkLQIVkxVUvW4A1EoLeWr9+x8G8nO19Ve4lUINd8Zyo otrg== X-Gm-Message-State: AOAM532z4faARMPF/R/U6rBxad5yuKsFkDkN+UxvPvzJxK4uz0nNfeBY ATEAbaDoZgbN+iSVts9M83T2cSNsJ5mCXtakqQ== X-Google-Smtp-Source: ABdhPJxfehStPEkJ3pUnpGoIAn6powT/fnb9r1MZcIuc+Bkv40QUK2XLdEFie9F8fMw6kxFJ4XcRsrnRfBWhIxZZAw== Sender: "lokeshgidra via sendgmr" X-Received: from lg.mtv.corp.google.com ([2620:15c:211:202:f693:9fff:fef4:29dd]) (user=lokeshgidra job=sendgmr) by 2002:a62:1a56:0:b029:19d:b6eb:291a with SMTP id a83-20020a621a560000b029019db6eb291amr2047387pfa.10.1610083985027; Thu, 07 Jan 2021 21:33:05 -0800 (PST) Date: Thu, 7 Jan 2021 21:32:57 -0800 In-Reply-To: <20210108053259.726613-1-lokeshgidra@google.com> Message-Id: <20210108053259.726613-3-lokeshgidra@google.com> Mime-Version: 1.0 References: <20210108053259.726613-1-lokeshgidra@google.com> X-Mailer: git-send-email 2.30.0.284.gd98b1dd5eaa7-goog Subject: [PATCH v14 2/4] fs: add LSM-supporting anon-inode interface From: Lokesh Gidra To: Andrea Arcangeli , Alexander Viro , James Morris , Stephen Smalley , Casey Schaufler , Eric Biggers , Paul Moore Cc: "Serge E. Hallyn" , Eric Paris , Lokesh Gidra , Daniel Colascione , Kees Cook , "Eric W. Biederman" , KP Singh , David Howells , Anders Roxell , Sami Tolvanen , Matthew Garrett , Randy Dunlap , "Joel Fernandes (Google)" , YueHaibing , Christian Brauner , Alexei Starovoitov , 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, jeffv@google.com, kernel-team@android.com, linux-mm@kvack.org, Andrew Morton , hch@infradead.org Precedence: bulk List-ID: X-Mailing-List: selinux@vger.kernel.org From: Daniel Colascione This change adds a new function, anon_inode_getfd_secure, that creates anonymous-node file with individual non-S_PRIVATE inode 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 function accepts an optional context_inode parameter that callers can use to provide additional contextual information to security modules. For example, in case of userfaultfd, the created inode is a 'logical child' of the context_inode (userfaultfd inode of the parent process) in the sense that it provides the security context required during creation of the child process' userfaultfd inode. Signed-off-by: Daniel Colascione [LG: Delete obsolete comments to alloc_anon_inode()] [LG: Add context_inode description in comments to anon_inode_getfd_secure()] [LG: Remove definition of anon_inode_getfile_secure() as there are no callers] [LG: Make __anon_inode_getfile() static] [LG: Use correct error cast in __anon_inode_getfile()] [LG: Fix error handling in __anon_inode_getfile()] Signed-off-by: Lokesh Gidra Reviewed-by: Eric Biggers --- fs/anon_inodes.c | 150 ++++++++++++++++++++++++++---------- fs/libfs.c | 5 -- include/linux/anon_inodes.h | 5 ++ 3 files changed, 115 insertions(+), 45 deletions(-) diff --git a/fs/anon_inodes.c b/fs/anon_inodes.c index 89714308c25b..023337d65a03 100644 --- a/fs/anon_inodes.c +++ b/fs/anon_inodes.c @@ -55,61 +55,79 @@ 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 file *file; + 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; +} - if (IS_ERR(anon_inode_inode)) - return ERR_PTR(-ENODEV); +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 (fops->owner && !try_module_get(fops->owner)) return ERR_PTR(-ENOENT); - /* - * 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, + if (secure) { + inode = anon_inode_make_secure_inode(name, context_inode); + if (IS_ERR(inode)) { + file = ERR_CAST(inode); + goto err; + } + } else { + inode = anon_inode_inode; + if (IS_ERR(inode)) { + file = ERR_PTR(-ENODEV); + goto err; + } + /* + * We know the anon_inode inode count is always + * greater than zero, so ihold() is safe. + */ + ihold(inode); + } + + file = alloc_file_pseudo(inode, anon_inode_mnt, name, flags & (O_ACCMODE | O_NONBLOCK), fops); if (IS_ERR(file)) - goto err; + goto err_iput; - file->f_mapping = anon_inode_inode->i_mapping; + file->f_mapping = inode->i_mapping; file->private_data = priv; return file; +err_iput: + iput(inode); err: - iput(anon_inode_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 - 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 +136,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 +162,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,8 +176,48 @@ int anon_inode_getfd(const char *name, const struct file_operations *fops, put_unused_fd(fd); return error; } + +/** + * 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_getfd() 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); +/** + * Like anon_inode_getfd(), but creates a new !S_PRIVATE anon inode rather than + * reuse the singleton anon inode, and calls the inode_init_security_anon() LSM + * hook. This allows the inode to have its own security context and for a LSM + * to reject creation of the inode. An optional @context_inode argument is + * also added to provide the logical relationship with the new inode. The LSM + * may use @context_inode in inode_init_security_anon(), but a reference to it + * is not held. + */ +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); + static int __init anon_inode_init(void) { anon_inode_mnt = kern_mount(&anon_inode_fs_type); diff --git a/fs/libfs.c b/fs/libfs.c index d1c3bade9f30..e52818fb276a 100644 --- a/fs/libfs.c +++ b/fs/libfs.c @@ -1214,11 +1214,6 @@ static int anon_set_page_dirty(struct page *page) return 0; }; -/* - * A single inode exists for all anon_inode files. Contrary to pipes, - * anon_inode inodes have no associated per-instance data, so we need - * only allocate one of them. - */ struct inode *alloc_anon_inode(struct super_block *s) { static const struct address_space_operations anon_aops = { diff --git a/include/linux/anon_inodes.h b/include/linux/anon_inodes.h index d0d7d96261ad..71881a2b6f78 100644 --- a/include/linux/anon_inodes.h +++ b/include/linux/anon_inodes.h @@ -10,12 +10,17 @@ #define _LINUX_ANON_INODES_H struct file_operations; +struct inode; struct file *anon_inode_getfile(const char *name, const struct file_operations *fops, void *priv, int flags); int anon_inode_getfd(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); #endif /* _LINUX_ANON_INODES_H */ From patchwork Fri Jan 8 05:32:58 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Lokesh Gidra X-Patchwork-Id: 12005807 X-Patchwork-Delegate: paul@paul-moore.com Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-26.6 required=3.0 tests=BAYES_00,DKIMWL_WL_MED, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_CR_TRAILER,INCLUDES_PATCH,MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS, USER_AGENT_GIT,USER_IN_DEF_DKIM_WL autolearn=unavailable autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 6546BC433E6 for ; Fri, 8 Jan 2021 05:34:38 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 40A572376F for ; Fri, 8 Jan 2021 05:34:38 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727975AbhAHFeS (ORCPT ); Fri, 8 Jan 2021 00:34:18 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:56526 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1727934AbhAHFeR (ORCPT ); Fri, 8 Jan 2021 00:34:17 -0500 Received: from mail-qv1-xf49.google.com (mail-qv1-xf49.google.com [IPv6:2607:f8b0:4864:20::f49]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 0237AC0612A2 for ; Thu, 7 Jan 2021 21:33:08 -0800 (PST) Received: by mail-qv1-xf49.google.com with SMTP id l3so7437589qvr.10 for ; Thu, 07 Jan 2021 21:33:07 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20161025; h=sender:date:in-reply-to:message-id:mime-version:references:subject :from:to:cc; bh=yvxLkMrnVxRSxC9KzTyVpuuqR+pCEABEK4TkVEGjmlM=; b=n8QszcMrbIG1zQA/diOQYNUs4mCdtg1DOV6tjUlQ2lnb4Xk+zOC4rmCdSfLa9SsHTD 1PprmyzinG1EOt3nJpebsLKdEdyvjZF1F2BghDq1S2ORAkTIkdEJwNxahywA9jft0xdc qoak145AZ6+a7UTh1S/Blc9JyXlsfKx5CdBSA3KY6OXoln7cuH1RKIP2mHO8XZNcHnx8 0pUcd4G6FGfYHob7nD7B/DTyCajiHZO9AAZ1DQA+d20psDVp27zaZ7Lv9bciaxH6jXfl TaDmcjn4rcZXDa5tf2lupNo7K2quDgoTMdE8NA+fLHyo30TSP/nFPac46ZWyAj1+rmzP vVjA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:sender:date:in-reply-to:message-id:mime-version :references:subject:from:to:cc; bh=yvxLkMrnVxRSxC9KzTyVpuuqR+pCEABEK4TkVEGjmlM=; b=ZFaXE5bji+2ydLkLtT98MhnBd4LBRToTuZ8vVTHsvhz6GPrstZWKMvs7ywwCa9KpFW q59ij9KtWWlpkLz+9bW1QPJ/uUL64iwCyulhSqbgo0n9prxd4RSZQWe91X5pgnkU+wNa 4Mz8fUR6VlC7r7AGVBigao4fmkLgqWowvNl/amI+UXyhPh5uqqNn9U5e/EU2wdwMhzYT 7hTbNHscxW/jFViZBXfqpW7rEy8z96o8Y2SAtd/aZuiFM1ky4mJboHdSry57QEYgYN7l 3ro9qybWRD61C6VO3FbpD8xarpKlwx/u9WTzSTUP11M1oX64aXH3fynm2Eoo17+2r60L SHAg== X-Gm-Message-State: AOAM530eNCq/Eu8t26cLgi/pyAtYmDHaLZb31s2FNjdiCLgy4D9tzclt o72EB04JXycD9l89kIUuSqnQIbUEGzKw1I0yGw== X-Google-Smtp-Source: ABdhPJzuOALQVMCIPqy/so0d6XS8F91KbIKIZiO0eWllFdMIerQfL+846wVfHQfzbGT2UDcMBAacWRe/HHphle9OEg== Sender: "lokeshgidra via sendgmr" X-Received: from lg.mtv.corp.google.com ([2620:15c:211:202:f693:9fff:fef4:29dd]) (user=lokeshgidra job=sendgmr) by 2002:a0c:a905:: with SMTP id y5mr1983204qva.55.1610083987068; Thu, 07 Jan 2021 21:33:07 -0800 (PST) Date: Thu, 7 Jan 2021 21:32:58 -0800 In-Reply-To: <20210108053259.726613-1-lokeshgidra@google.com> Message-Id: <20210108053259.726613-4-lokeshgidra@google.com> Mime-Version: 1.0 References: <20210108053259.726613-1-lokeshgidra@google.com> X-Mailer: git-send-email 2.30.0.284.gd98b1dd5eaa7-goog Subject: [PATCH v14 3/4] selinux: teach SELinux about anonymous inodes From: Lokesh Gidra To: Andrea Arcangeli , Alexander Viro , James Morris , Stephen Smalley , Casey Schaufler , Eric Biggers , Paul Moore Cc: "Serge E. Hallyn" , Eric Paris , Lokesh Gidra , Daniel Colascione , Kees Cook , "Eric W. Biederman" , KP Singh , David Howells , Anders Roxell , Sami Tolvanen , Matthew Garrett , Randy Dunlap , "Joel Fernandes (Google)" , YueHaibing , Christian Brauner , Alexei Starovoitov , 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, jeffv@google.com, kernel-team@android.com, linux-mm@kvack.org, Andrew Morton , hch@infradead.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 patches to give SELinux the ability to control anonymous-inode files that are created using the new anon_inode_getfd_secure() function. 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 Signed-off-by: Lokesh Gidra --- security/selinux/hooks.c | 59 +++++++++++++++++++++++++++++ security/selinux/include/classmap.h | 2 + 2 files changed, 61 insertions(+) diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 644b17ec9e63..8b4e155b2930 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -2934,6 +2933,63 @@ 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_initialized(&selinux_state))) + 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. + */ + isec->initialized = LABEL_INITIALIZED; + isec->sclass = SECCLASS_ANON_INODE; + + if (context_inode) { + struct inode_security_struct *context_isec = + selinux_inode(context_inode); + if (context_isec->initialized != LABEL_INITIALIZED) + return -EACCES; + if (context_isec->sclass != SECCLASS_ANON_INODE) { + pr_err("SELinux: initializing anonymous inode with non-anonymous inode"); + return -EACCES; + } + + isec->sid = context_isec->sid; + } else { + rc = security_transition_sid( + &selinux_state, tsec->sid, tsec->sid, + isec->sclass, name, &isec->sid); + if (rc) + return rc; + } + + /* + * 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, + ANON_INODE__CREATE, + &ad); +} + static int selinux_inode_create(struct inode *dir, struct dentry *dentry, umode_t mode) { return may_create(dir, dentry, SECCLASS_FILE); @@ -7000,6 +7057,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 Jan 8 05:32:59 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Lokesh Gidra X-Patchwork-Id: 12005811 X-Patchwork-Delegate: paul@paul-moore.com Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-26.6 required=3.0 tests=BAYES_00,DKIMWL_WL_MED, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_CR_TRAILER,INCLUDES_PATCH,MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS, USER_AGENT_GIT,USER_IN_DEF_DKIM_WL autolearn=unavailable autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 16716C432C3 for ; Fri, 8 Jan 2021 05:34:39 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id F0000233EE for ; Fri, 8 Jan 2021 05:34:38 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728004AbhAHFeU (ORCPT ); Fri, 8 Jan 2021 00:34:20 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:56550 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1727926AbhAHFeT (ORCPT ); Fri, 8 Jan 2021 00:34:19 -0500 Received: from mail-qk1-x749.google.com (mail-qk1-x749.google.com [IPv6:2607:f8b0:4864:20::749]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id E9A25C0612A5 for ; Thu, 7 Jan 2021 21:33:09 -0800 (PST) Received: by mail-qk1-x749.google.com with SMTP id 188so8360278qkh.7 for ; Thu, 07 Jan 2021 21:33:09 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20161025; h=sender:date:in-reply-to:message-id:mime-version:references:subject :from:to:cc; bh=Cge7zJHdUeDRqJYsTUJX6Q9Nj8WIU/VuWZZ/mr14Zmg=; b=qN+rRwo6MVgxmmI+RbC5qwLL+fMkVwWvNqMlQqHeJpnol+J5bOoiaOWcfCW3r8AHoW GYYLgp3gQHBaShRiCtiKkIw4wc9S4XawwRAb05Yr50CNRnEQgc4HtbkYBvnH7zjiRVOt YhzAdoKFJ+ai7dkkYNr+FZkc6JghkugaPQnpWPR7ptoiw+j3TlHhHENkYG7OqwGTfRIN PXc38cJeTAo8Zgjem7Lr+TtntNEJdbDAzWO2h2zvGPvnmLlfsyNRHE/iO4Yi8/Xq87Rf gLDo3nJWCDrbzKS+T64F/HLNM+Ohsf+j3E0gwRbEEOLHks9b/vd+nA5nmrHvDh++9Tr8 5LvQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:sender:date:in-reply-to:message-id:mime-version :references:subject:from:to:cc; bh=Cge7zJHdUeDRqJYsTUJX6Q9Nj8WIU/VuWZZ/mr14Zmg=; b=py47pfyraGxAa6Zj3KpHOcPZNR0vibGBTaO2DvG0TF3iAXzDd8ZZNhHA83joCkAofG KBJ+DxEZXVbEqxtLS1AcApCGtopjulSMLWDRZGZRnVuskCGU9mbWQvspCLOYOUnjWgv+ wjzH72M1gi8PT5CZS2+B8P3H5oJpNr9utJ4z2t5krEOPbf5vv6Q1RYkuOpQl7Hdxw3GU XvCwwFjT8nvVvdlDkexhtUFz5qMgqMWPr2ZRzM936c2GpAOcbTwSmrYhcx4xjowP/QcF fqGZmZ1qhEW8Ft+XYFP2LiZbk2898fh4JEK7+5irn3xcm4uC/zEOw4q5hQzTYhrL18u4 w9Pw== X-Gm-Message-State: AOAM531T5NWjbKCNlWQYaUa9qUYtES0qZFq9FfRPLvoDXwrWTKbkbFdN P2PJH173ClHvu2EnCv+58uExbsWCyOisxuckkw== X-Google-Smtp-Source: ABdhPJxm8tlrBDnY+v0WVkKBRI5KV7uvRVzcdBOqrHxkpxVhZYwYjuvyY1pgMx2tkFD/UUvuWNqF585dxeWZV3GCAg== Sender: "lokeshgidra via sendgmr" X-Received: from lg.mtv.corp.google.com ([2620:15c:211:202:f693:9fff:fef4:29dd]) (user=lokeshgidra job=sendgmr) by 2002:a05:6214:714:: with SMTP id b20mr2029281qvz.36.1610083989040; Thu, 07 Jan 2021 21:33:09 -0800 (PST) Date: Thu, 7 Jan 2021 21:32:59 -0800 In-Reply-To: <20210108053259.726613-1-lokeshgidra@google.com> Message-Id: <20210108053259.726613-5-lokeshgidra@google.com> Mime-Version: 1.0 References: <20210108053259.726613-1-lokeshgidra@google.com> X-Mailer: git-send-email 2.30.0.284.gd98b1dd5eaa7-goog Subject: [PATCH v14 4/4] userfaultfd: use secure anon inodes for userfaultfd From: Lokesh Gidra To: Andrea Arcangeli , Alexander Viro , James Morris , Stephen Smalley , Casey Schaufler , Eric Biggers , Paul Moore Cc: "Serge E. Hallyn" , Eric Paris , Lokesh Gidra , Daniel Colascione , Kees Cook , "Eric W. Biederman" , KP Singh , David Howells , Anders Roxell , Sami Tolvanen , Matthew Garrett , Randy Dunlap , "Joel Fernandes (Google)" , YueHaibing , Christian Brauner , Alexei Starovoitov , 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, jeffv@google.com, kernel-team@android.com, linux-mm@kvack.org, Andrew Morton , hch@infradead.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 [LG: Remove owner inode from userfaultfd_ctx] [LG: Use anon_inode_getfd_secure() instead of anon_inode_getfile_secure() in userfaultfd syscall] [LG: Use inode of file in userfaultfd_read() in resolve_userfault_fork()] Signed-off-by: Lokesh Gidra Reviewed-by: Eric Biggers --- fs/userfaultfd.c | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/fs/userfaultfd.c b/fs/userfaultfd.c index 894cc28142e7..0be8cdd4425a 100644 --- a/fs/userfaultfd.c +++ b/fs/userfaultfd.c @@ -979,14 +979,14 @@ 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; @@ -996,7 +996,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); @@ -1107,7 +1107,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)) { /* @@ -1167,6 +1167,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; @@ -1174,7 +1175,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))) @@ -1999,8 +2000,8 @@ 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);