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);