1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause 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 #include <sys/param.h> 34 #include <sys/errno.h> 35 #include <sys/jail.h> 36 #include <sys/time.h> 37 #include <sys/sysctl.h> 38 #include <sys/ucred.h> 39 #include <sys/uio.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 (fsidcmp(&rule->mbr_object.mbo_fsid, 336 &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 static 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 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 snprintf(errstr, buflen, "invalid uid: '%s'", spec1); 527 return (-1); 528 } 529 uid1 = value; 530 } 531 532 if (spec2 == NULL) { 533 *max = *min = uid1; 534 return (0); 535 } 536 537 pwd = getpwnam(spec2); 538 if (pwd != NULL) 539 uid2 = pwd->pw_uid; 540 else { 541 value = strtoul(spec2, &endp, 10); 542 if (*endp != '\0') { 543 snprintf(errstr, buflen, "invalid uid: '%s'", spec2); 544 return (-1); 545 } 546 uid2 = value; 547 } 548 549 *min = uid1; 550 *max = uid2; 551 552 return (0); 553 } 554 555 static int 556 bsde_parse_gidrange(char *spec, gid_t *min, gid_t *max, 557 size_t buflen, char *errstr){ 558 struct group *grp; 559 gid_t gid1, gid2; 560 char *spec1, *spec2, *endp; 561 unsigned long value; 562 563 spec2 = spec; 564 spec1 = strsep(&spec2, ":"); 565 566 grp = getgrnam(spec1); 567 if (grp != NULL) 568 gid1 = grp->gr_gid; 569 else { 570 value = strtoul(spec1, &endp, 10); 571 if (*endp != '\0') { 572 snprintf(errstr, buflen, "invalid gid: '%s'", spec1); 573 return (-1); 574 } 575 gid1 = value; 576 } 577 578 if (spec2 == NULL) { 579 *max = *min = gid1; 580 return (0); 581 } 582 583 grp = getgrnam(spec2); 584 if (grp != NULL) 585 gid2 = grp->gr_gid; 586 else { 587 value = strtoul(spec2, &endp, 10); 588 if (*endp != '\0') { 589 snprintf(errstr, buflen, "invalid gid: '%s'", spec2); 590 return (-1); 591 } 592 gid2 = value; 593 } 594 595 *min = gid1; 596 *max = gid2; 597 598 return (0); 599 } 600 601 static int 602 bsde_get_jailid(const char *name, size_t buflen, char *errstr) 603 { 604 char *ep; 605 int jid; 606 struct iovec jiov[4]; 607 608 /* Copy jail_getid(3) instead of messing with library dependancies */ 609 jid = strtoul(name, &ep, 10); 610 if (*name && !*ep) 611 return jid; 612 jiov[0].iov_base = __DECONST(char *, "name"); 613 jiov[0].iov_len = sizeof("name"); 614 jiov[1].iov_len = strlen(name) + 1; 615 jiov[1].iov_base = alloca(jiov[1].iov_len); 616 strcpy(jiov[1].iov_base, name); 617 if (errstr && buflen) { 618 jiov[2].iov_base = __DECONST(char *, "errmsg"); 619 jiov[2].iov_len = sizeof("errmsg"); 620 jiov[3].iov_base = errstr; 621 jiov[3].iov_len = buflen; 622 errstr[0] = 0; 623 jid = jail_get(jiov, 4, 0); 624 if (jid < 0 && !errstr[0]) 625 snprintf(errstr, buflen, "jail_get: %s", 626 strerror(errno)); 627 } else 628 jid = jail_get(jiov, 2, 0); 629 return jid; 630 } 631 632 static int 633 bsde_parse_subject(int argc, char *argv[], 634 struct mac_bsdextended_subject *subject, size_t buflen, char *errstr) 635 { 636 int not_seen, flags; 637 int current, neg, nextnot; 638 uid_t uid_min, uid_max; 639 gid_t gid_min, gid_max; 640 int jid = 0; 641 642 current = 0; 643 flags = 0; 644 neg = 0; 645 nextnot = 0; 646 647 if (strcmp("not", argv[current]) == 0) { 648 not_seen = 1; 649 current++; 650 } else 651 not_seen = 0; 652 653 while (current < argc) { 654 if (strcmp(argv[current], "uid") == 0) { 655 if (current + 2 > argc) { 656 snprintf(errstr, buflen, "uid short"); 657 return (-1); 658 } 659 if (flags & MBS_UID_DEFINED) { 660 snprintf(errstr, buflen, "one uid only"); 661 return (-1); 662 } 663 if (bsde_parse_uidrange(argv[current+1], 664 &uid_min, &uid_max, buflen, errstr) < 0) 665 return (-1); 666 flags |= MBS_UID_DEFINED; 667 if (nextnot) { 668 neg ^= MBS_UID_DEFINED; 669 nextnot = 0; 670 } 671 current += 2; 672 } else if (strcmp(argv[current], "gid") == 0) { 673 if (current + 2 > argc) { 674 snprintf(errstr, buflen, "gid short"); 675 return (-1); 676 } 677 if (flags & MBS_GID_DEFINED) { 678 snprintf(errstr, buflen, "one gid only"); 679 return (-1); 680 } 681 if (bsde_parse_gidrange(argv[current+1], 682 &gid_min, &gid_max, buflen, errstr) < 0) 683 return (-1); 684 flags |= MBS_GID_DEFINED; 685 if (nextnot) { 686 neg ^= MBS_GID_DEFINED; 687 nextnot = 0; 688 } 689 current += 2; 690 } else if (strcmp(argv[current], "jailid") == 0) { 691 if (current + 2 > argc) { 692 snprintf(errstr, buflen, "prison short"); 693 return (-1); 694 } 695 if (flags & MBS_PRISON_DEFINED) { 696 snprintf(errstr, buflen, "one jail only"); 697 return (-1); 698 } 699 jid = bsde_get_jailid(argv[current+1], buflen, errstr); 700 if (jid < 0) 701 return (-1); 702 flags |= MBS_PRISON_DEFINED; 703 if (nextnot) { 704 neg ^= MBS_PRISON_DEFINED; 705 nextnot = 0; 706 } 707 current += 2; 708 } else if (strcmp(argv[current], "!") == 0) { 709 if (nextnot) { 710 snprintf(errstr, buflen, "double negative"); 711 return (-1); 712 } 713 nextnot = 1; 714 current += 1; 715 } else { 716 snprintf(errstr, buflen, "'%s' not expected", 717 argv[current]); 718 return (-1); 719 } 720 } 721 722 subject->mbs_flags = flags; 723 if (not_seen) 724 subject->mbs_neg = MBS_ALL_FLAGS ^ neg; 725 else 726 subject->mbs_neg = neg; 727 if (flags & MBS_UID_DEFINED) { 728 subject->mbs_uid_min = uid_min; 729 subject->mbs_uid_max = uid_max; 730 } 731 if (flags & MBS_GID_DEFINED) { 732 subject->mbs_gid_min = gid_min; 733 subject->mbs_gid_max = gid_max; 734 } 735 if (flags & MBS_PRISON_DEFINED) 736 subject->mbs_prison = jid; 737 738 return (0); 739 } 740 741 static int 742 bsde_parse_type(char *spec, int *type, size_t buflen, char *errstr) 743 { 744 int i; 745 746 *type = 0; 747 for (i = 0; i < strlen(spec); i++) { 748 switch (spec[i]) { 749 case 'r': 750 case '-': 751 *type |= MBO_TYPE_REG; 752 break; 753 case 'd': 754 *type |= MBO_TYPE_DIR; 755 break; 756 case 'b': 757 *type |= MBO_TYPE_BLK; 758 break; 759 case 'c': 760 *type |= MBO_TYPE_CHR; 761 break; 762 case 'l': 763 *type |= MBO_TYPE_LNK; 764 break; 765 case 's': 766 *type |= MBO_TYPE_SOCK; 767 break; 768 case 'p': 769 *type |= MBO_TYPE_FIFO; 770 break; 771 case 'a': 772 *type |= MBO_ALL_TYPE; 773 break; 774 default: 775 snprintf(errstr, buflen, "Unknown type code: %c", 776 spec[i]); 777 return (-1); 778 } 779 } 780 781 return (0); 782 } 783 784 static int 785 bsde_parse_fsid(char *spec, struct fsid *fsid, size_t buflen, char *errstr) 786 { 787 struct statfs buf; 788 789 if (statfs(spec, &buf) < 0) { 790 snprintf(errstr, buflen, "Unable to get id for %s: %s", 791 spec, strerror(errno)); 792 return (-1); 793 } 794 795 *fsid = buf.f_fsid; 796 797 return (0); 798 } 799 800 static int 801 bsde_parse_object(int argc, char *argv[], 802 struct mac_bsdextended_object *object, size_t buflen, char *errstr) 803 { 804 int not_seen, flags; 805 int current, neg, nextnot; 806 int type; 807 uid_t uid_min, uid_max; 808 gid_t gid_min, gid_max; 809 struct fsid fsid; 810 811 current = 0; 812 flags = 0; 813 neg = 0; 814 nextnot = 0; 815 type = 0; 816 817 if (strcmp("not", argv[current]) == 0) { 818 not_seen = 1; 819 current++; 820 } else 821 not_seen = 0; 822 823 while (current < argc) { 824 if (strcmp(argv[current], "uid") == 0) { 825 if (current + 2 > argc) { 826 snprintf(errstr, buflen, "uid short"); 827 return (-1); 828 } 829 if (flags & MBO_UID_DEFINED) { 830 snprintf(errstr, buflen, "one uid only"); 831 return (-1); 832 } 833 if (bsde_parse_uidrange(argv[current+1], 834 &uid_min, &uid_max, buflen, errstr) < 0) 835 return (-1); 836 flags |= MBO_UID_DEFINED; 837 if (nextnot) { 838 neg ^= MBO_UID_DEFINED; 839 nextnot = 0; 840 } 841 current += 2; 842 } else if (strcmp(argv[current], "gid") == 0) { 843 if (current + 2 > argc) { 844 snprintf(errstr, buflen, "gid short"); 845 return (-1); 846 } 847 if (flags & MBO_GID_DEFINED) { 848 snprintf(errstr, buflen, "one gid only"); 849 return (-1); 850 } 851 if (bsde_parse_gidrange(argv[current+1], 852 &gid_min, &gid_max, buflen, errstr) < 0) 853 return (-1); 854 flags |= MBO_GID_DEFINED; 855 if (nextnot) { 856 neg ^= MBO_GID_DEFINED; 857 nextnot = 0; 858 } 859 current += 2; 860 } else if (strcmp(argv[current], "filesys") == 0) { 861 if (current + 2 > argc) { 862 snprintf(errstr, buflen, "filesys short"); 863 return (-1); 864 } 865 if (flags & MBO_FSID_DEFINED) { 866 snprintf(errstr, buflen, "one fsid only"); 867 return (-1); 868 } 869 if (bsde_parse_fsid(argv[current+1], &fsid, 870 buflen, errstr) < 0) 871 return (-1); 872 flags |= MBO_FSID_DEFINED; 873 if (nextnot) { 874 neg ^= MBO_FSID_DEFINED; 875 nextnot = 0; 876 } 877 current += 2; 878 } else if (strcmp(argv[current], "suid") == 0) { 879 flags |= MBO_SUID; 880 if (nextnot) { 881 neg ^= MBO_SUID; 882 nextnot = 0; 883 } 884 current += 1; 885 } else if (strcmp(argv[current], "sgid") == 0) { 886 flags |= MBO_SGID; 887 if (nextnot) { 888 neg ^= MBO_SGID; 889 nextnot = 0; 890 } 891 current += 1; 892 } else if (strcmp(argv[current], "uid_of_subject") == 0) { 893 flags |= MBO_UID_SUBJECT; 894 if (nextnot) { 895 neg ^= MBO_UID_SUBJECT; 896 nextnot = 0; 897 } 898 current += 1; 899 } else if (strcmp(argv[current], "gid_of_subject") == 0) { 900 flags |= MBO_GID_SUBJECT; 901 if (nextnot) { 902 neg ^= MBO_GID_SUBJECT; 903 nextnot = 0; 904 } 905 current += 1; 906 } else if (strcmp(argv[current], "type") == 0) { 907 if (current + 2 > argc) { 908 snprintf(errstr, buflen, "type short"); 909 return (-1); 910 } 911 if (flags & MBO_TYPE_DEFINED) { 912 snprintf(errstr, buflen, "one type only"); 913 return (-1); 914 } 915 if (bsde_parse_type(argv[current+1], &type, 916 buflen, errstr) < 0) 917 return (-1); 918 flags |= MBO_TYPE_DEFINED; 919 if (nextnot) { 920 neg ^= MBO_TYPE_DEFINED; 921 nextnot = 0; 922 } 923 current += 2; 924 } else if (strcmp(argv[current], "!") == 0) { 925 if (nextnot) { 926 snprintf(errstr, buflen, 927 "double negative'"); 928 return (-1); 929 } 930 nextnot = 1; 931 current += 1; 932 } else { 933 snprintf(errstr, buflen, "'%s' not expected", 934 argv[current]); 935 return (-1); 936 } 937 } 938 939 object->mbo_flags = flags; 940 if (not_seen) 941 object->mbo_neg = MBO_ALL_FLAGS ^ neg; 942 else 943 object->mbo_neg = neg; 944 if (flags & MBO_UID_DEFINED) { 945 object->mbo_uid_min = uid_min; 946 object->mbo_uid_max = uid_max; 947 } 948 if (flags & MBO_GID_DEFINED) { 949 object->mbo_gid_min = gid_min; 950 object->mbo_gid_max = gid_max; 951 } 952 if (flags & MBO_FSID_DEFINED) 953 object->mbo_fsid = fsid; 954 if (flags & MBO_TYPE_DEFINED) 955 object->mbo_type = type; 956 957 return (0); 958 } 959 960 int 961 bsde_parse_mode(int argc, char *argv[], mode_t *mode, size_t buflen, 962 char *errstr) 963 { 964 int i; 965 966 if (argc == 0) { 967 snprintf(errstr, buflen, "mode expects mode value"); 968 return (-1); 969 } 970 971 if (argc != 1) { 972 snprintf(errstr, buflen, "'%s' unexpected", argv[1]); 973 return (-1); 974 } 975 976 *mode = 0; 977 for (i = 0; i < strlen(argv[0]); i++) { 978 switch (argv[0][i]) { 979 case 'a': 980 *mode |= MBI_ADMIN; 981 break; 982 case 'r': 983 *mode |= MBI_READ; 984 break; 985 case 's': 986 *mode |= MBI_STAT; 987 break; 988 case 'w': 989 *mode |= MBI_WRITE; 990 break; 991 case 'x': 992 *mode |= MBI_EXEC; 993 break; 994 case 'n': 995 /* ignore */ 996 break; 997 default: 998 snprintf(errstr, buflen, "Unknown mode letter: %c", 999 argv[0][i]); 1000 return (-1); 1001 } 1002 } 1003 1004 return (0); 1005 } 1006 1007 int 1008 bsde_parse_rule(int argc, char *argv[], struct mac_bsdextended_rule *rule, 1009 size_t buflen, char *errstr) 1010 { 1011 int subject, subject_elements, subject_elements_length; 1012 int object, object_elements, object_elements_length; 1013 int mode, mode_elements, mode_elements_length; 1014 int error, i; 1015 1016 bzero(rule, sizeof(*rule)); 1017 1018 if (argc < 1) { 1019 snprintf(errstr, buflen, "Rule must begin with subject"); 1020 return (-1); 1021 } 1022 1023 if (strcmp(argv[0], "subject") != 0) { 1024 snprintf(errstr, buflen, "Rule must begin with subject"); 1025 return (-1); 1026 } 1027 subject = 0; 1028 subject_elements = 1; 1029 1030 /* Search forward for object. */ 1031 1032 object = -1; 1033 for (i = 1; i < argc; i++) 1034 if (strcmp(argv[i], "object") == 0) 1035 object = i; 1036 1037 if (object == -1) { 1038 snprintf(errstr, buflen, "Rule must contain an object"); 1039 return (-1); 1040 } 1041 1042 /* Search forward for mode. */ 1043 mode = -1; 1044 for (i = object; i < argc; i++) 1045 if (strcmp(argv[i], "mode") == 0) 1046 mode = i; 1047 1048 if (mode == -1) { 1049 snprintf(errstr, buflen, "Rule must contain mode"); 1050 return (-1); 1051 } 1052 1053 subject_elements_length = object - subject - 1; 1054 object_elements = object + 1; 1055 object_elements_length = mode - object_elements; 1056 mode_elements = mode + 1; 1057 mode_elements_length = argc - mode_elements; 1058 1059 error = bsde_parse_subject(subject_elements_length, 1060 argv + subject_elements, &rule->mbr_subject, buflen, errstr); 1061 if (error) 1062 return (-1); 1063 1064 error = bsde_parse_object(object_elements_length, 1065 argv + object_elements, &rule->mbr_object, buflen, errstr); 1066 if (error) 1067 return (-1); 1068 1069 error = bsde_parse_mode(mode_elements_length, argv + mode_elements, 1070 &rule->mbr_mode, buflen, errstr); 1071 if (error) 1072 return (-1); 1073 1074 return (0); 1075 } 1076 1077 int 1078 bsde_parse_rule_string(const char *string, struct mac_bsdextended_rule *rule, 1079 size_t buflen, char *errstr) 1080 { 1081 char *stringdup, *stringp, *argv[100], **ap; 1082 int argc, error; 1083 1084 stringp = stringdup = strdup(string); 1085 while (*stringp == ' ' || *stringp == '\t') 1086 stringp++; 1087 1088 argc = 0; 1089 for (ap = argv; (*ap = strsep(&stringp, " \t")) != NULL;) { 1090 argc++; 1091 if (**ap != '\0') 1092 if (++ap >= &argv[100]) 1093 break; 1094 } 1095 1096 error = bsde_parse_rule(argc, argv, rule, buflen, errstr); 1097 1098 free(stringdup); 1099 1100 return (error); 1101 } 1102 1103 int 1104 bsde_get_mib(const char *string, int *name, size_t *namelen) 1105 { 1106 size_t len; 1107 int error; 1108 1109 len = *namelen; 1110 error = sysctlnametomib(string, name, &len); 1111 if (error) 1112 return (error); 1113 1114 *namelen = len; 1115 return (0); 1116 } 1117 1118 static int 1119 bsde_check_version(size_t buflen, char *errstr) 1120 { 1121 size_t len; 1122 int error; 1123 int version; 1124 1125 len = sizeof(version); 1126 error = sysctlbyname(MIB ".rule_version", &version, &len, NULL, 0); 1127 if (error) { 1128 snprintf(errstr, buflen, "version check failed: %s", 1129 strerror(errno)); 1130 return (-1); 1131 } 1132 if (version != MB_VERSION) { 1133 snprintf(errstr, buflen, "module v%d != library v%d", 1134 version, MB_VERSION); 1135 return (-1); 1136 } 1137 return (0); 1138 } 1139 1140 int 1141 bsde_get_rule_count(size_t buflen, char *errstr) 1142 { 1143 size_t len; 1144 int error; 1145 int rule_count; 1146 1147 len = sizeof(rule_count); 1148 error = sysctlbyname(MIB ".rule_count", &rule_count, &len, NULL, 0); 1149 if (error) { 1150 snprintf(errstr, buflen, "%s", strerror(errno)); 1151 return (-1); 1152 } 1153 if (len != sizeof(rule_count)) { 1154 snprintf(errstr, buflen, "Data error in %s.rule_count", 1155 MIB); 1156 return (-1); 1157 } 1158 1159 return (rule_count); 1160 } 1161 1162 int 1163 bsde_get_rule_slots(size_t buflen, char *errstr) 1164 { 1165 size_t len; 1166 int error; 1167 int rule_slots; 1168 1169 len = sizeof(rule_slots); 1170 error = sysctlbyname(MIB ".rule_slots", &rule_slots, &len, NULL, 0); 1171 if (error) { 1172 snprintf(errstr, buflen, "%s", strerror(errno)); 1173 return (-1); 1174 } 1175 if (len != sizeof(rule_slots)) { 1176 snprintf(errstr, buflen, "Data error in %s.rule_slots", MIB); 1177 return (-1); 1178 } 1179 1180 return (rule_slots); 1181 } 1182 1183 /* 1184 * Returns 0 for success; 1185 * Returns -1 for failure; 1186 * Returns -2 for not present 1187 */ 1188 int 1189 bsde_get_rule(int rulenum, struct mac_bsdextended_rule *rule, size_t errlen, 1190 char *errstr) 1191 { 1192 int name[10]; 1193 size_t len, size; 1194 int error; 1195 1196 if (bsde_check_version(errlen, errstr) != 0) 1197 return (-1); 1198 1199 len = 10; 1200 error = bsde_get_mib(MIB ".rules", name, &len); 1201 if (error) { 1202 snprintf(errstr, errlen, "%s: %s", MIB ".rules", 1203 strerror(errno)); 1204 return (-1); 1205 } 1206 1207 size = sizeof(*rule); 1208 name[len] = rulenum; 1209 len++; 1210 error = sysctl(name, len, rule, &size, NULL, 0); 1211 if (error == -1 && errno == ENOENT) 1212 return (-2); 1213 if (error) { 1214 snprintf(errstr, errlen, "%s.%d: %s", MIB ".rules", 1215 rulenum, strerror(errno)); 1216 return (-1); 1217 } else if (size != sizeof(*rule)) { 1218 snprintf(errstr, errlen, "Data error in %s.%d: %s", 1219 MIB ".rules", rulenum, strerror(errno)); 1220 return (-1); 1221 } 1222 1223 return (0); 1224 } 1225 1226 int 1227 bsde_delete_rule(int rulenum, size_t buflen, char *errstr) 1228 { 1229 struct mac_bsdextended_rule rule; 1230 int name[10]; 1231 size_t len; 1232 int error; 1233 1234 if (bsde_check_version(buflen, errstr) != 0) 1235 return (-1); 1236 1237 len = 10; 1238 error = bsde_get_mib(MIB ".rules", name, &len); 1239 if (error) { 1240 snprintf(errstr, buflen, "%s: %s", MIB ".rules", 1241 strerror(errno)); 1242 return (-1); 1243 } 1244 1245 name[len] = rulenum; 1246 len++; 1247 1248 error = sysctl(name, len, NULL, NULL, &rule, 0); 1249 if (error) { 1250 snprintf(errstr, buflen, "%s.%d: %s", MIB ".rules", 1251 rulenum, strerror(errno)); 1252 return (-1); 1253 } 1254 1255 return (0); 1256 } 1257 1258 int 1259 bsde_set_rule(int rulenum, struct mac_bsdextended_rule *rule, size_t buflen, 1260 char *errstr) 1261 { 1262 int name[10]; 1263 size_t len; 1264 int error; 1265 1266 if (bsde_check_version(buflen, errstr) != 0) 1267 return (-1); 1268 1269 len = 10; 1270 error = bsde_get_mib(MIB ".rules", name, &len); 1271 if (error) { 1272 snprintf(errstr, buflen, "%s: %s", MIB ".rules", 1273 strerror(errno)); 1274 return (-1); 1275 } 1276 1277 name[len] = rulenum; 1278 len++; 1279 1280 error = sysctl(name, len, NULL, NULL, rule, sizeof(*rule)); 1281 if (error) { 1282 snprintf(errstr, buflen, "%s.%d: %s", MIB ".rules", 1283 rulenum, strerror(errno)); 1284 return (-1); 1285 } 1286 1287 return (0); 1288 } 1289 1290 int 1291 bsde_add_rule(int *rulenum, struct mac_bsdextended_rule *rule, size_t buflen, 1292 char *errstr) 1293 { 1294 char charstr[BUFSIZ]; 1295 int name[10]; 1296 size_t len; 1297 int error, rule_slots; 1298 1299 if (bsde_check_version(buflen, errstr) != 0) 1300 return (-1); 1301 1302 len = 10; 1303 error = bsde_get_mib(MIB ".rules", name, &len); 1304 if (error) { 1305 snprintf(errstr, buflen, "%s: %s", MIB ".rules", 1306 strerror(errno)); 1307 return (-1); 1308 } 1309 1310 rule_slots = bsde_get_rule_slots(BUFSIZ, charstr); 1311 if (rule_slots == -1) { 1312 snprintf(errstr, buflen, "unable to get rule slots: %s", 1313 strerror(errno)); 1314 return (-1); 1315 } 1316 1317 name[len] = rule_slots; 1318 len++; 1319 1320 error = sysctl(name, len, NULL, NULL, rule, sizeof(*rule)); 1321 if (error) { 1322 snprintf(errstr, buflen, "%s.%d: %s", MIB ".rules", 1323 rule_slots, strerror(errno)); 1324 return (-1); 1325 } 1326 1327 if (rulenum != NULL) 1328 *rulenum = rule_slots; 1329 1330 return (0); 1331 } 1332