14a8f34dcSTom Joseph #include "channel_auth.hpp"
24a8f34dcSTom Joseph
37b7f25f7SGeorge Liu #include <errno.h>
44f09eaeeSWilliam A. Kennington III #include <ipmid/api.h>
54a8f34dcSTom Joseph
660d6e4edSVernon Mauery #include <ipmid/types.hpp>
760d6e4edSVernon Mauery #include <ipmid/utils.hpp>
860d6e4edSVernon Mauery #include <nlohmann/json.hpp>
960d6e4edSVernon Mauery #include <phosphor-logging/elog-errors.hpp>
107b7f25f7SGeorge Liu #include <phosphor-logging/lg2.hpp>
11716d1efeSRichard Marian Thomaiyar #include <user_channel/channel_layer.hpp>
12716d1efeSRichard Marian Thomaiyar #include <user_channel/user_layer.hpp>
1360d6e4edSVernon Mauery #include <xyz/openbmc_project/Common/error.hpp>
14716d1efeSRichard Marian Thomaiyar
15bc8958feSGeorge Liu #include <fstream>
16bc8958feSGeorge Liu #include <set>
17bc8958feSGeorge Liu #include <string>
18bc8958feSGeorge Liu
194a8f34dcSTom Joseph namespace command
204a8f34dcSTom Joseph {
214a8f34dcSTom Joseph
2260d6e4edSVernon Mauery using namespace phosphor::logging;
2360d6e4edSVernon Mauery using namespace sdbusplus::xyz::openbmc_project::Common::Error;
2460d6e4edSVernon Mauery using Json = nlohmann::json;
2560d6e4edSVernon Mauery
GetChannelCapabilities(const std::vector<uint8_t> & inPayload,std::shared_ptr<message::Handler> &)26*33503e2aSPatrick Williams std::vector<uint8_t> GetChannelCapabilities(
27*33503e2aSPatrick Williams const std::vector<uint8_t>& inPayload,
28be1470ccSGeorge Liu std::shared_ptr<message::Handler>& /* handler */)
294a8f34dcSTom Joseph {
30716d1efeSRichard Marian Thomaiyar auto request =
31716d1efeSRichard Marian Thomaiyar reinterpret_cast<const GetChannelCapabilitiesReq*>(inPayload.data());
32716d1efeSRichard Marian Thomaiyar if (inPayload.size() != sizeof(*request))
33716d1efeSRichard Marian Thomaiyar {
34716d1efeSRichard Marian Thomaiyar std::vector<uint8_t> errorPayload{IPMI_CC_REQ_DATA_LEN_INVALID};
35716d1efeSRichard Marian Thomaiyar return errorPayload;
36716d1efeSRichard Marian Thomaiyar }
37052b7cf3SVernon Mauery constexpr unsigned int channelMask = 0x0f;
38052b7cf3SVernon Mauery uint8_t chNum = ipmi::convertCurrentChannelNum(
39052b7cf3SVernon Mauery request->channelNumber & channelMask, getInterfaceIndex());
40052b7cf3SVernon Mauery
41716d1efeSRichard Marian Thomaiyar if (!ipmi::isValidChannel(chNum) ||
42716d1efeSRichard Marian Thomaiyar (ipmi::EChannelSessSupported::none ==
43716d1efeSRichard Marian Thomaiyar ipmi::getChannelSessionSupport(chNum)) ||
44716d1efeSRichard Marian Thomaiyar !ipmi::isValidPrivLimit(request->reqMaxPrivLevel))
45716d1efeSRichard Marian Thomaiyar {
46716d1efeSRichard Marian Thomaiyar std::vector<uint8_t> errorPayload{IPMI_CC_INVALID_FIELD_REQUEST};
47716d1efeSRichard Marian Thomaiyar return errorPayload;
48716d1efeSRichard Marian Thomaiyar }
49716d1efeSRichard Marian Thomaiyar
504a8f34dcSTom Joseph std::vector<uint8_t> outPayload(sizeof(GetChannelCapabilitiesResp));
519e801a2bSVernon Mauery auto response =
529e801a2bSVernon Mauery reinterpret_cast<GetChannelCapabilitiesResp*>(outPayload.data());
534a8f34dcSTom Joseph
544a8f34dcSTom Joseph // A canned response, since there is no user and channel management.
554a8f34dcSTom Joseph response->completionCode = IPMI_CC_OK;
564a8f34dcSTom Joseph
57716d1efeSRichard Marian Thomaiyar response->channelNumber = chNum;
584a8f34dcSTom Joseph
594a8f34dcSTom Joseph response->ipmiVersion = 1; // IPMI v2.0 extended capabilities available.
604a8f34dcSTom Joseph response->reserved1 = 0;
614a8f34dcSTom Joseph response->oem = 0;
624a8f34dcSTom Joseph response->straightKey = 0;
634a8f34dcSTom Joseph response->reserved2 = 0;
644a8f34dcSTom Joseph response->md5 = 0;
654a8f34dcSTom Joseph response->md2 = 0;
664a8f34dcSTom Joseph
674a8f34dcSTom Joseph response->reserved3 = 0;
684a8f34dcSTom Joseph response->KGStatus = 0; // KG is set to default
694a8f34dcSTom Joseph response->perMessageAuth = 0; // Per-message Authentication is enabled
704a8f34dcSTom Joseph response->userAuth = 0; // User Level Authentication is enabled
71716d1efeSRichard Marian Thomaiyar uint8_t maxChUsers = 0;
72716d1efeSRichard Marian Thomaiyar uint8_t enabledUsers = 0;
73716d1efeSRichard Marian Thomaiyar uint8_t fixedUsers = 0;
74716d1efeSRichard Marian Thomaiyar ipmi::ipmiUserGetAllCounts(maxChUsers, enabledUsers, fixedUsers);
75716d1efeSRichard Marian Thomaiyar
76716d1efeSRichard Marian Thomaiyar response->nonNullUsers = enabledUsers > 0 ? 1 : 0; // Non-null usernames
77615e4fd3STom Joseph response->nullUsers = 0; // Null usernames disabled
784a8f34dcSTom Joseph response->anonymousLogin = 0; // Anonymous Login disabled
794a8f34dcSTom Joseph
804a8f34dcSTom Joseph response->reserved4 = 0;
814a8f34dcSTom Joseph response->extCapabilities = 0x2; // Channel supports IPMI v2.0 connections
824a8f34dcSTom Joseph
834a8f34dcSTom Joseph response->oemID[0] = 0;
844a8f34dcSTom Joseph response->oemID[1] = 0;
854a8f34dcSTom Joseph response->oemID[2] = 0;
864a8f34dcSTom Joseph response->oemAuxillary = 0;
874a8f34dcSTom Joseph return outPayload;
884a8f34dcSTom Joseph }
894a8f34dcSTom Joseph
9060d6e4edSVernon Mauery static constexpr const char* configFile =
9160d6e4edSVernon Mauery "/usr/share/ipmi-providers/cipher_list.json";
9260d6e4edSVernon Mauery static constexpr const char* cipher = "cipher";
9360d6e4edSVernon Mauery static constexpr uint8_t stdCipherSuite = 0xC0;
9460d6e4edSVernon Mauery static constexpr uint8_t oemCipherSuite = 0xC1;
9560d6e4edSVernon Mauery static constexpr const char* oem = "oemiana";
9660d6e4edSVernon Mauery static constexpr const char* auth = "authentication";
9760d6e4edSVernon Mauery static constexpr const char* integrity = "integrity";
9860d6e4edSVernon Mauery static constexpr uint8_t integrityTag = 0x40;
9960d6e4edSVernon Mauery static constexpr const char* conf = "confidentiality";
10060d6e4edSVernon Mauery static constexpr uint8_t confTag = 0x80;
10160d6e4edSVernon Mauery
10260d6e4edSVernon Mauery /** @brief Get the supported Cipher records
10360d6e4edSVernon Mauery *
10460d6e4edSVernon Mauery * The cipher records are read from the JSON file and converted into
10560d6e4edSVernon Mauery * 1. cipher suite record format mentioned in the IPMI specification. The
10660d6e4edSVernon Mauery * records can be either OEM or standard cipher. Each json entry is parsed and
10760d6e4edSVernon Mauery * converted into the cipher record format and pushed into the vector.
10860d6e4edSVernon Mauery * 2. Algorithms listed in vector format
10960d6e4edSVernon Mauery *
11060d6e4edSVernon Mauery * @return pair of vector containing 1. all the cipher suite records. 2.
11160d6e4edSVernon Mauery * Algorithms supported
11260d6e4edSVernon Mauery *
11360d6e4edSVernon Mauery */
getCipherRecords()11460d6e4edSVernon Mauery static std::pair<std::vector<uint8_t>, std::vector<uint8_t>> getCipherRecords()
11560d6e4edSVernon Mauery {
11660d6e4edSVernon Mauery std::vector<uint8_t> cipherRecords;
11760d6e4edSVernon Mauery std::vector<uint8_t> supportedAlgorithmRecords;
11860d6e4edSVernon Mauery // create set to get the unique supported algorithms
11960d6e4edSVernon Mauery std::set<uint8_t> supportedAlgorithmSet;
12060d6e4edSVernon Mauery
12160d6e4edSVernon Mauery std::ifstream jsonFile(configFile);
12260d6e4edSVernon Mauery if (!jsonFile.is_open())
12360d6e4edSVernon Mauery {
1247b7f25f7SGeorge Liu lg2::error("Channel Cipher suites file not found: {ERROR}", "ERROR",
1257b7f25f7SGeorge Liu strerror(errno));
12660d6e4edSVernon Mauery elog<InternalFailure>();
12760d6e4edSVernon Mauery }
12860d6e4edSVernon Mauery
12960d6e4edSVernon Mauery auto data = Json::parse(jsonFile, nullptr, false);
13060d6e4edSVernon Mauery if (data.is_discarded())
13160d6e4edSVernon Mauery {
1327b7f25f7SGeorge Liu lg2::error("Parsing channel cipher suites JSON failed: {ERROR}",
1337b7f25f7SGeorge Liu "ERROR", strerror(errno));
13460d6e4edSVernon Mauery elog<InternalFailure>();
13560d6e4edSVernon Mauery }
13660d6e4edSVernon Mauery
13760d6e4edSVernon Mauery for (const auto& record : data)
13860d6e4edSVernon Mauery {
13960d6e4edSVernon Mauery if (record.find(oem) != record.end())
14060d6e4edSVernon Mauery {
14160d6e4edSVernon Mauery // OEM cipher suite - 0xC1
14260d6e4edSVernon Mauery cipherRecords.push_back(oemCipherSuite);
14360d6e4edSVernon Mauery // Cipher Suite ID
14460d6e4edSVernon Mauery cipherRecords.push_back(record.value(cipher, 0));
14560d6e4edSVernon Mauery // OEM IANA - 3 bytes
14660d6e4edSVernon Mauery cipherRecords.push_back(record.value(oem, 0));
14760d6e4edSVernon Mauery cipherRecords.push_back(record.value(oem, 0) >> 8);
14860d6e4edSVernon Mauery cipherRecords.push_back(record.value(oem, 0) >> 16);
14960d6e4edSVernon Mauery }
15060d6e4edSVernon Mauery else
15160d6e4edSVernon Mauery {
15260d6e4edSVernon Mauery // Standard cipher suite - 0xC0
15360d6e4edSVernon Mauery cipherRecords.push_back(stdCipherSuite);
15460d6e4edSVernon Mauery // Cipher Suite ID
15560d6e4edSVernon Mauery cipherRecords.push_back(record.value(cipher, 0));
15660d6e4edSVernon Mauery }
15760d6e4edSVernon Mauery
15860d6e4edSVernon Mauery // Authentication algorithm number
15960d6e4edSVernon Mauery cipherRecords.push_back(record.value(auth, 0));
16060d6e4edSVernon Mauery supportedAlgorithmSet.insert(record.value(auth, 0));
16160d6e4edSVernon Mauery
16260d6e4edSVernon Mauery // Integrity algorithm number
16360d6e4edSVernon Mauery cipherRecords.push_back(record.value(integrity, 0) | integrityTag);
16460d6e4edSVernon Mauery supportedAlgorithmSet.insert(record.value(integrity, 0) | integrityTag);
16560d6e4edSVernon Mauery
16660d6e4edSVernon Mauery // Confidentiality algorithm number
16760d6e4edSVernon Mauery cipherRecords.push_back(record.value(conf, 0) | confTag);
16860d6e4edSVernon Mauery supportedAlgorithmSet.insert(record.value(conf, 0) | confTag);
16960d6e4edSVernon Mauery }
17060d6e4edSVernon Mauery
17160d6e4edSVernon Mauery // copy the set to supportedAlgorithmRecord which is vector based.
17260d6e4edSVernon Mauery std::copy(supportedAlgorithmSet.begin(), supportedAlgorithmSet.end(),
17360d6e4edSVernon Mauery std::back_inserter(supportedAlgorithmRecords));
17460d6e4edSVernon Mauery
17560d6e4edSVernon Mauery return std::make_pair(cipherRecords, supportedAlgorithmRecords);
17660d6e4edSVernon Mauery }
17760d6e4edSVernon Mauery
17860d6e4edSVernon Mauery /** @brief this command is used to look up what authentication, integrity,
17960d6e4edSVernon Mauery * confidentiality algorithms are supported.
18060d6e4edSVernon Mauery *
18160d6e4edSVernon Mauery * @ param inPayload - vector of input data
18260d6e4edSVernon Mauery * @ param handler - pointer to handler
18360d6e4edSVernon Mauery *
18460d6e4edSVernon Mauery * @returns ipmi completion code plus response data
18560d6e4edSVernon Mauery * - vector of response data: cc, channel, record data
18660d6e4edSVernon Mauery **/
getChannelCipherSuites(const std::vector<uint8_t> & inPayload,std::shared_ptr<message::Handler> &)187*33503e2aSPatrick Williams std::vector<uint8_t> getChannelCipherSuites(
188*33503e2aSPatrick Williams const std::vector<uint8_t>& inPayload,
189be1470ccSGeorge Liu std::shared_ptr<message::Handler>& /* handler */)
19060d6e4edSVernon Mauery {
19160d6e4edSVernon Mauery const auto errorResponse = [](uint8_t cc) {
19260d6e4edSVernon Mauery std::vector<uint8_t> rsp(1);
19360d6e4edSVernon Mauery rsp[0] = cc;
19460d6e4edSVernon Mauery return rsp;
19560d6e4edSVernon Mauery };
19660d6e4edSVernon Mauery
19760d6e4edSVernon Mauery static constexpr size_t getChannelCipherSuitesReqLen = 3;
19860d6e4edSVernon Mauery if (inPayload.size() != getChannelCipherSuitesReqLen)
19960d6e4edSVernon Mauery {
20060d6e4edSVernon Mauery return errorResponse(IPMI_CC_REQ_DATA_LEN_INVALID);
20160d6e4edSVernon Mauery }
20260d6e4edSVernon Mauery
20360d6e4edSVernon Mauery static constexpr uint8_t channelMask = 0x0f;
20460d6e4edSVernon Mauery uint8_t channelNumber = inPayload[0] & channelMask;
20560d6e4edSVernon Mauery if (channelNumber != inPayload[0])
20660d6e4edSVernon Mauery {
20760d6e4edSVernon Mauery return errorResponse(IPMI_CC_INVALID_FIELD_REQUEST);
20860d6e4edSVernon Mauery }
20960d6e4edSVernon Mauery static constexpr uint8_t payloadMask = 0x3f;
21060d6e4edSVernon Mauery uint8_t payloadType = inPayload[1] & payloadMask;
21160d6e4edSVernon Mauery if (payloadType != inPayload[1])
21260d6e4edSVernon Mauery {
21360d6e4edSVernon Mauery return errorResponse(IPMI_CC_INVALID_FIELD_REQUEST);
21460d6e4edSVernon Mauery }
21560d6e4edSVernon Mauery static constexpr uint8_t indexMask = 0x3f;
21660d6e4edSVernon Mauery uint8_t listIndex = inPayload[2] & indexMask;
21760d6e4edSVernon Mauery static constexpr uint8_t algoSelectShift = 7;
21860d6e4edSVernon Mauery uint8_t algoSelectBit = inPayload[2] >> algoSelectShift;
21960d6e4edSVernon Mauery if ((listIndex | (algoSelectBit << algoSelectShift)) != inPayload[2])
22060d6e4edSVernon Mauery {
22160d6e4edSVernon Mauery return errorResponse(IPMI_CC_INVALID_FIELD_REQUEST);
22260d6e4edSVernon Mauery }
22360d6e4edSVernon Mauery
22460d6e4edSVernon Mauery static std::vector<uint8_t> cipherRecords;
22560d6e4edSVernon Mauery static std::vector<uint8_t> supportedAlgorithms;
22660d6e4edSVernon Mauery static bool recordInit = false;
22760d6e4edSVernon Mauery
2288425624aSPatrick Williams uint8_t rspChannel =
2298425624aSPatrick Williams ipmi::convertCurrentChannelNum(channelNumber, getInterfaceIndex());
23060d6e4edSVernon Mauery
23160d6e4edSVernon Mauery if (!ipmi::isValidChannel(rspChannel))
23260d6e4edSVernon Mauery {
23360d6e4edSVernon Mauery return errorResponse(IPMI_CC_INVALID_FIELD_REQUEST);
23460d6e4edSVernon Mauery }
23560d6e4edSVernon Mauery if (!ipmi::isValidPayloadType(static_cast<ipmi::PayloadType>(payloadType)))
23660d6e4edSVernon Mauery {
2377b7f25f7SGeorge Liu lg2::debug("Get channel cipher suites - Invalid payload type: {ERROR}",
2387b7f25f7SGeorge Liu "ERROR", strerror(errno));
23960d6e4edSVernon Mauery constexpr uint8_t ccPayloadTypeNotSupported = 0x80;
24060d6e4edSVernon Mauery return errorResponse(ccPayloadTypeNotSupported);
24160d6e4edSVernon Mauery }
24260d6e4edSVernon Mauery
24360d6e4edSVernon Mauery if (!recordInit)
24460d6e4edSVernon Mauery {
24560d6e4edSVernon Mauery try
24660d6e4edSVernon Mauery {
24760d6e4edSVernon Mauery std::tie(cipherRecords, supportedAlgorithms) = getCipherRecords();
24860d6e4edSVernon Mauery recordInit = true;
24960d6e4edSVernon Mauery }
25060d6e4edSVernon Mauery catch (const std::exception& e)
25160d6e4edSVernon Mauery {
25260d6e4edSVernon Mauery return errorResponse(IPMI_CC_UNSPECIFIED_ERROR);
25360d6e4edSVernon Mauery }
25460d6e4edSVernon Mauery }
25560d6e4edSVernon Mauery
2568425624aSPatrick Williams const std::vector<uint8_t>& records =
2578425624aSPatrick Williams algoSelectBit ? cipherRecords : supportedAlgorithms;
25860d6e4edSVernon Mauery static constexpr auto respSize = 16;
25960d6e4edSVernon Mauery
26060d6e4edSVernon Mauery // Session support is available in active LAN channels.
26160d6e4edSVernon Mauery if ((ipmi::getChannelSessionSupport(rspChannel) ==
26260d6e4edSVernon Mauery ipmi::EChannelSessSupported::none) ||
26360d6e4edSVernon Mauery !(ipmi::doesDeviceExist(rspChannel)))
26460d6e4edSVernon Mauery {
2657b7f25f7SGeorge Liu lg2::debug("Get channel cipher suites - Device does not exist:{ERROR}",
2667b7f25f7SGeorge Liu "ERROR", strerror(errno));
26760d6e4edSVernon Mauery return errorResponse(IPMI_CC_INVALID_FIELD_REQUEST);
26860d6e4edSVernon Mauery }
26960d6e4edSVernon Mauery
27060d6e4edSVernon Mauery // List index(00h-3Fh), 0h selects the first set of 16, 1h selects the next
27160d6e4edSVernon Mauery // set of 16 and so on.
27260d6e4edSVernon Mauery
27360d6e4edSVernon Mauery // Calculate the number of record data bytes to be returned.
2748425624aSPatrick Williams auto start =
2758425624aSPatrick Williams std::min(static_cast<size_t>(listIndex) * respSize, records.size());
27660d6e4edSVernon Mauery auto end = std::min((static_cast<size_t>(listIndex) * respSize) + respSize,
27760d6e4edSVernon Mauery records.size());
27860d6e4edSVernon Mauery auto size = end - start;
27960d6e4edSVernon Mauery
28060d6e4edSVernon Mauery std::vector<uint8_t> rsp;
28160d6e4edSVernon Mauery rsp.push_back(IPMI_CC_OK);
28260d6e4edSVernon Mauery rsp.push_back(rspChannel);
28360d6e4edSVernon Mauery std::copy_n(records.data() + start, size, std::back_inserter(rsp));
28460d6e4edSVernon Mauery
28560d6e4edSVernon Mauery return rsp;
28660d6e4edSVernon Mauery }
28760d6e4edSVernon Mauery
2884a8f34dcSTom Joseph } // namespace command
289