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

Reply via email to