diff options
author | Spencer Jackson <spencer.jackson@mongodb.com> | 2016-04-21 18:49:20 -0400 |
---|---|---|
committer | Spencer Jackson <spencer.jackson@mongodb.com> | 2016-04-22 13:52:12 -0400 |
commit | 3e6a04652806e752c0c004fa72bc95025c3a9d54 (patch) | |
tree | 8426e72ec63c97cee130a71568ffabfadaaaf4a3 | |
parent | dd7427d7acf2aac9fe7b0caa7e892fe8a856c186 (diff) | |
download | mongo-3e6a04652806e752c0c004fa72bc95025c3a9d54.tar.gz |
SERVER-23503 Expand localhost exception to include role creation
-rw-r--r-- | jstests/auth/localhostAuthBypass.js | 24 | ||||
-rw-r--r-- | src/mongo/db/auth/authorization_session.cpp | 24 | ||||
-rw-r--r-- | src/mongo/db/auth/authorization_session.h | 8 | ||||
-rw-r--r-- | src/mongo/db/auth/authz_manager_external_state_local.cpp | 10 | ||||
-rw-r--r-- | src/mongo/db/auth/authz_manager_external_state_s.cpp | 23 | ||||
-rw-r--r-- | src/mongo/db/commands/user_management_commands_common.cpp | 3 |
6 files changed, 83 insertions, 9 deletions
diff --git a/jstests/auth/localhostAuthBypass.js b/jstests/auth/localhostAuthBypass.js index fdaf4386794..6cb315650ef 100644 --- a/jstests/auth/localhostAuthBypass.js +++ b/jstests/auth/localhostAuthBypass.js @@ -15,6 +15,12 @@ var createUser = function(mongo) { mongo.getDB("admin").createUser({user: username, pwd: password, roles: jsTest.adminUserRoles}); }; +var createRole = function(mongo) { + print("============ adding a role."); + mongo.getDB("admin").createRole( + {role: "roleAdministrator", roles: [{role: "userAdmin", db: "admin"}], privileges: []}); +}; + var assertCannotRunCommands = function(mongo) { print("============ ensuring that commands cannot be run."); @@ -171,7 +177,25 @@ var runNonlocalTest = function(host) { shutdown(conn); }; +// Per SERVER-23503, the existence of roles in the admin database should disable the localhost +// exception. +// Start the server without auth. Create a role. Restart the server with auth. The exception is +// now enabled. +var runRoleTest = function() { + var conn = MongoRunner.runMongod({dbpath: dbpath}); + var mongo = new Mongo("localhost:" + conn.port); + assertCanRunCommands(mongo); + createRole(mongo); + assertCanRunCommands(mongo); + MongoRunner.stopMongod(conn); + conn = MongoRunner.runMongod({auth: '', dbpath: dbpath, restart: true, cleanData: false}); + mongo = new Mongo("localhost:" + conn.port); + assertCannotRunCommands(mongo); +}; + runTest(false); runTest(true); runNonlocalTest(get_ipaddr()); + +runRoleTest(); diff --git a/src/mongo/db/auth/authorization_session.cpp b/src/mongo/db/auth/authorization_session.cpp index 14e3070b057..c2474ac5199 100644 --- a/src/mongo/db/auth/authorization_session.cpp +++ b/src/mongo/db/auth/authorization_session.cpp @@ -42,6 +42,7 @@ #include "mongo/db/auth/authorization_manager.h" #include "mongo/db/auth/privilege.h" #include "mongo/db/auth/security_key.h" +#include "mongo/db/auth/user_management_commands_parser.h" #include "mongo/db/client.h" #include "mongo/db/jsobj.h" #include "mongo/db/namespace_string.h" @@ -368,6 +369,29 @@ Status AuthorizationSession::checkAuthorizedToRevokePrivilege(const Privilege& p return Status::OK(); } +bool AuthorizationSession::isAuthorizedToCreateRole( + const struct auth::CreateOrUpdateRoleArgs& args) { + // A user is allowed to create a role under either of two conditions. + + // The user may create a role if the authorization system says they are allowed to. + if (isAuthorizedForActionsOnResource(ResourcePattern::forDatabaseName(args.roleName.getDB()), + ActionType::createRole)) { + return true; + } + + // The user may create a role if the localhost exception is enabled, and they already own the + // role. This implies they have obtained the role through an external authorization mechanism. + if (_externalState->shouldAllowLocalhost()) { + for (const User* const user : _authenticatedUsers) { + if (user->hasRole(args.roleName)) { + return true; + } + } + } + + return false; +} + bool AuthorizationSession::isAuthorizedToGrantRole(const RoleName& role) { return isAuthorizedForActionsOnResource(ResourcePattern::forDatabaseName(role.getDB()), ActionType::grantRole); diff --git a/src/mongo/db/auth/authorization_session.h b/src/mongo/db/auth/authorization_session.h index e46e46cef3a..4622f4cdb89 100644 --- a/src/mongo/db/auth/authorization_session.h +++ b/src/mongo/db/auth/authorization_session.h @@ -44,6 +44,11 @@ #include "mongo/db/namespace_string.h" namespace mongo { + +namespace auth { + +struct CreateOrUpdateRoleArgs; +} class ClientBasic; /** @@ -177,6 +182,9 @@ public: // from a role. Status checkAuthorizedToRevokePrivilege(const Privilege& privilege); + // Checks if this connection has the privileges necessary to create a new role + bool isAuthorizedToCreateRole(const auth::CreateOrUpdateRoleArgs& args); + // Utility function for isAuthorizedForActionsOnResource( // ResourcePattern::forDatabaseName(role.getDB()), ActionType::grantAnyRole) bool isAuthorizedToGrantRole(const RoleName& role); diff --git a/src/mongo/db/auth/authz_manager_external_state_local.cpp b/src/mongo/db/auth/authz_manager_external_state_local.cpp index 6e8beef70fc..7525464f6bb 100644 --- a/src/mongo/db/auth/authz_manager_external_state_local.cpp +++ b/src/mongo/db/auth/authz_manager_external_state_local.cpp @@ -131,11 +131,17 @@ void addPrivilegeObjectsOrWarningsToArrayElement(mutablebson::Element privileges bool AuthzManagerExternalStateLocal::hasAnyPrivilegeDocuments(OperationContext* txn) { BSONObj userBSONObj; - Status status = + Status statusFindUsers = findOne(txn, AuthorizationManager::usersCollectionNamespace, BSONObj(), &userBSONObj); + // If we were unable to complete the query, // it's best to assume that there _are_ privilege documents. - return status != ErrorCodes::NoMatchingDocument; + if (statusFindUsers != ErrorCodes::NoMatchingDocument) { + return true; + } + Status statusFindRoles = + findOne(txn, AuthorizationManager::rolesCollectionNamespace, BSONObj(), &userBSONObj); + return statusFindRoles != ErrorCodes::NoMatchingDocument; } Status AuthzManagerExternalStateLocal::getUserDescription(OperationContext* txn, diff --git a/src/mongo/db/auth/authz_manager_external_state_s.cpp b/src/mongo/db/auth/authz_manager_external_state_s.cpp index c86e3cf30aa..48800c500c4 100644 --- a/src/mongo/db/auth/authz_manager_external_state_s.cpp +++ b/src/mongo/db/auth/authz_manager_external_state_s.cpp @@ -169,9 +169,9 @@ Status AuthzManagerExternalStateMongos::getRoleDescriptionsForDB(OperationContex bool AuthzManagerExternalStateMongos::hasAnyPrivilegeDocuments(OperationContext* txn) { BSONObj usersInfoCmd = BSON("usersInfo" << 1); - BSONObjBuilder builder; - const bool ok = grid.catalogManager(txn) - ->runUserManagementReadCommand(txn, "admin", usersInfoCmd, &builder); + BSONObjBuilder userBuilder; + bool ok = grid.catalogManager(txn) + ->runUserManagementReadCommand(txn, "admin", usersInfoCmd, &userBuilder); if (!ok) { // If we were unable to complete the query, // it's best to assume that there _are_ privilege documents. This might happen @@ -180,9 +180,22 @@ bool AuthzManagerExternalStateMongos::hasAnyPrivilegeDocuments(OperationContext* return true; } - BSONObj cmdResult = builder.obj(); + BSONObj cmdResult = userBuilder.obj(); std::vector<BSONElement> foundUsers = cmdResult["users"].Array(); - return foundUsers.size() > 0; + if (foundUsers.size() > 0) { + return true; + } + + BSONObj rolesInfoCmd = BSON("rolesInfo" << 1); + BSONObjBuilder roleBuilder; + ok = grid.catalogManager(txn) + ->runUserManagementReadCommand(txn, "admin", rolesInfoCmd, &roleBuilder); + if (!ok) { + return true; + } + cmdResult = roleBuilder.obj(); + std::vector<BSONElement> foundRoles = cmdResult["roles"].Array(); + return foundRoles.size() > 0; } } // namespace mongo diff --git a/src/mongo/db/commands/user_management_commands_common.cpp b/src/mongo/db/commands/user_management_commands_common.cpp index 09445c5aea7..75db995d43b 100644 --- a/src/mongo/db/commands/user_management_commands_common.cpp +++ b/src/mongo/db/commands/user_management_commands_common.cpp @@ -204,8 +204,7 @@ Status checkAuthForCreateRoleCommand(ClientBasic* client, return status; } - if (!authzSession->isAuthorizedForActionsOnResource( - ResourcePattern::forDatabaseName(args.roleName.getDB()), ActionType::createRole)) { + if (!authzSession->isAuthorizedToCreateRole(args)) { return Status(ErrorCodes::Unauthorized, str::stream() << "Not authorized to create roles on db: " << args.roleName.getDB()); |