]>
Commit | Line | Data |
---|---|---|
5532af46 | 1 | /* Copyright (C) 2007-2013 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 PR |
21 | * \author Pablo Rincon Crespo <pablo.rincon.crespo@gmail.com> |
22 | * | |
23 | * App Layer Parser for FTP | |
24 | */ | |
25 | ||
26 | #include "suricata-common.h" | |
27 | #include "debug.h" | |
28 | #include "decode.h" | |
29 | #include "threads.h" | |
30 | ||
31 | #include "util-print.h" | |
32 | #include "util-pool.h" | |
33 | ||
cc76aa4b WM |
34 | #include "flow-util.h" |
35 | ||
0e4235cc WM |
36 | #include "detect-engine-state.h" |
37 | ||
f2f9b832 PR |
38 | #include "stream-tcp-private.h" |
39 | #include "stream-tcp-reassemble.h" | |
6a53ab9c | 40 | #include "stream-tcp.h" |
f2f9b832 PR |
41 | #include "stream.h" |
42 | ||
429c6388 | 43 | #include "app-layer.h" |
f2f9b832 PR |
44 | #include "app-layer-protos.h" |
45 | #include "app-layer-parser.h" | |
46 | #include "app-layer-ftp.h" | |
47 | ||
705471e4 | 48 | #include "util-spm.h" |
f2f9b832 PR |
49 | #include "util-unittest.h" |
50 | #include "util-debug.h" | |
1859ed54 | 51 | #include "util-memcmp.h" |
f2f9b832 | 52 | |
6ea8ac44 AS |
53 | static int FTPGetLineForDirection(FtpState *state, FtpLineState *line_state) |
54 | { | |
1f07d152 | 55 | void *ptmp; |
6ea8ac44 AS |
56 | if (line_state->current_line_lf_seen == 1) { |
57 | /* we have seen the lf for the previous line. Clear the parser | |
58 | * details to parse new line */ | |
59 | line_state->current_line_lf_seen = 0; | |
60 | if (line_state->current_line_db == 1) { | |
61 | line_state->current_line_db = 0; | |
62 | SCFree(line_state->db); | |
63 | line_state->db = NULL; | |
64 | line_state->db_len = 0; | |
65 | state->current_line = NULL; | |
66 | state->current_line_len = 0; | |
67 | } | |
68 | } | |
69 | ||
70 | uint8_t *lf_idx = memchr(state->input, 0x0a, state->input_len); | |
71 | ||
72 | if (lf_idx == NULL) { | |
73 | /* fragmented lines. Decoder event for special cases. Not all | |
74 | * fragmented lines should be treated as a possible evasion | |
75 | * attempt. With multi payload ftp chunks we can have valid | |
76 | * cases of fragmentation. But within the same segment chunk | |
77 | * if we see fragmentation then it's definitely something you | |
78 | * should alert about */ | |
79 | if (line_state->current_line_db == 0) { | |
80 | line_state->db = SCMalloc(state->input_len); | |
81 | if (line_state->db == NULL) { | |
82 | return -1; | |
83 | } | |
84 | line_state->current_line_db = 1; | |
85 | memcpy(line_state->db, state->input, state->input_len); | |
86 | line_state->db_len = state->input_len; | |
87 | } else { | |
1f07d152 EL |
88 | ptmp = SCRealloc(line_state->db, |
89 | (line_state->db_len + state->input_len)); | |
90 | if (ptmp == NULL) { | |
91 | SCFree(line_state->db); | |
92 | line_state->db = NULL; | |
93 | line_state->db_len = 0; | |
6ea8ac44 AS |
94 | return -1; |
95 | } | |
1f07d152 EL |
96 | line_state->db = ptmp; |
97 | ||
6ea8ac44 AS |
98 | memcpy(line_state->db + line_state->db_len, |
99 | state->input, state->input_len); | |
100 | line_state->db_len += state->input_len; | |
101 | } | |
102 | state->input += state->input_len; | |
103 | state->input_len = 0; | |
104 | ||
105 | return -1; | |
106 | ||
107 | } else { | |
108 | line_state->current_line_lf_seen = 1; | |
109 | ||
110 | if (line_state->current_line_db == 1) { | |
1f07d152 EL |
111 | ptmp = SCRealloc(line_state->db, |
112 | (line_state->db_len + (lf_idx + 1 - state->input))); | |
113 | if (ptmp == NULL) { | |
114 | SCFree(line_state->db); | |
115 | line_state->db = NULL; | |
116 | line_state->db_len = 0; | |
6ea8ac44 AS |
117 | return -1; |
118 | } | |
1f07d152 EL |
119 | line_state->db = ptmp; |
120 | ||
6ea8ac44 AS |
121 | memcpy(line_state->db + line_state->db_len, |
122 | state->input, (lf_idx + 1 - state->input)); | |
123 | line_state->db_len += (lf_idx + 1 - state->input); | |
124 | ||
125 | if (line_state->db_len > 1 && | |
126 | line_state->db[line_state->db_len - 2] == 0x0D) { | |
127 | line_state->db_len -= 2; | |
128 | state->current_line_delimiter_len = 2; | |
129 | } else { | |
130 | line_state->db_len -= 1; | |
131 | state->current_line_delimiter_len = 1; | |
132 | } | |
133 | ||
134 | state->current_line = line_state->db; | |
135 | state->current_line_len = line_state->db_len; | |
136 | ||
137 | } else { | |
138 | state->current_line = state->input; | |
139 | state->current_line_len = lf_idx - state->input; | |
140 | ||
141 | if (state->input != lf_idx && | |
142 | *(lf_idx - 1) == 0x0D) { | |
143 | state->current_line_len--; | |
144 | state->current_line_delimiter_len = 2; | |
145 | } else { | |
146 | state->current_line_delimiter_len = 1; | |
147 | } | |
148 | } | |
149 | ||
150 | state->input_len -= (lf_idx - state->input) + 1; | |
151 | state->input = (lf_idx + 1); | |
152 | ||
153 | return 0; | |
154 | } | |
155 | ||
156 | } | |
157 | ||
158 | static int FTPGetLine(FtpState *state) | |
159 | { | |
160 | SCEnter(); | |
161 | ||
162 | /* we have run out of input */ | |
163 | if (state->input_len <= 0) | |
164 | return -1; | |
165 | ||
166 | /* toserver */ | |
167 | if (state->direction == 0) | |
168 | return FTPGetLineForDirection(state, &state->line_state[0]); | |
169 | else | |
170 | return FTPGetLineForDirection(state, &state->line_state[1]); | |
171 | } | |
172 | ||
f2f9b832 PR |
173 | /** |
174 | * \brief This function is called to determine and set which command is being | |
175 | * transfered to the ftp server | |
176 | * \param ftp_state the ftp state structure for the parser | |
177 | * \param input input line of the command | |
178 | * \param len of the command | |
179 | * | |
180 | * \retval 1 when the command is parsed, 0 otherwise | |
181 | */ | |
182 | static int FTPParseRequestCommand(void *ftp_state, uint8_t *input, | |
8f1d7503 KS |
183 | uint32_t input_len) |
184 | { | |
f2f9b832 PR |
185 | SCEnter(); |
186 | FtpState *fstate = (FtpState *)ftp_state; | |
6ea8ac44 | 187 | fstate->command = FTP_COMMAND_UNKNOWN; |
f2f9b832 | 188 | |
f2f9b832 | 189 | if (input_len >= 4) { |
1859ed54 | 190 | if (SCMemcmpLowercase("port", input, 4) == 0) { |
f2f9b832 PR |
191 | fstate->command = FTP_COMMAND_PORT; |
192 | } | |
9d7baa7a | 193 | |
f2f9b832 PR |
194 | /* else { |
195 | * Add the ftp commands you need here | |
196 | * } | |
197 | */ | |
198 | } | |
199 | return 1; | |
200 | } | |
201 | ||
f2f9b832 PR |
202 | /** |
203 | * \brief This function is called to retrieve a ftp request | |
204 | * \param ftp_state the ftp state structure for the parser | |
205 | * \param input input line of the command | |
206 | * \param input_len length of the request | |
207 | * \param output the resulting output | |
208 | * | |
209 | * \retval 1 when the command is parsed, 0 otherwise | |
210 | */ | |
9a6aef45 | 211 | static int FTPParseRequest(Flow *f, void *ftp_state, |
9634e60e | 212 | AppLayerParserState *pstate, |
9a6aef45 | 213 | uint8_t *input, uint32_t input_len, |
429c6388 | 214 | void *local_data) |
9a6aef45 | 215 | { |
f2f9b832 | 216 | SCEnter(); |
f2f9b832 PR |
217 | /* PrintRawDataFp(stdout, input,input_len); */ |
218 | ||
6ea8ac44 | 219 | FtpState *state = (FtpState *)ftp_state; |
1f07d152 | 220 | void *ptmp; |
6ea8ac44 | 221 | |
4e7cb7b8 VJ |
222 | if (input == NULL && AppLayerParserStateIssetFlag(pstate, APP_LAYER_PARSER_EOF)) { |
223 | SCReturnInt(1); | |
f4f53924 VJ |
224 | } else if (input == NULL || input_len == 0) { |
225 | SCReturnInt(-1); | |
4e7cb7b8 VJ |
226 | } |
227 | ||
6ea8ac44 AS |
228 | state->input = input; |
229 | state->input_len = input_len; | |
230 | /* toserver stream */ | |
231 | state->direction = 0; | |
232 | ||
233 | while (FTPGetLine(state) >= 0) { | |
234 | FTPParseRequestCommand(state, | |
235 | state->current_line, state->current_line_len); | |
236 | if (state->command == FTP_COMMAND_PORT) { | |
237 | if (state->current_line_len > state->port_line_size) { | |
1f07d152 EL |
238 | ptmp = SCRealloc(state->port_line, state->current_line_len); |
239 | if (ptmp == NULL) { | |
240 | SCFree(state->port_line); | |
241 | state->port_line = NULL; | |
6ea8ac44 AS |
242 | state->port_line_size = 0; |
243 | return 0; | |
244 | } | |
1f07d152 EL |
245 | state->port_line = ptmp; |
246 | ||
6ea8ac44 AS |
247 | state->port_line_size = state->current_line_len; |
248 | } | |
249 | memcpy(state->port_line, state->current_line, | |
250 | state->current_line_len); | |
251 | state->port_line_len = state->current_line_len; | |
252 | } | |
f2f9b832 | 253 | } |
f2f9b832 | 254 | |
f2f9b832 PR |
255 | return 1; |
256 | } | |
257 | ||
258 | /** | |
259 | * \brief This function is called to retrieve a ftp response | |
260 | * \param ftp_state the ftp state structure for the parser | |
261 | * \param input input line of the command | |
262 | * \param input_len length of the request | |
263 | * \param output the resulting output | |
264 | * | |
265 | * \retval 1 when the command is parsed, 0 otherwise | |
266 | */ | |
9634e60e | 267 | static int FTPParseResponse(Flow *f, void *ftp_state, AppLayerParserState *pstate, |
f2f9b832 | 268 | uint8_t *input, uint32_t input_len, |
429c6388 | 269 | void *local_data) |
9a6aef45 | 270 | { |
f2f9b832 PR |
271 | return 1; |
272 | } | |
273 | ||
274 | #ifdef DEBUG | |
5532af46 | 275 | static SCMutex ftp_state_mem_lock = SCMUTEX_INITIALIZER; |
f2f9b832 PR |
276 | static uint64_t ftp_state_memuse = 0; |
277 | static uint64_t ftp_state_memcnt = 0; | |
278 | #endif | |
279 | ||
8f1d7503 KS |
280 | static void *FTPStateAlloc(void) |
281 | { | |
25a3a5c6 | 282 | void *s = SCMalloc(sizeof(FtpState)); |
e176be6f | 283 | if (unlikely(s == NULL)) |
f2f9b832 PR |
284 | return NULL; |
285 | ||
286 | memset(s, 0, sizeof(FtpState)); | |
287 | ||
288 | #ifdef DEBUG | |
289 | SCMutexLock(&ftp_state_mem_lock); | |
290 | ftp_state_memcnt++; | |
291 | ftp_state_memuse+=sizeof(FtpState); | |
292 | SCMutexUnlock(&ftp_state_mem_lock); | |
293 | #endif | |
294 | return s; | |
295 | } | |
296 | ||
8f1d7503 KS |
297 | static void FTPStateFree(void *s) |
298 | { | |
18954a2c PR |
299 | FtpState *fstate = (FtpState *) s; |
300 | if (fstate->port_line != NULL) | |
301 | SCFree(fstate->port_line); | |
b4ab9a0a VJ |
302 | if (fstate->line_state[0].db) |
303 | SCFree(fstate->line_state[0].db); | |
304 | if (fstate->line_state[1].db) | |
305 | SCFree(fstate->line_state[1].db); | |
402eb645 VJ |
306 | |
307 | //AppLayerDecoderEventsFreeEvents(&s->decoder_events); | |
308 | ||
309 | if (fstate->de_state != NULL) { | |
310 | DetectEngineStateFree(fstate->de_state); | |
311 | } | |
312 | ||
25a3a5c6 | 313 | SCFree(s); |
f2f9b832 PR |
314 | #ifdef DEBUG |
315 | SCMutexLock(&ftp_state_mem_lock); | |
316 | ftp_state_memcnt--; | |
317 | ftp_state_memuse-=sizeof(FtpState); | |
318 | SCMutexUnlock(&ftp_state_mem_lock); | |
319 | #endif | |
320 | } | |
321 | ||
402eb645 VJ |
322 | static int FTPStateHasTxDetectState(void *state) |
323 | { | |
324 | FtpState *ssh_state = (FtpState *)state; | |
325 | if (ssh_state->de_state) | |
326 | return 1; | |
327 | return 0; | |
328 | } | |
329 | ||
330 | static int FTPSetTxDetectState(void *state, void *vtx, DetectEngineState *de_state) | |
331 | { | |
332 | FtpState *ssh_state = (FtpState *)state; | |
333 | ssh_state->de_state = de_state; | |
334 | return 0; | |
335 | } | |
336 | ||
337 | static DetectEngineState *FTPGetTxDetectState(void *vtx) | |
338 | { | |
339 | FtpState *ssh_state = (FtpState *)vtx; | |
340 | return ssh_state->de_state; | |
341 | } | |
342 | ||
343 | static void FTPStateTransactionFree(void *state, uint64_t tx_id) | |
344 | { | |
345 | /* do nothing */ | |
346 | } | |
347 | ||
348 | static void *FTPGetTx(void *state, uint64_t tx_id) | |
349 | { | |
350 | FtpState *ssh_state = (FtpState *)state; | |
351 | return ssh_state; | |
352 | } | |
353 | ||
354 | static uint64_t FTPGetTxCnt(void *state) | |
355 | { | |
356 | /* single tx */ | |
357 | return 1; | |
358 | } | |
359 | ||
360 | static int FTPGetAlstateProgressCompletionStatus(uint8_t direction) | |
361 | { | |
362 | return FTP_STATE_FINISHED; | |
363 | } | |
364 | ||
365 | static int FTPGetAlstateProgress(void *tx, uint8_t direction) | |
366 | { | |
367 | FtpState *ftp_state = (FtpState *)tx; | |
368 | ||
369 | if (direction == STREAM_TOSERVER && | |
370 | ftp_state->command == FTP_COMMAND_PORT) { | |
371 | return FTP_STATE_PORT_DONE; | |
372 | } | |
373 | ||
374 | /* TODO: figure out further progress handling */ | |
375 | ||
376 | return FTP_STATE_IN_PROGRESS; | |
377 | } | |
378 | ||
379 | ||
429c6388 AS |
380 | static int FTPRegisterPatternsForProtocolDetection(void) |
381 | { | |
382 | if (AppLayerProtoDetectPMRegisterPatternCI(IPPROTO_TCP, ALPROTO_FTP, | |
383 | "USER ", 5, 0, STREAM_TOSERVER) < 0) | |
384 | { | |
385 | return -1; | |
386 | } | |
387 | if (AppLayerProtoDetectPMRegisterPatternCI(IPPROTO_TCP, ALPROTO_FTP, | |
388 | "PASS ", 5, 0, STREAM_TOSERVER) < 0) | |
389 | { | |
390 | return -1; | |
391 | } | |
392 | if (AppLayerProtoDetectPMRegisterPatternCI(IPPROTO_TCP, ALPROTO_FTP, | |
393 | "PORT ", 5, 0, STREAM_TOSERVER) < 0) | |
394 | { | |
395 | return -1; | |
396 | } | |
397 | ||
398 | return 0; | |
399 | } | |
400 | ||
8f1d7503 KS |
401 | void RegisterFTPParsers(void) |
402 | { | |
ab1200fb | 403 | const char *proto_name = "ftp"; |
10966245 | 404 | |
000ce98c | 405 | /** FTP */ |
429c6388 AS |
406 | if (AppLayerProtoDetectConfProtoDetectionEnabled("tcp", proto_name)) { |
407 | AppLayerProtoDetectRegisterProtocol(ALPROTO_FTP, proto_name); | |
408 | if (FTPRegisterPatternsForProtocolDetection() < 0 ) | |
409 | return; | |
ddde572f AS |
410 | } |
411 | ||
429c6388 AS |
412 | if (AppLayerParserConfParserEnabled("tcp", proto_name)) { |
413 | AppLayerParserRegisterParser(IPPROTO_TCP, ALPROTO_FTP, STREAM_TOSERVER, | |
414 | FTPParseRequest); | |
415 | AppLayerParserRegisterParser(IPPROTO_TCP, ALPROTO_FTP, STREAM_TOCLIENT, | |
416 | FTPParseResponse); | |
417 | AppLayerParserRegisterStateFuncs(IPPROTO_TCP, ALPROTO_FTP, FTPStateAlloc, FTPStateFree); | |
418 | AppLayerParserRegisterParserAcceptableDataDirection(IPPROTO_TCP, ALPROTO_FTP, STREAM_TOSERVER | STREAM_TOCLIENT); | |
402eb645 VJ |
419 | |
420 | AppLayerParserRegisterTxFreeFunc(IPPROTO_TCP, ALPROTO_FTP, FTPStateTransactionFree); | |
421 | ||
422 | AppLayerParserRegisterDetectStateFuncs(IPPROTO_TCP, ALPROTO_FTP, FTPStateHasTxDetectState, | |
423 | FTPGetTxDetectState, FTPSetTxDetectState); | |
424 | ||
425 | AppLayerParserRegisterGetTx(IPPROTO_TCP, ALPROTO_FTP, FTPGetTx); | |
426 | ||
427 | AppLayerParserRegisterGetTxCnt(IPPROTO_TCP, ALPROTO_FTP, FTPGetTxCnt); | |
428 | ||
429 | AppLayerParserRegisterGetStateProgressFunc(IPPROTO_TCP, ALPROTO_FTP, FTPGetAlstateProgress); | |
430 | ||
431 | AppLayerParserRegisterGetStateProgressCompletionStatus(ALPROTO_FTP, | |
432 | FTPGetAlstateProgressCompletionStatus); | |
ddde572f AS |
433 | } else { |
434 | SCLogInfo("Parsed disabled for %s protocol. Protocol detection" | |
435 | "still on.", proto_name); | |
436 | } | |
9faa4b74 | 437 | #ifdef UNITTESTS |
429c6388 | 438 | AppLayerParserRegisterProtocolUnittests(IPPROTO_TCP, ALPROTO_FTP, FTPParserRegisterTests); |
9faa4b74 | 439 | #endif |
f2f9b832 PR |
440 | } |
441 | ||
8f1d7503 KS |
442 | void FTPAtExitPrintStats(void) |
443 | { | |
f2f9b832 PR |
444 | #ifdef DEBUG |
445 | SCMutexLock(&ftp_state_mem_lock); | |
446 | SCLogDebug("ftp_state_memcnt %"PRIu64", ftp_state_memuse %"PRIu64"", | |
447 | ftp_state_memcnt, ftp_state_memuse); | |
448 | SCMutexUnlock(&ftp_state_mem_lock); | |
449 | #endif | |
450 | } | |
451 | ||
452 | /* UNITTESTS */ | |
453 | #ifdef UNITTESTS | |
454 | ||
455 | /** \test Send a get request in one chunk. */ | |
ab1200fb | 456 | static int FTPParserTest01(void) |
8f1d7503 | 457 | { |
f2f9b832 PR |
458 | int result = 1; |
459 | Flow f; | |
460 | uint8_t ftpbuf[] = "PORT 192,168,1,1,0,80\r\n"; | |
461 | uint32_t ftplen = sizeof(ftpbuf) - 1; /* minus the \0 */ | |
462 | TcpSession ssn; | |
8dbf7a0d | 463 | AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc(); |
f2f9b832 PR |
464 | |
465 | memset(&f, 0, sizeof(f)); | |
466 | memset(&ssn, 0, sizeof(ssn)); | |
cc76aa4b WM |
467 | |
468 | FLOW_INITIALIZE(&f); | |
f2f9b832 | 469 | f.protoctx = (void *)&ssn; |
429c6388 | 470 | f.proto = IPPROTO_TCP; |
f2f9b832 | 471 | |
6a53ab9c | 472 | StreamTcpInitConfig(TRUE); |
6a53ab9c | 473 | |
6530c3d0 | 474 | FLOWLOCK_WRLOCK(&f); |
675fa564 GL |
475 | int r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_FTP, |
476 | STREAM_TOSERVER | STREAM_EOF, ftpbuf, ftplen); | |
f2f9b832 | 477 | if (r != 0) { |
f729d6f7 | 478 | SCLogDebug("toserver chunk 1 returned %" PRId32 ", expected 0: ", r); |
f2f9b832 | 479 | result = 0; |
6530c3d0 | 480 | FLOWLOCK_UNLOCK(&f); |
f2f9b832 PR |
481 | goto end; |
482 | } | |
6530c3d0 | 483 | FLOWLOCK_UNLOCK(&f); |
f2f9b832 | 484 | |
06904c90 | 485 | FtpState *ftp_state = f.alstate; |
f2f9b832 | 486 | if (ftp_state == NULL) { |
f729d6f7 | 487 | SCLogDebug("no ftp state: "); |
f2f9b832 PR |
488 | result = 0; |
489 | goto end; | |
490 | } | |
491 | ||
492 | if (ftp_state->command != FTP_COMMAND_PORT) { | |
f729d6f7 | 493 | SCLogDebug("expected command %" PRIu32 ", got %" PRIu32 ": ", FTP_COMMAND_PORT, ftp_state->command); |
f2f9b832 PR |
494 | result = 0; |
495 | goto end; | |
496 | } | |
497 | ||
498 | end: | |
429c6388 | 499 | if (alp_tctx != NULL) |
fdefb65b | 500 | AppLayerParserThreadCtxFree(alp_tctx); |
6a53ab9c | 501 | StreamTcpFreeConfig(TRUE); |
0e4235cc | 502 | FLOW_DESTROY(&f); |
f2f9b832 PR |
503 | return result; |
504 | } | |
505 | ||
506 | /** \test Send a splitted get request. */ | |
ab1200fb | 507 | static int FTPParserTest03(void) |
8f1d7503 | 508 | { |
f2f9b832 PR |
509 | int result = 1; |
510 | Flow f; | |
511 | uint8_t ftpbuf1[] = "POR"; | |
512 | uint32_t ftplen1 = sizeof(ftpbuf1) - 1; /* minus the \0 */ | |
513 | uint8_t ftpbuf2[] = "T 192,168,1"; | |
514 | uint32_t ftplen2 = sizeof(ftpbuf2) - 1; /* minus the \0 */ | |
515 | uint8_t ftpbuf3[] = "1,1,10,20\r\n"; | |
516 | uint32_t ftplen3 = sizeof(ftpbuf3) - 1; /* minus the \0 */ | |
517 | TcpSession ssn; | |
8dbf7a0d | 518 | AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc(); |
f2f9b832 PR |
519 | |
520 | memset(&f, 0, sizeof(f)); | |
521 | memset(&ssn, 0, sizeof(ssn)); | |
48b3cb04 VJ |
522 | |
523 | FLOW_INITIALIZE(&f); | |
f2f9b832 | 524 | f.protoctx = (void *)&ssn; |
429c6388 | 525 | f.proto = IPPROTO_TCP; |
f2f9b832 | 526 | |
6a53ab9c | 527 | StreamTcpInitConfig(TRUE); |
6a53ab9c | 528 | |
6530c3d0 | 529 | FLOWLOCK_WRLOCK(&f); |
675fa564 GL |
530 | int r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_FTP, |
531 | STREAM_TOSERVER | STREAM_START, ftpbuf1, | |
532 | ftplen1); | |
f2f9b832 | 533 | if (r != 0) { |
f729d6f7 | 534 | SCLogDebug("toserver chunk 1 returned %" PRId32 ", expected 0: ", r); |
f2f9b832 | 535 | result = 0; |
6530c3d0 | 536 | FLOWLOCK_UNLOCK(&f); |
f2f9b832 PR |
537 | goto end; |
538 | } | |
6530c3d0 | 539 | FLOWLOCK_UNLOCK(&f); |
f2f9b832 | 540 | |
6530c3d0 | 541 | FLOWLOCK_WRLOCK(&f); |
675fa564 GL |
542 | r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_FTP, STREAM_TOSERVER, |
543 | ftpbuf2, ftplen2); | |
f2f9b832 | 544 | if (r != 0) { |
f729d6f7 | 545 | SCLogDebug("toserver chunk 2 returned %" PRId32 ", expected 0: ", r); |
f2f9b832 | 546 | result = 0; |
6530c3d0 | 547 | FLOWLOCK_UNLOCK(&f); |
f2f9b832 PR |
548 | goto end; |
549 | } | |
6530c3d0 | 550 | FLOWLOCK_UNLOCK(&f); |
f2f9b832 | 551 | |
6530c3d0 | 552 | FLOWLOCK_WRLOCK(&f); |
675fa564 GL |
553 | r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_FTP, |
554 | STREAM_TOSERVER | STREAM_EOF, ftpbuf3, ftplen3); | |
f2f9b832 | 555 | if (r != 0) { |
f729d6f7 | 556 | SCLogDebug("toserver chunk 3 returned %" PRId32 ", expected 0: ", r); |
f2f9b832 | 557 | result = 0; |
6530c3d0 | 558 | FLOWLOCK_UNLOCK(&f); |
f2f9b832 PR |
559 | goto end; |
560 | } | |
6530c3d0 | 561 | FLOWLOCK_UNLOCK(&f); |
f2f9b832 | 562 | |
06904c90 | 563 | FtpState *ftp_state = f.alstate; |
f2f9b832 | 564 | if (ftp_state == NULL) { |
f729d6f7 | 565 | SCLogDebug("no ftp state: "); |
f2f9b832 PR |
566 | result = 0; |
567 | goto end; | |
568 | } | |
569 | ||
570 | if (ftp_state->command != FTP_COMMAND_PORT) { | |
f729d6f7 | 571 | SCLogDebug("expected command %" PRIu32 ", got %" PRIu32 ": ", FTP_COMMAND_PORT, ftp_state->command); |
f2f9b832 PR |
572 | result = 0; |
573 | goto end; | |
574 | } | |
575 | ||
576 | end: | |
429c6388 | 577 | if (alp_tctx != NULL) |
fdefb65b | 578 | AppLayerParserThreadCtxFree(alp_tctx); |
6a53ab9c | 579 | StreamTcpFreeConfig(TRUE); |
f2f9b832 PR |
580 | return result; |
581 | } | |
582 | ||
583 | /** \test See how it deals with an incomplete request. */ | |
ab1200fb | 584 | static int FTPParserTest06(void) |
8f1d7503 | 585 | { |
f2f9b832 PR |
586 | int result = 1; |
587 | Flow f; | |
588 | uint8_t ftpbuf1[] = "PORT"; | |
589 | uint32_t ftplen1 = sizeof(ftpbuf1) - 1; /* minus the \0 */ | |
590 | TcpSession ssn; | |
8dbf7a0d | 591 | AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc(); |
f2f9b832 PR |
592 | |
593 | memset(&f, 0, sizeof(f)); | |
594 | memset(&ssn, 0, sizeof(ssn)); | |
cc76aa4b WM |
595 | |
596 | FLOW_INITIALIZE(&f); | |
f2f9b832 | 597 | f.protoctx = (void *)&ssn; |
429c6388 | 598 | f.proto = IPPROTO_TCP; |
f2f9b832 | 599 | |
6a53ab9c | 600 | StreamTcpInitConfig(TRUE); |
6a53ab9c | 601 | |
6530c3d0 | 602 | FLOWLOCK_WRLOCK(&f); |
675fa564 GL |
603 | int r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_FTP, |
604 | STREAM_TOSERVER | STREAM_START | STREAM_EOF, | |
605 | ftpbuf1, | |
606 | ftplen1); | |
f2f9b832 | 607 | if (r != 0) { |
f729d6f7 | 608 | SCLogDebug("toserver chunk 1 returned %" PRId32 ", expected 0: ", r); |
f2f9b832 | 609 | result = 0; |
6530c3d0 | 610 | FLOWLOCK_UNLOCK(&f); |
f2f9b832 PR |
611 | goto end; |
612 | } | |
6530c3d0 | 613 | FLOWLOCK_UNLOCK(&f); |
f2f9b832 | 614 | |
06904c90 | 615 | FtpState *ftp_state = f.alstate; |
f2f9b832 | 616 | if (ftp_state == NULL) { |
f729d6f7 | 617 | SCLogDebug("no ftp state: "); |
f2f9b832 PR |
618 | result = 0; |
619 | goto end; | |
620 | } | |
621 | ||
622 | if (ftp_state->command != FTP_COMMAND_UNKNOWN) { | |
f729d6f7 | 623 | SCLogDebug("expected command %" PRIu32 ", got %" PRIu32 ": ", FTP_COMMAND_UNKNOWN, ftp_state->command); |
f2f9b832 PR |
624 | result = 0; |
625 | goto end; | |
626 | } | |
627 | ||
628 | end: | |
429c6388 | 629 | if (alp_tctx != NULL) |
fdefb65b | 630 | AppLayerParserThreadCtxFree(alp_tctx); |
6a53ab9c | 631 | StreamTcpFreeConfig(TRUE); |
0e4235cc | 632 | FLOW_DESTROY(&f); |
f2f9b832 PR |
633 | return result; |
634 | } | |
635 | ||
636 | /** \test See how it deals with an incomplete request in multiple chunks. */ | |
ab1200fb | 637 | static int FTPParserTest07(void) |
8f1d7503 | 638 | { |
f2f9b832 PR |
639 | int result = 1; |
640 | Flow f; | |
641 | uint8_t ftpbuf1[] = "PO"; | |
642 | uint32_t ftplen1 = sizeof(ftpbuf1) - 1; /* minus the \0 */ | |
643 | uint8_t ftpbuf2[] = "RT\r\n"; | |
644 | uint32_t ftplen2 = sizeof(ftpbuf2) - 1; /* minus the \0 */ | |
645 | TcpSession ssn; | |
8dbf7a0d | 646 | AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc(); |
f2f9b832 PR |
647 | |
648 | memset(&f, 0, sizeof(f)); | |
649 | memset(&ssn, 0, sizeof(ssn)); | |
cc76aa4b WM |
650 | |
651 | FLOW_INITIALIZE(&f); | |
f2f9b832 | 652 | f.protoctx = (void *)&ssn; |
429c6388 | 653 | f.proto = IPPROTO_TCP; |
f2f9b832 | 654 | |
6a53ab9c | 655 | StreamTcpInitConfig(TRUE); |
6a53ab9c | 656 | |
6530c3d0 | 657 | FLOWLOCK_WRLOCK(&f); |
675fa564 GL |
658 | int r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_FTP, |
659 | STREAM_TOSERVER | STREAM_START, ftpbuf1, | |
660 | ftplen1); | |
f2f9b832 | 661 | if (r != 0) { |
f729d6f7 | 662 | SCLogDebug("toserver chunk 1 returned %" PRId32 ", expected 0: ", r); |
f2f9b832 | 663 | result = 0; |
6530c3d0 | 664 | FLOWLOCK_UNLOCK(&f); |
f2f9b832 PR |
665 | goto end; |
666 | } | |
6530c3d0 | 667 | FLOWLOCK_UNLOCK(&f); |
f2f9b832 | 668 | |
6530c3d0 | 669 | FLOWLOCK_WRLOCK(&f); |
675fa564 GL |
670 | r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_FTP, |
671 | STREAM_TOSERVER | STREAM_EOF, ftpbuf2, ftplen2); | |
f2f9b832 | 672 | if (r != 0) { |
f729d6f7 | 673 | SCLogDebug("toserver chunk 2 returned %" PRId32 ", expected 0: ", r); |
f2f9b832 | 674 | result = 0; |
6530c3d0 | 675 | FLOWLOCK_UNLOCK(&f); |
f2f9b832 PR |
676 | goto end; |
677 | } | |
6530c3d0 | 678 | FLOWLOCK_UNLOCK(&f); |
f2f9b832 | 679 | |
06904c90 | 680 | FtpState *ftp_state = f.alstate; |
f2f9b832 | 681 | if (ftp_state == NULL) { |
f729d6f7 | 682 | SCLogDebug("no ftp state: "); |
f2f9b832 PR |
683 | result = 0; |
684 | goto end; | |
685 | } | |
686 | ||
6ea8ac44 AS |
687 | if (ftp_state->command != FTP_COMMAND_PORT) { |
688 | SCLogDebug("expected command %" PRIu32 ", got %" PRIu32 ": ", | |
689 | FTP_COMMAND_PORT, ftp_state->command); | |
f2f9b832 PR |
690 | result = 0; |
691 | goto end; | |
692 | } | |
693 | ||
694 | end: | |
429c6388 | 695 | if (alp_tctx != NULL) |
fdefb65b | 696 | AppLayerParserThreadCtxFree(alp_tctx); |
6a53ab9c | 697 | StreamTcpFreeConfig(TRUE); |
0e4235cc | 698 | FLOW_DESTROY(&f); |
f2f9b832 PR |
699 | return result; |
700 | } | |
701 | ||
702 | /** \test Test case where chunks are smaller than the delim length and the | |
703 | * last chunk is supposed to match the delim. */ | |
ab1200fb | 704 | static int FTPParserTest10(void) |
8f1d7503 | 705 | { |
f2f9b832 PR |
706 | int result = 1; |
707 | Flow f; | |
708 | uint8_t ftpbuf1[] = "PORT 1,2,3,4,5,6\r\n"; | |
709 | uint32_t ftplen1 = sizeof(ftpbuf1) - 1; /* minus the \0 */ | |
710 | TcpSession ssn; | |
8dbf7a0d | 711 | AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc(); |
f2f9b832 PR |
712 | int r = 0; |
713 | memset(&f, 0, sizeof(f)); | |
714 | memset(&ssn, 0, sizeof(ssn)); | |
cc76aa4b WM |
715 | |
716 | FLOW_INITIALIZE(&f); | |
f2f9b832 | 717 | f.protoctx = (void *)&ssn; |
429c6388 | 718 | f.proto = IPPROTO_TCP; |
f2f9b832 | 719 | |
6a53ab9c | 720 | StreamTcpInitConfig(TRUE); |
6a53ab9c | 721 | |
f2f9b832 PR |
722 | uint32_t u; |
723 | for (u = 0; u < ftplen1; u++) { | |
724 | uint8_t flags = 0; | |
725 | ||
726 | if (u == 0) flags = STREAM_TOSERVER|STREAM_START; | |
727 | else if (u == (ftplen1 - 1)) flags = STREAM_TOSERVER|STREAM_EOF; | |
728 | else flags = STREAM_TOSERVER; | |
729 | ||
6530c3d0 | 730 | FLOWLOCK_WRLOCK(&f); |
675fa564 GL |
731 | r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_FTP, flags, |
732 | &ftpbuf1[u], 1); | |
f2f9b832 | 733 | if (r != 0) { |
f729d6f7 | 734 | SCLogDebug("toserver chunk %" PRIu32 " returned %" PRId32 ", expected 0: ", u, r); |
f2f9b832 | 735 | result = 0; |
6530c3d0 | 736 | FLOWLOCK_UNLOCK(&f); |
f2f9b832 PR |
737 | goto end; |
738 | } | |
6530c3d0 | 739 | FLOWLOCK_UNLOCK(&f); |
f2f9b832 PR |
740 | } |
741 | ||
06904c90 | 742 | FtpState *ftp_state = f.alstate; |
f2f9b832 | 743 | if (ftp_state == NULL) { |
f729d6f7 | 744 | SCLogDebug("no ftp state: "); |
f2f9b832 PR |
745 | result = 0; |
746 | goto end; | |
747 | } | |
748 | ||
749 | if (ftp_state->command != FTP_COMMAND_PORT) { | |
f729d6f7 | 750 | SCLogDebug("expected command %" PRIu32 ", got %" PRIu32 ": ", FTP_COMMAND_PORT, ftp_state->command); |
f2f9b832 PR |
751 | result = 0; |
752 | goto end; | |
753 | } | |
754 | ||
755 | end: | |
429c6388 | 756 | if (alp_tctx != NULL) |
fdefb65b | 757 | AppLayerParserThreadCtxFree(alp_tctx); |
6a53ab9c | 758 | StreamTcpFreeConfig(TRUE); |
0e4235cc | 759 | FLOW_DESTROY(&f); |
f2f9b832 PR |
760 | return result; |
761 | } | |
762 | #endif /* UNITTESTS */ | |
763 | ||
8f1d7503 KS |
764 | void FTPParserRegisterTests(void) |
765 | { | |
f2f9b832 | 766 | #ifdef UNITTESTS |
796dd522 JI |
767 | UtRegisterTest("FTPParserTest01", FTPParserTest01); |
768 | UtRegisterTest("FTPParserTest03", FTPParserTest03); | |
769 | UtRegisterTest("FTPParserTest06", FTPParserTest06); | |
770 | UtRegisterTest("FTPParserTest07", FTPParserTest07); | |
771 | UtRegisterTest("FTPParserTest10", FTPParserTest10); | |
f2f9b832 PR |
772 | #endif /* UNITTESTS */ |
773 | } | |
774 |