You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@httpd.apache.org by Bruno Harbulot <Br...@manchester.ac.uk> on 2008/09/30 13:22:57 UTC

mod_ssl, SSL_CLIENT_CERT_CHAIN, mod_proxy_ajp and full chain of client certificates

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.

Re: mod_ssl, SSL_CLIENT_CERT_CHAIN, mod_proxy_ajp and full chain of client certificates

Posted by Bruno Harbulot <Br...@manchester.ac.uk>.
Hello,

I thought that Tomcat (at least recent versions) was able to get the 
full chain, but I guess I was wrong.
I'm in fact using Jetty behind mod_jk, and it exposes the full chain of 
certificates in the "javax.servlet.request.X509Certificate" request 
attribute, as expected (I am using "JkOptions +ForwardSSLCertChain"). 
The version of mod_jk I'm using is that shipped with Ubuntu 8.04, so 
it's mod_jk 1.2.25. As far as I can tell, it hasn't been patched for 
this. I've also tried it successfully with the version shipped with CentOS.

I was getting the impression that mod_jk was being deprecated in favour 
of mod_proxy_ajp. I've also experienced intermittent connection problems 
between Apache Httpd and the Jetty-based application I'm using. These 
problems could be due to many things, including the way I've deployed 
the system, so I've tried to investigate it by using mod_proxy_ajp 
instead of mod_jk.

Best wishes,

Bruno.


Bill Barker wrote:
> 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" <Br...@manchester.ac.uk> wrote in message 
> news:gbt26i$9p2$1@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.


Re: mod_ssl, SSL_CLIENT_CERT_CHAIN, mod_proxy_ajp and full chain of client certificates

Posted by Bruno Harbulot <Br...@manchester.ac.uk>.

Mladen Turk wrote:
> Bill Barker wrote:
>>
>> 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 :).
>>
> 
> Right, I'll prepare something for mod_proxy as well.
> It is on my TODO list for a long time.

Thank you. As I was saying in my previous message on this list [*], I've 
been able to get the full chain of certificates with mod_jk successfully 
with Jetty. One of the reasons I wanted to change is that the Jetty team 
now recommends using mod_proxy.
In fact, they also suggest using mod_proxy_http rather than mod_proxy_ajp.
For this, I think getting the full chain of certificate would require a 
variable in mod_ssl (for example SSL_CLIENT_CERT_CHAIN as it's done 
using my patch, or something else and/or a better patch), combined with 
a custom header via mod_headers. Of course, this would also require the 
reverse proxy to clear such a user-provided request header, if present, 
to avoid spoofing. I suppose this could be useful for other containers 
behind a reverse proxy, even if they don't support AJP.

Best wishes,

Bruno.


[*] 
http://mail-archives.apache.org/mod_mbox/httpd-dev/200810.mbox/%3Cgcd6qb$9hg$1@ger.gmane.org%3E


Re: mod_ssl, SSL_CLIENT_CERT_CHAIN, mod_proxy_ajp and full chain of client certificates

Posted by Mladen Turk <mt...@apache.org>.
Bill Barker wrote:
> 
> 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 :).
>

Right, I'll prepare something for mod_proxy as well.
It is on my TODO list for a long time.

Regards
-- 
^(TM)

Re: mod_ssl, SSL_CLIENT_CERT_CHAIN, mod_proxy_ajp and full chain of client certificates

Posted by Bill Barker <wb...@wilshire.com>.
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" <Br...@manchester.ac.uk> wrote in message 
news:gbt26i$9p2$1@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
> @@ -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);
>