xref: /freebsd-src/sys/security/mac_do/mac_do.c (revision f1ddb6fb8c4d051a205dae3a848776c9d56f86ff)
18aac90f1SBaptiste Daroussin /*-
28aac90f1SBaptiste Daroussin  * SPDX-License-Identifier: BSD-2-Clause
3e94684b3SOlivier Certner  *
4e94684b3SOlivier Certner  * Copyright(c) 2024 Baptiste Daroussin <bapt@FreeBSD.org>
5e94684b3SOlivier Certner  * Copyright (c) 2024 The FreeBSD Foundation
6e94684b3SOlivier Certner  *
7e94684b3SOlivier Certner  * Portions of this software were developed by Olivier Certner
8e94684b3SOlivier Certner  * <olce.freebsd@certner.fr> at Kumacom SARL under sponsorship from the FreeBSD
9e94684b3SOlivier Certner  * Foundation.
108aac90f1SBaptiste Daroussin  */
118aac90f1SBaptiste Daroussin 
128aac90f1SBaptiste Daroussin #include <sys/param.h>
13f0600c41SOlivier Certner #include <sys/systm.h>
140af43c02SOlivier Certner #include <sys/ctype.h>
158aac90f1SBaptiste Daroussin #include <sys/jail.h>
168aac90f1SBaptiste Daroussin #include <sys/kernel.h>
170af43c02SOlivier Certner #include <sys/limits.h>
188aac90f1SBaptiste Daroussin #include <sys/lock.h>
19f0600c41SOlivier Certner #include <sys/malloc.h>
208aac90f1SBaptiste Daroussin #include <sys/module.h>
218aac90f1SBaptiste Daroussin #include <sys/mount.h>
228aac90f1SBaptiste Daroussin #include <sys/mutex.h>
238aac90f1SBaptiste Daroussin #include <sys/priv.h>
248aac90f1SBaptiste Daroussin #include <sys/proc.h>
253d8d91a5SOlivier Certner #include <sys/refcount.h>
268aac90f1SBaptiste Daroussin #include <sys/socket.h>
278aac90f1SBaptiste Daroussin #include <sys/sx.h>
288aac90f1SBaptiste Daroussin #include <sys/sysctl.h>
298aac90f1SBaptiste Daroussin #include <sys/ucred.h>
308aac90f1SBaptiste Daroussin #include <sys/vnode.h>
318aac90f1SBaptiste Daroussin 
3287c06b7dSOlivier Certner #include <machine/stdarg.h>
3387c06b7dSOlivier Certner 
348aac90f1SBaptiste Daroussin #include <security/mac/mac_policy.h>
358aac90f1SBaptiste Daroussin 
368aac90f1SBaptiste Daroussin static SYSCTL_NODE(_security_mac, OID_AUTO, do,
378aac90f1SBaptiste Daroussin     CTLFLAG_RW|CTLFLAG_MPSAFE, 0, "mac_do policy controls");
388aac90f1SBaptiste Daroussin 
398aac90f1SBaptiste Daroussin static int	do_enabled = 1;
408aac90f1SBaptiste Daroussin SYSCTL_INT(_security_mac_do, OID_AUTO, enabled, CTLFLAG_RWTUN,
418aac90f1SBaptiste Daroussin     &do_enabled, 0, "Enforce do policy");
428aac90f1SBaptiste Daroussin 
4387c06b7dSOlivier Certner static int	print_parse_error = 1;
4487c06b7dSOlivier Certner SYSCTL_INT(_security_mac_do, OID_AUTO, print_parse_error, CTLFLAG_RWTUN,
4587c06b7dSOlivier Certner     &print_parse_error, 0, "Print parse errors on setting rules "
4687c06b7dSOlivier Certner     "(via sysctl(8)).");
4787c06b7dSOlivier Certner 
488aac90f1SBaptiste Daroussin static MALLOC_DEFINE(M_DO, "do_rule", "Rules for mac_do");
498aac90f1SBaptiste Daroussin 
508aac90f1SBaptiste Daroussin #define MAC_RULE_STRING_LEN	1024
518aac90f1SBaptiste Daroussin 
5240a664a4SOlivier Certner static unsigned		osd_jail_slot;
538f7e8726SOlivier Certner static unsigned		osd_thread_slot;
548aac90f1SBaptiste Daroussin 
556c3def74SOlivier Certner #define IT_INVALID	0 /* Must stay 0. */
566c3def74SOlivier Certner #define IT_UID		1
576c3def74SOlivier Certner #define IT_GID		2
586c3def74SOlivier Certner #define IT_ANY		3
596c3def74SOlivier Certner #define IT_LAST		IT_ANY
608aac90f1SBaptiste Daroussin 
6165766063SOlivier Certner static const char *id_type_to_str[] = {
626c3def74SOlivier Certner 	[IT_INVALID]	= "invalid",
636c3def74SOlivier Certner 	[IT_UID]	= "uid",
646c3def74SOlivier Certner 	[IT_GID]	= "gid",
6565766063SOlivier Certner 	/* See also parse_id_type(). */
666c3def74SOlivier Certner 	[IT_ANY]	= "*",
6765766063SOlivier Certner };
6865766063SOlivier Certner 
6987c06b7dSOlivier Certner #define PARSE_ERROR_SIZE	256
7087c06b7dSOlivier Certner 
7187c06b7dSOlivier Certner struct parse_error {
7287c06b7dSOlivier Certner 	size_t	pos;
7387c06b7dSOlivier Certner 	char	msg[PARSE_ERROR_SIZE];
7487c06b7dSOlivier Certner };
7587c06b7dSOlivier Certner 
766aadc7b2SOlivier Certner /*
776aadc7b2SOlivier Certner  * We assume that 'uid_t' and 'gid_t' are aliases to 'u_int' in conversions
786aadc7b2SOlivier Certner  * required for parsing rules specification strings.
796aadc7b2SOlivier Certner  */
806aadc7b2SOlivier Certner _Static_assert(sizeof(uid_t) == sizeof(u_int) && (uid_t)-1 >= 0 &&
816aadc7b2SOlivier Certner     sizeof(gid_t) == sizeof(u_int) && (gid_t)-1 >= 0,
826aadc7b2SOlivier Certner     "mac_do(4) assumes that 'uid_t' and 'gid_t' are aliases to 'u_int'");
836aadc7b2SOlivier Certner 
846c3def74SOlivier Certner /*
856c3def74SOlivier Certner  * Internal flags.
866c3def74SOlivier Certner  *
876c3def74SOlivier Certner  * They either apply as per-type (t) or per-ID (i) but are conflated because all
886c3def74SOlivier Certner  * per-ID flags are also valid as per-type ones to qualify the "current" (".")
896c3def74SOlivier Certner  * per-type flag.  Also, some of them are in fact exclusive, but we use one-hot
906c3def74SOlivier Certner  * encoding for simplicity.
916c3def74SOlivier Certner  *
926c3def74SOlivier Certner  * There is currently room for "only" 16 bits.  As these flags are purely
936c3def74SOlivier Certner  * internal, they can be renumbered and/or their type changed as needed.
946c3def74SOlivier Certner  *
956c3def74SOlivier Certner  * See also the check_*() functions below.
966c3def74SOlivier Certner  */
976c3def74SOlivier Certner typedef uint16_t	flags_t;
986c3def74SOlivier Certner 
996c3def74SOlivier Certner /* (i,gid) Specification concerns primary groups. */
1006c3def74SOlivier Certner #define MDF_PRIMARY	(1u << 0)
1016c3def74SOlivier Certner /* (i,gid) Specification concerns supplementary groups. */
1026c3def74SOlivier Certner #define MDF_SUPP_ALLOW	(1u << 1)
1036c3def74SOlivier Certner /* (i,gid) Group must appear as a supplementary group. */
1046c3def74SOlivier Certner #define MDF_SUPP_MUST	(1u << 2)
1056c3def74SOlivier Certner /* (i,gid) Group must not appear as a supplementary group. */
1066c3def74SOlivier Certner #define MDF_SUPP_DONT	(1u << 3)
1076c3def74SOlivier Certner #define MDF_SUPP_MASK	(MDF_SUPP_ALLOW | MDF_SUPP_MUST | MDF_SUPP_DONT)
1086c3def74SOlivier Certner #define MDF_ID_MASK	(MDF_PRIMARY | MDF_SUPP_MASK)
1096c3def74SOlivier Certner 
1106c3def74SOlivier Certner /*
1116c3def74SOlivier Certner  * (t) All IDs allowed.
1126c3def74SOlivier Certner  *
1136c3def74SOlivier Certner  * For GIDs, MDF_ANY only concerns primary groups.  The MDF_PRIMARY and
1146c3def74SOlivier Certner  * MDF_SUPP_* flags never apply to MDF_ANY, but can be present if MDF_CURRENT is
1156c3def74SOlivier Certner  * present also, as usual.
1166c3def74SOlivier Certner  */
1176c3def74SOlivier Certner #define MDF_ANY			(1u << 8)
1186c3def74SOlivier Certner /* (t) Current IDs allowed. */
1196c3def74SOlivier Certner #define MDF_CURRENT		(1u << 9)
1206c3def74SOlivier Certner #define MDF_TYPE_COMMON_MASK	(MDF_ANY | MDF_CURRENT)
1216c3def74SOlivier Certner /* (t,gid) All IDs allowed as supplementary groups. */
1226c3def74SOlivier Certner #define MDF_ANY_SUPP		(1u << 10)
1236c3def74SOlivier Certner /* (t,gid) Some ID or MDF_CURRENT has MDF_SUPP_MUST or MDF_SUPP_DONT. */
1246c3def74SOlivier Certner #define MDF_MAY_REJ_SUPP	(1u << 11)
1256c3def74SOlivier Certner /* (t,gid) Some explicit ID (not MDF_CURRENT) has MDF_SUPP_MUST. */
1266c3def74SOlivier Certner #define MDF_EXPLICIT_SUPP_MUST	(1u << 12)
1276c3def74SOlivier Certner /* (t,gid) Whether any target clause is about primary groups.  Used during
1286c3def74SOlivier Certner  * parsing only. */
1296c3def74SOlivier Certner #define MDF_HAS_PRIMARY_CLAUSE	(1u << 13)
1306c3def74SOlivier Certner /* (t,gid) Whether any target clause is about supplementary groups.  Used during
1316c3def74SOlivier Certner  * parsing only. */
1326c3def74SOlivier Certner #define MDF_HAS_SUPP_CLAUSE	(1u << 14)
1336c3def74SOlivier Certner #define MDF_TYPE_GID_MASK	(MDF_ANY_SUPP | MDF_MAY_REJ_SUPP |	\
1346c3def74SOlivier Certner     MDF_EXPLICIT_SUPP_MUST | MDF_HAS_PRIMARY_CLAUSE | MDF_HAS_SUPP_CLAUSE)
1356c3def74SOlivier Certner #define MDF_TYPE_MASK		(MDF_TYPE_COMMON_MASK | MDF_TYPE_GID_MASK)
1366c3def74SOlivier Certner 
1376c3def74SOlivier Certner /*
1386c3def74SOlivier Certner  * Persistent structures.
1396c3def74SOlivier Certner  */
1406c3def74SOlivier Certner 
1416c3def74SOlivier Certner struct id_spec {
1426c3def74SOlivier Certner 	u_int		 id;
1436c3def74SOlivier Certner 	flags_t		 flags; /* See MDF_* above. */
1448aac90f1SBaptiste Daroussin };
1458aac90f1SBaptiste Daroussin 
1466c3def74SOlivier Certner /*
1476c3def74SOlivier Certner  * This limits the number of target clauses per type to 65535.  With the current
1486c3def74SOlivier Certner  * value of MAC_RULE_STRING_LEN (1024), this is way more than enough anyway.
1496c3def74SOlivier Certner  */
1506c3def74SOlivier Certner typedef uint16_t	 id_nb_t;
1516c3def74SOlivier Certner /* We only have a few IT_* types. */
1526c3def74SOlivier Certner typedef uint16_t	 id_type_t;
1536c3def74SOlivier Certner 
1546c3def74SOlivier Certner struct rule {
155c7fc71c6SOlivier Certner 	STAILQ_ENTRY(rule) r_entries;
1566c3def74SOlivier Certner 	id_type_t	 from_type;
1576c3def74SOlivier Certner 	u_int		 from_id;
1586c3def74SOlivier Certner 	flags_t		 uid_flags; /* See MDF_* above. */
1596c3def74SOlivier Certner 	id_nb_t		 uids_nb;
1606c3def74SOlivier Certner 	flags_t		 gid_flags; /* See MDF_* above. */
1616c3def74SOlivier Certner 	id_nb_t		 gids_nb;
1626c3def74SOlivier Certner 	struct id_spec	*uids;
1636c3def74SOlivier Certner 	struct id_spec	*gids;
1646c3def74SOlivier Certner };
1656c3def74SOlivier Certner 
166c7fc71c6SOlivier Certner STAILQ_HEAD(rulehead, rule);
1676c3def74SOlivier Certner 
16802ed945cSOlivier Certner struct rules {
1698aac90f1SBaptiste Daroussin 	char		string[MAC_RULE_STRING_LEN];
1706c3def74SOlivier Certner 	struct rulehead	head;
1713d8d91a5SOlivier Certner 	volatile u_int	use_count __aligned(CACHE_LINE_SIZE);
1728aac90f1SBaptiste Daroussin };
1738aac90f1SBaptiste Daroussin 
1746c3def74SOlivier Certner /*
1756c3def74SOlivier Certner  * Temporary structures used to build a 'struct rule' above.
1766c3def74SOlivier Certner  */
1776c3def74SOlivier Certner 
1786c3def74SOlivier Certner struct id_elem {
179c7fc71c6SOlivier Certner 	STAILQ_ENTRY(id_elem) ie_entries;
1806c3def74SOlivier Certner 	struct id_spec spec;
1816c3def74SOlivier Certner };
1826c3def74SOlivier Certner 
183c7fc71c6SOlivier Certner STAILQ_HEAD(id_list, id_elem);
1846c3def74SOlivier Certner 
1856c3def74SOlivier Certner #ifdef INVARIANTS
1866c3def74SOlivier Certner static void
1876c3def74SOlivier Certner check_type(const id_type_t type)
1886c3def74SOlivier Certner {
1896c3def74SOlivier Certner 	if (type > IT_LAST)
1906c3def74SOlivier Certner 		panic("Invalid type number %u", type);
1916c3def74SOlivier Certner }
1926c3def74SOlivier Certner 
1936c3def74SOlivier Certner static void
1946c3def74SOlivier Certner panic_for_unexpected_flags(const id_type_t type, const flags_t flags,
1956c3def74SOlivier Certner     const char *const str)
1966c3def74SOlivier Certner {
1976c3def74SOlivier Certner 	panic("ID type %s: Unexpected flags %u (%s), ", id_type_to_str[type],
1986c3def74SOlivier Certner 	    flags, str);
1996c3def74SOlivier Certner }
2006c3def74SOlivier Certner 
2016c3def74SOlivier Certner static void
2026c3def74SOlivier Certner check_type_and_id_flags(const id_type_t type, const flags_t flags)
2036c3def74SOlivier Certner {
2046c3def74SOlivier Certner 	const char *str;
2056c3def74SOlivier Certner 
2066c3def74SOlivier Certner 	check_type(type);
2076c3def74SOlivier Certner 	switch (type) {
2086c3def74SOlivier Certner 	case IT_UID:
2096c3def74SOlivier Certner 		if (flags != 0) {
2106c3def74SOlivier Certner 			str = "only 0 allowed";
2116c3def74SOlivier Certner 			goto unexpected_flags;
2126c3def74SOlivier Certner 		}
2136c3def74SOlivier Certner 		break;
2146c3def74SOlivier Certner 	case IT_GID:
2156c3def74SOlivier Certner 		if ((flags & ~MDF_ID_MASK) != 0) {
2166c3def74SOlivier Certner 			str = "only bits in MDF_ID_MASK allowed";
2176c3def74SOlivier Certner 			goto unexpected_flags;
2186c3def74SOlivier Certner 		}
2196c3def74SOlivier Certner 		if (!powerof2(flags & MDF_SUPP_MASK)) {
2206c3def74SOlivier Certner 			str = "only a single flag in MDF_SUPP_MASK allowed";
2216c3def74SOlivier Certner 			goto unexpected_flags;
2226c3def74SOlivier Certner 		}
2236c3def74SOlivier Certner 		break;
2246c3def74SOlivier Certner 	default:
2256c3def74SOlivier Certner 	    __assert_unreachable();
2266c3def74SOlivier Certner 	}
2276c3def74SOlivier Certner 	return;
2286c3def74SOlivier Certner 
2296c3def74SOlivier Certner unexpected_flags:
2306c3def74SOlivier Certner 	panic_for_unexpected_flags(type, flags, str);
2316c3def74SOlivier Certner }
2326c3def74SOlivier Certner 
2336c3def74SOlivier Certner static void
2346c3def74SOlivier Certner check_type_and_id_spec(const id_type_t type, const struct id_spec *const is)
2356c3def74SOlivier Certner {
2366c3def74SOlivier Certner 	check_type_and_id_flags(type, is->flags);
2376c3def74SOlivier Certner }
2386c3def74SOlivier Certner 
2396c3def74SOlivier Certner static void
2406c3def74SOlivier Certner check_type_and_type_flags(const id_type_t type, const flags_t flags)
2416c3def74SOlivier Certner {
2426c3def74SOlivier Certner 	const char *str;
2436c3def74SOlivier Certner 
2446c3def74SOlivier Certner 	check_type_and_id_flags(type, flags & MDF_ID_MASK);
2456c3def74SOlivier Certner 	if ((flags & ~MDF_ID_MASK & ~MDF_TYPE_MASK) != 0) {
2466c3def74SOlivier Certner 		str = "only MDF_ID_MASK | MDF_TYPE_MASK bits allowed";
2476c3def74SOlivier Certner 		goto unexpected_flags;
2486c3def74SOlivier Certner 	}
2496c3def74SOlivier Certner 	if ((flags & MDF_ANY) != 0 && (flags & MDF_CURRENT) != 0 &&
2506c3def74SOlivier Certner 	    (type == IT_UID || (flags & MDF_PRIMARY) != 0)) {
2516c3def74SOlivier Certner 		str = "MDF_ANY and MDF_CURRENT are exclusive for UIDs "
2526c3def74SOlivier Certner 		    "or primary group GIDs";
2536c3def74SOlivier Certner 		goto unexpected_flags;
2546c3def74SOlivier Certner 	}
2556c3def74SOlivier Certner 	if ((flags & MDF_ANY_SUPP) != 0 && (flags & MDF_CURRENT) != 0 &&
2566c3def74SOlivier Certner 	    (flags & MDF_SUPP_MASK) != 0) {
2576c3def74SOlivier Certner 		str = "MDF_SUPP_ANY and MDF_CURRENT with supplementary "
2586c3def74SOlivier Certner 		    "groups specification are exclusive";
2596c3def74SOlivier Certner 		goto unexpected_flags;
2606c3def74SOlivier Certner 	}
2616c3def74SOlivier Certner 	if (((flags & MDF_PRIMARY) != 0 || (flags & MDF_ANY) != 0) &&
2626c3def74SOlivier Certner 	    (flags & MDF_HAS_PRIMARY_CLAUSE) == 0) {
2636c3def74SOlivier Certner 		str = "Presence of folded primary clause not reflected "
2646c3def74SOlivier Certner 		    "by presence of MDF_HAS_PRIMARY_CLAUSE";
2656c3def74SOlivier Certner 		goto unexpected_flags;
2666c3def74SOlivier Certner 	}
2676c3def74SOlivier Certner 	if (((flags & MDF_SUPP_MASK) != 0 || (flags & MDF_ANY_SUPP) != 0) &&
2686c3def74SOlivier Certner 	    (flags & MDF_HAS_SUPP_CLAUSE) == 0) {
2696c3def74SOlivier Certner 		str = "Presence of folded supplementary clause not reflected "
2706c3def74SOlivier Certner 		    "by presence of MDF_HAS_SUPP_CLAUSE";
2716c3def74SOlivier Certner 		goto unexpected_flags;
2726c3def74SOlivier Certner 	}
2736c3def74SOlivier Certner 	return;
2746c3def74SOlivier Certner 
2756c3def74SOlivier Certner unexpected_flags:
2766c3def74SOlivier Certner 	panic_for_unexpected_flags(type, flags, str);
2776c3def74SOlivier Certner }
2786c3def74SOlivier Certner #else /* !INVARIANTS */
2796c3def74SOlivier Certner #define check_type_and_id_flags(...)
2806c3def74SOlivier Certner #define check_type_and_id_spec(...)
2816c3def74SOlivier Certner #define check_type_and_type_flags(...)
2826c3def74SOlivier Certner #endif /* INVARIANTS */
2836c3def74SOlivier Certner 
2846c3def74SOlivier Certner /*
2856c3def74SOlivier Certner  * Returns EALREADY if both flags have some overlap, or EINVAL if flags are
2866c3def74SOlivier Certner  * incompatible, else 0 with flags successfully merged into 'dest'.
2876c3def74SOlivier Certner  */
2886c3def74SOlivier Certner static int
2896c3def74SOlivier Certner coalesce_id_flags(const flags_t src, flags_t *const dest)
2906c3def74SOlivier Certner {
2916c3def74SOlivier Certner 	flags_t res;
2926c3def74SOlivier Certner 
2936c3def74SOlivier Certner 	if ((src & *dest) != 0)
2946c3def74SOlivier Certner 		return (EALREADY);
2956c3def74SOlivier Certner 
2966c3def74SOlivier Certner 	res = src | *dest;
2976c3def74SOlivier Certner 
2986c3def74SOlivier Certner 	/* Check for compatibility of supplementary flags, and coalesce. */
2996c3def74SOlivier Certner 	if ((res & MDF_SUPP_MASK) != 0) {
3006c3def74SOlivier Certner 		/* MDF_SUPP_DONT incompatible with the rest. */
3016c3def74SOlivier Certner 		if ((res & MDF_SUPP_DONT) != 0 && (res & MDF_SUPP_MASK &
3026c3def74SOlivier Certner 		    ~MDF_SUPP_DONT) != 0)
3036c3def74SOlivier Certner 			return (EINVAL);
3046c3def74SOlivier Certner 		/*
3056c3def74SOlivier Certner 		 * Coalesce MDF_SUPP_ALLOW and MDF_SUPP_MUST into MDF_SUPP_MUST.
3066c3def74SOlivier Certner 		 */
3076c3def74SOlivier Certner 		if ((res & MDF_SUPP_ALLOW) != 0 && (res & MDF_SUPP_MUST) != 0)
3086c3def74SOlivier Certner 			res &= ~MDF_SUPP_ALLOW;
3096c3def74SOlivier Certner 	}
3106c3def74SOlivier Certner 
3116c3def74SOlivier Certner 	*dest = res;
3126c3def74SOlivier Certner 	return (0);
3136c3def74SOlivier Certner }
3146c3def74SOlivier Certner 
3158aac90f1SBaptiste Daroussin static void
3163186b192SOlivier Certner toast_rules(struct rules *const rules)
3178aac90f1SBaptiste Daroussin {
3183186b192SOlivier Certner 	struct rulehead *const head = &rules->head;
3192110eef4SOlivier Certner 	struct rule *rule, *rule_next;
3208aac90f1SBaptiste Daroussin 
321c7fc71c6SOlivier Certner 	STAILQ_FOREACH_SAFE(rule, head, r_entries, rule_next) {
3226c3def74SOlivier Certner 		free(rule->uids, M_DO);
3236c3def74SOlivier Certner 		free(rule->gids, M_DO);
3243186b192SOlivier Certner 		free(rule, M_DO);
3258aac90f1SBaptiste Daroussin 	}
3263186b192SOlivier Certner 	free(rules, M_DO);
3273186b192SOlivier Certner }
3283186b192SOlivier Certner 
3293186b192SOlivier Certner static struct rules *
3303186b192SOlivier Certner alloc_rules(void)
3313186b192SOlivier Certner {
3323186b192SOlivier Certner 	struct rules *const rules = malloc(sizeof(*rules), M_DO, M_WAITOK);
3333186b192SOlivier Certner 
3343186b192SOlivier Certner 	_Static_assert(MAC_RULE_STRING_LEN > 0, "MAC_RULE_STRING_LEN <= 0!");
3353186b192SOlivier Certner 	rules->string[0] = 0;
336c7fc71c6SOlivier Certner 	STAILQ_INIT(&rules->head);
3373d8d91a5SOlivier Certner 	rules->use_count = 0;
3383186b192SOlivier Certner 	return (rules);
3398aac90f1SBaptiste Daroussin }
3408aac90f1SBaptiste Daroussin 
3416c3def74SOlivier Certner static bool
3426c3def74SOlivier Certner is_null_or_empty(const char *s)
3436c3def74SOlivier Certner {
3446c3def74SOlivier Certner 	return (s == NULL || s[0] == '\0');
3456c3def74SOlivier Certner }
3466c3def74SOlivier Certner 
3470af43c02SOlivier Certner /*
3480af43c02SOlivier Certner  * String to unsigned int.
3490af43c02SOlivier Certner  *
3500af43c02SOlivier Certner  * Contrary to the "standard" strtou*() family of functions, do not tolerate
3510af43c02SOlivier Certner  * spaces at start nor an empty string, and returns a status code, the 'u_int'
3520af43c02SOlivier Certner  * result being returned through a passed pointer (if no error).
3530af43c02SOlivier Certner  *
3540af43c02SOlivier Certner  * We detour through 'quad_t' because in-kernel strto*() functions cannot set
3550af43c02SOlivier Certner  * 'errno' and thus can't distinguish a true maximum value from one returned
3560af43c02SOlivier Certner  * because of overflow.  We use 'quad_t' instead of 'u_quad_t' to support
3570af43c02SOlivier Certner  * negative specifications (e.g., such as "-1" for UINT_MAX).
3580af43c02SOlivier Certner  */
3590af43c02SOlivier Certner static int
3600af43c02SOlivier Certner strtoui_strict(const char *const restrict s, const char **const restrict endptr,
3610af43c02SOlivier Certner     int base, u_int *result)
3620af43c02SOlivier Certner {
3630af43c02SOlivier Certner 	char *ep;
3640af43c02SOlivier Certner 	quad_t q;
3650af43c02SOlivier Certner 
3660af43c02SOlivier Certner 	/* Rule out spaces and empty specifications. */
3670af43c02SOlivier Certner 	if (s[0] == '\0' || isspace(s[0])) {
3680af43c02SOlivier Certner 		if (endptr != NULL)
3690af43c02SOlivier Certner 			*endptr = s;
3700af43c02SOlivier Certner 		return (EINVAL);
3710af43c02SOlivier Certner 	}
3720af43c02SOlivier Certner 
3730af43c02SOlivier Certner 	q = strtoq(s, &ep, base);
3740af43c02SOlivier Certner 	if (endptr != NULL)
3750af43c02SOlivier Certner 		*endptr = ep;
3760af43c02SOlivier Certner 	if (q < 0) {
3770af43c02SOlivier Certner 		/* We allow specifying a negative number. */
3780af43c02SOlivier Certner 		if (q < -(quad_t)UINT_MAX - 1 || q == QUAD_MIN)
3790af43c02SOlivier Certner 			return (EOVERFLOW);
3800af43c02SOlivier Certner 	} else {
3810af43c02SOlivier Certner 		if (q > UINT_MAX || q == UQUAD_MAX)
3820af43c02SOlivier Certner 			return (EOVERFLOW);
3830af43c02SOlivier Certner 	}
3840af43c02SOlivier Certner 
3850af43c02SOlivier Certner 	*result = (u_int)q;
3860af43c02SOlivier Certner 	return (0);
3870af43c02SOlivier Certner }
3880af43c02SOlivier Certner 
3894a03b645SOlivier Certner /*
3904a03b645SOlivier Certner  * strsep() variant skipping spaces and tabs.
3914a03b645SOlivier Certner  *
3924a03b645SOlivier Certner  * Skips spaces and tabs at beginning and end of the token before one of the
3934a03b645SOlivier Certner  * 'delim' characters, i.e., at start of string and just before one of the
3944a03b645SOlivier Certner  * delimiter characters (so it doesn't prevent tokens containing spaces and tabs
3954a03b645SOlivier Certner  * in the middle).
3964a03b645SOlivier Certner  */
3974a03b645SOlivier Certner static char *
3984a03b645SOlivier Certner strsep_noblanks(char **const stringp, const char *delim)
3994a03b645SOlivier Certner {
4004a03b645SOlivier Certner 	char *p = *stringp;
4014a03b645SOlivier Certner 	char *ret, *wsp;
4024a03b645SOlivier Certner 	size_t idx;
4034a03b645SOlivier Certner 
4044a03b645SOlivier Certner 	if (p == NULL)
4054a03b645SOlivier Certner 		return (NULL);
4064a03b645SOlivier Certner 
4074a03b645SOlivier Certner 	idx = strspn(p, " \t");
4084a03b645SOlivier Certner 	p += idx;
4094a03b645SOlivier Certner 
4104a03b645SOlivier Certner 	ret = strsep(&p, delim);
4114a03b645SOlivier Certner 
4124a03b645SOlivier Certner 	/* Rewind spaces/tabs at the end. */
4134a03b645SOlivier Certner 	if (p == NULL)
4144a03b645SOlivier Certner 		wsp = ret + strlen(ret);
4154a03b645SOlivier Certner 	else
4164a03b645SOlivier Certner 		wsp = p - 1;
4174a03b645SOlivier Certner 	for (; wsp != ret; --wsp) {
4184a03b645SOlivier Certner 		switch (wsp[-1]) {
4194a03b645SOlivier Certner 		case ' ':
4204a03b645SOlivier Certner 		case '\t':
4214a03b645SOlivier Certner 			continue;
4224a03b645SOlivier Certner 		}
4234a03b645SOlivier Certner 		break;
4244a03b645SOlivier Certner 	}
4254a03b645SOlivier Certner 	*wsp = '\0';
4264a03b645SOlivier Certner 
4274a03b645SOlivier Certner 	*stringp = p;
4284a03b645SOlivier Certner 	return (ret);
4294a03b645SOlivier Certner }
4304a03b645SOlivier Certner 
43187c06b7dSOlivier Certner 
43287c06b7dSOlivier Certner static void
43387c06b7dSOlivier Certner make_parse_error(struct parse_error **const parse_error, const size_t pos,
43487c06b7dSOlivier Certner     const char *const fmt, ...)
43587c06b7dSOlivier Certner {
43687c06b7dSOlivier Certner 	struct parse_error *const err = malloc(sizeof(*err), M_DO, M_WAITOK);
43787c06b7dSOlivier Certner 	va_list ap;
43887c06b7dSOlivier Certner 
43987c06b7dSOlivier Certner 	err->pos = pos;
44087c06b7dSOlivier Certner 	va_start(ap, fmt);
44187c06b7dSOlivier Certner 	vsnprintf(err->msg, PARSE_ERROR_SIZE, fmt, ap);
44287c06b7dSOlivier Certner 	va_end(ap);
44387c06b7dSOlivier Certner 
44487c06b7dSOlivier Certner 	MPASS(*parse_error == NULL);
44587c06b7dSOlivier Certner 	*parse_error = err;
44687c06b7dSOlivier Certner }
44787c06b7dSOlivier Certner 
44887c06b7dSOlivier Certner static void
44987c06b7dSOlivier Certner free_parse_error(struct parse_error *const parse_error)
45087c06b7dSOlivier Certner {
45187c06b7dSOlivier Certner 	free(parse_error, M_DO);
45287c06b7dSOlivier Certner }
45387c06b7dSOlivier Certner 
4548aac90f1SBaptiste Daroussin static int
45587c06b7dSOlivier Certner parse_id_type(const char *const string, id_type_t *const type,
45687c06b7dSOlivier Certner     struct parse_error **const parse_error)
45765766063SOlivier Certner {
45865766063SOlivier Certner 	/*
4596c3def74SOlivier Certner 	 * Special case for "any", as the canonical form for IT_ANY in
46065766063SOlivier Certner 	 * id_type_to_str[] is "*".
46165766063SOlivier Certner 	 */
46265766063SOlivier Certner 	if (strcmp(string, "any") == 0) {
4636c3def74SOlivier Certner 		*type = IT_ANY;
46465766063SOlivier Certner 		return (0);
46565766063SOlivier Certner 	}
46665766063SOlivier Certner 
46765766063SOlivier Certner 	/* Start at 1 to avoid parsing "invalid". */
4686c3def74SOlivier Certner 	for (size_t i = 1; i <= IT_LAST; ++i) {
46965766063SOlivier Certner 		if (strcmp(string, id_type_to_str[i]) == 0) {
47065766063SOlivier Certner 			*type = i;
47165766063SOlivier Certner 			return (0);
47265766063SOlivier Certner 		}
47365766063SOlivier Certner 	}
47465766063SOlivier Certner 
4756c3def74SOlivier Certner 	*type = IT_INVALID;
47687c06b7dSOlivier Certner 	make_parse_error(parse_error, 0, "No valid type found.");
4776c3def74SOlivier Certner 	return (EINVAL);
4786c3def74SOlivier Certner }
4796c3def74SOlivier Certner 
4806c3def74SOlivier Certner static size_t
4816c3def74SOlivier Certner parse_gid_flags(const char *const string, flags_t *const flags,
4826c3def74SOlivier Certner     flags_t *const gid_flags)
4836c3def74SOlivier Certner {
4846c3def74SOlivier Certner 	switch (string[0]) {
4856c3def74SOlivier Certner 	case '+':
4866c3def74SOlivier Certner 		*flags |= MDF_SUPP_ALLOW;
4876c3def74SOlivier Certner 		goto has_supp_clause;
4886c3def74SOlivier Certner 	case '!':
4896c3def74SOlivier Certner 		*flags |= MDF_SUPP_MUST;
4906c3def74SOlivier Certner 		*gid_flags |= MDF_MAY_REJ_SUPP;
4916c3def74SOlivier Certner 		goto has_supp_clause;
4926c3def74SOlivier Certner 	case '-':
4936c3def74SOlivier Certner 		*flags |= MDF_SUPP_DONT;
4946c3def74SOlivier Certner 		*gid_flags |= MDF_MAY_REJ_SUPP;
4956c3def74SOlivier Certner 		goto has_supp_clause;
4966c3def74SOlivier Certner 	has_supp_clause:
4976c3def74SOlivier Certner 		*gid_flags |= MDF_HAS_SUPP_CLAUSE;
4986c3def74SOlivier Certner 		return (1);
4996c3def74SOlivier Certner 	}
5006c3def74SOlivier Certner 
5016c3def74SOlivier Certner 	return (0);
5026c3def74SOlivier Certner }
5036c3def74SOlivier Certner 
5046c3def74SOlivier Certner static bool
5056c3def74SOlivier Certner parse_any(const char *const string)
5066c3def74SOlivier Certner {
5076c3def74SOlivier Certner 	return (strcmp(string, "*") == 0 || strcmp(string, "any") == 0);
5086c3def74SOlivier Certner }
5096c3def74SOlivier Certner 
5106c3def74SOlivier Certner static bool
5116c3def74SOlivier Certner has_clauses(const id_nb_t nb, const flags_t type_flags)
5126c3def74SOlivier Certner {
5136c3def74SOlivier Certner 	return ((type_flags & MDF_TYPE_MASK) != 0 || nb != 0);
5146c3def74SOlivier Certner }
5156c3def74SOlivier Certner 
5166c3def74SOlivier Certner static int
5176c3def74SOlivier Certner parse_target_clause(char *to, struct rule *const rule,
51887c06b7dSOlivier Certner     struct id_list *const uid_list, struct id_list *const gid_list,
51987c06b7dSOlivier Certner     struct parse_error **const parse_error)
5206c3def74SOlivier Certner {
52187c06b7dSOlivier Certner 	const char *const start = to;
5226c3def74SOlivier Certner 	char *to_type, *to_id;
5236c3def74SOlivier Certner 	const char *p;
5246c3def74SOlivier Certner 	struct id_list *list;
5256c3def74SOlivier Certner 	id_nb_t *nb;
5266c3def74SOlivier Certner 	flags_t *tflags;
5276c3def74SOlivier Certner 	struct id_elem *ie;
5286c3def74SOlivier Certner 	struct id_spec is = {.flags = 0};
5296c3def74SOlivier Certner 	flags_t gid_flags = 0;
5306c3def74SOlivier Certner 	id_type_t type;
5316c3def74SOlivier Certner 	int error;
5326c3def74SOlivier Certner 
53387c06b7dSOlivier Certner 	MPASS(*parse_error == NULL);
5346c3def74SOlivier Certner 	MPASS(to != NULL);
5354a03b645SOlivier Certner 	to_type = strsep_noblanks(&to, "=");
5366c3def74SOlivier Certner 	MPASS(to_type != NULL);
5376c3def74SOlivier Certner 	to_type += parse_gid_flags(to_type, &is.flags, &gid_flags);
53887c06b7dSOlivier Certner 	error = parse_id_type(to_type, &type, parse_error);
5396c3def74SOlivier Certner 	if (error != 0)
5406c3def74SOlivier Certner 		goto einval;
54187c06b7dSOlivier Certner 	if (type != IT_GID && is.flags != 0) {
54287c06b7dSOlivier Certner 		make_parse_error(parse_error, to_type - start,
54387c06b7dSOlivier Certner 		    "Expected type 'gid' after flags, not '%s'.",
54487c06b7dSOlivier Certner 		    to_type);
5456c3def74SOlivier Certner 		goto einval;
54687c06b7dSOlivier Certner 	}
5476c3def74SOlivier Certner 
5484a03b645SOlivier Certner 	to_id = strsep_noblanks(&to, "");
5496c3def74SOlivier Certner 	switch (type) {
5506c3def74SOlivier Certner 	case IT_GID:
55187c06b7dSOlivier Certner 		if (to_id == NULL) {
55287c06b7dSOlivier Certner 			make_parse_error(parse_error, to_type - start,
55387c06b7dSOlivier Certner 			    "No '=' and ID specification after type '%s'.",
55487c06b7dSOlivier Certner 			    to_type);
5556c3def74SOlivier Certner 			goto einval;
55687c06b7dSOlivier Certner 		}
5576c3def74SOlivier Certner 
5586c3def74SOlivier Certner 		if (is.flags == 0) {
5596c3def74SOlivier Certner 			/* No flags: Dealing with a primary group. */
5606c3def74SOlivier Certner 			is.flags |= MDF_PRIMARY;
5616c3def74SOlivier Certner 			gid_flags |= MDF_HAS_PRIMARY_CLAUSE;
5626c3def74SOlivier Certner 		}
5636c3def74SOlivier Certner 
5646c3def74SOlivier Certner 		list = gid_list;
5656c3def74SOlivier Certner 		nb = &rule->gids_nb;
5666c3def74SOlivier Certner 		tflags = &rule->gid_flags;
5676c3def74SOlivier Certner 
5686c3def74SOlivier Certner 		/* "*" or "any"? */
5696c3def74SOlivier Certner 		if (parse_any(to_id)) {
5706c3def74SOlivier Certner 			/*
5716c3def74SOlivier Certner 			 * We check that we have not seen any other clause of
5726c3def74SOlivier Certner 			 * the same category (i.e., concerning primary or
5736c3def74SOlivier Certner 			 * supplementary groups).
5746c3def74SOlivier Certner 			 */
5756c3def74SOlivier Certner 			if ((is.flags & MDF_PRIMARY) != 0) {
57687c06b7dSOlivier Certner 				if ((*tflags & MDF_HAS_PRIMARY_CLAUSE) != 0) {
57787c06b7dSOlivier Certner 					make_parse_error(parse_error,
57887c06b7dSOlivier Certner 					    to_id - start,
57987c06b7dSOlivier Certner 					    "'any' specified after another "
58087c06b7dSOlivier Certner 					    "(primary) GID.");
5816c3def74SOlivier Certner 					goto einval;
58287c06b7dSOlivier Certner 				}
5836c3def74SOlivier Certner 				*tflags |= gid_flags | MDF_ANY;
5846c3def74SOlivier Certner 			} else {
5856c3def74SOlivier Certner 				/*
5866c3def74SOlivier Certner 				 * If a supplementary group flag was present, it
5876c3def74SOlivier Certner 				 * must be MDF_SUPP_ALLOW ("+").
5886c3def74SOlivier Certner 				 */
58987c06b7dSOlivier Certner 				if ((is.flags & MDF_SUPP_MASK) != MDF_SUPP_ALLOW) {
59087c06b7dSOlivier Certner 					make_parse_error(parse_error,
59187c06b7dSOlivier Certner 					    to_id - start,
59287c06b7dSOlivier Certner 					    "'any' specified with another "
59387c06b7dSOlivier Certner 					    "flag than '+'.");
5946c3def74SOlivier Certner 					goto einval;
59587c06b7dSOlivier Certner 				}
59687c06b7dSOlivier Certner 				if ((*tflags & MDF_HAS_SUPP_CLAUSE) != 0) {
59787c06b7dSOlivier Certner 					make_parse_error(parse_error,
59887c06b7dSOlivier Certner 					    to_id - start,
59987c06b7dSOlivier Certner 					    "'any' with flag '+' specified after "
60087c06b7dSOlivier Certner 					    "another (supplementary) GID.");
60187c06b7dSOlivier Certner 					goto einval;
60287c06b7dSOlivier Certner 				}
6036c3def74SOlivier Certner 				*tflags |= gid_flags | MDF_ANY_SUPP;
6046c3def74SOlivier Certner 			}
6056c3def74SOlivier Certner 			goto check_type_and_finish;
6066c3def74SOlivier Certner 		} else {
6076c3def74SOlivier Certner 			/*
6086c3def74SOlivier Certner 			 * Check that we haven't already seen "any" for the same
6096c3def74SOlivier Certner 			 * category.
6106c3def74SOlivier Certner 			 */
6116c3def74SOlivier Certner 			if ((is.flags & MDF_PRIMARY) != 0) {
61287c06b7dSOlivier Certner 				if ((*tflags & MDF_ANY) != 0) {
61387c06b7dSOlivier Certner 					make_parse_error(parse_error,
61487c06b7dSOlivier Certner 					    to_id - start,
61587c06b7dSOlivier Certner 					    "Some (primary) GID specified after "
61687c06b7dSOlivier Certner 					    "'any'.");
6176c3def74SOlivier Certner 					goto einval;
61887c06b7dSOlivier Certner 				}
6196c3def74SOlivier Certner 			} else if ((*tflags & MDF_ANY_SUPP) != 0 &&
62087c06b7dSOlivier Certner 			    (is.flags & MDF_SUPP_ALLOW) != 0) {
62187c06b7dSOlivier Certner 				make_parse_error(parse_error,
62287c06b7dSOlivier Certner 				    to_id - start,
62387c06b7dSOlivier Certner 				    "Some (supplementary) GID specified after "
62487c06b7dSOlivier Certner 				    "'any' with flag '+'.");
6256c3def74SOlivier Certner 				goto einval;
62687c06b7dSOlivier Certner 			}
6276c3def74SOlivier Certner 			*tflags |= gid_flags;
6286c3def74SOlivier Certner 		}
6296c3def74SOlivier Certner 		break;
6306c3def74SOlivier Certner 
6316c3def74SOlivier Certner 	case IT_UID:
63287c06b7dSOlivier Certner 		if (to_id == NULL) {
63387c06b7dSOlivier Certner 			make_parse_error(parse_error, to_type - start,
63487c06b7dSOlivier Certner 			    "No '=' and ID specification after type '%s'.",
63587c06b7dSOlivier Certner 			    to_type);
6366c3def74SOlivier Certner 			goto einval;
63787c06b7dSOlivier Certner 		}
6386c3def74SOlivier Certner 
6396c3def74SOlivier Certner 		list = uid_list;
6406c3def74SOlivier Certner 		nb = &rule->uids_nb;
6416c3def74SOlivier Certner 		tflags = &rule->uid_flags;
6426c3def74SOlivier Certner 
6436c3def74SOlivier Certner 		/* "*" or "any"? */
6446c3def74SOlivier Certner 		if (parse_any(to_id)) {
6456c3def74SOlivier Certner 			/* There must not be any other clause. */
64687c06b7dSOlivier Certner 			if (has_clauses(*nb, *tflags)) {
64787c06b7dSOlivier Certner 				make_parse_error(parse_error, to_id - start,
64887c06b7dSOlivier Certner 				    "'any' specified after another UID.");
6496c3def74SOlivier Certner 				goto einval;
65087c06b7dSOlivier Certner 			}
6516c3def74SOlivier Certner 			*tflags |= MDF_ANY;
6526c3def74SOlivier Certner 			goto check_type_and_finish;
6536c3def74SOlivier Certner 		} else {
6546c3def74SOlivier Certner 			/*
6556c3def74SOlivier Certner 			 * Check that we haven't already seen "any" for the same
6566c3def74SOlivier Certner 			 * category.
6576c3def74SOlivier Certner 			 */
65887c06b7dSOlivier Certner 			if ((*tflags & MDF_ANY) != 0) {
65987c06b7dSOlivier Certner 				make_parse_error(parse_error, to_id - start,
66087c06b7dSOlivier Certner 				    "Some UID specified after 'any'.");
6616c3def74SOlivier Certner 				goto einval;
6626c3def74SOlivier Certner 			}
66387c06b7dSOlivier Certner 		}
6646c3def74SOlivier Certner 		break;
6656c3def74SOlivier Certner 
6666c3def74SOlivier Certner 	case IT_ANY:
6676c3def74SOlivier Certner 		/* No ID allowed. */
66887c06b7dSOlivier Certner 		if (to_id != NULL) {
66987c06b7dSOlivier Certner 			make_parse_error(parse_error, to_type - start,
67087c06b7dSOlivier Certner 			    "No '=' and ID allowed after type '%s'.", to_type);
6716c3def74SOlivier Certner 			goto einval;
67287c06b7dSOlivier Certner 		}
6736c3def74SOlivier Certner 		/*
6746c3def74SOlivier Certner 		 * We can't have IT_ANY after any other IT_*, it must be the
6756c3def74SOlivier Certner 		 * only one.
6766c3def74SOlivier Certner 		 */
6776c3def74SOlivier Certner 		if (has_clauses(rule->uids_nb, rule->uid_flags) ||
67887c06b7dSOlivier Certner 		    has_clauses(rule->gids_nb, rule->gid_flags)) {
67987c06b7dSOlivier Certner 			make_parse_error(parse_error, to_type - start,
68087c06b7dSOlivier Certner 			    "Target clause of type '%s' coming after another "
68187c06b7dSOlivier Certner 			    "clause (must be alone).", to_type);
6826c3def74SOlivier Certner 			goto einval;
68387c06b7dSOlivier Certner 		}
6846c3def74SOlivier Certner 		rule->uid_flags |= MDF_ANY;
6856c3def74SOlivier Certner 		rule->gid_flags |= MDF_ANY | MDF_ANY_SUPP |
6866c3def74SOlivier Certner 		    MDF_HAS_PRIMARY_CLAUSE | MDF_HAS_SUPP_CLAUSE;
6876c3def74SOlivier Certner 		goto finish;
6886c3def74SOlivier Certner 
6896c3def74SOlivier Certner 	default:
6906c3def74SOlivier Certner 		/* parse_id_type() returns no other types currently. */
6916c3def74SOlivier Certner 		__assert_unreachable();
6926c3def74SOlivier Certner 	}
6936c3def74SOlivier Certner 
6946c3def74SOlivier Certner 	/* Rule out cases that have been treated above. */
6956c3def74SOlivier Certner 	MPASS((type == IT_UID || type == IT_GID) && !parse_any(to_id));
6966c3def74SOlivier Certner 
6976c3def74SOlivier Certner 	/* "."? */
6986c3def74SOlivier Certner 	if (strcmp(to_id, ".") == 0) {
6996c3def74SOlivier Certner 		if ((*tflags & MDF_CURRENT) != 0) {
7006c3def74SOlivier Certner 			/* Duplicate "." <id>.  Try to coalesce. */
7016c3def74SOlivier Certner 			error = coalesce_id_flags(is.flags, tflags);
70287c06b7dSOlivier Certner 			if (error != 0) {
70387c06b7dSOlivier Certner 				make_parse_error(parse_error, to_id - start,
70487c06b7dSOlivier Certner 				    "Incompatible flags with prior clause "
70587c06b7dSOlivier Certner 				    "with same target.");
7066c3def74SOlivier Certner 				goto einval;
70787c06b7dSOlivier Certner 			}
7086c3def74SOlivier Certner 		} else
7096c3def74SOlivier Certner 			*tflags |= MDF_CURRENT | is.flags;
7106c3def74SOlivier Certner 		goto check_type_and_finish;
7116c3def74SOlivier Certner 	}
7126c3def74SOlivier Certner 
7136c3def74SOlivier Certner 	/* Parse an ID. */
7146c3def74SOlivier Certner 	error = strtoui_strict(to_id, &p, 10, &is.id);
71587c06b7dSOlivier Certner 	if (error != 0 || *p != '\0') {
71687c06b7dSOlivier Certner 		make_parse_error(parse_error, to_id - start,
71787c06b7dSOlivier Certner 		    "Cannot parse a numerical ID (base 10).");
7186c3def74SOlivier Certner 		goto einval;
71987c06b7dSOlivier Certner 	}
7206c3def74SOlivier Certner 
7216c3def74SOlivier Certner 	/* Explicit ID flags. */
7226c3def74SOlivier Certner 	if (type == IT_GID && (is.flags & MDF_SUPP_MUST) != 0)
7236c3def74SOlivier Certner 		*tflags |= MDF_EXPLICIT_SUPP_MUST;
7246c3def74SOlivier Certner 
7256c3def74SOlivier Certner 	/*
7266c3def74SOlivier Certner 	 * We check for duplicate IDs and coalesce their 'struct id_spec' only
7276c3def74SOlivier Certner 	 * at end of parse_single_rule() because it is much more performant then
7286c3def74SOlivier Certner 	 * (using sorted arrays).
7296c3def74SOlivier Certner 	 */
7306c3def74SOlivier Certner 	++*nb;
73187c06b7dSOlivier Certner 	if (*nb == 0) {
73287c06b7dSOlivier Certner 		make_parse_error(parse_error, 0,
73387c06b7dSOlivier Certner 		    "Too many target clauses of type '%s'.", to_type);
7346c3def74SOlivier Certner 		return (EOVERFLOW);
73587c06b7dSOlivier Certner 	}
7366c3def74SOlivier Certner 	ie = malloc(sizeof(*ie), M_DO, M_WAITOK);
7376c3def74SOlivier Certner 	ie->spec = is;
738c7fc71c6SOlivier Certner 	STAILQ_INSERT_TAIL(list, ie, ie_entries);
7396c3def74SOlivier Certner 	check_type_and_id_spec(type, &is);
7406c3def74SOlivier Certner check_type_and_finish:
7416c3def74SOlivier Certner 	check_type_and_type_flags(type, *tflags);
74287c06b7dSOlivier Certner finish:
7436c3def74SOlivier Certner 	return (0);
7446c3def74SOlivier Certner einval:
74587c06b7dSOlivier Certner 	/* We must have built a parse error on error. */
74687c06b7dSOlivier Certner 	MPASS(*parse_error != NULL);
74765766063SOlivier Certner 	return (EINVAL);
74865766063SOlivier Certner }
74965766063SOlivier Certner 
75065766063SOlivier Certner static int
7516c3def74SOlivier Certner u_int_cmp(const u_int i1, const u_int i2)
7528aac90f1SBaptiste Daroussin {
7536c3def74SOlivier Certner 	return ((i1 > i2) - (i1 < i2));
7546c3def74SOlivier Certner }
7556c3def74SOlivier Certner 
7566c3def74SOlivier Certner static int
7576c3def74SOlivier Certner id_spec_cmp(const void *const p1, const void *const p2)
7586c3def74SOlivier Certner {
7596c3def74SOlivier Certner 	const struct id_spec *const is1 = p1;
7606c3def74SOlivier Certner 	const struct id_spec *const is2 = p2;
7616c3def74SOlivier Certner 
7626c3def74SOlivier Certner 	return (u_int_cmp(is1->id, is2->id));
7636c3def74SOlivier Certner }
7646c3def74SOlivier Certner 
7656c3def74SOlivier Certner /*
7666c3def74SOlivier Certner  * Transfer content of 'list' into 'array', freeing and emptying list.
7676c3def74SOlivier Certner  *
7686c3def74SOlivier Certner  * 'nb' must be 'list''s length and not be greater than 'array''s size.  The
7696c3def74SOlivier Certner  * destination array is sorted by ID.  Structures 'struct id_spec' with same IDs
7706c3def74SOlivier Certner  * are coalesced if that makes sense (not including duplicate clauses), else
7716c3def74SOlivier Certner  * EINVAL is returned.  On success, 'nb' is updated (lowered) to account for
7726c3def74SOlivier Certner  * coalesced specifications.  The parameter 'type' is only for testing purposes
7736c3def74SOlivier Certner  * (INVARIANTS).
7746c3def74SOlivier Certner  */
7756c3def74SOlivier Certner static int
7766c3def74SOlivier Certner pour_list_into_rule(const id_type_t type, struct id_list *const list,
77787c06b7dSOlivier Certner     struct id_spec *const array, id_nb_t *const nb,
77887c06b7dSOlivier Certner     struct parse_error **const parse_error)
7796c3def74SOlivier Certner {
7806c3def74SOlivier Certner 	struct id_elem *ie, *ie_next;
7816c3def74SOlivier Certner 	size_t idx = 0;
7826c3def74SOlivier Certner 
7836c3def74SOlivier Certner 	/* Fill the array. */
784c7fc71c6SOlivier Certner 	STAILQ_FOREACH_SAFE(ie, list, ie_entries, ie_next) {
7856c3def74SOlivier Certner 		MPASS(idx < *nb);
7866c3def74SOlivier Certner 		array[idx] = ie->spec;
7876c3def74SOlivier Certner 		free(ie, M_DO);
7886c3def74SOlivier Certner 		++idx;
7896c3def74SOlivier Certner 	}
7906c3def74SOlivier Certner 	MPASS(idx == *nb);
791c7fc71c6SOlivier Certner 	STAILQ_INIT(list);
7926c3def74SOlivier Certner 
7936c3def74SOlivier Certner 	/* Sort it (by ID). */
7946c3def74SOlivier Certner 	qsort(array, *nb, sizeof(*array), id_spec_cmp);
7956c3def74SOlivier Certner 
7966c3def74SOlivier Certner 	/* Coalesce same IDs. */
7976c3def74SOlivier Certner 	if (*nb != 0) {
7986c3def74SOlivier Certner 		size_t ref_idx = 0;
7996c3def74SOlivier Certner 
8006c3def74SOlivier Certner 		for (idx = 1; idx < *nb; ++idx) {
8016c3def74SOlivier Certner 			const u_int id = array[idx].id;
8026c3def74SOlivier Certner 
8036c3def74SOlivier Certner 			if (id != array[ref_idx].id) {
8046c3def74SOlivier Certner 				++ref_idx;
8056c3def74SOlivier Certner 				if (ref_idx != idx)
8066c3def74SOlivier Certner 					array[ref_idx] = array[idx];
8076c3def74SOlivier Certner 				continue;
8086c3def74SOlivier Certner 			}
8096c3def74SOlivier Certner 
8106c3def74SOlivier Certner 			switch (type) {
8116c3def74SOlivier Certner 				int error;
8126c3def74SOlivier Certner 
8136c3def74SOlivier Certner 			case IT_GID:
8146c3def74SOlivier Certner 				error = coalesce_id_flags(array[idx].flags,
8156c3def74SOlivier Certner 				    &array[ref_idx].flags);
81687c06b7dSOlivier Certner 				if (error != 0) {
81787c06b7dSOlivier Certner 					make_parse_error(parse_error, 0,
81887c06b7dSOlivier Certner 					    "Incompatible flags or duplicate "
81987c06b7dSOlivier Certner 					    "GID %u.", id);
8206c3def74SOlivier Certner 					return (EINVAL);
82187c06b7dSOlivier Certner 				}
8226c3def74SOlivier Certner 				check_type_and_id_flags(type,
8236c3def74SOlivier Certner 				    array[ref_idx].flags);
8246c3def74SOlivier Certner 				break;
8256c3def74SOlivier Certner 
8266c3def74SOlivier Certner 			case IT_UID:
8276c3def74SOlivier Certner 				/*
8286c3def74SOlivier Certner 				 * No flags in this case.  Multiple appearances
8296c3def74SOlivier Certner 				 * of the same UID is an exact redundancy, so
8306c3def74SOlivier Certner 				 * error out.
8316c3def74SOlivier Certner 				 */
83287c06b7dSOlivier Certner 				make_parse_error(parse_error, 0,
83387c06b7dSOlivier Certner 				    "Duplicate UID %u.", id);
8346c3def74SOlivier Certner 				return (EINVAL);
8356c3def74SOlivier Certner 
8366c3def74SOlivier Certner 			default:
8376c3def74SOlivier Certner 				__assert_unreachable();
8386c3def74SOlivier Certner 			}
8396c3def74SOlivier Certner 		}
8406c3def74SOlivier Certner 		*nb = ref_idx + 1;
8416c3def74SOlivier Certner 	}
8426c3def74SOlivier Certner 
8436c3def74SOlivier Certner 	return (0);
8446c3def74SOlivier Certner }
8456c3def74SOlivier Certner 
8466c3def74SOlivier Certner /*
8476c3def74SOlivier Certner  * See also first comments for parse_rule() below.
8486c3def74SOlivier Certner  *
8496c3def74SOlivier Certner  * The second part of a rule, called <target> (or <to>), is a comma-separated
8506c3def74SOlivier Certner  * (',') list of '<flags><type>=<id>' clauses similar to that of the <from>
8516c3def74SOlivier Certner  * part, with the extensions that <id> may also be "*" or "any" or ".", and that
8526c3def74SOlivier Certner  * <flags> may contain at most one of the '+', '-' and '!' characters when
8536c3def74SOlivier Certner  * <type> is "gid" (no flags are allowed for "uid").  No two clauses in a single
8546c3def74SOlivier Certner  * <to> list may list the same <id>.  "*" and "any" both designate any ID for
8556c3def74SOlivier Certner  * the <type>, and are aliases to each other.  In front of "any" (or "*"), only
8566c3def74SOlivier Certner  * the '+' flag is allowed (in the "gid" case).  "." designates the process'
8576c3def74SOlivier Certner  * current IDs for the <type>.  The precise meaning of flags and "." is
8586c3def74SOlivier Certner  * explained in functions checking privileges below.
8596c3def74SOlivier Certner  */
8606c3def74SOlivier Certner static int
86187c06b7dSOlivier Certner parse_single_rule(char *rule, struct rules *const rules,
86287c06b7dSOlivier Certner     struct parse_error **const parse_error)
8636c3def74SOlivier Certner {
86487c06b7dSOlivier Certner 	const char *const start = rule;
8656c3def74SOlivier Certner 	const char *from_type, *from_id, *p;
8666c3def74SOlivier Certner 	char *to_list;
8676c3def74SOlivier Certner 	struct id_list uid_list, gid_list;
8686c3def74SOlivier Certner 	struct id_elem *ie, *ie_next;
8698aac90f1SBaptiste Daroussin 	struct rule *new;
87065766063SOlivier Certner 	int error;
8718aac90f1SBaptiste Daroussin 
87287c06b7dSOlivier Certner 	MPASS(*parse_error == NULL);
873c7fc71c6SOlivier Certner 	STAILQ_INIT(&uid_list);
874c7fc71c6SOlivier Certner 	STAILQ_INIT(&gid_list);
8758aac90f1SBaptiste Daroussin 
8766c3def74SOlivier Certner 	/* Freed when the 'struct rules' container is freed. */
8776c3def74SOlivier Certner 	new = malloc(sizeof(*new), M_DO, M_WAITOK | M_ZERO);
878e4ce30f8SOlivier Certner 
8794a03b645SOlivier Certner 	from_type = strsep_noblanks(&rule, "=");
8806c3def74SOlivier Certner 	MPASS(from_type != NULL); /* Because 'rule' was not NULL. */
88187c06b7dSOlivier Certner 	error = parse_id_type(from_type, &new->from_type, parse_error);
88265766063SOlivier Certner 	if (error != 0)
8836aadc7b2SOlivier Certner 		goto einval;
88465766063SOlivier Certner 	switch (new->from_type) {
8856c3def74SOlivier Certner 	case IT_UID:
8866c3def74SOlivier Certner 	case IT_GID:
88765766063SOlivier Certner 		break;
88865766063SOlivier Certner 	default:
88987c06b7dSOlivier Certner 		make_parse_error(parse_error, 0, "Type '%s' not allowed in "
89087c06b7dSOlivier Certner 		    "the \"from\" part of rules.");
89165766063SOlivier Certner 		goto einval;
89265766063SOlivier Certner 	}
893e4ce30f8SOlivier Certner 
8944a03b645SOlivier Certner 	from_id = strsep_noblanks(&rule, ":");
89587c06b7dSOlivier Certner 	if (is_null_or_empty(from_id)) {
89687c06b7dSOlivier Certner 		make_parse_error(parse_error, 0, "No ID specified.");
8976aadc7b2SOlivier Certner 		goto einval;
89887c06b7dSOlivier Certner 	}
899e4ce30f8SOlivier Certner 
9000af43c02SOlivier Certner 	error = strtoui_strict(from_id, &p, 10, &new->from_id);
90187c06b7dSOlivier Certner 	if (error != 0 || *p != '\0') {
90287c06b7dSOlivier Certner 		make_parse_error(parse_error, from_id - start,
90387c06b7dSOlivier Certner 		    "Cannot parse a numerical ID (base 10).");
9046aadc7b2SOlivier Certner 		goto einval;
90587c06b7dSOlivier Certner 	}
906e4ce30f8SOlivier Certner 
9076c3def74SOlivier Certner 	/*
9086c3def74SOlivier Certner 	 * We will now parse the "to" list.
9096c3def74SOlivier Certner 	 *
9106c3def74SOlivier Certner 	 * In order to ease parsing, we will begin by building lists of target
9116c3def74SOlivier Certner 	 * UIDs and GIDs in local variables 'uid_list' and 'gid_list'.  The
9126c3def74SOlivier Certner 	 * number of each type of IDs will be filled directly in 'new'.  At end
9136c3def74SOlivier Certner 	 * of parse, we will allocate both arrays of IDs to be placed into the
9146c3def74SOlivier Certner 	 * 'uids' and 'gids' members, sort them, and discard the tail queues
9156c3def74SOlivier Certner 	 * used to build them.  This conversion to sorted arrays at end of parse
9166c3def74SOlivier Certner 	 * allows to minimize memory allocations and enables searching IDs in
9176c3def74SOlivier Certner 	 * O(log(n)) instead of linearly.
9186c3def74SOlivier Certner 	 */
9194a03b645SOlivier Certner 	to_list = strsep_noblanks(&rule, ",");
92087c06b7dSOlivier Certner 	if (to_list == NULL) {
92187c06b7dSOlivier Certner 		make_parse_error(parse_error, 0, "No target list.");
9226c3def74SOlivier Certner 		goto einval;
92387c06b7dSOlivier Certner 	}
9246c3def74SOlivier Certner 	do {
92587c06b7dSOlivier Certner 		error = parse_target_clause(to_list, new, &uid_list, &gid_list,
92687c06b7dSOlivier Certner 		    parse_error);
92787c06b7dSOlivier Certner 		if (error != 0) {
92887c06b7dSOlivier Certner 			(*parse_error)->pos += to_list - start;
9296aadc7b2SOlivier Certner 			goto einval;
93087c06b7dSOlivier Certner 		}
9316aadc7b2SOlivier Certner 
9324a03b645SOlivier Certner 		to_list = strsep_noblanks(&rule, ",");
9336c3def74SOlivier Certner 	} while (to_list != NULL);
9346c3def74SOlivier Certner 
9356c3def74SOlivier Certner 	if (new->uids_nb != 0) {
9366c3def74SOlivier Certner 		new->uids = malloc(sizeof(*new->uids) * new->uids_nb, M_DO,
9376c3def74SOlivier Certner 		    M_WAITOK);
9386c3def74SOlivier Certner 		error = pour_list_into_rule(IT_UID, &uid_list, new->uids,
93987c06b7dSOlivier Certner 		    &new->uids_nb, parse_error);
9406c3def74SOlivier Certner 		if (error != 0)
9416aadc7b2SOlivier Certner 			goto einval;
9428aac90f1SBaptiste Daroussin 	}
943c7fc71c6SOlivier Certner 	MPASS(STAILQ_EMPTY(&uid_list));
9446c3def74SOlivier Certner 	if (!has_clauses(new->uids_nb, new->uid_flags)) {
9456c3def74SOlivier Certner 		/* No UID specified, default is "uid=.". */
9466c3def74SOlivier Certner 		MPASS(new->uid_flags == 0);
9476c3def74SOlivier Certner 		new->uid_flags = MDF_CURRENT;
9486c3def74SOlivier Certner 		check_type_and_type_flags(IT_UID, new->uid_flags);
9496c3def74SOlivier Certner 	}
950add521c1SOlivier Certner 
9516c3def74SOlivier Certner 	if (new->gids_nb != 0) {
9526c3def74SOlivier Certner 		new->gids = malloc(sizeof(*new->gids) * new->gids_nb, M_DO,
9536c3def74SOlivier Certner 		    M_WAITOK);
9546c3def74SOlivier Certner 		error = pour_list_into_rule(IT_GID, &gid_list, new->gids,
95587c06b7dSOlivier Certner 		    &new->gids_nb, parse_error);
9566c3def74SOlivier Certner 		if (error != 0)
9576c3def74SOlivier Certner 			goto einval;
9586c3def74SOlivier Certner 	}
959c7fc71c6SOlivier Certner 	MPASS(STAILQ_EMPTY(&gid_list));
9606c3def74SOlivier Certner 	if (!has_clauses(new->gids_nb, new->gid_flags)) {
9616c3def74SOlivier Certner 		/* No GID specified, default is "gid=.,!gid=.". */
9626c3def74SOlivier Certner 		MPASS(new->gid_flags == 0);
9636c3def74SOlivier Certner 		new->gid_flags = MDF_CURRENT | MDF_PRIMARY | MDF_SUPP_MUST |
9646c3def74SOlivier Certner 		    MDF_HAS_PRIMARY_CLAUSE | MDF_HAS_SUPP_CLAUSE;
9656c3def74SOlivier Certner 		check_type_and_type_flags(IT_GID, new->gid_flags);
9666c3def74SOlivier Certner 	}
9676c3def74SOlivier Certner 
968c7fc71c6SOlivier Certner 	STAILQ_INSERT_TAIL(&rules->head, new, r_entries);
969add521c1SOlivier Certner 	return (0);
9706c3def74SOlivier Certner 
9716aadc7b2SOlivier Certner einval:
9726c3def74SOlivier Certner 	free(new->gids, M_DO);
9736c3def74SOlivier Certner 	free(new->uids, M_DO);
9748aac90f1SBaptiste Daroussin 	free(new, M_DO);
975c7fc71c6SOlivier Certner 	STAILQ_FOREACH_SAFE(ie, &gid_list, ie_entries, ie_next)
9766c3def74SOlivier Certner 	    free(ie, M_DO);
977c7fc71c6SOlivier Certner 	STAILQ_FOREACH_SAFE(ie, &uid_list, ie_entries, ie_next)
9786c3def74SOlivier Certner 	    free(ie, M_DO);
97987c06b7dSOlivier Certner 	MPASS(*parse_error != NULL);
9806aadc7b2SOlivier Certner 	return (EINVAL);
9818aac90f1SBaptiste Daroussin }
9828aac90f1SBaptiste Daroussin 
983bbf8af66SOlivier Certner /*
984bbf8af66SOlivier Certner  * Parse rules specification and produce rule structures out of it.
985bbf8af66SOlivier Certner  *
9863186b192SOlivier Certner  * Returns 0 on success, with '*rulesp' made to point to a 'struct rule'
9873186b192SOlivier Certner  * representing the rules.  On error, the returned value is non-zero and
9883186b192SOlivier Certner  * '*rulesp' is unchanged.  If 'string' has length greater or equal to
989bbf8af66SOlivier Certner  * MAC_RULE_STRING_LEN, ENAMETOOLONG is returned.  If it is not in the expected
99087c06b7dSOlivier Certner  * format, EINVAL is returned.  If an error is returned, '*parse_error' is set
99187c06b7dSOlivier Certner  * to point to a 'struct parse_error' giving an error message for the problem,
99287c06b7dSOlivier Certner  * else '*parse_error' is set to NULL.
9936c3def74SOlivier Certner  *
9946c3def74SOlivier Certner  * Expected format: A semi-colon-separated list of rules of the form
9956c3def74SOlivier Certner  * "<from>:<target>".  The <from> part is of the form "<type>=<id>" where <type>
9966c3def74SOlivier Certner  * is "uid" or "gid", <id> an UID or GID (depending on <type>) and <target> is
9976c3def74SOlivier Certner  * "*", "any" or a comma-separated list of '<flags><type>=<id>' clauses (see the
9986c3def74SOlivier Certner  * comment for parse_single_rule() for more details).  For convenience, empty
9994a03b645SOlivier Certner  * rules are allowed (and do nothing), and spaces and tabs are allowed (and
10004a03b645SOlivier Certner  * removed) around each token (tokens are natural ones, except that
10014a03b645SOlivier Certner  * '<flags><type>' as a whole is considered a single token, so no blanks are
10024a03b645SOlivier Certner  * allowed between '<flags>' and '<type>').
10036c3def74SOlivier Certner  *
10046c3def74SOlivier Certner  * Examples:
10056c3def74SOlivier Certner  * - "uid=1001:uid=1010,gid=1010;uid=1002:any"
10066c3def74SOlivier Certner  * - "gid=1010:gid=1011,gid=1012,gid=1013"
1007bbf8af66SOlivier Certner  */
10088aac90f1SBaptiste Daroussin static int
100987c06b7dSOlivier Certner parse_rules(const char *const string, struct rules **const rulesp,
101087c06b7dSOlivier Certner     struct parse_error **const parse_error)
10118aac90f1SBaptiste Daroussin {
1012bbf8af66SOlivier Certner 	const size_t len = strlen(string);
10136c3def74SOlivier Certner 	char *copy, *p, *rule;
10143186b192SOlivier Certner 	struct rules *rules;
10158aac90f1SBaptiste Daroussin 	int error = 0;
10168aac90f1SBaptiste Daroussin 
101787c06b7dSOlivier Certner 	*parse_error = NULL;
101887c06b7dSOlivier Certner 
101987c06b7dSOlivier Certner 	if (len >= MAC_RULE_STRING_LEN) {
102087c06b7dSOlivier Certner 		make_parse_error(parse_error, 0,
102187c06b7dSOlivier Certner 		    "Rule specification string is too long (%zu, max %zu)",
102287c06b7dSOlivier Certner 		    len, MAC_RULE_STRING_LEN - 1);
1023bbf8af66SOlivier Certner 		return (ENAMETOOLONG);
102487c06b7dSOlivier Certner 	}
1025bbf8af66SOlivier Certner 
10263186b192SOlivier Certner 	rules = alloc_rules();
10273186b192SOlivier Certner 	bcopy(string, rules->string, len + 1);
10283186b192SOlivier Certner 	MPASS(rules->string[len] == '\0'); /* Catch some races. */
10293186b192SOlivier Certner 
1030bbf8af66SOlivier Certner 	copy = malloc(len + 1, M_DO, M_WAITOK);
1031bbf8af66SOlivier Certner 	bcopy(string, copy, len + 1);
1032bbf8af66SOlivier Certner 	MPASS(copy[len] == '\0'); /* Catch some races. */
1033bbf8af66SOlivier Certner 
1034bbf8af66SOlivier Certner 	p = copy;
10354a03b645SOlivier Certner 	while ((rule = strsep_noblanks(&p, ";")) != NULL) {
10366c3def74SOlivier Certner 		if (rule[0] == '\0')
10378aac90f1SBaptiste Daroussin 			continue;
103887c06b7dSOlivier Certner 		error = parse_single_rule(rule, rules, parse_error);
10392200a3ecSOlivier Certner 		if (error != 0) {
104087c06b7dSOlivier Certner 			(*parse_error)->pos += rule - copy;
10413186b192SOlivier Certner 			toast_rules(rules);
10428aac90f1SBaptiste Daroussin 			goto out;
10432200a3ecSOlivier Certner 		}
10448aac90f1SBaptiste Daroussin 	}
10453186b192SOlivier Certner 
10463186b192SOlivier Certner 	*rulesp = rules;
10478aac90f1SBaptiste Daroussin out:
10482200a3ecSOlivier Certner 	free(copy, M_DO);
10498aac90f1SBaptiste Daroussin 	return (error);
10508aac90f1SBaptiste Daroussin }
10518aac90f1SBaptiste Daroussin 
1052b2c661feSOlivier Certner /*
1053b2c661feSOlivier Certner  * Find rules applicable to the passed prison.
1054b2c661feSOlivier Certner  *
1055b2c661feSOlivier Certner  * Returns the applicable rules (and never NULL).  'pr' must be unlocked.
1056b2c661feSOlivier Certner  * 'aprp' is set to the (ancestor) prison holding these, and it must be unlocked
1057b2c661feSOlivier Certner  * once the caller is done accessing the rules.  '*aprp' is equal to 'pr' if and
1058b2c661feSOlivier Certner  * only if the current jail has its own set of rules.
1059b2c661feSOlivier Certner  */
106002ed945cSOlivier Certner static struct rules *
1061b2c661feSOlivier Certner find_rules(struct prison *const pr, struct prison **const aprp)
10628aac90f1SBaptiste Daroussin {
1063beb5603cSOlivier Certner 	struct prison *cpr, *ppr;
106402ed945cSOlivier Certner 	struct rules *rules;
10658aac90f1SBaptiste Daroussin 
1066beb5603cSOlivier Certner 	cpr = pr;
1067beb5603cSOlivier Certner 	for (;;) {
1068b2c661feSOlivier Certner 		prison_lock(cpr);
106940a664a4SOlivier Certner 		rules = osd_jail_get(cpr, osd_jail_slot);
10708aac90f1SBaptiste Daroussin 		if (rules != NULL)
10718aac90f1SBaptiste Daroussin 			break;
1072b2c661feSOlivier Certner 		prison_unlock(cpr);
1073beb5603cSOlivier Certner 
1074beb5603cSOlivier Certner 		ppr = cpr->pr_parent;
1075beb5603cSOlivier Certner 		MPASS(ppr != NULL); /* prison0 always has rules. */
1076beb5603cSOlivier Certner 		cpr = ppr;
10778aac90f1SBaptiste Daroussin 	}
10788aac90f1SBaptiste Daroussin 
10796c3def74SOlivier Certner 	*aprp = cpr;
10808aac90f1SBaptiste Daroussin 	return (rules);
10818aac90f1SBaptiste Daroussin }
10828aac90f1SBaptiste Daroussin 
10833d8d91a5SOlivier Certner static void
10843d8d91a5SOlivier Certner hold_rules(struct rules *const rules)
10853d8d91a5SOlivier Certner {
10863d8d91a5SOlivier Certner 	refcount_acquire(&rules->use_count);
10873d8d91a5SOlivier Certner }
10883d8d91a5SOlivier Certner 
10893d8d91a5SOlivier Certner static void
10903d8d91a5SOlivier Certner drop_rules(struct rules *const rules)
10913d8d91a5SOlivier Certner {
10923d8d91a5SOlivier Certner 	if (refcount_release(&rules->use_count))
10933d8d91a5SOlivier Certner 		toast_rules(rules);
10943d8d91a5SOlivier Certner }
10953d8d91a5SOlivier Certner 
10963d8d91a5SOlivier Certner #ifdef INVARIANTS
10973d8d91a5SOlivier Certner static void
10983d8d91a5SOlivier Certner check_rules_use_count(const struct rules *const rules, u_int expected)
10993d8d91a5SOlivier Certner {
11003d8d91a5SOlivier Certner 	const u_int use_count = refcount_load(&rules->use_count);
11013d8d91a5SOlivier Certner 
11023d8d91a5SOlivier Certner 	if (use_count != expected)
11033d8d91a5SOlivier Certner 		panic("MAC/do: Rules at %p: Use count is %u, expected %u",
11043d8d91a5SOlivier Certner 		    rules, use_count, expected);
11053d8d91a5SOlivier Certner }
11063d8d91a5SOlivier Certner #else
11073d8d91a5SOlivier Certner #define check_rules_use_count(...)
11083d8d91a5SOlivier Certner #endif /* INVARIANTS */
11093d8d91a5SOlivier Certner 
1110bbf8af66SOlivier Certner /*
111140a664a4SOlivier Certner  * OSD destructor for slot 'osd_jail_slot'.
1112bbf8af66SOlivier Certner  *
11133d8d91a5SOlivier Certner  * Called with 'value' not NULL.  We have arranged that it is only ever called
11143d8d91a5SOlivier Certner  * when the corresponding jail goes down or at module unload.
1115bbf8af66SOlivier Certner  */
1116bbf8af66SOlivier Certner static void
11178f7e8726SOlivier Certner dealloc_jail_osd(void *const value)
1118bbf8af66SOlivier Certner {
1119bbf8af66SOlivier Certner 	struct rules *const rules = value;
1120bbf8af66SOlivier Certner 
11218f7e8726SOlivier Certner 	/*
11228f7e8726SOlivier Certner 	 * If called because the "holding" jail goes down, no one should be
11238f7e8726SOlivier Certner 	 * using the rules but us at this point because no threads of that jail
11248f7e8726SOlivier Certner 	 * (or its sub-jails) should currently be executing (in particular,
11258f7e8726SOlivier Certner 	 * currently executing setcred()).  The case of module unload is more
11268f7e8726SOlivier Certner 	 * complex.  Although the MAC framework takes care that no hook is
11278f7e8726SOlivier Certner 	 * called while a module is unloading, the unload could happen between
11288f7e8726SOlivier Certner 	 * two calls to MAC hooks in the course of, e.g., executing setcred(),
11298f7e8726SOlivier Certner 	 * where the rules' reference count has been bumped to keep them alive
11308f7e8726SOlivier Certner 	 * even if the rules on the "holding" jail has been concurrently
11318f7e8726SOlivier Certner 	 * changed.  These other references are held in our thread OSD slot, so
11328f7e8726SOlivier Certner 	 * we ensure that all thread's slots are freed first in mac_do_destroy()
11338f7e8726SOlivier Certner 	 * to be able to check that only one reference remains.
11348f7e8726SOlivier Certner 	 */
11353d8d91a5SOlivier Certner 	check_rules_use_count(rules, 1);
11363186b192SOlivier Certner 	toast_rules(rules);
1137bbf8af66SOlivier Certner }
1138bbf8af66SOlivier Certner 
1139bbf8af66SOlivier Certner /*
11403186b192SOlivier Certner  * Remove the rules specifically associated to a prison.
11413186b192SOlivier Certner  *
11423186b192SOlivier Certner  * In practice, this means that the rules become inherited (from the closest
11433186b192SOlivier Certner  * ascendant that has some).
1144bbf8af66SOlivier Certner  *
114540a664a4SOlivier Certner  * Destroys the 'osd_jail_slot' slot of the passed jail.
1146bbf8af66SOlivier Certner  */
1147bbf8af66SOlivier Certner static void
11483186b192SOlivier Certner remove_rules(struct prison *const pr)
1149bbf8af66SOlivier Certner {
11503d8d91a5SOlivier Certner 	struct rules *old_rules;
11513d8d91a5SOlivier Certner 	int error __unused;
11523d8d91a5SOlivier Certner 
1153bbf8af66SOlivier Certner 	prison_lock(pr);
11543d8d91a5SOlivier Certner 	/*
11553d8d91a5SOlivier Certner 	 * We go to the burden of extracting rules first instead of just letting
11568f7e8726SOlivier Certner 	 * osd_jail_del() calling dealloc_jail_osd() as we want to decrement
11578f7e8726SOlivier Certner 	 * their use count, and possibly free them, outside of the prison lock.
11583d8d91a5SOlivier Certner 	 */
11593d8d91a5SOlivier Certner 	old_rules = osd_jail_get(pr, osd_jail_slot);
11603d8d91a5SOlivier Certner 	error = osd_jail_set(pr, osd_jail_slot, NULL);
11613d8d91a5SOlivier Certner 	/* osd_set() never fails nor allocate memory when 'value' is NULL. */
11623d8d91a5SOlivier Certner 	MPASS(error == 0);
11633d8d91a5SOlivier Certner 	/*
11643d8d91a5SOlivier Certner 	 * This completely frees the OSD slot, but doesn't call the destructor
11653d8d91a5SOlivier Certner 	 * since we've just put NULL in the slot.
11663d8d91a5SOlivier Certner 	 */
116740a664a4SOlivier Certner 	osd_jail_del(pr, osd_jail_slot);
1168bbf8af66SOlivier Certner 	prison_unlock(pr);
11693d8d91a5SOlivier Certner 
11703d8d91a5SOlivier Certner 	if (old_rules != NULL)
11713d8d91a5SOlivier Certner 		drop_rules(old_rules);
1172bbf8af66SOlivier Certner }
1173bbf8af66SOlivier Certner 
1174bbf8af66SOlivier Certner /*
11753186b192SOlivier Certner  * Assign already built rules to a jail.
1176bbf8af66SOlivier Certner  */
1177bbf8af66SOlivier Certner static void
11783186b192SOlivier Certner set_rules(struct prison *const pr, struct rules *const rules)
1179bbf8af66SOlivier Certner {
11803186b192SOlivier Certner 	struct rules *old_rules;
11813186b192SOlivier Certner 	void **rsv;
1182bbf8af66SOlivier Certner 
11833d8d91a5SOlivier Certner 	check_rules_use_count(rules, 0);
11843d8d91a5SOlivier Certner 	hold_rules(rules);
118540a664a4SOlivier Certner 	rsv = osd_reserve(osd_jail_slot);
1186bbf8af66SOlivier Certner 
11873186b192SOlivier Certner 	prison_lock(pr);
118840a664a4SOlivier Certner 	old_rules = osd_jail_get(pr, osd_jail_slot);
118940a664a4SOlivier Certner 	osd_jail_set_reserved(pr, osd_jail_slot, rsv, rules);
1190bbf8af66SOlivier Certner 	prison_unlock(pr);
11913186b192SOlivier Certner 	if (old_rules != NULL)
11923d8d91a5SOlivier Certner 		drop_rules(old_rules);
11933186b192SOlivier Certner }
11943186b192SOlivier Certner 
11953186b192SOlivier Certner /*
11963186b192SOlivier Certner  * Assigns empty rules to a jail.
11973186b192SOlivier Certner  */
11983186b192SOlivier Certner static void
11993186b192SOlivier Certner set_empty_rules(struct prison *const pr)
12003186b192SOlivier Certner {
12013186b192SOlivier Certner 	struct rules *const rules = alloc_rules();
12023186b192SOlivier Certner 
12033186b192SOlivier Certner 	set_rules(pr, rules);
1204bbf8af66SOlivier Certner }
1205bbf8af66SOlivier Certner 
1206bbf8af66SOlivier Certner /*
1207bbf8af66SOlivier Certner  * Parse a rules specification and assign them to a jail.
1208bbf8af66SOlivier Certner  *
1209bbf8af66SOlivier Certner  * Returns the same error code as parse_rules() (which see).
1210bbf8af66SOlivier Certner  */
1211bbf8af66SOlivier Certner static int
121287c06b7dSOlivier Certner parse_and_set_rules(struct prison *const pr, const char *rules_string,
121387c06b7dSOlivier Certner     struct parse_error **const parse_error)
1214bbf8af66SOlivier Certner {
12153186b192SOlivier Certner 	struct rules *rules;
1216bbf8af66SOlivier Certner 	int error;
1217bbf8af66SOlivier Certner 
121887c06b7dSOlivier Certner 	error = parse_rules(rules_string, &rules, parse_error);
1219bbf8af66SOlivier Certner 	if (error != 0)
1220bbf8af66SOlivier Certner 		return (error);
12213186b192SOlivier Certner 	set_rules(pr, rules);
1222bbf8af66SOlivier Certner 	return (0);
1223bbf8af66SOlivier Certner }
1224bbf8af66SOlivier Certner 
12258aac90f1SBaptiste Daroussin static int
122611ba1f2fSOlivier Certner mac_do_sysctl_rules(SYSCTL_HANDLER_ARGS)
12278aac90f1SBaptiste Daroussin {
1228292c8149SOlivier Certner 	char *const buf = malloc(MAC_RULE_STRING_LEN, M_DO, M_WAITOK);
122953d2e0d4SOlivier Certner 	struct prison *const td_pr = req->td->td_ucred->cr_prison;
12308aac90f1SBaptiste Daroussin 	struct prison *pr;
123102ed945cSOlivier Certner 	struct rules *rules;
123287c06b7dSOlivier Certner 	struct parse_error *parse_error;
12338aac90f1SBaptiste Daroussin 	int error;
12348aac90f1SBaptiste Daroussin 
123553d2e0d4SOlivier Certner 	rules = find_rules(td_pr, &pr);
1236292c8149SOlivier Certner 	strlcpy(buf, rules->string, MAC_RULE_STRING_LEN);
123783fcbbffSOlivier Certner 	prison_unlock(pr);
12388aac90f1SBaptiste Daroussin 
1239292c8149SOlivier Certner 	error = sysctl_handle_string(oidp, buf, MAC_RULE_STRING_LEN, req);
1240292c8149SOlivier Certner 	if (error != 0 || req->newptr == NULL)
12418aac90f1SBaptiste Daroussin 		goto out;
12428aac90f1SBaptiste Daroussin 
124353d2e0d4SOlivier Certner 	/* Set our prison's rules, not that of the jail we inherited from. */
124487c06b7dSOlivier Certner 	error = parse_and_set_rules(td_pr, buf, &parse_error);
124587c06b7dSOlivier Certner 	if (error != 0) {
124687c06b7dSOlivier Certner 		if (print_parse_error)
124787c06b7dSOlivier Certner 			printf("MAC/do: Parse error at index %zu: %s\n",
124887c06b7dSOlivier Certner 			    parse_error->pos, parse_error->msg);
124987c06b7dSOlivier Certner 		free_parse_error(parse_error);
125087c06b7dSOlivier Certner 	}
12518aac90f1SBaptiste Daroussin out:
1252292c8149SOlivier Certner 	free(buf, M_DO);
12538aac90f1SBaptiste Daroussin 	return (error);
12548aac90f1SBaptiste Daroussin }
12558aac90f1SBaptiste Daroussin 
12568aac90f1SBaptiste Daroussin SYSCTL_PROC(_security_mac_do, OID_AUTO, rules,
1257b3f93680SOlivier Certner     CTLTYPE_STRING|CTLFLAG_RW|CTLFLAG_PRISON|CTLFLAG_MPSAFE,
125811ba1f2fSOlivier Certner     0, 0, mac_do_sysctl_rules, "A",
12598aac90f1SBaptiste Daroussin     "Rules");
12608aac90f1SBaptiste Daroussin 
12618aac90f1SBaptiste Daroussin 
1262f3a06cedSOlivier Certner SYSCTL_JAIL_PARAM_SYS_SUBNODE(mac, do, CTLFLAG_RW, "Jail MAC/do parameters");
1263f3a06cedSOlivier Certner SYSCTL_JAIL_PARAM_STRING(_mac_do, rules, CTLFLAG_RW, MAC_RULE_STRING_LEN,
12648aac90f1SBaptiste Daroussin     "Jail MAC/do rules");
12658aac90f1SBaptiste Daroussin 
12662b2c19b7SOlivier Certner 
12678aac90f1SBaptiste Daroussin static int
12682b2c19b7SOlivier Certner mac_do_jail_create(void *obj, void *data __unused)
12692b2c19b7SOlivier Certner {
12702b2c19b7SOlivier Certner 	struct prison *const pr = obj;
12712b2c19b7SOlivier Certner 
12722b2c19b7SOlivier Certner 	set_empty_rules(pr);
12732b2c19b7SOlivier Certner 	return (0);
12742b2c19b7SOlivier Certner }
12752b2c19b7SOlivier Certner 
12762b2c19b7SOlivier Certner static int
12772b2c19b7SOlivier Certner mac_do_jail_get(void *obj, void *data)
12788aac90f1SBaptiste Daroussin {
12792a20ce91SOlivier Certner 	struct prison *ppr, *const pr = obj;
12802a20ce91SOlivier Certner 	struct vfsoptlist *const opts = data;
128102ed945cSOlivier Certner 	struct rules *rules;
12828aac90f1SBaptiste Daroussin 	int jsys, error;
12838aac90f1SBaptiste Daroussin 
12848ce57706SOlivier Certner 	rules = find_rules(pr, &ppr);
12852a20ce91SOlivier Certner 
12862a20ce91SOlivier Certner 	jsys = pr == ppr ?
1287c7fc71c6SOlivier Certner 	    (STAILQ_EMPTY(&rules->head) ? JAIL_SYS_DISABLE : JAIL_SYS_NEW) :
12882a20ce91SOlivier Certner 	    JAIL_SYS_INHERIT;
1289f3a06cedSOlivier Certner 	error = vfs_setopt(opts, "mac.do", &jsys, sizeof(jsys));
12908aac90f1SBaptiste Daroussin 	if (error != 0 && error != ENOENT)
12918aac90f1SBaptiste Daroussin 		goto done;
12922a20ce91SOlivier Certner 
1293f3a06cedSOlivier Certner 	error = vfs_setopts(opts, "mac.do.rules", rules->string);
12948aac90f1SBaptiste Daroussin 	if (error != 0 && error != ENOENT)
12958aac90f1SBaptiste Daroussin 		goto done;
12962a20ce91SOlivier Certner 
12978aac90f1SBaptiste Daroussin 	error = 0;
12988aac90f1SBaptiste Daroussin done:
12992a20ce91SOlivier Certner 	prison_unlock(ppr);
13002a20ce91SOlivier Certner 	return (error);
13018aac90f1SBaptiste Daroussin }
13028aac90f1SBaptiste Daroussin 
130311eb3295SOlivier Certner /*
130411eb3295SOlivier Certner  * -1 is used as a sentinel in mac_do_jail_check() and mac_do_jail_set() below.
130511eb3295SOlivier Certner  */
130611eb3295SOlivier Certner _Static_assert(-1 != JAIL_SYS_DISABLE && -1 != JAIL_SYS_NEW &&
130711eb3295SOlivier Certner     -1 != JAIL_SYS_INHERIT,
130811eb3295SOlivier Certner     "mac_do(4) uses -1 as a sentinel for uninitialized 'jsys'.");
130911eb3295SOlivier Certner 
131011eb3295SOlivier Certner /*
131111eb3295SOlivier Certner  * We perform only cheap checks here, i.e., we do not really parse the rules
131211eb3295SOlivier Certner  * specification string, if any.
131311eb3295SOlivier Certner  */
13148aac90f1SBaptiste Daroussin static int
13152b2c19b7SOlivier Certner mac_do_jail_check(void *obj, void *data)
13168aac90f1SBaptiste Daroussin {
13178aac90f1SBaptiste Daroussin 	struct vfsoptlist *opts = data;
13188aac90f1SBaptiste Daroussin 	char *rules_string;
131911eb3295SOlivier Certner 	int error, jsys, size;
13208aac90f1SBaptiste Daroussin 
1321f3a06cedSOlivier Certner 	error = vfs_copyopt(opts, "mac.do", &jsys, sizeof(jsys));
132211eb3295SOlivier Certner 	if (error == ENOENT)
132311eb3295SOlivier Certner 		jsys = -1;
132411eb3295SOlivier Certner 	else {
13258aac90f1SBaptiste Daroussin 		if (error != 0)
13268aac90f1SBaptiste Daroussin 			return (error);
132711eb3295SOlivier Certner 		if (jsys != JAIL_SYS_DISABLE && jsys != JAIL_SYS_NEW &&
132811eb3295SOlivier Certner 		    jsys != JAIL_SYS_INHERIT)
13298aac90f1SBaptiste Daroussin 			return (EINVAL);
13308aac90f1SBaptiste Daroussin 	}
133111eb3295SOlivier Certner 
133211eb3295SOlivier Certner 	/*
133311eb3295SOlivier Certner 	 * We use vfs_getopt() here instead of vfs_getopts() to get the length.
133411eb3295SOlivier Certner 	 * We perform the additional checks done by the latter here, even if
133511eb3295SOlivier Certner 	 * jail_set() calls vfs_getopts() itself later (they becoming
133611eb3295SOlivier Certner 	 * inconsistent wouldn't cause any security problem).
133711eb3295SOlivier Certner 	 */
133811eb3295SOlivier Certner 	error = vfs_getopt(opts, "mac.do.rules", (void**)&rules_string, &size);
133911eb3295SOlivier Certner 	if (error == ENOENT) {
134011eb3295SOlivier Certner 		/*
134111eb3295SOlivier Certner 		 * Default (in absence of "mac.do.rules") is to disable (and, in
134211eb3295SOlivier Certner 		 * particular, not inherit).
134311eb3295SOlivier Certner 		 */
134411eb3295SOlivier Certner 		if (jsys == -1)
134511eb3295SOlivier Certner 			jsys = JAIL_SYS_DISABLE;
134611eb3295SOlivier Certner 
134711eb3295SOlivier Certner 		if (jsys == JAIL_SYS_NEW) {
134811eb3295SOlivier Certner 			vfs_opterror(opts, "'mac.do.rules' must be specified "
134911eb3295SOlivier Certner 			    "given 'mac.do''s value");
135011eb3295SOlivier Certner 			return (EINVAL);
135111eb3295SOlivier Certner 		}
135211eb3295SOlivier Certner 
135311eb3295SOlivier Certner 		/* Absence of "mac.do.rules" at this point is OK. */
135411eb3295SOlivier Certner 		error = 0;
135511eb3295SOlivier Certner 	} else {
13568aac90f1SBaptiste Daroussin 		if (error != 0)
13578aac90f1SBaptiste Daroussin 			return (error);
135811eb3295SOlivier Certner 
135911eb3295SOlivier Certner 		/* Not a proper string. */
136011eb3295SOlivier Certner 		if (size == 0 || rules_string[size - 1] != '\0') {
136111eb3295SOlivier Certner 			vfs_opterror(opts, "'mac.do.rules' not a proper string");
136211eb3295SOlivier Certner 			return (EINVAL);
136311eb3295SOlivier Certner 		}
136411eb3295SOlivier Certner 
136511eb3295SOlivier Certner 		if (size > MAC_RULE_STRING_LEN) {
136611eb3295SOlivier Certner 			vfs_opterror(opts, "'mdo.rules' too long");
13678aac90f1SBaptiste Daroussin 			return (ENAMETOOLONG);
13688aac90f1SBaptiste Daroussin 		}
136911eb3295SOlivier Certner 
137011eb3295SOlivier Certner 		if (jsys == -1)
137111eb3295SOlivier Certner 			/* Default (if "mac.do.rules" is present). */
137211eb3295SOlivier Certner 			jsys = rules_string[0] == '\0' ? JAIL_SYS_DISABLE :
137311eb3295SOlivier Certner 			    JAIL_SYS_NEW;
137411eb3295SOlivier Certner 
137511eb3295SOlivier Certner 		/*
137611eb3295SOlivier Certner 		 * Be liberal and accept JAIL_SYS_DISABLE and JAIL_SYS_INHERIT
137711eb3295SOlivier Certner 		 * with an explicit empty rules specification.
137811eb3295SOlivier Certner 		 */
137911eb3295SOlivier Certner 		switch (jsys) {
138011eb3295SOlivier Certner 		case JAIL_SYS_DISABLE:
138111eb3295SOlivier Certner 		case JAIL_SYS_INHERIT:
138211eb3295SOlivier Certner 			if (rules_string[0] != '\0') {
138311eb3295SOlivier Certner 				vfs_opterror(opts, "'mac.do.rules' specified "
138411eb3295SOlivier Certner 				    "but should not given 'mac.do''s value");
138511eb3295SOlivier Certner 				return (EINVAL);
13868aac90f1SBaptiste Daroussin 			}
138711eb3295SOlivier Certner 			break;
138811eb3295SOlivier Certner 		}
138911eb3295SOlivier Certner 	}
139011eb3295SOlivier Certner 
13918aac90f1SBaptiste Daroussin 	return (error);
13928aac90f1SBaptiste Daroussin }
13938aac90f1SBaptiste Daroussin 
13942b2c19b7SOlivier Certner static int
13952b2c19b7SOlivier Certner mac_do_jail_set(void *obj, void *data)
13962b2c19b7SOlivier Certner {
13972b2c19b7SOlivier Certner 	struct prison *pr = obj;
13982b2c19b7SOlivier Certner 	struct vfsoptlist *opts = data;
13992b2c19b7SOlivier Certner 	char *rules_string;
140087c06b7dSOlivier Certner 	struct parse_error *parse_error;
140111eb3295SOlivier Certner 	int error, jsys;
140211eb3295SOlivier Certner 
140311eb3295SOlivier Certner 	/*
140411eb3295SOlivier Certner 	 * The invariants checks used below correspond to what has already been
140511eb3295SOlivier Certner 	 * checked in jail_check() above.
140611eb3295SOlivier Certner 	 */
14072b2c19b7SOlivier Certner 
1408f3a06cedSOlivier Certner 	error = vfs_copyopt(opts, "mac.do", &jsys, sizeof(jsys));
140911eb3295SOlivier Certner 	MPASS(error == 0 || error == ENOENT);
141011eb3295SOlivier Certner 	if (error != 0)
141111eb3295SOlivier Certner 		jsys = -1; /* Mark unfilled. */
141211eb3295SOlivier Certner 
141311eb3295SOlivier Certner 	rules_string = vfs_getopts(opts, "mac.do.rules", &error);
141411eb3295SOlivier Certner 	MPASS(error == 0 || error == ENOENT);
141511eb3295SOlivier Certner 	if (error == 0) {
141611eb3295SOlivier Certner 		MPASS(strlen(rules_string) < MAC_RULE_STRING_LEN);
141711eb3295SOlivier Certner 		if (jsys == -1)
141811eb3295SOlivier Certner 			/* Default (if "mac.do.rules" is present). */
141911eb3295SOlivier Certner 			jsys = rules_string[0] == '\0' ? JAIL_SYS_DISABLE :
142011eb3295SOlivier Certner 			    JAIL_SYS_NEW;
14212b2c19b7SOlivier Certner 		else
142211eb3295SOlivier Certner 			MPASS(jsys == JAIL_SYS_NEW ||
142311eb3295SOlivier Certner 			    ((jsys == JAIL_SYS_DISABLE ||
142411eb3295SOlivier Certner 			    jsys == JAIL_SYS_INHERIT) &&
142511eb3295SOlivier Certner 			    rules_string[0] == '\0'));
142611eb3295SOlivier Certner 	} else {
142711eb3295SOlivier Certner 		MPASS(jsys != JAIL_SYS_NEW);
142811eb3295SOlivier Certner 		if (jsys == -1)
142911eb3295SOlivier Certner 			/*
143011eb3295SOlivier Certner 			 * Default (in absence of "mac.do.rules") is to disable
143111eb3295SOlivier Certner 			 * (and, in particular, not inherit).
143211eb3295SOlivier Certner 			 */
143311eb3295SOlivier Certner 			jsys = JAIL_SYS_DISABLE;
143411eb3295SOlivier Certner 		/* If disabled, we'll store an empty rule specification. */
143511eb3295SOlivier Certner 		if (jsys == JAIL_SYS_DISABLE)
143611eb3295SOlivier Certner 			rules_string = "";
143711eb3295SOlivier Certner 	}
143811eb3295SOlivier Certner 
14392b2c19b7SOlivier Certner 	switch (jsys) {
14402b2c19b7SOlivier Certner 	case JAIL_SYS_INHERIT:
14412b2c19b7SOlivier Certner 		remove_rules(pr);
14422b2c19b7SOlivier Certner 		error = 0;
14432b2c19b7SOlivier Certner 		break;
144411eb3295SOlivier Certner 	case JAIL_SYS_DISABLE:
14452b2c19b7SOlivier Certner 	case JAIL_SYS_NEW:
144687c06b7dSOlivier Certner 		error = parse_and_set_rules(pr, rules_string, &parse_error);
144787c06b7dSOlivier Certner 		if (error != 0) {
144887c06b7dSOlivier Certner 			vfs_opterror(opts,
144987c06b7dSOlivier Certner 			    "MAC/do: Parse error at index %zu: %s\n",
145087c06b7dSOlivier Certner 			    parse_error->pos, parse_error->msg);
145187c06b7dSOlivier Certner 			free_parse_error(parse_error);
145287c06b7dSOlivier Certner 		}
14532b2c19b7SOlivier Certner 		break;
145411eb3295SOlivier Certner 	default:
145511eb3295SOlivier Certner 		__assert_unreachable();
14562b2c19b7SOlivier Certner 	}
14572b2c19b7SOlivier Certner 	return (error);
14582b2c19b7SOlivier Certner }
14592b2c19b7SOlivier Certner 
1460301eeb10SOlivier Certner /*
1461301eeb10SOlivier Certner  * OSD jail methods.
1462301eeb10SOlivier Certner  *
1463301eeb10SOlivier Certner  * There is no PR_METHOD_REMOVE, as OSD storage is destroyed by the common jail
14648f7e8726SOlivier Certner  * code (see prison_cleanup()), which triggers a run of our dealloc_jail_osd()
1465301eeb10SOlivier Certner  * destructor.
1466301eeb10SOlivier Certner  */
1467301eeb10SOlivier Certner static const osd_method_t osd_methods[PR_MAXMETHOD] = {
14682b2c19b7SOlivier Certner 	[PR_METHOD_CREATE] = mac_do_jail_create,
14692b2c19b7SOlivier Certner 	[PR_METHOD_GET] = mac_do_jail_get,
14702b2c19b7SOlivier Certner 	[PR_METHOD_CHECK] = mac_do_jail_check,
14712b2c19b7SOlivier Certner 	[PR_METHOD_SET] = mac_do_jail_set,
14728aac90f1SBaptiste Daroussin };
1473301eeb10SOlivier Certner 
14742b2c19b7SOlivier Certner 
14758f7e8726SOlivier Certner /*
14768f7e8726SOlivier Certner  * Common header structure.
14778f7e8726SOlivier Certner  *
14788f7e8726SOlivier Certner  * Each structure that is used to pass information between some MAC check
14798f7e8726SOlivier Certner  * function and priv_grant() must start with this header.
14808f7e8726SOlivier Certner  */
14818f7e8726SOlivier Certner struct mac_do_data_header {
14828f7e8726SOlivier Certner 	/* Size of the allocated buffer holding the containing structure. */
14838f7e8726SOlivier Certner 	size_t		 allocated_size;
14848f7e8726SOlivier Certner 	/* Full size of the containing structure. */
14858f7e8726SOlivier Certner 	size_t		 size;
14868f7e8726SOlivier Certner 	/*
14878f7e8726SOlivier Certner 	 * For convenience, we use privilege numbers as an identifier for the
14888f7e8726SOlivier Certner 	 * containing structure's type, since there is one distinct privilege
14898f7e8726SOlivier Certner 	 * for each privilege changing function we are supporting.  0 in 'priv'
14908f7e8726SOlivier Certner 	 * indicates this header is uninitialized.
14918f7e8726SOlivier Certner 	 */
14928f7e8726SOlivier Certner 	int		 priv;
14938f7e8726SOlivier Certner 	/* Rules to apply. */
14948f7e8726SOlivier Certner 	struct rules	*rules;
14958f7e8726SOlivier Certner };
14968f7e8726SOlivier Certner 
14978f7e8726SOlivier Certner /*
14988f7e8726SOlivier Certner  * The case of unusable or absent per-thread data can actually happen as nothing
14998f7e8726SOlivier Certner  * prevents, e.g., priv_check*() with privilege 'priv' to be called standalone,
15008f7e8726SOlivier Certner  * as it is currently by, e.g., the Linux emulator for PRIV_CRED_SETUID.  We
15018f7e8726SOlivier Certner  * interpret such calls to priv_check*() as full, unrestricted requests for
15028f7e8726SOlivier Certner  * 'priv', contrary to what we're doing here for selected operations, and
15038f7e8726SOlivier Certner  * consequently will not grant the requested privilege.
15048f7e8726SOlivier Certner  *
15058f7e8726SOlivier Certner  * Also, we protect ourselves from a concurrent change of 'do_enabled' while
15068f7e8726SOlivier Certner  * a call to setcred() is in progress by storing the rules per-thread
15078f7e8726SOlivier Certner  * which is then consulted by each successive hook so that they all have
15088f7e8726SOlivier Certner  * a coherent view of the specifications, and we empty the slot (actually, mark
15098f7e8726SOlivier Certner  * it as empty) when MAC/do is disabled.
15108f7e8726SOlivier Certner  */
15118f7e8726SOlivier Certner static int
15128f7e8726SOlivier Certner check_data_usable(const void *const data, const size_t size, const int priv)
15138f7e8726SOlivier Certner {
15148f7e8726SOlivier Certner 	const struct mac_do_data_header *const hdr = data;
15158f7e8726SOlivier Certner 
15168f7e8726SOlivier Certner 	if (hdr == NULL || hdr->priv == 0)
15178f7e8726SOlivier Certner 		return (ENOENT);
15188f7e8726SOlivier Certner 	/*
15198f7e8726SOlivier Certner 	 * Impacting changes in the protocols we are based on...  Don't crash in
15208f7e8726SOlivier Certner 	 * production.
15218f7e8726SOlivier Certner 	 */
15228f7e8726SOlivier Certner 	if (hdr->priv != priv) {
15238f7e8726SOlivier Certner 		MPASS(hdr->priv == priv);
15248f7e8726SOlivier Certner 		return (EBUSY);
15258f7e8726SOlivier Certner 	}
15268f7e8726SOlivier Certner 	MPASS(hdr->size == size);
15278f7e8726SOlivier Certner 	MPASS(hdr->size <= hdr->allocated_size);
15288f7e8726SOlivier Certner 	return (0);
15298f7e8726SOlivier Certner }
15308f7e8726SOlivier Certner 
15318f7e8726SOlivier Certner static void
15328f7e8726SOlivier Certner clear_data(void *const data)
15338f7e8726SOlivier Certner {
15348f7e8726SOlivier Certner 	struct mac_do_data_header *const hdr = data;
15358f7e8726SOlivier Certner 
15368f7e8726SOlivier Certner 	if (hdr != NULL) {
15378f7e8726SOlivier Certner 		drop_rules(hdr->rules);
15388f7e8726SOlivier Certner 		/* We don't deallocate so as to save time on next access. */
15398f7e8726SOlivier Certner 		hdr->priv = 0;
15408f7e8726SOlivier Certner 	}
15418f7e8726SOlivier Certner }
15428f7e8726SOlivier Certner 
15438f7e8726SOlivier Certner static void *
15448f7e8726SOlivier Certner fetch_data(void)
15458f7e8726SOlivier Certner {
15468f7e8726SOlivier Certner 	return (osd_thread_get_unlocked(curthread, osd_thread_slot));
15478f7e8726SOlivier Certner }
15488f7e8726SOlivier Certner 
15498f7e8726SOlivier Certner static bool
15508f7e8726SOlivier Certner is_data_reusable(const void *const data, const size_t size)
15518f7e8726SOlivier Certner {
15528f7e8726SOlivier Certner 	const struct mac_do_data_header *const hdr = data;
15538f7e8726SOlivier Certner 
15548f7e8726SOlivier Certner 	return (hdr != NULL && size <= hdr->allocated_size);
15558f7e8726SOlivier Certner }
15568f7e8726SOlivier Certner 
15578f7e8726SOlivier Certner static void
15588f7e8726SOlivier Certner set_data_header(void *const data, const size_t size, const int priv,
15598f7e8726SOlivier Certner     struct rules *const rules)
15608f7e8726SOlivier Certner {
15618f7e8726SOlivier Certner 	struct mac_do_data_header *const hdr = data;
15628f7e8726SOlivier Certner 
15638f7e8726SOlivier Certner 	MPASS(hdr->priv == 0);
15648f7e8726SOlivier Certner 	MPASS(priv != 0);
15658f7e8726SOlivier Certner 	MPASS(size <= hdr->allocated_size);
15668f7e8726SOlivier Certner 	hdr->size = size;
15678f7e8726SOlivier Certner 	hdr->priv = priv;
15688f7e8726SOlivier Certner 	hdr->rules = rules;
15698f7e8726SOlivier Certner }
15708f7e8726SOlivier Certner 
15718f7e8726SOlivier Certner /* The proc lock (and any other non-sleepable lock) must not be held. */
15728f7e8726SOlivier Certner static void *
15738f7e8726SOlivier Certner alloc_data(void *const data, const size_t size)
15748f7e8726SOlivier Certner {
15758f7e8726SOlivier Certner 	struct mac_do_data_header *const hdr = realloc(data, size, M_DO,
15768f7e8726SOlivier Certner 	    M_WAITOK);
15778f7e8726SOlivier Certner 
15788f7e8726SOlivier Certner 	MPASS(size >= sizeof(struct mac_do_data_header));
15798f7e8726SOlivier Certner 	hdr->allocated_size = size;
15808f7e8726SOlivier Certner 	hdr->priv = 0;
15818f7e8726SOlivier Certner 	if (hdr != data) {
15828f7e8726SOlivier Certner 		/*
15838f7e8726SOlivier Certner 		 * This call either reuses the existing memory allocated for the
15848f7e8726SOlivier Certner 		 * slot or tries to allocate some without blocking.
15858f7e8726SOlivier Certner 		 */
15868f7e8726SOlivier Certner 		int error = osd_thread_set(curthread, osd_thread_slot, hdr);
15878f7e8726SOlivier Certner 
15888f7e8726SOlivier Certner 		if (error != 0) {
15898f7e8726SOlivier Certner 			/* Going to make a M_WAITOK allocation. */
15908f7e8726SOlivier Certner 			void **const rsv = osd_reserve(osd_thread_slot);
15918f7e8726SOlivier Certner 
15928f7e8726SOlivier Certner 			error = osd_thread_set_reserved(curthread,
15938f7e8726SOlivier Certner 			    osd_thread_slot, rsv, hdr);
15948f7e8726SOlivier Certner 			MPASS(error == 0);
15958f7e8726SOlivier Certner 		}
15968f7e8726SOlivier Certner 	}
15978f7e8726SOlivier Certner 	return (hdr);
15988f7e8726SOlivier Certner }
15998f7e8726SOlivier Certner 
16008f7e8726SOlivier Certner /* Destructor for 'osd_thread_slot'. */
16018f7e8726SOlivier Certner static void
16028f7e8726SOlivier Certner dealloc_thread_osd(void *const value)
16038f7e8726SOlivier Certner {
16048f7e8726SOlivier Certner 	free(value, M_DO);
16058f7e8726SOlivier Certner }
16068f7e8726SOlivier Certner 
16078f7e8726SOlivier Certner /*
16088f7e8726SOlivier Certner  * Whether to grant access to some primary group according to flags.
16098f7e8726SOlivier Certner  *
16108f7e8726SOlivier Certner  * The passed 'flags' must be those of a rule's matching GID, or the IT_GID type
16118f7e8726SOlivier Certner  * flags when MDF_CURRENT has been matched.
16128f7e8726SOlivier Certner  *
16138f7e8726SOlivier Certner  * Return values:
16148f7e8726SOlivier Certner  * - 0:			Access granted.
16158f7e8726SOlivier Certner  * - EJUSTRETURN:	Flags are agnostic.
16168f7e8726SOlivier Certner  */
16178f7e8726SOlivier Certner static int
16188f7e8726SOlivier Certner grant_primary_group_from_flags(const flags_t flags)
16198f7e8726SOlivier Certner {
16208f7e8726SOlivier Certner 	return ((flags & MDF_PRIMARY) != 0 ? 0 : EJUSTRETURN);
16218f7e8726SOlivier Certner }
16228f7e8726SOlivier Certner 
16238f7e8726SOlivier Certner /*
16248f7e8726SOlivier Certner  * Same as grant_primary_group_from_flags(), but for supplementary groups.
16258f7e8726SOlivier Certner  *
16268f7e8726SOlivier Certner  * Return values:
16278f7e8726SOlivier Certner  * - 0:			Access granted.
16288f7e8726SOlivier Certner  * - EJUSTRETURN:	Flags are agnostic.
16298f7e8726SOlivier Certner  * - EPERM:		Access denied.
16308f7e8726SOlivier Certner  */
1631*f1ddb6fbSOlivier Certner static int __unused
16328f7e8726SOlivier Certner grant_supplementary_group_from_flags(const flags_t flags)
16338f7e8726SOlivier Certner {
16348f7e8726SOlivier Certner 	if ((flags & MDF_SUPP_MASK) != 0)
16358f7e8726SOlivier Certner 		return ((flags & MDF_SUPP_DONT) != 0 ? EPERM : 0);
16368f7e8726SOlivier Certner 
16378f7e8726SOlivier Certner 	return (EJUSTRETURN);
16388f7e8726SOlivier Certner }
16398f7e8726SOlivier Certner 
16408f7e8726SOlivier Certner static int
16418f7e8726SOlivier Certner rule_grant_supplementary_groups(const struct rule *const rule,
16428f7e8726SOlivier Certner     const struct ucred *const old_cred, const struct ucred *const new_cred)
16438f7e8726SOlivier Certner {
16448f7e8726SOlivier Certner 	const gid_t *const old_groups = old_cred->cr_groups;
16458f7e8726SOlivier Certner 	const gid_t *const new_groups = new_cred->cr_groups;
16468f7e8726SOlivier Certner 	const int old_ngroups = old_cred->cr_ngroups;
16478f7e8726SOlivier Certner 	const int new_ngroups = new_cred->cr_ngroups;
16488f7e8726SOlivier Certner 	const flags_t gid_flags = rule->gid_flags;
16498f7e8726SOlivier Certner 	const bool current_has_supp = (gid_flags & MDF_CURRENT) != 0 &&
16508f7e8726SOlivier Certner 	    (gid_flags & MDF_SUPP_MASK) != 0;
16518f7e8726SOlivier Certner 	id_nb_t rule_idx = 0;
16528f7e8726SOlivier Certner 	int old_idx = 1, new_idx = 1;
16538f7e8726SOlivier Certner 
16548f7e8726SOlivier Certner 	if ((gid_flags & MDF_ANY_SUPP) != 0 &&
16558f7e8726SOlivier Certner 	    (gid_flags & MDF_MAY_REJ_SUPP) == 0)
16568f7e8726SOlivier Certner 		/*
16578f7e8726SOlivier Certner 		 * Any set of supplementary groups is accepted, no need to loop
16588f7e8726SOlivier Certner 		 * over them.
16598f7e8726SOlivier Certner 		 */
16608f7e8726SOlivier Certner 		return (0);
16618f7e8726SOlivier Certner 
16628f7e8726SOlivier Certner 	for (; new_idx < new_ngroups; ++new_idx) {
16638f7e8726SOlivier Certner 		const gid_t gid = new_groups[new_idx];
16648f7e8726SOlivier Certner 		bool may_accept = false;
16658f7e8726SOlivier Certner 
16668f7e8726SOlivier Certner 		if ((gid_flags & MDF_ANY_SUPP) != 0)
16678f7e8726SOlivier Certner 			may_accept = true;
16688f7e8726SOlivier Certner 
16698f7e8726SOlivier Certner 		/* Do we have to check for the current supplementary groups? */
16708f7e8726SOlivier Certner 		if (current_has_supp) {
16718f7e8726SOlivier Certner 			/*
16728f7e8726SOlivier Certner 			 * Linear search, as both supplementary groups arrays
16738f7e8726SOlivier Certner 			 * are sorted.  Advancing 'old_idx' with a binary search
16748f7e8726SOlivier Certner 			 * on absence of MDF_SUPP_MUST doesn't seem worth it in
16758f7e8726SOlivier Certner 			 * practice.
16768f7e8726SOlivier Certner 			 */
16778f7e8726SOlivier Certner 			for (; old_idx < old_ngroups; ++old_idx) {
16788f7e8726SOlivier Certner 				const gid_t old_gid = old_groups[old_idx];
16798f7e8726SOlivier Certner 
16808f7e8726SOlivier Certner 				if (old_gid < gid) {
16818f7e8726SOlivier Certner 					/* Mandatory but absent. */
16828f7e8726SOlivier Certner 					if ((gid_flags & MDF_SUPP_MUST) != 0)
16838f7e8726SOlivier Certner 						return (EPERM);
16848f7e8726SOlivier Certner 				} else if (old_gid == gid) {
16858f7e8726SOlivier Certner 					switch (gid_flags & MDF_SUPP_MASK) {
16868f7e8726SOlivier Certner 					case MDF_SUPP_DONT:
16878f7e8726SOlivier Certner 						/* Present but forbidden. */
16888f7e8726SOlivier Certner 						return (EPERM);
16898f7e8726SOlivier Certner 					case MDF_SUPP_ALLOW:
16908f7e8726SOlivier Certner 					case MDF_SUPP_MUST:
16918f7e8726SOlivier Certner 						may_accept = true;
16928f7e8726SOlivier Certner 						break;
16938f7e8726SOlivier Certner 					default:
16948f7e8726SOlivier Certner #ifdef INVARIANTS
16958f7e8726SOlivier Certner 						__assert_unreachable();
16968f7e8726SOlivier Certner #else
16978f7e8726SOlivier Certner 						/* Better be safe than sorry. */
16988f7e8726SOlivier Certner 						return (EPERM);
16998f7e8726SOlivier Certner #endif
17008f7e8726SOlivier Certner 					}
17018f7e8726SOlivier Certner 					++old_idx;
17028f7e8726SOlivier Certner 					break;
17038f7e8726SOlivier Certner 				}
17048f7e8726SOlivier Certner 				else
17058f7e8726SOlivier Certner 					break;
17068f7e8726SOlivier Certner 			}
17078f7e8726SOlivier Certner 		}
17088f7e8726SOlivier Certner 
17098f7e8726SOlivier Certner 		/*
17108f7e8726SOlivier Certner 		 * Search by GID for a corresponding 'struct id_spec'.
17118f7e8726SOlivier Certner 		 *
17128f7e8726SOlivier Certner 		 * Again, linear search, with same note on not using binary
17138f7e8726SOlivier Certner 		 * search optimization as above (the trigger would be absence of
17148f7e8726SOlivier Certner 		 * MDF_EXPLICIT_SUPP_MUST this time).
17158f7e8726SOlivier Certner 		 */
17168f7e8726SOlivier Certner 		for (; rule_idx < rule->gids_nb; ++rule_idx) {
17178f7e8726SOlivier Certner 			const struct id_spec is = rule->gids[rule_idx];
17188f7e8726SOlivier Certner 
17198f7e8726SOlivier Certner 			if (is.id < gid) {
17208f7e8726SOlivier Certner 				/* Mandatory but absent. */
17218f7e8726SOlivier Certner 				if ((is.flags & MDF_SUPP_MUST) != 0)
17228f7e8726SOlivier Certner 					return (EPERM);
17238f7e8726SOlivier Certner 			} else if (is.id == gid) {
17248f7e8726SOlivier Certner 				switch (is.flags & MDF_SUPP_MASK) {
17258f7e8726SOlivier Certner 				case MDF_SUPP_DONT:
17268f7e8726SOlivier Certner 					/* Present but forbidden. */
17278f7e8726SOlivier Certner 					return (EPERM);
17288f7e8726SOlivier Certner 				case MDF_SUPP_ALLOW:
17298f7e8726SOlivier Certner 				case MDF_SUPP_MUST:
17308f7e8726SOlivier Certner 					may_accept = true;
17318f7e8726SOlivier Certner 					break;
17328f7e8726SOlivier Certner 				case 0:
17338f7e8726SOlivier Certner 					/* Primary group only. */
17348f7e8726SOlivier Certner 					break;
17358f7e8726SOlivier Certner 				default:
17368f7e8726SOlivier Certner #ifdef INVARIANTS
17378f7e8726SOlivier Certner 					__assert_unreachable();
17388f7e8726SOlivier Certner #else
17398f7e8726SOlivier Certner 					/* Better be safe than sorry. */
17408f7e8726SOlivier Certner 					return (EPERM);
17418f7e8726SOlivier Certner #endif
17428f7e8726SOlivier Certner 				}
17438f7e8726SOlivier Certner 				++rule_idx;
17448f7e8726SOlivier Certner 				break;
17458f7e8726SOlivier Certner 			}
17468f7e8726SOlivier Certner 			else
17478f7e8726SOlivier Certner 				break;
17488f7e8726SOlivier Certner 		}
17498f7e8726SOlivier Certner 
17508f7e8726SOlivier Certner 		/* 'gid' wasn't explicitly accepted. */
17518f7e8726SOlivier Certner 		if (!may_accept)
17528f7e8726SOlivier Certner 			return (EPERM);
17538f7e8726SOlivier Certner 	}
17548f7e8726SOlivier Certner 
17558f7e8726SOlivier Certner 	/*
17568f7e8726SOlivier Certner 	 * If we must have all current groups and we didn't browse all
17578f7e8726SOlivier Certner 	 * of them at this point (because the remaining ones have GIDs
17588f7e8726SOlivier Certner 	 * greater than the last requested group), we are simply missing
17598f7e8726SOlivier Certner 	 * them.
17608f7e8726SOlivier Certner 	 */
17618f7e8726SOlivier Certner 	if ((gid_flags & MDF_CURRENT) != 0 &&
17628f7e8726SOlivier Certner 	    (gid_flags & MDF_SUPP_MUST) != 0 &&
17638f7e8726SOlivier Certner 	    old_idx < old_ngroups)
17648f7e8726SOlivier Certner 		return (EPERM);
17658f7e8726SOlivier Certner 	/*
17668f7e8726SOlivier Certner 	 * Similarly, we have to finish browsing all GIDs from the rule
17678f7e8726SOlivier Certner 	 * in case some are marked mandatory.
17688f7e8726SOlivier Certner 	 */
17698f7e8726SOlivier Certner 	if ((gid_flags & MDF_EXPLICIT_SUPP_MUST) != 0) {
17708f7e8726SOlivier Certner 		for (; rule_idx < rule->gids_nb; ++rule_idx) {
17718f7e8726SOlivier Certner 			const struct id_spec is = rule->gids[rule_idx];
17728f7e8726SOlivier Certner 
17738f7e8726SOlivier Certner 			if ((is.flags & MDF_SUPP_MUST) != 0)
17748f7e8726SOlivier Certner 				return (EPERM);
17758f7e8726SOlivier Certner 		}
17768f7e8726SOlivier Certner 	}
17778f7e8726SOlivier Certner 
17788f7e8726SOlivier Certner 	return (0);
17798f7e8726SOlivier Certner }
17808f7e8726SOlivier Certner 
17818f7e8726SOlivier Certner static int
17828f7e8726SOlivier Certner rule_grant_primary_group(const struct rule *const rule,
17838f7e8726SOlivier Certner     const struct ucred *const old_cred, const gid_t gid)
17848f7e8726SOlivier Certner {
17858f7e8726SOlivier Certner 	struct id_spec gid_is = {.flags = 0};
17868f7e8726SOlivier Certner 	const struct id_spec *found_is;
17878f7e8726SOlivier Certner 	int error;
17888f7e8726SOlivier Certner 
17898f7e8726SOlivier Certner 	if ((rule->gid_flags & MDF_ANY) != 0)
17908f7e8726SOlivier Certner 		return (0);
17918f7e8726SOlivier Certner 
17928f7e8726SOlivier Certner 	/* Was MDF_CURRENT specified, and is 'gid' a current GID? */
17938f7e8726SOlivier Certner 	if ((rule->gid_flags & MDF_CURRENT) != 0 &&
17948f7e8726SOlivier Certner 	    group_is_primary(gid, old_cred)) {
17958f7e8726SOlivier Certner 		error = grant_primary_group_from_flags(rule->gid_flags);
17968f7e8726SOlivier Certner 		if (error == 0)
17978f7e8726SOlivier Certner 			return (0);
17988f7e8726SOlivier Certner 	}
17998f7e8726SOlivier Certner 
18008f7e8726SOlivier Certner 	/* Search by GID for a corresponding 'struct id_spec'. */
18018f7e8726SOlivier Certner 	gid_is.id = gid;
18028f7e8726SOlivier Certner 	found_is = bsearch(&gid_is, rule->gids, rule->gids_nb,
18038f7e8726SOlivier Certner 	    sizeof(*rule->gids), id_spec_cmp);
18048f7e8726SOlivier Certner 
18058f7e8726SOlivier Certner 	if (found_is != NULL) {
18068f7e8726SOlivier Certner 		error = grant_primary_group_from_flags(found_is->flags);
18078f7e8726SOlivier Certner 		if (error == 0)
18088f7e8726SOlivier Certner 			return (0);
18098f7e8726SOlivier Certner 	}
18108f7e8726SOlivier Certner 
18118f7e8726SOlivier Certner 	return (EPERM);
18128f7e8726SOlivier Certner }
18138f7e8726SOlivier Certner 
18148f7e8726SOlivier Certner static int
18158f7e8726SOlivier Certner rule_grant_primary_groups(const struct rule *const rule,
18168f7e8726SOlivier Certner     const struct ucred *const old_cred, const struct ucred *const new_cred)
18178f7e8726SOlivier Certner {
18188f7e8726SOlivier Certner 	int error;
18198f7e8726SOlivier Certner 
18208f7e8726SOlivier Certner 	/* Shortcut. */
18218f7e8726SOlivier Certner 	if ((rule->gid_flags & MDF_ANY) != 0)
18228f7e8726SOlivier Certner 		return (0);
18238f7e8726SOlivier Certner 
18248f7e8726SOlivier Certner 	error = rule_grant_primary_group(rule, old_cred, new_cred->cr_gid);
18258f7e8726SOlivier Certner 	if (error != 0)
18268f7e8726SOlivier Certner 		return (error);
18278f7e8726SOlivier Certner 	error = rule_grant_primary_group(rule, old_cred, new_cred->cr_rgid);
18288f7e8726SOlivier Certner 	if (error != 0)
18298f7e8726SOlivier Certner 		return (error);
18308f7e8726SOlivier Certner 	error = rule_grant_primary_group(rule, old_cred, new_cred->cr_svgid);
18318f7e8726SOlivier Certner 	if (error != 0)
18328f7e8726SOlivier Certner 		return (error);
18338f7e8726SOlivier Certner 	return (0);
18348f7e8726SOlivier Certner }
18358f7e8726SOlivier Certner 
18368f7e8726SOlivier Certner static bool
18378f7e8726SOlivier Certner user_is_current(const uid_t uid, const struct ucred *const old_cred)
18388f7e8726SOlivier Certner {
18398f7e8726SOlivier Certner 	return (uid == old_cred->cr_uid || uid == old_cred->cr_ruid ||
18408f7e8726SOlivier Certner 	    uid == old_cred->cr_svuid);
18418f7e8726SOlivier Certner }
18428f7e8726SOlivier Certner 
18438f7e8726SOlivier Certner static int
18448f7e8726SOlivier Certner rule_grant_user(const struct rule *const rule,
18458f7e8726SOlivier Certner     const struct ucred *const old_cred, const uid_t uid)
18468f7e8726SOlivier Certner {
18478f7e8726SOlivier Certner 	struct id_spec uid_is = {.flags = 0};
18488f7e8726SOlivier Certner 	const struct id_spec *found_is;
18498f7e8726SOlivier Certner 
18508f7e8726SOlivier Certner 	if ((rule->uid_flags & MDF_ANY) != 0)
18518f7e8726SOlivier Certner 		return (0);
18528f7e8726SOlivier Certner 
18538f7e8726SOlivier Certner 	/* Was MDF_CURRENT specified, and is 'uid' a current UID? */
18548f7e8726SOlivier Certner 	if ((rule->uid_flags & MDF_CURRENT) != 0 &&
18558f7e8726SOlivier Certner 	    user_is_current(uid, old_cred))
18568f7e8726SOlivier Certner 		return (0);
18578f7e8726SOlivier Certner 
18588f7e8726SOlivier Certner 	/* Search by UID for a corresponding 'struct id_spec'. */
18598f7e8726SOlivier Certner 	uid_is.id = uid;
18608f7e8726SOlivier Certner 	found_is = bsearch(&uid_is, rule->uids, rule->uids_nb,
18618f7e8726SOlivier Certner 	    sizeof(*rule->uids), id_spec_cmp);
18628f7e8726SOlivier Certner 
18638f7e8726SOlivier Certner 	if (found_is != NULL)
18648f7e8726SOlivier Certner 		return (0);
18658f7e8726SOlivier Certner 
18668f7e8726SOlivier Certner 	return (EPERM);
18678f7e8726SOlivier Certner }
18688f7e8726SOlivier Certner 
18698f7e8726SOlivier Certner static int
18708f7e8726SOlivier Certner rule_grant_users(const struct rule *const rule,
18718f7e8726SOlivier Certner     const struct ucred *const old_cred, const struct ucred *const new_cred)
18728f7e8726SOlivier Certner {
18738f7e8726SOlivier Certner 	int error;
18748f7e8726SOlivier Certner 
18758f7e8726SOlivier Certner 	/* Shortcut. */
18768f7e8726SOlivier Certner 	if ((rule->uid_flags & MDF_ANY) != 0)
18778f7e8726SOlivier Certner 		return (0);
18788f7e8726SOlivier Certner 
18798f7e8726SOlivier Certner 	error = rule_grant_user(rule, old_cred, new_cred->cr_uid);
18808f7e8726SOlivier Certner 	if (error != 0)
18818f7e8726SOlivier Certner 		return (error);
18828f7e8726SOlivier Certner 	error = rule_grant_user(rule, old_cred, new_cred->cr_ruid);
18838f7e8726SOlivier Certner 	if (error != 0)
18848f7e8726SOlivier Certner 		return (error);
18858f7e8726SOlivier Certner 	error = rule_grant_user(rule, old_cred, new_cred->cr_svuid);
18868f7e8726SOlivier Certner 	if (error != 0)
18878f7e8726SOlivier Certner 		return (error);
18888f7e8726SOlivier Certner 
18898f7e8726SOlivier Certner 	return (0);
18908f7e8726SOlivier Certner }
18918f7e8726SOlivier Certner 
18928f7e8726SOlivier Certner static int
18938f7e8726SOlivier Certner rule_grant_setcred(const struct rule *const rule,
18948f7e8726SOlivier Certner     const struct ucred *const old_cred, const struct ucred *const new_cred)
18958f7e8726SOlivier Certner {
18968f7e8726SOlivier Certner 	int error;
18978f7e8726SOlivier Certner 
18988f7e8726SOlivier Certner 	error = rule_grant_users(rule, old_cred, new_cred);
18998f7e8726SOlivier Certner 	if (error != 0)
19008f7e8726SOlivier Certner 		return (error);
19018f7e8726SOlivier Certner 	error = rule_grant_primary_groups(rule, old_cred, new_cred);
19028f7e8726SOlivier Certner 	if (error != 0)
19038f7e8726SOlivier Certner 		return (error);
19048f7e8726SOlivier Certner 	error = rule_grant_supplementary_groups(rule, old_cred, new_cred);
19058f7e8726SOlivier Certner 	if (error != 0)
19068f7e8726SOlivier Certner 		return (error);
19078f7e8726SOlivier Certner 
19088f7e8726SOlivier Certner 	return (0);
19098f7e8726SOlivier Certner }
19108f7e8726SOlivier Certner 
19118f7e8726SOlivier Certner static bool
19128f7e8726SOlivier Certner rule_applies(const struct rule *const rule, const struct ucred *const cred)
19138f7e8726SOlivier Certner {
1914de701f9bSOlivier Certner 	if (rule->from_type == IT_UID && rule->from_id == cred->cr_ruid)
19158f7e8726SOlivier Certner 		return (true);
1916de701f9bSOlivier Certner 	if (rule->from_type == IT_GID && realgroupmember(rule->from_id, cred))
19178f7e8726SOlivier Certner 		return (true);
19188f7e8726SOlivier Certner 	return (false);
19198f7e8726SOlivier Certner }
19208f7e8726SOlivier Certner 
19218f7e8726SOlivier Certner /*
19228f7e8726SOlivier Certner  * To pass data between check_setcred() and priv_grant() (on PRIV_CRED_SETCRED).
19238f7e8726SOlivier Certner  */
19248f7e8726SOlivier Certner struct mac_do_setcred_data {
19258f7e8726SOlivier Certner 	struct mac_do_data_header hdr;
19268f7e8726SOlivier Certner 	const struct ucred *new_cred;
19278f7e8726SOlivier Certner 	u_int setcred_flags;
19288f7e8726SOlivier Certner };
19298f7e8726SOlivier Certner 
19308f7e8726SOlivier Certner static int
19318f7e8726SOlivier Certner mac_do_priv_grant(struct ucred *cred, int priv)
19328f7e8726SOlivier Certner {
19338f7e8726SOlivier Certner 	struct mac_do_setcred_data *const data = fetch_data();
19348f7e8726SOlivier Certner 	const struct rules *rules;
19358f7e8726SOlivier Certner 	const struct ucred *new_cred;
19368f7e8726SOlivier Certner 	const struct rule *rule;
19378f7e8726SOlivier Certner 	u_int setcred_flags;
19388f7e8726SOlivier Certner 	int error;
19398f7e8726SOlivier Certner 
19408f7e8726SOlivier Certner 	/* Bail out fast if we aren't concerned. */
19418f7e8726SOlivier Certner 	if (priv != PRIV_CRED_SETCRED)
19428f7e8726SOlivier Certner 		return (EPERM);
19438f7e8726SOlivier Certner 
19448f7e8726SOlivier Certner 	/*
19458f7e8726SOlivier Certner 	 * Do we have to do something?
19468f7e8726SOlivier Certner 	 */
19478f7e8726SOlivier Certner 	if (check_data_usable(data, sizeof(*data), priv) != 0)
19488f7e8726SOlivier Certner 		/* No. */
19498f7e8726SOlivier Certner 		return (EPERM);
19508f7e8726SOlivier Certner 
19518f7e8726SOlivier Certner 	rules = data->hdr.rules;
19528f7e8726SOlivier Certner 	new_cred = data->new_cred;
19538f7e8726SOlivier Certner 	KASSERT(new_cred != NULL,
19548f7e8726SOlivier Certner 	    ("priv_check*() called before mac_cred_check_setcred()"));
19558f7e8726SOlivier Certner 	setcred_flags = data->setcred_flags;
19568f7e8726SOlivier Certner 
19578f7e8726SOlivier Certner 	/*
19588f7e8726SOlivier Certner 	 * Explicitly check that only the flags we currently support are present
19598f7e8726SOlivier Certner 	 * in order to avoid accepting transitions with other changes than those
19608f7e8726SOlivier Certner 	 * we are actually going to check.  Currently, this rules out the
19618f7e8726SOlivier Certner 	 * SETCREDF_MAC_LABEL flag.  This may be improved by adding code
19628f7e8726SOlivier Certner 	 * actually checking whether the requested label and the current one
19638f7e8726SOlivier Certner 	 * would differ.
19648f7e8726SOlivier Certner 	 */
19658f7e8726SOlivier Certner 	if ((setcred_flags & ~(SETCREDF_UID | SETCREDF_RUID | SETCREDF_SVUID |
19668f7e8726SOlivier Certner 	    SETCREDF_GID | SETCREDF_RGID | SETCREDF_SVGID |
19678f7e8726SOlivier Certner 	    SETCREDF_SUPP_GROUPS)) != 0)
19688f7e8726SOlivier Certner 		return (EPERM);
19698f7e8726SOlivier Certner 
19708f7e8726SOlivier Certner 	/*
19718f7e8726SOlivier Certner 	 * Browse rules, and for those that match the requestor, call specific
19728f7e8726SOlivier Certner 	 * privilege granting functions interpreting the "to"/"target" part.
19738f7e8726SOlivier Certner 	 */
19748f7e8726SOlivier Certner 	error = EPERM;
1975c7fc71c6SOlivier Certner 	STAILQ_FOREACH(rule, &rules->head, r_entries)
19768f7e8726SOlivier Certner 	    if (rule_applies(rule, cred)) {
19778f7e8726SOlivier Certner 		    error = rule_grant_setcred(rule, cred, new_cred);
19788f7e8726SOlivier Certner 		    if (error != EPERM)
19798f7e8726SOlivier Certner 			    break;
19808f7e8726SOlivier Certner 	    }
19818f7e8726SOlivier Certner 
19828f7e8726SOlivier Certner 	return (error);
19838f7e8726SOlivier Certner }
19848f7e8726SOlivier Certner 
19858f7e8726SOlivier Certner static int
19868f7e8726SOlivier Certner check_proc(void)
19878f7e8726SOlivier Certner {
19888f7e8726SOlivier Certner 	char *path, *to_free;
19898f7e8726SOlivier Certner 	int error;
19908f7e8726SOlivier Certner 
19918f7e8726SOlivier Certner 	/*
19928f7e8726SOlivier Certner 	 * Only grant privileges if requested by the right executable.
19938f7e8726SOlivier Certner 	 *
19948f7e8726SOlivier Certner 	 * XXXOC: We may want to base this check on a tunable path and/or
19958f7e8726SOlivier Certner 	 * a specific MAC label.  Going even further, e.g., envisioning to
19968f7e8726SOlivier Certner 	 * completely replace the path check with the latter, we would need to
19978f7e8726SOlivier Certner 	 * install FreeBSD on a FS with multilabel enabled by default, which in
19988f7e8726SOlivier Certner 	 * practice entails adding an option to ZFS to set MNT_MULTILABEL
19998f7e8726SOlivier Certner 	 * automatically on mounts, ensuring that root (and more if using
20008f7e8726SOlivier Certner 	 * different partitions) ZFS or UFS filesystems are created with
20018f7e8726SOlivier Certner 	 * multilabel turned on, and having the installation procedure support
20028f7e8726SOlivier Certner 	 * setting a MAC label per file (perhaps via additions to mtree(1)).  So
20038f7e8726SOlivier Certner 	 * this probably isn't going to happen overnight, if ever.
20048f7e8726SOlivier Certner 	 */
20058f7e8726SOlivier Certner 	if (vn_fullpath(curproc->p_textvp, &path, &to_free) != 0)
20068f7e8726SOlivier Certner 		return (EPERM);
20078f7e8726SOlivier Certner 	error = strcmp(path, "/usr/bin/mdo") == 0 ? 0 : EPERM;
20088f7e8726SOlivier Certner 	free(to_free, M_TEMP);
20098f7e8726SOlivier Certner 	return (error);
20108f7e8726SOlivier Certner }
20118f7e8726SOlivier Certner 
20128f7e8726SOlivier Certner static void
20138f7e8726SOlivier Certner mac_do_setcred_enter(void)
20148f7e8726SOlivier Certner {
20158f7e8726SOlivier Certner 	struct rules *rules;
20168f7e8726SOlivier Certner 	struct prison *pr;
20178f7e8726SOlivier Certner 	struct mac_do_setcred_data * data;
20188f7e8726SOlivier Certner 	int error;
20198f7e8726SOlivier Certner 
20208f7e8726SOlivier Certner 	/*
20218f7e8726SOlivier Certner 	 * If not enabled, don't prepare data.  Other hooks will check for that
20228f7e8726SOlivier Certner 	 * to know if they have to do something.
20238f7e8726SOlivier Certner 	 */
20248f7e8726SOlivier Certner 	if (do_enabled == 0)
20258f7e8726SOlivier Certner 		return;
20268f7e8726SOlivier Certner 
20278f7e8726SOlivier Certner 	/*
20288f7e8726SOlivier Certner 	 * MAC/do only applies to a process launched from a given executable.
20298f7e8726SOlivier Certner 	 * For other processes, we just won't intervene (we don't deny requests,
20308f7e8726SOlivier Certner 	 * nor do we grant privileges to them).
20318f7e8726SOlivier Certner 	 */
20328f7e8726SOlivier Certner 	error = check_proc();
20338f7e8726SOlivier Certner 	if (error != 0)
20348f7e8726SOlivier Certner 		return;
20358f7e8726SOlivier Certner 
20368f7e8726SOlivier Certner 	/*
20378f7e8726SOlivier Certner 	 * Find the currently applicable rules.
20388f7e8726SOlivier Certner 	 */
20398f7e8726SOlivier Certner 	rules = find_rules(curproc->p_ucred->cr_prison, &pr);
20408f7e8726SOlivier Certner 	hold_rules(rules);
20418f7e8726SOlivier Certner 	prison_unlock(pr);
20428f7e8726SOlivier Certner 
20438f7e8726SOlivier Certner 	/*
20448f7e8726SOlivier Certner 	 * Setup thread data to be used by other hooks.
20458f7e8726SOlivier Certner 	 */
20468f7e8726SOlivier Certner 	data = fetch_data();
20478f7e8726SOlivier Certner 	if (!is_data_reusable(data, sizeof(*data)))
20488f7e8726SOlivier Certner 		data = alloc_data(data, sizeof(*data));
20498f7e8726SOlivier Certner 	set_data_header(data, sizeof(*data), PRIV_CRED_SETCRED, rules);
20508f7e8726SOlivier Certner 	/* Not really necessary, but helps to catch programming errors. */
20518f7e8726SOlivier Certner 	data->new_cred = NULL;
20528f7e8726SOlivier Certner 	data->setcred_flags = 0;
20538f7e8726SOlivier Certner }
20548f7e8726SOlivier Certner 
20558f7e8726SOlivier Certner static int
20568f7e8726SOlivier Certner mac_do_check_setcred(u_int flags, const struct ucred *const old_cred,
20578f7e8726SOlivier Certner     struct ucred *const new_cred)
20588f7e8726SOlivier Certner {
20598f7e8726SOlivier Certner 	struct mac_do_setcred_data *const data = fetch_data();
20608f7e8726SOlivier Certner 
20618f7e8726SOlivier Certner 	/*
20628f7e8726SOlivier Certner 	 * Do we have to do something?
20638f7e8726SOlivier Certner 	 */
20648f7e8726SOlivier Certner 	if (check_data_usable(data, sizeof(*data), PRIV_CRED_SETCRED) != 0)
20658f7e8726SOlivier Certner 		/* No. */
20668f7e8726SOlivier Certner 		return (0);
20678f7e8726SOlivier Certner 
20688f7e8726SOlivier Certner 	/*
20698f7e8726SOlivier Certner 	 * Keep track of the setcred() flags and the new credentials for
20708f7e8726SOlivier Certner 	 * priv_check*().
20718f7e8726SOlivier Certner 	 */
20728f7e8726SOlivier Certner 	data->new_cred = new_cred;
20738f7e8726SOlivier Certner 	data->setcred_flags = flags;
20748f7e8726SOlivier Certner 
20758f7e8726SOlivier Certner 	return (0);
20768f7e8726SOlivier Certner }
20778f7e8726SOlivier Certner 
20788f7e8726SOlivier Certner static void
20798f7e8726SOlivier Certner mac_do_setcred_exit(void)
20808f7e8726SOlivier Certner {
20818f7e8726SOlivier Certner 	struct mac_do_setcred_data *const data = fetch_data();
20828f7e8726SOlivier Certner 
20838f7e8726SOlivier Certner 	if (check_data_usable(data, sizeof(*data), PRIV_CRED_SETCRED) == 0)
20848f7e8726SOlivier Certner 		/*
20858f7e8726SOlivier Certner 		 * This doesn't deallocate the small per-thread data storage,
20868f7e8726SOlivier Certner 		 * which can be reused on subsequent calls.  (That data is of
20878f7e8726SOlivier Certner 		 * course deallocated as the current thread dies or this module
20888f7e8726SOlivier Certner 		 * is unloaded.)
20898f7e8726SOlivier Certner 		 */
20908f7e8726SOlivier Certner 		clear_data(data);
20918f7e8726SOlivier Certner }
20928f7e8726SOlivier Certner 
2093301eeb10SOlivier Certner static void
209411ba1f2fSOlivier Certner mac_do_init(struct mac_policy_conf *mpc)
2095301eeb10SOlivier Certner {
20968aac90f1SBaptiste Daroussin 	struct prison *pr;
20978aac90f1SBaptiste Daroussin 
20988f7e8726SOlivier Certner 	osd_jail_slot = osd_jail_register(dealloc_jail_osd, osd_methods);
2099beb5603cSOlivier Certner 	set_empty_rules(&prison0);
21008aac90f1SBaptiste Daroussin 	sx_slock(&allprison_lock);
21013186b192SOlivier Certner 	TAILQ_FOREACH(pr, &allprison, pr_list)
21023186b192SOlivier Certner 	    set_empty_rules(pr);
21038aac90f1SBaptiste Daroussin 	sx_sunlock(&allprison_lock);
21048f7e8726SOlivier Certner 
21058f7e8726SOlivier Certner 	osd_thread_slot = osd_thread_register(dealloc_thread_osd);
21068aac90f1SBaptiste Daroussin }
21078aac90f1SBaptiste Daroussin 
210873cecc0eSOlivier Certner static void
210911ba1f2fSOlivier Certner mac_do_destroy(struct mac_policy_conf *mpc)
211073cecc0eSOlivier Certner {
21118f7e8726SOlivier Certner 	/*
21128f7e8726SOlivier Certner 	 * osd_thread_deregister() must be called before osd_jail_deregister(),
21138f7e8726SOlivier Certner 	 * for the reason explained in dealloc_jail_osd().
21148f7e8726SOlivier Certner 	 */
21158f7e8726SOlivier Certner 	osd_thread_deregister(osd_thread_slot);
211640a664a4SOlivier Certner 	osd_jail_deregister(osd_jail_slot);
211773cecc0eSOlivier Certner }
211873cecc0eSOlivier Certner 
21198aac90f1SBaptiste Daroussin static struct mac_policy_ops do_ops = {
212011ba1f2fSOlivier Certner 	.mpo_init = mac_do_init,
21218f7e8726SOlivier Certner 	.mpo_destroy = mac_do_destroy,
21228f7e8726SOlivier Certner 	.mpo_cred_setcred_enter = mac_do_setcred_enter,
21238f7e8726SOlivier Certner 	.mpo_cred_check_setcred = mac_do_check_setcred,
21248f7e8726SOlivier Certner 	.mpo_cred_setcred_exit = mac_do_setcred_exit,
212511ba1f2fSOlivier Certner 	.mpo_priv_grant = mac_do_priv_grant,
21268aac90f1SBaptiste Daroussin };
21278aac90f1SBaptiste Daroussin 
21288f7e8726SOlivier Certner MAC_POLICY_SET(&do_ops, mac_do, "MAC/do", MPC_LOADTIME_FLAG_UNLOADOK, NULL);
21298aac90f1SBaptiste Daroussin MODULE_VERSION(mac_do, 1);
2130