From e4c2a8a4d4fa263c43a598a2358f49c390cae947 Mon Sep 17 00:00:00 2001 From: Nathan Angelacos Date: Tue, 10 Jun 2014 22:08:04 +0000 Subject: Use a generic "METHOD_body=" variable for unencoded body (e.g. json POST request) --- ChangeLog | 10 ++++ src/haserl.c | 136 +++++++++++++++++++++++++++++---------------------- src/sliding_buffer.c | 57 +++++++++++---------- 3 files changed, 118 insertions(+), 85 deletions(-) diff --git a/ChangeLog b/ChangeLog index 75c1066..a5a4bf3 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,13 @@ +2014-06-07 + 0.9.33 + * Fix various security vulnerabilities - most serious is a + Heap Overflow Vulnerability in sliding_buffer.c + Reported by Stephen Röttger + * Allow PUT and DELETE method (But prefix is still POST/GET) + * On POST/PUT, Content-Type is not x-www-urlencoded, then + the body of the message is stored verbatim in POST_body= + + 2013-20-09 0.9.32 * Regression causing Lua always to be linked, never used diff --git a/src/haserl.c b/src/haserl.c index 6b346c0..6514604 100644 --- a/src/haserl.c +++ b/src/haserl.c @@ -1,5 +1,5 @@ /* -------------------------------------------------------------------------- - * Copyright 2003-2011 (inclusive) Nathan Angelacos + * Copyright 2003-2014 (inclusive) Nathan Angelacos * (nangel@users.sourceforge.net) * * This file is part of haserl. @@ -461,92 +461,108 @@ ReadCGIQueryString (list_t * env) */ int -ReadCGIPOSTValues (list_t * env) +ReadCGIPOSTValues (list_t * env ) { size_t content_length = 0; size_t max_len; + int urldecoding = 0; + char *matchstr = ""; size_t i, j, x; sliding_buffer_t sbuf; buffer_t token; unsigned char *data; const char *CONTENT_LENGTH = "CONTENT_LENGTH"; + const char *CONTENT_TYPE = "CONTENT_TYPE"; if ((getenv (CONTENT_LENGTH) == NULL) || (strtoul (getenv (CONTENT_LENGTH), NULL, 10) == 0)) return (0); - if (getenv ("CONTENT_TYPE")) - { - if (strncasecmp (getenv ("CONTENT_TYPE"), "multipart/form-data", 19) - == 0) - { + if (strncasecmp (getenv (CONTENT_TYPE) , "multipart/form-data", 19) + == 0) + { /* This is a mime request, we need to go to the mime handler */ i = rfc2388_handler (env); return (i); - } - } + } - s_buffer_init (&sbuf, 32768); - sbuf.fh = STDIN; - if (getenv (CONTENT_LENGTH)) - { - sbuf.maxread = strtoul (getenv (CONTENT_LENGTH), NULL, 10); - } - haserl_buffer_init (&token); + /* at this point its either urlencoded or some other blob */ + + if ( strncasecmp (getenv (CONTENT_TYPE), "x-www-form-urlencoded", 21) || + (getenv (CONTENT_TYPE) == NULL ) ) { + urldecoding = 1; + matchstr = "&"; + } /* Allow 2MB content, unless they have a global upload set */ max_len = ((global.uploadkb == 0) ? 2048 : global.uploadkb) *1024; - do - { - /* x is true if this token ends with a matchstr or is at the end of stream */ - x = s_buffer_read (&sbuf, "&"); - content_length += sbuf.len; - if (content_length > max_len) - { - die_with_message (NULL, NULL, - "Attempted to send content larger than allowed limits."); - } - - if ((x == 0) || (token.data)) - { - buffer_add (&token, (char *) sbuf.segment, sbuf.len); - } + s_buffer_init (&sbuf, 32768); + sbuf.fh = STDIN; - if (x) - { - data = sbuf.segment; - sbuf.segment[sbuf.len] = '\0'; - if (token.data) + if (getenv (CONTENT_LENGTH)) { - /* add the ASCIIZ */ - buffer_add (&token, sbuf.segment + sbuf.len, 1); - data = token.data; + sbuf.maxread = strtoul (getenv (CONTENT_LENGTH), NULL, 10); } + haserl_buffer_init (&token); + + if ( urldecoding == 0 ) { + buffer_add( &token, "body=", 5 ); + } - /* change plusses into spaces */ - j = strlen ((char *) data); - for (i = 0; i <= j; i++) + + do { - if (data[i] == '+') + /* x is true if this token ends with a matchstr or is at the end of stream */ + x = s_buffer_read (&sbuf, matchstr); + content_length += sbuf.len; + if (content_length > max_len) { - data[i] = ' '; + die_with_message (NULL, NULL, + "Attempted to send content larger than allowed limits."); + } + + if ((x == 0) || (token.data)) + { + buffer_add (&token, (char *) sbuf.segment, sbuf.len); + } + + if (x) + { + data = sbuf.segment; + sbuf.segment[sbuf.len] = '\0'; + if (token.data) + { + /* add the ASCIIZ */ + buffer_add (&token, sbuf.segment + sbuf.len, 1); + data = token.data; + } + + if (urldecoding) { + /* change plusses into spaces */ + j = strlen ((char *) data); + for (i = 0; i <= j; i++) + { + if (data[i] == '+') + { + data[i] = ' '; + } + } + unescape_url ((char *) data); + } + myputenv (env, (char *) data, global.var_prefix); + myputenv (env, (char *) data, global.post_prefix); + if (token.data) + { + buffer_reset (&token); + } } } - unescape_url ((char *) data); - myputenv (env, (char *) data, global.var_prefix); - myputenv (env, (char *) data, global.post_prefix); - if (token.data) - { - buffer_reset (&token); - } - } - } - while (!sbuf.eof); - s_buffer_destroy (&sbuf); - buffer_destroy (&token); - return (0); + while (!sbuf.eof); + s_buffer_destroy (&sbuf); + buffer_destroy (&token); + return (0); } @@ -867,14 +883,16 @@ main (int argc, char *argv[]) CookieVars (env); if (getenv ("REQUEST_METHOD")) { - if (strcasecmp (getenv ("REQUEST_METHOD"), "GET") == 0) + if ( (strcasecmp (getenv ("REQUEST_METHOD"), "GET") == 0) || + (strcasecmp (getenv ("REQUEST_METHOD"), "DELETE") == 0) ) { if (global.acceptall == TRUE) ReadCGIPOSTValues (env); ReadCGIQueryString (env); } - if (strcasecmp (getenv ("REQUEST_METHOD"), "POST") == 0) + if ( (strcasecmp (getenv ("REQUEST_METHOD"), "POST") == 0) || + (strcasecmp (getenv ("REQUEST_METHOD"), "PUT") == 0) ) { if (global.acceptall == TRUE) ReadCGIQueryString (env); diff --git a/src/sliding_buffer.c b/src/sliding_buffer.c index 1adade5..70d64a5 100644 --- a/src/sliding_buffer.c +++ b/src/sliding_buffer.c @@ -129,32 +129,37 @@ s_buffer_read (sliding_buffer_t * sbuf, char *matchstr) */ pos = 0; len = sbuf->bufsize - (int) (sbuf->ptr - sbuf->buf) - strlen (matchstr); - /* On a short read or very long matchstr, its possible to force len < 0 - That is bad. */ - if ( len < 0 ) - { - die_with_message ( NULL, NULL, "Short Read or MIME decode failure" ); - } - while (memcmp (matchstr, sbuf->ptr + pos, strlen (matchstr)) && (pos < len)) - { - pos++; - } - - /* - * if we found it - */ - if (pos < len) - { - sbuf->len = pos; - sbuf->segment = sbuf->ptr; - sbuf->ptr = sbuf->segment + pos + strlen (matchstr); - return -1; - } - - if (sbuf->eof) - { - len += strlen (matchstr); - } - + /* a malicious client can send a matchstr longer than the actual content body + do not allow reads beyond the buffer limits + */ + len = ( len < 0 ) ? 0 : len; + + /* if have a matchstr, look for it, otherwise return the chunk */ + if ( strlen(matchstr) > 0 ) { + + while (memcmp (matchstr, sbuf->ptr + pos, strlen (matchstr)) && (pos < len)) + { + pos++; + } + + /* + * if we found it + */ + if (pos < len) + { + sbuf->len = pos; + sbuf->segment = sbuf->ptr; + sbuf->ptr = sbuf->segment + pos + strlen (matchstr); + return -1; + } + + + if (sbuf->eof) + { + len += strlen (matchstr); + } + + } /* * ran off the end, didn't find the matchstr */ -- cgit v1.2.3