diff options
Diffstat (limited to 'cpp/src/IceSSL/OpenSSLEngine.cpp')
-rw-r--r-- | cpp/src/IceSSL/OpenSSLEngine.cpp | 386 |
1 files changed, 217 insertions, 169 deletions
diff --git a/cpp/src/IceSSL/OpenSSLEngine.cpp b/cpp/src/IceSSL/OpenSSLEngine.cpp index f4e4d196efa..3c1f54a9bc1 100644 --- a/cpp/src/IceSSL/OpenSSLEngine.cpp +++ b/cpp/src/IceSSL/OpenSSLEngine.cpp @@ -30,6 +30,7 @@ #include <openssl/rand.h> #include <openssl/err.h> #include <openssl/ssl.h> +#include <openssl/pkcs12.h> using namespace std; using namespace Ice; @@ -163,7 +164,8 @@ passwordError() return (reason == PEM_R_BAD_BASE64_DECODE || reason == PEM_R_BAD_DECRYPT || reason == PEM_R_BAD_PASSWORD_READ || - reason == PEM_R_PROBLEMS_GETTING_PASSWORD); + reason == PEM_R_PROBLEMS_GETTING_PASSWORD || + reason == PKCS12_R_MAC_VERIFY_FAILURE); } } @@ -245,24 +247,21 @@ OpenSSLEngine::OpenSSLEngine(const CommunicatorPtr& communicator) : if(!IceUtilInternal::splitString(randFiles, IceUtilInternal::pathsep, files)) { - PluginInitializationException ex(__FILE__, __LINE__); - ex.reason = "IceSSL: invalid value for IceSSL.Random:\n" + randFiles; - throw ex; + throw PluginInitializationException(__FILE__, __LINE__, + "IceSSL: invalid value for IceSSL.Random:\n" + randFiles); } 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; + throw PluginInitializationException(__FILE__, __LINE__, + "IceSSL: entropy data file not found:\n" + file); } if(!RAND_load_file(file.c_str(), 1024)) { - PluginInitializationException ex(__FILE__, __LINE__); - ex.reason = "IceSSL: unable to load entropy data from " + file; - throw ex; + throw PluginInitializationException(__FILE__, __LINE__, + "IceSSL: unable to load entropy data from " + file); } } } @@ -276,9 +275,8 @@ OpenSSLEngine::OpenSSLEngine(const CommunicatorPtr& communicator) : { if(RAND_egd(entropyDaemon.c_str()) <= 0) { - PluginInitializationException ex(__FILE__, __LINE__); - ex.reason = "IceSSL: EGD failure using file " + entropyDaemon; - throw ex; + throw PluginInitializationException(__FILE__, __LINE__, + "IceSSL: EGD failure using file " + entropyDaemon); } } # endif @@ -366,9 +364,8 @@ OpenSSLEngine::initialize() _ctx = SSL_CTX_new(getMethod(protocols)); if(!_ctx) { - PluginInitializationException ex(__FILE__, __LINE__); - ex.reason = "IceSSL: unable to create SSL context:\n" + sslErrors(); - throw ex; + throw PluginInitializationException(__FILE__, __LINE__, + "IceSSL: unable to create SSL context:\n" + sslErrors()); } // @@ -407,9 +404,8 @@ OpenSSLEngine::initialize() { if(!checkPath(caFile, defaultDir, false)) { - PluginInitializationException ex(__FILE__, __LINE__); - ex.reason = "IceSSL: CA certificate file not found:\n" + caFile; - throw ex; + throw PluginInitializationException(__FILE__, __LINE__, + "IceSSL: CA certificate file not found:\n" + caFile); } file = caFile.c_str(); } @@ -417,9 +413,8 @@ OpenSSLEngine::initialize() { if(!checkPath(caDir, defaultDir, true)) { - PluginInitializationException ex(__FILE__, __LINE__); - ex.reason = "IceSSL: CA certificate directory not found:\n" + caDir; - throw ex; + throw PluginInitializationException(__FILE__, __LINE__, + "IceSSL: CA certificate directory not found:\n" + caDir); } dir = caDir.c_str(); } @@ -430,18 +425,17 @@ OpenSSLEngine::initialize() // password retries. // int count = 0; - int err = 0; + int success = 0; while(count < passwordRetryMax) { ERR_clear_error(); - err = SSL_CTX_load_verify_locations(_ctx, file, dir); - if(err) + if((success = SSL_CTX_load_verify_locations(_ctx, file, dir))|| !passwordError()) { break; } ++count; } - if(err == 0) + if(!success) { string msg = "IceSSL: unable to establish CA certificates"; if(passwordError()) @@ -456,9 +450,7 @@ OpenSSLEngine::initialize() msg += ":\n" + err; } } - PluginInitializationException ex(__FILE__, __LINE__); - ex.reason = msg; - throw ex; + throw PluginInitializationException(__FILE__, __LINE__, msg); } } } @@ -467,137 +459,232 @@ OpenSSLEngine::initialize() // Establish the certificate chains and private keys. One RSA certificate and // one DSA certificate are allowed. // + string certFile = properties->getProperty(propPrefix + "CertFile"); + string keyFile = properties->getProperty(propPrefix + "KeyFile"); + bool keyLoaded = false; + + vector<string>::size_type numCerts = 0; + if(!certFile.empty()) { - 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, IceUtilInternal::pathsep, files) || files.size() > 2) { - vector<string> files; - if(!IceUtilInternal::splitString(certFile, IceUtilInternal::pathsep, files) || files.size() > 2) + PluginInitializationException ex(__FILE__, __LINE__, + "IceSSL: invalid value for " + propPrefix + "CertFile:\n" + certFile); + } + 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: invalid value for " + propPrefix + "CertFile:\n" + certFile; - throw ex; + PluginInitializationException ex(__FILE__, __LINE__, + "IceSSL: certificate file not found:\n" + file); + } + // + // First we try to load the certificate using PKCS12 format if that fails + // we fallback to PEM format. + // + FILE* f = fopen(file.c_str(), "rb"); + if(!f) + { + throw PluginInitializationException(__FILE__, __LINE__, + "IceSSL: unable to load certificate chain from file " + file + "\n" + + IceUtilInternal::lastErrorToString()); } - numCerts = files.size(); - for(vector<string>::iterator p = files.begin(); p != files.end(); ++p) + + int success = 0; + + PKCS12* p12 = d2i_PKCS12_fp(f, 0); + fclose(f); + if(p12) { - string file = *p; - if(!checkPath(file, defaultDir, false)) + X509* cert = 0; + EVP_PKEY* key = 0; + STACK_OF(X509)* chain = 0; + + int count = 0; + try { - PluginInitializationException ex(__FILE__, __LINE__); - ex.reason = "IceSSL: certificate file not found:\n" + file; - throw ex; + while(count < passwordRetryMax) + { + ERR_clear_error(); + if(!(success = PKCS12_parse(p12, password(false).c_str(), &key, &cert, &chain))) + { + if(passwordError()) + { + count++; + continue; + } + break; + } + + if(!cert || !SSL_CTX_use_certificate(_ctx, cert)) + { + throw PluginInitializationException(__FILE__, __LINE__, + "IceSSL: unable to establish SSL certificate:\n" + + (cert ? sslErrors() : "certificate not found")); + } + + if(!key || !SSL_CTX_use_PrivateKey(_ctx, key)) + { + throw PluginInitializationException(__FILE__, __LINE__, + "IceSSL: unable to establish SSL private key:\n" + + (key ? sslErrors() : "key not found")); + } + keyLoaded = true; + + if(chain && sk_X509_num(chain)) + { + for(int i = 0; i < sk_X509_num(chain); i++) + { + if(!SSL_CTX_add_extra_chain_cert(_ctx, sk_X509_value(chain, i))) + { + throw PluginInitializationException(__FILE__, __LINE__, + "IceSSL: unable to add extra SSL certificate:\n" + sslErrors()); + } + } + } + + if(chain) + { + sk_X509_pop_free(chain, X509_free); + } + assert(key && cert); + EVP_PKEY_free(key); + X509_free(cert); + break; + } + PKCS12_free(p12); + } + catch(...) + { + PKCS12_free(p12); + if(chain) + { + sk_X509_pop_free(chain, X509_free); + } + + if(key) + { + EVP_PKEY_free(key); + } + + if(cert) + { + X509_free(cert); + } + throw; } + } + else + { // // 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) + if(!(success = SSL_CTX_use_certificate_chain_file(_ctx, file.c_str()))) { - break; + if(passwordError()) + { + count++; + continue; + } } - ++count; + count++; } - if(err == 0) + } + + if(!success) + { + string msg = "IceSSL: unable to load certificate chain from file " + file; + if(passwordError()) { - string msg = "IceSSL: unable to load certificate chain from file " + file; - if(passwordError()) - { - msg += ":\ninvalid password"; - } - else + msg += ":\ninvalid password"; + } + else + { + string err = sslErrors(); + if(!err.empty()) { - string err = sslErrors(); - if(!err.empty()) - { - msg += ":\n" + err; - } + msg += ":\n" + err; } - PluginInitializationException ex(__FILE__, __LINE__); - ex.reason = msg; - throw ex; } + throw PluginInitializationException(__FILE__, __LINE__, msg); } } - if(keyFile.empty()) + } + + if(keyFile.empty()) + { + keyFile = certFile; // Assume the certificate file also contains the private key. + } + if(!keyLoaded && !keyFile.empty()) + { + vector<string> files; + if(!IceUtilInternal::splitString(keyFile, IceUtilInternal::pathsep, files) || files.size() > 2) + { + throw PluginInitializationException(__FILE__, __LINE__, + "IceSSL: invalid value for " + propPrefix + "KeyFile:\n" + keyFile); + } + if(files.size() != numCerts) { - keyFile = certFile; // Assume the certificate file also contains the private key. + throw PluginInitializationException(__FILE__, __LINE__, + "IceSSL: " + propPrefix + "KeyFile does not agree with " + propPrefix + "CertFile"); } - if(!keyFile.empty()) + for(vector<string>::iterator p = files.begin(); p != files.end(); ++p) { - vector<string> files; - if(!IceUtilInternal::splitString(keyFile, IceUtilInternal::pathsep, files) || files.size() > 2) - { - PluginInitializationException ex(__FILE__, __LINE__); - ex.reason = "IceSSL: invalid value for " + propPrefix + "KeyFile:\n" + keyFile; - throw ex; - } - if(files.size() != numCerts) + string file = *p; + if(!checkPath(file, defaultDir, false)) { - PluginInitializationException ex(__FILE__, __LINE__); - ex.reason = "IceSSL: " + propPrefix + "KeyFile does not agree with " + propPrefix + "CertFile"; - throw ex; + throw PluginInitializationException(__FILE__, __LINE__, + "IceSSL: key file not found:\n" + file); } - for(vector<string>::iterator p = files.begin(); p != files.end(); ++p) + // + // The private key may be stored in an encrypted file, so handle + // password retries. + // + int count = 0; + int err = 0; + while(count < passwordRetryMax) { - string file = *p; - if(!checkPath(file, defaultDir, false)) + ERR_clear_error(); + err = SSL_CTX_use_PrivateKey_file(_ctx, file.c_str(), SSL_FILETYPE_PEM); + if(err) { - PluginInitializationException ex(__FILE__, __LINE__); - ex.reason = "IceSSL: key file not found:\n" + file; - throw ex; + break; } - // - // The private key may be stored in an encrypted file, so handle - // password retries. - // - int count = 0; - int err = 0; - while(count < passwordRetryMax) + ++count; + } + if(err == 0) + { + string msg = "IceSSL: unable to load private key from file " + file; + if(passwordError()) { - ERR_clear_error(); - err = SSL_CTX_use_PrivateKey_file(_ctx, file.c_str(), SSL_FILETYPE_PEM); - if(err) - { - break; - } - ++count; + msg += ":\ninvalid password"; } - if(err == 0) + else { - string msg = "IceSSL: unable to load private key from file " + file; - if(passwordError()) - { - msg += ":\ninvalid password"; - } - else + string err = sslErrors(); + if(!err.empty()) { - string err = sslErrors(); - if(!err.empty()) - { - msg += ":\n" + err; - } + 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; + throw PluginInitializationException(__FILE__, __LINE__, msg); } } + keyLoaded = true; + } + + if(keyLoaded && !SSL_CTX_check_private_key(_ctx)) + { + throw PluginInitializationException(__FILE__, __LINE__, + "IceSSL: unable to validate private key(s):\n" + sslErrors()); } // @@ -630,15 +717,13 @@ OpenSSLEngine::initialize() 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; + throw PluginInitializationException(__FILE__, __LINE__, + "IceSSL: DH parameter file not found:\n" + file); } if(!_dhParams->add(keyLength, file)) { - PluginInitializationException ex(__FILE__, __LINE__); - ex.reason = "IceSSL: unable to read DH parameter file " + file; - throw ex; + throw PluginInitializationException(__FILE__, __LINE__, + "IceSSL: unable to read DH parameter file " + file); } } } @@ -684,9 +769,8 @@ OpenSSLEngine::initialize() { 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; + throw PluginInitializationException(__FILE__, __LINE__, + "IceSSL: unable to set ciphers using `" + ciphers + "':\n" + sslErrors()); } } @@ -694,7 +778,7 @@ OpenSSLEngine::initialize() // Determine whether a certificate is required from the peer. // { - int sslVerifyMode = SSL_VERIFY_PEER|SSL_VERIFY_FAIL_IF_NO_PEER_CERT; + int sslVerifyMode = SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT;; switch(getVerifyPeer()) { case 0: @@ -704,7 +788,7 @@ OpenSSLEngine::initialize() sslVerifyMode = SSL_VERIFY_PEER; break; case 2: - sslVerifyMode = SSL_VERIFY_PEER|SSL_VERIFY_FAIL_IF_NO_PEER_CERT; + sslVerifyMode = SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT; break; default: { @@ -733,9 +817,7 @@ OpenSSLEngine::context(SSL_CTX* context) { if(initialized()) { - PluginInitializationException ex(__FILE__, __LINE__); - ex.reason = "IceSSL: plug-in is already initialized"; - throw ex; + throw PluginInitializationException(__FILE__, __LINE__, "IceSSL: plug-in is already initialized"); } assert(!_ctx); @@ -748,38 +830,6 @@ OpenSSLEngine::context() const return _ctx; } -void -OpenSSLEngine::verifyPeer(SSL* ssl, SOCKET fd, const string& address, const NativeConnectionInfoPtr& info) -{ - long result = SSL_get_verify_result(ssl); - if(result != X509_V_OK) - { - if(getVerifyPeer() == 0) - { - if(securityTraceLevel() >= 1) - { - ostringstream ostr; - ostr << "IceSSL: ignoring certificate verification failure:\n" << X509_verify_cert_error_string(result); - getLogger()->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) - { - getLogger()->trace(securityTraceCategory(), msg); - } - SecurityException ex(__FILE__, __LINE__); - ex.reason = msg; - throw ex; - } - } - SSLEngine::verifyPeer(fd, address, info); -} - string OpenSSLEngine::sslErrors() const { @@ -853,9 +903,7 @@ OpenSSLEngine::parseProtocols(const StringSeq& protocols) const } else { - PluginInitializationException ex(__FILE__, __LINE__); - ex.reason = "IceSSL: unrecognized protocol `" + prot + "'"; - throw ex; + throw PluginInitializationException(__FILE__, __LINE__, "IceSSL: unrecognized protocol `" + prot + "'"); } } |