selinux: rewrite selinux_sb_eat_lsm_opts()
authorAl Viro <viro@zeniv.linux.org.uk>
Sat, 15 Dec 2018 02:56:23 +0000 (21:56 -0500)
committerAl Viro <viro@zeniv.linux.org.uk>
Fri, 21 Dec 2018 16:49:54 +0000 (11:49 -0500)
make it use selinux_add_opt() and avoid separate copies - gather
non-LSM options by memmove() in place

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

index 9b35007..5336d66 100644 (file)
@@ -2606,109 +2606,71 @@ static void selinux_sb_free_security(struct super_block *sb)
        superblock_free_security(sb);
 }
 
-static inline int match_prefix(char *prefix, int plen, char *option, int olen)
+static inline int opt_len(const char *s)
 {
-       if (plen > olen)
-               return 0;
-
-       return !memcmp(prefix, option, plen);
-}
-
-static inline int selinux_option(char *option, int len)
-{
-       return (match_prefix(CONTEXT_STR, sizeof(CONTEXT_STR)-1, option, len) ||
-               match_prefix(FSCONTEXT_STR, sizeof(FSCONTEXT_STR)-1, option, len) ||
-               match_prefix(DEFCONTEXT_STR, sizeof(DEFCONTEXT_STR)-1, option, len) ||
-               match_prefix(ROOTCONTEXT_STR, sizeof(ROOTCONTEXT_STR)-1, option, len) ||
-               match_prefix(LABELSUPP_STR, sizeof(LABELSUPP_STR)-1, option, len));
-}
-
-static inline void take_option(char **to, char *from, int *first, int len)
-{
-       if (!*first) {
-               **to = ',';
-               *to += 1;
-       } else
-               *first = 0;
-       memcpy(*to, from, len);
-       *to += len;
-}
-
-static inline void take_selinux_option(char **to, char *from, int *first,
-                                      int len)
-{
-       int current_size = 0;
-
-       if (!*first) {
-               **to = '|';
-               *to += 1;
-       } else
-               *first = 0;
+       bool open_quote = false;
+       int len;
+       char c;
 
-       while (current_size < len) {
-               if (*from != '"') {
-                       **to = *from;
-                       *to += 1;
-               }
-               from += 1;
-               current_size += 1;
+       for (len = 0; (c = s[len]) != '\0'; len++) {
+               if (c == '"')
+                       open_quote = !open_quote;
+               if (c == ',' && !open_quote)
+                       break;
        }
+       return len;
 }
 
-static int selinux_sb_copy_data(char *orig, char *copy)
+static int selinux_sb_eat_lsm_opts(char *options, void **mnt_opts)
 {
-       int fnosec, fsec, rc = 0;
-       char *in_save, *in_curr, *in_end;
-       char *sec_curr, *nosec_save, *nosec;
-       int open_quote = 0;
+       char *from = options;
+       char *to = options;
+       bool first = true;
 
-       in_curr = orig;
-       sec_curr = copy;
+       while (1) {
+               int len = opt_len(from);
+               int token, rc;
+               char *arg = NULL;
 
-       nosec = (char *)get_zeroed_page(GFP_KERNEL);
-       if (!nosec) {
-               rc = -ENOMEM;
-               goto out;
-       }
+               token = match_opt_prefix(from, len, &arg);
 
-       nosec_save = nosec;
-       fnosec = fsec = 1;
-       in_save = in_end = orig;
+               if (token != Opt_error) {
+                       char *p, *q;
 
-       do {
-               if (*in_end == '"')
-                       open_quote = !open_quote;
-               if ((*in_end == ',' && open_quote == 0) ||
-                               *in_end == '\0') {
-                       int len = in_end - in_curr;
-
-                       if (selinux_option(in_curr, len))
-                               take_selinux_option(&sec_curr, in_curr, &fsec, len);
-                       else
-                               take_option(&nosec, in_curr, &fnosec, len);
-
-                       in_curr = in_end + 1;
+                       /* strip quotes */
+                       if (arg) {
+                               for (p = q = arg; p < from + len; p++) {
+                                       char c = *p;
+                                       if (c != '"')
+                                               *q++ = c;
+                               }
+                               arg = kmemdup_nul(arg, q - arg, GFP_KERNEL);
+                       }
+                       rc = selinux_add_opt(token, arg, mnt_opts);
+                       if (unlikely(rc)) {
+                               kfree(arg);
+                               if (*mnt_opts) {
+                                       selinux_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;
                }
-       } while (*in_end++);
-
-       strcpy(in_save, nosec_save);
-       free_page((unsigned long)nosec_save);
-out:
-       return rc;
-}
-
-static int selinux_sb_eat_lsm_opts(char *options, void **mnt_opts)
-{
-       char *s = (char *)get_zeroed_page(GFP_KERNEL);
-       int err;
-
-       if (!s)
-               return -ENOMEM;
-       err = selinux_sb_copy_data(options, s);
-       if (!err)
-               err = selinux_parse_opts_str(s, mnt_opts);
-       free_page((unsigned long)s);
-       return err;
+               if (!from[len])
+                       break;
+               from += len + 1;
+       }
+       *to = '\0';
+       return 0;
 }
 
 static int selinux_sb_remount(struct super_block *sb, void *mnt_opts)