summary refs log tree commit diff
path: root/security
diff options
context:
space:
mode:
authorAl Viro <viro@zeniv.linux.org.uk>2018-12-14 22:44:50 -0500
committerAl Viro <viro@zeniv.linux.org.uk>2018-12-21 11:49:28 -0500
commit169d68efb03b728588c209c682f14328eec485c0 (patch)
tree9d0f9587ee8d2863ecb558be1de2342f9aa6ee92 /security
parentba6418623385abf19a6c15cf0b1cfaacfdf9afc8 (diff)
downloadlinux-169d68efb03b728588c209c682f14328eec485c0.tar.gz
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;
 		}
 	}