blob: 66030cf39964c337010fdb9c5610614615e951c3 [file] [log] [blame]
Tom Joseph4e57ada2016-08-10 06:51:12 -05001#include "message_parsers.hpp"
2
3#include <iostream>
4#include <memory>
5
6#include "endian.hpp"
7#include "main.hpp"
8#include "message.hpp"
9#include "sessions_manager.hpp"
10
11namespace message
12{
13
14namespace parser
15{
16
17std::tuple<std::unique_ptr<Message>, SessionHeader> unflatten(
18 std::vector<uint8_t>& inPacket)
19{
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
57std::vector<uint8_t> flatten(Message& outMessage,
58 SessionHeader authType,
59 session::Session& session)
60{
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
84std::unique_ptr<Message> unflatten(std::vector<uint8_t>& inPacket)
85{
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
92 auto message = std::make_unique<Message>();
93
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
104 (message->payload).assign(inPacket.data() + sizeof(SessionHeader_t),
105 inPacket.data() + sizeof(SessionHeader_t) +
106 payloadLen);
107
108 return message;
109}
110
111std::vector<uint8_t> flatten(Message& outMessage, session::Session& session)
112{
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;
Tom Joseph6a560762017-01-10 15:30:28 +0530124 header->sessId = endian::to_ipmi(outMessage.rcSessionID);
Tom Joseph4e57ada2016-08-10 06:51:12 -0500125
126 header->payloadLength = static_cast<uint8_t>(outMessage.payload.size());
127
128 // Insert the Payload into the Packet
129 packet.insert(packet.end(), outMessage.payload.begin(),
130 outMessage.payload.end());
131
132 // Insert the Session Trailer
133 packet.resize(packet.size() + sizeof(SessionTrailer_t));
134 auto trailer = reinterpret_cast<SessionTrailer_t*>(packet.data() +
135 packet.size());
136 trailer->legacyPad = 0x00;
137
138 return packet;
139}
140
141} // namespace ipmi15parser
142
143namespace ipmi20parser
144{
145
146std::unique_ptr<Message> unflatten(std::vector<uint8_t>& inPacket)
147{
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
154 auto message = std::make_unique<Message>();
155
156 auto header = reinterpret_cast<SessionHeader_t*>(inPacket.data());
157
158 message->payloadType = static_cast<PayloadType>
159 (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 {
Tom Joseph027dfc22017-01-26 13:30:53 +0530171 if (!(internal::verifyPacketIntegrity(inPacket,
172 *(message.get()),
173 payloadLen)))
Tom Joseph64703f42017-01-10 17:03:48 +0530174 {
175 throw std::runtime_error("Packet Integrity check failed");
176 }
177 }
178
Tom Joseph78478a82017-01-26 14:24:29 +0530179 // Decrypt the payload if the payload is encrypted
180 if (message->isPacketEncrypted)
181 {
182 // Assign the decrypted payload to the IPMI Message
183 message->payload = internal::decryptPayload(inPacket,
184 *(message.get()),
185 payloadLen);
186 }
187 else
188 {
189 message->payload.assign(inPacket.begin() + sizeof(SessionHeader_t),
190 inPacket.begin() + sizeof(SessionHeader_t) +
191 payloadLen);
192 }
193
Tom Joseph4e57ada2016-08-10 06:51:12 -0500194 return message;
195}
196
197std::vector<uint8_t> flatten(Message& outMessage, session::Session& session)
198{
199 std::vector<uint8_t> packet(sizeof(SessionHeader_t));
200
201 SessionHeader_t* header = reinterpret_cast<SessionHeader_t*>(packet.data());
202 header->base.version = parser::RMCP_VERSION;
203 header->base.reserved = 0x00;
204 header->base.rmcpSeqNum = parser::RMCP_SEQ;
205 header->base.classOfMsg = parser::RMCP_MESSAGE_CLASS_IPMI;
206 header->base.format.formatType =
207 static_cast<uint8_t>(parser::SessionHeader::IPMI20);
208 header->payloadType = static_cast<uint8_t>(outMessage.payloadType);
Tom Joseph6a560762017-01-10 15:30:28 +0530209 header->sessId = endian::to_ipmi(outMessage.rcSessionID);
Tom Joseph4e57ada2016-08-10 06:51:12 -0500210
211 // Add session sequence number
212 internal::addSequenceNumber(packet, session);
213
Tom Joseph1404bca2017-01-26 14:17:02 +0530214 size_t payloadLen = 0;
215
Tom Joseph75362832017-01-26 15:17:04 +0530216 // Encrypt the payload if needed
217 if (outMessage.isPacketEncrypted)
218 {
219 header->payloadType |= PAYLOAD_ENCRYPT_MASK;
220 auto cipherPayload = internal::encryptPayload(outMessage);
221 payloadLen = cipherPayload.size();
222 header->payloadLength = endian::to_ipmi<uint16_t>(cipherPayload.size());
223
224 // Insert the encrypted payload into the outgoing IPMI packet
225 packet.insert(packet.end(), cipherPayload.begin(), cipherPayload.end());
226 }
227 else
228 {
229 header->payloadLength = endian::to_ipmi<uint16_t>(
230 outMessage.payload.size());
231 payloadLen = outMessage.payload.size();
232
233 // Insert the Payload into the Packet
234 packet.insert(packet.end(), outMessage.payload.begin(),
235 outMessage.payload.end());
236 }
Tom Joseph4e57ada2016-08-10 06:51:12 -0500237
Tom Joseph5fa487c2017-01-20 12:42:39 +0530238 if (outMessage.isPacketAuthenticated)
Tom Joseph64703f42017-01-10 17:03:48 +0530239 {
Tom Joseph1404bca2017-01-26 14:17:02 +0530240 internal::addIntegrityData(packet, outMessage, payloadLen);
Tom Joseph64703f42017-01-10 17:03:48 +0530241 }
242
Tom Joseph4e57ada2016-08-10 06:51:12 -0500243 return packet;
244}
245
246namespace internal
247{
248
249void addSequenceNumber(std::vector<uint8_t>& packet, session::Session& session)
250{
251 SessionHeader_t* header = reinterpret_cast<SessionHeader_t*>(packet.data());
252
253 if (header->sessId == session::SESSION_ZERO)
254 {
255 header->sessSeqNum = 0x00;
256 }
257 else
258 {
259 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,
Tom Joseph027dfc22017-01-26 13:30:53 +0530265 const 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
278 auto trailer = reinterpret_cast<const SessionTrailer_t*>
279 (packet.data() + sessTrailerPos);
280
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
288 auto session = (std::get<session::Manager&>(singletonPool).getSession(
289 message.bmcSessionID)).lock();
290
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)) !=
297 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
313void addIntegrityData(std::vector<uint8_t>& packet,
Tom Joseph1404bca2017-01-26 14:17:02 +0530314 const Message& message,
315 size_t payloadLen)
Tom Joseph64703f42017-01-10 17:03:48 +0530316{
Tom Joseph64703f42017-01-10 17:03:48 +0530317 // The following logic calculates the number of padding bytes to be added to
318 // IPMI packet. If needed each integrity Pad byte is set to FFh.
Tom Joseph5a2d3ce2017-02-01 20:18:37 +0530319 auto paddingLen = 4 - ((payloadLen + 2) & 3);
Tom Joseph64703f42017-01-10 17:03:48 +0530320 packet.insert(packet.end(), paddingLen, 0xFF);
321
322 packet.resize(packet.size() + sizeof(SessionTrailer_t));
323
324 auto trailer = reinterpret_cast<SessionTrailer_t*>
325 (packet.data() + packet.size() -
326 sizeof(SessionTrailer_t));
327
328 trailer->padLength = paddingLen;
329 trailer->nextHeader = parser::RMCP_MESSAGE_CLASS_IPMI;
330
331 auto session = (std::get<session::Manager&>(singletonPool).getSession(
332 message.bmcSessionID)).lock();
333
334 auto integrityData = session->getIntegrityAlgo()->
335 generateIntegrityData(packet);
336
337 packet.insert(packet.end(), integrityData.begin(), integrityData.end());
338}
339
Tom Joseph78478a82017-01-26 14:24:29 +0530340std::vector<uint8_t> decryptPayload(const std::vector<uint8_t>& packet,
341 const Message& message,
342 size_t payloadLen)
343{
344 auto session = (std::get<session::Manager&>(singletonPool).getSession(
345 message.bmcSessionID)).lock();
346
347 return session->getCryptAlgo()->decryptPayload(packet,
348 sizeof(SessionHeader_t),
349 payloadLen);
350}
351
Tom Joseph75362832017-01-26 15:17:04 +0530352std::vector<uint8_t> encryptPayload(Message& message)
353{
354 auto session = (std::get<session::Manager&>(singletonPool).getSession(
355 message.bmcSessionID)).lock();
356
357 return session->getCryptAlgo()->encryptPayload(message.payload);
358}
359
Tom Joseph4e57ada2016-08-10 06:51:12 -0500360} // namespace internal
361
362} // namespace ipmi20parser
363
364} // namespace message