blob: 6fe0f3e10ee85d1d157e2a133862ed63b6b5793d [file] [log] [blame]
Vernon Mauery783dc072018-10-08 12:05:20 -07001#include <algorithm>
2#include <array>
3#include <ipmid/api.hpp>
4#include <ipmid/registration.hpp>
5#include <ipmiwhitelist.hpp>
6#include <phosphor-logging/elog-errors.hpp>
7#include <phosphor-logging/log.hpp>
8#include <settings.hpp>
9#include <xyz/openbmc_project/Control/Security/RestrictionMode/server.hpp>
10
11using namespace phosphor::logging;
12using namespace sdbusplus::xyz::openbmc_project::Common::Error;
13
14namespace ipmi
15{
16
17// put the filter provider in an unnamed namespace
18namespace
19{
20
21/** @class WhitelistFilter
22 *
23 * Class that implements an IPMI message filter based
24 * on incoming interface and a restriction mode setting
25 */
26class WhitelistFilter
27{
28 public:
29 WhitelistFilter();
30 ~WhitelistFilter() = default;
31 WhitelistFilter(WhitelistFilter const&) = delete;
32 WhitelistFilter(WhitelistFilter&&) = delete;
33 WhitelistFilter& operator=(WhitelistFilter const&) = delete;
34 WhitelistFilter& operator=(WhitelistFilter&&) = delete;
35
36 private:
37 void postInit();
38 void cacheRestrictedMode();
39 void handleRestrictedModeChange(sdbusplus::message::message& m);
40 ipmi::Cc filterMessage(ipmi::message::Request::ptr request);
41
42 bool restrictedMode = true;
43 std::shared_ptr<sdbusplus::asio::connection> bus;
44 std::unique_ptr<settings::Objects> objects;
45 std::unique_ptr<sdbusplus::bus::match::match> modeChangeMatch;
46
47 static constexpr const char restrictionModeIntf[] =
48 "xyz.openbmc_project.Control.Security.RestrictionMode";
49};
50
51WhitelistFilter::WhitelistFilter()
52{
53 bus = getSdBus();
54
55 log<level::INFO>("Loading whitelist filter");
56
57 ipmi::registerFilter(ipmi::prioOpenBmcBase,
58 [this](ipmi::message::Request::ptr request) {
59 return filterMessage(request);
60 });
61
62 // wait until io->run is going to fetch RestrictionMode
63 post_work([this]() { postInit(); });
64}
65
66void WhitelistFilter::cacheRestrictedMode()
67{
68 using namespace sdbusplus::xyz::openbmc_project::Control::Security::server;
69 std::string restrictionModeSetting;
70 std::string restrictionModeService;
71 try
72 {
73 restrictionModeSetting = objects->map.at(restrictionModeIntf).at(0);
74 restrictionModeService =
75 objects->service(restrictionModeSetting, restrictionModeIntf);
76 }
77 catch (const std::out_of_range& e)
78 {
79 log<level::ERR>(
80 "Could not look up restriction mode interface from cache");
81 return;
82 }
83 bus->async_method_call(
84 [this](boost::system::error_code ec, std::string mode) {
85 if (ec)
86 {
87 log<level::ERR>("Error in RestrictionMode Get");
88 // Fail-safe to true.
89 restrictedMode = true;
90 return;
91 }
92 auto restrictionMode =
93 RestrictionMode::convertModesFromString(mode);
94 restrictedMode =
95 (restrictionMode == RestrictionMode::Modes::Whitelist);
96 log<level::INFO>((restrictedMode ? "Set restrictedMode = true"
97 : "Set restrictedMode = false"));
98 },
99 restrictionModeService.c_str(), restrictionModeSetting.c_str(),
100 "org.freedesktop.DBus.Properties", "Get", "RestrictionMode");
101}
102
103void WhitelistFilter::handleRestrictedModeChange(sdbusplus::message::message& m)
104{
105 using namespace sdbusplus::xyz::openbmc_project::Control::Security::server;
106 std::string mode;
107 m.read(mode);
108 RestrictionMode::Modes restrictionMode =
109 RestrictionMode::convertModesFromString(mode);
110 restrictedMode = (restrictionMode == RestrictionMode::Modes::Whitelist);
111 log<level::INFO>((restrictedMode ? "Updated restrictedMode = true"
112 : "Updated restrictedMode = false"));
113}
114
115void WhitelistFilter::postInit()
116{
117 objects = std::make_unique<settings::Objects>(
118 *bus, std::vector<settings::Interface>({restrictionModeIntf}));
119 if (!objects)
120 {
121 log<level::ERR>(
122 "Failed to create settings object; defaulting to restricted mode");
123 return;
124 }
125
126 // Initialize restricted mode
127 cacheRestrictedMode();
128 // Wait for changes on Restricted mode
129 std::string filterStr;
130 try
131 {
132 filterStr = sdbusplus::bus::match::rules::propertiesChanged(
133 objects->map.at(restrictionModeIntf).at(0), restrictionModeIntf);
134 }
135 catch (const std::out_of_range& e)
136 {
137 log<level::ERR>("Failed to determine restriction mode filter string");
138 return;
139 }
140 modeChangeMatch = std::make_unique<sdbusplus::bus::match::match>(
141 *bus, filterStr, [this](sdbusplus::message::message& m) {
142 handleRestrictedModeChange(m);
143 });
144}
145
146ipmi::Cc WhitelistFilter::filterMessage(ipmi::message::Request::ptr request)
147{
148 if (request->ctx->channel == ipmi::channelSystemIface && restrictedMode)
149 {
150 if (!std::binary_search(
151 whitelist.cbegin(), whitelist.cend(),
152 std::make_pair(request->ctx->netFn, request->ctx->cmd)))
153 {
154 log<level::ERR>("Net function not whitelisted",
155 entry("NETFN=0x%X", int(request->ctx->netFn)),
156 entry("CMD=0x%X", int(request->ctx->cmd)));
157 return ipmi::ccInsufficientPrivilege;
158 }
159 }
160 return ipmi::ccSuccess;
161}
162
163// instantiate the WhitelistFilter when this shared object is loaded
164WhitelistFilter whitelistFilter;
165
166} // namespace
167
168} // namespace ipmi