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         header = reinterpret_cast<SessionHeader_t*>(packet.data());
238         header->payloadType |= PAYLOAD_AUTH_MASK;
239         internal::addIntegrityData(packet, outMessage, payloadLen);
240     }
241 
242     return packet;
243 }
244 
245 namespace internal
246 {
247 
248 void addSequenceNumber(std::vector<uint8_t>& packet,
249                        std::shared_ptr<session::Session> session)
250 {
251     SessionHeader_t* header = reinterpret_cast<SessionHeader_t*>(packet.data());
252 
253     if (header->sessId == session::sessionZero)
254     {
255         header->sessSeqNum = 0x00;
256     }
257     else
258     {
259         auto seqNum = session->sequenceNums.increment();
260         header->sessSeqNum = endian::to_ipmi(seqNum);
261     }
262 }
263 
264 bool verifyPacketIntegrity(const std::vector<uint8_t>& packet,
265                            const std::shared_ptr<Message> message,
266                            size_t payloadLen)
267 {
268     /*
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      */
274     auto paddingLen = 4 - ((payloadLen + 2) & 3);
275 
276     auto sessTrailerPos = sizeof(SessionHeader_t) + payloadLen + paddingLen;
277 
278     auto trailer = reinterpret_cast<const SessionTrailer_t*>(packet.data() +
279                                                              sessTrailerPos);
280 
281     // Check trailer->padLength against paddingLen, both should match up,
282     // return false if the lengths don't match
283     if (trailer->padLength != paddingLen)
284     {
285         return false;
286     }
287 
288     auto session = std::get<session::Manager&>(singletonPool)
289                        .getSession(message->bmcSessionID);
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.
296     if ((packet.size() - sessTrailerPos - sizeof(SessionTrailer_t)) !=
297         integrityAlgo->authCodeLength)
298     {
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 
313 void addIntegrityData(std::vector<uint8_t>& packet,
314                       const std::shared_ptr<Message> message, size_t payloadLen)
315 {
316     // 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.
318     auto paddingLen = 4 - ((payloadLen + 2) & 3);
319     packet.insert(packet.end(), paddingLen, 0xFF);
320 
321     packet.resize(packet.size() + sizeof(SessionTrailer_t));
322 
323     auto trailer = reinterpret_cast<SessionTrailer_t*>(
324         packet.data() + packet.size() - sizeof(SessionTrailer_t));
325 
326     trailer->padLength = paddingLen;
327     trailer->nextHeader = parser::RMCP_MESSAGE_CLASS_IPMI;
328 
329     auto session = std::get<session::Manager&>(singletonPool)
330                        .getSession(message->bmcSessionID);
331 
332     auto integrityData =
333         session->getIntegrityAlgo()->generateIntegrityData(packet);
334 
335     packet.insert(packet.end(), integrityData.begin(), integrityData.end());
336 }
337 
338 std::vector<uint8_t> decryptPayload(const std::vector<uint8_t>& packet,
339                                     const std::shared_ptr<Message> message,
340                                     size_t payloadLen)
341 {
342     auto session = std::get<session::Manager&>(singletonPool)
343                        .getSession(message->bmcSessionID);
344 
345     return session->getCryptAlgo()->decryptPayload(
346         packet, sizeof(SessionHeader_t), payloadLen);
347 }
348 
349 std::vector<uint8_t> encryptPayload(std::shared_ptr<Message> message)
350 {
351     auto session = std::get<session::Manager&>(singletonPool)
352                        .getSession(message->bmcSessionID);
353 
354     return session->getCryptAlgo()->encryptPayload(message->payload);
355 }
356 
357 } // namespace internal
358 
359 } // namespace ipmi20parser
360 
361 } // namespace message
362