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

File: [Pegasus] / pegasus / src / Pegasus / Server / Attic / SSLContextManager.cpp (download)
Revision: 1.3, Fri Feb 18 04:25:29 2005 UTC (19 years, 4 months ago) by nag.boranna
Branch: MAIN
CVS Tags: TASK-PEP362_RestfulService-merged_out_from_trunk, TASK-PEP348_SCMO-merged_out_from_trunk, TASK-PEP317_pullop-merged_out_from_trunk, TASK-PEP317_pullop-merged_in_to_trunk, TASK-PEP311_WSMan-root, TASK-PEP311_WSMan-branch, RELEASE_2_5_0-RC1, HPUX_TEST, HEAD
Changes since 1.2: +0 -0 lines
FILE REMOVED
PEP#: 187
TITLE: SSL Certificate Management Enhancements

DESCRIPTION: Moved the common OpenSSL related code from Server to Common directory. Made changes to fix the circular dependency issue on Windows build.

//%2005////////////////////////////////////////////////////////////////////////
//
// Copyright (c) 2000, 2001, 2002 BMC Software; Hewlett-Packard Development
// Company, L.P.; IBM Corp.; The Open Group; Tivoli Systems.
// Copyright (c) 2003 BMC Software; Hewlett-Packard Development Company, L.P.;
// IBM Corp.; EMC Corporation, The Open Group.
// Copyright (c) 2004 BMC Software; Hewlett-Packard Development Company, L.P.;
// IBM Corp.; EMC Corporation; VERITAS Software Corporation; The Open Group.
// Copyright (c) 2005 Hewlett-Packard Development Company, L.P.; IBM Corp.;
// EMC Corporation; VERITAS Software Corporation; The Open Group.
//
// 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: Nag Boranna, Hewlett-Packard Company (nagaraja.boranna@hp.com)
//
// Modified By:
//
//%/////////////////////////////////////////////////////////////////////////////

/////////////////////////////////////////////////////////////////////////////
//  SSLContextManager
/////////////////////////////////////////////////////////////////////////////

#ifdef PEGASUS_HAS_SSL

#ifdef PEGASUS_PLATFORM_WIN32_IX86_MSVC
//Use the X509_NAME in wincrypt.h on Windows.  See X509.h for more info.
#include <windows.h>
#include <wincrypt.h>
#endif

#define OPENSSL_NO_KRB5 1
#include <openssl/err.h>
#include <openssl/ssl.h>
#include <openssl/rand.h>
#else
#define SSL_CTX void
#define X509_STORE void

#endif // end of PEGASUS_HAS_SSL

#include <Pegasus/Common/Tracer.h>
#include <Pegasus/Common/SSLContextRep.h>
#include <Pegasus/Common/FileSystem.h>
#include <Pegasus/Config/ConfigManager.h>

#include "SSLContextManager.h"

PEGASUS_NAMESPACE_BEGIN


//
// This method along with the relevent code is moved here
// from CIMServer.cpp
//
Boolean verifyClientOptionalCallback(SSLCertificateInfo &certInfo)
{
    // SSL callback for the "optional" client verification setting
    // By always returning true, we allow the handshake to continue
    // even if the client sent no certificate or sent an untrusted certificate.
    return true;
}

SSLContextManager::SSLContextManager()
    : _trustStore(String::EMPTY),
      _crlStore(String::EMPTY),
      _exportTrustStore(String::EMPTY),
      _sslContext(0),
      _exportSSLContext(0)
{

}

SSLContextManager::~SSLContextManager()
{
    if (_sslContext)
    {
        delete _sslContext;
    }

    if (_exportSSLContext)
    {
        delete _exportSSLContext;
    }
}

//
// Original code for this method comes from the former _getSSLContext()
// and _getExportSSLContext() methods in CIMServer.cpp
//
SSLContext* SSLContextManager::getSSLContext(Uint32 contextType)
{
    PEG_METHOD_ENTER(TRC_SSL, "SSLContextManager::getSSLContext()");

    if ( (contextType == SERVER_CONTEXT && !_sslContext ) ||
         (contextType == EXPORT_CONTEXT && !_exportSSLContext ) )
    {
        static const String PROPERTY_NAME__SSL_CERT_FILEPATH = 
                                               "sslCertificateFilePath";
        static const String PROPERTY_NAME__SSL_KEY_FILEPATH  = "sslKeyFilePath";
        static const String PROPERTY_NAME__SSL_TRUST_STORE  = "sslTrustStore";
        static const String PROPERTY_NAME__EXPORT_SSL_TRUST_STORE = 
                                               "exportSSLTrustStore";
        static const String PROPERTY_NAME__SSL_CRL_STORE  = "crlStore";
        static const String PROPERTY_NAME__SSL_CLIENT_VERIFICATION = 
                                               "sslClientVerificationMode";
        static const String PROPERTY_NAME__SSL_AUTO_TRUST_STORE_UPDATE = 
                                               "enableSSLTrustStoreAutoUpdate";
        static const String PROPERTY_NAME__SSL_TRUST_STORE_USERNAME = 
                                               "sslTrustStoreUserName";
        static const String PROPERTY_NAME__HTTP_ENABLED = 
                                               "enableHttpConnection";

        //
        // Get a config manager instance
        //
        ConfigManager* configManager = ConfigManager::getInstance();

        String verifyClient = String::EMPTY;

        if ( contextType == SERVER_CONTEXT )
        {
            // Note that if invalid values were set for either sslKeyFilePath, 
            // sslCertificateFilePath, crlStore or sslTrustStore, the invalid 
            // paths would have been detected in SecurityPropertyOwner and 
            // terminated the server startup. This happens regardless of whether
            // or not HTTPS is enabled (not a great design, but that seems to 
            // be how other properties are validated as well)
            //
            // Get the sslClientVerificationMode property from the Config 
            // Manager.
            //
            verifyClient = configManager->getCurrentValue(
                                      PROPERTY_NAME__SSL_CLIENT_VERIFICATION);

            //
            // Get the sslTrustStore property from the Config Manager.
            //
            _trustStore = configManager->getCurrentValue(
                                     PROPERTY_NAME__SSL_TRUST_STORE);

            if (_trustStore != String::EMPTY)
            {
                _trustStore = ConfigManager::getHomedPath(_trustStore);
            }

            PEG_TRACE_STRING(TRC_SSL, Tracer::LEVEL4,
                "Server trust store name: " + _trustStore);
            //
            // Get the sslTrustStoreUserName property from the Config Manager.
            //
            String trustStoreUserName = String::EMPTY;
            trustStoreUserName = configManager->getCurrentValue(
                                      PROPERTY_NAME__SSL_TRUST_STORE_USERNAME);

            if (!String::equal(verifyClient, "disabled"))
            {
                //
                // 'required' setting must have a valid truststore
                // 'optional' setting can be used with or without a truststore; 
                // log a warning if a truststore is not specified
                //
                if (_trustStore == String::EMPTY)
                {
                    if (String::equal(verifyClient, "required"))
                    {
                        MessageLoaderParms parms(
                            "Pegasus.Server.SSLContextManager.SSL_CLIENT_VERIFICATION_EMPTY_TRUSTSTORE",
                            "The \"sslTrustStore\" configuration property must be set if \"sslClientVerificationMode\" is 'required'. cimserver not started.");
                        PEG_METHOD_EXIT();
                        throw SSLException(parms);
                    }
                    else if (String::equal(verifyClient, "optional"))
                    {
                        Logger::put(Logger::STANDARD_LOG, System::CIMSERVER, 
                            Logger::WARNING, 
                            "SSL client verification is enabled but no truststore was specified.");
                    }
                }

                //
                // ATTN: 'required' setting must have http port enabled.
                // If only https is enabled, and a call to shutdown the 
                // cimserver is given, the call will hang and a forced shutdown
                // will ensue. This is because the CIMClient::connectLocal call
                // cannot (and should not) specify a truststore to validate the
                // local server against.  This limitation is being investigated.
                //
                if (String::equal(verifyClient, "required"))
                {
                    String httpEnabled = configManager->getCurrentValue(
                                                  PROPERTY_NAME__HTTP_ENABLED);

                    if (!String::equal(httpEnabled, "true"))
                    {
                        MessageLoaderParms parms(
                            "Pegasus.Server.SSLContextManager.SSL_CLIENT_VERIFICATION_HTTP_NOT_ENABLED_WITH_REQUIRED",
                            "The HTTP port must be enabled if \"sslClientVerificationMode\" is 'required' in order for the cimserver to properly shutdown. cimserver not started.");
                        PEG_METHOD_EXIT();
                        throw SSLException(parms);
                    }
                }

                //
                // A truststore username must be specified if 
                // sslClientVerificationMode is enabled and the truststore is a
                // single CA file.  If the truststore is a directory, then the 
                // CertificateProvider should be used to register users with 
                // certificates.
                //
                if ((_trustStore != String::EMPTY) && 
                    (!FileSystem::isDirectory(_trustStore)))
                {
                    if (trustStoreUserName == String::EMPTY)
                    {
                        MessageLoaderParms parms(
                            "Pegasus.Server.SSLContextManager.SSL_CLIENT_VERIFICATION_EMPTY_USERNAME",
                            "The \"sslTrustStoreUserName\" property must specify a valid username if \"sslClientVerificationMode\" is 'required' or 'optional' and the truststore is a single CA file. To register individual certificates to users, you must use a truststore directory along with the CertificateProvider.  cimserver not started.");
                        PEG_METHOD_EXIT();
                        throw SSLException(parms);
                    }
                }
            }
        }
        else if ( contextType == EXPORT_CONTEXT )
        {
            //
            // Get the exportSSLTrustStore property from the Config Manager.
            //
            _exportTrustStore = configManager->getCurrentValue(
                                      PROPERTY_NAME__EXPORT_SSL_TRUST_STORE);

            if (_exportTrustStore == String::EMPTY)
            {
                MessageLoaderParms parms(
                    "Pegasus.Server.SSLContextManager.EXPORT_TRUST_EMPTY",
                    "The \"exportSSLTrustStore\" configuration property must be set when \"enableSSLExportClientVerification\" is true. cimserver not started.");

                PEG_METHOD_EXIT();
                throw Exception(parms);
            }

            _exportTrustStore = ConfigManager::getHomedPath(_exportTrustStore);

            PEG_TRACE_STRING(TRC_SSL, Tracer::LEVEL4,
                "Export trust store name: " + _exportTrustStore);
        }

        //
        // Get the crlStore property from the Config Manager.
        //
        _crlStore = configManager->getCurrentValue(
                                   PROPERTY_NAME__SSL_CRL_STORE);

        if (_crlStore != String::EMPTY)
        {
            _crlStore = ConfigManager::getHomedPath(_crlStore);
        }

        //
        // Get the sslCertificateFilePath property from the Config Manager.
        //
        String certPath;
        certPath = ConfigManager::getHomedPath(
            configManager->getCurrentValue(PROPERTY_NAME__SSL_CERT_FILEPATH));

        //
        // Get the sslKeyFilePath property from the Config Manager.
        //
        String keyPath;
        keyPath = ConfigManager::getHomedPath(
            configManager->getCurrentValue(PROPERTY_NAME__SSL_KEY_FILEPATH));

        String randFile = String::EMPTY;

#ifdef PEGASUS_SSL_RANDOMFILE
        // NOTE: It is technically not necessary to set up a random file on
        // the server side, but it is easier to use a consistent interface
        // on the client and server than to optimize out the random file on
        // the server side.
        randFile = ConfigManager::getHomedPath(PEGASUS_SSLSERVER_RANDOMFILE);
#endif

        if ( contextType == SERVER_CONTEXT )
        {
            PEG_TRACE_STRING(TRC_SSL, Tracer::LEVEL4,
                "Creating the Server SSL Context.");
            //
            // Create the SSLContext defined by the configuration properties
            //
            if (String::equal(verifyClient, "required"))
            {
                PEG_TRACE_STRING(TRC_SSL, Tracer::LEVEL2,
                    "SSL Client verification REQUIRED.");

                _sslContext = new SSLContext(_trustStore, certPath, 
                    keyPath, _crlStore, 0, randFile);
            }
            else if (String::equal(verifyClient, "optional"))
            {
                PEG_TRACE_STRING(TRC_SSL, Tracer::LEVEL2,
                    "SSL Client verification OPTIONAL.");

                _sslContext = new SSLContext(_trustStore, certPath, 
                    keyPath, _crlStore, 
                    (SSLCertificateVerifyFunction*)verifyClientOptionalCallback,
                    randFile);
            }
            else if (String::equal(verifyClient, "disabled") || 
                     verifyClient == String::EMPTY)
            {
                PEG_TRACE_STRING(TRC_SSL, Tracer::LEVEL2,
                    "SSL Client verification DISABLED.");

                _sslContext = new SSLContext(String::EMPTY, certPath, 
                    keyPath, _crlStore, 0, randFile);
            }
            PEG_METHOD_EXIT();
            return _sslContext;
        }
        else if ( contextType == EXPORT_CONTEXT )
        {
            PEG_TRACE_STRING(TRC_SSL, Tracer::LEVEL4,
                "Creating the Export SSL Context.");
            //
            // Note: Trust store is used by default on Export connections,
            // verification callback function is not used.
            //

            _exportSSLContext = new SSLContext(_exportTrustStore, certPath, 
                keyPath, _crlStore, 0, randFile);

            PEG_METHOD_EXIT();
            return _exportSSLContext;
        }
    }

    PEG_METHOD_EXIT();
    return 0;
}

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

void SSLContextManager::reloadTrustStore(Uint32 contextType)
{
    PEG_METHOD_ENTER(TRC_SSL, "SSLContextManager::reloadTrustStore()");

    SSL_CTX* sslContext;
    String trustStore = String::EMPTY;

    if ( contextType == SERVER_CONTEXT && _sslContext )
    {
        PEG_TRACE_STRING(TRC_SSL, Tracer::LEVEL4,
            "Context Type is Server Context.");
        sslContext = _sslContext->_rep->getContext();
        trustStore = _trustStore;
    }
    else if ( contextType == EXPORT_CONTEXT && _exportSSLContext )
    {
        PEG_TRACE_STRING(TRC_SSL, Tracer::LEVEL4,
            "Context Type is Export Context.");
        sslContext = _exportSSLContext->_rep->getContext();
        trustStore = _exportTrustStore;
    }
    else
    {
        PEG_TRACE_STRING(TRC_SSL, Tracer::LEVEL2,
          "Could not reload the trust store, SSL Context is not initialized.");

        MessageLoaderParms parms(
         "Pegasus.Server.SSLContextManager.COULD_NOT_RELOAD_TRUSTSTORE_SSL_CONTEXT_NOT_INITIALIZED",
         "Could not reload the trust store, SSL Context is not initialized.");
        PEG_METHOD_EXIT();
        throw SSLException(parms);
    }

    if (trustStore == String::EMPTY)
    {
        PEG_TRACE_STRING(TRC_SSL, Tracer::LEVEL4,
            "Could not reload the trust store, the trust store is not configured.");

        MessageLoaderParms parms(
            "Pegasus.Server.SSLContextManager.TRUST_STORE_NOT_CONFIGURED",
            "Could not reload the trust store, the trust store is not configured.");
        PEG_METHOD_EXIT();
        throw SSLException(parms);
    }

    X509_STORE* newStore = _getNewX509Store(trustStore);

    //
    // acquire write lock to Context object and then overwrite the trust 
    // store cache
    //
    {
        WriteLock contextLock(_sslContextObjectLock);
        SSL_CTX_set_cert_store(sslContext, newStore);
    }
    PEG_METHOD_EXIT();
}

void SSLContextManager::reloadCRLStore()
{
    PEG_METHOD_ENTER(TRC_SSL, "SSLContextManager::reloadCRLStore()");

    if (!_sslContext && !_exportSSLContext)
    {
        PEG_TRACE_STRING(TRC_SSL, Tracer::LEVEL2,
        "Could not reload the crl store, SSL Context is not initialized.");

        MessageLoaderParms parms(
         "Pegasus.Server.SSLContextManager.COULD_NOT_RELOAD_CRL_STORE_SSL_CONTEXT_NOT_INITIALIZED",
         "Could not reload the crl store, SSL Context is not initialized.");

        PEG_METHOD_EXIT();
        throw SSLException(parms);
    }

    if (_crlStore == String::EMPTY)
    {
        PEG_TRACE_STRING(TRC_SSL, Tracer::LEVEL4,
            "Could not reload the crl store, the crl store is not configured.");

        MessageLoaderParms parms(
            "Pegasus.Server.SSLContextManager.CRL_STORE_NOT_CONFIGURED",
            "Could not reload the crl store, the crl store is not configured.");
        PEG_METHOD_EXIT();
        throw SSLException(parms);
    }

    X509_STORE* newStore = _getNewX509Store(_crlStore);

    //
    // Acquire write lock to Context objects and then overwrite the
    // crl store cache for both the cimContext and exportContext
    //
    {
        WriteLock contextLock(_sslContextObjectLock);

        if (_sslContext)
        {
            SSL_CTX_set_cert_store(
                _sslContext->_rep->getContext(), newStore);
        }

        if (_exportSSLContext)
        {
            SSL_CTX_set_cert_store(
                _exportSSLContext->_rep->getContext(), newStore);
        }
    }

    PEG_METHOD_EXIT();
}

X509_STORE* SSLContextManager::_getNewX509Store(const String storePath)
{
    PEG_METHOD_ENTER(TRC_SSL, "SSLContextManager::_getNewX509Store()");

    //
    // reload certificates from the specified store
    //
    PEG_TRACE_STRING(TRC_SSL, Tracer::LEVEL2,
        "Reloading certificates from the store: " + storePath);

    X509_STORE* newStore = X509_STORE_new(); 

    //
    // Check if there is a CA certificate file or directory specified. 
    // If specified, load the certificates from the specified store path.
    //
    if (FileSystem::isDirectory(storePath))
    {
        X509_LOOKUP* storeLookup = X509_STORE_add_lookup(newStore, 
                                              X509_LOOKUP_hash_dir()); 
        if (storeLookup == NULL)
        {
            X509_STORE_free(newStore);

            PEG_TRACE_STRING(TRC_SSL, Tracer::LEVEL4,
                "Could not reload the trust or crl store.");

            MessageLoaderParms parms(
             "Pegasus.Server.SSLContextManager.COULD_NOT_RELOAD_TRUST_OR_CRL_STORE",
             "Could not reload the trust or crl store.");
            PEG_METHOD_EXIT();
            throw SSLException(parms);
        }
        X509_LOOKUP_add_dir(storeLookup, 
            storePath.getCString(), X509_FILETYPE_PEM); 
    }
    else if (FileSystem::exists(storePath))
    {
        X509_LOOKUP* storeLookup = X509_STORE_add_lookup(newStore, 
                                                     X509_LOOKUP_file()); 
        if (storeLookup == NULL)
        {
            X509_STORE_free(newStore);

            PEG_TRACE_STRING(TRC_SSL, Tracer::LEVEL4,
                "Could not reload the trust or crl store.");

            MessageLoaderParms parms(
             "Pegasus.Server.SSLContextManager.COULD_NOT_RELOAD_TRUST_OR_CRL_STORE",
             "Could not reload the trust or crl store.");
            PEG_METHOD_EXIT();
            throw SSLException(parms);
        }
        X509_LOOKUP_load_file(storeLookup, 
            storePath.getCString(), X509_FILETYPE_PEM); 
    }
    else
    {
        PEG_TRACE_STRING(TRC_SSL, Tracer::LEVEL4,
            "Could not reload the trust or crl store, configured store not found.");

        MessageLoaderParms parms(
         "Pegasus.Server.SSLContextManager.CONFIGURED_TRUST_OR_CRL_STORE_NOT_FOUND",
         "Could not reload the trust or crl store, configured store not found.");
        PEG_METHOD_EXIT();
        throw SSLException(parms);
    }

    PEG_METHOD_EXIT();
    return newStore;
}

#else    //#ifdef PEGASUS_HAS_SSL

void SSLContextManager::reloadTrustStore(Uint32 contextType) { }

void SSLContextManager::reloadCRLStore() { }

X509_STORE* SSLContextManager::_getNewX509Store(const String storePath) { return NULL; }

#endif   //#ifdef PEGASUS_HAS_SSL

/**
    Get a pointer to the sslContextObjectLock.
 */
ReadWriteSem* SSLContextManager::getSSLContextObjectLock()
{
    return &_sslContextObjectLock;
}

PEGASUS_NAMESPACE_END

No CVS admin address has been configured
Powered by
ViewCVS 0.9.2