Modify map_afrom_cs to take a validation callback to verify and fixup maps
authorArran Cudbard-Bell <a.cudbardb@freeradius.org>
Sun, 19 Oct 2014 22:54:36 +0000 (18:54 -0400)
committerArran Cudbard-Bell <a.cudbardb@freeradius.org>
Sun, 19 Oct 2014 22:56:16 +0000 (18:56 -0400)
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.

src/include/map.h
src/include/modcall.h
src/main/map.c
src/main/modcall.c
src/modules/rlm_cache/rlm_cache.c
src/modules/rlm_ldap/attrmap.c
src/modules/rlm_ldap/ldap.h
src/modules/rlm_ldap/rlm_ldap.c

index 4e9e50e..7d677eb 100644 (file)
@@ -64,6 +64,7 @@ typedef struct value_pair_map {
 } 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,
@@ -77,7 +78,7 @@ int           map_afrom_fields(TALLOC_CTX *ctx, value_pair_map_t **out, char const *lhs,
 
 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,
index f14a1b5..9a953dc 100644 (file)
@@ -21,6 +21,8 @@ extern "C" {
  */
 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
index 8d1847f..9f1dc7e 100644 (file)
@@ -243,164 +243,6 @@ int map_afrom_cp(TALLOC_CTX *ctx, value_pair_map_t **out, CONF_PAIR *cp,
                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;
@@ -422,10 +264,14 @@ error:
  *     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;
@@ -437,7 +283,7 @@ int map_afrom_cs(value_pair_map_t **out, CONF_SECTION *cs, pair_lists_t dst_list
 
        unsigned int total = 0;
        value_pair_map_t **tail, *map;
-       TALLOC_CTX *ctx;
+       TALLOC_CTX *parent;
 
        *out = NULL;
        tail = out;
@@ -448,7 +294,7 @@ int map_afrom_cs(value_pair_map_t **out, CONF_SECTION *cs, pair_lists_t dst_list
         *      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);
 
@@ -483,13 +329,18 @@ int map_afrom_cs(value_pair_map_t **out, CONF_SECTION *cs, pair_lists_t dst_list
                }
 
                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);
        }
 
index 032d9a1..cc21b89 100644 (file)
@@ -1575,31 +1575,127 @@ defaultactions[RLM_COMPONENT_COUNT][GROUPTYPE_COUNT][RLM_MODULE_NUMCODES] =
 #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
@@ -1608,54 +1704,115 @@ static modcallable *do_compile_modupdate(modcallable *parent, UNUSED rlm_compone
                 *      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));
index 0e4114d..0f438e6 100644 (file)
@@ -24,6 +24,7 @@ RCSID("$Id$")
 
 #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>
 
@@ -428,104 +429,47 @@ static rlm_cache_entry_t *cache_add(rlm_cache_t *inst, REQUEST *request, char co
        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;
 }
 
@@ -688,7 +632,15 @@ static int mod_instantiate(CONF_SECTION *conf, void *instance)
        /*
         *      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;
        }
 
index 2a6031c..4c0eb89 100644 (file)
@@ -126,285 +126,91 @@ static int rlm_ldap_map_getvalue(VALUE_PAIR **out, REQUEST *request, value_pair_
        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;
 }
 
index bfb4c5f..e4475a0 100644 (file)
@@ -365,7 +365,7 @@ rlm_rcode_t rlm_ldap_check_cached(ldap_instance_t const *inst, REQUEST *request,
 /*
  *     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);
 
index d143a5d..4a3e3f8 100644 (file)
@@ -704,8 +704,10 @@ static int mod_instantiate(CONF_SECTION *conf, void *instance)
        /*
         *      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;
        }
 
        /*