summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorSpencer Jackson <spencer.jackson@mongodb.com>2016-07-11 13:50:21 -0400
committerSpencer Jackson <spencer.jackson@mongodb.com>2016-07-29 19:27:08 -0400
commit62d931bf4ba6a4d881e53e10dd176a80d8f3b8b3 (patch)
tree676409e08781056977c858e6015775edb820b665 /src
parent5f288387f694706a14eedde8ab910ce234bc47b9 (diff)
downloadmongo-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/SConscript3
-rw-r--r--src/mongo/db/auth/auth_decorations.cpp18
-rw-r--r--src/mongo/db/auth/authorization_manager_global.cpp18
-rw-r--r--src/mongo/db/auth/authorization_session.cpp24
-rw-r--r--src/mongo/db/auth/authorization_session.h7
-rw-r--r--src/mongo/db/commands/current_op.cpp24
-rw-r--r--src/mongo/db/commands/kill_op.cpp87
-rw-r--r--src/mongo/db/service_context.cpp1
-rw-r--r--src/mongo/db/service_context.h8
-rw-r--r--src/mongo/db/service_context_d.cpp19
-rw-r--r--src/mongo/db/service_context_d.h10
-rw-r--r--src/mongo/db/service_context_noop.cpp4
-rw-r--r--src/mongo/db/service_context_noop.h3
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;