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(RmcpHeader_t)) 21 { 22 throw std::runtime_error("RMCP Header missing"); 23 } 24 25 auto rmcpHeaderPtr = reinterpret_cast<RmcpHeader_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 < static_cast<uint8_t>(ClassOfMsg::ASF) && 31 rmcpHeaderPtr->classOfMsg > static_cast<uint8_t>(ClassOfMsg::OEM))) 32 { 33 throw std::runtime_error("RMCP Header is invalid"); 34 } 35 36 if (rmcpHeaderPtr->classOfMsg == static_cast<uint8_t>(ClassOfMsg::ASF)) 37 { 38 #ifndef RMCP_PING 39 throw std::runtime_error("RMCP Ping is not supported"); 40 #else 41 return std::make_tuple(asfparser::unflatten(inPacket), 42 SessionHeader::IPMI15); 43 #endif // RMCP_PING 44 } 45 46 auto sessionHeaderPtr = reinterpret_cast<BasicHeader_t*>(inPacket.data()); 47 48 // Read the Session Header and invoke the parser corresponding to the 49 // header type 50 switch (static_cast<SessionHeader>(sessionHeaderPtr->format.formatType)) 51 { 52 case SessionHeader::IPMI15: 53 { 54 return std::make_tuple(ipmi15parser::unflatten(inPacket), 55 SessionHeader::IPMI15); 56 } 57 case SessionHeader::IPMI20: 58 { 59 return std::make_tuple(ipmi20parser::unflatten(inPacket), 60 SessionHeader::IPMI20); 61 } 62 default: 63 { 64 throw std::runtime_error("Invalid Session Header"); 65 } 66 } 67 } 68 69 std::vector<uint8_t> flatten(const std::shared_ptr<Message>& outMessage, 70 SessionHeader authType, 71 const std::shared_ptr<session::Session>& session) 72 { 73 // Call the flatten routine based on the header type 74 switch (authType) 75 { 76 case SessionHeader::IPMI15: 77 { 78 return ipmi15parser::flatten(outMessage, session); 79 } 80 case SessionHeader::IPMI20: 81 { 82 return ipmi20parser::flatten(outMessage, session); 83 } 84 default: 85 { 86 return {}; 87 } 88 } 89 } 90 91 } // namespace parser 92 93 namespace ipmi15parser 94 { 95 96 std::shared_ptr<Message> unflatten(std::vector<uint8_t>& inPacket) 97 { 98 if (inPacket.size() < sizeof(SessionHeader_t)) 99 { 100 throw std::runtime_error("IPMI1.5 Session Header Missing"); 101 } 102 103 auto header = reinterpret_cast<SessionHeader_t*>(inPacket.data()); 104 105 uint32_t sessionID = endian::from_ipmi(header->sessId); 106 if (sessionID != session::sessionZero) 107 { 108 throw std::runtime_error("IPMI1.5 session packets are unsupported"); 109 } 110 111 auto message = std::make_shared<Message>(); 112 113 message->payloadType = PayloadType::IPMI; 114 message->bmcSessionID = session::sessionZero; 115 message->sessionSeqNum = endian::from_ipmi(header->sessSeqNum); 116 message->isPacketEncrypted = false; 117 message->isPacketAuthenticated = false; 118 message->rmcpMsgClass = 119 static_cast<ClassOfMsg>(header->base.rmcp.classOfMsg); 120 121 // Confirm the number of data bytes received correlates to 122 // the packet length in the header 123 size_t payloadLen = header->payloadLength; 124 if ((payloadLen == 0) || (inPacket.size() < (sizeof(*header) + payloadLen))) 125 { 126 throw std::runtime_error("Invalid data length"); 127 } 128 129 (message->payload) 130 .assign(inPacket.data() + sizeof(SessionHeader_t), 131 inPacket.data() + sizeof(SessionHeader_t) + payloadLen); 132 133 return message; 134 } 135 136 std::vector<uint8_t> 137 flatten(const std::shared_ptr<Message>& outMessage, 138 const std::shared_ptr<session::Session>& /* session */) 139 { 140 std::vector<uint8_t> packet(sizeof(SessionHeader_t)); 141 142 // Insert Session Header into the Packet 143 auto header = reinterpret_cast<SessionHeader_t*>(packet.data()); 144 header->base.rmcp.version = parser::RMCP_VERSION; 145 header->base.rmcp.reserved = 0x00; 146 header->base.rmcp.rmcpSeqNum = parser::RMCP_SEQ; 147 header->base.rmcp.classOfMsg = static_cast<uint8_t>(ClassOfMsg::IPMI); 148 header->base.format.formatType = 149 static_cast<uint8_t>(parser::SessionHeader::IPMI15); 150 header->sessSeqNum = 0; 151 header->sessId = endian::to_ipmi(outMessage->rcSessionID); 152 153 header->payloadLength = static_cast<uint8_t>(outMessage->payload.size()); 154 155 // Insert the Payload into the Packet 156 packet.insert(packet.end(), outMessage->payload.begin(), 157 outMessage->payload.end()); 158 159 // Insert the Session Trailer 160 packet.resize(packet.size() + sizeof(SessionTrailer_t)); 161 auto trailer = 162 reinterpret_cast<SessionTrailer_t*>(packet.data() + packet.size()); 163 trailer->legacyPad = 0x00; 164 165 return packet; 166 } 167 168 } // namespace ipmi15parser 169 170 namespace ipmi20parser 171 { 172 173 std::shared_ptr<Message> unflatten(std::vector<uint8_t>& inPacket) 174 { 175 // Check if the packet has atleast the Session Header 176 if (inPacket.size() < sizeof(SessionHeader_t)) 177 { 178 throw std::runtime_error("IPMI2.0 Session Header Missing"); 179 } 180 181 auto header = reinterpret_cast<SessionHeader_t*>(inPacket.data()); 182 183 uint32_t sessionID = endian::from_ipmi(header->sessId); 184 185 auto session = session::Manager::get().getSession(sessionID); 186 if (!session) 187 { 188 throw std::runtime_error("RMCP+ message from unknown session"); 189 } 190 191 auto message = std::make_shared<Message>(); 192 193 message->payloadType = static_cast<PayloadType>(header->payloadType & 0x3F); 194 message->bmcSessionID = sessionID; 195 message->sessionSeqNum = endian::from_ipmi(header->sessSeqNum); 196 message->isPacketEncrypted = 197 ((header->payloadType & PAYLOAD_ENCRYPT_MASK) ? true : false); 198 message->isPacketAuthenticated = 199 ((header->payloadType & PAYLOAD_AUTH_MASK) ? true : false); 200 message->rmcpMsgClass = 201 static_cast<ClassOfMsg>(header->base.rmcp.classOfMsg); 202 203 // Confirm the number of data bytes received correlates to 204 // the packet length in the header 205 size_t payloadLen = endian::from_ipmi(header->payloadLength); 206 if ((payloadLen == 0) || (inPacket.size() < (sizeof(*header) + payloadLen))) 207 { 208 throw std::runtime_error("Invalid data length"); 209 } 210 211 bool integrityMismatch = session->isIntegrityAlgoEnabled() && 212 !message->isPacketAuthenticated; 213 bool encryptMismatch = session->isCryptAlgoEnabled() && 214 !message->isPacketEncrypted; 215 216 if (sessionID != session::sessionZero && 217 (integrityMismatch || encryptMismatch)) 218 { 219 throw std::runtime_error("unencrypted or unauthenticated message"); 220 } 221 222 if (message->isPacketAuthenticated) 223 { 224 if (!(internal::verifyPacketIntegrity(inPacket, message, payloadLen, 225 session))) 226 { 227 throw std::runtime_error("Packet Integrity check failed"); 228 } 229 } 230 231 // Decrypt the payload if the payload is encrypted 232 if (message->isPacketEncrypted) 233 { 234 // Assign the decrypted payload to the IPMI Message 235 message->payload = internal::decryptPayload(inPacket, message, 236 payloadLen, session); 237 } 238 else 239 { 240 message->payload.assign(inPacket.begin() + sizeof(SessionHeader_t), 241 inPacket.begin() + sizeof(SessionHeader_t) + 242 payloadLen); 243 } 244 245 return message; 246 } 247 248 std::vector<uint8_t> flatten(const std::shared_ptr<Message>& outMessage, 249 const std::shared_ptr<session::Session>& session) 250 { 251 std::vector<uint8_t> packet(sizeof(SessionHeader_t)); 252 253 SessionHeader_t* header = reinterpret_cast<SessionHeader_t*>(packet.data()); 254 header->base.rmcp.version = parser::RMCP_VERSION; 255 header->base.rmcp.reserved = 0x00; 256 header->base.rmcp.rmcpSeqNum = parser::RMCP_SEQ; 257 header->base.rmcp.classOfMsg = static_cast<uint8_t>(ClassOfMsg::IPMI); 258 header->base.format.formatType = 259 static_cast<uint8_t>(parser::SessionHeader::IPMI20); 260 header->payloadType = static_cast<uint8_t>(outMessage->payloadType); 261 header->sessId = endian::to_ipmi(outMessage->rcSessionID); 262 263 // Add session sequence number 264 internal::addSequenceNumber(packet, session); 265 266 size_t payloadLen = 0; 267 268 // Encrypt the payload if needed 269 if (outMessage->isPacketEncrypted) 270 { 271 header->payloadType |= PAYLOAD_ENCRYPT_MASK; 272 auto cipherPayload = internal::encryptPayload(outMessage, session); 273 payloadLen = cipherPayload.size(); 274 header->payloadLength = endian::to_ipmi<uint16_t>(cipherPayload.size()); 275 276 // Insert the encrypted payload into the outgoing IPMI packet 277 packet.insert(packet.end(), cipherPayload.begin(), cipherPayload.end()); 278 } 279 else 280 { 281 header->payloadLength = 282 endian::to_ipmi<uint16_t>(outMessage->payload.size()); 283 payloadLen = outMessage->payload.size(); 284 285 // Insert the Payload into the Packet 286 packet.insert(packet.end(), outMessage->payload.begin(), 287 outMessage->payload.end()); 288 } 289 290 if (outMessage->isPacketAuthenticated) 291 { 292 header = reinterpret_cast<SessionHeader_t*>(packet.data()); 293 header->payloadType |= PAYLOAD_AUTH_MASK; 294 internal::addIntegrityData(packet, outMessage, payloadLen, session); 295 } 296 297 return packet; 298 } 299 300 namespace internal 301 { 302 303 void addSequenceNumber(std::vector<uint8_t>& packet, 304 const std::shared_ptr<session::Session>& session) 305 { 306 SessionHeader_t* header = reinterpret_cast<SessionHeader_t*>(packet.data()); 307 308 if (header->sessId == session::sessionZero) 309 { 310 header->sessSeqNum = 0x00; 311 } 312 else 313 { 314 auto seqNum = session->sequenceNums.increment(); 315 header->sessSeqNum = endian::to_ipmi(seqNum); 316 } 317 } 318 319 bool verifyPacketIntegrity(const std::vector<uint8_t>& packet, 320 const std::shared_ptr<Message>& /* message */, 321 size_t payloadLen, 322 const std::shared_ptr<session::Session>& session) 323 { 324 /* 325 * Padding bytes are added to cause the number of bytes in the data range 326 * covered by the AuthCode(Integrity Data) field to be a multiple of 4 bytes 327 * .If present each integrity Pad byte is set to FFh. The following logic 328 * calculates the number of padding bytes added in the IPMI packet. 329 */ 330 auto paddingLen = 4 - ((payloadLen + 2) & 3); 331 332 auto sessTrailerPos = sizeof(SessionHeader_t) + payloadLen + paddingLen; 333 334 // verify packet size includes trailer struct starts at sessTrailerPos 335 if (packet.size() < (sessTrailerPos + sizeof(SessionTrailer_t))) 336 { 337 return false; 338 } 339 340 auto trailer = reinterpret_cast<const SessionTrailer_t*>(packet.data() + 341 sessTrailerPos); 342 343 // Check trailer->padLength against paddingLen, both should match up, 344 // return false if the lengths don't match 345 if (trailer->padLength != paddingLen) 346 { 347 return false; 348 } 349 350 auto integrityAlgo = session->getIntegrityAlgo(); 351 352 // Check if Integrity data length is as expected, check integrity data 353 // length is same as the length expected for the Integrity Algorithm that 354 // was negotiated during the session open process. 355 if ((packet.size() - sessTrailerPos - sizeof(SessionTrailer_t)) != 356 integrityAlgo->authCodeLength) 357 { 358 return false; 359 } 360 361 auto integrityIter = packet.cbegin(); 362 std::advance(integrityIter, sessTrailerPos + sizeof(SessionTrailer_t)); 363 364 // The integrity data is calculated from the AuthType/Format field up to and 365 // including the field that immediately precedes the AuthCode field itself. 366 size_t length = packet.size() - integrityAlgo->authCodeLength - 367 message::parser::RMCP_SESSION_HEADER_SIZE; 368 369 return integrityAlgo->verifyIntegrityData(packet, length, integrityIter, 370 packet.cend()); 371 } 372 373 void addIntegrityData(std::vector<uint8_t>& packet, 374 const std::shared_ptr<Message>& /* message */, 375 size_t payloadLen, 376 const std::shared_ptr<session::Session>& session) 377 { 378 // The following logic calculates the number of padding bytes to be added to 379 // IPMI packet. If needed each integrity Pad byte is set to FFh. 380 auto paddingLen = 4 - ((payloadLen + 2) & 3); 381 packet.insert(packet.end(), paddingLen, 0xFF); 382 383 packet.resize(packet.size() + sizeof(SessionTrailer_t)); 384 385 auto trailer = reinterpret_cast<SessionTrailer_t*>( 386 packet.data() + packet.size() - sizeof(SessionTrailer_t)); 387 388 trailer->padLength = paddingLen; 389 trailer->nextHeader = parser::RMCP_MESSAGE_CLASS_IPMI; 390 391 auto integrityData = 392 session->getIntegrityAlgo()->generateIntegrityData(packet); 393 394 packet.insert(packet.end(), integrityData.begin(), integrityData.end()); 395 } 396 397 std::vector<uint8_t> 398 decryptPayload(const std::vector<uint8_t>& packet, 399 const std::shared_ptr<Message>& /* message */, 400 size_t payloadLen, 401 const std::shared_ptr<session::Session>& session) 402 { 403 return session->getCryptAlgo()->decryptPayload( 404 packet, sizeof(SessionHeader_t), payloadLen); 405 } 406 407 std::vector<uint8_t> 408 encryptPayload(const std::shared_ptr<Message>& message, 409 const std::shared_ptr<session::Session>& session) 410 { 411 return session->getCryptAlgo()->encryptPayload(message->payload); 412 } 413 414 } // namespace internal 415 416 } // namespace ipmi20parser 417 418 #ifdef RMCP_PING 419 namespace asfparser 420 { 421 std::shared_ptr<Message> unflatten(std::vector<uint8_t>& inPacket) 422 { 423 auto message = std::make_shared<Message>(); 424 425 auto header = reinterpret_cast<AsfMessagePing_t*>(inPacket.data()); 426 427 message->payloadType = PayloadType::IPMI; 428 message->rmcpMsgClass = ClassOfMsg::ASF; 429 message->asfMsgTag = header->msgTag; 430 431 return message; 432 } 433 434 std::vector<uint8_t> flatten(uint8_t asfMsgTag) 435 { 436 std::vector<uint8_t> packet(sizeof(AsfMessagePong_t)); 437 438 // Insert RMCP header into the Packet 439 auto header = reinterpret_cast<AsfMessagePong_t*>(packet.data()); 440 header->ping.rmcp.version = parser::RMCP_VERSION; 441 header->ping.rmcp.reserved = 0x00; 442 header->ping.rmcp.rmcpSeqNum = parser::RMCP_SEQ; 443 header->ping.rmcp.classOfMsg = static_cast<uint8_t>(ClassOfMsg::ASF); 444 445 // No OEM-specific capabilities exist, therefore the second 446 // IANA Enterprise Number contains the same IANA(4542) 447 header->ping.iana = header->iana = endian::to_ipmi(parser::ASF_IANA); 448 header->ping.msgType = static_cast<uint8_t>(RmcpMsgType::PONG); 449 header->ping.msgTag = asfMsgTag; 450 header->ping.reserved = 0x00; 451 header->ping.dataLen = 452 parser::RMCP_ASF_PONG_DATA_LEN; // as per spec 13.2.4, 453 454 header->iana = parser::ASF_IANA; 455 header->oemDefined = 0x00; 456 header->suppEntities = parser::ASF_SUPP_ENT; 457 header->suppInteract = parser::ASF_SUPP_INT; 458 header->reserved1 = 0x00; 459 header->reserved2 = 0x00; 460 461 return packet; 462 } 463 464 } // namespace asfparser 465 #endif // RMCP_PING 466 467 } // namespace message 468