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