diff options
author | Spencer Jackson <spencer.jackson@mongodb.com> | 2016-07-11 13:50:21 -0400 |
---|---|---|
committer | Spencer Jackson <spencer.jackson@mongodb.com> | 2016-07-29 19:27:08 -0400 |
commit | 62d931bf4ba6a4d881e53e10dd176a80d8f3b8b3 (patch) | |
tree | 676409e08781056977c858e6015775edb820b665 /src | |
parent | 5f288387f694706a14eedde8ab910ce234bc47b9 (diff) | |
download | mongo-62d931bf4ba6a4d881e53e10dd176a80d8f3b8b3.tar.gz |
SERVER-17856: Allow mongod users to currentOp and killOp own operations
(cherry picked from commit 9380a1c12a19a061eaafabb5f6b9e87f16a28179)
Diffstat (limited to 'src')
-rw-r--r-- | src/mongo/db/auth/SConscript | 3 | ||||
-rw-r--r-- | src/mongo/db/auth/auth_decorations.cpp | 18 | ||||
-rw-r--r-- | src/mongo/db/auth/authorization_manager_global.cpp | 18 | ||||
-rw-r--r-- | src/mongo/db/auth/authorization_session.cpp | 24 | ||||
-rw-r--r-- | src/mongo/db/auth/authorization_session.h | 7 | ||||
-rw-r--r-- | src/mongo/db/commands/current_op.cpp | 24 | ||||
-rw-r--r-- | src/mongo/db/commands/kill_op.cpp | 87 | ||||
-rw-r--r-- | src/mongo/db/service_context.cpp | 1 | ||||
-rw-r--r-- | src/mongo/db/service_context.h | 8 | ||||
-rw-r--r-- | src/mongo/db/service_context_d.cpp | 19 | ||||
-rw-r--r-- | src/mongo/db/service_context_d.h | 10 | ||||
-rw-r--r-- | src/mongo/db/service_context_noop.cpp | 4 | ||||
-rw-r--r-- | src/mongo/db/service_context_noop.h | 3 |
13 files changed, 156 insertions, 70 deletions
diff --git a/src/mongo/db/auth/SConscript b/src/mongo/db/auth/SConscript index f8935e8d588..a0b78e4d3b2 100644 --- a/src/mongo/db/auth/SConscript +++ b/src/mongo/db/auth/SConscript @@ -15,6 +15,7 @@ env.Library('authcore', ['action_set.cpp', 'action_type.cpp', 'authorization_manager.cpp', 'authorization_session.cpp', + 'auth_decorations.cpp', 'authz_manager_external_state.cpp', 'authz_manager_external_state_local.cpp', 'authz_session_external_state.cpp', @@ -38,6 +39,7 @@ env.Library('authcore', ['action_set.cpp', '$BUILD_DIR/mongo/db/common', '$BUILD_DIR/mongo/db/ops/update_driver', '$BUILD_DIR/mongo/db/namespace_string', + '$BUILD_DIR/mongo/db/service_context', '$BUILD_DIR/mongo/util/md5']) env.Library('authcommon', @@ -50,7 +52,6 @@ env.Library('authcommon', env.Library('authorization_manager_global', [ - 'auth_decorations.cpp', 'authorization_manager_global.cpp', ], LIBDEPS=[ diff --git a/src/mongo/db/auth/auth_decorations.cpp b/src/mongo/db/auth/auth_decorations.cpp index 2bd2264e0f9..558f3f9ba3c 100644 --- a/src/mongo/db/auth/auth_decorations.cpp +++ b/src/mongo/db/auth/auth_decorations.cpp @@ -31,13 +31,10 @@ #include <memory> #include <utility> -#include "mongo/base/init.h" #include "mongo/db/auth/authentication_session.h" #include "mongo/db/auth/authorization_manager.h" -#include "mongo/db/auth/authz_manager_external_state.h" #include "mongo/db/auth/authorization_session.h" #include "mongo/db/client.h" -#include "mongo/db/server_options.h" #include "mongo/db/service_context.h" #include "mongo/stdx/memory.h" #include "mongo/util/assert_util.h" @@ -45,21 +42,6 @@ namespace mongo { namespace { -MONGO_INITIALIZER_WITH_PREREQUISITES(CreateAuthorizationManager, - ("SetupInternalSecurityUser", - "OIDGeneration", - "SetGlobalEnvironment", - "CreateAuthorizationExternalStateFactory", - "EndStartupOptionStorage")) -(InitializerContext* context) { - auto authzManager = - stdx::make_unique<AuthorizationManager>(AuthzManagerExternalState::create()); - authzManager->setAuthEnabled(serverGlobalParams.authState == - ServerGlobalParams::AuthState::kEnabled); - AuthorizationManager::set(getGlobalServiceContext(), std::move(authzManager)); - return Status::OK(); -} - const auto getAuthenticationSession = ClientBasic::declareDecoration<std::unique_ptr<AuthenticationSession>>(); diff --git a/src/mongo/db/auth/authorization_manager_global.cpp b/src/mongo/db/auth/authorization_manager_global.cpp index 2fc20deef25..66ceab0bc08 100644 --- a/src/mongo/db/auth/authorization_manager_global.cpp +++ b/src/mongo/db/auth/authorization_manager_global.cpp @@ -32,8 +32,11 @@ #include "mongo/base/init.h" #include "mongo/db/auth/authorization_manager.h" #include "mongo/db/auth/authorization_manager_global.h" +#include "mongo/db/auth/authz_manager_external_state.h" +#include "mongo/db/server_options.h" #include "mongo/db/server_parameters.h" #include "mongo/db/service_context.h" +#include "mongo/stdx/memory.h" #include "mongo/util/assert_util.h" namespace mongo { @@ -84,4 +87,19 @@ AuthorizationManager* getGlobalAuthorizationManager() { return globalAuthManager; } +MONGO_INITIALIZER_WITH_PREREQUISITES(CreateAuthorizationManager, + ("SetupInternalSecurityUser", + "OIDGeneration", + "SetGlobalEnvironment", + "CreateAuthorizationExternalStateFactory", + "EndStartupOptionStorage")) +(InitializerContext* context) { + auto authzManager = + stdx::make_unique<AuthorizationManager>(AuthzManagerExternalState::create()); + authzManager->setAuthEnabled(serverGlobalParams.authState == + ServerGlobalParams::AuthState::kEnabled); + AuthorizationManager::set(getGlobalServiceContext(), std::move(authzManager)); + return Status::OK(); +} + } // namespace mongo diff --git a/src/mongo/db/auth/authorization_session.cpp b/src/mongo/db/auth/authorization_session.cpp index 14e3070b057..1007316052b 100644 --- a/src/mongo/db/auth/authorization_session.cpp +++ b/src/mongo/db/auth/authorization_session.cpp @@ -598,6 +598,30 @@ void AuthorizationSession::setImpersonatedUserData(std::vector<UserName> usernam _impersonationFlag = true; } +bool AuthorizationSession::isCoauthorizedWithClient(ClientBasic* opClient) { + auto getUserNames = [](AuthorizationSession* authSession) { + if (authSession->isImpersonating()) { + return authSession->getImpersonatedUserNames(); + } else { + return authSession->getAuthenticatedUserNames(); + } + }; + + UserNameIterator it = getUserNames(this); + while (it.more()) { + UserNameIterator opIt = getUserNames(AuthorizationSession::get(opClient)); + while (opIt.more()) { + if (it.get() == opIt.get()) { + return true; + } + opIt.next(); + } + it.next(); + } + + return false; +} + UserNameIterator AuthorizationSession::getImpersonatedUserNames() { return makeUserNameIterator(_impersonatedUserNames.begin(), _impersonatedUserNames.end()); } diff --git a/src/mongo/db/auth/authorization_session.h b/src/mongo/db/auth/authorization_session.h index e46e46cef3a..da11de0da2d 100644 --- a/src/mongo/db/auth/authorization_session.h +++ b/src/mongo/db/auth/authorization_session.h @@ -239,6 +239,13 @@ public: // Clears the data for impersonated users. void clearImpersonatedUserData(); + // Returns true if the session and 'opClient's AuthorizationSession share an + // authenticated user. If either object has impersonated users, + // those users will be considered as 'authenticated' for the purpose of this check. + // + // The existence of 'opClient' must be guaranteed through locks taken by the caller. + bool isCoauthorizedWithClient(ClientBasic* opClient); + // Tells whether impersonation is active or not. This state is set when // setImpersonatedUserData is called and cleared when clearImpersonatedUserData is // called. diff --git a/src/mongo/db/commands/current_op.cpp b/src/mongo/db/commands/current_op.cpp index f15f60807e7..3447b9a1150 100644 --- a/src/mongo/db/commands/current_op.cpp +++ b/src/mongo/db/commands/current_op.cpp @@ -68,9 +68,18 @@ public: Status checkAuthForCommand(ClientBasic* client, const std::string& dbname, const BSONObj& cmdObj) final { - bool isAuthorized = AuthorizationSession::get(client)->isAuthorizedForActionsOnResource( - ResourcePattern::forClusterResource(), ActionType::inprog); - return isAuthorized ? Status::OK() : Status(ErrorCodes::Unauthorized, "Unauthorized"); + AuthorizationSession* authzSession = AuthorizationSession::get(client); + if (authzSession->isAuthorizedForActionsOnResource(ResourcePattern::forClusterResource(), + ActionType::inprog)) { + return Status::OK(); + } + + bool isAuthenticated = authzSession->getAuthenticatedUserNames().more(); + if (isAuthenticated && cmdObj["$ownOps"].trueValue()) { + return Status::OK(); + } + + return Status(ErrorCodes::Unauthorized, "Unauthorized"); } bool run(OperationContext* txn, @@ -80,6 +89,7 @@ public: std::string& errmsg, BSONObjBuilder& result) final { const bool includeAll = cmdObj["$all"].trueValue(); + const bool ownOpsOnly = cmdObj["$ownOps"].trueValue(); // Filter the output BSONObj filter; @@ -92,6 +102,8 @@ public: BSONElement e = i.next(); if (str::equals("$all", e.fieldName())) { continue; + } else if (str::equals("$ownOps", e.fieldName())) { + continue; } b.append(e); @@ -113,6 +125,12 @@ public: invariant(client); stdx::lock_guard<Client> lk(*client); + + if (ownOpsOnly && + !AuthorizationSession::get(txn->getClient())->isCoauthorizedWithClient(client)) { + continue; + } + const OperationContext* opCtx = client->getOperationContext(); if (!includeAll) { diff --git a/src/mongo/db/commands/kill_op.cpp b/src/mongo/db/commands/kill_op.cpp index baaf8106220..139bea97a76 100644 --- a/src/mongo/db/commands/kill_op.cpp +++ b/src/mongo/db/commands/kill_op.cpp @@ -39,6 +39,7 @@ #include "mongo/db/auth/authorization_session.h" #include "mongo/db/client.h" #include "mongo/db/commands.h" +#include "mongo/db/operation_context.h" #include "mongo/db/service_context.h" #include "mongo/util/log.h" #include "mongo/util/mongoutils/str.h" @@ -61,12 +62,69 @@ public: return true; } + static long long parseOpId(const BSONObj& cmdObj) { + long long op; + uassertStatusOK(bsonExtractIntegerField(cmdObj, "op", &op)); + + // Internally opid is an unsigned 32-bit int, but as BSON only has signed integer types, + // we wrap values exceeding 2,147,483,647 to negative numbers. The following undoes this + // transformation, so users can use killOp on the (negative) opid they received. + if (op >= std::numeric_limits<int>::min() && op < 0) + op += 1ull << 32; + + uassert(26823, + str::stream() << "invalid op : " << op, + (op >= 0) && (op <= std::numeric_limits<unsigned int>::max())); + + + return op; + } + + static StatusWith<std::tuple<stdx::unique_lock<Client>, OperationContext*>> _findOp( + ClientBasic* client, unsigned int opId) { + AuthorizationSession* authzSession = AuthorizationSession::get(client); + + for (ServiceContext::LockedClientsCursor cursor(client->getServiceContext()); + Client* opClient = cursor.next();) { + stdx::unique_lock<Client> lk(*opClient); + + OperationContext* opCtx = opClient->getOperationContext(); + if (opCtx && opCtx->getOpID() == opId) { + if (authzSession->isAuthorizedForActionsOnResource( + ResourcePattern::forClusterResource(), ActionType::killop) || + authzSession->isCoauthorizedWithClient(opClient)) { + return {std::make_tuple(std::move(lk), opCtx)}; + } + break; + } + } + + return Status(ErrorCodes::NoSuchKey, str::stream() << "Could not access opID: " << opId); + } + Status checkAuthForCommand(ClientBasic* client, const std::string& dbname, const BSONObj& cmdObj) final { - bool isAuthorized = AuthorizationSession::get(client)->isAuthorizedForActionsOnResource( - ResourcePattern::forClusterResource(), ActionType::killop); - return isAuthorized ? Status::OK() : Status(ErrorCodes::Unauthorized, "Unauthorized"); + AuthorizationSession* authzSession = AuthorizationSession::get(client); + + if (authzSession->isAuthorizedForActionsOnResource(ResourcePattern::forClusterResource(), + ActionType::killop)) { + // If we have administrative permission to run killop, we don't need to traverse the + // Client list to figure out if we own the operation which will be terminated. + return Status::OK(); + } + + bool isAuthenticated = + AuthorizationSession::get(client)->getAuthenticatedUserNames().more(); + if (isAuthenticated) { + long long opId = parseOpId(cmdObj); + auto swLkAndOp = _findOp(client, opId); + if (swLkAndOp.isOK()) { + // We were able to find the Operation, and we were authorized to interact with it. + return Status::OK(); + } + } + return Status(ErrorCodes::Unauthorized, "Unauthorized"); } bool run(OperationContext* txn, @@ -75,23 +133,18 @@ public: int options, std::string& errmsg, BSONObjBuilder& result) final { - long long op; - uassertStatusOK(bsonExtractIntegerField(cmdObj, "op", &op)); + long long opId = parseOpId(cmdObj); - log() << "going to kill op: " << op; + log() << "going to kill op: " << opId; result.append("info", "attempting to kill op"); + auto swLkAndOp = _findOp(txn->getClient(), opId); + if (swLkAndOp.isOK()) { + stdx::unique_lock<Client> lk; + OperationContext* opCtx; + std::tie(lk, opCtx) = std::move(swLkAndOp.getValue()); + txn->getServiceContext()->killOperation(opCtx); + } - // Internally opid is an unsigned 32-bit int, but as BSON only has signed integer types, - // we wrap values exceeding 2,147,483,647 to negative numbers. The following undoes this - // transformation, so users can use killOp on the (negative) opid they received. - if (op >= std::numeric_limits<int>::min() && op < 0) - op += 1ull << 32; - - uassert(26823, - str::stream() << "invalid op : " << op, - (op >= 0) && (op <= std::numeric_limits<unsigned int>::max())); - - getGlobalServiceContext()->killOperation(static_cast<unsigned int>(op)); return true; } } killOpCmd; diff --git a/src/mongo/db/service_context.cpp b/src/mongo/db/service_context.cpp index 7fe0e237bdc..7db42f6b324 100644 --- a/src/mongo/db/service_context.cpp +++ b/src/mongo/db/service_context.cpp @@ -260,4 +260,5 @@ BSONArray storageEngineList() { void appendStorageEngineList(BSONObjBuilder* result) { result->append("storageEngines", storageEngineList()); } + } // namespace mongo diff --git a/src/mongo/db/service_context.h b/src/mongo/db/service_context.h index 4e63046726f..bde2b2995ff 100644 --- a/src/mongo/db/service_context.h +++ b/src/mongo/db/service_context.h @@ -275,10 +275,12 @@ public: virtual bool getKillAllOperations() = 0; /** - * @param i opid of operation to kill - * @return if operation was found + * Kills the operation "txn" with the code "killCode", if txn has not already been killed. + * Caller must own the lock on txn->getClient, and txn->getServiceContext() must be the same as + * this service context. **/ - virtual bool killOperation(unsigned int opId) = 0; + virtual void killOperation(OperationContext* txn, + ErrorCodes::Error killCode = ErrorCodes::Interrupted) = 0; /** * Kills all operations that have a Client that is associated with an incoming user diff --git a/src/mongo/db/service_context_d.cpp b/src/mongo/db/service_context_d.cpp index 470dcb927a5..7391aee277d 100644 --- a/src/mongo/db/service_context_d.cpp +++ b/src/mongo/db/service_context_d.cpp @@ -220,8 +220,7 @@ bool ServiceContextMongoD::getKillAllOperations() { return _globalKill; } -void ServiceContextMongoD::_killOperation_inlock(OperationContext* opCtx, - ErrorCodes::Error killCode) { +void ServiceContextMongoD::killOperation(OperationContext* opCtx, ErrorCodes::Error killCode) { opCtx->markKilled(killCode); for (const auto listener : _killOpListeners) { @@ -233,20 +232,6 @@ void ServiceContextMongoD::_killOperation_inlock(OperationContext* opCtx, } } -bool ServiceContextMongoD::killOperation(unsigned int opId) { - for (LockedClientsCursor cursor(this); Client* client = cursor.next();) { - stdx::lock_guard<Client> lk(*client); - - OperationContext* opCtx = client->getOperationContext(); - if (opCtx && opCtx->getOpID() == opId) { - _killOperation_inlock(opCtx, ErrorCodes::Interrupted); - return true; - } - } - - return false; -} - void ServiceContextMongoD::killAllUserOperations(const OperationContext* txn, ErrorCodes::Error killCode) { for (LockedClientsCursor cursor(this); Client* client = cursor.next();) { @@ -260,7 +245,7 @@ void ServiceContextMongoD::killAllUserOperations(const OperationContext* txn, // Don't kill ourself. if (toKill && toKill->getOpID() != txn->getOpID()) { - _killOperation_inlock(toKill, killCode); + killOperation(toKill, killCode); } } } diff --git a/src/mongo/db/service_context_d.h b/src/mongo/db/service_context_d.h index dc834197e9e..62cdb87b9ee 100644 --- a/src/mongo/db/service_context_d.h +++ b/src/mongo/db/service_context_d.h @@ -65,7 +65,8 @@ public: bool getKillAllOperations() override; - bool killOperation(unsigned int opId) override; + void killOperation(OperationContext* txn, + ErrorCodes::Error killCode = ErrorCodes::Interrupted) override; void killAllUserOperations(const OperationContext* txn, ErrorCodes::Error killCode) override; @@ -78,13 +79,6 @@ public: private: std::unique_ptr<OperationContext> _newOpCtx(Client* client) override; - /** - * Kills the given operation. - * - * Caller must own the service context's _mutex. - */ - void _killOperation_inlock(OperationContext* opCtx, ErrorCodes::Error killCode); - bool _globalKill; // protected by parent class's _mutex diff --git a/src/mongo/db/service_context_noop.cpp b/src/mongo/db/service_context_noop.cpp index fd94b35db89..8c8436991c0 100644 --- a/src/mongo/db/service_context_noop.cpp +++ b/src/mongo/db/service_context_noop.cpp @@ -75,8 +75,8 @@ bool ServiceContextNoop::getKillAllOperations() { return false; } -bool ServiceContextNoop::killOperation(unsigned int opId) { - return false; +void ServiceContextNoop::killOperation(OperationContext* txn, ErrorCodes::Error killCode) { + return; } void ServiceContextNoop::killAllUserOperations(const OperationContext* txn, diff --git a/src/mongo/db/service_context_noop.h b/src/mongo/db/service_context_noop.h index 62e6a169896..d5b505ef8e0 100644 --- a/src/mongo/db/service_context_noop.h +++ b/src/mongo/db/service_context_noop.h @@ -47,7 +47,8 @@ public: StorageFactoriesIterator* makeStorageFactoriesIterator() override; - bool killOperation(unsigned int opId) override; + void killOperation(OperationContext* txn, + ErrorCodes::Error killCode = ErrorCodes::Interrupted) override; void killAllUserOperations(const OperationContext* txn, ErrorCodes::Error killCode) override; |