signed multifiles

This commit is contained in:
David Rose 2009-09-06 18:49:05 +00:00
parent 657845986d
commit 3c0e701659
20 changed files with 1330 additions and 425 deletions

View File

@ -35,7 +35,6 @@
multiplexStreamBuf.I multiplexStreamBuf.h \
patcher.h patcher.I \
socketStream.h socketStream.I \
ssl_utils.h \
stringStreamBuf.I stringStreamBuf.h \
stringStream.I stringStream.h \
urlSpec.I urlSpec.h \
@ -66,7 +65,6 @@
multiplexStream.cxx multiplexStreamBuf.cxx \
patcher.cxx \
socketStream.cxx \
ssl_utils.cxx \
stringStreamBuf.cxx \
stringStream.cxx \
urlSpec.cxx \
@ -99,7 +97,6 @@
multiplexStreamBuf.I multiplexStreamBuf.h \
patcher.h patcher.I \
socketStream.h socketStream.I \
ssl_utils.h \
stringStreamBuf.I stringStreamBuf.h \
stringStream.I stringStream.h \
urlSpec.h urlSpec.I \

View File

@ -14,7 +14,7 @@
#include "bioStreamBuf.h"
#include "config_downloader.h"
#include "ssl_utils.h"
#include "openSSLWrapper.h"
#include <errno.h>
#ifdef HAVE_OPENSSL
@ -199,7 +199,7 @@ underflow() {
<< "Lost connection to "
<< _source->get_server_name() << ":"
<< _source->get_port() << " (" << read_count << ").\n";
notify_ssl_errors();
OpenSSLWrapper::get_global_ptr()->notify_ssl_errors();
SSL *ssl = NULL;
BIO_get_ssl(*_source, &ssl);

View File

@ -64,9 +64,6 @@ ConfigVariableInt patcher_buffer_size
ConfigVariableList expected_ssl_server
("expected-ssl-server");
ConfigVariableList ssl_certificates
("ssl-certificates");
ConfigVariableBool http_proxy_tunnel
("http-proxy-tunnel", false,
PRC_DESC("This specifies the default value for HTTPChannel::set_proxy_tunnel(). "
@ -145,7 +142,7 @@ init_libdownloader() {
"library is loaded), or false to defer this until it is actually "
"needed (which will be the first time you open an https connection "
"or otherwise use encryption services). You can also call "
"HTTPClient::initialize_ssl() to "
"HTTPClient::init_random_seed() to "
"do this when you are ready. The issue is that on Windows, "
"OpenSSL will attempt to "
"randomize its seed by crawling through the entire heap of "

View File

@ -39,7 +39,6 @@ extern ConfigVariableDouble extractor_step_time;
extern ConfigVariableInt patcher_buffer_size;
extern ConfigVariableList expected_ssl_server;
extern ConfigVariableList ssl_certificates;
extern ConfigVariableBool http_proxy_tunnel;
extern ConfigVariableDouble http_connect_timeout;

View File

@ -13,7 +13,6 @@
#include "multiplexStreamBuf.cxx"
#include "patcher.cxx"
#include "socketStream.cxx"
#include "ssl_utils.cxx"
#include "stringStreamBuf.cxx"
#include "stringStream.cxx"
#include "urlSpec.cxx"

View File

@ -16,7 +16,6 @@
#include "httpClient.h"
#include "httpCookie.h"
#include "bioStream.h"
#include "ssl_utils.h"
#include "chunkedStream.h"
#include "identityStream.h"
#include "config_downloader.h"
@ -26,7 +25,6 @@
#include <stdio.h>
#ifdef HAVE_OPENSSL
#include "openssl/x509.h"
#ifdef WIN32_VC
#include <WinSock2.h>
@ -989,7 +987,7 @@ run_connecting() {
downloader_cat.info()
<< "Could not connect to " << _bio->get_server_name() << ":"
<< _bio->get_port() << "\n";
notify_ssl_errors();
OpenSSLWrapper::get_global_ptr()->notify_ssl_errors();
_status_entry._status_code = SC_no_connection;
_state = S_try_next_proxy;
return false;
@ -1487,7 +1485,7 @@ run_setup_ssl() {
if (result == 0) {
downloader_cat.error()
<< "Invalid cipher list: '" << cipher_list << "'\n";
notify_ssl_errors();
OpenSSLWrapper::get_global_ptr()->notify_ssl_errors();
_status_entry._status_code = SC_ssl_internal_failure;
_state = S_failure;
return false;
@ -1563,7 +1561,7 @@ run_ssl_handshake() {
downloader_cat.info()
<< "Could not establish SSL handshake with "
<< _request.get_url().get_server_and_port() << "\n";
notify_ssl_errors();
OpenSSLWrapper::get_global_ptr()->notify_ssl_errors();
// It seems to be an error to free sbio at this point; perhaps
// it's already been freed?
@ -2485,7 +2483,7 @@ begin_request(HTTPEnum::Method method, const DocumentSpec &url,
} else {
// Couldn't read the file.
notify_ssl_errors();
OpenSSLWrapper::get_global_ptr()->notify_ssl_errors();
_status_entry._status_code = SC_no_connection;
_state = S_failure;
}

View File

@ -15,7 +15,6 @@
#include "httpClient.h"
#include "httpChannel.h"
#include "config_downloader.h"
#include "ssl_utils.h"
#include "filename.h"
#include "config_express.h"
#include "virtualFileSystem.h"
@ -26,19 +25,6 @@
#ifdef HAVE_OPENSSL
#include "openssl/rand.h"
#include "openssl/err.h"
// Windows may define this macro inappropriately.
#ifdef X509_NAME
#undef X509_NAME
#endif
bool HTTPClient::_ssl_initialized = false;
// This is created once and never freed.
X509_STORE *HTTPClient::_x509_store = NULL;
PT(HTTPClient) HTTPClient::_global_ptr;
////////////////////////////////////////////////////////////////////
@ -183,10 +169,8 @@ HTTPClient() {
_client_certificate_priv = NULL;
// The first time we create an HTTPClient, we must initialize the
// OpenSSL library.
if (!_ssl_initialized) {
initialize_ssl();
}
// OpenSSL library. The OpenSSLWrapper object does that.
OpenSSLWrapper::get_global_ptr();
}
////////////////////////////////////////////////////////////////////
@ -238,7 +222,6 @@ HTTPClient::
// pointer from it, so it won't be destroyed along with it (this
// object is shared among all contexts).
if (_ssl_ctx != (SSL_CTX *)NULL) {
nassertv(_ssl_ctx->cert_store == _x509_store);
_ssl_ctx->cert_store = NULL;
SSL_CTX_free(_ssl_ctx);
}
@ -267,16 +250,9 @@ HTTPClient::
////////////////////////////////////////////////////////////////////
void HTTPClient::
init_random_seed() {
static bool _initialized = false;
if (!_initialized) {
_initialized = true;
// 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();
}
// Creating the global OpenSSLWrapper object is nowadays sufficient
// to ensure that OpenSSL and its random seed have been initialized.
OpenSSLWrapper::get_global_ptr();
}
////////////////////////////////////////////////////////////////////
@ -995,26 +971,8 @@ parse_http_version_string(const string &version) {
////////////////////////////////////////////////////////////////////
bool HTTPClient::
load_certificates(const Filename &filename) {
// The line below might be a recursive call, but it should be safe,
// since get_ssl_ctx() won't call load_certificates() until after it
// has assigned _ssl_ctx--guaranteeing that the second call to
// get_ssl_ctx() will be a no-op.
SSL_CTX *ctx = get_ssl_ctx();
int result = load_verify_locations(ctx, filename);
if (result <= 0) {
downloader_cat.info()
<< "Could not load certificates from " << filename << ".\n";
notify_ssl_errors();
return false;
}
downloader_cat.info()
<< "Appending " << result << " SSL certificates from "
<< filename << "\n";
return true;
OpenSSLWrapper *sslw = OpenSSLWrapper::get_global_ptr();
return sslw->load_certificates(filename);
}
////////////////////////////////////////////////////////////////////
@ -1178,7 +1136,8 @@ get_ssl_ctx() {
#endif
// Make sure the error strings are loaded.
notify_ssl_errors();
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();
@ -1187,29 +1146,7 @@ get_ssl_ctx() {
add_expected_server(expected_server);
}
if (_x509_store != (X509_STORE *)NULL) {
// If we've already created an x509 store object, share it with
// this context. It would be better to make a copy of the store
// object for each context, so we could locally add certificates,
// but (a) there doesn't seem to be an interface for this, and (b)
// something funny about loading certificates that seems to save
// some persistent global state anyway.
SSL_CTX_set_cert_store(_ssl_ctx, _x509_store);
} else {
// Create the first x509 store object, and fill it up with our
// certificates.
_x509_store = X509_STORE_new();
SSL_CTX_set_cert_store(_ssl_ctx, _x509_store);
// Load in any default certificates listed in the Configrc file.
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);
}
}
SSL_CTX_set_cert_store(_ssl_ctx, sslw->get_x509_store());
return _ssl_ctx;
}
@ -1461,114 +1398,6 @@ unload_client_certificate() {
_client_certificate_loaded = false;
}
////////////////////////////////////////////////////////////////////
// Function: HTTPClient::initialize_ssl
// Access: Private, Static
// Description: Called once the first time this class is used to
// initialize the OpenSSL library.
////////////////////////////////////////////////////////////////////
void HTTPClient::
initialize_ssl() {
OpenSSL_add_all_algorithms();
_ssl_initialized = true;
}
////////////////////////////////////////////////////////////////////
// Function: HTTPClient::load_verify_locations
// Access: Private, Static
// Description: An implementation of the OpenSSL-provided
// SSL_CTX_load_verify_locations() that takes a Filename
// (and supports Panda vfs).
//
// This reads the certificates from the named ca_file
// and makes them available to the given SSL context.
// It returns a positive number on success, or <= 0 on
// failure.
////////////////////////////////////////////////////////////////////
int HTTPClient::
load_verify_locations(SSL_CTX *ctx, const Filename &ca_file) {
VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr();
// First, read the complete file into memory.
string data;
if (!vfs->read_file(ca_file, data, true)) {
// Could not find or read file.
downloader_cat.info()
<< "Could not read " << ca_file << ".\n";
return 0;
}
STACK_OF(X509_INFO) *inf;
// Now create an in-memory BIO to read the "file" from the buffer we
// just read, and call the low-level routines to read the
// certificates from the BIO.
BIO *mbio = BIO_new_mem_buf((void *)data.data(), data.length());
// 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.
downloader_cat.info()
<< "PEM_X509_INFO_read_bio() returned NULL.\n";
notify_ssl_errors();
return 0;
}
if (downloader_cat.is_spam()) {
downloader_cat.spam()
<< "PEM_X509_INFO_read_bio() found " << sk_X509_INFO_num(inf)
<< " entries.\n";
}
// Now add the certificates to the context.
X509_STORE *store = ctx->cert_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) {
X509_STORE_add_cert(store, itmp->x509);
count++;
if (downloader_cat.is_spam()) {
downloader_cat.spam()
<< "Entry " << i << " is x509\n";
}
} else if (itmp->crl) {
X509_STORE_add_crl(store, itmp->crl);
count++;
if (downloader_cat.is_spam()) {
downloader_cat.spam()
<< "Entry " << i << " is crl\n";
}
} else if (itmp->x_pkey) {
// X509_STORE_add_crl(store, itmp->x_pkey);
// count++;
if (downloader_cat.is_spam()) {
downloader_cat.spam()
<< "Entry " << i << " is pkey\n";
}
} else {
if (downloader_cat.is_spam()) {
downloader_cat.spam()
<< "Entry " << i << " is unknown type\n";
}
}
}
sk_X509_INFO_pop_free(inf, X509_INFO_free);
return count;
}
////////////////////////////////////////////////////////////////////
// Function: HTTPClient::parse_x509_name
// Access: Private, Static

View File

@ -23,7 +23,6 @@
// communications.
#ifdef HAVE_OPENSSL
#define OPENSSL_NO_KRB5
#include "urlSpec.h"
#include "httpAuthorization.h"
@ -35,13 +34,7 @@
#include "pmap.h"
#include "pset.h"
#include "referenceCount.h"
#include "openssl/ssl.h"
// Windows may define this macro inappropriately.
#ifdef X509_NAME
#undef X509_NAME
#endif
#include "openSSLWrapper.h"
class Filename;
class HTTPChannel;
@ -155,9 +148,6 @@ private:
void unload_client_certificate();
static void initialize_ssl();
static int load_verify_locations(SSL_CTX *ctx, const Filename &ca_file);
static X509_NAME *parse_x509_name(const string &source);
#if defined(SSL_097) && !defined(NDEBUG)
@ -206,9 +196,6 @@ private:
X509 *_client_certificate_pub;
EVP_PKEY *_client_certificate_priv;
static bool _ssl_initialized;
static X509_STORE *_x509_store;
static PT(HTTPClient) _global_ptr;
friend class HTTPChannel;

View File

@ -1,52 +0,0 @@
// Filename: ssl_utils.cxx
// Created by: drose (15Dec03)
//
////////////////////////////////////////////////////////////////////
//
// 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 "ssl_utils.h"
#include "config_downloader.h"
#ifdef HAVE_OPENSSL
#ifdef REPORT_OPENSSL_ERRORS
#include "openssl/err.h"
#endif
////////////////////////////////////////////////////////////////////
// Function: notify_ssl_errors
// 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 downloader_cat.warning(). If
// REPORT_OPENSSL_ERRORS is not defined, the function
// does nothing.
////////////////////////////////////////////////////////////////////
void 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);
downloader_cat.warning() << buffer << "\n";
e = ERR_get_error();
}
#endif // REPORT_OPENSSL_ERRORS
}
#endif // HAVE_OPENSSL

View File

@ -22,6 +22,7 @@
#include "pointerTo.h"
#include "filename.h"
#include "pset.h"
#include "vector_string.h"
#include <stdio.h>
#include <time.h>
@ -47,6 +48,8 @@ Filename chdir_to; // -C
bool got_chdir_to = false;
size_t scale_factor = 0; // -F
pset<string> dont_compress; // -Z
vector_string sign_params; // -S
pvector<Filename> cert_chain; // -N
// Default extensions not to compress. May be overridden with -Z.
string dont_compress_str = "jpg,png,mp3,ogg";
@ -220,7 +223,27 @@ help() {
" -1 .. -9\n"
" Specify the compression level when -z is in effect. Larger numbers\n"
" generate slightly smaller files, but compression takes longer. The\n"
" default is -" << default_compression_level << ".\n\n";
" default is -" << default_compression_level << ".\n\n"
" -N ca-chain.crt\n"
" Adds the indicated certificate chain file to the multifile. This must\n"
" used in conjunction with -c or -u mode. The indicated file must be\n"
" a PEM-formatted collection of certificates, representing the chain of\n"
" authentication from the certificate used to sign the multifile (-S,\n"
" below), and a certificate authority. This may be necessary for\n"
" certain certificates whose signing authority is one or more steps\n"
" removed from a well-known certificate authority. You must add this\n"
" file to the multifile before signing it (or at least at the same time).\n\n"
" -S file.crt,file.key[,\"password\"]\n"
" Sign the multifile. The signing certificate should be in PEM form in\n"
" file.crt, with its private key in PEM form in file.key. If the key\n"
" is encrypted on-disk, specify the decryption password as the third\n"
" option. PEM form is the form accepted by the Apache web server. The\n"
" signature is written to the multifile to prove it is unchanged; any\n"
" subsequent change to the multifile will invalidate the signature.\n"
" This parameter may be repeated to sign the multifile with different\n"
" certificates.\n\n";
}
const string &
@ -236,16 +259,17 @@ get_password() {
bool
is_named(const string &subfile_name, int argc, char *argv[]) {
is_named(const string &subfile_name, const vector_string &params) {
// Returns true if the indicated subfile appears on the list of
// files named on the command line.
if (argc < 2) {
if (params.empty()) {
// No named files; everything is listed.
return true;
}
for (int i = 1; i < argc; i++) {
if (subfile_name == argv[i]) {
vector_string::const_iterator pi;
for (pi = params.begin(); pi != params.end(); ++pi) {
if (subfile_name == (*pi)) {
return true;
}
}
@ -273,20 +297,39 @@ get_compression_level(const Filename &subfile_name) {
}
bool
add_directory(Multifile *multifile, const Filename &directory_name) {
do_add_files(Multifile *multifile, const pvector<Filename> &filenames,
bool cert_chain_flag);
bool
do_add_directory(Multifile *multifile, const Filename &directory_name,
bool cert_chain_flag) {
vector_string files;
if (!directory_name.scan_directory(files)) {
cerr << "Unable to scan directory " << directory_name << "\n";
return false;
}
bool okflag = true;
pvector<Filename> filenames;
filenames.reserve(files.size());
vector_string::const_iterator fi;
for (fi = files.begin(); fi != files.end(); ++fi) {
Filename subfile_name(directory_name, (*fi));
filenames.push_back(subfile_name);
}
return do_add_files(multifile, filenames, cert_chain_flag);
}
bool
do_add_files(Multifile *multifile, const pvector<Filename> &filenames,
bool cert_chain_flag) {
bool okflag = true;
pvector<Filename>::const_iterator fi;
for (fi = filenames.begin(); fi != filenames.end(); ++fi) {
const Filename &subfile_name = (*fi);
if (subfile_name.is_directory()) {
if (!add_directory(multifile, subfile_name)) {
if (!do_add_directory(multifile, subfile_name, cert_chain_flag)) {
okflag = false;
}
@ -310,15 +353,20 @@ add_directory(Multifile *multifile, const Filename &directory_name) {
if (verbose) {
cout << new_subfile_name << "\n";
}
if (cert_chain_flag) {
// Set the cert_chain flag on the file.
int index = multifile->find_subfile(new_subfile_name);
nassertr(index >= 0, false);
multifile->set_subfile_is_cert_chain(index, true);
}
}
}
}
return okflag;
}
bool
add_files(int argc, char *argv[]) {
add_files(const vector_string &params) {
PT(Multifile) multifile = new Multifile;
if (append || update) {
if (!multifile->open_read_write(multifile_name)) {
@ -350,36 +398,17 @@ add_files(int argc, char *argv[]) {
multifile->set_scale_factor(scale_factor);
}
bool okflag = true;
for (int i = 1; i < argc; i++) {
Filename subfile_name = Filename::from_os_specific(argv[i]);
if (subfile_name.is_directory()) {
if (!add_directory(multifile, subfile_name)) {
okflag = false;
}
pvector<Filename> filenames;
filenames.reserve(params.size());
vector_string::const_iterator si;
for (si = params.begin(); si != params.end(); ++si) {
Filename subfile_name = Filename::from_os_specific(*si);
filenames.push_back(subfile_name);
}
} else if (!subfile_name.exists()) {
cerr << "Not found: " << subfile_name << "\n";
okflag = false;
} else {
string new_subfile_name;
if (update) {
new_subfile_name = multifile->update_subfile
(subfile_name, subfile_name, get_compression_level(subfile_name));
} else {
new_subfile_name = multifile->add_subfile
(subfile_name, subfile_name, get_compression_level(subfile_name));
}
if (new_subfile_name.empty()) {
cerr << "Unable to add " << subfile_name << ".\n";
okflag = false;
} else {
if (verbose) {
cout << new_subfile_name << "\n";
}
}
}
bool okflag = do_add_files(multifile, filenames, false);
if (okflag && !cert_chain.empty()) {
okflag = do_add_files(multifile, cert_chain, true);
}
if (multifile->needs_repack()) {
@ -398,7 +427,7 @@ add_files(int argc, char *argv[]) {
}
bool
extract_files(int argc, char *argv[]) {
extract_files(const vector_string &params) {
if (!multifile_name.exists()) {
cerr << multifile_name << " not found.\n";
return false;
@ -418,7 +447,7 @@ extract_files(int argc, char *argv[]) {
bool any_encrypted = false;
for (i = 0; i < num_subfiles && !any_encrypted; i++) {
string subfile_name = multifile->get_subfile_name(i);
if (is_named(subfile_name, argc, argv)) {
if (is_named(subfile_name, params)) {
if (multifile->is_subfile_encrypted(i)) {
any_encrypted = true;
}
@ -432,7 +461,7 @@ extract_files(int argc, char *argv[]) {
// Now walk back through the list and this time do the extraction.
for (i = 0; i < num_subfiles; i++) {
string subfile_name = multifile->get_subfile_name(i);
if (is_named(subfile_name, argc, argv)) {
if (is_named(subfile_name, params)) {
Filename filename = subfile_name;
if (got_chdir_to) {
filename = Filename(chdir_to, subfile_name);
@ -455,7 +484,7 @@ extract_files(int argc, char *argv[]) {
}
bool
kill_files(int argc, char *argv[]) {
kill_files(const vector_string &params) {
if (!multifile_name.exists()) {
cerr << multifile_name << " not found.\n";
return false;
@ -473,7 +502,7 @@ kill_files(int argc, char *argv[]) {
int i = 0;
while (i < multifile->get_num_subfiles()) {
string subfile_name = multifile->get_subfile_name(i);
if (is_named(subfile_name, argc, argv)) {
if (is_named(subfile_name, params)) {
Filename filename = subfile_name;
if (verbose) {
@ -502,6 +531,50 @@ kill_files(int argc, char *argv[]) {
return okflag;
}
bool
sign_multifile() {
#ifndef HAVE_OPENSSL
cerr << "Cannot sign multifiles without OpenSSL compiled in.\n";
return false;
#else // HAVE_OPENSSL
// Re-open the Multifile, and sign it with the indicated certificate
// and key files.
PT(Multifile) multifile = new Multifile;
if (!multifile->open_read_write(multifile_name)) {
cerr << "Unable to re-open " << multifile_name << " for signing.\n";
return false;
}
vector_string::iterator si;
for (si = sign_params.begin(); si != sign_params.end(); ++si) {
const string &param = (*si);
size_t comma1 = param.find(',');
if (comma1 == string::npos) {
cerr << "Signing parameter requires a comma: " << param << "\n";
return false;
}
size_t comma2 = param.find(',', comma1 + 1);
Filename certificate = Filename::from_os_specific(param.substr(0, comma1));
Filename pkey;
string password;
if (comma2 != string::npos) {
pkey = Filename::from_os_specific(param.substr(comma1 + 1, comma2 - comma1 - 1));
password = param.substr(comma2 + 1);
} else {
pkey = Filename::from_os_specific(param.substr(comma1 + 1));
}
if (!multifile->add_signature(certificate, pkey, password)) {
return false;
}
}
return true;
#endif // HAVE_OPENSSL
}
const char *
format_timestamp(bool record_timestamp, time_t timestamp) {
static const size_t buffer_size = 512;
@ -533,7 +606,7 @@ format_timestamp(bool record_timestamp, time_t timestamp) {
}
bool
list_files(int argc, char *argv[]) {
list_files(const vector_string &params) {
if (!multifile_name.exists()) {
cerr << multifile_name << " not found.\n";
return false;
@ -546,15 +619,20 @@ list_files(int argc, char *argv[]) {
int num_subfiles = multifile->get_num_subfiles();
int i;
if (verbose) {
cout << num_subfiles << " subfiles:\n" << flush;
for (int i = 0; i < num_subfiles; i++) {
for (i = 0; i < num_subfiles; i++) {
string subfile_name = multifile->get_subfile_name(i);
if (is_named(subfile_name, argc, argv)) {
if (is_named(subfile_name, params)) {
char encrypted_symbol = ' ';
if (multifile->is_subfile_encrypted(i)) {
encrypted_symbol = 'e';
}
char cert_chain_symbol = ' ';
if (multifile->get_subfile_is_cert_chain(i)) {
cert_chain_symbol = 'N';
}
if (multifile->is_subfile_compressed(i)) {
size_t orig_length = multifile->get_subfile_length(i);
size_t internal_length = multifile->get_subfile_internal_length(i);
@ -563,24 +641,25 @@ list_files(int argc, char *argv[]) {
ratio = (double)internal_length / (double)orig_length;
}
if (ratio > 1.0) {
printf("%12d worse %c %s %s\n",
printf("%12d worse %c%c %s %s\n",
(int)multifile->get_subfile_length(i),
encrypted_symbol,
encrypted_symbol, cert_chain_symbol,
format_timestamp(multifile->get_record_timestamp(),
multifile->get_subfile_timestamp(i)),
subfile_name.c_str());
} else {
printf("%12d %3.0f%% %c %s %s\n",
printf("%12d %3.0f%% %c%c %s %s\n",
(int)multifile->get_subfile_length(i),
100.0 - ratio * 100.0, encrypted_symbol,
100.0 - ratio * 100.0,
encrypted_symbol, cert_chain_symbol,
format_timestamp(multifile->get_record_timestamp(),
multifile->get_subfile_timestamp(i)),
subfile_name.c_str());
}
} else {
printf("%12d %c %s %s\n",
printf("%12d %c%c %s %s\n",
(int)multifile->get_subfile_length(i),
encrypted_symbol,
encrypted_symbol, cert_chain_symbol,
format_timestamp(multifile->get_record_timestamp(),
multifile->get_subfile_timestamp(i)),
subfile_name.c_str());
@ -601,14 +680,35 @@ list_files(int argc, char *argv[]) {
cout << "Multifile needs to be repacked.\n";
}
} else {
for (int i = 0; i < num_subfiles; i++) {
for (i = 0; i < num_subfiles; i++) {
string subfile_name = multifile->get_subfile_name(i);
if (is_named(subfile_name, argc, argv)) {
if (is_named(subfile_name, params)) {
cout << subfile_name << "\n";
}
}
}
#ifdef HAVE_OPENSSL
int num_signatures = multifile->get_num_signatures();
if (num_signatures != 0) {
multifile->load_certificate_chains();
cout << "\n";
for (i = 0; i < num_signatures; ++i) {
cout << "Signed by " << multifile->get_signature_common_name(i);
int verify_result = multifile->validate_signature_certificate(i);
if (verify_result == 0) {
cout << " (certificate validated)\n";
} else {
cout << " (certificate unknown, reason " << verify_result << ")\n";
}
if (verbose) {
multifile->write_signature_certificate(i, cout);
cout << "\n";
}
}
}
#endif // HAVE_OPENSSL
return true;
}
@ -647,7 +747,7 @@ main(int argc, char *argv[]) {
extern char *optarg;
extern int optind;
static const char *optflags = "crutxkvz123456789Z:T:f:OC:ep:P:F:h";
static const char *optflags = "crutxkvz123456789Z:T:S:T:f:OC:ep:P:F:h";
int flag = getopt(argc, argv, optflags);
Filename rel_path;
while (flag != EOF) {
@ -715,6 +815,12 @@ main(int argc, char *argv[]) {
case 'Z':
dont_compress_str = optarg;
break;
case 'N':
cert_chain.push_back(Filename::from_os_specific(optarg));
break;
case 'S':
sign_params.push_back(optarg);
break;
case 'T':
{
int flag;
@ -798,24 +904,35 @@ main(int argc, char *argv[]) {
// Split out the extensions named by -Z into different words.
tokenize_extensions(dont_compress_str, dont_compress);
// Build a list of remaining parameters.
vector_string params;
params.reserve(argc - 1);
for (int i = 1; i < argc; i++) {
params.push_back(argv[i]);
}
bool okflag = true;
if (create || append || update) {
okflag = add_files(argc, argv);
okflag = add_files(params);
} else if (extract) {
if (got_record_timestamp_flag) {
cerr << "Warning: -T ignored on extract.\n";
}
okflag = extract_files(argc, argv);
okflag = extract_files(params);
} else if (kill_cmd) {
if (got_record_timestamp_flag) {
cerr << "Warning: -T ignored on kill.\n";
}
okflag = kill_files(argc, argv);
okflag = kill_files(params);
} else { // list
if (got_record_timestamp_flag) {
cerr << "Warning: -T ignored on list.\n";
}
okflag = list_files(argc, argv);
okflag = list_files(params);
}
if (okflag && !sign_params.empty()) {
sign_multifile();
}
if (okflag) {

View File

@ -35,6 +35,7 @@
nodePointerToBase.h nodePointerToBase.I \
nodePointerTo.h nodePointerTo.I \
nodeReferenceCount.h nodeReferenceCount.I \
openSSLWrapper.h openSSLWrapper.I \
ordered_vector.h ordered_vector.I ordered_vector.T \
password_hash.h \
patchfile.I patchfile.h \
@ -88,6 +89,7 @@
nodePointerToBase.cxx \
nodePointerTo.cxx \
nodeReferenceCount.cxx \
openSSLWrapper.cxx \
ordered_vector.cxx \
password_hash.cxx \
patchfile.cxx \
@ -147,6 +149,7 @@
nodePointerToBase.h nodePointerToBase.I \
nodePointerTo.h nodePointerTo.I \
nodeReferenceCount.h nodeReferenceCount.I \
openSSLWrapper.h openSSLWrapper.I \
ordered_vector.h ordered_vector.I ordered_vector.T \
password_hash.h \
patchfile.I patchfile.h \

View File

@ -69,6 +69,9 @@ ConfigVariableBool collect_tcp
ConfigVariableDouble collect_tcp_interval
("collect-tcp-interval", 0.2);
ConfigVariableList ssl_certificates
("ssl-certificates");
////////////////////////////////////////////////////////////////////
// Function: init_libexpress
// Description: Initializes the library. This must be called at

View File

@ -21,6 +21,8 @@
#include "configVariableBool.h"
#include "configVariableInt.h"
#include "configVariableDouble.h"
#include "configVariableList.h"
// Include this so interrogate can find it.
#include "executionEnvironment.h"
@ -50,6 +52,7 @@ extern ConfigVariableBool keep_temporary_files;
extern EXPCL_PANDAEXPRESS ConfigVariableBool collect_tcp;
extern EXPCL_PANDAEXPRESS ConfigVariableDouble collect_tcp_interval;
extern ConfigVariableList ssl_certificates;
// Expose the Config variable for Python access.
BEGIN_PUBLISH

View File

@ -21,6 +21,7 @@
#include "nodePointerToBase.cxx"
#include "nodePointerTo.cxx"
#include "nodeReferenceCount.cxx"
#include "openSSLWrapper.cxx"
#include "ordered_vector.cxx"
#include "patchfile.cxx"
#include "password_hash.cxx"

View File

@ -436,15 +436,26 @@ Subfile() {
_source = (istream *)NULL;
_flags = 0;
_compression_level = 0;
#ifdef HAVE_OPENSSL
_pkey = NULL;
#endif
}
////////////////////////////////////////////////////////////////////
// Function: Multifile::Subfile::operator <
// Access: Public
// Description:
// Description: Compares two Subfiles for proper sorting within the
// index.
////////////////////////////////////////////////////////////////////
INLINE bool Multifile::Subfile::
operator < (const Multifile::Subfile &other) const {
// This should only be called on normal subfiles, not on certificate
// files or signature files. (We don't attempt to sort these
// special signature files.)
nassertr(!is_cert_special() && !other.is_cert_special(), false);
// Normal subfiles are simply sorted in alphabetical order by
// filename.
return _name < other._name;
}
@ -484,3 +495,28 @@ INLINE bool Multifile::Subfile::
is_data_invalid() const {
return (_flags & SF_data_invalid) != 0;
}
////////////////////////////////////////////////////////////////////
// Function: Multifile::Subfile::is_cert_special
// Access: Public
// Description: Returns true if this Subfile represents a signature
// record, which is treated specially; or false if it is
// an ordinary Subfile.
////////////////////////////////////////////////////////////////////
INLINE bool Multifile::Subfile::
is_cert_special() const {
return (_flags & SF_signature) != 0;
}
////////////////////////////////////////////////////////////////////
// Function: Multifile::Subfile::get_last_byte_pos
// Access: Public
// Description: Returns the byte position within the Multifile of the
// last byte that contributes to this Subfile, either in
// the index record or in the subfile data.
////////////////////////////////////////////////////////////////////
INLINE streampos Multifile::Subfile::
get_last_byte_pos() const {
return max(_index_start + (streampos)_index_length,
_data_start + (streampos)_data_length) - (streampos)1;
}

View File

@ -20,6 +20,7 @@
#include "datagram.h"
#include "zStream.h"
#include "encryptStream.h"
#include "virtualFileSystem.h"
#include <algorithm>
@ -116,6 +117,7 @@ Multifile() :
_owns_stream = false;
_next_index = 0;
_last_index = 0;
_last_data_byte = 0;
_needs_repack = false;
_timestamp = 0;
_timestamp_dirty = false;
@ -556,6 +558,375 @@ update_subfile(const string &subfile_name, const Filename &filename,
return name;
}
#ifdef HAVE_OPENSSL
////////////////////////////////////////////////////////////////////
// Function: Multifile::add_signature
// Access: Published
// Description: Adds a new signature to the Multifile. This
// signature associates the indicated certificate with
// the current contents of the Multifile. When the
// Multifile is read later, the signature will still be
// present only if the Multifile is unchanged; any
// subsequent changes to the Multifile will
// automatically invalidate and remove the signature.
//
// The specified private key must match the certificate,
// and the Multifile must be open in read-write mode.
// The private key is only used for generating the
// signature; it is not written to the Multifile and
// cannot be retrieved from the Multifile later.
// (However, the certificate *can* be retrieved from the
// Multifile later, to identify the entity that created
// the signature.)
//
// This implicitly causes a repack() operation if one is
// needed. Returns true on success, false on failure.
//
// This flavor of add_signature() reads the certificate
// and private key from a PEM-formatted file, for
// instance as generated by the openssl command. If the
// private key file is password-encrypted, the third
// parameter will be used as the password to decrypt it.
////////////////////////////////////////////////////////////////////
bool Multifile::
add_signature(const Filename &certificate, const Filename &pkey,
const string &password) {
VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr();
// Read the certificate file from VFS. First, read the complete
// file into memory.
string certificate_data;
if (!vfs->read_file(certificate, certificate_data, true)) {
express_cat.info()
<< "Could not read " << certificate << ".\n";
return false;
}
// Create an in-memory BIO to read the "file" from the buffer.
BIO *certificate_mbio = BIO_new_mem_buf((void *)certificate_data.data(), certificate_data.size());
X509 *x509 = PEM_read_bio_X509(certificate_mbio, NULL, NULL, (void *)"");
BIO_free(certificate_mbio);
if (x509 == NULL) {
express_cat.info()
<< "Could not read certificate in " << certificate << ".\n";
return false;
}
// Now do the same thing with the private key. This one may be
// password-encrypted on disk.
string pkey_data;
if (!vfs->read_file(pkey, pkey_data, true)) {
express_cat.info()
<< "Could not read " << pkey << ".\n";
return false;
}
// Create an in-memory BIO to read the "file" from the buffer.
BIO *pkey_mbio = BIO_new_mem_buf((void *)pkey_data.data(), pkey_data.size());
EVP_PKEY *evp_pkey = PEM_read_bio_PrivateKey(pkey_mbio, NULL, NULL,
(void *)password.c_str());
BIO_free(pkey_mbio);
if (evp_pkey == NULL) {
express_cat.info()
<< "Could not read private key in " << pkey << ".\n";
X509_free(x509);
return false;
}
bool result = add_signature(x509, evp_pkey);
EVP_PKEY_free(evp_pkey);
X509_free(x509);
return result;
}
#endif // HAVE_OPENSSL
#ifdef HAVE_OPENSSL
////////////////////////////////////////////////////////////////////
// Function: Multifile::add_signature
// Access: Published
// Description: Adds a new signature to the Multifile. This
// signature associates the indicated certificate with
// the current contents of the Multifile. When the
// Multifile is read later, the signature will still be
// present only if the Multifile is unchanged; any
// subsequent changes to the Multifile will
// automatically invalidate and remove the signature.
//
// The specified private key must match the certificate,
// and the Multifile must be open in read-write mode.
// The private key is only used for generating the
// signature; it is not written to the Multifile and
// cannot be retrieved from the Multifile later.
// (However, the certificate *can* be retrieved from the
// Multifile later, to identify the entity that created
// the signature.)
//
// This implicitly causes a repack() operation if one is
// needed. Returns true on success, false on failure.
////////////////////////////////////////////////////////////////////
bool Multifile::
add_signature(X509 *certificate, EVP_PKEY *pkey) {
if (_needs_repack) {
if (!repack()) {
return false;
}
} else {
if (!flush()) {
return false;
}
}
// Encode the certificate into DER form for writing to the
// Multifile.
int der_len = i2d_X509(certificate, NULL);
unsigned char *der_buf = new unsigned char[der_len];
unsigned char *p = der_buf;
i2d_X509(certificate, &p);
string der_string((char *)der_buf, der_len);
delete[] der_buf;
istringstream der_stream(der_string);
// Create a temporary Subfile for writing out the signature.
Subfile *subfile = new Subfile;
subfile->_pkey = pkey;
subfile->_flags |= SF_signature;
subfile->_source = &der_stream;
// Write the new Subfile at the end. The cert_special subfiles
// always go at the end, because they're not the part of the file
// that's signed.
nassertr(_new_subfiles.empty(), false);
_new_subfiles.push_back(subfile);
bool result = flush();
delete subfile;
return result;
}
#endif // HAVE_OPENSSL
#ifdef HAVE_OPENSSL
////////////////////////////////////////////////////////////////////
// Function: Multifile::get_num_signatures
// Access: Published
// Description: Returns the number of signatures found on the
// Multifile. This reports only the signatures that
// match the contents of the Multifile; signatures that
// have become invalidated by a change in the Multifile
// are not reported here. However, this does not verify
// that the certificates themselves are valid; only that
// they match the Multifile contents.
////////////////////////////////////////////////////////////////////
int Multifile::
get_num_signatures() const {
((Multifile *)this)->check_signatures();
return _signatures.size();
}
#endif // HAVE_OPENSSL
#ifdef HAVE_OPENSSL
////////////////////////////////////////////////////////////////////
// Function: Multifile::get_signature
// Access: Published
// Description: Returns the nth signature found on the Multifile.
// See the comments in get_num_signatures().
////////////////////////////////////////////////////////////////////
X509 *Multifile::
get_signature(int n) const {
nassertr(n >= 0 && n < (int)_signatures.size(), NULL);
return _signatures[n];
}
#endif // HAVE_OPENSSL
#ifdef HAVE_OPENSSL
////////////////////////////////////////////////////////////////////
// Function: Multifile::get_signature_subject_name
// Access: Published
// Description: Returns the "subject name" for the nth signature found
// on the Multifile. This is a string formatted
// according to RFC2253 that should more-or-less
// identify a particular certificate. See the comments
// in get_num_signatures().
////////////////////////////////////////////////////////////////////
string Multifile::
get_signature_subject_name(int n) const {
X509 *x509 = get_signature(n);
nassertr(x509 != NULL, "");
X509_NAME *xname = X509_get_subject_name(x509);
if (xname != NULL) {
// We use "print" to dump the output to a memory BIO. Is
// there an easier way to extract the X509_NAME text? Curse
// these incomplete docs.
BIO *mbio = BIO_new(BIO_s_mem());
X509_NAME_print_ex(mbio, xname, 0, XN_FLAG_RFC2253);
char *pp;
long pp_size = BIO_get_mem_data(mbio, &pp);
string name(pp, pp_size);
BIO_free(mbio);
return name;
}
return string();
}
#endif // HAVE_OPENSSL
#ifdef HAVE_OPENSSL
////////////////////////////////////////////////////////////////////
// Function: Multifile::get_signature_common_name
// Access: Published
// Description: Returns the "common name" for the nth signature found
// on the Multifile, encoded in utf-8. This is a subset
// of the subject_name, but it is probably a friendlier
// string to present to a user. See the comments in
// get_num_signatures().
////////////////////////////////////////////////////////////////////
string Multifile::
get_signature_common_name(int n) const {
X509 *x509 = get_signature(n);
nassertr(x509 != NULL, "");
// A complex OpenSSL interface to extract out the common name in
// utf-8.
X509_NAME *xname = X509_get_subject_name(x509);
if (xname != NULL) {
int pos = X509_NAME_get_index_by_NID(xname, NID_commonName, -1);
if (pos != -1) {
// We just get the first common name. I guess it's possible to
// have more than one; not sure what that means in this context.
X509_NAME_ENTRY *xentry = X509_NAME_get_entry(xname, pos);
if (xentry != NULL) {
ASN1_STRING *data = X509_NAME_ENTRY_get_data(xentry);
if (data != NULL) {
// We use "print" to dump the output to a memory BIO. Is
// there an easier way to decode the ASN1_STRING? Curse
// these incomplete docs.
BIO *mbio = BIO_new(BIO_s_mem());
ASN1_STRING_print_ex(mbio, data, ASN1_STRFLGS_RFC2253 & ~ASN1_STRFLGS_ESC_MSB);
char *pp;
long pp_size = BIO_get_mem_data(mbio, &pp);
string name(pp, pp_size);
BIO_free(mbio);
return name;
}
}
}
}
return string();
}
#endif // HAVE_OPENSSL
#ifdef HAVE_OPENSSL
////////////////////////////////////////////////////////////////////
// Function: Multifile::write_signature_certificate
// Access: Published
// Description: Writes the certificate for the nth signature, in
// verbose form, to the indicated stream. See the
// comments in get_num_signatures().
////////////////////////////////////////////////////////////////////
void Multifile::
write_signature_certificate(int n, ostream &out) const {
X509 *x509 = get_signature(n);
nassertv(x509 != NULL);
BIO *mbio = BIO_new(BIO_s_mem());
X509_print(mbio, x509);
char *pp;
long pp_size = BIO_get_mem_data(mbio, &pp);
out.write(pp, pp_size);
BIO_free(mbio);
}
#endif // HAVE_OPENSSL
#ifdef HAVE_OPENSSL
////////////////////////////////////////////////////////////////////
// Function: Multifile::load_certificate_chains
// Access: Published
// Description: Loads any certificate chains specified in the
// Multifile into the global certificate store. This
// may be necessary to successfully validate the
// certificate used to sign the multifile in
// validate_signature_certificate(), below.
////////////////////////////////////////////////////////////////////
void Multifile::
load_certificate_chains() {
OpenSSLWrapper *sslw = OpenSSLWrapper::get_global_ptr();
Subfiles::iterator si;
for (si = _subfiles.begin(); si != _subfiles.end(); ++si) {
Subfile *subfile = (*si);
if ((subfile->_flags & SF_cert_chain) != 0) {
// If it's a certificate chain, add it to the global chain list.
// Read the cert chain into memory.
istream *stream = open_read_subfile(subfile);
nassertv(stream != NULL);
pvector<unsigned char> buffer;
bool success = read_to_pvector(buffer, *stream);
nassertv(success);
close_read_subfile(stream);
// Now it to the global chain.
if (!buffer.empty()) {
int result = sslw->load_certificates_from_ram((char *)&buffer[0], buffer.size());
if (result > 0) {
express_cat.info()
<< "Loaded " << result << " certificates from "
<< subfile->_name << "\n";
}
}
}
}
}
#endif // HAVE_OPENSSL
#ifdef HAVE_OPENSSL
////////////////////////////////////////////////////////////////////
// Function: Multifile::validate_signature_certificate
// Access: Published
// Description: Checks that the certificate used for the nth
// signature is a valid, authorized certificate with
// some known certificate authority. Returns 0 if it
// is valid, -1 if there is some error, or the
// corresponding OpenSSL error code if it is invalid,
// out-of-date, or self-signed.
////////////////////////////////////////////////////////////////////
int Multifile::
validate_signature_certificate(int n) const {
int verify_result = -1;
X509 *x509 = get_signature(n);
nassertr(x509 != NULL, false);
OpenSSLWrapper *sslw = OpenSSLWrapper::get_global_ptr();
X509_STORE_CTX *ctx = X509_STORE_CTX_new();
X509_STORE_CTX_init(ctx, sslw->get_x509_store(), x509, NULL);
X509_STORE_CTX_set_cert(ctx, x509);
X509_verify_cert(ctx);
verify_result = X509_STORE_CTX_get_error(ctx);
if (express_cat.is_debug()) {
express_cat.debug()
<< get_signature_subject_name(n) << ": validate " << verify_result
<< "\n";
}
X509_STORE_CTX_cleanup(ctx);
X509_STORE_CTX_free(ctx);
return verify_result;
}
#endif // HAVE_OPENSSL
////////////////////////////////////////////////////////////////////
// Function: Multifile::flush
// Access: Published
@ -673,6 +1044,10 @@ flush() {
if (subfile->is_data_invalid()) {
wrote_ok = false;
}
if (!subfile->is_cert_special()) {
_last_data_byte = max(_last_data_byte, subfile->get_last_byte_pos());
}
nassertr(_next_index == _write->tellp(), false);
}
@ -711,6 +1086,7 @@ flush() {
close();
return false;
}
return true;
}
@ -770,6 +1146,7 @@ repack() {
copy(_subfiles.begin(), _subfiles.end(), back_inserter(_new_subfiles));
_next_index = 0;
_last_index = 0;
_last_data_byte = 0;
_scale_factor = _new_scale_factor;
// And we write our contents to our new temporary file.
@ -999,7 +1376,7 @@ get_subfile_timestamp(int index) const {
////////////////////////////////////////////////////////////////////
bool Multifile::
is_subfile_compressed(int index) const {
nassertr(index >= 0 && index < (int)_subfiles.size(), 0);
nassertr(index >= 0 && index < (int)_subfiles.size(), false);
return (_subfiles[index]->_flags & SF_compressed) != 0;
}
@ -1012,10 +1389,55 @@ is_subfile_compressed(int index) const {
////////////////////////////////////////////////////////////////////
bool Multifile::
is_subfile_encrypted(int index) const {
nassertr(index >= 0 && index < (int)_subfiles.size(), 0);
nassertr(index >= 0 && index < (int)_subfiles.size(), false);
return (_subfiles[index]->_flags & SF_encrypted) != 0;
}
////////////////////////////////////////////////////////////////////
// Function: Multifile::set_subfile_is_cert_chain
// Access: Published
// Description: Sets the cert_chain flag on the indicated subfile.
// This should be set for any subfiles that define the
// certificate chain that will be necessary to validate
// signatures that are subsequently used to sign the
// multifile.
//
// Note that the certificate chain subfiles must be
// added and this flag set *before* signing the
// multifile.
////////////////////////////////////////////////////////////////////
void Multifile::
set_subfile_is_cert_chain(int index, bool flag) {
nassertv(is_write_valid());
nassertv(index >= 0 && index < (int)_subfiles.size());
Subfile *subfile = _subfiles[index];
bool current_flag = (subfile->_flags & SF_cert_chain) != 0;
if (current_flag != flag) {
if (flag) {
subfile->_flags |= SF_cert_chain;
} else {
subfile->_flags &= ~SF_cert_chain;
}
subfile->rewrite_index_flags(*_write);
}
}
////////////////////////////////////////////////////////////////////
// Function: Multifile::get_subfile_is_cert_chain
// Access: Published
// Description: Returns the cert_chain flag on the indicated subfile.
// This should be set for any subfiles that define the
// certificate chain that will be necessary to validate
// signatures that are subsequently used to sign the
// multifile.
////////////////////////////////////////////////////////////////////
bool Multifile::
get_subfile_is_cert_chain(int index) const {
nassertr(index >= 0 && index < (int)_subfiles.size(), false);
return (_subfiles[index]->_flags & SF_cert_chain) != 0;
}
////////////////////////////////////////////////////////////////////
// Function: Multifile::get_index_end
// Access: Published
@ -1108,61 +1530,7 @@ open_read_subfile(int index) {
nassertr(subfile == _subfiles[index], NULL);
}
// Return an ISubStream object that references into the open
// Multifile istream.
nassertr(subfile->_data_start != (streampos)0, NULL);
istream *stream =
new ISubStream(_read, subfile->_data_start,
subfile->_data_start + (streampos)subfile->_data_length);
if ((subfile->_flags & SF_encrypted) != 0) {
#ifndef HAVE_OPENSSL
express_cat.error()
<< "OpenSSL not compiled in; cannot read encrypted multifiles.\n";
delete stream;
return NULL;
#else // HAVE_OPENSSL
// The subfile is encrypted. So actually, return an
// IDecryptStream that wraps around the ISubStream.
IDecryptStream *wrapper =
new IDecryptStream(stream, true, _encryption_password);
stream = wrapper;
// Validate the password by confirming that the encryption header
// matches.
char this_header[_encrypt_header_size];
stream->read(this_header, _encrypt_header_size);
if (stream->fail() || stream->gcount() != (unsigned)_encrypt_header_size ||
memcmp(this_header, _encrypt_header, _encrypt_header_size) != 0) {
express_cat.error()
<< "Unable to decrypt subfile " << subfile->_name << ".\n";
delete stream;
return NULL;
}
#endif // HAVE_OPENSSL
}
if ((subfile->_flags & SF_compressed) != 0) {
#ifndef HAVE_ZLIB
express_cat.error()
<< "zlib not compiled in; cannot read compressed multifiles.\n";
delete stream;
return NULL;
#else // HAVE_ZLIB
// Oops, the subfile is compressed. So actually, return an
// IDecompressStream that wraps around the ISubStream.
IDecompressStream *wrapper = new IDecompressStream(stream, true);
stream = wrapper;
#endif // HAVE_ZLIB
}
if (stream->fail()) {
// Hmm, some inexplicable problem.
delete stream;
return NULL;
}
return stream;
return open_read_subfile(subfile);
}
////////////////////////////////////////////////////////////////////
@ -1229,7 +1597,7 @@ extract_subfile_to(int index, ostream &out) {
return false;
}
static const size_t buffer_size = 1024;
static const size_t buffer_size = 4096;
char buffer[buffer_size];
in->read(buffer, buffer_size);
@ -1397,6 +1765,9 @@ bool Multifile::
read_subfile(int index, string &result) {
result = string();
// We use a temporary pvector, because dynamic accumulation of a
// pvector seems to be many times faster than that of a string, at
// least on the Windows implementation of STL.
pvector<unsigned char> pv;
if (!read_subfile(index, pv)) {
return false;
@ -1426,20 +1797,9 @@ read_subfile(int index, pvector<unsigned char> &result) {
return false;
}
static const size_t buffer_size = 1024;
char buffer[buffer_size];
in->read(buffer, buffer_size);
size_t count = in->gcount();
while (count != 0) {
result.insert(result.end(), buffer, buffer + count);
in->read(buffer, buffer_size);
count = in->gcount();
}
bool failed = in->fail() && !in->eof();
bool success = read_to_pvector(result, *in);
close_read_subfile(in);
nassertr(!failed, false);
nassertr(success, false);
return true;
}
@ -1519,6 +1879,76 @@ add_new_subfile(Subfile *subfile, int compression_level) {
_new_subfiles.push_back(subfile);
}
////////////////////////////////////////////////////////////////////
// Function: Multifile::open_read_subfile
// Access: Private
// Description: This variant of open_read_subfile() is used
// internally only, and accepts a pointer to the
// internal Subfile object, which is assumed to be valid
// and written to the multifile.
////////////////////////////////////////////////////////////////////
istream *Multifile::
open_read_subfile(Subfile *subfile) {
nassertr(subfile->_source == (istream *)NULL &&
subfile->_source_filename.empty(), NULL);
// Return an ISubStream object that references into the open
// Multifile istream.
nassertr(subfile->_data_start != (streampos)0, NULL);
istream *stream =
new ISubStream(_read, subfile->_data_start,
subfile->_data_start + (streampos)subfile->_data_length);
if ((subfile->_flags & SF_encrypted) != 0) {
#ifndef HAVE_OPENSSL
express_cat.error()
<< "OpenSSL not compiled in; cannot read encrypted multifiles.\n";
delete stream;
return NULL;
#else // HAVE_OPENSSL
// The subfile is encrypted. So actually, return an
// IDecryptStream that wraps around the ISubStream.
IDecryptStream *wrapper =
new IDecryptStream(stream, true, _encryption_password);
stream = wrapper;
// Validate the password by confirming that the encryption header
// matches.
char this_header[_encrypt_header_size];
stream->read(this_header, _encrypt_header_size);
if (stream->fail() || stream->gcount() != (unsigned)_encrypt_header_size ||
memcmp(this_header, _encrypt_header, _encrypt_header_size) != 0) {
express_cat.error()
<< "Unable to decrypt subfile " << subfile->_name << ".\n";
delete stream;
return NULL;
}
#endif // HAVE_OPENSSL
}
if ((subfile->_flags & SF_compressed) != 0) {
#ifndef HAVE_ZLIB
express_cat.error()
<< "zlib not compiled in; cannot read compressed multifiles.\n";
delete stream;
return NULL;
#else // HAVE_ZLIB
// Oops, the subfile is compressed. So actually, return an
// IDecompressStream that wraps around the ISubStream.
IDecompressStream *wrapper = new IDecompressStream(stream, true);
stream = wrapper;
#endif // HAVE_ZLIB
}
if (stream->fail()) {
// Hmm, some inexplicable problem.
delete stream;
return NULL;
}
return stream;
}
////////////////////////////////////////////////////////////////////
// Function: Multifile::standardize_subfile_name
// Access: Private
@ -1540,6 +1970,31 @@ standardize_subfile_name(const string &subfile_name) const {
}
}
////////////////////////////////////////////////////////////////////
// Function: Multifile::read_to_pvector
// Access: Private, Static
// Description: Helper function to read the entire contents of the
// indicated stream from the current position, and
// append it onto the indicated pvector. Returns true
// on success, false on failure.
////////////////////////////////////////////////////////////////////
bool Multifile::
read_to_pvector(pvector<unsigned char> &result, istream &stream) {
static const size_t buffer_size = 4096;
char buffer[buffer_size];
stream.read(buffer, buffer_size);
size_t count = stream.gcount();
while (count != 0) {
result.insert(result.end(), buffer, buffer + count);
stream.read(buffer, buffer_size);
count = stream.gcount();
}
bool failed = stream.fail() && !stream.eof();
return !failed;
}
////////////////////////////////////////////////////////////////////
// Function: Multifile::clear_subfiles
// Access: Private
@ -1560,6 +2015,21 @@ clear_subfiles() {
// also appear in _subfiles.
_new_subfiles.clear();
#ifdef HAVE_OPENSSL
for (pi = _cert_special.begin(); pi != _cert_special.end(); ++pi) {
Subfile *subfile = (*pi);
delete subfile;
}
_cert_special.clear();
Certificates::iterator ci;
for (ci = _signatures.begin(); ci != _signatures.end(); ++ci) {
X509 *cert = (*ci);
X509_free(cert);
}
_signatures.clear();
#endif // HAVE_OPENSSL
Subfiles::iterator fi;
for (fi = _subfiles.begin(); fi != _subfiles.end(); ++fi) {
Subfile *subfile = (*fi);
@ -1674,7 +2144,10 @@ read_index() {
_next_index = normalize_streampos(_next_index);
read->seekg(_next_index);
_last_index = 0;
_last_data_byte = 0;
streampos index_forward;
streamoff bytes_skipped = 0;
bool read_cert_special = false;
Subfile *subfile = new Subfile;
index_forward = subfile->read_index(*read, _next_index, this);
@ -1684,14 +2157,31 @@ read_index() {
// Ignore deleted Subfiles in the index.
_needs_repack = true;
delete subfile;
} else if (subfile->is_cert_special()) {
// Certificate chains and signature files get stored in a
// special list.
_cert_special.push_back(subfile);
read_cert_special = true;
} else {
_subfiles.push_back(subfile);
}
if (index_forward != normalize_streampos(read->tellg())) {
// If the index entries don't follow exactly sequentially, the
// file ought to be repacked.
_needs_repack = true;
if (!subfile->is_cert_special()) {
if (bytes_skipped != 0) {
// If the index entries don't follow exactly sequentially
// (except for the cert special files), the file ought to be
// repacked.
_needs_repack = true;
}
if (read_cert_special) {
// If we read a normal subfile following a cert_special entry,
// the file ought to be repacked (certificates have to go at
// the end).
_needs_repack = true;
}
_last_data_byte = max(_last_data_byte, subfile->get_last_byte_pos());
}
streampos curr_pos = normalize_streampos(read->tellg());
bytes_skipped = index_forward - curr_pos;
read->seekg(index_forward);
_next_index = index_forward;
subfile = new Subfile;
@ -1771,6 +2261,107 @@ write_header() {
return true;
}
////////////////////////////////////////////////////////////////////
// Function: Multifile::check_signatures
// Access: Private
// Description: Walks through the list of _cert_special entries in
// the Multifile, moving any valid signatures found to
// _signatures. After this call, _cert_special will be
// empty.
//
// This does not check the validity of the certificates
// themselves. It only checks that they correctly sign
// the Multifile contents.
////////////////////////////////////////////////////////////////////
void Multifile::
check_signatures() {
#ifdef HAVE_OPENSSL
PendingSubfiles::iterator pi;
for (pi = _cert_special.begin(); pi != _cert_special.end(); ++pi) {
Subfile *subfile = (*pi);
nassertv((subfile->_flags & SF_signature) != 0);
// Extract the signature data and certificate separately.
istream *stream = open_read_subfile(subfile);
nassertv(stream != NULL);
StreamReader reader(*stream);
size_t sig_size = reader.get_uint32();
string sig_string = reader.extract_bytes(sig_size);
pvector<unsigned char> buffer;
bool success = read_to_pvector(buffer, *stream);
nassertv(success);
close_read_subfile(stream);
X509 *x509 = NULL;
EVP_PKEY *pkey = NULL;
if (!buffer.empty()) {
const unsigned char *bp = (const unsigned char *)&buffer[0];
x509 = d2i_X509(NULL, &bp, buffer.size());
}
if (x509 != NULL) {
pkey = X509_get_pubkey(x509);
}
if (pkey != NULL) {
EVP_MD_CTX *md_ctx;
#ifdef SSL_097
md_ctx = EVP_MD_CTX_create();
#else
md_ctx = new EVP_MD_CTX;
#endif
EVP_VerifyInit(md_ctx, EVP_sha1());
nassertv(_read != NULL);
_read->acquire();
istream *read = _read->get_istream();
// Read and hash the multifile contents, but only up till
// _last_data_byte.
read->seekg(0);
streampos bytes_remaining = _last_data_byte;
static const size_t buffer_size = 4096;
char buffer[buffer_size];
read->read(buffer, min((streampos)buffer_size, bytes_remaining));
size_t count = read->gcount();
while (count != 0) {
nassertv(count <= buffer_size);
EVP_VerifyUpdate(md_ctx, buffer, count);
bytes_remaining -= count;
read->read(buffer, min((streampos)buffer_size, bytes_remaining));
count = read->gcount();
}
nassertv(bytes_remaining == (streampos)0);
_read->release();
// Now check that the signature matches the hash.
int verify_result =
EVP_VerifyFinal(md_ctx,
(unsigned char *)sig_string.data(),
sig_string.size(), pkey);
if (verify_result == 1) {
// The signature matches; save the certificate.
_signatures.push_back(x509);
x509 = NULL;
} else {
// Bad match.
_needs_repack = true;
}
}
if (x509 != NULL) {
// If we still have the X509 pointer by this point, we haven't
// saved it anywhere, so free it now.
X509_free(x509);
}
}
#endif // HAVE_OPENSSL
_cert_special.clear();
}
////////////////////////////////////////////////////////////////////
// Function: Multifile::Subfile::read_index
// Access: Public
@ -1801,6 +2392,7 @@ read_index(istream &read, streampos fpos, Multifile *multifile) {
// Now get the rest of the index.
_index_start = fpos;
_index_length = 0;
_data_start = multifile->word_to_streampos(reader.get_uint32());
_data_length = reader.get_uint32();
@ -1839,6 +2431,7 @@ read_index(istream &read, streampos fpos, Multifile *multifile) {
return 0;
}
_index_length = read.tellg() - fpos;
return next_index;
}
@ -1859,6 +2452,7 @@ write_index(ostream &write, streampos fpos, Multifile *multifile) {
nassertr(write.tellp() == fpos, fpos);
_index_start = fpos;
_index_length = 0;
// This will be the contents of this particular index record. We
// build it up first since it will be variable length.
@ -1893,6 +2487,7 @@ write_index(ostream &write, streampos fpos, Multifile *multifile) {
write.write((const char *)idg.get_data(), idg.get_length());
write.write((const char *)dg.get_data(), dg.get_length());
_index_length = write.tellp() - fpos;
return next_index;
}
@ -1970,7 +2565,8 @@ write_data(ostream &write, istream *read, streampos fpos,
// Without OpenSSL, we can't support encryption. The flag had
// better not be set.
nassertr((_flags & SF_encrypted) == 0, fpos);
#else // HAVE_ZLIB
#else // HAVE_OPENSSL
if ((_flags & SF_encrypted) != 0) {
// Write it encrypted.
OEncryptStream *encrypt = new OEncryptStream;
@ -1985,7 +2581,7 @@ write_data(ostream &write, istream *read, streampos fpos,
// decryption.
putter->write(_encrypt_header, _encrypt_header_size);
}
#endif // HAVE_ZLIB
#endif // HAVE_OPENSSL
#ifndef HAVE_ZLIB
// Without ZLIB, we can't support compression. The flag had
@ -2002,7 +2598,76 @@ write_data(ostream &write, istream *read, streampos fpos,
streampos write_start = fpos;
_uncompressed_length = 0;
static const size_t buffer_size = 1024;
#ifndef HAVE_OPENSSL
// We also need OpenSSL for signatures.
nassertr((_flags & SF_signature) == 0, fpos);
#else // HAVE_OPENSSL
if ((_flags & SF_signature) != 0) {
// If it's a special signature record, precede the record data
// (the certificate itself) with the signature data generated
// against the multifile contents.
// In order to generate a signature, we need to have a valid
// read pointer.
nassertr(read != NULL, fpos);
// And we also need to have a private key.
nassertr(_pkey != NULL, fpos);
EVP_MD_CTX *md_ctx;
#ifdef SSL_097
md_ctx = EVP_MD_CTX_create();
#else
md_ctx = new EVP_MD_CTX;
#endif
EVP_SignInit(md_ctx, EVP_sha1());
// Read and hash the multifile contents, but only up till
// _last_data_byte.
nassertr(multifile->_last_data_byte < fpos, fpos);
read->seekg(0);
streampos bytes_remaining = multifile->_last_data_byte;
static const size_t buffer_size = 4096;
char buffer[buffer_size];
read->read(buffer, min((streampos)buffer_size, bytes_remaining));
size_t count = read->gcount();
while (count != 0) {
nassertr(count <= buffer_size, fpos);
EVP_SignUpdate(md_ctx, buffer, count);
bytes_remaining -= count;
read->read(buffer, min((streampos)buffer_size, bytes_remaining));
count = read->gcount();
}
nassertr(bytes_remaining == (streampos)0, fpos);
// Now generate and write out the signature.
unsigned int max_size = EVP_PKEY_size(_pkey);
unsigned char *sig_data = new unsigned char[max_size];
unsigned int sig_size;
if (!EVP_SignFinal(md_ctx, sig_data, &sig_size, _pkey)) {
OpenSSLWrapper *sslw = OpenSSLWrapper::get_global_ptr();
sslw->notify_ssl_errors();
}
nassertr(sig_size <= max_size, fpos);
StreamWriter writer(*putter);
writer.add_uint32(sig_size);
putter->write((char *)sig_data, sig_size);
_uncompressed_length += 4 + sig_size;
delete[] sig_data;
#ifdef SSL_097
EVP_MD_CTX_destroy(md_ctx);
#else
delete md_ctx;
#endif
}
#endif // HAVE_OPENSSL
// Finally, we can write out the data itself.
static const size_t buffer_size = 4096;
char buffer[buffer_size];
source->read(buffer, buffer_size);
@ -2076,7 +2741,7 @@ rewrite_index_data_start(ostream &write, Multifile *multifile) {
////////////////////////////////////////////////////////////////////
// Function: Multifile::Subfile::rewrite_index_flags
// Access: Public
// Description: Seeks within the indicate pfstream back to the index
// Description: Seeks within the indicated ostream back to the index
// record and rewrites just the _flags part of the
// index record.
////////////////////////////////////////////////////////////////////

View File

@ -25,6 +25,7 @@
#include "indirectLess.h"
#include "referenceCount.h"
#include "pvector.h"
#include "openSSLWrapper.h"
////////////////////////////////////////////////////////////////////
// Class : Multifile
@ -81,6 +82,22 @@ PUBLISHED:
int compression_level);
string update_subfile(const string &subfile_name, const Filename &filename,
int compression_level);
#ifdef HAVE_OPENSSL
bool add_signature(const Filename &certificate, const Filename &pkey,
const string &password = "");
bool add_signature(X509 *certificate, EVP_PKEY *pkey);
int get_num_signatures() const;
X509 *get_signature(int n) const;
string get_signature_subject_name(int n) const;
string get_signature_common_name(int n) const;
void write_signature_certificate(int n, ostream &out) const;
void load_certificate_chains();
int validate_signature_certificate(int n) const;
#endif // HAVE_OPENSSL
BLOCKING bool flush();
BLOCKING bool repack();
@ -97,6 +114,8 @@ PUBLISHED:
time_t get_subfile_timestamp(int index) const;
bool is_subfile_compressed(int index) const;
bool is_subfile_encrypted(int index) const;
void set_subfile_is_cert_chain(int index, bool flag);
bool get_subfile_is_cert_chain(int index) const;
streampos get_index_end() const;
streampos get_subfile_internal_start(int index) const;
@ -128,6 +147,8 @@ private:
SF_data_invalid = 0x0004,
SF_compressed = 0x0008,
SF_encrypted = 0x0010,
SF_cert_chain = 0x0020,
SF_signature = 0x0040,
};
class Subfile {
@ -145,9 +166,12 @@ private:
INLINE bool is_deleted() const;
INLINE bool is_index_invalid() const;
INLINE bool is_data_invalid() const;
INLINE bool is_cert_special() const;
INLINE streampos get_last_byte_pos() const;
string _name;
streampos _index_start;
size_t _index_length;
streampos _data_start;
size_t _data_length;
size_t _uncompressed_length;
@ -156,6 +180,9 @@ private:
Filename _source_filename;
int _flags;
int _compression_level; // Not preserved on disk.
#ifdef HAVE_OPENSSL
EVP_PKEY *_pkey; // Not preserved on disk.
#endif
};
INLINE streampos word_to_streampos(size_t word) const;
@ -164,24 +191,34 @@ private:
streampos pad_to_streampos(streampos fpos);
void add_new_subfile(Subfile *subfile, int compression_level);
istream *open_read_subfile(Subfile *subfile);
string standardize_subfile_name(const string &subfile_name) const;
static bool read_to_pvector(pvector<unsigned char> &result, istream &stream);
void clear_subfiles();
bool read_index();
bool write_header();
void check_signatures();
typedef ov_set<Subfile *, IndirectLess<Subfile> > Subfiles;
Subfiles _subfiles;
typedef pvector<Subfile *> PendingSubfiles;
PendingSubfiles _new_subfiles;
PendingSubfiles _removed_subfiles;
PendingSubfiles _cert_special;
#ifdef HAVE_OPENSSL
typedef pvector<X509 *> Certificates;
Certificates _signatures;
#endif
IStreamWrapper *_read;
ostream *_write;
bool _owns_stream;
streampos _next_index;
streampos _last_index;
streampos _last_data_byte;
bool _needs_repack;
time_t _timestamp;

View File

@ -1,5 +1,5 @@
// Filename: ssl_utils.h
// Created by: drose (15Dec03)
// Filename: openSSLWrapper.I
// Created by: drose (05Sep09)
//
////////////////////////////////////////////////////////////////////
//
@ -12,21 +12,3 @@
//
////////////////////////////////////////////////////////////////////
#ifndef SSL_UTILS_H
#define SSL_UTILS_H
#include "pandabase.h"
// This module is not compiled if OpenSSL is not available.
#ifdef HAVE_OPENSSL
#define OPENSSL_NO_KRB5
#include "openssl/ssl.h"
EXPCL_PANDAEXPRESS void notify_ssl_errors();
#endif // HAVE_OPENSSL
#endif

View File

@ -0,0 +1,241 @@
// 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"
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();
// Load in any default certificates listed in the Config.prc file.
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::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().
//
// Returns the number of certificates read on success,
// or 0 on failure.
////////////////////////////////////////////////////////////////////
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_ram(data.data(), data.size());
if (result <= 0) {
express_cat.info()
<< "Could not load certificates from " << filename << ".\n";
notify_ssl_errors();
return 0;
}
express_cat.info()
<< "Appending " << result << " SSL certificates from "
<< filename << "\n";
return result;
}
////////////////////////////////////////////////////////////////////
// Function: OpenSSLWrapper::load_certificates_from_ram
// Access: Public
// Description: Reads a chain of 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.
////////////////////////////////////////////////////////////////////
int OpenSSLWrapper::
load_certificates_from_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) {
X509_STORE_add_cert(_x509_store, itmp->x509);
count++;
if (express_cat.is_spam()) {
express_cat.spam()
<< "Entry " << i << " is x509\n";
}
} else if (itmp->crl) {
X509_STORE_add_crl(_x509_store, itmp->crl);
count++;
if (express_cat.is_spam()) {
express_cat.spam()
<< "Entry " << i << " is crl\n";
}
} else if (itmp->x_pkey) {
// X509_STORE_add_crl(_x509_store, itmp->x_pkey);
// count++;
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);
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::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

View File

@ -0,0 +1,63 @@
// Filename: openSSLWrapper.h
// 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."
//
////////////////////////////////////////////////////////////////////
#ifndef OPENSSLWRAPPER_H
#define OPENSSLWRAPPER_H
#include "pandabase.h"
#ifdef HAVE_OPENSSL
#define OPENSSL_NO_KRB5
#include "openssl/ssl.h"
#include "openssl/rand.h"
#include "openssl/err.h"
#include "openssl/x509.h"
// Windows may define this macro inappropriately.
#ifdef X509_NAME
#undef X509_NAME
#endif
////////////////////////////////////////////////////////////////////
// Class : OpenSSLWrapper
// Description : Provides an interface wrapper around the OpenSSL
// library, to ensure that the library is properly
// initialized in the application, and to provide some
// hooks into global OpenSSL context data.
////////////////////////////////////////////////////////////////////
class EXPCL_PANDAEXPRESS OpenSSLWrapper {
private:
OpenSSLWrapper();
~OpenSSLWrapper();
public:
int load_certificates(const Filename &filename);
int load_certificates_from_ram(const char *data, size_t data_size);
X509_STORE *get_x509_store();
void notify_ssl_errors();
static OpenSSLWrapper *get_global_ptr();
private:
X509_STORE *_x509_store;
static OpenSSLWrapper *_global_ptr;
};
#include "openSSLWrapper.I"
#endif // HAVE_OPENSSL
#endif