Received: by 2002:a05:6a10:16a7:0:0:0:0 with SMTP id gp39csp102635pxb; Wed, 11 Nov 2020 21:38:59 -0800 (PST) X-Google-Smtp-Source: ABdhPJx3CJq+wRZ8H3DbKnRKmxD0p/jL6xKM+whrKI00UCb8jnIlcRQ4TQBPIUSMgFRRyPaIFR4G X-Received: by 2002:aa7:cb02:: with SMTP id s2mr3221527edt.211.1605159539298; Wed, 11 Nov 2020 21:38:59 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1605159539; cv=none; d=google.com; s=arc-20160816; b=EAKueQQtlxSUINxWd2aYVo/aW2uLStAYKQf0yOaiq7Vhkm+HsbeaXPjm2ysiF9tcVn rzYpa8TBQBy1pspyDH89lreoJO+5qF/65HRC7xGXLiZbDT4xVPPKYPdTGzn051CYyOyx sBS93VAPIjMWnUeH8bLRmTYj3DucEstX5RsInH9mUtMkflelHyNppN+Bp3ihj/Vmxykl 4SamGm+CelBtj/DG5JnvV7C3DsMgfNzkxmMe/ZGhmOuMf6u4YydWpeqhxfT+Li96q2+J 086LMDzU1AqHHzxfrz8QoStiNC9CM/wSXcm5VYURgYBWRcuVfX1kDCIo67w76l6UBxMg kHNw== 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=3lqfRZtVZbvZzlKideU4HlR83ofeb2JRCxMe3NEILqc=; b=jDgyPQZzGWF7fae66OOLOebvSoVx/tU56GXC0T5wZu6mZCdB1Vh8yVVjxlIsX05H1o Qjz8+DoeMeBqpQeHmdiPhvkOg2CVIfWNge/VjS6qbJ9+oVTR6HlICwb+lCceVJsZkNfV AfLMN8JUEebLd2/xlfQukr+CvlM7BV/dJYhiCYb80tp8TdChlIF6C2oVXPf5cQ7e022e AIAkCS/t+W/cIrW+yzV1s85tcA57egMxyrhx/MfrOm2HPNZwVb/gc0hidpLC6Ri3AVbM mCDgl3hxAOskRr/UpFUs5ojr4MJ56Dro02G4/KIY5ZxHt3xvZqJrEIotod8+yPffkooe PcFQ== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@google.com header.s=20161025 header.b=pdFjGdzJ; 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 a15si2938926ejs.508.2020.11.11.21.38.35; Wed, 11 Nov 2020 21:38:59 -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=pdFjGdzJ; 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 S1728628AbgKLFeC (ORCPT + 99 others); Thu, 12 Nov 2020 00:34:02 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:55192 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1728537AbgKLBy0 (ORCPT ); Wed, 11 Nov 2020 20:54:26 -0500 Received: from mail-pg1-x54a.google.com (mail-pg1-x54a.google.com [IPv6:2607:f8b0:4864:20::54a]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 753ACC040202 for ; Wed, 11 Nov 2020 17:54:12 -0800 (PST) Received: by mail-pg1-x54a.google.com with SMTP id d4so2491095pgi.16 for ; Wed, 11 Nov 2020 17:54:12 -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=3lqfRZtVZbvZzlKideU4HlR83ofeb2JRCxMe3NEILqc=; b=pdFjGdzJwu13zU0M/ASUiwCpli3mLLVPgtO6oabvuOWJu2ZP6zxWHqrFrIWAsQKvrJ OxOmpQOn6YZcjHoqviZ5TtjcLbUOkK6xCJypbCsuOnWgHTYNmmeGd3wzErIMA8sjwUEY FbFD3LT9IyF3iuyfAV19D6SWCiWgTvfJsDNSujCv9RXu1vMOe2D3wyQtqy5Gr0KsmU92 kzkXanNb6Z/EAGeE48pC8qjUp3i5E9A+MWGdzQdm/7k2d3xGohHv5VQ2yE3r+vXfNACk 84Nyc4K0WhhC91y/3PywLdJmN0Lfz9S1UDyuW8ctPDSSaumxKEoQvsetsPJc7yMK23aS VIMQ== 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=3lqfRZtVZbvZzlKideU4HlR83ofeb2JRCxMe3NEILqc=; b=tG8GjJ4onKlnX28mSrfMQOcetppte1S0LXlsUWy1MzAl6lgNUJscyu4m7yBVKj6HB9 kg5+B5YDNxmO7h1FrfDlkQmdL3XEGGHqf6nn/VEMcVkxpXoZRXALF/spw5ruPdAYS+dX bxGY7vuddqn6FmKII3YleXisAUtYTbl+CSyqwyPGkWim9xpsnxiY2CNnJRuigxkNdHjK oJlBVUJcKXOjNHGFcbqtqAP88PTQ6QOd/wLwyjStehz4BesS3bb5kN9G8QbANXg2gLRX F70FeQs0h+s7DMNQ9/qsRbauOvBugofqq9Vd0imvTj9XlfHDz9v+/ij5HaLbJDEhUVzl eOGQ== X-Gm-Message-State: AOAM530hxDFIwCRTvEVcxjoMy0JDkKpxXUHGTATPd02ZuBQ6SMcRT/GB KM1r8rOOWkqfjvsPfeqC//KTWaIDHF8u5m2DlQ== 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:90b:293:: with SMTP id az19mr685557pjb.1.1605146051307; Wed, 11 Nov 2020 17:54:11 -0800 (PST) Date: Wed, 11 Nov 2020 17:53:57 -0800 In-Reply-To: <20201112015359.1103333-1-lokeshgidra@google.com> Message-Id: <20201112015359.1103333-3-lokeshgidra@google.com> Mime-Version: 1.0 References: <20201112015359.1103333-1-lokeshgidra@google.com> X-Mailer: git-send-email 2.29.2.299.gdc1121823c-goog Subject: [PATCH v13 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 , 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, jeffv@google.com, kernel-team@android.com, linux-mm@kvack.org, Andrew Morton , hch@infradead.org, Daniel Colascione , Eric Biggers 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 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 [Delete obsolete 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 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 fc34361c1489..51c19c72e563 100644 --- a/fs/libfs.c +++ b/fs/libfs.c @@ -1212,11 +1212,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 */ -- 2.29.2.299.gdc1121823c-goog