diff options
author | sergey.galtsev <sergey.galtsev@mongodb.com> | 2021-08-16 21:13:10 +0000 |
---|---|---|
committer | Evergreen Agent <no-reply@evergreen.mongodb.com> | 2021-08-16 21:28:37 +0000 |
commit | 085d811dbee92b9b7f71205d3aa1b2ad0bd334c4 (patch) | |
tree | e9de53a1554b1602572037c5b622e1b78ac35169 | |
parent | 0f9e41dd9730b9bb4db3fcee4fef634922337061 (diff) | |
download | mongo-085d811dbee92b9b7f71205d3aa1b2ad0bd334c4.tar.gz |
SERVER-57716: use common CA file for OCSP where clusterCA is present
-rw-r--r-- | jstests/ocsp/lib/ocsp_helpers.js | 2 | ||||
-rw-r--r-- | jstests/ocsp/ocsp_basic.js | 2 | ||||
-rw-r--r-- | jstests/ocsp/ocsp_stapling.js | 25 | ||||
-rw-r--r-- | src/mongo/util/net/ssl_manager_openssl.cpp | 79 |
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)); }); } |