|
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 |
}
|