/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 | 858 | #define PARSER_STATUS_CHECK(PREFIX) do { \ |
25 | 858 | if (ctx->status == PREFIX##_ERROR) \ |
26 | 858 | return APREQ_ERROR_GENERAL; \ |
27 | 858 | else if (ctx->status == PREFIX##_COMPLETE) \ |
28 | 858 | return APR_SUCCESS; \ |
29 | 858 | else if (bb == NULL) \ |
30 | 858 | return APR_INCOMPLETE; \ |
31 | 858 | } 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 | 3.87k | { |
58 | 3.87k | if (*off > 1) { |
59 | 3.86k | apr_bucket_split(e, *off - 1); |
60 | 3.86k | e = APR_BUCKET_NEXT(e); |
61 | 3.86k | *dlen -= *off - 1; |
62 | 3.86k | *data += *off - 1; |
63 | 3.86k | *off = 1; |
64 | 3.86k | } |
65 | 3.87k | return e; |
66 | 3.87k | } |
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.25k | { |
75 | 1.25k | apreq_param_t *param; |
76 | 1.25k | apreq_value_t *v; |
77 | 1.25k | apr_bucket *e, *f; |
78 | 1.25k | apr_status_t s; |
79 | 1.25k | struct iovec vec[APREQ_DEFAULT_NELTS], *iov; |
80 | 1.25k | apr_array_header_t arr; |
81 | 1.25k | char *dest; |
82 | 1.25k | const char *data; |
83 | 1.25k | apr_size_t dlen; |
84 | 1.25k | int i, eol = 0; |
85 | | |
86 | 1.25k | param = apreq_param_make(pool, NULL, nlen, NULL, vlen); |
87 | 1.25k | if (param == NULL) |
88 | 0 | return APR_ENOMEM; |
89 | 1.25k | *(const apreq_value_t **)&v = ¶m->v; |
90 | | |
91 | 1.25k | arr.pool = pool; |
92 | 1.25k | arr.elt_size = sizeof(struct iovec); |
93 | 1.25k | arr.nelts = 0; |
94 | 1.25k | arr.nalloc = APREQ_DEFAULT_NELTS; |
95 | 1.25k | arr.elts = (char *)vec; |
96 | | |
97 | 1.25k | e = APR_BRIGADE_FIRST(bb); |
98 | | |
99 | | /* store name in a temporary iovec array */ |
100 | 1.26k | do { |
101 | 1.26k | apr_size_t len; |
102 | | |
103 | 1.26k | assert(e != APR_BRIGADE_SENTINEL(bb)); |
104 | 1.26k | iov = (struct iovec *)apr_array_push(&arr); |
105 | 1.26k | s = apr_bucket_read(e, (const char **)&iov->iov_base, |
106 | 1.26k | &len, APR_BLOCK_READ); |
107 | 1.26k | if (s != APR_SUCCESS) |
108 | 0 | return s; |
109 | 1.26k | iov->iov_len = len; |
110 | | |
111 | 1.26k | assert(nlen >= len); |
112 | 1.26k | nlen -= len; |
113 | | |
114 | 1.26k | e = APR_BUCKET_NEXT(e); |
115 | 1.26k | } while (nlen > 0); |
116 | | |
117 | | /* skip gap */ |
118 | 1.26k | do { |
119 | 1.26k | assert(e != APR_BRIGADE_SENTINEL(bb)); |
120 | 1.26k | s = apr_bucket_read(e, &data, &dlen, APR_BLOCK_READ); |
121 | 1.26k | if (s != APR_SUCCESS) |
122 | 0 | return s; |
123 | | |
124 | 1.26k | assert(glen >= dlen); |
125 | 1.26k | glen -= dlen; |
126 | | |
127 | 1.26k | e = APR_BUCKET_NEXT(e); |
128 | 1.26k | } while (glen > 0); |
129 | | |
130 | | /* copy value */ |
131 | 1.25k | dest = v->data; |
132 | 1.25k | do { |
133 | 1.25k | apr_size_t off; |
134 | | |
135 | 1.25k | assert(e != APR_BRIGADE_SENTINEL(bb)); |
136 | 1.25k | s = apr_bucket_read(e, &data, &dlen, APR_BLOCK_READ); |
137 | 1.25k | if (s != APR_SUCCESS) |
138 | 0 | return s; |
139 | | |
140 | 13.6k | for (off = 0; off < dlen; ++off) { |
141 | 12.4k | const char ch = data[off]; |
142 | 12.4k | if (ch == '\r' || ch == '\n') { |
143 | | /* Eat [CR]LF of continuation or end of line */ |
144 | 1.85k | if (!vlen && ch == '\n') |
145 | 1.25k | eol = 1; /* done */ |
146 | 1.85k | continue; |
147 | 1.85k | } |
148 | 10.5k | assert(vlen > 0); |
149 | 10.5k | *dest = ch; |
150 | 10.5k | ++dest; |
151 | 10.5k | --vlen; |
152 | 10.5k | } |
153 | | |
154 | 1.25k | e = APR_BUCKET_NEXT(e); |
155 | 1.25k | } while (!eol); |
156 | 1.25k | v->dlen = dest - v->data; |
157 | 1.25k | *dest++ = 0; |
158 | | |
159 | | /* write name */ |
160 | 1.25k | v->name = dest; |
161 | 2.51k | for (i = 0; i < arr.nelts; ++i) { |
162 | 1.26k | iov = &((struct iovec *)arr.elts)[i]; |
163 | 1.26k | memcpy(dest, iov->iov_base, iov->iov_len); |
164 | 1.26k | dest += iov->iov_len; |
165 | 1.26k | ++iov; |
166 | 1.26k | } |
167 | 1.25k | v->nlen = dest - v->name; |
168 | 1.25k | *dest = 0; |
169 | | |
170 | 5.03k | while ((f = APR_BRIGADE_FIRST(bb)) != e) |
171 | 3.78k | apr_bucket_delete(f); |
172 | | |
173 | 1.25k | apreq_param_tainted_on(param); |
174 | 1.25k | *p = param; |
175 | 1.25k | return APR_SUCCESS; |
176 | | |
177 | 1.25k | } |
178 | | |
179 | 10.2k | #define IS_TOKEN_CHAR(c) (apr_isalnum(c) \ |
180 | 10.2k | || ((c) && strchr("!#$%&'*+-.^_`|~", (c)))) |
181 | | |
182 | | APREQ_DECLARE_PARSER(apreq_parse_headers) |
183 | 858 | { |
184 | 858 | apr_pool_t *pool = parser->pool; |
185 | 858 | apr_bucket *e; |
186 | 858 | struct hdr_ctx *ctx; |
187 | 858 | char ch; |
188 | | |
189 | 858 | if (parser->ctx == NULL) { |
190 | 858 | ctx = apr_pcalloc(pool, sizeof *ctx); |
191 | 858 | ctx->bb = apr_brigade_create(pool, parser->bucket_alloc); |
192 | 858 | parser->ctx = ctx; |
193 | 858 | ctx->status = HDR_NAME; |
194 | 858 | } |
195 | 0 | else |
196 | 0 | ctx = parser->ctx; |
197 | | |
198 | 858 | PARSER_STATUS_CHECK(HDR); |
199 | 858 | e = APR_BRIGADE_LAST(ctx->bb); |
200 | 858 | APR_BRIGADE_CONCAT(ctx->bb, bb); |
201 | | |
202 | | /* parse the brigade for CRLF_CRLF-terminated header block, |
203 | | * each time starting from the front of the brigade. |
204 | | */ |
205 | | |
206 | 858 | for (e = APR_BUCKET_NEXT(e); |
207 | 961 | e != APR_BRIGADE_SENTINEL(ctx->bb); |
208 | 858 | e = APR_BUCKET_NEXT(e)) |
209 | 884 | { |
210 | 884 | apr_size_t off = 0, dlen; |
211 | 884 | const char *data; |
212 | 884 | apr_status_t s; |
213 | 884 | apreq_param_t *param = NULL; /* silences gcc-4.0 warning */ |
214 | | |
215 | 884 | if (APR_BUCKET_IS_EOS(e)) { |
216 | 0 | ctx->status = HDR_COMPLETE; |
217 | 0 | APR_BRIGADE_CONCAT(bb, ctx->bb); |
218 | 0 | return APR_SUCCESS; |
219 | 0 | } |
220 | | |
221 | 884 | s = apr_bucket_read(e, &data, &dlen, APR_BLOCK_READ); |
222 | 884 | if ( s != APR_SUCCESS ) { |
223 | 0 | ctx->status = HDR_ERROR; |
224 | 0 | return s; |
225 | 0 | } |
226 | 884 | if (dlen == 0) |
227 | 0 | continue; |
228 | | |
229 | 6.35k | parse_hdr_bucket: |
230 | | |
231 | | /* gap nlen = 13 |
232 | | * vvv glen = 3 |
233 | | * Sample-Header: grape vlen = 5 |
234 | | * ^^^^^^^^^^^^^ ^^^^^ |
235 | | * name value |
236 | | */ |
237 | | |
238 | 6.35k | switch (ctx->status) { |
239 | | |
240 | 1.37k | case HDR_NAME: |
241 | | |
242 | 11.0k | while (off < dlen) { |
243 | 11.0k | ch = data[off++]; |
244 | 11.0k | switch (ch) { |
245 | 1.31k | case ':': |
246 | 1.31k | if (!ctx->nlen) { |
247 | 2 | ctx->status = HDR_ERROR; |
248 | 2 | return APREQ_ERROR_BADHEADER; |
249 | 2 | } |
250 | 1.31k | e = split_header_line(e, &off, &data, &dlen); |
251 | 1.31k | ++ctx->glen; |
252 | 1.31k | ctx->status = HDR_GAP; |
253 | 1.31k | goto parse_hdr_bucket; |
254 | | |
255 | 9.71k | default: |
256 | 9.71k | if (!IS_TOKEN_CHAR(ch)) { |
257 | 22 | ctx->status = HDR_ERROR; |
258 | 22 | return APREQ_ERROR_BADCHAR; |
259 | 22 | } |
260 | 9.68k | ++ctx->nlen; |
261 | 11.0k | } |
262 | 11.0k | } |
263 | 37 | break; |
264 | | |
265 | | |
266 | 1.32k | case HDR_GAP: |
267 | | |
268 | 1.95k | while (off < dlen) { |
269 | 1.93k | ch = data[off++]; |
270 | 1.93k | switch (ch) { |
271 | 341 | case ' ': |
272 | 626 | case '\t': |
273 | 626 | ++ctx->glen; |
274 | 626 | break; |
275 | | |
276 | 361 | case '\n': |
277 | 361 | e = split_header_line(e, &off, &data, &dlen); |
278 | 361 | ctx->status = HDR_NEWLINE; |
279 | 361 | goto parse_hdr_bucket; |
280 | | |
281 | 76 | case '\r': |
282 | 76 | e = split_header_line(e, &off, &data, &dlen); |
283 | 76 | ctx->status = HDR_ENDLINE; |
284 | 76 | goto parse_hdr_bucket; |
285 | | |
286 | 869 | default: |
287 | 869 | if (apr_iscntrl(ch)) { |
288 | 2 | ctx->status = HDR_ERROR; |
289 | 2 | return APREQ_ERROR_BADCHAR; |
290 | 2 | } |
291 | 867 | e = split_header_line(e, &off, &data, &dlen); |
292 | 867 | ++ctx->vlen; |
293 | 867 | ctx->status = HDR_VALUE; |
294 | 867 | goto parse_hdr_bucket; |
295 | 1.93k | } |
296 | 1.93k | } |
297 | 23 | break; |
298 | | |
299 | | |
300 | 1.00k | case HDR_VALUE: |
301 | | |
302 | 10.4k | while (off < dlen) { |
303 | 10.3k | ch = data[off++]; |
304 | 10.3k | switch (ch) { |
305 | 749 | case '\n': |
306 | 749 | ctx->status = HDR_NEWLINE; |
307 | 749 | goto parse_hdr_bucket; |
308 | | |
309 | 231 | case '\r': |
310 | 231 | ctx->status = HDR_ENDLINE; |
311 | 231 | goto parse_hdr_bucket; |
312 | | |
313 | 9.41k | default: |
314 | 9.41k | if (apr_iscntrl(ch)) { |
315 | 7 | ctx->status = HDR_ERROR; |
316 | 7 | return APREQ_ERROR_BADCHAR; |
317 | 7 | } |
318 | 9.41k | ++ctx->vlen; |
319 | 10.3k | } |
320 | 10.3k | } |
321 | 17 | break; |
322 | | |
323 | | |
324 | 388 | case HDR_ENDLINE: |
325 | 553 | case HDR_LASTLINE: |
326 | | |
327 | 553 | if (off == dlen) |
328 | 4 | break; |
329 | | |
330 | 549 | if (data[off++] != '\n') { |
331 | 13 | ctx->status = HDR_ERROR; |
332 | 13 | return APREQ_ERROR_BADHEADER; |
333 | 13 | } |
334 | | |
335 | 536 | if (ctx->status == HDR_LASTLINE) { |
336 | 157 | ctx->status = HDR_COMPLETE; |
337 | 157 | goto parse_hdr_bucket; |
338 | 157 | } |
339 | | |
340 | | /* fall thru */ |
341 | 379 | ctx->status = HDR_NEWLINE; |
342 | | |
343 | 1.74k | case HDR_NEWLINE: |
344 | | |
345 | 1.74k | if (off == dlen) |
346 | 12 | break; |
347 | | |
348 | 1.73k | ch = data[off++]; |
349 | 1.73k | switch (ch) { |
350 | 238 | case ' ': |
351 | 480 | case '\t': |
352 | 480 | ++ctx->vlen; |
353 | 480 | break; |
354 | | |
355 | 1.25k | default: |
356 | 1.25k | e = split_header_line(e, &off, &data, &dlen); |
357 | | |
358 | | /* consume from splitted brigade now */ |
359 | 1.25k | s = consume_header_line(¶m, pool, ctx->bb, |
360 | 1.25k | ctx->nlen, ctx->glen, ctx->vlen); |
361 | 1.25k | if (parser->hook != NULL && s == APR_SUCCESS) |
362 | 0 | s = apreq_hook_run(parser->hook, param, NULL); |
363 | 1.25k | if (s != APR_SUCCESS) { |
364 | 0 | ctx->status = HDR_ERROR; |
365 | 0 | return s; |
366 | 0 | } |
367 | 1.25k | apreq_value_table_add(¶m->v, t); |
368 | 1.25k | ctx->nlen = 0; |
369 | 1.25k | ctx->vlen = 0; |
370 | 1.25k | ctx->glen = 0; |
371 | | |
372 | 1.25k | switch (ch) { |
373 | 567 | case '\n': |
374 | 567 | ctx->status = HDR_COMPLETE; |
375 | 567 | break; |
376 | | |
377 | 164 | case '\r': |
378 | 164 | ctx->status = HDR_LASTLINE; |
379 | 164 | break; |
380 | | |
381 | 524 | default: |
382 | 524 | if (!IS_TOKEN_CHAR(ch)) { |
383 | 10 | ctx->status = HDR_ERROR; |
384 | 10 | return APREQ_ERROR_BADCHAR; |
385 | 10 | } |
386 | 514 | ++ctx->nlen; |
387 | 514 | ctx->status = HDR_NAME; |
388 | 514 | break; |
389 | 1.25k | } |
390 | 1.24k | goto parse_hdr_bucket; |
391 | 1.73k | } |
392 | | |
393 | | /* fall thru */ |
394 | 480 | ctx->status = HDR_FOLDLINE; |
395 | | |
396 | 482 | case HDR_FOLDLINE: |
397 | | |
398 | 856 | while (off < dlen) { |
399 | 846 | ch = data[off++]; |
400 | 846 | switch (ch) { |
401 | 248 | case ' ': |
402 | 374 | case '\t': |
403 | 374 | ++ctx->vlen; |
404 | 374 | break; |
405 | | |
406 | 256 | case '\n': |
407 | 256 | ctx->status = HDR_NEWLINE; |
408 | 256 | goto parse_hdr_bucket; |
409 | | |
410 | 80 | case '\r': |
411 | 80 | ctx->status = HDR_ENDLINE; |
412 | 80 | goto parse_hdr_bucket; |
413 | | |
414 | 136 | default: |
415 | 136 | if (apr_iscntrl(ch)) { |
416 | 1 | ctx->status = HDR_ERROR; |
417 | 1 | return APREQ_ERROR_BADCHAR; |
418 | 1 | } |
419 | 135 | ctx->status = HDR_VALUE; |
420 | 135 | ++ctx->vlen; |
421 | 135 | goto parse_hdr_bucket; |
422 | 846 | } |
423 | 846 | } |
424 | 10 | break; |
425 | | |
426 | | |
427 | 724 | case HDR_COMPLETE: |
428 | | |
429 | 724 | if (off < dlen) |
430 | 714 | apr_bucket_split(e, off); |
431 | | |
432 | 724 | e = APR_BUCKET_NEXT(e); |
433 | 725 | do { |
434 | 725 | apr_bucket *f = APR_BRIGADE_FIRST(ctx->bb); |
435 | 725 | apr_bucket_delete(f); |
436 | 725 | } while (e != APR_BRIGADE_FIRST(ctx->bb)); |
437 | | |
438 | 724 | APR_BRIGADE_CONCAT(bb, ctx->bb); |
439 | 724 | return APR_SUCCESS; |
440 | | |
441 | | |
442 | 0 | default: |
443 | 0 | assert(0); /* not reached */ |
444 | 6.35k | } |
445 | 6.35k | } |
446 | 77 | apreq_brigade_setaside(ctx->bb,pool); |
447 | 77 | return APR_INCOMPLETE; |
448 | 858 | } |