]> git.ipfire.org Git - thirdparty/freeradius-server.git/commitdiff
add and export v3_compat flag for the "files" module
authorAlan T. DeKok <aland@freeradius.org>
Tue, 28 Jan 2025 02:22:45 +0000 (21:22 -0500)
committerAlan T. DeKok <aland@freeradius.org>
Tue, 28 Jan 2025 02:25:13 +0000 (21:25 -0500)
raddb/mods-available/files
src/lib/server/map.c
src/lib/server/map.h
src/lib/server/tmpl.h
src/lib/server/users_file.c
src/lib/server/users_file.h
src/modules/rlm_attr_filter/rlm_attr_filter.c
src/modules/rlm_files/rlm_files.c
src/tests/modules/files/authorize

index 55ba8336fcf8e1c7b6db8aaa07575dccc9d3cf61..d17fe40a101ebdb0248d834b67447551b7a255ca 100644 (file)
@@ -31,7 +31,7 @@ files {
        #  Note that unlike v4, the key does not have to be a string, but could instead
        #  be an IP address or netmask!  For more information, see
        #
-       #       doc/antora/modules/raddb/pages/mods-config/files/users.adoc 
+       #       doc/antora/modules/raddb/pages/mods-config/files/users.adoc
        #
 #      key = "%{&Stripped-User-Name || &User-Name}"
 
@@ -46,10 +46,21 @@ files {
        #  Note: the attriubte type should be capable of holding data of the type
        #  used as key values.
        #  Particularly useful if matching IP addresses to subnets, since the populated
-       #  value will be the subnet.  In that case it is best to use 0.0.0.0/0 in place
-       #  of DEFAULT for any catch-all entries.
+       #  value will be the subnet.  In that case it is best to use `0.0.0.0/0` in place
+       #  of `DEFAULT` for any catch-all entries.
        #
 #      match_attr = &control.User-Category
+
+       #
+       #  v3_compat:: Version 3 compatibility flag.
+       #
+       #  When this flag is set, any enumeration names (e.g. Service-Type := Framed-User)
+       #  do not need to have the v4 "::" prefix.  This flag helps with migrating v3
+       #  configurations to v4.
+       #
+       #  Default value "false".  Allowerd vlaues, "true' and "false".
+       #
+#      v3_compat = false
 }
 
 #
index 8066ebad5ba74e1c4c45859100ffba9918d11617..53b685566dabbebd45df21731dc7f4b03090024f 100644 (file)
@@ -462,6 +462,7 @@ ssize_t map_afrom_substr(TALLOC_CTX *ctx, map_t **out, map_t **parent_p, fr_sbuf
        fr_sbuff_marker_t               m_lhs, m_rhs, m_op;
        fr_sbuff_term_t const           *tt = p_rules ? p_rules->terminals : NULL;
        map_t                           *parent;
+       tmpl_rules_t                    our_rhs_rules;
 
        if (parent_p) {
                parent = *parent_p;
@@ -485,11 +486,7 @@ ssize_t map_afrom_substr(TALLOC_CTX *ctx, map_t **out, map_t **parent_p, fr_sbuf
        {
                tmpl_rules_t our_lhs_rules;
 
-               if (lhs_rules) {
-                       our_lhs_rules = *lhs_rules;
-               } else {
-                       memset(&our_lhs_rules, 0, sizeof(our_lhs_rules));
-               }
+               our_lhs_rules = *lhs_rules;
 
                /*
                 *      Allow for ".foo" to refer to the current
@@ -658,6 +655,20 @@ ssize_t map_afrom_substr(TALLOC_CTX *ctx, map_t **out, map_t **parent_p, fr_sbuf
        }
 
 parse_rhs:
+       if (tmpl_is_attr(map->lhs)) {
+               fr_dict_attr_t const *enumv = tmpl_attr_tail_da(map->lhs);
+
+               /*
+                *      LHS is a structural type.  The RHS is either empty (create empty LHS), or it's a string
+                *      containing a list of attributes to create.
+                */
+               if (fr_type_is_leaf(enumv->type)) {
+                       our_rhs_rules = *rhs_rules;
+                       our_rhs_rules.enumv = enumv;
+                       rhs_rules = &our_rhs_rules;
+               }
+       }
+
        fr_sbuff_out_by_longest_prefix(&slen, &token, cond_quote_table, &our_in, T_BARE_WORD);
        switch (token) {
        case T_SOLIDUS_QUOTED_STRING:
@@ -680,25 +691,27 @@ parse_rhs:
 
                        (void) tmpl_afrom_value_box(map, &map->rhs, fr_box_strvalue("ANY"), false);
 
-               } else {
-                       tmpl_rules_t my_rhs_rules;
+               } else if (rhs_rules->attr.bare_word_enum && rhs_rules->enumv) {
+                       fr_value_box_t *vb;
+
+                       MEM(vb = fr_value_box_alloc(map, rhs_rules->enumv->type, rhs_rules->enumv));
 
                        if (!p_rules) p_rules = &value_parse_rules_bareword_quoted;
 
                        /*
-                        *      If we parsed an attribute on the LHS, and the RHS looks like an enumerated
-                        *      value, then set the enumv.
-                        *
-                        *      @todo tmpl_require_enum_prefix - maybe just _always_ set enumv, because the
-                        *      caller shouldn't have set it?
+                        *      It MUST be the given data type, and it MAY be an enum name.
                         */
-                       if (rhs_rules && !rhs_rules->enumv && tmpl_is_attr(map->lhs) &&
-                           fr_sbuff_is_str_literal(&our_in, "::")) {
-                               my_rhs_rules = *rhs_rules;
-                               my_rhs_rules.enumv = tmpl_attr_tail_da(map->lhs);
-                               rhs_rules = &my_rhs_rules;
+                       slen = fr_value_box_from_substr(map, vb, rhs_rules->enumv->type, rhs_rules->enumv,
+                                                       &our_in, p_rules, false);
+                       if (slen < 0) goto error;
+
+                       if (tmpl_afrom_value_box(map, &map->rhs, vb, true) < 0) {
+                               goto error;
                        }
 
+               } else {
+                       if (!p_rules) p_rules = &value_parse_rules_bareword_quoted;
+
                        /*
                         *      Use the RHS termination rules ONLY for bare
                         *      words.  For quoted strings we already know how
index e18f9c633cc77dd223f69440705a5ae34786863e..8b8ec96faafb79d13698b6a4e75be3e527f2c5b2 100644 (file)
@@ -147,7 +147,7 @@ int         map_afrom_vp(TALLOC_CTX *ctx, map_t **out, fr_pair_t *vp,
 ssize_t                map_afrom_substr(TALLOC_CTX *ctx, map_t **out, map_t **parent_p, fr_sbuff_t *in,
                                fr_table_num_sorted_t const *op_table, size_t op_table_len,
                                tmpl_rules_t const *lhs_rules, tmpl_rules_t const *rhs_rules,
-                               fr_sbuff_parse_rules_t const *p_rules);
+                               fr_sbuff_parse_rules_t const *p_rules) CC_HINT(nonnull(1,2,4,5,7,8));
 
 int            map_afrom_fields(TALLOC_CTX *ctx, map_t **out, map_t **parent_p, request_t *request,
                                 char const *lhs, char const *op, char const *rhs,
index 4a8de91bf3407a951703a0c0f52459abd12e7825..91d10ca79bfb14b9d39324d155e41e3094fac428 100644 (file)
@@ -329,6 +329,8 @@ struct tmpl_attr_rules_s {
        uint8_t                 disallow_filters:1;     //!< disallow filters.
 
        uint8_t                 xlat:1  ;               //!< for %{User-Name}
+
+       uint8_t                 bare_word_enum:1;       //!< for v3 compatibility.
 };
 
 struct tmpl_xlat_rules_s {
index 514e1194c6fa3d69f5715d0b6b8ca502392be767..a769b8579b76bfc7a97b09749ddfd86978cbf1aa 100644 (file)
@@ -38,7 +38,7 @@ RCSID("$Id$")
 #include <fcntl.h>
 
 static int pairlist_read_internal(TALLOC_CTX *ctx, fr_dict_t const *dict, char const *file, PAIR_LIST_LIST *list,
-                                 bool complain, int *order);
+                                 bool complain, bool v3_compat, int *order);
 
 static inline void line_error_marker(char const *src_file, int src_line,
                                     char const *user_file, int user_line,
@@ -141,7 +141,7 @@ static fr_sbuff_parse_rules_t const rhs_term = {
  *     Caller saw a $INCLUDE at the start of a line.
  */
 static int users_include(TALLOC_CTX *ctx, fr_dict_t const *dict, fr_sbuff_t *sbuff, PAIR_LIST_LIST *list,
-                        char const *file, int lineno, int *order)
+                        char const *file, int lineno, bool v3_compat, int *order)
 {
        size_t          len;
        char            *newfile, *p, c;
@@ -221,7 +221,7 @@ static int users_include(TALLOC_CTX *ctx, fr_dict_t const *dict, fr_sbuff_t *sbu
        /*
         *      Read the $INCLUDEd file recursively.
         */
-       if (pairlist_read_internal(ctx, dict, newfile, list, false, order) != 0) {
+       if (pairlist_read_internal(ctx, dict, newfile, list, false, v3_compat, order) != 0) {
                ERROR("%s[%d]: Could not read included file %s: %s",
                      file, lineno, newfile, fr_syserror(errno));
                talloc_free(newfile);
@@ -232,17 +232,17 @@ static int users_include(TALLOC_CTX *ctx, fr_dict_t const *dict, fr_sbuff_t *sbu
        return 0;
 }
 
-int pairlist_read(TALLOC_CTX *ctx, fr_dict_t const *dict, char const *file, PAIR_LIST_LIST *list)
+int pairlist_read(TALLOC_CTX *ctx, fr_dict_t const *dict, char const *file, PAIR_LIST_LIST *list, bool v3_compat)
 {
        int order = 0;
 
-       return pairlist_read_internal(ctx, dict, file, list, true, &order);
+       return pairlist_read_internal(ctx, dict, file, list, true, v3_compat, &order);
 }
 
 /*
  *     Read the users file. Return a PAIR_LIST.
  */
-static int pairlist_read_internal(TALLOC_CTX *ctx, fr_dict_t const *dict, char const *file, PAIR_LIST_LIST *list, bool complain, int *order)
+static int pairlist_read_internal(TALLOC_CTX *ctx, fr_dict_t const *dict, char const *file, PAIR_LIST_LIST *list, bool complain, bool v3_compat, int *order)
 {
        char                    *q;
        int                     lineno          = 1;
@@ -281,6 +281,7 @@ static int pairlist_read_internal(TALLOC_CTX *ctx, fr_dict_t const *dict, char c
                        .prefix = TMPL_ATTR_REF_PREFIX_YES,
                        .list_def = request_attr_request,
                        .list_presence = TMPL_ATTR_LIST_ALLOW,
+                       .bare_word_enum = v3_compat,
                }
        };
 
@@ -321,7 +322,7 @@ static int pairlist_read_internal(TALLOC_CTX *ctx, fr_dict_t const *dict, char c
                 *      the tail of the current list.
                 */
                if (fr_sbuff_is_str(&sbuff, "$INCLUDE", 8)) {
-                       if (users_include(ctx, dict, &sbuff, list, file, lineno, order) < 0) goto fail;
+                       if (users_include(ctx, dict, &sbuff, list, file, lineno, v3_compat, order) < 0) goto fail;
 
                        if (fr_sbuff_next_if_char(&sbuff, '\n')) {
                                lineno++;
index 84ce2a2b1de437b46c6dc4366c5f97bf91bed676..97ef9c4a8141adb2424fa6f3d0f1155553d3092e 100644 (file)
@@ -53,8 +53,7 @@ typedef struct pair_list_list {
        fr_value_box_t          *box;           //!< parsed version of "name".
 } PAIR_LIST_LIST;
 
-/* users_file.c */
-int            pairlist_read(TALLOC_CTX *ctx, fr_dict_t const *dict, char const *file, PAIR_LIST_LIST *list);
+int            pairlist_read(TALLOC_CTX *ctx, fr_dict_t const *dict, char const *file, PAIR_LIST_LIST *list, bool v3_compat);
 
 static inline void pairlist_list_init(PAIR_LIST_LIST *list)
 {
index 72413589333cbc5bcdf8576bd87256d798aee1cd..11cdd0d105395985af053cb9caed71e2331ca87d 100644 (file)
@@ -106,7 +106,7 @@ static int attr_filter_getfile(TALLOC_CTX *ctx, module_inst_ctx_t const *mctx, c
        PAIR_LIST *entry = NULL;
        map_t *map;
 
-       rcode = pairlist_read(ctx, dict_radius, filename, pair_list);
+       rcode = pairlist_read(ctx, dict_radius, filename, pair_list, false);
        if (rcode < 0) {
                return -1;
        }
index f8e96ec7f41a8c40c96119b5d61e6b1c9a7f186d..6e7db7083f93277371155493b53cd8194761d6a2 100644 (file)
@@ -38,6 +38,7 @@ RCSID("$Id$")
 
 typedef struct {
        char const      *filename;
+       bool            v3_compat;
 } rlm_files_t;
 
 /**  Structure produced by custom call_env parser
@@ -78,6 +79,7 @@ fr_dict_attr_autoload_t rlm_files_dict_attr[] = {
 
 static const conf_parser_t module_config[] = {
        { FR_CONF_OFFSET_FLAGS("filename", CONF_FLAG_REQUIRED | CONF_FLAG_FILE_INPUT, rlm_files_t, filename) },
+       { FR_CONF_OFFSET("v3_compat", rlm_files_t, v3_compat) },
        CONF_PARSER_TERMINATOR
 };
 
@@ -101,7 +103,7 @@ static int pairlist_to_key(uint8_t **out, size_t *outlen, void const *a)
 }
 
 static int getrecv_filename(TALLOC_CTX *ctx, char const *filename, fr_htrie_t **ptree, PAIR_LIST_LIST **pdefault,
-                           fr_type_t data_type, fr_dict_attr_t const *key_enum, fr_dict_t const *dict)
+                           fr_type_t data_type, fr_dict_attr_t const *key_enum, fr_dict_t const *dict, bool v3_compat)
 {
        int                     rcode;
        PAIR_LIST_LIST          users;
@@ -119,7 +121,7 @@ static int getrecv_filename(TALLOC_CTX *ctx, char const *filename, fr_htrie_t **
        }
 
        pairlist_list_init(&users);
-       rcode = pairlist_read(ctx, dict, filename, &users);
+       rcode = pairlist_read(ctx, dict, filename, &users, v3_compat);
        if (rcode < 0) {
                return -1;
        }
@@ -678,7 +680,7 @@ static int call_env_parse(TALLOC_CTX *ctx, void *out, tmpl_rules_t const *t_rule
        }
 
        if (getrecv_filename(files_data, inst->filename, &files_data->htrie, &files_data->def,
-                            keytype, key_enum, t_rules->attr.dict_def) < 0) goto error;
+                            keytype, key_enum, t_rules->attr.dict_def, inst->v3_compat) < 0) goto error;
 
        *(void **)out = files_data;
        return 0;
index 86bcf048b6f075806c6892c5c14aa0cda5fc3329..b3227e51b10c1f4c9ea5473b38fd95e3ef4ddbe1 100644 (file)
@@ -152,8 +152,11 @@ undo       Password.Cleartext := "hello"
 #  This should fail, which means that the previous Reply-Message
 #  gets deleted as part of the "undo" operation.
 #
+#  Note that the contents of the RHS have to be expanded at run-time,
+#  otherwise it's a compile-time error.
+#
 undo
-       Framed-IP-Address := "this is not an IP address"
+       Framed-IP-Address := "hello %md5('foo')"
 
 #
 #  Test where additional checks uses the [*] filter