Received: by 2002:a05:6a10:16a7:0:0:0:0 with SMTP id gp39csp1369282pxb; Fri, 6 Nov 2020 07:58:35 -0800 (PST) X-Google-Smtp-Source: ABdhPJxKtncywN/nc7sw0terPnCytW4T0mSkesvUE2vHOw4x+QEWhIjYVgaqo2yNZ6qoqMn1uxIL X-Received: by 2002:a05:6402:1119:: with SMTP id u25mr2812797edv.37.1604678315187; Fri, 06 Nov 2020 07:58:35 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1604678315; cv=none; d=google.com; s=arc-20160816; b=aqgt/oUBZfa13dFTlFKR/ktRhcuyrOGQNrTgNHBNR0C3zhzj+oPJ0BqJoYbkCt9T/2 h9zKDLxzhZO8n5AVCUy0VD/tpi6voaEh/YLsY87ILzSHfC4cJQwcEmmv1os5v+klSJ0T MXbUL/wKRgXWt1sXmYEqK1e/+CoMa0r6kfMKpebAMaGDQFiehr+wR1oN5fHimRrz8067 5xXUHR+W0rXy/gfaj6MF8m9D4L1YIud+Qlscah+HmRgKvubIZ3ulxIpdr3GsFGsPTaqj r9ewXRlemG1RpeSSA+8IyaDIIGjjnU5R7+1R95wKpRxRilWyoT6vDJz2FmYLa617zbz9 vD4w== 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=K1tKoG3gV3mvaGumxi+ncw/ywOUYroypjPAyFUGu9Nk=; b=o3Tf30a2kFFQ7e01/I6Ad/a57qlXAZgc48RQWze1tNApVv4flknKf2QymXBi1ynqvW zYZcILBI5JLk7YTrKASWEU5U30gQsO2IhzxY1Vd1rwUWKROBv8ud+SKNuOlY80Z/0sx7 aGH1daJEp7J62e+256MoCu2s7GuSYbRTnIEInkg6AKhtr4slk49tI3oabAzodGgpt4j6 z02PBLYB1SoUL23N48aoIlEQQGoqNFg5NuVcrwmEiBQepghReUIZMh4rTUCb8+RYbwJk hcg6/DSpUPaROkgjkudXf/nnWNxpVntSOAe6BQAESDOdLgsmGnt6KYDtveGUvKgzz8zT 9Hxw== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@google.com header.s=20161025 header.b=jVcvCNyi; 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 r26si1218137ejb.420.2020.11.06.07.58.11; Fri, 06 Nov 2020 07:58:35 -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=jVcvCNyi; 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 S1727664AbgKFP4n (ORCPT + 99 others); Fri, 6 Nov 2020 10:56:43 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:57450 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1727703AbgKFP4l (ORCPT ); Fri, 6 Nov 2020 10:56:41 -0500 Received: from mail-pl1-x649.google.com (mail-pl1-x649.google.com [IPv6:2607:f8b0:4864:20::649]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 13174C0613CF for ; Fri, 6 Nov 2020 07:56:40 -0800 (PST) Received: by mail-pl1-x649.google.com with SMTP id u14so1098003plq.5 for ; Fri, 06 Nov 2020 07:56:40 -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=K1tKoG3gV3mvaGumxi+ncw/ywOUYroypjPAyFUGu9Nk=; b=jVcvCNyiv2ekGvZP1xq6eEzynrjZKEm6F9bVVR1vjTG0wQS7fufyRih438mqK7Kkiq KLOAWvUZ424CkL4A2tfOOif3hxB+InetxFxBpaTDUwz8fxbU5gz4gQON7mQDB8YIiQ7/ KTx6P8k8hOZXBok4bKAXalbY5qtdO9U3UjDqWBDKQw9o1gu4P4bT4B8huyjSTfbBkfkr 630T8LxaWzDI9OXCo4Idi9mA+B93K0DKxNkJbSp7FucfAzBdS0ijNTk2M5JhesxbNg4c /E00PdkrTeQLC2Vp+HTGygyYrsb266mOD8C82hVwlXiSj1gUQBA5dduyX8jwUwkibToo p17A== 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=K1tKoG3gV3mvaGumxi+ncw/ywOUYroypjPAyFUGu9Nk=; b=URrcOnmpMBKP95UjhrAhoxS4EoW9LF/dJcTwjDLDnzeKYd1jWCgVim6+kOPZmFe/my 79EADI1c75yvZNb1B1R7UxEwFw3U0FYcgp87vHPAOK1b92vWcEW5lFCFyyBMncCo5oao 4Kq/qC7HQoEgB8gGCSYZQUh1R+joXSespuNjKPRO5WxPJp9tuwzZGQgTKRI64DGxMkm2 8+vBTozFyssssnXkxrtCVWZYtL7MTYX4uWSOTuSRCBzauUl13KnO43YpYVwOVIc9rYPF MujXgIGEYs5RDJcKVp/KyvGip+m7jt1/BUlWlaRkuvefIfjGjNKmjJhO3ZJYCfdkUta4 Y1oQ== X-Gm-Message-State: AOAM532IjnaAR15cZOi5fHAB1e3kI+I7RMhwh/cwz9hi8+NL7q5iiT/2 OJksjCUo1uv6piHWu3aLhWbsVdjoc1GQoaMAGw== 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:3d0f:: with SMTP id h15mr218382pjc.234.1604678199465; Fri, 06 Nov 2020 07:56:39 -0800 (PST) Date: Fri, 6 Nov 2020 07:56:24 -0800 In-Reply-To: <20201106155626.3395468-1-lokeshgidra@google.com> Message-Id: <20201106155626.3395468-3-lokeshgidra@google.com> Mime-Version: 1.0 References: <20201106155626.3395468-1-lokeshgidra@google.com> X-Mailer: git-send-email 2.29.1.341.ge80a0c044ae-goog Subject: [PATCH v12 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 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 --- 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..f4b35aaed7b2 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 call the anon_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.1.341.ge80a0c044ae-goog