On Wed, Jun 23, 1999, Matthias Loepfe wrote:

> I'm testing some of your new features in mod_ssl. I'm currently testing the
> unreleased patch for the SSLProxy. 
> 
> Am I right that client certificate handling is not yet finished?

Hmmm... there might be still a bug, yes. Client certificate handling should
finally work, of course. At least the patch is _proposed_ to be complete.

The patch was originally derived from Stronghold 2.4 and contributed to the
project by C2Net. I've then ported it to the latest OpenSSL API, overhauled
it, cleaned it up and integrated it into one of my development trees. But I've
still not tested it myself in depth (that is together with the lack of
documentation the reason why it's still not released with 2.3). So either the
client cert handling was already broken in Stronghold ;), the stuff was
forgotten to incorporated or I've broken it when I overhauled it. So in order
to find the bug we've to look at the whole code again. 
 
> It seems that the private keys are not yet read what results in a SEGV deep in
> OpenSSL at the point where the private key is needed.
> 
> I have some more questions which I will send each in a different mail for 
> better handling.

Hmmm... the client handling should be done on-the-fly.  But perhaps the
loading is already broken. You can find it in functions
SSL_CA_load_certs_file() and SSL_CA_load_certs_path() in ssl_util_ssl.c.  The
on-the-fly handling is done by ssl_ext_mp_clientcert_cb() in ssl_engine_ext.c.
You can debug this by adding some ssl_log() calls to this function.
Perhaps no CA matches the client certs. 

I append you the latest state of the patch which should apply fine against
2.3.5. I've currently no real time available for this patch, so it would be
great when you can help me here a little bit more.

                                       Ralf S. Engelschall
                                       [EMAIL PROTECTED]
                                       www.engelschall.com

Index: modules/proxy/mod_proxy.c
===================================================================
RCS file: /e/modssl/cvs/mod_ssl/pkg.apache/src/modules/proxy/mod_proxy.c,v
retrieving revision 1.11
diff -u -r1.11 mod_proxy.c
--- modules/proxy/mod_proxy.c   1999/03/21 12:00:11     1.11
+++ modules/proxy/mod_proxy.c   1999/04/02 19:05:19
@@ -247,6 +247,10 @@
 static void proxy_init(server_rec *r, pool *p)
 {
     ap_proxy_garbage_init(r, p);
+#ifdef EAPI
+    ap_hook_use("ap::mod_proxy::init", 
+                AP_HOOK_SIG3(void,ptr,ptr), AP_HOOK_ALL, r, p);
+#endif
 }
 
 #ifdef EAPI
Index: modules/proxy/proxy_http.c
===================================================================
RCS file: /e/modssl/cvs/mod_ssl/pkg.apache/src/modules/proxy/proxy_http.c,v
retrieving revision 1.12
diff -u -r1.12 proxy_http.c
--- modules/proxy/proxy_http.c  1999/03/21 12:33:14     1.12
+++ modules/proxy/proxy_http.c  1999/04/02 19:05:51
@@ -188,6 +188,9 @@
     const char *urlptr = NULL;
     const char *datestr;
     struct tbl_do_args tdo;
+#ifdef EAPI
+    char *peer;
+#endif
 
     void *sconf = r->server->module_config;
     proxy_server_conf *conf =
@@ -248,12 +251,18 @@
        err = ap_proxy_host2addr(proxyhost, &server_hp);
        if (err != NULL)
            return DECLINED;    /* try another */
+#ifdef EAPI
+       peer = ap_psprintf(p, "%s:%u", proxyhost, proxyport);  
+#endif
     }
     else {
        server.sin_port = htons(destport);
        err = ap_proxy_host2addr(desthost, &server_hp);
        if (err != NULL)
            return ap_proxyerror(r, HTTP_INTERNAL_SERVER_ERROR, err);
+#ifdef EAPI
+       peer =  ap_psprintf(p, "%s:%u", desthost, destport);  
+#endif
     }
 
     sock = ap_psocket(p, PF_INET, SOCK_STREAM, IPPROTO_TCP);
@@ -312,9 +321,9 @@
     {
         char *errmsg = NULL;
         ap_hook_use("ap::mod_proxy::http::handler::new_connection", 
-                    AP_HOOK_SIG3(ptr,ptr,ptr), 
+                    AP_HOOK_SIG4(ptr,ptr,ptr,ptr), 
                     AP_HOOK_DECLINE(NULL),
-                    &errmsg, r, f);
+                    &errmsg, r, f, peer);
         if (errmsg != NULL)
             return ap_proxyerror(r, HTTP_BAD_GATEWAY, errmsg);
     }
Index: modules/ssl/mod_ssl.c
===================================================================
RCS file: /e/modssl/cvs/mod_ssl/pkg.apache/src/modules/ssl/mod_ssl.c,v
retrieving revision 1.55
diff -u -r1.55 mod_ssl.c
--- modules/ssl/mod_ssl.c       1999/05/06 09:56:35     1.55
+++ modules/ssl/mod_ssl.c       1999/05/15 10:39:20
@@ -151,6 +151,34 @@
                "Enable or disable various SSL protocols"
                "(`[+-][SSLv2|SSLv3|TLSv1] ...' - see manual)")
 
+    /* 
+     * Proxy configuration for remote SSL connections
+     */
+    AP_SRV_CMD(ProxyProtocol, RAW_ARGS,
+               "SSL Proxy: enable or disable SSL protocol flavors "
+               "(`[+-][SSLv2|SSLv3|TLSv1] ...' - see manual)")
+    AP_SRV_CMD(ProxyCipherSuite, TAKE1,
+               "SSL Proxy: colon-delimited list of permitted SSL ciphers "
+               "(`XXX:...:XXX' - see manual)")
+    AP_SRV_CMD(ProxyVerify, FLAG,
+               "SSL Proxy: whether to verify the remote certificate "
+               "(`on' or `off')")
+    AP_SRV_CMD(ProxyVerifyDepth, TAKE1,
+               "SSL Proxy: maximum certificate verification depth "
+               "(`N' - number of intermediate certificates)")
+    AP_SRV_CMD(ProxyCACertificateFile, TAKE1,
+               "SSL Proxy: file containing server certificates "
+               "(`/path/to/file' - PEM encoded certificates)")
+    AP_SRV_CMD(ProxyCACertificatePath, TAKE1,
+               "SSL Proxy: directory containing server certificates "
+               "(`/path/to/dir' - contains PEM encoded certificates)")
+    AP_SRV_CMD(ProxyMachineCertificateFile, TAKE1,
+               "SSL Proxy: file containing client certificates "
+               "(`/path/to/file' - PEM encoded certificates)")
+    AP_SRV_CMD(ProxyMachineCertificatePath, TAKE1,
+               "SSL Proxy: directory containing client certificates "
+               "(`/path/to/dir' - contains PEM encoded certificates)")
+
     /*
      * Per-directory context configuration directives
      */
Index: modules/ssl/mod_ssl.h
===================================================================
RCS file: /e/modssl/cvs/mod_ssl/pkg.apache/src/modules/ssl/mod_ssl.h,v
retrieving revision 1.103
diff -u -r1.103 mod_ssl.h
--- modules/ssl/mod_ssl.h       1999/06/22 15:04:40     1.103
+++ modules/ssl/mod_ssl.h       1999/06/23 11:39:02
@@ -535,6 +535,16 @@
     char        *szCARevocationPath;
     char        *szCARevocationFile;
     X509_STORE  *pRevocationStore;
+    /* Configuration details for proxy operation */
+    ssl_proto_t  nProxyProtocol;
+    int          bProxyVerify;
+    int          nProxyVerifyDepth;
+    char        *szProxyCACertificatePath;
+    char        *szProxyCACertificateFile;
+    char        *szProxyClientCertificateFile;
+    char        *szProxyClientCertificatePath;
+    char        *szProxyCipherSuite;
+    SSL_CTX     *pSSLProxyCtx;
 #ifdef SSL_VENDOR
     ap_ctx      *ctx;
 #endif
@@ -599,6 +609,14 @@
 const char  *ssl_cmd_SSLOptions(cmd_parms *, SSLDirConfigRec *, const char *);
 const char  *ssl_cmd_SSLRequireSSL(cmd_parms *, SSLDirConfigRec *, char *);
 const char  *ssl_cmd_SSLRequire(cmd_parms *, SSLDirConfigRec *, char *);
+const char  *ssl_cmd_SSLProxyProtocol(cmd_parms *, char *, const char *);
+const char  *ssl_cmd_SSLProxyCipherSuite(cmd_parms *, char *, char *);
+const char  *ssl_cmd_SSLProxyVerify(cmd_parms *, char *, int);
+const char  *ssl_cmd_SSLProxyVerifyDepth(cmd_parms *, char *, char *);
+const char  *ssl_cmd_SSLProxyCACertificatePath(cmd_parms *, char *, char *);
+const char  *ssl_cmd_SSLProxyCACertificateFile(cmd_parms *, char *, char *);
+const char  *ssl_cmd_SSLProxyMachineCertificatePath(cmd_parms *, char *, char *);
+const char  *ssl_cmd_SSLProxyMachineCertificateFile(cmd_parms *, char *, char *);
 
 /*  module initialization  */
 void         ssl_init_Module(server_rec *, pool *);
Index: modules/ssl/ssl_engine_compat.c
===================================================================
RCS file: /e/modssl/cvs/mod_ssl/pkg.apache/src/modules/ssl/ssl_engine_compat.c,v
retrieving revision 1.25
diff -u -r1.25 ssl_engine_compat.c
--- modules/ssl/ssl_engine_compat.c     1999/05/01 10:06:07     1.25
+++ modules/ssl/ssl_engine_compat.c     1999/05/01 14:28:03
@@ -159,12 +159,7 @@
     CRM_ENTRY( CRM_CMD("SSL_CertificateLogDir"),     CRM_LOG("Not supported by 
mod_ssl")      )
     CRM_ENTRY( CRM_CMD("AuthCertDir"),               CRM_LOG("Not supported by 
mod_ssl")      )
     CRM_ENTRY( CRM_CMD("SSL_Group"),                 CRM_LOG("Not supported by 
mod_ssl")      )
-    CRM_ENTRY( CRM_CMD("SSLProxyMachineCertPath"),   CRM_LOG("Not supported by 
mod_ssl")      )
-    CRM_ENTRY( CRM_CMD("SSLProxyMachineCertFile"),   CRM_LOG("Not supported by 
mod_ssl")      )
-    CRM_ENTRY( CRM_CMD("SSLProxyCACertificatePath"), CRM_LOG("Not supported by 
mod_ssl")      )
-    CRM_ENTRY( CRM_CMD("SSLProxyCACertificateFile"), CRM_LOG("Not supported by 
mod_ssl")      )
-    CRM_ENTRY( CRM_CMD("SSLProxyVerifyDepth"),       CRM_LOG("Not supported by 
mod_ssl")      )
-    CRM_ENTRY( CRM_CMD("SSLProxyCipherList"),        CRM_LOG("Not supported by 
mod_ssl")      )
+    CRM_ENTRY( CRM_CMD("SSLProxyCipherList"),        CRM_SUB("SSLProxyCipherSuite")   
+        )
 
     CRM_END
 };
Index: modules/ssl/ssl_engine_config.c
===================================================================
RCS file: /e/modssl/cvs/mod_ssl/pkg.apache/src/modules/ssl/ssl_engine_config.c,v
retrieving revision 1.57
diff -u -r1.57 ssl_engine_config.c
--- modules/ssl/ssl_engine_config.c     1999/06/17 10:05:24     1.57
+++ modules/ssl/ssl_engine_config.c     1999/06/17 10:13:38
@@ -202,6 +202,15 @@
     sc->szCARevocationPath     = NULL;
     sc->szCARevocationFile     = NULL;
     sc->pRevocationStore       = NULL;
+    sc->nProxyVerifyDepth             = UNSET;
+    sc->szProxyCACertificatePath      = NULL;
+    sc->szProxyCACertificateFile      = NULL;
+    sc->szProxyClientCertificateFile  = NULL;
+    sc->szProxyClientCertificatePath  = NULL;
+    sc->szProxyCipherSuite            = NULL;
+    sc->nProxyProtocol                = SSL_PROTOCOL_ALL & ~SSL_PROTOCOL_TLSV1;
+    sc->bProxyVerify                  = UNSET;
+    sc->pSSLProxyCtx                  = NULL;
 
     (void)memset(sc->szPublicCertFile, 0, SSL_AIDX_MAX*sizeof(char *));
     (void)memset(sc->szPrivateKeyFile, 0, SSL_AIDX_MAX*sizeof(char *));
@@ -260,6 +269,16 @@
                 p, base, add, new);
 #endif
 
+    cfgMergeInt(nProxyVerifyDepth);
+    cfgMergeString(szProxyCACertificatePath);
+    cfgMergeString(szProxyCACertificateFile);
+    cfgMergeString(szProxyClientCertificateFile);
+    cfgMergeString(szProxyClientCertificatePath);
+    cfgMergeString(szProxyCipherSuite);
+    cfgMerge(nProxyProtocol, (SSL_PROTOCOL_ALL & ~SSL_PROTOCOL_TLSV1));
+    cfgMergeBool(bProxyVerify);
+    cfgMerge(pSSLProxyCtx, NULL);
+
     return new;
 }
 
@@ -845,6 +864,132 @@
             options = thisopt;
     }
     sc->nProtocol = options;
+    return NULL;
+}
+
+const char *ssl_cmd_SSLProxyProtocol(
+    cmd_parms *cmd, char *struct_ptr, const char *opt)
+{
+    SSLSrvConfigRec *sc;
+    ssl_proto_t options, thisopt;
+    char action;
+    char *w;
+
+    sc = mySrvConfig(cmd->server);
+    options = SSL_PROTOCOL_NONE;
+    while (opt[0] != NUL) {
+        w = ap_getword_conf(cmd->pool, &opt);
+
+        action = NUL;
+        if (*w == '+' || *w == '-')
+            action = *(w++);
+
+        if (strcEQ(w, "SSLv2"))
+            thisopt = SSL_PROTOCOL_SSLV2;
+        else if (strcEQ(w, "SSLv3"))
+            thisopt = SSL_PROTOCOL_SSLV3;
+        else if (strcEQ(w, "TLSv1"))
+            thisopt = SSL_PROTOCOL_TLSV1;
+        else if (strcEQ(w, "all"))
+            thisopt = SSL_PROTOCOL_ALL;
+        else
+            return ap_pstrcat(cmd->pool, "SSLProxyProtocol: "
+                              "Illegal protocol '", w, "'", NULL);
+        if (action == '-')
+            options &= ~thisopt;
+        else if (action == '+')
+            options |= thisopt;
+        else
+            options = thisopt;
+    }
+    sc->nProxyProtocol = options;
+    return NULL;
+}
+
+const char *ssl_cmd_SSLProxyCipherSuite(
+    cmd_parms *cmd, char *struct_ptr, char *arg)
+{
+    SSLSrvConfigRec *sc = mySrvConfig(cmd->server);
+
+    sc->szProxyCipherSuite = arg;
+    return NULL;
+}
+
+const char *ssl_cmd_SSLProxyVerify(
+    cmd_parms *cmd, char *struct_ptr, int flag)
+{
+    SSLSrvConfigRec *sc = mySrvConfig(cmd->server);
+
+    sc->bProxyVerify = (flag ? TRUE : FALSE);
+    return NULL;
+}
+
+const char *ssl_cmd_SSLProxyVerifyDepth(
+    cmd_parms *cmd, char *struct_ptr, char *arg)
+{
+    SSLSrvConfigRec *sc = mySrvConfig(cmd->server);
+    int d;
+
+    d = atoi(arg);
+    if (d < 0)
+        return "SSLProxyVerifyDepth: Invalid argument";
+    sc->nProxyVerifyDepth = d;
+    return NULL;
+}
+
+const char *ssl_cmd_SSLProxyCACertificateFile(
+    cmd_parms *cmd, char *struct_ptr, char *arg)
+{
+    SSLSrvConfigRec *sc = mySrvConfig(cmd->server);
+    char *cpPath;
+
+    cpPath = ap_server_root_relative(cmd->pool, arg);
+    if (!ssl_util_path_check(SSL_PCM_EXISTS|SSL_PCM_ISREG|SSL_PCM_ISNONZERO, cpPath))
+        return ap_pstrcat(cmd->pool, "SSLProxyCACertificateFile: file '",
+                          cpPath, "' not exists or empty", NULL);
+    sc->szProxyCACertificateFile = cpPath;
+    return NULL;
+}
+
+const char *ssl_cmd_SSLProxyCACertificatePath(
+    cmd_parms *cmd, char *struct_ptr, char *arg)
+{
+    SSLSrvConfigRec *sc = mySrvConfig(cmd->server);
+    char *cpPath;
+
+    cpPath = ap_server_root_relative(cmd->pool, arg);
+    if (!ssl_util_path_check(SSL_PCM_EXISTS|SSL_PCM_ISDIR, cpPath))
+        return ap_pstrcat(cmd->pool, "SSLProxyCACertificatePath: directory '",
+                          cpPath, "' does not exists", NULL);
+    sc->szProxyCACertificatePath = cpPath;
+    return NULL;
+}
+
+const char *ssl_cmd_SSLProxyMachineCertificateFile(
+    cmd_parms *cmd, char *struct_ptr, char *arg)
+{
+    SSLSrvConfigRec *sc = mySrvConfig(cmd->server);
+    char *cpPath;
+
+    cpPath = ap_server_root_relative(cmd->pool, arg);
+    if (!ssl_util_path_check(SSL_PCM_EXISTS|SSL_PCM_ISREG|SSL_PCM_ISNONZERO, cpPath))
+        return ap_pstrcat(cmd->pool, "SSLProxyMachineCertFile: file '",
+                          cpPath, "' not exists or empty", NULL);
+    sc->szProxyClientCertificateFile = cpPath;
+    return NULL;
+}
+
+const char *ssl_cmd_SSLProxyMachineCertificatePath(
+    cmd_parms *cmd, char *struct_ptr, char *arg)
+{
+    SSLSrvConfigRec *sc = mySrvConfig(cmd->server);
+    char *cpPath;
+
+    cpPath = ap_server_root_relative(cmd->pool, arg);
+    if (!ssl_util_path_check(SSL_PCM_EXISTS|SSL_PCM_ISDIR, cpPath))
+        return ap_pstrcat(cmd->pool, "SSLProxyMachineCertPath: directory '",
+                          cpPath, "' does not exists", NULL);
+    sc->szProxyClientCertificatePath = cpPath;
     return NULL;
 }
 
Index: modules/ssl/ssl_engine_ext.c
===================================================================
RCS file: /e/modssl/cvs/mod_ssl/pkg.apache/src/modules/ssl/ssl_engine_ext.c,v
retrieving revision 1.27
diff -u -r1.27 ssl_engine_ext.c
--- modules/ssl/ssl_engine_ext.c        1999/06/17 11:25:13     1.27
+++ modules/ssl/ssl_engine_ext.c        1999/06/23 11:47:21
@@ -213,18 +213,23 @@
 **  _________________________________________________________________
 */
 
-static int   ssl_ext_mp_canon(request_rec *r, char *url);
-static int   ssl_ext_mp_handler(request_rec *r, void *cr, char *url, char *proxyhost, 
int proxyport, char *protocol);
-static int   ssl_ext_mp_set_destport(request_rec *r);
-static char *ssl_ext_mp_new_connection(request_rec *r, BUFF *fb);
-static void  ssl_ext_mp_close_connection(void *_fb);
-static int   ssl_ext_mp_write_host_header(request_rec *r, BUFF *fb, char *host, int 
port, char *portstr);
+static int   ssl_ext_mp_canon(request_rec *, char *);
+static int   ssl_ext_mp_handler(request_rec *, void *, char *, char *, int , char *);
+static int   ssl_ext_mp_set_destport(request_rec *);
+static char *ssl_ext_mp_new_connection(request_rec *, BUFF *, char *);
+static void  ssl_ext_mp_close_connection(void *);
+static int   ssl_ext_mp_write_host_header(request_rec *, BUFF *, char *, int, char *);
+static void  ssl_ext_mp_init(server_rec *, pool *);
+static int   ssl_ext_mp_verify_cb(int, X509_STORE_CTX *);
+static int   ssl_ext_mp_clientcert_cb(SSL *, X509 **, EVP_PKEY **);
 
 /*
  * register us ...
  */
 static void ssl_ext_mp_register(void)
 {
+    ap_hook_register("ap::mod_proxy::init",
+                     ssl_ext_mp_init, AP_HOOK_NOCTX);
     ap_hook_register("ap::mod_proxy::canon",
                      ssl_ext_mp_canon, AP_HOOK_NOCTX);
     ap_hook_register("ap::mod_proxy::handler",
@@ -240,6 +245,7 @@
 
 static void ssl_ext_mp_unregister(void)
 {
+    ap_hook_unregister("ap::mod_proxy::init", ssl_ext_mp_init);
     ap_hook_unregister("ap::mod_proxy::canon", ssl_ext_mp_canon);
     ap_hook_unregister("ap::mod_proxy::handler", ssl_ext_mp_handler);
     ap_hook_unregister("ap::mod_proxy::http::handler::set_destport",
@@ -251,6 +257,143 @@
     return;
 }
 
+/*
+ * SSL proxy initialization
+ */
+static void ssl_ext_mp_init(server_rec *s, pool *p)
+{
+    SSLSrvConfigRec *sc;
+    char *cpVHostID;
+    int nVerify;
+    SSL_CTX *ctx;
+    char *cp;
+    STACK_OF(X509_INFO) *sk;
+
+    /*
+     * Initialize each virtual server 
+     */
+    for (; s != NULL; s = s->next) {
+        sc = mySrvConfig(s);
+        cpVHostID = ssl_util_vhostid(p, s);
+        
+        if (sc->bProxyVerify == UNSET)
+            sc->bProxyVerify = FALSE;
+
+        /*
+         *  Create new SSL context and configure callbacks
+         */
+        if (sc->nProxyProtocol == SSL_PROTOCOL_NONE) {
+            ssl_log(s, SSL_LOG_ERROR,
+                    "Init: (%s) No Proxy SSL protocols available [hint: 
+SSLProxyProtocol]",
+                    cpVHostID);
+            ssl_die();
+        }
+        cp = ap_pstrcat(p, (sc->nProxyProtocol & SSL_PROTOCOL_SSLV2 ? "SSLv2, " : 
+""), 
+                           (sc->nProxyProtocol & SSL_PROTOCOL_SSLV3 ? "SSLv3, " : 
+""), 
+                           (sc->nProxyProtocol & SSL_PROTOCOL_TLSV1 ? "TLSv1, " : 
+""), NULL);
+        cp[strlen(cp)-2] = NUL;
+        ssl_log(s, SSL_LOG_TRACE, 
+                "Init: (%s) Creating new proxy SSL context (protocols: %s)", 
+                cpVHostID, cp);
+        if (sc->nProxyProtocol == SSL_PROTOCOL_SSLV2)
+            ctx = SSL_CTX_new(SSLv2_client_method());  /* only SSLv2 is left */ 
+        else
+            ctx = SSL_CTX_new(SSLv23_client_method()); /* be more flexible */
+        if (ctx == NULL) {
+            ssl_log(s, SSL_LOG_ERROR|SSL_ADD_SSLERR,
+                    "Init: (%s) Unable to create SSL Proxy context", cpVHostID);
+            ssl_die();
+        }
+        sc->pSSLProxyCtx = ctx;
+        SSL_CTX_set_options(ctx, SSL_OP_ALL);
+        if (!(sc->nProxyProtocol & SSL_PROTOCOL_SSLV2))
+            SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv2);
+        if (!(sc->nProxyProtocol & SSL_PROTOCOL_SSLV3))
+            SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv3);
+        if (!(sc->nProxyProtocol & SSL_PROTOCOL_TLSV1)) 
+            SSL_CTX_set_options(ctx, SSL_OP_NO_TLSv1);
+
+        if (sc->szProxyClientCertificateFile || sc->szProxyClientCertificatePath) {
+            sk = sk_X509_INFO_new_null();
+            if (sc->szProxyClientCertificateFile) 
+                SSL_CA_load_certs_file(p, sk, sc->szProxyClientCertificateFile);
+            if (sc->szProxyClientCertificatePath)
+                SSL_CA_load_certs_path(p, sk, sc->szProxyClientCertificatePath);
+            ssl_log(s, SSL_LOG_TRACE, "Init: (%s) loaded %d client certs for SSL 
+proxy",
+                    cpVHostID, sk_X509_INFO_num(sk));
+            if (sk_X509_INFO_num(sk) > 0) {
+                SSL_CTX_set_client_cert_cb(ctx, ssl_ext_mp_clientcert_cb);
+                ap_ctx_set(sc->ctx, "ssl::proxy::clientcerts", (void *)sk);
+            }
+        }
+
+        /*
+         * Calculate OpenSSL verify type for verifying the remote server
+         * certificate. We either verify it against our list of CA's, or don't
+         * bother at all.
+         */
+        nVerify = SSL_VERIFY_NONE;
+        if (sc->bProxyVerify)
+            nVerify |= SSL_VERIFY_PEER;
+        if (   nVerify & SSL_VERIFY_PEER 
+            && sc->szProxyCACertificateFile == NULL 
+            && sc->szProxyCACertificatePath == NULL) {
+            ssl_log(s, SSL_LOG_ERROR,
+                    "Init: (%s) SSLProxyVerify set to On but no CA certificates 
+configured",
+                    cpVHostID);
+            ssl_die();
+        }
+        if (   nVerify & SSL_VERIFY_NONE
+            && (   sc->szProxyCACertificateFile != NULL
+                || sc->szProxyCACertificatePath != NULL)) {
+            ssl_log(s, SSL_LOG_WARN, 
+                    "init: (%s) CA certificates configured but ignored because 
+SSLProxyVerify is Off",
+                    cpVHostID);
+        }
+        SSL_CTX_set_verify(ctx, nVerify, ssl_ext_mp_verify_cb);
+
+        /*
+         * Enable session caching. We can safely use the same cache
+         * as used for communicating with the other clients.
+         */
+        SSL_CTX_sess_set_new_cb(sc->pSSLProxyCtx,    
+ssl_callback_NewSessionCacheEntry);
+        SSL_CTX_sess_set_get_cb(sc->pSSLProxyCtx,    
+ssl_callback_GetSessionCacheEntry);
+        SSL_CTX_sess_set_remove_cb(sc->pSSLProxyCtx, 
+ssl_callback_DelSessionCacheEntry);
+
+        /*
+         *  Configure SSL Cipher Suite
+         */
+        ssl_log(s, SSL_LOG_TRACE,
+                "Init: (%s) Configuring permitted SSL ciphers for SSL proxy", 
+cpVHostID);
+        if (sc->szProxyCipherSuite != NULL) {
+            if (!SSL_CTX_set_cipher_list(sc->pSSLProxyCtx, sc->szProxyCipherSuite)) {
+                ssl_log(s, SSL_LOG_ERROR|SSL_ADD_SSLERR,
+                        "Init: (%s) Unable to configure permitted SSL ciphers for SSL 
+Proxy",
+                        cpVHostID);
+                ssl_die();
+            }
+        }
+
+        /*
+         * Configure Client Authentication details
+         */
+        if (sc->szProxyCACertificateFile != NULL || sc->szProxyCACertificatePath != 
+NULL) {
+             ssl_log(s, SSL_LOG_DEBUG, 
+                     "Init: (%s) Configuring client verification locations for SSL 
+Proxy", 
+                     cpVHostID);
+             if (!SSL_CTX_load_verify_locations(sc->pSSLProxyCtx,
+                                                sc->szProxyCACertificateFile,
+                                                sc->szProxyCACertificatePath)) {
+                 ssl_log(s, SSL_LOG_ERROR|SSL_ADD_SSLERR, 
+                         "Init: (%s) Unable to configure SSL verify locations for SSL 
+proxy",
+                         cpVHostID);
+                 ssl_die();
+             }
+        }
+    }
+    return;
+}
+
 static int ssl_ext_mp_canon(request_rec *r, char *url)
 {
     int rc;
@@ -289,23 +432,29 @@
         return DEFAULT_HTTP_PORT;
 }
 
-static char *ssl_ext_mp_new_connection(request_rec *r, BUFF *fb)
+static char *ssl_ext_mp_new_connection(request_rec *r, BUFF *fb, char *peer)
 {
-    SSL_CTX *ssl_ctx;
     SSL *ssl;
     char *errmsg;
     int rc;
     char *cpVHostID;
+    SSLSrvConfigRec *sc;
+    char *cp;
 
     if (ap_ctx_get(r->ctx, "ssl::proxy::enabled") == PFALSE)
         return NULL;
+
+    /*
+     * Find context
+     */
+    sc = mySrvConfig(r->server);
     cpVHostID = ssl_util_vhostid(r->pool, r->server);
 
     /*
      * Create a SSL context and handle
      */
-    ssl_ctx = SSL_CTX_new(SSLv23_client_method());
-    if ((ssl = SSL_new(ssl_ctx)) == NULL) {
+    ssl = SSL_new(sc->pSSLProxyCtx);
+    if (ssl == NULL) {
         errmsg = ap_pstrcat(r->pool, "SSL new failed (%s): ", cpVHostID,
                             ERR_reason_error_string(ERR_get_error()), NULL);
         ap_ctx_set(fb->ctx, "ssl", NULL);
@@ -314,7 +463,12 @@
     SSL_clear(ssl);
     SSL_set_session_id_context(ssl, cpVHostID, strlen(cpVHostID));
     SSL_set_fd(ssl, fb->fd);
+    SSL_set_app_data(ssl, fb->ctx);
     ap_ctx_set(fb->ctx, "ssl", ssl);
+    ap_ctx_set(fb->ctx, "ssl::proxy::server_rec", r->server);
+    ap_ctx_set(fb->ctx, "ssl::proxy::peer", peer);
+    ap_ctx_set(fb->ctx, "ssl::proxy::servername", cpVHostID);
+    ap_ctx_set(fb->ctx, "ssl::proxy::verifyerror", NULL);
 
     /*
      * Give us a chance to gracefully close the connection
@@ -326,8 +480,17 @@
      * Establish the SSL connection
      */
     if ((rc = SSL_connect(ssl)) <= 0) {
-        errmsg = ap_pstrcat(r->pool, "SSL connect failed (%s): ", cpVHostID,
-                            ERR_reason_error_string(ERR_get_error()), NULL);
+        if ((cp = (char *)ap_ctx_get(fb->ctx, "ssl::proxy::verifyerror")) != NULL) {
+            SSL_set_shutdown(ssl, SSL_RECEIVED_SHUTDOWN); 
+            SSL_smart_shutdown(ssl);
+            SSL_free(ssl);
+            ap_ctx_set(fb->ctx, "ssl", NULL);
+            ap_bsetflag(fb, B_EOF|B_EOUT, 1);
+            return NULL;
+        }
+        errmsg = ap_psprintf(r->pool, "SSL proxy connect failed (%s): peer %s: %s",
+                             cpVHostID, peer, 
+ERR_reason_error_string(ERR_get_error()));
+        ssl_log(r->server, SSL_LOG_ERROR, errmsg);
         SSL_free(ssl);
         ap_ctx_set(fb->ctx, "ssl", NULL);
         return errmsg;
@@ -364,6 +527,177 @@
     return DECLINED;
 }
 
+/* 
+ * Callback for client certificate stuff.
+ * If the remote site sent us a SSLv3 list of acceptable CA's then trawl the
+ * table of client certs and send the first one that matches.
+ */
+static int ssl_ext_mp_clientcert_cb(SSL *ssl, X509 **x509, EVP_PKEY **pkey) 
+{
+    SSLSrvConfigRec *sc;
+    X509_NAME *xnx;
+    X509_NAME *issuer;
+    X509_INFO *xi;
+    char *peer;
+    char *servername;
+    server_rec *s;
+    ap_ctx *pCtx;
+    STACK_OF(X509_NAME) *sk;
+    STACK_OF(X509_INFO) *pcerts;
+    char *cp;
+    int i, j;
+    
+    pCtx       = (ap_ctx *)SSL_get_app_data(ssl);
+    s          = ap_ctx_get(pCtx, "ssl::proxy::server_rec");
+    peer       = ap_ctx_get(pCtx, "ssl::proxy::peer");
+    servername = ap_ctx_get(pCtx, "ssl::proxy::servername");
+
+    sc         = mySrvConfig(s);
+    pcerts     = ap_ctx_get(sc->ctx, "ssl::proxy::clientcerts");
+
+    ssl_log(s, SSL_LOG_DEBUG, "Proxy client certificate callback: (%s) entered");
+
+    if ((pcerts == NULL) || (sk_X509_INFO_num(pcerts) <= 0)) {
+        ssl_log(s, SSL_LOG_TRACE,
+                "Proxy client certificate callback: (%s) "
+                "site wanted client certificate but none available", 
+                servername);
+        return 0;
+    }                                                                     
+
+    sk = SSL_get_client_CA_list(ssl);
+
+    if ((sk == NULL) || (sk_X509_NAME_num(sk) <= 0)) {
+        /* 
+         * remote site didn't send us a list of acceptable CA certs, 
+         * so lets send the first one we came across 
+         */   
+        xi = sk_X509_INFO_value(pcerts, 0);
+        cp  = X509_NAME_oneline(X509_get_subject_name(xi->x509), NULL, 0);
+        ssl_log(s, SSL_LOG_DEBUG,
+                "SSL Proxy: (%s) no acceptable CA list, sending %s", 
+                servername, cp != NULL ? cp : "-unknown-");
+        free(cp);
+        (*x509) = xi->x509;
+        (*pkey) = xi->x_pkey->dec_pkey;
+        /* Prevent OpenSSL from free'ing these structures */
+        (*x509)->references++;
+        (*pkey)->references++;     
+        return 1;
+    }         
+
+    for (i = 0; i < sk_X509_NAME_num(sk); i++) {
+        xnx = sk_X509_NAME_value(sk, i);
+        for (j = 0; j < sk_X509_INFO_num(pcerts); j++) {
+            xi = sk_X509_INFO_value(pcerts,j);
+            issuer = X509_get_issuer_name(xi->x509);
+            if (X509_NAME_cmp(issuer, xnx) == 0) {
+                cp = X509_NAME_oneline(X509_get_subject_name(xi->x509), NULL, 0);
+                ssl_log(s, SSL_LOG_DEBUG, "SSL Proxy: (%s) sending %s", 
+                        servername, cp != NULL ? cp : "-unknown-");
+                free(cp);
+                (*x509) = xi->x509;
+                (*pkey) = xi->x_pkey->dec_pkey;
+                /* Prevent OpenSSL freeing these structures */
+                (*x509)->references++;
+                (*pkey)->references++;     
+                return 1;
+            }
+        }
+    }
+    ssl_log(s, SSL_LOG_TRACE,
+            "Proxy client certificate callback: (%s) "
+            "no client certificate found!?", servername);
+    return 0; 
+}
+
+/*
+ * This is the verify callback when we are connecting to a remote SSL server
+ * from the proxy. Information is passed in via the SSL "ctx" app_data
+ * mechanism. We pass in an Apache context in this field, which contains
+ * server_rec of the server making the proxy connection from the
+ * "ssl::proxy::server_rec" context.
+ *
+ * The result of the verification is passed back out to SSLERR via the return
+ * value. We also store the error message in the "proxyverifyfailed" context,
+ * so the caller of SSL_connect() can log a detailed error message.
+ */
+static int ssl_ext_mp_verify_cb(int ok, X509_STORE_CTX *ctx)
+{
+    SSLSrvConfigRec *sc;
+    X509 *xs;
+    int errnum;
+    int errdepth;
+    char *cp, *cp2;
+    ap_ctx *pCtx;
+    server_rec *s;
+    SSL *ssl;
+    char *peer;
+    char *servername;
+
+    ssl        = (SSL *)X509_STORE_CTX_get_app_data(ctx);
+    pCtx       = (ap_ctx *)SSL_get_app_data(ssl);
+    s          = ap_ctx_get(pCtx, "ssl::proxy::server_rec");
+    peer       = ap_ctx_get(pCtx, "ssl::proxy::peer");
+    servername = ap_ctx_get(pCtx, "ssl::proxy::servername");
+    sc         = mySrvConfig(s);
+
+    /*
+     * Get verify ingredients
+     */
+    xs       = X509_STORE_CTX_get_current_cert(ctx);
+    errnum   = X509_STORE_CTX_get_error(ctx);
+    errdepth = X509_STORE_CTX_get_error_depth(ctx);
+
+    /* 
+     * Log verification information
+     */
+    cp  = X509_NAME_oneline(X509_get_subject_name(xs), NULL, 0);
+    cp2 = X509_NAME_oneline(X509_get_issuer_name(xs),  NULL, 0);
+    ssl_log(s, SSL_LOG_DEBUG,
+            "SSL Proxy: (%s) Certificate Verification for remote server %s: "
+            "depth: %d, subject: %s, issuer: %s", 
+            servername, peer != NULL ? peer : "-unknown-",
+            errdepth, cp != NULL ? cp : "-unknown-", 
+            cp2 != NULL ? cp2 : "-unknown");
+    free(cp);
+    free(cp2);
+
+    /*
+     * If we already know it's not ok, log the real reason
+     */
+    if (!ok) {
+        ssl_log(s, SSL_LOG_ERROR,
+                "SSL Proxy: (%s) Certificate Verification failed for %s: "
+                "Error (%d): %s", servername,
+                peer != NULL ? peer : "-unknown-",
+                errnum, X509_verify_cert_error_string(errnum));
+        ap_ctx_set(pCtx, "ssl::proxy::verifyerror", 
+                   (void *)X509_verify_cert_error_string(errnum));
+        return ok;
+    }
+
+    /*
+     * Check the depth of the certificate chain
+     */
+    if (sc->nProxyVerifyDepth > 0) {
+        if (errdepth > sc->nProxyVerifyDepth) {
+            ssl_log(s, SSL_LOG_ERROR,
+                "SSL Proxy: (%s) Certificate Verification failed for %s: "
+                "Certificate Chain too long "
+                "(chain has %d certificates, but maximum allowed are only %d)", 
+                servername, peer, errdepth, sc->nProxyVerifyDepth);
+            ap_ctx_set(pCtx, "ssl::proxy::verifyerror",
+                       (void 
+*)X509_verify_cert_error_string(X509_V_ERR_CERT_CHAIN_TOO_LONG));
+            ok = FALSE;
+        }
+    }
+
+    /*
+     * And finally signal OpenSSL the (perhaps changed) state
+     */
+    return (ok);
+}
 
 /*  _________________________________________________________________
 **
Index: modules/ssl/ssl_engine_init.c
===================================================================
RCS file: /e/modssl/cvs/mod_ssl/pkg.apache/src/modules/ssl/ssl_engine_init.c,v
retrieving revision 1.74
diff -u -r1.74 ssl_engine_init.c
--- modules/ssl/ssl_engine_init.c       1999/06/18 14:14:52     1.74
+++ modules/ssl/ssl_engine_init.c       1999/06/22 07:01:05
@@ -152,6 +152,8 @@
             sc->nVerifyClient = SSL_CVERIFY_NONE;
         if (sc->nVerifyDepth == UNSET)
             sc->nVerifyDepth = 1;
+        if (sc->nProxyVerifyDepth == UNSET)
+            sc->nProxyVerifyDepth = 1;
         if (sc->nSessionCacheTimeout == UNSET)
             sc->nSessionCacheTimeout = SSL_SESSION_CACHE_TIMEOUT;
         if (sc->nPassPhraseDialogType == SSL_PPTYPE_UNSET)
Index: modules/ssl/ssl_util_ssl.c
===================================================================
RCS file: /e/modssl/cvs/mod_ssl/pkg.apache/src/modules/ssl/ssl_util_ssl.c,v
retrieving revision 1.13
diff -u -r1.13 ssl_util_ssl.c
--- modules/ssl/ssl_util_ssl.c  1999/06/20 08:49:05     1.13
+++ modules/ssl/ssl_util_ssl.c  1999/06/22 07:05:51
@@ -358,3 +358,54 @@
     return TRUE;
 }
 
+/*  _________________________________________________________________
+**
+**  Low-Level CA Certificate Loading
+**  _________________________________________________________________
+*/
+
+BOOL SSL_CA_load_certs_file(pool *p, STACK_OF(X509_INFO) *sk, char *filename)
+{
+    BIO *in;
+
+    if ((in = BIO_new(BIO_s_file())) == NULL)
+        return FALSE;
+    if (BIO_read_filename(in, filename) <= 0) {
+        BIO_free(in);
+        return FALSE;
+    }
+    ERR_clear_error();
+    PEM_X509_INFO_read_bio(in, sk, NULL);
+    BIO_free(in);
+    return TRUE;
+}
+
+BOOL SSL_CA_load_certs_path(pool *p, STACK_OF(X509_INFO) *sk, char *pathname)
+{
+    struct stat st;
+    DIR *dir;
+    pool *sp;
+    struct dirent *nextent;
+    char *fullname;
+    BOOL ok;
+
+    sp = ap_make_sub_pool(p);
+    if ((dir = ap_popendir(sp, pathname)) == NULL) {
+        ap_destroy_pool(sp);
+        return FALSE;
+    }
+    ok = FALSE;
+    while ((nextent = readdir(dir)) != NULL) {
+        fullname = ap_pstrcat(sp, pathname, "/", nextent->d_name, NULL);
+        if (stat(fullname, &st) != 0)
+            continue;
+        if (!S_ISREG(st.st_mode))
+            continue;
+        if (SSL_CA_load_certs_file(sp, sk, fullname))
+            ok = TRUE;
+    }
+    ap_pclosedir(p, dir);
+    ap_destroy_pool(sp);
+    return ok;
+}              
+
Index: modules/ssl/ssl_util_ssl.h
===================================================================
RCS file: /e/modssl/cvs/mod_ssl/pkg.apache/src/modules/ssl/ssl_util_ssl.h,v
retrieving revision 1.12
diff -u -r1.12 ssl_util_ssl.h
--- modules/ssl/ssl_util_ssl.h  1999/06/18 14:14:53     1.12
+++ modules/ssl/ssl_util_ssl.h  1999/06/22 07:05:36
@@ -97,5 +97,7 @@
 char       *SSL_make_ciphersuite(pool *, SSL *);
 BOOL        SSL_X509_isSGC(X509 *);
 BOOL        SSL_X509_getBC(X509 *, int *, int *);
+BOOL        SSL_CA_load_certs_file(pool *, STACK_OF(X509_INFO) *, char *);
+BOOL        SSL_CA_load_certs_path(pool *, STACK_OF(X509_INFO) *, char *);
 
 #endif /* SSL_UTIL_SSL_H */
______________________________________________________________________
Apache Interface to OpenSSL (mod_ssl)                   www.modssl.org
User Support Mailing List                      [EMAIL PROTECTED]
Automated List Manager                            [EMAIL PROTECTED]

Reply via email to