xref: /illumos-gate/usr/src/lib/libc/port/gen/privlib.c (revision b6805bf78d2bbbeeaea8909a05623587b42d58b3)
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 (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
24  */
25 
26 #pragma weak _getprivimplinfo	= getprivimplinfo
27 #pragma weak _priv_addset	= priv_addset
28 #pragma weak _priv_allocset	= priv_allocset
29 #pragma weak _priv_copyset	= priv_copyset
30 #pragma weak _priv_delset	= priv_delset
31 #pragma weak _priv_emptyset	= priv_emptyset
32 #pragma weak _priv_basicset	= priv_basicset
33 #pragma weak _priv_fillset	= priv_fillset
34 #pragma weak _priv_freeset	= priv_freeset
35 #pragma weak _priv_getbyname	= priv_getbyname
36 #pragma weak _priv_getbynum	= priv_getbynum
37 #pragma weak _priv_getsetbyname	= priv_getsetbyname
38 #pragma weak _priv_getsetbynum	= priv_getsetbynum
39 #pragma weak _priv_ineffect	= priv_ineffect
40 #pragma weak _priv_intersect	= priv_intersect
41 #pragma weak _priv_inverse	= priv_inverse
42 #pragma weak _priv_isemptyset	= priv_isemptyset
43 #pragma weak _priv_isequalset	= priv_isequalset
44 #pragma weak _priv_isfullset	= priv_isfullset
45 #pragma weak _priv_ismember	= priv_ismember
46 #pragma weak _priv_issubset	= priv_issubset
47 #pragma weak _priv_set		= priv_set
48 #pragma weak _priv_union	= priv_union
49 
50 #include "lint.h"
51 
52 #define	_STRUCTURED_PROC	1
53 
54 #include "priv_private.h"
55 #include "mtlib.h"
56 #include "libc.h"
57 #include <errno.h>
58 #include <stdarg.h>
59 #include <stdlib.h>
60 #include <unistd.h>
61 #include <strings.h>
62 #include <synch.h>
63 #include <alloca.h>
64 #include <atomic.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 (__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 	if (privdata == NULL) {
314 		lmutex_lock(&pd_lock);
315 		if (privdata == NULL) {
316 			priv_data_t *tmp;
317 			priv_impl_info_t *ip;
318 			size_t size = sizeof (priv_impl_info_t) + 2048;
319 			size_t realsize;
320 			priv_impl_info_t *aip = alloca(size);
321 
322 			if (getprivinfo(aip, size) != 0)
323 				goto out;
324 
325 			realsize = PRIV_IMPL_INFO_SIZE(aip);
326 
327 			ip = libc_malloc(realsize);
328 
329 			if (ip == NULL)
330 				goto out;
331 
332 			if (realsize <= size) {
333 				(void) memcpy(ip, aip, realsize);
334 			} else if (getprivinfo(ip, realsize) != 0) {
335 				libc_free(ip);
336 				goto out;
337 			}
338 
339 			if ((tmp = __priv_parse_info(ip)) == NULL) {
340 				libc_free(ip);
341 				goto out;
342 			}
343 
344 			/* Allocate the zoneset just once, here */
345 			tmp->pd_zoneset = __priv_allocset(tmp);
346 			if (tmp->pd_zoneset == NULL)
347 				goto clean;
348 
349 			if (zone_getattr(getzoneid(), ZONE_ATTR_PRIVSET,
350 			    tmp->pd_zoneset, tmp->pd_setsize)
351 			    == tmp->pd_setsize) {
352 				membar_producer();
353 				privdata = tmp;
354 				goto out;
355 			}
356 
357 			priv_freeset(tmp->pd_zoneset);
358 clean:
359 			__priv_free_info(tmp);
360 			libc_free(ip);
361 		}
362 out:
363 		lmutex_unlock(&pd_lock);
364 	}
365 	membar_consumer();
366 	return (privdata);
367 }
368 
369 const priv_impl_info_t *
370 getprivimplinfo(void)
371 {
372 	priv_data_t *d;
373 
374 	LOADPRIVDATA(d);
375 
376 	return (d->pd_pinfo);
377 }
378 
379 static priv_set_t *
380 priv_vlist(va_list ap)
381 {
382 	priv_set_t *pset = priv_allocset();
383 	const char *priv;
384 
385 	if (pset == NULL)
386 		return (NULL);
387 
388 	priv_emptyset(pset);
389 
390 	while ((priv = va_arg(ap, const char *)) != NULL) {
391 		if (priv_addset(pset, priv) < 0) {
392 			priv_freeset(pset);
393 			return (NULL);
394 		}
395 	}
396 	return (pset);
397 }
398 
399 /*
400  * priv_set(op, set, priv_id1, priv_id2, ..., NULL)
401  *
402  * Library routine to enable a user process to set a specific
403  * privilege set appropriately using a single call.  User is
404  * required to terminate the list of privileges with NULL.
405  */
406 int
407 priv_set(priv_op_t op, priv_ptype_t setname, ...)
408 {
409 	va_list ap;
410 	priv_set_t *pset;
411 	int ret;
412 
413 	va_start(ap, setname);
414 
415 	pset = priv_vlist(ap);
416 
417 	va_end(ap);
418 
419 	if (pset == NULL)
420 		return (-1);
421 
422 	/* All sets */
423 	if (setname == NULL) {
424 		priv_data_t *d;
425 		int set;
426 
427 		LOADPRIVDATA(d);
428 
429 		for (set = 0; set < d->pd_nsets; set++)
430 			if ((ret = syscall(SYS_privsys, PRIVSYS_SETPPRIV, op,
431 			    set, (void *)pset, d->pd_setsize)) != 0)
432 				break;
433 	} else {
434 		ret = setppriv(op, setname, pset);
435 	}
436 
437 	priv_freeset(pset);
438 	return (ret);
439 }
440 
441 /*
442  * priv_ineffect(privilege).
443  * tests the existence of a privilege against the effective set.
444  */
445 boolean_t
446 priv_ineffect(const char *priv)
447 {
448 	priv_set_t *curset;
449 	boolean_t res;
450 
451 	curset = priv_allocset();
452 
453 	if (curset == NULL)
454 		return (B_FALSE);
455 
456 	if (getppriv(effective, curset) != 0 ||
457 	    !priv_ismember(curset, priv))
458 		res = B_FALSE;
459 	else
460 		res = B_TRUE;
461 
462 	priv_freeset(curset);
463 
464 	return (res);
465 }
466 
467 /*
468  * The routine __init_daemon_priv() is private to Solaris and is
469  * used by daemons to limit the privileges they can use and
470  * to set the uid they run under.
471  */
472 
473 static const char root_cp[] = "/core.%f.%t";
474 static const char daemon_cp[] = "/var/tmp/core.%f.%t";
475 
476 int
477 __init_daemon_priv(int flags, uid_t uid, gid_t gid, ...)
478 {
479 	priv_set_t *nset;
480 	priv_set_t *perm = NULL;
481 	va_list pa;
482 	priv_data_t *d;
483 	int ret = -1;
484 	char buf[1024];
485 
486 	LOADPRIVDATA(d);
487 
488 	va_start(pa, gid);
489 
490 	nset = priv_vlist(pa);
491 
492 	va_end(pa);
493 
494 	if (nset == NULL)
495 		return (-1);
496 
497 	/* Always add the basic set */
498 	if (d->pd_basicset != NULL)
499 		priv_union(d->pd_basicset, nset);
500 
501 	/*
502 	 * This is not a significant failure: it allows us to start programs
503 	 * with sufficient privileges and with the proper uid.   We don't
504 	 * care enough about the extra groups in that case.
505 	 */
506 	if (flags & PU_RESETGROUPS)
507 		(void) setgroups(0, NULL);
508 
509 	if (gid != (gid_t)-1 && setgid(gid) != 0)
510 		goto end;
511 
512 	perm = priv_allocset();
513 	if (perm == NULL)
514 		goto end;
515 
516 	/* E = P */
517 	(void) getppriv(permitted, perm);
518 	(void) setppriv(PRIV_SET, effective, perm);
519 
520 	/* Now reset suid and euid */
521 	if (uid != (uid_t)-1 && setreuid(uid, uid) != 0)
522 		goto end;
523 
524 	/* Check for the limit privs */
525 	if ((flags & PU_LIMITPRIVS) &&
526 	    setppriv(PRIV_SET, limit, nset) != 0)
527 		goto end;
528 
529 	if (flags & PU_CLEARLIMITSET) {
530 		priv_emptyset(perm);
531 		if (setppriv(PRIV_SET, limit, perm) != 0)
532 			goto end;
533 	}
534 
535 	/* Remove the privileges from all the other sets */
536 	if (setppriv(PRIV_SET, permitted, nset) != 0)
537 		goto end;
538 
539 	if (!(flags & PU_INHERITPRIVS))
540 		priv_emptyset(nset);
541 
542 	ret = setppriv(PRIV_SET, inheritable, nset);
543 end:
544 	priv_freeset(nset);
545 	priv_freeset(perm);
546 
547 	if (core_get_process_path(buf, sizeof (buf), getpid()) == 0 &&
548 	    strcmp(buf, "core") == 0) {
549 
550 		if ((uid == (uid_t)-1 ? geteuid() : uid) == 0) {
551 			(void) core_set_process_path(root_cp, sizeof (root_cp),
552 			    getpid());
553 		} else {
554 			(void) core_set_process_path(daemon_cp,
555 			    sizeof (daemon_cp), getpid());
556 		}
557 	}
558 	(void) setpflags(__PROC_PROTECT, 0);
559 
560 	return (ret);
561 }
562 
563 /*
564  * The routine __fini_daemon_priv() is private to Solaris and is
565  * used by daemons to clear remaining unwanted privileges and
566  * reenable core dumps.
567  */
568 void
569 __fini_daemon_priv(const char *priv, ...)
570 {
571 	priv_set_t *nset;
572 	va_list pa;
573 
574 	va_start(pa, priv);
575 
576 	if (priv != NULL) {
577 		nset = priv_vlist(pa);
578 		if (nset == NULL)
579 			return;
580 
581 		(void) priv_addset(nset, priv);
582 		(void) setppriv(PRIV_OFF, permitted, nset);
583 		priv_freeset(nset);
584 	}
585 
586 	va_end(pa);
587 
588 	(void) setpflags(__PROC_PROTECT, 0);
589 }
590 
591 /*
592  * The routine __init_suid_priv() is private to Solaris and is
593  * used by set-uid root programs to limit the privileges acquired
594  * to those actually needed.
595  */
596 
597 static priv_set_t *bracketpriv;
598 
599 int
600 __init_suid_priv(int flags, ...)
601 {
602 	priv_set_t *nset = NULL;
603 	priv_set_t *tmpset = NULL;
604 	va_list pa;
605 	int r = -1;
606 	uid_t ruid, euid;
607 
608 	euid = geteuid();
609 
610 	/* If we're not set-uid root, don't reset the uid */
611 	if (euid == 0) {
612 		ruid = getuid();
613 		/* If we're running as root, keep everything */
614 		if (ruid == 0)
615 			return (0);
616 	}
617 
618 	/* Can call this only once */
619 	if (bracketpriv != NULL)
620 		return (-1);
621 
622 	va_start(pa, flags);
623 
624 	nset = priv_vlist(pa);
625 
626 	va_end(pa);
627 
628 	if (nset == NULL)
629 		goto end;
630 
631 	tmpset = priv_allocset();
632 
633 	if (tmpset == NULL)
634 		goto end;
635 
636 	/* We cannot grow our privileges beyond P, so start there */
637 	(void) getppriv(permitted, tmpset);
638 
639 	/* Is the privilege we need even in P? */
640 	if (!priv_issubset(nset, tmpset))
641 		goto end;
642 
643 	bracketpriv = priv_allocset();
644 	if (bracketpriv == NULL)
645 		goto end;
646 
647 	priv_copyset(nset, bracketpriv);
648 
649 	/* Always add the basic set */
650 	priv_union(priv_basic(), nset);
651 
652 	/* But don't add what we don't have */
653 	priv_intersect(tmpset, nset);
654 
655 	(void) getppriv(inheritable, tmpset);
656 
657 	/* And stir in the inheritable privileges */
658 	priv_union(tmpset, nset);
659 
660 	if ((r = setppriv(PRIV_SET, effective, tmpset)) != 0)
661 		goto end;
662 
663 	if ((r = setppriv(PRIV_SET, permitted, nset)) != 0)
664 		goto end;
665 
666 	if (flags & PU_CLEARLIMITSET)
667 		priv_emptyset(nset);
668 
669 	if ((flags & (PU_LIMITPRIVS|PU_CLEARLIMITSET)) != 0 &&
670 	    (r = setppriv(PRIV_SET, limit, nset)) != 0)
671 		goto end;
672 
673 	if (euid == 0)
674 		r = setreuid(ruid, ruid);
675 
676 end:
677 	priv_freeset(tmpset);
678 	priv_freeset(nset);
679 	if (r != 0) {
680 		/* Fail without leaving uid 0 around */
681 		if (euid == 0)
682 			(void) setreuid(ruid, ruid);
683 		priv_freeset(bracketpriv);
684 		bracketpriv = NULL;
685 	}
686 
687 	return (r);
688 }
689 
690 /*
691  * Toggle privileges on/off in the effective set.
692  */
693 int
694 __priv_bracket(priv_op_t op)
695 {
696 	/* We're running fully privileged or didn't check errors first time */
697 	if (bracketpriv == NULL)
698 		return (0);
699 
700 	/* Only PRIV_ON and PRIV_OFF are valid */
701 	if (op == PRIV_SET)
702 		return (-1);
703 
704 	return (setppriv(op, effective, bracketpriv));
705 }
706 
707 /*
708  * Remove privileges from E & P.
709  */
710 void
711 __priv_relinquish(void)
712 {
713 	if (bracketpriv != NULL) {
714 		(void) setppriv(PRIV_OFF, permitted, bracketpriv);
715 		priv_freeset(bracketpriv);
716 		bracketpriv = NULL;
717 	}
718 }
719 
720 /*
721  * Use binary search on the ordered list.
722  */
723 int
724 __priv_getbyname(const priv_data_t *d, const char *name)
725 {
726 	char *const *list;
727 	const int *order;
728 	int lo = 0;
729 	int hi;
730 
731 	if (d == NULL)
732 		return (-1);
733 
734 	list = d->pd_privnames;
735 	order = d->pd_setsort;
736 	hi = d->pd_nprivs - 1;
737 
738 	if (strncasecmp(name, "priv_", 5) == 0)
739 		name += 5;
740 
741 	do {
742 		int mid = (lo + hi) / 2;
743 		int res = strcasecmp(name, list[order[mid]]);
744 
745 		if (res == 0)
746 			return (order[mid]);
747 		else if (res < 0)
748 			hi = mid - 1;
749 		else
750 			lo = mid + 1;
751 	} while (lo <= hi);
752 
753 	errno = EINVAL;
754 	return (-1);
755 }
756 
757 int
758 priv_getbyname(const char *name)
759 {
760 	WITHPRIVLOCKED(int, -1, __priv_getbyname(GETPRIVDATA(), name))
761 }
762 
763 int
764 __priv_getsetbyname(const priv_data_t *d, const char *name)
765 {
766 	int i;
767 	int n = d->pd_nsets;
768 	char *const *list = d->pd_setnames;
769 
770 	if (strncasecmp(name, "priv_", 5) == 0)
771 		name += 5;
772 
773 	for (i = 0; i < n; i++) {
774 		if (strcasecmp(list[i], name) == 0)
775 			return (i);
776 	}
777 
778 	errno = EINVAL;
779 	return (-1);
780 }
781 
782 int
783 priv_getsetbyname(const char *name)
784 {
785 	/* Not locked: sets don't change */
786 	return (__priv_getsetbyname(GETPRIVDATA(), name));
787 }
788 
789 static const char *
790 priv_bynum(int i, int n, char **list)
791 {
792 	if (i < 0 || i >= n)
793 		return (NULL);
794 
795 	return (list[i]);
796 }
797 
798 const char *
799 __priv_getbynum(const priv_data_t *d, int num)
800 {
801 	if (d == NULL)
802 		return (NULL);
803 	return (priv_bynum(num, d->pd_nprivs, d->pd_privnames));
804 }
805 
806 const char *
807 priv_getbynum(int num)
808 {
809 	WITHPRIVLOCKED(const char *, NULL, __priv_getbynum(GETPRIVDATA(), num))
810 }
811 
812 const char *
813 __priv_getsetbynum(const priv_data_t *d, int num)
814 {
815 	if (d == NULL)
816 		return (NULL);
817 	return (priv_bynum(num, d->pd_nsets, d->pd_setnames));
818 }
819 
820 const char *
821 priv_getsetbynum(int num)
822 {
823 	return (__priv_getsetbynum(GETPRIVDATA(), num));
824 }
825 
826 
827 /*
828  * Privilege manipulation functions
829  *
830  * Without knowing the details of the privilege set implementation,
831  * opaque pointers can be used to manipulate sets at will.
832  */
833 
834 static priv_set_t *
835 __priv_allocset(priv_data_t *d)
836 {
837 	if (d == NULL)
838 		return (NULL);
839 
840 	return (libc_malloc(d->pd_setsize));
841 }
842 
843 priv_set_t *
844 priv_allocset(void)
845 {
846 	return (__priv_allocset(GETPRIVDATA()));
847 }
848 
849 void
850 priv_freeset(priv_set_t *p)
851 {
852 	int er = errno;
853 
854 	libc_free(p);
855 	errno = er;
856 }
857 
858 void
859 __priv_emptyset(priv_data_t *d, priv_set_t *set)
860 {
861 	(void) memset(set, 0, d->pd_setsize);
862 }
863 
864 void
865 priv_emptyset(priv_set_t *set)
866 {
867 	__priv_emptyset(GETPRIVDATA(), set);
868 }
869 
870 void
871 priv_basicset(priv_set_t *set)
872 {
873 	priv_copyset(priv_basic(), set);
874 }
875 
876 void
877 __priv_fillset(priv_data_t *d, priv_set_t *set)
878 {
879 	(void) memset(set, ~0, d->pd_setsize);
880 }
881 
882 void
883 priv_fillset(priv_set_t *set)
884 {
885 	__priv_fillset(GETPRIVDATA(), set);
886 }
887 
888 
889 #define	PRIV_TEST_BODY_D(d, test) \
890 	int i; \
891 \
892 	for (i = d->pd_pinfo->priv_setsize; i-- > 0; ) \
893 		if (!(test)) \
894 			return (B_FALSE); \
895 \
896 	return (B_TRUE)
897 
898 boolean_t
899 priv_isequalset(const priv_set_t *a, const priv_set_t *b)
900 {
901 	priv_data_t *d;
902 
903 	LOADPRIVDATA(d);
904 
905 	return ((boolean_t)(memcmp(a, b, d->pd_setsize) == 0));
906 }
907 
908 boolean_t
909 __priv_isemptyset(priv_data_t *d, const priv_set_t *set)
910 {
911 	PRIV_TEST_BODY_D(d, ((priv_chunk_t *)set)[i] == 0);
912 }
913 
914 boolean_t
915 priv_isemptyset(const priv_set_t *set)
916 {
917 	return (__priv_isemptyset(GETPRIVDATA(), set));
918 }
919 
920 boolean_t
921 __priv_isfullset(priv_data_t *d, const priv_set_t *set)
922 {
923 	PRIV_TEST_BODY_D(d, ((priv_chunk_t *)set)[i] == ~(priv_chunk_t)0);
924 }
925 
926 boolean_t
927 priv_isfullset(const priv_set_t *set)
928 {
929 	return (__priv_isfullset(GETPRIVDATA(), set));
930 }
931 
932 /*
933  * Return true if a is a subset of b
934  */
935 boolean_t
936 __priv_issubset(priv_data_t *d, const priv_set_t *a, const priv_set_t *b)
937 {
938 	PRIV_TEST_BODY_D(d, (((priv_chunk_t *)a)[i] | ((priv_chunk_t *)b)[i]) ==
939 	    ((priv_chunk_t *)b)[i]);
940 }
941 
942 boolean_t
943 priv_issubset(const priv_set_t *a, const priv_set_t *b)
944 {
945 	return (__priv_issubset(GETPRIVDATA(), a, b));
946 }
947 
948 #define	PRIV_CHANGE_BODY(a, op, b) \
949 	int i; \
950 	priv_data_t *d; \
951 \
952 	LOADPRIVDATA(d); \
953 \
954 	for (i = 0; i < d->pd_pinfo->priv_setsize; i++) \
955 		((priv_chunk_t *)a)[i] op \
956 			((priv_chunk_t *)b)[i]
957 
958 /* B = A ^ B */
959 void
960 priv_intersect(const priv_set_t *a, priv_set_t *b)
961 {
962 	/* CSTYLED */
963 	PRIV_CHANGE_BODY(b, &=, a);
964 }
965 
966 /* B = A */
967 void
968 priv_copyset(const priv_set_t *a, priv_set_t *b)
969 {
970 	/* CSTYLED */
971 	PRIV_CHANGE_BODY(b, =, a);
972 }
973 
974 /* B = A v B */
975 void
976 priv_union(const priv_set_t *a, priv_set_t *b)
977 {
978 	/* CSTYLED */
979 	PRIV_CHANGE_BODY(b, |=, a);
980 }
981 
982 /* A = ! A */
983 void
984 priv_inverse(priv_set_t *a)
985 {
986 	PRIV_CHANGE_BODY(a, = ~, a);
987 }
988 
989 /*
990  * Manipulating single privileges.
991  */
992 
993 int
994 priv_addset(priv_set_t *a, const char *p)
995 {
996 	int priv = priv_getbyname(p);
997 
998 	if (priv < 0)
999 		return (-1);
1000 
1001 	PRIV_ADDSET(a, priv);
1002 
1003 	return (0);
1004 }
1005 
1006 int
1007 priv_delset(priv_set_t *a, const char *p)
1008 {
1009 	int priv = priv_getbyname(p);
1010 
1011 	if (priv < 0)
1012 		return (-1);
1013 
1014 	PRIV_DELSET(a, priv);
1015 	return (0);
1016 }
1017 
1018 boolean_t
1019 priv_ismember(const priv_set_t *a, const char *p)
1020 {
1021 	int priv = priv_getbyname(p);
1022 
1023 	if (priv < 0)
1024 		return (B_FALSE);
1025 
1026 	return ((boolean_t)PRIV_ISMEMBER(a, priv));
1027 }
1028