1 /*
2 * ModSecurity for Apache 2.x, http://www.modsecurity.org/
3 * Copyright (c) 2004-2013 Trustwave Holdings, Inc. (http://www.trustwave.com/)
4 *
5 * You may not use this file except in compliance with
6 * the License. You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * If any of the files related to licensing are missing or if you have any
11 * other questions related to licensing please contact Trustwave Holdings, Inc.
12 * directly using the email address security@modsecurity.org.
13 */
14
15 #include "modsecurity.h"
16 #include "re.h"
17 #include "msc_parsers.h"
18
19 #define CHUNK_CAPACITY 8192
20
21
22 /**
23 *
24 */
msre_engine_reqbody_processor_register(msre_engine * engine,const char * name,void * fn_init,void * fn_process,void * fn_complete)25 void msre_engine_reqbody_processor_register(msre_engine *engine,
26 const char *name, void *fn_init, void *fn_process, void *fn_complete)
27 {
28 msre_reqbody_processor_metadata *metadata =
29 (msre_reqbody_processor_metadata *)apr_pcalloc(engine->mp,
30 sizeof(msre_reqbody_processor_metadata));
31 if (metadata == NULL) return;
32
33 metadata->name = name;
34 metadata->init = fn_init;
35 metadata->process = fn_process;
36 metadata->complete = fn_complete;
37 apr_table_setn(engine->reqbody_processors, name, (void *)metadata);
38 }
39
40 /**
41 * Prepare to accept the request body (part 2).
42 */
modsecurity_request_body_start_init(modsec_rec * msr,char ** error_msg)43 static apr_status_t modsecurity_request_body_start_init(modsec_rec *msr, char **error_msg) {
44 *error_msg = NULL;
45
46 if(msr->msc_reqbody_storage == MSC_REQBODY_MEMORY) {
47 /* Prepare to store request body in memory. */
48
49 msr->msc_reqbody_chunks = apr_array_make(msr->msc_reqbody_mp,
50 32, sizeof(msc_data_chunk *));
51 if (msr->msc_reqbody_chunks == NULL) {
52 *error_msg = apr_pstrdup(msr->mp, "Input filter: Failed to prepare in-memory storage.");
53 return -1;
54 }
55 } else {
56 /* Prepare to store request body on disk. */
57
58 msr->msc_reqbody_filename = apr_psprintf(msr->mp, "%s/%s-%s-request_body-XXXXXX",
59 msr->txcfg->tmp_dir, current_filetime(msr->mp), msr->txid);
60 if (msr->msc_reqbody_filename == NULL) {
61 *error_msg = apr_pstrdup(msr->mp, "Input filter: Failed to generate an on-disk filename.");
62 return -1;
63 }
64
65 msr->msc_reqbody_fd = msc_mkstemp((char *)msr->msc_reqbody_filename);
66 if (msr->msc_reqbody_fd < 0) {
67 *error_msg = apr_psprintf(msr->mp, "Input filter: Failed to create temporary file: %s",
68 msr->msc_reqbody_filename);
69 return -1;
70 }
71
72 msr_log(msr, 4, "Input filter: Created temporary file to store request body: %s",
73 msr->msc_reqbody_filename);
74 }
75
76 return 1;
77 }
78
79 /**
80 * Prepare to accept the request body (part 1).
81 */
modsecurity_request_body_start(modsec_rec * msr,char ** error_msg)82 apr_status_t modsecurity_request_body_start(modsec_rec *msr, char **error_msg) {
83 *error_msg = NULL;
84 msr->msc_reqbody_length = 0;
85 msr->stream_input_length = 0;
86
87 /* Create a separate memory pool that will be used
88 * to allocate structures from (not data, which is allocated
89 * via malloc).
90 */
91 apr_pool_create(&msr->msc_reqbody_mp, NULL);
92
93 /* Initialise request body processors, if any. */
94
95 if (msr->msc_reqbody_processor != NULL) {
96 char *my_error_msg = NULL;
97 msre_reqbody_processor_metadata *metadata =
98 (msre_reqbody_processor_metadata *)apr_table_get(msr->modsecurity->msre->reqbody_processors, msr->msc_reqbody_processor);
99
100 if (metadata != NULL) {
101 if ( (metadata->init != NULL)
102 && (metadata->init(msr, &my_error_msg) < 0))
103 {
104 *error_msg = apr_psprintf(msr->mp,
105 "%s parsing error (init): %s",
106 msr->msc_reqbody_processor,
107 my_error_msg);
108 msr->msc_reqbody_error = 1;
109 msr->msc_reqbody_error_msg = my_error_msg;
110 msr_log(msr, 2, "%s", *error_msg);
111 }
112 }
113 // TODO: All these below need to be registered in the same way as above
114 else if (strcmp(msr->msc_reqbody_processor, "MULTIPART") == 0) {
115 if (multipart_init(msr, &my_error_msg) < 0) {
116 *error_msg = apr_psprintf(msr->mp, "Multipart parsing error (init): %s", my_error_msg);
117 msr->msc_reqbody_error = 1;
118 msr->msc_reqbody_error_msg = my_error_msg;
119 msr_log(msr, 2, "%s", *error_msg);
120 }
121 }
122 else if (strcmp(msr->msc_reqbody_processor, "XML") == 0) {
123 if (xml_init(msr, &my_error_msg) < 0) {
124 *error_msg = apr_psprintf(msr->mp, "XML parsing error (init): %s", my_error_msg);
125 msr->msc_reqbody_error = 1;
126 msr->msc_reqbody_error_msg = my_error_msg;
127 msr_log(msr, 2, "%s", *error_msg);
128 }
129 }
130 else if (strcmp(msr->msc_reqbody_processor, "JSON") == 0) {
131 #ifdef WITH_YAJL
132 if (json_init(msr, &my_error_msg) < 0) {
133 *error_msg = apr_psprintf(msr->mp, "JSON parsing error (init): %s", my_error_msg);
134 msr->msc_reqbody_error = 1;
135 msr->msc_reqbody_error_msg = my_error_msg;
136 msr_log(msr, 2, "%s", *error_msg);
137 }
138 #else
139 *error_msg = apr_psprintf(msr->mp, "JSON support was not enabled");
140 msr->msc_reqbody_error = 1;
141 msr->msc_reqbody_error_msg = my_error_msg;
142 msr_log(msr, 2, "%s", *error_msg);
143 #endif
144 }
145 else if (strcmp(msr->msc_reqbody_processor, "URLENCODED") == 0) {
146 /* Do nothing, URLENCODED processor does not support streaming yet. */
147 }
148 else {
149 *error_msg = apr_psprintf(msr->mp, "Unknown request body processor: %s",
150 msr->msc_reqbody_processor);
151 return -1;
152 }
153 }
154
155 return modsecurity_request_body_start_init(msr, error_msg);
156 }
157
158 /**
159 * Stores a chunk of request body data to disk.
160 */
modsecurity_request_body_store_disk(modsec_rec * msr,const char * data,apr_size_t length,char ** error_msg)161 static apr_status_t modsecurity_request_body_store_disk(modsec_rec *msr,
162 const char *data, apr_size_t length, char **error_msg)
163 {
164 apr_size_t i;
165
166 *error_msg = NULL;
167
168 i = write(msr->msc_reqbody_fd, data, length);
169 if (i != length) {
170 *error_msg = apr_psprintf(msr->mp, "Input filter: Failed writing %" APR_SIZE_T_FMT
171 " bytes to temporary file (rc %" APR_SIZE_T_FMT ").", length, i);
172 return -1;
173 }
174
175 return 1;
176 }
177
178 /**
179 * Stores one chunk of request body data in memory.
180 */
modsecurity_request_body_store_memory(modsec_rec * msr,const char * data,apr_size_t length,char ** error_msg)181 static apr_status_t modsecurity_request_body_store_memory(modsec_rec *msr,
182 const char *data, apr_size_t length, char **error_msg)
183 {
184 *error_msg = NULL;
185
186 /* Would storing this chunk mean going over the limit? */
187 if ((msr->msc_reqbody_spilltodisk)
188 && (msr->txcfg->reqbody_buffering != REQUEST_BODY_FORCEBUF_ON)
189 && (msr->msc_reqbody_length + length > (apr_size_t)msr->txcfg->reqbody_inmemory_limit))
190 {
191 msc_data_chunk **chunks;
192 unsigned int disklen = 0;
193 int i;
194
195 msr_log(msr, 4, "Input filter: Request too large to store in memory, switching to disk.");
196
197 /* NOTE Must use modsecurity_request_body_store_disk() here
198 * to prevent data to be sent to the streaming
199 * processors again.
200 */
201
202 /* Initialise disk storage */
203 msr->msc_reqbody_storage = MSC_REQBODY_DISK;
204 if (modsecurity_request_body_start_init(msr, error_msg) < 0) return -1;
205
206 /* Write the data we keep in memory */
207 chunks = (msc_data_chunk **)msr->msc_reqbody_chunks->elts;
208 for(i = 0; i < msr->msc_reqbody_chunks->nelts; i++) {
209 disklen += chunks[i]->length;
210
211 if (modsecurity_request_body_store_disk(msr, chunks[i]->data, chunks[i]->length, error_msg) < 0) {
212 return -1;
213 }
214
215 free(chunks[i]->data);
216 chunks[i]->data = NULL;
217 }
218
219 /* Clear the memory pool as we no longer need the bits. */
220
221 /* IMP1 But since we only used apr_pool_clear memory might
222 * not be released back to the OS straight away?
223 */
224 msr->msc_reqbody_chunks = NULL;
225 apr_pool_clear(msr->msc_reqbody_mp);
226
227 msr_log(msr, 4, "Input filter: Wrote %u bytes from memory to disk.", disklen);
228
229 /* Continue with disk storage from now on */
230 return modsecurity_request_body_store_disk(msr, data, length, error_msg);
231 }
232
233 /* If we're here that means we are not over the
234 * request body in-memory limit yet.
235 */
236 {
237 unsigned long int bucket_offset, bucket_left;
238
239 bucket_offset = 0;
240 bucket_left = length;
241
242 /* Although we store the request body in chunks we don't
243 * want to use the same chunk sizes as the incoming memory
244 * buffers. They are often of very small sizes and that
245 * would make us waste a lot of memory. That's why we
246 * use our own chunks of CHUNK_CAPACITY sizes.
247 */
248
249 /* Loop until we empty this bucket into our chunks. */
250 while(bucket_left > 0) {
251 /* Allocate a new chunk if we have to. */
252 if (msr->msc_reqbody_chunk_current == NULL) {
253 msr->msc_reqbody_chunk_current = (msc_data_chunk *)
254 apr_pcalloc(msr->msc_reqbody_mp, sizeof(msc_data_chunk));
255 if (msr->msc_reqbody_chunk_current == NULL) {
256 *error_msg = apr_psprintf(msr->mp, "Input filter: Failed to allocate %lu bytes "
257 "for request body chunk.", (unsigned long)sizeof(msc_data_chunk));
258 return -1;
259 }
260
261 msr->msc_reqbody_chunk_current->data = malloc(CHUNK_CAPACITY);
262 if (msr->msc_reqbody_chunk_current->data == NULL) {
263 *error_msg = apr_psprintf(msr->mp, "Input filter: Failed to allocate %d bytes "
264 "for request body chunk data.", CHUNK_CAPACITY);
265 return -1;
266 }
267
268 msr->msc_reqbody_chunk_current->length = 0;
269 msr->msc_reqbody_chunk_current->is_permanent = 1;
270
271 *(const msc_data_chunk **)apr_array_push(msr->msc_reqbody_chunks)
272 = msr->msc_reqbody_chunk_current;
273 }
274
275 if (bucket_left < (CHUNK_CAPACITY - msr->msc_reqbody_chunk_current->length)) {
276 /* There's enough space in the current chunk. */
277 memcpy(msr->msc_reqbody_chunk_current->data +
278 msr->msc_reqbody_chunk_current->length, data + bucket_offset, bucket_left);
279 msr->msc_reqbody_chunk_current->length += bucket_left;
280 bucket_left = 0;
281 } else {
282 /* Fill the existing chunk. */
283 unsigned long int copy_length = CHUNK_CAPACITY -
284 msr->msc_reqbody_chunk_current->length;
285
286 memcpy(msr->msc_reqbody_chunk_current->data +
287 msr->msc_reqbody_chunk_current->length, data + bucket_offset, copy_length);
288 bucket_offset += copy_length;
289 bucket_left -= copy_length;
290 msr->msc_reqbody_chunk_current->length += copy_length;
291
292 /* We're done with this chunk. Setting the pointer
293 * to NULL is going to force a new chunk to be allocated
294 * on the next go.
295 */
296 msr->msc_reqbody_chunk_current = NULL;
297 }
298 }
299
300 msr->msc_reqbody_length += length;
301 }
302
303 return 1;
304 }
305
306 /**
307 * Stores one chunk of request body data. Returns -1 on error.
308 */
modsecurity_request_body_store(modsec_rec * msr,const char * data,apr_size_t length,char ** error_msg)309 apr_status_t modsecurity_request_body_store(modsec_rec *msr,
310 const char *data, apr_size_t length, char **error_msg)
311 {
312 *error_msg = NULL;
313
314 /* If we have a processor for this request body send
315 * data to it first (but only if it did not report an
316 * error on previous invocations).
317 */
318 if ((msr->msc_reqbody_processor != NULL) && (msr->msc_reqbody_error == 0)) {
319 char *my_error_msg = NULL;
320 msre_reqbody_processor_metadata *metadata =
321 (msre_reqbody_processor_metadata *)apr_table_get(msr->modsecurity->msre->reqbody_processors, msr->msc_reqbody_processor);
322
323 if (metadata != NULL) {
324 if ( (metadata->process != NULL)
325 && (metadata->process(msr, data, length, &my_error_msg) < 0))
326 {
327 *error_msg = apr_psprintf(msr->mp,
328 "%s parsing error: %s",
329 msr->msc_reqbody_processor,
330 my_error_msg);
331 msr->msc_reqbody_error = 1;
332 msr->msc_reqbody_error_msg = my_error_msg;
333 msr_log(msr, 2, "%s", *error_msg);
334 }
335 }
336 // TODO: All these below need to be registered in the same way as above
337 else if (strcmp(msr->msc_reqbody_processor, "MULTIPART") == 0) {
338 /* The per-request data length counter will
339 * be updated by the multipart parser.
340 */
341
342 /* Process data as multipart/form-data. */
343 if (multipart_process_chunk(msr, data, length, &my_error_msg) < 0) {
344 *error_msg = apr_psprintf(msr->mp, "Multipart parsing error: %s", my_error_msg);
345 msr->msc_reqbody_error = 1;
346 msr->msc_reqbody_error_msg = *error_msg;
347 msr_log(msr, 2, "%s", *error_msg);
348 }
349 }
350 else if (strcmp(msr->msc_reqbody_processor, "XML") == 0) {
351 /* Increase per-request data length counter. */
352 msr->msc_reqbody_no_files_length += length;
353
354 /* Process data as XML. */
355 if (xml_process_chunk(msr, data, length, &my_error_msg) < 0) {
356 *error_msg = apr_psprintf(msr->mp, "XML parsing error: %s", my_error_msg);
357 msr->msc_reqbody_error = 1;
358 msr->msc_reqbody_error_msg = *error_msg;
359 msr_log(msr, 2, "%s", *error_msg);
360 }
361 }
362 else if (strcmp(msr->msc_reqbody_processor, "JSON") == 0) {
363 /* Increase per-request data length counter. */
364 msr->msc_reqbody_no_files_length += length;
365
366 /* Process data as JSON. */
367 #ifdef WITH_YAJL
368 if (json_process_chunk(msr, data, length, &my_error_msg) < 0) {
369 *error_msg = apr_psprintf(msr->mp, "JSON parsing error: %s", my_error_msg);
370 msr->msc_reqbody_error = 1;
371 msr->msc_reqbody_error_msg = *error_msg;
372 msr_log(msr, 2, "%s", *error_msg);
373 }
374 #else
375 *error_msg = apr_psprintf(msr->mp, "JSON support was not enabled");
376 msr->msc_reqbody_error = 1;
377 msr->msc_reqbody_error_msg = *error_msg;
378 msr_log(msr, 2, "%s", *error_msg);
379 #endif
380 }
381 else if (strcmp(msr->msc_reqbody_processor, "URLENCODED") == 0) {
382 /* Increase per-request data length counter. */
383 msr->msc_reqbody_no_files_length += length;
384
385 /* Do nothing else, URLENCODED processor does not support streaming. */
386 }
387 else {
388 *error_msg = apr_psprintf(msr->mp, "Unknown request body processor: %s",
389 msr->msc_reqbody_processor);
390 return -1;
391 }
392 } else if (msr->txcfg->reqbody_buffering != REQUEST_BODY_FORCEBUF_OFF) {
393 /* Increase per-request data length counter if forcing buffering. */
394 msr->msc_reqbody_no_files_length += length;
395 }
396
397 /* Check that we are not over the request body no files limit. */
398 if (msr->msc_reqbody_no_files_length >= (unsigned long) msr->txcfg->reqbody_no_files_limit) {
399 *error_msg = apr_psprintf(msr->mp, "Request body no files data length is larger than the "
400 "configured limit (%ld).", msr->txcfg->reqbody_no_files_limit);
401 if (msr->txcfg->debuglog_level >= 1) {
402 msr_log(msr, 1, "%s", *error_msg);
403 }
404
405 msr->msc_reqbody_error = 1;
406
407 if ((msr->txcfg->is_enabled == MODSEC_ENABLED) && (msr->txcfg->if_limit_action == REQUEST_BODY_LIMIT_ACTION_REJECT)) {
408 return -5;
409 } else if (msr->txcfg->if_limit_action == REQUEST_BODY_LIMIT_ACTION_PARTIAL) {
410 if(msr->txcfg->is_enabled == MODSEC_ENABLED)
411 return -5;
412 }
413 }
414
415 /* Store data. */
416 if (msr->msc_reqbody_storage == MSC_REQBODY_MEMORY) {
417 return modsecurity_request_body_store_memory(msr, data, length, error_msg);
418 }
419 else
420 if (msr->msc_reqbody_storage == MSC_REQBODY_DISK) {
421 return modsecurity_request_body_store_disk(msr, data, length, error_msg);
422 }
423
424 /* Should never happen. */
425 *error_msg = apr_psprintf(msr->mp, "Internal error, unknown value for msc_reqbody_storage: %u",
426 msr->msc_reqbody_storage);
427 return -1;
428 }
429
modsecurity_request_body_to_stream(modsec_rec * msr,const char * buffer,int buflen,char ** error_msg)430 apr_status_t modsecurity_request_body_to_stream(modsec_rec *msr, const char *buffer, int buflen, char **error_msg) {
431 #ifndef MSC_LARGE_STREAM_INPUT
432 char *stream_input_body = NULL;
433 char *data = NULL;
434 int first_pkt = 0;
435 #else
436 apr_size_t allocate_length = 0;
437 char* allocated = NULL;
438 #endif
439
440 #ifndef MSC_LARGE_STREAM_INPUT
441 if(msr->stream_input_data == NULL) {
442 msr->stream_input_data = (char *)calloc(sizeof(char), msr->stream_input_length + 1);
443 first_pkt = 1;
444 }
445 else {
446
447 data = (char *)malloc(msr->stream_input_length + 1 - buflen);
448
449 if(data == NULL)
450 return -1;
451
452 memset(data, 0, msr->stream_input_length + 1 - buflen);
453 memcpy(data, msr->stream_input_data, msr->stream_input_length - buflen);
454
455 stream_input_body = (char *)realloc(msr->stream_input_data, msr->stream_input_length + 1);
456
457 msr->stream_input_data = (char *)stream_input_body;
458 }
459
460 if (msr->stream_input_data == NULL) {
461 if(data) {
462 free(data);
463 data = NULL;
464 }
465 *error_msg = apr_psprintf(msr->mp, "Unable to allocate memory to hold request body on stream. Asked for %" APR_SIZE_T_FMT " bytes.",
466 msr->stream_input_length + 1);
467 return -1;
468 }
469
470 memset(msr->stream_input_data, 0, msr->stream_input_length+1);
471
472 if(first_pkt) {
473 memcpy(msr->stream_input_data, buffer, msr->stream_input_length);
474 } else {
475 memcpy(msr->stream_input_data, data, msr->stream_input_length - buflen);
476 memcpy(msr->stream_input_data+(msr->stream_input_length - buflen), buffer, buflen);
477 }
478
479 if(data) {
480 free(data);
481 data = NULL;
482 }
483 #else
484 if (msr->stream_input_data == NULL) {
485 // Is the request body length known beforehand? (requests that are not Transfer-Encoding: chunked)
486 if (msr->request_content_length > 0) {
487 // Use min of Content-Length and SecRequestBodyLimit
488 allocate_length = msr->request_content_length < msr->txcfg->reqbody_limit ? msr->request_content_length : msr->txcfg->reqbody_limit;
489 }
490 else {
491 // We don't know how this request is going to be, so hope for just buflen to begin with (requests that are Transfer-Encoding: chunked)
492 allocate_length = buflen;
493 }
494
495 allocated = (char*) calloc(allocate_length, sizeof(char));
496 if (allocated) {
497 msr->stream_input_data = allocated;
498 msr->stream_input_allocated_length = allocate_length;
499 }
500 else {
501 *error_msg = apr_psprintf(
502 msr->mp,
503 "Unable to allocate memory to hold request body on stream. Asked for %" APR_SIZE_T_FMT " bytes.",
504 allocate_length);
505 return -1;
506 }
507 }
508 else {
509 // Do we need to expand the space we have previously allocated?
510 if ((msr->stream_input_length + buflen) > msr->stream_input_allocated_length) {
511 // If this becomes a hotspot again, consider increasing by some percent extra each time, for fewer reallocs
512 allocate_length = msr->stream_input_length + buflen;
513
514 allocated = (char*) realloc(msr->stream_input_data, allocate_length);
515 if (allocated) {
516 msr->stream_input_data = allocated;
517 msr->stream_input_allocated_length = allocate_length;
518 }
519 else {
520 *error_msg = apr_psprintf(
521 msr->mp,
522 "Unable to reallocate memory to hold request body on stream. Asked for %" APR_SIZE_T_FMT " bytes.",
523 allocate_length);
524 free(msr->stream_input_data);
525 msr->stream_input_data = NULL;
526 msr->stream_input_length = 0;
527 msr->stream_input_allocated_length = 0;
528 return -1;
529 }
530 }
531 }
532 // Append buffer to msr->stream_input_data
533 memcpy(msr->stream_input_data + msr->stream_input_length, buffer, buflen);
534 msr->stream_input_length += buflen;
535 #endif
536
537 return 1;
538 }
539
540 /**
541 * Replace a bunch of chunks holding a request body with a single large chunk.
542 */
modsecurity_request_body_end_raw(modsec_rec * msr,char ** error_msg)543 static apr_status_t modsecurity_request_body_end_raw(modsec_rec *msr, char **error_msg) {
544 msc_data_chunk **chunks, *one_chunk;
545 char *d;
546 int i, sofar;
547
548 *error_msg = NULL;
549
550 /* Allocate a buffer large enough to hold the request body. */
551
552 if (msr->msc_reqbody_length + 1 == 0) {
553 *error_msg = apr_psprintf(msr->mp, "Internal error, request body length will overflow: %u",
554 msr->msc_reqbody_length);
555 return -1;
556 }
557
558 msr->msc_reqbody_buffer = malloc(msr->msc_reqbody_length + 1);
559 if (msr->msc_reqbody_buffer == NULL) {
560 *error_msg = apr_psprintf(msr->mp, "Unable to allocate memory to hold request body. Asked for %u bytes.",
561 msr->msc_reqbody_length + 1);
562 return -1;
563 }
564
565 msr->msc_reqbody_buffer[msr->msc_reqbody_length] = '\0';
566
567 /* Copy the data we keep in chunks into the new buffer. */
568
569 sofar = 0;
570 d = msr->msc_reqbody_buffer;
571 chunks = (msc_data_chunk **)msr->msc_reqbody_chunks->elts;
572 for(i = 0; i < msr->msc_reqbody_chunks->nelts; i++) {
573 if (sofar + chunks[i]->length <= msr->msc_reqbody_length) {
574 memcpy(d, chunks[i]->data, chunks[i]->length);
575 d += chunks[i]->length;
576 sofar += chunks[i]->length;
577 } else {
578 *error_msg = apr_psprintf(msr->mp, "Internal error, request body buffer overflow.");
579 return -1;
580 }
581 }
582
583
584 /* Now free the memory used by the chunks. */
585
586 chunks = (msc_data_chunk **)msr->msc_reqbody_chunks->elts;
587 for(i = 0; i < msr->msc_reqbody_chunks->nelts; i++) {
588 free(chunks[i]->data);
589 chunks[i]->data = NULL;
590 }
591
592 /* Create a new array with only one chunk in it. */
593
594 msr->msc_reqbody_chunks = apr_array_make(msr->msc_reqbody_mp, 2, sizeof(msc_data_chunk *));
595 if (msr->msc_reqbody_chunks == NULL) {
596 *error_msg = apr_pstrdup(msr->mp, "Failed to create structure to hold request body.");
597 return -1;
598 }
599
600 one_chunk = (msc_data_chunk *)apr_pcalloc(msr->msc_reqbody_mp, sizeof(msc_data_chunk));
601 one_chunk->data = msr->msc_reqbody_buffer;
602 one_chunk->length = msr->msc_reqbody_length;
603 one_chunk->is_permanent = 1;
604 *(const msc_data_chunk **)apr_array_push(msr->msc_reqbody_chunks) = one_chunk;
605
606 if(msr->txcfg->reqbody_limit > 0 && msr->txcfg->reqbody_limit < msr->msc_reqbody_length) {
607 msr->msc_reqbody_length = msr->txcfg->reqbody_limit;
608 }
609
610 return 1;
611 }
612
613 /**
614 *
615 */
modsecurity_request_body_end_urlencoded(modsec_rec * msr,char ** error_msg)616 static apr_status_t modsecurity_request_body_end_urlencoded(modsec_rec *msr, char **error_msg) {
617 int invalid_count = 0;
618
619 *error_msg = NULL;
620
621 /* Create the raw buffer */
622 if (modsecurity_request_body_end_raw(msr, error_msg) != 1) {
623 return -1;
624 }
625
626 /* Parse URL-encoded arguments in the request body. */
627
628 if (parse_arguments(msr, msr->msc_reqbody_buffer, msr->msc_reqbody_length,
629 msr->txcfg->argument_separator, "BODY", msr->arguments, &invalid_count) < 0)
630 {
631 *error_msg = apr_pstrdup(msr->mp, "Initialisation: Error occurred while parsing BODY arguments.");
632 return -1;
633 }
634
635 if (invalid_count) {
636 msr->urlencoded_error = 1;
637 }
638
639 return 1;
640 }
641
642 /**
643 * Stops receiving the request body.
644 */
modsecurity_request_body_end(modsec_rec * msr,char ** error_msg)645 apr_status_t modsecurity_request_body_end(modsec_rec *msr, char **error_msg) {
646 *error_msg = NULL;
647
648 /* Close open file descriptors, if any. */
649 if (msr->msc_reqbody_storage == MSC_REQBODY_DISK) {
650 if (msr->msc_reqbody_fd > 0) {
651 close(msr->msc_reqbody_fd);
652 msr->msc_reqbody_fd = -1;
653 }
654 }
655
656 /* Note that we've read the body. */
657 msr->msc_reqbody_read = 1;
658
659
660 /* Check that we are not over the request body no files limit. */
661 if (msr->msc_reqbody_no_files_length >= (unsigned long)msr->txcfg->reqbody_no_files_limit) {
662 *error_msg = apr_psprintf(msr->mp, "Request body no files data length is larger than the "
663 "configured limit (%ld).", msr->txcfg->reqbody_no_files_limit);
664 if (msr->txcfg->debuglog_level >= 1) {
665 msr_log(msr, 1, "%s", *error_msg);
666 }
667
668 return -5;
669 }
670
671
672 /* Finalise body processing. */
673 if ((msr->msc_reqbody_processor != NULL) && (msr->msc_reqbody_error == 0)) {
674 char *my_error_msg = NULL;
675 msre_reqbody_processor_metadata *metadata =
676 (msre_reqbody_processor_metadata *)apr_table_get(msr->modsecurity->msre->reqbody_processors, msr->msc_reqbody_processor);
677
678 if (metadata != NULL) {
679 if ( (metadata->complete != NULL)
680 && (metadata->complete(msr, &my_error_msg) < 0))
681 {
682 *error_msg = apr_psprintf(msr->mp,
683 "%s parsing error (complete): %s",
684 msr->msc_reqbody_processor,
685 my_error_msg);
686 msr->msc_reqbody_error = 1;
687 msr->msc_reqbody_error_msg = my_error_msg;
688 msr_log(msr, 2, "%s", *error_msg);
689 }
690 }
691 // TODO: All these below need to be registered in the same way as above
692 else if (strcmp(msr->msc_reqbody_processor, "MULTIPART") == 0) {
693 if (multipart_complete(msr, &my_error_msg) < 0) {
694 *error_msg = apr_psprintf(msr->mp, "Multipart parsing error: %s", my_error_msg);
695 msr->msc_reqbody_error = 1;
696 msr->msc_reqbody_error_msg = *error_msg;
697 if (msr->txcfg->debuglog_level >= 4) {
698 msr_log(msr, 4, "%s", *error_msg);
699 }
700 return -1;
701 }
702
703 if (multipart_get_arguments(msr, "BODY", msr->arguments) < 0) {
704 *error_msg = "Multipart parsing error: Failed to retrieve arguments.";
705 msr->msc_reqbody_error = 1;
706 msr->msc_reqbody_error_msg = *error_msg;
707 msr_log(msr, 2, "%s", *error_msg);
708 return -1;
709 }
710 }
711 else if (strcmp(msr->msc_reqbody_processor, "JSON") == 0) {
712 #ifdef WITH_YAJL
713 if (json_complete(msr, &my_error_msg) < 0 && msr->msc_reqbody_length > 0) {
714 *error_msg = apr_psprintf(msr->mp, "JSON parser error: %s", my_error_msg);
715 msr->msc_reqbody_error = 1;
716 msr->msc_reqbody_error_msg = *error_msg;
717 msr_log(msr, 2, "%s", *error_msg);
718 return -1;
719 }
720 #else
721 *error_msg = apr_psprintf(msr->mp, "JSON support was not enabled");
722 msr->msc_reqbody_error = 1;
723 msr->msc_reqbody_error_msg = *error_msg;
724 msr_log(msr, 2, "%s", *error_msg);
725 return -1;
726 #endif
727
728 }
729 else if (strcmp(msr->msc_reqbody_processor, "URLENCODED") == 0) {
730 return modsecurity_request_body_end_urlencoded(msr, error_msg);
731 }
732 else if (strcmp(msr->msc_reqbody_processor, "XML") == 0) {
733 if (xml_complete(msr, &my_error_msg) < 0) {
734 *error_msg = apr_psprintf(msr->mp, "XML parser error: %s", my_error_msg);
735 msr->msc_reqbody_error = 1;
736 msr->msc_reqbody_error_msg = *error_msg;
737 msr_log(msr, 2, "%s", *error_msg);
738 return -1;
739 }
740 }
741 } else if (msr->txcfg->reqbody_buffering != REQUEST_BODY_FORCEBUF_OFF) {
742 /* Convert to a single continous buffer, but don't do anything else. */
743 return modsecurity_request_body_end_raw(msr, error_msg);
744 }
745
746 /* Note the request body no files length. */
747 msr_log(msr, 4, "Request body no files length: %" APR_SIZE_T_FMT, msr->msc_reqbody_no_files_length);
748
749 return 1;
750 }
751
752 /**
753 * Prepares to forward the request body.
754 */
modsecurity_request_body_retrieve_start(modsec_rec * msr,char ** error_msg)755 apr_status_t modsecurity_request_body_retrieve_start(modsec_rec *msr, char **error_msg) {
756 *error_msg = NULL;
757
758 if (msr->msc_reqbody_storage == MSC_REQBODY_MEMORY) {
759 msr->msc_reqbody_chunk_position = 0;
760 msr->msc_reqbody_chunk_offset = 0;
761
762 msr->msc_reqbody_disk_chunk = apr_pcalloc(msr->msc_reqbody_mp, sizeof(msc_data_chunk));
763 if (msr->msc_reqbody_disk_chunk == NULL) {
764 *error_msg = apr_psprintf(msr->mp, "Failed to allocate %lu bytes for request body disk chunk.",
765 (unsigned long)sizeof(msc_data_chunk));
766 return -1;
767 }
768 msr->msc_reqbody_disk_chunk->is_permanent = 1;
769 }
770 else
771 if (msr->msc_reqbody_storage == MSC_REQBODY_DISK) {
772 msr->msc_reqbody_disk_chunk = apr_pcalloc(msr->msc_reqbody_mp, sizeof(msc_data_chunk));
773 if (msr->msc_reqbody_disk_chunk == NULL) {
774 *error_msg = apr_psprintf(msr->mp, "Failed to allocate %lu bytes for request body disk chunk.",
775 (unsigned long)sizeof(msc_data_chunk));
776 return -1;
777 }
778
779 msr->msc_reqbody_disk_chunk->is_permanent = 0;
780 msr->msc_reqbody_disk_chunk->data = apr_palloc(msr->msc_reqbody_mp, CHUNK_CAPACITY);
781 if (msr->msc_reqbody_disk_chunk->data == NULL) {
782 *error_msg = apr_psprintf(msr->mp, "Failed to allocate %d bytes for request body disk chunk data.",
783 CHUNK_CAPACITY);
784 return -1;
785 }
786
787 msr->msc_reqbody_fd = open(msr->msc_reqbody_filename, O_RDONLY | O_BINARY);
788 if (msr->msc_reqbody_fd < 0) {
789 *error_msg = apr_psprintf(msr->mp, "Failed to open temporary file for reading: %s",
790 msr->msc_reqbody_filename);
791 return -1;
792 }
793 }
794
795 return 1;
796 }
797
798 /**
799 *
800 */
modsecurity_request_body_retrieve_end(modsec_rec * msr)801 apr_status_t modsecurity_request_body_retrieve_end(modsec_rec *msr) {
802 if (msr->msc_reqbody_storage == MSC_REQBODY_DISK) {
803 if (msr->msc_reqbody_fd > 0) {
804 close(msr->msc_reqbody_fd);
805 msr->msc_reqbody_fd = -1;
806 }
807 }
808
809 return 1;
810 }
811
812 /**
813 * Returns one chunk of request body data. It stores a NULL
814 * in the chunk pointer when there is no data to return. The
815 * return code is 1 if more calls can be made to retrieve more
816 * data, 0 if there is no more data to retrieve, or -1 on error.
817 *
818 * The caller can limit the amount of data returned by providing
819 * a non-negative value in nbytes.
820 */
modsecurity_request_body_retrieve(modsec_rec * msr,msc_data_chunk ** chunk,long int nbytes,char ** error_msg)821 apr_status_t modsecurity_request_body_retrieve(modsec_rec *msr,
822 msc_data_chunk **chunk, long int nbytes, char **error_msg)
823 {
824 msc_data_chunk **chunks;
825
826 *error_msg = NULL;
827
828 if (chunk == NULL) {
829 *error_msg = apr_pstrdup(msr->mp, "Internal error, retrieving request body chunk.");
830 return -1;
831 }
832 *chunk = NULL;
833
834 if (msr->msc_reqbody_storage == MSC_REQBODY_MEMORY) {
835 /* Are there any chunks left? */
836 if (msr->msc_reqbody_chunk_position >= msr->msc_reqbody_chunks->nelts) {
837 /* No more chunks. */
838 return 0;
839 }
840
841 /* We always respond with the same chunk, just different information in it. */
842 *chunk = msr->msc_reqbody_disk_chunk;
843
844 /* Advance to the current chunk and position on the
845 * next byte we need to send.
846 */
847 chunks = (msc_data_chunk **)msr->msc_reqbody_chunks->elts;
848 msr->msc_reqbody_disk_chunk->data = chunks[msr->msc_reqbody_chunk_position]->data
849 + msr->msc_reqbody_chunk_offset;
850
851 if (nbytes < 0) {
852 /* Send what's left in this chunk as there is no limit on the size. */
853 msr->msc_reqbody_disk_chunk->length = chunks[msr->msc_reqbody_chunk_position]->length;
854 msr->msc_reqbody_chunk_position++;
855 msr->msc_reqbody_chunk_offset = 0;
856 } else {
857 /* We have a limit we must obey. */
858
859 if (chunks[msr->msc_reqbody_chunk_position]->length -
860 msr->msc_reqbody_chunk_offset <= (unsigned int)nbytes)
861 {
862 /* If what's left in our chunk is less than the limit
863 * then send it all back.
864 */
865 msr->msc_reqbody_disk_chunk->length =
866 chunks[msr->msc_reqbody_chunk_position]->length -
867 msr->msc_reqbody_chunk_offset;
868 msr->msc_reqbody_chunk_position++;
869 msr->msc_reqbody_chunk_offset = 0;
870 } else {
871 /* If we have more data in our chunk, send the
872 * maximum bytes we can (nbytes).
873 */
874 msr->msc_reqbody_disk_chunk->length = nbytes;
875 msr->msc_reqbody_chunk_offset += nbytes;
876 }
877 }
878
879 /* If we've advanced beyond our last chunk then
880 * we have no more data to send.
881 */
882 if (msr->msc_reqbody_chunk_position >= msr->msc_reqbody_chunks->nelts) {
883 return 0; /* No more chunks. */
884 }
885
886 /* More data available. */
887 return 1;
888 }
889
890 if (msr->msc_reqbody_storage == MSC_REQBODY_DISK) {
891 long int my_nbytes = CHUNK_CAPACITY;
892 int i;
893
894 /* Send CHUNK_CAPACITY bytes at a time unless a lower limit was requested. */
895 if ((nbytes != -1)&&(my_nbytes > nbytes)) {
896 my_nbytes = nbytes;
897 }
898
899 i = read(msr->msc_reqbody_fd, msr->msc_reqbody_disk_chunk->data, my_nbytes);
900 if (i < 0) {
901 *error_msg = apr_psprintf(msr->mp, "Input filter: Error reading from temporary file: %s",
902 strerror(errno));
903 return -1;
904 }
905
906 *chunk = msr->msc_reqbody_disk_chunk;
907 msr->msc_reqbody_disk_chunk->length = i;
908
909 if (i == 0) return 0; /* No more data available. */
910
911 return 1; /* More data available. */
912 }
913
914 /* Should never happen. */
915 *error_msg = apr_psprintf(msr->mp, "Internal error, invalid msc_reqbody_storage value: %u",
916 msr->msc_reqbody_storage);
917
918 return -1;
919 }
920
921 /**
922 *
923 */
modsecurity_request_body_clear(modsec_rec * msr,char ** error_msg)924 apr_status_t modsecurity_request_body_clear(modsec_rec *msr, char **error_msg) {
925 *error_msg = NULL;
926
927 /* Release memory we used to store request body data. */
928 if (msr->msc_reqbody_chunks != NULL) {
929 msc_data_chunk **chunks = (msc_data_chunk **)msr->msc_reqbody_chunks->elts;
930 int i;
931
932 for(i = 0; i < msr->msc_reqbody_chunks->nelts; i++) {
933 if (chunks[i]->data != NULL) {
934 free(chunks[i]->data);
935 chunks[i]->data = NULL;
936 }
937 }
938 }
939
940 if (msr->msc_reqbody_storage == MSC_REQBODY_DISK) {
941 int keep_body = 0;
942
943 /* Should we keep the body? This normally
944 * happens when a PUT method was used, which
945 * means the body is actually a file.
946 */
947 if ((msr->upload_remove_files == 0)&&(strcasecmp(msr->request_method, "PUT") == 0)) {
948 if (msr->txcfg->upload_dir != NULL) {
949 keep_body = 1;
950 } else {
951 *error_msg = apr_psprintf(msr->mp, "Input filter: SecUploadDir is undefined, "
952 "unable to store PUT file.");
953 }
954 }
955
956 /* Deal with a request body stored in a file. */
957
958 if (msr->msc_reqbody_filename != NULL) {
959 if (keep_body) {
960 /* Move request body (which is a file) to the storage area. */
961 const char *put_filename = NULL;
962 const char *put_basename = NULL;
963
964 if (strcmp(msr->txcfg->upload_dir, msr->txcfg->tmp_dir) == 0) {
965 msr_log(msr, 4, "Not moving file to identical location.");
966 goto nullify;
967 }
968
969 /* Construct the new filename. */
970 put_basename = file_basename(msr->msc_reqbody_mp, msr->msc_reqbody_filename);
971 if (put_basename == NULL) {
972 *error_msg = apr_psprintf(msr->mp, "Input filter: Failed to generate basename to PUT file \"%s\"", log_escape(msr->msc_reqbody_mp, msr->msc_reqbody_filename));
973 return -1;
974 }
975 put_filename = apr_psprintf(msr->msc_reqbody_mp, "%s/%s",
976 msr->txcfg->upload_dir, put_basename);
977 if (put_filename == NULL) {
978 *error_msg = apr_psprintf(msr->mp, "Input filter: Failed to generate filename to PUT file \"%s\"", log_escape(msr->msc_reqbody_mp, msr->msc_reqbody_filename));
979 return -1;
980 }
981
982 if (apr_file_rename(msr->msc_reqbody_filename, put_filename,
983 msr->msc_reqbody_mp) != APR_SUCCESS)
984 {
985 *error_msg = apr_psprintf(msr->mp, "Input filter: Failed to rename file from \"%s\" to \"%s\".",
986 log_escape(msr->msc_reqbody_mp, msr->msc_reqbody_filename),
987 log_escape(msr->msc_reqbody_mp, put_filename));
988 return -1;
989 } else {
990 msr_log(msr, 4, "Input filter: Moved file from \"%s\" to \"%s\".",
991 log_escape(msr->msc_reqbody_mp, msr->msc_reqbody_filename),
992 log_escape(msr->msc_reqbody_mp, put_filename));
993 }
994 } else {
995 /* make sure it is closed first */
996 if (msr->msc_reqbody_fd > 0) {
997 close(msr->msc_reqbody_fd);
998 msr->msc_reqbody_fd = -1;
999 }
1000
1001 /* We do not want to keep the request body. */
1002 if (apr_file_remove(msr->msc_reqbody_filename,
1003 msr->msc_reqbody_mp) != APR_SUCCESS)
1004 {
1005 *error_msg = apr_psprintf(msr->mp, "Input filter: Failed to delete temporary file: %s",
1006 log_escape(msr->mp, msr->msc_reqbody_filename));
1007 return -1;
1008 }
1009
1010 msr_log(msr, 4, "Input filter: Removed temporary file: %s",
1011 msr->msc_reqbody_filename);
1012 }
1013
1014 nullify:
1015
1016 msr->msc_reqbody_filename = NULL;
1017 }
1018 }
1019
1020 if (msr->msc_reqbody_mp != NULL) {
1021 apr_pool_destroy(msr->msc_reqbody_mp);
1022 msr->msc_reqbody_mp = NULL;
1023 }
1024
1025 return 1;
1026 }
1027