diff options
Diffstat (limited to 'cpp/src/Ice/SslSystemOpenSSL.cpp')
-rw-r--r-- | cpp/src/Ice/SslSystemOpenSSL.cpp | 3092 |
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()"); +} |