oob bios config password and bios reset
Tested:
1. Bios reset flag can be modified throw redfish
POST https://IP_ADDR/redfish/v1/Systems/system/Bios/Actions/Bios.ResetBios
{
"ResetFlag": "Factory"
}
root@intel-obmc:~# busctl call xyz.openbmc_project.BIOSConfigManager /xyz/openbmc_project/bios_config/manager org.freedesktop.DBus.Properties Get ss xyz.openbmc_project.BIOSConfig.Manager ResetBIOSSettings
v s "xyz.openbmc_project.BIOSConfig.Manager.ResetFlag.FactoryDefaults"
2. Bios change password:
root@intel-obmc:~# cat /var/lib/bios-settings-manager/seedData
{
"UserPwdHash": "08D91157785366CDC3AA64D87E5E3C621EDAB13E26B6E484397EBA5E459E54C567BF5B1FFB36A43B6142B18F8D642E9D",
"AdminPwdHash": "08D91157785366CDC3AA64D87E5E3C621EDAB13E26B6E484397EBA5E459E54C567BF5B1FFB36A43B6142B18F8D642E9D",
"Seed": "123456",
"HashAlgo": "SHA384"
}
POST https://IP_ADDR/redfish/v1/Systems/system/Bios/Actions/Bios.ChangePassword
{
"NewPassword": "12345678",
"OldPassword": "1234567890",
"PasswordName": "Administrator"
}
root@intel-obmc:~# cat /var/lib/bios-settings-manager/passwordData
{
"CurrentPassword": "1234567890",
"IsAdminPwdChanged": 1,
"IsUserPwdChanged": 0,
"NewPassword": "2DD65D57EB60B1D92C5F3D2DC84724FCEE7BC02E57AA75E834712266ED94CAC704047B2FF7CEC1C36BED280B36BB5AC6",
"UserName": "Administrator"
}
Change-Id: Ib54b36819e49c891c6169c95d9cdaebd5bcb06f3
Signed-off-by: Kuiying Wang <kuiying.wang@intel.com>
diff --git a/include/password.hpp b/include/password.hpp
new file mode 100644
index 0000000..1b1269c
--- /dev/null
+++ b/include/password.hpp
@@ -0,0 +1,87 @@
+/*
+// 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.
+*/
+#pragma once
+#include "config.h"
+
+#include <openssl/evp.h>
+#include <openssl/hmac.h>
+#include <openssl/sha.h>
+
+#include <nlohmann/json.hpp>
+#include <sdbusplus/asio/object_server.hpp>
+#include <sdbusplus/server.hpp>
+#include <xyz/openbmc_project/BIOSConfig/Password/server.hpp>
+
+#include <filesystem>
+#include <string>
+
+namespace bios_config_pwd
+{
+
+static constexpr auto servicePwd = "xyz.openbmc_project.BIOSConfigPassword";
+static constexpr auto objectPathPwd =
+ "/xyz/openbmc_project/bios_config/password";
+constexpr auto biosPasswordFile = "passwordData";
+constexpr auto biosSeedFile = "seedData";
+
+using Base = sdbusplus::xyz::openbmc_project::BIOSConfig::server::Password;
+namespace fs = std::filesystem;
+
+/** @class Password
+ *
+ * @brief Implements the BIOS Password
+ */
+class Password : public Base
+{
+ public:
+ Password() = delete;
+ ~Password() = default;
+ Password(const Password&) = delete;
+ Password& operator=(const Password&) = delete;
+ Password(Password&&) = delete;
+ Password& operator=(Password&&) = delete;
+
+ /** @brief Constructs Password object.
+ *
+ * @param[in] objectServer - object server
+ * @param[in] systemBus - bus connection
+ */
+ Password(sdbusplus::asio::object_server& objectServer,
+ std::shared_ptr<sdbusplus::asio::connection>& systemBus);
+
+ /** @brief Set the BIOS attribute with a new value, the new value is added
+ * to the PendingAttribute.
+ *
+ * @param[in] userName - User name - user / admin.
+ * @param[in] currentPassword - Current user/ admin Password.
+ * @param[in] newPassword - New user/ admin Password.
+ */
+ void changePassword(std::string userName, std::string currentPassword,
+ std::string newPassword) override;
+
+ private:
+ void verifyPassword(std::string userName, std::string currentPassword,
+ std::string newPassword);
+ bool isMatch(const std::string expected, const std::string seed,
+ const std::string rawData, const std::string algo);
+ sdbusplus::asio::object_server& objServer;
+ std::shared_ptr<sdbusplus::asio::connection>& systemBus;
+ std::filesystem::path passwordFile;
+ std::filesystem::path seedFile;
+ std::string mNewPassword;
+};
+
+} // namespace bios_config_pwd
diff --git a/meson.build b/meson.build
index e23a60a..fdec677 100755
--- a/meson.build
+++ b/meson.build
@@ -32,6 +32,7 @@
dependency('phosphor-logging'),
dependency('sdbusplus'),
dependency('systemd'),
+ dependency('openssl'),
]
executable('biosconfig-manager',
@@ -44,6 +45,15 @@
install: true,
install_dir: get_option('bindir'))
+executable('biosconfig-password',
+ 'src/password.cpp',
+ implicit_include_directories: true,
+ include_directories: ['include'],
+ dependencies: deps,
+ cpp_args : boost_args,
+ install: true,
+ install_dir: get_option('bindir'))
+
systemd = dependency('systemd')
systemd_system_unit_dir = systemd.get_pkgconfig_variable(
'systemdsystemunitdir',
@@ -56,3 +66,11 @@
install_dir: systemd_system_unit_dir,
output: 'xyz.openbmc_project.biosconfig_manager.service'
)
+
+configure_file(
+ copy: true,
+ input: 'service_files/xyz.openbmc_project.biosconfig_password.service',
+ install: true,
+ install_dir: systemd_system_unit_dir,
+ output: 'xyz.openbmc_project.biosconfig_password.service'
+)
\ No newline at end of file
diff --git a/service_files/xyz.openbmc_project.biosconfig_password.service b/service_files/xyz.openbmc_project.biosconfig_password.service
new file mode 100644
index 0000000..8b4ef9d
--- /dev/null
+++ b/service_files/xyz.openbmc_project.biosconfig_password.service
@@ -0,0 +1,12 @@
+[Unit]
+Description= BIOS Config Password - For Remote BIOS configuration update
+
+[Service]
+Restart=always
+ExecStart=/usr/bin/biosconfig-password
+SyslogIdentifier=biosconfig-password
+Type=dbus
+BusName=xyz.openbmc_project.BIOSConfigPassword
+
+[Install]
+WantedBy=multi-user.target
diff --git a/src/manager.cpp b/src/manager.cpp
index c173ee4..c92205f 100644
--- a/src/manager.cpp
+++ b/src/manager.cpp
@@ -20,6 +20,7 @@
#include "xyz/openbmc_project/Common/error.hpp"
#include <boost/asio.hpp>
+#include <phosphor-logging/elog-errors.hpp>
#include <sdbusplus/asio/connection.hpp>
#include <sdbusplus/asio/object_server.hpp>
@@ -117,12 +118,16 @@
// BIOS attribute not found in the BaseBIOSTable
if (iter == biosTable.end())
{
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "BIOS attribute not found in the BaseBIOSTable");
throw AttributeNotFound();
}
// BIOS attribute is read only
if (std::get<static_cast<uint8_t>(Index::readOnly)>(iter->second))
{
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "BIOS attribute is read only");
throw AttributeReadOnly();
}
@@ -130,15 +135,19 @@
std::get<static_cast<uint8_t>(Index::attributeType)>(iter->second);
if (attributeType != std::get<0>(pair.second))
{
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "attributeType is not same with bios base table");
throw InvalidArgument();
}
// Validate enumeration BIOS attributes
if (attributeType == AttributeType::Enumeration)
{
- // For enumeration the expected variant types is std::string
+ // For enumeration the expected variant types is Enumeration
if (std::get<1>(pair.second).index() == 0)
{
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "Enumeration property value is not enum");
throw InvalidArgument();
}
@@ -161,6 +170,8 @@
if (!found)
{
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "No valid attribute");
throw InvalidArgument();
}
}
@@ -170,6 +181,8 @@
// For enumeration the expected variant types is std::string
if (std::get<1>(pair.second).index() == 0)
{
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "String property value is not string");
throw InvalidArgument();
}
@@ -198,15 +211,19 @@
if ((attrValue.length() < static_cast<size_t>(minStringLength)) ||
(attrValue.length() > static_cast<size_t>(maxStringLength)))
{
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "std::string, length is invalid");
throw InvalidArgument();
}
}
if (attributeType == AttributeType::Integer)
{
- // For enumeration the expected variant types is std::string
+ // For enumeration the expected variant types is Integer
if (std::get<1>(pair.second).index() == 1)
{
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "Enumeration property value is not int");
throw InvalidArgument();
}
@@ -237,11 +254,16 @@
if ((attrValue < lowerBound) || (attrValue > upperBound))
{
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "Integer, bound is invalid");
throw InvalidArgument();
}
if (((std::abs(attrValue - lowerBound)) % scalarIncrement) != 0)
{
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "((std::abs(attrValue - lowerBound)) % scalarIncrement) != "
+ "0");
throw InvalidArgument();
}
}
diff --git a/src/password.cpp b/src/password.cpp
new file mode 100644
index 0000000..0c24347
--- /dev/null
+++ b/src/password.cpp
@@ -0,0 +1,202 @@
+/*
+// 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;
+
+bool Password::isMatch(const std::string expected, const std::string seed,
+ const std::string rawData, const std::string algo)
+{
+ phosphor::logging::log<phosphor::logging::level::ERR>("isMatch");
+ if (algo == "SHA384")
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>("SHA384");
+ std::vector<uint8_t> output(SHA384_DIGEST_LENGTH);
+ unsigned int mdLen = 0;
+ if (HMAC(EVP_sha384(), seed.c_str(), seed.length(),
+ reinterpret_cast<const unsigned char*>(rawData.c_str()),
+ rawData.length(), output.data(), &mdLen) == NULL)
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "Generate HMAC_SHA384 Integrity Check Value failed");
+ output.resize(0);
+ throw InternalFailure();
+ }
+ phosphor::logging::log<phosphor::logging::level::ERR>(expected.c_str());
+ std::string strOutput;
+ boost::algorithm::hex(output.begin(), output.end(),
+ std::back_inserter(strOutput));
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ strOutput.c_str());
+ if (expected == strOutput)
+ {
+ 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::string userPassword = "";
+ std::string adminPassword = "";
+ std::string seed = "";
+ std::string hashAlgo = "";
+ try
+ {
+ std::ifstream ifs(seedFile.c_str());
+ nlohmann::json json;
+ ifs >> json;
+ userPassword = json["UserPwdHash"];
+ adminPassword = json["AdminPwdHash"];
+ seed = json["Seed"];
+ hashAlgo = json["HashAlgo"];
+ }
+ catch (nlohmann::detail::exception& e)
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(e.what());
+ throw InternalFailure();
+ }
+ if (userName == "Administrator")
+ {
+ if (!isMatch(adminPassword, seed, currentPassword, hashAlgo))
+ {
+ throw InvalidCurrentPassword();
+ }
+ }
+ else
+ {
+ if (!isMatch(userPassword, seed, currentPassword, hashAlgo))
+ {
+ throw InvalidCurrentPassword();
+ }
+ }
+ if (hashAlgo == "SHA384")
+ {
+ std::vector<uint8_t> output(SHA384_DIGEST_LENGTH);
+ unsigned int mdLen = 0;
+ if (HMAC(
+ EVP_sha384(), seed.c_str(), seed.length(),
+ reinterpret_cast<const unsigned char*>(newPassword.c_str()),
+ newPassword.length(), output.data(), &mdLen) == NULL)
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "Generate HMAC_SHA384 Integrity Check Value failed");
+ output.resize(0);
+ throw InternalFailure();
+ }
+ std::string strOutput;
+ boost::algorithm::hex(output.begin(), output.end(),
+ std::back_inserter(strOutput));
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ strOutput.c_str());
+ mNewPassword = strOutput;
+ }
+
+ 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);
+
+ try
+ {
+ nlohmann::json json;
+ json["UserName"] = userName;
+ json["CurrentPassword"] = currentPassword;
+ json["NewPassword"] = mNewPassword;
+ if (userName == "Administrator")
+ {
+ json["IsAdminPwdChanged"] = 1;
+ json["IsUserPwdChanged"] = 0;
+ }
+ else
+ {
+ json["IsAdminPwdChanged"] = 0;
+ json["IsUserPwdChanged"] = 1;
+ }
+ std::ofstream ofs(passwordFile.c_str(), std::ios::out);
+ ofs << std::setw(4) << json << std::endl;
+ }
+ catch (nlohmann::detail::exception& e)
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(e.what());
+ 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);
+ passwordFile = biosDir / biosPasswordFile;
+ 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;
+}