static indexed_modcallable *lookup_by_index(rbtree_t *components, int comp, int idx) { indexed_modcallable myc; myc.comp = comp; myc.idx = idx; return rbtree_finddata(components, &myc); }
/** Find a map processor by name * * @param[in] name of map processor. * @return * - #map_proc matching name. * - NULL if none was found. */ map_proc_t *map_proc_find(char const *name) { map_proc_t find; if (!map_proc_root) return NULL; strlcpy(find.name, name, sizeof(find.name)); find.length = strlen(find.name); return rbtree_finddata(map_proc_root, &find); }
/* * Delete a request from the proxy trees. */ static void rl_delete_proxy(REQUEST *request, rbnode_t *node) { proxy_id_t myid, *entry; rad_assert(node != NULL); rbtree_delete(proxy_tree, node); myid.dst_ipaddr = request->proxy->dst_ipaddr; myid.dst_port = request->proxy->dst_port; /* * Find the Id in the array of allocated Id's, * and delete it. */ entry = rbtree_finddata(proxy_id_tree, &myid); if (entry) { int i; DEBUG3(" proxy: de-allocating %08x:%d %d", entry->dst_ipaddr, entry->dst_port, request->proxy->id); /* * Find the proxy socket associated with this * Id. We loop over all 32 proxy fd's, but we * partially index by proxy fd's, which means * that we almost always break out of the loop * quickly. */ for (i = 0; i < 32; i++) { int offset; offset = (request->proxy->sockfd + i) & 0x1f; if (proxy_fds[offset] == request->proxy->sockfd) { entry->id[request->proxy->id] &= ~(1 << offset); break; } } /* else die horribly? */ } else { /* * Hmm... not sure what to do here. */ DEBUG3(" proxy: FAILED TO FIND %08x:%d %d", myid.dst_ipaddr, myid.dst_port, request->proxy->id); } }
/** Perform a search and map the result of the search to server attributes * * @param[in] mod_inst #rlm_csv_t * @param[in] proc_inst mapping map entries to field numbers. * @param[in,out] request The current request. * @param[in] key key to look for * @param[in] maps Head of the map list. * @return * - #RLM_MODULE_NOOP no rows were returned. * - #RLM_MODULE_UPDATED if one or more #VALUE_PAIR were added to the #REQUEST. * - #RLM_MODULE_FAIL if an error occurred. */ static rlm_rcode_t mod_map_proc(void *mod_inst, UNUSED void *proc_inst, REQUEST *request, char const *key, vp_map_t const *maps) { rlm_csv_t *inst = mod_inst; rlm_csv_entry_t *e, my_entry; vp_map_t const *map; my_entry.key = key; e = rbtree_finddata(inst->tree, &my_entry); if (!e) return RLM_MODULE_NOOP; RINDENT(); for (map = maps; map != NULL; map = map->next) { int field; char *field_name; /* * Avoid memory allocations if possible. */ if (map->rhs->type != TMPL_TYPE_LITERAL) { if (tmpl_aexpand(request, &field_name, request, map->rhs, NULL, NULL) < 0) { RDEBUG("Failed expanding RHS at %s", map->lhs->name); return RLM_MODULE_FAIL; } } else { memcpy(&field_name, &map->rhs->name, sizeof(field_name)); /* const */ } field = fieldname2offset(inst, field_name); if (field_name != map->rhs->name) talloc_free(field_name); if (field < 0) { RDEBUG("No such field name %s", map->rhs->name); return RLM_MODULE_FAIL; } /* * Pass the raw data to the callback, which will * create the VP and add it to the map. */ if (map_to_request(request, map, csv_map_getvalue, e->data[field]) < 0) { return RLM_MODULE_FAIL; } } return RLM_MODULE_UPDATED; }
/** Delete a socket from a network. MUST be called only by the listener itself!. * * @param nr the network * @param listen Functions and context. */ int fr_network_socket_delete(fr_network_t *nr, fr_listen_t const *listen) { fr_network_socket_t *s, my_socket; my_socket.listen = listen; s = rbtree_finddata(nr->sockets, &my_socket); if (!s) { return -1; } fr_network_socket_dead(nr, s); return 0; }
/** Unregister a map processor * * @param[in] proc to unregister. */ static int _map_proc_unregister(map_proc_t *proc) { map_proc_t find; map_proc_t *found; strlcpy(find.name, proc->name, sizeof(find.name)); find.length = strlen(find.name); found = rbtree_finddata(map_proc_root, &find); if (!found) return 0; rbtree_deletebydata(map_proc_root, found); return 0; }
/** Signal the network to read from a listener * * @param nr the network * @param listen the listener to read from */ void fr_network_listen_read(fr_network_t *nr, fr_listen_t const *listen) { fr_network_socket_t my_socket, *s; (void) talloc_get_type_abort(nr, fr_network_t); (void) talloc_get_type_abort_const(listen, fr_listen_t); my_socket.listen = listen; s = rbtree_finddata(nr->sockets, &my_socket); if (!s) return; /* * Go read the socket. */ fr_network_read(nr->el, s->fd, 0, s); }
/** Unregister an xlat function * * We can only have one function to call per name, so the passing of "func" * here is extraneous. * * @param[in] name xlat to unregister. * @param[in] func unused. * @param[in] instance data. */ void xlat_unregister(char const *name, UNUSED RAD_XLAT_FUNC func, void *instance) { xlat_t *c; xlat_t my_xlat; if (!name) return; strlcpy(my_xlat.name, name, sizeof(my_xlat.name)); my_xlat.length = strlen(my_xlat.name); c = rbtree_finddata(xlat_root, &my_xlat); if (!c) return; if (c->instance != instance) return; rbtree_deletebydata(xlat_root, c); }
/* * Find a cached entry. */ static rlm_cache_entry_t *cache_find(rlm_cache_t *inst, REQUEST *request, char const *key) { int ttl; rlm_cache_entry_t *c, my_c; VALUE_PAIR *vp; /* * Look at the expiry heap. */ c = fr_heap_peek(inst->heap); if (!c) { rad_assert(rbtree_num_elements(inst->cache) == 0); return NULL; } /* * If it's time to expire an old entry, do so now. */ if (c->expires < request->timestamp) { fr_heap_extract(inst->heap, c); rbtree_deletebydata(inst->cache, c); } /* * Is there an entry for this key? */ my_c.key = key; c = rbtree_finddata(inst->cache, &my_c); if (!c) return NULL; /* * Yes, but it expired, OR the "forget all" epoch has * passed. Delete it, and pretend it doesn't exist. */ if ((c->expires < request->timestamp) || (c->created < inst->epoch)) { delete: RDEBUG("Entry has expired, removing"); fr_heap_extract(inst->heap, c); rbtree_deletebydata(inst->cache, c); return NULL; }
/* * Find a client in the RADCLIENTS list by number. * This is a support function for the statistics code. */ RADCLIENT *client_findbynumber(RADCLIENT_LIST const *clients, int number) { if (!clients) clients = root_clients; if (!clients) return NULL; if (number >= tree_num_max) return NULL; if (tree_num) { RADCLIENT myclient; myclient.number = number; return rbtree_finddata(tree_num, &myclient); } return NULL; }
/* * find the appropriate registered xlat function. */ static xlat_t *xlat_find(const char *module) { xlat_t my_xlat; /* * Look for dictionary attributes first. */ if ((dict_attrbyname(module) != NULL) || (strchr(module, '[') != NULL) || (strchr(module, '#') != NULL)) { module = "request"; } strlcpy(my_xlat.module, module, sizeof(my_xlat.module)); my_xlat.length = strlen(my_xlat.module); return rbtree_finddata(xlat_root, &my_xlat); }
/** * @brief Unregister an xlat function. * * We can only have one function to call per name, so the * passing of "func" here is extraneous. * * @param module xlat to unregister * @param func Unused * @return Void. */ void xlat_unregister(const char *module, RAD_XLAT_FUNC func, void *instance) { xlat_t *c; xlat_t my_xlat; func = func; /* -Wunused */ if (!module) return; strlcpy(my_xlat.module, module, sizeof(my_xlat.module)); my_xlat.length = strlen(my_xlat.module); c = rbtree_finddata(xlat_root, &my_xlat); if (!c) return; if (c->instance != instance) return; rbtree_deletebydata(xlat_root, c); }
/** Retrieve module/thread specific instance data for a module * * @param[in] mi to find thread specific data for. * @return * - Thread specific instance data on success. * - NULL if module has no thread instance data. */ module_thread_instance_t *module_thread_instance_find(module_instance_t *mi) { rbtree_t *tree = module_thread_inst_tree; module_thread_instance_t find = { .mod_inst = mi->dl_inst->data }; return rbtree_finddata(tree, &find); } /** Retrieve module/thread specific instance data for a module * * @param[in] mod_inst Module specific instance to find thread_data for. * @return * - Thread specific instance data on success. * - NULL if module has no thread instance data. */ void *module_thread_instance_by_data(void *mod_inst) { rbtree_t *tree = module_thread_inst_tree; module_thread_instance_t find = { .mod_inst = mod_inst }, *found;
static int cmd_stats_socket(FILE *fp, FILE *fp_err, void *ctx, fr_cmd_info_t const *info) { fr_network_t const *nr = ctx; fr_network_socket_t *s, my_s; my_s.number = info->box[0]->vb_uint32; s = rbtree_finddata(nr->sockets_by_num, &my_s); if (!s) { fprintf(fp_err, "No such socket number '%s'.\n", info->argv[0]); return -1; } fprintf(fp, "count.in\t%" PRIu64 "\n", s->stats.in); fprintf(fp, "count.out\t%" PRIu64 "\n", s->stats.out); fprintf(fp, "count.dup\t%" PRIu64 "\n", s->stats.dup); fprintf(fp, "count.dropped\t%" PRIu64 "\n", s->stats.dropped); return 0; }
/* * Evaluate a policy, keyed by name. */ static int policy_evaluate_name(policy_state_t *state, const char *name) { int rcode; const policy_item_t *this; policy_named_t mypolicy, *policy; mypolicy.name = name; policy = rbtree_finddata(state->inst->policies, &mypolicy); if (!policy) return RLM_MODULE_FAIL; DEBUG2("rlm_policy: Evaluating policy %s", name); rad_assert(policy->item.type != POLICY_TYPE_BAD); rad_assert(policy->item.type < POLICY_TYPE_NUM_TYPES); rcode = policy_stack_push(state, policy->policy); if (!rcode) { return RLM_MODULE_FAIL; } /* * FIXME: Look for magic keywords like "return", * where the packet gets accepted/rejected/whatever */ while (policy_stack_pop(state, &this)) { rad_assert(this != NULL); rad_assert(this->type != POLICY_TYPE_BAD); rad_assert(this->type < POLICY_TYPE_NUM_TYPES); debug_evaluate("Evaluating at line %d\n", this->lineno); rcode = (*evaluate_functions[this->type])(state, this); if (!rcode) { return RLM_MODULE_FAIL; } } /* loop until the stack is empty */ return state->rcode; }
/* * Find a client in the RADCLIENTS list. */ RADCLIENT *client_find(RADCLIENT_LIST const *clients, fr_ipaddr_t const *ipaddr, int proto) { int32_t i, max_prefix; RADCLIENT myclient; if (!clients) clients = root_clients; if (!clients || !ipaddr) return NULL; switch (ipaddr->af) { case AF_INET: max_prefix = 32; break; case AF_INET6: max_prefix = 128; break; default : return NULL; } for (i = max_prefix; i >= (int32_t) clients->min_prefix; i--) { void *data; myclient.ipaddr = *ipaddr; myclient.proto = proto; fr_ipaddr_mask(&myclient.ipaddr, i); if (!clients->trees[i]) continue; data = rbtree_finddata(clients->trees[i], &myclient); if (data) return data; } return NULL; }
static void got_packet(UNUSED uint8_t *args, const struct pcap_pkthdr *header, const uint8_t *data) { static int count = 1; /* Packets seen */ /* * Define pointers for packet's attributes */ const struct ip_header *ip; /* The IP header */ const struct udp_header *udp; /* The UDP header */ const uint8_t *payload; /* Packet payload */ /* * And define the size of the structures we're using */ int size_ethernet = sizeof(struct ethernet_header); int size_ip = sizeof(struct ip_header); int size_udp = sizeof(struct udp_header); /* * For FreeRADIUS */ RADIUS_PACKET *packet, *original; struct timeval elapsed; /* * Define our packet's attributes */ if ((data[0] == 2) && (data[1] == 0) && (data[2] == 0) && (data[3] == 0)) { ip = (const struct ip_header*) (data + 4); } else { ip = (const struct ip_header*)(data + size_ethernet); } udp = (const struct udp_header*)(((const uint8_t *) ip) + size_ip); payload = (const uint8_t *)(((const uint8_t *) udp) + size_udp); packet = rad_alloc(NULL, 0); if (!packet) { fprintf(stderr, "Out of memory\n"); return; } packet->src_ipaddr.af = AF_INET; packet->src_ipaddr.ipaddr.ip4addr.s_addr = ip->ip_src.s_addr; packet->src_port = ntohs(udp->udp_sport); packet->dst_ipaddr.af = AF_INET; packet->dst_ipaddr.ipaddr.ip4addr.s_addr = ip->ip_dst.s_addr; packet->dst_port = ntohs(udp->udp_dport); memcpy(&packet->data, &payload, sizeof(packet->data)); packet->data_len = header->len - (payload - data); if (!rad_packet_ok(packet, 0)) { DEBUG(log_dst, "Packet: %s\n", fr_strerror()); DEBUG(log_dst, " From %s:%d\n", inet_ntoa(ip->ip_src), ntohs(udp->udp_sport)); DEBUG(log_dst, " To: %s:%d\n", inet_ntoa(ip->ip_dst), ntohs(udp->udp_dport)); DEBUG(log_dst, " Type: %s\n", fr_packet_codes[packet->code]); rad_free(&packet); return; } switch (packet->code) { case PW_COA_REQUEST: /* we need a 16 x 0 byte vector for decrypting encrypted VSAs */ original = nullpacket; break; case PW_AUTHENTICATION_ACK: /* look for a matching request and use it for decoding */ original = rbtree_finddata(request_tree, packet); break; case PW_AUTHENTICATION_REQUEST: /* save the request for later matching */ original = rad_alloc_reply(NULL, packet); if (original) { /* just ignore allocation failures */ rbtree_deletebydata(request_tree, original); rbtree_insert(request_tree, original); } /* fallthrough */ default: /* don't attempt to decode any encrypted attributes */ original = NULL; } /* * Decode the data without bothering to check the signatures. */ if (rad_decode(packet, original, radius_secret) != 0) { rad_free(&packet); fr_perror("decode"); return; } /* * We've seen a successfull reply to this, so delete it now */ if (original) rbtree_deletebydata(request_tree, original); if (filter_vps && filter_packet(packet)) { rad_free(&packet); DEBUG(log_dst, "Packet number %d doesn't match\n", count++); return; } if (out) { pcap_dump((void *) out, header, data); goto check_filter; } INFO(log_dst, "%s Id %d\t", fr_packet_codes[packet->code], packet->id); /* * Print the RADIUS packet */ INFO(log_dst, "%s:%d -> ", inet_ntoa(ip->ip_src), ntohs(udp->udp_sport)); INFO(log_dst, "%s:%d", inet_ntoa(ip->ip_dst), ntohs(udp->udp_dport)); DEBUG1(log_dst, "\t(%d packets)", count++); if (!start_pcap.tv_sec) { start_pcap = header->ts; } tv_sub(&header->ts, &start_pcap, &elapsed); INFO(log_dst, "\t+%u.%03u", (unsigned int) elapsed.tv_sec, (unsigned int) elapsed.tv_usec / 1000); if (fr_debug_flag > 1) { DEBUG(log_dst, "\n"); if (packet->vps) { if (do_sort) sort(packet); vp_printlist(log_dst, packet->vps); pairfree(&packet->vps); } } INFO(log_dst, "\n"); if (!to_stdout && (fr_debug_flag > 4)) { rad_print_hex(packet); } fflush(log_dst); check_filter: /* * If we're doing filtering, Access-Requests are cached in the * filter tree. */ if (!filter_vps || ((packet->code != PW_AUTHENTICATION_REQUEST) && (packet->code != PW_ACCOUNTING_REQUEST))) { rad_free(&packet); } }
/* * Find a module on disk or in memory, and link to it. */ static module_entry_t *linkto_module(const char *module_name, CONF_SECTION *cs) { module_entry_t myentry; module_entry_t *node; lt_dlhandle handle = NULL; char module_struct[256]; char *p; const module_t *module; strlcpy(myentry.name, module_name, sizeof(myentry.name)); node = rbtree_finddata(module_tree, &myentry); if (node) return node; /* * Link to the module's rlm_FOO{} module structure. * * The module_name variable has the version number * embedded in it, and we don't want that here. */ strcpy(module_struct, module_name); p = strrchr(module_struct, '-'); if (p) *p = '\0'; #if !defined(WITH_LIBLTDL) && defined(HAVE_DLFCN_H) && defined(RTLD_SELF) module = lt_dlsym(RTLD_SELF, module_struct); if (module) goto open_self; #endif /* * Keep the handle around so we can dlclose() it. */ handle = fr_dlopenext(module_name); if (handle == NULL) { cf_log_err(cf_sectiontoitem(cs), "Failed to link to module '%s': %s\n", module_name, lt_dlerror()); return NULL; } DEBUG3(" (Loaded %s, checking if it's valid)", module_name); /* * libltld MAY core here, if the handle it gives us contains * garbage data. */ module = lt_dlsym(handle, module_struct); if (!module) { cf_log_err(cf_sectiontoitem(cs), "Failed linking to %s structure: %s\n", module_name, lt_dlerror()); lt_dlclose(handle); return NULL; } #if !defined(WIT_LIBLTDL) && defined (HAVE_DLFCN_H) && defined(RTLD_SELF) open_self: #endif /* * Before doing anything else, check if it's sane. */ if (module->magic != RLM_MODULE_MAGIC_NUMBER) { lt_dlclose(handle); cf_log_err(cf_sectiontoitem(cs), "Invalid version in module '%s'", module_name); return NULL; } /* make room for the module type */ node = rad_malloc(sizeof(*node)); memset(node, 0, sizeof(*node)); strlcpy(node->name, module_name, sizeof(node->name)); node->module = module; node->handle = handle; cf_log_module(cs, "Linked to module %s", module_name); /* * Add the module as "rlm_foo-version" to the configuration * section. */ if (!rbtree_insert(module_tree, node)) { radlog(L_ERR, "Failed to cache module %s", module_name); lt_dlclose(handle); free(node); return NULL; } return node; }
static void rs_process_packet(rs_event_t *event, struct pcap_pkthdr const *header, uint8_t const *data) { static int count = 0; /* Packets seen */ rs_stats_t *stats = event->stats; decode_fail_t reason; /* * Pointers into the packet data we just received */ size_t len; uint8_t const *p = data; struct ip_header const *ip = NULL; /* The IP header */ struct ip_header6 const *ip6 = NULL; /* The IPv6 header */ struct udp_header const *udp; /* The UDP header */ uint8_t version; /* IP header version */ bool response = false; /* Was it a response code */ RADIUS_PACKET *current, *original; struct timeval elapsed; struct timeval latency; count++; if (header->caplen <= 5) { INFO("Packet too small, captured %i bytes", header->caplen); return; } /* * Loopback header */ if ((p[0] == 2) && (p[1] == 0) && (p[2] == 0) && (p[3] == 0)) { p += 4; /* * Ethernet header */ } else { p += sizeof(struct ethernet_header); } version = (p[0] & 0xf0) >> 4; switch (version) { case 4: ip = (struct ip_header const *)p; len = (0x0f & ip->ip_vhl) * 4; /* ip_hl specifies length in 32bit words */ p += len; break; case 6: ip6 = (struct ip_header6 const *)p; p += sizeof(struct ip_header6); break; default: DEBUG("IP version invalid %i", version); return; } /* * End of variable length bits, do basic check now to see if packet looks long enough */ len = (p - data) + sizeof(struct udp_header) + (sizeof(radius_packet_t) - 1); /* length value */ if (len > header->caplen) { DEBUG("Packet too small, we require at least %zu bytes, captured %i bytes", (size_t) len, header->caplen); return; } udp = (struct udp_header const *)p; p += sizeof(struct udp_header); /* * With artificial talloc memory limits there's a good chance we can * recover once some requests timeout, so make an effort to deal * with allocation failures gracefully. */ current = rad_alloc(NULL, 0); if (!current) { ERROR("Failed allocating memory to hold decoded packet"); return; } current->timestamp = header->ts; current->data_len = header->caplen - (data - p); memcpy(¤t->data, &p, sizeof(current->data)); /* * Populate IP/UDP fields from PCAP data */ if (ip) { current->src_ipaddr.af = AF_INET; current->src_ipaddr.ipaddr.ip4addr.s_addr = ip->ip_src.s_addr; current->dst_ipaddr.af = AF_INET; current->dst_ipaddr.ipaddr.ip4addr.s_addr = ip->ip_dst.s_addr; } else { current->src_ipaddr.af = AF_INET6; memcpy(¤t->src_ipaddr.ipaddr.ip6addr.s6_addr, &ip6->ip_src.s6_addr, sizeof(current->src_ipaddr.ipaddr.ip6addr.s6_addr)); current->dst_ipaddr.af = AF_INET6; memcpy(¤t->dst_ipaddr.ipaddr.ip6addr.s6_addr, &ip6->ip_dst.s6_addr, sizeof(current->dst_ipaddr.ipaddr.ip6addr.s6_addr)); } current->src_port = ntohs(udp->udp_sport); current->dst_port = ntohs(udp->udp_dport); if (!rad_packet_ok(current, 0, &reason)) { DEBUG("(%i) ** %s **", count, fr_strerror()); DEBUG("(%i) %s Id %i %s:%s:%d -> %s:%d\t+%u.%03u", count, fr_packet_codes[current->code], current->id, event->in->name, fr_inet_ntop(current->src_ipaddr.af, ¤t->src_ipaddr.ipaddr), current->src_port, fr_inet_ntop(current->dst_ipaddr.af, ¤t->dst_ipaddr.ipaddr), current->dst_port, (unsigned int) elapsed.tv_sec, ((unsigned int) elapsed.tv_usec / 1000)); rad_free(¤t); return; } switch (current->code) { case PW_CODE_COA_REQUEST: /* we need a 16 x 0 byte vector for decrypting encrypted VSAs */ original = nullpacket; break; case PW_CODE_ACCOUNTING_RESPONSE: case PW_CODE_AUTHENTICATION_REJECT: case PW_CODE_AUTHENTICATION_ACK: response = true; /* look for a matching request and use it for decoding */ original = rbtree_finddata(request_tree, current); break; case PW_CODE_ACCOUNTING_REQUEST: case PW_CODE_AUTHENTICATION_REQUEST: /* save the request for later matching */ original = rad_alloc_reply(event->conf, current); original->timestamp = header->ts; if (original) { /* just ignore allocation failures */ rbtree_deletebydata(request_tree, original); rbtree_insert(request_tree, original); } /* fallthrough */ default: /* don't attempt to decode any encrypted attributes */ original = NULL; } /* * Decode the data without bothering to check the signatures. */ if (rad_decode(current, original, event->conf->radius_secret) != 0) { rad_free(¤t); fr_perror("decode"); return; } if (filter_vps && rs_filter_packet(current)) { rad_free(¤t); DEBUG("Packet number %d doesn't match", count++); return; } if (event->out) { pcap_dump((void *) (event->out->dumper), header, data); goto check_filter; } rs_tv_sub(&header->ts, &start_pcap, &elapsed); rs_stats_update_count(&stats->gauge, current); if (original) { rs_tv_sub(¤t->timestamp, &original->timestamp, &latency); /* * Update stats for both the request and response types. * * This isn't useful for things like Access-Requests, but will be useful for * CoA and Disconnect Messages, as we get the average latency across both * response types. * * It also justifies allocating 255 instances rs_latency_t. */ rs_stats_update_latency(&stats->exchange[current->code], &latency); rs_stats_update_latency(&stats->exchange[original->code], &latency); /* * Print info about the response. */ DEBUG("(%i) %s Id %i %s:%s:%d %s %s:%d\t+%u.%03u\t+%u.%03u", count, fr_packet_codes[current->code], current->id, event->in->name, fr_inet_ntop(current->src_ipaddr.af, ¤t->src_ipaddr.ipaddr), current->src_port, response ? "<-" : "->", fr_inet_ntop(current->dst_ipaddr.af, ¤t->dst_ipaddr.ipaddr), current->dst_port, (unsigned int) elapsed.tv_sec, ((unsigned int) elapsed.tv_usec / 1000), (unsigned int) latency.tv_sec, ((unsigned int) latency.tv_usec / 1000)); /* * It's the original request */ } else { /* * Print info about the request */ DEBUG("(%i) %s Id %i %s:%s:%d %s %s:%d\t+%u.%03u", count, fr_packet_codes[current->code], current->id, event->in->name, fr_inet_ntop(current->src_ipaddr.af, ¤t->src_ipaddr.ipaddr), current->src_port, response ? "<-" : "->", fr_inet_ntop(current->dst_ipaddr.af, ¤t->dst_ipaddr.ipaddr), current->dst_port, (unsigned int) elapsed.tv_sec, ((unsigned int) elapsed.tv_usec / 1000)); } if (fr_debug_flag > 1) { if (current->vps) { if (event->conf->do_sort) { pairsort(¤t->vps, true); } vp_printlist(log_dst, current->vps); pairfree(¤t->vps); } } /* * We've seen a successful reply to this, so delete it now */ if (original) { rbtree_deletebydata(request_tree, original); } if (!event->conf->to_stdout && (fr_debug_flag > 4)) { rad_print_hex(current); } fflush(log_dst); check_filter: /* * If we're doing filtering, Access-Requests are cached in the * filter tree. */ if (!filter_vps || ((current->code != PW_CODE_AUTHENTICATION_REQUEST) && (current->code != PW_CODE_ACCOUNTING_REQUEST))) { rad_free(¤t); } }
static void check_handler(void *data) { int do_warning = FALSE; uint8_t state[8]; check_handler_t *check = data; if (!check) return; if (!check->inst || !check->handler) { free(check); return; } if (!check->inst->handler_tree) goto done; PTHREAD_MUTEX_LOCK(&(check->inst->handler_mutex)); if (!rbtree_finddata(check->inst->handler_tree, check->handler)) { goto done; } /* * The session has continued *after* this packet. * Don't do a warning. */ if (check->handler->trips > check->trips) { goto done; } /* * No TLS means no warnings. */ if (!check->handler->tls) goto done; /* * If we're being deleted early, it's likely because we * received a transmit from the client that re-uses the * same RADIUS Id, which forces the current packet to be * deleted. In that case, ignore the error. */ if (time(NULL) < (check->handler->timestamp + 3)) goto done; if (!check->handler->finished) { do_warning = TRUE; memcpy(state, check->handler->state, sizeof(state)); } done: PTHREAD_MUTEX_UNLOCK(&(check->inst->handler_mutex)); free(check); if (do_warning) { DEBUGW("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"); DEBUGW("!! EAP session with state 0x%02x%02x%02x%02x%02x%02x%02x%02x did not finish! !!", state[0], state[1], state[2], state[3], state[4], state[5], state[6], state[7]); DEBUGW("!! Please read http://wiki.freeradius.org/guide/Certificate_Compatibility !!"); DEBUGW("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"); } }
/** Register an xlat function. * * @param[in] name xlat name. * @param[in] func xlat function to be called. * @param[in] escape function to sanitize any sub expansions passed to the xlat function. * @param[in] instance of module that's registering the xlat function. * @return 0 on success, -1 on failure */ int xlat_register(char const *name, RAD_XLAT_FUNC func, RADIUS_ESCAPE_STRING escape, void *instance) { xlat_t *c; xlat_t my_xlat; rbnode_t *node; if (!name || !*name) { DEBUG("xlat_register: Invalid xlat name"); return -1; } /* * First time around, build up the tree... * * FIXME: This code should be hoisted out of this function, * and into a global "initialization". But it isn't critical... */ if (!xlat_root) { #ifdef WITH_UNLANG int i; #endif xlat_root = rbtree_create(xlat_cmp, NULL, 0); if (!xlat_root) { DEBUG("xlat_register: Failed to create tree"); return -1; } #ifdef WITH_UNLANG for (i = 0; xlat_foreach_names[i] != NULL; i++) { xlat_register(xlat_foreach_names[i], xlat_foreach, NULL, &xlat_inst[i]); c = xlat_find(xlat_foreach_names[i]); rad_assert(c != NULL); c->internal = true; } #endif #define XLAT_REGISTER(_x) xlat_register(STRINGIFY(_x), xlat_ ## _x, NULL, NULL); \ c = xlat_find(STRINGIFY(_x)); \ rad_assert(c != NULL); \ c->internal = true XLAT_REGISTER(integer); XLAT_REGISTER(strlen); XLAT_REGISTER(length); XLAT_REGISTER(hex); XLAT_REGISTER(string); XLAT_REGISTER(xlat); XLAT_REGISTER(module); XLAT_REGISTER(debug_attr); xlat_register("debug", xlat_debug, NULL, &xlat_inst[0]); c = xlat_find("debug"); rad_assert(c != NULL); c->internal = true; } /* * If it already exists, replace the instance. */ strlcpy(my_xlat.name, name, sizeof(my_xlat.name)); my_xlat.length = strlen(my_xlat.name); c = rbtree_finddata(xlat_root, &my_xlat); if (c) { if (c->internal) { DEBUG("xlat_register: Cannot re-define internal xlat"); return -1; } c->func = func; c->escape = escape; c->instance = instance; return 0; } /* * Doesn't exist. Create it. */ c = talloc_zero(xlat_root, xlat_t); c->func = func; c->escape = escape; strlcpy(c->name, name, sizeof(c->name)); c->length = strlen(c->name); c->instance = instance; node = rbtree_insert_node(xlat_root, c); if (!node) { talloc_free(c); return -1; } /* * Ensure that the data is deleted when the node is * deleted. * * @todo: Maybe this should be the other way around... * when a thing IN the tree is deleted, it's automatically * removed from the tree. But for now, this works. */ (void) talloc_steal(node, c); return 0; }
/* * Find a module on disk or in memory, and link to it. */ static module_entry_t *linkto_module(char const *module_name, CONF_SECTION *cs) { module_entry_t myentry; module_entry_t *node; void *handle = NULL; const module_t *module; strlcpy(myentry.name, module_name, sizeof(myentry.name)); node = rbtree_finddata(module_tree, &myentry); if (node) return node; /* * Link to the module's rlm_FOO{} structure, the same as * the module name. */ #if !defined(WITH_LIBLTDL) && defined(HAVE_DLFCN_H) && defined(RTLD_SELF) module = dlsym(RTLD_SELF, module_name); if (module) goto open_self; #endif /* * Keep the handle around so we can dlclose() it. */ handle = lt_dlopenext(module_name); if (!handle) { cf_log_err_cs(cs, "Failed to link to module '%s': %s\n", module_name, dlerror()); return NULL; } DEBUG3(" (Loaded %s, checking if it's valid)", module_name); /* * libltld MAY core here, if the handle it gives us contains * garbage data. */ module = dlsym(handle, module_name); if (!module) { cf_log_err_cs(cs, "Failed linking to %s structure: %s\n", module_name, dlerror()); dlclose(handle); return NULL; } #if !defined(WITH_LIBLTDL) && defined (HAVE_DLFCN_H) && defined(RTLD_SELF) open_self: #endif /* * Before doing anything else, check if it's sane. */ if (module->magic != RLM_MODULE_MAGIC_NUMBER) { dlclose(handle); cf_log_err_cs(cs, "Invalid version in module '%s'", module_name); return NULL; } /* make room for the module type */ node = talloc_zero(cs, module_entry_t); talloc_set_destructor((void *) node, module_entry_free); strlcpy(node->name, module_name, sizeof(node->name)); node->module = module; node->handle = handle; cf_log_module(cs, "Loaded module %s", module_name); /* * Add the module as "rlm_foo-version" to the configuration * section. */ if (!rbtree_insert(module_tree, node)) { ERROR("Failed to cache module %s", module_name); dlclose(handle); talloc_free(node); return NULL; } return node; }
/* * Find a module instance. */ module_instance_t *find_module_instance(CONF_SECTION *modules, char const *askedname, int do_link) { int check_config_safe = false; CONF_SECTION *cs; char const *name1, *instname; module_instance_t *node, myNode; char module_name[256]; if (!modules) return NULL; /* * Look for the real name. Ignore the first character, * which tells the server "it's OK for this module to not * exist." */ instname = askedname; if (instname[0] == '-') { instname++; } /* * Module instances are declared in the modules{} block * and referenced later by their name, which is the * name2 from the config section, or name1 if there was * no name2. */ cs = cf_section_sub_find_name2(modules, NULL, instname); if (!cs) { ERROR("Cannot find a configuration entry for module \"%s\"", instname); return NULL; } /* * If there's already a module instance, return it. */ strlcpy(myNode.name, instname, sizeof(myNode.name)); node = rbtree_finddata(instance_tree, &myNode); if (node) { return node; } if (!do_link) { return NULL; } name1 = cf_section_name1(cs); /* * Found the configuration entry, hang the node struct off of the * configuration section. If the CS is free'd the instance will * be too. */ node = talloc_zero(cs, module_instance_t); node->cs = cs; /* * Names in the "modules" section aren't prefixed * with "rlm_", so we add it here. */ snprintf(module_name, sizeof(module_name), "rlm_%s", name1); /* * Pull in the module object */ node->entry = linkto_module(module_name, cs); if (!node->entry) { talloc_free(node); /* linkto_module logs any errors */ return NULL; } if (check_config && (node->entry->module->instantiate) && (node->entry->module->type & RLM_TYPE_CHECK_CONFIG_SAFE) == 0) { char const *value = NULL; CONF_PAIR *cp; cp = cf_pair_find(cs, "force_check_config"); if (cp) { value = cf_pair_value(cp); } if (value && (strcmp(value, "yes") == 0)) goto print_inst; cf_log_module(cs, "Skipping instantiation of %s", instname); } else { print_inst: check_config_safe = true; cf_log_module(cs, "Instantiating module \"%s\" from file %s", instname, cf_section_filename(cs)); } strlcpy(node->name, instname, sizeof(node->name)); /* * Parse the module configuration, and setup destructors so the * module's detach method is called when it's instance data is * about to be freed. */ if (module_conf_parse(node, &node->insthandle) < 0) { talloc_free(node); return NULL; } /* * Call the module's instantiation routine. */ if ((node->entry->module->instantiate) && (!check_config || check_config_safe) && ((node->entry->module->instantiate)(cs, node->insthandle) < 0)) { cf_log_err_cs(cs, "Instantiation failed for module \"%s\"", node->name); talloc_free(node); return NULL; } #ifdef HAVE_PTHREAD_H /* * If we're threaded, check if the module is thread-safe. * * If it isn't, we create a mutex. */ if ((node->entry->module->type & RLM_TYPE_THREAD_UNSAFE) != 0) { node->mutex = talloc_zero(node, pthread_mutex_t); /* * Initialize the mutex. */ pthread_mutex_init(node->mutex, NULL); } else { /* * The module is thread-safe. Don't give it a mutex. */ node->mutex = NULL; } #endif rbtree_insert(instance_tree, node); return node; }
/* * Find a module instance. */ module_instance_t *find_module_instance(CONF_SECTION *modules, const char *askedname, int do_link) { int check_config_safe = FALSE; CONF_SECTION *cs; const char *name1, *instname; module_instance_t *node, myNode; char module_name[256]; if (!modules) return NULL; /* * Look for the real name. Ignore the first character, * which tells the server "it's OK for this module to not * exist." */ instname = askedname; if (instname[0] == '-') instname++; /* * Module instances are declared in the modules{} block * and referenced later by their name, which is the * name2 from the config section, or name1 if there was * no name2. */ cs = cf_section_sub_find_name2(modules, NULL, instname); if (cs == NULL) { radlog(L_ERR, "ERROR: Cannot find a configuration entry for module \"%s\".\n", instname); return NULL; } /* * If there's already a module instance, return it. */ strlcpy(myNode.name, instname, sizeof(myNode.name)); node = rbtree_finddata(instance_tree, &myNode); if (node) return node; if (!do_link) return NULL; name1 = cf_section_name1(cs); /* * Found the configuration entry. */ node = rad_malloc(sizeof(*node)); memset(node, 0, sizeof(*node)); node->insthandle = NULL; node->cs = cs; /* * Names in the "modules" section aren't prefixed * with "rlm_", so we add it here. */ snprintf(module_name, sizeof(module_name), "rlm_%s", name1); node->entry = linkto_module(module_name, cs); if (!node->entry) { free(node); /* linkto_module logs any errors */ return NULL; } if (check_config && (node->entry->module->instantiate) && (node->entry->module->type & RLM_TYPE_CHECK_CONFIG_SAFE) == 0) { const char *value = NULL; CONF_PAIR *cp; cp = cf_pair_find(cs, "force_check_config"); if (cp) value = cf_pair_value(cp); if (value && (strcmp(value, "yes") == 0)) goto print_inst; cf_log_module(cs, "Skipping instantiation of %s", instname); } else { print_inst: check_config_safe = TRUE; cf_log_module(cs, "Instantiating module \"%s\" from file %s", instname, cf_section_filename(cs)); } /* * Call the module's instantiation routine. */ if ((node->entry->module->instantiate) && (!check_config || check_config_safe) && ((node->entry->module->instantiate)(cs, &node->insthandle) < 0)) { cf_log_err(cf_sectiontoitem(cs), "Instantiation failed for module \"%s\"", instname); free(node); return NULL; } /* * We're done. Fill in the rest of the data structure, * and link it to the module instance list. */ strlcpy(node->name, instname, sizeof(node->name)); #ifdef HAVE_PTHREAD_H /* * If we're threaded, check if the module is thread-safe. * * If it isn't, we create a mutex. */ if ((node->entry->module->type & RLM_TYPE_THREAD_UNSAFE) != 0) { node->mutex = (pthread_mutex_t *) rad_malloc(sizeof(pthread_mutex_t)); /* * Initialize the mutex. */ pthread_mutex_init(node->mutex, NULL); } else { /* * The module is thread-safe. Don't give it a mutex. */ node->mutex = NULL; } #endif rbtree_insert(instance_tree, node); return node; }
/* * Do the statistics */ static rlm_rcode_t CC_HINT(nonnull) mod_stats(void *instance, void *thread, REQUEST *request) { int i; uint32_t stats_type; rlm_stats_thread_t *t = thread; rlm_stats_t *inst = instance; VALUE_PAIR *vp; rlm_stats_data_t mydata, *stats; fr_cursor_t cursor; char buffer[64]; uint64_t local_stats[sizeof(inst->stats) / sizeof(inst->stats[0])]; /* * Increment counters only in "send foo" sections. * * i.e. only when we have a reply to send. */ if (request->request_state == REQUEST_SEND) { int src_code, dst_code; src_code = request->packet->code; if (src_code >= FR_MAX_PACKET_CODE) src_code = 0; dst_code = request->reply->code; if (dst_code >= FR_MAX_PACKET_CODE) dst_code = 0; t->stats[src_code]++; t->stats[dst_code]++; /* * Update source statistics */ mydata.ipaddr = request->packet->src_ipaddr; stats = rbtree_finddata(t->src, &mydata); if (!stats) { MEM(stats = talloc_zero(t, rlm_stats_data_t)); stats->ipaddr = request->packet->src_ipaddr; stats->created = request->async->recv_time; (void) rbtree_insert(t->src, stats); } stats->last_packet = request->async->recv_time; stats->stats[src_code]++; stats->stats[dst_code]++; /* * Update destination statistics */ mydata.ipaddr = request->packet->dst_ipaddr; stats = rbtree_finddata(t->dst, &mydata); if (!stats) { MEM(stats = talloc_zero(t, rlm_stats_data_t)); stats->ipaddr = request->packet->dst_ipaddr; stats->created = request->async->recv_time; (void) rbtree_insert(t->dst, stats); } stats->last_packet = request->async->recv_time; stats->stats[src_code]++; stats->stats[dst_code]++; /* * @todo - periodically clean up old entries. */ if ((t->last_global_update + NANOSEC) > request->async->recv_time) { return RLM_MODULE_UPDATED; } t->last_global_update = request->async->recv_time; pthread_mutex_lock(&inst->mutex); for (i = 0; i < FR_MAX_PACKET_CODE; i++) { inst->stats[i] += t->stats[i]; t->stats[i] = 0; } pthread_mutex_unlock(&inst->mutex); return RLM_MODULE_UPDATED; } /* * Ignore "authenticate" and anything other than Status-Server */ if ((request->request_state != REQUEST_RECV) || (request->packet->code != FR_CODE_STATUS_SERVER)) { return RLM_MODULE_NOOP; } vp = fr_pair_find_by_da(request->packet->vps, attr_freeradius_stats4_type, TAG_ANY); if (!vp) { stats_type = FR_FREERADIUS_STATS4_TYPE_VALUE_GLOBAL; } else { stats_type = vp->vp_uint32; } /* * Create attributes based on the statistics. */ fr_cursor_init(&cursor, &request->reply->vps); MEM(pair_update_reply(&vp, attr_freeradius_stats4_type) >= 0); vp->vp_uint32 = stats_type; switch (stats_type) { case FR_FREERADIUS_STATS4_TYPE_VALUE_GLOBAL: /* global */ /* * Merge our stats with the global stats, and then copy * the global stats to a thread-local variable. * * The copy helps minimize mutex contention. */ pthread_mutex_lock(&inst->mutex); for (i = 0; i < FR_MAX_PACKET_CODE; i++) { inst->stats[i] += t->stats[i]; t->stats[i] = 0; } memcpy(&local_stats, inst->stats, sizeof(inst->stats)); pthread_mutex_unlock(&inst->mutex); vp = NULL; break; case FR_FREERADIUS_STATS4_TYPE_VALUE_CLIENT: /* src */ vp = fr_pair_find_by_da(request->packet->vps, attr_freeradius_stats4_ipv4_address, TAG_ANY); if (!vp) vp = fr_pair_find_by_da(request->packet->vps, attr_freeradius_stats4_ipv6_address, TAG_ANY); if (!vp) return RLM_MODULE_NOOP; mydata.ipaddr = vp->vp_ip; coalesce(local_stats, t, offsetof(rlm_stats_thread_t, src), &mydata); break; case FR_FREERADIUS_STATS4_TYPE_VALUE_LISTENER: /* dst */ vp = fr_pair_find_by_da(request->packet->vps, attr_freeradius_stats4_ipv4_address, TAG_ANY); if (!vp) vp = fr_pair_find_by_da(request->packet->vps, attr_freeradius_stats4_ipv6_address, TAG_ANY); if (!vp) return RLM_MODULE_NOOP; mydata.ipaddr = vp->vp_ip; coalesce(local_stats, t, offsetof(rlm_stats_thread_t, dst), &mydata); break; default: REDEBUG("Invalid value '%d' for FreeRADIUS-Stats4-type", stats_type); return RLM_MODULE_FAIL; } if (vp ) { vp = fr_pair_copy(request->reply, vp); if (vp) { fr_cursor_append(&cursor, vp); (void) fr_cursor_tail(&cursor); } } strcpy(buffer, "FreeRADIUS-Stats4-"); for (i = 0; i < FR_MAX_PACKET_CODE; i++) { fr_dict_attr_t const *da; if (!local_stats[i]) continue; strlcpy(buffer + 18, fr_packet_codes[i], sizeof(buffer) - 18); da = fr_dict_attr_by_name(dict_radius, buffer); if (!da) continue; vp = fr_pair_afrom_da(request->reply, da); if (!vp) return RLM_MODULE_FAIL; vp->vp_uint64 = local_stats[i]; fr_cursor_append(&cursor, vp); (void) fr_cursor_tail(&cursor); } return RLM_MODULE_OK; }
static int filter_packet(RADIUS_PACKET *packet) { VALUE_PAIR *check_item; VALUE_PAIR *vp; unsigned int pass, fail; int compare; pass = fail = 0; for (vp = packet->vps; vp != NULL; vp = vp->next) { for (check_item = filter_vps; check_item != NULL; check_item = check_item->next) if ((check_item->da == vp->da) && (check_item->op != T_OP_SET)) { compare = paircmp(check_item, vp); if (compare == 1) pass++; else fail++; } } if (fail == 0 && pass != 0) { /* * Cache authentication requests, as the replies * may not match the RADIUS filter. */ if ((packet->code == PW_AUTHENTICATION_REQUEST) || (packet->code == PW_ACCOUNTING_REQUEST)) { rbtree_deletebydata(filter_tree, packet); if (!rbtree_insert(filter_tree, packet)) { oom: fprintf(stderr, "radsniff: Out of memory\n"); exit(1); } } return 0; /* matched */ } /* * Don't create erroneous matches. */ if ((packet->code == PW_AUTHENTICATION_REQUEST) || (packet->code == PW_ACCOUNTING_REQUEST)) { rbtree_deletebydata(filter_tree, packet); return 1; } /* * Else see if a previous Access-Request * matched. If so, also print out the * matching accept, reject, or challenge. */ if ((packet->code == PW_AUTHENTICATION_ACK) || (packet->code == PW_AUTHENTICATION_REJECT) || (packet->code == PW_ACCESS_CHALLENGE) || (packet->code == PW_ACCOUNTING_RESPONSE)) { RADIUS_PACKET *reply; /* * This swaps the various fields. */ reply = rad_alloc_reply(NULL, packet); if (!reply) goto oom; compare = 1; if (rbtree_finddata(filter_tree, reply)) { compare = 0; } rad_free(&reply); return compare; } return 1; }
return rbtree_finddata(tree, &find); } /** Retrieve module/thread specific instance data for a module * * @param[in] mod_inst Module specific instance to find thread_data for. * @return * - Thread specific instance data on success. * - NULL if module has no thread instance data. */ void *module_thread_instance_by_data(void *mod_inst) { rbtree_t *tree = module_thread_inst_tree; module_thread_instance_t find = { .mod_inst = mod_inst }, *found; found = rbtree_finddata(tree, &find); if (!found) return NULL; return found->data; } /** Destructor for module_thread_instance_t * * @note This cannot be converted to a talloc destructor, * as we need to call thread_detach *before* any of the children * of the talloc ctx are freed. */ static void _module_thread_instance_free(void *to_free) { module_thread_instance_t *ti = talloc_get_type_abort(to_free, module_thread_instance_t);
/** Add a client to a RADCLIENT_LIST * * @param clients list to add client to, may be NULL if global client list is being used. * @param client to add. * @return true on success, false on failure. */ bool client_add(RADCLIENT_LIST *clients, RADCLIENT *client) { RADCLIENT *old; char buffer[INET6_ADDRSTRLEN + 3]; if (!client) return false; /* * Hack to fixup wildcard clients * * If the IP is all zeros, with a 32 or 128 bit netmask * assume the user meant to configure 0.0.0.0/0 instead * of 0.0.0.0/32 - which would require the src IP of * the client to be all zeros. */ if (fr_inaddr_any(&client->ipaddr) == 1) switch (client->ipaddr.af) { case AF_INET: if (client->ipaddr.prefix == 32) client->ipaddr.prefix = 0; break; case AF_INET6: if (client->ipaddr.prefix == 128) client->ipaddr.prefix = 0; break; default: rad_assert(0); } fr_ntop(buffer, sizeof(buffer), &client->ipaddr); DEBUG3("Adding client %s (%s) to prefix tree %i", buffer, client->longname, client->ipaddr.prefix); /* * If the client also defines a server, do that now. */ if (client->defines_coa_server) if (!realm_home_server_add(client->coa_server)) return false; /* * If "clients" is NULL, it means add to the global list, * unless we're trying to add it to a virtual server... */ if (!clients) { if (client->server != NULL) { CONF_SECTION *cs; cs = cf_section_sub_find_name2(main_config.config, "server", client->server); if (!cs) { ERROR("Failed to find virtual server %s", client->server); return false; } /* * If the client list already exists, use that. * Otherwise, create a new client list. */ clients = cf_data_find(cs, "clients"); if (!clients) { clients = client_list_init(cs); if (!clients) { ERROR("Out of memory"); return false; } if (cf_data_add(cs, "clients", clients, (void (*)(void *)) client_list_free) < 0) { ERROR("Failed to associate clients with virtual server %s", client->server); client_list_free(clients); return false; } } } else { /* * Initialize the global list, if not done already. */ if (!root_clients) { root_clients = client_list_init(NULL); if (!root_clients) return false; } clients = root_clients; } } /* * Create a tree for it. */ if (!clients->trees[client->ipaddr.prefix]) { clients->trees[client->ipaddr.prefix] = rbtree_create(clients, client_ipaddr_cmp, NULL, 0); if (!clients->trees[client->ipaddr.prefix]) { return false; } } #define namecmp(a) ((!old->a && !client->a) || (old->a && client->a && (strcmp(old->a, client->a) == 0))) /* * Cannot insert the same client twice. */ old = rbtree_finddata(clients->trees[client->ipaddr.prefix], client); if (old) { /* * If it's a complete duplicate, then free the new * one, and return "OK". */ if ((fr_ipaddr_cmp(&old->ipaddr, &client->ipaddr) == 0) && (old->ipaddr.prefix == client->ipaddr.prefix) && namecmp(longname) && namecmp(secret) && namecmp(shortname) && namecmp(nas_type) && namecmp(login) && namecmp(password) && namecmp(server) && #ifdef WITH_DYNAMIC_CLIENTS (old->lifetime == client->lifetime) && namecmp(client_server) && #endif #ifdef WITH_COA namecmp(coa_name) && (old->coa_server == client->coa_server) && (old->coa_pool == client->coa_pool) && #endif (old->message_authenticator == client->message_authenticator)) { WARN("Ignoring duplicate client %s", client->longname); client_free(client); return true; } ERROR("Failed to add duplicate client %s", client->shortname); return false; } #undef namecmp /* * Other error adding client: likely is fatal. */ if (!rbtree_insert(clients->trees[client->ipaddr.prefix], client)) { return false; } #ifdef WITH_STATS if (!tree_num) { tree_num = rbtree_create(clients, client_num_cmp, NULL, 0); } #ifdef WITH_DYNAMIC_CLIENTS /* * More catching of clients added by rlm_sql. * * The sql modules sets the dynamic flag BEFORE calling * us. The client_afrom_request() function sets it AFTER * calling us. */ if (client->dynamic && (client->lifetime == 0)) { RADCLIENT *network; /* * If there IS an enclosing network, * inherit the lifetime from it. */ network = client_find(clients, &client->ipaddr, client->proto); if (network) { client->lifetime = network->lifetime; } } #endif client->number = tree_num_max; tree_num_max++; if (tree_num) rbtree_insert(tree_num, client); #endif if (client->ipaddr.prefix < clients->min_prefix) { clients->min_prefix = client->ipaddr.prefix; } (void) talloc_steal(clients, client); /* reparent it */ return true; }
/** * @brief Register an xlat function. * * @param module xlat name * @param func xlat function to be called * @param instance argument to xlat function * @return 0 on success, -1 on failure */ int xlat_register(const char *module, RAD_XLAT_FUNC func, void *instance) { xlat_t *c; xlat_t my_xlat; if (!module || !*module) { DEBUG("xlat_register: Invalid module name"); return -1; } /* * First time around, build up the tree... * * FIXME: This code should be hoisted out of this function, * and into a global "initialization". But it isn't critical... */ if (!xlat_root) { int i; #ifdef HAVE_REGEX_H char buffer[2]; #endif xlat_root = rbtree_create(xlat_cmp, free, 0); if (!xlat_root) { DEBUG("xlat_register: Failed to create tree."); return -1; } /* * Register the internal packet xlat's. */ for (i = 0; internal_xlat[i] != NULL; i++) { xlat_register(internal_xlat[i], xlat_packet, &xlat_inst[i]); c = xlat_find(internal_xlat[i]); rad_assert(c != NULL); c->internal = TRUE; } #ifdef WITH_UNLANG for (i = 0; xlat_foreach_names[i] != NULL; i++) { xlat_register(xlat_foreach_names[i], xlat_foreach, &xlat_inst[i]); c = xlat_find(xlat_foreach_names[i]); rad_assert(c != NULL); c->internal = TRUE; } #endif /* * New name: "control" */ xlat_register("control", xlat_packet, &xlat_inst[0]); c = xlat_find("control"); rad_assert(c != NULL); c->internal = TRUE; #define XLAT_REGISTER(_x) xlat_register(Stringify(_x), xlat_ ## _x, NULL); \ c = xlat_find(Stringify(_x)); \ rad_assert(c != NULL); \ c->internal = TRUE XLAT_REGISTER(integer); XLAT_REGISTER(hex); XLAT_REGISTER(base64); XLAT_REGISTER(string); XLAT_REGISTER(module); #ifdef HAVE_REGEX_H /* * Register xlat's for regexes. */ buffer[1] = '\0'; for (i = 0; i <= REQUEST_MAX_REGEX; i++) { buffer[0] = '0' + i; xlat_register(buffer, xlat_regex, &xlat_inst[i]); c = xlat_find(buffer); rad_assert(c != NULL); c->internal = TRUE; } #endif /* HAVE_REGEX_H */ xlat_register("debug", xlat_debug, &xlat_inst[0]); c = xlat_find("debug"); rad_assert(c != NULL); c->internal = TRUE; } /* * If it already exists, replace the instance. */ strlcpy(my_xlat.module, module, sizeof(my_xlat.module)); my_xlat.length = strlen(my_xlat.module); c = rbtree_finddata(xlat_root, &my_xlat); if (c) { if (c->internal) { DEBUG("xlat_register: Cannot re-define internal xlat"); return -1; } c->do_xlat = func; c->instance = instance; return 0; } /* * Doesn't exist. Create it. */ c = rad_malloc(sizeof(*c)); memset(c, 0, sizeof(*c)); c->do_xlat = func; strlcpy(c->module, module, sizeof(c->module)); c->length = strlen(c->module); c->instance = instance; rbtree_insert(xlat_root, c); return 0; }
/* * Find a module instance. */ module_instance_t *find_module_instance(CONF_SECTION *modules, char const *askedname, int do_link) { int check_config_safe = false; CONF_SECTION *cs; char const *name1, *instname; module_instance_t *node, myNode; char module_name[256]; if (!modules) return NULL; /* * Look for the real name. Ignore the first character, * which tells the server "it's OK for this module to not * exist." */ instname = askedname; if (instname[0] == '-') instname++; /* * Module instances are declared in the modules{} block * and referenced later by their name, which is the * name2 from the config section, or name1 if there was * no name2. */ cs = cf_section_sub_find_name2(modules, NULL, instname); if (cs == NULL) { ERROR("Cannot find a configuration entry for module \"%s\".\n", instname); return NULL; } /* * If there's already a module instance, return it. */ strlcpy(myNode.name, instname, sizeof(myNode.name)); node = rbtree_finddata(instance_tree, &myNode); if (node) return node; if (!do_link) return NULL; name1 = cf_section_name1(cs); /* * Found the configuration entry. */ node = talloc_zero(cs, module_instance_t); node->insthandle = NULL; node->cs = cs; /* * Names in the "modules" section aren't prefixed * with "rlm_", so we add it here. */ snprintf(module_name, sizeof(module_name), "rlm_%s", name1); node->entry = linkto_module(module_name, cs); if (!node->entry) { talloc_free(node); /* linkto_module logs any errors */ return NULL; } if (check_config && (node->entry->module->instantiate) && (node->entry->module->type & RLM_TYPE_CHECK_CONFIG_SAFE) == 0) { char const *value = NULL; CONF_PAIR *cp; cp = cf_pair_find(cs, "force_check_config"); if (cp) value = cf_pair_value(cp); if (value && (strcmp(value, "yes") == 0)) goto print_inst; cf_log_module(cs, "Skipping instantiation of %s", instname); } else { print_inst: check_config_safe = true; cf_log_module(cs, "Instantiating module \"%s\" from file %s", instname, cf_section_filename(cs)); } /* * If there is supposed to be instance data, allocate it now. * Also parse the configuration data, if required. */ if (node->entry->module->inst_size) { /* FIXME: make this rlm_config_t ?? */ node->insthandle = talloc_zero_array(node, uint8_t, node->entry->module->inst_size); rad_assert(node->insthandle != NULL); /* * So we can see where this configuration is from * FIXME: set it to rlm_NAME_t, or some such thing */ talloc_set_name(node->insthandle, "rlm_config_t"); if (node->entry->module->config && (cf_section_parse(cs, node->insthandle, node->entry->module->config) < 0)) { cf_log_err_cs(cs, "Invalid configuration for module \"%s\"", instname); talloc_free(node); return NULL; } /* * Set the destructor. */ if (node->entry->module->detach) { talloc_set_destructor((void *) node->insthandle, node->entry->module->detach); } } /* * Call the module's instantiation routine. */ if ((node->entry->module->instantiate) && (!check_config || check_config_safe) && ((node->entry->module->instantiate)(cs, node->insthandle) < 0)) { cf_log_err_cs(cs, "Instantiation failed for module \"%s\"", instname); talloc_free(node); return NULL; } /* * We're done. Fill in the rest of the data structure, * and link it to the module instance list. */ strlcpy(node->name, instname, sizeof(node->name)); #ifdef HAVE_PTHREAD_H /* * If we're threaded, check if the module is thread-safe. * * If it isn't, we create a mutex. */ if ((node->entry->module->type & RLM_TYPE_THREAD_UNSAFE) != 0) { node->mutex = talloc_zero(node, pthread_mutex_t); /* * Initialize the mutex. */ pthread_mutex_init(node->mutex, NULL); } else { /* * The module is thread-safe. Don't give it a mutex. */ node->mutex = NULL; } #endif rbtree_insert(instance_tree, node); return node; }