mirror of
https://github.com/apache/httpd.git
synced 2025-04-18 22:24:07 +03:00
The function apreq_param_make() will return NULL on failure. However NULL check are forgetten before derenference, which could lead to NULL pointer dereference. Adding NULL check to all use of apreq_param_make(). Submitted by: Zhou Qingyang <zhou1615@umn.edu> Github: closes #303 git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@1908981 13f79535-47bb-0310-9956-ffa450edef68
449 lines
12 KiB
C
449 lines
12 KiB
C
/*
|
|
** Licensed to the Apache Software Foundation (ASF) under one or more
|
|
** contributor license agreements. See the NOTICE file distributed with
|
|
** this work for additional information regarding copyright ownership.
|
|
** The ASF licenses this file to You under the Apache License, Version 2.0
|
|
** (the "License"); 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
|
|
**
|
|
** Unless required by applicable law or agreed to in writing, software
|
|
** distributed under the License is distributed on an "AS IS" BASIS,
|
|
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
** See the License for the specific language governing permissions and
|
|
** limitations under the License.
|
|
*/
|
|
#include <assert.h>
|
|
#include "apreq_parser.h"
|
|
#include "apreq_error.h"
|
|
#include "apreq_util.h"
|
|
|
|
#include "apr_lib.h" /* for apr_iscntrl() & co */
|
|
|
|
#define PARSER_STATUS_CHECK(PREFIX) do { \
|
|
if (ctx->status == PREFIX##_ERROR) \
|
|
return APREQ_ERROR_GENERAL; \
|
|
else if (ctx->status == PREFIX##_COMPLETE) \
|
|
return APR_SUCCESS; \
|
|
else if (bb == NULL) \
|
|
return APR_INCOMPLETE; \
|
|
} while (0);
|
|
|
|
|
|
struct hdr_ctx {
|
|
apr_bucket_brigade *bb;
|
|
apr_size_t nlen;
|
|
apr_size_t glen;
|
|
apr_size_t vlen;
|
|
enum {
|
|
HDR_NAME,
|
|
HDR_GAP,
|
|
HDR_VALUE,
|
|
HDR_NEWLINE,
|
|
HDR_ENDLINE,
|
|
HDR_FOLDLINE,
|
|
HDR_LASTLINE,
|
|
HDR_COMPLETE,
|
|
HDR_ERROR
|
|
} status;
|
|
};
|
|
|
|
/********************* header parsing utils ********************/
|
|
|
|
|
|
static apr_bucket *split_header_line(apr_bucket *e, apr_size_t *off,
|
|
const char **data, apr_size_t *dlen)
|
|
{
|
|
if (*off > 1) {
|
|
apr_bucket_split(e, *off - 1);
|
|
e = APR_BUCKET_NEXT(e);
|
|
*dlen -= *off - 1;
|
|
*data += *off - 1;
|
|
*off = 1;
|
|
}
|
|
return e;
|
|
}
|
|
|
|
static apr_status_t consume_header_line(apreq_param_t **p,
|
|
apr_pool_t *pool,
|
|
apr_bucket_brigade *bb,
|
|
apr_size_t nlen,
|
|
apr_size_t glen,
|
|
apr_size_t vlen)
|
|
{
|
|
apreq_param_t *param;
|
|
apreq_value_t *v;
|
|
apr_bucket *e, *f;
|
|
apr_status_t s;
|
|
struct iovec vec[APREQ_DEFAULT_NELTS], *iov;
|
|
apr_array_header_t arr;
|
|
char *dest;
|
|
const char *data;
|
|
apr_size_t dlen;
|
|
int i, eol = 0;
|
|
|
|
param = apreq_param_make(pool, NULL, nlen, NULL, vlen);
|
|
if (param == NULL)
|
|
return APR_ENOMEM;
|
|
*(const apreq_value_t **)&v = ¶m->v;
|
|
|
|
arr.pool = pool;
|
|
arr.elt_size = sizeof(struct iovec);
|
|
arr.nelts = 0;
|
|
arr.nalloc = APREQ_DEFAULT_NELTS;
|
|
arr.elts = (char *)vec;
|
|
|
|
e = APR_BRIGADE_FIRST(bb);
|
|
|
|
/* store name in a temporary iovec array */
|
|
do {
|
|
apr_size_t len;
|
|
|
|
assert(e != APR_BRIGADE_SENTINEL(bb));
|
|
iov = (struct iovec *)apr_array_push(&arr);
|
|
s = apr_bucket_read(e, (const char **)&iov->iov_base,
|
|
&len, APR_BLOCK_READ);
|
|
if (s != APR_SUCCESS)
|
|
return s;
|
|
iov->iov_len = len;
|
|
|
|
assert(nlen >= len);
|
|
nlen -= len;
|
|
|
|
e = APR_BUCKET_NEXT(e);
|
|
} while (nlen > 0);
|
|
|
|
/* skip gap */
|
|
do {
|
|
assert(e != APR_BRIGADE_SENTINEL(bb));
|
|
s = apr_bucket_read(e, &data, &dlen, APR_BLOCK_READ);
|
|
if (s != APR_SUCCESS)
|
|
return s;
|
|
|
|
assert(glen >= dlen);
|
|
glen -= dlen;
|
|
|
|
e = APR_BUCKET_NEXT(e);
|
|
} while (glen > 0);
|
|
|
|
/* copy value */
|
|
dest = v->data;
|
|
do {
|
|
apr_size_t off;
|
|
|
|
assert(e != APR_BRIGADE_SENTINEL(bb));
|
|
s = apr_bucket_read(e, &data, &dlen, APR_BLOCK_READ);
|
|
if (s != APR_SUCCESS)
|
|
return s;
|
|
|
|
for (off = 0; off < dlen; ++off) {
|
|
const char ch = data[off];
|
|
if (ch == '\r' || ch == '\n') {
|
|
/* Eat [CR]LF of continuation or end of line */
|
|
if (!vlen && ch == '\n')
|
|
eol = 1; /* done */
|
|
continue;
|
|
}
|
|
assert(vlen > 0);
|
|
*dest = ch;
|
|
++dest;
|
|
--vlen;
|
|
}
|
|
|
|
e = APR_BUCKET_NEXT(e);
|
|
} while (!eol);
|
|
v->dlen = dest - v->data;
|
|
*dest++ = 0;
|
|
|
|
/* write name */
|
|
v->name = dest;
|
|
for (i = 0; i < arr.nelts; ++i) {
|
|
iov = &((struct iovec *)arr.elts)[i];
|
|
memcpy(dest, iov->iov_base, iov->iov_len);
|
|
dest += iov->iov_len;
|
|
++iov;
|
|
}
|
|
v->nlen = dest - v->name;
|
|
*dest = 0;
|
|
|
|
while ((f = APR_BRIGADE_FIRST(bb)) != e)
|
|
apr_bucket_delete(f);
|
|
|
|
apreq_param_tainted_on(param);
|
|
*p = param;
|
|
return APR_SUCCESS;
|
|
|
|
}
|
|
|
|
#define IS_TOKEN_CHAR(c) (apr_isalnum(c) \
|
|
|| ((c) && strchr("!#$%&'*+-.^_`|~", (c))))
|
|
|
|
APREQ_DECLARE_PARSER(apreq_parse_headers)
|
|
{
|
|
apr_pool_t *pool = parser->pool;
|
|
apr_bucket *e;
|
|
struct hdr_ctx *ctx;
|
|
char ch;
|
|
|
|
if (parser->ctx == NULL) {
|
|
ctx = apr_pcalloc(pool, sizeof *ctx);
|
|
ctx->bb = apr_brigade_create(pool, parser->bucket_alloc);
|
|
parser->ctx = ctx;
|
|
ctx->status = HDR_NAME;
|
|
}
|
|
else
|
|
ctx = parser->ctx;
|
|
|
|
PARSER_STATUS_CHECK(HDR);
|
|
e = APR_BRIGADE_LAST(ctx->bb);
|
|
APR_BRIGADE_CONCAT(ctx->bb, bb);
|
|
|
|
/* parse the brigade for CRLF_CRLF-terminated header block,
|
|
* each time starting from the front of the brigade.
|
|
*/
|
|
|
|
for (e = APR_BUCKET_NEXT(e);
|
|
e != APR_BRIGADE_SENTINEL(ctx->bb);
|
|
e = APR_BUCKET_NEXT(e))
|
|
{
|
|
apr_size_t off = 0, dlen;
|
|
const char *data;
|
|
apr_status_t s;
|
|
apreq_param_t *param = NULL; /* silences gcc-4.0 warning */
|
|
|
|
if (APR_BUCKET_IS_EOS(e)) {
|
|
ctx->status = HDR_COMPLETE;
|
|
APR_BRIGADE_CONCAT(bb, ctx->bb);
|
|
return APR_SUCCESS;
|
|
}
|
|
|
|
s = apr_bucket_read(e, &data, &dlen, APR_BLOCK_READ);
|
|
if ( s != APR_SUCCESS ) {
|
|
ctx->status = HDR_ERROR;
|
|
return s;
|
|
}
|
|
if (dlen == 0)
|
|
continue;
|
|
|
|
parse_hdr_bucket:
|
|
|
|
/* gap nlen = 13
|
|
* vvv glen = 3
|
|
* Sample-Header: grape vlen = 5
|
|
* ^^^^^^^^^^^^^ ^^^^^
|
|
* name value
|
|
*/
|
|
|
|
switch (ctx->status) {
|
|
|
|
case HDR_NAME:
|
|
|
|
while (off < dlen) {
|
|
ch = data[off++];
|
|
switch (ch) {
|
|
case ':':
|
|
if (!ctx->nlen) {
|
|
ctx->status = HDR_ERROR;
|
|
return APREQ_ERROR_BADHEADER;
|
|
}
|
|
e = split_header_line(e, &off, &data, &dlen);
|
|
++ctx->glen;
|
|
ctx->status = HDR_GAP;
|
|
goto parse_hdr_bucket;
|
|
|
|
default:
|
|
if (!IS_TOKEN_CHAR(ch)) {
|
|
ctx->status = HDR_ERROR;
|
|
return APREQ_ERROR_BADCHAR;
|
|
}
|
|
++ctx->nlen;
|
|
}
|
|
}
|
|
break;
|
|
|
|
|
|
case HDR_GAP:
|
|
|
|
while (off < dlen) {
|
|
ch = data[off++];
|
|
switch (ch) {
|
|
case ' ':
|
|
case '\t':
|
|
++ctx->glen;
|
|
break;
|
|
|
|
case '\n':
|
|
e = split_header_line(e, &off, &data, &dlen);
|
|
ctx->status = HDR_NEWLINE;
|
|
goto parse_hdr_bucket;
|
|
|
|
case '\r':
|
|
e = split_header_line(e, &off, &data, &dlen);
|
|
ctx->status = HDR_ENDLINE;
|
|
goto parse_hdr_bucket;
|
|
|
|
default:
|
|
if (apr_iscntrl(ch)) {
|
|
ctx->status = HDR_ERROR;
|
|
return APREQ_ERROR_BADCHAR;
|
|
}
|
|
e = split_header_line(e, &off, &data, &dlen);
|
|
++ctx->vlen;
|
|
ctx->status = HDR_VALUE;
|
|
goto parse_hdr_bucket;
|
|
}
|
|
}
|
|
break;
|
|
|
|
|
|
case HDR_VALUE:
|
|
|
|
while (off < dlen) {
|
|
ch = data[off++];
|
|
switch (ch) {
|
|
case '\n':
|
|
ctx->status = HDR_NEWLINE;
|
|
goto parse_hdr_bucket;
|
|
|
|
case '\r':
|
|
ctx->status = HDR_ENDLINE;
|
|
goto parse_hdr_bucket;
|
|
|
|
default:
|
|
if (apr_iscntrl(ch)) {
|
|
ctx->status = HDR_ERROR;
|
|
return APREQ_ERROR_BADCHAR;
|
|
}
|
|
++ctx->vlen;
|
|
}
|
|
}
|
|
break;
|
|
|
|
|
|
case HDR_ENDLINE:
|
|
case HDR_LASTLINE:
|
|
|
|
if (off == dlen)
|
|
break;
|
|
|
|
if (data[off++] != '\n') {
|
|
ctx->status = HDR_ERROR;
|
|
return APREQ_ERROR_BADHEADER;
|
|
}
|
|
|
|
if (ctx->status == HDR_LASTLINE) {
|
|
ctx->status = HDR_COMPLETE;
|
|
goto parse_hdr_bucket;
|
|
}
|
|
|
|
/* fall thru */
|
|
ctx->status = HDR_NEWLINE;
|
|
|
|
case HDR_NEWLINE:
|
|
|
|
if (off == dlen)
|
|
break;
|
|
|
|
ch = data[off++];
|
|
switch (ch) {
|
|
case ' ':
|
|
case '\t':
|
|
++ctx->vlen;
|
|
break;
|
|
|
|
default:
|
|
e = split_header_line(e, &off, &data, &dlen);
|
|
|
|
/* consume from splitted brigade now */
|
|
s = consume_header_line(¶m, pool, ctx->bb,
|
|
ctx->nlen, ctx->glen, ctx->vlen);
|
|
if (parser->hook != NULL && s == APR_SUCCESS)
|
|
s = apreq_hook_run(parser->hook, param, NULL);
|
|
if (s != APR_SUCCESS) {
|
|
ctx->status = HDR_ERROR;
|
|
return s;
|
|
}
|
|
apreq_value_table_add(¶m->v, t);
|
|
ctx->nlen = 0;
|
|
ctx->vlen = 0;
|
|
ctx->glen = 0;
|
|
|
|
switch (ch) {
|
|
case '\n':
|
|
ctx->status = HDR_COMPLETE;
|
|
break;
|
|
|
|
case '\r':
|
|
ctx->status = HDR_LASTLINE;
|
|
break;
|
|
|
|
default:
|
|
if (!IS_TOKEN_CHAR(ch)) {
|
|
ctx->status = HDR_ERROR;
|
|
return APREQ_ERROR_BADCHAR;
|
|
}
|
|
++ctx->nlen;
|
|
ctx->status = HDR_NAME;
|
|
break;
|
|
}
|
|
goto parse_hdr_bucket;
|
|
}
|
|
|
|
/* fall thru */
|
|
ctx->status = HDR_FOLDLINE;
|
|
|
|
case HDR_FOLDLINE:
|
|
|
|
while (off < dlen) {
|
|
ch = data[off++];
|
|
switch (ch) {
|
|
case ' ':
|
|
case '\t':
|
|
++ctx->vlen;
|
|
break;
|
|
|
|
case '\n':
|
|
ctx->status = HDR_NEWLINE;
|
|
goto parse_hdr_bucket;
|
|
|
|
case '\r':
|
|
ctx->status = HDR_ENDLINE;
|
|
goto parse_hdr_bucket;
|
|
|
|
default:
|
|
if (apr_iscntrl(ch)) {
|
|
ctx->status = HDR_ERROR;
|
|
return APREQ_ERROR_BADCHAR;
|
|
}
|
|
ctx->status = HDR_VALUE;
|
|
++ctx->vlen;
|
|
goto parse_hdr_bucket;
|
|
}
|
|
}
|
|
break;
|
|
|
|
|
|
case HDR_COMPLETE:
|
|
|
|
if (off < dlen)
|
|
apr_bucket_split(e, off);
|
|
|
|
e = APR_BUCKET_NEXT(e);
|
|
do {
|
|
apr_bucket *f = APR_BRIGADE_FIRST(ctx->bb);
|
|
apr_bucket_delete(f);
|
|
} while (e != APR_BRIGADE_FIRST(ctx->bb));
|
|
|
|
APR_BRIGADE_CONCAT(bb, ctx->bb);
|
|
return APR_SUCCESS;
|
|
|
|
|
|
default:
|
|
assert(0); /* not reached */
|
|
}
|
|
}
|
|
apreq_brigade_setaside(ctx->bb,pool);
|
|
return APR_INCOMPLETE;
|
|
}
|