AST-2014-001: Stack overflow in HTTP processing of Cookie headers.
authorRichard Mudgett <rmudgett@digium.com>
Mon, 10 Mar 2014 17:21:01 +0000 (17:21 +0000)
committerRichard Mudgett <rmudgett@digium.com>
Mon, 10 Mar 2014 17:21:01 +0000 (17:21 +0000)
Sending a HTTP request that is handled by Asterisk with a large number of
Cookie headers could overflow the stack.

Another vulnerability along similar lines is any HTTP request with a
ridiculous number of headers in the request could exhaust system memory.

(closes issue ASTERISK-23340)
Reported by: Lucas Molas, researcher at Programa STIC, Fundacion; and Dr. Manuel Sadosky, Buenos Aires, Argentina
........

Merged revisions 410380 from http://svn.asterisk.org/svn/asterisk/branches/1.8
........

Merged revisions 410381 from http://svn.asterisk.org/svn/asterisk/branches/11
........

Merged revisions 410383 from http://svn.asterisk.org/svn/asterisk/branches/12

git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@410395 65c4cc65-6c06-0410-ace0-fbb531ad65f3

main/http.c

index e867de1..0e45b60 100644 (file)
@@ -197,9 +197,7 @@ uint32_t ast_http_manid_from_vars(struct ast_variable *headers)
                        break;
                }
        }
-       if (cookies) {
-               ast_variables_destroy(cookies);
-       }
+       ast_variables_destroy(cookies);
        return mngid;
 }
 
@@ -1083,12 +1081,13 @@ static int ssl_close(void *cookie)
 }*/
 #endif /* DO_SSL */
 
-static struct ast_variable *parse_cookies(char *cookies)
+static struct ast_variable *parse_cookies(const char *cookies)
 {
+       char *parse = ast_strdupa(cookies);
        char *cur;
        struct ast_variable *vars = NULL, *var;
 
-       while ((cur = strsep(&cookies, ";"))) {
+       while ((cur = strsep(&parse, ";"))) {
                char *name, *val;
 
                name = val = cur;
@@ -1118,16 +1117,12 @@ static struct ast_variable *parse_cookies(char *cookies)
 /* get cookie from Request headers */
 struct ast_variable *ast_http_get_cookies(struct ast_variable *headers)
 {
-       struct ast_variable *v, *cookies=NULL;
+       struct ast_variable *v, *cookies = NULL;
 
        for (v = headers; v; v = v->next) {
                if (!strcasecmp(v->name, "Cookie")) {
-                       char *tmp = ast_strdupa(v->value);
-                       if (cookies) {
-                               ast_variables_destroy(cookies);
-                       }
-
-                       cookies = parse_cookies(tmp);
+                       ast_variables_destroy(cookies);
+                       cookies = parse_cookies(v->value);
                }
        }
        return cookies;
@@ -1226,6 +1221,9 @@ struct ast_http_auth *ast_http_get_auth(struct ast_variable *headers)
        return NULL;
 }
 
+/*! Limit the number of request headers in case the sender is being ridiculous. */
+#define MAX_HTTP_REQUEST_HEADERS       100
+
 static void *httpd_helper_thread(void *data)
 {
        char buf[4096];
@@ -1236,6 +1234,7 @@ static void *httpd_helper_thread(void *data)
        char *uri, *method;
        enum ast_http_method http_method = AST_HTTP_UNKNOWN;
        const char *transfer_encoding;
+       int remaining_headers;
 
        if (ast_atomic_fetchadd_int(&session_count, +1) >= session_limit) {
                goto done;
@@ -1274,9 +1273,13 @@ static void *httpd_helper_thread(void *data)
                if (*c) {
                        *c = '\0';
                }
+       } else {
+               ast_http_error(ser, 400, "Bad Request", "Invalid Request");
+               goto done;
        }
 
        /* process "Request Headers" lines */
+       remaining_headers = MAX_HTTP_REQUEST_HEADERS;
        while (fgets(header_line, sizeof(header_line), ser->f)) {
                char *name, *value;
 
@@ -1299,6 +1302,11 @@ static void *httpd_helper_thread(void *data)
 
                ast_trim_blanks(name);
 
+               if (!remaining_headers--) {
+                       /* Too many headers. */
+                       ast_http_error(ser, 413, "Request Entity Too Large", "Too many headers");
+                       goto done;
+               }
                if (!headers) {
                        headers = ast_variable_new(name, value, __FILE__);
                        tail = headers;
@@ -1306,6 +1314,17 @@ static void *httpd_helper_thread(void *data)
                        tail->next = ast_variable_new(name, value, __FILE__);
                        tail = tail->next;
                }
+               if (!tail) {
+                       /*
+                        * Variable allocation failure.
+                        * Try to make some room.
+                        */
+                       ast_variables_destroy(headers);
+                       headers = NULL;
+
+                       ast_http_error(ser, 500, "Server Error", "Out of memory");
+                       goto done;
+               }
        }
 
        transfer_encoding = get_transfer_encoding(headers);
@@ -1325,20 +1344,13 @@ static void *httpd_helper_thread(void *data)
                goto done;
        }
 
-       if (!*uri) {
-               ast_http_error(ser, 400, "Bad Request", "Invalid Request");
-               goto done;
-       }
-
        handle_uri(ser, uri, http_method, headers);
 
 done:
        ast_atomic_fetchadd_int(&session_count, -1);
 
        /* clean up all the header information */
-       if (headers) {
-               ast_variables_destroy(headers);
-       }
+       ast_variables_destroy(headers);
 
        if (ser->f) {
                fclose(ser->f);