summaryrefslogtreecommitdiffstats
path: root/user.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'user.cpp')
-rw-r--r--user.cpp264
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
OpenPOWER on IntegriCloud