[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

Re: CVE-2016-9179



Back to Debian mailing lists.

Here is a patch to the wheezy version that appears to work for me. Based
on the upstream patches - the relevant parts seem to apply without any
major dramas - although some bits required manual processing.

This is available for testing. At:
https://people.debian.org/~bam/debian/pool/main/l/lynx-cur/


diff -Nru lynx-cur-2.8.8dev.12/debian/changelog lynx-cur-2.8.8dev.12/debian/changelog
--- lynx-cur-2.8.8dev.12/debian/changelog	2016-11-16 18:14:13.000000000 +1100
+++ lynx-cur-2.8.8dev.12/debian/changelog	2016-11-16 17:47:21.000000000 +1100
@@ -1,3 +1,10 @@
+lynx-cur (2.8.8dev.12-2+deb7u1) wheezy-security; urgency=high
+
+  * Non-maintainer upload by the LTS Team.
+  * Fix CVE-2016-9179: invalid URL parsing with '?'.
+
+ -- Brian May <bam@debian.org>  Wed, 16 Nov 2016 17:45:35 +1100
+
 lynx-cur (2.8.8dev.12-2) unstable; urgency=low
 
   * Applied use-dpkg-buildflags.patch which added hardening flags for
diff -Nru lynx-cur-2.8.8dev.12/debian/patches/CVE-2016-9179-1.patch lynx-cur-2.8.8dev.12/debian/patches/CVE-2016-9179-1.patch
--- lynx-cur-2.8.8dev.12/debian/patches/CVE-2016-9179-1.patch	1970-01-01 10:00:00.000000000 +1000
+++ lynx-cur-2.8.8dev.12/debian/patches/CVE-2016-9179-1.patch	2016-11-15 18:03:57.000000000 +1100
@@ -0,0 +1,163 @@
+--- a/WWW/Library/Implementation/HTTP.c
++++ b/WWW/Library/Implementation/HTTP.c
+@@ -415,27 +415,150 @@
+ #endif /* _WINDOWS */
+ 
+ /*
++ * RFC-1738 says we can have user/password using these ASCII characters
++ *    safe           = "$" | "-" | "_" | "." | "+"
++ *    extra          = "!" | "*" | "'" | "(" | ")" | ","
++ *    hex            = digit | "A" | "B" | "C" | "D" | "E" | "F" |
++ *                             "a" | "b" | "c" | "d" | "e" | "f"
++ *    escape         = "%" hex hex
++ *    unreserved     = alpha | digit | safe | extra
++ *    uchar          = unreserved | escape
++ *    user           = *[ uchar | ";" | "?" | "&" | "=" ]
++ *    password       = *[ uchar | ";" | "?" | "&" | "=" ]
++ * and we cannot have a password without user, i.e., no leading ":"
++ * and ":", "@", "/" must be encoded, i.e., will not appear as such.
++ *
++ * However, in a URL
++ *    //<user>:<password>@<host>:<port>/<url-path>
++ * valid characters in the host are different, not allowing most of those
++ * punctuation characters.
++ *
++ * RFC-3986 amends this, using
++ *     userinfo    = *( unreserved / pct-encoded / sub-delims / ":" )
++ *     unreserved    = ALPHA / DIGIT / "-" / "." / "_" / "~"
++ *     reserved      = gen-delims / sub-delims
++ *     gen-delims    = ":" / "/" / "?" / "#" / "[" / "]" / "@"
++ *     sub-delims    = "!" / "$" / "&" / "'" / "(" / ")"
++ *                     / "*" / "+" / "," / ";" / "="
++ * and
++ *     host          = IP-literal / IPv4address / reg-name
++ *     reg-name      = *( unreserved / pct-encoded / sub-delims )
++ */
++#define RFC_3986_UNRESERVED(c) (isalnum(UCH(c)) || strchr("-._~", UCH(c)) != 0)
++#define RFC_3986_GEN_DELIMS(c) ((c) != 0 && strchr(":/?#[]@", UCH(c)) != 0)
++#define RFC_3986_SUB_DELIMS(c) ((c) != 0 && strchr("!$&'()*+,;=", UCH(c)) != 0)
++
++static char *skip_user_passwd(char *host)
++{
++    char *result = 0;
++    char *s = host;
++    int pass = 0;
++    int ch;
++    int last = -1;
++
++    while ((ch = UCH(*s)) != '\0') {
++	if (ch == '\0') {
++	    break;
++	} else if (ch == ':') {
++	    if (pass++)
++		break;
++	} else if (ch == '@') {
++	    if (s != host && last != ':')
++		result = s;
++	    break;
++	} else if (RFC_3986_GEN_DELIMS(ch)) {
++	    if (!RFC_3986_GEN_DELIMS(s[1]))
++		break;
++	} else if (ch == '%') {
++	    if (!(isxdigit(UCH(s[1])) && isxdigit(UCH(s[2]))))
++		break;
++	} else if (!(RFC_3986_UNRESERVED(ch) ||
++		     RFC_3986_SUB_DELIMS(ch))) {
++	    break;
++	}
++	++s;
++	last = ch;
++    }
++    return result;
++}
++
++static char *fake_hostname(char *auth)
++{
++    char *result = NULL;
++    char *colon = NULL;
++
++    StrAllocCopy(result, auth);
++    if ((colon = strchr(result, ':')) != 0)
++	*colon = '\0';
++    if (strchr(result, '.') == 0)
++	FREE(result);
++    return result;
++}
++
++/*
+  * Strip any username from the given string so we retain only the host.
+  */
+ static void strip_userid(char *host)
+ {
+     char *p1 = host;
+-    char *p2 = strchr(host, '@');
+-    char *fake;
++    char *p2 = skip_user_passwd(host);
+ 
+     if (p2 != 0) {
++	char *msg = NULL;
++	char *auth = NULL;
++	char *save = NULL;
++	char *fake = NULL;
++	char *p3 = p2;
++	int gen_delims = 0;
++	int sub_delims = 0;
++	int my_delimit = UCH(*p2);
++	int do_trimming = (my_delimit == '@');
++
+ 	*p2++ = '\0';
+-	if ((fake = HTParse(host, "", PARSE_HOST)) != NULL) {
+-	    char *msg = NULL;
++	StrAllocCopy(auth, host);
+ 
+-	    CTRACE((tfp, "parsed:%s\n", fake));
+-	    HTSprintf0(&msg, gettext("Address contains a username: %s"), host);
+-	    HTAlert(msg);
+-	    FREE(msg);
++	/*
++	 * Trailing "gen-delims" demonstrates that there is no user/password.
++	 */
++	while ((p3 != host) && RFC_3986_GEN_DELIMS(p3[-1])) {
++	    ++gen_delims;
++	    *(--p3) = '\0';
+ 	}
+-	while ((*p1++ = *p2++) != '\0') {
+-	    ;
++	/*
++	 * While legal, punctuation-only user/password is questionable.
++	 */
++	while ((p3 != host) && RFC_3986_SUB_DELIMS(p3[-1])) {
++	    ++sub_delims;
++	    *(--p3) = '\0';
++	}
++	CTRACE((tfp, "trimmed:%s\n", host));
++	StrAllocCopy(save, host);
++
++	if (gen_delims || strcmp(save, auth)) {
++	    HTSprintf0(&msg,
++		       gettext("User/password may appear to be a hostname: '%s' (e.g, '%s')"),
++		       auth, save);
++	    do_trimming = !gen_delims;
++	} else if (*host == '\0' && sub_delims) {
++	    HTSprintf0(&msg,
++		       gettext("User/password contains only punctuation: %s"),
++		       auth);
++	} else if ((fake = fake_hostname(host)) != NULL) {
++	    HTSprintf0(&msg,
++		       gettext("User/password may be confused with hostname: '%s' (e.g, '%s')"),
++		       auth, fake);
++	}
++	if (msg != 0)
++	    HTAlert(msg);
++	if (do_trimming) {
++	    while ((*p1++ = *p2++) != '\0') {
++		;
++	    }
+ 	}
++	FREE(fake);
++	FREE(save);
++	FREE(auth);
++	FREE(msg);
+     }
+ }
+ 
diff -Nru lynx-cur-2.8.8dev.12/debian/patches/CVE-2016-9179-2.patch lynx-cur-2.8.8dev.12/debian/patches/CVE-2016-9179-2.patch
--- lynx-cur-2.8.8dev.12/debian/patches/CVE-2016-9179-2.patch	1970-01-01 10:00:00.000000000 +1000
+++ lynx-cur-2.8.8dev.12/debian/patches/CVE-2016-9179-2.patch	2016-11-16 17:40:59.000000000 +1100
@@ -0,0 +1,77 @@
+--- a/WWW/Library/Implementation/HTTCP.c
++++ b/WWW/Library/Implementation/HTTCP.c
+@@ -1581,7 +1581,6 @@
+     int status = 0;
+     char *line = NULL;
+     char *p1 = NULL;
+-    char *at_sign = NULL;
+     char *host = NULL;
+ 
+ #ifdef INET6
+@@ -1603,14 +1602,8 @@
+      * Get node name and optional port number.
+      */
+     p1 = HTParse(url, "", PARSE_HOST);
+-    if ((at_sign = strchr(p1, '@')) != NULL) {
+-	/*
+-	 * If there's an @ then use the stuff after it as a hostname.
+-	 */
+-	StrAllocCopy(host, (at_sign + 1));
+-    } else {
+-	StrAllocCopy(host, p1);
+-    }
++    StrAllocCopy(host, p1);
++    strip_userid(host, FALSE);
+     FREE(p1);
+ 
+     HTSprintf0(&line, "%s%s", WWW_FIND_MESSAGE, host);
+--- a/WWW/Library/Implementation/HTTP.c
++++ b/WWW/Library/Implementation/HTTP.c
+@@ -498,7 +498,7 @@
+ /*
+  * Strip any username from the given string so we retain only the host.
+  */
+-static void strip_userid(char *host)
++void strip_userid(char *host, int parse_only)
+ {
+     char *p1 = host;
+     char *p2 = skip_user_passwd(host);
+@@ -548,7 +548,7 @@
+ 		       gettext("User/password may be confused with hostname: '%s' (e.g, '%s')"),
+ 		       auth, fake);
+ 	}
+-	if (msg != 0)
++	if (msg != 0 && !parse_only)
+ 	    HTAlert(msg);
+ 	if (do_trimming) {
+ 	    while ((*p1++ = *p2++) != '\0') {
+@@ -1183,7 +1183,7 @@
+ 	char *host = NULL;
+ 
+ 	if ((host = HTParse(anAnchor->address, "", PARSE_HOST)) != NULL) {
+-	    strip_userid(host);
++	    strip_userid(host, TRUE);
+ 	    HTBprintf(&command, "Host: %s%c%c", host, CR, LF);
+ 	    FREE(host);
+ 	}
+--- a/WWW/Library/Implementation/HTUtils.h
++++ b/WWW/Library/Implementation/HTUtils.h
+@@ -778,6 +778,8 @@
+ 
+     extern FILE *TraceFP(void);
+ 
++    extern void strip_userid(char *host, int warn);
++
+ #ifdef USE_SSL
+     extern SSL *HTGetSSLHandle(void);
+     extern void HTSSLInitPRNG(void);
+--- a/src/LYUtils.c
++++ b/src/LYUtils.c
+@@ -4616,6 +4616,7 @@
+      * Do a DNS test on the potential host field as presently trimmed.  - FM
+      */
+     StrAllocCopy(host, Str);
++    strip_userid(host, FALSE);
+     HTUnEscape(host);
+     if (LYCursesON) {
+ 	StrAllocCopy(MsgStr, WWW_FIND_MESSAGE);
diff -Nru lynx-cur-2.8.8dev.12/debian/patches/series lynx-cur-2.8.8dev.12/debian/patches/series
--- lynx-cur-2.8.8dev.12/debian/patches/series	2016-11-16 18:14:13.000000000 +1100
+++ lynx-cur-2.8.8dev.12/debian/patches/series	2016-11-16 17:41:36.000000000 +1100
@@ -1,3 +1,5 @@
 lynxcfg.patch
 aboutlynx.patch
 use-dpkg-buildflags.patch
+CVE-2016-9179-1.patch
+CVE-2016-9179-2.patch
diff -Nru lynx-cur-2.8.8dev.12/debian/source/format lynx-cur-2.8.8dev.12/debian/source/format
--- lynx-cur-2.8.8dev.12/debian/source/format	1970-01-01 10:00:00.000000000 +1000
+++ lynx-cur-2.8.8dev.12/debian/source/format	2016-11-16 17:53:26.000000000 +1100
@@ -0,0 +1 @@
+3.0 (quilt)

-- 
Brian May <bam@debian.org>


Reply to: