/* * 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 * or http://www.opensolaris.org/os/licensing. * 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 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #include #include #include #include #include #include extern char *_strdup_null(char *); static struct _dabuff { FILE *_daf; /* pointer into /etc/security/device_allocate */ devalloc_t _interpdevalloc; char _interpdaline[DA_BUFSIZE + 1]; char *_DEVALLOC; } *__dabuff; #define daf (_da->_daf) #define interpdevalloc (_da->_interpdevalloc) #define interpdaline (_da->_interpdaline) #define DEVALLOC_FILE (_da->_DEVALLOC) static devalloc_t *da_interpret(char *); int da_matchname(devalloc_t *, char *); int da_matchtype(devalloc_t *, char *); static int system_labeled = 0; /* * trim_white - * trims off leading and trailing white space from input string. * The leading white space is skipped by moving the pointer forward. * The trailing white space is removed by nulling the white space * characters. * returns pointer to non-white string, else returns NULL if input string * is null or if the resulting string has zero length. */ char * trim_white(char *ptr) { char *tptr; if (ptr == NULL) return (NULL); while (isspace(*ptr)) ptr++; tptr = ptr + strlen(ptr); while (tptr != ptr && isspace(tptr[-1])) --tptr; *tptr = '\0'; if (*ptr == '\0') return (NULL); return (ptr); } /* * pack_white - * trims off multiple occurrences of white space from input string. * returns the number of spaces retained */ int pack_white(char *ptr) { int cnt = 0; char *tptr, ch; if (ptr == NULL) return (0); tptr = ptr; while (isspace(*tptr)) tptr++; for (;;) { while ((ch = *tptr) != '\0' && !isspace(ch)) { *ptr++ = ch; tptr++; } while (isspace(*tptr)) tptr++; if (*tptr == '\0') break; *ptr++ = ' '; cnt++; } *ptr = '\0'; return (cnt); } /* * getdadmline - * reads one device_alloc/device_maps line from stream into buff of len * bytes. Continued lines from stream are concatenated into one line in * buff. Comments are removed from buff. * returns the number of characters in buff, else returns 0 if no * characters are read or an error occurred. */ int getdadmline(char *buff, int len, FILE *stream) { int tmpcnt; int charcnt = 0; int fileerr = 0; int contline = 0; char *cp; char *ccp; do { cp = buff; *cp = '\0'; do { contline = 0; if (fgets(cp, len - charcnt, stream) == NULL) { fileerr = 1; break; } ccp = strchr(cp, '\n'); if (ccp != NULL) { if (ccp != cp && ccp[-1] == '\\') { ccp--; contline = 1; } else contline = 0; *ccp = '\0'; } tmpcnt = strlen(cp); cp += tmpcnt; charcnt += tmpcnt; } while ((contline) || (charcnt == 0)); ccp = strpbrk(buff, "#"); if (ccp != NULL) *ccp = '\0'; charcnt = strlen(buff); } while ((fileerr == 0) && (charcnt == 0)); if (fileerr && !charcnt) return (0); else return (charcnt); } /* * _daalloc - * allocates common buffers and structures. * returns pointer to the new structure, else returns NULL on error. */ static struct _dabuff * _daalloc(void) { struct _dabuff *_da = __dabuff; if (_da == NULL) { _da = (struct _dabuff *)calloc((unsigned)1, (unsigned)sizeof (*__dabuff)); if (_da == NULL) return (NULL); DEVALLOC_FILE = "/etc/security/device_allocate"; daf = NULL; __dabuff = _da; system_labeled = is_system_labeled(); } return (__dabuff); } /* * getdadmfield - * gets individual fields separated by skip in ptr. */ char * getdadmfield(char *ptr, char *skip) { static char *tptr = NULL; char *pend; /* check for a continuing search */ if (ptr == NULL) ptr = tptr; /* check for source end */ if (ptr == NULL || *ptr == '\0') return (NULL); /* find terminator */ pend = strpbrk(ptr, skip); /* terminate and set continuation pointer */ if (pend != NULL) { *pend++ = '\0'; tptr = pend; } else tptr = NULL; /* * trim off any surrounding white space, return what's left */ return (trim_white(ptr)); } /* * setdaent - * rewinds the device_allocate file to the begining. */ void setdaent(void) { struct _dabuff *_da = _daalloc(); if (_da == NULL) return; if (daf == NULL) daf = fopen(DEVALLOC_FILE, "rF"); else rewind(daf); } /* * enddaent - * closes device_allocate file. */ void enddaent(void) { struct _dabuff *_da = _daalloc(); if (_da == NULL) return; if (daf != NULL) { (void) fclose(daf); daf = NULL; } } /* * setdafile - * changes the default device_allocate file to the one specified. * It does not close the previous file. If this is desired, enddaent * should be called prior to setdafile. */ void setdafile(char *file) { struct _dabuff *_da = _daalloc(); if (_da == NULL) return; if (daf != NULL) { (void) fclose(daf); daf = NULL; } DEVALLOC_FILE = file; } void freedaent(devalloc_t *dap) { if (dap == NULL) return; _kva_free(dap->da_devopts); dap->da_devopts = NULL; } /* * getdaon - * checks if device_allocate has string DEVICE_ALLOCATION=ON or * DEVICE_ALLOCATION=OFF string in it. * returns 1 if the string is DEVICE_ALLOCATION=ON, 0 if it is * DEVICE_ALLOCATION=OFF, -1 if neither string present. */ int getdaon() { int is_on = -1; char line1[DA_BUFSIZE + 1]; struct _dabuff *_da = _daalloc(); setdaent(); if ((_da == NULL) || (daf == NULL)) { enddaent(); return (is_on); } while (getdadmline(line1, (int)sizeof (line1), daf) != 0) { if (strncmp(line1, DA_ON_STR, (strlen(DA_ON_STR) - 1)) == 0) { is_on = 1; break; } else if (strncmp(line1, DA_OFF_STR, (strlen(DA_OFF_STR) - 1)) == 0) { is_on = 0; break; } } enddaent(); return (is_on); } /* * getdaent - * When first called, returns a pointer to the first devalloc_t * structure in device_allocate; thereafter, it returns a pointer to the * next devalloc_t structure in the file. Thus, successive calls can be * used to search the entire file. * call to getdaent should be bracketed by setdaent and enddaent. * returns NULL on error. */ devalloc_t * getdaent(void) { char line1[DA_BUFSIZE + 1]; devalloc_t *da; struct _dabuff *_da = _daalloc(); if ((_da == 0) || (daf == NULL)) return (NULL); while (getdadmline(line1, (int)sizeof (line1), daf) != 0) { if ((strncmp(line1, DA_ON_STR, (strlen(DA_ON_STR) - 1)) == 0) || (strncmp(line1, DA_OFF_STR, (strlen(DA_OFF_STR) - 1)) == 0)) continue; if ((da = da_interpret(line1)) == NULL) continue; return (da); } return (NULL); } /* * getdanam * searches from the beginning of device_allocate for the device specified * by its name. * call to getdanam should be bracketed by setdaent and enddaent. * returns pointer to devalloc_t for the device if it is found, else * returns NULL if device not found or in case of error. */ devalloc_t * getdanam(char *name) { char line[DA_BUFSIZE + 1]; devalloc_t *da; struct _dabuff *_da = _daalloc(); if ((name == NULL) || (_da == 0) || (daf == NULL)) return (NULL); while (getdadmline(line, (int)sizeof (line), daf) != 0) { if (strstr(line, name) == NULL) continue; if ((da = da_interpret(line)) == NULL) continue; if (da_matchname(da, name)) { enddaent(); return (da); } freedaent(da); } return (NULL); } /* * getdatype - * searches from the beginning of device_allocate for the device specified * by its type. * call to getdatype should be bracketed by setdaent and enddaent. * returns pointer to devalloc_t for the device if it is found, else * returns NULL if device not found or in case of error. */ devalloc_t * getdatype(char *type) { char line1[DA_BUFSIZE + 1]; devalloc_t *da; struct _dabuff *_da = _daalloc(); if ((type == NULL) || (_da == NULL) || (daf == NULL)) return (NULL); while (getdadmline(line1, (int)sizeof (line1), daf) != 0) { if (strstr(line1, type) == NULL) continue; if ((da = da_interpret(line1)) == NULL) continue; if (da_matchtype(da, type)) return (da); freedaent(da); } return (NULL); } /* * da_matchname - * checks if the specified devalloc_t is for the device specified. * returns 1 if it is, else returns 0. */ int da_matchname(devalloc_t *dap, char *name) { if (dap->da_devname == NULL) return (0); return ((strcmp(dap->da_devname, name) == 0)); } /* * da_matchtype - * checks if the specified devalloc_t is for the device type specified. * returns 1 if match found, else, returns 0. */ int da_matchtype(devalloc_t *da, char *type) { if (da->da_devtype == NULL) return (0); return ((strcmp(da->da_devtype, type) == 0)); } /* * da_match - * calls da_matchname or da_matchdev as appropriate. */ int da_match(devalloc_t *dap, da_args *dargs) { if (dargs->devinfo->devname) return (da_matchname(dap, dargs->devinfo->devname)); else if (dargs->devinfo->devtype) return (da_matchtype(dap, dargs->devinfo->devtype)); return (0); } /* * da_interpret - * parses val and initializes pointers in devalloc_t. * returns pointer to parsed devalloc_t entry, else returns NULL on error. */ static devalloc_t * da_interpret(char *val) { struct _dabuff *_da = _daalloc(); char *opts; int i; kva_t *kvap; kv_t *kvp; if (_da == NULL) return (NULL); (void) strcpy(interpdaline, val); interpdevalloc.da_devname = getdadmfield(interpdaline, KV_DELIMITER); interpdevalloc.da_devtype = getdadmfield(NULL, KV_DELIMITER); opts = getdadmfield(NULL, KV_DELIMITER); (void) getdadmfield(NULL, KV_DELIMITER); /* reserved field */ interpdevalloc.da_devauth = getdadmfield(NULL, KV_DELIMITER); interpdevalloc.da_devexec = getdadmfield(NULL, KV_DELIMITER); interpdevalloc.da_devopts = NULL; if (interpdevalloc.da_devname == NULL || interpdevalloc.da_devtype == NULL) return (NULL); if ((opts != NULL) && (strncmp(opts, DA_RESERVED, strlen(DA_RESERVED)) != 0)) { interpdevalloc.da_devopts = _str2kva(opts, KV_ASSIGN, KV_TOKEN_DELIMIT); } /* remove any extraneous whitespace in the options */ if ((kvap = interpdevalloc.da_devopts) != NULL) { for (i = 0, kvp = kvap->data; i < kvap->length; i++, kvp++) { (void) pack_white(kvp->key); (void) pack_white(kvp->value); } } if (system_labeled) { /* if label range is not defined, use the default range. */ int i = 0, nlen = 0; char *minstr = NULL, *maxstr = NULL; kva_t *nkvap = NULL; kv_t *ndata = NULL, *odata = NULL; if (kvap == NULL) { nlen = 2; /* minlabel, maxlabel */ } else { nlen += kvap->length; if ((minstr = kva_match(kvap, DAOPT_MINLABEL)) == NULL) nlen++; if ((maxstr = kva_match(kvap, DAOPT_MAXLABEL)) == NULL) nlen++; } if ((minstr != NULL) && (maxstr != NULL)) /* * label range provided; we don't need to construct * default range. */ goto out; nkvap = _new_kva(nlen); ndata = nkvap->data; if (kvap != NULL) { for (i = 0; i < kvap->length; i++) { odata = kvap->data; ndata[i].key = _strdup_null(odata[i].key); ndata[i].value = _strdup_null(odata[i].value); nkvap->length++; } } if (minstr == NULL) { ndata[i].key = strdup(DAOPT_MINLABEL); ndata[i].value = strdup(DA_DEFAULT_MIN); nkvap->length++; i++; } if (maxstr == NULL) { ndata[i].key = strdup(DAOPT_MAXLABEL); ndata[i].value = strdup(DA_DEFAULT_MAX); nkvap->length++; } interpdevalloc.da_devopts = nkvap; } out: return (&interpdevalloc); }