compile in ca-bundle.crt

This commit is contained in:
David Rose 2009-10-08 02:37:20 +00:00
parent e2c1394007
commit e6e862b35f
12 changed files with 5301 additions and 166 deletions

View File

@ -122,12 +122,4 @@ egg-object-type-glow <Scalar> blend { add }
# maya2egg itself. So if it appears in an egg file, it means nothing.
egg-object-type-keep-all-uvsets
# This names the ca-bundle.crt file in its standard installation dir.
# This is useful for validating https servers correctly. It is also
# used when packaging up the p3dcert application for publish.
#define install_dir $[$[upcase $[PACKAGE]]_INSTALL]
#define install_data_dir $[or $[INSTALL_DATA_DIR],$[install_dir]/shared]
ca-bundle-filename $[install_data_dir]/ca-bundle.crt
#end 20_panda.prc

View File

@ -106,14 +106,3 @@
#define IGATESCAN all
#end lib_target
// This is a handy file for identifying public certificate authorities
// (and thus almost any public https server). It was taken from the
// OpenSSL distribution; if you need a fresher copy, go get a new one
// from the latest OpenSSL. To use this file, point the variable
// ssl-certificates in your Configrc file to its installed location,
// e.g:
//
// ssl-certificates /usr/local/panda/install/shared/ca-bundle.crt
//
#define INSTALL_DATA ca-bundle.crt

View File

@ -1594,7 +1594,7 @@ run_ssl_handshake() {
_bio->set_bio(_sbio);
_sbio = NULL;
// Now verify the server is who we expect it to be.
// Now verify the server certificate is valid.
long verify_result = SSL_get_verify_result(ssl);
if (verify_result == X509_V_ERR_CERT_HAS_EXPIRED) {
downloader_cat.info()
@ -1614,6 +1614,16 @@ run_ssl_handshake() {
return false;
}
} else if (verify_result == X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT ||
verify_result == X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN) {
downloader_cat.info()
<< "Self-signed certificate from " << _request.get_url().get_server_and_port() << "\n";
if (_client->get_verify_ssl() == HTTPClient::VS_normal) {
_status_entry._status_code = SC_ssl_self_signed_server_certificate;
_state = S_failure;
return false;
}
} else if (verify_result != X509_V_OK) {
downloader_cat.info()
<< "Unable to verify identity of " << _request.get_url().get_server_and_port()
@ -1630,8 +1640,7 @@ run_ssl_handshake() {
downloader_cat.info()
<< "No certificate was presented by server.\n";
// This shouldn't be possible, per the SSL specs.
if (_client->get_verify_ssl() != HTTPClient::VS_no_verify ||
!_client->_expected_servers.empty()) {
if (_client->get_verify_ssl() != HTTPClient::VS_no_verify) {
_status_entry._status_code = SC_ssl_invalid_server_certificate;
_state = S_failure;
return false;
@ -1660,9 +1669,36 @@ run_ssl_handshake() {
if (_client->get_verify_ssl() != HTTPClient::VS_no_verify) {
// Check that the server is someone we expected to be talking
// to.
if (!verify_server(subject)) {
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 does not match any expected server.\n";
<< "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";
_status_entry._status_code = SC_ssl_unexpected_server;
_state = S_failure;
return false;
@ -3127,63 +3163,6 @@ check_socket() {
}
}
////////////////////////////////////////////////////////////////////
// Function: HTTPChannel::verify_server
// Access: Private
// Description: Returns true if the indicated server matches one of
// our expected servers (or the list of expected servers
// is empty), or false if it does not match any of our
// expected servers.
////////////////////////////////////////////////////////////////////
bool HTTPChannel::
verify_server(X509_NAME *subject) const {
if (_client->_expected_servers.empty()) {
if (downloader_cat.is_debug()) {
downloader_cat.debug()
<< "No expected servers on list; allowing any https connection.\n";
}
return true;
}
if (downloader_cat.is_debug()) {
downloader_cat.debug() << "checking server: " << flush;
X509_NAME_print_ex_fp(stderr, subject, 0, 0);
fflush(stderr);
downloader_cat.debug(false) << "\n";
}
HTTPClient::ExpectedServers::const_iterator ei;
for (ei = _client->_expected_servers.begin();
ei != _client->_expected_servers.end();
++ei) {
X509_NAME *expected_name = (*ei);
if (x509_name_subset(expected_name, subject)) {
if (downloader_cat.is_debug()) {
downloader_cat.debug()
<< "Match found!\n";
}
return true;
}
}
// None of the lines matched.
if (downloader_cat.is_debug()) {
downloader_cat.debug()
<< "No match found against any of the following expected servers:\n";
for (ei = _client->_expected_servers.begin();
ei != _client->_expected_servers.end();
++ei) {
X509_NAME *expected_name = (*ei);
X509_NAME_print_ex_fp(stderr, expected_name, 0, 0);
fputs("\n", stderr);
}
fflush(stderr);
}
return false;
}
/*
Certificate verify error codes:

View File

@ -88,6 +88,7 @@ PUBLISHED:
SC_http_error_watermark,
SC_ssl_invalid_server_certificate,
SC_ssl_self_signed_server_certificate,
SC_ssl_unexpected_server,
// These errors are only generated after a download_to_*() call
@ -247,7 +248,6 @@ private:
bool parse_content_range(const string &content_range);
void check_socket();
bool verify_server(X509_NAME *subject) const;
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

@ -199,16 +199,6 @@ operator = (const HTTPClient &copy) {
_verify_ssl = copy._verify_ssl;
_usernames = copy._usernames;
_cookies = copy._cookies;
clear_expected_servers();
ExpectedServers::const_iterator ei;
for (ei = copy._expected_servers.begin();
ei != copy._expected_servers.end();
++ei) {
X509_NAME *orig_name = (*ei);
X509_NAME *new_name = X509_NAME_dup(orig_name);
_expected_servers.push_back(new_name);
}
}
////////////////////////////////////////////////////////////////////
@ -226,9 +216,6 @@ HTTPClient::
SSL_CTX_free(_ssl_ctx);
}
// Free all of the expected server definitions.
clear_expected_servers();
unload_client_certificate();
}
@ -975,52 +962,6 @@ load_certificates(const Filename &filename) {
return sslw->load_certificates(filename);
}
////////////////////////////////////////////////////////////////////
// Function: HTTPClient::add_expected_server
// Access: Published
// Description: Adds the indicated string as a definition of a valid
// server to contact via https. If no servers have been
// been added, an https connection will be allowed to
// any server. If at least one server has been added,
// an https connection will be allowed to any of the
// named servers, but none others.
//
// The string passed in defines a subset of the server
// properties that are to be insisted on, using the X509
// naming convention, e.g. O=WDI/OU=VRStudio/CN=ttown.
//
// It makes sense to use this in conjunction with
// set_verify_ssl(), which insists that the https
// connection uses a verifiable certificate.
////////////////////////////////////////////////////////////////////
bool HTTPClient::
add_expected_server(const string &server_attributes) {
X509_NAME *name = parse_x509_name(server_attributes);
if (name == (X509_NAME *)NULL) {
return false;
}
_expected_servers.push_back(name);
return true;
}
////////////////////////////////////////////////////////////////////
// Function: HTTPClient::clear_expected_servers
// Access: Published
// Description: Clears the set of expected servers; the HTTPClient
// will allow an https connection to any server.
////////////////////////////////////////////////////////////////////
void HTTPClient::
clear_expected_servers() {
for (ExpectedServers::iterator ei = _expected_servers.begin();
ei != _expected_servers.end();
++ei) {
X509_NAME *name = (*ei);
X509_NAME_free(name);
}
_expected_servers.clear();
}
////////////////////////////////////////////////////////////////////
// Function: HTTPClient::make_channel
// Access: Published
@ -1139,13 +1080,6 @@ get_ssl_ctx() {
OpenSSLWrapper *sslw = OpenSSLWrapper::get_global_ptr();
sslw->notify_ssl_errors();
// Get the configured set of expected servers.
int num_servers = expected_ssl_server.get_num_unique_values();
for (int si = 0; si < num_servers; si++) {
string expected_server = expected_ssl_server.get_unique_value(si);
add_expected_server(expected_server);
}
SSL_CTX_set_cert_store(_ssl_ctx, sslw->get_x509_store());
return _ssl_ctx;

View File

@ -117,9 +117,6 @@ PUBLISHED:
INLINE void set_cipher_list(const string &cipher_list);
INLINE const string &get_cipher_list() const;
bool add_expected_server(const string &server_attributes);
void clear_expected_servers();
PT(HTTPChannel) make_channel(bool persistent_connection);
BLOCKING PT(HTTPChannel) post_form(const URLSpec &url, const string &body);
BLOCKING PT(HTTPChannel) get_document(const URLSpec &url);
@ -186,11 +183,6 @@ private:
string _client_certificate_pem;
string _client_certificate_passphrase;
// List of allowable SSL servers to connect to. If the list is
// empty, any server is acceptable.
typedef pvector<X509_NAME *> ExpectedServers;
ExpectedServers _expected_servers;
SSL_CTX *_ssl_ctx;
bool _client_certificate_loaded;
X509 *_client_certificate_pub;

View File

@ -10,6 +10,7 @@
#define SOURCES \
buffer.I buffer.h \
ca_bundle_data_src.c \
checksumHashGenerator.I checksumHashGenerator.h circBuffer.I \
circBuffer.h \
config_express.h \
@ -200,6 +201,20 @@
#end lib_target
#begin test_bin_target
// Not really a "test" program; this program is used to regenerate
// ca_bundle_data_src.c.
#define TARGET make_ca_bundle
#define LOCAL_LIBS $[LOCAL_LIBS] express
#define OTHER_LIBS pystub
#define SOURCES \
make_ca_bundle.cxx
#end test_bin_target
#begin test_bin_target
#define TARGET test_types
#define LOCAL_LIBS $[LOCAL_LIBS] express

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,129 @@
// Filename: make_ca_bundle.cxx
// Created by: drose (07Oct09)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) Carnegie Mellon University. All rights reserved.
//
// All use of this software is subject to the terms of the revised BSD
// license. You should have received a copy of this license along
// with this source code in a file named "LICENSE."
//
////////////////////////////////////////////////////////////////////
#include "pandabase.h"
#include "openSSLWrapper.h"
#include <stdio.h>
static const char *source_filename = "ca-bundle.crt";
static const char *target_filename = "ca_bundle_data_src.c";
int
main(int argc, char *argv[]) {
FILE *fin = fopen(source_filename, "r");
if (fin == NULL) {
cerr << "Couldn't open " << source_filename << " for reading.\n";
return 1;
}
// Initialize OpenSSL.
OpenSSLWrapper::get_global_ptr();
// We have to be sure and clear the OpenSSL error state before we
// call this function, or it will get confused.
ERR_clear_error();
STACK_OF(X509_INFO) *inf;
inf = PEM_X509_INFO_read(fin, NULL, NULL, NULL);
if (!inf) {
// Could not scan certificates.
cerr << "Couldn't read PEM file in " << source_filename << "\n";
return 0;
}
cerr << "PEM_X509_INFO_read() found " << sk_X509_INFO_num(inf)
<< " entries.\n";
// Now convert the certificates to DER form.
stringstream der_stream;
int cert_count = 0;
int num_entries = sk_X509_INFO_num(inf);
for (int i = 0; i < num_entries; i++) {
X509_INFO *itmp = sk_X509_INFO_value(inf, i);
if (itmp->x509) {
X509 *cert = itmp->x509;
int der_len = i2d_X509(cert, NULL);
unsigned char *der_buf = new unsigned char[der_len];
unsigned char *p = der_buf;
i2d_X509(cert, &p);
der_stream.write((const char *)der_buf, der_len);
delete[] der_buf;
cert_count++;
}
}
sk_X509_INFO_pop_free(inf, X509_INFO_free);
fclose(fin);
// Now write the data to the .c file, in a compilable form, similar
// to bin2c.
ofstream out;
Filename target = Filename::text_filename(target_filename);
if (!target.open_write(out)) {
cerr << "Couldn't open " << target_filename << " for writing.\n";
return (1);
}
der_stream.seekg(0);
istream &in = der_stream;
string table_type = "const unsigned char ";
string length_type = "const int ";
string table_name = "ca_bundle_data";
string static_keyword = "static ";
static const int col_width = 11;
out << "\n"
<< "/*\n"
<< " * This table was generated by the command:\n"
<< " *\n"
<< " * make_ca_bundle\n"
<< " *\n"
<< " * which is a \"test\" program in the express directory; it reads\n"
<< " * ca-bundle.crt and produces this file.\n"
<< " *\n"
<< " * This file represents the set of well-known certificate authorities\n"
<< " * in DER form, for compiling into OpenSSLWrapper.\n"
<< " */\n\n"
<< static_keyword << table_type << table_name << "[] = {";
out << hex << setfill('0');
int count = 0;
int col = 0;
unsigned int ch;
ch = in.get();
while (!in.fail() && !in.eof()) {
if (col == 0) {
out << "\n ";
} else if (col == col_width) {
out << ",\n ";
col = 0;
} else {
out << ", ";
}
out << "0x" << setw(2) << ch;
col++;
count++;
ch = in.get();
}
out << "\n};\n\n"
<< static_keyword << length_type << table_name << "_len = "
<< dec << count << ";\n\n";
cerr << "Wrote " << cert_count << " certificates to "
<< target_filename << "\n";
return 0;
}

View File

@ -17,6 +17,7 @@
#ifdef HAVE_OPENSSL
#include "virtualFileSystem.h"
#include "ca_bundle_data_src.c"
OpenSSLWrapper *OpenSSLWrapper::_global_ptr = NULL;
@ -38,6 +39,10 @@ OpenSSLWrapper() {
_x509_store = X509_STORE_new();
X509_STORE_set_default_paths(_x509_store);
// Load in the well-known certificate authorities compiled into this
// program.
load_certificates_from_der_ram((const char *)ca_bundle_data, ca_bundle_data_len);
// Load in any default certificates listed in the Config.prc file.
if (!ca_bundle_filename.empty()) {
load_certificates(ca_bundle_filename);
@ -62,13 +67,32 @@ OpenSSLWrapper::
X509_STORE_free(_x509_store);
}
////////////////////////////////////////////////////////////////////
// Function: OpenSSLWrapper::clear_certificates
// Access: Public
// Description: Removes all the certificates from the global store,
// including the compiled-in certificates loaded from
// ca_bundle_data.c. You can add new certificates by
// calling load_certificates().
////////////////////////////////////////////////////////////////////
void OpenSSLWrapper::
clear_certificates() {
// We do this by deleting the store and creating a new one.
X509_STORE_free(_x509_store);
_x509_store = X509_STORE_new();
// We don't set the default path either. We want a squeaky-clean store.
//X509_STORE_set_default_paths(_x509_store);
}
////////////////////////////////////////////////////////////////////
// Function: OpenSSLWrapper::load_certificates
// Access: Public
// Description: Reads the certificate(s) (delimited by -----BEGIN
// CERTIFICATE----- and -----END CERTIFICATE-----) from
// the indicated file and adds them to the global store
// object, retrieved via get_x509_store().
// Description: Reads the PEM-formatted certificate(s) (delimited by
// -----BEGIN CERTIFICATE----- and -----END
// CERTIFICATE-----) from the indicated file and adds
// them to the global store object, retrieved via
// get_x509_store().
//
// Returns the number of certificates read on success,
// or 0 on failure.
@ -90,7 +114,7 @@ load_certificates(const Filename &filename) {
return 0;
}
int result = load_certificates_from_ram(data.data(), data.size());
int result = load_certificates_from_pem_ram(data.data(), data.size());
if (result <= 0) {
express_cat.info()
@ -109,19 +133,20 @@ load_certificates(const Filename &filename) {
}
////////////////////////////////////////////////////////////////////
// Function: OpenSSLWrapper::load_certificates_from_ram
// Function: OpenSSLWrapper::load_certificates_from_pem_ram
// Access: Public
// Description: Reads a chain of trusted certificates from the
// indicated data buffer and adds them to the X509_STORE
// object. Returns the number of certificates read on
// success, or 0 on failure.
// object. The data buffer should be PEM-formatted.
// Returns the number of certificates read on success,
// or 0 on failure.
//
// You should call this only with trusted,
// locally-stored certificates; not with certificates
// received from an untrusted source.
////////////////////////////////////////////////////////////////////
int OpenSSLWrapper::
load_certificates_from_ram(const char *data, size_t data_size) {
load_certificates_from_pem_ram(const char *data, size_t data_size) {
STACK_OF(X509_INFO) *inf;
// Create an in-memory BIO to read the "file" from the buffer, and
@ -192,6 +217,43 @@ load_certificates_from_ram(const char *data, size_t data_size) {
return count;
}
////////////////////////////////////////////////////////////////////
// Function: OpenSSLWrapper::load_certificates_from_der_ram
// Access: Public
// Description: Reads a chain of trusted certificates from the
// indicated data buffer and adds them to the X509_STORE
// object. The data buffer should be DER-formatted.
// Returns the number of certificates read on success,
// or 0 on failure.
//
// You should call this only with trusted,
// locally-stored certificates; not with certificates
// received from an untrusted source.
////////////////////////////////////////////////////////////////////
int OpenSSLWrapper::
load_certificates_from_der_ram(const char *data, size_t data_size) {
int count = 0;
#if OPENSSL_VERSION_NUMBER >= 0x00908000L
// Beginning in 0.9.8, d2i_X509() accepted a const unsigned char **.
const unsigned char *bp, *bp_end;
#else
// Prior to 0.9.8, d2i_X509() accepted an unsigned char **.
unsigned char *bp, *bp_end;
#endif
bp = (unsigned char *)data;
bp_end = bp + data_size;
X509 *x509 = d2i_X509(NULL, &bp, bp_end - bp);
while (x509 != NULL) {
X509_STORE_add_cert(_x509_store, x509);
++count;
x509 = d2i_X509(NULL, &bp, bp_end - bp);
}
return count;
}
////////////////////////////////////////////////////////////////////
// Function: OpenSSLWrapper::get_x509_store
// Access: Public

View File

@ -19,6 +19,8 @@
#ifdef HAVE_OPENSSL
#include "filename.h"
#define OPENSSL_NO_KRB5
#include "openssl/ssl.h"
#include "openssl/rand.h"
@ -43,8 +45,11 @@ private:
~OpenSSLWrapper();
public:
void clear_certificates();
int load_certificates(const Filename &filename);
int load_certificates_from_ram(const char *data, size_t data_size);
int load_certificates_from_pem_ram(const char *data, size_t data_size);
int load_certificates_from_der_ram(const char *data, size_t data_size);
X509_STORE *get_x509_store();
void notify_ssl_errors();