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;
 

Reply via email to