Coverage Report

Created: 2026-08-14 06:52

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