1 /* Copyright (C) 2007-2020 Open Information Security Foundation
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
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.
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
21 * \author Pablo Rincon Crespo <pablo.rincon.crespo@gmail.com>
22 * \author Eric Leblond <eric@regit.org>
23 * \author Jeff Lucovsky <jeff@lucovsky.org>
25 * App Layer Parser for FTP
28 #include "suricata-common.h"
33 #include "util-print.h"
34 #include "util-pool.h"
36 #include "flow-util.h"
37 #include "flow-storage.h"
39 #include "detect-engine-state.h"
41 #include "stream-tcp-private.h"
42 #include "stream-tcp-reassemble.h"
43 #include "stream-tcp.h"
46 #include "app-layer.h"
47 #include "app-layer-protos.h"
48 #include "app-layer-parser.h"
49 #include "app-layer-ftp.h"
50 #include "app-layer-expectation.h"
54 #include "util-unittest.h"
55 #include "util-debug.h"
56 #include "util-memcmp.h"
57 #include "util-memrchr.h"
59 #include "util-misc.h"
61 #include "output-json.h"
64 typedef struct FTPThreadCtx_
{
65 MpmThreadCtx
*ftp_mpm_thread_ctx
;
66 PrefilterRuleStore
*pmq
;
69 #define FTP_MPM mpm_default_matcher
71 static MpmCtx
*ftp_mpm_ctx
= NULL
;
73 const FtpCommand FtpCommands
[FTP_COMMAND_MAX
+ 1] = {
74 /* Parsed and handled */
75 { FTP_COMMAND_PORT
, "PORT", 4},
76 { FTP_COMMAND_EPRT
, "EPRT", 4},
77 { FTP_COMMAND_AUTH_TLS
, "AUTH TLS", 8},
78 { FTP_COMMAND_PASV
, "PASV", 4},
79 { FTP_COMMAND_RETR
, "RETR", 4},
80 { FTP_COMMAND_EPSV
, "EPSV", 4},
81 { FTP_COMMAND_STOR
, "STOR", 4},
83 /* Parsed, but not handled */
84 { FTP_COMMAND_ABOR
, "ABOR", 4},
85 { FTP_COMMAND_ACCT
, "ACCT", 4},
86 { FTP_COMMAND_ALLO
, "ALLO", 4},
87 { FTP_COMMAND_APPE
, "APPE", 4},
88 { FTP_COMMAND_CDUP
, "CDUP", 4},
89 { FTP_COMMAND_CHMOD
, "CHMOD", 5},
90 { FTP_COMMAND_CWD
, "CWD", 3},
91 { FTP_COMMAND_DELE
, "DELE", 4},
92 { FTP_COMMAND_HELP
, "HELP", 4},
93 { FTP_COMMAND_IDLE
, "IDLE", 4},
94 { FTP_COMMAND_LIST
, "LIST", 4},
95 { FTP_COMMAND_MAIL
, "MAIL", 4},
96 { FTP_COMMAND_MDTM
, "MDTM", 4},
97 { FTP_COMMAND_MKD
, "MKD", 3},
98 { FTP_COMMAND_MLFL
, "MLFL", 4},
99 { FTP_COMMAND_MODE
, "MODE", 4},
100 { FTP_COMMAND_MRCP
, "MRCP", 4},
101 { FTP_COMMAND_MRSQ
, "MRSQ", 4},
102 { FTP_COMMAND_MSAM
, "MSAM", 4},
103 { FTP_COMMAND_MSND
, "MSND", 4},
104 { FTP_COMMAND_MSOM
, "MSOM", 4},
105 { FTP_COMMAND_NLST
, "NLST", 4},
106 { FTP_COMMAND_NOOP
, "NOOP", 4},
107 { FTP_COMMAND_PASS
, "PASS", 4},
108 { FTP_COMMAND_PWD
, "PWD", 3},
109 { FTP_COMMAND_QUIT
, "QUIT", 4},
110 { FTP_COMMAND_REIN
, "REIN", 4},
111 { FTP_COMMAND_REST
, "REST", 4},
112 { FTP_COMMAND_RMD
, "RMD", 3},
113 { FTP_COMMAND_RNFR
, "RNFR", 4},
114 { FTP_COMMAND_RNTO
, "RNTO", 4},
115 { FTP_COMMAND_SITE
, "SITE", 4},
116 { FTP_COMMAND_SIZE
, "SIZE", 4},
117 { FTP_COMMAND_SMNT
, "SMNT", 4},
118 { FTP_COMMAND_STAT
, "STAT", 4},
119 { FTP_COMMAND_STOU
, "STOU", 4},
120 { FTP_COMMAND_STRU
, "STRU", 4},
121 { FTP_COMMAND_SYST
, "SYST", 4},
122 { FTP_COMMAND_TYPE
, "TYPE", 4},
123 { FTP_COMMAND_UMASK
, "UMASK", 5},
124 { FTP_COMMAND_USER
, "USER", 4},
125 { FTP_COMMAND_UNKNOWN
, NULL
, 0}
127 uint64_t ftp_config_memcap
= 0;
129 SC_ATOMIC_DECLARE(uint64_t, ftp_memuse
);
130 SC_ATOMIC_DECLARE(uint64_t, ftp_memcap
);
132 static FTPTransaction
*FTPGetOldestTx(FtpState
*);
134 static void FTPParseMemcap(void)
136 const char *conf_val
;
138 /** set config values for memcap, prealloc and hash_size */
139 if ((ConfGet("app-layer.protocols.ftp.memcap", &conf_val
)) == 1)
141 if (ParseSizeStringU64(conf_val
, &ftp_config_memcap
) < 0) {
142 SCLogError(SC_ERR_SIZE_PARSE
, "Error parsing ftp.memcap "
143 "from conf file - %s. Killing engine",
147 SCLogInfo("FTP memcap: %"PRIu64
, ftp_config_memcap
);
149 /* default to unlimited */
150 ftp_config_memcap
= 0;
153 SC_ATOMIC_INIT(ftp_memuse
);
154 SC_ATOMIC_INIT(ftp_memcap
);
157 static void FTPIncrMemuse(uint64_t size
)
159 (void) SC_ATOMIC_ADD(ftp_memuse
, size
);
163 static void FTPDecrMemuse(uint64_t size
)
165 (void) SC_ATOMIC_SUB(ftp_memuse
, size
);
169 uint64_t FTPMemuseGlobalCounter(void)
171 uint64_t tmpval
= SC_ATOMIC_GET(ftp_memuse
);
175 uint64_t FTPMemcapGlobalCounter(void)
177 uint64_t tmpval
= SC_ATOMIC_GET(ftp_memcap
);
182 * \brief Check if alloc'ing "size" would mean we're over memcap
184 * \retval 1 if in bounds
185 * \retval 0 if not in bounds
187 static int FTPCheckMemcap(uint64_t size
)
189 if (ftp_config_memcap
== 0 || size
+ SC_ATOMIC_GET(ftp_memuse
) <= ftp_config_memcap
)
191 (void) SC_ATOMIC_ADD(ftp_memcap
, 1);
195 static void *FTPMalloc(size_t size
)
199 if (FTPCheckMemcap((uint32_t)size
) == 0)
202 ptr
= SCMalloc(size
);
204 if (unlikely(ptr
== NULL
))
207 FTPIncrMemuse((uint64_t)size
);
212 static void *FTPCalloc(size_t n
, size_t size
)
214 if (FTPCheckMemcap((uint32_t)(n
* size
)) == 0)
217 void *ptr
= SCCalloc(n
, size
);
219 if (unlikely(ptr
== NULL
))
222 FTPIncrMemuse((uint64_t)(n
* size
));
226 static void *FTPRealloc(void *ptr
, size_t orig_size
, size_t size
)
230 if (FTPCheckMemcap((uint32_t)(size
- orig_size
)) == 0)
233 rptr
= SCRealloc(ptr
, size
);
237 if (size
> orig_size
) {
238 FTPIncrMemuse(size
- orig_size
);
240 FTPDecrMemuse(orig_size
- size
);
246 static void FTPFree(void *ptr
, size_t size
)
250 FTPDecrMemuse((uint64_t)size
);
253 static FTPString
*FTPStringAlloc(void)
255 return FTPCalloc(1, sizeof(FTPString
));
258 static void FTPStringFree(FTPString
*str
)
261 FTPFree(str
->str
, str
->len
);
264 FTPFree(str
, sizeof(FTPString
));
267 static void *FTPLocalStorageAlloc(void)
269 /* needed by the mpm */
270 FTPThreadCtx
*td
= SCCalloc(1, sizeof(*td
));
275 td
->pmq
= SCCalloc(1, sizeof(*td
->pmq
));
276 if (td
->pmq
== NULL
) {
281 td
->ftp_mpm_thread_ctx
= SCCalloc(1, sizeof(MpmThreadCtx
));
282 if (unlikely(td
->ftp_mpm_thread_ctx
== NULL
)) {
285 MpmInitThreadCtx(td
->ftp_mpm_thread_ctx
, FTP_MPM
);
289 static void FTPLocalStorageFree(void *ptr
)
291 FTPThreadCtx
*td
= ptr
;
293 if (td
->pmq
!= NULL
) {
298 if (td
->ftp_mpm_thread_ctx
!= NULL
) {
299 mpm_table
[FTP_MPM
].DestroyThreadCtx(ftp_mpm_ctx
, td
->ftp_mpm_thread_ctx
);
300 SCFree(td
->ftp_mpm_thread_ctx
);
308 static FTPTransaction
*FTPTransactionCreate(FtpState
*state
)
311 FTPTransaction
*tx
= FTPCalloc(1, sizeof(*tx
));
316 TAILQ_INSERT_TAIL(&state
->tx_list
, tx
, next
);
317 tx
->tx_id
= state
->tx_cnt
++;
319 TAILQ_INIT(&tx
->response_list
);
321 SCLogDebug("new transaction %p (state tx cnt %"PRIu64
")", tx
, state
->tx_cnt
);
325 static void FTPTransactionFree(FTPTransaction
*tx
)
329 if (tx
->de_state
!= NULL
) {
330 DetectEngineStateFree(tx
->de_state
);
334 FTPFree(tx
->request
, tx
->request_length
);
337 FTPString
*str
= NULL
;
338 while ((str
= TAILQ_FIRST(&tx
->response_list
))) {
339 TAILQ_REMOVE(&tx
->response_list
, str
, next
);
343 FTPFree(tx
, sizeof(*tx
));
346 static int FTPGetLineForDirection(FtpState
*state
, FtpLineState
*line_state
)
349 if (line_state
->current_line_lf_seen
== 1) {
350 /* we have seen the lf for the previous line. Clear the parser
351 * details to parse new line */
352 line_state
->current_line_lf_seen
= 0;
353 if (line_state
->current_line_db
== 1) {
354 line_state
->current_line_db
= 0;
355 FTPFree(line_state
->db
, line_state
->db_len
);
356 line_state
->db
= NULL
;
357 line_state
->db_len
= 0;
358 state
->current_line
= NULL
;
359 state
->current_line_len
= 0;
363 uint8_t *lf_idx
= memchr(state
->input
, 0x0a, state
->input_len
);
365 if (lf_idx
== NULL
) {
366 /* fragmented lines. Decoder event for special cases. Not all
367 * fragmented lines should be treated as a possible evasion
368 * attempt. With multi payload ftp chunks we can have valid
369 * cases of fragmentation. But within the same segment chunk
370 * if we see fragmentation then it's definitely something you
371 * should alert about */
372 if (line_state
->current_line_db
== 0) {
373 line_state
->db
= FTPMalloc(state
->input_len
);
374 if (line_state
->db
== NULL
) {
377 line_state
->current_line_db
= 1;
378 memcpy(line_state
->db
, state
->input
, state
->input_len
);
379 line_state
->db_len
= state
->input_len
;
381 ptmp
= FTPRealloc(line_state
->db
, line_state
->db_len
,
382 (line_state
->db_len
+ state
->input_len
));
384 FTPFree(line_state
->db
, line_state
->db_len
);
385 line_state
->db
= NULL
;
386 line_state
->db_len
= 0;
389 line_state
->db
= ptmp
;
391 memcpy(line_state
->db
+ line_state
->db_len
,
392 state
->input
, state
->input_len
);
393 line_state
->db_len
+= state
->input_len
;
395 state
->input
+= state
->input_len
;
396 state
->input_len
= 0;
401 line_state
->current_line_lf_seen
= 1;
403 if (line_state
->current_line_db
== 1) {
404 ptmp
= FTPRealloc(line_state
->db
, line_state
->db_len
,
405 (line_state
->db_len
+ (lf_idx
+ 1 - state
->input
)));
407 FTPFree(line_state
->db
, line_state
->db_len
);
408 line_state
->db
= NULL
;
409 line_state
->db_len
= 0;
412 line_state
->db
= ptmp
;
414 memcpy(line_state
->db
+ line_state
->db_len
,
415 state
->input
, (lf_idx
+ 1 - state
->input
));
416 line_state
->db_len
+= (lf_idx
+ 1 - state
->input
);
418 if (line_state
->db_len
> 1 &&
419 line_state
->db
[line_state
->db_len
- 2] == 0x0D) {
420 line_state
->db_len
-= 2;
421 state
->current_line_delimiter_len
= 2;
423 line_state
->db_len
-= 1;
424 state
->current_line_delimiter_len
= 1;
427 state
->current_line
= line_state
->db
;
428 state
->current_line_len
= line_state
->db_len
;
431 state
->current_line
= state
->input
;
432 state
->current_line_len
= lf_idx
- state
->input
;
434 if (state
->input
!= lf_idx
&&
435 *(lf_idx
- 1) == 0x0D) {
436 state
->current_line_len
--;
437 state
->current_line_delimiter_len
= 2;
439 state
->current_line_delimiter_len
= 1;
443 state
->input_len
-= (lf_idx
- state
->input
) + 1;
444 state
->input
= (lf_idx
+ 1);
451 static int FTPGetLine(FtpState
*state
)
455 /* we have run out of input */
456 if (state
->input_len
<= 0)
460 if (state
->direction
== 0)
461 return FTPGetLineForDirection(state
, &state
->line_state
[0]);
463 return FTPGetLineForDirection(state
, &state
->line_state
[1]);
467 * \brief This function is called to determine and set which command is being
468 * transferred to the ftp server
469 * \param thread context
470 * \param input input line of the command
471 * \param len of the command
472 * \param cmd_descriptor when the command has been parsed
474 * \retval 1 when the command is parsed, 0 otherwise
476 static int FTPParseRequestCommand(FTPThreadCtx
*td
,
477 const uint8_t *input
, uint32_t input_len
,
478 const FtpCommand
**cmd_descriptor
)
482 /* I don't like this pmq reset here. We'll devise a method later, that
483 * should make the use of the mpm very efficient */
485 int mpm_cnt
= mpm_table
[FTP_MPM
].Search(ftp_mpm_ctx
, td
->ftp_mpm_thread_ctx
,
486 td
->pmq
, input
, input_len
);
488 *cmd_descriptor
= &FtpCommands
[td
->pmq
->rule_id_array
[0]];
492 *cmd_descriptor
= NULL
;
496 struct FtpTransferCmd
{
497 /** Need to look like a ExpectationData so DFree must
498 * be first field . */
499 void (*DFree
)(void *);
503 FtpRequestCommand cmd
;
506 static void FtpTransferCmdFree(void *data
)
508 struct FtpTransferCmd
*cmd
= (struct FtpTransferCmd
*) data
;
511 if (cmd
->file_name
) {
512 FTPFree(cmd
->file_name
, cmd
->file_len
+ 1);
514 FTPFree(cmd
, sizeof(struct FtpTransferCmd
));
517 static uint32_t CopyCommandLine(uint8_t **dest
, const uint8_t *src
, uint32_t length
)
519 if (likely(length
)) {
520 uint8_t *where
= FTPCalloc(length
+ 1, sizeof(char));
521 if (unlikely(where
== NULL
)) {
524 memcpy(where
, src
, length
);
526 /* Remove trailing newlines/carriage returns */
527 while (length
&& isspace((unsigned char) where
[length
- 1])) {
531 where
[length
] = '\0';
534 /* either 0 or actual */
535 return length
? length
+ 1 : 0;
540 * \brief This function is called to retrieve a ftp request
541 * \param ftp_state the ftp state structure for the parser
542 * \param input input line of the command
543 * \param input_len length of the request
544 * \param output the resulting output
546 * \retval APP_LAYER_OK when input was process successfully
547 * \retval APP_LAYER_ERROR when a unrecoverable error was encountered
549 static AppLayerResult
FTPParseRequest(Flow
*f
, void *ftp_state
,
550 AppLayerParserState
*pstate
,
551 const uint8_t *input
, uint32_t input_len
,
552 void *local_data
, const uint8_t flags
)
554 FTPThreadCtx
*thread_data
= local_data
;
557 /* PrintRawDataFp(stdout, input,input_len); */
559 FtpState
*state
= (FtpState
*)ftp_state
;
562 if (input
== NULL
&& AppLayerParserStateIssetFlag(pstate
, APP_LAYER_PARSER_EOF
)) {
563 SCReturnStruct(APP_LAYER_OK
);
564 } else if (input
== NULL
|| input_len
== 0) {
565 SCReturnStruct(APP_LAYER_ERROR
);
568 state
->input
= input
;
569 state
->input_len
= input_len
;
570 /* toserver stream */
571 state
->direction
= 0;
573 int direction
= STREAM_TOSERVER
;
574 while (FTPGetLine(state
) >= 0) {
575 const FtpCommand
*cmd_descriptor
;
577 if (!FTPParseRequestCommand(thread_data
,
578 state
->current_line
, state
->current_line_len
,
580 state
->command
= FTP_COMMAND_UNKNOWN
;
584 state
->command
= cmd_descriptor
->command
;
586 FTPTransaction
*tx
= FTPTransactionCreate(state
);
587 if (unlikely(tx
== NULL
))
588 SCReturnStruct(APP_LAYER_ERROR
);
591 tx
->command_descriptor
= cmd_descriptor
;
592 tx
->request_length
= CopyCommandLine(&tx
->request
,
593 state
->current_line
, state
->current_line_len
);
595 /* change direction (default to server) so expectation will handle
596 * the correct message when expectation will match.
597 * For ftp active mode, data connection direction is opposite to
600 if ((state
->active
&& state
->command
== FTP_COMMAND_STOR
) ||
601 (!state
->active
&& state
->command
== FTP_COMMAND_RETR
)) {
602 direction
= STREAM_TOCLIENT
;
605 switch (state
->command
) {
606 case FTP_COMMAND_EPRT
:
608 case FTP_COMMAND_PORT
:
609 if (state
->current_line_len
+ 1 > state
->port_line_size
) {
610 /* Allocate an extra byte for a NULL terminator */
611 ptmp
= FTPRealloc(state
->port_line
, state
->port_line_size
,
612 state
->current_line_len
);
614 if (state
->port_line
) {
615 FTPFree(state
->port_line
, state
->port_line_size
);
616 state
->port_line
= NULL
;
617 state
->port_line_size
= 0;
619 SCReturnStruct(APP_LAYER_OK
);
621 state
->port_line
= ptmp
;
622 state
->port_line_size
= state
->current_line_len
;
624 memcpy(state
->port_line
, state
->current_line
,
625 state
->current_line_len
);
626 state
->port_line_len
= state
->current_line_len
;
628 case FTP_COMMAND_RETR
:
630 case FTP_COMMAND_STOR
: {
631 /* Ensure that there is a negotiated dyn port and a file
632 * name -- need more than 5 chars: cmd [4], space, <filename>
634 if (state
->dyn_port
== 0 || state
->current_line_len
< 6) {
635 SCReturnStruct(APP_LAYER_ERROR
);
637 struct FtpTransferCmd
*data
= FTPCalloc(1, sizeof(struct FtpTransferCmd
));
639 SCReturnStruct(APP_LAYER_ERROR
);
640 data
->DFree
= FtpTransferCmdFree
;
642 * Min size has been checked in FTPParseRequestCommand
643 * PATH_MAX includes the null
645 int file_name_len
= MIN(PATH_MAX
- 1, state
->current_line_len
- 5);
646 data
->file_name
= FTPCalloc(file_name_len
+ 1, sizeof(char));
647 if (data
->file_name
== NULL
) {
648 FtpTransferCmdFree(data
);
649 SCReturnStruct(APP_LAYER_ERROR
);
651 data
->file_name
[file_name_len
] = 0;
652 data
->file_len
= file_name_len
;
653 memcpy(data
->file_name
, state
->current_line
+ 5, file_name_len
);
654 data
->cmd
= state
->command
;
655 data
->flow_id
= FlowGetId(f
);
656 int ret
= AppLayerExpectationCreate(f
, direction
,
657 0, state
->dyn_port
, ALPROTO_FTPDATA
, data
);
659 FtpTransferCmdFree(data
);
660 SCLogDebug("No expectation created.");
661 SCReturnStruct(APP_LAYER_ERROR
);
663 SCLogDebug("Expectation created [direction: %s, dynamic port %"PRIu16
"].",
664 state
->active
? "to server" : "to client",
668 /* reset the dyn port to avoid duplicate */
670 /* reset active/passive indicator */
671 state
->active
= false;
679 SCReturnStruct(APP_LAYER_OK
);
682 static int FTPParsePassiveResponse(Flow
*f
, FtpState
*state
, const uint8_t *input
, uint32_t input_len
)
684 uint16_t dyn_port
= rs_ftp_pasv_response(input
, input_len
);
688 SCLogDebug("FTP passive mode (v4): dynamic port %"PRIu16
"", dyn_port
);
689 state
->active
= false;
690 state
->dyn_port
= dyn_port
;
691 state
->curr_tx
->dyn_port
= dyn_port
;
692 state
->curr_tx
->active
= false;
697 static int FTPParsePassiveResponseV6(Flow
*f
, FtpState
*state
, const uint8_t *input
, uint32_t input_len
)
699 uint16_t dyn_port
= rs_ftp_epsv_response(input
, input_len
);
703 SCLogDebug("FTP passive mode (v6): dynamic port %"PRIu16
"", dyn_port
);
704 state
->active
= false;
705 state
->dyn_port
= dyn_port
;
706 state
->curr_tx
->dyn_port
= dyn_port
;
707 state
->curr_tx
->active
= false;
712 * \brief Handle preliminary replies -- keep tx open
713 * \retval bool True for a positive preliminary reply; false otherwise
715 * 1yz Positive Preliminary reply
717 * The requested action is being initiated; expect another
718 * reply before proceeding with a new command
720 static inline bool FTPIsPPR(const uint8_t *input
, uint32_t input_len
)
722 return input_len
>= 4 && isdigit(input
[0]) && input
[0] == '1' &&
723 isdigit(input
[1]) && isdigit(input
[2]) && isspace(input
[3]);
727 * \brief This function is called to retrieve a ftp response
728 * \param ftp_state the ftp state structure for the parser
729 * \param input input line of the command
730 * \param input_len length of the request
731 * \param output the resulting output
733 * \retval 1 when the command is parsed, 0 otherwise
735 static AppLayerResult
FTPParseResponse(Flow
*f
, void *ftp_state
, AppLayerParserState
*pstate
,
736 const uint8_t *input
, uint32_t input_len
,
737 void *local_data
, const uint8_t flags
)
739 FtpState
*state
= (FtpState
*)ftp_state
;
741 if (unlikely(input_len
== 0)) {
742 SCReturnStruct(APP_LAYER_OK
);
744 state
->input
= input
;
745 state
->input_len
= input_len
;
746 /* toclient stream */
747 state
->direction
= 1;
749 while (FTPGetLine(state
) >= 0) {
750 FTPTransaction
*tx
= FTPGetOldestTx(state
);
752 tx
= FTPTransactionCreate(state
);
754 if (unlikely(tx
== NULL
)) {
755 SCReturnStruct(APP_LAYER_ERROR
);
757 if (state
->command
== FTP_COMMAND_UNKNOWN
|| tx
->command_descriptor
== NULL
) {
759 tx
->command_descriptor
= &FtpCommands
[FTP_COMMAND_MAX
-1];
764 switch (state
->command
) {
765 case FTP_COMMAND_AUTH_TLS
:
766 if (state
->current_line_len
>= 4 && SCMemcmp("234 ", state
->current_line
, 4) == 0) {
767 AppLayerRequestProtocolTLSUpgrade(f
);
771 case FTP_COMMAND_EPRT
:
772 dyn_port
= rs_ftp_active_eprt(state
->port_line
, state
->port_line_len
);
776 state
->dyn_port
= dyn_port
;
777 state
->active
= true;
778 tx
->dyn_port
= dyn_port
;
780 SCLogDebug("FTP active mode (v6): dynamic port %"PRIu16
"", dyn_port
);
783 case FTP_COMMAND_PORT
:
784 dyn_port
= rs_ftp_active_port(state
->port_line
, state
->port_line_len
);
788 state
->dyn_port
= dyn_port
;
789 state
->active
= true;
790 tx
->dyn_port
= state
->dyn_port
;
792 SCLogDebug("FTP active mode (v4): dynamic port %"PRIu16
"", dyn_port
);
795 case FTP_COMMAND_PASV
:
796 if (state
->current_line_len
>= 4 && SCMemcmp("227 ", state
->current_line
, 4) == 0) {
797 FTPParsePassiveResponse(f
, ftp_state
, state
->current_line
, state
->current_line_len
);
801 case FTP_COMMAND_EPSV
:
802 if (state
->current_line_len
>= 4 && SCMemcmp("229 ", state
->current_line
, 4) == 0) {
803 FTPParsePassiveResponseV6(f
, ftp_state
, state
->current_line
, state
->current_line_len
);
810 if (likely(state
->current_line_len
)) {
811 FTPString
*response
= FTPStringAlloc();
812 if (likely(response
)) {
813 response
->len
= CopyCommandLine(&response
->str
, state
->current_line
, state
->current_line_len
);
814 TAILQ_INSERT_TAIL(&tx
->response_list
, response
, next
);
818 /* Handle preliminary replies -- keep tx open */
819 if (FTPIsPPR(state
->current_line
, state
->current_line_len
)) {
826 SCReturnStruct(APP_LAYER_OK
);
831 static SCMutex ftp_state_mem_lock
= SCMUTEX_INITIALIZER
;
832 static uint64_t ftp_state_memuse
= 0;
833 static uint64_t ftp_state_memcnt
= 0;
836 static void *FTPStateAlloc(void)
838 void *s
= FTPCalloc(1, sizeof(FtpState
));
839 if (unlikely(s
== NULL
))
842 FtpState
*ftp_state
= (FtpState
*) s
;
843 TAILQ_INIT(&ftp_state
->tx_list
);
846 SCMutexLock(&ftp_state_mem_lock
);
848 ftp_state_memuse
+=sizeof(FtpState
);
849 SCMutexUnlock(&ftp_state_mem_lock
);
854 static void FTPStateFree(void *s
)
856 FtpState
*fstate
= (FtpState
*) s
;
857 if (fstate
->port_line
!= NULL
)
858 FTPFree(fstate
->port_line
, fstate
->port_line_size
);
859 if (fstate
->line_state
[0].db
)
860 FTPFree(fstate
->line_state
[0].db
, fstate
->line_state
[0].db_len
);
861 if (fstate
->line_state
[1].db
)
862 FTPFree(fstate
->line_state
[1].db
, fstate
->line_state
[1].db_len
);
864 //AppLayerDecoderEventsFreeEvents(&s->decoder_events);
866 FTPTransaction
*tx
= NULL
;
867 while ((tx
= TAILQ_FIRST(&fstate
->tx_list
))) {
868 TAILQ_REMOVE(&fstate
->tx_list
, tx
, next
);
869 SCLogDebug("[%s] state %p id %"PRIu64
", Freeing %d bytes at %p",
870 tx
->command_descriptor
->command_name
,
872 tx
->request_length
, tx
->request
);
873 FTPTransactionFree(tx
);
876 FTPFree(s
, sizeof(FtpState
));
878 SCMutexLock(&ftp_state_mem_lock
);
880 ftp_state_memuse
-=sizeof(FtpState
);
881 SCMutexUnlock(&ftp_state_mem_lock
);
885 static int FTPSetTxDetectState(void *vtx
, DetectEngineState
*de_state
)
887 FTPTransaction
*tx
= (FTPTransaction
*)vtx
;
888 tx
->de_state
= de_state
;
893 * \brief This function returns the oldest open transaction; if none
894 * are open, then the oldest transaction is returned
895 * \param ftp_state the ftp state structure for the parser
897 * \retval transaction pointer when a transaction was found; NULL otherwise.
899 static FTPTransaction
*FTPGetOldestTx(FtpState
*ftp_state
)
901 if (unlikely(!ftp_state
)) {
902 SCLogDebug("NULL state object; no transactions available");
905 FTPTransaction
*tx
= NULL
;
906 FTPTransaction
*lasttx
= NULL
;
907 TAILQ_FOREACH(tx
, &ftp_state
->tx_list
, next
) {
908 /* Return oldest open tx */
910 SCLogDebug("Returning tx %p id %"PRIu64
, tx
, tx
->tx_id
);
913 /* save for the end */
916 /* All tx are closed; return last element */
918 SCLogDebug("Returning OLDEST tx %p id %"PRIu64
, lasttx
, lasttx
->tx_id
);
922 static void *FTPGetTx(void *state
, uint64_t tx_id
)
924 FtpState
*ftp_state
= (FtpState
*)state
;
926 FTPTransaction
*tx
= NULL
;
928 if (ftp_state
->curr_tx
== NULL
)
930 if (ftp_state
->curr_tx
->tx_id
== tx_id
)
931 return ftp_state
->curr_tx
;
933 TAILQ_FOREACH(tx
, &ftp_state
->tx_list
, next
) {
934 if (tx
->tx_id
== tx_id
)
941 static DetectEngineState
*FTPGetTxDetectState(void *vtx
)
943 FTPTransaction
*tx
= (FTPTransaction
*)vtx
;
948 static AppLayerTxData
*FTPGetTxData(void *vtx
)
950 FTPTransaction
*tx
= (FTPTransaction
*)vtx
;
954 static void FTPStateTransactionFree(void *state
, uint64_t tx_id
)
956 FtpState
*ftp_state
= state
;
957 FTPTransaction
*tx
= NULL
;
958 TAILQ_FOREACH(tx
, &ftp_state
->tx_list
, next
) {
959 if (tx_id
< tx
->tx_id
)
961 else if (tx_id
> tx
->tx_id
)
964 if (tx
== ftp_state
->curr_tx
)
965 ftp_state
->curr_tx
= NULL
;
966 TAILQ_REMOVE(&ftp_state
->tx_list
, tx
, next
);
967 FTPTransactionFree(tx
);
972 static uint64_t FTPGetTxCnt(void *state
)
975 FtpState
*ftp_state
= state
;
977 cnt
= ftp_state
->tx_cnt
;
979 SCLogDebug("returning state %p %"PRIu64
, state
, cnt
);
983 static int FTPGetAlstateProgressCompletionStatus(uint8_t direction
)
985 return FTP_STATE_FINISHED
;
988 static int FTPGetAlstateProgress(void *vtx
, uint8_t direction
)
990 SCLogDebug("tx %p", vtx
);
991 FTPTransaction
*tx
= vtx
;
994 if (direction
== STREAM_TOSERVER
&&
995 tx
->command_descriptor
->command
== FTP_COMMAND_PORT
) {
996 return FTP_STATE_PORT_DONE
;
998 return FTP_STATE_IN_PROGRESS
;
1001 return FTP_STATE_FINISHED
;
1005 static int FTPRegisterPatternsForProtocolDetection(void)
1007 if (AppLayerProtoDetectPMRegisterPatternCI(IPPROTO_TCP
, ALPROTO_FTP
,
1008 "220 (", 5, 0, STREAM_TOCLIENT
) < 0)
1012 if (AppLayerProtoDetectPMRegisterPatternCI(IPPROTO_TCP
, ALPROTO_FTP
,
1013 "FEAT", 4, 0, STREAM_TOSERVER
) < 0)
1017 if (AppLayerProtoDetectPMRegisterPatternCI(IPPROTO_TCP
, ALPROTO_FTP
,
1018 "USER ", 5, 0, STREAM_TOSERVER
) < 0)
1022 if (AppLayerProtoDetectPMRegisterPatternCI(IPPROTO_TCP
, ALPROTO_FTP
,
1023 "PASS ", 5, 0, STREAM_TOSERVER
) < 0)
1027 if (AppLayerProtoDetectPMRegisterPatternCI(IPPROTO_TCP
, ALPROTO_FTP
,
1028 "PORT ", 5, 0, STREAM_TOSERVER
) < 0)
1037 static StreamingBufferConfig sbcfg
= STREAMING_BUFFER_CONFIG_INITIALIZER
;
1040 * \brief This function is called to retrieve a ftp request
1041 * \param ftp_state the ftp state structure for the parser
1042 * \param input input line of the command
1043 * \param input_len length of the request
1044 * \param output the resulting output
1046 * \retval 1 when the command is parsed, 0 otherwise
1048 static AppLayerResult
FTPDataParse(Flow
*f
, FtpDataState
*ftpdata_state
,
1049 AppLayerParserState
*pstate
,
1050 const uint8_t *input
, uint32_t input_len
,
1051 void *local_data
, int direction
)
1053 uint16_t flags
= FileFlowToFlags(f
, direction
);
1055 /* we depend on detection engine for file pruning */
1056 flags
|= FILE_USE_DETECT
;
1057 if (ftpdata_state
->files
== NULL
) {
1058 struct FtpTransferCmd
*data
= (struct FtpTransferCmd
*)FlowGetStorageById(f
, AppLayerExpectationGetDataId());
1060 SCReturnStruct(APP_LAYER_ERROR
);
1063 ftpdata_state
->files
= FileContainerAlloc();
1064 if (ftpdata_state
->files
== NULL
) {
1065 FlowFreeStorageById(f
, AppLayerExpectationGetDataId());
1066 SCReturnStruct(APP_LAYER_ERROR
);
1069 ftpdata_state
->file_name
= data
->file_name
;
1070 ftpdata_state
->file_len
= data
->file_len
;
1071 data
->file_name
= NULL
;
1073 f
->parent_id
= data
->flow_id
;
1074 ftpdata_state
->command
= data
->cmd
;
1075 switch (data
->cmd
) {
1076 case FTP_COMMAND_STOR
:
1077 ftpdata_state
->direction
= STREAM_TOSERVER
;
1079 case FTP_COMMAND_RETR
:
1080 ftpdata_state
->direction
= STREAM_TOCLIENT
;
1086 /* open with fixed track_id 0 as we can have just one
1087 * file per ftp-data flow. */
1088 if (FileOpenFileWithId(ftpdata_state
->files
, &sbcfg
,
1089 0ULL, (uint8_t *) ftpdata_state
->file_name
,
1090 ftpdata_state
->file_len
,
1091 input
, input_len
, flags
) != 0) {
1092 SCLogDebug("Can't open file");
1095 FlowFreeStorageById(f
, AppLayerExpectationGetDataId());
1097 if (input_len
!= 0) {
1098 ret
= FileAppendData(ftpdata_state
->files
, input
, input_len
);
1101 SCLogDebug("FileAppendData() - file no longer being extracted");
1103 } else if (ret
< 0) {
1104 SCLogDebug("FileAppendData() failed: %d", ret
);
1109 ret
= FileCloseFile(ftpdata_state
->files
, NULL
, 0, flags
);
1110 ftpdata_state
->state
= FTPDATA_STATE_FINISHED
;
1116 if (input_len
&& AppLayerParserStateIssetFlag(pstate
, APP_LAYER_PARSER_EOF
)) {
1117 ret
= FileCloseFile(ftpdata_state
->files
, (uint8_t *) NULL
, 0, flags
);
1118 ftpdata_state
->state
= FTPDATA_STATE_FINISHED
;
1123 SCReturnStruct(APP_LAYER_ERROR
);
1125 SCReturnStruct(APP_LAYER_OK
);
1128 static AppLayerResult
FTPDataParseRequest(Flow
*f
, void *ftp_state
,
1129 AppLayerParserState
*pstate
,
1130 const uint8_t *input
, uint32_t input_len
,
1131 void *local_data
, const uint8_t flags
)
1133 return FTPDataParse(f
, ftp_state
, pstate
, input
, input_len
,
1134 local_data
, STREAM_TOSERVER
);
1137 static AppLayerResult
FTPDataParseResponse(Flow
*f
, void *ftp_state
,
1138 AppLayerParserState
*pstate
,
1139 const uint8_t *input
, uint32_t input_len
,
1140 void *local_data
, const uint8_t flags
)
1142 return FTPDataParse(f
, ftp_state
, pstate
, input
, input_len
,
1143 local_data
, STREAM_TOCLIENT
);
1147 static SCMutex ftpdata_state_mem_lock
= SCMUTEX_INITIALIZER
;
1148 static uint64_t ftpdata_state_memuse
= 0;
1149 static uint64_t ftpdata_state_memcnt
= 0;
1152 static void *FTPDataStateAlloc(void)
1154 void *s
= FTPCalloc(1, sizeof(FtpDataState
));
1155 if (unlikely(s
== NULL
))
1158 FtpDataState
*state
= (FtpDataState
*) s
;
1159 state
->state
= FTPDATA_STATE_IN_PROGRESS
;
1162 SCMutexLock(&ftpdata_state_mem_lock
);
1163 ftpdata_state_memcnt
++;
1164 ftpdata_state_memuse
+=sizeof(FtpDataState
);
1165 SCMutexUnlock(&ftpdata_state_mem_lock
);
1170 static void FTPDataStateFree(void *s
)
1172 FtpDataState
*fstate
= (FtpDataState
*) s
;
1174 if (fstate
->de_state
!= NULL
) {
1175 DetectEngineStateFree(fstate
->de_state
);
1177 if (fstate
->file_name
!= NULL
) {
1178 FTPFree(fstate
->file_name
, fstate
->file_len
+ 1);
1181 FileContainerFree(fstate
->files
);
1183 FTPFree(s
, sizeof(FtpDataState
));
1185 SCMutexLock(&ftpdata_state_mem_lock
);
1186 ftpdata_state_memcnt
--;
1187 ftpdata_state_memuse
-=sizeof(FtpDataState
);
1188 SCMutexUnlock(&ftpdata_state_mem_lock
);
1192 static int FTPDataSetTxDetectState(void *vtx
, DetectEngineState
*de_state
)
1194 FtpDataState
*ftp_state
= (FtpDataState
*)vtx
;
1195 ftp_state
->de_state
= de_state
;
1199 static DetectEngineState
*FTPDataGetTxDetectState(void *vtx
)
1201 FtpDataState
*ftp_state
= (FtpDataState
*)vtx
;
1202 return ftp_state
->de_state
;
1205 static AppLayerTxData
*FTPDataGetTxData(void *vtx
)
1207 FtpDataState
*ftp_state
= (FtpDataState
*)vtx
;
1208 return &ftp_state
->tx_data
;
1211 static void FTPDataStateTransactionFree(void *state
, uint64_t tx_id
)
1216 static void *FTPDataGetTx(void *state
, uint64_t tx_id
)
1218 FtpDataState
*ftp_state
= (FtpDataState
*)state
;
1222 static uint64_t FTPDataGetTxCnt(void *state
)
1224 /* ftp-data is single tx */
1228 static int FTPDataGetAlstateProgressCompletionStatus(uint8_t direction
)
1230 return FTPDATA_STATE_FINISHED
;
1233 static int FTPDataGetAlstateProgress(void *tx
, uint8_t direction
)
1235 FtpDataState
*ftpdata_state
= (FtpDataState
*)tx
;
1236 return ftpdata_state
->state
;
1239 static FileContainer
*FTPDataStateGetFiles(void *state
, uint8_t direction
)
1241 FtpDataState
*ftpdata_state
= (FtpDataState
*)state
;
1243 if (direction
!= ftpdata_state
->direction
)
1244 SCReturnPtr(NULL
, "FileContainer");
1246 SCReturnPtr(ftpdata_state
->files
, "FileContainer");
1249 static void FTPSetMpmState(void)
1251 ftp_mpm_ctx
= SCMalloc(sizeof(MpmCtx
));
1252 if (unlikely(ftp_mpm_ctx
== NULL
)) {
1255 memset(ftp_mpm_ctx
, 0, sizeof(MpmCtx
));
1256 MpmInitCtx(ftp_mpm_ctx
, FTP_MPM
);
1259 for (i
= 0; i
< sizeof(FtpCommands
)/sizeof(FtpCommand
) - 1; i
++) {
1260 const FtpCommand
*cmd
= &FtpCommands
[i
];
1261 if (cmd
->command_length
== 0)
1264 MpmAddPatternCI(ftp_mpm_ctx
,
1265 (uint8_t *)cmd
->command_name
,
1266 cmd
->command_length
,
1267 0 /* defunct */, 0 /* defunct */,
1268 i
/* id */, i
/* rule id */ , 0 /* no flags */);
1271 mpm_table
[FTP_MPM
].Prepare(ftp_mpm_ctx
);
1275 static void FTPFreeMpmState(void)
1277 if (ftp_mpm_ctx
!= NULL
) {
1278 mpm_table
[FTP_MPM
].DestroyCtx(ftp_mpm_ctx
);
1279 SCFree(ftp_mpm_ctx
);
1284 void RegisterFTPParsers(void)
1286 const char *proto_name
= "ftp";
1287 const char *proto_data_name
= "ftp-data";
1290 if (AppLayerProtoDetectConfProtoDetectionEnabled("tcp", proto_name
)) {
1291 AppLayerProtoDetectRegisterProtocol(ALPROTO_FTP
, proto_name
);
1292 if (FTPRegisterPatternsForProtocolDetection() < 0 )
1294 AppLayerProtoDetectRegisterProtocol(ALPROTO_FTPDATA
, proto_data_name
);
1297 if (AppLayerParserConfParserEnabled("tcp", proto_name
)) {
1298 AppLayerParserRegisterParser(IPPROTO_TCP
, ALPROTO_FTP
, STREAM_TOSERVER
,
1300 AppLayerParserRegisterParser(IPPROTO_TCP
, ALPROTO_FTP
, STREAM_TOCLIENT
,
1302 AppLayerParserRegisterStateFuncs(IPPROTO_TCP
, ALPROTO_FTP
, FTPStateAlloc
, FTPStateFree
);
1303 AppLayerParserRegisterParserAcceptableDataDirection(IPPROTO_TCP
, ALPROTO_FTP
, STREAM_TOSERVER
| STREAM_TOCLIENT
);
1305 AppLayerParserRegisterTxFreeFunc(IPPROTO_TCP
, ALPROTO_FTP
, FTPStateTransactionFree
);
1307 AppLayerParserRegisterDetectStateFuncs(IPPROTO_TCP
, ALPROTO_FTP
,
1308 FTPGetTxDetectState
, FTPSetTxDetectState
);
1310 AppLayerParserRegisterGetTx(IPPROTO_TCP
, ALPROTO_FTP
, FTPGetTx
);
1311 AppLayerParserRegisterTxDataFunc(IPPROTO_TCP
, ALPROTO_FTP
, FTPGetTxData
);
1313 AppLayerParserRegisterLocalStorageFunc(IPPROTO_TCP
, ALPROTO_FTP
, FTPLocalStorageAlloc
,
1314 FTPLocalStorageFree
);
1315 AppLayerParserRegisterGetTxCnt(IPPROTO_TCP
, ALPROTO_FTP
, FTPGetTxCnt
);
1317 AppLayerParserRegisterGetStateProgressFunc(IPPROTO_TCP
, ALPROTO_FTP
, FTPGetAlstateProgress
);
1319 AppLayerParserRegisterGetStateProgressCompletionStatus(ALPROTO_FTP
,
1320 FTPGetAlstateProgressCompletionStatus
);
1323 AppLayerRegisterExpectationProto(IPPROTO_TCP
, ALPROTO_FTPDATA
);
1324 AppLayerParserRegisterParser(IPPROTO_TCP
, ALPROTO_FTPDATA
, STREAM_TOSERVER
,
1325 FTPDataParseRequest
);
1326 AppLayerParserRegisterParser(IPPROTO_TCP
, ALPROTO_FTPDATA
, STREAM_TOCLIENT
,
1327 FTPDataParseResponse
);
1328 AppLayerParserRegisterStateFuncs(IPPROTO_TCP
, ALPROTO_FTPDATA
, FTPDataStateAlloc
, FTPDataStateFree
);
1329 AppLayerParserRegisterParserAcceptableDataDirection(IPPROTO_TCP
, ALPROTO_FTPDATA
, STREAM_TOSERVER
| STREAM_TOCLIENT
);
1330 AppLayerParserRegisterTxFreeFunc(IPPROTO_TCP
, ALPROTO_FTPDATA
, FTPDataStateTransactionFree
);
1331 AppLayerParserRegisterDetectStateFuncs(IPPROTO_TCP
, ALPROTO_FTPDATA
,
1332 FTPDataGetTxDetectState
, FTPDataSetTxDetectState
);
1334 AppLayerParserRegisterGetFilesFunc(IPPROTO_TCP
, ALPROTO_FTPDATA
, FTPDataStateGetFiles
);
1336 AppLayerParserRegisterGetTx(IPPROTO_TCP
, ALPROTO_FTPDATA
, FTPDataGetTx
);
1337 AppLayerParserRegisterTxDataFunc(IPPROTO_TCP
, ALPROTO_FTPDATA
, FTPDataGetTxData
);
1339 AppLayerParserRegisterGetTxCnt(IPPROTO_TCP
, ALPROTO_FTPDATA
, FTPDataGetTxCnt
);
1341 AppLayerParserRegisterGetStateProgressFunc(IPPROTO_TCP
, ALPROTO_FTPDATA
, FTPDataGetAlstateProgress
);
1343 AppLayerParserRegisterGetStateProgressCompletionStatus(ALPROTO_FTPDATA
,
1344 FTPDataGetAlstateProgressCompletionStatus
);
1346 sbcfg
.buf_size
= 4096;
1347 sbcfg
.Malloc
= FTPMalloc
;
1348 sbcfg
.Calloc
= FTPCalloc
;
1349 sbcfg
.Realloc
= FTPRealloc
;
1350 sbcfg
.Free
= FTPFree
;
1354 SCLogInfo("Parsed disabled for %s protocol. Protocol detection"
1355 "still on.", proto_name
);
1361 AppLayerParserRegisterProtocolUnittests(IPPROTO_TCP
, ALPROTO_FTP
, FTPParserRegisterTests
);
1365 void FTPAtExitPrintStats(void)
1368 SCMutexLock(&ftp_state_mem_lock
);
1369 SCLogDebug("ftp_state_memcnt %"PRIu64
", ftp_state_memuse %"PRIu64
"",
1370 ftp_state_memcnt
, ftp_state_memuse
);
1371 SCMutexUnlock(&ftp_state_mem_lock
);
1377 * \brief Returns the ending offset of the next line from a multi-line buffer.
1379 * "Buffer" refers to a FTP response in a single buffer containing multiple lines.
1380 * Here, "next line" is defined as terminating on
1381 * - Newline character
1384 * \param buffer Contains zero or more characters.
1385 * \param len Size, in bytes, of buffer.
1387 * \retval Offset from the start of buffer indicating the where the
1388 * next "line ends". The characters between the input buffer and this
1389 * value comprise the line.
1391 * NULL is found first or a newline isn't found, then UINT16_MAX is returned.
1393 uint16_t JsonGetNextLineFromBuffer(const char *buffer
, const uint16_t len
)
1395 if (!buffer
|| *buffer
== '\0') {
1399 char *c
= strchr(buffer
, '\n');
1400 return c
== NULL
? len
: c
- buffer
+ 1;
1403 void EveFTPDataAddMetadata(const Flow
*f
, JsonBuilder
*jb
)
1405 const FtpDataState
*ftp_state
= NULL
;
1406 if (f
->alstate
== NULL
)
1409 ftp_state
= (FtpDataState
*)f
->alstate
;
1411 if (ftp_state
->file_name
) {
1412 jb_set_string_from_bytes(jb
, "filename", ftp_state
->file_name
, ftp_state
->file_len
);
1414 switch (ftp_state
->command
) {
1415 case FTP_COMMAND_STOR
:
1416 JB_SET_STRING(jb
, "command", "STOR");
1418 case FTP_COMMAND_RETR
:
1419 JB_SET_STRING(jb
, "command", "RETR");
1427 * \brief Free memory allocated for global FTP parser state.
1429 void FTPParserCleanup(void)
1437 /** \test Send a get request in one chunk. */
1438 static int FTPParserTest01(void)
1442 uint8_t ftpbuf
[] = "PORT 192,168,1,1,0,80\r\n";
1443 uint32_t ftplen
= sizeof(ftpbuf
) - 1; /* minus the \0 */
1445 AppLayerParserThreadCtx
*alp_tctx
= AppLayerParserThreadCtxAlloc();
1447 memset(&f
, 0, sizeof(f
));
1448 memset(&ssn
, 0, sizeof(ssn
));
1450 FLOW_INITIALIZE(&f
);
1451 f
.protoctx
= (void *)&ssn
;
1452 f
.proto
= IPPROTO_TCP
;
1453 f
.alproto
= ALPROTO_FTP
;
1455 StreamTcpInitConfig(TRUE
);
1457 FLOWLOCK_WRLOCK(&f
);
1458 int r
= AppLayerParserParse(NULL
, alp_tctx
, &f
, ALPROTO_FTP
,
1459 STREAM_TOSERVER
| STREAM_EOF
, ftpbuf
, ftplen
);
1461 SCLogDebug("toserver chunk 1 returned %" PRId32
", expected 0: ", r
);
1463 FLOWLOCK_UNLOCK(&f
);
1466 FLOWLOCK_UNLOCK(&f
);
1468 FtpState
*ftp_state
= f
.alstate
;
1469 if (ftp_state
== NULL
) {
1470 SCLogDebug("no ftp state: ");
1475 if (ftp_state
->command
!= FTP_COMMAND_PORT
) {
1476 SCLogDebug("expected command %" PRIu32
", got %" PRIu32
": ", FTP_COMMAND_PORT
, ftp_state
->command
);
1482 if (alp_tctx
!= NULL
)
1483 AppLayerParserThreadCtxFree(alp_tctx
);
1484 StreamTcpFreeConfig(TRUE
);
1489 /** \test Send a split get request. */
1490 static int FTPParserTest03(void)
1494 uint8_t ftpbuf1
[] = "POR";
1495 uint32_t ftplen1
= sizeof(ftpbuf1
) - 1; /* minus the \0 */
1496 uint8_t ftpbuf2
[] = "T 192,168,1";
1497 uint32_t ftplen2
= sizeof(ftpbuf2
) - 1; /* minus the \0 */
1498 uint8_t ftpbuf3
[] = "1,1,10,20\r\n";
1499 uint32_t ftplen3
= sizeof(ftpbuf3
) - 1; /* minus the \0 */
1501 AppLayerParserThreadCtx
*alp_tctx
= AppLayerParserThreadCtxAlloc();
1503 memset(&f
, 0, sizeof(f
));
1504 memset(&ssn
, 0, sizeof(ssn
));
1506 FLOW_INITIALIZE(&f
);
1507 f
.protoctx
= (void *)&ssn
;
1508 f
.proto
= IPPROTO_TCP
;
1509 f
.alproto
= ALPROTO_FTP
;
1511 StreamTcpInitConfig(TRUE
);
1513 FLOWLOCK_WRLOCK(&f
);
1514 int r
= AppLayerParserParse(NULL
, alp_tctx
, &f
, ALPROTO_FTP
,
1515 STREAM_TOSERVER
| STREAM_START
, ftpbuf1
,
1518 SCLogDebug("toserver chunk 1 returned %" PRId32
", expected 0: ", r
);
1520 FLOWLOCK_UNLOCK(&f
);
1523 FLOWLOCK_UNLOCK(&f
);
1525 FLOWLOCK_WRLOCK(&f
);
1526 r
= AppLayerParserParse(NULL
, alp_tctx
, &f
, ALPROTO_FTP
, STREAM_TOSERVER
,
1529 SCLogDebug("toserver chunk 2 returned %" PRId32
", expected 0: ", r
);
1531 FLOWLOCK_UNLOCK(&f
);
1534 FLOWLOCK_UNLOCK(&f
);
1536 FLOWLOCK_WRLOCK(&f
);
1537 r
= AppLayerParserParse(NULL
, alp_tctx
, &f
, ALPROTO_FTP
,
1538 STREAM_TOSERVER
| STREAM_EOF
, ftpbuf3
, ftplen3
);
1540 SCLogDebug("toserver chunk 3 returned %" PRId32
", expected 0: ", r
);
1542 FLOWLOCK_UNLOCK(&f
);
1545 FLOWLOCK_UNLOCK(&f
);
1547 FtpState
*ftp_state
= f
.alstate
;
1548 if (ftp_state
== NULL
) {
1549 SCLogDebug("no ftp state: ");
1554 if (ftp_state
->command
!= FTP_COMMAND_PORT
) {
1555 SCLogDebug("expected command %" PRIu32
", got %" PRIu32
": ", FTP_COMMAND_PORT
, ftp_state
->command
);
1561 if (alp_tctx
!= NULL
)
1562 AppLayerParserThreadCtxFree(alp_tctx
);
1563 StreamTcpFreeConfig(TRUE
);
1567 /** \test See how it deals with an incomplete request. */
1568 static int FTPParserTest06(void)
1572 uint8_t ftpbuf1
[] = "PORT";
1573 uint32_t ftplen1
= sizeof(ftpbuf1
) - 1; /* minus the \0 */
1575 AppLayerParserThreadCtx
*alp_tctx
= AppLayerParserThreadCtxAlloc();
1577 memset(&f
, 0, sizeof(f
));
1578 memset(&ssn
, 0, sizeof(ssn
));
1580 FLOW_INITIALIZE(&f
);
1581 f
.protoctx
= (void *)&ssn
;
1582 f
.proto
= IPPROTO_TCP
;
1583 f
.alproto
= ALPROTO_FTP
;
1585 StreamTcpInitConfig(TRUE
);
1587 FLOWLOCK_WRLOCK(&f
);
1588 int r
= AppLayerParserParse(NULL
, alp_tctx
, &f
, ALPROTO_FTP
,
1589 STREAM_TOSERVER
| STREAM_START
| STREAM_EOF
,
1593 SCLogDebug("toserver chunk 1 returned %" PRId32
", expected 0: ", r
);
1595 FLOWLOCK_UNLOCK(&f
);
1598 FLOWLOCK_UNLOCK(&f
);
1600 FtpState
*ftp_state
= f
.alstate
;
1601 if (ftp_state
== NULL
) {
1602 SCLogDebug("no ftp state: ");
1607 if (ftp_state
->command
!= FTP_COMMAND_UNKNOWN
) {
1608 SCLogDebug("expected command %" PRIu32
", got %" PRIu32
": ", FTP_COMMAND_UNKNOWN
, ftp_state
->command
);
1614 if (alp_tctx
!= NULL
)
1615 AppLayerParserThreadCtxFree(alp_tctx
);
1616 StreamTcpFreeConfig(TRUE
);
1621 /** \test See how it deals with an incomplete request in multiple chunks. */
1622 static int FTPParserTest07(void)
1626 uint8_t ftpbuf1
[] = "PO";
1627 uint32_t ftplen1
= sizeof(ftpbuf1
) - 1; /* minus the \0 */
1628 uint8_t ftpbuf2
[] = "RT\r\n";
1629 uint32_t ftplen2
= sizeof(ftpbuf2
) - 1; /* minus the \0 */
1631 AppLayerParserThreadCtx
*alp_tctx
= AppLayerParserThreadCtxAlloc();
1633 memset(&f
, 0, sizeof(f
));
1634 memset(&ssn
, 0, sizeof(ssn
));
1636 FLOW_INITIALIZE(&f
);
1637 f
.protoctx
= (void *)&ssn
;
1638 f
.proto
= IPPROTO_TCP
;
1639 f
.alproto
= ALPROTO_FTP
;
1641 StreamTcpInitConfig(TRUE
);
1643 FLOWLOCK_WRLOCK(&f
);
1644 int r
= AppLayerParserParse(NULL
, alp_tctx
, &f
, ALPROTO_FTP
,
1645 STREAM_TOSERVER
| STREAM_START
, ftpbuf1
,
1648 SCLogDebug("toserver chunk 1 returned %" PRId32
", expected 0: ", r
);
1650 FLOWLOCK_UNLOCK(&f
);
1653 FLOWLOCK_UNLOCK(&f
);
1655 FLOWLOCK_WRLOCK(&f
);
1656 r
= AppLayerParserParse(NULL
, alp_tctx
, &f
, ALPROTO_FTP
,
1657 STREAM_TOSERVER
| STREAM_EOF
, ftpbuf2
, ftplen2
);
1659 SCLogDebug("toserver chunk 2 returned %" PRId32
", expected 0: ", r
);
1661 FLOWLOCK_UNLOCK(&f
);
1664 FLOWLOCK_UNLOCK(&f
);
1666 FtpState
*ftp_state
= f
.alstate
;
1667 if (ftp_state
== NULL
) {
1668 SCLogDebug("no ftp state: ");
1673 if (ftp_state
->command
!= FTP_COMMAND_PORT
) {
1674 SCLogDebug("expected command %" PRIu32
", got %" PRIu32
": ",
1675 FTP_COMMAND_PORT
, ftp_state
->command
);
1681 if (alp_tctx
!= NULL
)
1682 AppLayerParserThreadCtxFree(alp_tctx
);
1683 StreamTcpFreeConfig(TRUE
);
1688 /** \test Test case where chunks are smaller than the delim length and the
1689 * last chunk is supposed to match the delim. */
1690 static int FTPParserTest10(void)
1694 uint8_t ftpbuf1
[] = "PORT 1,2,3,4,5,6\r\n";
1695 uint32_t ftplen1
= sizeof(ftpbuf1
) - 1; /* minus the \0 */
1697 AppLayerParserThreadCtx
*alp_tctx
= AppLayerParserThreadCtxAlloc();
1699 memset(&f
, 0, sizeof(f
));
1700 memset(&ssn
, 0, sizeof(ssn
));
1702 FLOW_INITIALIZE(&f
);
1703 f
.protoctx
= (void *)&ssn
;
1704 f
.proto
= IPPROTO_TCP
;
1705 f
.alproto
= ALPROTO_FTP
;
1707 StreamTcpInitConfig(TRUE
);
1710 for (u
= 0; u
< ftplen1
; u
++) {
1713 if (u
== 0) flags
= STREAM_TOSERVER
|STREAM_START
;
1714 else if (u
== (ftplen1
- 1)) flags
= STREAM_TOSERVER
|STREAM_EOF
;
1715 else flags
= STREAM_TOSERVER
;
1717 FLOWLOCK_WRLOCK(&f
);
1718 r
= AppLayerParserParse(NULL
, alp_tctx
, &f
, ALPROTO_FTP
, flags
,
1721 SCLogDebug("toserver chunk %" PRIu32
" returned %" PRId32
", expected 0: ", u
, r
);
1723 FLOWLOCK_UNLOCK(&f
);
1726 FLOWLOCK_UNLOCK(&f
);
1729 FtpState
*ftp_state
= f
.alstate
;
1730 if (ftp_state
== NULL
) {
1731 SCLogDebug("no ftp state: ");
1736 if (ftp_state
->command
!= FTP_COMMAND_PORT
) {
1737 SCLogDebug("expected command %" PRIu32
", got %" PRIu32
": ", FTP_COMMAND_PORT
, ftp_state
->command
);
1743 if (alp_tctx
!= NULL
)
1744 AppLayerParserThreadCtxFree(alp_tctx
);
1745 StreamTcpFreeConfig(TRUE
);
1750 /** \test Supply RETR without a filename */
1751 static int FTPParserTest11(void)
1755 uint8_t ftpbuf1
[] = "PORT 192,168,1,1,0,80\r\n";
1756 uint8_t ftpbuf2
[] = "RETR\r\n";
1757 uint8_t ftpbuf3
[] = "227 OK\r\n";
1760 AppLayerParserThreadCtx
*alp_tctx
= AppLayerParserThreadCtxAlloc();
1762 memset(&f
, 0, sizeof(f
));
1763 memset(&ssn
, 0, sizeof(ssn
));
1765 FLOW_INITIALIZE(&f
);
1766 f
.protoctx
= (void *)&ssn
;
1767 f
.proto
= IPPROTO_TCP
;
1768 f
.alproto
= ALPROTO_FTP
;
1770 StreamTcpInitConfig(TRUE
);
1772 FLOWLOCK_WRLOCK(&f
);
1773 int r
= AppLayerParserParse(NULL
, alp_tctx
, &f
, ALPROTO_FTP
,
1774 STREAM_TOSERVER
| STREAM_START
, ftpbuf1
,
1775 sizeof(ftpbuf1
) - 1);
1778 FLOWLOCK_UNLOCK(&f
);
1781 FLOWLOCK_UNLOCK(&f
);
1784 FLOWLOCK_WRLOCK(&f
);
1785 r
= AppLayerParserParse(NULL
, alp_tctx
, &f
, ALPROTO_FTP
,
1788 sizeof(ftpbuf3
) - 1);
1791 FLOWLOCK_UNLOCK(&f
);
1794 FLOWLOCK_UNLOCK(&f
);
1796 FLOWLOCK_WRLOCK(&f
);
1797 r
= AppLayerParserParse(NULL
, alp_tctx
, &f
, ALPROTO_FTP
,
1798 STREAM_TOSERVER
, ftpbuf2
,
1799 sizeof(ftpbuf2
) - 1);
1801 SCLogDebug("parse should've failed");
1803 FLOWLOCK_UNLOCK(&f
);
1806 FLOWLOCK_UNLOCK(&f
);
1808 FtpState
*ftp_state
= f
.alstate
;
1809 if (ftp_state
== NULL
) {
1810 SCLogDebug("no ftp state: ");
1815 if (ftp_state
->command
!= FTP_COMMAND_RETR
) {
1816 SCLogDebug("expected command %" PRIu32
", got %" PRIu32
": ",
1817 FTP_COMMAND_RETR
, ftp_state
->command
);
1823 if (alp_tctx
!= NULL
)
1824 AppLayerParserThreadCtxFree(alp_tctx
);
1825 StreamTcpFreeConfig(TRUE
);
1830 /** \test Supply STOR without a filename */
1831 static int FTPParserTest12(void)
1835 uint8_t ftpbuf1
[] = "PORT 192,168,1,1,0,80\r\n";
1836 uint8_t ftpbuf2
[] = "STOR\r\n";
1837 uint8_t ftpbuf3
[] = "227 OK\r\n";
1840 AppLayerParserThreadCtx
*alp_tctx
= AppLayerParserThreadCtxAlloc();
1842 memset(&f
, 0, sizeof(f
));
1843 memset(&ssn
, 0, sizeof(ssn
));
1845 FLOW_INITIALIZE(&f
);
1846 f
.protoctx
= (void *)&ssn
;
1847 f
.proto
= IPPROTO_TCP
;
1848 f
.alproto
= ALPROTO_FTP
;
1850 StreamTcpInitConfig(TRUE
);
1852 FLOWLOCK_WRLOCK(&f
);
1853 int r
= AppLayerParserParse(NULL
, alp_tctx
, &f
, ALPROTO_FTP
,
1854 STREAM_TOSERVER
| STREAM_START
, ftpbuf1
,
1855 sizeof(ftpbuf1
) - 1);
1858 FLOWLOCK_UNLOCK(&f
);
1861 FLOWLOCK_UNLOCK(&f
);
1864 FLOWLOCK_WRLOCK(&f
);
1865 r
= AppLayerParserParse(NULL
, alp_tctx
, &f
, ALPROTO_FTP
,
1868 sizeof(ftpbuf3
) - 1);
1871 FLOWLOCK_UNLOCK(&f
);
1874 FLOWLOCK_UNLOCK(&f
);
1876 FLOWLOCK_WRLOCK(&f
);
1877 r
= AppLayerParserParse(NULL
, alp_tctx
, &f
, ALPROTO_FTP
,
1878 STREAM_TOSERVER
, ftpbuf2
,
1879 sizeof(ftpbuf2
) - 1);
1881 SCLogDebug("parse should've failed");
1883 FLOWLOCK_UNLOCK(&f
);
1886 FLOWLOCK_UNLOCK(&f
);
1888 FtpState
*ftp_state
= f
.alstate
;
1889 if (ftp_state
== NULL
) {
1890 SCLogDebug("no ftp state: ");
1895 if (ftp_state
->command
!= FTP_COMMAND_STOR
) {
1896 SCLogDebug("expected command %" PRIu32
", got %" PRIu32
": ",
1897 FTP_COMMAND_STOR
, ftp_state
->command
);
1903 if (alp_tctx
!= NULL
)
1904 AppLayerParserThreadCtxFree(alp_tctx
);
1905 StreamTcpFreeConfig(TRUE
);
1909 #endif /* UNITTESTS */
1911 void FTPParserRegisterTests(void)
1914 UtRegisterTest("FTPParserTest01", FTPParserTest01
);
1915 UtRegisterTest("FTPParserTest03", FTPParserTest03
);
1916 UtRegisterTest("FTPParserTest06", FTPParserTest06
);
1917 UtRegisterTest("FTPParserTest07", FTPParserTest07
);
1918 UtRegisterTest("FTPParserTest10", FTPParserTest10
);
1919 UtRegisterTest("FTPParserTest11", FTPParserTest11
);
1920 UtRegisterTest("FTPParserTest12", FTPParserTest12
);
1921 #endif /* UNITTESTS */