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