diff options
author | Mark Benvenuto <mark.benvenuto@mongodb.com> | 2019-11-22 17:03:28 +0000 |
---|---|---|
committer | evergreen <evergreen@mongodb.com> | 2019-11-22 17:03:28 +0000 |
commit | 8ff79f256f1e57b32edccae37dc260b342323d26 (patch) | |
tree | f083c0b59d011d1fb05b3ed2cdf46d22b0d813ba /src/mongo | |
parent | e19d92cb777cb87779b614c30b1ed568a6eea088 (diff) | |
download | mongo-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.cpp | 174 |
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, |