xref: /freebsd/sys/security/mac_do/mac_do.c (revision 65766063f85d8b8fe8b24a50250a12a122974c26)
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		mac_do_osd_jail_slot;
40 
41 #define RULE_INVALID	0 /* Must stay 0. */
42 #define RULE_UID	1
43 #define RULE_GID	2
44 #define RULE_ANY	3
45 
46 static const char *id_type_to_str[] = {
47 	[RULE_INVALID]	= "invalid",
48 	[RULE_UID]	= "uid",
49 	[RULE_GID]	= "gid",
50 	/* See also parse_id_type(). */
51 	[RULE_ANY]	= "*",
52 	NULL
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 struct rule {
64 	u_int	from_type;
65 	u_int	from_id;
66 	u_int	to_type;
67 	u_int	to_id;
68 	TAILQ_ENTRY(rule) r_entries;
69 };
70 
71 struct rules {
72 	char string[MAC_RULE_STRING_LEN];
73 	TAILQ_HEAD(rulehead, rule) head;
74 };
75 
76 static void
77 toast_rules(struct rules *const rules)
78 {
79 	struct rulehead *const head = &rules->head;
80 	struct rule *rule;
81 
82 	while ((rule = TAILQ_FIRST(head)) != NULL) {
83 		TAILQ_REMOVE(head, rule, r_entries);
84 		free(rule, M_DO);
85 	}
86 	free(rules, M_DO);
87 }
88 
89 static struct rules *
90 alloc_rules(void)
91 {
92 	struct rules *const rules = malloc(sizeof(*rules), M_DO, M_WAITOK);
93 
94 	_Static_assert(MAC_RULE_STRING_LEN > 0, "MAC_RULE_STRING_LEN <= 0!");
95 	rules->string[0] = 0;
96 	TAILQ_INIT(&rules->head);
97 	return (rules);
98 }
99 
100 /*
101  * String to unsigned int.
102  *
103  * Contrary to the "standard" strtou*() family of functions, do not tolerate
104  * spaces at start nor an empty string, and returns a status code, the 'u_int'
105  * result being returned through a passed pointer (if no error).
106  *
107  * We detour through 'quad_t' because in-kernel strto*() functions cannot set
108  * 'errno' and thus can't distinguish a true maximum value from one returned
109  * because of overflow.  We use 'quad_t' instead of 'u_quad_t' to support
110  * negative specifications (e.g., such as "-1" for UINT_MAX).
111  */
112 static int
113 strtoui_strict(const char *const restrict s, const char **const restrict endptr,
114     int base, u_int *result)
115 {
116 	char *ep;
117 	quad_t q;
118 
119 	/* Rule out spaces and empty specifications. */
120 	if (s[0] == '\0' || isspace(s[0])) {
121 		if (endptr != NULL)
122 			*endptr = s;
123 		return (EINVAL);
124 	}
125 
126 	q = strtoq(s, &ep, base);
127 	if (endptr != NULL)
128 		*endptr = ep;
129 	if (q < 0) {
130 		/* We allow specifying a negative number. */
131 		if (q < -(quad_t)UINT_MAX - 1 || q == QUAD_MIN)
132 			return (EOVERFLOW);
133 	} else {
134 		if (q > UINT_MAX || q == UQUAD_MAX)
135 			return (EOVERFLOW);
136 	}
137 
138 	*result = (u_int)q;
139 	return (0);
140 }
141 
142 static int
143 parse_id_type(const char *const string, int *const type)
144 {
145 	/*
146 	 * Special case for "any", as the canonical form for RULE_ANY in
147 	 * id_type_to_str[] is "*".
148 	 */
149 	if (strcmp(string, "any") == 0) {
150 		*type = RULE_ANY;
151 		return (0);
152 	}
153 
154 	/* Start at 1 to avoid parsing "invalid". */
155 	for (size_t i = 1; id_type_to_str[i] != NULL; ++i) {
156 		if (strcmp(string, id_type_to_str[i]) == 0) {
157 			*type = i;
158 			return (0);
159 		}
160 	}
161 
162 	*type = RULE_INVALID;
163 	return (EINVAL);
164 }
165 
166 static int
167 parse_rule_element(char *element, struct rule **rule)
168 {
169 	const char *from_type, *from_id, *to, *p;
170 	struct rule *new;
171 	int error;
172 
173 	new = malloc(sizeof(*new), M_DO, M_ZERO|M_WAITOK);
174 
175 	from_type = strsep(&element, "=");
176 	if (from_type == NULL)
177 		goto einval;
178 
179 	error = parse_id_type(from_type, &new->from_type);
180 	if (error != 0)
181 		goto einval;
182 	switch (new->from_type) {
183 	case RULE_UID:
184 	case RULE_GID:
185 		break;
186 	default:
187 		goto einval;
188 	}
189 
190 	from_id = strsep(&element, ":");
191 	if (from_id == NULL || *from_id == '\0')
192 		goto einval;
193 
194 	error = strtoui_strict(from_id, &p, 10, &new->from_id);
195 	if (error != 0 || *p != '\0')
196 		goto einval;
197 
198 	to = element;
199 	if (to == NULL || *to == '\0')
200 		goto einval;
201 
202 	if (strcmp(to, "any") == 0 || strcmp(to, "*") == 0)
203 		new->to_type = RULE_ANY;
204 	else {
205 		new->to_type = RULE_UID;
206 		error = strtoui_strict(to, &p, 10, &new->to_id);
207 		if (error != 0 || *p != '\0')
208 			goto einval;
209 	}
210 
211 	*rule = new;
212 	return (0);
213 einval:
214 	free(new, M_DO);
215 	*rule = NULL;
216 	return (EINVAL);
217 }
218 
219 /*
220  * Parse rules specification and produce rule structures out of it.
221  *
222  * Returns 0 on success, with '*rulesp' made to point to a 'struct rule'
223  * representing the rules.  On error, the returned value is non-zero and
224  * '*rulesp' is unchanged.  If 'string' has length greater or equal to
225  * MAC_RULE_STRING_LEN, ENAMETOOLONG is returned.  If it is not in the expected
226  * format (comma-separated list of clauses of the form "<type>=<val>:<target>",
227  * where <type> is "uid" or "gid", <val> an UID or GID (depending on <type>) and
228  * <target> is "*", "any" or some UID), EINVAL is returned.
229  */
230 static int
231 parse_rules(const char *const string, struct rules **const rulesp)
232 {
233 	const size_t len = strlen(string);
234 	char *copy;
235 	char *p;
236 	char *element;
237 	struct rules *rules;
238 	struct rule *new;
239 	int error = 0;
240 
241 	if (len >= MAC_RULE_STRING_LEN)
242 		return (ENAMETOOLONG);
243 
244 	rules = alloc_rules();
245 	bcopy(string, rules->string, len + 1);
246 	MPASS(rules->string[len] == '\0'); /* Catch some races. */
247 
248 	copy = malloc(len + 1, M_DO, M_WAITOK);
249 	bcopy(string, copy, len + 1);
250 	MPASS(copy[len] == '\0'); /* Catch some races. */
251 
252 	p = copy;
253 	while ((element = strsep(&p, ",")) != NULL) {
254 		if (element[0] == '\0')
255 			continue;
256 		error = parse_rule_element(element, &new);
257 		if (error != 0) {
258 			toast_rules(rules);
259 			goto out;
260 		}
261 		TAILQ_INSERT_TAIL(&rules->head, new, r_entries);
262 	}
263 
264 	*rulesp = rules;
265 out:
266 	free(copy, M_DO);
267 	return (error);
268 }
269 
270 /*
271  * Find rules applicable to the passed prison.
272  *
273  * Returns the applicable rules (and never NULL).  'pr' must be unlocked.
274  * 'aprp' is set to the (ancestor) prison holding these, and it must be unlocked
275  * once the caller is done accessing the rules.  '*aprp' is equal to 'pr' if and
276  * only if the current jail has its own set of rules.
277  */
278 static struct rules *
279 find_rules(struct prison *const pr, struct prison **const aprp)
280 {
281 	struct prison *cpr, *ppr;
282 	struct rules *rules;
283 
284 	cpr = pr;
285 	for (;;) {
286 		prison_lock(cpr);
287 		rules = osd_jail_get(cpr, mac_do_osd_jail_slot);
288 		if (rules != NULL)
289 			break;
290 		prison_unlock(cpr);
291 
292 		ppr = cpr->pr_parent;
293 		MPASS(ppr != NULL); /* prison0 always has rules. */
294 		cpr = ppr;
295 	}
296 	*aprp = cpr;
297 
298 	return (rules);
299 }
300 
301 /*
302  * OSD destructor for slot 'mac_do_osd_jail_slot'.
303  *
304  * Called with 'value' not NULL.
305  */
306 static void
307 dealloc_osd(void *const value)
308 {
309 	struct rules *const rules = value;
310 
311 	toast_rules(rules);
312 }
313 
314 /*
315  * Remove the rules specifically associated to a prison.
316  *
317  * In practice, this means that the rules become inherited (from the closest
318  * ascendant that has some).
319  *
320  * Destroys the 'mac_do_osd_jail_slot' slot of the passed jail.
321  */
322 static void
323 remove_rules(struct prison *const pr)
324 {
325 	prison_lock(pr);
326 	/* This calls destructor dealloc_osd(). */
327 	osd_jail_del(pr, mac_do_osd_jail_slot);
328 	prison_unlock(pr);
329 }
330 
331 /*
332  * Assign already built rules to a jail.
333  */
334 static void
335 set_rules(struct prison *const pr, struct rules *const rules)
336 {
337 	struct rules *old_rules;
338 	void **rsv;
339 
340 	rsv = osd_reserve(mac_do_osd_jail_slot);
341 
342 	prison_lock(pr);
343 	old_rules = osd_jail_get(pr, mac_do_osd_jail_slot);
344 	osd_jail_set_reserved(pr, mac_do_osd_jail_slot, rsv, rules);
345 	prison_unlock(pr);
346 	if (old_rules != NULL)
347 		toast_rules(old_rules);
348 }
349 
350 /*
351  * Assigns empty rules to a jail.
352  */
353 static void
354 set_empty_rules(struct prison *const pr)
355 {
356 	struct rules *const rules = alloc_rules();
357 
358 	set_rules(pr, rules);
359 }
360 
361 /*
362  * Parse a rules specification and assign them to a jail.
363  *
364  * Returns the same error code as parse_rules() (which see).
365  */
366 static int
367 parse_and_set_rules(struct prison *const pr, const char *rules_string)
368 {
369 	struct rules *rules;
370 	int error;
371 
372 	error = parse_rules(rules_string, &rules);
373 	if (error != 0)
374 		return (error);
375 	set_rules(pr, rules);
376 	return (0);
377 }
378 
379 static int
380 mac_do_sysctl_rules(SYSCTL_HANDLER_ARGS)
381 {
382 	char *const buf = malloc(MAC_RULE_STRING_LEN, M_DO, M_WAITOK);
383 	struct prison *const td_pr = req->td->td_ucred->cr_prison;
384 	struct prison *pr;
385 	struct rules *rules;
386 	int error;
387 
388 	rules = find_rules(td_pr, &pr);
389 	strlcpy(buf, rules->string, MAC_RULE_STRING_LEN);
390 	prison_unlock(pr);
391 
392 	error = sysctl_handle_string(oidp, buf, MAC_RULE_STRING_LEN, req);
393 	if (error != 0 || req->newptr == NULL)
394 		goto out;
395 
396 	/* Set our prison's rules, not that of the jail we inherited from. */
397 	error = parse_and_set_rules(td_pr, buf);
398 out:
399 	free(buf, M_DO);
400 	return (error);
401 }
402 
403 SYSCTL_PROC(_security_mac_do, OID_AUTO, rules,
404     CTLTYPE_STRING|CTLFLAG_RW|CTLFLAG_PRISON|CTLFLAG_MPSAFE,
405     0, 0, mac_do_sysctl_rules, "A",
406     "Rules");
407 
408 
409 SYSCTL_JAIL_PARAM_SYS_SUBNODE(mac, do, CTLFLAG_RW, "Jail MAC/do parameters");
410 SYSCTL_JAIL_PARAM_STRING(_mac_do, rules, CTLFLAG_RW, MAC_RULE_STRING_LEN,
411     "Jail MAC/do rules");
412 
413 
414 static int
415 mac_do_jail_create(void *obj, void *data __unused)
416 {
417 	struct prison *const pr = obj;
418 
419 	set_empty_rules(pr);
420 	return (0);
421 }
422 
423 static int
424 mac_do_jail_get(void *obj, void *data)
425 {
426 	struct prison *ppr, *const pr = obj;
427 	struct vfsoptlist *const opts = data;
428 	struct rules *rules;
429 	int jsys, error;
430 
431 	rules = find_rules(pr, &ppr);
432 
433 	jsys = pr == ppr ?
434 	    (TAILQ_EMPTY(&rules->head) ? JAIL_SYS_DISABLE : JAIL_SYS_NEW) :
435 	    JAIL_SYS_INHERIT;
436 	error = vfs_setopt(opts, "mac.do", &jsys, sizeof(jsys));
437 	if (error != 0 && error != ENOENT)
438 		goto done;
439 
440 	error = vfs_setopts(opts, "mac.do.rules", rules->string);
441 	if (error != 0 && error != ENOENT)
442 		goto done;
443 
444 	error = 0;
445 done:
446 	prison_unlock(ppr);
447 	return (error);
448 }
449 
450 /*
451  * -1 is used as a sentinel in mac_do_jail_check() and mac_do_jail_set() below.
452  */
453 _Static_assert(-1 != JAIL_SYS_DISABLE && -1 != JAIL_SYS_NEW &&
454     -1 != JAIL_SYS_INHERIT,
455     "mac_do(4) uses -1 as a sentinel for uninitialized 'jsys'.");
456 
457 /*
458  * We perform only cheap checks here, i.e., we do not really parse the rules
459  * specification string, if any.
460  */
461 static int
462 mac_do_jail_check(void *obj, void *data)
463 {
464 	struct vfsoptlist *opts = data;
465 	char *rules_string;
466 	int error, jsys, size;
467 
468 	error = vfs_copyopt(opts, "mac.do", &jsys, sizeof(jsys));
469 	if (error == ENOENT)
470 		jsys = -1;
471 	else {
472 		if (error != 0)
473 			return (error);
474 		if (jsys != JAIL_SYS_DISABLE && jsys != JAIL_SYS_NEW &&
475 		    jsys != JAIL_SYS_INHERIT)
476 			return (EINVAL);
477 	}
478 
479 	/*
480 	 * We use vfs_getopt() here instead of vfs_getopts() to get the length.
481 	 * We perform the additional checks done by the latter here, even if
482 	 * jail_set() calls vfs_getopts() itself later (they becoming
483 	 * inconsistent wouldn't cause any security problem).
484 	 */
485 	error = vfs_getopt(opts, "mac.do.rules", (void**)&rules_string, &size);
486 	if (error == ENOENT) {
487 		/*
488 		 * Default (in absence of "mac.do.rules") is to disable (and, in
489 		 * particular, not inherit).
490 		 */
491 		if (jsys == -1)
492 			jsys = JAIL_SYS_DISABLE;
493 
494 		if (jsys == JAIL_SYS_NEW) {
495 			vfs_opterror(opts, "'mac.do.rules' must be specified "
496 			    "given 'mac.do''s value");
497 			return (EINVAL);
498 		}
499 
500 		/* Absence of "mac.do.rules" at this point is OK. */
501 		error = 0;
502 	} else {
503 		if (error != 0)
504 			return (error);
505 
506 		/* Not a proper string. */
507 		if (size == 0 || rules_string[size - 1] != '\0') {
508 			vfs_opterror(opts, "'mac.do.rules' not a proper string");
509 			return (EINVAL);
510 		}
511 
512 		if (size > MAC_RULE_STRING_LEN) {
513 			vfs_opterror(opts, "'mdo.rules' too long");
514 			return (ENAMETOOLONG);
515 		}
516 
517 		if (jsys == -1)
518 			/* Default (if "mac.do.rules" is present). */
519 			jsys = rules_string[0] == '\0' ? JAIL_SYS_DISABLE :
520 			    JAIL_SYS_NEW;
521 
522 		/*
523 		 * Be liberal and accept JAIL_SYS_DISABLE and JAIL_SYS_INHERIT
524 		 * with an explicit empty rules specification.
525 		 */
526 		switch (jsys) {
527 		case JAIL_SYS_DISABLE:
528 		case JAIL_SYS_INHERIT:
529 			if (rules_string[0] != '\0') {
530 				vfs_opterror(opts, "'mac.do.rules' specified "
531 				    "but should not given 'mac.do''s value");
532 				return (EINVAL);
533 			}
534 			break;
535 		}
536 	}
537 
538 	return (error);
539 }
540 
541 static int
542 mac_do_jail_set(void *obj, void *data)
543 {
544 	struct prison *pr = obj;
545 	struct vfsoptlist *opts = data;
546 	char *rules_string;
547 	int error, jsys;
548 
549 	/*
550 	 * The invariants checks used below correspond to what has already been
551 	 * checked in jail_check() above.
552 	 */
553 
554 	error = vfs_copyopt(opts, "mac.do", &jsys, sizeof(jsys));
555 	MPASS(error == 0 || error == ENOENT);
556 	if (error != 0)
557 		jsys = -1; /* Mark unfilled. */
558 
559 	rules_string = vfs_getopts(opts, "mac.do.rules", &error);
560 	MPASS(error == 0 || error == ENOENT);
561 	if (error == 0) {
562 		MPASS(strlen(rules_string) < MAC_RULE_STRING_LEN);
563 		if (jsys == -1)
564 			/* Default (if "mac.do.rules" is present). */
565 			jsys = rules_string[0] == '\0' ? JAIL_SYS_DISABLE :
566 			    JAIL_SYS_NEW;
567 		else
568 			MPASS(jsys == JAIL_SYS_NEW ||
569 			    ((jsys == JAIL_SYS_DISABLE ||
570 			    jsys == JAIL_SYS_INHERIT) &&
571 			    rules_string[0] == '\0'));
572 	} else {
573 		MPASS(jsys != JAIL_SYS_NEW);
574 		if (jsys == -1)
575 			/*
576 			 * Default (in absence of "mac.do.rules") is to disable
577 			 * (and, in particular, not inherit).
578 			 */
579 			jsys = JAIL_SYS_DISABLE;
580 		/* If disabled, we'll store an empty rule specification. */
581 		if (jsys == JAIL_SYS_DISABLE)
582 			rules_string = "";
583 	}
584 
585 	switch (jsys) {
586 	case JAIL_SYS_INHERIT:
587 		remove_rules(pr);
588 		error = 0;
589 		break;
590 	case JAIL_SYS_DISABLE:
591 	case JAIL_SYS_NEW:
592 		error = parse_and_set_rules(pr, rules_string);
593 		break;
594 	default:
595 		__assert_unreachable();
596 	}
597 	return (error);
598 }
599 
600 /*
601  * OSD jail methods.
602  *
603  * There is no PR_METHOD_REMOVE, as OSD storage is destroyed by the common jail
604  * code (see prison_cleanup()), which triggers a run of our dealloc_osd()
605  * destructor.
606  */
607 static const osd_method_t osd_methods[PR_MAXMETHOD] = {
608 	[PR_METHOD_CREATE] = mac_do_jail_create,
609 	[PR_METHOD_GET] = mac_do_jail_get,
610 	[PR_METHOD_CHECK] = mac_do_jail_check,
611 	[PR_METHOD_SET] = mac_do_jail_set,
612 };
613 
614 
615 static void
616 mac_do_init(struct mac_policy_conf *mpc)
617 {
618 	struct prison *pr;
619 
620 	mac_do_osd_jail_slot = osd_jail_register(dealloc_osd, osd_methods);
621 	set_empty_rules(&prison0);
622 	sx_slock(&allprison_lock);
623 	TAILQ_FOREACH(pr, &allprison, pr_list)
624 	    set_empty_rules(pr);
625 	sx_sunlock(&allprison_lock);
626 }
627 
628 static void
629 mac_do_destroy(struct mac_policy_conf *mpc)
630 {
631 	osd_jail_deregister(mac_do_osd_jail_slot);
632 }
633 
634 static bool
635 rule_applies(struct ucred *cred, struct rule *r)
636 {
637 	if (r->from_type == RULE_UID && r->from_id == cred->cr_uid)
638 		return (true);
639 	if (r->from_type == RULE_GID && groupmember(r->from_id, cred))
640 		return (true);
641 	return (false);
642 }
643 
644 static int
645 mac_do_priv_grant(struct ucred *cred, int priv)
646 {
647 	struct rule *r;
648 	struct prison *pr;
649 	struct rules *rule;
650 
651 	if (do_enabled == 0)
652 		return (EPERM);
653 
654 	rule = find_rules(cred->cr_prison, &pr);
655 	TAILQ_FOREACH(r, &rule->head, r_entries) {
656 		if (rule_applies(cred, r)) {
657 			switch (priv) {
658 			case PRIV_CRED_SETGROUPS:
659 			case PRIV_CRED_SETUID:
660 				prison_unlock(pr);
661 				return (0);
662 			default:
663 				break;
664 			}
665 		}
666 	}
667 	prison_unlock(pr);
668 	return (EPERM);
669 }
670 
671 static int
672 mac_do_check_setgroups(struct ucred *cred, int ngrp, gid_t *groups)
673 {
674 	struct rule *r;
675 	char *fullpath = NULL;
676 	char *freebuf = NULL;
677 	struct prison *pr;
678 	struct rules *rule;
679 
680 	if (do_enabled == 0)
681 		return (0);
682 	if (cred->cr_uid == 0)
683 		return (0);
684 
685 	if (vn_fullpath(curproc->p_textvp, &fullpath, &freebuf) != 0)
686 		return (EPERM);
687 	if (strcmp(fullpath, "/usr/bin/mdo") != 0) {
688 		free(freebuf, M_TEMP);
689 		return (EPERM);
690 	}
691 	free(freebuf, M_TEMP);
692 
693 	rule = find_rules(cred->cr_prison, &pr);
694 	TAILQ_FOREACH(r, &rule->head, r_entries) {
695 		if (rule_applies(cred, r)) {
696 			prison_unlock(pr);
697 			return (0);
698 		}
699 	}
700 	prison_unlock(pr);
701 
702 	return (EPERM);
703 }
704 
705 static int
706 mac_do_check_setuid(struct ucred *cred, uid_t uid)
707 {
708 	struct rule *r;
709 	int error;
710 	char *fullpath = NULL;
711 	char *freebuf = NULL;
712 	struct prison *pr;
713 	struct rules *rule;
714 
715 	if (do_enabled == 0)
716 		return (0);
717 	if (cred->cr_uid == uid || cred->cr_uid == 0 || cred->cr_ruid == 0)
718 		return (0);
719 
720 	if (vn_fullpath(curproc->p_textvp, &fullpath, &freebuf) != 0)
721 		return (EPERM);
722 	if (strcmp(fullpath, "/usr/bin/mdo") != 0) {
723 		free(freebuf, M_TEMP);
724 		return (EPERM);
725 	}
726 	free(freebuf, M_TEMP);
727 
728 	error = EPERM;
729 	rule = find_rules(cred->cr_prison, &pr);
730 	TAILQ_FOREACH(r, &rule->head, r_entries) {
731 		if (r->from_type == RULE_UID) {
732 			if (cred->cr_uid != r->from_id)
733 				continue;
734 			if (r->to_type == RULE_ANY) {
735 				error = 0;
736 				break;
737 			}
738 			if (r->to_type == RULE_UID && uid == r->to_id) {
739 				error = 0;
740 				break;
741 			}
742 		}
743 		if (r->from_type == RULE_GID) {
744 			if (!groupmember(r->from_id, cred))
745 				continue;
746 			if (r->to_type == RULE_ANY) {
747 				error = 0;
748 				break;
749 			}
750 			if (r->to_type == RULE_UID && uid == r->to_id) {
751 				error = 0;
752 				break;
753 			}
754 		}
755 	}
756 	prison_unlock(pr);
757 	return (error);
758 }
759 
760 static struct mac_policy_ops do_ops = {
761 	.mpo_destroy = mac_do_destroy,
762 	.mpo_init = mac_do_init,
763 	.mpo_cred_check_setuid = mac_do_check_setuid,
764 	.mpo_cred_check_setgroups = mac_do_check_setgroups,
765 	.mpo_priv_grant = mac_do_priv_grant,
766 };
767 
768 MAC_POLICY_SET(&do_ops, mac_do, "MAC/do",
769    MPC_LOADTIME_FLAG_UNLOADOK, NULL);
770 MODULE_VERSION(mac_do, 1);
771