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