blob: e83ad95b36544cbee992a6d4be673ad3c44d7ab4 [file] [log] [blame]
Tom Joseph4a8f34d2016-12-06 17:07:46 +05301#include "channel_auth.hpp"
2
William A. Kennington III4f09eae2019-02-12 17:10:35 -08003#include <ipmid/api.h>
Tom Joseph4a8f34d2016-12-06 17:07:46 +05304
Vernon Mauery60d6e4e2021-07-26 13:51:35 -07005#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>
Richard Marian Thomaiyar716d1ef2019-03-11 20:08:57 +053010#include <user_channel/channel_layer.hpp>
11#include <user_channel/user_layer.hpp>
Vernon Mauery60d6e4e2021-07-26 13:51:35 -070012#include <xyz/openbmc_project/Common/error.hpp>
Richard Marian Thomaiyar716d1ef2019-03-11 20:08:57 +053013
George Liubc8958f2022-07-04 09:29:49 +080014#include <fstream>
15#include <set>
16#include <string>
17
Tom Joseph4a8f34d2016-12-06 17:07:46 +053018namespace command
19{
20
Vernon Mauery60d6e4e2021-07-26 13:51:35 -070021using namespace phosphor::logging;
22using namespace sdbusplus::xyz::openbmc_project::Common::Error;
23using Json = nlohmann::json;
24
Vernon Mauery9e801a22018-10-12 13:20:49 -070025std::vector<uint8_t>
26 GetChannelCapabilities(const std::vector<uint8_t>& inPayload,
Vernon Mauery41ff9b52021-06-11 11:37:40 -070027 std::shared_ptr<message::Handler>& handler)
Tom Joseph4a8f34d2016-12-06 17:07:46 +053028{
Richard Marian Thomaiyar716d1ef2019-03-11 20:08:57 +053029 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 }
Vernon Mauery052b7cf2019-05-08 15:59:41 -070036 constexpr unsigned int channelMask = 0x0f;
37 uint8_t chNum = ipmi::convertCurrentChannelNum(
38 request->channelNumber & channelMask, getInterfaceIndex());
39
Richard Marian Thomaiyar716d1ef2019-03-11 20:08:57 +053040 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
Tom Joseph4a8f34d2016-12-06 17:07:46 +053049 std::vector<uint8_t> outPayload(sizeof(GetChannelCapabilitiesResp));
Vernon Mauery9e801a22018-10-12 13:20:49 -070050 auto response =
51 reinterpret_cast<GetChannelCapabilitiesResp*>(outPayload.data());
Tom Joseph4a8f34d2016-12-06 17:07:46 +053052
53 // A canned response, since there is no user and channel management.
Vernon Mauery9e801a22018-10-12 13:20:49 -070054 response->completionCode = IPMI_CC_OK;
Tom Joseph4a8f34d2016-12-06 17:07:46 +053055
Richard Marian Thomaiyar716d1ef2019-03-11 20:08:57 +053056 response->channelNumber = chNum;
Tom Joseph4a8f34d2016-12-06 17:07:46 +053057
Vernon Mauery9e801a22018-10-12 13:20:49 -070058 response->ipmiVersion = 1; // IPMI v2.0 extended capabilities available.
Tom Joseph4a8f34d2016-12-06 17:07:46 +053059 response->reserved1 = 0;
60 response->oem = 0;
61 response->straightKey = 0;
62 response->reserved2 = 0;
63 response->md5 = 0;
64 response->md2 = 0;
65
Tom Joseph4a8f34d2016-12-06 17:07:46 +053066 response->reserved3 = 0;
Vernon Mauery9e801a22018-10-12 13:20:49 -070067 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
Richard Marian Thomaiyar716d1ef2019-03-11 20:08:57 +053070 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
Tom Joseph615e4fd2019-02-09 23:10:48 +053076 response->nullUsers = 0; // Null usernames disabled
Vernon Mauery9e801a22018-10-12 13:20:49 -070077 response->anonymousLogin = 0; // Anonymous Login disabled
Tom Joseph4a8f34d2016-12-06 17:07:46 +053078
79 response->reserved4 = 0;
Vernon Mauery9e801a22018-10-12 13:20:49 -070080 response->extCapabilities = 0x2; // Channel supports IPMI v2.0 connections
Tom Joseph4a8f34d2016-12-06 17:07:46 +053081
82 response->oemID[0] = 0;
83 response->oemID[1] = 0;
84 response->oemID[2] = 0;
85 response->oemAuxillary = 0;
Tom Joseph4a8f34d2016-12-06 17:07:46 +053086 return outPayload;
87}
88
Vernon Mauery60d6e4e2021-07-26 13:51:35 -070089static constexpr const char* configFile =
90 "/usr/share/ipmi-providers/cipher_list.json";
91static constexpr const char* cipher = "cipher";
92static constexpr uint8_t stdCipherSuite = 0xC0;
93static constexpr uint8_t oemCipherSuite = 0xC1;
94static constexpr const char* oem = "oemiana";
95static constexpr const char* auth = "authentication";
96static constexpr const char* integrity = "integrity";
97static constexpr uint8_t integrityTag = 0x40;
98static constexpr const char* conf = "confidentiality";
99static 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 */
113static 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 **/
184std::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
Tom Joseph4a8f34d2016-12-06 17:07:46 +0530283} // namespace command