54 #define FTP_MPM mpm_default_matcher
56 static MpmCtx *ftp_mpm_ctx = NULL;
67 static void FTPParseMemcap(
void)
75 static void FTPIncrMemuse(uint64_t size)
80 static void FTPDecrMemuse(uint64_t size)
113 static int FTPCheckMemcap(uint64_t size)
121 static void *FTPCalloc(
size_t n,
size_t size)
123 if (FTPCheckMemcap((uint32_t)(n * size)) == 0) {
135 FTPIncrMemuse((uint64_t)(n * size));
139 static void *FTPRealloc(
void *ptr,
size_t orig_size,
size_t size)
141 if (FTPCheckMemcap((uint32_t)(size - orig_size)) == 0) {
152 if (size > orig_size) {
153 FTPIncrMemuse(size - orig_size);
155 FTPDecrMemuse(orig_size - size);
161 static void FTPFree(
void *ptr,
size_t size)
165 FTPDecrMemuse((uint64_t)size);
172 FTPIncrMemuse(response->total_size);
181 FTPDecrMemuse(wrapper->
response->total_size);
182 SCFTPFreeResponseLine(wrapper->
response);
188 static void *FTPLocalStorageAlloc(
void)
197 if (td->
pmq == NULL) {
210 static void FTPLocalStorageFree(
void *ptr)
214 if (td->
pmq != NULL) {
255 SCLogDebug(
"new transaction %p (state tx cnt %"PRIu64
")", tx, state->
tx_cnt);
270 while ((wrapper =
TAILQ_FIRST(&tx->response_list))) {
272 FTPResponseWrapperFree(wrapper);
275 FTPFree(tx,
sizeof(*tx));
293 while (input->
len > 0) {
294 const uint8_t *lf_idx = memchr(input->
buf + input->
consumed, 0x0a, input->
len);
296 if (lf_idx == NULL) {
298 *current_line_truncated =
true;
312 const uint32_t o_consumed = input->
consumed;
313 input->
consumed = (uint32_t)(lf_idx - input->
buf + 1);
314 const uint32_t line_len = (uint32_t)(input->
consumed - o_consumed);
315 input->
len -= (int32_t)line_len;
318 if (*current_line_truncated) {
322 *current_line_truncated =
false;
326 line->
buf = input->
buf + o_consumed;
327 line->
len = line_len;
363 static int FTPParseRequestCommand(
374 uint8_t command_code;
375 if (SCGetFtpCommandInfo(td->
pmq->
rule_id_array[0], NULL, &command_code, NULL)) {
386 const char *command_name = NULL;
387 (void)SCGetFtpCommandInfo(td->
pmq->
rule_id_array[0], &command_name, NULL, NULL);
388 SCLogDebug(
"matching FTP command is %s [code: %d, index %d]", command_name,
398 static void FtpTransferCmdFree(
void *data)
400 FtpTransferCmd *cmd = (FtpTransferCmd *)data;
403 if (cmd->file_name) {
404 FTPFree((
void *)cmd->file_name, cmd->file_len + 1);
406 SCFTPTransferCmdFree(cmd);
407 FTPDecrMemuse((uint64_t)
sizeof(FtpTransferCmd));
410 static uint32_t CopyCommandLine(uint8_t **dest,
FtpLineState *line)
414 while (line->
len && isspace((
unsigned char)line->
buf[line->
len - 1])) {
422 uint8_t *where = FTPCalloc(line->
len + 1,
sizeof(
char));
426 memcpy(where, line->
buf, line->
len);
427 where[line->
len] =
'\0';
430 return line->
len + 1;
452 const uint8_t *input = StreamSliceGetData(&stream_slice);
453 uint32_t input_len = StreamSliceGetDataLen(&stream_slice);
457 }
else if (input == NULL || input_len == 0) {
461 FtpInput ftpi = { .
buf = input, .len = input_len, .orig_len = input_len, .consumed = 0 };
462 FtpLineState line = { .
buf = NULL, .len = 0, .delim_len = 0, .truncated =
false };
464 uint8_t direction = STREAM_TOSERVER;
470 }
else if (res.
status == -1) {
475 if (!FTPParseRequestCommand(thread_data, &line, &cmd_descriptor)) {
476 state->
command = FTP_COMMAND_UNKNOWN;
500 (state->
command == FTP_COMMAND_STOR || state->
command == FTP_COMMAND_APPE ||
501 state->
command == FTP_COMMAND_STOU)) ||
503 (state->
command == FTP_COMMAND_RETR || state->
command == FTP_COMMAND_NLST ||
504 state->
command == FTP_COMMAND_LIST ||
505 state->
command == FTP_COMMAND_MLSD))) {
506 direction = STREAM_TOCLIENT;
509 bool has_file =
false;
512 case FTP_COMMAND_EPRT:
514 case FTP_COMMAND_PORT:
533 case FTP_COMMAND_RETR:
535 case FTP_COMMAND_STOR:
537 case FTP_COMMAND_APPE:
548 case FTP_COMMAND_STOU:
553 case FTP_COMMAND_NLST:
554 case FTP_COMMAND_LIST:
555 case FTP_COMMAND_MLSD: {
562 FtpTransferCmd *data = SCFTPTransferCmdNew();
565 FTPIncrMemuse((uint64_t)(
sizeof *data));
567 data->flow_id = FlowGetId(
f);
568 data->direction = direction;
569 data->data_free = FtpTransferCmdFree;
577 #if SC_FILENAME_MAX > UINT16_MAX
578 #error SC_FILENAME_MAX is greater than UINT16_MAX
580 data->file_name = FTPCalloc(file_name_len + 1,
sizeof(
char));
581 if (data->file_name == NULL) {
582 FtpTransferCmdFree(data);
585 data->file_name[file_name_len] = 0;
586 data->file_len = (uint16_t)file_name_len;
587 memcpy(data->file_name, line.
buf + 5, file_name_len);
588 }
else if (state->
command == FTP_COMMAND_STOU) {
589 const char default_file_name[] =
"<stou>";
590 uint32_t file_name_len =
sizeof(default_file_name);
591 data->file_name = FTPCalloc(file_name_len,
sizeof(
char));
592 if (data->file_name == NULL) {
593 FtpTransferCmdFree(data);
596 data->file_name[file_name_len - 1] = 0;
597 data->file_len = (uint16_t)file_name_len - 1;
598 memcpy(data->file_name, default_file_name, file_name_len);
603 FtpTransferCmdFree(data);
607 SCLogDebug(
"Expectation created [direction: %s, dynamic port %" PRIu16
"].",
627 static int FTPParsePassiveResponse(
FtpState *state,
const uint8_t *input, uint32_t input_len)
629 uint16_t dyn_port = SCFTPParsePortPasv(input, input_len);
633 SCLogDebug(
"FTP passive mode (v4): dynamic port %"PRIu16
"", dyn_port);
642 static int FTPParsePassiveResponseV6(
FtpState *state,
const uint8_t *input, uint32_t input_len)
644 uint16_t dyn_port = SCFTPParsePortEpsv(input, input_len);
648 SCLogDebug(
"FTP passive mode (v6): dynamic port %"PRIu16
"", dyn_port);
665 static inline bool FTPIsPPR(
const uint8_t *input, uint32_t input_len)
667 return input_len >= 4 && isdigit(input[0]) && input[0] ==
'1' &&
668 isdigit(input[1]) && isdigit(input[2]) && isspace(input[3]);
685 const uint8_t *input = StreamSliceGetData(&stream_slice);
686 uint32_t input_len = StreamSliceGetDataLen(&stream_slice);
691 FtpInput ftpi = { .
buf = input, .len = input_len, .orig_len = input_len, .consumed = 0 };
692 FtpLineState line = { .
buf = NULL, .len = 0, .delim_len = 0, .truncated =
false };
700 }
else if (res.
status == -1) {
705 tx = FTPTransactionCreate(state);
716 if (state->
command == FTP_COMMAND_UNKNOWN) {
725 case FTP_COMMAND_AUTH_TLS:
731 case FTP_COMMAND_EPRT:
740 SCLogDebug(
"FTP active mode (v6): dynamic port %" PRIu16
"", dyn_port);
743 case FTP_COMMAND_PORT:
752 SCLogDebug(
"FTP active mode (v4): dynamic port %" PRIu16
"", dyn_port);
755 case FTP_COMMAND_PASV:
757 FTPParsePassiveResponse(ftp_state, line.
buf, line.
len);
761 case FTP_COMMAND_EPSV:
763 FTPParsePassiveResponseV6(ftp_state, line.
buf, line.
len);
771 FTPResponseLine *response = SCFTPParseResponseLine((
const char *)line.
buf, line.
len);
776 if (response->truncated) {
782 SCFTPFreeResponseLine(response);
785 SCLogDebug(
"unable to parse FTP response line \"%s\"", line.
buf);
790 if (FTPIsPPR(line.
buf, line.
len)) {
804 static uint64_t ftp_state_memuse = 0;
805 static uint64_t ftp_state_memcnt = 0;
808 static void *FTPStateAlloc(
void *orig_state,
AppProto proto_orig)
810 void *s = FTPCalloc(1,
sizeof(
FtpState));
826 static void FTPStateFree(
void *s)
837 const char *command_name = NULL;
838 (void)SCGetFtpCommandInfo(
840 SCLogDebug(
"[%s] state %p id %" PRIu64
", Freeing %d bytes at %p",
846 FTPTransactionFree(tx);
869 SCLogDebug(
"NULL state object; no transactions available");
886 SCLogDebug(
"Returning OLDEST tx %p id %"PRIu64, lasttx, lasttx->
tx_id);
890 static void *FTPGetTx(
void *state, uint64_t tx_id)
896 if (ftp_state->
curr_tx == NULL)
902 if (tx->
tx_id == tx_id)
921 static void FTPStateTransactionFree(
void *state, uint64_t tx_id)
926 if (tx_id < tx->tx_id)
928 else if (tx_id > tx->
tx_id)
934 FTPTransactionFree(tx);
939 static uint64_t FTPGetTxCnt(
void *state)
950 static int FTPGetAlstateProgress(
void *vtx, uint8_t direction)
956 if (direction == STREAM_TOSERVER) {
957 return FTP_STATE_FINISHED;
960 return FTP_STATE_IN_PROGRESS;
963 return FTP_STATE_FINISHED;
966 static AppProto FTPUserProbingParser(
967 const Flow *
f, uint8_t direction,
const uint8_t *input, uint32_t
len, uint8_t *rdir)
980 static AppProto FTPQuitProbingParser(
981 const Flow *
f, uint8_t direction,
const uint8_t *input, uint32_t
len, uint8_t *rdir)
988 if (
SCMemcmp(input,
"QUIT", 4) != 0) {
994 static AppProto FTPServerProbingParser(
995 const Flow *
f, uint8_t direction,
const uint8_t *input, uint32_t
len, uint8_t *rdir)
1002 if (input[0] !=
'2' || input[1] !=
'2' || input[2] !=
'0') {
1006 if (input[3] !=
' ' && input[3] !=
'-') {
1012 if (memchr(input + 4,
'\n',
len - 4) != NULL) {
1019 static int FTPRegisterPatternsForProtocolDetection(
void)
1022 IPPROTO_TCP,
ALPROTO_FTP,
"220 (", 5, 0, STREAM_TOCLIENT) < 0) {
1026 IPPROTO_TCP,
ALPROTO_FTP,
"FEAT", 4, 0, STREAM_TOSERVER) < 0) {
1030 STREAM_TOSERVER, FTPUserProbingParser, 5, 5) < 0) {
1035 IPPROTO_TCP,
ALPROTO_FTP,
"PORT ", 5, 0, STREAM_TOSERVER) < 0) {
1041 "tcp", IPPROTO_TCP,
"ftp",
ALPROTO_FTP, 0, 5, NULL, FTPServerProbingParser)) {
1045 FTPQuitProbingParser, FTPServerProbingParser);
1063 const uint8_t *input = StreamSliceGetData(&stream_slice);
1064 uint32_t input_len = StreamSliceGetDataLen(&stream_slice);
1065 const bool eof = (direction & STREAM_TOSERVER)
1071 ftpdata_state->
tx_data.
file_tx = direction & (STREAM_TOSERVER | STREAM_TOCLIENT);
1072 if (direction & STREAM_TOSERVER) {
1081 SCLogDebug(
"FTP-DATA input_len %u flags %04x dir %d/%s EOF %s", input_len,
flags, direction,
1082 (direction & STREAM_TOSERVER) ?
"toserver" :
"toclient", eof ?
"true" :
"false");
1086 FtpTransferCmd *data =
1093 if ((direction & data->direction) == 0) {
1095 SCLogDebug(
"input %u not for our direction (%s): %s/%s", input_len,
1096 (direction & STREAM_TOSERVER) ?
"toserver" :
"toclient",
1097 data->cmd == FTP_COMMAND_STOR ?
"STOR" :
"RETR",
1098 (data->direction & STREAM_TOSERVER) ?
"toserver" :
"toclient");
1102 if (data->file_name) {
1104 if (ftpdata_state->
files == NULL) {
1109 ftpdata_state->
file_name = data->file_name;
1110 ftpdata_state->
file_len = data->file_len;
1111 data->file_name = NULL;
1115 ftpdata_state->
command = data->cmd;
1116 switch (data->cmd) {
1117 case FTP_COMMAND_STOR:
1118 ftpdata_state->
direction = data->direction;
1120 (ftpdata_state->
direction & STREAM_TOSERVER) ?
"toserver" :
"toclient");
1122 case FTP_COMMAND_APPE:
1123 ftpdata_state->
direction = data->direction;
1125 (ftpdata_state->
direction & STREAM_TOSERVER) ?
"toserver" :
"toclient");
1127 case FTP_COMMAND_STOU:
1128 ftpdata_state->
direction = data->direction;
1130 (ftpdata_state->
direction & STREAM_TOSERVER) ?
"toserver" :
"toclient");
1132 case FTP_COMMAND_RETR:
1133 ftpdata_state->
direction = data->direction;
1135 (ftpdata_state->
direction & STREAM_TOSERVER) ?
"toserver" :
"toclient");
1137 case FTP_COMMAND_NLST:
1138 ftpdata_state->
direction = data->direction;
1140 (ftpdata_state->
direction & STREAM_TOSERVER) ?
"toserver" :
"toclient");
1142 case FTP_COMMAND_LIST:
1143 ftpdata_state->
direction = data->direction;
1145 (ftpdata_state->
direction & STREAM_TOSERVER) ?
"toserver" :
"toclient");
1147 case FTP_COMMAND_MLSD:
1148 ftpdata_state->
direction = data->direction;
1150 (ftpdata_state->
direction & STREAM_TOSERVER) ?
"toserver" :
"toclient");
1161 input_len,
flags) != 0) {
1171 if ((direction & ftpdata_state->
direction) == 0) {
1175 SCLogDebug(
"input %u not for us (%s): %s/%s", input_len,
1176 (direction & STREAM_TOSERVER) ?
"toserver" :
"toclient",
1177 ftpdata_state->
command == FTP_COMMAND_STOR ?
"STOR" :
"RETR",
1178 (ftpdata_state->
direction & STREAM_TOSERVER) ?
"toserver" :
"toclient");
1181 if (ftpdata_state->
state == FTPDATA_STATE_FINISHED) {
1186 if (ftpdata_state->
file_name && input_len != 0) {
1190 SCLogDebug(
"FileAppendData() - file no longer being extracted");
1192 }
else if (ret < 0) {
1193 SCLogDebug(
"FileAppendData() failed: %d", ret);
1205 ftpdata_state->
state = FTPDATA_STATE_FINISHED;
1206 SCLogDebug(
"closed because of eof: state now FTPDATA_STATE_FINISHED");
1218 return FTPDataParse(
f, ftp_state, pstate, stream_slice, local_data, STREAM_TOSERVER);
1224 return FTPDataParse(
f, ftp_state, pstate, stream_slice, local_data, STREAM_TOCLIENT);
1229 static uint64_t ftpdata_state_memuse = 0;
1230 static uint64_t ftpdata_state_memcnt = 0;
1233 static void *FTPDataStateAlloc(
void *orig_state,
AppProto proto_orig)
1240 state->
state = FTPDATA_STATE_IN_PROGRESS;
1244 ftpdata_state_memcnt++;
1251 static void FTPDataStateFree(
void *s)
1266 ftpdata_state_memcnt--;
1284 static void FTPDataStateTransactionFree(
void *state, uint64_t tx_id)
1289 static void *FTPDataGetTx(
void *state, uint64_t tx_id)
1295 static uint64_t FTPDataGetTxCnt(
void *state)
1301 static int FTPDataGetAlstateProgress(
void *tx, uint8_t direction)
1304 if (direction == ftpdata_state->
direction)
1305 return ftpdata_state->
state;
1307 return FTPDATA_STATE_FINISHED;
1315 if (direction == ftpdata_state->
direction)
1316 files.
fc = ftpdata_state->
files;
1321 static void FTPSetMpmState(
void)
1324 if (
unlikely(ftp_mpm_ctx == NULL)) {
1329 SCFTPSetMpmState(ftp_mpm_ctx);
1333 static void FTPFreeMpmState(
void)
1335 if (ftp_mpm_ctx != NULL) {
1353 if (state->
un.
ptr == NULL) {
1359 while (tx_ptr->
tx_id < min_tx_id) {
1365 if (tx_ptr->
tx_id >= max_tx_id) {
1372 .has_next = (state->
un.
ptr != NULL),
1382 const char *proto_name =
"ftp";
1383 const char *proto_data_name =
"ftp-data";
1388 if (FTPRegisterPatternsForProtocolDetection() < 0 )
1400 IPPROTO_TCP,
ALPROTO_FTP, STREAM_TOSERVER | STREAM_TOCLIENT);
1410 FTPLocalStorageFree);
1416 ALPROTO_FTP, FTP_STATE_FINISHED, FTP_STATE_FINISHED);
1420 FTPDataParseRequest);
1422 FTPDataParseResponse);
1448 sbcfg.
Calloc = FTPCalloc;
1450 sbcfg.
Free = FTPFree;
1454 SCLogInfo(
"Parser disabled for %s protocol. Protocol detection still on.", proto_name);
1483 if (!buffer || *buffer ==
'\0') {
1487 const char *c = strchr(buffer,
'\n');
1488 return c == NULL ?
len : (uint16_t)(c - buffer + 1);
1494 SCJbOpenObject(jb,
"ftp_data");
1497 SCJbSetStringFromBytes(jb,
"filename", ftp_state->
file_name, ftp_state->
file_len);
1500 case FTP_COMMAND_STOR:
1503 case FTP_COMMAND_APPE:
1506 case FTP_COMMAND_STOU:
1509 case FTP_COMMAND_RETR:
1512 case FTP_COMMAND_NLST:
1515 case FTP_COMMAND_LIST:
1518 case FTP_COMMAND_MLSD:
1542 static int FTPParserTest01(
void)
1545 uint8_t ftpbuf[] =
"PORT 192,168,1,1,0,80\r\n";
1546 uint32_t ftplen =
sizeof(ftpbuf) - 1;
1550 memset(&
f, 0,
sizeof(
f));
1551 memset(&
ssn, 0,
sizeof(
ssn));
1560 STREAM_TOSERVER | STREAM_EOF, ftpbuf, ftplen);
1574 static int FTPParserTest11(
void)
1577 uint8_t ftpbuf1[] =
"PORT 192,168,1,1,0,80\r\n";
1578 uint8_t ftpbuf2[] =
"RETR\r\n";
1579 uint8_t ftpbuf3[] =
"227 OK\r\n";
1584 memset(&
f, 0,
sizeof(
f));
1585 memset(&
ssn, 0,
sizeof(
ssn));
1594 STREAM_TOSERVER | STREAM_START, ftpbuf1,
1595 sizeof(ftpbuf1) - 1);
1602 sizeof(ftpbuf3) - 1);
1606 STREAM_TOSERVER, ftpbuf2,
1607 sizeof(ftpbuf2) - 1);
1622 static int FTPParserTest12(
void)
1625 uint8_t ftpbuf1[] =
"PORT 192,168,1,1,0,80\r\n";
1626 uint8_t ftpbuf2[] =
"STOR\r\n";
1627 uint8_t ftpbuf3[] =
"227 OK\r\n";
1632 memset(&
f, 0,
sizeof(
f));
1633 memset(&
ssn, 0,
sizeof(
ssn));
1642 STREAM_TOSERVER | STREAM_START, ftpbuf1,
1643 sizeof(ftpbuf1) - 1);
1650 sizeof(ftpbuf3) - 1);
1654 STREAM_TOSERVER, ftpbuf2,
1655 sizeof(ftpbuf2) - 1);
1671 static int FTPParserTest13(
void)
1674 uint8_t ftpbuf[] =
"USER anonymous \r\n";
1675 const char expected[] =
"USER anonymous";
1680 memset(&
f, 0,
sizeof(
f));
1681 memset(&
ssn, 0,
sizeof(
ssn));
1692 ftpbuf,
sizeof(ftpbuf) - 1);
1718 static int FTPParserTest14(
void)
1721 uint8_t ftpbuf1[] =
"PORT 192,168,1,1,0,80 \r\n";
1722 uint8_t ftpbuf2[] =
"227 OK\r\n";
1723 const char expected[] =
"PORT 192,168,1,1,0,80";
1728 memset(&
f, 0,
sizeof(
f));
1729 memset(&
ssn, 0,
sizeof(
ssn));
1738 ftpbuf1,
sizeof(ftpbuf1) - 1);
1747 FAIL_IF(memcmp(ftp_state->
port_line, expected,
sizeof(expected) - 1) != 0);