blob: 95fcfafd4685d5e9a08e386d94a9337ec27fb807 [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{
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,
Vernon Mauery224f36a2018-10-25 08:52:23 -0700111 std::shared_ptr<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,
Vernon Mauery224f36a2018-10-25 08:52:23 -0700194 std::shared_ptr<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 Josephb882dbb2019-02-09 23:13:48 +0530237 header = reinterpret_cast<SessionHeader_t*>(packet.data());
238 header->payloadType |= PAYLOAD_AUTH_MASK;
Tom Joseph1404bca2017-01-26 14:17:02 +0530239 internal::addIntegrityData(packet, outMessage, payloadLen);
Tom Joseph64703f42017-01-10 17:03:48 +0530240 }
241
Tom Joseph4e57ada2016-08-10 06:51:12 -0500242 return packet;
243}
244
245namespace internal
246{
247
Vernon Mauery224f36a2018-10-25 08:52:23 -0700248void addSequenceNumber(std::vector<uint8_t>& packet,
249 std::shared_ptr<session::Session> session)
Tom Joseph4e57ada2016-08-10 06:51:12 -0500250{
251 SessionHeader_t* header = reinterpret_cast<SessionHeader_t*>(packet.data());
252
Suryakanth Sekarf8a34fc2019-06-12 20:59:18 +0530253 if (header->sessId == session::sessionZero)
Tom Joseph4e57ada2016-08-10 06:51:12 -0500254 {
255 header->sessSeqNum = 0x00;
256 }
257 else
258 {
Vernon Mauery224f36a2018-10-25 08:52:23 -0700259 auto seqNum = session->sequenceNums.increment();
Tom Joseph6a560762017-01-10 15:30:28 +0530260 header->sessSeqNum = endian::to_ipmi(seqNum);
Tom Joseph4e57ada2016-08-10 06:51:12 -0500261 }
262}
263
Tom Joseph64703f42017-01-10 17:03:48 +0530264bool verifyPacketIntegrity(const std::vector<uint8_t>& packet,
Vernon Maueryd999ffc2018-10-25 09:16:05 -0700265 const std::shared_ptr<Message> message,
266 size_t payloadLen)
Tom Joseph64703f42017-01-10 17:03:48 +0530267{
Tom Joseph64703f42017-01-10 17:03:48 +0530268 /*
269 * Padding bytes are added to cause the number of bytes in the data range
270 * covered by the AuthCode(Integrity Data) field to be a multiple of 4 bytes
271 * .If present each integrity Pad byte is set to FFh. The following logic
272 * calculates the number of padding bytes added in the IPMI packet.
273 */
Tom Joseph5a2d3ce2017-02-01 20:18:37 +0530274 auto paddingLen = 4 - ((payloadLen + 2) & 3);
Tom Joseph64703f42017-01-10 17:03:48 +0530275
276 auto sessTrailerPos = sizeof(SessionHeader_t) + payloadLen + paddingLen;
277
Vernon Mauery9e801a22018-10-12 13:20:49 -0700278 auto trailer = reinterpret_cast<const SessionTrailer_t*>(packet.data() +
279 sessTrailerPos);
Tom Joseph64703f42017-01-10 17:03:48 +0530280
281 // Check trailer->padLength against paddingLen, both should match up,
282 // return false if the lengths don't match
Tom Joseph5fa487c2017-01-20 12:42:39 +0530283 if (trailer->padLength != paddingLen)
Tom Joseph64703f42017-01-10 17:03:48 +0530284 {
285 return false;
286 }
287
Vernon Maueryae1fda42018-10-15 12:55:34 -0700288 auto session = std::get<session::Manager&>(singletonPool)
Vernon Maueryd999ffc2018-10-25 09:16:05 -0700289 .getSession(message->bmcSessionID);
Tom Joseph64703f42017-01-10 17:03:48 +0530290
291 auto integrityAlgo = session->getIntegrityAlgo();
292
293 // Check if Integrity data length is as expected, check integrity data
294 // length is same as the length expected for the Integrity Algorithm that
295 // was negotiated during the session open process.
Tom Joseph5fa487c2017-01-20 12:42:39 +0530296 if ((packet.size() - sessTrailerPos - sizeof(SessionTrailer_t)) !=
Vernon Mauery9e801a22018-10-12 13:20:49 -0700297 integrityAlgo->authCodeLength)
Tom Joseph64703f42017-01-10 17:03:48 +0530298 {
299 return false;
300 }
301
302 auto integrityIter = packet.cbegin();
303 std::advance(integrityIter, sessTrailerPos + sizeof(SessionTrailer_t));
304
305 // The integrity data is calculated from the AuthType/Format field up to and
306 // including the field that immediately precedes the AuthCode field itself.
307 size_t length = packet.size() - integrityAlgo->authCodeLength -
308 message::parser::RMCP_SESSION_HEADER_SIZE;
309
310 return integrityAlgo->verifyIntegrityData(packet, length, integrityIter);
311}
312
Vernon Maueryd999ffc2018-10-25 09:16:05 -0700313void addIntegrityData(std::vector<uint8_t>& packet,
314 const std::shared_ptr<Message> message, size_t payloadLen)
Tom Joseph64703f42017-01-10 17:03:48 +0530315{
Tom Joseph64703f42017-01-10 17:03:48 +0530316 // The following logic calculates the number of padding bytes to be added to
317 // IPMI packet. If needed each integrity Pad byte is set to FFh.
Tom Joseph5a2d3ce2017-02-01 20:18:37 +0530318 auto paddingLen = 4 - ((payloadLen + 2) & 3);
Tom Joseph64703f42017-01-10 17:03:48 +0530319 packet.insert(packet.end(), paddingLen, 0xFF);
320
321 packet.resize(packet.size() + sizeof(SessionTrailer_t));
322
Vernon Mauery9e801a22018-10-12 13:20:49 -0700323 auto trailer = reinterpret_cast<SessionTrailer_t*>(
324 packet.data() + packet.size() - sizeof(SessionTrailer_t));
Tom Joseph64703f42017-01-10 17:03:48 +0530325
326 trailer->padLength = paddingLen;
327 trailer->nextHeader = parser::RMCP_MESSAGE_CLASS_IPMI;
328
Vernon Maueryae1fda42018-10-15 12:55:34 -0700329 auto session = std::get<session::Manager&>(singletonPool)
Vernon Maueryd999ffc2018-10-25 09:16:05 -0700330 .getSession(message->bmcSessionID);
Tom Joseph64703f42017-01-10 17:03:48 +0530331
Vernon Mauery9e801a22018-10-12 13:20:49 -0700332 auto integrityData =
333 session->getIntegrityAlgo()->generateIntegrityData(packet);
Tom Joseph64703f42017-01-10 17:03:48 +0530334
335 packet.insert(packet.end(), integrityData.begin(), integrityData.end());
336}
337
Tom Joseph78478a82017-01-26 14:24:29 +0530338std::vector<uint8_t> decryptPayload(const std::vector<uint8_t>& packet,
Vernon Maueryd999ffc2018-10-25 09:16:05 -0700339 const std::shared_ptr<Message> message,
340 size_t payloadLen)
Tom Joseph78478a82017-01-26 14:24:29 +0530341{
Vernon Maueryae1fda42018-10-15 12:55:34 -0700342 auto session = std::get<session::Manager&>(singletonPool)
Vernon Maueryd999ffc2018-10-25 09:16:05 -0700343 .getSession(message->bmcSessionID);
Tom Joseph78478a82017-01-26 14:24:29 +0530344
Vernon Mauery9e801a22018-10-12 13:20:49 -0700345 return session->getCryptAlgo()->decryptPayload(
346 packet, sizeof(SessionHeader_t), payloadLen);
Tom Joseph78478a82017-01-26 14:24:29 +0530347}
348
Vernon Maueryd999ffc2018-10-25 09:16:05 -0700349std::vector<uint8_t> encryptPayload(std::shared_ptr<Message> message)
Tom Joseph75362832017-01-26 15:17:04 +0530350{
Vernon Maueryae1fda42018-10-15 12:55:34 -0700351 auto session = std::get<session::Manager&>(singletonPool)
Vernon Maueryd999ffc2018-10-25 09:16:05 -0700352 .getSession(message->bmcSessionID);
Tom Joseph75362832017-01-26 15:17:04 +0530353
Vernon Maueryd999ffc2018-10-25 09:16:05 -0700354 return session->getCryptAlgo()->encryptPayload(message->payload);
Tom Joseph75362832017-01-26 15:17:04 +0530355}
356
Tom Joseph4e57ada2016-08-10 06:51:12 -0500357} // namespace internal
358
359} // namespace ipmi20parser
360
361} // namespace message