Coverage Report

Created: 2023-03-26 06:28

/src/httpd/server/apreq_parser_header.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
**  Licensed to the Apache Software Foundation (ASF) under one or more
3
** contributor license agreements.  See the NOTICE file distributed with
4
** this work for additional information regarding copyright ownership.
5
** The ASF licenses this file to You under the Apache License, Version 2.0
6
** (the "License"); you may not use this file except in compliance with
7
** the License.  You may obtain a copy of the License at
8
**
9
**      http://www.apache.org/licenses/LICENSE-2.0
10
**
11
**  Unless required by applicable law or agreed to in writing, software
12
**  distributed under the License is distributed on an "AS IS" BASIS,
13
**  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
**  See the License for the specific language governing permissions and
15
**  limitations under the License.
16
*/
17
#include <assert.h>
18
#include "apreq_parser.h"
19
#include "apreq_error.h"
20
#include "apreq_util.h"
21
22
#include "apr_lib.h" /* for apr_iscntrl() & co */
23
24
918
#define PARSER_STATUS_CHECK(PREFIX)   do {         \
25
918
    if (ctx->status == PREFIX##_ERROR)             \
26
918
        return APREQ_ERROR_GENERAL;                \
27
918
    else if (ctx->status == PREFIX##_COMPLETE)     \
28
918
        return APR_SUCCESS;                        \
29
918
    else if (bb == NULL)                           \
30
918
        return APR_INCOMPLETE;                     \
31
918
} while (0);
32
33
34
struct hdr_ctx {
35
    apr_bucket_brigade *bb;
36
    apr_size_t          nlen;
37
    apr_size_t          glen;
38
    apr_size_t          vlen;
39
    enum {
40
        HDR_NAME,
41
        HDR_GAP,
42
        HDR_VALUE,
43
        HDR_NEWLINE,
44
        HDR_ENDLINE,
45
        HDR_FOLDLINE,
46
        HDR_LASTLINE,
47
        HDR_COMPLETE,
48
        HDR_ERROR
49
    }                   status;
50
};
51
52
/********************* header parsing utils ********************/
53
54
55
static apr_bucket *split_header_line(apr_bucket *e, apr_size_t *off,
56
                                     const char **data, apr_size_t *dlen)
57
4.15k
{
58
4.15k
    if (*off > 1) {
59
4.14k
        apr_bucket_split(e, *off - 1);
60
4.14k
        e = APR_BUCKET_NEXT(e);
61
4.14k
        *dlen -= *off - 1;
62
4.14k
        *data += *off - 1;
63
4.14k
        *off = 1;
64
4.14k
    }
65
4.15k
    return e;
66
4.15k
}
67
68
static apr_status_t consume_header_line(apreq_param_t **p,
69
                                        apr_pool_t *pool,
70
                                        apr_bucket_brigade *bb,
71
                                        apr_size_t nlen,
72
                                        apr_size_t glen,
73
                                        apr_size_t vlen)
74
1.35k
{
75
1.35k
    apreq_param_t *param;
76
1.35k
    apreq_value_t *v;
77
1.35k
    apr_bucket *e, *f;
78
1.35k
    apr_status_t s;
79
1.35k
    struct iovec vec[APREQ_DEFAULT_NELTS], *iov;
80
1.35k
    apr_array_header_t arr;
81
1.35k
    char *dest;
82
1.35k
    const char *data;
83
1.35k
    apr_size_t dlen;
84
1.35k
    int i, eol = 0;
85
86
1.35k
    param = apreq_param_make(pool, NULL, nlen, NULL, vlen);
87
1.35k
    *(const apreq_value_t **)&v = &param->v;
88
89
1.35k
    arr.pool     = pool;
90
1.35k
    arr.elt_size = sizeof(struct iovec);
91
1.35k
    arr.nelts    = 0;
92
1.35k
    arr.nalloc   = APREQ_DEFAULT_NELTS;
93
1.35k
    arr.elts     = (char *)vec;
94
95
1.35k
    e = APR_BRIGADE_FIRST(bb);
96
97
    /* store name in a temporary iovec array */
98
1.35k
    do {
99
1.35k
        apr_size_t len;
100
101
1.35k
        assert(e != APR_BRIGADE_SENTINEL(bb));
102
1.35k
        iov = (struct iovec *)apr_array_push(&arr);
103
1.35k
        s = apr_bucket_read(e, (const char **)&iov->iov_base,
104
1.35k
                            &len, APR_BLOCK_READ);
105
1.35k
        if (s != APR_SUCCESS)
106
0
            return s;
107
1.35k
        iov->iov_len = len;
108
109
1.35k
        assert(nlen >= len);
110
1.35k
        nlen -= len;
111
112
1.35k
        e = APR_BUCKET_NEXT(e);
113
1.35k
    } while (nlen > 0);
114
115
    /* skip gap */
116
1.35k
    do {
117
1.35k
        assert(e != APR_BRIGADE_SENTINEL(bb));
118
1.35k
        s = apr_bucket_read(e, &data, &dlen, APR_BLOCK_READ);
119
1.35k
        if (s != APR_SUCCESS)
120
0
            return s;
121
122
1.35k
        assert(glen >= dlen);
123
1.35k
        glen -= dlen;
124
125
1.35k
        e = APR_BUCKET_NEXT(e);
126
1.35k
    } while (glen > 0);
127
128
    /* copy value */
129
1.35k
    dest = v->data;
130
1.35k
    do {
131
1.35k
        apr_size_t off;
132
133
1.35k
        assert(e != APR_BRIGADE_SENTINEL(bb));
134
1.35k
        s = apr_bucket_read(e, &data, &dlen, APR_BLOCK_READ);
135
1.35k
        if (s != APR_SUCCESS)
136
0
            return s;
137
138
15.2k
        for (off = 0; off < dlen; ++off) {
139
13.8k
            const char ch = data[off];
140
13.8k
            if (ch == '\r' || ch == '\n') {
141
                /* Eat [CR]LF of continuation or end of line */
142
2.10k
                if (!vlen && ch == '\n')
143
1.35k
                    eol = 1; /* done */
144
2.10k
                continue;
145
2.10k
            }
146
11.7k
            assert(vlen > 0);
147
11.7k
            *dest = ch;
148
11.7k
            ++dest;
149
11.7k
            --vlen;
150
11.7k
        }
151
152
1.35k
        e = APR_BUCKET_NEXT(e);
153
1.35k
    } while (!eol);
154
1.35k
    v->dlen = dest - v->data;
155
1.35k
    *dest++ = 0;
156
157
    /* write name */
158
1.35k
    v->name = dest;
159
2.70k
    for (i = 0; i < arr.nelts; ++i) {
160
1.35k
        iov = &((struct iovec *)arr.elts)[i];
161
1.35k
        memcpy(dest, iov->iov_base, iov->iov_len);
162
1.35k
        dest += iov->iov_len;
163
1.35k
        ++iov;
164
1.35k
    }
165
1.35k
    v->nlen = dest - v->name;
166
1.35k
    *dest = 0;
167
168
5.41k
    while ((f = APR_BRIGADE_FIRST(bb)) != e)
169
4.06k
        apr_bucket_delete(f);
170
171
1.35k
    apreq_param_tainted_on(param);
172
1.35k
    *p = param;
173
1.35k
    return APR_SUCCESS;
174
175
1.35k
}
176
177
10.5k
#define IS_TOKEN_CHAR(c) (apr_isalnum(c) \
178
10.5k
                          || ((c) && strchr("!#$%&'*+-.^_`|~", (c))))
179
180
APREQ_DECLARE_PARSER(apreq_parse_headers)
181
918
{
182
918
    apr_pool_t *pool = parser->pool;
183
918
    apr_bucket *e;
184
918
    struct hdr_ctx *ctx;
185
918
    char ch;
186
187
918
    if (parser->ctx == NULL) {
188
918
        ctx = apr_pcalloc(pool, sizeof *ctx);
189
918
        ctx->bb = apr_brigade_create(pool, parser->bucket_alloc);
190
918
        parser->ctx = ctx;
191
918
        ctx->status = HDR_NAME;
192
918
    }
193
0
    else
194
0
        ctx = parser->ctx;
195
196
918
    PARSER_STATUS_CHECK(HDR);
197
918
    e = APR_BRIGADE_LAST(ctx->bb);
198
918
    APR_BRIGADE_CONCAT(ctx->bb, bb);
199
200
    /* parse the brigade for CRLF_CRLF-terminated header block,
201
     * each time starting from the front of the brigade.
202
     */
203
204
918
    for (e = APR_BUCKET_NEXT(e);
205
1.02k
         e != APR_BRIGADE_SENTINEL(ctx->bb);
206
918
         e = APR_BUCKET_NEXT(e))
207
943
    {
208
943
        apr_size_t off = 0, dlen;
209
943
        const char *data;
210
943
        apr_status_t s;
211
943
        apreq_param_t *param = NULL; /* silences gcc-4.0 warning */
212
213
943
        if (APR_BUCKET_IS_EOS(e)) {
214
0
            ctx->status = HDR_COMPLETE;
215
0
            APR_BRIGADE_CONCAT(bb, ctx->bb);
216
0
            return APR_SUCCESS;
217
0
        }
218
219
943
        s = apr_bucket_read(e, &data, &dlen, APR_BLOCK_READ);
220
943
        if ( s != APR_SUCCESS ) {
221
0
            ctx->status = HDR_ERROR;
222
0
            return s;
223
0
        }
224
943
        if (dlen == 0)
225
0
            continue;
226
227
6.77k
    parse_hdr_bucket:
228
229
        /*              gap           nlen = 13
230
         *              vvv           glen =  3
231
         * Sample-Header:  grape      vlen =  5
232
         * ^^^^^^^^^^^^^   ^^^^^
233
         *     name        value
234
         */
235
236
6.77k
        switch (ctx->status) {
237
238
1.48k
        case HDR_NAME:
239
240
11.3k
            while (off < dlen) {
241
11.3k
                ch = data[off++];
242
11.3k
                switch (ch) {
243
1.40k
                case ':':
244
1.40k
                    if (!ctx->nlen) {
245
1
                        ctx->status = HDR_ERROR;
246
1
                        return APREQ_ERROR_BADHEADER;
247
1
                    }
248
1.40k
                    e = split_header_line(e, &off, &data, &dlen);
249
1.40k
                    ++ctx->glen;
250
1.40k
                    ctx->status = HDR_GAP;
251
1.40k
                    goto parse_hdr_bucket;
252
253
9.93k
                default:
254
9.93k
                    if (!IS_TOKEN_CHAR(ch)) {
255
29
                        ctx->status = HDR_ERROR;
256
29
                        return APREQ_ERROR_BADCHAR;
257
29
                    }
258
9.90k
                    ++ctx->nlen;
259
11.3k
                }
260
11.3k
            }
261
45
            break;
262
263
264
1.41k
        case HDR_GAP:
265
266
2.25k
            while (off < dlen) {
267
2.23k
                ch = data[off++];
268
2.23k
                switch (ch) {
269
466
                case ' ':
270
837
                case '\t':
271
837
                    ++ctx->glen;
272
837
                    break;
273
274
365
                case '\n':
275
365
                    e = split_header_line(e, &off, &data, &dlen);
276
365
                    ctx->status = HDR_NEWLINE;
277
365
                    goto parse_hdr_bucket;
278
279
188
                case '\r':
280
188
                    e = split_header_line(e, &off, &data, &dlen);
281
188
                    ctx->status = HDR_ENDLINE;
282
188
                    goto parse_hdr_bucket;
283
284
844
                default:
285
844
                    if (apr_iscntrl(ch)) {
286
2
                        ctx->status = HDR_ERROR;
287
2
                        return APREQ_ERROR_BADCHAR;
288
2
                    }
289
842
                    e = split_header_line(e, &off, &data, &dlen);
290
842
                    ++ctx->vlen;
291
842
                    ctx->status = HDR_VALUE;
292
842
                    goto parse_hdr_bucket;
293
2.23k
                }
294
2.23k
            }
295
22
            break;
296
297
298
1.04k
        case HDR_VALUE:
299
300
11.4k
            while (off < dlen) {
301
11.4k
                ch = data[off++];
302
11.4k
                switch (ch) {
303
777
                case '\n':
304
777
                    ctx->status = HDR_NEWLINE;
305
777
                    goto parse_hdr_bucket;
306
307
242
                case '\r':
308
242
                    ctx->status = HDR_ENDLINE;
309
242
                    goto parse_hdr_bucket;
310
311
10.4k
                default:
312
10.4k
                    if (apr_iscntrl(ch)) {
313
4
                        ctx->status = HDR_ERROR;
314
4
                        return APREQ_ERROR_BADCHAR;
315
4
                    }
316
10.4k
                    ++ctx->vlen;
317
11.4k
                }
318
11.4k
            }
319
19
            break;
320
321
322
487
        case HDR_ENDLINE:
323
664
        case HDR_LASTLINE:
324
325
664
            if (off == dlen)
326
5
                break;
327
328
659
            if (data[off++] != '\n') {
329
12
                ctx->status = HDR_ERROR;
330
12
                return APREQ_ERROR_BADHEADER;
331
12
            }
332
333
647
            if (ctx->status == HDR_LASTLINE) {
334
173
                ctx->status = HDR_COMPLETE;
335
173
                goto parse_hdr_bucket;
336
173
            }
337
338
            /* fall thru */
339
474
            ctx->status = HDR_NEWLINE;
340
341
1.86k
        case HDR_NEWLINE:
342
343
1.86k
            if (off == dlen)
344
8
                break;
345
346
1.85k
            ch = data[off++];
347
1.85k
            switch (ch) {
348
332
            case ' ':
349
509
            case '\t':
350
509
                ++ctx->vlen;
351
509
                break;
352
353
1.35k
            default:
354
1.35k
                e = split_header_line(e, &off, &data, &dlen);
355
356
                /* consume from splitted brigade now */
357
1.35k
                s = consume_header_line(&param, pool, ctx->bb,
358
1.35k
                                        ctx->nlen, ctx->glen, ctx->vlen);
359
1.35k
                if (parser->hook != NULL && s == APR_SUCCESS)
360
0
                    s = apreq_hook_run(parser->hook, param, NULL);
361
1.35k
                if (s != APR_SUCCESS) {
362
0
                    ctx->status = HDR_ERROR;
363
0
                    return s;
364
0
                }
365
1.35k
                apreq_value_table_add(&param->v, t);
366
1.35k
                ctx->nlen = 0;
367
1.35k
                ctx->vlen = 0;
368
1.35k
                ctx->glen = 0;
369
370
1.35k
                switch (ch) {
371
601
                case '\n':
372
601
                    ctx->status = HDR_COMPLETE;
373
601
                    break;
374
375
176
                case '\r':
376
176
                    ctx->status = HDR_LASTLINE;
377
176
                    break;
378
379
573
                default:
380
573
                    if (!IS_TOKEN_CHAR(ch)) {
381
12
                        ctx->status = HDR_ERROR;
382
12
                        return APREQ_ERROR_BADCHAR;
383
12
                    }
384
561
                    ++ctx->nlen;
385
561
                    ctx->status = HDR_NAME;
386
561
                    break;
387
1.35k
                }
388
1.33k
                goto parse_hdr_bucket;
389
1.85k
            }
390
391
            /* fall thru */
392
509
            ctx->status = HDR_FOLDLINE;
393
394
512
        case HDR_FOLDLINE:
395
396
848
            while (off < dlen) {
397
839
                ch = data[off++];
398
839
                switch (ch) {
399
161
                case ' ':
400
336
                case '\t':
401
336
                    ++ctx->vlen;
402
336
                    break;
403
404
249
                case '\n':
405
249
                    ctx->status = HDR_NEWLINE;
406
249
                    goto parse_hdr_bucket;
407
408
56
                case '\r':
409
56
                    ctx->status = HDR_ENDLINE;
410
56
                    goto parse_hdr_bucket;
411
412
198
                default:
413
198
                    if (apr_iscntrl(ch)) {
414
1
                        ctx->status = HDR_ERROR;
415
1
                        return APREQ_ERROR_BADCHAR;
416
1
                    }
417
197
                    ctx->status = HDR_VALUE;
418
197
                    ++ctx->vlen;
419
197
                    goto parse_hdr_bucket;
420
839
                }
421
839
            }
422
9
            break;
423
424
425
774
        case HDR_COMPLETE:
426
427
774
            if (off < dlen)
428
768
                apr_bucket_split(e, off);
429
430
774
            e = APR_BUCKET_NEXT(e);
431
775
            do {
432
775
                apr_bucket *f = APR_BRIGADE_FIRST(ctx->bb);
433
775
                apr_bucket_delete(f);
434
775
            } while (e != APR_BRIGADE_FIRST(ctx->bb));
435
436
774
            APR_BRIGADE_CONCAT(bb, ctx->bb);
437
774
            return APR_SUCCESS;
438
439
440
0
        default:
441
0
            assert(0); /* not reached */
442
6.77k
        }
443
6.77k
    }
444
83
    apreq_brigade_setaside(ctx->bb,pool);
445
83
    return APR_INCOMPLETE;
446
918
}