blob: bdca968d3d2f0acd24a303f4c08a14c50213ef6d [file] [log] [blame]
Vishwanatha Subbanna035a9692017-09-15 18:50:43 +05301#include <sys/stat.h>
2#include <string>
3#include <fstream>
4#include <experimental/filesystem>
5#include <gtest/gtest.h>
6#include <sdbusplus/bus.hpp>
7#include "user.hpp"
8namespace phosphor
9{
10namespace user
11{
12
13namespace fs = std::experimental::filesystem;
14
15constexpr auto path = "/dummy/user";
16constexpr auto testShadow = "/tmp/__tshadow__";
17constexpr auto shadowCopy = "/tmp/__tshadowCopy__";
18constexpr auto shadowCompare = "/tmp/__tshadowCompare__";
19
20// New password
21constexpr auto password = "passw0rd";
22
23constexpr auto MD5 = "1";
24constexpr auto SHA512 = "6";
25constexpr auto salt = "1G.cK/YP";
26
27// Example entry matching /etc/shadow structure
28constexpr auto spPwdp = "$1$1G.cK/YP$JI5t0oliPxZveXOvLcZ/H.:17344:1:90:7:::";
29
30class UserTest : public ::testing::Test
31{
32 public:
33 const std::string md5Salt = '$' + std::string(MD5) + '$'
34 + std::string(salt) + '$';
35 const std::string shaSalt = '$' + std::string(SHA512) + '$'
36 + std::string(salt) + '$';
37
38 const std::string entry = fs::path(path).filename().string() +
39 ':' + std::string(spPwdp);
40 sdbusplus::bus::bus bus;
41 phosphor::user::User user;
42
43 // Gets called as part of each TEST_F construction
44 UserTest()
45 : bus(sdbusplus::bus::new_default()),
46 user(bus, path)
47 {
48 // Create a shadow file entry
49 std::ofstream file(testShadow);
50 file << entry;
51 file.close();
52
53 // File to compare against
54 std::ofstream compare(shadowCompare);
55 compare << entry;
56 compare.close();
57 }
58
59 // Gets called as part of each TEST_F destruction
60 ~UserTest()
61 {
62 if (fs::exists(testShadow))
63 {
64 fs::remove(testShadow);
65 }
66
67 if (fs::exists(shadowCopy))
68 {
69 fs::remove(shadowCopy);
70 }
71
72 if (fs::exists(shadowCompare))
73 {
74 fs::remove(shadowCompare);
75 }
76 }
77
78 /** @brief wrapper for get crypt field */
79 auto getCryptField(char* data)
80 {
81 return User::getCryptField(
82 std::forward<decltype(data)>(data));
83 }
84
85 /** @brief wrapper for getSaltString */
86 auto getSaltString(const std::string& crypt,
87 const std::string& salt)
88 {
89 return User::getSaltString(
90 std::forward<decltype(crypt)>(crypt),
91 std::forward<decltype(salt)>(salt));
92 }
93
94 /** @brief wrapper for generateHash */
95 auto generateHash(const std::string& password,
96 const std::string& salt)
97 {
98 return User::generateHash(
99 std::forward<decltype(password)>(password),
100 std::forward<decltype(salt)>(salt));
101 }
102
103 /** @brief Applies the new password */
104 auto applyPassword()
105 {
106 return user.applyPassword(testShadow, shadowCopy,
107 password, salt);
108 }
109};
110
111/** @brief Makes sure that SHA512 crypt field is extracted
112 */
113TEST_F(UserTest, sha512GetCryptField)
114{
115 auto salt = const_cast<char*>(shaSalt.c_str());
116 EXPECT_EQ(SHA512, this->getCryptField(salt));
117}
118
119/** @brief Makes sure that MD5 crypt field is extracted as default
120 */
121TEST_F(UserTest, md55GetCryptFieldDefault)
122{
123 auto salt = const_cast<char*>("hello");
124 EXPECT_EQ(MD5, this->getCryptField(salt));
125}
126
127/** @brief Makes sure that MD5 crypt field is extracted
128 */
129TEST_F(UserTest, md55GetCryptField)
130{
131 auto salt = const_cast<char*>(md5Salt.c_str());
132 EXPECT_EQ(MD5, this->getCryptField(salt));
133}
134
135/** @brief Makes sure that salt string is put within $$
136 */
137TEST_F(UserTest, getSaltString)
138{
139 EXPECT_EQ(md5Salt, this->getSaltString(MD5, salt));
140}
141
142/** @brief Makes sure hash is generated correctly
143 */
144TEST_F(UserTest, generateHash)
145{
146 std::string sample = crypt(password, md5Salt.c_str());
147 std::string actual = generateHash(password, md5Salt);
148 EXPECT_EQ(sample, actual);
149}
150
151/** @brief Verifies that the correct password is written to file
152 */
153TEST_F(UserTest, applyPassword)
154{
155 // Update the password
156 applyPassword();
157
158 // Read files and compare
159 std::ifstream shadow(testShadow);
160 std::ifstream copy(shadowCompare);
161
162 std::string shadowEntry;
163 shadow >> shadowEntry;
164
165 std::string shadowCompareEntry;
166 copy >> shadowCompareEntry;
167
168 EXPECT_EQ(shadowEntry, shadowCompareEntry);
169}
170
171/** @brief Verifies the shadow copy file is removed
172 */
173TEST_F(UserTest, checkShadowCopyRemove)
174{
175 // Update the password so that the temp file is in action
176 applyPassword();
177
178 // Compare the permission of 2 files
179 struct stat shadow{};
180 struct stat temp{};
181
182 stat(testShadow, &shadow);
183 stat(shadowCopy, &temp);
184 EXPECT_EQ(false, fs::exists(shadowCopy));
185}
186
187/** @brief Verifies the permissions are correct
188 */
189TEST_F(UserTest, verifyShadowPermission)
190{
191 // Change the permission to 400-> -r--------
192 chmod(testShadow, S_IRUSR);
193 chmod(shadowCompare, S_IRUSR);
194
195 // Update the password so that the temp file is in action
196 applyPassword();
197
198 // Compare the permission of 2 files.
199 // file rename would make sure that the permissions
200 // of old are moved to new
201 struct stat shadow{};
202 struct stat compare{};
203
204 stat(testShadow, &shadow);
205 stat(shadowCompare, &compare);
206 EXPECT_EQ(shadow.st_mode, compare.st_mode);
207}
208
209} // namespace user
210} // namespace phosphor