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 internal::addIntegrityData(packet, outMessage, payloadLen); 238 } 239 240 return packet; 241 } 242 243 namespace internal 244 { 245 246 void addSequenceNumber(std::vector<uint8_t>& packet, 247 std::shared_ptr<session::Session> session) 248 { 249 SessionHeader_t* header = reinterpret_cast<SessionHeader_t*>(packet.data()); 250 251 if (header->sessId == session::SESSION_ZERO) 252 { 253 header->sessSeqNum = 0x00; 254 } 255 else 256 { 257 auto seqNum = session->sequenceNums.increment(); 258 header->sessSeqNum = endian::to_ipmi(seqNum); 259 } 260 } 261 262 bool verifyPacketIntegrity(const std::vector<uint8_t>& packet, 263 const std::shared_ptr<Message> message, 264 size_t payloadLen) 265 { 266 /* 267 * Padding bytes are added to cause the number of bytes in the data range 268 * covered by the AuthCode(Integrity Data) field to be a multiple of 4 bytes 269 * .If present each integrity Pad byte is set to FFh. The following logic 270 * calculates the number of padding bytes added in the IPMI packet. 271 */ 272 auto paddingLen = 4 - ((payloadLen + 2) & 3); 273 274 auto sessTrailerPos = sizeof(SessionHeader_t) + payloadLen + paddingLen; 275 276 auto trailer = reinterpret_cast<const SessionTrailer_t*>(packet.data() + 277 sessTrailerPos); 278 279 // Check trailer->padLength against paddingLen, both should match up, 280 // return false if the lengths don't match 281 if (trailer->padLength != paddingLen) 282 { 283 return false; 284 } 285 286 auto session = std::get<session::Manager&>(singletonPool) 287 .getSession(message->bmcSessionID); 288 289 auto integrityAlgo = session->getIntegrityAlgo(); 290 291 // Check if Integrity data length is as expected, check integrity data 292 // length is same as the length expected for the Integrity Algorithm that 293 // was negotiated during the session open process. 294 if ((packet.size() - sessTrailerPos - sizeof(SessionTrailer_t)) != 295 integrityAlgo->authCodeLength) 296 { 297 return false; 298 } 299 300 auto integrityIter = packet.cbegin(); 301 std::advance(integrityIter, sessTrailerPos + sizeof(SessionTrailer_t)); 302 303 // The integrity data is calculated from the AuthType/Format field up to and 304 // including the field that immediately precedes the AuthCode field itself. 305 size_t length = packet.size() - integrityAlgo->authCodeLength - 306 message::parser::RMCP_SESSION_HEADER_SIZE; 307 308 return integrityAlgo->verifyIntegrityData(packet, length, integrityIter); 309 } 310 311 void addIntegrityData(std::vector<uint8_t>& packet, 312 const std::shared_ptr<Message> message, size_t payloadLen) 313 { 314 // The following logic calculates the number of padding bytes to be added to 315 // IPMI packet. If needed each integrity Pad byte is set to FFh. 316 auto paddingLen = 4 - ((payloadLen + 2) & 3); 317 packet.insert(packet.end(), paddingLen, 0xFF); 318 319 packet.resize(packet.size() + sizeof(SessionTrailer_t)); 320 321 auto trailer = reinterpret_cast<SessionTrailer_t*>( 322 packet.data() + packet.size() - sizeof(SessionTrailer_t)); 323 324 trailer->padLength = paddingLen; 325 trailer->nextHeader = parser::RMCP_MESSAGE_CLASS_IPMI; 326 327 auto session = std::get<session::Manager&>(singletonPool) 328 .getSession(message->bmcSessionID); 329 330 auto integrityData = 331 session->getIntegrityAlgo()->generateIntegrityData(packet); 332 333 packet.insert(packet.end(), integrityData.begin(), integrityData.end()); 334 } 335 336 std::vector<uint8_t> decryptPayload(const std::vector<uint8_t>& packet, 337 const std::shared_ptr<Message> message, 338 size_t payloadLen) 339 { 340 auto session = std::get<session::Manager&>(singletonPool) 341 .getSession(message->bmcSessionID); 342 343 return session->getCryptAlgo()->decryptPayload( 344 packet, sizeof(SessionHeader_t), payloadLen); 345 } 346 347 std::vector<uint8_t> encryptPayload(std::shared_ptr<Message> message) 348 { 349 auto session = std::get<session::Manager&>(singletonPool) 350 .getSession(message->bmcSessionID); 351 352 return session->getCryptAlgo()->encryptPayload(message->payload); 353 } 354 355 } // namespace internal 356 357 } // namespace ipmi20parser 358 359 } // namespace message 360