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 <iostream>
9 #include <memory>
10 
11 namespace message
12 {
13 
14 namespace parser
15 {
16 
17 std::tuple<std::unique_ptr<Message>, SessionHeader>
18     unflatten(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 
57 std::vector<uint8_t> flatten(Message& outMessage, SessionHeader authType,
58                              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::unique_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_unique<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(Message& outMessage, session::Session& session)
111 {
112     std::vector<uint8_t> packet(sizeof(SessionHeader_t));
113 
114     // Insert Session Header into the Packet
115     auto header = reinterpret_cast<SessionHeader_t*>(packet.data());
116     header->base.version = parser::RMCP_VERSION;
117     header->base.reserved = 0x00;
118     header->base.rmcpSeqNum = parser::RMCP_SEQ;
119     header->base.classOfMsg = parser::RMCP_MESSAGE_CLASS_IPMI;
120     header->base.format.formatType =
121         static_cast<uint8_t>(parser::SessionHeader::IPMI15);
122     header->sessSeqNum = 0;
123     header->sessId = endian::to_ipmi(outMessage.rcSessionID);
124 
125     header->payloadLength = static_cast<uint8_t>(outMessage.payload.size());
126 
127     // Insert the Payload into the Packet
128     packet.insert(packet.end(), outMessage.payload.begin(),
129                   outMessage.payload.end());
130 
131     // Insert the Session Trailer
132     packet.resize(packet.size() + sizeof(SessionTrailer_t));
133     auto trailer =
134         reinterpret_cast<SessionTrailer_t*>(packet.data() + packet.size());
135     trailer->legacyPad = 0x00;
136 
137     return packet;
138 }
139 
140 } // namespace ipmi15parser
141 
142 namespace ipmi20parser
143 {
144 
145 std::unique_ptr<Message> unflatten(std::vector<uint8_t>& inPacket)
146 {
147     // Check if the packet has atleast the Session Header
148     if (inPacket.size() < sizeof(SessionHeader_t))
149     {
150         throw std::runtime_error("IPMI2.0 Session Header Missing");
151     }
152 
153     auto message = std::make_unique<Message>();
154 
155     auto header = reinterpret_cast<SessionHeader_t*>(inPacket.data());
156 
157     message->payloadType = static_cast<PayloadType>(header->payloadType & 0x3F);
158     message->bmcSessionID = endian::from_ipmi(header->sessId);
159     message->sessionSeqNum = endian::from_ipmi(header->sessSeqNum);
160     message->isPacketEncrypted =
161         ((header->payloadType & PAYLOAD_ENCRYPT_MASK) ? true : false);
162     message->isPacketAuthenticated =
163         ((header->payloadType & PAYLOAD_AUTH_MASK) ? true : false);
164 
165     auto payloadLen = endian::from_ipmi(header->payloadLength);
166 
167     if (message->isPacketAuthenticated)
168     {
169         if (!(internal::verifyPacketIntegrity(inPacket, *(message.get()),
170                                               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.get()), 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(Message& outMessage, session::Session& session)
194 {
195     std::vector<uint8_t> packet(sizeof(SessionHeader_t));
196 
197     SessionHeader_t* header = reinterpret_cast<SessionHeader_t*>(packet.data());
198     header->base.version = parser::RMCP_VERSION;
199     header->base.reserved = 0x00;
200     header->base.rmcpSeqNum = parser::RMCP_SEQ;
201     header->base.classOfMsg = parser::RMCP_MESSAGE_CLASS_IPMI;
202     header->base.format.formatType =
203         static_cast<uint8_t>(parser::SessionHeader::IPMI20);
204     header->payloadType = static_cast<uint8_t>(outMessage.payloadType);
205     header->sessId = endian::to_ipmi(outMessage.rcSessionID);
206 
207     // Add session sequence number
208     internal::addSequenceNumber(packet, session);
209 
210     size_t payloadLen = 0;
211 
212     // Encrypt the payload if needed
213     if (outMessage.isPacketEncrypted)
214     {
215         header->payloadType |= PAYLOAD_ENCRYPT_MASK;
216         auto cipherPayload = internal::encryptPayload(outMessage);
217         payloadLen = cipherPayload.size();
218         header->payloadLength = endian::to_ipmi<uint16_t>(cipherPayload.size());
219 
220         // Insert the encrypted payload into the outgoing IPMI packet
221         packet.insert(packet.end(), cipherPayload.begin(), cipherPayload.end());
222     }
223     else
224     {
225         header->payloadLength =
226             endian::to_ipmi<uint16_t>(outMessage.payload.size());
227         payloadLen = outMessage.payload.size();
228 
229         // Insert the Payload into the Packet
230         packet.insert(packet.end(), outMessage.payload.begin(),
231                       outMessage.payload.end());
232     }
233 
234     if (outMessage.isPacketAuthenticated)
235     {
236         internal::addIntegrityData(packet, outMessage, payloadLen);
237     }
238 
239     return packet;
240 }
241 
242 namespace internal
243 {
244 
245 void addSequenceNumber(std::vector<uint8_t>& packet, session::Session& session)
246 {
247     SessionHeader_t* header = reinterpret_cast<SessionHeader_t*>(packet.data());
248 
249     if (header->sessId == session::SESSION_ZERO)
250     {
251         header->sessSeqNum = 0x00;
252     }
253     else
254     {
255         auto seqNum = session.sequenceNums.increment();
256         header->sessSeqNum = endian::to_ipmi(seqNum);
257     }
258 }
259 
260 bool verifyPacketIntegrity(const std::vector<uint8_t>& packet,
261                            const Message& message, size_t payloadLen)
262 {
263     /*
264      * Padding bytes are added to cause the number of bytes in the data range
265      * covered by the AuthCode(Integrity Data) field to be a multiple of 4 bytes
266      * .If present each integrity Pad byte is set to FFh. The following logic
267      * calculates the number of padding bytes added in the IPMI packet.
268      */
269     auto paddingLen = 4 - ((payloadLen + 2) & 3);
270 
271     auto sessTrailerPos = sizeof(SessionHeader_t) + payloadLen + paddingLen;
272 
273     auto trailer = reinterpret_cast<const SessionTrailer_t*>(packet.data() +
274                                                              sessTrailerPos);
275 
276     // Check trailer->padLength against paddingLen, both should match up,
277     // return false if the lengths don't match
278     if (trailer->padLength != paddingLen)
279     {
280         return false;
281     }
282 
283     auto session = (std::get<session::Manager&>(singletonPool)
284                         .getSession(message.bmcSessionID))
285                        .lock();
286 
287     auto integrityAlgo = session->getIntegrityAlgo();
288 
289     // Check if Integrity data length is as expected, check integrity data
290     // length is same as the length expected for the Integrity Algorithm that
291     // was negotiated during the session open process.
292     if ((packet.size() - sessTrailerPos - sizeof(SessionTrailer_t)) !=
293         integrityAlgo->authCodeLength)
294     {
295         return false;
296     }
297 
298     auto integrityIter = packet.cbegin();
299     std::advance(integrityIter, sessTrailerPos + sizeof(SessionTrailer_t));
300 
301     // The integrity data is calculated from the AuthType/Format field up to and
302     // including the field that immediately precedes the AuthCode field itself.
303     size_t length = packet.size() - integrityAlgo->authCodeLength -
304                     message::parser::RMCP_SESSION_HEADER_SIZE;
305 
306     return integrityAlgo->verifyIntegrityData(packet, length, integrityIter);
307 }
308 
309 void addIntegrityData(std::vector<uint8_t>& packet, const Message& message,
310                       size_t payloadLen)
311 {
312     // The following logic calculates the number of padding bytes to be added to
313     // IPMI packet. If needed each integrity Pad byte is set to FFh.
314     auto paddingLen = 4 - ((payloadLen + 2) & 3);
315     packet.insert(packet.end(), paddingLen, 0xFF);
316 
317     packet.resize(packet.size() + sizeof(SessionTrailer_t));
318 
319     auto trailer = reinterpret_cast<SessionTrailer_t*>(
320         packet.data() + packet.size() - sizeof(SessionTrailer_t));
321 
322     trailer->padLength = paddingLen;
323     trailer->nextHeader = parser::RMCP_MESSAGE_CLASS_IPMI;
324 
325     auto session = (std::get<session::Manager&>(singletonPool)
326                         .getSession(message.bmcSessionID))
327                        .lock();
328 
329     auto integrityData =
330         session->getIntegrityAlgo()->generateIntegrityData(packet);
331 
332     packet.insert(packet.end(), integrityData.begin(), integrityData.end());
333 }
334 
335 std::vector<uint8_t> decryptPayload(const std::vector<uint8_t>& packet,
336                                     const Message& message, size_t payloadLen)
337 {
338     auto session = (std::get<session::Manager&>(singletonPool)
339                         .getSession(message.bmcSessionID))
340                        .lock();
341 
342     return session->getCryptAlgo()->decryptPayload(
343         packet, sizeof(SessionHeader_t), payloadLen);
344 }
345 
346 std::vector<uint8_t> encryptPayload(Message& message)
347 {
348     auto session = (std::get<session::Manager&>(singletonPool)
349                         .getSession(message.bmcSessionID))
350                        .lock();
351 
352     return session->getCryptAlgo()->encryptPayload(message.payload);
353 }
354 
355 } // namespace internal
356 
357 } // namespace ipmi20parser
358 
359 } // namespace message
360