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