diff options
Diffstat (limited to 'user.cpp')
-rw-r--r-- | user.cpp | 264 |
1 files changed, 0 insertions, 264 deletions
diff --git a/user.cpp b/user.cpp deleted file mode 100644 index 6999a98..0000000 --- a/user.cpp +++ /dev/null @@ -1,264 +0,0 @@ -/** - * Copyright © 2017 IBM Corporation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#include <cstring> -#include <unistd.h> -#include <sys/types.h> -#include <sys/stat.h> -#include <shadow.h> -#include <array> -#include <random> -#include <errno.h> -#include <xyz/openbmc_project/Common/error.hpp> -#include <phosphor-logging/log.hpp> -#include <phosphor-logging/elog.hpp> -#include <phosphor-logging/elog-errors.hpp> -#include "user.hpp" -#include "file.hpp" -#include "shadowlock.hpp" -#include "config.h" -namespace phosphor -{ -namespace user -{ - -constexpr auto SHADOW_FILE = "/etc/shadow"; - -// See crypt(3) -constexpr int SALT_LENGTH = 16; - -using namespace phosphor::logging; -using InsufficientPermission = - sdbusplus::xyz::openbmc_project::Common::Error::InsufficientPermission; -using InternalFailure = - sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure; -// Sets or updates the password -void User::setPassword(std::string newPassword) -{ - // Gate any access to /etc/shadow - phosphor::user::shadow::Lock lock(); - - // rewind to the start of shadow entry - setspent(); - - // Generate a random string from set [A-Za-z0-9./] - std::string salt{}; - salt.resize(SALT_LENGTH); - salt = randomString(SALT_LENGTH); - - // Apply the change. Updates could be directly made to shadow - // but then any update must be contained within the boundary - // of that user, else it would run into next entry and thus - // corrupting it. Classic example is when a password is set on - // a user ID that does not have a prior password - applyPassword(SHADOW_FILE, newPassword, salt); - return; -} - -void User::applyPassword(const std::string& shadowFile, - const std::string& password, const std::string& salt) -{ - // Needed by getspnam_r - struct spwd shdp; - struct spwd* pshdp; - - // This should be fine even if SHA512 is used. - std::array<char, 1024> buffer{}; - - // Open the shadow file for reading - phosphor::user::File shadow(shadowFile, "r"); - if ((shadow)() == NULL) - { - return raiseException(errno, "Error opening shadow file"); - } - - // open temp shadow file, by suffixing random name in shadow file name. - std::vector<char> tempFileName(shadowFile.begin(), shadowFile.end()); - std::vector<char> fileTemplate = {'_', '_', 'X', 'X', 'X', - 'X', 'X', 'X', '\0'}; - tempFileName.insert(tempFileName.end(), fileTemplate.begin(), - fileTemplate.end()); - - int fd = mkstemp(tempFileName.data()); - if (fd == -1) - { - return raiseException(errno, "Error creating temp shadow file"); - } - - std::string strTempFileName(tempFileName.data()); - // Open the temp shadow file for writing from provided fd - // By "true", remove it at exit if still there. - // This is needed to cleanup the temp file at exception - phosphor::user::File temp(fd, strTempFileName, "w", true); - if ((temp)() == NULL) - { - close(fd); - return raiseException(errno, "Error opening temp shadow file"); - } - fd = -1; // don't use fd anymore, as the File object owns it - - // Change the permission of this new temp file - // to be same as shadow so that it's secure - struct stat st - { - }; - auto r = fstat(fileno((shadow)()), &st); - if (r < 0) - { - return raiseException(errno, "Error reading shadow file mode"); - } - - r = fchmod(fileno((temp)()), st.st_mode); - if (r < 0) - { - return raiseException(errno, "Error setting temp file mode"); - } - - // Read shadow file and process - while (true) - { - auto r = fgetspent_r((shadow)(), &shdp, buffer.data(), - buffer.max_size(), &pshdp); - if (r) - { - if (errno == EACCES || errno == ERANGE) - { - return raiseException(errno, "Error reading shadow file"); - } - else - { - // Seem to have run over all - break; - } - } - - // Hash of password if the user matches - std::string hash{}; - - // Matched user - if (user == shdp.sp_namp) - { - // Update with new hashed password - hash = hashPassword(shdp.sp_pwdp, password, salt); - shdp.sp_pwdp = const_cast<char*>(hash.c_str()); - } - - // Apply - r = putspent(&shdp, (temp)()); - if (r < 0) - { - return raiseException(errno, "Error updating temp shadow file"); - } - } // All entries - - // Done - endspent(); - // flush contents to file first, before renaming to avoid - // corruption during power failure - fflush((temp)()); - - // Everything must be fine at this point - fs::rename(strTempFileName, shadowFile); - return; -} - -void User::raiseException(int errNo, const std::string& errMsg) -{ - using namespace std::string_literals; - if (errNo == EACCES) - { - auto message = "Access denied "s + errMsg; - log<level::ERR>(message.c_str()); - elog<InsufficientPermission>(); - } - else - { - log<level::ERR>(errMsg.c_str(), entry("USER=%s", user.c_str()), - entry("ERRNO=%d", errNo)); - elog<InternalFailure>(); - } -} - -std::string User::hashPassword(char* spPwdp, const std::string& password, - const std::string& salt) -{ - // Parse and get crypt algo - auto cryptAlgo = getCryptField(spPwdp); - if (cryptAlgo.empty()) - { - log<level::ERR>("Error finding crypt algo", - entry("USER=%s", user.c_str())); - elog<InternalFailure>(); - } - - // Update shadow password pointer with hash - auto saltString = getSaltString(cryptAlgo, salt); - return generateHash(password, saltString); -} - -// Returns a random string in set [A-Za-z0-9./] -// of size numChars -const std::string User::randomString(int length) -{ - // Populated random string - std::string random{}; - - // Needed per crypt(3) - std::string set = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijk" - "lmnopqrstuvwxyz0123456789./"; - - // Will be used to obtain a seed for the random number engine - std::random_device rd; - - // Standard mersenne_twister_engine seeded with rd() - std::mt19937 gen(rd()); - - std::uniform_int_distribution<> dis(0, set.size() - 1); - for (int count = 0; count < length; count++) - { - // Use dis to transform the random unsigned int generated by - // gen into a int in [1, SALT_LENGTH] - random.push_back(set.at(dis(gen))); - } - return random; -} - -// Extract crypto algorithm field -CryptAlgo User::getCryptField(char* spPwdp) -{ - char* savePtr{}; - if (std::string{spPwdp}.front() != '$') - { - return DEFAULT_CRYPT_ALGO; - } - return strtok_r(spPwdp, "$", &savePtr); -} - -// Returns specific format of salt string -std::string User::getSaltString(const std::string& crypt, - const std::string& salt) -{ - return '$' + crypt + '$' + salt + '$'; -} - -// Given a password and salt, generates hash -std::string User::generateHash(const std::string& password, - const std::string& salt) -{ - return crypt(password.c_str(), salt.c_str()); -} - -} // namespace user -} // namespace phosphor |