10Sstevel@tonic-gate /* 20Sstevel@tonic-gate * CDDL HEADER START 30Sstevel@tonic-gate * 40Sstevel@tonic-gate * The contents of this file are subject to the terms of the 5*5753Sgww * Common Development and Distribution License (the "License"). 6*5753Sgww * You may not use this file except in compliance with the License. 70Sstevel@tonic-gate * 80Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 90Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 100Sstevel@tonic-gate * See the License for the specific language governing permissions 110Sstevel@tonic-gate * and limitations under the License. 120Sstevel@tonic-gate * 130Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 140Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 150Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 160Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 170Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 180Sstevel@tonic-gate * 190Sstevel@tonic-gate * CDDL HEADER END 200Sstevel@tonic-gate */ 210Sstevel@tonic-gate /* 22*5753Sgww * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 230Sstevel@tonic-gate * Use is subject to license terms. 240Sstevel@tonic-gate */ 250Sstevel@tonic-gate 260Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI" 270Sstevel@tonic-gate 280Sstevel@tonic-gate /* 290Sstevel@tonic-gate * Device policy implementation. 300Sstevel@tonic-gate * 310Sstevel@tonic-gate * Maintains the device policy table and defines the lookup functions. 320Sstevel@tonic-gate * 330Sstevel@tonic-gate * The table contains one entry for each major device number; each 340Sstevel@tonic-gate * major bucket has a list of minor number specific entries. First 350Sstevel@tonic-gate * match gets it. Not even simple minor names are expanded as that 360Sstevel@tonic-gate * would cause the device to be loaded. Non-wildcard entries are expanded 370Sstevel@tonic-gate * on first match. Wildcard entries are matched each open but the actual 380Sstevel@tonic-gate * policy is cached with the common snode, so the matching code will 390Sstevel@tonic-gate * probably be called infrequently. The trivial wildcard ``*'' does 400Sstevel@tonic-gate * not cause expensive string expansions and matches. 410Sstevel@tonic-gate * 420Sstevel@tonic-gate * When the policy is updated, the the generation count is increased; 430Sstevel@tonic-gate * whenever a cached policy is used, the generation count is compared; 440Sstevel@tonic-gate * if there's no match, the device policy is refreshed. 450Sstevel@tonic-gate * 460Sstevel@tonic-gate * The special policy "nullpolicy" is used to mean "no checking beyond DAC 470Sstevel@tonic-gate * needed". It too will change when the policy is rev'ed to make sure 480Sstevel@tonic-gate * that devices with nullpolicy are also refreshed. 490Sstevel@tonic-gate * 500Sstevel@tonic-gate * The special policy "dfltpolicy" is used for those devices with no 510Sstevel@tonic-gate * matching policy. On boot, it is "all privileges required". 520Sstevel@tonic-gate * This restriction on boot functions as a fail-safe; if no device policy 530Sstevel@tonic-gate * is loaded a "no restriction policy" would lead to security problems that 540Sstevel@tonic-gate * are not immediately noticable. 550Sstevel@tonic-gate */ 560Sstevel@tonic-gate 570Sstevel@tonic-gate #include <sys/priv_impl.h> 580Sstevel@tonic-gate #include <sys/policy.h> 590Sstevel@tonic-gate #include <sys/atomic.h> 600Sstevel@tonic-gate #include <sys/autoconf.h> 610Sstevel@tonic-gate #include <sys/sysmacros.h> 620Sstevel@tonic-gate #include <sys/systm.h> 630Sstevel@tonic-gate #include <sys/vnode.h> 640Sstevel@tonic-gate #include <sys/devpolicy.h> 650Sstevel@tonic-gate #include <sys/priv.h> 660Sstevel@tonic-gate #include <sys/kmem.h> 670Sstevel@tonic-gate #include <sys/ksynch.h> 680Sstevel@tonic-gate #include <sys/errno.h> 690Sstevel@tonic-gate #include <sys/sunddi.h> 700Sstevel@tonic-gate #include <c2/audit.h> 710Sstevel@tonic-gate #include <sys/fs/dv_node.h> 720Sstevel@tonic-gate 730Sstevel@tonic-gate /* 740Sstevel@tonic-gate * Internal data structures definitions. 750Sstevel@tonic-gate */ 760Sstevel@tonic-gate 770Sstevel@tonic-gate typedef struct devplcyent devplcyent_t; 780Sstevel@tonic-gate 790Sstevel@tonic-gate /* 800Sstevel@tonic-gate * The device policy entry; if there is an expression string, the 810Sstevel@tonic-gate * minor numbers are not relevant. This is indicated by dpe_len > 0. 820Sstevel@tonic-gate */ 830Sstevel@tonic-gate struct devplcyent { 840Sstevel@tonic-gate devplcyent_t *dpe_next; /* next entry in this list */ 850Sstevel@tonic-gate devplcy_t *dpe_plcy; /* policy for this entry */ 860Sstevel@tonic-gate char *dpe_expr; /* expression matching minor mode */ 870Sstevel@tonic-gate int dpe_len; /* size of allocated mem for expr */ 880Sstevel@tonic-gate uint32_t dpe_flags; /* flags */ 890Sstevel@tonic-gate minor_t dpe_lomin; /* expanded: low minor number */ 900Sstevel@tonic-gate minor_t dpe_himin; /* expanded: high minor number */ 910Sstevel@tonic-gate vtype_t dpe_spec; /* expanded: VBLK or VCHR */ 920Sstevel@tonic-gate }; 930Sstevel@tonic-gate 940Sstevel@tonic-gate #define DPE_WILDC 0x01 /* Expression has wildcard */ 950Sstevel@tonic-gate #define DPE_ALLMINOR 0x02 /* Matches all minor numbers */ 960Sstevel@tonic-gate #define DPE_EXPANDED 0x04 /* Minor numbers expanded */ 970Sstevel@tonic-gate 980Sstevel@tonic-gate typedef struct tableent { 990Sstevel@tonic-gate devplcyent_t *t_ent; /* list of policies by minor */ 1000Sstevel@tonic-gate major_t t_major; /* device major number */ 1010Sstevel@tonic-gate } tableent_t; 1020Sstevel@tonic-gate 1030Sstevel@tonic-gate /* 1040Sstevel@tonic-gate * The data store. 1050Sstevel@tonic-gate */ 1060Sstevel@tonic-gate 1070Sstevel@tonic-gate static int ntabent; /* # of major numbers */ 1080Sstevel@tonic-gate static int totitems; /* Number of entries in all buckets + dflt */ 1090Sstevel@tonic-gate static tableent_t *devpolicy; /* The device policy itself */ 1100Sstevel@tonic-gate 1110Sstevel@tonic-gate static krwlock_t policyrw; /* protects the table */ 1120Sstevel@tonic-gate static kmutex_t policymutex; /* allows only one concurrent devpolicy_load */ 1130Sstevel@tonic-gate 1140Sstevel@tonic-gate devplcy_t *nullpolicy; /* public because it's used for shortcuts */ 1150Sstevel@tonic-gate static devplcy_t *dfltpolicy; 1160Sstevel@tonic-gate static devplcy_t *netpolicy; 1170Sstevel@tonic-gate 1180Sstevel@tonic-gate /* 1190Sstevel@tonic-gate * Device policy generation count; only device policies matching the 1200Sstevel@tonic-gate * generation count are still valid. 1210Sstevel@tonic-gate */ 1220Sstevel@tonic-gate volatile uint32_t devplcy_gen; 1230Sstevel@tonic-gate 1240Sstevel@tonic-gate /* 1250Sstevel@tonic-gate * Tunable: maximum number of device policy entries to load in 1260Sstevel@tonic-gate * a system call. (Protects KM_SLEEP call) 1270Sstevel@tonic-gate */ 1280Sstevel@tonic-gate int maxdevpolicy = MAXDEVPOLICY; 1290Sstevel@tonic-gate 1300Sstevel@tonic-gate /* 1310Sstevel@tonic-gate * Initialize the device policy code 1320Sstevel@tonic-gate */ 1330Sstevel@tonic-gate void 1340Sstevel@tonic-gate devpolicy_init(void) 1350Sstevel@tonic-gate { 1360Sstevel@tonic-gate rw_init(&policyrw, NULL, RW_DRIVER, NULL); 1370Sstevel@tonic-gate mutex_init(&policymutex, NULL, MUTEX_DRIVER, NULL); 1380Sstevel@tonic-gate 1390Sstevel@tonic-gate /* The mutex is held here in order to satisfy the ASSERT in dpget() */ 1400Sstevel@tonic-gate mutex_enter(&policymutex); 1410Sstevel@tonic-gate 1420Sstevel@tonic-gate nullpolicy = dpget(); 1430Sstevel@tonic-gate dfltpolicy = dpget(); 1440Sstevel@tonic-gate netpolicy = dpget(); 1450Sstevel@tonic-gate 1460Sstevel@tonic-gate /* 1470Sstevel@tonic-gate * Initially, we refuse access to all devices except 1480Sstevel@tonic-gate * to processes with all privileges. 1490Sstevel@tonic-gate */ 1500Sstevel@tonic-gate priv_fillset(&dfltpolicy->dp_rdp); 1510Sstevel@tonic-gate priv_fillset(&dfltpolicy->dp_wrp); 1520Sstevel@tonic-gate 1530Sstevel@tonic-gate totitems = 1; 1540Sstevel@tonic-gate 1550Sstevel@tonic-gate devplcy_gen++; 1560Sstevel@tonic-gate mutex_exit(&policymutex); 1570Sstevel@tonic-gate 1580Sstevel@tonic-gate /* initialize default network privilege */ 1590Sstevel@tonic-gate priv_emptyset(&netpolicy->dp_rdp); 1600Sstevel@tonic-gate priv_emptyset(&netpolicy->dp_wrp); 1610Sstevel@tonic-gate priv_addset(&netpolicy->dp_rdp, PRIV_NET_RAWACCESS); 1620Sstevel@tonic-gate priv_addset(&netpolicy->dp_wrp, PRIV_NET_RAWACCESS); 1630Sstevel@tonic-gate } 1640Sstevel@tonic-gate 1650Sstevel@tonic-gate /* 1660Sstevel@tonic-gate * Devpolicy reference counting/allocation routines. 1670Sstevel@tonic-gate * cf. crget()/crhold()/crfree(). 1680Sstevel@tonic-gate */ 1690Sstevel@tonic-gate devplcy_t * 1700Sstevel@tonic-gate dpget(void) 1710Sstevel@tonic-gate { 1720Sstevel@tonic-gate devplcy_t *dp = kmem_zalloc(sizeof (*dp), KM_SLEEP); 1730Sstevel@tonic-gate 1740Sstevel@tonic-gate ASSERT(MUTEX_HELD(&policymutex)); 1750Sstevel@tonic-gate 1760Sstevel@tonic-gate dp->dp_ref = 1; 1770Sstevel@tonic-gate /* New ones belong to the next generation */ 1780Sstevel@tonic-gate dp->dp_gen = devplcy_gen + 1; 1790Sstevel@tonic-gate return (dp); 1800Sstevel@tonic-gate } 1810Sstevel@tonic-gate 1820Sstevel@tonic-gate void 1830Sstevel@tonic-gate dphold(devplcy_t *dp) 1840Sstevel@tonic-gate { 1850Sstevel@tonic-gate ASSERT(dp->dp_ref != 0xdeadbeef && dp->dp_ref != 0); 1860Sstevel@tonic-gate atomic_add_32(&dp->dp_ref, 1); 1870Sstevel@tonic-gate } 1880Sstevel@tonic-gate 1890Sstevel@tonic-gate void 1900Sstevel@tonic-gate dpfree(devplcy_t *dp) 1910Sstevel@tonic-gate { 1920Sstevel@tonic-gate ASSERT(dp->dp_ref != 0xdeadbeef && dp->dp_ref != 0); 1930Sstevel@tonic-gate if (atomic_add_32_nv(&dp->dp_ref, -1) == 0) 1940Sstevel@tonic-gate kmem_free(dp, sizeof (*dp)); 1950Sstevel@tonic-gate } 1960Sstevel@tonic-gate 1970Sstevel@tonic-gate /* 1980Sstevel@tonic-gate * Find the policy that matches this device. 1990Sstevel@tonic-gate */ 2000Sstevel@tonic-gate static devplcy_t * 2010Sstevel@tonic-gate match_policy(devplcyent_t *de, dev_t dev, vtype_t spec) 2020Sstevel@tonic-gate { 2030Sstevel@tonic-gate char *mname = NULL; 2040Sstevel@tonic-gate minor_t min = getminor(dev); 2050Sstevel@tonic-gate 2060Sstevel@tonic-gate for (; de != NULL; de = de->dpe_next) { 2070Sstevel@tonic-gate if (de->dpe_flags & DPE_ALLMINOR) 2080Sstevel@tonic-gate break; 2090Sstevel@tonic-gate 2100Sstevel@tonic-gate if (de->dpe_flags & DPE_EXPANDED) { 2110Sstevel@tonic-gate if (min >= de->dpe_lomin && min <= de->dpe_himin && 2120Sstevel@tonic-gate spec == de->dpe_spec) { 2130Sstevel@tonic-gate break; 2140Sstevel@tonic-gate } else { 2150Sstevel@tonic-gate continue; 2160Sstevel@tonic-gate } 2170Sstevel@tonic-gate } 2180Sstevel@tonic-gate 2190Sstevel@tonic-gate /* 2200Sstevel@tonic-gate * We now need the minor name to match string or 2210Sstevel@tonic-gate * simle regexp. Could we use csp->s_dip and not 2220Sstevel@tonic-gate * allocate a string here? 2230Sstevel@tonic-gate */ 2240Sstevel@tonic-gate if (mname == NULL && 2250Sstevel@tonic-gate ddi_lyr_get_minor_name(dev, spec, &mname) != DDI_SUCCESS) 2260Sstevel@tonic-gate /* mname can be set after the function fails */ 2270Sstevel@tonic-gate return (dfltpolicy); 2280Sstevel@tonic-gate 2290Sstevel@tonic-gate /* Simple wildcard, with only one ``*'' */ 2300Sstevel@tonic-gate if (de->dpe_flags & DPE_WILDC) { 2310Sstevel@tonic-gate int plen = de->dpe_len - 1; 2320Sstevel@tonic-gate int slen = strlen(mname); 2330Sstevel@tonic-gate char *pp = de->dpe_expr; 2340Sstevel@tonic-gate char *sp = mname; 2350Sstevel@tonic-gate 2360Sstevel@tonic-gate /* string must be at least as long as pattern w/o '*' */ 2370Sstevel@tonic-gate if (slen < plen - 1) 2380Sstevel@tonic-gate continue; 2390Sstevel@tonic-gate 2400Sstevel@tonic-gate /* skip prefix */ 2410Sstevel@tonic-gate while (*pp == *sp && *pp != '\0') { 2420Sstevel@tonic-gate pp++; 2430Sstevel@tonic-gate sp++; 2440Sstevel@tonic-gate } 2450Sstevel@tonic-gate /* matched single '*' */ 2460Sstevel@tonic-gate if (*pp == '\0') 2470Sstevel@tonic-gate if (*sp == '\0') 2480Sstevel@tonic-gate break; 2490Sstevel@tonic-gate else 2500Sstevel@tonic-gate continue; 2510Sstevel@tonic-gate if (*pp != '*') 2520Sstevel@tonic-gate continue; 2530Sstevel@tonic-gate 2540Sstevel@tonic-gate pp++; 2550Sstevel@tonic-gate /* 2560Sstevel@tonic-gate * skip characters matched by '*': difference of 2570Sstevel@tonic-gate * length of s and length of pattern sans '*' 2580Sstevel@tonic-gate */ 2590Sstevel@tonic-gate sp += slen - (plen - 1); 2600Sstevel@tonic-gate if (strcmp(pp, sp) == 0) /* match! */ 2610Sstevel@tonic-gate break; 2620Sstevel@tonic-gate 2630Sstevel@tonic-gate } else if (strcmp(de->dpe_expr, mname) == 0) { 2640Sstevel@tonic-gate /* Store minor number, if no contention */ 2650Sstevel@tonic-gate if (rw_tryupgrade(&policyrw)) { 2660Sstevel@tonic-gate de->dpe_lomin = de->dpe_himin = min; 2670Sstevel@tonic-gate de->dpe_spec = spec; 2680Sstevel@tonic-gate de->dpe_flags |= DPE_EXPANDED; 2690Sstevel@tonic-gate } 2700Sstevel@tonic-gate break; 2710Sstevel@tonic-gate } 2720Sstevel@tonic-gate 2730Sstevel@tonic-gate } 2740Sstevel@tonic-gate 2750Sstevel@tonic-gate if (mname != NULL) 2760Sstevel@tonic-gate kmem_free(mname, strlen(mname) + 1); 2770Sstevel@tonic-gate 2780Sstevel@tonic-gate return (de != NULL ? de->dpe_plcy : dfltpolicy); 2790Sstevel@tonic-gate } 2800Sstevel@tonic-gate 2810Sstevel@tonic-gate static int 2820Sstevel@tonic-gate devpolicyent_bymajor(major_t maj) 2830Sstevel@tonic-gate { 2840Sstevel@tonic-gate int lo, hi; 2850Sstevel@tonic-gate 2860Sstevel@tonic-gate ASSERT(RW_LOCK_HELD(&policyrw)); 2870Sstevel@tonic-gate 2880Sstevel@tonic-gate lo = 0; 2890Sstevel@tonic-gate hi = ntabent - 1; 2900Sstevel@tonic-gate 2910Sstevel@tonic-gate /* Binary search for major number */ 2920Sstevel@tonic-gate while (lo <= hi) { 2930Sstevel@tonic-gate int mid = (lo + hi) / 2; 2940Sstevel@tonic-gate 2950Sstevel@tonic-gate if (devpolicy[mid].t_major == maj) 2960Sstevel@tonic-gate return (mid); 2970Sstevel@tonic-gate else if (maj < devpolicy[mid].t_major) 2980Sstevel@tonic-gate hi = mid - 1; 2990Sstevel@tonic-gate else 3000Sstevel@tonic-gate lo = mid + 1; 3010Sstevel@tonic-gate } 3020Sstevel@tonic-gate return (-1); 3030Sstevel@tonic-gate } 3040Sstevel@tonic-gate 3050Sstevel@tonic-gate /* 3060Sstevel@tonic-gate * Returns held device policy for the specific device node. 3070Sstevel@tonic-gate * Note devfs_devpolicy returns with a hold on the policy. 3080Sstevel@tonic-gate */ 3090Sstevel@tonic-gate devplcy_t * 3100Sstevel@tonic-gate devpolicy_find(vnode_t *vp) 3110Sstevel@tonic-gate { 3120Sstevel@tonic-gate dev_t dev = vp->v_rdev; 3130Sstevel@tonic-gate vtype_t spec = vp->v_type; 3140Sstevel@tonic-gate major_t maj = getmajor(dev); 3150Sstevel@tonic-gate int i; 3160Sstevel@tonic-gate devplcy_t *res; 3170Sstevel@tonic-gate 3180Sstevel@tonic-gate if (maj == clone_major) 3190Sstevel@tonic-gate maj = getminor(dev); 3200Sstevel@tonic-gate 3210Sstevel@tonic-gate rw_enter(&policyrw, RW_READER); 3220Sstevel@tonic-gate 3230Sstevel@tonic-gate i = devpolicyent_bymajor(maj); 3240Sstevel@tonic-gate 3250Sstevel@tonic-gate if (i != -1) { 3260Sstevel@tonic-gate res = match_policy(devpolicy[i].t_ent, dev, spec); 3270Sstevel@tonic-gate dphold(res); 3280Sstevel@tonic-gate } else if (devfs_devpolicy(vp, &res) != 0) { 3290Sstevel@tonic-gate res = NETWORK_DRV(maj) ? netpolicy : dfltpolicy; 3300Sstevel@tonic-gate dphold(res); 3310Sstevel@tonic-gate } 3320Sstevel@tonic-gate 3330Sstevel@tonic-gate rw_exit(&policyrw); 3340Sstevel@tonic-gate 3350Sstevel@tonic-gate return (res); 3360Sstevel@tonic-gate } 3370Sstevel@tonic-gate 3380Sstevel@tonic-gate static devplcyent_t * 3390Sstevel@tonic-gate parse_policy(devplcysys_t *ds, devplcy_t *nullp, devplcy_t *defp) 3400Sstevel@tonic-gate { 3410Sstevel@tonic-gate devplcyent_t *de = kmem_zalloc(sizeof (*de), KM_SLEEP); 3420Sstevel@tonic-gate devplcy_t *np; 3430Sstevel@tonic-gate 3440Sstevel@tonic-gate if (priv_isemptyset(&ds->dps_rdp) && priv_isemptyset(&ds->dps_wrp)) 3450Sstevel@tonic-gate dphold(np = nullp); 3460Sstevel@tonic-gate else if (defp != nullp && 347*5753Sgww priv_isequalset(&ds->dps_rdp, &defp->dp_rdp) && 348*5753Sgww priv_isequalset(&ds->dps_wrp, &defp->dp_wrp)) 3490Sstevel@tonic-gate dphold(np = defp); 3500Sstevel@tonic-gate else { 3510Sstevel@tonic-gate np = dpget(); 3520Sstevel@tonic-gate np->dp_rdp = ds->dps_rdp; 3530Sstevel@tonic-gate np->dp_wrp = ds->dps_wrp; 3540Sstevel@tonic-gate } 3550Sstevel@tonic-gate 3560Sstevel@tonic-gate if (ds->dps_minornm[0] != '\0') { 3570Sstevel@tonic-gate de->dpe_len = strlen(ds->dps_minornm) + 1; 3580Sstevel@tonic-gate 3590Sstevel@tonic-gate if (strchr(ds->dps_minornm, '*') != NULL) { 3600Sstevel@tonic-gate if (de->dpe_len == 2) { /* "*\0" */ 3610Sstevel@tonic-gate de->dpe_flags = DPE_ALLMINOR; 3620Sstevel@tonic-gate de->dpe_len = 0; 3630Sstevel@tonic-gate } else 3640Sstevel@tonic-gate de->dpe_flags = DPE_WILDC; 3650Sstevel@tonic-gate } 3660Sstevel@tonic-gate if (de->dpe_len != 0) { 3670Sstevel@tonic-gate de->dpe_expr = kmem_alloc(de->dpe_len, KM_SLEEP); 3680Sstevel@tonic-gate (void) strcpy(de->dpe_expr, ds->dps_minornm); 3690Sstevel@tonic-gate } 3700Sstevel@tonic-gate } else { 3710Sstevel@tonic-gate de->dpe_lomin = ds->dps_lomin; 3720Sstevel@tonic-gate de->dpe_himin = ds->dps_himin; 3730Sstevel@tonic-gate de->dpe_flags = DPE_EXPANDED; 3740Sstevel@tonic-gate de->dpe_spec = ds->dps_isblock ? VBLK : VCHR; 3750Sstevel@tonic-gate } 3760Sstevel@tonic-gate de->dpe_plcy = np; 3770Sstevel@tonic-gate 3780Sstevel@tonic-gate ASSERT((de->dpe_flags & (DPE_ALLMINOR|DPE_EXPANDED)) || 379*5753Sgww de->dpe_expr != NULL); 3800Sstevel@tonic-gate 3810Sstevel@tonic-gate return (de); 3820Sstevel@tonic-gate } 3830Sstevel@tonic-gate 3840Sstevel@tonic-gate static void 3850Sstevel@tonic-gate freechain(devplcyent_t *de) 3860Sstevel@tonic-gate { 3870Sstevel@tonic-gate devplcyent_t *dn; 3880Sstevel@tonic-gate 3890Sstevel@tonic-gate do { 3900Sstevel@tonic-gate dn = de->dpe_next; 3910Sstevel@tonic-gate dpfree(de->dpe_plcy); 3920Sstevel@tonic-gate if (de->dpe_len != 0) 3930Sstevel@tonic-gate kmem_free(de->dpe_expr, de->dpe_len); 3940Sstevel@tonic-gate kmem_free(de, sizeof (*de)); 3950Sstevel@tonic-gate de = dn; 3960Sstevel@tonic-gate } while (de != NULL); 3970Sstevel@tonic-gate } 3980Sstevel@tonic-gate 3990Sstevel@tonic-gate /* 4000Sstevel@tonic-gate * Load the device policy. 4010Sstevel@tonic-gate * The device policy currently makes nu distinction between the 4020Sstevel@tonic-gate * block and characters devices; that is generally not a problem 4030Sstevel@tonic-gate * as the names of those devices cannot clash. 4040Sstevel@tonic-gate */ 4050Sstevel@tonic-gate int 4060Sstevel@tonic-gate devpolicy_load(int nitems, size_t sz, devplcysys_t *uitmp) 4070Sstevel@tonic-gate { 4080Sstevel@tonic-gate int i, j; 4090Sstevel@tonic-gate int nmaj = 0; 4100Sstevel@tonic-gate major_t lastmajor; 4110Sstevel@tonic-gate devplcysys_t *items; 4120Sstevel@tonic-gate size_t mem; 4130Sstevel@tonic-gate major_t curmaj; 4140Sstevel@tonic-gate devplcyent_t **last, *de; 4150Sstevel@tonic-gate 4160Sstevel@tonic-gate tableent_t *newpolicy, *oldpolicy; 4170Sstevel@tonic-gate devplcy_t *newnull, *newdflt, *oldnull, *olddflt; 4180Sstevel@tonic-gate int oldcnt; 4190Sstevel@tonic-gate int lastlen; 4200Sstevel@tonic-gate int lastwild; 4210Sstevel@tonic-gate 4220Sstevel@tonic-gate #ifdef lint 4230Sstevel@tonic-gate /* Lint can't figure out that the "i == 1" test protects all */ 4240Sstevel@tonic-gate lastlen = 0; 4250Sstevel@tonic-gate lastwild = 0; 4260Sstevel@tonic-gate lastmajor = 0; 4270Sstevel@tonic-gate #endif 4280Sstevel@tonic-gate /* 4290Sstevel@tonic-gate * The application must agree with the kernel on the size of each 4300Sstevel@tonic-gate * item; it must not exceed the maximum number and must be 4310Sstevel@tonic-gate * at least 1 item in size. 4320Sstevel@tonic-gate */ 4330Sstevel@tonic-gate if (sz != sizeof (devplcysys_t) || nitems > maxdevpolicy || nitems < 1) 4340Sstevel@tonic-gate return (EINVAL); 4350Sstevel@tonic-gate 4360Sstevel@tonic-gate mem = nitems * sz; 4370Sstevel@tonic-gate 4380Sstevel@tonic-gate items = kmem_alloc(mem, KM_SLEEP); 4390Sstevel@tonic-gate 4400Sstevel@tonic-gate if (copyin(uitmp, items, mem)) { 4410Sstevel@tonic-gate kmem_free(items, mem); 4420Sstevel@tonic-gate return (EFAULT); 4430Sstevel@tonic-gate } 4440Sstevel@tonic-gate 4450Sstevel@tonic-gate /* Check for default policy, it must exist and be sorted first */ 4460Sstevel@tonic-gate if (items[0].dps_maj != DEVPOLICY_DFLT_MAJ) { 4470Sstevel@tonic-gate kmem_free(items, mem); 4480Sstevel@tonic-gate return (EINVAL); 4490Sstevel@tonic-gate } 4500Sstevel@tonic-gate 4510Sstevel@tonic-gate /* 4520Sstevel@tonic-gate * Application must deliver entries sorted. 4530Sstevel@tonic-gate * Sorted meaning here: 4540Sstevel@tonic-gate * In major number order 4550Sstevel@tonic-gate * For each major number, we first need to have the explicit 4560Sstevel@tonic-gate * entries, then the wild card entries, longest first. 4570Sstevel@tonic-gate */ 4580Sstevel@tonic-gate for (i = 1; i < nitems; i++) { 4590Sstevel@tonic-gate int len, wild; 4600Sstevel@tonic-gate char *tmp; 4610Sstevel@tonic-gate 4620Sstevel@tonic-gate curmaj = items[i].dps_maj; 4630Sstevel@tonic-gate len = strlen(items[i].dps_minornm); 4640Sstevel@tonic-gate wild = len > 0 && 465*5753Sgww (tmp = strchr(items[i].dps_minornm, '*')) != NULL; 4660Sstevel@tonic-gate 4670Sstevel@tonic-gate /* Another default major, string too long or too many ``*'' */ 4680Sstevel@tonic-gate if (curmaj == DEVPOLICY_DFLT_MAJ || 4690Sstevel@tonic-gate len >= sizeof (items[i].dps_minornm) || 4700Sstevel@tonic-gate wild && strchr(tmp + 1, '*') != NULL) { 4710Sstevel@tonic-gate kmem_free(items, mem); 4720Sstevel@tonic-gate return (EINVAL); 4730Sstevel@tonic-gate } 4740Sstevel@tonic-gate if (i == 1 || lastmajor < curmaj) { 4750Sstevel@tonic-gate lastmajor = curmaj; 4760Sstevel@tonic-gate nmaj++; 4770Sstevel@tonic-gate } else if (lastmajor > curmaj || lastwild > wild || 478*5753Sgww lastwild && lastlen < len) { 4790Sstevel@tonic-gate kmem_free(items, mem); 4800Sstevel@tonic-gate return (EINVAL); 4810Sstevel@tonic-gate } 4820Sstevel@tonic-gate lastlen = len; 4830Sstevel@tonic-gate lastwild = wild; 4840Sstevel@tonic-gate } 4850Sstevel@tonic-gate 4860Sstevel@tonic-gate if (audit_active) 4870Sstevel@tonic-gate audit_devpolicy(nitems, items); 4880Sstevel@tonic-gate 4890Sstevel@tonic-gate /* 4900Sstevel@tonic-gate * Parse the policy. We create an array for all major numbers 4910Sstevel@tonic-gate * and in each major number bucket we'll have a linked list of 4920Sstevel@tonic-gate * entries. Each item may contain either a lo,hi minor pair 4930Sstevel@tonic-gate * or a string/wild card matching a minor node. 4940Sstevel@tonic-gate */ 4950Sstevel@tonic-gate if (nmaj > 0) 4960Sstevel@tonic-gate newpolicy = kmem_zalloc(nmaj * sizeof (tableent_t), KM_SLEEP); 4970Sstevel@tonic-gate 4980Sstevel@tonic-gate /* 4990Sstevel@tonic-gate * We want to lock out concurrent updates but we don't want to 5000Sstevel@tonic-gate * lock out device opens while we still need to allocate memory. 5010Sstevel@tonic-gate * As soon as we allocate new devplcy_t's we commit to the next 5020Sstevel@tonic-gate * generation number, so we must lock out other updates from here. 5030Sstevel@tonic-gate */ 5040Sstevel@tonic-gate mutex_enter(&policymutex); 5050Sstevel@tonic-gate 5060Sstevel@tonic-gate /* New default and NULL policy */ 5070Sstevel@tonic-gate newnull = dpget(); 5080Sstevel@tonic-gate 5090Sstevel@tonic-gate if (priv_isemptyset(&items[0].dps_rdp) && 5100Sstevel@tonic-gate priv_isemptyset(&items[0].dps_wrp)) { 5110Sstevel@tonic-gate newdflt = newnull; 5120Sstevel@tonic-gate dphold(newdflt); 5130Sstevel@tonic-gate } else { 5140Sstevel@tonic-gate newdflt = dpget(); 5150Sstevel@tonic-gate newdflt->dp_rdp = items[0].dps_rdp; 5160Sstevel@tonic-gate newdflt->dp_wrp = items[0].dps_wrp; 5170Sstevel@tonic-gate } 5180Sstevel@tonic-gate 5190Sstevel@tonic-gate j = -1; 5200Sstevel@tonic-gate 5210Sstevel@tonic-gate /* Userland made sure sorting was ok */ 5220Sstevel@tonic-gate for (i = 1; i < nitems; i++) { 5230Sstevel@tonic-gate de = parse_policy(&items[i], newnull, newdflt); 5240Sstevel@tonic-gate 5250Sstevel@tonic-gate if (j == -1 || curmaj != items[i].dps_maj) { 5260Sstevel@tonic-gate j++; 5270Sstevel@tonic-gate newpolicy[j].t_major = curmaj = items[i].dps_maj; 5280Sstevel@tonic-gate last = &newpolicy[j].t_ent; 5290Sstevel@tonic-gate } 5300Sstevel@tonic-gate *last = de; 5310Sstevel@tonic-gate last = &de->dpe_next; 5320Sstevel@tonic-gate } 5330Sstevel@tonic-gate 5340Sstevel@tonic-gate /* Done parsing, throw away input */ 5350Sstevel@tonic-gate kmem_free(items, mem); 5360Sstevel@tonic-gate 5370Sstevel@tonic-gate /* Lock out all devpolicy_find()s */ 5380Sstevel@tonic-gate rw_enter(&policyrw, RW_WRITER); 5390Sstevel@tonic-gate 5400Sstevel@tonic-gate /* Install the new global data */ 5410Sstevel@tonic-gate oldnull = nullpolicy; 5420Sstevel@tonic-gate nullpolicy = newnull; 5430Sstevel@tonic-gate 5440Sstevel@tonic-gate olddflt = dfltpolicy; 5450Sstevel@tonic-gate dfltpolicy = newdflt; 5460Sstevel@tonic-gate 5470Sstevel@tonic-gate oldcnt = ntabent; 5480Sstevel@tonic-gate ntabent = nmaj; 5490Sstevel@tonic-gate 5500Sstevel@tonic-gate totitems = nitems; 5510Sstevel@tonic-gate 5520Sstevel@tonic-gate oldpolicy = devpolicy; 5530Sstevel@tonic-gate devpolicy = newpolicy; 5540Sstevel@tonic-gate 5550Sstevel@tonic-gate /* Force all calls by devpolicy_find() */ 5560Sstevel@tonic-gate devplcy_gen++; 5570Sstevel@tonic-gate 5580Sstevel@tonic-gate /* Reenable policy finds */ 5590Sstevel@tonic-gate rw_exit(&policyrw); 5600Sstevel@tonic-gate mutex_exit(&policymutex); 5610Sstevel@tonic-gate 5620Sstevel@tonic-gate /* Free old stuff */ 5630Sstevel@tonic-gate if (oldcnt != 0) { 5640Sstevel@tonic-gate for (i = 0; i < oldcnt; i++) 5650Sstevel@tonic-gate freechain(oldpolicy[i].t_ent); 5660Sstevel@tonic-gate kmem_free(oldpolicy, oldcnt * sizeof (*oldpolicy)); 5670Sstevel@tonic-gate } 5680Sstevel@tonic-gate 5690Sstevel@tonic-gate dpfree(oldnull); 5700Sstevel@tonic-gate dpfree(olddflt); 5710Sstevel@tonic-gate 5720Sstevel@tonic-gate return (0); 5730Sstevel@tonic-gate } 5740Sstevel@tonic-gate 5750Sstevel@tonic-gate /* 5760Sstevel@tonic-gate * Get device policy: argument one is a pointer to an integer holding 5770Sstevel@tonic-gate * the number of items allocated for the 3rd argument; the size argument 5780Sstevel@tonic-gate * is a revision check between kernel and userland. 5790Sstevel@tonic-gate */ 5800Sstevel@tonic-gate int 5810Sstevel@tonic-gate devpolicy_get(int *nitemp, size_t sz, devplcysys_t *uitmp) 5820Sstevel@tonic-gate { 5830Sstevel@tonic-gate int i; 5840Sstevel@tonic-gate devplcyent_t *de; 5850Sstevel@tonic-gate devplcysys_t *itmp; 5860Sstevel@tonic-gate int ind; 5870Sstevel@tonic-gate int nitems; 5880Sstevel@tonic-gate int err = 0; 5890Sstevel@tonic-gate size_t alloced; 5900Sstevel@tonic-gate 5910Sstevel@tonic-gate if (sz != sizeof (devplcysys_t)) 5920Sstevel@tonic-gate return (EINVAL); 5930Sstevel@tonic-gate 5940Sstevel@tonic-gate if (copyin(nitemp, &nitems, sizeof (nitems))) 5950Sstevel@tonic-gate return (EFAULT); 5960Sstevel@tonic-gate 5970Sstevel@tonic-gate rw_enter(&policyrw, RW_READER); 5980Sstevel@tonic-gate 5990Sstevel@tonic-gate if (copyout(&totitems, nitemp, sizeof (totitems))) 6000Sstevel@tonic-gate err = EFAULT; 6010Sstevel@tonic-gate else if (nitems < totitems) 6020Sstevel@tonic-gate err = ENOMEM; 6030Sstevel@tonic-gate 6040Sstevel@tonic-gate if (err != 0) { 6050Sstevel@tonic-gate rw_exit(&policyrw); 6060Sstevel@tonic-gate return (err); 6070Sstevel@tonic-gate } 6080Sstevel@tonic-gate 6090Sstevel@tonic-gate alloced = totitems * sizeof (devplcysys_t); 6100Sstevel@tonic-gate itmp = kmem_zalloc(alloced, KM_SLEEP); 6110Sstevel@tonic-gate 6120Sstevel@tonic-gate itmp[0].dps_rdp = dfltpolicy->dp_rdp; 6130Sstevel@tonic-gate itmp[0].dps_wrp = dfltpolicy->dp_wrp; 6140Sstevel@tonic-gate itmp[0].dps_maj = DEVPOLICY_DFLT_MAJ; 6150Sstevel@tonic-gate 6160Sstevel@tonic-gate ind = 1; 6170Sstevel@tonic-gate 6180Sstevel@tonic-gate for (i = 0; i < ntabent; i++) { 6190Sstevel@tonic-gate for (de = devpolicy[i].t_ent; de != NULL; de = de->dpe_next) { 6200Sstevel@tonic-gate itmp[ind].dps_maj = devpolicy[i].t_major; 6210Sstevel@tonic-gate itmp[ind].dps_rdp = de->dpe_plcy->dp_rdp; 6220Sstevel@tonic-gate itmp[ind].dps_wrp = de->dpe_plcy->dp_wrp; 6230Sstevel@tonic-gate if (de->dpe_len) 6240Sstevel@tonic-gate (void) strcpy(itmp[ind].dps_minornm, 625*5753Sgww de->dpe_expr); 6260Sstevel@tonic-gate else if (de->dpe_flags & DPE_ALLMINOR) 6270Sstevel@tonic-gate (void) strcpy(itmp[ind].dps_minornm, "*"); 6280Sstevel@tonic-gate else { 6290Sstevel@tonic-gate itmp[ind].dps_lomin = de->dpe_lomin; 6300Sstevel@tonic-gate itmp[ind].dps_himin = de->dpe_himin; 6310Sstevel@tonic-gate itmp[ind].dps_isblock = de->dpe_spec == VBLK; 6320Sstevel@tonic-gate } 6330Sstevel@tonic-gate ind++; 6340Sstevel@tonic-gate } 6350Sstevel@tonic-gate } 6360Sstevel@tonic-gate 6370Sstevel@tonic-gate rw_exit(&policyrw); 6380Sstevel@tonic-gate 6390Sstevel@tonic-gate if (copyout(itmp, uitmp, alloced)) 6400Sstevel@tonic-gate err = EFAULT; 6410Sstevel@tonic-gate 6420Sstevel@tonic-gate kmem_free(itmp, alloced); 6430Sstevel@tonic-gate return (err); 6440Sstevel@tonic-gate } 6450Sstevel@tonic-gate 6460Sstevel@tonic-gate /* 6470Sstevel@tonic-gate * Get device policy by device name. 6480Sstevel@tonic-gate * This is the implementation of MODGETDEVPOLICYBYNAME 6490Sstevel@tonic-gate */ 6500Sstevel@tonic-gate int 6510Sstevel@tonic-gate devpolicy_getbyname(size_t sz, devplcysys_t *uitmp, char *devname) 6520Sstevel@tonic-gate { 6530Sstevel@tonic-gate devplcysys_t itm; 6540Sstevel@tonic-gate devplcy_t *plcy; 6550Sstevel@tonic-gate vtype_t spec; 6560Sstevel@tonic-gate vnode_t *vp; 6570Sstevel@tonic-gate 6580Sstevel@tonic-gate if (sz != sizeof (devplcysys_t)) 6590Sstevel@tonic-gate return (EINVAL); 6600Sstevel@tonic-gate 6610Sstevel@tonic-gate if (lookupname(devname, UIO_USERSPACE, FOLLOW, 6620Sstevel@tonic-gate NULLVPP, &vp) != 0) 6630Sstevel@tonic-gate return (EINVAL); 6640Sstevel@tonic-gate 6650Sstevel@tonic-gate spec = vp->v_type; 6660Sstevel@tonic-gate if (spec != VBLK && spec != VCHR) { 6670Sstevel@tonic-gate VN_RELE(vp); 6680Sstevel@tonic-gate return (EINVAL); 6690Sstevel@tonic-gate } 6700Sstevel@tonic-gate 6710Sstevel@tonic-gate plcy = devpolicy_find(vp); 6720Sstevel@tonic-gate VN_RELE(vp); 6730Sstevel@tonic-gate 6740Sstevel@tonic-gate bzero(&itm, sizeof (itm)); 6750Sstevel@tonic-gate 6760Sstevel@tonic-gate /* These are the only values of interest */ 6770Sstevel@tonic-gate itm.dps_rdp = plcy->dp_rdp; 6780Sstevel@tonic-gate itm.dps_wrp = plcy->dp_wrp; 6790Sstevel@tonic-gate 6800Sstevel@tonic-gate dpfree(plcy); 6810Sstevel@tonic-gate 6820Sstevel@tonic-gate if (copyout(&itm, uitmp, sz)) 6830Sstevel@tonic-gate return (EFAULT); 6840Sstevel@tonic-gate else 6850Sstevel@tonic-gate return (0); 6860Sstevel@tonic-gate } 6870Sstevel@tonic-gate 6880Sstevel@tonic-gate static void 6890Sstevel@tonic-gate priv_str_to_set(const char *priv_name, priv_set_t *priv_set) 6900Sstevel@tonic-gate { 6910Sstevel@tonic-gate if (priv_name == NULL || strcmp(priv_name, "none") == 0) { 6920Sstevel@tonic-gate priv_emptyset(priv_set); 6930Sstevel@tonic-gate } else if (strcmp(priv_name, "all") == 0) { 6940Sstevel@tonic-gate priv_fillset(priv_set); 6950Sstevel@tonic-gate } else { 6960Sstevel@tonic-gate int priv; 6970Sstevel@tonic-gate priv = priv_getbyname(priv_name, PRIV_ALLOC); 6980Sstevel@tonic-gate if (priv < 0) { 6990Sstevel@tonic-gate cmn_err(CE_WARN, "fail to allocate privilege: %s", 7000Sstevel@tonic-gate priv_name); 7010Sstevel@tonic-gate return; 7020Sstevel@tonic-gate } 7030Sstevel@tonic-gate priv_emptyset(priv_set); 7040Sstevel@tonic-gate priv_addset(priv_set, priv); 7050Sstevel@tonic-gate } 7060Sstevel@tonic-gate } 7070Sstevel@tonic-gate 7080Sstevel@tonic-gate /* 7090Sstevel@tonic-gate * Return device privileges by privilege name 7100Sstevel@tonic-gate * Called by ddi_create_priv_minor_node() 7110Sstevel@tonic-gate */ 7120Sstevel@tonic-gate devplcy_t * 7130Sstevel@tonic-gate devpolicy_priv_by_name(const char *read_priv, const char *write_priv) 7140Sstevel@tonic-gate { 7150Sstevel@tonic-gate devplcy_t *dp; 7160Sstevel@tonic-gate mutex_enter(&policymutex); 7170Sstevel@tonic-gate dp = dpget(); 7180Sstevel@tonic-gate mutex_exit(&policymutex); 7190Sstevel@tonic-gate priv_str_to_set(read_priv, &dp->dp_rdp); 7200Sstevel@tonic-gate priv_str_to_set(write_priv, &dp->dp_wrp); 7210Sstevel@tonic-gate 7220Sstevel@tonic-gate return (dp); 7230Sstevel@tonic-gate } 724