]> git.ipfire.org Git - thirdparty/freeradius-server.git/commitdiff
v4: convert %(config :) to new xlat api (#4033)
authorNick Porter <nick@portercomputing.co.uk>
Mon, 29 Mar 2021 09:18:04 +0000 (10:18 +0100)
committerGitHub <noreply@github.com>
Mon, 29 Mar 2021 09:18:04 +0000 (10:18 +0100)
* Convert %{config: } to new xlat api

* Update documentation for new %(config: ) xlat syntax

* Remove former config xlat escape function

* Add test for %(config: ) xlat

doc/antora/modules/reference/pages/xlat/builtin.adoc
src/lib/server/main_config.c
src/tests/keywords/xlat-config [new file with mode: 0644]

index 8110544100bbac4954966d9722dadce7d74bf3e0..38bdac403e49dc1a0c9c1edaccd6aabc901a6aae 100644 (file)
@@ -166,7 +166,7 @@ The string value of 0x7465737431 is test1
 
 == Server Manipulation
 
-=== %{config:<key>}
+=== %(config:<key>)
 
 Refers to a variable in the configuration file. See the documentation
 on configuration file references.
@@ -177,8 +177,8 @@ on configuration file references.
 
 [source,unlang]
 ----
-"Server installed in %{config:prefix}"
-"Module rlm_exec.shell_escape = %{config:modules.exec.shell_escape}"
+"Server installed in %(config:prefix)"
+"Module rlm_exec.shell_escape = %(config:modules.exec.shell_escape)"
 ----
 
 .Output
index 3b777bd9d8ef7c7885b2119cb0daf8a93b5d7fb2..8c5c569ba90b944cd5bf6b2f962da6643014ae78 100644 (file)
@@ -437,101 +437,109 @@ static int num_workers_parse(TALLOC_CTX *ctx, void *out, void *parent,
 }
 
 
-static size_t config_escape_func(UNUSED request_t *request, char *out, size_t outlen, char const *in, UNUSED void *arg)
+static int xlat_config_escape(UNUSED request_t *request, fr_value_box_t *vb, UNUSED void *uctx)
 {
-       size_t len = 0;
-       static char const disallowed[] = "%{}\\'\"`";
-
-       while (in[0]) {
+       static char const       disallowed[] = "%{}\\'\"`";
+       size_t                  outmax = vb->vb_length * 3;
+       size_t                  outlen = 0;
+       char                    escaped[outmax];
+       char const              *in, *end;
+       char                    *out = escaped;
+
+       fr_assert(vb->type == FR_TYPE_STRING);
+       in = vb->vb_strvalue;
+       end = in + vb->vb_length;
+
+       do {
                /*
                 *      Non-printable characters get replaced with their
                 *      mime-encoded equivalents.
                 */
                if ((in[0] < 32)) {
-                       if (outlen <= 3) break;
+                       if (outlen >= (outmax - 3)) return -1;
 
                        snprintf(out, outlen, "=%02X", (unsigned char) in[0]);
-                       in++;
                        out += 3;
-                       outlen -= 3;
-                       len += 3;
+                       outlen += 3;
                        continue;
 
                } else if (strchr(disallowed, *in) != NULL) {
-                       if (outlen <= 2) break;
+                       if (outlen >= (outmax - 2)) return -1;
 
                        out[0] = '\\';
                        out[1] = *in;
-                       in++;
                        out += 2;
-                       outlen -= 2;
-                       len += 2;
+                       outlen += 2;
                        continue;
                }
 
                /*
                 *      Only one byte left.
                 */
-               if (outlen <= 1) {
-                       break;
-               }
+               if (outlen >= (outmax - 1)) return -1;
 
                /*
                 *      Allowed character.
                 */
                *out = *in;
                out++;
-               in++;
-               outlen--;
-               len++;
-       }
+               outlen++;
+       } while (++in < end);
        *out = '\0';
-       return len;
+
+       /*
+        *      If the output length is greater than the input length
+        *      something has been escaped - replace the original string
+        */
+       if (outlen > vb->vb_length) {
+               char    *outbuff;
+               fr_value_box_bstr_realloc(vb, &outbuff, vb, outlen);
+               memcpy(outbuff, escaped, outlen);
+       }
+
+       return 0;
+
 }
 
+static xlat_arg_parser_t const xlat_config_args[] = {
+       { .required = true, .concat = true, .type = FR_TYPE_STRING, .always_escape = true, .func = xlat_config_escape },
+       XLAT_ARG_PARSER_TERMINATOR
+};
+
 /** xlat to get config values
  *
 @verbatim
-%{config:section.subsection.attribute}
+%(config:section.subsection.attribute)
 @endverbatim
  *
  * @ingroup xlat_functions
  */
-static ssize_t xlat_config(UNUSED TALLOC_CTX *ctx, char **out, size_t outlen,
-                          UNUSED void const *mod_inst, UNUSED void const *xlat_inst,
-                          request_t *request, char const *fmt)
+static xlat_action_t xlat_config(TALLOC_CTX *ctx, fr_dcursor_t *out, request_t *request,
+                                UNUSED void const *xlat_inst, UNUSED void *xlat_thread_inst,
+                                fr_value_box_list_t *in)
 {
-       char const *value;
-       CONF_PAIR *cp;
-       CONF_ITEM *ci;
-       char buffer[1024];
-
-       /*
-        *      Expand it safely.
-        */
-       if (xlat_eval(buffer, sizeof(buffer), request, fmt, config_escape_func, NULL) < 0) return 0;
+       char const      *value;
+       CONF_PAIR       *cp;
+       CONF_ITEM       *ci;
+       fr_value_box_t  *in_head = fr_dlist_head(in);
+       fr_value_box_t  *vb;
 
-       ci = cf_reference_item(main_config->root_cs, main_config->root_cs, buffer);
+       ci = cf_reference_item(main_config->root_cs, main_config->root_cs, in_head->vb_strvalue);
        if (!ci || !cf_item_is_pair(ci)) {
-               REDEBUG("Config item \"%s\" does not exist", fmt);
-               return -1;
+               REDEBUG("Config item \"%pV\" does not exist", in_head);
+               return XLAT_ACTION_FAIL;
        }
 
        cp = cf_item_to_pair(ci);
 
-       /*
-        *  Ensure that we only copy what's necessary.
-        *
-        *  If 'outlen' is too small, then the output is chopped to fit.
-        */
        value = cf_pair_value(cp);
-       if (!value) return 0;
-
-       if (outlen > strlen(value)) outlen = strlen(value) + 1;
+       if (!value) return XLAT_ACTION_DONE;
 
-       strlcpy(*out, value, outlen);
+       MEM(vb = fr_value_box_alloc_null(ctx));
+       fr_value_box_bstrndup(ctx, vb, NULL, value, strlen(value), false);
+       fr_dcursor_append(out, vb);
 
-       return strlen(*out);
+       return XLAT_ACTION_DONE;
 }
 
 #ifdef HAVE_SETUID
@@ -862,11 +870,12 @@ static int _dlhandle_free(void **dl_handle)
  */
 int main_config_init(main_config_t *config)
 {
-       char const              *p = NULL;
-       CONF_SECTION            *cs = NULL, *subcs;
-       struct stat             statbuf;
-       bool                    can_colourise = false;
-       char                    buffer[1024];
+       char const      *p = NULL;
+       CONF_SECTION    *cs = NULL, *subcs;
+       struct stat     statbuf;
+       bool            can_colourise = false;
+       char            buffer[1024];
+       xlat_t          *xlat;
 
        if (stat(config->raddb_dir, &statbuf) < 0) {
                ERROR("Error checking raddb_dir \"%s\": %s", config->raddb_dir, fr_syserror(errno));
@@ -1195,9 +1204,10 @@ do {\
        if (!client_list_parse_section(cs, 0, false)) goto failure;
 
        /*
-        *      Register the %{config:section.subsection} xlat function.
+        *      Register the %(config:section.subsection) xlat function.
         */
-       xlat_register_legacy(NULL, "config", xlat_config, NULL, NULL, 0, XLAT_DEFAULT_BUF_LEN);
+       xlat = xlat_register(NULL, "config", xlat_config, false);
+       xlat_func_args(xlat, xlat_config_args);
 
        /*
         *      Ensure cwd is inside the chroot.
diff --git a/src/tests/keywords/xlat-config b/src/tests/keywords/xlat-config
new file mode 100644 (file)
index 0000000..01850ac
--- /dev/null
@@ -0,0 +1,25 @@
+#
+# PRE: update if
+#
+
+update request {
+        &Tmp-String-0 := "%(config:modules.test.boolean)"
+}
+
+if (&Tmp-String-0 != "no") {
+        test_fail
+}
+
+update request {
+        &Tmp-String-0 := "test"
+}
+
+update request {
+        &Tmp-Integer-0 := "%(config:modules.%{Tmp-String-0}.integer)"
+}
+
+if (&Tmp-Integer-0 != 1) {
+        test_fail
+}
+
+success
\ No newline at end of file