summaryrefslogtreecommitdiff
path: root/src/mongo
diff options
context:
space:
mode:
authorMark Benvenuto <mark.benvenuto@mongodb.com>2019-11-22 17:03:28 +0000
committerevergreen <evergreen@mongodb.com>2019-11-22 17:03:28 +0000
commit8ff79f256f1e57b32edccae37dc260b342323d26 (patch)
treef083c0b59d011d1fb05b3ed2cdf46d22b0d813ba /src/mongo
parente19d92cb777cb87779b614c30b1ed568a6eea088 (diff)
downloadmongo-8ff79f256f1e57b32edccae37dc260b342323d26.tar.gz
SERVER-39574 Support intermediate certificates in PEMKeyFile on Windows
(cherry picked from commit aae362fc4d79c9f53c70228d1ba8b966feefaffb)
Diffstat (limited to 'src/mongo')
-rw-r--r--src/mongo/util/net/ssl_manager_windows.cpp174
1 files changed, 124 insertions, 50 deletions
diff --git a/src/mongo/util/net/ssl_manager_windows.cpp b/src/mongo/util/net/ssl_manager_windows.cpp
index 36e552c6e17..65a72ce2573 100644
--- a/src/mongo/util/net/ssl_manager_windows.cpp
+++ b/src/mongo/util/net/ssl_manager_windows.cpp
@@ -685,6 +685,73 @@ StatusWith<std::vector<BYTE>> decodeObject(const char* structType,
return std::move(binaryBlobBuf);
}
+StatusWith<std::vector<UniqueCertificate>> readCAPEMBuffer(StringData buffer) {
+ std::vector<UniqueCertificate> certs;
+
+ // Search the buffer for the various strings that make up a PEM file
+ size_t pos = 0;
+ bool found_one = false;
+
+ while (pos < buffer.size()) {
+ auto swBlob = findPEMBlob(buffer, "CERTIFICATE"_sd, pos, pos != 0);
+
+ // We expect to find at least one certificate
+ if (!swBlob.isOK()) {
+ if (found_one) {
+ return Status::OK();
+ }
+
+ return swBlob.getStatus();
+ }
+
+ found_one = true;
+
+ auto blobBuf = swBlob.getValue();
+
+ if (blobBuf.empty()) {
+ return {std::move(certs)};
+ }
+
+ pos = (blobBuf.rawData() + blobBuf.size()) - buffer.rawData();
+
+ auto swCert = decodePEMBlob(blobBuf);
+ if (!swCert.isOK()) {
+ return swCert.getStatus();
+ }
+
+ auto certBuf = swCert.getValue();
+
+ PCCERT_CONTEXT cert =
+ CertCreateCertificateContext(X509_ASN_ENCODING, certBuf.data(), certBuf.size());
+ if (cert == NULL) {
+ DWORD gle = GetLastError();
+ return Status(ErrorCodes::InvalidSSLConfiguration,
+ str::stream() << "CertCreateCertificateContext failed to decode cert: "
+ << errnoWithDescription(gle));
+ }
+
+ certs.emplace_back(cert);
+ }
+
+ return {std::move(certs)};
+}
+
+Status addCertificatesToStore(HCERTSTORE certStore, std::vector<UniqueCertificate>& certificates) {
+ for (auto& cert : certificates) {
+ BOOL ret =
+ CertAddCertificateContextToStore(certStore, cert.get(), CERT_STORE_ADD_NEW, NULL);
+
+ if (!ret) {
+ DWORD gle = GetLastError();
+ return Status(ErrorCodes::InvalidSSLConfiguration,
+ str::stream() << "CertAddCertificateContextToStore Failed "
+ << errnoWithDescription(gle));
+ }
+ }
+
+ return Status::OK();
+}
+
// Read a Certificate PEM file with a private key from disk
StatusWith<UniqueCertificateWithPrivateKey> readCertPEMFile(StringData fileName,
StringData password) {
@@ -715,10 +782,18 @@ StatusWith<UniqueCertificateWithPrivateKey> readCertPEMFile(StringData fileName,
// file.
auto secondPublicKeyBlobPosition =
buf.find("CERTIFICATE", (publicKeyBlob.rawData() + publicKeyBlob.size()) - buf.data());
+ std::vector<UniqueCertificate> extraCertificates;
if (secondPublicKeyBlobPosition != std::string::npos) {
- return Status(ErrorCodes::InvalidSSLConfiguration,
- str::stream() << "Certificate PEM files should only have one certificate, "
- "intermediate CA certificates belong in the CA file.");
+ // Read in extra certificates
+ StringData extraCertificatesBuffer =
+ StringData(buf).substr(secondPublicKeyBlobPosition - ("-----BEGIN "_sd).size());
+
+ auto swExtraCertificates = readCAPEMBuffer(extraCertificatesBuffer);
+ if (!swExtraCertificates.isOK()) {
+ return swExtraCertificates.getStatus();
+ }
+
+ extraCertificates = std::move(swExtraCertificates.getValue());
}
auto swCert = decodePEMBlob(publicKeyBlob);
@@ -738,6 +813,32 @@ StatusWith<UniqueCertificateWithPrivateKey> readCertPEMFile(StringData fileName,
<< errnoWithDescription(gle));
}
+ UniqueCertificate tempCertHolder(cert);
+
+ HCERTSTORE store = CertOpenStore(
+ CERT_STORE_PROV_MEMORY, 0, NULL, CERT_STORE_DEFER_CLOSE_UNTIL_LAST_FREE_FLAG, NULL);
+ if (store == NULL) {
+ DWORD gle = GetLastError();
+ return Status(ErrorCodes::InvalidSSLConfiguration,
+ str::stream() << "CertOpenStore failed to create memory store: "
+ << errnoWithDescription(gle));
+ }
+
+ UniqueCertStore storeHolder(store);
+
+ // Add the newly created certificate to the memory store, this makes a copy
+ BOOL ret = CertAddCertificateContextToStore(store, cert, CERT_STORE_ADD_NEW, NULL);
+
+ if (!ret) {
+ DWORD gle = GetLastError();
+ return Status(ErrorCodes::InvalidSSLConfiguration,
+ str::stream() << "CertAddCertificateContextToStore Memory Failed "
+ << errnoWithDescription(gle));
+ }
+
+ // Get the certificate from the store so we attach the private key to the cert in the store
+ cert = CertEnumCertificatesInStore(store, NULL);
+
UniqueCertificate certHolder(cert);
std::vector<uint8_t> privateKey;
@@ -807,7 +908,6 @@ StatusWith<UniqueCertificateWithPrivateKey> readCertPEMFile(StringData fileName,
HCRYPTPROV hProv;
std::wstring wstr;
- BOOL ret;
// Create the right Crypto context depending on whether we running in a server or outside.
// See https://msdn.microsoft.com/en-us/library/windows/desktop/aa375195(v=vs.85).aspx
@@ -902,7 +1002,10 @@ StatusWith<UniqueCertificateWithPrivateKey> readCertPEMFile(StringData fileName,
<< errnoWithDescription(gle));
}
- return UniqueCertificateWithPrivateKey(std::move(certHolder), std::move(cryptProvider));
+ // Add the extra certificates into the same certificate store as the certificate
+ addCertificatesToStore(certHolder->hCertStore, extraCertificates);
+
+ return UniqueCertificateWithPrivateKey{std::move(certHolder), std::move(cryptProvider)};
}
Status readCAPEMFile(HCERTSTORE certStore, StringData fileName) {
@@ -914,53 +1017,15 @@ Status readCAPEMFile(HCERTSTORE certStore, StringData fileName) {
std::string buf = std::move(swBuf.getValue());
- // Search the buffer for the various strings that make up a PEM file
- size_t pos = 0;
-
- while (pos < buf.size()) {
- auto swBlob = findPEMBlob(buf, "CERTIFICATE"_sd, pos, pos != 0);
-
- // We expect to find at least one certificate
- if (!swBlob.isOK()) {
- return swBlob.getStatus();
- }
-
- auto blobBuf = swBlob.getValue();
-
- if (blobBuf.empty()) {
- return Status::OK();
- }
-
- pos = (blobBuf.rawData() + blobBuf.size()) - buf.data();
-
- auto swCert = decodePEMBlob(blobBuf);
- if (!swCert.isOK()) {
- return swCert.getStatus();
- }
-
- auto certBuf = swCert.getValue();
-
- PCCERT_CONTEXT cert =
- CertCreateCertificateContext(X509_ASN_ENCODING, certBuf.data(), certBuf.size());
- if (cert == NULL) {
- DWORD gle = GetLastError();
- return Status(ErrorCodes::InvalidSSLConfiguration,
- str::stream() << "CertCreateCertificateContext failed to decode cert: "
- << errnoWithDescription(gle));
- }
- UniqueCertificate certHolder(cert);
-
- BOOL ret = CertAddCertificateContextToStore(certStore, cert, CERT_STORE_ADD_NEW, NULL);
- if (!ret) {
- DWORD gle = GetLastError();
- return Status(ErrorCodes::InvalidSSLConfiguration,
- str::stream() << "CertAddCertificateContextToStore Failed "
- << errnoWithDescription(gle));
- }
+ auto swCerts = readCAPEMBuffer(buf);
+ if (!swCerts.isOK()) {
+ return swCerts.getStatus();
}
- return Status::OK();
+ auto certs = std::move(swCerts.getValue());
+
+ return addCertificatesToStore(certStore, certs);
}
Status readCRLPEMFile(HCERTSTORE certStore, StringData fileName) {
@@ -974,15 +1039,22 @@ Status readCRLPEMFile(HCERTSTORE certStore, StringData fileName) {
// Search the buffer for the various strings that make up a PEM file
size_t pos = 0;
+ bool found_one = false;
while (pos < buf.size()) {
auto swBlob = findPEMBlob(buf, "X509 CRL"_sd, pos, pos != 0);
// We expect to find at least one CRL
if (!swBlob.isOK()) {
+ if (found_one) {
+ return Status::OK();
+ }
+
return swBlob.getStatus();
}
+ found_one = true;
+
auto blobBuf = swBlob.getValue();
if (blobBuf.empty()) {
@@ -1294,6 +1366,8 @@ Status SSLManagerWindows::initSSLContext(SCHANNEL_CRED* cred,
| SCH_CRED_REVOCATION_CHECK_CHAIN // Check certificate revocation
| SCH_CRED_NO_SERVERNAME_CHECK // Do not validate server name against cert
| SCH_CRED_NO_DEFAULT_CREDS // No Default Certificate
+ | SCH_CRED_MEMORY_STORE_CERT // Read intermediate certificates from memory store
+ // associated with client certificate.
| SCH_CRED_MANUAL_CRED_VALIDATION; // Validate Certificate Manually
}
@@ -1622,7 +1696,7 @@ Status validatePeerCertificate(const std::string& remoteHost,
BOOL ret = CertGetCertificateChain(certChainEngine,
cert,
NULL,
- NULL,
+ cert->hCertStore,
&certChainPara,
CERT_CHAIN_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT,
NULL,