]> git.ipfire.org Git - thirdparty/freeradius-server.git/commitdiff
special handlers for module return codes
authorAlan T. DeKok <aland@freeradius.org>
Thu, 2 Jun 2022 17:09:01 +0000 (13:09 -0400)
committerAlan T. DeKok <aland@freeradius.org>
Fri, 3 Jun 2022 11:16:00 +0000 (07:16 -0400)
which evalute to "true" if they match.

src/lib/unlang/xlat_eval.c
src/lib/unlang/xlat_expr.c
src/lib/unlang/xlat_priv.h
src/tests/unit/xlat/cond_base.txt
src/tests/unit/xlat/expr.txt

index bc2c8de957410c5d0e60daa6e5ffd629069b97f7..ab0029864a8e2d0a3b68b76f1b7ed6074763a58a 100644 (file)
@@ -48,7 +48,6 @@ static fr_dict_autoload_t xlat_eval_dict[] = {
 
 static fr_dict_attr_t const *attr_client_ip_address;
 static fr_dict_attr_t const *attr_client_shortname;
-static fr_dict_attr_t const *attr_module_return_code;
 static fr_dict_attr_t const *attr_packet_dst_ip_address;
 static fr_dict_attr_t const *attr_packet_dst_ipv6_address;
 static fr_dict_attr_t const *attr_packet_dst_port;
@@ -61,6 +60,7 @@ static fr_dict_attr_t const *attr_virtual_server;
 static fr_dict_attr_t const *attr_packet_authentication_vector;
 static fr_dict_attr_t const *attr_packet_type;
 fr_dict_attr_t const       *attr_expr_bool_enum; /* xlat_expr.c */
+fr_dict_attr_t const           *attr_module_return_code; /* xlat_expr.c */
 
 static fr_dict_attr_autoload_t xlat_eval_dict_attr[] = {
        { .out = &attr_client_ip_address, .name = "Client-IP-Address", .type = FR_TYPE_IPV4_ADDR, .dict = &dict_freeradius },
index 5a49a93a0a52c2103fead75040b638e666d45425..c3ea69294a6886b37ad2a0652ca86bd3ad2a301f 100644 (file)
@@ -1192,6 +1192,48 @@ static int xlat_function_args_to_tmpl(xlat_inst_ctx_t const *xctx)
 }
 
 
+static xlat_arg_parser_t const xlat_func_rcode_arg = {
+       .concat = true, .type = FR_TYPE_STRING,
+};
+
+/** Return the rcode as a string, or bool match if the argument is an rcode name
+ *
+ * Example:
+@verbatim
+"%{rcode:}" == "handled"
+"%{rcode:handled}" == true
+@endverbatim
+ *
+ * @ingroup xlat_functions
+ */
+static xlat_action_t xlat_func_rcode(TALLOC_CTX *ctx, fr_dcursor_t *out,
+                                    UNUSED xlat_ctx_t const *xctx,
+                                    request_t *request, fr_value_box_list_t *args)
+{
+       fr_value_box_t  *vb;
+       fr_value_box_t  *in = fr_dlist_head(args);
+
+       /*
+        *      Query the rcode if there's no argument.  Otherwise do a boolean check if the passed string
+        *      matches the current rcode.
+        */
+       if (!in) {
+               MEM(vb = fr_value_box_alloc(ctx, FR_TYPE_UINT32, attr_module_return_code, false));
+               vb->datum.int32 = request->rcode;
+       } else {
+               rlm_rcode_t rcode;
+
+               rcode = fr_table_value_by_str(rcode_table, in->vb_strvalue, RLM_MODULE_NOT_SET);
+
+               MEM(vb = fr_value_box_alloc(ctx, FR_TYPE_BOOL, attr_expr_bool_enum, false));
+               vb->vb_bool = (request->rcode == rcode);
+       }
+
+       fr_dcursor_append(out, vb);
+
+       return XLAT_ACTION_DONE;
+}
+
 #undef XLAT_REGISTER_BINARY_OP
 #define XLAT_REGISTER_BINARY_OP(_op, _name) \
 do { \
@@ -1236,6 +1278,13 @@ do { \
        xlat->token = _op; \
 } while (0)
 
+#define XLAT_REGISTER_MONO(_xlat, _func, _arg) \
+do { \
+       if (!(xlat = xlat_register(NULL, _xlat, _func, NULL))) return -1; \
+       xlat_func_mono(xlat, &_arg); \
+       xlat_internal(xlat); \
+} while (0)
+
 int xlat_register_expressions(void)
 {
        xlat_t *xlat;
@@ -1282,6 +1331,8 @@ int xlat_register_expressions(void)
        xlat_print_set(xlat, xlat_expr_print_unary);
        xlat->token = T_NOT;
 
+       XLAT_REGISTER_MONO("rcode", xlat_func_rcode, xlat_func_rcode_arg);
+
        return 0;
 }
 
@@ -1316,12 +1367,22 @@ static const fr_sbuff_term_elem_t binary_ops[T_TOKEN_LAST] = {
 /*
  *     These operations are N-ary.  i.e. we can concatenate all of
  *     their arguments together.
+ *
+ *     @todo - add T_ADD
  */
 static const bool nary_ops[T_TOKEN_LAST] = {
        [T_LAND] = true,
        [T_LOR] = true,
 };
 
+/*
+ *     Which are logical operations
+ */
+static const bool logical_ops[T_TOKEN_LAST] = {
+       [T_LAND] = true,
+       [T_LOR] = true,
+};
+
 /*
  *     Allow for BEDMAS ordering.  Gross ordering is first number,
  *     fine ordering is second number.  Unused operators are assigned as zero.
@@ -1819,6 +1880,69 @@ static bool valid_type(xlat_exp_t *node)
        return true;
 }
 
+static int reparse_rcode(TALLOC_CTX *ctx, xlat_exp_t **p_arg)
+{
+       rlm_rcode_t rcode;
+       ssize_t slen;
+       size_t len;
+       xlat_t *func;
+       xlat_exp_t *arg = *p_arg;
+       xlat_exp_t *node;
+
+       if ((arg->type != XLAT_TMPL) || (arg->quote != T_BARE_WORD)) return 0;
+
+       if (!tmpl_is_unresolved(arg->vpt)) return 0;
+
+       len = talloc_array_length(arg->vpt->name) - 1;
+
+       /*
+        *      Check for module return codes.  If we find one,
+        *      replace it with a function call that returns "true" if
+        *      the current module code matches what we supplied here.
+        */
+       fr_sbuff_out_by_longest_prefix(&slen, &rcode, rcode_table,
+                                      &FR_SBUFF_IN(arg->vpt->name, len), T_BARE_WORD);
+       if (slen < 0) return 0;
+
+       /*
+        *      It did match, so it must match exactly.
+        *
+        *      @todo - what about (ENUM == &Attr), where the ENUM starts with "ok"?
+        *      Maybe just fix that later. Or, if it's a typo such as
+        */
+       if (((size_t) slen) != len) {
+               fr_strerror_const("Unexpected text - attribute names must prefixed with '&'");
+               return -1;
+       }
+
+       func = xlat_func_find("rcode", 5);
+       fr_assert(func != NULL);
+
+       /*
+        *      @todo - free the arg, and replace it with XLAT_BOX of uint32.  Then also update func_rcode()
+        *      to take UINT32 or string...
+        */
+       if (tmpl_cast_in_place(arg->vpt, FR_TYPE_STRING, NULL) < 0) {
+               return -1;
+       }
+
+       MEM(node = xlat_exp_alloc(ctx, XLAT_FUNC, arg->vpt->name, len));
+       MEM(node->call.args = xlat_exp_head_alloc(node));
+       node->call.func = func;
+       node->flags = func->flags;
+
+       /*
+        *      Doesn't need resolving, isn't pure, doesn't need anything else.
+        */
+       arg->flags = (xlat_flags_t) { };
+
+       xlat_func_append_arg(node, arg);
+
+       *p_arg = node;
+
+       return 0;
+}
+
 /** Tokenize a mathematical operation.
  *
  *     (EXPR)
@@ -1940,7 +2064,8 @@ redo:
         *      We now parse the RHS, allowing a (perhaps different) cast on the RHS.
         */
        XLAT_DEBUG("    recurse RHS <-- %pV", fr_box_strvalue_len(fr_sbuff_current(&our_in), fr_sbuff_remaining(&our_in)));
-       slen = tokenize_expression(head, &rhs, &our_in, p_rules, t_rules, op, bracket_rules, ((op == T_OP_REG_EQ) || (op == T_OP_REG_NE)));
+       slen = tokenize_expression(head, &rhs, &our_in, p_rules, t_rules, op, bracket_rules,
+                                  ((op == T_OP_REG_EQ) || (op == T_OP_REG_NE)));
        if (slen <= 0) {
                talloc_free(lhs);
                FR_SBUFF_ERROR_RETURN_ADJ(&our_in, slen);
@@ -1963,6 +2088,14 @@ redo:
         *      "lhs" children.
         */
        if (nary_ops[op] && (lhs->type == XLAT_FUNC) && (lhs->call.func->token == op)) {
+               /*
+                *      Reparse module return codes if necessary.
+                */
+               if (logical_ops[op] && (reparse_rcode(head, &rhs) < 0)) {
+                       fr_sbuff_set(&our_in, &m_rhs);
+                       return -fr_sbuff_used(&our_in);
+               }
+
                xlat_func_append_arg(lhs, rhs);
 
                lhs->call.args->flags.can_purify |= rhs->flags.can_purify | rhs->flags.pure;
@@ -1985,6 +2118,18 @@ redo:
                }
        }
 
+       if (logical_ops[op]) {
+               if (reparse_rcode(head, &lhs) < 0) {
+                       fr_sbuff_set(&our_in, &m_lhs);
+                       return -fr_sbuff_used(&our_in);
+               }
+
+               if (reparse_rcode(head, &rhs) < 0) {
+                       fr_sbuff_set(&our_in, &m_rhs);
+                       return -fr_sbuff_used(&our_in);
+               }
+       }
+
        /*
         *      Create the function node, with the LHS / RHS arguments.
         */
@@ -2002,7 +2147,7 @@ redo:
        /*
         *      Logical operations can be purified if ANY of their arguments can be purified.
         */
-       if ((op == T_LAND) || (op == T_LOR)) {
+       if (logical_ops[op]) {
                xlat_exp_foreach(node->call.args, arg) {
                        node->call.args->flags.can_purify |= arg->flags.can_purify | arg->flags.pure;
                        if (node->call.args->flags.can_purify) break;
@@ -2050,6 +2195,7 @@ ssize_t xlat_tokenize_expression(TALLOC_CTX *ctx, xlat_exp_head_t **out, fr_sbuf
        fr_sbuff_parse_rules_t *terminal_rules = NULL;
        tmpl_rules_t my_rules = { };
        xlat_exp_head_t *head;
+       xlat_exp_t *node;
 
        /*
         *      Whatever the caller passes, ensure that we have a
@@ -2083,7 +2229,7 @@ ssize_t xlat_tokenize_expression(TALLOC_CTX *ctx, xlat_exp_head_t **out, fr_sbuf
                t_rules = &my_rules;
        }
 
-       slen = tokenize_expression(head, NULL, in, terminal_rules, t_rules, T_INVALID, bracket_rules, false);
+       slen = tokenize_expression(head, &node, in, terminal_rules, t_rules, T_INVALID, bracket_rules, false);
        talloc_free(bracket_rules);
        talloc_free(terminal_rules);
 
@@ -2092,11 +2238,26 @@ ssize_t xlat_tokenize_expression(TALLOC_CTX *ctx, xlat_exp_head_t **out, fr_sbuf
                return slen;
        }
 
+       if (!node) {
+               *out = head;
+               return slen;
+       }
+
+       /*
+        *      Convert raw rcodes to xlat's.
+        */
+       if (reparse_rcode(head, &node) < 0) {
+               talloc_free(head);
+               return 0;
+       }
+
+       xlat_exp_insert_tail(head, node);
+
        /*
         *      Add nodes that need to be bootstrapped to
         *      the registry.
         */
-       if (xlat_exp_head(head) && (xlat_bootstrap(head) < 0)) {
+       if (xlat_bootstrap(head) < 0) {
                talloc_free(head);
                return 0;
        }
@@ -2131,6 +2292,7 @@ ssize_t xlat_tokenize_ephemeral_expression(TALLOC_CTX *ctx, xlat_exp_head_t **ou
        fr_sbuff_parse_rules_t *terminal_rules = NULL;
        tmpl_rules_t my_rules = { };
        xlat_exp_head_t *head;
+       xlat_exp_t *node;
 
        /*
         *      Whatever the caller passes, ensure that we have a
@@ -2167,7 +2329,7 @@ ssize_t xlat_tokenize_ephemeral_expression(TALLOC_CTX *ctx, xlat_exp_head_t **ou
        my_rules.xlat.runtime_el = el;
        my_rules.at_runtime = true;
 
-       slen = tokenize_expression(head, NULL, in, terminal_rules, &my_rules, T_INVALID, bracket_rules, false);
+       slen = tokenize_expression(head, &node, in, terminal_rules, &my_rules, T_INVALID, bracket_rules, false);
        talloc_free(bracket_rules);
        talloc_free(terminal_rules);
 
@@ -2176,11 +2338,21 @@ ssize_t xlat_tokenize_ephemeral_expression(TALLOC_CTX *ctx, xlat_exp_head_t **ou
                return slen;
        }
 
-       if (!xlat_exp_head(head)) {
+       if (!node) {
                *out = head;
                return slen;
        }
 
+       /*
+        *      Convert raw rcodes to xlat's.
+        */
+       if (reparse_rcode(head, &node) < 0) {
+               talloc_free(head);
+               return 0;
+       }
+
+       xlat_exp_insert_tail(head, node);
+
        /*
         *      Create ephemeral instance data for the xlat
         */
index e35c8ceceeb9bf74073e5e12e53a9d9c033c6b3c..51c6fd7009853e9956751bc5818293ece3859bf9 100644 (file)
@@ -318,6 +318,7 @@ xlat_t      *xlat_func_find(char const *name, ssize_t namelen);
  *     xlat_eval.c
  */
 extern fr_dict_attr_t const *attr_expr_bool_enum;
+extern fr_dict_attr_t const *attr_module_return_code;
 
 void           xlat_signal(xlat_func_signal_t signal, xlat_exp_t const *exp,
                            request_t *request, void *rctx, fr_state_signal_t action);
index d4fabb2ce62a01267289fc308466ada60e53b387..637f1786e74ea17296c30e2b6889e0c2f16396af 100644 (file)
@@ -110,6 +110,9 @@ match Passed in 67 characters, but only parsed 66 characters
 xlat_purify ('handled' && (&Packet-Type == Access-Challenge))
 match  ('handled' && (&Packet-Type == Access-Challenge))
 
+xlat_purify (handled && (&Packet-Type == Access-Challenge))
+match (%{rcode:'handled'} && (&Packet-Type == Access-Challenge))
+
 # This is OK, without the braces
 xlat_purify 'handled' && &Packet-Type == Access-Challenge
 match ('handled' && (&Packet-Type == Access-Challenge))
@@ -709,5 +712,21 @@ match ((&User-Name == "bob") && (&User-Password == "hello"))
 xlat_purify (&User-Name == "bob") && ((&User-Password == "bob") || &EAP-Message)
 match ((&User-Name == "bob") && ((&User-Password == "bob") || &EAP-Message))
 
+#
+#  rcode tests
+#
+xlat_purify handled && (&User-Name == "bob")
+match (%{rcode:'handled'} && (&User-Name == "bob"))
+
+xlat_purify (&User-Name == "bob") && (&User-Password == "bob") && handled
+match ((&User-Name == "bob") && (&User-Password == "bob") && %{rcode:'handled'})
+
+xlat_purify handledx
+match ERROR offset 0: Unexpected text - attribute names must prefixed with '&'
+
+xlat_purify handled
+match %{rcode:'handled'}
+
+
 count
-match 290
+match 300
index 150c68957b67c55d6923691d0d73dcbd150d538a..f9fcaa8d57e9cbf9e16ec01199e4f4841981510a 100644 (file)
@@ -104,18 +104,9 @@ match ((4 + 3) + 6)
 xlat_expr 1 < (uint32) 2
 match (1 < 2)
 
-#
-#  @todo - for exec, xlat, etc., if we're doing an existence check of
-#  string / octets, then the check is for "length>0", NOT for parsing
-#  the contents of the data type.
-#
-
 xlat_expr 1 < 2 < 3
 match ((1 < 2) < 3)
 
-#
-#  @todo - tmpl print doesn't print casts!
-#
 xlat_expr (uint32) %(concat:1 2)
 match %(concat:1 2)