diff options
Diffstat (limited to 'src/mongo/db')
-rw-r--r-- | src/mongo/db/auth/authorization_session.cpp | 22 | ||||
-rw-r--r-- | src/mongo/db/auth/authorization_session.h | 5 | ||||
-rw-r--r-- | src/mongo/db/auth/authorization_session_test.cpp | 156 | ||||
-rw-r--r-- | src/mongo/db/clientcursor.cpp | 1 | ||||
-rw-r--r-- | src/mongo/db/clientcursor.h | 16 | ||||
-rw-r--r-- | src/mongo/db/commands/find_cmd.cpp | 1 | ||||
-rw-r--r-- | src/mongo/db/commands/getmore_cmd.cpp | 12 | ||||
-rw-r--r-- | src/mongo/db/commands/list_collections.cpp | 1 | ||||
-rw-r--r-- | src/mongo/db/commands/list_indexes.cpp | 1 | ||||
-rw-r--r-- | src/mongo/db/commands/parallel_collection_scan.cpp | 1 | ||||
-rw-r--r-- | src/mongo/db/commands/repair_cursor.cpp | 1 | ||||
-rw-r--r-- | src/mongo/db/commands/run_aggregate.cpp | 2 | ||||
-rw-r--r-- | src/mongo/db/query/find.cpp | 12 |
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(); |