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