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(std::shared_ptr<Message> outMessage, 70 SessionHeader authType, 71 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(std::shared_ptr<Message> outMessage, 137 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 { 225 throw std::runtime_error("Packet Integrity check failed"); 226 } 227 } 228 229 // Decrypt the payload if the payload is encrypted 230 if (message->isPacketEncrypted) 231 { 232 // Assign the decrypted payload to the IPMI Message 233 message->payload = 234 internal::decryptPayload(inPacket, message, payloadLen); 235 } 236 else 237 { 238 message->payload.assign(inPacket.begin() + sizeof(SessionHeader_t), 239 inPacket.begin() + sizeof(SessionHeader_t) + 240 payloadLen); 241 } 242 243 return message; 244 } 245 246 std::vector<uint8_t> flatten(std::shared_ptr<Message> outMessage, 247 std::shared_ptr<session::Session> session) 248 { 249 std::vector<uint8_t> packet(sizeof(SessionHeader_t)); 250 251 SessionHeader_t* header = reinterpret_cast<SessionHeader_t*>(packet.data()); 252 header->base.rmcp.version = parser::RMCP_VERSION; 253 header->base.rmcp.reserved = 0x00; 254 header->base.rmcp.rmcpSeqNum = parser::RMCP_SEQ; 255 header->base.rmcp.classOfMsg = static_cast<uint8_t>(ClassOfMsg::IPMI); 256 header->base.format.formatType = 257 static_cast<uint8_t>(parser::SessionHeader::IPMI20); 258 header->payloadType = static_cast<uint8_t>(outMessage->payloadType); 259 header->sessId = endian::to_ipmi(outMessage->rcSessionID); 260 261 // Add session sequence number 262 internal::addSequenceNumber(packet, session); 263 264 size_t payloadLen = 0; 265 266 // Encrypt the payload if needed 267 if (outMessage->isPacketEncrypted) 268 { 269 header->payloadType |= PAYLOAD_ENCRYPT_MASK; 270 auto cipherPayload = internal::encryptPayload(outMessage); 271 payloadLen = cipherPayload.size(); 272 header->payloadLength = endian::to_ipmi<uint16_t>(cipherPayload.size()); 273 274 // Insert the encrypted payload into the outgoing IPMI packet 275 packet.insert(packet.end(), cipherPayload.begin(), cipherPayload.end()); 276 } 277 else 278 { 279 header->payloadLength = 280 endian::to_ipmi<uint16_t>(outMessage->payload.size()); 281 payloadLen = outMessage->payload.size(); 282 283 // Insert the Payload into the Packet 284 packet.insert(packet.end(), outMessage->payload.begin(), 285 outMessage->payload.end()); 286 } 287 288 if (outMessage->isPacketAuthenticated) 289 { 290 header = reinterpret_cast<SessionHeader_t*>(packet.data()); 291 header->payloadType |= PAYLOAD_AUTH_MASK; 292 internal::addIntegrityData(packet, outMessage, payloadLen); 293 } 294 295 return packet; 296 } 297 298 namespace internal 299 { 300 301 void addSequenceNumber(std::vector<uint8_t>& packet, 302 std::shared_ptr<session::Session> session) 303 { 304 SessionHeader_t* header = reinterpret_cast<SessionHeader_t*>(packet.data()); 305 306 if (header->sessId == session::sessionZero) 307 { 308 header->sessSeqNum = 0x00; 309 } 310 else 311 { 312 auto seqNum = session->sequenceNums.increment(); 313 header->sessSeqNum = endian::to_ipmi(seqNum); 314 } 315 } 316 317 bool verifyPacketIntegrity(const std::vector<uint8_t>& packet, 318 const std::shared_ptr<Message> message, 319 size_t payloadLen) 320 { 321 /* 322 * Padding bytes are added to cause the number of bytes in the data range 323 * covered by the AuthCode(Integrity Data) field to be a multiple of 4 bytes 324 * .If present each integrity Pad byte is set to FFh. The following logic 325 * calculates the number of padding bytes added in the IPMI packet. 326 */ 327 auto paddingLen = 4 - ((payloadLen + 2) & 3); 328 329 auto sessTrailerPos = sizeof(SessionHeader_t) + payloadLen + paddingLen; 330 331 // verify packet size includes trailer struct starts at sessTrailerPos 332 if (packet.size() < (sessTrailerPos + sizeof(SessionTrailer_t))) 333 { 334 return false; 335 } 336 337 auto trailer = reinterpret_cast<const SessionTrailer_t*>(packet.data() + 338 sessTrailerPos); 339 340 // Check trailer->padLength against paddingLen, both should match up, 341 // return false if the lengths don't match 342 if (trailer->padLength != paddingLen) 343 { 344 return false; 345 } 346 347 auto session = session::Manager::get().getSession(message->bmcSessionID); 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, size_t payloadLen) 373 { 374 // The following logic calculates the number of padding bytes to be added to 375 // IPMI packet. If needed each integrity Pad byte is set to FFh. 376 auto paddingLen = 4 - ((payloadLen + 2) & 3); 377 packet.insert(packet.end(), paddingLen, 0xFF); 378 379 packet.resize(packet.size() + sizeof(SessionTrailer_t)); 380 381 auto trailer = reinterpret_cast<SessionTrailer_t*>( 382 packet.data() + packet.size() - sizeof(SessionTrailer_t)); 383 384 trailer->padLength = paddingLen; 385 trailer->nextHeader = parser::RMCP_MESSAGE_CLASS_IPMI; 386 387 auto session = session::Manager::get().getSession(message->bmcSessionID); 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> decryptPayload(const std::vector<uint8_t>& packet, 396 const std::shared_ptr<Message> message, 397 size_t payloadLen) 398 { 399 auto session = session::Manager::get().getSession(message->bmcSessionID); 400 401 return session->getCryptAlgo()->decryptPayload( 402 packet, sizeof(SessionHeader_t), payloadLen); 403 } 404 405 std::vector<uint8_t> encryptPayload(std::shared_ptr<Message> message) 406 { 407 auto session = session::Manager::get().getSession(message->bmcSessionID); 408 409 return session->getCryptAlgo()->encryptPayload(message->payload); 410 } 411 412 } // namespace internal 413 414 } // namespace ipmi20parser 415 416 #ifdef RMCP_PING 417 namespace asfparser 418 { 419 std::shared_ptr<Message> unflatten(std::vector<uint8_t>& inPacket) 420 { 421 auto message = std::make_shared<Message>(); 422 423 auto header = reinterpret_cast<AsfMessagePing_t*>(inPacket.data()); 424 425 message->payloadType = PayloadType::IPMI; 426 message->rmcpMsgClass = ClassOfMsg::ASF; 427 message->asfMsgTag = header->msgTag; 428 429 return message; 430 } 431 432 std::vector<uint8_t> flatten(uint8_t asfMsgTag) 433 { 434 std::vector<uint8_t> packet(sizeof(AsfMessagePong_t)); 435 436 // Insert RMCP header into the Packet 437 auto header = reinterpret_cast<AsfMessagePong_t*>(packet.data()); 438 header->ping.rmcp.version = parser::RMCP_VERSION; 439 header->ping.rmcp.reserved = 0x00; 440 header->ping.rmcp.rmcpSeqNum = parser::RMCP_SEQ; 441 header->ping.rmcp.classOfMsg = static_cast<uint8_t>(ClassOfMsg::ASF); 442 443 // No OEM-specific capabilities exist, therefore the second 444 // IANA Enterprise Number contains the same IANA(4542) 445 header->ping.iana = header->iana = endian::to_ipmi(parser::ASF_IANA); 446 header->ping.msgType = static_cast<uint8_t>(RmcpMsgType::PONG); 447 header->ping.msgTag = asfMsgTag; 448 header->ping.reserved = 0x00; 449 header->ping.dataLen = 450 parser::RMCP_ASF_PONG_DATA_LEN; // as per spec 13.2.4, 451 452 header->iana = parser::ASF_IANA; 453 header->oemDefined = 0x00; 454 header->suppEntities = parser::ASF_SUPP_ENT; 455 header->suppInteract = parser::ASF_SUPP_INT; 456 header->reserved1 = 0x00; 457 header->reserved2 = 0x00; 458 459 return packet; 460 } 461 462 } // namespace asfparser 463 #endif // RMCP_PING 464 465 } // namespace message 466