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