blob: 6703fc3002aaa76ca85e8d3553e6bb11039f747c [file] [log] [blame]
Tom Joseph4e57ada2016-08-10 06:51:12 -05001#include "message_parsers.hpp"
2
Tom Joseph4e57ada2016-08-10 06:51:12 -05003#include "endian.hpp"
4#include "main.hpp"
5#include "message.hpp"
6#include "sessions_manager.hpp"
7
Vernon Mauery9e801a22018-10-12 13:20:49 -07008#include <memory>
9
Tom Joseph4e57ada2016-08-10 06:51:12 -050010namespace message
11{
12
13namespace parser
14{
15
Vernon Maueryd999ffc2018-10-25 09:16:05 -070016std::tuple<std::shared_ptr<Message>, SessionHeader>
Vernon Mauery9e801a22018-10-12 13:20:49 -070017 unflatten(std::vector<uint8_t>& inPacket)
Tom Joseph4e57ada2016-08-10 06:51:12 -050018{
19 // Check if the packet has atleast the size of the RMCP Header
20 if (inPacket.size() < sizeof(BasicHeader_t))
21 {
22 throw std::runtime_error("RMCP Header missing");
23 }
24
25 auto rmcpHeaderPtr = reinterpret_cast<BasicHeader_t*>(inPacket.data());
26
27 // Verify if the fields in the RMCP header conforms to the specification
28 if ((rmcpHeaderPtr->version != RMCP_VERSION) ||
29 (rmcpHeaderPtr->rmcpSeqNum != RMCP_SEQ) ||
30 (rmcpHeaderPtr->classOfMsg != RMCP_MESSAGE_CLASS_IPMI))
31 {
32 throw std::runtime_error("RMCP Header is invalid");
33 }
34
35 // Read the Session Header and invoke the parser corresponding to the
36 // header type
37 switch (static_cast<SessionHeader>(rmcpHeaderPtr->format.formatType))
38 {
39 case SessionHeader::IPMI15:
40 {
41 return std::make_tuple(ipmi15parser::unflatten(inPacket),
42 SessionHeader::IPMI15);
43 }
44 case SessionHeader::IPMI20:
45 {
46 return std::make_tuple(ipmi20parser::unflatten(inPacket),
47 SessionHeader::IPMI20);
48 }
49 default:
50 {
51 throw std::runtime_error("Invalid Session Header");
52 }
53 }
54}
55
Vernon Maueryd999ffc2018-10-25 09:16:05 -070056std::vector<uint8_t> flatten(std::shared_ptr<Message> outMessage,
Vernon Mauery224f36a2018-10-25 08:52:23 -070057 SessionHeader authType,
58 std::shared_ptr<session::Session> session)
Tom Joseph4e57ada2016-08-10 06:51:12 -050059{
60 // Call the flatten routine based on the header type
61 switch (authType)
62 {
63 case SessionHeader::IPMI15:
64 {
65 return ipmi15parser::flatten(outMessage, session);
66 }
67 case SessionHeader::IPMI20:
68 {
69 return ipmi20parser::flatten(outMessage, session);
70 }
71 default:
72 {
73 return {};
74 }
75 }
76}
77
78} // namespace parser
79
80namespace ipmi15parser
81{
82
Vernon Maueryd999ffc2018-10-25 09:16:05 -070083std::shared_ptr<Message> unflatten(std::vector<uint8_t>& inPacket)
Tom Joseph4e57ada2016-08-10 06:51:12 -050084{
Tom Joseph4e57ada2016-08-10 06:51:12 -050085 if (inPacket.size() < sizeof(SessionHeader_t))
86 {
87 throw std::runtime_error("IPMI1.5 Session Header Missing");
88 }
89
Vernon Maueryd999ffc2018-10-25 09:16:05 -070090 auto message = std::make_shared<Message>();
Tom Joseph4e57ada2016-08-10 06:51:12 -050091
92 auto header = reinterpret_cast<SessionHeader_t*>(inPacket.data());
93
94 message->payloadType = PayloadType::IPMI;
Tom Joseph6a560762017-01-10 15:30:28 +053095 message->bmcSessionID = endian::from_ipmi(header->sessId);
96 message->sessionSeqNum = endian::from_ipmi(header->sessSeqNum);
Tom Joseph4e57ada2016-08-10 06:51:12 -050097 message->isPacketEncrypted = false;
98 message->isPacketAuthenticated = false;
99
100 auto payloadLen = header->payloadLength;
101
Zhikui Ren2b1edef2020-07-24 14:32:13 -0700102 // Confirm the number of data bytes received correlates to
103 // the packet length in the header
104 if (inPacket.size() < (sizeof(SessionHeader_t) + payloadLen))
105 {
106 throw std::runtime_error("Invalid data length");
107 }
108
Vernon Mauery9e801a22018-10-12 13:20:49 -0700109 (message->payload)
110 .assign(inPacket.data() + sizeof(SessionHeader_t),
111 inPacket.data() + sizeof(SessionHeader_t) + payloadLen);
Tom Joseph4e57ada2016-08-10 06:51:12 -0500112
113 return message;
114}
115
Vernon Maueryd999ffc2018-10-25 09:16:05 -0700116std::vector<uint8_t> flatten(std::shared_ptr<Message> outMessage,
Vernon Mauery224f36a2018-10-25 08:52:23 -0700117 std::shared_ptr<session::Session> session)
Tom Joseph4e57ada2016-08-10 06:51:12 -0500118{
119 std::vector<uint8_t> packet(sizeof(SessionHeader_t));
120
121 // Insert Session Header into the Packet
122 auto header = reinterpret_cast<SessionHeader_t*>(packet.data());
123 header->base.version = parser::RMCP_VERSION;
124 header->base.reserved = 0x00;
125 header->base.rmcpSeqNum = parser::RMCP_SEQ;
126 header->base.classOfMsg = parser::RMCP_MESSAGE_CLASS_IPMI;
127 header->base.format.formatType =
128 static_cast<uint8_t>(parser::SessionHeader::IPMI15);
129 header->sessSeqNum = 0;
Vernon Maueryd999ffc2018-10-25 09:16:05 -0700130 header->sessId = endian::to_ipmi(outMessage->rcSessionID);
Tom Joseph4e57ada2016-08-10 06:51:12 -0500131
Vernon Maueryd999ffc2018-10-25 09:16:05 -0700132 header->payloadLength = static_cast<uint8_t>(outMessage->payload.size());
Tom Joseph4e57ada2016-08-10 06:51:12 -0500133
134 // Insert the Payload into the Packet
Vernon Maueryd999ffc2018-10-25 09:16:05 -0700135 packet.insert(packet.end(), outMessage->payload.begin(),
136 outMessage->payload.end());
Tom Joseph4e57ada2016-08-10 06:51:12 -0500137
138 // Insert the Session Trailer
139 packet.resize(packet.size() + sizeof(SessionTrailer_t));
Vernon Mauery9e801a22018-10-12 13:20:49 -0700140 auto trailer =
141 reinterpret_cast<SessionTrailer_t*>(packet.data() + packet.size());
Tom Joseph4e57ada2016-08-10 06:51:12 -0500142 trailer->legacyPad = 0x00;
143
144 return packet;
145}
146
147} // namespace ipmi15parser
148
149namespace ipmi20parser
150{
151
Vernon Maueryd999ffc2018-10-25 09:16:05 -0700152std::shared_ptr<Message> unflatten(std::vector<uint8_t>& inPacket)
Tom Joseph4e57ada2016-08-10 06:51:12 -0500153{
154 // Check if the packet has atleast the Session Header
155 if (inPacket.size() < sizeof(SessionHeader_t))
156 {
157 throw std::runtime_error("IPMI2.0 Session Header Missing");
158 }
159
Vernon Maueryd999ffc2018-10-25 09:16:05 -0700160 auto message = std::make_shared<Message>();
Tom Joseph4e57ada2016-08-10 06:51:12 -0500161
162 auto header = reinterpret_cast<SessionHeader_t*>(inPacket.data());
163
Vernon Mauery9e801a22018-10-12 13:20:49 -0700164 message->payloadType = static_cast<PayloadType>(header->payloadType & 0x3F);
Tom Joseph6a560762017-01-10 15:30:28 +0530165 message->bmcSessionID = endian::from_ipmi(header->sessId);
166 message->sessionSeqNum = endian::from_ipmi(header->sessSeqNum);
Tom Joseph4e57ada2016-08-10 06:51:12 -0500167 message->isPacketEncrypted =
168 ((header->payloadType & PAYLOAD_ENCRYPT_MASK) ? true : false);
169 message->isPacketAuthenticated =
170 ((header->payloadType & PAYLOAD_AUTH_MASK) ? true : false);
171
Tom Joseph6a560762017-01-10 15:30:28 +0530172 auto payloadLen = endian::from_ipmi(header->payloadLength);
Tom Joseph4e57ada2016-08-10 06:51:12 -0500173
Tom Joseph5fa487c2017-01-20 12:42:39 +0530174 if (message->isPacketAuthenticated)
Tom Joseph64703f42017-01-10 17:03:48 +0530175 {
Vernon Maueryd999ffc2018-10-25 09:16:05 -0700176 if (!(internal::verifyPacketIntegrity(inPacket, message, payloadLen)))
Tom Joseph64703f42017-01-10 17:03:48 +0530177 {
178 throw std::runtime_error("Packet Integrity check failed");
179 }
180 }
181
Tom Joseph78478a82017-01-26 14:24:29 +0530182 // Decrypt the payload if the payload is encrypted
183 if (message->isPacketEncrypted)
184 {
185 // Assign the decrypted payload to the IPMI Message
Vernon Mauery9e801a22018-10-12 13:20:49 -0700186 message->payload =
Vernon Maueryd999ffc2018-10-25 09:16:05 -0700187 internal::decryptPayload(inPacket, message, payloadLen);
Tom Joseph78478a82017-01-26 14:24:29 +0530188 }
189 else
190 {
191 message->payload.assign(inPacket.begin() + sizeof(SessionHeader_t),
192 inPacket.begin() + sizeof(SessionHeader_t) +
Vernon Mauery9e801a22018-10-12 13:20:49 -0700193 payloadLen);
Tom Joseph78478a82017-01-26 14:24:29 +0530194 }
195
Tom Joseph4e57ada2016-08-10 06:51:12 -0500196 return message;
197}
198
Vernon Maueryd999ffc2018-10-25 09:16:05 -0700199std::vector<uint8_t> flatten(std::shared_ptr<Message> outMessage,
Vernon Mauery224f36a2018-10-25 08:52:23 -0700200 std::shared_ptr<session::Session> session)
Tom Joseph4e57ada2016-08-10 06:51:12 -0500201{
202 std::vector<uint8_t> packet(sizeof(SessionHeader_t));
203
204 SessionHeader_t* header = reinterpret_cast<SessionHeader_t*>(packet.data());
205 header->base.version = parser::RMCP_VERSION;
206 header->base.reserved = 0x00;
207 header->base.rmcpSeqNum = parser::RMCP_SEQ;
208 header->base.classOfMsg = parser::RMCP_MESSAGE_CLASS_IPMI;
209 header->base.format.formatType =
210 static_cast<uint8_t>(parser::SessionHeader::IPMI20);
Vernon Maueryd999ffc2018-10-25 09:16:05 -0700211 header->payloadType = static_cast<uint8_t>(outMessage->payloadType);
212 header->sessId = endian::to_ipmi(outMessage->rcSessionID);
Tom Joseph4e57ada2016-08-10 06:51:12 -0500213
214 // Add session sequence number
215 internal::addSequenceNumber(packet, session);
216
Tom Joseph1404bca2017-01-26 14:17:02 +0530217 size_t payloadLen = 0;
218
Tom Joseph75362832017-01-26 15:17:04 +0530219 // Encrypt the payload if needed
Vernon Maueryd999ffc2018-10-25 09:16:05 -0700220 if (outMessage->isPacketEncrypted)
Tom Joseph75362832017-01-26 15:17:04 +0530221 {
222 header->payloadType |= PAYLOAD_ENCRYPT_MASK;
223 auto cipherPayload = internal::encryptPayload(outMessage);
224 payloadLen = cipherPayload.size();
225 header->payloadLength = endian::to_ipmi<uint16_t>(cipherPayload.size());
226
227 // Insert the encrypted payload into the outgoing IPMI packet
228 packet.insert(packet.end(), cipherPayload.begin(), cipherPayload.end());
229 }
230 else
231 {
Vernon Mauery9e801a22018-10-12 13:20:49 -0700232 header->payloadLength =
Vernon Maueryd999ffc2018-10-25 09:16:05 -0700233 endian::to_ipmi<uint16_t>(outMessage->payload.size());
234 payloadLen = outMessage->payload.size();
Tom Joseph75362832017-01-26 15:17:04 +0530235
236 // Insert the Payload into the Packet
Vernon Maueryd999ffc2018-10-25 09:16:05 -0700237 packet.insert(packet.end(), outMessage->payload.begin(),
238 outMessage->payload.end());
Tom Joseph75362832017-01-26 15:17:04 +0530239 }
Tom Joseph4e57ada2016-08-10 06:51:12 -0500240
Vernon Maueryd999ffc2018-10-25 09:16:05 -0700241 if (outMessage->isPacketAuthenticated)
Tom Joseph64703f42017-01-10 17:03:48 +0530242 {
Tom Josephb882dbb2019-02-09 23:13:48 +0530243 header = reinterpret_cast<SessionHeader_t*>(packet.data());
244 header->payloadType |= PAYLOAD_AUTH_MASK;
Tom Joseph1404bca2017-01-26 14:17:02 +0530245 internal::addIntegrityData(packet, outMessage, payloadLen);
Tom Joseph64703f42017-01-10 17:03:48 +0530246 }
247
Tom Joseph4e57ada2016-08-10 06:51:12 -0500248 return packet;
249}
250
251namespace internal
252{
253
Vernon Mauery224f36a2018-10-25 08:52:23 -0700254void addSequenceNumber(std::vector<uint8_t>& packet,
255 std::shared_ptr<session::Session> session)
Tom Joseph4e57ada2016-08-10 06:51:12 -0500256{
257 SessionHeader_t* header = reinterpret_cast<SessionHeader_t*>(packet.data());
258
Suryakanth Sekarf8a34fc2019-06-12 20:59:18 +0530259 if (header->sessId == session::sessionZero)
Tom Joseph4e57ada2016-08-10 06:51:12 -0500260 {
261 header->sessSeqNum = 0x00;
262 }
263 else
264 {
Vernon Mauery224f36a2018-10-25 08:52:23 -0700265 auto seqNum = session->sequenceNums.increment();
Tom Joseph6a560762017-01-10 15:30:28 +0530266 header->sessSeqNum = endian::to_ipmi(seqNum);
Tom Joseph4e57ada2016-08-10 06:51:12 -0500267 }
268}
269
Tom Joseph64703f42017-01-10 17:03:48 +0530270bool verifyPacketIntegrity(const std::vector<uint8_t>& packet,
Vernon Maueryd999ffc2018-10-25 09:16:05 -0700271 const std::shared_ptr<Message> message,
272 size_t payloadLen)
Tom Joseph64703f42017-01-10 17:03:48 +0530273{
Tom Joseph64703f42017-01-10 17:03:48 +0530274 /*
275 * Padding bytes are added to cause the number of bytes in the data range
276 * covered by the AuthCode(Integrity Data) field to be a multiple of 4 bytes
277 * .If present each integrity Pad byte is set to FFh. The following logic
278 * calculates the number of padding bytes added in the IPMI packet.
279 */
Tom Joseph5a2d3ce2017-02-01 20:18:37 +0530280 auto paddingLen = 4 - ((payloadLen + 2) & 3);
Tom Joseph64703f42017-01-10 17:03:48 +0530281
282 auto sessTrailerPos = sizeof(SessionHeader_t) + payloadLen + paddingLen;
283
Zhikui Ren2b1edef2020-07-24 14:32:13 -0700284 // verify packet size includes trailer struct starts at sessTrailerPos
285 if (packet.size() < (sessTrailerPos + sizeof(SessionTrailer_t)))
286 {
287 return false;
288 }
289
Vernon Mauery9e801a22018-10-12 13:20:49 -0700290 auto trailer = reinterpret_cast<const SessionTrailer_t*>(packet.data() +
291 sessTrailerPos);
Tom Joseph64703f42017-01-10 17:03:48 +0530292
293 // Check trailer->padLength against paddingLen, both should match up,
294 // return false if the lengths don't match
Tom Joseph5fa487c2017-01-20 12:42:39 +0530295 if (trailer->padLength != paddingLen)
Tom Joseph64703f42017-01-10 17:03:48 +0530296 {
297 return false;
298 }
299
Vernon Maueryae1fda42018-10-15 12:55:34 -0700300 auto session = std::get<session::Manager&>(singletonPool)
Vernon Maueryd999ffc2018-10-25 09:16:05 -0700301 .getSession(message->bmcSessionID);
Tom Joseph64703f42017-01-10 17:03:48 +0530302
303 auto integrityAlgo = session->getIntegrityAlgo();
304
305 // Check if Integrity data length is as expected, check integrity data
306 // length is same as the length expected for the Integrity Algorithm that
307 // was negotiated during the session open process.
Tom Joseph5fa487c2017-01-20 12:42:39 +0530308 if ((packet.size() - sessTrailerPos - sizeof(SessionTrailer_t)) !=
Vernon Mauery9e801a22018-10-12 13:20:49 -0700309 integrityAlgo->authCodeLength)
Tom Joseph64703f42017-01-10 17:03:48 +0530310 {
311 return false;
312 }
313
314 auto integrityIter = packet.cbegin();
315 std::advance(integrityIter, sessTrailerPos + sizeof(SessionTrailer_t));
316
317 // The integrity data is calculated from the AuthType/Format field up to and
318 // including the field that immediately precedes the AuthCode field itself.
319 size_t length = packet.size() - integrityAlgo->authCodeLength -
320 message::parser::RMCP_SESSION_HEADER_SIZE;
321
322 return integrityAlgo->verifyIntegrityData(packet, length, integrityIter);
323}
324
Vernon Maueryd999ffc2018-10-25 09:16:05 -0700325void addIntegrityData(std::vector<uint8_t>& packet,
326 const std::shared_ptr<Message> message, size_t payloadLen)
Tom Joseph64703f42017-01-10 17:03:48 +0530327{
Tom Joseph64703f42017-01-10 17:03:48 +0530328 // The following logic calculates the number of padding bytes to be added to
329 // IPMI packet. If needed each integrity Pad byte is set to FFh.
Tom Joseph5a2d3ce2017-02-01 20:18:37 +0530330 auto paddingLen = 4 - ((payloadLen + 2) & 3);
Tom Joseph64703f42017-01-10 17:03:48 +0530331 packet.insert(packet.end(), paddingLen, 0xFF);
332
333 packet.resize(packet.size() + sizeof(SessionTrailer_t));
334
Vernon Mauery9e801a22018-10-12 13:20:49 -0700335 auto trailer = reinterpret_cast<SessionTrailer_t*>(
336 packet.data() + packet.size() - sizeof(SessionTrailer_t));
Tom Joseph64703f42017-01-10 17:03:48 +0530337
338 trailer->padLength = paddingLen;
339 trailer->nextHeader = parser::RMCP_MESSAGE_CLASS_IPMI;
340
Vernon Maueryae1fda42018-10-15 12:55:34 -0700341 auto session = std::get<session::Manager&>(singletonPool)
Vernon Maueryd999ffc2018-10-25 09:16:05 -0700342 .getSession(message->bmcSessionID);
Tom Joseph64703f42017-01-10 17:03:48 +0530343
Vernon Mauery9e801a22018-10-12 13:20:49 -0700344 auto integrityData =
345 session->getIntegrityAlgo()->generateIntegrityData(packet);
Tom Joseph64703f42017-01-10 17:03:48 +0530346
347 packet.insert(packet.end(), integrityData.begin(), integrityData.end());
348}
349
Tom Joseph78478a82017-01-26 14:24:29 +0530350std::vector<uint8_t> decryptPayload(const std::vector<uint8_t>& packet,
Vernon Maueryd999ffc2018-10-25 09:16:05 -0700351 const std::shared_ptr<Message> message,
352 size_t payloadLen)
Tom Joseph78478a82017-01-26 14:24:29 +0530353{
Vernon Maueryae1fda42018-10-15 12:55:34 -0700354 auto session = std::get<session::Manager&>(singletonPool)
Vernon Maueryd999ffc2018-10-25 09:16:05 -0700355 .getSession(message->bmcSessionID);
Tom Joseph78478a82017-01-26 14:24:29 +0530356
Vernon Mauery9e801a22018-10-12 13:20:49 -0700357 return session->getCryptAlgo()->decryptPayload(
358 packet, sizeof(SessionHeader_t), payloadLen);
Tom Joseph78478a82017-01-26 14:24:29 +0530359}
360
Vernon Maueryd999ffc2018-10-25 09:16:05 -0700361std::vector<uint8_t> encryptPayload(std::shared_ptr<Message> message)
Tom Joseph75362832017-01-26 15:17:04 +0530362{
Vernon Maueryae1fda42018-10-15 12:55:34 -0700363 auto session = std::get<session::Manager&>(singletonPool)
Vernon Maueryd999ffc2018-10-25 09:16:05 -0700364 .getSession(message->bmcSessionID);
Tom Joseph75362832017-01-26 15:17:04 +0530365
Vernon Maueryd999ffc2018-10-25 09:16:05 -0700366 return session->getCryptAlgo()->encryptPayload(message->payload);
Tom Joseph75362832017-01-26 15:17:04 +0530367}
368
Tom Joseph4e57ada2016-08-10 06:51:12 -0500369} // namespace internal
370
371} // namespace ipmi20parser
372
373} // namespace message