Question

I'm working on a C++ project which will need to both consume and provide RESTful HTTPS web services (isn't that a lovely combination!) and have chosen the POCO libraries to make it work (for license, platform support, and ease of use/adaptability reasons I won't go into here). I am new to POCO and to OpenSSL which it uses to provide HTTPS functionality.

The issue I've run into is that it seems that the SSLManager and Context classes (from POCO's NetSSL_OpenSSL library) only support either client or server contexts but not both. I've tested that this is the case with a simple HTTPS client and server. So I did some digging into the POCO code and for the SSLManager's part the server functionality seems to be a superset of the client functionality (so I could call the initializeServer() method on it and it should work). Where I seem to be in trouble is the Context, which seems to server as a wrapper around an OpenSSL context structure (hold the Heartbleed jokes, please! :) ).

Now, after some more searching I came across this page which has a nice little table which explains to me, the OpenSSL neophyte, that I need to fill out a SSL_METHOD structure in order to create an OpenSSL context and here are a few canned ones OpenSSL provides for client, server, and combined uses (for those that follow that link, no I'm not doing anything related to OpenVMS, I'm on Windows and Linux for the purposes of this question). And sure enough in the POCO Context createSSLContext() method several of the SSL_METHODs from the table are used, though not the ones for the combined client and server.

I have tried modifiying the POCO code to add a "MIXED_USE" usage and updated the context code appropriately and from my toy example it seems to work. This brings me to my question. Am I missing anything which would or might cause me problems down the road if I go down this route? Besides the obvious "well POCO also references the usage type over here you blind idiot" issues, are there any ill effects I might run into from basically taking a set of OpenSSL (wrapper) code and simply changing the SSL method for the SSL context in my web services? Any help/pointers/thoughts are appreciated.


TL;DR version: POCO HTTPS libraries expect you to use either a client or server OpenSSL context, but I want to tweak them and just change the SSL_METHOD to support a combined client and server OpenSSL context? How screwed am I?


EDIT 4/24/2014: So in response to jww's answer, let me sketch out the Context class a bit. It basically wraps a pointer to an OpenSSL context structure, named _pSSLContext. To the constructor there's a Usage enum parameter (which sets a member _usage) which has a few possible values. As part of the Context object's initialization it calls an internal method called createSSLContext() which looks like this:

void Context::createSSLContext()
{
    if (SSLManager::isFIPSEnabled())
    {
        _pSSLContext = SSL_CTX_new(TLSv1_method());
    }
    else
    {
        switch (_usage)
        {
        case CLIENT_USE:
            _pSSLContext = SSL_CTX_new(SSLv23_client_method());
            break;
        case SERVER_USE:
            _pSSLContext = SSL_CTX_new(SSLv23_server_method());
            break;
        case TLSV1_CLIENT_USE:
            _pSSLContext = SSL_CTX_new(TLSv1_client_method());
            break;
        case TLSV1_SERVER_USE:
            _pSSLContext = SSL_CTX_new(TLSv1_server_method());
            break;
        default:
            throw Poco::InvalidArgumentException("Invalid usage");
        }
    }
    if (!_pSSLContext) 
    {
        unsigned long err = ERR_get_error();
        throw SSLException("Cannot create SSL_CTX object", ERR_error_string(err, 0));
    }

    SSL_CTX_set_default_passwd_cb(_pSSLContext, &SSLManager::privateKeyPassphraseCallback);
    Utility::clearErrorStack();
    SSL_CTX_set_options(_pSSLContext, SSL_OP_ALL);
}

I think I should be able to add another value to the Usage enum which would cause the switch statement above to make call initialize the context with the SSL_METHOD being SSLv23_method(). I've tested this in a toy example and it seems to work.

My concern, which led to my question, is that there might be side effects of making a change like this. I don't know POCO or OpenSSL enough to know if there are other levers I need to pull in order to make that work. For instance, if there's something about the SSLv23_server/client_method()s where just inserting SSLv23_method() in its place without changing something somewhere else too might break something (possibly subtly so).

Was it helpful?

Solution

To answer my own question later, I was able to edit the POCO code to allow me to specifiy a "Both" context. That is I updated the code I initially posted to be like:

void Context::createSSLContext()
{
    if (SSLManager::isFIPSEnabled())
    {
        _pSSLContext = SSL_CTX_new(TLSv1_method());
    }
    else
    {
        switch (_usage)
        {
        case CLIENT_USE:
            _pSSLContext = SSL_CTX_new(SSLv23_client_method());
            break;
        case SERVER_USE:
            _pSSLContext = SSL_CTX_new(SSLv23_server_method());
            break;
        case BOTH_USE:
            _pSSLContext = SSL_CTX_new(SSLv23_method());
            break;
        case TLSV1_CLIENT_USE:
            _pSSLContext = SSL_CTX_new(TLSv1_client_method());
            break;
        case TLSV1_SERVER_USE:
            _pSSLContext = SSL_CTX_new(TLSv1_server_method());
            break;            
        case TLSV1_BOTH_USE:
            _pSSLContext = SSL_CTX_new(TLSv1_method());
            break;
        default:
            throw Poco::InvalidArgumentException("Invalid usage");
        }
    }
    if (!_pSSLContext) 
    {
        unsigned long err = ERR_get_error();
        throw SSLException("Cannot create SSL_CTX object", ERR_error_string(err, 0));
    }

    SSL_CTX_set_default_passwd_cb(_pSSLContext, &SSLManager::privateKeyPassphraseCallback);
    Utility::clearErrorStack();
    SSL_CTX_set_options(_pSSLContext, SSL_OP_ALL);
}

Modifying the code like this allowed my application to act as both an HTTPS server and client. If anybody is in the same boat I was I hope this puts your mind at ease.

OTHER TIPS

POCO HTTPS libraries expect you to use either a client or server OpenSSL context, but I want to tweak them and just change the SSL_METHOD to support a combined client and server OpenSSL context?

Well, I don't know POCO so I can't comment on it. But it appears you want to use a single context for both POCO_Client_Ctx and POCO_Server_Ctx (just guessing at the names because there's no code provided).

In this case, you might be able to reuse the same OpenSSL context. The reused context should set the common options, like protocols and your private PKI's CA.

SSL_CTX* CreateSSLContext()
{
    const SSL_METHOD* method = SSLv23_method();
    if(NULL == method) handleFailure();

    ctx = SSL_CTX_new(method);
    if((ctx == NULL) handleFailure();

    long flags = SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3;
    flags |= SSL_OP_NO_COMPRESSION;
    SSL_CTX_set_options(ctx, flags);

    rc = SSL_CTX_load_verify_locations(...);
    if(rc != 1) handleFailure();
    ...

    return ctx;
}

Then, your code:

static SSL_CTX* g_ctx = CreateSSLContext();

POCO_Client_Ctx(g_ctx);
POCO_Server_Ctx(g_ctx);

From CreateSSLContext, the SSL_CTX* has a reference count of 1. You can keep the SSL_CTX* for the life of both the POCO client and server. Or you can increment it with the following and delete it at will:

CRYPTO_add(&g_ctx->references, 1, CRYPTO_LOCK_SSL_CTX);

SSL_CTX_free will decrement the reference count. After the count reaches 0, the library will delete the SSL_CTX*.

POCO_Client_Ctx and POCO_Server_Ctx will eventually use the SSL_CTX* to get a SSL* reference. You probably need to fetch the SSL* from the POCO library.

You can further tune the properties on the SSL* using SSL_* functions. For example, the SSL* used by POCO_Client_Ctx might call SSL_set_tlsext_host_name; while the POCO_Server_Ctx might call SSL_use_certificate.

In general though, I've always created a separate context for clients and servers; and I've reused contexts among clients, and reused contexts among servers. But I've never comingled client and server contexts.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top