Open Pegasus 2.14.1 client connection issue

1.1k views Asked by At

I would like to build new version of Open Pegasus Client (2.14.1). Unfortunately I'm facing with some build issues. Does anybody know some workaround for these issues?

My environment is:

  • OS: Windows 8.1 Enterprise
  • Make version: GNU Make 3.81
  • Pegasus sources version: 2.14.1
  • OpenSSL version: 1.0.2a

My scenario is quite easy:

  1. I have downloaded source code of Open Pegasus 2.14.1
  2. I have downloaded the OpenSSL binaries (actual version v1.0.2a).
  3. After extraction of Pegasus source code I set my environment with these settings:

    call "C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\bin\vcvars32.bat"
    set PEGASUS_ROOT=D:/Dev/pegasus-2.14.1/pegasus
    set PEGASUS_HOME=%PEGASUS_ROOT%
    set PEGASUS_PLATFORM=WIN32_IX86_MSVC
    set path=%path%;%PEGASUS_HOME%\bin
    set OPENSSL_HOME=D:/Dev/OpenSSL-Win32
    set PEGASUS_HAS_SSL=true
    
  4. Next step was building of mu.exe tool. So, I have executed "make buildmu" => successfully build and copied to "/bin" folder. I would like to build Pegasus so: "make build" => after some time I got this error:

    Message.cpp(433) : error C2065: 'magic' : undeclared identifier

  5. I tried to fix this problem. I found that magic constant is defined in \pegasus-2.14.1\pegasus\src\Pegasus\Common\Linkable.h file so I had two options: a) Switch build configuration to DEBUG (set PEGASUS_DEBUG=true) b) Remove DEBUG condition from lines 62 in Linkable.h file Then, I tried to build Pegasus again, unfortunately I got this error:

    error LNK2005: _OPENSSL_Applink already defined in SSLContext.obj

At this point I have no clue how to fix this problem. I just tried to remove these lines:

# ifdef PEGASUS_OS_TYPE_WINDOWS
 # include<openssl/applink.c>
# endif

from SSLContextRep.h file. After this modification I was able to get Pegasus clients binaries. But these binaries work just without SSL, when I want to use SSL communication I always got error: " Pegasus Exception: 'Cannot connect to 10.199.1.139:5989. Connection failed.'.", so I assume that is because my code modification in SSLContextRep.h.

Outputs from Pegasus Tracer:

SSL: Not connected 1 error:140740BF:SSL routines:SSL23_CLIENT_HELLO:no protocols available SSL: Deleted SSL socket

Does anybody know what can be wrong? Does somebody own some (better) environment configuration steps for Windows to build OpenPegasus?

Many thanks in advance for any kind of help.


Edit:

I need to be able work without certificates. Because I'm using SSL communication with various Storage Arrays and I don't have their certificates. Therefore I'm using this constructor of SSLContext:

SSLContext sslContext(String::EMPTY, NULL, String::EMPTY);

this approach works for me fine in the OpenPegasus 2.13 version.

2

There are 2 answers

1
Lukas Huf On BEST ANSWER

I got response from Open Pegasus dev team. They created bug for the issue with "magic" constant. Also they recommending in my case to use sslBackwardCompatibility = true configuration for the build.

This setting helped me partially. For some Storage Arrays the SSL communication started work. But for some it still reporting "Cannot connect" exception.

Only workaround what I found is to replace code of _makeSSLContext() method with code from OpenPegasus 2.13 version. After this modification I'm able to use SSL communication with all my Storage Arrays + all features from new Pegasus version.

1
jww On
Outputs from Pegasus Tracer:

    SSL: Not connected 1 error:140740BF:SSL routines:SSL23_CLIENT_HELLO:no protocols available SSL: Deleted SSL socket

Here's where the message is coming from:

$ grep -nR "Deleted SSL socket" *
src/Pegasus/Common/TLS.cpp:172:    PEG_TRACE_CSTRING(TRC_SSL, Tracer::LEVEL3, "---> SSL: Deleted SSL socket");

And the code around line 172:

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

    close();
    delete static_cast<SharedPtr<X509_STORE, FreeX509STOREPtr>*>(_crlStore);
    SSL_free(static_cast<SSL*>(_SSLConnection));

    PEG_TRACE_CSTRING(TRC_SSL, Tracer::LEVEL3, "---> SSL: Deleted SSL socket");

    PEG_METHOD_EXIT();
}

If you look in .../src/Pegasus/Common/SSLContext.cpp, you will see:

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

    //
    // create SSL Context Area
    //
    SSL_CTX *sslContext = NULL;
    if (!(sslContext = SSL_CTX_new(SSLv23_method())))
    {
        PEG_METHOD_EXIT();
        MessageLoaderParms parms(
            "Common.SSLContext.COULD_NOT_GET",
            "Could not get SSL CTX");
        throw SSLException(parms);
    }

    int options = SSL_OP_ALL;

    SSL_CTX_set_options(sslContext, options);
    if ( _sslCompatibility == false )
    {

#ifdef TLS1_2_VERSION
        // Enable only TLSv1.2 and disable all other protocol (SSL v2, SSL v3,
        // TLS v1.0, TLSv1.1)

        options = SSL_OP_NO_TLSv1 | SSL_OP_NO_TLSv1_1 | SSL_OP_NO_SSLv3;
#else
        PEG_METHOD_EXIT();
        MessageLoaderParms parms(
            " Common.SSLContext.TLS_1_2_PROTO_NOT_SUPPORTED",
            "TLSv1.2 protocol support is not detected on this system. "
            " To run in less secured mode, set sslBackwardCompatibility=true"
            " in planned config file and start cimserver.");
        throw SSLException(parms);
#endif
    }

    // sslv2 is off permanently even if sslCompatibility is true
    options |= SSL_OP_NO_SSLv2;
    SSL_CTX_set_options(sslContext, options);

#ifdef PEGASUS_SSL_WEAKENCRYPTION
    if (!(SSL_CTX_set_cipher_list(sslContext, SSL_TXT_EXP40)))
    {
        SSL_CTX_free(sslContext);
        sslContext = NULL;

        MessageLoaderParms parms(
            "Common.SSLContext.COULD_NOT_SET_CIPHER_LIST",
            "Could not set the cipher list");
        throw SSLException(parms);
    }
#endif

    if (_cipherSuite.size() != 0)
    {
        if (!(SSL_CTX_set_cipher_list(sslContext, _cipherSuite.getCString())))
        {
            SSL_CTX_free(sslContext);
            sslContext = NULL;

            PEG_TRACE_CSTRING(TRC_SSL, Tracer::LEVEL3,
                "---> SSL: Cipher Suite could not be specified");
            MessageLoaderParms parms(
                "Common.SSLContext.COULD_NOT_SET_CIPHER_LIST",
                "Could not set the cipher list");
            throw SSLException(parms);
        }
        else
        {
           PEG_TRACE((TRC_SSL, Tracer::LEVEL3,
                "---> SSL: Cipher suite set to %s",
                (const char *)_cipherSuite.getCString()));
        }
    }
    ...
}

I would ditch that function for two reasons, and add something like the following instead.

First, its one of those amorphic routines written to be both client and server. What I have found from my experience with OpenSSL, is you have separate functions for SSL_CTX* GetClientContext() and SSL_CTX* GetServerContext().

Second, from a Security Engineering perspective, you don't allow folks to get into a bad state with things like PEGASUS_SSL_WEAKENCRYPTION or an empty cipher list. You take the gun away so they can't shoot themselves in the foot.

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

    SSL_CTX *sslContext = NULL;
    if (!(sslContext = SSL_CTX_new(SSLv23_method())))
    {
        PEG_METHOD_EXIT();
        MessageLoaderParms parms(
            "Common.SSLContext.COULD_NOT_GET",
            "Could not get SSL CTX");
        throw SSLException(parms);
    }       

    // TLS 1.0 and above. No compression because it leaks information.
    static const long options = SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_COMPRESSION;
    SSL_CTX_set_options(sslContext, options);

    const char* const PREFERRED_CIPHERS = "HIGH:!aNULL:!kRSA:!PSK:!SRP:!MD5:!RC4";
    int res = SSL_set_cipher_list(sslContext, PREFERRED_CIPHERS);
    if(res != 1)
    {
        PEG_TRACE_CSTRING(TRC_SSL, Tracer::LEVEL3,
            "---> SSL: Cipher Suite could not be specified");
        MessageLoaderParms parms(
            "Common.SSLContext.COULD_NOT_SET_CIPHER_LIST",
            "Could not set the cipher list");
        throw SSLException(parms);
    }

    // Keep this stuff
    SSL_CTX_set_quiet_shutdown(sslContext, 1);
    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_mode(sslContext, SSL_MODE_RELEASE_BUFFERS);

    // Back to gutting. We don't allow VERIFY_PEER_NONE.
    {
        PEG_TRACE_CSTRING(TRC_SSL, Tracer::LEVEL4,
            "---> SSL: certificate verification callback specified");
        SSL_CTX_set_verify(sslContext,
            SSL_VERIFY_PEER, prepareForCallback);
    }

    // Some more gutting. Certificates have to be verified.
    if(_trustStore.size() == 0)
    {
        PEG_TRACE((TRC_SSL, Tracer::LEVEL1,
                "---> SSL: Could not load certificates from the "
                "trust store: %s",
                (const char*)_trustStore.getCString()));
        MessageLoaderParms parms(
                "Common.SSLContext.COULD_NOT_LOAD_CERTIFICATES",
                "Could not load certificates in to trust store.");
        SSL_CTX_free(sslContext);
        sslContext = NULL;

        PEG_METHOD_EXIT();
        throw SSLException(parms);
    }

    if ( !SSL_CTX_load_verify_locations(
        sslContext, _trustStore.getCString(), NULL) )
        {
            PEG_TRACE((TRC_SSL, Tracer::LEVEL1,
                "---> SSL: Could not load certificates from the "
                "trust store: %s",
                (const char*)_trustStore.getCString()));
            MessageLoaderParms parms(
                "Common.SSLContext.COULD_NOT_LOAD_CERTIFICATES",
                "Could not load certificates in to trust store.");
            SSL_CTX_free(sslContext);
            sslContext = NULL;

            PEG_METHOD_EXIT();
            throw SSLException(parms);
        }

     // I'm not sure what to do with CRLs. They are usually a DoS waiting to happen....
     if (_crlPath.size() != 0)
     {
        // need to save this -- can we make it static since there's only
        // one CRL for cimserver?
        X509_LOOKUP* pLookup;

        _crlStore.reset(X509_STORE_new());
        if (_crlStore.get() == NULL)
        {
            SSL_CTX_free(sslContext);
            sslContext = NULL;
            PEG_METHOD_EXIT();
            throw PEGASUS_STD(bad_alloc)();
        }

        // the validity of the crlstore was checked in ConfigManager
        // during server startup
        if (FileSystem::isDirectory(_crlPath))
        {
            PEG_TRACE((TRC_SSL, Tracer::LEVEL4,
                "---> SSL: CRL store is a directory in %s",
                (const char*)_crlPath.getCString()));

            if ((pLookup = X509_STORE_add_lookup(
                     _crlStore.get(), X509_LOOKUP_hash_dir())) == NULL)
            {
                MessageLoaderParms parms(
                    "Common.SSLContext.COULD_NOT_LOAD_CRLS",
                    "Could not load certificate revocation list.");
                _crlStore.reset();
                SSL_CTX_free(sslContext);
                sslContext = NULL;
                PEG_METHOD_EXIT();
                throw SSLException(parms);
            }

            X509_LOOKUP_add_dir(
                pLookup, (const char*)_crlPath.getCString(), X509_FILETYPE_PEM);

            PEG_TRACE_CSTRING(TRC_SSL, Tracer::LEVEL3,
                "---> SSL: Successfully configured CRL directory");
        }
        else
        {
            PEG_TRACE((TRC_SSL, Tracer::LEVEL4,
                "---> SSL: CRL store is the file %s",
                (const char*)_crlPath.getCString()));

            if ((pLookup = X509_STORE_add_lookup(
                   _crlStore.get(), X509_LOOKUP_file())) == NULL)
            {
                MessageLoaderParms parms(
                    "Common.SSLContext.COULD_NOT_LOAD_CRLS",
                    "Could not load certificate revocation list.");
                _crlStore.reset();
                SSL_CTX_free(sslContext);
                sslContext = NULL;
                PEG_METHOD_EXIT();
                throw SSLException(parms);
            }

            X509_LOOKUP_load_file(
                pLookup, (const char*)_crlPath.getCString(), X509_FILETYPE_PEM);

            PEG_TRACE_CSTRING(TRC_SSL, Tracer::LEVEL4,
                "---> SSL: Successfully configured CRL file");
        }
    }

    Boolean keyLoaded = false;

    // Gut server specific certificate and key routines since this is a client.

    PEG_METHOD_EXIT();
    return sslContext;
}

TLS 1.2 and the AEAD cipher suites are a very good choice. However, for most intents and purposes, TLS 1.0 and above is fine.


I think this might be the cause of 0x140740BF in the client. Its from line SSLContext.cpp, 824:

SSL_CTX_set_verify(sslContext,
    SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE, prepareForCallback);

It looks like the server requires a certificate.

... but usually you get a different TLS alert.


And the sources does not call SSL_set_tlsext_host_name, so SNI appears to be broken. You should probably file a bug report for this one...

$ grep -nR SSL_set_tlsext_host_name *
$

You will have to figure out where the client makes its connection, and set that as a SSL* option:

SSL_set_tlsext_host_name(ssl, hostname);

Somewhere around SSLSocket::SSLSocket might be a good choice because its constructor takes a string and the sslConnection is available in the ctor.

SSLSocket::SSLSocket(
    SocketHandle socket,
    SSLContext * sslcontext,
    ReadWriteSem * sslContextObjectLock,
    const String& ipAddress)

But I'm pretty sure you need a DNS name and not an IP address because the multiplexing of different servers on the same IP is what caused the need for SNI in the first place.

But I could be wrong. const String& ipAddress could actually be a DNS name.