diff options
Diffstat (limited to 'ACE/ace/SSL/SSL_Context.cpp')
-rw-r--r-- | ACE/ace/SSL/SSL_Context.cpp | 612 |
1 files changed, 612 insertions, 0 deletions
diff --git a/ACE/ace/SSL/SSL_Context.cpp b/ACE/ace/SSL/SSL_Context.cpp new file mode 100644 index 00000000000..18dd760e110 --- /dev/null +++ b/ACE/ace/SSL/SSL_Context.cpp @@ -0,0 +1,612 @@ +#include "SSL_Context.h" + +#include "sslconf.h" + +#if !defined(__ACE_INLINE__) +#include "SSL_Context.inl" +#endif /* __ACE_INLINE__ */ + +#include "ace/Guard_T.h" +#include "ace/Object_Manager.h" +#include "ace/Log_Msg.h" +#include "ace/Singleton.h" +#include "ace/Synch_Traits.h" +#include "ace/ACE.h" +#include "ace/OS_NS_errno.h" +#include "ace/OS_NS_string.h" + +#ifdef ACE_HAS_THREADS +# include "ace/Thread_Mutex.h" +# include "ace/OS_NS_Thread.h" +#endif /* ACE_HAS_THREADS */ + +#include <openssl/x509.h> +#include <openssl/err.h> +#include <openssl/rand.h> +#include <openssl/safestack.h> + +ACE_RCSID (ACE_SSL, + SSL_Context, + "$Id$") + + +namespace +{ + /// Reference count of the number of times the ACE_SSL_Context was + /// initialized. + int ssl_library_init_count = 0; + + // @@ This should also be done with a singleton, otherwise it is not + // thread safe and/or portable to some weird platforms... + +#ifdef ACE_HAS_THREADS + /// Array of mutexes used internally by OpenSSL when the SSL + /// application is multithreaded. + ACE_SSL_Context::lock_type * ssl_locks = 0; + + // @@ This should also be managed by a singleton. +#endif +} + +#ifdef ACE_HAS_THREADS + +# if (defined (ACE_HAS_VERSIONED_NAMESPACE) && ACE_HAS_VERSIONED_NAMESPACE == 1) +# define ACE_SSL_LOCKING_CALLBACK_NAME ACE_PREPROC_CONCATENATE(ACE_VERSIONED_NAMESPACE_NAME, _ACE_SSL_locking_callback) +# define ACE_SSL_THREAD_ID_NAME ACE_PREPROC_CONCATENATE(ACE_VERSIONED_NAMESPACE_NAME, _ACE_SSL_thread_id) +# else +# define ACE_SSL_LOCKING_CALLBACK_NAME ACE_SSL_locking_callback +# define ACE_SSL_THREAD_ID_NAME ACE_SSL_thread_id +# endif /* ACE_HAS_VERSIONED_NAMESPACE == 1 */ + + + +extern "C" +{ + void + ACE_SSL_LOCKING_CALLBACK_NAME (int mode, + int type, + const char * /* file */, + int /* line */) + { + // #ifdef undef + // fprintf(stderr,"thread=%4d mode=%s lock=%s %s:%d\n", + // CRYPTO_thread_id(), + // (mode&CRYPTO_LOCK)?"l":"u", + // (type&CRYPTO_READ)?"r":"w",file,line); + // #endif + // /* + // if (CRYPTO_LOCK_SSL_CERT == type) + // fprintf(stderr,"(t,m,f,l) %ld %d %s %d\n", + // CRYPTO_thread_id(), + // mode,file,line); + // */ + if (mode & CRYPTO_LOCK) + (void) ssl_locks[type].acquire (); + else + (void) ssl_locks[type].release (); + } + + // ------------------------------- + + // Return the current thread ID. OpenSSL uses this on platforms + // that need it. + unsigned long + ACE_SSL_THREAD_ID_NAME (void) + { + return (unsigned long) ACE_VERSIONED_NAMESPACE_NAME::ACE_OS::thr_self (); + } +} +#endif /* ACE_HAS_THREADS */ + + +// **************************************************************** + +ACE_BEGIN_VERSIONED_NAMESPACE_DECL + +#ifdef ACE_HAS_THREADS +ACE_SSL_Context::lock_type * ACE_SSL_Context::locks_ = 0; +#endif /* ACE_HAS_THREADS */ + +ACE_SSL_Context::ACE_SSL_Context (void) + : context_ (0), + mode_ (-1), + default_verify_mode_ (SSL_VERIFY_NONE), + have_ca_ (0) +{ + ACE_SSL_Context::ssl_library_init (); +} + +ACE_SSL_Context::~ACE_SSL_Context (void) +{ + if (this->context_) + { + ::SSL_CTX_free (this->context_); + this->context_ = 0; + } + + ACE_SSL_Context::ssl_library_fini (); +} + +ACE_SSL_Context * +ACE_SSL_Context::instance (void) +{ + return ACE_Singleton<ACE_SSL_Context, ACE_SYNCH_MUTEX>::instance (); +} + +void +ACE_SSL_Context::ssl_library_init (void) +{ + ACE_MT (ACE_GUARD (ACE_Recursive_Thread_Mutex, + ace_ssl_mon, + *ACE_Static_Object_Lock::instance ())); + + if (ssl_library_init_count == 0) + { + // Initialize the locking callbacks before initializing anything + // else. +#ifdef ACE_HAS_THREADS + int const num_locks = ::CRYPTO_num_locks (); + + this->locks_ = new lock_type[num_locks]; + ssl_locks = this->locks_; + +# if !defined (WIN32) + // This call isn't necessary on some platforms. See the CRYPTO + // library's threads(3) man page for details. + ::CRYPTO_set_id_callback (ACE_SSL_THREAD_ID_NAME); +# endif /* !WIN32 */ + ::CRYPTO_set_locking_callback (ACE_SSL_LOCKING_CALLBACK_NAME); +#endif /* ACE_HAS_THREADS */ + + ::SSLeay_add_ssl_algorithms (); + ::SSL_load_error_strings (); + + // Seed the random number generator. Note that the random + // number generator can be seeded more than once to "stir" its + // state. + +#ifdef WIN32 + // Seed the random number generator by sampling the screen. + ::RAND_screen (); +#endif /* WIN32 */ + +#if OPENSSL_VERSION_NUMBER >= 0x00905100L + // OpenSSL < 0.9.5 doesn't have EGD support. + + const char *egd_socket_file = + ACE_OS::getenv (ACE_SSL_EGD_FILE_ENV); + + if (egd_socket_file != 0) + (void) this->egd_file (egd_socket_file); +#endif /* OPENSSL_VERSION_NUMBER */ + + const char *rand_file = + ACE_OS::getenv (ACE_SSL_RAND_FILE_ENV); + + if (rand_file != 0) + (void) this->seed_file (rand_file); + + // Initialize the mutexes that will be used by the SSL and + // crypto library. + + } + + ++ssl_library_init_count; +} + +void +ACE_SSL_Context::ssl_library_fini (void) +{ + ACE_MT (ACE_GUARD (ACE_Recursive_Thread_Mutex, + ace_ssl_mon, + *ACE_Static_Object_Lock::instance ())); + + --ssl_library_init_count; + if (ssl_library_init_count == 0) + { + ::ERR_free_strings (); + ::EVP_cleanup (); + + // Clean up the locking callbacks after everything else has been + // cleaned up. +#ifdef ACE_HAS_THREADS + ::CRYPTO_set_locking_callback (0); + ssl_locks = 0; + + delete [] this->locks_; + this->locks_ = 0; + +#endif /* ACE_HAS_THREADS */ + } +} + +int +ACE_SSL_Context::set_mode (int mode) +{ + ACE_MT (ACE_GUARD_RETURN (ACE_Recursive_Thread_Mutex, + ace_ssl_mon, + *ACE_Static_Object_Lock::instance (), + -1)); + + if (this->context_ != 0) + return -1; + + SSL_METHOD *method = 0; + + switch (mode) + { + case ACE_SSL_Context::SSLv2_client: + method = ::SSLv2_client_method (); + break; + case ACE_SSL_Context::SSLv2_server: + method = ::SSLv2_server_method (); + break; + case ACE_SSL_Context::SSLv2: + method = ::SSLv2_method (); + break; + case ACE_SSL_Context::SSLv3_client: + method = ::SSLv3_client_method (); + break; + case ACE_SSL_Context::SSLv3_server: + method = ::SSLv3_server_method (); + break; + case ACE_SSL_Context::SSLv3: + method = ::SSLv3_method (); + break; + case ACE_SSL_Context::SSLv23_client: + method = ::SSLv23_client_method (); + break; + case ACE_SSL_Context::SSLv23_server: + method = ::SSLv23_server_method (); + break; + case ACE_SSL_Context::SSLv23: + method = ::SSLv23_method (); + break; + case ACE_SSL_Context::TLSv1_client: + method = ::TLSv1_client_method (); + break; + case ACE_SSL_Context::TLSv1_server: + method = ::TLSv1_server_method (); + break; + case ACE_SSL_Context::TLSv1: + method = ::TLSv1_method (); + break; + default: + method = ::SSLv3_method (); + break; + } + + this->context_ = ::SSL_CTX_new (method); + if (this->context_ == 0) + return -1; + + this->mode_ = mode; + + // Load the trusted certificate authority (default) certificate + // locations. But do not return -1 on error, doing so confuses CTX + // allocation (severe error) with the less important loading of CA + // certificate location error. If it is important for your + // application then call ACE_SSL_Context::have_trusted_ca(), + // immediately following this call to set_mode(). + (void) this->load_trusted_ca (); + + return 0; +} + +int +ACE_SSL_Context::load_trusted_ca (const char* ca_file, + const char* ca_dir, + bool use_env_defaults) +{ + this->check_context (); + + if (ca_file == 0 && use_env_defaults) + { + // Use the default environment settings. + ca_file = ACE_OS::getenv (ACE_SSL_CERT_FILE_ENV); + if (ca_file == 0) + ca_file = ACE_DEFAULT_SSL_CERT_FILE; + } + + if (ca_dir == 0 && use_env_defaults) + { + // Use the default environment settings. + ca_dir = ACE_OS::getenv (ACE_SSL_CERT_DIR_ENV); + if (ca_dir == 0) + ca_dir = ACE_DEFAULT_SSL_CERT_DIR; + } + + // NOTE: SSL_CTX_load_verify_locations() returns 0 on error. + if (::SSL_CTX_load_verify_locations (this->context_, + ca_file, + ca_dir) <= 0) + { + if (ACE::debug ()) + ACE_SSL_Context::report_error (); + return -1; + } + + ++this->have_ca_; + + // For TLS/SSL servers scan all certificates in ca_file and ca_dir and + // list them as acceptable CAs when requesting a client certificate. + if (mode_ == SSLv23 + || mode_ == SSLv23_server + || mode_ == TLSv1 + || mode_ == TLSv1_server + || mode_ == SSLv3 + || mode_ == SSLv3_server + || mode_ == SSLv2 + || mode_ == SSLv2_server) + { + // Note: The STACK_OF(X509_NAME) pointer is a copy of the pointer in + // the CTX; any changes to it by way of these function calls will + // change the CTX directly. + STACK_OF (X509_NAME) * cert_names; + cert_names = ::SSL_CTX_get_client_CA_list (this->context_); + bool error = false; + + // Add CAs from both the file and dir, if specified. There should + // already be a STACK_OF(X509_NAME) in the CTX, but if not, we create + // one. + if (ca_file) + { + if (cert_names == 0) + { + if ((cert_names = ::SSL_load_client_CA_file (ca_file)) != 0) + ::SSL_CTX_set_client_CA_list (this->context_, cert_names); + else + error = true; + } + else + { + // Add new certificate names to the list. + error = (0 == ::SSL_add_file_cert_subjects_to_stack (cert_names, + ca_file)); + } + + if (error) + { + if (ACE::debug ()) + ACE_SSL_Context::report_error (); + return -1; + } + } + + // SSL_add_dir_cert_subjects_to_stack is defined at 0.9.8a (but not + // on OpenVMS or Mac Classic); it may be available earlier. Change + // this comparison if so. +#if defined (OPENSSL_VERSION_NUMBER) && (OPENSSL_VERSION_NUMBER >= 0x0090801fL) +# if !defined (OPENSSL_SYS_VMS) && !defined (OPENSSL_SYS_MACINTOSH_CLASSIC) + + if (ca_dir != 0) + { + if (cert_names == 0) + { + if ((cert_names = sk_X509_NAME_new_null ()) == 0) + { + if (ACE::debug ()) + ACE_SSL_Context::report_error (); + return -1; + } + ::SSL_CTX_set_client_CA_list (this->context_, cert_names); + } + if (0 == ::SSL_add_dir_cert_subjects_to_stack (cert_names, ca_dir)) + { + if (ACE::debug ()) + ACE_SSL_Context::report_error (); + return -1; + } + } +# endif /* !OPENSSL_SYS_VMS && !OPENSSL_SYS_MACINTOSH_CLASSIC */ +#endif /* OPENSSL_VERSION_NUMBER >= 0.9.8a release */ + + } + + return 0; +} + + +int +ACE_SSL_Context::private_key (const char *file_name, + int type) +{ + if (this->private_key_.type () != -1) + return 0; + + this->check_context (); + + this->private_key_ = ACE_SSL_Data_File (file_name, type); + + if (::SSL_CTX_use_PrivateKey_file (this->context_, + this->private_key_.file_name (), + this->private_key_.type ()) <= 0) + { + this->private_key_ = ACE_SSL_Data_File (); + return -1; + } + else + return this->verify_private_key (); +} + +int +ACE_SSL_Context::verify_private_key (void) +{ + this->check_context (); + + return (::SSL_CTX_check_private_key (this->context_) <= 0 ? -1 : 0); +} + +int +ACE_SSL_Context::certificate (const char *file_name, + int type) +{ + if (this->certificate_.type () != -1) + return 0; + + this->certificate_ = ACE_SSL_Data_File (file_name, type); + + this->check_context (); + + if (::SSL_CTX_use_certificate_file (this->context_, + this->certificate_.file_name (), + this->certificate_.type ()) <= 0) + { + this->certificate_ = ACE_SSL_Data_File (); + return -1; + } + else + return 0; +} + +void +ACE_SSL_Context::set_verify_peer (int strict, int once, int depth) +{ + this->check_context (); + + // Setup the peer verififcation mode. + + int verify_mode = SSL_VERIFY_PEER; + if (once) + verify_mode |= SSL_VERIFY_CLIENT_ONCE; + if (strict) + verify_mode |= SSL_VERIFY_FAIL_IF_NO_PEER_CERT; + + // set the default verify mode + this->default_verify_mode (verify_mode); + + // Set the max certificate depth but later let the verify_callback + // catch the depth error by adding one to the required depth. + if (depth > 0) + ::SSL_CTX_set_verify_depth (this->context_, depth + 1); +} + + +int +ACE_SSL_Context::random_seed (const char * seed) +{ + ::RAND_seed (seed, ACE_OS::strlen (seed)); + +#if OPENSSL_VERSION_NUMBER >= 0x00905100L + // RAND_status() returns 1 if the PRNG has enough entropy. + return (::RAND_status () == 1 ? 0 : -1); +#else + return 0; // Ugly, but OpenSSL <= 0.9.4 doesn't have RAND_status(). +#endif /* OPENSSL_VERSION_NUMBER >= 0x00905100L */ +} + +int +ACE_SSL_Context::egd_file (const char * socket_file) +{ +#if OPENSSL_VERSION_NUMBER < 0x00905100L + // OpenSSL < 0.9.5 doesn't have EGD support. + ACE_UNUSED_ARG (socket_file); + ACE_NOTSUP_RETURN (-1); +#else + // RAND_egd() returns the amount of entropy used to seed the random + // number generator. The actual value should be greater than 16, + // i.e. 128 bits. + if (::RAND_egd (socket_file) > 0) + return 0; + else + return -1; +#endif /* OPENSSL_VERSION_NUMBER >= 0x00905100L */ +} + +int +ACE_SSL_Context::seed_file (const char * seed_file, long bytes) +{ + // RAND_load_file() returns the number of bytes used to seed the + // random number generator. If the file reads ok, check RAND_status to + // see if it got enough entropy. + if (::RAND_load_file (seed_file, bytes) > 0) +#if OPENSSL_VERSION_NUMBER >= 0x00905100L + // RAND_status() returns 1 if the PRNG has enough entropy. + return (::RAND_status () == 1 ? 0 : -1); +#else + return 0; // Ugly, but OpenSSL <= 0.9.4 doesn't have RAND_status(). +#endif /* OPENSSL_VERSION_NUMBER >= 0x00905100L */ + else + return -1; +} + +void +ACE_SSL_Context::report_error (unsigned long error_code) +{ + if (error_code == 0) + return; + + char error_string[256]; + + (void) ::ERR_error_string (error_code, error_string); + + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("ACE_SSL (%P|%t) error code: %u - %C\n"), + error_code, + error_string)); +} + +void +ACE_SSL_Context::report_error (void) +{ + unsigned long error = ::ERR_get_error (); + ACE_SSL_Context::report_error (error); + ACE_OS::last_error (error); +} + +int +ACE_SSL_Context::dh_params (const char *file_name, + int type) +{ + if (this->dh_params_.type () != -1) + return 0; + + // For now we only support PEM encodings + if (type != SSL_FILETYPE_PEM) + return -1; + + this->dh_params_ = ACE_SSL_Data_File (file_name, type); + + this->check_context (); + + { + // Swiped from Rescorla's examples and the OpenSSL s_server.c app + DH * ret=0; + BIO * bio = 0; + + if ((bio = ::BIO_new_file (this->dh_params_.file_name (), "r")) == NULL) + { + this->dh_params_ = ACE_SSL_Data_File (); + return -1; + } + + ret = PEM_read_bio_DHparams (bio, NULL, NULL, NULL); + BIO_free (bio); + + if (ret == 0) + { + this->dh_params_ = ACE_SSL_Data_File (); + return -1; + } + + if (::SSL_CTX_set_tmp_dh (this->context_, ret) < 0) + { + this->dh_params_ = ACE_SSL_Data_File (); + return -1; + } + DH_free (ret); + } + + return 0; +} + +// **************************************************************** + +#if defined (ACE_HAS_EXPLICIT_STATIC_TEMPLATE_MEMBER_INSTANTIATION) + +template ACE_Singleton<ACE_SSL_Context, ACE_SYNCH_MUTEX> * + ACE_Singleton<ACE_SSL_Context, ACE_SYNCH_MUTEX>::singleton_; + +#endif /* ACE_HAS_EXPLICIT_STATIC_TEMPLATE_MEMBER_INSTANTIATION */ + +ACE_END_VERSIONED_NAMESPACE_DECL |