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
devpolicy_init(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 *
dpget(void)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
dphold(devplcy_t * dp)181 dphold(devplcy_t *dp)
182 {
183 ASSERT(dp->dp_ref != 0xdeadbeef && dp->dp_ref != 0);
184 atomic_inc_32(&dp->dp_ref);
185 }
186
187 void
dpfree(devplcy_t * dp)188 dpfree(devplcy_t *dp)
189 {
190 ASSERT(dp->dp_ref != 0xdeadbeef && dp->dp_ref != 0);
191 if (atomic_dec_32_nv(&dp->dp_ref) == 0)
192 kmem_free(dp, sizeof (*dp));
193 }
194
195 /*
196 * Find the policy that matches this device.
197 */
198 static devplcy_t *
match_policy(devplcyent_t * de,dev_t dev,vtype_t spec)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
devpolicyent_bymajor(major_t maj)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 *
devpolicy_find(vnode_t * vp)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 *
parse_policy(devplcysys_t * ds,devplcy_t * nullp,devplcy_t * defp)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
freechain(devplcyent_t * de)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
devpolicy_load(int nitems,size_t sz,devplcysys_t * uitmp)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
devpolicy_get(int * nitemp,size_t sz,devplcysys_t * uitmp)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
devpolicy_getbyname(size_t sz,devplcysys_t * uitmp,char * devname)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
priv_str_to_set(const char * priv_name,priv_set_t * priv_set)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 *
devpolicy_priv_by_name(const char * read_priv,const char * write_priv)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