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