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