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