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