mirror of
https://github.com/panda3d/panda3d.git
synced 2025-09-30 16:58:40 -04:00
signed multifiles
This commit is contained in:
parent
657845986d
commit
3c0e701659
@ -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 \
|
||||
|
@ -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);
|
||||
|
@ -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 "
|
||||
|
@ -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;
|
||||
|
@ -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"
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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
|
@ -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 ¶ms) {
|
||||
// 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 ¶ms) {
|
||||
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 ¶ms) {
|
||||
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 ¶ms) {
|
||||
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 ¶m = (*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 ¶ms) {
|
||||
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) {
|
||||
|
@ -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 \
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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"
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
||||
|
241
panda/src/express/openSSLWrapper.cxx
Normal file
241
panda/src/express/openSSLWrapper.cxx
Normal 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
|
63
panda/src/express/openSSLWrapper.h
Normal file
63
panda/src/express/openSSLWrapper.h
Normal 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
|
Loading…
x
Reference in New Issue
Block a user