blob: 9f1e7c8a0b27bc66bff905ab06c62bc1bee03c47 [file] [log] [blame]
#include <algorithm>
#include <array>
#include <ipmid/api.hpp>
#include <ipmid/utils.hpp>
#include <ipmiwhitelist.hpp>
#include <phosphor-logging/elog-errors.hpp>
#include <phosphor-logging/log.hpp>
#include <settings.hpp>
#include <xyz/openbmc_project/Control/Security/RestrictionMode/server.hpp>
using namespace phosphor::logging;
using namespace sdbusplus::xyz::openbmc_project::Common::Error;
namespace ipmi
{
// put the filter provider in an unnamed namespace
namespace
{
/** @class WhitelistFilter
*
* Class that implements an IPMI message filter based
* on incoming interface and a restriction mode setting
*/
class WhitelistFilter
{
public:
WhitelistFilter();
~WhitelistFilter() = default;
WhitelistFilter(WhitelistFilter const&) = delete;
WhitelistFilter(WhitelistFilter&&) = delete;
WhitelistFilter& operator=(WhitelistFilter const&) = delete;
WhitelistFilter& operator=(WhitelistFilter&&) = delete;
private:
void postInit();
void cacheRestrictedMode();
void handleRestrictedModeChange(sdbusplus::message::message& m);
ipmi::Cc filterMessage(ipmi::message::Request::ptr request);
bool restrictedMode = true;
std::shared_ptr<sdbusplus::asio::connection> bus;
std::unique_ptr<settings::Objects> objects;
std::unique_ptr<sdbusplus::bus::match::match> modeChangeMatch;
static constexpr const char restrictionModeIntf[] =
"xyz.openbmc_project.Control.Security.RestrictionMode";
};
WhitelistFilter::WhitelistFilter()
{
bus = getSdBus();
log<level::INFO>("Loading whitelist 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(); });
}
void WhitelistFilter::cacheRestrictedMode()
{
using namespace sdbusplus::xyz::openbmc_project::Control::Security::server;
std::string restrictionModeSetting;
std::string restrictionModeService;
try
{
restrictionModeSetting = objects->map.at(restrictionModeIntf).at(0);
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](boost::system::error_code ec, ipmi::Value v) {
if (ec)
{
log<level::ERR>("Error in RestrictionMode Get");
// Fail-safe to true.
restrictedMode = true;
return;
}
auto mode = std::get<std::string>(v);
auto restrictionMode =
RestrictionMode::convertModesFromString(mode);
restrictedMode =
(restrictionMode == RestrictionMode::Modes::Whitelist);
log<level::INFO>((restrictedMode ? "Set restrictedMode = true"
: "Set restrictedMode = false"));
},
restrictionModeService, restrictionModeSetting,
"org.freedesktop.DBus.Properties", "Get", restrictionModeIntf,
"RestrictionMode");
}
void WhitelistFilter::handleRestrictedModeChange(sdbusplus::message::message& m)
{
using namespace sdbusplus::xyz::openbmc_project::Control::Security::server;
std::string intf;
std::vector<std::pair<std::string, ipmi::Value>> propertyList;
m.read(intf, propertyList);
for (const auto& property : propertyList)
{
if (property.first == "RestrictionMode")
{
RestrictionMode::Modes restrictionMode =
RestrictionMode::convertModesFromString(
std::get<std::string>(property.second));
restrictedMode =
(restrictionMode == RestrictionMode::Modes::Whitelist);
log<level::INFO>((restrictedMode
? "Updated restrictedMode = true"
: "Updated restrictedMode = false"));
}
}
}
void WhitelistFilter::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;
}
// Initialize restricted mode
cacheRestrictedMode();
// Wait for changes on Restricted mode
std::string filterStr;
try
{
filterStr = sdbusplus::bus::match::rules::propertiesChanged(
objects->map.at(restrictionModeIntf).at(0), restrictionModeIntf);
}
catch (const std::out_of_range& e)
{
log<level::ERR>("Failed to determine restriction mode filter string");
return;
}
modeChangeMatch = std::make_unique<sdbusplus::bus::match::match>(
*bus, filterStr, [this](sdbusplus::message::message& m) {
handleRestrictedModeChange(m);
});
}
ipmi::Cc WhitelistFilter::filterMessage(ipmi::message::Request::ptr request)
{
if (request->ctx->channel == ipmi::channelSystemIface && restrictedMode)
{
if (!std::binary_search(
whitelist.cbegin(), whitelist.cend(),
std::make_pair(request->ctx->netFn, request->ctx->cmd)))
{
log<level::ERR>("Net function not whitelisted",
entry("NETFN=0x%X", int(request->ctx->netFn)),
entry("CMD=0x%X", int(request->ctx->cmd)));
return ipmi::ccInsufficientPrivilege;
}
}
return ipmi::ccSuccess;
}
// instantiate the WhitelistFilter when this shared object is loaded
WhitelistFilter whitelistFilter;
} // namespace
} // namespace ipmi