/* * dict.c Routines to read the dictionary file. * * Version: $Id: dict.c,v 1.39 2003/02/19 15:10:20 cmiller Exp $ * */ static const char rcsid[] = "$Id: dict.c,v 1.39 2003/02/19 15:10:20 cmiller Exp $"; #include "autoconf.h" #include #include #include #if HAVE_MALLOC_H #include #endif #include "libradius.h" #include "missing.h" static DICT_ATTR *dictionary_attributes = NULL; static DICT_VALUE *dictionary_values = NULL; static DICT_VENDOR *dictionary_vendors = NULL; static const LRAD_NAME_NUMBER type_table[] = { { "string", PW_TYPE_STRING }, { "integer", PW_TYPE_INTEGER }, { "ipaddr", PW_TYPE_IPADDR }, { "date", PW_TYPE_DATE }, { "abinary", PW_TYPE_ABINARY }, { "octets", PW_TYPE_OCTETS }, { NULL, 0 } }; /* * Quick pointers to the base 0..255 attributes. * * These attributes are referenced a LOT, especially during * decoding of the on-the-wire packets. It's useful to keep a * cache of their dictionary entries, so looking them up is * O(1), instead of O(N). (N==number of dictionary entries...) */ static DICT_ATTR *base_attributes[256]; /* * Free the dictionary_attributes and dictionary_values lists. */ static void dict_free(void) { DICT_ATTR *dattr, *anext; DICT_VALUE *dval, *vnext; DICT_VENDOR *dvend, *enext; for (dattr = dictionary_attributes; dattr; dattr = anext) { anext = dattr->next; free(dattr); } for (dval = dictionary_values; dval; dval = vnext) { vnext = dval->next; free(dval); } for (dvend = dictionary_vendors; dvend; dvend = enext) { enext = dvend->next; free(dvend); } dictionary_attributes = NULL; dictionary_values = NULL; dictionary_vendors = NULL; memset(base_attributes, 0, sizeof(base_attributes)); } /* * Add vendor to the list. */ int dict_addvendor(const char *name, int value) { DICT_VENDOR *vval; if (value >= (1 << 16)) { librad_log("dict_addvendor: Cannot handle vendor ID larger than 65535"); return -1; } if (strlen(name) > (sizeof(vval->vendorname) -1)) { librad_log("dict_addvendor: vendor name too long"); return -1; } if ((vval =(DICT_VENDOR *)malloc(sizeof(DICT_VENDOR))) == NULL) { librad_log("dict_addvendor: out of memory"); return -1; } strcpy(vval->vendorname, name); vval->vendorpec = value; /* Insert at front. */ vval->next = dictionary_vendors; dictionary_vendors = vval; return 0; } int dict_addattr(const char *name, int vendor, int type, int value, ATTR_FLAGS flags) { static int max_attr = 0; DICT_ATTR *attr; if (strlen(name) > (sizeof(attr->name) -1)) { librad_log("dict_addattr: attribute name too long"); return -1; } /* * If the value is '-1', that means use a pre-existing * one (if it already exists). If one does NOT already exist, * then create a new attribute, with a non-conflicting value, * and use that. */ if (value == -1) { attr = dict_attrbyname(name); if (attr != NULL) { return 0; /* exists, don't add it again */ } value = ++max_attr; } else if (vendor == 0) { /* * Update 'max_attr' */ if (value > max_attr) { max_attr = value; } } /* * Create a new attribute for the list */ if ((attr = (DICT_ATTR *)malloc(sizeof(DICT_ATTR))) == NULL) { librad_log("dict_addattr: out of memory"); return -1; } strcpy(attr->name, name); attr->attr = value; attr->type = type; attr->flags = flags; if (vendor) { attr->attr |= (vendor << 16); } else if ((attr->attr >= 0) && (attr->attr < 256)) { /* * If it's an on-the-wire base attribute, * then keep a quick reference to it, for speed. */ base_attributes[attr->attr] = attr; } /* * Add to the front of the list, so that * values at the end of the file override * those in the begin. */ attr->next = dictionary_attributes; dictionary_attributes = attr; return 0; } int dict_addvalue(const char *namestr, char *attrstr, int value) { DICT_ATTR *dattr; DICT_VALUE *dval; if (strlen(namestr) > (sizeof(dval->name) -1)) { librad_log("dict_addvalue: value name too long"); return -1; } if (strlen(attrstr) > (sizeof(dval->attrname) -1)) { librad_log("dict_addvalue: attribute name too long"); return -1; } if ((dval = (DICT_VALUE *)malloc(sizeof(DICT_VALUE))) == NULL) { librad_log("dict_addvalue: out of memory"); return -1; } strcpy(dval->name, namestr); strcpy(dval->attrname, attrstr); /* * Remember which attribute is associated with this * value, if possible. */ dattr = dict_attrbyname(attrstr); if (dattr) { dval->attr = dattr->attr; } else { dval->attr = 0; } dval->value = value; /* Insert at front. */ dval->next = dictionary_values; dictionary_values = dval; return 0; } /* * Process the ATTRIBUTE command */ static int process_attribute(const char* fn, const int line, const int block_vendor, const char* data) { int vendor; char namestr[256]; char valstr[256]; char typestr[256]; char optstr[256]; int value; int type; char *s, *c; ATTR_FLAGS flags; vendor = 0; optstr[0] = 0; if(sscanf(data, "%s%s%s%s", namestr, valstr, typestr, optstr) < 3) { librad_log("dict_init: %s[%d]: invalid ATTRIBUTE line", fn, line); return -1; } /* * Validate all entries */ if (!isdigit((int) *valstr)) { librad_log("dict_init: %s[%d]: invalid value", fn, line); return -1; } if (valstr[0] != '0') value = atoi(valstr); else sscanf(valstr, "%i", &value); /* * find the type of the attribute. */ type = lrad_str2int(type_table, typestr, -1); if (type < 0) { librad_log("dict_init: %s[%d]: invalid type \"%s\"", fn, line, typestr); return -1; } /* * Ignore comments */ if (optstr[0] == '#') optstr[0] = '\0'; /* * Only look up the vendor if the string * is non-empty. */ memset(&flags, 0, sizeof(flags)); s = strtok(optstr, ","); while(s) { if (strcmp(s, "has_tag") == 0 || strcmp(s, "has_tag=1") == 0) { /* Boolean flag, means this is a tagged attribute */ flags.has_tag = 1; } else if (strncmp(s, "len+=", 5) == 0 || strncmp(s, "len-=", 5) == 0) { /* Length difference, to accomodate braindead NASes & their vendors */ flags.len_disp = strtol(s + 5, &c, 0); if (*c) { librad_log("dict_init: %s[%d] invalid option %s", fn, line, s); return -1; } if (s[3] == '-') { flags.len_disp = -flags.len_disp; } } else if (strncmp(s, "encrypt=", 8) == 0) { /* Encryption method, defaults to 0 (none). Currently valid is just type 2, Tunnel-Password style, which can only be applied to strings. */ flags.encrypt = strtol(s + 8, &c, 0); if (*c) { librad_log( "dict_init: %s[%d] invalid option %s", fn, line, s); return -1; } } else { /* Must be a vendor 'flag'... */ if (strncmp(s, "vendor=", 5) == 0) { /* New format */ s += 5; } vendor = dict_vendorname(s); if (!vendor) { librad_log( "dict_init: %s[%d]: unknown vendor %s", fn, line, optstr); return -1; } if (block_vendor && optstr[0] && (block_vendor != vendor)) { librad_log("dict_init: %s[%d]: mismatched vendor %s within BEGIN-VENDOR/END-VENDOR block", fn, line, optstr); return -1; } } s = strtok(NULL, ","); } if (block_vendor) vendor = block_vendor; if (dict_addattr(namestr, vendor, type, value, flags) < 0) { librad_log("dict_init: %s[%d]: %s", fn, line, librad_errstr); return -1; } return 0; } /* * Process the VALUE command */ static int process_value(const char* fn, const int line, const int block_vendor, const char* data) { char namestr[256]; char valstr[256]; char attrstr[256]; int value; if (sscanf(data, "%s%s%s", attrstr, namestr, valstr) != 3) { librad_log("dict_init: %s[%d]: invalid VALUE line", fn, line); return -1; } /* * For Compatibility, skip "Server-Config" */ if (strcasecmp(attrstr, "Server-Config") == 0) return 0; /* * Validate all entries */ if (!isdigit((int) *valstr)) { librad_log("dict_init: %s[%d]: invalid value", fn, line); return -1; } if (valstr[0] != '0') value = atoi(valstr); else sscanf(valstr, "%i", &value); if (dict_addvalue(namestr, attrstr, value) < 0) { librad_log("dict_init: %s[%d]: %s", fn, line, librad_errstr); return -1; } return 0; } /* * Process the VENDOR command */ static int process_vendor(const char* fn, const int line, const int block_vendor, const char* data) { char valstr[256]; char attrstr[256]; int value; if (sscanf(data, "%s%s", attrstr, valstr) != 2) { librad_log( "dict_init: %s[%d] invalid VENDOR entry", fn, line); return -1; } /* * Validate all entries */ if (!isdigit((int) *valstr)) { librad_log("dict_init: %s[%d]: invalid value", fn, line); return -1; } value = atoi(valstr); /* Create a new VENDOR entry for the list */ if (dict_addvendor(attrstr, value) < 0) { librad_log("dict_init: %s[%d]: %s", fn, line, librad_errstr); return -1; } return 0; } /* * Initialize the dictionary. */ static int my_dict_init(const char *dir, const char *fn, const char *src_file, int src_line) { FILE *fp; char dirtmp[256]; char buf[256]; char optstr[256]; char *p; char *keyword; char *data; int line = 0; int vendor; int block_vendor; if (strlen(fn) >= sizeof(dirtmp) / 2 || strlen(dir) >= sizeof(dirtmp) / 2) { librad_log("dict_init: filename name too long"); return -1; } /* * First see if fn is relative to dir. If so, create * new filename. If not, remember the absolute dir. */ if ((p = strrchr(fn, '/')) != NULL) { strcpy(dirtmp, fn); dirtmp[p - fn] = 0; dir = dirtmp; } else if (dir && dir[0] && strcmp(dir, ".") != 0) { snprintf(dirtmp, sizeof(dirtmp), "%s/%s", dir, fn); fn = dirtmp; } if ((fp = fopen(fn, "r")) == NULL) { if (!src_file) { librad_log("dict_init: Couldn't open dictionary \"%s\": %s", fn, strerror(errno)); } else { librad_log("dict_init: %s[%d]: Couldn't open dictionary \"%s\": %s", src_file, src_line, fn, strerror(errno)); } return -1; } block_vendor = 0; while (fgets(buf, sizeof(buf), fp) != NULL) { line++; if (buf[0] == '#' || buf[0] == 0 || buf[0] == '\n' || buf[0] == '\r') continue; /* * Comment characters should NOT be appearing anywhere but * as start of a comment; */ p = strchr(buf, '#'); if (p) *p = '\0'; keyword = strtok(buf, " \t\r\n"); if (keyword == NULL) { continue; } data = strtok(NULL, "\r\n"); if (data == NULL || data[0] == 0) { librad_log("dict_init: %s[%d]: invalid entry for keyword %s", fn, line, keyword); fclose(fp); return -1; } /* * See if we need to import another dictionary. */ if (strcasecmp(keyword, "$INCLUDE") == 0) { p = strtok(data, " \t"); if (my_dict_init(dir, data, fn, line) < 0) { fclose(fp); return -1; } continue; } /* $INCLUDE */ /* * Perhaps this is an attribute. */ if (strcasecmp(keyword, "ATTRIBUTE") == 0) { if (process_attribute(fn, line, block_vendor, data) == -1) { fclose(fp); return -1; } continue; } /* * Process VALUE lines. */ if (strcasecmp(keyword, "VALUE") == 0) { if (process_value(fn, line, block_vendor, data) == -1) { fclose(fp); return -1; } continue; } /* * Process VENDOR lines. */ if (strcasecmp(keyword, "VENDOR") == 0) { if (process_vendor(fn, line, block_vendor, data) == -1) { fclose(fp); return -1; } continue; } if (strcasecmp(keyword, "BEGIN-VENDOR") == 0) { optstr[0] = 0; if (sscanf(data, "%s", optstr) != 1) { librad_log( "dict_init: %s[%d] invalid BEGIN-VENDOR entry", fn, line); fclose(fp); return -1; } vendor = dict_vendorname(optstr); if (!vendor) { librad_log( "dict_init: %s[%d]: unknown vendor %s", fn, line, optstr); fclose(fp); return -1; } block_vendor = vendor; continue; } /* BEGIN-VENDOR */ if (strcasecmp(keyword, "END-VENDOR") == 0) { optstr[0] = 0; if (sscanf(data, "%s", optstr) != 1) { librad_log( "dict_init: %s[%d] invalid END-VENDOR entry", fn, line); fclose(fp); return -1; } vendor = dict_vendorname(optstr); if (!vendor) { librad_log( "dict_init: %s[%d]: unknown vendor %s", fn, line, optstr); fclose(fp); return -1; } if (vendor != block_vendor) { librad_log( "dict_init: %s[%d]: END-VENDOR %s does not match any previous BEGIN-VENDOR", fn, line, optstr); fclose(fp); return -1; } block_vendor = 0; continue; } /* END-VENDOR */ /* * Any other string: We don't recognize it. */ librad_log( "dict_init: %s[%d] invalid keyword \"%s\"", fn, line, keyword); fclose(fp); return -1; } fclose(fp); return 0; } /* * Initialize the directory, then fix the attr member of * all attributes. */ int dict_init(const char *dir, const char *fn) { DICT_ATTR *attr; DICT_VALUE *dval; dict_free(); if (my_dict_init(dir, fn, NULL, 0) < 0) return -1; for (dval = dictionary_values; dval; dval = dval->next) { if (dval->attr != 0) continue; if ((attr = dict_attrbyname(dval->attrname)) == NULL) { librad_log("dict_init: VALUE %s for unknown ATTRIBUTE %s", dval->name, dval->attrname); return -1; } dval->attr = attr->attr; } return 0; } /* * Get an attribute by its numerical value. */ DICT_ATTR * dict_attrbyvalue(int val) { DICT_ATTR *a; /* * If it's an on-the-wire base attribute, return * the cached value for it. */ if ((val >= 0) && (val < 256)) { return base_attributes[val]; } for (a = dictionary_attributes; a; a = a->next) { if (a->attr == val) return a; } return NULL; } /* * Get an attribute by its name. */ DICT_ATTR * dict_attrbyname(const char *name) { DICT_ATTR *a; for (a = dictionary_attributes; a; a = a->next) { if (strcasecmp(a->name, name) == 0) return a; } return NULL; } /* * Associate a value with an attribute and return it. */ DICT_VALUE * dict_valbyattr(int attr, int val) { DICT_VALUE *v; for (v = dictionary_values; v; v = v->next) { if (v->attr == attr && v->value == val) return v; } return NULL; } /* * Get a value by its name. * If you pass an actual attr, it will try to match it. * If you just want it to return on the first match, * send it 0 as the attr. I hope this works the way it * seems to. :) --kph */ DICT_VALUE * dict_valbyname(int attr, const char *name) { DICT_VALUE *v; for (v = dictionary_values; v; v = v->next) { if ((attr == 0 || v->attr == attr) && strcasecmp(v->name, name) == 0) return v; } return NULL; } /* * Get the vendor PEC based on the vendor name */ int dict_vendorname(const char *name) { DICT_VENDOR *v; /* * Find the vendor, if any. */ for (v = dictionary_vendors; v; v = v->next) { if (strcasecmp(name, v->vendorname) == 0) { return v->vendorpec; } } return 0; }