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 }