diff --git a/fs/nfs/super.c b/fs/nfs/super.c index 95cdcb208dfb630396058a0688c4341a170b4968..6b4bf7622280223492abe76fa6b3a1e7f17537fa 100644 --- a/fs/nfs/super.c +++ b/fs/nfs/super.c @@ -2380,10 +2380,9 @@ int nfs_clone_sb_security(struct super_block *s, struct dentry *mntroot, struct nfs_mount_info *mount_info) { /* clone any lsm security options from the parent to the new sb */ - security_sb_clone_mnt_opts(mount_info->cloned->sb, s); if (mntroot->d_inode->i_op != NFS_SB(s)->nfs_client->rpc_ops->dir_inode_ops) return -ESTALE; - return 0; + return security_sb_clone_mnt_opts(mount_info->cloned->sb, s); } EXPORT_SYMBOL_GPL(nfs_clone_sb_security); diff --git a/include/linux/security.h b/include/linux/security.h index eee7478cda701ddeabc00150f607f368a4239350..4c7058dc55145019a20951151f1ad6d74201f5d8 100644 --- a/include/linux/security.h +++ b/include/linux/security.h @@ -1436,7 +1436,7 @@ struct security_operations { struct path *new_path); int (*sb_set_mnt_opts) (struct super_block *sb, struct security_mnt_opts *opts); - void (*sb_clone_mnt_opts) (const struct super_block *oldsb, + int (*sb_clone_mnt_opts) (const struct super_block *oldsb, struct super_block *newsb); int (*sb_parse_opts_str) (char *options, struct security_mnt_opts *opts); @@ -1721,7 +1721,7 @@ int security_sb_mount(const char *dev_name, struct path *path, int security_sb_umount(struct vfsmount *mnt, int flags); int security_sb_pivotroot(struct path *old_path, struct path *new_path); int security_sb_set_mnt_opts(struct super_block *sb, struct security_mnt_opts *opts); -void security_sb_clone_mnt_opts(const struct super_block *oldsb, +int security_sb_clone_mnt_opts(const struct super_block *oldsb, struct super_block *newsb); int security_sb_parse_opts_str(char *options, struct security_mnt_opts *opts); @@ -2011,9 +2011,11 @@ static inline int security_sb_set_mnt_opts(struct super_block *sb, return 0; } -static inline void security_sb_clone_mnt_opts(const struct super_block *oldsb, +static inline int security_sb_clone_mnt_opts(const struct super_block *oldsb, struct super_block *newsb) -{ } +{ + return 0; +} static inline int security_sb_parse_opts_str(char *options, struct security_mnt_opts *opts) { diff --git a/security/capability.c b/security/capability.c index 579775088967fdb11a044beb75c70de48d572ad6..a6290b625be939baf15aa870913119a68e33d73e 100644 --- a/security/capability.c +++ b/security/capability.c @@ -98,9 +98,10 @@ static int cap_sb_set_mnt_opts(struct super_block *sb, return 0; } -static void cap_sb_clone_mnt_opts(const struct super_block *oldsb, +static int cap_sb_clone_mnt_opts(const struct super_block *oldsb, struct super_block *newsb) { + return 0; } static int cap_sb_parse_opts_str(char *options, struct security_mnt_opts *opts) diff --git a/security/security.c b/security/security.c index 7b88c6aeaed43e5f37b449fd8ec87610b3b69a93..108281d2307a0999fe447caaf777e3dc736799a6 100644 --- a/security/security.c +++ b/security/security.c @@ -299,10 +299,10 @@ int security_sb_set_mnt_opts(struct super_block *sb, } EXPORT_SYMBOL(security_sb_set_mnt_opts); -void security_sb_clone_mnt_opts(const struct super_block *oldsb, +int security_sb_clone_mnt_opts(const struct super_block *oldsb, struct super_block *newsb) { - security_ops->sb_clone_mnt_opts(oldsb, newsb); + return security_ops->sb_clone_mnt_opts(oldsb, newsb); } EXPORT_SYMBOL(security_sb_clone_mnt_opts); diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 2fa28c88900c1cb4ad7a1cc57e7b28421f4c1587..3c02be3f6732c95ff2ea5ed5438470de3de47193 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -750,7 +750,37 @@ static int selinux_set_mnt_opts(struct super_block *sb, goto out; } -static void selinux_sb_clone_mnt_opts(const struct super_block *oldsb, +static int selinux_cmp_sb_context(const struct super_block *oldsb, + const struct super_block *newsb) +{ + struct superblock_security_struct *old = oldsb->s_security; + struct superblock_security_struct *new = newsb->s_security; + char oldflags = old->flags & SE_MNTMASK; + char newflags = new->flags & SE_MNTMASK; + + if (oldflags != newflags) + goto mismatch; + if ((oldflags & FSCONTEXT_MNT) && old->sid != new->sid) + goto mismatch; + if ((oldflags & CONTEXT_MNT) && old->mntpoint_sid != new->mntpoint_sid) + goto mismatch; + if ((oldflags & DEFCONTEXT_MNT) && old->def_sid != new->def_sid) + goto mismatch; + if (oldflags & ROOTCONTEXT_MNT) { + struct inode_security_struct *oldroot = oldsb->s_root->d_inode->i_security; + struct inode_security_struct *newroot = newsb->s_root->d_inode->i_security; + if (oldroot->sid != newroot->sid) + goto mismatch; + } + return 0; +mismatch: + printk(KERN_WARNING "SELinux: mount invalid. Same superblock, " + "different security settings for (dev %s, " + "type %s)\n", newsb->s_id, newsb->s_type->name); + return -EBUSY; +} + +static int selinux_sb_clone_mnt_opts(const struct super_block *oldsb, struct super_block *newsb) { 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, * mount options. thus we can safely deal with this superblock later */ if (!ss_initialized) - return; + return 0; /* how can we clone if the old one wasn't set up?? */ BUG_ON(!(oldsbsec->flags & SE_SBINITIALIZED)); - /* if fs is reusing a sb, just let its options stand... */ + /* if fs is reusing a sb, make sure that the contexts match */ if (newsbsec->flags & SE_SBINITIALIZED) - return; + return selinux_cmp_sb_context(oldsb, newsb); mutex_lock(&newsbsec->lock); @@ -805,6 +835,7 @@ static void selinux_sb_clone_mnt_opts(const struct super_block *oldsb, sb_finish_set_opts(newsb); mutex_unlock(&newsbsec->lock); + return 0; } static int selinux_parse_opts_str(char *options,