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