On 03.09.2011 22:39, Daniel Ruggeri wrote:
> updated method I'm using. Rather than storing the chain as
> STACK_OF(X509_INFO) I have switched to STACK_OF(X509) and am using the
> following function to build the chain.
> Comments are definitely appreciated as I don't have a very good frame of
> reference for using X509_verify_cert().
Switching to STACK_OF(X509) is the right thing, yes (you don't need
X509_INFO and its additional fields for the CA certs). After having
looked at ssl_init_proxy_certs() and ssl_callback_proxy_cert() a bit
more closely, I think some additional changes might be needed.
Thanks to your patch, ssl_callback_proxy_cert() will now pick the proper
cert even if the ca_list does not include the DN of the CA which signed
the cert, which is definitely an improvement. There's still one
drawback, though, IINM: for proxy connections, mod_ssl currently won't
include the intermediate CA certs of its client cert(s) in outgoing
connections.
As SSL_CTX_set_client_cert_cb(3) states:
The client_cert_cb() cannot return a complete certificate
chain, it can only return one client certificate. If the
chain only has a length of 2, the root CA certificate may
be omitted according to the TLS standard and thus a stan-
dard conforming answer can be sent to the server. For a
longer chain, the client must send the complete chain
(with the option to leave out the root CA certificate).
This can only be accomplished by either adding the inter-
mediate CA certificates into the trusted certificate
store for the SSL_CTX object (resulting in having to add
CA certificates that otherwise maybe would not be
trusted), or by adding the chain certificates using the
SSL_CTX_add_extra_chain_cert(3) function, which is only
available for the SSL_CTX object as a whole and that
therefore probably can only apply for one client certifi-
cate, making the concept of the callback function (to
allow the choice from several certificates) questionable.
SSL_CTX_add_extra_chain_cert() isn't an option for us, I assume, so the
best we can probably do in ssl_init_proxy_certs()) is to load the certs
specified via ProxyMachineCertificateChainFile with
X509_STORE_load_locations and rely on OpenSSL's "auto chain building"
afterwards.
Populating the mctx->pkp->ca_certs stacks is still required, however,
since these are needed in the callback to figure out the correct client
cert.
Attached is an *untested* patch which hopefully gives you an idea of the
approach I'm suggesting (you might still want to separate the chain
building into a function of its own, I simply left t inline in
ssl_init_proxy_certs for easier editing). Not sure if it works, but
would appreciate if you could give it a try.
Kaspar
Index: ssl_engine_init.c
===================================================================
--- ssl_engine_init.c (revision 1165347)
+++ ssl_engine_init.c (working copy)
@@ -1115,7 +1115,9 @@ static void ssl_init_proxy_certs(server_rec *s,
int n, ncerts = 0;
STACK_OF(X509_INFO) *sk;
modssl_pk_proxy_t *pkp = mctx->pkp;
- STACK_OF(X509_INFO) *chain;
+ STACK_OF(X509) *chain;
+ X509_STORE_CTX *sctx;
+ X509_STORE *store = SSL_CTX_get_cert_store(mctx->ssl_ctx);
SSL_CTX_set_client_cert_cb(mctx->ssl_ctx,
ssl_callback_proxy_cert);
@@ -1161,29 +1163,41 @@ static void ssl_init_proxy_certs(server_rec *s,
ncerts);
pkp->certs = sk;
- if (!pkp->ca_cert_file) {
+ if (!pkp->ca_cert_file || !store) {
return;
}
- /* Load all of the CA certs and construct a chain */
- sk = sk_X509_INFO_new_null();
+ sctx = X509_STORE_CTX_new();
+ if (!sctx) {
+ ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s,
+ "SSL proxy client cert initialization failed");
+ ssl_log_ssl_error(SSLLOG_MARK, APLOG_EMERG, s);
+ ssl_die();
+ }
- SSL_X509_INFO_load_file(ptemp, sk, pkp->ca_cert_file);
- pkp->ca_certs = (STACK_OF(X509_INFO) **) apr_pcalloc(p, ncerts *
sizeof(sk));
-
+ X509_STORE_load_locations(store, pkp->ca_cert_file, NULL);
+
for (n = 0; n < ncerts; n++) {
- int len;
+ int i, len;
X509_INFO *inf = sk_X509_INFO_value(pkp->certs, n);
- chain = sk_X509_INFO_new_null();
- len = SSL_X509_INFO_create_chain(inf->x509, sk, chain);
+
+ X509_STORE_CTX_init(sctx, store, inf->x509, NULL);
+ X509_verify_cert(sctx);
+ ERR_clear_error();
+
+ chain = sk_X509_new_null();
+ for (i = 0; i < sk_X509_num(sctx->chain); i++) {
+ sk_X509_push(chain, sk_X509_value(sctx->chain, i));
+ }
pkp->ca_certs[n] = chain;
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
"client certificate %i has loaded %i "
- "intermediary signers ", n, len);
+ "intermediate CAs", n, i);
}
sk_X509_INFO_free(sk);
+ X509_STORE_CTX_free(sctx);
}
static void ssl_init_proxy_ctx(server_rec *s,