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