Coverage Report

Created: 2026-05-30 07:22

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/PcapPlusPlus/Packet++/src/SSHLayer.cpp
Line
Count
Source
1
7.89k
#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
6.84k
#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
18.3k
  {
20
18.3k
    SSHIdentificationMessage* sshIdnetMsg = SSHIdentificationMessage::tryParse(data, dataLen, prevLayer, packet);
21
18.3k
    if (sshIdnetMsg != nullptr)
22
2.17k
      return sshIdnetMsg;
23
24
16.2k
    SSHHandshakeMessage* sshHandshakeMessage = SSHHandshakeMessage::tryParse(data, dataLen, prevLayer, packet);
25
16.2k
    if (sshHandshakeMessage != nullptr)
26
8.31k
      return sshHandshakeMessage;
27
28
7.89k
    return new SSHEncryptedMessage(data, dataLen, prevLayer, packet);
29
16.2k
  }
30
31
  void SSHLayer::parseNextLayer()
32
18.3k
  {
33
18.3k
    size_t headerLen = getHeaderLen();
34
18.3k
    if (m_DataLen <= headerLen)
35
16.2k
      return;
36
37
2.17k
    constructNextLayerFromFactory(SSHLayer::createSSHMessage, m_Data + headerLen, m_DataLen - headerLen);
38
2.17k
  }
39
40
  // --------------------------------
41
  // SSHIdentificationMessage methods
42
  // --------------------------------
43
44
  SSHIdentificationMessage* SSHIdentificationMessage::tryParse(uint8_t* data, size_t dataLen, Layer* prevLayer,
45
                                                               Packet* packet)
46
18.3k
  {
47
    // Payload must be at least as long as the string "SSH-"
48
18.3k
    if (dataLen < 5)
49
2.01k
      return nullptr;
50
51
    // Payload must begin with "SSH-" and end with "\n"
52
16.3k
    if (data[0] == 0x53 && data[1] == 0x53 && data[2] == 0x48 && data[3] == 0x2d && data[dataLen - 1] == 0x0a)
53
2.17k
      return new SSHIdentificationMessage(data, dataLen, prevLayer, packet);
54
55
14.1k
    return nullptr;
56
16.3k
  }
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
870
  {
67
870
    return std::string(SSH_LAYER_BASE_STRING) + ", " + "Identification message";
68
870
  }
69
70
  // ---------------------------
71
  // SSHHandshakeMessage methods
72
  // ---------------------------
73
74
  SSHHandshakeMessage::SSHHandshakeMessageType SSHHandshakeMessage::getMessageType() const
75
3.04k
  {
76
3.04k
    uint8_t messageCode = getMsgBaseHeader()->messageCode;
77
3.04k
    if (messageCode == 20 || messageCode == 21 || (messageCode >= 30 && messageCode <= 34))
78
3.01k
      return static_cast<SSHHandshakeMessage::SSHHandshakeMessageType>(messageCode);
79
24
    return SSHHandshakeMessage::SSH_MSG_UNKNOWN;
80
3.04k
  }
81
82
  std::string SSHHandshakeMessage::getMessageTypeStr() const
83
3.04k
  {
84
3.04k
    switch (getMessageType())
85
3.04k
    {
86
88
    case SSHHandshakeMessage::SSH_MSG_KEX_INIT:
87
88
      return "Key Exchange Init";
88
1.31k
    case SSHHandshakeMessage::SSH_MSG_NEW_KEYS:
89
1.31k
      return "New Keys";
90
2
    case SSHHandshakeMessage::SSH_MSG_KEX_DH_INIT:
91
2
      return "Diffie-Hellman Key Exchange Init";
92
40
    case SSHHandshakeMessage::SSH_MSG_KEX_DH_REPLY:
93
40
      return "Diffie-Hellman Key Exchange Reply";
94
810
    case SSHHandshakeMessage::SSH_MSG_KEX_DH_GEX_INIT:
95
810
      return "Diffie-Hellman Group Exchange Init";
96
688
    case SSHHandshakeMessage::SSH_MSG_KEX_DH_GEX_REPLY:
97
688
      return "Diffie-Hellman Group Exchange Reply";
98
78
    case SSHHandshakeMessage::SSH_MSG_KEX_DH_GEX_REQUEST:
99
78
      return "Diffie-Hellman Group Exchange Request";
100
24
    default:
101
24
      return "Unknown";
102
3.04k
    }
103
3.04k
  }
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
9.83k
  {
123
9.83k
    return (size_t)be32toh(getMsgBaseHeader()->packetLength) + sizeof(uint32_t);
124
9.83k
  }
125
126
  std::string SSHHandshakeMessage::toString() const
127
3.04k
  {
128
3.04k
    return std::string(SSH_LAYER_BASE_STRING) + ", " + "Handshake Message: " + getMessageTypeStr();
129
3.04k
  }
130
131
  SSHHandshakeMessage* SSHHandshakeMessage::tryParse(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet)
132
16.2k
  {
133
16.2k
    if (dataLen < sizeof(SSHHandshakeMessage::ssh_message_base))
134
2.01k
    {
135
2.01k
      PCPP_LOG_DEBUG(
136
2.01k
          "Data length is smaller than the minimum size of an SSH handshake message. It's probably not an SSH handshake message");
137
2.01k
      return nullptr;
138
2.01k
    }
139
140
14.1k
    SSHHandshakeMessage::ssh_message_base* msgBase = (SSHHandshakeMessage::ssh_message_base*)data;
141
142
14.1k
    uint32_t msgLength = be32toh(msgBase->packetLength);
143
14.1k
    if (msgLength + sizeof(uint32_t) > dataLen)
144
5.50k
    {
145
5.50k
      PCPP_LOG_DEBUG("Message size is larger than layer size. It's probably not an SSH handshake message");
146
5.50k
      return nullptr;
147
5.50k
    }
148
149
8.68k
    if (msgBase->paddingLength > msgLength)
150
108
    {
151
108
      PCPP_LOG_DEBUG("Message padding is larger than message size. It's probably not an SSH handshake message");
152
108
      return nullptr;
153
108
    }
154
155
8.57k
    if (msgBase->messageCode != 20 && msgBase->messageCode != 21 &&
156
4.72k
        (msgBase->messageCode < 30 || msgBase->messageCode > 49))
157
261
    {
158
261
      PCPP_LOG_DEBUG("Unknown message type " << (int)msgBase->messageCode
159
261
                                             << ". It's probably not an SSH handshake message");
160
261
      return nullptr;
161
261
    }
162
163
8.31k
    switch (msgBase->messageCode)
164
8.31k
    {
165
220
    case SSHHandshakeMessage::SSH_MSG_KEX_INIT:
166
220
      return new SSHKeyExchangeInitMessage(data, dataLen, prevLayer, packet);
167
8.09k
    default:
168
8.09k
      return new SSHHandshakeMessage(data, dataLen, prevLayer, packet);
169
8.31k
    }
170
8.31k
  }
171
172
  // ---------------------------------
173
  // SSHKeyExchangeInitMessage methods
174
  // ---------------------------------
175
176
  SSHKeyExchangeInitMessage::SSHKeyExchangeInitMessage(uint8_t* data, size_t dataLen, Layer* prevLayer,
177
                                                       Packet* packet)
178
220
      : SSHHandshakeMessage(data, dataLen, prevLayer, packet), m_OffsetsInitialized(false)
179
220
  {
180
220
    memset(m_FieldOffsets, 0, 11 * sizeof(size_t));
181
220
  }
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
2.93k
  {
257
2.93k
    return std::string(SSH_LAYER_BASE_STRING) + ", " + "Encrypted Message";
258
2.93k
  }
259
260
}  // namespace pcpp