10Sstevel@tonic-gate /*
20Sstevel@tonic-gate * CDDL HEADER START
30Sstevel@tonic-gate *
40Sstevel@tonic-gate * The contents of this file are subject to the terms of the
59539SCasper.Dik@Sun.COM * Common Development and Distribution License (the "License").
69539SCasper.Dik@Sun.COM * You may not use this file except in compliance with the License.
70Sstevel@tonic-gate *
80Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
90Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing.
100Sstevel@tonic-gate * See the License for the specific language governing permissions
110Sstevel@tonic-gate * and limitations under the License.
120Sstevel@tonic-gate *
130Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each
140Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
150Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the
160Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying
170Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner]
180Sstevel@tonic-gate *
190Sstevel@tonic-gate * CDDL HEADER END
200Sstevel@tonic-gate */
210Sstevel@tonic-gate /*
2211537SCasper.Dik@Sun.COM * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
230Sstevel@tonic-gate * Use is subject to license terms.
240Sstevel@tonic-gate */
250Sstevel@tonic-gate
260Sstevel@tonic-gate /*
270Sstevel@tonic-gate * Privilege implementation.
280Sstevel@tonic-gate *
290Sstevel@tonic-gate * This file provides the infrastructure for privilege sets and limits
300Sstevel@tonic-gate * the number of files that requires to include <sys/cred_impl.h> and/or
310Sstevel@tonic-gate * <sys/priv_impl.h>.
320Sstevel@tonic-gate *
330Sstevel@tonic-gate * The Solaris privilege mechanism has been designed in a
340Sstevel@tonic-gate * future proof manner. While the kernel may use fixed size arrays
350Sstevel@tonic-gate * and fixed bitmasks and bit values, the representation of those
360Sstevel@tonic-gate * is kernel private. All external interfaces as well as K-to-K interfaces
370Sstevel@tonic-gate * have been constructed in a manner to provide the maximum flexibility.
380Sstevel@tonic-gate *
390Sstevel@tonic-gate * There can be X privilege sets each containing Y 32 bit words.
400Sstevel@tonic-gate * <X, Y> are constant for a kernel invocation.
410Sstevel@tonic-gate *
420Sstevel@tonic-gate * As a consequence, all privilege set manipulation happens in functions
430Sstevel@tonic-gate * below.
440Sstevel@tonic-gate *
450Sstevel@tonic-gate */
460Sstevel@tonic-gate
470Sstevel@tonic-gate #include <sys/systm.h>
480Sstevel@tonic-gate #include <sys/ddi.h>
490Sstevel@tonic-gate #include <sys/kmem.h>
500Sstevel@tonic-gate #include <sys/sunddi.h>
510Sstevel@tonic-gate #include <sys/errno.h>
520Sstevel@tonic-gate #include <sys/debug.h>
530Sstevel@tonic-gate #include <sys/priv_impl.h>
540Sstevel@tonic-gate #include <sys/procfs.h>
550Sstevel@tonic-gate #include <sys/policy.h>
560Sstevel@tonic-gate #include <sys/cred_impl.h>
570Sstevel@tonic-gate #include <sys/devpolicy.h>
580Sstevel@tonic-gate #include <sys/atomic.h>
590Sstevel@tonic-gate
600Sstevel@tonic-gate /*
610Sstevel@tonic-gate * Privilege name to number mapping table consists in the generated
620Sstevel@tonic-gate * priv_const.c file. This lock protects against updates of the privilege
630Sstevel@tonic-gate * names and counts; all other priv_info fields are read-only.
640Sstevel@tonic-gate * The actual protected values are:
650Sstevel@tonic-gate * global variable nprivs
660Sstevel@tonic-gate * the priv_max field
670Sstevel@tonic-gate * the priv_names field
680Sstevel@tonic-gate * the priv names info item (cnt/strings)
690Sstevel@tonic-gate */
700Sstevel@tonic-gate krwlock_t privinfo_lock;
710Sstevel@tonic-gate
720Sstevel@tonic-gate static boolean_t priv_valid(const cred_t *);
730Sstevel@tonic-gate
740Sstevel@tonic-gate priv_set_t priv_fullset; /* set of all privileges */
750Sstevel@tonic-gate priv_set_t priv_unsafe; /* unsafe to exec set-uid root if these are not in L */
760Sstevel@tonic-gate
770Sstevel@tonic-gate /*
780Sstevel@tonic-gate * Privilege initialization functions.
790Sstevel@tonic-gate * Called from common/os/cred.c when cred_init is called.
800Sstevel@tonic-gate */
810Sstevel@tonic-gate
820Sstevel@tonic-gate void
priv_init(void)830Sstevel@tonic-gate priv_init(void)
840Sstevel@tonic-gate {
85*11569SCasper.Dik@Sun.COM #ifdef DEBUG
86*11569SCasper.Dik@Sun.COM int alloc_test_priv = 1;
87*11569SCasper.Dik@Sun.COM #else
88*11569SCasper.Dik@Sun.COM int alloc_test_priv = priv_debug;
89*11569SCasper.Dik@Sun.COM #endif
900Sstevel@tonic-gate rw_init(&privinfo_lock, NULL, RW_DRIVER, NULL);
910Sstevel@tonic-gate
920Sstevel@tonic-gate PRIV_BASIC_ASSERT(priv_basic);
930Sstevel@tonic-gate PRIV_UNSAFE_ASSERT(&priv_unsafe);
940Sstevel@tonic-gate priv_fillset(&priv_fullset);
950Sstevel@tonic-gate
9611537SCasper.Dik@Sun.COM /*
97*11569SCasper.Dik@Sun.COM * When booting with priv_debug set or in a DEBUG kernel, then we'll
98*11569SCasper.Dik@Sun.COM * add an additional basic privilege and we verify that it is always
99*11569SCasper.Dik@Sun.COM * present in E.
10011537SCasper.Dik@Sun.COM */
101*11569SCasper.Dik@Sun.COM if (alloc_test_priv != 0 &&
10211537SCasper.Dik@Sun.COM (priv_basic_test = priv_getbyname("basic_test", PRIV_ALLOC)) >= 0) {
10311537SCasper.Dik@Sun.COM priv_addset(priv_basic, priv_basic_test);
10411537SCasper.Dik@Sun.COM }
10511537SCasper.Dik@Sun.COM
1060Sstevel@tonic-gate devpolicy_init();
1070Sstevel@tonic-gate }
1080Sstevel@tonic-gate
1090Sstevel@tonic-gate /* Utility functions: privilege sets as opaque data types */
1100Sstevel@tonic-gate
1110Sstevel@tonic-gate /*
1120Sstevel@tonic-gate * Guts of prgetprivsize.
1130Sstevel@tonic-gate */
1140Sstevel@tonic-gate int
priv_prgetprivsize(prpriv_t * tmpl)1150Sstevel@tonic-gate priv_prgetprivsize(prpriv_t *tmpl)
1160Sstevel@tonic-gate {
1170Sstevel@tonic-gate return (sizeof (prpriv_t) +
1189539SCasper.Dik@Sun.COM PRIV_SETBYTES - sizeof (priv_chunk_t) +
1199539SCasper.Dik@Sun.COM (tmpl ? tmpl->pr_infosize : priv_info->priv_infosize));
1200Sstevel@tonic-gate }
1210Sstevel@tonic-gate
1220Sstevel@tonic-gate /*
1230Sstevel@tonic-gate * Guts of prgetpriv.
1240Sstevel@tonic-gate */
1250Sstevel@tonic-gate void
cred2prpriv(const cred_t * cp,prpriv_t * pr)1260Sstevel@tonic-gate cred2prpriv(const cred_t *cp, prpriv_t *pr)
1270Sstevel@tonic-gate {
1280Sstevel@tonic-gate priv_set_t *psa;
1290Sstevel@tonic-gate int i;
1300Sstevel@tonic-gate
1310Sstevel@tonic-gate pr->pr_nsets = PRIV_NSET;
1320Sstevel@tonic-gate pr->pr_setsize = PRIV_SETSIZE;
1330Sstevel@tonic-gate pr->pr_infosize = priv_info->priv_infosize;
1340Sstevel@tonic-gate
1350Sstevel@tonic-gate psa = (priv_set_t *)pr->pr_sets;
1360Sstevel@tonic-gate
1370Sstevel@tonic-gate for (i = 0; i < PRIV_NSET; i++)
1380Sstevel@tonic-gate psa[i] = *priv_getset(cp, i);
1390Sstevel@tonic-gate
1400Sstevel@tonic-gate priv_getinfo(cp, (char *)pr + PRIV_PRPRIV_INFO_OFFSET(pr));
1410Sstevel@tonic-gate }
1420Sstevel@tonic-gate
1430Sstevel@tonic-gate /*
1440Sstevel@tonic-gate * Guts of pr_spriv:
1450Sstevel@tonic-gate *
1460Sstevel@tonic-gate * Set the privileges of a process.
1470Sstevel@tonic-gate *
1480Sstevel@tonic-gate * In order to set the privileges, the setting process will need to
1490Sstevel@tonic-gate * have those privileges in its effective set in order to prevent
1500Sstevel@tonic-gate * specially privileged processes to easily gain additional privileges.
1510Sstevel@tonic-gate * Pre-existing privileges can be retained. To change any privileges,
1520Sstevel@tonic-gate * PRIV_PROC_OWNER needs to be asserted.
1530Sstevel@tonic-gate *
1540Sstevel@tonic-gate * In formula:
1550Sstevel@tonic-gate *
1560Sstevel@tonic-gate * S' <= S || S' <= S + Ea
1570Sstevel@tonic-gate *
1580Sstevel@tonic-gate * the new set must either be subset of the old set or a subset of
1590Sstevel@tonic-gate * the oldset merged with the effective set of the acting process; or just:
1600Sstevel@tonic-gate *
1610Sstevel@tonic-gate * S' <= S + Ea
1620Sstevel@tonic-gate *
1630Sstevel@tonic-gate * It's not legal to grow the limit set this way.
1640Sstevel@tonic-gate *
1650Sstevel@tonic-gate */
1660Sstevel@tonic-gate int
priv_pr_spriv(proc_t * p,prpriv_t * prpriv,const cred_t * cr)1670Sstevel@tonic-gate priv_pr_spriv(proc_t *p, prpriv_t *prpriv, const cred_t *cr)
1680Sstevel@tonic-gate {
1690Sstevel@tonic-gate cred_t *oldcred;
1700Sstevel@tonic-gate cred_t *newcred;
1710Sstevel@tonic-gate int i;
1720Sstevel@tonic-gate int err = EPERM;
1730Sstevel@tonic-gate cred_priv_t *cp, *ocp;
1740Sstevel@tonic-gate priv_set_t eset;
1750Sstevel@tonic-gate
1760Sstevel@tonic-gate ASSERT(MUTEX_HELD(&p->p_lock));
1770Sstevel@tonic-gate
1780Sstevel@tonic-gate /*
1790Sstevel@tonic-gate * Set must have proper dimension; infosize must be absent
1800Sstevel@tonic-gate * or properly sized.
1810Sstevel@tonic-gate */
1820Sstevel@tonic-gate if (prpriv->pr_nsets != PRIV_NSET ||
1830Sstevel@tonic-gate prpriv->pr_setsize != PRIV_SETSIZE ||
1840Sstevel@tonic-gate (prpriv->pr_infosize & (sizeof (uint32_t) - 1)) != 0 ||
1850Sstevel@tonic-gate prpriv->pr_infosize > priv_info->priv_infosize ||
1860Sstevel@tonic-gate prpriv->pr_infosize < 0)
1879539SCasper.Dik@Sun.COM return (EINVAL);
1880Sstevel@tonic-gate
1890Sstevel@tonic-gate mutex_exit(&p->p_lock);
1900Sstevel@tonic-gate
1910Sstevel@tonic-gate if (priv_proc_cred_perm(cr, p, &oldcred, VWRITE) != 0) {
1920Sstevel@tonic-gate mutex_enter(&p->p_lock);
1930Sstevel@tonic-gate return (EPERM);
1940Sstevel@tonic-gate }
1950Sstevel@tonic-gate
1960Sstevel@tonic-gate newcred = crdup(oldcred);
1970Sstevel@tonic-gate
1980Sstevel@tonic-gate /* Copy the privilege sets from prpriv to newcred */
1990Sstevel@tonic-gate bcopy(prpriv->pr_sets, CR_PRIVSETS(newcred), PRIV_SETBYTES);
2000Sstevel@tonic-gate
2010Sstevel@tonic-gate cp = &newcred->cr_priv;
2020Sstevel@tonic-gate ocp = &oldcred->cr_priv;
2030Sstevel@tonic-gate eset = CR_OEPRIV(cr);
2040Sstevel@tonic-gate
2050Sstevel@tonic-gate priv_intersect(&CR_LPRIV(oldcred), &eset);
2060Sstevel@tonic-gate
2070Sstevel@tonic-gate /*
2080Sstevel@tonic-gate * Verify the constraints laid out:
2090Sstevel@tonic-gate * for the limit set, we require that the new set is a subset
2100Sstevel@tonic-gate * of the old limit set.
2110Sstevel@tonic-gate * for all other sets, we require that the new set is either a
2120Sstevel@tonic-gate * subset of the old set or a subset of the intersection of
2130Sstevel@tonic-gate * the old limit set and the effective set of the acting process.
2140Sstevel@tonic-gate */
2150Sstevel@tonic-gate for (i = 0; i < PRIV_NSET; i++)
2160Sstevel@tonic-gate if (!priv_issubset(&cp->crprivs[i], &ocp->crprivs[i]) &&
2170Sstevel@tonic-gate (i == PRIV_LIMIT || !priv_issubset(&cp->crprivs[i], &eset)))
2180Sstevel@tonic-gate break;
2190Sstevel@tonic-gate
2200Sstevel@tonic-gate crfree(oldcred);
2210Sstevel@tonic-gate
2220Sstevel@tonic-gate if (i < PRIV_NSET || !priv_valid(newcred))
2230Sstevel@tonic-gate goto err;
2240Sstevel@tonic-gate
2250Sstevel@tonic-gate /* Load the settable privilege information */
2260Sstevel@tonic-gate if (prpriv->pr_infosize > 0) {
2270Sstevel@tonic-gate char *x = (char *)prpriv + PRIV_PRPRIV_INFO_OFFSET(prpriv);
2280Sstevel@tonic-gate char *lastx = x + prpriv->pr_infosize;
2290Sstevel@tonic-gate
2300Sstevel@tonic-gate while (x < lastx) {
2310Sstevel@tonic-gate priv_info_t *pi = (priv_info_t *)x;
2320Sstevel@tonic-gate priv_info_uint_t *pii;
2330Sstevel@tonic-gate
2340Sstevel@tonic-gate switch (pi->priv_info_type) {
2350Sstevel@tonic-gate case PRIV_INFO_FLAGS:
2360Sstevel@tonic-gate pii = (priv_info_uint_t *)x;
2370Sstevel@tonic-gate if (pii->info.priv_info_size != sizeof (*pii)) {
2380Sstevel@tonic-gate err = EINVAL;
2390Sstevel@tonic-gate goto err;
2400Sstevel@tonic-gate }
2410Sstevel@tonic-gate CR_FLAGS(newcred) &= ~PRIV_USER;
2420Sstevel@tonic-gate CR_FLAGS(newcred) |= (pii->val & PRIV_USER);
2430Sstevel@tonic-gate break;
2440Sstevel@tonic-gate default:
2450Sstevel@tonic-gate err = EINVAL;
2460Sstevel@tonic-gate goto err;
2470Sstevel@tonic-gate }
2480Sstevel@tonic-gate /* Guarantee alignment and forward progress */
2490Sstevel@tonic-gate if ((pi->priv_info_size & (sizeof (uint32_t) - 1)) ||
2500Sstevel@tonic-gate pi->priv_info_size < sizeof (*pi) ||
2510Sstevel@tonic-gate lastx - x > pi->priv_info_size) {
2520Sstevel@tonic-gate err = EINVAL;
2530Sstevel@tonic-gate goto err;
2540Sstevel@tonic-gate }
2550Sstevel@tonic-gate
2560Sstevel@tonic-gate x += pi->priv_info_size;
2570Sstevel@tonic-gate }
2580Sstevel@tonic-gate }
2590Sstevel@tonic-gate
2600Sstevel@tonic-gate /*
2610Sstevel@tonic-gate * We'll try to copy the privilege aware flag; but since the
2620Sstevel@tonic-gate * privileges sets are all individually set, they are set
2630Sstevel@tonic-gate * as if we're privilege aware. If PRIV_AWARE wasn't set
2640Sstevel@tonic-gate * or was explicitely unset, we need to set the flag and then
2650Sstevel@tonic-gate * try to get rid of it.
2660Sstevel@tonic-gate */
2670Sstevel@tonic-gate if ((CR_FLAGS(newcred) & PRIV_AWARE) == 0) {
2680Sstevel@tonic-gate CR_FLAGS(newcred) |= PRIV_AWARE;
2690Sstevel@tonic-gate priv_adjust_PA(newcred);
2700Sstevel@tonic-gate }
2710Sstevel@tonic-gate
2720Sstevel@tonic-gate mutex_enter(&p->p_crlock);
2730Sstevel@tonic-gate oldcred = p->p_cred;
2740Sstevel@tonic-gate p->p_cred = newcred;
2750Sstevel@tonic-gate mutex_exit(&p->p_crlock);
2760Sstevel@tonic-gate crfree(oldcred);
2770Sstevel@tonic-gate
2780Sstevel@tonic-gate mutex_enter(&p->p_lock);
2790Sstevel@tonic-gate return (0);
2800Sstevel@tonic-gate
2810Sstevel@tonic-gate err:
2820Sstevel@tonic-gate crfree(newcred);
2830Sstevel@tonic-gate mutex_enter(&p->p_lock);
2840Sstevel@tonic-gate return (err);
2850Sstevel@tonic-gate }
2860Sstevel@tonic-gate
2870Sstevel@tonic-gate priv_impl_info_t
priv_hold_implinfo(void)2880Sstevel@tonic-gate *priv_hold_implinfo(void)
2890Sstevel@tonic-gate {
2900Sstevel@tonic-gate rw_enter(&privinfo_lock, RW_READER);
2910Sstevel@tonic-gate return (priv_info);
2920Sstevel@tonic-gate }
2930Sstevel@tonic-gate
2940Sstevel@tonic-gate void
priv_release_implinfo(void)2950Sstevel@tonic-gate priv_release_implinfo(void)
2960Sstevel@tonic-gate {
2970Sstevel@tonic-gate rw_exit(&privinfo_lock);
2980Sstevel@tonic-gate }
2990Sstevel@tonic-gate
3000Sstevel@tonic-gate size_t
priv_get_implinfo_size(void)3010Sstevel@tonic-gate priv_get_implinfo_size(void)
3020Sstevel@tonic-gate {
3030Sstevel@tonic-gate return (privinfosize);
3040Sstevel@tonic-gate }
3050Sstevel@tonic-gate
3060Sstevel@tonic-gate
3070Sstevel@tonic-gate /*
3080Sstevel@tonic-gate * Return the nth privilege set
3090Sstevel@tonic-gate */
3100Sstevel@tonic-gate const priv_set_t *
priv_getset(const cred_t * cr,int set)3110Sstevel@tonic-gate priv_getset(const cred_t *cr, int set)
3120Sstevel@tonic-gate {
3130Sstevel@tonic-gate ASSERT(PRIV_VALIDSET(set));
3140Sstevel@tonic-gate
3150Sstevel@tonic-gate if ((CR_FLAGS(cr) & PRIV_AWARE) == 0)
3160Sstevel@tonic-gate switch (set) {
3170Sstevel@tonic-gate case PRIV_EFFECTIVE:
3180Sstevel@tonic-gate return (&CR_OEPRIV(cr));
3190Sstevel@tonic-gate case PRIV_PERMITTED:
3200Sstevel@tonic-gate return (&CR_OPPRIV(cr));
3210Sstevel@tonic-gate }
3220Sstevel@tonic-gate return (&CR_PRIVS(cr)->crprivs[set]);
3230Sstevel@tonic-gate }
3240Sstevel@tonic-gate
3250Sstevel@tonic-gate /*
3260Sstevel@tonic-gate * Buf must be allocated by caller and contain sufficient space to
3270Sstevel@tonic-gate * contain all additional info structures using priv_info.priv_infosize.
3280Sstevel@tonic-gate * The buffer must be properly aligned.
3290Sstevel@tonic-gate */
3300Sstevel@tonic-gate /*ARGSUSED*/
3310Sstevel@tonic-gate void
priv_getinfo(const cred_t * cr,void * buf)3320Sstevel@tonic-gate priv_getinfo(const cred_t *cr, void *buf)
3330Sstevel@tonic-gate {
3340Sstevel@tonic-gate struct priv_info_uint *ii;
3350Sstevel@tonic-gate
3360Sstevel@tonic-gate ii = buf;
3370Sstevel@tonic-gate ii->val = CR_FLAGS(cr);
3380Sstevel@tonic-gate ii->info.priv_info_size = (uint32_t)sizeof (*ii);
3390Sstevel@tonic-gate ii->info.priv_info_type = PRIV_INFO_FLAGS;
3400Sstevel@tonic-gate }
3410Sstevel@tonic-gate
3420Sstevel@tonic-gate int
priv_getbyname(const char * name,uint_t flag)3430Sstevel@tonic-gate priv_getbyname(const char *name, uint_t flag)
3440Sstevel@tonic-gate {
3450Sstevel@tonic-gate int i;
3460Sstevel@tonic-gate int wheld = 0;
3470Sstevel@tonic-gate int len;
3480Sstevel@tonic-gate char *p;
3490Sstevel@tonic-gate
3500Sstevel@tonic-gate if (flag != 0 && flag != PRIV_ALLOC)
3510Sstevel@tonic-gate return (-EINVAL);
3520Sstevel@tonic-gate
3530Sstevel@tonic-gate if (strncasecmp(name, "priv_", 5) == 0)
3540Sstevel@tonic-gate name += 5;
3550Sstevel@tonic-gate
3560Sstevel@tonic-gate rw_enter(&privinfo_lock, RW_READER);
3570Sstevel@tonic-gate rescan:
3580Sstevel@tonic-gate for (i = 0; i < nprivs; i++)
3590Sstevel@tonic-gate if (strcasecmp(priv_names[i], name) == 0) {
3600Sstevel@tonic-gate rw_exit(&privinfo_lock);
3610Sstevel@tonic-gate return (i);
3620Sstevel@tonic-gate }
3630Sstevel@tonic-gate
3640Sstevel@tonic-gate
3650Sstevel@tonic-gate if (!wheld) {
3660Sstevel@tonic-gate if (!(flag & PRIV_ALLOC)) {
3670Sstevel@tonic-gate rw_exit(&privinfo_lock);
3680Sstevel@tonic-gate return (-EINVAL);
3690Sstevel@tonic-gate }
3700Sstevel@tonic-gate
3710Sstevel@tonic-gate /* check length, validity and available space */
3720Sstevel@tonic-gate len = strlen(name) + 1;
3730Sstevel@tonic-gate
3740Sstevel@tonic-gate if (len > PRIVNAME_MAX) {
3750Sstevel@tonic-gate rw_exit(&privinfo_lock);
3760Sstevel@tonic-gate return (-ENAMETOOLONG);
3770Sstevel@tonic-gate }
3780Sstevel@tonic-gate
3790Sstevel@tonic-gate for (p = (char *)name; *p != '\0'; p++) {
3800Sstevel@tonic-gate char c = *p;
3810Sstevel@tonic-gate
3820Sstevel@tonic-gate if (!((c >= 'A' && c <= 'Z') ||
3830Sstevel@tonic-gate (c >= 'a' && c <= 'z') ||
3840Sstevel@tonic-gate (c >= '0' && c <= '9') ||
3850Sstevel@tonic-gate c == '_')) {
3860Sstevel@tonic-gate rw_exit(&privinfo_lock);
3870Sstevel@tonic-gate return (-EINVAL);
3880Sstevel@tonic-gate }
3890Sstevel@tonic-gate }
3900Sstevel@tonic-gate
3910Sstevel@tonic-gate if (!rw_tryupgrade(&privinfo_lock)) {
3920Sstevel@tonic-gate rw_exit(&privinfo_lock);
3930Sstevel@tonic-gate rw_enter(&privinfo_lock, RW_WRITER);
3940Sstevel@tonic-gate wheld = 1;
3950Sstevel@tonic-gate /* Someone may have added our privilege */
3960Sstevel@tonic-gate goto rescan;
3970Sstevel@tonic-gate }
3980Sstevel@tonic-gate }
3990Sstevel@tonic-gate
4000Sstevel@tonic-gate if (nprivs == MAX_PRIVILEGE || len + privbytes > maxprivbytes) {
4010Sstevel@tonic-gate rw_exit(&privinfo_lock);
4020Sstevel@tonic-gate return (-ENOMEM);
4030Sstevel@tonic-gate }
4040Sstevel@tonic-gate
4050Sstevel@tonic-gate priv_names[i] = p = priv_str + privbytes;
4060Sstevel@tonic-gate
4070Sstevel@tonic-gate bcopy(name, p, len);
4080Sstevel@tonic-gate
4090Sstevel@tonic-gate /* make the priv_names[i] and privilege name globally visible */
4100Sstevel@tonic-gate membar_producer();
4110Sstevel@tonic-gate
4120Sstevel@tonic-gate /* adjust priv count and bytes count */
4130Sstevel@tonic-gate priv_ninfo->cnt = priv_info->priv_max = ++nprivs;
4140Sstevel@tonic-gate privbytes += len;
4150Sstevel@tonic-gate
4160Sstevel@tonic-gate rw_exit(&privinfo_lock);
4170Sstevel@tonic-gate return (i);
4180Sstevel@tonic-gate }
4190Sstevel@tonic-gate
4200Sstevel@tonic-gate /*
4210Sstevel@tonic-gate * We can't afford locking the privileges here because of the locations
4220Sstevel@tonic-gate * we call this from; so we make sure that the privileges table
4230Sstevel@tonic-gate * is visible to us; it is made visible before the value of nprivs is
4240Sstevel@tonic-gate * updated.
4250Sstevel@tonic-gate */
4260Sstevel@tonic-gate const char *
priv_getbynum(int priv)4270Sstevel@tonic-gate priv_getbynum(int priv)
4280Sstevel@tonic-gate {
4290Sstevel@tonic-gate int maxpriv = nprivs;
4300Sstevel@tonic-gate
4310Sstevel@tonic-gate membar_consumer();
4320Sstevel@tonic-gate
4330Sstevel@tonic-gate if (priv >= 0 && priv < maxpriv)
4340Sstevel@tonic-gate return (priv_names[priv]);
4350Sstevel@tonic-gate
4360Sstevel@tonic-gate return (NULL);
4370Sstevel@tonic-gate }
4380Sstevel@tonic-gate
4390Sstevel@tonic-gate const char *
priv_getsetbynum(int setno)4400Sstevel@tonic-gate priv_getsetbynum(int setno)
4410Sstevel@tonic-gate {
4420Sstevel@tonic-gate if (!PRIV_VALIDSET(setno))
4430Sstevel@tonic-gate return (NULL);
4440Sstevel@tonic-gate
4450Sstevel@tonic-gate return (priv_setnames[setno]);
4460Sstevel@tonic-gate }
4470Sstevel@tonic-gate
4480Sstevel@tonic-gate /*
4490Sstevel@tonic-gate * Privilege sanity checking when setting: E <= P.
4500Sstevel@tonic-gate */
4510Sstevel@tonic-gate static boolean_t
priv_valid(const cred_t * cr)4520Sstevel@tonic-gate priv_valid(const cred_t *cr)
4530Sstevel@tonic-gate {
4540Sstevel@tonic-gate return (priv_issubset(&CR_EPRIV(cr), &CR_PPRIV(cr)));
4550Sstevel@tonic-gate }
4560Sstevel@tonic-gate
4570Sstevel@tonic-gate /*
4580Sstevel@tonic-gate * Privilege manipulation functions
4590Sstevel@tonic-gate *
4600Sstevel@tonic-gate * Without knowing the details of the privilege set implementation,
4610Sstevel@tonic-gate * opaque pointers can be used to manipulate sets at will.
4620Sstevel@tonic-gate */
4630Sstevel@tonic-gate void
priv_emptyset(priv_set_t * set)4640Sstevel@tonic-gate priv_emptyset(priv_set_t *set)
4650Sstevel@tonic-gate {
4660Sstevel@tonic-gate bzero(set, sizeof (*set));
4670Sstevel@tonic-gate }
4680Sstevel@tonic-gate
4690Sstevel@tonic-gate void
priv_fillset(priv_set_t * set)4700Sstevel@tonic-gate priv_fillset(priv_set_t *set)
4710Sstevel@tonic-gate {
4720Sstevel@tonic-gate int i;
4730Sstevel@tonic-gate
4740Sstevel@tonic-gate /* memset? */
4750Sstevel@tonic-gate for (i = 0; i < PRIV_SETSIZE; i++)
4760Sstevel@tonic-gate set->pbits[i] = ~(priv_chunk_t)0;
4770Sstevel@tonic-gate }
4780Sstevel@tonic-gate
4790Sstevel@tonic-gate void
priv_addset(priv_set_t * set,int priv)4800Sstevel@tonic-gate priv_addset(priv_set_t *set, int priv)
4810Sstevel@tonic-gate {
4820Sstevel@tonic-gate ASSERT(priv >= 0 && priv < MAX_PRIVILEGE);
4830Sstevel@tonic-gate __PRIV_ASSERT(set, priv);
4840Sstevel@tonic-gate }
4850Sstevel@tonic-gate
4860Sstevel@tonic-gate void
priv_delset(priv_set_t * set,int priv)4870Sstevel@tonic-gate priv_delset(priv_set_t *set, int priv)
4880Sstevel@tonic-gate {
4890Sstevel@tonic-gate ASSERT(priv >= 0 && priv < MAX_PRIVILEGE);
4900Sstevel@tonic-gate __PRIV_CLEAR(set, priv);
4910Sstevel@tonic-gate }
4920Sstevel@tonic-gate
4930Sstevel@tonic-gate boolean_t
priv_ismember(const priv_set_t * set,int priv)4940Sstevel@tonic-gate priv_ismember(const priv_set_t *set, int priv)
4950Sstevel@tonic-gate {
4960Sstevel@tonic-gate ASSERT(priv >= 0 && priv < MAX_PRIVILEGE);
4970Sstevel@tonic-gate return (__PRIV_ISASSERT(set, priv) ? B_TRUE : B_FALSE);
4980Sstevel@tonic-gate }
4990Sstevel@tonic-gate
5000Sstevel@tonic-gate #define PRIV_TEST_BODY(test) \
5010Sstevel@tonic-gate int i; \
5020Sstevel@tonic-gate \
5030Sstevel@tonic-gate for (i = 0; i < PRIV_SETSIZE; i++) \
5040Sstevel@tonic-gate if (!(test)) \
5050Sstevel@tonic-gate return (B_FALSE); \
5060Sstevel@tonic-gate \
5070Sstevel@tonic-gate return (B_TRUE)
5080Sstevel@tonic-gate
5090Sstevel@tonic-gate boolean_t
priv_isequalset(const priv_set_t * a,const priv_set_t * b)5100Sstevel@tonic-gate priv_isequalset(const priv_set_t *a, const priv_set_t *b)
5110Sstevel@tonic-gate {
5120Sstevel@tonic-gate return ((boolean_t)(bcmp(a, b, sizeof (*a)) == 0));
5130Sstevel@tonic-gate }
5140Sstevel@tonic-gate
5150Sstevel@tonic-gate boolean_t
priv_isemptyset(const priv_set_t * set)5160Sstevel@tonic-gate priv_isemptyset(const priv_set_t *set)
5170Sstevel@tonic-gate {
5180Sstevel@tonic-gate PRIV_TEST_BODY(set->pbits[i] == 0);
5190Sstevel@tonic-gate }
5200Sstevel@tonic-gate
5210Sstevel@tonic-gate boolean_t
priv_isfullset(const priv_set_t * set)5220Sstevel@tonic-gate priv_isfullset(const priv_set_t *set)
5230Sstevel@tonic-gate {
5240Sstevel@tonic-gate PRIV_TEST_BODY(set->pbits[i] == ~(priv_chunk_t)0);
5250Sstevel@tonic-gate }
5260Sstevel@tonic-gate
5270Sstevel@tonic-gate /*
5280Sstevel@tonic-gate * Return true if a is a subset of b
5290Sstevel@tonic-gate */
5300Sstevel@tonic-gate boolean_t
priv_issubset(const priv_set_t * a,const priv_set_t * b)5310Sstevel@tonic-gate priv_issubset(const priv_set_t *a, const priv_set_t *b)
5320Sstevel@tonic-gate {
5330Sstevel@tonic-gate PRIV_TEST_BODY((a->pbits[i] | b->pbits[i]) == b->pbits[i]);
5340Sstevel@tonic-gate }
5350Sstevel@tonic-gate
5360Sstevel@tonic-gate #define PRIV_CHANGE_BODY(a, op, b) \
5370Sstevel@tonic-gate int i; \
5380Sstevel@tonic-gate \
5390Sstevel@tonic-gate for (i = 0; i < PRIV_SETSIZE; i++) \
5400Sstevel@tonic-gate a->pbits[i] op b->pbits[i]
5410Sstevel@tonic-gate
5420Sstevel@tonic-gate /* B = A ^ B */
5430Sstevel@tonic-gate void
priv_intersect(const priv_set_t * a,priv_set_t * b)5440Sstevel@tonic-gate priv_intersect(const priv_set_t *a, priv_set_t *b)
5450Sstevel@tonic-gate {
5460Sstevel@tonic-gate /* CSTYLED */
5470Sstevel@tonic-gate PRIV_CHANGE_BODY(b, &=, a);
5480Sstevel@tonic-gate }
5490Sstevel@tonic-gate
5500Sstevel@tonic-gate /* B = A v B */
5510Sstevel@tonic-gate void
priv_union(const priv_set_t * a,priv_set_t * b)5520Sstevel@tonic-gate priv_union(const priv_set_t *a, priv_set_t *b)
5530Sstevel@tonic-gate {
5540Sstevel@tonic-gate /* CSTYLED */
5550Sstevel@tonic-gate PRIV_CHANGE_BODY(b, |=, a);
5560Sstevel@tonic-gate }
5570Sstevel@tonic-gate
5580Sstevel@tonic-gate /* A = ! A */
5590Sstevel@tonic-gate void
priv_inverse(priv_set_t * a)5600Sstevel@tonic-gate priv_inverse(priv_set_t *a)
5610Sstevel@tonic-gate {
5620Sstevel@tonic-gate PRIV_CHANGE_BODY(a, = ~, a);
5630Sstevel@tonic-gate }
5640Sstevel@tonic-gate
5650Sstevel@tonic-gate /*
5660Sstevel@tonic-gate * Can the source cred act on the target credential?
5670Sstevel@tonic-gate *
5680Sstevel@tonic-gate * We will you allow to gain uids this way but not privileges.
5690Sstevel@tonic-gate */
5700Sstevel@tonic-gate int
priv_proc_cred_perm(const cred_t * scr,proc_t * tp,cred_t ** pcr,int mode)5710Sstevel@tonic-gate priv_proc_cred_perm(const cred_t *scr, proc_t *tp, cred_t **pcr, int mode)
5720Sstevel@tonic-gate {
5730Sstevel@tonic-gate const priv_set_t *eset;
5740Sstevel@tonic-gate int idsmatch;
5750Sstevel@tonic-gate cred_t *tcr;
5760Sstevel@tonic-gate int res = 0;
5770Sstevel@tonic-gate
5780Sstevel@tonic-gate /* prevent the cred from going away */
5790Sstevel@tonic-gate mutex_enter(&tp->p_crlock);
5800Sstevel@tonic-gate crhold(tcr = tp->p_cred);
5810Sstevel@tonic-gate mutex_exit(&tp->p_crlock);
5820Sstevel@tonic-gate
5839539SCasper.Dik@Sun.COM if (scr == tcr && !(tp->p_flag & SNOCD))
5840Sstevel@tonic-gate goto out;
5850Sstevel@tonic-gate
5860Sstevel@tonic-gate idsmatch = (scr->cr_uid == tcr->cr_uid &&
5879539SCasper.Dik@Sun.COM scr->cr_uid == tcr->cr_ruid &&
5889539SCasper.Dik@Sun.COM scr->cr_uid == tcr->cr_suid &&
5899539SCasper.Dik@Sun.COM scr->cr_gid == tcr->cr_gid &&
5909539SCasper.Dik@Sun.COM scr->cr_gid == tcr->cr_rgid &&
5919539SCasper.Dik@Sun.COM scr->cr_gid == tcr->cr_sgid &&
5929539SCasper.Dik@Sun.COM !(tp->p_flag & SNOCD));
5930Sstevel@tonic-gate
5940Sstevel@tonic-gate /*
5950Sstevel@tonic-gate * Source credential must have the proc_zone privilege if referencing
5960Sstevel@tonic-gate * a process in another zone.
5970Sstevel@tonic-gate */
5980Sstevel@tonic-gate if (scr->cr_zone != tcr->cr_zone && secpolicy_proc_zone(scr) != 0) {
5990Sstevel@tonic-gate res = EACCES;
6000Sstevel@tonic-gate goto out;
6010Sstevel@tonic-gate }
6020Sstevel@tonic-gate
6030Sstevel@tonic-gate if (!(mode & VWRITE)) {
6040Sstevel@tonic-gate if (!idsmatch && secpolicy_proc_owner(scr, tcr, 0) != 0)
6050Sstevel@tonic-gate res = EACCES;
6060Sstevel@tonic-gate goto out;
6070Sstevel@tonic-gate }
6080Sstevel@tonic-gate
6090Sstevel@tonic-gate /*
6100Sstevel@tonic-gate * For writing, the effective set of scr must dominate all sets of tcr,
6110Sstevel@tonic-gate * We test Pt <= Es (Et <= Pt so no need to test) and It <= Es
6120Sstevel@tonic-gate * The Limit set of scr must be a superset of the limitset of
6130Sstevel@tonic-gate * tcr.
6140Sstevel@tonic-gate */
6150Sstevel@tonic-gate eset = &CR_OEPRIV(scr);
6160Sstevel@tonic-gate
6170Sstevel@tonic-gate if (!priv_issubset(&CR_IPRIV(tcr), eset) ||
6180Sstevel@tonic-gate !priv_issubset(&CR_OPPRIV(tcr), eset) ||
6190Sstevel@tonic-gate !priv_issubset(&CR_LPRIV(tcr), &CR_LPRIV(scr)) ||
6200Sstevel@tonic-gate !idsmatch && secpolicy_proc_owner(scr, tcr, mode) != 0)
6210Sstevel@tonic-gate res = EACCES;
6220Sstevel@tonic-gate
6230Sstevel@tonic-gate out:
6240Sstevel@tonic-gate if (res == 0 && pcr != NULL)
6250Sstevel@tonic-gate *pcr = tcr;
6260Sstevel@tonic-gate else
6270Sstevel@tonic-gate crfree(tcr);
6280Sstevel@tonic-gate return (res);
6290Sstevel@tonic-gate }
6300Sstevel@tonic-gate
6310Sstevel@tonic-gate /*
6329799SCasper.Dik@Sun.COM * Set the privilege aware bit, adding L to E/P if necessary.
6339799SCasper.Dik@Sun.COM * Each time we set it, we also clear PRIV_AWARE_RESET.
6340Sstevel@tonic-gate */
6350Sstevel@tonic-gate void
priv_set_PA(cred_t * cr)6360Sstevel@tonic-gate priv_set_PA(cred_t *cr)
6370Sstevel@tonic-gate {
6380Sstevel@tonic-gate ASSERT(cr->cr_ref <= 2);
6390Sstevel@tonic-gate
6409799SCasper.Dik@Sun.COM if ((CR_FLAGS(cr) & (PRIV_AWARE|PRIV_AWARE_RESET)) == PRIV_AWARE)
6410Sstevel@tonic-gate return;
6420Sstevel@tonic-gate
6430Sstevel@tonic-gate CR_FLAGS(cr) |= PRIV_AWARE;
6449799SCasper.Dik@Sun.COM CR_FLAGS(cr) &= ~PRIV_AWARE_RESET;
6450Sstevel@tonic-gate
6460Sstevel@tonic-gate if (cr->cr_uid == 0)
6470Sstevel@tonic-gate priv_union(&CR_LPRIV(cr), &CR_EPRIV(cr));
6480Sstevel@tonic-gate
6490Sstevel@tonic-gate if (cr->cr_uid == 0 || cr->cr_suid == 0 || cr->cr_ruid == 0)
6500Sstevel@tonic-gate priv_union(&CR_LPRIV(cr), &CR_PPRIV(cr));
6510Sstevel@tonic-gate }
6520Sstevel@tonic-gate
6530Sstevel@tonic-gate boolean_t
priv_can_clear_PA(const cred_t * cr)6540Sstevel@tonic-gate priv_can_clear_PA(const cred_t *cr)
6550Sstevel@tonic-gate {
6560Sstevel@tonic-gate /*
6570Sstevel@tonic-gate * We can clear PA in the following cases:
6580Sstevel@tonic-gate *
6590Sstevel@tonic-gate * None of the uids are 0.
6600Sstevel@tonic-gate * Any uid == 0 and P == L and (Euid != 0 or E == L)
6610Sstevel@tonic-gate */
6620Sstevel@tonic-gate return ((cr->cr_suid != 0 && cr->cr_ruid != 0 && cr->cr_uid != 0) ||
6630Sstevel@tonic-gate priv_isequalset(&CR_PPRIV(cr), &CR_LPRIV(cr)) &&
6640Sstevel@tonic-gate (cr->cr_uid != 0 || priv_isequalset(&CR_EPRIV(cr), &CR_LPRIV(cr))));
6650Sstevel@tonic-gate }
6660Sstevel@tonic-gate
6670Sstevel@tonic-gate /*
6680Sstevel@tonic-gate * Clear privilege aware bit if it is an idempotent operation and by
6690Sstevel@tonic-gate * clearing it the process cannot get to uid 0 and all privileges.
6700Sstevel@tonic-gate *
6710Sstevel@tonic-gate * This function should be called with caution as it may cause "E" to be
6720Sstevel@tonic-gate * lost once a processes assumes euid 0 again.
6730Sstevel@tonic-gate */
6740Sstevel@tonic-gate void
priv_adjust_PA(cred_t * cr)6750Sstevel@tonic-gate priv_adjust_PA(cred_t *cr)
6760Sstevel@tonic-gate {
6770Sstevel@tonic-gate ASSERT(cr->cr_ref <= 2);
6780Sstevel@tonic-gate
6790Sstevel@tonic-gate if (!(CR_FLAGS(cr) & PRIV_AWARE) ||
6809799SCasper.Dik@Sun.COM !priv_can_clear_PA(cr)) {
6819799SCasper.Dik@Sun.COM CR_FLAGS(cr) &= ~PRIV_AWARE_RESET;
6820Sstevel@tonic-gate return;
6839799SCasper.Dik@Sun.COM }
6840Sstevel@tonic-gate
6850Sstevel@tonic-gate if (CR_FLAGS(cr) & PRIV_AWARE_INHERIT)
6860Sstevel@tonic-gate return;
6870Sstevel@tonic-gate
6880Sstevel@tonic-gate /*
6890Sstevel@tonic-gate * We now need to adjust P/E in those cases when uids
6900Sstevel@tonic-gate * are zero; the rules are P' = I & L, E' = I & L;
6910Sstevel@tonic-gate * but since P = L and E = L, we can use P &= I, E &= I,
6920Sstevel@tonic-gate * depending on which uids are 0.
6930Sstevel@tonic-gate */
6940Sstevel@tonic-gate if (cr->cr_suid == 0 || cr->cr_ruid == 0 || cr->cr_uid == 0) {
6950Sstevel@tonic-gate if (cr->cr_uid == 0)
6960Sstevel@tonic-gate priv_intersect(&CR_IPRIV(cr), &CR_EPRIV(cr));
6970Sstevel@tonic-gate priv_intersect(&CR_IPRIV(cr), &CR_PPRIV(cr));
6980Sstevel@tonic-gate }
6990Sstevel@tonic-gate
7009799SCasper.Dik@Sun.COM CR_FLAGS(cr) &= ~(PRIV_AWARE|PRIV_AWARE_RESET);
7010Sstevel@tonic-gate }
7029799SCasper.Dik@Sun.COM
7039799SCasper.Dik@Sun.COM /*
7049799SCasper.Dik@Sun.COM * Reset privilege aware bit if so requested by setting the PRIV_AWARE_RESET
7059799SCasper.Dik@Sun.COM * flag.
7069799SCasper.Dik@Sun.COM */
7079799SCasper.Dik@Sun.COM void
priv_reset_PA(cred_t * cr,boolean_t finalize)7089799SCasper.Dik@Sun.COM priv_reset_PA(cred_t *cr, boolean_t finalize)
7099799SCasper.Dik@Sun.COM {
7109799SCasper.Dik@Sun.COM ASSERT(cr->cr_ref <= 2);
7119799SCasper.Dik@Sun.COM
7129799SCasper.Dik@Sun.COM if ((CR_FLAGS(cr) & (PRIV_AWARE|PRIV_AWARE_RESET)) !=
7139799SCasper.Dik@Sun.COM (PRIV_AWARE|PRIV_AWARE_RESET)) {
7149799SCasper.Dik@Sun.COM CR_FLAGS(cr) &= ~PRIV_AWARE_RESET;
7159799SCasper.Dik@Sun.COM return;
7169799SCasper.Dik@Sun.COM }
7179799SCasper.Dik@Sun.COM
7189799SCasper.Dik@Sun.COM /*
7199799SCasper.Dik@Sun.COM * When PRIV_AWARE_RESET is enabled, any change of uids causes
7209799SCasper.Dik@Sun.COM * a change to the P and E sets. Bracketing with
7219799SCasper.Dik@Sun.COM * seteuid(0) ... seteuid(uid)/setreuid(-1, 0) .. setreuid(-1, uid)
7229799SCasper.Dik@Sun.COM * will cause the privilege sets "do the right thing.".
7239799SCasper.Dik@Sun.COM * When the change of the uid is "final", e.g., by using setuid(uid),
7249799SCasper.Dik@Sun.COM * or setreuid(uid, uid) or when the last set*uid() call causes all
7259799SCasper.Dik@Sun.COM * uids to be the same, we set P and E to I & L, like when you exec.
7269799SCasper.Dik@Sun.COM * We make an exception when all the uids are 0; this is required
7279799SCasper.Dik@Sun.COM * when we login as root as in that particular case we cannot
7289799SCasper.Dik@Sun.COM * make a distinction between seteuid(0) and seteuid(uid).
7299799SCasper.Dik@Sun.COM * We rely on seteuid/setreuid/setuid to tell us with the
7309799SCasper.Dik@Sun.COM * "finalize" argument that we no longer expect new uid changes,
7319799SCasper.Dik@Sun.COM * cf. setreuid(uid, uid) and setuid(uid).
7329799SCasper.Dik@Sun.COM */
7339799SCasper.Dik@Sun.COM if (cr->cr_suid == cr->cr_ruid && cr->cr_suid == cr->cr_uid) {
7349799SCasper.Dik@Sun.COM if (finalize || cr->cr_uid != 0) {
7359799SCasper.Dik@Sun.COM CR_EPRIV(cr) = CR_IPRIV(cr);
7369799SCasper.Dik@Sun.COM priv_intersect(&CR_LPRIV(cr), &CR_EPRIV(cr));
7379799SCasper.Dik@Sun.COM CR_PPRIV(cr) = CR_EPRIV(cr);
7389799SCasper.Dik@Sun.COM CR_FLAGS(cr) &= ~(PRIV_AWARE|PRIV_AWARE_RESET);
7399799SCasper.Dik@Sun.COM } else {
7409799SCasper.Dik@Sun.COM CR_EPRIV(cr) = CR_PPRIV(cr);
7419799SCasper.Dik@Sun.COM }
7429799SCasper.Dik@Sun.COM } else if (cr->cr_uid != 0 && (cr->cr_ruid == 0 || cr->cr_suid == 0)) {
7439799SCasper.Dik@Sun.COM CR_EPRIV(cr) = CR_IPRIV(cr);
7449799SCasper.Dik@Sun.COM priv_intersect(&CR_LPRIV(cr), &CR_EPRIV(cr));
7459799SCasper.Dik@Sun.COM }
7469799SCasper.Dik@Sun.COM }
747