18f706213SKuiying Wang /*
28f706213SKuiying Wang // Copyright (c) 2020 Intel Corporation
38f706213SKuiying Wang //
48f706213SKuiying Wang // Licensed under the Apache License, Version 2.0 (the "License");
58f706213SKuiying Wang // you may not use this file except in compliance with the License.
68f706213SKuiying Wang // You may obtain a copy of the License at
78f706213SKuiying Wang //
88f706213SKuiying Wang // http://www.apache.org/licenses/LICENSE-2.0
98f706213SKuiying Wang //
108f706213SKuiying Wang // Unless required by applicable law or agreed to in writing, software
118f706213SKuiying Wang // distributed under the License is distributed on an "AS IS" BASIS,
128f706213SKuiying Wang // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
138f706213SKuiying Wang // See the License for the specific language governing permissions and
148f706213SKuiying Wang // limitations under the License.
158f706213SKuiying Wang */
168f706213SKuiying Wang #include "password.hpp"
178f706213SKuiying Wang
188f706213SKuiying Wang #include "xyz/openbmc_project/BIOSConfig/Common/error.hpp"
198f706213SKuiying Wang #include "xyz/openbmc_project/Common/error.hpp"
208f706213SKuiying Wang
218f706213SKuiying Wang #include <boost/algorithm/hex.hpp>
228f706213SKuiying Wang #include <boost/asio.hpp>
238f706213SKuiying Wang #include <phosphor-logging/elog-errors.hpp>
24567a3cf2SGeorge Liu #include <phosphor-logging/lg2.hpp>
258f706213SKuiying Wang #include <sdbusplus/asio/connection.hpp>
268f706213SKuiying Wang #include <sdbusplus/asio/object_server.hpp>
278f706213SKuiying Wang
288f706213SKuiying Wang #include <fstream>
298f706213SKuiying Wang #include <iostream>
308f706213SKuiying Wang
318f706213SKuiying Wang namespace bios_config_pwd
328f706213SKuiying Wang {
338f706213SKuiying Wang using namespace sdbusplus::xyz::openbmc_project::Common::Error;
348f706213SKuiying Wang using namespace sdbusplus::xyz::openbmc_project::BIOSConfig::Common::Error;
358f706213SKuiying Wang
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)36d0f034a2Syes bool Password::compareDigest(const EVP_MD* digestFunc, size_t digestLen,
37d0f034a2Syes const std::array<uint8_t, maxHashSize>& expected,
38d0f034a2Syes const std::array<uint8_t, maxSeedSize>& seed,
39d0f034a2Syes const std::string& rawData)
40d0f034a2Syes {
41d0f034a2Syes std::vector<uint8_t> output(digestLen);
42d0f034a2Syes unsigned int hashLen = digestLen;
43d0f034a2Syes
44d0f034a2Syes if (!PKCS5_PBKDF2_HMAC(reinterpret_cast<const char*>(rawData.c_str()),
45d0f034a2Syes rawData.length() + 1,
46d0f034a2Syes reinterpret_cast<const unsigned char*>(seed.data()),
47d0f034a2Syes seed.size(), iterValue, digestFunc, hashLen,
48d0f034a2Syes output.data()))
49d0f034a2Syes {
50d0f034a2Syes lg2::error("Generate PKCS5_PBKDF2_HMAC Integrity Check Value failed");
51d0f034a2Syes throw InternalFailure();
52d0f034a2Syes }
53d0f034a2Syes
54d0f034a2Syes if (std::memcmp(output.data(), expected.data(),
55d0f034a2Syes output.size() * sizeof(uint8_t)) == 0)
56d0f034a2Syes {
57d0f034a2Syes return true;
58d0f034a2Syes }
59d0f034a2Syes
60d0f034a2Syes return false;
61d0f034a2Syes }
62d0f034a2Syes
isMatch(const std::array<uint8_t,maxHashSize> & expected,const std::array<uint8_t,maxSeedSize> & seed,const std::string & rawData,const std::string & algo)6396e72ec5SAyushi Smriti bool Password::isMatch(const std::array<uint8_t, maxHashSize>& expected,
6496e72ec5SAyushi Smriti const std::array<uint8_t, maxSeedSize>& seed,
65616f9228SGeorge Liu const std::string& rawData, const std::string& algo)
668f706213SKuiying Wang {
67567a3cf2SGeorge Liu lg2::error("isMatch");
6896e72ec5SAyushi Smriti
6996e72ec5SAyushi Smriti if (algo == "SHA256")
708f706213SKuiying Wang {
71d0f034a2Syes return compareDigest(EVP_sha256(), SHA256_DIGEST_LENGTH, expected, seed,
72d0f034a2Syes rawData);
738f706213SKuiying Wang }
7496e72ec5SAyushi Smriti
7596e72ec5SAyushi Smriti if (algo == "SHA384")
7696e72ec5SAyushi Smriti {
77d0f034a2Syes return compareDigest(EVP_sha384(), SHA384_DIGEST_LENGTH, expected, seed,
78d0f034a2Syes rawData);
7996e72ec5SAyushi Smriti }
8096e72ec5SAyushi Smriti
818f706213SKuiying Wang return false;
828f706213SKuiying Wang }
838f706213SKuiying Wang
getParam(std::array<uint8_t,maxHashSize> & orgUsrPwdHash,std::array<uint8_t,maxHashSize> & orgAdminPwdHash,std::array<uint8_t,maxSeedSize> & seed,std::string & hashAlgo)84b3f7a79eSSmriti-Ayushi bool Password::getParam(std::array<uint8_t, maxHashSize>& orgUsrPwdHash,
85b3f7a79eSSmriti-Ayushi std::array<uint8_t, maxHashSize>& orgAdminPwdHash,
86b3f7a79eSSmriti-Ayushi std::array<uint8_t, maxSeedSize>& seed,
87b3f7a79eSSmriti-Ayushi std::string& hashAlgo)
88b3f7a79eSSmriti-Ayushi {
89b3f7a79eSSmriti-Ayushi try
90b3f7a79eSSmriti-Ayushi {
91b3f7a79eSSmriti-Ayushi nlohmann::json json = nullptr;
92b3f7a79eSSmriti-Ayushi std::ifstream ifs(seedFile.c_str());
93b3f7a79eSSmriti-Ayushi if (ifs.is_open())
94b3f7a79eSSmriti-Ayushi {
95b3f7a79eSSmriti-Ayushi try
96b3f7a79eSSmriti-Ayushi {
97b3f7a79eSSmriti-Ayushi json = nlohmann::json::parse(ifs, nullptr, false);
98b3f7a79eSSmriti-Ayushi }
99b3f7a79eSSmriti-Ayushi catch (const nlohmann::json::parse_error& e)
100b3f7a79eSSmriti-Ayushi {
101b3f7a79eSSmriti-Ayushi lg2::error("Failed to parse JSON file: {ERROR}", "ERROR", e);
102b3f7a79eSSmriti-Ayushi return false;
103b3f7a79eSSmriti-Ayushi }
104b3f7a79eSSmriti-Ayushi
105b3f7a79eSSmriti-Ayushi if (!json.is_discarded())
106b3f7a79eSSmriti-Ayushi {
107b3f7a79eSSmriti-Ayushi orgUsrPwdHash = json["UserPwdHash"];
108b3f7a79eSSmriti-Ayushi orgAdminPwdHash = json["AdminPwdHash"];
109b3f7a79eSSmriti-Ayushi seed = json["Seed"];
110b3f7a79eSSmriti-Ayushi hashAlgo = json["HashAlgo"];
111b3f7a79eSSmriti-Ayushi }
112b3f7a79eSSmriti-Ayushi }
113b3f7a79eSSmriti-Ayushi }
114b3f7a79eSSmriti-Ayushi catch (nlohmann::detail::exception& e)
115b3f7a79eSSmriti-Ayushi {
116b3f7a79eSSmriti-Ayushi lg2::error("Failed to parse JSON file: {ERROR}", "ERROR", e);
117b3f7a79eSSmriti-Ayushi return false;
118b3f7a79eSSmriti-Ayushi }
119b3f7a79eSSmriti-Ayushi
120b3f7a79eSSmriti-Ayushi return true;
121b3f7a79eSSmriti-Ayushi }
122b3f7a79eSSmriti-Ayushi
verifyIntegrityCheck(std::string & newPassword,std::array<uint8_t,maxSeedSize> & seed,unsigned int mdLen,const EVP_MD * digestFunc)123*ea6a65f0SPatrick Williams bool Password::verifyIntegrityCheck(
124*ea6a65f0SPatrick Williams std::string& newPassword, std::array<uint8_t, maxSeedSize>& seed,
125*ea6a65f0SPatrick Williams unsigned int mdLen, const EVP_MD* digestFunc)
1268c22d07bSyes {
1278c22d07bSyes mNewPwdHash.fill(0);
1288c22d07bSyes
1298c22d07bSyes if (!PKCS5_PBKDF2_HMAC(reinterpret_cast<const char*>(newPassword.c_str()),
1308c22d07bSyes newPassword.length() + 1,
1318c22d07bSyes reinterpret_cast<const unsigned char*>(seed.data()),
1328c22d07bSyes seed.size(), iterValue, digestFunc, mdLen,
1338c22d07bSyes mNewPwdHash.data()))
1348c22d07bSyes {
1358c22d07bSyes lg2::error("Verify PKCS5_PBKDF2_HMAC Integrity Check failed");
1368c22d07bSyes return false;
1378c22d07bSyes }
1388c22d07bSyes
1398c22d07bSyes return true;
1408c22d07bSyes }
1418c22d07bSyes
verifyPassword(std::string userName,std::string currentPassword,std::string newPassword)1428f706213SKuiying Wang void Password::verifyPassword(std::string userName, std::string currentPassword,
1438f706213SKuiying Wang std::string newPassword)
1448f706213SKuiying Wang {
1458f706213SKuiying Wang if (fs::exists(seedFile.c_str()))
1468f706213SKuiying Wang {
14796e72ec5SAyushi Smriti std::array<uint8_t, maxHashSize> orgUsrPwdHash;
14896e72ec5SAyushi Smriti std::array<uint8_t, maxHashSize> orgAdminPwdHash;
14996e72ec5SAyushi Smriti std::array<uint8_t, maxSeedSize> seed;
1508f706213SKuiying Wang std::string hashAlgo = "";
15196e72ec5SAyushi Smriti
152b3f7a79eSSmriti-Ayushi if (getParam(orgUsrPwdHash, orgAdminPwdHash, seed, hashAlgo))
153b3f7a79eSSmriti-Ayushi {
154b3f7a79eSSmriti-Ayushi if (orgUsrPwdHash.empty() || orgAdminPwdHash.empty() ||
155b3f7a79eSSmriti-Ayushi seed.empty() || hashAlgo.empty())
15696e72ec5SAyushi Smriti {
15796e72ec5SAyushi Smriti return;
15896e72ec5SAyushi Smriti }
1598f706213SKuiying Wang }
16096e72ec5SAyushi Smriti else
16196e72ec5SAyushi Smriti {
1628f706213SKuiying Wang throw InternalFailure();
1638f706213SKuiying Wang }
164b3f7a79eSSmriti-Ayushi
16596e72ec5SAyushi Smriti if (userName == "AdminPassword")
1668f706213SKuiying Wang {
16796e72ec5SAyushi Smriti if (!isMatch(orgAdminPwdHash, seed, currentPassword, hashAlgo))
1688f706213SKuiying Wang {
1698f706213SKuiying Wang throw InvalidCurrentPassword();
1708f706213SKuiying Wang }
1718f706213SKuiying Wang }
1728f706213SKuiying Wang else
1738f706213SKuiying Wang {
17496e72ec5SAyushi Smriti if (!isMatch(orgUsrPwdHash, seed, currentPassword, hashAlgo))
1758f706213SKuiying Wang {
1768f706213SKuiying Wang throw InvalidCurrentPassword();
1778f706213SKuiying Wang }
1788f706213SKuiying Wang }
17996e72ec5SAyushi Smriti if (hashAlgo == "SHA256")
1808f706213SKuiying Wang {
1818c22d07bSyes if (!verifyIntegrityCheck(newPassword, seed, 32, EVP_sha256()))
1828f706213SKuiying Wang {
1838f706213SKuiying Wang throw InternalFailure();
1848f706213SKuiying Wang }
1858f706213SKuiying Wang }
18696e72ec5SAyushi Smriti if (hashAlgo == "SHA384")
18796e72ec5SAyushi Smriti {
1888c22d07bSyes if (!verifyIntegrityCheck(newPassword, seed, 48, EVP_sha384()))
18996e72ec5SAyushi Smriti {
19096e72ec5SAyushi Smriti throw InternalFailure();
19196e72ec5SAyushi Smriti }
19296e72ec5SAyushi Smriti }
1938f706213SKuiying Wang return;
1948f706213SKuiying Wang }
1958f706213SKuiying Wang throw InternalFailure();
1968f706213SKuiying Wang }
changePassword(std::string userName,std::string currentPassword,std::string newPassword)1978f706213SKuiying Wang void Password::changePassword(std::string userName, std::string currentPassword,
1988f706213SKuiying Wang std::string newPassword)
1998f706213SKuiying Wang {
200567a3cf2SGeorge Liu lg2::debug("BIOS config changePassword");
2018f706213SKuiying Wang verifyPassword(userName, currentPassword, newPassword);
2028f706213SKuiying Wang
20396e72ec5SAyushi Smriti std::ifstream fs(seedFile.c_str());
20496e72ec5SAyushi Smriti nlohmann::json json = nullptr;
20596e72ec5SAyushi Smriti
20696e72ec5SAyushi Smriti if (fs.is_open())
20796e72ec5SAyushi Smriti {
2088f706213SKuiying Wang try
2098f706213SKuiying Wang {
21096e72ec5SAyushi Smriti json = nlohmann::json::parse(fs, nullptr, false);
21196e72ec5SAyushi Smriti }
21296e72ec5SAyushi Smriti catch (const nlohmann::json::parse_error& e)
2138f706213SKuiying Wang {
214567a3cf2SGeorge Liu lg2::error("Failed to parse JSON file: {ERROR}", "ERROR", e);
21596e72ec5SAyushi Smriti throw InternalFailure();
21696e72ec5SAyushi Smriti }
21796e72ec5SAyushi Smriti
21896e72ec5SAyushi Smriti if (json.is_discarded())
21996e72ec5SAyushi Smriti {
22096e72ec5SAyushi Smriti throw InternalFailure();
22196e72ec5SAyushi Smriti }
22296e72ec5SAyushi Smriti json["AdminPwdHash"] = mNewPwdHash;
22396e72ec5SAyushi Smriti json["IsAdminPwdChanged"] = true;
22496e72ec5SAyushi Smriti
22596e72ec5SAyushi Smriti std::ofstream ofs(seedFile.c_str(), std::ios::out);
22696e72ec5SAyushi Smriti const auto& writeData = json.dump();
22796e72ec5SAyushi Smriti ofs << writeData;
22896e72ec5SAyushi Smriti ofs.close();
2298f706213SKuiying Wang }
2308f706213SKuiying Wang else
2318f706213SKuiying Wang {
232567a3cf2SGeorge Liu lg2::debug("Cannot open file stream");
2338f706213SKuiying Wang throw InternalFailure();
2348f706213SKuiying Wang }
2358f706213SKuiying Wang }
Password(sdbusplus::asio::object_server & objectServer,std::shared_ptr<sdbusplus::asio::connection> & systemBus)2368f706213SKuiying Wang Password::Password(sdbusplus::asio::object_server& objectServer,
2378f706213SKuiying Wang std::shared_ptr<sdbusplus::asio::connection>& systemBus) :
2388f706213SKuiying Wang sdbusplus::xyz::openbmc_project::BIOSConfig::server::Password(
2398f706213SKuiying Wang *systemBus, objectPathPwd),
2408f706213SKuiying Wang objServer(objectServer), systemBus(systemBus)
2418f706213SKuiying Wang {
2428de46ffbSyes lg2::debug("BIOS config password is running");
2438f706213SKuiying Wang try
2448f706213SKuiying Wang {
2458f706213SKuiying Wang fs::path biosDir(BIOS_PERSIST_PATH);
2468f706213SKuiying Wang fs::create_directories(biosDir);
2478f706213SKuiying Wang seedFile = biosDir / biosSeedFile;
2488f706213SKuiying Wang }
2498f706213SKuiying Wang catch (const fs::filesystem_error& e)
2508f706213SKuiying Wang {
251567a3cf2SGeorge Liu lg2::error("Failed to parse JSON file: {ERROR}", "ERROR", e);
2528f706213SKuiying Wang throw InternalFailure();
2538f706213SKuiying Wang }
2548f706213SKuiying Wang }
2558f706213SKuiying Wang
2568f706213SKuiying Wang } // namespace bios_config_pwd
257