Panda3D

openSSLWrapper.cxx

00001 // Filename: openSSLWrapper.cxx
00002 // Created by:  drose (05Sep09)
00003 //
00004 ////////////////////////////////////////////////////////////////////
00005 //
00006 // PANDA 3D SOFTWARE
00007 // Copyright (c) Carnegie Mellon University.  All rights reserved.
00008 //
00009 // All use of this software is subject to the terms of the revised BSD
00010 // license.  You should have received a copy of this license along
00011 // with this source code in a file named "LICENSE."
00012 //
00013 ////////////////////////////////////////////////////////////////////
00014 
00015 #include "openSSLWrapper.h"
00016 
00017 #ifdef HAVE_OPENSSL
00018 
00019 #include "virtualFileSystem.h"
00020 #include "ca_bundle_data_src.c"
00021 
00022 OpenSSLWrapper *OpenSSLWrapper::_global_ptr = NULL;
00023 
00024 ////////////////////////////////////////////////////////////////////
00025 //     Function: OpenSSLWrapper::Constructor
00026 //       Access: Private
00027 //  Description: 
00028 ////////////////////////////////////////////////////////////////////
00029 OpenSSLWrapper::
00030 OpenSSLWrapper() {
00031   // It is necessary to call this before making any other OpenSSL
00032   // call, per the docs.  Also, the docs say that making this call
00033   // will seed the random number generator.  Apparently you can get
00034   // away with not calling it in versions prior to 0.9.8, however.
00035   SSL_library_init();
00036 
00037   OpenSSL_add_all_algorithms();
00038 
00039   _x509_store = X509_STORE_new();
00040   X509_STORE_set_default_paths(_x509_store);
00041 
00042   // Load in the well-known certificate authorities compiled into this
00043   // program.
00044   load_certificates_from_der_ram((const char *)ca_bundle_data, ca_bundle_data_len);
00045 
00046   // Load in any default certificates listed in the Config.prc file.
00047   ConfigVariableFilename ca_bundle_filename
00048     ("ca-bundle-filename", "",
00049      PRC_DESC("This names the certificate authority file for OpenSSL "
00050               "to use to verify whether SSL certificates are trusted or not.  "
00051               "The file named by this setting should contain one or more "
00052               "PEM-formatted certificates from trusted certificate "
00053               "authorities.  This is a fairly standard file; a copy of "
00054               "ca-bundle.crt is included in the OpenSSL distribution, and "
00055               "is also included with Panda."));
00056   
00057   if (!ca_bundle_filename.empty()) {
00058     load_certificates(ca_bundle_filename);
00059   }
00060 
00061   ConfigVariableList ssl_certificates
00062     ("ssl-certificates",
00063      PRC_DESC("This variable lists additional filenames, on top of the file "
00064               "named by ca-bundle-filename, that contain trusted SSL "
00065               "certificates or certificate authorities."));
00066 
00067   int num_certs = ssl_certificates.get_num_unique_values();
00068   for (int ci = 0; ci < num_certs; ci++) {
00069     string cert_file = ssl_certificates.get_unique_value(ci);
00070     Filename filename = Filename::expand_from(cert_file);
00071     load_certificates(filename);
00072   }
00073 }
00074 
00075 ////////////////////////////////////////////////////////////////////
00076 //     Function: OpenSSLWrapper::Destructor
00077 //       Access: Private
00078 //  Description: 
00079 ////////////////////////////////////////////////////////////////////
00080 OpenSSLWrapper::
00081 ~OpenSSLWrapper() {
00082   // Actually, the destructor is never called.
00083   X509_STORE_free(_x509_store);
00084 }
00085 
00086 ////////////////////////////////////////////////////////////////////
00087 //     Function: OpenSSLWrapper::clear_certificates
00088 //       Access: Public
00089 //  Description: Removes all the certificates from the global store,
00090 //               including the compiled-in certificates loaded from
00091 //               ca_bundle_data.c.  You can add new certificates by
00092 //               calling load_certificates().
00093 ////////////////////////////////////////////////////////////////////
00094 void OpenSSLWrapper::
00095 clear_certificates() {
00096   // We do this by deleting the store and creating a new one.
00097   X509_STORE_free(_x509_store);
00098   _x509_store = X509_STORE_new();
00099 
00100   // We don't set the default path either.  We want a squeaky-clean store.
00101   //X509_STORE_set_default_paths(_x509_store);
00102 }
00103 
00104 ////////////////////////////////////////////////////////////////////
00105 //     Function: OpenSSLWrapper::load_certificates
00106 //       Access: Public
00107 //  Description: Reads the PEM-formatted certificate(s) (delimited by
00108 //               -----BEGIN CERTIFICATE----- and -----END
00109 //               CERTIFICATE-----) from the indicated file and adds
00110 //               them to the global store object, retrieved via
00111 //               get_x509_store().
00112 //
00113 //               Returns the number of certificates read on success,
00114 //               or 0 on failure.
00115 //
00116 //               You should call this only with trusted,
00117 //               locally-stored certificates; not with certificates
00118 //               received from an untrusted source.
00119 ////////////////////////////////////////////////////////////////////
00120 int OpenSSLWrapper::
00121 load_certificates(const Filename &filename) {
00122   VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr();
00123 
00124   // First, read the complete file into memory.
00125   string data;
00126   if (!vfs->read_file(filename, data, true)) {
00127     // Could not find or read file.
00128     express_cat.info()
00129       << "Could not read " << filename << ".\n";
00130     return 0;
00131   }
00132 
00133   int result = load_certificates_from_pem_ram(data.data(), data.size());
00134 
00135   if (result <= 0) {
00136     express_cat.info()
00137       << "Could not load certificates from " << filename << ".\n";
00138     notify_ssl_errors();
00139     return 0;
00140   }
00141 
00142   if (express_cat.is_debug()) {
00143     express_cat.debug()
00144       << "Appending " << result << " SSL certificates from "
00145       << filename << "\n";
00146   }
00147 
00148   return result;
00149 }
00150 
00151 ////////////////////////////////////////////////////////////////////
00152 //     Function: OpenSSLWrapper::load_certificates_from_pem_ram
00153 //       Access: Public
00154 //  Description: Reads a chain of trusted certificates from the
00155 //               indicated data buffer and adds them to the X509_STORE
00156 //               object.  The data buffer should be PEM-formatted.
00157 //               Returns the number of certificates read on success,
00158 //               or 0 on failure.
00159 //
00160 //               You should call this only with trusted,
00161 //               locally-stored certificates; not with certificates
00162 //               received from an untrusted source.
00163 ////////////////////////////////////////////////////////////////////
00164 int OpenSSLWrapper::
00165 load_certificates_from_pem_ram(const char *data, size_t data_size) {
00166   STACK_OF(X509_INFO) *inf;
00167 
00168   // Create an in-memory BIO to read the "file" from the buffer, and
00169   // call the low-level routines to read the certificates from the
00170   // BIO.
00171   BIO *mbio = BIO_new_mem_buf((void *)data, data_size);
00172 
00173   // We have to be sure and clear the OpenSSL error state before we
00174   // call this function, or it will get confused.
00175   ERR_clear_error();
00176   inf = PEM_X509_INFO_read_bio(mbio, NULL, NULL, NULL);
00177   BIO_free(mbio);
00178 
00179   if (!inf) {
00180     // Could not scan certificates.
00181     express_cat.info()
00182       << "PEM_X509_INFO_read_bio() returned NULL.\n";
00183     notify_ssl_errors();
00184     return 0;
00185   }
00186   
00187   if (express_cat.is_spam()) {
00188     express_cat.spam()
00189       << "PEM_X509_INFO_read_bio() found " << sk_X509_INFO_num(inf)
00190       << " entries.\n";
00191   }
00192 
00193   // Now add the certificates to the store.
00194 
00195   int count = 0;
00196   int num_entries = sk_X509_INFO_num(inf);
00197   for (int i = 0; i < num_entries; i++) {
00198     X509_INFO *itmp = sk_X509_INFO_value(inf, i);
00199 
00200     if (itmp->x509) {
00201       int result = X509_STORE_add_cert(_x509_store, itmp->x509);
00202       if (result == 0) {
00203         notify_debug_ssl_errors();
00204       } else {
00205         ++count;
00206       }
00207 
00208       if (express_cat.is_spam()) {
00209         express_cat.spam()
00210           << "Entry " << i << " is x509\n";
00211       }
00212 
00213     } else if (itmp->crl) {
00214       int result = X509_STORE_add_crl(_x509_store, itmp->crl);
00215       if (result == 0) {
00216         notify_debug_ssl_errors();
00217       } else {
00218         ++count;
00219       }
00220 
00221       if (express_cat.is_spam()) {
00222         express_cat.spam()
00223           << "Entry " << i << " is crl\n";
00224       }
00225 
00226     } else if (itmp->x_pkey) {
00227       if (express_cat.is_spam()) {
00228         express_cat.spam()
00229           << "Entry " << i << " is pkey\n";
00230       }
00231 
00232     } else {
00233       if (express_cat.is_spam()) {
00234         express_cat.spam()
00235           << "Entry " << i << " is unknown type\n";
00236       }
00237     }
00238   }
00239   sk_X509_INFO_pop_free(inf, X509_INFO_free);
00240 
00241   if (express_cat.is_spam()) {
00242     express_cat.spam()
00243       << "successfully loaded " << count << " entries.\n";
00244   }
00245 
00246   return count;
00247 }
00248 
00249 ////////////////////////////////////////////////////////////////////
00250 //     Function: OpenSSLWrapper::load_certificates_from_der_ram
00251 //       Access: Public
00252 //  Description: Reads a chain of trusted certificates from the
00253 //               indicated data buffer and adds them to the X509_STORE
00254 //               object.  The data buffer should be DER-formatted.
00255 //               Returns the number of certificates read on success,
00256 //               or 0 on failure.
00257 //
00258 //               You should call this only with trusted,
00259 //               locally-stored certificates; not with certificates
00260 //               received from an untrusted source.
00261 ////////////////////////////////////////////////////////////////////
00262 int OpenSSLWrapper::
00263 load_certificates_from_der_ram(const char *data, size_t data_size) {
00264   if (express_cat.is_spam()) {
00265     express_cat.spam()
00266       << "load_certificates_from_der_ram(" << (void *)data
00267       << ", " << data_size << ")\n";
00268   }
00269 
00270   int count = 0;
00271 
00272 #if OPENSSL_VERSION_NUMBER >= 0x00908000L
00273   // Beginning in 0.9.8, d2i_X509() accepted a const unsigned char **.
00274   const unsigned char *bp, *bp_end;
00275 #else
00276   // Prior to 0.9.8, d2i_X509() accepted an unsigned char **.
00277   unsigned char *bp, *bp_end;
00278 #endif
00279 
00280   bp = (unsigned char *)data;
00281   bp_end = bp + data_size;
00282   while (bp < bp_end) {
00283     X509 *x509 = d2i_X509(NULL, &bp, bp_end - bp);
00284     if (x509 == NULL) {
00285       notify_ssl_errors();
00286       break;
00287     }
00288 
00289     int result = X509_STORE_add_cert(_x509_store, x509);
00290     if (result == 0) {
00291       notify_debug_ssl_errors();
00292     } else {
00293       ++count;
00294     }
00295   }
00296 
00297 
00298   if (express_cat.is_spam()) {
00299     express_cat.spam()
00300       << "loaded " << count << " certificates\n";
00301   }
00302 
00303   return count;
00304 }
00305 
00306 ////////////////////////////////////////////////////////////////////
00307 //     Function: OpenSSLWrapper::get_x509_store
00308 //       Access: Public
00309 //  Description: Returns the global X509_STORE object.
00310 //
00311 //               It has to be a global object, because OpenSSL seems
00312 //               to store some global pointers associated with this
00313 //               object whether you want it to or not, and keeping
00314 //               independent copies of a local X509_STORE object
00315 //               doesn't seem to work that well.  So, we have one
00316 //               store that keeps all certificates the application
00317 //               might need.
00318 ////////////////////////////////////////////////////////////////////
00319 X509_STORE *OpenSSLWrapper::
00320 get_x509_store() {
00321   return _x509_store;
00322 }
00323 
00324 ////////////////////////////////////////////////////////////////////
00325 //     Function: OpenSSLWrapper::notify_ssl_errors
00326 //       Access: Public
00327 //  Description: A convenience function that is itself a wrapper
00328 //               around the OpenSSL convenience function to output the
00329 //               recent OpenSSL errors.  This function sends the error
00330 //               string to express_cat.warning().  If
00331 //               REPORT_OPENSSL_ERRORS is not defined, the function
00332 //               does nothing.
00333 ////////////////////////////////////////////////////////////////////
00334 void OpenSSLWrapper::
00335 notify_ssl_errors() {
00336 #ifdef REPORT_OPENSSL_ERRORS
00337   static bool strings_loaded = false;
00338   if (!strings_loaded) {
00339     SSL_load_error_strings();
00340     strings_loaded = true;
00341   }
00342 
00343   unsigned long e = ERR_get_error();
00344   while (e != 0) {
00345     static const size_t buffer_len = 256;
00346     char buffer[buffer_len];
00347     ERR_error_string_n(e, buffer, buffer_len);
00348     express_cat.warning() << buffer << "\n";
00349     e = ERR_get_error();
00350   }
00351 #endif  //  REPORT_OPENSSL_ERRORS
00352 }
00353 
00354 ////////////////////////////////////////////////////////////////////
00355 //     Function: OpenSSLWrapper::notify_debug_ssl_errors
00356 //       Access: Public
00357 //  Description: As notify_ssl_errors(), but sends the output to debug
00358 //               instead of warning.
00359 ////////////////////////////////////////////////////////////////////
00360 void OpenSSLWrapper::
00361 notify_debug_ssl_errors() {
00362 #ifdef REPORT_OPENSSL_ERRORS
00363   static bool strings_loaded = false;
00364   if (!strings_loaded) {
00365     SSL_load_error_strings();
00366     strings_loaded = true;
00367   }
00368 
00369   unsigned long e = ERR_get_error();
00370   while (e != 0) {
00371     if (express_cat.is_debug()) {
00372       static const size_t buffer_len = 256;
00373       char buffer[buffer_len];
00374       ERR_error_string_n(e, buffer, buffer_len);
00375       express_cat.debug() << buffer << "\n";
00376     }
00377     e = ERR_get_error();
00378   }
00379 #endif  //  REPORT_OPENSSL_ERRORS
00380 }
00381 
00382 ////////////////////////////////////////////////////////////////////
00383 //     Function: OpenSSLWrapper::get_global_ptr
00384 //       Access: Public, Static
00385 //  Description: 
00386 ////////////////////////////////////////////////////////////////////
00387 OpenSSLWrapper *OpenSSLWrapper::
00388 get_global_ptr() {
00389   if (_global_ptr == NULL) {
00390     _global_ptr = new OpenSSLWrapper;
00391   }
00392   return _global_ptr;
00393 }
00394 
00395 #endif  // HAVE_OPENSSL
 All Classes Functions Variables Enumerations