• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

OISF / suricata / 23374838686

21 Mar 2026 07:29AM UTC coverage: 59.341% (-20.0%) from 79.315%
23374838686

Pull #15075

github

web-flow
Merge 90b4e834f into 6587e363a
Pull Request #15075: Stack 8001 v16.4

38 of 70 new or added lines in 10 files covered. (54.29%)

34165 existing lines in 563 files now uncovered.

119621 of 201584 relevant lines covered (59.34%)

650666.92 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

61.67
/src/app-layer-ssh.c
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"
36
#include "stream-tcp-reassemble.h"
37
#include "stream-tcp.h"
38
#include "stream.h"
39

40
#include "app-layer-detect-proto.h"
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
6✔
58
/* Bypassing the encrypted part of the connections */
59
#define SSH_CONFIG_DEFAULT_ENCRYPTION_BYPASS ENCRYPTION_HANDLING_TRACK_ONLY
4✔
60

61
static int SSHRegisterPatternsForProtocolDetection(void)
62
{
4✔
63
    if (SCAppLayerProtoDetectPMRegisterPatternCI(
4✔
64
                IPPROTO_TCP, ALPROTO_SSH, "SSH-", 4, 0, STREAM_TOSERVER) < 0) {
4✔
65
        return -1;
×
66
    }
×
67
    if (SCAppLayerProtoDetectPMRegisterPatternCI(
4✔
68
                IPPROTO_TCP, ALPROTO_SSH, "SSH-", 4, 0, STREAM_TOCLIENT) < 0) {
4✔
69
        return -1;
×
70
    }
×
71
    return 0;
4✔
72
}
4✔
73

74
bool SSHTxLogCondition(ThreadVars *tv, const Packet *p, void *state, void *tx, uint64_t tx_id)
75
{
1,669✔
76
    return SCSshTxGetLogCondition(tx);
1,669✔
77
}
1,669✔
78

79
/** \brief Function to register the SSH protocol parsers and other functions
80
 */
81
void RegisterSSHParsers(void)
82
{
4✔
83
    const char *proto_name = "ssh";
4✔
84

85
    if (SCAppLayerProtoDetectConfProtoDetectionEnabled("tcp", proto_name)) {
4✔
86
        AppLayerProtoDetectRegisterProtocol(ALPROTO_SSH, proto_name);
4✔
87
        if (SSHRegisterPatternsForProtocolDetection() < 0)
4✔
88
            return;
×
89

90
        /* Check if we should generate Hassh fingerprints */
91
        int enable_hassh = SSH_CONFIG_DEFAULT_HASSH;
4✔
92
        const char *strval = NULL;
4✔
93
        if (SCConfGet("app-layer.protocols.ssh.hassh", &strval) != 1) {
4✔
94
            enable_hassh = SSH_CONFIG_DEFAULT_HASSH;
2✔
95
        } else if (strcmp(strval, "auto") == 0) {
2✔
96
            enable_hassh = SSH_CONFIG_DEFAULT_HASSH;
×
97
        } else if (SCConfValIsFalse(strval)) {
2✔
UNCOV
98
            enable_hassh = SSH_CONFIG_DEFAULT_HASSH;
×
UNCOV
99
            SCSshDisableHassh();
×
100
        } else if (SCConfValIsTrue(strval)) {
2✔
101
            enable_hassh = true;
2✔
102
        }
2✔
103

104
        if (RunmodeIsUnittests() || enable_hassh) {
4✔
105
            SCSshEnableHassh();
2✔
106
        }
2✔
107

108
        EncryptionHandling encryption_bypass = SSH_CONFIG_DEFAULT_ENCRYPTION_BYPASS;
4✔
109
        SCConfNode *encryption_node = SCConfGetNode("app-layer.protocols.ssh.encryption-handling");
4✔
110
        if (encryption_node != NULL && encryption_node->val != NULL) {
4✔
UNCOV
111
            if (strcmp(encryption_node->val, "full") == 0) {
×
UNCOV
112
                encryption_bypass = ENCRYPTION_HANDLING_FULL;
×
UNCOV
113
            } else if (strcmp(encryption_node->val, "track-only") == 0) {
×
114
                encryption_bypass = ENCRYPTION_HANDLING_TRACK_ONLY;
×
UNCOV
115
            } else if (strcmp(encryption_node->val, "bypass") == 0) {
×
UNCOV
116
                encryption_bypass = ENCRYPTION_HANDLING_BYPASS;
×
UNCOV
117
            } else {
×
118
                encryption_bypass = SSH_CONFIG_DEFAULT_ENCRYPTION_BYPASS;
×
119
            }
×
UNCOV
120
        }
×
121

122
        if (encryption_bypass) {
4✔
UNCOV
123
            SCLogConfig("ssh: bypass on the start of encryption enabled");
×
UNCOV
124
            SCSshEnableBypass(encryption_bypass);
×
UNCOV
125
        }
×
126
    }
4✔
127

128
    SCLogDebug("Registering Rust SSH parser.");
4✔
129
    SCRegisterSshParser();
4✔
130

131
#ifdef UNITTESTS
132
    AppLayerParserRegisterProtocolUnittests(IPPROTO_TCP, ALPROTO_SSH, SSHParserRegisterTests);
133
#endif
134
}
4✔
135

136
/* UNITTESTS */
137
#ifdef UNITTESTS
138
#include "flow-util.h"
139
#include "stream-tcp-util.h"
140
#include "util-unittest-helper.h"
141

142
static int SSHParserTestUtilCheck(const char *protoexp, const char *softexp, void *tx, uint8_t flags) {
143
    const uint8_t *protocol = NULL;
144
    uint32_t p_len = 0;
145
    const uint8_t *software = NULL;
146
    uint32_t s_len = 0;
147

148
    if (SCSshTxGetProtocol(tx, flags, &protocol, &p_len) != 1) {
149
        printf("Version string not parsed correctly return: ");
150
        return 1;
151
    }
152
    if (protocol == NULL) {
153
        printf("Version string not parsed correctly NULL: ");
154
        return 1;
155
    }
156

157
    if (p_len != strlen(protoexp)) {
158
        printf("Version string not parsed correctly length: ");
159
        return 1;
160
    }
161
    if (memcmp(protocol, protoexp, strlen(protoexp)) != 0) {
162
        printf("Version string not parsed correctly: ");
163
        return 1;
164
    }
165

166
    if (softexp != NULL) {
167
        if (SCSshTxGetSoftware(tx, flags, &software, &s_len) != 1)
168
            return 1;
169
        if (software == NULL)
170
            return 1;
171
        if (s_len != strlen(softexp)) {
172
            printf("Software string not parsed correctly length: ");
173
            return 1;
174
        }
175
        if (memcmp(software, softexp, strlen(softexp)) != 0) {
176
            printf("Software string not parsed correctly: ");
177
            return 1;
178
        }
179
    }
180
    return 0;
181
}
182

183
/** \test Send a version string in one chunk (client version str). */
184
static int SSHParserTest01(void)
185
{
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;
190
    AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
191

192
    memset(&f, 0, sizeof(f));
193
    memset(&ssn, 0, sizeof(ssn));
194
    FLOW_INITIALIZE(&f);
195
    f.protoctx = (void *)&ssn;
196
    f.proto = IPPROTO_TCP;
197
    f.alproto = ALPROTO_SSH;
198

199
    StreamTcpInitConfig(true);
200

201
    int r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SSH,
202
                                STREAM_TOSERVER | STREAM_EOF, sshbuf, sshlen);
203
    FAIL_IF_NOT(r == 0);
204

205
    void *ssh_state = f.alstate;
206
    FAIL_IF_NULL(ssh_state);
207

208
    void *tx = SCSshStateGetTx(ssh_state, 0);
209
    FAIL_IF_NULL(tx);
210
    FAIL_IF(SCSshTxGetAlStateProgress(tx, STREAM_TOSERVER) != SshStateBannerDone);
211
    FAIL_IF(SSHParserTestUtilCheck("2.0", "MySSHClient-0.5.1", tx, STREAM_TOSERVER));
212

213
    FLOW_DESTROY(&f);
214
    AppLayerParserThreadCtxFree(alp_tctx);
215
    StreamTcpFreeConfig(true);
216
    PASS;
217
}
218

219
/** \test Send a version string in one chunk but multiple lines and comments.
220
 *        (client version str)
221
 */
222
static int SSHParserTest02(void)
223
{
224
    int result = 0;
225
    Flow f;
226
    uint8_t sshbuf[] = "SSH-2.0-MySSHClient-0.5.1 some comments...\n";
227
    uint32_t sshlen = sizeof(sshbuf) - 1;
228
    TcpSession ssn;
229
    AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
230

231
    memset(&f, 0, sizeof(f));
232
    memset(&ssn, 0, sizeof(ssn));
233
    FLOW_INITIALIZE(&f);
234
    f.protoctx = (void *)&ssn;
235
    f.proto = IPPROTO_TCP;
236
    f.alproto = ALPROTO_SSH;
237

238
    StreamTcpInitConfig(true);
239

240
    int r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SSH,
241
                                STREAM_TOSERVER | STREAM_EOF, sshbuf, sshlen);
242
    if (r != 0) {
243
        printf("toclient chunk 1 returned %" PRId32 ", expected 0: ", r);
244
        goto end;
245
    }
246

247
    void *ssh_state = f.alstate;
248
    if (ssh_state == NULL) {
249
        printf("no ssh state: ");
250
        goto end;
251
    }
252
    void *tx = SCSshStateGetTx(ssh_state, 0);
253

254
    if (SCSshTxGetAlStateProgress(tx, STREAM_TOSERVER) != SshStateBannerDone) {
255
        printf("Client version string not parsed: ");
256
        goto end;
257
    }
258
    if (SSHParserTestUtilCheck("2.0", "MySSHClient-0.5.1", tx, STREAM_TOSERVER))
259
        goto end;
260

261
    result = 1;
262
end:
263
    FLOW_DESTROY(&f);
264
    if (alp_tctx != NULL)
265
        AppLayerParserThreadCtxFree(alp_tctx);
266
    StreamTcpFreeConfig(true);
267
    return result;
268
}
269

270
/** \test Send a invalid version string in one chunk but multiple lines and comments.
271
 *        (client version str)
272
 */
273
static int SSHParserTest03(void)
274
{
275
    int result = 0;
276
    Flow f;
277
    uint8_t sshbuf[] = "SSH-2.0 some comments...\n";
278
    uint32_t sshlen = sizeof(sshbuf) - 1;
279
    TcpSession ssn;
280
    AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
281

282
    memset(&f, 0, sizeof(f));
283
    memset(&ssn, 0, sizeof(ssn));
284
    FLOW_INITIALIZE(&f);
285
    f.protoctx = (void *)&ssn;
286
    f.proto = IPPROTO_TCP;
287
    f.alproto = ALPROTO_SSH;
288

289
    StreamTcpInitConfig(true);
290

291
    int r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SSH,
292
                                STREAM_TOSERVER | STREAM_EOF, sshbuf, sshlen);
293
    if (r == 0) {
294
        printf("toclient chunk 1 returned %" PRId32 ", expected != 0: ", r);
295
        goto end;
296
    }
297

298
    void *ssh_state = f.alstate;
299
    if (ssh_state == NULL) {
300
        printf("no ssh state: ");
301
        goto end;
302
    }
303
    void *tx = SCSshStateGetTx(ssh_state, 0);
304

305
    if (SCSshTxGetAlStateProgress(tx, STREAM_TOSERVER) == SshStateBannerDone) {
306
        printf("Client version string parsed? It's not a valid string: ");
307
        goto end;
308
    }
309
    const uint8_t *dummy = NULL;
310
    uint32_t dummy_len = 0;
311
    if (SCSshTxGetProtocol(tx, STREAM_TOSERVER, &dummy, &dummy_len) != 0)
312
        goto end;
313
    if (SCSshTxGetSoftware(tx, STREAM_TOSERVER, &dummy, &dummy_len) != 0)
314
        goto end;
315

316
    result = 1;
317
end:
318
    FLOW_DESTROY(&f);
319
    if (alp_tctx != NULL)
320
        AppLayerParserThreadCtxFree(alp_tctx);
321
    StreamTcpFreeConfig(true);
322
    return result;
323
}
324

325
/** \test Send a version string in one chunk (server version str). */
326
static int SSHParserTest04(void)
327
{
328
    int result = 0;
329
    Flow f;
330
    uint8_t sshbuf[] = "SSH-2.0-MySSHClient-0.5.1\n";
331
    uint32_t sshlen = sizeof(sshbuf) - 1;
332
    TcpSession ssn;
333
    AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
334

335
    memset(&f, 0, sizeof(f));
336
    memset(&ssn, 0, sizeof(ssn));
337
    FLOW_INITIALIZE(&f);
338
    f.protoctx = (void *)&ssn;
339
    f.proto = IPPROTO_TCP;
340
    f.alproto = ALPROTO_SSH;
341

342
    StreamTcpInitConfig(true);
343

344
    int r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SSH,
345
                                STREAM_TOCLIENT | STREAM_EOF, sshbuf, sshlen);
346
    if (r != 0) {
347
        printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
348
        goto end;
349
    }
350

351
    void *ssh_state = f.alstate;
352
    if (ssh_state == NULL) {
353
        printf("no ssh state: ");
354
        goto end;
355
    }
356
    void *tx = SCSshStateGetTx(ssh_state, 0);
357

358
    if (SCSshTxGetAlStateProgress(tx, STREAM_TOCLIENT) != SshStateBannerDone) {
359
        printf("Client version string not parsed: ");
360
        goto end;
361
    }
362
    if (SSHParserTestUtilCheck("2.0", "MySSHClient-0.5.1", tx, STREAM_TOCLIENT))
363
        goto end;
364

365
    result = 1;
366

367
end:
368
    FLOW_DESTROY(&f);
369
    if (alp_tctx != NULL)
370
        AppLayerParserThreadCtxFree(alp_tctx);
371
    StreamTcpFreeConfig(true);
372
    return result;
373
}
374

375
/** \test Send a version string in one chunk (server version str)
376
 */
377
static int SSHParserTest05(void)
378
{
379
    int result = 0;
380
    Flow f;
381
    uint8_t sshbuf[] = "SSH-2.0-MySSHClient-0.5.1 some comments...\n";
382
    uint32_t sshlen = sizeof(sshbuf) - 1;
383
    TcpSession ssn;
384
    AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
385

386
    memset(&f, 0, sizeof(f));
387
    memset(&ssn, 0, sizeof(ssn));
388
    FLOW_INITIALIZE(&f);
389
    f.protoctx = (void *)&ssn;
390
    f.proto = IPPROTO_TCP;
391
    f.alproto = ALPROTO_SSH;
392

393
    StreamTcpInitConfig(true);
394

395
    int r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SSH,
396
                                STREAM_TOCLIENT | STREAM_EOF, sshbuf, sshlen);
397
    if (r != 0) {
398
        printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
399
        goto end;
400
    }
401

402
    void *ssh_state = f.alstate;
403
    if (ssh_state == NULL) {
404
        printf("no ssh state: ");
405
        goto end;
406
    }
407
    void *tx = SCSshStateGetTx(ssh_state, 0);
408

409
    if (SCSshTxGetAlStateProgress(tx, STREAM_TOCLIENT) != SshStateBannerDone) {
410
        printf("Client version string not parsed: ");
411
        goto end;
412
    }
413
    if (SSHParserTestUtilCheck("2.0", "MySSHClient-0.5.1", tx, STREAM_TOCLIENT))
414
        goto end;
415

416
    result = 1;
417
end:
418
    FLOW_DESTROY(&f);
419
    if (alp_tctx != NULL)
420
        AppLayerParserThreadCtxFree(alp_tctx);
421
    StreamTcpFreeConfig(true);
422
    return result;
423
}
424

425
/** \test Send a invalid version string in one chunk (server version str)
426
 */
427
static int SSHParserTest06(void)
428
{
429
    int result = 0;
430
    Flow f;
431
    uint8_t sshbuf[] = "SSH-2.0 some comments...\n";
432
    uint32_t sshlen = sizeof(sshbuf) - 1;
433
    TcpSession ssn;
434
    AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
435

436
    memset(&f, 0, sizeof(f));
437
    memset(&ssn, 0, sizeof(ssn));
438
    FLOW_INITIALIZE(&f);
439
    f.protoctx = (void *)&ssn;
440
    f.proto = IPPROTO_TCP;
441
    f.alproto = ALPROTO_SSH;
442

443
    StreamTcpInitConfig(true);
444

445
    int r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SSH,
446
                                STREAM_TOCLIENT | STREAM_EOF, sshbuf, sshlen);
447
    if (r == 0) {
448
        printf("toserver chunk 1 returned %" PRId32 ", expected != 0: ", r);
449
        goto end;
450
    }
451
    /* Ok, it returned an error. Let's make sure we didn't parse the string at all */
452

453
    void *ssh_state = f.alstate;
454
    if (ssh_state == NULL) {
455
        printf("no ssh state: ");
456
        goto end;
457
    }
458
    void *tx = SCSshStateGetTx(ssh_state, 0);
459

460
    if (SCSshTxGetAlStateProgress(tx, STREAM_TOCLIENT) == SshStateBannerDone) {
461
        printf("Client version string parsed? It's not a valid string: ");
462
        goto end;
463
    }
464
    const uint8_t *dummy = NULL;
465
    uint32_t dummy_len = 0;
466
    if (SCSshTxGetProtocol(tx, STREAM_TOCLIENT, &dummy, &dummy_len) != 0)
467
        goto end;
468
    if (SCSshTxGetSoftware(tx, STREAM_TOCLIENT, &dummy, &dummy_len) != 0)
469
        goto end;
470

471
    result = 1;
472
end:
473
    FLOW_DESTROY(&f);
474
    if (alp_tctx != NULL)
475
        AppLayerParserThreadCtxFree(alp_tctx);
476
    StreamTcpFreeConfig(true);
477
    return result;
478
}
479

480
#define MAX_SSH_TEST_SIZE 512
481

482
static int SSHParserTest07(void)
483
{
484
    TcpReassemblyThreadCtx *ra_ctx = NULL;
485
    ThreadVars tv;
486
    TcpSession ssn;
487
    Flow *f = NULL;
488
    Packet *p = NULL;
489

490
    char sshbufs[2][MAX_SSH_TEST_SIZE] = {"SSH-2.", "0-MySSHClient-0.5.1\r\n"};
491

492
    memset(&tv, 0x00, sizeof(tv));
493

494
    StreamTcpUTInit(&ra_ctx);
495
    StreamTcpUTInitInline();
496
    StreamTcpUTSetupSession(&ssn);
497
    StreamTcpUTSetupStream(&ssn.server, 1);
498
    StreamTcpUTSetupStream(&ssn.client, 1);
499

500
    f = UTHBuildFlow(AF_INET, "1.1.1.1", "2.2.2.2", 1234, 2222);
501
    FAIL_IF_NULL(f);
502
    f->protoctx = &ssn;
503
    f->proto = IPPROTO_TCP;
504
    f->alproto = ALPROTO_SSH;
505

506
    p = PacketGetFromAlloc();
507
    FAIL_IF(unlikely(p == NULL));
508
    p->proto = IPPROTO_TCP;
509
    p->flow = f;
510

511
    uint32_t seq = 2;
512
    for (int i=0; i<2; i++) {
513
        FAIL_IF(StreamTcpUTAddSegmentWithPayload(&tv, ra_ctx, &ssn.client, seq, (uint8_t *) sshbufs[i], strlen(sshbufs[i])) == -1);
514
        seq += strlen(sshbufs[i]);
515
        FAIL_IF(StreamTcpReassembleAppLayer(&tv, ra_ctx, &ssn, &ssn.client, p, UPDATE_DIR_PACKET) < 0);
516
    }
517

518
    void *ssh_state = f->alstate;
519
    FAIL_IF_NULL(ssh_state);
520
    void *tx = SCSshStateGetTx(ssh_state, 0);
521
    FAIL_IF(SCSshTxGetAlStateProgress(tx, STREAM_TOSERVER) != SshStateBannerDone);
522

523
    FAIL_IF(SSHParserTestUtilCheck("2.0", "MySSHClient-0.5.1", tx, STREAM_TOSERVER));
524

525
    UTHFreePacket(p);
526
    UTHFreeFlow(f);
527
    StreamTcpUTClearSession(&ssn);
528
    StreamTcpUTDeinit(ra_ctx);
529
    PASS;
530
}
531

532
/** \test Send a version banner in three chunks. */
533
static int SSHParserTest08(void)
534
{
535
    TcpReassemblyThreadCtx *ra_ctx = NULL;
536
    ThreadVars tv;
537
    TcpSession ssn;
538
    Flow *f = NULL;
539
    Packet *p = NULL;
540

541
    char sshbufs[3][MAX_SSH_TEST_SIZE] = {"SSH-", "2.", "0-MySSHClient-0.5.1\r\n"};
542

543
    memset(&tv, 0x00, sizeof(tv));
544

545
    StreamTcpUTInit(&ra_ctx);
546
    StreamTcpUTInitInline();
547
    StreamTcpUTSetupSession(&ssn);
548
    StreamTcpUTSetupStream(&ssn.server, 1);
549
    StreamTcpUTSetupStream(&ssn.client, 1);
550

551
    f = UTHBuildFlow(AF_INET, "1.1.1.1", "2.2.2.2", 1234, 2222);
552
    FAIL_IF_NULL(f);
553
    f->protoctx = &ssn;
554
    f->proto = IPPROTO_TCP;
555
    f->alproto = ALPROTO_SSH;
556

557
    p = PacketGetFromAlloc();
558
    FAIL_IF(unlikely(p == NULL));
559
    p->proto = IPPROTO_TCP;
560
    p->flow = f;
561

562
    uint32_t seq = 2;
563
    for (int i=0; i<3; i++) {
564
        FAIL_IF(StreamTcpUTAddSegmentWithPayload(&tv, ra_ctx, &ssn.client, seq, (uint8_t *) sshbufs[i], strlen(sshbufs[i])) == -1);
565
        seq += strlen(sshbufs[i]);
566
        FAIL_IF(StreamTcpReassembleAppLayer(&tv, ra_ctx, &ssn, &ssn.client, p, UPDATE_DIR_PACKET) < 0);
567
    }
568

569
    void *ssh_state = f->alstate;
570
    FAIL_IF_NULL(ssh_state);
571
    void *tx = SCSshStateGetTx(ssh_state, 0);
572
    FAIL_IF(SCSshTxGetAlStateProgress(tx, STREAM_TOSERVER) != SshStateBannerDone);
573

574
    FAIL_IF(SSHParserTestUtilCheck("2.0", "MySSHClient-0.5.1", tx, STREAM_TOSERVER));
575

576
    UTHFreePacket(p);
577
    UTHFreeFlow(f);
578
    StreamTcpUTClearSession(&ssn);
579
    StreamTcpUTDeinit(ra_ctx);
580
    PASS;
581
}
582

583
static int SSHParserTest09(void)
584
{
585
    TcpReassemblyThreadCtx *ra_ctx = NULL;
586
    ThreadVars tv;
587
    TcpSession ssn;
588
    Flow *f = NULL;
589
    Packet *p = NULL;
590

591
    char sshbufs[2][MAX_SSH_TEST_SIZE] = {"SSH-2.", "0-MySSHClient-0.5.1\r\n"};
592

593
    memset(&tv, 0x00, sizeof(tv));
594

595
    StreamTcpUTInit(&ra_ctx);
596
    StreamTcpUTInitInline();
597
    StreamTcpUTSetupSession(&ssn);
598
    StreamTcpUTSetupStream(&ssn.server, 1);
599
    StreamTcpUTSetupStream(&ssn.client, 1);
600

601
    f = UTHBuildFlow(AF_INET, "1.1.1.1", "2.2.2.2", 1234, 2222);
602
    FAIL_IF_NULL(f);
603
    f->protoctx = &ssn;
604
    f->proto = IPPROTO_TCP;
605
    f->alproto = ALPROTO_SSH;
606

607
    p = PacketGetFromAlloc();
608
    FAIL_IF(unlikely(p == NULL));
609
    p->proto = IPPROTO_TCP;
610
    p->flow = f;
611

612
    uint32_t seq = 2;
613
    for (int i=0; i<2; i++) {
614
        FAIL_IF(StreamTcpUTAddSegmentWithPayload(&tv, ra_ctx, &ssn.server, seq, (uint8_t *) sshbufs[i], strlen(sshbufs[i])) == -1);
615
        seq += strlen(sshbufs[i]);
616
        FAIL_IF(StreamTcpReassembleAppLayer(&tv, ra_ctx, &ssn, &ssn.server, p, UPDATE_DIR_PACKET) < 0);
617
    }
618

619
    void *ssh_state = f->alstate;
620
    FAIL_IF_NULL(ssh_state);
621
    void *tx = SCSshStateGetTx(ssh_state, 0);
622
    FAIL_IF(SCSshTxGetAlStateProgress(tx, STREAM_TOCLIENT) != SshStateBannerDone);
623

624
    FAIL_IF(SSHParserTestUtilCheck("2.0", "MySSHClient-0.5.1", tx, STREAM_TOCLIENT));
625

626
    UTHFreePacket(p);
627
    UTHFreeFlow(f);
628
    StreamTcpUTClearSession(&ssn);
629
    StreamTcpUTDeinit(ra_ctx);
630
    PASS;
631
}
632

633
/** \test Send a version banner in three chunks. */
634
static int SSHParserTest10(void)
635
{
636
    TcpReassemblyThreadCtx *ra_ctx = NULL;
637
    ThreadVars tv;
638
    TcpSession ssn;
639
    Flow *f = NULL;
640
    Packet *p = NULL;
641

642
    char sshbufs[3][MAX_SSH_TEST_SIZE] = {"SSH-", "2.", "0-MySSHClient-0.5.1\r\n"};
643

644
    memset(&tv, 0x00, sizeof(tv));
645

646
    StreamTcpUTInit(&ra_ctx);
647
    StreamTcpUTInitInline();
648
    StreamTcpUTSetupSession(&ssn);
649
    StreamTcpUTSetupStream(&ssn.server, 1);
650
    StreamTcpUTSetupStream(&ssn.client, 1);
651

652
    f = UTHBuildFlow(AF_INET, "1.1.1.1", "2.2.2.2", 1234, 2222);
653
    FAIL_IF_NULL(f);
654
    f->protoctx = &ssn;
655
    f->proto = IPPROTO_TCP;
656
    f->alproto = ALPROTO_SSH;
657

658
    p = PacketGetFromAlloc();
659
    FAIL_IF(unlikely(p == NULL));
660
    p->proto = IPPROTO_TCP;
661
    p->flow = f;
662

663
    uint32_t seq = 2;
664
    for (int i=0; i<3; i++) {
665
        FAIL_IF(StreamTcpUTAddSegmentWithPayload(&tv, ra_ctx, &ssn.server, seq, (uint8_t *) sshbufs[i], strlen(sshbufs[i])) == -1);
666
        seq += strlen(sshbufs[i]);
667
        FAIL_IF(StreamTcpReassembleAppLayer(&tv, ra_ctx, &ssn, &ssn.server, p, UPDATE_DIR_PACKET) < 0);
668
    }
669

670
    void *ssh_state = f->alstate;
671
    FAIL_IF_NULL(ssh_state);
672
    void *tx = SCSshStateGetTx(ssh_state, 0);
673
    FAIL_IF(SCSshTxGetAlStateProgress(tx, STREAM_TOCLIENT) != SshStateBannerDone);
674

675
    FAIL_IF(SSHParserTestUtilCheck("2.0", "MySSHClient-0.5.1", tx, STREAM_TOCLIENT));
676

677
    UTHFreePacket(p);
678
    UTHFreeFlow(f);
679
    StreamTcpUTClearSession(&ssn);
680
    StreamTcpUTDeinit(ra_ctx);
681
    PASS;
682
}
683

684
/** \test Send a banner and record in three chunks. */
685
static int SSHParserTest11(void)
686
{
687
    int result = 0;
688
    Flow f;
689
    uint8_t sshbuf1[] = "SSH-2.0-MySSHClient-0.5.1\r\n";
690
    uint32_t sshlen1 = sizeof(sshbuf1) - 1;
691
    uint8_t sshbuf2[] = { 0x00, 0x00, 0x00, 0x03, 0x01, 21, 0x00};
692
    uint32_t sshlen2 = sizeof(sshbuf2);
693
    TcpSession ssn;
694
    AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
695

696
    memset(&f, 0, sizeof(f));
697
    memset(&ssn, 0, sizeof(ssn));
698
    FLOW_INITIALIZE(&f);
699
    f.protoctx = (void *)&ssn;
700
    f.proto = IPPROTO_TCP;
701
    f.alproto = ALPROTO_SSH;
702

703
    StreamTcpInitConfig(true);
704

705
    int r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SSH,
706
                                STREAM_TOSERVER, sshbuf1, sshlen1);
707
    if (r != 0) {
708
        printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
709
        goto end;
710
    }
711
    r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SSH, STREAM_TOSERVER,
712
                            sshbuf2, sshlen2);
713
    if (r != 0) {
714
        printf("toserver chunk 2 returned %" PRId32 ", expected 0: ", r);
715
        goto end;
716
    }
717

718
    void *ssh_state = f.alstate;
719
    if (ssh_state == NULL) {
720
        printf("no ssh state: ");
721
        goto end;
722
    }
723
    void *tx = SCSshStateGetTx(ssh_state, 0);
724
    if (SCSshTxGetFlags(tx, STREAM_TOSERVER) != SshStateFinished) {
725
        printf("Didn't detect the msg code of new keys (ciphered data starts): ");
726
        goto end;
727
    }
728
    if (SSHParserTestUtilCheck("2.0", "MySSHClient-0.5.1", tx, STREAM_TOSERVER))
729
        goto end;
730

731
    result = 1;
732
end:
733
    FLOW_DESTROY(&f);
734
    if (alp_tctx != NULL)
735
        AppLayerParserThreadCtxFree(alp_tctx);
736
    StreamTcpFreeConfig(true);
737
    return result;
738
}
739

740
/** \test Send a banner and 2 records record in four chunks. */
741
static int SSHParserTest12(void)
742
{
743
    int result = 0;
744
    Flow f;
745
    uint8_t sshbuf1[] = "SSH-2.0-MySSHClient-0.5.1\r\n";
746
    uint32_t sshlen1 = sizeof(sshbuf1) - 1;
747
    uint8_t sshbuf2[] = { 0x00, 0x00, 0x00, 0x03,0x01, 17, 0x00};
748
    uint32_t sshlen2 = sizeof(sshbuf2);
749
    uint8_t sshbuf3[] = { 0x00, 0x00, 0x00, 0x03,0x01, 21, 0x00};
750
    uint32_t sshlen3 = sizeof(sshbuf3);
751
    TcpSession ssn;
752
    AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
753

754
    memset(&f, 0, sizeof(f));
755
    memset(&ssn, 0, sizeof(ssn));
756
    FLOW_INITIALIZE(&f);
757
    f.protoctx = (void *)&ssn;
758
    f.proto = IPPROTO_TCP;
759
    f.alproto = ALPROTO_SSH;
760

761
    StreamTcpInitConfig(true);
762

763
    int r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SSH,
764
                                STREAM_TOSERVER, sshbuf1, sshlen1);
765
    if (r != 0) {
766
        printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
767
        goto end;
768
    }
769
    r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SSH, STREAM_TOSERVER,
770
                            sshbuf2, sshlen2);
771
    if (r != 0) {
772
        printf("toserver chunk 2 returned %" PRId32 ", expected 0: ", r);
773
        goto end;
774
    }
775
    r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SSH, STREAM_TOSERVER,
776
                            sshbuf3, sshlen3);
777
    if (r != 0) {
778
        printf("toserver chunk 3 returned %" PRId32 ", expected 0: ", r);
779
        goto end;
780
    }
781

782
    void *ssh_state = f.alstate;
783
    if (ssh_state == NULL) {
784
        printf("no ssh state: ");
785
        goto end;
786
    }
787
    void *tx = SCSshStateGetTx(ssh_state, 0);
788
    if (SCSshTxGetFlags(tx, STREAM_TOSERVER) != SshStateFinished) {
789
        printf("Didn't detect the msg code of new keys (ciphered data starts): ");
790
        goto end;
791
    }
792
    if (SSHParserTestUtilCheck("2.0", "MySSHClient-0.5.1", tx, STREAM_TOSERVER))
793
        goto end;
794

795
    result = 1;
796
end:
797
    FLOW_DESTROY(&f);
798
    if (alp_tctx != NULL)
799
        AppLayerParserThreadCtxFree(alp_tctx);
800
    StreamTcpFreeConfig(true);
801
    return result;
802
}
803

804
/** \test Send a banner and 2 records record in four chunks. */
805
static int SSHParserTest13(void)
806
{
807
    TcpReassemblyThreadCtx *ra_ctx = NULL;
808
    ThreadVars tv;
809
    TcpSession ssn;
810
    Flow *f = NULL;
811
    Packet *p = NULL;
812

813
    uint8_t sshbuf1[] = "SSH-2.0-MySSHClient-0.5.1\r\n";
814
    uint8_t sshbuf2[] = { 0x00, 0x00, 0x00, 0x02, 0x01, 17};
815
    uint8_t sshbuf3[] = { 0x00, 0x00, 0x00, 0x02, 0x01, 21};
816

817
    uint8_t* sshbufs[3] = {sshbuf1, sshbuf2, sshbuf3};
818
    uint32_t sshlens[3] = {sizeof(sshbuf1) - 1, sizeof(sshbuf2), sizeof(sshbuf3)};
819

820
    memset(&tv, 0x00, sizeof(tv));
821

822
    StreamTcpUTInit(&ra_ctx);
823
    StreamTcpUTInitInline();
824
    StreamTcpUTSetupSession(&ssn);
825
    StreamTcpUTSetupStream(&ssn.server, 1);
826
    StreamTcpUTSetupStream(&ssn.client, 1);
827

828
    f = UTHBuildFlow(AF_INET, "1.1.1.1", "2.2.2.2", 1234, 2222);
829
    FAIL_IF_NULL(f);
830
    f->protoctx = &ssn;
831
    f->proto = IPPROTO_TCP;
832
    f->alproto = ALPROTO_SSH;
833

834
    p = PacketGetFromAlloc();
835
    FAIL_IF(unlikely(p == NULL));
836
    p->proto = IPPROTO_TCP;
837
    p->flow = f;
838

839
    uint32_t seq = 2;
840
    for (int i=0; i<3; i++) {
841
        FAIL_IF(StreamTcpUTAddSegmentWithPayload(&tv, ra_ctx, &ssn.client, seq, sshbufs[i], sshlens[i]) == -1);
842
        seq += sshlens[i];
843
        FAIL_IF(StreamTcpReassembleAppLayer(&tv, ra_ctx, &ssn, &ssn.client, p, UPDATE_DIR_PACKET) < 0);
844
    }
845

846
    void *ssh_state = f->alstate;
847
    FAIL_IF_NULL(ssh_state);
848
    void *tx = SCSshStateGetTx(ssh_state, 0);
849
    FAIL_IF(SCSshTxGetFlags(tx, STREAM_TOSERVER) != SshStateFinished);
850

851
    FAIL_IF(SSHParserTestUtilCheck("2.0", "MySSHClient-0.5.1", tx, STREAM_TOSERVER));
852

853
    UTHFreePacket(p);
854
    UTHFreeFlow(f);
855
    StreamTcpUTClearSession(&ssn);
856
    StreamTcpUTDeinit(ra_ctx);
857
    PASS;
858
}
859

860
/** \test Send a banner and 2 records record in four chunks. */
861
static int SSHParserTest14(void)
862
{
863
    TcpReassemblyThreadCtx *ra_ctx = NULL;
864
    ThreadVars tv;
865
    TcpSession ssn;
866
    Flow *f = NULL;
867
    Packet *p = NULL;
868

869
    uint8_t sshbuf1[] = "SSH-2.0-MySSHClient-0.5.1\r\n";
870
    uint8_t sshbuf2[] = { 0x00, 0x00, 0x00, 0x10, 0x01, 17, 0x00};
871
    uint8_t sshbuf3[] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08};
872
    uint8_t sshbuf4[] = { 0x09, 0x10, 0x11, 0x12, 0x13, 0x00};
873
    /* first byte of this record in sshbuf4 */
874
    uint8_t sshbuf5[] = { 0x00, 0x00, 0x02, 0x01, 21};
875

876
    uint8_t* sshbufs[5] = {sshbuf1, sshbuf2, sshbuf3, sshbuf4, sshbuf5};
877
    uint32_t sshlens[5] = {sizeof(sshbuf1) - 1, sizeof(sshbuf2), sizeof(sshbuf3), sizeof(sshbuf4), sizeof(sshbuf5)};
878

879
    memset(&tv, 0x00, sizeof(tv));
880

881
    StreamTcpUTInit(&ra_ctx);
882
    StreamTcpUTInitInline();
883
    StreamTcpUTSetupSession(&ssn);
884
    StreamTcpUTSetupStream(&ssn.server, 1);
885
    StreamTcpUTSetupStream(&ssn.client, 1);
886

887
    f = UTHBuildFlow(AF_INET, "1.1.1.1", "2.2.2.2", 1234, 2222);
888
    FAIL_IF_NULL(f);
889
    f->protoctx = &ssn;
890
    f->proto = IPPROTO_TCP;
891
    f->alproto = ALPROTO_SSH;
892

893
    p = PacketGetFromAlloc();
894
    FAIL_IF(unlikely(p == NULL));
895
    p->proto = IPPROTO_TCP;
896
    p->flow = f;
897

898
    uint32_t seq = 2;
899
    for (int i=0; i<5; i++) {
900
        FAIL_IF(StreamTcpUTAddSegmentWithPayload(&tv, ra_ctx, &ssn.client, seq, sshbufs[i], sshlens[i]) == -1);
901
        seq += sshlens[i];
902
        FAIL_IF(StreamTcpReassembleAppLayer(&tv, ra_ctx, &ssn, &ssn.client, p, UPDATE_DIR_PACKET) < 0);
903
    }
904

905
    void *ssh_state = f->alstate;
906
    FAIL_IF_NULL(ssh_state);
907
    void *tx = SCSshStateGetTx(ssh_state, 0);
908
    FAIL_IF(SCSshTxGetFlags(tx, STREAM_TOSERVER) != SshStateFinished);
909

910
    FAIL_IF(SSHParserTestUtilCheck("2.0", "MySSHClient-0.5.1", tx, STREAM_TOSERVER));
911

912
    UTHFreePacket(p);
913
    UTHFreeFlow(f);
914
    StreamTcpUTClearSession(&ssn);
915
    StreamTcpUTDeinit(ra_ctx);
916
    PASS;
917
}
918

919
/** \test Send a banner and 2 records record in four chunks. */
920
static int SSHParserTest15(void)
921
{
922
    TcpReassemblyThreadCtx *ra_ctx = NULL;
923
    ThreadVars tv;
924
    TcpSession ssn;
925
    Flow *f = NULL;
926
    Packet *p = NULL;
927

928
    uint8_t sshbuf1[] = "SSH-2.0-MySSHClient-0.5.1\r\n";
929
    uint8_t sshbuf2[] = { 0x00, 0x00, 0x00, 0x10, 0x01, 17, 0x00};
930
    uint8_t sshbuf3[] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08};
931
    uint8_t sshbuf4[] = { 0x09, 0x10, 0x11, 0x12, 0x13, 0x00};
932
    uint8_t sshbuf5[] = { 0x00, 0x00, 0x02, 0x01, 20, 0x00, 0x00, 0x00, 0x02, 0x01, 21};
933

934
    uint8_t* sshbufs[5] = {sshbuf1, sshbuf2, sshbuf3, sshbuf4, sshbuf5};
935
    uint32_t sshlens[5] = {sizeof(sshbuf1) - 1, sizeof(sshbuf2), sizeof(sshbuf3), sizeof(sshbuf4), sizeof(sshbuf5)};
936

937
    memset(&tv, 0x00, sizeof(tv));
938

939
    StreamTcpUTInit(&ra_ctx);
940
    StreamTcpUTInitInline();
941
    StreamTcpUTSetupSession(&ssn);
942
    StreamTcpUTSetupStream(&ssn.server, 1);
943
    StreamTcpUTSetupStream(&ssn.client, 1);
944

945
    f = UTHBuildFlow(AF_INET, "1.1.1.1", "2.2.2.2", 1234, 2222);
946
    FAIL_IF_NULL(f);
947
    f->protoctx = &ssn;
948
    f->proto = IPPROTO_TCP;
949
    f->alproto = ALPROTO_SSH;
950

951
    p = PacketGetFromAlloc();
952
    FAIL_IF(unlikely(p == NULL));
953
    p->proto = IPPROTO_TCP;
954
    p->flow = f;
955

956
    uint32_t seq = 2;
957
    for (int i=0; i<5; i++) {
958
        FAIL_IF(StreamTcpUTAddSegmentWithPayload(&tv, ra_ctx, &ssn.client, seq, sshbufs[i], sshlens[i]) == -1);
959
        seq += sshlens[i];
960
        FAIL_IF(StreamTcpReassembleAppLayer(&tv, ra_ctx, &ssn, &ssn.client, p, UPDATE_DIR_PACKET) < 0);
961
    }
962

963
    void *ssh_state = f->alstate;
964
    FAIL_IF_NULL(ssh_state);
965
    void *tx = SCSshStateGetTx(ssh_state, 0);
966
    FAIL_IF(SCSshTxGetFlags(tx, STREAM_TOSERVER) != SshStateFinished);
967

968
    FAIL_IF(SSHParserTestUtilCheck("2.0", "MySSHClient-0.5.1", tx, STREAM_TOSERVER));
969

970
    UTHFreePacket(p);
971
    UTHFreeFlow(f);
972
    StreamTcpUTClearSession(&ssn);
973
    StreamTcpUTDeinit(ra_ctx);
974
    PASS;
975
}
976

977
/** \test Send toserver a banner and record in three chunks. */
978
static int SSHParserTest16(void)
979
{
980
    TcpReassemblyThreadCtx *ra_ctx = NULL;
981
    ThreadVars tv;
982
    TcpSession ssn;
983
    Flow *f = NULL;
984
    Packet *p = NULL;
985

986
    uint8_t sshbuf1[] = "SSH-";
987
    uint8_t sshbuf2[] = "2.0-MySSHClient-0.5.1\r\n";
988
    uint8_t sshbuf3[] = { 0x00, 0x00, 0x00, 0x03,0x01, 21, 0x00};
989

990
    uint8_t* sshbufs[3] = {sshbuf1, sshbuf2, sshbuf3};
991
    uint32_t sshlens[3] = {sizeof(sshbuf1) - 1, sizeof(sshbuf2) - 1, sizeof(sshbuf3)};
992

993
    memset(&tv, 0x00, sizeof(tv));
994

995
    StreamTcpUTInit(&ra_ctx);
996
    StreamTcpUTInitInline();
997
    StreamTcpUTSetupSession(&ssn);
998
    StreamTcpUTSetupStream(&ssn.server, 1);
999
    StreamTcpUTSetupStream(&ssn.client, 1);
1000

1001
    f = UTHBuildFlow(AF_INET, "1.1.1.1", "2.2.2.2", 1234, 2222);
1002
    FAIL_IF_NULL(f);
1003
    f->protoctx = &ssn;
1004
    f->proto = IPPROTO_TCP;
1005
    f->alproto = ALPROTO_SSH;
1006

1007
    p = PacketGetFromAlloc();
1008
    FAIL_IF(unlikely(p == NULL));
1009
    p->proto = IPPROTO_TCP;
1010
    p->flow = f;
1011

1012
    uint32_t seq = 2;
1013
    for (int i=0; i<3; i++) {
1014
        FAIL_IF(StreamTcpUTAddSegmentWithPayload(&tv, ra_ctx, &ssn.server, seq, sshbufs[i], sshlens[i]) == -1);
1015
        seq += sshlens[i];
1016
        FAIL_IF(StreamTcpReassembleAppLayer(&tv, ra_ctx, &ssn, &ssn.server, p, UPDATE_DIR_PACKET) < 0);
1017
    }
1018

1019
    void *ssh_state = f->alstate;
1020
    FAIL_IF_NULL(ssh_state);
1021
    void *tx = SCSshStateGetTx(ssh_state, 0);
1022
    FAIL_IF(SCSshTxGetFlags(tx, STREAM_TOCLIENT) != SshStateFinished);
1023

1024
    FAIL_IF(SSHParserTestUtilCheck("2.0", "MySSHClient-0.5.1", tx, STREAM_TOCLIENT));
1025

1026
    UTHFreePacket(p);
1027
    UTHFreeFlow(f);
1028
    StreamTcpUTClearSession(&ssn);
1029
    StreamTcpUTDeinit(ra_ctx);
1030
    PASS;
1031
}
1032

1033
/** \test Send toserver a banner and 2 records record in four chunks. */
1034
static int SSHParserTest17(void)
1035
{
1036
    TcpReassemblyThreadCtx *ra_ctx = NULL;
1037
    ThreadVars tv;
1038
    TcpSession ssn;
1039
    Flow *f = NULL;
1040
    Packet *p = NULL;
1041

1042
    uint8_t sshbuf1[] = "SSH-";
1043
    uint8_t sshbuf2[] = "2.0-MySSHClient-0.5.1\r\n";
1044
    uint8_t sshbuf3[] = { 0x00, 0x00, 0x00, 0x03, 0x01, 17, 0x00};
1045
    uint8_t sshbuf4[] = { 0x00, 0x00, 0x00, 0x03, 0x01, 21, 0x00};
1046

1047
    uint8_t* sshbufs[4] = {sshbuf1, sshbuf2, sshbuf3, sshbuf4};
1048
    uint32_t sshlens[4] = {sizeof(sshbuf1) - 1, sizeof(sshbuf2) - 1, sizeof(sshbuf3), sizeof(sshbuf4)};
1049

1050
    memset(&tv, 0x00, sizeof(tv));
1051

1052
    StreamTcpUTInit(&ra_ctx);
1053
    StreamTcpUTInitInline();
1054
    StreamTcpUTSetupSession(&ssn);
1055
    StreamTcpUTSetupStream(&ssn.server, 1);
1056
    StreamTcpUTSetupStream(&ssn.client, 1);
1057

1058
    f = UTHBuildFlow(AF_INET, "1.1.1.1", "2.2.2.2", 1234, 2222);
1059
    FAIL_IF_NULL(f);
1060
    f->protoctx = &ssn;
1061
    f->proto = IPPROTO_TCP;
1062
    f->alproto = ALPROTO_SSH;
1063

1064
    p = PacketGetFromAlloc();
1065
    FAIL_IF(unlikely(p == NULL));
1066
    p->proto = IPPROTO_TCP;
1067
    p->flow = f;
1068

1069
    uint32_t seq = 2;
1070
    for (int i=0; i<4; i++) {
1071
        FAIL_IF(StreamTcpUTAddSegmentWithPayload(&tv, ra_ctx, &ssn.server, seq, sshbufs[i], sshlens[i]) == -1);
1072
        seq += sshlens[i];
1073
        FAIL_IF(StreamTcpReassembleAppLayer(&tv, ra_ctx, &ssn, &ssn.server, p, UPDATE_DIR_PACKET) < 0);
1074
    }
1075

1076
    void *ssh_state = f->alstate;
1077
    FAIL_IF_NULL(ssh_state);
1078
    void *tx = SCSshStateGetTx(ssh_state, 0);
1079
    FAIL_IF(SCSshTxGetFlags(tx, STREAM_TOCLIENT) != SshStateFinished);
1080

1081
    FAIL_IF(SSHParserTestUtilCheck("2.0", "MySSHClient-0.5.1", tx, STREAM_TOCLIENT));
1082

1083
    UTHFreePacket(p);
1084
    UTHFreeFlow(f);
1085
    StreamTcpUTClearSession(&ssn);
1086
    StreamTcpUTDeinit(ra_ctx);
1087
    PASS;
1088
}
1089

1090
/** \test 2 directional test */
1091
static int SSHParserTest18(void)
1092
{
1093
    TcpReassemblyThreadCtx *ra_ctx = NULL;
1094
    ThreadVars tv;
1095
    TcpSession ssn;
1096
    Flow *f = NULL;
1097
    Packet *p = NULL;
1098

1099
    uint8_t server1[] = "SSH-2.0-OpenSSH_4.7p1 Debian-8ubuntu3\r\n";
1100
    uint8_t sshbuf1[] = "SSH-";
1101
    uint8_t sshbuf2[] = "2.0-MySSHClient-0.5.1\r\n";
1102
    uint8_t server2[] = { 0x00, 0x00, 0x00, 0x03, 0x01, 21, 0x00 };
1103
    uint8_t sshbuf3[] = { 0x00, 0x00, 0x00, 0x03, 0x01, 21, 0x00 };
1104

1105

1106
    memset(&tv, 0x00, sizeof(tv));
1107

1108
    StreamTcpUTInit(&ra_ctx);
1109
    StreamTcpUTInitInline();
1110
    StreamTcpUTSetupSession(&ssn);
1111
    StreamTcpUTSetupStream(&ssn.server, 1);
1112
    StreamTcpUTSetupStream(&ssn.client, 1);
1113

1114
    uint8_t* sshbufs[5] = {server1, sshbuf1, sshbuf2, server2, sshbuf3};
1115
    uint32_t sshlens[5] = {sizeof(server1) - 1, sizeof(sshbuf1) - 1, sizeof(sshbuf2) -1, sizeof(server2) - 1, sizeof(sshbuf3)};
1116
    bool sshdirs[5] = {true, false, false, true, false};
1117

1118
    f = UTHBuildFlow(AF_INET, "1.1.1.1", "2.2.2.2", 1234, 2222);
1119
    FAIL_IF_NULL(f);
1120
    f->protoctx = &ssn;
1121
    f->proto = IPPROTO_TCP;
1122
    f->alproto = ALPROTO_SSH;
1123

1124
    p = PacketGetFromAlloc();
1125
    FAIL_IF(unlikely(p == NULL));
1126
    p->proto = IPPROTO_TCP;
1127
    p->flow = f;
1128

1129
    uint32_t seqcli = 2;
1130
    uint32_t seqsrv = 2;
1131
    for (int i=0; i<5; i++) {
1132
        if (sshdirs[i]) {
1133
            FAIL_IF(StreamTcpUTAddSegmentWithPayload(&tv, ra_ctx, &ssn.server, seqsrv, sshbufs[i], sshlens[i]) == -1);
1134
            seqsrv += sshlens[i];
1135
            FAIL_IF(StreamTcpReassembleAppLayer(&tv, ra_ctx, &ssn,  &ssn.server, p, UPDATE_DIR_PACKET) < 0);
1136
        } else {
1137
            FAIL_IF(StreamTcpUTAddSegmentWithPayload(&tv, ra_ctx,  &ssn.client, seqcli, sshbufs[i], sshlens[i]) == -1);
1138
            seqcli += sshlens[i];
1139
            FAIL_IF(StreamTcpReassembleAppLayer(&tv, ra_ctx, &ssn, &ssn.client, p, UPDATE_DIR_PACKET) < 0);
1140
        }
1141
    }
1142

1143
    void *ssh_state = f->alstate;
1144
    FAIL_IF_NULL(ssh_state);
1145
    void *tx = SCSshStateGetTx(ssh_state, 0);
1146
    FAIL_IF(SCSshTxGetFlags(tx, STREAM_TOCLIENT) != SshStateFinished);
1147

1148
    FAIL_IF(!(SCAppLayerParserStateIssetFlag(f->alparser, APP_LAYER_PARSER_NO_INSPECTION)));
1149

1150
    UTHFreePacket(p);
1151
    UTHFreeFlow(f);
1152
    StreamTcpUTClearSession(&ssn);
1153
    StreamTcpUTDeinit(ra_ctx);
1154
    PASS;
1155
}
1156

1157
/** \test Really long banner handling: bannel exactly 255 */
1158
static int SSHParserTest19(void)
1159
{
1160
    TcpReassemblyThreadCtx *ra_ctx = NULL;
1161
    ThreadVars tv;
1162
    TcpSession ssn;
1163
    Flow *f = NULL;
1164
    Packet *p = NULL;
1165

1166
    uint8_t sshbuf1[] = "SSH-";
1167
    uint8_t sshbuf2[] = "2.0-";
1168
    uint8_t sshbuf3[] = "abcdefghijklmnopqrstuvwxyz"
1169
                        "abcdefghijklmnopqrstuvwxyz"//60
1170
                        "abcdefghijklmnopqrstuvwxyz"
1171
                        "abcdefghijklmnopqrstuvwxyz"//112
1172
                        "abcdefghijklmnopqrstuvwxyz"
1173
                        "abcdefghijklmnopqrstuvwxyz"//164
1174
                        "abcdefghijklmnopqrstuvwxyz"
1175
                        "abcdefghijklmnopqrstuvwxyz"//216
1176
                        "abcdefghijklmnopqrstuvwxyz"//242
1177
                        "abcdefghijkl\r";//255
1178
    uint8_t sshbuf4[] = { 0x00, 0x00, 0x00, 0x03, 0x01, 21, 0x00};
1179

1180
    uint8_t* sshbufs[4] = {sshbuf1, sshbuf2, sshbuf3, sshbuf4};
1181
    uint32_t sshlens[4] = {sizeof(sshbuf1) - 1, sizeof(sshbuf2) - 1, sizeof(sshbuf3) - 1, sizeof(sshbuf4)};
1182

1183
    memset(&tv, 0x00, sizeof(tv));
1184

1185
    StreamTcpUTInit(&ra_ctx);
1186
    StreamTcpUTInitInline();
1187
    StreamTcpUTSetupSession(&ssn);
1188
    StreamTcpUTSetupStream(&ssn.server, 1);
1189
    StreamTcpUTSetupStream(&ssn.client, 1);
1190

1191
    f = UTHBuildFlow(AF_INET, "1.1.1.1", "2.2.2.2", 1234, 2222);
1192
    FAIL_IF_NULL(f);
1193
    f->protoctx = &ssn;
1194
    f->proto = IPPROTO_TCP;
1195
    f->alproto = ALPROTO_SSH;
1196

1197
    p = PacketGetFromAlloc();
1198
    FAIL_IF(unlikely(p == NULL));
1199
    p->proto = IPPROTO_TCP;
1200
    p->flow = f;
1201

1202
    uint32_t seq = 2;
1203
    for (int i=0; i<4; i++) {
1204
        FAIL_IF(StreamTcpUTAddSegmentWithPayload(&tv, ra_ctx, &ssn.server, seq, sshbufs[i], sshlens[i]) == -1);
1205
        seq += sshlens[i];
1206
        FAIL_IF(StreamTcpReassembleAppLayer(&tv, ra_ctx, &ssn, &ssn.server, p, UPDATE_DIR_PACKET) < 0);
1207
    }
1208

1209
    void *ssh_state = f->alstate;
1210
    FAIL_IF_NULL(ssh_state);
1211
    void *tx = SCSshStateGetTx(ssh_state, 0);
1212
    FAIL_IF(SCSshTxGetFlags(tx, STREAM_TOCLIENT) != SshStateFinished);
1213

1214
    sshbuf3[sizeof(sshbuf3) - 2] = 0;
1215
    FAIL_IF(SSHParserTestUtilCheck("2.0", (char *)sshbuf3, tx, STREAM_TOCLIENT));
1216

1217
    UTHFreePacket(p);
1218
    UTHFreeFlow(f);
1219
    StreamTcpUTClearSession(&ssn);
1220
    StreamTcpUTDeinit(ra_ctx);
1221
    PASS;
1222
}
1223

1224
/** \test Really long banner handling: banner exactly 255,
1225
 *        followed by malformed record */
1226
static int SSHParserTest20(void)
1227
{
1228
    TcpReassemblyThreadCtx *ra_ctx = NULL;
1229
    ThreadVars tv;
1230
    TcpSession ssn;
1231
    Flow *f = NULL;
1232
    Packet *p = NULL;
1233

1234
    uint8_t sshbuf1[] = "SSH-";
1235
    uint8_t sshbuf2[] = "2.0-";
1236
    uint8_t sshbuf3[] = "abcdefghijklmnopqrstuvwxyz"
1237
                        "abcdefghijklmnopqrstuvwxyz"//60
1238
                        "abcdefghijklmnopqrstuvwxyz"
1239
                        "abcdefghijklmnopqrstuvwxyz"//112
1240
                        "abcdefghijklmnopqrstuvwxyz"
1241
                        "abcdefghijklmnopqrstuvwxyz"//164
1242
                        "abcdefghijklmnopqrstuvwxyz"
1243
                        "abcdefghijklmnopqrstuvwxyz"//216
1244
                        "abcdefghijklmnopqrstuvwxyz"//242
1245
                        "abcdefghijklm\r";//256
1246
    uint8_t sshbuf4[] = {'a','b','c','d','e','f', '\r',
1247
                         0x00, 0x00, 0x00, 0x06, 0x01, 21, 0x00, 0x00, 0x00};
1248

1249
    uint8_t* sshbufs[4] = {sshbuf1, sshbuf2, sshbuf3, sshbuf4};
1250
    uint32_t sshlens[4] = {sizeof(sshbuf1) - 1, sizeof(sshbuf2) - 1, sizeof(sshbuf3) - 1, sizeof(sshbuf4) - 1};
1251

1252
    memset(&tv, 0x00, sizeof(tv));
1253

1254
    StreamTcpUTInit(&ra_ctx);
1255
    StreamTcpUTInitInline();
1256
    StreamTcpUTSetupSession(&ssn);
1257
    StreamTcpUTSetupStream(&ssn.server, 1);
1258
    StreamTcpUTSetupStream(&ssn.client, 1);
1259

1260
    f = UTHBuildFlow(AF_INET, "1.1.1.1", "2.2.2.2", 1234, 2222);
1261
    FAIL_IF_NULL(f);
1262
    f->protoctx = &ssn;
1263
    f->proto = IPPROTO_TCP;
1264
    f->alproto = ALPROTO_SSH;
1265

1266
    p = PacketGetFromAlloc();
1267
    FAIL_IF(unlikely(p == NULL));
1268
    p->proto = IPPROTO_TCP;
1269
    p->flow = f;
1270

1271
    uint32_t seq = 2;
1272
    for (int i=0; i<4; i++) {
1273
        FAIL_IF(StreamTcpUTAddSegmentWithPayload(&tv, ra_ctx, &ssn.server, seq, sshbufs[i], sshlens[i]) == -1);
1274
        seq += sshlens[i];
1275
        FAIL_IF(StreamTcpReassembleAppLayer(&tv, ra_ctx, &ssn, &ssn.server, p, UPDATE_DIR_PACKET) < 0);
1276
    }
1277

1278
    void *ssh_state = f->alstate;
1279
    FAIL_IF_NULL(ssh_state);
1280
    void *tx = SCSshStateGetTx(ssh_state, 0);
1281
    FAIL_IF(SCSshTxGetFlags(tx, STREAM_TOCLIENT) != SshStateFinished);
1282

1283
    FAIL_IF(SSHParserTestUtilCheck("2.0", NULL, tx, STREAM_TOCLIENT));
1284

1285
    UTHFreePacket(p);
1286
    UTHFreeFlow(f);
1287
    StreamTcpUTClearSession(&ssn);
1288
    StreamTcpUTDeinit(ra_ctx);
1289
    PASS;
1290
}
1291

1292
/** \test Fragmented banner handling: chunk has final part of bannel plus
1293
 *        a record. */
1294
static int SSHParserTest21(void)
1295
{
1296
    TcpReassemblyThreadCtx *ra_ctx = NULL;
1297
    ThreadVars tv;
1298
    TcpSession ssn;
1299
    Flow *f = NULL;
1300
    Packet *p = NULL;
1301

1302
    uint8_t sshbuf1[] = "SSH-";
1303
    uint8_t sshbuf2[] = "2.0-";
1304
    uint8_t sshbuf3[] = "abcdefghijklmnopqrstuvwxyz"
1305
                        "abcdefghijklmnopqrstuvwxyz"//60
1306
                        "abcdefghijklmnopqrstuvwxyz"
1307
                        "abcdefghijklmnopqrstuvwxyz"//112
1308
                        "abcdefghijklmnopqrstuvwxyz"
1309
                        "abcdefghijklmnopqrstuvwxyz"//164
1310
                        "abcdefghijklmnopqrstuvwxyz"
1311
                        "abcdefghijklmnopqrstuvwxyz"//216
1312
                        "abcdefghijklmnopqrstuvwxy";//241
1313
    uint8_t sshbuf4[] = {'l','i','b','s','s','h', '\r',
1314
                         0x00, 0x00, 0x00, 0x06, 0x01, 21, 0x00, 0x00, 0x00};
1315

1316
    uint8_t* sshbufs[4] = {sshbuf1, sshbuf2, sshbuf3, sshbuf4};
1317
    uint32_t sshlens[4] = {sizeof(sshbuf1) - 1, sizeof(sshbuf2) - 1, sizeof(sshbuf3) - 1, sizeof(sshbuf4)};
1318

1319
    memset(&tv, 0x00, sizeof(tv));
1320

1321
    StreamTcpUTInit(&ra_ctx);
1322
    StreamTcpUTInitInline();
1323
    StreamTcpUTSetupSession(&ssn);
1324
    StreamTcpUTSetupStream(&ssn.server, 1);
1325
    StreamTcpUTSetupStream(&ssn.client, 1);
1326

1327
    f = UTHBuildFlow(AF_INET, "1.1.1.1", "2.2.2.2", 1234, 2222);
1328
    FAIL_IF_NULL(f);
1329
    f->protoctx = &ssn;
1330
    f->proto = IPPROTO_TCP;
1331
    f->alproto = ALPROTO_SSH;
1332

1333
    p = PacketGetFromAlloc();
1334
    FAIL_IF(unlikely(p == NULL));
1335
    p->proto = IPPROTO_TCP;
1336
    p->flow = f;
1337

1338
    uint32_t seq = 2;
1339
    for (int i=0; i<4; i++) {
1340
        FAIL_IF(StreamTcpUTAddSegmentWithPayload(&tv, ra_ctx, &ssn.server, seq, sshbufs[i], sshlens[i]) == -1);
1341
        seq += sshlens[i];
1342
        FAIL_IF(StreamTcpReassembleAppLayer(&tv, ra_ctx, &ssn, &ssn.server, p, UPDATE_DIR_PACKET) < 0);
1343
    }
1344

1345
    void *ssh_state = f->alstate;
1346
    FAIL_IF_NULL(ssh_state);
1347
    void *tx = SCSshStateGetTx(ssh_state, 0);
1348
    FAIL_IF(SCSshTxGetFlags(tx, STREAM_TOCLIENT) != SshStateFinished);
1349

1350
    FAIL_IF(SSHParserTestUtilCheck("2.0", NULL, tx, STREAM_TOCLIENT));
1351

1352
    UTHFreePacket(p);
1353
    UTHFreeFlow(f);
1354
    StreamTcpUTClearSession(&ssn);
1355
    StreamTcpUTDeinit(ra_ctx);
1356
    PASS;
1357
}
1358

1359
/** \test Fragmented banner handling: chunk has final part of bannel plus
1360
 *        a record. */
1361
static int SSHParserTest22(void)
1362
{
1363
    TcpReassemblyThreadCtx *ra_ctx = NULL;
1364
    ThreadVars tv;
1365
    TcpSession ssn;
1366
    Flow *f = NULL;
1367
    Packet *p = NULL;
1368

1369
    uint8_t sshbuf1[] = "SSH-";
1370
    uint8_t sshbuf2[] = "2.0-";
1371
    uint8_t sshbuf3[] = {
1372
        'l', 'i', 'b', 's', 's', 'h', '\r', // 7
1373

1374
        0x00, 0x00, 0x00, 0x06, 0x01, 17, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x01, 17,
1375
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x01, 17, 0x00, 0x00, 0x00, 0x00, 0x00,
1376
        0x00, 0x00, 0x06, 0x01, 17, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x01, 17, 0x00,
1377
        0x00, 0x00, 0x00, // 50
1378

1379
        0x00, 0x00, 0x00, 0x06, 0x01, 17, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x01, 17,
1380
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x01, 17, 0x00, 0x00, 0x00, 0x00, 0x00,
1381
        0x00, 0x00, 0x06, 0x01, 17, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x01, 17, 0x00,
1382
        0x00, 0x00, 0x00, // 100
1383

1384
        0x00, 0x00, 0x00, 0x06, 0x01, 17, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x01, 17,
1385
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x01, 17, 0x00, 0x00, 0x00, 0x00, 0x00,
1386
        0x00, 0x00, 0x06, 0x01, 17, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x01, 17, 0x00,
1387
        0x00, 0x00, 0x00, // 150
1388

1389
        0x00, 0x00, 0x00, 0x06, 0x01, 17, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x01, 17,
1390
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x01, 17, 0x00, 0x00, 0x00, 0x00, 0x00,
1391
        0x00, 0x00, 0x06, 0x01, 17, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x01, 17, 0x00,
1392
        0x00, 0x00, 0x00, // 200
1393

1394
        0x00, 0x00, 0x00, 0x06, 0x01, 17, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x01, 17,
1395
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x01, 17, 0x00, 0x00, 0x00, 0x00, 0x00,
1396
        0x00, 0x00, 0x06, 0x01, 17, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x01, 17, 0x00,
1397
        0x00, 0x00, 0x00, // 250
1398

1399
        0x00, 0x00, 0x00, 0x06, 0x01, 17, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x01, 17,
1400
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x01, 17, 0x00, 0x00, 0x00, 0x00, 0x00,
1401
        0x00, 0x00, 0x06, 0x01, 17, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x01, 21, 0x00,
1402
        0x00, 0x00, 0x00, // 300
1403
    };
1404

1405
    uint8_t *sshbufs[3] = { sshbuf1, sshbuf2, sshbuf3 };
1406
    uint32_t sshlens[3] = { sizeof(sshbuf1) - 1, sizeof(sshbuf2) - 1, sizeof(sshbuf3) - 1 };
1407

1408
    memset(&tv, 0x00, sizeof(tv));
1409

1410
    StreamTcpUTInit(&ra_ctx);
1411
    StreamTcpUTInitInline();
1412
    StreamTcpUTSetupSession(&ssn);
1413
    StreamTcpUTSetupStream(&ssn.server, 1);
1414
    StreamTcpUTSetupStream(&ssn.client, 1);
1415

1416
    f = UTHBuildFlow(AF_INET, "1.1.1.1", "2.2.2.2", 1234, 2222);
1417
    FAIL_IF_NULL(f);
1418
    f->protoctx = &ssn;
1419
    f->proto = IPPROTO_TCP;
1420
    f->alproto = ALPROTO_SSH;
1421

1422
    p = PacketGetFromAlloc();
1423
    FAIL_IF(unlikely(p == NULL));
1424
    p->proto = IPPROTO_TCP;
1425
    p->flow = f;
1426

1427
    uint32_t seq = 2;
1428
    for (int i = 0; i < 3; i++) {
1429
        FAIL_IF(StreamTcpUTAddSegmentWithPayload(
1430
                        &tv, ra_ctx, &ssn.server, seq, sshbufs[i], sshlens[i]) == -1);
1431
        seq += sshlens[i];
1432
        FAIL_IF(StreamTcpReassembleAppLayer(&tv, ra_ctx, &ssn, &ssn.server, p, UPDATE_DIR_PACKET) <
1433
                0);
1434
    }
1435

1436
    void *ssh_state = f->alstate;
1437
    FAIL_IF_NULL(ssh_state);
1438
    void *tx = SCSshStateGetTx(ssh_state, 0);
1439
    FAIL_IF(SCSshTxGetFlags(tx, STREAM_TOCLIENT) != SshStateFinished);
1440

1441
    FAIL_IF(SSHParserTestUtilCheck("2.0", "libssh", tx, STREAM_TOCLIENT));
1442

1443
    UTHFreePacket(p);
1444
    UTHFreeFlow(f);
1445
    StreamTcpUTClearSession(&ssn);
1446
    StreamTcpUTDeinit(ra_ctx);
1447
    PASS;
1448
}
1449

1450
/** \test Send a version string in one chunk (client version str). */
1451
static int SSHParserTest23(void)
1452
{
1453
    int result = 0;
1454
    Flow f;
1455
    uint8_t sshbuf[] = "SSH-2.0\r-MySSHClient-0.5.1\n";
1456
    uint32_t sshlen = sizeof(sshbuf) - 1;
1457
    TcpSession ssn;
1458
    AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
1459

1460
    memset(&f, 0, sizeof(f));
1461
    memset(&ssn, 0, sizeof(ssn));
1462
    FLOW_INITIALIZE(&f);
1463
    f.protoctx = (void *)&ssn;
1464
    f.proto = IPPROTO_TCP;
1465
    f.alproto = ALPROTO_SSH;
1466

1467
    StreamTcpInitConfig(true);
1468

1469
    int r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SSH,
1470
                                STREAM_TOSERVER | STREAM_EOF, sshbuf, sshlen);
1471
    if (r == 0) {
1472
        printf("toclient chunk 1 returned 0 expected non null: ");
1473
        goto end;
1474
    }
1475

1476
    result = 1;
1477
end:
1478
    if (alp_tctx != NULL)
1479
        AppLayerParserThreadCtxFree(alp_tctx);
1480
    StreamTcpFreeConfig(true);
1481
    FLOW_DESTROY(&f);
1482
    return result;
1483
}
1484

1485
/** \test Send a version string in one chunk (client version str). */
1486
static int SSHParserTest24(void)
1487
{
1488
    int result = 0;
1489
    Flow f;
1490
    uint8_t sshbuf[] = "SSH-2.0-\rMySSHClient-0.5.1\n";
1491
    uint32_t sshlen = sizeof(sshbuf) - 1;
1492
    TcpSession ssn;
1493
    AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
1494

1495
    memset(&f, 0, sizeof(f));
1496
    memset(&ssn, 0, sizeof(ssn));
1497
    FLOW_INITIALIZE(&f);
1498
    f.protoctx = (void *)&ssn;
1499
    f.proto = IPPROTO_TCP;
1500
    f.alproto = ALPROTO_SSH;
1501

1502
    StreamTcpInitConfig(true);
1503

1504
    int r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SSH,
1505
                                STREAM_TOSERVER | STREAM_EOF, sshbuf, sshlen);
1506
    if (r != 0) {
1507
        printf("toclient chunk 1 returned %" PRId32 ", expected 0: ", r);
1508
        goto end;
1509
    }
1510

1511
    void *ssh_state = f.alstate;
1512
    if (ssh_state == NULL) {
1513
        printf("no ssh state: ");
1514
        goto end;
1515
    }
1516
    void *tx = SCSshStateGetTx(ssh_state, 0);
1517
    if (SCSshTxGetFlags(tx, STREAM_TOSERVER) != SshStateBannerDone) {
1518
        printf("Didn't detect the msg code of new keys (ciphered data starts): ");
1519
        goto end;
1520
    }
1521
    if (SSHParserTestUtilCheck("2.0", NULL, tx, STREAM_TOSERVER))
1522
        goto end;
1523

1524
    result = 1;
1525
end:
1526
    FLOW_DESTROY(&f);
1527
    if (alp_tctx != NULL)
1528
        AppLayerParserThreadCtxFree(alp_tctx);
1529
    StreamTcpFreeConfig(true);
1530
    return result;
1531
}
1532

1533
/** \test Send a malformed banner */
1534
static int SSHParserTest25(void)
1535
{
1536
    Flow f;
1537
    uint8_t sshbuf[] = "\n";
1538
    uint32_t sshlen = sizeof(sshbuf) - 1;
1539
    TcpSession ssn;
1540
    AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
1541
    FAIL_IF_NULL(alp_tctx);
1542

1543
    memset(&f, 0, sizeof(f));
1544
    memset(&ssn, 0, sizeof(ssn));
1545
    FLOW_INITIALIZE(&f);
1546
    f.protoctx = (void *)&ssn;
1547
    f.proto = IPPROTO_TCP;
1548
    f.alproto = ALPROTO_SSH;
1549

1550
    StreamTcpInitConfig(true);
1551

1552
    int r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SSH,
1553
                                STREAM_TOSERVER | STREAM_EOF, sshbuf, sshlen);
1554
    FAIL_IF(r != -1);
1555

1556
    void *ssh_state = f.alstate;
1557
    FAIL_IF_NULL(ssh_state);
1558
    void *tx = SCSshStateGetTx(ssh_state, 0);
1559
    FAIL_IF(SCSshTxGetFlags(tx, STREAM_TOSERVER) == SshStateBannerDone);
1560
    const uint8_t *dummy = NULL;
1561
    uint32_t dummy_len = 0;
1562
    FAIL_IF(SCSshTxGetSoftware(tx, STREAM_TOCLIENT, &dummy, &dummy_len) != 0);
1563

1564
    FLOW_DESTROY(&f);
1565
    AppLayerParserThreadCtxFree(alp_tctx);
1566
    StreamTcpFreeConfig(true);
1567
    PASS;
1568
}
1569

1570
#endif /* UNITTESTS */
1571

1572
void SSHParserRegisterTests(void)
UNCOV
1573
{
×
1574
#ifdef UNITTESTS
1575
    UtRegisterTest("SSHParserTest01 - ToServer", SSHParserTest01);
1576
    UtRegisterTest("SSHParserTest02 - ToServer", SSHParserTest02);
1577
    UtRegisterTest("SSHParserTest03 - ToServer", SSHParserTest03);
1578
    UtRegisterTest("SSHParserTest04 - ToClient", SSHParserTest04);
1579
    UtRegisterTest("SSHParserTest05 - ToClient", SSHParserTest05);
1580
    UtRegisterTest("SSHParserTest06 - ToClient", SSHParserTest06);
1581
    UtRegisterTest("SSHParserTest07 - ToServer 2 chunks", SSHParserTest07);
1582
    UtRegisterTest("SSHParserTest08 - ToServer 3 chunks", SSHParserTest08);
1583
    UtRegisterTest("SSHParserTest09 - ToClient 2 chunks", SSHParserTest09);
1584
    UtRegisterTest("SSHParserTest10 - ToClient 3 chunks", SSHParserTest10);
1585
    UtRegisterTest("SSHParserTest11 - ToClient 4 chunks", SSHParserTest11);
1586
    UtRegisterTest("SSHParserTest12 - ToClient 4 chunks", SSHParserTest12);
1587
    UtRegisterTest("SSHParserTest13 - ToClient 4 chunks", SSHParserTest13);
1588
    UtRegisterTest("SSHParserTest14 - ToClient 4 chunks", SSHParserTest14);
1589
    UtRegisterTest("SSHParserTest15", SSHParserTest15);
1590
    UtRegisterTest("SSHParserTest16", SSHParserTest16);
1591
    UtRegisterTest("SSHParserTest17", SSHParserTest17);
1592
    UtRegisterTest("SSHParserTest18", SSHParserTest18);
1593
    UtRegisterTest("SSHParserTest19", SSHParserTest19);
1594
    UtRegisterTest("SSHParserTest20", SSHParserTest20);
1595
    UtRegisterTest("SSHParserTest21", SSHParserTest21);
1596
    UtRegisterTest("SSHParserTest22", SSHParserTest22);
1597
    UtRegisterTest("SSHParserTest23", SSHParserTest23);
1598
    UtRegisterTest("SSHParserTest24", SSHParserTest24);
1599
    UtRegisterTest("SSHParserTest25", SSHParserTest25);
1600
#endif /* UNITTESTS */
UNCOV
1601
}
×
1602

STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc