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