Hello,

I'm trying to use mod_proxy_ajp instead of mod_jk, but I'd like to be able to pass the whole client certificate chain, instead of only the end certificate. The servlet specification allows for a chain of certificates to be presented and this is indeed possible with mod_jk, using "JkOptions +ForwardSSLCertChain".

This doesn't seem to be possible using mod_proxy_ajp, which uses the content of the SSL_CLIENT_CERT variable only.

I thought I would be able to pass the chain using mod_headers. Unfortunately, there doesn't seem to be a mod_ssl variable that represents the whole chain. There is a set of variables called SSL_CLIENT_CERT_CHAIN_n (where n is an integer), but they have to be named individually.

I'm attaching the patch I've written to provide a variable called SSL_CLIENT_CERT_CHAIN, which is the concatenation of all the certificates in the chain, in PEM format. (It also sets SSL_CLIENT_CERT_CHAIN_0 when there's no chain available but just one certificate.)

A few tests with mod_headers "RequestHeader set X-ClientCertChain %{SSL_CLIENT_CERT_CHAIN}s" seem to indicate that it works.

However, I've also tried to modify mod_proxy_ajp to send the whole chain, but this doesn't work:

--- a/modules/proxy/ajp.h
+++ b/modules/proxy/ajp.h
@@ -60,7 +60,7 @@

 /* The following environment variables match mod_ssl! */
 #define AJP13_HTTPS_INDICATOR           "HTTPS"
-#define AJP13_SSL_CLIENT_CERT_INDICATOR "SSL_CLIENT_CERT"
+#define AJP13_SSL_CLIENT_CERT_INDICATOR "SSL_CLIENT_CERT_CHAIN"
 #define AJP13_SSL_CIPHER_INDICATOR      "SSL_CIPHER"
 #define AJP13_SSL_SESSION_INDICATOR     "SSL_SESSION_ID"
 #define AJP13_SSL_KEY_SIZE_INDICATOR    "SSL_CIPHER_USEKEYSIZE"

This patch has been made against the svn trunk, rev 695234.


I'm aware that my knowledge of the Apache Httpd code is limited, so this patch is likely to need improvements (there's obviously something wrong since my modification to mod_proxy_ajp doesn't work).
I'd appreciate any comments and suggestions.


Best wishes,

Bruno.
diff --git a/modules/ssl/ssl_engine_kernel.c b/modules/ssl/ssl_engine_kernel.c
index e938d05..894bef8 100644
--- a/modules/ssl/ssl_engine_kernel.c
+++ b/modules/ssl/ssl_engine_kernel.c
@@ -1157,6 +1157,11 @@ int ssl_hook_Fixup(request_rec *r)
                     apr_table_setn(env, var, val);
                 }
             }
+        } else {
+            val = ssl_var_lookup(r->pool, r->server, r->connection,
+                                 r, "SSL_CLIENT_CERT");
+
+            apr_table_setn(env, "SSL_CLIENT_CERT_CHAIN_0", val);
         }
     }
 
diff --git a/modules/ssl/ssl_engine_vars.c b/modules/ssl/ssl_engine_vars.c
index 3d688cd..e191ddd 100644
--- a/modules/ssl/ssl_engine_vars.c
+++ b/modules/ssl/ssl_engine_vars.c
@@ -46,6 +46,7 @@ static char *ssl_var_lookup_ssl_cert_remain(apr_pool_t *p, 
ASN1_UTCTIME *tm);
 static char *ssl_var_lookup_ssl_cert_serial(apr_pool_t *p, X509 *xs);
 static char *ssl_var_lookup_ssl_cert_chain(apr_pool_t *p, STACK_OF(X509) *sk, 
char *var);
 static char *ssl_var_lookup_ssl_cert_PEM(apr_pool_t *p, X509 *xs);
+static char *ssl_var_lookup_ssl_cert_chain_PEM(apr_pool_t *p, STACK_OF(X509) 
*sk);
 static char *ssl_var_lookup_ssl_cert_verify(apr_pool_t *p, conn_rec *c);
 static char *ssl_var_lookup_ssl_cipher(apr_pool_t *p, conn_rec *c, char *var);
 static void  ssl_var_lookup_ssl_cipher_bits(SSL *ssl, int *usekeysize, int 
*algkeysize);
@@ -300,6 +301,10 @@ static char *ssl_var_lookup_ssl(apr_pool_t *p, conn_rec 
*c, char *var)
     else if (ssl != NULL && strlen(var) >= 6 && strcEQn(var, "CIPHER", 6)) {
         result = ssl_var_lookup_ssl_cipher(p, c, var+6);
     }
+    else if (ssl != NULL && strcEQ(var, "CLIENT_CERT_CHAIN")) {
+        sk = SSL_get_peer_cert_chain(ssl);
+        result = ssl_var_lookup_ssl_cert_chain(p, sk, NULL);
+    }
     else if (ssl != NULL && strlen(var) > 18 && strcEQn(var, 
"CLIENT_CERT_CHAIN_", 18)) {
         sk = SSL_get_peer_cert_chain(ssl);
         result = ssl_var_lookup_ssl_cert_chain(p, sk, var+18);
@@ -550,13 +555,18 @@ static char *ssl_var_lookup_ssl_cert_chain(apr_pool_t *p, 
STACK_OF(X509) *sk, ch
 
     result = NULL;
 
-    if (strspn(var, "0123456789") == strlen(var)) {
-        n = atoi(var);
-        if (n < sk_X509_num(sk)) {
-            xs = sk_X509_value(sk, n);
-            result = ssl_var_lookup_ssl_cert_PEM(p, xs);
+    if (var != NULL) {
+        if (strspn(var, "0123456789") == strlen(var)) {
+            n = atoi(var);
+            if (n < sk_X509_num(sk)) {
+                xs = sk_X509_value(sk, n);
+                result = ssl_var_lookup_ssl_cert_PEM(p, xs);
+            }
         }
     }
+    else {
+        result = ssl_var_lookup_ssl_cert_chain_PEM(p, sk);
+    }
 
     return result;
 }
@@ -578,6 +588,28 @@ static char *ssl_var_lookup_ssl_cert_PEM(apr_pool_t *p, 
X509 *xs)
     return result;
 }
 
+static char *ssl_var_lookup_ssl_cert_chain_PEM(apr_pool_t *p, STACK_OF(X509) 
*sk)
+{
+    char *result;
+    BIO *bio;
+    X509 *xs;
+    int n;
+    int i;
+
+    if ((bio = BIO_new(BIO_s_mem())) == NULL)
+        return NULL;
+    for (i=0; i < sk_X509_num(sk); i++) {
+        xs = sk_X509_value(sk, i);
+        PEM_write_bio_X509(bio, xs);
+    }
+    n = BIO_pending(bio);
+    result = apr_pcalloc(p, n+1);
+    n = BIO_read(bio, result, n);
+    result[n] = NUL;
+    BIO_free(bio);
+    return result;
+}
+
 static char *ssl_var_lookup_ssl_cert_verify(apr_pool_t *p, conn_rec *c)
 {
     SSLConnRec *sslconn = myConnConfig(c);

Reply via email to