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);