]> git.ipfire.org Git - thirdparty/freeradius-server.git/commitdiff
start of new pairmove API
authorAlan T. DeKok <aland@freeradius.org>
Sun, 17 Dec 2023 22:38:02 +0000 (17:38 -0500)
committerAlan T. DeKok <aland@freeradius.org>
Mon, 18 Dec 2023 00:41:46 +0000 (19:41 -0500)
which will (eventually) take LHS / op / RHS, so that it can be
called from SQL with fields.

Update rlm_files to call the new API, so that it no longer creates
an intermediate VP, and just passes the map to the new API.

Update rlm_files to move the "update control" attributes to the
reply list.  The new API will look at the tmpl to find the correct
request and list context.

src/lib/server/pairmove.c
src/lib/server/pairmove.h
src/modules/rlm_files/rlm_files.c

index d0a575ec960b992860753f09ed9188a23aa8831e..ca0f0b343562d867efab6e9df8ed68a5dd5e185a 100644 (file)
@@ -307,3 +307,109 @@ void radius_pairmove(request_t *request, fr_pair_list_t *to, fr_pair_list_t *fro
        talloc_free(edited);
        talloc_free(deleted);
 }
+
+/** Move a map using the operators from the old pairmove API.
+ *
+ */
+int fr_pairmove_map(request_t *request, map_t const *map)
+{
+       int rcode;
+       fr_pair_t *vp, *next;
+       fr_dict_attr_t const *da;
+       fr_pair_list_t *list;
+       TALLOC_CTX *ctx;
+
+       /*
+        *      Finds both the correct ctx and nested list.
+        */
+       tmpl_pair_list_and_ctx(ctx, list, request, tmpl_request(map->lhs), tmpl_list(map->lhs));
+       if (!ctx) return -1;
+
+       da = tmpl_attr_tail_da(map->lhs);
+
+       fr_assert(tmpl_is_data(map->rhs));
+
+       switch (map->op) {
+       case T_OP_CMP_FALSE:    /* delete all */
+               fr_pair_delete_by_da(list, da);
+               break;
+
+       case T_OP_EQ:           /* set only if not already exist */
+               vp = fr_pair_find_by_da(list, NULL, da);
+               if (vp) return 0;
+               goto add;
+
+       case T_OP_SET:          /* delete all and set one */
+               fr_pair_delete_by_da(list, da);
+               FALL_THROUGH;
+
+       case T_OP_ADD_EQ:       /* append one */
+       add:
+               vp = fr_pair_afrom_da(ctx, da);
+               if (!vp) return -1;
+
+               if (fr_value_box_copy(vp, &vp->data, tmpl_value(map->rhs)) < 0) {
+                       talloc_free(vp);
+                       return -1;
+               }
+
+               fr_pair_append(list, vp);
+               break;
+
+       case T_OP_PREPEND:      /* prepend one */
+               vp = fr_pair_afrom_da(ctx, da);
+               if (!vp) return -1;
+
+               if (fr_value_box_copy(vp, &vp->data, tmpl_value(map->rhs)) < 0) {
+                       talloc_free(vp);
+                       return -1;
+               }
+
+               fr_pair_prepend(list, vp);
+               break;
+
+       case T_OP_SUB_EQ:               /* delete if match */
+               vp = fr_pair_find_by_da(list, NULL, da);
+               if (!vp) break;
+
+       redo_sub:
+               next = fr_pair_find_by_da(list, vp, da);
+               rcode = fr_value_box_cmp_op(T_OP_CMP_EQ, &vp->data, tmpl_value(map->rhs));
+
+               if (rcode < 0) return -1;
+
+               if (rcode == 1) {
+                       fr_pair_delete(list, vp);
+               }
+
+               if (!next) break;
+               vp = next;
+               goto redo_sub;
+
+       case T_OP_CMP_EQ:       /* replace if not == */
+       case T_OP_LE:           /* replace if not <= */
+       case T_OP_GE:           /* replace if not >= */
+               vp = fr_pair_find_by_da(list, NULL, da);
+               if (!vp) goto add;
+
+       redo_filter:
+               rcode = fr_value_box_cmp_op(map->op, &vp->data, tmpl_value(map->rhs));
+               if (rcode < 0) return -1;
+
+               if (rcode == 0) {
+                       if (fr_value_box_copy(vp, &vp->data, tmpl_value(map->rhs)) < 0) {
+                               return -1;
+                       }
+               }
+
+               vp = fr_pair_find_by_da(list, vp, da);
+               if (vp) goto redo_filter;
+               break;
+
+       default:
+               fr_assert(0);
+               break;
+       }
+
+       return 0;
+}
index 40993bc17108d4c021cd48aeee915c4c22c94a39..223411071f85354309ced5a4b615dc23bea08050 100644 (file)
@@ -27,6 +27,7 @@
 RCSIDH(pairmove_h, "$Id$")
 
 #include <freeradius-devel/server/request.h>
+#include <freeradius-devel/server/map.h>
 
 #ifdef __cplusplus
 extern "C" {
@@ -34,6 +35,8 @@ extern "C" {
 
 void   radius_pairmove(request_t *request, fr_pair_list_t *to, fr_pair_list_t *from) CC_HINT(nonnull);
 
+int    fr_pairmove_map(request_t *request, map_t const *map);
+
 #ifdef __cplusplus
 }
 #endif
index d6187fd0f6df6dcaf00acdd9ccd4ffee9a5da436..c3a1e730e8ab386180e7d85d0954fe8789f8fb57 100644 (file)
@@ -128,6 +128,7 @@ static int getusersfile(TALLOC_CTX *ctx, char const *filename, fr_htrie_t **ptre
        fr_htrie_t *tree;
        fr_htrie_type_t htype;
        fr_value_box_t *box;
+       map_t           *reply_head;
 
        if (!filename) {
                *ptree = NULL;
@@ -148,8 +149,11 @@ static int getusersfile(TALLOC_CTX *ctx, char const *filename, fr_htrie_t **ptre
        entry = NULL;
        while ((entry = fr_dlist_next(&users.head, entry))) {
                map_t *map = NULL;
+               map_t *prev;
                fr_dict_attr_t const *da;
 
+               reply_head = NULL;
+
                /*
                 *      Look for improper use of '=' in the
                 *      check items.  They should be using
@@ -181,26 +185,20 @@ static int getusersfile(TALLOC_CTX *ctx, char const *filename, fr_htrie_t **ptre
                        }
 
                        /*
-                        *      Ignore attributes which are set
-                        *      properly.
+                        *      Move assignment operations to the reply list.
                         */
-                       if (map->op != T_OP_EQ) {
-                               continue;
-                       }
+                       switch (map->op) {
+                       case T_OP_EQ:
+                       case T_OP_SET:
+                       case T_OP_ADD_EQ:
+                               prev = map_list_remove(&entry->check, map);
+                               map_list_insert_after(&entry->reply, reply_head, map);
+                               reply_head = map;
+                               map = prev;
+                               break;
 
-                       /*
-                        *      If it's a vendor attribute,
-                        *      or it's a wire protocol,
-                        *      ensure it has '=='.
-                        */
-                       if ((fr_dict_vendor_num_by_da(da) != 0) ||
-                           (da->attr < 0x100)) {
-                               WARN("%s[%d] Changing '%s =' to '%s =='\n\tfor comparing RADIUS attribute in check item list for key %s",
-                                    entry->filename, entry->lineno,
-                                    da->name, da->name,
-                                    entry->name);
-                               map->op = T_OP_CMP_EQ;
-                               continue;
+                       default:
+                               break;
                        }
                } /* end of loop over check items */
 
@@ -220,6 +218,12 @@ static int getusersfile(TALLOC_CTX *ctx, char const *filename, fr_htrie_t **ptre
                        }
                        da = tmpl_attr_tail_da(map->lhs);
 
+                       if (map->op == T_OP_CMP_FALSE) {
+                               ERROR("%s[%d] Invalid operator '!*' for reply item %s",
+                                     entry->filename, entry->lineno, map->lhs->name);
+                               return -1;
+                       }
+
                        if ((htype != FR_HTRIE_TRIE) && (da == attr_next_shortest_prefix)) {
                                ERROR("%s[%d] Cannot use %s when key is not an IP / IP prefix",
                                      entry->filename, entry->lineno, da->name);
@@ -235,8 +239,6 @@ static int getusersfile(TALLOC_CTX *ctx, char const *filename, fr_htrie_t **ptre
                         *      free all subsequent maps.
                         */
                        if (da == attr_fall_through) {
-                               map_t *prev;
-
                                if (tmpl_is_data(map->rhs) && (tmpl_value_type(map->rhs) == FR_TYPE_BOOL)) {
                                        entry->fall_through = tmpl_value(map->rhs)->vb_bool;
                                }
@@ -246,15 +248,6 @@ static int getusersfile(TALLOC_CTX *ctx, char const *filename, fr_htrie_t **ptre
                                map = prev;
                                continue;
                        }
-
-                       /*
-                        *      If we allow list qualifiers in
-                        *      users_file.c, then this module also
-                        *      needs to be updated.  Ensure via an
-                        *      assertion that they do not get out of
-                        *      sync.
-                        */
-                       fr_assert(tmpl_list(map->lhs) == request_attr_reply);
                }
        }
 
@@ -475,10 +468,8 @@ redo:
         *      Find the entry for the user.
         */
        while (user_pl || default_pl) {
-               fr_pair_t *vp;
                map_t *map = NULL;
                PAIR_LIST const *pl;
-               fr_pair_list_t list;
                bool next_shortest_prefix;
                bool match = true;
 
@@ -508,14 +499,10 @@ redo:
                        default_pl = fr_dlist_next(&default_list->head, default_pl);
                }
 
-               fr_pair_list_init(&list);
-
                /*
-                *      Realize the map to a list of VPs
+                *      Run the check items.
                 */
                while ((map = map_list_next(&pl->check, map))) {
-                       fr_pair_list_t tmp_list;
-
                        RDEBUG3("    %s %s %s", map->lhs->name, fr_tokens[map->op], map->rhs->name);
 
                        /*
@@ -528,23 +515,15 @@ redo:
                        case T_OP_EQ:
                        case T_OP_SET:
                        case T_OP_ADD_EQ:
-                               fr_pair_list_init(&tmp_list);
-                               if (map_to_vp(request->control_ctx, &tmp_list, request, map, NULL) < 0) {
-                                       fr_pair_list_free(&list);
-                                       RPWARN("Failed parsing check item, skipping entry");
-                                       match = false;
-                                       break;
-                               }
-
-                               fr_pair_list_append(&list, &tmp_list);
-                               break;
+                               fr_assert(0);
+                               return -1;
 
                                /*
                                 *      Evaluate the map, including regexes.
                                 */
                        default:
                                if (!files_eval_map(request, map)) {
-                                       RDEBUG3("    failed match - %s", fr_strerror());
+                                       RDEBUG3("    failed match");
                                        match = false;
                                }
                                break;
@@ -553,46 +532,31 @@ redo:
                        if (!match) break;
                }
 
-               if (!match) {
-                       fr_pair_list_free(&list);
-                       continue;
-               }
+               if (!match) continue;
 
                RDEBUG2("Found match \"%s\" on line %d of %s", pl->name, pl->lineno, pl->filename);
                found = true;
                next_shortest_prefix = false;
 
-               /*
-                *      Move the control items over, too.
-                */
-               radius_pairmove(request, &request->control_pairs, &list);
-
                /* ctx may be reply */
                if (!map_list_empty(&pl->reply)) {
                        map = NULL;
-                       while ((map = map_list_next(&pl->reply, map))) {
-                               fr_pair_list_t tmp_list;
-                               fr_pair_list_init(&tmp_list);
-                               if (map->op == T_OP_CMP_FALSE) continue;
-
-                               if (map_to_vp(request->reply_ctx, &tmp_list, request, map, NULL) < 0) {
-                                       RPWARN("Failed parsing map for reply item %s, skipping it", map->lhs->name);
-                                       break;
-                               }
 
+                       while ((map = map_list_next(&pl->reply, map))) {
                                /*
-                                *      And do the same checks for prefix tries.
+                                *      Do Fall-Through style checks for prefix tries.
                                 */
-                               if (trie && (keylen > 0)) {
-                                       vp = fr_pair_list_head(&tmp_list);
-                                       if (vp->da == attr_next_shortest_prefix) {
-                                               next_shortest_prefix = vp->vp_bool;
-                                               fr_pair_list_free(&tmp_list);
-                                               continue;
-                                       }
+                               if (trie && (keylen > 0) && (tmpl_attr_tail_da(map->lhs) == attr_next_shortest_prefix)) {
+                                       fr_assert(tmpl_is_data(map->rhs));
+
+                                       next_shortest_prefix = tmpl_value(map->rhs)->vb_bool;
+                                       continue;
                                }
 
-                               radius_pairmove(request, &request->reply_pairs, &tmp_list);
+                               if (fr_pairmove_map(request, map) < 0) {
+                                       RPWARN("Failed parsing map for reply item %s, skipping it", map->lhs->name);
+                                       break;
+                               }
                        }
                }