diff --git a/sys/security/mac_do/mac_do.c b/sys/security/mac_do/mac_do.c
--- a/sys/security/mac_do/mac_do.c
+++ b/sys/security/mac_do/mac_do.c
@@ -368,6 +368,48 @@
 	return ((int)l);
 }
 
+/*
+ * strsep() variant skipping spaces and tabs.
+ *
+ * Skips spaces and tabs at beginning and end of the token before one of the
+ * 'delim' characters, i.e., at start of string and just before one of the
+ * delimiter characters (so it doesn't prevent tokens containing spaces and tabs
+ * in the middle).
+ */
+static char *
+strsep_noblanks(char **const stringp, const char *delim)
+{
+	char *p = *stringp;
+	char *ret, *wsp;
+	size_t idx;
+
+	if (p == NULL)
+		return (NULL);
+
+	idx = strspn(p, " \t");
+	p += idx;
+
+	ret = strsep(&p, delim);
+
+	/* Rewind spaces/tabs at the end. */
+	if (p == NULL)
+		wsp = ret + strlen(ret);
+	else
+		wsp = p - 1;
+	for (; wsp != ret; --wsp) {
+		switch (wsp[-1]) {
+		case ' ':
+		case '\t':
+			continue;
+		}
+		break;
+	}
+	*wsp = '\0';
+
+	*stringp = p;
+	return (ret);
+}
+
 
 static void
 alloc_parse_error(struct parse_error **const parse_error, const size_t pos,
@@ -448,13 +490,13 @@
 
 	MPASS(*parse_error == NULL);
 	MPASS(to != NULL);
-	to_type = strsep(&to, "=");
+	to_type = strsep_noblanks(&to, "=");
 	MPASS(to_type != NULL);
 	error = parse_id_type(to_type, &type, parse_error);
 	if (error != 0)
 		goto einval;
 
-	to_id = strsep(&to, "");
+	to_id = strsep_noblanks(&to, "");
 	switch (type) {
 	case IT_GID:
 		if (to_id == NULL) {
@@ -784,7 +826,7 @@
 	/* Freed when the 'struct rules' container is freed. */
 	new = malloc(sizeof(*new), M_DO, M_WAITOK | M_ZERO);
 
-	from_type = strsep(&rule, "=");
+	from_type = strsep_noblanks(&rule, "=");
 	MPASS(from_type != NULL); /* Because 'rule' was not NULL. */
 	error = parse_id_type(from_type, &new->from_type, parse_error);
 	if (error != 0)
@@ -799,7 +841,7 @@
 		goto einval;
 	}
 
-	from_id = strsep(&rule, ":");
+	from_id = strsep_noblanks(&rule, ":");
 	if (is_null_or_empty(from_id)) {
 		alloc_parse_error(parse_error, 0, "No ID specified.");
 		goto einval;
@@ -824,7 +866,7 @@
 	 * allows to minimize memory allocations and enables searching IDs in
 	 * O(log(n)) instead of linearly.
 	 */
-	to_list = strsep(&rule, ",");
+	to_list = strsep_noblanks(&rule, ",");
 	if (to_list == NULL) {
 		alloc_parse_error(parse_error, 0, "No target list.");
 		goto einval;
@@ -837,7 +879,7 @@
 			goto einval;
 		}
 
-		to_list = strsep(&rule, ",");
+		to_list = strsep_noblanks(&rule, ",");
 	} while (to_list != NULL);
 
 	if (new->uids_nb != 0) {
@@ -904,7 +946,10 @@
  * is "uid" or "gid", <id> an UID or GID (depending on <type>) and <target> is
  * "*", "any" or a comma-separated list of '<type>=<flags><id>' clauses (see the
  * comment for parse_single_rule() for more details).  For convenience, empty
- * rules are allowed (and do nothing).
+ * rules are allowed (and do nothing), and spaces and tabs are allowed (and
+ * removed) around each token (tokens are natural ones, except that
+ * '<flags><id>' as a whole is considered a single token, so no blanks are
+ * allowed between '<flags>' and '<id>').
  *
  * Examples:
  * - "uid=1001:uid=1010,gid=1010;uid=1002:any"
@@ -937,7 +982,7 @@
 	MPASS(copy[len] == '\0'); /* Catch some races. */
 
 	p = copy;
-	while ((rule = strsep(&p, ";")) != NULL) {
+	while ((rule = strsep_noblanks(&p, ";")) != NULL) {
 		if (rule[0] == '\0')
 			continue;
 		error = parse_single_rule(rule, rules, parse_error);