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>
GetChannelCapabilities(const std::vector<uint8_t> & inPayload,std::shared_ptr<message::Handler> &)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 */
getCipherRecords()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>
getChannelCipherSuites(const std::vector<uint8_t> & inPayload,std::shared_ptr<message::Handler> &)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 =
229 ipmi::convertCurrentChannelNum(channelNumber, 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 =
257 algoSelectBit ? cipherRecords : 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 =
275 std::min(static_cast<size_t>(listIndex) * respSize, 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