Sophie

Sophie

distrib > Scientific%20Linux > 5x > x86_64 > by-pkgid > fc11cd6e1c513a17304da94a5390f3cd > files > 3704

kernel-2.6.18-194.11.1.el5.src.rpm

From: Eric Paris <eparis@redhat.com>
Date: Tue, 19 May 2009 21:36:56 -0400
Subject: [selinux] warn on nfs mounts with same SB but diff opts
Message-id: 1242783416.2763.27.camel@dhcp231-142.rdu.redhat.com
O-Subject: [PATCH RHEL5.4 -v4] SELinux: warn when 2 nfs mounts with the same SB have different selinux options
Bugzilla: 466701
RH-Acked-by: Steve Dickson <SteveD@redhat.com>
RH-Acked-by: Jeff Layton <jlayton@redhat.com>
RH-Acked-by: Peter Staubach <staubach@redhat.com>

BZ 466701

In RHEL5 if one attempts to mount the same nfs export two different
places with different selinux mount options the options of the first
mount will be used for both.  This is the expected behavior for RHEL5
(if it is correct is debatable.)  There have been a number of SELinux
and NFS changes to better support mount options handling in upstream
kernels.  The changes are not however appropriate for RHEL since the
decision was made upstream to fail the second mount rather than allow it
with the options of the first.  The BZ in questions complains that the
discrepancy is silently ignore in RHEL5.  This patch does nothing but
look for the case where the same NFS export is mounted in 2 places with
different selinux options and complains about it.  We want to complain
so users will be prepared when RHEL6 is released for the change in
behavior.

diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index b30a281..1cbe95c 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -607,6 +607,104 @@ out:
 	return rc;
 }
 
+/*
+ * This is a horrible function and I should be fired for proposing it on list
+ * but the real solution is a major filesystem mounting rewrite I did upstream
+ * that I just don't feel comfortable bringing it back to RHEL and would introduce
+ * compatibility problems in RHEL.
+ *
+ * NFS can reuse superblocks when mounting different shares.  This code checks
+ * if this superblock is already selinux initialized and if the selinux options
+ * passed to the mount command are the same.  Upstream we fail if we are given
+ * different selinux options, but for historical reasons we should continue to
+ * ignore the new mount options in RHEL.  Instead all this function is going to
+ * do is complain in dmesg that your mount will fail in RHEL6.
+ */
+static void nfs_check_sb_options(struct super_block *sb, void *data)
+{
+	struct nfs_mount_data *d = data;
+	struct superblock_security_struct *sbsec = sb->s_security;
+	static int sent = 0;
+
+	if (sent)
+		return;
+
+	/* no context= on sb and !data we are good to go. */
+	if ((sbsec->mntpoint_sid == SECINITSID_UNLABELED) &&
+	    (sbsec->behavior == SECURITY_FS_USE_GENFS) &&
+	    (!data))
+		return;
+
+	/* This ain't supposed to happen */
+	if (!data) {
+		sent = 1;
+		printk(KERN_WARNING "SELinux: inside nfs_check_sb_options with !data.  Please inform RH support.\n");
+		return;
+	}
+
+	/* do we know how to handle this mount data? If not, just go ahead */
+	if (d->version < NFS_MOUNT_VERSION ||
+	    d->version > NFS_MOUNT_VERSION + 50)
+		return;
+
+	/* common case, no context= option at all */
+	if ((sbsec->mntpoint_sid == SECINITSID_UNLABELED) &&
+	    (sbsec->behavior == SECURITY_FS_USE_GENFS) &&
+	    (!d->context[0]))
+		return;
+
+	/* less common but ok, both things had that same label */
+	if ((sbsec->behavior == SECURITY_FS_USE_MNTPOINT) &&
+	    (d->context[0])) {
+		char *context = &d->context[0];
+		int rc;
+		u32 sid;
+
+		rc = security_context_to_sid(context, strlen(context), &sid);
+		/* on error announce the error and pass to the full printk */
+		if (rc)
+			printk(KERN_WARNING "SELinux: the context=%s used on this mount is an invalid context.\n", context);
+		/* if the old and new sid are the same that's great! */
+		else if (sid == sbsec->mntpoint_sid)
+			return;
+	}
+
+	sent = 1;
+
+	printk(KERN_WARNING "SELinux: You have mounted the same NFS export with different context= options.\n");
+	printk(KERN_WARNING "SELinux: The options used on the first mount will be used for both mounts.\n");
+	printk(KERN_WARNING "SELinux: In RHEL6 or later the second mount will not be allowed at all.\n");
+	printk(KERN_WARNING "SELinux: You may wish to investigate the nosharecache NFS options in order to get\n");
+	printk(KERN_WARNING "SELinux:    separate superblocks (and thus separate SELinux options) for the\n");
+	printk(KERN_WARNING "SELinux:    NFS mount dev=%s if you REALLY need this functionality.\n", sb->s_id);
+
+	/* print context= about first mount */
+	if (sbsec->behavior == SECURITY_FS_USE_MNTPOINT) {
+		char *context;
+		u32 len;
+		int rc;
+
+		rc = security_sid_to_context(sbsec->mntpoint_sid, &context, &len);
+		if (!rc) {
+			printk(KERN_WARNING "SELinux: original mount had the context=%s\n", context);
+			kfree(context);
+		}
+	} else {
+		printk(KERN_WARNING "SELinux: original mount had no context= mount option.\n");
+	}
+
+	/* print context= about second mount */
+	if (d->context[0])
+		printk(KERN_WARNING "SELinux: new mount has the context=%s\n", &d->context[0]);
+	else
+		printk(KERN_WARNING "SELinux: new mount has no context= mount option.\n");
+
+	printk(KERN_WARNING "SELinux: This is the only time you will get this message even if this\n");
+	printk(KERN_WARNING "SELinux:    is not the only mount in this situation.\n");
+
+	return;
+}
+
 static int superblock_doinit(struct super_block *sb, void *data)
 {
 	struct superblock_security_struct *sbsec = sb->s_security;
@@ -615,8 +713,11 @@ static int superblock_doinit(struct super_block *sb, void *data)
 	int rc = 0;
 
 	down(&sbsec->sem);
-	if (sbsec->initialized)
+	if (sbsec->initialized) {
+		if (!strncmp("nfs", sb->s_type->name, 3))
+			nfs_check_sb_options(sb, data);
 		goto out;
+	}
 
 	if (!ss_initialized) {
 		/* Defer initialization until selinux_complete_init,