xref: /illumos-gate/usr/src/uts/common/os/devpolicy.c (revision 2850d85b7b93f31e578520dc3b3feb24db609c62)
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