1 /* Copyright (C) 2007-2017 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>
24 * App Layer Parser for FTP
27 #include "suricata-common.h"
32 #include "util-print.h"
33 #include "util-pool.h"
35 #include "flow-util.h"
36 #include "flow-storage.h"
38 #include "detect-engine-state.h"
40 #include "stream-tcp-private.h"
41 #include "stream-tcp-reassemble.h"
42 #include "stream-tcp.h"
45 #include "app-layer.h"
46 #include "app-layer-protos.h"
47 #include "app-layer-parser.h"
48 #include "app-layer-ftp.h"
49 #include "app-layer-expectation.h"
52 #include "util-unittest.h"
53 #include "util-debug.h"
54 #include "util-memcmp.h"
55 #include "util-memrchr.h"
56 #include "util-byte.h"
58 #include "util-misc.h"
61 #include "rust-ftp-mod-gen.h"
64 uint64_t ftp_config_memcap
= 0;
66 SC_ATOMIC_DECLARE(uint64_t, ftp_memuse
);
67 SC_ATOMIC_DECLARE(uint64_t, ftp_memcap
);
69 static void FTPParseMemcap(void)
73 /** set config values for memcap, prealloc and hash_size */
74 if ((ConfGet("app-layer.protocols.ftp.memcap", &conf_val
)) == 1)
76 if (ParseSizeStringU64(conf_val
, &ftp_config_memcap
) < 0) {
77 SCLogError(SC_ERR_SIZE_PARSE
, "Error parsing ftp.memcap "
78 "from conf file - %s. Killing engine",
82 SCLogInfo("FTP memcap: %"PRIu64
, ftp_config_memcap
);
84 /* default to unlimited */
85 ftp_config_memcap
= 0;
88 SC_ATOMIC_INIT(ftp_memuse
);
89 SC_ATOMIC_INIT(ftp_memcap
);
92 static void FTPIncrMemuse(uint64_t size
)
94 (void) SC_ATOMIC_ADD(ftp_memuse
, size
);
98 static void FTPDecrMemuse(uint64_t size
)
100 (void) SC_ATOMIC_SUB(ftp_memuse
, size
);
104 uint64_t FTPMemuseGlobalCounter(void)
106 uint64_t tmpval
= SC_ATOMIC_GET(ftp_memuse
);
110 uint64_t FTPMemcapGlobalCounter(void)
112 uint64_t tmpval
= SC_ATOMIC_GET(ftp_memcap
);
117 * \brief Check if alloc'ing "size" would mean we're over memcap
119 * \retval 1 if in bounds
120 * \retval 0 if not in bounds
122 static int FTPCheckMemcap(uint64_t size
)
124 if (ftp_config_memcap
== 0 || size
+ SC_ATOMIC_GET(ftp_memuse
) <= ftp_config_memcap
)
126 (void) SC_ATOMIC_ADD(ftp_memcap
, 1);
130 static void *FTPMalloc(size_t size
)
134 if (FTPCheckMemcap((uint32_t)size
) == 0)
137 ptr
= SCMalloc(size
);
139 if (unlikely(ptr
== NULL
))
142 FTPIncrMemuse((uint64_t)size
);
147 static void *FTPCalloc(size_t n
, size_t size
)
151 if (FTPCheckMemcap((uint32_t)(n
* size
)) == 0)
154 ptr
= SCCalloc(n
, size
);
156 if (unlikely(ptr
== NULL
))
159 FTPIncrMemuse((uint64_t)(n
* size
));
164 static void *FTPRealloc(void *ptr
, size_t orig_size
, size_t size
)
168 if (FTPCheckMemcap((uint32_t)(size
- orig_size
)) == 0)
171 rptr
= SCRealloc(ptr
, size
);
175 if (size
> orig_size
) {
176 FTPIncrMemuse(size
- orig_size
);
178 FTPDecrMemuse(orig_size
- size
);
184 static void FTPFree(void *ptr
, size_t size
)
188 FTPDecrMemuse((uint64_t)size
);
191 static int FTPGetLineForDirection(FtpState
*state
, FtpLineState
*line_state
)
194 if (line_state
->current_line_lf_seen
== 1) {
195 /* we have seen the lf for the previous line. Clear the parser
196 * details to parse new line */
197 line_state
->current_line_lf_seen
= 0;
198 if (line_state
->current_line_db
== 1) {
199 line_state
->current_line_db
= 0;
200 FTPFree(line_state
->db
, line_state
->db_len
);
201 line_state
->db
= NULL
;
202 line_state
->db_len
= 0;
203 state
->current_line
= NULL
;
204 state
->current_line_len
= 0;
208 uint8_t *lf_idx
= memchr(state
->input
, 0x0a, state
->input_len
);
210 if (lf_idx
== NULL
) {
211 /* fragmented lines. Decoder event for special cases. Not all
212 * fragmented lines should be treated as a possible evasion
213 * attempt. With multi payload ftp chunks we can have valid
214 * cases of fragmentation. But within the same segment chunk
215 * if we see fragmentation then it's definitely something you
216 * should alert about */
217 if (line_state
->current_line_db
== 0) {
218 line_state
->db
= FTPMalloc(state
->input_len
);
219 if (line_state
->db
== NULL
) {
222 line_state
->current_line_db
= 1;
223 memcpy(line_state
->db
, state
->input
, state
->input_len
);
224 line_state
->db_len
= state
->input_len
;
226 ptmp
= FTPRealloc(line_state
->db
, line_state
->db_len
,
227 (line_state
->db_len
+ state
->input_len
));
229 FTPFree(line_state
->db
, line_state
->db_len
);
230 line_state
->db
= NULL
;
231 line_state
->db_len
= 0;
234 line_state
->db
= ptmp
;
236 memcpy(line_state
->db
+ line_state
->db_len
,
237 state
->input
, state
->input_len
);
238 line_state
->db_len
+= state
->input_len
;
240 state
->input
+= state
->input_len
;
241 state
->input_len
= 0;
246 line_state
->current_line_lf_seen
= 1;
248 if (line_state
->current_line_db
== 1) {
249 ptmp
= FTPRealloc(line_state
->db
, line_state
->db_len
,
250 (line_state
->db_len
+ (lf_idx
+ 1 - state
->input
)));
252 FTPFree(line_state
->db
, line_state
->db_len
);
253 line_state
->db
= NULL
;
254 line_state
->db_len
= 0;
257 line_state
->db
= ptmp
;
259 memcpy(line_state
->db
+ line_state
->db_len
,
260 state
->input
, (lf_idx
+ 1 - state
->input
));
261 line_state
->db_len
+= (lf_idx
+ 1 - state
->input
);
263 if (line_state
->db_len
> 1 &&
264 line_state
->db
[line_state
->db_len
- 2] == 0x0D) {
265 line_state
->db_len
-= 2;
266 state
->current_line_delimiter_len
= 2;
268 line_state
->db_len
-= 1;
269 state
->current_line_delimiter_len
= 1;
272 state
->current_line
= line_state
->db
;
273 state
->current_line_len
= line_state
->db_len
;
276 state
->current_line
= state
->input
;
277 state
->current_line_len
= lf_idx
- state
->input
;
279 if (state
->input
!= lf_idx
&&
280 *(lf_idx
- 1) == 0x0D) {
281 state
->current_line_len
--;
282 state
->current_line_delimiter_len
= 2;
284 state
->current_line_delimiter_len
= 1;
288 state
->input_len
-= (lf_idx
- state
->input
) + 1;
289 state
->input
= (lf_idx
+ 1);
296 static int FTPGetLine(FtpState
*state
)
300 /* we have run out of input */
301 if (state
->input_len
<= 0)
305 if (state
->direction
== 0)
306 return FTPGetLineForDirection(state
, &state
->line_state
[0]);
308 return FTPGetLineForDirection(state
, &state
->line_state
[1]);
312 * \brief This function is called to determine and set which command is being
313 * transfered to the ftp server
314 * \param ftp_state the ftp state structure for the parser
315 * \param input input line of the command
316 * \param len of the command
318 * \retval 1 when the command is parsed, 0 otherwise
320 static int FTPParseRequestCommand(void *ftp_state
, uint8_t *input
,
324 FtpState
*fstate
= (FtpState
*)ftp_state
;
325 fstate
->command
= FTP_COMMAND_UNKNOWN
;
327 if (input_len
>= 4 && SCMemcmpLowercase("port", input
, 4) == 0) {
328 fstate
->command
= FTP_COMMAND_PORT
;
331 if (input_len
>= 8 && SCMemcmpLowercase("auth tls", input
, 8) == 0) {
332 fstate
->command
= FTP_COMMAND_AUTH_TLS
;
335 if (input_len
>= 4 && SCMemcmpLowercase("pasv", input
, 4) == 0) {
336 fstate
->command
= FTP_COMMAND_PASV
;
339 if (input_len
> 5 && SCMemcmpLowercase("retr", input
, 4) == 0) {
340 fstate
->command
= FTP_COMMAND_RETR
;
343 if (input_len
>= 4 && SCMemcmpLowercase("epsv", input
, 4) == 0) {
344 fstate
->command
= FTP_COMMAND_EPSV
;
347 if (input_len
> 5 && SCMemcmpLowercase("stor", input
, 4) == 0) {
348 fstate
->command
= FTP_COMMAND_STOR
;
354 struct FtpTransferCmd
{
355 /** Need to look like a ExpectationData so DFree must
356 * be first field . */
357 void (*DFree
)(void *);
361 FtpRequestCommand cmd
;
364 static void FtpTransferCmdFree(void *data
)
366 struct FtpTransferCmd
*cmd
= (struct FtpTransferCmd
*) data
;
369 if (cmd
->file_name
) {
370 SCFree(cmd
->file_name
);
372 FTPFree(cmd
, sizeof(struct FtpTransferCmd
));
376 * \brief This function is called to retrieve a ftp request
377 * \param ftp_state the ftp state structure for the parser
378 * \param input input line of the command
379 * \param input_len length of the request
380 * \param output the resulting output
382 * \retval 1 when the command is parsed, 0 otherwise
384 static int FTPParseRequest(Flow
*f
, void *ftp_state
,
385 AppLayerParserState
*pstate
,
386 uint8_t *input
, uint32_t input_len
,
387 void *local_data
, const uint8_t flags
)
390 /* PrintRawDataFp(stdout, input,input_len); */
392 FtpState
*state
= (FtpState
*)ftp_state
;
395 if (input
== NULL
&& AppLayerParserStateIssetFlag(pstate
, APP_LAYER_PARSER_EOF
)) {
397 } else if (input
== NULL
|| input_len
== 0) {
401 state
->input
= input
;
402 state
->input_len
= input_len
;
403 /* toserver stream */
404 state
->direction
= 0;
406 int direction
= STREAM_TOSERVER
;
407 while (FTPGetLine(state
) >= 0) {
408 FTPParseRequestCommand(state
,
409 state
->current_line
, state
->current_line_len
);
410 switch (state
->command
) {
411 case FTP_COMMAND_PORT
:
412 if (state
->current_line_len
> state
->port_line_size
) {
413 ptmp
= FTPRealloc(state
->port_line
, state
->port_line_size
,
414 state
->current_line_len
);
416 FTPFree(state
->port_line
, state
->port_line_size
);
417 state
->port_line
= NULL
;
418 state
->port_line_size
= 0;
421 state
->port_line
= ptmp
;
423 state
->port_line_size
= state
->current_line_len
;
425 memcpy(state
->port_line
, state
->current_line
,
426 state
->current_line_len
);
427 state
->port_line_len
= state
->current_line_len
;
429 case FTP_COMMAND_RETR
:
430 /* change direction (default to server) so expectation will handle
431 * the correct message when expectation will match.
433 direction
= STREAM_TOCLIENT
;
435 case FTP_COMMAND_STOR
:
437 /* No dyn port negotiated so get out */
438 if (state
->dyn_port
== 0) {
441 struct FtpTransferCmd
*data
= FTPCalloc(1, sizeof(struct FtpTransferCmd
));
444 data
->DFree
= FtpTransferCmdFree
;
445 /* Min size has been checked in FTPParseRequestCommand */
446 data
->file_name
= FTPCalloc(state
->current_line_len
- 4, sizeof(char));
447 if (data
->file_name
== NULL
) {
448 FTPFree(data
, sizeof(struct FtpTransferCmd
));
451 data
->file_name
[state
->current_line_len
- 5] = 0;
452 data
->file_len
= state
->current_line_len
- 5;
453 memcpy(data
->file_name
, state
->current_line
+ 5, state
->current_line_len
- 5);
454 data
->cmd
= state
->command
;
455 data
->flow_id
= FlowGetId(f
);
456 int ret
= AppLayerExpectationCreate(f
, direction
, 0,
458 ALPROTO_FTPDATA
, data
);
460 FTPFree(data
, sizeof(struct FtpTransferCmd
));
461 SCLogDebug("No expectation created.");
464 SCLogDebug("Expectation created.");
466 /* reset the dyn port to avoid duplicate */
478 static int FTPParsePassiveResponse(Flow
*f
, FtpState
*state
, uint8_t *input
, uint32_t input_len
)
483 dyn_port
= rs_ftp_pasv_response(input
, input_len
);
488 uint16_t part1
, part2
;
489 uint8_t *ptr
= memrchr(input
, ',', input_len
);
493 part2
= atoi((char *)ptr
+ 1);
494 ptr
= memrchr(input
, ',', (ptr
- input
) - 1);
497 part1
= atoi((char *)ptr
+ 1);
499 dyn_port
= 256 * part1
+ part2
;
501 state
->dyn_port
= dyn_port
;
506 static int FTPParsePassiveResponseV6(Flow
*f
, FtpState
*state
, uint8_t *input
, uint32_t input_len
)
509 uint16_t dyn_port
= rs_ftp_epsv_response(input
, input_len
);
514 state
->dyn_port
= dyn_port
;
516 uint8_t *ptr
= memrchr(input
, '|', input_len
);
520 int n_length
= ptr
- input
- 1;
523 ptr
= memrchr(input
, '|', n_length
);
527 state
->dyn_port
= atoi((char *)ptr
+ 1);
533 * \brief This function is called to retrieve a ftp response
534 * \param ftp_state the ftp state structure for the parser
535 * \param input input line of the command
536 * \param input_len length of the request
537 * \param output the resulting output
539 * \retval 1 when the command is parsed, 0 otherwise
541 static int FTPParseResponse(Flow
*f
, void *ftp_state
, AppLayerParserState
*pstate
,
542 uint8_t *input
, uint32_t input_len
,
543 void *local_data
, const uint8_t flags
)
545 FtpState
*state
= (FtpState
*)ftp_state
;
547 if (state
->command
== FTP_COMMAND_AUTH_TLS
) {
548 if (input_len
>= 4 && SCMemcmp("234 ", input
, 4) == 0) {
549 AppLayerRequestProtocolTLSUpgrade(f
);
553 if (state
->command
== FTP_COMMAND_PASV
) {
554 if (input_len
>= 4 && SCMemcmp("227 ", input
, 4) == 0) {
555 FTPParsePassiveResponse(f
, ftp_state
, input
, input_len
);
559 if (state
->command
== FTP_COMMAND_EPSV
) {
560 if (input_len
>= 4 && SCMemcmp("229 ", input
, 4) == 0) {
561 FTPParsePassiveResponseV6(f
, ftp_state
, input
, input_len
);
569 static SCMutex ftp_state_mem_lock
= SCMUTEX_INITIALIZER
;
570 static uint64_t ftp_state_memuse
= 0;
571 static uint64_t ftp_state_memcnt
= 0;
574 static void *FTPStateAlloc(void)
576 void *s
= FTPMalloc(sizeof(FtpState
));
577 if (unlikely(s
== NULL
))
580 memset(s
, 0, sizeof(FtpState
));
583 SCMutexLock(&ftp_state_mem_lock
);
585 ftp_state_memuse
+=sizeof(FtpState
);
586 SCMutexUnlock(&ftp_state_mem_lock
);
591 static void FTPStateFree(void *s
)
593 FtpState
*fstate
= (FtpState
*) s
;
594 if (fstate
->port_line
!= NULL
)
595 FTPFree(fstate
->port_line
, fstate
->port_line_size
);
596 if (fstate
->line_state
[0].db
)
597 FTPFree(fstate
->line_state
[0].db
, fstate
->line_state
[0].db_len
);
598 if (fstate
->line_state
[1].db
)
599 FTPFree(fstate
->line_state
[1].db
, fstate
->line_state
[1].db_len
);
601 //AppLayerDecoderEventsFreeEvents(&s->decoder_events);
603 if (fstate
->de_state
!= NULL
) {
604 DetectEngineStateFree(fstate
->de_state
);
607 FTPFree(s
, sizeof(FtpState
));
609 SCMutexLock(&ftp_state_mem_lock
);
611 ftp_state_memuse
-=sizeof(FtpState
);
612 SCMutexUnlock(&ftp_state_mem_lock
);
616 static int FTPSetTxDetectState(void *vtx
, DetectEngineState
*de_state
)
618 FtpState
*ftp_state
= (FtpState
*)vtx
;
619 ftp_state
->de_state
= de_state
;
623 static DetectEngineState
*FTPGetTxDetectState(void *vtx
)
625 FtpState
*ftp_state
= (FtpState
*)vtx
;
626 return ftp_state
->de_state
;
629 static void FTPStateTransactionFree(void *state
, uint64_t tx_id
)
634 static void *FTPGetTx(void *state
, uint64_t tx_id
)
636 FtpState
*ftp_state
= (FtpState
*)state
;
640 static uint64_t FTPGetTxCnt(void *state
)
646 static int FTPGetAlstateProgressCompletionStatus(uint8_t direction
)
648 return FTP_STATE_FINISHED
;
651 static int FTPGetAlstateProgress(void *tx
, uint8_t direction
)
653 FtpState
*ftp_state
= (FtpState
*)tx
;
655 if (direction
== STREAM_TOSERVER
&&
656 ftp_state
->command
== FTP_COMMAND_PORT
) {
657 return FTP_STATE_PORT_DONE
;
660 /* TODO: figure out further progress handling */
662 return FTP_STATE_IN_PROGRESS
;
666 static int FTPRegisterPatternsForProtocolDetection(void)
668 if (AppLayerProtoDetectPMRegisterPatternCI(IPPROTO_TCP
, ALPROTO_FTP
,
669 "220 (", 5, 0, STREAM_TOCLIENT
) < 0)
673 if (AppLayerProtoDetectPMRegisterPatternCI(IPPROTO_TCP
, ALPROTO_FTP
,
674 "FEAT", 4, 0, STREAM_TOSERVER
) < 0)
678 if (AppLayerProtoDetectPMRegisterPatternCI(IPPROTO_TCP
, ALPROTO_FTP
,
679 "USER ", 5, 0, STREAM_TOSERVER
) < 0)
683 if (AppLayerProtoDetectPMRegisterPatternCI(IPPROTO_TCP
, ALPROTO_FTP
,
684 "PASS ", 5, 0, STREAM_TOSERVER
) < 0)
688 if (AppLayerProtoDetectPMRegisterPatternCI(IPPROTO_TCP
, ALPROTO_FTP
,
689 "PORT ", 5, 0, STREAM_TOSERVER
) < 0)
698 static StreamingBufferConfig sbcfg
= STREAMING_BUFFER_CONFIG_INITIALIZER
;
701 * \brief This function is called to retrieve a ftp request
702 * \param ftp_state the ftp state structure for the parser
703 * \param input input line of the command
704 * \param input_len length of the request
705 * \param output the resulting output
707 * \retval 1 when the command is parsed, 0 otherwise
709 static int FTPDataParse(Flow
*f
, FtpDataState
*ftpdata_state
,
710 AppLayerParserState
*pstate
,
711 uint8_t *input
, uint32_t input_len
,
712 void *local_data
, int direction
)
714 uint16_t flags
= FileFlowToFlags(f
, direction
);
716 /* we depend on detection engine for file pruning */
717 flags
|= FILE_USE_DETECT
;
718 if (ftpdata_state
->files
== NULL
) {
719 struct FtpTransferCmd
*data
= (struct FtpTransferCmd
*)FlowGetStorageById(f
, AppLayerExpectationGetDataId());
724 ftpdata_state
->files
= FileContainerAlloc();
725 if (ftpdata_state
->files
== NULL
) {
726 FlowFreeStorageById(f
, AppLayerExpectationGetDataId());
730 ftpdata_state
->file_name
= data
->file_name
;
731 ftpdata_state
->file_len
= data
->file_len
;
732 data
->file_name
= NULL
;
734 f
->parent_id
= data
->flow_id
;
735 ftpdata_state
->command
= data
->cmd
;
737 case FTP_COMMAND_STOR
:
738 ftpdata_state
->direction
= STREAM_TOSERVER
;
740 case FTP_COMMAND_RETR
:
741 ftpdata_state
->direction
= STREAM_TOCLIENT
;
747 if (FileOpenFile(ftpdata_state
->files
, &sbcfg
,
748 (uint8_t *) ftpdata_state
->file_name
,
749 ftpdata_state
->file_len
,
750 input
, input_len
, flags
) == NULL
) {
751 SCLogDebug("Can't open file");
754 FlowFreeStorageById(f
, AppLayerExpectationGetDataId());
756 if (input_len
!= 0) {
757 ret
= FileAppendData(ftpdata_state
->files
, input
, input_len
);
760 SCLogDebug("FileAppendData() - file no longer being extracted");
762 } else if (ret
< 0) {
763 SCLogDebug("FileAppendData() failed: %d", ret
);
768 ret
= FileCloseFile(ftpdata_state
->files
, NULL
, 0, flags
);
769 ftpdata_state
->state
= FTPDATA_STATE_FINISHED
;
775 if (input_len
&& AppLayerParserStateIssetFlag(pstate
, APP_LAYER_PARSER_EOF
)) {
776 ret
= FileCloseFile(ftpdata_state
->files
, (uint8_t *) NULL
, 0, flags
);
777 ftpdata_state
->state
= FTPDATA_STATE_FINISHED
;
781 if (ftpdata_state
->files
) {
782 FilePrune(ftpdata_state
->files
);
787 static int FTPDataParseRequest(Flow
*f
, void *ftp_state
,
788 AppLayerParserState
*pstate
,
789 uint8_t *input
, uint32_t input_len
,
790 void *local_data
, const uint8_t flags
)
792 return FTPDataParse(f
, ftp_state
, pstate
, input
, input_len
,
793 local_data
, STREAM_TOSERVER
);
796 static int FTPDataParseResponse(Flow
*f
, void *ftp_state
,
797 AppLayerParserState
*pstate
,
798 uint8_t *input
, uint32_t input_len
,
799 void *local_data
, const uint8_t flags
)
801 return FTPDataParse(f
, ftp_state
, pstate
, input
, input_len
,
802 local_data
, STREAM_TOCLIENT
);
806 static SCMutex ftpdata_state_mem_lock
= SCMUTEX_INITIALIZER
;
807 static uint64_t ftpdata_state_memuse
= 0;
808 static uint64_t ftpdata_state_memcnt
= 0;
811 static void *FTPDataStateAlloc(void)
813 void *s
= FTPMalloc(sizeof(FtpDataState
));
814 if (unlikely(s
== NULL
))
817 memset(s
, 0, sizeof(FtpDataState
));
818 ((FtpDataState
*)s
)->state
= FTPDATA_STATE_IN_PROGRESS
;
821 SCMutexLock(&ftpdata_state_mem_lock
);
822 ftpdata_state_memcnt
++;
823 ftpdata_state_memuse
+=sizeof(FtpDataState
);
824 SCMutexUnlock(&ftpdata_state_mem_lock
);
829 static void FTPDataStateFree(void *s
)
831 FtpDataState
*fstate
= (FtpDataState
*) s
;
833 if (fstate
->de_state
!= NULL
) {
834 DetectEngineStateFree(fstate
->de_state
);
836 if (fstate
->file_name
!= NULL
) {
837 FTPFree(fstate
->file_name
, fstate
->file_len
);
840 FileContainerFree(fstate
->files
);
844 SCMutexLock(&ftpdata_state_mem_lock
);
845 ftpdata_state_memcnt
--;
846 ftpdata_state_memuse
-=sizeof(FtpDataState
);
847 SCMutexUnlock(&ftpdata_state_mem_lock
);
851 static int FTPDataSetTxDetectState(void *vtx
, DetectEngineState
*de_state
)
853 FtpDataState
*ftp_state
= (FtpDataState
*)vtx
;
854 ftp_state
->de_state
= de_state
;
858 static DetectEngineState
*FTPDataGetTxDetectState(void *vtx
)
860 FtpDataState
*ftp_state
= (FtpDataState
*)vtx
;
861 return ftp_state
->de_state
;
864 static void FTPDataStateTransactionFree(void *state
, uint64_t tx_id
)
869 static void *FTPDataGetTx(void *state
, uint64_t tx_id
)
871 FtpDataState
*ftp_state
= (FtpDataState
*)state
;
875 static uint64_t FTPDataGetTxCnt(void *state
)
877 /* ftp-data is single tx */
881 static int FTPDataGetAlstateProgressCompletionStatus(uint8_t direction
)
883 return FTPDATA_STATE_FINISHED
;
886 static int FTPDataGetAlstateProgress(void *tx
, uint8_t direction
)
888 FtpDataState
*ftpdata_state
= (FtpDataState
*)tx
;
889 return ftpdata_state
->state
;
892 static FileContainer
*FTPDataStateGetFiles(void *state
, uint8_t direction
)
894 FtpDataState
*ftpdata_state
= (FtpDataState
*)state
;
896 if (direction
!= ftpdata_state
->direction
)
897 SCReturnPtr(NULL
, "FileContainer");
899 SCReturnPtr(ftpdata_state
->files
, "FileContainer");
902 void RegisterFTPParsers(void)
904 const char *proto_name
= "ftp";
905 const char *proto_data_name
= "ftp-data";
908 if (AppLayerProtoDetectConfProtoDetectionEnabled("tcp", proto_name
)) {
909 AppLayerProtoDetectRegisterProtocol(ALPROTO_FTP
, proto_name
);
910 if (FTPRegisterPatternsForProtocolDetection() < 0 )
912 AppLayerProtoDetectRegisterProtocol(ALPROTO_FTPDATA
, proto_data_name
);
915 if (AppLayerParserConfParserEnabled("tcp", proto_name
)) {
916 AppLayerParserRegisterParser(IPPROTO_TCP
, ALPROTO_FTP
, STREAM_TOSERVER
,
918 AppLayerParserRegisterParser(IPPROTO_TCP
, ALPROTO_FTP
, STREAM_TOCLIENT
,
920 AppLayerParserRegisterStateFuncs(IPPROTO_TCP
, ALPROTO_FTP
, FTPStateAlloc
, FTPStateFree
);
921 AppLayerParserRegisterParserAcceptableDataDirection(IPPROTO_TCP
, ALPROTO_FTP
, STREAM_TOSERVER
| STREAM_TOCLIENT
);
923 AppLayerParserRegisterTxFreeFunc(IPPROTO_TCP
, ALPROTO_FTP
, FTPStateTransactionFree
);
925 AppLayerParserRegisterDetectStateFuncs(IPPROTO_TCP
, ALPROTO_FTP
,
926 FTPGetTxDetectState
, FTPSetTxDetectState
);
928 AppLayerParserRegisterGetTx(IPPROTO_TCP
, ALPROTO_FTP
, FTPGetTx
);
930 AppLayerParserRegisterGetTxCnt(IPPROTO_TCP
, ALPROTO_FTP
, FTPGetTxCnt
);
932 AppLayerParserRegisterGetStateProgressFunc(IPPROTO_TCP
, ALPROTO_FTP
, FTPGetAlstateProgress
);
934 AppLayerParserRegisterGetStateProgressCompletionStatus(ALPROTO_FTP
,
935 FTPGetAlstateProgressCompletionStatus
);
938 AppLayerRegisterExpectationProto(IPPROTO_TCP
, ALPROTO_FTPDATA
);
939 AppLayerParserRegisterParser(IPPROTO_TCP
, ALPROTO_FTPDATA
, STREAM_TOSERVER
,
940 FTPDataParseRequest
);
941 AppLayerParserRegisterParser(IPPROTO_TCP
, ALPROTO_FTPDATA
, STREAM_TOCLIENT
,
942 FTPDataParseResponse
);
943 AppLayerParserRegisterStateFuncs(IPPROTO_TCP
, ALPROTO_FTPDATA
, FTPDataStateAlloc
, FTPDataStateFree
);
944 AppLayerParserRegisterParserAcceptableDataDirection(IPPROTO_TCP
, ALPROTO_FTPDATA
, STREAM_TOSERVER
| STREAM_TOCLIENT
);
945 AppLayerParserRegisterTxFreeFunc(IPPROTO_TCP
, ALPROTO_FTPDATA
, FTPDataStateTransactionFree
);
946 AppLayerParserRegisterDetectStateFuncs(IPPROTO_TCP
, ALPROTO_FTPDATA
,
947 FTPDataGetTxDetectState
, FTPDataSetTxDetectState
);
949 AppLayerParserRegisterGetFilesFunc(IPPROTO_TCP
, ALPROTO_FTPDATA
, FTPDataStateGetFiles
);
951 AppLayerParserRegisterGetTx(IPPROTO_TCP
, ALPROTO_FTPDATA
, FTPDataGetTx
);
953 AppLayerParserRegisterGetTxCnt(IPPROTO_TCP
, ALPROTO_FTPDATA
, FTPDataGetTxCnt
);
955 AppLayerParserRegisterGetStateProgressFunc(IPPROTO_TCP
, ALPROTO_FTPDATA
, FTPDataGetAlstateProgress
);
957 AppLayerParserRegisterGetStateProgressCompletionStatus(ALPROTO_FTPDATA
,
958 FTPDataGetAlstateProgressCompletionStatus
);
960 sbcfg
.buf_size
= 4096;
961 sbcfg
.Malloc
= FTPMalloc
;
962 sbcfg
.Calloc
= FTPCalloc
;
963 sbcfg
.Realloc
= FTPRealloc
;
964 sbcfg
.Free
= FTPFree
;
968 SCLogInfo("Parsed disabled for %s protocol. Protocol detection"
969 "still on.", proto_name
);
972 AppLayerParserRegisterProtocolUnittests(IPPROTO_TCP
, ALPROTO_FTP
, FTPParserRegisterTests
);
976 void FTPAtExitPrintStats(void)
979 SCMutexLock(&ftp_state_mem_lock
);
980 SCLogDebug("ftp_state_memcnt %"PRIu64
", ftp_state_memuse %"PRIu64
"",
981 ftp_state_memcnt
, ftp_state_memuse
);
982 SCMutexUnlock(&ftp_state_mem_lock
);
987 #ifdef HAVE_LIBJANSSON
988 json_t
*JsonFTPDataAddMetadata(const Flow
*f
)
990 const FtpDataState
*ftp_state
= NULL
;
991 if (f
->alstate
== NULL
)
993 ftp_state
= (FtpDataState
*)f
->alstate
;
994 json_t
*ftpd
= json_object();
997 if (ftp_state
->file_name
) {
998 char *s
= BytesToString(ftp_state
->file_name
, ftp_state
->file_len
);
999 json_object_set_new(ftpd
, "filename", json_string(s
));
1003 switch (ftp_state
->command
) {
1004 case FTP_COMMAND_STOR
:
1005 json_object_set_new(ftpd
, "command", json_string("STOR"));
1007 case FTP_COMMAND_RETR
:
1008 json_object_set_new(ftpd
, "command", json_string("RETR"));
1015 #endif /* HAVE_LIBJANSSON */
1020 /** \test Send a get request in one chunk. */
1021 static int FTPParserTest01(void)
1025 uint8_t ftpbuf
[] = "PORT 192,168,1,1,0,80\r\n";
1026 uint32_t ftplen
= sizeof(ftpbuf
) - 1; /* minus the \0 */
1028 AppLayerParserThreadCtx
*alp_tctx
= AppLayerParserThreadCtxAlloc();
1030 memset(&f
, 0, sizeof(f
));
1031 memset(&ssn
, 0, sizeof(ssn
));
1033 FLOW_INITIALIZE(&f
);
1034 f
.protoctx
= (void *)&ssn
;
1035 f
.proto
= IPPROTO_TCP
;
1036 f
.alproto
= ALPROTO_FTP
;
1038 StreamTcpInitConfig(TRUE
);
1040 FLOWLOCK_WRLOCK(&f
);
1041 int r
= AppLayerParserParse(NULL
, alp_tctx
, &f
, ALPROTO_FTP
,
1042 STREAM_TOSERVER
| STREAM_EOF
, ftpbuf
, ftplen
);
1044 SCLogDebug("toserver chunk 1 returned %" PRId32
", expected 0: ", r
);
1046 FLOWLOCK_UNLOCK(&f
);
1049 FLOWLOCK_UNLOCK(&f
);
1051 FtpState
*ftp_state
= f
.alstate
;
1052 if (ftp_state
== NULL
) {
1053 SCLogDebug("no ftp state: ");
1058 if (ftp_state
->command
!= FTP_COMMAND_PORT
) {
1059 SCLogDebug("expected command %" PRIu32
", got %" PRIu32
": ", FTP_COMMAND_PORT
, ftp_state
->command
);
1065 if (alp_tctx
!= NULL
)
1066 AppLayerParserThreadCtxFree(alp_tctx
);
1067 StreamTcpFreeConfig(TRUE
);
1072 /** \test Send a splitted get request. */
1073 static int FTPParserTest03(void)
1077 uint8_t ftpbuf1
[] = "POR";
1078 uint32_t ftplen1
= sizeof(ftpbuf1
) - 1; /* minus the \0 */
1079 uint8_t ftpbuf2
[] = "T 192,168,1";
1080 uint32_t ftplen2
= sizeof(ftpbuf2
) - 1; /* minus the \0 */
1081 uint8_t ftpbuf3
[] = "1,1,10,20\r\n";
1082 uint32_t ftplen3
= sizeof(ftpbuf3
) - 1; /* minus the \0 */
1084 AppLayerParserThreadCtx
*alp_tctx
= AppLayerParserThreadCtxAlloc();
1086 memset(&f
, 0, sizeof(f
));
1087 memset(&ssn
, 0, sizeof(ssn
));
1089 FLOW_INITIALIZE(&f
);
1090 f
.protoctx
= (void *)&ssn
;
1091 f
.proto
= IPPROTO_TCP
;
1092 f
.alproto
= ALPROTO_FTP
;
1094 StreamTcpInitConfig(TRUE
);
1096 FLOWLOCK_WRLOCK(&f
);
1097 int r
= AppLayerParserParse(NULL
, alp_tctx
, &f
, ALPROTO_FTP
,
1098 STREAM_TOSERVER
| STREAM_START
, ftpbuf1
,
1101 SCLogDebug("toserver chunk 1 returned %" PRId32
", expected 0: ", r
);
1103 FLOWLOCK_UNLOCK(&f
);
1106 FLOWLOCK_UNLOCK(&f
);
1108 FLOWLOCK_WRLOCK(&f
);
1109 r
= AppLayerParserParse(NULL
, alp_tctx
, &f
, ALPROTO_FTP
, STREAM_TOSERVER
,
1112 SCLogDebug("toserver chunk 2 returned %" PRId32
", expected 0: ", r
);
1114 FLOWLOCK_UNLOCK(&f
);
1117 FLOWLOCK_UNLOCK(&f
);
1119 FLOWLOCK_WRLOCK(&f
);
1120 r
= AppLayerParserParse(NULL
, alp_tctx
, &f
, ALPROTO_FTP
,
1121 STREAM_TOSERVER
| STREAM_EOF
, ftpbuf3
, ftplen3
);
1123 SCLogDebug("toserver chunk 3 returned %" PRId32
", expected 0: ", r
);
1125 FLOWLOCK_UNLOCK(&f
);
1128 FLOWLOCK_UNLOCK(&f
);
1130 FtpState
*ftp_state
= f
.alstate
;
1131 if (ftp_state
== NULL
) {
1132 SCLogDebug("no ftp state: ");
1137 if (ftp_state
->command
!= FTP_COMMAND_PORT
) {
1138 SCLogDebug("expected command %" PRIu32
", got %" PRIu32
": ", FTP_COMMAND_PORT
, ftp_state
->command
);
1144 if (alp_tctx
!= NULL
)
1145 AppLayerParserThreadCtxFree(alp_tctx
);
1146 StreamTcpFreeConfig(TRUE
);
1150 /** \test See how it deals with an incomplete request. */
1151 static int FTPParserTest06(void)
1155 uint8_t ftpbuf1
[] = "PORT";
1156 uint32_t ftplen1
= sizeof(ftpbuf1
) - 1; /* minus the \0 */
1158 AppLayerParserThreadCtx
*alp_tctx
= AppLayerParserThreadCtxAlloc();
1160 memset(&f
, 0, sizeof(f
));
1161 memset(&ssn
, 0, sizeof(ssn
));
1163 FLOW_INITIALIZE(&f
);
1164 f
.protoctx
= (void *)&ssn
;
1165 f
.proto
= IPPROTO_TCP
;
1166 f
.alproto
= ALPROTO_FTP
;
1168 StreamTcpInitConfig(TRUE
);
1170 FLOWLOCK_WRLOCK(&f
);
1171 int r
= AppLayerParserParse(NULL
, alp_tctx
, &f
, ALPROTO_FTP
,
1172 STREAM_TOSERVER
| STREAM_START
| STREAM_EOF
,
1176 SCLogDebug("toserver chunk 1 returned %" PRId32
", expected 0: ", r
);
1178 FLOWLOCK_UNLOCK(&f
);
1181 FLOWLOCK_UNLOCK(&f
);
1183 FtpState
*ftp_state
= f
.alstate
;
1184 if (ftp_state
== NULL
) {
1185 SCLogDebug("no ftp state: ");
1190 if (ftp_state
->command
!= FTP_COMMAND_UNKNOWN
) {
1191 SCLogDebug("expected command %" PRIu32
", got %" PRIu32
": ", FTP_COMMAND_UNKNOWN
, ftp_state
->command
);
1197 if (alp_tctx
!= NULL
)
1198 AppLayerParserThreadCtxFree(alp_tctx
);
1199 StreamTcpFreeConfig(TRUE
);
1204 /** \test See how it deals with an incomplete request in multiple chunks. */
1205 static int FTPParserTest07(void)
1209 uint8_t ftpbuf1
[] = "PO";
1210 uint32_t ftplen1
= sizeof(ftpbuf1
) - 1; /* minus the \0 */
1211 uint8_t ftpbuf2
[] = "RT\r\n";
1212 uint32_t ftplen2
= sizeof(ftpbuf2
) - 1; /* minus the \0 */
1214 AppLayerParserThreadCtx
*alp_tctx
= AppLayerParserThreadCtxAlloc();
1216 memset(&f
, 0, sizeof(f
));
1217 memset(&ssn
, 0, sizeof(ssn
));
1219 FLOW_INITIALIZE(&f
);
1220 f
.protoctx
= (void *)&ssn
;
1221 f
.proto
= IPPROTO_TCP
;
1222 f
.alproto
= ALPROTO_FTP
;
1224 StreamTcpInitConfig(TRUE
);
1226 FLOWLOCK_WRLOCK(&f
);
1227 int r
= AppLayerParserParse(NULL
, alp_tctx
, &f
, ALPROTO_FTP
,
1228 STREAM_TOSERVER
| STREAM_START
, ftpbuf1
,
1231 SCLogDebug("toserver chunk 1 returned %" PRId32
", expected 0: ", r
);
1233 FLOWLOCK_UNLOCK(&f
);
1236 FLOWLOCK_UNLOCK(&f
);
1238 FLOWLOCK_WRLOCK(&f
);
1239 r
= AppLayerParserParse(NULL
, alp_tctx
, &f
, ALPROTO_FTP
,
1240 STREAM_TOSERVER
| STREAM_EOF
, ftpbuf2
, ftplen2
);
1242 SCLogDebug("toserver chunk 2 returned %" PRId32
", expected 0: ", r
);
1244 FLOWLOCK_UNLOCK(&f
);
1247 FLOWLOCK_UNLOCK(&f
);
1249 FtpState
*ftp_state
= f
.alstate
;
1250 if (ftp_state
== NULL
) {
1251 SCLogDebug("no ftp state: ");
1256 if (ftp_state
->command
!= FTP_COMMAND_PORT
) {
1257 SCLogDebug("expected command %" PRIu32
", got %" PRIu32
": ",
1258 FTP_COMMAND_PORT
, ftp_state
->command
);
1264 if (alp_tctx
!= NULL
)
1265 AppLayerParserThreadCtxFree(alp_tctx
);
1266 StreamTcpFreeConfig(TRUE
);
1271 /** \test Test case where chunks are smaller than the delim length and the
1272 * last chunk is supposed to match the delim. */
1273 static int FTPParserTest10(void)
1277 uint8_t ftpbuf1
[] = "PORT 1,2,3,4,5,6\r\n";
1278 uint32_t ftplen1
= sizeof(ftpbuf1
) - 1; /* minus the \0 */
1280 AppLayerParserThreadCtx
*alp_tctx
= AppLayerParserThreadCtxAlloc();
1282 memset(&f
, 0, sizeof(f
));
1283 memset(&ssn
, 0, sizeof(ssn
));
1285 FLOW_INITIALIZE(&f
);
1286 f
.protoctx
= (void *)&ssn
;
1287 f
.proto
= IPPROTO_TCP
;
1288 f
.alproto
= ALPROTO_FTP
;
1290 StreamTcpInitConfig(TRUE
);
1293 for (u
= 0; u
< ftplen1
; u
++) {
1296 if (u
== 0) flags
= STREAM_TOSERVER
|STREAM_START
;
1297 else if (u
== (ftplen1
- 1)) flags
= STREAM_TOSERVER
|STREAM_EOF
;
1298 else flags
= STREAM_TOSERVER
;
1300 FLOWLOCK_WRLOCK(&f
);
1301 r
= AppLayerParserParse(NULL
, alp_tctx
, &f
, ALPROTO_FTP
, flags
,
1304 SCLogDebug("toserver chunk %" PRIu32
" returned %" PRId32
", expected 0: ", u
, r
);
1306 FLOWLOCK_UNLOCK(&f
);
1309 FLOWLOCK_UNLOCK(&f
);
1312 FtpState
*ftp_state
= f
.alstate
;
1313 if (ftp_state
== NULL
) {
1314 SCLogDebug("no ftp state: ");
1319 if (ftp_state
->command
!= FTP_COMMAND_PORT
) {
1320 SCLogDebug("expected command %" PRIu32
", got %" PRIu32
": ", FTP_COMMAND_PORT
, ftp_state
->command
);
1326 if (alp_tctx
!= NULL
)
1327 AppLayerParserThreadCtxFree(alp_tctx
);
1328 StreamTcpFreeConfig(TRUE
);
1332 #endif /* UNITTESTS */
1334 void FTPParserRegisterTests(void)
1337 UtRegisterTest("FTPParserTest01", FTPParserTest01
);
1338 UtRegisterTest("FTPParserTest03", FTPParserTest03
);
1339 UtRegisterTest("FTPParserTest06", FTPParserTest06
);
1340 UtRegisterTest("FTPParserTest07", FTPParserTest07
);
1341 UtRegisterTest("FTPParserTest10", FTPParserTest10
);
1342 #endif /* UNITTESTS */