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
75typedef 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 */
81struct 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
96typedef 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
105static int ntabent;		/* # of major numbers */
106static int totitems;		/* Number of entries in all buckets + dflt */
107static tableent_t *devpolicy;	/* The device policy itself */
108
109static krwlock_t policyrw;	/* protects the table */
110static kmutex_t policymutex;	/* allows only one concurrent devpolicy_load */
111
112devplcy_t *nullpolicy;		/* public because it's used for shortcuts */
113static devplcy_t *dfltpolicy;
114static devplcy_t *netpolicy;
115
116/*
117 * Device policy generation count; only device policies matching the
118 * generation count are still valid.
119 */
120volatile 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 */
126int maxdevpolicy = MAXDEVPOLICY;
127
128/*
129 * Initialize the device policy code
130 */
131void
132devpolicy_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 */
167devplcy_t *
168dpget(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
180void
181dphold(devplcy_t *dp)
182{
183	ASSERT(dp->dp_ref != 0xdeadbeef && dp->dp_ref != 0);
184	atomic_inc_32(&dp->dp_ref);
185}
186
187void
188dpfree(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 */
198static devplcy_t *
199match_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
279static int
280devpolicyent_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 */
307devplcy_t *
308devpolicy_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
336static devplcyent_t *
337parse_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
382static void
383freechain(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 */
403int
404devpolicy_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	/* gcc 9 can't figure out that the "i == 1" test protects all */
421	lastlen = 0;
422	lastwild = 0;
423	lastmajor = 0;
424	newpolicy = NULL;
425
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 */
578int
579devpolicy_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 */
648int
649devpolicy_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
686static void
687priv_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 */
710devplcy_t *
711devpolicy_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