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