summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Makefile.am15
-rw-r--r--whitelist-filter.cpp168
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
OpenPOWER on IntegriCloud