selinux: switch to private struct selinux_mnt_opts
authorAl Viro <viro@zeniv.linux.org.uk>
Thu, 13 Dec 2018 20:04:59 +0000 (15:04 -0500)
committerAl Viro <viro@zeniv.linux.org.uk>
Fri, 21 Dec 2018 16:48:45 +0000 (11:48 -0500)
none of the convolutions needed, just 4 strings, TYVM...

Reviewed-by: David Howells <dhowells@redhat.com>
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
security/selinux/hooks.c

index caf7ca7..238907d 100644 (file)
@@ -433,16 +433,17 @@ static void superblock_free_security(struct super_block *sb)
        kfree(sbsec);
 }
 
+struct selinux_mnt_opts {
+       const char *fscontext, *context, *rootcontext, *defcontext;
+};
+
 static void selinux_free_mnt_opts(void *mnt_opts)
 {
-       struct security_mnt_opts *opts = mnt_opts;
-       int i;
-
-       if (opts->mnt_opts)
-               for (i = 0; i < opts->num_mnt_opts; i++)
-                       kfree(opts->mnt_opts[i]);
-       kfree(opts->mnt_opts);
-       kfree(opts->mnt_opts_flags);
+       struct selinux_mnt_opts *opts = mnt_opts;
+       kfree(opts->fscontext);
+       kfree(opts->context);
+       kfree(opts->rootcontext);
+       kfree(opts->defcontext);
        kfree(opts);
 }
 
@@ -624,6 +625,17 @@ static int bad_option(struct superblock_security_struct *sbsec, char flag,
        return 0;
 }
 
+static int parse_sid(struct super_block *sb, const char *s, u32 *sid)
+{
+       int rc = security_context_str_to_sid(&selinux_state, s,
+                                            sid, GFP_KERNEL);
+       if (rc)
+               pr_warn("SELinux: security_context_str_to_sid"
+                      "(%s) failed for (dev %s, type %s) errno=%d\n",
+                      s, sb->s_id, sb->s_type->name, rc);
+       return rc;
+}
+
 /*
  * Allow filesystems with binary mount data to explicitly set mount point
  * labeling information.
@@ -634,22 +646,18 @@ static int selinux_set_mnt_opts(struct super_block *sb,
                                unsigned long *set_kern_flags)
 {
        const struct cred *cred = current_cred();
-       int rc = 0, i;
        struct superblock_security_struct *sbsec = sb->s_security;
-       const char *name = sb->s_type->name;
        struct dentry *root = sbsec->sb->s_root;
+       struct selinux_mnt_opts *opts = mnt_opts;
        struct inode_security_struct *root_isec;
        u32 fscontext_sid = 0, context_sid = 0, rootcontext_sid = 0;
        u32 defcontext_sid = 0;
-       struct security_mnt_opts *opts = mnt_opts;
-       char **mount_options = opts ? opts->mnt_opts : NULL;
-       int *flags = opts ? opts->mnt_opts_flags : NULL;
-       int num_opts = opts ? opts->num_mnt_opts : 0;
+       int rc = 0;
 
        mutex_lock(&sbsec->lock);
 
        if (!selinux_state.initialized) {
-               if (!num_opts) {
+               if (!opts) {
                        /* Defer initialization until selinux_complete_init,
                           after the initial policy is loaded and the security
                           server is ready to handle calls. */
@@ -679,7 +687,7 @@ static int selinux_set_mnt_opts(struct super_block *sb,
         * will be used for both mounts)
         */
        if ((sbsec->flags & SE_SBINITIALIZED) && (sb->s_type->fs_flags & FS_BINARY_MOUNTDATA)
-           && (num_opts == 0))
+           && !opts)
                goto out;
 
        root_isec = backing_inode_security_novalidate(root);
@@ -689,68 +697,48 @@ static int selinux_set_mnt_opts(struct super_block *sb,
         * also check if someone is trying to mount the same sb more
         * than once with different security options.
         */
-       for (i = 0; i < num_opts; i++) {
-               u32 sid;
-
-               if (flags[i] == SBLABEL_MNT)
-                       continue;
-               rc = security_context_str_to_sid(&selinux_state,
-                                                mount_options[i], &sid,
-                                                GFP_KERNEL);
-               if (rc) {
-                       pr_warn("SELinux: security_context_str_to_sid"
-                              "(%s) failed for (dev %s, type %s) errno=%d\n",
-                              mount_options[i], sb->s_id, name, rc);
-                       goto out;
-               }
-               switch (flags[i]) {
-               case FSCONTEXT_MNT:
-                       fscontext_sid = sid;
-
+       if (opts) {
+               if (opts->fscontext) {
+                       rc = parse_sid(sb, opts->fscontext, &fscontext_sid);
+                       if (rc)
+                               goto out;
                        if (bad_option(sbsec, FSCONTEXT_MNT, sbsec->sid,
                                        fscontext_sid))
                                goto out_double_mount;
-
                        sbsec->flags |= FSCONTEXT_MNT;
-                       break;
-               case CONTEXT_MNT:
-                       context_sid = sid;
-
+               }
+               if (opts->context) {
+                       rc = parse_sid(sb, opts->context, &context_sid);
+                       if (rc)
+                               goto out;
                        if (bad_option(sbsec, CONTEXT_MNT, sbsec->mntpoint_sid,
                                        context_sid))
                                goto out_double_mount;
-
                        sbsec->flags |= CONTEXT_MNT;
-                       break;
-               case ROOTCONTEXT_MNT:
-                       rootcontext_sid = sid;
-
+               }
+               if (opts->rootcontext) {
+                       rc = parse_sid(sb, opts->rootcontext, &rootcontext_sid);
+                       if (rc)
+                               goto out;
                        if (bad_option(sbsec, ROOTCONTEXT_MNT, root_isec->sid,
                                        rootcontext_sid))
                                goto out_double_mount;
-
                        sbsec->flags |= ROOTCONTEXT_MNT;
-
-                       break;
-               case DEFCONTEXT_MNT:
-                       defcontext_sid = sid;
-
+               }
+               if (opts->defcontext) {
+                       rc = parse_sid(sb, opts->defcontext, &defcontext_sid);
+                       if (rc)
+                               goto out;
                        if (bad_option(sbsec, DEFCONTEXT_MNT, sbsec->def_sid,
                                        defcontext_sid))
                                goto out_double_mount;
-
                        sbsec->flags |= DEFCONTEXT_MNT;
-
-                       break;
-               default:
-                       rc = -EINVAL;
-                       goto out;
                }
        }
 
        if (sbsec->flags & SE_SBINITIALIZED) {
                /* previously mounted with options, but not on this attempt? */
-               if ((sbsec->flags & SE_MNTMASK) && !num_opts)
+               if ((sbsec->flags & SE_MNTMASK) && !opts)
                        goto out_double_mount;
                rc = 0;
                goto out;
@@ -883,7 +871,8 @@ out:
 out_double_mount:
        rc = -EINVAL;
        pr_warn("SELinux: mount invalid.  Same superblock, different "
-              "security settings for (dev %s, type %s)\n", sb->s_id, name);
+              "security settings for (dev %s, type %s)\n", sb->s_id,
+              sb->s_type->name);
        goto out;
 }
 
@@ -998,20 +987,9 @@ out:
 static int selinux_parse_opts_str(char *options,
                                  void **mnt_opts)
 {
+       struct selinux_mnt_opts *opts = *mnt_opts;
        char *p;
-       char *context = NULL, *defcontext = NULL;
-       char *fscontext = NULL, *rootcontext = NULL;
-       int rc, num_mnt_opts = 0;
-       struct security_mnt_opts *opts = *mnt_opts;
-
-       if (!opts) {
-               opts = kzalloc(sizeof(struct security_mnt_opts), GFP_KERNEL);
-               *mnt_opts = opts;
-               if (!opts)
-                       return -ENOMEM;
-       }
-
-       opts->num_mnt_opts = 0;
+       int rc;
 
        /* Standard string-based options. */
        while ((p = strsep(&options, "|")) != NULL) {
@@ -1023,54 +1001,60 @@ static int selinux_parse_opts_str(char *options,
 
                token = match_token(p, tokens, args);
 
+               if (!opts) {
+                       opts = kzalloc(sizeof(struct selinux_mnt_opts), GFP_KERNEL);
+                       if (!opts)
+                               return -ENOMEM;
+               }
+
                switch (token) {
                case Opt_context:
-                       if (context || defcontext) {
+                       if (opts->context || opts->defcontext) {
                                rc = -EINVAL;
                                pr_warn(SEL_MOUNT_FAIL_MSG);
                                goto out_err;
                        }
-                       context = match_strdup(&args[0]);
-                       if (!context) {
+                       opts->context = match_strdup(&args[0]);
+                       if (!opts->context) {
                                rc = -ENOMEM;
                                goto out_err;
                        }
                        break;
 
                case Opt_fscontext:
-                       if (fscontext) {
+                       if (opts->fscontext) {
                                rc = -EINVAL;
                                pr_warn(SEL_MOUNT_FAIL_MSG);
                                goto out_err;
                        }
-                       fscontext = match_strdup(&args[0]);
-                       if (!fscontext) {
+                       opts->fscontext = match_strdup(&args[0]);
+                       if (!opts->fscontext) {
                                rc = -ENOMEM;
                                goto out_err;
                        }
                        break;
 
                case Opt_rootcontext:
-                       if (rootcontext) {
+                       if (opts->rootcontext) {
                                rc = -EINVAL;
                                pr_warn(SEL_MOUNT_FAIL_MSG);
                                goto out_err;
                        }
-                       rootcontext = match_strdup(&args[0]);
-                       if (!rootcontext) {
+                       opts->rootcontext = match_strdup(&args[0]);
+                       if (!opts->rootcontext) {
                                rc = -ENOMEM;
                                goto out_err;
                        }
                        break;
 
                case Opt_defcontext:
-                       if (context || defcontext) {
+                       if (opts->context || opts->defcontext) {
                                rc = -EINVAL;
                                pr_warn(SEL_MOUNT_FAIL_MSG);
                                goto out_err;
                        }
-                       defcontext = match_strdup(&args[0]);
-                       if (!defcontext) {
+                       opts->defcontext = match_strdup(&args[0]);
+                       if (!opts->defcontext) {
                                rc = -ENOMEM;
                                goto out_err;
                        }
@@ -1084,43 +1068,12 @@ static int selinux_parse_opts_str(char *options,
 
                }
        }
-
-       rc = -ENOMEM;
-       opts->mnt_opts = kcalloc(NUM_SEL_MNT_OPTS, sizeof(char *), GFP_KERNEL);
-       if (!opts->mnt_opts)
-               goto out_err;
-
-       opts->mnt_opts_flags = kcalloc(NUM_SEL_MNT_OPTS, sizeof(int),
-                                      GFP_KERNEL);
-       if (!opts->mnt_opts_flags)
-               goto out_err;
-
-       if (fscontext) {
-               opts->mnt_opts[num_mnt_opts] = fscontext;
-               opts->mnt_opts_flags[num_mnt_opts++] = FSCONTEXT_MNT;
-       }
-       if (context) {
-               opts->mnt_opts[num_mnt_opts] = context;
-               opts->mnt_opts_flags[num_mnt_opts++] = CONTEXT_MNT;
-       }
-       if (rootcontext) {
-               opts->mnt_opts[num_mnt_opts] = rootcontext;
-               opts->mnt_opts_flags[num_mnt_opts++] = ROOTCONTEXT_MNT;
-       }
-       if (defcontext) {
-               opts->mnt_opts[num_mnt_opts] = defcontext;
-               opts->mnt_opts_flags[num_mnt_opts++] = DEFCONTEXT_MNT;
-       }
-
-       opts->num_mnt_opts = num_mnt_opts;
+       *mnt_opts = opts;
        return 0;
 
 out_err:
-       security_free_mnt_opts(mnt_opts);
-       kfree(context);
-       kfree(defcontext);
-       kfree(fscontext);
-       kfree(rootcontext);
+       if (opts)
+               selinux_free_mnt_opts(opts);
        return rc;
 }
 
@@ -2752,10 +2705,10 @@ static int selinux_sb_eat_lsm_opts(char *options, void **mnt_opts)
 
 static int selinux_sb_remount(struct super_block *sb, void *mnt_opts)
 {
-       struct security_mnt_opts *opts = mnt_opts;
-       int i, *flags;
-       char **mount_options;
+       struct selinux_mnt_opts *opts = mnt_opts;
        struct superblock_security_struct *sbsec = sb->s_security;
+       u32 sid;
+       int rc;
 
        if (!(sbsec->flags & SE_SBINITIALIZED))
                return 0;
@@ -2763,48 +2716,35 @@ static int selinux_sb_remount(struct super_block *sb, void *mnt_opts)
        if (!opts)
                return 0;
 
-       mount_options = opts->mnt_opts;
-       flags = opts->mnt_opts_flags;
-
-       for (i = 0; i < opts->num_mnt_opts; i++) {
-               u32 sid;
-               int rc;
-
-               if (flags[i] == SBLABEL_MNT)
-                       continue;
-               rc = security_context_str_to_sid(&selinux_state,
-                                                mount_options[i], &sid,
-                                                GFP_KERNEL);
-               if (rc) {
-                       pr_warn("SELinux: security_context_str_to_sid"
-                              "(%s) failed for (dev %s, type %s) errno=%d\n",
-                              mount_options[i], sb->s_id, sb->s_type->name, rc);
+       if (opts->fscontext) {
+               rc = parse_sid(sb, opts->fscontext, &sid);
+               if (rc)
                        return rc;
-               }
-               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 = backing_inode_security(sb->s_root);
-
-                       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:
-                       return -EINVAL;
-               }
+               if (bad_option(sbsec, FSCONTEXT_MNT, sbsec->sid, sid))
+                       goto out_bad_option;
+       }
+       if (opts->context) {
+               rc = parse_sid(sb, opts->context, &sid);
+               if (rc)
+                       return rc;
+               if (bad_option(sbsec, CONTEXT_MNT, sbsec->mntpoint_sid, sid))
+                       goto out_bad_option;
+       }
+       if (opts->rootcontext) {
+               struct inode_security_struct *root_isec;
+               root_isec = backing_inode_security(sb->s_root);
+               rc = parse_sid(sb, opts->rootcontext, &sid);
+               if (rc)
+                       return rc;
+               if (bad_option(sbsec, ROOTCONTEXT_MNT, root_isec->sid, sid))
+                       goto out_bad_option;
+       }
+       if (opts->defcontext) {
+               rc = parse_sid(sb, opts->defcontext, &sid);
+               if (rc)
+                       return rc;
+               if (bad_option(sbsec, DEFCONTEXT_MNT, sbsec->def_sid, sid))
+                       goto out_bad_option;
        }
        return 0;