]> git.ipfire.org Git - thirdparty/freeradius-server.git/commitdiff
fix map resolution
authorAlan T. DeKok <aland@freeradius.org>
Wed, 26 Mar 2025 11:39:09 +0000 (07:39 -0400)
committerAlan T. DeKok <aland@freeradius.org>
Wed, 26 Mar 2025 11:39:09 +0000 (07:39 -0400)
the RHS of a map is NOT resolved in the context of the LHS.
Instead, the RHS is really a pointer to a value.  And the final
value is then resolved in the context of the LHS.

src/lib/server/map.c
src/lib/server/tmpl.h
src/lib/unlang/compile.c

index b96c2c964e7f8fc716e887f906853ed10da6f364..57f6dbd9543cc48eadc2e96d2f1cf671b20fc685 100644 (file)
@@ -319,13 +319,25 @@ int map_afrom_cp(TALLOC_CTX *ctx, map_t **out, map_t *parent, CONF_PAIR *cp,
        }
 
        /*
-        *      If we know that the assignment is forbidden, then fail early.
+        *      If we can resolve the RHS in the context of the LHS,
+        *      and are allowed to do so, then do that now.
         *
-        *      Note that we can do some assignment of strings to
-        *      structural elements.  The string is parsed as a list
-        *      of edit instructions.
+        *      The "map" keyword uses the RHS not as the value which
+        *      is assigned to the LHS.  Instead, it is a pointer to
+        *      the value.  As such, we cannot immediately resolve the
+        *      RHS in the context of the LHS.
+        *
+        *      The edit code allows string assignments to lists, and
+        *      will interpret the string as a sequence of edit
+        *      operations.  So we skip casting strings to lists.
+        *
+        *      @todo - that could arguably be done immediately here,
+        *      and the RHS could be parsed and created as a child
+        *      map.  That would allow for more compile-time sanity
+        *      checks.
         */
        if (tmpl_is_attr(map->lhs) && tmpl_is_data(map->rhs) &&
+           (!input_rhs_rules || !input_rhs_rules->attr.disallow_rhs_resolve) &&
            fr_type_is_leaf(tmpl_attr_tail_da(map->lhs)->type)) {
                fr_type_t cast_type;
 
index fb9473a9ba90dce0fddf66dc1d09d86f4c4aa8dd..c6f34a2a56d1129ef718cf201b2700d99393220c 100644 (file)
@@ -333,6 +333,8 @@ struct tmpl_attr_rules_s {
        uint8_t                 xlat:1  ;               //!< for %{User-Name}
 
        uint8_t                 bare_word_enum:1;       //!< for v3 compatibility.
+
+       uint8_t                 disallow_rhs_resolve:1; //!< map RHS is NOT immediately resolved in the context of the LHS.
 };
 
 struct tmpl_xlat_rules_s {
index 596368f52ee28dafe21b8c4deebabacfa37d6087..a24d432bd4786627af61277b9dc0cca710fa7308 100644 (file)
@@ -744,10 +744,10 @@ static unlang_t *compile_map(unlang_t *parent, unlang_compile_t *unlang_ctx, CON
        };
 
        /*
-        *      We allow unknown attributes here.
+        *      The RHS is NOT resolved in the context of the LHS.
         */
        t_rules = *(unlang_ctx->rules);
-       t_rules.attr.allow_unknown = true;
+       t_rules.attr.disallow_rhs_resolve = true;
        RULES_VERIFY(&t_rules);
 
        modules = cf_section_find(cf_root(cs), "modules", NULL);
@@ -819,7 +819,7 @@ static unlang_t *compile_map(unlang_t *parent, unlang_compile_t *unlang_ctx, CON
         *      This looks at cs->name2 to determine which list to update
         */
        map_list_init(&gext->map);
-       rcode = map_afrom_cs(gext, &gext->map, cs, &t_rules, &t_rules, unlang_fixup_map, NULL, 256);
+       rcode = map_afrom_cs(gext, &gext->map, cs, unlang_ctx->rules, &t_rules, unlang_fixup_map, NULL, 256);
        if (rcode < 0) return NULL; /* message already printed */
        if (map_list_empty(&gext->map)) {
                cf_log_err(cs, "'map' sections cannot be empty");