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