/* ====================================================================
 * Copyright (c) 1998 Ralf S. Engelschall. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer. 
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following
 *    disclaimer in the documentation and/or other materials
 *    provided with the distribution.
 *
 * 3. All advertising materials mentioning features or use of this
 *    software must display the following acknowledgment:
 *    "This product includes software developed by 
 *     Ralf S. Engelschall <rse@engelschall.com> for use in the
 *     mod_ssl project (http://www.engelschall.com/sw/mod_ssl/)."
 *
 * 4. The names "mod_ssl" must not be used to endorse or promote
 *    products derived from this software without prior written
 *    permission. For written permission, please contact
 *    rse@engelschall.com.
 *
 * 5. Products derived from this software may not be called "mod_ssl"
 *    nor may "mod_ssl" appear in their names without prior
 *    written permission of Ralf S. Engelschall.
 *
 * 6. Redistributions of any form whatsoever must retain the following
 *    acknowledgment:
 *    "This product includes software developed by 
 *     Ralf S. Engelschall <rse@engelschall.com> for use in the
 *     mod_ssl project (http://www.engelschall.com/sw/mod_ssl/)."
 *
 * THIS SOFTWARE IS PROVIDED BY RALF S. ENGELSCHALL ``AS IS'' AND ANY
 * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL RALF S. ENGELSCHALL OR
 * HIS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
 * OF THE POSSIBILITY OF SUCH DAMAGE.
 * ====================================================================
 *
 * This product includes software developed by Ben Laurie
 * for use in the Apache-SSL HTTP server project.
 */

/* ====================================================================
 * Copyright (c) 1995, 1996, 1997, 1998 Ben Laurie.  All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer. 
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 *
 * 3. All advertising materials mentioning features or use of this
 *    software must display the following acknowledgment:
 *    "This product includes software developed by Ben Laurie
 *    for use in the Apache-SSL HTTP server project."
 *
 * 4. The name "Apache-SSL Server" must not be used to
 *    endorse or promote products derived from this software without
 *    prior written permission.
 *
 * 5. Redistributions of any form whatsoever must retain the following
 *    acknowledgment:
 *    "This product includes software developed by Ben Laurie
 *    for use in the Apache-SSL HTTP server project."
 *
 * THIS SOFTWARE IS PROVIDED BY BEN LAURIE ``AS IS'' AND ANY
 * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL BEN LAURIE OR
 * HIS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
 * OF THE POSSIBILITY OF SUCH DAMAGE.
 * ====================================================================
 */

/*                      _             _ 
**  _ __ ___   ___   __| |    ___ ___| |  
** | '_ ` _ \ / _ \ / _` |   / __/ __| |  
** | | | | | | (_) | (_| |   \__ \__ \ | mod_ssl - Apache Interface to SSLeay
** |_| |_| |_|\___/ \__,_|___|___/___/_| http://www.engelschall.com/sw/mod_ssl/
**                      |_____|         
*/

/* OS headers */
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>

/* SSLeay headers */
#include <ssl.h>
#include <err.h>
#include <x509.h>
#include <pem.h>
#include <crypto.h>

/* Apache headers */
#include "httpd.h"
#include "http_config.h"
#include "http_conf_globals.h"
#include "http_log.h"

/* mod_ssl headers */
#include "mod_ssl.h"
#include "ssl_gcache.h"


/*  _________________________________________________________________
**
**  Global data
**  _________________________________________________________________
*/

static conn_rec   *g_pCurrentConnection  = NULL;
static const char *g_szCacheServerPort   = NULL;
static char       *g_szCacheServerPath   = NULL;
static server_rec *g_pServer             = NULL;
static pool       *g_pPermanentPool      = NULL;
static table      *g_pKeyTable           = NULL; 


/*  _________________________________________________________________
**
**  Apache API glue structures
**  _________________________________________________________________
*/

/* 
 *  the table of configuration directives we provide 
 */
static command_rec ssl_cmds[] = {
    { "SSLDisable", cmd_PolySetSlotTrue,
      (void *)XtOffsetOf(SSLConfigRec, bDisabled), RSRC_CONF, NO_ARGS,
      "Disable SSL protocol" },
    { "SSLEnable", cmd_PolySetSlotFalse,
      (void *)XtOffsetOf(SSLConfigRec, bDisabled), RSRC_CONF, NO_ARGS,
      "Enable SSL protocol" },
    { "SSLCertificateFile", cmd_PolySetSlotString,
      (void *)XtOffsetOf(SSLConfigRec, szCertificateFile), RSRC_CONF, TAKE1,
      "SSL Server Certificate file (PEM encoded)" },
    { "SSLCertificateKeyFile", cmd_PolySetSlotString,
      (void *)XtOffsetOf(SSLConfigRec, szKeyFile), RSRC_CONF, TAKE1,
      "SSL Server Certificate Private Key file (PEM encoded)"
      "(assumed to be SSLCertificateFile if absent)" },
    { "SSLCACertificatePath", cmd_PolySetSlotString,
      (void *)XtOffsetOf(SSLConfigRec, szCACertificatePath), RSRC_CONF, TAKE1,
      "SSL CA Certificate path [taken from SSL_CERT_DIR if absent]" },
    { "SSLCACertificateFile", cmd_PolySetSlotString,
      (void *)XtOffsetOf(SSLConfigRec, szCACertificateFile), RSRC_CONF, TAKE1,
      "SSL CA Certificate file (PEM encoded) [taken from SSL_CERT_FILE if absent]" },
    { "SSLVerifyDepth", cmd_PolySetSlotInt,
      (void *)XtOffsetOf(SSLConfigRec, nVerifyDepth), RSRC_CONF, TAKE1,
      "SSL Client verify depth (default 0)" },
    { "SSLVerifyClient", cmd_SSLVerifyClient,
      NULL, RSRC_CONF, TAKE1,
      "SSL Verify client type (none, optional, require, optional_no_ca)" },
    { "SSLFakeBasicAuth", cmd_PolySetSlotTrue,
      (void *)XtOffsetOf(SSLConfigRec, bFakeBasicAuth), RSRC_CONF, NO_ARGS,
      "Translate SSL client X509 certificate into a Basic Auth user name" },
    { "SSLLogFile", cmd_PolySetSlotString,
      (void *)XtOffsetOf(SSLConfigRec, szLogFile), RSRC_CONF, TAKE1,
      "SSL logfile for SSL-related messages" },
    { "SSLRequiredCiphers", cmd_PolySetSlotString,
      (void *)XtOffsetOf(SSLConfigRec, szReqCiphers), RSRC_CONF, TAKE1,
      "Colon-delimited list of required SSL Ciphers" },
    { "SSLRequireCipher", cmd_SSLRequireCipher,
      NULL, OR_FILEINFO, ITERATE,
      "Add a SSL Cipher to the per-directory list of required ciphers" },
    { "SSLBanCipher", cmd_SSLBanCipher, 
      NULL, OR_FILEINFO, ITERATE,
      "Add a SSL Cipher to the per-directory list of banned ciphers" },
    { "SSLRequireSSL", cmd_SSLRequireSSL, 
      NULL, OR_FILEINFO, NO_ARGS,
      "Require SSL (to protect directories that should not be served unencrypted)" },
    { "SSLCacheServerPath", cmd_SSLCacheServerPath, 
      NULL, RSRC_CONF, TAKE1,
      "SSL Session Cache server program path" },
    { "SSLCacheServerPort", cmd_SSLCacheServerPort, 
      NULL, RSRC_CONF, TAKE1,
      "SSL Session Cache server TCP port number" },
    { "SSLSessionCacheTimeout", cmd_PolySetSlotInt,
      (void *)XtOffsetOf(SSLConfigRec, tSessionCacheTimeout), RSRC_CONF, TAKE1,
      "SSL Session Cache object lifetime in seconds" },
    { NULL },
};

/* 
 *  the main Apache API config structure 
 */
module MODULE_VAR_EXPORT ssl_module = {
    STANDARD_MODULE_STUFF,
    init_Module,              /* module initializer                  */
    config_perdir_create,     /* create per-dir    config structures */
    config_perdir_merge,      /* merge  per-dir    config structures */
    config_server_create,     /* create per-server config structures */
    config_server_merge,      /* merge  per-server config structures */
    ssl_cmds,                 /* table of config file commands       */
    NULL,                     /* [#8] MIME-typed-dispatched handlers */
    NULL,                     /* [#1] URI to filename translation    */
    ssl_hook_Auth,            /* [#4] validate user id from request  */
    NULL,                     /* [#5] check if the user is ok _here_ */
    ssl_hook_Access,          /* [#2] check access by host address   */
    NULL,                     /* [#6] determine MIME type            */
    ssl_hook_Fixup,           /* [#7] pre-run fixups                 */
    NULL,                     /* [#9] log a transaction              */
    NULL,                     /* [#3] header parser                  */
    NULL,                     /* child_init                          */ 
    NULL,                     /* child_exit                          */ 
    NULL                      /* [#0] post read-request              */ 
};


/*  _________________________________________________________________
**
**  Configuration handling
**  _________________________________________________________________
*/

/*
 *  Create per-server SSL configuration
 */
static void *config_server_create(pool *p, server_rec *s)
{
    SSLConfigRec *rec = ap_pcalloc(p, sizeof(SSLConfigRec));

    rec->bDisabled            = UNSET;        /* Will become FALSE */
    rec->szCertificateFile    = NULL;
    rec->szKeyFile            = NULL;
    rec->szCACertificatePath  = NULL;
    rec->szCACertificateFile  = NULL;
    rec->szLogFile            = NULL;
    rec->szReqCiphers         = NULL;
    rec->nVerifyDepth         = 0;
    rec->nVerifyClient        = VERIFY_UNSET; /* Will become VERIFY_NONE */
    rec->bFakeBasicAuth       = UNSET;        /* Will become FALSE */
    rec->tSessionCacheTimeout = 0;

    rec->fileLogFile          = NULL;
    rec->px509Certificate     = NULL;
    rec->prsaKey              = NULL;
    rec->pSSLCtx              = NULL;

    return rec;
}

/*
 *  Merge per-server SSL configurations
 */
static void *config_server_merge(pool *p, void *basev, void *addv)
{
    SSLConfigRec *base = (SSLConfigRec *)basev;
    SSLConfigRec *add  = (SSLConfigRec *)addv;
    SSLConfigRec *new  = (SSLConfigRec *)ap_pcalloc(p, sizeof(SSLConfigRec));

    cfgMergeBool(bDisabled);
    cfgMergeString(szCertificateFile);
    cfgMergeString(szKeyFile);
    cfgMergeString(szCACertificatePath);
    cfgMergeString(szCACertificateFile);
    cfgMergeString(szLogFile);
    cfgMergeInt(nVerifyDepth);
    cfgMerge(nVerifyClient, VERIFY_UNSET);
    cfgMergeBool(bFakeBasicAuth);
    cfgMergeInt(tSessionCacheTimeout);

    cfgMerge(fileLogFile, NULL);
    cfgMerge(px509Certificate, NULL);
    cfgMerge(prsaKey, NULL);
    cfgMerge(pSSLCtx, NULL);

    return new;
}

/*
 *  Create per-directory SSL configuration
 */
static void *config_perdir_create(pool *p, char *dummy)
{
    SSLDirConfigRec *rec = ap_pcalloc(p, sizeof(SSLDirConfigRec));

    rec->tbRequiredCiphers = ap_make_table(p, 4);
    rec->tbBannedCiphers   = ap_make_table(p, 4);
    rec->bSSLRequired      = FALSE;

    return rec;
}

/*
 *  Merge per-directory SSL configurations
 */
static void *config_perdir_merge(pool *p, void *basev, void *addv)
{
    SSLDirConfigRec *base = (SSLDirConfigRec *)basev;
    SSLDirConfigRec *add  = (SSLDirConfigRec *)addv;
    SSLDirConfigRec *new  = (SSLDirConfigRec *)ap_palloc(p, 
                                               sizeof(SSLDirConfigRec));

    cfgMergeTable(tbRequiredCiphers);
    cfgMergeTable(tbBannedCiphers);
    cfgMerge(bSSLRequired, FALSE);

    return new;
}

/*
 *  Polymorphic functions for setting config slots
 */

static const char *cmd_PolySetSlotString(
    cmd_parms *cmd, char *struct_ptr, char *arg)
{
    char *pConfig = (char *)mySrvConfig(cmd->server);

    int offset = (int)cmd->info;
    *(char **)(pConfig + offset) = arg;
    return NULL;
}

static const char *cmd_PolySetSlotInt(
    cmd_parms *cmd, char *struct_ptr, char *arg)
{
    char *pConfig = (char *)mySrvConfig(cmd->server);

    int offset = (int)cmd->info;
    *(int *)(pConfig + offset) = atoi(arg);
    return NULL;
}

static const char *cmd_PolySetSlotTrue(
    cmd_parms *cmd, char *struct_ptr)
{
    char *pConfig = (char *)mySrvConfig(cmd->server);

    int offset = (int)cmd->info;
    *(BOOL *)(pConfig + offset) = TRUE;
    return NULL;
}

static const char *cmd_PolySetSlotFalse(
    cmd_parms *cmd, char *struct_ptr)
{
    char *pConfig = (char *)mySrvConfig(cmd->server);

    int offset = (int)cmd->info;
    *(BOOL *)(pConfig + offset) = FALSE;
    return NULL;
}

/*
 *  Configuration functions for particular directives
 */

static const char *cmd_SSLRequireCipher(
    cmd_parms *cmd, SSLDirConfigRec *rec, char *cipher)
{
    ap_table_set(rec->tbRequiredCiphers, cipher, "Required");
    return NULL;
}

static const char *cmd_SSLBanCipher(
    cmd_parms *cmd, SSLDirConfigRec * rec, char *cipher)
{
    ap_table_set(rec->tbBannedCiphers, cipher, "Banned");
    return NULL;
}

static const char *cmd_SSLRequireSSL(
    cmd_parms *cmd, SSLDirConfigRec * rec, char *cipher)
{
    rec->bSSLRequired = TRUE;
    return NULL;
}

static const char *cmd_SSLCacheServerPort(
    cmd_parms *cmd, char *struct_ptr, char *arg)
{
    if (strspn(arg, "0123456789") == strlen(arg))
        g_szCacheServerPort = arg;
    else
        g_szCacheServerPort = ap_server_root_relative(cmd->pool, arg);
    InitGlobalCache(g_szCacheServerPort);
    return NULL;
}

static const char *cmd_SSLCacheServerPath(
    cmd_parms *cmd, char *struct_ptr, char *arg)
{
    g_szCacheServerPath = ap_server_root_relative(cmd->pool, arg);
    return NULL;
}

static const char *cmd_SSLVerifyClient(
    cmd_parms *cmd, char *struct_ptr, char *level)
{
    SSLConfigRec *pConfig = mySrvConfig(cmd->server);

    if (strEQ(level, "0") || strcEQ(level, "none"))
        pConfig->nVerifyClient = VERIFY_NONE;
    else if (strEQ(level, "1") || strcEQ(level, "optional"))
        pConfig->nVerifyClient = VERIFY_OPTIONAL;
    else if (strEQ(level, "2") || strcEQ(level, "require"))
        pConfig->nVerifyClient = VERIFY_REQUIRE;
    else if (strEQ(level, "3") || strcEQ(level, "optional_no_ca"))
        pConfig->nVerifyClient = VERIFY_OPTIONAL_NO_CA;
    else
        return "SSLVerifyClient: Invalid argument";
    return NULL;
}


/*  _________________________________________________________________
**
**  Module Initialization
**  _________________________________________________________________
*/

static struct {
    char *szName;
    int nKeySize;
    int nSecretKeySize;
} aCipherspecs[] = {
#if SSLEAY_VERSION_NUMBER >= 0x0900
    { SSL3_TXT_RSA_IDEA_128_SHA          /*IDEA-CBC-SHA*/,           128, 128 },
#endif
    { SSL3_TXT_RSA_NULL_MD5              /*NULL-MD5*/,                 0,   0 },
    { SSL3_TXT_RSA_NULL_SHA              /*NULL-SHA*/,                 0,   0 },
    { SSL3_TXT_RSA_RC4_40_MD5            /*EXP-RC4-MD5*/,            128,  40 },
    { SSL3_TXT_RSA_RC4_128_MD5           /*RC4-MD5*/,                128, 128 },
    { SSL3_TXT_RSA_RC4_128_SHA           /*RC4-SHA*/,                128, 128 },
    { SSL3_TXT_RSA_RC2_40_MD5            /*EXP-RC2-CBC-MD5*/,        128,  40 },
    { SSL3_TXT_RSA_IDEA_128_SHA          /*IDEA-CBC-MD5*/,           128, 128 },
    { SSL3_TXT_RSA_DES_40_CBC_SHA        /*EXP-DES-CBC-SHA*/,         56,  40 },
    { SSL3_TXT_RSA_DES_64_CBC_SHA        /*DES-CBC-SHA*/ ,            56,  56 },
    { SSL3_TXT_RSA_DES_192_CBC3_SHA      /*DES-CBC3-SHA*/ ,          168, 168 },
    { SSL3_TXT_DH_DSS_DES_40_CBC_SHA     /*EXP-DH-DSS-DES-CBC-SHA*/,  56,  40 },
    { SSL3_TXT_DH_DSS_DES_64_CBC_SHA     /*DH-DSS-DES-CBC-SHA*/,      56,  56 },
    { SSL3_TXT_DH_DSS_DES_192_CBC3_SHA   /*DH-DSS-DES-CBC3-SHA*/,    168, 168 },
    { SSL3_TXT_DH_RSA_DES_40_CBC_SHA     /*EXP-DH-RSA-DES-CBC-SHA*/,  56,  40 },
    { SSL3_TXT_DH_RSA_DES_64_CBC_SHA     /*DH-RSA-DES-CBC-SHA*/,      56,  56 },
    { SSL3_TXT_DH_RSA_DES_192_CBC3_SHA   /*DH-RSA-DES-CBC3-SHA*/,    168, 168 },
    { SSL3_TXT_EDH_DSS_DES_40_CBC_SHA    /*EXP-EDH-DSS-DES-CBC-SHA*/, 56,  40 },
    { SSL3_TXT_EDH_DSS_DES_64_CBC_SHA    /*EDH-DSS-DES-CBC-SHA*/,     56,  56 },
    { SSL3_TXT_EDH_DSS_DES_192_CBC3_SHA  /*EDH-DSS-DES-CBC3-SHA*/,   168, 168 },
    { SSL3_TXT_EDH_RSA_DES_40_CBC_SHA    /*EXP-EDH-RSA-DES-CBC*/,     56,  40 },
    { SSL3_TXT_EDH_RSA_DES_64_CBC_SHA    /*EDH-RSA-DES-CBC-SHA*/,     56,  56 },
    { SSL3_TXT_EDH_RSA_DES_192_CBC3_SHA  /*EDH-RSA-DES-CBC3-SHA*/,   168, 168 },
    { SSL3_TXT_ADH_RC4_40_MD5            /*EXP-ADH-RC4-MD5*/,        128,  40 },
    { SSL3_TXT_ADH_RC4_128_MD5           /*ADH-RC4-MD5*/,            128, 128 },
    { SSL3_TXT_ADH_DES_40_CBC_SHA        /*EXP-ADH-DES-CBC-SHA*/,    128,  40 },
    { SSL3_TXT_ADH_DES_64_CBC_SHA        /*ADH-DES-CBC-SHA*/,         56,  56 },
    { SSL3_TXT_ADH_DES_192_CBC_SHA       /*ADH-DES-CBC3-SHA*/,       168, 168 },
    { SSL3_TXT_FZA_DMS_NULL_SHA          /*FZA-NULL-SHA*/,             0,   0 },
    { SSL3_TXT_FZA_DMS_FZA_SHA           /*FZA-FZA-CBC-SHA*/,         -1,  -1 },
    { SSL3_TXT_FZA_DMS_RC4_SHA           /*FZA-RC4-SHA*/,            128, 128 },
    { SSL2_TXT_DES_64_CFB64_WITH_MD5_1   /*DES-CFB-M1*/,              56,  56 },
    { SSL2_TXT_RC2_128_CBC_WITH_MD5      /*RC2-CBC-MD5*/,            128, 128 },
    { SSL2_TXT_DES_64_CBC_WITH_MD5       /*DES-CBC-MD5*/,             56,  56 },
    { SSL2_TXT_DES_192_EDE3_CBC_WITH_MD5 /*DES-CBC3-MD5*/,           168, 168 },
    { SSL2_TXT_RC4_64_WITH_MD5           /*RC4-64-MD5*/,              64,  64 },
    { SSL2_TXT_NULL                      /*NULL*/,                     0,   0 },
    { NULL,                                                            0,   0 }
};

/*
 *  Per-module initialization
 */
static void init_Module(server_rec *s, pool *p)
{
    char *szLogFile;
    char buf[MAX_STRING_LEN];
    server_rec *pMainServer = s;
    piped_log *pl;
    char *cp;
    char *cp2;
    int nSaveStderr;

    /*
     * Create our own global memory pool
     * which survives server restarts
     */
    if (g_pPermanentPool == NULL)
        g_pPermanentPool = ap_make_sub_pool(NULL);

    /*
     * Reconnect stderr to terminal (=stdout)
     */
    nSaveStderr = dup(STDERR_FILENO);
    close(STDERR_FILENO);
    (void)dup(STDOUT_FILENO);

    /*
     *  make sure the required SSL session cache is configured
     */
    if (!g_szCacheServerPort) {
        ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, s,
                     "mod_ssl: Required SSLCacheServerPort missing");
        ap_clear_pool(p);
        exit(1);
    }
    if (!g_szCacheServerPath) {
        ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, s,
                     "mod_ssl: Required SSLCacheServerPath missing");
        ap_clear_pool(p);
        exit(1);
    }

    /*
     *  initialize SSLeay
     */
    init_SSLeay(s, p);

    /*
     *  initialize servers
     */
    for (; s != NULL; s = s->next) {
        SSLConfigRec *pConfig = mySrvConfig(s);

        /* Fix up stuff that may not have been set */
        if (pConfig->bDisabled == UNSET)
            pConfig->bDisabled = FALSE;
        if (pConfig->nVerifyClient == VERIFY_UNSET)
            pConfig->nVerifyClient = VERIFY_NONE;
        if (pConfig->bFakeBasicAuth == UNSET)
            pConfig->bFakeBasicAuth = FALSE;

        /* If we are disabled skip this server but
           make sure the port is initialized correctly */
        if (pConfig->bDisabled) {
            if (!s->port)
                s->port = DEFAULT_HTTP_PORT;
            continue;
        }
        else {
            if (!s->port)
                s->port = DEFAULT_HTTPS_PORT;
        }

        /* 
         * Now check for important parameters and the
         * possibility that the user forgot to set them.
         */
        if (!pConfig->tSessionCacheTimeout) {
            ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, s,
                         "mod_ssl: No SSL Session Cache Timeout set [hint: SSLSessionCacheTimeout]");
            ap_clear_pool(p);
            exit(1);
        }
        if (!pConfig->szCertificateFile) {
            ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, s,
                         "mod_ssl: No SSL Certificate set for server %s:%d [hint: SSLCertificateFile]",
                         s->server_hostname, s->port);
            ap_clear_pool(p);
            exit(1);
        }
        if (!pConfig->szLogFile) {
            ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, s,
                         "mod_ssl: No SSL logfile set [hint: SSLLogFile]");
            ap_clear_pool(p);
            exit(1);
        }

        /*
         * Open the SSL logfile
         */
        if (pConfig->szLogFile != NULL) {
            if (strNE(pConfig->szLogFile, "/dev/null")) {
                if (pConfig->szLogFile[0] == '|') {
                    szLogFile = ap_server_root_relative(p, pConfig->szLogFile+1);
                    if ((pl = ap_open_piped_log(p, szLogFile)) == NULL) {
                        ap_log_error(APLOG_MARK, APLOG_ERR, s,
                                     "mod_ssl: Cannot open reliable pipe to SSL logfile filter %s",
                                     szLogFile);
                        ap_clear_pool(p);
                        exit(1);
                    }
                    pConfig->fileLogFile = ap_pfdopen(p, ap_piped_log_write_fd(pl), "a");
                    setbuf(pConfig->fileLogFile, NULL);
                }
                else {
                    szLogFile = ap_server_root_relative(p, pConfig->szLogFile);
                    if ((pConfig->fileLogFile = ap_pfopen(p, szLogFile, "a")) == NULL) {
                        ap_log_error(APLOG_MARK, APLOG_ERR, s,
                                     "mod_ssl: Cannot open SSL logfile %s",
                                     szLogFile);
                        ap_clear_pool(p);
                        exit(1);
                    }
                    setbuf(pConfig->fileLogFile, NULL);
                }
            }
        }

        /*
         * Read the server certificate and key
         */
        init_GetCertAndKey(s, p, pConfig);
    }

    /*
     *  launch SSL session cache process
     */
    LaunchCache(pMainServer, p);

    /*
     *  Reconnect stderr to error log
     */
    close(STDERR_FILENO);
    (void)dup(nSaveStderr);

    /*
     *  Announce mod_ssl and SSLeay in HTTP Server field
     *  as ``mod_ssl/X.X.X SSLeay/X.X.X''
     */
    ap_snprintf(buf, sizeof(buf), "mod_ssl/%s", MOD_SSL_VERSION);
    ap_add_version_component(buf);
    cp = __SSLeay SSLeay_version(SSLEAY_VERSION);
    for (; !isdigit(*cp); cp++);
    ap_cpystrn(buf, "SSLeay/", sizeof(buf));
    for (cp2 = buf + strlen(buf); *cp != ' ' && *cp != NUL;)
        *cp2++ = *cp++;
    *cp2 = NUL;
    ap_add_version_component(ap_pstrdup(p, buf));

    return;
}

/*
 *  Initialize SSLeay
 */
static void init_SSLeay(server_rec *s, pool *p)
{
    /*
     *  Fire up the SSLeay internals 
     */
    __SSLeay SSL_load_error_strings();
    __SSLeay SSLeay_add_ssl_algorithms();

    return;
}

/*
 * Read the SSL Server Certificate and Key
 */
static void init_GetCertAndKey(server_rec *s, pool *p, SSLConfigRec *pConfig)
{
    FILE *f;
    char szPath[MAX_STRING_LEN];

    /*  
     *  Check for problematic re-initializations
     */
    if (pConfig->px509Certificate) {
        ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, s,
                     "mod_ssl: Illegal attempt to re-initialise SSL for server %s",
                     s->server_hostname);
        ap_clear_pool(p);
        exit(1);
    }

    /*
     *  ???
     */
    /* printf("Reading certificate and key for server %s:%d\n",
               s->server_hostname, s->port); */
    pConfig->pSSLCtx = __SSLeay SSL_CTX_new(__SSLeay SSLv23_server_method());
    __SSLeay SSL_CTX_set_verify(pConfig->pSSLCtx, 
                                init_VerifyFlags(pConfig), 
                                ssl_callback_SSLVerify);
    __SSLeay SSL_CTX_sess_set_new_cb(pConfig->pSSLCtx, ssl_callback_NewSessionCache);
    __SSLeay SSL_CTX_sess_set_get_cb(pConfig->pSSLCtx, ssl_callback_GetSessionCache);

    /*
     *  Configure required SSL Ciphers 
     */
    if (pConfig->szReqCiphers != NULL) {
        if (!__SSLeay SSL_CTX_set_cipher_list(pConfig->pSSLCtx, pConfig->szReqCiphers)) {
            ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, s,
                         "mod_ssl: Unable to configure required SSL ciphers (SSLeay error follows)");
            ssl_log_ssleay2apache(s);
            ap_clear_pool(p);
            exit(1);
        }
    }

    /*
     * Configure SSL Client verification
     */
    if (   ( (   pConfig->szCACertificateFile 
              || pConfig->szCACertificatePath)
           && !__SSLeay SSL_CTX_load_verify_locations(pConfig->pSSLCtx,
                                                      pConfig->szCACertificateFile,
                                                      pConfig->szCACertificatePath) )
        || !__SSLeay SSL_CTX_set_default_verify_paths(pConfig->pSSLCtx) ) {
        ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, s,
                     "mod_ssl: Error setting SSL verify locations (SSLeay error follows)");
        ssl_log_ssleay2apache(s);
        ap_clear_pool(p);
        exit(1);
    }

    /*
     * Read server certificate
     */
    if ((f = ap_pfopen(p, pConfig->szCertificateFile, "r")) == NULL) {
        ap_snprintf(szPath, sizeof(szPath), "%s/%s", 
                    __SSLeay X509_get_default_cert_dir(),
                    pConfig->szCertificateFile);
        if ((f = ap_pfopen(p, szPath, "r")) == NULL) {
            ap_log_error(APLOG_MARK, APLOG_ERR, s,
                         "mod_ssl: Can't open SSL server certificate file %s, nor %s",
                         pConfig->szCertificateFile, szPath);
            ap_clear_pool(p);
            exit(1);
        }
    }
    else {
        /* in case it also contains the key */
        ap_cpystrn(szPath, pConfig->szCertificateFile, sizeof(szPath));
    }
    pConfig->px509Certificate = __SSLeay X509_new();
    if (!__SSLeay PEM_read_X509(f, &pConfig->px509Certificate, NULL)) {
        ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, s,
                     "mod_ssl: Error reading SSL server certificate file %s "
                     "(SSLeay error follows)", szPath);
        ssl_log_ssleay2apache(s);
        ap_clear_pool(p);
        exit(1);
    }
    ap_pfclose(p, f);

    /*
     * Read server private key 
     * REMEMBER: Here SSLeay perhaps opens an interactive console dialog!!
     */
    if (pConfig->szKeyFile)
        if (*pConfig->szKeyFile == '/')
            ap_cpystrn(szPath, pConfig->szKeyFile, sizeof(szPath));
        else
            ap_snprintf(szPath, sizeof(szPath), "%s/private/%s", 
                        __SSLeay X509_get_default_cert_area(), 
                        pConfig->szKeyFile);
    /* Otherwise the path already contains the name of the certificate file */
    init_GetPrivateKey(s, p, pConfig, szPath);

    /*
     * ???
     */
    __SSLeay SSL_CTX_set_tmp_rsa_callback(pConfig->pSSLCtx, ssl_callback_TmpRSA);

    /* 
     *  Really this should have its own directive and list, but I haven't got
     *  the time at the moment. --ben ???
     */
    if (pConfig->szCACertificateFile)
        __SSLeay SSL_CTX_set_client_CA_list(pConfig->pSSLCtx,
            __SSLeay SSL_load_client_CA_file(
                pConfig->szCACertificateFile));
}

static void init_GetPrivateKey(server_rec *s, pool *p, SSLConfigRec *pConfig,
                               const char *szKeyFile)
{
    RSA *prsaKey;
    FILE *fp;

    if (g_pKeyTable == NULL) {
        if ((g_pKeyTable = ap_make_table(g_pPermanentPool, 10)) == NULL) {
            ap_log_error(APLOG_MARK,APLOG_CRIT, s, 
                         "mod_ssl: Can't allocate table for private keys");
            ap_clear_pool(p);
            exit(1);
        }
    }
    prsaKey = (RSA *)ap_table_get(g_pKeyTable, szKeyFile);
    if (prsaKey != NULL) {
        pConfig->prsaKey = prsaKey;
        return;
    }
    if ((fp = ap_pfopen(p, szKeyFile, "r")) == NULL) {
        ap_log_error(APLOG_MARK,APLOG_CRIT, s,
                     "mod_ssl: Can't open SSL server private key file %s",
                     szKeyFile);
        ap_clear_pool(p);
        exit(1);
    }

    pConfig->prsaKey = RSA_new();
    if (!__SSLeay PEM_read_RSAPrivateKey(fp, &pConfig->prsaKey, NULL)) {
        ap_log_error(APLOG_MARK, APLOG_CRIT, s,
                    "mod_ssl: Failed to read private key file %s", szKeyFile);
        ssl_log_ssleay2apache(s);
        ap_clear_pool(p);
        exit(1);
    }
    ap_pfclose(p, fp);

    ap_table_setn(g_pKeyTable, 
                  ap_pstrdup(g_pPermanentPool, szKeyFile),
                  (const char *)pConfig->prsaKey);
    return;
}

static int init_VerifyFlags(SSLConfigRec *pConfig)
{
    int nVerify = 0;

    switch (pConfig->nVerifyClient) {
    case VERIFY_REQUIRE:
        nVerify |= SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT;
        break;
    case VERIFY_OPTIONAL:
    case VERIFY_OPTIONAL_NO_CA:
        nVerify |= SSL_VERIFY_PEER;
        break;
    default:
        break;
    }
    return nVerify;
}


/*  _________________________________________________________________
**
**  Apache API Handlers
**  _________________________________________________________________
*/

/*
 *  Connect Handler:
 *  Connect SSL to the accepted socket
 *
 *  Usually we would need an Apache API hook which is triggered right after
 *  the socket is accepted for handling a new request. But Apache 1.3 doesn't
 *  provide such a hook, so we have to patch http_main.c and call this
 *  function directly.
 */
/* NON STATIC */ int SSL_HOOK_SetupConnection(conn_rec *conn)
{
    server_rec *srvr = conn->server;
    BUFF *fb = conn->client;
    SSLConfigRec *pConfig = mySrvConfig(srvr);

    /*
     * Immediately stop processing if SSL is disabled for
     * this connection
     */
    if (pConfig->bDisabled) {
        fb->ssl = NULL;
        return TRUE;
    }

    /*
     * Remember the connection information for
     * later access inside callback functions
     */
    g_pCurrentConnection = conn;

    /*
     * Create a new SSL connection with the configured server SSL context and
     * attach this to the socket. Additionally we can register this attachment
     * so we can detach later.
     */
    fb->ssl = __SSLeay SSL_new(pConfig->pSSLCtx);
    __SSLeay SSL_set_fd(fb->ssl, fb->fd);
    ap_register_cleanup(conn->pool, (void *)conn, 
                        SSL_HOOK_CloseConnection, SSL_HOOK_CloseConnection);

    /*
     * Now SSL verify the connection
     */
    ssl_int_SetupVerify(conn);
    while (!__SSLeay SSL_is_init_finished(fb->ssl)) {

        int ret = __SSLeay SSL_accept(fb->ssl);

        if (ret <= 0) {
            ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, srvr,
                         "mod_ssl: SSL_accept failed");
            ssl_log_ssleay2apache(srvr);
            fb->flags |= B_EOF|B_EOUT;
            return FALSE;
        }

        if (conn->client->nVerifyError != X509_V_OK) {
            ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, conn->server,
                         "mod_ssl: Verification failed");
            ssl_log_ssleay2apache(srvr);
            fb->flags |= B_EOF | B_EOUT;
            return FALSE;
        }

        if (pConfig->nVerifyClient != VERIFY_NONE) {
            char *s;
            X509 *xs;
            if ((xs = __SSLeay SSL_get_peer_certificate(fb->ssl)) != NULL) {
                s = __SSLeay X509_NAME_oneline(X509_get_subject_name(xs), NULL, 0);
                conn->client->szClientX509 = ap_pstrdup(conn->pool, s);
                free(s);
            }
        }

        if (   pConfig->nVerifyClient == VERIFY_REQUIRE
            && !conn->client->szClientX509             ) {
            ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, conn->server,
                         "mod_ssl: No SSL Client Certificate");
            ssl_log_ssleay2apache(conn->server);
            return FALSE;
        }

        ssl_log_own(pConfig, "CIPHER is %s", 
                    __SSLeay SSL_get_cipher(conn->client->ssl));
    }

    /* 
     * This should be safe.... so I'll use it -- ben
     */
    __SSLeay SSL_set_read_ahead(fb->ssl, 1);

    /*
     * Ok, we now no longer need the global connection info
     */
    g_pCurrentConnection = NULL;

    return TRUE;
}

/*
 *  Close the SSL part of the socket connection
 */
static void SSL_HOOK_CloseConnection(void *_conn)
{
    conn_rec *conn = _conn;
    SSL *ssl = conn->client->ssl;

    if (ssl != NULL) {
        __SSLeay SSL_set_shutdown(ssl, SSL_SENT_SHUTDOWN|SSL_RECEIVED_SHUTDOWN);
        __SSLeay SSL_free(ssl);
    }
    return;
}

/*
 *  Auth Handler:
 *  Fake a Basic authentication from the X509 client certificate.
 *
 *  This must be run fairly early on to prevent a real authentication from
 *  occuring, in particular it must be run before anything else that
 *  authenticates a user.  This means that the Module statement for this
 *  module should be LAST in the Configuration file.
 */
static int ssl_hook_Auth(request_rec *r)
{
    SSLConfigRec *pConfig = mySrvConfig(r->server);
    char b1[MAX_STRING_LEN], b2[MAX_STRING_LEN];

    /*
     * We decline operation in various situations..
     */
    if (OPTIONAL_SSL)
        return DECLINED;
    if (!pConfig->bFakeBasicAuth)
        return DECLINED;
    if (r->connection->user)
        return DECLINED;
    if (!r->connection->client->szClientX509)
        return DECLINED;
    
    /*
     * Fake a password - which one would be immaterial, as, it seems, an empty
     * password in the users file would match ALL incoming passwords, if only
     * we were using the standard crypt library routine. Unfortunately, SSLeay
     * "fixes" a "bug" in crypt and thus prevents blank passwords from
     * working.  (IMHO what they really fix is a bug in the users of the code
     * - failing to program correctly for shadow passwords).  We need,
     * therefore, to provide a password. This password can be matched by
     * adding the string "xxj31ZMTZzkVA" as the password in the user file.
     * This is just the crypted variant of the word "password" ;-)
     */
    ap_snprintf(b1, sizeof(b1), "%s:password", 
                r->connection->client->szClientX509);
    util_uuencode(b2, b1);
    ap_snprintf(b1, sizeof(b1), "Basic %s", b2);
    ap_table_set(r->headers_in, "Authorization", b1);

    return DECLINED;
}

/*
 *  Access Handler
 */
static int ssl_hook_Access(request_rec *r)
{
    char *cipher;
    SSLDirConfigRec *rec  = myDirConfig(r);
    SSLConfigRec *pConfig = mySrvConfig(r->server);

    /*
     * Support for SSLRequire directive
     */
    if (rec->bSSLRequired && !r->connection->client->ssl) {
        ap_log_reason("SSL connection required", r->filename, r);
        return FORBIDDEN;
    }

    /* 
     * Check to see if SSL protocol is on 
     */
    if (pConfig->bDisabled || OPTIONAL_SSL)
        return DECLINED;

    /*
     * Check for banned SSL chipers 
     */
    cipher = __SSLeay SSL_get_cipher(r->connection->client->ssl);
    if (ap_table_get(rec->tbBannedCiphers, cipher)) {
        char *buf;
        buf = ap_pstrcat(r->pool, "SSL Cipher ", cipher, " is forbidden", NULL);
        ap_log_reason(buf, r->filename, r);
        return FORBIDDEN;
    }

    /*
     * If cipher is one of the required ones or no
     * required ciphers are configured, it's ok.
     */
    if (ap_table_get(rec->tbRequiredCiphers, cipher))
        return OK;
    if (ap_is_empty_table(rec->tbRequiredCiphers))
        return OK;
    else {
        char *buf;
        buf = ap_pstrcat(r->pool, "SSL Cipher ", cipher, 
                         " is not on the permitted list", NULL);
        ap_log_reason(buf, r->filename, r);
        return FORBIDDEN;
    }
}

/*
 *   Fixup Handler
 */
static int ssl_hook_Fixup(request_rec *r)
{
    table *e = r->subprocess_env;
    int keysize = 0;
    int secretkeysize = 0;
    char buf[MAX_STRING_LEN];
    char *cipher;
    int n;
    X509 *xs;

    SSLConfigRec *pConfig = mySrvConfig(r->server);

    /* 
     * Check to see if SSL is on
     */
    if (pConfig->bDisabled || OPTIONAL_SSL)
        return DECLINED;

    /*
     * Annotate the SSI/CGI environment with SSL information
     */

    cipher = __SSLeay SSL_get_cipher(r->connection->client->ssl);

    ap_table_set(e, "HTTPS", "on");
    ap_table_set(e, "HTTPS_CIPHER", cipher);
    ap_table_set(e, "SSL_CIPHER", cipher);

    ap_table_set(e, "SSL_PROTOCOL_VERSION",
                 __SSLeay SSL_get_version(r->connection->client->ssl));

    ap_table_set(e, "SSL_SSLEAY_VERSION", 
                 __SSLeay SSLeay_version(SSLEAY_VERSION));

    for (n = 0; aCipherspecs[n].szName; ++n) {
        if (strEQ(cipher, aCipherspecs[n].szName)) {
            keysize = aCipherspecs[n].nKeySize;
            secretkeysize = aCipherspecs[n].nSecretKeySize;
            break;
        }
    }
    ap_snprintf(buf, sizeof(buf), "%d", keysize);
    ap_table_set(e, "HTTPS_KEYSIZE", buf);
    ap_snprintf(buf, sizeof(buf), "%d", secretkeysize);
    ap_table_set(e, "HTTPS_SECRETKEYSIZE", buf);

    if (r->connection->client->szClientX509) {
        ssl_int_ExpandCert(e, "SSL_CLIENT_", r->connection->client->szClientX509);
        xs = __SSLeay SSL_get_peer_certificate(r->connection->client->ssl);
        ssl_int_ExpandCert(e, "SSL_CLIENT_I", 
                           __SSLeay X509_NAME_oneline(X509_get_issuer_name(xs), 
                                                      NULL, 0));
    }

    xs = __SSLeay SSL_get_certificate(r->connection->client->ssl);
    ssl_int_ExpandCert(e, "SSL_SERVER_", 
                       __SSLeay X509_NAME_oneline(__SSLeay X509_get_subject_name(xs),
                                                  NULL, 0));
    ssl_int_ExpandCert(e, "SSL_SERVER_I",
                       __SSLeay X509_NAME_oneline(__SSLeay X509_get_issuer_name(xs),
                                                  NULL, 0));
    return DECLINED;
}

/*  _________________________________________________________________
**
**  SSLeay Callback Functions
**  _________________________________________________________________
*/

/* 
 * FIXME: This is an expensive operation which should 
 * probably be done before forking
 */
static RSA *ssl_callback_TmpRSA(SSL *pSSL, int nExport)
{
    static RSA *pRSA = NULL;

    if (pRSA == NULL)
#if SSLEAY_VERSION_NUMBER >= 0x0900
        pRSA = __SSLeay RSA_generate_key(512, RSA_F4, NULL, NULL);
#else
        pRSA = __SSLeay RSA_generate_key(512, RSA_F4, NULL);
#endif
    return pRSA;
}

static int ssl_callback_SSLVerify(int ok, X509_STORE_CTX *ctx)
{
    X509 *xs  = __SSLeay X509_STORE_CTX_get_current_cert(ctx);
    int depth = __SSLeay X509_STORE_CTX_get_error_depth(ctx);
    int error = __SSLeay X509_STORE_CTX_get_error(ctx);

    SSLConfigRec *pConfig = mySrvConfig(g_pCurrentConnection->server);

    if (   error == X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY 
        || error == X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE) {
        char *s;

        if (pConfig->nVerifyClient == VERIFY_OPTIONAL_NO_CA) {
            ssl_log_own(pConfig, "No issuer, returning OK");
            return TRUE;
        }
        s = __SSLeay X509_NAME_oneline(__SSLeay X509_get_issuer_name(xs), NULL, 0);
        if (s == NULL) {
            ssl_log_own(pConfig, "Verify error");
            __SSLeay ERR_print_errors_fp(pConfig->fileLogFile);
            ssl_log_ssleay2apache(g_pCurrentConnection->server);
            return(0);
        }
        ssl_log_own(pConfig, "SSL Issuer: %s", s);
        free(s);
    }
    if (!ok) {
        ssl_log_own(pConfig, "Verify error:num=%d:%s", error,
               __SSLeay X509_verify_cert_error_string(error));
        ssl_log_ssleay2apache(g_pCurrentConnection->server);
        g_pCurrentConnection->client->szClientX509 = NULL;
    }
    if (depth >= pConfig->nVerifyDepth) {
        ssl_log_own(pConfig, "Verify depth exceeded");
        ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_NOERRNO,
                     g_pCurrentConnection->server, "mod_ssl: Verify depth exceeded");
        ok = 0;
    }
    ssl_log_own(pConfig, "SSL Verify return: %d", ok);
    return (ok);
}

static int ssl_callback_NewSessionCache(SSL *pSSL, SSL_SESSION *pNew)
{
    SendSessionToServer(pNew);
    return 0;
}

static SSL_SESSION *ssl_callback_GetSessionCache(
    SSL *pSSL, unsigned char *aucSessionID, int nLength, int *pCopy)
{
    SSL_SESSION *pSession;

    *pCopy = 0;
    pSession = GetSessionFromServer(aucSessionID, nLength);

    return pSession;
}


/*  _________________________________________________________________
**
**  SSL Internals
**  _________________________________________________________________
*/

/* 
 * Expand a X509_oneline entry into it's base components and register
 * them as environment variables. Needed if you want to pass certificate
 * information to CGI's. The naming convention SHOULD be fairly compatible
 * with CGI's written for stronghold's certificate info - Q 
 */
static void ssl_int_ExpandCert(table *pEnv, char *szPrefix, char *szCert)
{
    char var[HUGE_STRING_LEN];
    char buf[HUGE_STRING_LEN];
    char *s;

    ap_snprintf(var, sizeof(var), "%sDN", szPrefix);
    ap_table_set(pEnv, var, szCert);

    /* FIXME - strtok() and strcspn() may cause problems on some systems - Q */
    ap_cpystrn(buf, szCert, sizeof(buf));
    for (s = strtok(buf, "/"); s != NULL; s = strtok(NULL, "/")) {
        int n = strcspn(s, "=");
        s[n] = NUL;
        util_strupper(s);
        ap_snprintf(var, sizeof(var), "%s%s", szPrefix, s);
        ap_table_set(pEnv, var, s + n + 1);
    }
}

static void ssl_int_SetupVerify(conn_rec *conn)
{
    SSLConfigRec *pConfig = mySrvConfig(conn->server);

    conn->client->szClientX509 = NULL;

    g_pCurrentConnection = conn;

    conn->client->nVerifyError = X509_V_OK;

    if (!ssl_int_SetCertStuff(conn)) {
        ssl_log_own(pConfig, "ssl_int_SetCertStuff failed");
        ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_NOERRNO, conn->server,
                     "mod_ssl: ssl_int_SetCertStuff failed");
        /*
         * This particular exit() is bogus IMHO, because
         * it killed the entire httpd process -- rse
         */
        exit(1);
    }
}

static BOOL ssl_int_SetCertStuff(conn_rec *conn)
{
    SSLConfigRec *pConfig = mySrvConfig(conn->server);
    SSL *con = conn->client->ssl;
    char *cert_file = pConfig->szCertificateFile;

    /* PEM_set_getkey_callback(SSLKeyCallback); */
    if (cert_file != NULL) {
        if (__SSLeay SSL_use_certificate(con, pConfig->px509Certificate) <= 0) {
            ssl_log_own(pConfig, "Unable to set certificate");
            __SSLeay ERR_print_errors_fp(pConfig->fileLogFile);
            ssl_log_ssleay2apache(conn->server);
            return FALSE;
        }
        if (__SSLeay SSL_use_RSAPrivateKey(con, pConfig->prsaKey) <= 0) {
            ssl_log_own(pConfig, "Unable to set private key");
            __SSLeay ERR_print_errors_fp(pConfig->fileLogFile);
            ssl_log_ssleay2apache(conn->server);
            return FALSE;
        }
    }
    return TRUE;
}

/*  _________________________________________________________________
**
**  Session Cache Support
**  _________________________________________________________________
*/

#define MAX_SESSION_DER 100000

static void LaunchCache(server_rec *s, pool *pPool)
{
    int pid;

    g_pServer = s;
    pid = ap_spawn_child(pPool, CacheLauncher, g_szCacheServerPath, 
                         kill_always, NULL, NULL, NULL);
    return;
}

static int CacheLauncher(void *cmd, child_info *pInfo)
{
    char a1[1024];

    ap_error_log2stderr(g_pServer);
    dup2(STDERR_FILENO, STDOUT_FILENO);
    ap_snprintf(a1, sizeof(a1), "%d", ap_user_id);
    ap_cleanup_for_exec();
    execl(cmd, "ssl_gcache", a1, g_szCacheServerPort, NULL);
    perror(cmd);
    return 1;
}

static void SendSessionToServer(SSL_SESSION *pSession)
{
    uchar buf[MAX_SESSION_DER];
    Cache *p;
    uchar *t;
    int nLength;
    SSLConfigRec *pConfig;
    time_t tExpiresAt;

    pConfig = mySrvConfig(g_pCurrentConnection->server);

    if (LocalCacheFind(pSession->session_id, pSession->session_id_length))
        return;

    tExpiresAt = time(NULL) + pConfig->tSessionCacheTimeout;

    t = malloc(pSession->session_id_length);
    memcpy(t, pSession->session_id, pSession->session_id_length);
    if ((p = LocalCacheAdd(t, pSession->session_id_length, tExpiresAt)) == NULL)
        return;

    t = buf;
    nLength = __SSLeay i2d_SSL_SESSION(pSession, &t);

    p->aucData = malloc(nLength);
    memcpy(p->aucData, buf, nLength);
    p->nData = nLength;

    GlobalCacheAdd(p->aucKey, p->nKey, p->aucData, p->nData, tExpiresAt);
}

static SSL_SESSION *GetSessionFromServer(uchar *aucSessionID, int nLength)
{
    SSL_SESSION *pSession;
    Cache *p;
    uchar *t, *t2;
    int nData;

    p = LocalCacheFind(aucSessionID, nLength);
    if (!p) {
        time_t tExpiresAt;

        t = GlobalCacheGet(aucSessionID, nLength, &nData, &tExpiresAt);
        if (!t)
            return NULL;
        t2 = malloc(nLength);
        memcpy(t2, aucSessionID, nLength);
        p = LocalCacheAdd(t2, nLength, tExpiresAt);
        p->aucData = t;
        p->nData = nData;
    }
    else {
        t = p->aucData;
        nData = p->nData;
    }
    pSession = __SSLeay d2i_SSL_SESSION(NULL, &t, nData);
    if (pSession == NULL)
        __SSLeay ERR_print_errors_fp(stderr);
    return pSession;
}


/*  _________________________________________________________________
**
**  Logfile Support
**  _________________________________________________________________
*/

static void ssl_log_own(SSLConfigRec *pConfig, const char *msg, ...)
{
    char tstr[80];
    char vstr[1024];
    char str[1024];
    int timz;
    struct tm *t;
    char sign;
    va_list ap;

    va_start(ap, msg);

    t = ap_get_gmtoff(&timz);
    sign = (timz < 0 ? '-' : '+');
    if (timz < 0)
        timz = -timz;
    strftime(tstr, 80, "[%d/%b/%Y:%H:%M:%S ", t);
    ap_snprintf(tstr + strlen(tstr), 80-strlen(tstr), "%c%.2d%.2d]",
                sign, timz/60, timz%60);
    
    ap_vsnprintf(vstr, sizeof(vstr), msg, ap);

    ap_snprintf(str, sizeof(str), "%s %s\n", tstr, vstr);

    fprintf(pConfig->fileLogFile, str);
    fflush(pConfig->fileLogFile);

    va_end(ap);
    return;
}

static void ssl_log_ssleay2apache(server_rec *s)
{
    unsigned long l;
    char buf[MAX_STRING_LEN];

    /* Print SSLeay error messages to Apache error log */
    while ((l = __SSLeay ERR_get_error())) {
        __SSLeay ERR_error_string(l, buf);
        ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, s, "SSLeay: %s", buf);
    }
    return;
}

/*  _________________________________________________________________
**
**  Utility Functions
**  _________________________________________________________________
*/

static void util_strupper(char *s)
{
    for (; *s; ++s)
        *s = toupper(*s);
    return;
}

static const char util_uuencode_six2pr[64+1] =
    "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

static void util_uuencode(char *szTo, const char *szFrom)
{
    const unsigned char *s;

    for (s = (const unsigned char *)szFrom; *s; s += 3) {
        *szTo++ = util_uuencode_six2pr[s[0] >> 2];
        *szTo++ = util_uuencode_six2pr[(s[0] << 4 | s[1] >> 4) & 0x3f];
        if (!s[0])
            break;
        *szTo++ = util_uuencode_six2pr[(s[1] << 2 | s[2] >> 6) & 0x3f];
        if (!s[1])
            break;
        *szTo++ = util_uuencode_six2pr[s[2] & 0x3f];
        if (!s[2])
            break;
    }
    *szTo++ = NUL;
    return;
}

