(file) Return to SSLContext.cpp CVS log (file) (dir) Up to [Pegasus] / pegasus / src / Pegasus / Common

File: [Pegasus] / pegasus / src / Pegasus / Common / SSLContext.cpp (download)
Revision: 1.14, Thu Mar 13 18:41:47 2003 UTC (21 years, 3 months ago) by kumpf
Branch: MAIN
CVS Tags: mday-merge-start, mday-merge-pegasus/src/Pegasus/Server, mday-merge-pegasus/src/Pegasus/Common
Changes since 1.13: +218 -97 lines
HP-Nag: Modified SSLContext to use /dev/random and /dev/urandom (PEP:35). Added private constructor in SSLContext that take certPath and certKeyPath as parameters and modified CIMServer.cpp to pass certKeyPath instead of certPath (Fix for Bug:209).

//%/////////////////////////////////////////////////////////////////////////////
//
// Copyright (c) 2000, 2001, 2002 BMC Software, Hewlett-Packard Company, IBM,
// The Open Group, Tivoli Systems
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// 
// THE ABOVE COPYRIGHT NOTICE AND THIS PERMISSION NOTICE SHALL BE INCLUDED IN
// ALL COPIES OR SUBSTANTIAL PORTIONS OF THE SOFTWARE. THE SOFTWARE IS PROVIDED
// "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
// LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
//==============================================================================
//
// Author: Markus Mueller (sedgewick_de@yahoo.de)
//
// Modified By: Nag Boranna, Hewlett-Packard Company (nagaraja_boranna@hp.com)
//              Roger Kumpf, Hewlett-Packard Company (roger_kumpf@hp.com)
//
//%/////////////////////////////////////////////////////////////////////////////

#ifdef PEGASUS_HAS_SSL
#include <openssl/err.h>
#include <openssl/ssl.h>
#include <openssl/rand.h>
#else
#define SSL_CTX void
#endif // end of PEGASUS_HAS_SSL
#include <Pegasus/Common/Destroyer.h>
#include <Pegasus/Common/Socket.h>
#include <Pegasus/Common/Tracer.h>
#include <Pegasus/Common/FileSystem.h>

#include "SSLContext.h"
#include "SSLContextRep.h"

PEGASUS_USING_STD;

PEGASUS_NAMESPACE_BEGIN

//
// use the following definitions only if SSL is available
// 
#ifdef PEGASUS_HAS_SSL

//
// certificate handling routine
//

// ATTN-RK-20020905: This global variable is unsafe with multiple SSL contexts
SSLCertificateVerifyFunction* verify_certificate;

static int prepareForCallback(int preverifyOk, X509_STORE_CTX *ctx)
{
    PEG_METHOD_ENTER(TRC_SSL, "prepareForCallback()");

    char   buf[256];
    X509   *err_cert;
    int    err; 
    int    depth;
    String subjectName;
    String issuerName;
    int    verify_depth = 0;
    int    verify_error = X509_V_OK;

    err_cert = X509_STORE_CTX_get_current_cert(ctx);
    err = X509_STORE_CTX_get_error(ctx);

    depth = X509_STORE_CTX_get_error_depth(ctx);

    //FUTURE: Not sure what to do with these...?
    //ssl = X509_STORE_CTX_get_ex_data(ctx, SSL_get_ex_data_X509_STORE_CTX_idx());
    //mydata = SSL_get_ex_data(ssl, mydata_index);

    X509_NAME_oneline(X509_get_subject_name(err_cert), buf, 256);
    subjectName = String(buf);

    if (verify_depth >= depth)
    {
        preverifyOk = 1;
        verify_error = X509_V_OK;
    }
    else
    {
        preverifyOk = 0;
        verify_error = X509_V_ERR_CERT_CHAIN_TOO_LONG;
        X509_STORE_CTX_set_error(ctx, verify_error);
    }

    if (!preverifyOk)
    {
        PEG_TRACE_STRING(TRC_SSL, Tracer::LEVEL4, 
            "---> SSL: verify error: " + String(X509_verify_cert_error_string(err)));
    }

    if (!preverifyOk && (err == X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT))
    {
        X509_NAME_oneline(X509_get_issuer_name(ctx->current_cert), buf, 256);
        issuerName = String(buf);
    }
    else
    {
        X509_NAME_oneline(X509_get_issuer_name(err_cert), buf, 256);
        issuerName = String(buf);
    }

    //
    // Call the verify_certificate() callback
    //
    SSLCertificateInfo certInfo(subjectName, issuerName, depth, err, preverifyOk);

    if (verify_certificate(certInfo))
    {
        preverifyOk = 1;
    }

    PEG_METHOD_EXIT();
    return(preverifyOk);
}


//
// SSL context area
//
// For the OSs that don't have /dev/random device file,
// must enable PEGASUS_SSL_RANDOMFILE flag.
//
SSLContextRep::SSLContextRep(const String& certPath,
                       const String& certKeyPath,
                       SSLCertificateVerifyFunction* verifyCert,
                       const String& randomFile)
{
    PEG_METHOD_ENTER(TRC_SSL, "SSLContextRep::SSLContextRep()");

    _certPath = certPath.getCString();

    _certKeyPath = certKeyPath.getCString();

    verify_certificate = verifyCert;

    //
    // load SSL library
    //
    SSL_load_error_strings();
    SSL_library_init();

    _randomInit(randomFile);

    _sslContext = _makeSSLContext();

    PEG_METHOD_EXIT();
}

SSLContextRep::SSLContextRep(const SSLContextRep& sslContextRep)
{
    PEG_METHOD_ENTER(TRC_SSL, "SSLContextRep::SSLContextRep()");

    _certPath = sslContextRep._certPath;
    _certKeyPath = sslContextRep._certKeyPath;
    // ATTN: verify_certificate is set implicitly in global variable
    _randomFile = sslContextRep._randomFile;
    _sslContext = _makeSSLContext();

    PEG_METHOD_EXIT();
}

//
// Destructor
//

SSLContextRep::~SSLContextRep()
{
    PEG_METHOD_ENTER(TRC_SSL, "SSLContextRep::~SSLContextRep()");

    SSL_CTX_free(_sslContext);

    PEG_METHOD_EXIT();
}

//
// initialize OpenSSL's PRNG
//
void SSLContextRep::_randomInit(const String& randomFile)
{
    PEG_METHOD_ENTER(TRC_SSL, "SSLContextRep::_randomInit()");

    Boolean ret;
    int retVal = 0;
    int seeded = 0;

    const int DEV_RANDOM_BYTES = 64;            /* how many bytes to read */
    const String devRandom = "/dev/random";     /* random device name */
    const String devUrandom = "/dev/urandom";   /* pseudo-random device name */

#ifdef PEGASUS_SSL_DEVRANDOM

    if ( FileSystem::exists(devRandom) )
    {
        while ( RAND_status() == 0 )
        {
            //
            // Always attempt to seed from good entropy sources, first 
            // try /dev/random
            //
            retVal = RAND_load_file(devRandom.getCString(), DEV_RANDOM_BYTES);
            if (retVal < 0)
            {
                break;
            }
        }
    }

    if ( FileSystem::exists(devUrandom) )
    {
        while ( RAND_status() == 0 )
        {
            //
            // If there isn't /dev/random try /dev/urandom
            //
            retVal = RAND_load_file(devUrandom.getCString(), DEV_RANDOM_BYTES);
            if (retVal < 0)
            {
                break;
            }
        }
    }
#endif  /* PEGASUS_SSL_DEVRANDOM */


#ifdef PEGASUS_SSL_RANDOMFILE
    if ( RAND_status() == 0 )
    {
        //
        // Initialise OpenSSL random number generator.
        //
        if ( randomFile == String::EMPTY )
        {
            PEG_TRACE_STRING(TRC_SSL, Tracer::LEVEL4,
                "Random seed file is required.");
            PEG_METHOD_EXIT();
            throw( SSLException("Random seed file required"));
        }

        //
        // Try the given random seed file
        //
        ret = FileSystem::exists(randomFile);
        if( ret )
        {
            retVal = RAND_load_file(randomFile.getCString(), -1);
            if ( retVal < 0 )
            {
                PEG_TRACE_STRING(TRC_SSL, Tracer::LEVEL4,
                    "Not enough seed data in seed file: " + randomFile);
                PEG_METHOD_EXIT();
                throw( SSLException("Not enough seed data in random seed file."));
            }
        }

        if ( RAND_status() == 0 )
        {
            //
            // Try to do more seeding
            //
            long seedNumber;
            srandom((unsigned int)time(NULL)); // Initialize
            seedNumber = random();
            RAND_seed((unsigned char *) &seedNumber, sizeof(seedNumber));

            int  seedRet = RAND_status();
            if ( seedRet == 0 )
            {
                PEG_TRACE_STRING(TRC_SSL, Tracer::LEVEL4,
                    "Not enough seed data in random seed file, RAND_status = " + 
                    seedRet );
                PEG_METHOD_EXIT();
                throw( SSLException("Not enough seed data in random seed file."));
            }
        }
    }
#endif  /* PEGASUS_SSL_RANDOMFILE */

    int  seedRet = RAND_status();
    if ( seedRet == 0 )
    {
        PEG_TRACE_STRING(TRC_SSL, Tracer::LEVEL4,
            "Not enough seed data , RAND_status = " + seedRet );
        PEG_METHOD_EXIT();
        throw( SSLException("Not enough seed data."));
    }

    PEG_METHOD_EXIT();
}

SSL_CTX * SSLContextRep::_makeSSLContext()
{
    PEG_METHOD_ENTER(TRC_SSL, "SSLContextRep::_makeSSLContext()");

    SSL_CTX * sslContext = 0;

    //
    // create SSL Context Area
    //

    if (!( sslContext = SSL_CTX_new(SSLv23_method()) ))
    {
        PEG_METHOD_EXIT();
        throw( SSLException("Could not get SSL CTX"));
    }

#ifdef PEGASUS_SSL_WEAKENCRYPTION
    if (!(SSL_CTX_set_cipher_list(sslContext, SSL_TXT_EXP40)))
        throw( SSLException("Could not set the cipher list"));
#endif

    //
    // set overall SSL Context flags
    //

    SSL_CTX_set_quiet_shutdown(sslContext, 1);
    SSL_CTX_set_mode(sslContext, SSL_MODE_AUTO_RETRY);
    SSL_CTX_set_options(sslContext,SSL_OP_ALL);
    SSL_CTX_set_session_cache_mode(sslContext, SSL_SESS_CACHE_OFF);

    if (verify_certificate != NULL)
    {
        SSL_CTX_set_verify(sslContext, 
            SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE, prepareForCallback);
    }
    else
    {
        SSL_CTX_set_verify(sslContext, SSL_VERIFY_NONE, NULL);
    }

    //
    // Check if there is CA certificate file specified. If specified,
    // load the certificates from the file in to the CA trust store.
    //
    if (strncmp(_certPath, "", 1) != 0)
    {
        //
        // load certificates in to trust store
        //

        if ((!SSL_CTX_load_verify_locations(sslContext, _certPath, NULL)) ||
            (!SSL_CTX_set_default_verify_paths(sslContext)))
        {
            PEG_METHOD_EXIT();
            throw( SSLException("Could not load certificates in to trust store."));
        }
    }

    //
    // Check if there is a certificate key file (file containing server
    // certificate and private key) specified. If specified, validate the
    // certificate and the private key, and load them.
    //
    if (strncmp(_certKeyPath, "", 1) != 0)
    {
        //
        // load the specified server certificates
        //

        if (SSL_CTX_use_certificate_file(sslContext,
            _certKeyPath, SSL_FILETYPE_PEM) <=0)
        {
            PEG_TRACE_STRING(TRC_SSL, Tracer::LEVEL4,
                "---> SSL: no certificate found in " + String(_certKeyPath));
            PEG_METHOD_EXIT();
            throw( SSLException("Could not get server certificate."));
        }

        //
        // load given private key and check for validity
        //

        if (!_verifyPrivateKey(sslContext, _certKeyPath))
        {
            PEG_METHOD_EXIT();
            throw( SSLException("Could not get private key."));
        }
    }

    PEG_METHOD_EXIT();
    return sslContext;
}

Boolean SSLContextRep::_verifyPrivateKey(SSL_CTX *ctx, const char *keyFilePath)
{
    PEG_METHOD_ENTER(TRC_SSL, "_verifyPrivateKey()");

    if (SSL_CTX_use_PrivateKey_file(ctx, keyFilePath, SSL_FILETYPE_PEM) <= 0)
    {
        PEG_TRACE_STRING(TRC_SSL, Tracer::LEVEL4,
            "---> SSL: no private key found in " + String(keyFilePath));
        PEG_METHOD_EXIT();
        return false;
    }

    if (!SSL_CTX_check_private_key(ctx))
    {
        PEG_TRACE_STRING(TRC_SSL, Tracer::LEVEL4,
            "---> SSL: Private and public key do not match");
        PEG_METHOD_EXIT();
        return false;
    }

    PEG_METHOD_EXIT();
    return true;
}

SSL_CTX * SSLContextRep::getContext() const 
{
    return _sslContext;
}
#else 

//
// these definitions are used if ssl is not available
//

SSLContextRep::SSLContextRep(const String& certPath,
                       const String& certKeyPath,
                       SSLCertificateVerifyFunction* verifyCert,
                       const String& randomFile) {}

SSLContextRep::SSLContextRep(const SSLContextRep& sslContextRep) {}

SSLContextRep::~SSLContextRep() {}

SSL_CTX * SSLContextRep::_makeSSLContext() { return 0; }

Boolean SSLContextRep::_verifyPrivateKey(SSL_CTX *ctx,
                                         const char *keyFilePath) { return false; }

SSL_CTX * SSLContextRep::getContext() const { return 0; }

#endif // end of PEGASUS_HAS_SSL

///////////////////////////////////////////////////////////////////////////////
//
// SSLContext
//
///////////////////////////////////////////////////////////////////////////////


SSLContext::SSLContext(
    const String& certPath,
    SSLCertificateVerifyFunction* verifyCert,
    const String& randomFile)
{
    _rep = new SSLContextRep(certPath, String::EMPTY, verifyCert, randomFile);
}

#ifndef PEGASUS_REMOVE_DEPRECATED
SSLContext::SSLContext(
    const String& certPath,
    SSLCertificateVerifyFunction* verifyCert,
    const String& randomFile,
    Boolean isCIMClient)
{
    _rep = new SSLContextRep(certPath, String::EMPTY, verifyCert, randomFile);
}
#endif

SSLContext::SSLContext(
    const String& certPath,
    const String& certKeyPath,
    SSLCertificateVerifyFunction* verifyCert,
    const String& randomFile)
{
    _rep = new SSLContextRep(certPath, certKeyPath, verifyCert, randomFile);
}

SSLContext::SSLContext(const SSLContext& sslContext)
{
    _rep = new SSLContextRep(*sslContext._rep);
}

// Dummy constructor made private to disallow default construction
SSLContext::SSLContext()
{
}

SSLContext::~SSLContext() 
{
    delete _rep;
}


///////////////////////////////////////////////////////////////////////////////
//
// SSLCertificateInfo
//
///////////////////////////////////////////////////////////////////////////////

class SSLCertificateInfoRep
{
public:
    String subjectName;
    String issuerName;
    int    errorDepth;
    int    errorCode;
    int    respCode;
};


SSLCertificateInfo::SSLCertificateInfo(
    const String subjectName,
    const String issuerName,
    const int errorDepth,
    const int errorCode,
    const int respCode)
{
    _rep = new SSLCertificateInfoRep();
    _rep->subjectName = subjectName;
    _rep->issuerName = issuerName;
    _rep->errorDepth = errorDepth;
    _rep->errorCode = errorCode;
    _rep->respCode = respCode;
}

SSLCertificateInfo::SSLCertificateInfo(
    const SSLCertificateInfo& certificateInfo)
{
    _rep = new SSLCertificateInfoRep();
    _rep->subjectName = certificateInfo._rep->subjectName;
    _rep->issuerName = certificateInfo._rep->issuerName;
    _rep->errorDepth = certificateInfo._rep->errorDepth;
    _rep->errorCode = certificateInfo._rep->errorCode;
    _rep->respCode = certificateInfo._rep->respCode;
}

// Dummy constructor made private to disallow default construction
SSLCertificateInfo::SSLCertificateInfo()
{
}

SSLCertificateInfo::~SSLCertificateInfo()
{
    delete _rep;
}

String SSLCertificateInfo::getSubjectName() const
{
    return (_rep->subjectName);
}

String SSLCertificateInfo::getIssuerName() const
{
    return (_rep->issuerName);
}

int SSLCertificateInfo::getErrorDepth() const
{
    return (_rep->errorDepth);
}

int SSLCertificateInfo::getErrorCode() const
{
    return (_rep->errorCode);
}

int SSLCertificateInfo::getResponseCode() const
{
    return (_rep->respCode);
}

void SSLCertificateInfo::setResponseCode(const int respCode)
{
    _rep->respCode = respCode;
}

PEGASUS_NAMESPACE_END


No CVS admin address has been configured
Powered by
ViewCVS 0.9.2