summaryrefslogtreecommitdiff
path: root/cpp/src/IceSSL/Instance.cpp
diff options
context:
space:
mode:
authorJose <jose@zeroc.com>2014-06-06 16:33:11 +0200
committerJose <jose@zeroc.com>2014-06-06 16:33:11 +0200
commit7ba5b1fa9d9849182b19aebe5bad1570fb82452b (patch)
tree2b6a4f6407b4cc860d01f6e737959122a719ca3f /cpp/src/IceSSL/Instance.cpp
parentFixed ICE-5499, new garbage collection support (diff)
downloadice-7ba5b1fa9d9849182b19aebe5bad1570fb82452b.tar.bz2
ice-7ba5b1fa9d9849182b19aebe5bad1570fb82452b.tar.xz
ice-7ba5b1fa9d9849182b19aebe5bad1570fb82452b.zip
Fixed (ICE-4894) - Native SSL implementation for OS X
Diffstat (limited to 'cpp/src/IceSSL/Instance.cpp')
-rw-r--r--cpp/src/IceSSL/Instance.cpp1156
1 files changed, 6 insertions, 1150 deletions
diff --git a/cpp/src/IceSSL/Instance.cpp b/cpp/src/IceSSL/Instance.cpp
index 44c4704b2bc..3d3f24e58d8 100644
--- a/cpp/src/IceSSL/Instance.cpp
+++ b/cpp/src/IceSSL/Instance.cpp
@@ -11,1143 +11,18 @@
#ifdef _WIN32
# include <winsock2.h>
#endif
-
#include <IceSSL/Instance.h>
-#include <IceSSL/Util.h>
-#include <IceSSL/TrustManager.h>
-
-#include <Ice/Communicator.h>
-#include <Ice/LocalException.h>
-#include <Ice/Logger.h>
-#include <Ice/LoggerUtil.h>
#include <Ice/Properties.h>
-#include <IceUtil/Mutex.h>
-#include <IceUtil/MutexPtrLock.h>
-#include <IceUtil/StringUtil.h>
-
-#include <openssl/rand.h>
-#include <openssl/err.h>
-
-#include <IceUtil/DisableWarnings.h>
-
-// Ignore OS X OpenSSL deprecation warnings
-#ifdef __APPLE__
-#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
-#endif
-
using namespace std;
using namespace Ice;
using namespace IceSSL;
IceUtil::Shared* IceSSL::upCast(IceSSL::Instance* p) { return p; }
-IceUtil::Shared* IceSSL::upCast(IceSSL::SharedInstance* p) { return p; }
-
-namespace
-{
-
-IceUtil::Mutex* staticMutex = 0;
-int instanceCount = 0;
-IceUtil::Mutex* locks = 0;
-
-class Init
-{
-public:
-
- Init()
- {
- staticMutex = new IceUtil::Mutex;
- }
-
- ~Init()
- {
- delete staticMutex;
- staticMutex = 0;
- if(locks)
- {
- delete[] locks;
- locks = 0;
- }
- }
-};
-
-Init init;
-
-}
-
-extern "C"
-{
-
-//
-// OpenSSL mutex callback.
-//
-void
-IceSSL_opensslLockCallback(int mode, int n, const char* /*file*/, int /*line*/)
-{
- assert(locks);
- if(mode & CRYPTO_LOCK)
- {
- locks[n].lock();
- }
- else
- {
- locks[n].unlock();
- }
-}
-
-//
-// OpenSSL thread id callback.
-//
-unsigned long
-IceSSL_opensslThreadIdCallback()
-{
-#if defined(_WIN32)
- return static_cast<unsigned long>(GetCurrentThreadId());
-#elif defined(__FreeBSD__) || defined(__APPLE__) || defined(__osf1__)
- //
- // On some platforms, pthread_t is a pointer to a per-thread structure.
- //
- return reinterpret_cast<unsigned long>(pthread_self());
-#elif (defined(__linux) || defined(__sun) || defined(__hpux)) || defined(_AIX)
- //
- // On Linux, Solaris, HP-UX and AIX, pthread_t is an integer.
- //
- return static_cast<unsigned long>(pthread_self());
-#else
-# error "Unknown platform"
-#endif
-}
-
-int
-IceSSL_opensslPasswordCallback(char* buf, int size, int flag, void* userData)
-{
- IceSSL::SharedInstance* p = reinterpret_cast<IceSSL::SharedInstance*>(userData);
- string passwd = p->password(flag == 1);
- int sz = static_cast<int>(passwd.size());
- if(sz > size)
- {
- sz = size - 1;
- }
- strncpy(buf, passwd.c_str(), sz);
- buf[sz] = '\0';
-
- for(string::iterator i = passwd.begin(); i != passwd.end(); ++i)
- {
- *i = '\0';
- }
-
- return sz;
-}
-
-#ifndef OPENSSL_NO_DH
-DH*
-IceSSL_opensslDHCallback(SSL* ssl, int /*isExport*/, int keyLength)
-{
- IceSSL::SharedInstance* p = reinterpret_cast<IceSSL::SharedInstance*>(SSL_CTX_get_ex_data(ssl->ctx, 0));
- return p->dhParams(keyLength);
-}
-#endif
-
-int
-IceSSL_opensslVerifyCallback(int ok, X509_STORE_CTX* ctx)
-{
- SSL* ssl = reinterpret_cast<SSL*>(X509_STORE_CTX_get_ex_data(ctx, SSL_get_ex_data_X509_STORE_CTX_idx()));
- IceSSL::SharedInstance* p = reinterpret_cast<IceSSL::SharedInstance*>(SSL_CTX_get_ex_data(ssl->ctx, 0));
- return p->verifyCallback(ok, ssl, ctx);
-}
-
-}
-
-static bool
-passwordError()
-{
- int reason = ERR_GET_REASON(ERR_peek_error());
- return (reason == PEM_R_BAD_BASE64_DECODE ||
- reason == PEM_R_BAD_DECRYPT ||
- reason == PEM_R_BAD_PASSWORD_READ ||
- reason == PEM_R_PROBLEMS_GETTING_PASSWORD);
-}
-
-IceSSL::SharedInstance::SharedInstance(const CommunicatorPtr& communicator) :
- _communicator(communicator),
- _logger(communicator->getLogger()),
- _initialized(false),
- _ctx(0)
-{
- __setNoDelete(true);
-
- //
- // Initialize OpenSSL if necessary.
- //
- IceUtilInternal::MutexPtrLock<IceUtil::Mutex> sync(staticMutex);
- instanceCount++;
-
- if(instanceCount == 1)
- {
- PropertiesPtr properties = communicator->getProperties();
-
- //
- // The IceSSL.InitOpenSSL property specifies whether we should perform the global
- // startup (and shutdown) tasks for the OpenSSL library.
- //
- // If an application uses multiple components that each depend on OpenSSL, the
- // application should disable OpenSSL initialization in those components and
- // perform the initialization itself.
- //
- _initOpenSSL = properties->getPropertyAsIntWithDefault("IceSSL.InitOpenSSL", 1) > 0;
- if(_initOpenSSL)
- {
- //
- // Create the mutexes and set the callbacks.
- //
- if(!locks)
- {
- locks = new IceUtil::Mutex[CRYPTO_num_locks()];
- CRYPTO_set_locking_callback(IceSSL_opensslLockCallback);
- CRYPTO_set_id_callback(IceSSL_opensslThreadIdCallback);
- }
-
- //
- // Load human-readable error messages.
- //
- SSL_load_error_strings();
-
- //
- // Initialize the SSL library.
- //
- SSL_library_init();
-
- //
- // This is necessary to allow programs that use OpenSSL 0.9.x to
- // load private key files generated by OpenSSL 1.x.
- //
- OpenSSL_add_all_algorithms();
-
- //
- // Initialize the PRNG.
- //
-#ifdef WINDOWS
- RAND_screen(); // Uses data from the screen if possible.
-#endif
- char randFile[1024];
- if(RAND_file_name(randFile, sizeof(randFile))) // Gets the name of a default seed file.
- {
- RAND_load_file(randFile, 1024);
- }
-
- string randFiles = properties->getProperty("IceSSL.Random");
-
- if(!randFiles.empty())
- {
- vector<string> files;
-#ifdef _WIN32
- const string sep = ";";
-#else
- const string sep = ":";
-#endif
- string defaultDir = properties->getProperty("IceSSL.DefaultDir");
-
- if(!IceUtilInternal::splitString(randFiles, sep, files))
- {
- PluginInitializationException ex(__FILE__, __LINE__);
- ex.reason = "IceSSL: invalid value for IceSSL.Random:\n" + randFiles;
- throw ex;
- }
- for(vector<string>::iterator p = files.begin(); p != files.end(); ++p)
- {
- string file = *p;
- if(!checkPath(file, defaultDir, false))
- {
- PluginInitializationException ex(__FILE__, __LINE__);
- ex.reason = "IceSSL: entropy data file not found:\n" + file;
- throw ex;
- }
- if(!RAND_load_file(file.c_str(), 1024))
- {
- PluginInitializationException ex(__FILE__, __LINE__);
- ex.reason = "IceSSL: unable to load entropy data from " + file;
- throw ex;
- }
- }
- }
-#ifndef _WIN32
- //
- // The Entropy Gathering Daemon (EGD) is not available on Windows.
- // The file should be a Unix domain socket for the daemon.
- //
- string entropyDaemon = properties->getProperty("IceSSL.EntropyDaemon");
- if(!entropyDaemon.empty())
- {
- if(RAND_egd(entropyDaemon.c_str()) <= 0)
- {
- PluginInitializationException ex(__FILE__, __LINE__);
- ex.reason = "IceSSL: EGD failure using file " + entropyDaemon;
- throw ex;
- }
- }
-#endif
- if(!RAND_status())
- {
- communicator->getLogger()->warning("IceSSL: insufficient data to initialize PRNG");
- }
- }
- else
- {
- if(!properties->getProperty("IceSSL.Random").empty())
- {
- _logger->warning("IceSSL: ignoring IceSSL.Random because OpenSSL initialization is disabled");
- }
-#ifndef _WIN32
- else if(!properties->getProperty("IceSSL.EntropyDaemon").empty())
- {
- _logger->warning("IceSSL: ignoring IceSSL.EntropyDaemon because OpenSSL initialization is disabled");
- }
-#endif
- }
- }
-
- _securityTraceLevel = communicator->getProperties()->getPropertyAsInt("IceSSL.Trace.Security");
- _securityTraceCategory = "Security";
- _trustManager = new TrustManager(communicator);
-
- __setNoDelete(false);
-}
-
-IceSSL::SharedInstance::~SharedInstance()
-{
- //
- // Clean up OpenSSL resources.
- //
- IceUtilInternal::MutexPtrLock<IceUtil::Mutex> sync(staticMutex);
-
- if(--instanceCount == 0 && _initOpenSSL)
- {
- //
- // NOTE: We can't destroy the locks here: threads which might have called openssl methods
- // might access openssl locks upon termination (from DllMain/THREAD_DETACHED). Instead,
- // we release the locks in the ~Init() static destructor. See bug #4156.
- //
- //CRYPTO_set_locking_callback(0);
- //CRYPTO_set_id_callback(0);
- //delete[] locks;
- //locks = 0;
-
- CRYPTO_cleanup_all_ex_data();
- RAND_cleanup();
- ERR_free_strings();
- EVP_cleanup();
- }
-}
-
-void
-IceSSL::SharedInstance::initialize()
-{
- if(_initialized)
- {
- return;
- }
-
- try
- {
- const string propPrefix = "IceSSL.";
- PropertiesPtr properties = communicator()->getProperties();
-
- //
- // CheckCertName determines whether we compare the name in a peer's
- // certificate against its hostname.
- //
- _checkCertName = properties->getPropertyAsIntWithDefault(propPrefix + "CheckCertName", 0) > 0;
-
- //
- // VerifyDepthMax establishes the maximum length of a peer's certificate
- // chain, including the peer's certificate. A value of 0 means there is
- // no maximum.
- //
- _verifyDepthMax = properties->getPropertyAsIntWithDefault(propPrefix + "VerifyDepthMax", 2);
-
- //
- // VerifyPeer determines whether certificate validation failures abort a connection.
- //
- _verifyPeer = properties->getPropertyAsIntWithDefault(propPrefix + "VerifyPeer", 2);
-
- //
- // Protocols selects which protocols to enable.
- //
- const int protocols = parseProtocols(properties->getPropertyAsList(propPrefix + "Protocols"));
-
- //
- // Create an SSL context if the application hasn't supplied one.
- //
- if(!_ctx)
- {
- _ctx = SSL_CTX_new(getMethod(protocols));
- if(!_ctx)
- {
- PluginInitializationException ex(__FILE__, __LINE__);
- ex.reason = "IceSSL: unable to create SSL context:\n" + sslErrors();
- throw ex;
- }
-
- //
- // Check for a default directory. We look in this directory for
- // files mentioned in the configuration.
- //
- string defaultDir = properties->getProperty(propPrefix + "DefaultDir");
-
- //
- // If the configuration defines a password, or the application has supplied
- // a password prompt object, then register a password callback. Otherwise,
- // let OpenSSL use its default behavior.
- //
- {
- // TODO: Support quoted value?
- string password = properties->getProperty(propPrefix + "Password");
- if(!password.empty() || _prompt)
- {
- SSL_CTX_set_default_passwd_cb(_ctx, IceSSL_opensslPasswordCallback);
- SSL_CTX_set_default_passwd_cb_userdata(_ctx, this);
- _password = password;
- }
- }
-
- int passwordRetryMax = properties->getPropertyAsIntWithDefault(propPrefix + "PasswordRetryMax", 3);
-
- //
- // Establish the location of CA certificates.
- //
- {
- string caFile = properties->getProperty(propPrefix + "CertAuthFile");
- string caDir = properties->getPropertyWithDefault(propPrefix + "CertAuthDir", defaultDir);
- const char* file = 0;
- const char* dir = 0;
- if(!caFile.empty())
- {
- if(!checkPath(caFile, defaultDir, false))
- {
- PluginInitializationException ex(__FILE__, __LINE__);
- ex.reason = "IceSSL: CA certificate file not found:\n" + caFile;
- throw ex;
- }
- file = caFile.c_str();
- }
- if(!caDir.empty())
- {
- if(!checkPath(caDir, defaultDir, true))
- {
- PluginInitializationException ex(__FILE__, __LINE__);
- ex.reason = "IceSSL: CA certificate directory not found:\n" + caDir;
- throw ex;
- }
- dir = caDir.c_str();
- }
- if(file || dir)
- {
- //
- // The certificate may be stored in an encrypted file, so handle
- // password retries.
- //
- int count = 0;
- int err = 0;
- while(count < passwordRetryMax)
- {
- ERR_clear_error();
- err = SSL_CTX_load_verify_locations(_ctx, file, dir);
- if(err)
- {
- break;
- }
- ++count;
- }
- if(err == 0)
- {
- string msg = "IceSSL: unable to establish CA certificates";
- if(passwordError())
- {
- msg += ":\ninvalid password";
- }
- else
- {
- string err = sslErrors();
- if(!err.empty())
- {
- msg += ":\n" + err;
- }
- }
- PluginInitializationException ex(__FILE__, __LINE__);
- ex.reason = msg;
- throw ex;
- }
- }
- }
-
- //
- // Establish the certificate chains and private keys. One RSA certificate and
- // one DSA certificate are allowed.
- //
- {
-#ifdef _WIN32
- const string sep = ";";
-#else
- const string sep = ":";
-#endif
- string certFile = properties->getProperty(propPrefix + "CertFile");
- string keyFile = properties->getProperty(propPrefix + "KeyFile");
- vector<string>::size_type numCerts = 0;
- if(!certFile.empty())
- {
- vector<string> files;
- if(!IceUtilInternal::splitString(certFile, sep, files) || files.size() > 2)
- {
- PluginInitializationException ex(__FILE__, __LINE__);
- ex.reason = "IceSSL: invalid value for " + propPrefix + "CertFile:\n" + certFile;
- throw ex;
- }
- numCerts = files.size();
- for(vector<string>::iterator p = files.begin(); p != files.end(); ++p)
- {
- string file = *p;
- if(!checkPath(file, defaultDir, false))
- {
- PluginInitializationException ex(__FILE__, __LINE__);
- ex.reason = "IceSSL: certificate file not found:\n" + file;
- throw ex;
- }
- //
- // The certificate may be stored in an encrypted file, so handle
- // password retries.
- //
- int count = 0;
- int err = 0;
- while(count < passwordRetryMax)
- {
- ERR_clear_error();
- err = SSL_CTX_use_certificate_chain_file(_ctx, file.c_str());
- if(err)
- {
- break;
- }
- ++count;
- }
- if(err == 0)
- {
- string msg = "IceSSL: unable to load certificate chain from file " + file;
- if(passwordError())
- {
- msg += ":\ninvalid password";
- }
- else
- {
- string err = sslErrors();
- if(!err.empty())
- {
- msg += ":\n" + err;
- }
- }
- PluginInitializationException ex(__FILE__, __LINE__);
- ex.reason = msg;
- throw ex;
- }
- }
- }
- if(keyFile.empty())
- {
- keyFile = certFile; // Assume the certificate file also contains the private key.
- }
- if(!keyFile.empty())
- {
- vector<string> files;
- if(!IceUtilInternal::splitString(keyFile, sep, files) || files.size() > 2)
- {
- PluginInitializationException ex(__FILE__, __LINE__);
- ex.reason = "IceSSL: invalid value for " + propPrefix + "KeyFile:\n" + keyFile;
- throw ex;
- }
- if(files.size() != numCerts)
- {
- PluginInitializationException ex(__FILE__, __LINE__);
- ex.reason = "IceSSL: " + propPrefix + "KeyFile does not agree with " + propPrefix + "CertFile";
- throw ex;
- }
- for(vector<string>::iterator p = files.begin(); p != files.end(); ++p)
- {
- string file = *p;
- if(!checkPath(file, defaultDir, false))
- {
- PluginInitializationException ex(__FILE__, __LINE__);
- ex.reason = "IceSSL: key file not found:\n" + file;
- throw ex;
- }
- //
- // The private key may be stored in an encrypted file, so handle
- // password retries.
- //
- int count = 0;
- int err = 0;
- while(count < passwordRetryMax)
- {
- ERR_clear_error();
- err = SSL_CTX_use_PrivateKey_file(_ctx, file.c_str(), SSL_FILETYPE_PEM);
- if(err)
- {
- break;
- }
- ++count;
- }
- if(err == 0)
- {
- string msg = "IceSSL: unable to load private key from file " + file;
- if(passwordError())
- {
- msg += ":\ninvalid password";
- }
- else
- {
- string err = sslErrors();
- if(!err.empty())
- {
- msg += ":\n" + err;
- }
- }
- PluginInitializationException ex(__FILE__, __LINE__);
- ex.reason = msg;
- throw ex;
- }
- }
- if(!SSL_CTX_check_private_key(_ctx))
- {
- PluginInitializationException ex(__FILE__, __LINE__);
- ex.reason = "IceSSL: unable to validate private key(s):\n" + sslErrors();
- throw ex;
- }
- }
- }
-
- //
- // Diffie Hellman configuration.
- //
- {
-#ifndef OPENSSL_NO_DH
- _dhParams = new DHParams;
- SSL_CTX_set_options(_ctx, SSL_OP_SINGLE_DH_USE);
- SSL_CTX_set_tmp_dh_callback(_ctx, IceSSL_opensslDHCallback);
-#endif
- //
- // Properties have the following form:
- //
- // ...DH.<keyLength>=file
- //
- const string dhPrefix = propPrefix + "DH.";
- PropertyDict d = properties->getPropertiesForPrefix(dhPrefix);
- if(!d.empty())
- {
-#ifdef OPENSSL_NO_DH
- _logger->warning("IceSSL: OpenSSL is not configured for Diffie Hellman");
-#else
- for(PropertyDict::iterator p = d.begin(); p != d.end(); ++p)
- {
- string s = p->first.substr(dhPrefix.size());
- int keyLength = atoi(s.c_str());
- if(keyLength > 0)
- {
- string file = p->second;
- if(!checkPath(file, defaultDir, false))
- {
- PluginInitializationException ex(__FILE__, __LINE__);
- ex.reason = "IceSSL: DH parameter file not found:\n" + file;
- throw ex;
- }
- if(!_dhParams->add(keyLength, file))
- {
- PluginInitializationException ex(__FILE__, __LINE__);
- ex.reason = "IceSSL: unable to read DH parameter file " + file;
- throw ex;
- }
- }
- }
-#endif
- }
- }
- }
-
- //
- // Store a pointer to ourself for use in OpenSSL callbacks.
- //
- SSL_CTX_set_ex_data(_ctx, 0, this);
-
- //
- // This is necessary for successful interop with Java. Without it, a Java
- // client would fail to reestablish a connection: the server gets the
- // error "session id context uninitialized" and the client receives
- // "SSLHandshakeException: Remote host closed connection during handshake".
- //
- SSL_CTX_set_session_cache_mode(_ctx, SSL_SESS_CACHE_OFF);
-
- //
- // Although we disable session caching, we still need to set a session ID
- // context (ICE-5103). The value can be anything; here we just use the
- // pointer to this SharedInstance object.
- //
- SSL_CTX_set_session_id_context(_ctx, reinterpret_cast<unsigned char*>(this),
- static_cast<unsigned int>(sizeof(this)));
-
- //
- // Select protocols.
- //
- if(protocols != 0)
- {
- setOptions(protocols);
- }
-
- //
- // Establish the cipher list.
- //
- string ciphers = properties->getProperty(propPrefix + "Ciphers");
- if(!ciphers.empty())
- {
- if(!SSL_CTX_set_cipher_list(_ctx, ciphers.c_str()))
- {
- PluginInitializationException ex(__FILE__, __LINE__);
- ex.reason = "IceSSL: unable to set ciphers using `" + ciphers + "':\n" + sslErrors();
- throw ex;
- }
- }
-
- //
- // Determine whether a certificate is required from the peer.
- //
- {
- int sslVerifyMode;
- switch(_verifyPeer)
- {
- case 0:
- sslVerifyMode = SSL_VERIFY_NONE;
- break;
- case 1:
- sslVerifyMode = SSL_VERIFY_PEER;
- break;
- case 2:
- sslVerifyMode = SSL_VERIFY_PEER|SSL_VERIFY_FAIL_IF_NO_PEER_CERT;
- break;
- default:
- {
- PluginInitializationException ex(__FILE__, __LINE__);
- ex.reason = "IceSSL: invalid value for " + propPrefix + "VerifyPeer";
- throw ex;
- }
- }
- SSL_CTX_set_verify(_ctx, sslVerifyMode, IceSSL_opensslVerifyCallback);
- }
- }
- catch(...)
- {
- //
- // We free the SSL context regardless of whether the plugin created it
- // or the application supplied it.
- //
- SSL_CTX_free(_ctx);
- _ctx = 0;
- throw;
- }
-
- _initialized = true;
-}
-
-void
-IceSSL::SharedInstance::context(SSL_CTX* context)
-{
- if(_initialized)
- {
- PluginInitializationException ex(__FILE__, __LINE__);
- ex.reason = "IceSSL: plug-in is already initialized";
- throw ex;
- }
-
- assert(!_ctx);
- _ctx = context;
-}
-
-SSL_CTX*
-IceSSL::SharedInstance::context() const
-{
- return _ctx;
-}
-
-void
-IceSSL::SharedInstance::setCertificateVerifier(const CertificateVerifierPtr& verifier)
-{
- _verifier = verifier;
-}
-
-void
-IceSSL::SharedInstance::setPasswordPrompt(const PasswordPromptPtr& prompt)
-{
- _prompt = prompt;
-}
-
-CommunicatorPtr
-IceSSL::SharedInstance::communicator() const
-{
- return _communicator;
-}
-
-void
-IceSSL::SharedInstance::verifyPeer(SSL* ssl, SOCKET fd, const string& address, const NativeConnectionInfoPtr& info)
-{
- long result = SSL_get_verify_result(ssl);
- if(result != X509_V_OK)
- {
- if(_verifyPeer == 0)
- {
- if(_securityTraceLevel >= 1)
- {
- ostringstream ostr;
- ostr << "IceSSL: ignoring certificate verification failure:\n" << X509_verify_cert_error_string(result);
- _logger->trace(_securityTraceCategory, ostr.str());
- }
- }
- else
- {
- ostringstream ostr;
- ostr << "IceSSL: certificate verification failed:\n" << X509_verify_cert_error_string(result);
- string msg = ostr.str();
- if(_securityTraceLevel >= 1)
- {
- _logger->trace(_securityTraceCategory, msg);
- }
- SecurityException ex(__FILE__, __LINE__);
- ex.reason = msg;
- throw ex;
- }
- }
-
- X509* rawCert = SSL_get_peer_certificate(ssl);
- CertificatePtr cert;
- if(rawCert != 0)
- {
- cert = new Certificate(rawCert);
- }
-
- //
- // For an outgoing connection, we compare the proxy address (if any) against
- // fields in the server's certificate (if any).
- //
- if(cert && !address.empty())
- {
- //
- // Extract the IP addresses and the DNS names from the subject
- // alternative names.
- //
- vector<pair<int, string> > subjectAltNames = cert->getSubjectAlternativeNames();
- vector<string> ipAddresses;
- vector<string> dnsNames;
- for(vector<pair<int, string> >::const_iterator p = subjectAltNames.begin(); p != subjectAltNames.end(); ++p)
- {
- if(p->first == 7)
- {
- ipAddresses.push_back(IceUtilInternal::toLower(p->second));
- }
- else if(p->first == 2)
- {
- dnsNames.push_back(IceUtilInternal::toLower(p->second));
- }
- }
-
- //
- // Compare the peer's address against the common name.
- //
- bool certNameOK = false;
- string dn;
- string addrLower = IceUtilInternal::toLower(address);
- {
- DistinguishedName d = cert->getSubjectDN();
- dn = IceUtilInternal::toLower(string(d));
- string cn = "cn=" + addrLower;
- string::size_type pos = dn.find(cn);
- if(pos != string::npos)
- {
- //
- // Ensure we match the entire common name.
- //
- certNameOK = (pos + cn.size() == dn.size()) || (dn[pos + cn.size()] == ',');
- }
- }
-
- //
- // Compare the peer's address against the dnsName and ipAddress
- // values in the subject alternative name.
- //
- if(!certNameOK)
- {
- certNameOK = find(ipAddresses.begin(), ipAddresses.end(), addrLower) != ipAddresses.end();
- }
- if(!certNameOK)
- {
- certNameOK = find(dnsNames.begin(), dnsNames.end(), addrLower) != dnsNames.end();
- }
-
- //
- // Log a message if the name comparison fails. If CheckCertName is defined,
- // we also raise an exception to abort the connection. Don't log a message if
- // CheckCertName is not defined and a verifier is present.
- //
- if(!certNameOK && (_checkCertName || (_securityTraceLevel >= 1 && !_verifier)))
- {
- ostringstream ostr;
- ostr << "IceSSL: ";
- if(!_checkCertName)
- {
- ostr << "ignoring ";
- }
- ostr << "certificate validation failure:\npeer certificate does not have `" << address
- << "' as its commonName or in its subjectAltName extension";
- if(!dn.empty())
- {
- ostr << "\nSubject DN: " << dn;
- }
- if(!dnsNames.empty())
- {
- ostr << "\nDNS names found in certificate: ";
- for(vector<string>::const_iterator p = dnsNames.begin(); p != dnsNames.end(); ++p)
- {
- if(p != dnsNames.begin())
- {
- ostr << ", ";
- }
- ostr << *p;
- }
- }
- if(!ipAddresses.empty())
- {
- ostr << "\nIP addresses found in certificate: ";
- for(vector<string>::const_iterator p = ipAddresses.begin(); p != ipAddresses.end(); ++p)
- {
- if(p != ipAddresses.begin())
- {
- ostr << ", ";
- }
- ostr << *p;
- }
- }
- string msg = ostr.str();
- if(_securityTraceLevel >= 1)
- {
- Trace out(_logger, _securityTraceCategory);
- out << msg;
- }
- if(_checkCertName)
- {
- SecurityException ex(__FILE__, __LINE__);
- ex.reason = msg;
- throw ex;
- }
- }
- }
-
- if(_verifyDepthMax > 0 && static_cast<int>(info->certs.size()) > _verifyDepthMax)
- {
- ostringstream ostr;
- ostr << (info->incoming ? "incoming" : "outgoing") << " connection rejected:\n"
- << "length of peer's certificate chain (" << info->certs.size() << ") exceeds maximum of "
- << _verifyDepthMax;
- string msg = ostr.str();
- if(_securityTraceLevel >= 1)
- {
- _logger->trace(_securityTraceCategory, msg + "\n" + IceInternal::fdToString(fd));
- }
- SecurityException ex(__FILE__, __LINE__);
- ex.reason = msg;
- throw ex;
- }
-
- if(!_trustManager->verify(info))
- {
- string msg = string(info->incoming ? "incoming" : "outgoing") + " connection rejected by trust manager";
- if(_securityTraceLevel >= 1)
- {
- _logger->trace(_securityTraceCategory, msg + "\n" + IceInternal::fdToString(fd));
- }
- SecurityException ex(__FILE__, __LINE__);
- ex.reason = msg;
- throw ex;
- }
-
- if(_verifier && !_verifier->verify(info))
- {
- string msg = string(info->incoming ? "incoming" : "outgoing") + " connection rejected by certificate verifier";
- if(_securityTraceLevel >= 1)
- {
- _logger->trace(_securityTraceCategory, msg + "\n" + IceInternal::fdToString(fd));
- }
- SecurityException ex(__FILE__, __LINE__);
- ex.reason = msg;
- throw ex;
- }
-}
-
-string
-IceSSL::SharedInstance::sslErrors() const
-{
- return getSslErrors(_securityTraceLevel >= 1);
-}
-
-void
-IceSSL::SharedInstance::destroy()
-{
- if(_ctx)
- {
- SSL_CTX_free(_ctx);
- }
-}
-
-string
-IceSSL::SharedInstance::password(bool /*encrypting*/)
-{
- if(_prompt)
- {
- try
- {
- return _prompt->getPassword();
- }
- catch(...)
- {
- //
- // Don't allow exceptions to cross an OpenSSL boundary.
- //
- return string();
- }
- }
- else
- {
- return _password;
- }
-}
-
-int
-IceSSL::SharedInstance::verifyCallback(int ok, SSL* ssl, X509_STORE_CTX* c)
-{
- if(!ok && _securityTraceLevel >= 1)
- {
- X509* cert = X509_STORE_CTX_get_current_cert(c);
- int err = X509_STORE_CTX_get_error(c);
- char buf[256];
-
- Trace out(_logger, _securityTraceCategory);
- out << "certificate verification failure\n";
-
- X509_NAME_oneline(X509_get_issuer_name(cert), buf, static_cast<int>(sizeof(buf)));
- out << "issuer = " << buf << '\n';
- X509_NAME_oneline(X509_get_subject_name(cert), buf, static_cast<int>(sizeof(buf)));
- out << "subject = " << buf << '\n';
- out << "depth = " << X509_STORE_CTX_get_error_depth(c) << '\n';
- out << "error = " << X509_verify_cert_error_string(err) << '\n';
- out << IceInternal::fdToString(SSL_get_fd(ssl));
- }
- return ok;
-}
-
-#ifndef OPENSSL_NO_DH
-DH*
-IceSSL::SharedInstance::dhParams(int keyLength)
-{
- return _dhParams->get(keyLength);
-}
-#endif
-
-int
-IceSSL::SharedInstance::parseProtocols(const StringSeq& protocols)
-{
- int v = 0;
-
- for(Ice::StringSeq::const_iterator p = protocols.begin(); p != protocols.end(); ++p)
- {
- string prot = *p;
-
- if(prot == "ssl3" || prot == "sslv3")
- {
- v |= SSLv3;
- }
- else if(prot == "tls" || prot == "tls1" || prot == "tlsv1" || prot == "tls1_0" || prot == "tlsv1_0")
- {
- v |= TLSv1_0;
- }
- else if(prot == "tls1_1" || prot == "tlsv1_1")
- {
- v |= TLSv1_1;
- }
- else if(prot == "tls1_2" || prot == "tlsv1_2")
- {
- v |= TLSv1_2;
- }
- else
- {
- PluginInitializationException ex(__FILE__, __LINE__);
- ex.reason = "IceSSL: unrecognized protocol `" + prot + "'";
- throw ex;
- }
- }
-
- return v;
-}
-
-SSL_METHOD*
-IceSSL::SharedInstance::getMethod(int /*protocols*/)
-{
- //
- // Despite its name, the SSLv23 method can negotiate SSL3, TLS1.0, TLS1.1, and TLS1.2.
- // We use the const_cast for backward compatibility with older OpenSSL releases.
- //
- SSL_METHOD* meth = const_cast<SSL_METHOD*>(SSLv23_method());
-
- /*
- * Early versions of OpenSSL 1.0.1 would not negotiate a TLS1.2 connection using
- * the SSLv23 method. You can enable the code below to override the method.
- if(protocols & TLSv1_2)
- {
- meth = const_cast<SSL_METHOD*>(TLSv1_2_method());
- }
- */
-
- return meth;
-}
-
-void
-IceSSL::SharedInstance::setOptions(int protocols)
-{
- long opts = SSL_OP_NO_SSLv2; // SSLv2 is not supported.
- if(!(protocols & SSLv3))
- {
- opts |= SSL_OP_NO_SSLv3;
- }
- if(!(protocols & TLSv1_0))
- {
- opts |= SSL_OP_NO_TLSv1;
- }
-#ifdef SSL_OP_NO_TLSv1_1
- if(!(protocols & TLSv1_1))
- {
- opts |= SSL_OP_NO_TLSv1_1;
- //
- // The value of SSL_OP_NO_TLSv1_1 changed between 1.0.1a and 1.0.1b.
- //
- if(SSL_OP_NO_TLSv1_1 == 0x00000400L)
- {
- opts |= 0x10000000L; // New value of SSL_OP_NO_TLSv1_1.
- }
- }
-#endif
-#ifdef SSL_OP_NO_TLSv1_2
- if(!(protocols & TLSv1_2))
- {
- opts |= SSL_OP_NO_TLSv1_2;
- }
-#endif
- SSL_CTX_set_options(_ctx, opts);
-}
-
-IceSSL::Instance::Instance(const SharedInstancePtr& sharedInstance, Ice::Short type, const std::string& protocol) :
- ProtocolInstance(sharedInstance->communicator(), type, protocol),
- _sharedInstance(sharedInstance)
+IceSSL::Instance::Instance(const SSLEnginePtr& engine, Short type, const string& protocol) :
+ ProtocolInstance(engine->communicator(), type, protocol),
+ _engine(engine)
{
_securityTraceLevel = properties()->getPropertyAsInt("IceSSL.Trace.Security");
_securityTraceCategory = "Security";
@@ -1157,28 +32,10 @@ IceSSL::Instance::~Instance()
{
}
-void
-IceSSL::Instance::traceConnection(SSL* ssl, bool incoming)
+bool
+IceSSL::Instance::initialized() const
{
- Trace out(_logger, _securityTraceCategory);
- out << "SSL summary for " << (incoming ? "incoming" : "outgoing") << " connection\n";
-
- //
- // The const_cast is necesary because Solaris still uses OpenSSL 0.9.7.
- //
- //const SSL_CIPHER *cipher = SSL_get_current_cipher(ssl);
- SSL_CIPHER *cipher = const_cast<SSL_CIPHER*>(SSL_get_current_cipher(ssl));
- if(!cipher)
- {
- out << "unknown cipher\n";
- }
- else
- {
- out << "cipher = " << SSL_CIPHER_get_name(cipher) << "\n";
- out << "bits = " << SSL_CIPHER_get_bits(cipher, 0) << "\n";
- out << "protocol = " << SSL_get_version(ssl) << "\n";
- }
- out << IceInternal::fdToString(SSL_get_fd(ssl));
+ return _engine->initialized();
}
int
@@ -1192,4 +49,3 @@ IceSSL::Instance::securityTraceCategory() const
{
return _securityTraceCategory;
}
-