suricata
app-layer-ssh.c
Go to the documentation of this file.
1 /* Copyright (C) 2007-2014 Open Information Security Foundation
2  *
3  * You can copy, redistribute or modify this Program under the terms of
4  * the GNU General Public License version 2 as published by the Free
5  * Software Foundation.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * version 2 along with this program; if not, write to the Free Software
14  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
15  * 02110-1301, USA.
16  */
17 
18 /**
19  * \file
20  *
21  * \author Pablo Rincon <pablo.rincon.crespo@gmail.com>
22  * \author Victor Julien <victor@inliniac.net>
23  *
24  * App-layer parser for SSH protocol
25  *
26  */
27 
28 #include "suricata-common.h"
29 #include "suricata.h"
30 #include "decode.h"
31 
32 #include "app-layer-detect-proto.h"
33 #include "app-layer-protos.h"
34 #include "app-layer-parser.h"
35 #include "app-layer-ssh.h"
36 #include "rust.h"
37 
38 #include "conf.h"
39 
40 #include "util-debug.h"
41 
42 #include "util-memcmp.h"
43 #include "rust-bindings.h"
44 #include "suricata.h"
45 
46 /* HASSH fingerprints are disabled by default */
47 #define SSH_CONFIG_DEFAULT_HASSH false
48 /* Bypassing the encrypted part of the connections */
49 #define SSH_CONFIG_DEFAULT_ENCRYPTION_BYPASS ENCRYPTION_HANDLING_TRACK_ONLY
50 
51 static int SSHRegisterPatternsForProtocolDetection(void)
52 {
54  IPPROTO_TCP, ALPROTO_SSH, "SSH-", 4, 0, STREAM_TOSERVER) < 0) {
55  return -1;
56  }
58  IPPROTO_TCP, ALPROTO_SSH, "SSH-", 4, 0, STREAM_TOCLIENT) < 0) {
59  return -1;
60  }
61  return 0;
62 }
63 
64 bool SSHTxLogCondition(ThreadVars *tv, const Packet *p, void *state, void *tx, uint64_t tx_id)
65 {
66  /* A pending success record must be emitted when an IPS drop or a
67  * bypass ends the flow: the flow-end flush that would log it is
68  * skipped for those flows, and the app layer is disabled or freed
69  * right after, so no further update reaches this condition. An IDS
70  * drop keeps parsing, so the condition stays failure-only there: a
71  * later failure still wins, and the success record is emitted by
72  * the flow-end flush. */
73  if (p->flow != NULL) {
74  if ((p->flow->flags & FLOW_ACTION_DROP) != 0 && EngineModeIsIPS()) {
75  return true;
76  }
77  if (FlowIsBypassed(p->flow)) {
78  return true;
79  }
80  }
81  return SCSshTxGetLogCondition(tx);
82 }
83 
84 /** \brief Function to register the SSH protocol parsers and other functions
85  */
87 {
88  const char *proto_name = "ssh";
89 
90  if (SCAppLayerProtoDetectConfProtoDetectionEnabled("tcp", proto_name)) {
92  if (SSHRegisterPatternsForProtocolDetection() < 0)
93  return;
94 
95  /* Check if we should generate Hassh fingerprints */
96  int enable_hassh = SSH_CONFIG_DEFAULT_HASSH;
97  const char *strval = NULL;
98  if (SCConfGetNonNull("app-layer.protocols.ssh.hassh", &strval) != 1) {
99  enable_hassh = SSH_CONFIG_DEFAULT_HASSH;
100  } else if (strcmp(strval, "auto") == 0) {
101  enable_hassh = SSH_CONFIG_DEFAULT_HASSH;
102  } else if (SCConfValIsFalse(strval)) {
103  enable_hassh = SSH_CONFIG_DEFAULT_HASSH;
104  SCSshDisableHassh();
105  } else if (SCConfValIsTrue(strval)) {
106  enable_hassh = true;
107  }
108 
109  if (RunmodeIsUnittests() || enable_hassh) {
110  SCSshEnableHassh();
111  }
112 
113  EncryptionHandling encryption_bypass = SSH_CONFIG_DEFAULT_ENCRYPTION_BYPASS;
114  SCConfNode *encryption_node = SCConfGetNode("app-layer.protocols.ssh.encryption-handling");
115  if (encryption_node != NULL && encryption_node->val != NULL) {
116  if (strcmp(encryption_node->val, "full") == 0) {
117  encryption_bypass = ENCRYPTION_HANDLING_FULL;
118  } else if (strcmp(encryption_node->val, "track-only") == 0) {
119  encryption_bypass = ENCRYPTION_HANDLING_TRACK_ONLY;
120  } else if (strcmp(encryption_node->val, "bypass") == 0) {
121  encryption_bypass = ENCRYPTION_HANDLING_BYPASS;
122  } else {
123  encryption_bypass = SSH_CONFIG_DEFAULT_ENCRYPTION_BYPASS;
124  }
125  }
126 
127  if (encryption_bypass) {
128  SCLogConfig("ssh: bypass on the start of encryption enabled");
129  SCSshEnableBypass(encryption_bypass);
130  }
131  }
132 
133  SCLogDebug("Registering Rust SSH parser.");
134  SCRegisterSshParser();
135 
136 #ifdef UNITTESTS
138 #endif
139 }
140 
141 /* UNITTESTS */
142 #ifdef UNITTESTS
143 #include "flow-util.h"
144 #include "stream-tcp-util.h"
145 #include "util-unittest-helper.h"
146 #include "stream-tcp-private.h"
147 #include "stream-tcp-reassemble.h"
148 #include "stream-tcp.h"
149 
150 static int SSHParserTestUtilCheck(const char *protoexp, const char *softexp, void *tx, uint8_t flags) {
151  const uint8_t *protocol = NULL;
152  uint32_t p_len = 0;
153  const uint8_t *software = NULL;
154  uint32_t s_len = 0;
155 
156  if (SCSshTxGetProtocol(tx, flags, &protocol, &p_len) != 1) {
157  printf("Version string not parsed correctly return: ");
158  return 1;
159  }
160  if (protocol == NULL) {
161  printf("Version string not parsed correctly NULL: ");
162  return 1;
163  }
164 
165  if (p_len != strlen(protoexp)) {
166  printf("Version string not parsed correctly length: ");
167  return 1;
168  }
169  if (memcmp(protocol, protoexp, strlen(protoexp)) != 0) {
170  printf("Version string not parsed correctly: ");
171  return 1;
172  }
173 
174  if (softexp != NULL) {
175  if (SCSshTxGetSoftware(tx, flags, &software, &s_len) != 1)
176  return 1;
177  if (software == NULL)
178  return 1;
179  if (s_len != strlen(softexp)) {
180  printf("Software string not parsed correctly length: ");
181  return 1;
182  }
183  if (memcmp(software, softexp, strlen(softexp)) != 0) {
184  printf("Software string not parsed correctly: ");
185  return 1;
186  }
187  }
188  return 0;
189 }
190 
191 /** \test Send a version string in one chunk (client version str). */
192 static int SSHParserTest01(void)
193 {
194  Flow f;
195  uint8_t sshbuf[] = "SSH-2.0-MySSHClient-0.5.1\n";
196  uint32_t sshlen = sizeof(sshbuf) - 1;
197  TcpSession ssn;
199 
200  memset(&f, 0, sizeof(f));
201  memset(&ssn, 0, sizeof(ssn));
202  FLOW_INITIALIZE(&f);
203  f.protoctx = (void *)&ssn;
204  f.proto = IPPROTO_TCP;
206 
207  StreamTcpInitConfig(true);
208 
209  int r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SSH,
210  STREAM_TOSERVER | STREAM_EOF, sshbuf, sshlen);
211  FAIL_IF_NOT(r == 0);
212 
213  void *ssh_state = f.alstate;
214  FAIL_IF_NULL(ssh_state);
215 
216  void *tx = SCSshStateGetTx(ssh_state, 0);
217  FAIL_IF_NULL(tx);
218  FAIL_IF(SCSshTxGetAlStateProgress(tx, STREAM_TOSERVER) != SshStateKex);
219  FAIL_IF(SSHParserTestUtilCheck("2.0", "MySSHClient-0.5.1", tx, STREAM_TOSERVER));
220 
221  FLOW_DESTROY(&f);
223  StreamTcpFreeConfig(true);
224  PASS;
225 }
226 
227 /** \test Send a version string in one chunk but multiple lines and comments.
228  * (client version str)
229  */
230 static int SSHParserTest02(void)
231 {
232  int result = 0;
233  Flow f;
234  uint8_t sshbuf[] = "SSH-2.0-MySSHClient-0.5.1 some comments...\n";
235  uint32_t sshlen = sizeof(sshbuf) - 1;
236  TcpSession ssn;
238 
239  memset(&f, 0, sizeof(f));
240  memset(&ssn, 0, sizeof(ssn));
241  FLOW_INITIALIZE(&f);
242  f.protoctx = (void *)&ssn;
243  f.proto = IPPROTO_TCP;
245 
246  StreamTcpInitConfig(true);
247 
248  int r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SSH,
249  STREAM_TOSERVER | STREAM_EOF, sshbuf, sshlen);
250  if (r != 0) {
251  printf("toclient chunk 1 returned %" PRId32 ", expected 0: ", r);
252  goto end;
253  }
254 
255  void *ssh_state = f.alstate;
256  if (ssh_state == NULL) {
257  printf("no ssh state: ");
258  goto end;
259  }
260  void *tx = SCSshStateGetTx(ssh_state, 0);
261 
262  if (SCSshTxGetAlStateProgress(tx, STREAM_TOSERVER) != SshStateKex) {
263  printf("Client version string not parsed: ");
264  goto end;
265  }
266  if (SSHParserTestUtilCheck("2.0", "MySSHClient-0.5.1", tx, STREAM_TOSERVER))
267  goto end;
268 
269  result = 1;
270 end:
271  FLOW_DESTROY(&f);
272  if (alp_tctx != NULL)
274  StreamTcpFreeConfig(true);
275  return result;
276 }
277 
278 /** \test Send a invalid version string in one chunk but multiple lines and comments.
279  * (client version str)
280  */
281 static int SSHParserTest03(void)
282 {
283  int result = 0;
284  Flow f;
285  uint8_t sshbuf[] = "SSH-2.0 some comments...\n";
286  uint32_t sshlen = sizeof(sshbuf) - 1;
287  TcpSession ssn;
289 
290  memset(&f, 0, sizeof(f));
291  memset(&ssn, 0, sizeof(ssn));
292  FLOW_INITIALIZE(&f);
293  f.protoctx = (void *)&ssn;
294  f.proto = IPPROTO_TCP;
296 
297  StreamTcpInitConfig(true);
298 
299  int r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SSH,
300  STREAM_TOSERVER | STREAM_EOF, sshbuf, sshlen);
301  if (r == 0) {
302  printf("toclient chunk 1 returned %" PRId32 ", expected != 0: ", r);
303  goto end;
304  }
305 
306  void *ssh_state = f.alstate;
307  if (ssh_state == NULL) {
308  printf("no ssh state: ");
309  goto end;
310  }
311  void *tx = SCSshStateGetTx(ssh_state, 0);
312 
313  if (SCSshTxGetAlStateProgress(tx, STREAM_TOSERVER) == SshStateKex) {
314  printf("Client version string parsed? It's not a valid string: ");
315  goto end;
316  }
317  const uint8_t *dummy = NULL;
318  uint32_t dummy_len = 0;
319  if (SCSshTxGetProtocol(tx, STREAM_TOSERVER, &dummy, &dummy_len) != 0)
320  goto end;
321  if (SCSshTxGetSoftware(tx, STREAM_TOSERVER, &dummy, &dummy_len) != 0)
322  goto end;
323 
324  result = 1;
325 end:
326  FLOW_DESTROY(&f);
327  if (alp_tctx != NULL)
329  StreamTcpFreeConfig(true);
330  return result;
331 }
332 
333 /** \test Send a version string in one chunk (server version str). */
334 static int SSHParserTest04(void)
335 {
336  int result = 0;
337  Flow f;
338  uint8_t sshbuf[] = "SSH-2.0-MySSHClient-0.5.1\n";
339  uint32_t sshlen = sizeof(sshbuf) - 1;
340  TcpSession ssn;
342 
343  memset(&f, 0, sizeof(f));
344  memset(&ssn, 0, sizeof(ssn));
345  FLOW_INITIALIZE(&f);
346  f.protoctx = (void *)&ssn;
347  f.proto = IPPROTO_TCP;
349 
350  StreamTcpInitConfig(true);
351 
352  int r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SSH,
353  STREAM_TOCLIENT | STREAM_EOF, sshbuf, sshlen);
354  if (r != 0) {
355  printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
356  goto end;
357  }
358 
359  void *ssh_state = f.alstate;
360  if (ssh_state == NULL) {
361  printf("no ssh state: ");
362  goto end;
363  }
364  void *tx = SCSshStateGetTx(ssh_state, 0);
365 
366  if (SCSshTxGetAlStateProgress(tx, STREAM_TOCLIENT) != SshStateKex) {
367  printf("Client version string not parsed: ");
368  goto end;
369  }
370  if (SSHParserTestUtilCheck("2.0", "MySSHClient-0.5.1", tx, STREAM_TOCLIENT))
371  goto end;
372 
373  result = 1;
374 
375 end:
376  FLOW_DESTROY(&f);
377  if (alp_tctx != NULL)
379  StreamTcpFreeConfig(true);
380  return result;
381 }
382 
383 /** \test Send a version string in one chunk (server version str)
384  */
385 static int SSHParserTest05(void)
386 {
387  int result = 0;
388  Flow f;
389  uint8_t sshbuf[] = "SSH-2.0-MySSHClient-0.5.1 some comments...\n";
390  uint32_t sshlen = sizeof(sshbuf) - 1;
391  TcpSession ssn;
393 
394  memset(&f, 0, sizeof(f));
395  memset(&ssn, 0, sizeof(ssn));
396  FLOW_INITIALIZE(&f);
397  f.protoctx = (void *)&ssn;
398  f.proto = IPPROTO_TCP;
400 
401  StreamTcpInitConfig(true);
402 
403  int r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SSH,
404  STREAM_TOCLIENT | STREAM_EOF, sshbuf, sshlen);
405  if (r != 0) {
406  printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
407  goto end;
408  }
409 
410  void *ssh_state = f.alstate;
411  if (ssh_state == NULL) {
412  printf("no ssh state: ");
413  goto end;
414  }
415  void *tx = SCSshStateGetTx(ssh_state, 0);
416 
417  if (SCSshTxGetAlStateProgress(tx, STREAM_TOCLIENT) != SshStateKex) {
418  printf("Client version string not parsed: ");
419  goto end;
420  }
421  if (SSHParserTestUtilCheck("2.0", "MySSHClient-0.5.1", tx, STREAM_TOCLIENT))
422  goto end;
423 
424  result = 1;
425 end:
426  FLOW_DESTROY(&f);
427  if (alp_tctx != NULL)
429  StreamTcpFreeConfig(true);
430  return result;
431 }
432 
433 /** \test Send a invalid version string in one chunk (server version str)
434  */
435 static int SSHParserTest06(void)
436 {
437  int result = 0;
438  Flow f;
439  uint8_t sshbuf[] = "SSH-2.0 some comments...\n";
440  uint32_t sshlen = sizeof(sshbuf) - 1;
441  TcpSession ssn;
443 
444  memset(&f, 0, sizeof(f));
445  memset(&ssn, 0, sizeof(ssn));
446  FLOW_INITIALIZE(&f);
447  f.protoctx = (void *)&ssn;
448  f.proto = IPPROTO_TCP;
450 
451  StreamTcpInitConfig(true);
452 
453  int r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SSH,
454  STREAM_TOCLIENT | STREAM_EOF, sshbuf, sshlen);
455  if (r == 0) {
456  printf("toserver chunk 1 returned %" PRId32 ", expected != 0: ", r);
457  goto end;
458  }
459  /* Ok, it returned an error. Let's make sure we didn't parse the string at all */
460 
461  void *ssh_state = f.alstate;
462  if (ssh_state == NULL) {
463  printf("no ssh state: ");
464  goto end;
465  }
466  void *tx = SCSshStateGetTx(ssh_state, 0);
467 
468  if (SCSshTxGetAlStateProgress(tx, STREAM_TOCLIENT) == SshStateKex) {
469  printf("Client version string parsed? It's not a valid string: ");
470  goto end;
471  }
472  const uint8_t *dummy = NULL;
473  uint32_t dummy_len = 0;
474  if (SCSshTxGetProtocol(tx, STREAM_TOCLIENT, &dummy, &dummy_len) != 0)
475  goto end;
476  if (SCSshTxGetSoftware(tx, STREAM_TOCLIENT, &dummy, &dummy_len) != 0)
477  goto end;
478 
479  result = 1;
480 end:
481  FLOW_DESTROY(&f);
482  if (alp_tctx != NULL)
484  StreamTcpFreeConfig(true);
485  return result;
486 }
487 
488 #define MAX_SSH_TEST_SIZE 512
489 
490 static int SSHParserTest07(void)
491 {
492  TcpReassemblyThreadCtx *ra_ctx = NULL;
493  ThreadVars tv;
494  TcpSession ssn;
495  Flow *f = NULL;
496  Packet *p = NULL;
497 
498  char sshbufs[2][MAX_SSH_TEST_SIZE] = {"SSH-2.", "0-MySSHClient-0.5.1\r\n"};
499 
500  memset(&tv, 0x00, sizeof(tv));
501 
502  StreamTcpUTInit(&ra_ctx);
507 
508  f = UTHBuildFlow(AF_INET, "1.1.1.1", "2.2.2.2", 1234, 2222);
509  FAIL_IF_NULL(f);
510  f->protoctx = &ssn;
511  f->proto = IPPROTO_TCP;
512  f->alproto = ALPROTO_SSH;
513 
514  p = PacketGetFromAlloc();
515  FAIL_IF(unlikely(p == NULL));
516  p->proto = IPPROTO_TCP;
517  p->flow = f;
518 
519  uint32_t seq = 2;
520  for (int i=0; i<2; i++) {
521  FAIL_IF(StreamTcpUTAddSegmentWithPayload(&tv, ra_ctx, &ssn.client, seq, (uint8_t *) sshbufs[i], strlen(sshbufs[i])) == -1);
522  seq += strlen(sshbufs[i]);
524  }
525 
526  void *ssh_state = f->alstate;
527  FAIL_IF_NULL(ssh_state);
528  void *tx = SCSshStateGetTx(ssh_state, 0);
529  FAIL_IF(SCSshTxGetAlStateProgress(tx, STREAM_TOSERVER) != SshStateKex);
530 
531  FAIL_IF(SSHParserTestUtilCheck("2.0", "MySSHClient-0.5.1", tx, STREAM_TOSERVER));
532 
533  UTHFreePacket(p);
534  UTHFreeFlow(f);
536  StreamTcpUTDeinit(ra_ctx);
537  PASS;
538 }
539 
540 /** \test Send a version banner in three chunks. */
541 static int SSHParserTest08(void)
542 {
543  TcpReassemblyThreadCtx *ra_ctx = NULL;
544  ThreadVars tv;
545  TcpSession ssn;
546  Flow *f = NULL;
547  Packet *p = NULL;
548 
549  char sshbufs[3][MAX_SSH_TEST_SIZE] = {"SSH-", "2.", "0-MySSHClient-0.5.1\r\n"};
550 
551  memset(&tv, 0x00, sizeof(tv));
552 
553  StreamTcpUTInit(&ra_ctx);
558 
559  f = UTHBuildFlow(AF_INET, "1.1.1.1", "2.2.2.2", 1234, 2222);
560  FAIL_IF_NULL(f);
561  f->protoctx = &ssn;
562  f->proto = IPPROTO_TCP;
563  f->alproto = ALPROTO_SSH;
564 
565  p = PacketGetFromAlloc();
566  FAIL_IF(unlikely(p == NULL));
567  p->proto = IPPROTO_TCP;
568  p->flow = f;
569 
570  uint32_t seq = 2;
571  for (int i=0; i<3; i++) {
572  FAIL_IF(StreamTcpUTAddSegmentWithPayload(&tv, ra_ctx, &ssn.client, seq, (uint8_t *) sshbufs[i], strlen(sshbufs[i])) == -1);
573  seq += strlen(sshbufs[i]);
575  }
576 
577  void *ssh_state = f->alstate;
578  FAIL_IF_NULL(ssh_state);
579  void *tx = SCSshStateGetTx(ssh_state, 0);
580  FAIL_IF(SCSshTxGetAlStateProgress(tx, STREAM_TOSERVER) != SshStateKex);
581 
582  FAIL_IF(SSHParserTestUtilCheck("2.0", "MySSHClient-0.5.1", tx, STREAM_TOSERVER));
583 
584  UTHFreePacket(p);
585  UTHFreeFlow(f);
587  StreamTcpUTDeinit(ra_ctx);
588  PASS;
589 }
590 
591 static int SSHParserTest09(void)
592 {
593  TcpReassemblyThreadCtx *ra_ctx = NULL;
594  ThreadVars tv;
595  TcpSession ssn;
596  Flow *f = NULL;
597  Packet *p = NULL;
598 
599  char sshbufs[2][MAX_SSH_TEST_SIZE] = {"SSH-2.", "0-MySSHClient-0.5.1\r\n"};
600 
601  memset(&tv, 0x00, sizeof(tv));
602 
603  StreamTcpUTInit(&ra_ctx);
608 
609  f = UTHBuildFlow(AF_INET, "1.1.1.1", "2.2.2.2", 1234, 2222);
610  FAIL_IF_NULL(f);
611  f->protoctx = &ssn;
612  f->proto = IPPROTO_TCP;
613  f->alproto = ALPROTO_SSH;
614 
615  p = PacketGetFromAlloc();
616  FAIL_IF(unlikely(p == NULL));
617  p->proto = IPPROTO_TCP;
618  p->flow = f;
619 
620  uint32_t seq = 2;
621  for (int i=0; i<2; i++) {
622  FAIL_IF(StreamTcpUTAddSegmentWithPayload(&tv, ra_ctx, &ssn.server, seq, (uint8_t *) sshbufs[i], strlen(sshbufs[i])) == -1);
623  seq += strlen(sshbufs[i]);
625  }
626 
627  void *ssh_state = f->alstate;
628  FAIL_IF_NULL(ssh_state);
629  void *tx = SCSshStateGetTx(ssh_state, 0);
630  FAIL_IF(SCSshTxGetAlStateProgress(tx, STREAM_TOCLIENT) != SshStateKex);
631 
632  FAIL_IF(SSHParserTestUtilCheck("2.0", "MySSHClient-0.5.1", tx, STREAM_TOCLIENT));
633 
634  UTHFreePacket(p);
635  UTHFreeFlow(f);
637  StreamTcpUTDeinit(ra_ctx);
638  PASS;
639 }
640 
641 /** \test Send a version banner in three chunks. */
642 static int SSHParserTest10(void)
643 {
644  TcpReassemblyThreadCtx *ra_ctx = NULL;
645  ThreadVars tv;
646  TcpSession ssn;
647  Flow *f = NULL;
648  Packet *p = NULL;
649 
650  char sshbufs[3][MAX_SSH_TEST_SIZE] = {"SSH-", "2.", "0-MySSHClient-0.5.1\r\n"};
651 
652  memset(&tv, 0x00, sizeof(tv));
653 
654  StreamTcpUTInit(&ra_ctx);
659 
660  f = UTHBuildFlow(AF_INET, "1.1.1.1", "2.2.2.2", 1234, 2222);
661  FAIL_IF_NULL(f);
662  f->protoctx = &ssn;
663  f->proto = IPPROTO_TCP;
664  f->alproto = ALPROTO_SSH;
665 
666  p = PacketGetFromAlloc();
667  FAIL_IF(unlikely(p == NULL));
668  p->proto = IPPROTO_TCP;
669  p->flow = f;
670 
671  uint32_t seq = 2;
672  for (int i=0; i<3; i++) {
673  FAIL_IF(StreamTcpUTAddSegmentWithPayload(&tv, ra_ctx, &ssn.server, seq, (uint8_t *) sshbufs[i], strlen(sshbufs[i])) == -1);
674  seq += strlen(sshbufs[i]);
676  }
677 
678  void *ssh_state = f->alstate;
679  FAIL_IF_NULL(ssh_state);
680  void *tx = SCSshStateGetTx(ssh_state, 0);
681  FAIL_IF(SCSshTxGetAlStateProgress(tx, STREAM_TOCLIENT) != SshStateKex);
682 
683  FAIL_IF(SSHParserTestUtilCheck("2.0", "MySSHClient-0.5.1", tx, STREAM_TOCLIENT));
684 
685  UTHFreePacket(p);
686  UTHFreeFlow(f);
688  StreamTcpUTDeinit(ra_ctx);
689  PASS;
690 }
691 
692 /** \test Send a banner and record in three chunks. */
693 static int SSHParserTest11(void)
694 {
695  int result = 0;
696  Flow f;
697  uint8_t sshbuf1[] = "SSH-2.0-MySSHClient-0.5.1\r\n";
698  uint32_t sshlen1 = sizeof(sshbuf1) - 1;
699  uint8_t sshbuf2[] = { 0x00, 0x00, 0x00, 0x03, 0x01, 21, 0x00};
700  uint32_t sshlen2 = sizeof(sshbuf2);
701  TcpSession ssn;
703 
704  memset(&f, 0, sizeof(f));
705  memset(&ssn, 0, sizeof(ssn));
706  FLOW_INITIALIZE(&f);
707  f.protoctx = (void *)&ssn;
708  f.proto = IPPROTO_TCP;
710 
711  StreamTcpInitConfig(true);
712 
713  int r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SSH,
714  STREAM_TOSERVER, sshbuf1, sshlen1);
715  if (r != 0) {
716  printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
717  goto end;
718  }
719  r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SSH, STREAM_TOSERVER,
720  sshbuf2, sshlen2);
721  if (r != 0) {
722  printf("toserver chunk 2 returned %" PRId32 ", expected 0: ", r);
723  goto end;
724  }
725 
726  void *ssh_state = f.alstate;
727  if (ssh_state == NULL) {
728  printf("no ssh state: ");
729  goto end;
730  }
731  void *tx = SCSshStateGetTx(ssh_state, 0);
732  if (SCSshTxGetFlags(tx, STREAM_TOSERVER) != SshStateSession) {
733  printf("Didn't detect the msg code of new keys (ciphered data starts): ");
734  goto end;
735  }
736  if (SSHParserTestUtilCheck("2.0", "MySSHClient-0.5.1", tx, STREAM_TOSERVER))
737  goto end;
738 
739  result = 1;
740 end:
741  FLOW_DESTROY(&f);
742  if (alp_tctx != NULL)
744  StreamTcpFreeConfig(true);
745  return result;
746 }
747 
748 /** \test Send a banner and 2 records record in four chunks. */
749 static int SSHParserTest12(void)
750 {
751  int result = 0;
752  Flow f;
753  uint8_t sshbuf1[] = "SSH-2.0-MySSHClient-0.5.1\r\n";
754  uint32_t sshlen1 = sizeof(sshbuf1) - 1;
755  uint8_t sshbuf2[] = { 0x00, 0x00, 0x00, 0x03,0x01, 17, 0x00};
756  uint32_t sshlen2 = sizeof(sshbuf2);
757  uint8_t sshbuf3[] = { 0x00, 0x00, 0x00, 0x03,0x01, 21, 0x00};
758  uint32_t sshlen3 = sizeof(sshbuf3);
759  TcpSession ssn;
761 
762  memset(&f, 0, sizeof(f));
763  memset(&ssn, 0, sizeof(ssn));
764  FLOW_INITIALIZE(&f);
765  f.protoctx = (void *)&ssn;
766  f.proto = IPPROTO_TCP;
768 
769  StreamTcpInitConfig(true);
770 
771  int r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SSH,
772  STREAM_TOSERVER, sshbuf1, sshlen1);
773  if (r != 0) {
774  printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
775  goto end;
776  }
777  r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SSH, STREAM_TOSERVER,
778  sshbuf2, sshlen2);
779  if (r != 0) {
780  printf("toserver chunk 2 returned %" PRId32 ", expected 0: ", r);
781  goto end;
782  }
783  r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SSH, STREAM_TOSERVER,
784  sshbuf3, sshlen3);
785  if (r != 0) {
786  printf("toserver chunk 3 returned %" PRId32 ", expected 0: ", r);
787  goto end;
788  }
789 
790  void *ssh_state = f.alstate;
791  if (ssh_state == NULL) {
792  printf("no ssh state: ");
793  goto end;
794  }
795  void *tx = SCSshStateGetTx(ssh_state, 0);
796  if (SCSshTxGetFlags(tx, STREAM_TOSERVER) != SshStateSession) {
797  printf("Didn't detect the msg code of new keys (ciphered data starts): ");
798  goto end;
799  }
800  if (SSHParserTestUtilCheck("2.0", "MySSHClient-0.5.1", tx, STREAM_TOSERVER))
801  goto end;
802 
803  result = 1;
804 end:
805  FLOW_DESTROY(&f);
806  if (alp_tctx != NULL)
808  StreamTcpFreeConfig(true);
809  return result;
810 }
811 
812 /** \test Send a banner and 2 records record in four chunks. */
813 static int SSHParserTest13(void)
814 {
815  TcpReassemblyThreadCtx *ra_ctx = NULL;
816  ThreadVars tv;
817  TcpSession ssn;
818  Flow *f = NULL;
819  Packet *p = NULL;
820 
821  uint8_t sshbuf1[] = "SSH-2.0-MySSHClient-0.5.1\r\n";
822  uint8_t sshbuf2[] = { 0x00, 0x00, 0x00, 0x02, 0x01, 17};
823  uint8_t sshbuf3[] = { 0x00, 0x00, 0x00, 0x02, 0x01, 21};
824 
825  uint8_t* sshbufs[3] = {sshbuf1, sshbuf2, sshbuf3};
826  uint32_t sshlens[3] = {sizeof(sshbuf1) - 1, sizeof(sshbuf2), sizeof(sshbuf3)};
827 
828  memset(&tv, 0x00, sizeof(tv));
829 
830  StreamTcpUTInit(&ra_ctx);
835 
836  f = UTHBuildFlow(AF_INET, "1.1.1.1", "2.2.2.2", 1234, 2222);
837  FAIL_IF_NULL(f);
838  f->protoctx = &ssn;
839  f->proto = IPPROTO_TCP;
840  f->alproto = ALPROTO_SSH;
841 
842  p = PacketGetFromAlloc();
843  FAIL_IF(unlikely(p == NULL));
844  p->proto = IPPROTO_TCP;
845  p->flow = f;
846 
847  uint32_t seq = 2;
848  for (int i=0; i<3; i++) {
849  FAIL_IF(StreamTcpUTAddSegmentWithPayload(&tv, ra_ctx, &ssn.client, seq, sshbufs[i], sshlens[i]) == -1);
850  seq += sshlens[i];
852  }
853 
854  void *ssh_state = f->alstate;
855  FAIL_IF_NULL(ssh_state);
856  void *tx = SCSshStateGetTx(ssh_state, 0);
857  FAIL_IF(SCSshTxGetFlags(tx, STREAM_TOSERVER) != SshStateSession);
858 
859  FAIL_IF(SSHParserTestUtilCheck("2.0", "MySSHClient-0.5.1", tx, STREAM_TOSERVER));
860 
861  UTHFreePacket(p);
862  UTHFreeFlow(f);
864  StreamTcpUTDeinit(ra_ctx);
865  PASS;
866 }
867 
868 /** \test Send a banner and 2 records record in four chunks. */
869 static int SSHParserTest14(void)
870 {
871  TcpReassemblyThreadCtx *ra_ctx = NULL;
872  ThreadVars tv;
873  TcpSession ssn;
874  Flow *f = NULL;
875  Packet *p = NULL;
876 
877  uint8_t sshbuf1[] = "SSH-2.0-MySSHClient-0.5.1\r\n";
878  uint8_t sshbuf2[] = { 0x00, 0x00, 0x00, 0x10, 0x01, 17, 0x00};
879  uint8_t sshbuf3[] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08};
880  uint8_t sshbuf4[] = { 0x09, 0x10, 0x11, 0x12, 0x13, 0x00};
881  /* first byte of this record in sshbuf4 */
882  uint8_t sshbuf5[] = { 0x00, 0x00, 0x02, 0x01, 21};
883 
884  uint8_t* sshbufs[5] = {sshbuf1, sshbuf2, sshbuf3, sshbuf4, sshbuf5};
885  uint32_t sshlens[5] = {sizeof(sshbuf1) - 1, sizeof(sshbuf2), sizeof(sshbuf3), sizeof(sshbuf4), sizeof(sshbuf5)};
886 
887  memset(&tv, 0x00, sizeof(tv));
888 
889  StreamTcpUTInit(&ra_ctx);
894 
895  f = UTHBuildFlow(AF_INET, "1.1.1.1", "2.2.2.2", 1234, 2222);
896  FAIL_IF_NULL(f);
897  f->protoctx = &ssn;
898  f->proto = IPPROTO_TCP;
899  f->alproto = ALPROTO_SSH;
900 
901  p = PacketGetFromAlloc();
902  FAIL_IF(unlikely(p == NULL));
903  p->proto = IPPROTO_TCP;
904  p->flow = f;
905 
906  uint32_t seq = 2;
907  for (int i=0; i<5; i++) {
908  FAIL_IF(StreamTcpUTAddSegmentWithPayload(&tv, ra_ctx, &ssn.client, seq, sshbufs[i], sshlens[i]) == -1);
909  seq += sshlens[i];
911  }
912 
913  void *ssh_state = f->alstate;
914  FAIL_IF_NULL(ssh_state);
915  void *tx = SCSshStateGetTx(ssh_state, 0);
916  FAIL_IF(SCSshTxGetFlags(tx, STREAM_TOSERVER) != SshStateSession);
917 
918  FAIL_IF(SSHParserTestUtilCheck("2.0", "MySSHClient-0.5.1", tx, STREAM_TOSERVER));
919 
920  UTHFreePacket(p);
921  UTHFreeFlow(f);
923  StreamTcpUTDeinit(ra_ctx);
924  PASS;
925 }
926 
927 /** \test Send a banner and 2 records record in four chunks. */
928 static int SSHParserTest15(void)
929 {
930  TcpReassemblyThreadCtx *ra_ctx = NULL;
931  ThreadVars tv;
932  TcpSession ssn;
933  Flow *f = NULL;
934  Packet *p = NULL;
935 
936  uint8_t sshbuf1[] = "SSH-2.0-MySSHClient-0.5.1\r\n";
937  uint8_t sshbuf2[] = { 0x00, 0x00, 0x00, 0x10, 0x01, 17, 0x00};
938  uint8_t sshbuf3[] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08};
939  uint8_t sshbuf4[] = { 0x09, 0x10, 0x11, 0x12, 0x13, 0x00};
940  uint8_t sshbuf5[] = { 0x00, 0x00, 0x02, 0x01, 20, 0x00, 0x00, 0x00, 0x02, 0x01, 21};
941 
942  uint8_t* sshbufs[5] = {sshbuf1, sshbuf2, sshbuf3, sshbuf4, sshbuf5};
943  uint32_t sshlens[5] = {sizeof(sshbuf1) - 1, sizeof(sshbuf2), sizeof(sshbuf3), sizeof(sshbuf4), sizeof(sshbuf5)};
944 
945  memset(&tv, 0x00, sizeof(tv));
946 
947  StreamTcpUTInit(&ra_ctx);
952 
953  f = UTHBuildFlow(AF_INET, "1.1.1.1", "2.2.2.2", 1234, 2222);
954  FAIL_IF_NULL(f);
955  f->protoctx = &ssn;
956  f->proto = IPPROTO_TCP;
957  f->alproto = ALPROTO_SSH;
958 
959  p = PacketGetFromAlloc();
960  FAIL_IF(unlikely(p == NULL));
961  p->proto = IPPROTO_TCP;
962  p->flow = f;
963 
964  uint32_t seq = 2;
965  for (int i=0; i<5; i++) {
966  FAIL_IF(StreamTcpUTAddSegmentWithPayload(&tv, ra_ctx, &ssn.client, seq, sshbufs[i], sshlens[i]) == -1);
967  seq += sshlens[i];
969  }
970 
971  void *ssh_state = f->alstate;
972  FAIL_IF_NULL(ssh_state);
973  void *tx = SCSshStateGetTx(ssh_state, 0);
974  FAIL_IF(SCSshTxGetFlags(tx, STREAM_TOSERVER) != SshStateSession);
975 
976  FAIL_IF(SSHParserTestUtilCheck("2.0", "MySSHClient-0.5.1", tx, STREAM_TOSERVER));
977 
978  UTHFreePacket(p);
979  UTHFreeFlow(f);
981  StreamTcpUTDeinit(ra_ctx);
982  PASS;
983 }
984 
985 /** \test Send toserver a banner and record in three chunks. */
986 static int SSHParserTest16(void)
987 {
988  TcpReassemblyThreadCtx *ra_ctx = NULL;
989  ThreadVars tv;
990  TcpSession ssn;
991  Flow *f = NULL;
992  Packet *p = NULL;
993 
994  uint8_t sshbuf1[] = "SSH-";
995  uint8_t sshbuf2[] = "2.0-MySSHClient-0.5.1\r\n";
996  uint8_t sshbuf3[] = { 0x00, 0x00, 0x00, 0x03,0x01, 21, 0x00};
997 
998  uint8_t* sshbufs[3] = {sshbuf1, sshbuf2, sshbuf3};
999  uint32_t sshlens[3] = {sizeof(sshbuf1) - 1, sizeof(sshbuf2) - 1, sizeof(sshbuf3)};
1000 
1001  memset(&tv, 0x00, sizeof(tv));
1002 
1003  StreamTcpUTInit(&ra_ctx);
1008 
1009  f = UTHBuildFlow(AF_INET, "1.1.1.1", "2.2.2.2", 1234, 2222);
1010  FAIL_IF_NULL(f);
1011  f->protoctx = &ssn;
1012  f->proto = IPPROTO_TCP;
1013  f->alproto = ALPROTO_SSH;
1014 
1015  p = PacketGetFromAlloc();
1016  FAIL_IF(unlikely(p == NULL));
1017  p->proto = IPPROTO_TCP;
1018  p->flow = f;
1019 
1020  uint32_t seq = 2;
1021  for (int i=0; i<3; i++) {
1022  FAIL_IF(StreamTcpUTAddSegmentWithPayload(&tv, ra_ctx, &ssn.server, seq, sshbufs[i], sshlens[i]) == -1);
1023  seq += sshlens[i];
1025  }
1026 
1027  void *ssh_state = f->alstate;
1028  FAIL_IF_NULL(ssh_state);
1029  void *tx = SCSshStateGetTx(ssh_state, 0);
1030  FAIL_IF(SCSshTxGetFlags(tx, STREAM_TOCLIENT) != SshStateSession);
1031 
1032  FAIL_IF(SSHParserTestUtilCheck("2.0", "MySSHClient-0.5.1", tx, STREAM_TOCLIENT));
1033 
1034  UTHFreePacket(p);
1035  UTHFreeFlow(f);
1037  StreamTcpUTDeinit(ra_ctx);
1038  PASS;
1039 }
1040 
1041 /** \test Send toserver a banner and 2 records record in four chunks. */
1042 static int SSHParserTest17(void)
1043 {
1044  TcpReassemblyThreadCtx *ra_ctx = NULL;
1045  ThreadVars tv;
1046  TcpSession ssn;
1047  Flow *f = NULL;
1048  Packet *p = NULL;
1049 
1050  uint8_t sshbuf1[] = "SSH-";
1051  uint8_t sshbuf2[] = "2.0-MySSHClient-0.5.1\r\n";
1052  uint8_t sshbuf3[] = { 0x00, 0x00, 0x00, 0x03, 0x01, 17, 0x00};
1053  uint8_t sshbuf4[] = { 0x00, 0x00, 0x00, 0x03, 0x01, 21, 0x00};
1054 
1055  uint8_t* sshbufs[4] = {sshbuf1, sshbuf2, sshbuf3, sshbuf4};
1056  uint32_t sshlens[4] = {sizeof(sshbuf1) - 1, sizeof(sshbuf2) - 1, sizeof(sshbuf3), sizeof(sshbuf4)};
1057 
1058  memset(&tv, 0x00, sizeof(tv));
1059 
1060  StreamTcpUTInit(&ra_ctx);
1065 
1066  f = UTHBuildFlow(AF_INET, "1.1.1.1", "2.2.2.2", 1234, 2222);
1067  FAIL_IF_NULL(f);
1068  f->protoctx = &ssn;
1069  f->proto = IPPROTO_TCP;
1070  f->alproto = ALPROTO_SSH;
1071 
1072  p = PacketGetFromAlloc();
1073  FAIL_IF(unlikely(p == NULL));
1074  p->proto = IPPROTO_TCP;
1075  p->flow = f;
1076 
1077  uint32_t seq = 2;
1078  for (int i=0; i<4; i++) {
1079  FAIL_IF(StreamTcpUTAddSegmentWithPayload(&tv, ra_ctx, &ssn.server, seq, sshbufs[i], sshlens[i]) == -1);
1080  seq += sshlens[i];
1082  }
1083 
1084  void *ssh_state = f->alstate;
1085  FAIL_IF_NULL(ssh_state);
1086  void *tx = SCSshStateGetTx(ssh_state, 0);
1087  FAIL_IF(SCSshTxGetFlags(tx, STREAM_TOCLIENT) != SshStateSession);
1088 
1089  FAIL_IF(SSHParserTestUtilCheck("2.0", "MySSHClient-0.5.1", tx, STREAM_TOCLIENT));
1090 
1091  UTHFreePacket(p);
1092  UTHFreeFlow(f);
1094  StreamTcpUTDeinit(ra_ctx);
1095  PASS;
1096 }
1097 
1098 /** \test 2 directional test */
1099 static int SSHParserTest18(void)
1100 {
1101  TcpReassemblyThreadCtx *ra_ctx = NULL;
1102  ThreadVars tv;
1103  TcpSession ssn;
1104  Flow *f = NULL;
1105  Packet *p = NULL;
1106 
1107  uint8_t server1[] = "SSH-2.0-OpenSSH_4.7p1 Debian-8ubuntu3\r\n";
1108  uint8_t sshbuf1[] = "SSH-";
1109  uint8_t sshbuf2[] = "2.0-MySSHClient-0.5.1\r\n";
1110  uint8_t server2[] = { 0x00, 0x00, 0x00, 0x03, 0x01, 21, 0x00 };
1111  uint8_t sshbuf3[] = { 0x00, 0x00, 0x00, 0x03, 0x01, 21, 0x00 };
1112 
1113 
1114  memset(&tv, 0x00, sizeof(tv));
1115 
1116  StreamTcpUTInit(&ra_ctx);
1121 
1122  uint8_t* sshbufs[5] = {server1, sshbuf1, sshbuf2, server2, sshbuf3};
1123  uint32_t sshlens[5] = {sizeof(server1) - 1, sizeof(sshbuf1) - 1, sizeof(sshbuf2) -1, sizeof(server2) - 1, sizeof(sshbuf3)};
1124  bool sshdirs[5] = {true, false, false, true, false};
1125 
1126  f = UTHBuildFlow(AF_INET, "1.1.1.1", "2.2.2.2", 1234, 2222);
1127  FAIL_IF_NULL(f);
1128  f->protoctx = &ssn;
1129  f->proto = IPPROTO_TCP;
1130  f->alproto = ALPROTO_SSH;
1131 
1132  p = PacketGetFromAlloc();
1133  FAIL_IF(unlikely(p == NULL));
1134  p->proto = IPPROTO_TCP;
1135  p->flow = f;
1136 
1137  uint32_t seqcli = 2;
1138  uint32_t seqsrv = 2;
1139  for (int i=0; i<5; i++) {
1140  if (sshdirs[i]) {
1141  FAIL_IF(StreamTcpUTAddSegmentWithPayload(&tv, ra_ctx, &ssn.server, seqsrv, sshbufs[i], sshlens[i]) == -1);
1142  seqsrv += sshlens[i];
1144  } else {
1145  FAIL_IF(StreamTcpUTAddSegmentWithPayload(&tv, ra_ctx, &ssn.client, seqcli, sshbufs[i], sshlens[i]) == -1);
1146  seqcli += sshlens[i];
1148  }
1149  }
1150 
1151  void *ssh_state = f->alstate;
1152  FAIL_IF_NULL(ssh_state);
1153  void *tx = SCSshStateGetTx(ssh_state, 0);
1154  FAIL_IF(SCSshTxGetFlags(tx, STREAM_TOCLIENT) != SshStateSession);
1155 
1156  FAIL_IF(!(SCAppLayerParserStateIssetFlag(f->alparser, APP_LAYER_PARSER_NO_INSPECTION)));
1157 
1158  UTHFreePacket(p);
1159  UTHFreeFlow(f);
1161  StreamTcpUTDeinit(ra_ctx);
1162  PASS;
1163 }
1164 
1165 /** \test Really long banner handling: bannel exactly 255 */
1166 static int SSHParserTest19(void)
1167 {
1168  TcpReassemblyThreadCtx *ra_ctx = NULL;
1169  ThreadVars tv;
1170  TcpSession ssn;
1171  Flow *f = NULL;
1172  Packet *p = NULL;
1173 
1174  uint8_t sshbuf1[] = "SSH-";
1175  uint8_t sshbuf2[] = "2.0-";
1176  uint8_t sshbuf3[] = "abcdefghijklmnopqrstuvwxyz"
1177  "abcdefghijklmnopqrstuvwxyz"//60
1178  "abcdefghijklmnopqrstuvwxyz"
1179  "abcdefghijklmnopqrstuvwxyz"//112
1180  "abcdefghijklmnopqrstuvwxyz"
1181  "abcdefghijklmnopqrstuvwxyz"//164
1182  "abcdefghijklmnopqrstuvwxyz"
1183  "abcdefghijklmnopqrstuvwxyz"//216
1184  "abcdefghijklmnopqrstuvwxyz"//242
1185  "abcdefghijkl\r";//255
1186  uint8_t sshbuf4[] = { 0x00, 0x00, 0x00, 0x03, 0x01, 21, 0x00};
1187 
1188  uint8_t* sshbufs[4] = {sshbuf1, sshbuf2, sshbuf3, sshbuf4};
1189  uint32_t sshlens[4] = {sizeof(sshbuf1) - 1, sizeof(sshbuf2) - 1, sizeof(sshbuf3) - 1, sizeof(sshbuf4)};
1190 
1191  memset(&tv, 0x00, sizeof(tv));
1192 
1193  StreamTcpUTInit(&ra_ctx);
1198 
1199  f = UTHBuildFlow(AF_INET, "1.1.1.1", "2.2.2.2", 1234, 2222);
1200  FAIL_IF_NULL(f);
1201  f->protoctx = &ssn;
1202  f->proto = IPPROTO_TCP;
1203  f->alproto = ALPROTO_SSH;
1204 
1205  p = PacketGetFromAlloc();
1206  FAIL_IF(unlikely(p == NULL));
1207  p->proto = IPPROTO_TCP;
1208  p->flow = f;
1209 
1210  uint32_t seq = 2;
1211  for (int i=0; i<4; i++) {
1212  FAIL_IF(StreamTcpUTAddSegmentWithPayload(&tv, ra_ctx, &ssn.server, seq, sshbufs[i], sshlens[i]) == -1);
1213  seq += sshlens[i];
1215  }
1216 
1217  void *ssh_state = f->alstate;
1218  FAIL_IF_NULL(ssh_state);
1219  void *tx = SCSshStateGetTx(ssh_state, 0);
1220  FAIL_IF(SCSshTxGetFlags(tx, STREAM_TOCLIENT) != SshStateSession);
1221 
1222  sshbuf3[sizeof(sshbuf3) - 2] = 0;
1223  FAIL_IF(SSHParserTestUtilCheck("2.0", (char *)sshbuf3, tx, STREAM_TOCLIENT));
1224 
1225  UTHFreePacket(p);
1226  UTHFreeFlow(f);
1228  StreamTcpUTDeinit(ra_ctx);
1229  PASS;
1230 }
1231 
1232 /** \test Really long banner handling: banner exactly 255,
1233  * followed by malformed record */
1234 static int SSHParserTest20(void)
1235 {
1236  TcpReassemblyThreadCtx *ra_ctx = NULL;
1237  ThreadVars tv;
1238  TcpSession ssn;
1239  Flow *f = NULL;
1240  Packet *p = NULL;
1241 
1242  uint8_t sshbuf1[] = "SSH-";
1243  uint8_t sshbuf2[] = "2.0-";
1244  uint8_t sshbuf3[] = "abcdefghijklmnopqrstuvwxyz"
1245  "abcdefghijklmnopqrstuvwxyz"//60
1246  "abcdefghijklmnopqrstuvwxyz"
1247  "abcdefghijklmnopqrstuvwxyz"//112
1248  "abcdefghijklmnopqrstuvwxyz"
1249  "abcdefghijklmnopqrstuvwxyz"//164
1250  "abcdefghijklmnopqrstuvwxyz"
1251  "abcdefghijklmnopqrstuvwxyz"//216
1252  "abcdefghijklmnopqrstuvwxyz"//242
1253  "abcdefghijklm\r";//256
1254  uint8_t sshbuf4[] = {'a','b','c','d','e','f', '\r',
1255  0x00, 0x00, 0x00, 0x06, 0x01, 21, 0x00, 0x00, 0x00};
1256 
1257  uint8_t* sshbufs[4] = {sshbuf1, sshbuf2, sshbuf3, sshbuf4};
1258  uint32_t sshlens[4] = {sizeof(sshbuf1) - 1, sizeof(sshbuf2) - 1, sizeof(sshbuf3) - 1, sizeof(sshbuf4) - 1};
1259 
1260  memset(&tv, 0x00, sizeof(tv));
1261 
1262  StreamTcpUTInit(&ra_ctx);
1267 
1268  f = UTHBuildFlow(AF_INET, "1.1.1.1", "2.2.2.2", 1234, 2222);
1269  FAIL_IF_NULL(f);
1270  f->protoctx = &ssn;
1271  f->proto = IPPROTO_TCP;
1272  f->alproto = ALPROTO_SSH;
1273 
1274  p = PacketGetFromAlloc();
1275  FAIL_IF(unlikely(p == NULL));
1276  p->proto = IPPROTO_TCP;
1277  p->flow = f;
1278 
1279  uint32_t seq = 2;
1280  for (int i=0; i<4; i++) {
1281  FAIL_IF(StreamTcpUTAddSegmentWithPayload(&tv, ra_ctx, &ssn.server, seq, sshbufs[i], sshlens[i]) == -1);
1282  seq += sshlens[i];
1284  }
1285 
1286  void *ssh_state = f->alstate;
1287  FAIL_IF_NULL(ssh_state);
1288  void *tx = SCSshStateGetTx(ssh_state, 0);
1289  FAIL_IF(SCSshTxGetFlags(tx, STREAM_TOCLIENT) != SshStateSession);
1290 
1291  FAIL_IF(SSHParserTestUtilCheck("2.0", NULL, tx, STREAM_TOCLIENT));
1292 
1293  UTHFreePacket(p);
1294  UTHFreeFlow(f);
1296  StreamTcpUTDeinit(ra_ctx);
1297  PASS;
1298 }
1299 
1300 /** \test Fragmented banner handling: chunk has final part of bannel plus
1301  * a record. */
1302 static int SSHParserTest21(void)
1303 {
1304  TcpReassemblyThreadCtx *ra_ctx = NULL;
1305  ThreadVars tv;
1306  TcpSession ssn;
1307  Flow *f = NULL;
1308  Packet *p = NULL;
1309 
1310  uint8_t sshbuf1[] = "SSH-";
1311  uint8_t sshbuf2[] = "2.0-";
1312  uint8_t sshbuf3[] = "abcdefghijklmnopqrstuvwxyz"
1313  "abcdefghijklmnopqrstuvwxyz"//60
1314  "abcdefghijklmnopqrstuvwxyz"
1315  "abcdefghijklmnopqrstuvwxyz"//112
1316  "abcdefghijklmnopqrstuvwxyz"
1317  "abcdefghijklmnopqrstuvwxyz"//164
1318  "abcdefghijklmnopqrstuvwxyz"
1319  "abcdefghijklmnopqrstuvwxyz"//216
1320  "abcdefghijklmnopqrstuvwxy";//241
1321  uint8_t sshbuf4[] = {'l','i','b','s','s','h', '\r',
1322  0x00, 0x00, 0x00, 0x06, 0x01, 21, 0x00, 0x00, 0x00};
1323 
1324  uint8_t* sshbufs[4] = {sshbuf1, sshbuf2, sshbuf3, sshbuf4};
1325  uint32_t sshlens[4] = {sizeof(sshbuf1) - 1, sizeof(sshbuf2) - 1, sizeof(sshbuf3) - 1, sizeof(sshbuf4)};
1326 
1327  memset(&tv, 0x00, sizeof(tv));
1328 
1329  StreamTcpUTInit(&ra_ctx);
1334 
1335  f = UTHBuildFlow(AF_INET, "1.1.1.1", "2.2.2.2", 1234, 2222);
1336  FAIL_IF_NULL(f);
1337  f->protoctx = &ssn;
1338  f->proto = IPPROTO_TCP;
1339  f->alproto = ALPROTO_SSH;
1340 
1341  p = PacketGetFromAlloc();
1342  FAIL_IF(unlikely(p == NULL));
1343  p->proto = IPPROTO_TCP;
1344  p->flow = f;
1345 
1346  uint32_t seq = 2;
1347  for (int i=0; i<4; i++) {
1348  FAIL_IF(StreamTcpUTAddSegmentWithPayload(&tv, ra_ctx, &ssn.server, seq, sshbufs[i], sshlens[i]) == -1);
1349  seq += sshlens[i];
1351  }
1352 
1353  void *ssh_state = f->alstate;
1354  FAIL_IF_NULL(ssh_state);
1355  void *tx = SCSshStateGetTx(ssh_state, 0);
1356  FAIL_IF(SCSshTxGetFlags(tx, STREAM_TOCLIENT) != SshStateSession);
1357 
1358  FAIL_IF(SSHParserTestUtilCheck("2.0", NULL, tx, STREAM_TOCLIENT));
1359 
1360  UTHFreePacket(p);
1361  UTHFreeFlow(f);
1363  StreamTcpUTDeinit(ra_ctx);
1364  PASS;
1365 }
1366 
1367 /** \test Fragmented banner handling: chunk has final part of bannel plus
1368  * a record. */
1369 static int SSHParserTest22(void)
1370 {
1371  TcpReassemblyThreadCtx *ra_ctx = NULL;
1372  ThreadVars tv;
1373  TcpSession ssn;
1374  Flow *f = NULL;
1375  Packet *p = NULL;
1376 
1377  uint8_t sshbuf1[] = "SSH-";
1378  uint8_t sshbuf2[] = "2.0-";
1379  uint8_t sshbuf3[] = {
1380  'l', 'i', 'b', 's', 's', 'h', '\r', // 7
1381 
1382  0x00, 0x00, 0x00, 0x06, 0x01, 17, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x01, 17,
1383  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x01, 17, 0x00, 0x00, 0x00, 0x00, 0x00,
1384  0x00, 0x00, 0x06, 0x01, 17, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x01, 17, 0x00,
1385  0x00, 0x00, 0x00, // 50
1386 
1387  0x00, 0x00, 0x00, 0x06, 0x01, 17, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x01, 17,
1388  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x01, 17, 0x00, 0x00, 0x00, 0x00, 0x00,
1389  0x00, 0x00, 0x06, 0x01, 17, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x01, 17, 0x00,
1390  0x00, 0x00, 0x00, // 100
1391 
1392  0x00, 0x00, 0x00, 0x06, 0x01, 17, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x01, 17,
1393  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x01, 17, 0x00, 0x00, 0x00, 0x00, 0x00,
1394  0x00, 0x00, 0x06, 0x01, 17, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x01, 17, 0x00,
1395  0x00, 0x00, 0x00, // 150
1396 
1397  0x00, 0x00, 0x00, 0x06, 0x01, 17, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x01, 17,
1398  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x01, 17, 0x00, 0x00, 0x00, 0x00, 0x00,
1399  0x00, 0x00, 0x06, 0x01, 17, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x01, 17, 0x00,
1400  0x00, 0x00, 0x00, // 200
1401 
1402  0x00, 0x00, 0x00, 0x06, 0x01, 17, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x01, 17,
1403  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x01, 17, 0x00, 0x00, 0x00, 0x00, 0x00,
1404  0x00, 0x00, 0x06, 0x01, 17, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x01, 17, 0x00,
1405  0x00, 0x00, 0x00, // 250
1406 
1407  0x00, 0x00, 0x00, 0x06, 0x01, 17, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x01, 17,
1408  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x01, 17, 0x00, 0x00, 0x00, 0x00, 0x00,
1409  0x00, 0x00, 0x06, 0x01, 17, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x01, 21, 0x00,
1410  0x00, 0x00, 0x00, // 300
1411  };
1412 
1413  uint8_t *sshbufs[3] = { sshbuf1, sshbuf2, sshbuf3 };
1414  uint32_t sshlens[3] = { sizeof(sshbuf1) - 1, sizeof(sshbuf2) - 1, sizeof(sshbuf3) - 1 };
1415 
1416  memset(&tv, 0x00, sizeof(tv));
1417 
1418  StreamTcpUTInit(&ra_ctx);
1423 
1424  f = UTHBuildFlow(AF_INET, "1.1.1.1", "2.2.2.2", 1234, 2222);
1425  FAIL_IF_NULL(f);
1426  f->protoctx = &ssn;
1427  f->proto = IPPROTO_TCP;
1428  f->alproto = ALPROTO_SSH;
1429 
1430  p = PacketGetFromAlloc();
1431  FAIL_IF(unlikely(p == NULL));
1432  p->proto = IPPROTO_TCP;
1433  p->flow = f;
1434 
1435  uint32_t seq = 2;
1436  for (int i = 0; i < 3; i++) {
1438  &tv, ra_ctx, &ssn.server, seq, sshbufs[i], sshlens[i]) == -1);
1439  seq += sshlens[i];
1441  0);
1442  }
1443 
1444  void *ssh_state = f->alstate;
1445  FAIL_IF_NULL(ssh_state);
1446  void *tx = SCSshStateGetTx(ssh_state, 0);
1447  FAIL_IF(SCSshTxGetFlags(tx, STREAM_TOCLIENT) != SshStateSession);
1448 
1449  FAIL_IF(SSHParserTestUtilCheck("2.0", "libssh", tx, STREAM_TOCLIENT));
1450 
1451  UTHFreePacket(p);
1452  UTHFreeFlow(f);
1454  StreamTcpUTDeinit(ra_ctx);
1455  PASS;
1456 }
1457 
1458 /** \test Send a version string in one chunk (client version str). */
1459 static int SSHParserTest23(void)
1460 {
1461  int result = 0;
1462  Flow f;
1463  uint8_t sshbuf[] = "SSH-2.0\r-MySSHClient-0.5.1\n";
1464  uint32_t sshlen = sizeof(sshbuf) - 1;
1465  TcpSession ssn;
1467 
1468  memset(&f, 0, sizeof(f));
1469  memset(&ssn, 0, sizeof(ssn));
1470  FLOW_INITIALIZE(&f);
1471  f.protoctx = (void *)&ssn;
1472  f.proto = IPPROTO_TCP;
1473  f.alproto = ALPROTO_SSH;
1474 
1475  StreamTcpInitConfig(true);
1476 
1477  int r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SSH,
1478  STREAM_TOSERVER | STREAM_EOF, sshbuf, sshlen);
1479  if (r == 0) {
1480  printf("toclient chunk 1 returned 0 expected non null: ");
1481  goto end;
1482  }
1483 
1484  result = 1;
1485 end:
1486  if (alp_tctx != NULL)
1488  StreamTcpFreeConfig(true);
1489  FLOW_DESTROY(&f);
1490  return result;
1491 }
1492 
1493 /** \test Send a version string in one chunk (client version str). */
1494 static int SSHParserTest24(void)
1495 {
1496  int result = 0;
1497  Flow f;
1498  uint8_t sshbuf[] = "SSH-2.0-\rMySSHClient-0.5.1\n";
1499  uint32_t sshlen = sizeof(sshbuf) - 1;
1500  TcpSession ssn;
1502 
1503  memset(&f, 0, sizeof(f));
1504  memset(&ssn, 0, sizeof(ssn));
1505  FLOW_INITIALIZE(&f);
1506  f.protoctx = (void *)&ssn;
1507  f.proto = IPPROTO_TCP;
1508  f.alproto = ALPROTO_SSH;
1509 
1510  StreamTcpInitConfig(true);
1511 
1512  int r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SSH,
1513  STREAM_TOSERVER | STREAM_EOF, sshbuf, sshlen);
1514  if (r != 0) {
1515  printf("toclient chunk 1 returned %" PRId32 ", expected 0: ", r);
1516  goto end;
1517  }
1518 
1519  void *ssh_state = f.alstate;
1520  if (ssh_state == NULL) {
1521  printf("no ssh state: ");
1522  goto end;
1523  }
1524  void *tx = SCSshStateGetTx(ssh_state, 0);
1525  if (SCSshTxGetFlags(tx, STREAM_TOSERVER) != SshStateKex) {
1526  printf("Didn't detect the msg code of new keys (ciphered data starts): ");
1527  goto end;
1528  }
1529  if (SSHParserTestUtilCheck("2.0", NULL, tx, STREAM_TOSERVER))
1530  goto end;
1531 
1532  result = 1;
1533 end:
1534  FLOW_DESTROY(&f);
1535  if (alp_tctx != NULL)
1537  StreamTcpFreeConfig(true);
1538  return result;
1539 }
1540 
1541 /** \test Send a malformed banner */
1542 static int SSHParserTest25(void)
1543 {
1544  Flow f;
1545  uint8_t sshbuf[] = "\n";
1546  uint32_t sshlen = sizeof(sshbuf) - 1;
1547  TcpSession ssn;
1550 
1551  memset(&f, 0, sizeof(f));
1552  memset(&ssn, 0, sizeof(ssn));
1553  FLOW_INITIALIZE(&f);
1554  f.protoctx = (void *)&ssn;
1555  f.proto = IPPROTO_TCP;
1556  f.alproto = ALPROTO_SSH;
1557 
1558  StreamTcpInitConfig(true);
1559 
1560  int r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SSH,
1561  STREAM_TOSERVER | STREAM_EOF, sshbuf, sshlen);
1562  FAIL_IF(r != -1);
1563 
1564  void *ssh_state = f.alstate;
1565  FAIL_IF_NULL(ssh_state);
1566  void *tx = SCSshStateGetTx(ssh_state, 0);
1567  FAIL_IF(SCSshTxGetFlags(tx, STREAM_TOSERVER) == SshStateKex);
1568  const uint8_t *dummy = NULL;
1569  uint32_t dummy_len = 0;
1570  FAIL_IF(SCSshTxGetSoftware(tx, STREAM_TOCLIENT, &dummy, &dummy_len) != 0);
1571 
1572  FLOW_DESTROY(&f);
1574  StreamTcpFreeConfig(true);
1575  PASS;
1576 }
1577 
1578 /** \test State name table: the four phase names, the rejected legacy
1579  * names (no backward-compat aliases), the unhookable completion
1580  * state, unknown names and the id-to-name mapping. */
1581 static int SSHParserTest26(void)
1582 {
1583  /* new names */
1585  IPPROTO_TCP, ALPROTO_SSH, "request_banner", STREAM_TOSERVER) != SshStateBanner);
1587  IPPROTO_TCP, ALPROTO_SSH, "request_kex", STREAM_TOSERVER) != SshStateKex);
1588  FAIL_IF(AppLayerParserGetStateIdByName(IPPROTO_TCP, ALPROTO_SSH, "request_session",
1589  STREAM_TOSERVER) != SshStateSession);
1590  FAIL_IF(AppLayerParserGetStateIdByName(IPPROTO_TCP, ALPROTO_SSH, "response_banner",
1591  STREAM_TOCLIENT) != SshStateBanner);
1592  FAIL_IF(AppLayerParserGetStateIdByName(IPPROTO_TCP, ALPROTO_SSH, "response_session",
1593  STREAM_TOCLIENT) != SshStateSession);
1594 
1595  /* legacy names are rejected: no backward-compat aliases
1596  * (pre-production — breaking changes are allowed) */
1598  IPPROTO_TCP, ALPROTO_SSH, "request_in_progress", STREAM_TOSERVER) != -1);
1600  IPPROTO_TCP, ALPROTO_SSH, "request_banner_done", STREAM_TOSERVER) != -1);
1602  IPPROTO_TCP, ALPROTO_SSH, "request_finished", STREAM_TOSERVER) != -1);
1604  IPPROTO_TCP, ALPROTO_SSH, "response_finished", STREAM_TOCLIENT) != -1);
1605 
1606  /* the completion state is registered but not hookable */
1608  IPPROTO_TCP, ALPROTO_SSH, "request_done", STREAM_TOSERVER) != -1);
1610  IPPROTO_TCP, ALPROTO_SSH, "response_done", STREAM_TOCLIENT) != -1);
1611 
1612  /* unknown names (including banner_wait_eol) and wrong direction
1613  * prefix */
1615  IPPROTO_TCP, ALPROTO_SSH, "request_nosuchstate", STREAM_TOSERVER) != -1);
1617  IPPROTO_TCP, ALPROTO_SSH, "request_banner_wait_eol", STREAM_TOSERVER) != -1);
1619  IPPROTO_TCP, ALPROTO_SSH, "response_banner", STREAM_TOSERVER) != -1);
1620 
1621  /* id to name */
1623  IPPROTO_TCP, ALPROTO_SSH, SshStateBanner, STREAM_TOSERVER),
1624  "request_banner") != 0);
1626  IPPROTO_TCP, ALPROTO_SSH, SshStateKex, STREAM_TOSERVER),
1627  "request_kex") != 0);
1629  IPPROTO_TCP, ALPROTO_SSH, SshStateSession, STREAM_TOSERVER),
1630  "request_session") != 0);
1631  /* the completion state is out of the id-to-name table: the hook
1632  * listing and the policy key walk must not offer it */
1634  IPPROTO_TCP, ALPROTO_SSH, SshStateDone, STREAM_TOSERVER) != NULL);
1636  IPPROTO_TCP, ALPROTO_SSH, SshStateDone, STREAM_TOCLIENT) != NULL);
1637  FAIL_IF(AppLayerParserGetStateNameById(IPPROTO_TCP, ALPROTO_SSH, 9, STREAM_TOSERVER) != NULL);
1638 
1639  PASS;
1640 }
1641 
1642 /** \test Per-direction session state: the toserver direction reports
1643  * session after its own NewKeys while the toclient direction has not
1644  * seen anything yet. */
1645 static int SSHParserTest27(void)
1646 {
1647  Flow f;
1648  uint8_t sshbuf1[] = "SSH-2.0-MySSHClient-0.5.1\r\n";
1649  uint32_t sshlen1 = sizeof(sshbuf1) - 1;
1650  uint8_t sshbuf2[] = { 0x00, 0x00, 0x00, 0x03, 0x01, 21, 0x00 };
1651  uint32_t sshlen2 = sizeof(sshbuf2);
1652  TcpSession ssn;
1655 
1656  memset(&f, 0, sizeof(f));
1657  memset(&ssn, 0, sizeof(ssn));
1658  FLOW_INITIALIZE(&f);
1659  f.protoctx = (void *)&ssn;
1660  f.proto = IPPROTO_TCP;
1661  f.alproto = ALPROTO_SSH;
1662 
1663  StreamTcpInitConfig(true);
1664 
1665  int r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SSH, STREAM_TOSERVER, sshbuf1, sshlen1);
1666  FAIL_IF(r != 0);
1667  r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SSH, STREAM_TOSERVER, sshbuf2, sshlen2);
1668  FAIL_IF(r != 0);
1669 
1670  void *ssh_state = f.alstate;
1671  FAIL_IF_NULL(ssh_state);
1672  void *tx = SCSshStateGetTx(ssh_state, 0);
1673  FAIL_IF(SCSshTxGetAlStateProgress(tx, STREAM_TOSERVER) != SshStateSession);
1674  FAIL_IF(SCSshTxGetAlStateProgress(tx, STREAM_TOCLIENT) != SshStateBanner);
1675 
1676  FLOW_DESTROY(&f);
1678  StreamTcpFreeConfig(true);
1679  PASS;
1680 }
1681 
1682 /** \test Unrecoverable parse failure freezes the failing direction
1683  * in the state it failed in: an invalid banner leaves it at
1684  * banner, an invalid record at kex. The other direction is
1685  * unaffected, and post-failure data does not advance the failed
1686  * direction (the parse entry short-circuits on the error flag).
1687  * The parse call reports -1. */
1688 static int SSHParserTest28(void)
1689 {
1690  Flow f;
1691  uint8_t badbanner[] = "SSH-bogus\r\n";
1692  uint32_t badbannerlen = sizeof(badbanner) - 1;
1693  uint8_t banner[] = "SSH-2.0-TestClient-1.0\r\n";
1694  uint32_t bannerlen = sizeof(banner) - 1;
1695  uint8_t badrecord[] = { 0x00, 0x00, 0x00, 0x00, 0x08, 0x21, 0x00, 0x00 };
1696  uint32_t badrecordlen = sizeof(badrecord);
1697  TcpSession ssn;
1700 
1701  /* invalid banner */
1702  memset(&f, 0, sizeof(f));
1703  memset(&ssn, 0, sizeof(ssn));
1704  FLOW_INITIALIZE(&f);
1705  f.protoctx = (void *)&ssn;
1706  f.proto = IPPROTO_TCP;
1707  f.alproto = ALPROTO_SSH;
1708 
1709  StreamTcpInitConfig(true);
1710 
1711  int r = AppLayerParserParse(
1712  NULL, alp_tctx, &f, ALPROTO_SSH, STREAM_TOSERVER, badbanner, badbannerlen);
1713  FAIL_IF(r != -1);
1714  void *ssh_state = f.alstate;
1715  FAIL_IF_NULL(ssh_state);
1716  void *tx = SCSshStateGetTx(ssh_state, 0);
1717  FAIL_IF(SCSshTxGetAlStateProgress(tx, STREAM_TOSERVER) != SshStateBanner);
1718  FAIL_IF(SCSshTxGetAlStateProgress(tx, STREAM_TOCLIENT) != SshStateBanner);
1719 
1720  /* the failure event is published data: the tx is marked updated */
1721  struct AppLayerTxData *txdata = AppLayerParserGetTxData(IPPROTO_TCP, ALPROTO_SSH, tx);
1722  FAIL_IF_NULL(txdata);
1723  FAIL_IF(!txdata->updated_ts);
1724 
1725  /* the failed direction is frozen: a valid banner does not advance it */
1726  r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SSH, STREAM_TOSERVER, banner, bannerlen);
1727  FAIL_IF(r != 0);
1728  tx = SCSshStateGetTx(ssh_state, 0);
1729  FAIL_IF(SCSshTxGetAlStateProgress(tx, STREAM_TOSERVER) != SshStateBanner);
1730  FAIL_IF(SCSshTxGetAlStateProgress(tx, STREAM_TOCLIENT) != SshStateBanner);
1731 
1732  FLOW_DESTROY(&f);
1733 
1734  /* valid banner then invalid record (pkt_len=0) */
1735  memset(&f, 0, sizeof(f));
1736  memset(&ssn, 0, sizeof(ssn));
1737  FLOW_INITIALIZE(&f);
1738  f.protoctx = (void *)&ssn;
1739  f.proto = IPPROTO_TCP;
1740  f.alproto = ALPROTO_SSH;
1741 
1742  r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SSH, STREAM_TOSERVER, banner, bannerlen);
1743  FAIL_IF(r != 0);
1744  r = AppLayerParserParse(
1745  NULL, alp_tctx, &f, ALPROTO_SSH, STREAM_TOSERVER, badrecord, badrecordlen);
1746  FAIL_IF(r != -1);
1747  ssh_state = f.alstate;
1748  FAIL_IF_NULL(ssh_state);
1749  tx = SCSshStateGetTx(ssh_state, 0);
1750  FAIL_IF(SCSshTxGetAlStateProgress(tx, STREAM_TOSERVER) != SshStateKex);
1751 
1752  /* the failure event is published data: the tx is marked updated */
1753  txdata = AppLayerParserGetTxData(IPPROTO_TCP, ALPROTO_SSH, tx);
1754  FAIL_IF_NULL(txdata);
1755  FAIL_IF(!txdata->updated_ts);
1756 
1757  /* the failed direction is frozen: a valid newkeys record does not
1758  * advance it to session */
1759  uint8_t newkeys[] = { 0x00, 0x00, 0x00, 0x03, 0x01, 21, 0x00 };
1760  r = AppLayerParserParse(
1761  NULL, alp_tctx, &f, ALPROTO_SSH, STREAM_TOSERVER, newkeys, sizeof(newkeys));
1762  FAIL_IF(r != 0);
1763  tx = SCSshStateGetTx(ssh_state, 0);
1764  FAIL_IF(SCSshTxGetAlStateProgress(tx, STREAM_TOSERVER) != SshStateKex);
1765 
1766  FLOW_DESTROY(&f);
1768  StreamTcpFreeConfig(true);
1769  PASS;
1770 }
1771 
1772 /** \test A record reassembly stash and a header fragment mark the tx
1773  * \test updated at parse entry, like a record completion: the flag
1774  * \test is set at the parse entry, as the base parser did, so the
1775  * \test deliveries are swept by the firewall default policy. A
1776  * \test pkt_len=29 record (27 payload bytes after the 6B header,
1777  * \test msg 50) is fed in 3 calls: 16B (header + 10), 4B (pure
1778  * \test stash), 13B (completion); then a 3B header fragment.
1779  */
1780 static int SSHParserTest29(void)
1781 {
1782  Flow f;
1783  TcpSession ssn;
1785 
1786  uint8_t banner[] = "SSH-2.0-Client-1.0\r\n";
1787  uint8_t seg2[] = { 0x00, 0x00, 0x00, 29, 0x00, 50, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,
1788  0x68, 0x69, 0x6a };
1789  uint8_t seg3[4] = { 0x6b, 0x6c, 0x6d, 0x6e };
1790  uint8_t seg4[13] = { 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a,
1791  0x7b };
1792  uint8_t seg5[3] = { 0x7c, 0x7d, 0x7e };
1793 
1794  void *tx = NULL;
1795  struct AppLayerTxData *txdata = NULL;
1796  int r = 0;
1797 
1798  memset(&f, 0, sizeof(f));
1799  memset(&ssn, 0, sizeof(ssn));
1800  FLOW_INITIALIZE(&f);
1801  f.protoctx = (void *)&ssn;
1802  f.proto = IPPROTO_TCP;
1803  f.alproto = ALPROTO_SSH;
1804 
1805  StreamTcpInitConfig(true);
1808 
1809  r = AppLayerParserParse(
1810  NULL, alp_tctx, &f, ALPROTO_SSH, STREAM_TOSERVER, banner, sizeof(banner) - 1);
1811  FAIL_IF(r != 0);
1813  tx = SCSshStateGetTx(f.alstate, 0);
1814  FAIL_IF(SCSshTxGetAlStateProgress(tx, STREAM_TOSERVER) != SshStateKex);
1815  txdata = AppLayerParserGetTxData(IPPROTO_TCP, ALPROTO_SSH, tx);
1816  FAIL_IF_NULL(txdata);
1817  FAIL_IF(!txdata->updated_ts);
1818 
1819  // record header + 10 payload bytes: frames are published
1820  r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SSH, STREAM_TOSERVER, seg2, sizeof(seg2));
1821  FAIL_IF(r != 0);
1822  FAIL_IF(!txdata->updated_ts);
1823  txdata->updated_ts = false;
1824 
1825  // the 4B chunk is fully held by the reassembly stash: nothing is
1826  // published, but the tx is marked updated at parse entry
1827  r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SSH, STREAM_TOSERVER, seg3, sizeof(seg3));
1828  FAIL_IF(r != 0);
1829  FAIL_IF(!txdata->updated_ts);
1830 
1831  // the final 13B chunk completes the record
1832  r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SSH, STREAM_TOSERVER, seg4, sizeof(seg4));
1833  FAIL_IF(r != 0);
1834  FAIL_IF(!txdata->updated_ts);
1835  txdata->updated_ts = false;
1836 
1837  // 3B header fragment: held for the next delivery, the tx is
1838  // marked updated at parse entry
1839  r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SSH, STREAM_TOSERVER, seg5, sizeof(seg5));
1840  FAIL_IF(r != 1);
1841  FAIL_IF(!txdata->updated_ts);
1842  FAIL_IF(SCSshTxGetAlStateProgress(tx, STREAM_TOSERVER) != SshStateKex);
1843 
1844  FLOW_DESTROY(&f);
1846  StreamTcpFreeConfig(true);
1847  PASS;
1848 }
1849 
1850 /** \test A long banner (>=256B without EOL) keeps the direction in
1851  * \test the banner phase with the line consumed greedily; the line
1852  * \test completion moves it to kex, and a record that then fails
1853  * \test freezes the direction at kex. A >=256B line without EOL that
1854  * \test does not parse as a banner freezes the direction at banner
1855  * \test at once.
1856  */
1857 static int SSHParserTest30(void)
1858 {
1859  Flow f;
1860  TcpSession ssn;
1862  uint8_t longbanner[300];
1863  uint8_t junk[300];
1864  uint8_t badrecord[] = { 0x00, 0x00, 0x00, 0x00, 0x08, 0x21, 0x00, 0x00 };
1865  void *tx;
1866 
1867  memset(&f, 0, sizeof(f));
1868  memset(&ssn, 0, sizeof(ssn));
1869  FLOW_INITIALIZE(&f);
1870  f.protoctx = (void *)&ssn;
1871  f.proto = IPPROTO_TCP;
1872  f.alproto = ALPROTO_SSH;
1873 
1874  StreamTcpInitConfig(true);
1877 
1878  // phase 1: 300B banner without EOL -> the direction stays in the
1879  // banner phase (the line is still open); the line completion
1880  // moves it to kex; the following record is invalid -> the
1881  // direction freezes at kex
1882  memset(longbanner, 'A', sizeof(longbanner));
1883  memcpy(longbanner, "SSH-2.0-", 8);
1884  int r = AppLayerParserParse(
1885  NULL, alp_tctx, &f, ALPROTO_SSH, STREAM_TOSERVER, longbanner, sizeof(longbanner));
1886  FAIL_IF(r != 0);
1888  tx = SCSshStateGetTx(f.alstate, 0);
1889  FAIL_IF(SCSshTxGetAlStateProgress(tx, STREAM_TOSERVER) != SshStateBanner);
1890 
1891  // line completion -> kex
1892  r = AppLayerParserParse(
1893  NULL, alp_tctx, &f, ALPROTO_SSH, STREAM_TOSERVER, (const uint8_t *)"\r\n", 2);
1894  FAIL_IF(r != 0);
1895  FAIL_IF(SCSshTxGetAlStateProgress(tx, STREAM_TOSERVER) != SshStateKex);
1896 
1897  r = AppLayerParserParse(
1898  NULL, alp_tctx, &f, ALPROTO_SSH, STREAM_TOSERVER, badrecord, sizeof(badrecord));
1899  FAIL_IF(r != -1);
1900  FAIL_IF(SCSshTxGetAlStateProgress(tx, STREAM_TOSERVER) != SshStateKex);
1901  FAIL_IF(SCSshTxGetAlStateProgress(tx, STREAM_TOCLIENT) != SshStateBanner);
1902 
1903  // phase 2: 300B of junk without EOL (>= 256, not a valid banner)
1904  // -> frozen at banner at once
1905  FLOW_DESTROY(&f);
1906  memset(&ssn, 0, sizeof(ssn));
1907  memset(&f, 0, sizeof(f));
1908  FLOW_INITIALIZE(&f);
1909  f.protoctx = (void *)&ssn;
1910  f.proto = IPPROTO_TCP;
1911  f.alproto = ALPROTO_SSH;
1912  memset(junk, 'X', sizeof(junk));
1913  r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SSH, STREAM_TOSERVER, junk, sizeof(junk));
1914  FAIL_IF(r != -1);
1916  tx = SCSshStateGetTx(f.alstate, 0);
1917  FAIL_IF(SCSshTxGetAlStateProgress(tx, STREAM_TOSERVER) != SshStateBanner);
1918  FAIL_IF(SCSshTxGetAlStateProgress(tx, STREAM_TOCLIENT) != SshStateBanner);
1919 
1920  FLOW_DESTROY(&f);
1922  StreamTcpFreeConfig(true);
1923  PASS;
1924 }
1925 
1926 /** \test The eve ssh log condition is failure-only: a direction
1927  * \test that fails at banner (invalid banner) satisfies the
1928  * \test condition (the error field makes the failure observable
1929  * \test in eve), while a flow without failure does not - neither
1930  * \test a banner-only flow nor one with both banners parsed
1931  * \test (both directions at kex): the one-shot tx log must not be
1932  * \test consumed mid-flow before a later failure could be
1933  * \test reported, so successful flows are logged at the flow-end
1934  * \test flush. The failure admits the log on the failing
1935  * \test delivery.
1936  */
1937 static int SSHParserTest31(void)
1938 {
1939  Flow f;
1940  uint8_t badbanner[] = "SSH-bogus\r\n";
1941  uint32_t badbannerlen = sizeof(badbanner) - 1;
1942  uint8_t banner[] = "SSH-2.0-TestClient-1.0\r\n";
1943  uint32_t bannerlen = sizeof(banner) - 1;
1944  TcpSession ssn;
1947 
1948  /* invalid banner: the failed direction admits the log condition */
1949  memset(&f, 0, sizeof(f));
1950  memset(&ssn, 0, sizeof(ssn));
1951  FLOW_INITIALIZE(&f);
1952  f.protoctx = (void *)&ssn;
1953  f.proto = IPPROTO_TCP;
1954  f.alproto = ALPROTO_SSH;
1955 
1956  StreamTcpInitConfig(true);
1957 
1958  int r = AppLayerParserParse(
1959  NULL, alp_tctx, &f, ALPROTO_SSH, STREAM_TOSERVER, badbanner, badbannerlen);
1960  FAIL_IF(r != -1);
1961  void *ssh_state = f.alstate;
1962  FAIL_IF_NULL(ssh_state);
1963  void *tx = SCSshStateGetTx(ssh_state, 0);
1964  FAIL_IF(SCSshTxGetAlStateProgress(tx, STREAM_TOSERVER) != SshStateBanner);
1965  FAIL_IF(SCSshTxGetLogCondition(tx) != true);
1966 
1967  FLOW_DESTROY(&f);
1968 
1969  /* no failure yet (client banner only): the condition stays
1970  * false - the one-shot tx log is reserved for the failure
1971  * or the next EOF-flush delivery */
1972  memset(&f, 0, sizeof(f));
1973  memset(&ssn, 0, sizeof(ssn));
1974  FLOW_INITIALIZE(&f);
1975  f.protoctx = (void *)&ssn;
1976  f.proto = IPPROTO_TCP;
1977  f.alproto = ALPROTO_SSH;
1978 
1979  r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SSH, STREAM_TOSERVER, banner, bannerlen);
1980  FAIL_IF(r != 0);
1981  ssh_state = f.alstate;
1982  FAIL_IF_NULL(ssh_state);
1983  tx = SCSshStateGetTx(ssh_state, 0);
1984  FAIL_IF(SCSshTxGetLogCondition(tx) != false);
1985 
1986  FLOW_DESTROY(&f);
1987 
1988  /* both banners parsed (both directions at kex), still no
1989  * failure: the condition must stay false - a mid-flow success
1990  * log would consume the one-shot tx log before a later failure
1991  * could be reported */
1992  memset(&f, 0, sizeof(f));
1993  memset(&ssn, 0, sizeof(ssn));
1994  FLOW_INITIALIZE(&f);
1995  f.protoctx = (void *)&ssn;
1996  f.proto = IPPROTO_TCP;
1997  f.alproto = ALPROTO_SSH;
1998 
1999  r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SSH, STREAM_TOSERVER, banner, bannerlen);
2000  FAIL_IF(r != 0);
2001  r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SSH, STREAM_TOCLIENT, banner, bannerlen);
2002  FAIL_IF(r != 0);
2003  ssh_state = f.alstate;
2004  FAIL_IF_NULL(ssh_state);
2005  tx = SCSshStateGetTx(ssh_state, 0);
2006  FAIL_IF(SCSshTxGetLogCondition(tx) != false);
2007 
2008  /* the failure admits the log on the failing delivery */
2009  uint8_t badrecord[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
2010  r = AppLayerParserParse(
2011  NULL, alp_tctx, &f, ALPROTO_SSH, STREAM_TOCLIENT, badrecord, sizeof(badrecord));
2012  FAIL_IF(r != -1);
2013  FAIL_IF(SCSshTxGetLogCondition(tx) != true);
2014 
2015  FLOW_DESTROY(&f);
2017  StreamTcpFreeConfig(true);
2018  PASS;
2019 }
2020 
2021 /** \test A banner line split over two segments: the open-line segment
2022  * \test publishes nothing but still marks the tx updated at parse
2023  * \test entry; the line completion publishes the banner and takes
2024  * \test the direction to kex. */
2025 static int SSHParserTest32(void)
2026 {
2027  TcpReassemblyThreadCtx *ra_ctx = NULL;
2028  ThreadVars tv;
2029  TcpSession ssn;
2030  Flow *f = NULL;
2031  Packet *p = NULL;
2032 
2033  uint8_t part1[] = "SSH-2.0-TestClient-1.0";
2034  uint8_t part2[] = "\r\n";
2035  uint32_t part1len = sizeof(part1) - 1;
2036  uint32_t part2len = sizeof(part2) - 1;
2037 
2038  memset(&tv, 0x00, sizeof(tv));
2039 
2040  StreamTcpUTInit(&ra_ctx);
2045 
2046  f = UTHBuildFlow(AF_INET, "1.1.1.1", "2.2.2.2", 1234, 2222);
2047  FAIL_IF_NULL(f);
2048  f->protoctx = &ssn;
2049  f->proto = IPPROTO_TCP;
2050  f->alproto = ALPROTO_SSH;
2051 
2052  p = PacketGetFromAlloc();
2053  FAIL_IF_NULL(p);
2054  p->proto = IPPROTO_TCP;
2055  p->flow = f;
2056 
2057  uint32_t seqcli = 2;
2058  // segment 1: the open banner line; the parse consumes nothing and
2059  // publishes nothing, but the tx is marked updated at parse entry
2060  FAIL_IF(StreamTcpUTAddSegmentWithPayload(&tv, ra_ctx, &ssn.client, seqcli, part1, part1len) ==
2061  -1);
2062  seqcli += part1len;
2064 
2065  void *ssh_state = f->alstate;
2066  FAIL_IF_NULL(ssh_state);
2067  void *tx = SCSshStateGetTx(ssh_state, 0);
2068  struct AppLayerTxData *txdata = AppLayerParserGetTxData(IPPROTO_TCP, ALPROTO_SSH, tx);
2069  FAIL_IF_NULL(txdata);
2070  FAIL_IF(!txdata->updated_ts);
2071 
2072  // segment 2: the end-of-line completes the line; the banner
2073  // publishes and the direction advances to kex
2074  FAIL_IF(StreamTcpUTAddSegmentWithPayload(&tv, ra_ctx, &ssn.client, seqcli, part2, part2len) ==
2075  -1);
2076  seqcli += part2len;
2078 
2079  FAIL_IF(SCSshTxGetAlStateProgress(tx, STREAM_TOSERVER) != SshStateKex);
2080  FAIL_IF(!txdata->updated_ts);
2081 
2082  UTHFreePacket(p);
2083  UTHFreeFlow(f);
2085  StreamTcpUTDeinit(ra_ctx);
2086  PASS;
2087 }
2088 
2089 /** \test NewKeys while the peer direction failed: the peer never
2090  * \test actually switched keys, so it is not terminal for the
2091  * \test no-inspection decision (encryption bypass stays armed-able
2092  * \test only when the peer's own NewKeys was observed). */
2093 static int SSHParserTest33(void)
2094 {
2095  Flow f;
2096  uint8_t badbanner[] = "SSH-bogus\r\n";
2097  uint32_t badbannerlen = sizeof(badbanner) - 1;
2098  uint8_t banner[] = "SSH-2.0-TestClient-1.0\r\n";
2099  uint32_t bannerlen = sizeof(banner) - 1;
2100  uint8_t newkeys[] = { 0x00, 0x00, 0x00, 0x03, 0x01, 21, 0x00 };
2101  uint32_t newkeyslen = sizeof(newkeys);
2102  TcpSession ssn;
2105 
2106  memset(&f, 0, sizeof(f));
2107  memset(&ssn, 0, sizeof(ssn));
2108  FLOW_INITIALIZE(&f);
2109  f.protoctx = (void *)&ssn;
2110  f.proto = IPPROTO_TCP;
2111  f.alproto = ALPROTO_SSH;
2112 
2113  StreamTcpInitConfig(true);
2114 
2115  int r = AppLayerParserParse(
2116  NULL, alp_tctx, &f, ALPROTO_SSH, STREAM_TOSERVER, badbanner, badbannerlen);
2117  FAIL_IF(r != -1);
2118  r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SSH, STREAM_TOCLIENT, banner, bannerlen);
2119  FAIL_IF(r != 0);
2120  r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SSH, STREAM_TOCLIENT, newkeys, newkeyslen);
2121  FAIL_IF(r != 0);
2122 
2123  void *ssh_state = f.alstate;
2124  FAIL_IF_NULL(ssh_state);
2125  void *tx = SCSshStateGetTx(ssh_state, 0);
2126  FAIL_IF(SCSshTxGetFlags(tx, STREAM_TOSERVER) != SshStateBanner);
2127  FAIL_IF(SCSshTxGetFlags(tx, STREAM_TOCLIENT) != SshStateSession);
2128  FAIL_IF(SCAppLayerParserStateIssetFlag(f.alparser, APP_LAYER_PARSER_NO_INSPECTION));
2129 
2130  FLOW_DESTROY(&f);
2132  StreamTcpFreeConfig(true);
2133  PASS;
2134 }
2135 
2136 /** \test a parse failure ends app-layer parsing for the whole flow
2137  * \test (engine path): the failure site returns a parse error, so
2138  * \test StreamTcpDisableAppLayer runs and no further data in either
2139  * \test direction reaches the parser - mid-flow or at the flow-end
2140  * \test flush (the disable flag is checked at the reassembly
2141  * \test entry, so even pre-failure buffered bytes are not
2142  * \test delivered at EOF); the failing direction's state stays
2143  * \test where the error occurred, readable for the progress
2144  * \test accessor, and the failure event marks the tx updated. */
2145 static int SSHParserTest34(void)
2146 {
2147  TcpReassemblyThreadCtx *ra_ctx = NULL;
2148  ThreadVars tv;
2149  TcpSession ssn;
2150  Flow *f = NULL;
2151  Packet *p = NULL;
2152 
2153  uint8_t cli_banner[] = "SSH-2.0-TestClient-1.0\r\n";
2154  uint32_t cli_bannerlen = sizeof(cli_banner) - 1;
2155  uint8_t srv_banner[] = "SSH-2.0-TestServer-1.0\r\n";
2156  uint32_t srv_bannerlen = sizeof(srv_banner) - 1;
2157  uint8_t badrecord[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
2158  uint32_t badrecordlen = sizeof(badrecord);
2159  uint8_t newkeys[] = { 0x00, 0x00, 0x00, 0x03, 0x01, 21, 0x00 };
2160  uint32_t newkeyslen = sizeof(newkeys);
2161 
2162  memset(&tv, 0x00, sizeof(tv));
2163 
2164  StreamTcpUTInit(&ra_ctx);
2169 
2170  f = UTHBuildFlow(AF_INET, "1.1.1.1", "2.2.2.2", 1234, 2222);
2171  FAIL_IF_NULL(f);
2172  f->protoctx = &ssn;
2173  f->proto = IPPROTO_TCP;
2174  f->alproto = ALPROTO_SSH;
2175 
2176  p = PacketGetFromAlloc();
2177  FAIL_IF_NULL(p);
2178  p->proto = IPPROTO_TCP;
2179  p->flow = f;
2180 
2181  // the server banner is buffered, not yet delivered to the parser
2182  uint32_t seqsrv = 2;
2184  &tv, ra_ctx, &ssn.server, seqsrv, srv_banner, srv_bannerlen) == -1);
2185  seqsrv += srv_bannerlen;
2186 
2187  uint32_t seqcli = 2;
2188  // the client banner is delivered and parsed: the direction
2189  // advances to kex
2191  &tv, ra_ctx, &ssn.client, seqcli, cli_banner, cli_bannerlen) == -1);
2192  seqcli += cli_bannerlen;
2194 
2195  void *ssh_state = f->alstate;
2196  FAIL_IF_NULL(ssh_state);
2197  void *tx = SCSshStateGetTx(ssh_state, 0);
2198  FAIL_IF(SCSshTxGetAlStateProgress(tx, STREAM_TOSERVER) != SshStateKex);
2199 
2200  // an invalid record in the client direction: the engine ends
2201  // app-layer parsing for the whole flow
2203  &tv, ra_ctx, &ssn.client, seqcli, badrecord, badrecordlen) == -1);
2204  seqcli += badrecordlen;
2206 
2207  // the engine ended app-layer parsing for the whole flow
2209  // the failing direction is frozen at kex, still readable
2210  FAIL_IF(SCSshTxGetAlStateProgress(tx, STREAM_TOSERVER) != SshStateKex);
2211  // the failure event published: the tx is marked updated, so the
2212  // engine evaluates it (not only at the next EOF-flush delivery)
2213  struct AppLayerTxData *txdata = AppLayerParserGetTxData(IPPROTO_TCP, ALPROTO_SSH, tx);
2214  FAIL_IF_NULL(txdata);
2215  FAIL_IF(!txdata->updated_ts);
2216 
2217  // further data in either direction does not reach the parser -
2218  // neither directly nor via the next EOF-flush delivery: the disable flag
2219  // is checked at the reassembly entry, so the buffered
2220  // pre-failure server banner is not delivered at EOF
2223  &tv, ra_ctx, &ssn.client, seqcli, newkeys, newkeyslen) == -1);
2224  seqcli += newkeyslen;
2227 
2228  FAIL_IF(SCSshTxGetAlStateProgress(tx, STREAM_TOSERVER) != SshStateKex);
2229  FAIL_IF(SCSshTxGetAlStateProgress(tx, STREAM_TOCLIENT) != SshStateBanner);
2230 
2231  UTHFreePacket(p);
2232  UTHFreeFlow(f);
2234  StreamTcpUTDeinit(ra_ctx);
2235  PASS;
2236 }
2237 
2238 /** \test a NewKeys record split across two parser calls (segment
2239  * \test boundary after the 6-byte record header) arms the
2240  * \test no-inspection/bypass flags like a complete record: the
2241  * \test arming runs on the session transition itself, shared by
2242  * \test the complete-record and incomplete-header arms. The peer
2243  * \test direction completes its NewKeys record in one call first;
2244  * \test the split direction then delivers the record header (the
2245  * \test body byte is stashed) and the arming must happen on that
2246  * \test delivery. Also pins BYPASS_READY in bypass mode.
2247  */
2248 static int SSHParserTest35(void)
2249 {
2250  Flow f;
2251  uint8_t banner[] = "SSH-2.0-TestClient-1.0\r\n";
2252  uint32_t bannerlen = sizeof(banner) - 1;
2253  uint8_t newkeys[] = { 0x00, 0x00, 0x00, 0x03, 0x01, 21, 0x00 };
2254  uint8_t newkeys_hdr[6] = { 0x00, 0x00, 0x00, 0x03, 0x01, 21 };
2255  uint8_t newkeys_body[1] = { 0x00 };
2256  TcpSession ssn;
2259 
2260  memset(&f, 0, sizeof(f));
2261  memset(&ssn, 0, sizeof(ssn));
2262  FLOW_INITIALIZE(&f);
2263  f.protoctx = (void *)&ssn;
2264  f.proto = IPPROTO_TCP;
2265  f.alproto = ALPROTO_SSH;
2266 
2267  StreamTcpInitConfig(true);
2268 
2269  int r = AppLayerParserParse(
2270  NULL, alp_tctx, &f, ALPROTO_SSH, STREAM_TOSERVER, banner, bannerlen);
2271  FAIL_IF(r != 0);
2272  r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SSH, STREAM_TOCLIENT, banner, bannerlen);
2273  FAIL_IF(r != 0);
2274 
2275  /* peer direction: complete NewKeys record in one call -> session */
2276  r = AppLayerParserParse(
2277  NULL, alp_tctx, &f, ALPROTO_SSH, STREAM_TOCLIENT, newkeys, sizeof(newkeys));
2278  FAIL_IF(r != 0);
2279  void *ssh_state = f.alstate;
2280  FAIL_IF_NULL(ssh_state);
2281  void *tx = SCSshStateGetTx(ssh_state, 0);
2282  FAIL_IF(SCSshTxGetFlags(tx, STREAM_TOCLIENT) != SshStateSession);
2283 
2284  /* split direction: the record header only (the body byte is
2285  * stashed and arrives in the next call) */
2286  r = AppLayerParserParse(
2287  NULL, alp_tctx, &f, ALPROTO_SSH, STREAM_TOSERVER, newkeys_hdr, sizeof(newkeys_hdr));
2288  FAIL_IF(r != 0);
2289  tx = SCSshStateGetTx(ssh_state, 0);
2290  FAIL_IF(SCSshTxGetFlags(tx, STREAM_TOSERVER) != SshStateSession);
2291  /* the session transition armed the flags despite the split */
2292  FAIL_IF(!SCAppLayerParserStateIssetFlag(f.alparser, APP_LAYER_PARSER_NO_INSPECTION));
2293 
2294  /* the stashed body byte completes the record */
2295  r = AppLayerParserParse(
2296  NULL, alp_tctx, &f, ALPROTO_SSH, STREAM_TOSERVER, newkeys_body, sizeof(newkeys_body));
2297  FAIL_IF(r != 0);
2298  FAIL_IF(!SCAppLayerParserStateIssetFlag(f.alparser, APP_LAYER_PARSER_NO_INSPECTION));
2299 
2300  FLOW_DESTROY(&f);
2301 
2302  /* bypass mode: the transition also arms BYPASS_READY */
2303  SCSshEnableBypass(ENCRYPTION_HANDLING_BYPASS);
2304  memset(&f, 0, sizeof(f));
2305  memset(&ssn, 0, sizeof(ssn));
2306  FLOW_INITIALIZE(&f);
2307  f.protoctx = (void *)&ssn;
2308  f.proto = IPPROTO_TCP;
2309  f.alproto = ALPROTO_SSH;
2310 
2311  r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SSH, STREAM_TOSERVER, banner, bannerlen);
2312  FAIL_IF(r != 0);
2313  r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SSH, STREAM_TOCLIENT, banner, bannerlen);
2314  FAIL_IF(r != 0);
2315  r = AppLayerParserParse(
2316  NULL, alp_tctx, &f, ALPROTO_SSH, STREAM_TOCLIENT, newkeys, sizeof(newkeys));
2317  FAIL_IF(r != 0);
2318  r = AppLayerParserParse(
2319  NULL, alp_tctx, &f, ALPROTO_SSH, STREAM_TOSERVER, newkeys_hdr, sizeof(newkeys_hdr));
2320  FAIL_IF(r != 0);
2321  FAIL_IF(!SCAppLayerParserStateIssetFlag(f.alparser, APP_LAYER_PARSER_BYPASS_READY));
2322  SCSshEnableBypass(ENCRYPTION_HANDLING_TRACK_ONLY);
2323 
2324  FLOW_DESTROY(&f);
2326  StreamTcpFreeConfig(true);
2327  PASS;
2328 }
2329 
2330 #endif /* UNITTESTS */
2331 
2333 {
2334 #ifdef UNITTESTS
2335  UtRegisterTest("SSHParserTest01 - ToServer", SSHParserTest01);
2336  UtRegisterTest("SSHParserTest02 - ToServer", SSHParserTest02);
2337  UtRegisterTest("SSHParserTest03 - ToServer", SSHParserTest03);
2338  UtRegisterTest("SSHParserTest04 - ToClient", SSHParserTest04);
2339  UtRegisterTest("SSHParserTest05 - ToClient", SSHParserTest05);
2340  UtRegisterTest("SSHParserTest06 - ToClient", SSHParserTest06);
2341  UtRegisterTest("SSHParserTest07 - ToServer 2 chunks", SSHParserTest07);
2342  UtRegisterTest("SSHParserTest08 - ToServer 3 chunks", SSHParserTest08);
2343  UtRegisterTest("SSHParserTest09 - ToClient 2 chunks", SSHParserTest09);
2344  UtRegisterTest("SSHParserTest10 - ToClient 3 chunks", SSHParserTest10);
2345  UtRegisterTest("SSHParserTest11 - ToClient 4 chunks", SSHParserTest11);
2346  UtRegisterTest("SSHParserTest12 - ToClient 4 chunks", SSHParserTest12);
2347  UtRegisterTest("SSHParserTest13 - ToClient 4 chunks", SSHParserTest13);
2348  UtRegisterTest("SSHParserTest14 - ToClient 4 chunks", SSHParserTest14);
2349  UtRegisterTest("SSHParserTest15", SSHParserTest15);
2350  UtRegisterTest("SSHParserTest16", SSHParserTest16);
2351  UtRegisterTest("SSHParserTest17", SSHParserTest17);
2352  UtRegisterTest("SSHParserTest18", SSHParserTest18);
2353  UtRegisterTest("SSHParserTest19", SSHParserTest19);
2354  UtRegisterTest("SSHParserTest20", SSHParserTest20);
2355  UtRegisterTest("SSHParserTest21", SSHParserTest21);
2356  UtRegisterTest("SSHParserTest22", SSHParserTest22);
2357  UtRegisterTest("SSHParserTest23", SSHParserTest23);
2358  UtRegisterTest("SSHParserTest24", SSHParserTest24);
2359  UtRegisterTest("SSHParserTest25", SSHParserTest25);
2360  UtRegisterTest("SSHParserTest26 - State name table", SSHParserTest26);
2361  UtRegisterTest("SSHParserTest27 - Per-direction session state", SSHParserTest27);
2362  UtRegisterTest("SSHParserTest28 - failure freezes the failing direction at its state",
2363  SSHParserTest28);
2365  "SSHParserTest29 - stash and fragment deliveries mark the tx updated", SSHParserTest29);
2367  "SSHParserTest30 - long-banner continuation failure freezes at kex", SSHParserTest30);
2368  UtRegisterTest("SSHParserTest31 - failed flow admits the eve log condition", SSHParserTest31);
2369  UtRegisterTest("SSHParserTest32 - banner continuation marks the tx updated at parse entry",
2370  SSHParserTest32);
2371  UtRegisterTest("SSHParserTest33 - failed peer does not arm no-inspection", SSHParserTest33);
2372  UtRegisterTest("SSHParserTest34 - failure ends the flow's app-layer parsing (engine path)",
2373  SSHParserTest34);
2375  "SSHParserTest35 - split NewKeys arms the no-inspection/bypass flags", SSHParserTest35);
2376 #endif /* UNITTESTS */
2377 }
2378 
UPDATE_DIR_PACKET
@ UPDATE_DIR_PACKET
Definition: stream-tcp-reassemble.h:56
Packet_::proto
uint8_t proto
Definition: decode.h:538
SCAppLayerParserStateIssetFlag
uint16_t SCAppLayerParserStateIssetFlag(AppLayerParserState *pstate, uint16_t flag)
Definition: app-layer-parser.c:2079
SCConfValIsTrue
int SCConfValIsTrue(const char *val)
Check if a value is true.
Definition: conf.c:577
StreamTcpUTDeinit
void StreamTcpUTDeinit(TcpReassemblyThreadCtx *ra_ctx)
Definition: stream-tcp-util.c:50
FAIL_IF_NULL
#define FAIL_IF_NULL(expr)
Fail a test if expression evaluates to NULL.
Definition: util-unittest.h:89
app-layer-ssh.h
Flow_::flags
uint64_t flags
Definition: flow.h:408
AppLayerParserGetStateNameById
const char * AppLayerParserGetStateNameById(uint8_t ipproto, AppProto alproto, const int id, const uint8_t direction)
Definition: app-layer-parser.c:1847
flow-util.h
stream-tcp.h
unlikely
#define unlikely(expr)
Definition: util-optimize.h:35
UtRegisterTest
void UtRegisterTest(const char *name, int(*TestFn)(void))
Register unit test.
Definition: util-unittest.c:101
RegisterSSHParsers
void RegisterSSHParsers(void)
Function to register the SSH protocol parsers and other functions.
Definition: app-layer-ssh.c:86
SCLogDebug
#define SCLogDebug(...)
Definition: util-debug.h:282
seq
uint32_t seq
Definition: stream-tcp-private.h:2
Flow_::proto
uint8_t proto
Definition: flow.h:381
SCAppLayerProtoDetectPMRegisterPatternCI
int SCAppLayerProtoDetectPMRegisterPatternCI(uint8_t ipproto, AppProto alproto, const char *pattern, uint16_t depth, uint16_t offset, uint8_t direction)
Registers a case-insensitive pattern for protocol detection.
Definition: app-layer-detect-proto.c:1660
Packet_::flags
uint32_t flags
Definition: decode.h:562
ssn
TcpSession ssn
Definition: fuzz_dataset.c:31
Flow_
Flow data structure.
Definition: flow.h:359
StreamTcpReassembleAppLayer
int StreamTcpReassembleAppLayer(ThreadVars *tv, TcpReassemblyThreadCtx *ra_ctx, TcpSession *ssn, TcpStream *stream, Packet *p, enum StreamUpdateDir app_update_dir)
Update the stream reassembly upon receiving a packet.
Definition: stream-tcp-reassemble.c:1395
AppLayerParserGetStateIdByName
int AppLayerParserGetStateIdByName(uint8_t ipproto, AppProto alproto, const char *name, const uint8_t direction)
Definition: app-layer-parser.c:1832
SSHTxLogCondition
bool SSHTxLogCondition(ThreadVars *tv, const Packet *p, void *state, void *tx, uint64_t tx_id)
Definition: app-layer-ssh.c:64
AppLayerParserThreadCtxFree
void AppLayerParserThreadCtxFree(AppLayerParserThreadCtx *tctx)
Destroys the app layer parser thread context obtained using AppLayerParserThreadCtxAlloc().
Definition: app-layer-parser.c:364
rust.h
SCConfValIsFalse
int SCConfValIsFalse(const char *val)
Check if a value is false.
Definition: conf.c:602
stream-tcp-reassemble.h
FLOW_ACTION_DROP
#define FLOW_ACTION_DROP
Definition: flow.h:69
p
Packet * p
Definition: fuzz_dataset.c:30
ALPROTO_SSH
@ ALPROTO_SSH
Definition: app-layer-protos.h:40
Flow_::protoctx
void * protoctx
Definition: flow.h:438
StreamTcpUTInitInline
void StreamTcpUTInitInline(void)
Definition: stream-tcp-util.c:58
util-unittest-helper.h
FAIL_IF_NOT
#define FAIL_IF_NOT(expr)
Fail a test if expression evaluates to false.
Definition: util-unittest.h:82
util-memcmp.h
SCAppLayerProtoDetectConfProtoDetectionEnabled
int SCAppLayerProtoDetectConfProtoDetectionEnabled(const char *ipproto, const char *alproto)
Given a protocol name, checks if proto detection is enabled in the conf file.
Definition: app-layer-detect-proto.c:1989
Flow_::alparser
AppLayerParserState * alparser
Definition: flow.h:483
StreamTcpUTInit
void StreamTcpUTInit(TcpReassemblyThreadCtx **ra_ctx)
Definition: stream-tcp-util.c:43
app-layer-detect-proto.h
StreamTcpInitConfig
void StreamTcpInitConfig(bool)
To initialize the stream global configuration data.
Definition: stream-tcp.c:496
UTHBuildFlow
Flow * UTHBuildFlow(int family, const char *src, const char *dst, Port sp, Port dp)
Definition: util-unittest-helper.c:494
FLOW_INITIALIZE
#define FLOW_INITIALIZE(f)
Definition: flow-util.h:38
decode.h
util-debug.h
StreamTcpAppLayerIsDisabled
int StreamTcpAppLayerIsDisabled(Flow *f)
Definition: stream-tcp-reassemble.c:470
PASS
#define PASS
Pass the test.
Definition: util-unittest.h:105
AppLayerTxData
Definition: app-layer-parser.h:172
protocol
uint16_t protocol
Definition: decode-etag.h:4
PKT_PSEUDO_STREAM_END
#define PKT_PSEUDO_STREAM_END
Definition: decode.h:1313
alp_tctx
AppLayerParserThreadCtx * alp_tctx
Definition: fuzz_applayerparserparse.c:24
SCConfGetNonNull
int SCConfGetNonNull(const char *name, const char **vptr)
Retrieve the non-null value of a configuration node.
Definition: conf.c:380
ThreadVars_
Per thread variable structure.
Definition: threadvars.h:57
StreamTcpUTSetupStream
void StreamTcpUTSetupStream(TcpStream *s, uint32_t isn)
Definition: stream-tcp-util.c:79
app-layer-parser.h
AppLayerParserRegisterProtocolUnittests
void AppLayerParserRegisterProtocolUnittests(uint8_t ipproto, AppProto alproto, void(*RegisterUnittests)(void))
Definition: app-layer-parser.c:2090
Packet_
Definition: decode.h:516
stream-tcp-private.h
conf.h
AppLayerProtoDetectRegisterProtocol
void AppLayerProtoDetectRegisterProtocol(AppProto alproto, const char *alproto_name)
Registers a protocol for protocol detection phase.
Definition: app-layer-detect-proto.c:1769
RunmodeIsUnittests
int RunmodeIsUnittests(void)
Definition: suricata.c:293
UTHFreeFlow
void UTHFreeFlow(Flow *flow)
Definition: util-unittest-helper.c:499
AppLayerParserThreadCtxAlloc
AppLayerParserThreadCtx * AppLayerParserThreadCtxAlloc(void)
Gets a new app layer protocol's parser thread context.
Definition: app-layer-parser.c:337
StreamTcpUTClearSession
void StreamTcpUTClearSession(TcpSession *ssn)
Definition: stream-tcp-util.c:71
Packet_::flow
struct Flow_ * flow
Definition: decode.h:564
FAIL_IF
#define FAIL_IF(expr)
Fail a test if expression evaluates to true.
Definition: util-unittest.h:71
SSH_CONFIG_DEFAULT_ENCRYPTION_BYPASS
#define SSH_CONFIG_DEFAULT_ENCRYPTION_BYPASS
Definition: app-layer-ssh.c:49
StreamTcpFreeConfig
void StreamTcpFreeConfig(bool quiet)
Definition: stream-tcp.c:864
flags
uint8_t flags
Definition: decode-gre.h:0
AppLayerParserParse
int AppLayerParserParse(ThreadVars *tv, AppLayerParserThreadCtx *alp_tctx, Flow *f, AppProto alproto, uint8_t flags, const uint8_t *input, uint32_t input_len)
Definition: app-layer-parser.c:1528
suricata-common.h
AppLayerParserGetTxData
AppLayerTxData * AppLayerParserGetTxData(uint8_t ipproto, AppProto alproto, void *tx)
Definition: app-layer-parser.c:1420
TcpSession_::client
TcpStream client
Definition: stream-tcp-private.h:297
tv
ThreadVars * tv
Definition: fuzz_decodepcapfile.c:34
StreamTcpUTAddSegmentWithPayload
int StreamTcpUTAddSegmentWithPayload(ThreadVars *tv, TcpReassemblyThreadCtx *ra_ctx, TcpStream *stream, uint32_t seq, uint8_t *payload, uint16_t len)
Definition: stream-tcp-util.c:113
PacketGetFromAlloc
Packet * PacketGetFromAlloc(void)
Get a malloced packet.
Definition: decode.c:260
SCLogConfig
struct SCLogConfig_ SCLogConfig
Holds the config state used by the logging api.
StreamTcpUTSetupSession
void StreamTcpUTSetupSession(TcpSession *ssn)
Definition: stream-tcp-util.c:62
TcpSession_::server
TcpStream server
Definition: stream-tcp-private.h:296
SCConfGetNode
SCConfNode * SCConfGetNode(const char *name)
Get a SCConfNode by name.
Definition: conf.c:183
UTHFreePacket
void UTHFreePacket(Packet *p)
UTHFreePacket: function to release the allocated data from UTHBuildPacket and the packet itself.
Definition: util-unittest-helper.c:470
Flow_::alstate
void * alstate
Definition: flow.h:484
MAX_SSH_TEST_SIZE
#define MAX_SSH_TEST_SIZE
Definition: app-layer-ssh.c:488
stream-tcp-util.h
TcpReassemblyThreadCtx_
Definition: stream-tcp-reassemble.h:61
app-layer-protos.h
EngineModeIsIPS
int EngineModeIsIPS(void)
Definition: suricata.c:247
suricata.h
SSHParserRegisterTests
void SSHParserRegisterTests(void)
Definition: app-layer-ssh.c:2332
AppLayerParserThreadCtx_
Definition: app-layer-parser.c:72
TcpSession_
Definition: stream-tcp-private.h:283
Flow_::alproto
AppProto alproto
application level protocol
Definition: flow.h:455
SCConfNode_
Definition: conf.h:37
SCConfNode_::val
char * val
Definition: conf.h:39
FLOW_DESTROY
#define FLOW_DESTROY(f)
Definition: flow-util.h:119
SSH_CONFIG_DEFAULT_HASSH
#define SSH_CONFIG_DEFAULT_HASSH
Definition: app-layer-ssh.c:47
AppLayerTxData::updated_ts
bool updated_ts
Definition: app-layer-parser.h:180
f
Flow f
Definition: fuzz_dataset.c:32