xref: /titanic_52/usr/src/lib/libc/port/gen/privlib.c (revision c7158ae983f5a04c4a998f468ecefba6d23ba721)
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 /*
23  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI" /* TSOL 8 */
28 
29 #pragma weak getprivimplinfo	= _getprivimplinfo
30 #pragma weak priv_addset	= _priv_addset
31 #pragma weak priv_allocset	= _priv_allocset
32 #pragma weak priv_copyset	= _priv_copyset
33 #pragma weak priv_delset	= _priv_delset
34 #pragma weak priv_emptyset	= _priv_emptyset
35 #pragma weak priv_fillset	= _priv_fillset
36 #pragma weak priv_freeset	= _priv_freeset
37 #pragma weak priv_getbyname	= _priv_getbyname
38 #pragma weak priv_getbynum	= _priv_getbynum
39 #pragma weak priv_getsetbyname	= _priv_getsetbyname
40 #pragma weak priv_getsetbynum	= _priv_getsetbynum
41 #pragma weak priv_ineffect	= _priv_ineffect
42 #pragma weak priv_intersect	= _priv_intersect
43 #pragma weak priv_inverse	= _priv_inverse
44 #pragma weak priv_isemptyset	= _priv_isemptyset
45 #pragma weak priv_isequalset	= _priv_isequalset
46 #pragma weak priv_isfullset	= _priv_isfullset
47 #pragma weak priv_ismember	= _priv_ismember
48 #pragma weak priv_issubset	= _priv_issubset
49 #pragma weak priv_set		= _priv_set
50 #pragma weak priv_union		= _priv_union
51 
52 #include "synonyms.h"
53 
54 #define	_STRUCTURED_PROC	1
55 
56 #include "priv_private.h"
57 #include "mtlib.h"
58 #include "libc.h"
59 #include <errno.h>
60 #include <stdarg.h>
61 #include <stdlib.h>
62 #include <unistd.h>
63 #include <strings.h>
64 #include <synch.h>
65 #include <alloca.h>
66 #include <atomic.h>
67 #include <sys/ucred.h>
68 #include <sys/procfs.h>
69 #include <sys/param.h>
70 #include <sys/corectl.h>
71 #include <priv_utils.h>
72 #include <zone.h>
73 
74 /* Include each string only once - until the compiler/linker are fixed */
75 static const char *permitted	= PRIV_PERMITTED;
76 static const char *effective	= PRIV_EFFECTIVE;
77 static const char *limit	= PRIV_LIMIT;
78 static const char *inheritable	= PRIV_INHERITABLE;
79 /*
80  * Data independent privilege set operations.
81  *
82  * Only a few functions are provided that do not default to
83  * the system implementation of privileges.  A limited set of
84  * interfaces is provided that accepts a priv_data_t *
85  * argument; this set of interfaces is a private interface between libc
86  * and libproc.  It is delivered in order to interpret privilege sets
87  * in debuggers in a implementation independent way.  As such, we
88  * don't need to provide the bulk of the interfaces, only a few
89  * boolean tests (isfull, isempty) the name<->num mappings and
90  * set pretty print functions.   The boolean tests are only needed for
91  * the latter, so those aren't provided externally.
92  *
93  * Additionally, we provide the function that maps the kernel implementation
94  * structure into a libc private data structure.
95  */
96 
97 priv_data_t *privdata;
98 
99 static mutex_t pd_lock = DEFAULTMUTEX;
100 
101 static int
102 parseninfo(priv_info_names_t *na, char ***buf, int *cp)
103 {
104 	char *q;
105 	int i;
106 
107 	*buf = libc_malloc(sizeof (char *) * na->cnt);
108 
109 	if (*buf == NULL)
110 		return (-1);
111 
112 	q = na->names;
113 
114 	for (i = 0; i < na->cnt; i++) {
115 		int l = strlen(q);
116 
117 		(*buf)[i] = q;
118 		q += l + 1;
119 	}
120 	*cp = na->cnt;
121 	return (0);
122 }
123 
124 struct strint {
125 	char *name;
126 	int rank;
127 };
128 
129 static int
130 strintcmp(const void *a, const void *b)
131 {
132 	const struct strint *ap = a;
133 	const struct strint *bp = b;
134 
135 	return (strcasecmp(ap->name, bp->name));
136 }
137 
138 priv_data_t *
139 __priv_parse_info(priv_impl_info_t *ip)
140 {
141 	priv_data_t *tmp;
142 	char *x;
143 	size_t size = PRIV_IMPL_INFO_SIZE(ip);
144 	int i;
145 
146 	tmp = libc_malloc(sizeof (*tmp));
147 
148 	if (tmp == NULL)
149 		return (NULL);
150 
151 	(void) memset(tmp, 0, sizeof (*tmp));
152 
153 	tmp->pd_pinfo = ip;
154 	tmp->pd_setsize = sizeof (priv_chunk_t) * ip->priv_setsize;
155 	tmp->pd_ucredsize = UCRED_SIZE(ip);
156 
157 	x = (char *)ip;
158 	x += ip->priv_headersize;
159 
160 	while (x < ((char *)ip) + size) {
161 		/* LINTED: alignment */
162 		priv_info_names_t *na = (priv_info_names_t *)x;
163 		/* LINTED: alignment */
164 		priv_info_set_t *st = (priv_info_set_t *)x;
165 		struct strint *tmparr;
166 
167 		switch (na->info.priv_info_type) {
168 		case PRIV_INFO_SETNAMES:
169 			if (parseninfo(na, &tmp->pd_setnames, &tmp->pd_nsets))
170 				goto out;
171 			break;
172 		case PRIV_INFO_PRIVNAMES:
173 			if (parseninfo(na, &tmp->pd_privnames, &tmp->pd_nprivs))
174 				goto out;
175 			/*
176 			 * We compute a sorted index which allows us
177 			 * to present a sorted list of privileges
178 			 * without actually having to sort it each time.
179 			 */
180 			tmp->pd_setsort = libc_malloc(tmp->pd_nprivs *
181 			    sizeof (int));
182 			if (tmp->pd_setsort == NULL)
183 				goto out;
184 
185 			tmparr = libc_malloc(tmp->pd_nprivs *
186 			    sizeof (struct strint));
187 
188 			if (tmparr == NULL)
189 				goto out;
190 
191 			for (i = 0; i < tmp->pd_nprivs; i++) {
192 				tmparr[i].rank = i;
193 				tmparr[i].name = tmp->pd_privnames[i];
194 			}
195 			qsort(tmparr, tmp->pd_nprivs, sizeof (struct strint),
196 				strintcmp);
197 			for (i = 0; i < tmp->pd_nprivs; i++)
198 				tmp->pd_setsort[i] = tmparr[i].rank;
199 			libc_free(tmparr);
200 			break;
201 		case PRIV_INFO_BASICPRIVS:
202 			tmp->pd_basicset = (priv_set_t *)&st->set[0];
203 			break;
204 		default:
205 			/* unknown, ignore */
206 			break;
207 		}
208 		x += na->info.priv_info_size;
209 	}
210 	return (tmp);
211 out:
212 	libc_free(tmp->pd_setnames);
213 	libc_free(tmp->pd_privnames);
214 	libc_free(tmp->pd_setsort);
215 	libc_free(tmp);
216 	return (NULL);
217 }
218 
219 /*
220  * Caller must have allocated d->pd_pinfo and should free it,
221  * if necessary.
222  */
223 void
224 __priv_free_info(priv_data_t *d)
225 {
226 	libc_free(d->pd_setnames);
227 	libc_free(d->pd_privnames);
228 	libc_free(d->pd_setsort);
229 	libc_free(d);
230 }
231 
232 /*
233  * Return with the pd_lock held and data loaded or indicate failure.
234  */
235 int
236 lock_data(void)
237 {
238 	if (__priv_getdata() == NULL)
239 		return (-1);
240 
241 	lmutex_lock(&pd_lock);
242 	return (0);
243 }
244 
245 boolean_t
246 refresh_data(void)
247 {
248 	priv_impl_info_t *ip, ii;
249 	priv_data_t *tmp;
250 	char *p0, *q0;
251 	int oldn, newn;
252 	int i;
253 
254 	if (getprivinfo(&ii, sizeof (ii)) != 0 ||
255 	    ii.priv_max == privdata->pd_nprivs)
256 		return (B_FALSE);
257 
258 	ip = alloca(PRIV_IMPL_INFO_SIZE(&ii));
259 
260 	(void) getprivinfo(ip, PRIV_IMPL_INFO_SIZE(&ii));
261 
262 	/* Parse the info; then copy the additional bits */
263 	tmp = __priv_parse_info(ip);
264 	if (tmp == NULL)
265 		return (B_FALSE);
266 
267 	oldn = privdata->pd_nprivs;
268 	p0 = privdata->pd_privnames[0];
269 
270 	newn = tmp->pd_nprivs;
271 	q0 = tmp->pd_privnames[0];
272 
273 	/* copy the extra information to the old datastructure */
274 	(void) memcpy((char *)privdata->pd_pinfo + sizeof (priv_impl_info_t),
275 		(char *)ip + sizeof (priv_impl_info_t),
276 		PRIV_IMPL_INFO_SIZE(ip) - sizeof (priv_impl_info_t));
277 
278 	/* Copy the first oldn pointers */
279 	(void) memcpy(tmp->pd_privnames, privdata->pd_privnames,
280 	    oldn * sizeof (char *));
281 
282 	/* Adjust the rest */
283 	for (i = oldn; i < newn; i++)
284 		tmp->pd_privnames[i] += p0 - q0;
285 
286 	/* Install the larger arrays */
287 	libc_free(privdata->pd_privnames);
288 	privdata->pd_privnames = tmp->pd_privnames;
289 	tmp->pd_privnames = NULL;
290 
291 	libc_free(privdata->pd_setsort);
292 	privdata->pd_setsort = tmp->pd_setsort;
293 	tmp->pd_setsort = NULL;
294 
295 	/* Copy the rest of the data */
296 	*privdata->pd_pinfo = *ip;
297 
298 	privdata->pd_nprivs = newn;
299 
300 	__priv_free_info(tmp);
301 	return (B_TRUE);
302 }
303 
304 void
305 unlock_data(void)
306 {
307 	lmutex_unlock(&pd_lock);
308 }
309 
310 static priv_set_t *__priv_allocset(priv_data_t *);
311 
312 priv_data_t *
313 __priv_getdata(void)
314 {
315 	if (privdata == NULL) {
316 		lmutex_lock(&pd_lock);
317 		if (privdata == NULL) {
318 			priv_data_t *tmp;
319 			priv_impl_info_t *ip;
320 			size_t size = sizeof (priv_impl_info_t) + 2048;
321 			size_t realsize;
322 			priv_impl_info_t *aip = alloca(size);
323 
324 			if (getprivinfo(aip, size) != 0)
325 				goto out;
326 
327 			realsize = PRIV_IMPL_INFO_SIZE(aip);
328 
329 			ip = libc_malloc(realsize);
330 
331 			if (ip == NULL)
332 				goto out;
333 
334 			if (realsize <= size) {
335 				(void) memcpy(ip, aip, realsize);
336 			} else if (getprivinfo(ip, realsize) != 0) {
337 				libc_free(ip);
338 				goto out;
339 			}
340 
341 			if ((tmp = __priv_parse_info(ip)) == NULL) {
342 				libc_free(ip);
343 				goto out;
344 			}
345 
346 			/* Allocate the zoneset just once, here */
347 			tmp->pd_zoneset = __priv_allocset(tmp);
348 			if (tmp->pd_zoneset == NULL)
349 				goto clean;
350 
351 			if (zone_getattr(getzoneid(), ZONE_ATTR_PRIVSET,
352 			    tmp->pd_zoneset, tmp->pd_setsize)
353 			    == tmp->pd_setsize) {
354 				membar_producer();
355 				privdata = tmp;
356 				goto out;
357 			}
358 
359 			priv_freeset(tmp->pd_zoneset);
360 clean:
361 			__priv_free_info(tmp);
362 			libc_free(ip);
363 		}
364 out:
365 		lmutex_unlock(&pd_lock);
366 	}
367 	membar_consumer();
368 	return (privdata);
369 }
370 
371 const priv_impl_info_t *
372 _getprivimplinfo(void)
373 {
374 	priv_data_t *d;
375 
376 	LOADPRIVDATA(d);
377 
378 	return (d->pd_pinfo);
379 }
380 
381 static priv_set_t *
382 priv_vlist(va_list ap)
383 {
384 	priv_set_t *pset = priv_allocset();
385 	const char *priv;
386 
387 	if (pset == NULL)
388 		return (NULL);
389 
390 	priv_emptyset(pset);
391 
392 	while ((priv = va_arg(ap, const char *)) != NULL) {
393 		if (priv_addset(pset, priv) < 0) {
394 			priv_freeset(pset);
395 			return (NULL);
396 		}
397 	}
398 	return (pset);
399 }
400 
401 /*
402  * priv_set(op, set, priv_id1, priv_id2, ..., NULL)
403  *
404  * Library routine to enable a user process to set a specific
405  * privilege set appropriately using a single call.  User is
406  * required to terminate the list of privileges with NULL.
407  */
408 int
409 priv_set(priv_op_t op, priv_ptype_t setname, ...)
410 {
411 	va_list ap;
412 	priv_set_t *pset;
413 	int ret;
414 
415 	va_start(ap, setname);
416 
417 	pset = priv_vlist(ap);
418 
419 	va_end(ap);
420 
421 	if (pset == NULL)
422 		return (-1);
423 
424 	/* All sets */
425 	if (setname == NULL) {
426 		priv_data_t *d;
427 		int set;
428 
429 		LOADPRIVDATA(d);
430 
431 		for (set = 0; set < d->pd_nsets; set++)
432 			if ((ret = syscall(SYS_privsys, PRIVSYS_SETPPRIV, op,
433 					set, (void *)pset, d->pd_setsize)) != 0)
434 				break;
435 	} else {
436 		ret = setppriv(op, setname, pset);
437 	}
438 
439 	priv_freeset(pset);
440 	return (ret);
441 }
442 
443 /*
444  * priv_ineffect(privilege).
445  * tests the existence of a privilege against the effective set.
446  */
447 boolean_t
448 priv_ineffect(const char *priv)
449 {
450 	priv_set_t *curset;
451 	boolean_t res;
452 
453 	curset = priv_allocset();
454 
455 	if (curset == NULL)
456 		return (B_FALSE);
457 
458 	if (getppriv(effective, curset) != 0 ||
459 	    !priv_ismember(curset, priv))
460 		res = B_FALSE;
461 	else
462 		res = B_TRUE;
463 
464 	priv_freeset(curset);
465 
466 	return (res);
467 }
468 
469 /*
470  * The routine __init_daemon_priv() is private to Solaris and is
471  * used by daemons to limit the privileges they can use and
472  * to set the uid they run under.
473  */
474 
475 static const char root_cp[] = "/core.%f.%t";
476 static const char daemon_cp[] = "/var/tmp/core.%f.%t";
477 
478 int
479 __init_daemon_priv(int flags, uid_t uid, gid_t gid, ...)
480 {
481 	priv_set_t *nset;
482 	priv_set_t *perm = NULL;
483 	va_list pa;
484 	priv_data_t *d;
485 	int ret = -1;
486 	char buf[1024];
487 
488 	LOADPRIVDATA(d);
489 
490 	va_start(pa, gid);
491 
492 	nset = priv_vlist(pa);
493 
494 	va_end(pa);
495 
496 	if (nset == NULL)
497 		return (-1);
498 
499 	/* Always add the basic set */
500 	if (d->pd_basicset != NULL)
501 		priv_union(d->pd_basicset, nset);
502 
503 	/*
504 	 * This is not a significant failure: it allows us to start programs
505 	 * with sufficient privileges and with the proper uid.   We don't
506 	 * care enough about the extra groups in that case.
507 	 */
508 	if (flags & PU_RESETGROUPS)
509 		(void) setgroups(0, NULL);
510 
511 	if (gid != (gid_t)-1 && setgid(gid) != 0)
512 		goto end;
513 
514 	perm = priv_allocset();
515 	if (perm == NULL)
516 		goto end;
517 
518 	/* E = P */
519 	(void) getppriv(permitted, perm);
520 	(void) setppriv(PRIV_SET, effective, perm);
521 
522 	/* Now reset suid and euid */
523 	if (uid != (uid_t)-1 && setreuid(uid, uid) != 0)
524 		goto end;
525 
526 	/* Check for the limit privs */
527 	if ((flags & PU_LIMITPRIVS) &&
528 	    setppriv(PRIV_SET, limit, nset) != 0)
529 		goto end;
530 
531 	if (flags & PU_CLEARLIMITSET) {
532 		priv_emptyset(perm);
533 		if (setppriv(PRIV_SET, limit, perm) != 0)
534 			goto end;
535 	}
536 
537 	/* Remove the privileges from all the other sets */
538 	if (setppriv(PRIV_SET, permitted, nset) != 0)
539 		goto end;
540 
541 	if (!(flags & PU_INHERITPRIVS))
542 		priv_emptyset(nset);
543 
544 	ret = setppriv(PRIV_SET, inheritable, nset);
545 end:
546 	priv_freeset(nset);
547 	priv_freeset(perm);
548 
549 	if (core_get_process_path(buf, sizeof (buf), getpid()) == 0 &&
550 	    strcmp(buf, "core") == 0) {
551 
552 		if ((uid == (uid_t)-1 ? geteuid() : uid) == 0) {
553 			(void) core_set_process_path(root_cp, sizeof (root_cp),
554 			    getpid());
555 		} else {
556 			(void) core_set_process_path(daemon_cp,
557 			    sizeof (daemon_cp), getpid());
558 		}
559 	}
560 	(void) setpflags(__PROC_PROTECT, 0);
561 
562 	return (ret);
563 }
564 
565 /*
566  * The routine __fini_daemon_priv() is private to Solaris and is
567  * used by daemons to clear remaining unwanted privileges and
568  * reenable core dumps.
569  */
570 void
571 __fini_daemon_priv(const char *priv, ...)
572 {
573 	priv_set_t *nset;
574 	va_list pa;
575 
576 	va_start(pa, priv);
577 
578 	if (priv != NULL) {
579 		nset = priv_vlist(pa);
580 		if (nset == NULL)
581 			return;
582 
583 		(void) priv_addset(nset, priv);
584 		(void) setppriv(PRIV_OFF, permitted, nset);
585 		priv_freeset(nset);
586 	}
587 
588 	va_end(pa);
589 
590 	(void) setpflags(__PROC_PROTECT, 0);
591 }
592 
593 /*
594  * The routine __init_suid_priv() is private to Solaris and is
595  * used by set-uid root programs to limit the privileges acquired
596  * to those actually needed.
597  */
598 
599 static priv_set_t *bracketpriv;
600 
601 int
602 __init_suid_priv(int flags, ...)
603 {
604 	priv_set_t *nset = NULL;
605 	priv_set_t *tmpset = NULL;
606 	va_list pa;
607 	int r = -1;
608 	uid_t ruid, euid;
609 
610 	euid = geteuid();
611 
612 	/* If we're not set-uid root, don't reset the uid */
613 	if (euid == 0) {
614 		ruid = getuid();
615 		/* If we're running as root, keep everything */
616 		if (ruid == 0)
617 			return (0);
618 	}
619 
620 	/* Can call this only once */
621 	if (bracketpriv != NULL)
622 		return (-1);
623 
624 	va_start(pa, flags);
625 
626 	nset = priv_vlist(pa);
627 
628 	va_end(pa);
629 
630 	if (nset == NULL)
631 		goto end;
632 
633 	tmpset = priv_allocset();
634 
635 	if (tmpset == NULL)
636 		goto end;
637 
638 	/* We cannot grow our privileges beyond P, so start there */
639 	(void) getppriv(permitted, tmpset);
640 
641 	/* Is the privilege we need even in P? */
642 	if (!priv_issubset(nset, tmpset))
643 		goto end;
644 
645 	bracketpriv = priv_allocset();
646 	if (bracketpriv == NULL)
647 		goto end;
648 
649 	priv_copyset(nset, bracketpriv);
650 
651 	/* Always add the basic set */
652 	priv_union(priv_basic(), nset);
653 
654 	/* But don't add what we don't have */
655 	priv_intersect(tmpset, nset);
656 
657 	(void) getppriv(inheritable, tmpset);
658 
659 	/* And stir in the inheritable privileges */
660 	priv_union(tmpset, nset);
661 
662 	if ((r = setppriv(PRIV_SET, effective, tmpset)) != 0)
663 		goto end;
664 
665 	if ((r = setppriv(PRIV_SET, permitted, nset)) != 0)
666 		goto end;
667 
668 	if (flags & PU_CLEARLIMITSET)
669 		priv_emptyset(nset);
670 
671 	if ((flags & (PU_LIMITPRIVS|PU_CLEARLIMITSET)) != 0 &&
672 	    (r = setppriv(PRIV_SET, limit, nset)) != 0)
673 		goto end;
674 
675 	if (euid == 0)
676 		r = setreuid(ruid, ruid);
677 
678 end:
679 	priv_freeset(tmpset);
680 	priv_freeset(nset);
681 	if (r != 0) {
682 		/* Fail without leaving uid 0 around */
683 		if (euid == 0)
684 			(void) setreuid(ruid, ruid);
685 		priv_freeset(bracketpriv);
686 		bracketpriv = NULL;
687 	}
688 
689 	return (r);
690 }
691 
692 /*
693  * Toggle privileges on/off in the effective set.
694  */
695 int
696 __priv_bracket(priv_op_t op)
697 {
698 	/* We're running fully privileged or didn't check errors first time */
699 	if (bracketpriv == NULL)
700 		return (0);
701 
702 	/* Only PRIV_ON and PRIV_OFF are valid */
703 	if (op == PRIV_SET)
704 		return (-1);
705 
706 	return (setppriv(op, effective, bracketpriv));
707 }
708 
709 /*
710  * Remove privileges from E & P.
711  */
712 void
713 __priv_relinquish(void)
714 {
715 	if (bracketpriv != NULL) {
716 		(void) setppriv(PRIV_OFF, permitted, bracketpriv);
717 		priv_freeset(bracketpriv);
718 		bracketpriv = NULL;
719 	}
720 }
721 
722 /*
723  * Use binary search on the ordered list.
724  */
725 int
726 __priv_getbyname(const priv_data_t *d, const char *name)
727 {
728 	char *const *list;
729 	const int *order;
730 	int lo = 0;
731 	int hi;
732 
733 	if (d == NULL)
734 		return (-1);
735 
736 	list = d->pd_privnames;
737 	order = d->pd_setsort;
738 	hi = d->pd_nprivs - 1;
739 
740 	if (strncasecmp(name, "priv_", 5) == 0)
741 		name += 5;
742 
743 	do {
744 		int mid = (lo + hi) / 2;
745 		int res = strcasecmp(name, list[order[mid]]);
746 
747 		if (res == 0)
748 			return (order[mid]);
749 		else if (res < 0)
750 			hi = mid - 1;
751 		else
752 			lo = mid + 1;
753 	} while (lo <= hi);
754 
755 	errno = EINVAL;
756 	return (-1);
757 }
758 
759 int
760 priv_getbyname(const char *name)
761 {
762 	WITHPRIVLOCKED(int, -1, __priv_getbyname(GETPRIVDATA(), name));
763 }
764 
765 int
766 __priv_getsetbyname(const priv_data_t *d, const char *name)
767 {
768 	int i;
769 	int n = d->pd_nsets;
770 	char *const *list = d->pd_setnames;
771 
772 	if (strncasecmp(name, "priv_", 5) == 0)
773 		name += 5;
774 
775 	for (i = 0; i < n; i++) {
776 		if (strcasecmp(list[i], name) == 0)
777 			return (i);
778 	}
779 
780 	errno = EINVAL;
781 	return (-1);
782 }
783 
784 int
785 priv_getsetbyname(const char *name)
786 {
787 	/* Not locked: sets don't change */
788 	return (__priv_getsetbyname(GETPRIVDATA(), name));
789 }
790 
791 static const char *
792 priv_bynum(int i, int n, char **list)
793 {
794 	if (i < 0 || i >= n)
795 		return (NULL);
796 
797 	return (list[i]);
798 }
799 
800 const char *
801 __priv_getbynum(const priv_data_t *d, int num)
802 {
803 	if (d == NULL)
804 		return (NULL);
805 	return (priv_bynum(num, d->pd_nprivs, d->pd_privnames));
806 }
807 
808 const char *
809 priv_getbynum(int num)
810 {
811 	WITHPRIVLOCKED(const char *, NULL, __priv_getbynum(GETPRIVDATA(), num));
812 }
813 
814 const char *
815 __priv_getsetbynum(const priv_data_t *d, int num)
816 {
817 	if (d == NULL)
818 		return (NULL);
819 	return (priv_bynum(num, d->pd_nsets, d->pd_setnames));
820 }
821 
822 const char *
823 priv_getsetbynum(int num)
824 {
825 	return (__priv_getsetbynum(GETPRIVDATA(), num));
826 }
827 
828 
829 /*
830  * Privilege manipulation functions
831  *
832  * Without knowing the details of the privilege set implementation,
833  * opaque pointers can be used to manipulate sets at will.
834  */
835 
836 static priv_set_t *
837 __priv_allocset(priv_data_t *d)
838 {
839 	if (d == NULL)
840 		return (NULL);
841 
842 	return (libc_malloc(d->pd_setsize));
843 }
844 
845 priv_set_t *
846 priv_allocset(void)
847 {
848 	return (__priv_allocset(GETPRIVDATA()));
849 }
850 
851 void
852 priv_freeset(priv_set_t *p)
853 {
854 	int er = errno;
855 
856 	libc_free(p);
857 	errno = er;
858 }
859 
860 void
861 __priv_emptyset(priv_data_t *d, priv_set_t *set)
862 {
863 	(void) memset(set, 0, d->pd_setsize);
864 }
865 
866 void
867 priv_emptyset(priv_set_t *set)
868 {
869 	__priv_emptyset(GETPRIVDATA(), set);
870 }
871 
872 void
873 __priv_fillset(priv_data_t *d, priv_set_t *set)
874 {
875 	(void) memset(set, ~0, d->pd_setsize);
876 }
877 
878 void
879 priv_fillset(priv_set_t *set)
880 {
881 	__priv_fillset(GETPRIVDATA(), set);
882 }
883 
884 
885 #define	PRIV_TEST_BODY_D(d, test) \
886 	int i; \
887 \
888 	for (i = d->pd_pinfo->priv_setsize; i-- > 0; ) \
889 		if (!(test)) \
890 			return (B_FALSE); \
891 \
892 	return (B_TRUE)
893 
894 boolean_t
895 priv_isequalset(const priv_set_t *a, const priv_set_t *b)
896 {
897 	priv_data_t *d;
898 
899 	LOADPRIVDATA(d);
900 
901 	return ((boolean_t)(memcmp(a, b, d->pd_setsize) == 0));
902 }
903 
904 boolean_t
905 __priv_isemptyset(priv_data_t *d, const priv_set_t *set)
906 {
907 	PRIV_TEST_BODY_D(d, ((priv_chunk_t *)set)[i] == 0);
908 }
909 
910 boolean_t
911 priv_isemptyset(const priv_set_t *set)
912 {
913 	return (__priv_isemptyset(GETPRIVDATA(), set));
914 }
915 
916 boolean_t
917 __priv_isfullset(priv_data_t *d, const priv_set_t *set)
918 {
919 	PRIV_TEST_BODY_D(d, ((priv_chunk_t *)set)[i] == ~(priv_chunk_t)0);
920 }
921 
922 boolean_t
923 priv_isfullset(const priv_set_t *set)
924 {
925 	return (__priv_isfullset(GETPRIVDATA(), set));
926 }
927 
928 /*
929  * Return true if a is a subset of b
930  */
931 boolean_t
932 __priv_issubset(priv_data_t *d, const priv_set_t *a, const priv_set_t *b)
933 {
934 	PRIV_TEST_BODY_D(d, (((priv_chunk_t *)a)[i] | ((priv_chunk_t *)b)[i]) ==
935 		((priv_chunk_t *)b)[i]);
936 }
937 
938 boolean_t
939 priv_issubset(const priv_set_t *a, const priv_set_t *b)
940 {
941 	return (__priv_issubset(GETPRIVDATA(), a, b));
942 }
943 
944 #define	PRIV_CHANGE_BODY(a, op, b) \
945 	int i; \
946 	priv_data_t *d; \
947 \
948 	LOADPRIVDATA(d); \
949 \
950 	for (i = 0; i < d->pd_pinfo->priv_setsize; i++) \
951 		((priv_chunk_t *)a)[i] op \
952 			((priv_chunk_t *)b)[i]
953 
954 /* B = A ^ B */
955 void
956 priv_intersect(const priv_set_t *a, priv_set_t *b)
957 {
958 	/* CSTYLED */
959 	PRIV_CHANGE_BODY(b, &=, a);
960 }
961 
962 /* B = A */
963 void
964 priv_copyset(const priv_set_t *a, priv_set_t *b)
965 {
966 	/* CSTYLED */
967 	PRIV_CHANGE_BODY(b, =, a);
968 }
969 
970 /* B = A v B */
971 void
972 priv_union(const priv_set_t *a, priv_set_t *b)
973 {
974 	/* CSTYLED */
975 	PRIV_CHANGE_BODY(b, |=, a);
976 }
977 
978 /* A = ! A */
979 void
980 priv_inverse(priv_set_t *a)
981 {
982 	PRIV_CHANGE_BODY(a, = ~, a);
983 }
984 
985 /*
986  * Manipulating single privileges.
987  */
988 
989 int
990 priv_addset(priv_set_t *a, const char *p)
991 {
992 	int priv = priv_getbyname(p);
993 
994 	if (priv < 0)
995 		return (-1);
996 
997 	PRIV_ADDSET(a, priv);
998 
999 	return (0);
1000 }
1001 
1002 int
1003 priv_delset(priv_set_t *a, const char *p)
1004 {
1005 	int priv = priv_getbyname(p);
1006 
1007 	if (priv < 0)
1008 		return (-1);
1009 
1010 	PRIV_DELSET(a, priv);
1011 	return (0);
1012 }
1013 
1014 boolean_t
1015 priv_ismember(const priv_set_t *a, const char *p)
1016 {
1017 	int priv = priv_getbyname(p);
1018 
1019 	if (priv < 0)
1020 		return (B_FALSE);
1021 
1022 	return ((boolean_t)PRIV_ISMEMBER(a, priv));
1023 }
1024