blob: 5b8eb270f7111de460b3f1831a7f764cbb55ffe5 [file] [log] [blame]
Vernon Mauery0d0cd162020-01-31 10:04:10 -08001#include <ipmi-whitelist.hpp>
2#include <ipmid/api.hpp>
3#include <ipmid/utils.hpp>
4#include <phosphor-logging/elog-errors.hpp>
5#include <phosphor-logging/log.hpp>
6#include <xyz/openbmc_project/Control/Security/RestrictionMode/server.hpp>
7
James Feistfcd2d3a2020-05-28 10:38:15 -07008#include <algorithm>
9#include <array>
10
Vernon Mauery0d0cd162020-01-31 10:04:10 -080011using namespace phosphor::logging;
12using namespace sdbusplus::xyz::openbmc_project::Common::Error;
13using namespace sdbusplus::xyz::openbmc_project::Control::Security::server;
14
15namespace ipmi
16{
17
18// put the filter provider in an unnamed namespace
19namespace
20{
21
22/** @class WhitelistFilter
23 *
24 * Class that implements an IPMI message filter based
25 * on incoming interface and a restriction mode setting
26 */
27class WhitelistFilter
28{
29
30 public:
31 WhitelistFilter();
32 ~WhitelistFilter() = default;
33 WhitelistFilter(WhitelistFilter const&) = delete;
34 WhitelistFilter(WhitelistFilter&&) = delete;
35 WhitelistFilter& operator=(WhitelistFilter const&) = delete;
36 WhitelistFilter& operator=(WhitelistFilter&&) = delete;
37
38 private:
39 void postInit();
40 void cacheRestrictedAndPostCompleteMode();
41 void handleRestrictedModeChange(sdbusplus::message::message& m);
42 void handlePostCompleteChange(sdbusplus::message::message& m);
43 void updatePostComplete(const std::string& value);
44 void updateRestrictionMode(const std::string& value);
45 ipmi::Cc filterMessage(ipmi::message::Request::ptr request);
Arun P. Mohanana4cea1a2020-03-18 16:01:57 +053046 void handleCoreBiosDoneChange(sdbusplus::message::message& m);
47 void cacheCoreBiosDone();
Vernon Mauery0d0cd162020-01-31 10:04:10 -080048
49 // the BMC KCS Policy Control Modes document uses different names
50 // than the RestrictionModes D-Bus interface; use aliases
51 static constexpr RestrictionMode::Modes restrictionModeAllowAll =
52 RestrictionMode::Modes::Provisioning;
53 static constexpr RestrictionMode::Modes restrictionModeRestricted =
54 RestrictionMode::Modes::ProvisionedHostWhitelist;
55 static constexpr RestrictionMode::Modes restrictionModeDenyAll =
56 RestrictionMode::Modes::ProvisionedHostDisabled;
57
58 RestrictionMode::Modes restrictionMode = restrictionModeRestricted;
Arun P. Mohanana4cea1a2020-03-18 16:01:57 +053059 bool postCompleted = true;
60 bool coreBIOSDone = true;
arun-pm849c3192020-02-12 18:10:32 +053061 int channelSMM = -1;
Vernon Mauery0d0cd162020-01-31 10:04:10 -080062 std::shared_ptr<sdbusplus::asio::connection> bus;
63 std::unique_ptr<sdbusplus::bus::match::match> modeChangeMatch;
64 std::unique_ptr<sdbusplus::bus::match::match> modeIntfAddedMatch;
65 std::unique_ptr<sdbusplus::bus::match::match> postCompleteMatch;
66 std::unique_ptr<sdbusplus::bus::match::match> postCompleteIntfAddedMatch;
Arun P. Mohanana4cea1a2020-03-18 16:01:57 +053067 std::unique_ptr<sdbusplus::bus::match::match> platStateChangeMatch;
68 std::unique_ptr<sdbusplus::bus::match::match> platStateIntfAddedMatch;
Vernon Mauery0d0cd162020-01-31 10:04:10 -080069
70 static constexpr const char restrictionModeIntf[] =
71 "xyz.openbmc_project.Control.Security.RestrictionMode";
72 static constexpr const char* systemOsStatusIntf =
73 "xyz.openbmc_project.State.OperatingSystem.Status";
Arun P. Mohanana4cea1a2020-03-18 16:01:57 +053074 static constexpr const char* hostMiscIntf =
75 "xyz.openbmc_project.State.Host.Misc";
Arun P. Mohanan31c45f62021-10-18 21:52:39 +053076 static constexpr const char* restrictionModePath =
77 "/xyz/openbmc_project/control/security/restriction_mode";
78 static constexpr const char* systemOsStatusPath =
79 "/xyz/openbmc_project/state/os";
Vernon Mauery0d0cd162020-01-31 10:04:10 -080080};
81
arun-pm849c3192020-02-12 18:10:32 +053082static inline uint8_t getSMMChannel()
83{
84 ipmi::ChannelInfo chInfo;
85
86 for (int channel = 0; channel < ipmi::maxIpmiChannels; channel++)
87 {
88 if (ipmi::getChannelInfo(channel, chInfo) != ipmi::ccSuccess)
89 {
90 continue;
91 }
92
93 if (static_cast<ipmi::EChannelMediumType>(chInfo.mediumType) ==
94 ipmi::EChannelMediumType::systemInterface &&
95 channel != ipmi::channelSystemIface)
96 {
97 log<level::INFO>("SMM channel number",
98 entry("CHANNEL=%d", channel));
99 return channel;
100 }
101 }
102 log<level::ERR>("Unable to find SMM Channel Info");
103 return -1;
104}
105
Vernon Mauery0d0cd162020-01-31 10:04:10 -0800106WhitelistFilter::WhitelistFilter()
107{
108 bus = getSdBus();
109
110 log<level::INFO>("Loading whitelist filter");
111
112 ipmi::registerFilter(ipmi::prioOpenBmcBase,
113 [this](ipmi::message::Request::ptr request) {
114 return filterMessage(request);
115 });
116
arun-pm849c3192020-02-12 18:10:32 +0530117 channelSMM = getSMMChannel();
Vernon Mauery0d0cd162020-01-31 10:04:10 -0800118 // wait until io->run is going to fetch RestrictionMode
119 post_work([this]() { postInit(); });
120}
121
122void WhitelistFilter::cacheRestrictedAndPostCompleteMode()
123{
Vernon Mauery0d0cd162020-01-31 10:04:10 -0800124 try
125 {
Arun P. Mohanan31c45f62021-10-18 21:52:39 +0530126 auto service =
127 ipmi::getService(*bus, restrictionModeIntf, restrictionModePath);
128 ipmi::Value v =
129 ipmi::getDbusProperty(*bus, service, restrictionModePath,
130 restrictionModeIntf, "RestrictionMode");
131 auto& mode = std::get<std::string>(v);
132 restrictionMode = RestrictionMode::convertModesFromString(mode);
133 log<level::INFO>("Read restriction mode",
134 entry("VALUE=%d", static_cast<int>(restrictionMode)));
Vernon Mauery0d0cd162020-01-31 10:04:10 -0800135 }
136 catch (const std::exception&)
137 {
Arun P. Mohanan31c45f62021-10-18 21:52:39 +0530138 log<level::ERR>("Could not initialize provisioning mode, "
139 "defaulting to restricted",
140 entry("VALUE=%d", static_cast<int>(restrictionMode)));
Vernon Mauery0d0cd162020-01-31 10:04:10 -0800141 }
142
Arun P. Mohanan31c45f62021-10-18 21:52:39 +0530143 try
144 {
145 auto service =
146 ipmi::getService(*bus, systemOsStatusIntf, systemOsStatusPath);
147 ipmi::Value v =
148 ipmi::getDbusProperty(*bus, service, systemOsStatusPath,
149 systemOsStatusIntf, "OperatingSystemState");
150 auto& value = std::get<std::string>(v);
Andrei Kartashevbc9a82d2021-12-27 17:21:50 +0300151 updatePostComplete(value);
Arun P. Mohanan31c45f62021-10-18 21:52:39 +0530152 log<level::INFO>("Read POST complete value",
153 entry("VALUE=%d", postCompleted));
154 }
155 catch (const std::exception&)
156 {
157 log<level::ERR>("Error in OperatingSystemState Get");
158 postCompleted = true;
159 }
Vernon Mauery0d0cd162020-01-31 10:04:10 -0800160}
161
162void WhitelistFilter::updateRestrictionMode(const std::string& value)
163{
164 restrictionMode = RestrictionMode::convertModesFromString(value);
165 log<level::INFO>("Updated restriction mode",
166 entry("VALUE=%d", static_cast<int>(restrictionMode)));
167}
168
169void WhitelistFilter::handleRestrictedModeChange(sdbusplus::message::message& m)
170{
171 std::string signal = m.get_member();
172 if (signal == "PropertiesChanged")
173 {
174 std::string intf;
175 std::vector<std::pair<std::string, ipmi::Value>> propertyList;
176 m.read(intf, propertyList);
177 for (const auto& property : propertyList)
178 {
179 if (property.first == "RestrictionMode")
180 {
181 updateRestrictionMode(std::get<std::string>(property.second));
182 }
183 }
184 }
185 else if (signal == "InterfacesAdded")
186 {
187 sdbusplus::message::object_path path;
188 DbusInterfaceMap restModeObj;
189 m.read(path, restModeObj);
190 auto intfItr = restModeObj.find(restrictionModeIntf);
191 if (intfItr == restModeObj.end())
192 {
193 return;
194 }
195 PropertyMap& propertyList = intfItr->second;
196 auto itr = propertyList.find("RestrictionMode");
197 if (itr == propertyList.end())
198 {
199 return;
200 }
201 updateRestrictionMode(std::get<std::string>(itr->second));
202 }
203}
204
205void WhitelistFilter::updatePostComplete(const std::string& value)
206{
Andrei Kartashevbc9a82d2021-12-27 17:21:50 +0300207 // The short string "Standby" is deprecated in favor of the full enum string
208 // Support for the short string will be removed in the future.
209 postCompleted = (value == "Standby") ||
210 (value == "xyz.openbmc_project.State.OperatingSystem."
211 "Status.OSStatus.Standby");
Vernon Mauery0d0cd162020-01-31 10:04:10 -0800212 log<level::INFO>(postCompleted ? "Updated to POST Complete"
213 : "Updated to !POST Complete");
214}
215
216void WhitelistFilter::handlePostCompleteChange(sdbusplus::message::message& m)
217{
218 std::string signal = m.get_member();
219 if (signal == "PropertiesChanged")
220 {
221 std::string intf;
222 std::vector<std::pair<std::string, ipmi::Value>> propertyList;
223 m.read(intf, propertyList);
224 for (const auto& property : propertyList)
225 {
226 if (property.first == "OperatingSystemState")
227 {
228 updatePostComplete(std::get<std::string>(property.second));
229 }
230 }
231 }
232 else if (signal == "InterfacesAdded")
233 {
234 sdbusplus::message::object_path path;
235 DbusInterfaceMap postCompleteObj;
236 m.read(path, postCompleteObj);
237 auto intfItr = postCompleteObj.find(systemOsStatusIntf);
238 if (intfItr == postCompleteObj.end())
239 {
240 return;
241 }
242 PropertyMap& propertyList = intfItr->second;
243 auto itr = propertyList.find("OperatingSystemState");
244 if (itr == propertyList.end())
245 {
246 return;
247 }
248 updatePostComplete(std::get<std::string>(itr->second));
249 }
250}
Arun P. Mohanana4cea1a2020-03-18 16:01:57 +0530251
252void WhitelistFilter::cacheCoreBiosDone()
253{
254 std::string coreBiosDonePath;
255 std::string coreBiosDoneService;
256 try
257 {
258 ipmi::DbusObjectInfo coreBiosDoneObj =
259 ipmi::getDbusObject(*bus, hostMiscIntf);
260
261 coreBiosDonePath = coreBiosDoneObj.first;
262 coreBiosDoneService = coreBiosDoneObj.second;
263 }
264 catch (const std::exception&)
265 {
266 log<level::ERR>("Could not initialize CoreBiosDone, "
267 "coreBIOSDone asserted as default");
268 return;
269 }
270
271 bus->async_method_call(
272 [this](boost::system::error_code ec, const ipmi::Value& v) {
273 if (ec)
274 {
275 log<level::ERR>(
276 "async call failed, coreBIOSDone asserted as default");
277 return;
278 }
279 coreBIOSDone = std::get<bool>(v);
280 log<level::INFO>("Read CoreBiosDone",
281 entry("VALUE=%d", static_cast<int>(coreBIOSDone)));
282 },
283 coreBiosDoneService, coreBiosDonePath,
284 "org.freedesktop.DBus.Properties", "Get", hostMiscIntf, "CoreBiosDone");
285}
286
287void WhitelistFilter::handleCoreBiosDoneChange(sdbusplus::message::message& msg)
288{
289 std::string signal = msg.get_member();
290 if (signal == "PropertiesChanged")
291 {
292 std::string intf;
293 std::vector<std::pair<std::string, ipmi::Value>> propertyList;
294 msg.read(intf, propertyList);
295 auto it =
296 std::find_if(propertyList.begin(), propertyList.end(),
297 [](const std::pair<std::string, ipmi::Value>& prop) {
298 return prop.first == "CoreBiosDone";
299 });
300
301 if (it != propertyList.end())
302 {
303 coreBIOSDone = std::get<bool>(it->second);
304 log<level::INFO>(coreBIOSDone ? "coreBIOSDone asserted"
305 : "coreBIOSDone not asserted");
306 }
307 }
308 else if (signal == "InterfacesAdded")
309 {
310 sdbusplus::message::object_path path;
311 DbusInterfaceMap eSpiresetObj;
312 msg.read(path, eSpiresetObj);
313 auto intfItr = eSpiresetObj.find(hostMiscIntf);
314 if (intfItr == eSpiresetObj.end())
315 {
316 return;
317 }
318 PropertyMap& propertyList = intfItr->second;
319 auto itr = propertyList.find("CoreBiosDone");
320 if (itr == propertyList.end())
321 {
322 return;
323 }
324 coreBIOSDone = std::get<bool>(itr->second);
325 log<level::INFO>(coreBIOSDone ? "coreBIOSDone asserted"
326 : "coreBIOSDone not asserted");
327 }
328}
329
Vernon Mauery0d0cd162020-01-31 10:04:10 -0800330void WhitelistFilter::postInit()
331{
332 // Wait for changes on Restricted mode
333 namespace rules = sdbusplus::bus::match::rules;
334 const std::string filterStrModeChange =
335 rules::type::signal() + rules::member("PropertiesChanged") +
336 rules::interface("org.freedesktop.DBus.Properties") +
337 rules::argN(0, restrictionModeIntf);
338
339 const std::string filterStrModeIntfAdd =
340 rules::interfacesAdded() +
341 rules::argNpath(
342 0, "/xyz/openbmc_project/control/security/restriction_mode");
343
344 const std::string filterStrPostComplete =
345 rules::type::signal() + rules::member("PropertiesChanged") +
346 rules::interface("org.freedesktop.DBus.Properties") +
347 rules::argN(0, systemOsStatusIntf);
348
349 const std::string filterStrPostIntfAdd =
350 rules::interfacesAdded() +
351 rules::argNpath(0, "/xyz/openbmc_project/state/os");
352
Arun P. Mohanana4cea1a2020-03-18 16:01:57 +0530353 const std::string filterStrPlatStateChange =
354 rules::type::signal() + rules::member("PropertiesChanged") +
355 rules::interface("org.freedesktop.DBus.Properties") +
356 rules::argN(0, hostMiscIntf);
357
358 const std::string filterStrPlatStateIntfAdd =
359 rules::interfacesAdded() +
360 rules::argNpath(0, "/xyz/openbmc_project/misc/platform_state");
361
Vernon Mauery0d0cd162020-01-31 10:04:10 -0800362 modeChangeMatch = std::make_unique<sdbusplus::bus::match::match>(
363 *bus, filterStrModeChange, [this](sdbusplus::message::message& m) {
364 handleRestrictedModeChange(m);
365 });
366 modeIntfAddedMatch = std::make_unique<sdbusplus::bus::match::match>(
367 *bus, filterStrModeIntfAdd, [this](sdbusplus::message::message& m) {
368 handleRestrictedModeChange(m);
369 });
370
371 postCompleteMatch = std::make_unique<sdbusplus::bus::match::match>(
372 *bus, filterStrPostComplete, [this](sdbusplus::message::message& m) {
373 handlePostCompleteChange(m);
374 });
375
376 postCompleteIntfAddedMatch = std::make_unique<sdbusplus::bus::match::match>(
377 *bus, filterStrPostIntfAdd, [this](sdbusplus::message::message& m) {
378 handlePostCompleteChange(m);
379 });
380
Arun P. Mohanana4cea1a2020-03-18 16:01:57 +0530381 platStateChangeMatch = std::make_unique<sdbusplus::bus::match::match>(
382 *bus, filterStrPlatStateChange, [this](sdbusplus::message::message& m) {
383 handleCoreBiosDoneChange(m);
384 });
385
386 platStateIntfAddedMatch = std::make_unique<sdbusplus::bus::match::match>(
387 *bus, filterStrPlatStateIntfAdd,
388 [this](sdbusplus::message::message& m) {
389 handleCoreBiosDoneChange(m);
390 });
391
Vernon Mauery0d0cd162020-01-31 10:04:10 -0800392 // Initialize restricted mode
393 cacheRestrictedAndPostCompleteMode();
Arun P. Mohanana4cea1a2020-03-18 16:01:57 +0530394 // Initialize CoreBiosDone
395 cacheCoreBiosDone();
Vernon Mauery0d0cd162020-01-31 10:04:10 -0800396}
397
398ipmi::Cc WhitelistFilter::filterMessage(ipmi::message::Request::ptr request)
399{
400 auto channelMask = static_cast<unsigned short>(1 << request->ctx->channel);
401 bool whitelisted = std::binary_search(
402 whitelist.cbegin(), whitelist.cend(),
403 std::make_tuple(request->ctx->netFn, request->ctx->cmd, channelMask),
404 [](const netfncmd_tuple& first, const netfncmd_tuple& value) {
405 return (std::get<2>(first) & std::get<2>(value))
406 ? first < std::make_tuple(std::get<0>(value),
407 std::get<1>(value),
408 std::get<2>(first))
409 : first < value;
410 });
411
412 // no special handling for non-system-interface channels
arun-pm849c3192020-02-12 18:10:32 +0530413 if (!(request->ctx->channel == ipmi::channelSystemIface ||
414 request->ctx->channel == channelSMM))
Vernon Mauery0d0cd162020-01-31 10:04:10 -0800415 {
416 if (!whitelisted)
417 {
418 log<level::INFO>("Channel/NetFn/Cmd not whitelisted",
419 entry("CHANNEL=0x%X", request->ctx->channel),
420 entry("NETFN=0x%X", int(request->ctx->netFn)),
421 entry("CMD=0x%X", int(request->ctx->cmd)));
Snehalatha V65b66ad2020-07-09 14:58:39 +0000422 return ipmi::ccInsufficientPrivilege;
Vernon Mauery0d0cd162020-01-31 10:04:10 -0800423 }
424 return ipmi::ccSuccess;
425 }
426
427 // for system interface, filtering is done as follows:
428 // Allow All: preboot ? ccSuccess : ccSuccess
429 // Restricted: preboot ? ccSuccess :
Snehalatha V65b66ad2020-07-09 14:58:39 +0000430 // ( whitelist ? ccSuccess : ccInsufficientPrivilege )
431 // Deny All: preboot ? ccSuccess : ccInsufficientPrivilege
Vernon Mauery0d0cd162020-01-31 10:04:10 -0800432
Arun P. Mohanana4cea1a2020-03-18 16:01:57 +0530433 if (!(postCompleted || coreBIOSDone))
Vernon Mauery0d0cd162020-01-31 10:04:10 -0800434 {
Arun P. Mohanana4cea1a2020-03-18 16:01:57 +0530435 // Allow all commands, till POST or CoreBiosDone is completed
Vernon Mauery0d0cd162020-01-31 10:04:10 -0800436 return ipmi::ccSuccess;
437 }
438
439 switch (restrictionMode)
440 {
441 case RestrictionMode::Modes::None:
442 case restrictionModeAllowAll:
443 {
444 // Allow All
445 return ipmi::ccSuccess;
446 break;
447 }
448 case restrictionModeRestricted:
449 {
450 // Restricted - follow whitelist
451 break;
452 }
453 case restrictionModeDenyAll:
454 {
455 // Deny All
456 whitelisted = false;
457 break;
458 }
459 default: // for whitelist and blacklist
Snehalatha V65b66ad2020-07-09 14:58:39 +0000460 return ipmi::ccInsufficientPrivilege;
Vernon Mauery0d0cd162020-01-31 10:04:10 -0800461 }
462
463 if (!whitelisted)
464 {
465 log<level::INFO>("Channel/NetFn/Cmd not whitelisted",
466 entry("CHANNEL=0x%X", request->ctx->channel),
467 entry("NETFN=0x%X", int(request->ctx->netFn)),
468 entry("CMD=0x%X", int(request->ctx->cmd)));
Snehalatha V65b66ad2020-07-09 14:58:39 +0000469 return ipmi::ccInsufficientPrivilege;
Vernon Mauery0d0cd162020-01-31 10:04:10 -0800470 }
471 return ipmi::ccSuccess;
472} // namespace
473
474// instantiate the WhitelistFilter when this shared object is loaded
475WhitelistFilter whitelistFilter;
476
477} // namespace
478
479} // namespace ipmi