summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndreas Nilsson <andreas.nilsson@10gen.com>2013-06-20 16:44:41 +0100
committerAndreas Nilsson <andreas.nilsson@10gen.com>2013-07-10 16:04:01 +0100
commit6685c058c7bf4444f14fcae61f56b7783b5edebe (patch)
tree41bc78ae1eef54f8b6419ef255b3c2b765a937c2
parent9c222f4ffb1eac72d54b8c1237c8816b115af43b (diff)
downloadmongo-6685c058c7bf4444f14fcae61f56b7783b5edebe.tar.gz
SERVER-7455 keyfile replacement, command line parameters and upgrade
-rw-r--r--src/mongo/client/dbclient.cpp45
-rw-r--r--src/mongo/client/dbclientinterface.h2
-rw-r--r--src/mongo/db/auth/security_key.cpp37
-rw-r--r--src/mongo/db/auth/security_key.h9
-rw-r--r--src/mongo/db/cmdline.cpp24
-rw-r--r--src/mongo/db/cmdline.h6
-rw-r--r--src/mongo/db/commands/authentication_commands.cpp17
-rw-r--r--src/mongo/db/commands/isself.cpp2
-rw-r--r--src/mongo/db/db.cpp10
-rw-r--r--src/mongo/db/initialize_server_global_state.cpp15
-rw-r--r--src/mongo/db/repl/connections.h5
-rw-r--r--src/mongo/db/repl/oplogreader.cpp3
-rw-r--r--src/mongo/db/repl/sync_source_feedback.cpp8
-rw-r--r--src/mongo/s/server.cpp10
-rw-r--r--src/mongo/s/shard.cpp3
-rw-r--r--src/mongo/util/net/sock.cpp2
-rw-r--r--src/mongo/util/net/ssl_manager.cpp265
-rw-r--r--src/mongo/util/net/ssl_manager.h12
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