diff options
author | Andreas Nilsson <andreas.nilsson@10gen.com> | 2013-06-20 16:44:41 +0100 |
---|---|---|
committer | Andreas Nilsson <andreas.nilsson@10gen.com> | 2013-07-10 16:04:01 +0100 |
commit | 6685c058c7bf4444f14fcae61f56b7783b5edebe (patch) | |
tree | 41bc78ae1eef54f8b6419ef255b3c2b765a937c2 | |
parent | 9c222f4ffb1eac72d54b8c1237c8816b115af43b (diff) | |
download | mongo-6685c058c7bf4444f14fcae61f56b7783b5edebe.tar.gz |
SERVER-7455 keyfile replacement, command line parameters and upgrade
-rw-r--r-- | src/mongo/client/dbclient.cpp | 45 | ||||
-rw-r--r-- | src/mongo/client/dbclientinterface.h | 2 | ||||
-rw-r--r-- | src/mongo/db/auth/security_key.cpp | 37 | ||||
-rw-r--r-- | src/mongo/db/auth/security_key.h | 9 | ||||
-rw-r--r-- | src/mongo/db/cmdline.cpp | 24 | ||||
-rw-r--r-- | src/mongo/db/cmdline.h | 6 | ||||
-rw-r--r-- | src/mongo/db/commands/authentication_commands.cpp | 17 | ||||
-rw-r--r-- | src/mongo/db/commands/isself.cpp | 2 | ||||
-rw-r--r-- | src/mongo/db/db.cpp | 10 | ||||
-rw-r--r-- | src/mongo/db/initialize_server_global_state.cpp | 15 | ||||
-rw-r--r-- | src/mongo/db/repl/connections.h | 5 | ||||
-rw-r--r-- | src/mongo/db/repl/oplogreader.cpp | 3 | ||||
-rw-r--r-- | src/mongo/db/repl/sync_source_feedback.cpp | 8 | ||||
-rw-r--r-- | src/mongo/s/server.cpp | 10 | ||||
-rw-r--r-- | src/mongo/s/shard.cpp | 3 | ||||
-rw-r--r-- | src/mongo/util/net/sock.cpp | 2 | ||||
-rw-r--r-- | src/mongo/util/net/ssl_manager.cpp | 265 | ||||
-rw-r--r-- | src/mongo/util/net/ssl_manager.h | 12 |
18 files changed, 334 insertions, 141 deletions
diff --git a/src/mongo/client/dbclient.cpp b/src/mongo/client/dbclient.cpp index f0668c4f46c..ff42ea2c180 100644 --- a/src/mongo/client/dbclient.cpp +++ b/src/mongo/client/dbclient.cpp @@ -566,27 +566,24 @@ namespace mongo { errmsg, _authMongoCR(userSource, user, password, errmsg, digestPassword)); } +#ifdef MONGO_SSL else if (mechanism == StringData("MONGODB-X509", StringData::LiteralTag())){ std::string userSource; uassertStatusOK(bsonExtractStringField(params, saslCommandUserSourceFieldName, &userSource)); - std::string user; - uassertStatusOK(bsonExtractStringField(params, - saslCommandUserFieldName, - &user)); - std::string errmsg; uassert(ErrorCodes::AuthenticationFailed, errmsg, - _authMongoX509(userSource, user, errmsg)); + _authX509(userSource, getSSLManager()->getClientSubjectName(), errmsg)); } +#endif else if (saslClientAuthenticate != NULL) { uassertStatusOK(saslClientAuthenticate(this, params)); } else { uasserted(ErrorCodes::BadValue, - "SASL authentication support not compiled into client library."); + mechanism + " mechanism support not compiled into client library."); } }; @@ -614,23 +611,6 @@ namespace mongo { } } - bool DBClientWithCommands::_authMongoX509(const string&dbname, - const string &username, - string& errmsg){ - BSONObj authCmd; - BSONObjBuilder cmdBuilder; - cmdBuilder << "authenticate" << 1 << "mechanism" << "MONGODB-X509" << "user" << username; - authCmd = cmdBuilder.done(); - - BSONObj info; - if( runCommand(dbname, authCmd, info) ) { - return true; - } - - errmsg = info.toString(); - return false; - } - bool DBClientWithCommands::_authMongoCR(const string &dbname, const string &username, const string &password_text, @@ -679,6 +659,23 @@ namespace mongo { return false; } + bool DBClientWithCommands::_authX509(const string&dbname, + const string &username, + string& errmsg){ + BSONObj authCmd; + BSONObjBuilder cmdBuilder; + cmdBuilder << "authenticate" << 1 << "mechanism" << "MONGODB-X509" << "user" << username; + authCmd = cmdBuilder.done(); + + BSONObj info; + if( runCommand(dbname, authCmd, info) ) { + return true; + } + + errmsg = info.toString(); + return false; + } + void DBClientWithCommands::logout(const string &dbname, BSONObj& info) { runCommand(dbname, BSON("logout" << 1), info); } diff --git a/src/mongo/client/dbclientinterface.h b/src/mongo/client/dbclientinterface.h index 671ba5bfc7f..2604ef973ef 100644 --- a/src/mongo/client/dbclientinterface.h +++ b/src/mongo/client/dbclientinterface.h @@ -988,7 +988,7 @@ namespace mongo { * has already been communicated automatically as part of the connect call. * Returns false on failure and set "errmsg". */ - bool _authMongoX509(const string&dbname, + bool _authX509(const string&dbname, const string &username, string& errmsg); diff --git a/src/mongo/db/auth/security_key.cpp b/src/mongo/db/auth/security_key.cpp index d12efa2617c..1e74b683937 100644 --- a/src/mongo/db/auth/security_key.cpp +++ b/src/mongo/db/auth/security_key.cpp @@ -26,19 +26,37 @@ #include "mongo/db/auth/privilege.h" #include "mongo/client/sasl_client_authenticate.h" +static bool authParamsSet = false; + namespace mongo { + bool isInternalAuthSet() { + return authParamsSet; + } + bool setInternalUserAuthParams(BSONObj authParams) { - internalSecurity.authParams = authParams.copy(); - return true; + if (!isInternalAuthSet()) { + internalSecurity.authParams = authParams.copy(); + authParamsSet = true; + return true; + } + else { + log() << "Internal auth params have already been set" << endl; + return false; + } } bool authenticateInternalUser(DBClientWithCommands* conn){ + if (!isInternalAuthSet()) { + log() << "ERROR: No authentication params set for internal user" << endl; + return false; + } try { conn->auth(internalSecurity.authParams); return true; } catch(const UserException& ex) { - log() << "can't authenticate as internal user, error: " << ex.what() << endl; + log() << "can't authenticate to " << conn->toString() << " as internal user, error: " + << ex.what() << endl; return false; } } @@ -115,12 +133,13 @@ namespace mongo { DBClientConnection conn; internalSecurity.pwd = conn.createPasswordDigest(internalSecurity.user, str); - setInternalUserAuthParams(BSON(saslCommandMechanismFieldName << "MONGODB-CR" << - saslCommandUserSourceFieldName << "local" << - saslCommandUserFieldName << internalSecurity.user << - saslCommandPasswordFieldName << internalSecurity.pwd << - saslCommandDigestPasswordFieldName << false)); - + if (cmdLine.clusterAuthMode == "keyfile" || cmdLine.clusterAuthMode == "sendKeyfile") { + setInternalUserAuthParams(BSON(saslCommandMechanismFieldName << "MONGODB-CR" << + saslCommandUserSourceFieldName << "local" << + saslCommandUserFieldName << internalSecurity.user << + saslCommandPasswordFieldName << internalSecurity.pwd << + saslCommandDigestPasswordFieldName << false)); + } return true; } diff --git a/src/mongo/db/auth/security_key.h b/src/mongo/db/auth/security_key.h index df1740c6888..8ee67328c24 100644 --- a/src/mongo/db/auth/security_key.h +++ b/src/mongo/db/auth/security_key.h @@ -22,9 +22,14 @@ namespace mongo { /** + * @return true if internal authentication parameters has been set up + */ + extern bool isInternalAuthSet(); + + /** * This method initializes the internalSecurity object with authentication - * credentials to be used by authenticateInternalUser - * + * credentials to be used by authenticateInternalUser. This method should + * only be called once when setting up authentication method for the system. */ extern bool setInternalUserAuthParams(BSONObj authParams); diff --git a/src/mongo/db/cmdline.cpp b/src/mongo/db/cmdline.cpp index 5b271439f15..cbfac4703de 100644 --- a/src/mongo/db/cmdline.cpp +++ b/src/mongo/db/cmdline.cpp @@ -88,6 +88,9 @@ namespace { ("setParameter", po::value< std::vector<std::string> >()->composing(), "Set a configurable parameter") ("httpinterface", "enable http interface") + ("clusterAuthMode", po::value<std::string>(&cmdLine.clusterAuthMode), + "Authentication mode used for cluster authentication." + " Alternatives are (keyfile|sendKeyfile|sendX509|x509)") #ifndef _WIN32 ("nounixsocket", "disable listening on unix sockets") ("unixSocketPrefix", po::value<string>(), "alternative directory for UNIX domain sockets (defaults to /tmp)") @@ -415,6 +418,9 @@ namespace { } } } + if (!params.count("clusterAuthMode")){ + cmdLine.clusterAuthMode = "keyfile"; + } #ifdef MONGO_SSL if (params.count("sslWeakCertificateValidation")) { @@ -451,6 +457,24 @@ namespace { log() << "need to enable sslOnNormalPorts" << endl; return false; } + if (cmdLine.clusterAuthMode == "sendKeyfile" || + cmdLine.clusterAuthMode == "sendX509" || + cmdLine.clusterAuthMode == "x509") { + if (!cmdLine.sslOnNormalPorts){ + log() << "need to enable sslOnNormalPorts" << endl; + return false; + } + } + else if (cmdLine.clusterAuthMode != "keyfile") { + log() << "unsupported value for clusterAuthMode " << cmdLine.clusterAuthMode << endl; + return false; + } +#else // ifdef MONGO_SSL + // Keyfile is currently the only supported value if not using SSL + if (cmdLine.clusterAuthMode != "keyfile") { + log() << "unsupported value for clusterAuthMode " << cmdLine.clusterAuthMode << endl; + return false; + } #endif return true; diff --git a/src/mongo/db/cmdline.h b/src/mongo/db/cmdline.h index c5c0d9e06b1..8cbe05d2c52 100644 --- a/src/mongo/db/cmdline.h +++ b/src/mongo/db/cmdline.h @@ -126,12 +126,12 @@ namespace mongo { std::string logpath; // Path to log file, if logging to a file; otherwise, empty. bool logAppend; // True if logging to a file in append mode. bool logWithSyslog; // True if logging to syslog; must not be set if logpath is set. + std::string clusterAuthMode; // Cluster authentication mode #ifndef _WIN32 ProcessId parentProc; // --fork pid of initial process ProcessId leaderProc; // --fork pid of leader process #endif - #ifdef MONGO_SSL bool sslOnNormalPorts; // --sslOnNormalPorts std::string sslPEMKeyFile; // --sslPEMKeyFile @@ -140,8 +140,8 @@ namespace mongo { std::string sslClusterPassword; // --sslInternalKeyPassword std::string sslCAFile; // --sslCAFile std::string sslCRLFile; // --sslCRLFile - bool sslWeakCertificateValidation; - bool sslFIPSMode; + bool sslWeakCertificateValidation; // --sslWeakCertificateValidation + bool sslFIPSMode; // --sslFIPSMode #endif /** diff --git a/src/mongo/db/commands/authentication_commands.cpp b/src/mongo/db/commands/authentication_commands.cpp index 1f89d79c0d9..fbae655b7ab 100644 --- a/src/mongo/db/commands/authentication_commands.cpp +++ b/src/mongo/db/commands/authentication_commands.cpp @@ -119,6 +119,12 @@ namespace mongo { string user = cmdObj.getStringField("user"); + if (user == internalSecurity.user && cmdLine.clusterAuthMode == "x509") { + errmsg = "Mechanism x509 is required for internal cluster authentication"; + result.append(saslCommandCodeFieldName, ErrorCodes::AuthenticationFailed); + return false; + } + if (!_areNonceAuthenticateCommandsEnabled) { // SERVER-8461, MONGODB-CR must be enabled for authenticating the internal user, so that // cluster members may communicate with each other. @@ -237,15 +243,20 @@ namespace mongo { return false; } else { - StringData srvSubjectName = getSSLManager()->getSubjectName(); + StringData srvSubjectName = getSSLManager()->getServerSubjectName(); StringData srvClusterId = srvSubjectName.substr(0, srvSubjectName.find("/CN")+1); StringData peerClusterId = subjectName.substr(0, subjectName.find("/CN")+1); - // Handle internal cluster member + // Handle internal cluster member auth, only applies to server-server connections if (srvClusterId == peerClusterId) { + if (cmdLine.clusterAuthMode == "keyfile") { + errmsg = "X509 authentication is not allowed for cluster authentication"; + result.append(saslCommandCodeFieldName, ErrorCodes::AuthenticationFailed); + return false; + } authorizationSession->grantInternalAuthorization(UserName(user, "$external")); } - // Handle normal client authentication + // Handle normal client authentication, only applies to client-server connections else { Principal* principal = new Principal(UserName(user, "$external")); principal->setImplicitPrivilegeAcquisition(true); diff --git a/src/mongo/db/commands/isself.cpp b/src/mongo/db/commands/isself.cpp index b6f7e8d6d85..841863abf2a 100644 --- a/src/mongo/db/commands/isself.cpp +++ b/src/mongo/db/commands/isself.cpp @@ -250,7 +250,7 @@ namespace mongo { return false; } - if (AuthorizationManager::isAuthEnabled() && !cmdLine.keyFile.empty() ) { + if (AuthorizationManager::isAuthEnabled() && isInternalAuthSet()) { if (!authenticateInternalUser(&conn)) { return false; } diff --git a/src/mongo/db/db.cpp b/src/mongo/db/db.cpp index 51a1c61fb4b..bc2642b63ea 100644 --- a/src/mongo/db/db.cpp +++ b/src/mongo/db/db.cpp @@ -64,6 +64,7 @@ #include "mongo/util/exception_filter_win32.h" #include "mongo/util/file_allocator.h" #include "mongo/util/net/message_server.h" +#include "mongo/util/net/ssl_manager.h" #include "mongo/util/ntservice.h" #include "mongo/util/ramlog.h" #include "mongo/util/stacktrace.h" @@ -1302,6 +1303,15 @@ MONGO_INITIALIZER(CreateAuthorizationManager)(InitializerContext* context) { return Status::OK(); } +#ifdef MONGO_SSL +MONGO_INITIALIZER_GENERAL(setSSLManagerType, + MONGO_NO_PREREQUISITES, + ("SSLManager"))(InitializerContext* context) { + isSSLServer = true; + return Status::OK(); +} +#endif + static int mongoDbMain(int argc, char* argv[], char **envp) { static StaticObserver staticObserver; diff --git a/src/mongo/db/initialize_server_global_state.cpp b/src/mongo/db/initialize_server_global_state.cpp index 125ab6fccf1..2276b165e47 100644 --- a/src/mongo/db/initialize_server_global_state.cpp +++ b/src/mongo/db/initialize_server_global_state.cpp @@ -28,6 +28,7 @@ #endif #include "mongo/base/init.h" +#include "mongo/client/sasl_client_authenticate.h" #include "mongo/db/auth/authorization_manager.h" #include "mongo/db/auth/security_key.h" #include "mongo/db/cmdline.h" @@ -41,6 +42,7 @@ #include "mongo/platform/process_id.h" #include "mongo/util/log.h" #include "mongo/util/net/listen.h" +#include "mongo/util/net/ssl_manager.h" #include "mongo/util/processinfo.h" namespace fs = boost::filesystem; @@ -241,8 +243,7 @@ namespace mongo { writePidFile(cmdLine.pidFile); } - if (!cmdLine.keyFile.empty()) { - + if (!cmdLine.keyFile.empty() && cmdLine.clusterAuthMode != "x509") { if (!setUpSecurityKey(cmdLine.keyFile)) { // error message printed in setUpPrivateKey return false; @@ -250,7 +251,15 @@ namespace mongo { AuthorizationManager::setAuthEnabled(true); } - + +#ifdef MONGO_SSL + if (cmdLine.clusterAuthMode == "x509" || cmdLine.clusterAuthMode == "sendX509") { + setInternalUserAuthParams(BSON(saslCommandMechanismFieldName << "MONGODB-X509" << + saslCommandUserSourceFieldName << "$external" << + saslCommandUserFieldName << + getSSLManager()->getClientSubjectName())); + } +#endif return true; } diff --git a/src/mongo/db/repl/connections.h b/src/mongo/db/repl/connections.h index da780ccda36..f005d09f0b1 100644 --- a/src/mongo/db/repl/connections.h +++ b/src/mongo/db/repl/connections.h @@ -129,10 +129,7 @@ namespace mongo { // be rebooting. if their file has to change, they'll be rebooted so the // connection created above will go dead, reconnect, and reauth. if (AuthorizationManager::isAuthEnabled()) { - if (!authenticateInternalUser(connInfo->cc.get())) { - log() << "could not authenticate against " << _hostport << ", " << err << rsLog; - return false; - } + return authenticateInternalUser(connInfo->cc.get()); } return true; diff --git a/src/mongo/db/repl/oplogreader.cpp b/src/mongo/db/repl/oplogreader.cpp index 994ea872a30..ea55ada5c11 100644 --- a/src/mongo/db/repl/oplogreader.cpp +++ b/src/mongo/db/repl/oplogreader.cpp @@ -58,9 +58,10 @@ namespace mongo { return false; } - if (internalSecurity.pwd.length() > 0) { + if (isInternalAuthSet()) { return authenticateInternalUser(conn); } + BSONObj user; { Client::ReadContext ctxt("local."); diff --git a/src/mongo/db/repl/sync_source_feedback.cpp b/src/mongo/db/repl/sync_source_feedback.cpp index b26a7a644b9..21a7bffd3c4 100644 --- a/src/mongo/db/repl/sync_source_feedback.cpp +++ b/src/mongo/db/repl/sync_source_feedback.cpp @@ -47,9 +47,10 @@ namespace mongo { return false; } - if (internalSecurity.pwd.length() > 0) { - return authenticateInternalUser(_connection.get()); + if (isInternalAuthSet()) { + return authenticateInternalUser(_connection.get()); } + BSONObj user; { Client::ReadContext ctxt("local."); @@ -66,7 +67,8 @@ namespace mongo { massert(16889, "bad user object? [1]", !u.empty()); massert(16887, "bad user object? [2]", !p.empty()); - string err; + std::string err; + if( !_connection->auth("local", u.c_str(), p.c_str(), err, false) ) { log() << "replauthenticate: can't authenticate to master server, user:" << u << endl; return false; diff --git a/src/mongo/s/server.cpp b/src/mongo/s/server.cpp index 5d6d18c7c8d..3960dd97eee 100644 --- a/src/mongo/s/server.cpp +++ b/src/mongo/s/server.cpp @@ -49,6 +49,7 @@ #include "mongo/util/log.h" #include "mongo/util/net/message.h" #include "mongo/util/net/message_server.h" +#include "mongo/util/net/ssl_manager.h" #include "mongo/util/ntservice.h" #include "mongo/util/processinfo.h" #include "mongo/util/ramlog.h" @@ -551,6 +552,15 @@ MONGO_INITIALIZER(CreateAuthorizationManager)(InitializerContext* context) { return Status::OK(); } +#ifdef MONGO_SSL +MONGO_INITIALIZER_GENERAL(setSSLManagerType, + MONGO_NO_PREREQUISITES, + ("SSLManager"))(InitializerContext* context) { + isSSLServer = true; + return Status::OK(); +} +#endif + int mongoSMain(int argc, char* argv[], char** envp) { static StaticObserver staticObserver; if (argc < 1) diff --git a/src/mongo/s/shard.cpp b/src/mongo/s/shard.cpp index b57bf714cae..681cb932f6e 100644 --- a/src/mongo/s/shard.cpp +++ b/src/mongo/s/shard.cpp @@ -404,13 +404,12 @@ namespace mongo { void ShardingConnectionHook::onCreate( DBClientBase * conn ) { if(AuthorizationManager::isAuthEnabled()) { - string err; LOG(2) << "calling onCreate auth for " << conn->toString() << endl; bool result = authenticateInternalUser(conn); uassert( 15847, str::stream() << "can't authenticate to server " - << conn->getServerAddress() << causedBy( err ), + << conn->getServerAddress(), result ); } diff --git a/src/mongo/util/net/sock.cpp b/src/mongo/util/net/sock.cpp index ab9a56703a6..f64800a34c2 100644 --- a/src/mongo/util/net/sock.cpp +++ b/src/mongo/util/net/sock.cpp @@ -720,7 +720,7 @@ namespace mongo { // ret < 0 #ifdef MONGO_SSL if (_ssl) { - LOG(_logLevel) << "SSL Error ret: " << ret + LOG(_logLevel) << "SSL Error ret when receiving: " << ret << " err: " << _sslManager->SSL_get_error(_ssl , ret) << " " << _sslManager->ERR_error_string(_sslManager->ERR_get_error(), NULL) diff --git a/src/mongo/util/net/ssl_manager.cpp b/src/mongo/util/net/ssl_manager.cpp index 31065755c85..4fe3374d98e 100644 --- a/src/mongo/util/net/ssl_manager.cpp +++ b/src/mongo/util/net/ssl_manager.cpp @@ -32,6 +32,8 @@ #include "mongo/util/net/sock.h" #include "mongo/util/scopeguard.h" +#include <openssl/evp.h> + namespace mongo { namespace { @@ -119,12 +121,16 @@ namespace mongo { struct Params { Params(const std::string& pemfile, const std::string& pempwd, + const std::string& clusterfile, + const std::string& clusterpwd, const std::string& cafile = "", const std::string& crlfile = "", bool weakCertificateValidation = false, bool fipsMode = false) : pemfile(pemfile), pempwd(pempwd), + clusterfile(clusterfile), + clusterpwd(clusterpwd), cafile(cafile), crlfile(crlfile), weakCertificateValidation(weakCertificateValidation), @@ -132,6 +138,8 @@ namespace mongo { std::string pemfile; std::string pempwd; + std::string clusterfile; + std::string clusterpwd; std::string cafile; std::string crlfile; bool weakCertificateValidation; @@ -140,7 +148,7 @@ namespace mongo { class SSLManager : public SSLManagerInterface { public: - explicit SSLManager(const Params& params); + explicit SSLManager(const Params& params, bool isServer); virtual ~SSLManager(); @@ -152,8 +160,12 @@ namespace mongo { virtual void cleanupThreadLocals(); - virtual std::string getSubjectName() { - return _subjectName; + virtual std::string getServerSubjectName() { + return _serverSubjectName; + } + + virtual std::string getClientSubjectName() { + return _clientSubjectName; } virtual int SSL_read(SSL* ssl, void* buf, int num); @@ -171,17 +183,19 @@ namespace mongo { virtual void SSL_free(SSL* ssl); private: - SSL_CTX* _context; + SSL_CTX* _serverContext; // SSL context for incoming connections + SSL_CTX* _clientContext; // SSL context for outgoing connections std::string _password; bool _validateCertificates; bool _weakValidation; - std::string _subjectName; + std::string _serverSubjectName; + std::string _clientSubjectName; /** - * creates an SSL context to be used for this file descriptor. + * creates an SSL object to be used for this file descriptor. * caller must SSL_free it. */ - SSL* _secure(int fd); + SSL* _secure(SSL_CTX* context, int fd); /** * Fetches the error text for an error code, in a thread-safe manner. @@ -194,19 +208,31 @@ namespace mongo { */ void _handleSSLError(int code); + /* + * Init the SSL context using parameters provided in params. + */ + bool _initSSLContext(SSL_CTX** context, const Params& params); + + /* + * Parse the x509 subject name from the PEM keyfile and store it + */ + bool _setSubjectName(const std::string& keyFile, std::string& subjectName); + /** @return true if was successful, otherwise false */ - bool _setupPEM( const std::string& keyFile , const std::string& password ); + bool _setupPEM(SSL_CTX* context, + const std::string& keyFile, + const std::string& password); /* - * Set up SSL for certificate validation by loading a CA + * Set up an SSL context for certificate validation by loading a CA */ - bool _setupCA(const std::string& caFile); + bool _setupCA(SSL_CTX* context, const std::string& caFile); /* - * Import a certificate revocation list into our SSL context + * Import a certificate revocation list into an SSL context * for use with validating certificates */ - bool _setupCRL(const std::string& crlFile); + bool _setupCRL(SSL_CTX* context, const std::string& crlFile); /* * Activate FIPS 140-2 mode, if the server started with a command line @@ -230,17 +256,22 @@ namespace mongo { } // namespace + // Global variable indicating if this is a server or a client instance + bool isSSLServer = false; + MONGO_INITIALIZER(SSLManager)(InitializerContext* context) { SimpleMutex::scoped_lock lck(sslManagerMtx); if (cmdLine.sslOnNormalPorts) { const Params params( cmdLine.sslPEMKeyFile, cmdLine.sslPEMKeyPassword, + cmdLine.sslClusterFile, + cmdLine.sslClusterPassword, cmdLine.sslCAFile, cmdLine.sslCRLFile, cmdLine.sslWeakCertificateValidation, cmdLine.sslFIPSMode); - theSSLManager = new SSLManager(params); + theSSLManager = new SSLManager(params, isSSLServer); } return Status::OK(); } @@ -277,7 +308,7 @@ namespace mongo { SSLManagerInterface::~SSLManagerInterface() {} - SSLManager::SSLManager(const Params& params) : + SSLManager::SSLManager(const Params& params, bool isServer) : _validateCertificates(false), _weakValidation(params.weakCertificateValidation) { @@ -292,51 +323,57 @@ namespace mongo { // Add all digests and ciphers to OpenSSL's internal table // so that encryption/decryption is backwards compatible OpenSSL_add_all_algorithms(); + + SSLThreadInfo::init(); + SSLThreadInfo::get(); - _context = SSL_CTX_new(SSLv23_method()); - massert(15864, - mongoutils::str::stream() << "can't create SSL Context: " << - _getSSLErrorMessage(ERR_get_error()), - _context); - - // Activate all bug workaround options, to support buggy client SSL's. - SSL_CTX_set_options(_context, SSL_OP_ALL); - - // If renegotiation is needed, don't return from recv() or send() until it's successful. - // Note: this is for blocking sockets only. - SSL_CTX_set_mode(_context, SSL_MODE_AUTO_RETRY); - - // Set context within which session can be reused - int status = SSL_CTX_set_session_id_context( - _context, - static_cast<unsigned char*>(static_cast<void*>(&_context)), - sizeof(_context)); - if (!status) { - uasserted(16768,"ssl initialization problem"); + if (!_initSSLContext(&_clientContext, params)) { + uasserted(16768, "ssl initialization problem"); } - SSLThreadInfo::init(); - SSLThreadInfo::get(); + // SSL client specific initialization + if (!isServer) { + _serverContext = NULL; - if (!params.pemfile.empty()) { - if (!_setupPEM(params.pemfile, params.pempwd)) { - uasserted(16562, "ssl initialization problem"); + if (!params.pemfile.empty()) { + if (!_setSubjectName(params.pemfile, _clientSubjectName)) { + uasserted(16941, "ssl initialization problem"); + } } } - if (!params.cafile.empty()) { - // Set up certificate validation with a certificate authority - if (!_setupCA(params.cafile)) { - uasserted(16563, "ssl initialization problem"); + // SSL server specific initialization + if (isServer) { + if (!_initSSLContext(&_serverContext, params)) { + uasserted(16562, "ssl initialization problem"); } - } - if (!params.crlfile.empty()) { - if (!_setupCRL(params.crlfile)) { - uasserted(16582, "ssl initialization problem"); + + if (!_setSubjectName(params.pemfile, _serverSubjectName)) { + uasserted(16942, "ssl initialization problem"); + } + // use the cluster certificate for outgoing connections if specified + if (!params.clusterfile.empty()) { + if (!_setSubjectName(params.clusterfile, _clientSubjectName)) { + uasserted(16943, "ssl initialization problem"); + } + } + else { + if (!_setSubjectName(params.pemfile, _clientSubjectName)) { + uasserted(16944, "ssl initialization problem"); + } } } } SSLManager::~SSLManager() { + ERR_free_strings(); + EVP_cleanup(); + + if (NULL != _serverContext) { + SSL_CTX_free(_serverContext); + } + if (NULL != _clientContext) { + SSL_CTX_free(_clientContext); + } } int SSLManager::password_cb(char *buf,int num, int rwflag,void *userdata) { @@ -389,38 +426,67 @@ namespace mongo { log() << "FIPS 140-2 mode activated" << endl; } - bool SSLManager::_setupPEM(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 << ' ' << + bool SSLManager::_initSSLContext(SSL_CTX** context, const Params& params) { + *context = SSL_CTX_new(SSLv23_method()); + massert(15864, + mongoutils::str::stream() << "can't create SSL Context: " << + _getSSLErrorMessage(ERR_get_error()), + context); + + // Activate all bug workaround options, to support buggy client SSL's. + SSL_CTX_set_options(*context, SSL_OP_ALL); + + // If renegotiation is needed, don't return from recv() or send() until it's successful. + // Note: this is for blocking sockets only. + SSL_CTX_set_mode(*context, SSL_MODE_AUTO_RETRY); + + // Set context within which session can be reused + int status = SSL_CTX_set_session_id_context( + *context, + static_cast<unsigned char*>(static_cast<void*>(context)), + sizeof(*context)); + + if (!status) { + error() << "failed to set session id context: " << _getSSLErrorMessage(ERR_get_error()) << endl; return false; } - // If password is empty, use default OpenSSL callback, which uses the terminal - // to securely request the password interactively from the user. - if (!password.empty()) { - SSL_CTX_set_default_passwd_cb_userdata( _context , this ); - SSL_CTX_set_default_passwd_cb( _context, &SSLManager::password_cb ); + // Use the clusterfile for internal outgoing SSL connections if specified + if (context == &_clientContext && !params.clusterfile.empty()) { + EVP_set_pw_prompt("Enter cluster certificate passphrase"); + if (!_setupPEM(*context, params.clusterfile, params.clusterpwd)) { + return false; + } } - - if ( SSL_CTX_use_PrivateKey_file( _context , keyFile.c_str() , SSL_FILETYPE_PEM ) != 1 ) { - error() << "cannot read key file: " << keyFile << ' ' << - _getSSLErrorMessage(ERR_get_error()) << endl; - return false; + // Use the pemfile for everything else + else if (!params.pemfile.empty()) { + EVP_set_pw_prompt("Enter PEM passphrase"); + if (!_setupPEM(*context, params.pemfile, params.pempwd)) { + return false; + } } - - // Verify that the certificate and the key go together. - if (SSL_CTX_check_private_key(_context) != 1) { - error() << "SSL certificate validation: " << _getSSLErrorMessage(ERR_get_error()) - << endl; - return false; + + if (!params.cafile.empty()) { + // Set up certificate validation with a certificate authority + if (!_setupCA(*context, params.cafile)) { + return false; + } } - + + if (!params.crlfile.empty()) { + if (!_setupCRL(*context, params.crlfile)) { + return false; + } + } + + return true; + } + + bool SSLManager::_setSubjectName(const std::string& keyFile, std::string& subjectName) { // Read the certificate subject name and store it BIO *in = BIO_new(BIO_s_file_internal()); - if(NULL == in){ + if (NULL == in){ error() << "failed to allocate BIO object: " << _getSSLErrorMessage(ERR_get_error()) << endl; return false; @@ -428,7 +494,7 @@ namespace mongo { ON_BLOCK_EXIT(BIO_free, in); if (BIO_read_filename(in, keyFile.c_str()) <= 0){ - error() << "cannot read key file: " << keyFile << ' ' << + error() << "cannot read key file when setting subject name: " << keyFile << ' ' << _getSSLErrorMessage(ERR_get_error()) << endl; return false; } @@ -440,27 +506,61 @@ namespace mongo { return false; } ON_BLOCK_EXIT(X509_free, x509); - _subjectName = getCertificateSubjectName(x509); + subjectName = getCertificateSubjectName(x509); + + return true; + } + + 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()) << endl; + return false; + } + + // If password is empty, use default OpenSSL callback, which uses the terminal + // to securely request the password interactively from the user. + if (!password.empty()) { + SSL_CTX_set_default_passwd_cb_userdata( context , this ); + SSL_CTX_set_default_passwd_cb( context, &SSLManager::password_cb ); + } + + if ( SSL_CTX_use_PrivateKey_file( context , keyFile.c_str() , SSL_FILETYPE_PEM ) != 1 ) { + error() << "cannot read PEM key file: " << keyFile << ' ' << + _getSSLErrorMessage(ERR_get_error()) << endl; + return false; + } + + // Verify that the certificate and the key go together. + if (SSL_CTX_check_private_key(context) != 1) { + error() << "SSL certificate validation: " << _getSSLErrorMessage(ERR_get_error()) + << endl; + return false; + } return true; } - bool SSLManager::_setupCA(const std::string& caFile) { + bool SSLManager::_setupCA(SSL_CTX* context, const std::string& caFile) { // Load trusted CA - if (SSL_CTX_load_verify_locations(_context, caFile.c_str(), NULL) != 1) { + if (SSL_CTX_load_verify_locations(context, caFile.c_str(), NULL) != 1) { error() << "cannot read certificate authority file: " << caFile << " " << _getSSLErrorMessage(ERR_get_error()) << endl; return false; } // Set SSL to require peer (client) certificate verification // if a certificate is presented - SSL_CTX_set_verify(_context, SSL_VERIFY_PEER, &SSLManager::verify_cb); + SSL_CTX_set_verify(context, SSL_VERIFY_PEER, &SSLManager::verify_cb); _validateCertificates = true; return true; } - bool SSLManager::_setupCRL(const std::string& crlFile) { - X509_STORE *store = SSL_CTX_get_cert_store(_context); + bool SSLManager::_setupCRL(SSL_CTX* context, const std::string& crlFile) { + X509_STORE *store = SSL_CTX_get_cert_store(context); fassert(16583, store); X509_STORE_set_flags(store, X509_V_FLAG_CRL_CHECK); @@ -479,16 +579,17 @@ namespace mongo { return true; } - SSL* SSLManager::_secure(int fd) { + SSL* SSLManager::_secure(SSL_CTX* context, int fd) { // This just ensures that SSL multithreading support is set up for this thread, // if it's not already. SSLThreadInfo::get(); - SSL * ssl = SSL_new(_context); + SSL * ssl = SSL_new(context); + massert(15861, _getSSLErrorMessage(ERR_get_error()), ssl); - + int status = SSL_set_fd( ssl , fd ); massert(16510, _getSSLErrorMessage(ERR_get_error()), @@ -513,7 +614,7 @@ namespace mongo { return ret; } SSL* SSLManager::connect(int fd) { - SSL* ssl = _secure(fd); + SSL* ssl = _secure(_clientContext, fd); ScopeGuard guard = MakeGuard(::SSL_free, ssl); int ret = _ssl_connect(ssl); if (ret != 1) @@ -523,7 +624,7 @@ namespace mongo { } SSL* SSLManager::accept(int fd) { - SSL* ssl = _secure(fd); + SSL* ssl = _secure(_serverContext, fd); ScopeGuard guard = MakeGuard(::SSL_free, ssl); int ret = SSL_accept(ssl); if (ret != 1) diff --git a/src/mongo/util/net/ssl_manager.h b/src/mongo/util/net/ssl_manager.h index 374807b9f17..d801dfb9aba 100644 --- a/src/mongo/util/net/ssl_manager.h +++ b/src/mongo/util/net/ssl_manager.h @@ -57,10 +57,17 @@ namespace mongo { virtual void cleanupThreadLocals() = 0; /** - * Get the subject name of our own server certificate + * Gets the subject name of our own server certificate * @return the subject name. */ - virtual std::string getSubjectName() = 0; + virtual std::string getServerSubjectName() = 0; + + /** + * Gets the subject name of our own client certificate + * used for cluster authentiation + * @return the subject name. + */ + virtual std::string getClientSubjectName() = 0; /** * ssl.h shims @@ -83,5 +90,6 @@ namespace mongo { // Access SSL functions through this instance. SSLManagerInterface* getSSLManager(); + extern bool isSSLServer; } #endif |