SELinux: implement the new sb_remount LSM hook
Eric Paris [Thu, 3 Mar 2011 21:09:14 +0000 (16:09 -0500)]
For SELinux we do not allow security information to change during a remount
operation.  Thus this hook simply strips the security module options from
the data and verifies that those are the same options as exist on the
current superblock.

Signed-off-by: Eric Paris <eparis@redhat.com>
Reviewed-by: James Morris <jmorris@namei.org>

security/selinux/hooks.c

index 3decf07..f2f3cf2 100644 (file)
@@ -2363,6 +2363,91 @@ out:
        return rc;
 }
 
+static int selinux_sb_remount(struct super_block *sb, void *data)
+{
+       int rc, i, *flags;
+       struct security_mnt_opts opts;
+       char *secdata, **mount_options;
+       struct superblock_security_struct *sbsec = sb->s_security;
+
+       if (!(sbsec->flags & SE_SBINITIALIZED))
+               return 0;
+
+       if (!data)
+               return 0;
+
+       if (sb->s_type->fs_flags & FS_BINARY_MOUNTDATA)
+               return 0;
+
+       security_init_mnt_opts(&opts);
+       secdata = alloc_secdata();
+       if (!secdata)
+               return -ENOMEM;
+       rc = selinux_sb_copy_data(data, secdata);
+       if (rc)
+               goto out_free_secdata;
+
+       rc = selinux_parse_opts_str(secdata, &opts);
+       if (rc)
+               goto out_free_secdata;
+
+       mount_options = opts.mnt_opts;
+       flags = opts.mnt_opts_flags;
+
+       for (i = 0; i < opts.num_mnt_opts; i++) {
+               u32 sid;
+               size_t len;
+
+               if (flags[i] == SE_SBLABELSUPP)
+                       continue;
+               len = strlen(mount_options[i]);
+               rc = security_context_to_sid(mount_options[i], len, &sid);
+               if (rc) {
+                       printk(KERN_WARNING "SELinux: security_context_to_sid"
+                              "(%s) failed for (dev %s, type %s) errno=%d\n",
+                              mount_options[i], sb->s_id, sb->s_type->name, rc);
+                       goto out_free_opts;
+               }
+               rc = -EINVAL;
+               switch (flags[i]) {
+               case FSCONTEXT_MNT:
+                       if (bad_option(sbsec, FSCONTEXT_MNT, sbsec->sid, sid))
+                               goto out_bad_option;
+                       break;
+               case CONTEXT_MNT:
+                       if (bad_option(sbsec, CONTEXT_MNT, sbsec->mntpoint_sid, sid))
+                               goto out_bad_option;
+                       break;
+               case ROOTCONTEXT_MNT: {
+                       struct inode_security_struct *root_isec;
+                       root_isec = sb->s_root->d_inode->i_security;
+
+                       if (bad_option(sbsec, ROOTCONTEXT_MNT, root_isec->sid, sid))
+                               goto out_bad_option;
+                       break;
+               }
+               case DEFCONTEXT_MNT:
+                       if (bad_option(sbsec, DEFCONTEXT_MNT, sbsec->def_sid, sid))
+                               goto out_bad_option;
+                       break;
+               default:
+                       goto out_free_opts;
+               }
+       }
+
+       rc = 0;
+out_free_opts:
+       security_free_mnt_opts(&opts);
+out_free_secdata:
+       free_secdata(secdata);
+       return rc;
+out_bad_option:
+       printk(KERN_WARNING "SELinux: unable to change security options "
+              "during remount (dev %s, type=%s)\n", sb->s_id,
+              sb->s_type->name);
+       goto out_free_opts;
+}
+
 static int selinux_sb_kern_mount(struct super_block *sb, int flags, void *data)
 {
        const struct cred *cred = current_cred();
@@ -5356,6 +5441,7 @@ static struct security_operations selinux_ops = {
        .sb_alloc_security =            selinux_sb_alloc_security,
        .sb_free_security =             selinux_sb_free_security,
        .sb_copy_data =                 selinux_sb_copy_data,
+       .sb_remount =                   selinux_sb_remount,
        .sb_kern_mount =                selinux_sb_kern_mount,
        .sb_show_options =              selinux_sb_show_options,
        .sb_statfs =                    selinux_sb_statfs,