aboutsummaryrefslogtreecommitdiffstats
path: root/security
diff options
context:
space:
mode:
authorJeff Layton <jlayton@redhat.com>2013-04-01 08:14:24 -0400
committerJames Morris <james.l.morris@oracle.com>2013-04-01 20:30:13 -0400
commit094f7b69ea738d7d619cba449d2af97159949459 (patch)
treec9ee28631d0c0c72487ba45113cda2793417c4eb /security
parent9b0271d8ca4d99becbf9ca1871c0d0faa5fad676 (diff)
selinux: make security_sb_clone_mnt_opts return an error on context mismatch
I had the following problem reported a while back. If you mount the same filesystem twice using NFSv4 with different contexts, then the second context= option is ignored. For instance: # mount server:/export /mnt/test1 # mount server:/export /mnt/test2 -o context=system_u:object_r:tmp_t:s0 # ls -dZ /mnt/test1 drwxrwxrwt. root root system_u:object_r:nfs_t:s0 /mnt/test1 # ls -dZ /mnt/test2 drwxrwxrwt. root root system_u:object_r:nfs_t:s0 /mnt/test2 When we call into SELinux to set the context of a "cloned" superblock, it will currently just bail out when it notices that we're reusing an existing superblock. Since the existing superblock is already set up and presumably in use, we can't go overwriting its context with the one from the "original" sb. Because of this, the second context= option in this case cannot take effect. This patch fixes this by turning security_sb_clone_mnt_opts into an int return operation. When it finds that the "new" superblock that it has been handed is already set up, it checks to see whether the contexts on the old superblock match it. If it does, then it will just return success, otherwise it'll return -EBUSY and emit a printk to tell the admin why the second mount failed. Note that this patch may cause casualties. The NFSv4 code relies on being able to walk down to an export from the pseudoroot. If you mount filesystems that are nested within one another with different contexts, then this patch will make those mounts fail in new and "exciting" ways. For instance, suppose that /export is a separate filesystem on the server: # mount server:/ /mnt/test1 # mount salusa:/export /mnt/test2 -o context=system_u:object_r:tmp_t:s0 mount.nfs: an incorrect mount option was specified ...with the printk in the ring buffer. Because we *might* eventually walk down to /mnt/test1/export, the mount is denied due to this patch. The second mount needs the pseudoroot superblock, but that's already present with the wrong context. OTOH, if we mount these in the reverse order, then both mounts work, because the pseudoroot superblock created when mounting /export is discarded once that mount is done. If we then however try to walk into that directory, the automount fails for the similar reasons: # cd /mnt/test1/scratch/ -bash: cd: /mnt/test1/scratch: Device or resource busy The story I've gotten from the SELinux folks that I've talked to is that this is desirable behavior. In SELinux-land, mounting the same data under different contexts is wrong -- there can be only one. Cc: Steve Dickson <steved@redhat.com> Cc: Stephen Smalley <sds@tycho.nsa.gov> Signed-off-by: Jeff Layton <jlayton@redhat.com> Acked-by: Eric Paris <eparis@redhat.com> Signed-off-by: James Morris <james.l.morris@oracle.com>
Diffstat (limited to 'security')
-rw-r--r--security/capability.c3
-rw-r--r--security/security.c4
-rw-r--r--security/selinux/hooks.c39
3 files changed, 39 insertions, 7 deletions
diff --git a/security/capability.c b/security/capability.c
index 579775088967..a6290b625be9 100644
--- a/security/capability.c
+++ b/security/capability.c
@@ -98,9 +98,10 @@ static int cap_sb_set_mnt_opts(struct super_block *sb,
98 return 0; 98 return 0;
99} 99}
100 100
101static void cap_sb_clone_mnt_opts(const struct super_block *oldsb, 101static int cap_sb_clone_mnt_opts(const struct super_block *oldsb,
102 struct super_block *newsb) 102 struct super_block *newsb)
103{ 103{
104 return 0;
104} 105}
105 106
106static int cap_sb_parse_opts_str(char *options, struct security_mnt_opts *opts) 107static int cap_sb_parse_opts_str(char *options, struct security_mnt_opts *opts)
diff --git a/security/security.c b/security/security.c
index 7b88c6aeaed4..108281d2307a 100644
--- a/security/security.c
+++ b/security/security.c
@@ -299,10 +299,10 @@ int security_sb_set_mnt_opts(struct super_block *sb,
299} 299}
300EXPORT_SYMBOL(security_sb_set_mnt_opts); 300EXPORT_SYMBOL(security_sb_set_mnt_opts);
301 301
302void security_sb_clone_mnt_opts(const struct super_block *oldsb, 302int security_sb_clone_mnt_opts(const struct super_block *oldsb,
303 struct super_block *newsb) 303 struct super_block *newsb)
304{ 304{
305 security_ops->sb_clone_mnt_opts(oldsb, newsb); 305 return security_ops->sb_clone_mnt_opts(oldsb, newsb);
306} 306}
307EXPORT_SYMBOL(security_sb_clone_mnt_opts); 307EXPORT_SYMBOL(security_sb_clone_mnt_opts);
308 308
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index 2fa28c88900c..3c02be3f6732 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -750,7 +750,37 @@ out_double_mount:
750 goto out; 750 goto out;
751} 751}
752 752
753static void selinux_sb_clone_mnt_opts(const struct super_block *oldsb, 753static int selinux_cmp_sb_context(const struct super_block *oldsb,
754 const struct super_block *newsb)
755{
756 struct superblock_security_struct *old = oldsb->s_security;
757 struct superblock_security_struct *new = newsb->s_security;
758 char oldflags = old->flags & SE_MNTMASK;
759 char newflags = new->flags & SE_MNTMASK;
760
761 if (oldflags != newflags)
762 goto mismatch;
763 if ((oldflags & FSCONTEXT_MNT) && old->sid != new->sid)
764 goto mismatch;
765 if ((oldflags & CONTEXT_MNT) && old->mntpoint_sid != new->mntpoint_sid)
766 goto mismatch;
767 if ((oldflags & DEFCONTEXT_MNT) && old->def_sid != new->def_sid)
768 goto mismatch;
769 if (oldflags & ROOTCONTEXT_MNT) {
770 struct inode_security_struct *oldroot = oldsb->s_root->d_inode->i_security;
771 struct inode_security_struct *newroot = newsb->s_root->d_inode->i_security;
772 if (oldroot->sid != newroot->sid)
773 goto mismatch;
774 }
775 return 0;
776mismatch:
777 printk(KERN_WARNING "SELinux: mount invalid. Same superblock, "
778 "different security settings for (dev %s, "
779 "type %s)\n", newsb->s_id, newsb->s_type->name);
780 return -EBUSY;
781}
782
783static int selinux_sb_clone_mnt_opts(const struct super_block *oldsb,
754 struct super_block *newsb) 784 struct super_block *newsb)
755{ 785{
756 const struct superblock_security_struct *oldsbsec = oldsb->s_security; 786 const struct superblock_security_struct *oldsbsec = oldsb->s_security;
@@ -765,14 +795,14 @@ static void selinux_sb_clone_mnt_opts(const struct super_block *oldsb,
765 * mount options. thus we can safely deal with this superblock later 795 * mount options. thus we can safely deal with this superblock later
766 */ 796 */
767 if (!ss_initialized) 797 if (!ss_initialized)
768 return; 798 return 0;
769 799
770 /* how can we clone if the old one wasn't set up?? */ 800 /* how can we clone if the old one wasn't set up?? */
771 BUG_ON(!(oldsbsec->flags & SE_SBINITIALIZED)); 801 BUG_ON(!(oldsbsec->flags & SE_SBINITIALIZED));
772 802
773 /* if fs is reusing a sb, just let its options stand... */ 803 /* if fs is reusing a sb, make sure that the contexts match */
774 if (newsbsec->flags & SE_SBINITIALIZED) 804 if (newsbsec->flags & SE_SBINITIALIZED)
775 return; 805 return selinux_cmp_sb_context(oldsb, newsb);
776 806
777 mutex_lock(&newsbsec->lock); 807 mutex_lock(&newsbsec->lock);
778 808
@@ -805,6 +835,7 @@ static void selinux_sb_clone_mnt_opts(const struct super_block *oldsb,
805 835
806 sb_finish_set_opts(newsb); 836 sb_finish_set_opts(newsb);
807 mutex_unlock(&newsbsec->lock); 837 mutex_unlock(&newsbsec->lock);
838 return 0;
808} 839}
809 840
810static int selinux_parse_opts_str(char *options, 841static int selinux_parse_opts_str(char *options,