blob: 3308bf67316e7024eab9b66275c05ebadd0326f9 [file] [log] [blame]
Tom Joseph4a8f34d2016-12-06 17:07:46 +05301#include "channel_auth.hpp"
2
George Liu7b7f25f2022-07-04 17:07:32 +08003#include <errno.h>
William A. Kennington III4f09eae2019-02-12 17:10:35 -08004#include <ipmid/api.h>
Tom Joseph4a8f34d2016-12-06 17:07:46 +05305
Vernon Mauery60d6e4e2021-07-26 13:51:35 -07006#include <ipmid/types.hpp>
7#include <ipmid/utils.hpp>
8#include <nlohmann/json.hpp>
9#include <phosphor-logging/elog-errors.hpp>
George Liu7b7f25f2022-07-04 17:07:32 +080010#include <phosphor-logging/lg2.hpp>
Richard Marian Thomaiyar716d1ef2019-03-11 20:08:57 +053011#include <user_channel/channel_layer.hpp>
12#include <user_channel/user_layer.hpp>
Vernon Mauery60d6e4e2021-07-26 13:51:35 -070013#include <xyz/openbmc_project/Common/error.hpp>
Richard Marian Thomaiyar716d1ef2019-03-11 20:08:57 +053014
George Liubc8958f2022-07-04 09:29:49 +080015#include <fstream>
16#include <set>
17#include <string>
18
Tom Joseph4a8f34d2016-12-06 17:07:46 +053019namespace command
20{
21
Vernon Mauery60d6e4e2021-07-26 13:51:35 -070022using namespace phosphor::logging;
23using namespace sdbusplus::xyz::openbmc_project::Common::Error;
24using Json = nlohmann::json;
25
Vernon Mauery9e801a22018-10-12 13:20:49 -070026std::vector<uint8_t>
27 GetChannelCapabilities(const std::vector<uint8_t>& inPayload,
George Liube1470c2022-07-04 14:33:24 +080028 std::shared_ptr<message::Handler>& /* handler */)
Tom Joseph4a8f34d2016-12-06 17:07:46 +053029{
Richard Marian Thomaiyar716d1ef2019-03-11 20:08:57 +053030 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 }
Vernon Mauery052b7cf2019-05-08 15:59:41 -070037 constexpr unsigned int channelMask = 0x0f;
38 uint8_t chNum = ipmi::convertCurrentChannelNum(
39 request->channelNumber & channelMask, getInterfaceIndex());
40
Richard Marian Thomaiyar716d1ef2019-03-11 20:08:57 +053041 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
Tom Joseph4a8f34d2016-12-06 17:07:46 +053050 std::vector<uint8_t> outPayload(sizeof(GetChannelCapabilitiesResp));
Vernon Mauery9e801a22018-10-12 13:20:49 -070051 auto response =
52 reinterpret_cast<GetChannelCapabilitiesResp*>(outPayload.data());
Tom Joseph4a8f34d2016-12-06 17:07:46 +053053
54 // A canned response, since there is no user and channel management.
Vernon Mauery9e801a22018-10-12 13:20:49 -070055 response->completionCode = IPMI_CC_OK;
Tom Joseph4a8f34d2016-12-06 17:07:46 +053056
Richard Marian Thomaiyar716d1ef2019-03-11 20:08:57 +053057 response->channelNumber = chNum;
Tom Joseph4a8f34d2016-12-06 17:07:46 +053058
Vernon Mauery9e801a22018-10-12 13:20:49 -070059 response->ipmiVersion = 1; // IPMI v2.0 extended capabilities available.
Tom Joseph4a8f34d2016-12-06 17:07:46 +053060 response->reserved1 = 0;
61 response->oem = 0;
62 response->straightKey = 0;
63 response->reserved2 = 0;
64 response->md5 = 0;
65 response->md2 = 0;
66
Tom Joseph4a8f34d2016-12-06 17:07:46 +053067 response->reserved3 = 0;
Vernon Mauery9e801a22018-10-12 13:20:49 -070068 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
Richard Marian Thomaiyar716d1ef2019-03-11 20:08:57 +053071 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
Tom Joseph615e4fd2019-02-09 23:10:48 +053077 response->nullUsers = 0; // Null usernames disabled
Vernon Mauery9e801a22018-10-12 13:20:49 -070078 response->anonymousLogin = 0; // Anonymous Login disabled
Tom Joseph4a8f34d2016-12-06 17:07:46 +053079
80 response->reserved4 = 0;
Vernon Mauery9e801a22018-10-12 13:20:49 -070081 response->extCapabilities = 0x2; // Channel supports IPMI v2.0 connections
Tom Joseph4a8f34d2016-12-06 17:07:46 +053082
83 response->oemID[0] = 0;
84 response->oemID[1] = 0;
85 response->oemID[2] = 0;
86 response->oemAuxillary = 0;
Tom Joseph4a8f34d2016-12-06 17:07:46 +053087 return outPayload;
88}
89
Vernon Mauery60d6e4e2021-07-26 13:51:35 -070090static constexpr const char* configFile =
91 "/usr/share/ipmi-providers/cipher_list.json";
92static constexpr const char* cipher = "cipher";
93static constexpr uint8_t stdCipherSuite = 0xC0;
94static constexpr uint8_t oemCipherSuite = 0xC1;
95static constexpr const char* oem = "oemiana";
96static constexpr const char* auth = "authentication";
97static constexpr const char* integrity = "integrity";
98static constexpr uint8_t integrityTag = 0x40;
99static constexpr const char* conf = "confidentiality";
100static 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 */
114static 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 {
George Liu7b7f25f2022-07-04 17:07:32 +0800124 lg2::error("Channel Cipher suites file not found: {ERROR}", "ERROR",
125 strerror(errno));
Vernon Mauery60d6e4e2021-07-26 13:51:35 -0700126 elog<InternalFailure>();
127 }
128
129 auto data = Json::parse(jsonFile, nullptr, false);
130 if (data.is_discarded())
131 {
George Liu7b7f25f2022-07-04 17:07:32 +0800132 lg2::error("Parsing channel cipher suites JSON failed: {ERROR}",
133 "ERROR", strerror(errno));
Vernon Mauery60d6e4e2021-07-26 13:51:35 -0700134 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 **/
187std::vector<uint8_t>
188 getChannelCipherSuites(const std::vector<uint8_t>& inPayload,
George Liube1470c2022-07-04 14:33:24 +0800189 std::shared_ptr<message::Handler>& /* handler */)
Vernon Mauery60d6e4e2021-07-26 13:51:35 -0700190{
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
Patrick Williams099fb092023-05-10 07:50:31 -0500228 uint8_t rspChannel = ipmi::convertCurrentChannelNum(channelNumber,
229 getInterfaceIndex());
Vernon Mauery60d6e4e2021-07-26 13:51:35 -0700230
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 {
George Liu7b7f25f2022-07-04 17:07:32 +0800237 lg2::debug("Get channel cipher suites - Invalid payload type: {ERROR}",
238 "ERROR", strerror(errno));
Vernon Mauery60d6e4e2021-07-26 13:51:35 -0700239 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
Patrick Williams099fb092023-05-10 07:50:31 -0500256 const std::vector<uint8_t>& records = algoSelectBit ? cipherRecords
257 : supportedAlgorithms;
Vernon Mauery60d6e4e2021-07-26 13:51:35 -0700258 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 {
George Liu7b7f25f2022-07-04 17:07:32 +0800265 lg2::debug("Get channel cipher suites - Device does not exist:{ERROR}",
266 "ERROR", strerror(errno));
Vernon Mauery60d6e4e2021-07-26 13:51:35 -0700267 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.
Patrick Williams099fb092023-05-10 07:50:31 -0500274 auto start = std::min(static_cast<size_t>(listIndex) * respSize,
275 records.size());
Vernon Mauery60d6e4e2021-07-26 13:51:35 -0700276 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
Tom Joseph4a8f34d2016-12-06 17:07:46 +0530288} // namespace command