/** 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; }
/** 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; }
/** Create a client CONF_SECTION using a mapping section to map values from a result set to client attributes * * If we hit a CONF_SECTION we recurse and process its CONF_PAIRS too. * * @note Caller should free CONF_SECTION passed in as out, on error. * Contents of that section will be in an undefined state. * * @param[in,out] out Section to perform mapping on. Either the root of the client config, or a parent section * (when this function is called recursively). * Should be alloced with cf_section_alloc, or if there's a separate template section, the * result of calling cf_section_dup on that section. * @param[in] map section. * @param[in] func to call to retrieve CONF_PAIR values. Must return a talloced buffer containing the value. * @param[in] data to pass to func, usually a result pointer. * @return 0 on success else -1 on error. */ int client_map_section(CONF_SECTION *out, CONF_SECTION const *map, client_value_cb_t func, void *data) { 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; CONF_PAIR *old; char *value; char const *attr; /* * Recursively process map subsection */ if (cf_item_is_section(ci)) { CONF_SECTION *cs, *cc; cs = cf_item_to_section(ci); /* * Use pre-existing section or alloc a new one */ cc = cf_section_sub_find_name2(out, cf_section_name1(cs), cf_section_name2(cs)); if (!cc) { cc = cf_section_alloc(out, cf_section_name1(cs), cf_section_name2(cs)); cf_section_add(out, cc); if (!cc) return -1; } if (client_map_section(cc, cs, func, data) < 0) return -1; continue; } cp = cf_item_to_pair(ci); attr = cf_pair_attr(cp); /* * The callback can return 0 (success) and not provide a value * in which case we skip the mapping pair. * * Or return -1 in which case we error out. */ if (func(&value, cp, data) < 0) { cf_log_err_cs(out, "Failed performing mapping \"%s\" = \"%s\"", attr, cf_pair_value(cp)); return -1; } if (!value) continue; /* * Replace an existing CONF_PAIR */ old = cf_pair_find(out, attr); if (old) { cf_pair_replace(out, old, value); talloc_free(value); continue; } /* * ...or add a new CONF_PAIR */ cp = cf_pair_alloc(out, attr, value, T_OP_SET, T_BARE_WORD, T_SINGLE_QUOTED_STRING); if (!cp) { cf_log_err_cs(out, "Failed allocing pair \"%s\" = \"%s\"", attr, value); talloc_free(value); return -1; } talloc_free(value); cf_item_add(out, cf_pair_to_item(cp)); } return 0; }
/** Allocate a #CONF_SECTION * * @param[in] ctx to allocate * @param[in] parent #CONF_SECTION to hang this #CONF_SECTION off of. * If parent is not NULL, the new section will be added as a child. * @param[in] name1 Primary name. * @param[in] name2 Secondary name. * @param[in] filename Caller file name for debugging. May be overridden later. * @param[in] lineno Caller line number for debugging. May be overridden later. * @return * - NULL on error. * - A new #CONF_SECTION parented by parent. */ CONF_SECTION *_cf_section_alloc(TALLOC_CTX *ctx, CONF_SECTION *parent, char const *name1, char const *name2, char const *filename, int lineno) { CONF_SECTION *cs; char buffer[1024]; if (!name1) return NULL; if (name2 && parent) { char const *p; p = strchr(name2, '$'); if (p && (p[1] != '{')) p = NULL; if (p) { name2 = cf_expand_variables(parent->item.filename, &parent->item.lineno, parent, buffer, sizeof(buffer), name2, NULL); if (!name2) { ERROR("Failed expanding section name"); return NULL; } } } cs = talloc_zero(ctx, CONF_SECTION); if (!cs) return NULL; cs->item.type = CONF_ITEM_SECTION; cs->item.parent = cf_section_to_item(parent); fr_cursor_init(&cs->item.cursor, &cs->item.child); MEM(cs->name1 = talloc_typed_strdup(cs, name1)); if (name2) { MEM(cs->name2 = talloc_typed_strdup(cs, name2)); cs->name2_quote = T_BARE_WORD; } talloc_set_destructor(cs, _cf_section_free); if (filename) cf_filename_set(cs, filename); if (lineno) cf_lineno_set(cs, lineno); if (parent) { CONF_DATA const *cd; CONF_PARSER *rule; cs->depth = parent->depth + 1; cf_item_add(parent, &(cs->item)); cd = cf_data_find(CF_TO_ITEM(parent), CONF_PARSER, cf_section_name1(parent)); if (cd) { rule = cf_data_value(cd); /* * The parent has an ON_READ rule. Check * if that rule has a child which matches * this section. */ if ((FR_BASE_TYPE(rule->type) == FR_TYPE_SUBSECTION) && ((rule->type & FR_TYPE_ON_READ) != 0) && rule->subcs) { CONF_PARSER const *rule_p; for (rule_p = rule->subcs; rule_p->name; rule_p++) { if ((FR_BASE_TYPE(rule_p->type) == FR_TYPE_SUBSECTION) && ((rule_p->type & FR_TYPE_ON_READ) != 0) && (strcmp(rule_p->name, name1) == 0)) { (void) _cf_section_rule_push(cs, rule_p, cd->item.filename, cd->item.lineno); (void) rule_p->func(ctx, NULL, NULL, cf_section_to_item(cs), rule_p); return cs; } } } } /* * Call any ON_READ callback. And push this rule * to the child section, so that the child can * pick it up. */ cd = cf_data_find(CF_TO_ITEM(parent), CONF_PARSER, name1); if (cd) { rule = cf_data_value(cd); if (rule->func && (FR_BASE_TYPE(rule->type) == FR_TYPE_SUBSECTION) && ((rule->type & FR_TYPE_ON_READ) != 0)) { (void) cf_section_rules_push(cs, rule); (void) rule->func(ctx, NULL, NULL, cf_section_to_item(cs), rule); } } } return cs; }
/* * Expand the variables in an input string. */ char const *cf_expand_variables(char const *cf, int *lineno, CONF_SECTION *outer_cs, char *output, size_t outsize, char const *input, bool *soft_fail) { char *p; char const *end, *ptr; CONF_SECTION const *parent_cs; char name[8192]; if (soft_fail) *soft_fail = false; /* * Find the master parent conf section. * We can't use main_config->root_cs, because we're in the * process of re-building it, and it isn't set up yet... */ parent_cs = cf_root(outer_cs); p = output; ptr = input; while (*ptr) { /* * Ignore anything other than "${" */ if ((*ptr == '$') && (ptr[1] == '{')) { CONF_ITEM *ci; CONF_PAIR *cp; char *q; /* * FIXME: Add support for ${foo:-bar}, * like in xlat.c */ /* * Look for trailing '}', and log a * warning for anything that doesn't match, * and exit with a fatal error. */ end = strchr(ptr, '}'); if (end == NULL) { *p = '\0'; INFO("%s[%d]: Variable expansion missing }", cf, *lineno); return NULL; } ptr += 2; /* * Can't really happen because input lines are * capped at 8k, which is sizeof(name) */ if ((size_t) (end - ptr) >= sizeof(name)) { ERROR("%s[%d]: Reference string is too large", cf, *lineno); return NULL; } memcpy(name, ptr, end - ptr); name[end - ptr] = '\0'; q = strchr(name, ':'); if (q) { *(q++) = '\0'; } ci = cf_reference_item(parent_cs, outer_cs, name); if (!ci) { if (soft_fail) *soft_fail = true; ERROR("%s[%d]: Reference \"${%s}\" not found", cf, *lineno, name); return NULL; } /* * The expansion doesn't refer to another item or section * it's the property of a section. */ if (q) { CONF_SECTION *find = cf_item_to_section(ci); if (ci->type != CONF_ITEM_SECTION) { ERROR("%s[%d]: Can only reference properties of sections", cf, *lineno); return NULL; } switch (fr_str2int(conf_property_name, q, CONF_PROPERTY_INVALID)) { case CONF_PROPERTY_NAME: strcpy(p, find->name1); break; case CONF_PROPERTY_INSTANCE: strcpy(p, find->name2 ? find->name2 : find->name1); break; default: ERROR("%s[%d]: Invalid property '%s'", cf, *lineno, q); return NULL; } p += strlen(p); ptr = end + 1; } else if (ci->type == CONF_ITEM_PAIR) { /* * Substitute the value of the variable. */ cp = cf_item_to_pair(ci); /* * If the thing we reference is * marked up as being expanded in * pass2, don't expand it now. * Let it be expanded in pass2. */ if (cp->pass2) { if (soft_fail) *soft_fail = true; ERROR("%s[%d]: Reference \"%s\" points to a variable which has not been expanded.", cf, *lineno, input); return NULL; } if (!cp->value) { ERROR("%s[%d]: Reference \"%s\" has no value", cf, *lineno, input); return NULL; } if (p + strlen(cp->value) >= output + outsize) { ERROR("%s[%d]: Reference \"%s\" is too long", cf, *lineno, input); return NULL; } strcpy(p, cp->value); p += strlen(p); ptr = end + 1; } else if (ci->type == CONF_ITEM_SECTION) { CONF_SECTION *subcs; /* * Adding an entry again to a * section is wrong. We don't * want an infinite loop. */ if (cf_item_to_section(ci->parent) == outer_cs) { ERROR("%s[%d]: Cannot reference different item in same section", cf, *lineno); return NULL; } /* * Copy the section instead of * referencing it. */ subcs = cf_item_to_section(ci); subcs = cf_section_dup(outer_cs, outer_cs, subcs, cf_section_name1(subcs), cf_section_name2(subcs), false); if (!subcs) { ERROR("%s[%d]: Failed copying reference %s", cf, *lineno, name); return NULL; } subcs->item.filename = ci->filename; subcs->item.lineno = ci->lineno; cf_item_add(outer_cs, &(subcs->item)); ptr = end + 1; } else { ERROR("%s[%d]: Reference \"%s\" type is invalid", cf, *lineno, input); return NULL; } } else if (strncmp(ptr, "$ENV{", 5) == 0) { char *env; ptr += 5; /* * Look for trailing '}', and log a * warning for anything that doesn't match, * and exit with a fatal error. */ end = strchr(ptr, '}'); if (end == NULL) { *p = '\0'; INFO("%s[%d]: Environment variable expansion missing }", cf, *lineno); return NULL; } /* * Can't really happen because input lines are * capped at 8k, which is sizeof(name) */ if ((size_t) (end - ptr) >= sizeof(name)) { ERROR("%s[%d]: Environment variable name is too large", cf, *lineno); return NULL; } memcpy(name, ptr, end - ptr); name[end - ptr] = '\0'; /* * Get the environment variable. * If none exists, then make it an empty string. */ env = getenv(name); if (env == NULL) { *name = '\0'; env = name; } if (p + strlen(env) >= output + outsize) { ERROR("%s[%d]: Reference \"%s\" is too long", cf, *lineno, input); return NULL; } strcpy(p, env); p += strlen(p); ptr = end + 1; } else { /* * Copy it over verbatim. */ *(p++) = *(ptr++); } if (p >= (output + outsize)) { ERROR("%s[%d]: Reference \"%s\" is too long", cf, *lineno, input); return NULL; } } /* loop over all of the input string. */ *p = '\0'; return output; }