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