xref: /openbmc/phosphor-host-ipmid/app/channel.cpp (revision cc96b4250de30584897c9fcdebdcee3a2357a8e3)
1 #include "channel.hpp"
2 
3 #include "user_channel/channel_layer.hpp"
4 
5 #include <arpa/inet.h>
6 
7 #include <ipmid/types.hpp>
8 #include <ipmid/utils.hpp>
9 #include <phosphor-logging/elog-errors.hpp>
10 #include <phosphor-logging/lg2.hpp>
11 #include <xyz/openbmc_project/Common/error.hpp>
12 
13 #include <fstream>
14 #include <set>
15 #include <string>
16 
17 using namespace phosphor::logging;
18 using namespace sdbusplus::error::xyz::openbmc_project::common;
19 
20 namespace ipmi
21 {
22 constexpr Cc ccPayloadTypeNotSupported = 0x80;
23 
responsePayloadTypeNotSupported()24 static inline auto responsePayloadTypeNotSupported()
25 {
26     return response(ccPayloadTypeNotSupported);
27 }
28 } // namespace ipmi
29 
30 namespace cipher
31 {
32 
33 /** @brief Get the supported Cipher records
34  *
35  * The cipher records are read from the JSON file and converted into
36  * 1. cipher suite record format mentioned in the IPMI specification. The
37  * records can be either OEM or standard cipher. Each json entry is parsed and
38  * converted into the cipher record format and pushed into the vector.
39  * 2. Algorithms listed in vector format
40  *
41  * @return pair of vector containing 1. all the cipher suite records. 2.
42  * Algorithms supported
43  *
44  */
getCipherRecords()45 std::pair<std::vector<uint8_t>, std::vector<uint8_t>> getCipherRecords()
46 {
47     std::vector<uint8_t> cipherRecords;
48     std::vector<uint8_t> supportedAlgorithmRecords;
49     // create set to get the unique supported algorithms
50     std::set<uint8_t> supportedAlgorithmSet;
51 
52     std::ifstream jsonFile(configFile);
53     if (!jsonFile.is_open())
54     {
55         lg2::error("Channel Cipher suites file not found");
56         elog<InternalFailure>();
57     }
58 
59     auto data = Json::parse(jsonFile, nullptr, false);
60     if (data.is_discarded())
61     {
62         lg2::error("Parsing channel cipher suites JSON failed");
63         elog<InternalFailure>();
64     }
65 
66     for (const auto& record : data)
67     {
68         if (record.find(oem) != record.end())
69         {
70             // OEM cipher suite - 0xC1
71             cipherRecords.push_back(oemCipherSuite);
72             // Cipher Suite ID
73             cipherRecords.push_back(record.value(cipher, 0));
74             // OEM IANA - 3 bytes
75             cipherRecords.push_back(record.value(oem, 0));
76             cipherRecords.push_back(record.value(oem, 0) >> 8);
77             cipherRecords.push_back(record.value(oem, 0) >> 16);
78         }
79         else
80         {
81             // Standard cipher suite - 0xC0
82             cipherRecords.push_back(stdCipherSuite);
83             // Cipher Suite ID
84             cipherRecords.push_back(record.value(cipher, 0));
85         }
86 
87         // Authentication algorithm number
88         cipherRecords.push_back(record.value(auth, 0));
89         supportedAlgorithmSet.insert(record.value(auth, 0));
90 
91         // Integrity algorithm number
92         cipherRecords.push_back(record.value(integrity, 0) | integrityTag);
93         supportedAlgorithmSet.insert(record.value(integrity, 0) | integrityTag);
94 
95         // Confidentiality algorithm number
96         cipherRecords.push_back(record.value(conf, 0) | confTag);
97         supportedAlgorithmSet.insert(record.value(conf, 0) | confTag);
98     }
99 
100     // copy the set to supportedAlgorithmRecord which is vector based.
101     std::copy(supportedAlgorithmSet.begin(), supportedAlgorithmSet.end(),
102               std::back_inserter(supportedAlgorithmRecords));
103 
104     return std::make_pair(cipherRecords, supportedAlgorithmRecords);
105 }
106 
107 } // namespace cipher
108 
109 /** @brief this command is used to look up what authentication, integrity,
110  *  confidentiality algorithms are supported.
111  *
112  *  @ param ctx - context pointer
113  *  @ param channelNumber - channel number
114  *  @ param payloadType - payload type
115  *  @ param listIndex - list index
116  *  @ param algoSelectBit - list algorithms
117  *
118  *  @returns ipmi completion code plus response data
119  *  - rspChannel - channel number for authentication algorithm.
120  *  - rspRecords - cipher suite records.
121  **/
122 ipmi::RspType<uint8_t,             // Channel Number
123               std::vector<uint8_t> // Cipher Records
124               >
getChannelCipherSuites(ipmi::Context::ptr ctx,uint4_t channelNumber,uint4_t reserved1,uint8_t payloadType,uint6_t listIndex,uint1_t reserved2,uint1_t algoSelectBit)125     getChannelCipherSuites(ipmi::Context::ptr ctx, uint4_t channelNumber,
126                            uint4_t reserved1, uint8_t payloadType,
127                            uint6_t listIndex, uint1_t reserved2,
128                            uint1_t algoSelectBit)
129 {
130     static std::vector<uint8_t> cipherRecords;
131     static std::vector<uint8_t> supportedAlgorithms;
132     static auto recordInit = false;
133 
134     uint8_t rspChannel = ipmi::convertCurrentChannelNum(
135         static_cast<uint8_t>(channelNumber), ctx->channel);
136 
137     if (!ipmi::isValidChannel(rspChannel) || reserved1 != 0 || reserved2 != 0)
138     {
139         return ipmi::responseInvalidFieldRequest();
140     }
141     if (!ipmi::isValidPayloadType(static_cast<ipmi::PayloadType>(payloadType)))
142     {
143         lg2::debug("Get channel cipher suites - Invalid payload type");
144         return ipmi::responsePayloadTypeNotSupported();
145     }
146 
147     if (!recordInit)
148     {
149         try
150         {
151             std::tie(cipherRecords, supportedAlgorithms) =
152                 cipher::getCipherRecords();
153             recordInit = true;
154         }
155         catch (const std::exception& e)
156         {
157             return ipmi::responseUnspecifiedError();
158         }
159     }
160 
161     const std::vector<uint8_t>& records =
162         algoSelectBit ? cipherRecords : supportedAlgorithms;
163     static constexpr auto respSize = 16;
164 
165     // Session support is available in active LAN channels.
166     if ((ipmi::getChannelSessionSupport(rspChannel) ==
167          ipmi::EChannelSessSupported::none) ||
168         !(ipmi::doesDeviceExist(rspChannel)))
169     {
170         lg2::debug("Get channel cipher suites - Device does not exist");
171         return ipmi::responseInvalidFieldRequest();
172     }
173 
174     // List index(00h-3Fh), 0h selects the first set of 16, 1h selects the next
175     // set of 16 and so on.
176 
177     // Calculate the number of record data bytes to be returned.
178     auto start =
179         std::min(static_cast<size_t>(listIndex) * respSize, records.size());
180     auto end = std::min((static_cast<size_t>(listIndex) * respSize) + respSize,
181                         records.size());
182     auto size = end - start;
183 
184     std::vector<uint8_t> rspRecords;
185     std::copy_n(records.data() + start, size, std::back_inserter(rspRecords));
186 
187     return ipmi::responseSuccess(rspChannel, rspRecords);
188 }
189