File: alp2_pp.c

package info (click to toggle)
modsecurity-apache 2.6.6-6%2Bdeb7u2
  • links: PTS
  • area: main
  • in suites: wheezy
  • size: 4,660 kB
  • sloc: ansic: 26,421; sh: 10,265; perl: 1,338; makefile: 240
file content (362 lines) | stat: -rwxr-xr-x 12,195 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
/*
* ModSecurity for Apache 2.x, http://www.modsecurity.org/
* Copyright (c) 2004-2011 Trustwave Holdings, Inc. (http://www.trustwave.com/)
*
* You may not use this file except in compliance with
* the License.  You may obtain a copy of the License at
*
*     http://www.apache.org/licenses/LICENSE-2.0
*
* If any of the files related to licensing are missing or if you have any
* other questions related to licensing please contact Trustwave Holdings, Inc.
* directly using the email address security@modsecurity.org.
*/

#include <ctype.h>
#include <stdio.h>
#include <sys/param.h>

#include "alp2_pp.h"

/**
 * Take the line in the buffer and replace the new line
 * at the end with a NUL byte.
 */
char *alp2_pp_line_chomp(alp2_pp_t *pp) {
    if (pp->line_pos == 0) {
        pp->line_buf[0] = '\0';
    }
    else {
        pp->line_buf[pp->line_pos - 1] = '\0';
    }

    return &(pp->line_buf[0]);
}

/**
 * Look into the line buffer to determine if it
 * contains a boundary line.
 */
static int alp2_pp_is_boundary_line(alp2_pp_t *alp_pp) {
    char *new_boundary = NULL;
    unsigned int id;
    size_t i;

    /* A boundary line cannot be less than 14 characters long. */
    if (alp_pp->line_pos < 15) {
        return 0;
    }

    /* The first two characters must both be dashes. */
    if ((alp_pp->line_buf[0] != '-')||(alp_pp->line_buf[1] != '-')) {
        return 0;
    }

    /* Extract the boundary. */
    i = 2; /* Start after the second dash. */
    while((isxdigit(alp_pp->line_buf[i]))&&(i < alp_pp->line_pos)) {
        i++;
    }    

    /* The boundary cannot be shorter than 8 characters. */
    if (i - 2 < 8) {
        return 0;
    }

    // TODO Memory leak; use a single parser buffer to avoid per-entry
    //      allocation from the parser pool.
    new_boundary = apr_pstrndup(alp_pp->mp, &(alp_pp->line_buf[2]), i - 2);

    /* Check if the rest of the line is valid. */
    if (  (i + 5 < alp_pp->line_pos) /* Need at lest 5 more bytes. */
        ||(alp_pp->line_buf[i + 0] != '-')
        ||(alp_pp->line_buf[i + 1] < 'A')
        ||(alp_pp->line_buf[i + 1] > 'Z')
        ||(alp_pp->line_buf[i + 2] != '-')
        ||(alp_pp->line_buf[i + 3] != '-')
        ||(alp_pp->line_buf[i + 4] != '\n') )
    {
        return 0;
    }

    id = alp_pp->line_buf[i + 1];

    /* Are we in a middle of an entry right now? */
    if (alp_pp->current_entry == NULL) {
        /* We will accept a new boundary. */
        alp_pp->boundary = new_boundary;

        return id;
    }
    else {
        /* The boundary must match the boundary of
         * the entry we are currently working on.
         */
        if (strcmp(alp_pp->current_entry->boundary, new_boundary) != 0) {
            return 0;
        }
        else {
            return id;
        }
    }

    return 0;
}

/**
 * Process data belonging to a single part.
 */
static void alp2_pp_process_part_data(alp2_pp_t *alp_pp) {
    if (alp_pp->current_part == NULL) {
        return;
    }
    
    /* Invoke part processor. */
    if (alp_pp->callback != NULL) {
        if (alp_pp->callback(alp_pp, ALP2_EVENT_PART_DATA) == 0) {
            alp_pp->done = 1;
        }
    }
                
    /* Keep track of part size. */
    alp_pp->current_part->size += alp_pp->line_pos;

    /* Update the MD5 hash calculation. */
    if ((alp_pp->current_entry != NULL)&&(alp_pp->line_pos > 0)) {
        apr_md5_update(alp_pp->current_entry->md5_context, &alp_pp->line_buf[0], alp_pp->line_pos - 1);
    }
}

/**
 * Initialise parser.
 */
int alp2_pp_init(alp2_pp_t *alp_pp, void *user_data,
    int (*callback)(alp2_pp_t *alp_pp, int event_type), apr_pool_t *mp)
{
    memset(alp_pp, 0, sizeof(alp2_pp_t));

    alp_pp->user_data = user_data;
    alp_pp->callback = callback;
    alp_pp->mp = mp; /* Use the parent pool directly. */

    /* Set-up the line buffer. */
    alp_pp->line_buf = apr_pcalloc(mp, ALP2_MAX_LINE_SIZE);
    alp_pp->line_size = ALP2_MAX_LINE_SIZE;
    alp_pp->line_has_start = 1;
    alp_pp->line_offset = 0;
    
    return 1;
}

/**
 * Process data the parser has stored in the input buffer.
 */
static apr_status_t alp2_pp_process_internal(alp2_pp_t *alp_pp) {
    /* Do not proceed if we've previously 
     * encountered a fatal error.
     */
    if (alp_pp->errored != 0) {
        return ALP2_ERROR_FATAL;
    }

    if (alp_pp->done) {
        return ALP2_DONE;
    }

    /* Go back straight away if we don't have anything to work with. */
    if (alp_pp->input_len == 0) {
        return ALP2_NEED_DATA;
    }

    while (alp_pp->input_pos < alp_pp->input_len) {
        int c;

        if (alp_pp->done) {
            return ALP2_DONE;
        }

        if (alp_pp->line_pos >= alp_pp->line_size) {
            /* Our line buffer is full with the
             * line incomplete.
             */
            alp2_pp_process_part_data(alp_pp);

            /* Reset line buffer . */
            alp_pp->line_pos = 0;
            alp_pp->line_has_start = 0;
            alp_pp->line_offset = alp_pp->current_offset;
        }

        /* Consume one byte. */
        c = alp_pp->input_buf[alp_pp->input_pos];
        alp_pp->input_pos++;
        alp_pp->current_offset++;
        
        /* Copy the byte to the line buffer. */
        alp_pp->line_buf[alp_pp->line_pos] = c;
        alp_pp->line_pos++;
       
        /* Are we at the end of a line? */       
        if (c == '\n') {
            if (alp_pp->line_has_start) {
                /* We have one complete line. */
                
                int id = alp2_pp_is_boundary_line(alp_pp);

                if (id != 0) {
                    /* The line is a boundary. */
                    
                    /* Finish with the previous part, if any. */
                    if (alp_pp->current_part != NULL) {
                        /* Update the MD5 context. */
                        apr_md5_update(alp_pp->current_entry->md5_context,
                            &alp_pp->line_buf[0], alp_pp->line_pos - 1);
                        
                        /* Event PART_END. */
                        if (alp_pp->callback != NULL) {
                            if (alp_pp->callback(alp_pp, ALP2_EVENT_PART_END) == 0) {
                                alp_pp->done = 1;
                            }
                        }
                        
                        /* Add part to the current entry. */
                        *(alp2_pp_part_t **)apr_array_push(alp_pp->current_entry->parts)
                            = alp_pp->current_part;

                        /* Delete part. */
                        alp_pp->current_part = NULL;
                        
                        /* If the new part is part Z, then finish
                         * with the current entry. */
                        if (id == 'Z') {
                            alp_pp->current_entry->size = alp_pp->current_offset - alp_pp->current_entry->offset;

                            /* Create the MD5 digest. */
                            apr_md5_final(alp_pp->current_entry->md5_digest,
                                alp_pp->current_entry->md5_context);

                            /* Event ENTRY_END. */
                            if (alp_pp->callback != NULL) {
                                if (alp_pp->callback(alp_pp, ALP2_EVENT_ENTRY_END) == 0) {
                                    alp_pp->done = 1;
                                }
                            }

                            /* We are about to destroy our only reference to the per-entry
                             * memory pool, but that is all right since we've passed all
                             * responsibility for the entry to the higher-level handler.
                             */
                            alp_pp->current_entry = NULL;
                        }
                    }
                    
                    if (id != 'Z') {
                        /* Create new entry if necessary. */
                        if (alp_pp->current_entry == NULL) {
                            apr_pool_t *new_pool = NULL;

                            /* Create a per-entry pool directly from the main memory pool. */
                            apr_pool_create(&new_pool, apr_pool_parent_get(alp_pp->mp));

                            alp_pp->current_entry = apr_pcalloc(new_pool, sizeof(alp2_pp_entry_t));
                            alp_pp->current_entry->mp = new_pool;
                            alp_pp->current_entry->offset = alp_pp->line_offset;
                            alp_pp->current_entry->boundary = apr_pstrdup(new_pool, alp_pp->boundary);
                            alp_pp->boundary = NULL;

                            alp_pp->current_entry->parts = apr_array_make(alp_pp->current_entry->mp,
                                16, sizeof(alp2_pp_part_t *));

                            /* Initialise the MD5 context. */
                            alp_pp->current_entry->md5_context = apr_pcalloc(alp_pp->current_entry->mp,
                                sizeof(apr_md5_ctx_t));
                            apr_md5_init(alp_pp->current_entry->md5_context);

                            /* Start calculating the has with the first line. */
                            apr_md5_update(alp_pp->current_entry->md5_context, &alp_pp->line_buf[0], alp_pp->line_pos - 1);

                            /* Event ENTRY_START. */
                            if (alp_pp->callback != NULL) {
                                if (alp_pp->callback(alp_pp, ALP2_EVENT_ENTRY_START) == 0) {
                                    alp_pp->done = 1;
                                }
                            }
                        }
                    
                        /* Create new part, but only if we are not
                         * dealing with an entry terminator.
                         */
                        alp_pp->current_part = apr_pcalloc(alp_pp->current_entry->mp, sizeof(alp2_pp_part_t));
                        alp_pp->current_part->id = id;
                        alp_pp->current_part->offset = alp_pp->current_offset;

                        /* Event PART_START. */
                        if (alp_pp->callback != NULL) {
                            if (alp_pp->callback(alp_pp, ALP2_EVENT_PART_START) == 0) {
                                alp_pp->done = 1;
                            }
                        }
                    }
                }
                else {
                    /* The line does not contain a boundary,
                     * so process it as part data.
                     */
                    alp2_pp_process_part_data(alp_pp);
                }
            }
            else {
                /* We have a chunk of data that is not a line, which
                 * probably means that our buffer was not big enough, either
                 * because the line (is a line and it) was too big, or because
                 * we are processing binary data. Ideally the latter.
                 */
                alp2_pp_process_part_data(alp_pp);
            }
            
            /* Reset the line buffer. */
            alp_pp->line_pos = 0;
            alp_pp->line_has_start = 1;
            alp_pp->line_offset = alp_pp->current_offset;
        }
    }

    if (alp_pp->done) {
        return ALP2_DONE;
    }
    else {
        return ALP2_NEED_DATA;
    }
}

/**
 * Process the provided data.
 */
int alp2_pp_process(alp2_pp_t *alp_pp, const char *data, size_t len) {
    /* Do not proceed if we've previously 
     * encountered a fatal error.
     */
    if (alp_pp->errored != 0) {
        return ALP2_ERROR_FATAL;
    }
    
    /* Check that we've used up the existing buffer. */
    if (alp_pp->input_pos < alp_pp->input_len) {
        return ALP2_ERROR_INCORRECT_STATE;
    }
    
    alp_pp->input_buf = data;
    alp_pp->input_len = len;
    alp_pp->input_pos = 0;
    
    return alp2_pp_process_internal(alp_pp);
}

/**
 * Clean-up the parser structures.
 */
void alp2_pp_terminate(alp2_pp_t *alp_pp) {
    /* Nothing to do, but we may need
     * to do something in the future.
     */
}