1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved. 23 */ 24 /* 25 * Copyright (c) 2013 by Delphix. All rights reserved. 26 * Copyright 2015, Joyent, Inc. 27 */ 28 /* 29 * Program to examine or set process privileges. 30 */ 31 32 #include <stdio.h> 33 #include <stdio_ext.h> 34 #include <stdlib.h> 35 #include <unistd.h> 36 #include <fcntl.h> 37 #include <string.h> 38 #include <limits.h> 39 #include <sys/types.h> 40 #include <libproc.h> 41 #include <priv.h> 42 #include <errno.h> 43 #include <ctype.h> 44 45 #include <locale.h> 46 #include <langinfo.h> 47 48 static int look(char *); 49 static void perr(char *); 50 static void usage(void); 51 static void loadprivinfo(void); 52 static int parsespec(const char *); 53 static void privupdate(prpriv_t *, const char *); 54 static void privupdate_self(void); 55 static int dumppriv(char **); 56 static void flags2str(uint_t); 57 58 static char *command; 59 static char *procname; 60 static boolean_t verb = B_FALSE; 61 static boolean_t set = B_FALSE; 62 static boolean_t exec = B_FALSE; 63 static boolean_t Don = B_FALSE; 64 static boolean_t Doff = B_FALSE; 65 static boolean_t list = B_FALSE; 66 static boolean_t mac_aware = B_FALSE; 67 static boolean_t pfexec = B_FALSE; 68 static boolean_t xpol = B_FALSE; 69 static int mode = PRIV_STR_PORT; 70 71 int 72 main(int argc, char **argv) 73 { 74 int rc = 0; 75 int opt; 76 struct rlimit rlim; 77 78 (void) setlocale(LC_ALL, ""); 79 (void) textdomain(TEXT_DOMAIN); 80 81 if ((command = strrchr(argv[0], '/')) != NULL) 82 command++; 83 else 84 command = argv[0]; 85 86 while ((opt = getopt(argc, argv, "lDMNPevs:xS")) != EOF) { 87 switch (opt) { 88 case 'l': 89 list = B_TRUE; 90 break; 91 case 'D': 92 set = B_TRUE; 93 Don = B_TRUE; 94 break; 95 case 'M': 96 mac_aware = B_TRUE; 97 break; 98 case 'N': 99 set = B_TRUE; 100 Doff = B_TRUE; 101 break; 102 case 'P': 103 set = B_TRUE; 104 pfexec = B_TRUE; 105 break; 106 case 'e': 107 exec = B_TRUE; 108 break; 109 case 'S': 110 mode = PRIV_STR_SHORT; 111 break; 112 case 'v': 113 verb = B_TRUE; 114 mode = PRIV_STR_LIT; 115 break; 116 case 's': 117 set = B_TRUE; 118 if ((rc = parsespec(optarg)) != 0) 119 return (rc); 120 break; 121 case 'x': 122 set = B_TRUE; 123 xpol = B_TRUE; 124 break; 125 default: 126 usage(); 127 /*NOTREACHED*/ 128 } 129 } 130 131 argc -= optind; 132 argv += optind; 133 134 if ((argc < 1 && !list) || Doff && Don || list && (set || exec) || 135 (mac_aware && !exec)) 136 usage(); 137 138 /* 139 * Make sure we'll have enough file descriptors to handle a target 140 * that has many many mappings. 141 */ 142 if (getrlimit(RLIMIT_NOFILE, &rlim) == 0) { 143 rlim.rlim_cur = rlim.rlim_max; 144 (void) setrlimit(RLIMIT_NOFILE, &rlim); 145 (void) enable_extended_FILE_stdio(-1, -1); 146 } 147 148 if (exec) { 149 privupdate_self(); 150 rc = execvp(argv[0], &argv[0]); 151 (void) fprintf(stderr, "%s: %s: %s\n", command, argv[0], 152 strerror(errno)); 153 } else if (list) { 154 rc = dumppriv(argv); 155 } else { 156 while (argc-- > 0) 157 rc += look(*argv++); 158 } 159 160 return (rc); 161 } 162 163 static int 164 look(char *arg) 165 { 166 struct ps_prochandle *Pr; 167 int gcode; 168 size_t sz; 169 void *pdata; 170 char *x; 171 int i; 172 boolean_t nodata; 173 prpriv_t *ppriv; 174 175 procname = arg; /* for perr() */ 176 177 if ((Pr = proc_arg_grab(arg, set ? PR_ARG_PIDS : PR_ARG_ANY, 178 PGRAB_RETAIN | PGRAB_FORCE | (set ? 0 : PGRAB_RDONLY) | 179 PGRAB_NOSTOP, &gcode)) == NULL) { 180 (void) fprintf(stderr, "%s: cannot examine %s: %s\n", 181 command, arg, Pgrab_error(gcode)); 182 return (1); 183 } 184 185 if (Ppriv(Pr, &ppriv) == -1) { 186 perr(command); 187 Prelease(Pr, 0); 188 return (1); 189 } 190 sz = PRIV_PRPRIV_SIZE(ppriv); 191 192 /* 193 * The ppriv fields are unsigned and may overflow, so check them 194 * separately. Size must be word aligned, so check that too. 195 * Make sure size is "smallish" too. 196 */ 197 if ((sz & 3) || ppriv->pr_nsets == 0 || 198 sz / ppriv->pr_nsets < ppriv->pr_setsize || 199 ppriv->pr_infosize > sz || sz > 1024 * 1024) { 200 (void) fprintf(stderr, 201 "%s: %s: bad PRNOTES section, size = %lx\n", 202 command, arg, (long)sz); 203 Prelease(Pr, 0); 204 Ppriv_free(Pr, ppriv); 205 return (1); 206 } 207 208 if (set) { 209 privupdate(ppriv, arg); 210 if (Psetpriv(Pr, ppriv) != 0) { 211 perr(command); 212 Prelease(Pr, 0); 213 Ppriv_free(Pr, ppriv); 214 return (1); 215 } 216 Prelease(Pr, 0); 217 Ppriv_free(Pr, ppriv); 218 return (0); 219 } 220 221 if (Pstate(Pr) == PS_DEAD) { 222 (void) printf("core '%s' of %d:\t%.70s\n", 223 arg, (int)Ppsinfo(Pr)->pr_pid, Ppsinfo(Pr)->pr_psargs); 224 pdata = Pprivinfo(Pr); 225 nodata = Pstate(Pr) == PS_DEAD && pdata == NULL; 226 } else { 227 (void) printf("%d:\t%.70s\n", 228 (int)Ppsinfo(Pr)->pr_pid, Ppsinfo(Pr)->pr_psargs); 229 pdata = NULL; 230 nodata = B_FALSE; 231 } 232 233 x = (char *)ppriv + sz - ppriv->pr_infosize; 234 while (x < (char *)ppriv + sz) { 235 /* LINTED: alignment */ 236 priv_info_t *pi = (priv_info_t *)x; 237 priv_info_uint_t *pii; 238 239 switch (pi->priv_info_type) { 240 case PRIV_INFO_FLAGS: 241 /* LINTED: alignment */ 242 pii = (priv_info_uint_t *)x; 243 (void) printf("flags ="); 244 flags2str(pii->val); 245 (void) putchar('\n'); 246 break; 247 default: 248 (void) fprintf(stderr, "%s: unknown priv_info: %d\n", 249 arg, pi->priv_info_type); 250 break; 251 } 252 if (pi->priv_info_size > ppriv->pr_infosize || 253 pi->priv_info_size <= sizeof (priv_info_t) || 254 (pi->priv_info_size & 3) != 0) { 255 (void) fprintf(stderr, "%s: bad priv_info_size: %u\n", 256 arg, pi->priv_info_size); 257 break; 258 } 259 x += pi->priv_info_size; 260 } 261 262 for (i = 0; i < ppriv->pr_nsets; i++) { 263 extern const char *__priv_getsetbynum(const void *, int); 264 const char *setnm = pdata ? __priv_getsetbynum(pdata, i) : 265 priv_getsetbynum(i); 266 priv_chunk_t *pc = 267 (priv_chunk_t *)&ppriv->pr_sets[ppriv->pr_setsize * i]; 268 269 270 (void) printf("\t%c: ", setnm && !nodata ? *setnm : '?'); 271 if (!nodata) { 272 extern char *__priv_set_to_str(void *, 273 const priv_set_t *, char, int); 274 priv_set_t *pset = (priv_set_t *)pc; 275 276 char *s; 277 278 if (pdata) 279 s = __priv_set_to_str(pdata, pset, ',', mode); 280 else 281 s = priv_set_to_str(pset, ',', mode); 282 (void) puts(s); 283 free(s); 284 } else { 285 int j; 286 for (j = 0; j < ppriv->pr_setsize; j++) 287 (void) printf("%08x", pc[j]); 288 (void) putchar('\n'); 289 } 290 } 291 Prelease(Pr, 0); 292 Ppriv_free(Pr, ppriv); 293 return (0); 294 } 295 296 static void 297 fatal(const char *s) 298 { 299 (void) fprintf(stderr, "%s: %s: %s\n", command, s, strerror(errno)); 300 exit(3); 301 } 302 303 static void 304 perr(char *s) 305 { 306 int err = errno; 307 308 if (s != NULL) 309 (void) fprintf(stderr, "%s: ", procname); 310 else 311 s = procname; 312 313 errno = err; 314 perror(s); 315 } 316 317 static void 318 usage(void) 319 { 320 (void) fprintf(stderr, 321 "usage:\t%s [-v] [-S] [-D|-N] [-s spec] { pid | core } ...\n" 322 "\t%s -e [-D|-N] [-M] [-s spec] cmd [args ...]\n" 323 "\t%s -l [-v] [privilege ...]\n" 324 " (report, set or list process privileges)\n", command, 325 command, command); 326 exit(2); 327 /*NOTREACHED*/ 328 } 329 330 /* 331 * Parse the privilege bits to add and/or remove from 332 * a privilege set. 333 * 334 * [EPIL][+-=]priv,priv,priv 335 */ 336 337 static int 338 strindex(char c, const char *str) 339 { 340 const char *s; 341 342 if (islower(c)) 343 c = toupper(c); 344 345 s = strchr(str, c); 346 347 if (s == NULL) 348 return (-1); 349 else 350 return (s - str); 351 } 352 353 static void 354 badspec(const char *spec) 355 { 356 (void) fprintf(stderr, "%s: bad privilege specification: \"%s\"\n", 357 command, spec); 358 exit(3); 359 /*NOTREACHED*/ 360 } 361 362 /* 363 * For each set, you can set either add and/or 364 * remove or you can set assign. 365 */ 366 static priv_set_t **rem, **add, **assign; 367 static const priv_impl_info_t *pri = NULL; 368 static char *sets; 369 370 static void 371 loadprivinfo(void) 372 { 373 int i; 374 375 if (pri != NULL) 376 return; 377 378 pri = getprivimplinfo(); 379 380 if (pri == NULL) 381 fatal("getprivimplinfo"); 382 383 sets = malloc(pri->priv_nsets + 1); 384 if (sets == NULL) 385 fatal("malloc"); 386 387 for (i = 0; i < pri->priv_nsets; i++) { 388 sets[i] = *priv_getsetbynum(i); 389 if (islower(sets[i])) 390 sets[i] = toupper(sets[i]); 391 } 392 393 sets[pri->priv_nsets] = '\0'; 394 395 rem = calloc(pri->priv_nsets, sizeof (priv_set_t *)); 396 add = calloc(pri->priv_nsets, sizeof (priv_set_t *)); 397 assign = calloc(pri->priv_nsets, sizeof (priv_set_t *)); 398 if (rem == NULL || add == NULL || assign == NULL) 399 fatal("calloc"); 400 } 401 402 static int 403 parsespec(const char *spec) 404 { 405 char *p; 406 const char *q; 407 int count; 408 priv_set_t ***toupd; 409 priv_set_t *upd; 410 int i; 411 boolean_t freeupd = B_TRUE; 412 413 if (pri == NULL) 414 loadprivinfo(); 415 416 p = strpbrk(spec, "+-="); 417 418 if (p == NULL || p - spec > pri->priv_nsets) 419 badspec(spec); 420 421 if (p[1] == '\0' || (upd = priv_str_to_set(p + 1, ",", NULL)) == NULL) 422 badspec(p + 1); 423 424 count = p - spec; 425 switch (*p) { 426 case '+': 427 toupd = &add; 428 break; 429 case '-': 430 toupd = &rem; 431 priv_inverse(upd); 432 break; 433 case '=': 434 toupd = &assign; 435 break; 436 } 437 438 /* Update all sets? */ 439 if (count == 0 || *spec == 'a' || *spec == 'A') { 440 count = pri->priv_nsets; 441 q = sets; 442 } else 443 q = spec; 444 445 for (i = 0; i < count; i++) { 446 int ind = strindex(q[i], sets); 447 448 if (ind == -1) 449 badspec(spec); 450 451 /* Assign is mutually exclusive with add/remove and itself */ 452 if (((toupd == &rem || toupd == &add) && assign[ind] != NULL) || 453 (toupd == &assign && (assign[ind] != NULL || 454 rem[ind] != NULL || add[ind] != NULL))) { 455 (void) fprintf(stderr, "%s: conflicting spec: %s\n", 456 command, spec); 457 exit(1); 458 } 459 if ((*toupd)[ind] != NULL) { 460 if (*p == '-') 461 priv_intersect(upd, (*toupd)[ind]); 462 else 463 priv_union(upd, (*toupd)[ind]); 464 } else { 465 (*toupd)[ind] = upd; 466 freeupd = B_FALSE; 467 } 468 } 469 if (freeupd) 470 priv_freeset(upd); 471 return (0); 472 } 473 474 static void 475 privupdate(prpriv_t *pr, const char *arg) 476 { 477 int i; 478 479 if (sets != NULL) { 480 for (i = 0; i < pri->priv_nsets; i++) { 481 priv_set_t *target = 482 (priv_set_t *)&pr->pr_sets[pr->pr_setsize * i]; 483 if (rem[i] != NULL) 484 priv_intersect(rem[i], target); 485 if (add[i] != NULL) 486 priv_union(add[i], target); 487 if (assign[i] != NULL) 488 priv_copyset(assign[i], target); 489 } 490 } 491 492 if (Doff || Don || pfexec || xpol) { 493 priv_info_uint_t *pii; 494 int sz = PRIV_PRPRIV_SIZE(pr); 495 char *x = (char *)pr + PRIV_PRPRIV_INFO_OFFSET(pr); 496 uint32_t fl = 0; 497 498 while (x < (char *)pr + sz) { 499 /* LINTED: alignment */ 500 priv_info_t *pi = (priv_info_t *)x; 501 502 if (pi->priv_info_type == PRIV_INFO_FLAGS) { 503 /* LINTED: alignment */ 504 pii = (priv_info_uint_t *)x; 505 fl = pii->val; 506 goto done; 507 } 508 if (pi->priv_info_size > pr->pr_infosize || 509 pi->priv_info_size <= sizeof (priv_info_t) || 510 (pi->priv_info_size & 3) != 0) 511 break; 512 x += pi->priv_info_size; 513 } 514 (void) fprintf(stderr, 515 "%s: cannot find privilege flags to set\n", arg); 516 pr->pr_infosize = 0; 517 return; 518 done: 519 520 pr->pr_infosize = sizeof (priv_info_uint_t); 521 /* LINTED: alignment */ 522 pii = (priv_info_uint_t *) 523 ((char *)pr + PRIV_PRPRIV_INFO_OFFSET(pr)); 524 525 if (Don) 526 fl |= PRIV_DEBUG; 527 if (Doff) 528 fl &= ~PRIV_DEBUG; 529 if (pfexec) 530 fl |= PRIV_PFEXEC; 531 if (xpol) 532 fl |= PRIV_XPOLICY; 533 534 pii->info.priv_info_size = sizeof (*pii); 535 pii->info.priv_info_type = PRIV_INFO_FLAGS; 536 pii->val = fl; 537 } else { 538 pr->pr_infosize = 0; 539 } 540 } 541 542 static void 543 privupdate_self(void) 544 { 545 int set; 546 547 if (mac_aware) { 548 if (setpflags(NET_MAC_AWARE, 1) != 0) 549 fatal("setpflags(NET_MAC_AWARE)"); 550 if (setpflags(NET_MAC_AWARE_INHERIT, 1) != 0) 551 fatal("setpflags(NET_MAC_AWARE_INHERIT)"); 552 } 553 if (pfexec) { 554 if (setpflags(PRIV_PFEXEC, 1) != 0) 555 fatal("setpflags(PRIV_PFEXEC)"); 556 } 557 558 if (sets != NULL) { 559 priv_set_t *target = priv_allocset(); 560 561 if (target == NULL) 562 fatal("priv_allocet"); 563 564 set = priv_getsetbyname(PRIV_INHERITABLE); 565 if (rem[set] != NULL || add[set] != NULL || 566 assign[set] != NULL) { 567 (void) getppriv(PRIV_INHERITABLE, target); 568 if (rem[set] != NULL) 569 priv_intersect(rem[set], target); 570 if (add[set] != NULL) 571 priv_union(add[set], target); 572 if (assign[set] != NULL) 573 priv_copyset(assign[set], target); 574 if (setppriv(PRIV_SET, PRIV_INHERITABLE, target) != 0) 575 fatal("setppriv(Inheritable)"); 576 } 577 set = priv_getsetbyname(PRIV_LIMIT); 578 if (rem[set] != NULL || add[set] != NULL || 579 assign[set] != NULL) { 580 (void) getppriv(PRIV_LIMIT, target); 581 if (rem[set] != NULL) 582 priv_intersect(rem[set], target); 583 if (add[set] != NULL) 584 priv_union(add[set], target); 585 if (assign[set] != NULL) 586 priv_copyset(assign[set], target); 587 if (setppriv(PRIV_SET, PRIV_LIMIT, target) != 0) 588 fatal("setppriv(Limit)"); 589 } 590 priv_freeset(target); 591 } 592 593 if (Doff || Don) 594 (void) setpflags(PRIV_DEBUG, Don ? 1 : 0); 595 if (xpol) 596 (void) setpflags(PRIV_XPOLICY, 1); 597 if (pfexec) 598 (void) setpflags(PRIV_PFEXEC, 1); 599 } 600 601 static int 602 dopriv(const char *p) 603 { 604 (void) puts(p); 605 if (verb) { 606 char *text = priv_gettext(p); 607 char *p, *q; 608 if (text == NULL) 609 return (1); 610 for (p = text; q = strchr(p, '\n'); p = q + 1) { 611 *q = '\0'; 612 (void) printf("\t%s\n", p); 613 } 614 free(text); 615 } 616 return (0); 617 } 618 619 static int 620 dumppriv(char **argv) 621 { 622 int rc = 0; 623 const char *pname; 624 int i; 625 626 if (argv[0] == NULL) { 627 for (i = 0; ((pname = priv_getbynum(i++)) != NULL); ) 628 rc += dopriv(pname); 629 } else { 630 for (; *argv; argv++) { 631 priv_set_t *pset = priv_str_to_set(*argv, ",", NULL); 632 633 if (pset == NULL) { 634 (void) fprintf(stderr, "%s: %s: bad privilege" 635 " list\n", command, *argv); 636 rc++; 637 continue; 638 } 639 for (i = 0; ((pname = priv_getbynum(i++)) != NULL); ) 640 if (priv_ismember(pset, pname)) 641 rc += dopriv(pname); 642 } 643 } 644 return (rc); 645 } 646 647 static struct { 648 int flag; 649 char *name; 650 } flags[] = { 651 { PRIV_DEBUG, "PRIV_DEBUG" }, 652 { PRIV_AWARE, "PRIV_AWARE" }, 653 { PRIV_AWARE_INHERIT, "PRIV_AWARE_INHERIT" }, 654 { PRIV_AWARE_RESET, "PRIV_AWARE_RESET" }, 655 { PRIV_XPOLICY, "PRIV_XPOLICY" }, 656 { PRIV_PFEXEC, "PRIV_PFEXEC" }, 657 { NET_MAC_AWARE, "NET_MAC_AWARE" }, 658 { NET_MAC_AWARE_INHERIT, "NET_MAC_AWARE_INHERIT" }, 659 }; 660 661 /* 662 * Print flags preceeded by a space. 663 */ 664 static void 665 flags2str(uint_t pflags) 666 { 667 char c = ' '; 668 int i; 669 670 if (pflags == 0) { 671 (void) fputs(" <none>", stdout); 672 return; 673 } 674 for (i = 0; i < sizeof (flags)/sizeof (flags[0]) && pflags != 0; i++) { 675 if ((pflags & flags[i].flag) != 0) { 676 (void) printf("%c%s", c, flags[i].name); 677 pflags &= ~flags[i].flag; 678 c = '|'; 679 } 680 } 681 if (pflags != 0) 682 (void) printf("%c<0x%x>", c, pflags); 683 } 684