/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "OpenSSLContextSpi.h" #include #include #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_STRINGS_H #include #endif #ifdef HAVE_STRING_H #include #endif #ifdef HAVE_OPENSSL #include #include #include #include #define SSL_LOCK_MUTEX CRYPTO_LOCK #else #define SSL_LOCK_MUTEX 1 #endif using namespace decaf; using namespace decaf::lang; using namespace decaf::lang::exceptions; using namespace decaf::net; using namespace decaf::net::ssl; using namespace decaf::util; using namespace decaf::util::concurrent; using namespace decaf::security; using namespace decaf::internal; using namespace decaf::internal::net; using namespace decaf::internal::net::ssl; using namespace decaf::internal::net::ssl::openssl; //////////////////////////////////////////////////////////////////////////////// namespace decaf { namespace internal { namespace net { namespace ssl { namespace openssl { class ContextData { public: Mutex monitor; Pointer clientSocketFactory; Pointer serverSocketFactory; Pointer random; std::string password; static Mutex* locks; static std::string defaultCipherList; #ifdef HAVE_OPENSSL SSL_CTX* openSSLContext; #else void* openSSLContext; #endif private: ContextData( const ContextData& ); ContextData& operator= ( const ContextData& ); public: ContextData( int size ) : monitor(), clientSocketFactory(), serverSocketFactory(), random(), password(), openSSLContext(NULL) { ContextData::locks = new Mutex[size]; } ~ContextData() { #ifdef HAVE_OPENSSL try{ SSL_CTX_free( this->openSSLContext ); } catch(...) {} #endif delete [] locks; } // Used to allow OpenSSL to tell us when to lock / unlock a mutex for it. static void lockCallback( int mode, int type, const char *, int ) { if( mode & SSL_LOCK_MUTEX ) { locks[type].lock(); } else { locks[type].unlock(); } } // Returns to OpenSSL the password for a Certificate. static int passwordCallback( char* buffer, int size, int, void* data ) { ContextData* ctxData = static_cast( data ); if( !ctxData->password.empty() ) { size = size < (int)ctxData->password.size() ? size : (int)ctxData->password.size(); strncpy( buffer, ctxData->password.c_str(), size ); return size; } return 0; } static unsigned long getThreadId() { return (unsigned long)Thread::currentThread()->getId(); } }; Mutex* ContextData::locks = NULL; std::string ContextData::defaultCipherList = "ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH"; }}}}} //////////////////////////////////////////////////////////////////////////////// OpenSSLContextSpi::OpenSSLContextSpi() : data( NULL ) { } //////////////////////////////////////////////////////////////////////////////// OpenSSLContextSpi::~OpenSSLContextSpi() { try{ #ifdef HAVE_OPENSSL // Clean up all the OpenSSL resources. CRYPTO_set_locking_callback( 0 ); EVP_cleanup(); #endif delete this->data; } DECAF_CATCH_NOTHROW( Exception ) DECAF_CATCHALL_NOTHROW() } //////////////////////////////////////////////////////////////////////////////// void OpenSSLContextSpi::providerInit( SecureRandom* random ) { if( random == NULL ) { throw NullPointerException( __FILE__, __LINE__, "SecureRandom instance passed was NULL." ); } try{ #ifdef HAVE_OPENSSL // General library initialization. #ifdef WIN32 CRYPTO_malloc_init(); #endif SSL_load_error_strings(); SSL_library_init(); OpenSSL_add_all_algorithms(); // Initialize our data object and store the RNG for later. this->data = new ContextData( CRYPTO_num_locks() ); this->data->random.reset( random ); this->data->openSSLContext = SSL_CTX_new( SSLv23_method() ); // Setup the Crypto Library Thread callbacks. CRYPTO_set_id_callback( &ContextData::getThreadId ); CRYPTO_set_locking_callback( &ContextData::lockCallback ); // Load the Library default CA paths and Context Options. SSL_CTX_set_default_verify_paths( this->data->openSSLContext ); SSL_CTX_set_options( this->data->openSSLContext, SSL_OP_ALL | SSL_OP_NO_SSLv2 ); SSL_CTX_set_mode( this->data->openSSLContext, SSL_MODE_AUTO_RETRY ); // The Password Callback for cases where we need to open a Cert. SSL_CTX_set_default_passwd_cb( this->data->openSSLContext, &ContextData::passwordCallback ); SSL_CTX_set_default_passwd_cb_userdata( this->data->openSSLContext, (void*)this->data ); // Sets the Default set of allowed Ciphers, this could be overridden by the system // property: decaf.net.ssl.CipherSuites if( SSL_CTX_set_cipher_list( this->data->openSSLContext, ContextData::defaultCipherList.c_str() ) != 1 ) { throw OpenSSLSocketException( __FILE__, __LINE__ ); } // Here we load the configured KeyStore, this is where the client and server certificate are // stored, a client doesn't necessary need this if the server doesn't enforce client authentication. std::string keyStorePath = System::getProperty( "decaf.net.ssl.keyStore" ); this->data->password = System::getProperty( "decaf.net.ssl.keyStorePassword" ); // We assume the Public and Private keys are in the same file. if( !keyStorePath.empty() ) { if( SSL_CTX_use_certificate_chain_file( this->data->openSSLContext, keyStorePath.c_str() ) != 1 ) { throw OpenSSLSocketException( __FILE__, __LINE__ ); } if( SSL_CTX_use_PrivateKey_file( this->data->openSSLContext, keyStorePath.c_str(), SSL_FILETYPE_PEM ) != 1 ) { throw OpenSSLSocketException( __FILE__, __LINE__ ); } } // Here we load the configured TrustStore, this is where the trusted certificates are stored // and are used to validate that we trust the Certificate sent by the server or client. // A server might not need this if its not going to enforce client authentication. std::string trustStorePath = System::getProperty( "decaf.net.ssl.trustStore" ); // OpenSSL sort of assumes that the trust store files won't require a password so we just // ignore the trustStorePassword for now. // std::string trustStorePassword = System::getProperty( "decaf.net.ssl.trustStorePassword" ); // We only consider trust store's that consist of a PEM encoded file, we could try and // check for the extension and assume its a directory if not there, but the OpenSSL // directory restrictions for Certificates make using a directory rather complicated // for the user so only do it if someone asks really nicely. if( !trustStorePath.empty() ) { if( SSL_CTX_load_verify_locations( this->data->openSSLContext, trustStorePath.c_str(), NULL ) != 1 ) { throw OpenSSLSocketException( __FILE__, __LINE__ ); } } // Now seed the OpenSSL RNG. std::vector seed( 128 ); random->nextBytes( seed ); RAND_seed( (void*)( &seed[0] ), (int)seed.size() ); #endif } DECAF_CATCH_RETHROW( NullPointerException ) DECAF_CATCH_RETHROW( KeyManagementException ) DECAF_CATCH_EXCEPTION_CONVERT( Exception, KeyManagementException ) DECAF_CATCHALL_THROW( KeyManagementException ) } //////////////////////////////////////////////////////////////////////////////// SocketFactory* OpenSSLContextSpi::providerGetSocketFactory() { try{ synchronized( &( this->data->monitor ) ) { if( this->data->clientSocketFactory == NULL ) { this->data->clientSocketFactory.reset( new OpenSSLSocketFactory( this ) ); } } return this->data->clientSocketFactory.get(); } DECAF_CATCH_RETHROW( IllegalStateException ) DECAF_CATCH_RETHROW( Exception ) DECAF_CATCHALL_THROW( Exception ) } //////////////////////////////////////////////////////////////////////////////// ServerSocketFactory* OpenSSLContextSpi::providerGetServerSocketFactory() { try{ synchronized( &( this->data->monitor ) ) { if( this->data->serverSocketFactory == NULL ) { this->data->serverSocketFactory.reset( new OpenSSLServerSocketFactory( this ) ); } } return this->data->serverSocketFactory.get(); } DECAF_CATCH_RETHROW( IllegalStateException ) DECAF_CATCH_RETHROW( Exception ) DECAF_CATCHALL_THROW( Exception ) } //////////////////////////////////////////////////////////////////////////////// std::vector OpenSSLContextSpi::getDefaultCipherSuites() { return std::vector(); } //////////////////////////////////////////////////////////////////////////////// std::vector OpenSSLContextSpi::getSupportedCipherSuites() { return std::vector(); } //////////////////////////////////////////////////////////////////////////////// void* OpenSSLContextSpi::getOpenSSLCtx() { return (void*)this->data->openSSLContext; }