| /* |
| // 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 <boost/algorithm/hex.hpp> |
| #include <boost/asio.hpp> |
| #include <phosphor-logging/elog-errors.hpp> |
| #include <sdbusplus/asio/connection.hpp> |
| #include <sdbusplus/asio/object_server.hpp> |
| |
| #include <fstream> |
| #include <iostream> |
| |
| namespace bios_config_pwd |
| { |
| using namespace sdbusplus::xyz::openbmc_project::Common::Error; |
| using namespace sdbusplus::xyz::openbmc_project::BIOSConfig::Common::Error; |
| |
| uint8_t |
| Password::convertUnicode(const std::string& pwd, |
| std::array<uint16_t, maxPasswordLen>& unicodePwd) |
| { |
| if (pwd.size() > unicodePwd.size()) |
| { |
| phosphor::logging::log<phosphor::logging::level::DEBUG>( |
| "password size exceeds unicodePwd array size"); |
| throw InternalFailure(); |
| } |
| |
| for (std::string::size_type i = 0; i < pwd.size(); i++) |
| { |
| unicodePwd[i] = pwd[i]; |
| } |
| unicodePwd[pwd.size()] = 0; |
| |
| return (pwd.size() * sizeof(uint16_t) + sizeof(uint16_t)); |
| } |
| |
| bool Password::isMatch(const std::array<uint8_t, maxHashSize>& expected, |
| const std::array<uint8_t, maxSeedSize>& seed, |
| const std::string rawData, const std::string algo) |
| { |
| phosphor::logging::log<phosphor::logging::level::ERR>("isMatch"); |
| |
| if (algo == "SHA256") |
| { |
| std::vector<uint8_t> output(SHA256_DIGEST_LENGTH); |
| unsigned int hashLen = 0; |
| |
| std::array<uint16_t, maxPasswordLen> unicodePwd = {0}; |
| uint8_t unicodePwdLength = convertUnicode(rawData, unicodePwd); |
| |
| if (HMAC(EVP_sha256(), seed.data(), seed.size(), |
| reinterpret_cast<const unsigned char*>(unicodePwd.data()), |
| unicodePwdLength, output.data(), &hashLen) == NULL) |
| { |
| phosphor::logging::log<phosphor::logging::level::ERR>( |
| "Generate HMAC_SHA256 Integrity Check Value failed"); |
| throw InternalFailure(); |
| } |
| |
| int cmp; |
| cmp = std::memcmp(output.data(), expected.data(), |
| output.size() * sizeof(uint8_t)); |
| if (cmp == 0) |
| { |
| return true; |
| } |
| else |
| { |
| return false; |
| } |
| } |
| if (algo == "SHA384") |
| { |
| std::array<uint8_t, SHA384_DIGEST_LENGTH> output; |
| unsigned int hashLen = 0; |
| |
| std::array<uint16_t, maxPasswordLen> unicodePwd = {0}; |
| uint8_t unicodePwdLength = convertUnicode(rawData, unicodePwd); |
| |
| if (HMAC(EVP_sha384(), seed.data(), seed.size(), |
| reinterpret_cast<const unsigned char*>(unicodePwd.data()), |
| unicodePwdLength, output.data(), &hashLen) == NULL) |
| { |
| phosphor::logging::log<phosphor::logging::level::ERR>( |
| "Generate HMAC_SHA384 Integrity Check Value failed"); |
| throw InternalFailure(); |
| } |
| |
| int cmp; |
| cmp = std::memcmp(output.data(), expected.data(), |
| output.size() * sizeof(uint8_t)); |
| if (cmp == 0) |
| { |
| return true; |
| } |
| else |
| { |
| return false; |
| } |
| } |
| |
| return false; |
| } |
| |
| void Password::verifyPassword(std::string userName, std::string currentPassword, |
| std::string newPassword) |
| { |
| if (fs::exists(seedFile.c_str())) |
| { |
| std::array<uint8_t, maxHashSize> orgUsrPwdHash; |
| std::array<uint8_t, maxHashSize> orgAdminPwdHash; |
| std::array<uint8_t, maxSeedSize> 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) |
| { |
| phosphor::logging::log<phosphor::logging::level::ERR>( |
| e.what()); |
| throw InternalFailure(); |
| } |
| |
| if (json.is_discarded()) |
| { |
| return; |
| } |
| orgUsrPwdHash = json["UserPwdHash"]; |
| orgAdminPwdHash = json["AdminPwdHash"]; |
| seed = json["Seed"]; |
| hashAlgo = json["HashAlgo"]; |
| } |
| else |
| { |
| return; |
| } |
| } |
| catch (nlohmann::detail::exception& e) |
| { |
| phosphor::logging::log<phosphor::logging::level::ERR>(e.what()); |
| 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") |
| { |
| std::array<uint16_t, maxPasswordLen> unicodePwd; |
| uint8_t unicodePwdlength = 0; |
| unsigned int mdLen = 0; |
| unicodePwdlength = convertUnicode(newPassword, unicodePwd); |
| mNewPwdHash.fill(0); |
| |
| if (HMAC(EVP_sha256(), seed.data(), seed.size(), |
| reinterpret_cast<const unsigned char*>(unicodePwd.data()), |
| unicodePwdlength, mNewPwdHash.data(), &mdLen) == NULL) |
| { |
| throw InternalFailure(); |
| } |
| } |
| if (hashAlgo == "SHA384") |
| { |
| std::array<uint16_t, maxPasswordLen> unicodePwd; |
| uint8_t unicodePwdlength = 0; |
| unsigned int mdLen = 0; |
| unicodePwdlength = convertUnicode(newPassword, unicodePwd); |
| mNewPwdHash.fill(0); |
| |
| if (HMAC(EVP_sha384(), seed.data(), seed.size(), |
| reinterpret_cast<const unsigned char*>(unicodePwd.data()), |
| unicodePwdlength, mNewPwdHash.data(), &mdLen) == NULL) |
| { |
| throw InternalFailure(); |
| } |
| } |
| return; |
| } |
| throw InternalFailure(); |
| } |
| void Password::changePassword(std::string userName, std::string currentPassword, |
| std::string newPassword) |
| { |
| phosphor::logging::log<phosphor::logging::level::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) |
| { |
| phosphor::logging::log<phosphor::logging::level::ERR>(e.what()); |
| 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 |
| { |
| phosphor::logging::log<phosphor::logging::level::DEBUG>( |
| "Cannot open file stream"); |
| throw InternalFailure(); |
| } |
| } |
| Password::Password(sdbusplus::asio::object_server& objectServer, |
| std::shared_ptr<sdbusplus::asio::connection>& systemBus) : |
| sdbusplus::xyz::openbmc_project::BIOSConfig::server::Password( |
| *systemBus, objectPathPwd), |
| objServer(objectServer), systemBus(systemBus) |
| { |
| phosphor::logging::log<phosphor::logging::level::DEBUG>( |
| "BIOS config password is runing"); |
| try |
| { |
| fs::path biosDir(BIOS_PERSIST_PATH); |
| fs::create_directories(biosDir); |
| seedFile = biosDir / biosSeedFile; |
| } |
| catch (const fs::filesystem_error& e) |
| { |
| phosphor::logging::log<phosphor::logging::level::ERR>(e.what()); |
| throw InternalFailure(); |
| } |
| } |
| |
| } // namespace bios_config_pwd |
| |
| int main() |
| { |
| boost::asio::io_service io; |
| auto systemBus = std::make_shared<sdbusplus::asio::connection>(io); |
| |
| systemBus->request_name(bios_config_pwd::servicePwd); |
| sdbusplus::asio::object_server objectServer(systemBus); |
| bios_config_pwd::Password password(objectServer, systemBus); |
| |
| io.run(); |
| return 0; |
| } |