1 #include "channel_auth.hpp"
2 
3 #include <errno.h>
4 #include <ipmid/api.h>
5 
6 #include <ipmid/types.hpp>
7 #include <ipmid/utils.hpp>
8 #include <nlohmann/json.hpp>
9 #include <phosphor-logging/elog-errors.hpp>
10 #include <phosphor-logging/lg2.hpp>
11 #include <user_channel/channel_layer.hpp>
12 #include <user_channel/user_layer.hpp>
13 #include <xyz/openbmc_project/Common/error.hpp>
14 
15 #include <fstream>
16 #include <set>
17 #include <string>
18 
19 namespace command
20 {
21 
22 using namespace phosphor::logging;
23 using namespace sdbusplus::xyz::openbmc_project::Common::Error;
24 using Json = nlohmann::json;
25 
26 std::vector<uint8_t>
27     GetChannelCapabilities(const std::vector<uint8_t>& inPayload,
28                            std::shared_ptr<message::Handler>& /* handler */)
29 {
30     auto request =
31         reinterpret_cast<const GetChannelCapabilitiesReq*>(inPayload.data());
32     if (inPayload.size() != sizeof(*request))
33     {
34         std::vector<uint8_t> errorPayload{IPMI_CC_REQ_DATA_LEN_INVALID};
35         return errorPayload;
36     }
37     constexpr unsigned int channelMask = 0x0f;
38     uint8_t chNum = ipmi::convertCurrentChannelNum(
39         request->channelNumber & channelMask, getInterfaceIndex());
40 
41     if (!ipmi::isValidChannel(chNum) ||
42         (ipmi::EChannelSessSupported::none ==
43          ipmi::getChannelSessionSupport(chNum)) ||
44         !ipmi::isValidPrivLimit(request->reqMaxPrivLevel))
45     {
46         std::vector<uint8_t> errorPayload{IPMI_CC_INVALID_FIELD_REQUEST};
47         return errorPayload;
48     }
49 
50     std::vector<uint8_t> outPayload(sizeof(GetChannelCapabilitiesResp));
51     auto response =
52         reinterpret_cast<GetChannelCapabilitiesResp*>(outPayload.data());
53 
54     // A canned response, since there is no user and channel management.
55     response->completionCode = IPMI_CC_OK;
56 
57     response->channelNumber = chNum;
58 
59     response->ipmiVersion = 1; // IPMI v2.0 extended capabilities available.
60     response->reserved1 = 0;
61     response->oem = 0;
62     response->straightKey = 0;
63     response->reserved2 = 0;
64     response->md5 = 0;
65     response->md2 = 0;
66 
67     response->reserved3 = 0;
68     response->KGStatus = 0;       // KG is set to default
69     response->perMessageAuth = 0; // Per-message Authentication is enabled
70     response->userAuth = 0;       // User Level Authentication is enabled
71     uint8_t maxChUsers = 0;
72     uint8_t enabledUsers = 0;
73     uint8_t fixedUsers = 0;
74     ipmi::ipmiUserGetAllCounts(maxChUsers, enabledUsers, fixedUsers);
75 
76     response->nonNullUsers = enabledUsers > 0 ? 1 : 0; // Non-null usernames
77     response->nullUsers = 0;      // Null usernames disabled
78     response->anonymousLogin = 0; // Anonymous Login disabled
79 
80     response->reserved4 = 0;
81     response->extCapabilities = 0x2; // Channel supports IPMI v2.0 connections
82 
83     response->oemID[0] = 0;
84     response->oemID[1] = 0;
85     response->oemID[2] = 0;
86     response->oemAuxillary = 0;
87     return outPayload;
88 }
89 
90 static constexpr const char* configFile =
91     "/usr/share/ipmi-providers/cipher_list.json";
92 static constexpr const char* cipher = "cipher";
93 static constexpr uint8_t stdCipherSuite = 0xC0;
94 static constexpr uint8_t oemCipherSuite = 0xC1;
95 static constexpr const char* oem = "oemiana";
96 static constexpr const char* auth = "authentication";
97 static constexpr const char* integrity = "integrity";
98 static constexpr uint8_t integrityTag = 0x40;
99 static constexpr const char* conf = "confidentiality";
100 static constexpr uint8_t confTag = 0x80;
101 
102 /** @brief Get the supported Cipher records
103  *
104  * The cipher records are read from the JSON file and converted into
105  * 1. cipher suite record format mentioned in the IPMI specification. The
106  * records can be either OEM or standard cipher. Each json entry is parsed and
107  * converted into the cipher record format and pushed into the vector.
108  * 2. Algorithms listed in vector format
109  *
110  * @return pair of vector containing 1. all the cipher suite records. 2.
111  * Algorithms supported
112  *
113  */
114 static std::pair<std::vector<uint8_t>, std::vector<uint8_t>> getCipherRecords()
115 {
116     std::vector<uint8_t> cipherRecords;
117     std::vector<uint8_t> supportedAlgorithmRecords;
118     // create set to get the unique supported algorithms
119     std::set<uint8_t> supportedAlgorithmSet;
120 
121     std::ifstream jsonFile(configFile);
122     if (!jsonFile.is_open())
123     {
124         lg2::error("Channel Cipher suites file not found: {ERROR}", "ERROR",
125                    strerror(errno));
126         elog<InternalFailure>();
127     }
128 
129     auto data = Json::parse(jsonFile, nullptr, false);
130     if (data.is_discarded())
131     {
132         lg2::error("Parsing channel cipher suites JSON failed: {ERROR}",
133                    "ERROR", strerror(errno));
134         elog<InternalFailure>();
135     }
136 
137     for (const auto& record : data)
138     {
139         if (record.find(oem) != record.end())
140         {
141             // OEM cipher suite - 0xC1
142             cipherRecords.push_back(oemCipherSuite);
143             // Cipher Suite ID
144             cipherRecords.push_back(record.value(cipher, 0));
145             // OEM IANA - 3 bytes
146             cipherRecords.push_back(record.value(oem, 0));
147             cipherRecords.push_back(record.value(oem, 0) >> 8);
148             cipherRecords.push_back(record.value(oem, 0) >> 16);
149         }
150         else
151         {
152             // Standard cipher suite - 0xC0
153             cipherRecords.push_back(stdCipherSuite);
154             // Cipher Suite ID
155             cipherRecords.push_back(record.value(cipher, 0));
156         }
157 
158         // Authentication algorithm number
159         cipherRecords.push_back(record.value(auth, 0));
160         supportedAlgorithmSet.insert(record.value(auth, 0));
161 
162         // Integrity algorithm number
163         cipherRecords.push_back(record.value(integrity, 0) | integrityTag);
164         supportedAlgorithmSet.insert(record.value(integrity, 0) | integrityTag);
165 
166         // Confidentiality algorithm number
167         cipherRecords.push_back(record.value(conf, 0) | confTag);
168         supportedAlgorithmSet.insert(record.value(conf, 0) | confTag);
169     }
170 
171     // copy the set to supportedAlgorithmRecord which is vector based.
172     std::copy(supportedAlgorithmSet.begin(), supportedAlgorithmSet.end(),
173               std::back_inserter(supportedAlgorithmRecords));
174 
175     return std::make_pair(cipherRecords, supportedAlgorithmRecords);
176 }
177 
178 /** @brief this command is used to look up what authentication, integrity,
179  *  confidentiality algorithms are supported.
180  *
181  *  @ param inPayload - vector of input data
182  *  @ param handler - pointer to handler
183  *
184  *  @returns ipmi completion code plus response data
185  *   - vector of response data: cc, channel, record data
186  **/
187 std::vector<uint8_t>
188     getChannelCipherSuites(const std::vector<uint8_t>& inPayload,
189                            std::shared_ptr<message::Handler>& /* handler */)
190 {
191     const auto errorResponse = [](uint8_t cc) {
192         std::vector<uint8_t> rsp(1);
193         rsp[0] = cc;
194         return rsp;
195     };
196 
197     static constexpr size_t getChannelCipherSuitesReqLen = 3;
198     if (inPayload.size() != getChannelCipherSuitesReqLen)
199     {
200         return errorResponse(IPMI_CC_REQ_DATA_LEN_INVALID);
201     }
202 
203     static constexpr uint8_t channelMask = 0x0f;
204     uint8_t channelNumber = inPayload[0] & channelMask;
205     if (channelNumber != inPayload[0])
206     {
207         return errorResponse(IPMI_CC_INVALID_FIELD_REQUEST);
208     }
209     static constexpr uint8_t payloadMask = 0x3f;
210     uint8_t payloadType = inPayload[1] & payloadMask;
211     if (payloadType != inPayload[1])
212     {
213         return errorResponse(IPMI_CC_INVALID_FIELD_REQUEST);
214     }
215     static constexpr uint8_t indexMask = 0x3f;
216     uint8_t listIndex = inPayload[2] & indexMask;
217     static constexpr uint8_t algoSelectShift = 7;
218     uint8_t algoSelectBit = inPayload[2] >> algoSelectShift;
219     if ((listIndex | (algoSelectBit << algoSelectShift)) != inPayload[2])
220     {
221         return errorResponse(IPMI_CC_INVALID_FIELD_REQUEST);
222     }
223 
224     static std::vector<uint8_t> cipherRecords;
225     static std::vector<uint8_t> supportedAlgorithms;
226     static bool recordInit = false;
227 
228     uint8_t rspChannel = ipmi::convertCurrentChannelNum(channelNumber,
229                                                         getInterfaceIndex());
230 
231     if (!ipmi::isValidChannel(rspChannel))
232     {
233         return errorResponse(IPMI_CC_INVALID_FIELD_REQUEST);
234     }
235     if (!ipmi::isValidPayloadType(static_cast<ipmi::PayloadType>(payloadType)))
236     {
237         lg2::debug("Get channel cipher suites - Invalid payload type: {ERROR}",
238                    "ERROR", strerror(errno));
239         constexpr uint8_t ccPayloadTypeNotSupported = 0x80;
240         return errorResponse(ccPayloadTypeNotSupported);
241     }
242 
243     if (!recordInit)
244     {
245         try
246         {
247             std::tie(cipherRecords, supportedAlgorithms) = getCipherRecords();
248             recordInit = true;
249         }
250         catch (const std::exception& e)
251         {
252             return errorResponse(IPMI_CC_UNSPECIFIED_ERROR);
253         }
254     }
255 
256     const std::vector<uint8_t>& records = algoSelectBit ? cipherRecords
257                                                         : supportedAlgorithms;
258     static constexpr auto respSize = 16;
259 
260     // Session support is available in active LAN channels.
261     if ((ipmi::getChannelSessionSupport(rspChannel) ==
262          ipmi::EChannelSessSupported::none) ||
263         !(ipmi::doesDeviceExist(rspChannel)))
264     {
265         lg2::debug("Get channel cipher suites - Device does not exist:{ERROR}",
266                    "ERROR", strerror(errno));
267         return errorResponse(IPMI_CC_INVALID_FIELD_REQUEST);
268     }
269 
270     // List index(00h-3Fh), 0h selects the first set of 16, 1h selects the next
271     // set of 16 and so on.
272 
273     // Calculate the number of record data bytes to be returned.
274     auto start = std::min(static_cast<size_t>(listIndex) * respSize,
275                           records.size());
276     auto end = std::min((static_cast<size_t>(listIndex) * respSize) + respSize,
277                         records.size());
278     auto size = end - start;
279 
280     std::vector<uint8_t> rsp;
281     rsp.push_back(IPMI_CC_OK);
282     rsp.push_back(rspChannel);
283     std::copy_n(records.data() + start, size, std::back_inserter(rsp));
284 
285     return rsp;
286 }
287 
288 } // namespace command
289