Home | History | Annotate | Download | only in os
      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 2007 Sun Microsystems, Inc.  All rights reserved.
     23  * Use is subject to license terms.
     24  */
     25 
     26 #pragma ident	"%Z%%M%	%I%	%E% SMI"
     27 
     28 /*
     29  * Device policy implementation.
     30  *
     31  * Maintains the device policy table and defines the lookup functions.
     32  *
     33  * The table contains one entry for each major device number; each
     34  * major bucket has a list of minor number specific entries.  First
     35  * match gets it.  Not even simple minor names are expanded as that
     36  * would cause the device to be loaded.  Non-wildcard entries are expanded
     37  * on first match. Wildcard entries are matched each open but the actual
     38  * policy is cached with the common snode, so the matching code will
     39  * probably be called infrequently.  The trivial wildcard ``*'' does
     40  * not cause expensive string expansions and matches.
     41  *
     42  * When the policy is updated, the the generation count is increased;
     43  * whenever a cached policy is used, the generation count is compared;
     44  * if there's no match, the device policy is refreshed.
     45  *
     46  * The special policy "nullpolicy" is used to mean "no checking beyond DAC
     47  * needed".  It too will change when the policy is rev'ed to make sure
     48  * that devices with nullpolicy are also refreshed.
     49  *
     50  * The special policy "dfltpolicy" is used for those devices with no
     51  * matching policy.  On boot, it is "all privileges required".
     52  * This restriction on boot functions as a fail-safe; if no device policy
     53  * is loaded a "no restriction policy" would lead to security problems that
     54  * are not immediately noticable.
     55  */
     56 
     57 #include <sys/priv_impl.h>
     58 #include <sys/policy.h>
     59 #include <sys/atomic.h>
     60 #include <sys/autoconf.h>
     61 #include <sys/sysmacros.h>
     62 #include <sys/systm.h>
     63 #include <sys/vnode.h>
     64 #include <sys/devpolicy.h>
     65 #include <sys/priv.h>
     66 #include <sys/kmem.h>
     67 #include <sys/ksynch.h>
     68 #include <sys/errno.h>
     69 #include <sys/sunddi.h>
     70 #include <c2/audit.h>
     71 #include <sys/fs/dv_node.h>
     72 
     73 /*
     74  * Internal data structures definitions.
     75  */
     76 
     77 typedef struct devplcyent devplcyent_t;
     78 
     79 /*
     80  * The device policy entry; if there is an expression string, the
     81  * minor numbers are not relevant.  This is indicated by dpe_len > 0.
     82  */
     83 struct devplcyent {
     84 	devplcyent_t	*dpe_next;	/* next entry in this list */
     85 	devplcy_t	*dpe_plcy;	/* policy for this entry */
     86 	char		*dpe_expr;	/* expression matching minor mode */
     87 	int		dpe_len;	/* size of allocated mem for expr */
     88 	uint32_t	dpe_flags;	/* flags */
     89 	minor_t		dpe_lomin;	/* expanded: low minor number */
     90 	minor_t		dpe_himin;	/* expanded: high minor number */
     91 	vtype_t		dpe_spec;	/* expanded: VBLK or VCHR */
     92 };
     93 
     94 #define	DPE_WILDC	0x01		/* Expression has wildcard */
     95 #define	DPE_ALLMINOR	0x02		/* Matches all minor numbers */
     96 #define	DPE_EXPANDED	0x04		/* Minor numbers expanded */
     97 
     98 typedef struct tableent {
     99 	devplcyent_t	*t_ent;		/* list of policies by minor */
    100 	major_t		t_major;	/* device major number */
    101 } tableent_t;
    102 
    103 /*
    104  * The data store.
    105  */
    106 
    107 static int ntabent;		/* # of major numbers */
    108 static int totitems;		/* Number of entries in all buckets + dflt */
    109 static tableent_t *devpolicy;	/* The device policy itself */
    110 
    111 static krwlock_t policyrw;	/* protects the table */
    112 static kmutex_t policymutex;	/* allows only one concurrent devpolicy_load */
    113 
    114 devplcy_t *nullpolicy;		/* public because it's used for shortcuts */
    115 static devplcy_t *dfltpolicy;
    116 static devplcy_t *netpolicy;
    117 
    118 /*
    119  * Device policy generation count; only device policies matching the
    120  * generation count are still valid.
    121  */
    122 volatile uint32_t devplcy_gen;
    123 
    124 /*
    125  * Tunable: maximum number of device policy entries to load in
    126  * a system call.  (Protects KM_SLEEP call)
    127  */
    128 int maxdevpolicy = MAXDEVPOLICY;
    129 
    130 /*
    131  * Initialize the device policy code
    132  */
    133 void
    134 devpolicy_init(void)
    135 {
    136 	rw_init(&policyrw, NULL, RW_DRIVER, NULL);
    137 	mutex_init(&policymutex, NULL, MUTEX_DRIVER, NULL);
    138 
    139 	/* The mutex is held here in order to satisfy the ASSERT in dpget() */
    140 	mutex_enter(&policymutex);
    141 
    142 	nullpolicy = dpget();
    143 	dfltpolicy = dpget();
    144 	netpolicy = dpget();
    145 
    146 	/*
    147 	 * Initially, we refuse access to all devices except
    148 	 * to processes with all privileges.
    149 	 */
    150 	priv_fillset(&dfltpolicy->dp_rdp);
    151 	priv_fillset(&dfltpolicy->dp_wrp);
    152 
    153 	totitems = 1;
    154 
    155 	devplcy_gen++;
    156 	mutex_exit(&policymutex);
    157 
    158 	/* initialize default network privilege */
    159 	priv_emptyset(&netpolicy->dp_rdp);
    160 	priv_emptyset(&netpolicy->dp_wrp);
    161 	priv_addset(&netpolicy->dp_rdp, PRIV_NET_RAWACCESS);
    162 	priv_addset(&netpolicy->dp_wrp, PRIV_NET_RAWACCESS);
    163 }
    164 
    165 /*
    166  * Devpolicy reference counting/allocation routines.
    167  * cf. crget()/crhold()/crfree().
    168  */
    169 devplcy_t *
    170 dpget(void)
    171 {
    172 	devplcy_t *dp = kmem_zalloc(sizeof (*dp), KM_SLEEP);
    173 
    174 	ASSERT(MUTEX_HELD(&policymutex));
    175 
    176 	dp->dp_ref = 1;
    177 	/* New ones belong to the next generation */
    178 	dp->dp_gen = devplcy_gen + 1;
    179 	return (dp);
    180 }
    181 
    182 void
    183 dphold(devplcy_t *dp)
    184 {
    185 	ASSERT(dp->dp_ref != 0xdeadbeef && dp->dp_ref != 0);
    186 	atomic_add_32(&dp->dp_ref, 1);
    187 }
    188 
    189 void
    190 dpfree(devplcy_t *dp)
    191 {
    192 	ASSERT(dp->dp_ref != 0xdeadbeef && dp->dp_ref != 0);
    193 	if (atomic_add_32_nv(&dp->dp_ref, -1) == 0)
    194 		kmem_free(dp, sizeof (*dp));
    195 }
    196 
    197 /*
    198  * Find the policy that matches this device.
    199  */
    200 static devplcy_t *
    201 match_policy(devplcyent_t *de, dev_t dev, vtype_t spec)
    202 {
    203 	char *mname = NULL;
    204 	minor_t min = getminor(dev);
    205 
    206 	for (; de != NULL; de = de->dpe_next) {
    207 		if (de->dpe_flags & DPE_ALLMINOR)
    208 			break;
    209 
    210 		if (de->dpe_flags & DPE_EXPANDED) {
    211 			if (min >= de->dpe_lomin && min <= de->dpe_himin &&
    212 			    spec == de->dpe_spec) {
    213 				break;
    214 			} else {
    215 				continue;
    216 			}
    217 		}
    218 
    219 		/*
    220 		 * We now need the minor name to match string or
    221 		 * simle regexp.  Could we use csp->s_dip and not
    222 		 * allocate a string here?
    223 		 */
    224 		if (mname == NULL &&
    225 		    ddi_lyr_get_minor_name(dev, spec, &mname) != DDI_SUCCESS)
    226 			/* mname can be set after the function fails */
    227 			return (dfltpolicy);
    228 
    229 		/* Simple wildcard, with only one ``*'' */
    230 		if (de->dpe_flags & DPE_WILDC) {
    231 			int plen = de->dpe_len - 1;
    232 			int slen = strlen(mname);
    233 			char *pp = de->dpe_expr;
    234 			char *sp = mname;
    235 
    236 			/* string must be at least as long as pattern w/o '*' */
    237 			if (slen < plen - 1)
    238 				continue;
    239 
    240 			/* skip prefix */
    241 			while (*pp == *sp && *pp != '\0') {
    242 				pp++;
    243 				sp++;
    244 			}
    245 			/* matched single '*' */
    246 			if (*pp == '\0')
    247 				if (*sp == '\0')
    248 					break;
    249 				else
    250 					continue;
    251 			if (*pp != '*')
    252 				continue;
    253 
    254 			pp++;
    255 			/*
    256 			 * skip characters matched by '*': difference of
    257 			 * length of s and length of pattern sans '*'
    258 			 */
    259 			sp += slen - (plen - 1);
    260 			if (strcmp(pp, sp) == 0) 	/* match! */
    261 				break;
    262 
    263 		} else if (strcmp(de->dpe_expr, mname) == 0) {
    264 			/* Store minor number, if no contention */
    265 			if (rw_tryupgrade(&policyrw)) {
    266 				de->dpe_lomin = de->dpe_himin = min;
    267 				de->dpe_spec = spec;
    268 				de->dpe_flags |= DPE_EXPANDED;
    269 			}
    270 			break;
    271 		}
    272 
    273 	}
    274 
    275 	if (mname != NULL)
    276 		kmem_free(mname, strlen(mname) + 1);
    277 
    278 	return (de != NULL ? de->dpe_plcy : dfltpolicy);
    279 }
    280 
    281 static int
    282 devpolicyent_bymajor(major_t maj)
    283 {
    284 	int lo, hi;
    285 
    286 	ASSERT(RW_LOCK_HELD(&policyrw));
    287 
    288 	lo = 0;
    289 	hi = ntabent - 1;
    290 
    291 	/* Binary search for major number */
    292 	while (lo <= hi) {
    293 		int mid = (lo + hi) / 2;
    294 
    295 		if (devpolicy[mid].t_major == maj)
    296 			return (mid);
    297 		else if (maj < devpolicy[mid].t_major)
    298 			hi = mid - 1;
    299 		else
    300 			lo = mid + 1;
    301 	}
    302 	return (-1);
    303 }
    304 
    305 /*
    306  * Returns held device policy for the specific device node.
    307  * Note devfs_devpolicy returns with a hold on the policy.
    308  */
    309 devplcy_t *
    310 devpolicy_find(vnode_t *vp)
    311 {
    312 	dev_t dev = vp->v_rdev;
    313 	vtype_t spec = vp->v_type;
    314 	major_t maj = getmajor(dev);
    315 	int i;
    316 	devplcy_t *res;
    317 
    318 	if (maj == clone_major)
    319 		maj = getminor(dev);
    320 
    321 	rw_enter(&policyrw, RW_READER);
    322 
    323 	i = devpolicyent_bymajor(maj);
    324 
    325 	if (i != -1) {
    326 		res = match_policy(devpolicy[i].t_ent, dev, spec);
    327 		dphold(res);
    328 	} else if (devfs_devpolicy(vp, &res) != 0) {
    329 		res = NETWORK_DRV(maj) ? netpolicy : dfltpolicy;
    330 		dphold(res);
    331 	}
    332 
    333 	rw_exit(&policyrw);
    334 
    335 	return (res);
    336 }
    337 
    338 static devplcyent_t *
    339 parse_policy(devplcysys_t *ds, devplcy_t *nullp, devplcy_t *defp)
    340 {
    341 	devplcyent_t *de = kmem_zalloc(sizeof (*de), KM_SLEEP);
    342 	devplcy_t *np;
    343 
    344 	if (priv_isemptyset(&ds->dps_rdp) && priv_isemptyset(&ds->dps_wrp))
    345 		dphold(np = nullp);
    346 	else if (defp != nullp &&
    347 	    priv_isequalset(&ds->dps_rdp, &defp->dp_rdp) &&
    348 	    priv_isequalset(&ds->dps_wrp, &defp->dp_wrp))
    349 		dphold(np = defp);
    350 	else {
    351 		np = dpget();
    352 		np->dp_rdp = ds->dps_rdp;
    353 		np->dp_wrp = ds->dps_wrp;
    354 	}
    355 
    356 	if (ds->dps_minornm[0] != '\0') {
    357 		de->dpe_len = strlen(ds->dps_minornm) + 1;
    358 
    359 		if (strchr(ds->dps_minornm, '*') != NULL) {
    360 			if (de->dpe_len == 2) {		/* "*\0" */
    361 				de->dpe_flags = DPE_ALLMINOR;
    362 				de->dpe_len = 0;
    363 			} else
    364 				de->dpe_flags = DPE_WILDC;
    365 		}
    366 		if (de->dpe_len != 0) {
    367 			de->dpe_expr = kmem_alloc(de->dpe_len, KM_SLEEP);
    368 			(void) strcpy(de->dpe_expr, ds->dps_minornm);
    369 		}
    370 	} else {
    371 		de->dpe_lomin = ds->dps_lomin;
    372 		de->dpe_himin = ds->dps_himin;
    373 		de->dpe_flags = DPE_EXPANDED;
    374 		de->dpe_spec = ds->dps_isblock ? VBLK : VCHR;
    375 	}
    376 	de->dpe_plcy = np;
    377 
    378 	ASSERT((de->dpe_flags & (DPE_ALLMINOR|DPE_EXPANDED)) ||
    379 	    de->dpe_expr != NULL);
    380 
    381 	return (de);
    382 }
    383 
    384 static void
    385 freechain(devplcyent_t *de)
    386 {
    387 	devplcyent_t *dn;
    388 
    389 	do {
    390 		dn = de->dpe_next;
    391 		dpfree(de->dpe_plcy);
    392 		if (de->dpe_len != 0)
    393 			kmem_free(de->dpe_expr, de->dpe_len);
    394 		kmem_free(de, sizeof (*de));
    395 		de = dn;
    396 	} while (de != NULL);
    397 }
    398 
    399 /*
    400  * Load the device policy.
    401  * The device policy currently makes nu distinction between the
    402  * block and characters devices; that is generally not a problem
    403  * as the names of those devices cannot clash.
    404  */
    405 int
    406 devpolicy_load(int nitems, size_t sz, devplcysys_t *uitmp)
    407 {
    408 	int i, j;
    409 	int nmaj = 0;
    410 	major_t lastmajor;
    411 	devplcysys_t *items;
    412 	size_t mem;
    413 	major_t curmaj;
    414 	devplcyent_t **last, *de;
    415 
    416 	tableent_t *newpolicy, *oldpolicy;
    417 	devplcy_t *newnull, *newdflt, *oldnull, *olddflt;
    418 	int oldcnt;
    419 	int lastlen;
    420 	int lastwild;
    421 
    422 #ifdef lint
    423 	/* Lint can't figure out that the "i == 1" test protects all */
    424 	lastlen = 0;
    425 	lastwild = 0;
    426 	lastmajor = 0;
    427 #endif
    428 	/*
    429 	 * The application must agree with the kernel on the size of each
    430 	 * item; it must not exceed the maximum number and must be
    431 	 * at least 1 item in size.
    432 	 */
    433 	if (sz != sizeof (devplcysys_t) || nitems > maxdevpolicy || nitems < 1)
    434 		return (EINVAL);
    435 
    436 	mem = nitems * sz;
    437 
    438 	items = kmem_alloc(mem, KM_SLEEP);
    439 
    440 	if (copyin(uitmp, items, mem)) {
    441 		kmem_free(items, mem);
    442 		return (EFAULT);
    443 	}
    444 
    445 	/* Check for default policy, it must exist and be sorted first */
    446 	if (items[0].dps_maj != DEVPOLICY_DFLT_MAJ) {
    447 		kmem_free(items, mem);
    448 		return (EINVAL);
    449 	}
    450 
    451 	/*
    452 	 * Application must deliver entries sorted.
    453 	 * Sorted meaning here:
    454 	 *	In major number order
    455 	 *	For each major number, we first need to have the explicit
    456 	 *	entries, then the wild card entries, longest first.
    457 	 */
    458 	for (i = 1; i < nitems; i++) {
    459 		int len, wild;
    460 		char *tmp;
    461 
    462 		curmaj = items[i].dps_maj;
    463 		len = strlen(items[i].dps_minornm);
    464 		wild = len > 0 &&
    465 		    (tmp = strchr(items[i].dps_minornm, '*')) != NULL;
    466 
    467 		/* Another default major, string too long or too many ``*'' */
    468 		if (curmaj == DEVPOLICY_DFLT_MAJ ||
    469 		    len >= sizeof (items[i].dps_minornm) ||
    470 		    wild && strchr(tmp + 1, '*') != NULL) {
    471 			kmem_free(items, mem);
    472 			return (EINVAL);
    473 		}
    474 		if (i == 1 || lastmajor < curmaj) {
    475 			lastmajor = curmaj;
    476 			nmaj++;
    477 		} else if (lastmajor > curmaj || lastwild > wild ||
    478 		    lastwild && lastlen < len) {
    479 			kmem_free(items, mem);
    480 			return (EINVAL);
    481 		}
    482 		lastlen = len;
    483 		lastwild = wild;
    484 	}
    485 
    486 	if (audit_active)
    487 		audit_devpolicy(nitems, items);
    488 
    489 	/*
    490 	 * Parse the policy.  We create an array for all major numbers
    491 	 * and in each major number bucket we'll have a linked list of
    492 	 * entries.  Each item may contain either a lo,hi minor pair
    493 	 * or a string/wild card matching a minor node.
    494 	 */
    495 	if (nmaj > 0)
    496 		newpolicy = kmem_zalloc(nmaj * sizeof (tableent_t), KM_SLEEP);
    497 
    498 	/*
    499 	 * We want to lock out concurrent updates but we don't want to
    500 	 * lock out device opens while we still need to allocate memory.
    501 	 * As soon as we allocate new devplcy_t's we commit to the next
    502 	 * generation number, so we must lock out other updates from here.
    503 	 */
    504 	mutex_enter(&policymutex);
    505 
    506 	/* New default and NULL policy */
    507 	newnull = dpget();
    508 
    509 	if (priv_isemptyset(&items[0].dps_rdp) &&
    510 	    priv_isemptyset(&items[0].dps_wrp)) {
    511 		newdflt = newnull;
    512 		dphold(newdflt);
    513 	} else {
    514 		newdflt = dpget();
    515 		newdflt->dp_rdp = items[0].dps_rdp;
    516 		newdflt->dp_wrp = items[0].dps_wrp;
    517 	}
    518 
    519 	j = -1;
    520 
    521 	/* Userland made sure sorting was ok */
    522 	for (i = 1; i < nitems; i++) {
    523 		de = parse_policy(&items[i], newnull, newdflt);
    524 
    525 		if (j == -1 || curmaj != items[i].dps_maj) {
    526 			j++;
    527 			newpolicy[j].t_major = curmaj = items[i].dps_maj;
    528 			last = &newpolicy[j].t_ent;
    529 		}
    530 		*last = de;
    531 		last = &de->dpe_next;
    532 	}
    533 
    534 	/* Done parsing, throw away input */
    535 	kmem_free(items, mem);
    536 
    537 	/* Lock out all devpolicy_find()s */
    538 	rw_enter(&policyrw, RW_WRITER);
    539 
    540 	/* Install the new global data */
    541 	oldnull = nullpolicy;
    542 	nullpolicy = newnull;
    543 
    544 	olddflt = dfltpolicy;
    545 	dfltpolicy = newdflt;
    546 
    547 	oldcnt = ntabent;
    548 	ntabent = nmaj;
    549 
    550 	totitems = nitems;
    551 
    552 	oldpolicy = devpolicy;
    553 	devpolicy = newpolicy;
    554 
    555 	/* Force all calls by devpolicy_find() */
    556 	devplcy_gen++;
    557 
    558 	/* Reenable policy finds */
    559 	rw_exit(&policyrw);
    560 	mutex_exit(&policymutex);
    561 
    562 	/* Free old stuff */
    563 	if (oldcnt != 0) {
    564 		for (i = 0; i < oldcnt; i++)
    565 			freechain(oldpolicy[i].t_ent);
    566 		kmem_free(oldpolicy, oldcnt * sizeof (*oldpolicy));
    567 	}
    568 
    569 	dpfree(oldnull);
    570 	dpfree(olddflt);
    571 
    572 	return (0);
    573 }
    574 
    575 /*
    576  * Get device policy: argument one is a pointer to an integer holding
    577  * the number of items allocated for the 3rd argument; the size argument
    578  * is a revision check between kernel and userland.
    579  */
    580 int
    581 devpolicy_get(int *nitemp, size_t sz, devplcysys_t *uitmp)
    582 {
    583 	int i;
    584 	devplcyent_t *de;
    585 	devplcysys_t *itmp;
    586 	int ind;
    587 	int nitems;
    588 	int err = 0;
    589 	size_t alloced;
    590 
    591 	if (sz != sizeof (devplcysys_t))
    592 		return (EINVAL);
    593 
    594 	if (copyin(nitemp, &nitems, sizeof (nitems)))
    595 		return (EFAULT);
    596 
    597 	rw_enter(&policyrw, RW_READER);
    598 
    599 	if (copyout(&totitems, nitemp, sizeof (totitems)))
    600 		err = EFAULT;
    601 	else if (nitems < totitems)
    602 		err = ENOMEM;
    603 
    604 	if (err != 0) {
    605 		rw_exit(&policyrw);
    606 		return (err);
    607 	}
    608 
    609 	alloced = totitems * sizeof (devplcysys_t);
    610 	itmp = kmem_zalloc(alloced, KM_SLEEP);
    611 
    612 	itmp[0].dps_rdp = dfltpolicy->dp_rdp;
    613 	itmp[0].dps_wrp = dfltpolicy->dp_wrp;
    614 	itmp[0].dps_maj = DEVPOLICY_DFLT_MAJ;
    615 
    616 	ind = 1;
    617 
    618 	for (i = 0; i < ntabent; i++) {
    619 		for (de = devpolicy[i].t_ent; de != NULL; de = de->dpe_next) {
    620 			itmp[ind].dps_maj = devpolicy[i].t_major;
    621 			itmp[ind].dps_rdp = de->dpe_plcy->dp_rdp;
    622 			itmp[ind].dps_wrp = de->dpe_plcy->dp_wrp;
    623 			if (de->dpe_len)
    624 				(void) strcpy(itmp[ind].dps_minornm,
    625 				    de->dpe_expr);
    626 			else if (de->dpe_flags & DPE_ALLMINOR)
    627 				(void) strcpy(itmp[ind].dps_minornm, "*");
    628 			else {
    629 				itmp[ind].dps_lomin = de->dpe_lomin;
    630 				itmp[ind].dps_himin = de->dpe_himin;
    631 				itmp[ind].dps_isblock = de->dpe_spec == VBLK;
    632 			}
    633 			ind++;
    634 		}
    635 	}
    636 
    637 	rw_exit(&policyrw);
    638 
    639 	if (copyout(itmp, uitmp, alloced))
    640 		err = EFAULT;
    641 
    642 	kmem_free(itmp, alloced);
    643 	return (err);
    644 }
    645 
    646 /*
    647  * Get device policy by device name.
    648  * This is the implementation of MODGETDEVPOLICYBYNAME
    649  */
    650 int
    651 devpolicy_getbyname(size_t sz, devplcysys_t *uitmp, char *devname)
    652 {
    653 	devplcysys_t itm;
    654 	devplcy_t *plcy;
    655 	vtype_t spec;
    656 	vnode_t *vp;
    657 
    658 	if (sz != sizeof (devplcysys_t))
    659 		return (EINVAL);
    660 
    661 	if (lookupname(devname, UIO_USERSPACE, FOLLOW,
    662 	    NULLVPP, &vp) != 0)
    663 		return (EINVAL);
    664 
    665 	spec = vp->v_type;
    666 	if (spec != VBLK && spec != VCHR) {
    667 		VN_RELE(vp);
    668 		return (EINVAL);
    669 	}
    670 
    671 	plcy = devpolicy_find(vp);
    672 	VN_RELE(vp);
    673 
    674 	bzero(&itm, sizeof (itm));
    675 
    676 	/* These are the only values of interest */
    677 	itm.dps_rdp = plcy->dp_rdp;
    678 	itm.dps_wrp = plcy->dp_wrp;
    679 
    680 	dpfree(plcy);
    681 
    682 	if (copyout(&itm, uitmp, sz))
    683 		return (EFAULT);
    684 	else
    685 		return (0);
    686 }
    687 
    688 static void
    689 priv_str_to_set(const char *priv_name, priv_set_t *priv_set)
    690 {
    691 	if (priv_name == NULL || strcmp(priv_name, "none") == 0) {
    692 		priv_emptyset(priv_set);
    693 	} else if (strcmp(priv_name, "all") == 0) {
    694 		priv_fillset(priv_set);
    695 	} else {
    696 		int priv;
    697 		priv = priv_getbyname(priv_name, PRIV_ALLOC);
    698 		if (priv < 0) {
    699 			cmn_err(CE_WARN, "fail to allocate privilege: %s",
    700 			    priv_name);
    701 			return;
    702 		}
    703 		priv_emptyset(priv_set);
    704 		priv_addset(priv_set, priv);
    705 	}
    706 }
    707 
    708 /*
    709  * Return device privileges by privilege name
    710  * Called by ddi_create_priv_minor_node()
    711  */
    712 devplcy_t *
    713 devpolicy_priv_by_name(const char *read_priv, const char *write_priv)
    714 {
    715 	devplcy_t *dp;
    716 	mutex_enter(&policymutex);
    717 	dp = dpget();
    718 	mutex_exit(&policymutex);
    719 	priv_str_to_set(read_priv, &dp->dp_rdp);
    720 	priv_str_to_set(write_priv, &dp->dp_wrp);
    721 
    722 	return (dp);
    723 }
    724