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 
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 
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 
84*8c22d07bSyes bool Password::verifyIntegrityCheck(std::string& newPassword,
85*8c22d07bSyes                                     std::array<uint8_t, maxSeedSize>& seed,
86*8c22d07bSyes                                     unsigned int mdLen,
87*8c22d07bSyes                                     const EVP_MD* digestFunc)
88*8c22d07bSyes {
89*8c22d07bSyes     mNewPwdHash.fill(0);
90*8c22d07bSyes 
91*8c22d07bSyes     if (!PKCS5_PBKDF2_HMAC(reinterpret_cast<const char*>(newPassword.c_str()),
92*8c22d07bSyes                            newPassword.length() + 1,
93*8c22d07bSyes                            reinterpret_cast<const unsigned char*>(seed.data()),
94*8c22d07bSyes                            seed.size(), iterValue, digestFunc, mdLen,
95*8c22d07bSyes                            mNewPwdHash.data()))
96*8c22d07bSyes     {
97*8c22d07bSyes         lg2::error("Verify PKCS5_PBKDF2_HMAC Integrity Check failed");
98*8c22d07bSyes         return false;
99*8c22d07bSyes     }
100*8c22d07bSyes 
101*8c22d07bSyes     return true;
102*8c22d07bSyes }
103*8c22d07bSyes 
1048f706213SKuiying Wang void Password::verifyPassword(std::string userName, std::string currentPassword,
1058f706213SKuiying Wang                               std::string newPassword)
1068f706213SKuiying Wang {
1078f706213SKuiying Wang     if (fs::exists(seedFile.c_str()))
1088f706213SKuiying Wang     {
10996e72ec5SAyushi Smriti         std::array<uint8_t, maxHashSize> orgUsrPwdHash;
11096e72ec5SAyushi Smriti         std::array<uint8_t, maxHashSize> orgAdminPwdHash;
11196e72ec5SAyushi Smriti         std::array<uint8_t, maxSeedSize> seed;
1128f706213SKuiying Wang         std::string hashAlgo = "";
1138f706213SKuiying Wang         try
1148f706213SKuiying Wang         {
11596e72ec5SAyushi Smriti             nlohmann::json json = nullptr;
1168f706213SKuiying Wang             std::ifstream ifs(seedFile.c_str());
11796e72ec5SAyushi Smriti             if (ifs.is_open())
11896e72ec5SAyushi Smriti             {
11996e72ec5SAyushi Smriti                 try
12096e72ec5SAyushi Smriti                 {
12196e72ec5SAyushi Smriti                     json = nlohmann::json::parse(ifs, nullptr, false);
12296e72ec5SAyushi Smriti                 }
12396e72ec5SAyushi Smriti                 catch (const nlohmann::json::parse_error& e)
12496e72ec5SAyushi Smriti                 {
125567a3cf2SGeorge Liu                     lg2::error("Failed to parse JSON file: {ERROR}", "ERROR",
126567a3cf2SGeorge Liu                                e);
12796e72ec5SAyushi Smriti                     throw InternalFailure();
12896e72ec5SAyushi Smriti                 }
12996e72ec5SAyushi Smriti 
13096e72ec5SAyushi Smriti                 if (json.is_discarded())
13196e72ec5SAyushi Smriti                 {
13296e72ec5SAyushi Smriti                     return;
13396e72ec5SAyushi Smriti                 }
13496e72ec5SAyushi Smriti                 orgUsrPwdHash = json["UserPwdHash"];
13596e72ec5SAyushi Smriti                 orgAdminPwdHash = json["AdminPwdHash"];
1368f706213SKuiying Wang                 seed = json["Seed"];
1378f706213SKuiying Wang                 hashAlgo = json["HashAlgo"];
1388f706213SKuiying Wang             }
13996e72ec5SAyushi Smriti             else
14096e72ec5SAyushi Smriti             {
14196e72ec5SAyushi Smriti                 return;
14296e72ec5SAyushi Smriti             }
14396e72ec5SAyushi Smriti         }
1448f706213SKuiying Wang         catch (nlohmann::detail::exception& e)
1458f706213SKuiying Wang         {
146567a3cf2SGeorge Liu             lg2::error("Failed to parse JSON file: {ERROR}", "ERROR", e);
1478f706213SKuiying Wang             throw InternalFailure();
1488f706213SKuiying Wang         }
14996e72ec5SAyushi Smriti         if (userName == "AdminPassword")
1508f706213SKuiying Wang         {
15196e72ec5SAyushi Smriti             if (!isMatch(orgAdminPwdHash, seed, currentPassword, hashAlgo))
1528f706213SKuiying Wang             {
1538f706213SKuiying Wang                 throw InvalidCurrentPassword();
1548f706213SKuiying Wang             }
1558f706213SKuiying Wang         }
1568f706213SKuiying Wang         else
1578f706213SKuiying Wang         {
15896e72ec5SAyushi Smriti             if (!isMatch(orgUsrPwdHash, seed, currentPassword, hashAlgo))
1598f706213SKuiying Wang             {
1608f706213SKuiying Wang                 throw InvalidCurrentPassword();
1618f706213SKuiying Wang             }
1628f706213SKuiying Wang         }
16396e72ec5SAyushi Smriti         if (hashAlgo == "SHA256")
1648f706213SKuiying Wang         {
165*8c22d07bSyes             if (!verifyIntegrityCheck(newPassword, seed, 32, EVP_sha256()))
1668f706213SKuiying Wang             {
1678f706213SKuiying Wang                 throw InternalFailure();
1688f706213SKuiying Wang             }
1698f706213SKuiying Wang         }
17096e72ec5SAyushi Smriti         if (hashAlgo == "SHA384")
17196e72ec5SAyushi Smriti         {
172*8c22d07bSyes             if (!verifyIntegrityCheck(newPassword, seed, 48, EVP_sha384()))
17396e72ec5SAyushi Smriti             {
17496e72ec5SAyushi Smriti                 throw InternalFailure();
17596e72ec5SAyushi Smriti             }
17696e72ec5SAyushi Smriti         }
1778f706213SKuiying Wang         return;
1788f706213SKuiying Wang     }
1798f706213SKuiying Wang     throw InternalFailure();
1808f706213SKuiying Wang }
1818f706213SKuiying Wang void Password::changePassword(std::string userName, std::string currentPassword,
1828f706213SKuiying Wang                               std::string newPassword)
1838f706213SKuiying Wang {
184567a3cf2SGeorge Liu     lg2::debug("BIOS config changePassword");
1858f706213SKuiying Wang     verifyPassword(userName, currentPassword, newPassword);
1868f706213SKuiying Wang 
18796e72ec5SAyushi Smriti     std::ifstream fs(seedFile.c_str());
18896e72ec5SAyushi Smriti     nlohmann::json json = nullptr;
18996e72ec5SAyushi Smriti 
19096e72ec5SAyushi Smriti     if (fs.is_open())
19196e72ec5SAyushi Smriti     {
1928f706213SKuiying Wang         try
1938f706213SKuiying Wang         {
19496e72ec5SAyushi Smriti             json = nlohmann::json::parse(fs, nullptr, false);
19596e72ec5SAyushi Smriti         }
19696e72ec5SAyushi Smriti         catch (const nlohmann::json::parse_error& e)
1978f706213SKuiying Wang         {
198567a3cf2SGeorge Liu             lg2::error("Failed to parse JSON file: {ERROR}", "ERROR", e);
19996e72ec5SAyushi Smriti             throw InternalFailure();
20096e72ec5SAyushi Smriti         }
20196e72ec5SAyushi Smriti 
20296e72ec5SAyushi Smriti         if (json.is_discarded())
20396e72ec5SAyushi Smriti         {
20496e72ec5SAyushi Smriti             throw InternalFailure();
20596e72ec5SAyushi Smriti         }
20696e72ec5SAyushi Smriti         json["AdminPwdHash"] = mNewPwdHash;
20796e72ec5SAyushi Smriti         json["IsAdminPwdChanged"] = true;
20896e72ec5SAyushi Smriti 
20996e72ec5SAyushi Smriti         std::ofstream ofs(seedFile.c_str(), std::ios::out);
21096e72ec5SAyushi Smriti         const auto& writeData = json.dump();
21196e72ec5SAyushi Smriti         ofs << writeData;
21296e72ec5SAyushi Smriti         ofs.close();
2138f706213SKuiying Wang     }
2148f706213SKuiying Wang     else
2158f706213SKuiying Wang     {
216567a3cf2SGeorge Liu         lg2::debug("Cannot open file stream");
2178f706213SKuiying Wang         throw InternalFailure();
2188f706213SKuiying Wang     }
2198f706213SKuiying Wang }
2208f706213SKuiying Wang Password::Password(sdbusplus::asio::object_server& objectServer,
2218f706213SKuiying Wang                    std::shared_ptr<sdbusplus::asio::connection>& systemBus) :
2228f706213SKuiying Wang     sdbusplus::xyz::openbmc_project::BIOSConfig::server::Password(
2238f706213SKuiying Wang         *systemBus, objectPathPwd),
2248f706213SKuiying Wang     objServer(objectServer), systemBus(systemBus)
2258f706213SKuiying Wang {
2268de46ffbSyes     lg2::debug("BIOS config password is running");
2278f706213SKuiying Wang     try
2288f706213SKuiying Wang     {
2298f706213SKuiying Wang         fs::path biosDir(BIOS_PERSIST_PATH);
2308f706213SKuiying Wang         fs::create_directories(biosDir);
2318f706213SKuiying Wang         seedFile = biosDir / biosSeedFile;
2328f706213SKuiying Wang     }
2338f706213SKuiying Wang     catch (const fs::filesystem_error& e)
2348f706213SKuiying Wang     {
235567a3cf2SGeorge Liu         lg2::error("Failed to parse JSON file: {ERROR}", "ERROR", e);
2368f706213SKuiying Wang         throw InternalFailure();
2378f706213SKuiying Wang     }
2388f706213SKuiying Wang }
2398f706213SKuiying Wang 
2408f706213SKuiying Wang } // namespace bios_config_pwd
241