summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorShreyas Kalyan <shreyas.kalyan@10gen.com>2020-01-08 16:23:22 -0500
committerEvergreen Agent <no-reply@evergreen.mongodb.com>2020-02-04 23:24:42 +0000
commit6a79b0331f5c8d962315220ac4a051250925d1df (patch)
tree15ad746a5fa36cbb8be7df34471d452d43df96f7
parentb3c4e02b7454a3b496e038332fb2dd18e905359d (diff)
downloadmongo-6a79b0331f5c8d962315220ac4a051250925d1df.tar.gz
SERVER-44905 Futurize the retrieval of OCSP Responses
-rw-r--r--jstests/ocsp/ocsp_basic.js8
-rw-r--r--jstests/ocsp/ocsp_stapling.js8
-rw-r--r--src/mongo/base/error_codes.yml1
-rw-r--r--src/mongo/db/db.cpp3
-rw-r--r--src/mongo/s/server.cpp3
-rw-r--r--src/mongo/shell/dbshell.cpp5
-rw-r--r--src/mongo/transport/session_asio.h25
-rw-r--r--src/mongo/util/net/SConscript2
-rw-r--r--src/mongo/util/net/http_client_curl.cpp10
-rw-r--r--src/mongo/util/net/ocsp/ocsp_manager.cpp84
-rw-r--r--src/mongo/util/net/ocsp/ocsp_manager.h26
-rw-r--r--src/mongo/util/net/ssl_manager.h2
-rw-r--r--src/mongo/util/net/ssl_manager_apple.cpp26
-rw-r--r--src/mongo/util/net/ssl_manager_openssl.cpp251
-rw-r--r--src/mongo/util/net/ssl_manager_windows.cpp36
15 files changed, 321 insertions, 169 deletions
diff --git a/jstests/ocsp/ocsp_basic.js b/jstests/ocsp/ocsp_basic.js
index 2253429c105..f3b946e235b 100644
--- a/jstests/ocsp/ocsp_basic.js
+++ b/jstests/ocsp/ocsp_basic.js
@@ -39,6 +39,12 @@ assert.throws(() => {
new Mongo(conn.host);
});
-mock_ocsp.stop();
MongoRunner.stopMongod(conn);
+
+// The mongoRunner spawns a shell to validate the collections which races
+// with the shutdown logic of the mock_ocsp responder on some platforms.
+// We need this sleep to make sure that the threads don't interfere with
+// each other.
+sleep(1000);
+mock_ocsp.stop();
}()); \ No newline at end of file
diff --git a/jstests/ocsp/ocsp_stapling.js b/jstests/ocsp/ocsp_stapling.js
index 8486894fa90..207c873d11a 100644
--- a/jstests/ocsp/ocsp_stapling.js
+++ b/jstests/ocsp/ocsp_stapling.js
@@ -70,6 +70,12 @@ assert.throws(() => {
new Mongo(conn.host);
});
-mock_ocsp.stop();
MongoRunner.stopMongod(conn);
+
+// The mongoRunner spawns a shell to validate the collections which races
+// with the shutdown logic of the mock_ocsp responder on some platforms.
+// We need this sleep to make sure that the threads don't interfere with
+// each other.
+sleep(1000);
+mock_ocsp.stop();
}()); \ No newline at end of file
diff --git a/src/mongo/base/error_codes.yml b/src/mongo/base/error_codes.yml
index 7644749e989..4938c9a3f99 100644
--- a/src/mongo/base/error_codes.yml
+++ b/src/mongo/base/error_codes.yml
@@ -336,6 +336,7 @@ error_codes:
- {code: 299,name: OCSPCertificateStatusRevoked}
- {code: 300,name: RangeDeletionAbandonedBecauseCollectionWithUUIDDoesNotExist}
- {code: 301,name: DataCorruptionDetected}
+ - {code: 302,name: OCSPCertificateStatusUnknown}
# Error codes 4000-8999 are reserved.
diff --git a/src/mongo/db/db.cpp b/src/mongo/db/db.cpp
index 97f2580e389..14bed1ae035 100644
--- a/src/mongo/db/db.cpp
+++ b/src/mongo/db/db.cpp
@@ -170,6 +170,7 @@
#include "mongo/util/fast_clock_source_factory.h"
#include "mongo/util/latch_analyzer.h"
#include "mongo/util/log.h"
+#include "mongo/util/net/ocsp/ocsp_manager.h"
#include "mongo/util/net/socket_utils.h"
#include "mongo/util/net/ssl_manager.h"
#include "mongo/util/ntservice.h"
@@ -314,6 +315,8 @@ ExitCode _initAndListen(int listenPort) {
VersionInfoInterface::instance().logTargetMinOS();
#endif
+ OCSPManager::get()->startThreadPool();
+
logProcessDetails();
serviceContext->setServiceEntryPoint(std::make_unique<ServiceEntryPointMongod>(serviceContext));
diff --git a/src/mongo/s/server.cpp b/src/mongo/s/server.cpp
index faa5ccf183b..84f9a779fb0 100644
--- a/src/mongo/s/server.cpp
+++ b/src/mongo/s/server.cpp
@@ -104,6 +104,7 @@
#include "mongo/util/fast_clock_source_factory.h"
#include "mongo/util/latch_analyzer.h"
#include "mongo/util/log.h"
+#include "mongo/util/net/ocsp/ocsp_manager.h"
#include "mongo/util/net/socket_exception.h"
#include "mongo/util/net/socket_utils.h"
#include "mongo/util/net/ssl_manager.h"
@@ -511,6 +512,8 @@ ExitCode runMongosServer(ServiceContext* serviceContext) {
initWireSpec();
+ OCSPManager::get()->startThreadPool();
+
serviceContext->setServiceEntryPoint(std::make_unique<ServiceEntryPointMongos>(serviceContext));
auto tl =
diff --git a/src/mongo/shell/dbshell.cpp b/src/mongo/shell/dbshell.cpp
index a106a040a26..88699025c56 100644
--- a/src/mongo/shell/dbshell.cpp
+++ b/src/mongo/shell/dbshell.cpp
@@ -78,6 +78,7 @@
#include "mongo/util/exit.h"
#include "mongo/util/file.h"
#include "mongo/util/log.h"
+#include "mongo/util/net/ocsp/ocsp_manager.h"
#include "mongo/util/net/ssl_options.h"
#include "mongo/util/password.h"
#include "mongo/util/quick_exit.h"
@@ -752,6 +753,9 @@ int _main(int argc, char* argv[], char** envp) {
setGlobalServiceContext(ServiceContext::make());
// TODO This should use a TransportLayerManager or TransportLayerFactory
auto serviceContext = getGlobalServiceContext();
+
+ OCSPManager::get()->startThreadPool();
+
transport::TransportLayerASIO::Options opts;
opts.enableIPv6 = shellGlobalParams.enableIPv6;
opts.mode = transport::TransportLayerASIO::Options::kEgress;
@@ -804,7 +808,6 @@ int _main(int argc, char* argv[], char** envp) {
boost::log::core::get()->add_sink(std::move(consoleSink));
}
-
// Get the URL passed to the shell
std::string& cmdlineURI = shellGlobalParams.url;
diff --git a/src/mongo/transport/session_asio.h b/src/mongo/transport/session_asio.h
index f8c43554c07..687b1022662 100644
--- a/src/mongo/transport/session_asio.h
+++ b/src/mongo/transport/session_asio.h
@@ -258,9 +258,12 @@ protected:
return doHandshake().then([this, target] {
_ranHandshake = true;
- SSLPeerInfo::forSession(shared_from_this()) =
- uassertStatusOK(getSSLManager()->parseAndValidatePeerCertificate(
- _sslSocket->native_handle(), _sslSocket->get_sni(), target.host(), target));
+ return getSSLManager()
+ ->parseAndValidatePeerCertificate(
+ _sslSocket->native_handle(), _sslSocket->get_sni(), target.host(), target)
+ .then([this](SSLPeerInfo info) {
+ SSLPeerInfo::forSession(shared_from_this()) = info;
+ });
});
}
@@ -634,13 +637,17 @@ private:
}
};
return doHandshake().then([this](size_t size) {
- auto& sslPeerInfo = SSLPeerInfo::forSession(shared_from_this());
-
- if (sslPeerInfo.subjectName.empty()) {
- sslPeerInfo = uassertStatusOK(getSSLManager()->parseAndValidatePeerCertificate(
- _sslSocket->native_handle(), _sslSocket->get_sni(), "", _remote));
+ if (SSLPeerInfo::forSession(shared_from_this()).subjectName.empty()) {
+ return getSSLManager()
+ ->parseAndValidatePeerCertificate(
+ _sslSocket->native_handle(), _sslSocket->get_sni(), "", _remote)
+ .then([this](SSLPeerInfo info) -> bool {
+ SSLPeerInfo::forSession(shared_from_this()) = info;
+ return true;
+ });
}
- return true;
+
+ return Future<bool>::makeReady(true);
});
} else if (_tl->_sslMode() == SSLParams::SSLMode_requireSSL) {
uasserted(ErrorCodes::SSLHandshakeFailed,
diff --git a/src/mongo/util/net/SConscript b/src/mongo/util/net/SConscript
index c06b8cb0225..0f545358feb 100644
--- a/src/mongo/util/net/SConscript
+++ b/src/mongo/util/net/SConscript
@@ -121,6 +121,8 @@ if not get_option('ssl') == 'off':
],
LIBDEPS=[
'$BUILD_DIR/mongo/base',
+ '$BUILD_DIR/mongo/db/service_context',
+ '$BUILD_DIR/mongo/util/concurrency/thread_pool',
],
LIBDEPS_PRIVATE=[
'http_client',
diff --git a/src/mongo/util/net/http_client_curl.cpp b/src/mongo/util/net/http_client_curl.cpp
index 20ca65442b3..bddb5fb910f 100644
--- a/src/mongo/util/net/http_client_curl.cpp
+++ b/src/mongo/util/net/http_client_curl.cpp
@@ -131,17 +131,21 @@ private:
}
static void _lockShare(CURL*, curl_lock_data, curl_lock_access, void* ctx) {
- reinterpret_cast<Mutex*>(ctx)->lock();
+ reinterpret_cast<stdx::recursive_mutex*>(ctx)->lock();
}
static void _unlockShare(CURL*, curl_lock_data, void* ctx) {
- reinterpret_cast<Mutex*>(ctx)->unlock();
+ reinterpret_cast<stdx::recursive_mutex*>(ctx)->unlock();
}
private:
bool _initialized = false;
CURLSH* _share = nullptr;
- Mutex _shareMutex = MONGO_MAKE_LATCH("CurlLibraryManager::_shareMutex");
+
+ // A recursive mutex here is needed because CURL needs to lock this multiple times depending
+ // on the "internal CURL type" of the object that CURL is sending. Using a normal mutex
+ // causes the CURL system to deadlock.
+ stdx::recursive_mutex _shareMutex{};
} curlLibraryManager;
/**
diff --git a/src/mongo/util/net/ocsp/ocsp_manager.cpp b/src/mongo/util/net/ocsp/ocsp_manager.cpp
index 1b3bf98115e..11ebf085bde 100644
--- a/src/mongo/util/net/ocsp/ocsp_manager.cpp
+++ b/src/mongo/util/net/ocsp/ocsp_manager.cpp
@@ -29,27 +29,83 @@
#include "mongo/platform/basic.h"
-#include "mongo/util/net/http_client.h"
+#include "mongo/db/client.h"
+#include "mongo/executor/network_interface_factory.h"
#include "mongo/util/net/ocsp/ocsp_manager.h"
namespace mongo {
-StatusWith<std::vector<uint8_t>> ocspRequestStatus(ConstDataRange data, StringData responderURI) {
- auto client = HttpClient::create();
- if (!client) {
- return Status(ErrorCodes::InternalErrorNotSupported, "HTTP Client not supported");
+namespace {
+
+auto makeTaskExecutor() {
+ ThreadPool::Options tpOptions;
+ tpOptions.poolName = "OCSPManagerHTTP";
+ tpOptions.maxThreads = 10;
+ tpOptions.onCreateThread = [](const std::string& threadName) {
+ Client::initThread(threadName.c_str());
+ };
+ return std::make_unique<ThreadPool>(tpOptions);
+}
+
+} // namespace
+
+OCSPManager::OCSPManager() {
+ _pool = makeTaskExecutor();
+
+ _client = HttpClient::create();
+ if (!_client) {
+ return;
+ }
+
+ _client->allowInsecureHTTP(true);
+ _client->setTimeout(kOCSPRequestTimeoutSeconds);
+ _client->setHeaders({"Content-Type: application/ocsp-request"});
+}
+
+void OCSPManager::startThreadPool() {
+ if (_pool) {
+ _pool->startup();
}
- client->allowInsecureHTTP(true);
- client->setTimeout(kOCSPRequestTimeoutSeconds);
- client->setHeaders({"Content-Type: application/ocsp-request"});
- auto dataBuilder = client->post("http://" + responderURI, data);
- if (dataBuilder.size() == 0) {
- return Status(ErrorCodes::SSLHandshakeFailed, "OCSP Validation Failed");
+}
+
+/**
+ * Constructs the HTTP client and sends the OCSP request to the responder.
+ * Returns a vector of bytes to be constructed into a OCSP response.
+ */
+Future<std::vector<uint8_t>> OCSPManager::requestStatus(std::vector<uint8_t> data,
+ StringData responderURI) {
+ if (!this->_client) {
+ return Future<std::vector<uint8_t>>::makeReady(
+ Status(ErrorCodes::InternalErrorNotSupported, "HTTP Client not supported"));
}
- auto blobSize = dataBuilder.size();
- auto blobData = dataBuilder.release();
- return std::vector<uint8_t>(blobData.get(), blobData.get() + blobSize);
+ auto pf = makePromiseFuture<DataBuilder>();
+ std::string uri("http://" + responderURI);
+
+ _pool->schedule(
+ [this, promise = std::move(pf.promise), uri = std::move(uri), data = std::move(data)](
+ auto status) mutable {
+ if (!status.isOK()) {
+ return;
+ }
+ try {
+ auto result = this->_client->post(uri, data);
+ promise.emplaceValue(std::move(result));
+ } catch (...) {
+ promise.setError(exceptionToStatus());
+ }
+ });
+
+ return std::move(pf.future).then(
+ [](DataBuilder dataBuilder) mutable -> Future<std::vector<uint8_t>> {
+ if (dataBuilder.size() == 0) {
+ return Status(ErrorCodes::SSLHandshakeFailed, "Failed to acquire OCSP Response.");
+ }
+
+ auto blobSize = dataBuilder.size();
+ auto blobData = dataBuilder.release();
+ return std::vector<uint8_t>(blobData.get(), blobData.get() + blobSize);
+ });
}
} // namespace mongo \ No newline at end of file
diff --git a/src/mongo/util/net/ocsp/ocsp_manager.h b/src/mongo/util/net/ocsp/ocsp_manager.h
index 5d2a3d1a9a5..e392b5e0a12 100644
--- a/src/mongo/util/net/ocsp/ocsp_manager.h
+++ b/src/mongo/util/net/ocsp/ocsp_manager.h
@@ -30,17 +30,33 @@
#include "mongo/base/data_range.h"
+#include "mongo/util/concurrency/thread_pool.h"
#include "mongo/util/duration.h"
+#include "mongo/util/future.h"
#include "mongo/util/net/hostandport.h"
+#include "mongo/util/net/http_client.h"
namespace mongo {
constexpr Seconds kOCSPRequestTimeoutSeconds(5);
+class OCSPManager {
-/**
- * Constructs the HTTP client and sends the OCSP request to the responder.
- * Returns a vector of bytes to be constructed into a OCSP response.
- */
-StatusWith<std::vector<uint8_t>> ocspRequestStatus(ConstDataRange data, StringData responderURI);
+public:
+ OCSPManager();
+
+ static OCSPManager* get() {
+ static OCSPManager manager = OCSPManager();
+
+ return &manager;
+ };
+
+ Future<std::vector<uint8_t>> requestStatus(std::vector<uint8_t> data, StringData responderURI);
+
+ void startThreadPool();
+
+private:
+ std::unique_ptr<HttpClient> _client;
+ std::unique_ptr<ThreadPool> _pool;
+};
} // namespace mongo \ No newline at end of file
diff --git a/src/mongo/util/net/ssl_manager.h b/src/mongo/util/net/ssl_manager.h
index 8da19482cbb..499cdd85b7e 100644
--- a/src/mongo/util/net/ssl_manager.h
+++ b/src/mongo/util/net/ssl_manager.h
@@ -259,7 +259,7 @@ public:
* X509 authorization will be returned in `roles`.
* Further, the SNI Name will be captured into the `sni` value, when available.
*/
- virtual StatusWith<SSLPeerInfo> parseAndValidatePeerCertificate(
+ virtual Future<SSLPeerInfo> parseAndValidatePeerCertificate(
SSLConnectionType ssl,
boost::optional<std::string> sni,
const std::string& remoteHost,
diff --git a/src/mongo/util/net/ssl_manager_apple.cpp b/src/mongo/util/net/ssl_manager_apple.cpp
index eda69e8b9a7..970db6c9d3b 100644
--- a/src/mongo/util/net/ssl_manager_apple.cpp
+++ b/src/mongo/util/net/ssl_manager_apple.cpp
@@ -1196,11 +1196,10 @@ public:
const std::string& remoteHost,
const HostAndPort& hostForLogging) final;
- StatusWith<SSLPeerInfo> parseAndValidatePeerCertificate(
- ::SSLContextRef conn,
- boost::optional<std::string> sniName,
- const std::string& remoteHost,
- const HostAndPort& hostForLogging) final;
+ Future<SSLPeerInfo> parseAndValidatePeerCertificate(::SSLContextRef conn,
+ boost::optional<std::string> sniName,
+ const std::string& remoteHost,
+ const HostAndPort& hostForLogging) final;
const SSLConfiguration& getSSLConfiguration() const final {
return _sslConfiguration;
@@ -1394,7 +1393,7 @@ SSLPeerInfo SSLManagerApple::parseAndValidatePeerCertificateDeprecated(
auto ssl = checked_cast<const SSLConnectionApple*>(conn)->get();
auto swPeerSubjectName =
- parseAndValidatePeerCertificate(ssl, boost::none, remoteHost, hostForLogging);
+ parseAndValidatePeerCertificate(ssl, boost::none, remoteHost, hostForLogging).getNoThrow();
// We can't use uassertStatusOK here because we need to throw a NetworkException.
if (!swPeerSubjectName.isOK()) {
throwSocketError(SocketErrorKind::CONNECT_ERROR, swPeerSubjectName.getStatus().reason());
@@ -1420,7 +1419,7 @@ StatusWith<TLSVersion> mapTLSVersion(SSLContextRef ssl) {
}
-StatusWith<SSLPeerInfo> SSLManagerApple::parseAndValidatePeerCertificate(
+Future<SSLPeerInfo> SSLManagerApple::parseAndValidatePeerCertificate(
::SSLContextRef ssl,
boost::optional<std::string> sniName,
const std::string& remoteHost,
@@ -1430,7 +1429,7 @@ StatusWith<SSLPeerInfo> SSLManagerApple::parseAndValidatePeerCertificate(
// Record TLS version stats
auto tlsVersionStatus = mapTLSVersion(ssl);
if (!tlsVersionStatus.isOK()) {
- return tlsVersionStatus.getStatus();
+ return Future<SSLPeerInfo>::makeReady(tlsVersionStatus.getStatus());
}
recordTLSVersion(tlsVersionStatus.getValue(), hostForLogging);
@@ -1443,14 +1442,14 @@ StatusWith<SSLPeerInfo> SSLManagerApple::parseAndValidatePeerCertificate(
* so that the validation path runs anyway.
*/
if (!_sslConfiguration.hasCA && isSSLServer) {
- return SSLPeerInfo(sniName);
+ return Future<SSLPeerInfo>::makeReady(SSLPeerInfo(sniName));
}
- const auto badCert = [&](StringData msg, bool warn = false) -> StatusWith<SSLPeerInfo> {
+ const auto badCert = [&](StringData msg, bool warn = false) -> Future<SSLPeerInfo> {
constexpr StringData prefix = "SSL peer certificate validation failed: "_sd;
if (warn) {
warning() << prefix << msg;
- return SSLPeerInfo(sniName);
+ return Future<SSLPeerInfo>::makeReady(SSLPeerInfo(sniName));
} else {
std::string m = str::stream() << prefix << msg << "; connection rejected";
error() << m;
@@ -1573,7 +1572,8 @@ StatusWith<SSLPeerInfo> SSLManagerApple::parseAndValidatePeerCertificate(
if (!swPeerCertificateRoles.isOK()) {
return swPeerCertificateRoles.getStatus();
}
- return SSLPeerInfo(peerSubjectName, sniName, std::move(swPeerCertificateRoles.getValue()));
+ return Future<SSLPeerInfo>::makeReady(
+ SSLPeerInfo(peerSubjectName, sniName, std::move(swPeerCertificateRoles.getValue())));
}
// If this is an SSL client context (on a MongoDB server or client)
@@ -1642,7 +1642,7 @@ StatusWith<SSLPeerInfo> SSLManagerApple::parseAndValidatePeerCertificate(
}
}
- return SSLPeerInfo(peerSubjectName);
+ return Future<SSLPeerInfo>::makeReady(SSLPeerInfo(peerSubjectName));
}
int SSLManagerApple::SSL_read(SSLConnectionInterface* conn, void* buf, int num) {
diff --git a/src/mongo/util/net/ssl_manager_openssl.cpp b/src/mongo/util/net/ssl_manager_openssl.cpp
index 70f7a751497..702ae32b4c3 100644
--- a/src/mongo/util/net/ssl_manager_openssl.cpp
+++ b/src/mongo/util/net/ssl_manager_openssl.cpp
@@ -65,6 +65,7 @@
#include "mongo/util/net/ssl_types.h"
#include "mongo/util/scopeguard.h"
#include "mongo/util/str.h"
+#include "mongo/util/strong_weak_finish_line.h"
#include "mongo/util/text.h"
#include <arpa/inet.h>
@@ -386,11 +387,10 @@ public:
const std::string& remoteHost,
const HostAndPort& hostForLogging) final;
- StatusWith<SSLPeerInfo> parseAndValidatePeerCertificate(
- SSL* conn,
- boost::optional<std::string> sni,
- const std::string& remoteHost,
- const HostAndPort& hostForLogging) final;
+ Future<SSLPeerInfo> parseAndValidatePeerCertificate(SSL* conn,
+ boost::optional<std::string> sni,
+ const std::string& remoteHost,
+ const HostAndPort& hostForLogging) final;
const SSLConfiguration& getSSLConfiguration() const final {
return _sslConfiguration;
@@ -1000,8 +1000,8 @@ StatusWith<OCSPValidationContext> extractOcspUris(SSL_CTX* context,
std::move(ocspRequestMap), std::move(uniqueCertIds), std::move(leafResponders)};
}
-StatusWith<UniqueOCSPResponse> retrieveOCSPResponse(const std::string& host,
- OCSPRequestAndIDs& ocspRequestAndIDs) {
+Future<UniqueOCSPResponse> retrieveOCSPResponse(const std::string& host,
+ OCSPRequestAndIDs& ocspRequestAndIDs) {
auto& [ocspReq, certIDs] = ocspRequestAndIDs;
// Decompose the OCSP request into a DER encoded OCSP request
@@ -1018,20 +1018,21 @@ StatusWith<UniqueOCSPResponse> retrieveOCSPResponse(const std::string& host,
}
// Query the OCSP responder
- auto responseData = ocspRequestStatus(buffer, host);
- if (!responseData.isOK()) {
- return responseData.getStatus();
- }
- std::vector<uint8_t> respDataVector(std::move(responseData.getValue()));
- const uint8_t* respDataPtr = respDataVector.data();
+ return OCSPManager::get()
+ ->requestStatus(buffer, host)
+ .then([](std::vector<uint8_t> responseData) mutable -> StatusWith<UniqueOCSPResponse> {
+ const uint8_t* respDataPtr = responseData.data();
- // Convert the Response back to a OpenSSL known format
- UniqueOCSPResponse response(d2i_OCSP_RESPONSE(nullptr, &respDataPtr, respDataVector.size()));
+ // Convert the Response back to a OpenSSL known format
+ UniqueOCSPResponse response(
+ d2i_OCSP_RESPONSE(nullptr, &respDataPtr, responseData.size()));
- if (response == nullptr) {
- return getSSLFailure("Could not retrieve OCSP Response.");
- }
- return std::move(response);
+ if (response == nullptr) {
+ return getSSLFailure("Could not retrieve OCSP Response.");
+ }
+
+ return std::move(response);
+ });
}
/**
@@ -1136,9 +1137,17 @@ int ocspServerCallback(SSL* ssl, void* arg) {
stdx::lock_guard<mongo::Mutex> guard(sharedResponseMutex);
auto response = static_cast<std::shared_ptr<OCSP_RESPONSE>*>(arg);
+ if (!response) {
+ return SSL_TLSEXT_ERR_NOACK;
+ }
+
unsigned char* ocspResponseBuffer = NULL;
int length = i2d_OCSP_RESPONSE(response->get(), &ocspResponseBuffer);
+ if (length == 0) {
+ return SSL_TLSEXT_ERR_NOACK;
+ }
+
SSL_set_tlsext_status_ocsp_resp(ssl, ocspResponseBuffer, length);
}
@@ -1178,11 +1187,79 @@ StatusWith<bool> verifyStapledResponse(SSL* conn, X509* peerCert, OCSP_RESPONSE*
return false;
}
-// This function returns early if there is an issue processing the request in the beginning.
-// If there is an issue when processing the responses, the function will just continue to the
-// next certificate. If there is a revoked certificate in the chain, the function will fail.
-// Otherwise, the function will error if it cannot validate the peer certificate.
-Status verifyPeerCertWithOCSP(SSL* conn, X509* peerCert) {
+Future<std::pair<Status, UniqueOCSPResponse>> dispatchRequests(
+ SSL_CTX* context,
+ UniqueVerifiedChainPolyfill intermediateCerts,
+ OCSPValidationContext& ocspContext) {
+ auto& [ocspRequestMap, _, leafResponders] = ocspContext;
+
+ struct OCSPCompletionState {
+ OCSPCompletionState(int numRequests_,
+ Promise<std::pair<Status, UniqueOCSPResponse>> promise_,
+ UniqueVerifiedChainPolyfill intermediateCerts_)
+ : finishLine(numRequests_),
+ promise(std::move(promise_)),
+ intermediateCerts(std::move(intermediateCerts_)) {}
+
+ StrongWeakFinishLine finishLine;
+ Promise<std::pair<Status, UniqueOCSPResponse>> promise;
+ std::shared_ptr<STACK_OF(X509)> intermediateCerts;
+ };
+
+ std::vector<Future<UniqueOCSPResponse>> futureResponses{};
+
+ for (auto host : leafResponders) {
+ auto& ocspRequestAndIDs = ocspRequestMap[host];
+ Future<UniqueOCSPResponse> futureResponse = retrieveOCSPResponse(host, ocspRequestAndIDs);
+ futureResponses.push_back(std::move(futureResponse));
+ }
+
+ auto pf = makePromiseFuture<std::pair<Status, UniqueOCSPResponse>>();
+ auto state = std::make_shared<OCSPCompletionState>(
+ futureResponses.size(), std::move(pf.promise), std::move(intermediateCerts));
+
+ for (size_t i = 0; i < futureResponses.size(); i++) {
+ auto futureResponse = std::move(futureResponses[i]);
+ std::move(futureResponse)
+ .getAsync([context, state](StatusWith<UniqueOCSPResponse> swResponse) mutable {
+ if (!swResponse.isOK()) {
+ if (state->finishLine.arriveWeakly()) {
+ state->promise.setError(
+ Status(ErrorCodes::OCSPCertificateStatusUnknown,
+ "Could not obtain status information of certificates."));
+ }
+ return;
+ }
+
+ auto swCertIDSet = validateResponse(
+ context, swResponse.getValue().get(), state->intermediateCerts.get());
+
+ if (swCertIDSet.isOK() ||
+ swCertIDSet.getStatus() == ErrorCodes::OCSPCertificateStatusRevoked) {
+ if (state->finishLine.arriveStrongly()) {
+ state->promise.emplaceValue(swCertIDSet.getStatus(),
+ std::move(swResponse.getValue()));
+ return;
+ }
+ } else {
+ if (state->finishLine.arriveWeakly()) {
+ state->promise.setError(
+ Status(ErrorCodes::OCSPCertificateStatusUnknown,
+ "Could not obtain status information of certificates."));
+ return;
+ }
+ }
+ });
+ }
+
+ return std::move(pf.future);
+}
+
+// This function returns a future with a not OK status if there is an issue processing the
+// request in the beginning. If there is an issue when processing the responses, the function
+// will just continue to the next certificate. If there is a revoked certificate in the chain,
+// the future will resolve to a not OK status.
+Future<void> verifyPeerCertWithOCSP(SSL* conn, X509* peerCert) {
UniqueOpenSSLStringStack aiaOCSP(X509_get1_ocsp(peerCert));
if (!aiaOCSP) {
return Status::OK();
@@ -1192,53 +1269,24 @@ Status verifyPeerCertWithOCSP(SSL* conn, X509* peerCert) {
ERR_clear_error();
auto intermediateCerts = SSLgetVerifiedChain(conn);
- auto swOCSPContext = extractOcspUris(SSL_get_SSL_CTX(conn), peerCert, intermediateCerts.get());
+ auto context = SSL_get_SSL_CTX(conn);
+
+ auto swOCSPContext = extractOcspUris(context, peerCert, intermediateCerts.get());
if (!swOCSPContext.isOK()) {
return swOCSPContext.getStatus();
}
- auto& [ocspRequestMap, uniqueCertIds, leafResponders] = swOCSPContext.getValue();
-
- for (auto& [host, ocspRequestAndIDs] : ocspRequestMap) {
- auto swResponse = retrieveOCSPResponse(host, ocspRequestAndIDs);
- if (swResponse.getStatus().code() == ErrorCodes::InternalErrorNotSupported) {
- warning() << "Could not perform OCSP validation: " << swResponse.getStatus();
- return Status::OK();
- } else if (!swResponse.isOK()) {
- continue;
- }
-
- auto& response = swResponse.getValue();
-
- auto swCertIDSet =
- validateResponse(SSL_get_SSL_CTX(conn), response.get(), intermediateCerts.get());
-
- // The only error that will fail is an OCSPCertificateStatusRevoked. Even if a
- // response has an unknown error, we don't want to discredit all the responses
- // because of that one issue. The main thing we are looking for is status
- // information on the peer certificate as described below.
- if (swCertIDSet.getStatus() == ErrorCodes::OCSPCertificateStatusRevoked) {
- return swCertIDSet.getStatus();
- }
-
- if (swCertIDSet.isOK()) {
- for (auto& certId : swCertIDSet.getValue()) {
- uniqueCertIds.erase(certId);
- }
- }
- }
-
- auto swCertId = getCertIdForCert(SSL_get_SSL_CTX(conn), peerCert);
- if (!swCertId.isOK()) {
- return swCertId.getStatus();
- }
+ auto ocspContext = std::move(swOCSPContext.getValue());
- // If we can get status information on the peer certificate, everything is good to go.
- if (uniqueCertIds.find(swCertId.getValue()) != uniqueCertIds.end()) {
- return getSSLFailure("OCSP Validation Error: Could not validate the peer certificate.");
- }
+ return dispatchRequests(context, std::move(intermediateCerts), ocspContext)
+ .onCompletion(
+ [context](StatusWith<std::pair<Status, UniqueOCSPResponse>> swResponse) -> Status {
+ if (!swResponse.isOK()) {
+ return Status::OK();
+ }
- return Status::OK();
+ return swResponse.getValue().first;
+ });
}
// The definition of the callbacks
@@ -1300,7 +1348,7 @@ int ocspClientCallback(SSL* ssl, void* arg) {
* the chain using a CRL. If no CRL is provided then the shell should reach out to the OCSP
* responders itself and verify the status of the peer certificate.
*/
-Status ocspClientVerification(SSL* ssl) {
+Future<void> ocspClientVerification(SSL* ssl) {
UniqueX509 peerCert(SSL_get_peer_certificate(ssl));
const unsigned char* response_ptr = NULL;
@@ -1340,47 +1388,37 @@ Status stapleOCSPResponse(SSL_CTX* context) {
return Status::OK();
}
- STACK_OF(X509) * intermediateCerts;
+ STACK_OF(X509) * intermediateCertsPtr;
- if (SSL_CTX_get0_chain_certs(context, &intermediateCerts) == 0) {
+ if (SSL_CTX_get0_chain_certs(context, &intermediateCertsPtr) == 0) {
return getSSLFailure("Could not get chain for SSL Context.");
}
- auto swOCSPContext = extractOcspUris(context, cert, intermediateCerts);
+ UniqueVerifiedChainPolyfill intermediateCerts(intermediateCertsPtr);
+
+ auto swOCSPContext = extractOcspUris(context, cert, intermediateCerts.get());
if (!swOCSPContext.isOK()) {
+ warning() << "Could not staple OCSP response to outgoing certificate.";
return swOCSPContext.getStatus();
}
- auto& [ocspRequestMap, _, leafResponders] = swOCSPContext.getValue();
+ auto ocspContext = std::move(swOCSPContext.getValue());
- for (auto host : leafResponders) {
- auto& ocspRequestAndIDs = ocspRequestMap[host];
- auto swResponse = retrieveOCSPResponse(host, ocspRequestAndIDs);
- if (!swResponse.isOK()) {
- if (swResponse.getStatus() == ErrorCodes::InternalErrorNotSupported) {
- warning() << "Could not perform OCSP validation: " << swResponse.getStatus();
- return Status::OK();
+ dispatchRequests(context, std::move(intermediateCerts), ocspContext)
+ .getAsync([context](StatusWith<std::pair<Status, UniqueOCSPResponse>> swResponse) {
+ if (!swResponse.isOK()) {
+ warning() << "Could not staple OCSP response to outgoing certificate.";
+ return;
}
- continue;
- }
-
- auto status = validateResponse(context, swResponse.getValue().get(), intermediateCerts);
-
- // If the certificate status is neither OK nor revoked, then we can get the
- // status of the certificate from the next responder. If all are indeterminate,
- // we can put the onus on the client to retrieve the response.
- if (status.isOK() || status == ErrorCodes::OCSPCertificateStatusRevoked) {
stdx::lock_guard<mongo::Mutex> guard(sharedResponseMutex);
sharedResponseForServer =
- std::shared_ptr<OCSP_RESPONSE>(std::move(swResponse.getValue()));
- SSL_CTX_set_tlsext_status_cb(context, ocspServerCallback);
- SSL_CTX_set_tlsext_status_arg(context, &sharedResponseForServer);
- return Status::OK();
- }
- }
+ std::shared_ptr<OCSP_RESPONSE>(std::move(swResponse.getValue().second));
+ });
+
+ SSL_CTX_set_tlsext_status_cb(context, ocspServerCallback);
+ SSL_CTX_set_tlsext_status_arg(context, &sharedResponseForServer);
- warning() << "Could not staple OCSP response to outgoing certificate.";
return Status::OK();
}
#endif
@@ -1472,7 +1510,6 @@ Status SSLManagerOpenSSL::initSSLContext(SSL_CTX* context,
}
if (sslOCSPEnabled) {
-
#if OPENSSL_VERSION_NUMBER >= 0x10002000L
if (direction == SSLManagerInterface::ConnectionDirection::kIncoming) {
auto resp = stapleOCSPResponse(context);
@@ -1949,11 +1986,12 @@ Status _validatePeerRoles(const stdx::unordered_set<RoleName>& embeddedRoles, SS
}
} // namespace
-StatusWith<SSLPeerInfo> SSLManagerOpenSSL::parseAndValidatePeerCertificate(
+Future<SSLPeerInfo> SSLManagerOpenSSL::parseAndValidatePeerCertificate(
SSL* conn,
boost::optional<std::string> sni,
const std::string& remoteHost,
const HostAndPort& hostForLogging) {
+
auto tlsVersionStatus = mapTLSVersion(conn);
if (!tlsVersionStatus.isOK()) {
return tlsVersionStatus.getStatus();
@@ -1997,11 +2035,9 @@ StatusWith<SSLPeerInfo> SSLManagerOpenSSL::parseAndValidatePeerCertificate(
}
}
+ Future<void> ocspFuture;
if (sslOCSPEnabled) {
- auto status = ocspClientVerification(conn);
- if (!status.isOK()) {
- return status;
- }
+ ocspFuture = ocspClientVerification(conn);
}
// TODO: check optional cipher restriction, using cert.
@@ -2010,7 +2046,7 @@ StatusWith<SSLPeerInfo> SSLManagerOpenSSL::parseAndValidatePeerCertificate(
StatusWith<stdx::unordered_set<RoleName>> swPeerCertificateRoles = _parsePeerRoles(peerCert);
if (!swPeerCertificateRoles.isOK()) {
- return swPeerCertificateRoles.getStatus();
+ return Future<SSLPeerInfo>::makeReady(swPeerCertificateRoles.getStatus());
}
if (auto status = _validatePeerRoles(swPeerCertificateRoles.getValue(), conn); !status.isOK()) {
@@ -2039,7 +2075,13 @@ StatusWith<SSLPeerInfo> SSLManagerOpenSSL::parseAndValidatePeerCertificate(
warning() << "Client connecting with server's own TLS certificate";
}
- return SSLPeerInfo(peerSubject, sni, std::move(swPeerCertificateRoles.getValue()));
+ // void futures are default constructed as ready futures.
+ return std::move(ocspFuture)
+ .then([peerSubject,
+ sni,
+ peerCertificateRoles = std::move(swPeerCertificateRoles.getValue())] {
+ return SSLPeerInfo(peerSubject, sni, peerCertificateRoles);
+ });
}
// If this is an SSL client context (on a MongoDB server or client)
@@ -2139,11 +2181,11 @@ StatusWith<SSLPeerInfo> SSLManagerOpenSSL::parseAndValidatePeerCertificate(
warning() << msg;
} else {
error() << msg;
- return Status(ErrorCodes::SSLHandshakeFailed, msg);
+ return Future<SSLPeerInfo>::makeReady(Status(ErrorCodes::SSLHandshakeFailed, msg));
}
}
- return SSLPeerInfo(peerSubject);
+ return std::move(ocspFuture).then([this, peerSubject]() { return SSLPeerInfo(peerSubject); });
}
@@ -2154,7 +2196,8 @@ SSLPeerInfo SSLManagerOpenSSL::parseAndValidatePeerCertificateDeprecated(
const SSLConnectionOpenSSL* conn = checked_cast<const SSLConnectionOpenSSL*>(connInterface);
auto swPeerSubjectName =
- parseAndValidatePeerCertificate(conn->ssl, boost::none, remoteHost, hostForLogging);
+ parseAndValidatePeerCertificate(conn->ssl, boost::none, remoteHost, hostForLogging)
+ .getNoThrow();
// We can't use uassertStatusOK here because we need to throw a NetworkException.
if (!swPeerSubjectName.isOK()) {
throwSocketError(SocketErrorKind::CONNECT_ERROR, swPeerSubjectName.getStatus().reason());
diff --git a/src/mongo/util/net/ssl_manager_windows.cpp b/src/mongo/util/net/ssl_manager_windows.cpp
index 48e21eeae20..4c9bcb356c8 100644
--- a/src/mongo/util/net/ssl_manager_windows.cpp
+++ b/src/mongo/util/net/ssl_manager_windows.cpp
@@ -273,11 +273,10 @@ public:
const std::string& remoteHost,
const HostAndPort& hostForLogging) final;
- StatusWith<SSLPeerInfo> parseAndValidatePeerCertificate(
- PCtxtHandle ssl,
- boost::optional<std::string> sni,
- const std::string& remoteHost,
- const HostAndPort& hostForLogging) final;
+ Future<SSLPeerInfo> parseAndValidatePeerCertificate(PCtxtHandle ssl,
+ boost::optional<std::string> sni,
+ const std::string& remoteHost,
+ const HostAndPort& hostForLogging) final;
const SSLConfiguration& getSSLConfiguration() const final {
@@ -1601,17 +1600,19 @@ SSLPeerInfo SSLManagerWindows::parseAndValidatePeerCertificateDeprecated(
const SSLConnectionInterface* conn,
const std::string& remoteHost,
const HostAndPort& hostForLogging) {
- auto swPeerSubjectName = parseAndValidatePeerCertificate(
- const_cast<SSLConnectionWindows*>(static_cast<const SSLConnectionWindows*>(conn))
- ->_engine.native_handle(),
- boost::none,
- remoteHost,
- hostForLogging);
+
+ auto swPeerSubjectName =
+ parseAndValidatePeerCertificate(
+ const_cast<SSLConnectionWindows*>(static_cast<const SSLConnectionWindows*>(conn))
+ ->_engine.native_handle(),
+ boost::none,
+ remoteHost,
+ hostForLogging)
+ .getNoThrow();
// We can't use uassertStatusOK here because we need to throw a SocketException.
if (!swPeerSubjectName.isOK()) {
throwSocketError(SocketErrorKind::CONNECT_ERROR, swPeerSubjectName.getStatus().reason());
}
-
return swPeerSubjectName.getValue();
}
@@ -1859,7 +1860,7 @@ StatusWith<TLSVersion> mapTLSVersion(PCtxtHandle ssl) {
}
}
-StatusWith<SSLPeerInfo> SSLManagerWindows::parseAndValidatePeerCertificate(
+Future<SSLPeerInfo> SSLManagerWindows::parseAndValidatePeerCertificate(
PCtxtHandle ssl,
boost::optional<std::string> sni,
const std::string& remoteHost,
@@ -1876,7 +1877,7 @@ StatusWith<SSLPeerInfo> SSLManagerWindows::parseAndValidatePeerCertificate(
recordTLSVersion(tlsVersionStatus.getValue(), hostForLogging);
if (!_sslConfiguration.hasCA && isSSLServer)
- return SSLPeerInfo(sni);
+ return Future<SSLPeerInfo>::makeReady(SSLPeerInfo(sni));
SECURITY_STATUS ss = QueryContextAttributes(ssl, SECPKG_ATTR_REMOTE_CERT_CONTEXT, &cert);
@@ -1928,7 +1929,7 @@ StatusWith<SSLPeerInfo> SSLManagerWindows::parseAndValidatePeerCertificate(
}
if (peerSubjectName.empty()) {
- return SSLPeerInfo(sni);
+ return Future<SSLPeerInfo>::makeReady(SSLPeerInfo(sni));
}
LOG(2) << "Accepted TLS connection from peer: " << peerSubjectName;
@@ -1945,9 +1946,10 @@ StatusWith<SSLPeerInfo> SSLManagerWindows::parseAndValidatePeerCertificate(
return swPeerCertificateRoles.getStatus();
}
- return SSLPeerInfo(peerSubjectName, sni, std::move(swPeerCertificateRoles.getValue()));
+ return Future<SSLPeerInfo>::makeReady(
+ SSLPeerInfo(peerSubjectName, sni, std::move(swPeerCertificateRoles.getValue())));
} else {
- return SSLPeerInfo(peerSubjectName);
+ return Future<SSLPeerInfo>::makeReady(SSLPeerInfo(peerSubjectName));
}
}