1 From D19172394-96028208 Mon Sep 17 00:00:00 2001
2 From: Alex Guzman <reanimus@fb.com>
3 Date: Fri, 20 Dec 2019 12:41:23 -0800
4 Subject: [PATCH] Make OpenSSLCertUtils::asnTimeToTimepoint compatible with OpenSSL <1.0.2
6 Summary: Adds an implementation that reads the struct internals and parses them into a tm struct rather than use ASN1_TIME_diff, which was added in 1.0.2.
8 Differential Revision: D19172394
11 diff --git a/third-party/folly/src/folly/ssl/OpenSSLCertUtils.cpp b/third-party/folly/src/folly/ssl/OpenSSLCertUtils.cpp
12 --- a/third-party/folly/src/folly/ssl/OpenSSLCertUtils.cpp
13 +++ b/third-party/folly/src/folly/ssl/OpenSSLCertUtils.cpp
15 #include <folly/FileUtil.h>
16 #include <folly/ScopeGuard.h>
17 #include <folly/String.h>
18 +#include <folly/portability/Time.h>
19 #include <folly/ssl/OpenSSLPtrTypes.h>
24 std::chrono::system_clock::time_point OpenSSLCertUtils::asnTimeToTimepoint(
25 const ASN1_TIME* asnTime) {
26 + // Handle 1.1.0 taking const but all below not
27 + using MaybeConstAsnTime = typename std::
28 + conditional<FOLLY_OPENSSL_IS_110, const ASN1_TIME, ASN1_TIME>::type;
29 + auto maybeConstAsnTime = const_cast<MaybeConstAsnTime*>(asnTime);
30 + if (ASN1_TIME_check(maybeConstAsnTime) != 1) {
31 + throw std::runtime_error("ASN.1 time failed verification.");
33 +#if OPENSSL_VERSION_NUMBER < 0x1000200fL
34 + // Pre-1.0.2: Parse internal struct and convert to tm
35 + // ASN1 time is stored in string format as it is in the ASN.1, so
36 + // parse it depending on the type.
37 + auto timeStr = reinterpret_cast<const char*>(asnTime->data);
39 + throw std::runtime_error("Failed to get ASN1_TIME data");
42 + // Helper lambda to parse string numbers of arbitrary length <32
43 + auto getNum = [](const char*& p, size_t digits) mutable {
44 + unsigned int ret = 0;
45 + for (size_t i = 0; i < digits; i++) {
47 + ret += *(p++) - '0';
53 + // tm.year = years since 1900
54 + if (asnTime->type == V_ASN1_UTCTIME) {
56 + tm.tm_year = getNum(timeStr, 2);
57 + if (tm.tm_year < 50) {
60 + } else if (asnTime->type == V_ASN1_GENERALIZEDTIME) {
61 + // Check for date representing a not after date of undefined validity
62 + // (See RFC 5280 Section 4.1.2.5)
63 + // In that case, return the max date.
64 + const size_t cmpLen = std::min(asnTime->length, 15);
65 + if (strncmp(timeStr, "99991231235959Z", cmpLen) == 0) {
66 + return std::chrono::system_clock::time_point::max();
68 + tm.tm_year = getNum(timeStr, 4) - 1900;
70 + throw std::runtime_error("Unknown ASN.1 time type");
72 + tm.tm_mon = getNum(timeStr, 2) - 1; // (months since jan)
73 + tm.tm_mday = getNum(timeStr, 2);
74 + tm.tm_hour = getNum(timeStr, 2);
75 + tm.tm_min = getNum(timeStr, 2);
76 + tm.tm_sec = getNum(timeStr, 2);
77 + return std::chrono::system_clock::from_time_t(timegm(&tm));
79 + // 1.0.2+: Use ASN1_TIME_diff
85 return std::chrono::system_clock::time_point(
86 std::chrono::seconds(dSecs) + std::chrono::hours(24 * dDays));
90 std::string OpenSSLCertUtils::getDateTimeStr(const ASN1_TIME* time) {