Coverage Report

Created: 2025-07-18 07:14

/src/PcapPlusPlus/Packet++/src/SSHLayer.cpp
Line
Count
Source (jump to first uncovered line)
1
5.28k
#define LOG_MODULE PacketLogModuleSSHLayer
2
3
#include "SSHLayer.h"
4
#include "GeneralUtils.h"
5
#include "Logger.h"
6
#include "EndianPortable.h"
7
#include <cstring>
8
9
namespace pcpp
10
{
11
12
2.63k
#define SSH_LAYER_BASE_STRING "SSH Layer"
13
14
  // ----------------
15
  // SSHLayer methods
16
  // ----------------
17
18
  SSHLayer* SSHLayer::createSSHMessage(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet)
19
7.15k
  {
20
7.15k
    SSHIdentificationMessage* sshIdnetMsg = SSHIdentificationMessage::tryParse(data, dataLen, prevLayer, packet);
21
7.15k
    if (sshIdnetMsg != nullptr)
22
485
      return sshIdnetMsg;
23
24
6.66k
    SSHHandshakeMessage* sshHandshakeMessage = SSHHandshakeMessage::tryParse(data, dataLen, prevLayer, packet);
25
6.66k
    if (sshHandshakeMessage != nullptr)
26
1.37k
      return sshHandshakeMessage;
27
28
5.28k
    return new SSHEncryptedMessage(data, dataLen, prevLayer, packet);
29
6.66k
  }
30
31
  void SSHLayer::parseNextLayer()
32
7.15k
  {
33
7.15k
    size_t headerLen = getHeaderLen();
34
7.15k
    if (m_DataLen <= headerLen)
35
6.86k
      return;
36
288
    m_NextLayer = SSHLayer::createSSHMessage(m_Data + headerLen, m_DataLen - headerLen, this, m_Packet);
37
288
  }
38
39
  // --------------------------------
40
  // SSHIdentificationMessage methods
41
  // --------------------------------
42
43
  SSHIdentificationMessage* SSHIdentificationMessage::tryParse(uint8_t* data, size_t dataLen, Layer* prevLayer,
44
                                                               Packet* packet)
45
7.15k
  {
46
    // Payload must be at least as long as the string "SSH-"
47
7.15k
    if (dataLen < 5)
48
1.77k
      return nullptr;
49
50
    // Payload must begin with "SSH-" and end with "\n"
51
5.38k
    if (data[0] == 0x53 && data[1] == 0x53 && data[2] == 0x48 && data[3] == 0x2d && data[dataLen - 1] == 0x0a)
52
485
      return new SSHIdentificationMessage(data, dataLen, prevLayer, packet);
53
54
4.89k
    return nullptr;
55
5.38k
  }
56
57
  std::string SSHIdentificationMessage::getIdentificationMessage()
58
0
  {
59
    // check if message ends with "\r\n" or just with "\n"
60
0
    size_t identMsgEOL = (m_Data[m_DataLen - 2] == 0x0d ? 2 : 1);
61
0
    return std::string(reinterpret_cast<const char*>(m_Data), m_DataLen - identMsgEOL);
62
0
  }
63
64
  std::string SSHIdentificationMessage::toString() const
65
194
  {
66
194
    return std::string(SSH_LAYER_BASE_STRING) + ", " + "Identification message";
67
194
  }
68
69
  // ---------------------------
70
  // SSHHandshakeMessage methods
71
  // ---------------------------
72
73
  SSHHandshakeMessage::SSHHandshakeMessageType SSHHandshakeMessage::getMessageType() const
74
514
  {
75
514
    uint8_t messageCode = getMsgBaseHeader()->messageCode;
76
514
    if (messageCode == 20 || messageCode == 21 || (messageCode >= 30 && messageCode <= 34))
77
514
      return static_cast<SSHHandshakeMessage::SSHHandshakeMessageType>(messageCode);
78
0
    return SSHHandshakeMessage::SSH_MSG_UNKNOWN;
79
514
  }
80
81
  std::string SSHHandshakeMessage::getMessageTypeStr() const
82
514
  {
83
514
    switch (getMessageType())
84
514
    {
85
94
    case SSHHandshakeMessage::SSH_MSG_KEX_INIT:
86
94
      return "Key Exchange Init";
87
156
    case SSHHandshakeMessage::SSH_MSG_NEW_KEYS:
88
156
      return "New Keys";
89
0
    case SSHHandshakeMessage::SSH_MSG_KEX_DH_INIT:
90
0
      return "Diffie-Hellman Key Exchange Init";
91
30
    case SSHHandshakeMessage::SSH_MSG_KEX_DH_REPLY:
92
30
      return "Diffie-Hellman Key Exchange Reply";
93
104
    case SSHHandshakeMessage::SSH_MSG_KEX_DH_GEX_INIT:
94
104
      return "Diffie-Hellman Group Exchange Init";
95
96
    case SSHHandshakeMessage::SSH_MSG_KEX_DH_GEX_REPLY:
96
96
      return "Diffie-Hellman Group Exchange Reply";
97
34
    case SSHHandshakeMessage::SSH_MSG_KEX_DH_GEX_REQUEST:
98
34
      return "Diffie-Hellman Group Exchange Request";
99
0
    default:
100
0
      return "Unknown";
101
514
    }
102
514
  }
103
104
  uint8_t* SSHHandshakeMessage::getSSHHandshakeMessage() const
105
0
  {
106
0
    return m_Data + sizeof(SSHHandshakeMessage::ssh_message_base);
107
0
  }
108
109
  size_t SSHHandshakeMessage::getSSHHandshakeMessageLength() const
110
0
  {
111
0
    uint32_t length = be32toh(getMsgBaseHeader()->packetLength);
112
0
    return static_cast<size_t>(length) - getMsgBaseHeader()->paddingLength - sizeof(uint8_t) * 2;
113
0
  }
114
115
  size_t SSHHandshakeMessage::getPaddingLength() const
116
0
  {
117
0
    return getMsgBaseHeader()->paddingLength;
118
0
  }
119
120
  size_t SSHHandshakeMessage::getHeaderLen() const
121
1.63k
  {
122
1.63k
    return (size_t)be32toh(getMsgBaseHeader()->packetLength) + sizeof(uint32_t);
123
1.63k
  }
124
125
  std::string SSHHandshakeMessage::toString() const
126
514
  {
127
514
    return std::string(SSH_LAYER_BASE_STRING) + ", " + "Handshake Message: " + getMessageTypeStr();
128
514
  }
129
130
  SSHHandshakeMessage* SSHHandshakeMessage::tryParse(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet)
131
6.66k
  {
132
6.66k
    if (dataLen < sizeof(SSHHandshakeMessage::ssh_message_base))
133
1.77k
    {
134
1.77k
      PCPP_LOG_DEBUG(
135
1.77k
          "Data length is smaller than the minimum size of an SSH handshake message. It's probably not an SSH handshake message");
136
1.77k
      return nullptr;
137
1.77k
    }
138
139
4.89k
    SSHHandshakeMessage::ssh_message_base* msgBase = (SSHHandshakeMessage::ssh_message_base*)data;
140
141
4.89k
    uint32_t msgLength = be32toh(msgBase->packetLength);
142
4.89k
    if (msgLength + sizeof(uint32_t) > dataLen)
143
3.36k
    {
144
3.36k
      PCPP_LOG_DEBUG("Message size is larger than layer size. It's probably not an SSH handshake message");
145
3.36k
      return nullptr;
146
3.36k
    }
147
148
1.52k
    if (msgBase->paddingLength > msgLength)
149
66
    {
150
66
      PCPP_LOG_DEBUG("Message padding is larger than message size. It's probably not an SSH handshake message");
151
66
      return nullptr;
152
66
    }
153
154
1.46k
    if (msgBase->messageCode != 20 && msgBase->messageCode != 21 &&
155
1.46k
        (msgBase->messageCode < 30 || msgBase->messageCode > 49))
156
84
    {
157
84
      PCPP_LOG_DEBUG("Unknown message type " << (int)msgBase->messageCode
158
84
                                             << ". It's probably not an SSH handshake message");
159
84
      return nullptr;
160
84
    }
161
162
1.37k
    switch (msgBase->messageCode)
163
1.37k
    {
164
235
    case SSHHandshakeMessage::SSH_MSG_KEX_INIT:
165
235
      return new SSHKeyExchangeInitMessage(data, dataLen, prevLayer, packet);
166
1.14k
    default:
167
1.14k
      return new SSHHandshakeMessage(data, dataLen, prevLayer, packet);
168
1.37k
    }
169
1.37k
  }
170
171
  // ---------------------------------
172
  // SSHKeyExchangeInitMessage methods
173
  // ---------------------------------
174
175
  SSHKeyExchangeInitMessage::SSHKeyExchangeInitMessage(uint8_t* data, size_t dataLen, Layer* prevLayer,
176
                                                       Packet* packet)
177
235
      : SSHHandshakeMessage(data, dataLen, prevLayer, packet), m_OffsetsInitialized(false)
178
235
  {
179
235
    memset(m_FieldOffsets, 0, 11 * sizeof(size_t));
180
235
  }
181
182
  void SSHKeyExchangeInitMessage::parseMessageAndInitOffsets()
183
0
  {
184
0
    m_OffsetsInitialized = true;
185
0
    if (m_DataLen <= sizeof(ssh_message_base) + 16)
186
0
      return;
187
188
0
    size_t offset = sizeof(ssh_message_base) + 16;
189
0
    for (int i = 0; i < 10; i++)
190
0
    {
191
0
      if (offset + sizeof(uint32_t) >= m_DataLen)
192
0
        return;
193
194
0
      size_t fieldLength = static_cast<size_t>(be32toh(*(uint32_t*)(m_Data + offset)));
195
0
      if (offset + sizeof(uint32_t) + fieldLength > m_DataLen)
196
0
        return;
197
198
0
      PCPP_LOG_DEBUG("Field offset [" << i << "] = " << offset << ", length = " << fieldLength);
199
0
      m_FieldOffsets[i] = offset;
200
0
      offset += sizeof(uint32_t) + fieldLength;
201
0
    }
202
203
0
    if (offset >= m_DataLen)
204
0
      return;
205
206
0
    m_FieldOffsets[10] = offset;
207
0
  }
208
209
  std::string SSHKeyExchangeInitMessage::getFieldValue(int fieldOffsetIndex)
210
0
  {
211
0
    if (!m_OffsetsInitialized)
212
0
      parseMessageAndInitOffsets();
213
214
0
    if (m_FieldOffsets[fieldOffsetIndex] == 0)
215
0
      return "";
216
217
0
    size_t fieldOffset = m_FieldOffsets[fieldOffsetIndex];
218
0
    uint32_t fieldLength = be32toh(*(uint32_t*)(m_Data + fieldOffset));
219
0
    return std::string(reinterpret_cast<const char*>(m_Data + fieldOffset + sizeof(uint32_t)), (size_t)fieldLength);
220
0
  }
221
222
  uint8_t* SSHKeyExchangeInitMessage::getCookie()
223
0
  {
224
0
    if (m_DataLen < sizeof(ssh_message_base) + 16)
225
0
      return nullptr;
226
227
0
    return m_Data + sizeof(ssh_message_base);
228
0
  }
229
230
  std::string SSHKeyExchangeInitMessage::getCookieAsHexStream()
231
0
  {
232
0
    uint8_t* cookie = getCookie();
233
0
    if (cookie == nullptr)
234
0
      return "";
235
236
0
    return byteArrayToHexString(cookie, 16);
237
0
  }
238
239
  bool SSHKeyExchangeInitMessage::isFirstKexPacketFollows()
240
0
  {
241
0
    if (!m_OffsetsInitialized)
242
0
      parseMessageAndInitOffsets();
243
244
0
    if (m_FieldOffsets[10] == 0)
245
0
      return false;
246
247
0
    return m_Data[m_FieldOffsets[10]] != 0;
248
0
  }
249
250
  // ---------------------------
251
  // SSHEncryptedMessage methods
252
  // ---------------------------
253
254
  std::string SSHEncryptedMessage::toString() const
255
1.92k
  {
256
1.92k
    return std::string(SSH_LAYER_BASE_STRING) + ", " + "Encrypted Message";
257
1.92k
  }
258
259
}  // namespace pcpp