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