]> git.ipfire.org Git - thirdparty/freeradius-server.git/commitdiff
Add support for backticked expansions in legacy file formats, in certain situations
authorArran Cudbard-Bell <a.cudbardb@freeradius.org>
Thu, 1 Jan 2026 18:23:35 +0000 (18:23 +0000)
committerArran Cudbard-Bell <a.cudbardb@freeradius.org>
Thu, 1 Jan 2026 18:25:08 +0000 (18:25 +0000)
This lets us add dynamic values in certain situations.  Output from exec is limited to a single line (for now).

12 files changed:
src/bin/dhcpclient.c
src/bin/radclient-ng.c
src/bin/radclient.c
src/bin/radsniff.c
src/bin/unit_test_attribute.c
src/bin/unit_test_module.c
src/lib/util/pair_legacy.c
src/lib/util/pair_legacy.h
src/lib/util/pair_legacy_tests.c
src/listen/cron/cron.c
src/listen/cron/proto_cron_crontab.c
src/listen/load/proto_load_step.c

index 31f49372f8f2ed68e770692ddf85707dd87a3240..289b5bb5c69e1c9641b7b9fa4471180b151a51ec 100644 (file)
@@ -159,7 +159,7 @@ static int request_init(fr_packet_t **out, fr_pair_list_t *packet_vps, char cons
        /*
         *      Read the VP's.
         */
-       if (fr_pair_list_afrom_file(packet, dict_dhcpv4, packet_vps, fp, &filedone) < 0) {
+       if (fr_pair_list_afrom_file(packet, dict_dhcpv4, packet_vps, fp, &filedone, true) < 0) {
                fr_perror("dhcpclient");
                fr_packet_free(&packet);
                if (fp != stdin) fclose(fp);
index 645d89cc913d0766ff148247ad456de07c4b54f3..377499c0222ffddc11fb3bb0bc4852215bc4044a 100644 (file)
@@ -422,7 +422,7 @@ static int coa_init(rc_request_t *parent, FILE *coa_reply, char const *reply_fil
         *      Read the reply VP's.
         */
        if (fr_pair_list_afrom_file(request, dict_radius,
-                                   &request->reply_pairs, coa_reply, coa_reply_done) < 0) {
+                                   &request->reply_pairs, coa_reply, coa_reply_done, true) < 0) {
                REDEBUG("Error parsing \"%s\"", reply_filename);
        error:
                talloc_free(request);
@@ -440,7 +440,7 @@ static int coa_init(rc_request_t *parent, FILE *coa_reply, char const *reply_fil
         */
        if (coa_filter) {
                if (fr_pair_list_afrom_file(request, dict_radius,
-                                           &request->filter, coa_filter, coa_filter_done) < 0) {
+                                           &request->filter, coa_filter, coa_filter_done, true) < 0) {
                        REDEBUG("Error parsing \"%s\"", filter_filename);
                        goto error;
                }
@@ -577,7 +577,7 @@ static int radclient_init(TALLOC_CTX *ctx, rc_file_pair_t *files)
                 *      Read the request VP's.
                 */
                if (fr_pair_list_afrom_file(request, dict_radius,
-                                           &request->request_pairs, packets, &packets_done) < 0) {
+                                           &request->request_pairs, packets, &packets_done, true) < 0) {
                        char const *input;
 
                        if ((files->packets[0] == '-') && (files->packets[1] == '\0')) {
@@ -606,7 +606,7 @@ static int radclient_init(TALLOC_CTX *ctx, rc_file_pair_t *files)
                        bool filters_done;
 
                        if (fr_pair_list_afrom_file(request, dict_radius,
-                                                   &request->filter, filters, &filters_done) < 0) {
+                                                   &request->filter, filters, &filters_done, true) < 0) {
                                REDEBUG("Error parsing \"%s\"", files->filters);
                                goto error;
                        }
index 33c3f489a11a82de7dfe8af27cc1c189bc56f373..e68a38be0cb9a898af4e993f639784c01890db22 100644 (file)
@@ -406,7 +406,7 @@ static int coa_init(rc_request_t *parent, FILE *coa_reply, char const *reply_fil
         *      Read the reply VP's.
         */
        if (fr_pair_list_afrom_file(request, dict_radius,
-                                   &request->reply_pairs, coa_reply, coa_reply_done) < 0) {
+                                   &request->reply_pairs, coa_reply, coa_reply_done, true) < 0) {
                REDEBUG("Error parsing \"%s\"", reply_filename);
        error:
                talloc_free(request);
@@ -424,7 +424,7 @@ static int coa_init(rc_request_t *parent, FILE *coa_reply, char const *reply_fil
         */
        if (coa_filter) {
                if (fr_pair_list_afrom_file(request, dict_radius,
-                                           &request->filter, coa_filter, coa_filter_done) < 0) {
+                                           &request->filter, coa_filter, coa_filter_done, true) < 0) {
                        REDEBUG("Error parsing \"%s\"", filter_filename);
                        goto error;
                }
@@ -563,7 +563,7 @@ static int radclient_init(TALLOC_CTX *ctx, rc_file_pair_t *files)
                 *      Read the request VP's.
                 */
                if (fr_pair_list_afrom_file(request, dict_radius,
-                                           &request->request_pairs, packets, &packets_done) < 0) {
+                                           &request->request_pairs, packets, &packets_done, true) < 0) {
                        char const *input;
 
                        if ((files->packets[0] == '-') && (files->packets[1] == '\0')) {
@@ -592,7 +592,7 @@ static int radclient_init(TALLOC_CTX *ctx, rc_file_pair_t *files)
                        bool filters_done;
 
                        if (fr_pair_list_afrom_file(request, dict_radius,
-                                                   &request->filter, filters, &filters_done) < 0) {
+                                                   &request->filter, filters, &filters_done, true) < 0) {
                                REDEBUG("Error parsing \"%s\"", files->filters);
                                goto error;
                        }
index da9c465c279301f1b6c15dbbbcfd275e49957ad6..0b9616b78f0f45cdd9e6815a19d2f059ba162a90 100644 (file)
@@ -2108,6 +2108,7 @@ static int rs_build_filter(fr_pair_list_t *out, char const *filter)
                .dict = dict_radius,
                .internal = fr_dict_internal(),
                .allow_compare = true,
+               .allow_exec = true
        };
        relative = (fr_pair_parse_t) { };
 
index 912364efc29124fd889de2629ba65a073fbc1a7b..6ee306a9edd067d46a0b5fd81c788cd66dbfae9b 100644 (file)
@@ -1260,7 +1260,7 @@ static size_t command_allow_unresolved(command_result_t *result, command_file_ct
 static size_t command_attr_children(command_result_t *result, command_file_ctx_t *cc,
                                    UNUSED char *data, UNUSED size_t data_used, char *in, size_t inlen)
 {
-       fr_hash_table_t *namespace;     
+       fr_hash_table_t *namespace;
        fr_hash_iter_t  iter;
        fr_dict_attr_t const *ref;
        fr_sbuff_t out = FR_SBUFF_OUT(data, COMMAND_OUTPUT_MAX);
@@ -2141,6 +2141,7 @@ static size_t command_encode_pair(command_result_t *result, command_file_ctx_t *
                .list = &head,
                .dict = cc->tmpl_rules.attr.namespace->dict,
                .internal = fr_dict_internal(),
+               .allow_exec = true
        };
        relative = (fr_pair_parse_t) { };
 
@@ -2295,7 +2296,7 @@ static size_t command_read_file(command_result_t *result, command_file_ctx_t *cc
        }
 
        fr_pair_list_init(&head);
-       slen = fr_pair_list_afrom_file(cc->tmp_ctx, cc->tmpl_rules.attr.dict_def, &head, fp, &done);
+       slen = fr_pair_list_afrom_file(cc->tmp_ctx, cc->tmpl_rules.attr.dict_def, &head, fp, &done, true);
        fclose(fp);
        if (slen < 0) {
                RETURN_OK_WITH_ERROR();
@@ -2362,6 +2363,7 @@ static size_t command_encode_proto(command_result_t *result, command_file_ctx_t
                .list = &head,
                .dict = cc->tmpl_rules.attr.namespace->dict,
                .internal = fr_dict_internal(),
+               .allow_exec = true
        };
        relative = (fr_pair_parse_t) { };
 
@@ -2717,6 +2719,7 @@ static size_t command_pair_common(command_result_t *result, command_file_ctx_t *
                .dict = dict,
                .internal = fr_dict_internal(),
                .allow_compare = allow_compare,
+               .allow_exec = true
        };
        relative = (fr_pair_parse_t) { };
 
index 4852133281e41a1a8c942f61d1eeb539a6904cef..284ce63c7af132d8a8307b6a85ec1787a798380f 100644 (file)
@@ -225,7 +225,7 @@ static request_t *request_from_file(TALLOC_CTX *ctx, FILE *fp, fr_client_t *clie
        /*
         *      Read packet from fp
         */
-       if (fr_pair_list_afrom_file(request->request_ctx, dict_protocol, &request->request_pairs, fp, &filedone) < 0) {
+       if (fr_pair_list_afrom_file(request->request_ctx, dict_protocol, &request->request_pairs, fp, &filedone, true) < 0) {
                goto error;
        }
 
@@ -1126,7 +1126,7 @@ int main(int argc, char *argv[])
                        }
                }
 
-               if (fr_pair_list_afrom_file(request->request_ctx, dict_protocol, &filter_vps, fp, &filedone) < 0) {
+               if (fr_pair_list_afrom_file(request->request_ctx, dict_protocol, &filter_vps, fp, &filedone, true) < 0) {
                        fr_perror("Failed reading attributes from %s", filter_file);
                        EXIT_WITH_FAILURE;
                }
index a4f6db8b9fe66a0c5816aa821e8e5d8573bd18ed..5fa3304fc5c646160c4999f436741d295e835431 100644 (file)
  *
  * @copyright 2000,2006,2015 The FreeRADIUS server project
  */
-
 RCSID("$Id$")
 
+#include <sys/wait.h>
+
 #include <freeradius-devel/util/dict.h>
 #include <freeradius-devel/util/pair.h>
 #include <freeradius-devel/util/pair_legacy.h>
 #include <freeradius-devel/util/proto.h>
 #include <freeradius-devel/util/regex.h>
+#include <freeradius-devel/util/syserror.h>
+#include <freeradius-devel/util/sbuff.h>
+#include <freeradius-devel/util/value.h>
 
 #include <freeradius-devel/protocol/radius/rfc2865.h>
 #include <freeradius-devel/protocol/freeradius/freeradius.internal.h>
@@ -98,46 +102,149 @@ static fr_sbuff_parse_rules_t const bareword_unquoted = {
 };
 
 
-static ssize_t fr_pair_value_from_substr(fr_pair_t *vp, fr_sbuff_t *in, UNUSED bool tainted)
+static ssize_t fr_pair_value_from_substr(fr_pair_parse_t const *conf, fr_pair_t *vp, fr_sbuff_t *in)
 {
-       char quote;
-       ssize_t slen;
-       fr_sbuff_parse_rules_t const *rules;
+       fr_sbuff_t                      our_in = FR_SBUFF(in);
+       char                            quote;
+       ssize_t                         slen;
+       fr_sbuff_parse_rules_t const    *rules;
 
-       if (fr_sbuff_next_if_char(in, '"')) {
+       if (fr_sbuff_next_if_char(&our_in, '"')) {
                rules = &value_parse_rules_double_quoted;
                quote = '"';
-
-       } else if (fr_sbuff_next_if_char(in, '\'')) {
+       parse:
+               slen = fr_value_box_from_substr(vp, &vp->data, vp->da->type, vp->da, &our_in, rules);
+       } else if (fr_sbuff_next_if_char(&our_in, '\'')) {
                rules = &value_parse_rules_single_quoted;
                quote = '\'';
+               goto parse;
+       } else if (!fr_sbuff_next_if_char(&our_in, '`')) {
+               quote = '\0';
+               rules = &bareword_unquoted;
+               goto parse;
+       /*
+        *      We _sometimes_ support backticks, depending on the
+        *      source of the data.  This should ONLY be used on
+        *      trusted input, like config files.
+        *
+        *      We don't impose arbitrary limits on exec input or
+        *      output, as AGAIN this should only be used on trusted
+        *      input.
+        *
+        *      Only the first line of output from the process is used,
+        *      and no escape sequences in the output are processed.
+        */
+       } else {
+               fr_sbuff_t              *exec_in;
+               size_t                  exec_out_buff_len = 0;
+               ssize_t                 exec_out_len;
+               char                    *exec_out = NULL;
+               FILE                    *fp;
+               int                     ret;
+
+               if (!conf->allow_exec) {
+                       fr_strerror_const("Backticks are not supported here");
+                       return 0;
+               }
 
                /*
-                *      We don't support backticks here.
+                *      Should only be used for trusted resources, so no artificial limits
                 */
-       } else if (fr_sbuff_is_char(in, '\'')) {
-               fr_strerror_const("Backticks are not supported here");
-               return 0;
+               FR_SBUFF_TALLOC_THREAD_LOCAL(&exec_in, 1024, SIZE_MAX);
+               (void)fr_sbuff_out_unescape_until(exec_in, &our_in, SIZE_MAX, &FR_SBUFF_TERMS(L("`")), &fr_value_unescape_backtick);
+               /*
+                *      Don't exec if we know we're going to fail
+                */
+               if (!fr_sbuff_is_char(&our_in, '`')) {
+                       fr_strerror_const("Unterminated backtick string");
+                       return 0;
+               }
 
-       } else {
-               rules = &bareword_unquoted;
-               quote = '\0';
+               fp = popen(fr_sbuff_start(exec_in), "r");
+               if (!fp) {
+                       fr_strerror_printf("Cannot execute command `%pV`: %s",
+                                          fr_box_strvalue_len(fr_sbuff_start(exec_in), fr_sbuff_used(exec_in)),
+                                          fr_syserror(errno));
+                       return 0;
+               }
+
+               errno = 0; /* If we get EOF immediately, we don't want to emit spurious errors */
+               exec_out_len = getline(&exec_out, &exec_out_buff_len, fp);
+               if ((exec_out_len < 0) || (exec_out == NULL)) { /* defensive */
+                       fr_strerror_printf("Cannot read output from command `%pV`: %s",
+                                          fr_box_strvalue_len(fr_sbuff_start(exec_in), fr_sbuff_used(exec_in)),
+                                          fr_syserror(errno));
+                       pclose(fp);
+                       return 0;
+               }
+
+               /*
+                *      Protect against child writing too much data to stdout,
+                *      blocking, and never exiting.
+                *
+                *      This is likely overly cautious for this particular use
+                *      case, but it doesn't hurt.
+                */
+               {
+                       char buffer[128];
+
+                       while (fread(buffer, 1, sizeof(buffer), fp) > 0) { /* discard */ }
+               }
+
+               errno = 0;      /* ensure we don't have stale errno */
+               ret = pclose(fp);
+               if (ret < 0) {
+                       fr_strerror_printf("Error waiting for command `%pV` to finish: %s",
+                                          fr_box_strvalue_len(fr_sbuff_start(exec_in), fr_sbuff_used(exec_in)),
+                                          fr_syserror(errno));
+               pclose_error:
+                       free(exec_out);
+                       return 0;
+               } else if (ret != 0) {
+                       if (WIFEXITED(ret)) {
+                               fr_strerror_printf("Command `%pV` exited with status %d",
+                                                 fr_box_strvalue_len(fr_sbuff_start(exec_in), fr_sbuff_used(exec_in)),
+                                                 WEXITSTATUS(ret));
+                       } else if (WIFSIGNALED(ret)) {
+                               fr_strerror_printf("Command `%pV` terminated by signal %d",
+                                                  fr_box_strvalue_len(fr_sbuff_start(exec_in), fr_sbuff_used(exec_in)),
+                                                  WTERMSIG(ret));
+                       } else {
+                               fr_strerror_printf("Command `%pV` terminated abnormally",
+                                                  fr_box_strvalue_len(fr_sbuff_start(exec_in), fr_sbuff_used(exec_in)));
+                       }
+                       goto pclose_error;
+               }
+
+               /*
+                *      Trim line endings
+                */
+               if (exec_out_len > 0 && exec_out[exec_out_len - 1] == '\n') exec_out[--exec_out_len] = '\0';
+               if (exec_out_len > 0 && exec_out[exec_out_len - 1] == '\r') exec_out[--exec_out_len] = '\0';
+
+               slen = fr_value_box_from_substr(vp, &vp->data, vp->da->type, vp->da,
+                                               &FR_SBUFF_IN(exec_out, exec_out_len), &value_parse_rules_single_quoted);
+               free(exec_out);
+               if (unlikely(slen < 0)) {
+                       return 0; /* slen is parse position in the exec output*/
+               }
+
+               quote = '`';
        }
 
-       slen = fr_value_box_from_substr(vp, &vp->data, vp->da->type, vp->da, in, rules);
        if (slen < 0) {
                fr_assert(slen >= -((ssize_t) 1 << 20));
                return slen - (quote != 0);
        }
 
-       if (quote && !fr_sbuff_next_if_char(in, quote)) {
+       if (quote && !fr_sbuff_next_if_char(&our_in, quote)) {
                fr_strerror_const("Unterminated string");
                return 0;
        }
 
        fr_assert(slen <= ((ssize_t) 1 << 20));
 
-       return slen + ((quote != 0) << 1);
+       FR_SBUFF_SET_RETURN(in, &our_in);
 }
 
 /** Our version of a DA stack.
@@ -1008,7 +1115,7 @@ redo:
                *relative = my;
 
        } else {
-               slen = fr_pair_value_from_substr(vp, &our_in, relative->tainted);
+               slen = fr_pair_value_from_substr(root, vp, &our_in);
                if (slen <= 0) goto error;
 
                fr_pair_append(my.list, vp);
@@ -1141,11 +1248,12 @@ done:
  * @param[in,out] out          where the parsed fr_pair_ts will be appended.
  * @param[in] fp               to read valuepairs from.
  * @param[out] pfiledone       true if file parsing complete;
+ * @param[in] allow_exec       Whether we allow `backtick` expansions.
  * @return
  *     - 0 on success
  *     - -1 on error
  */
-int fr_pair_list_afrom_file(TALLOC_CTX *ctx, fr_dict_t const *dict, fr_pair_list_t *out, FILE *fp, bool *pfiledone)
+int fr_pair_list_afrom_file(TALLOC_CTX *ctx, fr_dict_t const *dict, fr_pair_list_t *out, FILE *fp, bool *pfiledone, bool allow_exec)
 {
        fr_pair_list_t tmp_list;
        fr_pair_parse_t root, relative;
@@ -1167,6 +1275,7 @@ int fr_pair_list_afrom_file(TALLOC_CTX *ctx, fr_dict_t const *dict, fr_pair_list
                .internal = fr_dict_internal(),
                .allow_crlf = true,
                .allow_compare = true,
+               .allow_exec = allow_exec
        };
        relative = (fr_pair_parse_t) { };
 
index fb449f694ea3a070cc79dca07b351d351dc81870..9d922f1ed322e833882405c71e451327cbd1d2f9 100644 (file)
@@ -35,11 +35,11 @@ extern "C" {
 #endif
 
 int            fr_pair_list_afrom_file(TALLOC_CTX *ctx, fr_dict_t const *dict,
-                                       fr_pair_list_t *out, FILE *fp, bool *pfiledone);
+                                       fr_pair_list_t *out, FILE *fp, bool *pfiledone, bool allow_exec);
 
 void           fr_pair_list_move_op(fr_pair_list_t *to, fr_pair_list_t *from, fr_token_t op);
 
-typedef struct fr_pair_parse_s {
+typedef struct {
        TALLOC_CTX              *ctx;
        fr_dict_attr_t const    *da;            //!< root da to start parsing from
        fr_pair_list_t          *list;          //!< list where output is placed
@@ -50,6 +50,11 @@ typedef struct fr_pair_parse_s {
        bool                    allow_compare;  //!< allow comparison operators
        bool                    allow_crlf;     //!< allow CRLF, and treat like comma
        bool                    allow_zeros;    //!< allow '\0' as end of attribute
+       bool                    allow_exec;     //!< allow `exec` to execute external commands.
+                                               ///< This should only be allowed in trusted input,
+                                               ///< and on startup only.  popen() is used for the
+                                               ///< execution, and it has no configurable timeout,
+                                               ///< so the calling code will wait indefinitely.
        bool                    tainted;        //!< source is tainted
        char                    last_char;      //!< last character we read - ',', '\n', or 0 for EOF
        bool                    end_of_list;    //!< do we expect an end of list '}' character?
index 3db64a303db59945acdbbf62e2470148fb3ce3cb..6c8b81f5bee9df97edf5c1ec4bc8a3de59b162e3 100644 (file)
@@ -124,6 +124,85 @@ static void test_fr_pair_list_afrom_substr(void)
        fr_pair_list_free(&list);
 }
 
+
+static void test_fr_pair_list_afrom_substr_exec(void)
+{
+       fr_pair_t       *vp;
+       ssize_t         len;
+       fr_pair_list_t  list;
+       ssize_t         slen;
+       char const      *buffer = "Test-Uint32-0 = 123, Test-String-0 = `echo \"Testing321\"`";
+       char const      *buffer_multi = "Test-String-0 = `echo \"Testing321\"`, Test-String-0 += 'Testing123'";
+       fr_pair_parse_t root, relative;
+
+       root = (fr_pair_parse_t) {
+               .ctx = autofree,
+               .da = fr_dict_root(test_dict),
+               .list = &list,
+               .dict = test_dict,
+               .internal = fr_dict_internal(),
+               .allow_exec = true
+       };
+       relative = (fr_pair_parse_t) { };
+
+       fr_pair_list_init(&list);
+       len = strlen(buffer);
+
+       TEST_CASE("Create 'vp' using fr_pair_list_afrom_substr()");
+       slen = fr_pair_list_afrom_substr(&root, &relative, &FR_SBUFF_IN(buffer, len));
+       TEST_CHECK_SLEN(slen, (ssize_t)len);
+       TEST_MSG_FAIL("fr_pair_list_afrom_substr(): %s", fr_strerror());
+
+       TEST_CASE("Looking for Test-Uint32-0");
+       TEST_CHECK((vp = fr_pair_find_by_da(&list, NULL, fr_dict_attr_test_uint32)) != NULL);
+
+       TEST_CASE("Validating PAIR_VERIFY()");
+       PAIR_VERIFY(vp);
+
+       TEST_CASE("Checking if (Test-Uint32-0 == 123)");
+       TEST_CHECK(vp && vp->vp_uint32 == 123);
+
+       TEST_CASE("Looking for Test-String-0");
+       TEST_CHECK((vp = fr_pair_find_by_da(&list, NULL, fr_dict_attr_test_string)) != NULL);
+
+       TEST_CASE("Validating PAIR_VERIFY()");
+       PAIR_VERIFY(vp);
+
+       TEST_MSG_FAIL("Pair value was: %s", vp->vp_strvalue);
+       TEST_CASE("Checking if (Test-String-0 == 'Testing321')");
+       TEST_CHECK(vp && strcmp(vp->vp_strvalue, "Testing321") == 0);
+
+       fr_pair_list_free(&list);
+
+       len = strlen(buffer_multi);
+       TEST_CASE("Create 'vp' using fr_pair_list_afrom_substr()");
+       slen = fr_pair_list_afrom_substr(&root, &relative, &FR_SBUFF_IN(buffer_multi, len));
+       TEST_CHECK_SLEN(slen, (ssize_t)len);
+       TEST_MSG_FAIL("fr_pair_list_afrom_substr(): %s", fr_strerror());
+
+       TEST_CASE("Looking for Test-String-0");
+       TEST_CHECK((vp = fr_pair_find_by_da(&list, NULL, fr_dict_attr_test_string)) != NULL);
+
+       TEST_CASE("Validating PAIR_VERIFY()");
+       PAIR_VERIFY(vp);
+
+       TEST_MSG_FAIL("Pair value was: %s", vp->vp_strvalue);
+       TEST_CASE("Checking if (Test-String-0 == 'Testing321')");
+       TEST_CHECK(vp && strcmp(vp->vp_strvalue, "Testing321") == 0);
+
+       TEST_CASE("Looking for Test-String-0");
+       TEST_CHECK((vp = fr_pair_find_by_da(&list, vp, fr_dict_attr_test_string)) != NULL);
+
+       TEST_CASE("Validating PAIR_VERIFY()");
+       PAIR_VERIFY(vp);
+
+       TEST_MSG_FAIL("Pair value was: %s", vp->vp_strvalue);
+       TEST_CASE("Checking if (Test-String-0 == 'Testing123')");
+       TEST_CHECK(vp && strcmp(vp->vp_strvalue, "Testing123") == 0);
+
+       fr_pair_list_free(&list);
+}
+
 static FILE *open_buffer_as_file(uint8_t const *buffer, size_t buffer_len)
 {
        FILE *fp;
@@ -147,7 +226,7 @@ static void test_fr_pair_list_afrom_file(void)
        fr_pair_list_init(&list);
 
        TEST_CASE("Create 'vp' using fr_pair_list_afrom_file()");
-       TEST_CHECK(fr_pair_list_afrom_file(autofree, test_dict, &list, fp, &pfiledone) == 0);
+       TEST_CHECK(fr_pair_list_afrom_file(autofree, test_dict, &list, fp, &pfiledone, false) == 0);
 
        TEST_CASE("Looking for Test-Uint32-0");
        TEST_CHECK((vp = fr_pair_find_by_da(&list, NULL, fr_dict_attr_test_uint32)) != NULL);
@@ -184,7 +263,7 @@ static void test_fr_pair_list_move_op(void)
        fr_pair_list_init(&new_list);
 
        TEST_CASE("Create 'vp' using fr_pair_list_afrom_file()");
-       TEST_CHECK(fr_pair_list_afrom_file(autofree, test_dict, &old_list, fp, &pfiledone) == 0);
+       TEST_CHECK(fr_pair_list_afrom_file(autofree, test_dict, &old_list, fp, &pfiledone, false) == 0);
        TEST_CHECK(pfiledone == true);
 
        TEST_CASE("Move pair from 'old_list' to 'new_list' using fr_pair_list_move_op()");
@@ -222,6 +301,7 @@ TEST_LIST = {
         *      Legacy calls
         */
        { "fr_pair_list_afrom_substr",  test_fr_pair_list_afrom_substr },
+       { "fr_pair_list_afrom_substr_exec", test_fr_pair_list_afrom_substr_exec },
        { "fr_pair_list_afrom_file", test_fr_pair_list_afrom_file },
        { "fr_pair_list_move_op",       test_fr_pair_list_move_op },
 
index 4543efed255d4eabe62b07a81e3451690fc25a4f..9c8df9160e9a3a785d98defd914987d13fe8632d 100644 (file)
@@ -421,7 +421,7 @@ static int mod_instantiate(void *instance, CONF_SECTION *conf)
                return -1;
        }
 
-       if (fr_pair_list_afrom_file(inst, inst->dict, &inst->vps, fp, &done) < 0) {
+       if (fr_pair_list_afrom_file(inst, inst->dict, &inst->vps, fp, &done, true) < 0) {
                fclose(fp);
                cf_log_err(conf, "Failed reading %s - %s", inst->filename, fr_strerror());
                return -1;
index b2f11508040cc5f1b4059be6bc47624c16a4314e..ec59ec845649c79d73bf03c254036d898b4591b3 100644 (file)
@@ -716,7 +716,7 @@ static int mod_instantiate(module_inst_ctx_t const *mctx)
                return -1;
        }
 
-       if (fr_pair_list_afrom_file(inst, inst->dict, &inst->pair_list, fp, &done) < 0) {
+       if (fr_pair_list_afrom_file(inst, inst->dict, &inst->pair_list, fp, &done, true) < 0) {
                cf_log_perr(conf, "Failed reading %s", inst->filename);
                fclose(fp);
                return -1;
index 1bd4b368dae0ef38582661641ae150ecae1ddc96..6db9eb808ab803c496ea15e2fff064ef382efbe6 100644 (file)
@@ -367,7 +367,7 @@ static int mod_instantiate(module_inst_ctx_t const *mctx)
                        return -1;
                }
 
-               if (fr_pair_list_afrom_file(inst, inst->dict, &inst->pair_list, fp, &done) < 0) {
+               if (fr_pair_list_afrom_file(inst, inst->dict, &inst->pair_list, fp, &done, true) < 0) {
                        cf_log_perr(conf, "Failed reading %s", inst->filename);
                        fclose(fp);
                        return -1;