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
main(int argc,char ** argv)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
look(char * arg)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
fatal(const char * s)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
perr(char * s)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
usage(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
strindex(char c,const char * str)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
badspec(const char * spec)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
loadprivinfo(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
parsespec(const char * spec)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
privupdate(prpriv_t * pr,const char * arg)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
privupdate_self(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
dopriv(const char * p)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
dumppriv(char ** argv)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
flags2str(uint_t pflags)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 }
683