From e5df36d18acb41ca54b545ff02a3fc1f98d08129 Mon Sep 17 00:00:00 2001 From: Daniel Ruggeri Date: Tue, 23 Aug 2011 19:35:07 +0000 Subject: [PATCH] Add SSLProxyMachineCertificateChainFile directive and documentation for bug 50812 git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@1160863 13f79535-47bb-0310-9956-ffa450edef68 --- docs/manual/mod/mod_ssl.xml | 25 +++++++++++++++++++++ modules/ssl/mod_ssl.c | 4 ++++ modules/ssl/ssl_engine_config.c | 18 +++++++++++++++ modules/ssl/ssl_engine_init.c | 25 +++++++++++++++++++++ modules/ssl/ssl_engine_kernel.c | 25 +++++++++++++++++---- modules/ssl/ssl_private.h | 2 ++ modules/ssl/ssl_util_ssl.c | 39 +++++++++++++++++++++++++++++++++ modules/ssl/ssl_util_ssl.h | 1 + 8 files changed, 135 insertions(+), 4 deletions(-) diff --git a/docs/manual/mod/mod_ssl.xml b/docs/manual/mod/mod_ssl.xml index 43d3625909..9ea11ee4e6 100644 --- a/docs/manual/mod/mod_ssl.xml +++ b/docs/manual/mod/mod_ssl.xml @@ -1493,6 +1493,31 @@ SSLProxyMachineCertificateFile /usr/local/apache2/conf/ssl.crt/proxy.pem + +SSLProxyMachineCertificateChainFile +File of concatenated PEM-encoded CA certificates to be used by the proxy for choosing a certificate +SSLProxyMachineCertificateChainFile filename +server config +Not applicable + + +

+This directive sets the all-in-one file where you keep the certificate chain +for all of the client certs in use. This directive will be needed if the +remote server presents a list of CA certificates that are not direct signers +of one of the configured client certificates. +

+

+This referenced file is simply the concatenation of the various PEM-encoded +certificate files. Upon startup, each client certificate configured will +be examined and a chain of trust will be constructed. +

+Example +SSLProxyMachineCertificateChainFile /usr/local/apache2/conf/ssl.crt/proxyCA.pem + +
+
+ SSLProxyVerify Type of remote server Certificate verification diff --git a/modules/ssl/mod_ssl.c b/modules/ssl/mod_ssl.c index 89089fb674..2f1af8fb85 100644 --- a/modules/ssl/mod_ssl.c +++ b/modules/ssl/mod_ssl.c @@ -169,6 +169,10 @@ static const command_rec ssl_config_cmds[] = { SSL_CMD_SRV(ProxyMachineCertificatePath, TAKE1, "SSL Proxy: directory containing client certificates " "('/path/to/dir' - contains PEM encoded certificates)") + SSL_CMD_SRV(ProxyMachineCertificateChainFile, TAKE1, + "SSL Proxy: file containing issuing certificates " + "of the client certificate " + "(`/path/to/file' - PEM encoded certificates)") SSL_CMD_SRV(ProxyCheckPeerExpire, FLAG, "SSL Proxy: check the peers certificate expiration date") SSL_CMD_SRV(ProxyCheckPeerCN, FLAG, diff --git a/modules/ssl/ssl_engine_config.c b/modules/ssl/ssl_engine_config.c index fee529d0b6..d196cd470f 100644 --- a/modules/ssl/ssl_engine_config.c +++ b/modules/ssl/ssl_engine_config.c @@ -160,7 +160,9 @@ static void modssl_ctx_init_proxy(SSLSrvConfigRec *sc, mctx->pkp->cert_file = NULL; mctx->pkp->cert_path = NULL; + mctx->pkp->ca_cert_file = NULL mctx->pkp->certs = NULL; + mctx->pkp->ca_certs = NULL; } static void modssl_ctx_init_server(SSLSrvConfigRec *sc, @@ -270,6 +272,7 @@ static void modssl_ctx_cfg_merge_proxy(modssl_ctx_t *base, cfgMergeString(pkp->cert_file); cfgMergeString(pkp->cert_path); + cfgMergeString(pkp->ca_cert_file); } static void modssl_ctx_cfg_merge_server(modssl_ctx_t *base, @@ -1408,6 +1411,21 @@ const char *ssl_cmd_SSLProxyMachineCertificatePath(cmd_parms *cmd, return NULL; } +const char *ssl_cmd_SSLProxyMachineCertificateChainFile(cmd_parms *cmd, + void *dcfg, + const char *arg) +{ + SSLSrvConfigRec *sc = mySrvConfig(cmd->server); + const char *err; + + if ((err = ssl_cmd_check_file(cmd, &arg))) { + return err; + } + + sc->proxy->pkp->ca_cert_file = arg; + + return NULL; +} const char *ssl_cmd_SSLUserName(cmd_parms *cmd, void *dcfg, const char *arg) diff --git a/modules/ssl/ssl_engine_init.c b/modules/ssl/ssl_engine_init.c index fd24b7b4ef..ca8ed99b87 100644 --- a/modules/ssl/ssl_engine_init.c +++ b/modules/ssl/ssl_engine_init.c @@ -1086,6 +1086,7 @@ 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; SSL_CTX_set_client_cert_cb(mctx->ssl_ctx, ssl_callback_proxy_cert); @@ -1130,6 +1131,30 @@ static void ssl_init_proxy_certs(server_rec *s, "loaded %d client certs for SSL proxy", ncerts); pkp->certs = sk; + + if (!pkp->ca_cert_file) { + return; + } + + /* Load all of the CA certs and construct a chain */ + sk = sk_X509_INFO_new_null(); + + SSL_X509_INFO_load_file(ptemp, sk, pkp->ca_cert_file); + pkp->ca_certs = (STACK_OF(X509_INFO) **) apr_pcalloc(p, ncerts * sizeof(sk)); + + for (n = 0; n < ncerts; n++) { + int 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); + pkp->ca_certs[n] = chain; + + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, + "client certificate %i has loaded %i " + "intermediary signers ", n, len); + } + + sk_X509_INFO_free(sk); } static void ssl_init_proxy_ctx(server_rec *s, diff --git a/modules/ssl/ssl_engine_kernel.c b/modules/ssl/ssl_engine_kernel.c index f516d83542..9ed669dc2b 100644 --- a/modules/ssl/ssl_engine_kernel.c +++ b/modules/ssl/ssl_engine_kernel.c @@ -1798,11 +1798,12 @@ int ssl_callback_proxy_cert(SSL *ssl, X509 **x509, EVP_PKEY **pkey) conn_rec *c = (conn_rec *)SSL_get_app_data(ssl); server_rec *s = mySrvFromConn(c); SSLSrvConfigRec *sc = mySrvConfig(s); - X509_NAME *ca_name, *issuer; - X509_INFO *info; + X509_NAME *ca_name, *issuer, *ca_issuer; + X509_INFO *info, *ca_info; STACK_OF(X509_NAME) *ca_list; STACK_OF(X509_INFO) *certs = sc->proxy->pkp->certs; - int i, j; + STACK_OF(X509_INFO) *ca_certs; + int i, j, k; ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, SSLPROXY_CERT_CB_LOG_FMT "entered", @@ -1839,6 +1840,7 @@ int ssl_callback_proxy_cert(SSL *ssl, X509 **x509, EVP_PKEY **pkey) info = sk_X509_INFO_value(certs, j); issuer = X509_get_issuer_name(info->x509); + /* Search certs (by issuer name) one by one*/ if (X509_NAME_cmp(issuer, ca_name) == 0) { modssl_proxy_info_log(s, info, "found acceptable cert"); @@ -1846,7 +1848,22 @@ int ssl_callback_proxy_cert(SSL *ssl, X509 **x509, EVP_PKEY **pkey) return TRUE; } - } + + /* Failed to find direct issuer - search intermediaries (by issuer name) */ + ca_certs = sc->proxy->pkp->ca_certs[j]; + for (k = 0; k < sk_X509_INFO_num(ca_certs); k++) { + ca_info = sk_X509_INFO_value(ca_certs, k); + ca_issuer = X509_get_issuer_name(ca_info->x509); + + if(X509_NAME_cmp(ca_issuer, ca_name) == 0 ) { + modssl_proxy_info_log(s, info, "found acceptable cert by intermediary"); + + modssl_set_cert_info(info, x509, pkey); + + return TRUE; + } + } /* end loop through chained certs */ + } /* end loop through available certs */ } ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, diff --git a/modules/ssl/ssl_private.h b/modules/ssl/ssl_private.h index d25337710d..96e47af33a 100644 --- a/modules/ssl/ssl_private.h +++ b/modules/ssl/ssl_private.h @@ -527,7 +527,9 @@ typedef struct { /** proxy can have any number of cert/key pairs */ const char *cert_file; const char *cert_path; + const char *ca_cert_file; STACK_OF(X509_INFO) *certs; + STACK_OF(X509_INFO) **ca_certs; /* ptr to array of ptrs */ } modssl_pk_proxy_t; /** stuff related to authentication that can also be per-dir */ diff --git a/modules/ssl/ssl_util_ssl.c b/modules/ssl/ssl_util_ssl.c index e0c07967bf..3833456c32 100644 --- a/modules/ssl/ssl_util_ssl.c +++ b/modules/ssl/ssl_util_ssl.c @@ -434,6 +434,45 @@ BOOL SSL_X509_INFO_load_path(apr_pool_t *ptemp, return ok; } +/* + * Construct a stack of X509_INFO containing only certificates + * that have signed the provided certificate or are an intermediary + * signer of the certificate +*/ +int SSL_X509_INFO_create_chain(const X509 *x509, + STACK_OF(X509_INFO) *ca_certs, + STACK_OF(X509_INFO) *chain) +{ + int can_proceed=1; + int len=0; + int i; + X509 *certificate = (X509 *)x509; + X509_INFO *info; + X509_NAME *cert_issuer_name, *ca_name, *ca_issuer_name; + + while (can_proceed) { + can_proceed = 0; + cert_issuer_name = X509_get_issuer_name(certificate); + + for (i = 0; i < sk_X509_INFO_num(ca_certs); i++) { + info = sk_X509_INFO_value(ca_certs, i); + ca_name = X509_get_subject_name(info->x509); + ca_issuer_name = X509_get_issuer_name(info->x509); + + if (X509_NAME_cmp(cert_issuer_name, ca_name) == 0) { + /* Check for a self-signed cert (no issuer) */ + can_proceed=X509_NAME_cmp(ca_name, ca_issuer_name) == 0 ? 0 : 1; + len++; + certificate = info->x509; + sk_X509_INFO_unshift(chain, info); + break; + } + } + } + + return len; +} + /* _________________________________________________________________ ** ** Extra Server Certificate Chain Support diff --git a/modules/ssl/ssl_util_ssl.h b/modules/ssl/ssl_util_ssl.h index 1b553e2278..d5a8990859 100644 --- a/modules/ssl/ssl_util_ssl.h +++ b/modules/ssl/ssl_util_ssl.h @@ -74,6 +74,7 @@ BOOL SSL_X509_INFO_load_file(apr_pool_t *, STACK_OF(X509_INFO) *, const c BOOL SSL_X509_INFO_load_path(apr_pool_t *, STACK_OF(X509_INFO) *, const char *); int SSL_CTX_use_certificate_chain(SSL_CTX *, char *, int, pem_password_cb *); char *SSL_SESSION_id2sz(unsigned char *, int, char *, int); +int SSL_X509_INFO_create_chain(const X509 *, STACK_OF(X509_INFO) *, STACK_OF(X509_INFO) *); #endif /* __SSL_UTIL_SSL_H__ */ /** @} */ -- 2.50.1