1 #include "channel.hpp"
2 
3 #include "net.hpp"
4 #include "transporthandler.hpp"
5 #include "types.hpp"
6 #include "utils.hpp"
7 
8 #include <arpa/inet.h>
9 
10 #include <boost/process/child.hpp>
11 #include <fstream>
12 #include <phosphor-logging/elog-errors.hpp>
13 #include <phosphor-logging/log.hpp>
14 #include <string>
15 #include <xyz/openbmc_project/Common/error.hpp>
16 
17 using namespace phosphor::logging;
18 using namespace sdbusplus::xyz::openbmc_project::Common::Error;
19 
20 /** @struct GetChannelAccessRequest
21  *
22  *  IPMI payload for Get Channel access command request.
23  */
24 struct GetChannelAccessRequest
25 {
26     uint8_t channelNumber;   //!< Channel number.
27     uint8_t volatileSetting; //!< Get non-volatile or the volatile setting.
28 } __attribute__((packed));
29 
30 /** @struct GetChannelAccessResponse
31  *
32  *  IPMI payload for Get Channel access command response.
33  */
34 struct GetChannelAccessResponse
35 {
36     uint8_t settings;       //!< Channel settings.
37     uint8_t privilegeLimit; //!< Channel privilege level limit.
38 } __attribute__((packed));
39 
40 ipmi_ret_t ipmi_get_channel_access(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
41                                    ipmi_request_t request,
42                                    ipmi_response_t response,
43                                    ipmi_data_len_t data_len,
44                                    ipmi_context_t context)
45 {
46     auto requestData =
47         reinterpret_cast<const GetChannelAccessRequest*>(request);
48     std::vector<uint8_t> outPayload(sizeof(GetChannelAccessResponse));
49     auto responseData =
50         reinterpret_cast<GetChannelAccessResponse*>(outPayload.data());
51 
52     /*
53      * The value Eh is used as a way to identify the current channel that
54      * the command is being received from.
55      */
56     constexpr auto channelE = 0x0E;
57     int channel = requestData->channelNumber;
58     auto ethdevice = ipmi::network::ChanneltoEthernet(channel);
59 
60     if (channel != channelE && ethdevice.empty())
61     {
62         *data_len = 0;
63         return IPMI_CC_INVALID_FIELD_REQUEST;
64     }
65 
66     /*
67      * [7:6] - reserved
68      * [5]   - 1b = Alerting disabled
69      * [4]   - 1b = per message authentication disabled
70      * [3]   - 0b = User level authentication enabled
71      * [2:0] - 2h = always available
72      */
73     constexpr auto channelSetting = 0x32;
74 
75     responseData->settings = channelSetting;
76     // Defaulting the channel privilege to administrator level.
77     responseData->privilegeLimit = PRIVILEGE_ADMIN;
78 
79     *data_len = outPayload.size();
80     memcpy(response, outPayload.data(), *data_len);
81 
82     return IPMI_CC_OK;
83 }
84 
85 // ATTENTION: This ipmi function is very hardcoded on purpose
86 // OpenBMC does not fully support IPMI.  This command is useful
87 // to have around because it enables testing of interfaces with
88 // the IPMI tool.
89 #define GET_CHANNEL_INFO_CHANNEL_OFFSET 0
90 // IPMI Table 6-2
91 #define IPMI_CHANNEL_TYPE_IPMB 1
92 // IPMI Table 6-3
93 #define IPMI_CHANNEL_MEDIUM_TYPE_OTHER 6
94 
95 ipmi_ret_t ipmi_app_channel_info(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
96                                  ipmi_request_t request,
97                                  ipmi_response_t response,
98                                  ipmi_data_len_t data_len,
99                                  ipmi_context_t context)
100 {
101     ipmi_ret_t rc = IPMI_CC_OK;
102     auto* p = static_cast<uint8_t*>(request);
103     int channel = (*p) & CHANNEL_MASK;
104     std::string ethdevice = ipmi::network::ChanneltoEthernet(channel);
105 
106     // The supported channels numbers are those which are configured.
107     // Channel Number E is used as way to identify the current channel
108     // that the command is being is received from.
109     if (channel != 0xe && ethdevice.empty())
110     {
111         rc = IPMI_CC_PARM_OUT_OF_RANGE;
112         *data_len = 0;
113     }
114     else
115     {
116         uint8_t resp[] = {1,
117                           IPMI_CHANNEL_MEDIUM_TYPE_OTHER,
118                           IPMI_CHANNEL_TYPE_IPMB,
119                           1,
120                           0x41,
121                           0xA7,
122                           0x00,
123                           0,
124                           0};
125 
126         *data_len = sizeof(resp);
127         memcpy(response, resp, *data_len);
128     }
129 
130     return rc;
131 }
132 
133 namespace cipher
134 {
135 
136 /** @brief Get the supported Cipher records
137  *
138  * The cipher records are read from the JSON file and converted into cipher
139  * suite record format mentioned in the IPMI specification. The records can be
140  * either OEM or standard cipher. Each json entry is parsed and converted into
141  * the cipher record format and pushed into the vector.
142  *
143  * @return vector containing all the cipher suite records.
144  *
145  */
146 std::vector<uint8_t> getCipherRecords()
147 {
148     std::vector<uint8_t> records;
149 
150     std::ifstream jsonFile(configFile);
151     if (!jsonFile.is_open())
152     {
153         log<level::ERR>("Channel Cipher suites file not found");
154         elog<InternalFailure>();
155     }
156 
157     auto data = Json::parse(jsonFile, nullptr, false);
158     if (data.is_discarded())
159     {
160         log<level::ERR>("Parsing channel cipher suites JSON failed");
161         elog<InternalFailure>();
162     }
163 
164     for (const auto& record : data)
165     {
166         if (record.find(oem) != record.end())
167         {
168             // OEM cipher suite - 0xC1
169             records.push_back(oemCipherSuite);
170             // Cipher Suite ID
171             records.push_back(record.value(cipher, 0));
172             // OEM IANA - 3 bytes
173             records.push_back(record.value(oem, 0));
174             records.push_back(record.value(oem, 0) >> 8);
175             records.push_back(record.value(oem, 0) >> 16);
176         }
177         else
178         {
179             // OEM cipher suite - 0xC0
180             records.push_back(stdCipherSuite);
181             // Cipher Suite ID
182             records.push_back(record.value(cipher, 0));
183         }
184 
185         // Authentication algorithm number
186         records.push_back(record.value(auth, 0));
187         // Integrity algorithm number
188         records.push_back(record.value(integrity, 0) | integrityTag);
189         // Confidentiality algorithm number
190         records.push_back(record.value(conf, 0) | confTag);
191     }
192 
193     return records;
194 }
195 
196 } // namespace cipher
197 
198 ipmi_ret_t getChannelCipherSuites(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
199                                   ipmi_request_t request,
200                                   ipmi_response_t response,
201                                   ipmi_data_len_t data_len,
202                                   ipmi_context_t context)
203 {
204     static std::vector<uint8_t> records;
205     static auto recordInit = false;
206 
207     auto requestData =
208         reinterpret_cast<const GetChannelCipherRequest*>(request);
209 
210     if (*data_len < sizeof(GetChannelCipherRequest))
211     {
212         *data_len = 0;
213         return IPMI_CC_REQ_DATA_LEN_INVALID;
214     }
215 
216     *data_len = 0;
217 
218     // Support only for list algorithms by cipher suite
219     if (cipher::listCipherSuite !=
220         (requestData->listIndex & cipher::listTypeMask))
221     {
222         return IPMI_CC_INVALID_FIELD_REQUEST;
223     }
224 
225     if (!recordInit)
226     {
227         try
228         {
229             records = cipher::getCipherRecords();
230             recordInit = true;
231         }
232         catch (const std::exception& e)
233         {
234             return IPMI_CC_UNSPECIFIED_ERROR;
235         }
236     }
237 
238     // List index(00h-3Fh), 0h selects the first set of 16, 1h selects the next
239     // set of 16 and so on.
240     auto index =
241         static_cast<size_t>(requestData->listIndex & cipher::listIndexMask);
242 
243     // Calculate the number of record data bytes to be returned.
244     auto start = std::min(index * cipher::respSize, records.size());
245     auto end =
246         std::min((index * cipher::respSize) + cipher::respSize, records.size());
247     auto size = end - start;
248 
249     auto responseData = reinterpret_cast<GetChannelCipherRespHeader*>(response);
250     responseData->channelNumber = cipher::defaultChannelNumber;
251 
252     if (!size)
253     {
254         *data_len = sizeof(GetChannelCipherRespHeader);
255     }
256     else
257     {
258         std::copy_n(records.data() + start, size,
259                     static_cast<uint8_t*>(response) + 1);
260         *data_len = size + sizeof(GetChannelCipherRespHeader);
261     }
262 
263     return IPMI_CC_OK;
264 }
265 
266 template <typename... ArgTypes>
267 static int executeCmd(const char* path, ArgTypes&&... tArgs)
268 {
269     boost::process::child execProg(path, const_cast<char*>(tArgs)...);
270     execProg.wait();
271     return execProg.exit_code();
272 }
273 
274 /** @brief Enable the network IPMI service on the specified ethernet interface.
275  *
276  *  @param[in] intf - ethernet interface on which to enable IPMI
277  */
278 void enableNetworkIPMI(const std::string& intf)
279 {
280     // Check if there is a iptable filter to drop IPMI packets for the
281     // interface.
282     auto retCode =
283         executeCmd("/usr/sbin/iptables", "-C", "INPUT", "-p", "udp", "-i",
284                    intf.c_str(), "--dport", "623", "-j", "DROP");
285 
286     // If the iptable filter exists, delete the filter.
287     if (!retCode)
288     {
289         auto response =
290             executeCmd("/usr/sbin/iptables", "-D", "INPUT", "-p", "udp", "-i",
291                        intf.c_str(), "--dport", "623", "-j", "DROP");
292 
293         if (response)
294         {
295             log<level::ERR>("Dropping the iptables filter failed",
296                             entry("INTF=%s", intf.c_str()),
297                             entry("RETURN_CODE:%d", response));
298         }
299     }
300 }
301 
302 /** @brief Disable the network IPMI service on the specified ethernet interface.
303  *
304  *  @param[in] intf - ethernet interface on which to disable IPMI
305  */
306 void disableNetworkIPMI(const std::string& intf)
307 {
308     // Check if there is a iptable filter to drop IPMI packets for the
309     // interface.
310     auto retCode =
311         executeCmd("/usr/sbin/iptables", "-C", "INPUT", "-p", "udp", "-i",
312                    intf.c_str(), "--dport", "623", "-j", "DROP");
313 
314     // If the iptable filter does not exist, add filter to drop network IPMI
315     // packets
316     if (retCode)
317     {
318         auto response =
319             executeCmd("/usr/sbin/iptables", "-I", "INPUT", "-p", "udp", "-i",
320                        intf.c_str(), "--dport", "623", "-j", "DROP");
321 
322         if (response)
323         {
324             log<level::ERR>("Inserting iptables filter failed",
325                             entry("INTF=%s", intf.c_str()),
326                             entry("RETURN_CODE:%d", response));
327         }
328     }
329 }
330 
331 /** @struct SetChannelAccessRequest
332  *
333  *  IPMI payload for Set Channel access command request.
334  */
335 struct SetChannelAccessRequest
336 {
337     uint8_t channelNumber; //!< Channel number
338     uint8_t accessMode;    //!< Access mode for IPMI messaging
339     uint8_t privLevel;     //!< Channel Privilege Level
340 } __attribute__((packed));
341 
342 ipmi_ret_t ipmi_set_channel_access(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
343                                    ipmi_request_t request,
344                                    ipmi_response_t response,
345                                    ipmi_data_len_t data_len,
346                                    ipmi_context_t context)
347 {
348     auto requestData =
349         reinterpret_cast<const SetChannelAccessRequest*>(request);
350 
351     int channel = requestData->channelNumber;
352     // Validate the channel number corresponds to any of the network channel.
353     auto ethdevice = ipmi::network::ChanneltoEthernet(channel);
354     if (ethdevice.empty())
355     {
356         *data_len = 0;
357         return IPMI_CC_INVALID_FIELD_REQUEST;
358     }
359 
360     sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()};
361     // Bits[2:0] indicates the Access Mode, this mask field will extract the
362     // access mode from the command data.
363     static constexpr auto accessModeMask = 0x07;
364     auto accessMode = requestData->accessMode & accessModeMask;
365     static constexpr auto disabled = 0;
366     static constexpr auto enabled = 2;
367 
368     try
369     {
370         if (accessMode == enabled)
371         {
372             enableNetworkIPMI(ethdevice);
373         }
374         else if (accessMode == disabled)
375         {
376             disableNetworkIPMI(ethdevice);
377         }
378     }
379     catch (const sdbusplus::exception::SdBusError& e)
380     {
381         log<level::ERR>(e.what());
382         *data_len = 0;
383         return IPMI_CC_UNSPECIFIED_ERROR;
384     }
385 
386     return IPMI_CC_OK;
387 }
388