Thomas Dickey dixit:
>On Wed, Mar 29, 2006 at 08:23:45PM +0000, Thorsten Glaser wrote:
>> Hi people,
>>
>> to add to all the traffic on the list... I've implemented full
>> SSL certificate validation taking into account wildcard certificates
>> (only if the wildcard is the first character, I feel it's more secure
>> this way) and multiple CNs in the DN (as employed by e.g. cacert.org).
>But if I
>see a patch, I'll add it to my to-do list...
It did take me a while *blush* but here you are... reviewed on
a DEC VT420 ;) and tested via ssh onto a GNU/Linux box (and of
course, weeks of testing as part of the MirOS BSD base system).
I have, by no means, validated whether the code allows "more
than it should", but it does what it promises: verify against
hosts with more than one CommonName in the DistinguishedName,
such as cacert.org, and handle leading asterisks in certifi-
cates well (I did not implement middle asterisks for securi-
ty reasons).
>>*) http://mirbsd.mirsolutions.de/cvs.cgi/src/etc/ssl.certs.shar?rev=HEAD
>> Please feel free to use them. These are the certificates from MSIE 5
>> on Win2k, some Netscape, plus CAcert.org; old or invalid certificates
>> removed or (when applicable, e.g. Thawte Root Rollover) updated. I do
>> of course not warrant they're correct, but that's the "standard set"
>> trusted by "the others" too.
Did anyone look at this?
bye,
//mirabile
--
> emacs als auch vi zum Kotzen finde (joe rules) und pine für den einzig
> bedienbaren textmode-mailclient halte (und ich hab sie alle ausprobiert). ;)
Hallooooo, ich bin der Holger ("Hallo Holger!"), und ich bin ebenfalls
... pine-User, und das auch noch gewohnheitsmäßig ("Oooooooohhh"). [aus dasr]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 <[EMAIL PROTECTED]>
+ */
+
+ /* 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"),
_______________________________________________
Lynx-dev mailing list
[email protected]
http://lists.nongnu.org/mailman/listinfo/lynx-dev