blob: d7074fa643b4849ac7310fb562b898ff73fb0905 [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 <iostream>
9#include <memory>
10
Tom Joseph4e57ada2016-08-10 06:51:12 -050011namespace message
12{
13
14namespace parser
15{
16
Vernon Maueryd999ffc2018-10-25 09:16:05 -070017std::tuple<std::shared_ptr<Message>, SessionHeader>
Vernon Mauery9e801a22018-10-12 13:20:49 -070018 unflatten(std::vector<uint8_t>& inPacket)
Tom Joseph4e57ada2016-08-10 06:51:12 -050019{
20 // Check if the packet has atleast the size of the RMCP Header
21 if (inPacket.size() < sizeof(BasicHeader_t))
22 {
23 throw std::runtime_error("RMCP Header missing");
24 }
25
26 auto rmcpHeaderPtr = reinterpret_cast<BasicHeader_t*>(inPacket.data());
27
28 // Verify if the fields in the RMCP header conforms to the specification
29 if ((rmcpHeaderPtr->version != RMCP_VERSION) ||
30 (rmcpHeaderPtr->rmcpSeqNum != RMCP_SEQ) ||
31 (rmcpHeaderPtr->classOfMsg != RMCP_MESSAGE_CLASS_IPMI))
32 {
33 throw std::runtime_error("RMCP Header is invalid");
34 }
35
36 // Read the Session Header and invoke the parser corresponding to the
37 // header type
38 switch (static_cast<SessionHeader>(rmcpHeaderPtr->format.formatType))
39 {
40 case SessionHeader::IPMI15:
41 {
42 return std::make_tuple(ipmi15parser::unflatten(inPacket),
43 SessionHeader::IPMI15);
44 }
45 case SessionHeader::IPMI20:
46 {
47 return std::make_tuple(ipmi20parser::unflatten(inPacket),
48 SessionHeader::IPMI20);
49 }
50 default:
51 {
52 throw std::runtime_error("Invalid Session Header");
53 }
54 }
55}
56
Vernon Maueryd999ffc2018-10-25 09:16:05 -070057std::vector<uint8_t> flatten(std::shared_ptr<Message> outMessage,
Vernon Mauery224f36a2018-10-25 08:52:23 -070058 SessionHeader authType,
59 std::shared_ptr<session::Session> session)
Tom Joseph4e57ada2016-08-10 06:51:12 -050060{
61 // Call the flatten routine based on the header type
62 switch (authType)
63 {
64 case SessionHeader::IPMI15:
65 {
66 return ipmi15parser::flatten(outMessage, session);
67 }
68 case SessionHeader::IPMI20:
69 {
70 return ipmi20parser::flatten(outMessage, session);
71 }
72 default:
73 {
74 return {};
75 }
76 }
77}
78
79} // namespace parser
80
81namespace ipmi15parser
82{
83
Vernon Maueryd999ffc2018-10-25 09:16:05 -070084std::shared_ptr<Message> unflatten(std::vector<uint8_t>& inPacket)
Tom Joseph4e57ada2016-08-10 06:51:12 -050085{
86 // Check if the packet has atleast the Session Header
87 if (inPacket.size() < sizeof(SessionHeader_t))
88 {
89 throw std::runtime_error("IPMI1.5 Session Header Missing");
90 }
91
Vernon Maueryd999ffc2018-10-25 09:16:05 -070092 auto message = std::make_shared<Message>();
Tom Joseph4e57ada2016-08-10 06:51:12 -050093
94 auto header = reinterpret_cast<SessionHeader_t*>(inPacket.data());
95
96 message->payloadType = PayloadType::IPMI;
Tom Joseph6a560762017-01-10 15:30:28 +053097 message->bmcSessionID = endian::from_ipmi(header->sessId);
98 message->sessionSeqNum = endian::from_ipmi(header->sessSeqNum);
Tom Joseph4e57ada2016-08-10 06:51:12 -050099 message->isPacketEncrypted = false;
100 message->isPacketAuthenticated = false;
101
102 auto payloadLen = header->payloadLength;
103
Vernon Mauery9e801a22018-10-12 13:20:49 -0700104 (message->payload)
105 .assign(inPacket.data() + sizeof(SessionHeader_t),
106 inPacket.data() + sizeof(SessionHeader_t) + payloadLen);
Tom Joseph4e57ada2016-08-10 06:51:12 -0500107
108 return message;
109}
110
Vernon Maueryd999ffc2018-10-25 09:16:05 -0700111std::vector<uint8_t> flatten(std::shared_ptr<Message> outMessage,
Vernon Mauery224f36a2018-10-25 08:52:23 -0700112 std::shared_ptr<session::Session> session)
Tom Joseph4e57ada2016-08-10 06:51:12 -0500113{
114 std::vector<uint8_t> packet(sizeof(SessionHeader_t));
115
116 // Insert Session Header into the Packet
117 auto header = reinterpret_cast<SessionHeader_t*>(packet.data());
118 header->base.version = parser::RMCP_VERSION;
119 header->base.reserved = 0x00;
120 header->base.rmcpSeqNum = parser::RMCP_SEQ;
121 header->base.classOfMsg = parser::RMCP_MESSAGE_CLASS_IPMI;
122 header->base.format.formatType =
123 static_cast<uint8_t>(parser::SessionHeader::IPMI15);
124 header->sessSeqNum = 0;
Vernon Maueryd999ffc2018-10-25 09:16:05 -0700125 header->sessId = endian::to_ipmi(outMessage->rcSessionID);
Tom Joseph4e57ada2016-08-10 06:51:12 -0500126
Vernon Maueryd999ffc2018-10-25 09:16:05 -0700127 header->payloadLength = static_cast<uint8_t>(outMessage->payload.size());
Tom Joseph4e57ada2016-08-10 06:51:12 -0500128
129 // Insert the Payload into the Packet
Vernon Maueryd999ffc2018-10-25 09:16:05 -0700130 packet.insert(packet.end(), outMessage->payload.begin(),
131 outMessage->payload.end());
Tom Joseph4e57ada2016-08-10 06:51:12 -0500132
133 // Insert the Session Trailer
134 packet.resize(packet.size() + sizeof(SessionTrailer_t));
Vernon Mauery9e801a22018-10-12 13:20:49 -0700135 auto trailer =
136 reinterpret_cast<SessionTrailer_t*>(packet.data() + packet.size());
Tom Joseph4e57ada2016-08-10 06:51:12 -0500137 trailer->legacyPad = 0x00;
138
139 return packet;
140}
141
142} // namespace ipmi15parser
143
144namespace ipmi20parser
145{
146
Vernon Maueryd999ffc2018-10-25 09:16:05 -0700147std::shared_ptr<Message> unflatten(std::vector<uint8_t>& inPacket)
Tom Joseph4e57ada2016-08-10 06:51:12 -0500148{
149 // Check if the packet has atleast the Session Header
150 if (inPacket.size() < sizeof(SessionHeader_t))
151 {
152 throw std::runtime_error("IPMI2.0 Session Header Missing");
153 }
154
Vernon Maueryd999ffc2018-10-25 09:16:05 -0700155 auto message = std::make_shared<Message>();
Tom Joseph4e57ada2016-08-10 06:51:12 -0500156
157 auto header = reinterpret_cast<SessionHeader_t*>(inPacket.data());
158
Vernon Mauery9e801a22018-10-12 13:20:49 -0700159 message->payloadType = static_cast<PayloadType>(header->payloadType & 0x3F);
Tom Joseph6a560762017-01-10 15:30:28 +0530160 message->bmcSessionID = endian::from_ipmi(header->sessId);
161 message->sessionSeqNum = endian::from_ipmi(header->sessSeqNum);
Tom Joseph4e57ada2016-08-10 06:51:12 -0500162 message->isPacketEncrypted =
163 ((header->payloadType & PAYLOAD_ENCRYPT_MASK) ? true : false);
164 message->isPacketAuthenticated =
165 ((header->payloadType & PAYLOAD_AUTH_MASK) ? true : false);
166
Tom Joseph6a560762017-01-10 15:30:28 +0530167 auto payloadLen = endian::from_ipmi(header->payloadLength);
Tom Joseph4e57ada2016-08-10 06:51:12 -0500168
Tom Joseph5fa487c2017-01-20 12:42:39 +0530169 if (message->isPacketAuthenticated)
Tom Joseph64703f42017-01-10 17:03:48 +0530170 {
Vernon Maueryd999ffc2018-10-25 09:16:05 -0700171 if (!(internal::verifyPacketIntegrity(inPacket, message, payloadLen)))
Tom Joseph64703f42017-01-10 17:03:48 +0530172 {
173 throw std::runtime_error("Packet Integrity check failed");
174 }
175 }
176
Tom Joseph78478a82017-01-26 14:24:29 +0530177 // Decrypt the payload if the payload is encrypted
178 if (message->isPacketEncrypted)
179 {
180 // Assign the decrypted payload to the IPMI Message
Vernon Mauery9e801a22018-10-12 13:20:49 -0700181 message->payload =
Vernon Maueryd999ffc2018-10-25 09:16:05 -0700182 internal::decryptPayload(inPacket, message, payloadLen);
Tom Joseph78478a82017-01-26 14:24:29 +0530183 }
184 else
185 {
186 message->payload.assign(inPacket.begin() + sizeof(SessionHeader_t),
187 inPacket.begin() + sizeof(SessionHeader_t) +
Vernon Mauery9e801a22018-10-12 13:20:49 -0700188 payloadLen);
Tom Joseph78478a82017-01-26 14:24:29 +0530189 }
190
Tom Joseph4e57ada2016-08-10 06:51:12 -0500191 return message;
192}
193
Vernon Maueryd999ffc2018-10-25 09:16:05 -0700194std::vector<uint8_t> flatten(std::shared_ptr<Message> outMessage,
Vernon Mauery224f36a2018-10-25 08:52:23 -0700195 std::shared_ptr<session::Session> session)
Tom Joseph4e57ada2016-08-10 06:51:12 -0500196{
197 std::vector<uint8_t> packet(sizeof(SessionHeader_t));
198
199 SessionHeader_t* header = reinterpret_cast<SessionHeader_t*>(packet.data());
200 header->base.version = parser::RMCP_VERSION;
201 header->base.reserved = 0x00;
202 header->base.rmcpSeqNum = parser::RMCP_SEQ;
203 header->base.classOfMsg = parser::RMCP_MESSAGE_CLASS_IPMI;
204 header->base.format.formatType =
205 static_cast<uint8_t>(parser::SessionHeader::IPMI20);
Vernon Maueryd999ffc2018-10-25 09:16:05 -0700206 header->payloadType = static_cast<uint8_t>(outMessage->payloadType);
207 header->sessId = endian::to_ipmi(outMessage->rcSessionID);
Tom Joseph4e57ada2016-08-10 06:51:12 -0500208
209 // Add session sequence number
210 internal::addSequenceNumber(packet, session);
211
Tom Joseph1404bca2017-01-26 14:17:02 +0530212 size_t payloadLen = 0;
213
Tom Joseph75362832017-01-26 15:17:04 +0530214 // Encrypt the payload if needed
Vernon Maueryd999ffc2018-10-25 09:16:05 -0700215 if (outMessage->isPacketEncrypted)
Tom Joseph75362832017-01-26 15:17:04 +0530216 {
217 header->payloadType |= PAYLOAD_ENCRYPT_MASK;
218 auto cipherPayload = internal::encryptPayload(outMessage);
219 payloadLen = cipherPayload.size();
220 header->payloadLength = endian::to_ipmi<uint16_t>(cipherPayload.size());
221
222 // Insert the encrypted payload into the outgoing IPMI packet
223 packet.insert(packet.end(), cipherPayload.begin(), cipherPayload.end());
224 }
225 else
226 {
Vernon Mauery9e801a22018-10-12 13:20:49 -0700227 header->payloadLength =
Vernon Maueryd999ffc2018-10-25 09:16:05 -0700228 endian::to_ipmi<uint16_t>(outMessage->payload.size());
229 payloadLen = outMessage->payload.size();
Tom Joseph75362832017-01-26 15:17:04 +0530230
231 // Insert the Payload into the Packet
Vernon Maueryd999ffc2018-10-25 09:16:05 -0700232 packet.insert(packet.end(), outMessage->payload.begin(),
233 outMessage->payload.end());
Tom Joseph75362832017-01-26 15:17:04 +0530234 }
Tom Joseph4e57ada2016-08-10 06:51:12 -0500235
Vernon Maueryd999ffc2018-10-25 09:16:05 -0700236 if (outMessage->isPacketAuthenticated)
Tom Joseph64703f42017-01-10 17:03:48 +0530237 {
Tom Joseph1404bca2017-01-26 14:17:02 +0530238 internal::addIntegrityData(packet, outMessage, payloadLen);
Tom Joseph64703f42017-01-10 17:03:48 +0530239 }
240
Tom Joseph4e57ada2016-08-10 06:51:12 -0500241 return packet;
242}
243
244namespace internal
245{
246
Vernon Mauery224f36a2018-10-25 08:52:23 -0700247void addSequenceNumber(std::vector<uint8_t>& packet,
248 std::shared_ptr<session::Session> session)
Tom Joseph4e57ada2016-08-10 06:51:12 -0500249{
250 SessionHeader_t* header = reinterpret_cast<SessionHeader_t*>(packet.data());
251
252 if (header->sessId == session::SESSION_ZERO)
253 {
254 header->sessSeqNum = 0x00;
255 }
256 else
257 {
Vernon Mauery224f36a2018-10-25 08:52:23 -0700258 auto seqNum = session->sequenceNums.increment();
Tom Joseph6a560762017-01-10 15:30:28 +0530259 header->sessSeqNum = endian::to_ipmi(seqNum);
Tom Joseph4e57ada2016-08-10 06:51:12 -0500260 }
261}
262
Tom Joseph64703f42017-01-10 17:03:48 +0530263bool verifyPacketIntegrity(const std::vector<uint8_t>& packet,
Vernon Maueryd999ffc2018-10-25 09:16:05 -0700264 const std::shared_ptr<Message> message,
265 size_t payloadLen)
Tom Joseph64703f42017-01-10 17:03:48 +0530266{
Tom Joseph64703f42017-01-10 17:03:48 +0530267 /*
268 * Padding bytes are added to cause the number of bytes in the data range
269 * covered by the AuthCode(Integrity Data) field to be a multiple of 4 bytes
270 * .If present each integrity Pad byte is set to FFh. The following logic
271 * calculates the number of padding bytes added in the IPMI packet.
272 */
Tom Joseph5a2d3ce2017-02-01 20:18:37 +0530273 auto paddingLen = 4 - ((payloadLen + 2) & 3);
Tom Joseph64703f42017-01-10 17:03:48 +0530274
275 auto sessTrailerPos = sizeof(SessionHeader_t) + payloadLen + paddingLen;
276
Vernon Mauery9e801a22018-10-12 13:20:49 -0700277 auto trailer = reinterpret_cast<const SessionTrailer_t*>(packet.data() +
278 sessTrailerPos);
Tom Joseph64703f42017-01-10 17:03:48 +0530279
280 // Check trailer->padLength against paddingLen, both should match up,
281 // return false if the lengths don't match
Tom Joseph5fa487c2017-01-20 12:42:39 +0530282 if (trailer->padLength != paddingLen)
Tom Joseph64703f42017-01-10 17:03:48 +0530283 {
284 return false;
285 }
286
Vernon Maueryae1fda42018-10-15 12:55:34 -0700287 auto session = std::get<session::Manager&>(singletonPool)
Vernon Maueryd999ffc2018-10-25 09:16:05 -0700288 .getSession(message->bmcSessionID);
Tom Joseph64703f42017-01-10 17:03:48 +0530289
290 auto integrityAlgo = session->getIntegrityAlgo();
291
292 // Check if Integrity data length is as expected, check integrity data
293 // length is same as the length expected for the Integrity Algorithm that
294 // was negotiated during the session open process.
Tom Joseph5fa487c2017-01-20 12:42:39 +0530295 if ((packet.size() - sessTrailerPos - sizeof(SessionTrailer_t)) !=
Vernon Mauery9e801a22018-10-12 13:20:49 -0700296 integrityAlgo->authCodeLength)
Tom Joseph64703f42017-01-10 17:03:48 +0530297 {
298 return false;
299 }
300
301 auto integrityIter = packet.cbegin();
302 std::advance(integrityIter, sessTrailerPos + sizeof(SessionTrailer_t));
303
304 // The integrity data is calculated from the AuthType/Format field up to and
305 // including the field that immediately precedes the AuthCode field itself.
306 size_t length = packet.size() - integrityAlgo->authCodeLength -
307 message::parser::RMCP_SESSION_HEADER_SIZE;
308
309 return integrityAlgo->verifyIntegrityData(packet, length, integrityIter);
310}
311
Vernon Maueryd999ffc2018-10-25 09:16:05 -0700312void addIntegrityData(std::vector<uint8_t>& packet,
313 const std::shared_ptr<Message> message, size_t payloadLen)
Tom Joseph64703f42017-01-10 17:03:48 +0530314{
Tom Joseph64703f42017-01-10 17:03:48 +0530315 // The following logic calculates the number of padding bytes to be added to
316 // IPMI packet. If needed each integrity Pad byte is set to FFh.
Tom Joseph5a2d3ce2017-02-01 20:18:37 +0530317 auto paddingLen = 4 - ((payloadLen + 2) & 3);
Tom Joseph64703f42017-01-10 17:03:48 +0530318 packet.insert(packet.end(), paddingLen, 0xFF);
319
320 packet.resize(packet.size() + sizeof(SessionTrailer_t));
321
Vernon Mauery9e801a22018-10-12 13:20:49 -0700322 auto trailer = reinterpret_cast<SessionTrailer_t*>(
323 packet.data() + packet.size() - sizeof(SessionTrailer_t));
Tom Joseph64703f42017-01-10 17:03:48 +0530324
325 trailer->padLength = paddingLen;
326 trailer->nextHeader = parser::RMCP_MESSAGE_CLASS_IPMI;
327
Vernon Maueryae1fda42018-10-15 12:55:34 -0700328 auto session = std::get<session::Manager&>(singletonPool)
Vernon Maueryd999ffc2018-10-25 09:16:05 -0700329 .getSession(message->bmcSessionID);
Tom Joseph64703f42017-01-10 17:03:48 +0530330
Vernon Mauery9e801a22018-10-12 13:20:49 -0700331 auto integrityData =
332 session->getIntegrityAlgo()->generateIntegrityData(packet);
Tom Joseph64703f42017-01-10 17:03:48 +0530333
334 packet.insert(packet.end(), integrityData.begin(), integrityData.end());
335}
336
Tom Joseph78478a82017-01-26 14:24:29 +0530337std::vector<uint8_t> decryptPayload(const std::vector<uint8_t>& packet,
Vernon Maueryd999ffc2018-10-25 09:16:05 -0700338 const std::shared_ptr<Message> message,
339 size_t payloadLen)
Tom Joseph78478a82017-01-26 14:24:29 +0530340{
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 Joseph78478a82017-01-26 14:24:29 +0530343
Vernon Mauery9e801a22018-10-12 13:20:49 -0700344 return session->getCryptAlgo()->decryptPayload(
345 packet, sizeof(SessionHeader_t), payloadLen);
Tom Joseph78478a82017-01-26 14:24:29 +0530346}
347
Vernon Maueryd999ffc2018-10-25 09:16:05 -0700348std::vector<uint8_t> encryptPayload(std::shared_ptr<Message> message)
Tom Joseph75362832017-01-26 15:17:04 +0530349{
Vernon Maueryae1fda42018-10-15 12:55:34 -0700350 auto session = std::get<session::Manager&>(singletonPool)
Vernon Maueryd999ffc2018-10-25 09:16:05 -0700351 .getSession(message->bmcSessionID);
Tom Joseph75362832017-01-26 15:17:04 +0530352
Vernon Maueryd999ffc2018-10-25 09:16:05 -0700353 return session->getCryptAlgo()->encryptPayload(message->payload);
Tom Joseph75362832017-01-26 15:17:04 +0530354}
355
Tom Joseph4e57ada2016-08-10 06:51:12 -0500356} // namespace internal
357
358} // namespace ipmi20parser
359
360} // namespace message