summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSpencer Jackson <spencer.jackson@mongodb.com>2016-04-21 18:49:20 -0400
committerSpencer Jackson <spencer.jackson@mongodb.com>2016-04-22 13:52:12 -0400
commit3e6a04652806e752c0c004fa72bc95025c3a9d54 (patch)
tree8426e72ec63c97cee130a71568ffabfadaaaf4a3
parentdd7427d7acf2aac9fe7b0caa7e892fe8a856c186 (diff)
downloadmongo-3e6a04652806e752c0c004fa72bc95025c3a9d54.tar.gz
SERVER-23503 Expand localhost exception to include role creation
-rw-r--r--jstests/auth/localhostAuthBypass.js24
-rw-r--r--src/mongo/db/auth/authorization_session.cpp24
-rw-r--r--src/mongo/db/auth/authorization_session.h8
-rw-r--r--src/mongo/db/auth/authz_manager_external_state_local.cpp10
-rw-r--r--src/mongo/db/auth/authz_manager_external_state_s.cpp23
-rw-r--r--src/mongo/db/commands/user_management_commands_common.cpp3
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());