blob: fd1fa5beeb216afebac3c0014751158eb6999e4c [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 <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>
Richard Marian Thomaiyar716d1ef2019-03-11 20:08:57 +053013#include <user_channel/channel_layer.hpp>
14#include <user_channel/user_layer.hpp>
Vernon Mauery60d6e4e2021-07-26 13:51:35 -070015#include <xyz/openbmc_project/Common/error.hpp>
Richard Marian Thomaiyar716d1ef2019-03-11 20:08:57 +053016
Tom Joseph4a8f34d2016-12-06 17:07:46 +053017namespace command
18{
19
Vernon Mauery60d6e4e2021-07-26 13:51:35 -070020using namespace phosphor::logging;
21using namespace sdbusplus::xyz::openbmc_project::Common::Error;
22using Json = nlohmann::json;
23
Vernon Mauery9e801a22018-10-12 13:20:49 -070024std::vector<uint8_t>
25 GetChannelCapabilities(const std::vector<uint8_t>& inPayload,
Vernon Mauery41ff9b52021-06-11 11:37:40 -070026 std::shared_ptr<message::Handler>& handler)
Tom Joseph4a8f34d2016-12-06 17:07:46 +053027{
Richard Marian Thomaiyar716d1ef2019-03-11 20:08:57 +053028 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 }
Vernon Mauery052b7cf2019-05-08 15:59:41 -070035 constexpr unsigned int channelMask = 0x0f;
36 uint8_t chNum = ipmi::convertCurrentChannelNum(
37 request->channelNumber & channelMask, getInterfaceIndex());
38
Richard Marian Thomaiyar716d1ef2019-03-11 20:08:57 +053039 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
Tom Joseph4a8f34d2016-12-06 17:07:46 +053048 std::vector<uint8_t> outPayload(sizeof(GetChannelCapabilitiesResp));
Vernon Mauery9e801a22018-10-12 13:20:49 -070049 auto response =
50 reinterpret_cast<GetChannelCapabilitiesResp*>(outPayload.data());
Tom Joseph4a8f34d2016-12-06 17:07:46 +053051
52 // A canned response, since there is no user and channel management.
Vernon Mauery9e801a22018-10-12 13:20:49 -070053 response->completionCode = IPMI_CC_OK;
Tom Joseph4a8f34d2016-12-06 17:07:46 +053054
Richard Marian Thomaiyar716d1ef2019-03-11 20:08:57 +053055 response->channelNumber = chNum;
Tom Joseph4a8f34d2016-12-06 17:07:46 +053056
Vernon Mauery9e801a22018-10-12 13:20:49 -070057 response->ipmiVersion = 1; // IPMI v2.0 extended capabilities available.
Tom Joseph4a8f34d2016-12-06 17:07:46 +053058 response->reserved1 = 0;
59 response->oem = 0;
60 response->straightKey = 0;
61 response->reserved2 = 0;
62 response->md5 = 0;
63 response->md2 = 0;
64
Tom Joseph4a8f34d2016-12-06 17:07:46 +053065 response->reserved3 = 0;
Vernon Mauery9e801a22018-10-12 13:20:49 -070066 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
Richard Marian Thomaiyar716d1ef2019-03-11 20:08:57 +053069 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
Tom Joseph615e4fd2019-02-09 23:10:48 +053075 response->nullUsers = 0; // Null usernames disabled
Vernon Mauery9e801a22018-10-12 13:20:49 -070076 response->anonymousLogin = 0; // Anonymous Login disabled
Tom Joseph4a8f34d2016-12-06 17:07:46 +053077
78 response->reserved4 = 0;
Vernon Mauery9e801a22018-10-12 13:20:49 -070079 response->extCapabilities = 0x2; // Channel supports IPMI v2.0 connections
Tom Joseph4a8f34d2016-12-06 17:07:46 +053080
81 response->oemID[0] = 0;
82 response->oemID[1] = 0;
83 response->oemID[2] = 0;
84 response->oemAuxillary = 0;
Tom Joseph4a8f34d2016-12-06 17:07:46 +053085 return outPayload;
86}
87
Vernon Mauery60d6e4e2021-07-26 13:51:35 -070088static constexpr const char* configFile =
89 "/usr/share/ipmi-providers/cipher_list.json";
90static constexpr const char* cipher = "cipher";
91static constexpr uint8_t stdCipherSuite = 0xC0;
92static constexpr uint8_t oemCipherSuite = 0xC1;
93static constexpr const char* oem = "oemiana";
94static constexpr const char* auth = "authentication";
95static constexpr const char* integrity = "integrity";
96static constexpr uint8_t integrityTag = 0x40;
97static constexpr const char* conf = "confidentiality";
98static 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 */
112static 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 **/
183std::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
Tom Joseph4a8f34d2016-12-06 17:07:46 +0530282} // namespace command