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