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