Re: [PATCH v3] nfs: Fix automount superblock LSM init problem, preventing sb sharing
From: Ondrej Mosnacek
Date: Thu Aug 11 2022 - 08:29:05 EST
On Fri, Aug 5, 2022 at 3:36 PM David Howells <dhowells@xxxxxxxxxx> wrote:
> When NFS superblocks are created by automounting, their LSM parameters
> aren't set in the fs_context struct prior to sget_fc() being called,
> leading to failure to match existing superblocks.
>
> Fix this by adding a new LSM hook to load fc->security for submount
> creation when alloc_fs_context() is creating the fs_context for it.
>
> However, this uncovers a further bug: nfs_get_root() initialises the
> superblock security manually by calling security_sb_set_mnt_opts() or
> security_sb_clone_mnt_opts() - but then vfs_get_tree() calls
> security_sb_set_mnt_opts(), which can lead to SELinux, at least,
> complaining.
>
> Fix that by adding a flag to the fs_context that suppresses the
> security_sb_set_mnt_opts() call in vfs_get_tree(). This can be set by NFS
> when it sets the LSM context on the new superblock.
>
> The first bug leads to messages like the following appearing in dmesg:
>
> NFS: Cache volume key already in use (nfs,4.2,2,108,106a8c0,1,,,,100000,100000,2ee,3a98,1d4c,3a98,1)
>
> Changes
> =======
> ver #2)
> - Made LSM parameter extraction dependent on fc->purpose ==
> FS_CONTEXT_FOR_SUBMOUNT. Shouldn't happen on FOR_RECONFIGURE.
>
> ver #2)
> - Added Smack support
> - Made LSM parameter extraction dependent on reference != NULL.
>
> Signed-off-by: David Howells <dhowells@xxxxxxxxxx>
> Fixes: 9bc61ab18b1d ("vfs: Introduce fs_context, switch vfs_kern_mount() to it.")
> Fixes: 779df6a5480f ("NFS: Ensure security label is set for root inode)
> cc: Trond Myklebust <trond.myklebust@xxxxxxxxxxxxxxx>
> cc: Anna Schumaker <anna@xxxxxxxxxx>
> cc: Alexander Viro <viro@xxxxxxxxxxxxxxxxxx>
> cc: Scott Mayhew <smayhew@xxxxxxxxxx>
> cc: Jeff Layton <jlayton@xxxxxxxxxx>
> cc: Paul Moore <paul@xxxxxxxxxxxxxx>
> cc: Casey Schaufler <casey@xxxxxxxxxxxxxxxx>
> cc: linux-nfs@xxxxxxxxxxxxxxx
> cc: selinux@xxxxxxxxxxxxxxx
> cc: linux-security-module@xxxxxxxxxxxxxxx
> cc: linux-fsdevel@xxxxxxxxxxxxxxx
> ---
>
> fs/fs_context.c | 4 +++
> fs/nfs/getroot.c | 1 +
> fs/super.c | 10 ++++---
> include/linux/fs_context.h | 1 +
> include/linux/lsm_hook_defs.h | 1 +
> include/linux/lsm_hooks.h | 6 +++-
> include/linux/security.h | 6 ++++
> security/security.c | 5 +++
> security/selinux/hooks.c | 29 +++++++++++++++++++
> security/smack/smack_lsm.c | 61 +++++++++++++++++++++++++++++++++++++++++
> 10 files changed, 119 insertions(+), 5 deletions(-)
<snip>
> diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
> index 1bbd53321d13..ddeaff4f3bb1 100644
> --- a/security/selinux/hooks.c
> +++ b/security/selinux/hooks.c
> @@ -2768,6 +2768,34 @@ static int selinux_umount(struct vfsmount *mnt, int flags)
> FILESYSTEM__UNMOUNT, NULL);
> }
>
> +static int selinux_fs_context_init(struct fs_context *fc,
> + struct dentry *reference)
> +{
> + const struct superblock_security_struct *sbsec;
> + const struct inode_security_struct *root_isec;
> + struct selinux_mnt_opts *opts;
> +
> + if (fc->purpose == FS_CONTEXT_FOR_SUBMOUNT) {
> + opts = kzalloc(sizeof(*opts), GFP_KERNEL);
> + if (!opts)
> + return -ENOMEM;
> +
> + root_isec = backing_inode_security(reference->d_sb->s_root);
> + sbsec = selinux_superblock(reference->d_sb);
> + if (sbsec->flags & FSCONTEXT_MNT)
> + opts->fscontext_sid = sbsec->sid;
> + if (sbsec->flags & CONTEXT_MNT)
> + opts->context_sid = sbsec->mntpoint_sid;
> + if (sbsec->flags & ROOTCONTEXT_MNT)
> + opts->rootcontext_sid = root_isec->sid;
I wonder if this part is correct... The rootcontext=... mount option
relates to the root inode of the mount where it is specified - i.e. in
case of NFS only to the toplevel inode of the initial mount. Setting
the same context on the root inode of submounts, which AFAIK are
supposed to be transparent to the user, doesn't seem correct to me -
i.e. it should just be left unset for the automatically created
submounts.
That said, I always feel lost in the super-complex LSM-VFS
interactions, so I'd welcome it if someone gives a second opinion
here.
> + if (sbsec->flags & DEFCONTEXT_MNT)
> + opts->defcontext_sid = sbsec->def_sid;
> + fc->security = opts;
> + }
> +
> + return 0;
> +}
> +
> static int selinux_fs_context_dup(struct fs_context *fc,
> struct fs_context *src_fc)
> {
> @@ -7239,6 +7267,7 @@ static struct security_hook_list selinux_hooks[] __lsm_ro_after_init = {
> /*
> * PUT "CLONING" (ACCESSING + ALLOCATING) HOOKS HERE
> */
> + LSM_HOOK_INIT(fs_context_init, selinux_fs_context_init),
> LSM_HOOK_INIT(fs_context_dup, selinux_fs_context_dup),
> LSM_HOOK_INIT(fs_context_parse_param, selinux_fs_context_parse_param),
> LSM_HOOK_INIT(sb_eat_lsm_opts, selinux_sb_eat_lsm_opts),
> diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c
> index 6207762dbdb1..6ba32bb097b5 100644
> --- a/security/smack/smack_lsm.c
> +++ b/security/smack/smack_lsm.c
> @@ -612,6 +612,66 @@ static int smack_add_opt(int token, const char *s, void **mnt_opts)
> return -EINVAL;
> }
>
> +/**
> + * smack_fs_context_init - Initialise security data for a filesystem context
> + * @fc: The filesystem context.
> + * @reference: Reference dentry (automount/reconfigure) or NULL
> + *
> + * Returns 0 on success or -ENOMEM on error.
> + */
> +static int smack_fs_context_init(struct fs_context *fc,
> + struct dentry *reference)
> +{
> + struct superblock_smack *sbsp;
> + struct smack_mnt_opts *ctx;
> + struct inode_smack *isp;
> +
> + ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
> + if (!ctx)
> + return -ENOMEM;
> + fc->security = ctx;
> +
> + if (fc->purpose == FS_CONTEXT_FOR_SUBMOUNT) {
> + sbsp = smack_superblock(reference->d_sb);
> + isp = smack_inode(reference->d_sb->s_root->d_inode);
> +
> + if (sbsp->smk_default) {
> + ctx->fsdefault = kstrdup(sbsp->smk_default->smk_known, GFP_KERNEL);
> + if (!ctx->fsdefault)
> + return -ENOMEM;
> + }
> +
> + if (sbsp->smk_floor) {
> + ctx->fsfloor = kstrdup(sbsp->smk_floor->smk_known, GFP_KERNEL);
> + if (!ctx->fsfloor)
> + return -ENOMEM;
> + }
> +
> + if (sbsp->smk_hat) {
> + ctx->fshat = kstrdup(sbsp->smk_hat->smk_known, GFP_KERNEL);
> + if (!ctx->fshat)
> + return -ENOMEM;
> + }
> +
> +
^ double empty line, FWIW
> + if (isp->smk_flags & SMK_INODE_TRANSMUTE) {
> + if (sbsp->smk_root) {
> + ctx->fstransmute = kstrdup(sbsp->smk_root->smk_known, GFP_KERNEL);
> + if (!ctx->fstransmute)
> + return -ENOMEM;
> + }
> + } else {
> + if (sbsp->smk_root) {
> + ctx->fsroot = kstrdup(sbsp->smk_root->smk_known, GFP_KERNEL);
> + if (!ctx->fsroot)
> + return -ENOMEM;
> + }
> + }
Similar concerns may or may not apply here, but I know too little
about how Smack handles mount options to be sure.
> + }
> +
> + return 0;
> +}
> +
> /**
> * smack_fs_context_dup - Duplicate the security data on fs_context duplication
> * @fc: The new filesystem context.
> @@ -4755,6 +4815,7 @@ static struct security_hook_list smack_hooks[] __lsm_ro_after_init = {
> LSM_HOOK_INIT(ptrace_traceme, smack_ptrace_traceme),
> LSM_HOOK_INIT(syslog, smack_syslog),
>
> + LSM_HOOK_INIT(fs_context_init, smack_fs_context_init),
> LSM_HOOK_INIT(fs_context_dup, smack_fs_context_dup),
> LSM_HOOK_INIT(fs_context_parse_param, smack_fs_context_parse_param),
>
>
>
--
Ondrej Mosnacek
Senior Software Engineer, Linux Security - SELinux kernel
Red Hat, Inc.