suricata
app-layer-ftp.c
Go to the documentation of this file.
1 /* Copyright (C) 2007-2017 Open Information Security Foundation
2  *
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
5  * Software Foundation.
6  *
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.
11  *
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
15  * 02110-1301, USA.
16  */
17 
18 /**
19  * \file
20  *
21  * \author Pablo Rincon Crespo <pablo.rincon.crespo@gmail.com>
22  * \author Eric Leblond <eric@regit.org>
23  * \author Jeff Lucovsky <jeff@lucovsky.org>
24  *
25  * App Layer Parser for FTP
26  */
27 
28 #include "suricata-common.h"
29 #include "debug.h"
30 #include "decode.h"
31 #include "threads.h"
32 
33 #include "util-print.h"
34 #include "util-pool.h"
35 
36 #include "flow-util.h"
37 #include "flow-storage.h"
38 
39 #include "detect-engine-state.h"
40 
41 #include "stream-tcp-private.h"
42 #include "stream-tcp-reassemble.h"
43 #include "stream-tcp.h"
44 #include "stream.h"
45 
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"
51 
52 #include "util-spm.h"
53 #include "util-unittest.h"
54 #include "util-debug.h"
55 #include "util-memcmp.h"
56 #include "util-memrchr.h"
57 #include "util-byte.h"
58 #include "util-mem.h"
59 #include "util-misc.h"
60 
61 #ifdef HAVE_RUST
62 #include "rust-ftp-mod-gen.h"
63 #endif
64 
65 #include "output-json.h"
66 
68  /* Parsed and handled */
69  { FTP_COMMAND_PORT, "PORT", "port", 4},
70  { FTP_COMMAND_EPRT, "EPRT", "eprt", 4},
71  { FTP_COMMAND_AUTH_TLS, "AUTH TLS", "auth tls", 8},
72  { FTP_COMMAND_PASV, "PASV", "pasv", 4},
73  { FTP_COMMAND_RETR, "RETR", "retr", 4},
74  { FTP_COMMAND_EPSV, "EPSV", "epsv", 4},
75  { FTP_COMMAND_STOR, "STOR", "stor", 4},
76 
77  /* Parsed, but not handled */
78  { FTP_COMMAND_ABOR, "ABOR", "abor", 4},
79  { FTP_COMMAND_ACCT, "ACCT", "acct", 4},
80  { FTP_COMMAND_ALLO, "ALLO", "allo", 4},
81  { FTP_COMMAND_APPE, "APPE", "appe", 4},
82  { FTP_COMMAND_CDUP, "CDUP", "cdup", 4},
83  { FTP_COMMAND_CHMOD, "CHMOD", "chmod", 5},
84  { FTP_COMMAND_CWD, "CWD", "cwd", 3},
85  { FTP_COMMAND_DELE, "DELE", "dele", 4},
86  { FTP_COMMAND_HELP, "HELP", "help", 4},
87  { FTP_COMMAND_IDLE, "IDLE", "idle", 4},
88  { FTP_COMMAND_LIST, "LIST", "list", 4},
89  { FTP_COMMAND_MAIL, "MAIL", "mail", 4},
90  { FTP_COMMAND_MDTM, "MDTM", "mdtm", 4},
91  { FTP_COMMAND_MKD, "MKD", "mkd", 3},
92  { FTP_COMMAND_MLFL, "MLFL", "mlfl", 4},
93  { FTP_COMMAND_MODE, "MODE", "mode", 4},
94  { FTP_COMMAND_MRCP, "MRCP", "mrcp", 4},
95  { FTP_COMMAND_MRSQ, "MRSQ", "mrsq", 4},
96  { FTP_COMMAND_MSAM, "MSAM", "msam", 4},
97  { FTP_COMMAND_MSND, "MSND", "msnd", 4},
98  { FTP_COMMAND_MSOM, "MSOM", "msom", 4},
99  { FTP_COMMAND_NLST, "NLST", "nlst", 4},
100  { FTP_COMMAND_NOOP, "NOOP", "noop", 4},
101  { FTP_COMMAND_PASS, "PASS", "pass", 4},
102  { FTP_COMMAND_PWD, "PWD", "pwd", 3},
103  { FTP_COMMAND_QUIT, "QUIT", "quit", 4},
104  { FTP_COMMAND_REIN, "REIN", "rein", 4},
105  { FTP_COMMAND_REST, "REST", "rest", 4},
106  { FTP_COMMAND_RMD, "RMD", "rmd", 3},
107  { FTP_COMMAND_RNFR, "RNFR", "rnfr", 4},
108  { FTP_COMMAND_RNTO, "RNTO", "rnto", 4},
109  { FTP_COMMAND_SITE, "SITE", "site", 4},
110  { FTP_COMMAND_SIZE, "SIZE", "size", 4},
111  { FTP_COMMAND_SMNT, "SMNT", "smnt", 4},
112  { FTP_COMMAND_STAT, "STAT", "stat", 4},
113  { FTP_COMMAND_STOU, "STOU", "stou", 4},
114  { FTP_COMMAND_STRU, "STRU", "stru", 4},
115  { FTP_COMMAND_SYST, "SYST", "syst", 4},
116  { FTP_COMMAND_TYPE, "TYPE", "type", 4},
117  { FTP_COMMAND_UMASK, "UMASK", "umask", 5},
118  { FTP_COMMAND_USER, "USER", "user", 4},
119  { FTP_COMMAND_UNKNOWN, NULL, NULL, 0}
120 };
121 uint64_t ftp_config_memcap = 0;
122 
123 SC_ATOMIC_DECLARE(uint64_t, ftp_memuse);
124 SC_ATOMIC_DECLARE(uint64_t, ftp_memcap);
125 
126 static FTPTransaction *FTPGetOldestTx(FtpState *);
127 
128 static void FTPParseMemcap(void)
129 {
130  const char *conf_val;
131 
132  /** set config values for memcap, prealloc and hash_size */
133  if ((ConfGet("app-layer.protocols.ftp.memcap", &conf_val)) == 1)
134  {
135  if (ParseSizeStringU64(conf_val, &ftp_config_memcap) < 0) {
136  SCLogError(SC_ERR_SIZE_PARSE, "Error parsing ftp.memcap "
137  "from conf file - %s. Killing engine",
138  conf_val);
139  exit(EXIT_FAILURE);
140  }
141  SCLogInfo("FTP memcap: %"PRIu64, ftp_config_memcap);
142  } else {
143  /* default to unlimited */
144  ftp_config_memcap = 0;
145  }
146 
147  SC_ATOMIC_INIT(ftp_memuse);
148  SC_ATOMIC_INIT(ftp_memcap);
149 }
150 
151 static void FTPIncrMemuse(uint64_t size)
152 {
153  (void) SC_ATOMIC_ADD(ftp_memuse, size);
154  return;
155 }
156 
157 static void FTPDecrMemuse(uint64_t size)
158 {
159  (void) SC_ATOMIC_SUB(ftp_memuse, size);
160  return;
161 }
162 
164 {
165  uint64_t tmpval = SC_ATOMIC_GET(ftp_memuse);
166  return tmpval;
167 }
168 
170 {
171  uint64_t tmpval = SC_ATOMIC_GET(ftp_memcap);
172  return tmpval;
173 }
174 
175 /**
176  * \brief Check if alloc'ing "size" would mean we're over memcap
177  *
178  * \retval 1 if in bounds
179  * \retval 0 if not in bounds
180  */
181 static int FTPCheckMemcap(uint64_t size)
182 {
183  if (ftp_config_memcap == 0 || size + SC_ATOMIC_GET(ftp_memuse) <= ftp_config_memcap)
184  return 1;
185  (void) SC_ATOMIC_ADD(ftp_memcap, 1);
186  return 0;
187 }
188 
189 static void *FTPMalloc(size_t size)
190 {
191  void *ptr = NULL;
192 
193  if (FTPCheckMemcap((uint32_t)size) == 0)
194  return NULL;
195 
196  ptr = SCMalloc(size);
197 
198  if (unlikely(ptr == NULL))
199  return NULL;
200 
201  FTPIncrMemuse((uint64_t)size);
202 
203  return ptr;
204 }
205 
206 static void *FTPCalloc(size_t n, size_t size)
207 {
208  void *ptr = NULL;
209 
210  if (FTPCheckMemcap((uint32_t)(n * size)) == 0)
211  return NULL;
212 
213  ptr = SCCalloc(n, size);
214 
215  if (unlikely(ptr == NULL))
216  return NULL;
217 
218  FTPIncrMemuse((uint64_t)(n * size));
219 
220  return ptr;
221 }
222 
223 static void *FTPRealloc(void *ptr, size_t orig_size, size_t size)
224 {
225  void *rptr = NULL;
226 
227  if (FTPCheckMemcap((uint32_t)(size - orig_size)) == 0)
228  return NULL;
229 
230  rptr = SCRealloc(ptr, size);
231  if (rptr == NULL)
232  return NULL;
233 
234  if (size > orig_size) {
235  FTPIncrMemuse(size - orig_size);
236  } else {
237  FTPDecrMemuse(orig_size - size);
238  }
239 
240  return rptr;
241 }
242 
243 static void FTPFree(void *ptr, size_t size)
244 {
245  SCFree(ptr);
246 
247  FTPDecrMemuse((uint64_t)size);
248 }
249 
250 static FTPString *FTPStringAlloc(void)
251 {
252  return FTPCalloc(1, sizeof(FTPString));
253 }
254 
255 static void FTPStringFree(FTPString *str)
256 {
257  if (str->str) {
258  FTPFree(str->str, str->len);
259  }
260 
261  FTPFree(str, sizeof(FTPString));
262 }
263 
264 static FTPTransaction *FTPTransactionCreate(FtpState *state)
265 {
266  SCEnter();
267  FTPTransaction *tx = FTPCalloc(1, sizeof(*tx));
268  if (tx == NULL) {
269  return NULL;
270  }
271 
272  TAILQ_INSERT_TAIL(&state->tx_list, tx, next);
273  tx->tx_id = state->tx_cnt++;
274 
275  TAILQ_INIT(&tx->response_list);
276 
277  SCLogDebug("new transaction %p (state tx cnt %"PRIu64")", tx, state->tx_cnt);
278  return tx;
279 }
280 
281 static void FTPTransactionFree(FTPTransaction *tx)
282 {
283  SCEnter();
284 
285  if (tx->de_state != NULL) {
287  }
288 
289  if (tx->request) {
290  FTPFree(tx->request, tx->request_length);
291  }
292 
293  FTPString *str = NULL;
294  while ((str = TAILQ_FIRST(&tx->response_list))) {
295  TAILQ_REMOVE(&tx->response_list, str, next);
296  FTPStringFree(str);
297  }
298 
299  SCFree(tx);
300 }
301 
302 static int FTPGetLineForDirection(FtpState *state, FtpLineState *line_state)
303 {
304  void *ptmp;
305  if (line_state->current_line_lf_seen == 1) {
306  /* we have seen the lf for the previous line. Clear the parser
307  * details to parse new line */
308  line_state->current_line_lf_seen = 0;
309  if (line_state->current_line_db == 1) {
310  line_state->current_line_db = 0;
311  FTPFree(line_state->db, line_state->db_len);
312  line_state->db = NULL;
313  line_state->db_len = 0;
314  state->current_line = NULL;
315  state->current_line_len = 0;
316  }
317  }
318 
319  uint8_t *lf_idx = memchr(state->input, 0x0a, state->input_len);
320 
321  if (lf_idx == NULL) {
322  /* fragmented lines. Decoder event for special cases. Not all
323  * fragmented lines should be treated as a possible evasion
324  * attempt. With multi payload ftp chunks we can have valid
325  * cases of fragmentation. But within the same segment chunk
326  * if we see fragmentation then it's definitely something you
327  * should alert about */
328  if (line_state->current_line_db == 0) {
329  line_state->db = FTPMalloc(state->input_len);
330  if (line_state->db == NULL) {
331  return -1;
332  }
333  line_state->current_line_db = 1;
334  memcpy(line_state->db, state->input, state->input_len);
335  line_state->db_len = state->input_len;
336  } else {
337  ptmp = FTPRealloc(line_state->db, line_state->db_len,
338  (line_state->db_len + state->input_len));
339  if (ptmp == NULL) {
340  FTPFree(line_state->db, line_state->db_len);
341  line_state->db = NULL;
342  line_state->db_len = 0;
343  return -1;
344  }
345  line_state->db = ptmp;
346 
347  memcpy(line_state->db + line_state->db_len,
348  state->input, state->input_len);
349  line_state->db_len += state->input_len;
350  }
351  state->input += state->input_len;
352  state->input_len = 0;
353 
354  return -1;
355 
356  } else {
357  line_state->current_line_lf_seen = 1;
358 
359  if (line_state->current_line_db == 1) {
360  ptmp = FTPRealloc(line_state->db, line_state->db_len,
361  (line_state->db_len + (lf_idx + 1 - state->input)));
362  if (ptmp == NULL) {
363  FTPFree(line_state->db, line_state->db_len);
364  line_state->db = NULL;
365  line_state->db_len = 0;
366  return -1;
367  }
368  line_state->db = ptmp;
369 
370  memcpy(line_state->db + line_state->db_len,
371  state->input, (lf_idx + 1 - state->input));
372  line_state->db_len += (lf_idx + 1 - state->input);
373 
374  if (line_state->db_len > 1 &&
375  line_state->db[line_state->db_len - 2] == 0x0D) {
376  line_state->db_len -= 2;
377  state->current_line_delimiter_len = 2;
378  } else {
379  line_state->db_len -= 1;
380  state->current_line_delimiter_len = 1;
381  }
382 
383  state->current_line = line_state->db;
384  state->current_line_len = line_state->db_len;
385 
386  } else {
387  state->current_line = state->input;
388  state->current_line_len = lf_idx - state->input;
389 
390  if (state->input != lf_idx &&
391  *(lf_idx - 1) == 0x0D) {
392  state->current_line_len--;
393  state->current_line_delimiter_len = 2;
394  } else {
395  state->current_line_delimiter_len = 1;
396  }
397  }
398 
399  state->input_len -= (lf_idx - state->input) + 1;
400  state->input = (lf_idx + 1);
401 
402  return 0;
403  }
404 
405 }
406 
407 static int FTPGetLine(FtpState *state)
408 {
409  SCEnter();
410 
411  /* we have run out of input */
412  if (state->input_len <= 0)
413  return -1;
414 
415  /* toserver */
416  if (state->direction == 0)
417  return FTPGetLineForDirection(state, &state->line_state[0]);
418  else
419  return FTPGetLineForDirection(state, &state->line_state[1]);
420 }
421 
422 /**
423  * \brief This function is called to determine and set which command is being
424  * transferred to the ftp server
425  * \param input input line of the command
426  * \param len of the command
427  * \param cmd_descriptor when the command has been parsed
428  *
429  * \retval 1 when the command is parsed, 0 otherwise
430  */
431 static int FTPParseRequestCommand(uint8_t *input, uint32_t input_len, const FtpCommand **cmd_descriptor)
432 {
433  SCEnter();
434 
435  *cmd_descriptor = NULL;
436 
437  for (int i = 0; i < FTP_COMMAND_MAX - 1; i++) {
438  if (!FtpCommands[i].command_length) {
439  break;
440  }
441  if (input_len >= FtpCommands[i].command_length &&
442  SCMemcmpLowercase(FtpCommands[i].command_name_lower,
443  input, FtpCommands[i].command_length) == 0) {
444 
445  *cmd_descriptor = &FtpCommands[i];
446  return 1;
447  }
448  }
449  return 0;
450 }
451 
453  /** Need to look like a ExpectationData so DFree must
454  * be first field . */
455  void (*DFree)(void *);
456  uint64_t flow_id;
457  uint8_t *file_name;
458  uint16_t file_len;
460 };
461 
462 static void FtpTransferCmdFree(void *data)
463 {
464  struct FtpTransferCmd *cmd = (struct FtpTransferCmd *) data;
465  if (cmd == NULL)
466  return;
467  if (cmd->file_name) {
468  FTPFree(cmd->file_name, cmd->file_len);
469  }
470  FTPFree(cmd, sizeof(struct FtpTransferCmd));
471 }
472 
473 static uint32_t CopyCommandLine(uint8_t **dest, uint8_t *src, uint32_t length)
474 {
475  if (likely(length)) {
476  uint8_t *where = FTPCalloc(length + 1, sizeof(char));
477  if (unlikely(where == NULL)) {
478  return 0;
479  }
480  memcpy(where, src, length);
481 
482  /* Remove trailing newlines/carriage returns */
483  while (length && isspace((unsigned char) where[length - 1])) {
484  length--;
485  }
486 
487  where[length] = '\0';
488  *dest = where;
489  }
490  /* either 0 or actual */
491  return length;
492 }
493 
494 static uint16_t ftp_validate_port(int computed_port_value)
495 {
496  unsigned int port_val = computed_port_value;
497 
498  if (port_val && port_val > UINT16_MAX)
499  return 0;
500 
501  return ((uint16_t) (port_val));
502 }
503 
504 /**
505  * \brief This function extracts a port number from the command input line for IPv6 FTP usage
506  * \param input input line of the command
507  * \param input_len length of the request
508  *
509  * \retval 0 if a port number could not be extracted; otherwise, the dynamic port number
510  */
511 static uint16_t FTPGetV6PortNumber(uint8_t *input, uint32_t input_len)
512 {
513  uint16_t res;
514 
515  uint8_t *ptr = memrchr(input, '|', input_len);
516  if (ptr == NULL) {
517  return 0;
518  }
519 
520  int n_length = ptr - input - 1;
521  if (n_length < 4)
522  return 0;
523 
524  ptr = memrchr(input, '|', n_length);
525  if (ptr == NULL)
526  return 0;
527 
528  if (ByteExtractStringUint16(&res, 10, 0, (const char *)ptr + 1) < 0) {
529  return 0;
530  }
531  return res;
532 }
533 
534 /**
535  * \brief This function extracts a port number from the command input line for IPv4 FTP usage
536  * \param input input line of the command
537  * \param input_len length of the request
538  *
539  * \retval 0 if a port number could not be extracted; otherwise, the dynamic port number
540  */
541 static uint16_t FTPGetV4PortNumber(uint8_t *input, uint32_t input_len)
542 {
543  uint16_t part1, part2;
544  uint8_t *ptr = memrchr(input, ',', input_len);
545  if (ptr == NULL)
546  return 0;
547 
548  part2 = atoi((char *)ptr + 1);
549  ptr = memrchr(input, ',', (ptr - input) - 1);
550  if (ptr == NULL)
551  return 0;
552  part1 = atoi((char *)ptr + 1);
553 
554  return ftp_validate_port(256 * part1 + part2);
555 }
556 
557 
558 /**
559  * \brief This function is called to retrieve a ftp request
560  * \param ftp_state the ftp state structure for the parser
561  * \param input input line of the command
562  * \param input_len length of the request
563  * \param output the resulting output
564  *
565  * \retval 1 when the command is parsed, 0 otherwise
566  */
567 static int FTPParseRequest(Flow *f, void *ftp_state,
568  AppLayerParserState *pstate,
569  uint8_t *input, uint32_t input_len,
570  void *local_data, const uint8_t flags)
571 {
572  SCEnter();
573  /* PrintRawDataFp(stdout, input,input_len); */
574 
575  FtpState *state = (FtpState *)ftp_state;
576  void *ptmp;
577 
578  if (input == NULL && AppLayerParserStateIssetFlag(pstate, APP_LAYER_PARSER_EOF)) {
579  SCReturnInt(1);
580  } else if (input == NULL || input_len == 0) {
581  SCReturnInt(-1);
582  }
583 
584  state->input = input;
585  state->input_len = input_len;
586  /* toserver stream */
587  state->direction = 0;
588 
589  int direction = STREAM_TOSERVER;
590  while (FTPGetLine(state) >= 0) {
591  const FtpCommand *cmd_descriptor;
592 
593  if (!FTPParseRequestCommand(state->current_line, state->current_line_len, &cmd_descriptor)) {
594  state->command = FTP_COMMAND_UNKNOWN;
595  continue;
596  }
597 
598  state->command = cmd_descriptor->command;
599 
600  FTPTransaction *tx = FTPTransactionCreate(state);
601  if (unlikely(tx == NULL))
602  return -1;
603  state->curr_tx = tx;
604 
605  tx->command_descriptor = cmd_descriptor;
606  tx->request_length = CopyCommandLine(&tx->request, state->current_line, state->current_line_len);
607 
608  switch (state->command) {
609  case FTP_COMMAND_EPRT:
610  // fallthrough
611  case FTP_COMMAND_PORT:
612  if (state->current_line_len + 1 > state->port_line_size) {
613  /* Allocate an extra byte for a NULL terminator */
614  ptmp = FTPRealloc(state->port_line, state->port_line_size,
615  state->current_line_len + 1);
616  if (ptmp == NULL) {
617  if (state->port_line) {
618  FTPFree(state->port_line, state->port_line_size);
619  state->port_line = NULL;
620  state->port_line_size = 0;
621  }
622  return 0;
623  }
624  state->port_line = ptmp;
625  state->port_line_size = state->current_line_len + 1;
626  }
627  memcpy(state->port_line, state->current_line,
628  state->current_line_len);
629  state->port_line[state->current_line_len] = '\0';
630  state->port_line_len = state->current_line_len;
631  break;
632  case FTP_COMMAND_RETR:
633  /* change direction (default to server) so expectation will handle
634  * the correct message when expectation will match.
635  */
636  direction = STREAM_TOCLIENT;
637  // fallthrough
638  case FTP_COMMAND_STOR:
639  {
640  /* No dyn port negotiated so get out */
641  if (state->dyn_port == 0) {
642  SCReturnInt(-1);
643  }
644  struct FtpTransferCmd *data = FTPCalloc(1, sizeof(struct FtpTransferCmd));
645  if (data == NULL)
646  SCReturnInt(-1);
647  data->DFree = FtpTransferCmdFree;
648  /* Min size has been checked in FTPParseRequestCommand */
649  data->file_name = FTPCalloc(state->current_line_len - 4, sizeof(char));
650  if (data->file_name == NULL) {
651  FtpTransferCmdFree(data);
652  SCReturnInt(-1);
653  }
654  data->file_name[state->current_line_len - 5] = 0;
655  data->file_len = state->current_line_len - 5;
656  memcpy(data->file_name, state->current_line + 5, state->current_line_len - 5);
657  data->cmd = state->command;
658  data->flow_id = FlowGetId(f);
659  int ret = AppLayerExpectationCreate(f,
660  state->active ? STREAM_TOSERVER : direction,
661  0, state->dyn_port, ALPROTO_FTPDATA, data);
662  if (ret == -1) {
663  FtpTransferCmdFree(data);
664  SCLogDebug("No expectation created.");
665  SCReturnInt(-1);
666  } else {
667  SCLogDebug("Expectation created [direction: %s, dynamic port %"PRIu16"].",
668  state->active ? "to server" : "to client",
669  state->dyn_port);
670  }
671 
672  /* reset the dyn port to avoid duplicate */
673  state->dyn_port = 0;
674  /* reset active/passive indicator */
675  state->active = false;
676  }
677  break;
678  default:
679  break;
680  }
681  }
682 
683  return 1;
684 }
685 
686 static int FTPParsePassiveResponse(Flow *f, FtpState *state, uint8_t *input, uint32_t input_len)
687 {
688  uint16_t dyn_port =
689 #ifdef HAVE_RUST
690  rs_ftp_pasv_response(input, input_len);
691 #else
692  FTPGetV4PortNumber(input, input_len);
693 #endif
694  if (dyn_port == 0) {
695  return -1;
696  }
697  SCLogDebug("FTP passive mode (v4): dynamic port %"PRIu16"", dyn_port);
698  state->active = false;
699  state->dyn_port = dyn_port;
700  state->curr_tx->dyn_port = dyn_port;
701  state->curr_tx->active = false;
702 
703  return 0;
704 }
705 
706 static int FTPParsePassiveResponseV6(Flow *f, FtpState *state, uint8_t *input, uint32_t input_len)
707 {
708  uint16_t dyn_port =
709 #ifdef HAVE_RUST
710  rs_ftp_epsv_response(input, input_len);
711 #else
712  FTPGetV6PortNumber(input, input_len);
713 #endif
714  if (dyn_port == 0) {
715  return -1;
716  }
717  SCLogDebug("FTP passive mode (v6): dynamic port %"PRIu16"", dyn_port);
718  state->active = false;
719  state->dyn_port = dyn_port;
720  state->curr_tx->dyn_port = dyn_port;
721  state->curr_tx->active = false;
722  return 0;
723 }
724 
725 /**
726  * \brief Handle preliminary replies -- keep tx open
727  * \retval: True for a positive preliminary reply; false otherwise
728  *
729  * 1yz Positive Preliminary reply
730  *
731  * The requested action is being initiated; expect another
732  * reply before proceeding with a new command
733  */
734 static inline bool FTPIsPPR(uint8_t *input, uint32_t input_len)
735 {
736  return input_len >= 4 && isdigit(input[0]) && input[0] == '1' &&
737  isdigit(input[1]) && isdigit(input[2]) && isspace(input[3]);
738 }
739 
740 /**
741  * \brief This function is called to retrieve a ftp response
742  * \param ftp_state the ftp state structure for the parser
743  * \param input input line of the command
744  * \param input_len length of the request
745  * \param output the resulting output
746  *
747  * \retval 1 when the command is parsed, 0 otherwise
748  */
749 static int FTPParseResponse(Flow *f, void *ftp_state, AppLayerParserState *pstate,
750  uint8_t *input, uint32_t input_len,
751  void *local_data, const uint8_t flags)
752 {
753  FtpState *state = (FtpState *)ftp_state;
754  int retcode = 1;
755 
756  if (unlikely(input_len == 0)) {
757  return 1;
758  }
759 
760  FTPTransaction *tx = FTPGetOldestTx(state);
761  if (tx == NULL) {
762  tx = FTPTransactionCreate(state);
763  }
764  if (unlikely(tx == NULL)) {
765  return -1;
766  }
767  if (state->command == FTP_COMMAND_UNKNOWN || tx->command_descriptor == NULL) {
768  /* unknown */
769  tx->command_descriptor = &FtpCommands[FTP_COMMAND_MAX -1];
770  }
771 
772  state->curr_tx = tx;
773  if (state->command == FTP_COMMAND_AUTH_TLS) {
774  if (input_len >= 4 && SCMemcmp("234 ", input, 4) == 0) {
776  }
777  }
778 
779  if (state->command == FTP_COMMAND_EPRT) {
780  uint16_t dyn_port = FTPGetV6PortNumber(state->port_line, state->port_line_len);
781  if (dyn_port == 0) {
782  retcode = 0;
783  goto tx_complete;
784  }
785  state->dyn_port = dyn_port;
786  state->active = true;
787  tx->dyn_port = dyn_port;
788  tx->active = true;
789  SCLogDebug("FTP active mode (v6): dynamic port %"PRIu16"", dyn_port);
790  }
791 
792  if (state->command == FTP_COMMAND_PORT) {
793  if ((flags & STREAM_TOCLIENT)) {
794  uint16_t dyn_port = FTPGetV4PortNumber(state->port_line, state->port_line_len);
795  if (dyn_port == 0) {
796  retcode = 0;
797  goto tx_complete;
798  }
799  state->dyn_port = dyn_port;
800  state->active = true;
801  tx->dyn_port = state->dyn_port;
802  tx->active = true;
803  SCLogDebug("FTP active mode (v4): dynamic port %"PRIu16"", dyn_port);
804  }
805  }
806 
807  if (state->command == FTP_COMMAND_PASV) {
808  if (input_len >= 4 && SCMemcmp("227 ", input, 4) == 0) {
809  FTPParsePassiveResponse(f, ftp_state, input, input_len);
810  }
811  }
812 
813  if (state->command == FTP_COMMAND_EPSV) {
814  if (input_len >= 4 && SCMemcmp("229 ", input, 4) == 0) {
815  FTPParsePassiveResponseV6(f, ftp_state, input, input_len);
816  }
817  }
818 
819  if (likely(input_len)) {
820  FTPString *response = FTPStringAlloc();
821  if (likely(response)) {
822  response->len = CopyCommandLine(&response->str, input, input_len);
823  TAILQ_INSERT_TAIL(&tx->response_list, response, next);
824  }
825  }
826 
827  /* Handle preliminary replies -- keep tx open */
828  if (FTPIsPPR(input, input_len)) {
829  return retcode;
830  }
831 
832 tx_complete:
833  tx->done = true;
834  return retcode;
835 }
836 
837 
838 #ifdef DEBUG
839 static SCMutex ftp_state_mem_lock = SCMUTEX_INITIALIZER;
840 static uint64_t ftp_state_memuse = 0;
841 static uint64_t ftp_state_memcnt = 0;
842 #endif
843 
844 static void *FTPStateAlloc(void)
845 {
846  void *s = FTPCalloc(1, sizeof(FtpState));
847  if (unlikely(s == NULL))
848  return NULL;
849 
850  FtpState *ftp_state = (FtpState *) s;
851  TAILQ_INIT(&ftp_state->tx_list);
852 
853 #ifdef DEBUG
854  SCMutexLock(&ftp_state_mem_lock);
855  ftp_state_memcnt++;
856  ftp_state_memuse+=sizeof(FtpState);
857  SCMutexUnlock(&ftp_state_mem_lock);
858 #endif
859  return s;
860 }
861 
862 static void FTPStateFree(void *s)
863 {
864  FtpState *fstate = (FtpState *) s;
865  if (fstate->port_line != NULL)
866  FTPFree(fstate->port_line, fstate->port_line_size);
867  if (fstate->line_state[0].db)
868  FTPFree(fstate->line_state[0].db, fstate->line_state[0].db_len);
869  if (fstate->line_state[1].db)
870  FTPFree(fstate->line_state[1].db, fstate->line_state[1].db_len);
871 
872  //AppLayerDecoderEventsFreeEvents(&s->decoder_events);
873 
874  FTPTransaction *tx = NULL;
875  while ((tx = TAILQ_FIRST(&fstate->tx_list))) {
876  TAILQ_REMOVE(&fstate->tx_list, tx, next);
877  SCLogDebug("[%s] state %p id %"PRIu64", Freeing %d bytes at %p",
879  s, tx->tx_id,
880  tx->request_length, tx->request);
881  FTPTransactionFree(tx);
882  }
883 
884  FTPFree(s, sizeof(FtpState));
885 #ifdef DEBUG
886  SCMutexLock(&ftp_state_mem_lock);
887  ftp_state_memcnt--;
888  ftp_state_memuse-=sizeof(FtpState);
889  SCMutexUnlock(&ftp_state_mem_lock);
890 #endif
891 }
892 
893 static int FTPSetTxDetectState(void *vtx, DetectEngineState *de_state)
894 {
895  FTPTransaction *tx = (FTPTransaction *)vtx;
896  tx->de_state = de_state;
897  return 0;
898 }
899 
900 /**
901  * \brief This function returns the oldest open transaction; if none
902  * are open, then the oldest transaction is returned
903  * \param ftp_state the ftp state structure for the parser
904  *
905  * \retval transaction pointer when a transaction was found; NULL otherwise.
906  */
907 static FTPTransaction *FTPGetOldestTx(FtpState *ftp_state)
908 {
909  if (unlikely(!ftp_state)) {
910  SCLogDebug("NULL state object; no transactions available");
911  return NULL;
912  }
913  FTPTransaction *tx = NULL;
914  FTPTransaction *lasttx = NULL;
915  TAILQ_FOREACH(tx, &ftp_state->tx_list, next) {
916  /* Return oldest open tx */
917  if (!tx->done) {
918  SCLogDebug("Returning tx %p id %"PRIu64, tx, tx->tx_id);
919  return tx;
920  }
921  /* save for the end */
922  lasttx = tx;
923  }
924  /* All tx are closed; return last element */
925  if (lasttx)
926  SCLogDebug("Returning OLDEST tx %p id %"PRIu64, lasttx, lasttx->tx_id);
927  return lasttx;
928 }
929 
930 static void *FTPGetTx(void *state, uint64_t tx_id)
931 {
932  FtpState *ftp_state = (FtpState *)state;
933  if (ftp_state) {
934  FTPTransaction *tx = NULL;
935 
936  if (ftp_state->curr_tx == NULL)
937  return NULL;
938  if (ftp_state->curr_tx->tx_id == tx_id)
939  return ftp_state->curr_tx;
940 
941  TAILQ_FOREACH(tx, &ftp_state->tx_list, next) {
942  if (tx->tx_id == tx_id)
943  return tx;
944  }
945  }
946  return NULL;
947 }
948 
949 static DetectEngineState *FTPGetTxDetectState(void *vtx)
950 {
951  FTPTransaction *tx = (FTPTransaction *)vtx;
952  return tx->de_state;
953 }
954 
955 
956 static uint64_t FTPGetTxDetectFlags(void *vtx, uint8_t dir)
957 {
958  FTPTransaction *tx = (FTPTransaction *)vtx;
959  if (dir & STREAM_TOSERVER) {
960  return tx->detect_flags_ts;
961  } else {
962  return tx->detect_flags_tc;
963  }
964 }
965 
966 static void FTPSetTxDetectFlags(void *vtx, uint8_t dir, uint64_t flags)
967 {
968  FTPTransaction *tx = (FTPTransaction *)vtx;
969  if (dir & STREAM_TOSERVER) {
970  tx->detect_flags_ts = flags;
971  } else {
972  tx->detect_flags_tc = flags;
973  }
974 }
975 
976 static void FTPStateTransactionFree(void *state, uint64_t tx_id)
977 {
978  FtpState *ftp_state = state;
979  FTPTransaction *tx = NULL;
980  TAILQ_FOREACH(tx, &ftp_state->tx_list, next) {
981  if (tx_id < tx->tx_id)
982  break;
983  else if (tx_id > tx->tx_id)
984  continue;
985 
986  if (tx == ftp_state->curr_tx)
987  ftp_state->curr_tx = NULL;
988  TAILQ_REMOVE(&ftp_state->tx_list, tx, next);
989  FTPTransactionFree(tx);
990  break;
991  }
992 }
993 
994 static uint64_t FTPGetTxCnt(void *state)
995 {
996  uint64_t cnt = 0;
997  FtpState *ftp_state = state;
998  if (ftp_state) {
999  cnt = ftp_state->tx_cnt;
1000  }
1001  SCLogDebug("returning state %p %"PRIu64, state, cnt);
1002  return cnt;
1003 }
1004 
1005 static int FTPGetAlstateProgressCompletionStatus(uint8_t direction)
1006 {
1007  return FTP_STATE_FINISHED;
1008 }
1009 
1010 static int FTPGetAlstateProgress(void *vtx, uint8_t direction)
1011 {
1012  SCLogDebug("tx %p", vtx);
1013  FTPTransaction *tx = vtx;
1014 
1015  if (direction == STREAM_TOSERVER &&
1017  return FTP_STATE_PORT_DONE;
1018  }
1019 
1020  if (!tx->done)
1021  return FTP_STATE_IN_PROGRESS;
1022 
1023  return FTP_STATE_FINISHED;
1024 }
1025 
1026 
1027 static int FTPRegisterPatternsForProtocolDetection(void)
1028 {
1030  "220 (", 5, 0, STREAM_TOCLIENT) < 0)
1031  {
1032  return -1;
1033  }
1035  "FEAT", 4, 0, STREAM_TOSERVER) < 0)
1036  {
1037  return -1;
1038  }
1040  "USER ", 5, 0, STREAM_TOSERVER) < 0)
1041  {
1042  return -1;
1043  }
1045  "PASS ", 5, 0, STREAM_TOSERVER) < 0)
1046  {
1047  return -1;
1048  }
1050  "PORT ", 5, 0, STREAM_TOSERVER) < 0)
1051  {
1052  return -1;
1053  }
1054 
1055  return 0;
1056 }
1057 
1058 
1060 
1061 /**
1062  * \brief This function is called to retrieve a ftp request
1063  * \param ftp_state the ftp state structure for the parser
1064  * \param input input line of the command
1065  * \param input_len length of the request
1066  * \param output the resulting output
1067  *
1068  * \retval 1 when the command is parsed, 0 otherwise
1069  */
1070 static int FTPDataParse(Flow *f, FtpDataState *ftpdata_state,
1071  AppLayerParserState *pstate,
1072  uint8_t *input, uint32_t input_len,
1073  void *local_data, int direction)
1074 {
1075  uint16_t flags = FileFlowToFlags(f, direction);
1076  int ret = 0;
1077  /* we depend on detection engine for file pruning */
1078  flags |= FILE_USE_DETECT;
1079  if (ftpdata_state->files == NULL) {
1081  if (data == NULL) {
1082  SCReturnInt(-1);
1083  }
1084 
1085  ftpdata_state->files = FileContainerAlloc();
1086  if (ftpdata_state->files == NULL) {
1088  SCReturnInt(-1);
1089  }
1090 
1091  ftpdata_state->file_name = data->file_name;
1092  ftpdata_state->file_len = data->file_len;
1093  data->file_name = NULL;
1094  data->file_len = 0;
1095  f->parent_id = data->flow_id;
1096  ftpdata_state->command = data->cmd;
1097  switch (data->cmd) {
1098  case FTP_COMMAND_STOR:
1099  ftpdata_state->direction = STREAM_TOSERVER;
1100  break;
1101  case FTP_COMMAND_RETR:
1102  ftpdata_state->direction = STREAM_TOCLIENT;
1103  break;
1104  default:
1105  break;
1106  }
1107 
1108  /* open with fixed track_id 0 as we can have just one
1109  * file per ftp-data flow. */
1110  if (FileOpenFileWithId(ftpdata_state->files, &sbcfg,
1111  0ULL, (uint8_t *) ftpdata_state->file_name,
1112  ftpdata_state->file_len,
1113  input, input_len, flags) != 0) {
1114  SCLogDebug("Can't open file");
1115  ret = -1;
1116  }
1118  } else {
1119  if (input_len != 0) {
1120  ret = FileAppendData(ftpdata_state->files, input, input_len);
1121  if (ret == -2) {
1122  ret = 0;
1123  SCLogDebug("FileAppendData() - file no longer being extracted");
1124  goto out;
1125  } else if (ret < 0) {
1126  SCLogDebug("FileAppendData() failed: %d", ret);
1127  ret = -2;
1128  goto out;
1129  }
1130  } else {
1131  ret = FileCloseFile(ftpdata_state->files, NULL, 0, flags);
1132  ftpdata_state->state = FTPDATA_STATE_FINISHED;
1133  if (ret < 0)
1134  goto out;
1135  }
1136  }
1137 
1138  if (input_len && AppLayerParserStateIssetFlag(pstate, APP_LAYER_PARSER_EOF)) {
1139  ret = FileCloseFile(ftpdata_state->files, (uint8_t *) NULL, 0, flags);
1140  ftpdata_state->state = FTPDATA_STATE_FINISHED;
1141  }
1142 
1143 out:
1144  if (ftpdata_state->files) {
1145  FilePrune(ftpdata_state->files);
1146  }
1147  return ret;
1148 }
1149 
1150 static void FTPStateSetTxLogged(void *state, void *vtx, LoggerId logged)
1151 {
1152  FTPTransaction *tx = vtx;
1153  tx->logged = logged;
1154 }
1155 
1156 static LoggerId FTPStateGetTxLogged(void *state, void *vtx)
1157 {
1158  FTPTransaction *tx = vtx;
1159  return tx->logged;
1160 }
1161 static int FTPDataParseRequest(Flow *f, void *ftp_state,
1162  AppLayerParserState *pstate,
1163  uint8_t *input, uint32_t input_len,
1164  void *local_data, const uint8_t flags)
1165 {
1166  return FTPDataParse(f, ftp_state, pstate, input, input_len,
1167  local_data, STREAM_TOSERVER);
1168 }
1169 
1170 static int FTPDataParseResponse(Flow *f, void *ftp_state,
1171  AppLayerParserState *pstate,
1172  uint8_t *input, uint32_t input_len,
1173  void *local_data, const uint8_t flags)
1174 {
1175  return FTPDataParse(f, ftp_state, pstate, input, input_len,
1176  local_data, STREAM_TOCLIENT);
1177 }
1178 
1179 #ifdef DEBUG
1180 static SCMutex ftpdata_state_mem_lock = SCMUTEX_INITIALIZER;
1181 static uint64_t ftpdata_state_memuse = 0;
1182 static uint64_t ftpdata_state_memcnt = 0;
1183 #endif
1184 
1185 static void *FTPDataStateAlloc(void)
1186 {
1187  void *s = FTPCalloc(1, sizeof(FtpDataState));
1188  if (unlikely(s == NULL))
1189  return NULL;
1190 
1191  FtpDataState *state = (FtpDataState *) s;
1193 
1194 #ifdef DEBUG
1195  SCMutexLock(&ftpdata_state_mem_lock);
1196  ftpdata_state_memcnt++;
1197  ftpdata_state_memuse+=sizeof(FtpDataState);
1198  SCMutexUnlock(&ftpdata_state_mem_lock);
1199 #endif
1200  return s;
1201 }
1202 
1203 static void FTPDataStateFree(void *s)
1204 {
1205  FtpDataState *fstate = (FtpDataState *) s;
1206 
1207  if (fstate->de_state != NULL) {
1209  }
1210  if (fstate->file_name != NULL) {
1211  FTPFree(fstate->file_name, fstate->file_len);
1212  }
1213 
1214  FileContainerFree(fstate->files);
1215 
1216  SCFree(s);
1217 #ifdef DEBUG
1218  SCMutexLock(&ftpdata_state_mem_lock);
1219  ftpdata_state_memcnt--;
1220  ftpdata_state_memuse-=sizeof(FtpDataState);
1221  SCMutexUnlock(&ftpdata_state_mem_lock);
1222 #endif
1223 }
1224 
1225 static int FTPDataSetTxDetectState(void *vtx, DetectEngineState *de_state)
1226 {
1227  FtpDataState *ftp_state = (FtpDataState *)vtx;
1228  ftp_state->de_state = de_state;
1229  return 0;
1230 }
1231 
1232 static DetectEngineState *FTPDataGetTxDetectState(void *vtx)
1233 {
1234  FtpDataState *ftp_state = (FtpDataState *)vtx;
1235  return ftp_state->de_state;
1236 }
1237 
1238 static void FTPDataStateTransactionFree(void *state, uint64_t tx_id)
1239 {
1240  /* do nothing */
1241 }
1242 
1243 static void *FTPDataGetTx(void *state, uint64_t tx_id)
1244 {
1245  FtpDataState *ftp_state = (FtpDataState *)state;
1246  return ftp_state;
1247 }
1248 
1249 static uint64_t FTPDataGetTxCnt(void *state)
1250 {
1251  /* ftp-data is single tx */
1252  return 1;
1253 }
1254 
1255 static int FTPDataGetAlstateProgressCompletionStatus(uint8_t direction)
1256 {
1257  return FTPDATA_STATE_FINISHED;
1258 }
1259 
1260 static int FTPDataGetAlstateProgress(void *tx, uint8_t direction)
1261 {
1262  FtpDataState *ftpdata_state = (FtpDataState *)tx;
1263  return ftpdata_state->state;
1264 }
1265 
1266 static FileContainer *FTPDataStateGetFiles(void *state, uint8_t direction)
1267 {
1268  FtpDataState *ftpdata_state = (FtpDataState *)state;
1269 
1270  if (direction != ftpdata_state->direction)
1271  SCReturnPtr(NULL, "FileContainer");
1272 
1273  SCReturnPtr(ftpdata_state->files, "FileContainer");
1274 }
1275 
1277 {
1278  const char *proto_name = "ftp";
1279  const char *proto_data_name = "ftp-data";
1280 
1281  /** FTP */
1282  if (AppLayerProtoDetectConfProtoDetectionEnabled("tcp", proto_name)) {
1284  if (FTPRegisterPatternsForProtocolDetection() < 0 )
1285  return;
1287  }
1288 
1289  if (AppLayerParserConfParserEnabled("tcp", proto_name)) {
1291  FTPParseRequest);
1293  FTPParseResponse);
1294  AppLayerParserRegisterStateFuncs(IPPROTO_TCP, ALPROTO_FTP, FTPStateAlloc, FTPStateFree);
1296 
1297  AppLayerParserRegisterTxFreeFunc(IPPROTO_TCP, ALPROTO_FTP, FTPStateTransactionFree);
1298 
1300  FTPGetTxDetectState, FTPSetTxDetectState);
1301 
1303  FTPGetTxDetectFlags, FTPSetTxDetectFlags);
1304 
1305  AppLayerParserRegisterGetTx(IPPROTO_TCP, ALPROTO_FTP, FTPGetTx);
1306  AppLayerParserRegisterLoggerFuncs(IPPROTO_TCP, ALPROTO_FTP, FTPStateGetTxLogged,
1307  FTPStateSetTxLogged);
1308 
1309  AppLayerParserRegisterGetTxCnt(IPPROTO_TCP, ALPROTO_FTP, FTPGetTxCnt);
1310 
1311  AppLayerParserRegisterGetStateProgressFunc(IPPROTO_TCP, ALPROTO_FTP, FTPGetAlstateProgress);
1312 
1314  FTPGetAlstateProgressCompletionStatus);
1315 
1316 
1319  FTPDataParseRequest);
1321  FTPDataParseResponse);
1322  AppLayerParserRegisterStateFuncs(IPPROTO_TCP, ALPROTO_FTPDATA, FTPDataStateAlloc, FTPDataStateFree);
1324  AppLayerParserRegisterTxFreeFunc(IPPROTO_TCP, ALPROTO_FTPDATA, FTPDataStateTransactionFree);
1326  FTPDataGetTxDetectState, FTPDataSetTxDetectState);
1327 
1328  AppLayerParserRegisterGetFilesFunc(IPPROTO_TCP, ALPROTO_FTPDATA, FTPDataStateGetFiles);
1329 
1330  AppLayerParserRegisterGetTx(IPPROTO_TCP, ALPROTO_FTPDATA, FTPDataGetTx);
1331 
1332  AppLayerParserRegisterGetTxCnt(IPPROTO_TCP, ALPROTO_FTPDATA, FTPDataGetTxCnt);
1333 
1334  AppLayerParserRegisterGetStateProgressFunc(IPPROTO_TCP, ALPROTO_FTPDATA, FTPDataGetAlstateProgress);
1335 
1337  FTPDataGetAlstateProgressCompletionStatus);
1338 
1339  sbcfg.buf_size = 4096;
1340  sbcfg.Malloc = FTPMalloc;
1341  sbcfg.Calloc = FTPCalloc;
1342  sbcfg.Realloc = FTPRealloc;
1343  sbcfg.Free = FTPFree;
1344 
1345  FTPParseMemcap();
1346  } else {
1347  SCLogInfo("Parsed disabled for %s protocol. Protocol detection"
1348  "still on.", proto_name);
1349  }
1350 #ifdef UNITTESTS
1352 #endif
1353 }
1354 
1356 {
1357 #ifdef DEBUG
1358  SCMutexLock(&ftp_state_mem_lock);
1359  SCLogDebug("ftp_state_memcnt %"PRIu64", ftp_state_memuse %"PRIu64"",
1360  ftp_state_memcnt, ftp_state_memuse);
1361  SCMutexUnlock(&ftp_state_mem_lock);
1362 #endif
1363 }
1364 
1365 
1366 #ifdef HAVE_LIBJANSSON
1367 json_t *JsonFTPDataAddMetadata(const Flow *f)
1368 {
1369  const FtpDataState *ftp_state = NULL;
1370  if (f->alstate == NULL)
1371  return NULL;
1372  ftp_state = (FtpDataState *)f->alstate;
1373  json_t *ftpd = json_object();
1374  if (ftpd == NULL)
1375  return NULL;
1376  if (ftp_state->file_name) {
1377  size_t size = ftp_state->file_len * 2 + 1;
1378  char string[size];
1379  BytesToStringBuffer(ftp_state->file_name, ftp_state->file_len, string, size);
1380  json_object_set_new(ftpd, "filename", SCJsonString(string));
1381  }
1382  switch (ftp_state->command) {
1383  case FTP_COMMAND_STOR:
1384  json_object_set_new(ftpd, "command", json_string("STOR"));
1385  break;
1386  case FTP_COMMAND_RETR:
1387  json_object_set_new(ftpd, "command", json_string("RETR"));
1388  break;
1389  default:
1390  break;
1391  }
1392  return ftpd;
1393 }
1394 #endif /* HAVE_LIBJANSSON */
1395 
1396 /* UNITTESTS */
1397 #ifdef UNITTESTS
1398 
1399 /** \test Send a get request in one chunk. */
1400 static int FTPParserTest01(void)
1401 {
1402  int result = 1;
1403  Flow f;
1404  uint8_t ftpbuf[] = "PORT 192,168,1,1,0,80\r\n";
1405  uint32_t ftplen = sizeof(ftpbuf) - 1; /* minus the \0 */
1406  TcpSession ssn;
1408 
1409  memset(&f, 0, sizeof(f));
1410  memset(&ssn, 0, sizeof(ssn));
1411 
1412  FLOW_INITIALIZE(&f);
1413  f.protoctx = (void *)&ssn;
1414  f.proto = IPPROTO_TCP;
1415  f.alproto = ALPROTO_FTP;
1416 
1418 
1419  FLOWLOCK_WRLOCK(&f);
1420  int r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_FTP,
1421  STREAM_TOSERVER | STREAM_EOF, ftpbuf, ftplen);
1422  if (r != 0) {
1423  SCLogDebug("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
1424  result = 0;
1425  FLOWLOCK_UNLOCK(&f);
1426  goto end;
1427  }
1428  FLOWLOCK_UNLOCK(&f);
1429 
1430  FtpState *ftp_state = f.alstate;
1431  if (ftp_state == NULL) {
1432  SCLogDebug("no ftp state: ");
1433  result = 0;
1434  goto end;
1435  }
1436 
1437  if (ftp_state->command != FTP_COMMAND_PORT) {
1438  SCLogDebug("expected command %" PRIu32 ", got %" PRIu32 ": ", FTP_COMMAND_PORT, ftp_state->command);
1439  result = 0;
1440  goto end;
1441  }
1442 
1443 end:
1444  if (alp_tctx != NULL)
1445  AppLayerParserThreadCtxFree(alp_tctx);
1447  FLOW_DESTROY(&f);
1448  return result;
1449 }
1450 
1451 /** \test Send a split get request. */
1452 static int FTPParserTest03(void)
1453 {
1454  int result = 1;
1455  Flow f;
1456  uint8_t ftpbuf1[] = "POR";
1457  uint32_t ftplen1 = sizeof(ftpbuf1) - 1; /* minus the \0 */
1458  uint8_t ftpbuf2[] = "T 192,168,1";
1459  uint32_t ftplen2 = sizeof(ftpbuf2) - 1; /* minus the \0 */
1460  uint8_t ftpbuf3[] = "1,1,10,20\r\n";
1461  uint32_t ftplen3 = sizeof(ftpbuf3) - 1; /* minus the \0 */
1462  TcpSession ssn;
1464 
1465  memset(&f, 0, sizeof(f));
1466  memset(&ssn, 0, sizeof(ssn));
1467 
1468  FLOW_INITIALIZE(&f);
1469  f.protoctx = (void *)&ssn;
1470  f.proto = IPPROTO_TCP;
1471  f.alproto = ALPROTO_FTP;
1472 
1474 
1475  FLOWLOCK_WRLOCK(&f);
1476  int r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_FTP,
1477  STREAM_TOSERVER | STREAM_START, ftpbuf1,
1478  ftplen1);
1479  if (r != 0) {
1480  SCLogDebug("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
1481  result = 0;
1482  FLOWLOCK_UNLOCK(&f);
1483  goto end;
1484  }
1485  FLOWLOCK_UNLOCK(&f);
1486 
1487  FLOWLOCK_WRLOCK(&f);
1488  r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_FTP, STREAM_TOSERVER,
1489  ftpbuf2, ftplen2);
1490  if (r != 0) {
1491  SCLogDebug("toserver chunk 2 returned %" PRId32 ", expected 0: ", r);
1492  result = 0;
1493  FLOWLOCK_UNLOCK(&f);
1494  goto end;
1495  }
1496  FLOWLOCK_UNLOCK(&f);
1497 
1498  FLOWLOCK_WRLOCK(&f);
1499  r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_FTP,
1500  STREAM_TOSERVER | STREAM_EOF, ftpbuf3, ftplen3);
1501  if (r != 0) {
1502  SCLogDebug("toserver chunk 3 returned %" PRId32 ", expected 0: ", r);
1503  result = 0;
1504  FLOWLOCK_UNLOCK(&f);
1505  goto end;
1506  }
1507  FLOWLOCK_UNLOCK(&f);
1508 
1509  FtpState *ftp_state = f.alstate;
1510  if (ftp_state == NULL) {
1511  SCLogDebug("no ftp state: ");
1512  result = 0;
1513  goto end;
1514  }
1515 
1516  if (ftp_state->command != FTP_COMMAND_PORT) {
1517  SCLogDebug("expected command %" PRIu32 ", got %" PRIu32 ": ", FTP_COMMAND_PORT, ftp_state->command);
1518  result = 0;
1519  goto end;
1520  }
1521 
1522 end:
1523  if (alp_tctx != NULL)
1524  AppLayerParserThreadCtxFree(alp_tctx);
1526  return result;
1527 }
1528 
1529 /** \test See how it deals with an incomplete request. */
1530 static int FTPParserTest06(void)
1531 {
1532  int result = 1;
1533  Flow f;
1534  uint8_t ftpbuf1[] = "PORT";
1535  uint32_t ftplen1 = sizeof(ftpbuf1) - 1; /* minus the \0 */
1536  TcpSession ssn;
1538 
1539  memset(&f, 0, sizeof(f));
1540  memset(&ssn, 0, sizeof(ssn));
1541 
1542  FLOW_INITIALIZE(&f);
1543  f.protoctx = (void *)&ssn;
1544  f.proto = IPPROTO_TCP;
1545  f.alproto = ALPROTO_FTP;
1546 
1548 
1549  FLOWLOCK_WRLOCK(&f);
1550  int r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_FTP,
1552  ftpbuf1,
1553  ftplen1);
1554  if (r != 0) {
1555  SCLogDebug("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
1556  result = 0;
1557  FLOWLOCK_UNLOCK(&f);
1558  goto end;
1559  }
1560  FLOWLOCK_UNLOCK(&f);
1561 
1562  FtpState *ftp_state = f.alstate;
1563  if (ftp_state == NULL) {
1564  SCLogDebug("no ftp state: ");
1565  result = 0;
1566  goto end;
1567  }
1568 
1569  if (ftp_state->command != FTP_COMMAND_UNKNOWN) {
1570  SCLogDebug("expected command %" PRIu32 ", got %" PRIu32 ": ", FTP_COMMAND_UNKNOWN, ftp_state->command);
1571  result = 0;
1572  goto end;
1573  }
1574 
1575 end:
1576  if (alp_tctx != NULL)
1577  AppLayerParserThreadCtxFree(alp_tctx);
1579  FLOW_DESTROY(&f);
1580  return result;
1581 }
1582 
1583 /** \test See how it deals with an incomplete request in multiple chunks. */
1584 static int FTPParserTest07(void)
1585 {
1586  int result = 1;
1587  Flow f;
1588  uint8_t ftpbuf1[] = "PO";
1589  uint32_t ftplen1 = sizeof(ftpbuf1) - 1; /* minus the \0 */
1590  uint8_t ftpbuf2[] = "RT\r\n";
1591  uint32_t ftplen2 = sizeof(ftpbuf2) - 1; /* minus the \0 */
1592  TcpSession ssn;
1594 
1595  memset(&f, 0, sizeof(f));
1596  memset(&ssn, 0, sizeof(ssn));
1597 
1598  FLOW_INITIALIZE(&f);
1599  f.protoctx = (void *)&ssn;
1600  f.proto = IPPROTO_TCP;
1601  f.alproto = ALPROTO_FTP;
1602 
1604 
1605  FLOWLOCK_WRLOCK(&f);
1606  int r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_FTP,
1607  STREAM_TOSERVER | STREAM_START, ftpbuf1,
1608  ftplen1);
1609  if (r != 0) {
1610  SCLogDebug("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
1611  result = 0;
1612  FLOWLOCK_UNLOCK(&f);
1613  goto end;
1614  }
1615  FLOWLOCK_UNLOCK(&f);
1616 
1617  FLOWLOCK_WRLOCK(&f);
1618  r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_FTP,
1619  STREAM_TOSERVER | STREAM_EOF, ftpbuf2, ftplen2);
1620  if (r != 0) {
1621  SCLogDebug("toserver chunk 2 returned %" PRId32 ", expected 0: ", r);
1622  result = 0;
1623  FLOWLOCK_UNLOCK(&f);
1624  goto end;
1625  }
1626  FLOWLOCK_UNLOCK(&f);
1627 
1628  FtpState *ftp_state = f.alstate;
1629  if (ftp_state == NULL) {
1630  SCLogDebug("no ftp state: ");
1631  result = 0;
1632  goto end;
1633  }
1634 
1635  if (ftp_state->command != FTP_COMMAND_PORT) {
1636  SCLogDebug("expected command %" PRIu32 ", got %" PRIu32 ": ",
1637  FTP_COMMAND_PORT, ftp_state->command);
1638  result = 0;
1639  goto end;
1640  }
1641 
1642 end:
1643  if (alp_tctx != NULL)
1644  AppLayerParserThreadCtxFree(alp_tctx);
1646  FLOW_DESTROY(&f);
1647  return result;
1648 }
1649 
1650 /** \test Test case where chunks are smaller than the delim length and the
1651  * last chunk is supposed to match the delim. */
1652 static int FTPParserTest10(void)
1653 {
1654  int result = 1;
1655  Flow f;
1656  uint8_t ftpbuf1[] = "PORT 1,2,3,4,5,6\r\n";
1657  uint32_t ftplen1 = sizeof(ftpbuf1) - 1; /* minus the \0 */
1658  TcpSession ssn;
1660  int r = 0;
1661  memset(&f, 0, sizeof(f));
1662  memset(&ssn, 0, sizeof(ssn));
1663 
1664  FLOW_INITIALIZE(&f);
1665  f.protoctx = (void *)&ssn;
1666  f.proto = IPPROTO_TCP;
1667  f.alproto = ALPROTO_FTP;
1668 
1670 
1671  uint32_t u;
1672  for (u = 0; u < ftplen1; u++) {
1673  uint8_t flags = 0;
1674 
1675  if (u == 0) flags = STREAM_TOSERVER|STREAM_START;
1676  else if (u == (ftplen1 - 1)) flags = STREAM_TOSERVER|STREAM_EOF;
1677  else flags = STREAM_TOSERVER;
1678 
1679  FLOWLOCK_WRLOCK(&f);
1680  r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_FTP, flags,
1681  &ftpbuf1[u], 1);
1682  if (r != 0) {
1683  SCLogDebug("toserver chunk %" PRIu32 " returned %" PRId32 ", expected 0: ", u, r);
1684  result = 0;
1685  FLOWLOCK_UNLOCK(&f);
1686  goto end;
1687  }
1688  FLOWLOCK_UNLOCK(&f);
1689  }
1690 
1691  FtpState *ftp_state = f.alstate;
1692  if (ftp_state == NULL) {
1693  SCLogDebug("no ftp state: ");
1694  result = 0;
1695  goto end;
1696  }
1697 
1698  if (ftp_state->command != FTP_COMMAND_PORT) {
1699  SCLogDebug("expected command %" PRIu32 ", got %" PRIu32 ": ", FTP_COMMAND_PORT, ftp_state->command);
1700  result = 0;
1701  goto end;
1702  }
1703 
1704 end:
1705  if (alp_tctx != NULL)
1706  AppLayerParserThreadCtxFree(alp_tctx);
1708  FLOW_DESTROY(&f);
1709  return result;
1710 }
1711 #endif /* UNITTESTS */
1712 
1714 {
1715 #ifdef UNITTESTS
1716  UtRegisterTest("FTPParserTest01", FTPParserTest01);
1717  UtRegisterTest("FTPParserTest03", FTPParserTest03);
1718  UtRegisterTest("FTPParserTest06", FTPParserTest06);
1719  UtRegisterTest("FTPParserTest07", FTPParserTest07);
1720  UtRegisterTest("FTPParserTest10", FTPParserTest10);
1721 #endif /* UNITTESTS */
1722 }
1723 
#define SCMutex
#define STREAMING_BUFFER_CONFIG_INITIALIZER
uint16_t flags
#define SCMemcmp(a, b, c)
Definition: util-memcmp.h:369
FtpLineState line_state[2]
#define SCLogDebug(...)
Definition: util-debug.h:335
uint8_t * input
void *(* Malloc)(size_t size)
#define TAILQ_FIRST(head)
Definition: queue.h:339
#define TAILQ_FOREACH(var, head, field)
Definition: queue.h:350
struct HtpBodyChunk_ * next
int AppLayerExpectationGetDataId(void)
void AppLayerParserRegisterGetStateProgressFunc(uint8_t ipproto, AppProto alproto, int(*StateGetProgress)(void *alstate, uint8_t direction))
uint8_t proto
Definition: flow.h:344
FileContainer * FileContainerAlloc(void)
allocate a FileContainer
Definition: util-file.c:380
LoggerId
#define FLOWLOCK_UNLOCK(fb)
Definition: flow.h:243
int logged
#define unlikely(expr)
Definition: util-optimize.h:35
uint64_t detect_flags_ts
int AppLayerProtoDetectConfProtoDetectionEnabled(const char *ipproto, const char *alproto)
Given a protocol name, checks if proto detection is enabled in the conf file.
void AppLayerParserRegisterDetectFlagsFuncs(uint8_t ipproto, AppProto alproto, uint64_t(*GetTxDetectFlags)(void *tx, uint8_t dir), void(*SetTxDetectFlags)(void *tx, uint8_t dir, uint64_t))
int16_t file_len
int AppLayerParserStateIssetFlag(AppLayerParserState *pstate, uint8_t flag)
void AppLayerParserRegisterParserAcceptableDataDirection(uint8_t ipproto, AppProto alproto, uint8_t direction)
void AppLayerParserRegisterDetectStateFuncs(uint8_t ipproto, AppProto alproto, DetectEngineState *(*GetTxDetectState)(void *tx), int(*SetTxDetectState)(void *tx, DetectEngineState *))
uint8_t * file_name
#define SC_ATOMIC_ADD(name, val)
add a value to our atomic variable
Definition: util-atomic.h:107
uint8_t * request
void AppLayerParserThreadCtxFree(AppLayerParserThreadCtx *tctx)
Destroys the app layer parser thread context obtained using AppLayerParserThreadCtxAlloc().
void(* DFree)(void *)
uint16_t src
void StreamTcpFreeConfig(char quiet)
Definition: stream-tcp.c:669
#define FLOWLOCK_WRLOCK(fb)
Definition: flow.h:240
#define SC_ATOMIC_SUB(name, val)
sub a value from our atomic variable
Definition: util-atomic.h:124
void AppLayerParserRegisterLoggerFuncs(uint8_t ipproto, AppProto alproto, LoggerId(*StateGetTxLogged)(void *, void *), void(*StateSetTxLogged)(void *, void *, LoggerId))
uint64_t FTPMemuseGlobalCounter(void)
#define TRUE
#define SCMutexLock(mut)
int ConfGet(const char *name, const char **vptr)
Retrieve the value of a configuration node.
Definition: conf.c:331
void * protoctx
Definition: flow.h:400
void *(* Calloc)(size_t n, size_t size)
FtpRequestCommand command
int ByteExtractStringUint16(uint16_t *res, int base, uint16_t len, const char *str)
Definition: util-byte.c:264
int ParseSizeStringU64(const char *size, uint64_t *res)
Definition: util-misc.c:203
FtpRequestCommand cmd
void * alstate
Definition: flow.h:438
SC_ATOMIC_DECLARE(uint64_t, ftp_memuse)
int AppLayerParserConfParserEnabled(const char *ipproto, const char *alproto_name)
check if a parser is enabled in the config Returns enabled always if: were running unittests and when...
#define SC_ATOMIC_INIT(name)
Initialize the previously declared atomic variable and it&#39;s lock.
Definition: util-atomic.h:81
uint8_t direction
#define str(s)
#define SCCalloc(nm, a)
Definition: util-mem.h:253
int AppLayerParserRegisterParser(uint8_t ipproto, AppProto alproto, uint8_t direction, AppLayerParserFPtr Parser)
Register app layer parser for the protocol.
#define SCMutexUnlock(mut)
uint64_t ftp_config_memcap
struct FtpState_ FtpState
void AppLayerParserRegisterGetTx(uint8_t ipproto, AppProto alproto, void *(StateGetTx)(void *alstate, uint64_t tx_id))
void * memrchr(const void *s, int c, size_t n)
Definition: util-memrchr.c:30
#define SCMUTEX_INITIALIZER
void AppLayerParserRegisterGetFilesFunc(uint8_t ipproto, AppProto alproto, FileContainer *(*StateGetFiles)(void *, uint8_t))
#define TAILQ_INIT(head)
Definition: queue.h:370
void AppLayerRegisterExpectationProto(uint8_t proto, AppProto alproto)
const char * command_name_upper
Definition: app-layer-ftp.h:92
Data structures and function prototypes for keeping state for the detection engine.
FtpRequestCommand command
#define SCLogError(err_code,...)
Macro used to log ERROR messages.
Definition: util-debug.h:294
#define FLOW_DESTROY(f)
Definition: flow-util.h:119
FTPTransaction * curr_tx
void FTPParserRegisterTests(void)
uint8_t * str
#define STREAM_EOF
Definition: stream.h:30
void UtRegisterTest(const char *name, int(*TestFn)(void))
Register unit test.
void FileContainerFree(FileContainer *ffc)
Free a FileContainer.
Definition: util-file.c:416
void RegisterFTPParsers(void)
void AppLayerRequestProtocolTLSUpgrade(Flow *f)
request applayer to wrap up this protocol and rerun protocol detection with expectation of TLS...
#define TAILQ_REMOVE(head, elm, field)
Definition: queue.h:412
void *(* Realloc)(void *ptr, size_t orig_size, size_t size)
void BytesToStringBuffer(const uint8_t *bytes, size_t nbytes, char *outstr, size_t outlen)
Turn byte array into string.
Definition: util-byte.c:85
#define SCEnter(...)
Definition: util-debug.h:337
void StreamTcpInitConfig(char)
To initialize the stream global configuration data.
Definition: stream-tcp.c:365
#define STREAM_TOCLIENT
Definition: stream.h:32
uint64_t FTPMemcapGlobalCounter(void)
void AppLayerParserRegisterGetStateProgressCompletionStatus(AppProto alproto, int(*StateGetProgressCompletionStatus)(uint8_t direction))
uint8_t * port_line
AppLayerParserThreadCtx * AppLayerParserThreadCtxAlloc(void)
Gets a new app layer protocol&#39;s parser thread context.
uint16_t dyn_port
void DetectEngineStateFree(DetectEngineState *state)
Frees a DetectEngineState object.
#define SCReturnInt(x)
Definition: util-debug.h:341
void FilePrune(FileContainer *ffc)
Definition: util-file.c:345
#define SCRealloc(x, a)
Definition: util-mem.h:238
#define TAILQ_INSERT_TAIL(head, elm, field)
Definition: queue.h:385
uint8_t * current_line
uint64_t tx_cnt
#define SCMalloc(a)
Definition: util-mem.h:222
#define FILE_USE_DETECT
Definition: util-file.h:49
void FTPAtExitPrintStats(void)
#define SCLogInfo(...)
Macro used to log INFORMATIONAL messages.
Definition: util-debug.h:254
void AppLayerProtoDetectRegisterProtocol(AppProto alproto, const char *alproto_name)
Registers a protocol for protocol detection phase.
uint32_t db_len
#define SCFree(a)
Definition: util-mem.h:322
PoolThreadReserved res
uint32_t request_length
uint16_t tx_id
uint8_t current_line_lf_seen
uint32_t port_line_len
void AppLayerParserRegisterProtocolUnittests(uint8_t ipproto, AppProto alproto, void(*RegisterUnittests)(void))
int64_t parent_id
Definition: flow.h:389
#define STREAM_START
Definition: stream.h:29
#define FLOW_INITIALIZE(f)
Definition: flow-util.h:39
uint8_t direction
#define STREAM_TOSERVER
Definition: stream.h:31
int AppLayerExpectationCreate(Flow *f, int direction, Port src, Port dst, AppProto alproto, void *data)
#define SCReturnPtr(x, type)
Definition: util-debug.h:353
int FileOpenFileWithId(FileContainer *ffc, const StreamingBufferConfig *sbcfg, uint32_t track_id, const uint8_t *name, uint16_t name_len, const uint8_t *data, uint32_t data_len, uint16_t flags)
Open a new File.
Definition: util-file.c:868
int FileCloseFile(FileContainer *ffc, const uint8_t *data, uint32_t data_len, uint16_t flags)
Close a File.
Definition: util-file.c:963
#define APP_LAYER_PARSER_EOF
#define SC_ATOMIC_GET(name)
Get the value from the atomic variable.
Definition: util-atomic.h:192
uint16_t FileFlowToFlags(const Flow *flow, uint8_t direction)
Definition: util-file.c:217
void * FlowGetStorageById(Flow *f, int id)
Definition: flow-storage.c:39
uint32_t port_line_size
uint16_t file_len
uint16_t length
struct FtpDataState_ FtpDataState
int AppLayerProtoDetectPMRegisterPatternCI(uint8_t ipproto, AppProto alproto, const char *pattern, uint16_t depth, uint16_t offset, uint8_t direction)
Registers a case-insensitive pattern for protocol detection.
FtpRequestCommand
Definition: app-layer-ftp.h:34
DetectEngineState * de_state
uint32_t current_line_len
uint64_t detect_flags_tc
DetectEngineState * de_state
uint8_t * db
uint8_t * file_name
void AppLayerParserRegisterGetTxCnt(uint8_t ipproto, AppProto alproto, uint64_t(*StateGetTxCnt)(void *alstate))
void AppLayerParserRegisterStateFuncs(uint8_t ipproto, AppProto alproto, void *(*StateAlloc)(void), void(*StateFree)(void *))
AppProto alproto
application level protocol
Definition: flow.h:409
const FtpCommand * command_descriptor
int FileAppendData(FileContainer *ffc, const uint8_t *data, uint32_t data_len)
Store/handle a chunk of file data in the File structure The last file in the FileContainer will be us...
Definition: util-file.c:663
void FlowFreeStorageById(Flow *f, int id)
Definition: flow-storage.c:54
#define likely(expr)
Definition: util-optimize.h:32
uint16_t len
int32_t input_len
const FtpCommand FtpCommands[FTP_COMMAND_MAX+1]
Definition: app-layer-ftp.c:67
Flow data structure.
Definition: flow.h:325
uint8_t current_line_delimiter_len
void(* Free)(void *ptr, size_t size)
int AppLayerParserParse(ThreadVars *tv, AppLayerParserThreadCtx *alp_tctx, Flow *f, AppProto alproto, uint8_t flags, uint8_t *input, uint32_t input_len)
FileContainer * files
FtpRequestCommand command
Definition: app-layer-ftp.h:91
uint8_t current_line_db
void AppLayerParserRegisterTxFreeFunc(uint8_t ipproto, AppProto alproto, void(*StateTransactionFree)(void *, uint64_t))
uint64_t flow_id