Skip to content

Commit

Permalink
NFS: Ensure security label is set for root inode
Browse files Browse the repository at this point in the history
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 <[email protected]>
Signed-off-by: Scott Mayhew <[email protected]>
Acked-by: Stephen Smalley <[email protected]>
Tested-by: Stephen Smalley <[email protected]>
[PM: fixed 80-char line width problems]
Signed-off-by: Paul Moore <[email protected]>
  • Loading branch information
scottmayhew authored and pcmoore committed Mar 30, 2020
1 parent 2797887 commit 779df6a
Show file tree
Hide file tree
Showing 4 changed files with 39 additions and 38 deletions.
39 changes: 35 additions & 4 deletions fs/nfs/getroot.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -83,24 +84,27 @@ 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);
if (IS_ERR(inode)) {
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
Expand All @@ -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);
Expand All @@ -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;
}
12 changes: 3 additions & 9 deletions fs/nfs/nfs4proc.c
Original file line number Diff line number Diff line change
Expand Up @@ -3988,31 +3988,25 @@ 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) {
dprintk("nfs4_get_root: getcaps error = %d\n", -error);
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;
}

Expand Down
25 changes: 0 additions & 25 deletions fs/nfs/super.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;

Expand All @@ -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;
Expand Down
1 change: 1 addition & 0 deletions include/linux/nfs_xdr.h
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down

0 comments on commit 779df6a

Please sign in to comment.