static int getusersfile(TALLOC_CTX *ctx, char const *filename, fr_hash_table_t **pht, char *compat_mode_str) { int rcode; PAIR_LIST *users = NULL; PAIR_LIST *entry, *next; fr_hash_table_t *ht, *tailht; int order = 0; if (!filename) { *pht = NULL; return 0; } rcode = pairlist_read(ctx, filename, &users, 1); if (rcode < 0) { return -1; } /* * Walk through the 'users' file list, if we're debugging, * or if we're in compat_mode. */ if ((debug_flag) || (strcmp(compat_mode_str, "cistron") == 0)) { VALUE_PAIR *vp; int compat_mode = false; if (strcmp(compat_mode_str, "cistron") == 0) { compat_mode = true; } entry = users; while (entry) { vp_cursor_t cursor; if (compat_mode) { DEBUG("[%s]:%d Cistron compatibility checks for entry %s ...", filename, entry->lineno, entry->name); } /* * Look for improper use of '=' in the * check items. They should be using * '==' for on-the-wire RADIUS attributes, * and probably ':=' for server * configuration items. */ for (vp = paircursor(&cursor, &entry->check); vp; vp = pairnext(&cursor)) { /* * Ignore attributes which are set * properly. */ if (vp->op != T_OP_EQ) { continue; } /* * If it's a vendor attribute, * or it's a wire protocol, * ensure it has '=='. */ if ((vp->da->vendor != 0) || (vp->da->attr < 0x100)) { if (!compat_mode) { WDEBUG("[%s]:%d Changing '%s =' to '%s =='\n\tfor comparing RADIUS attribute in check item list for user %s", filename, entry->lineno, vp->da->name, vp->da->name, entry->name); } else { DEBUG("\tChanging '%s =' to '%s =='", vp->da->name, vp->da->name); } vp->op = T_OP_CMP_EQ; continue; } /* * Cistron Compatibility mode. * * Re-write selected attributes * to be '+=', instead of '='. * * All others get set to '==' */ if (compat_mode) { /* * Non-wire attributes become += * * On the write attributes * become == */ if ((vp->da->attr >= 0x100) && (vp->da->attr <= 0xffff) && (vp->da->attr != PW_HINT) && (vp->da->attr != PW_HUNTGROUP_NAME)) { DEBUG("\tChanging '%s =' to '%s +='", vp->da->name, vp->da->name); vp->op = T_OP_ADD; } else { DEBUG("\tChanging '%s =' to '%s =='", vp->da->name, vp->da->name); vp->op = T_OP_CMP_EQ; } } } /* end of loop over check items */ /* * Look for server configuration items * in the reply list. * * It's a common enough mistake, that it's * worth doing. */ for (vp = paircursor(&cursor, &entry->reply); vp; vp = pairnext(&cursor)) { /* * If it's NOT a vendor attribute, * and it's NOT a wire protocol * and we ignore Fall-Through, * then bitch about it, giving a * good warning message. */ if ((vp->da->vendor == 0) && (vp->da->attr > 0xff) && (vp->da->attr > 1000)) { WDEBUG("[%s]:%d Check item \"%s\"\n" "\tfound in reply item list for user \"%s\".\n" "\tThis attribute MUST go on the first line" " with the other check items", filename, entry->lineno, vp->da->name, entry->name); } } entry = entry->next; } } ht = fr_hash_table_create(pairlist_hash, pairlist_cmp, my_pairlist_free); if (!ht) { pairlist_free(&users); return -1; } tailht = fr_hash_table_create(pairlist_hash, pairlist_cmp, NULL); if (!tailht) { fr_hash_table_free(ht); pairlist_free(&users); return -1; } /* * Now that we've read it in, put the entries into a hash * for faster access. */ for (entry = users; entry != NULL; entry = next) { PAIR_LIST *tail; next = entry->next; entry->next = NULL; entry->order = order++; /* * Insert it into the hash table, and remember * the tail of the linked list. */ tail = fr_hash_table_finddata(tailht, entry); if (!tail) { /* * Insert it into the head & tail. */ if (!fr_hash_table_insert(ht, entry) || !fr_hash_table_insert(tailht, entry)) { pairlist_free(&next); fr_hash_table_free(ht); fr_hash_table_free(tailht); return -1; } } else { tail->next = entry; if (!fr_hash_table_replace(tailht, entry)) { pairlist_free(&next); fr_hash_table_free(ht); fr_hash_table_free(tailht); return -1; } } } fr_hash_table_free(tailht); *pht = ht; return 0; }
/* * Add a value for an attribute to the dictionary. */ int dict_addvalue(const char *namestr, const char *attrstr, int value) { size_t length; DICT_ATTR *dattr; DICT_VALUE *dval; static DICT_ATTR *last_attr = NULL; if (!*namestr) { fr_strerror_printf("dict_addvalue: empty names are not permitted"); return -1; } if ((length = strlen(namestr)) >= DICT_VALUE_MAX_NAME_LEN) { fr_strerror_printf("dict_addvalue: value name too long"); return -1; } if ((dval = fr_pool_alloc(sizeof(*dval) + length)) == NULL) { fr_strerror_printf("dict_addvalue: out of memory"); return -1; } memset(dval, 0, sizeof(*dval)); strcpy(dval->name, namestr); dval->value = value; /* * Most VALUEs are bunched together by ATTRIBUTE. We can * save a lot of lookups on dictionary initialization by * caching the last attribute. */ if (last_attr && (strcasecmp(attrstr, last_attr->name) == 0)) { dattr = last_attr; } else { dattr = dict_attrbyname(attrstr); last_attr = dattr; } /* * Remember which attribute is associated with this * value, if possible. */ if (dattr) { if (dattr->flags.has_value_alias) { fr_strerror_printf("dict_addvalue: Cannot add VALUE for ATTRIBUTE \"%s\": It already has a VALUE-ALIAS", attrstr); return -1; } dval->attr = dattr->attr; /* * Enforce valid values * * Don't worry about fixups... */ switch (dattr->type) { case PW_TYPE_BYTE: if (value > 255) { fr_pool_free(dval); fr_strerror_printf("dict_addvalue: ATTRIBUTEs of type 'byte' cannot have VALUEs larger than 255"); return -1; } break; case PW_TYPE_SHORT: if (value > 65535) { fr_pool_free(dval); fr_strerror_printf("dict_addvalue: ATTRIBUTEs of type 'short' cannot have VALUEs larger than 65535"); return -1; } break; /* * Allow octets for now, because * of dictionary.cablelabs */ case PW_TYPE_OCTETS: case PW_TYPE_INTEGER: break; default: fr_pool_free(dval); fr_strerror_printf("dict_addvalue: VALUEs cannot be defined for attributes of type '%s'", fr_int2str(type_table, dattr->type, "?Unknown?")); return -1; } dattr->flags.has_value = 1; } else { value_fixup_t *fixup; fixup = (value_fixup_t *) malloc(sizeof(*fixup)); if (!fixup) { fr_pool_free(dval); fr_strerror_printf("dict_addvalue: out of memory"); return -1; } memset(fixup, 0, sizeof(*fixup)); strlcpy(fixup->attrstr, attrstr, sizeof(fixup->attrstr)); fixup->dval = dval; /* * Insert to the head of the list. */ fixup->next = value_fixup; value_fixup = fixup; return 0; } /* * Add the value into the dictionary. */ if (!fr_hash_table_insert(values_byname, dval)) { if (dattr) { DICT_VALUE *old; /* * Suppress duplicates with the same * name and value. There are lots in * dictionary.ascend. */ old = dict_valbyname(dattr->attr, namestr); if (old && (old->value == dval->value)) { fr_pool_free(dval); return 0; } } fr_pool_free(dval); fr_strerror_printf("dict_addvalue: Duplicate value name %s for attribute %s", namestr, attrstr); return -1; } /* * There are multiple VALUE's, keyed by attribute, so we * take care of that here. */ if (!fr_hash_table_replace(values_byvalue, dval)) { fr_strerror_printf("dict_addvalue: Failed inserting value %s", namestr); return -1; } return 0; }
/* * Add vendor to the list. */ int dict_addvendor(const char *name, int value) { size_t length; DICT_VENDOR *dv; if (value > 65535) { fr_strerror_printf("dict_addvendor: Cannot handle vendor ID larger than 65535"); return -1; } if ((length = strlen(name)) >= DICT_VENDOR_MAX_NAME_LEN) { fr_strerror_printf("dict_addvendor: vendor name too long"); return -1; } if ((dv = fr_pool_alloc(sizeof(*dv) + length)) == NULL) { fr_strerror_printf("dict_addvendor: out of memory"); return -1; } strcpy(dv->name, name); dv->vendorpec = value; dv->type = dv->length = 1; /* defaults */ if (!fr_hash_table_insert(vendors_byname, dv)) { DICT_VENDOR *old_dv; old_dv = fr_hash_table_finddata(vendors_byname, dv); if (!old_dv) { fr_strerror_printf("dict_addvendor: Failed inserting vendor name %s", name); return -1; } if (old_dv->vendorpec != dv->vendorpec) { fr_strerror_printf("dict_addvendor: Duplicate vendor name %s", name); return -1; } /* * Already inserted. Discard the duplicate entry. */ fr_pool_free(dv); return 0; } /* * Insert the SAME pointer (not free'd when this table is * deleted), into another table. * * We want this behaviour because we want OLD names for * the attributes to be read from the configuration * files, but when we're printing them, (and looking up * by value) we want to use the NEW name. */ if (!fr_hash_table_replace(vendors_byvalue, dv)) { fr_strerror_printf("dict_addvendor: Failed inserting vendor %s", name); return -1; } return 0; }
/* * Add an attribute to the dictionary. */ int dict_addattr(const char *name, int vendor, int type, int value, ATTR_FLAGS flags) { size_t namelen; static int max_attr = 0; DICT_ATTR *attr; namelen = strlen(name); if (namelen >= DICT_ATTR_MAX_NAME_LEN) { fr_strerror_printf("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) { if (dict_attrbyname(name)) { 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; } } if (value < 0) { fr_strerror_printf("dict_addattr: ATTRIBUTE has invalid number (less than zero)"); return -1; } if (value >= 65536) { fr_strerror_printf("dict_addattr: ATTRIBUTE has invalid number (larger than 65535)."); return -1; } if (vendor) { DICT_VENDOR *dv; static DICT_VENDOR *last_vendor = NULL; if (flags.is_tlv && (flags.encrypt != FLAG_ENCRYPT_NONE)) { fr_strerror_printf("Sub-TLV's cannot be encrypted"); return -1; } if (flags.has_tlv && (flags.encrypt != FLAG_ENCRYPT_NONE)) { fr_strerror_printf("TLV's cannot be encrypted"); return -1; } if (flags.is_tlv && flags.has_tag) { fr_strerror_printf("Sub-TLV's cannot have a tag"); return -1; } if (flags.has_tlv && flags.has_tag) { fr_strerror_printf("TLV's cannot have a tag"); return -1; } /* * Most ATTRIBUTEs are bunched together by * VENDOR. We can save a lot of lookups on * dictionary initialization by caching the last * vendor. */ if (last_vendor && (vendor == last_vendor->vendorpec)) { dv = last_vendor; } else { dv = dict_vendorbyvalue(vendor); last_vendor = dv; } /* * If the vendor isn't defined, die. */ if (!dv) { fr_strerror_printf("dict_addattr: Unknown vendor"); return -1; } /* * FIXME: Switch over dv->type, and limit things * properly. */ if ((dv->type == 1) && (value >= 256) && !flags.is_tlv) { fr_strerror_printf("dict_addattr: ATTRIBUTE has invalid number (larger than 255)."); return -1; } /* else 256..65535 are allowed */ } /* * Create a new attribute for the list */ if ((attr = fr_pool_alloc(sizeof(*attr) + namelen)) == NULL) { fr_strerror_printf("dict_addattr: out of memory"); return -1; } memcpy(attr->name, name, namelen); attr->name[namelen] = '\0'; attr->attr = value; attr->attr |= (vendor << 16); /* FIXME: hack */ attr->vendor = vendor; attr->type = type; attr->flags = flags; attr->vendor = vendor; /* * Insert the attribute, only if it's not a duplicate. */ if (!fr_hash_table_insert(attributes_byname, attr)) { DICT_ATTR *a; /* * If the attribute has identical number, then * ignore the duplicate. */ a = fr_hash_table_finddata(attributes_byname, attr); if (a && (strcasecmp(a->name, attr->name) == 0)) { if (a->attr != attr->attr) { fr_strerror_printf("dict_addattr: Duplicate attribute name %s", name); fr_pool_free(attr); return -1; } /* * Same name, same vendor, same attr, * maybe the flags and/or type is * different. Let the new value * over-ride the old one. */ } fr_hash_table_delete(attributes_byvalue, a); if (!fr_hash_table_replace(attributes_byname, attr)) { fr_strerror_printf("dict_addattr: Internal error storing attribute %s", name); fr_pool_free(attr); return -1; } } /* * Insert the SAME pointer (not free'd when this entry is * deleted), into another table. * * We want this behaviour because we want OLD names for * the attributes to be read from the configuration * files, but when we're printing them, (and looking up * by value) we want to use the NEW name. */ if (!fr_hash_table_replace(attributes_byvalue, attr)) { fr_strerror_printf("dict_addattr: Failed inserting attribute name %s", name); return -1; } if (!vendor && (value > 0) && (value < 256)) { dict_base_attrs[value] = attr; } return 0; }
/* * Initialize the directory, then fix the attr member of * all attributes. */ int dict_init(const char *dir, const char *fn) { /* * Check if we need to change anything. If not, don't do * anything. */ if (dict_stat_check(dir, fn)) { return 0; } /* * Free the dictionaries, and the stat cache. */ dict_free(); stat_root_dir = strdup(dir); stat_root_file = strdup(fn); /* * Create the table of vendor by name. There MAY NOT * be multiple vendors of the same name. * * Each vendor is malloc'd, so the free function is free. */ vendors_byname = fr_hash_table_create(dict_vendor_name_hash, dict_vendor_name_cmp, fr_pool_free); if (!vendors_byname) { return -1; } /* * Create the table of vendors by value. There MAY * be vendors of the same value. If there are, we * pick the latest one. */ vendors_byvalue = fr_hash_table_create(dict_vendor_value_hash, dict_vendor_value_cmp, fr_pool_free); if (!vendors_byvalue) { return -1; } /* * Create the table of attributes by name. There MAY NOT * be multiple attributes of the same name. * * Each attribute is malloc'd, so the free function is free. */ attributes_byname = fr_hash_table_create(dict_attr_name_hash, dict_attr_name_cmp, fr_pool_free); if (!attributes_byname) { return -1; } /* * Create the table of attributes by value. There MAY * be attributes of the same value. If there are, we * pick the latest one. */ attributes_byvalue = fr_hash_table_create(dict_attr_value_hash, dict_attr_value_cmp, fr_pool_free); if (!attributes_byvalue) { return -1; } values_byname = fr_hash_table_create(dict_value_name_hash, dict_value_name_cmp, fr_pool_free); if (!values_byname) { return -1; } values_byvalue = fr_hash_table_create(dict_value_value_hash, dict_value_value_cmp, fr_pool_free); if (!values_byvalue) { return -1; } value_fixup = NULL; /* just to be safe. */ if (my_dict_init(dir, fn, NULL, 0) < 0) return -1; if (value_fixup) { DICT_ATTR *a; value_fixup_t *this, *next; for (this = value_fixup; this != NULL; this = next) { next = this->next; a = dict_attrbyname(this->attrstr); if (!a) { fr_strerror_printf( "dict_init: No ATTRIBUTE \"%s\" defined for VALUE \"%s\"", this->attrstr, this->dval->name); return -1; /* leak, but they should die... */ } this->dval->attr = a->attr; /* * Add the value into the dictionary. */ if (!fr_hash_table_replace(values_byname, this->dval)) { fr_strerror_printf("dict_addvalue: Duplicate value name %s for attribute %s", this->dval->name, a->name); return -1; } /* * Allow them to use the old name, but * prefer the new name when printing * values. */ if (!fr_hash_table_finddata(values_byvalue, this->dval)) { fr_hash_table_replace(values_byvalue, this->dval); } free(this); /* * Just so we don't lose track of things. */ value_fixup = next; } } /* * Walk over all of the hash tables to ensure they're * initialized. We do this because the threads may perform * lookups, and we don't want multi-threaded re-ordering * of the table entries. That would be bad. */ fr_hash_table_walk(vendors_byname, null_callback, NULL); fr_hash_table_walk(vendors_byvalue, null_callback, NULL); fr_hash_table_walk(attributes_byname, null_callback, NULL); fr_hash_table_walk(attributes_byvalue, null_callback, NULL); fr_hash_table_walk(values_byvalue, null_callback, NULL); fr_hash_table_walk(values_byname, null_callback, NULL); return 0; }