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