1 #include "message_parsers.hpp"
2 
3 #include "endian.hpp"
4 #include "main.hpp"
5 #include "message.hpp"
6 #include "sessions_manager.hpp"
7 
8 #include <memory>
9 
10 namespace message
11 {
12 
13 namespace parser
14 {
15 
16 std::tuple<std::shared_ptr<Message>, SessionHeader>
17     unflatten(std::vector<uint8_t>& inPacket)
18 {
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 
56 std::vector<uint8_t> flatten(std::shared_ptr<Message> outMessage,
57                              SessionHeader authType,
58                              std::shared_ptr<session::Session> session)
59 {
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 
80 namespace ipmi15parser
81 {
82 
83 std::shared_ptr<Message> unflatten(std::vector<uint8_t>& inPacket)
84 {
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 
91     auto message = std::make_shared<Message>();
92 
93     auto header = reinterpret_cast<SessionHeader_t*>(inPacket.data());
94 
95     message->payloadType = PayloadType::IPMI;
96     message->bmcSessionID = endian::from_ipmi(header->sessId);
97     message->sessionSeqNum = endian::from_ipmi(header->sessSeqNum);
98     message->isPacketEncrypted = false;
99     message->isPacketAuthenticated = false;
100 
101     auto payloadLen = header->payloadLength;
102 
103     (message->payload)
104         .assign(inPacket.data() + sizeof(SessionHeader_t),
105                 inPacket.data() + sizeof(SessionHeader_t) + payloadLen);
106 
107     return message;
108 }
109 
110 std::vector<uint8_t> flatten(std::shared_ptr<Message> outMessage,
111                              std::shared_ptr<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;
124     header->sessId = endian::to_ipmi(outMessage->rcSessionID);
125 
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 =
135         reinterpret_cast<SessionTrailer_t*>(packet.data() + packet.size());
136     trailer->legacyPad = 0x00;
137 
138     return packet;
139 }
140 
141 } // namespace ipmi15parser
142 
143 namespace ipmi20parser
144 {
145 
146 std::shared_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_shared<Message>();
155 
156     auto header = reinterpret_cast<SessionHeader_t*>(inPacket.data());
157 
158     message->payloadType = static_cast<PayloadType>(header->payloadType & 0x3F);
159     message->bmcSessionID = endian::from_ipmi(header->sessId);
160     message->sessionSeqNum = endian::from_ipmi(header->sessSeqNum);
161     message->isPacketEncrypted =
162         ((header->payloadType & PAYLOAD_ENCRYPT_MASK) ? true : false);
163     message->isPacketAuthenticated =
164         ((header->payloadType & PAYLOAD_AUTH_MASK) ? true : false);
165 
166     auto payloadLen = endian::from_ipmi(header->payloadLength);
167 
168     if (message->isPacketAuthenticated)
169     {
170         if (!(internal::verifyPacketIntegrity(inPacket, message, payloadLen)))
171         {
172             throw std::runtime_error("Packet Integrity check failed");
173         }
174     }
175 
176     // Decrypt the payload if the payload is encrypted
177     if (message->isPacketEncrypted)
178     {
179         // Assign the decrypted payload to the IPMI Message
180         message->payload =
181             internal::decryptPayload(inPacket, message, payloadLen);
182     }
183     else
184     {
185         message->payload.assign(inPacket.begin() + sizeof(SessionHeader_t),
186                                 inPacket.begin() + sizeof(SessionHeader_t) +
187                                     payloadLen);
188     }
189 
190     return message;
191 }
192 
193 std::vector<uint8_t> flatten(std::shared_ptr<Message> outMessage,
194                              std::shared_ptr<session::Session> session)
195 {
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);
205     header->payloadType = static_cast<uint8_t>(outMessage->payloadType);
206     header->sessId = endian::to_ipmi(outMessage->rcSessionID);
207 
208     // Add session sequence number
209     internal::addSequenceNumber(packet, session);
210 
211     size_t payloadLen = 0;
212 
213     // Encrypt the payload if needed
214     if (outMessage->isPacketEncrypted)
215     {
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     {
226         header->payloadLength =
227             endian::to_ipmi<uint16_t>(outMessage->payload.size());
228         payloadLen = outMessage->payload.size();
229 
230         // Insert the Payload into the Packet
231         packet.insert(packet.end(), outMessage->payload.begin(),
232                       outMessage->payload.end());
233     }
234 
235     if (outMessage->isPacketAuthenticated)
236     {
237         internal::addIntegrityData(packet, outMessage, payloadLen);
238     }
239 
240     return packet;
241 }
242 
243 namespace internal
244 {
245 
246 void addSequenceNumber(std::vector<uint8_t>& packet,
247                        std::shared_ptr<session::Session> session)
248 {
249     SessionHeader_t* header = reinterpret_cast<SessionHeader_t*>(packet.data());
250 
251     if (header->sessId == session::SESSION_ZERO)
252     {
253         header->sessSeqNum = 0x00;
254     }
255     else
256     {
257         auto seqNum = session->sequenceNums.increment();
258         header->sessSeqNum = endian::to_ipmi(seqNum);
259     }
260 }
261 
262 bool verifyPacketIntegrity(const std::vector<uint8_t>& packet,
263                            const std::shared_ptr<Message> message,
264                            size_t payloadLen)
265 {
266     /*
267      * Padding bytes are added to cause the number of bytes in the data range
268      * covered by the AuthCode(Integrity Data) field to be a multiple of 4 bytes
269      * .If present each integrity Pad byte is set to FFh. The following logic
270      * calculates the number of padding bytes added in the IPMI packet.
271      */
272     auto paddingLen = 4 - ((payloadLen + 2) & 3);
273 
274     auto sessTrailerPos = sizeof(SessionHeader_t) + payloadLen + paddingLen;
275 
276     auto trailer = reinterpret_cast<const SessionTrailer_t*>(packet.data() +
277                                                              sessTrailerPos);
278 
279     // Check trailer->padLength against paddingLen, both should match up,
280     // return false if the lengths don't match
281     if (trailer->padLength != paddingLen)
282     {
283         return false;
284     }
285 
286     auto session = std::get<session::Manager&>(singletonPool)
287                        .getSession(message->bmcSessionID);
288 
289     auto integrityAlgo = session->getIntegrityAlgo();
290 
291     // Check if Integrity data length is as expected, check integrity data
292     // length is same as the length expected for the Integrity Algorithm that
293     // was negotiated during the session open process.
294     if ((packet.size() - sessTrailerPos - sizeof(SessionTrailer_t)) !=
295         integrityAlgo->authCodeLength)
296     {
297         return false;
298     }
299 
300     auto integrityIter = packet.cbegin();
301     std::advance(integrityIter, sessTrailerPos + sizeof(SessionTrailer_t));
302 
303     // The integrity data is calculated from the AuthType/Format field up to and
304     // including the field that immediately precedes the AuthCode field itself.
305     size_t length = packet.size() - integrityAlgo->authCodeLength -
306                     message::parser::RMCP_SESSION_HEADER_SIZE;
307 
308     return integrityAlgo->verifyIntegrityData(packet, length, integrityIter);
309 }
310 
311 void addIntegrityData(std::vector<uint8_t>& packet,
312                       const std::shared_ptr<Message> message, size_t payloadLen)
313 {
314     // The following logic calculates the number of padding bytes to be added to
315     // IPMI packet. If needed each integrity Pad byte is set to FFh.
316     auto paddingLen = 4 - ((payloadLen + 2) & 3);
317     packet.insert(packet.end(), paddingLen, 0xFF);
318 
319     packet.resize(packet.size() + sizeof(SessionTrailer_t));
320 
321     auto trailer = reinterpret_cast<SessionTrailer_t*>(
322         packet.data() + packet.size() - sizeof(SessionTrailer_t));
323 
324     trailer->padLength = paddingLen;
325     trailer->nextHeader = parser::RMCP_MESSAGE_CLASS_IPMI;
326 
327     auto session = std::get<session::Manager&>(singletonPool)
328                        .getSession(message->bmcSessionID);
329 
330     auto integrityData =
331         session->getIntegrityAlgo()->generateIntegrityData(packet);
332 
333     packet.insert(packet.end(), integrityData.begin(), integrityData.end());
334 }
335 
336 std::vector<uint8_t> decryptPayload(const std::vector<uint8_t>& packet,
337                                     const std::shared_ptr<Message> message,
338                                     size_t payloadLen)
339 {
340     auto session = std::get<session::Manager&>(singletonPool)
341                        .getSession(message->bmcSessionID);
342 
343     return session->getCryptAlgo()->decryptPayload(
344         packet, sizeof(SessionHeader_t), payloadLen);
345 }
346 
347 std::vector<uint8_t> encryptPayload(std::shared_ptr<Message> message)
348 {
349     auto session = std::get<session::Manager&>(singletonPool)
350                        .getSession(message->bmcSessionID);
351 
352     return session->getCryptAlgo()->encryptPayload(message->payload);
353 }
354 
355 } // namespace internal
356 
357 } // namespace ipmi20parser
358 
359 } // namespace message
360