1/*- 2 * Copyright (c) 2002-2005 Networks Associates Technology, Inc. 3 * All rights reserved. 4 * 5 * This software was developed for the FreeBSD Project by Network Associates 6 * Laboratories, the Security Research Division of Network Associates, Inc. 7 * under DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), as part of the 8 * DARPA CHATS research program. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 * SUCH DAMAGE. 30 * 31 * $FreeBSD: stable/10/lib/libugidfw/ugidfw.c 336039 2018-07-06 19:10:07Z jamie $ 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 61int 62bsde_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, truncated, numfs, i, notdone; 70 71 cur = buf; 72 left = buflen; 73 truncated = 0; 74 75 len = snprintf(cur, left, "subject "); 76 if (len < 0 || len > left) 77 goto truncated; 78 left -= len; 79 cur += len; 80 if (rule->mbr_subject.mbs_flags) { 81 if (rule->mbr_subject.mbs_neg == MBS_ALL_FLAGS) { 82 len = snprintf(cur, left, "not "); 83 if (len < 0 || len > left) 84 goto truncated; 85 left -= len; 86 cur += len; 87 notdone = 1; 88 } else { 89 notdone = 0; 90 } 91 92 if (!notdone && (rule->mbr_subject.mbs_neg & MBO_UID_DEFINED)) { 93 len = snprintf(cur, left, "! "); 94 if (len < 0 || len > left) 95 goto truncated; 96 left -= len; 97 cur += len; 98 } 99 if (rule->mbr_subject.mbs_flags & MBO_UID_DEFINED) { 100 pwd = getpwuid(rule->mbr_subject.mbs_uid_min); 101 if (pwd != NULL) { 102 len = snprintf(cur, left, "uid %s", 103 pwd->pw_name); 104 if (len < 0 || len > left) 105 goto truncated; 106 left -= len; 107 cur += len; 108 } else { 109 len = snprintf(cur, left, "uid %u", 110 rule->mbr_subject.mbs_uid_min); 111 if (len < 0 || len > left) 112 goto truncated; 113 left -= len; 114 cur += len; 115 } 116 if (rule->mbr_subject.mbs_uid_min != 117 rule->mbr_subject.mbs_uid_max) { 118 pwd = getpwuid(rule->mbr_subject.mbs_uid_max); 119 if (pwd != NULL) { 120 len = snprintf(cur, left, ":%s ", 121 pwd->pw_name); 122 if (len < 0 || len > left) 123 goto truncated; 124 left -= len; 125 cur += len; 126 } else { 127 len = snprintf(cur, left, ":%u ", 128 rule->mbr_subject.mbs_uid_max); 129 if (len < 0 || len > left) 130 goto truncated; 131 left -= len; 132 cur += len; 133 } 134 } else { 135 len = snprintf(cur, left, " "); 136 if (len < 0 || len > left) 137 goto truncated; 138 left -= len; 139 cur += len; 140 } 141 } 142 if (!notdone && (rule->mbr_subject.mbs_neg & MBO_GID_DEFINED)) { 143 len = snprintf(cur, left, "! "); 144 if (len < 0 || len > left) 145 goto truncated; 146 left -= len; 147 cur += len; 148 } 149 if (rule->mbr_subject.mbs_flags & MBO_GID_DEFINED) { 150 grp = getgrgid(rule->mbr_subject.mbs_gid_min); 151 if (grp != NULL) { 152 len = snprintf(cur, left, "gid %s", 153 grp->gr_name); 154 if (len < 0 || len > left) 155 goto truncated; 156 left -= len; 157 cur += len; 158 } else { 159 len = snprintf(cur, left, "gid %u", 160 rule->mbr_subject.mbs_gid_min); 161 if (len < 0 || len > left) 162 goto truncated; 163 left -= len; 164 cur += len; 165 } 166 if (rule->mbr_subject.mbs_gid_min != 167 rule->mbr_subject.mbs_gid_max) { 168 grp = getgrgid(rule->mbr_subject.mbs_gid_max); 169 if (grp != NULL) { 170 len = snprintf(cur, left, ":%s ", 171 grp->gr_name); 172 if (len < 0 || len > left) 173 goto truncated; 174 left -= len; 175 cur += len; 176 } else { 177 len = snprintf(cur, left, ":%u ", 178 rule->mbr_subject.mbs_gid_max); 179 if (len < 0 || len > left) 180 goto truncated; 181 left -= len; 182 cur += len; 183 } 184 } else { 185 len = snprintf(cur, left, " "); 186 if (len < 0 || len > left) 187 goto truncated; 188 left -= len; 189 cur += len; 190 } 191 } 192 if (!notdone && (rule->mbr_subject.mbs_neg & MBS_PRISON_DEFINED)) { 193 len = snprintf(cur, left, "! "); 194 if (len < 0 || len > left) 195 goto truncated; 196 left -= len; 197 cur += len; 198 } 199 if (rule->mbr_subject.mbs_flags & MBS_PRISON_DEFINED) { 200 len = snprintf(cur, left, "jailid %d ", 201 rule->mbr_subject.mbs_prison); 202 if (len < 0 || len > left) 203 goto truncated; 204 left -= len; 205 cur += len; 206 } 207 } 208 209 len = snprintf(cur, left, "object "); 210 if (len < 0 || len > left) 211 goto truncated; 212 left -= len; 213 cur += len; 214 if (rule->mbr_object.mbo_flags) { 215 if (rule->mbr_object.mbo_neg == MBO_ALL_FLAGS) { 216 len = snprintf(cur, left, "not "); 217 if (len < 0 || len > left) 218 goto truncated; 219 left -= len; 220 cur += len; 221 notdone = 1; 222 } else { 223 notdone = 0; 224 } 225 226 if (!notdone && (rule->mbr_object.mbo_neg & MBO_UID_DEFINED)) { 227 len = snprintf(cur, left, "! "); 228 if (len < 0 || len > left) 229 goto truncated; 230 left -= len; 231 cur += len; 232 } 233 if (rule->mbr_object.mbo_flags & MBO_UID_DEFINED) { 234 pwd = getpwuid(rule->mbr_object.mbo_uid_min); 235 if (pwd != NULL) { 236 len = snprintf(cur, left, "uid %s", 237 pwd->pw_name); 238 if (len < 0 || len > left) 239 goto truncated; 240 left -= len; 241 cur += len; 242 } else { 243 len = snprintf(cur, left, "uid %u", 244 rule->mbr_object.mbo_uid_min); 245 if (len < 0 || len > left) 246 goto truncated; 247 left -= len; 248 cur += len; 249 } 250 if (rule->mbr_object.mbo_uid_min != 251 rule->mbr_object.mbo_uid_max) { 252 pwd = getpwuid(rule->mbr_object.mbo_uid_max); 253 if (pwd != NULL) { 254 len = snprintf(cur, left, ":%s ", 255 pwd->pw_name); 256 if (len < 0 || len > left) 257 goto truncated; 258 left -= len; 259 cur += len; 260 } else { 261 len = snprintf(cur, left, ":%u ", 262 rule->mbr_object.mbo_uid_max); 263 if (len < 0 || len > left) 264 goto truncated; 265 left -= len; 266 cur += len; 267 } 268 } else { 269 len = snprintf(cur, left, " "); 270 if (len < 0 || len > left) 271 goto truncated; 272 left -= len; 273 cur += len; 274 } 275 } 276 if (!notdone && (rule->mbr_object.mbo_neg & MBO_GID_DEFINED)) { 277 len = snprintf(cur, left, "! "); 278 if (len < 0 || len > left) 279 goto truncated; 280 left -= len; 281 cur += len; 282 } 283 if (rule->mbr_object.mbo_flags & MBO_GID_DEFINED) { 284 grp = getgrgid(rule->mbr_object.mbo_gid_min); 285 if (grp != NULL) { 286 len = snprintf(cur, left, "gid %s", 287 grp->gr_name); 288 if (len < 0 || len > left) 289 goto truncated; 290 left -= len; 291 cur += len; 292 } else { 293 len = snprintf(cur, left, "gid %u", 294 rule->mbr_object.mbo_gid_min); 295 if (len < 0 || len > left) 296 goto truncated; 297 left -= len; 298 cur += len; 299 } 300 if (rule->mbr_object.mbo_gid_min != 301 rule->mbr_object.mbo_gid_max) { 302 grp = getgrgid(rule->mbr_object.mbo_gid_max); 303 if (grp != NULL) { 304 len = snprintf(cur, left, ":%s ", 305 grp->gr_name); 306 if (len < 0 || len > left) 307 goto truncated; 308 left -= len; 309 cur += len; 310 } else { 311 len = snprintf(cur, left, ":%u ", 312 rule->mbr_object.mbo_gid_max); 313 if (len < 0 || len > left) 314 goto truncated; 315 left -= len; 316 cur += len; 317 } 318 } else { 319 len = snprintf(cur, left, " "); 320 if (len < 0 || len > left) 321 goto truncated; 322 left -= len; 323 cur += len; 324 } 325 } 326 if (!notdone && (rule->mbr_object.mbo_neg & MBO_FSID_DEFINED)) { 327 len = snprintf(cur, left, "! "); 328 if (len < 0 || len > left) 329 goto truncated; 330 left -= len; 331 cur += len; 332 } 333 if (rule->mbr_object.mbo_flags & MBO_FSID_DEFINED) { 334 numfs = getmntinfo(&mntbuf, MNT_NOWAIT); 335 for (i = 0; i < numfs; i++) 336 if (memcmp(&(rule->mbr_object.mbo_fsid), 337 &(mntbuf[i].f_fsid), 338 sizeof(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 507truncated: 508 return (-1); 509} 510 511int 512bsde_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 557int 558bsde_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 603int 604bsde_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 634static int 635bsde_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; 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 743int 744bsde_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 786int 787bsde_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 802int 803bsde_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 uid_t uid_min, uid_max; 809 gid_t gid_min, gid_max; 810 int type; 811 struct fsid fsid; 812 813 current = 0; 814 flags = 0; 815 neg = 0; 816 nextnot = 0; 817 818 if (strcmp("not", argv[current]) == 0) { 819 not_seen = 1; 820 current++; 821 } else 822 not_seen = 0; 823 824 while (current < argc) { 825 if (strcmp(argv[current], "uid") == 0) { 826 if (current + 2 > argc) { 827 snprintf(errstr, buflen, "uid short"); 828 return (-1); 829 } 830 if (flags & MBO_UID_DEFINED) { 831 snprintf(errstr, buflen, "one uid only"); 832 return (-1); 833 } 834 if (bsde_parse_uidrange(argv[current+1], 835 &uid_min, &uid_max, buflen, errstr) < 0) 836 return (-1); 837 flags |= MBO_UID_DEFINED; 838 if (nextnot) { 839 neg ^= MBO_UID_DEFINED; 840 nextnot = 0; 841 } 842 current += 2; 843 } else if (strcmp(argv[current], "gid") == 0) { 844 if (current + 2 > argc) { 845 snprintf(errstr, buflen, "gid short"); 846 return (-1); 847 } 848 if (flags & MBO_GID_DEFINED) { 849 snprintf(errstr, buflen, "one gid only"); 850 return (-1); 851 } 852 if (bsde_parse_gidrange(argv[current+1], 853 &gid_min, &gid_max, buflen, errstr) < 0) 854 return (-1); 855 flags |= MBO_GID_DEFINED; 856 if (nextnot) { 857 neg ^= MBO_GID_DEFINED; 858 nextnot = 0; 859 } 860 current += 2; 861 } else if (strcmp(argv[current], "filesys") == 0) { 862 if (current + 2 > argc) { 863 snprintf(errstr, buflen, "filesys short"); 864 return (-1); 865 } 866 if (flags & MBO_FSID_DEFINED) { 867 snprintf(errstr, buflen, "one fsid only"); 868 return (-1); 869 } 870 if (bsde_parse_fsid(argv[current+1], &fsid, 871 buflen, errstr) < 0) 872 return (-1); 873 flags |= MBO_FSID_DEFINED; 874 if (nextnot) { 875 neg ^= MBO_FSID_DEFINED; 876 nextnot = 0; 877 } 878 current += 2; 879 } else if (strcmp(argv[current], "suid") == 0) { 880 flags |= MBO_SUID; 881 if (nextnot) { 882 neg ^= MBO_SUID; 883 nextnot = 0; 884 } 885 current += 1; 886 } else if (strcmp(argv[current], "sgid") == 0) { 887 flags |= MBO_SGID; 888 if (nextnot) { 889 neg ^= MBO_SGID; 890 nextnot = 0; 891 } 892 current += 1; 893 } else if (strcmp(argv[current], "uid_of_subject") == 0) { 894 flags |= MBO_UID_SUBJECT; 895 if (nextnot) { 896 neg ^= MBO_UID_SUBJECT; 897 nextnot = 0; 898 } 899 current += 1; 900 } else if (strcmp(argv[current], "gid_of_subject") == 0) { 901 flags |= MBO_GID_SUBJECT; 902 if (nextnot) { 903 neg ^= MBO_GID_SUBJECT; 904 nextnot = 0; 905 } 906 current += 1; 907 } else if (strcmp(argv[current], "type") == 0) { 908 if (current + 2 > argc) { 909 snprintf(errstr, buflen, "type short"); 910 return (-1); 911 } 912 if (flags & MBO_TYPE_DEFINED) { 913 snprintf(errstr, buflen, "one type only"); 914 return (-1); 915 } 916 if (bsde_parse_type(argv[current+1], &type, 917 buflen, errstr) < 0) 918 return (-1); 919 flags |= MBO_TYPE_DEFINED; 920 if (nextnot) { 921 neg ^= MBO_TYPE_DEFINED; 922 nextnot = 0; 923 } 924 current += 2; 925 } else if (strcmp(argv[current], "!") == 0) { 926 if (nextnot) { 927 snprintf(errstr, buflen, 928 "double negative'"); 929 return (-1); 930 } 931 nextnot = 1; 932 current += 1; 933 } else { 934 snprintf(errstr, buflen, "'%s' not expected", 935 argv[current]); 936 return (-1); 937 } 938 } 939 940 object->mbo_flags = flags; 941 if (not_seen) 942 object->mbo_neg = MBO_ALL_FLAGS ^ neg; 943 else 944 object->mbo_neg = neg; 945 if (flags & MBO_UID_DEFINED) { 946 object->mbo_uid_min = uid_min; 947 object->mbo_uid_max = uid_max; 948 } 949 if (flags & MBO_GID_DEFINED) { 950 object->mbo_gid_min = gid_min; 951 object->mbo_gid_max = gid_max; 952 } 953 if (flags & MBO_FSID_DEFINED) 954 object->mbo_fsid = fsid; 955 if (flags & MBO_TYPE_DEFINED) 956 object->mbo_type = type; 957 958 return (0); 959} 960 961int 962bsde_parse_mode(int argc, char *argv[], mode_t *mode, size_t buflen, 963 char *errstr) 964{ 965 int i; 966 967 if (argc == 0) { 968 snprintf(errstr, buflen, "mode expects mode value"); 969 return (-1); 970 } 971 972 if (argc != 1) { 973 snprintf(errstr, buflen, "'%s' unexpected", argv[1]); 974 return (-1); 975 } 976 977 *mode = 0; 978 for (i = 0; i < strlen(argv[0]); i++) { 979 switch (argv[0][i]) { 980 case 'a': 981 *mode |= MBI_ADMIN; 982 break; 983 case 'r': 984 *mode |= MBI_READ; 985 break; 986 case 's': 987 *mode |= MBI_STAT; 988 break; 989 case 'w': 990 *mode |= MBI_WRITE; 991 break; 992 case 'x': 993 *mode |= MBI_EXEC; 994 break; 995 case 'n': 996 /* ignore */ 997 break; 998 default: 999 snprintf(errstr, buflen, "Unknown mode letter: %c", 1000 argv[0][i]); 1001 return (-1); 1002 } 1003 } 1004 1005 return (0); 1006} 1007 1008int 1009bsde_parse_rule(int argc, char *argv[], struct mac_bsdextended_rule *rule, 1010 size_t buflen, char *errstr) 1011{ 1012 int subject, subject_elements, subject_elements_length; 1013 int object, object_elements, object_elements_length; 1014 int mode, mode_elements, mode_elements_length; 1015 int error, i; 1016 1017 bzero(rule, sizeof(*rule)); 1018 1019 if (argc < 1) { 1020 snprintf(errstr, buflen, "Rule must begin with subject"); 1021 return (-1); 1022 } 1023 1024 if (strcmp(argv[0], "subject") != 0) { 1025 snprintf(errstr, buflen, "Rule must begin with subject"); 1026 return (-1); 1027 } 1028 subject = 0; 1029 subject_elements = 1; 1030 1031 /* Search forward for object. */ 1032 1033 object = -1; 1034 for (i = 1; i < argc; i++) 1035 if (strcmp(argv[i], "object") == 0) 1036 object = i; 1037 1038 if (object == -1) { 1039 snprintf(errstr, buflen, "Rule must contain an object"); 1040 return (-1); 1041 } 1042 1043 /* Search forward for mode. */ 1044 mode = -1; 1045 for (i = object; i < argc; i++) 1046 if (strcmp(argv[i], "mode") == 0) 1047 mode = i; 1048 1049 if (mode == -1) { 1050 snprintf(errstr, buflen, "Rule must contain mode"); 1051 return (-1); 1052 } 1053 1054 subject_elements_length = object - subject - 1; 1055 object_elements = object + 1; 1056 object_elements_length = mode - object_elements; 1057 mode_elements = mode + 1; 1058 mode_elements_length = argc - mode_elements; 1059 1060 error = bsde_parse_subject(subject_elements_length, 1061 argv + subject_elements, &rule->mbr_subject, buflen, errstr); 1062 if (error) 1063 return (-1); 1064 1065 error = bsde_parse_object(object_elements_length, 1066 argv + object_elements, &rule->mbr_object, buflen, errstr); 1067 if (error) 1068 return (-1); 1069 1070 error = bsde_parse_mode(mode_elements_length, argv + mode_elements, 1071 &rule->mbr_mode, buflen, errstr); 1072 if (error) 1073 return (-1); 1074 1075 return (0); 1076} 1077 1078int 1079bsde_parse_rule_string(const char *string, struct mac_bsdextended_rule *rule, 1080 size_t buflen, char *errstr) 1081{ 1082 char *stringdup, *stringp, *argv[100], **ap; 1083 int argc, error; 1084 1085 stringp = stringdup = strdup(string); 1086 while (*stringp == ' ' || *stringp == '\t') 1087 stringp++; 1088 1089 argc = 0; 1090 for (ap = argv; (*ap = strsep(&stringp, " \t")) != NULL;) { 1091 argc++; 1092 if (**ap != '\0') 1093 if (++ap >= &argv[100]) 1094 break; 1095 } 1096 1097 error = bsde_parse_rule(argc, argv, rule, buflen, errstr); 1098 1099 free(stringdup); 1100 1101 return (error); 1102} 1103 1104int 1105bsde_get_mib(const char *string, int *name, size_t *namelen) 1106{ 1107 size_t len; 1108 int error; 1109 1110 len = *namelen; 1111 error = sysctlnametomib(string, name, &len); 1112 if (error) 1113 return (error); 1114 1115 *namelen = len; 1116 return (0); 1117} 1118 1119int 1120bsde_check_version(size_t buflen, char *errstr) 1121{ 1122 size_t len; 1123 int error; 1124 int version; 1125 1126 len = sizeof(version); 1127 error = sysctlbyname(MIB ".rule_version", &version, &len, NULL, 0); 1128 if (error) { 1129 snprintf(errstr, buflen, "version check failed: %s", 1130 strerror(errno)); 1131 return (-1); 1132 } 1133 if (version != MB_VERSION) { 1134 snprintf(errstr, buflen, "module v%d != library v%d", 1135 version, MB_VERSION); 1136 return (-1); 1137 } 1138 return (0); 1139} 1140 1141int 1142bsde_get_rule_count(size_t buflen, char *errstr) 1143{ 1144 size_t len; 1145 int error; 1146 int rule_count; 1147 1148 len = sizeof(rule_count); 1149 error = sysctlbyname(MIB ".rule_count", &rule_count, &len, NULL, 0); 1150 if (error) { 1151 snprintf(errstr, buflen, "%s", strerror(errno)); 1152 return (-1); 1153 } 1154 if (len != sizeof(rule_count)) { 1155 snprintf(errstr, buflen, "Data error in %s.rule_count", 1156 MIB); 1157 return (-1); 1158 } 1159 1160 return (rule_count); 1161} 1162 1163int 1164bsde_get_rule_slots(size_t buflen, char *errstr) 1165{ 1166 size_t len; 1167 int error; 1168 int rule_slots; 1169 1170 len = sizeof(rule_slots); 1171 error = sysctlbyname(MIB ".rule_slots", &rule_slots, &len, NULL, 0); 1172 if (error) { 1173 snprintf(errstr, buflen, "%s", strerror(errno)); 1174 return (-1); 1175 } 1176 if (len != sizeof(rule_slots)) { 1177 snprintf(errstr, buflen, "Data error in %s.rule_slots", MIB); 1178 return (-1); 1179 } 1180 1181 return (rule_slots); 1182} 1183 1184/* 1185 * Returns 0 for success; 1186 * Returns -1 for failure; 1187 * Returns -2 for not present 1188 */ 1189int 1190bsde_get_rule(int rulenum, struct mac_bsdextended_rule *rule, size_t errlen, 1191 char *errstr) 1192{ 1193 int name[10]; 1194 size_t len, size; 1195 int error; 1196 1197 if (bsde_check_version(errlen, errstr) != 0) 1198 return (-1); 1199 1200 len = 10; 1201 error = bsde_get_mib(MIB ".rules", name, &len); 1202 if (error) { 1203 snprintf(errstr, errlen, "%s: %s", MIB ".rules", 1204 strerror(errno)); 1205 return (-1); 1206 } 1207 1208 size = sizeof(*rule); 1209 name[len] = rulenum; 1210 len++; 1211 error = sysctl(name, len, rule, &size, NULL, 0); 1212 if (error == -1 && errno == ENOENT) 1213 return (-2); 1214 if (error) { 1215 snprintf(errstr, errlen, "%s.%d: %s", MIB ".rules", 1216 rulenum, strerror(errno)); 1217 return (-1); 1218 } else if (size != sizeof(*rule)) { 1219 snprintf(errstr, errlen, "Data error in %s.%d: %s", 1220 MIB ".rules", rulenum, strerror(errno)); 1221 return (-1); 1222 } 1223 1224 return (0); 1225} 1226 1227int 1228bsde_delete_rule(int rulenum, size_t buflen, char *errstr) 1229{ 1230 struct mac_bsdextended_rule rule; 1231 int name[10]; 1232 size_t len, size; 1233 int error; 1234 1235 if (bsde_check_version(buflen, errstr) != 0) 1236 return (-1); 1237 1238 len = 10; 1239 error = bsde_get_mib(MIB ".rules", name, &len); 1240 if (error) { 1241 snprintf(errstr, buflen, "%s: %s", MIB ".rules", 1242 strerror(errno)); 1243 return (-1); 1244 } 1245 1246 name[len] = rulenum; 1247 len++; 1248 1249 size = sizeof(rule); 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 1260int 1261bsde_set_rule(int rulenum, struct mac_bsdextended_rule *rule, size_t buflen, 1262 char *errstr) 1263{ 1264 int name[10]; 1265 size_t len, size; 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 size = sizeof(*rule); 1283 error = sysctl(name, len, NULL, NULL, rule, size); 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 1293int 1294bsde_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, size; 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 size = sizeof(*rule); 1324 error = sysctl(name, len, NULL, NULL, rule, size); 1325 if (error) { 1326 snprintf(errstr, buflen, "%s.%d: %s", MIB ".rules", 1327 rule_slots, strerror(errno)); 1328 return (-1); 1329 } 1330 1331 if (rulenum != NULL) 1332 *rulenum = rule_slots; 1333 1334 return (0); 1335} 1336