Skip to content

Commit 0b4d345

Browse files
scottmayhewpcmoore
authored andcommitted
security/selinux: allow security_sb_clone_mnt_opts to enable/disable native labeling behavior
When an NFSv4 client performs a mount operation, it first mounts the NFSv4 root and then does path walk to the exported path and performs a submount on that, cloning the security mount options from the root's superblock to the submount's superblock in the process. Unless the NFS server has an explicit fsid=0 export with the "security_label" option, the NFSv4 root superblock will not have SBLABEL_MNT set, and neither will the submount superblock after cloning the security mount options. As a result, setxattr's of security labels over NFSv4.2 will fail. In a similar fashion, NFSv4.2 mounts mounted with the context= mount option will not show the correct labels because the nfs_server->caps flags of the cloned superblock will still have NFS_CAP_SECURITY_LABEL set. Allowing the NFSv4 client to enable or disable SECURITY_LSM_NATIVE_LABELS behavior will ensure that the SBLABEL_MNT flag has the correct value when the client traverses from an exported path without the "security_label" option to one with the "security_label" option and vice versa. Similarly, checking to see if SECURITY_LSM_NATIVE_LABELS is set upon return from security_sb_clone_mnt_opts() and clearing NFS_CAP_SECURITY_LABEL if necessary will allow the correct labels to be displayed for NFSv4.2 mounts mounted with the context= mount option. Resolves: SELinuxProject/selinux-kernel#35 Signed-off-by: Scott Mayhew <smayhew@redhat.com> Reviewed-by: Stephen Smalley <sds@tycho.nsa.gov> Tested-by: Stephen Smalley <sds@tycho.nsa.gov> Signed-off-by: Paul Moore <paul@paul-moore.com>
1 parent b4958c8 commit 0b4d345

File tree

5 files changed

+63
-8
lines changed

5 files changed

+63
-8
lines changed

fs/nfs/super.c

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2544,10 +2544,25 @@ EXPORT_SYMBOL_GPL(nfs_set_sb_security);
25442544
int nfs_clone_sb_security(struct super_block *s, struct dentry *mntroot,
25452545
struct nfs_mount_info *mount_info)
25462546
{
2547+
int error;
2548+
unsigned long kflags = 0, kflags_out = 0;
2549+
25472550
/* clone any lsm security options from the parent to the new sb */
25482551
if (d_inode(mntroot)->i_op != NFS_SB(s)->nfs_client->rpc_ops->dir_inode_ops)
25492552
return -ESTALE;
2550-
return security_sb_clone_mnt_opts(mount_info->cloned->sb, s);
2553+
2554+
if (NFS_SB(s)->caps & NFS_CAP_SECURITY_LABEL)
2555+
kflags |= SECURITY_LSM_NATIVE_LABELS;
2556+
2557+
error = security_sb_clone_mnt_opts(mount_info->cloned->sb, s, kflags,
2558+
&kflags_out);
2559+
if (error)
2560+
return error;
2561+
2562+
if (NFS_SB(s)->caps & NFS_CAP_SECURITY_LABEL &&
2563+
!(kflags_out & SECURITY_LSM_NATIVE_LABELS))
2564+
NFS_SB(s)->caps &= ~NFS_CAP_SECURITY_LABEL;
2565+
return 0;
25512566
}
25522567
EXPORT_SYMBOL_GPL(nfs_clone_sb_security);
25532568

include/linux/lsm_hooks.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1409,7 +1409,9 @@ union security_list_options {
14091409
unsigned long kern_flags,
14101410
unsigned long *set_kern_flags);
14111411
int (*sb_clone_mnt_opts)(const struct super_block *oldsb,
1412-
struct super_block *newsb);
1412+
struct super_block *newsb,
1413+
unsigned long kern_flags,
1414+
unsigned long *set_kern_flags);
14131415
int (*sb_parse_opts_str)(char *options, struct security_mnt_opts *opts);
14141416
int (*dentry_init_security)(struct dentry *dentry, int mode,
14151417
const struct qstr *name, void **ctx,

include/linux/security.h

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -249,7 +249,9 @@ int security_sb_set_mnt_opts(struct super_block *sb,
249249
unsigned long kern_flags,
250250
unsigned long *set_kern_flags);
251251
int security_sb_clone_mnt_opts(const struct super_block *oldsb,
252-
struct super_block *newsb);
252+
struct super_block *newsb,
253+
unsigned long kern_flags,
254+
unsigned long *set_kern_flags);
253255
int security_sb_parse_opts_str(char *options, struct security_mnt_opts *opts);
254256
int security_dentry_init_security(struct dentry *dentry, int mode,
255257
const struct qstr *name, void **ctx,
@@ -605,7 +607,9 @@ static inline int security_sb_set_mnt_opts(struct super_block *sb,
605607
}
606608

607609
static inline int security_sb_clone_mnt_opts(const struct super_block *oldsb,
608-
struct super_block *newsb)
610+
struct super_block *newsb,
611+
unsigned long kern_flags,
612+
unsigned long *set_kern_flags)
609613
{
610614
return 0;
611615
}

security/security.c

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -420,9 +420,12 @@ int security_sb_set_mnt_opts(struct super_block *sb,
420420
EXPORT_SYMBOL(security_sb_set_mnt_opts);
421421

422422
int security_sb_clone_mnt_opts(const struct super_block *oldsb,
423-
struct super_block *newsb)
423+
struct super_block *newsb,
424+
unsigned long kern_flags,
425+
unsigned long *set_kern_flags)
424426
{
425-
return call_int_hook(sb_clone_mnt_opts, 0, oldsb, newsb);
427+
return call_int_hook(sb_clone_mnt_opts, 0, oldsb, newsb,
428+
kern_flags, set_kern_flags);
426429
}
427430
EXPORT_SYMBOL(security_sb_clone_mnt_opts);
428431

security/selinux/hooks.c

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -525,8 +525,16 @@ static int sb_finish_set_opts(struct super_block *sb)
525525
}
526526

527527
sbsec->flags |= SE_SBINITIALIZED;
528+
529+
/*
530+
* Explicitly set or clear SBLABEL_MNT. It's not sufficient to simply
531+
* leave the flag untouched because sb_clone_mnt_opts might be handing
532+
* us a superblock that needs the flag to be cleared.
533+
*/
528534
if (selinux_is_sblabel_mnt(sb))
529535
sbsec->flags |= SBLABEL_MNT;
536+
else
537+
sbsec->flags &= ~SBLABEL_MNT;
530538

531539
/* Initialize the root inode. */
532540
rc = inode_doinit_with_dentry(root_inode, root);
@@ -959,8 +967,11 @@ static int selinux_cmp_sb_context(const struct super_block *oldsb,
959967
}
960968

961969
static int selinux_sb_clone_mnt_opts(const struct super_block *oldsb,
962-
struct super_block *newsb)
970+
struct super_block *newsb,
971+
unsigned long kern_flags,
972+
unsigned long *set_kern_flags)
963973
{
974+
int rc = 0;
964975
const struct superblock_security_struct *oldsbsec = oldsb->s_security;
965976
struct superblock_security_struct *newsbsec = newsb->s_security;
966977

@@ -975,6 +986,13 @@ static int selinux_sb_clone_mnt_opts(const struct super_block *oldsb,
975986
if (!ss_initialized)
976987
return 0;
977988

989+
/*
990+
* Specifying internal flags without providing a place to
991+
* place the results is not allowed.
992+
*/
993+
if (kern_flags && !set_kern_flags)
994+
return -EINVAL;
995+
978996
/* how can we clone if the old one wasn't set up?? */
979997
BUG_ON(!(oldsbsec->flags & SE_SBINITIALIZED));
980998

@@ -990,6 +1008,18 @@ static int selinux_sb_clone_mnt_opts(const struct super_block *oldsb,
9901008
newsbsec->def_sid = oldsbsec->def_sid;
9911009
newsbsec->behavior = oldsbsec->behavior;
9921010

1011+
if (newsbsec->behavior == SECURITY_FS_USE_NATIVE &&
1012+
!(kern_flags & SECURITY_LSM_NATIVE_LABELS) && !set_context) {
1013+
rc = security_fs_use(newsb);
1014+
if (rc)
1015+
goto out;
1016+
}
1017+
1018+
if (kern_flags & SECURITY_LSM_NATIVE_LABELS && !set_context) {
1019+
newsbsec->behavior = SECURITY_FS_USE_NATIVE;
1020+
*set_kern_flags |= SECURITY_LSM_NATIVE_LABELS;
1021+
}
1022+
9931023
if (set_context) {
9941024
u32 sid = oldsbsec->mntpoint_sid;
9951025

@@ -1009,8 +1039,9 @@ static int selinux_sb_clone_mnt_opts(const struct super_block *oldsb,
10091039
}
10101040

10111041
sb_finish_set_opts(newsb);
1042+
out:
10121043
mutex_unlock(&newsbsec->lock);
1013-
return 0;
1044+
return rc;
10141045
}
10151046

10161047
static int selinux_parse_opts_str(char *options,

0 commit comments

Comments
 (0)