summaryrefslogtreecommitdiff
path: root/cpp/src/Ice/SslSystemOpenSSL.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'cpp/src/Ice/SslSystemOpenSSL.cpp')
-rw-r--r--cpp/src/Ice/SslSystemOpenSSL.cpp3092
1 files changed, 1546 insertions, 1546 deletions
diff --git a/cpp/src/Ice/SslSystemOpenSSL.cpp b/cpp/src/Ice/SslSystemOpenSSL.cpp
index bd7302d94df..9ed157eaae7 100644
--- a/cpp/src/Ice/SslSystemOpenSSL.cpp
+++ b/cpp/src/Ice/SslSystemOpenSSL.cpp
@@ -1,1546 +1,1546 @@
-// **********************************************************************
-//
-// Copyright (c) 2001
-// MutableRealms, Inc.
-// Huntsville, AL, USA
-//
-// All Rights Reserved
-//
-// **********************************************************************
-#ifdef WIN32
-#pragma warning(disable:4786)
-#endif
-
-#include <sstream>
-#include <openssl/err.h>
-#include <openssl/e_os.h>
-#include <openssl/rand.h>
-#include <Ice/Security.h>
-#include <Ice/SslSystem.h>
-#include <Ice/SslException.h>
-#include <Ice/SslConnectionOpenSSLClient.h>
-#include <Ice/SslConnectionOpenSSLServer.h>
-#include <Ice/SslConfig.h>
-
-using namespace std;
-
-namespace IceSecurity
-{
-
-namespace Ssl
-{
-
-namespace OpenSSL
-{
-
-//
-// TODO: These Diffie-Hellman params have been blatantly stolen from
-// OpenSSL's demo programs. We SHOULD define our own here, but
-// these will suffice for testing purposes. Please note, these
-// are not keys themselves, simply a DH Group that allows OpenSSL
-// to create Diffie-Hellman keys.
-//
-
-// Instantiation of temporary Diffie-Hellman 512bit key.
-unsigned char System::_tempDiffieHellman512p[] =
-{
- 0xDA,0x58,0x3C,0x16,0xD9,0x85,0x22,0x89,0xD0,0xE4,0xAF,0x75,
- 0x6F,0x4C,0xCA,0x92,0xDD,0x4B,0xE5,0x33,0xB8,0x04,0xFB,0x0F,
- 0xED,0x94,0xEF,0x9C,0x8A,0x44,0x03,0xED,0x57,0x46,0x50,0xD3,
- 0x69,0x99,0xDB,0x29,0xD7,0x76,0x27,0x6B,0xA2,0xD3,0xD4,0x12,
- 0xE2,0x18,0xF4,0xDD,0x1E,0x08,0x4C,0xF6,0xD8,0x00,0x3E,0x7C,
- 0x47,0x74,0xE8,0x33,
-};
-
-unsigned char System::_tempDiffieHellman512g[] =
-{
- 0x02,
-};
-
-// TODO: Very possibly a problem later if we have mutliple loggers going on simultaneously.
-// This is a horrible necessity in order to make the trace levels
-// and logger available to the bio_dump_cb() callback function.
-// Otherwise, we would have to jump through hoops, creating a mapping
-// from BIO pointers to the relevent System object. The system object
-// will initialize these. NOTE: If we SHOULD have multiple loggers
-// going on simultaneously, this will definitely cause a problem.
-TraceLevelsPtr System::_globalTraceLevels = 0;
-Ice::LoggerPtr System::_globalLogger = 0;
-
-}
-
-}
-
-}
-
-using IceSecurity::Ssl::OpenSSL::ContextException;
-
-//
-// NOTE: The following (mon, getGeneralizedTime, getUTCTime and getASN1time are routines that
-// have been abducted from the OpenSSL X509 library, and modified to work with the STL
-// basic_string template.
-
-static const char *mon[12]=
-{
- "Jan","Feb","Mar","Apr","May","Jun",
- "Jul","Aug","Sep","Oct","Nov","Dec"
-};
-
-string
-getGeneralizedTime(ASN1_GENERALIZEDTIME *tm)
-{
- char buf[30];
- char *v;
- int gmt=0;
- int i;
- int y = 0, M = 0, d = 0, h = 0, m = 0, s = 0;
-
- i = tm->length;
- v = (char *) tm->data;
-
- if (i < 12)
- {
- goto err;
- }
-
- if (v[i-1] == 'Z')
- {
- gmt=1;
- }
-
- for (i=0; i<12; i++)
- {
- if ((v[i] > '9') || (v[i] < '0'))
- {
- goto err;
- }
- }
-
- y = (v[0] - '0') * 1000 + (v[1] - '0') * 100 + (v[2] - '0') * 10 + (v[3] - '0');
- M = (v[4] - '0') * 10 + (v[5] - '0');
-
- if ((M > 12) || (M < 1))
- {
- goto err;
- }
-
- d = (v[6] - '0') * 10 + (v[7] - '0');
- h = (v[8] - '0') * 10 + (v[9] - '0');
- m = (v[10] - '0') * 10 + (v[11] - '0');
-
- if ((v[12] >= '0') && (v[12] <= '9') &&
- (v[13] >= '0') && (v[13] <= '9'))
- {
- s = (v[12] - '0') * 10 + (v[13] - '0');
- }
-
- sprintf(buf, "%s %2d %02d:%02d:%02d %d%s", mon[M-1], d, h, m, s, y, (gmt)?" GMT":"");
- return string(buf);
-
-err:
- return string("Bad time value");
-}
-
-string
-getUTCTime(ASN1_UTCTIME *tm)
-{
- char buf[30];
- char *v;
- int gmt=0;
- int i;
- int y = 0, M = 0, d = 0, h = 0, m = 0, s = 0;
-
- i = tm->length;
- v = (char *) tm->data;
-
- if (i < 10)
- {
- goto err;
- }
-
- if (v[i-1] == 'Z')
- {
- gmt=1;
- }
-
- for (i = 0; i < 10; i++)
- {
- if ((v[i] > '9') || (v[i] < '0'))
- {
- goto err;
- }
- }
-
- y = (v[0] - '0') * 10 + (v[1] - '0');
-
- if (y < 50)
- {
- y+=100;
- }
-
- M = (v[2] - '0') * 10 + (v[3] - '0');
-
- if ((M > 12) || (M < 1))
- {
- goto err;
- }
-
- d = (v[4] - '0') * 10 + (v[5] - '0');
- h = (v[6] - '0') * 10 + (v[7] - '0');
- m = (v[8] - '0') * 10 + (v[9] - '0');
-
- if ((v[10] >= '0') && (v[10] <= '9') && (v[11] >= '0') && (v[11] <= '9'))
- {
- s = (v[10] - '0') * 10 + (v[11] - '0');
- }
-
- sprintf(buf, "%s %2d %02d:%02d:%02d %d%s", mon[M-1], d, h, m, s, y+1900, (gmt)?" GMT":"");
- return string(buf);
-
-err:
- return string("Bad time value");
-}
-
-string
-getASN1time(ASN1_TIME *tm)
-{
- string theTime;
-
- switch (tm->type)
- {
- case V_ASN1_UTCTIME :
- {
- theTime = getUTCTime(tm);
- }
-
- case V_ASN1_GENERALIZEDTIME :
- {
- theTime = getGeneralizedTime(tm);
- }
-
- default :
- {
- theTime = "Bad time value";
- }
- }
-
- return theTime;
-}
-
-extern "C"
-{
-
-RSA*
-tmpRSACallback(SSL *s, int isExport, int keyLength)
-{
- IceSecurity::Ssl::System* sslSystem = IceSecurity::Ssl::Factory::getSystemFromHandle(s);
-
- IceSecurity::Ssl::OpenSSL::System* openSslSystem = dynamic_cast<IceSecurity::Ssl::OpenSSL::System*>(sslSystem);
-
- RSA* rsaKey = openSslSystem->getRSAKey(s, isExport, keyLength);
-
- IceSecurity::Ssl::Factory::releaseSystemFromHandle(s, sslSystem);
-
- return rsaKey;
-}
-
-DH*
-tmpDHCallback(SSL *s, int isExport, int keyLength)
-{
- IceSecurity::Ssl::System* sslSystem = IceSecurity::Ssl::Factory::getSystemFromHandle(s);
-
- IceSecurity::Ssl::OpenSSL::System* openSslSystem = dynamic_cast<IceSecurity::Ssl::OpenSSL::System*>(sslSystem);
-
- DH* dh = openSslSystem->getDHParams(s, isExport, keyLength);
-
- IceSecurity::Ssl::Factory::releaseSystemFromHandle(s, sslSystem);
-
- return dh;
-}
-
-// verifyCallback - Certificate Verification callback function.
-int
-verifyCallback(int ok, X509_STORE_CTX *ctx)
-{
- X509* err_cert = X509_STORE_CTX_get_current_cert(ctx);
- int verifyError = X509_STORE_CTX_get_error(ctx);
- int depth = X509_STORE_CTX_get_error_depth(ctx);
-
- // If we have no errors so far, and the certificate chain is too long
- if ((verifyError != X509_V_OK) && (10 < depth))
- {
- verifyError = X509_V_ERR_CERT_CHAIN_TOO_LONG;
- }
-
- if (verifyError != X509_V_OK)
- {
- // If we have ANY errors, we bail out.
- ok = 0;
- }
-
- // Only if PROTOCOL level logging is on do we worry about this.
- if (IceSecurity::Ssl::OpenSSL::System::_globalTraceLevels->security >= IceSecurity::SECURITY_PROTOCOL)
- {
- char buf[256];
-
- X509_NAME_oneline(X509_get_subject_name(err_cert), buf, sizeof(buf));
-
- ostringstream outStringStream;
-
- outStringStream << "depth = " << depth << ":" << buf << endl;
-
- if (!ok)
- {
- outStringStream << "verify error: num = " << verifyError << " : " << X509_verify_cert_error_string(verifyError) << endl;
-
- }
-
- switch (verifyError)
- {
- case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT:
- {
- X509_NAME_oneline(X509_get_issuer_name(ctx->current_cert), buf, sizeof(buf));
- outStringStream << "issuer = " << buf << endl;
- break;
- }
-
- case X509_V_ERR_CERT_NOT_YET_VALID:
- case X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD:
- {
- outStringStream << "notBefore =" << getASN1time(X509_get_notBefore(ctx->current_cert)) << endl;
- break;
- }
-
- case X509_V_ERR_CERT_HAS_EXPIRED:
- case X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD:
- {
- outStringStream << "notAfter =" << getASN1time(X509_get_notAfter(ctx->current_cert)) << endl;
- break;
- }
- }
-
- outStringStream << "verify return = " << ok << endl;
-
- IceSecurity::Ssl::OpenSSL::System::_globalLogger->trace(IceSecurity::Ssl::OpenSSL::System::_globalTraceLevels->securityCat, outStringStream.str());
- }
-
- return ok;
-}
-
-// This code duplicates functionality that existed in the BIO library of
-// OpenSSL, but outputs to a Logger compatible source (ostringstream).
-void
-dump(ostringstream& outStringStream, const char* s, int len)
-{
- unsigned char ch;
- char hexStr[8];
-
- int trunc = 0;
-
- // Calculate how much white space we're truncating.
- for(; (len > 0) && ((s[len - 1] == ' ') || (s[len - 1] == '\0')); len--)
- {
- trunc++;
- }
-
- int dump_width = 12;
-
- int rows = len / dump_width;
-
- if ((rows * dump_width) < len)
- {
- rows++;
- }
-
- if (rows > 0)
- {
- outStringStream << endl;
- }
-
- for(int i = 0; i < rows; i++)
- {
- // Would like to have not used sprintf(), but
- // I could not find an appropriate STL methodology
- // for preserving the field width.
- sprintf(hexStr,"%04x",(i * dump_width));
- outStringStream << hexStr << " - ";
-
- int j;
-
- // Hex Dump
- for(j = 0; j < dump_width; j++)
- {
- if (((i * dump_width) + j) >= len)
- {
- outStringStream << " ";
- }
- else
- {
- char sep = (j == 7 ? '-' : ' ');
-
- // Get a character from the dump we've been handed.
- ch = ((unsigned char)*(s + i * dump_width + j)) & 0xff;
-
- // Would like to have not used sprintf(), but
- // I could not find an appropriate STL methodology
- // for preserving the field width.
- sprintf(hexStr,"%02x",ch);
- outStringStream << hexStr << sep;
- }
- }
-
- outStringStream << " ";
-
- // Printable characters dump.
- for(j = 0; j < dump_width; j++)
- {
- if (((i * dump_width) + j) >= len)
- {
- break;
- }
-
- ch = ((unsigned char) * (s + i * dump_width + j)) & 0xff;
-
- // Print printables only.
- ch = ((ch >= ' ') && (ch <= '~')) ? ch : '.';
-
- outStringStream << ch;
- }
-
- outStringStream << endl;
- }
-
- if (trunc > 0)
- {
- outStringStream << hex << (len + trunc) << " - " << "<SPACES/NULS>" << endl;
- }
-}
-
-long
-bio_dump_cb(BIO *bio, int cmd, const char *argp, int argi, long argl, long ret)
-{
- if (IceSecurity::Ssl::OpenSSL::System::_globalTraceLevels->security >= IceSecurity::SECURITY_PROTOCOL)
- {
- ostringstream outStringStream;
-
- if (cmd == (BIO_CB_READ|BIO_CB_RETURN))
- {
- outStringStream << "PTC ";
- outStringStream << "read from " << hex << (void *)bio << " [" << hex << (void *)argp;
- outStringStream << "] (" << dec << argi << " bytes => " << ret << " (0x";
- outStringStream << hex << ret << "))";
- dump(outStringStream, argp,(int)ret);
- }
- else if (cmd == (BIO_CB_WRITE|BIO_CB_RETURN))
- {
- outStringStream << "PTC ";
- outStringStream << "write to " << hex << (void *)bio << " [" << hex << (void *)argp;
- outStringStream << "] (" << dec << argi << " bytes => " << ret << " (0x";
- outStringStream << hex << ret << "))";
- dump(outStringStream, argp,(int)ret);
- }
-
- if (cmd == (BIO_CB_READ|BIO_CB_RETURN) || cmd == (BIO_CB_WRITE|BIO_CB_RETURN))
- {
- IceSecurity::Ssl::OpenSSL::System::_globalLogger->trace(IceSecurity::Ssl::OpenSSL::System::_globalTraceLevels->securityCat, outStringStream.str());
- }
- }
-
- return ret;
-}
-
-}
-
-void
-IceSecurity::Ssl::OpenSSL::System::printContextInfo(SSL_CTX* context)
-{
- if (SECURITY_LEVEL_PROTOCOL)
- {
- ostringstream s;
-
- s << endl;
- s << "SSL_CTX Structure" << endl;
- s << "=================" << endl;
- s << "options: 0x" << hex << context->options << endl;
- s << "mode: 0x" << hex << context->mode << endl;
-
- s << "session_cache_size: " << context->session_cache_size << endl;
- s << "session_cache_mode: 0x" << hex << context->session_cache_mode << endl;
- s << "session_timeout: " << Int(context->session_timeout) << endl << endl;
-
- s << "Stats" << endl;
- s << "Connect: " << context->stats.sess_connect << " (New Connect Started)" << endl;
- s << "Renegotiate: " << context->stats.sess_connect_renegotiate << " (Renegotiation Requested)" << endl;
- s << "Connect Good: " << context->stats.sess_connect_good << " (Connect/Renegotiation finished)";
- s << endl << endl;
-
- s << "Accept: " << context->stats.sess_accept << " (New Accept Started)" << endl;
- s << "Renegotiate: " << context->stats.sess_accept_renegotiate << " (Renegotiation Requested)" << endl;
- s << "Accept Good: " << context->stats.sess_accept_good << " (Accept/Renegotiation finished)";
- s << endl << endl;
-
- s << "Miss: " << context->stats.sess_miss << " (Session Lookup Misses)" << endl;
- s << "Timeout: " << context->stats.sess_timeout << " (Reuse attempt on Timeouted Session)" << endl;
- s << "Cache Full: " << context->stats.sess_cache_full << " (Session Removed due to full cache)" << endl;
- s << "Hit: " << context->stats.sess_hit << " (Session Reuse actually done.)" << endl;
- s << "CB Hit: " << context->stats.sess_cb_hit << " (Session ID supplied by Callback)" << endl;
-
- s << "read_ahead: " << context->read_ahead << endl;
- s << "verify_mode: 0x" << hex << context->verify_mode << endl;
- s << "verify_depth: " << Int(context->verify_depth) << endl;
-
- PROTOCOL(s.str());
- }
-}
-
-IceSecurity::Ssl::Connection*
-IceSecurity::Ssl::OpenSSL::System::createServerConnection(int socket)
-{
- METHOD_INV("OpenSSL::System::createServerConnection()");
-
- SSL* sslConnection = createConnection(_sslServerContext, socket);
-
- // Set the Accept Connection state for this connection.
- SSL_set_accept_state(sslConnection);
-
- Connection* connection = new ServerConnection(sslConnection, _systemID);
-
- connection->setTrace(_traceLevels);
- connection->setLogger(_logger);
-
- continueInit:
- try
- {
- while (!connection->init()) { }
- }
- catch (const TimeoutException&)
- {
- // Ignore, this might happen a lot during handshake.
- goto continueInit;
- }
- catch (...)
- {
- if (connection != 0)
- {
- delete connection;
- connection = 0;
- }
-
- throw;
- }
-
- METHOD_RET("OpenSSL::System::createServerConnection()");
-
- return connection;
-}
-
-IceSecurity::Ssl::Connection*
-IceSecurity::Ssl::OpenSSL::System::createClientConnection(int socket)
-{
- METHOD_INV("OpenSSL::System::createClientConnection()");
-
- SSL* sslConnection = createConnection(_sslClientContext, socket);
-
- // Set the Connect Connection state for this connection.
- SSL_set_connect_state(sslConnection);
-
- Connection* connection = new ClientConnection(sslConnection, _systemID);
-
- connection->setTrace(_traceLevels);
- connection->setLogger(_logger);
-
- continueInit:
- try
- {
- while (!connection->init()) { }
- }
- catch (const TimeoutException&)
- {
- // Ignore, this might happen a lot during handshake.
- goto continueInit;
- }
- catch (...)
- {
- if (connection != 0)
- {
- delete connection;
- connection = 0;
- }
-
- throw;
- }
-
- METHOD_RET("OpenSSL::System::createClientConnection()");
-
- return connection;
-}
-
-void
-IceSecurity::Ssl::OpenSSL::System::shutdown()
-{
- METHOD_INV("OpenSSL::System::shutdown()");
-
- if (_sslServerContext != 0)
- {
- SSL_CTX_free(_sslServerContext);
-
- _sslServerContext = 0;
- }
-
- if (_sslClientContext != 0)
- {
- SSL_CTX_free(_sslClientContext);
-
- _sslClientContext = 0;
- }
-
- // Free our temporary RSA keys.
- RSAMap::iterator iRSA = _tempRSAKeys.begin();
- RSAMap::iterator eRSA = _tempRSAKeys.end();
-
- while (iRSA != eRSA)
- {
- RSA_free((*iRSA).second);
- iRSA++;
- }
-
- // Free our temporary DH params.
- DHMap::iterator iDH = _tempDHKeys.begin();
- DHMap::iterator eDH = _tempDHKeys.end();
-
- while (iDH != eDH)
- {
- DH_free((*iDH).second);
- iDH++;
- }
-
- METHOD_RET("OpenSSL::System::shutdown()");
-}
-
-bool
-IceSecurity::Ssl::OpenSSL::System::isConfigLoaded()
-{
- METHOD_INS("OpenSSL::System::isConfigLoaded()");
-
- return _configLoaded;
-}
-
-void
-IceSecurity::Ssl::OpenSSL::System::loadConfig()
-{
- METHOD_INV("OpenSSL::System::loadConfig()");
-
- // This step is required in order to supply callback functions
- // with access to the TraceLevels and Logger.
- if (_globalTraceLevels == 0)
- {
- _globalTraceLevels = _traceLevels;
- _globalLogger = _logger;
- }
-
- const string& systemID = getSystemID();
- Parser sslConfig(systemID);
-
- sslConfig.setTrace(_traceLevels);
- sslConfig.setLogger(_logger);
-
- // Actually parse the file now.
- sslConfig.process();
-
- GeneralConfig clientGeneral;
- CertificateAuthority clientCertAuth;
- BaseCertificates clientBaseCerts;
-
- // Walk the parse tree, get the Client configuration.
- if (sslConfig.loadClientConfig(clientGeneral, clientCertAuth, clientBaseCerts))
- {
- if (SECURITY_LEVEL_PROTOCOL)
- {
- ostringstream s;
-
- s << endl;
- s << "General Configuration - Client" << endl;
- s << "------------------------------" << endl;
- s << clientGeneral << endl << endl;
-
- s << "Base Certificates - Client" << endl;
- s << "--------------------------" << endl;
- s << clientBaseCerts << endl;
-
- PROTOCOL(s.str());
- }
-
- initClient(clientGeneral, clientCertAuth, clientBaseCerts);
- }
-
- GeneralConfig serverGeneral;
- CertificateAuthority serverCertAuth;
- BaseCertificates serverBaseCerts;
- TempCertificates serverTempCerts;
-
- // Walk the parse tree, get the Server configuration.
- if (sslConfig.loadServerConfig(serverGeneral, serverCertAuth, serverBaseCerts, serverTempCerts))
- {
- if (SECURITY_LEVEL_PROTOCOL)
- {
- ostringstream s;
-
- s << endl;
- s << "General Configuration - Server" << endl;
- s << "------------------------------" << endl;
- s << serverGeneral << endl << endl;
-
- s << "Base Certificates - Server" << endl;
- s << "--------------------------" << endl;
- s << serverBaseCerts << endl << endl;
-
- s << "Temp Certificates - Server" << endl;
- s << "--------------------------" << endl;
- s << serverTempCerts << endl;
-
- PROTOCOL(s.str());
- }
-
- initServer(serverGeneral, serverCertAuth, serverBaseCerts, serverTempCerts);
- }
-
- METHOD_RET("OpenSSL::System::loadConfig()");
-}
-
-RSA*
-IceSecurity::Ssl::OpenSSL::System::getRSAKey(SSL *s, int isExport, int keyLength)
-{
- METHOD_INV("OpenSSL::System::getRSAKey()");
-
- JTCSyncT<JTCMutex> sync(_tempRSAKeysMutex);
-
- RSA* rsa_tmp = 0;
-
- RSAMap::iterator retVal = _tempRSAKeys.find(keyLength);
-
- // Does the key already exist?
- if (retVal != _tempRSAKeys.end())
- {
- // Yes! Use it.
- rsa_tmp = (*retVal).second;
- }
- else
- {
- const RSACertMap::iterator& it = _tempRSAFileMap.find(keyLength);
-
- if (it != _tempRSAFileMap.end())
- {
- CertificateDesc& rsaKeyCert = (*it).second;
-
- const string& privKeyFile = rsaKeyCert.getPrivate().getFileName();
- const string& pubCertFile = rsaKeyCert.getPublic().getFileName();
-
- RSA* rsaCert = 0;
- RSA* rsaKey = 0;
- BIO* bio = 0;
-
- if ((bio = BIO_new_file(pubCertFile.c_str(), "r")) != 0)
- {
- rsaCert = PEM_read_bio_RSAPublicKey(bio, 0, 0, 0);
-
- BIO_free(bio);
- bio = 0;
- }
-
- if (rsaCert != 0)
- {
- if ((bio = BIO_new_file(privKeyFile.c_str(), "r")) != 0)
- {
- rsaKey = PEM_read_bio_RSAPrivateKey(bio, &rsaCert, 0, 0);
-
- BIO_free(bio);
- bio = 0;
- }
- }
-
- // Now, if all was well, the Certificate and Key should both be loaded into
- // rsaCert. We check to ensure that both are not 0, because if either are,
- // one of the reads failed.
-
- if ((rsaCert != 0) && (rsaKey != 0))
- {
- rsa_tmp = rsaCert;
- }
- else
- {
- RSA_free(rsaCert);
- rsaCert = 0;
- }
- }
-
- // Last ditch effort - generate a key on the fly.
- if (rsa_tmp == 0)
- {
- rsa_tmp = RSA_generate_key(keyLength, RSA_F4, 0, 0);
- }
-
- // Save in our temporary key cache.
- if (rsa_tmp == 0)
- {
- _tempRSAKeys[keyLength] = rsa_tmp;
- }
- }
-
- METHOD_RET("OpenSSL::System::getRSAKey()");
-
- return rsa_tmp;
-}
-
-DH*
-IceSecurity::Ssl::OpenSSL::System::getDHParams(SSL *s, int isExport, int keyLength)
-{
- METHOD_INV("OpenSSL::System::getDHParams()");
-
- JTCSyncT<JTCMutex> sync(_tempDHKeysMutex);
-
- DH *dh_tmp = 0;
-
- const DHMap::iterator& retVal = _tempDHKeys.find(keyLength);
-
- // Does the key already exist?
- if (retVal != _tempDHKeys.end())
- {
- // Yes! Use it.
- dh_tmp = (*retVal).second;
- }
- else
- {
- const DHParamsMap::iterator& it = _tempDHParamsFileMap.find(keyLength);
-
- if (it != _tempDHParamsFileMap.end())
- {
- DiffieHellmanParamsFile& dhParamsFile = (*it).second;
-
- string dhFile = dhParamsFile.getFileName();
-
- dh_tmp = loadDHParam(dhFile.c_str());
-
- if (dh_tmp != 0)
- {
- _tempDHKeys[keyLength] = dh_tmp;
- }
- }
- }
-
- METHOD_RET("OpenSSL::System::getDHParams()");
-
- return dh_tmp;
-}
-
-//
-// Protected
-//
-
-IceSecurity::Ssl::OpenSSL::System::System(string& systemID) :
- IceSecurity::Ssl::System(systemID)
-{
- _configLoaded = false;
-
- _sessionContext = "iceServer";
-
- _sslServerContext = 0;
- _sslClientContext = 0;
-
- SSL_load_error_strings();
-
- OpenSSL_add_ssl_algorithms();
-}
-
-IceSecurity::Ssl::OpenSSL::System::~System()
-{
- METHOD_INV("OpenSSL::~System()");
-
- shutdown();
-
- METHOD_RET("OpenSSL::~System()");
-}
-
-//
-// Private
-//
-
-void
-IceSecurity::Ssl::OpenSSL::System::initClient(GeneralConfig& general,
- CertificateAuthority& certAuth,
- BaseCertificates& baseCerts)
-{
- METHOD_INV("OpenSSL::System::initClient()");
-
- // Init the Random Number System.
- initRandSystem(general.getRandomBytesFiles());
-
- // Create an SSL Context based on the context params.
- _sslClientContext = createContext(general.getProtocol());
-
- // Begin setting up the SSL Context.
- if (_sslClientContext != 0)
- {
- // Get the cipherlist and set it in the context.
- setCipherList(_sslClientContext, general.getCipherList());
-
- // Set the certificate verification mode.
- SSL_CTX_set_verify(_sslClientContext, general.getVerifyMode(), verifyCallback);
-
- // Set the certificate verify depth to 10 deep.
- SSL_CTX_set_verify_depth(_sslClientContext, general.getVerifyDepth());
-
- // Process the RSA Certificate (if present).
- if (baseCerts.getRSACert().getKeySize() != 0)
- {
- processCertificate(_sslClientContext, baseCerts.getRSACert());
- }
-
- // Process the DSA Certificate (if present).
- if (baseCerts.getDSACert().getKeySize() != 0)
- {
- processCertificate(_sslClientContext, baseCerts.getDSACert());
- }
-
- // Set the DH key agreement parameters.
- if (baseCerts.getDHParams().getKeySize() != 0)
- {
- setDHParams(_sslClientContext, baseCerts);
- }
-
- // Load the Certificate Authority files, and check them.
- loadCAFiles(_sslClientContext, certAuth);
- }
-
- METHOD_RET("OpenSSL::System::initClient()");
-}
-
-void
-IceSecurity::Ssl::OpenSSL::System::initServer(GeneralConfig& general,
- CertificateAuthority& certAuth,
- BaseCertificates& baseCerts,
- TempCertificates& tempCerts)
-{
- METHOD_INV("OpenSSL::System::initServer()");
-
- // Init the Random Number System.
- initRandSystem(general.getRandomBytesFiles());
-
- // Create an SSL Context based on the context params.
- _sslServerContext = createContext(general.getProtocol());
-
- // Begin setting up the SSL Context.
- if (_sslServerContext != 0)
- {
- // On servers, Attempt to use non-export (strong) encryption
- // first. This option does not always work, and in the OpenSSL
- // documentation is declared as 'broken'.
- // SSL_CTX_set_options(_sslServerContext,SSL_OP_NON_EXPORT_FIRST);
-
- // Always use a new DH key when using Diffie-Hellman key agreement.
- SSL_CTX_set_options(_sslServerContext, SSL_OP_SINGLE_DH_USE);
-
- loadTempCerts(tempCerts);
-
- // Load the Certificate Authority files, and check them.
- loadAndCheckCAFiles(_sslServerContext, certAuth);
-
- // Process the RSA Certificate (if present).
- if (baseCerts.getRSACert().getKeySize() != 0)
- {
- processCertificate(_sslServerContext, baseCerts.getRSACert());
- }
-
- // Process the DSA Certificate (if present).
- if (baseCerts.getDSACert().getKeySize() != 0)
- {
- processCertificate(_sslServerContext, baseCerts.getDSACert());
- }
-
- // Set the DH key agreement parameters.
- if (baseCerts.getDHParams().getKeySize() != 0)
- {
- setDHParams(_sslServerContext, baseCerts);
- }
-
- // Set the RSA Callback routine in case we need to build a temporary RSA key.
- SSL_CTX_set_tmp_rsa_callback(_sslServerContext, tmpRSACallback);
-
- // Set the DH Callback routine in case we need a temporary DH key.
- SSL_CTX_set_tmp_dh_callback(_sslServerContext, tmpDHCallback);
-
- // Get the cipherlist and set it in the context.
- setCipherList(_sslServerContext, general.getCipherList());
-
- // Set the certificate verification mode.
- SSL_CTX_set_verify(_sslServerContext, general.getVerifyMode(), verifyCallback);
-
- // Set the certificate verify depth
- SSL_CTX_set_verify_depth(_sslServerContext, general.getVerifyDepth());
-
- // Set the default context for the SSL system (can be overridden if needed) [SERVER ONLY].
- SSL_CTX_set_session_id_context(_sslServerContext,
- reinterpret_cast<const unsigned char *>(_sessionContext.c_str()),
- _sessionContext.size());
- }
-
- printContextInfo(_sslServerContext);
-
- METHOD_RET("OpenSSL::System::initServer()");
-}
-
-SSL_METHOD*
-IceSecurity::Ssl::OpenSSL::System::getSslMethod(SslProtocol sslVersion)
-{
- METHOD_INV("OpenSSL::System::getSslMethod()");
-
- SSL_METHOD* sslMethod = 0;
-
- switch (sslVersion)
- {
- case SSL_V2 :
- {
- sslMethod = SSLv2_method();
- break;
- }
-
- case SSL_V23 :
- {
- sslMethod = SSLv23_method();
- break;
- }
-
- case SSL_V3 :
- {
- sslMethod = SSLv3_method();
- break;
- }
-
- case TLS_V1 :
- {
- sslMethod = TLSv1_method();
- break;
- }
-
- default :
- {
- string errorString;
-
- errorString = "SSL Version ";
- errorString += sslVersion;
- errorString += " not supported.";
-
- EXCEPTION(errorString);
-
- throw ContextException(errorString.c_str(), __FILE__, __LINE__);
- }
- }
-
- METHOD_RET("OpenSSL::System::getSslMethod()");
-
- return sslMethod;
-}
-
-void
-IceSecurity::Ssl::OpenSSL::System::processCertificate(SSL_CTX* sslContext, const CertificateDesc& certificateDesc)
-{
- METHOD_INV("OpenSSL::System::processCertificate()");
-
- const CertificateFile& publicCert = certificateDesc.getPublic();
- const CertificateFile& privateKey = certificateDesc.getPrivate();
-
- addKeyCert(sslContext, publicCert, privateKey);
-
- METHOD_RET("OpenSSL::System::processCertificate()");
-}
-
-void
-IceSecurity::Ssl::OpenSSL::System::addKeyCert(SSL_CTX* sslContext,
- const CertificateFile& publicCert,
- const CertificateFile& privateKey)
-{
- METHOD_INV("OpenSSL::System::addKeyCert()");
-
- if (!publicCert.getFileName().empty())
- {
- string publicCertFile = publicCert.getFileName();
- const char* publicFile = publicCertFile.c_str();
- int publicEncoding = publicCert.getEncoding();
-
- string privCertFile = privateKey.getFileName();
- const char* privKeyFile = privCertFile.c_str();
- int privKeyFileType = privateKey.getEncoding();
-
- // Set which Public Key file to use.
- if (SSL_CTX_use_certificate_file(sslContext, publicFile, publicEncoding) <= 0)
- {
- string errorString;
-
- errorString = "Unable to get certificate from '";
- errorString += publicFile;
- errorString += "'\n";
- errorString += sslGetErrors();
-
- EXCEPTION(errorString);
-
- throw ContextException(errorString.c_str(), __FILE__, __LINE__);
- }
-
- if (privateKey.getFileName().empty())
- {
- WARNING("No Private Key specified - using the certificate.");
-
- privKeyFile = publicFile;
- privKeyFileType = publicEncoding;
- }
-
- // Set which Private Key file to use.
- if (SSL_CTX_use_PrivateKey_file(sslContext, privKeyFile, privKeyFileType) <= 0)
- {
- string errorString;
-
- errorString = "unable to get private key from '";
- errorString += privKeyFile;
- errorString += "'\n";
- errorString += sslGetErrors();
-
- EXCEPTION(errorString);
-
- throw ContextException(errorString.c_str(), __FILE__, __LINE__);
- }
-
- // Check to see if the Private and Public keys that have been
- // set against the SSL context match up.
- if (!SSL_CTX_check_private_key(sslContext))
- {
- string errorString = "Private key does not match the certificate public key";
-
- EXCEPTION(errorString);
-
- throw ContextException(errorString.c_str(), __FILE__, __LINE__);
- }
-
- }
-
- METHOD_RET("OpenSSL::System::addKeyCert()");
-}
-
-
-SSL_CTX*
-IceSecurity::Ssl::OpenSSL::System::createContext(SslProtocol sslProtocol)
-{
- METHOD_INV("OpenSSL::System::createContext()");
-
- SSL_CTX* context = SSL_CTX_new(getSslMethod(sslProtocol));
-
- if (context == 0)
- {
- string errorString = "Unable to create SSL Context.";
-
- EXCEPTION(errorString);
-
- throw ContextException(errorString.c_str(), __FILE__, __LINE__);
- }
-
- METHOD_RET("OpenSSL::System::createContext()");
-
- return context;
-}
-
-
-//
-// TODO: Clean this up. This routine is pretty much ripped off from the OpenSSL
-// routine ERR_print_errors_fp(), except we got rid of the file pointer.
-//
-string
-IceSecurity::Ssl::OpenSSL::System::sslGetErrors()
-{
- METHOD_INV("OpenSSL::System::sslGetErrors()");
-
- string errorMessage;
- char buf[200], bigBuffer[1024];
- const char* file;
- const char* data = 0;
- int line, flags;
-
- unsigned long es = CRYPTO_thread_id();
-
- while ((line = ERR_get_error_line_data(&file, &line, &data, &flags)) != 0)
- {
- // Request an error from the OpenSSL library
- ERR_error_string_n(line, buf, sizeof(buf));
-
- sprintf(bigBuffer,"%lu:%s:%s:%d:%s\n",es,buf,file,line,(flags&ERR_TXT_STRING)?data:"");
-
- errorMessage += bigBuffer;
- }
-
- METHOD_RET("OpenSSL::System::sslGetErrors()");
-
- return errorMessage;
-}
-
-SSL*
-IceSecurity::Ssl::OpenSSL::System::createConnection(SSL_CTX* sslContext, int socket)
-{
- METHOD_INV("OpenSSL::System::createConnection()");
-
- SSL *sslConnection = 0;
-
- sslConnection = SSL_new(sslContext);
-
- SSL_clear(sslConnection);
-
- SSL_set_fd(sslConnection, socket);
-
- if (SECURITY_LEVEL_PROTOCOL_DEBUG)
- {
- sslConnection->debug = 1;
- BIO_set_callback(SSL_get_rbio(sslConnection), bio_dump_cb);
- BIO_set_callback_arg(SSL_get_rbio(sslConnection), 0);
- BIO_set_callback(SSL_get_wbio(sslConnection), bio_dump_cb);
- BIO_set_callback_arg(SSL_get_rbio(sslConnection), 0);
- }
-
- // Map the SSL Connection to this SslSystem
- // This is required for the OpenSSL callbacks
- // to work properly.
- Factory::addSystemHandle(sslConnection, this);
-
- METHOD_RET("OpenSSL::System::createConnection()");
-
- return sslConnection;
-}
-
-void
-IceSecurity::Ssl::OpenSSL::System::loadCAFiles(SSL_CTX* sslContext, CertificateAuthority& certAuth)
-{
- METHOD_INV("OpenSSL::System::loadCAFiles()");
-
- string caFile = certAuth.getCAFileName();
- string caPath = certAuth.getCAPath();
-
- loadCAFiles(sslContext, caFile.c_str(), caPath.c_str());
-
- METHOD_RET("OpenSSL::System::loadCAFiles()");
-}
-
-void
-IceSecurity::Ssl::OpenSSL::System::loadCAFiles(SSL_CTX* sslContext, const char* caFile, const char* caPath)
-{
- METHOD_INV("OpenSSL::System::loadCAFiles()");
-
- if (sslContext != 0)
- {
- // The following checks are required to send the expected values to the OpenSSL library.
- // It does not like receiving "", but prefers NULLs.
- if ((caFile != 0) && (strlen(caFile) == 0))
- {
- caFile = 0;
- }
-
- if ((caPath != 0) && (strlen(caPath) == 0))
- {
- caPath = 0;
- }
-
- // Check the Certificate Authority file(s).
- if ((!SSL_CTX_load_verify_locations(sslContext, caFile, caPath)) ||
- (!SSL_CTX_set_default_verify_paths(sslContext)))
- {
- // Non Fatal.
- WARNING("Unable to load/verify Certificate Authorities.");
- }
- }
-
- METHOD_RET("OpenSSL::System::loadCAFiles()");
-}
-
-void
-IceSecurity::Ssl::OpenSSL::System::loadAndCheckCAFiles(SSL_CTX* sslContext, CertificateAuthority& certAuth)
-{
- METHOD_INV("OpenSSL::System::loadAndCheckCAFiles()");
-
- if (sslContext != 0)
- {
- string caFile = certAuth.getCAFileName();
- string caPath = certAuth.getCAPath();
-
- // Check the Certificate Authority file(s).
- loadCAFiles(sslContext, caFile.c_str(), caPath.c_str());
-
- if (!caPath.empty())
- {
- STACK_OF(X509_NAME)* certNames = SSL_load_client_CA_file(caFile.c_str());
-
- if (certNames != 0)
- {
- SSL_CTX_set_client_CA_list(sslContext, certNames);
- }
- else
- {
- WARNING("Unable to set SSL context Certificate Authorities.");
- }
- }
- }
-
- METHOD_RET("OpenSSL::System::loadAndCheckCAFiles()");
-}
-
-DH*
-IceSecurity::Ssl::OpenSSL::System::loadDHParam(const char* dhfile)
-{
- METHOD_INV(string("OpenSSL::System::loadDHParam(") + dhfile + string(")"));
-
- DH* ret = 0;
- BIO* bio;
-
- if ((bio = BIO_new_file(dhfile,"r")) != 0)
- {
- ret = PEM_read_bio_DHparams(bio, 0, 0, 0);
- }
-
- if (bio != 0)
- {
- BIO_free(bio);
- }
-
- METHOD_RET(string("OpenSSL::System::loadDHParam(") + dhfile + string(")"));
-
- return ret;
-}
-
-DH*
-IceSecurity::Ssl::OpenSSL::System::getTempDH(unsigned char* p, int plen, unsigned char* g, int glen)
-{
- METHOD_INV("OpenSSL::System::getTempDH()");
-
- DH* dh = 0;
-
- if ((dh = DH_new()) != 0)
- {
- dh->p = BN_bin2bn(p, plen, 0);
-
- dh->g = BN_bin2bn(g, glen, 0);
-
- if ((dh->p == 0) || (dh->g == 0))
- {
- DH_free(dh);
- dh = 0;
- }
- }
-
- METHOD_RET("OpenSSL::System::getTempDH()");
-
- return dh;
-}
-
-DH*
-IceSecurity::Ssl::OpenSSL::System::getTempDH512()
-{
- METHOD_INV("OpenSSL::System::getTempDH512()");
-
- DH* dh = getTempDH(_tempDiffieHellman512p, sizeof(_tempDiffieHellman512p),
- _tempDiffieHellman512g, sizeof(_tempDiffieHellman512g));
-
- METHOD_RET("OpenSSL::System::getTempDH512()");
-
- return dh;
-}
-
-void
-IceSecurity::Ssl::OpenSSL::System::setDHParams(SSL_CTX* sslContext, BaseCertificates& baseCerts)
-{
- METHOD_INV("OpenSSL::System::setDHParams()");
-
- string dhFile;
- int encoding = 0;
-
- if (baseCerts.getDHParams().getKeySize() != 0)
- {
- dhFile = baseCerts.getDHParams().getFileName();
- encoding = baseCerts.getDHParams().getEncoding();
- }
- else if (baseCerts.getRSACert().getKeySize() != 0)
- {
- dhFile = baseCerts.getRSACert().getPublic().getFileName();
- encoding = baseCerts.getRSACert().getPublic().getEncoding();
- }
-
- DH* dh = 0;
-
- // File type must be PEM - that's the only way we can load
- // DH Params, apparently.
- if ((!dhFile.empty()) && (encoding == SSL_FILETYPE_PEM))
- {
- dh = loadDHParam(dhFile.c_str());
- }
-
- if (dh == 0)
- {
- WARNING("Could not load Diffie-Hellman params, generating a temporary 512bit key.");
-
- dh = getTempDH512();
- }
-
- if (dh != 0)
- {
- SSL_CTX_set_tmp_dh(sslContext, dh);
-
- DH_free(dh);
- }
-
- METHOD_RET("OpenSSL::System::setDHParams()");
-}
-
-void
-IceSecurity::Ssl::OpenSSL::System::setCipherList(SSL_CTX* sslContext, const string& cipherList)
-{
- METHOD_INV("OpenSSL::System::setCipherList()");
-
- if (!cipherList.empty() && (!SSL_CTX_set_cipher_list(sslContext, cipherList.c_str())))
- {
- string errorString = "Error setting cipher list.";
-
- EXCEPTION(errorString);
-
- throw ContextException(errorString.c_str(), __FILE__, __LINE__);
- }
-
- METHOD_RET("OpenSSL::System::setCipherList()");
-}
-
-int
-IceSecurity::Ssl::OpenSSL::System::seedRand()
-{
- METHOD_INV("OpenSSL::System::seedRand()");
-
- int retCode = 1;
- char buffer[1024];
-
-#ifdef WINDOWS
- RAND_screen();
-#endif
-
- const char* file = RAND_file_name(buffer, sizeof(buffer));
-
- if (file == 0 || !RAND_load_file(file, -1))
- {
- retCode = 0;
- }
- else
- {
- _randSeeded = 1;
- }
-
- METHOD_RET("OpenSSL::System::seedRand()");
-
- return retCode;
-}
-
-long
-IceSecurity::Ssl::OpenSSL::System::loadRandFiles(const string& names)
-{
- METHOD_INV("OpenSSL::System::loadRandFiles(" + names + ")");
-
- long tot = 0;
-
- if (!names.empty())
- {
- int egd;
-
- // Make a modifiable copy of the string.
- char* namesString = new char[names.length() + 1];
- strcpy(namesString, names.c_str());
-
- char seps[5];
-
- sprintf(seps, "%c", LIST_SEPARATOR_CHAR);
-
- char* token = strtok(namesString, seps);
-
- while (token != 0)
- {
- egd = RAND_egd(token);
-
- if (egd > 0)
- {
- tot += egd;
- }
- else
- {
- tot += RAND_load_file(token, -1);
- }
-
- token = strtok(0, seps);
- }
-
- if (tot > 512)
- {
- _randSeeded = 1;
- }
-
- delete []namesString;
- }
-
- METHOD_RET("OpenSSL::System::loadRandFiles(" + names + ")");
-
- return tot;
-}
-
-void
-IceSecurity::Ssl::OpenSSL::System::initRandSystem(const string& randBytesFiles)
-{
- METHOD_INV("OpenSSL::System::initRandSystem(" + randBytesFiles + ")");
-
- if (!_randSeeded)
- {
- long randBytesLoaded = 0;
-
- if (!seedRand() && randBytesFiles.empty() && !RAND_status())
- {
- WARNING("There is a lack of random data, consider specifying a random data file.");
- }
-
- if (!randBytesFiles.empty())
- {
- randBytesLoaded = loadRandFiles(randBytesFiles);
- }
- }
-
- METHOD_RET("OpenSSL::System::initRandSystem(" + randBytesFiles + ")");
-}
-
-void
-IceSecurity::Ssl::OpenSSL::System::loadTempCerts(TempCertificates& tempCerts)
-{
- METHOD_INV("OpenSSL::System::loadTempCerts()");
-
- RSAVector::iterator iRSA = tempCerts.getRSACerts().begin();
- RSAVector::iterator eRSA = tempCerts.getRSACerts().end();
-
- while (iRSA != eRSA)
- {
- _tempRSAFileMap[(*iRSA).getKeySize()] = *iRSA;
- iRSA++;
- }
-
- DSAVector::iterator iDSA = tempCerts.getDSACerts().begin();
- DSAVector::iterator eDSA = tempCerts.getDSACerts().end();
-
- while (iDSA != eDSA)
- {
- _tempDSAFileMap[(*iDSA).getKeySize()] = *iDSA;
- iDSA++;
- }
-
- DHVector::iterator iDHP = tempCerts.getDHParams().begin();
- DHVector::iterator eDHP = tempCerts.getDHParams().end();
-
- while (iDHP != eDHP)
- {
- _tempDHParamsFileMap[(*iDHP).getKeySize()] = *iDHP;
- iDHP++;
- }
-
- METHOD_RET("OpenSSL::System::loadTempCerts()");
-}
+// **********************************************************************
+//
+// Copyright (c) 2001
+// MutableRealms, Inc.
+// Huntsville, AL, USA
+//
+// All Rights Reserved
+//
+// **********************************************************************
+#ifdef WIN32
+#pragma warning(disable:4786)
+#endif
+
+#include <sstream>
+#include <openssl/err.h>
+#include <openssl/e_os.h>
+#include <openssl/rand.h>
+#include <Ice/Security.h>
+#include <Ice/SslSystem.h>
+#include <Ice/SslException.h>
+#include <Ice/SslConnectionOpenSSLClient.h>
+#include <Ice/SslConnectionOpenSSLServer.h>
+#include <Ice/SslConfig.h>
+
+using namespace std;
+
+namespace IceSecurity
+{
+
+namespace Ssl
+{
+
+namespace OpenSSL
+{
+
+//
+// TODO: These Diffie-Hellman params have been blatantly stolen from
+// OpenSSL's demo programs. We SHOULD define our own here, but
+// these will suffice for testing purposes. Please note, these
+// are not keys themselves, simply a DH Group that allows OpenSSL
+// to create Diffie-Hellman keys.
+//
+
+// Instantiation of temporary Diffie-Hellman 512bit key.
+unsigned char System::_tempDiffieHellman512p[] =
+{
+ 0xDA,0x58,0x3C,0x16,0xD9,0x85,0x22,0x89,0xD0,0xE4,0xAF,0x75,
+ 0x6F,0x4C,0xCA,0x92,0xDD,0x4B,0xE5,0x33,0xB8,0x04,0xFB,0x0F,
+ 0xED,0x94,0xEF,0x9C,0x8A,0x44,0x03,0xED,0x57,0x46,0x50,0xD3,
+ 0x69,0x99,0xDB,0x29,0xD7,0x76,0x27,0x6B,0xA2,0xD3,0xD4,0x12,
+ 0xE2,0x18,0xF4,0xDD,0x1E,0x08,0x4C,0xF6,0xD8,0x00,0x3E,0x7C,
+ 0x47,0x74,0xE8,0x33,
+};
+
+unsigned char System::_tempDiffieHellman512g[] =
+{
+ 0x02,
+};
+
+// TODO: Very possibly a problem later if we have mutliple loggers going on simultaneously.
+// This is a horrible necessity in order to make the trace levels
+// and logger available to the bio_dump_cb() callback function.
+// Otherwise, we would have to jump through hoops, creating a mapping
+// from BIO pointers to the relevent System object. The system object
+// will initialize these. NOTE: If we SHOULD have multiple loggers
+// going on simultaneously, this will definitely cause a problem.
+TraceLevelsPtr System::_globalTraceLevels = 0;
+Ice::LoggerPtr System::_globalLogger = 0;
+
+}
+
+}
+
+}
+
+using IceSecurity::Ssl::OpenSSL::ContextException;
+
+//
+// NOTE: The following (mon, getGeneralizedTime, getUTCTime and getASN1time are routines that
+// have been abducted from the OpenSSL X509 library, and modified to work with the STL
+// basic_string template.
+
+static const char *mon[12]=
+{
+ "Jan","Feb","Mar","Apr","May","Jun",
+ "Jul","Aug","Sep","Oct","Nov","Dec"
+};
+
+string
+getGeneralizedTime(ASN1_GENERALIZEDTIME *tm)
+{
+ char buf[30];
+ char *v;
+ int gmt=0;
+ int i;
+ int y = 0, M = 0, d = 0, h = 0, m = 0, s = 0;
+
+ i = tm->length;
+ v = (char *) tm->data;
+
+ if (i < 12)
+ {
+ goto err;
+ }
+
+ if (v[i-1] == 'Z')
+ {
+ gmt=1;
+ }
+
+ for (i=0; i<12; i++)
+ {
+ if ((v[i] > '9') || (v[i] < '0'))
+ {
+ goto err;
+ }
+ }
+
+ y = (v[0] - '0') * 1000 + (v[1] - '0') * 100 + (v[2] - '0') * 10 + (v[3] - '0');
+ M = (v[4] - '0') * 10 + (v[5] - '0');
+
+ if ((M > 12) || (M < 1))
+ {
+ goto err;
+ }
+
+ d = (v[6] - '0') * 10 + (v[7] - '0');
+ h = (v[8] - '0') * 10 + (v[9] - '0');
+ m = (v[10] - '0') * 10 + (v[11] - '0');
+
+ if ((v[12] >= '0') && (v[12] <= '9') &&
+ (v[13] >= '0') && (v[13] <= '9'))
+ {
+ s = (v[12] - '0') * 10 + (v[13] - '0');
+ }
+
+ sprintf(buf, "%s %2d %02d:%02d:%02d %d%s", mon[M-1], d, h, m, s, y, (gmt)?" GMT":"");
+ return string(buf);
+
+err:
+ return string("Bad time value");
+}
+
+string
+getUTCTime(ASN1_UTCTIME *tm)
+{
+ char buf[30];
+ char *v;
+ int gmt=0;
+ int i;
+ int y = 0, M = 0, d = 0, h = 0, m = 0, s = 0;
+
+ i = tm->length;
+ v = (char *) tm->data;
+
+ if (i < 10)
+ {
+ goto err;
+ }
+
+ if (v[i-1] == 'Z')
+ {
+ gmt=1;
+ }
+
+ for (i = 0; i < 10; i++)
+ {
+ if ((v[i] > '9') || (v[i] < '0'))
+ {
+ goto err;
+ }
+ }
+
+ y = (v[0] - '0') * 10 + (v[1] - '0');
+
+ if (y < 50)
+ {
+ y+=100;
+ }
+
+ M = (v[2] - '0') * 10 + (v[3] - '0');
+
+ if ((M > 12) || (M < 1))
+ {
+ goto err;
+ }
+
+ d = (v[4] - '0') * 10 + (v[5] - '0');
+ h = (v[6] - '0') * 10 + (v[7] - '0');
+ m = (v[8] - '0') * 10 + (v[9] - '0');
+
+ if ((v[10] >= '0') && (v[10] <= '9') && (v[11] >= '0') && (v[11] <= '9'))
+ {
+ s = (v[10] - '0') * 10 + (v[11] - '0');
+ }
+
+ sprintf(buf, "%s %2d %02d:%02d:%02d %d%s", mon[M-1], d, h, m, s, y+1900, (gmt)?" GMT":"");
+ return string(buf);
+
+err:
+ return string("Bad time value");
+}
+
+string
+getASN1time(ASN1_TIME *tm)
+{
+ string theTime;
+
+ switch (tm->type)
+ {
+ case V_ASN1_UTCTIME :
+ {
+ theTime = getUTCTime(tm);
+ }
+
+ case V_ASN1_GENERALIZEDTIME :
+ {
+ theTime = getGeneralizedTime(tm);
+ }
+
+ default :
+ {
+ theTime = "Bad time value";
+ }
+ }
+
+ return theTime;
+}
+
+extern "C"
+{
+
+RSA*
+tmpRSACallback(SSL *s, int isExport, int keyLength)
+{
+ IceSecurity::Ssl::System* sslSystem = IceSecurity::Ssl::Factory::getSystemFromHandle(s);
+
+ IceSecurity::Ssl::OpenSSL::System* openSslSystem = dynamic_cast<IceSecurity::Ssl::OpenSSL::System*>(sslSystem);
+
+ RSA* rsaKey = openSslSystem->getRSAKey(s, isExport, keyLength);
+
+ IceSecurity::Ssl::Factory::releaseSystemFromHandle(s, sslSystem);
+
+ return rsaKey;
+}
+
+DH*
+tmpDHCallback(SSL *s, int isExport, int keyLength)
+{
+ IceSecurity::Ssl::System* sslSystem = IceSecurity::Ssl::Factory::getSystemFromHandle(s);
+
+ IceSecurity::Ssl::OpenSSL::System* openSslSystem = dynamic_cast<IceSecurity::Ssl::OpenSSL::System*>(sslSystem);
+
+ DH* dh = openSslSystem->getDHParams(s, isExport, keyLength);
+
+ IceSecurity::Ssl::Factory::releaseSystemFromHandle(s, sslSystem);
+
+ return dh;
+}
+
+// verifyCallback - Certificate Verification callback function.
+int
+verifyCallback(int ok, X509_STORE_CTX *ctx)
+{
+ X509* err_cert = X509_STORE_CTX_get_current_cert(ctx);
+ int verifyError = X509_STORE_CTX_get_error(ctx);
+ int depth = X509_STORE_CTX_get_error_depth(ctx);
+
+ // If we have no errors so far, and the certificate chain is too long
+ if ((verifyError != X509_V_OK) && (10 < depth))
+ {
+ verifyError = X509_V_ERR_CERT_CHAIN_TOO_LONG;
+ }
+
+ if (verifyError != X509_V_OK)
+ {
+ // If we have ANY errors, we bail out.
+ ok = 0;
+ }
+
+ // Only if PROTOCOL level logging is on do we worry about this.
+ if (IceSecurity::Ssl::OpenSSL::System::_globalTraceLevels->security >= IceSecurity::SECURITY_PROTOCOL)
+ {
+ char buf[256];
+
+ X509_NAME_oneline(X509_get_subject_name(err_cert), buf, sizeof(buf));
+
+ ostringstream outStringStream;
+
+ outStringStream << "depth = " << depth << ":" << buf << endl;
+
+ if (!ok)
+ {
+ outStringStream << "verify error: num = " << verifyError << " : " << X509_verify_cert_error_string(verifyError) << endl;
+
+ }
+
+ switch (verifyError)
+ {
+ case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT:
+ {
+ X509_NAME_oneline(X509_get_issuer_name(ctx->current_cert), buf, sizeof(buf));
+ outStringStream << "issuer = " << buf << endl;
+ break;
+ }
+
+ case X509_V_ERR_CERT_NOT_YET_VALID:
+ case X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD:
+ {
+ outStringStream << "notBefore =" << getASN1time(X509_get_notBefore(ctx->current_cert)) << endl;
+ break;
+ }
+
+ case X509_V_ERR_CERT_HAS_EXPIRED:
+ case X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD:
+ {
+ outStringStream << "notAfter =" << getASN1time(X509_get_notAfter(ctx->current_cert)) << endl;
+ break;
+ }
+ }
+
+ outStringStream << "verify return = " << ok << endl;
+
+ IceSecurity::Ssl::OpenSSL::System::_globalLogger->trace(IceSecurity::Ssl::OpenSSL::System::_globalTraceLevels->securityCat, outStringStream.str());
+ }
+
+ return ok;
+}
+
+// This code duplicates functionality that existed in the BIO library of
+// OpenSSL, but outputs to a Logger compatible source (ostringstream).
+void
+dump(ostringstream& outStringStream, const char* s, int len)
+{
+ unsigned char ch;
+ char hexStr[8];
+
+ int trunc = 0;
+
+ // Calculate how much white space we're truncating.
+ for(; (len > 0) && ((s[len - 1] == ' ') || (s[len - 1] == '\0')); len--)
+ {
+ trunc++;
+ }
+
+ int dump_width = 12;
+
+ int rows = len / dump_width;
+
+ if ((rows * dump_width) < len)
+ {
+ rows++;
+ }
+
+ if (rows > 0)
+ {
+ outStringStream << endl;
+ }
+
+ for(int i = 0; i < rows; i++)
+ {
+ // Would like to have not used sprintf(), but
+ // I could not find an appropriate STL methodology
+ // for preserving the field width.
+ sprintf(hexStr,"%04x",(i * dump_width));
+ outStringStream << hexStr << " - ";
+
+ int j;
+
+ // Hex Dump
+ for(j = 0; j < dump_width; j++)
+ {
+ if (((i * dump_width) + j) >= len)
+ {
+ outStringStream << " ";
+ }
+ else
+ {
+ char sep = (j == 7 ? '-' : ' ');
+
+ // Get a character from the dump we've been handed.
+ ch = ((unsigned char)*(s + i * dump_width + j)) & 0xff;
+
+ // Would like to have not used sprintf(), but
+ // I could not find an appropriate STL methodology
+ // for preserving the field width.
+ sprintf(hexStr,"%02x",ch);
+ outStringStream << hexStr << sep;
+ }
+ }
+
+ outStringStream << " ";
+
+ // Printable characters dump.
+ for(j = 0; j < dump_width; j++)
+ {
+ if (((i * dump_width) + j) >= len)
+ {
+ break;
+ }
+
+ ch = ((unsigned char) * (s + i * dump_width + j)) & 0xff;
+
+ // Print printables only.
+ ch = ((ch >= ' ') && (ch <= '~')) ? ch : '.';
+
+ outStringStream << ch;
+ }
+
+ outStringStream << endl;
+ }
+
+ if (trunc > 0)
+ {
+ outStringStream << hex << (len + trunc) << " - " << "<SPACES/NULS>" << endl;
+ }
+}
+
+long
+bio_dump_cb(BIO *bio, int cmd, const char *argp, int argi, long argl, long ret)
+{
+ if (IceSecurity::Ssl::OpenSSL::System::_globalTraceLevels->security >= IceSecurity::SECURITY_PROTOCOL)
+ {
+ ostringstream outStringStream;
+
+ if (cmd == (BIO_CB_READ|BIO_CB_RETURN))
+ {
+ outStringStream << "PTC ";
+ outStringStream << "read from " << hex << (void *)bio << " [" << hex << (void *)argp;
+ outStringStream << "] (" << dec << argi << " bytes => " << ret << " (0x";
+ outStringStream << hex << ret << "))";
+ dump(outStringStream, argp,(int)ret);
+ }
+ else if (cmd == (BIO_CB_WRITE|BIO_CB_RETURN))
+ {
+ outStringStream << "PTC ";
+ outStringStream << "write to " << hex << (void *)bio << " [" << hex << (void *)argp;
+ outStringStream << "] (" << dec << argi << " bytes => " << ret << " (0x";
+ outStringStream << hex << ret << "))";
+ dump(outStringStream, argp,(int)ret);
+ }
+
+ if (cmd == (BIO_CB_READ|BIO_CB_RETURN) || cmd == (BIO_CB_WRITE|BIO_CB_RETURN))
+ {
+ IceSecurity::Ssl::OpenSSL::System::_globalLogger->trace(IceSecurity::Ssl::OpenSSL::System::_globalTraceLevels->securityCat, outStringStream.str());
+ }
+ }
+
+ return ret;
+}
+
+}
+
+void
+IceSecurity::Ssl::OpenSSL::System::printContextInfo(SSL_CTX* context)
+{
+ if (SECURITY_LEVEL_PROTOCOL)
+ {
+ ostringstream s;
+
+ s << endl;
+ s << "SSL_CTX Structure" << endl;
+ s << "=================" << endl;
+ s << "options: 0x" << hex << context->options << endl;
+ s << "mode: 0x" << hex << context->mode << endl;
+
+ s << "session_cache_size: " << context->session_cache_size << endl;
+ s << "session_cache_mode: 0x" << hex << context->session_cache_mode << endl;
+ s << "session_timeout: " << Int(context->session_timeout) << endl << endl;
+
+ s << "Stats" << endl;
+ s << "Connect: " << context->stats.sess_connect << " (New Connect Started)" << endl;
+ s << "Renegotiate: " << context->stats.sess_connect_renegotiate << " (Renegotiation Requested)" << endl;
+ s << "Connect Good: " << context->stats.sess_connect_good << " (Connect/Renegotiation finished)";
+ s << endl << endl;
+
+ s << "Accept: " << context->stats.sess_accept << " (New Accept Started)" << endl;
+ s << "Renegotiate: " << context->stats.sess_accept_renegotiate << " (Renegotiation Requested)" << endl;
+ s << "Accept Good: " << context->stats.sess_accept_good << " (Accept/Renegotiation finished)";
+ s << endl << endl;
+
+ s << "Miss: " << context->stats.sess_miss << " (Session Lookup Misses)" << endl;
+ s << "Timeout: " << context->stats.sess_timeout << " (Reuse attempt on Timeouted Session)" << endl;
+ s << "Cache Full: " << context->stats.sess_cache_full << " (Session Removed due to full cache)" << endl;
+ s << "Hit: " << context->stats.sess_hit << " (Session Reuse actually done.)" << endl;
+ s << "CB Hit: " << context->stats.sess_cb_hit << " (Session ID supplied by Callback)" << endl;
+
+ s << "read_ahead: " << context->read_ahead << endl;
+ s << "verify_mode: 0x" << hex << context->verify_mode << endl;
+ s << "verify_depth: " << Int(context->verify_depth) << endl;
+
+ PROTOCOL(s.str());
+ }
+}
+
+IceSecurity::Ssl::Connection*
+IceSecurity::Ssl::OpenSSL::System::createServerConnection(int socket)
+{
+ METHOD_INV("OpenSSL::System::createServerConnection()");
+
+ SSL* sslConnection = createConnection(_sslServerContext, socket);
+
+ // Set the Accept Connection state for this connection.
+ SSL_set_accept_state(sslConnection);
+
+ Connection* connection = new ServerConnection(sslConnection, _systemID);
+
+ connection->setTrace(_traceLevels);
+ connection->setLogger(_logger);
+
+ continueInit:
+ try
+ {
+ while (!connection->init()) { }
+ }
+ catch (const TimeoutException&)
+ {
+ // Ignore, this might happen a lot during handshake.
+ goto continueInit;
+ }
+ catch (...)
+ {
+ if (connection != 0)
+ {
+ delete connection;
+ connection = 0;
+ }
+
+ throw;
+ }
+
+ METHOD_RET("OpenSSL::System::createServerConnection()");
+
+ return connection;
+}
+
+IceSecurity::Ssl::Connection*
+IceSecurity::Ssl::OpenSSL::System::createClientConnection(int socket)
+{
+ METHOD_INV("OpenSSL::System::createClientConnection()");
+
+ SSL* sslConnection = createConnection(_sslClientContext, socket);
+
+ // Set the Connect Connection state for this connection.
+ SSL_set_connect_state(sslConnection);
+
+ Connection* connection = new ClientConnection(sslConnection, _systemID);
+
+ connection->setTrace(_traceLevels);
+ connection->setLogger(_logger);
+
+ continueInit:
+ try
+ {
+ while (!connection->init()) { }
+ }
+ catch (const TimeoutException&)
+ {
+ // Ignore, this might happen a lot during handshake.
+ goto continueInit;
+ }
+ catch (...)
+ {
+ if (connection != 0)
+ {
+ delete connection;
+ connection = 0;
+ }
+
+ throw;
+ }
+
+ METHOD_RET("OpenSSL::System::createClientConnection()");
+
+ return connection;
+}
+
+void
+IceSecurity::Ssl::OpenSSL::System::shutdown()
+{
+ METHOD_INV("OpenSSL::System::shutdown()");
+
+ if (_sslServerContext != 0)
+ {
+ SSL_CTX_free(_sslServerContext);
+
+ _sslServerContext = 0;
+ }
+
+ if (_sslClientContext != 0)
+ {
+ SSL_CTX_free(_sslClientContext);
+
+ _sslClientContext = 0;
+ }
+
+ // Free our temporary RSA keys.
+ RSAMap::iterator iRSA = _tempRSAKeys.begin();
+ RSAMap::iterator eRSA = _tempRSAKeys.end();
+
+ while (iRSA != eRSA)
+ {
+ RSA_free((*iRSA).second);
+ iRSA++;
+ }
+
+ // Free our temporary DH params.
+ DHMap::iterator iDH = _tempDHKeys.begin();
+ DHMap::iterator eDH = _tempDHKeys.end();
+
+ while (iDH != eDH)
+ {
+ DH_free((*iDH).second);
+ iDH++;
+ }
+
+ METHOD_RET("OpenSSL::System::shutdown()");
+}
+
+bool
+IceSecurity::Ssl::OpenSSL::System::isConfigLoaded()
+{
+ METHOD_INS("OpenSSL::System::isConfigLoaded()");
+
+ return _configLoaded;
+}
+
+void
+IceSecurity::Ssl::OpenSSL::System::loadConfig()
+{
+ METHOD_INV("OpenSSL::System::loadConfig()");
+
+ // This step is required in order to supply callback functions
+ // with access to the TraceLevels and Logger.
+ if (_globalTraceLevels == 0)
+ {
+ _globalTraceLevels = _traceLevels;
+ _globalLogger = _logger;
+ }
+
+ const string& systemID = getSystemID();
+ Parser sslConfig(systemID);
+
+ sslConfig.setTrace(_traceLevels);
+ sslConfig.setLogger(_logger);
+
+ // Actually parse the file now.
+ sslConfig.process();
+
+ GeneralConfig clientGeneral;
+ CertificateAuthority clientCertAuth;
+ BaseCertificates clientBaseCerts;
+
+ // Walk the parse tree, get the Client configuration.
+ if (sslConfig.loadClientConfig(clientGeneral, clientCertAuth, clientBaseCerts))
+ {
+ if (SECURITY_LEVEL_PROTOCOL)
+ {
+ ostringstream s;
+
+ s << endl;
+ s << "General Configuration - Client" << endl;
+ s << "------------------------------" << endl;
+ s << clientGeneral << endl << endl;
+
+ s << "Base Certificates - Client" << endl;
+ s << "--------------------------" << endl;
+ s << clientBaseCerts << endl;
+
+ PROTOCOL(s.str());
+ }
+
+ initClient(clientGeneral, clientCertAuth, clientBaseCerts);
+ }
+
+ GeneralConfig serverGeneral;
+ CertificateAuthority serverCertAuth;
+ BaseCertificates serverBaseCerts;
+ TempCertificates serverTempCerts;
+
+ // Walk the parse tree, get the Server configuration.
+ if (sslConfig.loadServerConfig(serverGeneral, serverCertAuth, serverBaseCerts, serverTempCerts))
+ {
+ if (SECURITY_LEVEL_PROTOCOL)
+ {
+ ostringstream s;
+
+ s << endl;
+ s << "General Configuration - Server" << endl;
+ s << "------------------------------" << endl;
+ s << serverGeneral << endl << endl;
+
+ s << "Base Certificates - Server" << endl;
+ s << "--------------------------" << endl;
+ s << serverBaseCerts << endl << endl;
+
+ s << "Temp Certificates - Server" << endl;
+ s << "--------------------------" << endl;
+ s << serverTempCerts << endl;
+
+ PROTOCOL(s.str());
+ }
+
+ initServer(serverGeneral, serverCertAuth, serverBaseCerts, serverTempCerts);
+ }
+
+ METHOD_RET("OpenSSL::System::loadConfig()");
+}
+
+RSA*
+IceSecurity::Ssl::OpenSSL::System::getRSAKey(SSL *s, int isExport, int keyLength)
+{
+ METHOD_INV("OpenSSL::System::getRSAKey()");
+
+ JTCSyncT<JTCMutex> sync(_tempRSAKeysMutex);
+
+ RSA* rsa_tmp = 0;
+
+ RSAMap::iterator retVal = _tempRSAKeys.find(keyLength);
+
+ // Does the key already exist?
+ if (retVal != _tempRSAKeys.end())
+ {
+ // Yes! Use it.
+ rsa_tmp = (*retVal).second;
+ }
+ else
+ {
+ const RSACertMap::iterator& it = _tempRSAFileMap.find(keyLength);
+
+ if (it != _tempRSAFileMap.end())
+ {
+ CertificateDesc& rsaKeyCert = (*it).second;
+
+ const string& privKeyFile = rsaKeyCert.getPrivate().getFileName();
+ const string& pubCertFile = rsaKeyCert.getPublic().getFileName();
+
+ RSA* rsaCert = 0;
+ RSA* rsaKey = 0;
+ BIO* bio = 0;
+
+ if ((bio = BIO_new_file(pubCertFile.c_str(), "r")) != 0)
+ {
+ rsaCert = PEM_read_bio_RSAPublicKey(bio, 0, 0, 0);
+
+ BIO_free(bio);
+ bio = 0;
+ }
+
+ if (rsaCert != 0)
+ {
+ if ((bio = BIO_new_file(privKeyFile.c_str(), "r")) != 0)
+ {
+ rsaKey = PEM_read_bio_RSAPrivateKey(bio, &rsaCert, 0, 0);
+
+ BIO_free(bio);
+ bio = 0;
+ }
+ }
+
+ // Now, if all was well, the Certificate and Key should both be loaded into
+ // rsaCert. We check to ensure that both are not 0, because if either are,
+ // one of the reads failed.
+
+ if ((rsaCert != 0) && (rsaKey != 0))
+ {
+ rsa_tmp = rsaCert;
+ }
+ else
+ {
+ RSA_free(rsaCert);
+ rsaCert = 0;
+ }
+ }
+
+ // Last ditch effort - generate a key on the fly.
+ if (rsa_tmp == 0)
+ {
+ rsa_tmp = RSA_generate_key(keyLength, RSA_F4, 0, 0);
+ }
+
+ // Save in our temporary key cache.
+ if (rsa_tmp == 0)
+ {
+ _tempRSAKeys[keyLength] = rsa_tmp;
+ }
+ }
+
+ METHOD_RET("OpenSSL::System::getRSAKey()");
+
+ return rsa_tmp;
+}
+
+DH*
+IceSecurity::Ssl::OpenSSL::System::getDHParams(SSL *s, int isExport, int keyLength)
+{
+ METHOD_INV("OpenSSL::System::getDHParams()");
+
+ JTCSyncT<JTCMutex> sync(_tempDHKeysMutex);
+
+ DH *dh_tmp = 0;
+
+ const DHMap::iterator& retVal = _tempDHKeys.find(keyLength);
+
+ // Does the key already exist?
+ if (retVal != _tempDHKeys.end())
+ {
+ // Yes! Use it.
+ dh_tmp = (*retVal).second;
+ }
+ else
+ {
+ const DHParamsMap::iterator& it = _tempDHParamsFileMap.find(keyLength);
+
+ if (it != _tempDHParamsFileMap.end())
+ {
+ DiffieHellmanParamsFile& dhParamsFile = (*it).second;
+
+ string dhFile = dhParamsFile.getFileName();
+
+ dh_tmp = loadDHParam(dhFile.c_str());
+
+ if (dh_tmp != 0)
+ {
+ _tempDHKeys[keyLength] = dh_tmp;
+ }
+ }
+ }
+
+ METHOD_RET("OpenSSL::System::getDHParams()");
+
+ return dh_tmp;
+}
+
+//
+// Protected
+//
+
+IceSecurity::Ssl::OpenSSL::System::System(string& systemID) :
+ IceSecurity::Ssl::System(systemID)
+{
+ _configLoaded = false;
+
+ _sessionContext = "iceServer";
+
+ _sslServerContext = 0;
+ _sslClientContext = 0;
+
+ SSL_load_error_strings();
+
+ OpenSSL_add_ssl_algorithms();
+}
+
+IceSecurity::Ssl::OpenSSL::System::~System()
+{
+ METHOD_INV("OpenSSL::~System()");
+
+ shutdown();
+
+ METHOD_RET("OpenSSL::~System()");
+}
+
+//
+// Private
+//
+
+void
+IceSecurity::Ssl::OpenSSL::System::initClient(GeneralConfig& general,
+ CertificateAuthority& certAuth,
+ BaseCertificates& baseCerts)
+{
+ METHOD_INV("OpenSSL::System::initClient()");
+
+ // Init the Random Number System.
+ initRandSystem(general.getRandomBytesFiles());
+
+ // Create an SSL Context based on the context params.
+ _sslClientContext = createContext(general.getProtocol());
+
+ // Begin setting up the SSL Context.
+ if (_sslClientContext != 0)
+ {
+ // Get the cipherlist and set it in the context.
+ setCipherList(_sslClientContext, general.getCipherList());
+
+ // Set the certificate verification mode.
+ SSL_CTX_set_verify(_sslClientContext, general.getVerifyMode(), verifyCallback);
+
+ // Set the certificate verify depth to 10 deep.
+ SSL_CTX_set_verify_depth(_sslClientContext, general.getVerifyDepth());
+
+ // Process the RSA Certificate (if present).
+ if (baseCerts.getRSACert().getKeySize() != 0)
+ {
+ processCertificate(_sslClientContext, baseCerts.getRSACert());
+ }
+
+ // Process the DSA Certificate (if present).
+ if (baseCerts.getDSACert().getKeySize() != 0)
+ {
+ processCertificate(_sslClientContext, baseCerts.getDSACert());
+ }
+
+ // Set the DH key agreement parameters.
+ if (baseCerts.getDHParams().getKeySize() != 0)
+ {
+ setDHParams(_sslClientContext, baseCerts);
+ }
+
+ // Load the Certificate Authority files, and check them.
+ loadCAFiles(_sslClientContext, certAuth);
+ }
+
+ METHOD_RET("OpenSSL::System::initClient()");
+}
+
+void
+IceSecurity::Ssl::OpenSSL::System::initServer(GeneralConfig& general,
+ CertificateAuthority& certAuth,
+ BaseCertificates& baseCerts,
+ TempCertificates& tempCerts)
+{
+ METHOD_INV("OpenSSL::System::initServer()");
+
+ // Init the Random Number System.
+ initRandSystem(general.getRandomBytesFiles());
+
+ // Create an SSL Context based on the context params.
+ _sslServerContext = createContext(general.getProtocol());
+
+ // Begin setting up the SSL Context.
+ if (_sslServerContext != 0)
+ {
+ // On servers, Attempt to use non-export (strong) encryption
+ // first. This option does not always work, and in the OpenSSL
+ // documentation is declared as 'broken'.
+ // SSL_CTX_set_options(_sslServerContext,SSL_OP_NON_EXPORT_FIRST);
+
+ // Always use a new DH key when using Diffie-Hellman key agreement.
+ SSL_CTX_set_options(_sslServerContext, SSL_OP_SINGLE_DH_USE);
+
+ loadTempCerts(tempCerts);
+
+ // Load the Certificate Authority files, and check them.
+ loadAndCheckCAFiles(_sslServerContext, certAuth);
+
+ // Process the RSA Certificate (if present).
+ if (baseCerts.getRSACert().getKeySize() != 0)
+ {
+ processCertificate(_sslServerContext, baseCerts.getRSACert());
+ }
+
+ // Process the DSA Certificate (if present).
+ if (baseCerts.getDSACert().getKeySize() != 0)
+ {
+ processCertificate(_sslServerContext, baseCerts.getDSACert());
+ }
+
+ // Set the DH key agreement parameters.
+ if (baseCerts.getDHParams().getKeySize() != 0)
+ {
+ setDHParams(_sslServerContext, baseCerts);
+ }
+
+ // Set the RSA Callback routine in case we need to build a temporary RSA key.
+ SSL_CTX_set_tmp_rsa_callback(_sslServerContext, tmpRSACallback);
+
+ // Set the DH Callback routine in case we need a temporary DH key.
+ SSL_CTX_set_tmp_dh_callback(_sslServerContext, tmpDHCallback);
+
+ // Get the cipherlist and set it in the context.
+ setCipherList(_sslServerContext, general.getCipherList());
+
+ // Set the certificate verification mode.
+ SSL_CTX_set_verify(_sslServerContext, general.getVerifyMode(), verifyCallback);
+
+ // Set the certificate verify depth
+ SSL_CTX_set_verify_depth(_sslServerContext, general.getVerifyDepth());
+
+ // Set the default context for the SSL system (can be overridden if needed) [SERVER ONLY].
+ SSL_CTX_set_session_id_context(_sslServerContext,
+ reinterpret_cast<const unsigned char *>(_sessionContext.c_str()),
+ _sessionContext.size());
+ }
+
+ printContextInfo(_sslServerContext);
+
+ METHOD_RET("OpenSSL::System::initServer()");
+}
+
+SSL_METHOD*
+IceSecurity::Ssl::OpenSSL::System::getSslMethod(SslProtocol sslVersion)
+{
+ METHOD_INV("OpenSSL::System::getSslMethod()");
+
+ SSL_METHOD* sslMethod = 0;
+
+ switch (sslVersion)
+ {
+ case SSL_V2 :
+ {
+ sslMethod = SSLv2_method();
+ break;
+ }
+
+ case SSL_V23 :
+ {
+ sslMethod = SSLv23_method();
+ break;
+ }
+
+ case SSL_V3 :
+ {
+ sslMethod = SSLv3_method();
+ break;
+ }
+
+ case TLS_V1 :
+ {
+ sslMethod = TLSv1_method();
+ break;
+ }
+
+ default :
+ {
+ string errorString;
+
+ errorString = "SSL Version ";
+ errorString += sslVersion;
+ errorString += " not supported.";
+
+ EXCEPTION(errorString);
+
+ throw ContextException(errorString.c_str(), __FILE__, __LINE__);
+ }
+ }
+
+ METHOD_RET("OpenSSL::System::getSslMethod()");
+
+ return sslMethod;
+}
+
+void
+IceSecurity::Ssl::OpenSSL::System::processCertificate(SSL_CTX* sslContext, const CertificateDesc& certificateDesc)
+{
+ METHOD_INV("OpenSSL::System::processCertificate()");
+
+ const CertificateFile& publicCert = certificateDesc.getPublic();
+ const CertificateFile& privateKey = certificateDesc.getPrivate();
+
+ addKeyCert(sslContext, publicCert, privateKey);
+
+ METHOD_RET("OpenSSL::System::processCertificate()");
+}
+
+void
+IceSecurity::Ssl::OpenSSL::System::addKeyCert(SSL_CTX* sslContext,
+ const CertificateFile& publicCert,
+ const CertificateFile& privateKey)
+{
+ METHOD_INV("OpenSSL::System::addKeyCert()");
+
+ if (!publicCert.getFileName().empty())
+ {
+ string publicCertFile = publicCert.getFileName();
+ const char* publicFile = publicCertFile.c_str();
+ int publicEncoding = publicCert.getEncoding();
+
+ string privCertFile = privateKey.getFileName();
+ const char* privKeyFile = privCertFile.c_str();
+ int privKeyFileType = privateKey.getEncoding();
+
+ // Set which Public Key file to use.
+ if (SSL_CTX_use_certificate_file(sslContext, publicFile, publicEncoding) <= 0)
+ {
+ string errorString;
+
+ errorString = "Unable to get certificate from '";
+ errorString += publicFile;
+ errorString += "'\n";
+ errorString += sslGetErrors();
+
+ EXCEPTION(errorString);
+
+ throw ContextException(errorString.c_str(), __FILE__, __LINE__);
+ }
+
+ if (privateKey.getFileName().empty())
+ {
+ WARNING("No Private Key specified - using the certificate.");
+
+ privKeyFile = publicFile;
+ privKeyFileType = publicEncoding;
+ }
+
+ // Set which Private Key file to use.
+ if (SSL_CTX_use_PrivateKey_file(sslContext, privKeyFile, privKeyFileType) <= 0)
+ {
+ string errorString;
+
+ errorString = "unable to get private key from '";
+ errorString += privKeyFile;
+ errorString += "'\n";
+ errorString += sslGetErrors();
+
+ EXCEPTION(errorString);
+
+ throw ContextException(errorString.c_str(), __FILE__, __LINE__);
+ }
+
+ // Check to see if the Private and Public keys that have been
+ // set against the SSL context match up.
+ if (!SSL_CTX_check_private_key(sslContext))
+ {
+ string errorString = "Private key does not match the certificate public key";
+
+ EXCEPTION(errorString);
+
+ throw ContextException(errorString.c_str(), __FILE__, __LINE__);
+ }
+
+ }
+
+ METHOD_RET("OpenSSL::System::addKeyCert()");
+}
+
+
+SSL_CTX*
+IceSecurity::Ssl::OpenSSL::System::createContext(SslProtocol sslProtocol)
+{
+ METHOD_INV("OpenSSL::System::createContext()");
+
+ SSL_CTX* context = SSL_CTX_new(getSslMethod(sslProtocol));
+
+ if (context == 0)
+ {
+ string errorString = "Unable to create SSL Context.";
+
+ EXCEPTION(errorString);
+
+ throw ContextException(errorString.c_str(), __FILE__, __LINE__);
+ }
+
+ METHOD_RET("OpenSSL::System::createContext()");
+
+ return context;
+}
+
+
+//
+// TODO: Clean this up. This routine is pretty much ripped off from the OpenSSL
+// routine ERR_print_errors_fp(), except we got rid of the file pointer.
+//
+string
+IceSecurity::Ssl::OpenSSL::System::sslGetErrors()
+{
+ METHOD_INV("OpenSSL::System::sslGetErrors()");
+
+ string errorMessage;
+ char buf[200], bigBuffer[1024];
+ const char* file;
+ const char* data = 0;
+ int line, flags;
+
+ unsigned long es = CRYPTO_thread_id();
+
+ while ((line = ERR_get_error_line_data(&file, &line, &data, &flags)) != 0)
+ {
+ // Request an error from the OpenSSL library
+ ERR_error_string_n(line, buf, sizeof(buf));
+
+ sprintf(bigBuffer,"%lu:%s:%s:%d:%s\n",es,buf,file,line,(flags&ERR_TXT_STRING)?data:"");
+
+ errorMessage += bigBuffer;
+ }
+
+ METHOD_RET("OpenSSL::System::sslGetErrors()");
+
+ return errorMessage;
+}
+
+SSL*
+IceSecurity::Ssl::OpenSSL::System::createConnection(SSL_CTX* sslContext, int socket)
+{
+ METHOD_INV("OpenSSL::System::createConnection()");
+
+ SSL *sslConnection = 0;
+
+ sslConnection = SSL_new(sslContext);
+
+ SSL_clear(sslConnection);
+
+ SSL_set_fd(sslConnection, socket);
+
+ if (SECURITY_LEVEL_PROTOCOL_DEBUG)
+ {
+ sslConnection->debug = 1;
+ BIO_set_callback(SSL_get_rbio(sslConnection), bio_dump_cb);
+ BIO_set_callback_arg(SSL_get_rbio(sslConnection), 0);
+ BIO_set_callback(SSL_get_wbio(sslConnection), bio_dump_cb);
+ BIO_set_callback_arg(SSL_get_rbio(sslConnection), 0);
+ }
+
+ // Map the SSL Connection to this SslSystem
+ // This is required for the OpenSSL callbacks
+ // to work properly.
+ Factory::addSystemHandle(sslConnection, this);
+
+ METHOD_RET("OpenSSL::System::createConnection()");
+
+ return sslConnection;
+}
+
+void
+IceSecurity::Ssl::OpenSSL::System::loadCAFiles(SSL_CTX* sslContext, CertificateAuthority& certAuth)
+{
+ METHOD_INV("OpenSSL::System::loadCAFiles()");
+
+ string caFile = certAuth.getCAFileName();
+ string caPath = certAuth.getCAPath();
+
+ loadCAFiles(sslContext, caFile.c_str(), caPath.c_str());
+
+ METHOD_RET("OpenSSL::System::loadCAFiles()");
+}
+
+void
+IceSecurity::Ssl::OpenSSL::System::loadCAFiles(SSL_CTX* sslContext, const char* caFile, const char* caPath)
+{
+ METHOD_INV("OpenSSL::System::loadCAFiles()");
+
+ if (sslContext != 0)
+ {
+ // The following checks are required to send the expected values to the OpenSSL library.
+ // It does not like receiving "", but prefers NULLs.
+ if ((caFile != 0) && (strlen(caFile) == 0))
+ {
+ caFile = 0;
+ }
+
+ if ((caPath != 0) && (strlen(caPath) == 0))
+ {
+ caPath = 0;
+ }
+
+ // Check the Certificate Authority file(s).
+ if ((!SSL_CTX_load_verify_locations(sslContext, caFile, caPath)) ||
+ (!SSL_CTX_set_default_verify_paths(sslContext)))
+ {
+ // Non Fatal.
+ WARNING("Unable to load/verify Certificate Authorities.");
+ }
+ }
+
+ METHOD_RET("OpenSSL::System::loadCAFiles()");
+}
+
+void
+IceSecurity::Ssl::OpenSSL::System::loadAndCheckCAFiles(SSL_CTX* sslContext, CertificateAuthority& certAuth)
+{
+ METHOD_INV("OpenSSL::System::loadAndCheckCAFiles()");
+
+ if (sslContext != 0)
+ {
+ string caFile = certAuth.getCAFileName();
+ string caPath = certAuth.getCAPath();
+
+ // Check the Certificate Authority file(s).
+ loadCAFiles(sslContext, caFile.c_str(), caPath.c_str());
+
+ if (!caPath.empty())
+ {
+ STACK_OF(X509_NAME)* certNames = SSL_load_client_CA_file(caFile.c_str());
+
+ if (certNames != 0)
+ {
+ SSL_CTX_set_client_CA_list(sslContext, certNames);
+ }
+ else
+ {
+ WARNING("Unable to set SSL context Certificate Authorities.");
+ }
+ }
+ }
+
+ METHOD_RET("OpenSSL::System::loadAndCheckCAFiles()");
+}
+
+DH*
+IceSecurity::Ssl::OpenSSL::System::loadDHParam(const char* dhfile)
+{
+ METHOD_INV(string("OpenSSL::System::loadDHParam(") + dhfile + string(")"));
+
+ DH* ret = 0;
+ BIO* bio;
+
+ if ((bio = BIO_new_file(dhfile,"r")) != 0)
+ {
+ ret = PEM_read_bio_DHparams(bio, 0, 0, 0);
+ }
+
+ if (bio != 0)
+ {
+ BIO_free(bio);
+ }
+
+ METHOD_RET(string("OpenSSL::System::loadDHParam(") + dhfile + string(")"));
+
+ return ret;
+}
+
+DH*
+IceSecurity::Ssl::OpenSSL::System::getTempDH(unsigned char* p, int plen, unsigned char* g, int glen)
+{
+ METHOD_INV("OpenSSL::System::getTempDH()");
+
+ DH* dh = 0;
+
+ if ((dh = DH_new()) != 0)
+ {
+ dh->p = BN_bin2bn(p, plen, 0);
+
+ dh->g = BN_bin2bn(g, glen, 0);
+
+ if ((dh->p == 0) || (dh->g == 0))
+ {
+ DH_free(dh);
+ dh = 0;
+ }
+ }
+
+ METHOD_RET("OpenSSL::System::getTempDH()");
+
+ return dh;
+}
+
+DH*
+IceSecurity::Ssl::OpenSSL::System::getTempDH512()
+{
+ METHOD_INV("OpenSSL::System::getTempDH512()");
+
+ DH* dh = getTempDH(_tempDiffieHellman512p, sizeof(_tempDiffieHellman512p),
+ _tempDiffieHellman512g, sizeof(_tempDiffieHellman512g));
+
+ METHOD_RET("OpenSSL::System::getTempDH512()");
+
+ return dh;
+}
+
+void
+IceSecurity::Ssl::OpenSSL::System::setDHParams(SSL_CTX* sslContext, BaseCertificates& baseCerts)
+{
+ METHOD_INV("OpenSSL::System::setDHParams()");
+
+ string dhFile;
+ int encoding = 0;
+
+ if (baseCerts.getDHParams().getKeySize() != 0)
+ {
+ dhFile = baseCerts.getDHParams().getFileName();
+ encoding = baseCerts.getDHParams().getEncoding();
+ }
+ else if (baseCerts.getRSACert().getKeySize() != 0)
+ {
+ dhFile = baseCerts.getRSACert().getPublic().getFileName();
+ encoding = baseCerts.getRSACert().getPublic().getEncoding();
+ }
+
+ DH* dh = 0;
+
+ // File type must be PEM - that's the only way we can load
+ // DH Params, apparently.
+ if ((!dhFile.empty()) && (encoding == SSL_FILETYPE_PEM))
+ {
+ dh = loadDHParam(dhFile.c_str());
+ }
+
+ if (dh == 0)
+ {
+ WARNING("Could not load Diffie-Hellman params, generating a temporary 512bit key.");
+
+ dh = getTempDH512();
+ }
+
+ if (dh != 0)
+ {
+ SSL_CTX_set_tmp_dh(sslContext, dh);
+
+ DH_free(dh);
+ }
+
+ METHOD_RET("OpenSSL::System::setDHParams()");
+}
+
+void
+IceSecurity::Ssl::OpenSSL::System::setCipherList(SSL_CTX* sslContext, const string& cipherList)
+{
+ METHOD_INV("OpenSSL::System::setCipherList()");
+
+ if (!cipherList.empty() && (!SSL_CTX_set_cipher_list(sslContext, cipherList.c_str())))
+ {
+ string errorString = "Error setting cipher list.";
+
+ EXCEPTION(errorString);
+
+ throw ContextException(errorString.c_str(), __FILE__, __LINE__);
+ }
+
+ METHOD_RET("OpenSSL::System::setCipherList()");
+}
+
+int
+IceSecurity::Ssl::OpenSSL::System::seedRand()
+{
+ METHOD_INV("OpenSSL::System::seedRand()");
+
+ int retCode = 1;
+ char buffer[1024];
+
+#ifdef WINDOWS
+ RAND_screen();
+#endif
+
+ const char* file = RAND_file_name(buffer, sizeof(buffer));
+
+ if (file == 0 || !RAND_load_file(file, -1))
+ {
+ retCode = 0;
+ }
+ else
+ {
+ _randSeeded = 1;
+ }
+
+ METHOD_RET("OpenSSL::System::seedRand()");
+
+ return retCode;
+}
+
+long
+IceSecurity::Ssl::OpenSSL::System::loadRandFiles(const string& names)
+{
+ METHOD_INV("OpenSSL::System::loadRandFiles(" + names + ")");
+
+ long tot = 0;
+
+ if (!names.empty())
+ {
+ int egd;
+
+ // Make a modifiable copy of the string.
+ char* namesString = new char[names.length() + 1];
+ strcpy(namesString, names.c_str());
+
+ char seps[5];
+
+ sprintf(seps, "%c", LIST_SEPARATOR_CHAR);
+
+ char* token = strtok(namesString, seps);
+
+ while (token != 0)
+ {
+ egd = RAND_egd(token);
+
+ if (egd > 0)
+ {
+ tot += egd;
+ }
+ else
+ {
+ tot += RAND_load_file(token, -1);
+ }
+
+ token = strtok(0, seps);
+ }
+
+ if (tot > 512)
+ {
+ _randSeeded = 1;
+ }
+
+ delete []namesString;
+ }
+
+ METHOD_RET("OpenSSL::System::loadRandFiles(" + names + ")");
+
+ return tot;
+}
+
+void
+IceSecurity::Ssl::OpenSSL::System::initRandSystem(const string& randBytesFiles)
+{
+ METHOD_INV("OpenSSL::System::initRandSystem(" + randBytesFiles + ")");
+
+ if (!_randSeeded)
+ {
+ long randBytesLoaded = 0;
+
+ if (!seedRand() && randBytesFiles.empty() && !RAND_status())
+ {
+ WARNING("There is a lack of random data, consider specifying a random data file.");
+ }
+
+ if (!randBytesFiles.empty())
+ {
+ randBytesLoaded = loadRandFiles(randBytesFiles);
+ }
+ }
+
+ METHOD_RET("OpenSSL::System::initRandSystem(" + randBytesFiles + ")");
+}
+
+void
+IceSecurity::Ssl::OpenSSL::System::loadTempCerts(TempCertificates& tempCerts)
+{
+ METHOD_INV("OpenSSL::System::loadTempCerts()");
+
+ RSAVector::iterator iRSA = tempCerts.getRSACerts().begin();
+ RSAVector::iterator eRSA = tempCerts.getRSACerts().end();
+
+ while (iRSA != eRSA)
+ {
+ _tempRSAFileMap[(*iRSA).getKeySize()] = *iRSA;
+ iRSA++;
+ }
+
+ DSAVector::iterator iDSA = tempCerts.getDSACerts().begin();
+ DSAVector::iterator eDSA = tempCerts.getDSACerts().end();
+
+ while (iDSA != eDSA)
+ {
+ _tempDSAFileMap[(*iDSA).getKeySize()] = *iDSA;
+ iDSA++;
+ }
+
+ DHVector::iterator iDHP = tempCerts.getDHParams().begin();
+ DHVector::iterator eDHP = tempCerts.getDHParams().end();
+
+ while (iDHP != eDHP)
+ {
+ _tempDHParamsFileMap[(*iDHP).getKeySize()] = *iDHP;
+ iDHP++;
+ }
+
+ METHOD_RET("OpenSSL::System::loadTempCerts()");
+}