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 #include <sys/ucred.h> 38 #include <sys/mount.h> 39 40 #include <security/mac_bsdextended/mac_bsdextended.h> 41 42 #include <grp.h> 43 #include <pwd.h> 44 #include <stdio.h> 45 #include <stdlib.h> 46 #include <string.h> 47 48 #include "ugidfw.h" 49 50 /* 51 * Text format for rules: rules contain subject and object elements, mode. 52 * The total form is "subject [s_element] object [o_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 struct statfs *mntbuf; 65 char *cur, type[sizeof(rule->mbr_object.mbo_type) * CHAR_BIT + 1]; 66 size_t left, len; 67 int anymode, unknownmode, truncated, numfs, i, notdone; 68 69 cur = buf; 70 left = buflen; 71 truncated = 0; 72 73 len = snprintf(cur, left, "subject "); 74 if (len < 0 || len > left) 75 goto truncated; 76 left -= len; 77 cur += len; 78 if (rule->mbr_subject.mbs_flags) { 79 if (rule->mbr_subject.mbs_neg == MBS_ALL_FLAGS) { 80 len = snprintf(cur, left, "not "); 81 if (len < 0 || len > left) 82 goto truncated; 83 left -= len; 84 cur += len; 85 notdone = 1; 86 } else { 87 notdone = 0; 88 } 89 90 if (!notdone && (rule->mbr_subject.mbs_neg & MBO_UID_DEFINED)) { 91 len = snprintf(cur, left, "! "); 92 if (len < 0 || len > left) 93 goto truncated; 94 left -= len; 95 cur += len; 96 } 97 if (rule->mbr_subject.mbs_flags & MBO_UID_DEFINED) { 98 pwd = getpwuid(rule->mbr_subject.mbs_uid_min); 99 if (pwd != NULL) { 100 len = snprintf(cur, left, "uid %s", 101 pwd->pw_name); 102 if (len < 0 || len > left) 103 goto truncated; 104 left -= len; 105 cur += len; 106 } else { 107 len = snprintf(cur, left, "uid %u", 108 rule->mbr_subject.mbs_uid_min); 109 if (len < 0 || len > left) 110 goto truncated; 111 left -= len; 112 cur += len; 113 } 114 if (rule->mbr_subject.mbs_uid_min != 115 rule->mbr_subject.mbs_uid_max) { 116 pwd = getpwuid(rule->mbr_subject.mbs_uid_max); 117 if (pwd != NULL) { 118 len = snprintf(cur, left, ":%s ", 119 pwd->pw_name); 120 if (len < 0 || len > left) 121 goto truncated; 122 left -= len; 123 cur += len; 124 } else { 125 len = snprintf(cur, left, ":%u ", 126 rule->mbr_subject.mbs_uid_max); 127 if (len < 0 || len > left) 128 goto truncated; 129 left -= len; 130 cur += len; 131 } 132 } else { 133 len = snprintf(cur, left, " "); 134 if (len < 0 || len > left) 135 goto truncated; 136 left -= len; 137 cur += len; 138 } 139 } 140 if (!notdone && (rule->mbr_subject.mbs_neg & MBO_GID_DEFINED)) { 141 len = snprintf(cur, left, "! "); 142 if (len < 0 || len > left) 143 goto truncated; 144 left -= len; 145 cur += len; 146 } 147 if (rule->mbr_subject.mbs_flags & MBO_GID_DEFINED) { 148 grp = getgrgid(rule->mbr_subject.mbs_gid_min); 149 if (grp != NULL) { 150 len = snprintf(cur, left, "gid %s", 151 grp->gr_name); 152 if (len < 0 || len > left) 153 goto truncated; 154 left -= len; 155 cur += len; 156 } else { 157 len = snprintf(cur, left, "gid %u", 158 rule->mbr_subject.mbs_gid_min); 159 if (len < 0 || len > left) 160 goto truncated; 161 left -= len; 162 cur += len; 163 } 164 if (rule->mbr_subject.mbs_gid_min != 165 rule->mbr_subject.mbs_gid_max) { 166 grp = getgrgid(rule->mbr_subject.mbs_gid_max); 167 if (grp != NULL) { 168 len = snprintf(cur, left, ":%s ", 169 grp->gr_name); 170 if (len < 0 || len > left) 171 goto truncated; 172 left -= len; 173 cur += len; 174 } else { 175 len = snprintf(cur, left, ":%u ", 176 rule->mbr_subject.mbs_gid_max); 177 if (len < 0 || len > left) 178 goto truncated; 179 left -= len; 180 cur += len; 181 } 182 } else { 183 len = snprintf(cur, left, " "); 184 if (len < 0 || len > left) 185 goto truncated; 186 left -= len; 187 cur += len; 188 } 189 } 190 if (!notdone && (rule->mbr_subject.mbs_neg & MBS_PRISON_DEFINED)) { 191 len = snprintf(cur, left, "! "); 192 if (len < 0 || len > left) 193 goto truncated; 194 left -= len; 195 cur += len; 196 } 197 if (rule->mbr_subject.mbs_flags & MBS_PRISON_DEFINED) { 198 len = snprintf(cur, left, "jailid %d ", 199 rule->mbr_subject.mbs_prison); 200 if (len < 0 || len > left) 201 goto truncated; 202 left -= len; 203 cur += len; 204 } 205 } 206 207 len = snprintf(cur, left, "object "); 208 if (len < 0 || len > left) 209 goto truncated; 210 left -= len; 211 cur += len; 212 if (rule->mbr_object.mbo_flags) { 213 if (rule->mbr_object.mbo_neg == MBO_ALL_FLAGS) { 214 len = snprintf(cur, left, "not "); 215 if (len < 0 || len > left) 216 goto truncated; 217 left -= len; 218 cur += len; 219 notdone = 1; 220 } else { 221 notdone = 0; 222 } 223 224 if (!notdone && (rule->mbr_object.mbo_neg & MBO_UID_DEFINED)) { 225 len = snprintf(cur, left, "! "); 226 if (len < 0 || len > left) 227 goto truncated; 228 left -= len; 229 cur += len; 230 } 231 if (rule->mbr_object.mbo_flags & MBO_UID_DEFINED) { 232 pwd = getpwuid(rule->mbr_object.mbo_uid_min); 233 if (pwd != NULL) { 234 len = snprintf(cur, left, "uid %s", 235 pwd->pw_name); 236 if (len < 0 || len > left) 237 goto truncated; 238 left -= len; 239 cur += len; 240 } else { 241 len = snprintf(cur, left, "uid %u", 242 rule->mbr_object.mbo_uid_min); 243 if (len < 0 || len > left) 244 goto truncated; 245 left -= len; 246 cur += len; 247 } 248 if (rule->mbr_object.mbo_uid_min != 249 rule->mbr_object.mbo_uid_max) { 250 pwd = getpwuid(rule->mbr_object.mbo_uid_max); 251 if (pwd != NULL) { 252 len = snprintf(cur, left, ":%s ", 253 pwd->pw_name); 254 if (len < 0 || len > left) 255 goto truncated; 256 left -= len; 257 cur += len; 258 } else { 259 len = snprintf(cur, left, ":%u ", 260 rule->mbr_object.mbo_uid_max); 261 if (len < 0 || len > left) 262 goto truncated; 263 left -= len; 264 cur += len; 265 } 266 } else { 267 len = snprintf(cur, left, " "); 268 if (len < 0 || len > left) 269 goto truncated; 270 left -= len; 271 cur += len; 272 } 273 } 274 if (!notdone && (rule->mbr_object.mbo_neg & MBO_GID_DEFINED)) { 275 len = snprintf(cur, left, "! "); 276 if (len < 0 || len > left) 277 goto truncated; 278 left -= len; 279 cur += len; 280 } 281 if (rule->mbr_object.mbo_flags & MBO_GID_DEFINED) { 282 grp = getgrgid(rule->mbr_object.mbo_gid_min); 283 if (grp != NULL) { 284 len = snprintf(cur, left, "gid %s", 285 grp->gr_name); 286 if (len < 0 || len > left) 287 goto truncated; 288 left -= len; 289 cur += len; 290 } else { 291 len = snprintf(cur, left, "gid %u", 292 rule->mbr_object.mbo_gid_min); 293 if (len < 0 || len > left) 294 goto truncated; 295 left -= len; 296 cur += len; 297 } 298 if (rule->mbr_object.mbo_gid_min != 299 rule->mbr_object.mbo_gid_max) { 300 grp = getgrgid(rule->mbr_object.mbo_gid_max); 301 if (grp != NULL) { 302 len = snprintf(cur, left, ":%s ", 303 grp->gr_name); 304 if (len < 0 || len > left) 305 goto truncated; 306 left -= len; 307 cur += len; 308 } else { 309 len = snprintf(cur, left, ":%u ", 310 rule->mbr_object.mbo_gid_max); 311 if (len < 0 || len > left) 312 goto truncated; 313 left -= len; 314 cur += len; 315 } 316 } else { 317 len = snprintf(cur, left, " "); 318 if (len < 0 || len > left) 319 goto truncated; 320 left -= len; 321 cur += len; 322 } 323 } 324 if (!notdone && (rule->mbr_object.mbo_neg & MBO_FSID_DEFINED)) { 325 len = snprintf(cur, left, "! "); 326 if (len < 0 || len > left) 327 goto truncated; 328 left -= len; 329 cur += len; 330 } 331 if (rule->mbr_object.mbo_flags & MBO_FSID_DEFINED) { 332 numfs = getmntinfo(&mntbuf, MNT_NOWAIT); 333 for (i = 0; i < numfs; i++) 334 if (memcmp(&(rule->mbr_object.mbo_fsid), 335 &(mntbuf[i].f_fsid), 336 sizeof(mntbuf[i].f_fsid)) == 0) 337 break; 338 len = snprintf(cur, left, "filesys %s ", 339 i == numfs ? "???" : mntbuf[i].f_mntonname); 340 if (len < 0 || len > left) 341 goto truncated; 342 left -= len; 343 cur += len; 344 } 345 if (!notdone && (rule->mbr_object.mbo_neg & MBO_SUID)) { 346 len = snprintf(cur, left, "! "); 347 if (len < 0 || len > left) 348 goto truncated; 349 left -= len; 350 cur += len; 351 } 352 if (rule->mbr_object.mbo_flags & MBO_SUID) { 353 len = snprintf(cur, left, "suid "); 354 if (len < 0 || len > left) 355 goto truncated; 356 left -= len; 357 cur += len; 358 } 359 if (!notdone && (rule->mbr_object.mbo_neg & MBO_SGID)) { 360 len = snprintf(cur, left, "! "); 361 if (len < 0 || len > left) 362 goto truncated; 363 left -= len; 364 cur += len; 365 } 366 if (rule->mbr_object.mbo_flags & MBO_SGID) { 367 len = snprintf(cur, left, "sgid "); 368 if (len < 0 || len > left) 369 goto truncated; 370 left -= len; 371 cur += len; 372 } 373 if (!notdone && (rule->mbr_object.mbo_neg & MBO_UID_SUBJECT)) { 374 len = snprintf(cur, left, "! "); 375 if (len < 0 || len > left) 376 goto truncated; 377 left -= len; 378 cur += len; 379 } 380 if (rule->mbr_object.mbo_flags & MBO_UID_SUBJECT) { 381 len = snprintf(cur, left, "uid_of_subject "); 382 if (len < 0 || len > left) 383 goto truncated; 384 left -= len; 385 cur += len; 386 } 387 if (!notdone && (rule->mbr_object.mbo_neg & MBO_GID_SUBJECT)) { 388 len = snprintf(cur, left, "! "); 389 if (len < 0 || len > left) 390 goto truncated; 391 left -= len; 392 cur += len; 393 } 394 if (rule->mbr_object.mbo_flags & MBO_GID_SUBJECT) { 395 len = snprintf(cur, left, "gid_of_subject "); 396 if (len < 0 || len > left) 397 goto truncated; 398 left -= len; 399 cur += len; 400 } 401 if (!notdone && (rule->mbr_object.mbo_neg & MBO_TYPE_DEFINED)) { 402 len = snprintf(cur, left, "! "); 403 if (len < 0 || len > left) 404 goto truncated; 405 left -= len; 406 cur += len; 407 } 408 if (rule->mbr_object.mbo_flags & MBO_TYPE_DEFINED) { 409 i = 0; 410 if (rule->mbr_object.mbo_type & MBO_TYPE_REG) 411 type[i++] = 'r'; 412 if (rule->mbr_object.mbo_type & MBO_TYPE_DIR) 413 type[i++] = 'd'; 414 if (rule->mbr_object.mbo_type & MBO_TYPE_BLK) 415 type[i++] = 'b'; 416 if (rule->mbr_object.mbo_type & MBO_TYPE_CHR) 417 type[i++] = 'c'; 418 if (rule->mbr_object.mbo_type & MBO_TYPE_LNK) 419 type[i++] = 'l'; 420 if (rule->mbr_object.mbo_type & MBO_TYPE_SOCK) 421 type[i++] = 's'; 422 if (rule->mbr_object.mbo_type & MBO_TYPE_FIFO) 423 type[i++] = 'p'; 424 if (rule->mbr_object.mbo_type == MBO_ALL_TYPE) { 425 i = 0; 426 type[i++] = 'a'; 427 } 428 type[i++] = '\0'; 429 len = snprintf(cur, left, "type %s ", type); 430 if (len < 0 || len > left) 431 goto truncated; 432 left -= len; 433 cur += len; 434 } 435 } 436 437 len = snprintf(cur, left, "mode "); 438 if (len < 0 || len > left) 439 goto truncated; 440 left -= len; 441 cur += len; 442 443 anymode = (rule->mbr_mode & MBI_ALLPERM); 444 unknownmode = (rule->mbr_mode & ~MBI_ALLPERM); 445 446 if (rule->mbr_mode & MBI_ADMIN) { 447 len = snprintf(cur, left, "a"); 448 if (len < 0 || len > left) 449 goto truncated; 450 451 left -= len; 452 cur += len; 453 } 454 if (rule->mbr_mode & MBI_READ) { 455 len = snprintf(cur, left, "r"); 456 if (len < 0 || len > left) 457 goto truncated; 458 459 left -= len; 460 cur += len; 461 } 462 if (rule->mbr_mode & MBI_STAT) { 463 len = snprintf(cur, left, "s"); 464 if (len < 0 || len > left) 465 goto truncated; 466 467 left -= len; 468 cur += len; 469 } 470 if (rule->mbr_mode & MBI_WRITE) { 471 len = snprintf(cur, left, "w"); 472 if (len < 0 || len > left) 473 goto truncated; 474 475 left -= len; 476 cur += len; 477 } 478 if (rule->mbr_mode & MBI_EXEC) { 479 len = snprintf(cur, left, "x"); 480 if (len < 0 || len > left) 481 goto truncated; 482 483 left -= len; 484 cur += len; 485 } 486 if (!anymode) { 487 len = snprintf(cur, left, "n"); 488 if (len < 0 || len > left) 489 goto truncated; 490 491 left -= len; 492 cur += len; 493 } 494 if (unknownmode) { 495 len = snprintf(cur, left, "?"); 496 if (len < 0 || len > left) 497 goto truncated; 498 499 left -= len; 500 cur += len; 501 } 502 503 return (0); 504 505 truncated: 506 return (-1); 507 } 508 509 int 510 bsde_parse_uidrange(char *spec, uid_t *min, uid_t *max, 511 size_t buflen, char *errstr){ 512 struct passwd *pwd; 513 uid_t uid1, uid2; 514 char *spec1, *spec2, *endp; 515 unsigned long value; 516 size_t len; 517 518 spec2 = spec; 519 spec1 = strsep(&spec2, ":"); 520 521 pwd = getpwnam(spec1); 522 if (pwd != NULL) 523 uid1 = pwd->pw_uid; 524 else { 525 value = strtoul(spec1, &endp, 10); 526 if (*endp != '\0') { 527 len = snprintf(errstr, buflen, 528 "invalid uid: '%s'", spec1); 529 return (-1); 530 } 531 uid1 = value; 532 } 533 534 if (spec2 == NULL) { 535 *max = *min = uid1; 536 return (0); 537 } 538 539 pwd = getpwnam(spec2); 540 if (pwd != NULL) 541 uid2 = pwd->pw_uid; 542 else { 543 value = strtoul(spec2, &endp, 10); 544 if (*endp != '\0') { 545 len = snprintf(errstr, buflen, 546 "invalid uid: '%s'", spec2); 547 return (-1); 548 } 549 uid2 = value; 550 } 551 552 *min = uid1; 553 *max = uid2; 554 555 return (0); 556 } 557 558 int 559 bsde_parse_gidrange(char *spec, gid_t *min, gid_t *max, 560 size_t buflen, char *errstr){ 561 struct group *grp; 562 gid_t gid1, gid2; 563 char *spec1, *spec2, *endp; 564 unsigned long value; 565 size_t len; 566 567 spec2 = spec; 568 spec1 = strsep(&spec2, ":"); 569 570 grp = getgrnam(spec1); 571 if (grp != NULL) 572 gid1 = grp->gr_gid; 573 else { 574 value = strtoul(spec1, &endp, 10); 575 if (*endp != '\0') { 576 len = snprintf(errstr, buflen, 577 "invalid gid: '%s'", spec1); 578 return (-1); 579 } 580 gid1 = value; 581 } 582 583 if (spec2 == NULL) { 584 *max = *min = gid1; 585 return (0); 586 } 587 588 grp = getgrnam(spec2); 589 if (grp != NULL) 590 gid2 = grp->gr_gid; 591 else { 592 value = strtoul(spec2, &endp, 10); 593 if (*endp != '\0') { 594 len = snprintf(errstr, buflen, 595 "invalid gid: '%s'", spec2); 596 return (-1); 597 } 598 gid2 = value; 599 } 600 601 *min = gid1; 602 *max = gid2; 603 604 return (0); 605 } 606 607 int 608 bsde_parse_subject(int argc, char *argv[], 609 struct mac_bsdextended_subject *subject, size_t buflen, char *errstr) 610 { 611 int not_seen, flags; 612 int current, neg, nextnot; 613 char *endp; 614 uid_t uid_min, uid_max; 615 gid_t gid_min, gid_max; 616 int jid; 617 size_t len; 618 long value; 619 620 current = 0; 621 flags = 0; 622 neg = 0; 623 nextnot = 0; 624 625 if (strcmp("not", argv[current]) == 0) { 626 not_seen = 1; 627 current++; 628 } else 629 not_seen = 0; 630 631 while (current < argc) { 632 if (strcmp(argv[current], "uid") == 0) { 633 if (current + 2 > argc) { 634 len = snprintf(errstr, buflen, "uid short"); 635 return (-1); 636 } 637 if (flags & MBS_UID_DEFINED) { 638 len = snprintf(errstr, buflen, "one uid only"); 639 return (-1); 640 } 641 if (bsde_parse_uidrange(argv[current+1], 642 &uid_min, &uid_max, buflen, errstr) < 0) 643 return (-1); 644 flags |= MBS_UID_DEFINED; 645 if (nextnot) { 646 neg ^= MBS_UID_DEFINED; 647 nextnot = 0; 648 } 649 current += 2; 650 } else if (strcmp(argv[current], "gid") == 0) { 651 if (current + 2 > argc) { 652 len = snprintf(errstr, buflen, "gid short"); 653 return (-1); 654 } 655 if (flags & MBS_GID_DEFINED) { 656 len = snprintf(errstr, buflen, "one gid only"); 657 return (-1); 658 } 659 if (bsde_parse_gidrange(argv[current+1], 660 &gid_min, &gid_max, buflen, errstr) < 0) 661 return (-1); 662 flags |= MBS_GID_DEFINED; 663 if (nextnot) { 664 neg ^= MBS_GID_DEFINED; 665 nextnot = 0; 666 } 667 current += 2; 668 } else if (strcmp(argv[current], "jailid") == 0) { 669 if (current + 2 > argc) { 670 len = snprintf(errstr, buflen, "prison short"); 671 return (-1); 672 } 673 if (flags & MBS_PRISON_DEFINED) { 674 len = snprintf(errstr, buflen, "one jail only"); 675 return (-1); 676 } 677 value = strtol(argv[current+1], &endp, 10); 678 if (*endp != '\0') { 679 len = snprintf(errstr, buflen, 680 "invalid jid: '%s'", argv[current+1]); 681 return (-1); 682 } 683 jid = value; 684 flags |= MBS_PRISON_DEFINED; 685 if (nextnot) { 686 neg ^= MBS_PRISON_DEFINED; 687 nextnot = 0; 688 } 689 current += 2; 690 } else if (strcmp(argv[current], "!") == 0) { 691 if (nextnot) { 692 len = snprintf(errstr, buflen, 693 "double negative"); 694 return (-1); 695 } 696 nextnot = 1; 697 current += 1; 698 } else { 699 len = snprintf(errstr, buflen, "'%s' not expected", 700 argv[current]); 701 return (-1); 702 } 703 } 704 705 subject->mbs_flags = flags; 706 if (not_seen) 707 subject->mbs_neg = MBS_ALL_FLAGS ^ neg; 708 else 709 subject->mbs_neg = neg; 710 if (flags & MBS_UID_DEFINED) { 711 subject->mbs_uid_min = uid_min; 712 subject->mbs_uid_max = uid_max; 713 } 714 if (flags & MBS_GID_DEFINED) { 715 subject->mbs_gid_min = gid_min; 716 subject->mbs_gid_max = gid_max; 717 } 718 if (flags & MBS_PRISON_DEFINED) 719 subject->mbs_prison = jid; 720 721 return (0); 722 } 723 724 int 725 bsde_parse_type(char *spec, int *type, size_t buflen, char *errstr) 726 { 727 size_t len; 728 int i; 729 730 *type = 0; 731 for (i = 0; i < strlen(spec); i++) { 732 switch (spec[i]) { 733 case 'r': 734 case '-': 735 *type |= MBO_TYPE_REG; 736 break; 737 case 'd': 738 *type |= MBO_TYPE_DIR; 739 break; 740 case 'b': 741 *type |= MBO_TYPE_BLK; 742 break; 743 case 'c': 744 *type |= MBO_TYPE_CHR; 745 break; 746 case 'l': 747 *type |= MBO_TYPE_LNK; 748 break; 749 case 's': 750 *type |= MBO_TYPE_SOCK; 751 break; 752 case 'p': 753 *type |= MBO_TYPE_FIFO; 754 break; 755 case 'a': 756 *type |= MBO_ALL_TYPE; 757 break; 758 default: 759 len = snprintf(errstr, buflen, "Unknown type code: %c", 760 spec[i]); 761 return (-1); 762 } 763 } 764 765 return (0); 766 } 767 768 int 769 bsde_parse_fsid(char *spec, struct fsid *fsid, size_t buflen, char *errstr) 770 { 771 size_t len; 772 struct statfs buf; 773 int i; 774 775 if (statfs(spec, &buf) < 0) { 776 len = snprintf(errstr, buflen, "Unable to get id for %s: %s", 777 spec, strerror(errno)); 778 return (-1); 779 } 780 781 *fsid = buf.f_fsid; 782 783 return (0); 784 } 785 786 int 787 bsde_parse_object(int argc, char *argv[], 788 struct mac_bsdextended_object *object, size_t buflen, char *errstr) 789 { 790 int not_seen, flags; 791 int current, neg, nextnot; 792 uid_t uid_min, uid_max; 793 gid_t gid_min, gid_max; 794 int type; 795 struct fsid fsid; 796 size_t len; 797 798 current = 0; 799 flags = 0; 800 neg = 0; 801 nextnot = 0; 802 803 if (strcmp("not", argv[current]) == 0) { 804 not_seen = 1; 805 current++; 806 } else 807 not_seen = 0; 808 809 while (current < argc) { 810 if (strcmp(argv[current], "uid") == 0) { 811 if (current + 2 > argc) { 812 len = snprintf(errstr, buflen, "uid short"); 813 return (-1); 814 } 815 if (flags & MBO_UID_DEFINED) { 816 len = snprintf(errstr, buflen, "one uid only"); 817 return (-1); 818 } 819 if (bsde_parse_uidrange(argv[current+1], 820 &uid_min, &uid_max, buflen, errstr) < 0) 821 return (-1); 822 flags |= MBO_UID_DEFINED; 823 if (nextnot) { 824 neg ^= MBO_UID_DEFINED; 825 nextnot = 0; 826 } 827 current += 2; 828 } else if (strcmp(argv[current], "gid") == 0) { 829 if (current + 2 > argc) { 830 len = snprintf(errstr, buflen, "gid short"); 831 return (-1); 832 } 833 if (flags & MBO_GID_DEFINED) { 834 len = snprintf(errstr, buflen, "one gid only"); 835 return (-1); 836 } 837 if (bsde_parse_gidrange(argv[current+1], 838 &gid_min, &gid_max, buflen, errstr) < 0) 839 return (-1); 840 flags |= MBO_GID_DEFINED; 841 if (nextnot) { 842 neg ^= MBO_GID_DEFINED; 843 nextnot = 0; 844 } 845 current += 2; 846 } else if (strcmp(argv[current], "filesys") == 0) { 847 if (current + 2 > argc) { 848 len = snprintf(errstr, buflen, "filesys short"); 849 return (-1); 850 } 851 if (flags & MBO_FSID_DEFINED) { 852 len = snprintf(errstr, buflen, "one fsid only"); 853 return (-1); 854 } 855 if (bsde_parse_fsid(argv[current+1], &fsid, 856 buflen, errstr) < 0) 857 return (-1); 858 flags |= MBO_FSID_DEFINED; 859 if (nextnot) { 860 neg ^= MBO_FSID_DEFINED; 861 nextnot = 0; 862 } 863 current += 2; 864 } else if (strcmp(argv[current], "suid") == 0) { 865 flags |= MBO_SUID; 866 if (nextnot) { 867 neg ^= MBO_SUID; 868 nextnot = 0; 869 } 870 current += 1; 871 } else if (strcmp(argv[current], "sgid") == 0) { 872 flags |= MBO_SGID; 873 if (nextnot) { 874 neg ^= MBO_SGID; 875 nextnot = 0; 876 } 877 current += 1; 878 } else if (strcmp(argv[current], "uid_of_subject") == 0) { 879 flags |= MBO_UID_SUBJECT; 880 if (nextnot) { 881 neg ^= MBO_UID_SUBJECT; 882 nextnot = 0; 883 } 884 current += 1; 885 } else if (strcmp(argv[current], "gid_of_subject") == 0) { 886 flags |= MBO_GID_SUBJECT; 887 if (nextnot) { 888 neg ^= MBO_GID_SUBJECT; 889 nextnot = 0; 890 } 891 current += 1; 892 } else if (strcmp(argv[current], "type") == 0) { 893 if (current + 2 > argc) { 894 len = snprintf(errstr, buflen, "type short"); 895 return (-1); 896 } 897 if (flags & MBO_TYPE_DEFINED) { 898 len = snprintf(errstr, buflen, "one type only"); 899 return (-1); 900 } 901 if (bsde_parse_type(argv[current+1], &type, 902 buflen, errstr) < 0) 903 return (-1); 904 flags |= MBO_TYPE_DEFINED; 905 if (nextnot) { 906 neg ^= MBO_TYPE_DEFINED; 907 nextnot = 0; 908 } 909 current += 2; 910 } else if (strcmp(argv[current], "!") == 0) { 911 if (nextnot) { 912 len = snprintf(errstr, buflen, 913 "double negative'"); 914 return (-1); 915 } 916 nextnot = 1; 917 current += 1; 918 } else { 919 len = snprintf(errstr, buflen, "'%s' not expected", 920 argv[current]); 921 return (-1); 922 } 923 } 924 925 object->mbo_flags = flags; 926 if (not_seen) 927 object->mbo_neg = MBO_ALL_FLAGS ^ neg; 928 else 929 object->mbo_neg = neg; 930 if (flags & MBO_UID_DEFINED) { 931 object->mbo_uid_min = uid_min; 932 object->mbo_uid_max = uid_max; 933 } 934 if (flags & MBO_GID_DEFINED) { 935 object->mbo_gid_min = gid_min; 936 object->mbo_gid_max = gid_max; 937 } 938 if (flags & MBO_FSID_DEFINED) 939 object->mbo_fsid = fsid; 940 if (flags & MBO_TYPE_DEFINED) 941 object->mbo_type = type; 942 943 return (0); 944 } 945 946 int 947 bsde_parse_mode(int argc, char *argv[], mode_t *mode, size_t buflen, 948 char *errstr) 949 { 950 size_t len; 951 int i; 952 953 if (argc == 0) { 954 len = snprintf(errstr, buflen, "mode expects mode value"); 955 return (-1); 956 } 957 958 if (argc != 1) { 959 len = snprintf(errstr, buflen, "'%s' unexpected", argv[1]); 960 return (-1); 961 } 962 963 *mode = 0; 964 for (i = 0; i < strlen(argv[0]); i++) { 965 switch (argv[0][i]) { 966 case 'a': 967 *mode |= MBI_ADMIN; 968 break; 969 case 'r': 970 *mode |= MBI_READ; 971 break; 972 case 's': 973 *mode |= MBI_STAT; 974 break; 975 case 'w': 976 *mode |= MBI_WRITE; 977 break; 978 case 'x': 979 *mode |= MBI_EXEC; 980 break; 981 case 'n': 982 /* ignore */ 983 break; 984 default: 985 len = snprintf(errstr, buflen, "Unknown mode letter: %c", 986 argv[0][i]); 987 return (-1); 988 } 989 } 990 991 return (0); 992 } 993 994 int 995 bsde_parse_rule(int argc, char *argv[], struct mac_bsdextended_rule *rule, 996 size_t buflen, char *errstr) 997 { 998 int subject, subject_elements, subject_elements_length; 999 int object, object_elements, object_elements_length; 1000 int mode, mode_elements, mode_elements_length; 1001 int error, i; 1002 size_t len; 1003 1004 bzero(rule, sizeof(*rule)); 1005 1006 if (argc < 1) { 1007 len = snprintf(errstr, buflen, "Rule must begin with subject"); 1008 return (-1); 1009 } 1010 1011 if (strcmp(argv[0], "subject") != 0) { 1012 len = snprintf(errstr, buflen, "Rule must begin with subject"); 1013 return (-1); 1014 } 1015 subject = 0; 1016 subject_elements = 1; 1017 1018 /* Search forward for object. */ 1019 1020 object = -1; 1021 for (i = 1; i < argc; i++) 1022 if (strcmp(argv[i], "object") == 0) 1023 object = i; 1024 1025 if (object == -1) { 1026 len = snprintf(errstr, buflen, "Rule must contain an object"); 1027 return (-1); 1028 } 1029 1030 /* Search forward for mode. */ 1031 mode = -1; 1032 for (i = object; i < argc; i++) 1033 if (strcmp(argv[i], "mode") == 0) 1034 mode = i; 1035 1036 if (mode == -1) { 1037 len = snprintf(errstr, buflen, "Rule must contain mode"); 1038 return (-1); 1039 } 1040 1041 subject_elements_length = object - subject - 1; 1042 object_elements = object + 1; 1043 object_elements_length = mode - object_elements; 1044 mode_elements = mode + 1; 1045 mode_elements_length = argc - mode_elements; 1046 1047 error = bsde_parse_subject(subject_elements_length, 1048 argv + subject_elements, &rule->mbr_subject, buflen, errstr); 1049 if (error) 1050 return (-1); 1051 1052 error = bsde_parse_object(object_elements_length, 1053 argv + object_elements, &rule->mbr_object, buflen, errstr); 1054 if (error) 1055 return (-1); 1056 1057 error = bsde_parse_mode(mode_elements_length, argv + mode_elements, 1058 &rule->mbr_mode, buflen, errstr); 1059 if (error) 1060 return (-1); 1061 1062 return (0); 1063 } 1064 1065 int 1066 bsde_parse_rule_string(const char *string, struct mac_bsdextended_rule *rule, 1067 size_t buflen, char *errstr) 1068 { 1069 char *stringdup, *stringp, *argv[100], **ap; 1070 int argc, error; 1071 1072 stringp = stringdup = strdup(string); 1073 while (*stringp == ' ' || *stringp == '\t') 1074 stringp++; 1075 1076 argc = 0; 1077 for (ap = argv; (*ap = strsep(&stringp, " \t")) != NULL;) { 1078 argc++; 1079 if (**ap != '\0') 1080 if (++ap >= &argv[100]) 1081 break; 1082 } 1083 1084 error = bsde_parse_rule(argc, argv, rule, buflen, errstr); 1085 1086 free(stringdup); 1087 1088 return (error); 1089 } 1090 1091 int 1092 bsde_get_mib(const char *string, int *name, size_t *namelen) 1093 { 1094 size_t len; 1095 int error; 1096 1097 len = *namelen; 1098 error = sysctlnametomib(string, name, &len); 1099 if (error) 1100 return (error); 1101 1102 *namelen = len; 1103 return (0); 1104 } 1105 1106 int 1107 bsde_check_version(size_t buflen, char *errstr) 1108 { 1109 size_t len; 1110 int error; 1111 int version; 1112 1113 len = sizeof(version); 1114 error = sysctlbyname(MIB ".rule_version", &version, &len, NULL, 0); 1115 if (error) { 1116 len = snprintf(errstr, buflen, "version check failed: %s", 1117 strerror(errno)); 1118 return (-1); 1119 } 1120 if (version != MB_VERSION) { 1121 len = snprintf(errstr, buflen, "module v%d != library v%d", 1122 version, MB_VERSION); 1123 return (-1); 1124 } 1125 return (0); 1126 } 1127 1128 int 1129 bsde_get_rule_count(size_t buflen, char *errstr) 1130 { 1131 size_t len; 1132 int error; 1133 int rule_count; 1134 1135 len = sizeof(rule_count); 1136 error = sysctlbyname(MIB ".rule_count", &rule_count, &len, NULL, 0); 1137 if (error) { 1138 len = snprintf(errstr, buflen, strerror(errno)); 1139 return (-1); 1140 } 1141 if (len != sizeof(rule_count)) { 1142 len = snprintf(errstr, buflen, "Data error in %s.rule_count", 1143 MIB); 1144 return (-1); 1145 } 1146 1147 return (rule_count); 1148 } 1149 1150 int 1151 bsde_get_rule_slots(size_t buflen, char *errstr) 1152 { 1153 size_t len; 1154 int error; 1155 int rule_slots; 1156 1157 len = sizeof(rule_slots); 1158 error = sysctlbyname(MIB ".rule_slots", &rule_slots, &len, NULL, 0); 1159 if (error) { 1160 len = snprintf(errstr, buflen, strerror(errno)); 1161 return (-1); 1162 } 1163 if (len != sizeof(rule_slots)) { 1164 len = snprintf(errstr, buflen, "Data error in %s.rule_slots", 1165 MIB); 1166 return (-1); 1167 } 1168 1169 return (rule_slots); 1170 } 1171 1172 /* 1173 * Returns 0 for success; 1174 * Returns -1 for failure; 1175 * Returns -2 for not present 1176 */ 1177 int 1178 bsde_get_rule(int rulenum, struct mac_bsdextended_rule *rule, size_t errlen, 1179 char *errstr) 1180 { 1181 int name[10]; 1182 size_t len, size; 1183 int error; 1184 1185 if (bsde_check_version(errlen, errstr) != 0) 1186 return (-1); 1187 1188 len = 10; 1189 error = bsde_get_mib(MIB ".rules", name, &len); 1190 if (error) { 1191 len = snprintf(errstr, errlen, "%s: %s", MIB ".rules", 1192 strerror(errno)); 1193 return (-1); 1194 } 1195 1196 size = sizeof(*rule); 1197 name[len] = rulenum; 1198 len++; 1199 error = sysctl(name, len, rule, &size, NULL, 0); 1200 if (error == -1 && errno == ENOENT) 1201 return (-2); 1202 if (error) { 1203 len = snprintf(errstr, errlen, "%s.%d: %s", MIB ".rules", 1204 rulenum, strerror(errno)); 1205 return (-1); 1206 } else if (size != sizeof(*rule)) { 1207 len = snprintf(errstr, errlen, "Data error in %s.%d: %s", 1208 MIB ".rules", rulenum, strerror(errno)); 1209 return (-1); 1210 } 1211 1212 return (0); 1213 } 1214 1215 int 1216 bsde_delete_rule(int rulenum, size_t buflen, char *errstr) 1217 { 1218 struct mac_bsdextended_rule rule; 1219 int name[10]; 1220 size_t len, size; 1221 int error; 1222 1223 if (bsde_check_version(buflen, errstr) != 0) 1224 return (-1); 1225 1226 len = 10; 1227 error = bsde_get_mib(MIB ".rules", name, &len); 1228 if (error) { 1229 len = snprintf(errstr, buflen, "%s: %s", MIB ".rules", 1230 strerror(errno)); 1231 return (-1); 1232 } 1233 1234 name[len] = rulenum; 1235 len++; 1236 1237 size = sizeof(rule); 1238 error = sysctl(name, len, NULL, NULL, &rule, 0); 1239 if (error) { 1240 len = snprintf(errstr, buflen, "%s.%d: %s", MIB ".rules", 1241 rulenum, strerror(errno)); 1242 return (-1); 1243 } 1244 1245 return (0); 1246 } 1247 1248 int 1249 bsde_set_rule(int rulenum, struct mac_bsdextended_rule *rule, size_t buflen, 1250 char *errstr) 1251 { 1252 int name[10]; 1253 size_t len, size; 1254 int error; 1255 1256 if (bsde_check_version(buflen, errstr) != 0) 1257 return (-1); 1258 1259 len = 10; 1260 error = bsde_get_mib(MIB ".rules", name, &len); 1261 if (error) { 1262 len = snprintf(errstr, buflen, "%s: %s", MIB ".rules", 1263 strerror(errno)); 1264 return (-1); 1265 } 1266 1267 name[len] = rulenum; 1268 len++; 1269 1270 size = sizeof(*rule); 1271 error = sysctl(name, len, NULL, NULL, rule, size); 1272 if (error) { 1273 len = snprintf(errstr, buflen, "%s.%d: %s", MIB ".rules", 1274 rulenum, strerror(errno)); 1275 return (-1); 1276 } 1277 1278 return (0); 1279 } 1280 1281 int 1282 bsde_add_rule(int *rulenum, struct mac_bsdextended_rule *rule, size_t buflen, 1283 char *errstr) 1284 { 1285 char charstr[BUFSIZ]; 1286 int name[10]; 1287 size_t len, size; 1288 int error, rule_slots; 1289 1290 if (bsde_check_version(buflen, errstr) != 0) 1291 return (-1); 1292 1293 len = 10; 1294 error = bsde_get_mib(MIB ".rules", name, &len); 1295 if (error) { 1296 len = snprintf(errstr, buflen, "%s: %s", MIB ".rules", 1297 strerror(errno)); 1298 return (-1); 1299 } 1300 1301 rule_slots = bsde_get_rule_slots(BUFSIZ, charstr); 1302 if (rule_slots == -1) { 1303 len = snprintf(errstr, buflen, "unable to get rule slots: %s", 1304 strerror(errno)); 1305 return (-1); 1306 } 1307 1308 name[len] = rule_slots; 1309 len++; 1310 1311 size = sizeof(*rule); 1312 error = sysctl(name, len, NULL, NULL, rule, size); 1313 if (error) { 1314 len = snprintf(errstr, buflen, "%s.%d: %s", MIB ".rules", 1315 rule_slots, strerror(errno)); 1316 return (-1); 1317 } 1318 1319 if (rulenum != NULL) 1320 *rulenum = rule_slots; 1321 1322 return (0); 1323 } 1324