46 #define FTP_MPM mpm_default_matcher
48 static MpmCtx *ftp_mpm_ctx = NULL;
59 static void FTPParseMemcap(
void)
67 static void FTPIncrMemuse(uint64_t size)
72 static void FTPDecrMemuse(uint64_t size)
105 static int FTPCheckMemcap(uint64_t size)
113 static void *FTPCalloc(
size_t n,
size_t size)
115 if (FTPCheckMemcap((uint32_t)(n * size)) == 0) {
127 FTPIncrMemuse((uint64_t)(n * size));
131 static void *FTPRealloc(
void *ptr,
size_t orig_size,
size_t size)
133 if (FTPCheckMemcap((uint32_t)(size - orig_size)) == 0) {
144 if (size > orig_size) {
145 FTPIncrMemuse(size - orig_size);
147 FTPDecrMemuse(orig_size - size);
153 static void FTPFree(
void *ptr,
size_t size)
157 FTPDecrMemuse((uint64_t)size);
164 FTPIncrMemuse(response->total_size);
173 FTPDecrMemuse(wrapper->
response->total_size);
174 SCFTPFreeResponseLine(wrapper->
response);
180 static void *FTPLocalStorageAlloc(
void)
189 if (td->
pmq == NULL) {
202 static void FTPLocalStorageFree(
void *ptr)
206 if (td->
pmq != NULL) {
237 SCLogDebug(
"new transaction %p (state tx cnt %"PRIu64
")", tx, state->
tx_cnt);
245 SCAppLayerTxDataCleanup(&tx->
tx_data);
252 while ((wrapper =
TAILQ_FIRST(&tx->response_list))) {
254 FTPResponseWrapperFree(wrapper);
257 FTPFree(tx,
sizeof(*tx));
267 static AppLayerResult FTPGetLineForDirection(
276 uint8_t *lf_idx = memchr(input->
buf + input->
consumed, 0x0a, input->
len);
278 if (lf_idx == NULL) {
280 *current_line_truncated =
true;
288 }
else if (*current_line_truncated) {
290 *current_line_truncated =
false;
300 uint32_t o_consumed = input->
consumed;
301 input->
consumed = (uint32_t)(lf_idx - input->
buf + 1);
306 line->
buf = input->
buf + o_consumed;
308 *current_line_truncated =
true;
333 static int FTPParseRequestCommand(
344 uint8_t command_code;
345 if (SCGetFtpCommandInfo(td->
pmq->
rule_id_array[0], NULL, &command_code, NULL)) {
356 const char *command_name = NULL;
357 (void)SCGetFtpCommandInfo(td->
pmq->
rule_id_array[0], &command_name, NULL, NULL);
358 SCLogDebug(
"matching FTP command is %s [code: %d, index %d]", command_name,
368 static void FtpTransferCmdFree(
void *data)
370 FtpTransferCmd *cmd = (FtpTransferCmd *)data;
373 if (cmd->file_name) {
374 FTPFree((
void *)cmd->file_name, cmd->file_len + 1);
376 SCFTPTransferCmdFree(cmd);
377 FTPDecrMemuse((uint64_t)
sizeof(FtpTransferCmd));
380 static uint32_t CopyCommandLine(uint8_t **dest,
FtpLineState *line)
383 uint8_t *where = FTPCalloc(line->
len + 1,
sizeof(
char));
387 memcpy(where, line->
buf, line->
len);
390 while (line->
len && isspace((
unsigned char)where[line->
len - 1])) {
394 where[line->
len] =
'\0';
398 return line->
len ? line->
len + 1 : 0;
411 StreamSlice stream_slice,
void *local_data)
421 const uint8_t *input = StreamSliceGetData(&stream_slice);
422 uint32_t input_len = StreamSliceGetDataLen(&stream_slice);
426 }
else if (input == NULL || input_len == 0) {
430 FtpInput ftpi = { .
buf = input, .len = input_len, .orig_len = input_len, .consumed = 0 };
431 FtpLineState line = { .
buf = NULL, .len = 0, .delim_len = 0, .lf_found =
false };
433 uint8_t direction = STREAM_TOSERVER;
437 if (res.status == 1) {
439 }
else if (res.status == -1) {
444 if (!FTPParseRequestCommand(thread_data, &line, &cmd_descriptor)) {
445 state->
command = FTP_COMMAND_UNKNOWN;
473 direction = STREAM_TOCLIENT;
477 case FTP_COMMAND_EPRT:
479 case FTP_COMMAND_PORT:
498 case FTP_COMMAND_RETR:
500 case FTP_COMMAND_STOR: {
507 FtpTransferCmd *data = SCFTPTransferCmdNew();
510 FTPIncrMemuse((uint64_t)(
sizeof *data));
511 data->data_free = FtpTransferCmdFree;
518 #if SC_FILENAME_MAX > UINT16_MAX
519 #error SC_FILENAME_MAX is greater than UINT16_MAX
521 data->file_name = FTPCalloc(file_name_len + 1,
sizeof(
char));
522 if (data->file_name == NULL) {
523 FtpTransferCmdFree(data);
526 data->file_name[file_name_len] = 0;
527 data->file_len = (uint16_t)file_name_len;
528 memcpy(data->file_name, line.
buf + 5, file_name_len);
530 data->flow_id = FlowGetId(f);
531 data->direction = direction;
535 FtpTransferCmdFree(data);
539 SCLogDebug(
"Expectation created [direction: %s, dynamic port %"PRIu16
"].",
540 state->
active ?
"to server" :
"to client",
561 static int FTPParsePassiveResponse(
FtpState *state,
const uint8_t *input, uint32_t input_len)
563 uint16_t dyn_port = rs_ftp_pasv_response(input, input_len);
567 SCLogDebug(
"FTP passive mode (v4): dynamic port %"PRIu16
"", dyn_port);
576 static int FTPParsePassiveResponseV6(
FtpState *state,
const uint8_t *input, uint32_t input_len)
578 uint16_t dyn_port = rs_ftp_epsv_response(input, input_len);
582 SCLogDebug(
"FTP passive mode (v6): dynamic port %"PRIu16
"", dyn_port);
599 static inline bool FTPIsPPR(
const uint8_t *input, uint32_t input_len)
601 return input_len >= 4 && isdigit(input[0]) && input[0] ==
'1' &&
602 isdigit(input[1]) && isdigit(input[2]) && isspace(input[3]);
615 StreamSlice stream_slice,
void *local_data)
619 const uint8_t *input = StreamSliceGetData(&stream_slice);
620 uint32_t input_len = StreamSliceGetDataLen(&stream_slice);
625 FtpInput ftpi = { .
buf = input, .len = input_len, .orig_len = input_len, .consumed = 0 };
626 FtpLineState line = { .
buf = NULL, .len = 0, .delim_len = 0, .lf_found =
false };
632 if (res.status == 1) {
634 }
else if (res.status == -1) {
639 tx = FTPTransactionCreate(state);
646 if (state->
command == FTP_COMMAND_UNKNOWN) {
654 case FTP_COMMAND_AUTH_TLS:
660 case FTP_COMMAND_EPRT:
669 SCLogDebug(
"FTP active mode (v6): dynamic port %" PRIu16
"", dyn_port);
672 case FTP_COMMAND_PORT:
681 SCLogDebug(
"FTP active mode (v4): dynamic port %" PRIu16
"", dyn_port);
684 case FTP_COMMAND_PASV:
686 FTPParsePassiveResponse(ftp_state, line.
buf, line.
len);
690 case FTP_COMMAND_EPSV:
692 FTPParsePassiveResponseV6(ftp_state, line.
buf, line.
len);
700 FTPResponseLine *response = SCFTPParseResponseLine((
const char *)line.
buf, line.
len);
705 if (response->truncated) {
707 &tx->
tx_data.events, FtpEventResponseCommandTooLong);
714 SCFTPFreeResponseLine(response);
717 SCLogDebug(
"unable to parse FTP response line \"%s\"", line.
buf);
722 if (FTPIsPPR(line.
buf, line.
len)) {
740 static uint64_t ftp_state_memuse = 0;
741 static uint64_t ftp_state_memcnt = 0;
744 static void *FTPStateAlloc(
void *orig_state,
AppProto proto_orig)
746 void *s = FTPCalloc(1,
sizeof(
FtpState));
762 static void FTPStateFree(
void *s)
773 const char *command_name = NULL;
774 (void)SCGetFtpCommandInfo(
776 SCLogDebug(
"[%s] state %p id %" PRIu64
", Freeing %d bytes at %p",
782 FTPTransactionFree(tx);
805 SCLogDebug(
"NULL state object; no transactions available");
822 SCLogDebug(
"Returning OLDEST tx %p id %"PRIu64, lasttx, lasttx->
tx_id);
826 static void *FTPGetTx(
void *state, uint64_t tx_id)
832 if (ftp_state->
curr_tx == NULL)
838 if (tx->
tx_id == tx_id)
851 static AppLayerStateData *FTPGetStateData(
void *vstate)
857 static void FTPStateTransactionFree(
void *state, uint64_t tx_id)
862 if (tx_id < tx->tx_id)
864 else if (tx_id > tx->
tx_id)
870 FTPTransactionFree(tx);
875 static uint64_t FTPGetTxCnt(
void *state)
886 static int FTPGetAlstateProgress(
void *vtx, uint8_t direction)
892 if (direction == STREAM_TOSERVER &&
894 return FTP_STATE_PORT_DONE;
896 return FTP_STATE_IN_PROGRESS;
899 return FTP_STATE_FINISHED;
902 static AppProto FTPUserProbingParser(
903 Flow *f, uint8_t direction,
const uint8_t *input, uint32_t
len, uint8_t *rdir)
912 static AppProto FTPServerProbingParser(
913 Flow *f, uint8_t direction,
const uint8_t *input, uint32_t
len, uint8_t *rdir)
920 if (input[0] !=
'2' || input[1] !=
'2' || input[2] !=
'0') {
924 if (input[3] !=
' ' && input[3] !=
'-') {
930 if (memchr(input + 4,
'\n',
len - 4) != NULL) {
937 static int FTPRegisterPatternsForProtocolDetection(
void)
940 IPPROTO_TCP,
ALPROTO_FTP,
"220 (", 5, 0, STREAM_TOCLIENT) < 0) {
944 IPPROTO_TCP,
ALPROTO_FTP,
"FEAT", 4, 0, STREAM_TOSERVER) < 0) {
948 STREAM_TOSERVER, FTPUserProbingParser, 5, 5) < 0) {
952 IPPROTO_TCP,
ALPROTO_FTP,
"PASS ", 5, 0, STREAM_TOSERVER) < 0) {
956 IPPROTO_TCP,
ALPROTO_FTP,
"PORT ", 5, 0, STREAM_TOSERVER) < 0) {
962 "tcp", IPPROTO_TCP,
"ftp",
ALPROTO_FTP, 0, 5, NULL, FTPServerProbingParser)) {
966 FTPServerProbingParser);
982 AppLayerParserState *pstate, StreamSlice stream_slice,
void *local_data, uint8_t direction)
984 const uint8_t *input = StreamSliceGetData(&stream_slice);
985 uint32_t input_len = StreamSliceGetDataLen(&stream_slice);
986 const bool eof = (direction & STREAM_TOSERVER)
990 SCTxDataUpdateFileFlags(&ftpdata_state->
tx_data, ftpdata_state->
state_data.file_flags);
991 if (ftpdata_state->
tx_data.file_tx == 0)
992 ftpdata_state->
tx_data.file_tx = direction & (STREAM_TOSERVER | STREAM_TOCLIENT);
993 if (direction & STREAM_TOSERVER) {
994 ftpdata_state->
tx_data.updated_ts =
true;
996 ftpdata_state->
tx_data.updated_tc =
true;
1002 SCLogDebug(
"FTP-DATA input_len %u flags %04x dir %d/%s EOF %s", input_len,
flags, direction,
1003 (direction & STREAM_TOSERVER) ?
"toserver" :
"toclient", eof ?
"true" :
"false");
1006 if (input_len && ftpdata_state->
files == NULL) {
1007 FtpTransferCmd *data =
1014 if ((direction & data->direction) == 0) {
1016 SCLogDebug(
"input %u not for our direction (%s): %s/%s", input_len,
1017 (direction & STREAM_TOSERVER) ?
"toserver" :
"toclient",
1018 data->cmd == FTP_COMMAND_STOR ?
"STOR" :
"RETR",
1019 (data->direction & STREAM_TOSERVER) ?
"toserver" :
"toclient");
1024 if (ftpdata_state->
files == NULL) {
1029 ftpdata_state->
file_name = data->file_name;
1030 ftpdata_state->
file_len = data->file_len;
1031 data->file_name = NULL;
1034 ftpdata_state->
command = data->cmd;
1035 switch (data->cmd) {
1036 case FTP_COMMAND_STOR:
1037 ftpdata_state->
direction = data->direction;
1039 (ftpdata_state->
direction & STREAM_TOSERVER) ?
"toserver" :
"toclient");
1041 case FTP_COMMAND_RETR:
1042 ftpdata_state->
direction = data->direction;
1044 (ftpdata_state->
direction & STREAM_TOSERVER) ?
"toserver" :
"toclient");
1053 0ULL, (uint8_t *) ftpdata_state->
file_name,
1055 input, input_len,
flags) != 0) {
1060 ftpdata_state->
tx_data.files_opened = 1;
1062 if (ftpdata_state->
state == FTPDATA_STATE_FINISHED) {
1067 if ((direction & ftpdata_state->
direction) == 0) {
1071 SCLogDebug(
"input %u not for us (%s): %s/%s", input_len,
1072 (direction & STREAM_TOSERVER) ?
"toserver" :
"toclient",
1073 ftpdata_state->
command == FTP_COMMAND_STOR ?
"STOR" :
"RETR",
1074 (ftpdata_state->
direction & STREAM_TOSERVER) ?
"toserver" :
"toclient");
1077 if (input_len != 0) {
1081 SCLogDebug(
"FileAppendData() - file no longer being extracted");
1083 }
else if (ret < 0) {
1084 SCLogDebug(
"FileAppendData() failed: %d", ret);
1094 ftpdata_state->
state = FTPDATA_STATE_FINISHED;
1095 SCLogDebug(
"closed because of eof: state now FTPDATA_STATE_FINISHED");
1105 StreamSlice stream_slice,
void *local_data)
1107 return FTPDataParse(f, ftp_state, pstate, stream_slice, local_data, STREAM_TOSERVER);
1111 StreamSlice stream_slice,
void *local_data)
1113 return FTPDataParse(f, ftp_state, pstate, stream_slice, local_data, STREAM_TOCLIENT);
1118 static uint64_t ftpdata_state_memuse = 0;
1119 static uint64_t ftpdata_state_memcnt = 0;
1122 static void *FTPDataStateAlloc(
void *orig_state,
AppProto proto_orig)
1129 state->
state = FTPDATA_STATE_IN_PROGRESS;
1133 ftpdata_state_memcnt++;
1140 static void FTPDataStateFree(
void *s)
1144 SCAppLayerTxDataCleanup(&fstate->
tx_data);
1155 ftpdata_state_memcnt--;
1167 static AppLayerStateData *FTPDataGetStateData(
void *vstate)
1173 static void FTPDataStateTransactionFree(
void *state, uint64_t tx_id)
1178 static void *FTPDataGetTx(
void *state, uint64_t tx_id)
1184 static uint64_t FTPDataGetTxCnt(
void *state)
1190 static int FTPDataGetAlstateProgress(
void *tx, uint8_t direction)
1193 if (direction == ftpdata_state->
direction)
1194 return ftpdata_state->
state;
1196 return FTPDATA_STATE_FINISHED;
1199 static AppLayerGetFileState FTPDataStateGetTxFiles(
void *tx, uint8_t direction)
1202 AppLayerGetFileState files = { .fc = NULL, .cfg = &sbcfg };
1204 if (direction == ftpdata_state->
direction)
1205 files.fc = ftpdata_state->
files;
1210 static void FTPSetMpmState(
void)
1213 if (
unlikely(ftp_mpm_ctx == NULL)) {
1218 SCFTPSetMpmState(ftp_mpm_ctx);
1222 static void FTPFreeMpmState(
void)
1224 if (ftp_mpm_ctx != NULL) {
1235 static AppLayerGetTxIterTuple FTPGetTxIterator(
const uint8_t ipproto,
const AppProto alproto,
1239 AppLayerGetTxIterTuple no_tuple = { NULL, 0,
false };
1242 if (state->
un.
ptr == NULL) {
1248 while (tx_ptr->
tx_id < min_tx_id) {
1254 if (tx_ptr->
tx_id >= max_tx_id) {
1258 AppLayerGetTxIterTuple tuple = {
1261 .has_next = (state->
un.
ptr != NULL),
1271 const char *proto_name =
"ftp";
1272 const char *proto_data_name =
"ftp-data";
1277 if (FTPRegisterPatternsForProtocolDetection() < 0 )
1298 FTPLocalStorageFree);
1304 ALPROTO_FTP, FTP_STATE_FINISHED, FTP_STATE_FINISHED);
1308 FTPDataParseRequest);
1310 FTPDataParseResponse);
1335 sbcfg.
Calloc = FTPCalloc;
1337 sbcfg.
Free = FTPFree;
1341 SCLogInfo(
"Parser disabled for %s protocol. Protocol detection still on.", proto_name);
1370 if (!buffer || *buffer ==
'\0') {
1374 char *c = strchr(buffer,
'\n');
1375 return c == NULL ?
len : (uint16_t)(c - buffer + 1);
1381 SCJbOpenObject(jb,
"ftp_data");
1384 SCJbSetStringFromBytes(jb,
"filename", ftp_state->
file_name, ftp_state->
file_len);
1387 case FTP_COMMAND_STOR:
1390 case FTP_COMMAND_RETR:
1413 static int FTPParserTest01(
void)
1416 uint8_t ftpbuf[] =
"PORT 192,168,1,1,0,80\r\n";
1417 uint32_t ftplen =
sizeof(ftpbuf) - 1;
1421 memset(&f, 0,
sizeof(f));
1422 memset(&ssn, 0,
sizeof(ssn));
1425 f.
proto = IPPROTO_TCP;
1431 STREAM_TOSERVER | STREAM_EOF, ftpbuf, ftplen);
1444 static int FTPParserTest11(
void)
1447 uint8_t ftpbuf1[] =
"PORT 192,168,1,1,0,80\r\n";
1448 uint8_t ftpbuf2[] =
"RETR\r\n";
1449 uint8_t ftpbuf3[] =
"227 OK\r\n";
1454 memset(&f, 0,
sizeof(f));
1455 memset(&ssn, 0,
sizeof(ssn));
1458 f.
proto = IPPROTO_TCP;
1464 STREAM_TOSERVER | STREAM_START, ftpbuf1,
1465 sizeof(ftpbuf1) - 1);
1472 sizeof(ftpbuf3) - 1);
1476 STREAM_TOSERVER, ftpbuf2,
1477 sizeof(ftpbuf2) - 1);
1491 static int FTPParserTest12(
void)
1494 uint8_t ftpbuf1[] =
"PORT 192,168,1,1,0,80\r\n";
1495 uint8_t ftpbuf2[] =
"STOR\r\n";
1496 uint8_t ftpbuf3[] =
"227 OK\r\n";
1501 memset(&f, 0,
sizeof(f));
1502 memset(&ssn, 0,
sizeof(ssn));
1505 f.
proto = IPPROTO_TCP;
1511 STREAM_TOSERVER | STREAM_START, ftpbuf1,
1512 sizeof(ftpbuf1) - 1);
1519 sizeof(ftpbuf3) - 1);
1523 STREAM_TOSERVER, ftpbuf2,
1524 sizeof(ftpbuf2) - 1);