/** * Copyright (C) 2015 MongoDB Inc. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . * * As a special exception, the copyright holders give permission to link the * code of portions of this program with the OpenSSL library under certain * conditions as described in each individual source file and distribute * linked combinations including the program with the OpenSSL library. You * must comply with the GNU Affero General Public License in all respects for * all of the code used other than as permitted herein. If you modify file(s) * with this exception, you may extend this exception to your version of the * file(s), but you are not obligated to do so. If you do not wish to do so, * delete this exception statement from your version. If you delete this * exception statement from all source files in the program, then also delete * it in the license file. */ #define MONGO_LOG_DEFAULT_COMPONENT ::mongo::logger::LogComponent::kAccessControl #include "mongo/platform/basic.h" #include "mongo/db/commands/user_management_commands.h" #include #include #include "mongo/base/status.h" #include "mongo/bson/mutable/algorithm.h" #include "mongo/config.h" #include "mongo/db/auth/action_set.h" #include "mongo/db/auth/action_type.h" #include "mongo/db/auth/authorization_session.h" #include "mongo/db/auth/resource_pattern.h" #include "mongo/db/auth/user.h" #include "mongo/db/auth/user_management_commands_parser.h" #include "mongo/db/jsobj.h" #include "mongo/util/log.h" #include "mongo/util/mongoutils/str.h" #include "mongo/util/sequence_util.h" namespace mongo { namespace auth { void redactPasswordData(mutablebson::Element parent) { namespace mmb = mutablebson; const auto pwdFieldName = "pwd"_sd; for (mmb::Element pwdElement = mmb::findFirstChildNamed(parent, pwdFieldName); pwdElement.ok(); pwdElement = mmb::findElementNamed(pwdElement.rightSibling(), pwdFieldName)) { pwdElement.setValueString("xxx").transitional_ignore(); } } Status checkAuthorizedToGrantRoles(AuthorizationSession* authzSession, const std::vector& roles) { for (size_t i = 0; i < roles.size(); ++i) { if (!authzSession->isAuthorizedToGrantRole(roles[i])) { return Status(ErrorCodes::Unauthorized, str::stream() << "Not authorized to grant role: " << roles[i].getFullName()); } } return Status::OK(); } Status checkAuthorizedToGrantPrivileges(AuthorizationSession* authzSession, const PrivilegeVector& privileges) { for (PrivilegeVector::const_iterator it = privileges.begin(); it != privileges.end(); ++it) { Status status = authzSession->checkAuthorizedToGrantPrivilege(*it); if (!status.isOK()) { return status; } } return Status::OK(); } Status checkAuthorizedToRevokeRoles(AuthorizationSession* authzSession, const std::vector& roles) { for (size_t i = 0; i < roles.size(); ++i) { if (!authzSession->isAuthorizedToRevokeRole(roles[i])) { return Status(ErrorCodes::Unauthorized, str::stream() << "Not authorized to revoke role: " << roles[i].getFullName()); } } return Status::OK(); } Status checkAuthorizedToRevokePrivileges(AuthorizationSession* authzSession, const PrivilegeVector& privileges) { for (PrivilegeVector::const_iterator it = privileges.begin(); it != privileges.end(); ++it) { Status status = authzSession->checkAuthorizedToRevokePrivilege(*it); if (!status.isOK()) { return status; } } return Status::OK(); } Status checkAuthorizedToSetRestrictions(AuthorizationSession* authzSession, bool hasAuthRestriction, StringData dbname) { if (hasAuthRestriction) { if (!authzSession->isAuthorizedForActionsOnResource( ResourcePattern::forDatabaseName(dbname), ActionType::setAuthenticationRestriction)) { return Status(ErrorCodes::Unauthorized, "Unauthorized"); } } return Status::OK(); } Status checkAuthForCreateUserCommand(Client* client, const std::string& dbname, const BSONObj& cmdObj) { AuthorizationSession* authzSession = AuthorizationSession::get(client); auth::CreateOrUpdateUserArgs args; Status status = auth::parseCreateOrUpdateUserCommands(cmdObj, "createUser", dbname, &args); if (!status.isOK()) { return status; } if (!authzSession->isAuthorizedForActionsOnResource( ResourcePattern::forDatabaseName(args.userName.getDB()), ActionType::createUser)) { return Status(ErrorCodes::Unauthorized, str::stream() << "Not authorized to create users on db: " << args.userName.getDB()); } status = checkAuthorizedToGrantRoles(authzSession, args.roles); if (!status.isOK()) { return status; } status = checkAuthorizedToSetRestrictions( authzSession, static_cast(args.authenticationRestrictions), args.userName.getDB()); if (!status.isOK()) { return status; } return Status::OK(); } Status checkAuthForUpdateUserCommand(Client* client, const std::string& dbname, const BSONObj& cmdObj) { AuthorizationSession* authzSession = AuthorizationSession::get(client); auth::CreateOrUpdateUserArgs args; Status status = auth::parseCreateOrUpdateUserCommands(cmdObj, "updateUser", dbname, &args); if (!status.isOK()) { return status; } if (args.hasPassword) { if (!authzSession->isAuthorizedToChangeOwnPasswordAsUser(args.userName) && !authzSession->isAuthorizedForActionsOnResource( ResourcePattern::forDatabaseName(args.userName.getDB()), ActionType::changePassword)) { return Status(ErrorCodes::Unauthorized, str::stream() << "Not authorized to change password of user: " << args.userName.getFullName()); } } if (args.hasCustomData) { if (!authzSession->isAuthorizedToChangeOwnCustomDataAsUser(args.userName) && !authzSession->isAuthorizedForActionsOnResource( ResourcePattern::forDatabaseName(args.userName.getDB()), ActionType::changeCustomData)) { return Status(ErrorCodes::Unauthorized, str::stream() << "Not authorized to change customData of user: " << args.userName.getFullName()); } } if (args.hasRoles) { // You don't know what roles you might be revoking, so require the ability to // revoke any role in the system. if (!authzSession->isAuthorizedForActionsOnResource(ResourcePattern::forAnyNormalResource(), ActionType::revokeRole)) { return Status(ErrorCodes::Unauthorized, "In order to use updateUser to set roles array, must be " "authorized to revoke any role in the system"); } status = checkAuthorizedToGrantRoles(authzSession, args.roles); if (!status.isOK()) { return status; } } status = checkAuthorizedToSetRestrictions( authzSession, static_cast(args.authenticationRestrictions), args.userName.getDB()); if (!status.isOK()) { return status; } return Status::OK(); } Status checkAuthForGrantRolesToUserCommand(Client* client, const std::string& dbname, const BSONObj& cmdObj) { AuthorizationSession* authzSession = AuthorizationSession::get(client); std::vector roles; std::string unusedUserNameString; Status status = auth::parseRolePossessionManipulationCommands( cmdObj, "grantRolesToUser", dbname, &unusedUserNameString, &roles); if (!status.isOK()) { return status; } return checkAuthorizedToGrantRoles(authzSession, roles); } Status checkAuthForCreateRoleCommand(Client* client, const std::string& dbname, const BSONObj& cmdObj) { AuthorizationSession* authzSession = AuthorizationSession::get(client); auth::CreateOrUpdateRoleArgs args; Status status = auth::parseCreateOrUpdateRoleCommands(cmdObj, "createRole", dbname, &args); if (!status.isOK()) { return status; } if (!authzSession->isAuthorizedToCreateRole(args)) { return Status(ErrorCodes::Unauthorized, str::stream() << "Not authorized to create roles on db: " << args.roleName.getDB()); } status = checkAuthorizedToGrantRoles(authzSession, args.roles); if (!status.isOK()) { return status; } status = checkAuthorizedToGrantPrivileges(authzSession, args.privileges); if (!status.isOK()) { return status; } status = checkAuthorizedToSetRestrictions( authzSession, static_cast(args.authenticationRestrictions), args.roleName.getDB()); if (!status.isOK()) { return status; } return Status::OK(); } Status checkAuthForUpdateRoleCommand(Client* client, const std::string& dbname, const BSONObj& cmdObj) { AuthorizationSession* authzSession = AuthorizationSession::get(client); auth::CreateOrUpdateRoleArgs args; Status status = auth::parseCreateOrUpdateRoleCommands(cmdObj, "updateRole", dbname, &args); if (!status.isOK()) { return status; } // You don't know what roles or privileges you might be revoking, so require the ability // to revoke any role (or privilege) in the system. if (!authzSession->isAuthorizedForActionsOnResource(ResourcePattern::forAnyNormalResource(), ActionType::revokeRole)) { return Status(ErrorCodes::Unauthorized, "updateRole command required the ability to revoke any role in the " "system"); } status = checkAuthorizedToGrantRoles(authzSession, args.roles); if (!status.isOK()) { return status; } status = checkAuthorizedToGrantPrivileges(authzSession, args.privileges); if (!status.isOK()) { return status; } status = checkAuthorizedToSetRestrictions( authzSession, static_cast(args.authenticationRestrictions), args.roleName.getDB()); if (!status.isOK()) { return status; } return Status::OK(); } Status checkAuthForGrantRolesToRoleCommand(Client* client, const std::string& dbname, const BSONObj& cmdObj) { AuthorizationSession* authzSession = AuthorizationSession::get(client); std::vector roles; std::string unusedUserNameString; Status status = auth::parseRolePossessionManipulationCommands( cmdObj, "grantRolesToRole", dbname, &unusedUserNameString, &roles); if (!status.isOK()) { return status; } return checkAuthorizedToGrantRoles(authzSession, roles); } Status checkAuthForGrantPrivilegesToRoleCommand(Client* client, const std::string& dbname, const BSONObj& cmdObj) { AuthorizationSession* authzSession = AuthorizationSession::get(client); PrivilegeVector privileges; RoleName unusedRoleName; Status status = auth::parseAndValidateRolePrivilegeManipulationCommands( cmdObj, "grantPrivilegesToRole", dbname, &unusedRoleName, &privileges); if (!status.isOK()) { return status; } return checkAuthorizedToGrantPrivileges(authzSession, privileges); } Status checkAuthForDropUserCommand(Client* client, const std::string& dbname, const BSONObj& cmdObj) { AuthorizationSession* authzSession = AuthorizationSession::get(client); UserName userName; Status status = auth::parseAndValidateDropUserCommand(cmdObj, dbname, &userName); if (!status.isOK()) { return status; } if (!authzSession->isAuthorizedForActionsOnResource( ResourcePattern::forDatabaseName(userName.getDB()), ActionType::dropUser)) { return Status(ErrorCodes::Unauthorized, str::stream() << "Not authorized to drop users from the " << userName.getDB() << " database"); } return Status::OK(); } Status checkAuthForDropRoleCommand(Client* client, const std::string& dbname, const BSONObj& cmdObj) { AuthorizationSession* authzSession = AuthorizationSession::get(client); RoleName roleName; Status status = auth::parseDropRoleCommand(cmdObj, dbname, &roleName); if (!status.isOK()) { return status; } if (!authzSession->isAuthorizedForActionsOnResource( ResourcePattern::forDatabaseName(roleName.getDB()), ActionType::dropRole)) { return Status(ErrorCodes::Unauthorized, str::stream() << "Not authorized to drop roles from the " << roleName.getDB() << " database"); } return Status::OK(); } Status checkAuthForDropAllUsersFromDatabaseCommand(Client* client, const std::string& dbname) { AuthorizationSession* authzSession = AuthorizationSession::get(client); if (!authzSession->isAuthorizedForActionsOnResource(ResourcePattern::forDatabaseName(dbname), ActionType::dropUser)) { return Status(ErrorCodes::Unauthorized, str::stream() << "Not authorized to drop users from the " << dbname << " database"); } return Status::OK(); } Status checkAuthForRevokeRolesFromUserCommand(Client* client, const std::string& dbname, const BSONObj& cmdObj) { AuthorizationSession* authzSession = AuthorizationSession::get(client); std::vector roles; std::string unusedUserNameString; Status status = auth::parseRolePossessionManipulationCommands( cmdObj, "revokeRolesFromUser", dbname, &unusedUserNameString, &roles); if (!status.isOK()) { return status; } return checkAuthorizedToRevokeRoles(authzSession, roles); } Status checkAuthForRevokeRolesFromRoleCommand(Client* client, const std::string& dbname, const BSONObj& cmdObj) { AuthorizationSession* authzSession = AuthorizationSession::get(client); std::vector roles; std::string unusedUserNameString; Status status = auth::parseRolePossessionManipulationCommands( cmdObj, "revokeRolesFromRole", dbname, &unusedUserNameString, &roles); if (!status.isOK()) { return status; } return checkAuthorizedToRevokeRoles(authzSession, roles); } Status checkAuthForUsersInfoCommand(Client* client, const std::string& dbname, const BSONObj& cmdObj) { AuthorizationSession* authzSession = AuthorizationSession::get(client); auth::UsersInfoArgs args; Status status = auth::parseUsersInfoCommand(cmdObj, dbname, &args); if (!status.isOK()) { return status; } if (args.target == auth::UsersInfoArgs::Target::kDB) { if (!authzSession->isAuthorizedForActionsOnResource( ResourcePattern::forDatabaseName(dbname), ActionType::viewUser)) { return Status(ErrorCodes::Unauthorized, str::stream() << "Not authorized to view users from the " << dbname << " database"); } } else if (args.target == auth::UsersInfoArgs::Target::kGlobal) { if (!authzSession->isAuthorizedForActionsOnResource(ResourcePattern::forClusterResource(), ActionType::viewUser)) { return Status(ErrorCodes::Unauthorized, str::stream() << "Not authorized to view users from all" << " databases"); } } else { for (size_t i = 0; i < args.userNames.size(); ++i) { if (authzSession->lookupUser(args.userNames[i])) { continue; // Can always view users you are logged in as } if (!authzSession->isAuthorizedForActionsOnResource( ResourcePattern::forDatabaseName(args.userNames[i].getDB()), ActionType::viewUser)) { return Status(ErrorCodes::Unauthorized, str::stream() << "Not authorized to view users from the " << dbname << " database"); } } } return Status::OK(); } Status checkAuthForRevokePrivilegesFromRoleCommand(Client* client, const std::string& dbname, const BSONObj& cmdObj) { AuthorizationSession* authzSession = AuthorizationSession::get(client); PrivilegeVector privileges; RoleName unusedRoleName; Status status = auth::parseAndValidateRolePrivilegeManipulationCommands( cmdObj, "revokePrivilegesFromRole", dbname, &unusedRoleName, &privileges); if (!status.isOK()) { return status; } return checkAuthorizedToRevokePrivileges(authzSession, privileges); } Status checkAuthForDropAllRolesFromDatabaseCommand(Client* client, const std::string& dbname) { AuthorizationSession* authzSession = AuthorizationSession::get(client); if (!authzSession->isAuthorizedForActionsOnResource(ResourcePattern::forDatabaseName(dbname), ActionType::dropRole)) { return Status(ErrorCodes::Unauthorized, str::stream() << "Not authorized to drop roles from the " << dbname << " database"); } return Status::OK(); } Status checkAuthForRolesInfoCommand(Client* client, const std::string& dbname, const BSONObj& cmdObj) { AuthorizationSession* authzSession = AuthorizationSession::get(client); auth::RolesInfoArgs args; Status status = auth::parseRolesInfoCommand(cmdObj, dbname, &args); if (!status.isOK()) { return status; } if (args.allForDB) { if (!authzSession->isAuthorizedForActionsOnResource( ResourcePattern::forDatabaseName(dbname), ActionType::viewRole)) { return Status(ErrorCodes::Unauthorized, str::stream() << "Not authorized to view roles from the " << dbname << " database"); } } else { for (size_t i = 0; i < args.roleNames.size(); ++i) { if (authzSession->isAuthenticatedAsUserWithRole(args.roleNames[i])) { continue; // Can always see roles that you are a member of } if (!authzSession->isAuthorizedForActionsOnResource( ResourcePattern::forDatabaseName(args.roleNames[i].getDB()), ActionType::viewRole)) { return Status(ErrorCodes::Unauthorized, str::stream() << "Not authorized to view roles from the " << args.roleNames[i].getDB() << " database"); } } } return Status::OK(); } Status checkAuthForInvalidateUserCacheCommand(Client* client) { AuthorizationSession* authzSession = AuthorizationSession::get(client); if (!authzSession->isAuthorizedForActionsOnResource(ResourcePattern::forClusterResource(), ActionType::invalidateUserCache)) { return Status(ErrorCodes::Unauthorized, "Not authorized to invalidate user cache"); } return Status::OK(); } Status checkAuthForGetUserCacheGenerationCommand(Client* client) { AuthorizationSession* authzSession = AuthorizationSession::get(client); if (!authzSession->isAuthorizedForActionsOnResource(ResourcePattern::forClusterResource(), ActionType::internal)) { return Status(ErrorCodes::Unauthorized, "Not authorized to get cache generation"); } return Status::OK(); } Status checkAuthForMergeAuthzCollectionsCommand(Client* client, const BSONObj& cmdObj) { auth::MergeAuthzCollectionsArgs args; Status status = auth::parseMergeAuthzCollectionsCommand(cmdObj, &args); if (!status.isOK()) { return status; } AuthorizationSession* authzSession = AuthorizationSession::get(client); ActionSet actions; actions.addAction(ActionType::createUser); actions.addAction(ActionType::createRole); actions.addAction(ActionType::grantRole); actions.addAction(ActionType::revokeRole); if (args.drop) { actions.addAction(ActionType::dropUser); actions.addAction(ActionType::dropRole); } if (!authzSession->isAuthorizedForActionsOnResource(ResourcePattern::forAnyNormalResource(), actions)) { return Status(ErrorCodes::Unauthorized, "Not authorized to update user/role data using _mergeAuthzCollections" " command"); } if (!args.usersCollName.empty() && !authzSession->isAuthorizedForActionsOnResource( ResourcePattern::forExactNamespace(NamespaceString(args.usersCollName)), ActionType::find)) { return Status(ErrorCodes::Unauthorized, str::stream() << "Not authorized to read " << args.usersCollName); } if (!args.rolesCollName.empty() && !authzSession->isAuthorizedForActionsOnResource( ResourcePattern::forExactNamespace(NamespaceString(args.rolesCollName)), ActionType::find)) { return Status(ErrorCodes::Unauthorized, str::stream() << "Not authorized to read " << args.rolesCollName); } return Status::OK(); } } // namespace auth } // namespace mongo