From 7f4aa0e1d70e230c19027d539db54dfa8a469e64 Mon Sep 17 00:00:00 2001 From: David Rose Date: Fri, 9 Oct 2009 18:50:20 +0000 Subject: [PATCH] support wildcard certs --- panda/src/downloader/httpChannel.cxx | 141 +++++++++++++++++++++------ panda/src/downloader/httpChannel.h | 2 + panda/src/express/openSSLWrapper.h | 1 + 3 files changed, 114 insertions(+), 30 deletions(-) diff --git a/panda/src/downloader/httpChannel.cxx b/panda/src/downloader/httpChannel.cxx index 544f27f147..a97a47fe47 100644 --- a/panda/src/downloader/httpChannel.cxx +++ b/panda/src/downloader/httpChannel.cxx @@ -21,6 +21,7 @@ #include "config_downloader.h" #include "virtualFileMountHTTP.h" #include "ramfile.h" +#include "globPattern.h" #include @@ -1669,36 +1670,7 @@ run_ssl_handshake() { if (_client->get_verify_ssl() != HTTPClient::VS_no_verify) { // Check that the server is someone we expected to be talking // to. - string common_name = get_x509_name_component(subject, NID_commonName); - string hostname = _request.get_url().get_server(); - - /* - X509_STORE *store = OpenSSLWrapper::get_global_ptr()->get_x509_store(); - - // Create the X509_STORE_CTX for verifying the cert. - X509_STORE_CTX *ctx = X509_STORE_CTX_new(); - X509_STORE_CTX_init(ctx, store, cert, NULL); - X509_STORE_CTX_set_cert(ctx, cert); - - if (!X509_verify_cert(ctx)) { - int verify_result = X509_STORE_CTX_get_error(ctx); - downloader_cat.info() - << "Server certificate from " << hostname - << " is invalid: " << verify_result << "\n"; - _status_entry._status_code = SC_ssl_invalid_server_certificate; - } - _state = S_failure; - return false; - } - - X509_STORE_CTX_cleanup(ctx); - X509_STORE_CTX_free(ctx); - */ - - if (common_name != hostname) { - downloader_cat.info() - << "Server certificate from " << hostname - << " provides wrong name: " << common_name << "\n"; + if (!validate_server_name(cert)) { _status_entry._status_code = SC_ssl_unexpected_server; _state = S_failure; return false; @@ -3341,6 +3313,115 @@ certificate signing */ +//////////////////////////////////////////////////////////////////// +// Function: HTTPChannel::validate_server_name +// Access: Private +// Description: Returns true if the name in the cert matches the +// hostname of the server, false otherwise. +//////////////////////////////////////////////////////////////////// +bool HTTPChannel:: +validate_server_name(X509 *cert) { + string hostname = _request.get_url().get_server(); + + vector_string cert_names; + + // According to RFC 2818, we should check the DNS name(s) in the + // subjectAltName extension first, if that extension exists. + GENERAL_NAMES *subject_alt_names = + (GENERAL_NAMES *)X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL); + if (subject_alt_names != NULL) { + int num_alts = sk_GENERAL_NAME_num(subject_alt_names); + for (int i = 0; i < num_alts; ++i) { + // Get the ith alt name. + const GENERAL_NAME *alt_name = + sk_GENERAL_NAME_value(subject_alt_names, i); + + if (alt_name->type == GEN_DNS) { + char *buffer = NULL; + ASN1_STRING_to_UTF8((unsigned char**)&buffer, + alt_name->d.ia5); + cert_names.push_back(buffer); + OPENSSL_free(buffer); + } + } + } + + if (cert_names.empty()) { + // If there were no DNS names, use the common name instead. + + X509_NAME *xname = X509_get_subject_name(cert); + if (xname != NULL) { + string common_name = get_x509_name_component(xname, NID_commonName); + cert_names.push_back(common_name); + } + } + + if (cert_names.empty()) { + downloader_cat.info() + << "Server certificate from " << hostname + << " provides no name.\n"; + return false; + } + + if (downloader_cat.is_debug()) { + downloader_cat.debug() + << "Server certificate from " << hostname + << " provides name(s):"; + vector_string::const_iterator si; + for (si = cert_names.begin(); si != cert_names.end(); ++si) { + const string &cert_name = (*si); + downloader_cat.debug(false) + << " " << cert_name; + } + downloader_cat.debug(false) + << "\n"; + } + + // Now validate the names we found. If any of them matches, the + // cert matches. + vector_string::const_iterator si; + for (si = cert_names.begin(); si != cert_names.end(); ++si) { + const string &cert_name = (*si); + + if (match_cert_name(cert_name, hostname)) { + return true; + } + } + + downloader_cat.info() + << "Server certificate from " << hostname + << " provides wrong name(s):"; + for (si = cert_names.begin(); si != cert_names.end(); ++si) { + const string &cert_name = (*si); + downloader_cat.info(false) + << " " << cert_name; + } + downloader_cat.info(false) + << "\n"; + + return false; +} + +//////////////////////////////////////////////////////////////////// +// Function: HTTPChannel::match_cert_name +// Access: Private, Static +// Description: Returns true if this particular name from the +// certificate matches the indicated hostname, false +// otherwise. +//////////////////////////////////////////////////////////////////// +bool HTTPChannel:: +match_cert_name(const string &cert_name, const string &hostname) { + // We use GlobPattern to match the name. This isn't quite + // consistent with RFC2818, since it also accepts additional + // wildcard characters like "?" and "[]", but I think it's close + // enough. + + GlobPattern pattern(cert_name); + pattern.set_case_sensitive(false); + pattern.set_nomatch_chars("."); + return pattern.matches(hostname); +} + //////////////////////////////////////////////////////////////////// // Function: HTTPChannel::get_x509_name_component // Access: Private, Static diff --git a/panda/src/downloader/httpChannel.h b/panda/src/downloader/httpChannel.h index ade07a2137..689f8856ea 100644 --- a/panda/src/downloader/httpChannel.h +++ b/panda/src/downloader/httpChannel.h @@ -249,6 +249,8 @@ private: void check_socket(); + bool validate_server_name(X509 *cert); + static bool match_cert_name(const string &cert_name, const string &hostname); static string get_x509_name_component(X509_NAME *name, int nid); static bool x509_name_subset(X509_NAME *name_a, X509_NAME *name_b); diff --git a/panda/src/express/openSSLWrapper.h b/panda/src/express/openSSLWrapper.h index 66d5960439..9045842138 100644 --- a/panda/src/express/openSSLWrapper.h +++ b/panda/src/express/openSSLWrapper.h @@ -26,6 +26,7 @@ #include "openssl/rand.h" #include "openssl/err.h" #include "openssl/x509.h" +#include "openssl/x509v3.h" // Windows may define this macro inappropriately. #ifdef X509_NAME