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
278 lines
7.5 KiB
C
278 lines
7.5 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 "apreq_parser.h"
|
|
#include "apreq_util.h"
|
|
#include "apreq_error.h"
|
|
|
|
|
|
#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 url_ctx {
|
|
apr_bucket_brigade *bb;
|
|
apr_size_t nlen;
|
|
apr_size_t vlen;
|
|
enum {
|
|
URL_NAME,
|
|
URL_VALUE,
|
|
URL_COMPLETE,
|
|
URL_ERROR
|
|
} status;
|
|
};
|
|
|
|
|
|
/******************** application/x-www-form-urlencoded ********************/
|
|
|
|
static apr_status_t split_urlword(apreq_param_t **p, apr_pool_t *pool,
|
|
apr_bucket_brigade *bb,
|
|
apr_size_t nlen,
|
|
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];
|
|
apr_array_header_t arr;
|
|
apr_size_t mark;
|
|
apreq_charset_t charset;
|
|
|
|
if (nlen == 0)
|
|
return APR_EBADARG;
|
|
|
|
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;
|
|
|
|
++nlen, ++vlen;
|
|
e = APR_BRIGADE_FIRST(bb);
|
|
|
|
while (!APR_BUCKET_IS_EOS(e)) {
|
|
struct iovec *iov = apr_array_push(&arr);
|
|
apr_size_t len;
|
|
s = apr_bucket_read(e, (const char **)&iov->iov_base,
|
|
&len, APR_BLOCK_READ);
|
|
if (s != APR_SUCCESS)
|
|
return s;
|
|
|
|
iov->iov_len = len;
|
|
nlen -= len;
|
|
|
|
e = APR_BUCKET_NEXT(e);
|
|
|
|
if (nlen == 0) {
|
|
iov->iov_len--;
|
|
break;
|
|
}
|
|
}
|
|
|
|
mark = arr.nelts;
|
|
|
|
while (!APR_BUCKET_IS_EOS(e)) {
|
|
struct iovec *iov = apr_array_push(&arr);
|
|
apr_size_t len;
|
|
s = apr_bucket_read(e, (const char **)&iov->iov_base,
|
|
&len, APR_BLOCK_READ);
|
|
if (s != APR_SUCCESS)
|
|
return s;
|
|
|
|
iov->iov_len = len;
|
|
vlen -= len;
|
|
|
|
e = APR_BUCKET_NEXT(e);
|
|
|
|
if (vlen == 0) {
|
|
iov->iov_len--;
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
s = apreq_decodev(v->data, &vlen,
|
|
(struct iovec *)arr.elts + mark, arr.nelts - mark);
|
|
if (s != APR_SUCCESS)
|
|
return s;
|
|
|
|
charset = apreq_charset_divine(v->data, vlen);
|
|
|
|
v->name = v->data + vlen + 1;
|
|
v->dlen = vlen;
|
|
|
|
s = apreq_decodev(v->name, &nlen, (struct iovec *)arr.elts, mark);
|
|
if (s != APR_SUCCESS)
|
|
return s;
|
|
|
|
switch (apreq_charset_divine(v->name, nlen)) {
|
|
case APREQ_CHARSET_UTF8:
|
|
if (charset == APREQ_CHARSET_ASCII)
|
|
charset = APREQ_CHARSET_UTF8;
|
|
case APREQ_CHARSET_ASCII:
|
|
break;
|
|
|
|
case APREQ_CHARSET_LATIN1:
|
|
if (charset != APREQ_CHARSET_CP1252)
|
|
charset = APREQ_CHARSET_LATIN1;
|
|
break;
|
|
case APREQ_CHARSET_CP1252:
|
|
charset = APREQ_CHARSET_CP1252;
|
|
}
|
|
|
|
v->nlen = nlen;
|
|
|
|
while ((f = APR_BRIGADE_FIRST(bb)) != e)
|
|
apr_bucket_delete(f);
|
|
|
|
apreq_param_tainted_on(param);
|
|
apreq_param_charset_set(param, charset);
|
|
*p = param;
|
|
return APR_SUCCESS;
|
|
}
|
|
|
|
APREQ_DECLARE_PARSER(apreq_parse_urlencoded)
|
|
{
|
|
apr_pool_t *pool = parser->pool;
|
|
apr_bucket *e;
|
|
struct url_ctx *ctx;
|
|
|
|
if (parser->ctx == NULL) {
|
|
ctx = apr_pcalloc(pool, sizeof *ctx);
|
|
ctx->bb = apr_brigade_create(pool, parser->bucket_alloc);
|
|
parser->ctx = ctx;
|
|
ctx->status = URL_NAME;
|
|
}
|
|
else
|
|
ctx = parser->ctx;
|
|
|
|
PARSER_STATUS_CHECK(URL);
|
|
e = APR_BRIGADE_LAST(ctx->bb);
|
|
APR_BRIGADE_CONCAT(ctx->bb, bb);
|
|
|
|
parse_url_brigade:
|
|
|
|
for (e = APR_BUCKET_NEXT(e);
|
|
e != APR_BRIGADE_SENTINEL(ctx->bb);
|
|
e = APR_BUCKET_NEXT(e))
|
|
{
|
|
apreq_param_t *param;
|
|
apr_size_t off = 0, dlen;
|
|
const char *data;
|
|
apr_status_t s;
|
|
|
|
if (APR_BUCKET_IS_EOS(e)) {
|
|
if (ctx->status == URL_NAME) {
|
|
s = APR_SUCCESS;
|
|
}
|
|
else {
|
|
s = split_urlword(¶m, pool, ctx->bb, ctx->nlen, ctx->vlen);
|
|
if (parser->hook != NULL && s == APR_SUCCESS)
|
|
s = apreq_hook_run(parser->hook, param, NULL);
|
|
|
|
if (s == APR_SUCCESS) {
|
|
apreq_value_table_add(¶m->v, t);
|
|
ctx->status = URL_COMPLETE;
|
|
}
|
|
else {
|
|
ctx->status = URL_ERROR;
|
|
}
|
|
}
|
|
|
|
APR_BRIGADE_CONCAT(bb, ctx->bb);
|
|
return s;
|
|
}
|
|
|
|
s = apr_bucket_read(e, &data, &dlen, APR_BLOCK_READ);
|
|
if ( s != APR_SUCCESS ) {
|
|
ctx->status = URL_ERROR;
|
|
return s;
|
|
}
|
|
|
|
parse_url_bucket:
|
|
|
|
switch (ctx->status) {
|
|
|
|
case URL_NAME:
|
|
while (off < dlen) {
|
|
switch (data[off++]) {
|
|
case '=':
|
|
apr_bucket_split(e, off);
|
|
dlen -= off;
|
|
data += off;
|
|
off = 0;
|
|
e = APR_BUCKET_NEXT(e);
|
|
ctx->status = URL_VALUE;
|
|
goto parse_url_bucket;
|
|
default:
|
|
++ctx->nlen;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case URL_VALUE:
|
|
while (off < dlen) {
|
|
|
|
switch (data[off++]) {
|
|
case '&':
|
|
case ';':
|
|
apr_bucket_split(e, off);
|
|
s = split_urlword(¶m, pool, ctx->bb,
|
|
ctx->nlen, ctx->vlen);
|
|
if (parser->hook != NULL && s == APR_SUCCESS)
|
|
s = apreq_hook_run(parser->hook, param, NULL);
|
|
|
|
if (s != APR_SUCCESS) {
|
|
ctx->status = URL_ERROR;
|
|
return s;
|
|
}
|
|
|
|
apreq_value_table_add(¶m->v, t);
|
|
ctx->status = URL_NAME;
|
|
ctx->nlen = 0;
|
|
ctx->vlen = 0;
|
|
e = APR_BRIGADE_SENTINEL(ctx->bb);
|
|
goto parse_url_brigade;
|
|
|
|
default:
|
|
++ctx->vlen;
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
; /* not reached */
|
|
}
|
|
}
|
|
apreq_brigade_setaside(ctx->bb, pool);
|
|
return APR_INCOMPLETE;
|
|
}
|
|
|
|
|