/* // Copyright (c) 2020 Intel Corporation // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. */ #include "password.hpp" #include "xyz/openbmc_project/BIOSConfig/Common/error.hpp" #include "xyz/openbmc_project/Common/error.hpp" #include #include #include #include #include #include #include #include namespace bios_config_pwd { using namespace sdbusplus::xyz::openbmc_project::Common::Error; using namespace sdbusplus::xyz::openbmc_project::BIOSConfig::Common::Error; bool Password::compareDigest(const EVP_MD* digestFunc, size_t digestLen, const std::array& expected, const std::array& seed, const std::string& rawData) { std::vector output(digestLen); unsigned int hashLen = digestLen; if (!PKCS5_PBKDF2_HMAC(reinterpret_cast(rawData.c_str()), rawData.length() + 1, reinterpret_cast(seed.data()), seed.size(), iterValue, digestFunc, hashLen, output.data())) { lg2::error("Generate PKCS5_PBKDF2_HMAC Integrity Check Value failed"); throw InternalFailure(); } if (std::memcmp(output.data(), expected.data(), output.size() * sizeof(uint8_t)) == 0) { return true; } return false; } bool Password::isMatch(const std::array& expected, const std::array& seed, const std::string& rawData, const std::string& algo) { lg2::error("isMatch"); if (algo == "SHA256") { return compareDigest(EVP_sha256(), SHA256_DIGEST_LENGTH, expected, seed, rawData); } if (algo == "SHA384") { return compareDigest(EVP_sha384(), SHA384_DIGEST_LENGTH, expected, seed, rawData); } return false; } bool Password::getParam(std::array& orgUsrPwdHash, std::array& orgAdminPwdHash, std::array& seed, std::string& hashAlgo) { try { nlohmann::json json = nullptr; std::ifstream ifs(seedFile.c_str()); if (ifs.is_open()) { try { json = nlohmann::json::parse(ifs, nullptr, false); } catch (const nlohmann::json::parse_error& e) { lg2::error("Failed to parse JSON file: {ERROR}", "ERROR", e); return false; } if (!json.is_discarded()) { orgUsrPwdHash = json["UserPwdHash"]; orgAdminPwdHash = json["AdminPwdHash"]; seed = json["Seed"]; hashAlgo = json["HashAlgo"]; } } } catch (nlohmann::detail::exception& e) { lg2::error("Failed to parse JSON file: {ERROR}", "ERROR", e); return false; } return true; } bool Password::verifyIntegrityCheck(std::string& newPassword, std::array& seed, unsigned int mdLen, const EVP_MD* digestFunc) { mNewPwdHash.fill(0); if (!PKCS5_PBKDF2_HMAC(reinterpret_cast(newPassword.c_str()), newPassword.length() + 1, reinterpret_cast(seed.data()), seed.size(), iterValue, digestFunc, mdLen, mNewPwdHash.data())) { lg2::error("Verify PKCS5_PBKDF2_HMAC Integrity Check failed"); return false; } return true; } void Password::verifyPassword(std::string userName, std::string currentPassword, std::string newPassword) { if (fs::exists(seedFile.c_str())) { std::array orgUsrPwdHash; std::array orgAdminPwdHash; std::array seed; std::string hashAlgo = ""; if (getParam(orgUsrPwdHash, orgAdminPwdHash, seed, hashAlgo)) { if (orgUsrPwdHash.empty() || orgAdminPwdHash.empty() || seed.empty() || hashAlgo.empty()) { return; } } else { throw InternalFailure(); } if (userName == "AdminPassword") { if (!isMatch(orgAdminPwdHash, seed, currentPassword, hashAlgo)) { throw InvalidCurrentPassword(); } } else { if (!isMatch(orgUsrPwdHash, seed, currentPassword, hashAlgo)) { throw InvalidCurrentPassword(); } } if (hashAlgo == "SHA256") { if (!verifyIntegrityCheck(newPassword, seed, 32, EVP_sha256())) { throw InternalFailure(); } } if (hashAlgo == "SHA384") { if (!verifyIntegrityCheck(newPassword, seed, 48, EVP_sha384())) { throw InternalFailure(); } } return; } throw InternalFailure(); } void Password::changePassword(std::string userName, std::string currentPassword, std::string newPassword) { lg2::debug("BIOS config changePassword"); verifyPassword(userName, currentPassword, newPassword); std::ifstream fs(seedFile.c_str()); nlohmann::json json = nullptr; if (fs.is_open()) { try { json = nlohmann::json::parse(fs, nullptr, false); } catch (const nlohmann::json::parse_error& e) { lg2::error("Failed to parse JSON file: {ERROR}", "ERROR", e); throw InternalFailure(); } if (json.is_discarded()) { throw InternalFailure(); } json["AdminPwdHash"] = mNewPwdHash; json["IsAdminPwdChanged"] = true; std::ofstream ofs(seedFile.c_str(), std::ios::out); const auto& writeData = json.dump(); ofs << writeData; ofs.close(); } else { lg2::debug("Cannot open file stream"); throw InternalFailure(); } } Password::Password(sdbusplus::asio::object_server& objectServer, std::shared_ptr& systemBus) : sdbusplus::xyz::openbmc_project::BIOSConfig::server::Password( *systemBus, objectPathPwd), objServer(objectServer), systemBus(systemBus) { lg2::debug("BIOS config password is running"); try { fs::path biosDir(BIOS_PERSIST_PATH); fs::create_directories(biosDir); seedFile = biosDir / biosSeedFile; } catch (const fs::filesystem_error& e) { lg2::error("Failed to parse JSON file: {ERROR}", "ERROR", e); throw InternalFailure(); } } } // namespace bios_config_pwd