summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorsergey.galtsev <sergey.galtsev@mongodb.com>2021-08-16 21:13:10 +0000
committerEvergreen Agent <no-reply@evergreen.mongodb.com>2021-08-16 21:28:37 +0000
commit085d811dbee92b9b7f71205d3aa1b2ad0bd334c4 (patch)
treee9de53a1554b1602572037c5b622e1b78ac35169
parent0f9e41dd9730b9bb4db3fcee4fef634922337061 (diff)
downloadmongo-085d811dbee92b9b7f71205d3aa1b2ad0bd334c4.tar.gz
SERVER-57716: use common CA file for OCSP where clusterCA is present
-rw-r--r--jstests/ocsp/lib/ocsp_helpers.js2
-rw-r--r--jstests/ocsp/ocsp_basic.js2
-rw-r--r--jstests/ocsp/ocsp_stapling.js25
-rw-r--r--src/mongo/util/net/ssl_manager_openssl.cpp79
4 files changed, 87 insertions, 21 deletions
diff --git a/jstests/ocsp/lib/ocsp_helpers.js b/jstests/ocsp/lib/ocsp_helpers.js
index 150fa7b4442..fd79d9c2941 100644
--- a/jstests/ocsp/lib/ocsp_helpers.js
+++ b/jstests/ocsp/lib/ocsp_helpers.js
@@ -7,6 +7,8 @@ load("jstests/ssl/libs/ssl_helpers.js");
const OCSP_CA_PEM = "jstests/libs/ocsp/ca_ocsp.pem";
const OCSP_CA_CERT = "jstests/libs/ocsp/ca_ocsp.crt";
const OCSP_CA_KEY = "jstests/libs/ocsp/ca_ocsp.key";
+const CLUSTER_CA_CERT = "jstests/libs/ca.pem";
+const CLUSTER_KEY = "jstests/libs/server.pem";
const OCSP_SERVER_CERT = "jstests/libs/ocsp/server_ocsp.pem";
const OCSP_CLIENT_CERT = "jstests/libs/ocsp/client_ocsp.pem";
const OCSP_SERVER_MUSTSTAPLE_CERT = "jstests/libs/ocsp/server_ocsp_mustStaple.pem";
diff --git a/jstests/ocsp/ocsp_basic.js b/jstests/ocsp/ocsp_basic.js
index 1ad966497fe..fc503d4dd6f 100644
--- a/jstests/ocsp/ocsp_basic.js
+++ b/jstests/ocsp/ocsp_basic.js
@@ -34,6 +34,7 @@ mock_ocsp.stop();
Object.extend(ocsp_options, {waitForConnect: false});
ocsp_options.sslPEMKeyFile = OCSP_SERVER_CERT_REVOKED;
+print("Restarting MockOCSPServer with FAULT_REVOKED option");
mock_ocsp = new MockOCSPServer(FAULT_REVOKED, 1);
mock_ocsp.start();
@@ -42,6 +43,7 @@ conn = MongoRunner.runMongod(ocsp_options);
waitForServer(conn);
assert.throws(() => {
+ print("Following connection should fail");
new Mongo(conn.host);
});
diff --git a/jstests/ocsp/ocsp_stapling.js b/jstests/ocsp/ocsp_stapling.js
index e2735f758d9..97896502e57 100644
--- a/jstests/ocsp/ocsp_stapling.js
+++ b/jstests/ocsp/ocsp_stapling.js
@@ -10,7 +10,14 @@ if (!supportsStapling()) {
return;
}
-function test(serverCert, caCert, responderCertPair) {
+const CLUSTER_CA = {
+ tlsClusterFile: CLUSTER_KEY,
+ tlsClusterCAFile: CLUSTER_CA_CERT,
+ tlsAllowConnectionsWithoutCertificates: "",
+ tlsAllowInvalidCertificates: "",
+};
+
+function test(serverCert, caCert, responderCertPair, extraOpts) {
const ocsp_options = {
sslMode: "requireSSL",
sslPEMKeyFile: serverCert,
@@ -19,9 +26,13 @@ function test(serverCert, caCert, responderCertPair) {
setParameter: {
"ocspStaplingRefreshPeriodSecs": 500,
"ocspEnabled": "true",
- },
+ }
};
+ if (extraOpts) {
+ Object.extend(ocsp_options, extraOpts);
+ }
+
// This is to test what happens when the responder is down,
// making sure that we soft fail.
let conn = null;
@@ -84,9 +95,19 @@ function test(serverCert, caCert, responderCertPair) {
}
test(OCSP_SERVER_CERT, OCSP_CA_PEM, OCSP_DELEGATE_RESPONDER);
+test(OCSP_SERVER_CERT, OCSP_CA_PEM, OCSP_DELEGATE_RESPONDER, CLUSTER_CA);
test(OCSP_SERVER_CERT, OCSP_CA_PEM, OCSP_CA_RESPONDER);
+test(OCSP_SERVER_CERT, OCSP_CA_PEM, OCSP_CA_RESPONDER, CLUSTER_CA);
+
+// This test can not be repeated with CLUSTER_CA, because intermediate cert
+// is not part of cluster CA chain
test(OCSP_SERVER_SIGNED_BY_INTERMEDIATE_CA_PEM,
OCSP_INTERMEDIATE_CA_WITH_ROOT_PEM,
OCSP_INTERMEDIATE_RESPONDER);
+
test(OCSP_SERVER_AND_INTERMEDIATE_APPENDED_PEM, OCSP_CA_PEM, OCSP_INTERMEDIATE_RESPONDER);
+test(OCSP_SERVER_AND_INTERMEDIATE_APPENDED_PEM,
+ OCSP_CA_PEM,
+ OCSP_INTERMEDIATE_RESPONDER,
+ CLUSTER_CA);
}());
diff --git a/src/mongo/util/net/ssl_manager_openssl.cpp b/src/mongo/util/net/ssl_manager_openssl.cpp
index 18f5ac36372..b222f01954a 100644
--- a/src/mongo/util/net/ssl_manager_openssl.cpp
+++ b/src/mongo/util/net/ssl_manager_openssl.cpp
@@ -797,7 +797,7 @@ StatusWith<std::pair<OCSPCertIDSet, boost::optional<Date_t>>> iterateResponse(
* earliest expiration date on the OCSPResponse.
*/
StatusWith<std::pair<OCSPCertIDSet, boost::optional<Date_t>>> parseAndValidateOCSPResponse(
- SSL_CTX* context, OCSP_RESPONSE* response, STACK_OF(X509) * intermediateCerts) {
+ SSL_CTX* context, X509_STORE* ca, OCSP_RESPONSE* response, STACK_OF(X509) * intermediateCerts) {
// Read the overall status of the OCSP response
int responseStatus = OCSP_response_status(response);
switch (responseStatus) {
@@ -825,7 +825,7 @@ StatusWith<std::pair<OCSPCertIDSet, boost::optional<Date_t>>> parseAndValidateOC
return getSSLFailure("incomplete OCSP response.");
}
- X509_STORE* store = SSL_CTX_get_cert_store(context);
+ X509_STORE* store = ca == nullptr ? SSL_CTX_get_cert_store(context) : ca;
// OCSP_basic_verify takes in the Response from the responder and verifies
// that the signer of the OCSP response is in intermediateCerts. Then it tries
@@ -837,10 +837,11 @@ StatusWith<std::pair<OCSPCertIDSet, boost::optional<Date_t>>> parseAndValidateOC
return iterateResponse(basicResponse.get(), intermediateCerts);
}
-Future<OCSPFetchResponse> dispatchRequests(SSL_CTX* context,
- std::shared_ptr<STACK_OF(X509)> intermediateCerts,
- OCSPValidationContext& ocspContext,
- OCSPPurpose purpose) {
+Future<OCSPFetchResponse> dispatchOCSPRequests(SSL_CTX* context,
+ std::shared_ptr<X509_STORE> ca,
+ std::shared_ptr<STACK_OF(X509)> intermediateCerts,
+ OCSPValidationContext& ocspContext,
+ OCSPPurpose purpose) {
auto& [ocspRequestMap, _, leafResponders] = ocspContext;
struct OCSPCompletionState {
@@ -872,7 +873,7 @@ Future<OCSPFetchResponse> dispatchRequests(SSL_CTX* context,
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 {
+ .getAsync([context, ca, state](StatusWith<UniqueOCSPResponse> swResponse) mutable {
if (!swResponse.isOK()) {
if (state->finishLine.arriveWeakly()) {
state->promise.setError(
@@ -883,7 +884,7 @@ Future<OCSPFetchResponse> dispatchRequests(SSL_CTX* context,
}
auto swCertIDSetAndDuration = parseAndValidateOCSPResponse(
- context, swResponse.getValue().get(), state->intermediateCerts.get());
+ context, ca.get(), swResponse.getValue().get(), state->intermediateCerts.get());
if (swCertIDSetAndDuration.isOK() ||
swCertIDSetAndDuration.getStatus() ==
@@ -981,10 +982,12 @@ private:
auto ocspContext = std::move(swOCSPContext.getValue());
- auto swResponse =
- dispatchRequests(
- key.context, key.intermediateCerts, ocspContext, OCSPPurpose::kClientVerify)
- .getNoThrow();
+ auto swResponse = dispatchOCSPRequests(key.context,
+ nullptr,
+ key.intermediateCerts,
+ ocspContext,
+ OCSPPurpose::kClientVerify)
+ .getNoThrow();
if (!swResponse.isOK()) {
return boost::none;
}
@@ -1063,6 +1066,7 @@ private:
UniqueSSL _ssl{nullptr};
SSL_CTX* _context{nullptr};
X509* _cert{nullptr};
+ std::shared_ptr<X509_STORE> _ca;
// Note: A ref is kept by all futures and the periodic job on the SSLManagerOpenSSL ensure this
// object is alive.
@@ -1089,6 +1093,10 @@ public:
const SSLParams& params,
ConnectionDirection direction) final;
+ std::shared_ptr<X509_STORE> getOCSPCertStore() {
+ return _ocspCertStore;
+ }
+
SSLConnectionInterface* connect(Socket* socket) final;
SSLConnectionInterface* accept(Socket* socket, const char* initialBytes, int len) final;
@@ -1134,8 +1142,9 @@ private:
const int _rolesNid = OBJ_create(mongodbRolesOID.identifier.c_str(),
mongodbRolesOID.shortDescription.c_str(),
mongodbRolesOID.longDescription.c_str());
- UniqueSSLContext _serverContext; // SSL context for incoming connections
- UniqueSSLContext _clientContext; // SSL context for outgoing connections
+ UniqueSSLContext _serverContext; // SSL context for incoming connections
+ UniqueSSLContext _clientContext; // SSL context for outgoing connections
+ std::shared_ptr<X509_STORE> _ocspCertStore; // X509 Store specifically for OCSP stapling
bool _weakValidation;
bool _allowInvalidCertificates;
@@ -1448,6 +1457,7 @@ SSLConnectionOpenSSL::~SSLConnectionOpenSSL() {
SSLManagerOpenSSL::SSLManagerOpenSSL(const SSLParams& params, bool isServer)
: _serverContext(nullptr),
_clientContext(nullptr),
+ _ocspCertStore(nullptr),
_weakValidation(params.sslWeakCertificateValidation),
_allowInvalidCertificates(params.sslAllowInvalidCertificates),
_allowInvalidHostnames(params.sslAllowInvalidHostnames),
@@ -1498,6 +1508,24 @@ SSLManagerOpenSSL::SSLManagerOpenSSL(const SSLParams& params, bool isServer)
static CertificateExpirationMonitor task =
CertificateExpirationMonitor(_sslConfiguration.serverCertificateExpirationDate);
+
+ if (tlsOCSPEnabled) {
+ OpenSSLDeleter<decltype(::X509_STORE_free), ::X509_STORE_free> deleter;
+ _ocspCertStore = std::shared_ptr<X509_STORE>(X509_STORE_new(), deleter);
+ if (!_ocspCertStore) {
+ uasserted(5771600, "failed to allocate X509 store for OCSP fetcher");
+ }
+
+ LOGV2_DEBUG(5771602, 1, "Loading ocsp store", "cafile"_attr = params.sslCAFile);
+ int result = params.sslCAFile.empty()
+ ? X509_STORE_set_default_paths(_ocspCertStore.get())
+ : X509_STORE_load_locations(
+ _ocspCertStore.get(), params.sslCAFile.c_str(), nullptr);
+
+ if (result == 0) {
+ uasserted(5771601, "failed to load certificates into OCSP X509 store");
+ }
+ }
}
}
@@ -1604,13 +1632,14 @@ StatusWith<bool> verifyStapledResponse(SSL* conn, X509* peerCert, OCSP_RESPONSE*
auto intermediateCerts = SSLgetVerifiedChain(conn);
OCSPCertIDSet emptyCertIDSet{};
- auto swCertId = getCertIdForCert(SSL_get_SSL_CTX(conn), peerCert, intermediateCerts.get());
+ auto context = SSL_get_SSL_CTX(conn);
+ auto swCertId = getCertIdForCert(context, peerCert, intermediateCerts.get());
if (!swCertId.isOK()) {
return swCertId.getStatus();
}
auto swCertIDSetAndDuration =
- parseAndValidateOCSPResponse(SSL_get_SSL_CTX(conn), response, intermediateCerts.get());
+ parseAndValidateOCSPResponse(context, nullptr, response, intermediateCerts.get());
if (swCertIDSetAndDuration.getStatus() == ErrorCodes::OCSPCertificateStatusRevoked) {
return swCertIDSetAndDuration.getStatus();
@@ -1838,6 +1867,10 @@ Status SSLManagerOpenSSL::stapleOCSPResponse(SSL_CTX* context) {
return Status::OK();
}
+ if (!isSSLServer) {
+ return Status::OK();
+ }
+
return _fetcher.start(context, true);
}
#else
@@ -1850,6 +1883,8 @@ Status OCSPFetcher::start(SSL_CTX* context, bool asyncOCSPStaple) {
// Increment the ref count on SSL_CTX by creating a SSL object so that our context lives with
// the OCSPFetcher
_ssl = UniqueSSL(SSL_new(context));
+ _ca = _manager->getOCSPCertStore();
+ invariant(_ca);
_context = context;
auto certificateHolder = getCertificateForContext(_context);
@@ -1956,13 +1991,17 @@ void sslContextGetOtherCerts(SSL_CTX* ctx, STACK_OF(X509) * *sk) {
#endif
Future<Milliseconds> OCSPFetcher::fetchAndStaple(Promise<void>* promise) {
+
+ LOGV2_INFO(577164, "OCSP fetch/staple started");
+
// Generate a new verified X509StoreContext to get our own certificate chain
UniqueX509StoreCtx storeCtx(X509_STORE_CTX_new());
if (!storeCtx) {
return getSSLFailure("Could not create X509 store.");
}
- if (X509_STORE_CTX_init(storeCtx.get(), SSL_CTX_get_cert_store(_context), NULL, NULL) == 0) {
+ X509_STORE* store = _ca ? _ca.get() : SSL_CTX_get_cert_store(_context);
+ if (X509_STORE_CTX_init(storeCtx.get(), store, NULL, NULL) == 0) {
return getSSLFailure("Could not initialize the X509 Store Context.");
}
@@ -1988,10 +2027,11 @@ Future<Milliseconds> OCSPFetcher::fetchAndStaple(Promise<void>* promise) {
auto ocspContext = std::move(swOCSPContext.getValue());
- return dispatchRequests(
- _context, std::move(intermediateCerts), ocspContext, OCSPPurpose::kStaple)
+ return dispatchOCSPRequests(
+ _context, _ca, std::move(intermediateCerts), ocspContext, OCSPPurpose::kStaple)
.onCompletion(
[this, promise](StatusWith<OCSPFetchResponse> swResponse) mutable -> Milliseconds {
+ LOGV2_INFO(577165, "OCSP fetch/staple completion");
stdx::lock_guard<Latch> lock(this->_staplingMutex);
if (_shutdown) {
@@ -2007,6 +2047,7 @@ Future<Milliseconds> OCSPFetcher::fetchAndStaple(Promise<void>* promise) {
});
}
+ LOGV2_INFO(577163, "OCSP response", "status"_attr = swResponse.getStatus());
return _manager->updateOcspStaplingContextWithResponse(std::move(swResponse));
});
}