]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
lib-var-expand: Validate UTF-8 input
authorAki Tuomi <aki.tuomi@open-xchange.com>
Mon, 17 Feb 2025 09:47:52 +0000 (11:47 +0200)
committeraki.tuomi <aki.tuomi@open-xchange.com>
Mon, 17 Feb 2025 18:43:45 +0000 (18:43 +0000)
src/lib-var-expand/test-var-expand.c
src/lib-var-expand/var-expand-lexer.l

index 6c227df5670e84028f2eaafee4621ceff86bbd30..3f3327f1b347537b2a6ca964b38317e2db06f86e 100644 (file)
@@ -89,6 +89,7 @@ static void test_var_expand_builtin_filters(void) {
                { .key = "multivalue", .value = "one\ttwo\tthree" },
                { .key = "uidvalidity", .value = "1727121943" },
                { .key = "empty", .value = "" },
+               { .key = "\xce\xb8", .value = "is theta" },
                { .key = "null", .value = NULL },
                VAR_EXPAND_TABLE_END
        };
@@ -242,6 +243,8 @@ static void test_var_expand_builtin_filters(void) {
                { .in = "%{first | md5 % 30 | hex(2) }", .out = "16", .ret = 0 },
                { .in = "%{first | md5 % 30 | hex(4) }", .out = "0016", .ret = 0 },
                { .in = "%{first | md5 % 30 | hex(-4) }", .out = "1600", .ret = 0 },
+               { .in = "%{\xce\xb8}", .out = "is theta", .ret = 0 },
+               { .in = "%{\xff\xfe\xff}", .out = "Invalid UTF-8 string", .ret = -1 },
        };
 
        const struct var_expand_params params = {
index 64e0cc2794eb3399348e488b94a5c3fbb090ca7f..0b8433ab91b1d9dcdc70f47a13d5bec05e5b974e 100644 (file)
@@ -14,6 +14,7 @@
 %{
 
 #include "lib.h"
+#include "unichar.h"
 #include "str.h"
 #include "var-expand-private.h"
 #include "var-expand-parser-private.h"
@@ -47,6 +48,11 @@ static size_t input_proc(char *buf, size_t size, yyscan_t scanner);
        } STMT_END
 
 static int scanner_error(void *yyscanner, const char *msg);
+static bool append_valid_utf8(void *yyscanner, YYSTYPE *state, const char *value);
+#define append_valid_utf8(state, value) \
+       if (!append_valid_utf8(yyscanner, (state), (value))) { \
+               return VAR_EXPAND_PARSER_error; \
+       }
 %}
 
 %x stringsq
@@ -83,20 +89,20 @@ static int scanner_error(void *yyscanner, const char *msg);
 <stringdq>{
   [\\]    { yy_push_state(quote, yyscanner); }
   ["]     { yy_pop_state(yyscanner); return VALUE; }
-  [^"\\]* { str_append(yylval->str, yytext); }
+  [^"\\]* { append_valid_utf8(yylval, yytext); }
 }
 
 <stringsq>{
   [\\]    { yy_push_state(quote, yyscanner); }
   [']     { yy_pop_state(yyscanner); return VALUE; }
-  [^'\\]* { str_append(yylval->str, yytext); }
+  [^'\\]* { append_valid_utf8(yylval, yytext); }
 }
 
 <expr>{
   "}"      { yy_pop_state(yyscanner); return CCBRACE; }
   [ \t]     { /* ignore */ }
   \%        { return PERC; }
-  [a-zA-Z\x80-\xff][a-zA-Z0-9_/;:.\x80-\xff-]* { INIT_STR; str_append(yylval->str, yytext); return NAME; }
+  [a-zA-Z\x80-\xff][a-zA-Z0-9_/;:.\x80-\xff-]* { INIT_STR; append_valid_utf8(yylval, yytext); return NAME; }
   \|        { return PIPE; }
   \(        { return OBRACE; }
   \)        { return CBRACE; }
@@ -110,7 +116,7 @@ static int scanner_error(void *yyscanner, const char *msg);
   ["]       { yy_push_state(stringdq, yyscanner); INIT_STR; }
   [0-9]+    { INIT_STR; str_append(yylval->str, yytext); return NUMBER; }
 }
-
+y
 "%{"     { yy_push_state(expr, yyscanner); return OCBRACE; }
 "%%{"     { INIT_STR; str_append(yylval->str, "%{"); return VALUE; }
 "%"      { return PERC; }
@@ -151,6 +157,17 @@ void yyfree(void *ptr, void *yyscanner)
        p_free(state->pool, ptr);
 }
 
+#undef append_valid_utf8
+static bool append_valid_utf8(void *scanner, YYSTYPE *state, const char *value)
+{
+       if (!uni_utf8_str_is_valid(value)) {
+               (void)scanner_error(scanner, "Invalid UTF-8 string");
+               return FALSE;
+       }
+       str_append(state->str, value);
+       return TRUE;
+}
+
 #define INPUT_POS(state) (state->input + state->input_pos)
 
 static size_t input_proc(char *buf, size_t size, yyscan_t scanner)