#define LOG_MODULE PacketLogModuleSSHLayer
#include "SSHLayer.h"
#include "GeneralUtils.h"
#include "Logger.h"
#include "EndianPortable.h"
#include <string.h>
namespace pcpp
{
#define SSH_LAYER_BASE_STRING "SSH Layer"
SSHLayer* SSHLayer::createSSHMessage(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet)
{
SSHIdentificationMessage* sshIdnetMsg = SSHIdentificationMessage::tryParse(data, dataLen, prevLayer, packet);
if (sshIdnetMsg != nullptr)
return sshIdnetMsg;
SSHHandshakeMessage* sshHandshakeMessage = SSHHandshakeMessage::tryParse(data, dataLen, prevLayer, packet);
if (sshHandshakeMessage != nullptr)
return sshHandshakeMessage;
return new SSHEncryptedMessage(data, dataLen, prevLayer, packet);
}
void SSHLayer::parseNextLayer()
{
size_t headerLen = getHeaderLen();
if (m_DataLen <= headerLen)
return;
m_NextLayer = SSHLayer::createSSHMessage(m_Data + headerLen, m_DataLen - headerLen, this, m_Packet);
}
SSHIdentificationMessage* SSHIdentificationMessage::tryParse(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet)
{
if (dataLen < 5)
return nullptr;
if (data[0] == 0x53 && data[1] == 0x53 && data[2] == 0x48 && data[3] == 0x2d && data[dataLen - 1] == 0x0a)
return new SSHIdentificationMessage(data, dataLen, prevLayer, packet);
return nullptr;
}
std::string SSHIdentificationMessage::getIdentificationMessage()
{
size_t identMsgEOL = (m_Data[m_DataLen - 2] == 0x0d ? 2 : 1);
return std::string(reinterpret_cast<const char*>(m_Data), m_DataLen - identMsgEOL);
}
std::string SSHIdentificationMessage::toString() const
{
return std::string(SSH_LAYER_BASE_STRING) + ", " + "Identification message";
}
SSHHandshakeMessage::SSHHandshakeMessageType SSHHandshakeMessage::getMessageType() const
{
uint8_t messageCode = getMsgBaseHeader()->messageCode;
if (messageCode == 20 || messageCode == 21 || (messageCode >= 30 && messageCode <= 34))
return static_cast<SSHHandshakeMessage::SSHHandshakeMessageType>(messageCode);
return SSHHandshakeMessage::SSH_MSG_UNKNOWN;
}
std::string SSHHandshakeMessage::getMessageTypeStr() const
{
switch (getMessageType())
{
case SSHHandshakeMessage::SSH_MSG_KEX_INIT:
return "Key Exchange Init";
case SSHHandshakeMessage::SSH_MSG_NEW_KEYS:
return "New Keys";
case SSHHandshakeMessage::SSH_MSG_KEX_DH_INIT:
return "Diffie-Hellman Key Exchange Init";
case SSHHandshakeMessage::SSH_MSG_KEX_DH_REPLY:
return "Diffie-Hellman Key Exchange Reply";
case SSHHandshakeMessage::SSH_MSG_KEX_DH_GEX_INIT:
return "Diffie-Hellman Group Exchange Init";
case SSHHandshakeMessage::SSH_MSG_KEX_DH_GEX_REPLY:
return "Diffie-Hellman Group Exchange Reply";
case SSHHandshakeMessage::SSH_MSG_KEX_DH_GEX_REQUEST:
return "Diffie-Hellman Group Exchange Request";
default:
return "Unknown";
}
}
uint8_t* SSHHandshakeMessage::getSSHHandshakeMessage() const
{
return m_Data + sizeof(SSHHandshakeMessage::ssh_message_base);
}
size_t SSHHandshakeMessage::getSSHHandshakeMessageLength() const
{
uint32_t length = be32toh(getMsgBaseHeader()->packetLength);
return static_cast<size_t>(length) - getMsgBaseHeader()->paddingLength - sizeof(uint8_t)*2;
}
size_t SSHHandshakeMessage::getPaddingLength() const
{
return getMsgBaseHeader()->paddingLength;
}
size_t SSHHandshakeMessage::getHeaderLen() const
{
return (size_t)be32toh(getMsgBaseHeader()->packetLength) + sizeof(uint32_t);
}
std::string SSHHandshakeMessage::toString() const
{
return std::string(SSH_LAYER_BASE_STRING) + ", " + "Handshake Message: " + getMessageTypeStr();
}
SSHHandshakeMessage* SSHHandshakeMessage::tryParse(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet)
{
if (dataLen < sizeof(SSHHandshakeMessage::ssh_message_base))
{
PCPP_LOG_DEBUG("Data length is smaller than the minimum size of an SSH handshake message. It's probably not an SSH handshake message");
return nullptr;
}
SSHHandshakeMessage::ssh_message_base* msgBase = (SSHHandshakeMessage::ssh_message_base*)data;
uint32_t msgLength = be32toh(msgBase->packetLength);
if (msgLength + sizeof(uint32_t) > dataLen)
{
PCPP_LOG_DEBUG("Message size is larger than layer size. It's probably not an SSH handshake message");
return nullptr;
}
if (msgBase->paddingLength > msgLength)
{
PCPP_LOG_DEBUG("Message padding is larger than message size. It's probably not an SSH handshake message");
return nullptr;
}
if (msgBase->messageCode != 20 &&
msgBase->messageCode != 21 &&
(msgBase->messageCode < 30 || msgBase->messageCode > 49))
{
PCPP_LOG_DEBUG("Unknown message type " << (int)msgBase->messageCode << ". It's probably not an SSH handshake message");
return nullptr;
}
switch (msgBase->messageCode)
{
case SSHHandshakeMessage::SSH_MSG_KEX_INIT:
return new SSHKeyExchangeInitMessage(data, dataLen, prevLayer, packet);
default:
return new SSHHandshakeMessage(data, dataLen, prevLayer, packet);
}
}
SSHKeyExchangeInitMessage::SSHKeyExchangeInitMessage(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet) : SSHHandshakeMessage(data, dataLen, prevLayer, packet), m_OffsetsInitialized(false)
{
memset(m_FieldOffsets, 0, 11*sizeof(size_t));
}
void SSHKeyExchangeInitMessage::parseMessageAndInitOffsets()
{
m_OffsetsInitialized = true;
if (m_DataLen <= sizeof(ssh_message_base) + 16)
return;
size_t offset = sizeof(ssh_message_base) + 16;
for (int i = 0; i < 10; i++)
{
if (offset + sizeof(uint32_t) >= m_DataLen)
return;
size_t fieldLength = static_cast<size_t>(be32toh(*(uint32_t*)(m_Data + offset)));
if (offset + sizeof(uint32_t) + fieldLength > m_DataLen)
return;
PCPP_LOG_DEBUG("Field offset [" << i << "] = " << offset << ", length = " << fieldLength);
m_FieldOffsets[i] = offset;
offset += sizeof(uint32_t) + fieldLength;
}
if (offset >= m_DataLen)
return;
m_FieldOffsets[10] = offset;
}
std::string SSHKeyExchangeInitMessage::getFieldValue(int fieldOffsetIndex)
{
if (!m_OffsetsInitialized)
parseMessageAndInitOffsets();
if (m_FieldOffsets[fieldOffsetIndex] == 0)
return "";
size_t fieldOffset = m_FieldOffsets[fieldOffsetIndex];
uint32_t fieldLength = be32toh(*(uint32_t*)(m_Data + fieldOffset));
return std::string(reinterpret_cast<const char*>(m_Data + fieldOffset + sizeof(uint32_t)), (size_t)fieldLength);
}
uint8_t* SSHKeyExchangeInitMessage::getCookie()
{
if (m_DataLen < sizeof(ssh_message_base) + 16)
return nullptr;
return m_Data + sizeof(ssh_message_base);
}
std::string SSHKeyExchangeInitMessage::getCookieAsHexStream()
{
uint8_t* cookie = getCookie();
if (cookie == nullptr)
return "";
return byteArrayToHexString(cookie, 16);
}
bool SSHKeyExchangeInitMessage::isFirstKexPacketFollows()
{
if (!m_OffsetsInitialized)
parseMessageAndInitOffsets();
if (m_FieldOffsets[10] == 0)
return false;
return m_Data[m_FieldOffsets[10]] != 0;
}
std::string SSHEncryptedMessage::toString() const
{
return std::string(SSH_LAYER_BASE_STRING) + ", " + "Encrypted Message";
}
}