summaryrefslogtreecommitdiff
path: root/src/mongo/db
diff options
context:
space:
mode:
authorTess Avitabile <tess.avitabile@mongodb.com>2017-03-21 11:22:11 -0400
committerTess Avitabile <tess.avitabile@mongodb.com>2017-03-22 13:09:21 -0400
commitd66405f651b0a49a06aacb286e3d1740a0b020af (patch)
tree86f20f45d29d63b53137772c13ea8e917193b18e /src/mongo/db
parent70151a3b5cc65bd1b16831c523a6f5b477b82c3d (diff)
downloadmongo-d66405f651b0a49a06aacb286e3d1740a0b020af.tar.gz
SERVER-9609 Ensure users can only call getMore on cursors they created
Diffstat (limited to 'src/mongo/db')
-rw-r--r--src/mongo/db/auth/authorization_session.cpp22
-rw-r--r--src/mongo/db/auth/authorization_session.h5
-rw-r--r--src/mongo/db/auth/authorization_session_test.cpp156
-rw-r--r--src/mongo/db/clientcursor.cpp1
-rw-r--r--src/mongo/db/clientcursor.h16
-rw-r--r--src/mongo/db/commands/find_cmd.cpp1
-rw-r--r--src/mongo/db/commands/getmore_cmd.cpp12
-rw-r--r--src/mongo/db/commands/list_collections.cpp1
-rw-r--r--src/mongo/db/commands/list_indexes.cpp1
-rw-r--r--src/mongo/db/commands/parallel_collection_scan.cpp1
-rw-r--r--src/mongo/db/commands/repair_cursor.cpp1
-rw-r--r--src/mongo/db/commands/run_aggregate.cpp2
-rw-r--r--src/mongo/db/query/find.cpp12
13 files changed, 230 insertions, 1 deletions
diff --git a/src/mongo/db/auth/authorization_session.cpp b/src/mongo/db/auth/authorization_session.cpp
index 0fdf38884cd..c8fdb8b6332 100644
--- a/src/mongo/db/auth/authorization_session.cpp
+++ b/src/mongo/db/auth/authorization_session.cpp
@@ -49,6 +49,7 @@
#include "mongo/db/jsobj.h"
#include "mongo/db/namespace_string.h"
#include "mongo/db/pipeline/pipeline.h"
+#include "mongo/db/server_options.h"
#include "mongo/util/assert_util.h"
#include "mongo/util/log.h"
#include "mongo/util/mongoutils/str.h"
@@ -855,6 +856,27 @@ bool AuthorizationSession::isCoauthorizedWithClient(Client* opClient) {
return false;
}
+bool AuthorizationSession::isCoauthorizedWith(UserNameIterator userNameIter) {
+ if (!getAuthorizationManager().isAuthEnabled()) {
+ return true;
+ }
+ if (!userNameIter.more() && !getAuthenticatedUserNames().more()) {
+ return true;
+ }
+
+ for (; userNameIter.more(); userNameIter.next()) {
+ for (UserNameIterator thisUserNameIter = getAuthenticatedUserNames();
+ thisUserNameIter.more();
+ thisUserNameIter.next()) {
+ if (*userNameIter == *thisUserNameIter) {
+ return true;
+ }
+ }
+ }
+
+ 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 c2bb3fbfafd..5d6089b3a9f 100644
--- a/src/mongo/db/auth/authorization_session.h
+++ b/src/mongo/db/auth/authorization_session.h
@@ -271,6 +271,11 @@ public:
// The existence of 'opClient' must be guaranteed through locks taken by the caller.
bool isCoauthorizedWithClient(Client* opClient);
+ // Returns true if the session and 'userNameIter' share an authenticated user, or if both have
+ // no authenticated users. Impersonated users are not considered as 'authenticated' for the
+ // purpose of this check. This always returns true if auth is not enabled.
+ bool isCoauthorizedWith(UserNameIterator userNameIter);
+
// 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/auth/authorization_session_test.cpp b/src/mongo/db/auth/authorization_session_test.cpp
index ec82f193947..3764227069d 100644
--- a/src/mongo/db/auth/authorization_session_test.cpp
+++ b/src/mongo/db/auth/authorization_session_test.cpp
@@ -763,5 +763,161 @@ TEST_F(AuthorizationSessionTest,
BSONObj cmdObj = BSON("aggregate" << testFooNss.coll() << "pipeline" << pipeline);
ASSERT_OK(authzSession->checkAuthForAggregate(testFooNss, cmdObj));
}
+
+TEST_F(AuthorizationSessionTest, UnauthorizedSessionIsCoauthorizedWithEmptyUserSet) {
+ std::vector<UserName> userSet;
+ ASSERT_TRUE(
+ authzSession->isCoauthorizedWith(makeUserNameIterator(userSet.begin(), userSet.end())));
+}
+
+TEST_F(AuthorizationSessionTest, UnauthorizedSessionIsNotCoauthorizedWithNonemptyUserSet) {
+ std::vector<UserName> userSet;
+ userSet.emplace_back("spencer", "test");
+ ASSERT_FALSE(
+ authzSession->isCoauthorizedWith(makeUserNameIterator(userSet.begin(), userSet.end())));
+}
+
+TEST_F(AuthorizationSessionTest,
+ UnauthorizedSessionIsCoauthorizedWithNonemptyUserSetWhenAuthIsDisabled) {
+ authzManager->setAuthEnabled(false);
+ std::vector<UserName> userSet;
+ userSet.emplace_back("spencer", "test");
+ ASSERT_TRUE(
+ authzSession->isCoauthorizedWith(makeUserNameIterator(userSet.begin(), userSet.end())));
+}
+
+TEST_F(AuthorizationSessionTest, AuthorizedSessionIsNotCoauthorizedWithEmptyUserSet) {
+ ASSERT_OK(managerState->insertPrivilegeDocument(&_opCtx,
+ BSON("user"
+ << "spencer"
+ << "db"
+ << "test"
+ << "credentials"
+ << BSON("MONGODB-CR"
+ << "a")
+ << "roles"
+ << BSONArray()),
+ BSONObj()));
+ ASSERT_OK(authzSession->addAndAuthorizeUser(&_opCtx, UserName("spencer", "test")));
+ std::vector<UserName> userSet;
+ ASSERT_FALSE(
+ authzSession->isCoauthorizedWith(makeUserNameIterator(userSet.begin(), userSet.end())));
+}
+
+TEST_F(AuthorizationSessionTest,
+ AuthorizedSessionIsCoauthorizedWithEmptyUserSetWhenAuthIsDisabled) {
+ authzManager->setAuthEnabled(false);
+ ASSERT_OK(managerState->insertPrivilegeDocument(&_opCtx,
+ BSON("user"
+ << "spencer"
+ << "db"
+ << "test"
+ << "credentials"
+ << BSON("MONGODB-CR"
+ << "a")
+ << "roles"
+ << BSONArray()),
+ BSONObj()));
+ ASSERT_OK(authzSession->addAndAuthorizeUser(&_opCtx, UserName("spencer", "test")));
+ std::vector<UserName> userSet;
+ ASSERT_TRUE(
+ authzSession->isCoauthorizedWith(makeUserNameIterator(userSet.begin(), userSet.end())));
+}
+
+TEST_F(AuthorizationSessionTest, AuthorizedSessionIsCoauthorizedWithIntersectingUserSet) {
+ ASSERT_OK(managerState->insertPrivilegeDocument(&_opCtx,
+ BSON("user"
+ << "spencer"
+ << "db"
+ << "test"
+ << "credentials"
+ << BSON("MONGODB-CR"
+ << "a")
+ << "roles"
+ << BSONArray()),
+ BSONObj()));
+ ASSERT_OK(managerState->insertPrivilegeDocument(&_opCtx,
+ BSON("user"
+ << "admin"
+ << "db"
+ << "test"
+ << "credentials"
+ << BSON("MONGODB-CR"
+ << "a")
+ << "roles"
+ << BSONArray()),
+ BSONObj()));
+ ASSERT_OK(authzSession->addAndAuthorizeUser(&_opCtx, UserName("spencer", "test")));
+ ASSERT_OK(authzSession->addAndAuthorizeUser(&_opCtx, UserName("admin", "test")));
+ std::vector<UserName> userSet;
+ userSet.emplace_back("admin", "test");
+ userSet.emplace_back("tess", "test");
+ ASSERT_TRUE(
+ authzSession->isCoauthorizedWith(makeUserNameIterator(userSet.begin(), userSet.end())));
+}
+
+TEST_F(AuthorizationSessionTest, AuthorizedSessionIsNotCoauthorizedWithNonintersectingUserSet) {
+ ASSERT_OK(managerState->insertPrivilegeDocument(&_opCtx,
+ BSON("user"
+ << "spencer"
+ << "db"
+ << "test"
+ << "credentials"
+ << BSON("MONGODB-CR"
+ << "a")
+ << "roles"
+ << BSONArray()),
+ BSONObj()));
+ ASSERT_OK(managerState->insertPrivilegeDocument(&_opCtx,
+ BSON("user"
+ << "admin"
+ << "db"
+ << "test"
+ << "credentials"
+ << BSON("MONGODB-CR"
+ << "a")
+ << "roles"
+ << BSONArray()),
+ BSONObj()));
+ ASSERT_OK(authzSession->addAndAuthorizeUser(&_opCtx, UserName("spencer", "test")));
+ ASSERT_OK(authzSession->addAndAuthorizeUser(&_opCtx, UserName("admin", "test")));
+ std::vector<UserName> userSet;
+ userSet.emplace_back("tess", "test");
+ ASSERT_FALSE(
+ authzSession->isCoauthorizedWith(makeUserNameIterator(userSet.begin(), userSet.end())));
+}
+
+TEST_F(AuthorizationSessionTest,
+ AuthorizedSessionIsCoauthorizedWithNonintersectingUserSetWhenAuthIsDisabled) {
+ authzManager->setAuthEnabled(false);
+ ASSERT_OK(managerState->insertPrivilegeDocument(&_opCtx,
+ BSON("user"
+ << "spencer"
+ << "db"
+ << "test"
+ << "credentials"
+ << BSON("MONGODB-CR"
+ << "a")
+ << "roles"
+ << BSONArray()),
+ BSONObj()));
+ ASSERT_OK(managerState->insertPrivilegeDocument(&_opCtx,
+ BSON("user"
+ << "admin"
+ << "db"
+ << "test"
+ << "credentials"
+ << BSON("MONGODB-CR"
+ << "a")
+ << "roles"
+ << BSONArray()),
+ BSONObj()));
+ ASSERT_OK(authzSession->addAndAuthorizeUser(&_opCtx, UserName("spencer", "test")));
+ ASSERT_OK(authzSession->addAndAuthorizeUser(&_opCtx, UserName("admin", "test")));
+ std::vector<UserName> userSet;
+ userSet.emplace_back("tess", "test");
+ ASSERT_TRUE(
+ authzSession->isCoauthorizedWith(makeUserNameIterator(userSet.begin(), userSet.end())));
+}
} // namespace
} // namespace mongo
diff --git a/src/mongo/db/clientcursor.cpp b/src/mongo/db/clientcursor.cpp
index 70003c2da2b..a662ac9dfbe 100644
--- a/src/mongo/db/clientcursor.cpp
+++ b/src/mongo/db/clientcursor.cpp
@@ -82,6 +82,7 @@ ClientCursor::ClientCursor(ClientCursorParams&& params,
CursorId cursorId)
: _cursorid(cursorId),
_nss(std::move(params.nss)),
+ _authenticatedUsers(std::move(params.authenticatedUsers)),
_isReadCommitted(params.isReadCommitted),
_cursorManager(cursorManager),
_originatingCommand(params.originatingCommandObj),
diff --git a/src/mongo/db/clientcursor.h b/src/mongo/db/clientcursor.h
index 91e7a0d325c..384cede1fd6 100644
--- a/src/mongo/db/clientcursor.h
+++ b/src/mongo/db/clientcursor.h
@@ -29,6 +29,7 @@
#pragma once
#include "mongo/client/dbclientinterface.h"
+#include "mongo/db/auth/user_name.h"
#include "mongo/db/cursor_id.h"
#include "mongo/db/jsobj.h"
#include "mongo/db/query/plan_executor.h"
@@ -52,6 +53,7 @@ class RecoveryUnit;
struct ClientCursorParams {
ClientCursorParams(std::unique_ptr<PlanExecutor> planExecutor,
NamespaceString nss,
+ UserNameIterator authenticatedUsersIter,
bool isReadCommitted,
BSONObj originatingCommandObj)
: exec(std::move(planExecutor)),
@@ -60,10 +62,15 @@ struct ClientCursorParams {
queryOptions(exec->getCanonicalQuery()
? exec->getCanonicalQuery()->getQueryRequest().getOptions()
: 0),
- originatingCommandObj(originatingCommandObj.getOwned()) {}
+ originatingCommandObj(originatingCommandObj.getOwned()) {
+ while (authenticatedUsersIter.more()) {
+ authenticatedUsers.emplace_back(authenticatedUsersIter.next());
+ }
+ }
std::unique_ptr<PlanExecutor> exec;
const NamespaceString nss;
+ std::vector<UserName> authenticatedUsers;
bool isReadCommitted = false;
int queryOptions = 0;
BSONObj originatingCommandObj;
@@ -97,6 +104,10 @@ public:
return _nss;
}
+ UserNameIterator getAuthenticatedUsers() const {
+ return makeUserNameIterator(_authenticatedUsers.begin(), _authenticatedUsers.end());
+ }
+
bool isReadCommitted() const {
return _isReadCommitted;
}
@@ -253,6 +264,9 @@ private:
// The namespace we're operating on.
const NamespaceString _nss;
+ // The set of authenticated users when this cursor was created.
+ std::vector<UserName> _authenticatedUsers;
+
const bool _isReadCommitted = false;
// A pointer to the CursorManager which owns this cursor. This must be filled out when the
diff --git a/src/mongo/db/commands/find_cmd.cpp b/src/mongo/db/commands/find_cmd.cpp
index 60ae2e2eba4..30620833db1 100644
--- a/src/mongo/db/commands/find_cmd.cpp
+++ b/src/mongo/db/commands/find_cmd.cpp
@@ -401,6 +401,7 @@ public:
ClientCursorPin pinnedCursor = collection->getCursorManager()->registerCursor(
{std::move(exec),
nss,
+ AuthorizationSession::get(opCtx->getClient())->getAuthenticatedUserNames(),
opCtx->recoveryUnit()->isReadingFromMajorityCommittedSnapshot(),
cmdObj});
cursorId = pinnedCursor.getCursor()->cursorid();
diff --git a/src/mongo/db/commands/getmore_cmd.cpp b/src/mongo/db/commands/getmore_cmd.cpp
index 448177a8068..72e3ec5db34 100644
--- a/src/mongo/db/commands/getmore_cmd.cpp
+++ b/src/mongo/db/commands/getmore_cmd.cpp
@@ -232,6 +232,18 @@ public:
}
}
+ // A user can only call getMore on their own cursor. If there were multiple users
+ // authenticated when the cursor was created, then at least one of them must be
+ // authenticated in order to run getMore on the cursor.
+ if (!AuthorizationSession::get(opCtx->getClient())
+ ->isCoauthorizedWith(cursor->getAuthenticatedUsers())) {
+ return appendCommandStatus(
+ result,
+ Status(ErrorCodes::Unauthorized,
+ str::stream() << "cursor id " << request.cursorid
+ << " was not created by the authenticated user"));
+ }
+
if (request.nss != cursor->nss()) {
return appendCommandStatus(
result,
diff --git a/src/mongo/db/commands/list_collections.cpp b/src/mongo/db/commands/list_collections.cpp
index a636ed51d17..a85c0d6abbe 100644
--- a/src/mongo/db/commands/list_collections.cpp
+++ b/src/mongo/db/commands/list_collections.cpp
@@ -330,6 +330,7 @@ public:
auto pinnedCursor = CursorManager::getGlobalCursorManager()->registerCursor(
{std::move(exec),
cursorNss,
+ AuthorizationSession::get(opCtx->getClient())->getAuthenticatedUserNames(),
opCtx->recoveryUnit()->isReadingFromMajorityCommittedSnapshot(),
jsobj});
cursorId = pinnedCursor.getCursor()->cursorid();
diff --git a/src/mongo/db/commands/list_indexes.cpp b/src/mongo/db/commands/list_indexes.cpp
index 7ea9b23ed0d..7ef1f599828 100644
--- a/src/mongo/db/commands/list_indexes.cpp
+++ b/src/mongo/db/commands/list_indexes.cpp
@@ -230,6 +230,7 @@ public:
auto pinnedCursor = CursorManager::getGlobalCursorManager()->registerCursor(
{std::move(exec),
cursorNss,
+ AuthorizationSession::get(opCtx->getClient())->getAuthenticatedUserNames(),
opCtx->recoveryUnit()->isReadingFromMajorityCommittedSnapshot(),
cmdObj});
cursorId = pinnedCursor.getCursor()->cursorid();
diff --git a/src/mongo/db/commands/parallel_collection_scan.cpp b/src/mongo/db/commands/parallel_collection_scan.cpp
index c46cb6d8c5b..c5166658105 100644
--- a/src/mongo/db/commands/parallel_collection_scan.cpp
+++ b/src/mongo/db/commands/parallel_collection_scan.cpp
@@ -152,6 +152,7 @@ public:
auto pinnedCursor = collection->getCursorManager()->registerCursor(
{std::move(exec),
ns,
+ AuthorizationSession::get(opCtx->getClient())->getAuthenticatedUserNames(),
opCtx->recoveryUnit()->isReadingFromMajorityCommittedSnapshot(),
cmdObj});
pinnedCursor.getCursor()->setLeftoverMaxTimeMicros(
diff --git a/src/mongo/db/commands/repair_cursor.cpp b/src/mongo/db/commands/repair_cursor.cpp
index 69ded443ec6..1390b0281b3 100644
--- a/src/mongo/db/commands/repair_cursor.cpp
+++ b/src/mongo/db/commands/repair_cursor.cpp
@@ -109,6 +109,7 @@ public:
auto pinnedCursor = collection->getCursorManager()->registerCursor(
{std::move(exec),
ns,
+ AuthorizationSession::get(opCtx->getClient())->getAuthenticatedUserNames(),
opCtx->recoveryUnit()->isReadingFromMajorityCommittedSnapshot(),
cmdObj});
diff --git a/src/mongo/db/commands/run_aggregate.cpp b/src/mongo/db/commands/run_aggregate.cpp
index f0ef162e9af..c7a7e44d913 100644
--- a/src/mongo/db/commands/run_aggregate.cpp
+++ b/src/mongo/db/commands/run_aggregate.cpp
@@ -35,6 +35,7 @@
#include <boost/optional.hpp>
#include <vector>
+#include "mongo/db/auth/authorization_session.h"
#include "mongo/db/catalog/database.h"
#include "mongo/db/curop.h"
#include "mongo/db/db_raii.h"
@@ -418,6 +419,7 @@ Status runAggregate(OperationContext* opCtx,
auto pin = CursorManager::getGlobalCursorManager()->registerCursor(
{std::move(exec),
origNss,
+ AuthorizationSession::get(opCtx->getClient())->getAuthenticatedUserNames(),
opCtx->recoveryUnit()->isReadingFromMajorityCommittedSnapshot(),
cmdObj});
ScopeGuard cursorFreer = MakeGuard(&ClientCursorPin::deleteUnderlying, &pin);
diff --git a/src/mongo/db/query/find.cpp b/src/mongo/db/query/find.cpp
index 62a08389e3f..903153d0824 100644
--- a/src/mongo/db/query/find.cpp
+++ b/src/mongo/db/query/find.cpp
@@ -33,6 +33,7 @@
#include "mongo/db/query/find.h"
#include "mongo/client/dbclientinterface.h"
+#include "mongo/db/auth/authorization_session.h"
#include "mongo/db/catalog/collection.h"
#include "mongo/db/catalog/database_holder.h"
#include "mongo/db/client.h"
@@ -324,6 +325,16 @@ Message getMore(OperationContext* opCtx,
<< " belongs to namespace "
<< cc->nss().ns(),
nss == cc->nss());
+
+ // A user can only call getMore on their own cursor. If there were multiple users
+ // authenticated when the cursor was created, then at least one of them must be
+ // authenticated in order to run getMore on the cursor.
+ uassert(ErrorCodes::Unauthorized,
+ str::stream() << "cursor id " << cursorid
+ << " was not created by the authenticated user",
+ AuthorizationSession::get(opCtx->getClient())
+ ->isCoauthorizedWith(cc->getAuthenticatedUsers()));
+
*isCursorAuthorized = true;
if (cc->isReadCommitted())
@@ -653,6 +664,7 @@ std::string runQuery(OperationContext* opCtx,
ClientCursorPin pinnedCursor = collection->getCursorManager()->registerCursor(
{std::move(exec),
nss,
+ AuthorizationSession::get(opCtx->getClient())->getAuthenticatedUserNames(),
opCtx->recoveryUnit()->isReadingFromMajorityCommittedSnapshot(),
upconvertQueryEntry(q.query, qr.nss(), q.ntoreturn, q.ntoskip)});
ccId = pinnedCursor.getCursor()->cursorid();