]> rtime.felk.cvut.cz Git - zynq/linux.git/blobdiff - security/smack/smack_lsm.c
Merge branch 'mount.part1' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs
[zynq/linux.git] / security / smack / smack_lsm.c
index cd720c06b78ca7efd8b1a92b7b4d9743ad50ae36..430d4f35e55c0f2a77cfd81c3f3685b8c8390500 100644 (file)
@@ -59,14 +59,31 @@ static LIST_HEAD(smk_ipv6_port_list);
 static struct kmem_cache *smack_inode_cache;
 int smack_enabled;
 
-static const match_table_t smk_mount_tokens = {
-       {Opt_fsdefault, SMK_FSDEFAULT "%s"},
-       {Opt_fsfloor, SMK_FSFLOOR "%s"},
-       {Opt_fshat, SMK_FSHAT "%s"},
-       {Opt_fsroot, SMK_FSROOT "%s"},
-       {Opt_fstransmute, SMK_FSTRANS "%s"},
-       {Opt_error, NULL},
+#define A(s) {"smack"#s, sizeof("smack"#s) - 1, Opt_##s}
+static struct {
+       const char *name;
+       int len;
+       int opt;
+} smk_mount_opts[] = {
+       A(fsdefault), A(fsfloor), A(fshat), A(fsroot), A(fstransmute)
 };
+#undef A
+
+static int match_opt_prefix(char *s, int l, char **arg)
+{
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(smk_mount_opts); i++) {
+               size_t len = smk_mount_opts[i].len;
+               if (len > l || memcmp(s, smk_mount_opts[i].name, len))
+                       continue;
+               if (len == l || s[len] != '=')
+                       continue;
+               *arg = s + len + 1;
+               return smk_mount_opts[i].opt;
+       }
+       return Opt_error;
+}
 
 #ifdef CONFIG_SECURITY_SMACK_BRINGUP
 static char *smk_bu_mess[] = {
@@ -567,175 +584,110 @@ static void smack_sb_free_security(struct super_block *sb)
        sb->s_security = NULL;
 }
 
-/**
- * smack_sb_copy_data - copy mount options data for processing
- * @orig: where to start
- * @smackopts: mount options string
- *
- * Returns 0 on success or -ENOMEM on error.
- *
- * Copy the Smack specific mount options out of the mount
- * options list.
- */
-static int smack_sb_copy_data(char *orig, char *smackopts)
-{
-       char *cp, *commap, *otheropts, *dp;
-
-       otheropts = (char *)get_zeroed_page(GFP_KERNEL);
-       if (otheropts == NULL)
-               return -ENOMEM;
+struct smack_mnt_opts {
+       const char *fsdefault, *fsfloor, *fshat, *fsroot, *fstransmute;
+};
 
-       for (cp = orig, commap = orig; commap != NULL; cp = commap + 1) {
-               if (strstr(cp, SMK_FSDEFAULT) == cp)
-                       dp = smackopts;
-               else if (strstr(cp, SMK_FSFLOOR) == cp)
-                       dp = smackopts;
-               else if (strstr(cp, SMK_FSHAT) == cp)
-                       dp = smackopts;
-               else if (strstr(cp, SMK_FSROOT) == cp)
-                       dp = smackopts;
-               else if (strstr(cp, SMK_FSTRANS) == cp)
-                       dp = smackopts;
-               else
-                       dp = otheropts;
+static void smack_free_mnt_opts(void *mnt_opts)
+{
+       struct smack_mnt_opts *opts = mnt_opts;
+       kfree(opts->fsdefault);
+       kfree(opts->fsfloor);
+       kfree(opts->fshat);
+       kfree(opts->fsroot);
+       kfree(opts->fstransmute);
+       kfree(opts);
+}
 
-               commap = strchr(cp, ',');
-               if (commap != NULL)
-                       *commap = '\0';
+static int smack_add_opt(int token, const char *s, void **mnt_opts)
+{
+       struct smack_mnt_opts *opts = *mnt_opts;
 
-               if (*dp != '\0')
-                       strcat(dp, ",");
-               strcat(dp, cp);
+       if (!opts) {
+               opts = kzalloc(sizeof(struct smack_mnt_opts), GFP_KERNEL);
+               if (!opts)
+                       return -ENOMEM;
+               *mnt_opts = opts;
        }
+       if (!s)
+               return -ENOMEM;
 
-       strcpy(orig, otheropts);
-       free_page((unsigned long)otheropts);
-
+       switch (token) {
+       case Opt_fsdefault:
+               if (opts->fsdefault)
+                       goto out_opt_err;
+               opts->fsdefault = s;
+               break;
+       case Opt_fsfloor:
+               if (opts->fsfloor)
+                       goto out_opt_err;
+               opts->fsfloor = s;
+               break;
+       case Opt_fshat:
+               if (opts->fshat)
+                       goto out_opt_err;
+               opts->fshat = s;
+               break;
+       case Opt_fsroot:
+               if (opts->fsroot)
+                       goto out_opt_err;
+               opts->fsroot = s;
+               break;
+       case Opt_fstransmute:
+               if (opts->fstransmute)
+                       goto out_opt_err;
+               opts->fstransmute = s;
+               break;
+       }
        return 0;
+
+out_opt_err:
+       pr_warn("Smack: duplicate mount options\n");
+       return -EINVAL;
 }
 
-/**
- * smack_parse_opts_str - parse Smack specific mount options
- * @options: mount options string
- * @opts: where to store converted mount opts
- *
- * Returns 0 on success or -ENOMEM on error.
- *
- * converts Smack specific mount options to generic security option format
- */
-static int smack_parse_opts_str(char *options,
-               struct security_mnt_opts *opts)
+static int smack_sb_eat_lsm_opts(char *options, void **mnt_opts)
 {
-       char *p;
-       char *fsdefault = NULL;
-       char *fsfloor = NULL;
-       char *fshat = NULL;
-       char *fsroot = NULL;
-       char *fstransmute = NULL;
-       int rc = -ENOMEM;
-       int num_mnt_opts = 0;
-       int token;
-
-       opts->num_mnt_opts = 0;
-
-       if (!options)
-               return 0;
-
-       while ((p = strsep(&options, ",")) != NULL) {
-               substring_t args[MAX_OPT_ARGS];
+       char *from = options, *to = options;
+       bool first = true;
 
-               if (!*p)
-                       continue;
-
-               token = match_token(p, smk_mount_tokens, args);
+       while (1) {
+               char *next = strchr(from, ',');
+               int token, len, rc;
+               char *arg = NULL;
 
-               switch (token) {
-               case Opt_fsdefault:
-                       if (fsdefault)
-                               goto out_opt_err;
-                       fsdefault = match_strdup(&args[0]);
-                       if (!fsdefault)
-                               goto out_err;
-                       break;
-               case Opt_fsfloor:
-                       if (fsfloor)
-                               goto out_opt_err;
-                       fsfloor = match_strdup(&args[0]);
-                       if (!fsfloor)
-                               goto out_err;
-                       break;
-               case Opt_fshat:
-                       if (fshat)
-                               goto out_opt_err;
-                       fshat = match_strdup(&args[0]);
-                       if (!fshat)
-                               goto out_err;
-                       break;
-               case Opt_fsroot:
-                       if (fsroot)
-                               goto out_opt_err;
-                       fsroot = match_strdup(&args[0]);
-                       if (!fsroot)
-                               goto out_err;
-                       break;
-               case Opt_fstransmute:
-                       if (fstransmute)
-                               goto out_opt_err;
-                       fstransmute = match_strdup(&args[0]);
-                       if (!fstransmute)
-                               goto out_err;
-                       break;
-               default:
-                       rc = -EINVAL;
-                       pr_warn("Smack:  unknown mount option\n");
-                       goto out_err;
+               if (next)
+                       len = next - from;
+               else
+                       len = strlen(from);
+
+               token = match_opt_prefix(from, len, &arg);
+               if (token != Opt_error) {
+                       arg = kmemdup_nul(arg, from + len - arg, GFP_KERNEL);
+                       rc = smack_add_opt(token, arg, mnt_opts);
+                       if (unlikely(rc)) {
+                               kfree(arg);
+                               if (*mnt_opts)
+                                       smack_free_mnt_opts(*mnt_opts);
+                               *mnt_opts = NULL;
+                               return rc;
+                       }
+               } else {
+                       if (!first) {   // copy with preceding comma
+                               from--;
+                               len++;
+                       }
+                       if (to != from)
+                               memmove(to, from, len);
+                       to += len;
+                       first = false;
                }
+               if (!from[len])
+                       break;
+               from += len + 1;
        }
-
-       opts->mnt_opts = kcalloc(NUM_SMK_MNT_OPTS, sizeof(char *), GFP_KERNEL);
-       if (!opts->mnt_opts)
-               goto out_err;
-
-       opts->mnt_opts_flags = kcalloc(NUM_SMK_MNT_OPTS, sizeof(int),
-                       GFP_KERNEL);
-       if (!opts->mnt_opts_flags)
-               goto out_err;
-
-       if (fsdefault) {
-               opts->mnt_opts[num_mnt_opts] = fsdefault;
-               opts->mnt_opts_flags[num_mnt_opts++] = FSDEFAULT_MNT;
-       }
-       if (fsfloor) {
-               opts->mnt_opts[num_mnt_opts] = fsfloor;
-               opts->mnt_opts_flags[num_mnt_opts++] = FSFLOOR_MNT;
-       }
-       if (fshat) {
-               opts->mnt_opts[num_mnt_opts] = fshat;
-               opts->mnt_opts_flags[num_mnt_opts++] = FSHAT_MNT;
-       }
-       if (fsroot) {
-               opts->mnt_opts[num_mnt_opts] = fsroot;
-               opts->mnt_opts_flags[num_mnt_opts++] = FSROOT_MNT;
-       }
-       if (fstransmute) {
-               opts->mnt_opts[num_mnt_opts] = fstransmute;
-               opts->mnt_opts_flags[num_mnt_opts++] = FSTRANS_MNT;
-       }
-
-       opts->num_mnt_opts = num_mnt_opts;
+       *to = '\0';
        return 0;
-
-out_opt_err:
-       rc = -EINVAL;
-       pr_warn("Smack: duplicate mount options\n");
-
-out_err:
-       kfree(fsdefault);
-       kfree(fsfloor);
-       kfree(fshat);
-       kfree(fsroot);
-       kfree(fstransmute);
-       return rc;
 }
 
 /**
@@ -751,7 +703,7 @@ out_err:
  * labels.
  */
 static int smack_set_mnt_opts(struct super_block *sb,
-               struct security_mnt_opts *opts,
+               void *mnt_opts,
                unsigned long kern_flags,
                unsigned long *set_kern_flags)
 {
@@ -760,9 +712,8 @@ static int smack_set_mnt_opts(struct super_block *sb,
        struct superblock_smack *sp = sb->s_security;
        struct inode_smack *isp;
        struct smack_known *skp;
-       int i;
-       int num_opts = opts->num_mnt_opts;
-       int transmute = 0;
+       struct smack_mnt_opts *opts = mnt_opts;
+       bool transmute = false;
 
        if (sp->smk_flags & SMK_SB_INITIALIZED)
                return 0;
@@ -771,7 +722,7 @@ static int smack_set_mnt_opts(struct super_block *sb,
                /*
                 * Unprivileged mounts don't get to specify Smack values.
                 */
-               if (num_opts)
+               if (opts)
                        return -EPERM;
                /*
                 * Unprivileged mounts get root and default from the caller.
@@ -787,48 +738,44 @@ static int smack_set_mnt_opts(struct super_block *sb,
                if (sb->s_user_ns != &init_user_ns &&
                    sb->s_magic != SYSFS_MAGIC && sb->s_magic != TMPFS_MAGIC &&
                    sb->s_magic != RAMFS_MAGIC) {
-                       transmute = 1;
+                       transmute = true;
                        sp->smk_flags |= SMK_SB_UNTRUSTED;
                }
        }
 
        sp->smk_flags |= SMK_SB_INITIALIZED;
 
-       for (i = 0; i < num_opts; i++) {
-               switch (opts->mnt_opts_flags[i]) {
-               case FSDEFAULT_MNT:
-                       skp = smk_import_entry(opts->mnt_opts[i], 0);
+       if (opts) {
+               if (opts->fsdefault) {
+                       skp = smk_import_entry(opts->fsdefault, 0);
                        if (IS_ERR(skp))
                                return PTR_ERR(skp);
                        sp->smk_default = skp;
-                       break;
-               case FSFLOOR_MNT:
-                       skp = smk_import_entry(opts->mnt_opts[i], 0);
+               }
+               if (opts->fsfloor) {
+                       skp = smk_import_entry(opts->fsfloor, 0);
                        if (IS_ERR(skp))
                                return PTR_ERR(skp);
                        sp->smk_floor = skp;
-                       break;
-               case FSHAT_MNT:
-                       skp = smk_import_entry(opts->mnt_opts[i], 0);
+               }
+               if (opts->fshat) {
+                       skp = smk_import_entry(opts->fshat, 0);
                        if (IS_ERR(skp))
                                return PTR_ERR(skp);
                        sp->smk_hat = skp;
-                       break;
-               case FSROOT_MNT:
-                       skp = smk_import_entry(opts->mnt_opts[i], 0);
+               }
+               if (opts->fsroot) {
+                       skp = smk_import_entry(opts->fsroot, 0);
                        if (IS_ERR(skp))
                                return PTR_ERR(skp);
                        sp->smk_root = skp;
-                       break;
-               case FSTRANS_MNT:
-                       skp = smk_import_entry(opts->mnt_opts[i], 0);
+               }
+               if (opts->fstransmute) {
+                       skp = smk_import_entry(opts->fstransmute, 0);
                        if (IS_ERR(skp))
                                return PTR_ERR(skp);
                        sp->smk_root = skp;
-                       transmute = 1;
-                       break;
-               default:
-                       break;
+                       transmute = true;
                }
        }
 
@@ -850,37 +797,6 @@ static int smack_set_mnt_opts(struct super_block *sb,
        return 0;
 }
 
-/**
- * smack_sb_kern_mount - Smack specific mount processing
- * @sb: the file system superblock
- * @flags: the mount flags
- * @data: the smack mount options
- *
- * Returns 0 on success, an error code on failure
- */
-static int smack_sb_kern_mount(struct super_block *sb, int flags, void *data)
-{
-       int rc = 0;
-       char *options = data;
-       struct security_mnt_opts opts;
-
-       security_init_mnt_opts(&opts);
-
-       if (!options)
-               goto out;
-
-       rc = smack_parse_opts_str(options, &opts);
-       if (rc)
-               goto out_err;
-
-out:
-       rc = smack_set_mnt_opts(sb, &opts, 0, NULL);
-
-out_err:
-       security_free_mnt_opts(&opts);
-       return rc;
-}
-
 /**
  * smack_sb_statfs - Smack check on statfs
  * @dentry: identifies the file system in question
@@ -4673,11 +4589,10 @@ static struct security_hook_list smack_hooks[] __lsm_ro_after_init = {
 
        LSM_HOOK_INIT(sb_alloc_security, smack_sb_alloc_security),
        LSM_HOOK_INIT(sb_free_security, smack_sb_free_security),
-       LSM_HOOK_INIT(sb_copy_data, smack_sb_copy_data),
-       LSM_HOOK_INIT(sb_kern_mount, smack_sb_kern_mount),
+       LSM_HOOK_INIT(sb_free_mnt_opts, smack_free_mnt_opts),
+       LSM_HOOK_INIT(sb_eat_lsm_opts, smack_sb_eat_lsm_opts),
        LSM_HOOK_INIT(sb_statfs, smack_sb_statfs),
        LSM_HOOK_INIT(sb_set_mnt_opts, smack_set_mnt_opts),
-       LSM_HOOK_INIT(sb_parse_opts_str, smack_parse_opts_str),
 
        LSM_HOOK_INIT(bprm_set_creds, smack_bprm_set_creds),