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 <iostream> 9 #include <memory> 10 11 namespace message 12 { 13 14 namespace parser 15 { 16 17 std::tuple<std::unique_ptr<Message>, SessionHeader> 18 unflatten(std::vector<uint8_t>& inPacket) 19 { 20 // Check if the packet has atleast the size of the RMCP Header 21 if (inPacket.size() < sizeof(BasicHeader_t)) 22 { 23 throw std::runtime_error("RMCP Header missing"); 24 } 25 26 auto rmcpHeaderPtr = reinterpret_cast<BasicHeader_t*>(inPacket.data()); 27 28 // Verify if the fields in the RMCP header conforms to the specification 29 if ((rmcpHeaderPtr->version != RMCP_VERSION) || 30 (rmcpHeaderPtr->rmcpSeqNum != RMCP_SEQ) || 31 (rmcpHeaderPtr->classOfMsg != RMCP_MESSAGE_CLASS_IPMI)) 32 { 33 throw std::runtime_error("RMCP Header is invalid"); 34 } 35 36 // Read the Session Header and invoke the parser corresponding to the 37 // header type 38 switch (static_cast<SessionHeader>(rmcpHeaderPtr->format.formatType)) 39 { 40 case SessionHeader::IPMI15: 41 { 42 return std::make_tuple(ipmi15parser::unflatten(inPacket), 43 SessionHeader::IPMI15); 44 } 45 case SessionHeader::IPMI20: 46 { 47 return std::make_tuple(ipmi20parser::unflatten(inPacket), 48 SessionHeader::IPMI20); 49 } 50 default: 51 { 52 throw std::runtime_error("Invalid Session Header"); 53 } 54 } 55 } 56 57 std::vector<uint8_t> flatten(Message& outMessage, SessionHeader authType, 58 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::unique_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_unique<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(Message& outMessage, session::Session& session) 111 { 112 std::vector<uint8_t> packet(sizeof(SessionHeader_t)); 113 114 // Insert Session Header into the Packet 115 auto header = reinterpret_cast<SessionHeader_t*>(packet.data()); 116 header->base.version = parser::RMCP_VERSION; 117 header->base.reserved = 0x00; 118 header->base.rmcpSeqNum = parser::RMCP_SEQ; 119 header->base.classOfMsg = parser::RMCP_MESSAGE_CLASS_IPMI; 120 header->base.format.formatType = 121 static_cast<uint8_t>(parser::SessionHeader::IPMI15); 122 header->sessSeqNum = 0; 123 header->sessId = endian::to_ipmi(outMessage.rcSessionID); 124 125 header->payloadLength = static_cast<uint8_t>(outMessage.payload.size()); 126 127 // Insert the Payload into the Packet 128 packet.insert(packet.end(), outMessage.payload.begin(), 129 outMessage.payload.end()); 130 131 // Insert the Session Trailer 132 packet.resize(packet.size() + sizeof(SessionTrailer_t)); 133 auto trailer = 134 reinterpret_cast<SessionTrailer_t*>(packet.data() + packet.size()); 135 trailer->legacyPad = 0x00; 136 137 return packet; 138 } 139 140 } // namespace ipmi15parser 141 142 namespace ipmi20parser 143 { 144 145 std::unique_ptr<Message> unflatten(std::vector<uint8_t>& inPacket) 146 { 147 // Check if the packet has atleast the Session Header 148 if (inPacket.size() < sizeof(SessionHeader_t)) 149 { 150 throw std::runtime_error("IPMI2.0 Session Header Missing"); 151 } 152 153 auto message = std::make_unique<Message>(); 154 155 auto header = reinterpret_cast<SessionHeader_t*>(inPacket.data()); 156 157 message->payloadType = static_cast<PayloadType>(header->payloadType & 0x3F); 158 message->bmcSessionID = endian::from_ipmi(header->sessId); 159 message->sessionSeqNum = endian::from_ipmi(header->sessSeqNum); 160 message->isPacketEncrypted = 161 ((header->payloadType & PAYLOAD_ENCRYPT_MASK) ? true : false); 162 message->isPacketAuthenticated = 163 ((header->payloadType & PAYLOAD_AUTH_MASK) ? true : false); 164 165 auto payloadLen = endian::from_ipmi(header->payloadLength); 166 167 if (message->isPacketAuthenticated) 168 { 169 if (!(internal::verifyPacketIntegrity(inPacket, *(message.get()), 170 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.get()), 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(Message& outMessage, session::Session& session) 194 { 195 std::vector<uint8_t> packet(sizeof(SessionHeader_t)); 196 197 SessionHeader_t* header = reinterpret_cast<SessionHeader_t*>(packet.data()); 198 header->base.version = parser::RMCP_VERSION; 199 header->base.reserved = 0x00; 200 header->base.rmcpSeqNum = parser::RMCP_SEQ; 201 header->base.classOfMsg = parser::RMCP_MESSAGE_CLASS_IPMI; 202 header->base.format.formatType = 203 static_cast<uint8_t>(parser::SessionHeader::IPMI20); 204 header->payloadType = static_cast<uint8_t>(outMessage.payloadType); 205 header->sessId = endian::to_ipmi(outMessage.rcSessionID); 206 207 // Add session sequence number 208 internal::addSequenceNumber(packet, session); 209 210 size_t payloadLen = 0; 211 212 // Encrypt the payload if needed 213 if (outMessage.isPacketEncrypted) 214 { 215 header->payloadType |= PAYLOAD_ENCRYPT_MASK; 216 auto cipherPayload = internal::encryptPayload(outMessage); 217 payloadLen = cipherPayload.size(); 218 header->payloadLength = endian::to_ipmi<uint16_t>(cipherPayload.size()); 219 220 // Insert the encrypted payload into the outgoing IPMI packet 221 packet.insert(packet.end(), cipherPayload.begin(), cipherPayload.end()); 222 } 223 else 224 { 225 header->payloadLength = 226 endian::to_ipmi<uint16_t>(outMessage.payload.size()); 227 payloadLen = outMessage.payload.size(); 228 229 // Insert the Payload into the Packet 230 packet.insert(packet.end(), outMessage.payload.begin(), 231 outMessage.payload.end()); 232 } 233 234 if (outMessage.isPacketAuthenticated) 235 { 236 internal::addIntegrityData(packet, outMessage, payloadLen); 237 } 238 239 return packet; 240 } 241 242 namespace internal 243 { 244 245 void addSequenceNumber(std::vector<uint8_t>& packet, session::Session& session) 246 { 247 SessionHeader_t* header = reinterpret_cast<SessionHeader_t*>(packet.data()); 248 249 if (header->sessId == session::SESSION_ZERO) 250 { 251 header->sessSeqNum = 0x00; 252 } 253 else 254 { 255 auto seqNum = session.sequenceNums.increment(); 256 header->sessSeqNum = endian::to_ipmi(seqNum); 257 } 258 } 259 260 bool verifyPacketIntegrity(const std::vector<uint8_t>& packet, 261 const Message& message, size_t payloadLen) 262 { 263 /* 264 * Padding bytes are added to cause the number of bytes in the data range 265 * covered by the AuthCode(Integrity Data) field to be a multiple of 4 bytes 266 * .If present each integrity Pad byte is set to FFh. The following logic 267 * calculates the number of padding bytes added in the IPMI packet. 268 */ 269 auto paddingLen = 4 - ((payloadLen + 2) & 3); 270 271 auto sessTrailerPos = sizeof(SessionHeader_t) + payloadLen + paddingLen; 272 273 auto trailer = reinterpret_cast<const SessionTrailer_t*>(packet.data() + 274 sessTrailerPos); 275 276 // Check trailer->padLength against paddingLen, both should match up, 277 // return false if the lengths don't match 278 if (trailer->padLength != paddingLen) 279 { 280 return false; 281 } 282 283 auto session = (std::get<session::Manager&>(singletonPool) 284 .getSession(message.bmcSessionID)) 285 .lock(); 286 287 auto integrityAlgo = session->getIntegrityAlgo(); 288 289 // Check if Integrity data length is as expected, check integrity data 290 // length is same as the length expected for the Integrity Algorithm that 291 // was negotiated during the session open process. 292 if ((packet.size() - sessTrailerPos - sizeof(SessionTrailer_t)) != 293 integrityAlgo->authCodeLength) 294 { 295 return false; 296 } 297 298 auto integrityIter = packet.cbegin(); 299 std::advance(integrityIter, sessTrailerPos + sizeof(SessionTrailer_t)); 300 301 // The integrity data is calculated from the AuthType/Format field up to and 302 // including the field that immediately precedes the AuthCode field itself. 303 size_t length = packet.size() - integrityAlgo->authCodeLength - 304 message::parser::RMCP_SESSION_HEADER_SIZE; 305 306 return integrityAlgo->verifyIntegrityData(packet, length, integrityIter); 307 } 308 309 void addIntegrityData(std::vector<uint8_t>& packet, const Message& message, 310 size_t payloadLen) 311 { 312 // The following logic calculates the number of padding bytes to be added to 313 // IPMI packet. If needed each integrity Pad byte is set to FFh. 314 auto paddingLen = 4 - ((payloadLen + 2) & 3); 315 packet.insert(packet.end(), paddingLen, 0xFF); 316 317 packet.resize(packet.size() + sizeof(SessionTrailer_t)); 318 319 auto trailer = reinterpret_cast<SessionTrailer_t*>( 320 packet.data() + packet.size() - sizeof(SessionTrailer_t)); 321 322 trailer->padLength = paddingLen; 323 trailer->nextHeader = parser::RMCP_MESSAGE_CLASS_IPMI; 324 325 auto session = (std::get<session::Manager&>(singletonPool) 326 .getSession(message.bmcSessionID)) 327 .lock(); 328 329 auto integrityData = 330 session->getIntegrityAlgo()->generateIntegrityData(packet); 331 332 packet.insert(packet.end(), integrityData.begin(), integrityData.end()); 333 } 334 335 std::vector<uint8_t> decryptPayload(const std::vector<uint8_t>& packet, 336 const Message& message, size_t payloadLen) 337 { 338 auto session = (std::get<session::Manager&>(singletonPool) 339 .getSession(message.bmcSessionID)) 340 .lock(); 341 342 return session->getCryptAlgo()->decryptPayload( 343 packet, sizeof(SessionHeader_t), payloadLen); 344 } 345 346 std::vector<uint8_t> encryptPayload(Message& message) 347 { 348 auto session = (std::get<session::Manager&>(singletonPool) 349 .getSession(message.bmcSessionID)) 350 .lock(); 351 352 return session->getCryptAlgo()->encryptPayload(message.payload); 353 } 354 355 } // namespace internal 356 357 } // namespace ipmi20parser 358 359 } // namespace message 360