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 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 subjectand 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 value = strtol(argv[current+1], &endp, 10); 345 if (*endp != '\0') { 346 len = snprintf(errstr, buflen, "invalid uid: '%s'", 347 argv[current+1]); 348 return (-1); 349 } 350 uid = value; 351 uid_seen = 1; 352 current += 2; 353 } else if (strcmp("gid", argv[current]) == 0) { 354 if (gid_seen) { 355 len = snprintf(errstr, buflen, 356 "Only one gid permitted per identity clause"); 357 return (-1); 358 } 359 if (current + 2 > argc) { 360 len = snprintf(errstr, buflen, "gid short"); 361 return (-1); 362 } 363 value = strtol(argv[current+1], &endp, 10); 364 if (*endp != '\0') { 365 len = snprintf(errstr, buflen, "invalid gid: '%s'", 366 argv[current+1]); 367 return (-1); 368 } 369 gid = value; 370 gid_seen = 1; 371 current += 2; 372 } else { 373 len = snprintf(errstr, buflen, "'%s' not expected", 374 argv[current]); 375 return (-1); 376 } 377 } 378 379 if (current +1 < argc) { 380 len = snprintf(errstr, buflen, "'%s' not expected", 381 argv[current]); 382 return (-1); 383 } 384 385 /* Fill out the identity. */ 386 identity->mbi_flags = 0; 387 388 if (not_seen) 389 identity->mbi_flags |= MBI_NEGATED; 390 391 if (uid_seen) { 392 identity->mbi_flags |= MBI_UID_DEFINED; 393 identity->mbi_uid = uid; 394 } else 395 identity->mbi_uid = 0; 396 397 if (gid_seen) { 398 identity->mbi_flags |= MBI_GID_DEFINED; 399 identity->mbi_gid = gid; 400 } else 401 identity->mbi_gid = 0; 402 403 return (0); 404 } 405 406 int 407 bsde_parse_mode(int argc, char *argv[], mode_t *mode, size_t buflen, 408 char *errstr) 409 { 410 size_t len; 411 int i; 412 413 if (argc == 0) { 414 len = snprintf(errstr, buflen, "mode expects mode value"); 415 return (-1); 416 } 417 418 if (argc != 1) { 419 len = snprintf(errstr, buflen, "'%s' unexpected", argv[1]); 420 return (-1); 421 } 422 423 *mode = 0; 424 for (i = 0; i < strlen(argv[0]); i++) { 425 switch (argv[0][i]) { 426 case 'a': 427 *mode |= MBI_ADMIN; 428 break; 429 case 'r': 430 *mode |= MBI_READ; 431 break; 432 case 's': 433 *mode |= MBI_STAT; 434 break; 435 case 'w': 436 *mode |= MBI_WRITE; 437 break; 438 case 'x': 439 *mode |= MBI_EXEC; 440 break; 441 case 'n': 442 /* ignore */ 443 break; 444 default: 445 len = snprintf(errstr, buflen, "Unknown mode letter: %c", 446 argv[0][i]); 447 return (-1); 448 } 449 } 450 451 return (0); 452 } 453 454 int 455 bsde_parse_rule(int argc, char *argv[], struct mac_bsdextended_rule *rule, 456 size_t buflen, char *errstr) 457 { 458 int subject, subject_elements, subject_elements_length; 459 int object, object_elements, object_elements_length; 460 int mode, mode_elements, mode_elements_length; 461 int error, i; 462 size_t len; 463 464 bzero(rule, sizeof(*rule)); 465 466 if (argc < 1) { 467 len = snprintf(errstr, buflen, "Rule must begin with subject"); 468 return (-1); 469 } 470 471 if (strcmp(argv[0], "subject") != 0) { 472 len = snprintf(errstr, buflen, "Rule must begin with subject"); 473 return (-1); 474 } 475 subject = 0; 476 subject_elements = 1; 477 478 /* Search forward for object. */ 479 480 object = -1; 481 for (i = 1; i < argc; i++) 482 if (strcmp(argv[i], "object") == 0) 483 object = i; 484 485 if (object == -1) { 486 len = snprintf(errstr, buflen, "Rule must contain an object"); 487 return (-1); 488 } 489 490 /* Search forward for mode. */ 491 mode = -1; 492 for (i = object; i < argc; i++) 493 if (strcmp(argv[i], "mode") == 0) 494 mode = i; 495 496 if (mode == -1) { 497 len = snprintf(errstr, buflen, "Rule must contain mode"); 498 return (-1); 499 } 500 501 subject_elements_length = object - subject - 1; 502 object_elements = object + 1; 503 object_elements_length = mode - object_elements; 504 mode_elements = mode + 1; 505 mode_elements_length = argc - mode_elements; 506 507 error = bsde_parse_identity(subject_elements_length, 508 argv + subject_elements, &rule->mbr_subject, buflen, errstr); 509 if (error) 510 return (-1); 511 512 error = bsde_parse_identity(object_elements_length, 513 argv + object_elements, &rule->mbr_object, buflen, errstr); 514 if (error) 515 return (-1); 516 517 error = bsde_parse_mode(mode_elements_length, argv + mode_elements, 518 &rule->mbr_mode, buflen, errstr); 519 if (error) 520 return (-1); 521 522 return (0); 523 } 524 525 int 526 bsde_parse_rule_string(const char *string, struct mac_bsdextended_rule *rule, 527 size_t buflen, char *errstr) 528 { 529 char *stringdup, *stringp, *argv[20], **ap; 530 int argc, error; 531 532 stringp = stringdup = strdup(string); 533 while (*stringp == ' ' || *stringp == '\t') 534 stringp++; 535 536 argc = 0; 537 for (ap = argv; (*ap = strsep(&stringp, " \t")) != NULL;) { 538 argc++; 539 if (**ap != '\0') 540 if (++ap >= &argv[20]) 541 break; 542 } 543 544 error = bsde_parse_rule(argc, argv, rule, buflen, errstr); 545 546 free(stringdup); 547 548 return (error); 549 } 550 551 int 552 bsde_get_mib(const char *string, int *name, size_t *namelen) 553 { 554 size_t len; 555 int error; 556 557 len = *namelen; 558 error = sysctlnametomib(string, name, &len); 559 if (error) 560 return (error); 561 562 *namelen = len; 563 return (0); 564 } 565 566 int 567 bsde_get_rule_count(size_t buflen, char *errstr) 568 { 569 size_t len; 570 int error; 571 int rule_count; 572 573 len = sizeof(rule_count); 574 error = sysctlbyname(MIB ".rule_count", &rule_count, &len, NULL, 0); 575 if (error) { 576 len = snprintf(errstr, buflen, strerror(errno)); 577 return (-1); 578 } 579 if (len != sizeof(rule_count)) { 580 len = snprintf(errstr, buflen, "Data error in %s.rule_count", 581 MIB); 582 return (-1); 583 } 584 585 return (rule_count); 586 } 587 588 int 589 bsde_get_rule_slots(size_t buflen, char *errstr) 590 { 591 size_t len; 592 int error; 593 int rule_slots; 594 595 len = sizeof(rule_slots); 596 error = sysctlbyname(MIB ".rule_slots", &rule_slots, &len, NULL, 0); 597 if (error) { 598 len = snprintf(errstr, buflen, strerror(errno)); 599 return (-1); 600 } 601 if (len != sizeof(rule_slots)) { 602 len = snprintf(errstr, buflen, "Data error in %s.rule_slots", 603 MIB); 604 return (-1); 605 } 606 607 return (rule_slots); 608 } 609 610 /* 611 * Returns 0 for success; 612 * Returns -1 for failure; 613 * Returns -2 for not present 614 */ 615 int 616 bsde_get_rule(int rulenum, struct mac_bsdextended_rule *rule, size_t errlen, 617 char *errstr) 618 { 619 int name[10]; 620 size_t len, size; 621 int error; 622 623 len = 10; 624 error = bsde_get_mib(MIB ".rules", name, &len); 625 if (error) { 626 len = snprintf(errstr, errlen, "%s: %s", MIB ".rules", 627 strerror(errno)); 628 return (-1); 629 } 630 631 size = sizeof(*rule); 632 name[len] = rulenum; 633 len++; 634 error = sysctl(name, len, rule, &size, NULL, 0); 635 if (error == -1 && errno == ENOENT) 636 return (-2); 637 if (error) { 638 len = snprintf(errstr, errlen, "%s.%d: %s", MIB ".rules", 639 rulenum, strerror(errno)); 640 return (-1); 641 } else if (size != sizeof(*rule)) { 642 len = snprintf(errstr, errlen, "Data error in %s.%d: %s", 643 MIB ".rules", rulenum, strerror(errno)); 644 return (-1); 645 } 646 647 return (0); 648 } 649 650 int 651 bsde_delete_rule(int rulenum, size_t buflen, char *errstr) 652 { 653 struct mac_bsdextended_rule rule; 654 int name[10]; 655 size_t len, size; 656 int error; 657 658 len = 10; 659 error = bsde_get_mib(MIB ".rules", name, &len); 660 if (error) { 661 len = snprintf(errstr, buflen, "%s: %s", MIB ".rules", 662 strerror(errno)); 663 return (-1); 664 } 665 666 name[len] = rulenum; 667 len++; 668 669 size = sizeof(rule); 670 error = sysctl(name, len, NULL, NULL, &rule, 0); 671 if (error) { 672 len = snprintf(errstr, buflen, "%s.%d: %s", MIB ".rules", 673 rulenum, strerror(errno)); 674 return (-1); 675 } 676 677 return (0); 678 } 679 680 int 681 bsde_set_rule(int rulenum, struct mac_bsdextended_rule *rule, size_t buflen, 682 char *errstr) 683 { 684 int name[10]; 685 size_t len, size; 686 int error; 687 688 len = 10; 689 error = bsde_get_mib(MIB ".rules", name, &len); 690 if (error) { 691 len = snprintf(errstr, buflen, "%s: %s", MIB ".rules", 692 strerror(errno)); 693 return (-1); 694 } 695 696 name[len] = rulenum; 697 len++; 698 699 size = sizeof(*rule); 700 error = sysctl(name, len, NULL, NULL, rule, size); 701 if (error) { 702 len = snprintf(errstr, buflen, "%s.%d: %s", MIB ".rules", 703 rulenum, strerror(errno)); 704 return (-1); 705 } 706 707 return (0); 708 } 709 710 int 711 bsde_add_rule(int *rulenum, struct mac_bsdextended_rule *rule, size_t buflen, 712 char *errstr) 713 { 714 char charstr[BUFSIZ]; 715 int name[10]; 716 size_t len, size; 717 int error, rule_slots; 718 719 len = 10; 720 error = bsde_get_mib(MIB ".rules", name, &len); 721 if (error) { 722 len = snprintf(errstr, buflen, "%s: %s", MIB ".rules", 723 strerror(errno)); 724 return (-1); 725 } 726 727 rule_slots = bsde_get_rule_slots(BUFSIZ, charstr); 728 if (rule_slots == -1) { 729 len = snprintf(errstr, buflen, "unable to get rule slots: %s", 730 strerror(errno)); 731 return (-1); 732 } 733 734 name[len] = rule_slots; 735 len++; 736 737 size = sizeof(*rule); 738 error = sysctl(name, len, NULL, NULL, rule, size); 739 if (error) { 740 len = snprintf(errstr, buflen, "%s.%d: %s", MIB ".rules", 741 rule_slots, strerror(errno)); 742 return (-1); 743 } 744 745 if (rulenum != NULL) 746 rule_slots; 747 748 return (0); 749 } 750