]> git.ipfire.org Git - thirdparty/freeradius-server.git/commitdiff
regex now loops over input
authorAlan T. DeKok <aland@freeradius.org>
Tue, 12 Jul 2022 18:48:42 +0000 (14:48 -0400)
committerAlan T. DeKok <aland@freeradius.org>
Thu, 14 Jul 2022 14:04:01 +0000 (10:04 -0400)
src/lib/unlang/xlat_expr.c
src/tests/keywords/if-regex-multivalue

index d7ffbbaa3c7f21478ec33067619c1f179a5a2611..0a537b175840fff3219e5a842ad49747aabf08c8 100644 (file)
@@ -460,8 +460,40 @@ static int xlat_instantiate_regex(xlat_inst_ctx_t const *xctx)
        return 0;
 }
 
+
+static const fr_sbuff_escape_rules_t regex_escape_rules = {
+       .name = "regex",
+       .chr = '\\',
+       .subs = {
+               ['$'] = '$',
+               ['('] = '(',
+               ['*'] = '*',
+               ['+'] = '+',
+               ['.'] = '.',
+               ['/'] = '/',
+               ['?'] = '?',
+               ['['] = '[',
+               ['\\'] = '\\',
+               ['^'] = '^',
+               ['`'] = '`',
+               ['|'] = '|',
+               ['\a'] = 'a',
+               ['\b'] = 'b',
+               ['\n'] = 'n',
+               ['\r'] = 'r',
+               ['\t'] = 't',
+               ['\v'] = 'v'
+       },
+       .esc = {
+               SBUFF_CHAR_UNPRINTABLES_LOW,
+               SBUFF_CHAR_UNPRINTABLES_EXTENDED
+       },
+       .do_utf8 = true,
+       .do_oct = true
+};
+
 static xlat_arg_parser_t const regex_op_xlat_args[] = {
-       { .required = true, .concat = true, .type = FR_TYPE_STRING },
+       { .required = true, .type = FR_TYPE_STRING },
        { .concat = true, .type = FR_TYPE_STRING },
        XLAT_ARG_PARSER_TERMINATOR
 };
@@ -470,7 +502,7 @@ static xlat_arg_parser_t const regex_op_xlat_args[] = {
 /** Perform a regular expressions comparison between two operands
  *
  * @param[in] request          The current request.
- * @param[in] subject          to executed regex against.
+ * @param[in] in               list of item or items
  * @param[in,out] preg         Pointer to pre-compiled or runtime-compiled
  *                             regular expression.  In the case of runtime-compiled
  *                             the pattern may be stolen by the `regex_sub_to_request`
@@ -485,41 +517,82 @@ static xlat_arg_parser_t const regex_op_xlat_args[] = {
  *     - 0 for "no match".
  *     - 1 for "match".
  */
-static xlat_action_t xlat_regex_match(TALLOC_CTX *ctx, request_t *request, fr_value_box_t const *subject, regex_t **preg,
+static xlat_action_t xlat_regex_match(TALLOC_CTX *ctx, request_t *request, fr_value_box_list_t *in, regex_t **preg,
                                      fr_dcursor_t *out, fr_token_t op)
 {
        uint32_t        subcaptures;
-       int             ret;
+       int             ret = 0;
 
        fr_regmatch_t   *regmatch;
        fr_value_box_t  *dst;
+       fr_value_box_t  *arg, *vb;
+       fr_sbuff_t      *agg;
+       char const      *subject;
+       size_t          len;
+
+       FR_SBUFF_TALLOC_THREAD_LOCAL(&agg, 256, 8192);
 
-       if (!fr_cond_assert(subject != NULL)) return XLAT_ACTION_FAIL;
-       if (!fr_cond_assert(subject->type == FR_TYPE_STRING)) return XLAT_ACTION_FAIL;
+       arg = fr_dlist_head(in);
+       fr_assert(arg != NULL);
+       fr_assert(arg->type == FR_TYPE_GROUP);
 
        subcaptures = regex_subcapture_count(*preg);
        if (!subcaptures) subcaptures = REQUEST_MAX_REGEX + 1;  /* +1 for %{0} (whole match) capture group */
        MEM(regmatch = regex_match_data_alloc(NULL, subcaptures));
 
-       /*
-        *      Evaluate the expression
-        */
-       ret = regex_exec(*preg, subject->vb_strvalue, subject->vb_length, regmatch);
-       switch (ret) {
-       default:
-               RPEDEBUG("REGEX failed");
-               return XLAT_ACTION_FAIL;
+       while ((vb = fr_dlist_pop_head(&arg->vb_group)) != NULL) {
+               if (vb->type == FR_TYPE_STRING) {
+                       subject = vb->vb_strvalue;
+                       len = vb->vb_length;
 
-       case 0:
-               regex_sub_to_request(request, NULL, NULL);      /* clear out old entries */
-               break;
+               } else {
+                       fr_value_box_list_t     list;
 
-       case 1:
-               regex_sub_to_request(request, preg, &regmatch);
-               break;
+                       fr_value_box_list_init(&list);
+                       fr_dlist_insert_head(&list, vb);
+                       vb = NULL;
+
+                       /*
+                        *      Concatenate everything, and escape untrusted inputs.
+                        */
+                       if (fr_value_box_list_concat_as_string(NULL, agg, &list, NULL, 0, &regex_escape_rules,
+                                                              FR_VALUE_BOX_LIST_FREE_BOX, true, false) < 0) {
+                               RPEDEBUG("Failed concatenating regular expression string");
+                               talloc_free(regmatch);
+                               return XLAT_ACTION_FAIL;
+                       }
+
+                       subject = fr_sbuff_start(agg);
+                       len = fr_sbuff_used(agg);
+               }
 
+               /*
+                *      Evaluate the expression
+                */
+               ret = regex_exec(*preg, subject, len, regmatch);
+               switch (ret) {
+               default:
+                       RPEDEBUG("REGEX failed");
+                       talloc_free(vb);
+                       talloc_free(regmatch);
+                       return XLAT_ACTION_FAIL;
+
+               case 0:
+                       regex_sub_to_request(request, NULL, NULL);      /* clear out old entries */
+                       continue;
+
+               case 1:
+                       RDEBUG("MATCH");
+                       regex_sub_to_request(request, preg, &regmatch);
+                       talloc_free(vb);
+                       goto done;
+
+               }
+
+               talloc_free(vb);
        }
 
+done:  
        talloc_free(regmatch);  /* free if not consumed */
 
        MEM(dst = fr_value_box_alloc(ctx, FR_TYPE_BOOL, attr_expr_bool_enum, false));
@@ -530,37 +603,6 @@ static xlat_action_t xlat_regex_match(TALLOC_CTX *ctx, request_t *request, fr_va
        return XLAT_ACTION_DONE;
 }
 
-static const fr_sbuff_escape_rules_t regex_escape_rules = {
-       .name = "regex",
-       .chr = '\\',
-       .subs = {
-               ['$'] = '$',
-               ['('] = '(',
-               ['*'] = '*',
-               ['+'] = '+',
-               ['.'] = '.',
-               ['/'] = '/',
-               ['?'] = '?',
-               ['['] = '[',
-               ['\\'] = '\\',
-               ['^'] = '^',
-               ['`'] = '`',
-               ['|'] = '|',
-               ['\a'] = 'a',
-               ['\b'] = 'b',
-               ['\n'] = 'n',
-               ['\r'] = 'r',
-               ['\t'] = 't',
-               ['\v'] = 'v'
-       },
-       .esc = {
-               SBUFF_CHAR_UNPRINTABLES_LOW,
-               SBUFF_CHAR_UNPRINTABLES_EXTENDED
-       },
-       .do_utf8 = true,
-       .do_oct = true
-};
-
 static xlat_action_t xlat_regex_resume(TALLOC_CTX *ctx, fr_dcursor_t *out,
                                       xlat_ctx_t const *xctx,
                                       request_t *request, fr_value_box_list_t *in)
@@ -568,7 +610,6 @@ static xlat_action_t xlat_regex_resume(TALLOC_CTX *ctx, fr_dcursor_t *out,
        xlat_regex_inst_t const *inst = talloc_get_type_abort_const(xctx->inst, xlat_regex_inst_t);
        xlat_regex_rctx_t       *rctx = talloc_get_type_abort(xctx->rctx, xlat_regex_rctx_t);
        ssize_t                 slen;
-       fr_value_box_t          *lhs;
        regex_t                 *preg = NULL;
        fr_sbuff_t              *agg;
 
@@ -583,14 +624,9 @@ static xlat_action_t xlat_regex_resume(TALLOC_CTX *ctx, fr_dcursor_t *out,
        }
 
        /*
-        *      LHS should already have been expanded.  RHS was just expanded by us.
-        */
-       lhs = fr_dlist_head(in);
-
-       /*
-        *      Because we expanded the RHS ourselves, the "concat"
-        *      flag to the RHS argument is ignored.  So we just
-        *      concatenate it here.  We escape the various untrusted inputs.
+        *      Because we expanded the RHS ourselves, the "concat"
+        *      flag to the RHS argument is ignored.  So we just
+        *      concatenate it here.  We escape the various untrusted inputs.
         */
        if (fr_value_box_list_concat_as_string(NULL, agg, &rctx->list, NULL, 0, &regex_escape_rules,
                                               FR_VALUE_BOX_LIST_FREE_BOX, true, false) < 0) {
@@ -604,7 +640,7 @@ static xlat_action_t xlat_regex_resume(TALLOC_CTX *ctx, fr_dcursor_t *out,
                             tmpl_regex_flags(inst->xlat->vpt), true, true); /* flags, allow subcaptures, at runtime */
        if (slen <= 0) return XLAT_ACTION_FAIL;
 
-       return xlat_regex_match(ctx, request, lhs, &preg, out, inst->op);
+       return xlat_regex_match(ctx, request, in, &preg, out, inst->op);
 }
 
 static xlat_action_t xlat_regex_op(TALLOC_CTX *ctx, fr_dcursor_t *out,
@@ -615,9 +651,6 @@ static xlat_action_t xlat_regex_op(TALLOC_CTX *ctx, fr_dcursor_t *out,
        xlat_regex_inst_t const *inst = talloc_get_type_abort_const(xctx->inst, xlat_regex_inst_t);
        xlat_regex_rctx_t       *rctx;
        regex_t                 *preg;
-       fr_value_box_t          *lhs;
-
-       lhs = fr_dlist_head(in);
 
        /*
         *      Just run precompiled regexes.
@@ -625,7 +658,7 @@ static xlat_action_t xlat_regex_op(TALLOC_CTX *ctx, fr_dcursor_t *out,
        if (inst->regex) {
                preg = tmpl_regex(inst->xlat->vpt);
 
-               return xlat_regex_match(ctx, request, lhs, &preg, out, op);
+               return xlat_regex_match(ctx, request, in, &preg, out, op);
        }
 
        MEM(rctx = talloc_zero(unlang_interpret_frame_talloc_ctx(request), xlat_regex_rctx_t));
index 66f0315144a64a751351ef752eb0a5f5bb177ddc..3a08820b274e5ad056b2999a8933ac5e752ca7a3 100644 (file)
@@ -8,6 +8,15 @@ update request {
        &Vendor-Specific.Cisco.AVPair += 'baz=foo'
 }
 
+if (&Vendor-Specific.Cisco.AVPair[1] =~ /bar=(.*)/) {
+       if ("%{1}" != 'baz') {
+               test_fail
+       }
+}
+else {
+       test_fail
+}
+
 if (&Vendor-Specific.Cisco.AVPair[*] =~ /bar=(.*)/) {
        if ("%{1}" != 'baz') {
                test_fail