blob: ddb9d011d15a2ec9dbc3f85a1067305ba50f4826 [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,
58 SessionHeader authType, 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{
85 // Check if the packet has atleast the Session Header
86 if (inPacket.size() < sizeof(SessionHeader_t))
87 {
88 throw std::runtime_error("IPMI1.5 Session Header Missing");
89 }
90
Vernon Maueryd999ffc2018-10-25 09:16:05 -070091 auto message = std::make_shared<Message>();
Tom Joseph4e57ada2016-08-10 06:51:12 -050092
93 auto header = reinterpret_cast<SessionHeader_t*>(inPacket.data());
94
95 message->payloadType = PayloadType::IPMI;
Tom Joseph6a560762017-01-10 15:30:28 +053096 message->bmcSessionID = endian::from_ipmi(header->sessId);
97 message->sessionSeqNum = endian::from_ipmi(header->sessSeqNum);
Tom Joseph4e57ada2016-08-10 06:51:12 -050098 message->isPacketEncrypted = false;
99 message->isPacketAuthenticated = false;
100
101 auto payloadLen = header->payloadLength;
102
Vernon Mauery9e801a22018-10-12 13:20:49 -0700103 (message->payload)
104 .assign(inPacket.data() + sizeof(SessionHeader_t),
105 inPacket.data() + sizeof(SessionHeader_t) + payloadLen);
Tom Joseph4e57ada2016-08-10 06:51:12 -0500106
107 return message;
108}
109
Vernon Maueryd999ffc2018-10-25 09:16:05 -0700110std::vector<uint8_t> flatten(std::shared_ptr<Message> outMessage,
111 session::Session& session)
Tom Joseph4e57ada2016-08-10 06:51:12 -0500112{
113 std::vector<uint8_t> packet(sizeof(SessionHeader_t));
114
115 // Insert Session Header into the Packet
116 auto header = reinterpret_cast<SessionHeader_t*>(packet.data());
117 header->base.version = parser::RMCP_VERSION;
118 header->base.reserved = 0x00;
119 header->base.rmcpSeqNum = parser::RMCP_SEQ;
120 header->base.classOfMsg = parser::RMCP_MESSAGE_CLASS_IPMI;
121 header->base.format.formatType =
122 static_cast<uint8_t>(parser::SessionHeader::IPMI15);
123 header->sessSeqNum = 0;
Vernon Maueryd999ffc2018-10-25 09:16:05 -0700124 header->sessId = endian::to_ipmi(outMessage->rcSessionID);
Tom Joseph4e57ada2016-08-10 06:51:12 -0500125
Vernon Maueryd999ffc2018-10-25 09:16:05 -0700126 header->payloadLength = static_cast<uint8_t>(outMessage->payload.size());
Tom Joseph4e57ada2016-08-10 06:51:12 -0500127
128 // Insert the Payload into the Packet
Vernon Maueryd999ffc2018-10-25 09:16:05 -0700129 packet.insert(packet.end(), outMessage->payload.begin(),
130 outMessage->payload.end());
Tom Joseph4e57ada2016-08-10 06:51:12 -0500131
132 // Insert the Session Trailer
133 packet.resize(packet.size() + sizeof(SessionTrailer_t));
Vernon Mauery9e801a22018-10-12 13:20:49 -0700134 auto trailer =
135 reinterpret_cast<SessionTrailer_t*>(packet.data() + packet.size());
Tom Joseph4e57ada2016-08-10 06:51:12 -0500136 trailer->legacyPad = 0x00;
137
138 return packet;
139}
140
141} // namespace ipmi15parser
142
143namespace ipmi20parser
144{
145
Vernon Maueryd999ffc2018-10-25 09:16:05 -0700146std::shared_ptr<Message> unflatten(std::vector<uint8_t>& inPacket)
Tom Joseph4e57ada2016-08-10 06:51:12 -0500147{
148 // Check if the packet has atleast the Session Header
149 if (inPacket.size() < sizeof(SessionHeader_t))
150 {
151 throw std::runtime_error("IPMI2.0 Session Header Missing");
152 }
153
Vernon Maueryd999ffc2018-10-25 09:16:05 -0700154 auto message = std::make_shared<Message>();
Tom Joseph4e57ada2016-08-10 06:51:12 -0500155
156 auto header = reinterpret_cast<SessionHeader_t*>(inPacket.data());
157
Vernon Mauery9e801a22018-10-12 13:20:49 -0700158 message->payloadType = static_cast<PayloadType>(header->payloadType & 0x3F);
Tom Joseph6a560762017-01-10 15:30:28 +0530159 message->bmcSessionID = endian::from_ipmi(header->sessId);
160 message->sessionSeqNum = endian::from_ipmi(header->sessSeqNum);
Tom Joseph4e57ada2016-08-10 06:51:12 -0500161 message->isPacketEncrypted =
162 ((header->payloadType & PAYLOAD_ENCRYPT_MASK) ? true : false);
163 message->isPacketAuthenticated =
164 ((header->payloadType & PAYLOAD_AUTH_MASK) ? true : false);
165
Tom Joseph6a560762017-01-10 15:30:28 +0530166 auto payloadLen = endian::from_ipmi(header->payloadLength);
Tom Joseph4e57ada2016-08-10 06:51:12 -0500167
Tom Joseph5fa487c2017-01-20 12:42:39 +0530168 if (message->isPacketAuthenticated)
Tom Joseph64703f42017-01-10 17:03:48 +0530169 {
Vernon Maueryd999ffc2018-10-25 09:16:05 -0700170 if (!(internal::verifyPacketIntegrity(inPacket, message, payloadLen)))
Tom Joseph64703f42017-01-10 17:03:48 +0530171 {
172 throw std::runtime_error("Packet Integrity check failed");
173 }
174 }
175
Tom Joseph78478a82017-01-26 14:24:29 +0530176 // Decrypt the payload if the payload is encrypted
177 if (message->isPacketEncrypted)
178 {
179 // Assign the decrypted payload to the IPMI Message
Vernon Mauery9e801a22018-10-12 13:20:49 -0700180 message->payload =
Vernon Maueryd999ffc2018-10-25 09:16:05 -0700181 internal::decryptPayload(inPacket, message, payloadLen);
Tom Joseph78478a82017-01-26 14:24:29 +0530182 }
183 else
184 {
185 message->payload.assign(inPacket.begin() + sizeof(SessionHeader_t),
186 inPacket.begin() + sizeof(SessionHeader_t) +
Vernon Mauery9e801a22018-10-12 13:20:49 -0700187 payloadLen);
Tom Joseph78478a82017-01-26 14:24:29 +0530188 }
189
Tom Joseph4e57ada2016-08-10 06:51:12 -0500190 return message;
191}
192
Vernon Maueryd999ffc2018-10-25 09:16:05 -0700193std::vector<uint8_t> flatten(std::shared_ptr<Message> outMessage,
194 session::Session& session)
Tom Joseph4e57ada2016-08-10 06:51:12 -0500195{
196 std::vector<uint8_t> packet(sizeof(SessionHeader_t));
197
198 SessionHeader_t* header = reinterpret_cast<SessionHeader_t*>(packet.data());
199 header->base.version = parser::RMCP_VERSION;
200 header->base.reserved = 0x00;
201 header->base.rmcpSeqNum = parser::RMCP_SEQ;
202 header->base.classOfMsg = parser::RMCP_MESSAGE_CLASS_IPMI;
203 header->base.format.formatType =
204 static_cast<uint8_t>(parser::SessionHeader::IPMI20);
Vernon Maueryd999ffc2018-10-25 09:16:05 -0700205 header->payloadType = static_cast<uint8_t>(outMessage->payloadType);
206 header->sessId = endian::to_ipmi(outMessage->rcSessionID);
Tom Joseph4e57ada2016-08-10 06:51:12 -0500207
208 // Add session sequence number
209 internal::addSequenceNumber(packet, session);
210
Tom Joseph1404bca2017-01-26 14:17:02 +0530211 size_t payloadLen = 0;
212
Tom Joseph75362832017-01-26 15:17:04 +0530213 // Encrypt the payload if needed
Vernon Maueryd999ffc2018-10-25 09:16:05 -0700214 if (outMessage->isPacketEncrypted)
Tom Joseph75362832017-01-26 15:17:04 +0530215 {
216 header->payloadType |= PAYLOAD_ENCRYPT_MASK;
217 auto cipherPayload = internal::encryptPayload(outMessage);
218 payloadLen = cipherPayload.size();
219 header->payloadLength = endian::to_ipmi<uint16_t>(cipherPayload.size());
220
221 // Insert the encrypted payload into the outgoing IPMI packet
222 packet.insert(packet.end(), cipherPayload.begin(), cipherPayload.end());
223 }
224 else
225 {
Vernon Mauery9e801a22018-10-12 13:20:49 -0700226 header->payloadLength =
Vernon Maueryd999ffc2018-10-25 09:16:05 -0700227 endian::to_ipmi<uint16_t>(outMessage->payload.size());
228 payloadLen = outMessage->payload.size();
Tom Joseph75362832017-01-26 15:17:04 +0530229
230 // Insert the Payload into the Packet
Vernon Maueryd999ffc2018-10-25 09:16:05 -0700231 packet.insert(packet.end(), outMessage->payload.begin(),
232 outMessage->payload.end());
Tom Joseph75362832017-01-26 15:17:04 +0530233 }
Tom Joseph4e57ada2016-08-10 06:51:12 -0500234
Vernon Maueryd999ffc2018-10-25 09:16:05 -0700235 if (outMessage->isPacketAuthenticated)
Tom Joseph64703f42017-01-10 17:03:48 +0530236 {
Tom Joseph1404bca2017-01-26 14:17:02 +0530237 internal::addIntegrityData(packet, outMessage, payloadLen);
Tom Joseph64703f42017-01-10 17:03:48 +0530238 }
239
Tom Joseph4e57ada2016-08-10 06:51:12 -0500240 return packet;
241}
242
243namespace internal
244{
245
246void addSequenceNumber(std::vector<uint8_t>& packet, session::Session& session)
247{
248 SessionHeader_t* header = reinterpret_cast<SessionHeader_t*>(packet.data());
249
250 if (header->sessId == session::SESSION_ZERO)
251 {
252 header->sessSeqNum = 0x00;
253 }
254 else
255 {
256 auto seqNum = session.sequenceNums.increment();
Tom Joseph6a560762017-01-10 15:30:28 +0530257 header->sessSeqNum = endian::to_ipmi(seqNum);
Tom Joseph4e57ada2016-08-10 06:51:12 -0500258 }
259}
260
Tom Joseph64703f42017-01-10 17:03:48 +0530261bool verifyPacketIntegrity(const std::vector<uint8_t>& packet,
Vernon Maueryd999ffc2018-10-25 09:16:05 -0700262 const std::shared_ptr<Message> message,
263 size_t payloadLen)
Tom Joseph64703f42017-01-10 17:03:48 +0530264{
Tom Joseph64703f42017-01-10 17:03:48 +0530265 /*
266 * Padding bytes are added to cause the number of bytes in the data range
267 * covered by the AuthCode(Integrity Data) field to be a multiple of 4 bytes
268 * .If present each integrity Pad byte is set to FFh. The following logic
269 * calculates the number of padding bytes added in the IPMI packet.
270 */
Tom Joseph5a2d3ce2017-02-01 20:18:37 +0530271 auto paddingLen = 4 - ((payloadLen + 2) & 3);
Tom Joseph64703f42017-01-10 17:03:48 +0530272
273 auto sessTrailerPos = sizeof(SessionHeader_t) + payloadLen + paddingLen;
274
Vernon Mauery9e801a22018-10-12 13:20:49 -0700275 auto trailer = reinterpret_cast<const SessionTrailer_t*>(packet.data() +
276 sessTrailerPos);
Tom Joseph64703f42017-01-10 17:03:48 +0530277
278 // Check trailer->padLength against paddingLen, both should match up,
279 // return false if the lengths don't match
Tom Joseph5fa487c2017-01-20 12:42:39 +0530280 if (trailer->padLength != paddingLen)
Tom Joseph64703f42017-01-10 17:03:48 +0530281 {
282 return false;
283 }
284
Vernon Maueryae1fda42018-10-15 12:55:34 -0700285 auto session = std::get<session::Manager&>(singletonPool)
Vernon Maueryd999ffc2018-10-25 09:16:05 -0700286 .getSession(message->bmcSessionID);
Tom Joseph64703f42017-01-10 17:03:48 +0530287
288 auto integrityAlgo = session->getIntegrityAlgo();
289
290 // Check if Integrity data length is as expected, check integrity data
291 // length is same as the length expected for the Integrity Algorithm that
292 // was negotiated during the session open process.
Tom Joseph5fa487c2017-01-20 12:42:39 +0530293 if ((packet.size() - sessTrailerPos - sizeof(SessionTrailer_t)) !=
Vernon Mauery9e801a22018-10-12 13:20:49 -0700294 integrityAlgo->authCodeLength)
Tom Joseph64703f42017-01-10 17:03:48 +0530295 {
296 return false;
297 }
298
299 auto integrityIter = packet.cbegin();
300 std::advance(integrityIter, sessTrailerPos + sizeof(SessionTrailer_t));
301
302 // The integrity data is calculated from the AuthType/Format field up to and
303 // including the field that immediately precedes the AuthCode field itself.
304 size_t length = packet.size() - integrityAlgo->authCodeLength -
305 message::parser::RMCP_SESSION_HEADER_SIZE;
306
307 return integrityAlgo->verifyIntegrityData(packet, length, integrityIter);
308}
309
Vernon Maueryd999ffc2018-10-25 09:16:05 -0700310void addIntegrityData(std::vector<uint8_t>& packet,
311 const std::shared_ptr<Message> message, size_t payloadLen)
Tom Joseph64703f42017-01-10 17:03:48 +0530312{
Tom Joseph64703f42017-01-10 17:03:48 +0530313 // The following logic calculates the number of padding bytes to be added to
314 // IPMI packet. If needed each integrity Pad byte is set to FFh.
Tom Joseph5a2d3ce2017-02-01 20:18:37 +0530315 auto paddingLen = 4 - ((payloadLen + 2) & 3);
Tom Joseph64703f42017-01-10 17:03:48 +0530316 packet.insert(packet.end(), paddingLen, 0xFF);
317
318 packet.resize(packet.size() + sizeof(SessionTrailer_t));
319
Vernon Mauery9e801a22018-10-12 13:20:49 -0700320 auto trailer = reinterpret_cast<SessionTrailer_t*>(
321 packet.data() + packet.size() - sizeof(SessionTrailer_t));
Tom Joseph64703f42017-01-10 17:03:48 +0530322
323 trailer->padLength = paddingLen;
324 trailer->nextHeader = parser::RMCP_MESSAGE_CLASS_IPMI;
325
Vernon Maueryae1fda42018-10-15 12:55:34 -0700326 auto session = std::get<session::Manager&>(singletonPool)
Vernon Maueryd999ffc2018-10-25 09:16:05 -0700327 .getSession(message->bmcSessionID);
Tom Joseph64703f42017-01-10 17:03:48 +0530328
Vernon Mauery9e801a22018-10-12 13:20:49 -0700329 auto integrityData =
330 session->getIntegrityAlgo()->generateIntegrityData(packet);
Tom Joseph64703f42017-01-10 17:03:48 +0530331
332 packet.insert(packet.end(), integrityData.begin(), integrityData.end());
333}
334
Tom Joseph78478a82017-01-26 14:24:29 +0530335std::vector<uint8_t> decryptPayload(const std::vector<uint8_t>& packet,
Vernon Maueryd999ffc2018-10-25 09:16:05 -0700336 const std::shared_ptr<Message> message,
337 size_t payloadLen)
Tom Joseph78478a82017-01-26 14:24:29 +0530338{
Vernon Maueryae1fda42018-10-15 12:55:34 -0700339 auto session = std::get<session::Manager&>(singletonPool)
Vernon Maueryd999ffc2018-10-25 09:16:05 -0700340 .getSession(message->bmcSessionID);
Tom Joseph78478a82017-01-26 14:24:29 +0530341
Vernon Mauery9e801a22018-10-12 13:20:49 -0700342 return session->getCryptAlgo()->decryptPayload(
343 packet, sizeof(SessionHeader_t), payloadLen);
Tom Joseph78478a82017-01-26 14:24:29 +0530344}
345
Vernon Maueryd999ffc2018-10-25 09:16:05 -0700346std::vector<uint8_t> encryptPayload(std::shared_ptr<Message> message)
Tom Joseph75362832017-01-26 15:17:04 +0530347{
Vernon Maueryae1fda42018-10-15 12:55:34 -0700348 auto session = std::get<session::Manager&>(singletonPool)
Vernon Maueryd999ffc2018-10-25 09:16:05 -0700349 .getSession(message->bmcSessionID);
Tom Joseph75362832017-01-26 15:17:04 +0530350
Vernon Maueryd999ffc2018-10-25 09:16:05 -0700351 return session->getCryptAlgo()->encryptPayload(message->payload);
Tom Joseph75362832017-01-26 15:17:04 +0530352}
353
Tom Joseph4e57ada2016-08-10 06:51:12 -0500354} // namespace internal
355
356} // namespace ipmi20parser
357
358} // namespace message