Replace reset BMC authentication with factory reset
Due to security concerns, replace the command that performs a
reset of the BMC authentication mechanism to perform a full
BMC reset so that any potential sensitive data is erased when
the authentication mechanism needs to be reset.
Tested: Verified the host was powered off and the BMC was
factory reset as part of this command.
Change-Id: I71dead237538d410e88710d2b7a53cea30de1426
Signed-off-by: Adriana Kobylak <anoo@us.ibm.com>
diff --git a/Makefile.am b/Makefile.am
index a59ec07..65d6856 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -3,7 +3,6 @@
liboemhandler_la_SOURCES = oemhandler.cpp \
host-interface.cpp \
- local_users.cpp \
org/open_power/OCC/Metrics/error.cpp \
inventory-sensor.cpp
diff --git a/README.md b/README.md
index 86bee27..85a3369 100644
--- a/README.md
+++ b/README.md
@@ -15,5 +15,33 @@
## Supported Commands
- Partial Add
- Prepare for host update
-- Reset BMC authentication
+- BMC Factory Reset
+
+## Command Documentation
+
+### BMC Factory Reset
+Netfun: 0x3a
+Command: 0x11
+
+This command will call to reset the BMC to its factory default. See [here][0]
+for the factory reset implementation details.
+
+This includes:
+1. Power the chassis off. The host needs to be powered off because the factory
+reset deletes the inventory items, which are needed for the BMC during the power
+on path, causing the power on to fail. The inventory items are repopulated
+during a host power on.
+An enhancement to OpenBMC would be to handle missing inventory items during
+a BMC reboot when the host is on.
+2. Set the BMC to perform factory reset on reboot.
+3. Reboot the BMC for the factory reset to take effect.
+
+Because the chassis is powered off, the host does not receive a return code
+when successful.
+
+This command is not allowed when the IPMI restriction mode is set to
+'Whitelist'. See [here][1] for more information.
+
+[0]: https://github.com/openbmc/phosphor-dbus-interfaces/tree/master/xyz/openbmc_project/Common/FactoryReset#xyzopenbmc_projectsoftwarebmcupdater
+[1]: https://github.com/openbmc/phosphor-dbus-interfaces/blob/master/xyz/openbmc_project/Control/Security/RestrictionMode.interface.yaml
diff --git a/local_users.cpp b/local_users.cpp
deleted file mode 100644
index ecf6024..0000000
--- a/local_users.cpp
+++ /dev/null
@@ -1,109 +0,0 @@
-#include "config.h"
-
-#include "local_users.hpp"
-
-#include <ipmid-host/cmd.hpp>
-#include <phosphor-logging/log.hpp>
-
-namespace local
-{
-namespace users
-{
-
-using namespace phosphor::logging;
-
-constexpr auto userIface = "xyz.openbmc_project.User.Attributes";
-constexpr auto propIface = "org.freedesktop.DBus.Properties";
-
-using DbusObjectPath = std::string;
-using DbusService = std::string;
-using DbusInterface = std::string;
-using ObjectTree =
- std::map<DbusObjectPath, std::map<DbusService, std::vector<DbusInterface>>>;
-
-/**
- * @brief Gets a list of all local users in the form of GetSubTree
- * results.
- *
- * @param[out] users - Filled in with the results of a
- * GetSubTree call that returns all users.
- */
-void getUsers(ObjectTree& users)
-{
- auto& bus = ipmid_get_sdbus_plus_handler();
-
- try
- {
- auto method = bus->new_method_call(MAPPER_BUS_NAME, MAPPER_OBJ,
- MAPPER_IFACE, "GetSubTree");
- method.append("/xyz/openbmc_project/user/", 0,
- std::vector<std::string>{userIface});
-
- auto reply = bus->call(method);
- if (reply.is_method_error())
- {
- throw std::runtime_error("Method error on GetSubTree call");
- }
-
- reply.read(users);
- }
- catch (sdbusplus::exception::SdBusError& e)
- {
- throw std::runtime_error(e.what());
- }
-}
-
-/**
- * @brief Enables the user passed in
- *
- * @param[in] path - The user object path
- * @param[in] service - The service hosting the user
- */
-void enableUser(const std::string& path, const std::string& service)
-{
- auto& bus = ipmid_get_sdbus_plus_handler();
-
- try
- {
- auto method = bus->new_method_call(service.c_str(), path.c_str(),
- propIface, "Set");
- sdbusplus::message::variant<bool> enabled{true};
- method.append(userIface, "UserEnabled", enabled);
-
- auto reply = bus->call(method);
- if (reply.is_method_error())
- {
- throw std::runtime_error("Method error on property set call");
- }
- }
- catch (sdbusplus::exception::SdBusError& e)
- {
- throw std::runtime_error(e.what());
- }
-}
-
-ipmi_ret_t enableUsers()
-{
- ObjectTree users;
-
- try
- {
- getUsers(users);
-
- for (const auto& user : users)
- {
- enableUser(user.first, user.second.begin()->first);
- }
- }
- catch (std::runtime_error& e)
- {
- log<level::ERR>("Failed enabling local users",
- entry("ERROR=%s", e.what()));
- return IPMI_CC_UNSPECIFIED_ERROR;
- }
-
- return IPMI_CC_OK;
-}
-
-} // namespace users
-} // namespace local
diff --git a/local_users.hpp b/local_users.hpp
deleted file mode 100644
index b04156e..0000000
--- a/local_users.hpp
+++ /dev/null
@@ -1,21 +0,0 @@
-#pragma once
-
-#include <ipmid/api.h>
-
-namespace local
-{
-namespace users
-{
-
-/**
- * @brief Enable all local BMC users
- *
- * Sets the UserEnabled property on all
- * /xyz/openbmc_project/user/<user> objects.
- *
- * @return ipmi_ret_t - IPMI CC
- */
-ipmi_ret_t enableUsers();
-
-} // namespace users
-} // namespace local
diff --git a/oemhandler.cpp b/oemhandler.cpp
index e35de77..e76405c 100644
--- a/oemhandler.cpp
+++ b/oemhandler.cpp
@@ -3,7 +3,6 @@
#include "oemhandler.hpp"
#include "elog-errors.hpp"
-#include "local_users.hpp"
#include <endian.h>
#include <ipmid/api.h>
@@ -19,6 +18,7 @@
#include <org/open_power/Host/error.hpp>
#include <org/open_power/OCC/Metrics/error.hpp>
#include <sdbusplus/bus.hpp>
+#include <sdbusplus/exception.hpp>
void register_netfn_ibm_oem_commands() __attribute__((constructor));
@@ -79,6 +79,40 @@
return {};
}
+std::string getService(sdbusplus::bus::bus& bus, const std::string& path,
+ const std::string& interface)
+{
+ auto method = bus.new_method_call(MAPPER_BUS_NAME, MAPPER_OBJ, MAPPER_IFACE,
+ "GetObject");
+
+ method.append(path);
+ method.append(std::vector<std::string>({interface}));
+
+ std::map<std::string, std::vector<std::string>> response;
+
+ try
+ {
+ auto reply = bus.call(method);
+
+ reply.read(response);
+ if (response.empty())
+ {
+ log<level::ERR>("Error in mapper response for getting service name",
+ entry("PATH=%s", path.c_str()),
+ entry("INTERFACE=%s", interface.c_str()));
+ return std::string{};
+ }
+ }
+ catch (const sdbusplus::exception::SdBusError& e)
+ {
+ log<level::ERR>("Error in mapper method call",
+ entry("ERROR=%s", e.what()));
+ return std::string{};
+ }
+
+ return response.begin()->first;
+}
+
std::string readESEL(const char* fileName)
{
std::string content{};
@@ -305,17 +339,87 @@
return ipmi_rc;
}
-ipmi_ret_t ipmi_ibm_oem_reset_bmc_auth(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
- ipmi_request_t request,
- ipmi_response_t response,
- ipmi_data_len_t data_len,
- ipmi_context_t context)
+ipmi_ret_t ipmi_ibm_oem_bmc_factory_reset(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
+ ipmi_request_t request,
+ ipmi_response_t response,
+ ipmi_data_len_t data_len,
+ ipmi_context_t context)
{
- ipmi_ret_t rc;
+ sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()};
+ // The timeout for IPMI commands is 5s
+ constexpr auto powerOffWait = std::chrono::seconds(1);
+ constexpr auto setFactoryWait = std::chrono::seconds(3);
- rc = local::users::enableUsers();
+ // Power Off Chassis
+ auto service = getService(bus, stateChassisPath, stateChassisIntf);
+ if (service.empty())
+ {
+ return IPMI_CC_UNSPECIFIED_ERROR;
+ }
+ sdbusplus::message::variant<std::string> off =
+ "xyz.openbmc_project.State.Chassis.Transition.Off";
+ auto method = bus.new_method_call(service.c_str(), stateChassisPath,
+ propertiesIntf, "Set");
+ method.append(stateChassisIntf, "RequestedPowerTransition", off);
+ try
+ {
+ bus.call_noreply(method);
+ }
+ catch (const sdbusplus::exception::SdBusError& e)
+ {
+ log<level::ERR>("Error powering off the chassis",
+ entry("ERROR=%s", e.what()));
+ return IPMI_CC_UNSPECIFIED_ERROR;
+ }
- return rc;
+ // Wait a few seconds for the chassis to power off
+ std::this_thread::sleep_for(powerOffWait);
+
+ // Set Factory Reset
+ method = bus.new_method_call(bmcUpdaterServiceName, softwarePath,
+ factoryResetIntf, "Reset");
+ try
+ {
+ bus.call_noreply(method);
+ }
+ catch (const sdbusplus::exception::SdBusError& e)
+ {
+ log<level::ERR>("Error setting factory reset",
+ entry("ERROR=%s", e.what()));
+ return IPMI_CC_UNSPECIFIED_ERROR;
+ }
+
+ // Wait a few seconds for service that sets the reset env variable to
+ // complete before the BMC is rebooted
+ std::this_thread::sleep_for(setFactoryWait);
+
+ // Reboot BMC
+ service = getService(bus, stateBmcPath, stateBmcIntf);
+ if (service.empty())
+ {
+ log<level::ALERT>("Error getting the service name to reboot the BMC. "
+ "The BMC needs to be manually rebooted to complete "
+ "the factory reset.");
+ return IPMI_CC_UNSPECIFIED_ERROR;
+ }
+ sdbusplus::message::variant<std::string> reboot =
+ "xyz.openbmc_project.State.BMC.Transition.Reboot";
+ method = bus.new_method_call(service.c_str(), stateBmcPath, propertiesIntf,
+ "Set");
+ method.append(stateBmcIntf, "RequestedBMCTransition", reboot);
+ try
+ {
+ bus.call_noreply(method);
+ }
+ catch (const sdbusplus::exception::SdBusError& e)
+ {
+ log<level::ALERT>("Error calling to reboot the BMC. The BMC needs to "
+ "be manually rebooted to complete the factory reset.",
+ entry("ERROR=%s", e.what()));
+ return IPMI_CC_UNSPECIFIED_ERROR;
+ }
+
+ return IPMI_CC_OK;
}
namespace
@@ -339,8 +443,8 @@
ipmi_register_callback(NETFUN_OEM, IPMI_CMD_PREP_FW_UPDATE, NULL,
ipmi_ibm_oem_prep_fw_update, SYSTEM_INTERFACE);
- ipmi_register_callback(NETFUN_IBM_OEM, IPMI_CMD_RESET_BMC_AUTH, NULL,
- ipmi_ibm_oem_reset_bmc_auth, SYSTEM_INTERFACE);
+ ipmi_register_callback(NETFUN_IBM_OEM, IPMI_CMD_BMC_FACTORY_RESET, NULL,
+ ipmi_ibm_oem_bmc_factory_reset, SYSTEM_INTERFACE);
// Create new object on the bus
auto objPath = std::string{CONTROL_HOST_OBJ_MGR} + '/' + HOST_NAME + '0';
diff --git a/oemhandler.hpp b/oemhandler.hpp
index c6794b8..9c5f563 100644
--- a/oemhandler.hpp
+++ b/oemhandler.hpp
@@ -6,11 +6,22 @@
#include <map>
#include <string>
+static constexpr auto propertiesIntf = "org.freedesktop.DBus.Properties";
+static constexpr auto bmcUpdaterServiceName =
+ "xyz.openbmc_project.Software.BMC.Updater";
+static constexpr auto softwarePath = "/xyz/openbmc_project/software";
+static constexpr auto factoryResetIntf =
+ "xyz.openbmc_project.Common.FactoryReset";
+static constexpr auto stateChassisPath = "/xyz/openbmc_project/state/chassis0";
+static constexpr auto stateChassisIntf = "xyz.openbmc_project.State.Chassis";
+static constexpr auto stateBmcPath = "/xyz/openbmc_project/state/bmc0";
+static constexpr auto stateBmcIntf = "xyz.openbmc_project.State.BMC";
+
// IPMI commands for net functions.
enum ipmi_netfn_oem_cmds
{
IPMI_CMD_PREP_FW_UPDATE = 0x10,
- IPMI_CMD_RESET_BMC_AUTH = 0x11,
+ IPMI_CMD_BMC_FACTORY_RESET = 0x11,
IPMI_CMD_PESEL = 0xF0,
IPMI_CMD_OCC_RESET = 0x0E,
};