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,std::string persistPath)236 Password::Password(sdbusplus::asio::object_server& objectServer,
237 std::shared_ptr<sdbusplus::asio::connection>& systemBus,
238 std::string persistPath) :
239 sdbusplus::xyz::openbmc_project::BIOSConfig::server::Password(
240 *systemBus, objectPathPwd),
241 objServer(objectServer), systemBus(systemBus)
242 {
243 lg2::debug("BIOS config password is running");
244 try
245 {
246 fs::path biosDir(persistPath);
247 fs::create_directories(biosDir);
248 seedFile = biosDir / biosSeedFile;
249 }
250 catch (const fs::filesystem_error& e)
251 {
252 lg2::error("Failed to parse JSON file: {ERROR}", "ERROR", e);
253 throw InternalFailure();
254 }
255 }
256
257 } // namespace bios_config_pwd
258