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