From 68dd665d51c925a118cfced4583942b7157b59de Mon Sep 17 00:00:00 2001 From: Edwin Mons Date: Fri, 18 Jan 2019 16:25:58 +0100 Subject: Allow ownership transfer of certificates OpenSSL TLS contexts assume ownership of any additional certificate passed into it. The CertificateFactory now returns a vector of unique_ptrs, and OpenSSLContext will do the needful with releasing ownership at the right moment. A unit test has been added that uses a chained certificate in client/server context. Before the fix, this test would either fail, or result in a segmentation fault, depending on the mood of OpenSSL. Test-Information: Unit tests pass on Debian 9 Ran manual tests with server test code, tested both chained and single certificates, and no longer observed crashes when accepting a connection. Change-Id: I21814969e45c7d77e9a1af14f2c958c4c0311cd0 diff --git a/Swiften/TLS/CertificateFactory.cpp b/Swiften/TLS/CertificateFactory.cpp index 303bcf7..aaf27d9 100644 --- a/Swiften/TLS/CertificateFactory.cpp +++ b/Swiften/TLS/CertificateFactory.cpp @@ -23,9 +23,9 @@ namespace Swift { CertificateFactory::~CertificateFactory() { } -std::vector CertificateFactory::createCertificateChain(const ByteArray& /* data */) { +std::vector> CertificateFactory::createCertificateChain(const ByteArray& /* data */) { assert(false); - return std::vector(); + return std::vector>(); } PrivateKey::ref CertificateFactory::createPrivateKey(const SafeByteArray& data, boost::optional password) { diff --git a/Swiften/TLS/CertificateFactory.h b/Swiften/TLS/CertificateFactory.h index cb7fb6e..619031c 100644 --- a/Swiften/TLS/CertificateFactory.h +++ b/Swiften/TLS/CertificateFactory.h @@ -19,7 +19,7 @@ namespace Swift { virtual ~CertificateFactory(); virtual Certificate* createCertificateFromDER(const ByteArray& der) = 0; - virtual std::vector createCertificateChain(const ByteArray& data); + virtual std::vector> createCertificateChain(const ByteArray& data); PrivateKey::ref createPrivateKey(const SafeByteArray& data, boost::optional password = boost::optional()); }; } diff --git a/Swiften/TLS/OpenSSL/OpenSSLCertificateFactory.cpp b/Swiften/TLS/OpenSSL/OpenSSLCertificateFactory.cpp index c94702c..5eb626b 100644 --- a/Swiften/TLS/OpenSSL/OpenSSLCertificateFactory.cpp +++ b/Swiften/TLS/OpenSSL/OpenSSLCertificateFactory.cpp @@ -20,8 +20,8 @@ Certificate* OpenSSLCertificateFactory::createCertificateFromDER(const ByteArray return new OpenSSLCertificate(der); } -std::vector OpenSSLCertificateFactory::createCertificateChain(const ByteArray& data) { - std::vector certificateChain; +std::vector> OpenSSLCertificateFactory::createCertificateChain(const ByteArray& data) { + std::vector> certificateChain; if (data.size() > std::numeric_limits::max()) { return certificateChain; @@ -35,11 +35,11 @@ std::vector OpenSSLCertificateFactory::createCertificateChain( auto x509certFromPEM = PEM_read_bio_X509(bio.get(), &openSSLCert, nullptr, nullptr); if (x509certFromPEM && openSSLCert) { std::shared_ptr x509Cert(openSSLCert, X509_free); - certificateChain.push_back(std::make_shared(x509Cert)); + certificateChain.emplace_back(std::make_unique(x509Cert)); openSSLCert = nullptr; while ((x509certFromPEM = PEM_read_bio_X509(bio.get(), &openSSLCert, nullptr, nullptr)) != nullptr) { std::shared_ptr x509Cert(openSSLCert, X509_free); - certificateChain.push_back(std::make_shared(x509Cert)); + certificateChain.emplace_back(std::make_unique(x509Cert)); openSSLCert = nullptr; } } diff --git a/Swiften/TLS/OpenSSL/OpenSSLCertificateFactory.h b/Swiften/TLS/OpenSSL/OpenSSLCertificateFactory.h index af45a33..48e9b2c 100644 --- a/Swiften/TLS/OpenSSL/OpenSSLCertificateFactory.h +++ b/Swiften/TLS/OpenSSL/OpenSSLCertificateFactory.h @@ -16,6 +16,6 @@ namespace Swift { virtual ~OpenSSLCertificateFactory() override final; virtual Certificate* createCertificateFromDER(const ByteArray& der) override final; - virtual std::vector createCertificateChain(const ByteArray& data) override final; + virtual std::vector> createCertificateChain(const ByteArray& data) override final; }; } diff --git a/Swiften/TLS/OpenSSL/OpenSSLContext.cpp b/Swiften/TLS/OpenSSL/OpenSSLContext.cpp index 968ef8f..e9889bc 100644 --- a/Swiften/TLS/OpenSSL/OpenSSLContext.cpp +++ b/Swiften/TLS/OpenSSL/OpenSSLContext.cpp @@ -338,14 +338,14 @@ void OpenSSLContext::sendPendingDataToApplication() { } } -bool OpenSSLContext::setCertificateChain(const std::vector& certificateChain) { +bool OpenSSLContext::setCertificateChain(std::vector>&& certificateChain) { if (certificateChain.size() == 0) { SWIFT_LOG(warning) << "Trying to load empty certificate chain." << std::endl; return false; } // load endpoint certificate - auto openSSLCert = std::dynamic_pointer_cast(certificateChain[0]); + auto openSSLCert = dynamic_cast(certificateChain[0].get()); if (!openSSLCert) { return false; } @@ -355,8 +355,8 @@ bool OpenSSLContext::setCertificateChain(const std::vector& ce } if (certificateChain.size() > 1) { - for (auto certificate : range(certificateChain.begin() + 1, certificateChain.end())) { - auto openSSLCert = std::dynamic_pointer_cast(certificate); + for (auto certificate = certificateChain.begin() + 1; certificate != certificateChain.end(); ++certificate) { + auto openSSLCert = dynamic_cast(certificate->get()); if (!openSSLCert) { return false; } @@ -364,6 +364,7 @@ bool OpenSSLContext::setCertificateChain(const std::vector& ce SWIFT_LOG(warning) << "Trying to load empty certificate chain." << std::endl; return false; } + certificate->release(); } } diff --git a/Swiften/TLS/OpenSSL/OpenSSLContext.h b/Swiften/TLS/OpenSSL/OpenSSLContext.h index cfa852a..c18a6f4 100644 --- a/Swiften/TLS/OpenSSL/OpenSSLContext.h +++ b/Swiften/TLS/OpenSSL/OpenSSLContext.h @@ -45,7 +45,7 @@ namespace Swift { void connect() override final; void connect(const std::string& requestHostname) override final; - bool setCertificateChain(const std::vector& certificateChain) override final; + bool setCertificateChain(std::vector>&& certificateChain) override final; bool setPrivateKey(const PrivateKey::ref& privateKey) override final; bool setClientCertificate(CertificateWithKey::ref cert) override final; void setAbortTLSHandshake(bool abort) override final; diff --git a/Swiften/TLS/TLSContext.cpp b/Swiften/TLS/TLSContext.cpp index cc05834..666ea7f 100644 --- a/Swiften/TLS/TLSContext.cpp +++ b/Swiften/TLS/TLSContext.cpp @@ -21,7 +21,7 @@ void TLSContext::connect(const std::string& /* serverName */) { assert(false); } -bool TLSContext::setCertificateChain(const std::vector& /* certificateChain */) { +bool TLSContext::setCertificateChain(std::vector>&& /* certificateChain */) { assert(false); return false; } diff --git a/Swiften/TLS/TLSContext.h b/Swiften/TLS/TLSContext.h index 9b0a2eb..003069f 100644 --- a/Swiften/TLS/TLSContext.h +++ b/Swiften/TLS/TLSContext.h @@ -28,7 +28,7 @@ namespace Swift { virtual void connect() = 0; virtual void connect(const std::string& serverName); - virtual bool setCertificateChain(const std::vector& /* certificateChain */); + virtual bool setCertificateChain(std::vector>&& /* certificateChain */); virtual bool setPrivateKey(const PrivateKey::ref& /* privateKey */); virtual bool setClientCertificate(CertificateWithKey::ref cert) = 0; diff --git a/Swiften/TLS/UnitTest/CertificateTest.cpp b/Swiften/TLS/UnitTest/CertificateTest.cpp index aac2cfb..d5bf210 100644 --- a/Swiften/TLS/UnitTest/CertificateTest.cpp +++ b/Swiften/TLS/UnitTest/CertificateTest.cpp @@ -73,7 +73,7 @@ zqCzpwGL auto certificateChain = tlsFactories.getCertificateFactory()->createCertificateChain(createByteArray(PEMCertificate)); CPPUNIT_ASSERT_EQUAL(size_t(1), certificateChain.size()); - auto certificate = certificateChain[0]; + auto certificate = certificateChain[0].get(); CPPUNIT_ASSERT_EQUAL(std::string("/CN=montague.example"), certificate->getSubjectName()); } diff --git a/Swiften/TLS/UnitTest/ClientServerTest.cpp b/Swiften/TLS/UnitTest/ClientServerTest.cpp index e60364e..24bd7c5 100644 --- a/Swiften/TLS/UnitTest/ClientServerTest.cpp +++ b/Swiften/TLS/UnitTest/ClientServerTest.cpp @@ -84,7 +84,49 @@ S05kF7XRpludRB4QkAJt5BNNv6BPP7HPIKyR/rq94ONvzVPAo7uASyFE2sMBsfwP pXAI1LVolPCoUC13jEkKdmc8kMSxU+XtsvFryNhkfQtZfSg+nBRFYptFE7GrZ9WY GMSL4g== -----END CERTIFICATE----- -)"}}; +)"}, + {"casigned.example.com", +R"(-----BEGIN CERTIFICATE----- +MIIDYTCCAkmgAwIBAgIJAPEpYlUWlJW8MA0GCSqGSIb3DQEBCwUAMEAxCzAJBgNV +BAYTAkdCMRAwDgYDVQQIDAdFbmdsYW5kMQ4wDAYDVQQKDAVJc29kZTEPMA0GA1UE +AwwGVGVzdENBMB4XDTE4MDExODAwMDAwMFoXDTQ5MDExODAwMDAwMFowTjELMAkG +A1UEBhMCR0IxEDAOBgNVBAgMB0VuZ2xhbmQxDjAMBgNVBAoMBUlzb2RlMR0wGwYD +VQQDDBRjYXNpZ25lZC5leGFtcGxlLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEP +ADCCAQoCggEBAKw4iCscGIDCc55mwxgJsn/v2GEorpE9zHYsMfl6px2aRLGgB8ki +xYyNiEPA/b/ilShRp7pp22LPb5O3aOVVWzAs5FTvPdFY2vS8YIxR6XSKXO3u4Q8/ +w8CR0AbKpeCtL0TwSl1u76nq6ORMep6QGsTjDLQ7wUwdShL4qV4nIGxJWon/5GI0 +nl4Xr/wzmysbMjAMSiWOR90DNZMvqQBBhQcJdqdirc+whR8gOhIMvJBn5jlMuFEB +zL/bR+kG/zFuD0EMsMTy6TETefYuxeWmhuzQENyAV5+1v48G/1a9zzN9Y0xQ6T0N +ppbLzq+/zhxT1eBN8/O0bAGUzzk1VSFvouECAwEAAaNQME4wHQYDVR0OBBYEFCnR +umlxjdaUvcpf77zahrDmPnt1MB8GA1UdIwQYMBaAFAbbhPAKtD5YzxQqGH2Um4/f +OmdFMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBAEPrTTKS+7A4dJJh +gSAS44ZYyR8/kt9Y3V4qxJM2xrOrW6rM8N15n2i/osSy0AYHiPuvgDG+D7hLt5Ct +7jZKrp3JRsBaU8//9k6+euwVyWS+EKo/eb7qtSNibZOk7GwrXekJMbRXz1cHTKnW +0R8UG+EMkOqKQh5dhWuEBXff9SYLTALf0/i+/Ixl3b9rQ6zpY/7UwBdlWLaliM1i +odIgVu1XRE6Su+bGMa1L5ArniN0LMFq9Mxag2H0V7Kru7vIvbBkL7U6tbi7u26hv +q4kUFROW2U2C02FTmJeSEBvWoDyMoVjKkmaTL0+vDSQRPGtCsvCgaCXgOwU2Po9s +8jjHQxg= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIDUzCCAjugAwIBAgIJAPEpYlUWlJW7MA0GCSqGSIb3DQEBCwUAMEAxCzAJBgNV +BAYTAkdCMRAwDgYDVQQIDAdFbmdsYW5kMQ4wDAYDVQQKDAVJc29kZTEPMA0GA1UE +AwwGVGVzdENBMB4XDTE4MDExODAwMDAwMFoXDTQ5MDExODAwMDAwMFowQDELMAkG +A1UEBhMCR0IxEDAOBgNVBAgMB0VuZ2xhbmQxDjAMBgNVBAoMBUlzb2RlMQ8wDQYD +VQQDDAZUZXN0Q0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCiCA+9 +bdSHFNPOgbsnhrKbLJP+1lxWTe06+ExwJA3QgCKsR13MsAK8rhmuNB0k8ZIFJL+c +yUOUgw0370eP940QAtK+HinJpvaGEZKEwaB8VUCjzFTb7zUBio1Y7B45aawEV3qT +Lzcnil3f7vPIxnAoaptzPriySdggIsimj+y6AWum5FUPyfZzF7EnYfF/VH8V/ZNX +fku7PPbaeDHvu4EF/0s8P+/l6jddz8dqbDMjH3GDM8sjryWPQPNDjKZ1x80BIdyq +s9m/kXqIIySNwdIa/X+nYTMchUa5y46n2N4n0RA4F+rf+Ni1Cxk9Ejmmz5hinV+t +Yzh6jsbB7yZiqEnzAgMBAAGjUDBOMB0GA1UdDgQWBBQG24TwCrQ+WM8UKhh9lJuP +3zpnRTAfBgNVHSMEGDAWgBQG24TwCrQ+WM8UKhh9lJuP3zpnRTAMBgNVHRMEBTAD +AQH/MA0GCSqGSIb3DQEBCwUAA4IBAQBMHQ3WMEhkEc0VoOEw3mbsngt1eyJz+nFM +FeGdRb7JSwpJfe/yBxyGodGyHsQJPVole6sUdq8S1QGT547PkgDHc6Btoq96Mahq +brgfx1JL9a1F8qaqcENebHc7ltW8kps4xtdlithfo4nKaciYZ5mKbug+AFv569mb +LU3F282dyW1dLa7+L8dGbb8Ntpnp0BB0Cotm4GX3Xi6Y+fikouqcmXj7vX+uMhG4 +pQpXqb2ML1Gev4w6XUsnls+OJok97x99NsOcqtwqsLJS/sg0cpwrgZ9+50hQKlHO +8H6hhNjGBkvpIiNsWb9UOc+Id86J+drs9Ed93Eyyvwnq2XcqPGF3 +-----END CERTIFICATE-----)"}}; std::map privateKeyPEM = { {"montague.example", R"(-----BEGIN PRIVATE KEY----- @@ -192,6 +234,36 @@ mgPPV65cznhofUsg2QenT8zKisvYPYN3p3p9Jo6IqHyT/CCymwIB4OMZITiwXxQs PMAxlZGkX3Uri5A8Ln3QQ46elanI2TlC+ZDa84gu/Gw691JWCfsaSaQDTJKnGqos dwiNVl130YWaJLjiA9Poc2llKtypfQ8= -----END PRIVATE KEY----- +)"}, + {"casigned.example.com", +R"(-----BEGIN PRIVATE KEY----- +MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCsOIgrHBiAwnOe +ZsMYCbJ/79hhKK6RPcx2LDH5eqcdmkSxoAfJIsWMjYhDwP2/4pUoUae6adtiz2+T +t2jlVVswLORU7z3RWNr0vGCMUel0ilzt7uEPP8PAkdAGyqXgrS9E8Epdbu+p6ujk +THqekBrE4wy0O8FMHUoS+KleJyBsSVqJ/+RiNJ5eF6/8M5srGzIwDEoljkfdAzWT +L6kAQYUHCXanYq3PsIUfIDoSDLyQZ+Y5TLhRAcy/20fpBv8xbg9BDLDE8ukxE3n2 +LsXlpobs0BDcgFeftb+PBv9Wvc8zfWNMUOk9DaaWy86vv84cU9XgTfPztGwBlM85 +NVUhb6LhAgMBAAECggEAP5KesUstwFoHU/GbEsSBn7hpp3Pc/MBTOUwXJoCgL81C +jk8EkLrFMZ0NL/WNtcduR/PcdnOdCFyKU3zgu8dGYgIwRyoJJ74lFTJ0cVfLVbm9 +lBcZ/QcMu4kXwWoM0WMcj9YjnVnWHmMYlS2mFeIb/5HQnrdwIyisr1QbMSZAUbgn +HOLSjkd0KUcXxwP9HfE/8zUFbUsv/wPHE9T5eWqZz7lKZRvgUyk3jYElpewxqPvR +HPOq9BN87ft7rPzmmZ4JCU2tM5MEQONo3jT5pljTU/iz4pfs1HArHS+/zZbGcoiu +AhODErhLucodwZ07MZsE9KAdGPGUeob4SgMwmkIiAQKBgQDYZ33DHOgYd5Fa7Tqv +TC77Nbpjmnk5lyoInNf+7pxBTgqIuKbpS6Ctq76nHI0qerNfd44LqojQEJ9S5rbL +3s+HR+pypx3DyysE92uLaVsF4rfwZeEpJyQ1Q9NGbsKVb2nNrdGs3LZhG5PL+JQT +8lS9h/sMIbh5Gd/Dkt8a2HlTawKBgQDLu3YdXWeWUOcq63YrLFDO+RUGsCe1cf+2 +3wTmMRgxfzpAPDZKymUPwrJNd1ddCQiRVKg91pLhlDl8B89Q4iWdOpYnE6vrnw3w +6iEdEyA6IauvEvD3JPXHGtVHKBS/wz06wQVGzsgMvVun1w32SDzTGZ/somvkN1UE +BTMRVqXB4wKBgCgkgnq4GQG45FjGmtU7v5dP7yRc2lHpjZ2NYq/imU+v51pXTncG +VWcB7drX/AO+QRnbdpkJaFmYS8W4EH6e9UY1+/KKt4XDYVaJgwSYZY8g8FgCvhLB +NQFAdKejEitgQHMr5DI5uwcmc3MpyGYu1LNXzyT/+FVWjGCeFQ7IyzDDAoGAXK9I ++ez/bkQaFs2OMe/0R2TL6wIC3qohMpndSbf2ELaX0pdR/856EaR6p4FujlXIvw0r +iQ5wPg3H5SRMbbikchYwL1DkusnedsadD/QeijO9PmW4mEcJRW5wrvGOFvuEhJFB +jEgQfLM82muc33lXqpYSyn3N5cJCfBS9edrYiA8CgYEA0Tsa4KvwzmBXzy2jexUT +PPYtV9S7hS0D7Owf5SBI1wtWczOu3Y6DsCG4mbDi/tvo1dxC9d5Ns8RXHH9mtdyn +8VUuSu24lk7VZ0OP5/byiAGqC2urDFGe0iBqePdb+3uS1g132LzAJm4RqBYw4Xjd +P6n9b0V3iodcFLMTy1ShJ+I= +-----END PRIVATE KEY----- )"}}; auto montagueEncryptedPEM = R"(-----BEGIN RSA PRIVATE KEY----- @@ -568,6 +640,40 @@ TEST(ClientServerTest, testClientServerBasicCommunicationEncryptedPrivateKeyRigh })->second))); } +TEST(ClientServerTest, testClientServerBasicCommunicationWithChainedCert) { + auto clientContext = createTLSContext(TLSContext::Mode::Client); + auto serverContext = createTLSContext(TLSContext::Mode::Server); + + TLSClientServerEventHistory events(clientContext.get(), serverContext.get()); + + ClientServerConnector connector(clientContext.get(), serverContext.get()); + + auto tlsFactories = std::make_shared(); + + ASSERT_TRUE(serverContext->setCertificateChain(tlsFactories->getCertificateFactory()->createCertificateChain(createByteArray(certificatePEM["casigned.example.com"])))); + + auto privateKey = tlsFactories->getCertificateFactory()->createPrivateKey(createSafeByteArray(privateKeyPEM["casigned.example.com"])); + ASSERT_NE(nullptr, privateKey.get()); + ASSERT_TRUE(serverContext->setPrivateKey(privateKey)); + + serverContext->accept(); + clientContext->connect(); + + clientContext->handleDataFromApplication(createSafeByteArray("This is a test message from the client.")); + serverContext->handleDataFromApplication(createSafeByteArray("This is a test message from the server.")); + + auto serverEvent = std::find_if(events.events.begin(), events.events.end(), [](std::pair& event){ + return event.first == "server" && (event.second.type() == typeid(TLSDataForApplication)); + }); + ASSERT_NE(events.events.end(), serverEvent); + ASSERT_EQ(safeByteArrayToString(createSafeByteArray("This is a test message from the client.")), safeByteArrayToString(boost::apply_visitor(TLSEventToSafeByteArrayVisitor(), serverEvent->second))); + auto clientEvent = std::find_if(events.events.begin(), events.events.end(), [](std::pair& event){ + return event.first == "client" && (event.second.type() == typeid(TLSDataForApplication)); + }); + ASSERT_NE(events.events.end(), clientEvent); + ASSERT_EQ(safeByteArrayToString(createSafeByteArray("This is a test message from the server.")), safeByteArrayToString(boost::apply_visitor(TLSEventToSafeByteArrayVisitor(), clientEvent->second))); +} + TEST(ClientServerTest, testSettingPrivateKeyWithWrongPassword) { auto clientContext = createTLSContext(TLSContext::Mode::Client); auto serverContext = createTLSContext(TLSContext::Mode::Server); @@ -610,7 +716,7 @@ TEST(ClientServerTest, testClientServerSNIRequestedHostAvailable) { serverContext->onServerNameRequested.connect([&](const std::string& requestedName) { if (certificatePEM.find(requestedName) != certificatePEM.end() && privateKeyPEM.find(requestedName) != privateKeyPEM.end()) { auto certChain = tlsFactories->getCertificateFactory()->createCertificateChain(createByteArray(certificatePEM[requestedName])); - ASSERT_EQ(true, serverContext->setCertificateChain(certChain)); + ASSERT_EQ(true, serverContext->setCertificateChain(std::move(certChain))); auto privateKey = tlsFactories->getCertificateFactory()->createPrivateKey(createSafeByteArray(privateKeyPEM[requestedName])); ASSERT_NE(nullptr, privateKey.get()); -- cgit v0.10.2-6-g49f6