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