From 9c1e2a83d11d49826f6f818ee343731bf049dcd8 Mon Sep 17 00:00:00 2001 From: Hayden Roche Date: Thu, 31 Mar 2022 12:50:15 -0700 Subject: [PATCH] Fix year 2038 problem in wolfSSL_ASN1_TIME_diff. Prior to this commit, this function used XMKTIME (mktime) to convert the passed in WOLFSSL_ASN1_TIMEs to Unix timestamps. On platforms where time_t is 32 bits long, times after the year 2038 can't be represented with this type. To fix this, we need to not use XMKTIME. With this commit, the static function time2epoch is added to ssl.c, which uses the date time information to compute seconds since the Unix epoch without the use of mktime. It returns the seconds as a long long. This is sufficient to make the function work for years > 2038 on the platform of the user who discovered this problem in the first place (Yocto Linux on ARMv7). --- src/ssl.c | 84 ++++++++++++++++++++++++++--------------------------- tests/api.c | 11 +++++-- 2 files changed, 51 insertions(+), 44 deletions(-) diff --git a/src/ssl.c b/src/ssl.c index 31ed6075e..add02f7e8 100644 --- a/src/ssl.c +++ b/src/ssl.c @@ -32167,15 +32167,34 @@ int wolfSSL_ASN1_TIME_check(const WOLFSSL_ASN1_TIME* a) return WOLFSSL_SUCCESS; } +/* + * From curl. + * time2epoch: time stamp to seconds since epoch in GMT time zone. Similar to + * mktime but for GMT only. + */ +static long long time2epoch(int sec, int min, int hour, int mday, int mon, + int year) +{ + static const int monthDaysCumulative [12] = { + 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 + }; + int leapDays = year - (mon <= 1); + leapDays = ((leapDays / 4) - (leapDays / 100) + (leapDays / 400) - + (1969 / 4) + (1969 / 100) - (1969 / 400)); + return ((((long long) (year - 1970) * 365 + leapDays + + monthDaysCumulative[mon] + mday - 1) * 24 + hour) * 60 + min) * 60 + + sec; +} + int wolfSSL_ASN1_TIME_diff(int *days, int *secs, const WOLFSSL_ASN1_TIME *from, const WOLFSSL_ASN1_TIME *to) { -#if defined(XMKTIME) && defined(XDIFFTIME) const int SECS_PER_DAY = 24 * 60 * 60; - struct tm fromTm_s, *fromTm = &fromTm_s; - struct tm toTm_s, *toTm = &toTm_s; - time_t fromSecs; - time_t toSecs; + struct tm fromTm_s, *fromTmGmt = &fromTm_s; + struct tm toTm_s, *toTmGmt = &toTm_s; + time_t currTime; + long long fromSecs; + long long toSecs; double diffSecs; struct tm *tmpTs; #if defined(NEED_TMP_TIME) @@ -32205,66 +32224,47 @@ int wolfSSL_ASN1_TIME_diff(int *days, int *secs, const WOLFSSL_ASN1_TIME *from, } if (from == NULL) { - fromSecs = wc_Time(0); - fromTm = XGMTIME(&fromSecs, tmpTs); - if (fromTm == NULL) { + currTime = wc_Time(0); + fromTmGmt = XGMTIME(&currTime, tmpTs); + if (fromTmGmt == NULL) { WOLFSSL_MSG("XGMTIME for from time failed."); return WOLFSSL_FAILURE; } } - else if (wolfSSL_ASN1_TIME_to_tm(from, fromTm) != WOLFSSL_SUCCESS) { + else if (wolfSSL_ASN1_TIME_to_tm(from, fromTmGmt) != WOLFSSL_SUCCESS) { WOLFSSL_MSG("Failed to convert from time to struct tm."); return WOLFSSL_FAILURE; } -#ifdef HAVE_ERRNO_H - errno = 0; -#endif - fromSecs = XMKTIME(fromTm); - /* Result can be negative due to time zones around UNIX epoch */ - if (fromSecs == -1 -#ifdef HAVE_ERRNO_H - /* Double check with errno that -1 is actually an error */ - && errno != 0 -#endif - ) { - WOLFSSL_MSG("XMKTIME for from time failed."); - return WOLFSSL_FAILURE; - } + /* We use time2epoch here instead of XMKTIME to avoid the Year 2038 problem + * on platforms where time_t is 32 bits. struct tm stores the year as years + * since 1900, so we add 1900 to the year. */ + fromSecs = time2epoch(fromTmGmt->tm_sec, fromTmGmt->tm_min, + fromTmGmt->tm_hour, fromTmGmt->tm_mday, + fromTmGmt->tm_mon, fromTmGmt->tm_year + 1900); if (to == NULL) { - toSecs = wc_Time(0); - toTm = XGMTIME(&toSecs, tmpTs); - if (toTm == NULL) { + currTime = wc_Time(0); + toTmGmt = XGMTIME(&currTime, tmpTs); + if (toTmGmt == NULL) { WOLFSSL_MSG("XGMTIME for to time failed."); return WOLFSSL_FAILURE; } } - else if (wolfSSL_ASN1_TIME_to_tm(to, toTm) != WOLFSSL_SUCCESS) { + else if (wolfSSL_ASN1_TIME_to_tm(to, toTmGmt) != WOLFSSL_SUCCESS) { WOLFSSL_MSG("Failed to convert to time to struct tm."); return WOLFSSL_FAILURE; } - toSecs = XMKTIME(toTm); - /* Result can be negative due to time zones around UNIX epoch */ - if (toSecs == -1 -#ifdef HAVE_ERRNO_H - /* Double check with errno that -1 is actually an error */ - && errno != 0 -#endif - ) { - WOLFSSL_MSG("XMKTIME for to time failed."); - return WOLFSSL_FAILURE; - } + toSecs = time2epoch(toTmGmt->tm_sec, toTmGmt->tm_min, toTmGmt->tm_hour, + toTmGmt->tm_mday, toTmGmt->tm_mon, + toTmGmt->tm_year + 1900); - diffSecs = XDIFFTIME(toSecs, fromSecs); + diffSecs = (double)(toSecs - fromSecs); *days = (int) (diffSecs / SECS_PER_DAY); *secs = (int) (diffSecs - (((double)*days) * SECS_PER_DAY)); return WOLFSSL_SUCCESS; -#else - return WOLFSSL_FAILURE; -#endif /* XMKTIME && XDIFFTIME */ } #endif /* !NO_ASN_TIME */ diff --git a/tests/api.c b/tests/api.c index d17e5b5ea..b2ff32a99 100644 --- a/tests/api.c +++ b/tests/api.c @@ -30768,11 +30768,18 @@ static void test_wolfSSL_ASN1_TIME_diff(void) /* Edge case with Unix epoch. */ AssertNotNull(ASN1_TIME_set_string(fromTime, "19700101000000Z")); AssertNotNull(ASN1_TIME_set_string(toTime, "19800101000000Z")); - AssertIntEQ(ASN1_TIME_diff(&daysDiff, &secsDiff, fromTime, toTime), WOLFSSL_SUCCESS); - + AssertIntEQ(ASN1_TIME_diff(&daysDiff, &secsDiff, fromTime, toTime), + WOLFSSL_SUCCESS); AssertIntEQ(daysDiff, 3652); AssertIntEQ(secsDiff, 0); + /* Edge case with year > 2038 (year 2038 problem). */ + AssertNotNull(ASN1_TIME_set_string(toTime, "99991231235959Z")); + AssertIntEQ(ASN1_TIME_diff(&daysDiff, &secsDiff, fromTime, toTime), + WOLFSSL_SUCCESS); + AssertIntEQ(daysDiff, 2932896); + AssertIntEQ(secsDiff, 86399); + ASN1_TIME_free(fromTime); ASN1_TIME_free(toTime);