aboutsummaryrefslogtreecommitdiffstats
path: root/framework/src/suricata/src/detect-ftpbounce.c
blob: 39e1c5a2a027ff7035c72b497e82350a7c793f0d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
@media only all and (prefers-color-scheme: dark) {
.highlight .hll { background-color: #49483e }
.highlight .c { color: #75715e } /* Comment */
.highlight .err { color: #960050; background-color: #1e0010 } /* Error */
.highlight .k { color: #66d9ef } /* Keyword */
.highlight .l { color: #ae81ff } /* Literal */
.highlight .n { color: #f8f8f2 } /* Name */
.highlight .o { color: #f92672 } /* Operator */
.highlight .p { color: #f8f8f2 } /* Punctuation */
.highlight .ch { color: #75715e } /* Comment.Hashbang */
.highlight .cm { color: #75715e } /* Comment.Multiline */
.highlight .cp { color: #75715e } /* Comment.Preproc */
.highlight .cpf { color: #75715e } /* Comment.PreprocFile */
.highlight .c1 { color: #75715e } /* Comment.Single */
.highlight .cs { color: #75715e } /* Comment.Special */
.highlight .gd { color: #f92672 } /* Generic.Deleted */
.highlight .ge { font-style: italic } /* Generic.Emph */
.highlight .gi { color: #a6e22e } /* Generic.Inserted */
.highlight .gs { font-weight: bold } /* Generic.Strong */
.highlight .gu { color: #75715e } /* Generic.Subheading */
.highlight .kc { color: #66d9ef } /* Keyword.Constant */
.highlight .kd { color: #66d9ef } /* Keyword.Declaration */
.highlight .kn { color: #f92672 } /* Keyword.Namespace */
.highlight .kp { color: #66d9ef } /* Keyword.Pseudo */
.highlight .kr { color: #66d9ef } /* Keyword.Reserved */
.highlight .kt { color: #66d9ef } /* Keyword.Type */
.highlight .ld { color: #e6db74 } /* Literal.Date */
.highlight .m { color: #ae81ff } /* Literal.Number */
.highlight .s { color: #e6db74 } /* Literal.String */
.highlight .na { color: #a6e22e } /* Name.Attribute */
.highlight .nb { color: #f8f8f2 } /* Name.Builtin */
.highlight .nc { color: #a6e22e } /* Name.Class */
.highlight .no { color: #66d9ef } /* Name.Constant */
.highlight .nd { color: #a6e22e } /* Name.Decorator */
.highlight .ni { color: #f8f8f2 } /* Name.Entity */
.highlight .ne { color: #a6e22e } /* Name.Exception */
.highlight .nf { color: #a6e22e } /* Name.Function */
.highlight .nl { color: #f8f8f2 } /* Name.Label */
.highlight .nn { color: #f8f8f2 } /* Name.Namespace */
.highlight .nx { color: #a6e22e } /* Name.Other */
.highlight .py { color: #f8f8f2 } /* Name.Property */
.highlight .nt { color: #f92672 } /* Name.Tag */
.highlight .nv { color: #f8f8f2 } /* Name.Variable */
.highlight .ow { color: #f92672 } /* Operator.Word */
.highlight .w { color: #f8f8f2 } /* Text.Whitespace */
.highlight .mb { color: #ae81ff } /* Literal.Number.Bin */
.highlight .mf { color: #ae81ff } /* Literal.Number.Float */
.highlight .mh { color: #ae81ff } /* Literal.Number.Hex */
.highlight .mi { color: #ae81ff } /* Literal.Number.Integer */
.highlight .mo { color: #ae81ff } /* Literal.Number.Oct */
.highlight .sa { color: #e6db74 } /* Literal.String.Affix */
.highlight .sb { color: #e6db74 } /* Literal.String.Backtick */
.highlight .sc { color: #e6db74 } /* Literal.String.Char */
.highlight .dl { color: #e6db74 } /* Literal.String.Delimiter */
.highlight .sd { color: #e6db74 } /* Literal.String.Doc */
.highlight .s2 { color: #e6db74 } /* Literal.String.Double */
.highlight .se { color: #ae81ff } /* Literal.String.Escape */
.highlight .sh { color: #e6db74 } /* Literal.String.Heredoc */
.highlight .si { color: #e6db74 } /* Literal.String.Interpol */
.highlight .sx { color: #e6db74 } /* Literal.String.Other */
.highlight .sr { color: #e6db74 } /* Literal.String.Regex */
.highlight .s1 { color: #e6db74 } /* Literal.String.Single */
.highlight .ss { color: #e6db74 } /* Literal.String.Symbol */
.highlight .bp { color: #f8f8f2 } /* Name.Builtin.Pseudo */
.highlight .fm { color: #a6e22e } /* Name.Function.Magic */
.highlight .vc { color: #f8f8f2 } /* Name.Variable.Class */
.highlight .vg { color: #f8f8f2 } /* Name.Variable.Global */
.highlight .vi { color: #f8f8f2 } /* Name.Variable.Instance */
.highlight .vm { color: #f8f8f2 } /* Name.Variable.Magic */
.highlight .il { color: #ae81ff } /* Literal.Number.Integer.Long */
}
@media (prefers-color-scheme: light) {
.highlight .hll { background-color: #ffffcc }
.highlight .c { color: #888888 } /* Comment */
.highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */
.highlight .k { color: #008800; font-weight: bold } /* Keyword */
.highlight .ch { color: #888888 } /* Comment.Hashbang */
.highlight .cm { color: #888888 } /* Comment.Multiline */
.highlight .cp { color: #cc0000; font-weight: bold } /* Comment.Preproc */
.highlight .cpf { color: #888888 } /* Comment.PreprocFile */
.highlight .c1 { color: #888888 } /* Comment.Single */
.highlight .cs { color: #cc0000; font-weight: bold; background-color: #fff0f0 } /* Comment.Special */
.highlight .gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */
.highlight .ge { font-style: italic } /* Generic.Emph */
.highlight .gr { color: #aa0000 } /* Generic.Error */
.highlight .gh { color: #333333 } /* Generic.Heading */
.highlight .gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */
.highlight .go { color: #888888 } /* Generic.Output */
.highlight .gp { color: #555555 } /* Generic.Prompt */
.highlight .gs { font-weight: bold } /* Generic.Strong */
.highlight .gu { color: #666666 } /* Generic.Subheading */
.highlight .gt { color: #aa0000 } /* Generic.Traceback */
.highlight .kc { color: #008800; font-weight: bold } /* Keyword.Constant */
.highlight .kd { color: #008800; font-weight: bold } /* Keyword.Declaration */
.highlight .kn { color: #008800; font-weight: bold } /* Keyword.Namespace */
.highlight .kp { color: #008800 } /* Keyword.Pseudo */
.highlight .kr { color: #008800; font-weight: bold } /* Keyword.Reserved */
.highlight .kt { color: #888888; font-weight: bold } /* Keyword.Type */
.highlight .m { color: #0000DD; font-weight: bold } /* Literal.Number */
.highlight .s { color: #dd2200; background-color: #fff0f0 } /* Literal.String */
.highlight .na { color: #336699 } /* Name.Attribute */
.highlight .nb { color: #003388 } /* Name.Builtin */
.highlight .nc { color: #bb0066; font-weight: bold } /* Name.Class */
.highlight .no { color: #003366; font-weight: bold } /* Name.Constant */
.highlight .nd { color: #555555 } /* Name.Decorator */
.highlight .ne { color: #bb0066; font-weight: bold } /* Name.Exception */
.highlight .nf { color: #0066bb; font-weight: bold } /* Name.Function */
.highlight .nl { color: #336699; font-style: italic } /* Name.Label */
.highlight .nn { color: #bb0066; font-weight: bold } /* Name.Namespace */
.highlight .py { color: #336699; font-weight: bold } /* Name.Property */
.highlight .nt { color: #bb0066; font-weight: bold } /* Name.Tag */
.highlight .nv { color: #336699 } /* Name.Variable */
.highlight .ow { color: #008800 } /* Operator.Word */
.highlight .w { color: #bbbbbb } /* Text.Whitespace */
.highlight .mb { color: #0000DD; font-weight: bold } /* Literal.Number.Bin */
.highlight .mf { color: #0000DD; font-weight: bold } /* Literal.Number.Float */
.highlight .mh { color: #0000DD; font-weight: bold } /* Literal.Number.Hex */
.highlight .mi { color: #0000DD; font-weight: bold } /* Literal.Number.Integer */
.highlight .mo { color: #0000DD; font-weight: bold } /* Literal.Number.Oct */
.highlight .sa { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Affix */
.highlight .sb { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Backtick */
.highlight .sc { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Char */
.highlight .dl { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Delimiter */
.highlight .sd { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Doc */
.highlight .s2 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Double */
.highlight .se { color: #0044dd; background-color: #fff0f0 } /* Literal.String.Escape */
.highlight .sh { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Heredoc */
.highlight .si { color: #3333bb; background-color: #fff0f0 } /* Literal.String.Interpol */
.highlight .sx { color: #22bb22; background-color: #f0fff0 } /* Literal.String.Other */
.highlight .sr { color: #008800; background-color: #fff0ff } /* Literal.String.Regex */
.highlight .s1 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Single */
.highlight .ss { color: #aa6600; background-color: #fff0f0 } /* Literal.String.Symbol */
.highlight .bp { color: #003388 } /* Name.Builtin.Pseudo */
.highlight .fm { color: #0066bb; font-weight: bold } /* Name.Function.Magic */
.highlight .vc { color: #336699 } /* Name.Variable.Class */
.highlight .vg { color: #dd7700 } /* Name.Variable.Global */
.highlight .vi { color: #3333bb } /* Name.Variable.Instance */
.highlight .vm { color: #336699 } /* Name.Variable.Magic */
.highlight .il { color: #0000DD; font-weight: bold } /* Literal.Number.Integer.Long */
}
#Dummy file used to store history of PRs
#Note this is only needed for triggering commits with no code change in
#Apex, but changes do occur in opnfv-tht
#PR number, PR Title
15,Add sleep to galera and mongodb service start
18,Fix sql race condition
21,Serialize db_sync calls and increase sql-sleep timer
23,Fix odl env files
25,Force metadata on all scenarios
26,Fixes ODL ML2 IP
30,Adds OVS DPDK config
33,Removes QOS service plugin from Neutron
35,Use nic1 as control plane inteface name instead of default br-ex
38,Fixes nova default floating pool to be 'external'
39,removing extra whitespace from congress parameter
40,Move mongo to step 1
42, congress typo
44,Fixes nova host/ip bug
43,Add support for live migration and resize
41,Add pcs cleanup exec
50,Fix rabbitmq ipv6 config
52,Add notifier topic to ceilometer
53,Add numa to controller hiera hierarchy
54,fix network mtu
56,fixes tacker config for heat_uri
57,Remove trailing newline from dpdk pci address
58,Enable Ceph on boot
61,Add dpdk bind lock file for vpp deployment
63,Fixes honeycomb on compute
62,Heat Domain
64,Fix missing metadata param
351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560
/* Copyright (C) 2007-2010 Open Information Security Foundation
 *
 * You can copy, redistribute or modify this Program under the terms of
 * the GNU General Public License version 2 as published by the Free
 * Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * version 2 along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
 * 02110-1301, USA.
 */

/**
 * \file
 *
 * \author Pablo Rincon <pablo.rincon.crespo@gmail.com>
 *
 * ftpbounce keyword, part of the detection engine.
 */

#include "suricata-common.h"
#include "debug.h"
#include "decode.h"
#include "detect.h"
#include "detect-parse.h"
#include "detect-engine.h"
#include "detect-engine-mpm.h"
#include "detect-engine-state.h"
#include "detect-content.h"

#include "app-layer.h"
#include "app-layer-parser.h"
#include "app-layer-ftp.h"
#include "util-unittest.h"
#include "util-unittest-helper.h"
#include "util-debug.h"
#include "flow.h"
#include "flow-var.h"
#include "flow-util.h"
#include "threads.h"
#include "detect-ftpbounce.h"
#include "stream-tcp.h"
#include "util-byte.h"

int DetectFtpbounceMatch(ThreadVars *, DetectEngineThreadCtx *, Packet *,
                          Signature *, SigMatch *);
int DetectFtpbounceALMatch(ThreadVars *, DetectEngineThreadCtx *, Flow *,
                           uint8_t, void *, Signature *, SigMatch *);
static int DetectFtpbounceSetup(DetectEngineCtx *, Signature *, char *);
int DetectFtpbounceMatchArgs(uint8_t *payload, uint16_t payload_len,
                             uint32_t ip_orig, uint16_t offset);
void DetectFtpbounceRegisterTests(void);
void DetectFtpbounceFree(void *);

/**
 * \brief Registration function for ftpbounce: keyword
 * \todo add support for no_stream and stream_only
 */
void DetectFtpbounceRegister(void)
{
    sigmatch_table[DETECT_FTPBOUNCE].name = "ftpbounce";
    sigmatch_table[DETECT_FTPBOUNCE].Setup = DetectFtpbounceSetup;
    sigmatch_table[DETECT_FTPBOUNCE].Match = NULL;
    sigmatch_table[DETECT_FTPBOUNCE].AppLayerMatch = DetectFtpbounceALMatch;
    sigmatch_table[DETECT_FTPBOUNCE].alproto = ALPROTO_FTP;
    sigmatch_table[DETECT_FTPBOUNCE].Free  = NULL;
    sigmatch_table[DETECT_FTPBOUNCE].RegisterTests = DetectFtpbounceRegisterTests;
    sigmatch_table[DETECT_FTPBOUNCE].flags = SIGMATCH_NOOPT;
    return;
}

/**
 * \brief This function is used to match ftpbounce attacks
 *
 * \param payload Payload of the PORT command
 * \param payload_len Length of the payload
 * \param ip_orig IP source to check the ftpbounce condition
 * \param offset offset to the arguments of the PORT command
 *
 * \retval 1 if ftpbounce detected, 0 if not
 */
int DetectFtpbounceMatchArgs(uint8_t *payload, uint16_t payload_len,
                             uint32_t ip_orig, uint16_t offset)
{
    SCEnter();
    SCLogDebug("Checking ftpbounce condition");
    char *c = NULL;
    uint16_t i = 0;
    int octet = 0;
    int octet_ascii_len = 0;
    int noctet = 0;
    uint32_t ip = 0;
    /* PrintRawDataFp(stdout, payload, payload_len); */

    if (payload_len < 7) {
        /* we need at least a differet ip address
         * in the format 1,2,3,4,x,y where x,y is the port
         * in two byte representation so let's look at
         * least for the IP octets in comma separated */
        return 0;
    }

    if (offset + 7 >= payload_len)
        return 0;

    c =(char*) payload;
    if (c == NULL) {
        SCLogDebug("No payload to check");
        return 0;
    }

    i = offset;
    /* Search for the first IP octect(Skips "PORT ") */
    while (i < payload_len && !isdigit((unsigned char)c[i])) i++;

    for (;i < payload_len && octet_ascii_len < 4 ;i++) {
        if (isdigit((unsigned char)c[i])) {
            octet =(c[i] - '0') + octet * 10;
            octet_ascii_len++;
        } else {
            if (octet > 256) {
                SCLogDebug("Octet not in ip format");
                return 0;
            }

            if (isspace((unsigned char)c[i]))
                while (i < payload_len && isspace((unsigned char)c[i]) ) i++;

            if (i < payload_len && c[i] == ',') { /* we have an octet */
                noctet++;
                octet_ascii_len = 0;
                ip =(ip << 8) + octet;
                octet = 0;
            } else {
                SCLogDebug("Unrecognized character '%c'", c[i]);
                return 0;
            }
            if (noctet == 4) {
                /* Different IP than src, ftp bounce scan */
                ip = SCByteSwap32(ip);

                if (ip != ip_orig) {
                    SCLogDebug("Different ip, so Matched ip:%d <-> ip_orig:%d",
                               ip, ip_orig);
                    return 1;
                }
                SCLogDebug("Same ip, so no match here");
                return 0;
            }
        }
    }
    SCLogDebug("No match");
    return 0;
}

/**
 * \brief This function is used to check matches from the FTP App Layer Parser
 *
 * \param t pointer to thread vars
 * \param det_ctx pointer to the pattern matcher thread
 * \param p pointer to the current packet
 * \param m pointer to the sigmatch but we don't use it since ftpbounce
 *          has no options
 * \retval 0 no match
 * \retval 1 match
 */
int DetectFtpbounceALMatch(ThreadVars *t, DetectEngineThreadCtx *det_ctx,
                           Flow *f, uint8_t flags, void *state, Signature *s,
                           SigMatch *m)
{
    SCEnter();
    FtpState *ftp_state =(FtpState *)state;
    if (ftp_state == NULL) {
        SCLogDebug("no ftp state, no match");
        SCReturnInt(0);
    }

    int ret = 0;

    if (ftp_state->command == FTP_COMMAND_PORT) {
        ret = DetectFtpbounceMatchArgs(ftp_state->port_line,
                  ftp_state->port_line_len, f->src.address.address_un_data32[0],
                  ftp_state->arg_offset);
    }

    SCReturnInt(ret);
}

/**
 * \brief this function is used to add the parsed ftpbounce
 *
 * \param de_ctx pointer to the Detection Engine Context
 * \param s pointer to the Current Signature
 * \param m pointer to the Current SigMatch
 * \param ftpbouncestr pointer to the user provided ftpbounce options
 *                     currently there are no options.
 *
 * \retval 0 on Success
 * \retval -1 on Failure
 */
int DetectFtpbounceSetup(DetectEngineCtx *de_ctx, Signature *s, char *ftpbouncestr)
{
    SCEnter();

    SigMatch *sm = NULL;

    sm = SigMatchAlloc();
    if (sm == NULL) {
        goto error;;
    }

    sm->type = DETECT_FTPBOUNCE;

    /* We don't need to allocate any data for ftpbounce here.
    *
    * TODO: As a suggestion, maybe we can add a flag in the flow
    * to set the stream as "bounce detected" for fast Match.
    * When you do a ftp bounce attack you usually use the same
    * communication control stream to "setup" various destinations
    * whithout breaking the connection, so I guess we can make it a bit faster
    * with a flow flag set lookup in the Match function.
    */
    sm->ctx = NULL;

    if (s->alproto != ALPROTO_UNKNOWN && s->alproto != ALPROTO_FTP) {
        SCLogError(SC_ERR_CONFLICTING_RULE_KEYWORDS, "rule contains conflicting keywords.");
        goto error;
    }

    SigMatchAppendSMToList(s, sm, DETECT_SM_LIST_AMATCH);

    s->alproto = ALPROTO_FTP;
    s->flags |= SIG_FLAG_APPLAYER;
    SCReturnInt(0);

error:
    if (sm != NULL) {
        SigMatchFree(sm);
    }
    SCReturnInt(-1);
}

#ifdef UNITTESTS

/**
 * \test DetectFtpbounceTestSetup01 is a test for the Setup ftpbounce
 */
int DetectFtpbounceTestSetup01(void)
{
    int res = 0;
    DetectEngineCtx *de_ctx = NULL;
    Signature *s = SigAlloc();
    if (s == NULL)
        return 0;

    /* ftpbounce doesn't accept options so the str is NULL */
    res = !DetectFtpbounceSetup(de_ctx, s, NULL);
    res &= s->sm_lists[DETECT_SM_LIST_AMATCH] != NULL && s->sm_lists[DETECT_SM_LIST_AMATCH]->type & DETECT_FTPBOUNCE;

    SigFree(s);
    return res;
}

#include "stream-tcp-reassemble.h"

/**
 * \test Check the ftpbounce match, send a get request in three chunks
 * + more data.
 * \brief This test tests the ftpbounce condition match, based on the
 *   ftp layer parser
 */
static int DetectFtpbounceTestALMatch02(void)
{
    int result = 0;

    uint8_t ftpbuf1[] = { 'P','O' };
    uint32_t ftplen1 = sizeof(ftpbuf1);
    uint8_t ftpbuf2[] = { 'R', 'T' };
    uint32_t ftplen2 = sizeof(ftpbuf2);
    uint8_t ftpbuf3[] = { ' ', '8','0',',','5' };
    uint32_t ftplen3 = sizeof(ftpbuf3);
    uint8_t ftpbuf4[] = "8,0,33,10,20\r\n";
    uint32_t ftplen4 = sizeof(ftpbuf4);

    TcpSession ssn;
    Flow f;
    Packet *p = NULL;
    Signature *s = NULL;
    ThreadVars th_v;
    DetectEngineThreadCtx *det_ctx = NULL;
    AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();

    memset(&th_v, 0, sizeof(th_v));
    memset(&f, 0, sizeof(f));
    memset(&ssn, 0, sizeof(ssn));

    p = UTHBuildPacketSrcDst(NULL, 0, IPPROTO_TCP, "1.2.3.4", "5.6.7.8");

    FLOW_INITIALIZE(&f);
    f.src.address.address_un_data32[0]=0x01020304;
    f.protoctx =(void *)&ssn;
    f.proto = IPPROTO_TCP;

    p->flow = &f;
    p->flowflags |= FLOW_PKT_TOSERVER;
    p->flowflags |= FLOW_PKT_ESTABLISHED;
    p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
    f.alproto = ALPROTO_FTP;

    StreamTcpInitConfig(TRUE);

    DetectEngineCtx *de_ctx = DetectEngineCtxInit();
    if (de_ctx == NULL) {
        goto end;
    }

    de_ctx->flags |= DE_QUIET;

    s = de_ctx->sig_list = SigInit(de_ctx,"alert tcp any any -> any any "
                                   "(msg:\"Ftp Bounce\"; ftpbounce; sid:1;)");
    if (s == NULL) {
        goto end;
    }

    SigGroupBuild(de_ctx);
    DetectEngineThreadCtxInit(&th_v,(void *)de_ctx,(void *)&det_ctx);

    SCMutexLock(&f.m);
    int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_FTP, STREAM_TOSERVER, ftpbuf1, ftplen1);
    if (r != 0) {
        SCLogDebug("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
        result = 0;
        SCMutexUnlock(&f.m);
        goto end;
    }

    r = AppLayerParserParse(alp_tctx, &f,ALPROTO_FTP, STREAM_TOSERVER, ftpbuf2, ftplen2);
    if (r != 0) {
        SCLogDebug("toserver chunk 2 returned %" PRId32 ", expected 0: ", r);
        result = 0;
        SCMutexUnlock(&f.m);
        goto end;
    }

    r = AppLayerParserParse(alp_tctx, &f,ALPROTO_FTP, STREAM_TOSERVER, ftpbuf3, ftplen3);
    if (r != 0) {
        SCLogDebug("toserver chunk 3 returned %" PRId32 ", expected 0: ", r);
        result = 0;
        SCMutexUnlock(&f.m);
        goto end;
    }

    r = AppLayerParserParse(alp_tctx, &f,ALPROTO_FTP, STREAM_TOSERVER, ftpbuf4, ftplen4);
    if (r != 0) {
        SCLogDebug("toserver chunk 4 returned %" PRId32 ", expected 0: ", r);
        result = 0;
        SCMutexUnlock(&f.m);
        goto end;
    }

    SCMutexUnlock(&f.m);

    FtpState *ftp_state = f.alstate;
    if (ftp_state == NULL) {
        SCLogDebug("no ftp state: ");
        result = 0;
        goto end;
    }

    if (ftp_state->command != FTP_COMMAND_PORT) {
        SCLogDebug("expected command port not detected");
        result = 0;
        goto end;
    }

    /* do detect */
    SigMatchSignatures(&th_v, de_ctx, det_ctx, p);

    if (!(PacketAlertCheck(p, 1))) {
        goto end;
    }

    result = 1;
end:
    if (alp_tctx != NULL)
        AppLayerParserThreadCtxFree(alp_tctx);
    SigGroupCleanup(de_ctx);
    SigCleanSignatures(de_ctx);

    DetectEngineThreadCtxDeinit(&th_v,(void *)det_ctx);
    DetectEngineCtxFree(de_ctx);

    StreamTcpFreeConfig(TRUE);
    FLOW_DESTROY(&f);

    UTHFreePackets(&p, 1);
    return result;
}

/**
 * \test Check the ftpbounce match
 * \brief This test tests the ftpbounce condition match, based on
 *  the ftp layer parser
 */
static int DetectFtpbounceTestALMatch03(void)
{
    int result = 0;

    uint8_t ftpbuf1[] = { 'P','O' };
    uint32_t ftplen1 = sizeof(ftpbuf1);
    uint8_t ftpbuf2[] = { 'R', 'T' };
    uint32_t ftplen2 = sizeof(ftpbuf2);
    uint8_t ftpbuf3[] = { ' ', '1',',','2',',' };
    uint32_t ftplen3 = sizeof(ftpbuf3);
    uint8_t ftpbuf4[] = "3,4,10,20\r\n";
    uint32_t ftplen4 = sizeof(ftpbuf4);

    TcpSession ssn;
    Flow f;
    Packet *p = SCMalloc(SIZE_OF_PACKET);
    if (unlikely(p == NULL))
        return 0;
    Signature *s = NULL;
    ThreadVars th_v;
    DetectEngineThreadCtx *det_ctx = NULL;
    AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();

    memset(&th_v, 0, sizeof(th_v));
    memset(p, 0, SIZE_OF_PACKET);
    memset(&f, 0, sizeof(f));
    memset(&ssn, 0, sizeof(ssn));

    p->src.family = AF_INET;
    p->dst.family = AF_INET;
    p->src.addr_data32[0] = 0x04030201;
    p->payload = NULL;
    p->payload_len = 0;
    p->proto = IPPROTO_TCP;

    FLOW_INITIALIZE(&f);
    f.src.address.address_un_data32[0]=0x04030201;
    f.protoctx =(void *)&ssn;
    f.proto = IPPROTO_TCP;

    p->flow = &f;
    p->flowflags |= FLOW_PKT_TOSERVER;
    p->flowflags |= FLOW_PKT_ESTABLISHED;
    p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
    f.alproto = ALPROTO_FTP;

    StreamTcpInitConfig(TRUE);

    DetectEngineCtx *de_ctx = DetectEngineCtxInit();
    if (de_ctx == NULL) {
        goto end;
    }

    de_ctx->flags |= DE_QUIET;

    s = de_ctx->sig_list = SigInit(de_ctx,"alert tcp any any -> any any "
                                   "(msg:\"Ftp Bounce\"; ftpbounce; sid:1;)");
    if (s == NULL) {
        goto end;
    }

    SigGroupBuild(de_ctx);
    DetectEngineThreadCtxInit(&th_v,(void *)de_ctx,(void *)&det_ctx);

    SCMutexLock(&f.m);
    int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_FTP, STREAM_TOSERVER, ftpbuf1, ftplen1);
    if (r != 0) {
        SCLogDebug("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
        result = 0;
        SCMutexUnlock(&f.m);
        goto end;
    }

    r = AppLayerParserParse(alp_tctx, &f,ALPROTO_FTP, STREAM_TOSERVER, ftpbuf2, ftplen2);
    if (r != 0) {
        SCLogDebug("toserver chunk 2 returned %" PRId32 ", expected 0: ", r);
        result = 0;
        SCMutexUnlock(&f.m);
        goto end;
    }

    r = AppLayerParserParse(alp_tctx, &f,ALPROTO_FTP, STREAM_TOSERVER, ftpbuf3, ftplen3);
    if (r != 0) {
        SCLogDebug("toserver chunk 3 returned %" PRId32 ", expected 0: ", r);
        result = 0;
        SCMutexUnlock(&f.m);
        goto end;
    }

    r = AppLayerParserParse(alp_tctx, &f,ALPROTO_FTP, STREAM_TOSERVER, ftpbuf4, ftplen4);
    if (r != 0) {
        SCLogDebug("toserver chunk 4 returned %" PRId32 ", expected 0: ", r);
        result = 0;
        SCMutexUnlock(&f.m);
        goto end;
    }
    SCMutexUnlock(&f.m);

    FtpState *ftp_state = f.alstate;
    if (ftp_state == NULL) {
        SCLogDebug("no ftp state: ");
        result = 0;
        goto end;
    }

    if (ftp_state->command != FTP_COMMAND_PORT) {
        SCLogDebug("expected command port not detected");
        result = 0;
        goto end;
    }

    /* do detect */
    SigMatchSignatures(&th_v, de_ctx, det_ctx, p);

    /* It should not match */
    if (!(PacketAlertCheck(p, 1))) {
        result = 1;
    } else {
        SCLogDebug("It should not match here!");
    }

end:
    if (alp_tctx != NULL)
        AppLayerParserThreadCtxFree(alp_tctx);
    SigGroupCleanup(de_ctx);
    SigCleanSignatures(de_ctx);

    DetectEngineThreadCtxDeinit(&th_v,(void *)det_ctx);
    DetectEngineCtxFree(de_ctx);

    StreamTcpFreeConfig(TRUE);
    FLOW_DESTROY(&f);
    SCFree(p);
    return result;
}

#endif /* UNITTESTS */

/**
 * \brief this function registers unit tests for DetectFtpbounce
 */
void DetectFtpbounceRegisterTests(void)
{
#ifdef UNITTESTS
    UtRegisterTest("DetectFtpbounceTestSetup01", DetectFtpbounceTestSetup01, 1);
    UtRegisterTest("DetectFtpbounceTestALMatch02",
                   DetectFtpbounceTestALMatch02, 1);
    UtRegisterTest("DetectFtpbounceTestALMatch03",
                   DetectFtpbounceTestALMatch03, 1);
#endif /* UNITTESTS */
}