2N/A/*
2N/A * CDDL HEADER START
2N/A *
2N/A * The contents of this file are subject to the terms of the
2N/A * Common Development and Distribution License (the "License").
2N/A * You may not use this file except in compliance with the License.
2N/A *
2N/A * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
2N/A * or http://www.opensolaris.org/os/licensing.
2N/A * See the License for the specific language governing permissions
2N/A * and limitations under the License.
2N/A *
2N/A * When distributing Covered Code, include this CDDL HEADER in each
2N/A * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
2N/A * If applicable, add the following below this CDDL HEADER, with the
2N/A * fields enclosed by brackets "[]" replaced with your own identifying
2N/A * information: Portions Copyright [yyyy] [name of copyright owner]
2N/A *
2N/A * CDDL HEADER END
2N/A */
2N/A/*
2N/A * Copyright (c) 1988, 2011, Oracle and/or its affiliates. All rights reserved.
2N/A */
2N/A
2N/A#include <ctype.h>
2N/A#include <string.h>
2N/A#include <stdlib.h>
2N/A#include <tsol/label.h>
2N/A#include <dev_alloc.h>
2N/A#include <devalloc_impl.h>
2N/A#include <secdb.h>
2N/A
2N/Astatic struct _dabuff {
2N/A FILE *_daf; /* pointer into /etc/security/device_allocate */
2N/A devalloc_t _interpdevalloc;
2N/A char _interpdaline[DA_BUFSIZE + 1];
2N/A char *_DEVALLOC;
2N/A} *__dabuff;
2N/A
2N/A#define daf (_da->_daf)
2N/A#define interpdevalloc (_da->_interpdevalloc)
2N/A#define interpdaline (_da->_interpdaline)
2N/A#define DEVALLOC_FILE (_da->_DEVALLOC)
2N/Astatic devalloc_t *da_interpret(char *);
2N/A
2N/Aint da_matchname(devalloc_t *, char *);
2N/Aint da_matchtype(devalloc_t *, char *);
2N/A
2N/Astatic int system_labeled = 0;
2N/A
2N/A/*
2N/A * trim_white -
2N/A * trims off leading and trailing white space from input string.
2N/A * The leading white space is skipped by moving the pointer forward.
2N/A * The trailing white space is removed by nulling the white space
2N/A * characters.
2N/A * returns pointer to non-white string, else returns NULL if input string
2N/A * is null or if the resulting string has zero length.
2N/A */
2N/Achar *
2N/Atrim_white(char *ptr)
2N/A{
2N/A char *tptr;
2N/A
2N/A if (ptr == NULL)
2N/A return (NULL);
2N/A while (isspace(*ptr))
2N/A ptr++;
2N/A tptr = ptr + strlen(ptr);
2N/A while (tptr != ptr && isspace(tptr[-1]))
2N/A --tptr;
2N/A *tptr = '\0';
2N/A if (*ptr == '\0')
2N/A return (NULL);
2N/A
2N/A return (ptr);
2N/A}
2N/A
2N/A/*
2N/A * pack_white -
2N/A * trims off multiple occurrences of white space from input string.
2N/A * returns the number of spaces retained
2N/A */
2N/Aint
2N/Apack_white(char *ptr)
2N/A{
2N/A int cnt = 0;
2N/A char *tptr, ch;
2N/A
2N/A if (ptr == NULL)
2N/A return (0);
2N/A tptr = ptr;
2N/A while (isspace(*tptr))
2N/A tptr++;
2N/A for (;;) {
2N/A while ((ch = *tptr) != '\0' && !isspace(ch)) {
2N/A *ptr++ = ch;
2N/A tptr++;
2N/A }
2N/A while (isspace(*tptr))
2N/A tptr++;
2N/A if (*tptr == '\0')
2N/A break;
2N/A *ptr++ = ' ';
2N/A cnt++;
2N/A }
2N/A *ptr = '\0';
2N/A
2N/A return (cnt);
2N/A}
2N/A
2N/A/*
2N/A * getdadmline -
2N/A * reads one device_alloc/device_maps line from stream into buff of len
2N/A * bytes. Continued lines from stream are concatenated into one line in
2N/A * buff. Comments are removed from buff.
2N/A * returns the number of characters in buff, else returns 0 if no
2N/A * characters are read or an error occurred.
2N/A */
2N/Aint
2N/Agetdadmline(char *buff, int len, FILE *stream)
2N/A{
2N/A int tmpcnt;
2N/A int charcnt = 0;
2N/A int fileerr = 0;
2N/A int contline = 0;
2N/A char *cp;
2N/A char *ccp;
2N/A
2N/A do {
2N/A cp = buff;
2N/A *cp = NULL;
2N/A do {
2N/A contline = 0;
2N/A if (fgets(cp, len - charcnt, stream) == NULL) {
2N/A fileerr = 1;
2N/A break;
2N/A }
2N/A ccp = strchr(cp, '\n');
2N/A if (ccp != NULL) {
2N/A if (ccp != cp && ccp[-1] == '\\') {
2N/A ccp--;
2N/A contline = 1;
2N/A }
2N/A else
2N/A contline = 0;
2N/A *ccp = NULL;
2N/A }
2N/A tmpcnt = strlen(cp);
2N/A cp += tmpcnt;
2N/A charcnt += tmpcnt;
2N/A } while ((contline) || (charcnt == 0));
2N/A ccp = strpbrk(buff, "#");
2N/A if (ccp != NULL)
2N/A *ccp = NULL;
2N/A charcnt = strlen(buff);
2N/A } while ((fileerr == 0) && (charcnt == 0));
2N/A
2N/A if (fileerr && !charcnt)
2N/A return (0);
2N/A else
2N/A return (charcnt);
2N/A}
2N/A
2N/A/*
2N/A * _daalloc -
2N/A * allocates common buffers and structures.
2N/A * returns pointer to the new structure, else returns NULL on error.
2N/A */
2N/Astatic struct _dabuff *
2N/A_daalloc(void)
2N/A{
2N/A struct _dabuff *_da = __dabuff;
2N/A
2N/A if (_da == NULL) {
2N/A _da = (struct _dabuff *)calloc((unsigned)1,
2N/A (unsigned)sizeof (*__dabuff));
2N/A if (_da == NULL)
2N/A return (NULL);
2N/A DEVALLOC_FILE = "/etc/security/device_allocate";
2N/A daf = NULL;
2N/A __dabuff = _da;
2N/A system_labeled = is_system_labeled();
2N/A }
2N/A
2N/A return (__dabuff);
2N/A}
2N/A
2N/A/*
2N/A * getdadmfield -
2N/A * gets individual fields separated by skip in ptr.
2N/A */
2N/Achar *
2N/Agetdadmfield(char *ptr, char *skip)
2N/A{
2N/A static char *tptr = NULL;
2N/A char *pend;
2N/A
2N/A /* check for a continuing search */
2N/A if (ptr == NULL)
2N/A ptr = tptr;
2N/A /* check for source end */
2N/A if (ptr == NULL || *ptr == '\0')
2N/A return (NULL);
2N/A /* find terminator */
2N/A pend = strpbrk(ptr, skip);
2N/A /* terminate and set continuation pointer */
2N/A if (pend != NULL) {
2N/A *pend++ = '\0';
2N/A tptr = pend;
2N/A } else
2N/A tptr = NULL;
2N/A /*
2N/A * trim off any surrounding white space, return what's left
2N/A */
2N/A
2N/A return (trim_white(ptr));
2N/A}
2N/A
2N/A/*
2N/A * setdaent -
2N/A * rewinds the device_allocate file to the begining.
2N/A */
2N/A
2N/Avoid
2N/Asetdaent(void)
2N/A{
2N/A struct _dabuff *_da = _daalloc();
2N/A
2N/A if (_da == NULL)
2N/A return;
2N/A if (daf == NULL)
2N/A daf = fopen(DEVALLOC_FILE, "rF");
2N/A else
2N/A rewind(daf);
2N/A}
2N/A
2N/A/*
2N/A * enddaent -
2N/A * closes device_allocate file.
2N/A */
2N/A
2N/Avoid
2N/Aenddaent(void)
2N/A{
2N/A struct _dabuff *_da = _daalloc();
2N/A
2N/A if (_da == NULL)
2N/A return;
2N/A if (daf != NULL) {
2N/A (void) fclose(daf);
2N/A daf = NULL;
2N/A }
2N/A}
2N/A
2N/A/*
2N/A * setdafile -
2N/A * changes the default device_allocate file to the one specified.
2N/A * It does not close the previous file. If this is desired, enddaent
2N/A * should be called prior to setdafile.
2N/A */
2N/Avoid
2N/Asetdafile(char *file)
2N/A{
2N/A struct _dabuff *_da = _daalloc();
2N/A
2N/A if (_da == NULL)
2N/A return;
2N/A if (daf != NULL) {
2N/A (void) fclose(daf);
2N/A daf = NULL;
2N/A }
2N/A DEVALLOC_FILE = file;
2N/A}
2N/A
2N/Avoid
2N/Afreedaent(devalloc_t *dap)
2N/A{
2N/A if (dap == NULL)
2N/A return;
2N/A _kva_free(dap->da_devopts);
2N/A dap->da_devopts = NULL;
2N/A}
2N/A
2N/A/*
2N/A * getdaon -
2N/A * checks if device_allocate has string DEVICE_ALLOCATION=ON or
2N/A * DEVICE_ALLOCATION=OFF string in it.
2N/A * returns 1 if the string is DEVICE_ALLOCATION=ON, 0 if it is
2N/A * DEVICE_ALLOCATION=OFF, -1 if neither string present.
2N/A */
2N/Aint
2N/Agetdaon()
2N/A{
2N/A int is_on = -1;
2N/A char line1[DA_BUFSIZE + 1];
2N/A struct _dabuff *_da = _daalloc();
2N/A
2N/A setdaent();
2N/A if ((_da == NULL) || (daf == NULL)) {
2N/A enddaent();
2N/A return (is_on);
2N/A }
2N/A while (getdadmline(line1, (int)sizeof (line1), daf) != 0) {
2N/A if (strncmp(line1, DA_ON_STR, (strlen(DA_ON_STR) - 1)) == 0) {
2N/A is_on = 1;
2N/A break;
2N/A } else if (strncmp(line1, DA_OFF_STR,
2N/A (strlen(DA_OFF_STR) - 1)) == 0) {
2N/A is_on = 0;
2N/A break;
2N/A }
2N/A }
2N/A enddaent();
2N/A
2N/A return (is_on);
2N/A}
2N/A
2N/A/*
2N/A * getdaent -
2N/A * When first called, returns a pointer to the first devalloc_t
2N/A * structure in device_allocate; thereafter, it returns a pointer to the
2N/A * next devalloc_t structure in the file. Thus, successive calls can be
2N/A * used to search the entire file.
2N/A * call to getdaent should be bracketed by setdaent and enddaent.
2N/A * returns NULL on error.
2N/A */
2N/Adevalloc_t *
2N/Agetdaent(void)
2N/A{
2N/A char line1[DA_BUFSIZE + 1];
2N/A devalloc_t *da;
2N/A struct _dabuff *_da = _daalloc();
2N/A
2N/A if ((_da == 0) || (daf == NULL))
2N/A return (NULL);
2N/A
2N/A while (getdadmline(line1, (int)sizeof (line1), daf) != 0) {
2N/A if ((strncmp(line1, DA_ON_STR, (strlen(DA_ON_STR) - 1)) == 0) ||
2N/A (strncmp(line1, DA_OFF_STR, (strlen(DA_OFF_STR) - 1)) == 0))
2N/A continue;
2N/A if ((da = da_interpret(line1)) == NULL)
2N/A continue;
2N/A return (da);
2N/A }
2N/A
2N/A return (NULL);
2N/A}
2N/A
2N/A/*
2N/A * getdanam
2N/A * searches from the beginning of device_allocate for the device specified
2N/A * by its name.
2N/A * call to getdanam should be bracketed by setdaent and enddaent.
2N/A * returns pointer to devalloc_t for the device if it is found, else
2N/A * returns NULL if device not found or in case of error.
2N/A */
2N/Adevalloc_t *
2N/Agetdanam(char *name)
2N/A{
2N/A char line[DA_BUFSIZE + 1];
2N/A devalloc_t *da;
2N/A struct _dabuff *_da = _daalloc();
2N/A
2N/A if ((name == NULL) || (_da == 0) || (daf == NULL))
2N/A return (NULL);
2N/A
2N/A while (getdadmline(line, (int)sizeof (line), daf) != 0) {
2N/A if (strstr(line, name) == NULL)
2N/A continue;
2N/A if ((da = da_interpret(line)) == NULL)
2N/A continue;
2N/A if (da_matchname(da, name)) {
2N/A enddaent();
2N/A return (da);
2N/A }
2N/A freedaent(da);
2N/A }
2N/A
2N/A return (NULL);
2N/A}
2N/A
2N/A/*
2N/A * getdatype -
2N/A * searches from the beginning of device_allocate for the device specified
2N/A * by its type.
2N/A * call to getdatype should be bracketed by setdaent and enddaent.
2N/A * returns pointer to devalloc_t for the device if it is found, else
2N/A * returns NULL if device not found or in case of error.
2N/A */
2N/Adevalloc_t *
2N/Agetdatype(char *type)
2N/A{
2N/A char line1[DA_BUFSIZE + 1];
2N/A devalloc_t *da;
2N/A struct _dabuff *_da = _daalloc();
2N/A
2N/A if ((type == NULL) || (_da == NULL) || (daf == NULL))
2N/A return (NULL);
2N/A
2N/A while (getdadmline(line1, (int)sizeof (line1), daf) != 0) {
2N/A if (strstr(line1, type) == NULL)
2N/A continue;
2N/A if ((da = da_interpret(line1)) == NULL)
2N/A continue;
2N/A if (da_matchtype(da, type))
2N/A return (da);
2N/A freedaent(da);
2N/A }
2N/A
2N/A return (NULL);
2N/A}
2N/A
2N/A/*
2N/A * da_matchname -
2N/A * checks if the specified devalloc_t is for the device specified.
2N/A * returns 1 if it is, else returns 0.
2N/A */
2N/Aint
2N/Ada_matchname(devalloc_t *dap, char *name)
2N/A{
2N/A if (dap->da_devname == NULL)
2N/A return (0);
2N/A
2N/A return ((strcmp(dap->da_devname, name) == 0));
2N/A}
2N/A
2N/A/*
2N/A * da_matchtype -
2N/A * checks if the specified devalloc_t is for the device type specified.
2N/A * returns 1 if match found, else, returns 0.
2N/A */
2N/Aint
2N/Ada_matchtype(devalloc_t *da, char *type)
2N/A{
2N/A if (da->da_devtype == NULL)
2N/A return (0);
2N/A
2N/A return ((strcmp(da->da_devtype, type) == 0));
2N/A}
2N/A
2N/A/*
2N/A * da_match -
2N/A * calls da_matchname or da_matchdev as appropriate.
2N/A */
2N/Aint
2N/Ada_match(devalloc_t *dap, da_args *dargs)
2N/A{
2N/A if (dargs->devinfo->devname)
2N/A return (da_matchname(dap, dargs->devinfo->devname));
2N/A else if (dargs->devinfo->devtype)
2N/A return (da_matchtype(dap, dargs->devinfo->devtype));
2N/A
2N/A return (0);
2N/A}
2N/A
2N/A/*
2N/A * da_interpret -
2N/A * parses val and initializes pointers in devalloc_t.
2N/A * returns pointer to parsed devalloc_t entry, else returns NULL on error.
2N/A */
2N/Astatic devalloc_t *
2N/Ada_interpret(char *val)
2N/A{
2N/A struct _dabuff *_da = _daalloc();
2N/A char *opts;
2N/A int i;
2N/A kva_t *kvap;
2N/A kv_t *kvp;
2N/A
2N/A if (_da == NULL)
2N/A return (NULL);
2N/A
2N/A (void) strcpy(interpdaline, val);
2N/A interpdevalloc.da_devname = getdadmfield(interpdaline, KV_DELIMITER);
2N/A interpdevalloc.da_devtype = getdadmfield(NULL, KV_DELIMITER);
2N/A opts = getdadmfield(NULL, KV_DELIMITER);
2N/A (void) getdadmfield(NULL, KV_DELIMITER); /* reserved field */
2N/A interpdevalloc.da_devauth = getdadmfield(NULL, KV_DELIMITER);
2N/A interpdevalloc.da_devexec = getdadmfield(NULL, KV_DELIMITER);
2N/A interpdevalloc.da_devopts = NULL;
2N/A if (interpdevalloc.da_devname == NULL ||
2N/A interpdevalloc.da_devtype == NULL)
2N/A return (NULL);
2N/A if ((opts != NULL) &&
2N/A (strncmp(opts, DA_RESERVED, strlen(DA_RESERVED)) != 0)) {
2N/A interpdevalloc.da_devopts =
2N/A _str2kva(opts, KV_ASSIGN, KV_TOKEN_DELIMIT);
2N/A }
2N/A /* remove any extraneous whitespace in the options */
2N/A if ((kvap = interpdevalloc.da_devopts) != NULL) {
2N/A for (i = 0, kvp = kvap->data; i < kvap->length; i++, kvp++) {
2N/A (void) pack_white(kvp->key);
2N/A (void) pack_white(kvp->value);
2N/A }
2N/A }
2N/A
2N/A if (system_labeled) {
2N/A /* if label range is not defined, use the default range. */
2N/A int i = 0, nlen = 0;
2N/A char *minstr = NULL, *maxstr = NULL;
2N/A kva_t *nkvap = NULL;
2N/A kv_t *ndata = NULL, *odata = NULL;
2N/A
2N/A if (kvap == NULL) {
2N/A nlen = 2; /* minlabel, maxlabel */
2N/A } else {
2N/A nlen += kvap->length;
2N/A if ((minstr = kva_match(kvap, DAOPT_MINLABEL)) == NULL)
2N/A nlen++;
2N/A if ((maxstr = kva_match(kvap, DAOPT_MAXLABEL)) == NULL)
2N/A nlen++;
2N/A }
2N/A if ((minstr != NULL) && (maxstr != NULL))
2N/A /*
2N/A * label range provided; we don't need to construct
2N/A * default range.
2N/A */
2N/A goto out;
2N/A nkvap = _new_kva(nlen);
2N/A ndata = nkvap->data;
2N/A if (kvap != NULL) {
2N/A for (i = 0; i < kvap->length; i++) {
2N/A odata = kvap->data;
2N/A ndata[i].key = _strdup_null(odata[i].key);
2N/A ndata[i].value = _strdup_null(odata[i].value);
2N/A nkvap->length++;
2N/A }
2N/A }
2N/A if (minstr == NULL) {
2N/A ndata[i].key = strdup(DAOPT_MINLABEL);
2N/A ndata[i].value = strdup(DA_DEFAULT_MIN);
2N/A nkvap->length++;
2N/A i++;
2N/A }
2N/A if (maxstr == NULL) {
2N/A ndata[i].key = strdup(DAOPT_MAXLABEL);
2N/A ndata[i].value = strdup(DA_DEFAULT_MAX);
2N/A nkvap->length++;
2N/A }
2N/A interpdevalloc.da_devopts = nkvap;
2N/A }
2N/A
2N/Aout:
2N/A return (&interpdevalloc);
2N/A}