OSDN Git Service

update the verification of the certificate.
authorvisor <visor@users.sourceforge.jp>
Tue, 23 Jul 2013 15:21:30 +0000 (00:21 +0900)
committervisor <visor@users.sourceforge.jp>
Tue, 23 Jul 2013 15:21:30 +0000 (00:21 +0900)
lib/util_tcp.cc
lib/util_tcp.h

index b9214bf..6f371cb 100644 (file)
@@ -2,6 +2,7 @@
 #include "config.h"
 #include "util_const.h"
 #include "util_check.h"
+#include "util_string.h"
 #include "ustring.h"
 #include <boost/regex.hpp>
 #include <sys/uio.h>
 #include <string.h>
 #include <unistd.h>
 #include <iostream>
+#include <locale>
 
 #define SSL_NAME_LEN   256
 
+#if 0
 static int  case_diffs (const char *s, const char *t) {
     unsigned char  x;
     unsigned char  y;
@@ -37,6 +40,7 @@ static int  case_diffs (const char *s, const char *t) {
     }
     return ((int)(unsigned int) x) - ((int)(unsigned int) y);
 }
+#endif
 
 //============================================================
 bool  TcpBuf::empty () {
@@ -389,8 +393,9 @@ void  SslClient::loadCAFile (const char* certfile, int depth) {
 
 bool  SslClient::verifyCA () {
     X509_autoptr  cert;
-    char  buf[SSL_NAME_LEN];
     long  rc;
+    ustring  lhost;
+    bool  ans;
 
     if ((rc = SSL_get_verify_result (ssl.get ())) != X509_V_OK) {
        switch (rc) {
@@ -404,25 +409,82 @@ bool  SslClient::verifyCA () {
     cert.reset (SSL_get_peer_certificate (ssl.get ()));
     if (!cert.get ())
        return false;
-    X509_NAME_get_text_by_NID (X509_get_subject_name (cert.get ()), NID_commonName, buf, sizeof (buf));
-
-    if (host && host->host.length () > 0) {
-//     std::cerr << "hostname: " << buf << "\n";
-//     std::cerr << "hostname: " << host->host << "\n";
-       buf[SSL_NAME_LEN - 1] = 0;
-       if (buf[0] == '*' && buf[1] == '.') {
-           size_t  n1 = strlen (buf);
-           size_t  n2 = host->host.length ();
-//         std::cerr << "hostname: " << buf + 1<< "\n";
-//         std::cerr << "hostname: " << host->host.c_str () + n2 - n1 + 1 << "\n";
-           if (n2 <= n1 || case_diffs (host->host.c_str () + n2 - n1 + 1, buf + 1) != 0)
-               return false;
+
+    lhost.resize (host->host.length ());
+    std::transform (host->host.begin (), host->host.end (), lhost.begin (), ::tolower);
+    ans = verifyCN (lhost, cert.get ()) || verifyAltName (lhost, cert.get ());
+    if (! ans) {
+       std::cerr << lhost << ": hostname not match\n";
+    }
+    return ans;
+}
+
+bool  SslClient::verifyCN (const ustring& lhost, X509* cert) {
+    char  buf[SSL_NAME_LEN];
+    long  rc;
+    int  len;
+    ustring  cn;
+
+    len = X509_NAME_get_text_by_NID (X509_get_subject_name (cert), NID_commonName, buf, sizeof (buf));
+
+    if (len > 0 && lhost.length () > 0) {
+       if (len >= sizeof (buf)) {
+           std::cerr << "Too long common name of the server certificate\n";
+           return false;
+       }
+       cn.assign (buf, len);
+       std::transform (cn.begin (), cn.end (), cn.begin (), ::tolower);
+       return globMatch (lhost, cn);
+    }
+    return false;
+}
+
+bool  SslClient::verifyAltName (const ustring& lhost, X509* cert) {
+    int  pos;
+    X509_EXTENSION*  ext = NULL;
+    GENERAL_NAMES*  names;
+    GENERAL_NAME*  name;
+    int  i, n;
+    unsigned char*  dns;
+
+    pos = X509_get_ext_by_NID (cert, NID_subject_alt_name, -1);
+    if (pos >= 0) {
+       ext = X509_get_ext (cert, pos);
+       if (ext) {
+           names = (GENERAL_NAMES*)X509V3_EXT_d2i (ext);
+           n = sk_GENERAL_NAME_num (names);
+           for (i = 0; i < n; ++ i) {
+               name = sk_GENERAL_NAME_value (names, i);
+               if (name->type == GEN_DNS) {
+                   ASN1_STRING_to_UTF8 (&dns, name->d.dNSName);
+//                 std::cerr << "dns:" << dns << "\n";
+                   ustring  name (char_type (dns));
+                   std::transform (name.begin (), name.end (), name.begin (), ::tolower);
+                   if (globMatch (lhost, ustring (char_type (dns)))) {
+                       OPENSSL_free (dns);
+                       return true;
+                   } else {
+                       OPENSSL_free (dns);
+                   }
+               }
+           }
+       }
+    }
+    return false;
+}
+
+bool  SslClient::globMatch (const ustring& lhost, const ustring& name) {
+    if (matchHead (name, CharConst ("*."))) {
+       // XXX RFC2459(= HTTP over TLS)では、*は一つのサブドメインのみにマッチする。
+       ustring::size_type  p = lhost.find ('.');
+       if (p != ustring::npos) {
+           return ustring (lhost.begin () + p, lhost.end ()) == ustring (name.begin () + 1, name.end ());
        } else {
-           if (case_diffs (host->host.c_str (), buf) != 0)
-               return false;
+           return false;
        }
+    } else {
+       return lhost == name;
     }
-    return true;
 }
 
 ssize_t  SslClient::write2 (struct iovec* iov, int iovcnt) {
index 918aecf..c6a0c9f 100644 (file)
@@ -10,6 +10,7 @@
 #include <sys/socket.h>
 #include <openssl/crypto.h>
 #include <openssl/x509.h>
+#include <openssl/x509v3.h>
 #include <openssl/pem.h>
 #include <openssl/ssl.h>
 #include <openssl/err.h>
@@ -206,6 +207,9 @@ class  SslClient: public TcpClient {
     virtual bool  sslOpen ();
     virtual void  loadCAFile (const char* certfile, int depth = 5);
     virtual bool  verifyCA ();
+    virtual bool  verifyCN (const ustring& lhost, X509* cert);
+    virtual bool  verifyAltName (const ustring& lhost, X509* cert);
+    virtual bool  globMatch (const ustring& lhost, const ustring& name);
     virtual ssize_t  write2 (struct iovec* iov, int iovcnt);
     virtual ssize_t  read2 (void* buf, size_t nbytes);
 };