Commit 2c30fa3f authored by William A. Rowe Jr's avatar William A. Rowe Jr
Browse files

mod_ssl: Add SSLProxyMachineCertificateChainFile directive uses openssl

to construct a chain for each proxy cert. When a remote server requests
a client certificate that is NOT the direct issuer of any available client
certificate, the chain for that certificate will be used to trace it to a
known CA and that client certificate will be used. 

Submitted by: druggeri
Reviewed by: kbrand, rpluem
Backports: 1160863,1162103,1170833,1172010,1175946,1242089



git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/branches/2.2.x@1374374 13f79535-47bb-0310-9956-ffa450edef68
parent e7cf20d7
Loading
Loading
Loading
Loading
+4 −0
Original line number Diff line number Diff line
@@ -5,6 +5,10 @@ Changes with Apache 2.2.23
     envvars: Fix insecure handling of LD_LIBRARY_PATH that could lead to the
     current working directory to be searched for DSOs. [Stefan Fritsch]

  *) Added SSLProxyMachineCertificateChainFile directive so the proxy client
     can select the proper client certificate when using a chain and the
     remote server only lists the root CA as allowed.

  *) mpm_event, mpm_worker: Remain active amidst prevalent child process
     resource shortages.  [Jeff Trawick]

+31 −0
Original line number Diff line number Diff line
@@ -1457,6 +1457,37 @@ 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>
<compatibility>Available in Apache 2.2.22 and later</compatibility>

<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>
<note type="warning"><title>Security warning</title>
<p>If this directive is enabled, all of the certificates in the file will be
trusted as if they were also in <directive module="mod_ssl">
SSLProxyCACertificateFile</directive>.</p>
</note>
<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>
+4 −0
Original line number Diff line number Diff line
@@ -189,6 +189,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,
+18 −0
Original line number Diff line number Diff line
@@ -141,7 +141,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,
@@ -233,6 +235,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,
@@ -1478,6 +1481,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)
+86 −0
Original line number Diff line number Diff line
@@ -975,6 +975,9 @@ static void ssl_init_proxy_certs(server_rec *s,
{
    int n, ncerts = 0;
    STACK_OF(X509_INFO) *sk;
    STACK_OF(X509) *chain;
    X509_STORE_CTX *sctx;
    X509_STORE *store = SSL_CTX_get_cert_store(mctx->ssl_ctx);
    modssl_pk_proxy_t *pkp = mctx->pkp;

    SSL_CTX_set_client_cert_cb(mctx->ssl_ctx,
@@ -1020,6 +1023,78 @@ 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 || !store) {
        return;
    }

    /* Load all of the CA certs and construct a chain */
    pkp->ca_certs = (STACK_OF(X509) **) apr_pcalloc(p, ncerts * sizeof(sk));
    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(APLOG_MARK, APLOG_EMERG, s);
        ssl_die();
    }

    X509_STORE_load_locations(store, pkp->ca_cert_file, NULL);

    for (n = 0; n < ncerts; n++) {
        int i;

        X509_INFO *inf = sk_X509_INFO_value(pkp->certs, n);
        X509_NAME *name = X509_get_subject_name(inf->x509);
        char *cert_dn = SSL_X509_NAME_to_string(ptemp, name, 0);
        X509_STORE_CTX_init(sctx, store, inf->x509, NULL);

        /* Attempt to verify the client cert */
        if (X509_verify_cert(sctx) != 1) {
            int err = X509_STORE_CTX_get_error(sctx);
            ap_log_error(APLOG_MARK, APLOG_WARNING, 0, s,
                         "SSL proxy client cert chain verification failed for %s: %s",
                         cert_dn, X509_verify_cert_error_string(err));
        }

        /* Clear X509_verify_cert errors */
        ERR_clear_error();

        /* Obtain a copy of the verified chain */
        chain = X509_STORE_CTX_get1_chain(sctx);

        if (chain != NULL) {
            /* Discard end entity cert from the chain */
            X509_free(sk_X509_shift(chain));

            if ((i = sk_X509_num(chain)) > 0) {
                /* Store the chain for later use */
                pkp->ca_certs[n] = chain;
            }
            else {
                /* Discard empty chain */
                sk_X509_pop_free(chain, X509_free);
                pkp->ca_certs[n] = NULL;
            }

            ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
                         "loaded %i intermediate CA%s for cert %i (%s)",
                         i, i == 1 ? "" : "s", n, cert_dn);
            if (i > 0) {
                int j;
                for (j = 0; j < i; j++) {
                    X509_NAME *ca_name = X509_get_subject_name(sk_X509_value(chain, j));
                    char *ca_dn = SSL_X509_NAME_to_string(ptemp, ca_name, 0);
                    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, "%i: %s", j, ca_dn);
                }
            }
        }

        /* get ready for next X509_STORE_CTX_init */
        X509_STORE_CTX_cleanup(sctx);
    }

    X509_STORE_CTX_free(sctx);
}

static void ssl_init_proxy_ctx(server_rec *s,
@@ -1302,6 +1377,17 @@ static void ssl_init_ctx_cleanup_proxy(modssl_ctx_t *mctx)
    ssl_init_ctx_cleanup(mctx);

    if (mctx->pkp->certs) {
        int i = 0;
        int ncerts = sk_X509_INFO_num(mctx->pkp->certs);

        if (mctx->pkp->ca_certs) {
            for (i = 0; i < ncerts; i++) {
                if (mctx->pkp->ca_certs[i] != NULL) {
                    sk_X509_pop_free(mctx->pkp->ca_certs[i], X509_free);
                }
            }
        }

        sk_X509_INFO_pop_free(mctx->pkp->certs, X509_INFO_free);
        mctx->pkp->certs = NULL;
    }
Loading