File lynx-CVE-2016-9179.patch of Package lynx

Index: lynx2-8-7/WWW/Library/Implementation/HTTP.c
===================================================================
--- lynx2-8-7.orig/WWW/Library/Implementation/HTTP.c	2017-02-06 19:55:12.392985155 +0100
+++ lynx2-8-7/WWW/Library/Implementation/HTTP.c	2017-02-06 20:11:43.034365672 +0100
@@ -379,27 +379,151 @@ int ws_netread(int fd, char *buf, int le
 #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)
+void strip_userid(char *host, int parse_only)
 {
     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);
-	}
-	while ((*p1++ = *p2++) != '\0') {
-	    ;
-	}
+        /*
+         * Trailing "gen-delims" demonstrates that there is no user/password.
+         */
+        while ((p3 != host) && RFC_3986_GEN_DELIMS(p3[-1])) {
+            ++gen_delims;
+            *(--p3) = '\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 && !parse_only)
+            HTAlert(msg);
+        if (do_trimming) {
+            while ((*p1++ = *p2++) != '\0') {
+                ;
+            }
+
+        }
+        FREE(fake);
+        FREE(save);
+        FREE(auth);
+        FREE(msg);
     }
 }
 
@@ -1021,7 +1145,7 @@ static int HTLoadHTTP(const char *arg,
 	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);
 	}
Index: lynx2-8-7/WWW/Library/Implementation/HTTCP.c
===================================================================
--- lynx2-8-7.orig/WWW/Library/Implementation/HTTCP.c	2008-12-15 01:24:56.000000000 +0100
+++ lynx2-8-7/WWW/Library/Implementation/HTTCP.c	2017-02-06 19:57:44.899041404 +0100
@@ -1577,7 +1577,6 @@ int HTDoConnect(const char *url,
     int status = 0;
     char *line = NULL;
     char *p1 = NULL;
-    char *at_sign = NULL;
     char *host = NULL;
 
 #ifdef INET6
@@ -1599,14 +1598,8 @@ int HTDoConnect(const char *url,
      * 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);
Index: lynx2-8-7/WWW/Library/Implementation/HTUtils.h
===================================================================
--- lynx2-8-7.orig/WWW/Library/Implementation/HTUtils.h	2009-05-25 23:05:31.000000000 +0200
+++ lynx2-8-7/WWW/Library/Implementation/HTUtils.h	2017-02-06 19:55:12.404985318 +0100
@@ -747,6 +747,8 @@ extern "C" {
 
     extern FILE *TraceFP(void);
 
+    extern void strip_userid(char *host, int warn);
+
 #ifdef USE_SSL
     extern SSL *HTGetSSLHandle(void);
     extern void HTSSLInitPRNG(void);
Index: lynx2-8-7/src/LYUtils.c
===================================================================
--- lynx2-8-7.orig/src/LYUtils.c	2009-05-26 02:10:12.000000000 +0200
+++ lynx2-8-7/src/LYUtils.c	2017-02-06 19:55:12.404985318 +0100
@@ -4604,6 +4604,7 @@ BOOLEAN LYExpandHostForURL(char **Alloca
      * 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);
openSUSE Build Service is sponsored by