]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
lib-var-expand: Harden program import
authorAki Tuomi <aki.tuomi@open-xchange.com>
Sun, 22 Mar 2026 20:06:23 +0000 (22:06 +0200)
committerAki Tuomi <aki.tuomi@open-xchange.com>
Mon, 23 Mar 2026 13:40:28 +0000 (15:40 +0200)
src/lib-var-expand/expansion-program.c
src/lib-var-expand/test-var-expand.c

index bcde793f21eae5c6a64025d92e7a48dbd527ba40..ef472cd9e37fa1d7ed7c2e9da6ed24a3f2b6257e 100644 (file)
@@ -500,10 +500,15 @@ const char *var_expand_program_to_string(const struct var_expand_program *progra
 
 /* Import code */
 
-static int extract_name(char *data, size_t size,
+static int extract_name(pool_t pool, const char *data, size_t size,
                        const char **value_r, const char **error_r)
 {
-       char *ptr = memchr(data, '\1', size);
+       if (size == 0) {
+               *error_r = "Missing end of name";
+               return -1;
+       }
+
+       const char *ptr = memchr(data, '\1', size);
        if (ptr == NULL) {
                *error_r = "Missing end of name";
                return -1;
@@ -513,23 +518,27 @@ static int extract_name(char *data, size_t size,
                *value_r = NULL;
                return 1;
        }
-       *value_r = data;
-       *ptr = '\0';
+       *value_r = p_strdup_until(pool, data, ptr);
        return len + 1;
-
 }
 
-static int extract_value(char *data, size_t size,
+static int extract_value(pool_t pool, const char *data, size_t size,
                         const char **value_r, const char **error_r)
 {
-       char *ptr = memchr(data, '\r', size);
+       if (size == 0) {
+               *error_r = "Missing end of string";
+               return -1;
+       }
+
+       const char *ptr = memchr(data, '\r', size);
        if (ptr == NULL) {
                *error_r = "Missing end of string";
                return -1;
        }
        size_t len = ptr - data;
-       *ptr = '\0';
-       *value_r = str_tabunescape(data);
+       string_t *unescaped = str_new(pool, len);
+       str_append_tabunescaped(unescaped, data, len);
+       *value_r = str_c(unescaped);
        /* make sure we end up in right place. */
        return len + 1;
 }
@@ -537,6 +546,11 @@ static int extract_value(char *data, size_t size,
 static int extract_number(const char *data, size_t size, intmax_t *value_r,
                          const char **error_r)
 {
+       if (size == 0) {
+               *error_r = "Too short number";
+               return -1;
+       }
+
        const unsigned char *ptr = (const unsigned char*)data;
        bool negative;
        size_t len = 1;
@@ -548,6 +562,11 @@ static int extract_number(const char *data, size_t size, intmax_t *value_r,
                return 1;
        }
 
+       if (size < 2) {
+               *error_r = "Too short number";
+               return -1;
+       }
+
        const char sign = *ptr - 0x80;
        if (sign == '+') {
                negative = FALSE;
@@ -557,13 +576,15 @@ static int extract_number(const char *data, size_t size, intmax_t *value_r,
                *error_r = "Unknown number";
                return -1;
        }
+       size--;
        ptr++;
 
        intmax_t value = 0;
        intmax_t shift = 0;
+       size_t max_size = I_MIN(size, 9);
 
        /* a number can be at most 9 bytes */
-       for (size_t i = 0; i < I_MIN(size, 9); i++) {
+       for (size_t i = 0; i < max_size; i++) {
                len++;
                value |= ((*(ptr) & 0x7fLL) << shift);
                /* if high byte is set, the number continues */
@@ -571,9 +592,10 @@ static int extract_number(const char *data, size_t size, intmax_t *value_r,
                        break;
                shift += 7;
                ptr++;
+               size--;
        }
 
-       if ((*ptr & 0x80) != 0) {
+       if (size > 0 && (*ptr & 0x80) != 0) {
                *error_r = "Unfinished number";
                return -1;
        }
@@ -594,7 +616,7 @@ static int extract_number(const char *data, size_t size, intmax_t *value_r,
        data = data + (count); \
        size = size - (size_t)(count);
 
-static int var_expand_program_import_stmt(char *data, size_t size,
+static int var_expand_program_import_stmt(const char *data, size_t size,
                                          struct var_expand_program *program,
                                          const char **error_r)
 {
@@ -603,7 +625,7 @@ static int var_expand_program_import_stmt(char *data, size_t size,
        size_t orig_size = size;
 
        /* normal program, starts with filter name */
-       int ret = extract_name(data, size, &name, error_r);
+       int ret = extract_name(program->pool, data, size, &name, error_r);
        if (ret < 0)
                return -1;
        if (name == NULL) {
@@ -635,7 +657,7 @@ static int var_expand_program_import_stmt(char *data, size_t size,
                        param->idx = ++idx;
                        ADVANCE_INPUT(1);
                } else {
-                       ret = extract_name(data, size,
+                       ret = extract_name(program->pool, data, size,
                                           &name, error_r);
                        if (ret < 0)
                                return -1;
@@ -643,6 +665,11 @@ static int var_expand_program_import_stmt(char *data, size_t size,
                        param->key = name;
                }
 
+               if (size < 1) {
+                       *error_r = "Premature end of data";
+                       return -1;
+               }
+
                /* check the parameter type */
                switch (*data) {
                case 's':
@@ -663,9 +690,14 @@ static int var_expand_program_import_stmt(char *data, size_t size,
                }
                ADVANCE_INPUT(1);
 
+               if (size == 0) {
+                       *error_r = "Premature end of data";
+                       return -1;
+               }
+
                if (param->value_type == VAR_EXPAND_PARAMETER_VALUE_TYPE_STRING ||
                    param->value_type == VAR_EXPAND_PARAMETER_VALUE_TYPE_VARIABLE) {
-                       ret = extract_value(data, size,
+                       ret = extract_value(program->pool, data, size,
                                            &value, error_r);
                        if (ret < 0)
                                return -1;
@@ -689,9 +721,9 @@ static int var_expand_program_import_stmt(char *data, size_t size,
                        prev->next = param;
                prev = param;
 
-               if (*data == '\t') {
+               if (size > 0 && *data == '\t') {
                        break;
-               } else if (*data == '\1') {
+               } else if (size > 0 && *data == '\1') {
                        ADVANCE_INPUT(1);
                } else {
                        *error_r = "Missing parameter end";
@@ -699,7 +731,7 @@ static int var_expand_program_import_stmt(char *data, size_t size,
                }
        }
 
-       if (*data != '\t')
+       if (size < 1 || *data != '\t')
                *error_r = "Missing parameter statement end";
 
        ADVANCE_INPUT(1);
@@ -707,11 +739,11 @@ static int var_expand_program_import_stmt(char *data, size_t size,
        return orig_size - size;
 }
 
-static int var_expand_program_import_one(char **_data, size_t *_size,
+static int var_expand_program_import_one(const char **_data, size_t *_size,
                                         struct var_expand_program *program,
                                         const char **error_r)
 {
-       char *data = *_data;
+       const char *data = *_data;
        size_t size = *_size;
        const char *value;
        int ret;
@@ -719,7 +751,7 @@ static int var_expand_program_import_one(char **_data, size_t *_size,
        /* Only literal */
        if (*data == '\1') {
                ADVANCE_INPUT(1);
-               ret = extract_value(data, size, &value, error_r);
+               ret = extract_value(program->pool, data, size, &value, error_r);
                if (ret < 0)
                        return -1;
                ADVANCE_INPUT(ret);
@@ -739,30 +771,33 @@ static int var_expand_program_import_one(char **_data, size_t *_size,
        /* A full program */
        } else if (*data == '\2') {
                ADVANCE_INPUT(1);
-               while (*data != '\t' && size > 0) {
+               while (size > 0 && *data != '\t') {
                        int ret = var_expand_program_import_stmt(data, size, program, error_r);
                        if (ret < 0)
                                return -1;
                        ADVANCE_INPUT(ret);
-                       if (*data == '\t') {
+                       if (size > 0 && *data == '\t') {
                                ADVANCE_INPUT(1);
                                break;
-                       } else if (*data != '\1') {
+                       } else if (size == 0 || *data != '\1') {
                                *error_r = "Missing statement end";
                                return -1;
                        }
                        ADVANCE_INPUT(1);
                }
                /* And finally there should be variables */
-               if (*data != '\t') {
+               if (size > 0 && *data != '\t') {
                        const char *ptr = memchr(data, '\t', size);
                        if (ptr == NULL) {
                                *error_r = "Missing variables end";
                                return -1;
                        }
                        size_t len = ptr - data;
+                       /* ensure data is properly terminated, since
+                          p_strsplit() expects a NUL terminated string. */
+                       const char *variables = t_strdup_until(data, ptr);
                        program->variables = (const char *const *)
-                               p_strsplit(program->pool, data, "\1");
+                               p_strsplit(program->pool, variables, "\1");
                        ADVANCE_INPUT(len + 1);
                } else {
                        ADVANCE_INPUT(1);
@@ -793,14 +828,13 @@ int var_expand_program_import_sized(const char *data, size_t size,
        struct var_expand_program *prev = NULL;
        struct var_expand_program *first = NULL;
        int ret;
-       char *copy_data = p_strndup(pool, data, size);
 
        while (size > 0) {
                struct var_expand_program *program =
                        p_new(pool, struct var_expand_program, 1);
                program->pool = pool;
                T_BEGIN {
-                       ret = var_expand_program_import_one(&copy_data, &size,
+                       ret = var_expand_program_import_one(&data, &size,
                                                            program, error_r);
                } T_END;
                if (ret < 0)
index a8174b38af42a88b0a7426e8837a3dc78f043731..ee6a9feb092bd74ec0bd1fe70643e5c6c6124056 100644 (file)
@@ -1246,9 +1246,9 @@ static void test_var_expand_export_import(void)
                { "\x01literal", "Missing end of string" },
                { "\x03literal", "Unknown input" },
                { "\x02literal\x01", "Premature end of data" },
-               { "\x02literal\x01text\x01", "Unsupported parameter type" },
+               { "\x02literal\x01text\x01", "Premature end of data" },
                { "\x02literal\x01\x01stext\t", "Missing end of string" },
-               { "\x02literal\x01\x01i\xa1", "Unknown number" },
+               { "\x02literal\x01\x01i\xa1", "Too short number" },
                { "\x02literal\x01\x01i\xab\xf0\t", "Missing parameter end" },
                {
                        "\x02literal\x01\x01i\xab\xf0\xf0\xf0\xf0\xf0\xf0\xf0\xf0\xf0\xf0",
@@ -1256,7 +1256,7 @@ static void test_var_expand_export_import(void)
                },
                { "\x02literal\x01\x01stext\r", "Missing parameter end" },
                { "\x02literal\x01\x01stext\r\t", "Missing statement end" },
-               { "\x02literal\x01\x01stext\r\t\t", "Missing variables end" },
+               { "\x02literal\x01\x01stext\r\t\t", "Premature end of data" },
        };
 
        for(size_t i = 0; i < N_ELEMENTS(test_cases_err); i++) {
@@ -1270,7 +1270,7 @@ static void test_var_expand_export_import(void)
                        continue;
                }
 
-               test_assert_strcmp(error, t->error);
+               test_assert_strcmp_idx(error, t->error, i);
        }
 
        test_end();