/* * Load a sub-module list, as found inside an Auth-Type foo {} * block */ static int load_subcomponent_section(modcallable *parent, CONF_SECTION *cs, rbtree_t *components, const DICT_ATTR *dattr, int comp) { indexed_modcallable *subcomp; modcallable *ml; DICT_VALUE *dval; const char *name2 = cf_section_name2(cs); rad_assert(comp >= RLM_COMPONENT_AUTH); rad_assert(comp < RLM_COMPONENT_COUNT); /* * Sanity check. */ if (!name2) { cf_log_err(cf_sectiontoitem(cs), "No name specified for %s block", section_type_value[comp].typename); return 1; }
/* * Parse TLS configuration * * If the option given by 'attr' is set, we find the config section * of that name and use that for the TLS configuration. If not, we * fall back to compatibility mode and read the TLS options from * the 'tls' section. */ fr_tls_server_conf_t *eaptls_conf_parse(CONF_SECTION *cs, char const *attr) { char const *tls_conf_name; CONF_PAIR *cp; CONF_SECTION *parent; CONF_SECTION *tls_cs; fr_tls_server_conf_t *tls_conf; if (!cs) return NULL; rad_assert(attr != NULL); parent = cf_item_parent(cf_sectiontoitem(cs)); cp = cf_pair_find(cs, attr); if (cp) { tls_conf_name = cf_pair_value(cp); tls_cs = cf_section_sub_find_name2(parent, TLS_CONFIG_SECTION, tls_conf_name); if (!tls_cs) { ERROR("Cannot find tls config '%s'", tls_conf_name); return NULL; } } else { /* * If we can't find the section given by the 'attr', we * fall-back to looking for the "tls" section, as in * previous versions. * * We don't fall back if the 'attr' is specified, but we can't * find the section - that is just a config error. */ INFO("debug: '%s' option missing, trying to use legacy configuration", attr); tls_cs = cf_section_sub_find(parent, "tls"); } if (!tls_cs) return NULL; tls_conf = tls_server_conf_parse(tls_cs); if (!tls_conf) return NULL; /* * The EAP RFC's say 1020, but we're less picky. */ if (tls_conf->fragment_size < 100) { ERROR("Fragment size is too small"); return NULL; } /* * The maximum size for a RADIUS packet is 4096, * minus the header (20), Message-Authenticator (18), * and State (18), etc. results in about 4000 bytes of data * that can be devoted *solely* to EAP. */ if (tls_conf->fragment_size > 4000) { ERROR("Fragment size is too large"); return NULL; } /* * Account for the EAP header (4), and the EAP-TLS header * (6), as per Section 4.2 of RFC 2716. What's left is * the maximum amount of data we read from a TLS buffer. */ tls_conf->fragment_size -= 10; return tls_conf; }
/** Convert an 'update' config section into an attribute map. * * Uses 'name2' of section to set default request and lists. * * @param[in] cs the update section * @param[out] head Where to store the head of the map. * @param[in] dst_list_def The default destination list, usually dictated by * the section the module is being called in. * @param[in] src_list_def The default source list, usually dictated by the * section the module is being called in. * @param[in] max number of mappings to process. * @return -1 on error, else 0. */ int radius_attrmap(CONF_SECTION *cs, value_pair_map_t **head, pair_lists_t dst_list_def, pair_lists_t src_list_def, unsigned int max) { char const *cs_list, *p; request_refs_t request_def = REQUEST_CURRENT; CONF_ITEM *ci; CONF_PAIR *cp; unsigned int total = 0; value_pair_map_t **tail, *map; TALLOC_CTX *ctx; *head = NULL; tail = head; if (!cs) return 0; /* * The first map has cs as the parent. * The rest have the previous map as the parent. */ ctx = cs; ci = cf_sectiontoitem(cs); cs_list = p = cf_section_name2(cs); if (cs_list) { request_def = radius_request_name(&p, REQUEST_CURRENT); if (request_def == REQUEST_UNKNOWN) { cf_log_err(ci, "Default request specified " "in mapping section is invalid"); return -1; } dst_list_def = fr_str2int(pair_lists, p, PAIR_LIST_UNKNOWN); if (dst_list_def == PAIR_LIST_UNKNOWN) { cf_log_err(ci, "Default list \"%s\" specified " "in mapping section is invalid", p); return -1; } } for (ci = cf_item_find_next(cs, NULL); ci != NULL; ci = cf_item_find_next(cs, ci)) { if (total++ == max) { cf_log_err(ci, "Map size exceeded"); goto error; } if (!cf_item_is_pair(ci)) { cf_log_err(ci, "Entry is not in \"attribute = " "value\" format"); goto error; } cp = cf_itemtopair(ci); map = radius_cp2map(ctx, cp, request_def, dst_list_def, REQUEST_CURRENT, src_list_def); if (!map) { goto error; } ctx = *tail = map; tail = &(map->next); } return 0; error: talloc_free(*head); return -1; }
/* * 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; }
/* * 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; }
/** Convert an 'update' config section into an attribute map. * * Uses 'name2' of section to set default request and lists. * * @param[in] parent to convert to map. * @param[out] head Where to store the head of the map. * @param[in] dst_list_def The default destination list, usually dictated by * the section the module is being called in. * @param[in] src_list_def The default source list, usually dictated by the * section the module is being called in. * @param[in] max number of mappings to process. * @return -1 on error, else 0. */ int radius_attrmap(CONF_SECTION *parent, value_pair_map_t **head, pair_lists_t dst_list_def, pair_lists_t src_list_def, unsigned int max) { const char *cs_list, *p; request_refs_t request_def = REQUEST_CURRENT; CONF_SECTION *cs; CONF_ITEM *ci = cf_sectiontoitem(cs); CONF_PAIR *cp; unsigned int total = 0; value_pair_map_t **tail, *map; *head = NULL; tail = head; if (!parent) return 0; cs = cf_section_sub_find(parent, "update"); if (!cs) return 0; cs_list = p = cf_section_name2(cs); if (cs_list) { request_def = radius_request_name(&p, REQUEST_UNKNOWN); if (request_def == REQUEST_UNKNOWN) { cf_log_err(ci, "Default request specified " "in mapping section is invalid"); return -1; } dst_list_def = fr_str2int(pair_lists, p, PAIR_LIST_UNKNOWN); if (dst_list_def == PAIR_LIST_UNKNOWN) { cf_log_err(ci, "Default list \"%s\" specified " "in mapping section is invalid", p); return -1; } } for (ci = cf_item_find_next(cs, NULL); ci != NULL; ci = cf_item_find_next(cs, ci)) { if (total++ == max) { cf_log_err(ci, "Map size exceeded"); goto error; } if (!cf_item_is_pair(ci)) { cf_log_err(ci, "Entry is not in \"attribute = " "value\" format"); goto error; } cp = cf_itemtopair(ci); map = radius_cp2map(cp, request_def, dst_list_def, REQUEST_CURRENT, src_list_def); if (!map) { goto error; } *tail = map; tail = &(map->next); } return 0; error: radius_mapfree(head); return -1; }
/** Convert an 'update' config section into an attribute map. * * Uses 'name2' of section to set default request and lists. * Copied from map_afrom_cs, except that list assignments can have the RHS * be a bare word. * * @param[in] cs the update section * @param[out] out Where to store the head of the map. * @param[in] dst_list_def The default destination list, usually dictated by * the section the module is being called in. * @param[in] src_list_def The default source list, usually dictated by the * section the module is being called in. * @param[in] max number of mappings to process. * @return -1 on error, else 0. */ static int ldap_map_afrom_cs(value_pair_map_t **out, CONF_SECTION *cs, pair_lists_t dst_list_def, pair_lists_t src_list_def, unsigned int max) { char const *cs_list, *p; request_refs_t request_def = REQUEST_CURRENT; CONF_ITEM *ci; unsigned int total = 0; value_pair_map_t **tail, *map; TALLOC_CTX *ctx; *out = NULL; tail = out; if (!cs) return 0; /* * The first map has cs as the parent. * The rest have the previous map as the parent. */ ctx = cs; ci = cf_sectiontoitem(cs); cs_list = p = cf_section_name2(cs); if (cs_list) { request_def = radius_request_name(&p, REQUEST_CURRENT); if (request_def == REQUEST_UNKNOWN) { cf_log_err(ci, "Default request specified " "in mapping section is invalid"); return -1; } dst_list_def = fr_str2int(pair_lists, p, PAIR_LIST_UNKNOWN); if (dst_list_def == PAIR_LIST_UNKNOWN) { cf_log_err(ci, "Default list \"%s\" specified " "in mapping section is invalid", p); return -1; } } for (ci = cf_item_find_next(cs, NULL); ci != NULL; ci = cf_item_find_next(cs, ci)) { char const *attr; FR_TOKEN type; CONF_PAIR *cp; cp = cf_itemtopair(ci); type = cf_pair_value_type(cp); if (total++ == max) { cf_log_err(ci, "Map size exceeded"); goto error; } if (!cf_item_is_pair(ci)) { cf_log_err(ci, "Entry is not in \"attribute = value\" format"); goto error; } cp = cf_itemtopair(ci); /* * Look for "list: OP BARE_WORD". If it exists, * we can make the RHS a bare word. Otherwise, * just call map_afrom_cp() * * Otherwise, the map functions check the RHS of * list assignments, and complain that the RHS * isn't another list. */ attr = cf_pair_attr(cp); p = strrchr(attr, ':'); if (!p || (p[1] != '\0') || (type == T_DOUBLE_QUOTED_STRING)) { if (map_afrom_cp(ctx, &map, cp, request_def, dst_list_def, REQUEST_CURRENT, src_list_def) < 0) { goto error; } } else { ssize_t slen; char const *value; map = talloc_zero(ctx, value_pair_map_t); map->op = cf_pair_operator(cp); map->ci = cf_pairtoitem(cp); slen = tmpl_afrom_attr_str(ctx, &map->lhs, attr, request_def, dst_list_def); if (slen <= 0) { char *spaces, *text; fr_canonicalize_error(ctx, &spaces, &text, slen, attr); cf_log_err(ci, "Failed parsing list reference"); cf_log_err(ci, "%s", text); cf_log_err(ci, "%s^ %s", spaces, fr_strerror()); talloc_free(spaces); talloc_free(text); goto error; } if (map->lhs->type != TMPL_TYPE_LIST) { cf_log_err(map->ci, "Invalid list name"); goto error; } if (map->op != T_OP_ADD) { cf_log_err(map->ci, "Only '+=' operator is permitted for valuepair to list mapping"); goto error; } value = cf_pair_value(cp); if (!value) { cf_log_err(map->ci, "No value specified for list assignment"); goto error; } /* * the RHS type is a bare word or single * quoted string. We don't want it being * interpreted as a list or attribute * reference, so we force the RHS to be a * literal. */ slen = tmpl_afrom_str(ctx, &map->rhs, value, T_SINGLE_QUOTED_STRING, request_def, dst_list_def); if (slen <= 0) { char *spaces, *text; fr_canonicalize_error(ctx, &spaces, &text, slen, value); cf_log_err(ci, "Failed parsing string"); cf_log_err(ci, "%s", text); cf_log_err(ci, "%s^ %s", spaces, fr_strerror()); talloc_free(spaces); talloc_free(text); goto error; } /* * And unlike map_afrom_cp(), we do NOT * try to parse the RHS as a list * reference. It's a literal, and we * leave it as a literal. */ rad_assert(map->rhs->type == TMPL_TYPE_LITERAL); } ctx = *tail = map; tail = &(map->next); } return 0; error: TALLOC_FREE(*out); return -1; }