Posted: March 30, 2009 by Datazygte, Inc - contribution by ron scheckelhoff -- rscheckelhoff- at-fourcalorieserver -dot-com
I've always been wary of canned security applications. Black box security may be no security at all.
Now there are a number of toolkits available to provide an
an opportunity to more closely monitor and modify security related infrastructure.
From a programmer's standpoint, these toolkits are not so much about the encryption algorithms themselves, but instead deals with the
handling of the "glue code" that must surround the algorithms in order to make them do useful things. This "glue code" accomplishes
a lot of legwork, doing things that line up the private keys and the certificates, negotiate the selection of ciphers, and numerous
other details.
This article is intended not so much as a complete application description, but is instead given with the intention of shining some light on the general process by which a toolkit API may be utilized to accomplish security related tasks.
We use applications every day that contain embedded security measures (or lack thereof), and we are often unaware of the consequences of decisions made within the software, and by the software, that may ultimately lower or eliminate security benefits.
By relying on software that makes all of the decisions for us, we are sometimes led down the primrose path. For instance, with an SSL connection being negotiated between a client and a server, wherein each allows for a selection of security attributes and ciphers ranging from poor to good, the ultimate settlement may be upon a pick that effectively downgrades the security measures to the least effective one in order to make the connection. In some cases, this "downgrading" is not advertised in any way to the user.
By using the toolkit API to build our own security glue software, we can force the decisions to be what we want, and not some
lowly common denominator.
Note that this author is himself just at this moment investigating the capabilities of the toolkit API, and so is learning by
writing about it, analyzing, quantifying, and abstracting it such that the reader may learn as he learns.
I note that the online documentation about the use of these kinds of APIs at a higher level is somewhat scarce, and the online docs are relatively spartan in terms of the descriptions of what happens when, and for what reason. The descriptions of the individual API methods are generally quite excellent, in my opinion.
The code (below) shows my initial stab at a high level abstraction of the major API method players - those that always become involved in a secure transaction implemented by the toolkit. Note the code is very preliminary, includes some pseudo-code, and should not be used in any real application. It is part of the learning experience. It is "step one" of our experiment.
/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
/*
# Copyright (c) 2009, Datazygte, Inc
# Contributing author(s): Ron Scheckelhoff
#
# Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following
# conditions ar# met:
# * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer
# in the documentation and/or other materials provided with the distribution.
# * Neither Datazygte, Inc, nor the names of its contributors may be used to endorse or promote products derived from this software
# without specific prior written permission.
# * Distribution must include this entire web page, intact: http://www.fourcalorieservers.com/servers/fourcalssl.html
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
# (INCLUDING BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <stdio.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <signal.h>
#include <sys/types.h>
#include <fcntl.h>
#include <signal.h>
#include <unistd.h>
#include <dzssl.h>
//
// setupSSLContext - return is NULL unless context successfully created
//
SSL_CTX* setupSSLContext()
{
SSL_METHOD* sslmeth = NULL;
SSL_CTX* sslctx = NULL;
int intReturn = SSL_library_init();
if (intReturn == 1)
{
sslmeth = SSLv3_method();
if (sslmeth != NULL)
{
sslctx = SSL_CTX_new(sslmeth);
}
}
return (sslctx);
}
//
// Process Connection
//
int ProcessConnection(SSL* ssl, int socNew))
{
int intCode = 0;
// Configure SSL Connection
int intResult = ConfigureSSLConnection(ssl); // pseudo
if (intResult == SUCCESS)
{
// Connect SSL
intResult = SSL_connect(ssl);
if (intResult > 0)
{
// Use Select, SSL_write, and SSL_read to transfer encrypted data
// ...
}
else
{
intResult = SSL_get_error(ssl, intResult);
switch (intResult)
{
// No, it's not silly ... stepping through this in debugger is informative
case SSL_ERROR_ZERO_RETURN:
intCode = 1;
break;
case SSL_ERROR_WANT_READ:
intCode = 2;
break;
case SSL_ERROR_WANT_WRITE:
intCode = 3;
break;
default:
intCode = 4;
}
intResult = intCode;
}
}
return (intResult)
}
//
// main function is obviously designed to run one and only one connection,
// which keeps it sweet and simple.
//
int main()
{
int socNew = 0;
int intResult = FAILURE;
int intShutdown = 0;
struct sockaddr_in addr;
BIO* bioNew = NULL;
SSL* ssl = NULL;
SSL_CTX* sslctx1 = NULL;
sslctx1 = setupSSLContext();
if (sslctx1 != NULL)
{
memset(&addr,0,sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(443);
addr.sin_addr.s_addr = inet_addr("127.0.0.1");
socNew = socket(AF_INET, SOCK_STREAM, 0);
if (socNew > 0)
{
intResult = connect(socNew, (struct sockaddr*) &addr, sizeof(addr));
if (intResult >= 0)
{
ssl = SSL_new(sslctx1);
if (ssl != NULL)
{
bioNew = BIO_new_socket(socNew, BIO_NOCLOSE);
if (bioNew != NULL)
{
SSL_set_bio(ssl, bioNew, bioNew);
intResult = ProcessConnection(ssl, socNew);
intShutdown = SSL_shutdown(ssl);
}
else
intResult = FAILURE_BIO;
SSL_free(ssl); // also frees bioNew;
}
else
intResult = FAILURE_SSL;
}
close(socNew);
}
else
intResult = FAILURE_SOCK;
SSL_CTX_free(sslctx1);
}
else
intResult = FAILURE_CONTEXT;
// Since this is part of our "experiment", terse error ok
if (intResult != SUCCESS)
printf("Problem! Error code: %d \n", intResult);
printf("Shutdown status: %d \n", intShutdown);
}
What Have we Written?
SSL
Continued post: 04/02/2009
So, what's going on? The API really is a blend of SSL related functions
and a number of wrappers for the Berkley sockets that it works around. This makes
the API pleasantly familiar for those with sockets experience.
When referencing the code (above), one will note that the standard Berkley socket was set up in advance of anything else, other than the context. In fact, the actual TCP/IP connection was made in advance of any manipulations by the SSL API. Subsequent manipulations by the SSL wrappers thusly allow the encrypted traffic to ride on that initial socket connection.
Later in the code, we will use wrappers for the regular socket equivalents, to read and write encrypted data over the basic TCP/IP connection. Thus, there must be some way to tell the toolkit about our regular connection, so that the toolkit libraries can use that connection to send (and receive) our communications (after it's wrappers encrypt and/or decrypt those comunications.
That "method", or "way" to tell the toolkit libraries about our regular connection is via the BIO_new_socket() and SSL_set_bio() API calls.
In the snippet (below), it is readily apparent that the library API call BIO_new_socket receives the socket descriptor for our regular socket.
bioNew = BIO_new_socket(socNew, BIO_NOCLOSE);
Just as apparent is the association to the ssl object:
SSL_set_bio(ssl, bioNew, bioNew);
To be continued ...
Note that the toolkit used in this review is an open source project which is under a BSD-like license type, is not affiliated
with this site or it's owners, and
does not endorse this site in any way. All things related to this third party toolkit may be found at:
http://www.openssl.org
Contact for this site (fourcalorieservers.com) : Ron Scheckelhoff -- Email suggestions to: rscheckelhoff@fourcalorieservers.com
( )