diff options
author | Spencer Jackson <spencer.jackson@mongodb.com> | 2017-09-27 16:06:45 -0400 |
---|---|---|
committer | Spencer Jackson <spencer.jackson@mongodb.com> | 2017-09-29 11:54:59 -0400 |
commit | fb4b207cddb61a9b1c5e010be693ba41156e7f58 (patch) | |
tree | a9a0fc28e23976c2184fe69c0b35252cd4f73410 /src | |
parent | 2fc2b3fbde6adbeb43a7b8df70e26000cedf01d5 (diff) | |
download | mongo-fb4b207cddb61a9b1c5e010be693ba41156e7f58.tar.gz |
SERVER-31248: Use of UUID in command requires privilege
Diffstat (limited to 'src')
-rw-r--r-- | src/mongo/db/auth/action_types.txt | 1 | ||||
-rw-r--r-- | src/mongo/db/auth/authorization_session.cpp | 15 | ||||
-rw-r--r-- | src/mongo/db/auth/authorization_session.h | 4 | ||||
-rw-r--r-- | src/mongo/db/auth/authorization_session_test.cpp | 29 | ||||
-rw-r--r-- | src/mongo/db/auth/role_graph_builtin_roles.cpp | 3 | ||||
-rw-r--r-- | src/mongo/db/commands/count_cmd.cpp | 22 | ||||
-rw-r--r-- | src/mongo/db/commands/find_cmd.cpp | 15 | ||||
-rw-r--r-- | src/mongo/db/commands/list_indexes.cpp | 4 | ||||
-rw-r--r-- | src/mongo/db/commands/parallel_collection_scan.cpp | 24 |
9 files changed, 96 insertions, 21 deletions
diff --git a/src/mongo/db/auth/action_types.txt b/src/mongo/db/auth/action_types.txt index 1f57ba6084b..88d777a4950 100644 --- a/src/mongo/db/auth/action_types.txt +++ b/src/mongo/db/auth/action_types.txt @@ -106,6 +106,7 @@ "top", "touch", "unlock", +"useUUID", "update", "updateRole", # Not used for permissions checks, but to id the event in logs. "updateUser", # Not used for permissions checks, but to id the event in logs. diff --git a/src/mongo/db/auth/authorization_session.cpp b/src/mongo/db/auth/authorization_session.cpp index 023d5fc02a7..15ee3480a88 100644 --- a/src/mongo/db/auth/authorization_session.cpp +++ b/src/mongo/db/auth/authorization_session.cpp @@ -601,6 +601,21 @@ Status AuthorizationSession::checkAuthorizedToRevokePrivilege(const Privilege& p return Status::OK(); } +bool AuthorizationSession::isAuthorizedToParseNamespaceElement(const BSONElement& element) { + const bool isUUID = element.type() == BinData && element.binDataType() == BinDataType::newUUID; + + uassert(ErrorCodes::InvalidNamespace, + "Failed to parse namespace element", + element.type() == String || isUUID); + + if (isUUID) { + return isAuthorizedForActionsOnResource(ResourcePattern::forClusterResource(), + ActionType::useUUID); + } + + return true; +} + bool AuthorizationSession::isAuthorizedToCreateRole( const struct auth::CreateOrUpdateRoleArgs& args) { // A user is allowed to create a role under either of two conditions. diff --git a/src/mongo/db/auth/authorization_session.h b/src/mongo/db/auth/authorization_session.h index 1f41180df18..a1e159c80de 100644 --- a/src/mongo/db/auth/authorization_session.h +++ b/src/mongo/db/auth/authorization_session.h @@ -227,6 +227,10 @@ public: // Checks if this connection is using the localhost bypass bool isUsingLocalhostBypass(); + // Checks if this connection has the privileges necessary to parse a namespace from a + // given BSONElement. + bool isAuthorizedToParseNamespaceElement(const BSONElement& elem); + // Checks if this connection has the privileges necessary to create a new role bool isAuthorizedToCreateRole(const auth::CreateOrUpdateRoleArgs& args); diff --git a/src/mongo/db/auth/authorization_session_test.cpp b/src/mongo/db/auth/authorization_session_test.cpp index 308156c31ee..0ec66e94598 100644 --- a/src/mongo/db/auth/authorization_session_test.cpp +++ b/src/mongo/db/auth/authorization_session_test.cpp @@ -1278,5 +1278,34 @@ TEST_F(AuthorizationSessionTest, CanListCollectionsWithListCollectionsPrivilege) ASSERT_TRUE(authzSession->isAuthorizedToListCollections(testQuxNss.db())); } +TEST_F(AuthorizationSessionTest, CanUseUUIDNamespacesWithPrivilege) { + BSONObj stringObj = BSON("a" + << "string"); + BSONObj uuidObj = BSON("a" << UUID::gen()); + BSONObj invalidObj = BSON("a" << 12); + + // Strings require no privileges + ASSERT_TRUE(authzSession->isAuthorizedToParseNamespaceElement(stringObj.firstElement())); + + // UUIDs cannot be parsed with default privileges + ASSERT_FALSE(authzSession->isAuthorizedToParseNamespaceElement(uuidObj.firstElement())); + + // Element must be either a string, or a UUID + ASSERT_THROWS_CODE(authzSession->isAuthorizedToParseNamespaceElement(invalidObj.firstElement()), + AssertionException, + ErrorCodes::InvalidNamespace); + + // The useUUID privilege allows UUIDs to be parsed + authzSession->assumePrivilegesForDB( + Privilege(ResourcePattern::forClusterResource(), {ActionType::useUUID})); + + ASSERT_TRUE(authzSession->isAuthorizedToParseNamespaceElement(stringObj.firstElement())); + ASSERT_TRUE(authzSession->isAuthorizedToParseNamespaceElement(uuidObj.firstElement())); + ASSERT_THROWS_CODE(authzSession->isAuthorizedToParseNamespaceElement(invalidObj.firstElement()), + AssertionException, + ErrorCodes::InvalidNamespace); +} + + } // namespace } // namespace mongo diff --git a/src/mongo/db/auth/role_graph_builtin_roles.cpp b/src/mongo/db/auth/role_graph_builtin_roles.cpp index 31e8e1234ec..94b14014549 100644 --- a/src/mongo/db/auth/role_graph_builtin_roles.cpp +++ b/src/mongo/db/auth/role_graph_builtin_roles.cpp @@ -194,6 +194,7 @@ MONGO_INITIALIZER(AuthorizationBuiltinRoles)(InitializerContext* context) { << ActionType::replSetGetStatus // clusterManager gets this also << ActionType::serverStatus << ActionType::top + << ActionType::useUUID << ActionType::inprog << ActionType::shardingState; @@ -499,7 +500,7 @@ void addQueryableBackupPrivileges(PrivilegeVector* privileges) { ActionSet clusterActions; clusterActions << ActionType::getParameter // To check authSchemaVersion - << ActionType::listDatabases; + << ActionType::listDatabases << ActionType::useUUID; Privilege::addPrivilegeToPrivilegeVector( privileges, Privilege(ResourcePattern::forClusterResource(), clusterActions)); diff --git a/src/mongo/db/commands/count_cmd.cpp b/src/mongo/db/commands/count_cmd.cpp index 9ce6c54675e..d70c9ce6c80 100644 --- a/src/mongo/db/commands/count_cmd.cpp +++ b/src/mongo/db/commands/count_cmd.cpp @@ -30,6 +30,7 @@ #include "mongo/platform/basic.h" +#include "mongo/db/auth/authorization_session.h" #include "mongo/db/client.h" #include "mongo/db/commands.h" #include "mongo/db/commands/run_aggregate.h" @@ -91,12 +92,21 @@ public: help << "count objects in collection"; } - virtual void addRequiredPrivileges(const std::string& dbname, - const BSONObj& cmdObj, - std::vector<Privilege>* out) { - ActionSet actions; - actions.addAction(ActionType::find); - out->push_back(Privilege(parseResourcePattern(dbname, cmdObj), actions)); + Status checkAuthForOperation(OperationContext* opCtx, + const std::string& dbname, + const BSONObj& cmdObj) override { + AuthorizationSession* authSession = AuthorizationSession::get(opCtx->getClient()); + + if (!authSession->isAuthorizedToParseNamespaceElement(cmdObj.firstElement())) { + return Status(ErrorCodes::Unauthorized, "Unauthorized"); + } + + const NamespaceString nss(parseNsOrUUID(opCtx, dbname, cmdObj)); + if (!authSession->isAuthorizedForActionsOnNamespace(nss, ActionType::find)) { + return Status(ErrorCodes::Unauthorized, "Unauthorized"); + } + + return Status::OK(); } virtual Status explain(OperationContext* opCtx, diff --git a/src/mongo/db/commands/find_cmd.cpp b/src/mongo/db/commands/find_cmd.cpp index cbe0ce20d55..d3d9aafc096 100644 --- a/src/mongo/db/commands/find_cmd.cpp +++ b/src/mongo/db/commands/find_cmd.cpp @@ -123,12 +123,17 @@ public: return false; } - Status checkAuthForCommand(Client* client, - const std::string& dbname, - const BSONObj& cmdObj) override { - const NamespaceString nss(parseNs(dbname, cmdObj)); + Status checkAuthForOperation(OperationContext* opCtx, + const std::string& dbname, + const BSONObj& cmdObj) override { + AuthorizationSession* authSession = AuthorizationSession::get(opCtx->getClient()); + + if (!authSession->isAuthorizedToParseNamespaceElement(cmdObj.firstElement())) { + return Status(ErrorCodes::Unauthorized, "Unauthorized"); + } + const NamespaceString nss(parseNsOrUUID(opCtx, dbname, cmdObj)); auto hasTerm = cmdObj.hasField(kTermField); - return AuthorizationSession::get(client)->checkAuthForFind(nss, hasTerm); + return authSession->checkAuthForFind(nss, hasTerm); } Status explain(OperationContext* opCtx, diff --git a/src/mongo/db/commands/list_indexes.cpp b/src/mongo/db/commands/list_indexes.cpp index 75d79099c39..d4e77888247 100644 --- a/src/mongo/db/commands/list_indexes.cpp +++ b/src/mongo/db/commands/list_indexes.cpp @@ -98,6 +98,10 @@ public: const BSONObj& cmdObj) { AuthorizationSession* authzSession = AuthorizationSession::get(client); + if (!authzSession->isAuthorizedToParseNamespaceElement(cmdObj.firstElement())) { + return Status(ErrorCodes::Unauthorized, "Unauthorized"); + } + // Check for the listIndexes ActionType on the database, or find on system.indexes for pre // 3.0 systems. const NamespaceString ns(parseNsOrUUID(client->getOperationContext(), dbname, cmdObj)); diff --git a/src/mongo/db/commands/parallel_collection_scan.cpp b/src/mongo/db/commands/parallel_collection_scan.cpp index 6b84996369c..533daa6ab94 100644 --- a/src/mongo/db/commands/parallel_collection_scan.cpp +++ b/src/mongo/db/commands/parallel_collection_scan.cpp @@ -74,15 +74,21 @@ public: return ReadWriteType::kCommand; } - virtual Status checkAuthForCommand(Client* client, - const std::string& dbname, - const BSONObj& cmdObj) { - ActionSet actions; - actions.addAction(ActionType::find); - Privilege p(parseResourcePattern(dbname, cmdObj), actions); - if (AuthorizationSession::get(client)->isAuthorizedForPrivilege(p)) - return Status::OK(); - return Status(ErrorCodes::Unauthorized, "Unauthorized"); + Status checkAuthForOperation(OperationContext* opCtx, + const std::string& dbname, + const BSONObj& cmdObj) override { + AuthorizationSession* authSession = AuthorizationSession::get(opCtx->getClient()); + + if (!authSession->isAuthorizedToParseNamespaceElement(cmdObj.firstElement())) { + return Status(ErrorCodes::Unauthorized, "Unauthorized"); + } + + const NamespaceString ns(parseNsOrUUID(opCtx, dbname, cmdObj)); + if (!authSession->isAuthorizedForActionsOnNamespace(ns, ActionType::find)) { + return Status(ErrorCodes::Unauthorized, "Unauthorized"); + } + + return Status::OK(); } virtual bool run(OperationContext* opCtx, |