Imported Upstream version 2.5.11
[libapache-mod-security.git] / apache2 / msc_reqbody.c
1 /*
2  * ModSecurity for Apache 2.x, http://www.modsecurity.org/
3  * Copyright (c) 2004-2009 Breach Security, Inc. (http://www.breach.com/)
4  *
5  * This product is released under the terms of the General Public Licence,
6  * version 2 (GPLv2). Please refer to the file LICENSE (included with this
7  * distribution) which contains the complete text of the licence.
8  *
9  * There are special exceptions to the terms and conditions of the GPL
10  * as it is applied to this software. View the full text of the exception in
11  * file MODSECURITY_LICENSING_EXCEPTION in the directory of this software
12  * distribution.
13  *
14  * If any of the files related to licensing are missing or if you have any
15  * other questions related to licensing please contact Breach Security, Inc.
16  * directly using the email address support@breach.com.
17  *
18  */
19 #include "modsecurity.h"
20 #include "msc_parsers.h"
21
22 #define CHUNK_CAPACITY 8192
23
24 /**
25  * Prepare to accept the request body (part 2).
26  */
27 static apr_status_t modsecurity_request_body_start_init(modsec_rec *msr, char **error_msg) {
28     *error_msg = NULL;
29
30     if(msr->msc_reqbody_storage == MSC_REQBODY_MEMORY) {
31         /* Prepare to store request body in memory. */
32
33         msr->msc_reqbody_chunks = apr_array_make(msr->msc_reqbody_mp,
34             32, sizeof(msc_data_chunk *));
35         if (msr->msc_reqbody_chunks == NULL) {
36             *error_msg = apr_pstrdup(msr->mp, "Input filter: Failed to prepare in-memory storage.");
37             return -1;
38         }
39     } else {
40         /* Prepare to store request body on disk. */
41
42         msr->msc_reqbody_filename = apr_psprintf(msr->mp, "%s/%s-%s-request_body-XXXXXX",
43             msr->txcfg->tmp_dir, current_filetime(msr->mp), msr->txid);
44         if (msr->msc_reqbody_filename == NULL) {
45             *error_msg = apr_pstrdup(msr->mp, "Input filter: Failed to generate an on-disk filename.");
46             return -1;
47         }
48
49         msr->msc_reqbody_fd = msc_mkstemp((char *)msr->msc_reqbody_filename);
50         if (msr->msc_reqbody_fd < 0) {
51             *error_msg = apr_psprintf(msr->mp, "Input filter: Failed to create temporary file: %s",
52                 msr->msc_reqbody_filename);
53             return -1;
54         }
55
56         msr_log(msr, 4, "Input filter: Created temporary file to store request body: %s",
57             msr->msc_reqbody_filename);
58     }
59
60     return 1;
61 }
62
63 /**
64  * Prepare to accept the request body (part 1).
65  */
66 apr_status_t modsecurity_request_body_start(modsec_rec *msr, char **error_msg) {
67     *error_msg = NULL;
68     msr->msc_reqbody_length = 0;
69
70     /* Create a separate memory pool that will be used
71      * to allocate structures from (not data, which is allocated
72      * via malloc).
73      */
74     apr_pool_create(&msr->msc_reqbody_mp, NULL);
75
76     /* Initialise request body processors, if any. */
77
78     if (msr->msc_reqbody_processor != NULL) {
79         char *my_error_msg = NULL;
80
81         if (strcmp(msr->msc_reqbody_processor, "MULTIPART") == 0) {
82             if (multipart_init(msr, &my_error_msg) < 0) {
83                 *error_msg = apr_psprintf(msr->mp, "Multipart parsing error (init): %s", my_error_msg);
84                 msr->msc_reqbody_error = 1;
85                 msr->msc_reqbody_error_msg = my_error_msg;
86                 msr_log(msr, 2, "%s", *error_msg);
87             }
88         }
89         else
90         if (strcmp(msr->msc_reqbody_processor, "XML") == 0) {
91             if (xml_init(msr, &my_error_msg) < 0) {
92                 *error_msg = apr_psprintf(msr->mp, "XML parsing error (init): %s", my_error_msg);
93                 msr->msc_reqbody_error = 1;
94                 msr->msc_reqbody_error_msg = my_error_msg;
95                 msr_log(msr, 2, "%s", *error_msg);
96             }
97         }
98         else
99         if (strcmp(msr->msc_reqbody_processor, "URLENCODED") == 0) {
100             /* Do nothing, URLENCODED processor does not support streaming yet. */
101         }
102         else {
103             *error_msg = apr_psprintf(msr->mp, "Unknown request body processor: %s",
104                 msr->msc_reqbody_processor);
105             return -1;
106         }
107     }
108
109     return modsecurity_request_body_start_init(msr, error_msg);
110 }
111
112 /**
113  * Stores a chunk of request body data to disk.
114  */
115 static apr_status_t modsecurity_request_body_store_disk(modsec_rec *msr,
116     const char *data, apr_size_t length, char **error_msg)
117 {
118     apr_size_t i;
119
120     *error_msg = NULL;
121
122     i = write(msr->msc_reqbody_fd, data, length);
123     if (i != length) {
124         *error_msg = apr_psprintf(msr->mp, "Input filter: Failed writing %" APR_SIZE_T_FMT
125             " bytes to temporary file (rc %" APR_SIZE_T_FMT ").", length, i);
126         return -1;
127     }
128
129     return 1;
130 }
131
132 /**
133  * Stores one chunk of request body data in memory.
134  */
135 static apr_status_t modsecurity_request_body_store_memory(modsec_rec *msr,
136     const char *data, apr_size_t length, char **error_msg)
137 {
138     *error_msg = NULL;
139
140     /* Would storing this chunk mean going over the limit? */
141     if ((msr->msc_reqbody_spilltodisk)
142         && (msr->msc_reqbody_length + length > (apr_size_t)msr->txcfg->reqbody_inmemory_limit))
143     {
144         msc_data_chunk **chunks;
145         unsigned int disklen = 0;
146         int i;
147
148         msr_log(msr, 4, "Input filter: Request too large to store in memory, switching to disk.");
149
150         /* NOTE Must use modsecurity_request_body_store_disk() here
151          *      to prevent data to be sent to the streaming
152          *      processors again.
153          */
154
155         /* Initialise disk storage */
156         msr->msc_reqbody_storage = MSC_REQBODY_DISK;
157         if (modsecurity_request_body_start_init(msr, error_msg) < 0) return -1;
158
159         /* Write the data we keep in memory */
160         chunks = (msc_data_chunk **)msr->msc_reqbody_chunks->elts;
161         for(i = 0; i < msr->msc_reqbody_chunks->nelts; i++) {
162             disklen += chunks[i]->length;
163
164             if (modsecurity_request_body_store_disk(msr, chunks[i]->data, chunks[i]->length, error_msg) < 0) {
165                 return -1;
166             }
167
168             free(chunks[i]->data);
169             chunks[i]->data = NULL;
170         }
171
172         /* Clear the memory pool as we no longer need the bits. */
173
174         /* IMP1 But since we only used apr_pool_clear memory might
175          * not be released back to the OS straight away?
176          */
177         msr->msc_reqbody_chunks = NULL;
178         apr_pool_clear(msr->msc_reqbody_mp);
179
180         msr_log(msr, 4, "Input filter: Wrote %u bytes from memory to disk.", disklen);
181
182         /* Continue with disk storage from now on */
183         return modsecurity_request_body_store_disk(msr, data, length, error_msg);
184     }
185
186     /* If we're here that means we are not over the
187      * request body in-memory limit yet.
188      */
189     {
190         unsigned long int bucket_offset, bucket_left;
191
192         bucket_offset = 0;
193         bucket_left = length;
194
195         /* Although we store the request body in chunks we don't
196          * want to use the same chunk sizes as the incoming memory
197          * buffers. They are often of very small sizes and that
198          * would make us waste a lot of memory. That's why we
199          * use our own chunks of CHUNK_CAPACITY sizes.
200          */
201
202         /* Loop until we empty this bucket into our chunks. */
203         while(bucket_left > 0) {
204             /* Allocate a new chunk if we have to. */
205             if (msr->msc_reqbody_chunk_current == NULL) {
206                 msr->msc_reqbody_chunk_current = (msc_data_chunk *)
207                     apr_pcalloc(msr->msc_reqbody_mp, sizeof(msc_data_chunk));
208                 if (msr->msc_reqbody_chunk_current == NULL) {
209                     *error_msg = apr_psprintf(msr->mp, "Input filter: Failed to allocate %lu bytes "
210                         "for request body chunk.", (unsigned long)sizeof(msc_data_chunk));
211                     return -1;
212                 }
213
214                 msr->msc_reqbody_chunk_current->data = malloc(CHUNK_CAPACITY);
215                 if (msr->msc_reqbody_chunk_current->data == NULL) {
216                     *error_msg = apr_psprintf(msr->mp, "Input filter: Failed to allocate %d bytes "
217                         "for request body chunk data.", CHUNK_CAPACITY);
218                     return -1;
219                 }
220
221                 msr->msc_reqbody_chunk_current->length = 0;
222                 msr->msc_reqbody_chunk_current->is_permanent = 1;
223
224                 *(const msc_data_chunk **)apr_array_push(msr->msc_reqbody_chunks)
225                     = msr->msc_reqbody_chunk_current;
226             }
227
228             if (bucket_left < (CHUNK_CAPACITY - msr->msc_reqbody_chunk_current->length)) {
229                 /* There's enough space in the current chunk. */
230                 memcpy(msr->msc_reqbody_chunk_current->data +
231                     msr->msc_reqbody_chunk_current->length, data + bucket_offset, bucket_left);
232                 msr->msc_reqbody_chunk_current->length += bucket_left;
233                 bucket_left = 0;
234             } else {
235                 /* Fill the existing chunk. */
236                 unsigned long int copy_length = CHUNK_CAPACITY -
237                     msr->msc_reqbody_chunk_current->length;
238
239                 memcpy(msr->msc_reqbody_chunk_current->data +
240                     msr->msc_reqbody_chunk_current->length, data + bucket_offset, copy_length);
241                 bucket_offset += copy_length;
242                 bucket_left -= copy_length;
243                 msr->msc_reqbody_chunk_current->length += copy_length;
244
245                 /* We're done with this chunk. Setting the pointer
246                  * to NULL is going to force a new chunk to be allocated
247                  * on the next go.
248                  */
249                 msr->msc_reqbody_chunk_current = NULL;
250             }
251         }
252
253         msr->msc_reqbody_length += length;
254     }
255
256     return 1;
257 }
258
259 /**
260  * Stores one chunk of request body data. Returns -1 on error.
261  */
262 apr_status_t modsecurity_request_body_store(modsec_rec *msr,
263     const char *data, apr_size_t length, char **error_msg)
264 {
265     *error_msg = NULL;
266
267     /* If we have a processor for this request body send
268      * data to it first (but only if it did not report an
269      * error on previous invocations).
270      */
271     if ((msr->msc_reqbody_processor != NULL)&&(msr->msc_reqbody_error == 0)) {
272         char *my_error_msg = NULL;
273
274         if (strcmp(msr->msc_reqbody_processor, "MULTIPART") == 0) {
275             /* The per-request data length counter will
276              * be updated by the multipart parser.
277              */
278
279             /* Process data as multipart/form-data. */
280             if (multipart_process_chunk(msr, data, length, &my_error_msg) < 0) {
281                 *error_msg = apr_psprintf(msr->mp, "Multipart parsing error: %s", my_error_msg);
282                 msr->msc_reqbody_error = 1;
283                 msr->msc_reqbody_error_msg = *error_msg;
284                 msr_log(msr, 2, "%s", *error_msg);
285             }
286         }
287         else
288         if (strcmp(msr->msc_reqbody_processor, "XML") == 0) {
289             /* Increase per-request data length counter. */
290             msr->msc_reqbody_no_files_length += length;
291
292             /* Process data as XML. */
293             if (xml_process_chunk(msr, data, length, &my_error_msg) < 0) {
294                 *error_msg = apr_psprintf(msr->mp, "XML parsing error: %s", my_error_msg);
295                 msr->msc_reqbody_error = 1;
296                 msr->msc_reqbody_error_msg = *error_msg;
297                 msr_log(msr, 2, "%s", *error_msg);
298             }
299         }
300         else
301         if (strcmp(msr->msc_reqbody_processor, "URLENCODED") == 0) {
302             /* Increase per-request data length counter. */
303             msr->msc_reqbody_no_files_length += length;
304
305             /* Do nothing else, URLENCODED processor does not support streaming. */
306         }
307         else {
308             *error_msg = apr_psprintf(msr->mp, "Unknown request body processor: %s",
309                 msr->msc_reqbody_processor);
310             return -1;
311         }
312     } else if (msr->txcfg->reqbody_buffering != REQUEST_BODY_FORCEBUF_OFF) {
313         /* Increase per-request data length counter if forcing buffering. */
314         msr->msc_reqbody_no_files_length += length;
315     }
316
317     /* Check that we are not over the request body no files limit. */
318     if (msr->msc_reqbody_no_files_length >= (unsigned long) msr->txcfg->reqbody_no_files_limit) {
319         return -5;
320     }
321
322     /* Store data. */
323     if (msr->msc_reqbody_storage == MSC_REQBODY_MEMORY) {
324         return modsecurity_request_body_store_memory(msr, data, length, error_msg);
325     }
326     else
327     if (msr->msc_reqbody_storage == MSC_REQBODY_DISK) {
328         return modsecurity_request_body_store_disk(msr, data, length, error_msg);
329     }
330
331     /* Should never happen. */
332     *error_msg = apr_psprintf(msr->mp, "Internal error, unknown value for msc_reqbody_storage: %u",
333         msr->msc_reqbody_storage);
334     return -1;
335 }
336
337 /**
338  * Replace a bunch of chunks holding a request body with a single large chunk.
339  */
340 static apr_status_t modsecurity_request_body_end_raw(modsec_rec *msr, char **error_msg) {
341     msc_data_chunk **chunks, *one_chunk;
342     char *d;
343     int i, sofar;
344
345     *error_msg = NULL;
346
347     /* Allocate a buffer large enough to hold the request body. */
348
349     if (msr->msc_reqbody_length + 1 == 0) {
350         *error_msg = apr_psprintf(msr->mp, "Internal error, request body length will overflow: %u",
351             msr->msc_reqbody_length);
352         return -1;
353     }
354
355     msr->msc_reqbody_buffer = malloc(msr->msc_reqbody_length + 1);
356     if (msr->msc_reqbody_buffer == NULL) {
357         *error_msg = apr_psprintf(msr->mp, "Unable to allocate memory to hold request body. Asked for %u bytes.",
358             msr->msc_reqbody_length + 1);
359         return -1;
360     }
361
362     msr->msc_reqbody_buffer[msr->msc_reqbody_length] = '\0';
363
364     /* Copy the data we keep in chunks into the new buffer. */
365
366     sofar = 0;
367     d = msr->msc_reqbody_buffer;
368     chunks = (msc_data_chunk **)msr->msc_reqbody_chunks->elts;
369     for(i = 0; i < msr->msc_reqbody_chunks->nelts; i++) {
370         if (sofar + chunks[i]->length <= msr->msc_reqbody_length) {
371             memcpy(d, chunks[i]->data, chunks[i]->length);
372             d += chunks[i]->length;
373             sofar += chunks[i]->length;
374         } else {
375             *error_msg = apr_psprintf(msr->mp, "Internal error, request body buffer overflow.");
376             return -1;
377         }
378     }
379
380     /* Now free the memory used by the chunks. */
381
382     chunks = (msc_data_chunk **)msr->msc_reqbody_chunks->elts;
383     for(i = 0; i < msr->msc_reqbody_chunks->nelts; i++) {
384         free(chunks[i]->data);
385         chunks[i]->data = NULL;
386     }
387
388     /* Create a new array with only one chunk in it. */
389
390     msr->msc_reqbody_chunks = apr_array_make(msr->msc_reqbody_mp, 2, sizeof(msc_data_chunk *));
391     if (msr->msc_reqbody_chunks == NULL) {
392         *error_msg = apr_pstrdup(msr->mp, "Failed to create structure to hold request body.");
393         return -1;
394     }
395
396     one_chunk = (msc_data_chunk *)apr_pcalloc(msr->msc_reqbody_mp, sizeof(msc_data_chunk));
397     one_chunk->data = msr->msc_reqbody_buffer;
398     one_chunk->length = msr->msc_reqbody_length;
399     one_chunk->is_permanent = 1;
400     *(const msc_data_chunk **)apr_array_push(msr->msc_reqbody_chunks) = one_chunk;
401
402     return 1;
403 }
404
405 /**
406  *
407  */
408 static apr_status_t modsecurity_request_body_end_urlencoded(modsec_rec *msr, char **error_msg) {
409     int invalid_count = 0;
410
411     *error_msg = NULL;
412
413     /* Create the raw buffer */
414     if (modsecurity_request_body_end_raw(msr, error_msg) != 1) {
415         return -1;
416     }
417
418     /* Parse URL-encoded arguments in the request body. */
419
420     if (parse_arguments(msr, msr->msc_reqbody_buffer, msr->msc_reqbody_length,
421         msr->txcfg->argument_separator, "BODY", msr->arguments, &invalid_count) < 0)
422     {
423         *error_msg = apr_pstrdup(msr->mp, "Initialisation: Error occurred while parsing BODY arguments.");
424         return -1;
425     }
426
427     return 1;
428 }
429
430 /**
431  * Stops receiving the request body.
432  */
433 apr_status_t modsecurity_request_body_end(modsec_rec *msr, char **error_msg) {
434     *error_msg = NULL;
435
436     /* Close open file descriptors, if any. */
437     if (msr->msc_reqbody_storage == MSC_REQBODY_DISK) {
438         if (msr->msc_reqbody_fd > 0) {
439             close(msr->msc_reqbody_fd);
440             msr->msc_reqbody_fd = -1;
441         }
442     }
443
444     /* Note that we've read the body. */
445     msr->msc_reqbody_read = 1;
446
447     /* Finalise body processing. */
448     if ((msr->msc_reqbody_processor != NULL)&&(msr->msc_reqbody_error == 0)) {
449         char *my_error_msg = NULL;
450
451         if (strcmp(msr->msc_reqbody_processor, "MULTIPART") == 0) {
452             if (multipart_complete(msr, &my_error_msg) < 0) {
453                 *error_msg = apr_psprintf(msr->mp, "Multipart parsing error: %s", my_error_msg);
454                 msr->msc_reqbody_error = 1;
455                 msr->msc_reqbody_error_msg = *error_msg;
456                 msr_log(msr, 2, "%s", *error_msg);
457                 return -1;
458             }
459
460             if (multipart_get_arguments(msr, "BODY", msr->arguments) < 0) {
461                 *error_msg = "Multipart parsing error: Failed to retrieve arguments.";
462                 msr->msc_reqbody_error = 1;
463                 msr->msc_reqbody_error_msg = *error_msg;
464                 msr_log(msr, 2, "%s", *error_msg);
465                 return -1;
466             }
467         }
468         else
469         if (strcmp(msr->msc_reqbody_processor, "URLENCODED") == 0) {
470             return modsecurity_request_body_end_urlencoded(msr, error_msg);
471         }
472         else
473         if (strcmp(msr->msc_reqbody_processor, "XML") == 0) {
474             if (xml_complete(msr, &my_error_msg) < 0) {
475                 *error_msg = apr_psprintf(msr->mp, "XML parser error: %s", my_error_msg);
476                 msr->msc_reqbody_error = 1;
477                 msr->msc_reqbody_error_msg = *error_msg;
478                 msr_log(msr, 2, "%s", *error_msg);
479                 return -1;
480             }
481         }
482     } else if (msr->txcfg->reqbody_buffering != REQUEST_BODY_FORCEBUF_OFF) {
483         /* Convert to a single continous buffer, but don't do anything else. */
484         return modsecurity_request_body_end_raw(msr, error_msg);
485     }
486
487     /* Note the request body no files length. */
488     msr_log(msr, 4, "Reqest body no files length: %" APR_SIZE_T_FMT, msr->msc_reqbody_no_files_length);
489
490     return 1;
491 }
492
493 /**
494  * Prepares to forward the request body.
495  */
496 apr_status_t modsecurity_request_body_retrieve_start(modsec_rec *msr, char **error_msg) {
497     *error_msg = NULL;
498
499     if (msr->msc_reqbody_storage == MSC_REQBODY_MEMORY) {
500         msr->msc_reqbody_chunk_position = 0;
501         msr->msc_reqbody_chunk_offset = 0;
502
503         msr->msc_reqbody_disk_chunk = apr_pcalloc(msr->msc_reqbody_mp, sizeof(msc_data_chunk));
504         if (msr->msc_reqbody_disk_chunk == NULL) {
505             *error_msg = apr_psprintf(msr->mp, "Failed to allocate %lu bytes for request body disk chunk.",
506                 (unsigned long)sizeof(msc_data_chunk));
507             return -1;
508         }
509         msr->msc_reqbody_disk_chunk->is_permanent = 1;
510     }
511     else
512     if (msr->msc_reqbody_storage == MSC_REQBODY_DISK) {
513         msr->msc_reqbody_disk_chunk = apr_pcalloc(msr->msc_reqbody_mp, sizeof(msc_data_chunk));
514         if (msr->msc_reqbody_disk_chunk == NULL) {
515             *error_msg = apr_psprintf(msr->mp, "Failed to allocate %lu bytes for request body disk chunk.",
516                 (unsigned long)sizeof(msc_data_chunk));
517             return -1;
518         }
519
520         msr->msc_reqbody_disk_chunk->is_permanent = 0;
521         msr->msc_reqbody_disk_chunk->data = apr_palloc(msr->msc_reqbody_mp, CHUNK_CAPACITY);
522         if (msr->msc_reqbody_disk_chunk->data == NULL) {
523             *error_msg = apr_psprintf(msr->mp, "Failed to allocate %d bytes for request body disk chunk data.",
524                 CHUNK_CAPACITY);
525             return -1;
526         }
527
528         msr->msc_reqbody_fd = open(msr->msc_reqbody_filename, O_RDONLY | O_BINARY);
529         if (msr->msc_reqbody_fd < 0) {
530             *error_msg = apr_psprintf(msr->mp, "Failed to open temporary file for reading: %s",
531                 msr->msc_reqbody_filename);
532             return -1;
533         }
534     }
535
536     return 1;
537 }
538
539 /**
540  *
541  */
542 apr_status_t modsecurity_request_body_retrieve_end(modsec_rec *msr) {
543     if (msr->msc_reqbody_storage == MSC_REQBODY_DISK) {
544         if (msr->msc_reqbody_fd > 0) {
545             close(msr->msc_reqbody_fd);
546             msr->msc_reqbody_fd = -1;
547         }
548     }
549
550     return 1;
551 }
552
553 /**
554  * Returns one chunk of request body data. It stores a NULL
555  * in the chunk pointer when there is no data to return. The
556  * return code is 1 if more calls can be made to retrieve more
557  * data, 0 if there is no more data to retrieve, or -1 on error.
558  *
559  * The caller can limit the amount of data returned by providing
560  * a non-negative value in nbytes.
561  */
562 apr_status_t modsecurity_request_body_retrieve(modsec_rec *msr,
563     msc_data_chunk **chunk, long int nbytes, char **error_msg)
564 {
565     msc_data_chunk **chunks;
566
567     *error_msg = NULL;
568
569     if (chunk == NULL) {
570         *error_msg = apr_pstrdup(msr->mp, "Internal error, retrieving request body chunk.");
571         return -1;
572     }
573     *chunk = NULL;
574
575     if (msr->msc_reqbody_storage == MSC_REQBODY_MEMORY) {
576         /* Are there any chunks left? */
577         if (msr->msc_reqbody_chunk_position >= msr->msc_reqbody_chunks->nelts) {
578             /* No more chunks. */
579             return 0;
580         }
581
582         /* We always respond with the same chunk, just different information in it. */
583         *chunk = msr->msc_reqbody_disk_chunk;
584
585         /* Advance to the current chunk and position on the
586          * next byte we need to send.
587          */
588         chunks = (msc_data_chunk **)msr->msc_reqbody_chunks->elts;
589         msr->msc_reqbody_disk_chunk->data = chunks[msr->msc_reqbody_chunk_position]->data
590             + msr->msc_reqbody_chunk_offset;
591
592         if (nbytes < 0) {
593             /* Send what's left in this chunk as there is no limit on the size. */
594             msr->msc_reqbody_disk_chunk->length = chunks[msr->msc_reqbody_chunk_position]->length;
595             msr->msc_reqbody_chunk_position++;
596             msr->msc_reqbody_chunk_offset = 0;
597         } else {
598             /* We have a limit we must obey. */
599
600             if (chunks[msr->msc_reqbody_chunk_position]->length -
601                 msr->msc_reqbody_chunk_offset <= (unsigned int)nbytes)
602             {
603                 /* If what's left in our chunk is less than the limit
604                  * then send it all back.
605                  */
606                 msr->msc_reqbody_disk_chunk->length =
607                     chunks[msr->msc_reqbody_chunk_position]->length -
608                     msr->msc_reqbody_chunk_offset;
609                 msr->msc_reqbody_chunk_position++;
610                 msr->msc_reqbody_chunk_offset = 0;
611             } else {
612                 /* If we have more data in our chunk, send the
613                  * maximum bytes we can (nbytes).
614                  */
615                 msr->msc_reqbody_disk_chunk->length = nbytes;
616                 msr->msc_reqbody_chunk_offset += nbytes;
617             }
618         }
619
620         /* If we've advanced beyond our last chunk then
621          * we have no more data to send.
622          */
623         if (msr->msc_reqbody_chunk_position >= msr->msc_reqbody_chunks->nelts) {
624             return 0; /* No more chunks. */
625         }
626
627         /* More data available. */
628         return 1;
629     }
630
631     if (msr->msc_reqbody_storage == MSC_REQBODY_DISK) {
632         long int my_nbytes = CHUNK_CAPACITY;
633         int i;
634
635         /* Send CHUNK_CAPACITY bytes at a time unless a lower limit was requested. */
636         if ((nbytes != -1)&&(my_nbytes > nbytes)) {
637             my_nbytes = nbytes;
638         }
639
640         i = read(msr->msc_reqbody_fd, msr->msc_reqbody_disk_chunk->data, my_nbytes);
641         if (i < 0) {
642             *error_msg = apr_psprintf(msr->mp, "Input filter: Error reading from temporary file: %s",
643                 strerror(errno));
644             return -1;
645         }
646
647         *chunk = msr->msc_reqbody_disk_chunk;
648         msr->msc_reqbody_disk_chunk->length = i;
649
650         if (i == 0) return 0; /* No more data available. */
651
652         return 1; /* More data available. */
653     }
654
655     /* Should never happen. */
656     *error_msg = apr_psprintf(msr->mp, "Internal error, invalid msc_reqbody_storage value: %u",
657         msr->msc_reqbody_storage);
658
659     return -1;
660 }
661
662 /**
663  *
664  */
665 apr_status_t modsecurity_request_body_clear(modsec_rec *msr, char **error_msg) {
666     *error_msg = NULL;
667
668     /* Release memory we used to store request body data. */
669     if (msr->msc_reqbody_chunks != NULL) {
670         msc_data_chunk **chunks = (msc_data_chunk **)msr->msc_reqbody_chunks->elts;
671         int i;
672
673         for(i = 0; i < msr->msc_reqbody_chunks->nelts; i++) {
674             if (chunks[i]->data != NULL) {
675                 free(chunks[i]->data);
676                 chunks[i]->data = NULL;
677             }
678         }
679     }
680
681     if (msr->msc_reqbody_storage == MSC_REQBODY_DISK) {
682         int keep_body = 0;
683
684         /* Should we keep the body? This normally
685          * happens when a PUT method was used, which
686          * means the body is actually a file.
687          */
688         if ((msr->upload_remove_files == 0)&&(strcasecmp(msr->request_method, "PUT") == 0)) {
689             if (msr->txcfg->upload_dir != NULL) {
690                 keep_body = 1;
691             } else {
692                 *error_msg = apr_psprintf(msr->mp, "Input filter: SecUploadDir is undefined, "
693                     "unable to store PUT file.");
694             }
695         }
696
697         /* Deal with a request body stored in a file. */
698
699         if (msr->msc_reqbody_filename != NULL) {
700             if (keep_body) {
701                 /* Move request body (which is a file) to the storage area. */
702                 const char *put_filename = NULL;
703                 const char *put_basename = NULL;
704
705                 /* Construct the new filename. */
706                 put_basename = file_basename(msr->msc_reqbody_mp, msr->msc_reqbody_filename);
707                 if (put_basename == NULL) {
708                     *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));
709                     return -1;
710                 }
711                 put_filename = apr_psprintf(msr->msc_reqbody_mp, "%s/%s",
712                     msr->txcfg->upload_dir, put_basename);
713                 if (put_filename == NULL) {
714                     *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));
715                     return -1;
716                 }
717
718                 if (apr_file_rename(msr->msc_reqbody_filename, put_filename,
719                     msr->msc_reqbody_mp) != APR_SUCCESS)
720                 {
721                     *error_msg = apr_psprintf(msr->mp, "Input filter: Failed to rename file from \"%s\" to \"%s\".",
722                         log_escape(msr->msc_reqbody_mp, msr->msc_reqbody_filename),
723                         log_escape(msr->msc_reqbody_mp, put_filename));
724                     return -1;
725                 } else {
726                     msr_log(msr, 4, "Input filter: Moved file from \"%s\" to \"%s\".",
727                         log_escape(msr->msc_reqbody_mp, msr->msc_reqbody_filename),
728                         log_escape(msr->msc_reqbody_mp, put_filename));
729                 }
730             } else {
731                 /* make sure it is closed first */
732                 if (msr->msc_reqbody_fd > 0) {
733                     close(msr->msc_reqbody_fd);
734                     msr->msc_reqbody_fd = -1;
735                 }
736
737                 /* We do not want to keep the request body. */
738                 if (apr_file_remove(msr->msc_reqbody_filename,
739                     msr->msc_reqbody_mp) != APR_SUCCESS)
740                 {
741                     *error_msg = apr_psprintf(msr->mp, "Input filter: Failed to delete temporary file: %s",
742                         log_escape(msr->mp, msr->msc_reqbody_filename));
743                     return -1;
744                 }
745
746                 msr_log(msr, 4, "Input filter: Removed temporary file: %s",
747                     msr->msc_reqbody_filename);
748             }
749
750             msr->msc_reqbody_filename = NULL;
751         }
752     }
753
754     if (msr->msc_reqbody_mp != NULL) {
755         apr_pool_destroy(msr->msc_reqbody_mp);
756         msr->msc_reqbody_mp = NULL;
757     }
758
759     return 1;
760 }