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