1 /*- 2 * Copyright (c) 2002, 2004 Networks Associates Technology, Inc. 3 * All rights reserved. 4 * 5 * This software was developed for the FreeBSD Project by Network Associates 6 * Laboratories, the Security Research Division of Network Associates, Inc. 7 * under DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), as part of the 8 * DARPA CHATS research program. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 * SUCH DAMAGE. 30 * 31 * $FreeBSD$ 32 */ 33 #include <sys/param.h> 34 #include <sys/errno.h> 35 #include <sys/time.h> 36 #include <sys/sysctl.h> 37 #include <sys/vnode.h> 38 39 #include <security/mac_bsdextended/mac_bsdextended.h> 40 41 #include <grp.h> 42 #include <pwd.h> 43 #include <stdio.h> 44 #include <stdlib.h> 45 #include <string.h> 46 47 #include "ugidfw.h" 48 49 /* 50 * Text format for rules: rules contain subjectand object elements, mode. 51 * Each element takes the form "[not] [uid number] [gid number]". 52 * The total form is "subject [element] object [element] mode [mode]". 53 * At least * one of a uid or gid entry must be present; both may also be 54 * present. 55 */ 56 57 #define MIB "security.mac.bsdextended" 58 59 int 60 bsde_rule_to_string(struct mac_bsdextended_rule *rule, char *buf, size_t buflen) 61 { 62 struct group *grp; 63 struct passwd *pwd; 64 char *cur; 65 size_t left, len; 66 int anymode, unknownmode, truncated; 67 68 cur = buf; 69 left = buflen; 70 truncated = 0; 71 72 if (rule->mbr_subject.mbi_flags & (MBI_UID_DEFINED | 73 MBI_GID_DEFINED)) { 74 len = snprintf(cur, left, "subject "); 75 if (len < 0 || len > left) 76 goto truncated; 77 left -= len; 78 cur += len; 79 80 if (rule->mbr_subject.mbi_flags & MBI_NEGATED) { 81 len = snprintf(cur, left, "not "); 82 if (len < 0 || len > left) 83 goto truncated; 84 left -= len; 85 cur += len; 86 } 87 if (rule->mbr_subject.mbi_flags & MBI_UID_DEFINED) { 88 pwd = getpwuid(rule->mbr_subject.mbi_uid); 89 if (pwd != NULL) { 90 len = snprintf(cur, left, "uid %s ", 91 pwd->pw_name); 92 if (len < 0 || len > left) 93 goto truncated; 94 left -= len; 95 cur += len; 96 } else { 97 len = snprintf(cur, left, "uid %u ", 98 rule->mbr_subject.mbi_uid); 99 if (len < 0 || len > left) 100 goto truncated; 101 left -= len; 102 cur += len; 103 } 104 } 105 if (rule->mbr_subject.mbi_flags & MBI_GID_DEFINED) { 106 grp = getgrgid(rule->mbr_subject.mbi_gid); 107 if (grp != NULL) { 108 len = snprintf(cur, left, "gid %s ", 109 grp->gr_name); 110 if (len < 0 || len > left) 111 goto truncated; 112 left -= len; 113 cur += len; 114 } else { 115 len = snprintf(cur, left, "gid %u ", 116 rule->mbr_subject.mbi_gid); 117 if (len < 0 || len > left) 118 goto truncated; 119 left -= len; 120 cur += len; 121 } 122 } 123 } 124 if (rule->mbr_object.mbi_flags & (MBI_UID_DEFINED | 125 MBI_GID_DEFINED)) { 126 len = snprintf(cur, left, "object "); 127 if (len < 0 || len > left) 128 goto truncated; 129 left -= len; 130 cur += len; 131 132 if (rule->mbr_object.mbi_flags & MBI_NEGATED) { 133 len = snprintf(cur, left, "not "); 134 if (len < 0 || len > left) 135 goto truncated; 136 left -= len; 137 cur += len; 138 } 139 if (rule->mbr_object.mbi_flags & MBI_UID_DEFINED) { 140 pwd = getpwuid(rule->mbr_object.mbi_uid); 141 if (pwd != NULL) { 142 len = snprintf(cur, left, "uid %s ", 143 pwd->pw_name); 144 if (len < 0 || len > left) 145 goto truncated; 146 left -= len; 147 cur += len; 148 } else { 149 len = snprintf(cur, left, "uid %u ", 150 rule->mbr_object.mbi_uid); 151 left -= len; 152 cur += len; 153 } 154 } 155 if (rule->mbr_object.mbi_flags & MBI_GID_DEFINED) { 156 grp = getgrgid(rule->mbr_object.mbi_gid); 157 if (grp != NULL) { 158 len = snprintf(cur, left, "gid %s ", 159 grp->gr_name); 160 if (len < 0 || len > left) 161 goto truncated; 162 left -= len; 163 cur += len; 164 } else { 165 len = snprintf(cur, left, "gid %u ", 166 rule->mbr_object.mbi_gid); 167 if (len < 0 || len > left) 168 goto truncated; 169 left -= len; 170 cur += len; 171 } 172 } 173 } 174 175 len = snprintf(cur, left, "mode "); 176 if (len < 0 || len > left) 177 goto truncated; 178 left -= len; 179 cur += len; 180 181 anymode = (rule->mbr_mode & VALLPERM); 182 unknownmode = (rule->mbr_mode & ~VALLPERM); 183 184 if (rule->mbr_mode & VADMIN) { 185 len = snprintf(cur, left, "a"); 186 if (len < 0 || len > left) 187 goto truncated; 188 189 left -= len; 190 cur += len; 191 } 192 if (rule->mbr_mode & VREAD) { 193 len = snprintf(cur, left, "r"); 194 if (len < 0 || len > left) 195 goto truncated; 196 197 left -= len; 198 cur += len; 199 } 200 if (rule->mbr_mode & VSTAT) { 201 len = snprintf(cur, left, "s"); 202 if (len < 0 || len > left) 203 goto truncated; 204 205 left -= len; 206 cur += len; 207 } 208 if (rule->mbr_mode & VWRITE) { 209 len = snprintf(cur, left, "w"); 210 if (len < 0 || len > left) 211 goto truncated; 212 213 left -= len; 214 cur += len; 215 } 216 if (rule->mbr_mode & VEXEC) { 217 len = snprintf(cur, left, "x"); 218 if (len < 0 || len > left) 219 goto truncated; 220 221 left -= len; 222 cur += len; 223 } 224 if (!anymode) { 225 len = snprintf(cur, left, "n"); 226 if (len < 0 || len > left) 227 goto truncated; 228 229 left -= len; 230 cur += len; 231 } 232 if (unknownmode) { 233 len = snprintf(cur, left, "?"); 234 if (len < 0 || len > left) 235 goto truncated; 236 237 left -= len; 238 cur += len; 239 } 240 241 return (0); 242 243 truncated: 244 return (-1); 245 } 246 247 int 248 bsde_parse_identity(int argc, char *argv[], 249 struct mac_bsdextended_identity *identity, size_t buflen, char *errstr) 250 { 251 struct group *grp; 252 struct passwd *pwd; 253 int uid_seen, gid_seen, not_seen; 254 int current; 255 char *endp; 256 long value; 257 uid_t uid; 258 gid_t gid; 259 size_t len; 260 261 if (argc == 0) { 262 len = snprintf(errstr, buflen, "Identity must not be empty"); 263 return (-1); 264 } 265 266 current = 0; 267 268 /* First element might be "not". */ 269 if (strcmp("not", argv[0]) == 0) { 270 not_seen = 1; 271 current++; 272 } else 273 not_seen = 0; 274 275 if (current >= argc) { 276 len = snprintf(errstr, buflen, "Identity short"); 277 return (-1); 278 } 279 280 uid_seen = 0; 281 uid = 0; 282 gid_seen = 0; 283 gid = 0; 284 285 /* First phrase: uid [uid] or gid[gid]. */ 286 if (strcmp("uid", argv[current]) == 0) { 287 if (current + 2 > argc) { 288 len = snprintf(errstr, buflen, "uid short"); 289 return (-1); 290 } 291 pwd = getpwnam(argv[current+1]); 292 if (pwd != NULL) 293 uid = pwd->pw_uid; 294 else { 295 value = strtol(argv[current+1], &endp, 10); 296 if (*endp != '\0') { 297 len = snprintf(errstr, buflen, 298 "invalid uid: '%s'", 299 argv[current+1]); 300 return (-1); 301 } 302 uid = value; 303 } 304 uid_seen = 1; 305 current += 2; 306 } else if (strcmp("gid", argv[current]) == 0) { 307 if (current + 2 > argc) { 308 len = snprintf(errstr, buflen, "gid short"); 309 return (-1); 310 } 311 grp = getgrnam(argv[current+1]); 312 if (grp != NULL) 313 gid = grp->gr_gid; 314 else { 315 value = strtol(argv[current+1], &endp, 10); 316 if (*endp != '\0') { 317 len = snprintf(errstr, buflen, 318 "invalid gid: '%s'", 319 argv[current+1]); 320 return (-1); 321 } 322 gid = value; 323 } 324 gid_seen = 1; 325 current += 2; 326 } else { 327 len = snprintf(errstr, buflen, "'%s' not expected", 328 argv[current]); 329 return (-1); 330 } 331 332 /* Onto optional second phrase. */ 333 if (current + 1 < argc) { 334 /* Second phrase: uid [uid] or gid [gid], but not a repeat. */ 335 if (strcmp("uid", argv[current]) == 0) { 336 if (uid_seen) { 337 len = snprintf(errstr, buflen, 338 "Only one uid permitted per identity clause"); 339 return (-1); 340 } 341 if (current + 2 > argc) { 342 len = snprintf(errstr, buflen, "uid short"); 343 return (-1); 344 } 345 value = strtol(argv[current+1], &endp, 10); 346 if (*endp != '\0') { 347 len = snprintf(errstr, buflen, "invalid uid: '%s'", 348 argv[current+1]); 349 return (-1); 350 } 351 uid = value; 352 uid_seen = 1; 353 current += 2; 354 } else if (strcmp("gid", argv[current]) == 0) { 355 if (gid_seen) { 356 len = snprintf(errstr, buflen, 357 "Only one gid permitted per identity clause"); 358 return (-1); 359 } 360 if (current + 2 > argc) { 361 len = snprintf(errstr, buflen, "gid short"); 362 return (-1); 363 } 364 value = strtol(argv[current+1], &endp, 10); 365 if (*endp != '\0') { 366 len = snprintf(errstr, buflen, "invalid gid: '%s'", 367 argv[current+1]); 368 return (-1); 369 } 370 gid = value; 371 gid_seen = 1; 372 current += 2; 373 } else { 374 len = snprintf(errstr, buflen, "'%s' not expected", 375 argv[current]); 376 return (-1); 377 } 378 } 379 380 if (current +1 < argc) { 381 len = snprintf(errstr, buflen, "'%s' not expected", 382 argv[current]); 383 return (-1); 384 } 385 386 /* Fill out the identity. */ 387 identity->mbi_flags = 0; 388 389 if (not_seen) 390 identity->mbi_flags |= MBI_NEGATED; 391 392 if (uid_seen) { 393 identity->mbi_flags |= MBI_UID_DEFINED; 394 identity->mbi_uid = uid; 395 } else 396 identity->mbi_uid = 0; 397 398 if (gid_seen) { 399 identity->mbi_flags |= MBI_GID_DEFINED; 400 identity->mbi_gid = gid; 401 } else 402 identity->mbi_gid = 0; 403 404 return (0); 405 } 406 407 int 408 bsde_parse_mode(int argc, char *argv[], mode_t *mode, size_t buflen, 409 char *errstr) 410 { 411 size_t len; 412 int i; 413 414 if (argc == 0) { 415 len = snprintf(errstr, buflen, "mode expects mode value"); 416 return (-1); 417 } 418 419 if (argc != 1) { 420 len = snprintf(errstr, buflen, "'%s' unexpected", argv[1]); 421 return (-1); 422 } 423 424 *mode = 0; 425 for (i = 0; i < strlen(argv[0]); i++) { 426 switch (argv[0][i]) { 427 case 'a': 428 *mode |= VADMIN; 429 break; 430 case 'r': 431 *mode |= VREAD; 432 break; 433 case 's': 434 *mode |= VSTAT; 435 break; 436 case 'w': 437 *mode |= VWRITE; 438 break; 439 case 'x': 440 *mode |= VEXEC; 441 break; 442 case 'n': 443 /* ignore */ 444 break; 445 default: 446 len = snprintf(errstr, buflen, "Unknown mode letter: %c", 447 argv[0][i]); 448 return (-1); 449 } 450 } 451 452 return (0); 453 } 454 455 int 456 bsde_parse_rule(int argc, char *argv[], struct mac_bsdextended_rule *rule, 457 size_t buflen, char *errstr) 458 { 459 int subject, subject_elements, subject_elements_length; 460 int object, object_elements, object_elements_length; 461 int mode, mode_elements, mode_elements_length; 462 int error, i; 463 size_t len; 464 465 bzero(rule, sizeof(*rule)); 466 467 if (argc < 1) { 468 len = snprintf(errstr, buflen, "Rule must begin with subject"); 469 return (-1); 470 } 471 472 if (strcmp(argv[0], "subject") != 0) { 473 len = snprintf(errstr, buflen, "Rule must begin with subject"); 474 return (-1); 475 } 476 subject = 0; 477 subject_elements = 1; 478 479 /* Search forward for object. */ 480 481 object = -1; 482 for (i = 1; i < argc; i++) 483 if (strcmp(argv[i], "object") == 0) 484 object = i; 485 486 if (object == -1) { 487 len = snprintf(errstr, buflen, "Rule must contain an object"); 488 return (-1); 489 } 490 491 /* Search forward for mode. */ 492 mode = -1; 493 for (i = object; i < argc; i++) 494 if (strcmp(argv[i], "mode") == 0) 495 mode = i; 496 497 if (mode == -1) { 498 len = snprintf(errstr, buflen, "Rule must contain mode"); 499 return (-1); 500 } 501 502 subject_elements_length = object - subject - 1; 503 object_elements = object + 1; 504 object_elements_length = mode - object_elements; 505 mode_elements = mode + 1; 506 mode_elements_length = argc - mode_elements; 507 508 error = bsde_parse_identity(subject_elements_length, 509 argv + subject_elements, &rule->mbr_subject, buflen, errstr); 510 if (error) 511 return (-1); 512 513 error = bsde_parse_identity(object_elements_length, 514 argv + object_elements, &rule->mbr_object, buflen, errstr); 515 if (error) 516 return (-1); 517 518 error = bsde_parse_mode(mode_elements_length, argv + mode_elements, 519 &rule->mbr_mode, buflen, errstr); 520 if (error) 521 return (-1); 522 523 return (0); 524 } 525 526 int 527 bsde_parse_rule_string(const char *string, struct mac_bsdextended_rule *rule, 528 size_t buflen, char *errstr) 529 { 530 char *stringdup, *stringp, *argv[20], **ap; 531 int argc, error; 532 533 stringp = stringdup = strdup(string); 534 while (*stringp == ' ' || *stringp == '\t') 535 stringp++; 536 537 argc = 0; 538 for (ap = argv; (*ap = strsep(&stringp, " \t")) != NULL;) { 539 argc++; 540 if (**ap != '\0') 541 if (++ap >= &argv[20]) 542 break; 543 } 544 545 error = bsde_parse_rule(argc, argv, rule, buflen, errstr); 546 547 free(stringdup); 548 549 return (error); 550 } 551 552 int 553 bsde_get_mib(const char *string, int *name, size_t *namelen) 554 { 555 size_t len; 556 int error; 557 558 len = *namelen; 559 error = sysctlnametomib(string, name, &len); 560 if (error) 561 return (error); 562 563 *namelen = len; 564 return (0); 565 } 566 567 int 568 bsde_get_rule_count(size_t buflen, char *errstr) 569 { 570 size_t len; 571 int error; 572 int rule_count; 573 574 len = sizeof(rule_count); 575 error = sysctlbyname(MIB ".rule_count", &rule_count, &len, NULL, 0); 576 if (error) { 577 len = snprintf(errstr, buflen, strerror(errno)); 578 return (-1); 579 } 580 if (len != sizeof(rule_count)) { 581 len = snprintf(errstr, buflen, "Data error in %s.rule_count", 582 MIB); 583 return (-1); 584 } 585 586 return (rule_count); 587 } 588 589 int 590 bsde_get_rule_slots(size_t buflen, char *errstr) 591 { 592 size_t len; 593 int error; 594 int rule_slots; 595 596 len = sizeof(rule_slots); 597 error = sysctlbyname(MIB ".rule_slots", &rule_slots, &len, NULL, 0); 598 if (error) { 599 len = snprintf(errstr, buflen, strerror(errno)); 600 return (-1); 601 } 602 if (len != sizeof(rule_slots)) { 603 len = snprintf(errstr, buflen, "Data error in %s.rule_slots", 604 MIB); 605 return (-1); 606 } 607 608 return (rule_slots); 609 } 610 611 /* 612 * Returns 0 for success; 613 * Returns -1 for failure; 614 * Returns -2 for not present 615 */ 616 int 617 bsde_get_rule(int rulenum, struct mac_bsdextended_rule *rule, size_t errlen, 618 char *errstr) 619 { 620 int name[10]; 621 size_t len, size; 622 int error; 623 624 len = 10; 625 error = bsde_get_mib(MIB ".rules", name, &len); 626 if (error) { 627 len = snprintf(errstr, errlen, "%s: %s", MIB ".rules", 628 strerror(errno)); 629 return (-1); 630 } 631 632 size = sizeof(*rule); 633 name[len] = rulenum; 634 len++; 635 error = sysctl(name, len, rule, &size, NULL, 0); 636 if (error == -1 && errno == ENOENT) 637 return (-2); 638 if (error) { 639 len = snprintf(errstr, errlen, "%s.%d: %s", MIB ".rules", 640 rulenum, strerror(errno)); 641 return (-1); 642 } else if (size != sizeof(*rule)) { 643 len = snprintf(errstr, errlen, "Data error in %s.%d: %s", 644 MIB ".rules", rulenum, strerror(errno)); 645 return (-1); 646 } 647 648 return (0); 649 } 650 651 int 652 bsde_delete_rule(int rulenum, size_t buflen, char *errstr) 653 { 654 struct mac_bsdextended_rule rule; 655 int name[10]; 656 size_t len, size; 657 int error; 658 659 len = 10; 660 error = bsde_get_mib(MIB ".rules", name, &len); 661 if (error) { 662 len = snprintf(errstr, buflen, "%s: %s", MIB ".rules", 663 strerror(errno)); 664 return (-1); 665 } 666 667 name[len] = rulenum; 668 len++; 669 670 size = sizeof(rule); 671 error = sysctl(name, len, NULL, NULL, &rule, 0); 672 if (error) { 673 len = snprintf(errstr, buflen, "%s.%d: %s", MIB ".rules", 674 rulenum, strerror(errno)); 675 return (-1); 676 } 677 678 return (0); 679 } 680 681 int 682 bsde_set_rule(int rulenum, struct mac_bsdextended_rule *rule, size_t buflen, 683 char *errstr) 684 { 685 int name[10]; 686 size_t len, size; 687 int error; 688 689 len = 10; 690 error = bsde_get_mib(MIB ".rules", name, &len); 691 if (error) { 692 len = snprintf(errstr, buflen, "%s: %s", MIB ".rules", 693 strerror(errno)); 694 return (-1); 695 } 696 697 name[len] = rulenum; 698 len++; 699 700 size = sizeof(*rule); 701 error = sysctl(name, len, NULL, NULL, rule, size); 702 if (error) { 703 len = snprintf(errstr, buflen, "%s.%d: %s", MIB ".rules", 704 rulenum, strerror(errno)); 705 return (-1); 706 } 707 708 return (0); 709 } 710 711 int 712 bsde_add_rule(int *rulenum, struct mac_bsdextended_rule *rule, size_t buflen, 713 char *errstr) 714 { 715 char charstr[BUFSIZ]; 716 int name[10]; 717 size_t len, size; 718 int error, rule_slots; 719 720 len = 10; 721 error = bsde_get_mib(MIB ".rules", name, &len); 722 if (error) { 723 len = snprintf(errstr, buflen, "%s: %s", MIB ".rules", 724 strerror(errno)); 725 return (-1); 726 } 727 728 rule_slots = bsde_get_rule_slots(BUFSIZ, charstr); 729 if (rule_slots == -1) { 730 len = snprintf(errstr, buflen, "unable to get rule slots: %s", 731 strerror(errno)); 732 return (-1); 733 } 734 735 name[len] = rule_slots; 736 len++; 737 738 size = sizeof(*rule); 739 error = sysctl(name, len, NULL, NULL, rule, size); 740 if (error) { 741 len = snprintf(errstr, buflen, "%s.%d: %s", MIB ".rules", 742 rule_slots, strerror(errno)); 743 return (-1); 744 } 745 746 if (rulenum != NULL) 747 rule_slots; 748 749 return (0); 750 } 751