]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
lib-imap: Fail parsing on impossible imap-dates/times
authorTimo Sirainen <timo.sirainen@open-xchange.com>
Sat, 13 May 2023 21:25:20 +0000 (00:25 +0300)
committeraki.tuomi <aki.tuomi@open-xchange.com>
Mon, 30 Dec 2024 06:47:22 +0000 (06:47 +0000)
For example 32-Jan-2023 was converted into a max-timestamp before instead of
being rejected.

src/lib-imap/Makefile.am
src/lib-imap/imap-date.c
src/lib-imap/test-imap-date.c [new file with mode: 0644]

index b1d275c724a975ed8abcab771d15ece3995926aa..52e0e998e0c3d62f19165622bfc2a01bca0afc77 100644 (file)
@@ -44,6 +44,7 @@ pkginc_lib_HEADERS = $(headers)
 
 test_programs = \
        test-imap-bodystructure \
+       test-imap-date \
        test-imap-envelope \
        test-imap-match \
        test-imap-parser \
@@ -64,6 +65,10 @@ test_imap_bodystructure_SOURCES = test-imap-bodystructure.c
 test_imap_bodystructure_LDADD = imap-bodystructure.lo imap-envelope.lo imap-quote.lo imap-parser.lo imap-arg.lo ../lib-mail/libmail.la $(test_libs)
 test_imap_bodystructure_DEPENDENCIES = $(test_deps) ../lib-mail/libmail.la
 
+test_imap_date_SOURCES = test-imap-date.c
+test_imap_date_LDADD = imap-date.lo $(test_libs)
+test_imap_date_DEPENDENCIES = $(test_deps)
+
 test_imap_envelope_SOURCES = test-imap-envelope.c
 test_imap_envelope_LDADD = imap-envelope.lo imap-quote.lo imap-parser.lo imap-arg.lo ../lib-mail/libmail.la $(test_libs)
 test_imap_envelope_DEPENDENCIES = $(test_deps) ../lib-mail/libmail.la
index 2f04cabcce70097938fba75461e235e686eafad8..c9262932c1b011051e51b87dfe8fc40447bbdbae 100644 (file)
@@ -79,11 +79,43 @@ static const char *imap_parse_date_internal(const char *str, struct tm *tm)
        return str;
 }
 
-static bool imap_mktime(struct tm *tm, time_t *time_r)
+static bool tm_is_too_large(const struct tm *tm, time_t *max_time_r)
+{
+       static time_t max_time = 0;
+       static struct tm max_tm = { 0, };
+
+       if (max_time == 0) {
+#if TIME_T_MAX_BITS == 32
+               max_time = 0xffffffffUL;
+#elif TIME_T_MAX_BITS == 64
+               max_time = 0xffffffffffffffffULL;
+#else
+               max_time = ((time_t)1 << TIME_T_MAX_BITS) - 1;
+#endif
+               max_tm = *gmtime(&max_time);
+       }
+       *max_time_r = max_time;
+
+       if (tm->tm_year != max_tm.tm_year)
+               return tm->tm_year > max_tm.tm_year;
+       if (tm->tm_mon != max_tm.tm_mon)
+               return tm->tm_mon > max_tm.tm_mon;
+       if (tm->tm_mday != max_tm.tm_mday)
+               return tm->tm_mday > max_tm.tm_mday;
+       if (tm->tm_hour != max_tm.tm_hour)
+               return tm->tm_hour > max_tm.tm_hour;
+       if (tm->tm_min != max_tm.tm_min)
+               return tm->tm_min > max_tm.tm_min;
+       if (tm->tm_sec != max_tm.tm_sec)
+               return tm->tm_sec > max_tm.tm_sec;
+       return FALSE;
+}
+
+static int imap_mktime(struct tm *tm, time_t *time_r)
 {
        *time_r = utc_mktime(tm);
        if (*time_r != (time_t)-1)
-               return TRUE;
+               return 1;
 
        /* the date is outside valid range for time_t. it might still be
           technically valid though, so try to handle this case.
@@ -96,17 +128,15 @@ static bool imap_mktime(struct tm *tm, time_t *time_r)
 #else
                *time_r = 0;
 #endif
-       } else {
+               return 0;
+       } else if (tm_is_too_large(tm, time_r)) {
                /* too high. return the highest allowed value.
                   we shouldn't get here with 64bit time_t,
                   but handle that anyway. */
-#if (TIME_T_MAX_BITS == 32 || TIME_T_MAX_BITS == 64)
-               *time_r = (1UL << (TIME_T_MAX_BITS-1)) - 1;
-#else
-               *time_r = (1UL << TIME_T_MAX_BITS) - 1;
-#endif
+               return 0;
+       } else {
+               return -1;
        }
-       return FALSE;
 }
 
 bool imap_parse_date(const char *str, time_t *timestamp_r)
@@ -118,7 +148,8 @@ bool imap_parse_date(const char *str, time_t *timestamp_r)
                return FALSE;
 
        tm.tm_isdst = -1;
-       (void)imap_mktime(&tm, timestamp_r);
+       if (imap_mktime(&tm, timestamp_r) < 0)
+               return FALSE;
        return TRUE;
 }
 
@@ -126,6 +157,7 @@ bool imap_parse_datetime(const char *str, time_t *timestamp_r,
                         int *timezone_offset_r)
 {
        struct tm tm;
+       int ret;
 
        str = imap_parse_date_internal(str, &tm);
        if (str == NULL)
@@ -157,9 +189,9 @@ bool imap_parse_datetime(const char *str, time_t *timestamp_r,
        *timezone_offset_r = parse_timezone(str);
 
        tm.tm_isdst = -1;
-       if (imap_mktime(&tm, timestamp_r))
+       if ((ret = imap_mktime(&tm, timestamp_r)) > 0)
                *timestamp_r -= *timezone_offset_r * 60;
-       return TRUE;
+       return ret >= 0;
 }
 
 static void imap_to_date_tm(char buf[11], const struct tm *tm)
diff --git a/src/lib-imap/test-imap-date.c b/src/lib-imap/test-imap-date.c
new file mode 100644 (file)
index 0000000..5a2a364
--- /dev/null
@@ -0,0 +1,111 @@
+/* Copyright (c) 2023 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+#include "env-util.h"
+#include "imap-date.h"
+#include "test-common.h"
+
+#include <time.h>
+
+static void test_imap_date(void)
+{
+       const struct {
+               const char *str;
+               time_t timestamp;
+       } tests[] = {
+               { "01-Jan-1970", 0 },
+               { "19-Jan-2038", 2147472000 },
+#if TIME_T_MAX_BITS >= 32
+               { "07-Feb-2106", 4294944000 },
+#endif
+#if TIME_T_MAX_BITS >= 37
+               { "08-Apr-6325", 137438899200LL },
+#endif
+#if TIME_T_MAX_BITS >= 38
+               { "31-Dec-9999", 253402214400LL },
+#endif
+               /* conversions to maximum values */
+#if TIME_T_MAX_BITS == 31
+               { "20-Jan-2038", 2147483647 },
+               { "31-Dec-9999", 2147483647 },
+#elif TIME_T_MAX_BITS == 32
+               { "08-Feb-2106", 4294967295 },
+               { "31-Dec-9999", 4294967295 },
+#endif
+       };
+       const char *invalid_tests[] = {
+               "32-Jan-2023",
+               "29-Feb-2023",
+               "31-Apr-2023",
+       };
+       time_t ts;
+
+       test_begin("imap_parse_date()");
+       for (unsigned int i = 0; i < N_ELEMENTS(tests); i++) {
+               test_assert_idx(imap_parse_date(tests[i].str, &ts), i);
+               test_assert_cmp_idx(tests[i].timestamp, ==, ts, i);
+       }
+       for (unsigned int i = 0; i < N_ELEMENTS(invalid_tests); i++)
+               test_assert_idx(!imap_parse_date(invalid_tests[i], &ts), i);
+       test_end();
+}
+
+static void test_imap_datetime(void)
+{
+       const struct {
+               const char *str;
+               time_t timestamp;
+               int tz;
+       } tests[] = {
+               { "01-Jan-1970 00:00:00 +0000", 0, 0 },
+               { "19-Jan-2038 03:14:07 +0000", 2147483647, 0 },
+               { "19-Jan-2038 05:14:07 +0200", 2147483647, 2*60 },
+#if TIME_T_MAX_BITS >= 32
+               { "07-Feb-2106 06:28:15 +0000", 4294967295, 0 },
+#endif
+#if TIME_T_MAX_BITS >= 37
+               { "08-Apr-6325 15:04:31 +0000", 137438953471LL, 0 },
+#endif
+#if TIME_T_MAX_BITS >= 38
+               { "31-Dec-9999 23:59:59 +2359", 253402300799LL - 23*60*60 - 59*60, 23*60 + 59 },
+               { "31-Dec-9999 23:59:59 -2359", 253402300799LL + 23*60*60 + 59*60, -23*60 - 59 },
+#endif
+               /* conversions to maximum values */
+#if TIME_T_MAX_BITS == 31
+               { "19-Jan-2038 03:14:08 +0000", 2147483647, 0 },
+               { "31-Dec-9999 23:59:59 -2359", 2147483647, -23*60 - 59 },
+#elif TIME_T_MAX_BITS == 32
+               { "07-Feb-2106 06:28:16 +0000", 4294967295, 0 },
+               { "31-Dec-9999 23:59:59 -2359", 4294967295, -23*60 - 59 },
+#endif
+       };
+       const char *invalid_tests[] = {
+               "02-Jan-2023 24:00:00",
+               "02-Jan-2023 23:60:00",
+               "02-Jan-2023 23:00:60",
+       };
+       time_t ts;
+       int tz;
+
+       test_begin("imap_parse_date()");
+       for (unsigned int i = 0; i < N_ELEMENTS(tests); i++) {
+               test_assert_idx(imap_parse_datetime(tests[i].str, &ts, &tz), i);
+               test_assert_cmp_idx(tests[i].timestamp, ==, ts, i);
+               test_assert_idx(tests[i].tz == tz, i);
+       }
+       for (unsigned int i = 0; i < N_ELEMENTS(invalid_tests); i++)
+               test_assert_idx(!imap_parse_datetime(invalid_tests[i], &ts, &tz), i);
+       test_end();
+}
+
+int main(void)
+{
+       static void (*const test_functions[])(void) = {
+               test_imap_date,
+               test_imap_datetime,
+               NULL
+       };
+       env_put("TZ", "UTC");
+       tzset();
+       return test_run(test_functions);
+}