]> granicus.if.org Git - apache/commitdiff
Merge r1546804, r1553824, r1554192, r1555463, r1555467, r1563417, r1564760, r1565081...
authorJim Jagielski <jim@apache.org>
Sun, 2 Mar 2014 20:20:14 +0000 (20:20 +0000)
committerJim Jagielski <jim@apache.org>
Sun, 2 Mar 2014 20:20:14 +0000 (20:20 +0000)
Throw away the myCtxVar{Set,Get} abomination and introduce
a pphrase_cb_arg_t struct instead, for passing stuff between
ssl_pphrase_Handle and ssl_pphrase_Handle_CB. Prefer struct
members instead of using additional local variables, to make
the data flow more transparent. (Doesn't "vastly simplify"
the code yet, but hopefully we'll get there when further
stripping down ssl_pphrase_Handle.)

Remove the hardcoded algorithm-type dependency for the SSLCertificateFile
and SSLCertificateKeyFile directives, and deprecate SSLCertificateChainFile

Splitting the patch into smaller pieces turned out to be infeasible,
unfortunately, due to the heavily intertwined code in ssl_engine_config.c,
ssl_engine_init.c and ssl_engine_pphrase.c, which all depends on the
modssl_pk_server_t data structure. For better comprehensibility,
a detailed listing of the changes follows:

ssl_private.h
- drop the X509 certs and EVP_PKEY keys arrays from modssl_pk_server_t
- use apr_array_header_t for cert_files and key_files
- drop tPublicCert from SSLModConfigRec
- drop the ssl_algo_t struct and the SSL_ALGO_* and SSL_AIDX_* constants

ssl_engine_config.c
- change to apr_array_header_t for SSLCertificate[Key]File
- drop ssl_cmd_check_aidx_max, i.e. allow an arbitrary number of certs
  and keys (in theory; currently OpenSSL does not support more than
  one cert/key per algorithm type)
- add deprecation warning for SSLCertificateChainFile

ssl_engine_init.c
- configure server certs/keys in ssl_init_server_certs (no longer via
  ssl_pphrase_Handle in ssl_init_Module)
- in ssl_init_server_certs, read in certificates and keys with standard
  OpenSSL API functions (SSL_CTX_use_*_file), and only fall back to
  ssl_load_encrypted_pkey when encountering an encrypted private key
- drop ssl_server_import_cert, ssl_server_import_key, ssl_init_server_check,
  and ssl_init_ctx_cleanup_server
- move the "problematic re-initialization" check to ssl_init_server_ctx

ssl_engine_pphrase.c
- use servername:port:index as the key identifier, instead of the
  previously used servername:port:algorithm
- ssl_pphrase_Handle overhaul: remove all cert/public-key handling,
  make it only load a single (encrypted) private key, and rename
  to ssl_load_encrypted_pkey
- in the passphrase prompt message, show the private key file name
  instead of the vhost id and the algorithm name
- do no longer supply the algorithm name as an argument to "exec"-type
  passphrase prompting programs

ssl_util.c
- drop ssl_util_algotypeof, ssl_util_algotypestr, ssl_asn1_keystr,
  and ssl_asn1_table_keyfmt

ssl_util_ssl.{c,h}
- drop SSL_read_X509
- constify the filename arg for SSL_read_PrivateKey

CodeWarrior compiler doesnt allow vars as struct inits.

Remove per-certificate chain handling code (obsoleted by
https://git.openssl.org/gitweb/?p=openssl.git;a=commitdiff;h=b9fa413a08d436d6b522749b5e808fcd931fd943)

make the ppcb_arg initialization a bit more uniform and easier to read

Followup fix for r1553824:

also pass the file name to ssl_load_encrypted_pkey, to make sure that we
retry with the same filename we used for SSL_CTX_use_PrivateKey_file first

With OpenSSL 1.0.2 or later, enable OCSP stapling in a loop based on
SSL_CTX_set_current_cert(), near the end of ssl_init_server_ctx.

update APLOGNO for r1564760
Submitted by: kbrand, fuankg, kbrand, kbrand, kbrand, kbrand, kbrand
Reviewed/backported by: jim

git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/branches/2.4.x@1573360 13f79535-47bb-0310-9956-ffa450edef68

CHANGES
docs/manual/mod/mod_ssl.xml
modules/ssl/ssl_engine_config.c
modules/ssl/ssl_engine_init.c
modules/ssl/ssl_engine_pphrase.c
modules/ssl/ssl_private.h
modules/ssl/ssl_util.c
modules/ssl/ssl_util_ssl.c
modules/ssl/ssl_util_ssl.h

diff --git a/CHANGES b/CHANGES
index 1065b612fc95adfbf7bd88c24ea13d184c9f445f..a4b04db8d179d2c638d2bf0b68681ec2f7574362 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -2,6 +2,11 @@
 
 Changes with Apache 2.4.8
 
+  *) mod_ssl: Remove the hardcoded algorithm-type dependency for the
+     SSLCertificateFile and SSLCertificateKeyFile directives, to enable
+     future algorithm agility, and deprecate the SSLCertificateChainFile
+     directive (obsoleted by SSLCertificateFile). [Kaspar Brand]
+
   *) mod_rewrite: Add RewriteOptions InheritDown, InheritDownBefore, 
      and IgnoreInherit to allow RewriteRules to be pushed from parent scopes
      to child scopes without explicitly configuring each child scope.
index 741c35c048e1d2a53532b4580e7f262f1e8e9d9d..b9607b4ada6ca3b4afa36ac061b36c451ddce016 100644 (file)
@@ -801,25 +801,44 @@ SSLCipherSuite RSA:!EXP:!NULL:+HIGH:+MEDIUM:-LOW
 
 <directivesynopsis>
 <name>SSLCertificateFile</name>
-<description>Server PEM-encoded X.509 Certificate file</description>
+<description>Server PEM-encoded X.509 certificate data file</description>
 <syntax>SSLCertificateFile <em>file-path</em></syntax>
 <contextlist><context>server config</context>
 <context>virtual host</context></contextlist>
 
 <usage>
 <p>
-This directive points to the file with the PEM-encoded certificate,
-optionally also the corresponding private key, and - beginning with
-version 2.4.7 - DH parameters and/or an EC curve name
-for ephemeral keys (as generated by <code>openssl dhparam</code>
-and <code>openssl ecparam</code>, respectively). If the private key
-is encrypted, the pass phrase dialog is forced at startup time.
+This directive points to a file with certificate data in PEM format.
+At a minimum, the file must include an end-entity (leaf) certificate.
+Beginning with version 2.4.8, it may also include intermediate CA
+certificates, sorted from leaf to root, and obsoletes
+<directive module="mod_ssl">SSLCertificateChainFile</directive>.
 </p>
+
+<p>
+Additional optional elements are DH parameters and/or an EC curve name
+for ephemeral keys, as generated by <code>openssl dhparam</code> and
+<code>openssl ecparam</code>, respectively (supported in version 2.4.7
+or later) and finally, the end-entity certificate's private key.
+If the private key is encrypted, the pass phrase dialog is forced
+at startup time.</p>
+
+<p>
+This directive can be used multiple times (referencing different filenames)
+to support multiple algorithms for server authentication - typically
+RSA, DSA, and ECC. The number of supported algorithms depends on the
+OpenSSL version being used for mod_ssl: with version 1.0.0 or later,
+<code>openssl list-public-key-algorithms</code> will output a list
+of supported algorithms.</p>
+
 <p>
-This directive can be used up to three times (referencing different filenames)
-when both an RSA, a DSA, and an ECC based server certificate is used in
-parallel. Note that DH and ECDH parameters are only read from the first
-<directive>SSLCertificateFile</directive> directive.</p>
+When running with OpenSSL 1.0.2 or later, this directive allows
+to configure the intermediate CA chain on a per-certificate basis,
+which removes a limitation of the (now obsolete)
+<directive module="mod_ssl">SSLCertificateChainFile</directive> directive.
+DH and ECDH parameters, however, are only read from the first
+<directive>SSLCertificateFile</directive> directive, as they
+are applied independently of the authentication algorithm type.</p>
 
 <note>
 <title>DH parameter interoperability with primes > 1024 bit</title>
@@ -845,25 +864,26 @@ SSLCertificateFile /usr/local/apache2/conf/ssl.crt/server.crt
 
 <directivesynopsis>
 <name>SSLCertificateKeyFile</name>
-<description>Server PEM-encoded Private Key file</description>
+<description>Server PEM-encoded private key file</description>
 <syntax>SSLCertificateKeyFile <em>file-path</em></syntax>
 <contextlist><context>server config</context>
 <context>virtual host</context></contextlist>
 
 <usage>
 <p>
-This directive points to the PEM-encoded Private Key file for the
-server. If the Private Key is not combined with the Certificate in the
-<directive>SSLCertificateFile</directive>, use this additional directive to
-point to the file with the stand-alone Private Key. When
-<directive>SSLCertificateFile</directive> is used and the file
-contains both the Certificate and the Private Key this directive need
-not be used. But we strongly discourage this practice.  Instead we
-recommend you to separate the Certificate and the Private Key. If the
-contained Private Key is encrypted, the Pass Phrase dialog is forced
-at startup time. This directive can be used up to three times
-(referencing different filenames) when both a RSA, a DSA, and an ECC based
-private key is used in parallel.</p>
+This directive points to the PEM-encoded private key file for the
+server (the private key may also be combined with the certificate in the
+<directive module="mod_ssl">SSLCertificateFile</directive>, but this practice
+is discouraged). If the contained private key is encrypted, the pass phrase
+dialog is forced at startup time.</p>
+
+<p>
+The directive can be used multiple times (referencing different filenames)
+to support multiple algorithms for server authentication. For each
+<directive module="mod_ssl">SSLCertificateKeyFile</directive>
+directive, there must be a matching <directive>SSLCertificateFile</directive>
+directive.</p>
+
 <example><title>Example</title>
 <highlight language="config">
 SSLCertificateKeyFile /usr/local/apache2/conf/ssl.key/server.key
@@ -880,6 +900,13 @@ SSLCertificateKeyFile /usr/local/apache2/conf/ssl.key/server.key
 <context>virtual host</context></contextlist>
 
 <usage>
+<note><title>SSLCertificateChainFile is deprecated</title>
+<p><code>SSLCertificateChainFile</code> became obsolete with version 2.4.8,
+when <directive module="mod_ssl">SSLCertificateFile</directive>
+was extended to also load intermediate CA certificates from the server
+certificate file.</p>
+</note>
+
 <p>
 This directive sets the optional <em>all-in-one</em> file where you can
 assemble the certificates of Certification Authorities (CA) which form the
index d843ce546c90ca23d782c140ac36e7e92a273afb..2dda6a9532df2f62937495bd6c5c96d6a6e6c51a 100644 (file)
@@ -66,7 +66,6 @@ SSLModConfigRec *ssl_config_global_create(server_rec *s)
                                                 sizeof(ssl_randseed_t));
     mc->tVHostKeys             = apr_hash_make(pool);
     mc->tPrivateKey            = apr_hash_make(pool);
-    mc->tPublicCert            = apr_hash_make(pool);
 #if defined(HAVE_OPENSSL_ENGINE_H) && defined(HAVE_ENGINE_INIT)
     mc->szCryptoDevice         = NULL;
 #endif
@@ -190,7 +189,8 @@ static void modssl_ctx_init_server(SSLSrvConfigRec *sc,
 
     mctx->pks = apr_pcalloc(p, sizeof(*mctx->pks));
 
-    /* mctx->pks->... certs/keys are set during module init */
+    mctx->pks->cert_files = apr_array_make(p, 3, sizeof(char *));
+    mctx->pks->key_files  = apr_array_make(p, 3, sizeof(char *));
 
 #ifdef HAVE_TLS_SESSION_TICKETS
     mctx->ticket_key = apr_pcalloc(p, sizeof(*mctx->ticket_key));
@@ -314,14 +314,10 @@ static void modssl_ctx_cfg_merge_server(apr_pool_t *p,
                                         modssl_ctx_t *add,
                                         modssl_ctx_t *mrg)
 {
-    int i;
-
     modssl_ctx_cfg_merge(p, base, add, mrg);
 
-    for (i = 0; i < SSL_AIDX_MAX; i++) {
-        cfgMergeString(pks->cert_files[i]);
-        cfgMergeString(pks->key_files[i]);
-    }
+    cfgMergeArray(pks->cert_files);
+    cfgMergeArray(pks->key_files);
 
     cfgMergeString(pks->ca_name_path);
     cfgMergeString(pks->ca_name_file);
@@ -758,56 +754,20 @@ static const char *ssl_cmd_check_dir(cmd_parms *parms,
 
 }
 
-#define SSL_AIDX_CERTS 1
-#define SSL_AIDX_KEYS  2
-
-static const char *ssl_cmd_check_aidx_max(cmd_parms *parms,
-                                          const char *arg,
-                                          int idx)
-{
-    SSLSrvConfigRec *sc = mySrvConfig(parms->server);
-    const char *err, *desc=NULL, **files=NULL;
-    int i;
-
-    if ((err = ssl_cmd_check_file(parms, &arg))) {
-        return err;
-    }
-
-    switch (idx) {
-      case SSL_AIDX_CERTS:
-        desc = "certificates";
-        files = sc->server->pks->cert_files;
-        break;
-      case SSL_AIDX_KEYS:
-        desc = "private keys";
-        files = sc->server->pks->key_files;
-        break;
-    }
-
-    for (i = 0; i < SSL_AIDX_MAX; i++) {
-        if (!files[i]) {
-            files[i] = arg;
-            return NULL;
-        }
-    }
-
-    return apr_psprintf(parms->pool,
-                        "%s: only up to %d "
-                        "different %s per virtual host allowed",
-                         parms->cmd->name, SSL_AIDX_MAX, desc);
-}
-
 const char *ssl_cmd_SSLCertificateFile(cmd_parms *cmd,
                                        void *dcfg,
                                        const char *arg)
 {
-
+    SSLSrvConfigRec *sc = mySrvConfig(cmd->server);
     const char *err;
 
-    if ((err = ssl_cmd_check_aidx_max(cmd, arg, SSL_AIDX_CERTS))) {
+    if ((err = ssl_cmd_check_file(cmd, &arg))) {
         return err;
     }
 
+    *(const char **)apr_array_push(sc->server->pks->cert_files) =
+        apr_pstrdup(cmd->pool, arg);
+    
     return NULL;
 }
 
@@ -815,12 +775,16 @@ const char *ssl_cmd_SSLCertificateKeyFile(cmd_parms *cmd,
                                           void *dcfg,
                                           const char *arg)
 {
+    SSLSrvConfigRec *sc = mySrvConfig(cmd->server);
     const char *err;
 
-    if ((err = ssl_cmd_check_aidx_max(cmd, arg, SSL_AIDX_KEYS))) {
+    if ((err = ssl_cmd_check_file(cmd, &arg))) {
         return err;
     }
 
+    *(const char **)apr_array_push(sc->server->pks->key_files) =
+        apr_pstrdup(cmd->pool, arg);
+
     return NULL;
 }
 
@@ -831,6 +795,12 @@ const char *ssl_cmd_SSLCertificateChainFile(cmd_parms *cmd,
     SSLSrvConfigRec *sc = mySrvConfig(cmd->server);
     const char *err;
 
+    ap_log_error(APLOG_MARK, APLOG_WARNING|APLOG_STARTUP, 0, cmd->server,
+                 APLOGNO(02559)
+                 "The SSLCertificateChainFile directive (%s:%d) is deprecated, "
+                 "SSLCertificateFile should be used instead",
+                 cmd->directive->filename, cmd->directive->line_num);
+
     if ((err = ssl_cmd_check_file(cmd, &arg))) {
         return err;
     }
@@ -1881,8 +1851,12 @@ void ssl_hook_ConfigTest(apr_pool_t *pconf, server_rec *s)
             modssl_pk_server_t *const pks = sc->server->pks;
             int i;
 
-            for (i = 0; (i < SSL_AIDX_MAX) && pks->cert_files[i]; i++) {
-                apr_file_printf(out, "  %s\n", pks->cert_files[i]);
+            for (i = 0; (i < pks->cert_files->nelts) &&
+                        APR_ARRAY_IDX(pks->cert_files, i, const char *);
+                 i++) {
+                apr_file_printf(out, "  %s\n",
+                                APR_ARRAY_IDX(pks->cert_files,
+                                              i, const char *));
             }
         }
 
index fd95a512906095d0a37736dc494666c8be77e73a..dc3849177b9225474e93800cf3741a51007d7bc7 100644 (file)
@@ -67,6 +67,7 @@ apr_status_t ssl_init_Module(apr_pool_t *p, apr_pool_t *plog,
     SSLSrvConfigRec *sc;
     server_rec *s;
     apr_status_t rv;
+    apr_array_header_t *pphrases;
 
     if (SSLeay() < SSL_LIBRARY_VERSION) {
         ap_log_error(APLOG_MARK, APLOG_WARNING, 0, base_server, APLOGNO(01882)
@@ -188,16 +189,6 @@ apr_status_t ssl_init_Module(apr_pool_t *p, apr_pool_t *plog,
     }
 #endif
 
-    /*
-     * read server private keys/public certs into memory.
-     * decrypting any encrypted keys via configured SSLPassPhraseDialogs
-     * anything that needs to live longer than ptemp needs to also survive
-     * restarts, in which case they'll live inside s->process->pool.
-     */
-    if ((rv = ssl_pphrase_Handle(base_server, ptemp)) != APR_SUCCESS) {
-        return rv;
-    }
-
     /*
      * initialize the mutex handling
      */
@@ -215,6 +206,8 @@ apr_status_t ssl_init_Module(apr_pool_t *p, apr_pool_t *plog,
         return rv;
     }
 
+    pphrases = apr_array_make(ptemp, 2, sizeof(char *));
+
     /*
      *  initialize servers
      */
@@ -232,11 +225,19 @@ apr_status_t ssl_init_Module(apr_pool_t *p, apr_pool_t *plog,
         /*
          * Read the server certificate and key
          */
-        if ((rv = ssl_init_ConfigureServer(s, p, ptemp, sc)) != APR_SUCCESS) {
+        if ((rv = ssl_init_ConfigureServer(s, p, ptemp, sc, pphrases))
+            != APR_SUCCESS) {
             return rv;
         }
     }
 
+    if (pphrases->nelts > 0) {
+        memset(pphrases->elts, 0, pphrases->elt_size * pphrases->nelts);
+        pphrases->nelts = 0;
+        ap_log_error(APLOG_MARK, APLOG_INFO, 0, s, APLOGNO(02560)
+                     "Init: Wiped out the queried pass phrases from memory");
+    }
+
     /*
      * Configuration consistency checks
      */
@@ -296,40 +297,6 @@ apr_status_t ssl_init_Engine(server_rec *s, apr_pool_t *p)
 }
 #endif
 
-static apr_status_t ssl_init_server_check(server_rec *s,
-                                          apr_pool_t *p,
-                                          apr_pool_t *ptemp,
-                                          modssl_ctx_t *mctx)
-{
-    /*
-     * check for important parameters and the
-     * possibility that the user forgot to set them.
-     */
-    if (!mctx->pks->cert_files[0]) {
-        ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(01891)
-                "No SSL Certificate set [hint: SSLCertificateFile]");
-        return ssl_die(s);
-    }
-
-    /*
-     *  Check for problematic re-initializations
-     */
-    if (mctx->pks->certs[SSL_AIDX_RSA] ||
-        mctx->pks->certs[SSL_AIDX_DSA]
-#ifdef HAVE_ECC
-      || mctx->pks->certs[SSL_AIDX_ECC]
-#endif
-        )
-    {
-        ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(01892)
-                "Illegal attempt to re-initialise SSL for server "
-                "(SSLEngine On should go in the VirtualHost, not in global scope.)");
-        return ssl_die(s);
-    }
-
-    return APR_SUCCESS;
-}
-
 #ifdef HAVE_TLSEXT
 static apr_status_t ssl_init_ctx_tls_extensions(server_rec *s,
                                                 apr_pool_t *p,
@@ -775,8 +742,9 @@ static apr_status_t ssl_init_ctx_cert_chain(server_rec *s,
         return APR_SUCCESS;
     }
 
-    for (i = 0; (i < SSL_AIDX_MAX) && mctx->pks->cert_files[i]; i++) {
-        if (strEQ(mctx->pks->cert_files[i], chain)) {
+    for (i = 0; (i < mctx->pks->cert_files->nelts) &&
+         APR_ARRAY_IDX(mctx->pks->cert_files, i, const char *); i++) {
+        if (strEQ(APR_ARRAY_IDX(mctx->pks->cert_files, i, const char *), chain)) {
             skip_first = TRUE;
             break;
         }
@@ -842,104 +810,10 @@ static apr_status_t ssl_init_ctx(server_rec *s,
     return APR_SUCCESS;
 }
 
-static apr_status_t ssl_server_import_cert(server_rec *s,
-                                           modssl_ctx_t *mctx,
-                                           const char *id,
-                                           int idx)
-{
-    SSLModConfigRec *mc = myModConfig(s);
-    ssl_asn1_t *asn1;
-    const unsigned char *ptr;
-    const char *type = ssl_asn1_keystr(idx);
-    X509 *cert;
-
-    if (!(asn1 = ssl_asn1_table_get(mc->tPublicCert, id))) {
-        return APR_NOTFOUND;
-    }
-
-    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(02232)
-                 "Configuring %s server certificate", type);
-
-    ptr = asn1->cpData;
-    if (!(cert = d2i_X509(NULL, &ptr, asn1->nData))) {
-        ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(02233)
-                "Unable to import %s server certificate", type);
-        ssl_log_ssl_error(SSLLOG_MARK, APLOG_EMERG, s);
-        return ssl_die(s);
-    }
-
-    if (SSL_CTX_use_certificate(mctx->ssl_ctx, cert) <= 0) {
-        ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(02234)
-                "Unable to configure %s server certificate", type);
-        ssl_log_ssl_error(SSLLOG_MARK, APLOG_EMERG, s);
-        return ssl_die(s);
-    }
-
-#ifdef HAVE_OCSP_STAPLING
-    if ((mctx->pkp == FALSE) && (mctx->stapling_enabled == TRUE)) {
-        if (!ssl_stapling_init_cert(s, mctx, cert)) {
-            ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, APLOGNO(02235)
-                         "Unable to configure server certificate for stapling");
-        }
-    }
-#endif
-
-    mctx->pks->certs[idx] = cert;
-
-    return APR_SUCCESS;
-}
-
-static apr_status_t ssl_server_import_key(server_rec *s,
-                                          modssl_ctx_t *mctx,
-                                          const char *id,
-                                          int idx)
-{
-    SSLModConfigRec *mc = myModConfig(s);
-    ssl_asn1_t *asn1;
-    const unsigned char *ptr;
-    const char *type = ssl_asn1_keystr(idx);
-    int pkey_type;
-    EVP_PKEY *pkey;
-
-#ifdef HAVE_ECC
-    if (idx == SSL_AIDX_ECC)
-      pkey_type = EVP_PKEY_EC;
-    else
-#endif
-    pkey_type = (idx == SSL_AIDX_RSA) ? EVP_PKEY_RSA : EVP_PKEY_DSA;
-
-    if (!(asn1 = ssl_asn1_table_get(mc->tPrivateKey, id))) {
-        return APR_NOTFOUND;
-    }
-
-    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(02236)
-                 "Configuring %s server private key", type);
-
-    ptr = asn1->cpData;
-    if (!(pkey = d2i_PrivateKey(pkey_type, NULL, &ptr, asn1->nData)))
-    {
-        ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(02237)
-                "Unable to import %s server private key", type);
-        ssl_log_ssl_error(SSLLOG_MARK, APLOG_EMERG, s);
-        return ssl_die(s);
-    }
-
-    if (SSL_CTX_use_PrivateKey(mctx->ssl_ctx, pkey) <= 0) {
-        ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(02238)
-                "Unable to configure %s server private key", type);
-        ssl_log_ssl_error(SSLLOG_MARK, APLOG_EMERG, s);
-        return ssl_die(s);
-    }
-
-    mctx->pks->keys[idx] = pkey;
-
-    return APR_SUCCESS;
-}
-
 static void ssl_check_public_cert(server_rec *s,
                                   apr_pool_t *ptemp,
                                   X509 *cert,
-                                  int type)
+                                  const char *key_id)
 {
     int is_ca, pathlen;
 
@@ -955,132 +829,196 @@ static void ssl_check_public_cert(server_rec *s,
         if (is_ca) {
             ap_log_error(APLOG_MARK, APLOG_WARNING, 0, s, APLOGNO(01906)
                          "%s server certificate is a CA certificate "
-                         "(BasicConstraints: CA == TRUE !?)",
-                         ssl_asn1_keystr(type));
+                         "(BasicConstraints: CA == TRUE !?)", key_id);
         }
 
         if (pathlen > 0) {
             ap_log_error(APLOG_MARK, APLOG_WARNING, 0, s, APLOGNO(01907)
                          "%s server certificate is not a leaf certificate "
                          "(BasicConstraints: pathlen == %d > 0 !?)",
-                         ssl_asn1_keystr(type), pathlen);
+                         key_id, pathlen);
         }
     }
 
     if (SSL_X509_match_name(ptemp, cert, (const char *)s->server_hostname,
                             TRUE, s) == FALSE) {
         ap_log_error(APLOG_MARK, APLOG_WARNING, 0, s, APLOGNO(01909)
-                     "%s certificate configured for %s does NOT include "
-                     "an ID which matches the server name",
-                     ssl_asn1_keystr(type), (mySrvConfig(s))->vhost_id);
+                     "%s server certificate does NOT include an ID "
+                     "which matches the server name", key_id);
     }
 }
 
+/* prevent OpenSSL from showing its "Enter PEM pass phrase:" prompt */
+static int ssl_no_passwd_prompt_cb(char *buf, int size, int rwflag,
+                                   void *userdata) {
+   return 0;
+}
+
 static apr_status_t ssl_init_server_certs(server_rec *s,
                                           apr_pool_t *p,
                                           apr_pool_t *ptemp,
-                                          modssl_ctx_t *mctx)
+                                          modssl_ctx_t *mctx,
+                                          apr_array_header_t *pphrases)
 {
-    const char *rsa_id, *dsa_id;
+    SSLModConfigRec *mc = myModConfig(s);
+    const char *vhost_id = mctx->sc->vhost_id, *key_id, *certfile, *keyfile;
+    int i;
+    X509 *cert;
+    DH *dhparams;
 #ifdef HAVE_ECC
-    const char *ecc_id;
     EC_GROUP *ecparams;
     int nid;
     EC_KEY *eckey;
 #endif
-    const char *vhost_id = mctx->sc->vhost_id;
-    int i;
-    apr_status_t have_rsa, have_dsa;
-    DH *dhparams;
-#ifdef HAVE_ECC
-    apr_status_t have_ecc;
+#ifndef HAVE_SSL_CONF_CMD
+    SSL *ssl;
 #endif
 
-    rsa_id = ssl_asn1_table_keyfmt(ptemp, vhost_id, SSL_AIDX_RSA);
-    dsa_id = ssl_asn1_table_keyfmt(ptemp, vhost_id, SSL_AIDX_DSA);
-#ifdef HAVE_ECC
-    ecc_id = ssl_asn1_table_keyfmt(ptemp, vhost_id, SSL_AIDX_ECC);
-#endif
+    /* no OpenSSL default prompts for any of the SSL_CTX_use_* calls, please */
+    SSL_CTX_set_default_passwd_cb(mctx->ssl_ctx, ssl_no_passwd_prompt_cb);
+
+    /* Iterate over the SSLCertificateFile array */
+    for (i = 0; (i < mctx->pks->cert_files->nelts) &&
+                (certfile = APR_ARRAY_IDX(mctx->pks->cert_files, i,
+                                          const char *));
+         i++) {
+        key_id = apr_psprintf(ptemp, "%s:%d", vhost_id, i);
+
+        /* first the certificate (public key) */
+        if (mctx->cert_chain) {
+            if ((SSL_CTX_use_certificate_file(mctx->ssl_ctx, certfile,
+                                              SSL_FILETYPE_PEM) < 1)) {
+                ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(02561)
+                             "Failed to configure certificate %s, check %s",
+                             key_id, certfile);
+                ssl_log_ssl_error(SSLLOG_MARK, APLOG_EMERG, s);
+                return APR_EGENERAL;
+            }
+        } else {
+            if ((SSL_CTX_use_certificate_chain_file(mctx->ssl_ctx,
+                                                    certfile) < 1)) {
+                ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(02562)
+                             "Failed to configure certificate %s (with chain),"
+                             " check %s", key_id, certfile);
+                ssl_log_ssl_error(SSLLOG_MARK, APLOG_EMERG, s);
+                return APR_EGENERAL;
+            }
+        }
 
-    have_rsa = ssl_server_import_cert(s, mctx, rsa_id, SSL_AIDX_RSA);
-    if (have_rsa != APR_SUCCESS && have_rsa != APR_NOTFOUND) {
-        return have_rsa;
-    }
-    have_dsa = ssl_server_import_cert(s, mctx, dsa_id, SSL_AIDX_DSA);
-    if (have_dsa != APR_SUCCESS && have_dsa != APR_NOTFOUND) {
-        return have_dsa;
-    }
-#ifdef HAVE_ECC
-    have_ecc = ssl_server_import_cert(s, mctx, ecc_id, SSL_AIDX_ECC);
-    if (have_ecc != APR_SUCCESS && have_ecc != APR_NOTFOUND) {
-        return have_ecc;
-    }
-#endif
+        /* and second, the private key */
+        keyfile = APR_ARRAY_IDX(mctx->pks->key_files, i, const char *);
+        if (keyfile == NULL)
+            keyfile = certfile;
 
-    if ((have_rsa != APR_SUCCESS) && (have_dsa != APR_SUCCESS)
-#ifdef HAVE_ECC
-        && (have_ecc != APR_SUCCESS)
+        ERR_clear_error();
+
+        if ((SSL_CTX_use_PrivateKey_file(mctx->ssl_ctx, keyfile,
+                                         SSL_FILETYPE_PEM) < 1) &&
+            (ERR_GET_FUNC(ERR_peek_last_error())
+                != X509_F_X509_CHECK_PRIVATE_KEY)) {
+            ssl_asn1_t *asn1;
+            EVP_PKEY *pkey;
+            const unsigned char *ptr;
+
+            ERR_clear_error();
+
+            /* perhaps it's an encrypted private key, so try again */
+            ssl_load_encrypted_pkey(s, ptemp, i, keyfile, &pphrases);
+
+            if (!(asn1 = ssl_asn1_table_get(mc->tPrivateKey, key_id)) ||
+                !(ptr = asn1->cpData) ||
+                !(pkey = d2i_AutoPrivateKey(NULL, &ptr, asn1->nData)) ||
+                (SSL_CTX_use_PrivateKey(mctx->ssl_ctx, pkey) < 1)) {
+                ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(02564)
+                             "Failed to configure encrypted (?) private key %s,"
+                             " check %s", key_id, keyfile);
+                ssl_log_ssl_error(SSLLOG_MARK, APLOG_EMERG, s);
+                return APR_EGENERAL;
+            }
+        }
+
+        if (SSL_CTX_check_private_key(mctx->ssl_ctx) < 1) {
+            ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(02565)
+                         "Certificate and private key %s from %s and %s "
+                         "do not match", key_id, certfile, keyfile);
+            return APR_EGENERAL;
+        }
+
+#ifdef HAVE_SSL_CONF_CMD
+        /* 
+         * workaround for those OpenSSL versions where SSL_CTX_get0_certificate
+         * is not yet available: create an SSL struct which we dispose of
+         * as soon as we no longer need access to the cert. (Strictly speaking,
+         * SSL_CTX_get0_certificate does not depend on the SSL_CONF stuff,
+         * but there's no reliable way to check for its existence, so we
+         * assume that if SSL_CONF is available, it's OpenSSL 1.0.2 or later,
+         * and SSL_CTX_get0_certificate is implemented.)
+         */
+        if (!(cert = SSL_CTX_get0_certificate(mctx->ssl_ctx))) {
+#else
+        if (!(ssl = SSL_new(mctx->ssl_ctx)) ||
+            !(cert = SSL_get_certificate(ssl))) {
 #endif
-) {
-        ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(01910)
-                "Oops, no " KEYTYPES " server certificate found "
-                "for '%s:%d'?!", s->server_hostname, s->port);
-        return ssl_die(s);
-    }
+            ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, APLOGNO(02566)
+                         "Unable to retrieve certificate %s", key_id);
+#ifndef HAVE_SSL_CONF_CMD
+            if (ssl)
+                SSL_free(ssl);
+#endif
+            return APR_EGENERAL;
+        }
 
-    for (i = 0; i < SSL_AIDX_MAX; i++) {
-        ssl_check_public_cert(s, ptemp, mctx->pks->certs[i], i);
-    }
+        /* warn about potential cert issues */
+        ssl_check_public_cert(s, ptemp, cert, key_id);
 
-    have_rsa = ssl_server_import_key(s, mctx, rsa_id, SSL_AIDX_RSA);
-    if (have_rsa != APR_SUCCESS && have_rsa != APR_NOTFOUND) {
-        return have_rsa;
-    }
-    have_dsa = ssl_server_import_key(s, mctx, dsa_id, SSL_AIDX_DSA);
-    if (have_dsa != APR_SUCCESS && have_dsa != APR_NOTFOUND) {
-        return have_dsa;
-    }
-#ifdef HAVE_ECC
-    have_ecc = ssl_server_import_key(s, mctx, ecc_id, SSL_AIDX_ECC);
-    if (have_ecc != APR_SUCCESS && have_ecc != APR_NOTFOUND) {
-        return have_ecc;
-    }
+#if defined(HAVE_OCSP_STAPLING) && !defined(SSL_CTRL_SET_CURRENT_CERT)
+        /* 
+         * OpenSSL up to 1.0.1: configure stapling as we go. In 1.0.2
+         * and later, there's SSL_CTX_set_current_cert, which allows
+         * iterating over all certs in an SSL_CTX (including those possibly
+         * loaded via SSLOpenSSLConfCmd Certificate), so for 1.0.2 and
+         * later, we defer to the code in ssl_init_server_ctx.
+         */
+        if ((mctx->stapling_enabled == TRUE) &&
+            !ssl_stapling_init_cert(s, mctx, cert)) {
+            ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, APLOGNO(02567)
+                         "Unable to configure certificate %s for stapling",
+                         key_id);
+        }
 #endif
 
-    if ((have_rsa != APR_SUCCESS) && (have_dsa != APR_SUCCESS)
-#ifdef HAVE_ECC
-        && (have_ecc != APR_SUCCESS)
+#ifndef HAVE_SSL_CONF_CMD
+        SSL_free(ssl);
 #endif
-          ) {
-        ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(01911)
-                "Oops, no " KEYTYPES " server private key found?!");
-        return ssl_die(s);
+
+        ap_log_error(APLOG_MARK, APLOG_INFO, 0, s, APLOGNO(02568)
+                     "Certificate and private key %s configured from %s and %s",
+                     key_id, certfile, keyfile);
     }
 
     /*
      * Try to read DH parameters from the (first) SSLCertificateFile
      */
-    if ((mctx->pks->cert_files[0] != NULL) &&
-        (dhparams = ssl_dh_GetParamFromFile(mctx->pks->cert_files[0]))) {
+    if ((certfile = APR_ARRAY_IDX(mctx->pks->cert_files, 0, const char *)) &&
+        (dhparams = ssl_dh_GetParamFromFile(certfile))) {
         SSL_CTX_set_tmp_dh(mctx->ssl_ctx, dhparams);
         ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(02540)
                      "Custom DH parameters (%d bits) for %s loaded from %s",
-                     BN_num_bits(dhparams->p), vhost_id,
-                     mctx->pks->cert_files[0]);
+                     BN_num_bits(dhparams->p), vhost_id, certfile);
     }
 
 #ifdef HAVE_ECC
     /*
      * Similarly, try to read the ECDH curve name from SSLCertificateFile...
      */
-    if ((mctx->pks->cert_files[0] != NULL) &&
-        (ecparams = ssl_ec_GetParamFromFile(mctx->pks->cert_files[0])) &&
+    if ((certfile != NULL) && 
+        (ecparams = ssl_ec_GetParamFromFile(certfile)) &&
         (nid = EC_GROUP_get_curve_name(ecparams)) &&
         (eckey = EC_KEY_new_by_curve_name(nid))) {
         SSL_CTX_set_tmp_ecdh(mctx->ssl_ctx, eckey);
         ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(02541)
                      "ECDH curve %s for %s specified in %s",
-                     OBJ_nid2sn(nid), vhost_id, mctx->pks->cert_files[0]);
+                     OBJ_nid2sn(nid), vhost_id, certfile);
     }
     /*
      * ...otherwise, enable auto curve selection (OpenSSL 1.0.2 and later)
@@ -1322,7 +1260,8 @@ static apr_status_t ssl_init_proxy_ctx(server_rec *s,
 static apr_status_t ssl_init_server_ctx(server_rec *s,
                                         apr_pool_t *p,
                                         apr_pool_t *ptemp,
-                                        SSLSrvConfigRec *sc)
+                                        SSLSrvConfigRec *sc,
+                                        apr_array_header_t *pphrases)
 {
     apr_status_t rv;
 #ifdef HAVE_SSL_CONF_CMD
@@ -1331,15 +1270,22 @@ static apr_status_t ssl_init_server_ctx(server_rec *s,
     int i;
 #endif
 
-    if ((rv = ssl_init_server_check(s, p, ptemp, sc->server)) != APR_SUCCESS) {
-        return rv;
+    /*
+     *  Check for problematic re-initializations
+     */
+    if (sc->server->ssl_ctx) {
+        ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(02569)
+                     "Illegal attempt to re-initialise SSL for server "
+                     "(SSLEngine On should go in the VirtualHost, not in global scope.)");
+        return APR_EGENERAL;
     }
 
     if ((rv = ssl_init_ctx(s, p, ptemp, sc->server)) != APR_SUCCESS) {
         return rv;
     }
 
-    if ((rv = ssl_init_server_certs(s, p, ptemp, sc->server)) != APR_SUCCESS) {
+    if ((rv = ssl_init_server_certs(s, p, ptemp, sc->server, pphrases))
+        != APR_SUCCESS) {
         return rv;
     }
 
@@ -1358,23 +1304,6 @@ static apr_status_t ssl_init_server_ctx(server_rec *s,
                          "\"SSLOpenSSLConfCmd %s %s\" applied to %s",
                          param->name, param->value, sc->vhost_id);
         }
-#ifdef HAVE_OCSP_STAPLING
-        /*
-         * Special case: if OCSP stapling is enabled, and a certificate
-         * has been loaded via "SSLOpenSSLConfCmd Certificate ...", then
-         * we also need to call ssl_stapling_init_cert here.
-         */
-        if ((sc->server->stapling_enabled == TRUE) &&
-            !strcasecmp(param->name, "Certificate")) {
-            X509 *cert = SSL_CTX_get0_certificate(sc->server->ssl_ctx);
-            if (!cert || !ssl_stapling_init_cert(s, sc->server, cert)) {
-                ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, APLOGNO(02571)
-                             "Unable to configure certificate loaded "
-                             "from %s for %s for stapling",
-                             param->value, sc->vhost_id);
-            }
-        }
-#endif
     }
 
     if (SSL_CONF_CTX_finish(cctx) == 0) {
@@ -1387,6 +1316,40 @@ static apr_status_t ssl_init_server_ctx(server_rec *s,
     SSL_CONF_CTX_free(cctx);
 #endif
 
+    if (SSL_CTX_check_private_key(sc->server->ssl_ctx) != 1) {
+        ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(02572)
+                     "Failed to configure at least one certificate and key "
+                     "for %s", sc->vhost_id);
+        ssl_log_ssl_error(SSLLOG_MARK, APLOG_EMERG, s);
+        return ssl_die(s);
+    }
+
+#if defined(HAVE_OCSP_STAPLING) && defined(SSL_CTRL_SET_CURRENT_CERT)
+    /*
+     * OpenSSL 1.0.2 and later allows iterating over all SSL_CTX certs
+     * by means of SSL_CTX_set_current_cert. Enabling stapling at this
+     * (late) point makes sure that we catch both certificates loaded
+     * via SSLCertificateFile and SSLOpenSSLConfCmd Certificate.
+     */
+    if (sc->server->stapling_enabled == TRUE) {
+        X509 *cert;
+        int i = 0;
+        int ret = SSL_CTX_set_current_cert(sc->server->ssl_ctx,
+                                           SSL_CERT_SET_FIRST);
+        while (ret) {
+            cert = SSL_CTX_get0_certificate(sc->server->ssl_ctx);
+            if (!cert || !ssl_stapling_init_cert(s, sc->server, cert)) {
+                ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, APLOGNO(02604)
+                             "Unable to configure certificate %s:%d "
+                             "for stapling", sc->vhost_id, i);
+            }
+            ret = SSL_CTX_set_current_cert(sc->server->ssl_ctx,
+                                           SSL_CERT_SET_NEXT);
+            i++;
+        }
+    }
+#endif
+
 #ifdef HAVE_TLS_SESSION_TICKETS
     if ((rv = ssl_init_ticket_key(s, p, ptemp, sc->server)) != APR_SUCCESS) {
         return rv;
@@ -1402,7 +1365,8 @@ static apr_status_t ssl_init_server_ctx(server_rec *s,
 apr_status_t ssl_init_ConfigureServer(server_rec *s,
                                       apr_pool_t *p,
                                       apr_pool_t *ptemp,
-                                      SSLSrvConfigRec *sc)
+                                      SSLSrvConfigRec *sc,
+                                      apr_array_header_t *pphrases)
 {
     apr_status_t rv;
 
@@ -1411,7 +1375,8 @@ apr_status_t ssl_init_ConfigureServer(server_rec *s,
     if ((sc->enabled == SSL_ENABLED_TRUE) || (sc->enabled == SSL_ENABLED_OPTIONAL)) {
         ap_log_error(APLOG_MARK, APLOG_INFO, 0, s, APLOGNO(01914)
                      "Configuring server %s for SSL protocol", sc->vhost_id);
-        if ((rv = ssl_init_server_ctx(s, p, ptemp, sc)) != APR_SUCCESS) {
+        if ((rv = ssl_init_server_ctx(s, p, ptemp, sc, pphrases))
+            != APR_SUCCESS) {
             return rv;
         }
     }
@@ -1688,21 +1653,6 @@ static void ssl_init_ctx_cleanup_proxy(modssl_ctx_t *mctx)
     }
 }
 
-static void ssl_init_ctx_cleanup_server(modssl_ctx_t *mctx)
-{
-    int i;
-
-    ssl_init_ctx_cleanup(mctx);
-
-    for (i=0; i < SSL_AIDX_MAX; i++) {
-        MODSSL_CFG_ITEM_FREE(X509_free,
-                             mctx->pks->certs[i]);
-
-        MODSSL_CFG_ITEM_FREE(EVP_PKEY_free,
-                             mctx->pks->keys[i]);
-    }
-}
-
 apr_status_t ssl_init_ModuleKill(void *data)
 {
     SSLSrvConfigRec *sc;
@@ -1723,9 +1673,8 @@ apr_status_t ssl_init_ModuleKill(void *data)
 
         ssl_init_ctx_cleanup_proxy(sc->proxy);
 
-        ssl_init_ctx_cleanup_server(sc->server);
+        ssl_init_ctx_cleanup(sc->server);
     }
 
     return APR_SUCCESS;
 }
-
index 342be0e9e7cb3284914037a5bdca1b0c19f162dc..df81d121984a015ee72498ca3e50f6395759e825 100644 (file)
                                            -- Clifford Stoll     */
 #include "ssl_private.h"
 
+typedef struct {
+    server_rec         *s;
+    apr_pool_t         *p;
+    apr_array_header_t *aPassPhrase;
+    int                 nPassPhraseCur;
+    char               *cpPassPhraseCur;
+    int                 nPassPhraseDialog;
+    int                 nPassPhraseDialogCur;
+    BOOL                bPassPhraseDialogOnce;
+    const char         *key_id;
+    const char         *pkey_file;
+} pphrase_cb_arg_t;
+
 /*
  * Return true if the named file exists and is readable
  */
 
-static apr_status_t exists_and_readable(char *fname, apr_pool_t *pool, apr_time_t *mtime)
+static apr_status_t exists_and_readable(const char *fname, apr_pool_t *pool,
+                                        apr_time_t *mtime)
 {
     apr_status_t stat;
     apr_finfo_t sbuf;
@@ -73,11 +87,11 @@ static apr_status_t exists_and_readable(char *fname, apr_pool_t *pool, apr_time_
  * since apr_array_push() will apr_alloc arr->nalloc * 2 elts,
  * leaving the original arr->elts to waste.
  */
-static char *asn1_table_vhost_key(SSLModConfigRec *mc, apr_pool_t *p,
-                                  char *id, char *an)
+static const char *asn1_table_vhost_key(SSLModConfigRec *mc, apr_pool_t *p,
+                                  const char *id, int i)
 {
     /* 'p' pool used here is cleared on restarts (or sooner) */
-    char *key = apr_psprintf(p, "%s:%s", id, an);
+    char *key = apr_psprintf(p, "%s:%d", id, i);
     void *keyptr = apr_hash_get(mc->tVHostKeys, key,
                                 APR_HASH_KEY_STRING);
 
@@ -103,12 +117,6 @@ static char *asn1_table_vhost_key(SSLModConfigRec *mc, apr_pool_t *p,
 static apr_file_t *writetty = NULL;
 static apr_file_t *readtty = NULL;
 
-/*
- * sslc has a nasty flaw where its
- * PEM_read_bio_PrivateKey does not take a callback arg.
- */
-static server_rec *ssl_pphrase_server_rec = NULL;
-
 int ssl_pphrase_Handle_CB(char *, int, int, void *);
 
 static char *pphrase_array_get(apr_array_header_t *arr, int idx)
@@ -120,449 +128,260 @@ static char *pphrase_array_get(apr_array_header_t *arr, int idx)
     return ((char **)arr->elts)[idx];
 }
 
-static void pphrase_array_clear(apr_array_header_t *arr)
-{
-    if (arr->nelts > 0) {
-        memset(arr->elts, 0, arr->elt_size * arr->nelts);
-    }
-    arr->nelts = 0;
-}
-
-/* Abandon all hope, ye who read this code.  Don't believe the name:
- * "passphrase handling" is really a peripheral (if complex) concern;
- * the core purpose of this function to load into memory all
- * configured certs and key from files.  The private key handling in
- * here should be split out into a separate function for improved
- * readability.  The myCtxVarGet abomination can be thrown away with
- * SSLC support, vastly simplifying the code. */
-apr_status_t ssl_pphrase_Handle(server_rec *s, apr_pool_t *p)
+apr_status_t ssl_load_encrypted_pkey(server_rec *s, apr_pool_t *p, int idx,
+                                     const char *pkey_file,
+                                     apr_array_header_t **pphrases)
 {
     SSLModConfigRec *mc = myModConfig(s);
-    SSLSrvConfigRec *sc;
-    server_rec *pServ;
-    char *cpVHostID;
-    char szPath[MAX_STRING_LEN];
-    EVP_PKEY *pPrivateKey;
+    SSLSrvConfigRec *sc = mySrvConfig(s);
+    const char *key_id = asn1_table_vhost_key(mc, p, sc->vhost_id, idx);
+    EVP_PKEY *pPrivateKey = NULL;
     ssl_asn1_t *asn1;
     unsigned char *ucp;
     long int length;
-    X509 *pX509Cert;
     BOOL bReadable;
-    apr_array_header_t *aPassPhrase;
-    int nPassPhrase;
-    int nPassPhraseCur;
-    char *cpPassPhraseCur;
-    int nPassPhraseRetry;
-    int nPassPhraseDialog;
-    int nPassPhraseDialogCur;
-    BOOL bPassPhraseDialogOnce;
-    char **cpp;
-    int i, j;
-    ssl_algo_t algoCert, algoKey, at;
-    char *an;
+    int nPassPhrase = (*pphrases)->nelts;
+    int nPassPhraseRetry = 0;
     apr_time_t pkey_mtime = 0;
     apr_status_t rv;
-    /*
-     * Start with a fresh pass phrase array
-     */
-    aPassPhrase       = apr_array_make(p, 2, sizeof(char *));
-    nPassPhrase       = 0;
-    nPassPhraseDialog = 0;
+    pphrase_cb_arg_t ppcb_arg;
+
+    if (!pkey_file) {
+         ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(02573)
+                      "Init: No private key specified for %s", key_id);
+         return ssl_die(s);
+    }
+    else if ((rv = exists_and_readable(pkey_file, p, &pkey_mtime))
+             != APR_SUCCESS ) {
+         ap_log_error(APLOG_MARK, APLOG_EMERG, rv, s, APLOGNO(02574)
+                      "Init: Can't open server private key file %s", pkey_file);
+         return ssl_die(s);
+    }
+
+    ppcb_arg.s                     = s;
+    ppcb_arg.p                     = p;
+    ppcb_arg.aPassPhrase           = *pphrases;
+    ppcb_arg.nPassPhraseCur        = 0;
+    ppcb_arg.cpPassPhraseCur       = NULL;
+    ppcb_arg.nPassPhraseDialog     = 0;
+    ppcb_arg.nPassPhraseDialogCur  = 0;
+    ppcb_arg.bPassPhraseDialogOnce = TRUE;
+    ppcb_arg.key_id                = key_id;
+    ppcb_arg.pkey_file             = pkey_file;
 
     /*
-     * Walk through all configured servers
+     * if the private key is encrypted and SSLPassPhraseDialog
+     * is configured to "builtin" it isn't possible to prompt for
+     * a password after httpd has detached from the tty.
+     * in this case if we already have a private key and the
+     * file name/mtime hasn't changed, then reuse the existing key.
+     * we also reuse existing private keys that were encrypted for
+     * exec: and pipe: dialogs to minimize chances to snoop the
+     * password.  that and pipe: dialogs might prompt the user
+     * for password, which on win32 for example could happen 4
+     * times at startup.  twice for each child and twice within
+     * each since apache "restarts itself" on startup.
+     * of course this will not work for the builtin dialog if
+     * the server was started without LoadModule ssl_module
+     * configured, then restarted with it configured.
+     * but we fall through with a chance of success if the key
+     * is not encrypted or can be handled via exec or pipe dialog.
+     * and in the case of fallthrough, pkey_mtime and isatty()
+     * are used to give a better idea as to what failed.
      */
-    for (pServ = s; pServ != NULL; pServ = pServ->next) {
-        sc = mySrvConfig(pServ);
-        cpVHostID = ssl_util_vhostid(p, pServ);
-        if (!sc->enabled) {
-            ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, pServ, APLOGNO(02199)
-                         "SSL not enabled on vhost %s, skipping SSL setup",
-                         cpVHostID);
-            continue;
+    if (pkey_mtime) {
+        ssl_asn1_t *asn1 = ssl_asn1_table_get(mc->tPrivateKey, key_id);
+        if (asn1 && (asn1->source_mtime == pkey_mtime)) {
+            ap_log_error(APLOG_MARK, APLOG_INFO, 0, s, APLOGNO(02575)
+                         "Reusing existing private key from %s on restart",
+                         ppcb_arg.pkey_file);
+            return APR_SUCCESS;
         }
+    }
 
-        ap_log_error(APLOG_MARK, APLOG_INFO, 0, pServ, APLOGNO(02200)
-                     "Loading certificate & private key of SSL-aware server '%s'",
-                     cpVHostID);
+    ap_log_error(APLOG_MARK, APLOG_INFO, 0, s, APLOGNO(02576)
+                 "Attempting to load encrypted (?) private key %s", key_id);
 
+    for (;;) {
         /*
-         * Read in server certificate(s): This is the easy part
-         * because this file isn't encrypted in any way.
+         * Try to read the private key file with the help of
+         * the callback function which serves the pass
+         * phrases to OpenSSL
          */
-        if (sc->server->pks->cert_files[0] == NULL) {
-            ap_log_error(APLOG_MARK, APLOG_EMERG, 0, pServ, APLOGNO(02240)
-                         "Server should be SSL-aware but has no certificate "
-                         "configured [Hint: SSLCertificateFile] (%s:%d)",
-                         pServ->defn_name, pServ->defn_line_number);
-            return ssl_die(pServ);
-        }
-
-        /* Bitmasks for all key algorithms configured for this server;
-         * initialize to zero. */
-        algoCert = SSL_ALGO_UNKNOWN;
-        algoKey  = SSL_ALGO_UNKNOWN;
-
-        /* Iterate through configured certificate files for this
-         * server. */
-        for (i = 0, j = 0; i < SSL_AIDX_MAX
-                 && (sc->server->pks->cert_files[i] != NULL); i++) {
-            const char *key_id;
-            int using_cache = 0;
-
-            apr_cpystrn(szPath, sc->server->pks->cert_files[i],
-                        sizeof(szPath));
-            if ((rv = exists_and_readable(szPath, p, NULL))
-                != APR_SUCCESS) {
-                ap_log_error(APLOG_MARK, APLOG_EMERG, rv, s, APLOGNO(02201)
-                             "Init: Can't open server certificate file %s",
-                             szPath);
-                return ssl_die(s);
-            }
-            if ((pX509Cert = SSL_read_X509(szPath, NULL, NULL)) == NULL) {
-                ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(02241)
-                             "Init: Unable to read server certificate from"
-                             " file %s", szPath);
-                ssl_log_ssl_error(SSLLOG_MARK, APLOG_EMERG, s);
-                return ssl_die(s);
-            }
-            ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(02202)
-                         "Init: Read server certificate from '%s'",
-                         szPath);
-            /*
-             * check algorithm type of certificate and make
-             * sure only one certificate per type is used.
-             */
-            at = ssl_util_algotypeof(pX509Cert, NULL);
-            an = ssl_util_algotypestr(at);
-            if (algoCert & at) {
-                ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(02242)
-                             "Init: Multiple %s server certificates not "
-                             "allowed", an);
-                ssl_log_ssl_error(SSLLOG_MARK, APLOG_EMERG, s);
-                return ssl_die(s);
-            }
-            algoCert |= at;
-
-            /* Determine the hash key used for this (vhost, algo-type)
-             * pair used to index both the mc->tPrivateKey and
-             * mc->tPublicCert tables: */
-            key_id = asn1_table_vhost_key(mc, p, cpVHostID, an);
 
-            /*
-             * Insert the certificate into global module configuration to let it
-             * survive the processing between the 1st Apache API init round (where
-             * we operate here) and the 2nd Apache init round (where the
-             * certificate is actually used to configure mod_ssl's per-server
-             * configuration structures).
-             */
-            length = i2d_X509(pX509Cert, NULL);
-            ucp = ssl_asn1_table_set(mc->tPublicCert, key_id, length);
-            (void)i2d_X509(pX509Cert, &ucp); /* 2nd arg increments */
+        ppcb_arg.cpPassPhraseCur = NULL;
 
-            /*
-             * Free the X509 structure
-             */
-            X509_free(pX509Cert);
+        /* Ensure that the error stack is empty; some SSL
+         * functions will fail spuriously if the error stack
+         * is not empty. */
+        ERR_clear_error();
 
-            /*
-             * Read in the private key: This is the non-trivial part, because the
-             * key is typically encrypted, so a pass phrase dialog has to be used
-             * to request it from the user (or it has to be alternatively gathered
-             * from a dialog program). The important point here is that ISPs
-             * usually have hundrets of virtual servers configured and a lot of
-             * them use SSL, so really we have to minimize the pass phrase
-             * dialogs.
-             *
-             * The idea is this: When N virtual hosts are configured and all of
-             * them use encrypted private keys with different pass phrases, we
-             * have no chance and have to pop up N pass phrase dialogs. But
-             * usually the admin is clever enough and uses the same pass phrase
-             * for more private key files (typically he even uses one single pass
-             * phrase for all). When this is the case we can minimize the dialogs
-             * by trying to re-use already known/entered pass phrases.
-             */
-            if (sc->server->pks->key_files[j] != NULL)
-                apr_cpystrn(szPath, sc->server->pks->key_files[j++], sizeof(szPath));
+        bReadable = ((pPrivateKey = SSL_read_PrivateKey(ppcb_arg.pkey_file,
+                     NULL, ssl_pphrase_Handle_CB, &ppcb_arg)) != NULL ?
+                     TRUE : FALSE);
 
-            /*
-             * Try to read the private key file with the help of
-             * the callback function which serves the pass
-             * phrases to OpenSSL
-             */
-            myCtxVarSet(mc,  1, pServ);
-            myCtxVarSet(mc,  2, p);
-            myCtxVarSet(mc,  3, aPassPhrase);
-            myCtxVarSet(mc,  4, &nPassPhraseCur);
-            myCtxVarSet(mc,  5, &cpPassPhraseCur);
-            myCtxVarSet(mc,  6, cpVHostID);
-            myCtxVarSet(mc,  7, an);
-            myCtxVarSet(mc,  8, &nPassPhraseDialog);
-            myCtxVarSet(mc,  9, &nPassPhraseDialogCur);
-            myCtxVarSet(mc, 10, &bPassPhraseDialogOnce);
-
-            nPassPhraseCur        = 0;
-            nPassPhraseRetry      = 0;
-            nPassPhraseDialogCur  = 0;
-            bPassPhraseDialogOnce = TRUE;
-
-            pPrivateKey = NULL;
-
-            for (;;) {
-                /*
-                 * Try to read the private key file with the help of
-                 * the callback function which serves the pass
-                 * phrases to OpenSSL
-                 */
-                if ((rv = exists_and_readable(szPath, p,
-                                              &pkey_mtime)) != APR_SUCCESS ) {
-                     ap_log_error(APLOG_MARK, APLOG_EMERG, rv, s, APLOGNO(02243)
-                                  "Init: Can't open server private key file "
-                                  "%s",szPath);
-                     return ssl_die(s);
-                }
-
-                /*
-                 * if the private key is encrypted and SSLPassPhraseDialog
-                 * is configured to "builtin" it isn't possible to prompt for
-                 * a password after httpd has detached from the tty.
-                 * in this case if we already have a private key and the
-                 * file name/mtime hasn't changed, then reuse the existing key.
-                 * we also reuse existing private keys that were encrypted for
-                 * exec: and pipe: dialogs to minimize chances to snoop the
-                 * password.  that and pipe: dialogs might prompt the user
-                 * for password, which on win32 for example could happen 4
-                 * times at startup.  twice for each child and twice within
-                 * each since apache "restarts itself" on startup.
-                 * of course this will not work for the builtin dialog if
-                 * the server was started without LoadModule ssl_module
-                 * configured, then restarted with it configured.
-                 * but we fall through with a chance of success if the key
-                 * is not encrypted or can be handled via exec or pipe dialog.
-                 * and in the case of fallthrough, pkey_mtime and isatty()
-                 * are used to give a better idea as to what failed.
-                 */
-                if (pkey_mtime) {
-                    ssl_asn1_t *asn1 =
-                        ssl_asn1_table_get(mc->tPrivateKey, key_id);
-
-                    if (asn1 && (asn1->source_mtime == pkey_mtime)) {
-                        ap_log_error(APLOG_MARK, APLOG_INFO,
-                                     0, pServ, APLOGNO(02244)
-                                     "%s reusing existing "
-                                     "%s private key on restart",
-                                     cpVHostID, ssl_asn1_keystr(i));
-                        using_cache = 1;
-                        break;
-                    }
-                }
+        /*
+         * when the private key file now was readable,
+         * it's fine and we go out of the loop
+         */
+        if (bReadable)
+           break;
 
-                cpPassPhraseCur = NULL;
-                ssl_pphrase_server_rec = s; /* to make up for sslc flaw */
-
-                /* Ensure that the error stack is empty; some SSL
-                 * functions will fail spuriously if the error stack
-                 * is not empty. */
-                ERR_clear_error();
-
-                bReadable = ((pPrivateKey = SSL_read_PrivateKey(szPath, NULL,
-                            ssl_pphrase_Handle_CB, s)) != NULL ? TRUE : FALSE);
-
-                /*
-                 * when the private key file now was readable,
-                 * it's fine and we go out of the loop
-                 */
-                if (bReadable)
-                   break;
-
-                /*
-                 * when we have more remembered pass phrases
-                 * try to reuse these first.
-                 */
-                if (nPassPhraseCur < nPassPhrase) {
-                    nPassPhraseCur++;
-                    continue;
-                }
+        /*
+         * when we have more remembered pass phrases
+         * try to reuse these first.
+         */
+        if (ppcb_arg.nPassPhraseCur < nPassPhrase) {
+            ppcb_arg.nPassPhraseCur++;
+            continue;
+        }
 
-                /*
-                 * else it's not readable and we have no more
-                 * remembered pass phrases. Then this has to mean
-                 * that the callback function popped up the dialog
-                 * but a wrong pass phrase was entered.  We give the
-                 * user (but not the dialog program) a few more
-                 * chances...
-                 */
+        /*
+         * else it's not readable and we have no more
+         * remembered pass phrases. Then this has to mean
+         * that the callback function popped up the dialog
+         * but a wrong pass phrase was entered.  We give the
+         * user (but not the dialog program) a few more
+         * chances...
+         */
 #ifndef WIN32
-                if ((sc->server->pphrase_dialog_type == SSL_PPTYPE_BUILTIN
-                       || sc->server->pphrase_dialog_type == SSL_PPTYPE_PIPE)
+        if ((sc->server->pphrase_dialog_type == SSL_PPTYPE_BUILTIN
+             || sc->server->pphrase_dialog_type == SSL_PPTYPE_PIPE)
 #else
-                if (sc->server->pphrase_dialog_type == SSL_PPTYPE_PIPE
+        if (sc->server->pphrase_dialog_type == SSL_PPTYPE_PIPE
 #endif
-                    && cpPassPhraseCur != NULL
-                    && nPassPhraseRetry < BUILTIN_DIALOG_RETRIES ) {
-                    apr_file_printf(writetty, "Apache:mod_ssl:Error: Pass phrase incorrect "
-                            "(%d more retr%s permitted).\n",
-                            (BUILTIN_DIALOG_RETRIES-nPassPhraseRetry),
-                            (BUILTIN_DIALOG_RETRIES-nPassPhraseRetry) == 1 ? "y" : "ies");
-                    nPassPhraseRetry++;
-                    if (nPassPhraseRetry > BUILTIN_DIALOG_BACKOFF)
-                        apr_sleep((nPassPhraseRetry-BUILTIN_DIALOG_BACKOFF)
-                                    * 5 * APR_USEC_PER_SEC);
-                    continue;
-                }
+            && ppcb_arg.cpPassPhraseCur != NULL
+            && nPassPhraseRetry < BUILTIN_DIALOG_RETRIES ) {
+            apr_file_printf(writetty, "Apache:mod_ssl:Error: Pass phrase incorrect "
+                    "(%d more retr%s permitted).\n",
+                    (BUILTIN_DIALOG_RETRIES-nPassPhraseRetry),
+                    (BUILTIN_DIALOG_RETRIES-nPassPhraseRetry) == 1 ? "y" : "ies");
+            nPassPhraseRetry++;
+            if (nPassPhraseRetry > BUILTIN_DIALOG_BACKOFF)
+                apr_sleep((nPassPhraseRetry-BUILTIN_DIALOG_BACKOFF)
+                            * 5 * APR_USEC_PER_SEC);
+            continue;
+        }
 #ifdef WIN32
-                if (sc->server->pphrase_dialog_type == SSL_PPTYPE_BUILTIN) {
-                    ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(02245)
-                                 "Init: SSLPassPhraseDialog builtin is not "
-                                 "supported on Win32 (key file "
-                                 "%s)", szPath);
-                    return ssl_die(s);
-                }
+        if (sc->server->pphrase_dialog_type == SSL_PPTYPE_BUILTIN) {
+            ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(02577)
+                         "Init: SSLPassPhraseDialog builtin is not "
+                         "supported on Win32 (key file "
+                         "%s)", ppcb_arg.pkey_file);
+            return ssl_die(s);
+        }
 #endif /* WIN32 */
 
-                /*
-                 * Ok, anything else now means a fatal error.
-                 */
-                if (cpPassPhraseCur == NULL) {
-                    if (nPassPhraseDialogCur && pkey_mtime &&
-                        !isatty(fileno(stdout))) /* XXX: apr_isatty() */
-                    {
-                        ap_log_error(APLOG_MARK, APLOG_ERR, 0,
-                                     pServ, APLOGNO(02246)
-                                     "Init: Unable to read pass phrase "
-                                     "[Hint: key introduced or changed "
-                                     "before restart?]");
-                        ssl_log_ssl_error(SSLLOG_MARK, APLOG_ERR, pServ);
-                    }
-                    else {
-                        ap_log_error(APLOG_MARK, APLOG_ERR, 0,
-                                     pServ, APLOGNO(02203) "Init: Private key not found");
-                        ssl_log_ssl_error(SSLLOG_MARK, APLOG_ERR, pServ);
-                    }
-                    if (writetty) {
-                        apr_file_printf(writetty, "Apache:mod_ssl:Error: Private key not found.\n");
-                        apr_file_printf(writetty, "**Stopped\n");
-                    }
-                }
-                else {
-                    ap_log_error(APLOG_MARK, APLOG_EMERG, 0, pServ, APLOGNO(02204)
-                                 "Init: Pass phrase incorrect for key of %s",
-                                 cpVHostID);
-                    ssl_log_ssl_error(SSLLOG_MARK, APLOG_EMERG, pServ);
-
-                    if (writetty) {
-                        apr_file_printf(writetty, "Apache:mod_ssl:Error: Pass phrase incorrect.\n");
-                        apr_file_printf(writetty, "**Stopped\n");
-                    }
-                }
-                return ssl_die(pServ);
-            }
-
-            /* If a cached private key was found, nothing more to do
-             * here; loop through to the next configured cert for this
-             * vhost. */
-            if (using_cache)
-                continue;
-
-            if (pPrivateKey == NULL) {
-                ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(02247)
-                            "Init: Unable to read server private key from "
-                            "file %s [Hint: Perhaps it is in a separate file? "
-                            "  See SSLCertificateKeyFile]", szPath);
-                ssl_log_ssl_error(SSLLOG_MARK, APLOG_EMERG, s);
-                return ssl_die(s);
-            }
-
-            /*
-             * check algorithm type of private key and make
-             * sure only one private key per type is used.
-             */
-            at = ssl_util_algotypeof(NULL, pPrivateKey);
-            an = ssl_util_algotypestr(at);
-            if (algoKey & at) {
-                ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(02248)
-                             "Init: Multiple %s server private keys not "
-                             "allowed", an);
-                ssl_log_ssl_error(SSLLOG_MARK, APLOG_EMERG, s);
-                return ssl_die(s);
-            }
-            algoKey |= at;
-
-            /*
-             * Log the type of reading
-             */
-            if (nPassPhraseDialogCur == 0) {
-                ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, pServ, APLOGNO(02249)
-                             "unencrypted %s private key - pass phrase not "
-                             "required", an);
+        /*
+         * Ok, anything else now means a fatal error.
+         */
+        if (ppcb_arg.cpPassPhraseCur == NULL) {
+            if (ppcb_arg.nPassPhraseDialogCur && pkey_mtime &&
+                !isatty(fileno(stdout))) /* XXX: apr_isatty() */
+            {
+                ap_log_error(APLOG_MARK, APLOG_ERR, 0,
+                             s, APLOGNO(02578)
+                             "Init: Unable to read pass phrase "
+                             "[Hint: key introduced or changed "
+                             "before restart?]");
+                ssl_log_ssl_error(SSLLOG_MARK, APLOG_ERR, s);
             }
             else {
-                if (cpPassPhraseCur != NULL) {
-                    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0,
-                                 pServ, APLOGNO(02250)
-                                 "encrypted %s private key - pass phrase "
-                                 "requested", an);
-                }
-                else {
-                    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0,
-                                 pServ, APLOGNO(02251)
-                                 "encrypted %s private key - pass phrase"
-                                 " reused", an);
-                }
+                ap_log_error(APLOG_MARK, APLOG_ERR, 0,
+                             s, APLOGNO(02579) "Init: Private key not found");
+                ssl_log_ssl_error(SSLLOG_MARK, APLOG_ERR, s);
             }
-
-            /*
-             * Ok, when we have one more pass phrase store it
-             */
-            if (cpPassPhraseCur != NULL) {
-                cpp = (char **)apr_array_push(aPassPhrase);
-                *cpp = cpPassPhraseCur;
-                nPassPhrase++;
+            if (writetty) {
+                apr_file_printf(writetty, "Apache:mod_ssl:Error: Private key not found.\n");
+                apr_file_printf(writetty, "**Stopped\n");
             }
-
-            /*
-             * Insert private key into the global module configuration
-             * (we convert it to a stand-alone DER byte sequence
-             * because the SSL library uses static variables inside a
-             * RSA structure which do not survive DSO reloads!)
-             */
-            length = i2d_PrivateKey(pPrivateKey, NULL);
-            ucp = ssl_asn1_table_set(mc->tPrivateKey, key_id, length);
-            (void)i2d_PrivateKey(pPrivateKey, &ucp); /* 2nd arg increments */
-
-            if (nPassPhraseDialogCur != 0) {
-                /* remember mtime of encrypted keys */
-                asn1 = ssl_asn1_table_get(mc->tPrivateKey, key_id);
-                asn1->source_mtime = pkey_mtime;
+        }
+        else {
+            ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(02580)
+                         "Init: Pass phrase incorrect for key %s",
+                         key_id);
+            ssl_log_ssl_error(SSLLOG_MARK, APLOG_EMERG, s);
+
+            if (writetty) {
+                apr_file_printf(writetty, "Apache:mod_ssl:Error: Pass phrase incorrect.\n");
+                apr_file_printf(writetty, "**Stopped\n");
             }
+        }
+        return ssl_die(s);
+    }
 
-            /*
-             * Free the private key structure
-             */
-            EVP_PKEY_free(pPrivateKey);
+    if (pPrivateKey == NULL) {
+        ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(02581)
+                     "Init: Unable to read server private key from file %s",
+                     ppcb_arg.pkey_file);
+        ssl_log_ssl_error(SSLLOG_MARK, APLOG_EMERG, s);
+        return ssl_die(s);
+    }
+
+    /*
+     * Log the type of reading
+     */
+    if (ppcb_arg.nPassPhraseDialogCur == 0) {
+        ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(02582)
+                     "unencrypted %s private key - pass phrase not "
+                     "required", key_id);
+    }
+    else {
+        if (ppcb_arg.cpPassPhraseCur != NULL) {
+            ap_log_error(APLOG_MARK, APLOG_DEBUG, 0,
+                         s, APLOGNO(02583)
+                         "encrypted %s private key - pass phrase "
+                         "requested", key_id);
+        }
+        else {
+            ap_log_error(APLOG_MARK, APLOG_DEBUG, 0,
+                         s, APLOGNO(02584)
+                         "encrypted %s private key - pass phrase"
+                         " reused", key_id);
         }
     }
 
+    /*
+     * Ok, when we have one more pass phrase store it
+     */
+    if (ppcb_arg.cpPassPhraseCur != NULL) {
+        *(const char **)apr_array_push(ppcb_arg.aPassPhrase) =
+            ppcb_arg.cpPassPhraseCur;
+        nPassPhrase++;
+    }
+
+    /*
+     * Insert private key into the global module configuration
+     * (we convert it to a stand-alone DER byte sequence
+     * because the SSL library uses static variables inside a
+     * RSA structure which do not survive DSO reloads!)
+     */
+    length = i2d_PrivateKey(pPrivateKey, NULL);
+    ucp = ssl_asn1_table_set(mc->tPrivateKey, key_id, length);
+    (void)i2d_PrivateKey(pPrivateKey, &ucp); /* 2nd arg increments */
+
+    if (ppcb_arg.nPassPhraseDialogCur != 0) {
+        /* remember mtime of encrypted keys */
+        asn1 = ssl_asn1_table_get(mc->tPrivateKey, key_id);
+        asn1->source_mtime = pkey_mtime;
+    }
+
+    /*
+     * Free the private key structure
+     */
+    EVP_PKEY_free(pPrivateKey);
+
     /*
      * Let the user know when we're successful.
      */
-    if (nPassPhraseDialog > 0) {
+    if ((ppcb_arg.nPassPhraseDialog > 0) &&
+        (ppcb_arg.cpPassPhraseCur != NULL)) {
         if (writetty) {
             apr_file_printf(writetty, "\n"
                             "OK: Pass Phrase Dialog successful.\n");
         }
     }
 
-    /*
-     * Wipe out the used memory from the
-     * pass phrase array and then deallocate it
-     */
-    if (aPassPhrase->nelts) {
-        pphrase_array_clear(aPassPhrase);
-        ap_log_error(APLOG_MARK, APLOG_INFO, 0, s, APLOGNO(02205)
-                     "Init: Wiped out the queried pass phrases from memory");
-    }
-
     /* Close the pipes if they were opened
      */
     if (readtty) {
@@ -638,45 +457,19 @@ static int pipe_get_passwd_cb(char *buf, int length, char *prompt, int verify)
 
 int ssl_pphrase_Handle_CB(char *buf, int bufsize, int verify, void *srv)
 {
-    SSLModConfigRec *mc;
-    server_rec *s;
-    apr_pool_t *p;
-    apr_array_header_t *aPassPhrase;
-    SSLSrvConfigRec *sc;
-    int *pnPassPhraseCur;
-    char **cppPassPhraseCur;
-    char *cpVHostID;
-    char *cpAlgoType;
-    int *pnPassPhraseDialog;
-    int *pnPassPhraseDialogCur;
-    BOOL *pbPassPhraseDialogOnce;
+    pphrase_cb_arg_t *ppcb_arg = (pphrase_cb_arg_t *)srv;
+    SSLSrvConfigRec *sc = mySrvConfig(ppcb_arg->s);
     char *cpp;
     int len = -1;
 
-    mc = myModConfig((server_rec *)srv);
-
-    /*
-     * Reconnect to the context of ssl_phrase_Handle()
-     */
-    s                      = myCtxVarGet(mc,  1, server_rec *);
-    p                      = myCtxVarGet(mc,  2, apr_pool_t *);
-    aPassPhrase            = myCtxVarGet(mc,  3, apr_array_header_t *);
-    pnPassPhraseCur        = myCtxVarGet(mc,  4, int *);
-    cppPassPhraseCur       = myCtxVarGet(mc,  5, char **);
-    cpVHostID              = myCtxVarGet(mc,  6, char *);
-    cpAlgoType             = myCtxVarGet(mc,  7, char *);
-    pnPassPhraseDialog     = myCtxVarGet(mc,  8, int *);
-    pnPassPhraseDialogCur  = myCtxVarGet(mc,  9, int *);
-    pbPassPhraseDialogOnce = myCtxVarGet(mc, 10, BOOL *);
-    sc                     = mySrvConfig(s);
-
-    (*pnPassPhraseDialog)++;
-    (*pnPassPhraseDialogCur)++;
+    ppcb_arg->nPassPhraseDialog++;
+    ppcb_arg->nPassPhraseDialogCur++;
 
     /*
      * When remembered pass phrases are available use them...
      */
-    if ((cpp = pphrase_array_get(aPassPhrase, *pnPassPhraseCur)) != NULL) {
+    if ((cpp = pphrase_array_get(ppcb_arg->aPassPhrase,
+                                 ppcb_arg->nPassPhraseCur)) != NULL) {
         apr_cpystrn(buf, cpp, bufsize);
         len = strlen(buf);
         return len;
@@ -692,25 +485,29 @@ int ssl_pphrase_Handle_CB(char *buf, int bufsize, int verify, void *srv)
 
         if (sc->server->pphrase_dialog_type == SSL_PPTYPE_PIPE) {
             if (!readtty) {
-                ap_log_error(APLOG_MARK, APLOG_INFO, 0, s, APLOGNO(01965)
+                ap_log_error(APLOG_MARK, APLOG_INFO, 0, ppcb_arg->s,
+                             APLOGNO(01965)
                              "Init: Creating pass phrase dialog pipe child "
                              "'%s'", sc->server->pphrase_dialog_path);
-                if (ssl_pipe_child_create(p, sc->server->pphrase_dialog_path)
+                if (ssl_pipe_child_create(ppcb_arg->p,
+                                          sc->server->pphrase_dialog_path)
                         != APR_SUCCESS) {
-                    ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, APLOGNO(01966)
+                    ap_log_error(APLOG_MARK, APLOG_ERR, 0, ppcb_arg->s,
+                                 APLOGNO(01966)
                                  "Init: Failed to create pass phrase pipe '%s'",
                                  sc->server->pphrase_dialog_path);
-                    PEMerr(PEM_F_PEM_DEF_CALLBACK,PEM_R_PROBLEMS_GETTING_PASSWORD);
+                    PEMerr(PEM_F_PEM_DEF_CALLBACK,
+                           PEM_R_PROBLEMS_GETTING_PASSWORD);
                     memset(buf, 0, (unsigned int)bufsize);
                     return (-1);
                 }
             }
-            ap_log_error(APLOG_MARK, APLOG_INFO, 0, s, APLOGNO(01967)
+            ap_log_error(APLOG_MARK, APLOG_INFO, 0, ppcb_arg->s, APLOGNO(01967)
                          "Init: Requesting pass phrase via piped dialog");
         }
         else { /* sc->server->pphrase_dialog_type == SSL_PPTYPE_BUILTIN */
 #ifdef WIN32
-            PEMerr(PEM_F_PEM_DEF_CALLBACK,PEM_R_PROBLEMS_GETTING_PASSWORD);
+            PEMerr(PEM_F_PEM_DEF_CALLBACK, PEM_R_PROBLEMS_GETTING_PASSWORD);
             memset(buf, 0, (unsigned int)bufsize);
             return (-1);
 #else
@@ -720,9 +517,9 @@ int ssl_pphrase_Handle_CB(char *buf, int bufsize, int verify, void *srv)
              * we print the prompt to stdout before EVP_read_pw_string turns
              * off tty echo
              */
-            apr_file_open_stdout(&writetty, p);
+            apr_file_open_stdout(&writetty, ppcb_arg->p);
 
-            ap_log_error(APLOG_MARK, APLOG_INFO, 0, s, APLOGNO(01968)
+            ap_log_error(APLOG_MARK, APLOG_INFO, 0, ppcb_arg->s, APLOGNO(01968)
                          "Init: Requesting pass phrase via builtin terminal "
                          "dialog");
 #endif
@@ -734,16 +531,17 @@ int ssl_pphrase_Handle_CB(char *buf, int bufsize, int verify, void *srv)
          * this terminal dialog and why to the hell he has to enter
          * something...
          */
-        if (*pnPassPhraseDialog == 1) {
+        if (ppcb_arg->nPassPhraseDialog == 1) {
             apr_file_printf(writetty, "%s mod_ssl (Pass Phrase Dialog)\n",
                             AP_SERVER_BASEVERSION);
             apr_file_printf(writetty, "Some of your private key files are encrypted for security reasons.\n");
             apr_file_printf(writetty, "In order to read them you have to provide the pass phrases.\n");
         }
-        if (*pbPassPhraseDialogOnce) {
-            *pbPassPhraseDialogOnce = FALSE;
+        if (ppcb_arg->bPassPhraseDialogOnce) {
+            ppcb_arg->bPassPhraseDialogOnce = FALSE;
             apr_file_printf(writetty, "\n");
-            apr_file_printf(writetty, "Server %s (%s)\n", cpVHostID, cpAlgoType);
+            apr_file_printf(writetty, "Private key %s (%s)\n",
+                            ppcb_arg->key_id, ppcb_arg->pkey_file);
         }
 
         /*
@@ -778,19 +576,18 @@ int ssl_pphrase_Handle_CB(char *buf, int bufsize, int verify, void *srv)
      */
     else if (sc->server->pphrase_dialog_type == SSL_PPTYPE_FILTER) {
         const char *cmd = sc->server->pphrase_dialog_path;
-        const char **argv = apr_palloc(p, sizeof(char *) * 4);
+        const char **argv = apr_palloc(ppcb_arg->p, sizeof(char *) * 3);
         char *result;
 
-        ap_log_error(APLOG_MARK, APLOG_INFO, 0, s, APLOGNO(01969)
+        ap_log_error(APLOG_MARK, APLOG_INFO, 0, ppcb_arg->s, APLOGNO(01969)
                      "Init: Requesting pass phrase from dialog filter "
                      "program (%s)", cmd);
 
         argv[0] = cmd;
-        argv[1] = cpVHostID;
-        argv[2] = cpAlgoType;
-        argv[3] = NULL;
+        argv[1] = ppcb_arg->key_id;
+        argv[2] = NULL;
 
-        result = ssl_util_readfilter(s, p, cmd, argv);
+        result = ssl_util_readfilter(ppcb_arg->s, ppcb_arg->p, cmd, argv);
         apr_cpystrn(buf, result, bufsize);
         len = strlen(buf);
     }
@@ -798,11 +595,10 @@ int ssl_pphrase_Handle_CB(char *buf, int bufsize, int verify, void *srv)
     /*
      * Ok, we now have the pass phrase, so give it back
      */
-    *cppPassPhraseCur = apr_pstrdup(p, buf);
+    ppcb_arg->cpPassPhraseCur = apr_pstrdup(ppcb_arg->p, buf);
 
     /*
      * And return its length to OpenSSL...
      */
     return (len);
 }
-
index c09da0c098cc8ea92c377c4d350370dc314fba35..516d7e654dfd04235acc3102848845cdfd7ae88b 100644 (file)
@@ -236,9 +236,6 @@ ap_set_module_config(c->conn_config, &ssl_module, val)
 #define mySrvConfigFromConn(c) mySrvConfig(mySrvFromConn(c))
 #define myModConfigFromConn(c) myModConfig(mySrvFromConn(c))
 
-#define myCtxVarSet(mc,num,val)  mc->rCtx.pV##num = val
-#define myCtxVarGet(mc,num,type) (type)(mc->rCtx.pV##num)
-
 /**
  * Defaults for the configuration
  */
@@ -261,31 +258,6 @@ ap_set_module_config(c->conn_config, &ssl_module, val)
 #define DEFAULT_OCSP_TIMEOUT 10
 #endif
 
-/**
- * Define the certificate algorithm types
- */
-
-typedef int ssl_algo_t;
-
-#define SSL_ALGO_UNKNOWN (0)
-#define SSL_ALGO_RSA     (1<<0)
-#define SSL_ALGO_DSA     (1<<1)
-#ifdef HAVE_ECC
-#define SSL_ALGO_ECC     (1<<2)
-#define SSL_ALGO_ALL     (SSL_ALGO_RSA|SSL_ALGO_DSA|SSL_ALGO_ECC)
-#else
-#define SSL_ALGO_ALL     (SSL_ALGO_RSA|SSL_ALGO_DSA)
-#endif
-
-#define SSL_AIDX_RSA     (0)
-#define SSL_AIDX_DSA     (1)
-#ifdef HAVE_ECC
-#define SSL_AIDX_ECC     (2)
-#define SSL_AIDX_MAX     (3)
-#else
-#define SSL_AIDX_MAX     (2)
-#endif
-
 /**
  * Define the SSL options
  */
@@ -502,13 +474,10 @@ typedef struct {
     apr_array_header_t   *aRandSeed;
     apr_hash_t     *tVHostKeys;
 
-    /* Two hash tables of pointers to ssl_asn1_t structures.  The
-     * structures are used to store certificates and private keys
-     * respectively, in raw DER format (serialized OpenSSL X509 and
-     * PrivateKey structures).  The tables are indexed by (vhost-id,
-     * algorithm type) using the function ssl_asn1_table_keyfmt(); for
-     * example the string "vhost.example.com:443:RSA". */
-    apr_hash_t     *tPublicCert;
+    /* A hash table of pointers to ssl_asn1_t structures.  The structures
+     * are used to store private keys in raw DER format (serialized OpenSSL
+     * PrivateKey structures).  The table is indexed by (vhost-id,
+     * index), for example the string "vhost.example.com:443:0". */
     apr_hash_t     *tPrivateKey;
 
 #if defined(HAVE_OPENSSL_ENGINE_H) && defined(HAVE_ENGINE_INIT)
@@ -520,27 +489,14 @@ typedef struct {
     ap_socache_instance_t *stapling_cache_context;
     apr_global_mutex_t   *stapling_mutex;
 #endif
-
-    struct {
-        void *pV1, *pV2, *pV3, *pV4, *pV5, *pV6, *pV7, *pV8, *pV9, *pV10;
-    } rCtx;
 } SSLModConfigRec;
 
 /** Structure representing configured filenames for certs and keys for
- * a given vhost, and the corresponding in-memory structures once the
- * files are parsed.  */
+ * a given vhost */
 typedef struct {
-    /* Lists of configured certs and keys for this server; from index
-     * 0 up to SSL_AIDX_MAX-1 or the first NULL pointer.  Note that
-     * these arrays are NOT indexed by algorithm type, they are simply
-     * unordered lists. */
-    const char  *cert_files[SSL_AIDX_MAX];
-    const char  *key_files[SSL_AIDX_MAX];
-    /* Loaded certs and keys; these arrays ARE indexed by the
-     * algorithm type, i.e.  keys[SSL_AIDX_RSA] maps to the RSA
-     * private key. */
-    X509        *certs[SSL_AIDX_MAX];
-    EVP_PKEY    *keys[SSL_AIDX_MAX];
+    /* Lists of configured certs and keys for this server */
+    apr_array_header_t *cert_files;
+    apr_array_header_t *key_files;
 
     /** Certificates which specify the set of CA names which should be
      * sent in the CertificateRequest message: */
@@ -780,7 +736,8 @@ const char *ssl_cmd_SSLFIPS(cmd_parms *cmd, void *dcfg, int flag);
 /**  module initialization  */
 apr_status_t ssl_init_Module(apr_pool_t *, apr_pool_t *, apr_pool_t *, server_rec *);
 apr_status_t ssl_init_Engine(server_rec *, apr_pool_t *);
-apr_status_t ssl_init_ConfigureServer(server_rec *, apr_pool_t *, apr_pool_t *, SSLSrvConfigRec *);
+apr_status_t ssl_init_ConfigureServer(server_rec *, apr_pool_t *, apr_pool_t *, SSLSrvConfigRec *,
+                                      apr_array_header_t *);
 apr_status_t ssl_init_CheckServers(server_rec *, apr_pool_t *);
 STACK_OF(X509_NAME)
             *ssl_init_FindCAList(server_rec *, apr_pool_t *, const char *, const char *);
@@ -871,13 +828,12 @@ void         ssl_util_ppclose(server_rec *, apr_pool_t *, apr_file_t *);
 char        *ssl_util_readfilter(server_rec *, apr_pool_t *, const char *,
                                  const char * const *);
 BOOL         ssl_util_path_check(ssl_pathcheck_t, const char *, apr_pool_t *);
-ssl_algo_t   ssl_util_algotypeof(X509 *, EVP_PKEY *);
-char        *ssl_util_algotypestr(ssl_algo_t);
 void         ssl_util_thread_setup(apr_pool_t *);
 int          ssl_init_ssl_connection(conn_rec *c, request_rec *r);
 
 /**  Pass Phrase Support  */
-apr_status_t ssl_pphrase_Handle(server_rec *, apr_pool_t *);
+apr_status_t ssl_load_encrypted_pkey(server_rec *, apr_pool_t *, int,
+                                     const char *, apr_array_header_t **);
 
 /**  Diffie-Hellman Parameter Support  */
 DH           *ssl_dh_GetParamFromFile(const char *);
@@ -895,12 +851,6 @@ ssl_asn1_t *ssl_asn1_table_get(apr_hash_t *table,
 void ssl_asn1_table_unset(apr_hash_t *table,
                           const char *key);
 
-const char *ssl_asn1_keystr(int keytype);
-
-const char *ssl_asn1_table_keyfmt(apr_pool_t *p,
-                                  const char *id,
-                                  int keytype);
-
 /**  Mutex Support  */
 int          ssl_mutex_init(server_rec *, apr_pool_t *);
 int          ssl_mutex_reinit(server_rec *, apr_pool_t *);
index 3a03e032c12ad41943bd8c6dc6820c521b6eb2fd..476aa0b6d4baa584da2eb5c2075a965c49de5769 100644 (file)
@@ -135,61 +135,8 @@ BOOL ssl_util_path_check(ssl_pathcheck_t pcm, const char *path, apr_pool_t *p)
     return TRUE;
 }
 
-ssl_algo_t ssl_util_algotypeof(X509 *pCert, EVP_PKEY *pKey)
-{
-    ssl_algo_t t;
-    EVP_PKEY *pFreeKey = NULL;
-
-    t = SSL_ALGO_UNKNOWN;
-    if (pCert != NULL)
-        pFreeKey = pKey = X509_get_pubkey(pCert);
-    if (pKey != NULL) {
-        switch (EVP_PKEY_type(pKey->type)) {
-            case EVP_PKEY_RSA:
-                t = SSL_ALGO_RSA;
-                break;
-            case EVP_PKEY_DSA:
-                t = SSL_ALGO_DSA;
-                break;
-#ifdef HAVE_ECC
-            case EVP_PKEY_EC:
-                t = SSL_ALGO_ECC;
-                break;
-#endif
-            default:
-                break;
-        }
-    }
-    if (pFreeKey != NULL)
-        EVP_PKEY_free(pFreeKey);
-    return t;
-}
-
-char *ssl_util_algotypestr(ssl_algo_t t)
-{
-    char *cp;
-
-    cp = "UNKNOWN";
-    switch (t) {
-        case SSL_ALGO_RSA:
-            cp = "RSA";
-            break;
-        case SSL_ALGO_DSA:
-            cp = "DSA";
-            break;
-#ifdef HAVE_ECC
-        case SSL_ALGO_ECC:
-            cp = "ECC";
-            break;
-#endif
-        default:
-            break;
-    }
-    return cp;
-}
-
 /*
- * certain key and cert data needs to survive restarts,
+ * certain key data needs to survive restarts,
  * which are stored in the user data table of s->process->pool.
  * to prevent "leaking" of this data, we use malloc/free
  * rather than apr_palloc and these wrappers to help make sure
@@ -253,30 +200,6 @@ void ssl_asn1_table_unset(apr_hash_t *table,
     apr_hash_set(table, key, klen, NULL);
 }
 
-#ifdef HAVE_ECC
-static const char *ssl_asn1_key_types[] = {"RSA", "DSA", "ECC"};
-#else
-static const char *ssl_asn1_key_types[] = {"RSA", "DSA"};
-#endif
-
-const char *ssl_asn1_keystr(int keytype)
-{
-    if (keytype >= SSL_AIDX_MAX) {
-        return NULL;
-    }
-
-    return ssl_asn1_key_types[keytype];
-}
-
-const char *ssl_asn1_table_keyfmt(apr_pool_t *p,
-                                  const char *id,
-                                  int keytype)
-{
-    const char *keystr = ssl_asn1_keystr(keytype);
-
-    return apr_pstrcat(p, id, ":", keystr, NULL);
-}
-
 #if APR_HAS_THREADS
 /*
  * To ensure thread-safetyness in OpenSSL - work in progress
index d2e3be366d5c9c5a51d388aa9876e4677840313d..0bf377682c0c6d02f94fa19ba515255334029b5d 100644 (file)
@@ -70,52 +70,11 @@ void SSL_set_app_data2(SSL *ssl, void *arg)
 
 /*  _________________________________________________________________
 **
-**  High-Level Certificate / Private Key Loading
+**  High-Level Private Key Loading
 **  _________________________________________________________________
 */
 
-X509 *SSL_read_X509(char* filename, X509 **x509, pem_password_cb *cb)
-{
-    X509 *rc;
-    BIO *bioS;
-    BIO *bioF;
-
-    /* 1. try PEM (= DER+Base64+headers) */
-    if ((bioS=BIO_new_file(filename, "r")) == NULL)
-        return NULL;
-    rc = PEM_read_bio_X509 (bioS, x509, cb, NULL);
-    BIO_free(bioS);
-
-    if (rc == NULL) {
-        /* 2. try DER+Base64 */
-        if ((bioS=BIO_new_file(filename, "r")) == NULL)
-            return NULL;
-
-        if ((bioF = BIO_new(BIO_f_base64())) == NULL) {
-            BIO_free(bioS);
-            return NULL;
-        }
-        bioS = BIO_push(bioF, bioS);
-        rc = d2i_X509_bio(bioS, NULL);
-        BIO_free_all(bioS);
-
-        if (rc == NULL) {
-            /* 3. try plain DER */
-            if ((bioS=BIO_new_file(filename, "r")) == NULL)
-                return NULL;
-            rc = d2i_X509_bio(bioS, NULL);
-            BIO_free(bioS);
-        }
-    }
-    if (rc != NULL && x509 != NULL) {
-        if (*x509 != NULL)
-            X509_free(*x509);
-        *x509 = rc;
-    }
-    return rc;
-}
-
-EVP_PKEY *SSL_read_PrivateKey(char* filename, EVP_PKEY **key, pem_password_cb *cb, void *s)
+EVP_PKEY *SSL_read_PrivateKey(const char* filename, EVP_PKEY **key, pem_password_cb *cb, void *s)
 {
     EVP_PKEY *rc;
     BIO *bioS;
index 80a7b98ede377e6a49dd709e91a13e640d60f03c..6f6873b487b660b011128a699d4a10c861355429 100644 (file)
@@ -60,8 +60,7 @@
 void        SSL_init_app_data2_idx(void);
 void       *SSL_get_app_data2(SSL *);
 void        SSL_set_app_data2(SSL *, void *);
-X509       *SSL_read_X509(char *, X509 **, pem_password_cb *);
-EVP_PKEY   *SSL_read_PrivateKey(char *, EVP_PKEY **, pem_password_cb *, void *);
+EVP_PKEY   *SSL_read_PrivateKey(const char *, EVP_PKEY **, pem_password_cb *, void *);
 int         SSL_smart_shutdown(SSL *ssl);
 BOOL        SSL_X509_getBC(X509 *, int *, int *);
 char       *SSL_X509_NAME_ENTRY_to_string(apr_pool_t *p, X509_NAME_ENTRY *xsne);