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