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