Package: elinks Severity: normal Tags: patch See: http://bugzilla.elinks.cz/show_bug.cgi?id=937 http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2007-5034 https://bugs.launchpad.net/ubuntu/+source/elinks/+bug/141018
Jamie Strandboge -- Email: [EMAIL PROTECTED] IRC (freenode): jdstrand
diff -Nru elinks-0.10.6.orig/src/protocol/http/http.c elinks-0.10.6/src/protocol/http/http.c --- elinks-0.10.6.orig/src/protocol/http/http.c 2005-05-01 18:47:55.000000000 -0400 +++ elinks-0.10.6/src/protocol/http/http.c 2007-09-24 13:08:24.000000000 -0400 @@ -490,7 +490,7 @@ int trace = get_opt_bool("protocol.http.trace"); struct string header; unsigned char *post_data = NULL; - struct auth_entry *entry; + struct auth_entry *entry = NULL; struct uri *uri = conn->proxied_uri; /* Set to the real uri */ unsigned char *optstr; int use_connect, talking_to_proxy; @@ -535,6 +535,11 @@ add_to_string(&header, "TRACE "); } else if (use_connect) { add_to_string(&header, "CONNECT "); + /* In CONNECT requests, we send only a subset of the + * headers to the proxy. See the "CONNECT:" comments + * below. After the CONNECT request succeeds, we + * negotiate TLS with the real server and make a new + * HTTP request that includes all the headers. */ } else if (uri->post) { add_to_string(&header, "POST "); conn->unrestartable = 1; @@ -567,10 +572,14 @@ add_long_to_string(&header, info->sent_version.minor); add_crlf_to_string(&header); + /* CONNECT: Sending a Host header seems pointless as the same + * information is already in the CONNECT line. It's harmless + * though and Mozilla does it too. */ add_to_string(&header, "Host: "); add_uri_to_string(&header, uri, URI_HTTP_HOST); add_crlf_to_string(&header); + /* CONNECT: Proxy-Authorization is intended to be seen by the proxy. */ if (talking_to_proxy) { unsigned char *user = get_opt_str("protocol.http.proxy.user"); unsigned char *passwd = get_opt_str("protocol.http.proxy.passwd"); @@ -618,6 +627,9 @@ } } + /* CONNECT: User-Agent does not reveal anything about the + * resource we're fetching, and it may help the proxy return + * better error messages. */ optstr = get_opt_str("protocol.http.user_agent"); if (*optstr && strcmp(optstr, " ")) { unsigned char *ustr, ts[64] = ""; @@ -643,33 +655,47 @@ add_crlf_to_string(&header); } - switch (get_opt_int("protocol.http.referer.policy")) { - case REFERER_NONE: - /* oh well */ - break; + /* CONNECT: Referer probably is a secret page in the HTTPS + * server, so don't reveal it to the proxy. */ + if (!use_connect) { + switch (get_opt_int("protocol.http.referer.policy")) { + case REFERER_NONE: + /* oh well */ + break; - case REFERER_FAKE: - optstr = get_opt_str("protocol.http.referer.fake"); - if (!optstr[0]) break; - add_to_string(&header, "Referer: "); - add_to_string(&header, optstr); - add_crlf_to_string(&header); - break; + case REFERER_FAKE: + optstr = get_opt_str("protocol.http.referer.fake"); + if (!optstr[0]) break; + add_to_string(&header, "Referer: "); + add_to_string(&header, optstr); + add_crlf_to_string(&header); + break; - case REFERER_TRUE: - if (!conn->referrer) break; - add_to_string(&header, "Referer: "); - add_url_to_http_string(&header, conn->referrer, URI_HTTP_REFERRER); - add_crlf_to_string(&header); - break; + case REFERER_TRUE: + if (!conn->referrer) break; + add_to_string(&header, "Referer: "); + add_url_to_http_string(&header, conn->referrer, URI_HTTP_REFERRER); + add_crlf_to_string(&header); + break; - case REFERER_SAME_URL: - add_to_string(&header, "Referer: "); - add_url_to_http_string(&header, uri, URI_HTTP_REFERRER); - add_crlf_to_string(&header); - break; + case REFERER_SAME_URL: + add_to_string(&header, "Referer: "); + add_url_to_http_string(&header, uri, URI_HTTP_REFERRER); + add_crlf_to_string(&header); + break; + } } + /* CONNECT: Do send all Accept* headers to the CONNECT proxy, + * because they do not reveal anything about the resource + * we're going to request via TLS, and they may affect the + * error message if the CONNECT request fails. + * + * If ELinks is ever changed to vary its Accept headers based + * on what it intends to do with the returned resource, e.g. + * sending "Accept: text/css" when it wants an external + * stylesheet, then it should do that only in the inner GET + * and not in the outer CONNECT. */ add_to_string(&header, "Accept: */*"); add_crlf_to_string(&header); @@ -724,6 +750,11 @@ } #endif + /* CONNECT: Proxy-Connection is intended to be seen by the + * proxy. If the CONNECT request succeeds, then the proxy + * will forward the remainder of the TCP connection to the + * origin server, and Proxy-Connection does not matter; but + * if the request fails, then Proxy-Connection may matter. */ /* FIXME: What about post-HTTP/1.1?? --Zas */ if (HTTP_1_1(info->sent_version)) { if (!IS_PROXY_URI(conn->uri)) { @@ -740,7 +771,9 @@ add_crlf_to_string(&header); } - if (conn->cached) { + /* CONNECT: Do not tell the proxy anything we have cached + * about the resource. */ + if (!use_connect && conn->cached) { if (!conn->cached->incomplete && conn->cached->head && conn->cached->last_modified && conn->cache_mode <= CACHE_MODE_CHECK_IF_MODIFIED) { add_to_string(&header, "If-Modified-Since: "); @@ -749,6 +782,8 @@ } } + /* CONNECT: Let's send cache control headers to the proxy too; + * they may affect DNS caching. */ if (conn->cache_mode >= CACHE_MODE_FORCE_RELOAD) { add_to_string(&header, "Pragma: no-cache"); add_crlf_to_string(&header); @@ -756,7 +791,9 @@ add_crlf_to_string(&header); } - if (conn->from || (conn->progress.start > 0)) { + /* CONNECT: Do not reveal byte ranges to the proxy. It can't + * do anything good with that information anyway. */ + if (!use_connect && (conn->from || (conn->progress.start > 0))) { /* conn->from takes precedence. conn->progress.start is set only the first * time, then conn->from gets updated and in case of any retries * etc we have everything interesting in conn->from already. */ @@ -766,7 +803,10 @@ add_crlf_to_string(&header); } - entry = find_auth(uri); + /* CONNECT: The Authorization header is for the origin server only. */ + if (!use_connect) { + entry = find_auth(uri); + } if (entry) { if (entry->digest) { unsigned char *response; @@ -806,7 +846,8 @@ } } - if (uri->post) { + /* CONNECT: Any POST data is for the origin server only. */ + if (!use_connect && uri->post) { /* We search for first '\n' in uri->post to get content type * as set by get_form_uri(). This '\n' is dropped if any * and replaced by correct '\r\n' termination here. */ @@ -825,7 +866,8 @@ } #ifdef CONFIG_COOKIES - { + /* CONNECT: Cookies are for the origin server only. */ + if (!use_connect) { struct string *cookies = send_cookies(uri); if (cookies) { @@ -839,12 +881,17 @@ add_crlf_to_string(&header); + /* CONNECT: Any POST data is for the origin server only. + * This was already checked above and post_data is NULL + * in that case. Verified with an assertion below. */ if (post_data) { #define POST_BUFFER_SIZE 4096 unsigned char *post = post_data; unsigned char buffer[POST_BUFFER_SIZE]; int n = 0; + assert(!use_connect); /* see comment above */ + while (post[0] && post[1]) { int h1, h2;
diff -Nru elinks-0.11.1.orig/src/protocol/http/http.c elinks-0.11.1/src/protocol/http/http.c --- elinks-0.11.1.orig/src/protocol/http/http.c 2006-01-29 08:10:39.000000000 -0500 +++ elinks-0.11.1/src/protocol/http/http.c 2007-09-24 09:51:28.000000000 -0400 @@ -551,7 +551,7 @@ int trace = get_opt_bool("protocol.http.trace"); struct string header; unsigned char *post_data = NULL; - struct auth_entry *entry; + struct auth_entry *entry = NULL; struct uri *uri = conn->proxied_uri; /* Set to the real uri */ unsigned char *optstr; int use_connect, talking_to_proxy; @@ -577,6 +577,11 @@ add_to_string(&header, "TRACE "); } else if (use_connect) { add_to_string(&header, "CONNECT "); + /* In CONNECT requests, we send only a subset of the + * headers to the proxy. See the "CONNECT:" comments + * below. After the CONNECT request succeeds, we + * negotiate TLS with the real server and make a new + * HTTP request that includes all the headers. */ } else if (uri->post) { add_to_string(&header, "POST "); conn->unrestartable = 1; @@ -609,10 +614,14 @@ add_long_to_string(&header, http->sent_version.minor); add_crlf_to_string(&header); + /* CONNECT: Sending a Host header seems pointless as the same + * information is already in the CONNECT line. It's harmless + * though and Mozilla does it too. */ add_to_string(&header, "Host: "); add_uri_to_string(&header, uri, URI_HTTP_HOST); add_crlf_to_string(&header); + /* CONNECT: Proxy-Authorization is intended to be seen by the proxy. */ if (talking_to_proxy) { unsigned char *user = get_opt_str("protocol.http.proxy.user"); unsigned char *passwd = get_opt_str("protocol.http.proxy.passwd"); @@ -660,6 +669,9 @@ } } + /* CONNECT: User-Agent does not reveal anything about the + * resource we're fetching, and it may help the proxy return + * better error messages. */ optstr = get_opt_str("protocol.http.user_agent"); if (*optstr && strcmp(optstr, " ")) { unsigned char *ustr, ts[64] = ""; @@ -685,33 +697,47 @@ add_crlf_to_string(&header); } - switch (get_opt_int("protocol.http.referer.policy")) { - case REFERER_NONE: - /* oh well */ - break; + /* CONNECT: Referer probably is a secret page in the HTTPS + * server, so don't reveal it to the proxy. */ + if (!use_connect) { + switch (get_opt_int("protocol.http.referer.policy")) { + case REFERER_NONE: + /* oh well */ + break; - case REFERER_FAKE: - optstr = get_opt_str("protocol.http.referer.fake"); - if (!optstr[0]) break; - add_to_string(&header, "Referer: "); - add_to_string(&header, optstr); - add_crlf_to_string(&header); - break; + case REFERER_FAKE: + optstr = get_opt_str("protocol.http.referer.fake"); + if (!optstr[0]) break; + add_to_string(&header, "Referer: "); + add_to_string(&header, optstr); + add_crlf_to_string(&header); + break; - case REFERER_TRUE: - if (!conn->referrer) break; - add_to_string(&header, "Referer: "); - add_url_to_http_string(&header, conn->referrer, URI_HTTP_REFERRER); - add_crlf_to_string(&header); - break; + case REFERER_TRUE: + if (!conn->referrer) break; + add_to_string(&header, "Referer: "); + add_url_to_http_string(&header, conn->referrer, URI_HTTP_REFERRER); + add_crlf_to_string(&header); + break; - case REFERER_SAME_URL: - add_to_string(&header, "Referer: "); - add_url_to_http_string(&header, uri, URI_HTTP_REFERRER); - add_crlf_to_string(&header); - break; + case REFERER_SAME_URL: + add_to_string(&header, "Referer: "); + add_url_to_http_string(&header, uri, URI_HTTP_REFERRER); + add_crlf_to_string(&header); + break; + } } + /* CONNECT: Do send all Accept* headers to the CONNECT proxy, + * because they do not reveal anything about the resource + * we're going to request via TLS, and they may affect the + * error message if the CONNECT request fails. + * + * If ELinks is ever changed to vary its Accept headers based + * on what it intends to do with the returned resource, e.g. + * sending "Accept: text/css" when it wants an external + * stylesheet, then it should do that only in the inner GET + * and not in the outer CONNECT. */ add_to_string(&header, "Accept: */*"); add_crlf_to_string(&header); @@ -766,6 +792,11 @@ } #endif + /* CONNECT: Proxy-Connection is intended to be seen by the + * proxy. If the CONNECT request succeeds, then the proxy + * will forward the remainder of the TCP connection to the + * origin server, and Proxy-Connection does not matter; but + * if the request fails, then Proxy-Connection may matter. */ /* FIXME: What about post-HTTP/1.1?? --Zas */ if (HTTP_1_1(http->sent_version)) { if (!IS_PROXY_URI(conn->uri)) { @@ -782,7 +813,9 @@ add_crlf_to_string(&header); } - if (conn->cached) { + /* CONNECT: Do not tell the proxy anything we have cached + * about the resource. */ + if (!use_connect && conn->cached) { if (!conn->cached->incomplete && conn->cached->head && conn->cached->last_modified && conn->cache_mode <= CACHE_MODE_CHECK_IF_MODIFIED) { add_to_string(&header, "If-Modified-Since: "); @@ -791,6 +824,8 @@ } } + /* CONNECT: Let's send cache control headers to the proxy too; + * they may affect DNS caching. */ if (conn->cache_mode >= CACHE_MODE_FORCE_RELOAD) { add_to_string(&header, "Pragma: no-cache"); add_crlf_to_string(&header); @@ -798,7 +833,9 @@ add_crlf_to_string(&header); } - if (conn->from || conn->progress->start > 0) { + /* CONNECT: Do not reveal byte ranges to the proxy. It can't + * do anything good with that information anyway. */ + if (!use_connect && (conn->from || conn->progress->start > 0)) { /* conn->from takes precedence. conn->progress.start is set only the first * time, then conn->from gets updated and in case of any retries * etc we have everything interesting in conn->from already. */ @@ -808,7 +845,10 @@ add_crlf_to_string(&header); } - entry = find_auth(uri); + /* CONNECT: The Authorization header is for the origin server only. */ + if (!use_connect) { + entry = find_auth(uri); + } if (entry) { if (entry->digest) { unsigned char *response; @@ -848,7 +888,8 @@ } } - if (uri->post) { + /* CONNECT: Any POST data is for the origin server only. */ + if (!use_connect && uri->post) { /* We search for first '\n' in uri->post to get content type * as set by get_form_uri(). This '\n' is dropped if any * and replaced by correct '\r\n' termination here. */ @@ -867,7 +908,8 @@ } #ifdef CONFIG_COOKIES - { + /* CONNECT: Cookies are for the origin server only. */ + if (!use_connect) { struct string *cookies = send_cookies(uri); if (cookies) { @@ -881,12 +923,17 @@ add_crlf_to_string(&header); + /* CONNECT: Any POST data is for the origin server only. + * This was already checked above and post_data is NULL + * in that case. Verified with an assertion below. */ if (post_data) { #define POST_BUFFER_SIZE 4096 unsigned char *post = post_data; unsigned char buffer[POST_BUFFER_SIZE]; int n = 0; + assert(!use_connect); /* see comment above */ + while (post[0] && post[1]) { int h1, h2;