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