diff options
Diffstat (limited to '')
-rw-r--r-- | modules/proxy/mod_proxy_uwsgi.c | 148 |
1 files changed, 99 insertions, 49 deletions
diff --git a/modules/proxy/mod_proxy_uwsgi.c b/modules/proxy/mod_proxy_uwsgi.c index c5d4f8e..4e57196 100644 --- a/modules/proxy/mod_proxy_uwsgi.c +++ b/modules/proxy/mod_proxy_uwsgi.c @@ -84,10 +84,29 @@ static int uwsgi_canon(request_rec *r, char *url) host = apr_pstrcat(r->pool, "[", host, "]", NULL); } - path = ap_proxy_canonenc(r->pool, url, strlen(url), enc_path, 0, - r->proxyreq); - if (!path) { - return HTTP_BAD_REQUEST; + if (apr_table_get(r->notes, "proxy-nocanon") + || apr_table_get(r->notes, "proxy-noencode")) { + path = url; /* this is the raw/encoded path */ + } + else { + core_dir_config *d = ap_get_core_module_config(r->per_dir_config); + int flags = d->allow_encoded_slashes && !d->decode_encoded_slashes ? PROXY_CANONENC_NOENCODEDSLASHENCODING : 0; + + path = ap_proxy_canonenc_ex(r->pool, url, strlen(url), enc_path, flags, + r->proxyreq); + if (!path) { + return HTTP_BAD_REQUEST; + } + } + /* + * If we have a raw control character or a ' ' in nocanon path, + * correct encoding was missed. + */ + if (path == url && *ap_scan_vchar_obstext(path)) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(10417) + "To be forwarded path contains control " + "characters or spaces"); + return HTTP_FORBIDDEN; } r->filename = @@ -136,7 +155,7 @@ static int uwsgi_send_headers(request_rec *r, proxy_conn_rec * conn) int j; apr_size_t headerlen = 4; - apr_uint16_t pktsize, keylen, vallen; + apr_size_t pktsize, keylen, vallen; const char *script_name; const char *path_info; const char *auth; @@ -175,7 +194,16 @@ static int uwsgi_send_headers(request_rec *r, proxy_conn_rec * conn) env = (apr_table_entry_t *) env_table->elts; for (j = 0; j < env_table->nelts; ++j) { - headerlen += 2 + strlen(env[j].key) + 2 + strlen(env[j].val); + headerlen += 2 + strlen(env[j].key) + 2 + (env[j].val ? strlen(env[j].val) : 0); + } + + pktsize = headerlen - 4; + if (pktsize > APR_UINT16_MAX) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(10259) + "can't send headers to %s:%u: packet size too " + "large (%" APR_SIZE_T_FMT ")", + conn->hostname, conn->port, pktsize); + return HTTP_INTERNAL_SERVER_ERROR; } ptr = buf = apr_palloc(r->pool, headerlen); @@ -189,15 +217,15 @@ static int uwsgi_send_headers(request_rec *r, proxy_conn_rec * conn) memcpy(ptr, env[j].key, keylen); ptr += keylen; - vallen = strlen(env[j].val); + vallen = env[j].val ? strlen(env[j].val) : 0; *ptr++ = (apr_byte_t) (vallen & 0xff); *ptr++ = (apr_byte_t) ((vallen >> 8) & 0xff); - memcpy(ptr, env[j].val, vallen); + if (env[j].val) { + memcpy(ptr, env[j].val, vallen); + } ptr += vallen; } - pktsize = headerlen - 4; - buf[0] = 0; buf[1] = (apr_byte_t) (pktsize & 0xff); buf[2] = (apr_byte_t) ((pktsize >> 8) & 0xff); @@ -238,6 +266,7 @@ static request_rec *make_fake_req(conn_rec *c, request_rec *r) request_rec *rp; apr_pool_create(&pool, c->pool); + apr_pool_tag(pool, "proxy_uwsgi_rp"); rp = apr_pcalloc(pool, sizeof(*r)); @@ -297,18 +326,16 @@ static int uwsgi_response(request_rec *r, proxy_conn_rec * backend, pass_bb = apr_brigade_create(r->pool, c->bucket_alloc); len = ap_getline(buffer, sizeof(buffer), rp, 1); - if (len <= 0) { - /* oops */ + /* invalid or empty */ return HTTP_INTERNAL_SERVER_ERROR; } - backend->worker->s->read += len; - - if (len >= sizeof(buffer) - 1) { - /* oops */ + if ((apr_size_t)len >= sizeof(buffer)) { + /* too long */ return HTTP_INTERNAL_SERVER_ERROR; } + /* Position of http status code */ if (apr_date_checkmask(buffer, "HTTP/#.# ###*")) { status_start = 9; @@ -317,8 +344,8 @@ static int uwsgi_response(request_rec *r, proxy_conn_rec * backend, status_start = 7; } else { - /* oops */ - return HTTP_INTERNAL_SERVER_ERROR; + /* not HTTP */ + return HTTP_BAD_GATEWAY; } status_end = status_start + 3; @@ -338,21 +365,50 @@ static int uwsgi_response(request_rec *r, proxy_conn_rec * backend, } r->status_line = apr_pstrdup(r->pool, &buffer[status_start]); - /* start parsing headers */ + /* parse headers */ while ((len = ap_getline(buffer, sizeof(buffer), rp, 1)) > 0) { + if ((apr_size_t)len >= sizeof(buffer)) { + /* too long */ + len = -1; + break; + } value = strchr(buffer, ':'); - /* invalid header skip */ - if (!value) - continue; - *value = '\0'; - ++value; + if (!value) { + /* invalid header */ + len = -1; + break; + } + *value++ = '\0'; + if (*ap_scan_http_token(buffer)) { + /* invalid name */ + len = -1; + break; + } while (apr_isspace(*value)) ++value; for (end = &value[strlen(value) - 1]; end > value && apr_isspace(*end); --end) *end = '\0'; + if (*ap_scan_http_field_content(value)) { + /* invalid value */ + len = -1; + break; + } apr_table_add(r->headers_out, buffer, value); } + if (len < 0) { + /* Reset headers, but not to NULL because things below the chain expect + * this to be non NULL e.g. the ap_content_length_filter. + */ + r->headers_out = apr_table_make(r->pool, 1); + return HTTP_BAD_GATEWAY; + } + + /* T-E wins over C-L */ + if (apr_table_get(r->headers_out, "Transfer-Encoding")) { + apr_table_unset(r->headers_out, "Content-Length"); + backend->close = 1; + } if ((buf = apr_table_get(r->headers_out, "Content-Type"))) { ap_set_content_type(r, apr_pstrdup(r->pool, buf)); @@ -362,9 +418,9 @@ static int uwsgi_response(request_rec *r, proxy_conn_rec * backend, #if AP_MODULE_MAGIC_AT_LEAST(20101106,0) dconf = ap_get_module_config(r->per_dir_config, &proxy_module); - if (dconf->error_override && ap_is_HTTP_ERROR(r->status)) { + if (ap_proxy_should_override(dconf, r->status)) { #else - if (conf->error_override && ap_is_HTTP_ERROR(r->status)) { + if (ap_proxy_should_override(conf, r->status)) { #endif int status = r->status; r->status = HTTP_OK; @@ -446,11 +502,8 @@ static int uwsgi_handler(request_rec *r, proxy_worker * worker, const char *proxyname, apr_port_t proxyport) { int status; - int delta = 0; - int decode_status; proxy_conn_rec *backend = NULL; apr_pool_t *p = r->pool; - size_t w_len; char server_portstr[32]; char *u_path_info; apr_uri_t *uri; @@ -462,24 +515,23 @@ static int uwsgi_handler(request_rec *r, proxy_worker * worker, uri = apr_palloc(r->pool, sizeof(*uri)); - /* ADD PATH_INFO */ -#if AP_MODULE_MAGIC_AT_LEAST(20111130,0) - w_len = strlen(worker->s->name); -#else - w_len = strlen(worker->name); -#endif - u_path_info = r->filename + 6 + w_len; - if (u_path_info[0] != '/') { - delta = 1; + /* ADD PATH_INFO (unescaped) */ + u_path_info = ap_strchr(url + sizeof(UWSGI_SCHEME) + 2, '/'); + if (!u_path_info) { + u_path_info = apr_pstrdup(r->pool, "/"); } - decode_status = ap_unescape_url(url + w_len - delta); - if (decode_status) { + else if (ap_unescape_url(u_path_info) != OK) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(10100) - "unable to decode uri: %s", url + w_len - delta); + "unable to decode uwsgi uri: %s", url); return HTTP_INTERNAL_SERVER_ERROR; } - apr_table_add(r->subprocess_env, "PATH_INFO", url + w_len - delta); - + else { + /* Remove duplicate slashes at the beginning of PATH_INFO */ + while (u_path_info[1] == '/') { + u_path_info++; + } + } + apr_table_add(r->subprocess_env, "PATH_INFO", u_path_info); /* Create space for state information */ status = ap_proxy_acquire_connection(UWSGI_SCHEME, &backend, worker, @@ -509,12 +561,10 @@ static int uwsgi_handler(request_rec *r, proxy_worker * worker, } /* Step Three: Create conn_rec */ - if (!backend->connection) { - if ((status = ap_proxy_connection_create(UWSGI_SCHEME, backend, - r->connection, - r->server)) != OK) - goto cleanup; - } + if ((status = ap_proxy_connection_create(UWSGI_SCHEME, backend, + r->connection, + r->server)) != OK) + goto cleanup; /* Step Four: Process the Request */ if (((status = ap_setup_client_block(r, REQUEST_CHUNKED_ERROR)) != OK) |