/** Iterate over all client attribute pairs and create client pair data using JSON element names * * If we hit a CONF_SECTION we recurse and process its CONF_PAIRS as well to support nested * configurations sections. * * @param client The new client config section using the mapped names. * @param map The client attribute section from the module configuration. * @param json JSON object representation of a client document fetched from Couchbase. * @param docid Document id. * @return Returns 0 on success, -1 on error. */ int _mod_client_map_section(CONF_SECTION *client, CONF_SECTION const *map, json_object *json, char const *docid) { CONF_ITEM const *ci; for (ci = cf_item_find_next(map, NULL); ci != NULL; ci = cf_item_find_next(map, ci)) { CONF_PAIR const *cp; char const *attribute; char const *element; json_object *jval; /* * Recursively process map subsection */ if (cf_item_is_section(ci)) { CONF_SECTION *cs, *cc; /* local scoped for new section */ cs = cf_itemtosection(ci); cc = cf_section_alloc(client, cf_section_name1(cs), cf_section_name2(cs)); if (!cc) return -1; cf_section_add(client, cc); if (_mod_client_map_section(cc, cs, json, docid) != 0) { return -1; } /* continue on to the next item */ continue; } /* create pair from item and get attribute name and value */ cp = cf_itemtopair(ci); attribute = cf_pair_attr(cp); element = cf_pair_value(cp); /* attempt to find element in json object */ if (!json_object_object_get_ex(json, element, &jval)) { /* skip this item */ continue; } /* allocate config pair */ cp = cf_pair_alloc(client, attribute, json_object_get_string(jval), T_OP_SET, T_SINGLE_QUOTED_STRING); /* check pair */ if (!cp) { ERROR("rlm_couchbase: failed allocating config pair '%s' = '%s'", attribute, json_object_get_string(jval)); return -1; } /* add pair to section */ cf_item_add(client, cf_pairtoitem(cp)); } /* return success */ return 0; }
/** Build a JSON object map from the configuration "map" section * * Parse the "map" section from the module configuration file and store this * as a JSON object (key/value list) in the module instance. This map will be * used to lookup and map attributes for all incoming accounting requests. * * @param conf Configuration section. * @param instance The module instance. * @return Returns 0 on success, -1 on error. */ int mod_build_attribute_element_map(CONF_SECTION *conf, void *instance) { rlm_couchbase_t *inst = instance; /* our module instance */ CONF_SECTION *cs; /* module config section */ CONF_ITEM *ci; /* config item */ CONF_PAIR *cp; /* conig pair */ const char *attribute, *element; /* attribute and element names */ /* find map section */ cs = cf_section_sub_find(conf, "map"); /* check section */ if (!cs) { ERROR("rlm_couchbase: failed to find 'map' section in config"); /* fail */ return -1; } /* create attribute map object */ inst->map = json_object_new_object(); /* parse update section */ for (ci = cf_item_find_next(cs, NULL); ci != NULL; ci = cf_item_find_next(cs, ci)) { /* validate item */ if (!cf_item_is_pair(ci)) { ERROR("rlm_couchbase: failed to parse invalid item in 'map' section"); /* free map */ if (inst->map) { json_object_put(inst->map); } /* fail */ return -1; } /* get value pair from item */ cp = cf_itemtopair(ci); /* get pair name (element name) */ element = cf_pair_attr(cp); /* get pair value (attribute name) */ attribute = cf_pair_value(cp); /* add pair name and value */ json_object_object_add(inst->map, attribute, json_object_new_string(element)); /* debugging */ DEBUG("rlm_couchbase: added attribute '%s' to element '%s' map to object", attribute, element); } /* debugging */ DEBUG("rlm_couchbase: built attribute to element map %s", json_object_to_json_string(inst->map)); /* return */ return 0; }
/* * Xlat for %{config:section.subsection.attribute} */ static size_t xlat_config(void *instance, REQUEST *request, const char *fmt, char *out, size_t outlen) { const char *value; CONF_PAIR *cp; CONF_ITEM *ci; char buffer[1024]; request = request; /* -Wunused */ instance = instance; /* -Wunused */ /* * Expand it safely. */ if (!radius_xlat(buffer, sizeof(buffer), fmt, request, config_escape_func, NULL)) { return 0; } ci = cf_reference_item(request->root->config, request->root->config, buffer); if (!ci || !cf_item_is_pair(ci)) { *out = '\0'; return 0; } cp = cf_itemtopair(ci); /* * Ensure that we only copy what's necessary. * * If 'outlen' is too small, then the output is chopped to fit. */ value = cf_pair_value(cp); if (!value) { out[0] = '\0'; return 0; } if (outlen > strlen(value)) { outlen = strlen(value) + 1; } strlcpy(out, value, outlen); return strlen(out); }
/** Iterate over pairs in mapping section creating equivalent client pairs from LDAP values * * If we hit a CONF_SECTION we recurse and process its CONF_PAIRS too. * * @param[in] inst rlm_ldap configuration. * @param[out] client config section. * @param[in] map section. * @param[in] conn LDAP connection. * @param[in] entry returned from search. * @return 0 on success else -1 on error. */ static int rlm_ldap_client_map_section(ldap_instance_t const *inst, CONF_SECTION *client, CONF_SECTION const *map, ldap_handle_t *conn, LDAPMessage *entry) { CONF_ITEM const *ci; for (ci = cf_item_find_next(map, NULL); ci != NULL; ci = cf_item_find_next(map, ci)) { CONF_PAIR const *cp; char **value; char const *attr; /* * Recursively process map subsection */ if (cf_item_is_section(ci)) { CONF_SECTION *cs, *cc; cs = cf_itemtosection(ci); cc = cf_section_alloc(client, cf_section_name1(cs), cf_section_name2(cs)); if (!cc) return -1; cf_section_add(client, cc); if (rlm_ldap_client_map_section(inst, cc, cs, conn, entry) < 0) return -1; continue; } cp = cf_itemtopair(ci); attr = cf_pair_attr(cp); value = ldap_get_values(conn->handle, entry, cf_pair_value(cp)); if (!value) continue; cp = cf_pair_alloc(client, attr, value[0], T_OP_SET, T_SINGLE_QUOTED_STRING); if (!cp) { LDAP_ERR("Failed allocing pair \"%s\" = \"%s\"", attr, value[0]); return -1; } cf_item_add(client, cf_pairtoitem(cp)); } return 0; }
/* * Xlat for %{config:section.subsection.attribute} */ static ssize_t xlat_config(UNUSED void *instance, REQUEST *request, char const *fmt, char *out, size_t outlen) { char const *value; CONF_PAIR *cp; CONF_ITEM *ci; char buffer[1024]; /* * Expand it safely. */ if (radius_xlat(buffer, sizeof(buffer), request, fmt, config_escape_func, NULL) < 0) { return 0; } ci = cf_reference_item(request->root->config, request->root->config, buffer); if (!ci || !cf_item_is_pair(ci)) { REDEBUG("Config item \"%s\" does not exist", fmt); *out = '\0'; return -1; } cp = cf_itemtopair(ci); /* * Ensure that we only copy what's necessary. * * If 'outlen' is too small, then the output is chopped to fit. */ value = cf_pair_value(cp); if (!value) { out[0] = '\0'; return 0; } if (outlen > strlen(value)) { outlen = strlen(value) + 1; } strlcpy(out, value, outlen); return strlen(out); }
/** Iterate over pairs in mapping section recording their values in an array * * This array is the list of attributes we retrieve from LDAP, and is NULL * terminated. * * If we hit a CONF_SECTION we recurse and process its CONF_PAIRS too. * * @param[out] values array of char pointers. * @param[in,out] idx records current array offset. * @param[in] cs to iterate over. * @return 0 on success else -1 on error. */ static int rlm_ldap_client_get_attrs(char const **values, int *idx, CONF_SECTION const *cs) { CONF_ITEM const *ci; for (ci = cf_item_find_next(cs, NULL); ci != NULL; ci = cf_item_find_next(cs, ci)) { char const *value; if (cf_item_is_section(ci)) { if (rlm_ldap_client_get_attrs(values, idx, cf_itemtosection(ci)) < 0) return -1; continue; } value = cf_pair_value(cf_itemtopair(ci)); if (!value) return -1; values[(*idx)++] = value; } values[*idx] = NULL; return 0; }
static rlm_rcode_t do_linelog(void *instance, REQUEST *request) { int fd = -1; char buffer[4096]; char *p; char line[1024]; rlm_linelog_t *inst = (rlm_linelog_t*) instance; const char *value = inst->line; #ifdef HAVE_GRP_H gid_t gid; struct group *grp; char *endptr; #endif if (inst->reference) { CONF_ITEM *ci; CONF_PAIR *cp; radius_xlat(line + 1, sizeof(line) - 2, inst->reference, request, linelog_escape_func, NULL); line[0] = '.'; /* force to be in current section */ /* * Don't allow it to go back up */ if (line[1] == '.') goto do_log; ci = cf_reference_item(NULL, inst->cs, line); if (!ci) { RDEBUG2("No such entry \"%s\"", line); return RLM_MODULE_NOOP; } if (!cf_item_is_pair(ci)) { RDEBUG2("Entry \"%s\" is not a variable assignment ", line); goto do_log; } cp = cf_itemtopair(ci); value = cf_pair_value(cp); if (!value) { RDEBUG2("Entry \"%s\" has no value", line); goto do_log; } /* * Value exists, but is empty. Don't log anything. */ if (!*value) return RLM_MODULE_OK; } do_log: /* * FIXME: Check length. */ if (strcmp(inst->filename, "syslog") != 0) { radius_xlat(buffer, sizeof(buffer), inst->filename, request, NULL, NULL); /* check path and eventually create subdirs */ p = strrchr(buffer,'/'); if (p) { *p = '\0'; if (rad_mkdir(buffer, 0700) < 0) { radlog_request(L_ERR, 0, request, "rlm_linelog: Failed to create directory %s: %s", buffer, strerror(errno)); return RLM_MODULE_FAIL; } *p = '/'; } fd = open(buffer, O_WRONLY | O_APPEND | O_CREAT, inst->permissions); if (fd == -1) { radlog(L_ERR, "rlm_linelog: Failed to open %s: %s", buffer, strerror(errno)); return RLM_MODULE_FAIL; } #ifdef HAVE_GRP_H if (inst->group != NULL) { gid = strtol(inst->group, &endptr, 10); if (*endptr != '\0') { grp = getgrnam(inst->group); if (grp == NULL) { RDEBUG2("Unable to find system group \"%s\"", inst->group); goto skip_group; } gid = grp->gr_gid; } if (chown(buffer, -1, gid) == -1) { RDEBUG2("Unable to change system group of \"%s\"", buffer); } } #endif } skip_group: /* * FIXME: Check length. */ radius_xlat(line, sizeof(line) - 1, value, request, linelog_escape_func, NULL); if (fd >= 0) { strcat(line, "\n"); write(fd, line, strlen(line)); close(fd); #ifdef HAVE_SYSLOG_H } else { syslog(inst->facility, "%s", line); #endif } return RLM_MODULE_OK; }
/* * Parse a configuration section, and populate a HV. * This function is recursively called (allows to have nested hashes.) */ static void perl_parse_config(CONF_SECTION *cs, int lvl, HV *rad_hv) { if (!cs || !rad_hv) return; int indent_section = (lvl + 1) * 4; int indent_item = (lvl + 2) * 4; DEBUG("%*s%s {", indent_section, " ", cf_section_name1(cs)); CONF_ITEM *ci; for (ci = cf_item_find_next(cs, NULL); ci; ci = cf_item_find_next(cs, ci)) { /* * This is a section. * Create a new HV, store it as a reference in current HV, * Then recursively call perl_parse_config with this section and the new HV. */ if (cf_item_is_section(ci)) { CONF_SECTION *sub_cs = cf_itemtosection(ci); char const *key = cf_section_name1(sub_cs); /* hash key */ HV *sub_hv; SV *ref; if (!key) continue; if (hv_exists(rad_hv, key, strlen(key))) { WARN("rlm_perl: Ignoring duplicate config section '%s'", key); continue; } sub_hv = newHV(); ref = newRV_inc((SV*) sub_hv); (void)hv_store(rad_hv, key, strlen(key), ref, 0); perl_parse_config(sub_cs, lvl + 1, sub_hv); } else if (cf_item_is_pair(ci)){ CONF_PAIR *cp = cf_itemtopair(ci); char const *key = cf_pair_attr(cp); /* hash key */ char const *value = cf_pair_value(cp); /* hash value */ if (!key || !value) continue; /* * This is an item. * Store item attr / value in current HV. */ if (hv_exists(rad_hv, key, strlen(key))) { WARN("rlm_perl: Ignoring duplicate config item '%s'", key); continue; } (void)hv_store(rad_hv, key, strlen(key), newSVpvn(value, strlen(value)), 0); DEBUG("%*s%s = %s", indent_item, " ", key, value); } } DEBUG("%*s}", indent_section, " "); }
static void load_component_section(CONF_SECTION *cs, int comp, const char *filename) { modcallable *this; CONF_ITEM *modref; int modreflineno; int idx; indexed_modcallable *subcomp; const char *modname; char *visiblename; for (modref=cf_item_find_next(cs, NULL); modref != NULL; modref=cf_item_find_next(cs, modref)) { if (cf_item_is_section(modref)) { CONF_SECTION *scs; scs = cf_itemtosection(modref); if (strcmp(cf_section_name1(scs), subcomponent_names[comp]) == 0) { load_subcomponent_section(scs, comp, filename); continue; } /* * Allow old names, too. */ if (strcmp(cf_section_name1(scs), old_subcomponent_names[comp]) == 0) { load_subcomponent_section(scs, comp, filename); continue; } modreflineno = cf_section_lineno(scs); } else { CONF_PAIR *cp; cp = cf_itemtopair(modref); modreflineno = cf_pair_lineno(cp); } this = compile_modsingle(comp, modref, filename, &modname); if (comp == RLM_COMPONENT_AUTH) { DICT_VALUE *dval; dval = dict_valbyname(PW_AUTH_TYPE, modname); rad_assert(dval != NULL); idx = dval->value; } else { /* See the comment in new_sublist() for explanation * of the special index 0 */ idx = 0; } subcomp = new_sublist(comp, idx); if (subcomp == NULL) { radlog(L_ERR|L_CONS, "%s %s %s already configured - skipping", filename, subcomponent_names[comp], modname); modcallable_free(&this); continue; } /* If subcomp->modulelist is NULL, add_to_modcallable will * create it */ visiblename = cf_section_name2(cs); if (visiblename == NULL) visiblename = cf_section_name1(cs); add_to_modcallable(&subcomp->modulelist, this, comp, visiblename); } }
/* * (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; }
/** 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; }
/** 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; }
void exec_trigger(REQUEST *request, CONF_SECTION *cs, const char *name, int quench) { CONF_SECTION *subcs; CONF_ITEM *ci; CONF_PAIR *cp; const char *attr; const char *value; VALUE_PAIR *vp; /* * Use global "trigger" section if no local config is given. */ if (!cs) { cs = mainconfig.config; attr = name; } else { /* * Try to use pair name, rather than reference. */ attr = strrchr(name, '.'); if (attr) { attr++; } else { attr = name; } } /* * Find local "trigger" subsection. If it isn't found, * try using the global "trigger" section, and reset the * reference to the full path, rather than the sub-path. */ subcs = cf_section_sub_find(cs, "trigger"); if (!subcs && (cs != mainconfig.config)) { subcs = cf_section_sub_find(mainconfig.config, "trigger"); attr = name; } if (!subcs) return; ci = cf_reference_item(subcs, mainconfig.config, attr); if (!ci) { DEBUG3("No such item in trigger section: %s", attr); return; } if (!cf_item_is_pair(ci)) { DEBUG2("Trigger is not a configuration variable: %s", attr); return; } cp = cf_itemtopair(ci); if (!cp) return; value = cf_pair_value(cp); if (!value) { DEBUG2("Trigger has no value: %s", name); return; } /* * May be called for Status-Server packets. */ vp = NULL; if (request && request->packet) vp = request->packet->vps; /* * Perform periodic quenching. */ if (quench) { time_t *last_time; last_time = cf_data_find(cs, value); if (!last_time) { last_time = rad_malloc(sizeof(*last_time)); *last_time = 0; if (cf_data_add(cs, value, last_time, time_free) < 0) { free(last_time); last_time = NULL; } } /* * Send the quenched traps at most once per second. */ if (last_time) { time_t now = time(NULL); if (*last_time == now) return; *last_time = now; } } DEBUG("Trigger %s -> %s", name, value); radius_exec_program(value, request, 0, NULL, 0, vp, NULL, 1); }
static int do_linelog(void *instance, REQUEST *request) { int fd = -1; char buffer[4096]; char line[1024]; rlm_linelog_t *inst = (rlm_linelog_t*) instance; const char *value = inst->line; if (inst->reference) { CONF_ITEM *ci; CONF_PAIR *cp; radius_xlat(line + 1, sizeof(line) - 2, inst->reference, request, linelog_escape_func); line[0] = '.'; /* force to be in current section */ /* * Don't allow it to go back up */ if (line[1] == '.') goto do_log; ci = cf_reference_item(NULL, inst->cs, line); if (!ci) { RDEBUG2("No such entry \"%s\"", line); return RLM_MODULE_NOOP; } if (!cf_item_is_pair(ci)) { RDEBUG2("Entry \"%s\" is not a variable assignment ", line); goto do_log; } cp = cf_itemtopair(ci); value = cf_pair_value(cp); if (!value) { RDEBUG2("Entry \"%s\" has no value", line); goto do_log; } /* * Value exists, but is empty. Don't log anything. */ if (!*value) return RLM_MODULE_OK; } do_log: /* * FIXME: Check length. */ if (strcmp(inst->filename, "syslog") != 0) { radius_xlat(buffer, sizeof(buffer), inst->filename, request, NULL); fd = open(buffer, O_WRONLY | O_APPEND | O_CREAT, 0600); if (fd == -1) { radlog(L_ERR, "rlm_linelog: Failed to open %s: %s", buffer, strerror(errno)); return RLM_MODULE_FAIL; } } /* * FIXME: Check length. */ radius_xlat(line, sizeof(line) - 1, value, request, linelog_escape_func); if (fd >= 0) { strcat(line, "\n"); write(fd, line, strlen(line)); close(fd); #ifdef HAVE_SYSLOG_H } else { syslog(LOG_INFO, "%s", line); #endif } return RLM_MODULE_OK; }
void exec_trigger(REQUEST *request, CONF_SECTION *cs, const char *name) { CONF_SECTION *subcs; CONF_ITEM *ci; CONF_PAIR *cp; const char *attr; const char *value; VALUE_PAIR *vp; /* * Use global "trigger" section if no local config is given. */ if (!cs) { cs = mainconfig.config; attr = name; } else { /* * Try to use pair name, rather than reference. */ attr = strrchr(name, '.'); if (attr) { attr++; } else { attr = name; } } /* * Find local "trigger" subsection. If it isn't found, * try using the global "trigger" section, and reset the * reference to the full path, rather than the sub-path. */ subcs = cf_section_sub_find(cs, "trigger"); if (!subcs && (cs != mainconfig.config)) { subcs = cf_section_sub_find(mainconfig.config, "trigger"); attr = name; } if (!subcs) { DEBUG3("No trigger subsection: ignoring trigger %s", name); return; } ci = cf_reference_item(subcs, mainconfig.config, attr); if (!ci) { DEBUG3("No such item in trigger section: %s", attr); return; } if (!cf_item_is_pair(ci)) { DEBUG2("Trigger is not a configuration variable: %s", attr); return; } cp = cf_itemtopair(ci); if (!cp) return; value = cf_pair_value(cp); if (!value) { DEBUG2("Trigger has no value: %s", name); return; } /* * May be called for Status-Server packets. */ vp = NULL; if (request && request->packet) vp = request->packet->vps; DEBUG("Trigger %s -> %s", name, value); radius_exec_program(value, request, 0, NULL, 0, vp, NULL, 1); }
/* * Generic function for failing between a bunch of queries. * * Uses the same principle as rlm_linelog, expanding the 'reference' config * item using xlat to figure out what query it should execute. * * If the reference matches multiple config items, and a query fails or * doesn't update any rows, the next matching config item is used. * */ static int acct_redundant(rlm_sql_t *inst, REQUEST *request, sql_acct_section_t *section) { rlm_rcode_t rcode = RLM_MODULE_OK; rlm_sql_handle_t *handle = NULL; int sql_ret; int numaffected = 0; CONF_ITEM *item; CONF_PAIR *pair; char const *attr = NULL; char const *value; char path[MAX_STRING_LEN]; char *p = path; char *expanded = NULL; rad_assert(section); if (section->reference[0] != '.') { *p++ = '.'; } if (radius_xlat(p, sizeof(path) - (p - path), request, section->reference, NULL, NULL) < 0) { rcode = RLM_MODULE_FAIL; goto finish; } item = cf_reference_item(NULL, section->cs, path); if (!item) { rcode = RLM_MODULE_FAIL; goto finish; } if (cf_item_is_section(item)){ REDEBUG("Sections are not supported as references"); rcode = RLM_MODULE_FAIL; goto finish; } pair = cf_itemtopair(item); attr = cf_pair_attr(pair); RDEBUG2("Using query template '%s'", attr); handle = sql_get_socket(inst); if (!handle) { rcode = RLM_MODULE_FAIL; goto finish; } sql_set_user(inst, request, NULL); while (true) { value = cf_pair_value(pair); if (!value) { RDEBUG("Ignoring null query"); rcode = RLM_MODULE_NOOP; goto finish; } if (radius_axlat(&expanded, request, value, sql_escape_func, inst) < 0) { rcode = RLM_MODULE_FAIL; goto finish; } if (!*expanded) { RDEBUG("Ignoring null query"); rcode = RLM_MODULE_NOOP; talloc_free(expanded); goto finish; } rlm_sql_query_log(inst, request, section, expanded); /* * If rlm_sql_query cannot use the socket it'll try and * reconnect. Reconnecting will automatically release * the current socket, and try to select a new one. * * If we get RLM_SQL_RECONNECT it means all connections in the pool * were exhausted, and we couldn't create a new connection, * so we do not need to call sql_release_socket. */ sql_ret = rlm_sql_query(&handle, inst, expanded); TALLOC_FREE(expanded); if (sql_ret == RLM_SQL_RECONNECT) { rcode = RLM_MODULE_FAIL; goto finish; } rad_assert(handle); /* * Assume all other errors are incidental, and just meant our * operation failed and its not a client or SQL syntax error. * * @fixme We should actually be able to distinguish between key * constraint violations (which we expect) and other errors. */ if (sql_ret == RLM_SQL_OK) { numaffected = (inst->module->sql_affected_rows)(handle, inst->config); if (numaffected > 0) { break; /* A query succeeded, were done! */ } RDEBUG("No records updated"); } (inst->module->sql_finish_query)(handle, inst->config); /* * We assume all entries with the same name form a redundant * set of queries. */ pair = cf_pair_find_next(section->cs, pair, attr); if (!pair) { RDEBUG("No additional queries configured"); rcode = RLM_MODULE_NOOP; goto finish; } RDEBUG("Trying next query..."); } (inst->module->sql_finish_query)(handle, inst->config); finish: talloc_free(expanded); sql_release_socket(inst, handle); return rcode; }
/* * Generic function for failing between a bunch of queries. * * Uses the same principle as rlm_linelog, expanding the 'reference' config * item using xlat to figure out what query it should execute. * * If the reference matches multiple config items, and a query fails or * doesn't update any rows, the next matching config item is used. * */ static int acct_redundant(rlm_sql_t *inst, REQUEST *request, sql_acct_section_t *section) { int ret = RLM_MODULE_OK; rlm_sql_handle_t *handle = NULL; int sql_ret; int numaffected = 0; CONF_ITEM *item; CONF_PAIR *pair; const char *attr = NULL; const char *value; char path[MAX_STRING_LEN]; char querystr[MAX_QUERY_LEN]; char *p = path; rad_assert(section); if (section->reference[0] != '.') *p++ = '.'; if (!radius_xlat(p, (sizeof(path) - (p - path)) - 1, section->reference, request, NULL, NULL)) return RLM_MODULE_FAIL; item = cf_reference_item(NULL, section->cs, path); if (!item) return RLM_MODULE_FAIL; if (cf_item_is_section(item)){ radlog(L_ERR, "Sections are not supported as references"); return RLM_MODULE_FAIL; } pair = cf_itemtopair(item); attr = cf_pair_attr(pair); RDEBUG2("Using query template '%s'", attr); handle = sql_get_socket(inst); if (handle == NULL) return RLM_MODULE_FAIL; sql_set_user(inst, request, NULL); while (TRUE) { value = cf_pair_value(pair); if (!value) { RDEBUG("Ignoring null query"); ret = RLM_MODULE_NOOP; goto release; } radius_xlat(querystr, sizeof(querystr), value, request, sql_escape_func, inst); if (!*querystr) { RDEBUG("Ignoring null query"); ret = RLM_MODULE_NOOP; goto release; } rlm_sql_query_log(inst, request, section, querystr); /* * If rlm_sql_query cannot use the socket it'll try and * reconnect. Reconnecting will automatically release * the current socket, and try to select a new one. * * If we get SQL_DOWN it means all connections in the pool * were exhausted, and we couldn't create a new connection, * so we do not need to call sql_release_socket. */ sql_ret = rlm_sql_query(&handle, inst, querystr); if (sql_ret == SQL_DOWN) return RLM_MODULE_FAIL; rad_assert(handle); /* * Assume all other errors are incidental, and just meant our * operation failed and its not a client or SQL syntax error. */ if (sql_ret == 0) { numaffected = (inst->module->sql_affected_rows) (handle, inst->config); if (numaffected > 0) break; RDEBUG("No records updated"); } (inst->module->sql_finish_query)(handle, inst->config); /* * We assume all entries with the same name form a redundant * set of queries. */ pair = cf_pair_find_next(section->cs, pair, attr); if (!pair) { RDEBUG("No additional queries configured"); ret = RLM_MODULE_NOOP; goto release; } RDEBUG("Trying next query..."); } (inst->module->sql_finish_query)(handle, inst->config); release: sql_release_socket(inst, handle); return ret; }
/** Modify user's object in LDAP * * Process a modifcation map to update a user object in the LDAP directory. * * @param inst rlm_ldap instance. * @param request Current request. * @param section that holds the map to process. * @return one of the RLM_MODULE_* values. */ static rlm_rcode_t user_modify(ldap_instance_t *inst, REQUEST *request, ldap_acct_section_t *section) { rlm_rcode_t rcode = RLM_MODULE_OK; ldap_rcode_t status; ldap_handle_t *conn = NULL; LDAPMod *mod_p[LDAP_MAX_ATTRMAP + 1], mod_s[LDAP_MAX_ATTRMAP]; LDAPMod **modify = mod_p; char *passed[LDAP_MAX_ATTRMAP * 2]; int i, total = 0, last_pass = 0; char *expanded[LDAP_MAX_ATTRMAP]; int last_exp = 0; char const *attr; char const *value; char const *dn; /* * Build our set of modifications using the update sections in * the config. */ CONF_ITEM *ci; CONF_PAIR *cp; CONF_SECTION *cs; FR_TOKEN op; char path[MAX_STRING_LEN]; char *p = path; rad_assert(section); /* * Locate the update section were going to be using */ if (section->reference[0] != '.') { *p++ = '.'; } if (radius_xlat(p, (sizeof(path) - (p - path)) - 1, request, section->reference, NULL, NULL) < 0) { goto error; } ci = cf_reference_item(NULL, section->cs, path); if (!ci) { goto error; } if (!cf_item_is_section(ci)){ REDEBUG("Reference must resolve to a section"); goto error; } cs = cf_section_sub_find(cf_itemtosection(ci), "update"); if (!cs) { REDEBUG("Section must contain 'update' subsection"); goto error; } /* * Iterate over all the pairs, building our mods array */ for (ci = cf_item_find_next(cs, NULL); ci != NULL; ci = cf_item_find_next(cs, ci)) { bool do_xlat = false; if (total == LDAP_MAX_ATTRMAP) { REDEBUG("Modify map size exceeded"); goto error; } if (!cf_item_is_pair(ci)) { REDEBUG("Entry is not in \"ldap-attribute = value\" format"); goto error; } /* * Retrieve all the information we need about the pair */ cp = cf_itemtopair(ci); value = cf_pair_value(cp); attr = cf_pair_attr(cp); op = cf_pair_operator(cp); if (!value || (*value == '\0')) { RDEBUG("Empty value string, skipping attribute \"%s\"", attr); continue; } switch (cf_pair_value_type(cp)) { case T_BARE_WORD: case T_SINGLE_QUOTED_STRING: break; case T_BACK_QUOTED_STRING: case T_DOUBLE_QUOTED_STRING: do_xlat = true; break; default: rad_assert(0); goto error; } if (op == T_OP_CMP_FALSE) { passed[last_pass] = NULL; } else if (do_xlat) { char *exp = NULL; if (radius_axlat(&exp, request, value, NULL, NULL) <= 0) { RDEBUG("Skipping attribute \"%s\"", attr); talloc_free(exp); continue; } expanded[last_exp++] = exp; passed[last_pass] = exp; /* * Static strings */ } else { memcpy(&(passed[last_pass]), &value, sizeof(passed[last_pass])); } passed[last_pass + 1] = NULL; mod_s[total].mod_values = &(passed[last_pass]); last_pass += 2; switch (op) { /* * T_OP_EQ is *NOT* supported, it is impossible to * support because of the lack of transactions in LDAP */ case T_OP_ADD: mod_s[total].mod_op = LDAP_MOD_ADD; break; case T_OP_SET: mod_s[total].mod_op = LDAP_MOD_REPLACE; break; case T_OP_SUB: case T_OP_CMP_FALSE: mod_s[total].mod_op = LDAP_MOD_DELETE; break; #ifdef LDAP_MOD_INCREMENT case T_OP_INCRM: mod_s[total].mod_op = LDAP_MOD_INCREMENT; break; #endif default: REDEBUG("Operator '%s' is not supported for LDAP modify operations", fr_int2str(fr_tokens, op, "<INVALID>")); goto error; } /* * Now we know the value is ok, copy the pointers into * the ldapmod struct. */ memcpy(&(mod_s[total].mod_type), &attr, sizeof(mod_s[total].mod_type)); mod_p[total] = &(mod_s[total]); total++; } if (total == 0) { rcode = RLM_MODULE_NOOP; goto release; } mod_p[total] = NULL; conn = mod_conn_get(inst, request); if (!conn) return RLM_MODULE_FAIL; dn = rlm_ldap_find_user(inst, request, &conn, NULL, false, NULL, &rcode); if (!dn || (rcode != RLM_MODULE_OK)) { goto error; } status = rlm_ldap_modify(inst, request, &conn, dn, modify); switch (status) { case LDAP_PROC_SUCCESS: break; case LDAP_PROC_REJECT: case LDAP_PROC_BAD_DN: rcode = RLM_MODULE_INVALID; break; default: rcode = RLM_MODULE_FAIL; break; }; release: error: /* * Free up any buffers we allocated for xlat expansion */ for (i = 0; i < last_exp; i++) { talloc_free(expanded[i]); } mod_conn_release(inst, conn); return rcode; }
/** 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; }