]> git.ipfire.org Git - people/ms/suricata.git/blame - src/app-layer-ftp.c
nfs: code cleanups
[people/ms/suricata.git] / src / app-layer-ftp.c
CommitLineData
b0a69344 1/* Copyright (C) 2007-2017 Open Information Security Foundation
ce019275
WM
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.
f2f9b832 6 *
ce019275
WM
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/**
f2f9b832 19 * \file
ce019275 20 *
f2f9b832 21 * \author Pablo Rincon Crespo <pablo.rincon.crespo@gmail.com>
b0a69344 22 * \author Eric Leblond <eric@regit.org>
1930b1f5 23 * \author Jeff Lucovsky <jeff@lucovsky.org>
f2f9b832
PR
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
cc76aa4b 36#include "flow-util.h"
b0a69344 37#include "flow-storage.h"
cc76aa4b 38
0e4235cc
WM
39#include "detect-engine-state.h"
40
f2f9b832
PR
41#include "stream-tcp-private.h"
42#include "stream-tcp-reassemble.h"
6a53ab9c 43#include "stream-tcp.h"
f2f9b832
PR
44#include "stream.h"
45
429c6388 46#include "app-layer.h"
f2f9b832
PR
47#include "app-layer-protos.h"
48#include "app-layer-parser.h"
49#include "app-layer-ftp.h"
b0a69344 50#include "app-layer-expectation.h"
f2f9b832 51
705471e4 52#include "util-spm.h"
09ab032a 53#include "util-mpm.h"
f2f9b832
PR
54#include "util-unittest.h"
55#include "util-debug.h"
1859ed54 56#include "util-memcmp.h"
b0a69344
EL
57#include "util-memrchr.h"
58#include "util-byte.h"
711b6fb3
EL
59#include "util-mem.h"
60#include "util-misc.h"
b0a69344 61
7e1235c9 62#include "output-json.h"
b573c16d 63#include "rust.h"
7e1235c9 64
09ab032a
JL
65typedef struct FTPThreadCtx_ {
66 MpmThreadCtx *ftp_mpm_thread_ctx;
67 PrefilterRuleStore *pmq;
68} FTPThreadCtx;
69
70#define FTP_MPM mpm_default_matcher
71
72static MpmCtx *ftp_mpm_ctx = NULL;
73
1930b1f5
JL
74const FtpCommand FtpCommands[FTP_COMMAND_MAX + 1] = {
75 /* Parsed and handled */
09ab032a
JL
76 { FTP_COMMAND_PORT, "PORT", 4},
77 { FTP_COMMAND_EPRT, "EPRT", 4},
78 { FTP_COMMAND_AUTH_TLS, "AUTH TLS", 8},
79 { FTP_COMMAND_PASV, "PASV", 4},
80 { FTP_COMMAND_RETR, "RETR", 4},
81 { FTP_COMMAND_EPSV, "EPSV", 4},
82 { FTP_COMMAND_STOR, "STOR", 4},
1930b1f5
JL
83
84 /* Parsed, but not handled */
09ab032a
JL
85 { FTP_COMMAND_ABOR, "ABOR", 4},
86 { FTP_COMMAND_ACCT, "ACCT", 4},
87 { FTP_COMMAND_ALLO, "ALLO", 4},
88 { FTP_COMMAND_APPE, "APPE", 4},
89 { FTP_COMMAND_CDUP, "CDUP", 4},
90 { FTP_COMMAND_CHMOD, "CHMOD", 5},
91 { FTP_COMMAND_CWD, "CWD", 3},
92 { FTP_COMMAND_DELE, "DELE", 4},
93 { FTP_COMMAND_HELP, "HELP", 4},
94 { FTP_COMMAND_IDLE, "IDLE", 4},
95 { FTP_COMMAND_LIST, "LIST", 4},
96 { FTP_COMMAND_MAIL, "MAIL", 4},
97 { FTP_COMMAND_MDTM, "MDTM", 4},
98 { FTP_COMMAND_MKD, "MKD", 3},
99 { FTP_COMMAND_MLFL, "MLFL", 4},
100 { FTP_COMMAND_MODE, "MODE", 4},
101 { FTP_COMMAND_MRCP, "MRCP", 4},
102 { FTP_COMMAND_MRSQ, "MRSQ", 4},
103 { FTP_COMMAND_MSAM, "MSAM", 4},
104 { FTP_COMMAND_MSND, "MSND", 4},
105 { FTP_COMMAND_MSOM, "MSOM", 4},
106 { FTP_COMMAND_NLST, "NLST", 4},
107 { FTP_COMMAND_NOOP, "NOOP", 4},
108 { FTP_COMMAND_PASS, "PASS", 4},
109 { FTP_COMMAND_PWD, "PWD", 3},
110 { FTP_COMMAND_QUIT, "QUIT", 4},
111 { FTP_COMMAND_REIN, "REIN", 4},
112 { FTP_COMMAND_REST, "REST", 4},
113 { FTP_COMMAND_RMD, "RMD", 3},
114 { FTP_COMMAND_RNFR, "RNFR", 4},
115 { FTP_COMMAND_RNTO, "RNTO", 4},
116 { FTP_COMMAND_SITE, "SITE", 4},
117 { FTP_COMMAND_SIZE, "SIZE", 4},
118 { FTP_COMMAND_SMNT, "SMNT", 4},
119 { FTP_COMMAND_STAT, "STAT", 4},
120 { FTP_COMMAND_STOU, "STOU", 4},
121 { FTP_COMMAND_STRU, "STRU", 4},
122 { FTP_COMMAND_SYST, "SYST", 4},
123 { FTP_COMMAND_TYPE, "TYPE", 4},
124 { FTP_COMMAND_UMASK, "UMASK", 5},
125 { FTP_COMMAND_USER, "USER", 4},
126 { FTP_COMMAND_UNKNOWN, NULL, 0}
1930b1f5 127};
711b6fb3
EL
128uint64_t ftp_config_memcap = 0;
129
130SC_ATOMIC_DECLARE(uint64_t, ftp_memuse);
131SC_ATOMIC_DECLARE(uint64_t, ftp_memcap);
132
8ae69115 133static FTPTransaction *FTPGetOldestTx(FtpState *);
2149807b 134
711b6fb3
EL
135static void FTPParseMemcap(void)
136{
137 const char *conf_val;
138
139 /** set config values for memcap, prealloc and hash_size */
140 if ((ConfGet("app-layer.protocols.ftp.memcap", &conf_val)) == 1)
141 {
142 if (ParseSizeStringU64(conf_val, &ftp_config_memcap) < 0) {
143 SCLogError(SC_ERR_SIZE_PARSE, "Error parsing ftp.memcap "
144 "from conf file - %s. Killing engine",
145 conf_val);
146 exit(EXIT_FAILURE);
147 }
148 SCLogInfo("FTP memcap: %"PRIu64, ftp_config_memcap);
149 } else {
150 /* default to unlimited */
151 ftp_config_memcap = 0;
152 }
153
154 SC_ATOMIC_INIT(ftp_memuse);
155 SC_ATOMIC_INIT(ftp_memcap);
156}
157
158static void FTPIncrMemuse(uint64_t size)
159{
160 (void) SC_ATOMIC_ADD(ftp_memuse, size);
161 return;
162}
163
164static void FTPDecrMemuse(uint64_t size)
165{
166 (void) SC_ATOMIC_SUB(ftp_memuse, size);
167 return;
168}
169
170uint64_t FTPMemuseGlobalCounter(void)
171{
172 uint64_t tmpval = SC_ATOMIC_GET(ftp_memuse);
173 return tmpval;
174}
175
176uint64_t FTPMemcapGlobalCounter(void)
177{
178 uint64_t tmpval = SC_ATOMIC_GET(ftp_memcap);
179 return tmpval;
180}
181
182/**
183 * \brief Check if alloc'ing "size" would mean we're over memcap
184 *
185 * \retval 1 if in bounds
186 * \retval 0 if not in bounds
187 */
188static int FTPCheckMemcap(uint64_t size)
189{
190 if (ftp_config_memcap == 0 || size + SC_ATOMIC_GET(ftp_memuse) <= ftp_config_memcap)
191 return 1;
192 (void) SC_ATOMIC_ADD(ftp_memcap, 1);
193 return 0;
194}
195
196static void *FTPMalloc(size_t size)
197{
198 void *ptr = NULL;
199
200 if (FTPCheckMemcap((uint32_t)size) == 0)
201 return NULL;
202
203 ptr = SCMalloc(size);
204
205 if (unlikely(ptr == NULL))
206 return NULL;
207
208 FTPIncrMemuse((uint64_t)size);
209
210 return ptr;
211}
212
213static void *FTPCalloc(size_t n, size_t size)
214{
215 void *ptr = NULL;
216
217 if (FTPCheckMemcap((uint32_t)(n * size)) == 0)
218 return NULL;
219
220 ptr = SCCalloc(n, size);
221
222 if (unlikely(ptr == NULL))
223 return NULL;
224
225 FTPIncrMemuse((uint64_t)(n * size));
226
227 return ptr;
228}
229
230static void *FTPRealloc(void *ptr, size_t orig_size, size_t size)
231{
232 void *rptr = NULL;
233
234 if (FTPCheckMemcap((uint32_t)(size - orig_size)) == 0)
235 return NULL;
236
237 rptr = SCRealloc(ptr, size);
238 if (rptr == NULL)
239 return NULL;
240
241 if (size > orig_size) {
242 FTPIncrMemuse(size - orig_size);
243 } else {
244 FTPDecrMemuse(orig_size - size);
245 }
246
247 return rptr;
248}
249
250static void FTPFree(void *ptr, size_t size)
251{
252 SCFree(ptr);
253
254 FTPDecrMemuse((uint64_t)size);
255}
256
1930b1f5
JL
257static FTPString *FTPStringAlloc(void)
258{
259 return FTPCalloc(1, sizeof(FTPString));
260}
261
262static void FTPStringFree(FTPString *str)
263{
264 if (str->str) {
265 FTPFree(str->str, str->len);
266 }
267
268 FTPFree(str, sizeof(FTPString));
269}
270
09ab032a
JL
271static void *FTPLocalStorageAlloc(void)
272{
273 /* needed by the mpm */
274 FTPThreadCtx *td = SCCalloc(1, sizeof(*td));
275 if (td == NULL) {
276 exit(EXIT_FAILURE);
277 }
278
279 td->pmq = SCCalloc(1, sizeof(*td->pmq));
280 if (td->pmq == NULL) {
281 exit(EXIT_FAILURE);
282 }
283 PmqSetup(td->pmq);
284
285 td->ftp_mpm_thread_ctx = SCCalloc(1, sizeof(MpmThreadCtx));
286 if (unlikely(td->ftp_mpm_thread_ctx == NULL)) {
287 exit(EXIT_FAILURE);
288 }
289 MpmInitThreadCtx(td->ftp_mpm_thread_ctx, FTP_MPM);
290 return td;
291}
292
293static void FTPLocalStorageFree(void *ptr)
294{
295 FTPThreadCtx *td = ptr;
296 if (td != NULL) {
297 if (td->pmq != NULL) {
298 PmqFree(td->pmq);
299 SCFree(td->pmq);
300 }
301
302 if (td->ftp_mpm_thread_ctx != NULL) {
303 mpm_table[FTP_MPM].DestroyThreadCtx(ftp_mpm_ctx, td->ftp_mpm_thread_ctx);
304 SCFree(td->ftp_mpm_thread_ctx);
305 }
306
307 SCFree(td);
308 }
309
310 return;
311}
1930b1f5
JL
312static FTPTransaction *FTPTransactionCreate(FtpState *state)
313{
314 SCEnter();
315 FTPTransaction *tx = FTPCalloc(1, sizeof(*tx));
316 if (tx == NULL) {
317 return NULL;
318 }
319
320 TAILQ_INSERT_TAIL(&state->tx_list, tx, next);
321 tx->tx_id = state->tx_cnt++;
322
323 TAILQ_INIT(&tx->response_list);
324
325 SCLogDebug("new transaction %p (state tx cnt %"PRIu64")", tx, state->tx_cnt);
326 return tx;
327}
328
329static void FTPTransactionFree(FTPTransaction *tx)
330{
331 SCEnter();
332
333 if (tx->de_state != NULL) {
334 DetectEngineStateFree(tx->de_state);
335 }
336
337 if (tx->request) {
338 FTPFree(tx->request, tx->request_length);
339 }
1930b1f5
JL
340
341 FTPString *str = NULL;
342 while ((str = TAILQ_FIRST(&tx->response_list))) {
343 TAILQ_REMOVE(&tx->response_list, str, next);
344 FTPStringFree(str);
345 }
346
347 SCFree(tx);
348}
349
6ea8ac44
AS
350static int FTPGetLineForDirection(FtpState *state, FtpLineState *line_state)
351{
1f07d152 352 void *ptmp;
6ea8ac44
AS
353 if (line_state->current_line_lf_seen == 1) {
354 /* we have seen the lf for the previous line. Clear the parser
355 * details to parse new line */
356 line_state->current_line_lf_seen = 0;
357 if (line_state->current_line_db == 1) {
358 line_state->current_line_db = 0;
711b6fb3 359 FTPFree(line_state->db, line_state->db_len);
6ea8ac44
AS
360 line_state->db = NULL;
361 line_state->db_len = 0;
362 state->current_line = NULL;
363 state->current_line_len = 0;
364 }
365 }
366
367 uint8_t *lf_idx = memchr(state->input, 0x0a, state->input_len);
368
369 if (lf_idx == NULL) {
370 /* fragmented lines. Decoder event for special cases. Not all
371 * fragmented lines should be treated as a possible evasion
372 * attempt. With multi payload ftp chunks we can have valid
373 * cases of fragmentation. But within the same segment chunk
374 * if we see fragmentation then it's definitely something you
375 * should alert about */
376 if (line_state->current_line_db == 0) {
711b6fb3 377 line_state->db = FTPMalloc(state->input_len);
6ea8ac44
AS
378 if (line_state->db == NULL) {
379 return -1;
380 }
381 line_state->current_line_db = 1;
382 memcpy(line_state->db, state->input, state->input_len);
383 line_state->db_len = state->input_len;
384 } else {
711b6fb3 385 ptmp = FTPRealloc(line_state->db, line_state->db_len,
1f07d152
EL
386 (line_state->db_len + state->input_len));
387 if (ptmp == NULL) {
711b6fb3 388 FTPFree(line_state->db, line_state->db_len);
1f07d152
EL
389 line_state->db = NULL;
390 line_state->db_len = 0;
6ea8ac44
AS
391 return -1;
392 }
1f07d152
EL
393 line_state->db = ptmp;
394
6ea8ac44
AS
395 memcpy(line_state->db + line_state->db_len,
396 state->input, state->input_len);
397 line_state->db_len += state->input_len;
398 }
399 state->input += state->input_len;
400 state->input_len = 0;
401
402 return -1;
403
404 } else {
405 line_state->current_line_lf_seen = 1;
406
407 if (line_state->current_line_db == 1) {
711b6fb3 408 ptmp = FTPRealloc(line_state->db, line_state->db_len,
1f07d152
EL
409 (line_state->db_len + (lf_idx + 1 - state->input)));
410 if (ptmp == NULL) {
711b6fb3 411 FTPFree(line_state->db, line_state->db_len);
1f07d152
EL
412 line_state->db = NULL;
413 line_state->db_len = 0;
6ea8ac44
AS
414 return -1;
415 }
1f07d152
EL
416 line_state->db = ptmp;
417
6ea8ac44
AS
418 memcpy(line_state->db + line_state->db_len,
419 state->input, (lf_idx + 1 - state->input));
420 line_state->db_len += (lf_idx + 1 - state->input);
421
422 if (line_state->db_len > 1 &&
423 line_state->db[line_state->db_len - 2] == 0x0D) {
424 line_state->db_len -= 2;
425 state->current_line_delimiter_len = 2;
426 } else {
427 line_state->db_len -= 1;
428 state->current_line_delimiter_len = 1;
429 }
430
431 state->current_line = line_state->db;
432 state->current_line_len = line_state->db_len;
433
434 } else {
435 state->current_line = state->input;
436 state->current_line_len = lf_idx - state->input;
437
438 if (state->input != lf_idx &&
439 *(lf_idx - 1) == 0x0D) {
440 state->current_line_len--;
441 state->current_line_delimiter_len = 2;
442 } else {
443 state->current_line_delimiter_len = 1;
444 }
445 }
446
447 state->input_len -= (lf_idx - state->input) + 1;
448 state->input = (lf_idx + 1);
449
450 return 0;
451 }
452
453}
454
455static int FTPGetLine(FtpState *state)
456{
457 SCEnter();
458
459 /* we have run out of input */
460 if (state->input_len <= 0)
461 return -1;
462
463 /* toserver */
464 if (state->direction == 0)
465 return FTPGetLineForDirection(state, &state->line_state[0]);
466 else
467 return FTPGetLineForDirection(state, &state->line_state[1]);
468}
469
f2f9b832
PR
470/**
471 * \brief This function is called to determine and set which command is being
1930b1f5 472 * transferred to the ftp server
86fabef0 473 * \param thread context
f2f9b832
PR
474 * \param input input line of the command
475 * \param len of the command
1930b1f5 476 * \param cmd_descriptor when the command has been parsed
f2f9b832
PR
477 *
478 * \retval 1 when the command is parsed, 0 otherwise
479 */
86fabef0 480static int FTPParseRequestCommand(FTPThreadCtx *td,
579cc9f0 481 const uint8_t *input, uint32_t input_len,
09ab032a 482 const FtpCommand **cmd_descriptor)
8f1d7503 483{
f2f9b832 484 SCEnter();
11b9e6fd 485
09ab032a
JL
486 /* I don't like this pmq reset here. We'll devise a method later, that
487 * should make the use of the mpm very efficient */
488 PmqReset(td->pmq);
489 int mpm_cnt = mpm_table[FTP_MPM].Search(ftp_mpm_ctx, td->ftp_mpm_thread_ctx,
490 td->pmq, input, input_len);
491 if (mpm_cnt) {
492 *cmd_descriptor = &FtpCommands[td->pmq->rule_id_array[0]];
86fabef0 493 SCReturnInt(1);
b0a69344 494 }
09ab032a
JL
495
496 *cmd_descriptor = NULL;
86fabef0 497 SCReturnInt(0);
f2f9b832
PR
498}
499
b0a69344
EL
500struct FtpTransferCmd {
501 /** Need to look like a ExpectationData so DFree must
502 * be first field . */
503 void (*DFree)(void *);
504 uint64_t flow_id;
505 uint8_t *file_name;
506 uint16_t file_len;
507 FtpRequestCommand cmd;
508};
509
510static void FtpTransferCmdFree(void *data)
511{
512 struct FtpTransferCmd *cmd = (struct FtpTransferCmd *) data;
513 if (cmd == NULL)
514 return;
515 if (cmd->file_name) {
de983fb7 516 FTPFree(cmd->file_name, cmd->file_len);
b0a69344 517 }
711b6fb3 518 FTPFree(cmd, sizeof(struct FtpTransferCmd));
b0a69344
EL
519}
520
579cc9f0 521static uint32_t CopyCommandLine(uint8_t **dest, const uint8_t *src, uint32_t length)
1930b1f5
JL
522{
523 if (likely(length)) {
1588cd87 524 uint8_t *where = FTPCalloc(length + 1, sizeof(char));
1930b1f5
JL
525 if (unlikely(where == NULL)) {
526 return 0;
527 }
528 memcpy(where, src, length);
529
530 /* Remove trailing newlines/carriage returns */
1588cd87
ZK
531 while (length && isspace((unsigned char) where[length - 1])) {
532 length--;
1930b1f5 533 }
1588cd87
ZK
534
535 where[length] = '\0';
1930b1f5
JL
536 *dest = where;
537 }
538 /* either 0 or actual */
539 return length;
540}
541
4f33b8c1 542
f2f9b832
PR
543/**
544 * \brief This function is called to retrieve a ftp request
545 * \param ftp_state the ftp state structure for the parser
546 * \param input input line of the command
547 * \param input_len length of the request
548 * \param output the resulting output
549 *
550 * \retval 1 when the command is parsed, 0 otherwise
551 */
9a6aef45 552static int FTPParseRequest(Flow *f, void *ftp_state,
9634e60e 553 AppLayerParserState *pstate,
579cc9f0 554 const uint8_t *input, uint32_t input_len,
7bc3c3ac 555 void *local_data, const uint8_t flags)
9a6aef45 556{
09ab032a
JL
557 FTPThreadCtx *thread_data = local_data;
558
f2f9b832 559 SCEnter();
f2f9b832
PR
560 /* PrintRawDataFp(stdout, input,input_len); */
561
6ea8ac44 562 FtpState *state = (FtpState *)ftp_state;
1f07d152 563 void *ptmp;
6ea8ac44 564
4e7cb7b8
VJ
565 if (input == NULL && AppLayerParserStateIssetFlag(pstate, APP_LAYER_PARSER_EOF)) {
566 SCReturnInt(1);
f4f53924
VJ
567 } else if (input == NULL || input_len == 0) {
568 SCReturnInt(-1);
4e7cb7b8
VJ
569 }
570
6ea8ac44
AS
571 state->input = input;
572 state->input_len = input_len;
573 /* toserver stream */
574 state->direction = 0;
575
b0a69344 576 int direction = STREAM_TOSERVER;
6ea8ac44 577 while (FTPGetLine(state) >= 0) {
1930b1f5
JL
578 const FtpCommand *cmd_descriptor;
579
86fabef0 580 if (!FTPParseRequestCommand(thread_data, state->current_line, state->current_line_len, &cmd_descriptor)) {
1930b1f5
JL
581 state->command = FTP_COMMAND_UNKNOWN;
582 continue;
583 }
584
585 state->command = cmd_descriptor->command;
586
2149807b
JL
587 FTPTransaction *tx = FTPTransactionCreate(state);
588 if (unlikely(tx == NULL))
589 return -1;
590 state->curr_tx = tx;
1930b1f5
JL
591
592 tx->command_descriptor = cmd_descriptor;
593 tx->request_length = CopyCommandLine(&tx->request, state->current_line, state->current_line_len);
594
b0a69344 595 switch (state->command) {
4f33b8c1
JL
596 case FTP_COMMAND_EPRT:
597 // fallthrough
b0a69344 598 case FTP_COMMAND_PORT:
3ae2edb2 599 if (state->current_line_len + 1 > state->port_line_size) {
4f33b8c1 600 /* Allocate an extra byte for a NULL terminator */
711b6fb3 601 ptmp = FTPRealloc(state->port_line, state->port_line_size,
b4070b6d 602 state->current_line_len);
b0a69344 603 if (ptmp == NULL) {
4f33b8c1
JL
604 if (state->port_line) {
605 FTPFree(state->port_line, state->port_line_size);
606 state->port_line = NULL;
607 state->port_line_size = 0;
608 }
b0a69344
EL
609 return 0;
610 }
611 state->port_line = ptmp;
b4070b6d 612 state->port_line_size = state->current_line_len;
6ea8ac44 613 }
b0a69344
EL
614 memcpy(state->port_line, state->current_line,
615 state->current_line_len);
616 state->port_line_len = state->current_line_len;
617 break;
618 case FTP_COMMAND_RETR:
619 /* change direction (default to server) so expectation will handle
620 * the correct message when expectation will match.
621 */
622 direction = STREAM_TOCLIENT;
623 // fallthrough
624 case FTP_COMMAND_STOR:
625 {
354074ba
JL
626 /* Ensure that there is a negotiated dyn port and a file
627 * name -- need more than 5 chars: cmd [4], space, <filename>
628 */
629 if (state->dyn_port == 0 || state->current_line_len < 6) {
b0a69344
EL
630 SCReturnInt(-1);
631 }
711b6fb3 632 struct FtpTransferCmd *data = FTPCalloc(1, sizeof(struct FtpTransferCmd));
b0a69344
EL
633 if (data == NULL)
634 SCReturnInt(-1);
635 data->DFree = FtpTransferCmdFree;
636 /* Min size has been checked in FTPParseRequestCommand */
711b6fb3 637 data->file_name = FTPCalloc(state->current_line_len - 4, sizeof(char));
b0a69344 638 if (data->file_name == NULL) {
de983fb7 639 FtpTransferCmdFree(data);
b0a69344
EL
640 SCReturnInt(-1);
641 }
642 data->file_name[state->current_line_len - 5] = 0;
643 data->file_len = state->current_line_len - 5;
644 memcpy(data->file_name, state->current_line + 5, state->current_line_len - 5);
645 data->cmd = state->command;
646 data->flow_id = FlowGetId(f);
4f33b8c1
JL
647 int ret = AppLayerExpectationCreate(f,
648 state->active ? STREAM_TOSERVER : direction,
649 0, state->dyn_port, ALPROTO_FTPDATA, data);
b0a69344 650 if (ret == -1) {
de983fb7 651 FtpTransferCmdFree(data);
b0a69344
EL
652 SCLogDebug("No expectation created.");
653 SCReturnInt(-1);
654 } else {
4f33b8c1
JL
655 SCLogDebug("Expectation created [direction: %s, dynamic port %"PRIu16"].",
656 state->active ? "to server" : "to client",
657 state->dyn_port);
b0a69344 658 }
4f33b8c1 659
b0a69344
EL
660 /* reset the dyn port to avoid duplicate */
661 state->dyn_port = 0;
4f33b8c1
JL
662 /* reset active/passive indicator */
663 state->active = false;
b0a69344
EL
664 }
665 break;
666 default:
667 break;
6ea8ac44 668 }
f2f9b832 669 }
f2f9b832 670
f2f9b832
PR
671 return 1;
672}
673
579cc9f0 674static int FTPParsePassiveResponse(Flow *f, FtpState *state, const uint8_t *input, uint32_t input_len)
b0a69344 675{
f79316d7 676 uint16_t dyn_port = rs_ftp_pasv_response(input, input_len);
b0a69344
EL
677 if (dyn_port == 0) {
678 return -1;
679 }
4f33b8c1
JL
680 SCLogDebug("FTP passive mode (v4): dynamic port %"PRIu16"", dyn_port);
681 state->active = false;
b0a69344 682 state->dyn_port = dyn_port;
1930b1f5
JL
683 state->curr_tx->dyn_port = dyn_port;
684 state->curr_tx->active = false;
b0a69344
EL
685
686 return 0;
687}
688
579cc9f0 689static int FTPParsePassiveResponseV6(Flow *f, FtpState *state, const uint8_t *input, uint32_t input_len)
b0a69344 690{
f79316d7 691 uint16_t dyn_port = rs_ftp_epsv_response(input, input_len);
b0a69344
EL
692 if (dyn_port == 0) {
693 return -1;
694 }
4f33b8c1
JL
695 SCLogDebug("FTP passive mode (v6): dynamic port %"PRIu16"", dyn_port);
696 state->active = false;
b0a69344 697 state->dyn_port = dyn_port;
1930b1f5
JL
698 state->curr_tx->dyn_port = dyn_port;
699 state->curr_tx->active = false;
b0a69344
EL
700 return 0;
701}
702
1930b1f5
JL
703/**
704 * \brief Handle preliminary replies -- keep tx open
705 * \retval: True for a positive preliminary reply; false otherwise
706 *
707 * 1yz Positive Preliminary reply
708 *
709 * The requested action is being initiated; expect another
710 * reply before proceeding with a new command
711 */
579cc9f0 712static inline bool FTPIsPPR(const uint8_t *input, uint32_t input_len)
1930b1f5
JL
713{
714 return input_len >= 4 && isdigit(input[0]) && input[0] == '1' &&
715 isdigit(input[1]) && isdigit(input[2]) && isspace(input[3]);
716}
717
f2f9b832
PR
718/**
719 * \brief This function is called to retrieve a ftp response
720 * \param ftp_state the ftp state structure for the parser
721 * \param input input line of the command
722 * \param input_len length of the request
723 * \param output the resulting output
724 *
725 * \retval 1 when the command is parsed, 0 otherwise
726 */
9634e60e 727static int FTPParseResponse(Flow *f, void *ftp_state, AppLayerParserState *pstate,
579cc9f0 728 const uint8_t *input, uint32_t input_len,
7bc3c3ac 729 void *local_data, const uint8_t flags)
9a6aef45 730{
11b9e6fd 731 FtpState *state = (FtpState *)ftp_state;
1930b1f5
JL
732 int retcode = 1;
733
b595da6c
VJ
734 if (unlikely(input_len == 0)) {
735 return 1;
736 }
a04b1c16 737
b595da6c
VJ
738 FTPTransaction *tx = FTPGetOldestTx(state);
739 if (tx == NULL) {
740 tx = FTPTransactionCreate(state);
741 }
742 if (unlikely(tx == NULL)) {
743 return -1;
744 }
745 if (state->command == FTP_COMMAND_UNKNOWN || tx->command_descriptor == NULL) {
a04b1c16
JL
746 /* unknown */
747 tx->command_descriptor = &FtpCommands[FTP_COMMAND_MAX -1];
1930b1f5 748 }
11b9e6fd 749
fb019213 750 state->curr_tx = tx;
11b9e6fd
MK
751 if (state->command == FTP_COMMAND_AUTH_TLS) {
752 if (input_len >= 4 && SCMemcmp("234 ", input, 4) == 0) {
6f42ae91 753 AppLayerRequestProtocolTLSUpgrade(f);
11b9e6fd
MK
754 }
755 }
756
4f33b8c1 757 if (state->command == FTP_COMMAND_EPRT) {
b4070b6d 758 uint16_t dyn_port = rs_ftp_active_eprt(state->port_line, state->port_line_len);
4f33b8c1 759 if (dyn_port == 0) {
1930b1f5
JL
760 retcode = 0;
761 goto tx_complete;
4f33b8c1
JL
762 }
763 state->dyn_port = dyn_port;
764 state->active = true;
1930b1f5
JL
765 tx->dyn_port = dyn_port;
766 tx->active = true;
4f33b8c1
JL
767 SCLogDebug("FTP active mode (v6): dynamic port %"PRIu16"", dyn_port);
768 }
769
770 if (state->command == FTP_COMMAND_PORT) {
771 if ((flags & STREAM_TOCLIENT)) {
b4070b6d 772 uint16_t dyn_port = rs_ftp_active_port(state->port_line, state->port_line_len);
4f33b8c1 773 if (dyn_port == 0) {
1930b1f5
JL
774 retcode = 0;
775 goto tx_complete;
4f33b8c1
JL
776 }
777 state->dyn_port = dyn_port;
778 state->active = true;
1930b1f5
JL
779 tx->dyn_port = state->dyn_port;
780 tx->active = true;
4f33b8c1
JL
781 SCLogDebug("FTP active mode (v4): dynamic port %"PRIu16"", dyn_port);
782 }
783 }
1930b1f5 784
b0a69344
EL
785 if (state->command == FTP_COMMAND_PASV) {
786 if (input_len >= 4 && SCMemcmp("227 ", input, 4) == 0) {
787 FTPParsePassiveResponse(f, ftp_state, input, input_len);
788 }
789 }
790
791 if (state->command == FTP_COMMAND_EPSV) {
792 if (input_len >= 4 && SCMemcmp("229 ", input, 4) == 0) {
793 FTPParsePassiveResponseV6(f, ftp_state, input, input_len);
794 }
795 }
796
1930b1f5
JL
797 if (likely(input_len)) {
798 FTPString *response = FTPStringAlloc();
799 if (likely(response)) {
800 response->len = CopyCommandLine(&response->str, input, input_len);
2149807b 801 TAILQ_INSERT_TAIL(&tx->response_list, response, next);
1930b1f5
JL
802 }
803 }
804
911d423a
JL
805 /* Handle preliminary replies -- keep tx open */
806 if (FTPIsPPR(input, input_len)) {
343ba459
VJ
807 return retcode;
808 }
809
1930b1f5
JL
810tx_complete:
811 tx->done = true;
812 return retcode;
f2f9b832
PR
813}
814
911d423a 815
f2f9b832 816#ifdef DEBUG
5532af46 817static SCMutex ftp_state_mem_lock = SCMUTEX_INITIALIZER;
f2f9b832
PR
818static uint64_t ftp_state_memuse = 0;
819static uint64_t ftp_state_memcnt = 0;
820#endif
821
8f1d7503
KS
822static void *FTPStateAlloc(void)
823{
1930b1f5 824 void *s = FTPCalloc(1, sizeof(FtpState));
e176be6f 825 if (unlikely(s == NULL))
f2f9b832
PR
826 return NULL;
827
1930b1f5
JL
828 FtpState *ftp_state = (FtpState *) s;
829 TAILQ_INIT(&ftp_state->tx_list);
f2f9b832
PR
830
831#ifdef DEBUG
832 SCMutexLock(&ftp_state_mem_lock);
833 ftp_state_memcnt++;
834 ftp_state_memuse+=sizeof(FtpState);
835 SCMutexUnlock(&ftp_state_mem_lock);
836#endif
837 return s;
838}
839
8f1d7503
KS
840static void FTPStateFree(void *s)
841{
18954a2c
PR
842 FtpState *fstate = (FtpState *) s;
843 if (fstate->port_line != NULL)
711b6fb3 844 FTPFree(fstate->port_line, fstate->port_line_size);
b4ab9a0a 845 if (fstate->line_state[0].db)
711b6fb3 846 FTPFree(fstate->line_state[0].db, fstate->line_state[0].db_len);
b4ab9a0a 847 if (fstate->line_state[1].db)
711b6fb3 848 FTPFree(fstate->line_state[1].db, fstate->line_state[1].db_len);
402eb645
VJ
849
850 //AppLayerDecoderEventsFreeEvents(&s->decoder_events);
851
1930b1f5
JL
852 FTPTransaction *tx = NULL;
853 while ((tx = TAILQ_FIRST(&fstate->tx_list))) {
854 TAILQ_REMOVE(&fstate->tx_list, tx, next);
2149807b 855 SCLogDebug("[%s] state %p id %"PRIu64", Freeing %d bytes at %p",
09ab032a 856 tx->command_descriptor->command_name,
2149807b
JL
857 s, tx->tx_id,
858 tx->request_length, tx->request);
1930b1f5 859 FTPTransactionFree(tx);
402eb645
VJ
860 }
861
711b6fb3 862 FTPFree(s, sizeof(FtpState));
f2f9b832
PR
863#ifdef DEBUG
864 SCMutexLock(&ftp_state_mem_lock);
865 ftp_state_memcnt--;
866 ftp_state_memuse-=sizeof(FtpState);
867 SCMutexUnlock(&ftp_state_mem_lock);
868#endif
869}
870
7548944b 871static int FTPSetTxDetectState(void *vtx, DetectEngineState *de_state)
402eb645 872{
1930b1f5
JL
873 FTPTransaction *tx = (FTPTransaction *)vtx;
874 tx->de_state = de_state;
402eb645
VJ
875 return 0;
876}
877
2149807b
JL
878/**
879 * \brief This function returns the oldest open transaction; if none
880 * are open, then the oldest transaction is returned
881 * \param ftp_state the ftp state structure for the parser
882 *
883 * \retval transaction pointer when a transaction was found; NULL otherwise.
884 */
8ae69115 885static FTPTransaction *FTPGetOldestTx(FtpState *ftp_state)
2149807b
JL
886{
887 if (unlikely(!ftp_state)) {
888 SCLogDebug("NULL state object; no transactions available");
889 return NULL;
890 }
891 FTPTransaction *tx = NULL;
892 FTPTransaction *lasttx = NULL;
893 TAILQ_FOREACH(tx, &ftp_state->tx_list, next) {
894 /* Return oldest open tx */
895 if (!tx->done) {
896 SCLogDebug("Returning tx %p id %"PRIu64, tx, tx->tx_id);
897 return tx;
898 }
899 /* save for the end */
900 lasttx = tx;
901 }
902 /* All tx are closed; return last element */
903 if (lasttx)
904 SCLogDebug("Returning OLDEST tx %p id %"PRIu64, lasttx, lasttx->tx_id);
905 return lasttx;
906}
907
1930b1f5
JL
908static void *FTPGetTx(void *state, uint64_t tx_id)
909{
910 FtpState *ftp_state = (FtpState *)state;
911 if (ftp_state) {
912 FTPTransaction *tx = NULL;
913
914 if (ftp_state->curr_tx == NULL)
915 return NULL;
916 if (ftp_state->curr_tx->tx_id == tx_id)
917 return ftp_state->curr_tx;
918
919 TAILQ_FOREACH(tx, &ftp_state->tx_list, next) {
920 if (tx->tx_id == tx_id)
921 return tx;
922 }
923 }
924 return NULL;
1930b1f5
JL
925}
926
402eb645
VJ
927static DetectEngineState *FTPGetTxDetectState(void *vtx)
928{
1930b1f5
JL
929 FTPTransaction *tx = (FTPTransaction *)vtx;
930 return tx->de_state;
402eb645
VJ
931}
932
1930b1f5
JL
933
934static uint64_t FTPGetTxDetectFlags(void *vtx, uint8_t dir)
402eb645 935{
1930b1f5
JL
936 FTPTransaction *tx = (FTPTransaction *)vtx;
937 if (dir & STREAM_TOSERVER) {
938 return tx->detect_flags_ts;
939 } else {
940 return tx->detect_flags_tc;
941 }
402eb645
VJ
942}
943
1930b1f5 944static void FTPSetTxDetectFlags(void *vtx, uint8_t dir, uint64_t flags)
402eb645 945{
1930b1f5
JL
946 FTPTransaction *tx = (FTPTransaction *)vtx;
947 if (dir & STREAM_TOSERVER) {
948 tx->detect_flags_ts = flags;
949 } else {
950 tx->detect_flags_tc = flags;
951 }
952}
953
954static void FTPStateTransactionFree(void *state, uint64_t tx_id)
955{
956 FtpState *ftp_state = state;
957 FTPTransaction *tx = NULL;
958 TAILQ_FOREACH(tx, &ftp_state->tx_list, next) {
959 if (tx_id < tx->tx_id)
960 break;
961 else if (tx_id > tx->tx_id)
962 continue;
963
964 if (tx == ftp_state->curr_tx)
965 ftp_state->curr_tx = NULL;
966 TAILQ_REMOVE(&ftp_state->tx_list, tx, next);
967 FTPTransactionFree(tx);
968 break;
969 }
402eb645
VJ
970}
971
972static uint64_t FTPGetTxCnt(void *state)
973{
1930b1f5
JL
974 uint64_t cnt = 0;
975 FtpState *ftp_state = state;
976 if (ftp_state) {
977 cnt = ftp_state->tx_cnt;
978 }
979 SCLogDebug("returning state %p %"PRIu64, state, cnt);
980 return cnt;
402eb645
VJ
981}
982
983static int FTPGetAlstateProgressCompletionStatus(uint8_t direction)
984{
985 return FTP_STATE_FINISHED;
986}
987
1930b1f5 988static int FTPGetAlstateProgress(void *vtx, uint8_t direction)
402eb645 989{
1930b1f5
JL
990 SCLogDebug("tx %p", vtx);
991 FTPTransaction *tx = vtx;
402eb645
VJ
992
993 if (direction == STREAM_TOSERVER &&
1930b1f5 994 tx->command_descriptor->command == FTP_COMMAND_PORT) {
402eb645
VJ
995 return FTP_STATE_PORT_DONE;
996 }
997
dc80d520
VJ
998 if (!tx->done)
999 return FTP_STATE_IN_PROGRESS;
402eb645 1000
dc80d520 1001 return FTP_STATE_FINISHED;
402eb645
VJ
1002}
1003
1004
429c6388
AS
1005static int FTPRegisterPatternsForProtocolDetection(void)
1006{
8125f78f
MK
1007 if (AppLayerProtoDetectPMRegisterPatternCI(IPPROTO_TCP, ALPROTO_FTP,
1008 "220 (", 5, 0, STREAM_TOCLIENT) < 0)
1009 {
1010 return -1;
1011 }
1012 if (AppLayerProtoDetectPMRegisterPatternCI(IPPROTO_TCP, ALPROTO_FTP,
1013 "FEAT", 4, 0, STREAM_TOSERVER) < 0)
1014 {
1015 return -1;
1016 }
429c6388
AS
1017 if (AppLayerProtoDetectPMRegisterPatternCI(IPPROTO_TCP, ALPROTO_FTP,
1018 "USER ", 5, 0, STREAM_TOSERVER) < 0)
1019 {
1020 return -1;
1021 }
1022 if (AppLayerProtoDetectPMRegisterPatternCI(IPPROTO_TCP, ALPROTO_FTP,
1023 "PASS ", 5, 0, STREAM_TOSERVER) < 0)
1024 {
1025 return -1;
1026 }
1027 if (AppLayerProtoDetectPMRegisterPatternCI(IPPROTO_TCP, ALPROTO_FTP,
1028 "PORT ", 5, 0, STREAM_TOSERVER) < 0)
1029 {
1030 return -1;
1031 }
1032
1033 return 0;
1034}
1035
b0a69344
EL
1036
1037static StreamingBufferConfig sbcfg = STREAMING_BUFFER_CONFIG_INITIALIZER;
1038
1039/**
1040 * \brief This function is called to retrieve a ftp request
1041 * \param ftp_state the ftp state structure for the parser
1042 * \param input input line of the command
1043 * \param input_len length of the request
1044 * \param output the resulting output
1045 *
1046 * \retval 1 when the command is parsed, 0 otherwise
1047 */
1048static int FTPDataParse(Flow *f, FtpDataState *ftpdata_state,
1049 AppLayerParserState *pstate,
579cc9f0 1050 const uint8_t *input, uint32_t input_len,
b0a69344
EL
1051 void *local_data, int direction)
1052{
1053 uint16_t flags = FileFlowToFlags(f, direction);
1054 int ret = 0;
1055 /* we depend on detection engine for file pruning */
1056 flags |= FILE_USE_DETECT;
1057 if (ftpdata_state->files == NULL) {
1058 struct FtpTransferCmd *data = (struct FtpTransferCmd *)FlowGetStorageById(f, AppLayerExpectationGetDataId());
1059 if (data == NULL) {
1060 SCReturnInt(-1);
1061 }
1062
1063 ftpdata_state->files = FileContainerAlloc();
1064 if (ftpdata_state->files == NULL) {
1065 FlowFreeStorageById(f, AppLayerExpectationGetDataId());
1066 SCReturnInt(-1);
1067 }
1068
1069 ftpdata_state->file_name = data->file_name;
1070 ftpdata_state->file_len = data->file_len;
1071 data->file_name = NULL;
1072 data->file_len = 0;
1073 f->parent_id = data->flow_id;
1074 ftpdata_state->command = data->cmd;
2515c892
EL
1075 switch (data->cmd) {
1076 case FTP_COMMAND_STOR:
1077 ftpdata_state->direction = STREAM_TOSERVER;
1078 break;
1079 case FTP_COMMAND_RETR:
1080 ftpdata_state->direction = STREAM_TOCLIENT;
1081 break;
1082 default:
1083 break;
1084 }
b0a69344 1085
9132e403
VJ
1086 /* open with fixed track_id 0 as we can have just one
1087 * file per ftp-data flow. */
1088 if (FileOpenFileWithId(ftpdata_state->files, &sbcfg,
1089 0ULL, (uint8_t *) ftpdata_state->file_name,
b0a69344 1090 ftpdata_state->file_len,
9132e403 1091 input, input_len, flags) != 0) {
b0a69344
EL
1092 SCLogDebug("Can't open file");
1093 ret = -1;
1094 }
1095 FlowFreeStorageById(f, AppLayerExpectationGetDataId());
1096 } else {
1097 if (input_len != 0) {
1098 ret = FileAppendData(ftpdata_state->files, input, input_len);
1099 if (ret == -2) {
1100 ret = 0;
1101 SCLogDebug("FileAppendData() - file no longer being extracted");
1102 goto out;
1103 } else if (ret < 0) {
1104 SCLogDebug("FileAppendData() failed: %d", ret);
1105 ret = -2;
1106 goto out;
1107 }
1108 } else {
1109 ret = FileCloseFile(ftpdata_state->files, NULL, 0, flags);
1110 ftpdata_state->state = FTPDATA_STATE_FINISHED;
1111 if (ret < 0)
1112 goto out;
1113 }
1114 }
1115
1116 if (input_len && AppLayerParserStateIssetFlag(pstate, APP_LAYER_PARSER_EOF)) {
1117 ret = FileCloseFile(ftpdata_state->files, (uint8_t *) NULL, 0, flags);
1118 ftpdata_state->state = FTPDATA_STATE_FINISHED;
1119 }
1120
1121out:
b0a69344
EL
1122 return ret;
1123}
1124
1930b1f5
JL
1125static void FTPStateSetTxLogged(void *state, void *vtx, LoggerId logged)
1126{
1127 FTPTransaction *tx = vtx;
1128 tx->logged = logged;
1129}
1130
1131static LoggerId FTPStateGetTxLogged(void *state, void *vtx)
1132{
1133 FTPTransaction *tx = vtx;
1134 return tx->logged;
1135}
b0a69344
EL
1136static int FTPDataParseRequest(Flow *f, void *ftp_state,
1137 AppLayerParserState *pstate,
579cc9f0 1138 const uint8_t *input, uint32_t input_len,
7bc3c3ac 1139 void *local_data, const uint8_t flags)
b0a69344
EL
1140{
1141 return FTPDataParse(f, ftp_state, pstate, input, input_len,
1142 local_data, STREAM_TOSERVER);
1143}
1144
1145static int FTPDataParseResponse(Flow *f, void *ftp_state,
1146 AppLayerParserState *pstate,
579cc9f0 1147 const uint8_t *input, uint32_t input_len,
7bc3c3ac 1148 void *local_data, const uint8_t flags)
b0a69344
EL
1149{
1150 return FTPDataParse(f, ftp_state, pstate, input, input_len,
1151 local_data, STREAM_TOCLIENT);
1152}
1153
1154#ifdef DEBUG
1155static SCMutex ftpdata_state_mem_lock = SCMUTEX_INITIALIZER;
1156static uint64_t ftpdata_state_memuse = 0;
1157static uint64_t ftpdata_state_memcnt = 0;
1158#endif
1159
1160static void *FTPDataStateAlloc(void)
1161{
1930b1f5 1162 void *s = FTPCalloc(1, sizeof(FtpDataState));
b0a69344
EL
1163 if (unlikely(s == NULL))
1164 return NULL;
1165
1930b1f5
JL
1166 FtpDataState *state = (FtpDataState *) s;
1167 state->state = FTPDATA_STATE_IN_PROGRESS;
b0a69344
EL
1168
1169#ifdef DEBUG
1170 SCMutexLock(&ftpdata_state_mem_lock);
1171 ftpdata_state_memcnt++;
1172 ftpdata_state_memuse+=sizeof(FtpDataState);
1173 SCMutexUnlock(&ftpdata_state_mem_lock);
1174#endif
1175 return s;
1176}
1177
1178static void FTPDataStateFree(void *s)
1179{
1180 FtpDataState *fstate = (FtpDataState *) s;
1181
1182 if (fstate->de_state != NULL) {
1183 DetectEngineStateFree(fstate->de_state);
1184 }
1185 if (fstate->file_name != NULL) {
711b6fb3 1186 FTPFree(fstate->file_name, fstate->file_len);
b0a69344
EL
1187 }
1188
1189 FileContainerFree(fstate->files);
1190
1191 SCFree(s);
1192#ifdef DEBUG
1193 SCMutexLock(&ftpdata_state_mem_lock);
1194 ftpdata_state_memcnt--;
1195 ftpdata_state_memuse-=sizeof(FtpDataState);
1196 SCMutexUnlock(&ftpdata_state_mem_lock);
1197#endif
1198}
1199
7548944b 1200static int FTPDataSetTxDetectState(void *vtx, DetectEngineState *de_state)
b0a69344 1201{
7548944b 1202 FtpDataState *ftp_state = (FtpDataState *)vtx;
b0a69344
EL
1203 ftp_state->de_state = de_state;
1204 return 0;
1205}
1206
1207static DetectEngineState *FTPDataGetTxDetectState(void *vtx)
1208{
1209 FtpDataState *ftp_state = (FtpDataState *)vtx;
1210 return ftp_state->de_state;
1211}
1212
b1beb76f
JI
1213static void FTPDataSetTxDetectFlags(void *vtx, uint8_t dir, uint64_t flags)
1214{
1215 FtpDataState *ftp_state = (FtpDataState *)vtx;
1216 if (dir & STREAM_TOSERVER) {
1217 ftp_state->detect_flags_ts = flags;
1218 } else {
1219 ftp_state->detect_flags_tc = flags;
1220 }
1221}
1222
1223static uint64_t FTPDataGetTxDetectFlags(void *vtx, uint8_t dir)
1224{
1225 FtpDataState *ftp_state = (FtpDataState *)vtx;
1226 if (dir & STREAM_TOSERVER) {
1227 return ftp_state->detect_flags_ts;
1228 } else {
1229 return ftp_state->detect_flags_tc;
1230 }
1231}
1232
b0a69344
EL
1233static void FTPDataStateTransactionFree(void *state, uint64_t tx_id)
1234{
1235 /* do nothing */
1236}
1237
1238static void *FTPDataGetTx(void *state, uint64_t tx_id)
1239{
1240 FtpDataState *ftp_state = (FtpDataState *)state;
1241 return ftp_state;
1242}
1243
1244static uint64_t FTPDataGetTxCnt(void *state)
1245{
1246 /* ftp-data is single tx */
1247 return 1;
1248}
1249
1250static int FTPDataGetAlstateProgressCompletionStatus(uint8_t direction)
1251{
1252 return FTPDATA_STATE_FINISHED;
1253}
1254
1255static int FTPDataGetAlstateProgress(void *tx, uint8_t direction)
1256{
1257 FtpDataState *ftpdata_state = (FtpDataState *)tx;
1258 return ftpdata_state->state;
1259}
1260
1261static FileContainer *FTPDataStateGetFiles(void *state, uint8_t direction)
1262{
1263 FtpDataState *ftpdata_state = (FtpDataState *)state;
1264
2515c892
EL
1265 if (direction != ftpdata_state->direction)
1266 SCReturnPtr(NULL, "FileContainer");
1267
b0a69344
EL
1268 SCReturnPtr(ftpdata_state->files, "FileContainer");
1269}
1270
09ab032a
JL
1271static void FTPSetMpmState(void)
1272{
1273 ftp_mpm_ctx = SCMalloc(sizeof(MpmCtx));
1274 if (unlikely(ftp_mpm_ctx == NULL)) {
1275 exit(EXIT_FAILURE);
1276 }
1277 memset(ftp_mpm_ctx, 0, sizeof(MpmCtx));
1278 MpmInitCtx(ftp_mpm_ctx, FTP_MPM);
1279
1280 uint32_t i = 0;
1281 for (i = 0; i < sizeof(FtpCommands)/sizeof(FtpCommand) - 1; i++) {
1282 const FtpCommand *cmd = &FtpCommands[i];
86deaefe
JL
1283 if (cmd->command_length == 0)
1284 continue;
1285
09ab032a
JL
1286 MpmAddPatternCI(ftp_mpm_ctx,
1287 (uint8_t *)cmd->command_name,
1288 cmd->command_length,
1289 0 /* defunct */, 0 /* defunct */,
1290 i /* id */, i /* rule id */ , 0 /* no flags */);
1291 }
1292
1293 mpm_table[FTP_MPM].Prepare(ftp_mpm_ctx);
1294
1295}
1296
1297static void FTPFreeMpmState(void)
1298{
1299 if (ftp_mpm_ctx != NULL) {
1300 mpm_table[FTP_MPM].DestroyCtx(ftp_mpm_ctx);
1301 SCFree(ftp_mpm_ctx);
1302 ftp_mpm_ctx = NULL;
1303 }
1304}
1305
8f1d7503
KS
1306void RegisterFTPParsers(void)
1307{
ab1200fb 1308 const char *proto_name = "ftp";
b0a69344 1309 const char *proto_data_name = "ftp-data";
10966245 1310
000ce98c 1311 /** FTP */
429c6388
AS
1312 if (AppLayerProtoDetectConfProtoDetectionEnabled("tcp", proto_name)) {
1313 AppLayerProtoDetectRegisterProtocol(ALPROTO_FTP, proto_name);
1314 if (FTPRegisterPatternsForProtocolDetection() < 0 )
1315 return;
b0a69344 1316 AppLayerProtoDetectRegisterProtocol(ALPROTO_FTPDATA, proto_data_name);
ddde572f
AS
1317 }
1318
429c6388
AS
1319 if (AppLayerParserConfParserEnabled("tcp", proto_name)) {
1320 AppLayerParserRegisterParser(IPPROTO_TCP, ALPROTO_FTP, STREAM_TOSERVER,
1321 FTPParseRequest);
1322 AppLayerParserRegisterParser(IPPROTO_TCP, ALPROTO_FTP, STREAM_TOCLIENT,
1323 FTPParseResponse);
1324 AppLayerParserRegisterStateFuncs(IPPROTO_TCP, ALPROTO_FTP, FTPStateAlloc, FTPStateFree);
1325 AppLayerParserRegisterParserAcceptableDataDirection(IPPROTO_TCP, ALPROTO_FTP, STREAM_TOSERVER | STREAM_TOCLIENT);
402eb645
VJ
1326
1327 AppLayerParserRegisterTxFreeFunc(IPPROTO_TCP, ALPROTO_FTP, FTPStateTransactionFree);
1328
7548944b
VJ
1329 AppLayerParserRegisterDetectStateFuncs(IPPROTO_TCP, ALPROTO_FTP,
1330 FTPGetTxDetectState, FTPSetTxDetectState);
402eb645 1331
1930b1f5
JL
1332 AppLayerParserRegisterDetectFlagsFuncs(IPPROTO_TCP, ALPROTO_FTP,
1333 FTPGetTxDetectFlags, FTPSetTxDetectFlags);
1334
402eb645 1335 AppLayerParserRegisterGetTx(IPPROTO_TCP, ALPROTO_FTP, FTPGetTx);
1930b1f5
JL
1336 AppLayerParserRegisterLoggerFuncs(IPPROTO_TCP, ALPROTO_FTP, FTPStateGetTxLogged,
1337 FTPStateSetTxLogged);
402eb645 1338
09ab032a
JL
1339 AppLayerParserRegisterLocalStorageFunc(IPPROTO_TCP, ALPROTO_FTP, FTPLocalStorageAlloc,
1340 FTPLocalStorageFree);
402eb645
VJ
1341 AppLayerParserRegisterGetTxCnt(IPPROTO_TCP, ALPROTO_FTP, FTPGetTxCnt);
1342
1343 AppLayerParserRegisterGetStateProgressFunc(IPPROTO_TCP, ALPROTO_FTP, FTPGetAlstateProgress);
1344
1345 AppLayerParserRegisterGetStateProgressCompletionStatus(ALPROTO_FTP,
1346 FTPGetAlstateProgressCompletionStatus);
b0a69344
EL
1347
1348
1349 AppLayerRegisterExpectationProto(IPPROTO_TCP, ALPROTO_FTPDATA);
1350 AppLayerParserRegisterParser(IPPROTO_TCP, ALPROTO_FTPDATA, STREAM_TOSERVER,
1351 FTPDataParseRequest);
1352 AppLayerParserRegisterParser(IPPROTO_TCP, ALPROTO_FTPDATA, STREAM_TOCLIENT,
1353 FTPDataParseResponse);
1354 AppLayerParserRegisterStateFuncs(IPPROTO_TCP, ALPROTO_FTPDATA, FTPDataStateAlloc, FTPDataStateFree);
1355 AppLayerParserRegisterParserAcceptableDataDirection(IPPROTO_TCP, ALPROTO_FTPDATA, STREAM_TOSERVER | STREAM_TOCLIENT);
1356 AppLayerParserRegisterTxFreeFunc(IPPROTO_TCP, ALPROTO_FTPDATA, FTPDataStateTransactionFree);
7548944b 1357 AppLayerParserRegisterDetectStateFuncs(IPPROTO_TCP, ALPROTO_FTPDATA,
b0a69344 1358 FTPDataGetTxDetectState, FTPDataSetTxDetectState);
b1beb76f
JI
1359 AppLayerParserRegisterDetectFlagsFuncs(IPPROTO_TCP, ALPROTO_FTPDATA,
1360 FTPDataGetTxDetectFlags, FTPDataSetTxDetectFlags);
b0a69344
EL
1361
1362 AppLayerParserRegisterGetFilesFunc(IPPROTO_TCP, ALPROTO_FTPDATA, FTPDataStateGetFiles);
1363
1364 AppLayerParserRegisterGetTx(IPPROTO_TCP, ALPROTO_FTPDATA, FTPDataGetTx);
1365
1366 AppLayerParserRegisterGetTxCnt(IPPROTO_TCP, ALPROTO_FTPDATA, FTPDataGetTxCnt);
1367
1368 AppLayerParserRegisterGetStateProgressFunc(IPPROTO_TCP, ALPROTO_FTPDATA, FTPDataGetAlstateProgress);
1369
1370 AppLayerParserRegisterGetStateProgressCompletionStatus(ALPROTO_FTPDATA,
1371 FTPDataGetAlstateProgressCompletionStatus);
1372
1373 sbcfg.buf_size = 4096;
711b6fb3
EL
1374 sbcfg.Malloc = FTPMalloc;
1375 sbcfg.Calloc = FTPCalloc;
1376 sbcfg.Realloc = FTPRealloc;
1377 sbcfg.Free = FTPFree;
b0a69344 1378
711b6fb3 1379 FTPParseMemcap();
ddde572f
AS
1380 } else {
1381 SCLogInfo("Parsed disabled for %s protocol. Protocol detection"
1382 "still on.", proto_name);
1383 }
09ab032a
JL
1384
1385 FTPSetMpmState();
1386
9faa4b74 1387#ifdef UNITTESTS
429c6388 1388 AppLayerParserRegisterProtocolUnittests(IPPROTO_TCP, ALPROTO_FTP, FTPParserRegisterTests);
9faa4b74 1389#endif
f2f9b832
PR
1390}
1391
8f1d7503
KS
1392void FTPAtExitPrintStats(void)
1393{
f2f9b832
PR
1394#ifdef DEBUG
1395 SCMutexLock(&ftp_state_mem_lock);
1396 SCLogDebug("ftp_state_memcnt %"PRIu64", ftp_state_memuse %"PRIu64"",
1397 ftp_state_memcnt, ftp_state_memuse);
1398 SCMutexUnlock(&ftp_state_mem_lock);
1399#endif
1400}
1401
b0a69344 1402
3df2b343
JL
1403/*
1404 * \brief Returns the ending offset of the next line from a multi-line buffer.
1405 *
1406 * "Buffer" refers to a FTP response in a single buffer containing multiple lines.
1407 * Here, "next line" is defined as terminating on
1408 * - Newline character
1409 * - Null character
1410 *
1411 * \param buffer Contains zero or more characters.
1412 * \param len Size, in bytes, of buffer.
1413 *
1414 * \retval Offset from the start of buffer indicating the where the
1415 * next "line ends". The characters between the input buffer and this
1416 * value comprise the line.
1417 *
86fabef0 1418 * NULL is found first or a newline isn't found, then UINT16_MAX is returned.
3df2b343
JL
1419 */
1420uint16_t JsonGetNextLineFromBuffer(const char *buffer, const uint16_t len)
1421{
86fabef0
JL
1422 if (!buffer || *buffer == '\0') {
1423 return UINT16_MAX;
1424 }
3df2b343 1425
86fabef0
JL
1426 char *c = strchr(buffer, '\n');
1427 return c == NULL ? len : c - buffer + 1;
3df2b343
JL
1428}
1429
b0a69344
EL
1430json_t *JsonFTPDataAddMetadata(const Flow *f)
1431{
1432 const FtpDataState *ftp_state = NULL;
1433 if (f->alstate == NULL)
1434 return NULL;
1435 ftp_state = (FtpDataState *)f->alstate;
1436 json_t *ftpd = json_object();
1437 if (ftpd == NULL)
1438 return NULL;
1439 if (ftp_state->file_name) {
7e1235c9
VJ
1440 size_t size = ftp_state->file_len * 2 + 1;
1441 char string[size];
1442 BytesToStringBuffer(ftp_state->file_name, ftp_state->file_len, string, size);
1443 json_object_set_new(ftpd, "filename", SCJsonString(string));
b0a69344
EL
1444 }
1445 switch (ftp_state->command) {
1446 case FTP_COMMAND_STOR:
1447 json_object_set_new(ftpd, "command", json_string("STOR"));
1448 break;
1449 case FTP_COMMAND_RETR:
1450 json_object_set_new(ftpd, "command", json_string("RETR"));
1451 break;
1452 default:
1453 break;
1454 }
1455 return ftpd;
1456}
b0a69344 1457
09ab032a
JL
1458/**
1459 * \brief Free memory allocated for global SMTP parser state.
1460 */
1461void FTPParserCleanup(void)
1462{
1463 FTPFreeMpmState();
1464}
1465
f2f9b832
PR
1466/* UNITTESTS */
1467#ifdef UNITTESTS
1468
1469/** \test Send a get request in one chunk. */
ab1200fb 1470static int FTPParserTest01(void)
8f1d7503 1471{
f2f9b832
PR
1472 int result = 1;
1473 Flow f;
1474 uint8_t ftpbuf[] = "PORT 192,168,1,1,0,80\r\n";
1475 uint32_t ftplen = sizeof(ftpbuf) - 1; /* minus the \0 */
1476 TcpSession ssn;
8dbf7a0d 1477 AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
f2f9b832
PR
1478
1479 memset(&f, 0, sizeof(f));
1480 memset(&ssn, 0, sizeof(ssn));
cc76aa4b
WM
1481
1482 FLOW_INITIALIZE(&f);
f2f9b832 1483 f.protoctx = (void *)&ssn;
429c6388 1484 f.proto = IPPROTO_TCP;
5c01b409 1485 f.alproto = ALPROTO_FTP;
f2f9b832 1486
6a53ab9c 1487 StreamTcpInitConfig(TRUE);
6a53ab9c 1488
6530c3d0 1489 FLOWLOCK_WRLOCK(&f);
675fa564
GL
1490 int r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_FTP,
1491 STREAM_TOSERVER | STREAM_EOF, ftpbuf, ftplen);
f2f9b832 1492 if (r != 0) {
f729d6f7 1493 SCLogDebug("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
f2f9b832 1494 result = 0;
6530c3d0 1495 FLOWLOCK_UNLOCK(&f);
f2f9b832
PR
1496 goto end;
1497 }
6530c3d0 1498 FLOWLOCK_UNLOCK(&f);
f2f9b832 1499
06904c90 1500 FtpState *ftp_state = f.alstate;
f2f9b832 1501 if (ftp_state == NULL) {
f729d6f7 1502 SCLogDebug("no ftp state: ");
f2f9b832
PR
1503 result = 0;
1504 goto end;
1505 }
1506
1507 if (ftp_state->command != FTP_COMMAND_PORT) {
f729d6f7 1508 SCLogDebug("expected command %" PRIu32 ", got %" PRIu32 ": ", FTP_COMMAND_PORT, ftp_state->command);
f2f9b832
PR
1509 result = 0;
1510 goto end;
1511 }
1512
1513end:
429c6388 1514 if (alp_tctx != NULL)
fdefb65b 1515 AppLayerParserThreadCtxFree(alp_tctx);
6a53ab9c 1516 StreamTcpFreeConfig(TRUE);
0e4235cc 1517 FLOW_DESTROY(&f);
f2f9b832
PR
1518 return result;
1519}
1520
1930b1f5 1521/** \test Send a split get request. */
ab1200fb 1522static int FTPParserTest03(void)
8f1d7503 1523{
f2f9b832
PR
1524 int result = 1;
1525 Flow f;
1526 uint8_t ftpbuf1[] = "POR";
1527 uint32_t ftplen1 = sizeof(ftpbuf1) - 1; /* minus the \0 */
1528 uint8_t ftpbuf2[] = "T 192,168,1";
1529 uint32_t ftplen2 = sizeof(ftpbuf2) - 1; /* minus the \0 */
1530 uint8_t ftpbuf3[] = "1,1,10,20\r\n";
1531 uint32_t ftplen3 = sizeof(ftpbuf3) - 1; /* minus the \0 */
1532 TcpSession ssn;
8dbf7a0d 1533 AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
f2f9b832
PR
1534
1535 memset(&f, 0, sizeof(f));
1536 memset(&ssn, 0, sizeof(ssn));
48b3cb04
VJ
1537
1538 FLOW_INITIALIZE(&f);
f2f9b832 1539 f.protoctx = (void *)&ssn;
429c6388 1540 f.proto = IPPROTO_TCP;
5c01b409 1541 f.alproto = ALPROTO_FTP;
f2f9b832 1542
6a53ab9c 1543 StreamTcpInitConfig(TRUE);
6a53ab9c 1544
6530c3d0 1545 FLOWLOCK_WRLOCK(&f);
675fa564
GL
1546 int r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_FTP,
1547 STREAM_TOSERVER | STREAM_START, ftpbuf1,
1548 ftplen1);
f2f9b832 1549 if (r != 0) {
f729d6f7 1550 SCLogDebug("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
f2f9b832 1551 result = 0;
6530c3d0 1552 FLOWLOCK_UNLOCK(&f);
f2f9b832
PR
1553 goto end;
1554 }
6530c3d0 1555 FLOWLOCK_UNLOCK(&f);
f2f9b832 1556
6530c3d0 1557 FLOWLOCK_WRLOCK(&f);
675fa564
GL
1558 r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_FTP, STREAM_TOSERVER,
1559 ftpbuf2, ftplen2);
f2f9b832 1560 if (r != 0) {
f729d6f7 1561 SCLogDebug("toserver chunk 2 returned %" PRId32 ", expected 0: ", r);
f2f9b832 1562 result = 0;
6530c3d0 1563 FLOWLOCK_UNLOCK(&f);
f2f9b832
PR
1564 goto end;
1565 }
6530c3d0 1566 FLOWLOCK_UNLOCK(&f);
f2f9b832 1567
6530c3d0 1568 FLOWLOCK_WRLOCK(&f);
675fa564
GL
1569 r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_FTP,
1570 STREAM_TOSERVER | STREAM_EOF, ftpbuf3, ftplen3);
f2f9b832 1571 if (r != 0) {
f729d6f7 1572 SCLogDebug("toserver chunk 3 returned %" PRId32 ", expected 0: ", r);
f2f9b832 1573 result = 0;
6530c3d0 1574 FLOWLOCK_UNLOCK(&f);
f2f9b832
PR
1575 goto end;
1576 }
6530c3d0 1577 FLOWLOCK_UNLOCK(&f);
f2f9b832 1578
06904c90 1579 FtpState *ftp_state = f.alstate;
f2f9b832 1580 if (ftp_state == NULL) {
f729d6f7 1581 SCLogDebug("no ftp state: ");
f2f9b832
PR
1582 result = 0;
1583 goto end;
1584 }
1585
1586 if (ftp_state->command != FTP_COMMAND_PORT) {
f729d6f7 1587 SCLogDebug("expected command %" PRIu32 ", got %" PRIu32 ": ", FTP_COMMAND_PORT, ftp_state->command);
f2f9b832
PR
1588 result = 0;
1589 goto end;
1590 }
1591
1592end:
429c6388 1593 if (alp_tctx != NULL)
fdefb65b 1594 AppLayerParserThreadCtxFree(alp_tctx);
6a53ab9c 1595 StreamTcpFreeConfig(TRUE);
f2f9b832
PR
1596 return result;
1597}
1598
1599/** \test See how it deals with an incomplete request. */
ab1200fb 1600static int FTPParserTest06(void)
8f1d7503 1601{
f2f9b832
PR
1602 int result = 1;
1603 Flow f;
1604 uint8_t ftpbuf1[] = "PORT";
1605 uint32_t ftplen1 = sizeof(ftpbuf1) - 1; /* minus the \0 */
1606 TcpSession ssn;
8dbf7a0d 1607 AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
f2f9b832
PR
1608
1609 memset(&f, 0, sizeof(f));
1610 memset(&ssn, 0, sizeof(ssn));
cc76aa4b
WM
1611
1612 FLOW_INITIALIZE(&f);
f2f9b832 1613 f.protoctx = (void *)&ssn;
429c6388 1614 f.proto = IPPROTO_TCP;
5c01b409 1615 f.alproto = ALPROTO_FTP;
f2f9b832 1616
6a53ab9c 1617 StreamTcpInitConfig(TRUE);
6a53ab9c 1618
6530c3d0 1619 FLOWLOCK_WRLOCK(&f);
675fa564
GL
1620 int r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_FTP,
1621 STREAM_TOSERVER | STREAM_START | STREAM_EOF,
1622 ftpbuf1,
1623 ftplen1);
f2f9b832 1624 if (r != 0) {
f729d6f7 1625 SCLogDebug("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
f2f9b832 1626 result = 0;
6530c3d0 1627 FLOWLOCK_UNLOCK(&f);
f2f9b832
PR
1628 goto end;
1629 }
6530c3d0 1630 FLOWLOCK_UNLOCK(&f);
f2f9b832 1631
06904c90 1632 FtpState *ftp_state = f.alstate;
f2f9b832 1633 if (ftp_state == NULL) {
f729d6f7 1634 SCLogDebug("no ftp state: ");
f2f9b832
PR
1635 result = 0;
1636 goto end;
1637 }
1638
1639 if (ftp_state->command != FTP_COMMAND_UNKNOWN) {
f729d6f7 1640 SCLogDebug("expected command %" PRIu32 ", got %" PRIu32 ": ", FTP_COMMAND_UNKNOWN, ftp_state->command);
f2f9b832
PR
1641 result = 0;
1642 goto end;
1643 }
1644
1645end:
429c6388 1646 if (alp_tctx != NULL)
fdefb65b 1647 AppLayerParserThreadCtxFree(alp_tctx);
6a53ab9c 1648 StreamTcpFreeConfig(TRUE);
0e4235cc 1649 FLOW_DESTROY(&f);
f2f9b832
PR
1650 return result;
1651}
1652
1653/** \test See how it deals with an incomplete request in multiple chunks. */
ab1200fb 1654static int FTPParserTest07(void)
8f1d7503 1655{
f2f9b832
PR
1656 int result = 1;
1657 Flow f;
1658 uint8_t ftpbuf1[] = "PO";
1659 uint32_t ftplen1 = sizeof(ftpbuf1) - 1; /* minus the \0 */
1660 uint8_t ftpbuf2[] = "RT\r\n";
1661 uint32_t ftplen2 = sizeof(ftpbuf2) - 1; /* minus the \0 */
1662 TcpSession ssn;
8dbf7a0d 1663 AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
f2f9b832
PR
1664
1665 memset(&f, 0, sizeof(f));
1666 memset(&ssn, 0, sizeof(ssn));
cc76aa4b
WM
1667
1668 FLOW_INITIALIZE(&f);
f2f9b832 1669 f.protoctx = (void *)&ssn;
429c6388 1670 f.proto = IPPROTO_TCP;
5c01b409 1671 f.alproto = ALPROTO_FTP;
f2f9b832 1672
6a53ab9c 1673 StreamTcpInitConfig(TRUE);
6a53ab9c 1674
6530c3d0 1675 FLOWLOCK_WRLOCK(&f);
675fa564
GL
1676 int r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_FTP,
1677 STREAM_TOSERVER | STREAM_START, ftpbuf1,
1678 ftplen1);
f2f9b832 1679 if (r != 0) {
f729d6f7 1680 SCLogDebug("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
f2f9b832 1681 result = 0;
6530c3d0 1682 FLOWLOCK_UNLOCK(&f);
f2f9b832
PR
1683 goto end;
1684 }
6530c3d0 1685 FLOWLOCK_UNLOCK(&f);
f2f9b832 1686
6530c3d0 1687 FLOWLOCK_WRLOCK(&f);
675fa564
GL
1688 r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_FTP,
1689 STREAM_TOSERVER | STREAM_EOF, ftpbuf2, ftplen2);
f2f9b832 1690 if (r != 0) {
f729d6f7 1691 SCLogDebug("toserver chunk 2 returned %" PRId32 ", expected 0: ", r);
f2f9b832 1692 result = 0;
6530c3d0 1693 FLOWLOCK_UNLOCK(&f);
f2f9b832
PR
1694 goto end;
1695 }
6530c3d0 1696 FLOWLOCK_UNLOCK(&f);
f2f9b832 1697
06904c90 1698 FtpState *ftp_state = f.alstate;
f2f9b832 1699 if (ftp_state == NULL) {
f729d6f7 1700 SCLogDebug("no ftp state: ");
f2f9b832
PR
1701 result = 0;
1702 goto end;
1703 }
1704
6ea8ac44
AS
1705 if (ftp_state->command != FTP_COMMAND_PORT) {
1706 SCLogDebug("expected command %" PRIu32 ", got %" PRIu32 ": ",
1707 FTP_COMMAND_PORT, ftp_state->command);
f2f9b832
PR
1708 result = 0;
1709 goto end;
1710 }
1711
1712end:
429c6388 1713 if (alp_tctx != NULL)
fdefb65b 1714 AppLayerParserThreadCtxFree(alp_tctx);
6a53ab9c 1715 StreamTcpFreeConfig(TRUE);
0e4235cc 1716 FLOW_DESTROY(&f);
f2f9b832
PR
1717 return result;
1718}
1719
1720/** \test Test case where chunks are smaller than the delim length and the
1721 * last chunk is supposed to match the delim. */
ab1200fb 1722static int FTPParserTest10(void)
8f1d7503 1723{
f2f9b832
PR
1724 int result = 1;
1725 Flow f;
1726 uint8_t ftpbuf1[] = "PORT 1,2,3,4,5,6\r\n";
1727 uint32_t ftplen1 = sizeof(ftpbuf1) - 1; /* minus the \0 */
1728 TcpSession ssn;
8dbf7a0d 1729 AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
f2f9b832
PR
1730 int r = 0;
1731 memset(&f, 0, sizeof(f));
1732 memset(&ssn, 0, sizeof(ssn));
cc76aa4b
WM
1733
1734 FLOW_INITIALIZE(&f);
f2f9b832 1735 f.protoctx = (void *)&ssn;
429c6388 1736 f.proto = IPPROTO_TCP;
5c01b409 1737 f.alproto = ALPROTO_FTP;
f2f9b832 1738
6a53ab9c 1739 StreamTcpInitConfig(TRUE);
6a53ab9c 1740
f2f9b832
PR
1741 uint32_t u;
1742 for (u = 0; u < ftplen1; u++) {
1743 uint8_t flags = 0;
1744
1745 if (u == 0) flags = STREAM_TOSERVER|STREAM_START;
1746 else if (u == (ftplen1 - 1)) flags = STREAM_TOSERVER|STREAM_EOF;
1747 else flags = STREAM_TOSERVER;
1748
6530c3d0 1749 FLOWLOCK_WRLOCK(&f);
675fa564
GL
1750 r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_FTP, flags,
1751 &ftpbuf1[u], 1);
f2f9b832 1752 if (r != 0) {
f729d6f7 1753 SCLogDebug("toserver chunk %" PRIu32 " returned %" PRId32 ", expected 0: ", u, r);
f2f9b832 1754 result = 0;
6530c3d0 1755 FLOWLOCK_UNLOCK(&f);
f2f9b832
PR
1756 goto end;
1757 }
6530c3d0 1758 FLOWLOCK_UNLOCK(&f);
f2f9b832
PR
1759 }
1760
06904c90 1761 FtpState *ftp_state = f.alstate;
f2f9b832 1762 if (ftp_state == NULL) {
f729d6f7 1763 SCLogDebug("no ftp state: ");
f2f9b832
PR
1764 result = 0;
1765 goto end;
1766 }
1767
1768 if (ftp_state->command != FTP_COMMAND_PORT) {
f729d6f7 1769 SCLogDebug("expected command %" PRIu32 ", got %" PRIu32 ": ", FTP_COMMAND_PORT, ftp_state->command);
f2f9b832
PR
1770 result = 0;
1771 goto end;
1772 }
1773
354074ba
JL
1774end:
1775 if (alp_tctx != NULL)
1776 AppLayerParserThreadCtxFree(alp_tctx);
1777 StreamTcpFreeConfig(TRUE);
1778 FLOW_DESTROY(&f);
1779 return result;
1780}
1781
1782/** \test Supply RETR without a filename */
1783static int FTPParserTest11(void)
1784{
1785 int result = 1;
1786 Flow f;
1787 uint8_t ftpbuf1[] = "PORT 192,168,1,1,0,80\r\n";
1788 uint8_t ftpbuf2[] = "RETR\r\n";
1789 uint8_t ftpbuf3[] = "227 OK\r\n";
1790 TcpSession ssn;
1791
1792 AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
1793
1794 memset(&f, 0, sizeof(f));
1795 memset(&ssn, 0, sizeof(ssn));
1796
1797 FLOW_INITIALIZE(&f);
1798 f.protoctx = (void *)&ssn;
1799 f.proto = IPPROTO_TCP;
1800 f.alproto = ALPROTO_FTP;
1801
1802 StreamTcpInitConfig(TRUE);
1803
1804 FLOWLOCK_WRLOCK(&f);
1805 int r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_FTP,
1806 STREAM_TOSERVER | STREAM_START, ftpbuf1,
1807 sizeof(ftpbuf1) - 1);
1808 if (r != 0) {
1809 result = 0;
1810 FLOWLOCK_UNLOCK(&f);
1811 goto end;
1812 }
1813 FLOWLOCK_UNLOCK(&f);
1814
1815 /* Response */
1816 FLOWLOCK_WRLOCK(&f);
1817 r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_FTP,
1818 STREAM_TOCLIENT,
1819 ftpbuf3,
1820 sizeof(ftpbuf3) - 1);
1821 if (r != 0) {
1822 result = 0;
1823 FLOWLOCK_UNLOCK(&f);
1824 goto end;
1825 }
1826 FLOWLOCK_UNLOCK(&f);
1827
1828 FLOWLOCK_WRLOCK(&f);
1829 r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_FTP,
1830 STREAM_TOSERVER, ftpbuf2,
1831 sizeof(ftpbuf2) - 1);
1832 if (r == 0) {
1833 SCLogDebug("parse should've failed");
1834 result = 0;
1835 FLOWLOCK_UNLOCK(&f);
1836 goto end;
1837 }
1838 FLOWLOCK_UNLOCK(&f);
1839
1840 FtpState *ftp_state = f.alstate;
1841 if (ftp_state == NULL) {
1842 SCLogDebug("no ftp state: ");
1843 result = 0;
1844 goto end;
1845 }
1846
1847 if (ftp_state->command != FTP_COMMAND_RETR) {
1848 SCLogDebug("expected command %" PRIu32 ", got %" PRIu32 ": ",
1849 FTP_COMMAND_RETR, ftp_state->command);
1850 result = 0;
1851 goto end;
1852 }
1853
1854end:
1855 if (alp_tctx != NULL)
1856 AppLayerParserThreadCtxFree(alp_tctx);
1857 StreamTcpFreeConfig(TRUE);
1858 FLOW_DESTROY(&f);
1859 return result;
1860}
1861
1862/** \test Supply STOR without a filename */
1863static int FTPParserTest12(void)
1864{
1865 int result = 1;
1866 Flow f;
1867 uint8_t ftpbuf1[] = "PORT 192,168,1,1,0,80\r\n";
1868 uint8_t ftpbuf2[] = "STOR\r\n";
1869 uint8_t ftpbuf3[] = "227 OK\r\n";
1870 TcpSession ssn;
1871
1872 AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
1873
1874 memset(&f, 0, sizeof(f));
1875 memset(&ssn, 0, sizeof(ssn));
1876
1877 FLOW_INITIALIZE(&f);
1878 f.protoctx = (void *)&ssn;
1879 f.proto = IPPROTO_TCP;
1880 f.alproto = ALPROTO_FTP;
1881
1882 StreamTcpInitConfig(TRUE);
1883
1884 FLOWLOCK_WRLOCK(&f);
1885 int r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_FTP,
1886 STREAM_TOSERVER | STREAM_START, ftpbuf1,
1887 sizeof(ftpbuf1) - 1);
1888 if (r != 0) {
1889 result = 0;
1890 FLOWLOCK_UNLOCK(&f);
1891 goto end;
1892 }
1893 FLOWLOCK_UNLOCK(&f);
1894
1895 /* Response */
1896 FLOWLOCK_WRLOCK(&f);
1897 r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_FTP,
1898 STREAM_TOCLIENT,
1899 ftpbuf3,
1900 sizeof(ftpbuf3) - 1);
1901 if (r != 0) {
1902 result = 0;
1903 FLOWLOCK_UNLOCK(&f);
1904 goto end;
1905 }
1906 FLOWLOCK_UNLOCK(&f);
1907
1908 FLOWLOCK_WRLOCK(&f);
1909 r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_FTP,
1910 STREAM_TOSERVER, ftpbuf2,
1911 sizeof(ftpbuf2) - 1);
1912 if (r == 0) {
1913 SCLogDebug("parse should've failed");
1914 result = 0;
1915 FLOWLOCK_UNLOCK(&f);
1916 goto end;
1917 }
1918 FLOWLOCK_UNLOCK(&f);
1919
1920 FtpState *ftp_state = f.alstate;
1921 if (ftp_state == NULL) {
1922 SCLogDebug("no ftp state: ");
1923 result = 0;
1924 goto end;
1925 }
1926
1927 if (ftp_state->command != FTP_COMMAND_STOR) {
1928 SCLogDebug("expected command %" PRIu32 ", got %" PRIu32 ": ",
1929 FTP_COMMAND_STOR, ftp_state->command);
1930 result = 0;
1931 goto end;
1932 }
1933
f2f9b832 1934end:
429c6388 1935 if (alp_tctx != NULL)
fdefb65b 1936 AppLayerParserThreadCtxFree(alp_tctx);
6a53ab9c 1937 StreamTcpFreeConfig(TRUE);
0e4235cc 1938 FLOW_DESTROY(&f);
f2f9b832
PR
1939 return result;
1940}
1941#endif /* UNITTESTS */
1942
8f1d7503
KS
1943void FTPParserRegisterTests(void)
1944{
f2f9b832 1945#ifdef UNITTESTS
796dd522
JI
1946 UtRegisterTest("FTPParserTest01", FTPParserTest01);
1947 UtRegisterTest("FTPParserTest03", FTPParserTest03);
1948 UtRegisterTest("FTPParserTest06", FTPParserTest06);
1949 UtRegisterTest("FTPParserTest07", FTPParserTest07);
1950 UtRegisterTest("FTPParserTest10", FTPParserTest10);
354074ba
JL
1951 UtRegisterTest("FTPParserTest11", FTPParserTest11);
1952 UtRegisterTest("FTPParserTest12", FTPParserTest12);
f2f9b832
PR
1953#endif /* UNITTESTS */
1954}
1955