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