/* * Get a value by its name, keyed off of an attribute. */ DICT_VALUE *dict_valbyname(unsigned int attr, const char *name) { DICT_VALUE *my_dv, *dv; uint32_t* buffer = NULL; if (!name) return NULL; buffer = calloc(1,DICT_ATTR_MAX_NAME_LEN); if(buffer == NULL) { return NULL; } my_dv = (DICT_VALUE *) buffer; my_dv->attr = attr; my_dv->name[0] = '\0'; /* * Look up the attribute alias target, and use * the correct attribute number if found. */ dv = fr_hash_table_finddata(values_byname, my_dv); if (dv) my_dv->attr = dv->value; strncpy(my_dv->name, name,DICT_ATTR_MAX_NAME_LEN - sizeof(*my_dv) - 1); dv = fr_hash_table_finddata(values_byname, my_dv); free(buffer); return dv; }
/* * Should be called AFTER yanking it from the list, so that * any newly inserted entries don't collide with this one. */ int fr_packet_list_id_free(fr_packet_list_t *pl, RADIUS_PACKET *request) { fr_packet_socket_t *ps; fr_packet_dst2id_t my_pd, *pd; if (!pl || !request) return 0; ps = fr_socket_find(pl, request->sockfd); if (!ps) return 0; my_pd.dst_ipaddr = request->dst_ipaddr; my_pd.dst_port = request->dst_port; pd = fr_hash_table_finddata(pl->dst2id_ht, &my_pd); if (!pd) return 0; pd->id[request->id] &= ~(1 << ps->offset); pd->num_outgoing--; ps->num_outgoing--; pl->num_outgoing--; if(pd->num_outgoing == 0) { fr_hash_table_delete(pl->dst2id_ht,&my_pd); } return 1; }
/* * Return the vendor struct based on the PEC. */ DICT_VENDOR *dict_vendorbyvalue(int vendorpec) { DICT_VENDOR dv; dv.vendorpec = vendorpec; return fr_hash_table_finddata(vendors_byvalue, &dv); }
/* * Get an attribute by its numerical value. */ DICT_ATTR *dict_attrbyvalue(unsigned int attr) { DICT_ATTR dattr; if ((attr > 0) && (attr < 256)) return dict_base_attrs[attr]; dattr.attr = attr; dattr.vendor = VENDOR(attr); return fr_hash_table_finddata(attributes_byvalue, &dattr); }
/* * Associate a value with an attribute and return it. */ DICT_VALUE *dict_valbyattr(unsigned int attr, int value) { DICT_VALUE dval, *dv; /* * First, look up aliases. */ dval.attr = attr; dval.name[0] = '\0'; /* * Look up the attribute alias target, and use * the correct attribute number if found. */ dv = fr_hash_table_finddata(values_byname, &dval); if (dv) dval.attr = dv->value; dval.value = value; return fr_hash_table_finddata(values_byvalue, &dval); }
/* * Get an attribute by its name. */ DICT_ATTR *dict_attrbyname(const char *name) { DICT_ATTR *da; uint32_t buffer[(sizeof(*da) + DICT_ATTR_MAX_NAME_LEN + 3)/4]; if (!name) return NULL; da = (DICT_ATTR *) buffer; strlcpy(da->name, name, DICT_ATTR_MAX_NAME_LEN + 1); return fr_hash_table_finddata(attributes_byname, da); }
/* * Get a value by its name, keyed off of an attribute. */ DICT_VALUE *dict_valbyname(unsigned int attr, const char *name) { DICT_VALUE *my_dv, *dv; uint32_t buffer[(sizeof(*my_dv) + DICT_VALUE_MAX_NAME_LEN + 3)/4]; if (!name) return NULL; my_dv = (DICT_VALUE *) buffer; my_dv->attr = attr; my_dv->name[0] = '\0'; /* * Look up the attribute alias target, and use * the correct attribute number if found. */ dv = fr_hash_table_finddata(values_byname, my_dv); if (dv) my_dv->attr = dv->value; strlcpy(my_dv->name, name, DICT_VALUE_MAX_NAME_LEN + 1); return fr_hash_table_finddata(values_byname, my_dv); }
/* * Get the vendor PEC based on the vendor name * * This is efficient only for small numbers of vendors. */ int dict_vendorbyname(const char *name) { DICT_VENDOR *dv; uint32_t buffer[(sizeof(*dv) + DICT_VENDOR_MAX_NAME_LEN + 3)/4]; if (!name) return 0; dv = (DICT_VENDOR *) buffer; strlcpy(dv->name, name, DICT_VENDOR_MAX_NAME_LEN + 1); dv = fr_hash_table_finddata(vendors_byname, dv); if (!dv) return 0; return dv->vendorpec; }
/* * Get an attribute by its name. */ DICT_ATTR *dict_attrbyname(const char *name) { DICT_ATTR *da,*res; uint32_t* buffer = NULL; if (!name) return NULL; buffer = calloc(1,DICT_ATTR_MAX_NAME_LEN); if(buffer == NULL) { return NULL; } da = (DICT_ATTR *) buffer; strncpy(da->name, name,DICT_ATTR_MAX_NAME_LEN - sizeof(*da) - 1); res = fr_hash_table_finddata(attributes_byname, da); free(buffer); return res; }
/* * Wait 10 seconds at most for a child to exit, then give up. */ pid_t rad_waitpid(pid_t pid, int *status) { int i; thread_fork_t mytf, *tf; if (!pool_initialized) return waitpid(pid, status, 0); if (pid <= 0) return -1; mytf.pid = pid; pthread_mutex_lock(&thread_pool.wait_mutex); tf = fr_hash_table_finddata(thread_pool.waiters, &mytf); pthread_mutex_unlock(&thread_pool.wait_mutex); if (!tf) return -1; for (i = 0; i < 100; i++) { reap_children(); if (tf->exited) { *status = tf->status; pthread_mutex_lock(&thread_pool.wait_mutex); fr_hash_table_delete(thread_pool.waiters, &mytf); pthread_mutex_unlock(&thread_pool.wait_mutex); return pid; } usleep(100000); /* sleep for 1/10 of a second */ } /* * 10 seconds have passed, give up on the child. */ pthread_mutex_lock(&thread_pool.wait_mutex); fr_hash_table_delete(thread_pool.waiters, &mytf); pthread_mutex_unlock(&thread_pool.wait_mutex); return 0; }
/* * Get the vendor PEC based on the vendor name * * This is efficient only for small numbers of vendors. */ int dict_vendorbyname(const char *name) { DICT_VENDOR *dv; uint32_t* buffer = NULL; if (!name) return 0; buffer = calloc(1,DICT_VENDOR_MAX_NAME_LEN); if(buffer == NULL) { return 0; } dv = (DICT_VENDOR *) buffer; strncpy(dv->name, name,DICT_VENDOR_MAX_NAME_LEN - sizeof(*dv) - 1); dv = fr_hash_table_finddata(vendors_byname, dv); free(buffer); if (!dv) return 0; return dv->vendorpec; }
/* * We don't want to catch SIGCHLD for a host of reasons. * * - exec_wait means that someone, somewhere, somewhen, will * call waitpid(), and catch the child. * * - SIGCHLD is delivered to a random thread, not the one that * forked. * * - if another thread catches the child, we have to coordinate * with the thread doing the waiting. * * - if we don't waitpid() for non-wait children, they'll be zombies, * and will hang around forever. * */ static void reap_children(void) { pid_t pid; int status; thread_fork_t mytf, *tf; pthread_mutex_lock(&thread_pool.wait_mutex); do { pid = waitpid(0, &status, WNOHANG); if (pid <= 0) break; mytf.pid = pid; tf = fr_hash_table_finddata(thread_pool.waiters, &mytf); if (!tf) continue; tf->status = status; tf->exited = 1; } while (fr_hash_table_num_elements(thread_pool.waiters) > 0); pthread_mutex_unlock(&thread_pool.wait_mutex); }
/* * Common code called by everything below. */ static rlm_rcode_t file_common(rlm_files_t *inst, REQUEST *request, char const *filename, fr_hash_table_t *ht, VALUE_PAIR *request_pairs, VALUE_PAIR **reply_pairs) { char const *name, *match; VALUE_PAIR *check_tmp; VALUE_PAIR *reply_tmp; const PAIR_LIST *user_pl, *default_pl; int found = 0; PAIR_LIST my_pl; char buffer[256]; if (!inst->key) { VALUE_PAIR *namepair; namepair = request->username; name = namepair ? namepair->vp_strvalue : "NONE"; } else { int len; len = radius_xlat(buffer, sizeof(buffer), request, inst->key, NULL, NULL); if (len < 0) { return RLM_MODULE_FAIL; } name = len ? buffer : "NONE"; } if (!ht) return RLM_MODULE_NOOP; my_pl.name = name; user_pl = fr_hash_table_finddata(ht, &my_pl); my_pl.name = "DEFAULT"; default_pl = fr_hash_table_finddata(ht, &my_pl); /* * Find the entry for the user. */ while (user_pl || default_pl) { const PAIR_LIST *pl; if (!default_pl && user_pl) { pl = user_pl; match = name; user_pl = user_pl->next; } else if (!user_pl && default_pl) { pl = default_pl; match = "DEFAULT"; default_pl = default_pl->next; } else if (user_pl->order < default_pl->order) { pl = user_pl; match = name; user_pl = user_pl->next; } else { pl = default_pl; match = "DEFAULT"; default_pl = default_pl->next; } if (paircompare(request, request_pairs, pl->check, reply_pairs) == 0) { RDEBUG2("%s: Matched entry %s at line %d", filename, match, pl->lineno); found = 1; check_tmp = paircopy(request, pl->check); /* ctx may be reply or proxy */ reply_tmp = paircopy(request, pl->reply); radius_xlat_move(request, reply_pairs, &reply_tmp); pairmove(request, &request->config_items, &check_tmp); pairfree(&reply_tmp); pairfree(&check_tmp); /* * Fallthrough? */ if (!fallthrough(pl->reply)) break; } } /* * Remove server internal parameters. */ pairdelete(reply_pairs, PW_FALL_THROUGH, 0, TAG_ANY); /* * See if we succeeded. */ if (!found) return RLM_MODULE_NOOP; /* on to the next module */ return RLM_MODULE_OK; }
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 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; }
/* * 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; }
/* * 1 == ID was allocated & assigned * 0 == error allocating memory * -1 == all ID's are used, caller should open a new socket. * * Note that this ALSO assigns a socket to use, and updates * packet->request->src_ipaddr && packet->request->src_port * * In multi-threaded systems, the calls to id_alloc && id_free * should be protected by a mutex. This does NOT have to be * the same mutex as the one protecting the insert/find/yank * calls! */ int fr_packet_list_id_alloc(fr_packet_list_t *pl, RADIUS_PACKET *request) { int i, id, start; int src_any = 0; uint32_t free_mask; fr_packet_dst2id_t my_pd, *pd; fr_packet_socket_t *ps; bzero(&my_pd,sizeof(my_pd)); if (!pl || !pl->alloc_id || !request) { fr_strerror_printf("Invalid arguments"); return 0; } /* * Error out if no destination is specified. */ if ((request->dst_ipaddr.af == AF_UNSPEC) || (request->dst_port == 0)) { fr_strerror_printf("No destination address/port specified"); return 0; } /* * Special case: unspec == "don't care" */ if (request->src_ipaddr.af == AF_UNSPEC) { memset(&request->src_ipaddr, 0, sizeof(request->src_ipaddr)); request->src_ipaddr.af = request->dst_ipaddr.af; } src_any = fr_inaddr_any(&request->src_ipaddr); if (src_any < 0) { fr_strerror_printf("Error checking src IP address"); return 0; } /* * MUST specify a destination address. */ if (fr_inaddr_any(&request->dst_ipaddr) != 0) { fr_strerror_printf("Error checking dst IP address"); return 0; } my_pd.dst_ipaddr = request->dst_ipaddr; my_pd.dst_port = request->dst_port; pd = fr_hash_table_finddata(pl->dst2id_ht, &my_pd); if (!pd) { pd = malloc(sizeof(*pd) + 255 * sizeof(pd->id[0])); if (!pd) return 0; memset(pd, 0, sizeof(*pd) + 255 * sizeof(pd->id[0])); pd->dst_ipaddr = request->dst_ipaddr; pd->dst_port = request->dst_port; if (!fr_hash_table_insert(pl->dst2id_ht, pd)) { free(pd); fr_strerror_printf("Failed inserting into hash"); return 0; } } /* * FIXME: Go to an LRU system. This prevents ID re-use * for as long as possible. The main problem with that * approach is that it requires us to populate the * LRU/FIFO when we add a new socket, or a new destination, * which can be expensive. * * The LRU can be avoided if the caller takes care to free * Id's only when all responses have been received, OR after * a timeout. */ id = start = (int) fr_rand() & 0xff; while (pd->id[id] == pl->mask) { /* all sockets are using this ID */ id++; id &= 0xff; if (id == start) { fr_strerror_printf("All IDs are being used"); return 0; } } free_mask = ~((~pd->id[id]) & pl->mask); start = -1; for (i = 0; i < MAX_SOCKETS; i++) { if (pl->sockets[i].sockfd == -1) continue; /* paranoia */ ps = &(pl->sockets[i]); /* * Address families don't match, skip it. */ if (ps->ipaddr.af != request->dst_ipaddr.af) continue; /* * We're sourcing from *, and they asked for a * specific source address: ignore it. */ if (ps->inaddr_any && !src_any) continue; /* * We're sourcing from a specific IP, and they * asked for a source IP that isn't us: ignore * it. */ if (!ps->inaddr_any && !src_any && (fr_ipaddr_cmp(&request->src_ipaddr, &ps->ipaddr) != 0)) continue; if ((free_mask & (1 << i)) == 0) { start = i; break; } } if (start < 0) { fr_strerror_printf("Internal sanity check failed"); return 0; /* bad error */ } pd->id[id] |= (1 << start); ps = &pl->sockets[start]; pd->num_outgoing++; ps->num_outgoing++; pl->num_outgoing++; /* * Set the ID, source IP, and source port. */ request->id = id; request->sockfd = ps->sockfd; request->src_ipaddr = ps->ipaddr; request->src_port = ps->port; return 1; }
/* * (Re-)read radiusd.conf into memory. */ static int mod_instantiate(CONF_SECTION *conf, void *instance) { detail_instance_t *inst = instance; CONF_SECTION *cs; inst->name = cf_section_name2(conf); if (!inst->name) { inst->name = cf_section_name1(conf); } inst->lf= fr_logfile_init(inst); if (!inst->lf) { cf_log_err_cs(conf, "Failed creating log file context"); return -1; } /* * Suppress certain attributes. */ cs = cf_section_sub_find(conf, "suppress"); if (cs) { CONF_ITEM *ci; inst->ht = fr_hash_table_create(detail_hash, detail_cmp, NULL); for (ci = cf_item_find_next(cs, NULL); ci != NULL; ci = cf_item_find_next(cs, ci)) { char const *attr; DICT_ATTR const *da; if (!cf_item_is_pair(ci)) continue; attr = cf_pair_attr(cf_itemtopair(ci)); if (!attr) continue; /* pair-anoia */ da = dict_attrbyname(attr); if (!da) { cf_log_err_cs(conf, "No such attribute '%s'", attr); return -1; } /* * Be kind to minor mistakes. */ if (fr_hash_table_finddata(inst->ht, da)) { WARN("rlm_detail (%s): Ignoring duplicate entry '%s'", inst->name, attr); continue; } if (!fr_hash_table_insert(inst->ht, da)) { ERROR("rlm_detail (%s): Failed inserting '%s' into suppression table", inst->name, attr); return -1; } DEBUG("rlm_detail (%s): '%s' suppressed, will not appear in detail output", inst->name, attr); } /* * If we didn't suppress anything, delete the hash table. */ if (fr_hash_table_num_elements(inst->ht) == 0) { fr_hash_table_free(inst->ht); inst->ht = NULL; } } return 0; }
/** Write a single detail entry to file pointer * * @param[in] out Where to write entry. * @param[in] inst Instance of rlm_detail. * @param[in] request The current request. * @param[in] packet associated with the request (request, reply, proxy-request, proxy-reply...). * @param[in] compat Write out entry in compatibility mode. */ static int detail_write(FILE *out, detail_instance_t *inst, REQUEST *request, RADIUS_PACKET *packet, bool compat) { VALUE_PAIR *vp; char timestamp[256]; if (radius_xlat(timestamp, sizeof(timestamp), request, inst->header, NULL, NULL) < 0) { return -1; } #define WRITE(fmt, ...) do {\ if (fprintf(out, fmt, ## __VA_ARGS__) < 0) {\ RERROR("Failed writing to detail file: %s", fr_syserror(errno));\ return -1;\ }\ } while(0) WRITE("%s\n", timestamp); /* * Write the information to the file. */ if (!compat) { /* * Print out names, if they're OK. * Numbers, if not. */ if (is_radius_code(packet->code)) { WRITE("\tPacket-Type = %s\n", fr_packet_codes[packet->code]); } else { WRITE("\tPacket-Type = %d\n", packet->code); } } if (inst->log_srcdst) { VALUE_PAIR src_vp, dst_vp; memset(&src_vp, 0, sizeof(src_vp)); memset(&dst_vp, 0, sizeof(dst_vp)); switch (packet->src_ipaddr.af) { case AF_INET: src_vp.da = dict_attrbyvalue(PW_PACKET_SRC_IP_ADDRESS, 0); src_vp.vp_ipaddr = packet->src_ipaddr.ipaddr.ip4addr.s_addr; dst_vp.da = dict_attrbyvalue(PW_PACKET_DST_IP_ADDRESS, 0); dst_vp.vp_ipaddr = packet->dst_ipaddr.ipaddr.ip4addr.s_addr; break; case AF_INET6: src_vp.da = dict_attrbyvalue(PW_PACKET_SRC_IPV6_ADDRESS, 0); memcpy(&src_vp.vp_ipv6addr, &packet->src_ipaddr.ipaddr.ip6addr, sizeof(packet->src_ipaddr.ipaddr.ip6addr)); dst_vp.da = dict_attrbyvalue(PW_PACKET_DST_IPV6_ADDRESS, 0); memcpy(&dst_vp.vp_ipv6addr, &packet->dst_ipaddr.ipaddr.ip6addr, sizeof(packet->dst_ipaddr.ipaddr.ip6addr)); break; default: break; } detail_vp_print(request, out, &src_vp); detail_vp_print(request, out, &dst_vp); src_vp.da = dict_attrbyvalue(PW_PACKET_SRC_PORT, 0); src_vp.vp_integer = packet->src_port; dst_vp.da = dict_attrbyvalue(PW_PACKET_DST_PORT, 0); dst_vp.vp_integer = packet->dst_port; detail_vp_print(request, out, &src_vp); detail_vp_print(request, out, &dst_vp); } { vp_cursor_t cursor; /* Write each attribute/value to the log file */ for (vp = fr_cursor_init(&cursor, &packet->vps); vp; vp = fr_cursor_next(&cursor)) { FR_TOKEN op; if (inst->ht && fr_hash_table_finddata(inst->ht, vp->da)) continue; /* * Don't print passwords in old format... */ if (compat && !vp->da->vendor && (vp->da->attr == PW_USER_PASSWORD)) continue; /* * Print all of the attributes, operator should always be '='. */ op = vp->op; vp->op = T_OP_EQ; vp_print(out, vp); vp->op = op; } } /* * Add non-protocol attributes. */ if (compat) { #ifdef WITH_PROXY if (request->proxy) { char proxy_buffer[128]; inet_ntop(request->proxy->dst_ipaddr.af, &request->proxy->dst_ipaddr.ipaddr, proxy_buffer, sizeof(proxy_buffer)); WRITE("\tFreeradius-Proxied-To = %s\n", proxy_buffer); } #endif WRITE("\tTimestamp = %ld\n", (unsigned long) request->timestamp); } WRITE("\n"); 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; }