2021-05-17 14:19:44

by Ondrej Mosnacek

[permalink] [raw]
Subject: [PATCH v2 2/2] selinux: fix SECURITY_LSM_NATIVE_LABELS flag handling on double mount

When mounting an NFS export that is a mountpoint on the host, doing the
same mount a second time leads to a security_sb_set_mnt_opts() call on
an already intialized superblock, which leaves the
SECURITY_LSM_NATIVE_LABELS flag unset even if it's provided by the FS.
NFS then obediently clears NFS_CAP_SECURITY_LABEL from its server
capability set, leading to any newly created inodes for this superblock
to end up without labels.

To fix this, make sure to return the SECURITY_LSM_NATIVE_LABELS flag
when security_sb_set_mnt_opts() is called on an already initialized
superblock with matching security options.

While there, also do a sanity check to ensure that
SECURITY_LSM_NATIVE_LABELS is set in kflags if and only if
sbsec->behavior == SECURITY_FS_USE_NATIVE.

Minimal reproducer:
# systemctl start nfs-server
# exportfs -o rw,no_root_squash,security_label localhost:/
# mount -t nfs -o "nfsvers=4.2" localhost:/etc /mnt
# mount -t nfs -o "nfsvers=4.2" localhost:/etc /mnt
# ls -lZ /mnt
[all labels are system_u:object_r:unlabeled_t:s0]

Signed-off-by: Ondrej Mosnacek <[email protected]>
---
security/selinux/hooks.c | 17 +++++++++++++++++
1 file changed, 17 insertions(+)

diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index 041529cbf214..367e7739cb18 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -734,7 +734,24 @@ static int selinux_set_mnt_opts(struct super_block *sb,
/* previously mounted with options, but not on this attempt? */
if ((sbsec->flags & SE_MNTMASK) && !opts)
goto out_double_mount;
+
+ /*
+ * If we are checking an already initialized mount and the
+ * options match, make sure to return back the
+ * SECURITY_LSM_NATIVE_LABELS flag if applicable. If the
+ * superblock has the NATIVE behavior set and the FS is not
+ * signaling its support (or vice versa), then it is a
+ * programmer error, so emit a WARNING and return -EINVAL.
+ */
rc = 0;
+ if (sbsec->behavior == SECURITY_FS_USE_NATIVE) {
+ if (WARN_ON(!(kern_flags & SECURITY_LSM_NATIVE_LABELS)))
+ rc = -EINVAL;
+ else
+ *set_kern_flags |= SECURITY_LSM_NATIVE_LABELS;
+ } else if (WARN_ON(kern_flags & SECURITY_LSM_NATIVE_LABELS)) {
+ rc = -EINVAL;
+ }
goto out;
}

--
2.31.1