support wildcard certs

This commit is contained in:
David Rose 2009-10-09 18:50:20 +00:00
parent f4107d22ac
commit 7f4aa0e1d7
3 changed files with 114 additions and 30 deletions

View File

@ -21,6 +21,7 @@
#include "config_downloader.h"
#include "virtualFileMountHTTP.h"
#include "ramfile.h"
#include "globPattern.h"
#include <stdio.h>
@ -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

View File

@ -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);

View File

@ -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