diff options
author | Louis Williams <louis.williams@mongodb.com> | 2018-05-25 12:57:13 -0400 |
---|---|---|
committer | Louis Williams <louis.williams@mongodb.com> | 2018-05-25 12:57:13 -0400 |
commit | 7d0ea48dc8522f41e93b86d9c8f77c64b623ba60 (patch) | |
tree | 50273fdffe5a6ce820292182af98e609c57f45a9 | |
parent | b322ee9200172276b8d4935c623728129d62c3ef (diff) | |
download | mongo-7d0ea48dc8522f41e93b86d9c8f77c64b623ba60.tar.gz |
Revert "SERVER-34653 don't parse if early auth-checks can reject."
This reverts commit f2e762dc80e63fa47bd4c1d48e05f628464b0f54.
19 files changed, 69 insertions, 164 deletions
diff --git a/jstests/core/txns/commands_not_allowed_in_txn.js b/jstests/core/txns/commands_not_allowed_in_txn.js index d8d38a046c2..20bb7605bbb 100644 --- a/jstests/core/txns/commands_not_allowed_in_txn.js +++ b/jstests/core/txns/commands_not_allowed_in_txn.js @@ -85,8 +85,7 @@ } // - // Test commands that check out the session but are not allowed in multi-document - // transactions. + // Test commands that check out the session but are not allowed in multi-document transactions. // const sessionCommands = [ @@ -99,6 +98,7 @@ {group: {ns: collName, key: {_id: 1}, $reduce: function(curr, result) {}, initial: {}}}, {mapReduce: collName, map: function() {}, reduce: function(key, vals) {}, out: "out"}, {parallelCollectionScan: collName, numCursors: 1}, + {refreshLogicalSessionCacheNow: 1} ]; sessionCommands.forEach(testCommand); diff --git a/jstests/noPassthrough/end_sessions_command.js b/jstests/noPassthrough/end_sessions_command.js index 1ed1c471557..07bc9316494 100644 --- a/jstests/noPassthrough/end_sessions_command.js +++ b/jstests/noPassthrough/end_sessions_command.js @@ -72,11 +72,9 @@ { var session = conn.startSession(); - assert.commandWorked(session.getDatabase("admin").runCommand({usersInfo: 1}), - "do something to tickle the session"); assert.commandWorked(session.getDatabase("admin").runCommand(refresh), "failed to refresh"); assert.eq( - config.system.sessions.count(), 1, "usersInfo should have written 1 session record"); + config.system.sessions.count(), 1, "refresh should have written 1 session record"); session.endSession(); assert.commandWorked(admin.runCommand(refresh), "failed to refresh"); diff --git a/jstests/replsets/auth1.js b/jstests/replsets/auth1.js index 81d3ddca816..f784c366cb4 100644 --- a/jstests/replsets/auth1.js +++ b/jstests/replsets/auth1.js @@ -93,7 +93,7 @@ load("jstests/replsets/rslib.js"); "find did not throw, returned: " + tojson(r)) .toString(); printjson(error); - assert.gt(error.indexOf("command find requires authentication"), -1, "error was non-auth"); + assert.gt(error.indexOf("not authorized"), -1, "error was non-auth"); } doQueryOn(slave); diff --git a/jstests/sharding/cleanup_orphaned_auth.js b/jstests/sharding/cleanup_orphaned_auth.js index 4943643c33d..913bea9b1b7 100644 --- a/jstests/sharding/cleanup_orphaned_auth.js +++ b/jstests/sharding/cleanup_orphaned_auth.js @@ -9,8 +9,7 @@ if (assert._debug && msg) print("in assert for: " + msg); - if (res.ok == 0 && (res.errmsg.startsWith('not authorized') || - res.errmsg.match(/requires authentication/))) + if (res.ok == 0 && res.errmsg.startsWith('not authorized')) return; var finalMsg = "command worked when it should have been unauthorized: " + tojson(res); diff --git a/src/mongo/db/SConscript b/src/mongo/db/SConscript index 274f3d88204..dbf3eb40306 100644 --- a/src/mongo/db/SConscript +++ b/src/mongo/db/SConscript @@ -547,7 +547,6 @@ env.Library( 'audit', 'command_generic_argument', 'commands/server_status_core', - 'commands/test_commands_enabled', 'namespace_string', ], ) diff --git a/src/mongo/db/commands.cpp b/src/mongo/db/commands.cpp index edb6893aac3..30e064221d9 100644 --- a/src/mongo/db/commands.cpp +++ b/src/mongo/db/commands.cpp @@ -45,7 +45,6 @@ #include "mongo/db/auth/privilege.h" #include "mongo/db/client.h" #include "mongo/db/command_generic_argument.h" -#include "mongo/db/commands/test_commands_enabled.h" #include "mongo/db/curop.h" #include "mongo/db/jsobj.h" #include "mongo/db/namespace_string.h" @@ -55,7 +54,6 @@ #include "mongo/util/invariant.h" #include "mongo/util/log.h" #include "mongo/util/mongoutils/str.h" -#include "mongo/util/scopeguard.h" namespace mongo { @@ -71,62 +69,6 @@ const WriteConcernOptions kMajorityWriteConcern( WriteConcernOptions::SyncMode::UNSET, Seconds(60)); -// Returns true if found to be authorized, false if undecided. Throws if unauthorized. -bool checkAuthorizationImplPreParse(OperationContext* opCtx, - const Command* command, - const OpMsgRequest& request) { - auto client = opCtx->getClient(); - if (client->isInDirectClient()) - return true; - uassert(ErrorCodes::Unauthorized, - str::stream() << command->getName() << " may only be run against the admin database.", - !command->adminOnly() || request.getDatabase() == NamespaceString::kAdminDb); - - auto authzSession = AuthorizationSession::get(client); - if (!authzSession->getAuthorizationManager().isAuthEnabled()) { - // Running without auth, so everything should be allowed except remotely invoked - // commands that have the 'localHostOnlyIfNoAuth' restriction. - uassert(ErrorCodes::Unauthorized, - str::stream() << command->getName() - << " must run from localhost when running db without auth", - !command->adminOnly() || !command->localHostOnlyIfNoAuth() || - client->getIsLocalHostConnection()); - return true; // Blanket authorization: don't need to check anything else. - } - if (authzSession->isUsingLocalhostBypass()) - return false; // Still can't decide on auth because of the localhost bypass. - uassert(ErrorCodes::Unauthorized, - str::stream() << "command " << command->getName() << " requires authentication", - !command->requiresAuth() || authzSession->isAuthenticated()); - return false; -} - -void auditLogAuthEventImpl(OperationContext* opCtx, - const Command* command, - const NamespaceString& nss, - const OpMsgRequest& request, - ErrorCodes::Error err) { - class Hook final : public audit::CommandInterface { - public: - explicit Hook(const Command* command, const NamespaceString* nss) - : _command(command), _nss(nss) {} - - void redactForLogging(mutablebson::Document* cmdObj) const override { - _command->redactForLogging(cmdObj); - } - - NamespaceString ns() const override { - return *_nss; - } - - private: - const Command* _command; - const NamespaceString* _nss; - }; - - audit::logCommandAuthzCheck(opCtx->getClient(), request, Hook(command, &nss), err); -} - } // namespace @@ -154,18 +96,26 @@ BSONObj CommandHelpers::runCommandDirectly(OperationContext* opCtx, const OpMsgR return BSONObj(bb.release()); } -void CommandHelpers::auditLogAuthEvent(OperationContext* opCtx, - const CommandInvocation* invocation, - const OpMsgRequest& request, - ErrorCodes::Error err) { - auditLogAuthEventImpl(opCtx, invocation->definition(), invocation->ns(), request, err); -} +void CommandHelpers::logAuthViolation(OperationContext* opCtx, + const CommandInvocation* invocation, + const OpMsgRequest& request, + ErrorCodes::Error err) { + struct Hook final : public audit::CommandInterface { + public: + explicit Hook(const CommandInvocation* invocation) : _invocation{invocation} {} -void CommandHelpers::auditLogAuthEvent(OperationContext* opCtx, - const Command* command, - const OpMsgRequest& request, - ErrorCodes::Error err) { - auditLogAuthEventImpl(opCtx, command, NamespaceString(request.getDatabase()), request, err); + void redactForLogging(mutablebson::Document* cmdObj) const override { + _invocation->definition()->redactForLogging(cmdObj); + } + + NamespaceString ns() const override { + return _invocation->ns(); + } + + private: + const CommandInvocation* _invocation; + }; + audit::logCommandAuthzCheck(opCtx->getClient(), request, Hook(invocation), err); } void CommandHelpers::uassertNoDocumentSequences(StringData commandName, @@ -375,17 +325,6 @@ bool CommandHelpers::isHelpRequest(const BSONElement& helpElem) { return !helpElem.eoo() && helpElem.trueValue(); } -bool CommandHelpers::uassertShouldAttemptParse(OperationContext* opCtx, - const Command* command, - const OpMsgRequest& request) { - try { - return checkAuthorizationImplPreParse(opCtx, command, request); - } catch (const ExceptionFor<ErrorCodes::Unauthorized>& e) { - CommandHelpers::auditLogAuthEvent(opCtx, command, request, e.code()); - throw; - } -} - constexpr StringData CommandHelpers::kHelpFieldName; ////////////////////////////////////////////////////////////// @@ -413,29 +352,12 @@ CommandInvocation::~CommandInvocation() = default; void CommandInvocation::checkAuthorization(OperationContext* opCtx, const OpMsgRequest& request) const { - // Always send an authorization event to audit log, even if OK. - ErrorCodes::Error auditCode = ErrorCodes::OK; - auto auditCodeGuard = - MakeGuard([&] { CommandHelpers::auditLogAuthEvent(opCtx, this, request, auditCode); }); try { - const Command* c = definition(); - if (checkAuthorizationImplPreParse(opCtx, c, request)) { - return; // Blanket authorization: don't need to check anything else. - } - try { - doCheckAuthorization(opCtx); - } catch (const ExceptionFor<ErrorCodes::Unauthorized>&) { - namespace mmb = mutablebson; - mmb::Document cmdToLog(request.body, mmb::Document::kInPlaceDisabled); - c->redactForLogging(&cmdToLog); - auto dbname = request.getDatabase(); - uasserted(ErrorCodes::Unauthorized, - str::stream() << "not authorized on " << dbname << " to execute command " - << redact(cmdToLog.getObject())); - } + _checkAuthorizationImpl(opCtx, request); + CommandHelpers::logAuthViolation(opCtx, this, request, ErrorCodes::OK); } catch (const DBException& e) { log(LogComponent::kAccessControl) << e.toStatus(); - auditCode = e.code(); + CommandHelpers::logAuthViolation(opCtx, this, request, e.code()); throw; } } @@ -458,7 +380,7 @@ private: bool ok = _command->run(opCtx, _dbName, _request->body, bob); CommandHelpers::appendSimpleCommandStatus(bob, ok); } catch (const ExceptionFor<ErrorCodes::Unauthorized>& e) { - CommandHelpers::auditLogAuthEvent(opCtx, this, *_request, e.code()); + CommandHelpers::logAuthViolation(opCtx, this, *_request, e.code()); throw; } } @@ -537,6 +459,36 @@ Status BasicCommand::checkAuthForCommand(Client* client, return Status(ErrorCodes::Unauthorized, "unauthorized"); } +void CommandInvocation::_checkAuthorizationImpl(OperationContext* opCtx, + const OpMsgRequest& request) const { + const Command* c = definition(); + auto client = opCtx->getClient(); + auto dbname = request.getDatabase(); + uassert(ErrorCodes::Unauthorized, + str::stream() << c->getName() << " may only be run against the admin database.", + !c->adminOnly() || dbname == NamespaceString::kAdminDb); + if (!AuthorizationSession::get(client)->getAuthorizationManager().isAuthEnabled()) { + // Running without auth, so everything should be allowed except remotely invoked commands + // that have the 'localHostOnlyIfNoAuth' restriction. + uassert(ErrorCodes::Unauthorized, + str::stream() << c->getName() + << " must run from localhost when running db without auth", + !c->adminOnly() || !c->localHostOnlyIfNoAuth() || + client->getIsLocalHostConnection()); + return; // Blanket authorization: don't need to check anything else. + } + try { + doCheckAuthorization(opCtx); + } catch (const ExceptionFor<ErrorCodes::Unauthorized>&) { + namespace mmb = mutablebson; + mmb::Document cmdToLog(request.body, mmb::Document::kInPlaceDisabled); + c->redactForLogging(&cmdToLog); + uasserted(ErrorCodes::Unauthorized, + str::stream() << "not authorized on " << dbname << " to execute command " + << redact(cmdToLog.getObject())); + } +} + void Command::generateHelpResponse(OperationContext* opCtx, rpc::ReplyBuilderInterface* replyBuilder, const Command& command) { diff --git a/src/mongo/db/commands.h b/src/mongo/db/commands.h index 9ec2f993eb5..5e35ff22cc6 100644 --- a/src/mongo/db/commands.h +++ b/src/mongo/db/commands.h @@ -216,31 +216,13 @@ struct CommandHelpers { */ static BSONObj runCommandDirectly(OperationContext* opCtx, const OpMsgRequest& request); - static void auditLogAuthEvent(OperationContext* opCtx, - const CommandInvocation* invocation, - const OpMsgRequest& request, - ErrorCodes::Error err); - /** - * Overload taking a Command instead of CommandInvocation. It has to punt on the logged - * namespace, giving only the request's $db. Since the Command hasn't parsed the request body, - * we can't know the collection part of that namespace, so we leave it blank in the audit log. - */ - static void auditLogAuthEvent(OperationContext* opCtx, - const Command* command, - const OpMsgRequest& request, - ErrorCodes::Error err); + static void logAuthViolation(OperationContext* opCtx, + const CommandInvocation* invocation, + const OpMsgRequest& request, + ErrorCodes::Error err); static void uassertNoDocumentSequences(StringData commandName, const OpMsgRequest& request); - /** - * Should be called before trying to Command::parse a request. Throws 'Unauthorized', - * and emits an audit log entry, as an early failure if the calling client can't invoke that - * Command. Returns true if no more auth checks should be performed. - */ - static bool uassertShouldAttemptParse(OperationContext* opCtx, - const Command* command, - const OpMsgRequest& request); - static constexpr StringData kHelpFieldName = "help"_sd; }; @@ -552,6 +534,8 @@ private: */ virtual void doCheckAuthorization(OperationContext* opCtx) const = 0; + void _checkAuthorizationImpl(OperationContext* opCtx, const OpMsgRequest& request) const; + const Command* const _definition; }; diff --git a/src/mongo/db/commands/authentication_commands.cpp b/src/mongo/db/commands/authentication_commands.cpp index 1dcaaeb6fc7..00bf6dd23cb 100644 --- a/src/mongo/db/commands/authentication_commands.cpp +++ b/src/mongo/db/commands/authentication_commands.cpp @@ -131,9 +131,6 @@ public: virtual bool supportsWriteConcern(const BSONObj& cmd) const override { return false; } - bool requiresAuth() const override { - return false; - } virtual void addRequiredPrivileges(const std::string& dbname, const BSONObj& cmdObj, std::vector<Privilege>* out) const {} // No auth required @@ -199,9 +196,6 @@ public: return false; } - bool requiresAuth() const override { - return false; - } void addRequiredPrivileges(const std::string& dbname, const BSONObj& cmdObj, std::vector<Privilege>* out) const final { diff --git a/src/mongo/db/commands/connection_status.cpp b/src/mongo/db/commands/connection_status.cpp index a4bd0fe3ead..bd71bcdbe0f 100644 --- a/src/mongo/db/commands/connection_status.cpp +++ b/src/mongo/db/commands/connection_status.cpp @@ -47,9 +47,6 @@ public: virtual bool supportsWriteConcern(const BSONObj& cmd) const override { return false; } - bool requiresAuth() const override { - return false; - } virtual void addRequiredPrivileges(const std::string& dbname, const BSONObj& cmdObj, std::vector<Privilege>* out) const {} // No auth required diff --git a/src/mongo/db/commands/explain_cmd.cpp b/src/mongo/db/commands/explain_cmd.cpp index 003047168db..71dfc51d19d 100644 --- a/src/mongo/db/commands/explain_cmd.cpp +++ b/src/mongo/db/commands/explain_cmd.cpp @@ -101,8 +101,7 @@ public: BSONObjBuilder bob = result->getBodyBuilder(); _innerInvocation->explain(opCtx, _verbosity, &bob); } catch (const ExceptionFor<ErrorCodes::Unauthorized>&) { - CommandHelpers::auditLogAuthEvent( - opCtx, this, *_outerRequest, ErrorCodes::Unauthorized); + CommandHelpers::logAuthViolation(opCtx, this, *_outerRequest, ErrorCodes::Unauthorized); throw; } } diff --git a/src/mongo/db/commands/fail_point_cmd.cpp b/src/mongo/db/commands/fail_point_cmd.cpp index 567d5f9db0e..89451c1ca21 100644 --- a/src/mongo/db/commands/fail_point_cmd.cpp +++ b/src/mongo/db/commands/fail_point_cmd.cpp @@ -81,10 +81,6 @@ public: return true; } - bool requiresAuth() const override { - return false; - } - // No auth needed because it only works when enabled via command line. virtual void addRequiredPrivileges(const std::string& dbname, const BSONObj& cmdObj, diff --git a/src/mongo/db/commands/refresh_logical_session_cache_now.cpp b/src/mongo/db/commands/refresh_logical_session_cache_now.cpp index c99f704ca5c..9a8124a1e4e 100644 --- a/src/mongo/db/commands/refresh_logical_session_cache_now.cpp +++ b/src/mongo/db/commands/refresh_logical_session_cache_now.cpp @@ -62,10 +62,6 @@ public: return "force the logical session cache to refresh. Test command only."; } - bool requiresAuth() const override { - return false; - } - // No auth needed because it only works when enabled via command line. Status checkAuthForOperation(OperationContext* opCtx, const std::string& dbname, diff --git a/src/mongo/db/commands/write_commands/write_commands.cpp b/src/mongo/db/commands/write_commands/write_commands.cpp index 50af2a67329..4625b571cea 100644 --- a/src/mongo/db/commands/write_commands/write_commands.cpp +++ b/src/mongo/db/commands/write_commands/write_commands.cpp @@ -237,7 +237,7 @@ private: throw; } } catch (const ExceptionFor<ErrorCodes::Unauthorized>&) { - CommandHelpers::auditLogAuthEvent(opCtx, this, *_request, ErrorCodes::Unauthorized); + CommandHelpers::logAuthViolation(opCtx, this, *_request, ErrorCodes::Unauthorized); throw; } } diff --git a/src/mongo/db/service_entry_point_common.cpp b/src/mongo/db/service_entry_point_common.cpp index 0bf931f4eab..116a5098a7d 100644 --- a/src/mongo/db/service_entry_point_common.cpp +++ b/src/mongo/db/service_entry_point_common.cpp @@ -591,7 +591,6 @@ void execCommandDatabase(OperationContext* opCtx, const OpMsgRequest& request, rpc::ReplyBuilderInterface* replyBuilder, const ServiceEntryPointCommon::Hooks& behaviors) { - CommandHelpers::uassertShouldAttemptParse(opCtx, command, request); BSONObjBuilder extraFieldsBuilder; auto startOperationTime = getClientOperationTime(opCtx); auto invocation = command->parse(opCtx, request); diff --git a/src/mongo/s/commands/cluster_explain_cmd.cpp b/src/mongo/s/commands/cluster_explain_cmd.cpp index f5e49fdb401..cf9125bdeea 100644 --- a/src/mongo/s/commands/cluster_explain_cmd.cpp +++ b/src/mongo/s/commands/cluster_explain_cmd.cpp @@ -101,8 +101,7 @@ private: auto bob = result->getBodyBuilder(); _innerInvocation->explain(opCtx, _verbosity, &bob); } catch (const ExceptionFor<ErrorCodes::Unauthorized>&) { - CommandHelpers::auditLogAuthEvent( - opCtx, this, *_outerRequest, ErrorCodes::Unauthorized); + CommandHelpers::logAuthViolation(opCtx, this, *_outerRequest, ErrorCodes::Unauthorized); throw; } } diff --git a/src/mongo/s/commands/cluster_is_db_grid_cmd.cpp b/src/mongo/s/commands/cluster_is_db_grid_cmd.cpp index fc473d7a637..e876744d1d2 100644 --- a/src/mongo/s/commands/cluster_is_db_grid_cmd.cpp +++ b/src/mongo/s/commands/cluster_is_db_grid_cmd.cpp @@ -38,9 +38,6 @@ class IsDbGridCmd : public BasicCommand { public: IsDbGridCmd() : BasicCommand("isdbgrid") {} - bool requiresAuth() const override { - return false; - } virtual bool supportsWriteConcern(const BSONObj& cmd) const override { return false; diff --git a/src/mongo/s/commands/cluster_whats_my_uri_cmd.cpp b/src/mongo/s/commands/cluster_whats_my_uri_cmd.cpp index d94b9570535..c0092996a8a 100644 --- a/src/mongo/s/commands/cluster_whats_my_uri_cmd.cpp +++ b/src/mongo/s/commands/cluster_whats_my_uri_cmd.cpp @@ -42,6 +42,7 @@ public: return AllowedOnSecondary::kAlways; } + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { return false; } @@ -50,10 +51,6 @@ public: return "{whatsmyuri:1}"; } - bool requiresAuth() const override { - return false; - } - virtual void addRequiredPrivileges(const std::string& dbname, const BSONObj& cmdObj, std::vector<Privilege>* out) const { diff --git a/src/mongo/s/commands/cluster_write_cmd.cpp b/src/mongo/s/commands/cluster_write_cmd.cpp index d0fab4af8cf..84a4036fe1e 100644 --- a/src/mongo/s/commands/cluster_write_cmd.cpp +++ b/src/mongo/s/commands/cluster_write_cmd.cpp @@ -308,7 +308,7 @@ private: bool ok = runImpl(opCtx, *_request, _batchedRequest, bob); CommandHelpers::appendSimpleCommandStatus(bob, ok); } catch (const ExceptionFor<ErrorCodes::Unauthorized>&) { - CommandHelpers::auditLogAuthEvent(opCtx, this, *_request, ErrorCodes::Unauthorized); + CommandHelpers::logAuthViolation(opCtx, this, *_request, ErrorCodes::Unauthorized); throw; } } diff --git a/src/mongo/s/commands/strategy.cpp b/src/mongo/s/commands/strategy.cpp index c1997dff559..80873b48382 100644 --- a/src/mongo/s/commands/strategy.cpp +++ b/src/mongo/s/commands/strategy.cpp @@ -300,7 +300,6 @@ void runCommand(OperationContext* opCtx, return; } - CommandHelpers::uassertShouldAttemptParse(opCtx, command, request); // Transactions are disallowed in sharded clusters in MongoDB 4.0. uassert(50841, "Multi-document transactions cannot be run in a sharded cluster.", |