| #include <sys/stat.h> |
| #include <string> |
| #include <fstream> |
| #include <experimental/filesystem> |
| #include <gtest/gtest.h> |
| #include <sdbusplus/bus.hpp> |
| #include "user.hpp" |
| namespace phosphor |
| { |
| namespace user |
| { |
| |
| namespace fs = std::experimental::filesystem; |
| |
| constexpr auto path = "/dummy/user"; |
| constexpr auto testShadow = "/tmp/__tshadow__"; |
| constexpr auto shadowCompare = "/tmp/__tshadowCompare__"; |
| |
| // New password |
| constexpr auto password = "passw0rd"; |
| |
| constexpr auto MD5 = "1"; |
| constexpr auto SHA512 = "6"; |
| constexpr auto salt = "1G.cK/YP"; |
| |
| // Example entry matching /etc/shadow structure |
| constexpr auto spPwdp = "$1$1G.cK/YP$JI5t0oliPxZveXOvLcZ/H.:17344:1:90:7:::"; |
| |
| class UserTest : public ::testing::Test |
| { |
| public: |
| const std::string md5Salt = |
| '$' + std::string(MD5) + '$' + std::string(salt) + '$'; |
| const std::string shaSalt = |
| '$' + std::string(SHA512) + '$' + std::string(salt) + '$'; |
| |
| const std::string entry = |
| fs::path(path).filename().string() + ':' + std::string(spPwdp); |
| sdbusplus::bus::bus bus; |
| phosphor::user::User user; |
| |
| // Gets called as part of each TEST_F construction |
| UserTest() : bus(sdbusplus::bus::new_default()), user(bus, path) |
| { |
| // Create a shadow file entry |
| std::ofstream file(testShadow); |
| file << entry; |
| file.close(); |
| |
| // File to compare against |
| std::ofstream compare(shadowCompare); |
| compare << entry; |
| compare.close(); |
| } |
| |
| // Gets called as part of each TEST_F destruction |
| ~UserTest() |
| { |
| if (fs::exists(testShadow)) |
| { |
| fs::remove(testShadow); |
| } |
| |
| if (fs::exists(shadowCompare)) |
| { |
| fs::remove(shadowCompare); |
| } |
| } |
| |
| /** @brief wrapper for get crypt field */ |
| auto getCryptField(char* data) |
| { |
| return User::getCryptField(std::forward<decltype(data)>(data)); |
| } |
| |
| /** @brief wrapper for getSaltString */ |
| auto getSaltString(const std::string& crypt, const std::string& salt) |
| { |
| return User::getSaltString(std::forward<decltype(crypt)>(crypt), |
| std::forward<decltype(salt)>(salt)); |
| } |
| |
| /** @brief wrapper for generateHash */ |
| auto generateHash(const std::string& password, const std::string& salt) |
| { |
| return User::generateHash(std::forward<decltype(password)>(password), |
| std::forward<decltype(salt)>(salt)); |
| } |
| |
| /** @brief Applies the new password */ |
| auto applyPassword() |
| { |
| return user.applyPassword(testShadow, password, salt); |
| } |
| }; |
| |
| /** @brief Makes sure that SHA512 crypt field is extracted |
| */ |
| TEST_F(UserTest, sha512GetCryptField) |
| { |
| auto salt = const_cast<char*>(shaSalt.c_str()); |
| EXPECT_EQ(SHA512, this->getCryptField(salt)); |
| } |
| |
| /** @brief Makes sure that MD5 crypt field is extracted as default |
| */ |
| TEST_F(UserTest, md55GetCryptFieldDefault) |
| { |
| auto salt = const_cast<char*>("hello"); |
| EXPECT_EQ(MD5, this->getCryptField(salt)); |
| } |
| |
| /** @brief Makes sure that MD5 crypt field is extracted |
| */ |
| TEST_F(UserTest, md55GetCryptField) |
| { |
| auto salt = const_cast<char*>(md5Salt.c_str()); |
| EXPECT_EQ(MD5, this->getCryptField(salt)); |
| } |
| |
| /** @brief Makes sure that salt string is put within $$ |
| */ |
| TEST_F(UserTest, getSaltString) |
| { |
| EXPECT_EQ(md5Salt, this->getSaltString(MD5, salt)); |
| } |
| |
| /** @brief Makes sure hash is generated correctly |
| */ |
| TEST_F(UserTest, generateHash) |
| { |
| std::string sample = crypt(password, md5Salt.c_str()); |
| std::string actual = generateHash(password, md5Salt); |
| EXPECT_EQ(sample, actual); |
| } |
| |
| /** @brief Verifies that the correct password is written to file |
| */ |
| TEST_F(UserTest, applyPassword) |
| { |
| // Update the password |
| applyPassword(); |
| |
| // Read files and compare |
| std::ifstream shadow(testShadow); |
| std::ifstream copy(shadowCompare); |
| |
| std::string shadowEntry; |
| shadow >> shadowEntry; |
| |
| std::string shadowCompareEntry; |
| copy >> shadowCompareEntry; |
| |
| EXPECT_EQ(shadowEntry, shadowCompareEntry); |
| } |
| |
| /** @brief Verifies the permissions are correct |
| */ |
| TEST_F(UserTest, verifyShadowPermission) |
| { |
| // Change the permission to 400-> -r-------- |
| chmod(testShadow, S_IRUSR); |
| chmod(shadowCompare, S_IRUSR); |
| |
| // Update the password so that the temp file is in action |
| applyPassword(); |
| |
| // Compare the permission of 2 files. |
| // file rename would make sure that the permissions |
| // of old are moved to new |
| struct stat shadow |
| { |
| }; |
| struct stat compare |
| { |
| }; |
| |
| stat(testShadow, &shadow); |
| stat(shadowCompare, &compare); |
| EXPECT_EQ(shadow.st_mode, compare.st_mode); |
| } |
| |
| } // namespace user |
| } // namespace phosphor |