396 lines
13 KiB
C++
396 lines
13 KiB
C++
// Filename: openSSLWrapper.cxx
|
|
// Created by: drose (05Sep09)
|
|
//
|
|
////////////////////////////////////////////////////////////////////
|
|
//
|
|
// 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 "openSSLWrapper.h"
|
|
|
|
#ifdef HAVE_OPENSSL
|
|
|
|
#include "virtualFileSystem.h"
|
|
#include "ca_bundle_data_src.c"
|
|
|
|
OpenSSLWrapper *OpenSSLWrapper::_global_ptr = NULL;
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: OpenSSLWrapper::Constructor
|
|
// Access: Private
|
|
// Description:
|
|
////////////////////////////////////////////////////////////////////
|
|
OpenSSLWrapper::
|
|
OpenSSLWrapper() {
|
|
// It is necessary to call this before making any other OpenSSL
|
|
// call, per the docs. Also, the docs say that making this call
|
|
// will seed the random number generator. Apparently you can get
|
|
// away with not calling it in versions prior to 0.9.8, however.
|
|
SSL_library_init();
|
|
|
|
OpenSSL_add_all_algorithms();
|
|
|
|
_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.
|
|
ConfigVariableFilename ca_bundle_filename
|
|
("ca-bundle-filename", "",
|
|
PRC_DESC("This names the certificate authority file for OpenSSL "
|
|
"to use to verify whether SSL certificates are trusted or not. "
|
|
"The file named by this setting should contain one or more "
|
|
"PEM-formatted certificates from trusted certificate "
|
|
"authorities. This is a fairly standard file; a copy of "
|
|
"ca-bundle.crt is included in the OpenSSL distribution, and "
|
|
"is also included with Panda."));
|
|
|
|
if (!ca_bundle_filename.empty()) {
|
|
load_certificates(ca_bundle_filename);
|
|
}
|
|
|
|
ConfigVariableList ssl_certificates
|
|
("ssl-certificates",
|
|
PRC_DESC("This variable lists additional filenames, on top of the file "
|
|
"named by ca-bundle-filename, that contain trusted SSL "
|
|
"certificates or certificate authorities."));
|
|
|
|
int num_certs = ssl_certificates.get_num_unique_values();
|
|
for (int ci = 0; ci < num_certs; ci++) {
|
|
string cert_file = ssl_certificates.get_unique_value(ci);
|
|
Filename filename = Filename::expand_from(cert_file);
|
|
load_certificates(filename);
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: OpenSSLWrapper::Destructor
|
|
// Access: Private
|
|
// Description:
|
|
////////////////////////////////////////////////////////////////////
|
|
OpenSSLWrapper::
|
|
~OpenSSLWrapper() {
|
|
// Actually, the destructor is never called.
|
|
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 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.
|
|
//
|
|
// You should call this only with trusted,
|
|
// locally-stored certificates; not with certificates
|
|
// received from an untrusted source.
|
|
////////////////////////////////////////////////////////////////////
|
|
int OpenSSLWrapper::
|
|
load_certificates(const Filename &filename) {
|
|
VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr();
|
|
|
|
// First, read the complete file into memory.
|
|
string data;
|
|
if (!vfs->read_file(filename, data, true)) {
|
|
// Could not find or read file.
|
|
express_cat.info()
|
|
<< "Could not read " << filename << ".\n";
|
|
return 0;
|
|
}
|
|
|
|
int result = load_certificates_from_pem_ram(data.data(), data.size());
|
|
|
|
if (result <= 0) {
|
|
express_cat.info()
|
|
<< "Could not load certificates from " << filename << ".\n";
|
|
notify_ssl_errors();
|
|
return 0;
|
|
}
|
|
|
|
if (express_cat.is_debug()) {
|
|
express_cat.debug()
|
|
<< "Appending " << result << " SSL certificates from "
|
|
<< filename << "\n";
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// 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. 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_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
|
|
// call the low-level routines to read the certificates from the
|
|
// BIO.
|
|
BIO *mbio = BIO_new_mem_buf((void *)data, data_size);
|
|
|
|
// We have to be sure and clear the OpenSSL error state before we
|
|
// call this function, or it will get confused.
|
|
ERR_clear_error();
|
|
inf = PEM_X509_INFO_read_bio(mbio, NULL, NULL, NULL);
|
|
BIO_free(mbio);
|
|
|
|
if (!inf) {
|
|
// Could not scan certificates.
|
|
express_cat.info()
|
|
<< "PEM_X509_INFO_read_bio() returned NULL.\n";
|
|
notify_ssl_errors();
|
|
return 0;
|
|
}
|
|
|
|
if (express_cat.is_spam()) {
|
|
express_cat.spam()
|
|
<< "PEM_X509_INFO_read_bio() found " << sk_X509_INFO_num(inf)
|
|
<< " entries.\n";
|
|
}
|
|
|
|
// Now add the certificates to the store.
|
|
|
|
int 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) {
|
|
int result = X509_STORE_add_cert(_x509_store, itmp->x509);
|
|
if (result == 0) {
|
|
notify_debug_ssl_errors();
|
|
} else {
|
|
++count;
|
|
}
|
|
|
|
if (express_cat.is_spam()) {
|
|
express_cat.spam()
|
|
<< "Entry " << i << " is x509\n";
|
|
}
|
|
|
|
} else if (itmp->crl) {
|
|
int result = X509_STORE_add_crl(_x509_store, itmp->crl);
|
|
if (result == 0) {
|
|
notify_debug_ssl_errors();
|
|
} else {
|
|
++count;
|
|
}
|
|
|
|
if (express_cat.is_spam()) {
|
|
express_cat.spam()
|
|
<< "Entry " << i << " is crl\n";
|
|
}
|
|
|
|
} else if (itmp->x_pkey) {
|
|
if (express_cat.is_spam()) {
|
|
express_cat.spam()
|
|
<< "Entry " << i << " is pkey\n";
|
|
}
|
|
|
|
} else {
|
|
if (express_cat.is_spam()) {
|
|
express_cat.spam()
|
|
<< "Entry " << i << " is unknown type\n";
|
|
}
|
|
}
|
|
}
|
|
sk_X509_INFO_pop_free(inf, X509_INFO_free);
|
|
|
|
if (express_cat.is_spam()) {
|
|
express_cat.spam()
|
|
<< "successfully loaded " << count << " entries.\n";
|
|
}
|
|
|
|
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) {
|
|
if (express_cat.is_spam()) {
|
|
express_cat.spam()
|
|
<< "load_certificates_from_der_ram(" << (void *)data
|
|
<< ", " << data_size << ")\n";
|
|
}
|
|
|
|
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;
|
|
while (bp < bp_end) {
|
|
X509 *x509 = d2i_X509(NULL, &bp, bp_end - bp);
|
|
if (x509 == NULL) {
|
|
notify_ssl_errors();
|
|
break;
|
|
}
|
|
|
|
int result = X509_STORE_add_cert(_x509_store, x509);
|
|
if (result == 0) {
|
|
notify_debug_ssl_errors();
|
|
} else {
|
|
++count;
|
|
}
|
|
}
|
|
|
|
|
|
if (express_cat.is_spam()) {
|
|
express_cat.spam()
|
|
<< "loaded " << count << " certificates\n";
|
|
}
|
|
|
|
return count;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: OpenSSLWrapper::get_x509_store
|
|
// Access: Public
|
|
// Description: Returns the global X509_STORE object.
|
|
//
|
|
// It has to be a global object, because OpenSSL seems
|
|
// to store some global pointers associated with this
|
|
// object whether you want it to or not, and keeping
|
|
// independent copies of a local X509_STORE object
|
|
// doesn't seem to work that well. So, we have one
|
|
// store that keeps all certificates the application
|
|
// might need.
|
|
////////////////////////////////////////////////////////////////////
|
|
X509_STORE *OpenSSLWrapper::
|
|
get_x509_store() {
|
|
return _x509_store;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: OpenSSLWrapper::notify_ssl_errors
|
|
// Access: Public
|
|
// Description: A convenience function that is itself a wrapper
|
|
// around the OpenSSL convenience function to output the
|
|
// recent OpenSSL errors. This function sends the error
|
|
// string to express_cat.warning(). If
|
|
// REPORT_OPENSSL_ERRORS is not defined, the function
|
|
// does nothing.
|
|
////////////////////////////////////////////////////////////////////
|
|
void OpenSSLWrapper::
|
|
notify_ssl_errors() {
|
|
#ifdef REPORT_OPENSSL_ERRORS
|
|
static bool strings_loaded = false;
|
|
if (!strings_loaded) {
|
|
SSL_load_error_strings();
|
|
strings_loaded = true;
|
|
}
|
|
|
|
unsigned long e = ERR_get_error();
|
|
while (e != 0) {
|
|
static const size_t buffer_len = 256;
|
|
char buffer[buffer_len];
|
|
ERR_error_string_n(e, buffer, buffer_len);
|
|
express_cat.warning() << buffer << "\n";
|
|
e = ERR_get_error();
|
|
}
|
|
#endif // REPORT_OPENSSL_ERRORS
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: OpenSSLWrapper::notify_debug_ssl_errors
|
|
// Access: Public
|
|
// Description: As notify_ssl_errors(), but sends the output to debug
|
|
// instead of warning.
|
|
////////////////////////////////////////////////////////////////////
|
|
void OpenSSLWrapper::
|
|
notify_debug_ssl_errors() {
|
|
#ifdef REPORT_OPENSSL_ERRORS
|
|
static bool strings_loaded = false;
|
|
if (!strings_loaded) {
|
|
SSL_load_error_strings();
|
|
strings_loaded = true;
|
|
}
|
|
|
|
unsigned long e = ERR_get_error();
|
|
while (e != 0) {
|
|
if (express_cat.is_debug()) {
|
|
static const size_t buffer_len = 256;
|
|
char buffer[buffer_len];
|
|
ERR_error_string_n(e, buffer, buffer_len);
|
|
express_cat.debug() << buffer << "\n";
|
|
}
|
|
e = ERR_get_error();
|
|
}
|
|
#endif // REPORT_OPENSSL_ERRORS
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: OpenSSLWrapper::get_global_ptr
|
|
// Access: Public, Static
|
|
// Description:
|
|
////////////////////////////////////////////////////////////////////
|
|
OpenSSLWrapper *OpenSSLWrapper::
|
|
get_global_ptr() {
|
|
if (_global_ptr == NULL) {
|
|
_global_ptr = new OpenSSLWrapper;
|
|
}
|
|
return _global_ptr;
|
|
}
|
|
|
|
#endif // HAVE_OPENSSL
|