Commit af6f2c49 authored by Doug MacEachern's avatar Doug MacEachern
Browse files

implement proxy client certificate callback

(uses SSLProxyMachineCertificate{File,Cert} when downstream server
requires a client certificate)


git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk/modules/ssl@94329 13f79535-47bb-0310-9956-ffa450edef68
parent 79cd12cf
Loading
Loading
Loading
Loading
+1 −0
Changes for mod_ssl.h: 1 added line, 0 removed lines.
Original line number Diff line number Diff line
@@ -628,6 +628,7 @@ RSA *ssl_callback_TmpRSA(SSL *, int, int);
DH          *ssl_callback_TmpDH(SSL *, int, int);
int          ssl_callback_SSLVerify(int, X509_STORE_CTX *);
int          ssl_callback_SSLVerify_CRL(int, X509_STORE_CTX *, server_rec *);
int          ssl_callback_proxy_cert(SSL *ssl, X509 **x509, EVP_PKEY **pkey);
int          ssl_callback_NewSessionCacheEntry(SSL *, SSL_SESSION *);
SSL_SESSION *ssl_callback_GetSessionCacheEntry(SSL *, unsigned char *, int, int *);
void         ssl_callback_DelSessionCacheEntry(SSL_CTX *, SSL_SESSION *);
+4 −0
Changes for ssl_engine_init.c: 4 added lines, 0 removed lines.
Original line number Diff line number Diff line
@@ -908,6 +908,9 @@ static void ssl_init_proxy_certs(server_rec *s,
    STACK_OF(X509_INFO) *sk;
    modssl_pk_proxy_t *pkp = mctx->pkp;

    SSL_CTX_set_client_cert_cb(mctx->ssl_ctx,
                               ssl_callback_proxy_cert);

    if (!(pkp->cert_file || pkp->cert_path)) {
        return;
    }
@@ -926,6 +929,7 @@ static void ssl_init_proxy_certs(server_rec *s,
        ssl_log(s, SSL_LOG_TRACE|SSL_INIT,
                "loaded %d client certs for SSL proxy",
                ncerts);

        pkp->certs = sk;
    }
    else {
+98 −0
Changes for ssl_engine_kernel.c: 98 added lines, 0 removed lines.
Original line number Diff line number Diff line
@@ -1543,6 +1543,104 @@ int ssl_callback_SSLVerify_CRL(int ok, X509_STORE_CTX *ctx, server_rec *s)
    return ok;
}

#define SSLPROXY_CERT_CB_LOG_FMT \
   "Proxy client certificate callback: (%s) "

static void modssl_proxy_info_log(server_rec *s,
                                  X509_INFO *info,
                                  const char *msg)
{
    SSLSrvConfigRec *sc = mySrvConfig(s);
    char name_buf[256];
    X509_NAME *name;
    const char *dn;

    if (sc->log_level < SSL_LOG_TRACE) {
        return;
    }

    name = X509_get_subject_name(info->x509);
    dn = X509_NAME_oneline(name, name_buf, sizeof(name_buf));

    ssl_log(s, SSL_LOG_TRACE,
            SSLPROXY_CERT_CB_LOG_FMT "%s, sending %s", 
            sc->vhost_id, msg, dn ? dn : "-uknown-");
}

/*
 * caller will decrement the cert and key reference
 * so we need to increment here to prevent them from
 * being freed.
 */
#define modssl_set_cert_info(info, cert, pkey) \
    *cert = info->x509; \
    *pkey = info->x_pkey->dec_pkey; \
    CRYPTO_add(&((*cert)->references), +1, CRYPTO_LOCK_X509_PKEY); \
    CRYPTO_add(&((*pkey)->references), +1, CRYPTO_LOCK_X509_PKEY)

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 = c->base_server;
    SSLSrvConfigRec *sc = mySrvConfig(s);
    X509_NAME *ca_name, *issuer;
    X509_INFO *info;
    STACK_OF(X509_NAME) *ca_list;
    STACK_OF(X509_INFO) *certs = sc->proxy->pkp->certs;
    int i, j;
    
    ssl_log(s, SSL_LOG_TRACE, 
            SSLPROXY_CERT_CB_LOG_FMT "entered",
            sc->vhost_id);

    if (!certs || (sk_X509_INFO_num(certs) <= 0)) {
        ssl_log(s, SSL_LOG_WARN,
                SSLPROXY_CERT_CB_LOG_FMT
                "downstream server wanted client certificate "
                "but none are configured", sc->vhost_id);
        return FALSE;
    }                                                                     

    ca_list = SSL_get_client_CA_list(ssl);

    if (!ca_list || (sk_X509_NAME_num(ca_list) <= 0)) {
        /* 
         * downstream server didn't send us a list of acceptable CA certs, 
         * so we send the first client cert in the list.
         */   
        info = sk_X509_INFO_value(certs, 0);
        
        modssl_proxy_info_log(s, info, "no acceptable CA list");

        modssl_set_cert_info(info, x509, pkey);

        return TRUE;
    }         

    for (i = 0; i < sk_X509_NAME_num(ca_list); i++) {
        ca_name = sk_X509_NAME_value(ca_list, i);

        for (j = 0; j < sk_X509_INFO_num(certs); j++) {
            info = sk_X509_INFO_value(certs, j);
            issuer = X509_get_issuer_name(info->x509);

            if (X509_NAME_cmp(issuer, ca_name) == 0) {
                modssl_proxy_info_log(s, info, "found acceptable cert");

                modssl_set_cert_info(info, x509, pkey);

                return TRUE;
            }
        }
    }

    ssl_log(s, SSL_LOG_TRACE,
            SSLPROXY_CERT_CB_LOG_FMT
            "no client certificate found!?", sc->vhost_id);

    return FALSE; 
}

static void ssl_session_log(server_rec *s,
                            const char *request,
                            unsigned char *id,