NFS: Ensure security label is set for root inode

When using NFSv4.2, the security label for the root inode should be set
via a call to nfs_setsecurity() during the mount process, otherwise the
inode will appear as unlabeled for up to acdirmin seconds.  Currently
the label for the root inode is allocated, retrieved, and freed entirely
witin nfs4_proc_get_root().

Add a field for the label to the nfs_fattr struct, and allocate & free
the label in nfs_get_root(), where we also add a call to
nfs_setsecurity().  Note that for the call to nfs_setsecurity() to
succeed, it's necessary to also move the logic calling
security_sb_{set,clone}_security() from nfs_get_tree_common() down into
nfs_get_root()... otherwise the SBLABEL_MNT flag will not be set in the
super_block's security flags and nfs_setsecurity() will silently fail.

Reported-by: Richard Haines <richard_c_haines@btinternet.com>
Signed-off-by: Scott Mayhew <smayhew@redhat.com>
Acked-by: Stephen Smalley <sds@tycho.nsa.gov>
Tested-by: Stephen Smalley <sds@tycho.nsa.gov>
[PM: fixed 80-char line width problems]
Signed-off-by: Paul Moore <paul@paul-moore.com>
This commit is contained in:
Scott Mayhew 2020-03-03 17:58:37 -05:00 committed by Paul Moore
parent 2797887217
commit 779df6a548
4 changed files with 39 additions and 38 deletions

View File

@ -73,6 +73,7 @@ int nfs_get_root(struct super_block *s, struct fs_context *fc)
struct inode *inode;
char *name;
int error = -ENOMEM;
unsigned long kflags = 0, kflags_out = 0;
name = kstrdup(fc->source, GFP_KERNEL);
if (!name)
@ -83,11 +84,14 @@ int nfs_get_root(struct super_block *s, struct fs_context *fc)
if (fsinfo.fattr == NULL)
goto out_name;
fsinfo.fattr->label = nfs4_label_alloc(server, GFP_KERNEL);
if (IS_ERR(fsinfo.fattr->label))
goto out_fattr;
error = server->nfs_client->rpc_ops->getroot(server, ctx->mntfh, &fsinfo);
if (error < 0) {
dprintk("nfs_get_root: getattr error = %d\n", -error);
nfs_errorf(fc, "NFS: Couldn't getattr on root");
goto out_fattr;
goto out_label;
}
inode = nfs_fhget(s, ctx->mntfh, fsinfo.fattr, NULL);
@ -95,12 +99,12 @@ int nfs_get_root(struct super_block *s, struct fs_context *fc)
dprintk("nfs_get_root: get root inode failed\n");
error = PTR_ERR(inode);
nfs_errorf(fc, "NFS: Couldn't get root inode");
goto out_fattr;
goto out_label;
}
error = nfs_superblock_set_dummy_root(s, inode);
if (error != 0)
goto out_fattr;
goto out_label;
/* root dentries normally start off anonymous and get spliced in later
* if the dentry tree reaches them; however if the dentry already
@ -111,7 +115,7 @@ int nfs_get_root(struct super_block *s, struct fs_context *fc)
dprintk("nfs_get_root: get root dentry failed\n");
error = PTR_ERR(root);
nfs_errorf(fc, "NFS: Couldn't get root dentry");
goto out_fattr;
goto out_label;
}
security_d_instantiate(root, inode);
@ -123,12 +127,39 @@ int nfs_get_root(struct super_block *s, struct fs_context *fc)
}
spin_unlock(&root->d_lock);
fc->root = root;
if (NFS_SB(s)->caps & NFS_CAP_SECURITY_LABEL)
kflags |= SECURITY_LSM_NATIVE_LABELS;
if (ctx->clone_data.sb) {
if (d_inode(fc->root)->i_fop != &nfs_dir_operations) {
error = -ESTALE;
goto error_splat_root;
}
/* clone lsm security options from the parent to the new sb */
error = security_sb_clone_mnt_opts(ctx->clone_data.sb,
s, kflags, &kflags_out);
} else {
error = security_sb_set_mnt_opts(s, fc->security,
kflags, &kflags_out);
}
if (error)
goto error_splat_root;
if (NFS_SB(s)->caps & NFS_CAP_SECURITY_LABEL &&
!(kflags_out & SECURITY_LSM_NATIVE_LABELS))
NFS_SB(s)->caps &= ~NFS_CAP_SECURITY_LABEL;
nfs_setsecurity(inode, fsinfo.fattr, fsinfo.fattr->label);
error = 0;
out_label:
nfs4_label_free(fsinfo.fattr->label);
out_fattr:
nfs_free_fattr(fsinfo.fattr);
out_name:
kfree(name);
out:
return error;
error_splat_root:
dput(fc->root);
fc->root = NULL;
goto out_label;
}

View File

@ -3988,7 +3988,7 @@ static int nfs4_proc_get_root(struct nfs_server *server, struct nfs_fh *mntfh,
{
int error;
struct nfs_fattr *fattr = info->fattr;
struct nfs4_label *label = NULL;
struct nfs4_label *label = fattr->label;
error = nfs4_server_capabilities(server, mntfh);
if (error < 0) {
@ -3996,23 +3996,17 @@ static int nfs4_proc_get_root(struct nfs_server *server, struct nfs_fh *mntfh,
return error;
}
label = nfs4_label_alloc(server, GFP_KERNEL);
if (IS_ERR(label))
return PTR_ERR(label);
error = nfs4_proc_getattr(server, mntfh, fattr, label, NULL);
if (error < 0) {
dprintk("nfs4_get_root: getattr error = %d\n", -error);
goto err_free_label;
goto out;
}
if (fattr->valid & NFS_ATTR_FATTR_FSID &&
!nfs_fsid_equal(&server->fsid, &fattr->fsid))
memcpy(&server->fsid, &fattr->fsid, sizeof(server->fsid));
err_free_label:
nfs4_label_free(label);
out:
return error;
}

View File

@ -1179,7 +1179,6 @@ int nfs_get_tree_common(struct fs_context *fc)
struct super_block *s;
int (*compare_super)(struct super_block *, struct fs_context *) = nfs_compare_super;
struct nfs_server *server = ctx->server;
unsigned long kflags = 0, kflags_out = 0;
int error;
ctx->server = NULL;
@ -1239,26 +1238,6 @@ int nfs_get_tree_common(struct fs_context *fc)
goto error_splat_super;
}
if (NFS_SB(s)->caps & NFS_CAP_SECURITY_LABEL)
kflags |= SECURITY_LSM_NATIVE_LABELS;
if (ctx->clone_data.sb) {
if (d_inode(fc->root)->i_fop != &nfs_dir_operations) {
error = -ESTALE;
goto error_splat_root;
}
/* clone any lsm security options from the parent to the new sb */
error = security_sb_clone_mnt_opts(ctx->clone_data.sb, s, kflags,
&kflags_out);
} else {
error = security_sb_set_mnt_opts(s, fc->security,
kflags, &kflags_out);
}
if (error)
goto error_splat_root;
if (NFS_SB(s)->caps & NFS_CAP_SECURITY_LABEL &&
!(kflags_out & SECURITY_LSM_NATIVE_LABELS))
NFS_SB(s)->caps &= ~NFS_CAP_SECURITY_LABEL;
s->s_flags |= SB_ACTIVE;
error = 0;
@ -1268,10 +1247,6 @@ int nfs_get_tree_common(struct fs_context *fc)
out_err_nosb:
nfs_free_server(server);
goto out;
error_splat_root:
dput(fc->root);
fc->root = NULL;
error_splat_super:
deactivate_locked_super(s);
goto out;

View File

@ -75,6 +75,7 @@ struct nfs_fattr {
struct nfs4_string *owner_name;
struct nfs4_string *group_name;
struct nfs4_threshold *mdsthreshold; /* pNFS threshold hints */
struct nfs4_label *label;
};
#define NFS_ATTR_FATTR_TYPE (1U << 0)