From: Alan T. DeKok Date: Sun, 11 Dec 2022 15:37:29 +0000 (-0500) Subject: add fr_skip_xlat() to mirror fr_skip_string() X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=326ea5f3e7bf6e9c8fd44e429a06bc5dd6b23445;p=thirdparty%2Ffreeradius-server.git add fr_skip_xlat() to mirror fr_skip_string() which now takes an "end" ptr, and is a bit more intelligent --- diff --git a/src/lib/server/cf_file.c b/src/lib/server/cf_file.c index 616bbb518f8..37bccf1f2d4 100644 --- a/src/lib/server/cf_file.c +++ b/src/lib/server/cf_file.c @@ -1166,54 +1166,123 @@ static int process_template(cf_stack_t *stack) static int cf_file_fill(cf_stack_t *stack); -/* - * Expansions can contain other expansions, but can't contain regexes. + +/** Skip an xlat expression. + * + * This is a simple "peek ahead" parser which tries to not be wrong. It may accept + * some things which will later parse as invalid (e.g. unknown attributes, etc.) + * But it also rejects all malformed expressions. + * + * It's used as a quick hack because the full parser isn't always available. + * + * @param[in] start start of the expression, MUST point to the "%{" or "%(" + * @param[in] end end of the string (or NULL for zero-terminated strings) + * @return + * >0 length of the string which was parsed + * <=0 on error */ -static uint8_t const *skip_expanse(uint8_t const *p) +static ssize_t fr_skip_xlat(char const *start, char const *end) { - int depth = 1; /* caller skips '{' */ + int depth = 1; /* caller skips '{' */ + ssize_t slen; + char quote, end_quote; + char const *p = start; + + /* + * At least %{1} + */ + if (end && ((start + 4) < end)) { + fr_strerror_const("Invalid expansion"); + return 0; + } + + if ((*p != '%') && (*p != '$')) { + fail: + fr_strerror_const("Unexpected character in xlat"); + return -(p - start); + } + + p++; + if ((*p != '{') && (*p != '(')) goto fail; + + quote = *(p++); + if (quote == '{') { + end_quote = '}'; + } else { + end_quote = ')'; + } - while (*p >= ' ') { - if (*p == '{') { + while ((end && (p < end)) || (*p >= ' ')) { + if (*p == quote) { p++; depth++; continue; } - if (*p == '}') { + + if (*p == end_quote) { p++; depth--; - if (!depth) return p; + if (!depth) return p - start; continue; } - if (*p == '\\') { - if (p[1] < ' ') return NULL; - p += 2; - continue; - } + /* + * Nested expansion. + */ + if ((p[0] == '$') || (p[0] == '%')) { + if (end && (p + 2) >= end) break; + + if ((p[1] == '{') || ((p[0] == '$') && (p[1] == '('))) { + slen = fr_skip_xlat(p, end); + + check: + if (slen <= 0) return -(p - start) + slen; - if (((p[0] == '$') || (p[0] == '%')) && (p[1] == '{')) { - p = skip_expanse(p + 2); - if (!p) return NULL; + p += slen; + continue; + } + + /* + * Bare $ or %, just leave it alone. + */ + p++; continue; } + /* + * A quoted string. + */ if ((*p == '"') || (*p == '\'') || (*p == '`')) { - ssize_t slen; + slen = fr_skip_string(p, end); + goto check; + } - slen = fr_skip_string((char const *) p, NULL); - if (slen <= 0) return NULL; + /* + * @todo - bare '(' is a condition or nested + * expression. The brackets need to balance + * here, too. + */ - p += slen; + if (*p != '\\') { + p++; continue; } - p++; - } + if (end && ((p + 2) >= end)) break; + /* + * Escapes here are only one-character escapes. + */ + if (p[1] < ' ') break; + p += 2; + } - return NULL; + /* + * Unexpected end of xlat + */ + fr_strerror_const("Unexpected end of xlat"); + return -(p - start); } static CONF_ITEM *process_if(cf_stack_t *stack) @@ -1323,13 +1392,16 @@ static CONF_ITEM *process_if(cf_stack_t *stack) * Bare words of ${..} or %{...} */ if (((p[0] == '$') || (p[0] == '%')) && (p[1] == '{')) { - p = skip_expanse(p + 2); - if (!p) { - cf_log_err(cs, "Unexpected end of expansion"); + ssize_t my_slen; + + my_slen = fr_skip_xlat((char const *) p, NULL); + if (my_slen <= 0) { + cf_log_err(cs, "Failed parsing xlat in condition - %s", fr_strerror()); talloc_free(cs); return NULL; } + p += my_slen; continue; } @@ -1343,7 +1415,7 @@ static CONF_ITEM *process_if(cf_stack_t *stack) my_slen = fr_skip_string((char const *) p, NULL); if (my_slen <= 0) { - cf_log_err(cs, "Unexpected end of string"); + cf_log_err(cs, "Failed parsing string in condition - %s", fr_strerror()); talloc_free(cs); return NULL; }