privlib.c revision 4321:a8930ec16e52
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 */
75static const char *permitted	= PRIV_PERMITTED;
76static const char *effective	= PRIV_EFFECTIVE;
77static const char *limit	= PRIV_LIMIT;
78static 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
97priv_data_t *privdata;
98
99static mutex_t pd_lock = DEFAULTMUTEX;
100
101static int
102parseninfo(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
124struct strint {
125	char *name;
126	int rank;
127};
128
129static int
130strintcmp(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
138priv_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);
211out:
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 */
223void
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 */
235int
236lock_data(void)
237{
238	if (__priv_getdata() == NULL)
239		return (-1);
240
241	lmutex_lock(&pd_lock);
242	return (0);
243}
244
245boolean_t
246refresh_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
304void
305unlock_data(void)
306{
307	lmutex_unlock(&pd_lock);
308}
309
310static priv_set_t *__priv_allocset(priv_data_t *);
311
312priv_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);
360clean:
361			__priv_free_info(tmp);
362			libc_free(ip);
363		}
364out:
365		lmutex_unlock(&pd_lock);
366	}
367	membar_consumer();
368	return (privdata);
369}
370
371const 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
381static priv_set_t *
382priv_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 */
408int
409priv_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 existance of a privilege against the effective set.
446 */
447boolean_t
448priv_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
475static const char root_cp[] = "/core.%f.%t";
476static const char daemon_cp[] = "/var/tmp/core.%f.%t";
477
478int
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);
545end:
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 */
570void
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
599static priv_set_t *bracketpriv;
600
601int
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
678end:
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 */
695int
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 */
712void
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 */
725int
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
759int
760priv_getbyname(const char *name)
761{
762	WITHPRIVLOCKED(int, -1, __priv_getbyname(GETPRIVDATA(), name));
763}
764
765int
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
784int
785priv_getsetbyname(const char *name)
786{
787	/* Not locked: sets don't change */
788	return (__priv_getsetbyname(GETPRIVDATA(), name));
789}
790
791static const char *
792priv_bynum(int i, int n, char **list)
793{
794	if (i < 0 || i >= n)
795		return (NULL);
796
797	return (list[i]);
798}
799
800const 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
808const char *
809priv_getbynum(int num)
810{
811	WITHPRIVLOCKED(const char *, NULL, __priv_getbynum(GETPRIVDATA(), num));
812}
813
814const 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
822const char *
823priv_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
836static 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
845priv_set_t *
846priv_allocset(void)
847{
848	return (__priv_allocset(GETPRIVDATA()));
849}
850
851void
852priv_freeset(priv_set_t *p)
853{
854	int er = errno;
855
856	libc_free(p);
857	errno = er;
858}
859
860void
861__priv_emptyset(priv_data_t *d, priv_set_t *set)
862{
863	(void) memset(set, 0, d->pd_setsize);
864}
865
866void
867priv_emptyset(priv_set_t *set)
868{
869	__priv_emptyset(GETPRIVDATA(), set);
870}
871
872void
873__priv_fillset(priv_data_t *d, priv_set_t *set)
874{
875	(void) memset(set, ~0, d->pd_setsize);
876}
877
878void
879priv_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
894boolean_t
895priv_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
904boolean_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
910boolean_t
911priv_isemptyset(const priv_set_t *set)
912{
913	return (__priv_isemptyset(GETPRIVDATA(), set));
914}
915
916boolean_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
922boolean_t
923priv_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 */
931boolean_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
938boolean_t
939priv_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 */
955void
956priv_intersect(const priv_set_t *a, priv_set_t *b)
957{
958	/* CSTYLED */
959	PRIV_CHANGE_BODY(b, &=, a);
960}
961
962/* B = A */
963void
964priv_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 */
971void
972priv_union(const priv_set_t *a, priv_set_t *b)
973{
974	/* CSTYLED */
975	PRIV_CHANGE_BODY(b, |=, a);
976}
977
978/* A = ! A */
979void
980priv_inverse(priv_set_t *a)
981{
982	PRIV_CHANGE_BODY(a, = ~, a);
983}
984
985/*
986 * Manipulating single privileges.
987 */
988
989int
990priv_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
1002int
1003priv_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
1014boolean_t
1015priv_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