summaryrefslogtreecommitdiffstats
path: root/security
diff options
context:
space:
mode:
authorAl Viro <viro@zeniv.linux.org.uk>2018-12-15 04:44:50 +0100
committerAl Viro <viro@zeniv.linux.org.uk>2018-12-21 17:49:28 +0100
commit169d68efb03b728588c209c682f14328eec485c0 (patch)
tree9d0f9587ee8d2863ecb558be1de2342f9aa6ee92 /security
parentselinux: new helper - selinux_add_opt() (diff)
downloadlinux-169d68efb03b728588c209c682f14328eec485c0.tar.xz
linux-169d68efb03b728588c209c682f14328eec485c0.zip
selinux: switch away from match_token()
It's not a good fit, unfortunately, and the next step will make it even less so. Open-code what we need here. Reviewed-by: David Howells <dhowells@redhat.com> Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Diffstat (limited to 'security')
-rw-r--r--security/selinux/hooks.c82
1 files changed, 55 insertions, 27 deletions
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index 26ec7d67e15d..8f2285cb9029 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -459,19 +459,41 @@ enum {
Opt_defcontext = 3,
Opt_rootcontext = 4,
Opt_labelsupport = 5,
- Opt_nextmntopt = 6,
};
-#define NUM_SEL_MNT_OPTS (Opt_nextmntopt - 1)
-
-static const match_table_t tokens = {
- {Opt_context, CONTEXT_STR "%s"},
- {Opt_fscontext, FSCONTEXT_STR "%s"},
- {Opt_defcontext, DEFCONTEXT_STR "%s"},
- {Opt_rootcontext, ROOTCONTEXT_STR "%s"},
- {Opt_labelsupport, LABELSUPP_STR},
- {Opt_error, NULL},
+#define A(s, opt, has_arg) {s, sizeof(s) - 1, opt, has_arg}
+static struct {
+ const char *name;
+ int len;
+ int opt;
+ bool has_arg;
+} tokens[] = {
+ A("context", Opt_context, true),
+ A("fscontext", Opt_fscontext, true),
+ A("defcontext", Opt_defcontext, true),
+ A("rootcontext", Opt_rootcontext, true),
+ A("seclabel", Opt_labelsupport, false),
};
+#undef A
+
+static int match_opt_prefix(char *s, int l, char **arg)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(tokens); i++) {
+ size_t len = tokens[i].len;
+ if (len > l || memcmp(s, tokens[i].name, len))
+ continue;
+ if (tokens[i].has_arg) {
+ if (len == l || s[len] != '=')
+ continue;
+ *arg = s + len + 1;
+ } else if (len != l)
+ continue;
+ return tokens[i].opt;
+ }
+ return Opt_error;
+}
#define SEL_MOUNT_FAIL_MSG "SELinux: duplicate or incompatible mount options\n"
@@ -988,6 +1010,9 @@ static int selinux_add_opt(int token, const char *s, void **mnt_opts)
{
struct selinux_mnt_opts *opts = *mnt_opts;
+ if (token == Opt_labelsupport) /* eaten and completely ignored */
+ return 0;
+
if (!opts) {
opts = kzalloc(sizeof(struct selinux_mnt_opts), GFP_KERNEL);
if (!opts)
@@ -1021,36 +1046,39 @@ static int selinux_add_opt(int token, const char *s, void **mnt_opts)
return 0;
Einval:
pr_warn(SEL_MOUNT_FAIL_MSG);
- kfree(s);
return -EINVAL;
}
static int selinux_parse_opts_str(char *options,
void **mnt_opts)
{
- char *p;
+ char *p = options, *next;
+ int rc;
/* Standard string-based options. */
- while ((p = strsep(&options, "|")) != NULL) {
- int token, rc;
- substring_t args[MAX_OPT_ARGS];
- const char *arg;
+ for (p = options; *p; p = next) {
+ int token, len;
+ char *arg = NULL;
- if (!*p)
- continue;
-
- token = match_token(p, tokens, args);
+ next = strchr(p, '|');
+ if (next) {
+ len = next++ - p;
+ } else {
+ len = strlen(p);
+ next = p + len;
+ }
- if (token == Opt_labelsupport) /* eaten and completely ignored */
+ if (!len)
continue;
- arg = match_strdup(&args[0]);
+
+ token = match_opt_prefix(p, len, &arg);
+ if (arg)
+ arg = kmemdup_nul(arg, p + len - arg, GFP_KERNEL);
rc = selinux_add_opt(token, arg, mnt_opts);
- if (unlikely(rc)) {
+ if (rc) {
kfree(arg);
- if (*mnt_opts) {
- selinux_free_mnt_opts(*mnt_opts);
- *mnt_opts = NULL;
- }
+ selinux_free_mnt_opts(*mnt_opts);
+ *mnt_opts = NULL;
return rc;
}
}