]>
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 AS |
221 | |
222 | state->input = input; | |
223 | state->input_len = input_len; | |
224 | /* toserver stream */ | |
225 | state->direction = 0; | |
226 | ||
227 | while (FTPGetLine(state) >= 0) { | |
228 | FTPParseRequestCommand(state, | |
229 | state->current_line, state->current_line_len); | |
230 | if (state->command == FTP_COMMAND_PORT) { | |
231 | if (state->current_line_len > state->port_line_size) { | |
1f07d152 EL |
232 | ptmp = SCRealloc(state->port_line, state->current_line_len); |
233 | if (ptmp == NULL) { | |
234 | SCFree(state->port_line); | |
235 | state->port_line = NULL; | |
6ea8ac44 AS |
236 | state->port_line_size = 0; |
237 | return 0; | |
238 | } | |
1f07d152 EL |
239 | state->port_line = ptmp; |
240 | ||
6ea8ac44 AS |
241 | state->port_line_size = state->current_line_len; |
242 | } | |
243 | memcpy(state->port_line, state->current_line, | |
244 | state->current_line_len); | |
245 | state->port_line_len = state->current_line_len; | |
246 | } | |
f2f9b832 | 247 | } |
f2f9b832 | 248 | |
f2f9b832 PR |
249 | return 1; |
250 | } | |
251 | ||
252 | /** | |
253 | * \brief This function is called to retrieve a ftp response | |
254 | * \param ftp_state the ftp state structure for the parser | |
255 | * \param input input line of the command | |
256 | * \param input_len length of the request | |
257 | * \param output the resulting output | |
258 | * | |
259 | * \retval 1 when the command is parsed, 0 otherwise | |
260 | */ | |
9634e60e | 261 | static int FTPParseResponse(Flow *f, void *ftp_state, AppLayerParserState *pstate, |
f2f9b832 | 262 | uint8_t *input, uint32_t input_len, |
429c6388 | 263 | void *local_data) |
9a6aef45 | 264 | { |
f2f9b832 PR |
265 | return 1; |
266 | } | |
267 | ||
268 | #ifdef DEBUG | |
5532af46 | 269 | static SCMutex ftp_state_mem_lock = SCMUTEX_INITIALIZER; |
f2f9b832 PR |
270 | static uint64_t ftp_state_memuse = 0; |
271 | static uint64_t ftp_state_memcnt = 0; | |
272 | #endif | |
273 | ||
8f1d7503 KS |
274 | static void *FTPStateAlloc(void) |
275 | { | |
25a3a5c6 | 276 | void *s = SCMalloc(sizeof(FtpState)); |
e176be6f | 277 | if (unlikely(s == NULL)) |
f2f9b832 PR |
278 | return NULL; |
279 | ||
280 | memset(s, 0, sizeof(FtpState)); | |
281 | ||
282 | #ifdef DEBUG | |
283 | SCMutexLock(&ftp_state_mem_lock); | |
284 | ftp_state_memcnt++; | |
285 | ftp_state_memuse+=sizeof(FtpState); | |
286 | SCMutexUnlock(&ftp_state_mem_lock); | |
287 | #endif | |
288 | return s; | |
289 | } | |
290 | ||
8f1d7503 KS |
291 | static void FTPStateFree(void *s) |
292 | { | |
18954a2c PR |
293 | FtpState *fstate = (FtpState *) s; |
294 | if (fstate->port_line != NULL) | |
295 | SCFree(fstate->port_line); | |
b4ab9a0a VJ |
296 | if (fstate->line_state[0].db) |
297 | SCFree(fstate->line_state[0].db); | |
298 | if (fstate->line_state[1].db) | |
299 | SCFree(fstate->line_state[1].db); | |
25a3a5c6 | 300 | SCFree(s); |
f2f9b832 PR |
301 | #ifdef DEBUG |
302 | SCMutexLock(&ftp_state_mem_lock); | |
303 | ftp_state_memcnt--; | |
304 | ftp_state_memuse-=sizeof(FtpState); | |
305 | SCMutexUnlock(&ftp_state_mem_lock); | |
306 | #endif | |
307 | } | |
308 | ||
429c6388 AS |
309 | static int FTPRegisterPatternsForProtocolDetection(void) |
310 | { | |
311 | if (AppLayerProtoDetectPMRegisterPatternCI(IPPROTO_TCP, ALPROTO_FTP, | |
312 | "USER ", 5, 0, STREAM_TOSERVER) < 0) | |
313 | { | |
314 | return -1; | |
315 | } | |
316 | if (AppLayerProtoDetectPMRegisterPatternCI(IPPROTO_TCP, ALPROTO_FTP, | |
317 | "PASS ", 5, 0, STREAM_TOSERVER) < 0) | |
318 | { | |
319 | return -1; | |
320 | } | |
321 | if (AppLayerProtoDetectPMRegisterPatternCI(IPPROTO_TCP, ALPROTO_FTP, | |
322 | "PORT ", 5, 0, STREAM_TOSERVER) < 0) | |
323 | { | |
324 | return -1; | |
325 | } | |
326 | ||
327 | return 0; | |
328 | } | |
329 | ||
8f1d7503 KS |
330 | void RegisterFTPParsers(void) |
331 | { | |
10966245 AS |
332 | char *proto_name = "ftp"; |
333 | ||
000ce98c | 334 | /** FTP */ |
429c6388 AS |
335 | if (AppLayerProtoDetectConfProtoDetectionEnabled("tcp", proto_name)) { |
336 | AppLayerProtoDetectRegisterProtocol(ALPROTO_FTP, proto_name); | |
337 | if (FTPRegisterPatternsForProtocolDetection() < 0 ) | |
338 | return; | |
ddde572f AS |
339 | } |
340 | ||
429c6388 AS |
341 | if (AppLayerParserConfParserEnabled("tcp", proto_name)) { |
342 | AppLayerParserRegisterParser(IPPROTO_TCP, ALPROTO_FTP, STREAM_TOSERVER, | |
343 | FTPParseRequest); | |
344 | AppLayerParserRegisterParser(IPPROTO_TCP, ALPROTO_FTP, STREAM_TOCLIENT, | |
345 | FTPParseResponse); | |
346 | AppLayerParserRegisterStateFuncs(IPPROTO_TCP, ALPROTO_FTP, FTPStateAlloc, FTPStateFree); | |
347 | AppLayerParserRegisterParserAcceptableDataDirection(IPPROTO_TCP, ALPROTO_FTP, STREAM_TOSERVER | STREAM_TOCLIENT); | |
ddde572f AS |
348 | } else { |
349 | SCLogInfo("Parsed disabled for %s protocol. Protocol detection" | |
350 | "still on.", proto_name); | |
351 | } | |
9faa4b74 | 352 | #ifdef UNITTESTS |
429c6388 | 353 | AppLayerParserRegisterProtocolUnittests(IPPROTO_TCP, ALPROTO_FTP, FTPParserRegisterTests); |
9faa4b74 | 354 | #endif |
f2f9b832 PR |
355 | } |
356 | ||
8f1d7503 KS |
357 | void FTPAtExitPrintStats(void) |
358 | { | |
f2f9b832 PR |
359 | #ifdef DEBUG |
360 | SCMutexLock(&ftp_state_mem_lock); | |
361 | SCLogDebug("ftp_state_memcnt %"PRIu64", ftp_state_memuse %"PRIu64"", | |
362 | ftp_state_memcnt, ftp_state_memuse); | |
363 | SCMutexUnlock(&ftp_state_mem_lock); | |
364 | #endif | |
365 | } | |
366 | ||
367 | /* UNITTESTS */ | |
368 | #ifdef UNITTESTS | |
369 | ||
370 | /** \test Send a get request in one chunk. */ | |
8f1d7503 KS |
371 | int FTPParserTest01(void) |
372 | { | |
f2f9b832 PR |
373 | int result = 1; |
374 | Flow f; | |
375 | uint8_t ftpbuf[] = "PORT 192,168,1,1,0,80\r\n"; | |
376 | uint32_t ftplen = sizeof(ftpbuf) - 1; /* minus the \0 */ | |
377 | TcpSession ssn; | |
8dbf7a0d | 378 | AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc(); |
f2f9b832 PR |
379 | |
380 | memset(&f, 0, sizeof(f)); | |
381 | memset(&ssn, 0, sizeof(ssn)); | |
cc76aa4b WM |
382 | |
383 | FLOW_INITIALIZE(&f); | |
f2f9b832 | 384 | f.protoctx = (void *)&ssn; |
429c6388 | 385 | f.proto = IPPROTO_TCP; |
f2f9b832 | 386 | |
6a53ab9c | 387 | StreamTcpInitConfig(TRUE); |
6a53ab9c | 388 | |
cd3e32ce | 389 | SCMutexLock(&f.m); |
429c6388 | 390 | int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_FTP, STREAM_TOSERVER|STREAM_EOF, ftpbuf, ftplen); |
f2f9b832 | 391 | if (r != 0) { |
f729d6f7 | 392 | SCLogDebug("toserver chunk 1 returned %" PRId32 ", expected 0: ", r); |
f2f9b832 | 393 | result = 0; |
cd3e32ce | 394 | SCMutexUnlock(&f.m); |
f2f9b832 PR |
395 | goto end; |
396 | } | |
cd3e32ce | 397 | SCMutexUnlock(&f.m); |
f2f9b832 | 398 | |
06904c90 | 399 | FtpState *ftp_state = f.alstate; |
f2f9b832 | 400 | if (ftp_state == NULL) { |
f729d6f7 | 401 | SCLogDebug("no ftp state: "); |
f2f9b832 PR |
402 | result = 0; |
403 | goto end; | |
404 | } | |
405 | ||
406 | if (ftp_state->command != FTP_COMMAND_PORT) { | |
f729d6f7 | 407 | SCLogDebug("expected command %" PRIu32 ", got %" PRIu32 ": ", FTP_COMMAND_PORT, ftp_state->command); |
f2f9b832 PR |
408 | result = 0; |
409 | goto end; | |
410 | } | |
411 | ||
412 | end: | |
429c6388 | 413 | if (alp_tctx != NULL) |
fdefb65b | 414 | AppLayerParserThreadCtxFree(alp_tctx); |
6a53ab9c | 415 | StreamTcpFreeConfig(TRUE); |
0e4235cc | 416 | FLOW_DESTROY(&f); |
f2f9b832 PR |
417 | return result; |
418 | } | |
419 | ||
420 | /** \test Send a splitted get request. */ | |
8f1d7503 KS |
421 | int FTPParserTest03(void) |
422 | { | |
f2f9b832 PR |
423 | int result = 1; |
424 | Flow f; | |
425 | uint8_t ftpbuf1[] = "POR"; | |
426 | uint32_t ftplen1 = sizeof(ftpbuf1) - 1; /* minus the \0 */ | |
427 | uint8_t ftpbuf2[] = "T 192,168,1"; | |
428 | uint32_t ftplen2 = sizeof(ftpbuf2) - 1; /* minus the \0 */ | |
429 | uint8_t ftpbuf3[] = "1,1,10,20\r\n"; | |
430 | uint32_t ftplen3 = sizeof(ftpbuf3) - 1; /* minus the \0 */ | |
431 | TcpSession ssn; | |
8dbf7a0d | 432 | AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc(); |
f2f9b832 PR |
433 | |
434 | memset(&f, 0, sizeof(f)); | |
435 | memset(&ssn, 0, sizeof(ssn)); | |
f2f9b832 | 436 | f.protoctx = (void *)&ssn; |
429c6388 | 437 | f.proto = IPPROTO_TCP; |
f2f9b832 | 438 | |
6a53ab9c | 439 | StreamTcpInitConfig(TRUE); |
6a53ab9c | 440 | |
cd3e32ce | 441 | SCMutexLock(&f.m); |
429c6388 | 442 | int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_FTP, STREAM_TOSERVER|STREAM_START, ftpbuf1, ftplen1); |
f2f9b832 | 443 | if (r != 0) { |
f729d6f7 | 444 | SCLogDebug("toserver chunk 1 returned %" PRId32 ", expected 0: ", r); |
f2f9b832 | 445 | result = 0; |
cd3e32ce | 446 | SCMutexUnlock(&f.m); |
f2f9b832 PR |
447 | goto end; |
448 | } | |
cd3e32ce | 449 | SCMutexUnlock(&f.m); |
f2f9b832 | 450 | |
cd3e32ce | 451 | SCMutexLock(&f.m); |
429c6388 | 452 | r = AppLayerParserParse(alp_tctx, &f, ALPROTO_FTP, STREAM_TOSERVER, ftpbuf2, ftplen2); |
f2f9b832 | 453 | if (r != 0) { |
f729d6f7 | 454 | SCLogDebug("toserver chunk 2 returned %" PRId32 ", expected 0: ", r); |
f2f9b832 | 455 | result = 0; |
cd3e32ce | 456 | SCMutexUnlock(&f.m); |
f2f9b832 PR |
457 | goto end; |
458 | } | |
cd3e32ce | 459 | SCMutexUnlock(&f.m); |
f2f9b832 | 460 | |
cd3e32ce | 461 | SCMutexLock(&f.m); |
429c6388 | 462 | r = AppLayerParserParse(alp_tctx, &f, ALPROTO_FTP, STREAM_TOSERVER|STREAM_EOF, ftpbuf3, ftplen3); |
f2f9b832 | 463 | if (r != 0) { |
f729d6f7 | 464 | SCLogDebug("toserver chunk 3 returned %" PRId32 ", expected 0: ", r); |
f2f9b832 | 465 | result = 0; |
cd3e32ce | 466 | SCMutexUnlock(&f.m); |
f2f9b832 PR |
467 | goto end; |
468 | } | |
cd3e32ce | 469 | SCMutexUnlock(&f.m); |
f2f9b832 | 470 | |
06904c90 | 471 | FtpState *ftp_state = f.alstate; |
f2f9b832 | 472 | if (ftp_state == NULL) { |
f729d6f7 | 473 | SCLogDebug("no ftp state: "); |
f2f9b832 PR |
474 | result = 0; |
475 | goto end; | |
476 | } | |
477 | ||
478 | if (ftp_state->command != FTP_COMMAND_PORT) { | |
f729d6f7 | 479 | SCLogDebug("expected command %" PRIu32 ", got %" PRIu32 ": ", FTP_COMMAND_PORT, ftp_state->command); |
f2f9b832 PR |
480 | result = 0; |
481 | goto end; | |
482 | } | |
483 | ||
484 | end: | |
429c6388 | 485 | if (alp_tctx != NULL) |
fdefb65b | 486 | AppLayerParserThreadCtxFree(alp_tctx); |
6a53ab9c | 487 | StreamTcpFreeConfig(TRUE); |
f2f9b832 PR |
488 | return result; |
489 | } | |
490 | ||
491 | /** \test See how it deals with an incomplete request. */ | |
8f1d7503 KS |
492 | int FTPParserTest06(void) |
493 | { | |
f2f9b832 PR |
494 | int result = 1; |
495 | Flow f; | |
496 | uint8_t ftpbuf1[] = "PORT"; | |
497 | uint32_t ftplen1 = sizeof(ftpbuf1) - 1; /* minus the \0 */ | |
498 | TcpSession ssn; | |
8dbf7a0d | 499 | AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc(); |
f2f9b832 PR |
500 | |
501 | memset(&f, 0, sizeof(f)); | |
502 | memset(&ssn, 0, sizeof(ssn)); | |
cc76aa4b WM |
503 | |
504 | FLOW_INITIALIZE(&f); | |
f2f9b832 | 505 | f.protoctx = (void *)&ssn; |
429c6388 | 506 | f.proto = IPPROTO_TCP; |
f2f9b832 | 507 | |
6a53ab9c | 508 | StreamTcpInitConfig(TRUE); |
6a53ab9c | 509 | |
cd3e32ce | 510 | SCMutexLock(&f.m); |
429c6388 | 511 | int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_FTP, STREAM_TOSERVER|STREAM_START|STREAM_EOF, ftpbuf1, ftplen1); |
f2f9b832 | 512 | if (r != 0) { |
f729d6f7 | 513 | SCLogDebug("toserver chunk 1 returned %" PRId32 ", expected 0: ", r); |
f2f9b832 | 514 | result = 0; |
cd3e32ce | 515 | SCMutexUnlock(&f.m); |
f2f9b832 PR |
516 | goto end; |
517 | } | |
cd3e32ce | 518 | SCMutexUnlock(&f.m); |
f2f9b832 | 519 | |
06904c90 | 520 | FtpState *ftp_state = f.alstate; |
f2f9b832 | 521 | if (ftp_state == NULL) { |
f729d6f7 | 522 | SCLogDebug("no ftp state: "); |
f2f9b832 PR |
523 | result = 0; |
524 | goto end; | |
525 | } | |
526 | ||
527 | if (ftp_state->command != FTP_COMMAND_UNKNOWN) { | |
f729d6f7 | 528 | SCLogDebug("expected command %" PRIu32 ", got %" PRIu32 ": ", FTP_COMMAND_UNKNOWN, ftp_state->command); |
f2f9b832 PR |
529 | result = 0; |
530 | goto end; | |
531 | } | |
532 | ||
533 | end: | |
429c6388 | 534 | if (alp_tctx != NULL) |
fdefb65b | 535 | AppLayerParserThreadCtxFree(alp_tctx); |
6a53ab9c | 536 | StreamTcpFreeConfig(TRUE); |
0e4235cc | 537 | FLOW_DESTROY(&f); |
f2f9b832 PR |
538 | return result; |
539 | } | |
540 | ||
541 | /** \test See how it deals with an incomplete request in multiple chunks. */ | |
8f1d7503 KS |
542 | int FTPParserTest07(void) |
543 | { | |
f2f9b832 PR |
544 | int result = 1; |
545 | Flow f; | |
546 | uint8_t ftpbuf1[] = "PO"; | |
547 | uint32_t ftplen1 = sizeof(ftpbuf1) - 1; /* minus the \0 */ | |
548 | uint8_t ftpbuf2[] = "RT\r\n"; | |
549 | uint32_t ftplen2 = sizeof(ftpbuf2) - 1; /* minus the \0 */ | |
550 | TcpSession ssn; | |
8dbf7a0d | 551 | AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc(); |
f2f9b832 PR |
552 | |
553 | memset(&f, 0, sizeof(f)); | |
554 | memset(&ssn, 0, sizeof(ssn)); | |
cc76aa4b WM |
555 | |
556 | FLOW_INITIALIZE(&f); | |
f2f9b832 | 557 | f.protoctx = (void *)&ssn; |
429c6388 | 558 | f.proto = IPPROTO_TCP; |
f2f9b832 | 559 | |
6a53ab9c | 560 | StreamTcpInitConfig(TRUE); |
6a53ab9c | 561 | |
cd3e32ce | 562 | SCMutexLock(&f.m); |
429c6388 | 563 | int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_FTP, STREAM_TOSERVER|STREAM_START, ftpbuf1, ftplen1); |
f2f9b832 | 564 | if (r != 0) { |
f729d6f7 | 565 | SCLogDebug("toserver chunk 1 returned %" PRId32 ", expected 0: ", r); |
f2f9b832 | 566 | result = 0; |
cd3e32ce | 567 | SCMutexUnlock(&f.m); |
f2f9b832 PR |
568 | goto end; |
569 | } | |
cd3e32ce | 570 | SCMutexUnlock(&f.m); |
f2f9b832 | 571 | |
cd3e32ce | 572 | SCMutexLock(&f.m); |
429c6388 | 573 | r = AppLayerParserParse(alp_tctx, &f, ALPROTO_FTP, STREAM_TOSERVER|STREAM_EOF, ftpbuf2, ftplen2); |
f2f9b832 | 574 | if (r != 0) { |
f729d6f7 | 575 | SCLogDebug("toserver chunk 2 returned %" PRId32 ", expected 0: ", r); |
f2f9b832 | 576 | result = 0; |
cd3e32ce | 577 | SCMutexUnlock(&f.m); |
f2f9b832 PR |
578 | goto end; |
579 | } | |
cd3e32ce | 580 | SCMutexUnlock(&f.m); |
f2f9b832 | 581 | |
06904c90 | 582 | FtpState *ftp_state = f.alstate; |
f2f9b832 | 583 | if (ftp_state == NULL) { |
f729d6f7 | 584 | SCLogDebug("no ftp state: "); |
f2f9b832 PR |
585 | result = 0; |
586 | goto end; | |
587 | } | |
588 | ||
6ea8ac44 AS |
589 | if (ftp_state->command != FTP_COMMAND_PORT) { |
590 | SCLogDebug("expected command %" PRIu32 ", got %" PRIu32 ": ", | |
591 | FTP_COMMAND_PORT, ftp_state->command); | |
f2f9b832 PR |
592 | result = 0; |
593 | goto end; | |
594 | } | |
595 | ||
596 | end: | |
429c6388 | 597 | if (alp_tctx != NULL) |
fdefb65b | 598 | AppLayerParserThreadCtxFree(alp_tctx); |
6a53ab9c | 599 | StreamTcpFreeConfig(TRUE); |
0e4235cc | 600 | FLOW_DESTROY(&f); |
f2f9b832 PR |
601 | return result; |
602 | } | |
603 | ||
604 | /** \test Test case where chunks are smaller than the delim length and the | |
605 | * last chunk is supposed to match the delim. */ | |
8f1d7503 KS |
606 | int FTPParserTest10(void) |
607 | { | |
f2f9b832 PR |
608 | int result = 1; |
609 | Flow f; | |
610 | uint8_t ftpbuf1[] = "PORT 1,2,3,4,5,6\r\n"; | |
611 | uint32_t ftplen1 = sizeof(ftpbuf1) - 1; /* minus the \0 */ | |
612 | TcpSession ssn; | |
8dbf7a0d | 613 | AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc(); |
f2f9b832 PR |
614 | int r = 0; |
615 | memset(&f, 0, sizeof(f)); | |
616 | memset(&ssn, 0, sizeof(ssn)); | |
cc76aa4b WM |
617 | |
618 | FLOW_INITIALIZE(&f); | |
f2f9b832 | 619 | f.protoctx = (void *)&ssn; |
429c6388 | 620 | f.proto = IPPROTO_TCP; |
f2f9b832 | 621 | |
6a53ab9c | 622 | StreamTcpInitConfig(TRUE); |
6a53ab9c | 623 | |
f2f9b832 PR |
624 | uint32_t u; |
625 | for (u = 0; u < ftplen1; u++) { | |
626 | uint8_t flags = 0; | |
627 | ||
628 | if (u == 0) flags = STREAM_TOSERVER|STREAM_START; | |
629 | else if (u == (ftplen1 - 1)) flags = STREAM_TOSERVER|STREAM_EOF; | |
630 | else flags = STREAM_TOSERVER; | |
631 | ||
cd3e32ce | 632 | SCMutexLock(&f.m); |
429c6388 | 633 | r = AppLayerParserParse(alp_tctx, &f, ALPROTO_FTP, flags, &ftpbuf1[u], 1); |
f2f9b832 | 634 | if (r != 0) { |
f729d6f7 | 635 | SCLogDebug("toserver chunk %" PRIu32 " returned %" PRId32 ", expected 0: ", u, r); |
f2f9b832 | 636 | result = 0; |
cd3e32ce | 637 | SCMutexUnlock(&f.m); |
f2f9b832 PR |
638 | goto end; |
639 | } | |
cd3e32ce | 640 | SCMutexUnlock(&f.m); |
f2f9b832 PR |
641 | } |
642 | ||
06904c90 | 643 | FtpState *ftp_state = f.alstate; |
f2f9b832 | 644 | if (ftp_state == NULL) { |
f729d6f7 | 645 | SCLogDebug("no ftp state: "); |
f2f9b832 PR |
646 | result = 0; |
647 | goto end; | |
648 | } | |
649 | ||
650 | if (ftp_state->command != FTP_COMMAND_PORT) { | |
f729d6f7 | 651 | SCLogDebug("expected command %" PRIu32 ", got %" PRIu32 ": ", FTP_COMMAND_PORT, ftp_state->command); |
f2f9b832 PR |
652 | result = 0; |
653 | goto end; | |
654 | } | |
655 | ||
656 | end: | |
429c6388 | 657 | if (alp_tctx != NULL) |
fdefb65b | 658 | AppLayerParserThreadCtxFree(alp_tctx); |
6a53ab9c | 659 | StreamTcpFreeConfig(TRUE); |
0e4235cc | 660 | FLOW_DESTROY(&f); |
f2f9b832 PR |
661 | return result; |
662 | } | |
663 | #endif /* UNITTESTS */ | |
664 | ||
8f1d7503 KS |
665 | void FTPParserRegisterTests(void) |
666 | { | |
f2f9b832 PR |
667 | #ifdef UNITTESTS |
668 | UtRegisterTest("FTPParserTest01", FTPParserTest01, 1); | |
669 | UtRegisterTest("FTPParserTest03", FTPParserTest03, 1); | |
670 | UtRegisterTest("FTPParserTest06", FTPParserTest06, 1); | |
671 | UtRegisterTest("FTPParserTest07", FTPParserTest07, 1); | |
672 | UtRegisterTest("FTPParserTest10", FTPParserTest10, 1); | |
673 | #endif /* UNITTESTS */ | |
674 | } | |
675 |