1 /*
2 // Copyright (c) 2020 Intel Corporation
3 //
4 // Licensed under the Apache License, Version 2.0 (the "License");
5 // you may not use this file except in compliance with the License.
6 // You may obtain a copy of the License at
7 //
8 // http://www.apache.org/licenses/LICENSE-2.0
9 //
10 // Unless required by applicable law or agreed to in writing, software
11 // distributed under the License is distributed on an "AS IS" BASIS,
12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 // See the License for the specific language governing permissions and
14 // limitations under the License.
15 */
16 #include "password.hpp"
17
18 #include "xyz/openbmc_project/BIOSConfig/Common/error.hpp"
19 #include "xyz/openbmc_project/Common/error.hpp"
20
21 #include <boost/algorithm/hex.hpp>
22 #include <boost/asio.hpp>
23 #include <phosphor-logging/elog-errors.hpp>
24 #include <phosphor-logging/lg2.hpp>
25 #include <sdbusplus/asio/connection.hpp>
26 #include <sdbusplus/asio/object_server.hpp>
27
28 #include <fstream>
29 #include <iostream>
30
31 namespace bios_config_pwd
32 {
33 using namespace sdbusplus::xyz::openbmc_project::Common::Error;
34 using namespace sdbusplus::xyz::openbmc_project::BIOSConfig::Common::Error;
35
compareDigest(const EVP_MD * digestFunc,size_t digestLen,const std::array<uint8_t,maxHashSize> & expected,const std::array<uint8_t,maxSeedSize> & seed,const std::string & rawData)36 bool Password::compareDigest(const EVP_MD* digestFunc, size_t digestLen,
37 const std::array<uint8_t, maxHashSize>& expected,
38 const std::array<uint8_t, maxSeedSize>& seed,
39 const std::string& rawData)
40 {
41 std::vector<uint8_t> output(digestLen);
42 unsigned int hashLen = digestLen;
43
44 if (!PKCS5_PBKDF2_HMAC(reinterpret_cast<const char*>(rawData.c_str()),
45 rawData.length() + 1,
46 reinterpret_cast<const unsigned char*>(seed.data()),
47 seed.size(), iterValue, digestFunc, hashLen,
48 output.data()))
49 {
50 lg2::error("Generate PKCS5_PBKDF2_HMAC Integrity Check Value failed");
51 throw InternalFailure();
52 }
53
54 if (std::memcmp(output.data(), expected.data(),
55 output.size() * sizeof(uint8_t)) == 0)
56 {
57 return true;
58 }
59
60 return false;
61 }
62
isMatch(const std::array<uint8_t,maxHashSize> & expected,const std::array<uint8_t,maxSeedSize> & seed,const std::string & rawData,const std::string & algo)63 bool Password::isMatch(const std::array<uint8_t, maxHashSize>& expected,
64 const std::array<uint8_t, maxSeedSize>& seed,
65 const std::string& rawData, const std::string& algo)
66 {
67 lg2::error("isMatch");
68
69 if (algo == "SHA256")
70 {
71 return compareDigest(EVP_sha256(), SHA256_DIGEST_LENGTH, expected, seed,
72 rawData);
73 }
74
75 if (algo == "SHA384")
76 {
77 return compareDigest(EVP_sha384(), SHA384_DIGEST_LENGTH, expected, seed,
78 rawData);
79 }
80
81 return false;
82 }
83
getParam(std::array<uint8_t,maxHashSize> & orgUsrPwdHash,std::array<uint8_t,maxHashSize> & orgAdminPwdHash,std::array<uint8_t,maxSeedSize> & seed,std::string & hashAlgo)84 bool Password::getParam(std::array<uint8_t, maxHashSize>& orgUsrPwdHash,
85 std::array<uint8_t, maxHashSize>& orgAdminPwdHash,
86 std::array<uint8_t, maxSeedSize>& seed,
87 std::string& hashAlgo)
88 {
89 try
90 {
91 nlohmann::json json = nullptr;
92 std::ifstream ifs(seedFile.c_str());
93 if (ifs.is_open())
94 {
95 try
96 {
97 json = nlohmann::json::parse(ifs, nullptr, false);
98 }
99 catch (const nlohmann::json::parse_error& e)
100 {
101 lg2::error("Failed to parse JSON file: {ERROR}", "ERROR", e);
102 return false;
103 }
104
105 if (!json.is_discarded())
106 {
107 orgUsrPwdHash = json["UserPwdHash"];
108 orgAdminPwdHash = json["AdminPwdHash"];
109 seed = json["Seed"];
110 hashAlgo = json["HashAlgo"];
111 }
112 }
113 }
114 catch (nlohmann::detail::exception& e)
115 {
116 lg2::error("Failed to parse JSON file: {ERROR}", "ERROR", e);
117 return false;
118 }
119
120 return true;
121 }
122
verifyIntegrityCheck(std::string & newPassword,std::array<uint8_t,maxSeedSize> & seed,unsigned int mdLen,const EVP_MD * digestFunc)123 bool Password::verifyIntegrityCheck(
124 std::string& newPassword, std::array<uint8_t, maxSeedSize>& seed,
125 unsigned int mdLen, const EVP_MD* digestFunc)
126 {
127 mNewPwdHash.fill(0);
128
129 if (!PKCS5_PBKDF2_HMAC(reinterpret_cast<const char*>(newPassword.c_str()),
130 newPassword.length() + 1,
131 reinterpret_cast<const unsigned char*>(seed.data()),
132 seed.size(), iterValue, digestFunc, mdLen,
133 mNewPwdHash.data()))
134 {
135 lg2::error("Verify PKCS5_PBKDF2_HMAC Integrity Check failed");
136 return false;
137 }
138
139 return true;
140 }
141
verifyPassword(std::string userName,std::string currentPassword,std::string newPassword)142 void Password::verifyPassword(std::string userName, std::string currentPassword,
143 std::string newPassword)
144 {
145 if (fs::exists(seedFile.c_str()))
146 {
147 std::array<uint8_t, maxHashSize> orgUsrPwdHash;
148 std::array<uint8_t, maxHashSize> orgAdminPwdHash;
149 std::array<uint8_t, maxSeedSize> seed;
150 std::string hashAlgo = "";
151
152 if (getParam(orgUsrPwdHash, orgAdminPwdHash, seed, hashAlgo))
153 {
154 if (orgUsrPwdHash.empty() || orgAdminPwdHash.empty() ||
155 seed.empty() || hashAlgo.empty())
156 {
157 return;
158 }
159 }
160 else
161 {
162 throw InternalFailure();
163 }
164
165 if (userName == "AdminPassword")
166 {
167 if (!isMatch(orgAdminPwdHash, seed, currentPassword, hashAlgo))
168 {
169 throw InvalidCurrentPassword();
170 }
171 }
172 else
173 {
174 if (!isMatch(orgUsrPwdHash, seed, currentPassword, hashAlgo))
175 {
176 throw InvalidCurrentPassword();
177 }
178 }
179 if (hashAlgo == "SHA256")
180 {
181 if (!verifyIntegrityCheck(newPassword, seed, 32, EVP_sha256()))
182 {
183 throw InternalFailure();
184 }
185 }
186 if (hashAlgo == "SHA384")
187 {
188 if (!verifyIntegrityCheck(newPassword, seed, 48, EVP_sha384()))
189 {
190 throw InternalFailure();
191 }
192 }
193 return;
194 }
195 throw InternalFailure();
196 }
changePassword(std::string userName,std::string currentPassword,std::string newPassword)197 void Password::changePassword(std::string userName, std::string currentPassword,
198 std::string newPassword)
199 {
200 lg2::debug("BIOS config changePassword");
201 verifyPassword(userName, currentPassword, newPassword);
202
203 std::ifstream fs(seedFile.c_str());
204 nlohmann::json json = nullptr;
205
206 if (fs.is_open())
207 {
208 try
209 {
210 json = nlohmann::json::parse(fs, nullptr, false);
211 }
212 catch (const nlohmann::json::parse_error& e)
213 {
214 lg2::error("Failed to parse JSON file: {ERROR}", "ERROR", e);
215 throw InternalFailure();
216 }
217
218 if (json.is_discarded())
219 {
220 throw InternalFailure();
221 }
222 json["AdminPwdHash"] = mNewPwdHash;
223 json["IsAdminPwdChanged"] = true;
224
225 std::ofstream ofs(seedFile.c_str(), std::ios::out);
226 const auto& writeData = json.dump();
227 ofs << writeData;
228 ofs.close();
229 }
230 else
231 {
232 lg2::debug("Cannot open file stream");
233 throw InternalFailure();
234 }
235 }
Password(sdbusplus::asio::object_server & objectServer,std::shared_ptr<sdbusplus::asio::connection> & systemBus)236 Password::Password(sdbusplus::asio::object_server& objectServer,
237 std::shared_ptr<sdbusplus::asio::connection>& systemBus) :
238 sdbusplus::xyz::openbmc_project::BIOSConfig::server::Password(
239 *systemBus, objectPathPwd),
240 objServer(objectServer), systemBus(systemBus)
241 {
242 lg2::debug("BIOS config password is running");
243 try
244 {
245 fs::path biosDir(BIOS_PERSIST_PATH);
246 fs::create_directories(biosDir);
247 seedFile = biosDir / biosSeedFile;
248 }
249 catch (const fs::filesystem_error& e)
250 {
251 lg2::error("Failed to parse JSON file: {ERROR}", "ERROR", e);
252 throw InternalFailure();
253 }
254 }
255
256 } // namespace bios_config_pwd
257