diff options
-rw-r--r-- | jstests/auth/auth_pass_prompt.js | 2 | ||||
-rw-r--r-- | jstests/auth/speculative-sasl-start.js | 16 | ||||
-rw-r--r-- | src/mongo/db/audit.cpp | 300 | ||||
-rw-r--r-- | src/mongo/db/audit.h | 57 | ||||
-rw-r--r-- | src/mongo/db/auth/authentication_session.cpp | 32 | ||||
-rw-r--r-- | src/mongo/db/auth/sasl_commands.cpp | 44 | ||||
-rw-r--r-- | src/mongo/db/auth/sasl_mechanism_registry.h | 7 |
7 files changed, 261 insertions, 197 deletions
diff --git a/jstests/auth/auth_pass_prompt.js b/jstests/auth/auth_pass_prompt.js index 13f38b3a989..11dccae04d1 100644 --- a/jstests/auth/auth_pass_prompt.js +++ b/jstests/auth/auth_pass_prompt.js @@ -22,7 +22,7 @@ if (!_isWindows()) { assert.soon(() => { const output = rawMongoProgramOutput(); - return output.includes("Enter password:") && output.includes("Successful authentication"); + return output.includes("Enter password:") && output.includes("Authentication succeeded"); }); MongoRunner.stopMongod(conn); diff --git a/jstests/auth/speculative-sasl-start.js b/jstests/auth/speculative-sasl-start.js index d0206fd099e..eadb501f09a 100644 --- a/jstests/auth/speculative-sasl-start.js +++ b/jstests/auth/speculative-sasl-start.js @@ -83,35 +83,35 @@ const speculativeSHA1AuthFailedAttrs = { "speculative": true, "principalName": "admin", "authenticationDatabase": "admin", - "result": "AuthenticationFailed: SCRAM authentication failed, storedKey mismatch" + "error": "AuthenticationFailed: SCRAM authentication failed, storedKey mismatch" }; const sha1AuthFailedAttrs = { "mechanism": "SCRAM-SHA-1", "speculative": false, "principalName": "admin", "authenticationDatabase": "admin", - "result": "AuthenticationFailed: SCRAM authentication failed, storedKey mismatch" + "error": "AuthenticationFailed: SCRAM authentication failed, storedKey mismatch" }; const speculativeSHA256AuthFailedAttrs = { "mechanism": "SCRAM-SHA-256", "speculative": true, "principalName": "admin", "authenticationDatabase": "admin", - "result": "AuthenticationFailed: SCRAM authentication failed, storedKey mismatch" + "error": "AuthenticationFailed: SCRAM authentication failed, storedKey mismatch" }; const sha256AuthFailedAttrs = { "mechanism": "SCRAM-SHA-256", "speculative": false, "principalName": "admin", "authenticationDatabase": "admin", - "result": "AuthenticationFailed: SCRAM authentication failed, storedKey mismatch" + "error": "AuthenticationFailed: SCRAM authentication failed, storedKey mismatch" }; const speculativeSHA256MechUnavailableAttrs = { "mechanism": "SCRAM-SHA-256", "speculative": true, "principalName": "admin", "authenticationDatabase": "admin", - "result": + "error": "MechanismUnavailable: Unable to use SCRAM-SHA-256 based authentication for user without any SCRAM-SHA-256 credentials registered" }; const sha256MechUnavailableAttrs = { @@ -119,7 +119,7 @@ const sha256MechUnavailableAttrs = { "speculative": false, "principalName": "admin", "authenticationDatabase": "admin", - "result": + "error": "MechanismUnavailable: Unable to use SCRAM-SHA-256 based authentication for user without any SCRAM-SHA-256 credentials registered" }; const speculativeClusterAuthFailedAttrs = { @@ -127,14 +127,14 @@ const speculativeClusterAuthFailedAttrs = { "speculative": true, "principalName": "__system", "authenticationDatabase": "local", - "result": "AuthenticationFailed: SCRAM authentication failed, storedKey mismatch" + "error": "AuthenticationFailed: SCRAM authentication failed, storedKey mismatch" }; const clusterAuthFailedAttrs = { "mechanism": "SCRAM-SHA-256", "speculative": false, "principalName": "__system", "authenticationDatabase": "local", - "result": "AuthenticationFailed: SCRAM authentication failed, storedKey mismatch" + "error": "AuthenticationFailed: SCRAM authentication failed, storedKey mismatch" }; admin.setLogLevel(5); // Invalid password should never connect regardless of speculative auth. diff --git a/src/mongo/db/audit.cpp b/src/mongo/db/audit.cpp index ab5aed971ab..60e826e91fb 100644 --- a/src/mongo/db/audit.cpp +++ b/src/mongo/db/audit.cpp @@ -29,190 +29,174 @@ #include "mongo/db/audit.h" -#if !MONGO_ENTERPRISE_AUDIT +namespace mongo { +namespace audit { -mongo::audit::ImpersonatedClientAttrs::ImpersonatedClientAttrs(Client* client) {} - -void mongo::audit::logAuthentication(Client* client, - StringData mechanism, - const UserName& user, - ErrorCodes::Error result) {} - -void mongo::audit::logCommandAuthzCheck(Client* client, - const OpMsgRequest& cmdObj, - const CommandInterface& command, - ErrorCodes::Error result) {} - -void mongo::audit::logDeleteAuthzCheck(Client* client, - const NamespaceString& ns, - const BSONObj& pattern, - ErrorCodes::Error result) {} - -void mongo::audit::logGetMoreAuthzCheck(Client* client, - const NamespaceString& ns, - long long cursorId, - ErrorCodes::Error result) {} - -void mongo::audit::logInsertAuthzCheck(Client* client, - const NamespaceString& ns, - const BSONObj& insertedObj, - ErrorCodes::Error result) {} - -void mongo::audit::logKillCursorsAuthzCheck(Client* client, - const NamespaceString& ns, - long long cursorId, - ErrorCodes::Error result) {} - -void mongo::audit::logQueryAuthzCheck(Client* client, - const NamespaceString& ns, - const BSONObj& query, - ErrorCodes::Error result) {} - -void mongo::audit::logUpdateAuthzCheck(Client* client, - const NamespaceString& ns, - const BSONObj& query, - const write_ops::UpdateModification& update, - bool isUpsert, - bool isMulti, - ErrorCodes::Error result) {} - -void mongo::audit::logCreateUser(Client* client, - const UserName& username, - bool password, - const BSONObj* customData, - const std::vector<RoleName>& roles, - const boost::optional<BSONArray>& restrictions) {} - -void mongo::audit::logDropUser(Client* client, const UserName& username) {} - -void mongo::audit::logDropAllUsersFromDatabase(Client* client, StringData dbname) {} - -void mongo::audit::logUpdateUser(Client* client, - const UserName& username, - bool password, - const BSONObj* customData, - const std::vector<RoleName>* roles, - const boost::optional<BSONArray>& restrictions) {} - -void mongo::audit::logGrantRolesToUser(Client* client, - const UserName& username, - const std::vector<RoleName>& roles) {} - -void mongo::audit::logRevokeRolesFromUser(Client* client, - const UserName& username, - const std::vector<RoleName>& roles) {} - -void mongo::audit::logCreateRole(Client* client, - const RoleName& role, - const std::vector<RoleName>& roles, - const PrivilegeVector& privileges, - const boost::optional<BSONArray>& restrictions) {} +#if !MONGO_ENTERPRISE_AUDIT -void mongo::audit::logUpdateRole(Client* client, +ImpersonatedClientAttrs::ImpersonatedClientAttrs(Client* client) {} + +void logAuthentication(Client*, const AuthenticateEvent&) {} + +void logCommandAuthzCheck(Client* client, + const OpMsgRequest& cmdObj, + const CommandInterface& command, + ErrorCodes::Error result) {} + +void logDeleteAuthzCheck(Client* client, + const NamespaceString& ns, + const BSONObj& pattern, + ErrorCodes::Error result) {} + +void logGetMoreAuthzCheck(Client* client, + const NamespaceString& ns, + long long cursorId, + ErrorCodes::Error result) {} + +void logInsertAuthzCheck(Client* client, + const NamespaceString& ns, + const BSONObj& insertedObj, + ErrorCodes::Error result) {} + +void logKillCursorsAuthzCheck(Client* client, + const NamespaceString& ns, + long long cursorId, + ErrorCodes::Error result) {} + +void logQueryAuthzCheck(Client* client, + const NamespaceString& ns, + const BSONObj& query, + ErrorCodes::Error result) {} + +void logUpdateAuthzCheck(Client* client, + const NamespaceString& ns, + const BSONObj& query, + const write_ops::UpdateModification& update, + bool isUpsert, + bool isMulti, + ErrorCodes::Error result) {} + +void logCreateUser(Client* client, + const UserName& username, + bool password, + const BSONObj* customData, + const std::vector<RoleName>& roles, + const boost::optional<BSONArray>& restrictions) {} + +void logDropUser(Client* client, const UserName& username) {} + +void logDropAllUsersFromDatabase(Client* client, StringData dbname) {} + +void logUpdateUser(Client* client, + const UserName& username, + bool password, + const BSONObj* customData, + const std::vector<RoleName>* roles, + const boost::optional<BSONArray>& restrictions) {} + +void logGrantRolesToUser(Client* client, + const UserName& username, + const std::vector<RoleName>& roles) {} + +void logRevokeRolesFromUser(Client* client, + const UserName& username, + const std::vector<RoleName>& roles) {} + +void logCreateRole(Client* client, + const RoleName& role, + const std::vector<RoleName>& roles, + const PrivilegeVector& privileges, + const boost::optional<BSONArray>& restrictions) {} + +void logUpdateRole(Client* client, + const RoleName& role, + const std::vector<RoleName>* roles, + const PrivilegeVector* privileges, + const boost::optional<BSONArray>& restrictions) {} + +void logDropRole(Client* client, const RoleName& role) {} + +void logDropAllRolesFromDatabase(Client* client, StringData dbname) {} + +void logGrantRolesToRole(Client* client, const RoleName& role, const std::vector<RoleName>& roles) { +} + +void logRevokeRolesFromRole(Client* client, + const RoleName& role, + const std::vector<RoleName>& roles) {} + +void logGrantPrivilegesToRole(Client* client, + const RoleName& role, + const PrivilegeVector& privileges) {} + +void logRevokePrivilegesFromRole(Client* client, const RoleName& role, - const std::vector<RoleName>* roles, - const PrivilegeVector* privileges, - const boost::optional<BSONArray>& restrictions) {} - -void mongo::audit::logDropRole(Client* client, const RoleName& role) {} - -void mongo::audit::logDropAllRolesFromDatabase(Client* client, StringData dbname) {} + const PrivilegeVector& privileges) {} -void mongo::audit::logGrantRolesToRole(Client* client, - const RoleName& role, - const std::vector<RoleName>& roles) {} +void logReplSetReconfig(Client* client, const BSONObj* oldConfig, const BSONObj* newConfig) {} -void mongo::audit::logRevokeRolesFromRole(Client* client, - const RoleName& role, - const std::vector<RoleName>& roles) {} +void logApplicationMessage(Client* client, StringData msg) {} -void mongo::audit::logGrantPrivilegesToRole(Client* client, - const RoleName& role, - const PrivilegeVector& privileges) {} +void logStartupOptions(Client* client, const BSONObj& startupOptions) {} -void mongo::audit::logRevokePrivilegesFromRole(Client* client, - const RoleName& role, - const PrivilegeVector& privileges) {} +void logShutdown(Client* client) {} -void mongo::audit::logReplSetReconfig(Client* client, - const BSONObj* oldConfig, - const BSONObj* newConfig) {} +void logLogout(Client* client, + StringData reason, + const BSONArray& initialUsers, + const BSONArray& updatedUsers) {} -void mongo::audit::logApplicationMessage(Client* client, StringData msg) {} +void logCreateIndex(Client* client, + const BSONObj* indexSpec, + StringData indexname, + const NamespaceString& nsname) {} -void mongo::audit::logStartupOptions(Client* client, const BSONObj& startupOptions) {} +void logCreateCollection(Client* client, const NamespaceString& nsname) {} -void mongo::audit::logShutdown(Client* client) {} +void logCreateView(Client* client, + const NamespaceString& nsname, + StringData viewOn, + BSONArray pipeline, + ErrorCodes::Error code) {} -void mongo::audit::logLogout(Client* client, - StringData reason, - const BSONArray& initialUsers, - const BSONArray& updatedUsers) {} +void logImportCollection(Client* client, const NamespaceString& nsname) {} -void mongo::audit::logCreateIndex(Client* client, - const BSONObj* indexSpec, - StringData indexname, - const NamespaceString& nsname) {} +void logCreateDatabase(Client* client, StringData dbname) {} -void mongo::audit::logCreateCollection(Client* client, const NamespaceString& nsname) {} -void mongo::audit::logCreateView(Client* client, - const NamespaceString& nsname, - StringData viewOn, - BSONArray pipeline, - ErrorCodes::Error code) {} +void logDropIndex(Client* client, StringData indexname, const NamespaceString& nsname) {} -void mongo::audit::logImportCollection(Client* client, const NamespaceString& nsname) {} +void logDropCollection(Client* client, const NamespaceString& nsname) {} -void mongo::audit::logCreateDatabase(Client* client, StringData dbname) {} +void logDropView(Client* client, + const NamespaceString& nsname, + StringData viewOn, + const std::vector<BSONObj>& pipeline, + ErrorCodes::Error code) {} +void logDropDatabase(Client* client, StringData dbname) {} -void mongo::audit::logDropIndex(Client* client, - StringData indexname, - const NamespaceString& nsname) {} +void logRenameCollection(Client* client, + const NamespaceString& source, + const NamespaceString& target) {} -void mongo::audit::logDropCollection(Client* client, const NamespaceString& nsname) {} +void logEnableSharding(Client* client, StringData dbname) {} -void mongo::audit::logDropView(Client* client, - const NamespaceString& nsname, - StringData viewOn, - const std::vector<BSONObj>& pipeline, - ErrorCodes::Error code) {} +void logAddShard(Client* client, StringData name, const std::string& servers, long long maxSize) {} -void mongo::audit::logDropDatabase(Client* client, StringData dbname) {} +void logRemoveShard(Client* client, StringData shardname) {} -void mongo::audit::logRenameCollection(Client* client, - const NamespaceString& source, - const NamespaceString& target) {} +void logShardCollection(Client* client, StringData ns, const BSONObj& keyPattern, bool unique) {} -void mongo::audit::logEnableSharding(Client* client, StringData dbname) {} +void logRefineCollectionShardKey(Client* client, StringData ns, const BSONObj& keyPattern) {} -void mongo::audit::logAddShard(Client* client, - StringData name, - const std::string& servers, - long long maxSize) {} +void logInsertOperation(Client* client, const NamespaceString& nss, const BSONObj& doc) {} -void mongo::audit::logRemoveShard(Client* client, StringData shardname) {} +void logUpdateOperation(Client* client, const NamespaceString& nss, const BSONObj& doc) {} -void mongo::audit::logShardCollection(Client* client, - StringData ns, - const BSONObj& keyPattern, - bool unique) {} - -void mongo::audit::logRefineCollectionShardKey(Client* client, - StringData ns, - const BSONObj& keyPattern) {} - -void mongo::audit::logInsertOperation(Client* client, - const NamespaceString& nss, - const BSONObj& doc) {} - -void mongo::audit::logUpdateOperation(Client* client, - const NamespaceString& nss, - const BSONObj& doc) {} - -void mongo::audit::logRemoveOperation(Client* client, - const NamespaceString& nss, - const BSONObj& doc) {} +void logRemoveOperation(Client* client, const NamespaceString& nss, const BSONObj& doc) {} #endif + +} // namespace audit +} // namespace mongo diff --git a/src/mongo/db/audit.h b/src/mongo/db/audit.h index 1fb5a06f254..5f0c3e329db 100644 --- a/src/mongo/db/audit.h +++ b/src/mongo/db/audit.h @@ -39,11 +39,13 @@ #include "mongo/db/auth/user.h" #include "mongo/db/ops/write_ops.h" #include "mongo/rpc/op_msg.h" +#include "mongo/util/functional.h" namespace mongo { class AuthorizationSession; class BSONObj; +class BSONObjBuilder; class Client; class NamespaceString; class OperationContext; @@ -86,12 +88,59 @@ public: }; /** + * AuthenticateEvent is a opaque view into a finished authentication handshake. + * + * This object is only valid within its initial stack context. + */ +class AuthenticateEvent { +public: + using Appender = unique_function<void(BSONObjBuilder*)>; + + AuthenticateEvent(StringData mechanism, + StringData db, + StringData user, + Appender appender, + ErrorCodes::Error result) + : _mechanism(mechanism), + _db(db), + _user(user), + _appender(std::move(appender)), + _result(result) {} + + StringData getMechanism() const { + return _mechanism; + } + + StringData getDatabase() const { + return _db; + } + + StringData getUser() const { + return _user; + } + + ErrorCodes::Error getResult() const { + return _result; + } + + void appendExtraInfo(BSONObjBuilder* bob) const { + _appender(bob); + } + +private: + StringData _mechanism; + StringData _db; + StringData _user; + + Appender _appender; + + ErrorCodes::Error _result; +}; + +/** * Logs the result of an authentication attempt. */ -void logAuthentication(Client* client, - StringData mechanism, - const UserName& user, - ErrorCodes::Error result); +void logAuthentication(Client* client, const AuthenticateEvent& event); // // Authorization (authz) logging functions. diff --git a/src/mongo/db/auth/authentication_session.cpp b/src/mongo/db/auth/authentication_session.cpp index 45b70cacd68..c0fa8a8b898 100644 --- a/src/mongo/db/auth/authentication_session.cpp +++ b/src/mongo/db/auth/authentication_session.cpp @@ -91,6 +91,14 @@ auto registerer = ServiceContext::ConstructorActionRegisterer{ "AuthenticationClientObserver", [](ServiceContext* service) { service->registerClientObserver(std::make_unique<AuthenticationClientObserver>()); }}; + +auto makeAppender(ServerMechanismBase* mech) { + return [mech](BSONObjBuilder* bob) { + if (mech) { + mech->appendExtraInfo(bob); + } + }; +} } // namespace AuthenticationSession::StepGuard::StepGuard(OperationContext* opCtx, StepType currentStep) @@ -197,10 +205,12 @@ void AuthenticationSession::_verifyUserNameFromSaslSupportedMechanisms(const Use // Reset _ssmUserName since we have found a conflict. auto ssmUserName = std::exchange(_ssmUserName, {}); - audit::logAuthentication(_client, - auth::kSaslSupportedMechanisms, - std::move(ssmUserName), - ErrorCodes::AuthenticationAbandoned); + auto event = audit::AuthenticateEvent(auth::kSaslSupportedMechanisms, + ssmUserName.getDB(), + ssmUserName.getUser(), + makeAppender(_mech.get()), + ErrorCodes::AuthenticationAbandoned); + audit::logAuthentication(_client, event); } } @@ -264,7 +274,12 @@ void AuthenticationSession::markSuccessful() { _mechCounter->incSpeculativeAuthenticateSuccessful(); } - audit::logAuthentication(_client, _mechName, _userName, ErrorCodes::OK); + auto event = audit::AuthenticateEvent(_mechName, + _userName.getDB(), + _userName.getUser(), + makeAppender(_mech.get()), + ErrorCodes::OK); + audit::logAuthentication(_client, event); LOGV2_DEBUG(5286306, kDiagnosticLogLevel, @@ -280,7 +295,12 @@ void AuthenticationSession::markSuccessful() { void AuthenticationSession::markFailed(const Status& status) { _finish(); - audit::logAuthentication(_client, _mechName, _userName, status.code()); + auto event = audit::AuthenticateEvent(_mechName, + _userName.getDB(), + _userName.getUser(), + makeAppender(_mech.get()), + status.code()); + audit::logAuthentication(_client, event); LOGV2_DEBUG(5286307, kDiagnosticLogLevel, diff --git a/src/mongo/db/auth/sasl_commands.cpp b/src/mongo/db/auth/sasl_commands.cpp index 2c997e12c85..519f512ae90 100644 --- a/src/mongo/db/auth/sasl_commands.cpp +++ b/src/mongo/db/auth/sasl_commands.cpp @@ -50,6 +50,7 @@ #include "mongo/db/commands.h" #include "mongo/db/commands/authentication_commands.h" #include "mongo/db/server_options.h" +#include "mongo/logv2/attribute_storage.h" #include "mongo/logv2/log.h" #include "mongo/util/base64.h" #include "mongo/util/sequence_util.h" @@ -143,24 +144,33 @@ SaslReply doSaslStep(OperationContext* opCtx, // Passing in a payload and extracting a responsePayload StatusWith<std::string> swResponse = mechanism.step(opCtx, payload.get()); + auto makeLogAttributes = [&]() { + logv2::DynamicAttributes attrs; + attrs.add("mechanism", mechanism.mechanismName()); + attrs.add("speculative", session->isSpeculative()); + attrs.add("principalName", mechanism.getPrincipalName()); + attrs.add("authenticationDatabase", mechanism.getAuthenticationDatabase()); + attrs.addDeepCopy("remote", opCtx->getClient()->getRemote().toString()); + { + auto bob = BSONObjBuilder(); + mechanism.appendExtraInfo(&bob); + attrs.add("extraInfo", bob.obj()); + } + + return attrs; + }; + if (!swResponse.isOK()) { int64_t dLevel = 0; if (session->isSpeculative() && (swResponse.getStatus() == ErrorCodes::MechanismUnavailable)) { dLevel = 5; } - LOGV2_DEBUG(20249, - dLevel, - "SASL {mechanism} authentication failed for " - "{principalName} on {authenticationDatabase} from client " - "{client} ; {result}", - "Authentication failed", - "mechanism"_attr = mechanism.mechanismName(), - "speculative"_attr = session->isSpeculative(), - "principalName"_attr = mechanism.getPrincipalName(), - "authenticationDatabase"_attr = mechanism.getAuthenticationDatabase(), - "remote"_attr = opCtx->getClient()->getRemote(), - "result"_attr = redact(swResponse.getStatus())); + + auto attrs = makeLogAttributes(); + auto errorString = redact(swResponse.getStatus()); + attrs.add("error", errorString); + LOGV2_DEBUG(20249, dLevel, "Authentication failed", attrs); sleepmillis(saslGlobalParams.authFailedDelay.load()); // All the client needs to know is that authentication has failed. @@ -173,14 +183,8 @@ SaslReply doSaslStep(OperationContext* opCtx, AuthorizationSession::get(opCtx->getClient())->addAndAuthorizeUser(opCtx, userName)); if (!serverGlobalParams.quiet.load()) { - LOGV2(20250, - "Successfully authenticated as principal {principalName} on " - "{authenticationDatabase} from client {client} with mechanism {mechanism}", - "Successful authentication", - "mechanism"_attr = mechanism.mechanismName(), - "principalName"_attr = mechanism.getPrincipalName(), - "authenticationDatabase"_attr = mechanism.getAuthenticationDatabase(), - "remote"_attr = opCtx->getClient()->session()->remote()); + auto attrs = makeLogAttributes(); + LOGV2(20250, "Authentication succeeded", attrs); } session->markSuccessful(); diff --git a/src/mongo/db/auth/sasl_mechanism_registry.h b/src/mongo/db/auth/sasl_mechanism_registry.h index f23b0c5ac83..cceff146750 100644 --- a/src/mongo/db/auth/sasl_mechanism_registry.h +++ b/src/mongo/db/auth/sasl_mechanism_registry.h @@ -43,6 +43,7 @@ namespace mongo { class User; +class BSONObjBuilder; /** * The set of attributes SASL mechanisms may possess. @@ -145,6 +146,12 @@ public: } /** + * Appends mechanism specific info in BSON form. The schema of this BSON will vary by mechanism + * implementation, thus this info is entirely diagnostic/for records. + */ + virtual void appendExtraInfo(BSONObjBuilder*) const {} + + /** * Standard method in mongodb for determining if "authenticatedUser" may act as "requestedUser." * * The standard rule in MongoDB is simple. The authenticated user name must be the same as the |