Skip to content

Commit a8ac9e4

Browse files
committed
tlsutility: normalize cert valid for ts on 32-bit systems
1 parent ec26a2d commit a8ac9e4

File tree

3 files changed

+37
-32
lines changed

3 files changed

+37
-32
lines changed

lib/base/tlsutility.cpp

Lines changed: 29 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -478,7 +478,7 @@ std::shared_ptr<X509> GetX509Certificate(const String& pemfile)
478478
return std::shared_ptr<X509>(cert, X509_free);
479479
}
480480

481-
int MakeX509CSR(const String& cn, const String& keyfile, const String& csrfile, const String& certfile, int validFor, bool ca)
481+
int MakeX509CSR(const String& cn, const String& keyfile, const String& csrfile, const String& certfile, long validFor, bool ca)
482482
{
483483
char errbuf[256];
484484

@@ -640,7 +640,7 @@ int MakeX509CSR(const String& cn, const String& keyfile, const String& csrfile,
640640
return 1;
641641
}
642642

643-
std::shared_ptr<X509> CreateCert(EVP_PKEY *pubkey, X509_NAME *subject, X509_NAME *issuer, EVP_PKEY *cakey, int validFor, bool ca)
643+
std::shared_ptr<X509> CreateCert(EVP_PKEY *pubkey, X509_NAME *subject, X509_NAME *issuer, EVP_PKEY *cakey, long validFor, bool ca)
644644
{
645645
X509 *cert = X509_new();
646646
X509_set_version(cert, 2);
@@ -649,7 +649,7 @@ std::shared_ptr<X509> CreateCert(EVP_PKEY *pubkey, X509_NAME *subject, X509_NAME
649649
notBefore = validFor - validFor / 4; // Add 25% of the validity period to the past
650650
}
651651
X509_gmtime_adj(X509_get_notBefore(cert), notBefore);
652-
X509_gmtime_adj(X509_get_notAfter(cert), validFor);
652+
X509_gmtime_adj(X509_get_notAfter(cert), NormalizeCertValidFor(validFor));
653653
X509_set_pubkey(cert, pubkey);
654654

655655
X509_set_subject_name(cert, subject);
@@ -732,7 +732,7 @@ String GetIcingaCADir()
732732
return Configuration::DataDir + "/ca";
733733
}
734734

735-
std::shared_ptr<X509> CreateCertIcingaCA(EVP_PKEY *pubkey, X509_NAME *subject, int validFor, bool ca)
735+
std::shared_ptr<X509> CreateCertIcingaCA(EVP_PKEY *pubkey, X509_NAME *subject, long validFor, bool ca)
736736
{
737737
char errbuf[256];
738738

@@ -779,7 +779,7 @@ std::shared_ptr<X509> CreateCertIcingaCA(EVP_PKEY *pubkey, X509_NAME *subject, i
779779
* @param validFor The validity period in seconds. Defaults to LEAF_VALID_FOR.
780780
* @returns The new X509 certificate or an empty shared_ptr on error.
781781
*/
782-
std::shared_ptr<X509> CreateCertIcingaCA(const std::shared_ptr<X509>& cert, int validFor)
782+
std::shared_ptr<X509> CreateCertIcingaCA(const std::shared_ptr<X509>& cert, long validFor)
783783
{
784784
std::shared_ptr<EVP_PKEY> pkey = std::shared_ptr<EVP_PKEY>(X509_get_pubkey(cert.get()), EVP_PKEY_free);
785785
return CreateCertIcingaCA(pkey.get(), X509_get_subject_name(cert.get()), validFor);
@@ -812,6 +812,30 @@ bool IsCaUptodate(X509* cert)
812812
return !CertExpiresWithin(cert, LEAF_VALID_FOR);
813813
}
814814

815+
/**
816+
* Normalizes the specified certificate validity period in seconds to avoid
817+
* issues with 32-bit time_t wrap-around in 2038.
818+
*
819+
* On 64-bit systems this function simply returns the specified value. On 32-bit systems it ensures that
820+
* the current time plus the specified value never overflows the maximum value of time_t. Otherwise, the
821+
* returned value is adjusted so that the sum is exactly the maximum value of time_t.
822+
*
823+
* @param seconds The desired validity period in seconds.
824+
* @returns The normalized validity period in seconds.
825+
*/
826+
long NormalizeCertValidFor(long seconds)
827+
{
828+
if constexpr (sizeof(time_t) > sizeof(int32_t)) {
829+
return seconds;
830+
}
831+
832+
constexpr time_t maxTimeT = static_cast<time_t>(std::numeric_limits<int32_t>::max());
833+
if (auto rem = maxTimeT - time(nullptr); rem < seconds) {
834+
return rem;
835+
}
836+
return seconds;
837+
}
838+
815839
String CertificateToString(X509* cert)
816840
{
817841
BIO *mem = BIO_new(BIO_s_mem());

lib/base/tlsutility.hpp

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -54,8 +54,8 @@ Shared<boost::asio::ssl::context>::Ptr SetupSslContext(String certPath, String k
5454

5555
String GetCertificateCN(const std::shared_ptr<X509>& certificate);
5656
std::shared_ptr<X509> GetX509Certificate(const String& pemfile);
57-
int MakeX509CSR(const String& cn, const String& keyfile, const String& csrfile = String(), const String& certfile = String(), int validFor = LEAF_VALID_FOR, bool ca = false);
58-
std::shared_ptr<X509> CreateCert(EVP_PKEY *pubkey, X509_NAME *subject, X509_NAME *issuer, EVP_PKEY *cakey, int validFor, bool ca);
57+
int MakeX509CSR(const String& cn, const String& keyfile, const String& csrfile = String(), const String& certfile = String(), long validFor = LEAF_VALID_FOR, bool ca = false);
58+
std::shared_ptr<X509> CreateCert(EVP_PKEY *pubkey, X509_NAME *subject, X509_NAME *issuer, EVP_PKEY *cakey, long validFor, bool ca);
5959

6060
String GetIcingaCADir();
6161
String CertificateToString(X509* cert);
@@ -66,10 +66,11 @@ inline String CertificateToString(const std::shared_ptr<X509>& cert)
6666
}
6767

6868
std::shared_ptr<X509> StringToCertificate(const String& cert);
69-
std::shared_ptr<X509> CreateCertIcingaCA(EVP_PKEY *pubkey, X509_NAME *subject, int validFor, bool ca = false);
70-
std::shared_ptr<X509> CreateCertIcingaCA(const std::shared_ptr<X509>& cert, int validFor = LEAF_VALID_FOR);
69+
std::shared_ptr<X509> CreateCertIcingaCA(EVP_PKEY *pubkey, X509_NAME *subject, long validFor, bool ca = false);
70+
std::shared_ptr<X509> CreateCertIcingaCA(const std::shared_ptr<X509>& cert, long validFor = LEAF_VALID_FOR);
7171
bool IsCertUptodate(const std::shared_ptr<X509>& cert);
7272
bool IsCaUptodate(X509* cert);
73+
long NormalizeCertValidFor(long seconds);
7374

7475
String PBKDF2_SHA1(const String& password, const String& salt, int iterations);
7576
String PBKDF2_SHA256(const String& password, const String& salt, int iterations);

test/base-tlsutility.cpp

Lines changed: 3 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -70,26 +70,6 @@ static std::shared_ptr<EVP_PKEY> GetEVP_PKEY(const String& keyfile)
7070
return std::shared_ptr<EVP_PKEY>(pkey, EVP_PKEY_free);
7171
}
7272

73-
/**
74-
* Returns a time_t value that is the current time plus the specified number of seconds,
75-
* but capped to max time_t on 32-bit systems to avoid the 2038 problem.
76-
*
77-
* @param timestamp The number of seconds to add to the current time.
78-
* @return The resulting time_t value, capped to max time_t on 32-bit systems.
79-
*/
80-
time_t ValidCertValidity(long timestamp)
81-
{
82-
time_t t = time(nullptr);
83-
if constexpr (sizeof(t) > sizeof(int32_t)) {
84-
t = t + timestamp;
85-
} else {
86-
// On 32-bit systems time_t is 32-bit and thus limited to 2038.
87-
// So, make sure t+timestamp is never greater than max time_t.
88-
t = std::min(t + timestamp, std::numeric_limits<time_t>::max());
89-
}
90-
return t;
91-
}
92-
9373
/**
9474
* Creates a new X509 certificate signed by the Icinga CA based on an existing CA certificate.
9575
*
@@ -118,7 +98,7 @@ BOOST_FIXTURE_TEST_CASE(create_verify_ca, CertificateFixture)
11898
BOOST_CHECK_EQUAL(true, VerifyCertificate(cacert, cacert, String())); // Self-signed CA!
11999
BOOST_CHECK_EQUAL(true, IsCaUptodate(cacert.get())); // Is CA up-to-date after its creation?
120100

121-
auto caValidUntil = ValidCertValidity(ROOT_VALID_FOR);
101+
auto caValidUntil = NormalizeCertValidFor(ROOT_VALID_FOR);
122102
// Due to processing time the expiration date might be off by a few seconds,
123103
// so we just check whether it is less than the expected value and not exactly equal.
124104
BOOST_CHECK_EQUAL(-1, X509_cmp_time(X509_get_notAfter(cacert.get()), &caValidUntil));
@@ -158,7 +138,7 @@ BOOST_FIXTURE_TEST_CASE(create_verify_leaf_certs, CertificateFixture)
158138
BOOST_CHECK_EQUAL(true, IsCertUptodate(cert)); // Is leaf up-to-date after its creation?
159139
BOOST_CHECK_EQUAL(true, VerifyCertificate(cacert, cert, String())); // Signed by our CA?
160140

161-
auto certValidUntil = ValidCertValidity(LEAF_VALID_FOR);
141+
auto certValidUntil = NormalizeCertValidFor(LEAF_VALID_FOR);
162142
// Due to processing time the expiration date might be off by a few seconds,
163143
// so we just check whether it is less than the expected value and not exactly equal.
164144
BOOST_CHECK_EQUAL(-1, X509_cmp_time(X509_get_notAfter(cert.get()), &certValidUntil));
@@ -170,7 +150,7 @@ BOOST_FIXTURE_TEST_CASE(create_verify_leaf_certs, CertificateFixture)
170150
// Check whether expired certificates are correctly detected and verification fails.
171151
PkiUtility::SignCsr(certInfo.csrFile, certInfo.crtFile, 60*60*24*-10); // Expire 10 days ago!
172152
cert = GetX509Certificate(certInfo.crtFile);
173-
certValidUntil = ValidCertValidity(60*60*24*-10);
153+
certValidUntil = NormalizeCertValidFor(60*60*24*-10);
174154
BOOST_CHECK_EQUAL(-1, X509_cmp_time(X509_get_notAfter(cert.get()), &certValidUntil)); // Is certificate indeed expired?
175155
BOOST_CHECK_EQUAL(false, IsCertUptodate(cert)); // It's already expired, so definitely not up-to-date.
176156
BOOST_CHECK_THROW(VerifyCertificate(cacert, cert, String()), openssl_error);

0 commit comments

Comments
 (0)