summaryrefslogtreecommitdiff
path: root/cpp/src/IceSSL/Instance.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'cpp/src/IceSSL/Instance.cpp')
-rw-r--r--cpp/src/IceSSL/Instance.cpp534
1 files changed, 269 insertions, 265 deletions
diff --git a/cpp/src/IceSSL/Instance.cpp b/cpp/src/IceSSL/Instance.cpp
index 9c4875f6267..47f40cf1e64 100644
--- a/cpp/src/IceSSL/Instance.cpp
+++ b/cpp/src/IceSSL/Instance.cpp
@@ -128,6 +128,7 @@ passwordError()
IceSSL::Instance::Instance(const CommunicatorPtr& communicator) :
_logger(communicator->getLogger()),
+ _initialized(false),
_ctx(0)
{
__setNoDelete(true);
@@ -264,218 +265,96 @@ IceSSL::Instance::~Instance()
void
IceSSL::Instance::initialize()
{
- if(_ctx)
+ if(_initialized)
{
return;
}
- _ctx = SSL_CTX_new(SSLv23_method());
- if(!_ctx)
- {
- PluginInitializationException ex(__FILE__, __LINE__);
- ex.reason = "IceSSL: unable to create SSL context:\n" + sslErrors();
- throw ex;
- }
-
try
{
- //
- // 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);
-
- PropertiesPtr properties = communicator()->getProperties();
const string propPrefix = "IceSSL.";
-
- //
- // Check for a default directory. We look in this directory for
- // files mentioned in the configuration.
- //
- {
- _defaultDir = properties->getProperty(propPrefix + "DefaultDir");
- }
-
- //
- // Select protocols.
- //
- {
- StringSeq protocols = properties->getPropertyAsList(propPrefix + "Protocols");
- if(!protocols.empty())
- {
- parseProtocols(protocols);
- }
- }
+ 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;
- }
+ _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);
- }
+ _verifyDepthMax = properties->getPropertyAsIntWithDefault(propPrefix + "VerifyDepthMax", 2);
//
- // Determine whether a certificate is required from the peer.
+ // Create an SSL context if the application hasn't supplied one.
//
+ if(!_ctx)
{
- int verifyPeer = properties->getPropertyAsIntWithDefault(propPrefix + "VerifyPeer", 2);
- 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:
+ _ctx = SSL_CTX_new(SSLv23_method());
+ if(!_ctx)
{
PluginInitializationException ex(__FILE__, __LINE__);
- ex.reason = "IceSSL: invalid value for " + propPrefix + "VerifyPeer";
+ ex.reason = "IceSSL: unable to create SSL context:\n" + sslErrors();
throw ex;
}
- }
- SSL_CTX_set_verify(_ctx, sslVerifyMode, IceSSL_opensslVerifyCallback);
- }
- //
- // 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);
+ //
+ // Check for a default directory. We look in this directory for
+ // files mentioned in the configuration.
+ //
+ string defaultDir = properties->getProperty(propPrefix + "DefaultDir");
- //
- // 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 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.
+ //
{
- if(!checkPath(caDir, _defaultDir, true))
+ // TODO: Support quoted value?
+ string password = properties->getProperty(propPrefix + "Password");
+ if(!password.empty() || _prompt)
{
- PluginInitializationException ex(__FILE__, __LINE__);
- ex.reason = "IceSSL: CA certificate directory not found:\n" + caDir;
- throw ex;
+ SSL_CTX_set_default_passwd_cb(_ctx, IceSSL_opensslPasswordCallback);
+ SSL_CTX_set_default_passwd_cb_userdata(_ctx, this);
+ _password = password;
}
- dir = caDir.c_str();
}
- if(file || dir)
+
+ int passwordRetryMax = properties->getPropertyAsIntWithDefault(propPrefix + "PasswordRetryMax", 3);
+
+ //
+ // Establish the location of CA certificates.
+ //
{
- //
- // The certificate may be stored in an encrypted file, so handle
- // password retries.
- //
- int count = 0;
- int err = 0;
- while(count < passwordRetryMax)
+ string caFile = properties->getProperty(propPrefix + "CertAuthFile");
+ string caDir = properties->getPropertyWithDefault(propPrefix + "CertAuthDir", defaultDir);
+ const char* file = 0;
+ const char* dir = 0;
+ if(!caFile.empty())
{
- ERR_clear_error();
- err = SSL_CTX_load_verify_locations(_ctx, file, dir);
- if(err || !passwordError())
+ if(!checkPath(caFile, defaultDir, false))
{
- break;
+ PluginInitializationException ex(__FILE__, __LINE__);
+ ex.reason = "IceSSL: CA certificate file not found:\n" + caFile;
+ throw ex;
}
- ++count;
+ file = caFile.c_str();
}
- if(err == 0)
+ if(!caDir.empty())
{
- 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))
+ if(!checkPath(caDir, defaultDir, true))
{
PluginInitializationException ex(__FILE__, __LINE__);
- ex.reason = "IceSSL: certificate file not found:\n" + file;
+ 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.
@@ -485,7 +364,7 @@ IceSSL::Instance::initialize()
while(count < passwordRetryMax)
{
ERR_clear_error();
- err = SSL_CTX_use_certificate_chain_file(_ctx, file.c_str());
+ err = SSL_CTX_load_verify_locations(_ctx, file, dir);
if(err || !passwordError())
{
break;
@@ -494,7 +373,7 @@ IceSSL::Instance::initialize()
}
if(err == 0)
{
- string msg = "IceSSL: unable to load certificate chain from file " + file;
+ string msg = "IceSSL: unable to establish CA certificates";
if(passwordError())
{
msg += ":\ninvalid password";
@@ -513,159 +392,284 @@ IceSSL::Instance::initialize()
}
}
}
- if(keyFile.empty())
- {
- keyFile = certFile; // Assume the certificate file also contains the private key.
- }
- if(!keyFile.empty())
+
+ //
+ // Establish the certificate chains and private keys. One RSA certificate and
+ // one DSA certificate are allowed.
+ //
{
- vector<string> files;
- if(!IceUtilInternal::splitString(keyFile, sep, files) || files.size() > 2)
+#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())
{
- PluginInitializationException ex(__FILE__, __LINE__);
- ex.reason = "IceSSL: invalid value for " + propPrefix + "KeyFile:\n" + keyFile;
- throw ex;
+ 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 || !passwordError())
+ {
+ 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(files.size() != numCerts)
+ if(keyFile.empty())
{
- PluginInitializationException ex(__FILE__, __LINE__);
- ex.reason = "IceSSL: " + propPrefix + "KeyFile does not agree with " + propPrefix + "CertFile";
- throw ex;
+ keyFile = certFile; // Assume the certificate file also contains the private key.
}
- for(vector<string>::iterator p = files.begin(); p != files.end(); ++p)
+ if(!keyFile.empty())
{
- string file = *p;
- if(!checkPath(file, _defaultDir, false))
+ vector<string> files;
+ if(!IceUtilInternal::splitString(keyFile, sep, files) || files.size() > 2)
{
PluginInitializationException ex(__FILE__, __LINE__);
- ex.reason = "IceSSL: key file not found:\n" + file;
+ ex.reason = "IceSSL: invalid value for " + propPrefix + "KeyFile:\n" + keyFile;
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)
+ if(files.size() != numCerts)
{
- ERR_clear_error();
- err = SSL_CTX_use_PrivateKey_file(_ctx, file.c_str(), SSL_FILETYPE_PEM);
- if(err || !passwordError())
- {
- break;
- }
- ++count;
+ PluginInitializationException ex(__FILE__, __LINE__);
+ ex.reason = "IceSSL: " + propPrefix + "KeyFile does not agree with " + propPrefix + "CertFile";
+ throw ex;
}
- if(err == 0)
+ for(vector<string>::iterator p = files.begin(); p != files.end(); ++p)
{
- string msg = "IceSSL: unable to load private key from file " + file;
- if(passwordError())
+ string file = *p;
+ if(!checkPath(file, defaultDir, false))
{
- msg += ":\ninvalid password";
+ PluginInitializationException ex(__FILE__, __LINE__);
+ ex.reason = "IceSSL: key file not found:\n" + file;
+ throw ex;
}
- else
+ //
+ // The private key may be stored in an encrypted file, so handle
+ // password retries.
+ //
+ int count = 0;
+ int err = 0;
+ while(count < passwordRetryMax)
{
- string err = sslErrors();
- if(!err.empty())
+ ERR_clear_error();
+ err = SSL_CTX_use_PrivateKey_file(_ctx, file.c_str(), SSL_FILETYPE_PEM);
+ if(err || !passwordError())
{
- msg += ":\n" + 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 = msg;
+ ex.reason = "IceSSL: unable to validate private key(s):\n" + sslErrors();
throw ex;
}
}
- if(!SSL_CTX_check_private_key(_ctx))
+ }
+
+ //
+ // 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())
{
- PluginInitializationException ex(__FILE__, __LINE__);
- ex.reason = "IceSSL: unable to validate private key(s):\n" + sslErrors();
- throw ex;
+#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);
+
+ //
+ // Select protocols.
+ //
+ StringSeq protocols = properties->getPropertyAsList(propPrefix + "Protocols");
+ if(!protocols.empty())
+ {
+ parseProtocols(protocols);
+ }
+
+ //
// Establish the cipher list.
//
+ string ciphers = properties->getProperty(propPrefix + "Ciphers");
+ if(!ciphers.empty())
{
- string ciphers = properties->getProperty(propPrefix + "Ciphers");
- if(!ciphers.empty())
+ if(!SSL_CTX_set_cipher_list(_ctx, ciphers.c_str()))
{
- 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;
- }
+ PluginInitializationException ex(__FILE__, __LINE__);
+ ex.reason = "IceSSL: unable to set ciphers using `" + ciphers + "':\n" + sslErrors();
+ throw ex;
}
}
//
- // Diffie Hellman configuration.
+ // Determine whether a certificate is required from the peer.
//
{
-#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())
+ int verifyPeer = properties->getPropertyAsIntWithDefault(propPrefix + "VerifyPeer", 2);
+ int sslVerifyMode;
+ switch(verifyPeer)
{
-#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
+ 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::Instance::context(SSL_CTX* context)
{
- if(_ctx)
+ if(_initialized)
{
PluginInitializationException ex(__FILE__, __LINE__);
ex.reason = "IceSSL: plug-in is already initialized";
throw ex;
}
+ assert(!_ctx);
_ctx = context;
}