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