blob: 994925e9bef31a21c7da6588051353cccd58ac38 [file] [log] [blame]
#include <ipmiallowlist.hpp>
#include <ipmid/api.hpp>
#include <ipmid/utils.hpp>
#include <phosphor-logging/elog-errors.hpp>
#include <phosphor-logging/log.hpp>
#include <settings.hpp>
#include <xyz/openbmc_project/Common/error.hpp>
#include <xyz/openbmc_project/Control/Security/RestrictionMode/server.hpp>
#include <algorithm>
#include <array>
using namespace phosphor::logging;
using namespace sdbusplus::error::xyz::openbmc_project::common;
namespace ipmi
{
// put the filter provider in an unnamed namespace
namespace
{
/** @class AllowlistFilter
*
* Class that implements an IPMI message filter based
* on incoming interface and a restriction mode setting
*/
class AllowlistFilter
{
public:
AllowlistFilter();
~AllowlistFilter() = default;
AllowlistFilter(const AllowlistFilter&) = delete;
AllowlistFilter(AllowlistFilter&&) = delete;
AllowlistFilter& operator=(const AllowlistFilter&) = delete;
AllowlistFilter& operator=(AllowlistFilter&&) = delete;
private:
void postInit();
void cacheRestrictedMode(const std::vector<std::string>& devices);
void handleRestrictedModeChange(
sdbusplus::message_t& m,
const std::map<std::string, size_t>& deviceList);
ipmi::Cc filterMessage(ipmi::message::Request::ptr request);
std::vector<bool> restrictedMode;
std::shared_ptr<sdbusplus::asio::connection> bus;
std::unique_ptr<settings::Objects> objects;
std::unique_ptr<sdbusplus::bus::match_t> modeChangeMatch;
static constexpr const char restrictionModeIntf[] =
"xyz.openbmc_project.Control.Security.RestrictionMode";
};
AllowlistFilter::AllowlistFilter()
{
bus = getSdBus();
log<level::INFO>("Loading allowlist filter");
ipmi::registerFilter(ipmi::prioOpenBmcBase,
[this](ipmi::message::Request::ptr request) {
return filterMessage(request);
});
// wait until io->run is going to fetch RestrictionMode
post_work([this]() { postInit(); });
}
/** @brief Get RestrictionMode of the devices which has RestrictionMode support
* enabled
* @param[in] devices - vector of devices object path
* @returns void.
*/
void AllowlistFilter::cacheRestrictedMode(
const std::vector<std::string>& devices)
{
using namespace sdbusplus::server::xyz::openbmc_project::control::security;
std::string restrictionModeSetting;
std::string restrictionModeService;
for (auto& dev : devices)
{
try
{
restrictionModeSetting = dev;
restrictionModeService = objects->service(restrictionModeSetting,
restrictionModeIntf);
}
catch (const std::out_of_range& e)
{
log<level::ERR>(
"Could not look up restriction mode interface from cache");
return;
}
bus->async_method_call(
[this, index = std::distance(&*std::begin(devices), &dev)](
boost::system::error_code ec, ipmi::Value v) {
if (ec)
{
log<level::ERR>("Error in RestrictionMode Get");
// Fail-safe to true.
restrictedMode[index] = true;
return;
}
auto mode = std::get<std::string>(v);
auto restrictionMode =
RestrictionMode::convertModesFromString(mode);
bool restrictMode =
(restrictionMode == RestrictionMode::Modes::Allowlist);
restrictedMode.emplace_back(restrictMode);
log<level::INFO>((restrictMode ? "Set restrictedMode = true"
: "Set restrictedMode = false"));
},
restrictionModeService, restrictionModeSetting,
"org.freedesktop.DBus.Properties", "Get", restrictionModeIntf,
"RestrictionMode");
}
}
/** @brief Update RestrictionMode if any changes in RestrictionMode
* @param[in] m - sdbusplus message. Using this to get Updated Mode dbus path
* @param[in] deviceList - map to store devices path and their index
* @returns void.
*/
void AllowlistFilter::handleRestrictedModeChange(
sdbusplus::message_t& m, const std::map<std::string, size_t>& deviceList)
{
using namespace sdbusplus::server::xyz::openbmc_project::control::security;
std::string intf;
std::vector<std::pair<std::string, ipmi::Value>> propertyList;
m.read(intf, propertyList);
std::string path = m.get_path();
size_t hostId = 0;
auto it = deviceList.find(path);
if (it == deviceList.end())
{
log<level::ERR>("Key not found in deviceList ");
}
else
{
hostId = it->second;
}
for (const auto& property : propertyList)
{
if (property.first == "RestrictionMode")
{
RestrictionMode::Modes restrictionMode =
RestrictionMode::convertModesFromString(
std::get<std::string>(property.second));
bool restrictMode =
(restrictionMode == RestrictionMode::Modes::Allowlist);
restrictedMode[hostId] = restrictMode;
log<level::INFO>((restrictMode ? "Updated restrictedMode = true"
: "Updated restrictedMode = false"));
}
}
}
/** @brief Get and Update RestrictionModes of supported devices
* @param[in] void
* @returns void.
*/
void AllowlistFilter::postInit()
{
objects = std::make_unique<settings::Objects>(
*bus, std::vector<settings::Interface>({restrictionModeIntf}));
if (!objects)
{
log<level::ERR>(
"Failed to create settings object; defaulting to restricted mode");
return;
}
std::vector<std::string> devices;
try
{
devices = objects->map.at(restrictionModeIntf);
}
catch (const std::out_of_range& e)
{
log<level::ERR>(
"Could not look up restriction mode interface from cache");
return;
}
// Initialize restricted mode
cacheRestrictedMode(devices);
// Wait for changes on Restricted mode
std::map<std::string, size_t> deviceList;
for (size_t index = 0; index < devices.size(); index++)
{
deviceList.emplace(devices[index], index);
}
std::string filterStr;
std::string devicesDbusPath{"/xyz/openbmc_project/control"};
filterStr = sdbusplus::bus::match::rules::propertiesChangedNamespace(
devicesDbusPath, restrictionModeIntf);
modeChangeMatch = std::make_unique<sdbusplus::bus::match_t>(
*bus, filterStr, [this, deviceList](sdbusplus::message_t& m) {
handleRestrictedModeChange(m, deviceList);
});
}
/** @brief Filter IPMI messages with RestrictedMode
* @param[in] request - IPMI messahe request
* @returns IPMI completion code success or error.
*/
ipmi::Cc AllowlistFilter::filterMessage(ipmi::message::Request::ptr request)
{
/* Getting hostIdx for all IPMI devices like hosts, debugcard and other
devices from ipmi::message::Request and call postInit() to get the
restriction mode for all the IPMI commands */
size_t hostIdx = request->ctx->hostIdx;
if (request->ctx->channel == ipmi::channelSystemIface &&
restrictedMode[hostIdx])
{
if (!std::binary_search(
allowlist.cbegin(), allowlist.cend(),
std::make_pair(request->ctx->netFn, request->ctx->cmd)))
{
log<level::ERR>("Net function not allowlisted",
entry("NETFN=0x%X", int(request->ctx->netFn)),
entry("CMD=0x%X", int(request->ctx->cmd)));
return ipmi::ccInsufficientPrivilege;
}
}
return ipmi::ccSuccess;
}
// instantiate the AllowlistFilter when this shared object is loaded
AllowlistFilter allowlistFilter;
} // namespace
} // namespace ipmi