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