xref: /freebsd/sys/security/mac_do/mac_do.c (revision 87c06b7d026f2beeb3c2f695567ef72aa3a427ea)
1 /*-
2  * Copyright(c) 2024 Baptiste Daroussin <bapt@FreeBSD.org>
3  *
4  * SPDX-License-Identifier: BSD-2-Clause
5  */
6 
7 #include <sys/param.h>
8 #include <sys/systm.h>
9 #include <sys/ctype.h>
10 #include <sys/jail.h>
11 #include <sys/kernel.h>
12 #include <sys/limits.h>
13 #include <sys/lock.h>
14 #include <sys/malloc.h>
15 #include <sys/module.h>
16 #include <sys/mount.h>
17 #include <sys/mutex.h>
18 #include <sys/priv.h>
19 #include <sys/proc.h>
20 #include <sys/socket.h>
21 #include <sys/sx.h>
22 #include <sys/sysctl.h>
23 #include <sys/ucred.h>
24 #include <sys/vnode.h>
25 
26 #include <machine/stdarg.h>
27 
28 #include <security/mac/mac_policy.h>
29 
30 static SYSCTL_NODE(_security_mac, OID_AUTO, do,
31     CTLFLAG_RW|CTLFLAG_MPSAFE, 0, "mac_do policy controls");
32 
33 static int	do_enabled = 1;
34 SYSCTL_INT(_security_mac_do, OID_AUTO, enabled, CTLFLAG_RWTUN,
35     &do_enabled, 0, "Enforce do policy");
36 
37 static int	print_parse_error = 1;
38 SYSCTL_INT(_security_mac_do, OID_AUTO, print_parse_error, CTLFLAG_RWTUN,
39     &print_parse_error, 0, "Print parse errors on setting rules "
40     "(via sysctl(8)).");
41 
42 static MALLOC_DEFINE(M_DO, "do_rule", "Rules for mac_do");
43 
44 #define MAC_RULE_STRING_LEN	1024
45 
46 static unsigned		osd_jail_slot;
47 
48 #define IT_INVALID	0 /* Must stay 0. */
49 #define IT_UID		1
50 #define IT_GID		2
51 #define IT_ANY		3
52 #define IT_LAST		IT_ANY
53 
54 static const char *id_type_to_str[] = {
55 	[IT_INVALID]	= "invalid",
56 	[IT_UID]	= "uid",
57 	[IT_GID]	= "gid",
58 	/* See also parse_id_type(). */
59 	[IT_ANY]	= "*",
60 };
61 
62 #define PARSE_ERROR_SIZE	256
63 
64 struct parse_error {
65 	size_t	pos;
66 	char	msg[PARSE_ERROR_SIZE];
67 };
68 
69 /*
70  * We assume that 'uid_t' and 'gid_t' are aliases to 'u_int' in conversions
71  * required for parsing rules specification strings.
72  */
73 _Static_assert(sizeof(uid_t) == sizeof(u_int) && (uid_t)-1 >= 0 &&
74     sizeof(gid_t) == sizeof(u_int) && (gid_t)-1 >= 0,
75     "mac_do(4) assumes that 'uid_t' and 'gid_t' are aliases to 'u_int'");
76 
77 /*
78  * Internal flags.
79  *
80  * They either apply as per-type (t) or per-ID (i) but are conflated because all
81  * per-ID flags are also valid as per-type ones to qualify the "current" (".")
82  * per-type flag.  Also, some of them are in fact exclusive, but we use one-hot
83  * encoding for simplicity.
84  *
85  * There is currently room for "only" 16 bits.  As these flags are purely
86  * internal, they can be renumbered and/or their type changed as needed.
87  *
88  * See also the check_*() functions below.
89  */
90 typedef uint16_t	flags_t;
91 
92 /* (i,gid) Specification concerns primary groups. */
93 #define MDF_PRIMARY	(1u << 0)
94 /* (i,gid) Specification concerns supplementary groups. */
95 #define MDF_SUPP_ALLOW	(1u << 1)
96 /* (i,gid) Group must appear as a supplementary group. */
97 #define MDF_SUPP_MUST	(1u << 2)
98 /* (i,gid) Group must not appear as a supplementary group. */
99 #define MDF_SUPP_DONT	(1u << 3)
100 #define MDF_SUPP_MASK	(MDF_SUPP_ALLOW | MDF_SUPP_MUST | MDF_SUPP_DONT)
101 #define MDF_ID_MASK	(MDF_PRIMARY | MDF_SUPP_MASK)
102 
103 /*
104  * (t) All IDs allowed.
105  *
106  * For GIDs, MDF_ANY only concerns primary groups.  The MDF_PRIMARY and
107  * MDF_SUPP_* flags never apply to MDF_ANY, but can be present if MDF_CURRENT is
108  * present also, as usual.
109  */
110 #define MDF_ANY			(1u << 8)
111 /* (t) Current IDs allowed. */
112 #define MDF_CURRENT		(1u << 9)
113 #define MDF_TYPE_COMMON_MASK	(MDF_ANY | MDF_CURRENT)
114 /* (t,gid) All IDs allowed as supplementary groups. */
115 #define MDF_ANY_SUPP		(1u << 10)
116 /* (t,gid) Some ID or MDF_CURRENT has MDF_SUPP_MUST or MDF_SUPP_DONT. */
117 #define MDF_MAY_REJ_SUPP	(1u << 11)
118 /* (t,gid) Some explicit ID (not MDF_CURRENT) has MDF_SUPP_MUST. */
119 #define MDF_EXPLICIT_SUPP_MUST	(1u << 12)
120 /* (t,gid) Whether any target clause is about primary groups.  Used during
121  * parsing only. */
122 #define MDF_HAS_PRIMARY_CLAUSE	(1u << 13)
123 /* (t,gid) Whether any target clause is about supplementary groups.  Used during
124  * parsing only. */
125 #define MDF_HAS_SUPP_CLAUSE	(1u << 14)
126 #define MDF_TYPE_GID_MASK	(MDF_ANY_SUPP | MDF_MAY_REJ_SUPP |	\
127     MDF_EXPLICIT_SUPP_MUST | MDF_HAS_PRIMARY_CLAUSE | MDF_HAS_SUPP_CLAUSE)
128 #define MDF_TYPE_MASK		(MDF_TYPE_COMMON_MASK | MDF_TYPE_GID_MASK)
129 
130 /*
131  * Persistent structures.
132  */
133 
134 struct id_spec {
135 	u_int		 id;
136 	flags_t		 flags; /* See MDF_* above. */
137 };
138 
139 /*
140  * This limits the number of target clauses per type to 65535.  With the current
141  * value of MAC_RULE_STRING_LEN (1024), this is way more than enough anyway.
142  */
143 typedef uint16_t	 id_nb_t;
144 /* We only have a few IT_* types. */
145 typedef uint16_t	 id_type_t;
146 
147 struct rule {
148 	TAILQ_ENTRY(rule) r_entries;
149 	id_type_t	 from_type;
150 	u_int		 from_id;
151 	flags_t		 uid_flags; /* See MDF_* above. */
152 	id_nb_t		 uids_nb;
153 	flags_t		 gid_flags; /* See MDF_* above. */
154 	id_nb_t		 gids_nb;
155 	struct id_spec	*uids;
156 	struct id_spec	*gids;
157 };
158 
159 TAILQ_HEAD(rulehead, rule);
160 
161 struct rules {
162 	char string[MAC_RULE_STRING_LEN];
163 	struct rulehead head;
164 };
165 
166 /*
167  * Temporary structures used to build a 'struct rule' above.
168  */
169 
170 struct id_elem {
171 	TAILQ_ENTRY(id_elem) ie_entries;
172 	struct id_spec spec;
173 };
174 
175 TAILQ_HEAD(id_list, id_elem);
176 
177 #ifdef INVARIANTS
178 static void
179 check_type(const id_type_t type)
180 {
181 	if (type > IT_LAST)
182 		panic("Invalid type number %u", type);
183 }
184 
185 static void
186 panic_for_unexpected_flags(const id_type_t type, const flags_t flags,
187     const char *const str)
188 {
189 	panic("ID type %s: Unexpected flags %u (%s), ", id_type_to_str[type],
190 	    flags, str);
191 }
192 
193 static void
194 check_type_and_id_flags(const id_type_t type, const flags_t flags)
195 {
196 	const char *str;
197 
198 	check_type(type);
199 	switch (type) {
200 	case IT_UID:
201 		if (flags != 0) {
202 			str = "only 0 allowed";
203 			goto unexpected_flags;
204 		}
205 		break;
206 	case IT_GID:
207 		if ((flags & ~MDF_ID_MASK) != 0) {
208 			str = "only bits in MDF_ID_MASK allowed";
209 			goto unexpected_flags;
210 		}
211 		if (!powerof2(flags & MDF_SUPP_MASK)) {
212 			str = "only a single flag in MDF_SUPP_MASK allowed";
213 			goto unexpected_flags;
214 		}
215 		break;
216 	default:
217 	    __assert_unreachable();
218 	}
219 	return;
220 
221 unexpected_flags:
222 	panic_for_unexpected_flags(type, flags, str);
223 }
224 
225 static void
226 check_type_and_id_spec(const id_type_t type, const struct id_spec *const is)
227 {
228 	check_type_and_id_flags(type, is->flags);
229 }
230 
231 static void
232 check_type_and_type_flags(const id_type_t type, const flags_t flags)
233 {
234 	const char *str;
235 
236 	check_type_and_id_flags(type, flags & MDF_ID_MASK);
237 	if ((flags & ~MDF_ID_MASK & ~MDF_TYPE_MASK) != 0) {
238 		str = "only MDF_ID_MASK | MDF_TYPE_MASK bits allowed";
239 		goto unexpected_flags;
240 	}
241 	if ((flags & MDF_ANY) != 0 && (flags & MDF_CURRENT) != 0 &&
242 	    (type == IT_UID || (flags & MDF_PRIMARY) != 0)) {
243 		str = "MDF_ANY and MDF_CURRENT are exclusive for UIDs "
244 		    "or primary group GIDs";
245 		goto unexpected_flags;
246 	}
247 	if ((flags & MDF_ANY_SUPP) != 0 && (flags & MDF_CURRENT) != 0 &&
248 	    (flags & MDF_SUPP_MASK) != 0) {
249 		str = "MDF_SUPP_ANY and MDF_CURRENT with supplementary "
250 		    "groups specification are exclusive";
251 		goto unexpected_flags;
252 	}
253 	if (((flags & MDF_PRIMARY) != 0 || (flags & MDF_ANY) != 0) &&
254 	    (flags & MDF_HAS_PRIMARY_CLAUSE) == 0) {
255 		str = "Presence of folded primary clause not reflected "
256 		    "by presence of MDF_HAS_PRIMARY_CLAUSE";
257 		goto unexpected_flags;
258 	}
259 	if (((flags & MDF_SUPP_MASK) != 0 || (flags & MDF_ANY_SUPP) != 0) &&
260 	    (flags & MDF_HAS_SUPP_CLAUSE) == 0) {
261 		str = "Presence of folded supplementary clause not reflected "
262 		    "by presence of MDF_HAS_SUPP_CLAUSE";
263 		goto unexpected_flags;
264 	}
265 	return;
266 
267 unexpected_flags:
268 	panic_for_unexpected_flags(type, flags, str);
269 }
270 #else /* !INVARIANTS */
271 #define check_type_and_id_flags(...)
272 #define check_type_and_id_spec(...)
273 #define check_type_and_type_flags(...)
274 #endif /* INVARIANTS */
275 
276 /*
277  * Returns EALREADY if both flags have some overlap, or EINVAL if flags are
278  * incompatible, else 0 with flags successfully merged into 'dest'.
279  */
280 static int
281 coalesce_id_flags(const flags_t src, flags_t *const dest)
282 {
283 	flags_t res;
284 
285 	if ((src & *dest) != 0)
286 		return (EALREADY);
287 
288 	res = src | *dest;
289 
290 	/* Check for compatibility of supplementary flags, and coalesce. */
291 	if ((res & MDF_SUPP_MASK) != 0) {
292 		/* MDF_SUPP_DONT incompatible with the rest. */
293 		if ((res & MDF_SUPP_DONT) != 0 && (res & MDF_SUPP_MASK &
294 		    ~MDF_SUPP_DONT) != 0)
295 			return (EINVAL);
296 		/*
297 		 * Coalesce MDF_SUPP_ALLOW and MDF_SUPP_MUST into MDF_SUPP_MUST.
298 		 */
299 		if ((res & MDF_SUPP_ALLOW) != 0 && (res & MDF_SUPP_MUST) != 0)
300 			res &= ~MDF_SUPP_ALLOW;
301 	}
302 
303 	*dest = res;
304 	return (0);
305 }
306 
307 static void
308 toast_rules(struct rules *const rules)
309 {
310 	struct rulehead *const head = &rules->head;
311 	struct rule *rule;
312 
313 	while ((rule = TAILQ_FIRST(head)) != NULL) {
314 		TAILQ_REMOVE(head, rule, r_entries);
315 		free(rule->uids, M_DO);
316 		free(rule->gids, M_DO);
317 		free(rule, M_DO);
318 	}
319 	free(rules, M_DO);
320 }
321 
322 static struct rules *
323 alloc_rules(void)
324 {
325 	struct rules *const rules = malloc(sizeof(*rules), M_DO, M_WAITOK);
326 
327 	_Static_assert(MAC_RULE_STRING_LEN > 0, "MAC_RULE_STRING_LEN <= 0!");
328 	rules->string[0] = 0;
329 	TAILQ_INIT(&rules->head);
330 	return (rules);
331 }
332 
333 static bool
334 is_null_or_empty(const char *s)
335 {
336 	return (s == NULL || s[0] == '\0');
337 }
338 
339 /*
340  * String to unsigned int.
341  *
342  * Contrary to the "standard" strtou*() family of functions, do not tolerate
343  * spaces at start nor an empty string, and returns a status code, the 'u_int'
344  * result being returned through a passed pointer (if no error).
345  *
346  * We detour through 'quad_t' because in-kernel strto*() functions cannot set
347  * 'errno' and thus can't distinguish a true maximum value from one returned
348  * because of overflow.  We use 'quad_t' instead of 'u_quad_t' to support
349  * negative specifications (e.g., such as "-1" for UINT_MAX).
350  */
351 static int
352 strtoui_strict(const char *const restrict s, const char **const restrict endptr,
353     int base, u_int *result)
354 {
355 	char *ep;
356 	quad_t q;
357 
358 	/* Rule out spaces and empty specifications. */
359 	if (s[0] == '\0' || isspace(s[0])) {
360 		if (endptr != NULL)
361 			*endptr = s;
362 		return (EINVAL);
363 	}
364 
365 	q = strtoq(s, &ep, base);
366 	if (endptr != NULL)
367 		*endptr = ep;
368 	if (q < 0) {
369 		/* We allow specifying a negative number. */
370 		if (q < -(quad_t)UINT_MAX - 1 || q == QUAD_MIN)
371 			return (EOVERFLOW);
372 	} else {
373 		if (q > UINT_MAX || q == UQUAD_MAX)
374 			return (EOVERFLOW);
375 	}
376 
377 	*result = (u_int)q;
378 	return (0);
379 }
380 
381 
382 static void
383 make_parse_error(struct parse_error **const parse_error, const size_t pos,
384     const char *const fmt, ...)
385 {
386 	struct parse_error *const err = malloc(sizeof(*err), M_DO, M_WAITOK);
387 	va_list ap;
388 
389 	err->pos = pos;
390 	va_start(ap, fmt);
391 	vsnprintf(err->msg, PARSE_ERROR_SIZE, fmt, ap);
392 	va_end(ap);
393 
394 	MPASS(*parse_error == NULL);
395 	*parse_error = err;
396 }
397 
398 static void
399 free_parse_error(struct parse_error *const parse_error)
400 {
401 	free(parse_error, M_DO);
402 }
403 
404 static int
405 parse_id_type(const char *const string, id_type_t *const type,
406     struct parse_error **const parse_error)
407 {
408 	/*
409 	 * Special case for "any", as the canonical form for IT_ANY in
410 	 * id_type_to_str[] is "*".
411 	 */
412 	if (strcmp(string, "any") == 0) {
413 		*type = IT_ANY;
414 		return (0);
415 	}
416 
417 	/* Start at 1 to avoid parsing "invalid". */
418 	for (size_t i = 1; i <= IT_LAST; ++i) {
419 		if (strcmp(string, id_type_to_str[i]) == 0) {
420 			*type = i;
421 			return (0);
422 		}
423 	}
424 
425 	*type = IT_INVALID;
426 	make_parse_error(parse_error, 0, "No valid type found.");
427 	return (EINVAL);
428 }
429 
430 static size_t
431 parse_gid_flags(const char *const string, flags_t *const flags,
432     flags_t *const gid_flags)
433 {
434 	switch (string[0]) {
435 	case '+':
436 		*flags |= MDF_SUPP_ALLOW;
437 		goto has_supp_clause;
438 	case '!':
439 		*flags |= MDF_SUPP_MUST;
440 		*gid_flags |= MDF_MAY_REJ_SUPP;
441 		goto has_supp_clause;
442 	case '-':
443 		*flags |= MDF_SUPP_DONT;
444 		*gid_flags |= MDF_MAY_REJ_SUPP;
445 		goto has_supp_clause;
446 	has_supp_clause:
447 		*gid_flags |= MDF_HAS_SUPP_CLAUSE;
448 		return (1);
449 	}
450 
451 	return (0);
452 }
453 
454 static bool
455 parse_any(const char *const string)
456 {
457 	return (strcmp(string, "*") == 0 || strcmp(string, "any") == 0);
458 }
459 
460 static bool
461 has_clauses(const id_nb_t nb, const flags_t type_flags)
462 {
463 	return ((type_flags & MDF_TYPE_MASK) != 0 || nb != 0);
464 }
465 
466 static int
467 parse_target_clause(char *to, struct rule *const rule,
468     struct id_list *const uid_list, struct id_list *const gid_list,
469     struct parse_error **const parse_error)
470 {
471 	const char *const start = to;
472 	char *to_type, *to_id;
473 	const char *p;
474 	struct id_list *list;
475 	id_nb_t *nb;
476 	flags_t *tflags;
477 	struct id_elem *ie;
478 	struct id_spec is = {.flags = 0};
479 	flags_t gid_flags = 0;
480 	id_type_t type;
481 	int error;
482 
483 	MPASS(*parse_error == NULL);
484 	MPASS(to != NULL);
485 	to_type = strsep(&to, "=");
486 	MPASS(to_type != NULL);
487 	to_type += parse_gid_flags(to_type, &is.flags, &gid_flags);
488 	error = parse_id_type(to_type, &type, parse_error);
489 	if (error != 0)
490 		goto einval;
491 	if (type != IT_GID && is.flags != 0) {
492 		make_parse_error(parse_error, to_type - start,
493 		    "Expected type 'gid' after flags, not '%s'.",
494 		    to_type);
495 		goto einval;
496 	}
497 
498 	to_id = strsep(&to, "");
499 	switch (type) {
500 	case IT_GID:
501 		if (to_id == NULL) {
502 			make_parse_error(parse_error, to_type - start,
503 			    "No '=' and ID specification after type '%s'.",
504 			    to_type);
505 			goto einval;
506 		}
507 
508 		if (is.flags == 0) {
509 			/* No flags: Dealing with a primary group. */
510 			is.flags |= MDF_PRIMARY;
511 			gid_flags |= MDF_HAS_PRIMARY_CLAUSE;
512 		}
513 
514 		list = gid_list;
515 		nb = &rule->gids_nb;
516 		tflags = &rule->gid_flags;
517 
518 		/* "*" or "any"? */
519 		if (parse_any(to_id)) {
520 			/*
521 			 * We check that we have not seen any other clause of
522 			 * the same category (i.e., concerning primary or
523 			 * supplementary groups).
524 			 */
525 			if ((is.flags & MDF_PRIMARY) != 0) {
526 				if ((*tflags & MDF_HAS_PRIMARY_CLAUSE) != 0) {
527 					make_parse_error(parse_error,
528 					    to_id - start,
529 					    "'any' specified after another "
530 					    "(primary) GID.");
531 					goto einval;
532 				}
533 				*tflags |= gid_flags | MDF_ANY;
534 			} else {
535 				/*
536 				 * If a supplementary group flag was present, it
537 				 * must be MDF_SUPP_ALLOW ("+").
538 				 */
539 				if ((is.flags & MDF_SUPP_MASK) != MDF_SUPP_ALLOW) {
540 					make_parse_error(parse_error,
541 					    to_id - start,
542 					    "'any' specified with another "
543 					    "flag than '+'.");
544 					goto einval;
545 				}
546 				if ((*tflags & MDF_HAS_SUPP_CLAUSE) != 0) {
547 					make_parse_error(parse_error,
548 					    to_id - start,
549 					    "'any' with flag '+' specified after "
550 					    "another (supplementary) GID.");
551 					goto einval;
552 				}
553 				*tflags |= gid_flags | MDF_ANY_SUPP;
554 			}
555 			goto check_type_and_finish;
556 		} else {
557 			/*
558 			 * Check that we haven't already seen "any" for the same
559 			 * category.
560 			 */
561 			if ((is.flags & MDF_PRIMARY) != 0) {
562 				if ((*tflags & MDF_ANY) != 0) {
563 					make_parse_error(parse_error,
564 					    to_id - start,
565 					    "Some (primary) GID specified after "
566 					    "'any'.");
567 					goto einval;
568 				}
569 			} else if ((*tflags & MDF_ANY_SUPP) != 0 &&
570 			    (is.flags & MDF_SUPP_ALLOW) != 0) {
571 				make_parse_error(parse_error,
572 				    to_id - start,
573 				    "Some (supplementary) GID specified after "
574 				    "'any' with flag '+'.");
575 				goto einval;
576 			}
577 			*tflags |= gid_flags;
578 		}
579 		break;
580 
581 	case IT_UID:
582 		if (to_id == NULL) {
583 			make_parse_error(parse_error, to_type - start,
584 			    "No '=' and ID specification after type '%s'.",
585 			    to_type);
586 			goto einval;
587 		}
588 
589 		list = uid_list;
590 		nb = &rule->uids_nb;
591 		tflags = &rule->uid_flags;
592 
593 		/* "*" or "any"? */
594 		if (parse_any(to_id)) {
595 			/* There must not be any other clause. */
596 			if (has_clauses(*nb, *tflags)) {
597 				make_parse_error(parse_error, to_id - start,
598 				    "'any' specified after another UID.");
599 				goto einval;
600 			}
601 			*tflags |= MDF_ANY;
602 			goto check_type_and_finish;
603 		} else {
604 			/*
605 			 * Check that we haven't already seen "any" for the same
606 			 * category.
607 			 */
608 			if ((*tflags & MDF_ANY) != 0) {
609 				make_parse_error(parse_error, to_id - start,
610 				    "Some UID specified after 'any'.");
611 				goto einval;
612 			}
613 		}
614 		break;
615 
616 	case IT_ANY:
617 		/* No ID allowed. */
618 		if (to_id != NULL) {
619 			make_parse_error(parse_error, to_type - start,
620 			    "No '=' and ID allowed after type '%s'.", to_type);
621 			goto einval;
622 		}
623 		/*
624 		 * We can't have IT_ANY after any other IT_*, it must be the
625 		 * only one.
626 		 */
627 		if (has_clauses(rule->uids_nb, rule->uid_flags) ||
628 		    has_clauses(rule->gids_nb, rule->gid_flags)) {
629 			make_parse_error(parse_error, to_type - start,
630 			    "Target clause of type '%s' coming after another "
631 			    "clause (must be alone).", to_type);
632 			goto einval;
633 		}
634 		rule->uid_flags |= MDF_ANY;
635 		rule->gid_flags |= MDF_ANY | MDF_ANY_SUPP |
636 		    MDF_HAS_PRIMARY_CLAUSE | MDF_HAS_SUPP_CLAUSE;
637 		goto finish;
638 
639 	default:
640 		/* parse_id_type() returns no other types currently. */
641 		__assert_unreachable();
642 	}
643 
644 	/* Rule out cases that have been treated above. */
645 	MPASS((type == IT_UID || type == IT_GID) && !parse_any(to_id));
646 
647 	/* "."? */
648 	if (strcmp(to_id, ".") == 0) {
649 		if ((*tflags & MDF_CURRENT) != 0) {
650 			/* Duplicate "." <id>.  Try to coalesce. */
651 			error = coalesce_id_flags(is.flags, tflags);
652 			if (error != 0) {
653 				make_parse_error(parse_error, to_id - start,
654 				    "Incompatible flags with prior clause "
655 				    "with same target.");
656 				goto einval;
657 			}
658 		} else
659 			*tflags |= MDF_CURRENT | is.flags;
660 		goto check_type_and_finish;
661 	}
662 
663 	/* Parse an ID. */
664 	error = strtoui_strict(to_id, &p, 10, &is.id);
665 	if (error != 0 || *p != '\0') {
666 		make_parse_error(parse_error, to_id - start,
667 		    "Cannot parse a numerical ID (base 10).");
668 		goto einval;
669 	}
670 
671 	/* Explicit ID flags. */
672 	if (type == IT_GID && (is.flags & MDF_SUPP_MUST) != 0)
673 		*tflags |= MDF_EXPLICIT_SUPP_MUST;
674 
675 	/*
676 	 * We check for duplicate IDs and coalesce their 'struct id_spec' only
677 	 * at end of parse_single_rule() because it is much more performant then
678 	 * (using sorted arrays).
679 	 */
680 	++*nb;
681 	if (*nb == 0) {
682 		make_parse_error(parse_error, 0,
683 		    "Too many target clauses of type '%s'.", to_type);
684 		return (EOVERFLOW);
685 	}
686 	ie = malloc(sizeof(*ie), M_DO, M_WAITOK);
687 	ie->spec = is;
688 	TAILQ_INSERT_TAIL(list, ie, ie_entries);
689 	check_type_and_id_spec(type, &is);
690 check_type_and_finish:
691 	check_type_and_type_flags(type, *tflags);
692 finish:
693 	return (0);
694 einval:
695 	/* We must have built a parse error on error. */
696 	MPASS(*parse_error != NULL);
697 	return (EINVAL);
698 }
699 
700 static int
701 u_int_cmp(const u_int i1, const u_int i2)
702 {
703 	return ((i1 > i2) - (i1 < i2));
704 }
705 
706 static int
707 id_spec_cmp(const void *const p1, const void *const p2)
708 {
709 	const struct id_spec *const is1 = p1;
710 	const struct id_spec *const is2 = p2;
711 
712 	return (u_int_cmp(is1->id, is2->id));
713 }
714 
715 /*
716  * Transfer content of 'list' into 'array', freeing and emptying list.
717  *
718  * 'nb' must be 'list''s length and not be greater than 'array''s size.  The
719  * destination array is sorted by ID.  Structures 'struct id_spec' with same IDs
720  * are coalesced if that makes sense (not including duplicate clauses), else
721  * EINVAL is returned.  On success, 'nb' is updated (lowered) to account for
722  * coalesced specifications.  The parameter 'type' is only for testing purposes
723  * (INVARIANTS).
724  */
725 static int
726 pour_list_into_rule(const id_type_t type, struct id_list *const list,
727     struct id_spec *const array, id_nb_t *const nb,
728     struct parse_error **const parse_error)
729 {
730 	struct id_elem *ie, *ie_next;
731 	size_t idx = 0;
732 
733 	/* Fill the array. */
734 	TAILQ_FOREACH_SAFE(ie, list, ie_entries, ie_next) {
735 		MPASS(idx < *nb);
736 		array[idx] = ie->spec;
737 		free(ie, M_DO);
738 		++idx;
739 	}
740 	MPASS(idx == *nb);
741 	TAILQ_INIT(list);
742 
743 	/* Sort it (by ID). */
744 	qsort(array, *nb, sizeof(*array), id_spec_cmp);
745 
746 	/* Coalesce same IDs. */
747 	if (*nb != 0) {
748 		size_t ref_idx = 0;
749 
750 		for (idx = 1; idx < *nb; ++idx) {
751 			const u_int id = array[idx].id;
752 
753 			if (id != array[ref_idx].id) {
754 				++ref_idx;
755 				if (ref_idx != idx)
756 					array[ref_idx] = array[idx];
757 				continue;
758 			}
759 
760 			switch (type) {
761 				int error;
762 
763 			case IT_GID:
764 				error = coalesce_id_flags(array[idx].flags,
765 				    &array[ref_idx].flags);
766 				if (error != 0) {
767 					make_parse_error(parse_error, 0,
768 					    "Incompatible flags or duplicate "
769 					    "GID %u.", id);
770 					return (EINVAL);
771 				}
772 				check_type_and_id_flags(type,
773 				    array[ref_idx].flags);
774 				break;
775 
776 			case IT_UID:
777 				/*
778 				 * No flags in this case.  Multiple appearances
779 				 * of the same UID is an exact redundancy, so
780 				 * error out.
781 				 */
782 				make_parse_error(parse_error, 0,
783 				    "Duplicate UID %u.", id);
784 				return (EINVAL);
785 
786 			default:
787 				__assert_unreachable();
788 			}
789 		}
790 		*nb = ref_idx + 1;
791 	}
792 
793 	return (0);
794 }
795 
796 /*
797  * See also first comments for parse_rule() below.
798  *
799  * The second part of a rule, called <target> (or <to>), is a comma-separated
800  * (',') list of '<flags><type>=<id>' clauses similar to that of the <from>
801  * part, with the extensions that <id> may also be "*" or "any" or ".", and that
802  * <flags> may contain at most one of the '+', '-' and '!' characters when
803  * <type> is "gid" (no flags are allowed for "uid").  No two clauses in a single
804  * <to> list may list the same <id>.  "*" and "any" both designate any ID for
805  * the <type>, and are aliases to each other.  In front of "any" (or "*"), only
806  * the '+' flag is allowed (in the "gid" case).  "." designates the process'
807  * current IDs for the <type>.  The precise meaning of flags and "." is
808  * explained in functions checking privileges below.
809  */
810 static int
811 parse_single_rule(char *rule, struct rules *const rules,
812     struct parse_error **const parse_error)
813 {
814 	const char *const start = rule;
815 	const char *from_type, *from_id, *p;
816 	char *to_list;
817 	struct id_list uid_list, gid_list;
818 	struct id_elem *ie, *ie_next;
819 	struct rule *new;
820 	int error;
821 
822 	MPASS(*parse_error == NULL);
823 	TAILQ_INIT(&uid_list);
824 	TAILQ_INIT(&gid_list);
825 
826 	/* Freed when the 'struct rules' container is freed. */
827 	new = malloc(sizeof(*new), M_DO, M_WAITOK | M_ZERO);
828 
829 	from_type = strsep(&rule, "=");
830 	MPASS(from_type != NULL); /* Because 'rule' was not NULL. */
831 	error = parse_id_type(from_type, &new->from_type, parse_error);
832 	if (error != 0)
833 		goto einval;
834 	switch (new->from_type) {
835 	case IT_UID:
836 	case IT_GID:
837 		break;
838 	default:
839 		make_parse_error(parse_error, 0, "Type '%s' not allowed in "
840 		    "the \"from\" part of rules.");
841 		goto einval;
842 	}
843 
844 	from_id = strsep(&rule, ":");
845 	if (is_null_or_empty(from_id)) {
846 		make_parse_error(parse_error, 0, "No ID specified.");
847 		goto einval;
848 	}
849 
850 	error = strtoui_strict(from_id, &p, 10, &new->from_id);
851 	if (error != 0 || *p != '\0') {
852 		make_parse_error(parse_error, from_id - start,
853 		    "Cannot parse a numerical ID (base 10).");
854 		goto einval;
855 	}
856 
857 	/*
858 	 * We will now parse the "to" list.
859 	 *
860 	 * In order to ease parsing, we will begin by building lists of target
861 	 * UIDs and GIDs in local variables 'uid_list' and 'gid_list'.  The
862 	 * number of each type of IDs will be filled directly in 'new'.  At end
863 	 * of parse, we will allocate both arrays of IDs to be placed into the
864 	 * 'uids' and 'gids' members, sort them, and discard the tail queues
865 	 * used to build them.  This conversion to sorted arrays at end of parse
866 	 * allows to minimize memory allocations and enables searching IDs in
867 	 * O(log(n)) instead of linearly.
868 	 */
869 	to_list = strsep(&rule, ",");
870 	if (to_list == NULL) {
871 		make_parse_error(parse_error, 0, "No target list.");
872 		goto einval;
873 	}
874 	do {
875 		error = parse_target_clause(to_list, new, &uid_list, &gid_list,
876 		    parse_error);
877 		if (error != 0) {
878 			(*parse_error)->pos += to_list - start;
879 			goto einval;
880 		}
881 
882 		to_list = strsep(&rule, ",");
883 	} while (to_list != NULL);
884 
885 	if (new->uids_nb != 0) {
886 		new->uids = malloc(sizeof(*new->uids) * new->uids_nb, M_DO,
887 		    M_WAITOK);
888 		error = pour_list_into_rule(IT_UID, &uid_list, new->uids,
889 		    &new->uids_nb, parse_error);
890 		if (error != 0)
891 			goto einval;
892 	}
893 	MPASS(TAILQ_EMPTY(&uid_list));
894 	if (!has_clauses(new->uids_nb, new->uid_flags)) {
895 		/* No UID specified, default is "uid=.". */
896 		MPASS(new->uid_flags == 0);
897 		new->uid_flags = MDF_CURRENT;
898 		check_type_and_type_flags(IT_UID, new->uid_flags);
899 	}
900 
901 	if (new->gids_nb != 0) {
902 		new->gids = malloc(sizeof(*new->gids) * new->gids_nb, M_DO,
903 		    M_WAITOK);
904 		error = pour_list_into_rule(IT_GID, &gid_list, new->gids,
905 		    &new->gids_nb, parse_error);
906 		if (error != 0)
907 			goto einval;
908 	}
909 	MPASS(TAILQ_EMPTY(&gid_list));
910 	if (!has_clauses(new->gids_nb, new->gid_flags)) {
911 		/* No GID specified, default is "gid=.,!gid=.". */
912 		MPASS(new->gid_flags == 0);
913 		new->gid_flags = MDF_CURRENT | MDF_PRIMARY | MDF_SUPP_MUST |
914 		    MDF_HAS_PRIMARY_CLAUSE | MDF_HAS_SUPP_CLAUSE;
915 		check_type_and_type_flags(IT_GID, new->gid_flags);
916 	}
917 
918 	TAILQ_INSERT_TAIL(&rules->head, new, r_entries);
919 	return (0);
920 
921 einval:
922 	free(new->gids, M_DO);
923 	free(new->uids, M_DO);
924 	free(new, M_DO);
925 	TAILQ_FOREACH_SAFE(ie, &gid_list, ie_entries, ie_next)
926 	    free(ie, M_DO);
927 	TAILQ_FOREACH_SAFE(ie, &uid_list, ie_entries, ie_next)
928 	    free(ie, M_DO);
929 	MPASS(*parse_error != NULL);
930 	return (EINVAL);
931 }
932 
933 /*
934  * Parse rules specification and produce rule structures out of it.
935  *
936  * Returns 0 on success, with '*rulesp' made to point to a 'struct rule'
937  * representing the rules.  On error, the returned value is non-zero and
938  * '*rulesp' is unchanged.  If 'string' has length greater or equal to
939  * MAC_RULE_STRING_LEN, ENAMETOOLONG is returned.  If it is not in the expected
940  * format, EINVAL is returned.  If an error is returned, '*parse_error' is set
941  * to point to a 'struct parse_error' giving an error message for the problem,
942  * else '*parse_error' is set to NULL.
943  *
944  * Expected format: A semi-colon-separated list of rules of the form
945  * "<from>:<target>".  The <from> part is of the form "<type>=<id>" where <type>
946  * is "uid" or "gid", <id> an UID or GID (depending on <type>) and <target> is
947  * "*", "any" or a comma-separated list of '<flags><type>=<id>' clauses (see the
948  * comment for parse_single_rule() for more details).  For convenience, empty
949  * rules are allowed (and do nothing).
950  *
951  * Examples:
952  * - "uid=1001:uid=1010,gid=1010;uid=1002:any"
953  * - "gid=1010:gid=1011,gid=1012,gid=1013"
954  */
955 static int
956 parse_rules(const char *const string, struct rules **const rulesp,
957     struct parse_error **const parse_error)
958 {
959 	const size_t len = strlen(string);
960 	char *copy, *p, *rule;
961 	struct rules *rules;
962 	int error = 0;
963 
964 	*parse_error = NULL;
965 
966 	if (len >= MAC_RULE_STRING_LEN) {
967 		make_parse_error(parse_error, 0,
968 		    "Rule specification string is too long (%zu, max %zu)",
969 		    len, MAC_RULE_STRING_LEN - 1);
970 		return (ENAMETOOLONG);
971 	}
972 
973 	rules = alloc_rules();
974 	bcopy(string, rules->string, len + 1);
975 	MPASS(rules->string[len] == '\0'); /* Catch some races. */
976 
977 	copy = malloc(len + 1, M_DO, M_WAITOK);
978 	bcopy(string, copy, len + 1);
979 	MPASS(copy[len] == '\0'); /* Catch some races. */
980 
981 	p = copy;
982 	while ((rule = strsep(&p, ";")) != NULL) {
983 		if (rule[0] == '\0')
984 			continue;
985 		error = parse_single_rule(rule, rules, parse_error);
986 		if (error != 0) {
987 			(*parse_error)->pos += rule - copy;
988 			toast_rules(rules);
989 			goto out;
990 		}
991 	}
992 
993 	*rulesp = rules;
994 out:
995 	free(copy, M_DO);
996 	return (error);
997 }
998 
999 /*
1000  * Find rules applicable to the passed prison.
1001  *
1002  * Returns the applicable rules (and never NULL).  'pr' must be unlocked.
1003  * 'aprp' is set to the (ancestor) prison holding these, and it must be unlocked
1004  * once the caller is done accessing the rules.  '*aprp' is equal to 'pr' if and
1005  * only if the current jail has its own set of rules.
1006  */
1007 static struct rules *
1008 find_rules(struct prison *const pr, struct prison **const aprp)
1009 {
1010 	struct prison *cpr, *ppr;
1011 	struct rules *rules;
1012 
1013 	cpr = pr;
1014 	for (;;) {
1015 		prison_lock(cpr);
1016 		rules = osd_jail_get(cpr, osd_jail_slot);
1017 		if (rules != NULL)
1018 			break;
1019 		prison_unlock(cpr);
1020 
1021 		ppr = cpr->pr_parent;
1022 		MPASS(ppr != NULL); /* prison0 always has rules. */
1023 		cpr = ppr;
1024 	}
1025 
1026 	*aprp = cpr;
1027 	return (rules);
1028 }
1029 
1030 /*
1031  * OSD destructor for slot 'osd_jail_slot'.
1032  *
1033  * Called with 'value' not NULL.
1034  */
1035 static void
1036 dealloc_osd(void *const value)
1037 {
1038 	struct rules *const rules = value;
1039 
1040 	toast_rules(rules);
1041 }
1042 
1043 /*
1044  * Remove the rules specifically associated to a prison.
1045  *
1046  * In practice, this means that the rules become inherited (from the closest
1047  * ascendant that has some).
1048  *
1049  * Destroys the 'osd_jail_slot' slot of the passed jail.
1050  */
1051 static void
1052 remove_rules(struct prison *const pr)
1053 {
1054 	prison_lock(pr);
1055 	/* This calls destructor dealloc_osd(). */
1056 	osd_jail_del(pr, osd_jail_slot);
1057 	prison_unlock(pr);
1058 }
1059 
1060 /*
1061  * Assign already built rules to a jail.
1062  */
1063 static void
1064 set_rules(struct prison *const pr, struct rules *const rules)
1065 {
1066 	struct rules *old_rules;
1067 	void **rsv;
1068 
1069 	rsv = osd_reserve(osd_jail_slot);
1070 
1071 	prison_lock(pr);
1072 	old_rules = osd_jail_get(pr, osd_jail_slot);
1073 	osd_jail_set_reserved(pr, osd_jail_slot, rsv, rules);
1074 	prison_unlock(pr);
1075 	if (old_rules != NULL)
1076 		toast_rules(old_rules);
1077 }
1078 
1079 /*
1080  * Assigns empty rules to a jail.
1081  */
1082 static void
1083 set_empty_rules(struct prison *const pr)
1084 {
1085 	struct rules *const rules = alloc_rules();
1086 
1087 	set_rules(pr, rules);
1088 }
1089 
1090 /*
1091  * Parse a rules specification and assign them to a jail.
1092  *
1093  * Returns the same error code as parse_rules() (which see).
1094  */
1095 static int
1096 parse_and_set_rules(struct prison *const pr, const char *rules_string,
1097     struct parse_error **const parse_error)
1098 {
1099 	struct rules *rules;
1100 	int error;
1101 
1102 	error = parse_rules(rules_string, &rules, parse_error);
1103 	if (error != 0)
1104 		return (error);
1105 	set_rules(pr, rules);
1106 	return (0);
1107 }
1108 
1109 static int
1110 mac_do_sysctl_rules(SYSCTL_HANDLER_ARGS)
1111 {
1112 	char *const buf = malloc(MAC_RULE_STRING_LEN, M_DO, M_WAITOK);
1113 	struct prison *const td_pr = req->td->td_ucred->cr_prison;
1114 	struct prison *pr;
1115 	struct rules *rules;
1116 	struct parse_error *parse_error;
1117 	int error;
1118 
1119 	rules = find_rules(td_pr, &pr);
1120 	strlcpy(buf, rules->string, MAC_RULE_STRING_LEN);
1121 	prison_unlock(pr);
1122 
1123 	error = sysctl_handle_string(oidp, buf, MAC_RULE_STRING_LEN, req);
1124 	if (error != 0 || req->newptr == NULL)
1125 		goto out;
1126 
1127 	/* Set our prison's rules, not that of the jail we inherited from. */
1128 	error = parse_and_set_rules(td_pr, buf, &parse_error);
1129 	if (error != 0) {
1130 		if (print_parse_error)
1131 			printf("MAC/do: Parse error at index %zu: %s\n",
1132 			    parse_error->pos, parse_error->msg);
1133 		free_parse_error(parse_error);
1134 	}
1135 out:
1136 	free(buf, M_DO);
1137 	return (error);
1138 }
1139 
1140 SYSCTL_PROC(_security_mac_do, OID_AUTO, rules,
1141     CTLTYPE_STRING|CTLFLAG_RW|CTLFLAG_PRISON|CTLFLAG_MPSAFE,
1142     0, 0, mac_do_sysctl_rules, "A",
1143     "Rules");
1144 
1145 
1146 SYSCTL_JAIL_PARAM_SYS_SUBNODE(mac, do, CTLFLAG_RW, "Jail MAC/do parameters");
1147 SYSCTL_JAIL_PARAM_STRING(_mac_do, rules, CTLFLAG_RW, MAC_RULE_STRING_LEN,
1148     "Jail MAC/do rules");
1149 
1150 
1151 static int
1152 mac_do_jail_create(void *obj, void *data __unused)
1153 {
1154 	struct prison *const pr = obj;
1155 
1156 	set_empty_rules(pr);
1157 	return (0);
1158 }
1159 
1160 static int
1161 mac_do_jail_get(void *obj, void *data)
1162 {
1163 	struct prison *ppr, *const pr = obj;
1164 	struct vfsoptlist *const opts = data;
1165 	struct rules *rules;
1166 	int jsys, error;
1167 
1168 	rules = find_rules(pr, &ppr);
1169 
1170 	jsys = pr == ppr ?
1171 	    (TAILQ_EMPTY(&rules->head) ? JAIL_SYS_DISABLE : JAIL_SYS_NEW) :
1172 	    JAIL_SYS_INHERIT;
1173 	error = vfs_setopt(opts, "mac.do", &jsys, sizeof(jsys));
1174 	if (error != 0 && error != ENOENT)
1175 		goto done;
1176 
1177 	error = vfs_setopts(opts, "mac.do.rules", rules->string);
1178 	if (error != 0 && error != ENOENT)
1179 		goto done;
1180 
1181 	error = 0;
1182 done:
1183 	prison_unlock(ppr);
1184 	return (error);
1185 }
1186 
1187 /*
1188  * -1 is used as a sentinel in mac_do_jail_check() and mac_do_jail_set() below.
1189  */
1190 _Static_assert(-1 != JAIL_SYS_DISABLE && -1 != JAIL_SYS_NEW &&
1191     -1 != JAIL_SYS_INHERIT,
1192     "mac_do(4) uses -1 as a sentinel for uninitialized 'jsys'.");
1193 
1194 /*
1195  * We perform only cheap checks here, i.e., we do not really parse the rules
1196  * specification string, if any.
1197  */
1198 static int
1199 mac_do_jail_check(void *obj, void *data)
1200 {
1201 	struct vfsoptlist *opts = data;
1202 	char *rules_string;
1203 	int error, jsys, size;
1204 
1205 	error = vfs_copyopt(opts, "mac.do", &jsys, sizeof(jsys));
1206 	if (error == ENOENT)
1207 		jsys = -1;
1208 	else {
1209 		if (error != 0)
1210 			return (error);
1211 		if (jsys != JAIL_SYS_DISABLE && jsys != JAIL_SYS_NEW &&
1212 		    jsys != JAIL_SYS_INHERIT)
1213 			return (EINVAL);
1214 	}
1215 
1216 	/*
1217 	 * We use vfs_getopt() here instead of vfs_getopts() to get the length.
1218 	 * We perform the additional checks done by the latter here, even if
1219 	 * jail_set() calls vfs_getopts() itself later (they becoming
1220 	 * inconsistent wouldn't cause any security problem).
1221 	 */
1222 	error = vfs_getopt(opts, "mac.do.rules", (void**)&rules_string, &size);
1223 	if (error == ENOENT) {
1224 		/*
1225 		 * Default (in absence of "mac.do.rules") is to disable (and, in
1226 		 * particular, not inherit).
1227 		 */
1228 		if (jsys == -1)
1229 			jsys = JAIL_SYS_DISABLE;
1230 
1231 		if (jsys == JAIL_SYS_NEW) {
1232 			vfs_opterror(opts, "'mac.do.rules' must be specified "
1233 			    "given 'mac.do''s value");
1234 			return (EINVAL);
1235 		}
1236 
1237 		/* Absence of "mac.do.rules" at this point is OK. */
1238 		error = 0;
1239 	} else {
1240 		if (error != 0)
1241 			return (error);
1242 
1243 		/* Not a proper string. */
1244 		if (size == 0 || rules_string[size - 1] != '\0') {
1245 			vfs_opterror(opts, "'mac.do.rules' not a proper string");
1246 			return (EINVAL);
1247 		}
1248 
1249 		if (size > MAC_RULE_STRING_LEN) {
1250 			vfs_opterror(opts, "'mdo.rules' too long");
1251 			return (ENAMETOOLONG);
1252 		}
1253 
1254 		if (jsys == -1)
1255 			/* Default (if "mac.do.rules" is present). */
1256 			jsys = rules_string[0] == '\0' ? JAIL_SYS_DISABLE :
1257 			    JAIL_SYS_NEW;
1258 
1259 		/*
1260 		 * Be liberal and accept JAIL_SYS_DISABLE and JAIL_SYS_INHERIT
1261 		 * with an explicit empty rules specification.
1262 		 */
1263 		switch (jsys) {
1264 		case JAIL_SYS_DISABLE:
1265 		case JAIL_SYS_INHERIT:
1266 			if (rules_string[0] != '\0') {
1267 				vfs_opterror(opts, "'mac.do.rules' specified "
1268 				    "but should not given 'mac.do''s value");
1269 				return (EINVAL);
1270 			}
1271 			break;
1272 		}
1273 	}
1274 
1275 	return (error);
1276 }
1277 
1278 static int
1279 mac_do_jail_set(void *obj, void *data)
1280 {
1281 	struct prison *pr = obj;
1282 	struct vfsoptlist *opts = data;
1283 	char *rules_string;
1284 	struct parse_error *parse_error;
1285 	int error, jsys;
1286 
1287 	/*
1288 	 * The invariants checks used below correspond to what has already been
1289 	 * checked in jail_check() above.
1290 	 */
1291 
1292 	error = vfs_copyopt(opts, "mac.do", &jsys, sizeof(jsys));
1293 	MPASS(error == 0 || error == ENOENT);
1294 	if (error != 0)
1295 		jsys = -1; /* Mark unfilled. */
1296 
1297 	rules_string = vfs_getopts(opts, "mac.do.rules", &error);
1298 	MPASS(error == 0 || error == ENOENT);
1299 	if (error == 0) {
1300 		MPASS(strlen(rules_string) < MAC_RULE_STRING_LEN);
1301 		if (jsys == -1)
1302 			/* Default (if "mac.do.rules" is present). */
1303 			jsys = rules_string[0] == '\0' ? JAIL_SYS_DISABLE :
1304 			    JAIL_SYS_NEW;
1305 		else
1306 			MPASS(jsys == JAIL_SYS_NEW ||
1307 			    ((jsys == JAIL_SYS_DISABLE ||
1308 			    jsys == JAIL_SYS_INHERIT) &&
1309 			    rules_string[0] == '\0'));
1310 	} else {
1311 		MPASS(jsys != JAIL_SYS_NEW);
1312 		if (jsys == -1)
1313 			/*
1314 			 * Default (in absence of "mac.do.rules") is to disable
1315 			 * (and, in particular, not inherit).
1316 			 */
1317 			jsys = JAIL_SYS_DISABLE;
1318 		/* If disabled, we'll store an empty rule specification. */
1319 		if (jsys == JAIL_SYS_DISABLE)
1320 			rules_string = "";
1321 	}
1322 
1323 	switch (jsys) {
1324 	case JAIL_SYS_INHERIT:
1325 		remove_rules(pr);
1326 		error = 0;
1327 		break;
1328 	case JAIL_SYS_DISABLE:
1329 	case JAIL_SYS_NEW:
1330 		error = parse_and_set_rules(pr, rules_string, &parse_error);
1331 		if (error != 0) {
1332 			vfs_opterror(opts,
1333 			    "MAC/do: Parse error at index %zu: %s\n",
1334 			    parse_error->pos, parse_error->msg);
1335 			free_parse_error(parse_error);
1336 		}
1337 		break;
1338 	default:
1339 		__assert_unreachable();
1340 	}
1341 	return (error);
1342 }
1343 
1344 /*
1345  * OSD jail methods.
1346  *
1347  * There is no PR_METHOD_REMOVE, as OSD storage is destroyed by the common jail
1348  * code (see prison_cleanup()), which triggers a run of our dealloc_osd()
1349  * destructor.
1350  */
1351 static const osd_method_t osd_methods[PR_MAXMETHOD] = {
1352 	[PR_METHOD_CREATE] = mac_do_jail_create,
1353 	[PR_METHOD_GET] = mac_do_jail_get,
1354 	[PR_METHOD_CHECK] = mac_do_jail_check,
1355 	[PR_METHOD_SET] = mac_do_jail_set,
1356 };
1357 
1358 
1359 static void
1360 mac_do_init(struct mac_policy_conf *mpc)
1361 {
1362 	struct prison *pr;
1363 
1364 	osd_jail_slot = osd_jail_register(dealloc_osd, osd_methods);
1365 	set_empty_rules(&prison0);
1366 	sx_slock(&allprison_lock);
1367 	TAILQ_FOREACH(pr, &allprison, pr_list)
1368 	    set_empty_rules(pr);
1369 	sx_sunlock(&allprison_lock);
1370 }
1371 
1372 static void
1373 mac_do_destroy(struct mac_policy_conf *mpc)
1374 {
1375 	osd_jail_deregister(osd_jail_slot);
1376 }
1377 
1378 static bool
1379 rule_applies(struct ucred *cred, struct rule *r)
1380 {
1381 	if (r->from_type == IT_UID && r->from_id == cred->cr_uid)
1382 		return (true);
1383 	if (r->from_type == IT_GID && groupmember(r->from_id, cred))
1384 		return (true);
1385 	return (false);
1386 }
1387 
1388 static int
1389 mac_do_priv_grant(struct ucred *cred, int priv)
1390 {
1391 	struct rule *r;
1392 	struct prison *pr;
1393 	struct rules *rule;
1394 
1395 	if (do_enabled == 0)
1396 		return (EPERM);
1397 
1398 	rule = find_rules(cred->cr_prison, &pr);
1399 	TAILQ_FOREACH(r, &rule->head, r_entries) {
1400 		if (rule_applies(cred, r)) {
1401 			switch (priv) {
1402 			case PRIV_CRED_SETGROUPS:
1403 			case PRIV_CRED_SETUID:
1404 				prison_unlock(pr);
1405 				return (0);
1406 			default:
1407 				break;
1408 			}
1409 		}
1410 	}
1411 	prison_unlock(pr);
1412 	return (EPERM);
1413 }
1414 
1415 static int
1416 mac_do_check_setgroups(struct ucred *cred, int ngrp, gid_t *groups)
1417 {
1418 	struct rule *r;
1419 	char *fullpath = NULL;
1420 	char *freebuf = NULL;
1421 	struct prison *pr;
1422 	struct rules *rule;
1423 
1424 	if (do_enabled == 0)
1425 		return (0);
1426 	if (cred->cr_uid == 0)
1427 		return (0);
1428 
1429 	if (vn_fullpath(curproc->p_textvp, &fullpath, &freebuf) != 0)
1430 		return (EPERM);
1431 	if (strcmp(fullpath, "/usr/bin/mdo") != 0) {
1432 		free(freebuf, M_TEMP);
1433 		return (EPERM);
1434 	}
1435 	free(freebuf, M_TEMP);
1436 
1437 	rule = find_rules(cred->cr_prison, &pr);
1438 	TAILQ_FOREACH(r, &rule->head, r_entries) {
1439 		if (rule_applies(cred, r)) {
1440 			prison_unlock(pr);
1441 			return (0);
1442 		}
1443 	}
1444 	prison_unlock(pr);
1445 
1446 	return (EPERM);
1447 }
1448 
1449 static int
1450 mac_do_check_setuid(struct ucred *cred, uid_t uid)
1451 {
1452 	struct rule *r;
1453 	char *fullpath = NULL;
1454 	char *freebuf = NULL;
1455 	struct prison *pr;
1456 	struct rules *rule;
1457 	struct id_spec uid_is = {.id = uid};
1458 	int error;
1459 
1460 	if (do_enabled == 0)
1461 		return (0);
1462 	if (cred->cr_uid == uid || cred->cr_uid == 0 || cred->cr_ruid == 0)
1463 		return (0);
1464 
1465 	if (vn_fullpath(curproc->p_textvp, &fullpath, &freebuf) != 0)
1466 		return (EPERM);
1467 	if (strcmp(fullpath, "/usr/bin/mdo") != 0) {
1468 		free(freebuf, M_TEMP);
1469 		return (EPERM);
1470 	}
1471 	free(freebuf, M_TEMP);
1472 
1473 	error = EPERM;
1474 	rule = find_rules(cred->cr_prison, &pr);
1475 	TAILQ_FOREACH(r, &rule->head, r_entries) {
1476 		if (!((r->from_type == IT_UID && cred->cr_uid == r->from_id) ||
1477 		    (r->from_type == IT_GID && groupmember(r->from_id, cred))))
1478 			continue;
1479 
1480 		if (r->uid_flags & MDF_ANY ||
1481 		    ((r->uid_flags & MDF_CURRENT) && (uid == cred->cr_uid ||
1482 		    uid == cred->cr_ruid || uid == cred->cr_svuid)) ||
1483 		    bsearch(&uid_is, r->uids, r->uids_nb, sizeof(*r->uids),
1484 		    id_spec_cmp) != NULL) {
1485 			error = 0;
1486 			break;
1487 		}
1488 	}
1489 	prison_unlock(pr);
1490 	return (error);
1491 }
1492 
1493 static struct mac_policy_ops do_ops = {
1494 	.mpo_destroy = mac_do_destroy,
1495 	.mpo_init = mac_do_init,
1496 	.mpo_cred_check_setuid = mac_do_check_setuid,
1497 	.mpo_cred_check_setgroups = mac_do_check_setgroups,
1498 	.mpo_priv_grant = mac_do_priv_grant,
1499 };
1500 
1501 MAC_POLICY_SET(&do_ops, mac_do, "MAC/do",
1502    MPC_LOADTIME_FLAG_UNLOADOK, NULL);
1503 MODULE_VERSION(mac_do, 1);
1504