diff options
-rw-r--r-- | security/selinux/hooks.c | 146 |
1 files changed, 54 insertions, 92 deletions
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 9b350070ed9e..5336d6671c5c 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -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) |