Coverage Report

Created: 2026-10-17 07:09

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