diff -pru lynx2-8-6.orig/WWW/Library/Implementation/HTString.c lynx2-8-6/WWW/Library/Implementation/HTString.c --- lynx2-8-6.orig/WWW/Library/Implementation/HTString.c Tue May 9 10:10:35 2006 +++ lynx2-8-6/WWW/Library/Implementation/HTString.c Tue May 9 10:12:55 2006 @@ -155,6 +155,38 @@ int strncasecomp(const char *a, return ((long) n < 0 ? 0 : cm[*us1] - cm[*--us2]); } +/* XXX this only handles leading asterisks but is ok for now (SSL) */ +int strcasecomp_asterisk(const char *a, const char *b) +{ + unsigned const char *cm = charmap; + unsigned const char *us1 = a; + unsigned const char *us2 = b; + + if ((*a != '*') && (*b != '*')) + return strcasecomp(a, b); + + if (*b == '*') { + us1 = b; + us2 = a; + } + + if (strlen(us2) < (strlen(us1) - 1)) + return 1; + + while (*++us1 != '\0') + ; + while (*++us2 != '\0') + ; + + while (1) { + if (cm[*us1] != cm[*us2]) + return 1; + if ((*--us1) == '*') + return 0; + --us2; + } +} + #else /* SH_EX */ /* Strings of any length @@ -201,6 +233,36 @@ int strncasecomp(const char *a, return diff; } /*NOTREACHED */ +} + +int strcasecomp_asterisk(const char *a, const char *b) +{ + unsigned const char *us1 = a; + unsigned const char *us2 = b; + + if ((*a != '*') && (*b != '*')) + return strcasecomp(a, b); + + if (*b == '*') { + us1 = us2; + us2 = a; + } + + if (strlen(us2) < (strlen(us1) - 1)) + return 1; + + while (*++us1 != '\0') + ; + while (*++us2 != '\0') + ; + + while (1) { + if (TOLOWER(*us1) != TOLOWER(*us2)) + return 1; + if ((*--us1) == '*') + return 0; + --us2; + } } #endif /* SH_EX */ diff -pru lynx2-8-6.orig/WWW/Library/Implementation/HTString.h lynx2-8-6/WWW/Library/Implementation/HTString.h --- lynx2-8-6.orig/WWW/Library/Implementation/HTString.h Tue May 9 10:10:35 2006 +++ lynx2-8-6/WWW/Library/Implementation/HTString.h Tue May 9 10:11:10 2006 @@ -45,6 +45,8 @@ Case-insensitive string comparison extern int strcasecomp8(const char *a, const char *b); extern int strncasecomp8(const char *a, const char *b, int n); + extern int strcasecomp_asterisk(const char *a, const char *b); + /* * strcasecomp8 and strncasecomp8 are variants of strcasecomp and * strncasecomp, but use 8bit upper/lower case information from the diff -pru lynx2-8-6.orig/WWW/Library/Implementation/HTTP.c lynx2-8-6/WWW/Library/Implementation/HTTP.c --- lynx2-8-6.orig/WWW/Library/Implementation/HTTP.c Tue May 9 10:10:35 2006 +++ lynx2-8-6/WWW/Library/Implementation/HTTP.c Tue May 9 10:14:21 2006 @@ -451,11 +451,14 @@ static int HTLoadHTTP(const char *arg, const char *connect_url = NULL; /* The URL being proxied */ char *connect_host = NULL; /* The host being proxied */ SSL *handle = NULL; /* The SSL handle */ - char ssl_dn[256]; + char ssl_dn[1024]; char *cert_host; char *ssl_host; char *p; char *msg = NULL; + int status_sslcertcheck; + char *ssl_dn_start; + char *ssl_all_cns; #if SSLEAY_VERSION_NUMBER >= 0x0900 BOOL try_tls = TRUE; @@ -621,35 +624,75 @@ static int HTLoadHTTP(const char *arg, X509_NAME_oneline(X509_get_subject_name(SSL_get_peer_certificate(handle)), ssl_dn, sizeof(ssl_dn)); - if ((cert_host = strstr(ssl_dn, "/CN=")) == NULL) { - HTSprintf0(&msg, - gettext("SSL error:Can't find common name in certificate-Continue?")); - if (!HTForcedPrompt(ssl_noprompt, msg, YES)) { - status = HT_NOT_LOADED; - FREE(msg); - goto done; - } - } else { + /* + * X.509 DN validation taking ALL CN fields into account + * (c) 2006 Thorsten Glaser + */ + + /* initialise status information */ + status_sslcertcheck = 0; /* 0 = no CN found in DN */ + ssl_dn_start = ssl_dn; + ssl_all_cns = NULL; + /* get host we're connecting to */ + ssl_host = HTParse(url, "", PARSE_HOST); + /* strip port number */ + if ((p = strchr(ssl_host, ':')) != NULL) + *p = '\0'; + /* validate all CNs found in DN */ + while ((cert_host = strstr(ssl_dn_start, "/CN=")) != NULL) { + status_sslcertcheck = 1; /* 1 = could not verify CN */ + /* start of CommonName */ cert_host += 4; - if ((p = strchr(cert_host, '/')) != NULL) + /* find next part of DistinguishedName */ + if ((p = strchr(cert_host, '/')) != NULL) { *p = '\0'; + ssl_dn_start = p; /* yes this points to the NUL byte */ + } else + ssl_dn_start = NULL; + /* strip port number */ if ((p = strchr(cert_host, ':')) != NULL) *p = '\0'; - ssl_host = HTParse(url, "", PARSE_HOST); - if ((p = strchr(ssl_host, ':')) != NULL) - *p = '\0'; - if (strcasecomp(ssl_host, cert_host)) { + /* verify this CN */ + if (!strcasecomp_asterisk(ssl_host, cert_host)) { + status_sslcertcheck = 2; /* 2 = verified peer */ + /* I think this is cool to have in the logs --mirabilos */ HTSprintf0(&msg, - gettext("SSL error:host(%s)!=cert(%s)-Continue?"), - ssl_host, - cert_host); - if (!HTForcedPrompt(ssl_noprompt, msg, YES)) { - status = HT_NOT_LOADED; - FREE(msg); - goto done; - } + gettext("Verified connection to %s (cert=%s)"), + ssl_host, cert_host); + _HTProgress(msg); + FREE(msg); + /* no need to continue the verification loop */ + break; } + /* add this CN to list of failed CNs */ + if (ssl_all_cns == NULL) { + StrAllocCopy(ssl_all_cns, cert_host); + } else { + StrAllocCat(ssl_all_cns, ":"); + StrAllocCat(ssl_all_cns, cert_host); + } + /* if we cannot retry, don't try it */ + if (ssl_dn_start == NULL) + break; + /* now retry next CN found in DN */ + *ssl_dn_start = '/'; /* formerly NUL byte */ } + /* if an error occured, format the appropriate message */ + if (status_sslcertcheck == 0) + HTSprintf0(&msg, + gettext("SSL error:Can't find common name in certificate-Continue?")); + else if (status_sslcertcheck == 1) + HTSprintf0(&msg, + gettext("SSL error:host(%s)!=cert(%s)-Continue?"), + ssl_host, ssl_all_cns); + /* if an error occured, let the user decide how much he trusts */ + if (status_sslcertcheck < 2) + if (!HTForcedPrompt(ssl_noprompt, msg, YES)) { + status = HT_NOT_LOADED; + FREE(msg); + FREE(ssl_all_cns); + goto done; + } HTSprintf0(&msg, gettext("Secure %d-bit %s (%s) HTTP connection"),