/* * Process the ATTRIBUTE command */ static int process_attribute(const char* fn, const int line, const int block_vendor, DICT_ATTR *block_tlv, char **argv, int argc) { int vendor = 0; int value; int type; ATTR_FLAGS flags; if ((argc < 3) || (argc > 4)) { fr_strerror_printf("dict_init: %s[%d]: invalid ATTRIBUTE line", fn, line); return -1; } /* * Validate all entries */ if (!sscanf_i(argv[1], &value)) { fr_strerror_printf("dict_init: %s[%d]: invalid value", fn, line); return -1; } /* * find the type of the attribute. */ type = fr_str2int(type_table, argv[2], -1); if (type < 0) { fr_strerror_printf("dict_init: %s[%d]: invalid type \"%s\"", fn, line, argv[2]); return -1; } /* * Only look up the vendor if the string * is non-empty. */ memset(&flags, 0, sizeof(flags)); if (argc == 4) { char *key, *next, *last; key = argv[3]; do { next = strchr(key, ','); if (next) *(next++) = '\0'; if (strcmp(key, "has_tag") == 0 || strcmp(key, "has_tag=1") == 0) { /* Boolean flag, means this is a tagged attribute */ flags.has_tag = 1; } else if (strncmp(key, "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(key + 8, &last, 0); if (*last) { fr_strerror_printf( "dict_init: %s[%d] invalid option %s", fn, line, key); return -1; } } else if (strncmp(key, "array", 8) == 0) { flags.array = 1; switch (type) { case PW_TYPE_IPADDR: case PW_TYPE_BYTE: case PW_TYPE_SHORT: case PW_TYPE_INTEGER: case PW_TYPE_DATE: break; default: fr_strerror_printf( "dict_init: %s[%d] Only IP addresses can have the \"array\" flag set.", fn, line); return -1; } /* * The only thing is the vendor name, * and it's a known name: allow it. */ } else if ((key == argv[3]) && !next && !block_vendor && ((vendor = dict_vendorbyname(key)) !=0)) { break; } else { fr_strerror_printf( "dict_init: %s[%d]: unknown option \"%s\"", fn, line, key); return -1; } key = next; if (key && !*key) break; } while (key); } if (block_vendor) vendor = block_vendor; /* * Special checks for tags, they make our life much more * difficult. */ if (flags.has_tag) { /* * Only string, octets, and integer can be tagged. */ switch (type) { case PW_TYPE_STRING: case PW_TYPE_INTEGER: break; default: fr_strerror_printf("dict_init: %s[%d]: Attributes of type %s cannot be tagged.", fn, line, fr_int2str(type_table, type, "?Unknown?")); return -1; } } if (type == PW_TYPE_TLV) { flags.has_tlv = 1; } if (block_tlv) { /* * TLV's can be only one octet. */ if ((value <= 0) || (value > 255)) { fr_strerror_printf( "dict_init: %s[%d]: sub-tlv's cannot have value > 255", fn, line); return -1; } if (flags.encrypt != FLAG_ENCRYPT_NONE) { fr_strerror_printf( "dict_init: %s[%d]: sub-tlv's cannot be encrypted", fn, line); return -1; } /* * */ value <<= 8; value |= (block_tlv->attr & 0xffff); flags.is_tlv = 1; } #ifdef WITH_DICTIONARY_WARNINGS /* * Hack to help us discover which vendors have illegal * attributes. */ if (!vendor && (value < 256) && !strstr(fn, "rfc") && !strstr(fn, "illegal")) { fprintf(stderr, "WARNING: Illegal Attribute %s in %s\n", argv[0], fn); } #endif /* * Add it in. */ if (dict_addattr(argv[0], vendor, type, value, flags) < 0) { char buffer[256]; strlcpy(buffer, fr_strerror(), sizeof(buffer)); fr_strerror_printf("dict_init: %s[%d]: %s", fn, line, buffer); return -1; } return 0; }
/* * Create a VALUE_PAIR from an ASCII attribute and value, * where the attribute name is in the form: * * Attr-%d * Vendor-%d-Attr-%d * VendorName-Attr-%d */ static VALUE_PAIR *pairmake_any(const char *attribute, const char *value, int operator) { int attr, vendor; size_t size; const char *p = attribute; char *q; VALUE_PAIR *vp; /* * Unknown attributes MUST be of type 'octets' */ if (value && (strncasecmp(value, "0x", 2) != 0)) { fr_strerror_printf("Unknown attribute \"%s\" requires a hex string, not \"%s\"", attribute, value); return NULL; } vendor = 0; /* * Pull off vendor prefix first. */ if (strncasecmp(p, "Attr-", 5) != 0) { if (strncasecmp(p, "Vendor-", 7) == 0) { vendor = (int) strtol(p + 7, &q, 10); if ((vendor == 0) || (vendor > 65535)) { fr_strerror_printf("Invalid vendor value in attribute name \"%s\"", attribute); return NULL; } p = q; } else { /* must be vendor name */ char buffer[256]; q = strchr(p, '-'); if (!q) { fr_strerror_printf("Invalid vendor name in attribute name \"%s\"", attribute); return NULL; } if ((size_t) (q - p) >= sizeof(buffer)) { fr_strerror_printf("Vendor name too long in attribute name \"%s\"", attribute); return NULL; } memcpy(buffer, p, (q - p)); buffer[q - p] = '\0'; vendor = dict_vendorbyname(buffer); if (!vendor) { fr_strerror_printf("Unknown vendor name in attribute name \"%s\"", attribute); return NULL; } p = q; } if (*p != '-') { fr_strerror_printf("Invalid text following vendor definition in attribute name \"%s\"", attribute); return NULL; } p++; } /* * Attr-%d */ if (strncasecmp(p, "Attr-", 5) != 0) { fr_strerror_printf("Invalid format in attribute name \"%s\"", attribute); return NULL; } attr = strtol(p + 5, &q, 10); /* * Invalid, or trailing text after number. */ if ((attr == 0) || *q) { fr_strerror_printf("Invalid value in attribute name \"%s\"", attribute); return NULL; } /* * Double-check the size of attr. */ if (vendor) { DICT_VENDOR *dv = dict_vendorbyvalue(vendor); if (!dv) { if (attr > 255) { attr_error: fr_strerror_printf("Invalid attribute number in attribute name \"%s\"", attribute); return NULL; } } else switch (dv->type) { case 1: if (attr > 255) goto attr_error; break; case 2: if (attr > 65535) goto attr_error; break; case 4: /* Internal limitations! */ if (attr > 65535) goto attr_error; break; default: fr_strerror_printf("Internal sanity check failed"); return NULL; } } attr |= vendor << 16; /* * We've now parsed the attribute properly, Let's create * it. This next stop also looks the attribute up in the * dictionary, and creates the appropriate type for it. */ if ((vp = paircreate(attr, PW_TYPE_OCTETS)) == NULL) { fr_strerror_printf("out of memory"); return NULL; } vp->operator = (operator == 0) ? T_OP_EQ : operator; if (!value) return vp; size = strlen(value + 2); /* * We may be reading something like Attr-5. i.e. * who-ever wrote the text didn't understand it, but we * do. */ switch (vp->type) { default: if (size == (vp->length * 2)) break; vp->type = PW_TYPE_OCTETS; /* FALL-THROUGH */ case PW_TYPE_OCTETS: case PW_TYPE_ABINARY: vp->length = size >> 1; if (vp->length > sizeof(vp->vp_octets)) { vp->length = sizeof(vp->vp_octets); } break; case PW_TYPE_STRING: vp->length = size >> 1; memset(&vp->vp_strvalue, 0, sizeof(vp->vp_strvalue)); if (vp->length >= sizeof(vp->vp_strvalue)) { vp->length = sizeof(vp->vp_strvalue) - 1; } break; } if (fr_hex2bin(value + 2, vp->vp_octets, size) != vp->length) { fr_strerror_printf("Invalid hex string"); free(vp); return NULL; } /* * Move contents around based on type. This is * to work around the historical use of "lvalue". */ switch (vp->type) { case PW_TYPE_DATE: case PW_TYPE_IPADDR: case PW_TYPE_INTEGER: memcpy(&vp->lvalue, vp->vp_octets, sizeof(vp->lvalue)); vp->vp_strvalue[0] = '\0'; break; default: break; } return vp; }
/* * 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 *p; int line = 0; int vendor; int block_vendor; struct stat statbuf; char *argv[MAX_ARGV]; int argc; DICT_ATTR *da, *block_tlv = NULL; if (strlen(fn) >= sizeof(dirtmp) / 2 || strlen(dir) >= sizeof(dirtmp) / 2) { fr_strerror_printf("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, FR_DIR_SEP)) != 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) { fr_strerror_printf("dict_init: Couldn't open dictionary \"%s\": %s", fn, strerror(errno)); } else { fr_strerror_printf("dict_init: %s[%d]: Couldn't open dictionary \"%s\": %s", src_file, src_line, fn, strerror(errno)); } return -1; } stat(fn, &statbuf); /* fopen() guarantees this will succeed */ if (!S_ISREG(statbuf.st_mode)) { fclose(fp); fr_strerror_printf("dict_init: Dictionary \"%s\" is not a regular file", fn); return -1; } /* * Globally writable dictionaries means that users can control * the server configuration with little difficulty. */ #ifdef S_IWOTH if ((statbuf.st_mode & S_IWOTH) != 0) { fclose(fp); fr_strerror_printf("dict_init: Dictionary \"%s\" is globally writable. Refusing to start due to insecure configuration.", fn); return -1; } #endif dict_stat_add(fn, &statbuf); /* * Seed the random pool with data. */ fr_rand_seed(&statbuf, sizeof(statbuf)); 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'; argc = str2argv(buf, argv, MAX_ARGV); if (argc == 0) continue; if (argc == 1) { fr_strerror_printf( "dict_init: %s[%d] invalid entry", fn, line); fclose(fp); return -1; } /* * Process VALUE lines. */ if (strcasecmp(argv[0], "VALUE") == 0) { if (process_value(fn, line, argv + 1, argc - 1) == -1) { fclose(fp); return -1; } continue; } /* * Perhaps this is an attribute. */ if (strcasecmp(argv[0], "ATTRIBUTE") == 0) { if (process_attribute(fn, line, block_vendor, block_tlv, argv + 1, argc - 1) == -1) { fclose(fp); return -1; } continue; } /* * See if we need to import another dictionary. */ if (strcasecmp(argv[0], "$INCLUDE") == 0) { if (my_dict_init(dir, argv[1], fn, line) < 0) { fclose(fp); return -1; } continue; } /* $INCLUDE */ if (strcasecmp(argv[0], "VALUE-ALIAS") == 0) { if (process_value_alias(fn, line, argv + 1, argc - 1) == -1) { fclose(fp); return -1; } continue; } /* * Process VENDOR lines. */ if (strcasecmp(argv[0], "VENDOR") == 0) { if (process_vendor(fn, line, argv + 1, argc - 1) == -1) { fclose(fp); return -1; } continue; } if (strcasecmp(argv[0], "BEGIN-TLV") == 0) { if (argc != 2) { fr_strerror_printf( "dict_init: %s[%d] invalid BEGIN-TLV entry", fn, line); fclose(fp); return -1; } da = dict_attrbyname(argv[1]); if (!da) { fr_strerror_printf( "dict_init: %s[%d]: unknown attribute %s", fn, line, argv[1]); fclose(fp); return -1; } if (da->type != PW_TYPE_TLV) { fr_strerror_printf( "dict_init: %s[%d]: attribute %s is not of type tlv", fn, line, argv[1]); fclose(fp); return -1; } if (block_tlv) { fr_strerror_printf( "dict_init: %s[%d]: Cannot nest TLVs", fn, line); fclose(fp); return -1; } block_tlv = da; continue; } /* BEGIN-TLV */ if (strcasecmp(argv[0], "END-TLV") == 0) { if (argc != 2) { fr_strerror_printf( "dict_init: %s[%d] invalid END-TLV entry", fn, line); fclose(fp); return -1; } da = dict_attrbyname(argv[1]); if (!da) { fr_strerror_printf( "dict_init: %s[%d]: unknown attribute %s", fn, line, argv[1]); fclose(fp); return -1; } if (da != block_tlv) { fr_strerror_printf( "dict_init: %s[%d]: END-TLV %s does not match any previous BEGIN-TLV", fn, line, argv[1]); fclose(fp); return -1; } block_tlv = NULL; continue; } /* END-VENDOR */ if (strcasecmp(argv[0], "BEGIN-VENDOR") == 0) { if (argc != 2) { fr_strerror_printf( "dict_init: %s[%d] invalid BEGIN-VENDOR entry", fn, line); fclose(fp); return -1; } vendor = dict_vendorbyname(argv[1]); if (!vendor) { fr_strerror_printf( "dict_init: %s[%d]: unknown vendor %s", fn, line, argv[1]); fclose(fp); return -1; } block_vendor = vendor; continue; } /* BEGIN-VENDOR */ if (strcasecmp(argv[0], "END-VENDOR") == 0) { if (argc != 2) { fr_strerror_printf( "dict_init: %s[%d] invalid END-VENDOR entry", fn, line); fclose(fp); return -1; } vendor = dict_vendorbyname(argv[1]); if (!vendor) { fr_strerror_printf( "dict_init: %s[%d]: unknown vendor %s", fn, line, argv[1]); fclose(fp); return -1; } if (vendor != block_vendor) { fr_strerror_printf( "dict_init: %s[%d]: END-VENDOR %s does not match any previous BEGIN-VENDOR", fn, line, argv[1]); fclose(fp); return -1; } block_vendor = 0; continue; } /* END-VENDOR */ /* * Any other string: We don't recognize it. */ fr_strerror_printf("dict_init: %s[%d] invalid keyword \"%s\"", fn, line, argv[0]); fclose(fp); return -1; } fclose(fp); return 0; }