Received: by 2002:a05:6a10:16a7:0:0:0:0 with SMTP id gp39csp808716pxb; Thu, 5 Nov 2020 13:38:16 -0800 (PST) X-Google-Smtp-Source: ABdhPJyYFJanjq2Mvpq108LvUEW4ZB/I65Aswv3Wzu9eODpdA76JFBXk6k0zocWyEqlHhLFPfJyh X-Received: by 2002:a17:906:2512:: with SMTP id i18mr4576241ejb.184.1604612295976; Thu, 05 Nov 2020 13:38:15 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1604612295; cv=none; d=google.com; s=arc-20160816; b=kox63fgZAnuFUd75BEyaLk68UNlZH0WvQhLu95KpEOaOBEFdXR15UfMP4hxTAeFzbU v6OhBbQc2U4Zq3q/IyvXoRH1XSKsiffp8V5sTyA21U+A5U5W3TSE4kEqe0SYW9TJlF3L vkZYwqGsmre0AWGiL9vZ7PusDMdNOwxCLJ4Vy0b6Cqb97M34MdaiiMc89OVfQy8WjnWc avYTc+VjNPNUUuVjncRssOORoCKp2e54yTiu/MdHsyCX5U9mgkRP88rkScne6J1ygJoA 0xNkpqB8nj5vOkalYlgFKXVSIraHVClaXvyPJev6Bfd1KcRvXXvwODqbj3U+xE4gFCjh h8ZQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:cc:to:from:subject:references:mime-version :message-id:in-reply-to:date:sender:dkim-signature; bh=AZ9CuApbWUuyKZjL7V5mL8MUZ1TwkboSp5kTlPzCu3s=; b=HPUk2OnUw2541i4ivszAlYapmoRva4Q25Ac8bUVEi9Yyf1u8t0wZ9mVsJicIDlbGbz GA4TM+5/gJcgUOKiID/i6ruPq9U3kQe7UAI9ItXZ3Bosw8J2910WwMjPncazsNcle0gX FBNNSHAyXgiIE+cOKHzTXdPHHXI8hnS2JJzf5QRnR4FmnopYbr8dxhD20jdO+WpKQhuo 5fPBDitbWc74Xfl6iFRbXNUYkR4WdN8eb+h4VLtMGv73uBCJCeYPimoRcfAk+hjzgnmc cilTBoKaUM65Fmhr2u0bX0XkZh/9Wek5TnV5aNm+lXFk5StPnoldXni/0mTCkbJfMXrV Njtg== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@google.com header.s=20161025 header.b=UmXaLViL; 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; dmarc=pass (p=REJECT sp=REJECT dis=NONE) header.from=google.com Return-Path: Received: from vger.kernel.org (vger.kernel.org. [23.128.96.18]) by mx.google.com with ESMTP id lo27si2115028ejb.685.2020.11.05.13.37.52; Thu, 05 Nov 2020 13:38:15 -0800 (PST) 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; dkim=pass header.i=@google.com header.s=20161025 header.b=UmXaLViL; 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; dmarc=pass (p=REJECT sp=REJECT dis=NONE) header.from=google.com Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1732527AbgKEVdu (ORCPT + 99 others); Thu, 5 Nov 2020 16:33:50 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:54664 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1732398AbgKEVde (ORCPT ); Thu, 5 Nov 2020 16:33:34 -0500 Received: from mail-pl1-x64a.google.com (mail-pl1-x64a.google.com [IPv6:2607:f8b0:4864:20::64a]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 2B960C061A4A for ; Thu, 5 Nov 2020 13:33:34 -0800 (PST) Received: by mail-pl1-x64a.google.com with SMTP id bc2so1636535plb.23 for ; Thu, 05 Nov 2020 13:33:34 -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=AZ9CuApbWUuyKZjL7V5mL8MUZ1TwkboSp5kTlPzCu3s=; b=UmXaLViLtRlhTD4Oeu4GcPnE4zu/VgoYhWFG2sTKhVfu8SWcZUk1UynjvLf69yP59+ +okdhFr5cnFo4oYl5KzvYe5qLtWcUf+Qk4cObjKcs20KopeYzXmOF5+lPMTYJb4NiXfO Y8knJuYlJMm85cE1c4FUHL2wVSsNgnbCTGOhOkA9LyUIej4Zvrwq63lGHFej4JoAPQxp hsxjkz6P8YNsVz+XHI+s9sZvCWbNHLRjNkohPL1YE/Z4lOQgIq532c2aqoyeeSNSTKI+ s2jo669lud71fuCpCymlC6ZE+Gy67vvMLios70yxqF6ibtvb486uoDkophzY4D1BA6Tr 9VHw== 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=AZ9CuApbWUuyKZjL7V5mL8MUZ1TwkboSp5kTlPzCu3s=; b=pJibKVx2LK1oUhNtRi/HMiI0nnYbMF/xL9yDYAdhlyxWosrRvRYpjBrEYoC1gZnx+X tqbO1au5o1JjcmOdtuEjaeFXDDHpPsclPsDraYgkZkDlphI1rdMk56loAFCzy8WC1ONl IbteWXAuIyBZCgevOseF4R6d9DROCab1cBRSyy+//YkII1RGzzZmF5VhU94qmKHrIxOH xa53H7RuHA9GTc7FXdLvxa3E9zqpCLcFBqah305NawxcRxEuOzXNWaBsb3zAeVLvqpzx gzZhA4Z9z2s1a3jMH2KzJf60ILJAq4McDbgD4PDXZ12svAhT6ZpDvOUziGGu83oGpWe6 6QKA== X-Gm-Message-State: AOAM530SiimXrWzGWERGyzJPHOtVSSJ8iI7D+yMekAy/Csyl4litxIwO nrm+eLtbLsS0y9UpWFVmPlAVVakNnw4ptU3hMA== 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:a17:90a:bb8b:: with SMTP id v11mr4353310pjr.57.1604612013631; Thu, 05 Nov 2020 13:33:33 -0800 (PST) Date: Thu, 5 Nov 2020 13:33:22 -0800 In-Reply-To: <20201105213324.3111570-1-lokeshgidra@google.com> Message-Id: <20201105213324.3111570-3-lokeshgidra@google.com> Mime-Version: 1.0 References: <20201105213324.3111570-1-lokeshgidra@google.com> X-Mailer: git-send-email 2.29.1.341.ge80a0c044ae-goog Subject: [PATCH v11 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 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, linux-mm@kvack.org, Andrew Morton , hch@infradead.org, Daniel Colascione Content-Type: text/plain; charset="UTF-8" Precedence: bulk List-ID: X-Mailing-List: linux-kernel@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 granting/denying permission to create an anon inode of the same type. 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 [Update comments to alloc_anon_inode()] [Add context_inode description in comments to anon_inode_getfd_secure()] [Remove definition of anon_inode_getfile_secure() as there are no callers] [Make __anon_inode_getfile() static] [Use correct error cast in __anon_inode_getfile()] [Fix error handling in __anon_inode_getfile()] Signed-off-by: Lokesh Gidra --- fs/anon_inodes.c | 149 ++++++++++++++++++++++++++---------- fs/libfs.c | 6 +- include/linux/anon_inodes.h | 5 ++ 3 files changed, 117 insertions(+), 43 deletions(-) diff --git a/fs/anon_inodes.c b/fs/anon_inodes.c index 89714308c25b..fc935acb90d6 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,47 @@ 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 call the 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 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 fc34361c1489..5b12228ecc81 100644 --- a/fs/libfs.c +++ b/fs/libfs.c @@ -1213,9 +1213,9 @@ static int anon_set_page_dirty(struct page *page) }; /* - * 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. + * A single inode exists for all anon_inode files, except for the secure ones. + * Contrary to pipes and secure anon_inode inodes, ordinary 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) { diff --git a/include/linux/anon_inodes.h b/include/linux/anon_inodes.h index d0d7d96261ad..6cf447cfceed 100644 --- a/include/linux/anon_inodes.h +++ b/include/linux/anon_inodes.h @@ -10,10 +10,15 @@ #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_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); -- 2.29.1.341.ge80a0c044ae-goog