1 #include <ipmiallowlist.hpp>
2 #include <ipmid/api.hpp>
3 #include <ipmid/utils.hpp>
4 #include <phosphor-logging/elog-errors.hpp>
5 #include <phosphor-logging/lg2.hpp>
6 #include <settings.hpp>
7 #include <xyz/openbmc_project/Common/error.hpp>
8 #include <xyz/openbmc_project/Control/Security/RestrictionMode/server.hpp>
9
10 #include <algorithm>
11 #include <array>
12
13 using namespace phosphor::logging;
14 using namespace sdbusplus::error::xyz::openbmc_project::common;
15
16 namespace ipmi
17 {
18
19 // put the filter provider in an unnamed namespace
20 namespace
21 {
22
23 /** @class AllowlistFilter
24 *
25 * Class that implements an IPMI message filter based
26 * on incoming interface and a restriction mode setting
27 */
28 class AllowlistFilter
29 {
30 public:
31 AllowlistFilter();
32 ~AllowlistFilter() = default;
33 AllowlistFilter(const AllowlistFilter&) = delete;
34 AllowlistFilter(AllowlistFilter&&) = delete;
35 AllowlistFilter& operator=(const AllowlistFilter&) = delete;
36 AllowlistFilter& operator=(AllowlistFilter&&) = delete;
37
38 private:
39 void postInit();
40 void cacheRestrictedMode(const std::vector<std::string>& devices);
41 void handleRestrictedModeChange(
42 sdbusplus::message_t& m,
43 const std::map<std::string, size_t>& deviceList);
44 ipmi::Cc filterMessage(ipmi::message::Request::ptr request);
45
46 std::vector<bool> restrictedMode;
47 std::shared_ptr<sdbusplus::asio::connection> bus;
48 std::unique_ptr<settings::Objects> objects;
49 std::unique_ptr<sdbusplus::bus::match_t> modeChangeMatch;
50
51 static constexpr const char restrictionModeIntf[] =
52 "xyz.openbmc_project.Control.Security.RestrictionMode";
53 };
54
AllowlistFilter()55 AllowlistFilter::AllowlistFilter()
56 {
57 bus = getSdBus();
58
59 lg2::info("Loading allowlist filter");
60 ipmi::registerFilter(ipmi::prioOpenBmcBase,
61 [this](ipmi::message::Request::ptr request) {
62 return filterMessage(request);
63 });
64
65 // wait until io->run is going to fetch RestrictionMode
66 post_work([this]() { postInit(); });
67 }
68
69 /** @brief Get RestrictionMode of the devices which has RestrictionMode support
70 * enabled
71 * @param[in] devices - vector of devices object path
72 * @returns void.
73 */
74
cacheRestrictedMode(const std::vector<std::string> & devices)75 void AllowlistFilter::cacheRestrictedMode(
76 const std::vector<std::string>& devices)
77 {
78 using namespace sdbusplus::server::xyz::openbmc_project::control::security;
79 std::string restrictionModeSetting;
80 std::string restrictionModeService;
81
82 for (auto& dev : devices)
83 {
84 try
85 {
86 restrictionModeSetting = dev;
87 restrictionModeService =
88 objects->service(restrictionModeSetting, restrictionModeIntf);
89 }
90 catch (const std::out_of_range& e)
91 {
92 lg2::error(
93 "Could not look up restriction mode interface from cache");
94 return;
95 }
96
97 std::string mode;
98 try
99 {
100 auto propValue = ipmi::getDbusProperty(
101 *bus, restrictionModeService, restrictionModeSetting,
102 restrictionModeIntf, "RestrictionMode");
103 mode = std::get<std::string>(propValue);
104 }
105 catch (const std::exception& e)
106 {
107 lg2::error("Error in RestrictionMode Get");
108 // Fail-safe to true.
109 size_t index = std::distance(&*std::begin(devices), &dev);
110 restrictedMode[index] = true;
111 }
112
113 auto restrictionMode = RestrictionMode::convertModesFromString(mode);
114
115 bool restrictMode =
116 (restrictionMode == RestrictionMode::Modes::Allowlist);
117 restrictedMode.emplace_back(restrictMode);
118
119 lg2::info("Set restrictedMode = {RESTRICTED_MODE}", "RESTRICTED_MODE",
120 restrictMode);
121 }
122 }
123
124 /** @brief Update RestrictionMode if any changes in RestrictionMode
125 * @param[in] m - sdbusplus message. Using this to get Updated Mode dbus path
126 * @param[in] deviceList - map to store devices path and their index
127 * @returns void.
128 */
129
handleRestrictedModeChange(sdbusplus::message_t & m,const std::map<std::string,size_t> & deviceList)130 void AllowlistFilter::handleRestrictedModeChange(
131 sdbusplus::message_t& m, const std::map<std::string, size_t>& deviceList)
132 {
133 using namespace sdbusplus::server::xyz::openbmc_project::control::security;
134 std::string intf;
135 std::vector<std::pair<std::string, ipmi::Value>> propertyList;
136 m.read(intf, propertyList);
137
138 std::string path = m.get_path();
139 size_t hostId = 0;
140 auto it = deviceList.find(path);
141
142 if (it == deviceList.end())
143 {
144 lg2::error("Key not found in deviceList ");
145 }
146 else
147 {
148 hostId = it->second;
149 }
150
151 for (const auto& property : propertyList)
152 {
153 if (property.first == "RestrictionMode")
154 {
155 RestrictionMode::Modes restrictionMode =
156 RestrictionMode::convertModesFromString(
157 std::get<std::string>(property.second));
158 bool restrictMode =
159 (restrictionMode == RestrictionMode::Modes::Allowlist);
160 restrictedMode[hostId] = restrictMode;
161
162 lg2::info("Updated restrictedMode = {RESTRICTED_MODE}",
163 "RESTRICTED_MODE", restrictMode);
164 }
165 }
166 }
167
168 /** @brief Get and Update RestrictionModes of supported devices
169 * @param[in] void
170 * @returns void.
171 */
172
postInit()173 void AllowlistFilter::postInit()
174 {
175 objects = std::make_unique<settings::Objects>(
176 *bus, std::vector<settings::Interface>({restrictionModeIntf}));
177 if (!objects)
178 {
179 lg2::error(
180 "Failed to create settings object; defaulting to restricted mode");
181 return;
182 }
183
184 std::vector<std::string> devices;
185 try
186 {
187 devices = objects->map.at(restrictionModeIntf);
188 }
189 catch (const std::out_of_range& e)
190 {
191 lg2::error("Could not look up restriction mode interface from cache");
192 return;
193 }
194
195 // Initialize restricted mode
196 cacheRestrictedMode(devices);
197 // Wait for changes on Restricted mode
198 std::map<std::string, size_t> deviceList;
199
200 for (size_t index = 0; index < devices.size(); index++)
201 {
202 deviceList.emplace(devices[index], index);
203 }
204
205 std::string filterStr;
206 std::string devicesDbusPath{"/xyz/openbmc_project/control"};
207
208 filterStr = sdbusplus::bus::match::rules::propertiesChangedNamespace(
209 devicesDbusPath, restrictionModeIntf);
210
211 modeChangeMatch = std::make_unique<sdbusplus::bus::match_t>(
212 *bus, filterStr, [this, deviceList](sdbusplus::message_t& m) {
213 handleRestrictedModeChange(m, deviceList);
214 });
215 }
216
217 /** @brief Filter IPMI messages with RestrictedMode
218 * @param[in] request - IPMI messahe request
219 * @returns IPMI completion code success or error.
220 */
221
filterMessage(ipmi::message::Request::ptr request)222 ipmi::Cc AllowlistFilter::filterMessage(ipmi::message::Request::ptr request)
223 {
224 /* Getting hostIdx for all IPMI devices like hosts, debugcard and other
225 devices from ipmi::message::Request and call postInit() to get the
226 restriction mode for all the IPMI commands */
227
228 size_t hostIdx = request->ctx->hostIdx;
229
230 if (request->ctx->channel == ipmi::channelSystemIface &&
231 restrictedMode[hostIdx])
232 {
233 if (!std::binary_search(
234 allowlist.cbegin(), allowlist.cend(),
235 std::make_pair(request->ctx->netFn, request->ctx->cmd)))
236 {
237 lg2::error("Net function not allowlisted, "
238 "NetFn: {NETFN}, Cmd: {CMD}",
239 "NETFN", lg2::hex, request->ctx->netFn, "CMD", lg2::hex,
240 request->ctx->cmd);
241
242 return ipmi::ccInsufficientPrivilege;
243 }
244 }
245 return ipmi::ccSuccess;
246 }
247
248 // instantiate the AllowlistFilter when this shared object is loaded
249 AllowlistFilter allowlistFilter;
250
251 } // namespace
252
253 } // namespace ipmi
254