Blame ssl/oh_ssl.c

Packit db01ca
/*
Packit db01ca
 * Copyright (C) 2007-2008, Hewlett-Packard Development Company, LLP
Packit db01ca
 *		       All rights reserved.
Packit db01ca
 *
Packit db01ca
 * Redistribution and use in source and binary forms, with or
Packit db01ca
 * without modification, are permitted provided that the following
Packit db01ca
 * conditions are met:
Packit db01ca
 *
Packit db01ca
 * Redistributions of source code must retain the above copyright
Packit db01ca
 * notice, this list of conditions and the following disclaimer.
Packit db01ca
 * Redistributions in binary form must reproduce the above copyright
Packit db01ca
 * notice, this list of conditions and the following disclaimer in
Packit db01ca
 * the documentation and/or other materials provided with the distribution.
Packit db01ca
 *
Packit db01ca
 * Neither the name of the Hewlett-Packard Corporation, nor the names
Packit db01ca
 * of its contributors may be used to endorse or promote products
Packit db01ca
 * derived from this software without specific prior written permission.
Packit db01ca
 *
Packit db01ca
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
Packit db01ca
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
Packit db01ca
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
Packit db01ca
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
Packit db01ca
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
Packit db01ca
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
Packit db01ca
 * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
Packit db01ca
 * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
Packit db01ca
 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
Packit db01ca
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
Packit db01ca
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Packit db01ca
 *
Packit db01ca
 * Author(s)
Packit db01ca
 *     Bryan Sutula <Bryan.Sutula@hp.com>
Packit db01ca
 *     Shuah Khan <shuah.khan@hp.com>
Packit db01ca
 *     Richard White <richard.white@hp.com>
Packit db01ca
 *
Packit db01ca
 *
Packit db01ca
 * This file implements OpenSSL initialization and connection management
Packit db01ca
 * functionality such as open, close, send, and receive.
Packit db01ca
 *
Packit db01ca
 * The initialization is done here and called by the OpenHPI infrastructure,
Packit db01ca
 * so that individual modules and plugins don't have to worry about any
Packit db01ca
 * global OpenSSL initialization.  Some of this initialization can only be
Packit db01ca
 * done once, so this is the best place for it.
Packit db01ca
 *
Packit db01ca
 * OpenSSL is a complex library, and the code to reliably communicate over
Packit db01ca
 * an SSL channel includes a number of subtle factors.  Rather than having
Packit db01ca
 * each plug-in duplicate the effort to get this right, it's easier to
Packit db01ca
 * centralize SSL communication support in this library.
Packit db01ca
 *
Packit db01ca
 * The following functions are provided:
Packit db01ca
 *
Packit db01ca
 * oh_ssl_init()		- Intializes the OpenSSL library (called by the
Packit db01ca
 * 				  OpenHPI infrastructure only)
Packit db01ca
 * oh_ssl_ctx_init()            - Creates a new SSL_CTX object
Packit db01ca
 * oh_ssl_ctx_free()            - Free an SSL_CTX object
Packit db01ca
 * oh_ssl_connect()             - Create and open a new ssl conection
Packit db01ca
 * oh_ssl_disconnect()          - Close and free an SSL connection
Packit db01ca
 * oh_ssl_read()                - Read from an SSL connection
Packit db01ca
 * oh_ssl_write()               - Write to an SSL connection
Packit db01ca
 */
Packit db01ca
Packit db01ca
Packit db01ca
/* OpenSSL and other header files */
Packit db01ca
#include <openssl/ssl.h>
Packit db01ca
#include <openssl/err.h>
Packit db01ca
#include <openssl/crypto.h>
Packit db01ca
#include <openssl/bio.h>
Packit db01ca
#include <openssl/rand.h>
Packit db01ca
#include <openssl/engine.h>
Packit db01ca
#include <unistd.h>
Packit db01ca
#include <string.h>
Packit db01ca
#include <sys/select.h>
Packit db01ca
#include <glib.h>
Packit db01ca
#include <oh_ssl.h>
Packit db01ca
#include <oh_error.h>
Packit db01ca
Packit db01ca
#include <sys/ioctl.h>
Packit db01ca
#include <sys/socket.h>
Packit db01ca
#include <sys/types.h>
Packit db01ca
#include <arpa/inet.h>
Packit db01ca
#include <netdb.h>
Packit db01ca
#include <sahpi_wrappers.h>
Packit db01ca
Packit db01ca
/* Data types used by this module */
Packit db01ca
struct CRYPTO_dynlock_value {
Packit db01ca
	GMutex	*mutex;
Packit db01ca
};
Packit db01ca
Packit db01ca
Packit db01ca
/* Global (static) data for this module */
Packit db01ca
static int	oh_ssl_init_done = 0;	/* Will be set true when done */
Packit db01ca
static GMutex **mutexes = NULL;         /* Holds array of SSL mutexes */
Packit db01ca
#if GLIB_CHECK_VERSION (2, 32, 0)
Packit db01ca
static GMutex ssl_mutexes; /* Lock for above */
Packit db01ca
#else
Packit db01ca
static GStaticMutex ssl_mutexes = G_STATIC_MUTEX_INIT; /* Lock for above */
Packit db01ca
#endif
Packit db01ca
Packit db01ca
/* Local (static) functions, used by this module.  Note that these aren't
Packit db01ca
 * necessary if we aren't compiling as a threaded implementation, so we
Packit db01ca
 * skip them in that case.
Packit db01ca
 */
Packit db01ca
Packit db01ca
Packit db01ca
/**
Packit db01ca
 * id_function
Packit db01ca
 *
Packit db01ca
 * SSL thread ID function
Packit db01ca
 *
Packit db01ca
 * Return value: a unique thread identifier, as an unsigned long
Packit db01ca
 **/
Packit db01ca
static unsigned long id_function(void)
Packit db01ca
{
Packit db01ca
	return((unsigned long) g_thread_self());
Packit db01ca
}
Packit db01ca
Packit db01ca
Packit db01ca
/**
Packit db01ca
 * lock_function
Packit db01ca
 * @mode:	Includes the CRYPTO_LOCK bit if a lock is intended.  Otherwise,
Packit db01ca
 *		an unlock is desired.
Packit db01ca
 * @type:	Ordinal number of the mutex being manipulated
Packit db01ca
 * @file:	(unused)
Packit db01ca
 * @line:	(unused)
Packit db01ca
 *
Packit db01ca
 * SSL mutex lock and unlock function.  This is complicated, somewhat,
Packit db01ca
 * because we're trying to defer allocation of memory and mutexes until
Packit db01ca
 * they're actually needed.
Packit db01ca
 *
Packit db01ca
 * Note that OpenSSL defines that this function has no error return.  In the
Packit db01ca
 * case where we can't allocate memory, we'll just have to return, pretending
Packit db01ca
 * that we did the lock.  This will be a silent failure.  The alternative
Packit db01ca
 * would be to allocate the array of mutex pointers during thread_setup().
Packit db01ca
 *
Packit db01ca
 * Return value: (none)
Packit db01ca
 **/
Packit db01ca
static void lock_function(int mode, int type, const char * file, int line)
Packit db01ca
{
Packit db01ca
	/* Do we have an array of mutex pointers yet? */
Packit db01ca
	if (! mutexes) {
Packit db01ca
		/* Messing with this requires the static lock */
Packit db01ca
		wrap_g_static_mutex_lock(&ssl_mutexes);
Packit db01ca
		if (! mutexes) {	/* Need to check again */
Packit db01ca
			mutexes = (GMutex **)g_malloc0(CRYPTO_num_locks() *
Packit db01ca
						       sizeof(GMutex *));
Packit db01ca
			if (! mutexes) {
Packit db01ca
				CRIT("out of memory");
Packit db01ca
				wrap_g_static_mutex_unlock(&ssl_mutexes);
Packit db01ca
				return;
Packit db01ca
			}
Packit db01ca
		}
Packit db01ca
		wrap_g_static_mutex_unlock(&ssl_mutexes);
Packit db01ca
	}
Packit db01ca
Packit db01ca
	/* Have we initialized this particular mutex? */
Packit db01ca
	if (! mutexes[type]) {
Packit db01ca
		/* Same firedrill as above */
Packit db01ca
		wrap_g_static_mutex_lock(&ssl_mutexes);
Packit db01ca
		if (! mutexes[type]) {
Packit db01ca
			mutexes[type] = wrap_g_mutex_new_init();
Packit db01ca
		}
Packit db01ca
		wrap_g_static_mutex_unlock(&ssl_mutexes);
Packit db01ca
	}
Packit db01ca
Packit db01ca
	/* Finally, go ahead and lock or unlock it */
Packit db01ca
	if (mode & CRYPTO_LOCK) {
Packit db01ca
		g_mutex_lock(mutexes[type]);
Packit db01ca
	}
Packit db01ca
	else {
Packit db01ca
		g_mutex_unlock(mutexes[type]);
Packit db01ca
	}
Packit db01ca
}
Packit db01ca
Packit db01ca
Packit db01ca
/**
Packit db01ca
 * dyn_create_function
Packit db01ca
 * @file:	(unused)
Packit db01ca
 * @line:	(unused)
Packit db01ca
 *
Packit db01ca
 * Function to create and initialize dynlock mutexes
Packit db01ca
 *
Packit db01ca
 * Return value: pointer to dynlock structure, or NULL on failure (out of mem)
Packit db01ca
 **/
Packit db01ca
static struct CRYPTO_dynlock_value *dyn_create_function(const char *file,
Packit db01ca
							int line)
Packit db01ca
{
Packit db01ca
	struct CRYPTO_dynlock_value *value;
Packit db01ca
Packit db01ca
	if ((value = (struct CRYPTO_dynlock_value *)
Packit db01ca
			g_malloc0(sizeof(struct CRYPTO_dynlock_value)))) {
Packit db01ca
		value->mutex = wrap_g_mutex_new_init();
Packit db01ca
	}
Packit db01ca
	else {
Packit db01ca
		CRIT("out of memory");
Packit db01ca
	}
Packit db01ca
Packit db01ca
	return(value);
Packit db01ca
}
Packit db01ca
Packit db01ca
Packit db01ca
/**
Packit db01ca
 * dyn_lock_function
Packit db01ca
 * @mode:	Includes the CRYPTO_LOCK bit if a lock is intended.  Otherwise,
Packit db01ca
 *		an unlock is desired.
Packit db01ca
 * @l:		Pointer to dynlock structure returned by dyn_create_function()
Packit db01ca
 * @file:	(unused)
Packit db01ca
 * @line:	(unused)
Packit db01ca
 *
Packit db01ca
 * Function to lock and unlock dynlock mutexes
Packit db01ca
 *
Packit db01ca
 * Return value: (none)
Packit db01ca
 **/
Packit db01ca
static void dyn_lock_function(int mode, struct CRYPTO_dynlock_value *l,
Packit db01ca
			      const char *file, int line)
Packit db01ca
{
Packit db01ca
	if (mode & CRYPTO_LOCK) {
Packit db01ca
		g_mutex_lock(l->mutex);
Packit db01ca
	}
Packit db01ca
	else {
Packit db01ca
		g_mutex_unlock(l->mutex);
Packit db01ca
	}
Packit db01ca
}
Packit db01ca
Packit db01ca
Packit db01ca
/**
Packit db01ca
 * dyn_destroy_function
Packit db01ca
 * @l:		Pointer to dynlock structure returned by dyn_create_function()
Packit db01ca
 * @file:	(unused)
Packit db01ca
 * @line:	(unused)
Packit db01ca
 *
Packit db01ca
 * Function to destroy dynlock mutexes
Packit db01ca
 *
Packit db01ca
 * Return value: (none)
Packit db01ca
 **/
Packit db01ca
static void	dyn_destroy_function(struct CRYPTO_dynlock_value *l,
Packit db01ca
				     const char *file, int line)
Packit db01ca
{
Packit db01ca
	wrap_g_mutex_free_clear(l->mutex);
Packit db01ca
	g_free(l);
Packit db01ca
}
Packit db01ca
Packit db01ca
Packit db01ca
/**
Packit db01ca
 * thread_setup
Packit db01ca
 *
Packit db01ca
 * Set up multi-thread protection used by the SSL library
Packit db01ca
 *
Packit db01ca
 * Return value: 0 for success, -1 for failure
Packit db01ca
 **/
Packit db01ca
static int	thread_setup(void)
Packit db01ca
{
Packit db01ca
	/* Register our locking functions with the SSL library */
Packit db01ca
	CRYPTO_set_id_callback(id_function);
Packit db01ca
	CRYPTO_set_locking_callback(lock_function);
Packit db01ca
	CRYPTO_set_dynlock_create_callback(dyn_create_function);
Packit db01ca
	CRYPTO_set_dynlock_lock_callback(dyn_lock_function);
Packit db01ca
	CRYPTO_set_dynlock_destroy_callback(dyn_destroy_function);
Packit db01ca
Packit db01ca
	return(0);			/* No errors */
Packit db01ca
}
Packit db01ca
Packit db01ca
Packit db01ca
/**
Packit db01ca
 * thread_cleanup
Packit db01ca
 *
Packit db01ca
 * Clean up multi-thread protection used by the SSL library.
Packit db01ca
 *
Packit db01ca
 * Note that this function is not currently used because there is no shutdown
Packit db01ca
 * code for plugins.  It is left here in case that happens in the future.
Packit db01ca
 *
Packit db01ca
 * Return value: 0 for success, -1 for failure (though it currently can't fail)
Packit db01ca
 **/
Packit db01ca
static int	thread_cleanup(void)
Packit db01ca
{
Packit db01ca
	int		i;
Packit db01ca
Packit db01ca
	/* Nullify the locking functions we registered with the SSL library */
Packit db01ca
	CRYPTO_set_id_callback(NULL);
Packit db01ca
	CRYPTO_set_locking_callback(NULL);
Packit db01ca
	CRYPTO_set_dynlock_create_callback(NULL);
Packit db01ca
	CRYPTO_set_dynlock_lock_callback(NULL);
Packit db01ca
	CRYPTO_set_dynlock_destroy_callback(NULL);
Packit db01ca
Packit db01ca
	/* Clean up and destroy mutexes, if we have any */
Packit db01ca
	wrap_g_static_mutex_lock(&ssl_mutexes);
Packit db01ca
	if (mutexes) {
Packit db01ca
		for (i = 0; i < CRYPTO_num_locks(); i++) {
Packit db01ca
			if (mutexes[i]) {
Packit db01ca
				wrap_g_mutex_free_clear(mutexes[i]);
Packit db01ca
			}
Packit db01ca
		}
Packit db01ca
		g_free(mutexes);
Packit db01ca
		mutexes = NULL;
Packit db01ca
	}
Packit db01ca
        wrap_g_static_mutex_unlock(&ssl_mutexes);
Packit db01ca
	wrap_g_static_mutex_free_clear(&ssl_mutexes);
Packit db01ca
Packit db01ca
	return(0);			/* No errors */
Packit db01ca
}
Packit db01ca
Packit db01ca
/**
Packit db01ca
 * oh_ssl_init
Packit db01ca
 *
Packit db01ca
 * Intialize the OpenSSL library.  Note that the calls used in this routine
Packit db01ca
 * set up global data and are only to be called once for an SSL-based program.
Packit db01ca
 * To enforce this while allowing multiple callers (plugins) to initialize
Packit db01ca
 * the library, we use a static global variable to mark when we've done the
Packit db01ca
 * initialization.
Packit db01ca
 *
Packit db01ca
 * Note that the thread-safe initialization portion requires that
Packit db01ca
 * g_thread_init() has already been called, so don't call this routine
Packit db01ca
 * before then.
Packit db01ca
 *
Packit db01ca
 * Return value: 0 for success, -1 for failure
Packit db01ca
 **/
Packit db01ca
int		oh_ssl_init(void)
Packit db01ca
{
Packit db01ca
	if (! oh_ssl_init_done) {	/* Do this only once */
Packit db01ca
		oh_ssl_init_done = 1;
Packit db01ca
Packit db01ca
		/* Load error strings to provide human-readable error
Packit db01ca
		 * messages
Packit db01ca
		 */
Packit db01ca
		SSL_load_error_strings();
Packit db01ca
		ERR_load_BIO_strings();
Packit db01ca
Packit db01ca
		/* Initialize the SSL library */
Packit db01ca
		if (! SSL_library_init()) {
Packit db01ca
			CRIT("SSL_library_init() failed");
Packit db01ca
			return(-1);
Packit db01ca
		}
Packit db01ca
Packit db01ca
#ifndef NO_SSL_RAND_SEED		/* In case this isn't portable */
Packit db01ca
		/* Actions to seed PRNG */
Packit db01ca
		RAND_load_file("/dev/urandom", 1024);
Packit db01ca
#endif
Packit db01ca
Packit db01ca
		/* Set up multi-thread protection functions */
Packit db01ca
		if (thread_setup() ) {
Packit db01ca
			CRIT("SSL multi-thread protection setup call failed");
Packit db01ca
			return(-1);
Packit db01ca
		}
Packit db01ca
Packit db01ca
	}
Packit db01ca
Packit db01ca
	return(0);			/* Successful return */
Packit db01ca
}
Packit db01ca
Packit db01ca
/**
Packit db01ca
 * oh_ssl_finit
Packit db01ca
 *
Packit db01ca
 * Finalizes the OpenSSL library. The calls used in this routine releases global
Packit db01ca
 * data created/initialized by the OpenSSL library.
Packit db01ca
 *
Packit db01ca
 * Note that it is the responisbility of the caller of this function to make
Packit db01ca
 * sure that no other threads are making the OpenSSL library calls. The openhpid
Packit db01ca
 * should close all the threads and call this function from the main (single)
Packit db01ca
 * thread.
Packit db01ca
 *
Packit db01ca
 * Return value: None
Packit db01ca
 **/
Packit db01ca
void oh_ssl_finit(void)
Packit db01ca
{
Packit db01ca
        /* TODO: Check whether any other SSL library cleanup should be called */
Packit db01ca
        thread_cleanup();
Packit db01ca
        ENGINE_cleanup();
Packit db01ca
        CONF_modules_unload(0);
Packit db01ca
        ERR_free_strings();
Packit db01ca
        EVP_cleanup();
Packit db01ca
        CRYPTO_cleanup_all_ex_data();
Packit db01ca
        /* The valgrind is showing possible memory leak by
Packit db01ca
         * SSL_COMP_get_compression_methods() call.
Packit db01ca
         *
Packit db01ca
         * Call to SSL_free_comp_methods() may resolve the memory leak.
Packit db01ca
         * But not able to find this call in the openssl 0.9.8e
Packit db01ca
         * TODO: Find whether its a real problem or not
Packit db01ca
         */
Packit db01ca
Packit db01ca
}
Packit db01ca
Packit db01ca
/**
Packit db01ca
 * oh_ssl_ctx_init
Packit db01ca
 *
Packit db01ca
 * Create a new SSL_CTX object as a framework for TLS/SSL enabled functions.
Packit db01ca
 * In particular:
Packit db01ca
 * - Creates a new CTX object with default option values
Packit db01ca
 * - Sets common compatibility options
Packit db01ca
 * - Sets the default locations for trusted CA certificates.
Packit db01ca
 *   SSL_CTX_set_default_verify_paths() is used to add system-wide default
Packit db01ca
 *   certificate paths to the verify CApath without having to specify a
Packit db01ca
 *   default location.  The intent is that the distribution's configured
Packit db01ca
 *   location will be used.
Packit db01ca
 *
Packit db01ca
 * Return value: pointer to SSL_CTX or NULL for failure
Packit db01ca
 **/
Packit db01ca
SSL_CTX         *oh_ssl_ctx_init()
Packit db01ca
{
Packit db01ca
        SSL_CTX         *ctx;
Packit db01ca
Packit db01ca
        ctx = SSL_CTX_new(SSLv23_client_method());
Packit db01ca
        if (ctx == NULL) {
Packit db01ca
                CRIT("SSL_CTX_new() failed");
Packit db01ca
                return(NULL);
Packit db01ca
        }
Packit db01ca
Packit db01ca
        SSL_CTX_set_options(ctx, SSL_OP_TLS_ROLLBACK_BUG | SSL_OP_ALL);
Packit db01ca
Packit db01ca
        if (! SSL_CTX_set_default_verify_paths(ctx)) {
Packit db01ca
                CRIT("SSL_CTX_set_default_verify_paths() failed");
Packit db01ca
                return(NULL);
Packit db01ca
        }
Packit db01ca
Packit db01ca
        return(ctx);
Packit db01ca
}
Packit db01ca
Packit db01ca
Packit db01ca
/**
Packit db01ca
 * oh_ssl_ctx_free
Packit db01ca
 * @ctx:        pointer to SSL_CTX as returned by oh_ssl_ctx_init()
Packit db01ca
 *
Packit db01ca
 * Free an SSL_CTX object
Packit db01ca
 *
Packit db01ca
 * Return value: 0 for success, -1 for failure
Packit db01ca
 **/
Packit db01ca
int             oh_ssl_ctx_free(SSL_CTX *ctx)
Packit db01ca
{
Packit db01ca
        if (ctx == NULL) {
Packit db01ca
                CRIT("unexpected NULL ctx pointer");
Packit db01ca
                return(-1);
Packit db01ca
        }
Packit db01ca
Packit db01ca
        SSL_CTX_free(ctx);
Packit db01ca
Packit db01ca
        return(0);
Packit db01ca
}
Packit db01ca
Packit db01ca
Packit db01ca
/**
Packit db01ca
 * oh_ssl_connect
Packit db01ca
 * @hostname:   Name of target host.  Format:
Packit db01ca
 *                  "hostname:port" or "IPaddress:port"
Packit db01ca
 * @ctx:        pointer to SSL_CTX as returned by oh_ssl_ctx_init()
Packit db01ca
 * @timeout:    maximum number of seconds to wait for a connection to
Packit db01ca
 *              hostname, or zero to wait forever
Packit db01ca
 *
Packit db01ca
 * Create and open a new ssl conection to the specified host.
Packit db01ca
 *
Packit db01ca
 * Return value: pointer to BIO, or NULL for failure
Packit db01ca
 **/
Packit db01ca
BIO             *oh_ssl_connect(char *hostname, SSL_CTX *ctx, long timeout)
Packit db01ca
{
Packit db01ca
        BIO             *bio;
Packit db01ca
        SSL             *ssl;
Packit db01ca
        int             err;
Packit db01ca
        int len, retval = 0;
Packit db01ca
        int RetVal, socket_desc = 0;
Packit db01ca
        char *Server = NULL;
Packit db01ca
        char *Port = NULL;
Packit db01ca
        struct addrinfo Hints, *AddrInfo = NULL, *ai = NULL;
Packit db01ca
Packit db01ca
        memset(&Hints, 0, sizeof(Hints));
Packit db01ca
        Hints.ai_family = AF_UNSPEC;
Packit db01ca
        Hints.ai_socktype = SOCK_STREAM;
Packit db01ca
        len = strlen(hostname);
Packit db01ca
Packit db01ca
        if (hostname == NULL) {
Packit db01ca
                CRIT("NULL hostname in oh_ssl_connect()");
Packit db01ca
                return(NULL);
Packit db01ca
        }
Packit db01ca
        if (ctx == NULL) {
Packit db01ca
                CRIT("NULL ctx in oh_ssl_connect()");
Packit db01ca
                return(NULL);
Packit db01ca
        }
Packit db01ca
        if (timeout < 0) {
Packit db01ca
                CRIT("inappropriate timeout in oh_ssl_connect()");
Packit db01ca
                return(NULL);
Packit db01ca
        }
Packit db01ca
Packit db01ca
        /* Allocate memory to a char pointer "Server" */
Packit db01ca
        Server = (char *) g_malloc0(sizeof(char) * len);
Packit db01ca
        if (Server == NULL){
Packit db01ca
                CRIT("out of memory");
Packit db01ca
                return NULL;
Packit db01ca
        }
Packit db01ca
        memset(Server, 0, len);
Packit db01ca
        /* hostname contains "Port" along with "IP Address". As, only
Packit db01ca
         * "IP Address" is needed for some of the below operations, so copy
Packit db01ca
         * "IP Address" from hostname to "Server".
Packit db01ca
         */
Packit db01ca
        strncpy(Server, hostname, (len - 4));
Packit db01ca
Packit db01ca
        /* Allocate memory to a char pointer "Port" */
Packit db01ca
        Port = (char *) g_malloc0(sizeof(char) * 4);
Packit db01ca
        if (Port == NULL){
Packit db01ca
                CRIT("out of memory");
Packit db01ca
                g_free(Server);
Packit db01ca
                return NULL;
Packit db01ca
        }
Packit db01ca
        /* As Port number is needed separately for some of the below
Packit db01ca
         * operations, so copy port number from hostname to "Port".
Packit db01ca
         */
Packit db01ca
        strncpy(Port, hostname + (len - 3), 3);
Packit db01ca
        
Packit db01ca
        /* Create socket address structure to prepare client socket */
Packit db01ca
        RetVal = getaddrinfo(Server, Port, &Hints, &AddrInfo);
Packit db01ca
        if (RetVal != 0) {
Packit db01ca
                CRIT("Cannot resolve address [%s] and port [%s],"
Packit db01ca
                     " error %d: %s",
Packit db01ca
                       Server, Port, RetVal, gai_strerror(RetVal));
Packit db01ca
                g_free(Server);
Packit db01ca
                g_free(Port);
Packit db01ca
                return NULL;
Packit db01ca
        }
Packit db01ca
        
Packit db01ca
        ai = AddrInfo;
Packit db01ca
        /* Create a socket point */
Packit db01ca
        socket_desc = socket(ai->ai_family, ai->ai_socktype,
Packit db01ca
                                            ai->ai_protocol);
Packit db01ca
        if (socket_desc == -1) {
Packit db01ca
                CRIT("Socket failed with error: %s", 
Packit db01ca
                      strerror(errno));
Packit db01ca
                g_free(Server);
Packit db01ca
                g_free(Port);
Packit db01ca
                freeaddrinfo(AddrInfo);	
Packit db01ca
                return NULL;
Packit db01ca
        }
Packit db01ca
Packit db01ca
        /* Now connect to target IP Address */
Packit db01ca
        retval = connect(socket_desc, ai->ai_addr, ai->ai_addrlen);
Packit db01ca
        if (retval != 0) {
Packit db01ca
                CRIT("Socket connect failed with error: %s",
Packit db01ca
                      strerror(errno));
Packit db01ca
                g_free(Server);
Packit db01ca
                g_free(Port);
Packit db01ca
                freeaddrinfo(AddrInfo);	
Packit db01ca
                close(socket_desc);
Packit db01ca
                return NULL;
Packit db01ca
        }
Packit db01ca
Packit db01ca
        /* Create new SSL structure for connection */
Packit db01ca
        ssl = SSL_new(ctx);
Packit db01ca
Packit db01ca
        /* Connect ssl object with a socket descriptor */
Packit db01ca
        SSL_set_fd(ssl, socket_desc);
Packit db01ca
Packit db01ca
        /* Initiate SSL connection */
Packit db01ca
        err = SSL_connect(ssl);
Packit db01ca
        if (err != 1) {
Packit db01ca
                CRIT("SSL connection failed");
Packit db01ca
                g_free(Server);
Packit db01ca
                g_free(Port);
Packit db01ca
                freeaddrinfo(AddrInfo);	
Packit db01ca
                SSL_free(ssl);
Packit db01ca
                close(socket_desc);
Packit db01ca
                return (NULL);
Packit db01ca
        }
Packit db01ca
Packit db01ca
        bio = BIO_new(BIO_f_ssl());             /* create an ssl BIO */
Packit db01ca
        BIO_set_ssl(bio, ssl, BIO_CLOSE);       /* assign the ssl BIO to SSL */
Packit db01ca
Packit db01ca
        /* TODO: Do I need to set the client or server mode here?  I don't
Packit db01ca
         * think so.
Packit db01ca
         */
Packit db01ca
Packit db01ca
        g_free(Server);
Packit db01ca
        g_free(Port);
Packit db01ca
        freeaddrinfo(AddrInfo);	
Packit db01ca
        return(bio);
Packit db01ca
}
Packit db01ca
Packit db01ca
Packit db01ca
/**
Packit db01ca
 * oh_ssl_disconnect
Packit db01ca
 * @bio:        pointer to a BIO as returned by oh_ssl_connect()
Packit db01ca
 * @shutdown:   Selects a uni-directional or bi-directional SSL shutdown.
Packit db01ca
 *              See the SSL_shutdown() man page.
Packit db01ca
 *
Packit db01ca
 * Close the SSL connection and free the memory associated with it.
Packit db01ca
 *
Packit db01ca
 * Return value: 0 for success, -1 for failure
Packit db01ca
 **/
Packit db01ca
int             oh_ssl_disconnect(BIO *bio, enum OH_SSL_SHUTDOWN_TYPE shutdown)
Packit db01ca
{
Packit db01ca
        SSL             *ssl;
Packit db01ca
        int             ret, fd;
Packit db01ca
Packit db01ca
        if (bio == NULL) {
Packit db01ca
                CRIT("NULL bio in oh_ssl_disconnect()");
Packit db01ca
                return(-1);
Packit db01ca
        }
Packit db01ca
Packit db01ca
        /* Shut down the SSL connection.  This may involve a handshake with
Packit db01ca
         * the server.
Packit db01ca
         */
Packit db01ca
        BIO_get_ssl(bio, &ssl;;
Packit db01ca
        if (ssl == NULL) {
Packit db01ca
                CRIT("BIO_get_ssl() failed");
Packit db01ca
                return(-1);
Packit db01ca
        }
Packit db01ca
        ret = SSL_shutdown(ssl);
Packit db01ca
        if (ret == -1) {
Packit db01ca
                CRIT("SSL_shutdown() failed");
Packit db01ca
                /* Continuing on to free BIO memory */
Packit db01ca
        }
Packit db01ca
        else if ((ret == 0) && (shutdown == OH_SSL_BI)) {
Packit db01ca
                /* Still need stage 2 shutdown (see SSL_shutdown() man page) */
Packit db01ca
                ret = SSL_shutdown(ssl);
Packit db01ca
                if (ret == -1) {
Packit db01ca
                        CRIT("SSL_shutdown() failed");
Packit db01ca
                        /* Continuing on to free BIO memory */
Packit db01ca
                }
Packit db01ca
                else if (ret == 0) {
Packit db01ca
                        CRIT("stage 2 of SSL_shutdown() failed");
Packit db01ca
                        /* Continuing on to free BIO memory */
Packit db01ca
                }
Packit db01ca
        }
Packit db01ca
        /* Close the socket */
Packit db01ca
        fd = SSL_get_fd(ssl);
Packit db01ca
        if (fd == -1) {
Packit db01ca
                CRIT("SSL_get_fd() failed");
Packit db01ca
                return(-1);
Packit db01ca
        }
Packit db01ca
        close(fd);
Packit db01ca
Packit db01ca
        /* Free the connection */
Packit db01ca
        BIO_free_all(bio);
Packit db01ca
Packit db01ca
        return(0);
Packit db01ca
}
Packit db01ca
Packit db01ca
Packit db01ca
/**
Packit db01ca
 * oh_ssl_read
Packit db01ca
 * @bio:        pointer to a BIO as returned by oh_ssl_connect()
Packit db01ca
 * @buf:        buffer for the data which is read from the connection
Packit db01ca
 * @size:       maximum number of bytes to be read into buf
Packit db01ca
 * @timeout:    maximum number of seconds to wait for input to be available,
Packit db01ca
 *              or zero to wait forever
Packit db01ca
 *
Packit db01ca
 * Read from an existing SSL connection.  The data and number of bytes read
Packit db01ca
 * are returned.
Packit db01ca
 *
Packit db01ca
 * Note that oh_ssl_read() and oh_ssl_write() have some subtle differences
Packit db01ca
 * in behavior.  While oh_ssl_write() will try to write all the bytes,
Packit db01ca
 * oh_ssl_read() will return as soon as it has read some data.
Packit db01ca
 *
Packit db01ca
 * Return value: (as follows)
Packit db01ca
 *   >0:        number of bytes read
Packit db01ca
 *    0:        nothing more to read; remote host closed the connection
Packit db01ca
 *   -1:        SSL or other error
Packit db01ca
 *   -2:        Timeout
Packit db01ca
 **/
Packit db01ca
int             oh_ssl_read(BIO *bio, char *buf, int size, long timeout)
Packit db01ca
{
Packit db01ca
        SSL             *ssl;
Packit db01ca
        int             bytes = 0;
Packit db01ca
        fd_set          readfds;
Packit db01ca
        fd_set          writefds;
Packit db01ca
        struct          timeval tv;
Packit db01ca
        int             read_wait;
Packit db01ca
        int             done;
Packit db01ca
        int             err;
Packit db01ca
        int             fd;
Packit db01ca
        int             e = 0;
Packit db01ca
Packit db01ca
        if (bio == NULL) {
Packit db01ca
                CRIT("NULL bio in oh_ssl_read()");
Packit db01ca
                return(-1);
Packit db01ca
        }
Packit db01ca
        if (buf == NULL) {
Packit db01ca
                CRIT("NULL buf in oh_ssl_read()");
Packit db01ca
                return(-1);
Packit db01ca
        }
Packit db01ca
        if (size <= 0) {
Packit db01ca
                CRIT("inappropriate size in oh_ssl_read()");
Packit db01ca
                return(-1);
Packit db01ca
        }
Packit db01ca
        if (timeout < 0) {
Packit db01ca
                CRIT("inappropriate timeout in oh_ssl_read()");
Packit db01ca
                return(-1);
Packit db01ca
        }
Packit db01ca
Packit db01ca
        /* Get underlying file descriptor, needed for select call */
Packit db01ca
        fd = BIO_get_fd(bio, NULL);
Packit db01ca
        if (fd == -1) {
Packit db01ca
                CRIT("BIO doesn't seem to be initialized in oh_ssl_read()");
Packit db01ca
                return(-1);
Packit db01ca
        }
Packit db01ca
Packit db01ca
        /* We also need the SSL connection pointer */
Packit db01ca
        BIO_get_ssl(bio, &ssl;;
Packit db01ca
        if (ssl == NULL) {
Packit db01ca
                CRIT("BIO_get_ssl() failed");
Packit db01ca
                return(-1);
Packit db01ca
        }
Packit db01ca
Packit db01ca
        /* Because of SSL renegotiations, we may have to wait on a socket
Packit db01ca
         * write even though we're trying to do a read.  The initial value
Packit db01ca
         * of read_wait indicates that we're trying to read, but it can be
Packit db01ca
         * set to 0 if we end up waiting for a socket write.
Packit db01ca
         */
Packit db01ca
        read_wait = 1;
Packit db01ca
        done = 0;
Packit db01ca
Packit db01ca
        /* We have to loop on the read call, until we get something we
Packit db01ca
         * can return to the user.
Packit db01ca
         */
Packit db01ca
        while (! done) {
Packit db01ca
                /* First, we need to wait until something happens on the
Packit db01ca
                 * underlying socket.  We are either waiting for a read
Packit db01ca
                 * or a write (but not both).
Packit db01ca
                 */
Packit db01ca
                FD_ZERO(&readfds);
Packit db01ca
                FD_ZERO(&writefds);
Packit db01ca
                if (read_wait) {
Packit db01ca
                        FD_SET(fd, &readfds);
Packit db01ca
                }
Packit db01ca
                else {
Packit db01ca
                        FD_SET(fd, &writefds);
Packit db01ca
                }
Packit db01ca
                if (timeout) {
Packit db01ca
                        tv.tv_sec = timeout;
Packit db01ca
                        tv.tv_usec = 0;
Packit db01ca
                        err = select(fd + 1, &readfds, &writefds, NULL, &tv;;
Packit db01ca
                }
Packit db01ca
                else {                  /* No timeout */
Packit db01ca
                        err = select(fd + 1, &readfds, &writefds, NULL, NULL);
Packit db01ca
                }
Packit db01ca
Packit db01ca
                /* Evaluate select() return code */
Packit db01ca
                if (err < 0) {
Packit db01ca
                        CRIT("error during select()");
Packit db01ca
                        return(-1);
Packit db01ca
                }
Packit db01ca
                if (err == 0) {
Packit db01ca
                        return(-2);     /* Timeout */
Packit db01ca
                }
Packit db01ca
Packit db01ca
                /* The socket has something.  Ready to try (or re-try)
Packit db01ca
                 * the read call.
Packit db01ca
                 */
Packit db01ca
                ERR_clear_error();
Packit db01ca
                bytes = SSL_read(ssl, buf, size);
Packit db01ca
                switch (SSL_get_error(ssl, bytes)) {
Packit db01ca
                        case SSL_ERROR_NONE:
Packit db01ca
                                /* No error */
Packit db01ca
                                if (bytes) {
Packit db01ca
                                        done = 1;
Packit db01ca
                                }
Packit db01ca
                                break;
Packit db01ca
                        case SSL_ERROR_ZERO_RETURN:
Packit db01ca
                                /* Connection was closed.  For this case,
Packit db01ca
                                 * since it's normal for the remote host
Packit db01ca
                                 * to close when it's done, we'll not signal
Packit db01ca
                                 * any error, but will return zero bytes.
Packit db01ca
                                 */
Packit db01ca
                                return(0);
Packit db01ca
                        case SSL_ERROR_WANT_READ:
Packit db01ca
                                read_wait = 1;
Packit db01ca
                                break;
Packit db01ca
                        case SSL_ERROR_WANT_WRITE:
Packit db01ca
                                read_wait = 0;
Packit db01ca
                                break;
Packit db01ca
                        case SSL_ERROR_SSL:
Packit db01ca
                                e = ERR_get_error();
Packit db01ca
                                CRIT("SSL_read reported error %s",
Packit db01ca
                                           ERR_error_string(e, NULL));
Packit db01ca
                                return(-1);
Packit db01ca
                        case SSL_ERROR_SYSCALL:
Packit db01ca
                                e = ERR_get_error();
Packit db01ca
                                if (bytes == 0 ) {
Packit db01ca
                                      CRIT("No bytes read");
Packit db01ca
                                } else if ( bytes == -1 ) {
Packit db01ca
                                      CRIT("Reading data error %s",
Packit db01ca
                                            strerror(errno));
Packit db01ca
                                } else {
Packit db01ca
                                      CRIT("SSL_read error %s",
Packit db01ca
                                                ERR_error_string(e, NULL));
Packit db01ca
                                } 
Packit db01ca
                                return(-1);
Packit db01ca
                        default:
Packit db01ca
                                /* Some other sort of error */
Packit db01ca
                                e = ERR_get_error();
Packit db01ca
                                CRIT("SSL_read reported error %s",
Packit db01ca
                                           ERR_error_string(e, NULL));
Packit db01ca
                                return(-1);
Packit db01ca
Packit db01ca
                }
Packit db01ca
        }
Packit db01ca
Packit db01ca
        return(bytes);
Packit db01ca
}
Packit db01ca
Packit db01ca
Packit db01ca
/**
Packit db01ca
 * oh_ssl_write
Packit db01ca
 * @bio:        pointer to a BIO as returned by oh_ssl_connect()
Packit db01ca
 * @buf:        buffer to write to the connection
Packit db01ca
 * @size:       number of bytes to be written
Packit db01ca
 * @timeout:    maximum number of seconds to wait for the remote host to
Packit db01ca
 *              accept the data, or zero to wait forever
Packit db01ca
 *
Packit db01ca
 * Write data to an existing SSL connection.
Packit db01ca
 *
Packit db01ca
 * Note that oh_ssl_read() and oh_ssl_write() have some subtle differences
Packit db01ca
 * in behavior.  While oh_ssl_read() returns as soon as it has data for the
Packit db01ca
 * caller, oh_ssl_write() does not return until all the bytes have been
Packit db01ca
 * written to the remote host.
Packit db01ca
 *
Packit db01ca
 * Return value: (as follows)
Packit db01ca
 *    0:        success
Packit db01ca
 *   -1:        error
Packit db01ca
 *   -2:        timeout
Packit db01ca
 **/
Packit db01ca
int             oh_ssl_write(BIO *bio, char *buf, int size, long timeout)
Packit db01ca
{
Packit db01ca
        SSL             *ssl;
Packit db01ca
        int             bytes;
Packit db01ca
        fd_set          readfds;
Packit db01ca
        fd_set          writefds;
Packit db01ca
        struct          timeval tv;
Packit db01ca
        int             write_wait;
Packit db01ca
        int             done;
Packit db01ca
        int             err;
Packit db01ca
        int             fd;
Packit db01ca
        int             sent;
Packit db01ca
        int             e = 0;
Packit db01ca
Packit db01ca
        if (bio == NULL) {
Packit db01ca
                CRIT("NULL bio in oh_ssl_write()");
Packit db01ca
                return(-1);
Packit db01ca
        }
Packit db01ca
        if (buf == NULL) {
Packit db01ca
                CRIT("NULL buf in oh_ssl_write()");
Packit db01ca
                return(-1);
Packit db01ca
        }
Packit db01ca
        if (size <= 0) {
Packit db01ca
                CRIT("inappropriate size in oh_ssl_write()");
Packit db01ca
                return(-1);
Packit db01ca
        }
Packit db01ca
        if (timeout < 0) {
Packit db01ca
                CRIT("inappropriate timeout in oh_ssl_write()");
Packit db01ca
                return(-1);
Packit db01ca
        }
Packit db01ca
Packit db01ca
        /* Get underlying file descriptor, needed for select call */
Packit db01ca
        fd = BIO_get_fd(bio, NULL);
Packit db01ca
        if (fd == -1) {
Packit db01ca
                CRIT("BIO doesn't seem to be initialized in oh_ssl_write()");
Packit db01ca
                return(-1);
Packit db01ca
        }
Packit db01ca
Packit db01ca
        /* We also need the SSL connection pointer */
Packit db01ca
        BIO_get_ssl(bio, &ssl;;
Packit db01ca
        if (ssl == NULL) {
Packit db01ca
                CRIT("BIO_get_ssl() failed");
Packit db01ca
                return(-1);
Packit db01ca
        }
Packit db01ca
Packit db01ca
        /* Because of SSL renegotiations, we may have to wait on a socket
Packit db01ca
         * read even though we're trying to do a write.  The initial value
Packit db01ca
         * of write_wait indicates that we're trying to write, but it can
Packit db01ca
         * be set to 0 if we end up waiting for a socket read.
Packit db01ca
         */
Packit db01ca
        write_wait = 1;
Packit db01ca
        done = 0;
Packit db01ca
        sent = 0;
Packit db01ca
Packit db01ca
        /* We have to loop on the write call, until everything gets written */
Packit db01ca
        while (! done) {
Packit db01ca
                /* First, we need to wait until something happens on the
Packit db01ca
                 * underlying socket.  We are either waiting for a read
Packit db01ca
                 * or a write (but not both).
Packit db01ca
                 */
Packit db01ca
                FD_ZERO(&readfds);
Packit db01ca
                FD_ZERO(&writefds);
Packit db01ca
                if (write_wait) {
Packit db01ca
                        FD_SET(fd, &writefds);
Packit db01ca
                }
Packit db01ca
                else {
Packit db01ca
                        FD_SET(fd, &readfds);
Packit db01ca
                }
Packit db01ca
                if (timeout) {
Packit db01ca
                        tv.tv_sec = timeout;
Packit db01ca
                        tv.tv_usec = 0;
Packit db01ca
                        err = select(fd + 1, &readfds, &writefds, NULL, &tv;;
Packit db01ca
                }
Packit db01ca
                else {                  /* No timeout */
Packit db01ca
                        err = select(fd + 1, &readfds, &writefds, NULL, NULL);
Packit db01ca
                }
Packit db01ca
Packit db01ca
                /* Evaluate select() return code */
Packit db01ca
                if (err < 0) {
Packit db01ca
                        CRIT("error during select()");
Packit db01ca
                        return(-1);
Packit db01ca
                }
Packit db01ca
                if (err == 0) {
Packit db01ca
                        return(-2);     /* Timeout */
Packit db01ca
                }
Packit db01ca
Packit db01ca
                /* The socket is ready.  Ready to try (or re-try) the write
Packit db01ca
                 * call.
Packit db01ca
                 */
Packit db01ca
                ERR_clear_error();
Packit db01ca
                bytes = SSL_write(ssl, buf + sent, size - sent);
Packit db01ca
                switch (SSL_get_error(ssl, bytes)) {
Packit db01ca
                        case SSL_ERROR_NONE:
Packit db01ca
                                /* No error */
Packit db01ca
                                sent += bytes;
Packit db01ca
                                if (sent == size) {
Packit db01ca
                                        done = 1;
Packit db01ca
                                }
Packit db01ca
                                break;
Packit db01ca
                        case SSL_ERROR_ZERO_RETURN:
Packit db01ca
                                /* Connection was closed.  Since we're trying
Packit db01ca
                                 * to write, this is an error condition.
Packit db01ca
                                 */
Packit db01ca
                                CRIT("remote host unexpectedly closed"
Packit db01ca
                                    " the connection");
Packit db01ca
                                return(-1);
Packit db01ca
                        case SSL_ERROR_WANT_READ:
Packit db01ca
                                write_wait = 0;
Packit db01ca
                                break;
Packit db01ca
                        case SSL_ERROR_WANT_WRITE:
Packit db01ca
                                write_wait = 1;
Packit db01ca
                                break;
Packit db01ca
                        case SSL_ERROR_SSL:
Packit db01ca
                                e = ERR_get_error();
Packit db01ca
                                CRIT("SSL_write reported error %s",
Packit db01ca
                                           ERR_error_string(e, NULL));
Packit db01ca
                                return(-1);
Packit db01ca
                        case SSL_ERROR_SYSCALL:
Packit db01ca
                                e = ERR_get_error();
Packit db01ca
                                if (bytes == 0 ) {
Packit db01ca
                                      CRIT("No bytes written");
Packit db01ca
                                } else if ( bytes == -1 ) {
Packit db01ca
                                      CRIT("Writing data error %s",
Packit db01ca
                                            strerror(errno));
Packit db01ca
                                } else {
Packit db01ca
                                      CRIT("SSL_write error %s",
Packit db01ca
                                                ERR_error_string(e, NULL));
Packit db01ca
                                } 
Packit db01ca
                                return(-1);
Packit db01ca
                        default:
Packit db01ca
                                /* Some other sort of error */
Packit db01ca
                                e = ERR_get_error();
Packit db01ca
                                CRIT("SSL_write reported error %s",
Packit db01ca
                                              ERR_error_string(e, NULL));
Packit db01ca
                                return(-1);
Packit db01ca
                }
Packit db01ca
        }
Packit db01ca
Packit db01ca
        return(0);
Packit db01ca
}