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