summaryrefslogtreecommitdiff
path: root/src/mongo
diff options
context:
space:
mode:
authorShreyas Kalyan <shreyas.kalyan@mongodb.com>2019-11-07 21:18:38 +0000
committerevergreen <evergreen@mongodb.com>2019-11-07 21:18:38 +0000
commit4da9a17daca441f6bb79c671bd4eb98a8c6ad2a1 (patch)
treebf1987e55576dc0c0768939ecc06839279dfba9f /src/mongo
parent1de0b3fa193c6b7aab8e59835bf881f2a02d3da5 (diff)
downloadmongo-4da9a17daca441f6bb79c671bd4eb98a8c6ad2a1.tar.gz
SERVER-42934 Implement and test OCSP Client Validation
Diffstat (limited to 'src/mongo')
-rw-r--r--src/mongo/util/net/SConscript14
-rw-r--r--src/mongo/util/net/ocsp/ocsp_manager.cpp55
-rw-r--r--src/mongo/util/net/ocsp/ocsp_manager.h46
-rw-r--r--src/mongo/util/net/ssl_manager_openssl.cpp378
-rw-r--r--src/mongo/util/net/ssl_options_client.idl1
-rw-r--r--src/mongo/util/net/ssl_parameters.idl6
6 files changed, 499 insertions, 1 deletions
diff --git a/src/mongo/util/net/SConscript b/src/mongo/util/net/SConscript
index 3031cabf263..7922f718f6f 100644
--- a/src/mongo/util/net/SConscript
+++ b/src/mongo/util/net/SConscript
@@ -115,6 +115,19 @@ if not get_option('ssl') == 'off':
],
);
env.Library(
+ target='ocsp',
+ source=[
+ "ocsp/ocsp_manager.cpp",
+ ],
+ LIBDEPS=[
+ '$BUILD_DIR/mongo/base',
+ ],
+ LIBDEPS_PRIVATE=[
+ 'http_client',
+ ]
+ )
+
+ env.Library(
target='ssl_manager',
source=[
"private/ssl_expiration.cpp",
@@ -133,6 +146,7 @@ if not get_option('ssl') == 'off':
'socket',
'ssl_options',
'ssl_types',
+ 'ocsp',
],
LIBDEPS_PRIVATE=[
'$BUILD_DIR/mongo/base/secure_allocator',
diff --git a/src/mongo/util/net/ocsp/ocsp_manager.cpp b/src/mongo/util/net/ocsp/ocsp_manager.cpp
new file mode 100644
index 00000000000..62d88c4858f
--- /dev/null
+++ b/src/mongo/util/net/ocsp/ocsp_manager.cpp
@@ -0,0 +1,55 @@
+/**
+ * Copyright (C) 2019-present MongoDB, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the Server Side Public License, version 1,
+ * as published by MongoDB, Inc.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * Server Side Public License for more details.
+ *
+ * You should have received a copy of the Server Side Public License
+ * along with this program. If not, see
+ * <http://www.mongodb.com/licensing/server-side-public-license>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the Server Side Public License in all respects for
+ * all of the code used other than as permitted herein. If you modify file(s)
+ * with this exception, you may extend this exception to your version of the
+ * file(s), but you are not obligated to do so. If you do not wish to do so,
+ * delete this exception statement from your version. If you delete this
+ * exception statement from all source files in the program, then also delete
+ * it in the license file.
+ */
+
+#include "mongo/platform/basic.h"
+
+#include "mongo/util/net/http_client.h"
+#include "mongo/util/net/ocsp/ocsp_manager.h"
+
+namespace mongo {
+
+StatusWith<std::vector<uint8_t>> ocspRequestStatus(ConstDataRange data, HostAndPort hostAndPort) {
+ auto client = HttpClient::create();
+ if (!client) {
+ return Status(ErrorCodes::InternalErrorNotSupported, "HTTP Client not supported");
+ }
+ client->allowInsecureHTTP(true);
+ client->setTimeout(kOCSPRequestTimeoutSeconds);
+ client->setHeaders({"Content-Type: application/ocsp-request"});
+ auto dataBuilder = client->post("http://" + hostAndPort.toString(), data);
+ if (dataBuilder.size() == 0) {
+ return Status(ErrorCodes::SSLHandshakeFailed, "OCSP Validation Failed");
+ }
+
+ 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
new file mode 100644
index 00000000000..82b96843d4a
--- /dev/null
+++ b/src/mongo/util/net/ocsp/ocsp_manager.h
@@ -0,0 +1,46 @@
+/**
+ * Copyright (C) 2019-present MongoDB, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the Server Side Public License, version 1,
+ * as published by MongoDB, Inc.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * Server Side Public License for more details.
+ *
+ * You should have received a copy of the Server Side Public License
+ * along with this program. If not, see
+ * <http://www.mongodb.com/licensing/server-side-public-license>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the Server Side Public License in all respects for
+ * all of the code used other than as permitted herein. If you modify file(s)
+ * with this exception, you may extend this exception to your version of the
+ * file(s), but you are not obligated to do so. If you do not wish to do so,
+ * delete this exception statement from your version. If you delete this
+ * exception statement from all source files in the program, then also delete
+ * it in the license file.
+ */
+#pragma once
+
+
+#include "mongo/base/data_range.h"
+#include "mongo/util/duration.h"
+#include "mongo/util/net/hostandport.h"
+
+namespace mongo {
+
+constexpr Seconds kOCSPRequestTimeoutSeconds(5);
+
+/**
+ * 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, HostAndPort hostAndPort);
+
+} // namespace mongo \ No newline at end of file
diff --git a/src/mongo/util/net/ssl_manager_openssl.cpp b/src/mongo/util/net/ssl_manager_openssl.cpp
index eb8c70cef4d..83bfb5a53db 100644
--- a/src/mongo/util/net/ssl_manager_openssl.cpp
+++ b/src/mongo/util/net/ssl_manager_openssl.cpp
@@ -56,6 +56,7 @@
#include "mongo/util/log.h"
#include "mongo/util/net/cidr.h"
#include "mongo/util/net/dh_openssl.h"
+#include "mongo/util/net/ocsp/ocsp_manager.h"
#include "mongo/util/net/private/ssl_expiration.h"
#include "mongo/util/net/socket_exception.h"
#include "mongo/util/net/ssl_options.h"
@@ -73,6 +74,7 @@
#include <openssl/asn1t.h>
#include <openssl/dh.h>
#include <openssl/evp.h>
+#include <openssl/ocsp.h>
#include <openssl/ssl.h>
#include <openssl/x509_vfy.h>
#include <openssl/x509v3.h>
@@ -251,6 +253,38 @@ inline int X509_NAME_ENTRY_set(const X509_NAME_ENTRY* ne) {
return ne->set;
}
+inline void X509_OBJECT_free(X509_OBJECT* a) {
+ X509_OBJECT_free_contents(a);
+ OPENSSL_free(a);
+}
+
+X509_OBJECT* X509_STORE_CTX_get_obj_by_subject(X509_STORE_CTX* vs, int type, X509_NAME* name) {
+ X509_OBJECT* ret;
+ ret = (X509_OBJECT*)OPENSSL_malloc(sizeof(X509_OBJECT));
+
+ if (ret == NULL) {
+ return NULL;
+ }
+ if (!X509_STORE_get_by_subject(vs, type, name, ret)) {
+ X509_OBJECT_free(ret);
+ return NULL;
+ }
+ return ret;
+}
+
+X509* X509_OBJECT_get0_X509(const X509_OBJECT* a) {
+ if (a == NULL || a->type != X509_LU_X509) {
+ return NULL;
+ }
+
+ return a->data.x509;
+}
+
+// TODO SERVER-44325 add polyfill for getting verified cert chain
+STACK_OF(X509) * SSL_get0_verified_chain(SSL* s) {
+ return SSL_get_peer_cert_chain(s);
+}
+
#if OPENSSL_VERSION_NUMBER < 0x10002000L
inline bool ASN1_TIME_diff(int*, int*, const ASN1_TIME*, const ASN1_TIME*) {
return false;
@@ -1362,6 +1396,343 @@ StatusWith<TLSVersion> mapTLSVersion(SSL* conn) {
}
}
+struct X509_STORE_CTXFree {
+ void operator()(X509_STORE_CTX* store) noexcept {
+ if (store) {
+ X509_STORE_CTX_free(store);
+ }
+ }
+};
+
+using UniqueX509StoreCtx = std::unique_ptr<X509_STORE_CTX, X509_STORE_CTXFree>;
+
+struct OCSP_REQUESTFree {
+ void operator()(OCSP_REQUEST* req) noexcept {
+ if (req) {
+ OCSP_REQUEST_free(req);
+ }
+ }
+};
+
+using UniqueOCSPRequest = std::unique_ptr<OCSP_REQUEST, OCSP_REQUESTFree>;
+
+struct OCSP_RESPONSEFree {
+ void operator()(OCSP_RESPONSE* resp) noexcept {
+ if (resp) {
+ OCSP_RESPONSE_free(resp);
+ }
+ }
+};
+
+using UniqueOCSPResponse = std::unique_ptr<OCSP_RESPONSE, OCSP_RESPONSEFree>;
+
+struct X509_OBJECTFree {
+ void operator()(X509_OBJECT* obj) noexcept {
+ if (obj) {
+ X509_OBJECT_free(obj);
+ }
+ }
+};
+
+using UniqueX509Object = std::unique_ptr<X509_OBJECT, X509_OBJECTFree>;
+
+struct OCSP_CERTIDFree {
+ void operator()(OCSP_CERTID* id) {
+ if (id) {
+ OCSP_CERTID_free(id);
+ }
+ }
+};
+
+using UniqueCertId = std::unique_ptr<OCSP_CERTID, OCSP_CERTIDFree>;
+
+struct OpenSSLStringStackFree {
+ void operator()(STACK_OF(OPENSSL_STRING) * aia) {
+ if (aia) {
+ X509_email_free(aia);
+ }
+ }
+};
+
+using UniqueOpenSSLStringStack = std::unique_ptr<STACK_OF(OPENSSL_STRING), OpenSSLStringStackFree>;
+
+struct OCSPBasicRespFree {
+ void operator()(OCSP_BASICRESP* resp) {
+ if (resp) {
+ OCSP_BASICRESP_free(resp);
+ }
+ }
+};
+
+using UniqueOcspBasicResp = std::unique_ptr<OCSP_BASICRESP, OCSPBasicRespFree>;
+
+struct OCSPCertIDCompare {
+ bool operator()(const UniqueCertId& id1, const UniqueCertId& id2) const {
+ if (OCSP_id_cmp(id1.get(), id2.get()) > 0) {
+ return true;
+ }
+ return false;
+ }
+};
+
+using OCSPCertIDSet = std::set<UniqueCertId, OCSPCertIDCompare>;
+
+Status getSSLFailure(StringData errorMsg) {
+ return Status(ErrorCodes::SSLHandshakeFailed,
+ str::stream() << "SSL peer certificate revocation status checking failed: "
+ << errorMsg << " "
+ << SSLManagerInterface::getSSLErrorMessage(ERR_get_error()));
+}
+
+struct OCSPRequestAndIDs {
+ UniqueOCSPRequest request;
+ OCSPCertIDSet certIDs;
+};
+
+/**
+ * This function takes an individual certificate and adds its OCSP certificate ID
+ * to the ocspRequestMap. See comment in extractOcspUris for details.
+ */
+Status addOCSPUrlToMap(SSL* conn,
+ X509* cert,
+ std::map<HostAndPort, OCSPRequestAndIDs>& ocspRequestMap,
+ OCSPCertIDSet& uniqueCertIds) {
+
+ UniqueOpenSSLStringStack aiaOCSP(X509_get1_ocsp(cert));
+ if (aiaOCSP) {
+ // Look in the certificate store for the certificate that issued cert
+ UniqueX509StoreCtx storeCtx(X509_STORE_CTX_new());
+ if (!storeCtx) {
+ return getSSLFailure("Could not create X509 store.");
+ }
+ X509_STORE_CTX_init(
+ storeCtx.get(), SSL_CTX_get_cert_store(SSL_get_SSL_CTX(conn)), NULL, NULL);
+
+ UniqueX509Object obj(X509_STORE_CTX_get_obj_by_subject(
+ storeCtx.get(), X509_LU_X509, X509_get_issuer_name(cert)));
+ if (obj == nullptr) {
+ return getSSLFailure("Could not get X509 Object from store.");
+ }
+
+ // Iterate through all the values in the Authority Information Access extension in
+ // the certificate to get the location of the OCSP responder.
+ for (int i = 0; i < sk_OPENSSL_STRING_num(aiaOCSP.get()); i++) {
+ int useSSL = 0;
+ char *host, *port, *path;
+ auto OCSPStrGuard = makeGuard([&] {
+ if (host) {
+ OPENSSL_free(host);
+ }
+ if (port) {
+ OPENSSL_free(port);
+ }
+ if (path) {
+ OPENSSL_free(path);
+ }
+ });
+ if (!OCSP_parse_url(
+ sk_OPENSSL_STRING_value(aiaOCSP.get(), i), &host, &port, &path, &useSSL)) {
+ return getSSLFailure("Could not parse AIA url.");
+ }
+
+ HostAndPort hostAndPort(str::stream() << host << ":" << port);
+ UniqueCertId certID(OCSP_cert_to_id(nullptr, cert, X509_OBJECT_get0_X509(obj.get())));
+ if (!certID) {
+ return getSSLFailure("Could not get certificate ID for Map.");
+ }
+
+ UniqueCertId certIDForArray(
+ OCSP_cert_to_id(nullptr, cert, X509_OBJECT_get0_X509(obj.get())));
+ if (certIDForArray == nullptr) {
+ return getSSLFailure("Could not get certificate ID for Array.");
+ }
+
+ OCSPRequestAndIDs reqAndIDs{UniqueOCSPRequest(OCSP_REQUEST_new()), OCSPCertIDSet()};
+
+ auto [mapIter, _] = ocspRequestMap.try_emplace(hostAndPort, std::move(reqAndIDs));
+
+ OCSP_request_add0_id(mapIter->second.request.get(), certID.release());
+ mapIter->second.certIDs.insert(std::move(certIDForArray));
+ }
+
+ UniqueCertId certID(OCSP_cert_to_id(nullptr, cert, X509_OBJECT_get0_X509(obj.get())));
+ if (!certID) {
+ return getSSLFailure("Could not get certificate ID for Set.");
+ }
+ uniqueCertIds.insert(std::move(certID));
+ }
+ return Status::OK();
+}
+
+struct OCSPContext {
+ std::map<HostAndPort, OCSPRequestAndIDs> ocspRequestMap;
+ OCSPCertIDSet uniqueCertIds;
+};
+
+/**
+ * Iterates over a list of intermediate certificates and the peer certificate
+ * in a chain of X509 certificates
+ * and adds the OCSP certificate ID to the correct OCSP Request object.
+ * OCSP Request objects need to be separated by the specific OCSP responder URI.
+ */
+StatusWith<OCSPContext> extractOcspUris(SSL* conn,
+ X509* peerCert,
+ STACK_OF(X509) * intermediateCerts) {
+
+ std::map<HostAndPort, OCSPRequestAndIDs> ocspRequestMap;
+ OCSPCertIDSet uniqueCertIds;
+
+ auto status = addOCSPUrlToMap(conn, peerCert, ocspRequestMap, uniqueCertIds);
+ if (!status.isOK()) {
+ return status;
+ }
+
+ for (int i = 0; i < sk_X509_num(intermediateCerts); i++) {
+ auto cert = sk_X509_value(intermediateCerts, i);
+ status = addOCSPUrlToMap(conn, cert, ocspRequestMap, uniqueCertIds);
+ if (!status.isOK()) {
+ return status;
+ }
+ }
+ return OCSPContext{std::move(ocspRequestMap), std::move(uniqueCertIds)};
+}
+
+Status verifyOCSP(SSL* conn, X509* peerCert) {
+ UniqueOpenSSLStringStack aiaOCSP(X509_get1_ocsp(peerCert));
+ if (aiaOCSP) {
+ // OCSP checks. AIA stands for the Authority Information Access x509 extension.
+ ERR_clear_error();
+ STACK_OF(X509)* intermediateCerts = SSL_get0_verified_chain(conn);
+
+ auto swOCSPContext = extractOcspUris(conn, peerCert, intermediateCerts);
+ if (!swOCSPContext.isOK()) {
+ return swOCSPContext.getStatus();
+ }
+ auto& [ocspRequestMap, uniqueCertIds] = swOCSPContext.getValue();
+
+ // This loops over all the unique OCSP responders identified
+ // by the certificates in the chain
+ for (auto& [host, ocspContext] : ocspRequestMap) {
+ auto& [ocspReq, certIDs] = ocspContext;
+
+ // Decompose the OCSP request into a DER encoded OCSP request
+ auto len = i2d_OCSP_REQUEST(ocspReq.get(), nullptr);
+ std::vector<uint8_t> buffer;
+ if (len > 0) {
+ buffer.resize(len);
+ auto bufferData = buffer.data();
+ i2d_OCSP_REQUEST(ocspReq.get(), &bufferData);
+ } else {
+ return getSSLFailure("Could not decode response from responder.");
+ }
+
+ // Query the OCSP responder
+ auto responseData = ocspRequestStatus(buffer, host);
+ if (!responseData.isOK()) {
+ if (responseData.getStatus().code() == ErrorCodes::InternalErrorNotSupported) {
+ warning() << "Could not perform OCSP validation: " << responseData.getStatus();
+ return Status::OK();
+ }
+ return responseData.getStatus();
+ }
+ std::vector<uint8_t> respDataVector(std::move(responseData.getValue()));
+ const uint8_t* respDataPtr = respDataVector.data();
+
+ // Convert the Response back to a OpenSSL known format
+ UniqueOCSPResponse response(
+ d2i_OCSP_RESPONSE(nullptr, &respDataPtr, respDataVector.size()));
+
+ if (response == nullptr) {
+ return getSSLFailure("Could not retrieve OCSP Response.");
+ }
+
+ // Read the overall status of the OCSP response
+ int responseStatus = OCSP_response_status(response.get());
+ if (responseStatus != OCSP_RESPONSE_STATUS_SUCCESSFUL) {
+ if (responseStatus == OCSP_RESPONSE_STATUS_MALFORMEDREQUEST ||
+ responseStatus == OCSP_RESPONSE_STATUS_UNAUTHORIZED ||
+ responseStatus == OCSP_RESPONSE_STATUS_SIGREQUIRED) {
+ return getSSLFailure(
+ str::stream()
+ << "Error querying the OCSP responder, issue with OCSP request. "
+ << "Response Status: " << responseStatus);
+ } else if (responseStatus == OCSP_RESPONSE_STATUS_TRYLATER ||
+ responseStatus == OCSP_RESPONSE_STATUS_INTERNALERROR) {
+ // TODO: SERVER-42936 Add support for tlsAllowInvalidCertificates
+ return getSSLFailure(
+ str::stream()
+ << "Error querying the OCSP responder, an error occured in the "
+ << "responder itself. Response Status: " << responseStatus);
+ } else {
+ return getSSLFailure(str::stream() << "Error querying the OCSP responder. "
+ << "Response Status: " << responseStatus);
+ }
+ }
+
+ // Owned by the UniqueOCSPResponse object
+ UniqueOcspBasicResp basicResponse(OCSP_response_get1_basic(response.get()));
+ if (!basicResponse) {
+ return getSSLFailure("incomplete OCSP response.");
+ }
+
+ SSL_CTX* ssl_ctx = SSL_get_SSL_CTX(conn);
+ X509_STORE* store = SSL_CTX_get_cert_store(ssl_ctx);
+
+ // 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
+ // to form a chain from the signer certificate to the trusted CA in the store.
+ if (OCSP_basic_verify(basicResponse.get(), intermediateCerts, store, 0) != 0) {
+ return getSSLFailure("Failed to verify signature from OCSP response.");
+ }
+
+ // TODO SERVER-42938 the updated time will be implemented in the cache
+ ASN1_GENERALIZEDTIME* earliestNextUpdate = nullptr;
+
+ // Iterate over the list of certificateIDs that went into the
+ // OCSP request for this specific responder.
+ for (const auto& id : certIDs) {
+ int reason, status;
+ // TODO SERVER-42938 the updated time will be implemented in the cache
+ ASN1_GENERALIZEDTIME *revtime, *thisupd, *nextupd;
+
+ if (OCSP_resp_find_status(basicResponse.get(),
+ id.get(),
+ &status,
+ &reason,
+ &revtime,
+ &thisupd,
+ &nextupd) != 1) {
+ return getSSLFailure("Could not find ID in OCSP Response.");
+ }
+
+ if (status == V_OCSP_CERTSTATUS_REVOKED) {
+ // TODO SERVER-42938 include revtime in error message
+ return getSSLFailure(str::stream()
+ << "OCSP Certificate Status: Revoked. Reason: "
+ << OCSP_crl_reason_str(reason));
+ } else if (status != V_OCSP_CERTSTATUS_GOOD) {
+ return getSSLFailure(
+ str::stream() << "Unexpected OCSP Certificate Status. Reason: " << status);
+ }
+
+ if (earliestNextUpdate) {
+ earliestNextUpdate =
+ (earliestNextUpdate < nextupd) ? earliestNextUpdate : nextupd;
+ } else {
+ earliestNextUpdate = nextupd;
+ }
+
+ uniqueCertIds.erase(id);
+ }
+
+ if (uniqueCertIds.empty()) {
+ break;
+ }
+ }
+ }
+ return Status::OK();
+}
+
StatusWith<SSLPeerInfo> SSLManagerOpenSSL::parseAndValidatePeerCertificate(
SSL* conn,
boost::optional<std::string> sni,
@@ -1410,6 +1781,13 @@ StatusWith<SSLPeerInfo> SSLManagerOpenSSL::parseAndValidatePeerCertificate(
}
}
+ if (sslOCSPEnabled) {
+ auto status = verifyOCSP(conn, peerCert);
+ if (!status.isOK()) {
+ return status;
+ }
+ }
+
// TODO: check optional cipher restriction, using cert.
auto peerSubject = getCertificateSubjectX509Name(peerCert);
LOG(2) << "Accepted TLS connection from peer: " << peerSubject;
diff --git a/src/mongo/util/net/ssl_options_client.idl b/src/mongo/util/net/ssl_options_client.idl
index 2c6c41f2d60..e2bc6cad487 100644
--- a/src/mongo/util/net/ssl_options_client.idl
+++ b/src/mongo/util/net/ssl_options_client.idl
@@ -124,4 +124,3 @@ configs:
deprecated_short_name: sslDisabledProtocols
arg_vartype: String
requires: tls
-
diff --git a/src/mongo/util/net/ssl_parameters.idl b/src/mongo/util/net/ssl_parameters.idl
index 4fb4df253d1..bad759341e1 100644
--- a/src/mongo/util/net/ssl_parameters.idl
+++ b/src/mongo/util/net/ssl_parameters.idl
@@ -46,6 +46,12 @@ server_parameters:
description: "Do not send a client certificate when establishing intra-cluster connections"
set_at: startup
cpp_varname: "sslGlobalParams.tlsWithholdClientCertificate"
+ ocspEnabled:
+ description: "Enable OCSP"
+ set_at: startup
+ default: false
+ cpp_vartype: bool
+ cpp_varname: "sslOCSPEnabled"
opensslCipherConfig:
description: "Cipher configuration string for OpenSSL based TLS connections"