Re: mod_ssl, SSL_CLIENT_CERT_CHAIN, mod_proxy_ajp and full chain of client certificates
Bill Barker <wbarker <at> wilshire.com>
2008-10-05 22:59:20 GMT
Yes, while mod_jk has an option to send the cert chain (added a little over
18 months ago by mturk), no Tomcat connector has an option to read it. As a
result, Tomcat will read the end certificate and ignore the rest of the
chain.
This is because the AJP/1.3 protocol was created back in the days of
Servlet-2.2 (corresponding to Tomcat 3.x) and back then only the end
certificate was exposed by the Servlet-API.
Mladen's patch to mod_jk is simplier than this one, so I would prefer it to
this one. But I have no voting rights on this list :).
"Bruno Harbulot" <Bruno.Harbulot <at> manchester.ac.uk> wrote in message
news:gbt26i$9p2$1 <at> ger.gmane.org...
> 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
> <at> <at> -60,7 +60,7 <at> <at>
>
> /* 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
> <at> <at> -1157,6 +1157,11 <at> <at> 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
> <at> <at> -46,6 +46,7 <at> <at> 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);
> <at> <at> -300,6 +301,10 <at> <at> 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);
> <at> <at> -550,13 +555,18 <at> <at> 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;
> }
> <at> <at> -578,6 +588,28 <at> <at> 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);
>