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 "decode.h"
30#include "threads.h"
31
32#include "util-print.h"
33#include "util-pool.h"
34
35#include "stream-tcp-private.h"
37#include "stream-tcp.h"
38#include "stream.h"
39
41#include "app-layer-protos.h"
42#include "app-layer-parser.h"
43#include "app-layer-ssh.h"
44#include "rust.h"
45
46#include "conf.h"
47
48#include "util-spm.h"
49#include "util-unittest.h"
50#include "util-debug.h"
51#include "flow-private.h"
52
53#include "util-byte.h"
54#include "util-memcmp.h"
55
56/* HASSH fingerprints are disabled by default */
57#define SSH_CONFIG_DEFAULT_HASSH false
58/* Bypassing the encrypted part of the connections */
59#define SSH_CONFIG_DEFAULT_ENCRYPTION_BYPASS SSH_HANDLE_ENCRYPTION_TRACK_ONLY
60
61static int SSHRegisterPatternsForProtocolDetection(void)
62{
64 IPPROTO_TCP, ALPROTO_SSH, "SSH-", 4, 0, STREAM_TOSERVER) < 0) {
65 return -1;
66 }
68 IPPROTO_TCP, ALPROTO_SSH, "SSH-", 4, 0, STREAM_TOCLIENT) < 0) {
69 return -1;
70 }
71 return 0;
72}
73
74bool SSHTxLogCondition(ThreadVars *tv, const Packet *p, void *state, void *tx, uint64_t tx_id)
75{
76 return SCSshTxGetLogCondition(tx);
77}
78
79/** \brief Function to register the SSH protocol parsers and other functions
80 */
82{
83 const char *proto_name = "ssh";
84
87 if (SSHRegisterPatternsForProtocolDetection() < 0)
88 return;
89
90 /* Check if we should generate Hassh fingerprints */
91 int enable_hassh = SSH_CONFIG_DEFAULT_HASSH;
92 const char *strval = NULL;
93 if (SCConfGet("app-layer.protocols.ssh.hassh", &strval) != 1) {
94 enable_hassh = SSH_CONFIG_DEFAULT_HASSH;
95 } else if (strcmp(strval, "auto") == 0) {
96 enable_hassh = SSH_CONFIG_DEFAULT_HASSH;
97 } else if (SCConfValIsFalse(strval)) {
98 enable_hassh = SSH_CONFIG_DEFAULT_HASSH;
99 } else if (SCConfValIsTrue(strval)) {
100 enable_hassh = true;
101 }
102
103 if (RunmodeIsUnittests() || enable_hassh) {
104 SCSshEnableHassh();
105 }
106
107 SshEncryptionHandling encryption_bypass = SSH_CONFIG_DEFAULT_ENCRYPTION_BYPASS;
108 SCConfNode *encryption_node = SCConfGetNode("app-layer.protocols.ssh.encryption-handling");
109 if (encryption_node != NULL && encryption_node->val != NULL) {
110 if (strcmp(encryption_node->val, "full") == 0) {
111 encryption_bypass = SSH_HANDLE_ENCRYPTION_FULL;
112 } else if (strcmp(encryption_node->val, "track-only") == 0) {
113 encryption_bypass = SSH_HANDLE_ENCRYPTION_TRACK_ONLY;
114 } else if (strcmp(encryption_node->val, "bypass") == 0) {
115 encryption_bypass = SSH_HANDLE_ENCRYPTION_BYPASS;
116 } else {
117 encryption_bypass = SSH_CONFIG_DEFAULT_ENCRYPTION_BYPASS;
118 }
119 }
120
121 if (encryption_bypass) {
122 SCLogConfig("ssh: bypass on the start of encryption enabled");
123 SCSshEnableBypass(encryption_bypass);
124 }
125 }
126
127 SCLogDebug("Registering Rust SSH parser.");
128 SCRegisterSshParser();
129
130#ifdef UNITTESTS
132#endif
133}
134
135/* UNITTESTS */
136#ifdef UNITTESTS
137#include "flow-util.h"
138#include "stream-tcp-util.h"
139#include "util-unittest-helper.h"
140
141static int SSHParserTestUtilCheck(const char *protoexp, const char *softexp, void *tx, uint8_t flags) {
142 const uint8_t *protocol = NULL;
143 uint32_t p_len = 0;
144 const uint8_t *software = NULL;
145 uint32_t s_len = 0;
146
147 if (SCSshTxGetProtocol(tx, &protocol, &p_len, flags) != 1) {
148 printf("Version string not parsed correctly return: ");
149 return 1;
150 }
151 if (protocol == NULL) {
152 printf("Version string not parsed correctly NULL: ");
153 return 1;
154 }
155
156 if (p_len != strlen(protoexp)) {
157 printf("Version string not parsed correctly length: ");
158 return 1;
159 }
160 if (memcmp(protocol, protoexp, strlen(protoexp)) != 0) {
161 printf("Version string not parsed correctly: ");
162 return 1;
163 }
164
165 if (softexp != NULL) {
166 if (SCSshTxGetSoftware(tx, &software, &s_len, flags) != 1)
167 return 1;
168 if (software == NULL)
169 return 1;
170 if (s_len != strlen(softexp)) {
171 printf("Software string not parsed correctly length: ");
172 return 1;
173 }
174 if (memcmp(software, softexp, strlen(softexp)) != 0) {
175 printf("Software string not parsed correctly: ");
176 return 1;
177 }
178 }
179 return 0;
180}
181
182/** \test Send a version string in one chunk (client version str). */
183static int SSHParserTest01(void)
184{
185 int result = 0;
186 Flow f;
187 uint8_t sshbuf[] = "SSH-2.0-MySSHClient-0.5.1\n";
188 uint32_t sshlen = sizeof(sshbuf) - 1;
189 TcpSession ssn;
191
192 memset(&f, 0, sizeof(f));
193 memset(&ssn, 0, sizeof(ssn));
194 FLOW_INITIALIZE(&f);
195 f.protoctx = (void *)&ssn;
197
199
200 int r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SSH,
201 STREAM_TOSERVER | STREAM_EOF, sshbuf, sshlen);
202 if (r != 0) {
203 printf("toclient chunk 1 returned %" PRId32 ", expected 0: ", r);
204 goto end;
205 }
206
207 void *ssh_state = f.alstate;
208 if (ssh_state == NULL) {
209 printf("no ssh state: ");
210 goto end;
211 }
212
213 void *tx = SCSshStateGetTx(ssh_state, 0);
214 if (SCSshTxGetAlStateProgress(tx, STREAM_TOSERVER) != SshStateBannerDone) {
215 printf("Client version string not parsed: ");
216 goto end;
217 }
218
219 if (SSHParserTestUtilCheck("2.0", "MySSHClient-0.5.1", tx, STREAM_TOSERVER))
220 goto end;
221
222 result = 1;
223end:
224 if (alp_tctx != NULL)
227 FLOW_DESTROY(&f);
228 return result;
229}
230
231/** \test Send a version string in one chunk but multiple lines and comments.
232 * (client version str)
233 */
234static int SSHParserTest02(void)
235{
236 int result = 0;
237 Flow f;
238 uint8_t sshbuf[] = "SSH-2.0-MySSHClient-0.5.1 some comments...\n";
239 uint32_t sshlen = sizeof(sshbuf) - 1;
240 TcpSession ssn;
242
243 memset(&f, 0, sizeof(f));
244 memset(&ssn, 0, sizeof(ssn));
245 FLOW_INITIALIZE(&f);
246 f.protoctx = (void *)&ssn;
248
250
251 int r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SSH,
252 STREAM_TOSERVER | STREAM_EOF, sshbuf, sshlen);
253 if (r != 0) {
254 printf("toclient chunk 1 returned %" PRId32 ", expected 0: ", r);
255 goto end;
256 }
257
258 void *ssh_state = f.alstate;
259 if (ssh_state == NULL) {
260 printf("no ssh state: ");
261 goto end;
262 }
263 void *tx = SCSshStateGetTx(ssh_state, 0);
264
265 if (SCSshTxGetAlStateProgress(tx, STREAM_TOSERVER) != SshStateBannerDone) {
266 printf("Client version string not parsed: ");
267 goto end;
268 }
269 if (SSHParserTestUtilCheck("2.0", "MySSHClient-0.5.1", tx, STREAM_TOSERVER))
270 goto end;
271
272 result = 1;
273end:
274 if (alp_tctx != NULL)
277 FLOW_DESTROY(&f);
278 return result;
279}
280
281/** \test Send a invalid version string in one chunk but multiple lines and comments.
282 * (client version str)
283 */
284static int SSHParserTest03(void)
285{
286 int result = 0;
287 Flow f;
288 uint8_t sshbuf[] = "SSH-2.0 some comments...\n";
289 uint32_t sshlen = sizeof(sshbuf) - 1;
290 TcpSession ssn;
292
293 memset(&f, 0, sizeof(f));
294 memset(&ssn, 0, sizeof(ssn));
295 FLOW_INITIALIZE(&f);
296 f.protoctx = (void *)&ssn;
298
300
301 int r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SSH,
302 STREAM_TOSERVER | STREAM_EOF, sshbuf, sshlen);
303 if (r == 0) {
304 printf("toclient chunk 1 returned %" PRId32 ", expected != 0: ", r);
305 goto end;
306 }
307
308 void *ssh_state = f.alstate;
309 if (ssh_state == NULL) {
310 printf("no ssh state: ");
311 goto end;
312 }
313 void *tx = SCSshStateGetTx(ssh_state, 0);
314
315 if (SCSshTxGetAlStateProgress(tx, STREAM_TOSERVER) == SshStateBannerDone) {
316 printf("Client version string parsed? It's not a valid string: ");
317 goto end;
318 }
319 const uint8_t *dummy = NULL;
320 uint32_t dummy_len = 0;
321 if (SCSshTxGetProtocol(tx, &dummy, &dummy_len, STREAM_TOSERVER) != 0)
322 goto end;
323 if (SCSshTxGetSoftware(tx, &dummy, &dummy_len, STREAM_TOSERVER) != 0)
324 goto end;
325
326 result = 1;
327end:
328 if (alp_tctx != NULL)
331 FLOW_DESTROY(&f);
332 return result;
333}
334
335/** \test Send a version string in one chunk (server version str). */
336static int SSHParserTest04(void)
337{
338 int result = 0;
339 Flow f;
340 uint8_t sshbuf[] = "SSH-2.0-MySSHClient-0.5.1\n";
341 uint32_t sshlen = sizeof(sshbuf) - 1;
342 TcpSession ssn;
344
345 memset(&f, 0, sizeof(f));
346 memset(&ssn, 0, sizeof(ssn));
347 FLOW_INITIALIZE(&f);
348 f.protoctx = (void *)&ssn;
350
352
353 int r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SSH,
354 STREAM_TOCLIENT | STREAM_EOF, sshbuf, sshlen);
355 if (r != 0) {
356 printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
357 goto end;
358 }
359
360 void *ssh_state = f.alstate;
361 if (ssh_state == NULL) {
362 printf("no ssh state: ");
363 goto end;
364 }
365 void *tx = SCSshStateGetTx(ssh_state, 0);
366
367 if (SCSshTxGetAlStateProgress(tx, STREAM_TOCLIENT) != SshStateBannerDone) {
368 printf("Client version string not parsed: ");
369 goto end;
370 }
371 if (SSHParserTestUtilCheck("2.0", "MySSHClient-0.5.1", tx, STREAM_TOCLIENT))
372 goto end;
373
374 result = 1;
375
376end:
377 if (alp_tctx != NULL)
380 FLOW_DESTROY(&f);
381 return result;
382}
383
384/** \test Send a version string in one chunk (server version str)
385 */
386static int SSHParserTest05(void)
387{
388 int result = 0;
389 Flow f;
390 uint8_t sshbuf[] = "SSH-2.0-MySSHClient-0.5.1 some comments...\n";
391 uint32_t sshlen = sizeof(sshbuf) - 1;
392 TcpSession ssn;
394
395 memset(&f, 0, sizeof(f));
396 memset(&ssn, 0, sizeof(ssn));
397 FLOW_INITIALIZE(&f);
398 f.protoctx = (void *)&ssn;
400
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) != SshStateBannerDone) {
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;
425end:
426 if (alp_tctx != NULL)
429 FLOW_DESTROY(&f);
430 return result;
431}
432
433/** \test Send a invalid version string in one chunk (server version str)
434 */
435static 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;
449
451
452 int r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SSH,
453 STREAM_TOCLIENT | STREAM_EOF, sshbuf, sshlen);
454 if (r == 0) {
455 printf("toserver chunk 1 returned %" PRId32 ", expected != 0: ", r);
456 goto end;
457 }
458 /* Ok, it returned an error. Let's make sure we didn't parse the string at all */
459
460 void *ssh_state = f.alstate;
461 if (ssh_state == NULL) {
462 printf("no ssh state: ");
463 goto end;
464 }
465 void *tx = SCSshStateGetTx(ssh_state, 0);
466
467 if (SCSshTxGetAlStateProgress(tx, STREAM_TOCLIENT) == SshStateBannerDone) {
468 printf("Client version string parsed? It's not a valid string: ");
469 goto end;
470 }
471 const uint8_t *dummy = NULL;
472 uint32_t dummy_len = 0;
473 if (SCSshTxGetProtocol(tx, &dummy, &dummy_len, STREAM_TOCLIENT) != 0)
474 goto end;
475 if (SCSshTxGetSoftware(tx, &dummy, &dummy_len, STREAM_TOCLIENT) != 0)
476 goto end;
477
478
479 result = 1;
480end:
481 if (alp_tctx != NULL)
484 FLOW_DESTROY(&f);
485 return result;
486}
487
488#define MAX_SSH_TEST_SIZE 512
489
490static int SSHParserTest07(void)
491{
492 TcpReassemblyThreadCtx *ra_ctx = NULL;
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]);
523 FAIL_IF(StreamTcpReassembleAppLayer(&tv, ra_ctx, &ssn, &ssn.client, p, UPDATE_DIR_PACKET) < 0);
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) != SshStateBannerDone);
530
531 FAIL_IF(SSHParserTestUtilCheck("2.0", "MySSHClient-0.5.1", tx, STREAM_TOSERVER));
532
533 UTHFreePacket(p);
535 StreamTcpUTDeinit(ra_ctx);
536 UTHFreeFlow(f);
537 PASS;
538}
539
540/** \test Send a version banner in three chunks. */
541static int SSHParserTest08(void)
542{
543 TcpReassemblyThreadCtx *ra_ctx = NULL;
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]);
574 FAIL_IF(StreamTcpReassembleAppLayer(&tv, ra_ctx, &ssn, &ssn.client, p, UPDATE_DIR_PACKET) < 0);
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) != SshStateBannerDone);
581
582 FAIL_IF(SSHParserTestUtilCheck("2.0", "MySSHClient-0.5.1", tx, STREAM_TOSERVER));
583
584 UTHFreePacket(p);
586 StreamTcpUTDeinit(ra_ctx);
587 UTHFreeFlow(f);
588 PASS;
589}
590
591static int SSHParserTest09(void)
592{
593 TcpReassemblyThreadCtx *ra_ctx = NULL;
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]);
624 FAIL_IF(StreamTcpReassembleAppLayer(&tv, ra_ctx, &ssn, &ssn.server, p, UPDATE_DIR_PACKET) < 0);
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) != SshStateBannerDone);
631
632 FAIL_IF(SSHParserTestUtilCheck("2.0", "MySSHClient-0.5.1", tx, STREAM_TOCLIENT));
633
634 UTHFreePacket(p);
636 StreamTcpUTDeinit(ra_ctx);
637 UTHFreeFlow(f);
638 PASS;
639}
640
641/** \test Send a version banner in three chunks. */
642static int SSHParserTest10(void)
643{
644 TcpReassemblyThreadCtx *ra_ctx = NULL;
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]);
675 FAIL_IF(StreamTcpReassembleAppLayer(&tv, ra_ctx, &ssn, &ssn.server, p, UPDATE_DIR_PACKET) < 0);
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) != SshStateBannerDone);
682
683 FAIL_IF(SSHParserTestUtilCheck("2.0", "MySSHClient-0.5.1", tx, STREAM_TOCLIENT));
684
685 UTHFreePacket(p);
687 StreamTcpUTDeinit(ra_ctx);
688 UTHFreeFlow(f);
689 PASS;
690}
691
692/** \test Send a banner and record in three chunks. */
693static 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;
709
711
712 int r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SSH,
713 STREAM_TOSERVER, sshbuf1, sshlen1);
714 if (r != 0) {
715 printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
716 goto end;
717 }
718 r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SSH, STREAM_TOSERVER,
719 sshbuf2, sshlen2);
720 if (r != 0) {
721 printf("toserver chunk 2 returned %" PRId32 ", expected 0: ", r);
722 goto end;
723 }
724
725 void *ssh_state = f.alstate;
726 if (ssh_state == NULL) {
727 printf("no ssh state: ");
728 goto end;
729 }
730 void *tx = SCSshStateGetTx(ssh_state, 0);
731 if (SCSshTxGetFlags(tx, STREAM_TOSERVER) != SshStateFinished) {
732 printf("Didn't detect the msg code of new keys (ciphered data starts): ");
733 goto end;
734 }
735 if (SSHParserTestUtilCheck("2.0", "MySSHClient-0.5.1", tx, STREAM_TOSERVER))
736 goto end;
737
738 result = 1;
739end:
740 if (alp_tctx != NULL)
743 FLOW_DESTROY(&f);
744 return result;
745}
746
747/** \test Send a banner and 2 records record in four chunks. */
748static int SSHParserTest12(void)
749{
750 int result = 0;
751 Flow f;
752 uint8_t sshbuf1[] = "SSH-2.0-MySSHClient-0.5.1\r\n";
753 uint32_t sshlen1 = sizeof(sshbuf1) - 1;
754 uint8_t sshbuf2[] = { 0x00, 0x00, 0x00, 0x03,0x01, 17, 0x00};
755 uint32_t sshlen2 = sizeof(sshbuf2);
756 uint8_t sshbuf3[] = { 0x00, 0x00, 0x00, 0x03,0x01, 21, 0x00};
757 uint32_t sshlen3 = sizeof(sshbuf3);
758 TcpSession ssn;
760
761 memset(&f, 0, sizeof(f));
762 memset(&ssn, 0, sizeof(ssn));
763 FLOW_INITIALIZE(&f);
764 f.protoctx = (void *)&ssn;
766
768
769 int r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SSH,
770 STREAM_TOSERVER, sshbuf1, sshlen1);
771 if (r != 0) {
772 printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
773 goto end;
774 }
775 r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SSH, STREAM_TOSERVER,
776 sshbuf2, sshlen2);
777 if (r != 0) {
778 printf("toserver chunk 2 returned %" PRId32 ", expected 0: ", r);
779 goto end;
780 }
781 r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SSH, STREAM_TOSERVER,
782 sshbuf3, sshlen3);
783 if (r != 0) {
784 printf("toserver chunk 3 returned %" PRId32 ", expected 0: ", r);
785 goto end;
786 }
787
788 void *ssh_state = f.alstate;
789 if (ssh_state == NULL) {
790 printf("no ssh state: ");
791 goto end;
792 }
793 void *tx = SCSshStateGetTx(ssh_state, 0);
794 if (SCSshTxGetFlags(tx, STREAM_TOSERVER) != SshStateFinished) {
795 printf("Didn't detect the msg code of new keys (ciphered data starts): ");
796 goto end;
797 }
798 if (SSHParserTestUtilCheck("2.0", "MySSHClient-0.5.1", tx, STREAM_TOSERVER))
799 goto end;
800
801 result = 1;
802end:
803 if (alp_tctx != NULL)
806 FLOW_DESTROY(&f);
807 return result;
808}
809
810/** \test Send a banner and 2 records record in four chunks. */
811static int SSHParserTest13(void)
812{
813 TcpReassemblyThreadCtx *ra_ctx = NULL;
815 TcpSession ssn;
816 Flow *f = NULL;
817 Packet *p = NULL;
818
819 uint8_t sshbuf1[] = "SSH-2.0-MySSHClient-0.5.1\r\n";
820 uint8_t sshbuf2[] = { 0x00, 0x00, 0x00, 0x02, 0x01, 17};
821 uint8_t sshbuf3[] = { 0x00, 0x00, 0x00, 0x02, 0x01, 21};
822
823 uint8_t* sshbufs[3] = {sshbuf1, sshbuf2, sshbuf3};
824 uint32_t sshlens[3] = {sizeof(sshbuf1) - 1, sizeof(sshbuf2), sizeof(sshbuf3)};
825
826 memset(&tv, 0x00, sizeof(tv));
827
828 StreamTcpUTInit(&ra_ctx);
833
834 f = UTHBuildFlow(AF_INET, "1.1.1.1", "2.2.2.2", 1234, 2222);
835 FAIL_IF_NULL(f);
836 f->protoctx = &ssn;
837 f->proto = IPPROTO_TCP;
838 f->alproto = ALPROTO_SSH;
839
840 p = PacketGetFromAlloc();
841 FAIL_IF(unlikely(p == NULL));
842 p->proto = IPPROTO_TCP;
843 p->flow = f;
844
845 uint32_t seq = 2;
846 for (int i=0; i<3; i++) {
847 FAIL_IF(StreamTcpUTAddSegmentWithPayload(&tv, ra_ctx, &ssn.client, seq, sshbufs[i], sshlens[i]) == -1);
848 seq += sshlens[i];
849 FAIL_IF(StreamTcpReassembleAppLayer(&tv, ra_ctx, &ssn, &ssn.client, p, UPDATE_DIR_PACKET) < 0);
850 }
851
852 void *ssh_state = f->alstate;
853 FAIL_IF_NULL(ssh_state);
854 void *tx = SCSshStateGetTx(ssh_state, 0);
855 FAIL_IF(SCSshTxGetFlags(tx, STREAM_TOSERVER) != SshStateFinished);
856
857 FAIL_IF(SSHParserTestUtilCheck("2.0", "MySSHClient-0.5.1", tx, STREAM_TOSERVER));
858
859 UTHFreePacket(p);
861 StreamTcpUTDeinit(ra_ctx);
862 UTHFreeFlow(f);
863 PASS;
864}
865
866/** \test Send a banner and 2 records record in four chunks. */
867static int SSHParserTest14(void)
868{
869 TcpReassemblyThreadCtx *ra_ctx = NULL;
871 TcpSession ssn;
872 Flow *f = NULL;
873 Packet *p = NULL;
874
875 uint8_t sshbuf1[] = "SSH-2.0-MySSHClient-0.5.1\r\n";
876 uint8_t sshbuf2[] = { 0x00, 0x00, 0x00, 0x10, 0x01, 17, 0x00};
877 uint8_t sshbuf3[] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08};
878 uint8_t sshbuf4[] = { 0x09, 0x10, 0x11, 0x12, 0x13, 0x00};
879 /* first byte of this record in sshbuf4 */
880 uint8_t sshbuf5[] = { 0x00, 0x00, 0x02, 0x01, 21};
881
882 uint8_t* sshbufs[5] = {sshbuf1, sshbuf2, sshbuf3, sshbuf4, sshbuf5};
883 uint32_t sshlens[5] = {sizeof(sshbuf1) - 1, sizeof(sshbuf2), sizeof(sshbuf3), sizeof(sshbuf4), sizeof(sshbuf5)};
884
885 memset(&tv, 0x00, sizeof(tv));
886
887 StreamTcpUTInit(&ra_ctx);
892
893 f = UTHBuildFlow(AF_INET, "1.1.1.1", "2.2.2.2", 1234, 2222);
894 FAIL_IF_NULL(f);
895 f->protoctx = &ssn;
896 f->proto = IPPROTO_TCP;
897 f->alproto = ALPROTO_SSH;
898
899 p = PacketGetFromAlloc();
900 FAIL_IF(unlikely(p == NULL));
901 p->proto = IPPROTO_TCP;
902 p->flow = f;
903
904 uint32_t seq = 2;
905 for (int i=0; i<5; i++) {
906 FAIL_IF(StreamTcpUTAddSegmentWithPayload(&tv, ra_ctx, &ssn.client, seq, sshbufs[i], sshlens[i]) == -1);
907 seq += sshlens[i];
908 FAIL_IF(StreamTcpReassembleAppLayer(&tv, ra_ctx, &ssn, &ssn.client, p, UPDATE_DIR_PACKET) < 0);
909 }
910
911 void *ssh_state = f->alstate;
912 FAIL_IF_NULL(ssh_state);
913 void *tx = SCSshStateGetTx(ssh_state, 0);
914 FAIL_IF(SCSshTxGetFlags(tx, STREAM_TOSERVER) != SshStateFinished);
915
916 FAIL_IF(SSHParserTestUtilCheck("2.0", "MySSHClient-0.5.1", tx, STREAM_TOSERVER));
917
918 UTHFreePacket(p);
920 StreamTcpUTDeinit(ra_ctx);
921 UTHFreeFlow(f);
922 PASS;
923}
924
925/** \test Send a banner and 2 records record in four chunks. */
926static int SSHParserTest15(void)
927{
928 TcpReassemblyThreadCtx *ra_ctx = NULL;
930 TcpSession ssn;
931 Flow *f = NULL;
932 Packet *p = NULL;
933
934 uint8_t sshbuf1[] = "SSH-2.0-MySSHClient-0.5.1\r\n";
935 uint8_t sshbuf2[] = { 0x00, 0x00, 0x00, 0x10, 0x01, 17, 0x00};
936 uint8_t sshbuf3[] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08};
937 uint8_t sshbuf4[] = { 0x09, 0x10, 0x11, 0x12, 0x13, 0x00};
938 uint8_t sshbuf5[] = { 0x00, 0x00, 0x02, 0x01, 20, 0x00, 0x00, 0x00, 0x02, 0x01, 21};
939
940 uint8_t* sshbufs[5] = {sshbuf1, sshbuf2, sshbuf3, sshbuf4, sshbuf5};
941 uint32_t sshlens[5] = {sizeof(sshbuf1) - 1, sizeof(sshbuf2), sizeof(sshbuf3), sizeof(sshbuf4), sizeof(sshbuf5)};
942
943 memset(&tv, 0x00, sizeof(tv));
944
945 StreamTcpUTInit(&ra_ctx);
950
951 f = UTHBuildFlow(AF_INET, "1.1.1.1", "2.2.2.2", 1234, 2222);
952 FAIL_IF_NULL(f);
953 f->protoctx = &ssn;
954 f->proto = IPPROTO_TCP;
955 f->alproto = ALPROTO_SSH;
956
957 p = PacketGetFromAlloc();
958 FAIL_IF(unlikely(p == NULL));
959 p->proto = IPPROTO_TCP;
960 p->flow = f;
961
962 uint32_t seq = 2;
963 for (int i=0; i<5; i++) {
964 FAIL_IF(StreamTcpUTAddSegmentWithPayload(&tv, ra_ctx, &ssn.client, seq, sshbufs[i], sshlens[i]) == -1);
965 seq += sshlens[i];
966 FAIL_IF(StreamTcpReassembleAppLayer(&tv, ra_ctx, &ssn, &ssn.client, p, UPDATE_DIR_PACKET) < 0);
967 }
968
969 void *ssh_state = f->alstate;
970 FAIL_IF_NULL(ssh_state);
971 void *tx = SCSshStateGetTx(ssh_state, 0);
972 FAIL_IF(SCSshTxGetFlags(tx, STREAM_TOSERVER) != SshStateFinished);
973
974 FAIL_IF(SSHParserTestUtilCheck("2.0", "MySSHClient-0.5.1", tx, STREAM_TOSERVER));
975
976 UTHFreePacket(p);
978 StreamTcpUTDeinit(ra_ctx);
979 UTHFreeFlow(f);
980 PASS;
981}
982
983/** \test Send toserver a banner and record in three chunks. */
984static int SSHParserTest16(void)
985{
986 TcpReassemblyThreadCtx *ra_ctx = NULL;
988 TcpSession ssn;
989 Flow *f = NULL;
990 Packet *p = NULL;
991
992 uint8_t sshbuf1[] = "SSH-";
993 uint8_t sshbuf2[] = "2.0-MySSHClient-0.5.1\r\n";
994 uint8_t sshbuf3[] = { 0x00, 0x00, 0x00, 0x03,0x01, 21, 0x00};
995
996 uint8_t* sshbufs[3] = {sshbuf1, sshbuf2, sshbuf3};
997 uint32_t sshlens[3] = {sizeof(sshbuf1) - 1, sizeof(sshbuf2) - 1, sizeof(sshbuf3)};
998
999 memset(&tv, 0x00, sizeof(tv));
1000
1001 StreamTcpUTInit(&ra_ctx);
1006
1007 f = UTHBuildFlow(AF_INET, "1.1.1.1", "2.2.2.2", 1234, 2222);
1008 FAIL_IF_NULL(f);
1009 f->protoctx = &ssn;
1010 f->proto = IPPROTO_TCP;
1011 f->alproto = ALPROTO_SSH;
1012
1013 p = PacketGetFromAlloc();
1014 FAIL_IF(unlikely(p == NULL));
1015 p->proto = IPPROTO_TCP;
1016 p->flow = f;
1017
1018 uint32_t seq = 2;
1019 for (int i=0; i<3; i++) {
1020 FAIL_IF(StreamTcpUTAddSegmentWithPayload(&tv, ra_ctx, &ssn.server, seq, sshbufs[i], sshlens[i]) == -1);
1021 seq += sshlens[i];
1022 FAIL_IF(StreamTcpReassembleAppLayer(&tv, ra_ctx, &ssn, &ssn.server, p, UPDATE_DIR_PACKET) < 0);
1023 }
1024
1025 void *ssh_state = f->alstate;
1026 FAIL_IF_NULL(ssh_state);
1027 void *tx = SCSshStateGetTx(ssh_state, 0);
1028 FAIL_IF(SCSshTxGetFlags(tx, STREAM_TOCLIENT) != SshStateFinished);
1029
1030 FAIL_IF(SSHParserTestUtilCheck("2.0", "MySSHClient-0.5.1", tx, STREAM_TOCLIENT));
1031
1032 UTHFreePacket(p);
1034 StreamTcpUTDeinit(ra_ctx);
1035 UTHFreeFlow(f);
1036 PASS;
1037}
1038
1039/** \test Send toserver a banner and 2 records record in four chunks. */
1040static int SSHParserTest17(void)
1041{
1042 TcpReassemblyThreadCtx *ra_ctx = NULL;
1043 ThreadVars tv;
1044 TcpSession ssn;
1045 Flow *f = NULL;
1046 Packet *p = NULL;
1047
1048 uint8_t sshbuf1[] = "SSH-";
1049 uint8_t sshbuf2[] = "2.0-MySSHClient-0.5.1\r\n";
1050 uint8_t sshbuf3[] = { 0x00, 0x00, 0x00, 0x03, 0x01, 17, 0x00};
1051 uint8_t sshbuf4[] = { 0x00, 0x00, 0x00, 0x03, 0x01, 21, 0x00};
1052
1053 uint8_t* sshbufs[4] = {sshbuf1, sshbuf2, sshbuf3, sshbuf4};
1054 uint32_t sshlens[4] = {sizeof(sshbuf1) - 1, sizeof(sshbuf2) - 1, sizeof(sshbuf3), sizeof(sshbuf4)};
1055
1056 memset(&tv, 0x00, sizeof(tv));
1057
1058 StreamTcpUTInit(&ra_ctx);
1063
1064 f = UTHBuildFlow(AF_INET, "1.1.1.1", "2.2.2.2", 1234, 2222);
1065 FAIL_IF_NULL(f);
1066 f->protoctx = &ssn;
1067 f->proto = IPPROTO_TCP;
1068 f->alproto = ALPROTO_SSH;
1069
1070 p = PacketGetFromAlloc();
1071 FAIL_IF(unlikely(p == NULL));
1072 p->proto = IPPROTO_TCP;
1073 p->flow = f;
1074
1075 uint32_t seq = 2;
1076 for (int i=0; i<4; i++) {
1077 FAIL_IF(StreamTcpUTAddSegmentWithPayload(&tv, ra_ctx, &ssn.server, seq, sshbufs[i], sshlens[i]) == -1);
1078 seq += sshlens[i];
1079 FAIL_IF(StreamTcpReassembleAppLayer(&tv, ra_ctx, &ssn, &ssn.server, p, UPDATE_DIR_PACKET) < 0);
1080 }
1081
1082 void *ssh_state = f->alstate;
1083 FAIL_IF_NULL(ssh_state);
1084 void *tx = SCSshStateGetTx(ssh_state, 0);
1085 FAIL_IF(SCSshTxGetFlags(tx, STREAM_TOCLIENT) != SshStateFinished);
1086
1087 FAIL_IF(SSHParserTestUtilCheck("2.0", "MySSHClient-0.5.1", tx, STREAM_TOCLIENT));
1088
1089 UTHFreePacket(p);
1091 StreamTcpUTDeinit(ra_ctx);
1092 UTHFreeFlow(f);
1093 PASS;
1094}
1095
1096/** \test 2 directional test */
1097static int SSHParserTest18(void)
1098{
1099 TcpReassemblyThreadCtx *ra_ctx = NULL;
1100 ThreadVars tv;
1101 TcpSession ssn;
1102 Flow *f = NULL;
1103 Packet *p = NULL;
1104
1105 uint8_t server1[] = "SSH-2.0-OpenSSH_4.7p1 Debian-8ubuntu3\r\n";
1106 uint8_t sshbuf1[] = "SSH-";
1107 uint8_t sshbuf2[] = "2.0-MySSHClient-0.5.1\r\n";
1108 uint8_t server2[] = { 0x00, 0x00, 0x00, 0x03, 0x01, 21, 0x00 };
1109 uint8_t sshbuf3[] = { 0x00, 0x00, 0x00, 0x03, 0x01, 21, 0x00 };
1110
1111
1112 memset(&tv, 0x00, sizeof(tv));
1113
1114 StreamTcpUTInit(&ra_ctx);
1119
1120 uint8_t* sshbufs[5] = {server1, sshbuf1, sshbuf2, server2, sshbuf3};
1121 uint32_t sshlens[5] = {sizeof(server1) - 1, sizeof(sshbuf1) - 1, sizeof(sshbuf2) -1, sizeof(server2) - 1, sizeof(sshbuf3)};
1122 bool sshdirs[5] = {true, false, false, true, false};
1123
1124 f = UTHBuildFlow(AF_INET, "1.1.1.1", "2.2.2.2", 1234, 2222);
1125 FAIL_IF_NULL(f);
1126 f->protoctx = &ssn;
1127 f->proto = IPPROTO_TCP;
1128 f->alproto = ALPROTO_SSH;
1129
1130 p = PacketGetFromAlloc();
1131 FAIL_IF(unlikely(p == NULL));
1132 p->proto = IPPROTO_TCP;
1133 p->flow = f;
1134
1135 uint32_t seqcli = 2;
1136 uint32_t seqsrv = 2;
1137 for (int i=0; i<5; i++) {
1138 if (sshdirs[i]) {
1139 FAIL_IF(StreamTcpUTAddSegmentWithPayload(&tv, ra_ctx, &ssn.server, seqsrv, sshbufs[i], sshlens[i]) == -1);
1140 seqsrv += sshlens[i];
1141 FAIL_IF(StreamTcpReassembleAppLayer(&tv, ra_ctx, &ssn, &ssn.server, p, UPDATE_DIR_PACKET) < 0);
1142 } else {
1143 FAIL_IF(StreamTcpUTAddSegmentWithPayload(&tv, ra_ctx, &ssn.client, seqcli, sshbufs[i], sshlens[i]) == -1);
1144 seqcli += sshlens[i];
1145 FAIL_IF(StreamTcpReassembleAppLayer(&tv, ra_ctx, &ssn, &ssn.client, p, UPDATE_DIR_PACKET) < 0);
1146 }
1147 }
1148
1149 void *ssh_state = f->alstate;
1150 FAIL_IF_NULL(ssh_state);
1151 void *tx = SCSshStateGetTx(ssh_state, 0);
1152 FAIL_IF(SCSshTxGetFlags(tx, STREAM_TOCLIENT) != SshStateFinished);
1153
1155
1156 UTHFreePacket(p);
1158 StreamTcpUTDeinit(ra_ctx);
1159 UTHFreeFlow(f);
1160 PASS;
1161}
1162
1163/** \test Really long banner handling: bannel exactly 255 */
1164static int SSHParserTest19(void)
1165{
1166 TcpReassemblyThreadCtx *ra_ctx = NULL;
1167 ThreadVars tv;
1168 TcpSession ssn;
1169 Flow *f = NULL;
1170 Packet *p = NULL;
1171
1172 uint8_t sshbuf1[] = "SSH-";
1173 uint8_t sshbuf2[] = "2.0-";
1174 uint8_t sshbuf3[] = "abcdefghijklmnopqrstuvwxyz"
1175 "abcdefghijklmnopqrstuvwxyz"//60
1176 "abcdefghijklmnopqrstuvwxyz"
1177 "abcdefghijklmnopqrstuvwxyz"//112
1178 "abcdefghijklmnopqrstuvwxyz"
1179 "abcdefghijklmnopqrstuvwxyz"//164
1180 "abcdefghijklmnopqrstuvwxyz"
1181 "abcdefghijklmnopqrstuvwxyz"//216
1182 "abcdefghijklmnopqrstuvwxyz"//242
1183 "abcdefghijkl\r";//255
1184 uint8_t sshbuf4[] = { 0x00, 0x00, 0x00, 0x03, 0x01, 21, 0x00};
1185
1186 uint8_t* sshbufs[4] = {sshbuf1, sshbuf2, sshbuf3, sshbuf4};
1187 uint32_t sshlens[4] = {sizeof(sshbuf1) - 1, sizeof(sshbuf2) - 1, sizeof(sshbuf3) - 1, sizeof(sshbuf4)};
1188
1189 memset(&tv, 0x00, sizeof(tv));
1190
1191 StreamTcpUTInit(&ra_ctx);
1196
1197 f = UTHBuildFlow(AF_INET, "1.1.1.1", "2.2.2.2", 1234, 2222);
1198 FAIL_IF_NULL(f);
1199 f->protoctx = &ssn;
1200 f->proto = IPPROTO_TCP;
1201 f->alproto = ALPROTO_SSH;
1202
1203 p = PacketGetFromAlloc();
1204 FAIL_IF(unlikely(p == NULL));
1205 p->proto = IPPROTO_TCP;
1206 p->flow = f;
1207
1208 uint32_t seq = 2;
1209 for (int i=0; i<4; i++) {
1210 FAIL_IF(StreamTcpUTAddSegmentWithPayload(&tv, ra_ctx, &ssn.server, seq, sshbufs[i], sshlens[i]) == -1);
1211 seq += sshlens[i];
1212 FAIL_IF(StreamTcpReassembleAppLayer(&tv, ra_ctx, &ssn, &ssn.server, p, UPDATE_DIR_PACKET) < 0);
1213 }
1214
1215 void *ssh_state = f->alstate;
1216 FAIL_IF_NULL(ssh_state);
1217 void *tx = SCSshStateGetTx(ssh_state, 0);
1218 FAIL_IF(SCSshTxGetFlags(tx, STREAM_TOCLIENT) != SshStateFinished);
1219
1220 sshbuf3[sizeof(sshbuf3) - 2] = 0;
1221 FAIL_IF(SSHParserTestUtilCheck("2.0", (char *)sshbuf3, tx, STREAM_TOCLIENT));
1222
1223 UTHFreePacket(p);
1225 StreamTcpUTDeinit(ra_ctx);
1226 UTHFreeFlow(f);
1227 PASS;
1228}
1229
1230/** \test Really long banner handling: banner exactly 255,
1231 * followed by malformed record */
1232static int SSHParserTest20(void)
1233{
1234 TcpReassemblyThreadCtx *ra_ctx = NULL;
1235 ThreadVars tv;
1236 TcpSession ssn;
1237 Flow *f = NULL;
1238 Packet *p = NULL;
1239
1240 uint8_t sshbuf1[] = "SSH-";
1241 uint8_t sshbuf2[] = "2.0-";
1242 uint8_t sshbuf3[] = "abcdefghijklmnopqrstuvwxyz"
1243 "abcdefghijklmnopqrstuvwxyz"//60
1244 "abcdefghijklmnopqrstuvwxyz"
1245 "abcdefghijklmnopqrstuvwxyz"//112
1246 "abcdefghijklmnopqrstuvwxyz"
1247 "abcdefghijklmnopqrstuvwxyz"//164
1248 "abcdefghijklmnopqrstuvwxyz"
1249 "abcdefghijklmnopqrstuvwxyz"//216
1250 "abcdefghijklmnopqrstuvwxyz"//242
1251 "abcdefghijklm\r";//256
1252 uint8_t sshbuf4[] = {'a','b','c','d','e','f', '\r',
1253 0x00, 0x00, 0x00, 0x06, 0x01, 21, 0x00, 0x00, 0x00};
1254
1255 uint8_t* sshbufs[4] = {sshbuf1, sshbuf2, sshbuf3, sshbuf4};
1256 uint32_t sshlens[4] = {sizeof(sshbuf1) - 1, sizeof(sshbuf2) - 1, sizeof(sshbuf3) - 1, sizeof(sshbuf4) - 1};
1257
1258 memset(&tv, 0x00, sizeof(tv));
1259
1260 StreamTcpUTInit(&ra_ctx);
1265
1266 f = UTHBuildFlow(AF_INET, "1.1.1.1", "2.2.2.2", 1234, 2222);
1267 FAIL_IF_NULL(f);
1268 f->protoctx = &ssn;
1269 f->proto = IPPROTO_TCP;
1270 f->alproto = ALPROTO_SSH;
1271
1272 p = PacketGetFromAlloc();
1273 FAIL_IF(unlikely(p == NULL));
1274 p->proto = IPPROTO_TCP;
1275 p->flow = f;
1276
1277 uint32_t seq = 2;
1278 for (int i=0; i<4; i++) {
1279 FAIL_IF(StreamTcpUTAddSegmentWithPayload(&tv, ra_ctx, &ssn.server, seq, sshbufs[i], sshlens[i]) == -1);
1280 seq += sshlens[i];
1281 FAIL_IF(StreamTcpReassembleAppLayer(&tv, ra_ctx, &ssn, &ssn.server, p, UPDATE_DIR_PACKET) < 0);
1282 }
1283
1284 void *ssh_state = f->alstate;
1285 FAIL_IF_NULL(ssh_state);
1286 void *tx = SCSshStateGetTx(ssh_state, 0);
1287 FAIL_IF(SCSshTxGetFlags(tx, STREAM_TOCLIENT) != SshStateFinished);
1288
1289 FAIL_IF(SSHParserTestUtilCheck("2.0", NULL, tx, STREAM_TOCLIENT));
1290
1291 UTHFreePacket(p);
1293 StreamTcpUTDeinit(ra_ctx);
1294 UTHFreeFlow(f);
1295 PASS;
1296}
1297
1298/** \test Fragmented banner handling: chunk has final part of bannel plus
1299 * a record. */
1300static int SSHParserTest21(void)
1301{
1302 TcpReassemblyThreadCtx *ra_ctx = NULL;
1303 ThreadVars tv;
1304 TcpSession ssn;
1305 Flow *f = NULL;
1306 Packet *p = NULL;
1307
1308 uint8_t sshbuf1[] = "SSH-";
1309 uint8_t sshbuf2[] = "2.0-";
1310 uint8_t sshbuf3[] = "abcdefghijklmnopqrstuvwxyz"
1311 "abcdefghijklmnopqrstuvwxyz"//60
1312 "abcdefghijklmnopqrstuvwxyz"
1313 "abcdefghijklmnopqrstuvwxyz"//112
1314 "abcdefghijklmnopqrstuvwxyz"
1315 "abcdefghijklmnopqrstuvwxyz"//164
1316 "abcdefghijklmnopqrstuvwxyz"
1317 "abcdefghijklmnopqrstuvwxyz"//216
1318 "abcdefghijklmnopqrstuvwxy";//241
1319 uint8_t sshbuf4[] = {'l','i','b','s','s','h', '\r',
1320 0x00, 0x00, 0x00, 0x06, 0x01, 21, 0x00, 0x00, 0x00};
1321
1322 uint8_t* sshbufs[4] = {sshbuf1, sshbuf2, sshbuf3, sshbuf4};
1323 uint32_t sshlens[4] = {sizeof(sshbuf1) - 1, sizeof(sshbuf2) - 1, sizeof(sshbuf3) - 1, sizeof(sshbuf4)};
1324
1325 memset(&tv, 0x00, sizeof(tv));
1326
1327 StreamTcpUTInit(&ra_ctx);
1332
1333 f = UTHBuildFlow(AF_INET, "1.1.1.1", "2.2.2.2", 1234, 2222);
1334 FAIL_IF_NULL(f);
1335 f->protoctx = &ssn;
1336 f->proto = IPPROTO_TCP;
1337 f->alproto = ALPROTO_SSH;
1338
1339 p = PacketGetFromAlloc();
1340 FAIL_IF(unlikely(p == NULL));
1341 p->proto = IPPROTO_TCP;
1342 p->flow = f;
1343
1344 uint32_t seq = 2;
1345 for (int i=0; i<4; i++) {
1346 FAIL_IF(StreamTcpUTAddSegmentWithPayload(&tv, ra_ctx, &ssn.server, seq, sshbufs[i], sshlens[i]) == -1);
1347 seq += sshlens[i];
1348 FAIL_IF(StreamTcpReassembleAppLayer(&tv, ra_ctx, &ssn, &ssn.server, p, UPDATE_DIR_PACKET) < 0);
1349 }
1350
1351 void *ssh_state = f->alstate;
1352 FAIL_IF_NULL(ssh_state);
1353 void *tx = SCSshStateGetTx(ssh_state, 0);
1354 FAIL_IF(SCSshTxGetFlags(tx, STREAM_TOCLIENT) != SshStateFinished);
1355
1356 FAIL_IF(SSHParserTestUtilCheck("2.0", NULL, tx, STREAM_TOCLIENT));
1357
1358 UTHFreePacket(p);
1360 StreamTcpUTDeinit(ra_ctx);
1361 UTHFreeFlow(f);
1362 PASS;
1363}
1364
1365/** \test Fragmented banner handling: chunk has final part of bannel plus
1366 * a record. */
1367static int SSHParserTest22(void)
1368{
1369 TcpReassemblyThreadCtx *ra_ctx = NULL;
1370 ThreadVars tv;
1371 TcpSession ssn;
1372 Flow *f = NULL;
1373 Packet *p = NULL;
1374
1375 uint8_t sshbuf1[] = "SSH-";
1376 uint8_t sshbuf2[] = "2.0-";
1377 uint8_t sshbuf3[] = {
1378 'l', 'i', 'b', 's', 's', 'h', '\r', //7
1379
1380 0x00, 0x00, 0x00, 0x06, 0x01, 17, 0x00, 0x00, 0x00, 0x00,
1381 0x00, 0x00, 0x00, 0x06, 0x01, 17, 0x00, 0x00, 0x00, 0x00,
1382 0x00, 0x00, 0x00, 0x06, 0x01, 17, 0x00, 0x00, 0x00, 0x00,
1383 0x00, 0x00, 0x00, 0x06, 0x01, 17, 0x00, 0x00, 0x00, 0x00,
1384 0x00, 0x00, 0x00, 0x06, 0x01, 17, 0x00, 0x00, 0x00, 0x00, //50
1385
1386 0x00, 0x00, 0x00, 0x06, 0x01, 17, 0x00, 0x00, 0x00, 0x00,
1387 0x00, 0x00, 0x00, 0x06, 0x01, 17, 0x00, 0x00, 0x00, 0x00,
1388 0x00, 0x00, 0x00, 0x06, 0x01, 17, 0x00, 0x00, 0x00, 0x00,
1389 0x00, 0x00, 0x00, 0x06, 0x01, 17, 0x00, 0x00, 0x00, 0x00,
1390 0x00, 0x00, 0x00, 0x06, 0x01, 17, 0x00, 0x00, 0x00, 0x00, //100
1391
1392 0x00, 0x00, 0x00, 0x06, 0x01, 17, 0x00, 0x00, 0x00, 0x00,
1393 0x00, 0x00, 0x00, 0x06, 0x01, 17, 0x00, 0x00, 0x00, 0x00,
1394 0x00, 0x00, 0x00, 0x06, 0x01, 17, 0x00, 0x00, 0x00, 0x00,
1395 0x00, 0x00, 0x00, 0x06, 0x01, 17, 0x00, 0x00, 0x00, 0x00,
1396 0x00, 0x00, 0x00, 0x06, 0x01, 17, 0x00, 0x00, 0x00, 0x00, //150
1397
1398 0x00, 0x00, 0x00, 0x06, 0x01, 17, 0x00, 0x00, 0x00, 0x00,
1399 0x00, 0x00, 0x00, 0x06, 0x01, 17, 0x00, 0x00, 0x00, 0x00,
1400 0x00, 0x00, 0x00, 0x06, 0x01, 17, 0x00, 0x00, 0x00, 0x00,
1401 0x00, 0x00, 0x00, 0x06, 0x01, 17, 0x00, 0x00, 0x00, 0x00,
1402 0x00, 0x00, 0x00, 0x06, 0x01, 17, 0x00, 0x00, 0x00, 0x00, //200
1403
1404 0x00, 0x00, 0x00, 0x06, 0x01, 17, 0x00, 0x00, 0x00, 0x00,
1405 0x00, 0x00, 0x00, 0x06, 0x01, 17, 0x00, 0x00, 0x00, 0x00,
1406 0x00, 0x00, 0x00, 0x06, 0x01, 17, 0x00, 0x00, 0x00, 0x00,
1407 0x00, 0x00, 0x00, 0x06, 0x01, 17, 0x00, 0x00, 0x00, 0x00,
1408 0x00, 0x00, 0x00, 0x06, 0x01, 17, 0x00, 0x00, 0x00, 0x00, //250
1409
1410 0x00, 0x00, 0x00, 0x06, 0x01, 17, 0x00, 0x00, 0x00, 0x00,
1411 0x00, 0x00, 0x00, 0x06, 0x01, 17, 0x00, 0x00, 0x00, 0x00,
1412 0x00, 0x00, 0x00, 0x06, 0x01, 17, 0x00, 0x00, 0x00, 0x00,
1413 0x00, 0x00, 0x00, 0x06, 0x01, 17, 0x00, 0x00, 0x00, 0x00,
1414 0x00, 0x00, 0x00, 0x06, 0x01, 21, 0x00, 0x00, 0x00, 0x00, //300
1415 };
1416
1417
1418 uint8_t* sshbufs[3] = {sshbuf1, sshbuf2, sshbuf3};
1419 uint32_t sshlens[3] = {sizeof(sshbuf1) - 1, sizeof(sshbuf2) - 1, sizeof(sshbuf3) - 1};
1420
1421 memset(&tv, 0x00, sizeof(tv));
1422
1423 StreamTcpUTInit(&ra_ctx);
1428
1429 f = UTHBuildFlow(AF_INET, "1.1.1.1", "2.2.2.2", 1234, 2222);
1430 FAIL_IF_NULL(f);
1431 f->protoctx = &ssn;
1432 f->proto = IPPROTO_TCP;
1433 f->alproto = ALPROTO_SSH;
1434
1435 p = PacketGetFromAlloc();
1436 FAIL_IF(unlikely(p == NULL));
1437 p->proto = IPPROTO_TCP;
1438 p->flow = f;
1439
1440 uint32_t seq = 2;
1441 for (int i=0; i<3; i++) {
1442 FAIL_IF(StreamTcpUTAddSegmentWithPayload(&tv, ra_ctx, &ssn.server, seq, sshbufs[i], sshlens[i]) == -1);
1443 seq += sshlens[i];
1444 FAIL_IF(StreamTcpReassembleAppLayer(&tv, ra_ctx, &ssn, &ssn.server, p, UPDATE_DIR_PACKET) < 0);
1445 }
1446
1447 void *ssh_state = f->alstate;
1448 FAIL_IF_NULL(ssh_state);
1449 void *tx = SCSshStateGetTx(ssh_state, 0);
1450 FAIL_IF(SCSshTxGetFlags(tx, STREAM_TOCLIENT) != SshStateFinished);
1451
1452 FAIL_IF(SSHParserTestUtilCheck("2.0", "libssh", tx, STREAM_TOCLIENT));
1453
1454 UTHFreePacket(p);
1456 StreamTcpUTDeinit(ra_ctx);
1457 UTHFreeFlow(f);
1458 PASS;
1459}
1460
1461/** \test Send a version string in one chunk (client version str). */
1462static int SSHParserTest23(void)
1463{
1464 int result = 0;
1465 Flow f;
1466 uint8_t sshbuf[] = "SSH-2.0\r-MySSHClient-0.5.1\n";
1467 uint32_t sshlen = sizeof(sshbuf) - 1;
1468 TcpSession ssn;
1470
1471 memset(&f, 0, sizeof(f));
1472 memset(&ssn, 0, sizeof(ssn));
1473 FLOW_INITIALIZE(&f);
1474 f.protoctx = (void *)&ssn;
1475 f.alproto = ALPROTO_SSH;
1476
1477 StreamTcpInitConfig(true);
1478
1479 int r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SSH,
1480 STREAM_TOSERVER | STREAM_EOF, sshbuf, sshlen);
1481 if (r == 0) {
1482 printf("toclient chunk 1 returned 0 expected non null: ");
1483 goto end;
1484 }
1485
1486 result = 1;
1487end:
1488 if (alp_tctx != NULL)
1490 StreamTcpFreeConfig(true);
1491 FLOW_DESTROY(&f);
1492 return result;
1493}
1494
1495/** \test Send a version string in one chunk (client version str). */
1496static int SSHParserTest24(void)
1497{
1498 int result = 0;
1499 Flow f;
1500 uint8_t sshbuf[] = "SSH-2.0-\rMySSHClient-0.5.1\n";
1501 uint32_t sshlen = sizeof(sshbuf) - 1;
1502 TcpSession ssn;
1504
1505 memset(&f, 0, sizeof(f));
1506 memset(&ssn, 0, sizeof(ssn));
1507 FLOW_INITIALIZE(&f);
1508 f.protoctx = (void *)&ssn;
1509 f.alproto = ALPROTO_SSH;
1510
1511 StreamTcpInitConfig(true);
1512
1513 int r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SSH,
1514 STREAM_TOSERVER | STREAM_EOF, sshbuf, sshlen);
1515 if (r != 0) {
1516 printf("toclient chunk 1 returned %" PRId32 ", expected 0: ", r);
1517 goto end;
1518 }
1519
1520 void *ssh_state = f.alstate;
1521 if (ssh_state == NULL) {
1522 printf("no ssh state: ");
1523 goto end;
1524 }
1525 void *tx = SCSshStateGetTx(ssh_state, 0);
1526 if (SCSshTxGetFlags(tx, STREAM_TOSERVER) != SshStateBannerDone) {
1527 printf("Didn't detect the msg code of new keys (ciphered data starts): ");
1528 goto end;
1529 }
1530 if (SSHParserTestUtilCheck("2.0", NULL, tx, STREAM_TOSERVER))
1531 goto end;
1532
1533 result = 1;
1534end:
1535 if (alp_tctx != NULL)
1537 StreamTcpFreeConfig(true);
1538 FLOW_DESTROY(&f);
1539 return result;
1540}
1541
1542/** \test Send a malformed banner */
1543static int SSHParserTest25(void)
1544{
1545 Flow f;
1546 uint8_t sshbuf[] = "\n";
1547 uint32_t sshlen = sizeof(sshbuf) - 1;
1548 TcpSession ssn;
1551
1552 memset(&f, 0, sizeof(f));
1553 memset(&ssn, 0, sizeof(ssn));
1554 FLOW_INITIALIZE(&f);
1555 f.protoctx = (void *)&ssn;
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) == SshStateBannerDone);
1568 const uint8_t *dummy = NULL;
1569 uint32_t dummy_len = 0;
1570 FAIL_IF(SCSshTxGetSoftware(tx, &dummy, &dummy_len, STREAM_TOCLIENT) != 0);
1571
1573 StreamTcpFreeConfig(true);
1574 FLOW_DESTROY(&f);
1575 PASS;
1576}
1577
1578#endif /* UNITTESTS */
1579
1581{
1582#ifdef UNITTESTS
1583 UtRegisterTest("SSHParserTest01 - ToServer", SSHParserTest01);
1584 UtRegisterTest("SSHParserTest02 - ToServer", SSHParserTest02);
1585 UtRegisterTest("SSHParserTest03 - ToServer", SSHParserTest03);
1586 UtRegisterTest("SSHParserTest04 - ToClient", SSHParserTest04);
1587 UtRegisterTest("SSHParserTest05 - ToClient", SSHParserTest05);
1588 UtRegisterTest("SSHParserTest06 - ToClient", SSHParserTest06);
1589 UtRegisterTest("SSHParserTest07 - ToServer 2 chunks", SSHParserTest07);
1590 UtRegisterTest("SSHParserTest08 - ToServer 3 chunks", SSHParserTest08);
1591 UtRegisterTest("SSHParserTest09 - ToClient 2 chunks", SSHParserTest09);
1592 UtRegisterTest("SSHParserTest10 - ToClient 3 chunks", SSHParserTest10);
1593 UtRegisterTest("SSHParserTest11 - ToClient 4 chunks", SSHParserTest11);
1594 UtRegisterTest("SSHParserTest12 - ToClient 4 chunks", SSHParserTest12);
1595 UtRegisterTest("SSHParserTest13 - ToClient 4 chunks", SSHParserTest13);
1596 UtRegisterTest("SSHParserTest14 - ToClient 4 chunks", SSHParserTest14);
1597 UtRegisterTest("SSHParserTest15", SSHParserTest15);
1598 UtRegisterTest("SSHParserTest16", SSHParserTest16);
1599 UtRegisterTest("SSHParserTest17", SSHParserTest17);
1600 UtRegisterTest("SSHParserTest18", SSHParserTest18);
1601 UtRegisterTest("SSHParserTest19", SSHParserTest19);
1602 UtRegisterTest("SSHParserTest20", SSHParserTest20);
1603 UtRegisterTest("SSHParserTest21", SSHParserTest21);
1604 UtRegisterTest("SSHParserTest22", SSHParserTest22);
1605 UtRegisterTest("SSHParserTest23", SSHParserTest23);
1606 UtRegisterTest("SSHParserTest24", SSHParserTest24);
1607 UtRegisterTest("SSHParserTest25", SSHParserTest25);
1608#endif /* UNITTESTS */
1609}
1610
void AppLayerProtoDetectRegisterProtocol(AppProto alproto, const char *alproto_name)
Registers a protocol for protocol detection phase.
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.
int SCAppLayerProtoDetectConfProtoDetectionEnabled(const char *ipproto, const char *alproto)
Given a protocol name, checks if proto detection is enabled in the conf file.
uint16_t SCAppLayerParserStateIssetFlag(AppLayerParserState *pstate, uint16_t flag)
void AppLayerParserRegisterProtocolUnittests(uint8_t ipproto, AppProto alproto, void(*RegisterUnittests)(void))
AppLayerParserThreadCtx * AppLayerParserThreadCtxAlloc(void)
Gets a new app layer protocol's parser thread context.
void AppLayerParserThreadCtxFree(AppLayerParserThreadCtx *tctx)
Destroys the app layer parser thread context obtained using AppLayerParserThreadCtxAlloc().
int AppLayerParserParse(ThreadVars *tv, AppLayerParserThreadCtx *alp_tctx, Flow *f, AppProto alproto, uint8_t flags, const uint8_t *input, uint32_t input_len)
#define APP_LAYER_PARSER_NO_INSPECTION
@ ALPROTO_SSH
#define SSH_CONFIG_DEFAULT_HASSH
#define SSH_CONFIG_DEFAULT_ENCRYPTION_BYPASS
bool SSHTxLogCondition(ThreadVars *tv, const Packet *p, void *state, void *tx, uint64_t tx_id)
void SSHParserRegisterTests(void)
#define MAX_SSH_TEST_SIZE
void RegisterSSHParsers(void)
Function to register the SSH protocol parsers and other functions.
SCConfNode * SCConfGetNode(const char *name)
Get a SCConfNode by name.
Definition conf.c:181
int SCConfValIsTrue(const char *val)
Check if a value is true.
Definition conf.c:551
int SCConfValIsFalse(const char *val)
Check if a value is false.
Definition conf.c:576
int SCConfGet(const char *name, const char **vptr)
Retrieve the value of a configuration node.
Definition conf.c:350
uint8_t flags
Definition decode-gre.h:0
uint16_t protocol
Definition decode-ppp.h:2
#define FLOW_INITIALIZE(f)
Definition flow-util.h:38
#define FLOW_DESTROY(f)
Definition flow-util.h:119
AppLayerParserThreadCtx * alp_tctx
ThreadVars * tv
#define FAIL_IF_NULL(expr)
Fail a test if expression evaluates to NULL.
void UtRegisterTest(const char *name, int(*TestFn)(void))
Register unit test.
#define PASS
Pass the test.
#define FAIL_IF(expr)
Fail a test if expression evaluates to true.
Packet * PacketGetFromAlloc(void)
Get a malloced packet.
Definition decode.c:258
uint32_t seq
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.
@ UPDATE_DIR_PACKET
void StreamTcpUTSetupSession(TcpSession *ssn)
void StreamTcpUTSetupStream(TcpStream *s, uint32_t isn)
void StreamTcpUTClearSession(TcpSession *ssn)
void StreamTcpUTInitInline(void)
int StreamTcpUTAddSegmentWithPayload(ThreadVars *tv, TcpReassemblyThreadCtx *ra_ctx, TcpStream *stream, uint32_t seq, uint8_t *payload, uint16_t len)
void StreamTcpUTInit(TcpReassemblyThreadCtx **ra_ctx)
void StreamTcpUTDeinit(TcpReassemblyThreadCtx *ra_ctx)
void StreamTcpFreeConfig(bool quiet)
Definition stream-tcp.c:859
void StreamTcpInitConfig(bool)
To initialize the stream global configuration data.
Definition stream-tcp.c:488
Flow data structure.
Definition flow.h:356
uint8_t proto
Definition flow.h:378
AppProto alproto
application level protocol
Definition flow.h:450
void * alstate
Definition flow.h:479
void * protoctx
Definition flow.h:441
AppLayerParserState * alparser
Definition flow.h:478
struct Flow_ * flow
Definition decode.h:546
uint8_t proto
Definition decode.h:523
char * val
Definition conf.h:39
Per thread variable structure.
Definition threadvars.h:58
int RunmodeIsUnittests(void)
Definition suricata.c:270
#define SCLogDebug(...)
Definition util-debug.h:275
#define SCLogConfig(...)
Definition util-debug.h:229
#define unlikely(expr)
void UTHFreeFlow(Flow *flow)
void UTHFreePacket(Packet *p)
UTHFreePacket: function to release the allocated data from UTHBuildPacket and the packet itself.
Flow * UTHBuildFlow(int family, const char *src, const char *dst, Port sp, Port dp)