diff options
author | Spencer Jackson <spencer.jackson@mongodb.com> | 2016-11-08 17:06:17 -0500 |
---|---|---|
committer | Spencer Jackson <spencer.jackson@mongodb.com> | 2016-11-08 17:06:17 -0500 |
commit | 3220495083b0d678578a76591f54ee1d7a5ec5df (patch) | |
tree | 4d94972b3fda0a574450661cb0280d6c399ca105 | |
parent | 1c2c402147d3e6fea734ffd16784cdb1e82da91d (diff) | |
download | mongo-3220495083b0d678578a76591f54ee1d7a5ec5df.tar.gz |
SERVER-26369: Fix shard server crash with encrypted PEMKeyFiles
-rw-r--r-- | jstests/sslSpecial/SERVER-26369.js | 19 | ||||
-rw-r--r-- | src/mongo/util/net/ssl_manager.cpp | 56 |
2 files changed, 63 insertions, 12 deletions
diff --git a/jstests/sslSpecial/SERVER-26369.js b/jstests/sslSpecial/SERVER-26369.js new file mode 100644 index 00000000000..96dd0d15183 --- /dev/null +++ b/jstests/sslSpecial/SERVER-26369.js @@ -0,0 +1,19 @@ +'use strict'; +(function() { + load("jstests/ssl/libs/ssl_helpers.js"); + + var st = new ShardingTest({ + shards: {rs0: {nodes: 1}}, + mongos: 1, + }); + + st.rs0.restart(0, { + sslMode: "allowSSL", + sslPEMKeyFile: "jstests/libs/password_protected.pem", + sslPEMKeyPassword: "qwerty", + sslCAFile: "jstests/libs/ca.pem", + shardsvr: '' + }); + + st.stop(); +})(); diff --git a/src/mongo/util/net/ssl_manager.cpp b/src/mongo/util/net/ssl_manager.cpp index 47ffcf83a01..504e3d55ab5 100644 --- a/src/mongo/util/net/ssl_manager.cpp +++ b/src/mongo/util/net/ssl_manager.cpp @@ -249,7 +249,6 @@ private: mongodbRolesOID.longDescription.c_str()); UniqueSSLContext _serverContext; // SSL context for incoming connections UniqueSSLContext _clientContext; // SSL context for outgoing connections - std::string _password; bool _weakValidation; bool _allowInvalidCertificates; bool _allowInvalidHostnames; @@ -292,6 +291,7 @@ private: * @return bool showing if the function was successful. */ bool _parseAndValidateCertificate(const std::string& keyFile, + const std::string& keyPassword, std::string* subjectName, Date_t* serverNotAfter); @@ -512,18 +512,21 @@ SSLManager::SSLManager(const SSLParams& params, bool isServer) } // pick the certificate for use in outgoing connections, - std::string clientPEM; + std::string clientPEM, clientPassword; if (!isServer || params.sslClusterFile.empty()) { // We are either a client, or a server without a cluster key, // so use the PEM key file, if specified clientPEM = params.sslPEMKeyFile; + clientPassword = params.sslPEMKeyPassword; } else { // We are a server with a cluster key, so use the cluster key file clientPEM = params.sslClusterFile; + clientPassword = params.sslClusterPassword; } if (!clientPEM.empty()) { - if (!_parseAndValidateCertificate(clientPEM, &_sslConfiguration.clientSubjectName, NULL)) { + if (!_parseAndValidateCertificate( + clientPEM, clientPassword, &_sslConfiguration.clientSubjectName, NULL)) { uasserted(16941, "ssl initialization problem"); } } @@ -534,6 +537,7 @@ SSLManager::SSLManager(const SSLParams& params, bool isServer) } if (!_parseAndValidateCertificate(params.sslPEMKeyFile, + params.sslPEMKeyPassword, &_sslConfiguration.serverSubjectName, &_sslConfiguration.serverCertificateExpirationDate)) { uasserted(16942, "ssl initialization problem"); @@ -547,8 +551,10 @@ SSLManager::SSLManager(const SSLParams& params, bool isServer) int SSLManager::password_cb(char* buf, int num, int rwflag, void* userdata) { // Unless OpenSSL misbehaves, num should always be positive fassert(17314, num > 0); - SSLManager* sm = static_cast<SSLManager*>(userdata); - const size_t copied = sm->_password.copy(buf, num - 1); + invariant(userdata); + auto pw = static_cast<const std::string*>(userdata); + + const size_t copied = pw->copy(buf, num - 1); buf[copied] = '\0'; return copied; } @@ -732,6 +738,7 @@ unsigned long long SSLManager::_convertASN1ToMillis(ASN1_TIME* asn1time) { } bool SSLManager::_parseAndValidateCertificate(const std::string& keyFile, + const std::string& keyPassword, std::string* subjectName, Date_t* serverCertificateExpirationDate) { BIO* inBIO = BIO_new(BIO_s_file_internal()); @@ -747,7 +754,11 @@ bool SSLManager::_parseAndValidateCertificate(const std::string& keyFile, return false; } - X509* x509 = PEM_read_bio_X509(inBIO, NULL, &SSLManager::password_cb, this); + // Callback will not manipulate the password, so const_cast is safe. + X509* x509 = PEM_read_bio_X509(inBIO, + NULL, + &SSLManager::password_cb, + const_cast<void*>(static_cast<const void*>(&keyPassword))); if (x509 == NULL) { error() << "cannot retrieve certificate from keyfile: " << keyFile << ' ' << getSSLErrorMessage(ERR_get_error()); @@ -783,26 +794,47 @@ bool SSLManager::_parseAndValidateCertificate(const std::string& keyFile, bool SSLManager::_setupPEM(SSL_CTX* context, const std::string& keyFile, const std::string& password) { - _password = password; - if (SSL_CTX_use_certificate_chain_file(context, keyFile.c_str()) != 1) { error() << "cannot read certificate file: " << keyFile << ' ' << getSSLErrorMessage(ERR_get_error()); return false; } + BIO* inBio = BIO_new(BIO_s_file_internal()); + if (!inBio) { + error() << "failed to allocate BIO object: " << getSSLErrorMessage(ERR_get_error()); + return false; + } + const auto bioGuard = MakeGuard([&inBio]() { BIO_free(inBio); }); + + if (BIO_read_filename(inBio, keyFile.c_str()) <= 0) { + error() << "cannot read PEM key file: " << keyFile << ' ' + << getSSLErrorMessage(ERR_get_error()); + return false; + } + // If password is empty, use default OpenSSL callback, which uses the terminal // to securely request the password interactively from the user. + decltype(&SSLManager::password_cb) password_cb = nullptr; + void* userdata = nullptr; if (!password.empty()) { - SSL_CTX_set_default_passwd_cb_userdata(context, this); - SSL_CTX_set_default_passwd_cb(context, &SSLManager::password_cb); + password_cb = &SSLManager::password_cb; + // SSLManager::password_cb will not manipulate the password, so const_cast is safe. + userdata = const_cast<void*>(static_cast<const void*>(&password)); } - - if (SSL_CTX_use_PrivateKey_file(context, keyFile.c_str(), SSL_FILETYPE_PEM) != 1) { + EVP_PKEY* privateKey = PEM_read_bio_PrivateKey(inBio, nullptr, password_cb, userdata); + if (!privateKey) { error() << "cannot read PEM key file: " << keyFile << ' ' << getSSLErrorMessage(ERR_get_error()); return false; } + const auto privateKeyGuard = MakeGuard([&privateKey]() { EVP_PKEY_free(privateKey); }); + + if (SSL_CTX_use_PrivateKey(context, privateKey) != 1) { + error() << "cannot use PEM key file: " << keyFile << ' ' + << getSSLErrorMessage(ERR_get_error()); + return false; + } // Verify that the certificate and the key go together. if (SSL_CTX_check_private_key(context) != 1) { |