blob: 0e348cee77f607005e2d3fdd5ace8c180ff1ead5 [file] [log] [blame]
/*
// 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 <phosphor-logging/lg2.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;
bool Password::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)
{
std::vector<uint8_t> output(digestLen);
unsigned int hashLen = digestLen;
if (!PKCS5_PBKDF2_HMAC(reinterpret_cast<const char*>(rawData.c_str()),
rawData.length() + 1,
reinterpret_cast<const unsigned char*>(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<uint8_t, maxHashSize>& expected,
const std::array<uint8_t, maxSeedSize>& 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<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)
{
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<uint8_t, maxSeedSize>& seed,
unsigned int mdLen,
const EVP_MD* digestFunc)
{
mNewPwdHash.fill(0);
if (!PKCS5_PBKDF2_HMAC(reinterpret_cast<const char*>(newPassword.c_str()),
newPassword.length() + 1,
reinterpret_cast<const unsigned char*>(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<uint8_t, maxHashSize> orgUsrPwdHash;
std::array<uint8_t, maxHashSize> orgAdminPwdHash;
std::array<uint8_t, maxSeedSize> 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<sdbusplus::asio::connection>& 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