1 #include "channel.hpp"
2 #include "types.hpp"
3 #include "transporthandler.hpp"
4 #include "utils.hpp"
5 #include "net.hpp"
6 
7 #include <fstream>
8 #include <string>
9 #include <arpa/inet.h>
10 
11 #include <phosphor-logging/log.hpp>
12 #include <phosphor-logging/elog-errors.hpp>
13 #include "xyz/openbmc_project/Common/error.hpp"
14 
15 
16 using namespace phosphor::logging;
17 using namespace sdbusplus::xyz::openbmc_project::Common::Error;
18 
19 /** @struct GetChannelAccessRequest
20  *
21  *  IPMI payload for Get Channel access command request.
22  */
23 struct GetChannelAccessRequest
24 {
25     uint8_t channelNumber;      //!< Channel number.
26     uint8_t volatileSetting;    //!< Get non-volatile or the volatile setting.
27 } __attribute__((packed));
28 
29 /** @struct GetChannelAccessResponse
30  *
31  *  IPMI payload for Get Channel access command response.
32  */
33 struct GetChannelAccessResponse
34 {
35     uint8_t settings;          //!< Channel settings.
36     uint8_t privilegeLimit;    //!< Channel privilege level limit.
37 } __attribute__((packed));
38 
39 
40 ipmi_ret_t ipmi_get_channel_access(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
41                             ipmi_request_t request, ipmi_response_t response,
42                             ipmi_data_len_t data_len, ipmi_context_t context)
43 {
44     auto requestData = reinterpret_cast<const GetChannelAccessRequest*>
45                    (request);
46     std::vector<uint8_t> outPayload(sizeof(GetChannelAccessResponse));
47     auto responseData = reinterpret_cast<GetChannelAccessResponse*>
48             (outPayload.data());
49 
50     /*
51      * The value Eh is used as a way to identify the current channel that
52      * the command is being received from.
53      */
54     constexpr auto channelE = 0x0E;
55     int channel = requestData->channelNumber;
56     auto ethdevice = ipmi::network::ChanneltoEthernet(channel);
57 
58     if (channel != channelE && ethdevice.empty())
59     {
60         *data_len = 0;
61         return IPMI_CC_INVALID_FIELD_REQUEST;
62     }
63 
64     /*
65      * [7:6] - reserved
66      * [5]   - 1b = Alerting disabled
67      * [4]   - 1b = per message authentication disabled
68      * [3]   - 0b = User level authentication enabled
69      * [2:0] - 2h = always available
70      */
71     constexpr auto channelSetting = 0x32;
72 
73     responseData->settings = channelSetting;
74     //Defaulting the channel privilege to administrator level.
75     responseData->privilegeLimit = PRIVILEGE_ADMIN;
76 
77     *data_len = outPayload.size();
78     memcpy(response, outPayload.data(), *data_len);
79 
80     return IPMI_CC_OK;
81 }
82 
83 // ATTENTION: This ipmi function is very hardcoded on purpose
84 // OpenBMC does not fully support IPMI.  This command is useful
85 // to have around because it enables testing of interfaces with
86 // the IPMI tool.
87 #define GET_CHANNEL_INFO_CHANNEL_OFFSET 0
88 // IPMI Table 6-2
89 #define IPMI_CHANNEL_TYPE_IPMB 1
90 // IPMI Table 6-3
91 #define IPMI_CHANNEL_MEDIUM_TYPE_OTHER 6
92 
93 ipmi_ret_t ipmi_app_channel_info(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
94                              ipmi_request_t request, ipmi_response_t response,
95                              ipmi_data_len_t data_len, ipmi_context_t context)
96 {
97     ipmi_ret_t rc = IPMI_CC_OK;
98     uint8_t resp[] = {
99         1,
100         IPMI_CHANNEL_MEDIUM_TYPE_OTHER,
101         IPMI_CHANNEL_TYPE_IPMB,
102         1,0x41,0xA7,0x00,0,0};
103     uint8_t *p = (uint8_t*) request;
104     int channel = (*p) & CHANNEL_MASK;
105     std::string ethdevice = ipmi::network::ChanneltoEthernet(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         rc = IPMI_CC_PARM_OUT_OF_RANGE;
112         *data_len = 0;
113     } else {
114         *data_len = sizeof(resp);
115         memcpy(response, resp, *data_len);
116     }
117 
118     return rc;
119 }
120 
121 namespace cipher
122 {
123 
124 /** @brief Get the supported Cipher records
125  *
126  * The cipher records are read from the JSON file and converted into cipher
127  * suite record format mentioned in the IPMI specification. The records can be
128  * either OEM or standard cipher. Each json entry is parsed and converted into
129  * the cipher record format and pushed into the vector.
130  *
131  * @return vector containing all the cipher suite records.
132  *
133  */
134 std::vector<uint8_t> getCipherRecords()
135 {
136     std::vector<uint8_t> records;
137 
138     std::ifstream jsonFile(configFile);
139     if (!jsonFile.is_open())
140     {
141         log<level::ERR>("Channel Cipher suites file not found");
142         elog<InternalFailure>();
143     }
144 
145     auto data = Json::parse(jsonFile, nullptr, false);
146     if (data.is_discarded())
147     {
148         log<level::ERR>("Parsing channel cipher suites JSON failed");
149         elog<InternalFailure>();
150     }
151 
152     for (const auto& record : data)
153     {
154         if (record.find(oem) != record.end())
155         {
156             // OEM cipher suite - 0xC1
157             records.push_back(oemCipherSuite);
158             // Cipher Suite ID
159             records.push_back(record.value(cipher, 0));
160             // OEM IANA - 3 bytes
161             records.push_back(record.value(oem, 0));
162             records.push_back(record.value(oem, 0) >> 8);
163             records.push_back(record.value(oem, 0) >> 16);
164 
165         }
166         else
167         {
168             // OEM cipher suite - 0xC0
169             records.push_back(stdCipherSuite);
170             // Cipher Suite ID
171             records.push_back(record.value(cipher, 0));
172         }
173 
174         // Authentication algorithm number
175         records.push_back(record.value(auth, 0));
176         // Integrity algorithm number
177         records.push_back(record.value(integrity, 0) | integrityTag);
178         // Confidentiality algorithm number
179         records.push_back(record.value(conf, 0) | confTag);
180     }
181 
182     return records;
183 }
184 
185 } //namespace cipher
186 
187 ipmi_ret_t getChannelCipherSuites(ipmi_netfn_t netfn,
188                                   ipmi_cmd_t cmd,
189                                   ipmi_request_t request,
190                                   ipmi_response_t response,
191                                   ipmi_data_len_t data_len,
192                                   ipmi_context_t context)
193 {
194     static std::vector<uint8_t> records;
195     static auto recordInit = false;
196 
197     auto requestData =
198         reinterpret_cast<const GetChannelCipherRequest*>(request);
199 
200 
201     if (*data_len < sizeof(GetChannelCipherRequest))
202     {
203         *data_len = 0;
204         return IPMI_CC_REQ_DATA_LEN_INVALID;
205     }
206 
207     *data_len = 0;
208 
209     // Support only for list algorithms by cipher suite
210     if (cipher::listCipherSuite !=
211             (requestData->listIndex & cipher::listTypeMask))
212     {
213         return IPMI_CC_INVALID_FIELD_REQUEST;
214     }
215 
216     if (!recordInit)
217     {
218         try
219         {
220             records = cipher::getCipherRecords();
221             recordInit = true;
222         }
223         catch (const std::exception &e)
224         {
225             return IPMI_CC_UNSPECIFIED_ERROR;
226         }
227     }
228 
229     // List index(00h-3Fh), 0h selects the first set of 16, 1h selects the next
230     // set of 16 and so on.
231     auto index = static_cast<size_t>(
232             requestData->listIndex & cipher::listIndexMask);
233 
234     // Calculate the number of record data bytes to be returned.
235     auto start = std::min(index * cipher::respSize, records.size());
236     auto end = std::min((index * cipher::respSize) + cipher::respSize,
237                         records.size());
238     auto size = end - start;
239 
240     auto responseData = reinterpret_cast<GetChannelCipherRespHeader*>
241             (response);
242     responseData->channelNumber = cipher::defaultChannelNumber;
243 
244     if (!size)
245     {
246         *data_len = sizeof(GetChannelCipherRespHeader);
247     }
248     else
249     {
250         std::copy_n(records.data() + start,
251                     size,
252                     static_cast<uint8_t*>(response) + 1);
253         *data_len = size + sizeof(GetChannelCipherRespHeader);
254     }
255 
256     return IPMI_CC_OK;
257 }
258