~ubuntu-branches/ubuntu/oneiric/libapache-mod-security/oneiric-updates

« back to all changes in this revision

Viewing changes to apache2/msc_reqbody.c

  • Committer: Bazaar Package Importer
  • Author(s): Alberto Gonzalez Iniesta
  • Date: 2008-08-08 13:31:56 UTC
  • mfrom: (1.1.2 upstream)
  • Revision ID: james.westby@ubuntu.com-20080808133156-6jqb24apsw787v33
Tags: 2.5.6-1
* The 'Back to the archive!' Release (Closes: #487431)
* Drop '2' from package name, now libapache-mod-security
* New upstream release
  - Includes a new licensing exception that allows binary 
    distribution with licenses not compatible with GPLv2,
    such as Apache's. See MODSECURITY_LICENSING_EXCEPTION
* Removed debian/bug and debian/rules entry to install bug
  handling when out of the archive.
* Bumped Standards-Version to 3.8.0.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * ModSecurity for Apache 2.x, http://www.modsecurity.org/
 
3
 * Copyright (c) 2004-2008 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
    }
 
313
 
 
314
    /* Check that we are not over the request body no files limit. */
 
315
    if (msr->msc_reqbody_no_files_length >= (unsigned long) msr->txcfg->reqbody_no_files_limit) {
 
316
        return -5;
 
317
    }
 
318
 
 
319
    /* Store data. */
 
320
    if (msr->msc_reqbody_storage == MSC_REQBODY_MEMORY) {
 
321
        return modsecurity_request_body_store_memory(msr, data, length, error_msg);
 
322
    }
 
323
    else
 
324
    if (msr->msc_reqbody_storage == MSC_REQBODY_DISK) {
 
325
        return modsecurity_request_body_store_disk(msr, data, length, error_msg);
 
326
    }
 
327
 
 
328
    /* Should never happen. */
 
329
    *error_msg = apr_psprintf(msr->mp, "Internal error, unknown value for msc_reqbody_storage: %u",
 
330
        msr->msc_reqbody_storage);
 
331
    return -1;
 
332
}
 
333
 
 
334
/**
 
335
 *
 
336
 */
 
337
static apr_status_t modsecurity_request_body_end_urlencoded(modsec_rec *msr, char **error_msg) {
 
338
    msc_data_chunk **chunks, *one_chunk;
 
339
    char *d;
 
340
    int i, sofar;
 
341
    int invalid_count = 0;
 
342
 
 
343
    *error_msg = NULL;
 
344
 
 
345
    /* Allocate a buffer large enough to hold the request body. */
 
346
 
 
347
    if (msr->msc_reqbody_length + 1 == 0) {
 
348
        *error_msg = apr_psprintf(msr->mp, "Internal error, request body length will overflow: %u",
 
349
            msr->msc_reqbody_length);
 
350
        return -1;
 
351
    }
 
352
    msr->msc_reqbody_buffer = malloc(msr->msc_reqbody_length + 1);
 
353
    if (msr->msc_reqbody_buffer == NULL) {
 
354
        *error_msg = apr_psprintf(msr->mp, "Unable to allocate memory to hold request body. Asked for %u bytes.",
 
355
            msr->msc_reqbody_length + 1);
 
356
        return -1;
 
357
    }
 
358
    msr->msc_reqbody_buffer[msr->msc_reqbody_length] = '\0';
 
359
 
 
360
    /* Copy the data we keep in chunks into the new buffer. */
 
361
 
 
362
    sofar = 0;
 
363
    d = msr->msc_reqbody_buffer;
 
364
    chunks = (msc_data_chunk **)msr->msc_reqbody_chunks->elts;
 
365
    for(i = 0; i < msr->msc_reqbody_chunks->nelts; i++) {
 
366
        if (sofar + chunks[i]->length <= msr->msc_reqbody_length) {
 
367
            memcpy(d, chunks[i]->data, chunks[i]->length);
 
368
            d += chunks[i]->length;
 
369
            sofar += chunks[i]->length;
 
370
        } else {
 
371
            *error_msg = apr_psprintf(msr->mp, "Internal error, request body buffer overflow.");
 
372
            return -1;
 
373
        }
 
374
    }
 
375
 
 
376
    /* Now free the memory used by the chunks. */
 
377
 
 
378
    chunks = (msc_data_chunk **)msr->msc_reqbody_chunks->elts;
 
379
    for(i = 0; i < msr->msc_reqbody_chunks->nelts; i++) {
 
380
        free(chunks[i]->data);
 
381
        chunks[i]->data = NULL;
 
382
    }
 
383
 
 
384
    /* Create a new array with only one chunk in it. */
 
385
 
 
386
    msr->msc_reqbody_chunks = apr_array_make(msr->msc_reqbody_mp, 2, sizeof(msc_data_chunk *));
 
387
    if (msr->msc_reqbody_chunks == NULL) {
 
388
        *error_msg = apr_pstrdup(msr->mp, "Failed to create structure to hold request body.");
 
389
        return -1;
 
390
    }
 
391
    one_chunk = (msc_data_chunk *)apr_pcalloc(msr->msc_reqbody_mp, sizeof(msc_data_chunk));
 
392
    one_chunk->data = msr->msc_reqbody_buffer;
 
393
    one_chunk->length = msr->msc_reqbody_length;
 
394
    one_chunk->is_permanent = 1;
 
395
    *(const msc_data_chunk **)apr_array_push(msr->msc_reqbody_chunks) = one_chunk;
 
396
 
 
397
    /* Parse URL-encoded arguments in the request body. */
 
398
 
 
399
    if (parse_arguments(msr, msr->msc_reqbody_buffer, msr->msc_reqbody_length,
 
400
        msr->txcfg->argument_separator, "BODY", msr->arguments, &invalid_count) < 0)
 
401
    {
 
402
        *error_msg = apr_pstrdup(msr->mp, "Initialisation: Error occurred while parsing BODY arguments.");
 
403
        return -1;
 
404
    }
 
405
 
 
406
    return 1;
 
407
}
 
408
 
 
409
/**
 
410
 * Stops receiving the request body.
 
411
 */
 
412
apr_status_t modsecurity_request_body_end(modsec_rec *msr, char **error_msg) {
 
413
    *error_msg = NULL;
 
414
 
 
415
    /* Close open file descriptors, if any. */
 
416
    if (msr->msc_reqbody_storage == MSC_REQBODY_DISK) {
 
417
        if (msr->msc_reqbody_fd > 0) {
 
418
            close(msr->msc_reqbody_fd);
 
419
            msr->msc_reqbody_fd = -1;
 
420
        }
 
421
    }
 
422
 
 
423
    /* Note that we've read the body. */
 
424
    msr->msc_reqbody_read = 1;
 
425
 
 
426
    /* Finalise body processing. */
 
427
    if ((msr->msc_reqbody_processor != NULL)&&(msr->msc_reqbody_error == 0)) {
 
428
        char *my_error_msg = NULL;
 
429
 
 
430
        if (strcmp(msr->msc_reqbody_processor, "MULTIPART") == 0) {
 
431
            if (multipart_complete(msr, &my_error_msg) < 0) {
 
432
                *error_msg = apr_psprintf(msr->mp, "Multipart parsing error: %s", my_error_msg);
 
433
                msr->msc_reqbody_error = 1;
 
434
                msr->msc_reqbody_error_msg = *error_msg;
 
435
                msr_log(msr, 2, "%s", *error_msg);
 
436
                return -1;
 
437
            }
 
438
 
 
439
            if (multipart_get_arguments(msr, "BODY", msr->arguments) < 0) {
 
440
                *error_msg = "Multipart parsing error: Failed to retrieve arguments.";
 
441
                msr->msc_reqbody_error = 1;
 
442
                msr->msc_reqbody_error_msg = *error_msg;
 
443
                msr_log(msr, 2, "%s", *error_msg);
 
444
                return -1;
 
445
            }
 
446
        }
 
447
        else
 
448
        if (strcmp(msr->msc_reqbody_processor, "URLENCODED") == 0) {
 
449
            return modsecurity_request_body_end_urlencoded(msr, error_msg);
 
450
        }
 
451
        else
 
452
        if (strcmp(msr->msc_reqbody_processor, "XML") == 0) {
 
453
            if (xml_complete(msr, &my_error_msg) < 0) {
 
454
                *error_msg = apr_psprintf(msr->mp, "XML parser error: %s", my_error_msg);
 
455
                msr->msc_reqbody_error = 1;
 
456
                msr->msc_reqbody_error_msg = *error_msg;
 
457
                msr_log(msr, 2, "%s", *error_msg);
 
458
                return -1;
 
459
            }
 
460
        }
 
461
    }
 
462
 
 
463
    /* Note the request body no files length. */
 
464
    msr_log(msr, 4, "Reqest body no files length: %" APR_SIZE_T_FMT, msr->msc_reqbody_no_files_length);
 
465
 
 
466
    return 1;
 
467
}
 
468
 
 
469
/**
 
470
 * Prepares to forward the request body.
 
471
 */
 
472
apr_status_t modsecurity_request_body_retrieve_start(modsec_rec *msr, char **error_msg) {
 
473
    *error_msg = NULL;
 
474
 
 
475
    if (msr->msc_reqbody_storage == MSC_REQBODY_MEMORY) {
 
476
        msr->msc_reqbody_chunk_position = 0;
 
477
        msr->msc_reqbody_chunk_offset = 0;
 
478
 
 
479
        msr->msc_reqbody_disk_chunk = apr_pcalloc(msr->msc_reqbody_mp, sizeof(msc_data_chunk));
 
480
        if (msr->msc_reqbody_disk_chunk == NULL) {
 
481
            *error_msg = apr_psprintf(msr->mp, "Failed to allocate %lu bytes for request body disk chunk.",
 
482
                (unsigned long)sizeof(msc_data_chunk));
 
483
            return -1;
 
484
        }
 
485
        msr->msc_reqbody_disk_chunk->is_permanent = 1;
 
486
    }
 
487
    else
 
488
    if (msr->msc_reqbody_storage == MSC_REQBODY_DISK) {
 
489
        msr->msc_reqbody_disk_chunk = apr_pcalloc(msr->msc_reqbody_mp, sizeof(msc_data_chunk));
 
490
        if (msr->msc_reqbody_disk_chunk == NULL) {
 
491
            *error_msg = apr_psprintf(msr->mp, "Failed to allocate %lu bytes for request body disk chunk.",
 
492
                (unsigned long)sizeof(msc_data_chunk));
 
493
            return -1;
 
494
        }
 
495
 
 
496
        msr->msc_reqbody_disk_chunk->is_permanent = 0;
 
497
        msr->msc_reqbody_disk_chunk->data = apr_palloc(msr->msc_reqbody_mp, CHUNK_CAPACITY);
 
498
        if (msr->msc_reqbody_disk_chunk->data == NULL) {
 
499
            *error_msg = apr_psprintf(msr->mp, "Failed to allocate %d bytes for request body disk chunk data.",
 
500
                CHUNK_CAPACITY);
 
501
            return -1;
 
502
        }
 
503
 
 
504
        msr->msc_reqbody_fd = open(msr->msc_reqbody_filename, O_RDONLY | O_BINARY);
 
505
        if (msr->msc_reqbody_fd < 0) {
 
506
            *error_msg = apr_psprintf(msr->mp, "Failed to open temporary file for reading: %s",
 
507
                msr->msc_reqbody_filename);
 
508
            return -1;
 
509
        }
 
510
    }
 
511
 
 
512
    return 1;
 
513
}
 
514
 
 
515
/**
 
516
 *
 
517
 */
 
518
apr_status_t modsecurity_request_body_retrieve_end(modsec_rec *msr) {
 
519
    if (msr->msc_reqbody_storage == MSC_REQBODY_DISK) {
 
520
        if (msr->msc_reqbody_fd > 0) {
 
521
            close(msr->msc_reqbody_fd);
 
522
            msr->msc_reqbody_fd = -1;
 
523
        }
 
524
    }
 
525
 
 
526
    return 1;
 
527
}
 
528
 
 
529
/**
 
530
 * Returns one chunk of request body data. It stores a NULL
 
531
 * in the chunk pointer when there is no data to return. The
 
532
 * return code is 1 if more calls can be made to retrieve more
 
533
 * data, 0 if there is no more data to retrieve, or -1 on error.
 
534
 *
 
535
 * The caller can limit the amount of data returned by providing
 
536
 * a non-negative value in nbytes.
 
537
 */
 
538
apr_status_t modsecurity_request_body_retrieve(modsec_rec *msr,
 
539
    msc_data_chunk **chunk, long int nbytes, char **error_msg)
 
540
{
 
541
    msc_data_chunk **chunks;
 
542
 
 
543
    *error_msg = NULL;
 
544
 
 
545
    if (chunk == NULL) {
 
546
        *error_msg = apr_pstrdup(msr->mp, "Internal error, retrieving request body chunk.");
 
547
        return -1;
 
548
    }
 
549
    *chunk = NULL;
 
550
 
 
551
    if (msr->msc_reqbody_storage == MSC_REQBODY_MEMORY) {
 
552
        /* Are there any chunks left? */
 
553
        if (msr->msc_reqbody_chunk_position >= msr->msc_reqbody_chunks->nelts) {
 
554
            /* No more chunks. */
 
555
            return 0;
 
556
        }
 
557
 
 
558
        /* We always respond with the same chunk, just different information in it. */
 
559
        *chunk = msr->msc_reqbody_disk_chunk;
 
560
 
 
561
        /* Advance to the current chunk and position on the
 
562
         * next byte we need to send.
 
563
         */
 
564
        chunks = (msc_data_chunk **)msr->msc_reqbody_chunks->elts;
 
565
        msr->msc_reqbody_disk_chunk->data = chunks[msr->msc_reqbody_chunk_position]->data
 
566
            + msr->msc_reqbody_chunk_offset;
 
567
 
 
568
        if (nbytes < 0) {
 
569
            /* Send what's left in this chunk as there is no limit on the size. */
 
570
            msr->msc_reqbody_disk_chunk->length = chunks[msr->msc_reqbody_chunk_position]->length;
 
571
            msr->msc_reqbody_chunk_position++;
 
572
            msr->msc_reqbody_chunk_offset = 0;
 
573
        } else {
 
574
            /* We have a limit we must obey. */
 
575
 
 
576
            if (chunks[msr->msc_reqbody_chunk_position]->length -
 
577
                msr->msc_reqbody_chunk_offset <= (unsigned int)nbytes)
 
578
            {
 
579
                /* If what's left in our chunk is less than the limit
 
580
                 * then send it all back.
 
581
                 */
 
582
                msr->msc_reqbody_disk_chunk->length =
 
583
                    chunks[msr->msc_reqbody_chunk_position]->length -
 
584
                    msr->msc_reqbody_chunk_offset;
 
585
                msr->msc_reqbody_chunk_position++;
 
586
                msr->msc_reqbody_chunk_offset = 0;
 
587
            } else {
 
588
                /* If we have more data in our chunk, send the
 
589
                 * maximum bytes we can (nbytes).
 
590
                 */
 
591
                msr->msc_reqbody_disk_chunk->length = nbytes;
 
592
                msr->msc_reqbody_chunk_offset += nbytes;
 
593
            }
 
594
        }
 
595
 
 
596
        /* If we've advanced beyond our last chunk then
 
597
         * we have no more data to send.
 
598
         */
 
599
        if (msr->msc_reqbody_chunk_position >= msr->msc_reqbody_chunks->nelts) {
 
600
            return 0; /* No more chunks. */
 
601
        }
 
602
 
 
603
        /* More data available. */
 
604
        return 1;
 
605
    }
 
606
 
 
607
    if (msr->msc_reqbody_storage == MSC_REQBODY_DISK) {
 
608
        long int my_nbytes = CHUNK_CAPACITY;
 
609
        int i;
 
610
 
 
611
        /* Send CHUNK_CAPACITY bytes at a time unless a lower limit was requested. */
 
612
        if ((nbytes != -1)&&(my_nbytes > nbytes)) {
 
613
            my_nbytes = nbytes;
 
614
        }
 
615
 
 
616
        i = read(msr->msc_reqbody_fd, msr->msc_reqbody_disk_chunk->data, my_nbytes);
 
617
        if (i < 0) {
 
618
            *error_msg = apr_psprintf(msr->mp, "Input filter: Error reading from temporary file: %s",
 
619
                strerror(errno));
 
620
            return -1;
 
621
        }
 
622
 
 
623
        *chunk = msr->msc_reqbody_disk_chunk;
 
624
        msr->msc_reqbody_disk_chunk->length = i;
 
625
 
 
626
        if (i == 0) return 0; /* No more data available. */
 
627
 
 
628
        return 1; /* More data available. */
 
629
    }
 
630
 
 
631
    /* Should never happen. */
 
632
    *error_msg = apr_psprintf(msr->mp, "Internal error, invalid msc_reqbody_storage value: %u",
 
633
        msr->msc_reqbody_storage);
 
634
 
 
635
    return -1;
 
636
}
 
637
 
 
638
/**
 
639
 *
 
640
 */
 
641
apr_status_t modsecurity_request_body_clear(modsec_rec *msr, char **error_msg) {
 
642
    *error_msg = NULL;
 
643
 
 
644
    /* Release memory we used to store request body data. */
 
645
    if (msr->msc_reqbody_chunks != NULL) {
 
646
        msc_data_chunk **chunks = (msc_data_chunk **)msr->msc_reqbody_chunks->elts;
 
647
        int i;
 
648
 
 
649
        for(i = 0; i < msr->msc_reqbody_chunks->nelts; i++) {
 
650
            if (chunks[i]->data != NULL) {
 
651
                free(chunks[i]->data);
 
652
                chunks[i]->data = NULL;
 
653
            }
 
654
        }
 
655
    }
 
656
 
 
657
    if (msr->msc_reqbody_storage == MSC_REQBODY_DISK) {
 
658
        int keep_body = 0;
 
659
 
 
660
        /* Should we keep the body? This normally
 
661
         * happens when a PUT method was used, which
 
662
         * means the body is actually a file.
 
663
         */
 
664
        if ((msr->upload_remove_files == 0)&&(strcasecmp(msr->request_method, "PUT") == 0)) {
 
665
            if (msr->txcfg->upload_dir != NULL) {
 
666
                keep_body = 1;
 
667
            } else {
 
668
                *error_msg = apr_psprintf(msr->mp, "Input filter: SecUploadDir is undefined, "
 
669
                    "unable to store PUT file.");
 
670
            }
 
671
        }
 
672
 
 
673
        /* Deal with a request body stored in a file. */
 
674
 
 
675
        if (msr->msc_reqbody_filename != NULL) {
 
676
            if (keep_body) {
 
677
                /* Move request body (which is a file) to the storage area. */
 
678
                const char *put_filename = NULL;
 
679
                const char *put_basename = NULL;
 
680
 
 
681
                /* Construct the new filename. */
 
682
                put_basename = file_basename(msr->msc_reqbody_mp, msr->msc_reqbody_filename);
 
683
                if (put_basename == NULL) {
 
684
                    *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));
 
685
                    return -1;
 
686
                }
 
687
                put_filename = apr_psprintf(msr->msc_reqbody_mp, "%s/%s",
 
688
                    msr->txcfg->upload_dir, put_basename);
 
689
                if (put_filename == NULL) {
 
690
                    *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));
 
691
                    return -1;
 
692
                }
 
693
 
 
694
                if (apr_file_rename(msr->msc_reqbody_filename, put_filename,
 
695
                    msr->msc_reqbody_mp) != APR_SUCCESS)
 
696
                {
 
697
                    *error_msg = apr_psprintf(msr->mp, "Input filter: Failed to rename file from \"%s\" to \"%s\".",
 
698
                        log_escape(msr->msc_reqbody_mp, msr->msc_reqbody_filename),
 
699
                        log_escape(msr->msc_reqbody_mp, put_filename));
 
700
                    return -1;
 
701
                } else {
 
702
                    msr_log(msr, 4, "Input filter: Moved file from \"%s\" to \"%s\".",
 
703
                        log_escape(msr->msc_reqbody_mp, msr->msc_reqbody_filename),
 
704
                        log_escape(msr->msc_reqbody_mp, put_filename));
 
705
                }
 
706
            } else {
 
707
                /* make sure it is closed first */
 
708
                if (msr->msc_reqbody_fd > 0) {
 
709
                    close(msr->msc_reqbody_fd);
 
710
                    msr->msc_reqbody_fd = -1;
 
711
                }
 
712
 
 
713
                /* We do not want to keep the request body. */
 
714
                if (apr_file_remove(msr->msc_reqbody_filename,
 
715
                    msr->msc_reqbody_mp) != APR_SUCCESS)
 
716
                {
 
717
                    *error_msg = apr_psprintf(msr->mp, "Input filter: Failed to delete temporary file: %s",
 
718
                        log_escape(msr->mp, msr->msc_reqbody_filename));
 
719
                    return -1;
 
720
                }
 
721
 
 
722
                msr_log(msr, 4, "Input filter: Removed temporary file: %s",
 
723
                    msr->msc_reqbody_filename);
 
724
            }
 
725
 
 
726
            msr->msc_reqbody_filename = NULL;
 
727
        }
 
728
    }
 
729
 
 
730
    if (msr->msc_reqbody_mp != NULL) {
 
731
        apr_pool_destroy(msr->msc_reqbody_mp);
 
732
        msr->msc_reqbody_mp = NULL;
 
733
    }
 
734
 
 
735
    return 1;
 
736
}