diff options
-rw-r--r-- | Makefile.am | 15 | ||||
-rw-r--r-- | whitelist-filter.cpp | 168 |
2 files changed, 181 insertions, 2 deletions
diff --git a/Makefile.am b/Makefile.am index ce96af7..b62741a 100644 --- a/Makefile.am +++ b/Makefile.am @@ -15,8 +15,6 @@ ipmid_SOURCES = \ host-cmd-manager.cpp \ utils.cpp -nodist_ipmid_SOURCES = ipmiwhitelist.cpp - libipmi20_BUILT_LIST = \ sensor-gen.cpp \ inventory-sensor-gen.cpp \ @@ -149,6 +147,19 @@ libsysintfcmds_la_LDFLAGS = \ -version-info 0:0:0 -shared libsysintfcmds_la_CXXFLAGS = $(COMMON_CXX) +libwhitelistdir = ${libdir}/ipmid-providers +libwhitelist_LTLIBRARIES = libwhitelist.la +libwhitelist_la_SOURCES = \ + whitelist-filter.cpp +libwhitelist_la_LDFLAGS = \ + $(SYSTEMD_LIBS) \ + $(libmapper_LIBS) \ + $(PHOSPHOR_LOGGING_LIBS) \ + $(PHOSPHOR_DBUS_INTERFACES_LIBS) \ + -version-info 0:0:0 -shared +libwhitelist_la_CXXFLAGS = $(COMMON_CXX) +nodist_libwhitelist_la_SOURCES = ipmiwhitelist.cpp + nobase_include_HEADERS = \ user_channel/channel_layer.hpp \ user_channel/user_layer.hpp diff --git a/whitelist-filter.cpp b/whitelist-filter.cpp new file mode 100644 index 0000000..6fe0f3e --- /dev/null +++ b/whitelist-filter.cpp @@ -0,0 +1,168 @@ +#include <algorithm> +#include <array> +#include <ipmid/api.hpp> +#include <ipmid/registration.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, std::string mode) { + if (ec) + { + log<level::ERR>("Error in RestrictionMode Get"); + // Fail-safe to true. + restrictedMode = true; + return; + } + auto restrictionMode = + RestrictionMode::convertModesFromString(mode); + restrictedMode = + (restrictionMode == RestrictionMode::Modes::Whitelist); + log<level::INFO>((restrictedMode ? "Set restrictedMode = true" + : "Set restrictedMode = false")); + }, + restrictionModeService.c_str(), restrictionModeSetting.c_str(), + "org.freedesktop.DBus.Properties", "Get", "RestrictionMode"); +} + +void WhitelistFilter::handleRestrictedModeChange(sdbusplus::message::message& m) +{ + using namespace sdbusplus::xyz::openbmc_project::Control::Security::server; + std::string mode; + m.read(mode); + RestrictionMode::Modes restrictionMode = + RestrictionMode::convertModesFromString(mode); + 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 |