devpolicy.c revision 005d3feb53a9a10272d4a24b03991575d6a9bcb3
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2010 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
* Device policy implementation.
*
* Maintains the device policy table and defines the lookup functions.
*
* The table contains one entry for each major device number; each
* major bucket has a list of minor number specific entries. First
* match gets it. Not even simple minor names are expanded as that
* would cause the device to be loaded. Non-wildcard entries are expanded
* on first match. Wildcard entries are matched each open but the actual
* policy is cached with the common snode, so the matching code will
* probably be called infrequently. The trivial wildcard ``*'' does
* not cause expensive string expansions and matches.
*
* When the policy is updated, the the generation count is increased;
* whenever a cached policy is used, the generation count is compared;
* if there's no match, the device policy is refreshed.
*
* The special policy "nullpolicy" is used to mean "no checking beyond DAC
* needed". It too will change when the policy is rev'ed to make sure
* that devices with nullpolicy are also refreshed.
*
* The special policy "dfltpolicy" is used for those devices with no
* matching policy. On boot, it is "all privileges required".
* This restriction on boot functions as a fail-safe; if no device policy
* is loaded a "no restriction policy" would lead to security problems that
* are not immediately noticable.
*/
#include <sys/priv_impl.h>
#include <sys/autoconf.h>
#include <sys/sysmacros.h>
#include <sys/devpolicy.h>
/*
* Internal data structures definitions.
*/
typedef struct devplcyent devplcyent_t;
/*
* The device policy entry; if there is an expression string, the
* minor numbers are not relevant. This is indicated by dpe_len > 0.
*/
struct devplcyent {
char *dpe_expr; /* expression matching minor mode */
int dpe_len; /* size of allocated mem for expr */
};
typedef struct tableent {
} tableent_t;
/*
* The data store.
*/
static int ntabent; /* # of major numbers */
static int totitems; /* Number of entries in all buckets + dflt */
static devplcy_t *dfltpolicy;
/*
* Device policy generation count; only device policies matching the
* generation count are still valid.
*/
volatile uint32_t devplcy_gen;
/*
* Tunable: maximum number of device policy entries to load in
* a system call. (Protects KM_SLEEP call)
*/
int maxdevpolicy = MAXDEVPOLICY;
/*
* Initialize the device policy code
*/
void
devpolicy_init(void)
{
/* The mutex is held here in order to satisfy the ASSERT in dpget() */
nullpolicy = dpget();
dfltpolicy = dpget();
/*
* Initially, we refuse access to all devices except
* to processes with all privileges.
*/
totitems = 1;
devplcy_gen++;
/* initialize default network privilege */
}
/*
* Devpolicy reference counting/allocation routines.
* cf. crget()/crhold()/crfree().
*/
dpget(void)
{
/* New ones belong to the next generation */
return (dp);
}
void
{
}
void
{
}
/*
* Find the policy that matches this device.
*/
static devplcy_t *
{
break;
break;
} else {
continue;
}
}
/*
* We now need the minor name to match string or
* simle regexp. Could we use csp->s_dip and not
* allocate a string here?
*/
/* mname can be set after the function fails */
return (dfltpolicy);
/* Simple wildcard, with only one ``*'' */
/* string must be at least as long as pattern w/o '*' */
continue;
/* skip prefix */
pp++;
sp++;
}
/* matched single '*' */
if (*pp == '\0')
if (*sp == '\0')
break;
else
continue;
if (*pp != '*')
continue;
pp++;
/*
* skip characters matched by '*': difference of
* length of s and length of pattern sans '*'
*/
break;
/* Store minor number, if no contention */
if (rw_tryupgrade(&policyrw)) {
}
break;
}
}
}
static int
{
lo = 0;
/* Binary search for major number */
return (mid);
else
}
return (-1);
}
/*
* Returns held device policy for the specific device node.
* Note devfs_devpolicy returns with a hold on the policy.
*/
{
int i;
if (maj == clone_major)
i = devpolicyent_bymajor(maj);
if (i != -1) {
}
return (res);
}
static devplcyent_t *
{
else {
}
} else
}
}
} else {
}
return (de);
}
static void
{
do {
}
/*
* Load the device policy.
* The device policy currently makes nu distinction between the
* block and characters devices; that is generally not a problem
* as the names of those devices cannot clash.
*/
int
{
int i, j;
int nmaj = 0;
int oldcnt;
int lastlen;
int lastwild;
#ifdef lint
/* Lint can't figure out that the "i == 1" test protects all */
lastlen = 0;
lastwild = 0;
lastmajor = 0;
#endif
/*
* The application must agree with the kernel on the size of each
* item; it must not exceed the maximum number and must be
* at least 1 item in size.
*/
return (EINVAL);
return (EFAULT);
}
/* Check for default policy, it must exist and be sorted first */
return (EINVAL);
}
/*
* Application must deliver entries sorted.
* Sorted meaning here:
* In major number order
* For each major number, we first need to have the explicit
* entries, then the wild card entries, longest first.
*/
for (i = 1; i < nitems; i++) {
char *tmp;
/* Another default major, string too long or too many ``*'' */
if (curmaj == DEVPOLICY_DFLT_MAJ ||
return (EINVAL);
}
nmaj++;
return (EINVAL);
}
}
if (AU_AUDITING())
/*
* Parse the policy. We create an array for all major numbers
* and in each major number bucket we'll have a linked list of
* entries. Each item may contain either a lo,hi minor pair
*/
if (nmaj > 0)
/*
* We want to lock out concurrent updates but we don't want to
* lock out device opens while we still need to allocate memory.
* As soon as we allocate new devplcy_t's we commit to the next
* generation number, so we must lock out other updates from here.
*/
/* New default and NULL policy */
} else {
}
j = -1;
/* Userland made sure sorting was ok */
for (i = 1; i < nitems; i++) {
j++;
}
}
/* Done parsing, throw away input */
/* Lock out all devpolicy_find()s */
/* Install the new global data */
/* Force all calls by devpolicy_find() */
devplcy_gen++;
/* Reenable policy finds */
/* Free old stuff */
if (oldcnt != 0) {
for (i = 0; i < oldcnt; i++)
}
return (0);
}
/*
* Get device policy: argument one is a pointer to an integer holding
* the number of items allocated for the 3rd argument; the size argument
* is a revision check between kernel and userland.
*/
int
{
int i;
int ind;
int nitems;
int err = 0;
if (sz != sizeof (devplcysys_t))
return (EINVAL);
return (EFAULT);
if (err != 0) {
return (err);
}
ind = 1;
for (i = 0; i < ntabent; i++) {
else {
}
ind++;
}
}
return (err);
}
/*
* Get device policy by device name.
* This is the implementation of MODGETDEVPOLICYBYNAME
*/
int
{
if (sz != sizeof (devplcysys_t))
return (EINVAL);
return (EINVAL);
return (EINVAL);
}
/* These are the only values of interest */
return (EFAULT);
else
return (0);
}
static void
{
} else {
int priv;
if (priv < 0) {
return;
}
}
}
/*
* Return device privileges by privilege name
* Called by ddi_create_priv_minor_node()
*/
{
return (dp);
}