From 9164fd9b15e7af88e8b80c7ed7dad45266327a57 Mon Sep 17 00:00:00 2001 From: Richard Marian Thomaiyar Date: Wed, 13 Jun 2018 16:51:00 +0530 Subject: Support for password & security configuration Support for password & security enforcement configuration added. Implements the D-Bus interface properties to read and configure minimum password length, old password remember history, unlock timeout and maximum login attempt. Change-Id: I1a462a8a5d1f5dd07f3b594d62bd9c61bbdddb9c Signed-off-by: Richard Marian Thomaiyar Signed-off-by: Brad Bishop --- user_mgr.cpp | 306 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++- user_mgr.hpp | 61 +++++++++++- 2 files changed, 365 insertions(+), 2 deletions(-) diff --git a/user_mgr.cpp b/user_mgr.cpp index cba9366..ee72b05 100644 --- a/user_mgr.cpp +++ b/user_mgr.cpp @@ -47,6 +47,20 @@ static constexpr size_t ipmiMaxUserNameLen = 16; static constexpr size_t systemMaxUserNameLen = 30; static constexpr size_t maxSystemUsers = 30; static constexpr const char *grpSsh = "ssh"; +static constexpr uint8_t minPasswdLength = 8; +static constexpr int success = 0; +static constexpr int failure = -1; + +// pam modules related +static constexpr const char *pamTally2 = "pam_tally2.so"; +static constexpr const char *pamCrackLib = "pam_cracklib.so"; +static constexpr const char *pamPWHistory = "pam_pwhistory.so"; +static constexpr const char *minPasswdLenProp = "minlen"; +static constexpr const char *remOldPasswdCount = "remember"; +static constexpr const char *maxFailedAttempt = "deny"; +static constexpr const char *unlockTimeout = "unlock_time"; +static constexpr const char *pamPasswdConfigFile = "/etc/pam.d/common-password"; +static constexpr const char *pamAuthConfigFile = "/etc/pam.d/common-auth"; using namespace phosphor::logging; using InsufficientPermission = @@ -408,6 +422,193 @@ void UserMgr::updateGroupsAndPriv(const std::string &userName, return; } +uint8_t UserMgr::minPasswordLength(uint8_t value) +{ + if (value == AccountPolicyIface::minPasswordLength()) + { + return value; + } + if (value < minPasswdLength) + { + return value; + } + if (setPamModuleArgValue(pamCrackLib, minPasswdLenProp, + std::to_string(value)) != success) + { + log("Unable to set minPasswordLength"); + elog(); + } + return AccountPolicyIface::minPasswordLength(value); +} + +uint8_t UserMgr::rememberOldPasswordTimes(uint8_t value) +{ + if (value == AccountPolicyIface::rememberOldPasswordTimes()) + { + return value; + } + if (setPamModuleArgValue(pamPWHistory, remOldPasswdCount, + std::to_string(value)) != success) + { + log("Unable to set rememberOldPasswordTimes"); + elog(); + } + return AccountPolicyIface::rememberOldPasswordTimes(value); +} + +uint16_t UserMgr::maxLoginAttemptBeforeLockout(uint16_t value) +{ + if (value == AccountPolicyIface::maxLoginAttemptBeforeLockout()) + { + return value; + } + if (setPamModuleArgValue(pamTally2, maxFailedAttempt, + std::to_string(value)) != success) + { + log("Unable to set maxLoginAttemptBeforeLockout"); + elog(); + } + return AccountPolicyIface::maxLoginAttemptBeforeLockout(value); +} + +uint32_t UserMgr::accountUnlockTimeout(uint32_t value) +{ + if (value == AccountPolicyIface::accountUnlockTimeout()) + { + return value; + } + if (setPamModuleArgValue(pamTally2, unlockTimeout, std::to_string(value)) != + success) + { + log("Unable to set accountUnlockTimeout"); + elog(); + } + return AccountPolicyIface::accountUnlockTimeout(value); +} + +int UserMgr::getPamModuleArgValue(const std::string &moduleName, + const std::string &argName, + std::string &argValue) +{ + std::string fileName; + if (moduleName == pamTally2) + { + fileName = pamAuthConfigFile; + } + else + { + fileName = pamPasswdConfigFile; + } + std::ifstream fileToRead(fileName, std::ios::in); + if (!fileToRead.is_open()) + { + log("Failed to open pam configuration file", + entry("FILE_NAME=%s", fileName.c_str())); + return failure; + } + std::string line; + auto argSearch = argName + "="; + size_t startPos = 0; + size_t endPos = 0; + while (getline(fileToRead, line)) + { + // skip comments section starting with # + if ((startPos = line.find('#')) != std::string::npos) + { + if (startPos == 0) + { + continue; + } + // skip comments after meaningful section and process those + line = line.substr(0, startPos); + } + if (line.find(moduleName) != std::string::npos) + { + if ((startPos = line.find(argSearch)) != std::string::npos) + { + if ((endPos = line.find(' ', startPos)) == std::string::npos) + { + endPos = line.size(); + } + startPos += argSearch.size(); + argValue = line.substr(startPos, endPos - startPos); + return success; + } + } + } + return failure; +} + +int UserMgr::setPamModuleArgValue(const std::string &moduleName, + const std::string &argName, + const std::string &argValue) +{ + std::string fileName; + if (moduleName == pamTally2) + { + fileName = pamAuthConfigFile; + } + else + { + fileName = pamPasswdConfigFile; + } + std::string tmpFileName = fileName + "_tmp"; + std::ifstream fileToRead(fileName, std::ios::in); + std::ofstream fileToWrite(tmpFileName, std::ios::out); + if (!fileToRead.is_open() || !fileToWrite.is_open()) + { + log("Failed to open pam configuration /tmp file", + entry("FILE_NAME=%s", fileName.c_str())); + return failure; + } + std::string line; + auto argSearch = argName + "="; + size_t startPos = 0; + size_t endPos = 0; + bool found = false; + while (getline(fileToRead, line)) + { + // skip comments section starting with # + if ((startPos = line.find('#')) != std::string::npos) + { + if (startPos == 0) + { + fileToWrite << line << std::endl; + continue; + } + // skip comments after meaningful section and process those + line = line.substr(0, startPos); + } + if (line.find(moduleName) != std::string::npos) + { + if ((startPos = line.find(argSearch)) != std::string::npos) + { + if ((endPos = line.find(' ', startPos)) == std::string::npos) + { + endPos = line.size(); + } + startPos += argSearch.size(); + fileToWrite << line.substr(0, startPos) << argValue + << line.substr(endPos, line.size() - endPos) + << std::endl; + found = true; + continue; + } + } + fileToWrite << line << std::endl; + } + fileToWrite.close(); + fileToRead.close(); + if (found) + { + if (std::rename(tmpFileName.c_str(), fileName.c_str()) == 0) + { + return success; + } + } + return failure; +} + void UserMgr::userEnable(const std::string &userName, bool enabled) { // All user management lock has to be based on /etc/shadow @@ -594,11 +795,114 @@ void UserMgr::initUserObjects(void) } UserMgr::UserMgr(sdbusplus::bus::bus &bus, const char *path) : - UserMgrIface(bus, path), bus(bus), path(path) + UserMgrIface(bus, path), AccountPolicyIface(bus, path), bus(bus), path(path) { UserMgrIface::allPrivileges(privMgr); std::sort(groupsMgr.begin(), groupsMgr.end()); UserMgrIface::allGroups(groupsMgr); + std::string valueStr; + auto value = minPasswdLength; + unsigned long tmp = 0; + if (getPamModuleArgValue(pamCrackLib, minPasswdLenProp, valueStr) != + success) + { + AccountPolicyIface::minPasswordLength(minPasswdLength); + } + else + { + try + { + tmp = std::stoul(valueStr, nullptr); + if (tmp > std::numeric_limits::max()) + { + throw std::out_of_range("Out of range"); + } + value = static_cast(tmp); + } + catch (const std::exception &e) + { + log("Exception for MinPasswordLength", + entry("WHAT=%s", e.what())); + throw e; + } + AccountPolicyIface::minPasswordLength(value); + } + valueStr.clear(); + if (getPamModuleArgValue(pamPWHistory, remOldPasswdCount, valueStr) != + success) + { + AccountPolicyIface::rememberOldPasswordTimes(0); + } + else + { + value = 0; + try + { + tmp = std::stoul(valueStr, nullptr); + if (tmp > std::numeric_limits::max()) + { + throw std::out_of_range("Out of range"); + } + value = static_cast(tmp); + } + catch (const std::exception &e) + { + log("Exception for RememberOldPasswordTimes", + entry("WHAT=%s", e.what())); + throw e; + } + AccountPolicyIface::rememberOldPasswordTimes(value); + } + valueStr.clear(); + if (getPamModuleArgValue(pamTally2, maxFailedAttempt, valueStr) != success) + { + AccountPolicyIface::maxLoginAttemptBeforeLockout(0); + } + else + { + uint16_t value16 = 0; + try + { + tmp = std::stoul(valueStr, nullptr); + if (tmp > std::numeric_limits::max()) + { + throw std::out_of_range("Out of range"); + } + value16 = static_cast(tmp); + } + catch (const std::exception &e) + { + log("Exception for MaxLoginAttemptBeforLockout", + entry("WHAT=%s", e.what())); + throw e; + } + AccountPolicyIface::maxLoginAttemptBeforeLockout(value16); + } + valueStr.clear(); + if (getPamModuleArgValue(pamTally2, unlockTimeout, valueStr) != success) + { + AccountPolicyIface::accountUnlockTimeout(0); + } + else + { + uint32_t value32 = 0; + try + { + tmp = std::stoul(valueStr, nullptr); + if (tmp > std::numeric_limits::max()) + { + throw std::out_of_range("Out of range"); + } + value32 = static_cast(tmp); + } + catch (const std::exception &e) + { + log("Exception for AccountUnlockTimeout", + entry("WHAT=%s", e.what())); + throw e; + } + AccountPolicyIface::accountUnlockTimeout(value32); + } initUserObjects(); } diff --git a/user_mgr.hpp b/user_mgr.hpp index 44e14f7..b599724 100644 --- a/user_mgr.hpp +++ b/user_mgr.hpp @@ -17,6 +17,7 @@ #include #include #include +#include #include #include "users.hpp" @@ -28,10 +29,13 @@ namespace user using UserMgrIface = sdbusplus::xyz::openbmc_project::User::server::Manager; using UserSSHLists = std::pair, std::vector>; +using AccountPolicyIface = + sdbusplus::xyz::openbmc_project::User::server::AccountPolicy; + /** @class UserMgr * @brief Responsible for managing user accounts over the D-Bus interface. */ -class UserMgr : public UserMgrIface +class UserMgr : public UserMgrIface, AccountPolicyIface { public: UserMgr() = delete; @@ -93,6 +97,35 @@ class UserMgr : public UserMgrIface */ void userEnable(const std::string &userName, bool enabled); + /** @brief update minimum password length requirement + * + * @param[in] val - minimum password length + * @return - minimum password length + */ + uint8_t minPasswordLength(uint8_t val) override; + + /** @brief update old password history count + * + * @param[in] val - number of times old passwords has to be avoided + * @return - number of times old password has to be avoided + */ + uint8_t rememberOldPasswordTimes(uint8_t val) override; + + /** @brief update maximum number of failed login attempt before locked + * out. + * + * @param[in] val - number of allowed attempt + * @return - number of allowed attempt + */ + uint16_t maxLoginAttemptBeforeLockout(uint16_t val) override; + + /** @brief update timeout to unlock the account + * + * @param[in] val - value in seconds + * @return - value in seconds + */ + uint32_t accountUnlockTimeout(uint32_t val) override; + private: /** @brief sdbusplus handler */ sdbusplus::bus::bus &bus; @@ -201,6 +234,32 @@ class UserMgr : public UserMgrIface * @return - returns user count */ size_t getIpmiUsersCount(void); + + /** @brief get pam argument value + * method to get argument value from pam configuration + * + * @param[in] moduleName - name of the module from where arg has to be read + * @param[in] argName - argument name + * @param[out] argValue - argument value + * + * @return 0 - success state of the function + */ + int getPamModuleArgValue(const std::string &moduleName, + const std::string &argName, std::string &argValue); + + /** @brief set pam argument value + * method to set argument value in pam configuration + * + * @param[in] moduleName - name of the module in which argument value has + * to be set + * @param[in] argName - argument name + * @param[out] argValue - argument value + * + * @return 0 - success state of the function + */ + int setPamModuleArgValue(const std::string &moduleName, + const std::string &argName, + const std::string &argValue); }; } // namespace user -- cgit v1.2.1