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