// ---------------------------------------------------------------------- // File: Access.cc // Author: Andreas-Joachim Peters - CERN // ---------------------------------------------------------------------- /************************************************************************ * EOS - the CERN Disk Storage System * * Copyright (C) 2011 CERN/Switzerland * * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, either version 3 of the License, or * * (at your option) any later version. * * * * 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 General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see .* ************************************************************************/ #include "mgm/Namespace.hh" #include "mgm/Access.hh" #include "mgm/FsView.hh" #include "common/StringConversion.hh" #include EOSMGMNAMESPACE_BEGIN //! singleton set for banned user IDs std::set Access::gBannedUsers; //! singleton set for banned group IDS std::set Access::gBannedGroups; //! singleton set for banned host names std::set Access::gBannedHosts; //! singleton set for banned host names std::set Access::gBannedDomains; //! singleton set for banned host names std::set Access::gBannedTokens; //! singleton set for allowed user IDs std::set Access::gAllowedUsers; //! singleton set for allowed group IDs std::set Access::gAllowedGroups; //! singleton set for allowed host names std::set Access::gAllowedHosts; //! singleton set for allowed host domains std::set Access::gAllowedDomains; //! singleton set for allowed host domains std::set Access::gAllowedTokens; //! singleton map for redirection rules std::map Access::gRedirectionRules; //! singleton map for stall rules std::map Access::gStallRules; //! singleton map for stall comments std::map Access::gStallComment; //! indicates a list of hostname matching std::set Access::gStallHosts; //! indicates a list of hostname matching a std::set Access::gNoStallHosts; //! indicates global stall rule std::atomic Access::gStallGlobal {false}; //! indicates global read stall std::atomic Access::gStallRead {false}; //! indicates global write stall std::atomic Access::gStallWrite {false}; //! indicates a user or group rate stall entry std::atomic Access::gStallUserGroup {false}; //! singleton map for UID based redirection (not used yet) std::map Access::gUserRedirection; //! singleton map for GID based redirection (not used yet) std::map Access::gGroupRedirection; //! global rw mutex protecting all static singletons eos::common::RWMutex Access::gAccessMutex; /*----------------------------------------------------------------------------*/ //! constant used in the configuration store const char* Access::gUserKey = "BanUsers"; //! constant used in the configuration store const char* Access::gGroupKey = "BanGroups"; //! constant used in the configuration store const char* Access::gHostKey = "BanHosts"; //! constant used in the configuration store const char* Access::gDomainKey = "BanDomains"; //! constant used in the configuration store const char* Access::gTokenKey = "BanTokens"; //! constant used in the configuration store const char* Access::gAllowedUserKey = "AllowedUsers"; //! constant used in the configuration store const char* Access::gAllowedGroupKey = "AllowedGroups"; //! constant used in the configuration store const char* Access::gAllowedHostKey = "AllowedHosts"; //! constant used in the configuration store const char* Access::gAllowedDomainKey = "AllowedDomains"; //! constant used in the configuration store const char* Access::gAllowedTokenKey = "AllowedTokens"; //! constant used in the configuration store const char* Access::gStallKey = "Stall"; //! constant used in the configuration store const char* Access::gRedirectionKey = "Redirection"; //! constant used in the configuration store const char* Access::gStallHostsKey = "StallHosts"; //! constant used in the configuration store const char* Access::gNoStallHostsKey = "NoStallHosts"; //------------------------------------------------------------------------------ // Static function to reset all singleton objects defining access rules. //------------------------------------------------------------------------------ void Access::Reset(bool skip_stall_redirect) { eos_static_debug("%s", "msg=\"reset all access rules\""); eos::common::RWMutexWriteLock lock(Access::gAccessMutex); Access::gBannedUsers.clear(); Access::gBannedGroups.clear(); Access::gBannedHosts.clear(); Access::gBannedDomains.clear(); Access::gBannedTokens.clear(); Access::gAllowedUsers.clear(); Access::gAllowedGroups.clear(); Access::gAllowedHosts.clear(); Access::gAllowedDomains.clear(); Access::gAllowedTokens.clear(); Access::gStallHosts.clear(); Access::gNoStallHosts.clear(); if (skip_stall_redirect == false) { Access::gRedirectionRules.clear(); Access::gStallRules.clear(); Access::gStallComment.clear(); Access::gUserRedirection.clear(); Access::gGroupRedirection.clear(); Access::gStallGlobal = Access::gStallRead = Access::gStallWrite = Access::gStallUserGroup = false; } } /*----------------------------------------------------------------------------*/ /** * @brief Static function to retrieve the access configuration from the global * configuration and apply to the static singleton rules. */ /*----------------------------------------------------------------------------*/ void Access::ApplyAccessConfig(bool applyredirectandstall) { Access::Reset(!applyredirectandstall); eos::common::RWMutexWriteLock lock(Access::gAccessMutex); std::string userval = FsView::gFsView.GetGlobalConfig(gUserKey); std::string groupval = FsView::gFsView.GetGlobalConfig(gGroupKey); std::string hostval = FsView::gFsView.GetGlobalConfig(gHostKey); std::string domainval = FsView::gFsView.GetGlobalConfig(gDomainKey); std::string tokenval = FsView::gFsView.GetGlobalConfig(gTokenKey); std::string useraval = FsView::gFsView.GetGlobalConfig(gAllowedUserKey); std::string groupaval = FsView::gFsView.GetGlobalConfig(gAllowedGroupKey); std::string hostaval = FsView::gFsView.GetGlobalConfig(gAllowedHostKey); std::string domainaval = FsView::gFsView.GetGlobalConfig(gAllowedDomainKey); std::string tokenaval = FsView::gFsView.GetGlobalConfig(gAllowedTokenKey); std::string stall = FsView::gFsView.GetGlobalConfig(gStallKey); std::string redirect = FsView::gFsView.GetGlobalConfig(gRedirectionKey); std::string stallhosts = FsView::gFsView.GetGlobalConfig(gStallHostsKey); std::string nostallhosts = FsView::gFsView.GetGlobalConfig(gNoStallHostsKey); // parse the list's and fill the hash std::vector tokens; std::string delimiter = ":"; std::vector subtokens; std::string subdelimiter = "~"; tokens.clear(); eos::common::StringConversion::Tokenize(userval, tokens, delimiter); for (size_t i = 0; i < tokens.size(); i++) { if (tokens[i].length()) { uid_t uid = atoi(tokens[i].c_str()); Access::gBannedUsers.insert(uid); } } tokens.clear(); eos::common::StringConversion::Tokenize(groupval, tokens, delimiter); for (size_t i = 0; i < tokens.size(); i++) { if (tokens[i].length()) { gid_t gid = atoi(tokens[i].c_str()); Access::gBannedGroups.insert(gid); } } tokens.clear(); eos::common::StringConversion::Tokenize(hostval, tokens, delimiter); for (size_t i = 0; i < tokens.size(); i++) { if (tokens[i].length()) { Access::gBannedHosts.insert(tokens[i]); } } tokens.clear(); eos::common::StringConversion::Tokenize(domainval, tokens, delimiter); for (size_t i = 0; i < tokens.size(); i++) { if (tokens[i].length()) { Access::gBannedDomains.insert(tokens[i]); } } tokens.clear(); eos::common::StringConversion::Tokenize(tokenval, tokens, delimiter); for (size_t i = 0; i < tokens.size(); i++) { if (tokens[i].length()) { Access::gBannedTokens.insert(tokens[i]); } } tokens.clear(); eos::common::StringConversion::Tokenize(useraval, tokens, delimiter); for (size_t i = 0; i < tokens.size(); i++) { if (tokens[i].length()) { uid_t uid = atoi(tokens[i].c_str()); Access::gAllowedUsers.insert(uid); } } tokens.clear(); eos::common::StringConversion::Tokenize(groupaval, tokens, delimiter); for (size_t i = 0; i < tokens.size(); i++) { if (tokens[i].length()) { gid_t gid = atoi(tokens[i].c_str()); Access::gAllowedGroups.insert(gid); } } tokens.clear(); eos::common::StringConversion::Tokenize(hostaval, tokens, delimiter); for (size_t i = 0; i < tokens.size(); i++) { if (tokens[i].length()) { Access::gAllowedHosts.insert(tokens[i]); } } tokens.clear(); eos::common::StringConversion::Tokenize(domainaval, tokens, delimiter); for (size_t i = 0; i < tokens.size(); i++) { if (tokens[i].length()) { Access::gAllowedDomains.insert(tokens[i]); } } tokens.clear(); eos::common::StringConversion::Tokenize(tokenaval, tokens, delimiter); for (size_t i = 0; i < tokens.size(); i++) { if (tokens[i].length()) { Access::gAllowedTokens.insert(tokens[i]); } } tokens.clear(); eos::common::StringConversion::Tokenize(stallhosts, tokens, delimiter); for (size_t i = 0; i < tokens.size(); i++) { if (tokens[i].length()) { Access::gStallHosts.insert(tokens[i]); } } tokens.clear(); eos::common::StringConversion::Tokenize(nostallhosts, tokens, delimiter); for (size_t i = 0; i < tokens.size(); i++) { if (tokens[i].length()) { Access::gNoStallHosts.insert(tokens[i]); } } tokens.clear(); delimiter = ","; eos::common::StringConversion::Tokenize(stall, tokens, delimiter); for (size_t i = 0; i < tokens.size(); ++i) { if (tokens[i].length()) { if (applyredirectandstall || ((tokens[i].find("rate:") == 0) || (tokens[i].find("threads:") == 0))) { subtokens.clear(); eos::common::StringConversion::Tokenize(tokens[i], subtokens, subdelimiter); if (subtokens.size() >= 2) { Access::gStallRules[subtokens[0]] = subtokens[1]; if (subtokens[0] == ("r:*")) { gStallRead = true; } if (subtokens[0] == ("w:*")) { gStallWrite = true; } if (subtokens[0] == ("*")) { gStallGlobal = true; } if ((subtokens[0].find("rate:") == 0)) { gStallUserGroup = true; } if (subtokens.size() == 3) { XrdOucString comment = subtokens[2].c_str(); while (comment.replace("_#KOMMA#_", ",")) { } while (comment.replace("_#TILDE#_", "~")) { } Access::gStallComment[subtokens[0]] = comment.c_str(); } } } } } if (applyredirectandstall) { tokens.clear(); delimiter = ","; eos::common::StringConversion::Tokenize(redirect, tokens, delimiter); for (size_t i = 0; i < tokens.size(); i++) { if (tokens[i].length()) { subtokens.clear(); eos::common::StringConversion::Tokenize(tokens[i], subtokens, subdelimiter); if (subtokens.size() == 2) { Access::gRedirectionRules[subtokens[0]] = subtokens[1]; } } } } } /*----------------------------------------------------------------------------*/ /** * @brief Static function to store all defined rules in the global configuration. * * @return true if successful, otherwise false */ /*----------------------------------------------------------------------------*/ bool Access::StoreAccessConfig() { std::set::const_iterator ituid; std::set::const_iterator itgid; std::set::const_iterator ithost; std::set::const_iterator itdomain; std::set::const_iterator ittoken; std::set::const_iterator itstallhosts; std::set::const_iterator itnostallhosts; std::map::const_iterator itstall; std::map::const_iterator itredirect; std::string userval = ""; std::string groupval = ""; std::string hostval = ""; std::string domainval = ""; std::string tokenval = ""; std::string useraval = ""; std::string groupaval = ""; std::string hostaval = ""; std::string domainaval = ""; std::string tokenaval = ""; std::string stall = ""; std::string redirect = ""; std::string stallhosts = ""; std::string nostallhosts = ""; for (ituid = Access::gBannedUsers.begin(); ituid != Access::gBannedUsers.end(); ituid++) { userval += eos::common::Mapping::UidAsString(*ituid); userval += ":"; } for (itgid = Access::gBannedGroups.begin(); itgid != Access::gBannedGroups.end(); itgid++) { groupval += eos::common::Mapping::GidAsString(*itgid); groupval += ":"; } for (ithost = Access::gBannedHosts.begin(); ithost != Access::gBannedHosts.end(); ithost++) { hostval += ithost->c_str(); hostval += ":"; } for (itdomain = Access::gBannedDomains.begin(); itdomain != Access::gBannedDomains.end(); itdomain++) { hostval += itdomain->c_str(); hostval += ":"; } for (ituid = Access::gAllowedUsers.begin(); ituid != Access::gAllowedUsers.end(); ituid++) { useraval += eos::common::Mapping::UidAsString(*ituid); useraval += ":"; } for (itgid = Access::gAllowedGroups.begin(); itgid != Access::gAllowedGroups.end(); itgid++) { groupaval += eos::common::Mapping::GidAsString(*itgid); groupaval += ":"; } for (ithost = Access::gAllowedHosts.begin(); ithost != Access::gAllowedHosts.end(); ithost++) { hostaval += ithost->c_str(); hostaval += ":"; } for (itdomain = Access::gAllowedDomains.begin(); itdomain != Access::gAllowedDomains.end(); itdomain++) { domainaval += itdomain->c_str(); domainaval += ":"; } for (ittoken = Access::gAllowedTokens.begin(); ittoken != Access::gAllowedTokens.end(); ittoken++) { tokenaval += ittoken->c_str(); tokenaval += ":"; } for (itstallhosts = Access::gStallHosts.begin(); itstallhosts != Access::gStallHosts.end(); itstallhosts++) { stallhosts += itstallhosts->c_str(); stallhosts += ":"; } for (itnostallhosts = Access::gNoStallHosts.begin(); itnostallhosts != Access::gNoStallHosts.end(); itnostallhosts++) { nostallhosts += itnostallhosts->c_str(); nostallhosts += ":"; } gStallRead = gStallWrite = gStallGlobal = gStallUserGroup = false; for (itstall = Access::gStallRules.begin(); itstall != Access::gStallRules.end(); itstall++) { stall += itstall->first.c_str(); stall += "~"; stall += itstall->second.c_str(); stall += "~"; XrdOucString comment = Access::gStallComment[itstall->first].c_str(); while (comment.replace(",", "_#KOMMA#_")) { } while (comment.replace("~", "_#TILDE#_")) { } stall += comment.c_str(); stall += ","; if (itstall->first == ("r:*")) { gStallRead = true; } if (itstall->first == ("w:*")) { gStallWrite = true; } if (itstall->first == ("*")) { gStallGlobal = true; } if ((itstall->first.find("rate:") == 0)) { gStallUserGroup = true; } } for (itredirect = Access::gRedirectionRules.begin(); itredirect != Access::gRedirectionRules.end(); itredirect++) { redirect += itredirect->first.c_str(); redirect += "~"; redirect += itredirect->second.c_str(); redirect += ","; } std::string ukey = gUserKey; std::string gkey = gGroupKey; std::string hkey = gHostKey; std::string dkey = gDomainKey; std::string tkey = gTokenKey; std::string uakey = gAllowedUserKey; std::string gakey = gAllowedGroupKey; std::string hakey = gAllowedHostKey; std::string dakey = gAllowedDomainKey; std::string takey = gAllowedTokenKey; std::string shkey = gStallHostsKey; std::string nshkey = gNoStallHostsKey; bool ok = 1; ok &= FsView::gFsView.SetGlobalConfig(ukey, userval); ok &= FsView::gFsView.SetGlobalConfig(gkey, groupval); ok &= FsView::gFsView.SetGlobalConfig(hkey, hostval); ok &= FsView::gFsView.SetGlobalConfig(dkey, domainval); ok &= FsView::gFsView.SetGlobalConfig(tkey, tokenval); ok &= FsView::gFsView.SetGlobalConfig(uakey, useraval); ok &= FsView::gFsView.SetGlobalConfig(gakey, groupaval); ok &= FsView::gFsView.SetGlobalConfig(hakey, hostaval); ok &= FsView::gFsView.SetGlobalConfig(dakey, domainaval); ok &= FsView::gFsView.SetGlobalConfig(takey, tokenaval); ok &= FsView::gFsView.SetGlobalConfig(gStallKey, stall); ok &= FsView::gFsView.SetGlobalConfig(gRedirectionKey, redirect); ok &= FsView::gFsView.SetGlobalConfig(shkey, stallhosts); ok &= FsView::gFsView.SetGlobalConfig(nshkey, nostallhosts); if (!ok) { eos_static_err("unable to store configuration"); return false; } return true; } //------------------------------------------------------------------------------ // Get find limits in number of files/dirs returned for a certain user //------------------------------------------------------------------------------ void Access::GetFindLimits(const eos::common::VirtualIdentity& vid, uint64_t& dir_limit, uint64_t& file_limit) { eos::common::RWMutexReadLock access_rd_lock(gAccessMutex); if (gStallUserGroup) { std::string usermatchfiles = "rate:user:"; usermatchfiles += vid.uid_string; usermatchfiles += ":"; usermatchfiles += "FindFiles"; std::string groupmatchfiles = "rate:group:"; groupmatchfiles += vid.gid_string; groupmatchfiles += ":"; groupmatchfiles += "FindFiles"; std::string userwildcardmatchfiles = "rate:user:*:FindFiles"; if (gStallRules.count(usermatchfiles)) { file_limit = strtoul(gStallRules[usermatchfiles].c_str(), 0, 10); } else if (gStallRules.count(groupmatchfiles)) { file_limit = strtoul(gStallRules[groupmatchfiles].c_str(), 0, 10); } else if (gStallRules.count(userwildcardmatchfiles)) { file_limit = strtoul(gStallRules[userwildcardmatchfiles].c_str(), 0, 10); } std::string usermatchdirs = "rate:user:"; usermatchdirs += vid.uid_string; usermatchdirs += ":"; usermatchdirs += "FindDirs"; std::string groupmatchdirs = "rate:group:"; groupmatchdirs += vid.gid_string; groupmatchdirs += ":"; groupmatchdirs += "FindDirs"; std::string userwildcardmatchdirs = "rate:user:*:FindDirs"; if (gStallRules.count(usermatchdirs)) { dir_limit = strtoul(gStallRules[usermatchdirs].c_str(), 0, 10); } else if (gStallRules.count(groupmatchdirs)) { dir_limit = strtoul(gStallRules[groupmatchdirs].c_str(), 0, 10); } else if (gStallRules.count(userwildcardmatchdirs)) { dir_limit = strtoul(gStallRules[userwildcardmatchdirs].c_str(), 0, 10); } } } //------------------------------------------------------------------------------ // Set global stall rule and save the previous status //------------------------------------------------------------------------------ void Access::SetStallRule(const StallInfo& new_stall, StallInfo& old_stall) { eos::common::RWMutexWriteLock lock(Access::gAccessMutex); if (new_stall.mType.empty()) { return; } old_stall.mType = new_stall.mType; if (Access::gStallRules.count(new_stall.mType)) { old_stall.mDelay = Access::gStallRules[old_stall.mType]; old_stall.mComment = Access::gStallComment[old_stall.mType]; old_stall.mIsGlobal = Access::gStallGlobal; } if (new_stall.mDelay.empty()) { Access::gStallRules.erase(new_stall.mType); } else { Access::gStallRules[new_stall.mType] = new_stall.mDelay; } if (new_stall.mComment.empty()) { Access::gStallComment.erase(new_stall.mType); } else { Access::gStallComment[new_stall.mType] = new_stall.mComment; } Access::gStallGlobal = new_stall.mIsGlobal; } //------------------------------------------------------------------------------ // Set access rules for a slave to master transition. More precisely remove // any stall and redirection rules //------------------------------------------------------------------------------ void Access::SetSlaveToMasterRules() { eos_static_info("%s", "msg=\"remove any stall and redirection rules\""); eos::common::RWMutexWriteLock wr_lock(Access::gAccessMutex); Access::gRedirectionRules.erase(std::string("w:*")); Access::gRedirectionRules.erase(std::string("ENOENT:*")); Access::gStallRules.erase(std::string("w:*")); Access::gStallWrite = false; } //---------------------------------------------------------------------------- // Set access rules for a master to slave transition. //---------------------------------------------------------------------------- void Access::SetMasterToSlaveRules(const std::string& other_master_id) { eos::common::RWMutexWriteLock wr_lock(Access::gAccessMutex); if (other_master_id.empty()) { // No master - remove redirections and put a stall for writes eos_static_info("%s", "msg=\"no master, add global stall\""); Access::gRedirectionRules.erase(std::string("w:*")); Access::gRedirectionRules.erase(std::string("ENOENT:*")); Access::gStallRules[std::string("*")] = "60"; Access::gStallWrite = true; Access::gStallGlobal = true; } else { // We're the slave and there is a master - set redirection to him eos_static_info("msg=\"add redirection to master %s\"", other_master_id.c_str()); std::string host = other_master_id.substr(0, other_master_id.find(':')); Access::gRedirectionRules[std::string("w:*")] = host.c_str(); Access::gRedirectionRules[std::string("ENOENT:*")] = host.c_str(); // Remove write stall Access::gStallRules.erase(std::string("*")); Access::gStallRules.erase(std::string("w:*")); Access::gStallWrite = false; Access::gStallGlobal = false; } } //------------------------------------------------------------------------------ // Remove stall rule specified by key //------------------------------------------------------------------------------ void Access::RemoveStallRule(const std::string& key) { eos::common::RWMutexWriteLock wr_lock(Access::gAccessMutex); Access::gStallRules.erase(key); if (key.find("w:*") == 0) { Access::gStallWrite = false; } else if (key.find("r:*") == 0) { Access::gStallRead = false; } else if (key.find("*") == 0) { Access::gStallGlobal = false; } } //------------------------------------------------------------------------------ // Thread limit by user id //------------------------------------------------------------------------------ size_t Access::ThreadLimit(uid_t uid, bool lock_ns) { std::string id = "threads:" + std::to_string(uid); eos::common::RWMutexReadLock access_rd_lock; if (lock_ns) { access_rd_lock.Grab(gAccessMutex); } if (gStallRules.count(id.c_str())) { return strtoul(gStallRules[id.c_str()].c_str(), 0, 10); } else { if (gStallRules.count("threads:*")) { return strtoul(gStallRules["threads:*"].c_str(), 0, 10); } } return 65536; } //------------------------------------------------------------------------------ // Global thread limit //------------------------------------------------------------------------------ size_t Access::ThreadLimit(bool lock_ns) { eos::common::RWMutexReadLock access_rd_lock; if (lock_ns) { access_rd_lock.Grab(gAccessMutex); } if (gStallRules.count("threads:max")) { return strtoul(gStallRules["threads:max"].c_str(), 0, 10); } return 1000000; } //------------------------------------------------------------------------------ // Check if host matches white or black list for stalling //------------------------------------------------------------------------------ bool Access::CanStall(const char* host) { // you should have this lock eos::common::RWMutexReadLock access_rd_lock(gAccessMutex); if (gStallHosts.size()) { // white list behaviour for (auto rules : gStallHosts) { regex_t regex; if (!regcomp(®ex, rules.c_str(), REG_ICASE | REG_EXTENDED | REG_NOSUB)) { if (!regexec(®ex, host, 0, NULL, 0)) { regfree(®ex); return true; } else { regfree(®ex); } } } return false; } if (gNoStallHosts.size()) { // black list haviour for (auto rules : gNoStallHosts) { regex_t regex; if (!regcomp(®ex, rules.c_str(), REG_ICASE | REG_EXTENDED | REG_NOSUB)) { if (!regexec(®ex, host, 0, NULL, 0)) { regfree(®ex); return false; } else { regfree(®ex); } } } return true; } return true; } EOSMGMNAMESPACE_END