Add attribute mapping functions to valuepair.c, and refactor previous list/request...
authorArran Cudbard-Bell <a.cudbardb@freeradius.org>
Fri, 30 Nov 2012 15:53:56 +0000 (15:53 +0000)
committerArran Cudbard-Bell <a.cudbardb@freeradius.org>
Sat, 1 Dec 2012 12:04:30 +0000 (12:04 +0000)
src/include/radiusd.h
src/main/evaluate.c
src/main/modcall.c
src/main/valuepair.c
src/main/xlat.c
src/modules/rlm_dhcp/rlm_dhcp.c
src/modules/rlm_rest/rest.c

index b313336..a544bc4 100644 (file)
@@ -279,6 +279,15 @@ typedef enum pair_lists {
 
 extern const FR_NAME_NUMBER pair_lists[];
 
+typedef enum requests {
+       REQUEST_UNKNOWN = 0,
+       REQUEST_OUTER,
+       REQUEST_CURRENT,
+       REQUEST_PARENT  /* For future use */
+} request_refs_t;
+
+extern const FR_NAME_NUMBER request_refs[];
+
 typedef struct pair_list {
        const char              *name;
        VALUE_PAIR              *check;
@@ -640,8 +649,8 @@ VALUE_PAIR *radius_pairmake(REQUEST *request, VALUE_PAIR **vps,
 /* xlat.c */
 typedef size_t (*RADIUS_ESCAPE_STRING)(REQUEST *, char *out, size_t outlen, const char *in, void *arg);
 
-int            radius_xlat(char * out, int outlen, const char *fmt,
-                          REQUEST * request, RADIUS_ESCAPE_STRING func, void *funcarg);
+size_t          radius_xlat(char * out, int outlen, const char *fmt,
+                           REQUEST * request, RADIUS_ESCAPE_STRING func, void *funcarg);
 typedef size_t (*RAD_XLAT_FUNC)(void *instance, REQUEST *, const char *, char *, size_t);
 int            xlat_register(const char *module, RAD_XLAT_FUNC func,
                              void *instance);
@@ -708,7 +717,49 @@ void radius_pairmove(REQUEST *request, VALUE_PAIR **to, VALUE_PAIR *from);
 
 VALUE_PAIR **radius_list(REQUEST *request, pair_lists_t list);
 pair_lists_t radius_list_name(const char **name, pair_lists_t unknown);
-int radius_ref_request(REQUEST **request, const char **name);
+int radius_request(REQUEST **request, request_refs_t name);
+request_refs_t radius_request_name(const char **name, request_refs_t unknown);
+
+/*
+ *  See main/valuepair.c
+ *
+ *  Value pair template, used when processing various mappings sections
+ *  to create a real valuepair later.
+ */
+typedef struct value_pair_tmpl {
+       const char              *name;
+       const DICT_ATTR         *da;
+       
+       int                     do_xlat;
+       
+       request_refs_t          request;
+       pair_lists_t            list;
+} VALUE_PAIR_TMPL;
+
+/*
+ *  Value pair map
+ */
+typedef struct value_pair_map {
+       VALUE_PAIR_TMPL         dst;
+       VALUE_PAIR_TMPL         src;
+       
+       FR_TOKEN                op_token;
+       
+       struct value_pair_map   *next;
+} VALUE_PAIR_MAP;
+
+typedef VALUE_PAIR *(*radius_tmpl_getvalue_t)(REQUEST *request,
+                                             VALUE_PAIR_TMPL *src, void *ctx);
+
+int radius_attr2tmpl(const char *name, VALUE_PAIR_TMPL *vpt,
+                    request_refs_t request_def, pair_lists_t list_def);
+int radius_str2tmpl(const char *name, VALUE_PAIR_TMPL *vpt);
+VALUE_PAIR_MAP *radius_cp2map(CONF_PAIR *cp, request_refs_t request_def,
+                             pair_lists_t list_def);
+int radius_map2request(REQUEST *request, const VALUE_PAIR_MAP *map,
+                      const char *src, radius_tmpl_getvalue_t func, void *ctx);
+void radius_mapfree(VALUE_PAIR_MAP **map);
+
 int radius_get_vp(REQUEST *request, const char *name, VALUE_PAIR **vp_p);
 
 #ifdef WITH_TLS
index 3cccfe9..a954bb0 100644 (file)
@@ -242,7 +242,7 @@ static int radius_do_cmp(REQUEST *request, int *presult,
                /*
                 *      Bare words on the left can be attribute names.
                 */
-               if (radius_get_vp(request, pleft, &vp)) {
+               if (!(radius_get_vp(request, pleft, &vp) < 0)) {
                        VALUE_PAIR myvp;
 
                        /*
@@ -1126,14 +1126,22 @@ int radius_update_attrlist(REQUEST *request, CONF_SECTION *cs,
        CONF_ITEM *ci;
        VALUE_PAIR *newlist, *vp;
        VALUE_PAIR **output_vps;
+       request_refs_t request_name;
        REQUEST *update_request = request;
 
        if (!request || !cs) return RLM_MODULE_INVALID;
 
+       request_name = radius_request_name(&name, REQUEST_CURRENT);
+       if (request_name == REQUEST_UNKNOWN) {
+               RDEBUG("ERROR: Invalid request name");
+               
+               return RLM_MODULE_INVALID;
+       }
+       
        /* 
         *      Qualifiers not valid for this request
         */
-       if(!radius_ref_request(&update_request, &name)){
+       if (!radius_request(&update_request, request_name)){
                RDEBUG("WARNING: List name refers to outer request"
                       " but not in a tunnel.");
                return RLM_MODULE_NOOP; 
index 3c6ad42..4b6580d 100644 (file)
@@ -550,7 +550,7 @@ int modcall(int component, modcallable *c, REQUEST *request)
                                goto handle_result;
                        }
 
-                       if (radius_get_vp(request, child->name, &vp)) {
+                       if (!(radius_get_vp(request, child->name, &vp) < 0)) {
                                RDEBUG2("%.*sforeach %s {",
                                        stack.pointer + 1, modcall_spaces,
                                        child->name);
index 474cc4b..73d2af3 100644 (file)
@@ -72,6 +72,13 @@ const FR_NAME_NUMBER pair_lists[] = {
        {  NULL , -1 }
 };
 
+const FR_NAME_NUMBER request_refs[] = {
+       { "outer",              REQUEST_OUTER },
+       { "current",            REQUEST_CURRENT },
+       { "parent",             REQUEST_PARENT },
+       {  NULL , -1 }
+};
+
 struct cmp {
        unsigned int attribute;
        unsigned int otherattr;
@@ -937,7 +944,7 @@ VALUE_PAIR **radius_list(REQUEST *request, pair_lists_t list)
  *
  * @see dict_attrbyname
  *
- * @param[in,out] name of attribute.
+ * @param[in+out] name of attribute.
  * @param[in] unknown the list to return if no qualifiers were found.
  * @return PAIR_LIST_UNKOWN if qualifiers couldn't be resolved to a list.
  */
@@ -976,6 +983,42 @@ pair_lists_t radius_list_name(const char **name, pair_lists_t unknown)
        return fr_substr2int(pair_lists, p, PAIR_LIST_UNKNOWN, (q - p));
 }
 
+/** Resolve request to a request.
+ * 
+ * Resolve name to a current request.
+ *
+ * @see radius_list
+ * @param[in+out] request to use as context, and to write result to.
+ * @param[in] name (request) to resolve to.
+ * @return TRUE if request is valid in this context, else FALSE.
+ */
+int radius_request(REQUEST **request, request_refs_t name)
+{
+       rad_assert(request && *request);
+       
+       switch (name) {
+               case REQUEST_CURRENT:
+                       return TRUE;
+               
+               case REQUEST_PARENT:    /* for future use in request chaining */
+               case REQUEST_OUTER:
+                       if (!(*request)->parent) {
+                               return FALSE;
+                       }
+                       
+                       *request = (*request)->parent;
+                       
+                       break;
+       
+               case REQUEST_UNKNOWN:
+               default:
+                       rad_assert(0);
+                       return FALSE;
+       }
+       
+       return TRUE;
+}
+
 /** Resolve attribute name to a request.
  * 
  * Check the name string for qualifiers that reference a parent request and
@@ -987,24 +1030,257 @@ pair_lists_t radius_list_name(const char **name, pair_lists_t unknown)
  * radius_ref_request should be called before radius_list_name.
  *
  * @see radius_list_name
- * @param[in,out] request current request.
  * @param[in,out] name of attribute.
- * @return FALSE if qualifiers found but not in a tunnel, else TRUE.
+ * @return one of the REQUEST_* definitions or REQUEST_UNKOWN
  */
-int radius_ref_request(REQUEST **request, const char **name)
+request_refs_t radius_request_name(const char **name, request_refs_t unknown)
 {
-       rad_assert(name && *name);
-       rad_assert(request && *request);
+       char *p;
+       int request;
        
-       if (strncmp(*name, "outer.", 6) == 0) {
-               if (!(*request)->parent) {
-                       return FALSE;
+       p = strchr(*name, '.');
+       if (!p) {
+               return REQUEST_CURRENT;
+       }
+       
+       request = fr_substr2int(request_refs, *name, unknown,
+                               p - *name);
+       
+       if (request != REQUEST_UNKNOWN) {
+               *name = p + 1;
+       }
+       
+       return request;
+}
+
+/** Parse qualifiers to convert attrname into a VALUE_PAIR_TMPL.
+ *
+ * VPTs are used in various places where we need to pre-parse configuration 
+ * sections into attribute mappings.
+ *
+ * @param[in] name attribute name including qualifiers.
+ * @param[out] vpt to modify.
+ * @param[in] request_def The default request to insert unqualified 
+ *     attributes into.
+ * @param[in] list_def The default list to insert unqualified attributes into.
+ * @return -1 if either the attribute or qualifier were invalid, else 0
+ */
+int radius_attr2tmpl(const char *name, VALUE_PAIR_TMPL *vpt,
+                    request_refs_t request_def, pair_lists_t list_def)
+{
+       char buffer[128];
+       const char *p;
+       size_t len;
+       
+       vpt->name = strdup(name);
+       
+       p = vpt->name;
+       
+       vpt->request = radius_request_name(&p, request_def);
+       len = p - name;
+       if (vpt->request == REQUEST_UNKNOWN) {
+               strlcpy(buffer, name, len < sizeof(buffer) ?
+                       len + 1 : sizeof(buffer));
+               
+               radlog(L_ERR, "Invalid request qualifier \"%s\"", buffer);
+               return -1;      
+       }
+       name += len;
+       
+       vpt->list = radius_list_name(&p, list_def);
+       if (vpt->list == PAIR_LIST_UNKNOWN) {
+               len = p - name;
+               strlcpy(buffer, name, len < sizeof(buffer) ?
+                       len + 1 : sizeof(buffer));
+                               
+               radlog(L_ERR, "Invalid list qualifier \"%s\"", buffer);
+               return -1;
+       }
+       
+       vpt->da = dict_attrbyname(p);
+       if (!vpt->da) {
+               radlog(L_ERR, "Attribute \"%s\" unknown", p);
+               return -1;
+       }
+       
+       return 0;
+}
+
+/** Convert module specific attribute id to VALUE_PAIR_TMPL.
+ *
+ * @param[in] name string to convert.
+ * @param[out] vpt to modify.
+ * @return 0
+ */
+int radius_str2tmpl(const char *name, VALUE_PAIR_TMPL *vpt)
+{
+       vpt->name = strdup(name);
+       
+       return 0;
+}
+
+/** Release memory used by a map linked list.
+ *
+ * @param map Head of the map linked list.
+ */
+void radius_mapfree(VALUE_PAIR_MAP **map)
+{
+       VALUE_PAIR_MAP *next, *vpm;
+       
+       if (!map) return;
+       
+       vpm = *map; 
+        
+       while (vpm != NULL) {
+               next = vpm->next;
+               
+               if (vpm->src.name) free(vpm->src.name);
+               if (vpm->dst.name) free(vpm->dst.name);
+               
+               free(vpm);
+               vpm = next;
+       }
+       
+       *map = NULL;
+}
+
+/** Convert CONFIG_PAIR to VALUE_PAIR_MAP.
+ *
+ * Treats the left operand as a <request>.<list>.<attribute> reference
+ * and the right operand as a module specific value.
+ *
+ * The left operand will be pre-parsed into request ref, dst list, and da,
+ * the right operand will be left as a string.
+ *
+ * Return must be freed with radius_mapfree.
+ *
+ * @param[in] cp to convert to map.
+ * @param[in] request_def The default request to insert unqualified
+ *     attributes into.
+ * @param[in] list_def The default list to insert unqualified attributes into.
+ * @return VALUE_PAIR_MAP if successful or NULL on error.
+ */
+VALUE_PAIR_MAP *radius_cp2map(CONF_PAIR *cp, request_refs_t request_def,
+                             pair_lists_t list_def)
+{
+       VALUE_PAIR_MAP *map;
+       const char *attr;
+       const char *value;
+       
+       map = rad_malloc(sizeof(VALUE_PAIR_MAP));
+       memset(map, 0, sizeof(VALUE_PAIR_MAP));
+     
+       attr = cf_pair_attr(cp);
+       
+       if (radius_attr2tmpl(attr, &map->dst, list_def, request_def) < 0){
+               goto error;
+       }
+
+       value = cf_pair_value(cp);
+       if (!value) {
+               radlog(L_ERR, "Missing attribute name");
+               
+               goto error;
+       }
+       
+       if (radius_str2tmpl(value, &map->src) < 0) {
+               goto error;
+       }
+       
+       map->op_token = cf_pair_operator(cp);
+       
+       /*
+        *      Infer whether we need to expand the mapping values
+        *      The old style attribute map allowed the user to specify
+        *      whether the LDAP value should be expanded. 
+        *      We can't really support that easily, but equivalent
+        *      functionality should be available with %{eval:}
+        */
+       switch (cf_pair_value_type(cp))
+       {
+               case T_BARE_WORD:
+               case T_SINGLE_QUOTED_STRING:
+                       map->src.do_xlat = FALSE;
+               break;
+               case T_BACK_QUOTED_STRING:
+               case T_DOUBLE_QUOTED_STRING:
+                       map->src.do_xlat = TRUE;                
+               break;
+               default:
+                       rad_assert(0);
+                       goto error;
+       }
+       
+       return map;
+       
+       error:
+               radius_mapfree(&map);
+               return NULL;
+}
+
+/** Convert VALUE_PAIR_MAP to VALUE_PAIR(s) and add them to a REQUEST.
+ *
+ * Takes a single VALUE_PAIR_MAP, resolves request and list identifiers
+ * to pointers in the current request, the attempts to retrieve module
+ * specific value(s) using callback, and adds the resulting values to the
+ * correct request/list.
+ *
+ * @param request The current request.
+ * @param map specifying destination attribute and location and src identifier.
+ * @param func to retrieve module specific values and convert them to
+ *     VLAUE_PAIRS.
+ * @param ctx to be passed to func.
+ * @param src name to be used in debugging if different from map value.
+ * @return -1 if either attribute or qualifier weren't valid in this context
+ *     or callback returned NULL pointer, else 0.
+ */
+int radius_map2request(REQUEST *request, const VALUE_PAIR_MAP *map,
+                      const char *src, radius_tmpl_getvalue_t func, void *ctx)
+{
+       VALUE_PAIR **list, *vp, *head;
+       char buffer[MAX_STRING_LEN];
+       
+       if (!radius_request(&request, map->dst.request)) {
+               RDEBUG("WARNING: Request in mapping \"%s\" -> \"%s\" "
+                      "invalid in this context, skipping!",
+                      map->src.name, map->dst.name);
+               
+               return -1;
+       }
+       
+       list = radius_list(request, map->dst.list);
+       if (!list) {
+               RDEBUG("WARNING: List in mapping \"%s\" -> \"%s\" "
+                      "invalid in this context, skipping!",
+                      map->src.name, map->dst.name);
+                      
+               return -1;
+       }
+       
+       head = func(request, &map->dst, ctx);
+       if (head == NULL) {
+               return -1;
+       }
+       
+       for (vp = head; vp != NULL; vp = vp->next) {
+               vp->operator = map->op_token;
+               
+               if (debug_flag) {
+                       vp_prints_value(buffer, sizeof(buffer), vp, 1);
+                       
+                       RDEBUG("\t%s %s %s (%s)", map->dst.name,
+                              fr_int2str(fr_tokens, vp->operator, "¿unknown?"), 
+                              buffer, src ? src : map->src.name);
                }
-               *request = (*request)->parent;
-               *name += 6;
        }
        
-       return TRUE;
+       /*
+        *      Use pairmove so the operator is respected
+        */
+       pairmove(list, &vp);
+       pairfree(&vp); /* Free the VP if for some reason it wasn't moved */
+       
+       return 0;
 }
 
 /** Return a VP from the specified request.
@@ -1013,42 +1289,40 @@ int radius_ref_request(REQUEST **request, const char **name)
  * @param name attribute name including qualifiers.
  * @param vp_p where to write the pointer to the resolved VP. 
  *     Will be NULL if the attribute couldn't be resolved.
- * @return False if either the attribute or qualifier were invalid, else true
+ * @return -1 if either the attribute or qualifier were invalid, else 0
  */
 int radius_get_vp(REQUEST *request, const char *name, VALUE_PAIR **vp_p)
 {
+       VALUE_PAIR_TMPL vpt;
        VALUE_PAIR **vps;
-       pair_lists_t list;
-       
-       const DICT_ATTR *da;
-       
+
        *vp_p = NULL;
        
-       if (!radius_ref_request(&request, &name)) {
-               RDEBUG("WARNING: Attribute name refers to outer request"
-                      " but not in a tunnel.");
-               return TRUE;    /* Discuss, we don't actually know if
-                                  the attrname was valid... */
+       if (radius_attr2tmpl(name, &vpt, REQUEST_CURRENT, PAIR_LIST_REQUEST) < 0) {
+               return -1;
        }
        
-       list = radius_list_name(&name, PAIR_LIST_REQUEST);
-       if (list == PAIR_LIST_UNKNOWN) {
-               RDEBUG("ERROR: Invalid list qualifier");
-               return FALSE;
+       if (radius_request(&request, vpt.request) < 0) {
+               RDEBUG("WARNING: Specified request \"%s\" is not available in "
+                      "this context", fr_int2str(request_refs, vpt.request,
+                                                 "¿unknown?"));
+                      
+               return 0;
        }
        
-       da = dict_attrbyname(name);
-       if (!da) {
-               RDEBUG("ERROR: Attribute \"%s\" unknown", name);
-               return FALSE;
+       vps = radius_list(request, vpt.list);
+       if (!vps) {
+               RDEBUG("WARNING: Specified list \"%s\" is not available in "
+                      "this context", fr_int2str(pair_lists, vpt.list,
+                                                 "¿unknown?"));
+                      
+               return 0;
        }
-
-       vps = radius_list(request, list);
-       rad_assert(vps);
        
        /*
         *      May not may not be found, but it *is* a known name.
         */
-       *vp_p = pairfind(*vps, da->attr, da->vendor);
-       return TRUE;
+       *vp_p = pairfind(*vps, vpt.da->attr, vpt.da->vendor);
+       
+       return -1;
 }
index 46419fd..b7194f3 100644 (file)
@@ -496,7 +496,7 @@ static size_t xlat_integer(UNUSED void *instance, REQUEST *request,
 
        while (isspace((int) *fmt)) fmt++;
 
-       if (!radius_get_vp(request, fmt, &vp) || !vp) {
+       if ((radius_get_vp(request, fmt, &vp) < 0) || !vp) {
                *out = '\0';
                return 0;
        }
@@ -532,7 +532,7 @@ static size_t xlat_hex(UNUSED void *instance, REQUEST *request,
 
        while (isspace((int) *fmt)) fmt++;
 
-       if (!radius_get_vp(request, fmt, &vp) || !vp) {
+       if ((radius_get_vp(request, fmt, &vp) < 0) || !vp) {
                *out = '\0';
                return 0;
        }
@@ -569,7 +569,7 @@ static size_t xlat_base64(UNUSED void *instance, REQUEST *request,
        
        while (isspace((int) *fmt)) fmt++;
 
-       if (!radius_get_vp(request, fmt, &vp) || !vp) {
+       if ((radius_get_vp(request, fmt, &vp) < 0) || !vp) {
                *out = '\0';
                return 0;
        }
@@ -654,7 +654,7 @@ static size_t xlat_string(UNUSED void *instance, REQUEST *request,
                return 0;
        }
 
-       if (!radius_get_vp(request, fmt, &vp)) goto nothing;
+       if (radius_get_vp(request, fmt, &vp) < 0) goto nothing;
 
        if (!vp) goto nothing;
 
index aa9a67c..6120e77 100644 (file)
@@ -56,7 +56,7 @@ static size_t dhcp_options_xlat(UNUSED void *instance, REQUEST *request,
        while (isspace((int) *fmt)) fmt++;
        
        
-       if (!radius_get_vp(request, fmt, &vp) || !vp) {
+       if ((radius_get_vp(request, fmt, &vp) < 0) || !vp) {
                 *out = '\0';
                 
                 return 0;
index 5762695..d39b3e4 100644 (file)
@@ -989,7 +989,8 @@ static int rest_decode_post(rlm_rest_t *instance,
        const DICT_ATTR **current, *processed[REST_BODY_MAX_ATTRS + 1];
        VALUE_PAIR *tmp;
 
-       pair_lists_t list;
+       pair_lists_t list_name;
+       request_refs_t request_name;
        REQUEST *reference = request;
        VALUE_PAIR **vps;
 
@@ -1016,8 +1017,17 @@ static int rest_decode_post(rlm_rest_t *instance,
                p = (q + 1);
 
                RDEBUG("Decoding attribute \"%s\"", name);
+               
+               request_name = radius_request_name(&attribute, REQUEST_CURRENT);
+               if (request_name == REQUEST_UNKNOWN) {
+                       RDEBUG("WARNING: Invalid request qualifier, skipping");
+
+                       curl_free(name);
+
+                       continue;
+               }
 
-               if (!radius_ref_request(&reference, &attribute)) {
+               if (!radius_request(&reference, request_name)) {
                        RDEBUG("WARNING: Attribute name refers to outer request"
                               " but not in a tunnel, skipping");
 
@@ -1026,8 +1036,8 @@ static int rest_decode_post(rlm_rest_t *instance,
                        continue;
                }
 
-               list = radius_list_name(&attribute, PAIR_LIST_REPLY);
-               if (list == PAIR_LIST_UNKNOWN) {
+               list_name = radius_list_name(&attribute, PAIR_LIST_REPLY);
+               if (list_name == PAIR_LIST_UNKNOWN) {
                        RDEBUG("WARNING: Invalid list qualifier, skipping");
 
                        curl_free(name);
@@ -1045,7 +1055,7 @@ static int rest_decode_post(rlm_rest_t *instance,
                        continue;
                }
 
-               vps = radius_list(reference, list);
+               vps = radius_list(reference, list_name);
 
                assert(vps);
 
@@ -1267,7 +1277,8 @@ static VALUE_PAIR *json_pairmake(rlm_rest_t *instance,
        const DICT_ATTR *da;
        VALUE_PAIR *vp;
        
-       pair_lists_t list;
+       request_refs_t request_name;
+       pair_lists_t list_name;
        REQUEST *reference = request;
        VALUE_PAIR **vps;
 
@@ -1313,16 +1324,23 @@ static VALUE_PAIR *json_pairmake(rlm_rest_t *instance,
                 *      pairlist.
                 */
                RDEBUG2("Decoding attribute \"%s\"", name);
+               
+               request_name = radius_request_name(&attribute, REQUEST_CURRENT);
+               if (request_name == REQUEST_UNKNOWN) {
+                       RDEBUG("WARNING: Request qualifier, skipping");
+
+                       continue;
+               }
 
-               if (!radius_ref_request(&reference, &attribute)) {
+               if (!radius_request(&reference, request_name)) {
                        RDEBUG("WARNING: Attribute name refers to outer request"
                               " but not in a tunnel, skipping");
 
                        continue;
                }
 
-               list = radius_list_name(&attribute, PAIR_LIST_REPLY);
-               if (list == PAIR_LIST_UNKNOWN) {
+               list_name = radius_list_name(&attribute, PAIR_LIST_REPLY);
+               if (list_name == PAIR_LIST_UNKNOWN) {
                        RDEBUG("WARNING: Invalid list qualifier, skipping");
 
                        continue;
@@ -1336,7 +1354,7 @@ static VALUE_PAIR *json_pairmake(rlm_rest_t *instance,
                        continue;
                }
 
-               vps = radius_list(reference, list);
+               vps = radius_list(reference, list_name);
 
                assert(vps);