]> granicus.if.org Git - apache/commitdiff
Add SSLProxyMachineCertificateChainFile directive and documentation for bug 50812
authorDaniel Ruggeri <druggeri@apache.org>
Tue, 23 Aug 2011 19:35:07 +0000 (19:35 +0000)
committerDaniel Ruggeri <druggeri@apache.org>
Tue, 23 Aug 2011 19:35:07 +0000 (19:35 +0000)
git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@1160863 13f79535-47bb-0310-9956-ffa450edef68

docs/manual/mod/mod_ssl.xml
modules/ssl/mod_ssl.c
modules/ssl/ssl_engine_config.c
modules/ssl/ssl_engine_init.c
modules/ssl/ssl_engine_kernel.c
modules/ssl/ssl_private.h
modules/ssl/ssl_util_ssl.c
modules/ssl/ssl_util_ssl.h

index 43d36259090d9209c274843a978587cea92a17eb..9ea11ee4e6b48ea4928348dc51f6f996429111eb 100644 (file)
@@ -1493,6 +1493,31 @@ SSLProxyMachineCertificateFile /usr/local/apache2/conf/ssl.crt/proxy.pem
 </usage>
 </directivesynopsis>
 
+<directivesynopsis>
+<name>SSLProxyMachineCertificateChainFile</name>
+<description>File of concatenated PEM-encoded CA certificates to be used by the proxy for choosing a certificate</description>
+<syntax>SSLProxyMachineCertificateChainFile <em>filename</em></syntax>
+<contextlist><context>server config</context></contextlist>
+<override>Not applicable</override>
+
+<usage>
+<p>
+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.
+</p>
+<p>
+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.
+</p>
+<example><title>Example</title>
+SSLProxyMachineCertificateChainFile /usr/local/apache2/conf/ssl.crt/proxyCA.pem
+</example>
+</usage>
+</directivesynopsis>
+
 <directivesynopsis>
 <name>SSLProxyVerify</name>
 <description>Type of remote server Certificate verification</description>
index 89089fb674d3b1525ddd550763b604ddded2cf5a..2f1af8fb85c114c3b9f68adce313b1acd1537c6a 100644 (file)
@@ -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,
index fee529d0b623232c7d8cda43f58a5e662666b510..d196cd470fb0ba06c248129214330ffad347d169 100644 (file)
@@ -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)
index fd24b7b4efda15c35ca11bf2ebf08d939125dfce..ca8ed99b870c99a0f5536b81b616fc2f3bbfce64 100644 (file)
@@ -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,
index f516d83542133721a8497013b6f03bb1eebd70b4..9ed669dc2b7567aadfcbe6dd0c8f525043948985 100644 (file)
@@ -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,
index d25337710d0b2c1df90cffe74e80abcb062c2bdb..96e47af33aeb9483c1f72c62ef46a921d9fb511e 100644 (file)
@@ -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 */
index e0c07967bf10caaa822bf358fb6757c8a20c9b3a..3833456c325d311322ecee35860caa870ec75952 100644 (file)
@@ -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
index 1b553e22785a6eaeff2aa0e74028fd64915660ec..d5a89908593449b4d7d3bc09423f81d929699210 100644 (file)
@@ -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__ */
 /** @} */