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