version 1.57, 2005/08/14 00:28:19
|
version 1.72, 2006/08/22 17:49:09
|
|
|
//%2005//////////////////////////////////////////////////////////////////////// |
//%2006//////////////////////////////////////////////////////////////////////// |
// | // |
// Copyright (c) 2000, 2001, 2002 BMC Software; Hewlett-Packard Development | // Copyright (c) 2000, 2001, 2002 BMC Software; Hewlett-Packard Development |
// Company, L.P.; IBM Corp.; The Open Group; Tivoli Systems. | // Company, L.P.; IBM Corp.; The Open Group; Tivoli Systems. |
|
|
// IBM Corp.; EMC Corporation; VERITAS Software Corporation; The Open Group. | // IBM Corp.; EMC Corporation; VERITAS Software Corporation; The Open Group. |
// Copyright (c) 2005 Hewlett-Packard Development Company, L.P.; IBM Corp.; | // Copyright (c) 2005 Hewlett-Packard Development Company, L.P.; IBM Corp.; |
// EMC Corporation; VERITAS Software Corporation; The Open Group. | // EMC Corporation; VERITAS Software Corporation; The Open Group. |
|
// Copyright (c) 2006 Hewlett-Packard Development Company, L.P.; IBM Corp.; |
|
// EMC Corporation; Symantec Corporation; The Open Group. |
// | // |
// Permission is hereby granted, free of charge, to any person obtaining a copy | // Permission is hereby granted, free of charge, to any person obtaining a copy |
// of this software and associated documentation files (the "Software"), to | // of this software and associated documentation files (the "Software"), to |
|
|
// Sushma Fernandes, Hewlett-Packard Company (sushma_fernandes@hp.com) | // Sushma Fernandes, Hewlett-Packard Company (sushma_fernandes@hp.com) |
// Heather Sterling, IBM (hsterl@us.ibm.com) | // Heather Sterling, IBM (hsterl@us.ibm.com) |
// Amit K Arora, IBM (amita@in.ibm.com) for Bug#1090 | // Amit K Arora, IBM (amita@in.ibm.com) for Bug#1090 |
|
// David Dillard, Symantec Corp. (david_dillard@symantec.com) |
|
// Aruran, IBM (ashanmug@in.ibm.com) for Bug#4422 |
// | // |
//%///////////////////////////////////////////////////////////////////////////// | //%///////////////////////////////////////////////////////////////////////////// |
| |
#ifdef PEGASUS_HAS_SSL | #ifdef PEGASUS_HAS_SSL |
| |
#ifdef PEGASUS_PLATFORM_WIN32_IX86_MSVC |
#include <Pegasus/Common/Config.h> |
//Bugzilla 2366 |
|
//Use the X509_NAME in wincrypt.h on Windows. See X509.h for more info. |
#include "Network.h" |
#include <windows.h> |
|
#include <wincrypt.h> |
|
#endif |
|
| |
#define OPENSSL_NO_KRB5 1 | #define OPENSSL_NO_KRB5 1 |
#include <openssl/err.h> | #include <openssl/err.h> |
|
|
// | // |
#ifdef PEGASUS_HAS_SSL | #ifdef PEGASUS_HAS_SSL |
| |
// Mutex for SSL locks. |
// Mutex for SSL locks which will get initialized by AutoArrayPtr constructor. |
Mutex* SSLContextRep::_sslLocks = 0; |
AutoArrayPtr<Mutex> SSLContextRep::_sslLocks; |
| |
// Mutex for _countRep. | // Mutex for _countRep. |
Mutex SSLContextRep::_countRepMutex; | Mutex SSLContextRep::_countRepMutex; |
|
|
if (sslCRLStore == NULL) | if (sslCRLStore == NULL) |
{ | { |
PEG_TRACE_STRING(TRC_SSL, Tracer::LEVEL3, "---> SSL: CRL store is NULL"); | PEG_TRACE_STRING(TRC_SSL, Tracer::LEVEL3, "---> SSL: CRL store is NULL"); |
|
PEG_METHOD_EXIT(); |
return 0; | return 0; |
} | } |
| |
|
|
if (X509_STORE_get_by_subject(&crlStoreCtx, X509_LU_CRL, issuerName, &obj) <= 0) | if (X509_STORE_get_by_subject(&crlStoreCtx, X509_LU_CRL, issuerName, &obj) <= 0) |
{ | { |
PEG_TRACE_STRING(TRC_SSL, Tracer::LEVEL3, "---> SSL: No CRL by that issuer"); | PEG_TRACE_STRING(TRC_SSL, Tracer::LEVEL3, "---> SSL: No CRL by that issuer"); |
|
PEG_METHOD_EXIT(); |
return 0; | return 0; |
} | } |
X509_STORE_CTX_cleanup(&crlStoreCtx); | X509_STORE_CTX_cleanup(&crlStoreCtx); |
|
|
if (crl == NULL) | if (crl == NULL) |
{ | { |
PEG_TRACE_STRING(TRC_SSL, Tracer::LEVEL4, "---> SSL: CRL is null"); | PEG_TRACE_STRING(TRC_SSL, Tracer::LEVEL4, "---> SSL: CRL is null"); |
|
PEG_METHOD_EXIT(); |
return 0; | return 0; |
} else | } else |
{ | { |
|
|
{ | { |
PEG_TRACE_STRING(TRC_SSL, Tracer::LEVEL2, "---> SSL: Certificate is revoked"); | PEG_TRACE_STRING(TRC_SSL, Tracer::LEVEL2, "---> SSL: Certificate is revoked"); |
X509_STORE_CTX_set_error(ctx, X509_V_ERR_CERT_REVOKED); | X509_STORE_CTX_set_error(ctx, X509_V_ERR_CERT_REVOKED); |
|
PEG_METHOD_EXIT(); |
return 1; | return 1; |
} | } |
} | } |
|
|
ssl = (SSL*) X509_STORE_CTX_get_ex_data(ctx, SSL_get_ex_data_X509_STORE_CTX_idx()); | ssl = (SSL*) X509_STORE_CTX_get_ex_data(ctx, SSL_get_ex_data_X509_STORE_CTX_idx()); |
SSLCallbackInfo* exData = (SSLCallbackInfo*) SSL_get_ex_data(ssl, SSLCallbackInfo::SSL_CALLBACK_INDEX); | SSLCallbackInfo* exData = (SSLCallbackInfo*) SSL_get_ex_data(ssl, SSLCallbackInfo::SSL_CALLBACK_INDEX); |
| |
// |
|
// If the SSLContext does not have an additional callback |
|
// simply return the preverification error (or check the CRL) |
|
// We do not need to go through the additional steps. |
|
// |
|
if (exData->_rep->verifyCertificateCallback == NULL) |
|
{ |
|
Tracer::trace(TRC_SSL, Tracer::LEVEL4, |
|
"--->SSL: No verification callback specified"); |
|
|
|
if (exData->_rep->crlStore != NULL) |
|
{ |
|
revoked = verificationCRLCallback(preVerifyOk,ctx,exData->_rep->crlStore); |
|
Tracer::trace(TRC_SSL, Tracer::LEVEL4, "---> SSL: CRL callback returned %d", revoked); |
|
|
|
if (revoked) //with the SSL callbacks '0' indicates failure |
|
{ |
|
PEG_METHOD_EXIT(); |
|
return 0; |
|
} |
|
} |
|
} |
|
| |
|
#ifdef PEGASUS_ENABLE_SSL_CRL_VERIFICATION |
// | // |
// Check to see if a CRL path is defined | // Check to see if a CRL path is defined |
// | // |
|
|
} | } |
| |
Tracer::trace(TRC_SSL, Tracer::LEVEL4, "---> SSL: CRL callback returned %d", revoked); | Tracer::trace(TRC_SSL, Tracer::LEVEL4, "---> SSL: CRL callback returned %d", revoked); |
|
#endif |
| |
// | // |
// get the current certificate | // get the current certificate |
|
|
// | // |
// get the issuer name on the certificate | // get the issuer name on the certificate |
// | // |
if (!preVerifyOk && (errorCode == X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT)) |
|
{ |
|
X509_NAME_oneline(X509_get_issuer_name(ctx->current_cert), buf, 256); |
|
} |
|
else |
|
{ |
|
X509_NAME_oneline(X509_get_issuer_name(currentCert), buf, 256); | X509_NAME_oneline(X509_get_issuer_name(currentCert), buf, 256); |
} |
|
String issuerName = String(buf); | String issuerName = String(buf); |
| |
// | // |
// Create the certificate object | // Create the certificate object |
// | // |
if (exData->_rep->peerCertificate != NULL) |
|
|
//insert at the beginning of the array so that the peer certificate is first and the root CA is last |
|
exData->_rep->peerCertificate.insert(0, new SSLCertificateInfo(subjectName, issuerName, version, serialNumber, |
|
notBefore, notAfter, depth, errorCode, errorStr, preVerifyOk)); |
|
|
|
PEG_TRACE_STRING(TRC_SSL, Tracer::LEVEL3, "Created SSLCertificateInfo"); |
|
|
|
// NOT_YET_VALID checks do not work correctly on subsequent tries -- Bugzilla#4283 |
|
// call this prior to calling the user-specified callback in case they want to override it |
|
if (errorCode == X509_V_OK && (CIMDateTime::getDifference(CIMDateTime::getCurrentDateTime(), notBefore) > 0)) |
{ | { |
//Delete an existing certificate object from a previous call. |
PEG_TRACE_STRING(TRC_SSL, Tracer::LEVEL4, "Certificate was not yet valid."); |
//SSL validates the certificate chain starting with the root CA and working down to the peer certificate. |
X509_STORE_CTX_set_error(ctx, X509_V_ERR_CERT_NOT_YET_VALID); |
//With this strategy, we end up with the peer certificate as the last certificate stored in the SSLCallbackInfo |
|
//so we can retrieve the correct certificate info and username. |
|
delete exData->_rep->peerCertificate; |
|
exData->_rep->peerCertificate = NULL; |
|
} | } |
| |
exData->_rep->peerCertificate = new SSLCertificateInfo(subjectName, issuerName, version, serialNumber, |
|
notBefore, notAfter, depth, errorCode, errorStr, preVerifyOk); |
|
|
|
// | // |
// Call the application callback. |
// Call the user-specified application callback if it is specified. If it is null, return OpenSSL's verification code. |
// Note that the verification result does not automatically get set to X509_V_OK if the callback is successful. | // Note that the verification result does not automatically get set to X509_V_OK if the callback is successful. |
// This is because OpenSSL retains the original default error in case we want to use it later. | // This is because OpenSSL retains the original default error in case we want to use it later. |
// To set the error, we could use X509_STORE_CTX_set_error(ctx, verifyError); but there is no real benefit to doing that here. | // To set the error, we could use X509_STORE_CTX_set_error(ctx, verifyError); but there is no real benefit to doing that here. |
// | // |
if (exData->_rep->verifyCertificateCallback(*exData->_rep->peerCertificate)) |
if (exData->_rep->verifyCertificateCallback == NULL) |
|
{ |
|
return preVerifyOk; |
|
|
|
} else |
|
{ |
|
if (exData->_rep->verifyCertificateCallback(*exData->_rep->peerCertificate[0])) |
{ | { |
Tracer::trace(TRC_SSL, Tracer::LEVEL4, | Tracer::trace(TRC_SSL, Tracer::LEVEL4, |
"--> SSL: _rep->verifyCertificateCallback() returned X509_V_OK"); | "--> SSL: _rep->verifyCertificateCallback() returned X509_V_OK"); |
|
|
else // verification failed, handshake will be immediately terminated | else // verification failed, handshake will be immediately terminated |
{ | { |
Tracer::trace(TRC_SSL, Tracer::LEVEL4, | Tracer::trace(TRC_SSL, Tracer::LEVEL4, |
"--> SSL: _rep->verifyCertificateCallback() returned error %d", exData->_rep->peerCertificate->getErrorCode()); |
"--> SSL: _rep->verifyCertificateCallback() returned error %d", exData->_rep->peerCertificate[0]->getErrorCode()); |
| |
PEG_METHOD_EXIT(); | PEG_METHOD_EXIT(); |
return 0; | return 0; |
} | } |
} | } |
|
} |
| |
// | // |
// Callback function called by OpenSSL. This request is merely forwarded to the static | // Callback function called by OpenSSL. This request is merely forwarded to the static |
|
|
if ( mode & CRYPTO_LOCK ) | if ( mode & CRYPTO_LOCK ) |
{ | { |
/*Tracer::trace(TRC_SSL, Tracer::LEVEL4, | /*Tracer::trace(TRC_SSL, Tracer::LEVEL4, |
"Now locking for %d", pegasus_thread_self());*/ |
"Now locking for %d", Threads::id());*/ |
SSLContextRep::_sslLocks[type].lock( pegasus_thread_self() ); |
SSLContextRep::_sslLocks.get()[type].lock( ); |
} | } |
else | else |
{ | { |
/*Tracer::trace(TRC_SSL, Tracer::LEVEL4, | /*Tracer::trace(TRC_SSL, Tracer::LEVEL4, |
"Now unlocking for %d", pegasus_thread_self());*/ |
"Now unlocking for %d", Threads::id());*/ |
SSLContextRep::_sslLocks[type].unlock( ); |
SSLContextRep::_sslLocks.get()[type].unlock( ); |
|
} |
} | } |
|
|
|
static unsigned long _get_thread_id() |
|
{ |
|
#if defined(PEGASUS_HAVE_PTHREADS) |
|
return pthread_self(); |
|
#else |
|
return 0; |
|
#endif |
} | } |
| |
// | // |
|
|
SSL_OS400_Init(); | SSL_OS400_Init(); |
#endif | #endif |
| |
_sslLocks= new Mutex[CRYPTO_num_locks()]; |
_sslLocks.reset(new Mutex[CRYPTO_num_locks()]); |
| |
// Set the ID callback. The ID callback returns a thread ID. | // Set the ID callback. The ID callback returns a thread ID. |
| |
CRYPTO_set_id_callback((CRYPTO_SET_ID_CALLBACK) pegasus_thread_self); |
CRYPTO_set_id_callback((CRYPTO_SET_ID_CALLBACK)_get_thread_id); |
| |
// Set the locking callback to pegasus_locking_callback. | // Set the locking callback to pegasus_locking_callback. |
| |
|
|
CRYPTO_set_id_callback (NULL); | CRYPTO_set_id_callback (NULL); |
PEG_TRACE_STRING(TRC_SSL, Tracer::LEVEL4, | PEG_TRACE_STRING(TRC_SSL, Tracer::LEVEL4, |
"Freed SSL callback."); | "Freed SSL callback."); |
|
_sslLocks.reset(); |
delete []_sslLocks; |
|
} | } |
| |
| |
|
|
// load SSL library | // load SSL library |
// | // |
Tracer::trace(TRC_SSL, Tracer::LEVEL4, | Tracer::trace(TRC_SSL, Tracer::LEVEL4, |
"Before calling SSL_load_error_strings %d", pegasus_thread_self()); |
"Before calling SSL_load_error_strings %d", Threads::id()); |
| |
SSL_load_error_strings(); | SSL_load_error_strings(); |
| |
Tracer::trace(TRC_SSL, Tracer::LEVEL4, | Tracer::trace(TRC_SSL, Tracer::LEVEL4, |
"After calling SSL_load_error_strings %d", pegasus_thread_self()); |
"After calling SSL_load_error_strings %d", Threads::id()); |
| |
Tracer::trace(TRC_SSL, Tracer::LEVEL4, | Tracer::trace(TRC_SSL, Tracer::LEVEL4, |
"Before calling SSL_library_init %d", pegasus_thread_self()); |
"Before calling SSL_library_init %d", Threads::id()); |
| |
SSL_library_init(); | SSL_library_init(); |
| |
Tracer::trace(TRC_SSL, Tracer::LEVEL4, | Tracer::trace(TRC_SSL, Tracer::LEVEL4, |
"After calling SSL_library_init %d", pegasus_thread_self()); |
"After calling SSL_library_init %d", Threads::id()); |
|
|
} | } |
| |
_countRep++; | _countRep++; |
|
|
// Try to do more seeding | // Try to do more seeding |
// | // |
long seedNumber; | long seedNumber; |
#if defined(PEGASUS_PLATFORM_WIN32_IX86_MSVC) |
#if defined(PEGASUS_COMPILER_MSVC) |
srand((unsigned int)time(NULL)); // Initialize | srand((unsigned int)time(NULL)); // Initialize |
seedNumber = rand(); | seedNumber = rand(); |
#else | #else |
|
|
| |
SSL_CTX_set_quiet_shutdown(sslContext, 1); | SSL_CTX_set_quiet_shutdown(sslContext, 1); |
SSL_CTX_set_mode(sslContext, SSL_MODE_AUTO_RETRY); | SSL_CTX_set_mode(sslContext, SSL_MODE_AUTO_RETRY); |
|
SSL_CTX_set_mode(sslContext, SSL_MODE_ENABLE_PARTIAL_WRITE); |
SSL_CTX_set_session_cache_mode(sslContext, SSL_SESS_CACHE_OFF); | SSL_CTX_set_session_cache_mode(sslContext, SSL_SESS_CACHE_OFF); |
| |
int options = SSL_OP_ALL; | int options = SSL_OP_ALL; |
|
|
SSLCertificateVerifyFunction* verifyCert, | SSLCertificateVerifyFunction* verifyCert, |
const String& randomFile) | const String& randomFile) |
{ | { |
|
#ifndef PEGASUS_ENABLE_SSL_CRL_VERIFICATION |
|
if ( crlPath.size() > 0 ) |
|
{ |
|
MessageLoaderParms parms("Common.Exception.SSL_CRL_NOT_ENABLED_EXCEPTION", |
|
"SSL CRL verification is not enabled."); |
|
throw Exception(parms); |
|
} |
|
#endif |
_rep = new SSLContextRep(trustStore, certPath, keyPath, crlPath, verifyCert, randomFile); | _rep = new SSLContextRep(trustStore, certPath, keyPath, crlPath, verifyCert, randomFile); |
} | } |
| |
|
|
| |
String SSLContext::getCRLPath() const | String SSLContext::getCRLPath() const |
{ | { |
|
#ifdef PEGASUS_ENABLE_SSL_CRL_VERIFICATION |
return (_rep->getCRLPath()); | return (_rep->getCRLPath()); |
|
#else |
|
MessageLoaderParms parms("Common.Exception.SSL_CRL_NOT_ENABLED_EXCEPTION", |
|
"SSL CRL verification is not enabled."); |
|
throw Exception(parms); |
|
#endif |
} | } |
| |
X509_STORE* SSLContext::getCRLStore() const | X509_STORE* SSLContext::getCRLStore() const |
{ | { |
|
#ifdef PEGASUS_ENABLE_SSL_CRL_VERIFICATION |
return (_rep->getCRLStore()); | return (_rep->getCRLStore()); |
|
#else |
|
MessageLoaderParms parms("Common.Exception.SSL_CRL_NOT_ENABLED_EXCEPTION", |
|
"SSL CRL verification is not enabled."); |
|
throw Exception(parms); |
|
#endif |
} | } |
| |
Boolean SSLContext::isPeerVerificationEnabled() const | Boolean SSLContext::isPeerVerificationEnabled() const |
|
|
_rep = new SSLCallbackInfoRep(); | _rep = new SSLCallbackInfoRep(); |
_rep->verifyCertificateCallback = verifyCert; | _rep->verifyCertificateCallback = verifyCert; |
_rep->crlStore = NULL; | _rep->crlStore = NULL; |
_rep->peerCertificate = NULL; |
|
} | } |
| |
SSLCallbackInfo::SSLCallbackInfo(SSLCertificateVerifyFunction* verifyCert, X509_STORE* crlStore) | SSLCallbackInfo::SSLCallbackInfo(SSLCertificateVerifyFunction* verifyCert, X509_STORE* crlStore) |
|
|
_rep = new SSLCallbackInfoRep(); | _rep = new SSLCallbackInfoRep(); |
_rep->verifyCertificateCallback = verifyCert; | _rep->verifyCertificateCallback = verifyCert; |
_rep->crlStore = crlStore; | _rep->crlStore = crlStore; |
_rep->peerCertificate = NULL; |
|
} | } |
| |
SSLCallbackInfo::~SSLCallbackInfo() | SSLCallbackInfo::~SSLCallbackInfo() |
{ | { |
if (_rep->peerCertificate) |
PEG_METHOD_ENTER(TRC_SSL, "SSLCallbackInfo::~SSLCallbackInfo"); |
|
for (Uint32 i = 0; i < _rep->peerCertificate.size(); i++) |
{ | { |
delete _rep->peerCertificate; |
delete _rep->peerCertificate[i]; |
} | } |
delete _rep; | delete _rep; |
|
PEG_METHOD_EXIT(); |
} | } |
| |
PEGASUS_NAMESPACE_END | PEGASUS_NAMESPACE_END |