diff options
Diffstat (limited to 'src/mongo/db/commands/authentication_commands.cpp')
-rw-r--r-- | src/mongo/db/commands/authentication_commands.cpp | 603 |
1 files changed, 300 insertions, 303 deletions
diff --git a/src/mongo/db/commands/authentication_commands.cpp b/src/mongo/db/commands/authentication_commands.cpp index 3ab8b0852ce..743400e4495 100644 --- a/src/mongo/db/commands/authentication_commands.cpp +++ b/src/mongo/db/commands/authentication_commands.cpp @@ -63,367 +63,364 @@ namespace mongo { - using std::hex; - using std::string; - using std::stringstream; - - static bool _isCRAuthDisabled; - static bool _isX509AuthDisabled; - static const char _nonceAuthenticationDisabledMessage[] = - "Challenge-response authentication using getnonce and authenticate commands is disabled."; - static const char _x509AuthenticationDisabledMessage[] = - "x.509 authentication is disabled."; - - void CmdAuthenticate::disableAuthMechanism(std::string authMechanism) { - if (authMechanism == "MONGODB-CR") { - _isCRAuthDisabled = true; - } - if (authMechanism == "MONGODB-X509") { - _isX509AuthDisabled = true; - } +using std::hex; +using std::string; +using std::stringstream; + +static bool _isCRAuthDisabled; +static bool _isX509AuthDisabled; +static const char _nonceAuthenticationDisabledMessage[] = + "Challenge-response authentication using getnonce and authenticate commands is disabled."; +static const char _x509AuthenticationDisabledMessage[] = "x.509 authentication is disabled."; + +void CmdAuthenticate::disableAuthMechanism(std::string authMechanism) { + if (authMechanism == "MONGODB-CR") { + _isCRAuthDisabled = true; } + if (authMechanism == "MONGODB-X509") { + _isX509AuthDisabled = true; + } +} - /* authentication - - system.users contains - { user : <username>, pwd : <pwd_digest>, ... } +/* authentication - getnonce sends nonce to client + system.users contains + { user : <username>, pwd : <pwd_digest>, ... } - client then sends { authenticate:1, nonce64:<nonce_str>, user:<username>, key:<key> } + getnonce sends nonce to client - where <key> is md5(<nonce_str><username><pwd_digest_str>) as a string - */ + client then sends { authenticate:1, nonce64:<nonce_str>, user:<username>, key:<key> } - class CmdGetNonce : public Command { - public: - CmdGetNonce() : - Command("getnonce"), - _randMutex("getnonce"), - _random(SecureRandom::create()) { - } + where <key> is md5(<nonce_str><username><pwd_digest_str>) as a string +*/ - virtual bool slaveOk() const { - return true; - } - void help(stringstream& h) const { h << "internal"; } - virtual bool isWriteCommandForConfigServer() const { return false; } - virtual void addRequiredPrivileges(const std::string& dbname, - const BSONObj& cmdObj, - std::vector<Privilege>* out) {} // No auth required - bool run(OperationContext* txn, const string&, BSONObj& cmdObj, int, string& errmsg, BSONObjBuilder& result, bool fromRepl) { - nonce64 n = getNextNonce(); - stringstream ss; - ss << hex << n; - result.append("nonce", ss.str() ); - ClientBasic::getCurrent()->resetAuthenticationSession( - new MongoAuthenticationSession(n)); - return true; - } +class CmdGetNonce : public Command { +public: + CmdGetNonce() : Command("getnonce"), _randMutex("getnonce"), _random(SecureRandom::create()) {} - private: - nonce64 getNextNonce() { - SimpleMutex::scoped_lock lk(_randMutex); - return _random->nextInt64(); - } + virtual bool slaveOk() const { + return true; + } + void help(stringstream& h) const { + h << "internal"; + } + virtual bool isWriteCommandForConfigServer() const { + return false; + } + virtual void addRequiredPrivileges(const std::string& dbname, + const BSONObj& cmdObj, + std::vector<Privilege>* out) {} // No auth required + bool run(OperationContext* txn, + const string&, + BSONObj& cmdObj, + int, + string& errmsg, + BSONObjBuilder& result, + bool fromRepl) { + nonce64 n = getNextNonce(); + stringstream ss; + ss << hex << n; + result.append("nonce", ss.str()); + ClientBasic::getCurrent()->resetAuthenticationSession(new MongoAuthenticationSession(n)); + return true; + } - SimpleMutex _randMutex; // Synchronizes accesses to _random. - boost::scoped_ptr<SecureRandom> _random; - } cmdGetNonce; +private: + nonce64 getNextNonce() { + SimpleMutex::scoped_lock lk(_randMutex); + return _random->nextInt64(); + } - void CmdAuthenticate::redactForLogging(mutablebson::Document* cmdObj) { - namespace mmb = mutablebson; - static const int numRedactedFields = 2; - static const char* redactedFields[numRedactedFields] = { "key", "nonce" }; - for (int i = 0; i < numRedactedFields; ++i) { - for (mmb::Element element = mmb::findFirstChildNamed(cmdObj->root(), redactedFields[i]); - element.ok(); - element = mmb::findElementNamed(element.rightSibling(), redactedFields[i])) { + SimpleMutex _randMutex; // Synchronizes accesses to _random. + boost::scoped_ptr<SecureRandom> _random; +} cmdGetNonce; - element.setValueString("xxx"); - } +void CmdAuthenticate::redactForLogging(mutablebson::Document* cmdObj) { + namespace mmb = mutablebson; + static const int numRedactedFields = 2; + static const char* redactedFields[numRedactedFields] = {"key", "nonce"}; + for (int i = 0; i < numRedactedFields; ++i) { + for (mmb::Element element = mmb::findFirstChildNamed(cmdObj->root(), redactedFields[i]); + element.ok(); + element = mmb::findElementNamed(element.rightSibling(), redactedFields[i])) { + element.setValueString("xxx"); } } +} - bool CmdAuthenticate::run(OperationContext* txn, const string& dbname, - BSONObj& cmdObj, - int, - string& errmsg, - BSONObjBuilder& result, - bool fromRepl) { - - if (!serverGlobalParams.quiet) { - mutablebson::Document cmdToLog(cmdObj, mutablebson::Document::kInPlaceDisabled); - redactForLogging(&cmdToLog); - log() << " authenticate db: " << dbname << " " << cmdToLog; - } +bool CmdAuthenticate::run(OperationContext* txn, + const string& dbname, + BSONObj& cmdObj, + int, + string& errmsg, + BSONObjBuilder& result, + bool fromRepl) { + if (!serverGlobalParams.quiet) { + mutablebson::Document cmdToLog(cmdObj, mutablebson::Document::kInPlaceDisabled); + redactForLogging(&cmdToLog); + log() << " authenticate db: " << dbname << " " << cmdToLog; + } - UserName user(cmdObj.getStringField("user"), dbname); - if (Command::testCommandsEnabled && - user.getDB() == "admin" && - user.getUser() == internalSecurity.user->getName().getUser()) { - // Allows authenticating as the internal user against the admin database. This is to - // support the auth passthrough test framework on mongos (since you can't use the local - // database on a mongos, so you can't auth as the internal user without this). - user = internalSecurity.user->getName(); - } + UserName user(cmdObj.getStringField("user"), dbname); + if (Command::testCommandsEnabled && user.getDB() == "admin" && + user.getUser() == internalSecurity.user->getName().getUser()) { + // Allows authenticating as the internal user against the admin database. This is to + // support the auth passthrough test framework on mongos (since you can't use the local + // database on a mongos, so you can't auth as the internal user without this). + user = internalSecurity.user->getName(); + } - std::string mechanism = cmdObj.getStringField("mechanism"); - if (mechanism.empty()) { - mechanism = "MONGODB-CR"; + std::string mechanism = cmdObj.getStringField("mechanism"); + if (mechanism.empty()) { + mechanism = "MONGODB-CR"; + } + Status status = _authenticate(txn, mechanism, user, cmdObj); + audit::logAuthentication(ClientBasic::getCurrent(), mechanism, user, status.code()); + if (!status.isOK()) { + if (!serverGlobalParams.quiet) { + log() << "Failed to authenticate " << user << " with mechanism " << mechanism << ": " + << status; } - Status status = _authenticate(txn, mechanism, user, cmdObj); - audit::logAuthentication(ClientBasic::getCurrent(), - mechanism, - user, - status.code()); - if (!status.isOK()) { - if (!serverGlobalParams.quiet) { - log() << "Failed to authenticate " << user << " with mechanism " << mechanism - << ": " << status; - } - if (status.code() == ErrorCodes::AuthenticationFailed) { - // Statuses with code AuthenticationFailed may contain messages we do not wish to - // reveal to the user, so we return a status with the message "auth failed". - appendCommandStatus(result, - Status(ErrorCodes::AuthenticationFailed, "auth failed")); - } - else { - appendCommandStatus(result, status); - } - return false; + if (status.code() == ErrorCodes::AuthenticationFailed) { + // Statuses with code AuthenticationFailed may contain messages we do not wish to + // reveal to the user, so we return a status with the message "auth failed". + appendCommandStatus(result, Status(ErrorCodes::AuthenticationFailed, "auth failed")); + } else { + appendCommandStatus(result, status); } - result.append("dbname", user.getDB()); - result.append("user", user.getUser()); - return true; + return false; } + result.append("dbname", user.getDB()); + result.append("user", user.getUser()); + return true; +} - Status CmdAuthenticate::_authenticate(OperationContext* txn, - const std::string& mechanism, - const UserName& user, - const BSONObj& cmdObj) { - - if (mechanism == "MONGODB-CR") { - return _authenticateCR(txn, user, cmdObj); - } +Status CmdAuthenticate::_authenticate(OperationContext* txn, + const std::string& mechanism, + const UserName& user, + const BSONObj& cmdObj) { + if (mechanism == "MONGODB-CR") { + return _authenticateCR(txn, user, cmdObj); + } #ifdef MONGO_SSL - if (mechanism == "MONGODB-X509") { - return _authenticateX509(txn, user, cmdObj); - } -#endif - return Status(ErrorCodes::BadValue, "Unsupported mechanism: " + mechanism); + if (mechanism == "MONGODB-X509") { + return _authenticateX509(txn, user, cmdObj); } +#endif + return Status(ErrorCodes::BadValue, "Unsupported mechanism: " + mechanism); +} - Status CmdAuthenticate::_authenticateCR( - OperationContext* txn, const UserName& user, const BSONObj& cmdObj) { - - if (user == internalSecurity.user->getName() && - serverGlobalParams.clusterAuthMode.load() == - ServerGlobalParams::ClusterAuthMode_x509) { - return Status(ErrorCodes::AuthenticationFailed, - "Mechanism x509 is required for internal cluster authentication"); - } +Status CmdAuthenticate::_authenticateCR(OperationContext* txn, + const UserName& user, + const BSONObj& cmdObj) { + if (user == internalSecurity.user->getName() && + serverGlobalParams.clusterAuthMode.load() == ServerGlobalParams::ClusterAuthMode_x509) { + return Status(ErrorCodes::AuthenticationFailed, + "Mechanism x509 is required for internal cluster authentication"); + } - if (_isCRAuthDisabled) { - // SERVER-8461, MONGODB-CR must be enabled for authenticating the internal user, so that - // cluster members may communicate with each other. - if (user != internalSecurity.user->getName()) { - return Status(ErrorCodes::BadValue, _nonceAuthenticationDisabledMessage); - } + if (_isCRAuthDisabled) { + // SERVER-8461, MONGODB-CR must be enabled for authenticating the internal user, so that + // cluster members may communicate with each other. + if (user != internalSecurity.user->getName()) { + return Status(ErrorCodes::BadValue, _nonceAuthenticationDisabledMessage); } + } - string key = cmdObj.getStringField("key"); - string received_nonce = cmdObj.getStringField("nonce"); - - if( user.getUser().empty() || key.empty() || received_nonce.empty() ) { - sleepmillis(10); - return Status(ErrorCodes::ProtocolError, - "field missing/wrong type in received authenticate command"); - } + string key = cmdObj.getStringField("key"); + string received_nonce = cmdObj.getStringField("nonce"); - stringstream digestBuilder; + if (user.getUser().empty() || key.empty() || received_nonce.empty()) { + sleepmillis(10); + return Status(ErrorCodes::ProtocolError, + "field missing/wrong type in received authenticate command"); + } - { - ClientBasic *client = ClientBasic::getCurrent(); - boost::scoped_ptr<AuthenticationSession> session; - client->swapAuthenticationSession(session); - if (!session || session->getType() != AuthenticationSession::SESSION_TYPE_MONGO) { + stringstream digestBuilder; + + { + ClientBasic* client = ClientBasic::getCurrent(); + boost::scoped_ptr<AuthenticationSession> session; + client->swapAuthenticationSession(session); + if (!session || session->getType() != AuthenticationSession::SESSION_TYPE_MONGO) { + sleepmillis(30); + return Status(ErrorCodes::ProtocolError, "No pending nonce"); + } else { + nonce64 nonce = static_cast<MongoAuthenticationSession*>(session.get())->getNonce(); + digestBuilder << hex << nonce; + if (digestBuilder.str() != received_nonce) { sleepmillis(30); - return Status(ErrorCodes::ProtocolError, "No pending nonce"); - } - else { - nonce64 nonce = static_cast<MongoAuthenticationSession*>(session.get())->getNonce(); - digestBuilder << hex << nonce; - if (digestBuilder.str() != received_nonce) { - sleepmillis(30); - return Status(ErrorCodes::AuthenticationFailed, "Received wrong nonce."); - } + return Status(ErrorCodes::AuthenticationFailed, "Received wrong nonce."); } } + } - User* userObj; - Status status = getGlobalAuthorizationManager()->acquireUser(txn, user, &userObj); - if (!status.isOK()) { - // Failure to find the privilege document indicates no-such-user, a fact that we do not - // wish to reveal to the client. So, we return AuthenticationFailed rather than passing - // through the returned status. - return Status(ErrorCodes::AuthenticationFailed, status.toString()); - } - string pwd = userObj->getCredentials().password; - getGlobalAuthorizationManager()->releaseUser(userObj); - - if (pwd.empty()) { - return Status(ErrorCodes::AuthenticationFailed, - "MONGODB-CR credentials missing in the user document"); - } + User* userObj; + Status status = getGlobalAuthorizationManager()->acquireUser(txn, user, &userObj); + if (!status.isOK()) { + // Failure to find the privilege document indicates no-such-user, a fact that we do not + // wish to reveal to the client. So, we return AuthenticationFailed rather than passing + // through the returned status. + return Status(ErrorCodes::AuthenticationFailed, status.toString()); + } + string pwd = userObj->getCredentials().password; + getGlobalAuthorizationManager()->releaseUser(userObj); - md5digest d; - { - digestBuilder << user.getUser() << pwd; - string done = digestBuilder.str(); + if (pwd.empty()) { + return Status(ErrorCodes::AuthenticationFailed, + "MONGODB-CR credentials missing in the user document"); + } - md5_state_t st; - md5_init(&st); - md5_append(&st, (const md5_byte_t *) done.c_str(), done.size()); - md5_finish(&st, d); - } + md5digest d; + { + digestBuilder << user.getUser() << pwd; + string done = digestBuilder.str(); - string computed = digestToString( d ); + md5_state_t st; + md5_init(&st); + md5_append(&st, (const md5_byte_t*)done.c_str(), done.size()); + md5_finish(&st, d); + } - if ( key != computed ) { - return Status(ErrorCodes::AuthenticationFailed, "key mismatch"); - } + string computed = digestToString(d); - AuthorizationSession* authorizationSession = - ClientBasic::getCurrent()->getAuthorizationSession(); - status = authorizationSession->addAndAuthorizeUser(txn, user); - if (!status.isOK()) { - return status; - } + if (key != computed) { + return Status(ErrorCodes::AuthenticationFailed, "key mismatch"); + } - return Status::OK(); + AuthorizationSession* authorizationSession = + ClientBasic::getCurrent()->getAuthorizationSession(); + status = authorizationSession->addAndAuthorizeUser(txn, user); + if (!status.isOK()) { + return status; } + return Status::OK(); +} + #ifdef MONGO_SSL - void canonicalizeClusterDN(std::vector<std::string>* dn) { - // remove all RDNs we don't care about - for (size_t i=0; i<dn->size(); i++) { - std::string& comp = dn->at(i); - boost::algorithm::trim(comp); - if (!mongoutils::str::startsWith(comp.c_str(), "DC=") && - !mongoutils::str::startsWith(comp.c_str(), "O=") && - !mongoutils::str::startsWith(comp.c_str(), "OU=")) { - dn->erase(dn->begin()+i); - i--; - } +void canonicalizeClusterDN(std::vector<std::string>* dn) { + // remove all RDNs we don't care about + for (size_t i = 0; i < dn->size(); i++) { + std::string& comp = dn->at(i); + boost::algorithm::trim(comp); + if (!mongoutils::str::startsWith(comp.c_str(), "DC=") && + !mongoutils::str::startsWith(comp.c_str(), "O=") && + !mongoutils::str::startsWith(comp.c_str(), "OU=")) { + dn->erase(dn->begin() + i); + i--; } - std::stable_sort(dn->begin(), dn->end()); } + std::stable_sort(dn->begin(), dn->end()); +} - bool CmdAuthenticate::_clusterIdMatch(const std::string& subjectName, - const std::string& srvSubjectName) { - std::vector<string> clientRDN = StringSplitter::split(subjectName, ","); - std::vector<string> serverRDN = StringSplitter::split(srvSubjectName, ","); +bool CmdAuthenticate::_clusterIdMatch(const std::string& subjectName, + const std::string& srvSubjectName) { + std::vector<string> clientRDN = StringSplitter::split(subjectName, ","); + std::vector<string> serverRDN = StringSplitter::split(srvSubjectName, ","); - canonicalizeClusterDN(&clientRDN); - canonicalizeClusterDN(&serverRDN); + canonicalizeClusterDN(&clientRDN); + canonicalizeClusterDN(&serverRDN); - if (clientRDN.size() == 0 || clientRDN.size() != serverRDN.size()) { - return false; - } + if (clientRDN.size() == 0 || clientRDN.size() != serverRDN.size()) { + return false; + } - for (size_t i=0; i < serverRDN.size(); i++) { - if(clientRDN[i] != serverRDN[i]) { - return false; - } + for (size_t i = 0; i < serverRDN.size(); i++) { + if (clientRDN[i] != serverRDN[i]) { + return false; } - return true; } - - Status CmdAuthenticate::_authenticateX509( - OperationContext* txn, const UserName& user, const BSONObj& cmdObj) { - if (!getSSLManager()) { - return Status(ErrorCodes::ProtocolError, - "SSL support is required for the MONGODB-X509 mechanism."); - } - if(user.getDB() != "$external") { - return Status(ErrorCodes::ProtocolError, - "X.509 authentication must always use the $external database."); - } + return true; +} - ClientBasic *client = ClientBasic::getCurrent(); - AuthorizationSession* authorizationSession = client->getAuthorizationSession(); - std::string subjectName = client->port()->getX509SubjectName(); +Status CmdAuthenticate::_authenticateX509(OperationContext* txn, + const UserName& user, + const BSONObj& cmdObj) { + if (!getSSLManager()) { + return Status(ErrorCodes::ProtocolError, + "SSL support is required for the MONGODB-X509 mechanism."); + } + if (user.getDB() != "$external") { + return Status(ErrorCodes::ProtocolError, + "X.509 authentication must always use the $external database."); + } - if (!getSSLManager()->getSSLConfiguration().hasCA) { - return Status(ErrorCodes::AuthenticationFailed, - "Unable to verify x.509 certificate, as no CA has been provided."); - } - else if (user.getUser() != subjectName) { - return Status(ErrorCodes::AuthenticationFailed, - "There is no x.509 client certificate matching the user."); + ClientBasic* client = ClientBasic::getCurrent(); + AuthorizationSession* authorizationSession = client->getAuthorizationSession(); + std::string subjectName = client->port()->getX509SubjectName(); + + if (!getSSLManager()->getSSLConfiguration().hasCA) { + return Status(ErrorCodes::AuthenticationFailed, + "Unable to verify x.509 certificate, as no CA has been provided."); + } else if (user.getUser() != subjectName) { + return Status(ErrorCodes::AuthenticationFailed, + "There is no x.509 client certificate matching the user."); + } else { + std::string srvSubjectName = getSSLManager()->getSSLConfiguration().serverSubjectName; + + // Handle internal cluster member auth, only applies to server-server connections + if (_clusterIdMatch(subjectName, srvSubjectName)) { + int clusterAuthMode = serverGlobalParams.clusterAuthMode.load(); + if (clusterAuthMode == ServerGlobalParams::ClusterAuthMode_undefined || + clusterAuthMode == ServerGlobalParams::ClusterAuthMode_keyFile) { + return Status(ErrorCodes::AuthenticationFailed, + "The provided certificate " + "can only be used for cluster authentication, not client " + "authentication. The current configuration does not allow " + "x.509 cluster authentication, check the --clusterAuthMode flag"); + } + authorizationSession->grantInternalAuthorization(); } + // Handle normal client authentication, only applies to client-server connections else { - std::string srvSubjectName = getSSLManager()->getSSLConfiguration().serverSubjectName; - - // Handle internal cluster member auth, only applies to server-server connections - if (_clusterIdMatch(subjectName, srvSubjectName)) { - int clusterAuthMode = serverGlobalParams.clusterAuthMode.load(); - if (clusterAuthMode == ServerGlobalParams::ClusterAuthMode_undefined || - clusterAuthMode == ServerGlobalParams::ClusterAuthMode_keyFile) { - return Status(ErrorCodes::AuthenticationFailed, "The provided certificate " - "can only be used for cluster authentication, not client " - "authentication. The current configuration does not allow " - "x.509 cluster authentication, check the --clusterAuthMode flag"); - } - authorizationSession->grantInternalAuthorization(); + if (_isX509AuthDisabled) { + return Status(ErrorCodes::BadValue, _x509AuthenticationDisabledMessage); } - // Handle normal client authentication, only applies to client-server connections - else { - if (_isX509AuthDisabled) { - return Status(ErrorCodes::BadValue, - _x509AuthenticationDisabledMessage); - } - Status status = authorizationSession->addAndAuthorizeUser(txn, user); - if (!status.isOK()) { - return status; - } + Status status = authorizationSession->addAndAuthorizeUser(txn, user); + if (!status.isOK()) { + return status; } - return Status::OK(); } + return Status::OK(); } +} #endif - CmdAuthenticate cmdAuthenticate; +CmdAuthenticate cmdAuthenticate; - class CmdLogout : public Command { - public: - virtual bool slaveOk() const { - return true; - } - virtual void addRequiredPrivileges(const std::string& dbname, - const BSONObj& cmdObj, - std::vector<Privilege>* out) {} // No auth required - void help(stringstream& h) const { h << "de-authenticate"; } - virtual bool isWriteCommandForConfigServer() const { return false; } - CmdLogout() : Command("logout") {} - bool run(OperationContext* txn, const string& dbname, - BSONObj& cmdObj, - int options, - string& errmsg, - BSONObjBuilder& result, - bool fromRepl) { - AuthorizationSession* authSession = - ClientBasic::getCurrent()->getAuthorizationSession(); - authSession->logoutDatabase(dbname); - if (Command::testCommandsEnabled && dbname == "admin") { - // Allows logging out as the internal user against the admin database, however - // this actually logs out of the local database as well. This is to - // support the auth passthrough test framework on mongos (since you can't use the - // local database on a mongos, so you can't logout as the internal user - // without this). - authSession->logoutDatabase("local"); - } - return true; +class CmdLogout : public Command { +public: + virtual bool slaveOk() const { + return true; + } + virtual void addRequiredPrivileges(const std::string& dbname, + const BSONObj& cmdObj, + std::vector<Privilege>* out) {} // No auth required + void help(stringstream& h) const { + h << "de-authenticate"; + } + virtual bool isWriteCommandForConfigServer() const { + return false; + } + CmdLogout() : Command("logout") {} + bool run(OperationContext* txn, + const string& dbname, + BSONObj& cmdObj, + int options, + string& errmsg, + BSONObjBuilder& result, + bool fromRepl) { + AuthorizationSession* authSession = ClientBasic::getCurrent()->getAuthorizationSession(); + authSession->logoutDatabase(dbname); + if (Command::testCommandsEnabled && dbname == "admin") { + // Allows logging out as the internal user against the admin database, however + // this actually logs out of the local database as well. This is to + // support the auth passthrough test framework on mongos (since you can't use the + // local database on a mongos, so you can't logout as the internal user + // without this). + authSession->logoutDatabase("local"); } - } cmdLogout; + return true; + } +} cmdLogout; } |