#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;
}
return ((int)(unsigned int) x) - ((int)(unsigned int) y);
}
+#endif
//============================================================
bool TcpBuf::empty () {
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) {
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) {