/src/suricata7/src/app-layer-htp-file.c
Line | Count | Source |
1 | | /* Copyright (C) 2007-2021 Open Information Security Foundation |
2 | | * |
3 | | * You can copy, redistribute or modify this Program under the terms of |
4 | | * the GNU General Public License version 2 as published by the Free |
5 | | * Software Foundation. |
6 | | * |
7 | | * This program is distributed in the hope that it will be useful, |
8 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
9 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
10 | | * GNU General Public License for more details. |
11 | | * |
12 | | * You should have received a copy of the GNU General Public License |
13 | | * version 2 along with this program; if not, write to the Free Software |
14 | | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA |
15 | | * 02110-1301, USA. |
16 | | */ |
17 | | |
18 | | /** |
19 | | * \file |
20 | | * |
21 | | * \author Victor Julien <victor@inliniac.net> |
22 | | * |
23 | | * This file provides HTTP protocol file handling support for the engine |
24 | | * using the HTP library. |
25 | | */ |
26 | | |
27 | | #include "suricata-common.h" |
28 | | #include "app-layer-htp-file.h" |
29 | | #include "app-layer-htp-range.h" |
30 | | #include "app-layer-events.h" |
31 | | #include "util-validate.h" |
32 | | |
33 | | extern StreamingBufferConfig htp_sbcfg; |
34 | | |
35 | | /** |
36 | | * \brief Open the file with "filename" and pass the first chunk |
37 | | * of data if any. |
38 | | * |
39 | | * \param s http state |
40 | | * \param filename name of the file |
41 | | * \param filename_len length of the name |
42 | | * \param data data chunk (if any) |
43 | | * \param data_len length of the data portion |
44 | | * \param direction flow direction |
45 | | * |
46 | | * \retval 0 ok |
47 | | * \retval -1 error |
48 | | * \retval -2 not handling files on this flow |
49 | | */ |
50 | | int HTPFileOpen(HtpState *s, HtpTxUserData *tx, const uint8_t *filename, uint16_t filename_len, |
51 | | const uint8_t *data, uint32_t data_len, uint64_t txid, uint8_t direction) |
52 | 384k | { |
53 | 384k | int retval = 0; |
54 | 384k | uint16_t flags = 0; |
55 | 384k | FileContainer *files = NULL; |
56 | | |
57 | 384k | SCLogDebug("data %p data_len %"PRIu32, data, data_len); |
58 | | |
59 | 384k | if (direction & STREAM_TOCLIENT) { |
60 | 350k | files = &tx->files_tc; |
61 | 350k | flags = FileFlowFlagsToFlags(tx->tx_data.file_flags, STREAM_TOCLIENT); |
62 | | |
63 | | // we shall not open a new file if there is a current one |
64 | 350k | DEBUG_VALIDATE_BUG_ON(tx->file_range != NULL); |
65 | 350k | } else { |
66 | 33.7k | files = &tx->files_ts; |
67 | 33.7k | flags = FileFlowFlagsToFlags(tx->tx_data.file_flags, STREAM_TOSERVER); |
68 | 33.7k | } |
69 | | |
70 | 384k | if (FileOpenFileWithId(files, &htp_sbcfg, s->file_track_id++, filename, filename_len, data, |
71 | 384k | data_len, flags) != 0) { |
72 | 0 | retval = -1; |
73 | 384k | } else { |
74 | 384k | const HTPCfgDir *cfg; |
75 | 384k | if (direction & STREAM_TOCLIENT) { |
76 | 350k | cfg = &s->cfg->response; |
77 | 350k | } else { |
78 | 33.7k | cfg = &s->cfg->request; |
79 | 33.7k | } |
80 | 384k | FileSetInspectSizes(files->tail, cfg->inspect_window, cfg->inspect_min_size); |
81 | 384k | } |
82 | | |
83 | 384k | tx->tx_data.files_opened++; |
84 | | |
85 | 384k | SCReturnInt(retval); |
86 | 384k | } |
87 | | |
88 | | /** |
89 | | * Performs parsing of the content-range value |
90 | | * |
91 | | * @param[in] rawvalue |
92 | | * @param[out] range |
93 | | * |
94 | | * @return HTP_OK on success, HTP_ERROR on failure. |
95 | | */ |
96 | | int HTPParseContentRange(bstr *rawvalue, HTTPContentRange *range) |
97 | 41.9k | { |
98 | 41.9k | uint32_t len = bstr_len(rawvalue); |
99 | 41.9k | return rs_http_parse_content_range(range, bstr_ptr(rawvalue), len); |
100 | 41.9k | } |
101 | | |
102 | | /** |
103 | | * Performs parsing + checking of the content-range value |
104 | | * |
105 | | * @param[in] rawvalue |
106 | | * @param[out] range |
107 | | * |
108 | | * @return HTP_OK on success, HTP_ERROR, -2, -3 on failure. |
109 | | */ |
110 | | static int HTPParseAndCheckContentRange( |
111 | | bstr *rawvalue, HTTPContentRange *range, HtpState *s, HtpTxUserData *htud) |
112 | 60.3k | { |
113 | 60.3k | int r = HTPParseContentRange(rawvalue, range); |
114 | 60.3k | if (r != 0) { |
115 | 6.63k | AppLayerDecoderEventsSetEventRaw(&htud->tx_data.events, HTTP_DECODER_EVENT_RANGE_INVALID); |
116 | 6.63k | s->events++; |
117 | 6.63k | SCLogDebug("parsing range failed, going back to normal file"); |
118 | 6.63k | return r; |
119 | 6.63k | } |
120 | | /* crparsed.end <= 0 means a range with only size |
121 | | * this is the answer to an unsatisfied range with the whole file |
122 | | * crparsed.size <= 0 means an unknown size, so we do not know |
123 | | * when to close it... |
124 | | */ |
125 | 53.7k | if (range->end <= 0 || range->size <= 0) { |
126 | 782 | SCLogDebug("range without all information"); |
127 | 782 | return -2; |
128 | 52.9k | } else if (range->end == range->size - 1 && range->start == 0) { |
129 | 878 | SCLogDebug("range without all information"); |
130 | 878 | return -3; |
131 | 52.0k | } else if (range->start > range->end || range->end > range->size - 1) { |
132 | 1.79k | AppLayerDecoderEventsSetEventRaw(&htud->tx_data.events, HTTP_DECODER_EVENT_RANGE_INVALID); |
133 | 1.79k | s->events++; |
134 | 1.79k | SCLogDebug("invalid range"); |
135 | 1.79k | return -4; |
136 | 1.79k | } |
137 | 50.2k | return r; |
138 | 53.7k | } |
139 | | |
140 | | /** |
141 | | * \brief Sets range for a file |
142 | | * |
143 | | * \param s http state |
144 | | * \param rawvalue raw header value |
145 | | * |
146 | | * \retval 0 ok |
147 | | * \retval -1 error |
148 | | */ |
149 | | int HTPFileOpenWithRange(HtpState *s, HtpTxUserData *txud, const uint8_t *filename, |
150 | | uint16_t filename_len, const uint8_t *data, uint32_t data_len, uint64_t txid, |
151 | | bstr *rawvalue, HtpTxUserData *htud) |
152 | 11.6k | { |
153 | 11.6k | SCEnter(); |
154 | 11.6k | uint16_t flags; |
155 | | |
156 | 11.6k | DEBUG_VALIDATE_BUG_ON(s == NULL); |
157 | | |
158 | | // This function is only called STREAM_TOCLIENT from HtpResponseBodyHandle |
159 | 11.6k | HTTPContentRange crparsed; |
160 | 11.6k | if (HTPParseAndCheckContentRange(rawvalue, &crparsed, s, htud) != 0) { |
161 | | // range is invalid, fall back to classic open |
162 | 412 | return HTPFileOpen(s, txud, filename, filename_len, data, data_len, txid, STREAM_TOCLIENT); |
163 | 412 | } |
164 | 11.2k | flags = FileFlowToFlags(s->f, STREAM_TOCLIENT); |
165 | 11.2k | FileContainer *files = &txud->files_tc; |
166 | | |
167 | | // we open a file for this specific range |
168 | 11.2k | if (FileOpenFileWithId(files, &htp_sbcfg, s->file_track_id++, filename, filename_len, data, |
169 | 11.2k | data_len, flags) != 0) { |
170 | 0 | SCReturnInt(-1); |
171 | 11.2k | } else { |
172 | 11.2k | const HTPCfgDir *cfg = &s->cfg->response; |
173 | 11.2k | FileSetInspectSizes(files->tail, cfg->inspect_window, cfg->inspect_min_size); |
174 | 11.2k | } |
175 | 11.2k | txud->tx_data.files_opened++; |
176 | | |
177 | 11.2k | if (FileSetRange(files, crparsed.start, crparsed.end) < 0) { |
178 | 0 | SCLogDebug("set range failed"); |
179 | 0 | } |
180 | | |
181 | | // Then, we will try to handle reassembly of different ranges of the same file |
182 | | // TODO have the caller pass directly the tx |
183 | 11.2k | htp_tx_t *tx = htp_list_get(s->conn->transactions, txid - s->tx_freed); |
184 | 11.2k | if (!tx) { |
185 | 0 | SCReturnInt(-1); |
186 | 0 | } |
187 | 11.2k | uint8_t *keyurl; |
188 | 11.2k | uint32_t keylen; |
189 | 11.2k | if (tx->request_hostname != NULL) { |
190 | 8.57k | keylen = bstr_len(tx->request_hostname) + filename_len; |
191 | 8.57k | keyurl = SCMalloc(keylen); |
192 | 8.57k | if (keyurl == NULL) { |
193 | 0 | SCReturnInt(-1); |
194 | 0 | } |
195 | 8.57k | memcpy(keyurl, bstr_ptr(tx->request_hostname), bstr_len(tx->request_hostname)); |
196 | 8.57k | memcpy(keyurl + bstr_len(tx->request_hostname), filename, filename_len); |
197 | 8.57k | } else { |
198 | | // do not reassemble file without host info |
199 | 2.62k | SCReturnInt(0); |
200 | 2.62k | } |
201 | 8.57k | DEBUG_VALIDATE_BUG_ON(htud->file_range); |
202 | 8.57k | htud->file_range = HttpRangeContainerOpenFile(keyurl, keylen, s->f, &crparsed, &htp_sbcfg, |
203 | 8.57k | filename, filename_len, flags, data, data_len); |
204 | 8.57k | SCFree(keyurl); |
205 | 8.57k | if (htud->file_range == NULL) { |
206 | 0 | SCReturnInt(-1); |
207 | 0 | } |
208 | 8.57k | SCReturnInt(0); |
209 | 8.57k | } |
210 | | |
211 | | /** |
212 | | * \brief Store a chunk of data in the flow |
213 | | * |
214 | | * \param s HtpState |
215 | | * \param tx HtpTxUserData |
216 | | * \param data data chunk (if any) |
217 | | * \param data_len length of the data portion |
218 | | * \param direction flow direction |
219 | | * |
220 | | * \retval 0 ok |
221 | | * \retval -1 error |
222 | | * \retval -2 file doesn't need storing |
223 | | */ |
224 | | int HTPFileStoreChunk( |
225 | | HtpState *s, HtpTxUserData *tx, const uint8_t *data, uint32_t data_len, uint8_t direction) |
226 | 2.92M | { |
227 | 2.92M | SCEnter(); |
228 | | |
229 | 2.92M | int retval = 0; |
230 | 2.92M | int result = 0; |
231 | 2.92M | FileContainer *files = NULL; |
232 | | |
233 | 2.92M | if (direction & STREAM_TOCLIENT) { |
234 | 2.76M | files = &tx->files_tc; |
235 | 2.76M | } else { |
236 | 164k | files = &tx->files_ts; |
237 | 164k | } |
238 | 2.92M | SCLogDebug("files %p data %p data_len %" PRIu32, files, data, data_len); |
239 | | |
240 | 2.92M | if (files == NULL) { |
241 | 0 | SCLogDebug("no files in state"); |
242 | 0 | retval = -1; |
243 | 0 | goto end; |
244 | 0 | } |
245 | | |
246 | 2.92M | if (tx->file_range != NULL) { |
247 | 195k | if (HttpRangeAppendData(&htp_sbcfg, tx->file_range, data, data_len) < 0) { |
248 | 0 | SCLogDebug("Failed to append data"); |
249 | 0 | } |
250 | 195k | } |
251 | | |
252 | 2.92M | result = FileAppendData(files, &htp_sbcfg, data, data_len); |
253 | 2.92M | if (result == -1) { |
254 | 674k | SCLogDebug("appending data failed"); |
255 | 674k | retval = -1; |
256 | 2.25M | } else if (result == -2) { |
257 | 0 | retval = -2; |
258 | 0 | } |
259 | 2.92M | SCLogDebug("result %u", result); |
260 | | |
261 | 2.92M | end: |
262 | 2.92M | SCReturnInt(retval); |
263 | 2.92M | } |
264 | | |
265 | | /** \brief close range, add reassembled file if possible |
266 | | * \retval true if reassembled file was added |
267 | | * \retval false if no reassembled file was added |
268 | | */ |
269 | | bool HTPFileCloseHandleRange(const StreamingBufferConfig *sbcfg, FileContainer *files, |
270 | | const uint16_t flags, HttpRangeContainerBlock *c, const uint8_t *data, uint32_t data_len) |
271 | 26.4k | { |
272 | 26.4k | bool added = false; |
273 | 26.4k | if (HttpRangeAppendData(sbcfg, c, data, data_len) < 0) { |
274 | 0 | SCLogDebug("Failed to append data"); |
275 | 0 | } |
276 | 26.4k | if (c->container) { |
277 | | // we only call HttpRangeClose if we may some new data |
278 | | // ie we do not call it if we skipped all this range request |
279 | 19.5k | THashDataLock(c->container->hdata); |
280 | 19.5k | if (c->container->error) { |
281 | 0 | SCLogDebug("range in ERROR state"); |
282 | 0 | } |
283 | 19.5k | File *ranged = HttpRangeClose(sbcfg, c, flags); |
284 | 19.5k | if (ranged && files) { |
285 | | /* HtpState owns the constructed file now */ |
286 | 268 | FileContainerAdd(files, ranged); |
287 | 268 | added = true; |
288 | 268 | } |
289 | 19.5k | DEBUG_VALIDATE_BUG_ON(ranged && !files); |
290 | 19.5k | THashDataUnlock(c->container->hdata); |
291 | 19.5k | } |
292 | 26.4k | return added; |
293 | 26.4k | } |
294 | | |
295 | | /** |
296 | | * \brief Close the file in the flow |
297 | | * |
298 | | * \param tx HtpTxUserData |
299 | | * \param data data chunk if any |
300 | | * \param data_len length of the data portion |
301 | | * \param flags flags to indicate events |
302 | | * \param direction flow direction |
303 | | * |
304 | | * Currently on the FLOW_FILE_TRUNCATED flag is implemented, indicating |
305 | | * that the file isn't complete but we're stopping storing it. |
306 | | * |
307 | | * \retval 0 ok |
308 | | * \retval -1 error |
309 | | * \retval -2 not storing files on this flow/tx |
310 | | */ |
311 | | int HTPFileClose(HtpState *s, HtpTxUserData *tx, const uint8_t *data, uint32_t data_len, |
312 | | uint8_t flags, uint8_t direction) |
313 | 428k | { |
314 | 428k | SCEnter(); |
315 | | |
316 | 428k | SCLogDebug("flags %04x FILE_TRUNCATED %s", flags, (flags & FILE_TRUNCATED) ? "true" : "false"); |
317 | | |
318 | 428k | int retval = 0; |
319 | 428k | int result = 0; |
320 | 428k | FileContainer *files = NULL; |
321 | | |
322 | 428k | if (direction & STREAM_TOCLIENT) { |
323 | 394k | files = &tx->files_tc; |
324 | 394k | } else { |
325 | 34.3k | files = &tx->files_ts; |
326 | 34.3k | } |
327 | | |
328 | 428k | SCLogDebug("files %p data %p data_len %" PRIu32, files, data, data_len); |
329 | | |
330 | 428k | if (files == NULL) { |
331 | 0 | retval = -1; |
332 | 0 | goto end; |
333 | 0 | } |
334 | | |
335 | 428k | result = FileCloseFile(files, &htp_sbcfg, data, data_len, flags); |
336 | 428k | if (result == -1) { |
337 | 2.73k | retval = -1; |
338 | 425k | } else if (result == -2) { |
339 | 0 | retval = -2; |
340 | 0 | } |
341 | 428k | SCLogDebug("result %u", result); |
342 | | |
343 | 428k | if (tx->file_range != NULL) { |
344 | 37.2k | bool added = |
345 | 37.2k | HTPFileCloseHandleRange(&htp_sbcfg, files, flags, tx->file_range, data, data_len); |
346 | 37.2k | if (added) { |
347 | 12 | tx->tx_data.files_opened++; |
348 | 12 | } |
349 | 37.2k | HttpRangeFreeBlock(tx->file_range); |
350 | 37.2k | tx->file_range = NULL; |
351 | 37.2k | } |
352 | | |
353 | 428k | end: |
354 | 428k | SCReturnInt(retval); |
355 | 428k | } |
356 | | |
357 | | #ifdef UNITTESTS |
358 | | #include "stream-tcp.h" |
359 | | #include "app-layer-parser.h" |
360 | | #include "util-unittest-helper.h" |
361 | | |
362 | | static int HTPFileParserTest01(void) |
363 | | { |
364 | | uint8_t httpbuf1[] = "POST /upload.cgi HTTP/1.1\r\n" |
365 | | "Host: www.server.lan\r\n" |
366 | | "Content-Type: multipart/form-data; boundary=---------------------------277531038314945\r\n" |
367 | | "Content-Length: 215\r\n" |
368 | | "\r\n" |
369 | | "-----------------------------277531038314945\r\n" |
370 | | "Content-Disposition: form-data; name=\"uploadfile_0\"; filename=\"somepicture1.jpg\"\r\n" |
371 | | "Content-Type: image/jpeg\r\n" |
372 | | "\r\n"; |
373 | | |
374 | | uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */ |
375 | | uint8_t httpbuf2[] = "filecontent\r\n" |
376 | | "-----------------------------277531038314945--"; |
377 | | uint32_t httplen2 = sizeof(httpbuf2) - 1; /* minus the \0 */ |
378 | | |
379 | | TcpSession ssn; |
380 | | AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc(); |
381 | | HtpState *http_state = NULL; |
382 | | memset(&ssn, 0, sizeof(ssn)); |
383 | | |
384 | | Flow *f = UTHBuildFlow(AF_INET, "1.2.3.4", "1.2.3.5", 1024, 80); |
385 | | FAIL_IF_NULL(f); |
386 | | f->protoctx = &ssn; |
387 | | f->proto = IPPROTO_TCP; |
388 | | f->alproto = ALPROTO_HTTP1; |
389 | | |
390 | | StreamTcpInitConfig(true); |
391 | | |
392 | | SCLogDebug("\n>>>> processing chunk 1 <<<<\n"); |
393 | | int r = AppLayerParserParse( |
394 | | NULL, alp_tctx, f, ALPROTO_HTTP1, STREAM_TOSERVER | STREAM_START, httpbuf1, httplen1); |
395 | | FAIL_IF_NOT(r == 0); |
396 | | |
397 | | SCLogDebug("\n>>>> processing chunk 2 size %u <<<<\n", httplen2); |
398 | | r = AppLayerParserParse( |
399 | | NULL, alp_tctx, f, ALPROTO_HTTP1, STREAM_TOSERVER | STREAM_EOF, httpbuf2, httplen2); |
400 | | FAIL_IF_NOT(r == 0); |
401 | | |
402 | | http_state = f->alstate; |
403 | | FAIL_IF_NULL(http_state); |
404 | | |
405 | | htp_tx_t *tx = AppLayerParserGetTx(IPPROTO_TCP, ALPROTO_HTTP1, http_state, 0); |
406 | | FAIL_IF_NULL(tx); |
407 | | FAIL_IF_NULL(tx->request_method); |
408 | | |
409 | | FAIL_IF(memcmp(bstr_util_strdup_to_c(tx->request_method), "POST", 4) != 0); |
410 | | |
411 | | AppLayerParserThreadCtxFree(alp_tctx); |
412 | | StreamTcpFreeConfig(true); |
413 | | UTHFreeFlow(f); |
414 | | PASS; |
415 | | } |
416 | | |
417 | | static int HTPFileParserTest02(void) |
418 | | { |
419 | | uint8_t httpbuf1[] = "POST /upload.cgi HTTP/1.1\r\n" |
420 | | "Host: www.server.lan\r\n" |
421 | | "Content-Type: multipart/form-data; boundary=---------------------------277531038314945\r\n" |
422 | | "Content-Length: 337\r\n" |
423 | | "\r\n"; |
424 | | uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */ |
425 | | |
426 | | uint8_t httpbuf2[] = "-----------------------------277531038314945\r\n" |
427 | | "Content-Disposition: form-data; name=\"email\"\r\n" |
428 | | "\r\n" |
429 | | "someaddress@somedomain.lan\r\n"; |
430 | | uint32_t httplen2 = sizeof(httpbuf2) - 1; /* minus the \0 */ |
431 | | |
432 | | uint8_t httpbuf3[] = "-----------------------------277531038314945\r\n" |
433 | | "Content-Disposition: form-data; name=\"uploadfile_0\"; filename=\"somepicture1.jpg\"\r\n" |
434 | | "Content-Type: image/jpeg\r\n" |
435 | | "\r\n"; |
436 | | uint32_t httplen3 = sizeof(httpbuf3) - 1; /* minus the \0 */ |
437 | | |
438 | | uint8_t httpbuf4[] = "filecontent\r\n" |
439 | | "-----------------------------277531038314945--"; |
440 | | uint32_t httplen4 = sizeof(httpbuf4) - 1; /* minus the \0 */ |
441 | | |
442 | | TcpSession ssn; |
443 | | HtpState *http_state = NULL; |
444 | | AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc(); |
445 | | |
446 | | memset(&ssn, 0, sizeof(ssn)); |
447 | | |
448 | | Flow *f = UTHBuildFlow(AF_INET, "1.2.3.4", "1.2.3.5", 1024, 80); |
449 | | FAIL_IF_NULL(f); |
450 | | f->protoctx = &ssn; |
451 | | f->proto = IPPROTO_TCP; |
452 | | f->alproto = ALPROTO_HTTP1; |
453 | | |
454 | | StreamTcpInitConfig(true); |
455 | | |
456 | | SCLogDebug("\n>>>> processing chunk 1 <<<<\n"); |
457 | | int r = AppLayerParserParse( |
458 | | NULL, alp_tctx, f, ALPROTO_HTTP1, STREAM_TOSERVER | STREAM_START, httpbuf1, httplen1); |
459 | | FAIL_IF_NOT(r == 0); |
460 | | |
461 | | SCLogDebug("\n>>>> processing chunk 2 size %u <<<<\n", httplen2); |
462 | | r = AppLayerParserParse( |
463 | | NULL, alp_tctx, f, ALPROTO_HTTP1, STREAM_TOSERVER | STREAM_EOF, httpbuf2, httplen2); |
464 | | FAIL_IF_NOT(r == 0); |
465 | | |
466 | | SCLogDebug("\n>>>> processing chunk 3 size %u <<<<\n", httplen3); |
467 | | r = AppLayerParserParse( |
468 | | NULL, alp_tctx, f, ALPROTO_HTTP1, STREAM_TOSERVER | STREAM_EOF, httpbuf3, httplen3); |
469 | | FAIL_IF_NOT(r == 0); |
470 | | |
471 | | SCLogDebug("\n>>>> processing chunk 4 size %u <<<<\n", httplen4); |
472 | | r = AppLayerParserParse( |
473 | | NULL, alp_tctx, f, ALPROTO_HTTP1, STREAM_TOSERVER | STREAM_EOF, httpbuf4, httplen4); |
474 | | FAIL_IF_NOT(r == 0); |
475 | | |
476 | | http_state = f->alstate; |
477 | | FAIL_IF_NULL(http_state); |
478 | | |
479 | | htp_tx_t *tx = AppLayerParserGetTx(IPPROTO_TCP, ALPROTO_HTTP1, http_state, 0); |
480 | | FAIL_IF_NULL(tx); |
481 | | FAIL_IF_NULL(tx->request_method); |
482 | | FAIL_IF(memcmp(bstr_util_strdup_to_c(tx->request_method), "POST", 4) != 0); |
483 | | HtpTxUserData *tx_ud = htp_tx_get_user_data(tx); |
484 | | FAIL_IF_NULL(tx_ud); |
485 | | FAIL_IF_NULL(tx_ud->files_ts.tail); |
486 | | FAIL_IF(tx_ud->files_ts.tail->state != FILE_STATE_CLOSED); |
487 | | |
488 | | AppLayerParserThreadCtxFree(alp_tctx); |
489 | | StreamTcpFreeConfig(true); |
490 | | UTHFreeFlow(f); |
491 | | PASS; |
492 | | } |
493 | | |
494 | | static int HTPFileParserTest03(void) |
495 | | { |
496 | | uint8_t httpbuf1[] = "POST /upload.cgi HTTP/1.1\r\n" |
497 | | "Host: www.server.lan\r\n" |
498 | | "Content-Type: multipart/form-data; boundary=---------------------------277531038314945\r\n" |
499 | | "Content-Length: 337\r\n" |
500 | | "\r\n"; |
501 | | uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */ |
502 | | |
503 | | uint8_t httpbuf2[] = "-----------------------------277531038314945\r\n" |
504 | | "Content-Disposition: form-data; name=\"email\"\r\n" |
505 | | "\r\n" |
506 | | "someaddress@somedomain.lan\r\n"; |
507 | | uint32_t httplen2 = sizeof(httpbuf2) - 1; /* minus the \0 */ |
508 | | |
509 | | uint8_t httpbuf3[] = "-----------------------------277531038314945\r\n" |
510 | | "Content-Disposition: form-data; name=\"uploadfile_0\"; filename=\"somepicture1.jpg\"\r\n" |
511 | | "Content-Type: image/jpeg\r\n" |
512 | | "\r\n"; |
513 | | uint32_t httplen3 = sizeof(httpbuf3) - 1; /* minus the \0 */ |
514 | | |
515 | | uint8_t httpbuf4[] = "file"; |
516 | | uint32_t httplen4 = sizeof(httpbuf4) - 1; /* minus the \0 */ |
517 | | |
518 | | uint8_t httpbuf5[] = "content\r\n"; |
519 | | uint32_t httplen5 = sizeof(httpbuf5) - 1; /* minus the \0 */ |
520 | | |
521 | | uint8_t httpbuf6[] = "-----------------------------277531038314945--"; |
522 | | uint32_t httplen6 = sizeof(httpbuf6) - 1; /* minus the \0 */ |
523 | | |
524 | | TcpSession ssn; |
525 | | HtpState *http_state = NULL; |
526 | | AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc(); |
527 | | |
528 | | memset(&ssn, 0, sizeof(ssn)); |
529 | | |
530 | | Flow *f = UTHBuildFlow(AF_INET, "1.2.3.4", "1.2.3.5", 1024, 80); |
531 | | FAIL_IF_NULL(f); |
532 | | f->protoctx = &ssn; |
533 | | f->proto = IPPROTO_TCP; |
534 | | f->alproto = ALPROTO_HTTP1; |
535 | | |
536 | | StreamTcpInitConfig(true); |
537 | | |
538 | | SCLogDebug("\n>>>> processing chunk 1 <<<<\n"); |
539 | | int r = AppLayerParserParse( |
540 | | NULL, alp_tctx, f, ALPROTO_HTTP1, STREAM_TOSERVER | STREAM_START, httpbuf1, httplen1); |
541 | | FAIL_IF_NOT(r == 0); |
542 | | |
543 | | SCLogDebug("\n>>>> processing chunk 2 size %u <<<<\n", httplen2); |
544 | | r = AppLayerParserParse( |
545 | | NULL, alp_tctx, f, ALPROTO_HTTP1, STREAM_TOSERVER | STREAM_EOF, httpbuf2, httplen2); |
546 | | FAIL_IF_NOT(r == 0); |
547 | | |
548 | | SCLogDebug("\n>>>> processing chunk 3 size %u <<<<\n", httplen3); |
549 | | r = AppLayerParserParse( |
550 | | NULL, alp_tctx, f, ALPROTO_HTTP1, STREAM_TOSERVER | STREAM_EOF, httpbuf3, httplen3); |
551 | | FAIL_IF_NOT(r == 0); |
552 | | |
553 | | SCLogDebug("\n>>>> processing chunk 4 size %u <<<<\n", httplen4); |
554 | | r = AppLayerParserParse( |
555 | | NULL, alp_tctx, f, ALPROTO_HTTP1, STREAM_TOSERVER | STREAM_EOF, httpbuf4, httplen4); |
556 | | FAIL_IF_NOT(r == 0); |
557 | | |
558 | | SCLogDebug("\n>>>> processing chunk 5 size %u <<<<\n", httplen5); |
559 | | r = AppLayerParserParse( |
560 | | NULL, alp_tctx, f, ALPROTO_HTTP1, STREAM_TOSERVER | STREAM_EOF, httpbuf5, httplen5); |
561 | | FAIL_IF_NOT(r == 0); |
562 | | |
563 | | SCLogDebug("\n>>>> processing chunk 6 size %u <<<<\n", httplen6); |
564 | | r = AppLayerParserParse( |
565 | | NULL, alp_tctx, f, ALPROTO_HTTP1, STREAM_TOSERVER | STREAM_EOF, httpbuf6, httplen6); |
566 | | FAIL_IF_NOT(r == 0); |
567 | | |
568 | | http_state = f->alstate; |
569 | | FAIL_IF_NULL(http_state); |
570 | | |
571 | | htp_tx_t *tx = AppLayerParserGetTx(IPPROTO_TCP, ALPROTO_HTTP1, http_state, 0); |
572 | | FAIL_IF_NULL(tx); |
573 | | FAIL_IF_NULL(tx->request_method); |
574 | | |
575 | | FAIL_IF(memcmp(bstr_util_strdup_to_c(tx->request_method), "POST", 4) != 0); |
576 | | |
577 | | HtpTxUserData *tx_ud = htp_tx_get_user_data(tx); |
578 | | FAIL_IF_NULL(tx_ud); |
579 | | FAIL_IF_NULL(tx_ud->files_ts.head); |
580 | | FAIL_IF_NULL(tx_ud->files_ts.tail); |
581 | | FAIL_IF(tx_ud->files_ts.tail->state != FILE_STATE_CLOSED); |
582 | | FAIL_IF(FileDataSize(tx_ud->files_ts.head) != 11); |
583 | | |
584 | | AppLayerParserThreadCtxFree(alp_tctx); |
585 | | StreamTcpFreeConfig(true); |
586 | | UTHFreeFlow(f); |
587 | | PASS; |
588 | | } |
589 | | |
590 | | static int HTPFileParserTest04(void) |
591 | | { |
592 | | uint8_t httpbuf1[] = "POST /upload.cgi HTTP/1.1\r\n" |
593 | | "Host: www.server.lan\r\n" |
594 | | "Content-Type: multipart/form-data; boundary=---------------------------277531038314945\r\n" |
595 | | "Content-Length: 373\r\n" |
596 | | "\r\n"; |
597 | | uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */ |
598 | | |
599 | | uint8_t httpbuf2[] = "-----------------------------277531038314945\r\n" |
600 | | "Content-Disposition: form-data; name=\"email\"\r\n" |
601 | | "\r\n" |
602 | | "someaddress@somedomain.lan\r\n"; |
603 | | uint32_t httplen2 = sizeof(httpbuf2) - 1; /* minus the \0 */ |
604 | | |
605 | | uint8_t httpbuf3[] = "-----------------------------277531038314945\r\n" |
606 | | "Content-Disposition: form-data; name=\"uploadfile_0\"; filename=\"somepicture1.jpg\"\r\n" |
607 | | "Content-Type: image/jpeg\r\n" |
608 | | "\r\n"; |
609 | | uint32_t httplen3 = sizeof(httpbuf3) - 1; /* minus the \0 */ |
610 | | |
611 | | uint8_t httpbuf4[] = "file0123456789abcdefghijklmnopqrstuvwxyz"; |
612 | | uint32_t httplen4 = sizeof(httpbuf4) - 1; /* minus the \0 */ |
613 | | |
614 | | uint8_t httpbuf5[] = "content\r\n"; |
615 | | uint32_t httplen5 = sizeof(httpbuf5) - 1; /* minus the \0 */ |
616 | | |
617 | | uint8_t httpbuf6[] = "-----------------------------277531038314945--"; |
618 | | uint32_t httplen6 = sizeof(httpbuf6) - 1; /* minus the \0 */ |
619 | | |
620 | | TcpSession ssn; |
621 | | HtpState *http_state = NULL; |
622 | | AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc(); |
623 | | |
624 | | memset(&ssn, 0, sizeof(ssn)); |
625 | | |
626 | | Flow *f = UTHBuildFlow(AF_INET, "1.2.3.4", "1.2.3.5", 1024, 80); |
627 | | FAIL_IF_NULL(f); |
628 | | f->protoctx = &ssn; |
629 | | f->proto = IPPROTO_TCP; |
630 | | f->alproto = ALPROTO_HTTP1; |
631 | | |
632 | | StreamTcpInitConfig(true); |
633 | | |
634 | | SCLogDebug("\n>>>> processing chunk 1 <<<<\n"); |
635 | | int r = AppLayerParserParse( |
636 | | NULL, alp_tctx, f, ALPROTO_HTTP1, STREAM_TOSERVER | STREAM_START, httpbuf1, httplen1); |
637 | | FAIL_IF_NOT(r == 0); |
638 | | |
639 | | SCLogDebug("\n>>>> processing chunk 2 size %u <<<<\n", httplen2); |
640 | | r = AppLayerParserParse( |
641 | | NULL, alp_tctx, f, ALPROTO_HTTP1, STREAM_TOSERVER | STREAM_EOF, httpbuf2, httplen2); |
642 | | FAIL_IF_NOT(r == 0); |
643 | | |
644 | | SCLogDebug("\n>>>> processing chunk 3 size %u <<<<\n", httplen3); |
645 | | r = AppLayerParserParse( |
646 | | NULL, alp_tctx, f, ALPROTO_HTTP1, STREAM_TOSERVER | STREAM_EOF, httpbuf3, httplen3); |
647 | | FAIL_IF_NOT(r == 0); |
648 | | |
649 | | SCLogDebug("\n>>>> processing chunk 4 size %u <<<<\n", httplen4); |
650 | | r = AppLayerParserParse( |
651 | | NULL, alp_tctx, f, ALPROTO_HTTP1, STREAM_TOSERVER | STREAM_EOF, httpbuf4, httplen4); |
652 | | FAIL_IF_NOT(r == 0); |
653 | | |
654 | | SCLogDebug("\n>>>> processing chunk 5 size %u <<<<\n", httplen5); |
655 | | r = AppLayerParserParse( |
656 | | NULL, alp_tctx, f, ALPROTO_HTTP1, STREAM_TOSERVER | STREAM_EOF, httpbuf5, httplen5); |
657 | | FAIL_IF_NOT(r == 0); |
658 | | |
659 | | SCLogDebug("\n>>>> processing chunk 6 size %u <<<<\n", httplen6); |
660 | | r = AppLayerParserParse( |
661 | | NULL, alp_tctx, f, ALPROTO_HTTP1, STREAM_TOSERVER | STREAM_EOF, httpbuf6, httplen6); |
662 | | FAIL_IF_NOT(r == 0); |
663 | | |
664 | | http_state = f->alstate; |
665 | | FAIL_IF_NULL(http_state); |
666 | | |
667 | | htp_tx_t *tx = AppLayerParserGetTx(IPPROTO_TCP, ALPROTO_HTTP1, http_state, 0); |
668 | | FAIL_IF_NULL(tx); |
669 | | FAIL_IF_NULL(tx->request_method); |
670 | | |
671 | | FAIL_IF(memcmp(bstr_util_strdup_to_c(tx->request_method), "POST", 4) != 0); |
672 | | |
673 | | HtpTxUserData *tx_ud = htp_tx_get_user_data(tx); |
674 | | FAIL_IF_NULL(tx_ud); |
675 | | FAIL_IF_NULL(tx_ud->files_ts.head); |
676 | | FAIL_IF_NULL(tx_ud->files_ts.tail); |
677 | | FAIL_IF(tx_ud->files_ts.tail->state != FILE_STATE_CLOSED); |
678 | | |
679 | | AppLayerParserThreadCtxFree(alp_tctx); |
680 | | StreamTcpFreeConfig(true); |
681 | | UTHFreeFlow(f); |
682 | | PASS; |
683 | | } |
684 | | |
685 | | static int HTPFileParserTest05(void) |
686 | | { |
687 | | uint8_t httpbuf1[] = "POST /upload.cgi HTTP/1.1\r\n" |
688 | | "Host: www.server.lan\r\n" |
689 | | "Content-Type: multipart/form-data; boundary=---------------------------277531038314945\r\n" |
690 | | "Content-Length: 544\r\n" |
691 | | "\r\n" |
692 | | "-----------------------------277531038314945\r\n" |
693 | | "Content-Disposition: form-data; name=\"uploadfile_0\"; filename=\"somepicture1.jpg\"\r\n" |
694 | | "Content-Type: image/jpeg\r\n" |
695 | | "\r\n" |
696 | | "filecontent\r\n" |
697 | | "-----------------------------277531038314945\r\n"; |
698 | | uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */ |
699 | | uint8_t httpbuf2[] = "Content-Disposition: form-data; name=\"uploadfile_1\"; filename=\"somepicture2.jpg\"\r\n" |
700 | | "Content-Type: image/jpeg\r\n" |
701 | | "\r\n" |
702 | | "FILECONTENT\r\n" |
703 | | "-----------------------------277531038314945--"; |
704 | | uint32_t httplen2 = sizeof(httpbuf2) - 1; /* minus the \0 */ |
705 | | |
706 | | TcpSession ssn; |
707 | | HtpState *http_state = NULL; |
708 | | AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc(); |
709 | | |
710 | | memset(&ssn, 0, sizeof(ssn)); |
711 | | |
712 | | Flow *f = UTHBuildFlow(AF_INET, "1.2.3.4", "1.2.3.5", 1024, 80); |
713 | | FAIL_IF_NULL(f); |
714 | | f->protoctx = &ssn; |
715 | | f->proto = IPPROTO_TCP; |
716 | | f->alproto = ALPROTO_HTTP1; |
717 | | |
718 | | StreamTcpInitConfig(true); |
719 | | |
720 | | SCLogDebug("\n>>>> processing chunk 1 size %u <<<<\n", httplen1); |
721 | | int r = AppLayerParserParse( |
722 | | NULL, alp_tctx, f, ALPROTO_HTTP1, STREAM_TOSERVER | STREAM_START, httpbuf1, httplen1); |
723 | | FAIL_IF_NOT(r == 0); |
724 | | |
725 | | SCLogDebug("\n>>>> processing chunk 2 size %u <<<<\n", httplen2); |
726 | | r = AppLayerParserParse( |
727 | | NULL, alp_tctx, f, ALPROTO_HTTP1, STREAM_TOSERVER | STREAM_EOF, httpbuf2, httplen2); |
728 | | FAIL_IF_NOT(r == 0); |
729 | | |
730 | | http_state = f->alstate; |
731 | | FAIL_IF_NULL(http_state); |
732 | | |
733 | | htp_tx_t *tx = AppLayerParserGetTx(IPPROTO_TCP, ALPROTO_HTTP1, http_state, 0); |
734 | | FAIL_IF_NULL(tx); |
735 | | FAIL_IF_NULL(tx->request_method); |
736 | | |
737 | | FAIL_IF(memcmp(bstr_util_strdup_to_c(tx->request_method), "POST", 4) != 0); |
738 | | |
739 | | HtpTxUserData *tx_ud = htp_tx_get_user_data(tx); |
740 | | FAIL_IF_NULL(tx_ud); |
741 | | FAIL_IF_NULL(tx_ud->files_ts.head); |
742 | | FAIL_IF_NULL(tx_ud->files_ts.tail); |
743 | | FAIL_IF(tx_ud->files_ts.tail->state != FILE_STATE_CLOSED); |
744 | | |
745 | | FAIL_IF(tx_ud->files_ts.head == tx_ud->files_ts.tail); |
746 | | FAIL_IF(tx_ud->files_ts.head->next != tx_ud->files_ts.tail); |
747 | | |
748 | | FAIL_IF(StreamingBufferCompareRawData(tx_ud->files_ts.head->sb, (uint8_t *)"filecontent", 11) != |
749 | | 1); |
750 | | |
751 | | FAIL_IF(StreamingBufferCompareRawData(tx_ud->files_ts.tail->sb, (uint8_t *)"FILECONTENT", 11) != |
752 | | 1); |
753 | | AppLayerParserThreadCtxFree(alp_tctx); |
754 | | StreamTcpFreeConfig(true); |
755 | | UTHFreeFlow(f); |
756 | | PASS; |
757 | | } |
758 | | |
759 | | /** \test first multipart part contains file but doesn't end in first chunk */ |
760 | | static int HTPFileParserTest06(void) |
761 | | { |
762 | | uint8_t httpbuf1[] = "POST /upload.cgi HTTP/1.1\r\n" |
763 | | "Host: www.server.lan\r\n" |
764 | | "Content-Type: multipart/form-data; boundary=---------------------------277531038314945\r\n" |
765 | | "Content-Length: 544\r\n" |
766 | | "\r\n" |
767 | | "-----------------------------277531038314945\r\n" |
768 | | "Content-Disposition: form-data; name=\"uploadfile_0\"; filename=\"somepicture1.jpg\"\r\n" |
769 | | "Content-Type: image/jpeg\r\n" |
770 | | "\r\n" |
771 | | "filecontent\r\n" |
772 | | "-----------------------------27753103831494"; |
773 | | uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */ |
774 | | uint8_t httpbuf2[] = "5\r\nContent-Disposition: form-data; name=\"uploadfile_1\"; filename=\"somepicture2.jpg\"\r\n" |
775 | | "Content-Type: image/jpeg\r\n" |
776 | | "\r\n" |
777 | | "FILECONTENT\r\n" |
778 | | "-----------------------------277531038314945--"; |
779 | | uint32_t httplen2 = sizeof(httpbuf2) - 1; /* minus the \0 */ |
780 | | |
781 | | TcpSession ssn; |
782 | | HtpState *http_state = NULL; |
783 | | AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc(); |
784 | | |
785 | | memset(&ssn, 0, sizeof(ssn)); |
786 | | |
787 | | Flow *f = UTHBuildFlow(AF_INET, "1.2.3.4", "1.2.3.5", 1024, 80); |
788 | | FAIL_IF_NULL(f); |
789 | | f->protoctx = &ssn; |
790 | | f->proto = IPPROTO_TCP; |
791 | | f->alproto = ALPROTO_HTTP1; |
792 | | |
793 | | StreamTcpInitConfig(true); |
794 | | |
795 | | SCLogDebug("\n>>>> processing chunk 1 size %u <<<<\n", httplen1); |
796 | | int r = AppLayerParserParse( |
797 | | NULL, alp_tctx, f, ALPROTO_HTTP1, STREAM_TOSERVER | STREAM_START, httpbuf1, httplen1); |
798 | | FAIL_IF_NOT(r == 0); |
799 | | |
800 | | SCLogDebug("\n>>>> processing chunk 2 size %u <<<<\n", httplen2); |
801 | | r = AppLayerParserParse( |
802 | | NULL, alp_tctx, f, ALPROTO_HTTP1, STREAM_TOSERVER | STREAM_EOF, httpbuf2, httplen2); |
803 | | FAIL_IF_NOT(r == 0); |
804 | | |
805 | | http_state = f->alstate; |
806 | | FAIL_IF_NULL(http_state); |
807 | | |
808 | | htp_tx_t *tx = AppLayerParserGetTx(IPPROTO_TCP, ALPROTO_HTTP1, http_state, 0); |
809 | | FAIL_IF_NULL(tx); |
810 | | FAIL_IF_NULL(tx->request_method); |
811 | | |
812 | | FAIL_IF(memcmp(bstr_util_strdup_to_c(tx->request_method), "POST", 4) != 0); |
813 | | |
814 | | HtpTxUserData *tx_ud = htp_tx_get_user_data(tx); |
815 | | FAIL_IF_NULL(tx_ud); |
816 | | FAIL_IF_NULL(tx_ud->files_ts.head); |
817 | | FAIL_IF_NULL(tx_ud->files_ts.tail); |
818 | | FAIL_IF(tx_ud->files_ts.tail->state != FILE_STATE_CLOSED); |
819 | | |
820 | | FAIL_IF(tx_ud->files_ts.head == tx_ud->files_ts.tail); |
821 | | FAIL_IF(tx_ud->files_ts.head->next != tx_ud->files_ts.tail); |
822 | | |
823 | | FAIL_IF(StreamingBufferCompareRawData(tx_ud->files_ts.head->sb, (uint8_t *)"filecontent", 11) != |
824 | | 1); |
825 | | |
826 | | FAIL_IF(StreamingBufferCompareRawData(tx_ud->files_ts.tail->sb, (uint8_t *)"FILECONTENT", 11) != |
827 | | 1); |
828 | | |
829 | | AppLayerParserThreadCtxFree(alp_tctx); |
830 | | StreamTcpFreeConfig(true); |
831 | | UTHFreeFlow(f); |
832 | | PASS; |
833 | | } |
834 | | |
835 | | /** \test POST, but not multipart */ |
836 | | static int HTPFileParserTest07(void) |
837 | | { |
838 | | uint8_t httpbuf1[] = "POST /filename HTTP/1.1\r\n" |
839 | | "Host: www.server.lan\r\n" |
840 | | "Content-Length: 11\r\n" |
841 | | "\r\n"; |
842 | | uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */ |
843 | | uint8_t httpbuf2[] = "FILECONTENT"; |
844 | | uint32_t httplen2 = sizeof(httpbuf2) - 1; /* minus the \0 */ |
845 | | |
846 | | TcpSession ssn; |
847 | | HtpState *http_state = NULL; |
848 | | AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc(); |
849 | | |
850 | | memset(&ssn, 0, sizeof(ssn)); |
851 | | |
852 | | Flow *f = UTHBuildFlow(AF_INET, "1.2.3.4", "1.2.3.5", 1024, 80); |
853 | | FAIL_IF_NULL(f); |
854 | | f->protoctx = &ssn; |
855 | | f->proto = IPPROTO_TCP; |
856 | | f->alproto = ALPROTO_HTTP1; |
857 | | |
858 | | StreamTcpInitConfig(true); |
859 | | |
860 | | SCLogDebug("\n>>>> processing chunk 1 size %u <<<<\n", httplen1); |
861 | | int r = AppLayerParserParse( |
862 | | NULL, alp_tctx, f, ALPROTO_HTTP1, STREAM_TOSERVER | STREAM_START, httpbuf1, httplen1); |
863 | | FAIL_IF_NOT(r == 0); |
864 | | |
865 | | SCLogDebug("\n>>>> processing chunk 2 size %u <<<<\n", httplen2); |
866 | | r = AppLayerParserParse( |
867 | | NULL, alp_tctx, f, ALPROTO_HTTP1, STREAM_TOSERVER | STREAM_EOF, httpbuf2, httplen2); |
868 | | FAIL_IF_NOT(r == 0); |
869 | | |
870 | | http_state = f->alstate; |
871 | | FAIL_IF_NULL(http_state); |
872 | | |
873 | | htp_tx_t *tx = AppLayerParserGetTx(IPPROTO_TCP, ALPROTO_HTTP1, http_state, 0); |
874 | | FAIL_IF_NULL(tx); |
875 | | FAIL_IF_NULL(tx->request_method); |
876 | | FAIL_IF(memcmp(bstr_util_strdup_to_c(tx->request_method), "POST", 4) != 0); |
877 | | |
878 | | HtpTxUserData *tx_ud = htp_tx_get_user_data(tx); |
879 | | FAIL_IF_NULL(tx_ud); |
880 | | FAIL_IF_NULL(tx_ud->files_ts.head); |
881 | | FAIL_IF_NULL(tx_ud->files_ts.tail); |
882 | | FAIL_IF(tx_ud->files_ts.tail->state != FILE_STATE_CLOSED); |
883 | | |
884 | | FAIL_IF(StreamingBufferCompareRawData(tx_ud->files_ts.tail->sb, (uint8_t *)"FILECONTENT", 11) != |
885 | | 1); |
886 | | |
887 | | AppLayerParserThreadCtxFree(alp_tctx); |
888 | | StreamTcpFreeConfig(true); |
889 | | UTHFreeFlow(f); |
890 | | PASS; |
891 | | } |
892 | | |
893 | | static int HTPFileParserTest08(void) |
894 | | { |
895 | | uint8_t httpbuf1[] = "POST /upload.cgi HTTP/1.1\r\n" |
896 | | "Host: www.server.lan\r\n" |
897 | | "Content-Type: multipart/form-data; boundary=---------------------------277531038314945\r\n" |
898 | | "Content-Length: 215\r\n" |
899 | | "\r\n" |
900 | | "-----------------------------277531038314945\r\n" |
901 | | "Content-Disposition: form-data; name=\"uploadfile_0\"; filename=\"somepicture1.jpg\"\r\n" |
902 | | "Content-Type: image/jpeg\r\n"; |
903 | | |
904 | | uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */ |
905 | | uint8_t httpbuf2[] = "filecontent\r\n\r\n" |
906 | | "-----------------------------277531038314945--"; |
907 | | uint32_t httplen2 = sizeof(httpbuf2) - 1; /* minus the \0 */ |
908 | | |
909 | | TcpSession ssn; |
910 | | AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc(); |
911 | | HtpState *http_state = NULL; |
912 | | memset(&ssn, 0, sizeof(ssn)); |
913 | | |
914 | | Flow *f = UTHBuildFlow(AF_INET, "1.2.3.4", "1.2.3.5", 1024, 80); |
915 | | FAIL_IF_NULL(f); |
916 | | f->protoctx = &ssn; |
917 | | f->proto = IPPROTO_TCP; |
918 | | f->alproto = ALPROTO_HTTP1; |
919 | | |
920 | | StreamTcpInitConfig(true); |
921 | | |
922 | | SCLogDebug("\n>>>> processing chunk 1 <<<<\n"); |
923 | | int r = AppLayerParserParse( |
924 | | NULL, alp_tctx, f, ALPROTO_HTTP1, STREAM_TOSERVER | STREAM_START, httpbuf1, httplen1); |
925 | | FAIL_IF_NOT(r == 0); |
926 | | |
927 | | SCLogDebug("\n>>>> processing chunk 2 size %u <<<<\n", httplen2); |
928 | | r = AppLayerParserParse( |
929 | | NULL, alp_tctx, f, ALPROTO_HTTP1, STREAM_TOSERVER | STREAM_EOF, httpbuf2, httplen2); |
930 | | FAIL_IF_NOT(r == 0); |
931 | | |
932 | | http_state = f->alstate; |
933 | | FAIL_IF_NULL(http_state); |
934 | | |
935 | | void *tx = AppLayerParserGetTx(IPPROTO_TCP, ALPROTO_HTTP1, f->alstate, 0); |
936 | | FAIL_IF_NULL(tx); |
937 | | |
938 | | AppLayerDecoderEvents *decoder_events = |
939 | | AppLayerParserGetEventsByTx(IPPROTO_TCP, ALPROTO_HTTP1, tx); |
940 | | FAIL_IF_NULL(decoder_events); |
941 | | |
942 | | FAIL_IF(decoder_events->cnt != 2); |
943 | | |
944 | | AppLayerParserThreadCtxFree(alp_tctx); |
945 | | StreamTcpFreeConfig(true); |
946 | | UTHFreeFlow(f); |
947 | | PASS; |
948 | | } |
949 | | |
950 | | /** \test invalid header: Somereallylongheaderstr: has no value */ |
951 | | static int HTPFileParserTest09(void) |
952 | | { |
953 | | uint8_t httpbuf1[] = "POST /upload.cgi HTTP/1.1\r\n" |
954 | | "Host: www.server.lan\r\n" |
955 | | "Content-Type: multipart/form-data; boundary=---------------------------277531038314945\r\n" |
956 | | "Content-Length: 337\r\n" |
957 | | "\r\n"; |
958 | | uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */ |
959 | | |
960 | | uint8_t httpbuf2[] = "-----------------------------277531038314945\r\n" |
961 | | "Content-Disposition: form-data; name=\"email\"\r\n" |
962 | | "\r\n" |
963 | | "someaddress@somedomain.lan\r\n"; |
964 | | uint32_t httplen2 = sizeof(httpbuf2) - 1; /* minus the \0 */ |
965 | | |
966 | | uint8_t httpbuf3[] = "-----------------------------277531038314945\r\n" |
967 | | "Content-Disposition: form-data; name=\"uploadfile_0\"; filename=\"somepicture1.jpg\"\r\n" |
968 | | "Somereallylongheaderstr:\r\n" |
969 | | "\r\n"; |
970 | | uint32_t httplen3 = sizeof(httpbuf3) - 1; /* minus the \0 */ |
971 | | |
972 | | uint8_t httpbuf4[] = "filecontent\r\n" |
973 | | "-----------------------------277531038314945--"; |
974 | | uint32_t httplen4 = sizeof(httpbuf4) - 1; /* minus the \0 */ |
975 | | |
976 | | TcpSession ssn; |
977 | | HtpState *http_state = NULL; |
978 | | AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc(); |
979 | | |
980 | | memset(&ssn, 0, sizeof(ssn)); |
981 | | |
982 | | Flow *f = UTHBuildFlow(AF_INET, "1.2.3.4", "1.2.3.5", 1024, 80); |
983 | | FAIL_IF_NULL(f); |
984 | | f->protoctx = &ssn; |
985 | | f->proto = IPPROTO_TCP; |
986 | | f->alproto = ALPROTO_HTTP1; |
987 | | |
988 | | StreamTcpInitConfig(true); |
989 | | |
990 | | SCLogDebug("\n>>>> processing chunk 1 <<<<\n"); |
991 | | int r = AppLayerParserParse( |
992 | | NULL, alp_tctx, f, ALPROTO_HTTP1, STREAM_TOSERVER | STREAM_START, httpbuf1, httplen1); |
993 | | FAIL_IF_NOT(r == 0); |
994 | | |
995 | | SCLogDebug("\n>>>> processing chunk 2 size %u <<<<\n", httplen2); |
996 | | r = AppLayerParserParse( |
997 | | NULL, alp_tctx, f, ALPROTO_HTTP1, STREAM_TOSERVER | STREAM_EOF, httpbuf2, httplen2); |
998 | | FAIL_IF_NOT(r == 0); |
999 | | |
1000 | | SCLogDebug("\n>>>> processing chunk 3 size %u <<<<\n", httplen3); |
1001 | | r = AppLayerParserParse( |
1002 | | NULL, alp_tctx, f, ALPROTO_HTTP1, STREAM_TOSERVER | STREAM_EOF, httpbuf3, httplen3); |
1003 | | FAIL_IF_NOT(r == 0); |
1004 | | |
1005 | | SCLogDebug("\n>>>> processing chunk 4 size %u <<<<\n", httplen4); |
1006 | | r = AppLayerParserParse( |
1007 | | NULL, alp_tctx, f, ALPROTO_HTTP1, STREAM_TOSERVER | STREAM_EOF, httpbuf4, httplen4); |
1008 | | FAIL_IF_NOT(r == 0); |
1009 | | |
1010 | | http_state = f->alstate; |
1011 | | FAIL_IF_NULL(http_state); |
1012 | | |
1013 | | void *tx = AppLayerParserGetTx(IPPROTO_TCP, ALPROTO_HTTP1, f->alstate, 0); |
1014 | | FAIL_IF_NULL(tx); |
1015 | | |
1016 | | AppLayerDecoderEvents *decoder_events = |
1017 | | AppLayerParserGetEventsByTx(IPPROTO_TCP, ALPROTO_HTTP1, tx); |
1018 | | FAIL_IF_NULL(decoder_events); |
1019 | | |
1020 | | FAIL_IF(decoder_events->cnt != 1); |
1021 | | |
1022 | | AppLayerParserThreadCtxFree(alp_tctx); |
1023 | | StreamTcpFreeConfig(true); |
1024 | | UTHFreeFlow(f); |
1025 | | PASS; |
1026 | | } |
1027 | | |
1028 | | /** \test empty entries */ |
1029 | | static int HTPFileParserTest10(void) |
1030 | | { |
1031 | | uint8_t httpbuf1[] = "POST /upload.cgi HTTP/1.1\r\n" |
1032 | | "Host: www.server.lan\r\n" |
1033 | | "Content-Type: multipart/form-data; boundary=---------------------------277531038314945\r\n" |
1034 | | "Content-Length: 337\r\n" |
1035 | | "\r\n"; |
1036 | | uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */ |
1037 | | |
1038 | | uint8_t httpbuf2[] = "-----------------------------277531038314945\r\n" |
1039 | | "\r\n"; |
1040 | | uint32_t httplen2 = sizeof(httpbuf2) - 1; /* minus the \0 */ |
1041 | | |
1042 | | uint8_t httpbuf3[] = "-----------------------------277531038314945\r\n" |
1043 | | "Content-Disposition: form-data; name=\"uploadfile_0\"; filename=\"somepicture1.jpg\"\r\n" |
1044 | | "Somereallylongheaderstr: with a good value\r\n" |
1045 | | "\r\n"; |
1046 | | uint32_t httplen3 = sizeof(httpbuf3) - 1; /* minus the \0 */ |
1047 | | |
1048 | | uint8_t httpbuf4[] = "filecontent\r\n" |
1049 | | "-----------------------------277531038314945--"; |
1050 | | uint32_t httplen4 = sizeof(httpbuf4) - 1; /* minus the \0 */ |
1051 | | |
1052 | | TcpSession ssn; |
1053 | | HtpState *http_state = NULL; |
1054 | | AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc(); |
1055 | | |
1056 | | memset(&ssn, 0, sizeof(ssn)); |
1057 | | |
1058 | | Flow *f = UTHBuildFlow(AF_INET, "1.2.3.4", "1.2.3.5", 1024, 80); |
1059 | | FAIL_IF_NULL(f); |
1060 | | f->protoctx = &ssn; |
1061 | | f->proto = IPPROTO_TCP; |
1062 | | f->alproto = ALPROTO_HTTP1; |
1063 | | |
1064 | | StreamTcpInitConfig(true); |
1065 | | |
1066 | | SCLogDebug("\n>>>> processing chunk 1 <<<<\n"); |
1067 | | int r = AppLayerParserParse( |
1068 | | NULL, alp_tctx, f, ALPROTO_HTTP1, STREAM_TOSERVER | STREAM_START, httpbuf1, httplen1); |
1069 | | FAIL_IF_NOT(r == 0); |
1070 | | |
1071 | | SCLogDebug("\n>>>> processing chunk 2 size %u <<<<\n", httplen2); |
1072 | | r = AppLayerParserParse( |
1073 | | NULL, alp_tctx, f, ALPROTO_HTTP1, STREAM_TOSERVER | STREAM_EOF, httpbuf2, httplen2); |
1074 | | FAIL_IF_NOT(r == 0); |
1075 | | |
1076 | | SCLogDebug("\n>>>> processing chunk 3 size %u <<<<\n", httplen3); |
1077 | | r = AppLayerParserParse( |
1078 | | NULL, alp_tctx, f, ALPROTO_HTTP1, STREAM_TOSERVER | STREAM_EOF, httpbuf3, httplen3); |
1079 | | FAIL_IF_NOT(r == 0); |
1080 | | |
1081 | | SCLogDebug("\n>>>> processing chunk 4 size %u <<<<\n", httplen4); |
1082 | | r = AppLayerParserParse( |
1083 | | NULL, alp_tctx, f, ALPROTO_HTTP1, STREAM_TOSERVER | STREAM_EOF, httpbuf4, httplen4); |
1084 | | FAIL_IF_NOT(r == 0); |
1085 | | |
1086 | | http_state = f->alstate; |
1087 | | FAIL_IF_NULL(http_state); |
1088 | | |
1089 | | void *tx = AppLayerParserGetTx(IPPROTO_TCP, ALPROTO_HTTP1, f->alstate, 0); |
1090 | | FAIL_IF_NULL(tx); |
1091 | | AppLayerDecoderEvents *decoder_events = |
1092 | | AppLayerParserGetEventsByTx(IPPROTO_TCP, ALPROTO_HTTP1, tx); |
1093 | | FAIL_IF_NOT_NULL(decoder_events); |
1094 | | |
1095 | | AppLayerParserThreadCtxFree(alp_tctx); |
1096 | | StreamTcpFreeConfig(true); |
1097 | | UTHFreeFlow(f); |
1098 | | PASS; |
1099 | | } |
1100 | | |
1101 | | /** \test filedata cut in two pieces */ |
1102 | | static int HTPFileParserTest11(void) |
1103 | | { |
1104 | | uint8_t httpbuf1[] = "POST /upload.cgi HTTP/1.1\r\n" |
1105 | | "Host: www.server.lan\r\n" |
1106 | | "Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryBRDbP74mBhBxsIdo\r\n" |
1107 | | "Content-Length: 1102\r\n" |
1108 | | "\r\n"; |
1109 | | uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */ |
1110 | | |
1111 | | uint8_t httpbuf2[] = "------WebKitFormBoundaryBRDbP74mBhBxsIdo\r\n"; |
1112 | | uint32_t httplen2 = sizeof(httpbuf2) - 1; /* minus the \0 */ |
1113 | | |
1114 | | uint8_t httpbuf3[] = "Content-Disposition: form-data; name=\"PROGRESS_URL\"\r\n" |
1115 | | "\r\n" |
1116 | | "http://somserver.com/progress.php?UPLOAD_IDENTIFIER=XXXXXXXXX.XXXXXXXXXX.XXXXXXXX.XX.X\r\n" |
1117 | | "------WebKitFormBoundaryBRDbP74mBhBxsIdo\r\n" |
1118 | | "Content-Disposition: form-data; name=\"DESTINATION_DIR\"\r\n" |
1119 | | "\r\n" |
1120 | | "10\r\n" |
1121 | | "------WebKitFormBoundaryBRDbP74mBhBxsIdo\r\n" |
1122 | | "Content-Disposition: form-data; name=\"js_enabled\"\r\n" |
1123 | | "\r\n" |
1124 | | "1" |
1125 | | "------WebKitFormBoundaryBRDbP74mBhBxsIdo\r\n" |
1126 | | "Content-Disposition: form-data; name=\"signature\"\r\n" |
1127 | | "\r\n" |
1128 | | "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\r\n" |
1129 | | "------WebKitFormBoundaryBRDbP74mBhBxsIdo\r\n" |
1130 | | "Content-Disposition: form-data; name=\"upload_files\"\r\n" |
1131 | | "\r\n" |
1132 | | "------WebKitFormBoundaryBRDbP74mBhBxsIdo\r\n" |
1133 | | "Content-Disposition: form-data; name=\"terms\"\r\n" |
1134 | | "\r\n" |
1135 | | "1" |
1136 | | "------WebKitFormBoundaryBRDbP74mBhBxsIdo\r\n" |
1137 | | "Content-Disposition: form-data; name=\"file[]\"\r\n" |
1138 | | "\r\n" |
1139 | | "------WebKitFormBoundaryBRDbP74mBhBxsIdo\r\n" |
1140 | | "Content-Disposition: form-data; name=\"description[]\"\r\n" |
1141 | | "\r\n" |
1142 | | "------WebKitFormBoundaryBRDbP74mBhBxsIdo\r\n" |
1143 | | "Content-Disposition: form-data; name=\"upload_file[]\"; filename=\"filename.doc\"\r\n" |
1144 | | "Content-Type: application/msword\r\n" |
1145 | | "\r\n" |
1146 | | "FILE"; |
1147 | | uint32_t httplen3 = sizeof(httpbuf3) - 1; /* minus the \0 */ |
1148 | | |
1149 | | uint8_t httpbuf4[] = "CONTENT\r\n" |
1150 | | "------WebKitFormBoundaryBRDbP74mBhBxsIdo--"; |
1151 | | uint32_t httplen4 = sizeof(httpbuf4) - 1; /* minus the \0 */ |
1152 | | |
1153 | | TcpSession ssn; |
1154 | | HtpState *http_state = NULL; |
1155 | | AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc(); |
1156 | | |
1157 | | memset(&ssn, 0, sizeof(ssn)); |
1158 | | |
1159 | | Flow *f = UTHBuildFlow(AF_INET, "1.2.3.4", "1.2.3.5", 1024, 80); |
1160 | | FAIL_IF_NULL(f); |
1161 | | f->protoctx = &ssn; |
1162 | | f->proto = IPPROTO_TCP; |
1163 | | f->alproto = ALPROTO_HTTP1; |
1164 | | |
1165 | | StreamTcpInitConfig(true); |
1166 | | |
1167 | | SCLogDebug("\n>>>> processing chunk 1 <<<<\n"); |
1168 | | int r = AppLayerParserParse( |
1169 | | NULL, alp_tctx, f, ALPROTO_HTTP1, STREAM_TOSERVER | STREAM_START, httpbuf1, httplen1); |
1170 | | FAIL_IF_NOT(r == 0); |
1171 | | |
1172 | | SCLogDebug("\n>>>> processing chunk 2 size %u <<<<\n", httplen2); |
1173 | | r = AppLayerParserParse(NULL, alp_tctx, f, ALPROTO_HTTP1, STREAM_TOSERVER, httpbuf2, httplen2); |
1174 | | FAIL_IF_NOT(r == 0); |
1175 | | |
1176 | | SCLogDebug("\n>>>> processing chunk 3 size %u <<<<\n", httplen3); |
1177 | | r = AppLayerParserParse(NULL, alp_tctx, f, ALPROTO_HTTP1, STREAM_TOSERVER, httpbuf3, httplen3); |
1178 | | FAIL_IF_NOT(r == 0); |
1179 | | |
1180 | | SCLogDebug("\n>>>> processing chunk 4 size %u <<<<\n", httplen4); |
1181 | | r = AppLayerParserParse( |
1182 | | NULL, alp_tctx, f, ALPROTO_HTTP1, STREAM_TOSERVER | STREAM_EOF, httpbuf4, httplen4); |
1183 | | FAIL_IF_NOT(r == 0); |
1184 | | |
1185 | | http_state = f->alstate; |
1186 | | FAIL_IF_NULL(http_state); |
1187 | | |
1188 | | void *txtmp = AppLayerParserGetTx(IPPROTO_TCP, ALPROTO_HTTP1, f->alstate, 0); |
1189 | | FAIL_IF_NULL(txtmp); |
1190 | | |
1191 | | AppLayerDecoderEvents *decoder_events = |
1192 | | AppLayerParserGetEventsByTx(IPPROTO_TCP, ALPROTO_HTTP1, txtmp); |
1193 | | FAIL_IF_NOT_NULL(decoder_events); |
1194 | | |
1195 | | htp_tx_t *tx = AppLayerParserGetTx(IPPROTO_TCP, ALPROTO_HTTP1, http_state, 0); |
1196 | | FAIL_IF_NULL(tx); |
1197 | | FAIL_IF_NULL(tx->request_method); |
1198 | | |
1199 | | FAIL_IF(memcmp(bstr_util_strdup_to_c(tx->request_method), "POST", 4) != 0); |
1200 | | |
1201 | | HtpTxUserData *tx_ud = htp_tx_get_user_data(tx); |
1202 | | FAIL_IF_NULL(tx_ud); |
1203 | | FAIL_IF_NULL(tx_ud->files_ts.head); |
1204 | | FAIL_IF_NULL(tx_ud->files_ts.tail); |
1205 | | FAIL_IF(tx_ud->files_ts.tail->state != FILE_STATE_CLOSED); |
1206 | | |
1207 | | FAIL_IF(StreamingBufferCompareRawData(tx_ud->files_ts.tail->sb, (uint8_t *)"FILECONTENT", 11) != |
1208 | | 1); |
1209 | | |
1210 | | AppLayerParserThreadCtxFree(alp_tctx); |
1211 | | StreamTcpFreeConfig(true); |
1212 | | UTHFreeFlow(f); |
1213 | | PASS; |
1214 | | } |
1215 | | |
1216 | | void AppLayerHtpFileRegisterTests (void); |
1217 | | #include "tests/app-layer-htp-file.c" |
1218 | | #endif /* UNITTESTS */ |
1219 | | |
1220 | | void HTPFileParserRegisterTests(void) |
1221 | 0 | { |
1222 | | #ifdef UNITTESTS |
1223 | | UtRegisterTest("HTPFileParserTest01", HTPFileParserTest01); |
1224 | | UtRegisterTest("HTPFileParserTest02", HTPFileParserTest02); |
1225 | | UtRegisterTest("HTPFileParserTest03", HTPFileParserTest03); |
1226 | | UtRegisterTest("HTPFileParserTest04", HTPFileParserTest04); |
1227 | | UtRegisterTest("HTPFileParserTest05", HTPFileParserTest05); |
1228 | | UtRegisterTest("HTPFileParserTest06", HTPFileParserTest06); |
1229 | | UtRegisterTest("HTPFileParserTest07", HTPFileParserTest07); |
1230 | | UtRegisterTest("HTPFileParserTest08", HTPFileParserTest08); |
1231 | | UtRegisterTest("HTPFileParserTest09", HTPFileParserTest09); |
1232 | | UtRegisterTest("HTPFileParserTest10", HTPFileParserTest10); |
1233 | | UtRegisterTest("HTPFileParserTest11", HTPFileParserTest11); |
1234 | | AppLayerHtpFileRegisterTests(); |
1235 | | #endif /* UNITTESTS */ |
1236 | 0 | } |