/**************************************************************************
*** COPYRIGHT (c) 2002 by TransNexus, Inc.                              ***
***                                                                     ***
*** This software is property of TransNexus, Inc.                       ***
*** This software is freely available under license from TransNexus.    ***
*** The license terms and conditions for free use of this software by   ***
*** third parties are defined in the OSP Toolkit Software License       ***
*** Agreement (LICENSE.txt).  Any use of this software by third         ***
*** parties, which does not comply with the terms and conditions of the ***
*** OSP Toolkit Software License Agreement is prohibited without        ***
*** the prior, express, written consent of TransNexus, Inc.             ***
***                                                                     ***
*** Thank you for using the OSP ToolKit(TM).  Please report any bugs,   ***
*** suggestions or feedback to support@transnexus.com                   ***
***                                                                     ***
**************************************************************************/

/* ospopenssl.c - SSL API bridge to openSSL library implementation (v0.9.4) */

#include "osp/osp.h"
#include "osp/ospsocket.h"
#include "osp/ospssl.h"
#include "osp/osputils.h"
#include "osp/ospcomm.h"
#include "osp/ospsecurity.h"

/* OpenSSL headers */
#include "openssl/bio.h"
#include "openssl/crypto.h"
#include "openssl/x509.h"
#include "openssl/ssl.h"
#include "openssl/err.h"
#include "openssl/rand.h"
#include "openssl/crypto.h"
#include "openssl/engine.h"

#define OSPC_MAX_CERT_BUFFER         4096

/*
** Local Prototype
*/

int OSPPSSLVerifyCallback(int ok, X509_STORE_CTX * ctx);
long bio_dump_cb(BIO * bio, int cmd, const char *argp, int argi, long argl, long ret);
int OSPPSSLLoadCerts(OSPTSEC *ospvRef);
int rand_init();
int cha_engine_init(OSPTBOOL hw_enabled);
void thread_setup(void);
unsigned long pthreads_thread_id(void);
#ifdef _WIN32
void win32_locking_callback(int mode, int type, const char *file, int line);
#else
void pthreads_locking_callback(int mode, int type, char *file, int line);
#endif
void thread_cleanup(void);

/*
 * BIO_stdout = File handle for output of SSL debugging
 */
BIO *bio_stdout = NULL;

/*
 * OSPC_DBG_SSL constant for debugging OpenSSL SSL session
 *  uncomment the define for ssl debugging
 *
#define OSPC_DBG_SSL 1
 */

int OSPPSSLWrapInit(
    void *ospvRef)
{
    int errorcode = OSPC_ERR_NO_ERROR, off = 0;
    SSL_CTX **ctx = OSPC_OSNULL;
    const SSL_METHOD *version = OSPC_OSNULL;
    OSPTSEC *security = OSPC_OSNULL;

    OSPM_DBGENTER(("ENTER: OSPPSSLWrapInit()\n"));

    security = (OSPTSEC *)ospvRef;

    if (security != OSPC_OSNULL) {
        /*
         * Openssl initialization has now been moved from here to the OSPPInit
         * function. It will be done only once now, rather than with every ProviderNew
         */
        ctx = (SSL_CTX **)&(security->ContextRef);
        version = TLSv1_client_method();
        *ctx = SSL_CTX_new(version);

        if (*ctx != OSPC_OSNULL) {
            SSL_CTX_set_options(*ctx, off);
            SSL_CTX_set_timeout(*ctx, OSPPSecGetSSLLifetime(security));
#ifdef OSP_SSL_VERIFY_NONE
            SSL_CTX_set_verify(*ctx, SSL_VERIFY_NONE, NULL);
#else
            SSL_CTX_set_verify(*ctx, SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE, OSPPSSLVerifyCallback);
#endif
        }

        /*
         * Initialize the CertMutex. This mutex is used to streamline access
         * to the LoadCerts function
         */
        OSPM_MUTEX_INIT(security->SSLCertMutex, OSPC_OSNULL, errorcode);
    }

    OSPM_DBGEXIT(("EXIT : OSPPSSLWrapInit() (%d)\n", errorcode));
    return errorcode;
}

void OSPPOpenSSLInit(
    OSPTBOOL hw_enabled)
{
    SSL_library_init();
    OpenSSL_add_all_algorithms();
    OpenSSL_add_all_digests();
    ERR_load_ERR_strings();
    ERR_load_crypto_strings();

    bio_stdout = BIO_new_fp(stdout, BIO_NOCLOSE);

    /*
     *  Seed random generator
     */
    rand_init();

    /*
     * Initialize openssl for multithreading
     */
    OSPPInitSSLMultiThread();

    /*
     *  Set Cryptographic Hardware Acceleration Engine
     */
    cha_engine_init(hw_enabled);

}

/*
 * Global params for openssl multi
 * thread initialization
 */
#ifdef _WIN32
static HANDLE *lock_cs;
#else
static OSPTMUTEX *lock_cs;
static long *lock_count;
#endif

#ifdef _WIN32
void thread_setup(void)
{
    int i;

    lock_cs = OPENSSL_malloc(CRYPTO_num_locks() * sizeof(HANDLE));
    for (i = 0; i < CRYPTO_num_locks(); i++) {
        lock_cs[i] = CreateMutex(NULL, FALSE, NULL);
    }

    CRYPTO_set_locking_callback((void (*)(int, int, const char *, int)) win32_locking_callback);
    /* id callback defined */
}

void thread_cleanup(void)
{
    int i;

    CRYPTO_set_locking_callback(NULL);
    for (i = 0; i < CRYPTO_num_locks(); i++)
        CloseHandle(lock_cs[i]);
    OPENSSL_free(lock_cs);
}

void win32_locking_callback(
    int mode,
    int type,
    const char *file,
    int line)
{
    if (mode & CRYPTO_LOCK) {
        WaitForSingleObject(lock_cs[type], INFINITE);
    } else {
        ReleaseMutex(lock_cs[type]);
    }
}

#else   /* Solaris and Linux */

void thread_setup(void)
{
    int i;
    int errcode = OSPC_ERR_NO_ERROR;

    lock_cs = OPENSSL_malloc(CRYPTO_num_locks() * sizeof(OSPTMUTEX));
    lock_count = OPENSSL_malloc(CRYPTO_num_locks() * sizeof(long));

    for (i = 0; i < CRYPTO_num_locks(); i++) {
        lock_count[i] = 0;
        OSPM_MUTEX_INIT(lock_cs[i], NULL, errcode);
    }

    CRYPTO_set_id_callback((unsigned long (*)()) pthreads_thread_id);
    CRYPTO_set_locking_callback((void (*)()) pthreads_locking_callback);
}

unsigned long pthreads_thread_id(void)
{
    unsigned long ret;

    ret = (unsigned long)OSPM_THR_SELF();

    return ret;
}

void pthreads_locking_callback(
    int mode,
    int type,
    char *file,
    int line)
{
    int errcode = OSPC_ERR_NO_ERROR;

    if (mode & CRYPTO_LOCK) {
        OSPM_MUTEX_LOCK(lock_cs[type], errcode);
        lock_count[type]++;
    } else {
        OSPM_MUTEX_UNLOCK(lock_cs[type], errcode);
    }
}

void thread_cleanup(void)
{
    int i;
    int errcode = OSPC_ERR_NO_ERROR;

    CRYPTO_set_locking_callback(NULL);
    for (i = 0; i < CRYPTO_num_locks(); i++) {
        OSPM_MUTEX_DESTROY(lock_cs[i], errcode);
    }
    if (lock_cs != NULL)
        OPENSSL_free(lock_cs);
    if (lock_count != NULL)
        OPENSSL_free(lock_count);

}
#endif

void OSPPInitSSLMultiThread(void)
{
    thread_setup();
}

void OSPPOpenSSLCleanUp(void)
{
    OSPPOpenSSLMultiThreadCleanUp();
}

void OSPPOpenSSLMultiThreadCleanUp(void)
{
    thread_cleanup();
}

void OSPPSSLWrapCleanup(
    void *ospvRef)
{
    SSL_CTX **ctx = OSPC_OSNULL;
    OSPTSEC *security = OSPC_OSNULL;
    int errcode = OSPC_ERR_NO_ERROR;

    OSPM_DBGENTER(("ENTER: OSPPSSLWrapCleanup()\n"));
    security = (OSPTSEC *)ospvRef;
    if (security != OSPC_OSNULL) {
        ctx = (SSL_CTX **)&(security->ContextRef);
        SSL_CTX_free(*ctx);
    }
    if (bio_stdout != OSPC_OSNULL) {
        BIO_free(bio_stdout);
        bio_stdout = OSPC_OSNULL;
    }
    /*
     * Destroy the CertMutex
     */
    OSPM_MUTEX_DESTROY(security->SSLCertMutex, errcode);
    OSPM_DBGEXIT(("EXIT : OSPPSSLWrapCleanup()\n"));
}

int OSPPSSLWrapSessionContextNew(
    void *ospvConnection,
    void *ospvContextRef)
{
    int errorcode = OSPC_ERR_NO_ERROR;
    SSL_CTX *ctx = OSPC_OSNULL;
    SSL **conref = OSPC_OSNULL;
    OSPTSEC *security = OSPC_OSNULL;
    OSPTSSLSESSION *sslsession = OSPC_OSNULL;

    OSPM_DBGENTER(("ENTER: OSPPSSLWrapSessionContextNew()\n"));

    OSPM_ARGUSED(ospvConnection);   /* not needed for SSLEAY */

    security = (OSPTSEC *)ospvContextRef;

    /*
     * Make sure we have security before we
     * start de-referencing
     */

    if (security != OSPC_OSNULL) {
        ctx = (SSL_CTX *)security->ContextRef;
        sslsession = ((OSPTHTTP *)ospvConnection)->SSLSession;

        conref = (SSL **)&(sslsession->Context);

        if (OSPPSSLLoadCerts(security) == OSPC_ERR_NO_ERROR) {
            *conref = (SSL *)SSL_new(ctx);

            if (*conref == OSPC_OSNULL) {
                ERR_print_errors(bio_stdout);
                errorcode = OSPC_ERR_SSL_MALLOC_FAILED;
                OSPM_DBGERRORLOG(errorcode, "malloc of new SSL Context failed");
            }
#ifdef OSPC_DBG_SSL
            (*conref)->debug = 1;
#endif
        }
    }

    OSPM_DBGEXIT(("EXIT : OSPPSSLWrapSessionContextNew() (%d)\n", errorcode));

    return errorcode;
}

int OSPPSSLWrapAttachConnection(
    OSPTSSLSESSION *ospvSSLSession,
    void *ospvConnection)
{
    int errorcode = OSPC_ERR_NO_ERROR;
    BIO *sbio = OSPC_OSNULL;
    SSL *conref = OSPC_OSNULL;

    OSPM_DBGENTER(("ENTER: OSPPSSLWrapAttachConnection()\n"));

    conref = (SSL *)OSPPSSLSessionGetContext(ospvSSLSession);

    sbio = BIO_new_socket(((OSPTHTTP *)ospvConnection)->SockFd, BIO_NOCLOSE);

    if (sbio != OSPC_OSNULL) {
        SSL_set_bio(conref, sbio, sbio);

#ifdef OSPC_DBG_SSL
        BIO_set_callback(sbio, bio_dump_cb);
        BIO_set_callback_arg(sbio, bio_stdout);
#endif

        SSL_set_connect_state(conref);
        SSL_set_bio(conref, sbio, sbio);
        SSL_set_connect_state(conref);
    } else {
        errorcode = OSPC_ERR_SSL_ATTACH_SOCK_FAILED;
    }

    OSPM_DBGEXIT(("EXIT : OSPPSSLWrapAttachConnection() (%d)\n", errorcode));
    return errorcode;
}

int OSPPSSLWrapHandshake(
    OSPTSSLSESSION *ospvSSLSession)
{
    int errorcode = OSPC_ERR_NO_ERROR;

    OSPM_DBGENTER(("ENTER: OSPPSSLWrapHandshake()\n"));
    OSPM_ARGUSED(ospvSSLSession);

    /*
     * automagically done by SSLeay
     */

    OSPM_DBGEXIT(("EXIT : OSPPSSLWrapHandshake() (%d)\n", errorcode));
    return errorcode;
}

int OSPPSSLWrapSessionContextDelete(
    OSPTSSLSESSION *ospvSSLSession)
{
    int errorcode = OSPC_ERR_NO_ERROR;
    SSL *conref = OSPC_OSNULL;

    OSPM_DBGENTER(("ENTER: OSPPSSLWrapSessionContextDelete()\n"));

    conref = (SSL *)OSPPSSLSessionGetContext(ospvSSLSession);
    SSL_free(conref);

    OSPM_DBGEXIT(("EXIT : OSPPSSLWrapSessionContextDelete()\n"));

    return errorcode;
}

int OSPPSSLWrapGetData(
    void *ospvBuffer,
    unsigned int *ospvLength,
    OSPTSSLSESSION *ospvSSLSession)
{
    int errcode = OSPC_ERR_NO_ERROR, expected = 0, sslerr = 0, bytesread = 0;
    SSL *conref = OSPC_OSNULL;

    OSPM_DBGENTER(("ENTER: OSPPSSLWrapGetData()\n"));
    conref = (SSL *)OSPPSSLSessionGetContext(ospvSSLSession);

    do {
        bytesread = SSL_read(conref, (char *)ospvBuffer + expected, *ospvLength - expected);
        expected += bytesread;
    } while (expected != (int) *ospvLength && bytesread > 0);

    if (expected != (int) *ospvLength) {
        sslerr = SSL_get_error(conref, bytesread);
        OSPM_DBGERRORLOG(sslerr, "SSL_read() failed, Connection Reset by Peer");
        errcode = OSPC_ERR_SSL_READ_FAILED;
    }

    OSPM_DBGEXIT(("EXIT : OSPPSSLWrapGetData() (%d)\n", errcode));

    return errcode;
}

int OSPPSSLWrapSendData(
    void *ospvBuffer,
    unsigned int *ospvLength,
    OSPTSSLSESSION *ospvSSLSession)
{
    int errorcode = OSPC_ERR_NO_ERROR, expected = 0, sslerr = 0, byteswritten = 0;
    SSL *conref = OSPC_OSNULL;

    OSPM_DBGENTER(("ENTER: OSPPSSLWrapSendData()\n"));

    conref = (SSL *)OSPPSSLSessionGetContext(ospvSSLSession);

    ERR_print_errors(bio_stdout);

    do {
        byteswritten = SSL_write(conref, (char *)ospvBuffer, *ospvLength);
        expected += byteswritten;
    } while (expected != (int) *ospvLength && byteswritten > 0);

    if (expected != (int) *ospvLength) {
        sslerr = SSL_get_error(conref, byteswritten);
        OSPM_PRINTF("SSLERROR: Error: %d\n", sslerr);
        OSPM_DBGERRORLOG(sslerr, "SSL_write() failed");
        errorcode = OSPC_ERR_SSL_WRITE_FAILED;
    }
    OSPM_DBGEXIT(("EXIT : OSPPSSLWrapSendData() (%d)\n", errorcode));
    return errorcode;
}

int OSPPSSLWrapSessionGracefulShutdown(
    OSPTSSLSESSION *ospvSSLSession)
{
    OSPM_DBGENTER(("ENTER: OSPPSSLWrapSessionGracefulShutdown()\n"));
    OSPM_ARGUSED(ospvSSLSession);
    OSPM_DBGEXIT(("EXIT : OSPPSSLWrapSessionGracefulShutdown() (%d)\n", 0));
    return OSPC_ERR_NO_ERROR;
}

int OSPPSSLWrapGetServerRootCACert(
    void **ospvRootCACert,
    int *ospvRootCACertLen,
    OSPTSSLSESSION *ospvSSLSession)
{
    OSPM_DBGENTER(("ENTER: OSPPSSLWrapGetServerRootCACert()\n"));

    OSPM_ARGUSED(ospvSSLSession);
    OSPM_ARGUSED(ospvRootCACert);
    OSPM_ARGUSED(ospvRootCACertLen);

    OSPM_DBGEXIT(("EXIT : OSPPSSLWrapGetServerRootCACert() (%d)\n", 0));
    return OSPC_ERR_NO_ERROR;

}

void OSPPSSLWrapFreeServerRootCACert(
    void **ospvRootCACert)
{
    OSPM_DBGENTER(("ENTER: OSPPSSLWrapFreeServerRootCACert()\n"));
    OSPM_ARGUSED(ospvRootCACert);
    OSPM_DBGEXIT(("EXIT : OSPPSSLWrapFreeServerRootCACert() (%d)\n", 0));
}

int OSPPSSLVerifyCallback(
    int ok,
    X509_STORE_CTX * ctx)
{
    int verify_depth = 1;

    char buf[256];
    X509 *err_cert;
    int err, depth;

    err_cert = X509_STORE_CTX_get_current_cert(ctx);
    err = X509_STORE_CTX_get_error(ctx);
    depth = X509_STORE_CTX_get_error_depth(ctx);

    X509_NAME_oneline(X509_get_subject_name(err_cert), buf, 256);
    OSPM_PRINTTOERR((stderr, "depth=%d %s\n", depth, buf));
    if (!ok) {
        OSPM_PRINTTOERR((stderr, "verify error:num=%d:%s\n", err, X509_verify_cert_error_string(err)));
        if (verify_depth >= depth || err == X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT) {
            ok = 1;
        } else {
            ok = 0;
        }
    }
    switch (ctx->error) {
    case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT:
        X509_NAME_oneline(X509_get_issuer_name(ctx->current_cert), buf, 256);
        BIO_printf(bio_stdout, "issuer= %s\n", buf);
        break;
    case X509_V_ERR_CERT_NOT_YET_VALID:
    case X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD:
        BIO_printf(bio_stdout, "notBefore=");
        ASN1_TIME_print(bio_stdout, X509_get_notBefore(ctx->current_cert));
        BIO_printf(bio_stdout, "\n");
        break;
    case X509_V_ERR_CERT_HAS_EXPIRED:
    case X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD:
        BIO_printf(bio_stdout, "notAfter=");
        ASN1_TIME_print(bio_stdout, X509_get_notAfter(ctx->current_cert));
        BIO_printf(bio_stdout, "\n");
        break;
    }
    OSPM_PRINTTOERR((stderr, "verify return:%d\n", ok));

    return ok;
}

long bio_dump_cb(
    BIO * bio,
    int cmd,
    const char *argp,
    int argi,
    long argl,
    long ret)
{
    BIO *out;

    out = (BIO *)BIO_get_callback_arg(bio);

    if (out == NULL)
        return ret;

    if (cmd == (BIO_CB_READ | BIO_CB_RETURN)) {
        BIO_printf(out, "read from %p [%p] (%d bytes => %ld (0x%lX))\n", bio, argp, argi, ret, ret);
        BIO_dump(out, argp, (int) ret);
        return ret;
    } else if (cmd == (BIO_CB_WRITE | BIO_CB_RETURN)) {
        BIO_printf(out, "write to %p [%p] (%d bytes => %ld (0x%lX))\n", bio, argp, argi, ret, ret);
        BIO_dump(out, argp, (int) ret);
    }

    return ret;
}

int OSPPSSLLoadCerts(
    OSPTSEC * security)
{
    unsigned count = 0, i = 0;
    unsigned certlen = 0;
    unsigned char *ca = OSPC_OSNULL, *pkey = OSPC_OSNULL, certbuf[OSPC_MAX_CERT_BUFFER] = "";
    X509 *x509 = OSPC_OSNULL;
    SSL_CTX **ctx = OSPC_OSNULL;
    int errorcode = OSPC_ERR_SEC_MODULE;
    int lock = OSPC_FALSE;

    OSPM_DBGENTER(("ENTER: OSPPSSLLoadCerts()\n"));
    /*
     * We need to streamline the access to this function.
     * If we don't, then while running multiple calls, two
     * http threads may try to overwrite the same providers context.
     * This was a bug in the toolkit which is fixed by the addition
     * of this mutex
     */
    OSPM_MUTEX_LOCK(security->SSLCertMutex, errorcode);

    /*
     * Make sure the Security Object is present, this object contains
     * the certificates (CA,LOCAL,PRIVATE)
     */

    if ((security != OSPC_OSNULL) && (errorcode == OSPC_ERR_NO_ERROR)) {
        lock = OSPC_TRUE;
        errorcode = OSPC_ERR_SEC_MODULE;
        if ((ctx = (SSL_CTX **)&(security->ContextRef)) != OSPC_OSNULL)
            if ((errorcode = OSPPSecGetNumberOfAuthorityCertificates(security, &count)) == OSPC_ERR_NO_ERROR) {
                if (count <= 0) {
                    errorcode = OSPC_ERR_SEC_NO_AUTHORITY_CERTIFICATES;
                    OSPM_DBGERRORLOG(errorcode, "There are no CA Certificates available");
                } else {
                    for (i = 0; i < count; i++) {
                        if (security->AuthorityCertInfo[i] != OSPC_OSNULL) {
                            if ((errorcode = OSPPX509CertGetCertificate(security->AuthorityCertInfo[i], &ca, &certlen)) == OSPC_ERR_NO_ERROR) {
                                if ((x509 = d2i_X509(NULL, (const unsigned char **)(&ca), certlen)) != OSPC_OSNULL) {
                                    SSL_CTX_add_client_CA(*ctx, x509);
                                    /* decrement reference count */
                                    X509_free(x509);
                                } else {
                                    errorcode = OSPC_ERR_SEC_NO_SPACE_FOR_CERTIFICATE;
                                    OSPM_DBGERRORLOG(errorcode, "Unable to create X509 certificate authority");
                                    ERR_print_errors(bio_stdout);
                                    break;
                                }
                            }
                        }
                    }
                }

                if (errorcode == OSPC_ERR_NO_ERROR) {
                    certlen = OSPC_MAX_CERT_BUFFER;
                    if ((errorcode = OSPPSecCopyLocalCertificate(security, &certlen, &certbuf[0])) != OSPC_ERR_NO_ERROR) {
                        errorcode = OSPC_ERR_SEC_LOCAL_CERTINFO_UNDEFINED;
                        OSPM_PRINTTOERR((stderr, "Unable to get Local Certificate\n"));
                    } else {
                        ca = &certbuf[0];
                        if ((x509 = d2i_X509(NULL, (const unsigned char **)(&ca), certlen)) != OSPC_OSNULL) {
                            if (SSL_CTX_use_certificate(*ctx, x509) > 0) {
                                /* decrement reference count */
                                X509_free(x509);

                                if (certlen > OSPC_MAX_CERT_BUFFER) {
                                    errorcode = OSPC_ERR_SEC_CERTIFICATE_TOO_BIG;
                                    OSPM_DBGERRORLOG(errorcode, "Private Key is too big");
                                } else {
                                    if ((errorcode = OSPPSecGetPrivateKeyData(security, &pkey, (unsigned int *)&certlen)) != OSPC_ERR_NO_ERROR) {
                                        errorcode = OSPC_ERR_SEC_PRIVATE_KEY_NOT_FOUND;
                                        OSPM_DBGERRORLOG(errorcode, "Unable to get private key");
                                    } else {
                                        if (!SSL_CTX_use_RSAPrivateKey_ASN1(*ctx, pkey, (long) certlen)) {
                                            errorcode = OSPC_ERR_SEC_NO_PRIVATE_KEY;
                                            OSPM_DBGERRORLOG(errorcode, "Unable to load RSAPrivate Key");
                                            ERR_print_errors(bio_stdout);
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
    } else {
        errorcode = OSPC_ERR_SEC_MODULE;
        OSPM_DBGERRORLOG(errorcode, "Security Context is not valid");
    }

    if (lock == OSPC_TRUE) {
        OSPM_MUTEX_UNLOCK(security->SSLCertMutex, errorcode);
    }
    OSPM_DBGEXIT(("EXIT : OSPPSSLLoadCerts() (%d)\n", 0));
    return 0;
}

/*
 *  Read from ".rnd" file located in the current directory and
 *  seed the PRNG.  If the file does not exist or is smaller
 *  than 128 bits (128 / 8 = 16 bytes) long, an error occures.
 */
int rand_init(void)
{
    int errorcode = OSPC_ERR_NO_ERROR;
    int bytesread = -1;

    OSPM_DBGENTER(("ENTER: rand_init()\n"));

    bytesread = RAND_load_file(".rnd", -1);

    if (bytesread < 16) {
        errorcode = OSPC_ERR_SEC_MODULE;
        OSPM_DBGERRORLOG(errorcode, "Random seed file does not exist or is to small");
    }

    OSPM_DBGEXIT(("EXIT : rand_init() (%d)\n", errorcode));

    return errorcode;
}

/*
 *  If compile time flag "hardware_accel" is set,
 *  iterate through all supported engines and set
 *  the first available one for all crypto operations
 */
int cha_engine_init(OSPTBOOL hw_enabled)
{
    int errorcode = OSPC_ERR_NO_ERROR;
    ENGINE *e = OSPC_OSNULL;

    if (hw_enabled) {
        OSPM_DBGENTER(("ENTER: cha_engine_init()\n"));

        ENGINE_load_builtin_engines();
        /*
         * Engine registration as implemented in the server.
         * Recreate ENGINE_register_all_complete_except for the DSA operation.
         * For some reason, DSA verification using the hardware does not work
         */
        for (e = ENGINE_get_first(); e; e = ENGINE_get_next(e)) {
            ENGINE_register_ciphers(e);
            ENGINE_register_digests(e);
            ENGINE_register_RSA(e);
            ENGINE_register_DH(e);
            ENGINE_register_RAND(e);
        }
    }
    OSPM_DBGEXIT(("EXIT : cha_engine_init() (%d)\n", errorcode));

    return errorcode;
}
