Commit 35318b83 authored by William A. Rowe Jr's avatar William A. Rowe Jr
Browse files

Resume building the appropriate patches for relevant 2.2.x -> 2.4.x changes

to allow us to apply the 2.4.x already-reviewed HttpProtocolOptions and
parser changes.



git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/branches/2.2.x-merge-http-strict@1775749 13f79535-47bb-0310-9956-ffa450edef68
parents dab40449 bda8d18b
Loading
Loading
Loading
Loading
+4 −6
Original line number Diff line number Diff line
                                                         -*- coding: utf-8 -*-
Changes with Apache 2.2.32

  *) Core: reject NULLs in request line or request headers.
     PR 43039 [Nick Kew]

  *) core: Enforce LimitRequestFieldSize after multiple headers with the same
     name have been merged. [Stefan Fritsch]

Changes with Apache 2.2.31

  *) Correct win32 build issues for mod_proxy exports, OpenSSL 1.0.x headers.
     [Yann Ylavic, Gregg Smith]

  *) core: Do not over allocate memory within 'ap_rgetline_core' for
     the common case. [Christophe Jaillet]

  *) Core: reject NULLs in request line or request headers.
     PR 43039 [Nick Kew]

Changes with Apache 2.2.30 (not released)

  *) SECURITY: CVE-2015-3183 (cve.mitre.org)
+87 −44
Original line number Diff line number Diff line
@@ -183,6 +183,9 @@ AP_DECLARE(apr_time_t) ap_rationalize_mtime(request_rec *r, apr_time_t mtime)
    return (mtime > now) ? now : mtime;
}

/* Min # of bytes to allocate when reading a request line */
#define MIN_LINE_ALLOC 80

/* Get a line of protocol input, including any continuation lines
 * caused by MIME folding (or broken clients) if fold != 0, and place it
 * in the buffer s, of size n bytes, without the ending newline.
@@ -219,8 +222,9 @@ AP_DECLARE(apr_status_t) ap_rgetline_core(char **s, apr_size_t n,
     * against APR_ASCII_LF at the end of the loop if bb only contains
     * zero-length buckets.
     */
    if (last_char)
    if (last_char) {
        *last_char = '\0';
    }

    for (;;) {
        apr_brigade_cleanup(bb);
@@ -281,6 +285,9 @@ AP_DECLARE(apr_status_t) ap_rgetline_core(char **s, apr_size_t n,
                /* We'll assume the common case where one bucket is enough. */
                if (!*s) {
                    current_alloc = len;
                    if (current_alloc < MIN_LINE_ALLOC) {
                        current_alloc = MIN_LINE_ALLOC;
                    }
                    *s = apr_palloc(r->pool, current_alloc);
                }
                else if (bytes_handled + len > current_alloc) {
@@ -553,10 +560,7 @@ static int read_request_line(request_rec *r, apr_bucket_brigade *bb)
    const char *uri;
    const char *pro;

#if 0
    conn_rec *conn = r->connection;
#endif
    int major = 1, minor = 0;   /* Assume HTTP/1.0 if non-"HTTP" protocol */
    unsigned int major = 1, minor = 0;   /* Assume HTTP/1.0 if non-"HTTP" protocol */
    char http[5];
    apr_size_t len;
    int num_blank_lines = 0;
@@ -598,13 +602,13 @@ static int read_request_line(request_rec *r, apr_bucket_brigade *bb)
             * buffer before finding the end-of-line.  This is only going to
             * happen if it exceeds the configured limit for a request-line.
             */
            if (rv == APR_ENOSPC) {
            if (APR_STATUS_IS_ENOSPC(rv)) {
                r->status    = HTTP_REQUEST_URI_TOO_LARGE;
            }
            else if (APR_STATUS_IS_TIMEUP(rv)) {
                r->status = HTTP_REQUEST_TIME_OUT;
            }
            else if (rv == APR_EINVAL) {
            else if (APR_STATUS_IS_EINVAL(rv)) {
                r->status = HTTP_BAD_REQUEST;
            }
            r->proto_num = HTTP_VERSION(1,0);
@@ -613,20 +617,10 @@ static int read_request_line(request_rec *r, apr_bucket_brigade *bb)
        }
    } while ((len <= 0) && (++num_blank_lines < max_blank_lines));

    /* we've probably got something to do, ignore graceful restart requests */

    r->request_time = apr_time_now();
    ll = r->the_request;
    r->method = ap_getword_white(r->pool, &ll);

#if 0
/* XXX If we want to keep track of the Method, the protocol module should do
 * it.  That support isn't in the scoreboard yet.  Hopefully next week
 * sometime.   rbb */
    ap_update_connection_status(AP_CHILD_THREAD_FROM_ID(conn->id), "Method",
                                r->method);
#endif

    uri = ap_getword_white(r->pool, &ll);

    /* Provide quick information about the request method as soon as known */
@@ -649,8 +643,6 @@ static int read_request_line(request_rec *r, apr_bucket_brigade *bb)
    }
    r->protocol = apr_pstrmemdup(r->pool, pro, len);

    /* XXX ap_update_connection_status(conn->id, "Protocol", r->protocol); */

    /* Avoid sscanf in the common case */
    if (len == 8
        && pro[0] == 'H' && pro[1] == 'T' && pro[2] == 'T' && pro[3] == 'P'
@@ -678,6 +670,35 @@ static int field_name_len(const char *field)
    return end - field;
}

static int table_do_fn_check_lengths(void *r_, const char *key,
                                     const char *value)
{
    request_rec *r = r_;
    if (value == NULL || r->server->limit_req_fieldsize >= strlen(value) )
        return 1;

    r->status = HTTP_BAD_REQUEST;
    apr_table_setn(r->notes, "error-notes",
                   apr_pstrcat(r->pool, "Size of a request header field "
                               "after merging exceeds server limit.<br />"
                               "\n<pre>\n",
                               ap_escape_html(r->pool, key),
                               "</pre>\n", NULL));
    ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, "Request header exceeds "
                  "LimitRequestFieldSize after merging: %s", key);
    return 0;
}

/* get the length of the field name for logging, but no more than 80 bytes */
#define LOG_NAME_MAX_LEN 80
static int field_name_len(const char *field)
{
    const char *end = ap_strchr_c(field, ':');
    if (end == NULL || end - field > LOG_NAME_MAX_LEN)
        return LOG_NAME_MAX_LEN;
    return end - field;
}

AP_DECLARE(void) ap_get_mime_headers_core(request_rec *r, apr_bucket_brigade *bb)
{
    char *last_field = NULL;
@@ -715,16 +736,29 @@ AP_DECLARE(void) ap_get_mime_headers_core(request_rec *r, apr_bucket_brigade *bb
             * finding the end-of-line.  This is only going to happen if it
             * exceeds the configured limit for a field size.
             */
            if (rv == APR_ENOSPC && field) {
            if (rv == APR_ENOSPC) {
                const char *field_escaped;
                if (field) {
                    /* ensure ap_escape_html will terminate correctly */
                    field[len - 1] = '\0';
                    field_escaped = ap_escape_html(r->pool, field);
                }
                else {
                    field_escaped = field = "";
                }

                apr_table_setn(r->notes, "error-notes",
                               apr_psprintf(r->pool,
                                           "Size of a request header field "
                                           "exceeds server limit.<br />\n"
                                           "<pre>\n%.*s\n</pre>/n",
                                           field_name_len(field), 
                                           ap_escape_html(r->pool, field)));
                                           field_name_len(field_escaped),
                                           field_escaped));
                ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, 
                              "Request header exceeds LimitRequestFieldSize%s"
                              "%.*s",
                              *field ? ": " : "",
                              field_name_len(field), field);
                ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
                              "Request header exceeds LimitRequestFieldSize: "
                              "%.*s", field_name_len(field), field);
@@ -759,6 +793,10 @@ AP_DECLARE(void) ap_get_mime_headers_core(request_rec *r, apr_bucket_brigade *bb
                                  "Request header exceeds LimitRequestFieldSize "
                                  "after folding: %.*s",
                                  field_name_len(last_field), last_field);
                    ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
                                  "Request header exceeds LimitRequestFieldSize "
                                  "after folding: %.*s",
                                  field_name_len(last_field), last_field);
                    return;
                }

@@ -784,6 +822,9 @@ AP_DECLARE(void) ap_get_mime_headers_core(request_rec *r, apr_bucket_brigade *bb
                    apr_table_setn(r->notes, "error-notes",
                                   "The number of request header fields "
                                   "exceeds this server's limit.");
                    ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
                                  "Number of request headers exceeds "
                                  "LimitRequestFields");
                    return;
                }

@@ -802,6 +843,10 @@ AP_DECLARE(void) ap_get_mime_headers_core(request_rec *r, apr_bucket_brigade *bb
                                  "separator: %.*s", (int)LOG_NAME_MAX_LEN,
                                  last_field);

                    ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
                                  "Request header field is missing ':' "
                                  "separator: %.*s", (int)LOG_NAME_MAX_LEN,
                                  last_field);
                    return;
                }

@@ -857,6 +902,9 @@ AP_DECLARE(void) ap_get_mime_headers_core(request_rec *r, apr_bucket_brigade *bb
     * field-name, following RFC 2616, 4.2.
     */
    apr_table_compress(r->headers_in, APR_OVERLAP_TABLES_MERGE);

    /* enforce LimitRequestFieldSize for merged headers */
    apr_table_do(table_do_fn_check_lengths, r, r->headers_in, NULL);
}

AP_DECLARE(void) ap_get_mime_headers(request_rec *r)
@@ -872,7 +920,7 @@ request_rec *ap_read_request(conn_rec *conn)
    request_rec *r;
    apr_pool_t *p;
    const char *expect;
    int access_status;
    int access_status = HTTP_OK;
    apr_bucket_brigade *tmp_bb;
    apr_socket_t *csd;
    apr_interval_time_t cur_timeout;
@@ -926,13 +974,14 @@ request_rec *ap_read_request(conn_rec *conn)
    if (!read_request_line(r, tmp_bb)) {
        if (r->status == HTTP_REQUEST_URI_TOO_LARGE
            || r->status == HTTP_BAD_REQUEST) {
            if (r->status == HTTP_BAD_REQUEST) {
                ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
                              "request failed: invalid characters in URI");
            if (r->status == HTTP_REQUEST_URI_TOO_LARGE) {
                ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
                              "request failed: client's request-line exceeds LimitRequestLine (longer than %d)",
                              r->server->limit_req_line);
            }
            else {
                ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
                              "request failed: URI too long (longer than %d)", r->server->limit_req_line);
            else if (r->method == NULL) {
                ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
                              "request failed: invalid characters in URI");
            }
            ap_send_error_response(r, 0);
            ap_update_child_status(conn->sbh, SERVER_BUSY_LOG, r);
@@ -969,7 +1018,7 @@ request_rec *ap_read_request(conn_rec *conn)

        ap_get_mime_headers_core(r, tmp_bb);
        if (r->status != HTTP_OK) {
            ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
            ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
                          "request failed: error reading the headers");
            ap_send_error_response(r, 0);
            ap_update_child_status(conn->sbh, SERVER_BUSY_LOG, r);
@@ -1016,7 +1065,7 @@ request_rec *ap_read_request(conn_rec *conn)
             * headers! Have to dink things just to make sure the error message
             * comes through...
             */
            ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
            ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
                          "client sent invalid HTTP/0.9 request: HEAD %s",
                          r->uri);
            r->header_only = 0;
@@ -1057,8 +1106,8 @@ request_rec *ap_read_request(conn_rec *conn)
         * HTTP/1.1 mentions twice (S9, S14.23) that a request MUST contain
         * a Host: header, and the server MUST respond with 400 if it doesn't.
         */
        r->status = HTTP_BAD_REQUEST;
        ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
        access_status = HTTP_BAD_REQUEST;
        ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
                      "client sent HTTP/1.1 request without hostname "
                      "(see RFC2616 section 14.23): %s", r->uri);
    }
@@ -1073,14 +1122,8 @@ request_rec *ap_read_request(conn_rec *conn)
    ap_add_input_filter_handle(ap_http_input_filter_handle,
                               NULL, r, r->connection);

    if (r->status != HTTP_OK) {
        ap_send_error_response(r, 0);
        ap_update_child_status(conn->sbh, SERVER_BUSY_LOG, r);
        ap_run_log_transaction(r);
        return r;
    }

    if ((access_status = ap_run_post_read_request(r))) {
    if (access_status != HTTP_OK
        || (access_status = ap_run_post_read_request(r))) {
        ap_die(access_status, r);
        ap_update_child_status(conn->sbh, SERVER_BUSY_LOG, r);
        ap_run_log_transaction(r);
@@ -1280,7 +1323,7 @@ AP_DECLARE(int) ap_get_basic_auth_pw(request_rec *r, const char **pw)

    if (strcasecmp(ap_getword(r->pool, &auth_line, ' '), "Basic")) {
        /* Client tried to authenticate using wrong auth scheme */
        ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
        ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
                      "client used wrong authentication scheme: %s", r->uri);
        ap_note_basic_auth_failure(r);
        return HTTP_UNAUTHORIZED;