This makes the code much easier to follow, removes a bunch of duplicated validation code,
and avoids duplicating map_afrom_cs for any modules that have different validation rules.
} while (0)
#endif
+typedef int (*map_validate_t)(value_pair_map_t *map, void *ctx);
typedef int (*radius_map_getvalue_t)(VALUE_PAIR **out, REQUEST *request, value_pair_map_t const *map, void *ctx);
int map_afrom_cp(TALLOC_CTX *ctx, value_pair_map_t **out, CONF_PAIR *cp,
int 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);
+ map_validate_t validate, void *ctx, unsigned int max) CC_HINT(nonnull(1, 2));
int map_afrom_vp_str(value_pair_map_t **out, REQUEST *request, char const *raw,
request_refs_t dst_request_def, pair_lists_t dst_list_def,
*/
typedef struct modcallable modcallable;
+int modcall_fixup_update(value_pair_map_t *map, void *ctx);
+
int modcall(rlm_components_t component, modcallable *c, REQUEST *request);
/* Parse a module-method's config section (e.g. authorize{}) into a tree that
goto error;
}
- /*
- * Anal-retentive checks.
- */
- if (debug_flag > 2) {
- if ((map->lhs->type == TMPL_TYPE_ATTR) && (*attr != '&')) {
- WARN("%s[%d]: Please change attribute reference to '&%s %s ...'",
- cf_pair_filename(cp), cf_pair_lineno(cp),
- attr, fr_int2str(fr_tokens, map->op, "<INVALID>"));
- }
-
- if ((map->rhs->type == TMPL_TYPE_ATTR) && (*value != '&')) {
- WARN("%s[%d]: Please change attribute reference to '... %s &%s'",
- cf_pair_filename(cp), cf_pair_lineno(cp),
- fr_int2str(fr_tokens, map->op, "<INVALID>"), value);
- }
- }
-
- /*
- * Values used by unary operators should be literal ANY
- *
- * We then free the template and alloc a NULL one instead.
- */
- if (map->op == T_OP_CMP_FALSE) {
- if ((map->rhs->type != TMPL_TYPE_LITERAL) || (strcmp(map->rhs->name, "ANY") != 0)) {
- WARN("%s[%d] Wildcard deletion MUST use '!* ANY'", cf_pair_filename(cp), cf_pair_lineno(cp));
- }
-
- tmpl_free(&map->rhs);
-
- map->rhs = tmpl_alloc(map, TMPL_TYPE_NULL, NULL, 0);
- }
-
- /*
- * Lots of sanity checks for insane people...
- */
-
- /*
- * We don't support implicit type conversion,
- * except for "octets"
- */
- if (((map->lhs->type == TMPL_TYPE_ATTR) || (map->lhs->type == TMPL_TYPE_DATA)) &&
- ((map->rhs->type == TMPL_TYPE_ATTR) || (map->rhs->type == TMPL_TYPE_DATA))) {
- PW_TYPE rhs_type;
- PW_TYPE lhs_type;
-
- switch (map->lhs->type) {
- case TMPL_TYPE_ATTR:
- lhs_type = map->lhs->tmpl_da->type;
- break;
-
- case TMPL_TYPE_DATA:
- lhs_type = map->lhs->tmpl_data_type;
- break;
-
- default:
- rad_assert(0);
- }
-
- switch (map->rhs->type) {
- case TMPL_TYPE_ATTR:
- rhs_type = map->rhs->tmpl_da->type;
- break;
-
- case TMPL_TYPE_DATA:
- rhs_type = map->rhs->tmpl_data_type;
- break;
-
- default:
- rad_assert(0);
- }
-
-
- if ((lhs_type != rhs_type) &&
- (lhs_type != PW_TYPE_OCTETS) &&
- (rhs_type != PW_TYPE_OCTETS)) {
- cf_log_err(ci, "Attribute type mismatch");
- goto error;
- }
- }
-
- /*
- * What exactly where you expecting to happen here?
- */
- if ((map->lhs->type == TMPL_TYPE_ATTR) &&
- (map->rhs->type == TMPL_TYPE_LIST)) {
- cf_log_err(ci, "Can't copy list into an attribute");
- goto error;
- }
-
- /*
- * Depending on the attribute type, some operators are
- * disallowed.
- */
- if (map->lhs->type == TMPL_TYPE_ATTR) {
- switch (map->op) {
- default:
- cf_log_err(ci, "Invalid operator for attribute");
- goto error;
-
- case T_OP_EQ:
- case T_OP_CMP_EQ:
- case T_OP_ADD:
- case T_OP_SUB:
- case T_OP_LE:
- case T_OP_GE:
- case T_OP_CMP_FALSE:
- case T_OP_SET:
- break;
- }
- }
-
- if (map->lhs->type == TMPL_TYPE_LIST) {
- /*
- * Only += and :=, and !* operators are supported
- * for lists.
- */
- switch (map->op) {
- case T_OP_CMP_FALSE:
- break;
-
- case T_OP_ADD:
- if ((map->rhs->type != TMPL_TYPE_LIST) &&
- (map->rhs->type != TMPL_TYPE_EXEC)) {
- cf_log_err(ci, "Invalid source for list assignment '%s += ...'",
- map->lhs->name);
- goto error;
- }
- break;
-
- case T_OP_SET:
- if (map->rhs->type == TMPL_TYPE_EXEC) {
- WARN("%s[%d] Please change ':=' to '=' for list assignment",
- cf_pair_filename(cp), cf_pair_lineno(cp));
- break;
- }
-
- if (map->rhs->type != TMPL_TYPE_LIST) {
- cf_log_err(ci, "Invalid source for list assignment '%s := ...'",
- map->lhs->name);
- goto error;
- }
- break;
-
- case T_OP_EQ:
- if (map->rhs->type != TMPL_TYPE_EXEC) {
- cf_log_err(ci, "Invalid source for list assignment '%s = ...'",
- map->lhs->name);
- goto error;
- }
- break;
-
- default:
- cf_log_err(ci, "Operator \"%s\" not allowed for list assignment",
- fr_int2str(fr_tokens, map->op, "<INVALID>"));
- goto error;
- }
- }
-
VERIFY_MAP(map);
*out = map;
* 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] validate map using this callback (may be NULL).
+ * @param[in] ctx to pass to callback.
* @param[in] max number of mappings to process.
* @return -1 on error, else 0.
*/
-int map_afrom_cs(value_pair_map_t **out, CONF_SECTION *cs, pair_lists_t dst_list_def, pair_lists_t src_list_def,
+int map_afrom_cs(value_pair_map_t **out, CONF_SECTION *cs,
+ pair_lists_t dst_list_def, pair_lists_t src_list_def,
+ map_validate_t validate, void *ctx,
unsigned int max)
{
char const *cs_list, *p;
unsigned int total = 0;
value_pair_map_t **tail, *map;
- TALLOC_CTX *ctx;
+ TALLOC_CTX *parent;
*out = NULL;
tail = out;
* The first map has cs as the parent.
* The rest have the previous map as the parent.
*/
- ctx = cs;
+ parent = cs;
ci = cf_sectiontoitem(cs);
}
cp = cf_itemtopair(ci);
- if (map_afrom_cp(ctx, &map, cp, request_def, dst_list_def, REQUEST_CURRENT, src_list_def) < 0) {
+ if (map_afrom_cp(parent, &map, cp, request_def, dst_list_def, REQUEST_CURRENT, src_list_def) < 0) {
goto error;
}
VERIFY_MAP(map);
- ctx = *tail = map;
+ /*
+ * Check the types in the map are valid
+ */
+ if (validate && (validate(map, ctx) < 0)) goto error;
+
+ parent = *tail = map;
tail = &(map->next);
}
#endif
};
-
-#ifdef WITH_UNLANG
-static modcallable *do_compile_modupdate(modcallable *parent, UNUSED rlm_components_t component,
- CONF_SECTION *cs, char const *name2)
+/** Validate and fixup a map that's part of an update section.
+ *
+ * @param map to validate.
+ * @return 0 if valid else -1.
+ */
+int modcall_fixup_update(value_pair_map_t *map, UNUSED void *ctx)
{
- int rcode;
- modgroup *g;
- modcallable *csingle;
- value_pair_map_t *map, *head = NULL;
- CONF_ITEM *ci;
+ CONF_PAIR *cp = cf_itemtopair(map->ci);
/*
- * This looks at cs->name2 to determine which list to update
+ * Anal-retentive checks.
*/
- rcode = map_afrom_cs(&head, cs, PAIR_LIST_REQUEST, PAIR_LIST_REQUEST, 128);
- if (rcode < 0) return NULL; /* message already printed */
+ if (DEBUG_ENABLED2) {
+ if ((map->lhs->type == TMPL_TYPE_ATTR) && (map->lhs->name[0] != '&')) {
+ WARN("%s[%d]: Please change attribute reference to '&%s %s ...'",
+ cf_pair_filename(cp), cf_pair_lineno(cp),
+ map->lhs->name, fr_int2str(fr_tokens, map->op, "<INVALID>"));
+ }
- if (!head) {
- cf_log_err_cs(cs, "'update' sections cannot be empty");
- return NULL;
+ if ((map->rhs->type == TMPL_TYPE_ATTR) && (map->rhs->name[0] != '&')) {
+ WARN("%s[%d]: Please change attribute reference to '... %s &%s'",
+ cf_pair_filename(cp), cf_pair_lineno(cp),
+ fr_int2str(fr_tokens, map->op, "<INVALID>"), map->rhs->name);
+ }
+ }
+
+ /*
+ * Values used by unary operators should be literal ANY
+ *
+ * We then free the template and alloc a NULL one instead.
+ */
+ if (map->op == T_OP_CMP_FALSE) {
+ if ((map->rhs->type != TMPL_TYPE_LITERAL) || (strcmp(map->rhs->name, "ANY") != 0)) {
+ WARN("%s[%d] Wildcard deletion MUST use '!* ANY'",
+ cf_pair_filename(cp), cf_pair_lineno(cp));
+ }
+
+ tmpl_free(&map->rhs);
+
+ map->rhs = tmpl_alloc(map, TMPL_TYPE_NULL, NULL, 0);
}
- for (map = head, ci = cf_item_find_next(cs, NULL);
- map != NULL;
- map = map->next, ci = cf_item_find_next(cs, ci)) {
+ /*
+ * Lots of sanity checks for insane people...
+ */
+
+ /*
+ * We don't support implicit type conversion,
+ * except for "octets"
+ */
+ if (((map->lhs->type == TMPL_TYPE_ATTR) || (map->lhs->type == TMPL_TYPE_DATA)) &&
+ ((map->rhs->type == TMPL_TYPE_ATTR) || (map->rhs->type == TMPL_TYPE_DATA))) {
+ PW_TYPE rhs_type;
+ PW_TYPE lhs_type;
+
+ switch (map->lhs->type) {
+ case TMPL_TYPE_ATTR:
+ lhs_type = map->lhs->tmpl_da->type;
+ break;
+
+ case TMPL_TYPE_DATA:
+ lhs_type = map->lhs->tmpl_data_type;
+ break;
+
+ default:
+ rad_assert(0);
+ }
+
+ switch (map->rhs->type) {
+ case TMPL_TYPE_ATTR:
+ rhs_type = map->rhs->tmpl_da->type;
+ break;
+
+ case TMPL_TYPE_DATA:
+ rhs_type = map->rhs->tmpl_data_type;
+ break;
+
+ default:
+ rad_assert(0);
+ }
+
+
+ if ((lhs_type != rhs_type) &&
+ (lhs_type != PW_TYPE_OCTETS) &&
+ (rhs_type != PW_TYPE_OCTETS)) {
+ cf_log_err(map->ci, "Attribute type mismatch");
+ return -1;
+ }
+ }
+
+ /*
+ * What exactly where you expecting to happen here?
+ */
+ if ((map->lhs->type == TMPL_TYPE_ATTR) &&
+ (map->rhs->type == TMPL_TYPE_LIST)) {
+ cf_log_err(map->ci, "Can't copy list into an attribute");
+ return -1;
+ }
+
+ /*
+ * Depending on the attribute type, some operators are disallowed.
+ */
+ if (map->lhs->type == TMPL_TYPE_ATTR) {
+ switch (map->op) {
+ default:
+ cf_log_err(map->ci, "Invalid operator for attribute");
+ return -1;
+
+ case T_OP_EQ:
+ case T_OP_CMP_EQ:
+ case T_OP_ADD:
+ case T_OP_SUB:
+ case T_OP_LE:
+ case T_OP_GE:
+ case T_OP_CMP_FALSE:
+ case T_OP_SET:
+ break;
+ }
+ }
+
+ if (map->lhs->type == TMPL_TYPE_LIST) {
/*
* Can't copy an xlat expansion or literal into a list,
* we don't know what type of attribute we'd need
* The only exception is where were using a unary
* operator like !*.
*/
- if ((map->lhs->type == TMPL_TYPE_LIST) &&
- (map->op != T_OP_CMP_FALSE) &&
- ((map->rhs->type == TMPL_TYPE_XLAT) || (map->rhs->type == TMPL_TYPE_LITERAL))) {
+ if (map->op != T_OP_CMP_FALSE) switch (map->rhs->type) {
+ case TMPL_TYPE_XLAT:
+ case TMPL_TYPE_LITERAL:
cf_log_err(map->ci, "Can't copy value into list (we don't know which attribute to create)");
- talloc_free(head);
- return NULL;
+ return -1;
+
+ default:
+ break;
}
/*
- * If LHS is an attribute, and RHS is a literal, we can
- * preparse the information into a TMPL_TYPE_DATA.
- *
- * Unless it's a unary operator in which case we
- * ignore map->rhs.
+ * Only += and :=, and !* operators are supported
+ * for lists.
*/
- if ((map->lhs->type == TMPL_TYPE_ATTR) && (map->op != T_OP_CMP_FALSE) &&
- (map->rhs->type == TMPL_TYPE_LITERAL)) {
- CONF_PAIR *cp;
+ switch (map->op) {
+ case T_OP_CMP_FALSE:
+ break;
- cp = cf_itemtopair(ci);
- rad_assert(cp != NULL);
+ case T_OP_ADD:
+ if ((map->rhs->type != TMPL_TYPE_LIST) &&
+ (map->rhs->type != TMPL_TYPE_EXEC)) {
+ cf_log_err(map->ci, "Invalid source for list assignment '%s += ...'", map->lhs->name);
+ return -1;
+ }
+ break;
- /*
- * It's a literal string, just copy it.
- * Don't escape anything.
- */
- if ((map->lhs->tmpl_da->type == PW_TYPE_STRING) &&
- (cf_pair_value_type(cp) == T_SINGLE_QUOTED_STRING)) {
- value_data_t *vpd;
+ case T_OP_SET:
+ if (map->rhs->type == TMPL_TYPE_EXEC) {
+ WARN("%s[%d] Please change ':=' to '=' for list assignment",
+ cf_pair_filename(cp), cf_pair_lineno(cp));
+ }
+
+ if (map->rhs->type != TMPL_TYPE_LIST) {
+ cf_log_err(map->ci, "Invalid source for list assignment '%s := ...'", map->lhs->name);
+ return -1;
+ }
+ break;
- map->rhs->tmpl_data_value = vpd = talloc_zero(map->rhs, value_data_t);
- rad_assert(vpd != NULL);
+ case T_OP_EQ:
+ if (map->rhs->type != TMPL_TYPE_EXEC) {
+ cf_log_err(map->ci, "Invalid source for list assignment '%s = ...'", map->lhs->name);
+ return -1;
+ }
+ break;
- vpd->strvalue = talloc_typed_strdup(vpd, map->rhs->name);
- rad_assert(vpd->strvalue != NULL);
+ default:
+ cf_log_err(map->ci, "Operator \"%s\" not allowed for list assignment",
+ fr_int2str(fr_tokens, map->op, "<INVALID>"));
+ return -1;
+ }
+ }
- map->rhs->type = TMPL_TYPE_DATA;
- map->rhs->tmpl_data_type = map->lhs->tmpl_da->type;
- map->rhs->tmpl_data_length = talloc_array_length(vpd->strvalue) - 1;
- } else {
- if (!tmpl_cast_in_place(map->rhs, map->lhs->tmpl_da)) {
- cf_log_err(map->ci, "%s", fr_strerror());
- talloc_free(head);
- return NULL;
- }
+ /*
+ * If LHS is an attribute, and RHS is a literal, we can
+ * preparse the information into a TMPL_TYPE_DATA.
+ *
+ * Unless it's a unary operator in which case we
+ * ignore map->rhs.
+ */
+ if ((map->lhs->type == TMPL_TYPE_ATTR) && (map->rhs->type == TMPL_TYPE_LITERAL) &&
+ (map->op != T_OP_CMP_FALSE)) {
+ /*
+ * It's a literal string, just copy it.
+ * Don't escape anything.
+ */
+ if ((map->lhs->tmpl_da->type == PW_TYPE_STRING) &&
+ (cf_pair_value_type(cp) == T_SINGLE_QUOTED_STRING)) {
+ value_data_t *vpd;
+
+ map->rhs->tmpl_data_value = vpd = talloc_zero(map->rhs, value_data_t);
+ rad_assert(vpd != NULL);
+
+ vpd->strvalue = talloc_typed_strdup(vpd, map->rhs->name);
+ rad_assert(vpd->strvalue != NULL);
+
+ map->rhs->type = TMPL_TYPE_DATA;
+ map->rhs->tmpl_data_type = map->lhs->tmpl_da->type;
+ map->rhs->tmpl_data_length = talloc_array_length(vpd->strvalue) - 1;
+ } else {
+ if (!tmpl_cast_in_place(map->rhs, map->lhs->tmpl_da)) {
+ cf_log_err(map->ci, "%s", fr_strerror());
+ return -1;
}
- } /* else we can't precompile the data */
- } /* loop over the conf_pairs in the update section */
+ }
+ } /* else we can't precompile the data */
+
+ return 0;
+}
+
+
+#ifdef WITH_UNLANG
+static modcallable *do_compile_modupdate(modcallable *parent, UNUSED rlm_components_t component,
+ CONF_SECTION *cs, char const *name2)
+{
+ int rcode;
+ modgroup *g;
+ modcallable *csingle;
+
+ value_pair_map_t *head;
+
+ /*
+ * This looks at cs->name2 to determine which list to update
+ */
+ rcode = map_afrom_cs(&head, cs, PAIR_LIST_REQUEST, PAIR_LIST_REQUEST, modcall_fixup_update, NULL, 128);
+ if (rcode < 0) return NULL; /* message already printed */
+ if (!head) {
+ cf_log_err_cs(cs, "'update' sections cannot be empty");
+ return NULL;
+ }
g = rad_malloc(sizeof(*g)); /* never fails */
memset(g, 0, sizeof(*g));
#include <freeradius-devel/radiusd.h>
#include <freeradius-devel/modules.h>
+#include <freeradius-devel/modcall.h>
#include <freeradius-devel/heap.h>
#include <freeradius-devel/rad_assert.h>
return c;
}
-/*
- * Verify that the cache section makes sense.
+/** Verify that a map in the cache section makes sense
+ *
*/
-static int cache_verify(rlm_cache_t *inst, value_pair_map_t **head)
+static int cache_verify(value_pair_map_t *map, UNUSED void *ctx)
{
- value_pair_map_t *map;
+ if (modcall_fixup_update(map, ctx) < 0) return -1;
- if (map_afrom_cs(head, cf_section_sub_find(inst->cs, "update"),
- PAIR_LIST_REQUEST, PAIR_LIST_REQUEST, MAX_ATTRMAP) < 0) {
+ if ((map->lhs->type != TMPL_TYPE_ATTR) &&
+ (map->lhs->type != TMPL_TYPE_LIST)) {
+ cf_log_err(map->ci, "Left operand must be an attribute ref or a list");
return -1;
}
- if (!*head) {
- cf_log_err_cs(inst->cs,
- "Cache config must contain an update section, and "
- "that section must not be empty");
-
+ switch (map->rhs->type) {
+ case TMPL_TYPE_EXEC:
+ cf_log_err(map->ci, "Exec values are not allowed");
return -1;
- }
-
- for (map = *head; map != NULL; map = map->next) {
- if ((map->lhs->type != TMPL_TYPE_ATTR) &&
- (map->lhs->type != TMPL_TYPE_LIST)) {
- cf_log_err(map->ci, "Left operand must be an attribute "
- "ref or a list");
-
- return -1;
- }
-
- /*
- * Can't copy an xlat expansion or literal into a list,
- * we don't know what type of attribute we'd need
- * to create.
- *
- * The only exception is where were using a unary
- * operator like !*.
- */
- if ((map->lhs->type == TMPL_TYPE_LIST) &&
- (map->op != T_OP_CMP_FALSE) &&
- ((map->rhs->type == TMPL_TYPE_XLAT) || (map->rhs->type == TMPL_TYPE_LITERAL))) {
- cf_log_err(map->ci, "Can't copy value into list (we don't know which attribute to create)");
-
- return -1;
- }
-
- switch (map->rhs->type) {
- case TMPL_TYPE_EXEC:
- cf_log_err(map->ci, "Exec values are not allowed");
-
- return -1;
-
- /*
- * Only =, :=, += and -= operators are supported for
- * cache entries.
- */
- case TMPL_TYPE_LITERAL:
- /*
- * @fixme: This should be moved into a common function
- * with the check in do_compile_modupdate.
- */
- if (map->lhs->type == TMPL_TYPE_ATTR) {
- VALUE_PAIR *vp;
- int ret;
-
- MEM(vp = pairalloc(map->lhs, map->lhs->tmpl_da));
- vp->op = map->op;
-
- ret = pairparsevalue(vp, map->rhs->name, 0);
- talloc_free(vp);
- if (ret < 0) {
- cf_log_err(map->ci, "%s", fr_strerror());
- return -1;
- }
- }
- /* FALL-THROUGH */
-
- case TMPL_TYPE_XLAT:
- case TMPL_TYPE_ATTR:
- switch (map->op) {
- case T_OP_SET:
- case T_OP_EQ:
- case T_OP_SUB:
- case T_OP_ADD:
- break;
+ /*
+ * Only =, :=, += and -= operators are supported for
+ * cache entries.
+ */
+ case TMPL_TYPE_LITERAL:
+ case TMPL_TYPE_XLAT:
+ case TMPL_TYPE_ATTR:
+ switch (map->op) {
+ case T_OP_SET:
+ case T_OP_EQ:
+ case T_OP_SUB:
+ case T_OP_ADD:
+ break;
- default:
- cf_log_err(map->ci, "Operator \"%s\" not "
- "allowed for %s values",
- fr_int2str(fr_tokens, map->op,
- "<INVALID>"),
- fr_int2str(tmpl_types, map->rhs->type,
- "<INVALID>"));
- return -1;
- }
default:
- break;
+ cf_log_err(map->ci, "Operator \"%s\" not allowed for %s values",
+ fr_int2str(fr_tokens, map->op, "<INVALID>"),
+ fr_int2str(tmpl_types, map->rhs->type, "<INVALID>"));
+ return -1;
}
+ default:
+ break;
}
+
return 0;
}
/*
* Make sure the users don't screw up too badly.
*/
- if (cache_verify(inst, &inst->maps) < 0) {
+ if (map_afrom_cs(&inst->maps, cf_section_sub_find(inst->cs, "update"),
+ PAIR_LIST_REQUEST, PAIR_LIST_REQUEST, cache_verify, NULL, MAX_ATTRMAP) < 0) {
+ return -1;
+ }
+
+ if (!inst->maps) {
+ cf_log_err_cs(inst->cs, "Cache config must contain an update section, and "
+ "that section must not be empty");
+
return -1;
}
return 0;
}
-/** 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)
+int rlm_ldap_map_verify(value_pair_map_t *map, void *instance)
{
- 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;
+ ldap_instance_t *inst = instance;
/*
- * The first map has cs as the parent.
- * The rest have the previous map as the parent.
+ * Destinations where we can put the VALUE_PAIRs we
+ * create using LDAP values.
*/
- 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;
- }
+ switch (map->lhs->type) {
+ case TMPL_TYPE_LIST:
+ case TMPL_TYPE_ATTR:
+ break;
- 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;
- }
+ default:
+ cf_log_err(map->ci, "RADIUS (RHS) of map must be an attribute or list, not %s",
+ fr_int2str(tmpl_types, map->lhs->type, "<INVALID>"));
+ 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);
- }
+ /*
+ * Sources we can use to get the name of the attribute
+ * we're retrieving from LDAP.
+ */
+ switch (map->rhs->type) {
+ case TMPL_TYPE_XLAT:
+ case TMPL_TYPE_ATTR:
+ case TMPL_TYPE_EXEC:
+ case TMPL_TYPE_LITERAL:
+ break;
- ctx = *tail = map;
- tail = &(map->next);
+ default:
+ cf_log_err(map->ci, "LDAP (LHS) of map must be an xlat, attribute, exec, or literal, not %s",
+ fr_int2str(tmpl_types, map->rhs->type, "<INVALID>"));
+ return -1;
}
- return 0;
-error:
- TALLOC_FREE(*out);
- return -1;
-}
-
-
-
-int rlm_ldap_map_verify(ldap_instance_t *inst, value_pair_map_t **head)
-{
- value_pair_map_t *map;
+ /*
+ * Only =, :=, += and -= operators are supported for LDAP mappings.
+ */
+ switch (map->op) {
+ case T_OP_SET:
+ case T_OP_EQ:
+ case T_OP_SUB:
+ case T_OP_ADD:
+ break;
- if (ldap_map_afrom_cs(head, cf_section_sub_find(inst->cs, "update"),
- PAIR_LIST_REPLY,
- PAIR_LIST_REQUEST, LDAP_MAX_ATTRMAP) < 0) {
+ default:
+ cf_log_err(map->ci, "Operator \"%s\" not allowed for LDAP mappings",
+ fr_int2str(fr_tokens, map->op, "<INVALID>"));
return -1;
}
+
/*
- * Attrmap only performs some basic validation checks, we need
- * to do rlm_ldap specific checks here.
+ * Be smart about whether we warn the user about missing passwords.
+ * If there are no password attributes in the mapping, then the user's either an idiot
+ * and has no idea what they're doing, or they're authenticating the user using a different
+ * method.
*/
- for (map = *head; map != NULL; map = map->next) {
- switch (map->lhs->type) {
- case TMPL_TYPE_LIST:
- break; /* parsed specially above */
-
- case TMPL_TYPE_ATTR:
+ if (!inst->expect_password && (map->lhs->type == TMPL_TYPE_ATTR) && map->lhs->tmpl_da) {
+ switch (map->lhs->tmpl_da->attr) {
+ case PW_CLEARTEXT_PASSWORD:
+ case PW_NT_PASSWORD:
+ case PW_USER_PASSWORD:
+ case PW_PASSWORD_WITH_HEADER:
+ case PW_CRYPT_PASSWORD:
/*
- * "update" sections in LDAP are for reading LDAP attributes, not other attributes.
+ * Because you just know someone is going to map NT-Password to the
+ * request list, and then complain it's not working...
*/
- if (map->rhs->type == TMPL_TYPE_ATTR) {
- cf_log_err(map->ci, "Cannot assign from a RADIUS attribute when reading data from LDAP.");
- return -1;
+ if (map->lhs->tmpl_list != PAIR_LIST_CONTROL) {
+ LDAP_DBGW("Mapping LDAP (%s) attribute to \"known good\" password attribute "
+ "(%s) in %s list. This is probably *NOT* the correct list, "
+ "you should prepend \"control:\" to password attribute "
+ "(control:%s)",
+ map->rhs->name, map->lhs->tmpl_da->name,
+ fr_int2str(pair_lists, map->lhs->tmpl_list, "<invalid>"),
+ map->lhs->tmpl_da->name);
}
- break;
- default:
- cf_log_err(map->ci, "valuepair destination must be an attribute or list");
- return -1;
- }
-
- switch (map->rhs->type) {
- case TMPL_TYPE_LIST:
- cf_log_err(map->ci, "LDAP attribute name cannot be derived from a list");
- return -1;
-
- default:
- break;
- }
-
- /*
- * Be smart about whether we warn the user about missing passwords.
- * If there are no password attributes in the mapping, then the user's either an idiot
- * and has no idea what they're doing, or they're authenticating the user using a different
- * method.
- */
- if (!inst->expect_password && (map->lhs->type == TMPL_TYPE_ATTR) && map->lhs->tmpl_da) {
- switch (map->lhs->tmpl_da->attr) {
- case PW_CLEARTEXT_PASSWORD:
- case PW_NT_PASSWORD:
- case PW_USER_PASSWORD:
- case PW_PASSWORD_WITH_HEADER:
- case PW_CRYPT_PASSWORD:
- /*
- * Because you just know someone is going to map NT-Password to the
- * request list, and then complain it's not working...
- */
- if (map->lhs->tmpl_list != PAIR_LIST_CONTROL) {
- LDAP_DBGW("Mapping LDAP (%s) attribute to \"known good\" password attribute "
- "(%s) in %s list. This is probably *NOT* the correct list, "
- "you should prepend \"control:\" to password attribute "
- "(control:%s)",
- map->rhs->name, map->lhs->tmpl_da->name,
- fr_int2str(pair_lists, map->lhs->tmpl_list, "<invalid>"),
- map->lhs->tmpl_da->name);
- }
-
- inst->expect_password = true;
- default:
- break;
- }
- }
-
- switch (map->rhs->type) {
- /*
- * Only =, :=, += and -= operators are supported for
- * cache entries.
- */
- case TMPL_TYPE_LITERAL:
- case TMPL_TYPE_XLAT:
- case TMPL_TYPE_ATTR:
- switch (map->op) {
- case T_OP_SET:
- case T_OP_EQ:
- case T_OP_SUB:
- case T_OP_ADD:
- break;
-
- default:
- cf_log_err(map->ci, "Operator \"%s\" not allowed for %s values",
- fr_int2str(fr_tokens, map->op, "<INVALID>"),
- fr_int2str(tmpl_types, map->rhs->type, "<INVALID>"));
- return -1;
- }
+ inst->expect_password = true;
default:
break;
}
}
+
return 0;
}
/*
* attrmap.c - Attribute mapping code.
*/
-int rlm_ldap_map_verify(ldap_instance_t *inst, value_pair_map_t **head);
+int rlm_ldap_map_verify(value_pair_map_t *map, void *instance);
void rlm_ldap_map_xlat_free(rlm_ldap_map_xlat_t const *expanded);
/*
* Build the attribute map
*/
- if (rlm_ldap_map_verify(inst, &(inst->user_map)) < 0) {
- goto error;
+ if (map_afrom_cs(&inst->user_map, cf_section_sub_find(inst->cs, "update"),
+ PAIR_LIST_REPLY, PAIR_LIST_REQUEST, rlm_ldap_map_verify, inst,
+ LDAP_MAX_ATTRMAP) < 0) {
+ return -1;
}
/*