1d78fa4
# ./pullrev.sh 1830819 1830836 1830912 1830913 1830927 1831168 1831173
1d78fa4
1d78fa4
http://svn.apache.org/viewvc?view=revision&revision=1830819
1d78fa4
http://svn.apache.org/viewvc?view=revision&revision=1830912
1d78fa4
http://svn.apache.org/viewvc?view=revision&revision=1830913
1d78fa4
http://svn.apache.org/viewvc?view=revision&revision=1830927
1d78fa4
http://svn.apache.org/viewvc?view=revision&revision=1831168
1d78fa4
http://svn.apache.org/viewvc?view=revision&revision=1831173
1d78fa4
http://svn.apache.org/viewvc?view=revision&revision=1835240
1d78fa4
http://svn.apache.org/viewvc?view=revision&revision=1835242
1d78fa4
1810bd5
diff --git a/modules/ssl/ssl_engine_config.c b/modules/ssl/ssl_engine_config.c
1810bd5
index d276fea..5467d23 100644
a185523
--- httpd-2.4.38/modules/ssl/ssl_engine_config.c.r1830819+
a185523
+++ httpd-2.4.38/modules/ssl/ssl_engine_config.c
a185523
@@ -916,7 +916,9 @@
1d78fa4
     SSLSrvConfigRec *sc = mySrvConfig(cmd->server);
1d78fa4
     const char *err;
1d78fa4
 
1d78fa4
-    if ((err = ssl_cmd_check_file(cmd, &arg))) {
1d78fa4
+    /* Only check for non-ENGINE based certs. */
1d78fa4
+    if (!modssl_is_engine_id(arg)
1d78fa4
+        && (err = ssl_cmd_check_file(cmd, &arg))) {
1d78fa4
         return err;
1d78fa4
     }
1d78fa4
 
a185523
@@ -932,7 +934,9 @@
1d78fa4
     SSLSrvConfigRec *sc = mySrvConfig(cmd->server);
1d78fa4
     const char *err;
1d78fa4
 
1d78fa4
-    if ((err = ssl_cmd_check_file(cmd, &arg))) {
1d78fa4
+    /* Check keyfile exists for non-ENGINE keys. */
1d78fa4
+    if (!modssl_is_engine_id(arg)
1d78fa4
+        && (err = ssl_cmd_check_file(cmd, &arg))) {
1d78fa4
         return err;
1d78fa4
     }
1d78fa4
 
a185523
--- httpd-2.4.38/modules/ssl/ssl_engine_init.c.r1830819+
a185523
+++ httpd-2.4.38/modules/ssl/ssl_engine_init.c
a185523
@@ -1228,12 +1228,18 @@
1d78fa4
                 (certfile = APR_ARRAY_IDX(mctx->pks->cert_files, i,
1d78fa4
                                           const char *));
1d78fa4
          i++) {
1d78fa4
+        EVP_PKEY *pkey;
1d78fa4
+        const char *engine_certfile = NULL;
1d78fa4
+
1d78fa4
         key_id = apr_psprintf(ptemp, "%s:%d", vhost_id, i);
1d78fa4
 
1d78fa4
         ERR_clear_error();
1d78fa4
 
1d78fa4
         /* first the certificate (public key) */
1d78fa4
-        if (mctx->cert_chain) {
1d78fa4
+        if (modssl_is_engine_id(certfile)) {
1d78fa4
+            engine_certfile = certfile;
1d78fa4
+        }
1d78fa4
+        else if (mctx->cert_chain) {
1d78fa4
             if ((SSL_CTX_use_certificate_file(mctx->ssl_ctx, certfile,
1d78fa4
                                               SSL_FILETYPE_PEM) < 1)) {
1d78fa4
                 ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(02561)
a185523
@@ -1262,12 +1268,46 @@
1d78fa4
 
1d78fa4
         ERR_clear_error();
1d78fa4
 
1d78fa4
-        if ((SSL_CTX_use_PrivateKey_file(mctx->ssl_ctx, keyfile,
1d78fa4
-                                         SSL_FILETYPE_PEM) < 1) &&
1d78fa4
-            (ERR_GET_FUNC(ERR_peek_last_error())
1d78fa4
-                != X509_F_X509_CHECK_PRIVATE_KEY)) {
1d78fa4
+        if (modssl_is_engine_id(keyfile)) {
1d78fa4
+            apr_status_t rv;
1d78fa4
+
1d78fa4
+            cert = NULL;
1d78fa4
+            
1d78fa4
+            if ((rv = modssl_load_engine_keypair(s, ptemp, vhost_id,
1d78fa4
+                                                 engine_certfile, keyfile,
1d78fa4
+                                                 &cert, &pkey))) {
1d78fa4
+                return rv;
1d78fa4
+            }
1d78fa4
+
1d78fa4
+            if (cert) {
1d78fa4
+                if (SSL_CTX_use_certificate(mctx->ssl_ctx, cert) < 1) {
1d78fa4
+                    ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(10137)
1d78fa4
+                                 "Failed to configure engine certificate %s, check %s",
1d78fa4
+                                 key_id, certfile);
1d78fa4
+                    ssl_log_ssl_error(SSLLOG_MARK, APLOG_EMERG, s);
1d78fa4
+                    return APR_EGENERAL;
1d78fa4
+                }
1d78fa4
+
1d78fa4
+                /* SSL_CTX now owns the cert. */
1d78fa4
+                X509_free(cert);
1d78fa4
+            }                    
1d78fa4
+            
1d78fa4
+            if (SSL_CTX_use_PrivateKey(mctx->ssl_ctx, pkey) < 1) {
1d78fa4
+                ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(10130)
1d78fa4
+                             "Failed to configure private key %s from engine",
1d78fa4
+                             keyfile);
1d78fa4
+                ssl_log_ssl_error(SSLLOG_MARK, APLOG_EMERG, s);
1d78fa4
+                return APR_EGENERAL;
1d78fa4
+            }
1d78fa4
+
1d78fa4
+            /* SSL_CTX now owns the key */
1d78fa4
+            EVP_PKEY_free(pkey);
1d78fa4
+        }
1d78fa4
+        else if ((SSL_CTX_use_PrivateKey_file(mctx->ssl_ctx, keyfile,
1d78fa4
+                                              SSL_FILETYPE_PEM) < 1)
1d78fa4
+                 && (ERR_GET_FUNC(ERR_peek_last_error())
1d78fa4
+                     != X509_F_X509_CHECK_PRIVATE_KEY)) {
1d78fa4
             ssl_asn1_t *asn1;
1d78fa4
-            EVP_PKEY *pkey;
1d78fa4
             const unsigned char *ptr;
1d78fa4
 
1d78fa4
             ERR_clear_error();
a185523
@@ -1354,8 +1394,9 @@
1d78fa4
     /*
1d78fa4
      * Try to read DH parameters from the (first) SSLCertificateFile
1d78fa4
      */
1d78fa4
-    if ((certfile = APR_ARRAY_IDX(mctx->pks->cert_files, 0, const char *)) &&
1d78fa4
-        (dhparams = ssl_dh_GetParamFromFile(certfile))) {
1d78fa4
+    certfile = APR_ARRAY_IDX(mctx->pks->cert_files, 0, const char *);
1d78fa4
+    if (certfile && !modssl_is_engine_id(certfile)
1d78fa4
+        && (dhparams = ssl_dh_GetParamFromFile(certfile))) {
1d78fa4
         SSL_CTX_set_tmp_dh(mctx->ssl_ctx, dhparams);
1d78fa4
         ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(02540)
1d78fa4
                      "Custom DH parameters (%d bits) for %s loaded from %s",
a185523
@@ -1367,10 +1408,10 @@
1d78fa4
     /*
1d78fa4
      * Similarly, try to read the ECDH curve name from SSLCertificateFile...
1d78fa4
      */
1d78fa4
-    if ((certfile != NULL) && 
1d78fa4
-        (ecparams = ssl_ec_GetParamFromFile(certfile)) &&
1d78fa4
-        (nid = EC_GROUP_get_curve_name(ecparams)) &&
1d78fa4
-        (eckey = EC_KEY_new_by_curve_name(nid))) {
1d78fa4
+    if (certfile && !modssl_is_engine_id(certfile)
1d78fa4
+        && (ecparams = ssl_ec_GetParamFromFile(certfile))
1d78fa4
+        && (nid = EC_GROUP_get_curve_name(ecparams)) 
1d78fa4
+        && (eckey = EC_KEY_new_by_curve_name(nid))) {
1d78fa4
         SSL_CTX_set_tmp_ecdh(mctx->ssl_ctx, eckey);
1d78fa4
         ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(02541)
1d78fa4
                      "ECDH curve %s for %s specified in %s",
a185523
--- httpd-2.4.38/modules/ssl/ssl_engine_pphrase.c.r1830819+
a185523
+++ httpd-2.4.38/modules/ssl/ssl_engine_pphrase.c
a185523
@@ -143,8 +143,6 @@
1d78fa4
     const char *key_id = asn1_table_vhost_key(mc, p, sc->vhost_id, idx);
1d78fa4
     EVP_PKEY *pPrivateKey = NULL;
1d78fa4
     ssl_asn1_t *asn1;
1d78fa4
-    unsigned char *ucp;
1d78fa4
-    long int length;
1d78fa4
     int nPassPhrase = (*pphrases)->nelts;
1d78fa4
     int nPassPhraseRetry = 0;
1d78fa4
     apr_time_t pkey_mtime = 0;
a185523
@@ -221,7 +219,7 @@
a185523
          * is not empty. */
a185523
         ERR_clear_error();
a185523
 
a185523
-        pPrivateKey = modssl_read_privatekey(ppcb_arg.pkey_file, NULL,
a185523
+        pPrivateKey = modssl_read_privatekey(ppcb_arg.pkey_file,
a185523
                                              ssl_pphrase_Handle_CB, &ppcb_arg);
a185523
         /* If the private key was successfully read, nothing more to
a185523
            do here. */
a185523
@@ -351,19 +349,12 @@
1d78fa4
         nPassPhrase++;
1d78fa4
     }
1d78fa4
 
1d78fa4
-    /*
1d78fa4
-     * Insert private key into the global module configuration
1d78fa4
-     * (we convert it to a stand-alone DER byte sequence
1d78fa4
-     * because the SSL library uses static variables inside a
1d78fa4
-     * RSA structure which do not survive DSO reloads!)
1d78fa4
-     */
1d78fa4
-    length = i2d_PrivateKey(pPrivateKey, NULL);
1d78fa4
-    ucp = ssl_asn1_table_set(mc->tPrivateKey, key_id, length);
1d78fa4
-    (void)i2d_PrivateKey(pPrivateKey, &ucp;; /* 2nd arg increments */
1d78fa4
+    /* Cache the private key in the global module configuration so it
1d78fa4
+     * can be used after subsequent reloads. */
1d78fa4
+    asn1 = ssl_asn1_table_set(mc->tPrivateKey, key_id, pPrivateKey);
1d78fa4
 
1d78fa4
     if (ppcb_arg.nPassPhraseDialogCur != 0) {
1d78fa4
         /* remember mtime of encrypted keys */
1d78fa4
-        asn1 = ssl_asn1_table_get(mc->tPrivateKey, key_id);
1d78fa4
         asn1->source_mtime = pkey_mtime;
1d78fa4
     }
1d78fa4
 
a185523
@@ -614,3 +605,288 @@
1d78fa4
      */
1d78fa4
     return (len);
1d78fa4
 }
1d78fa4
+
1d78fa4
+
1d78fa4
+#if defined(HAVE_OPENSSL_ENGINE_H) && defined(HAVE_ENGINE_INIT)
1d78fa4
+
1d78fa4
+/* OpenSSL UI implementation for passphrase entry; largely duplicated
1d78fa4
+ * from ssl_pphrase_Handle_CB but adjusted for UI API. TODO: Might be
1d78fa4
+ * worth trying to shift pphrase handling over to the UI API
1d78fa4
+ * completely. */
1d78fa4
+static int passphrase_ui_open(UI *ui)
1d78fa4
+{
1d78fa4
+    pphrase_cb_arg_t *ppcb = UI_get0_user_data(ui);
1d78fa4
+    SSLSrvConfigRec *sc = mySrvConfig(ppcb->s);
1d78fa4
+
1d78fa4
+    ppcb->nPassPhraseDialog++;
1d78fa4
+    ppcb->nPassPhraseDialogCur++;
1d78fa4
+
1d78fa4
+    /*
1d78fa4
+     * Builtin or Pipe dialog
1d78fa4
+     */
1d78fa4
+    if (sc->server->pphrase_dialog_type == SSL_PPTYPE_BUILTIN
1d78fa4
+        || sc->server->pphrase_dialog_type == SSL_PPTYPE_PIPE) {
1d78fa4
+        if (sc->server->pphrase_dialog_type == SSL_PPTYPE_PIPE) {
1d78fa4
+            if (!readtty) {
1d78fa4
+                ap_log_error(APLOG_MARK, APLOG_INFO, 0, ppcb->s,
1d78fa4
+                             APLOGNO(10143)
1d78fa4
+                             "Init: Creating pass phrase dialog pipe child "
1d78fa4
+                             "'%s'", sc->server->pphrase_dialog_path);
1d78fa4
+                if (ssl_pipe_child_create(ppcb->p,
1d78fa4
+                            sc->server->pphrase_dialog_path)
1d78fa4
+                        != APR_SUCCESS) {
1d78fa4
+                    ap_log_error(APLOG_MARK, APLOG_ERR, 0, ppcb->s,
1d78fa4
+                                 APLOGNO(10144)
1d78fa4
+                                 "Init: Failed to create pass phrase pipe '%s'",
1d78fa4
+                                 sc->server->pphrase_dialog_path);
1d78fa4
+                    return 0;
1d78fa4
+                }
1d78fa4
+            }
1d78fa4
+            ap_log_error(APLOG_MARK, APLOG_INFO, 0, ppcb->s, APLOGNO(10145)
1d78fa4
+                         "Init: Requesting pass phrase via piped dialog");
1d78fa4
+        }
1d78fa4
+        else { /* sc->server->pphrase_dialog_type == SSL_PPTYPE_BUILTIN */
1d78fa4
+#ifdef WIN32
1d78fa4
+            ap_log_error(APLOG_MARK, APLOG_ERR, 0, ppcb->s, APLOGNO(10146)
1d78fa4
+                         "Init: Failed to create pass phrase pipe '%s'",
1d78fa4
+                         sc->server->pphrase_dialog_path);
1d78fa4
+            return 0;
1d78fa4
+#else
1d78fa4
+            /*
1d78fa4
+             * stderr has already been redirected to the error_log.
1d78fa4
+             * rather than attempting to temporarily rehook it to the terminal,
1d78fa4
+             * we print the prompt to stdout before EVP_read_pw_string turns
1d78fa4
+             * off tty echo
1d78fa4
+             */
1d78fa4
+            apr_file_open_stdout(&writetty, ppcb->p);
1d78fa4
+
1d78fa4
+            ap_log_error(APLOG_MARK, APLOG_INFO, 0, ppcb->s, APLOGNO(10147)
1d78fa4
+                         "Init: Requesting pass phrase via builtin terminal "
1d78fa4
+                         "dialog");
1d78fa4
+#endif
1d78fa4
+        }
1d78fa4
+
1d78fa4
+        /*
1d78fa4
+         * The first time display a header to inform the user about what
1d78fa4
+         * program he actually speaks to, which module is responsible for
1d78fa4
+         * this terminal dialog and why to the hell he has to enter
1d78fa4
+         * something...
1d78fa4
+         */
1d78fa4
+        if (ppcb->nPassPhraseDialog == 1) {
1d78fa4
+            apr_file_printf(writetty, "%s mod_ssl (Pass Phrase Dialog)\n",
1d78fa4
+                            AP_SERVER_BASEVERSION);
1d78fa4
+            apr_file_printf(writetty,
1d78fa4
+                            "A pass phrase is required to access the private key.\n");
1d78fa4
+        }
1d78fa4
+        if (ppcb->bPassPhraseDialogOnce) {
1d78fa4
+            ppcb->bPassPhraseDialogOnce = FALSE;
1d78fa4
+            apr_file_printf(writetty, "\n");
1d78fa4
+            apr_file_printf(writetty, "Private key %s (%s)\n",
1d78fa4
+                            ppcb->key_id, ppcb->pkey_file);
1d78fa4
+        }
1d78fa4
+    }
1d78fa4
+
1d78fa4
+    return 1;
1d78fa4
+}
1d78fa4
+
1d78fa4
+static int passphrase_ui_read(UI *ui, UI_STRING *uis)
1d78fa4
+{
1d78fa4
+    pphrase_cb_arg_t *ppcb = UI_get0_user_data(ui);
1d78fa4
+    SSLSrvConfigRec *sc = mySrvConfig(ppcb->s);
1d78fa4
+    const char *prompt;
1d78fa4
+    int i;
1d78fa4
+    int bufsize;
1d78fa4
+    int len;
1d78fa4
+    char *buf;
1d78fa4
+
1d78fa4
+    prompt = UI_get0_output_string(uis);
1d78fa4
+    if (prompt == NULL) {
1d78fa4
+        prompt = "Enter pass phrase:";
1d78fa4
+    }
1d78fa4
+
1d78fa4
+    /*
1d78fa4
+     * Get the maximum expected size and allocate the buffer
1d78fa4
+     */
1d78fa4
+    bufsize = UI_get_result_maxsize(uis);
1d78fa4
+    buf = apr_pcalloc(ppcb->p, bufsize);
1d78fa4
+
1d78fa4
+    if (sc->server->pphrase_dialog_type == SSL_PPTYPE_BUILTIN
1d78fa4
+        || sc->server->pphrase_dialog_type == SSL_PPTYPE_PIPE) {
1d78fa4
+        /*
1d78fa4
+         * Get the pass phrase through a callback.
1d78fa4
+         * Empty input is not accepted.
1d78fa4
+         */
1d78fa4
+        for (;;) {
1d78fa4
+            if (sc->server->pphrase_dialog_type == SSL_PPTYPE_PIPE) {
1d78fa4
+                i = pipe_get_passwd_cb(buf, bufsize, "", FALSE);
1d78fa4
+            }
1d78fa4
+            else { /* sc->server->pphrase_dialog_type == SSL_PPTYPE_BUILTIN */
1d78fa4
+                i = EVP_read_pw_string(buf, bufsize, "", FALSE);
1d78fa4
+            }
1d78fa4
+            if (i != 0) {
1d78fa4
+                OPENSSL_cleanse(buf, bufsize);
1d78fa4
+                return 0;
1d78fa4
+            }
1d78fa4
+            len = strlen(buf);
1d78fa4
+            if (len < 1){
1d78fa4
+                apr_file_printf(writetty, "Apache:mod_ssl:Error: Pass phrase"
1d78fa4
+                                "empty (needs to be at least 1 character).\n");
1d78fa4
+                apr_file_puts(prompt, writetty);
1d78fa4
+            }
1d78fa4
+            else {
1d78fa4
+                break;
1d78fa4
+            }
1d78fa4
+        }
1d78fa4
+    }
1d78fa4
+    /*
1d78fa4
+     * Filter program
1d78fa4
+     */
1d78fa4
+    else if (sc->server->pphrase_dialog_type == SSL_PPTYPE_FILTER) {
1d78fa4
+        const char *cmd = sc->server->pphrase_dialog_path;
1d78fa4
+        const char **argv = apr_palloc(ppcb->p, sizeof(char *) * 3);
1d78fa4
+        char *result;
1d78fa4
+
1d78fa4
+        ap_log_error(APLOG_MARK, APLOG_INFO, 0, ppcb->s, APLOGNO(10148)
1d78fa4
+                     "Init: Requesting pass phrase from dialog filter "
1d78fa4
+                     "program (%s)", cmd);
1d78fa4
+
1d78fa4
+        argv[0] = cmd;
1d78fa4
+        argv[1] = ppcb->key_id;
1d78fa4
+        argv[2] = NULL;
1d78fa4
+
1d78fa4
+        result = ssl_util_readfilter(ppcb->s, ppcb->p, cmd, argv);
1d78fa4
+        apr_cpystrn(buf, result, bufsize);
1d78fa4
+        len = strlen(buf);
1d78fa4
+    }
1d78fa4
+
1d78fa4
+    /*
1d78fa4
+     * Ok, we now have the pass phrase, so give it back
1d78fa4
+     */
1d78fa4
+    ppcb->cpPassPhraseCur = apr_pstrdup(ppcb->p, buf);
1d78fa4
+    UI_set_result(ui, uis, buf);
1d78fa4
+
1d78fa4
+    /* Clear sensitive data. */
1d78fa4
+    OPENSSL_cleanse(buf, bufsize);
1d78fa4
+    return 1;
1d78fa4
+}
1d78fa4
+
1d78fa4
+static int passphrase_ui_write(UI *ui, UI_STRING *uis)
1d78fa4
+{
1d78fa4
+    pphrase_cb_arg_t *ppcb = UI_get0_user_data(ui);
1d78fa4
+    SSLSrvConfigRec *sc;
1d78fa4
+    const char *prompt;
1d78fa4
+
1d78fa4
+    sc = mySrvConfig(ppcb->s);
1d78fa4
+
1d78fa4
+    if (sc->server->pphrase_dialog_type == SSL_PPTYPE_BUILTIN
1d78fa4
+        || sc->server->pphrase_dialog_type == SSL_PPTYPE_PIPE) {
1d78fa4
+        prompt = UI_get0_output_string(uis);
1d78fa4
+        apr_file_puts(prompt, writetty);
1d78fa4
+    }
1d78fa4
+
1d78fa4
+    return 1;
1d78fa4
+}
1d78fa4
+
1d78fa4
+static int passphrase_ui_close(UI *ui)
1d78fa4
+{
1d78fa4
+    /*
1d78fa4
+     * Close the pipes if they were opened
1d78fa4
+     */
1d78fa4
+    if (readtty) {
1d78fa4
+        apr_file_close(readtty);
1d78fa4
+        apr_file_close(writetty);
1d78fa4
+        readtty = writetty = NULL;
1d78fa4
+    }
1d78fa4
+    return 1;
1d78fa4
+}
1d78fa4
+
1d78fa4
+static apr_status_t pp_ui_method_cleanup(void *uip)
1d78fa4
+{
1d78fa4
+    UI_METHOD *uim = uip;
1d78fa4
+    
1d78fa4
+    UI_destroy_method(uim);
1d78fa4
+
1d78fa4
+    return APR_SUCCESS;
1d78fa4
+}
1d78fa4
+
1d78fa4
+static UI_METHOD *get_passphrase_ui(apr_pool_t *p)
1d78fa4
+{
1d78fa4
+    UI_METHOD *ui_method = UI_create_method("Passphrase UI");
1d78fa4
+
1d78fa4
+    UI_method_set_opener(ui_method, passphrase_ui_open);
1d78fa4
+    UI_method_set_reader(ui_method, passphrase_ui_read);
1d78fa4
+    UI_method_set_writer(ui_method, passphrase_ui_write);
1d78fa4
+    UI_method_set_closer(ui_method, passphrase_ui_close);
1d78fa4
+
1d78fa4
+    apr_pool_cleanup_register(p, ui_method, pp_ui_method_cleanup,
1d78fa4
+                              pp_ui_method_cleanup);
1d78fa4
+    
1d78fa4
+    return ui_method;
1d78fa4
+}
1d78fa4
+
1d78fa4
+
1d78fa4
+apr_status_t modssl_load_engine_keypair(server_rec *s, apr_pool_t *p,
1d78fa4
+                                        const char *vhostid,
1d78fa4
+                                        const char *certid, const char *keyid,
1d78fa4
+                                        X509 **pubkey, EVP_PKEY **privkey)
1d78fa4
+{
1d78fa4
+    SSLModConfigRec *mc = myModConfig(s);
1d78fa4
+    ENGINE *e;
1d78fa4
+    UI_METHOD *ui_method = get_passphrase_ui(p);
1d78fa4
+    pphrase_cb_arg_t ppcb;
1d78fa4
+
1d78fa4
+    memset(&ppcb, 0, sizeof ppcb);
1d78fa4
+    ppcb.s = s;
1d78fa4
+    ppcb.p = p;
1d78fa4
+    ppcb.bPassPhraseDialogOnce = TRUE;
1d78fa4
+    ppcb.key_id = vhostid;
1d78fa4
+    ppcb.pkey_file = keyid;
1d78fa4
+
1d78fa4
+    if (!mc->szCryptoDevice) {
1d78fa4
+        ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(10131)
1d78fa4
+                     "Init: Cannot load private key `%s' without engine",
1d78fa4
+                     keyid);
1d78fa4
+        return ssl_die(s);
1d78fa4
+    }
1d78fa4
+
1d78fa4
+    if (!(e = ENGINE_by_id(mc->szCryptoDevice))) {
1d78fa4
+        ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(10132)
1d78fa4
+                     "Init: Failed to load Crypto Device API `%s'",
1d78fa4
+                     mc->szCryptoDevice);
1d78fa4
+        ssl_log_ssl_error(SSLLOG_MARK, APLOG_EMERG, s);
1d78fa4
+        return ssl_die(s);
1d78fa4
+    }
1d78fa4
+
1d78fa4
+    if (APLOGdebug(s)) {
1d78fa4
+        ENGINE_ctrl_cmd_string(e, "VERBOSE", NULL, 0);
1d78fa4
+    }
1d78fa4
+
1d78fa4
+    if (certid) {
1d78fa4
+        struct {
1d78fa4
+            const char *cert_id;
1d78fa4
+            X509 *cert;
1d78fa4
+        } params = { certid, NULL };
1d78fa4
+
1d78fa4
+        if (!ENGINE_ctrl_cmd(e, "LOAD_CERT_CTRL", 0, &params, NULL, 1)) {
1d78fa4
+            ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(10136)
1d78fa4
+                         "Init: Unable to get the certificate");
1d78fa4
+            ssl_log_ssl_error(SSLLOG_MARK, APLOG_EMERG, s);
1d78fa4
+            return ssl_die(s);
1d78fa4
+        }
1d78fa4
+
1d78fa4
+        *pubkey = params.cert;
1d78fa4
+    }
1d78fa4
+
1d78fa4
+    *privkey = ENGINE_load_private_key(e, keyid, ui_method, &ppcb);
1d78fa4
+    if (*privkey == NULL) {
1d78fa4
+        ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(10133)
1d78fa4
+                     "Init: Unable to get the private key");
1d78fa4
+        ssl_log_ssl_error(SSLLOG_MARK, APLOG_EMERG, s);
1d78fa4
+        return ssl_die(s);
1d78fa4
+    }
1d78fa4
+
1d78fa4
+    ENGINE_free(e);
1d78fa4
+
1d78fa4
+    return APR_SUCCESS;
1d78fa4
+}
1d78fa4
+#endif
a185523
--- httpd-2.4.38/modules/ssl/ssl_private.h.r1830819+
a185523
+++ httpd-2.4.38/modules/ssl/ssl_private.h
a185523
@@ -1002,21 +1002,28 @@
1d78fa4
 apr_status_t ssl_load_encrypted_pkey(server_rec *, apr_pool_t *, int,
1d78fa4
                                      const char *, apr_array_header_t **);
1d78fa4
 
1d78fa4
+/* Load public and/or private key from the configured ENGINE. Private
1d78fa4
+ * key returned as *pkey.  certid can be NULL, in which case *pubkey
1d78fa4
+ * is not altered.  Errors logged on failure. */
1d78fa4
+apr_status_t modssl_load_engine_keypair(server_rec *s, apr_pool_t *p,
1d78fa4
+                                        const char *vhostid,
1d78fa4
+                                        const char *certid, const char *keyid,
1d78fa4
+                                        X509 **pubkey, EVP_PKEY **privkey);
1d78fa4
+
1d78fa4
 /**  Diffie-Hellman Parameter Support  */
1d78fa4
 DH           *ssl_dh_GetParamFromFile(const char *);
1d78fa4
 #ifdef HAVE_ECC
1d78fa4
 EC_GROUP     *ssl_ec_GetParamFromFile(const char *);
1d78fa4
 #endif
1d78fa4
 
1d78fa4
-unsigned char *ssl_asn1_table_set(apr_hash_t *table,
1d78fa4
-                                  const char *key,
1d78fa4
-                                  long int length);
1d78fa4
-
1d78fa4
-ssl_asn1_t *ssl_asn1_table_get(apr_hash_t *table,
1d78fa4
-                               const char *key);
1d78fa4
-
1d78fa4
-void ssl_asn1_table_unset(apr_hash_t *table,
1d78fa4
-                          const char *key);
1d78fa4
+/* Store the EVP_PKEY key (serialized into DER) in the hash table with
1d78fa4
+ * key, returning the ssl_asn1_t structure pointer. */
1d78fa4
+ssl_asn1_t *ssl_asn1_table_set(apr_hash_t *table, const char *key,
1d78fa4
+                               EVP_PKEY *pkey);
1d78fa4
+/* Retrieve the ssl_asn1_t structure with given key from the hash. */
1d78fa4
+ssl_asn1_t *ssl_asn1_table_get(apr_hash_t *table, const char *key);
1d78fa4
+/* Remove and free the ssl_asn1_t structure with given key. */
1d78fa4
+void ssl_asn1_table_unset(apr_hash_t *table, const char *key);
1d78fa4
 
1d78fa4
 /**  Mutex Support  */
1d78fa4
 int          ssl_mutex_init(server_rec *, apr_pool_t *);
a185523
@@ -1109,6 +1116,10 @@
1d78fa4
 int ssl_is_challenge(conn_rec *c, const char *servername, 
1d78fa4
                      X509 **pcert, EVP_PKEY **pkey);
1d78fa4
 
1d78fa4
+/* Returns non-zero if the cert/key filename should be handled through
1d78fa4
+ * the configured ENGINE. */
1d78fa4
+int modssl_is_engine_id(const char *name);
1d78fa4
+
1d78fa4
 #endif /* SSL_PRIVATE_H */
1d78fa4
 /** @} */
1d78fa4
 
a185523
--- httpd-2.4.38/modules/ssl/ssl_util.c.r1830819+
a185523
+++ httpd-2.4.38/modules/ssl/ssl_util.c
a185523
@@ -192,45 +192,37 @@
1d78fa4
     return TRUE;
1d78fa4
 }
1d78fa4
 
1d78fa4
-/*
1d78fa4
- * certain key data needs to survive restarts,
1d78fa4
- * which are stored in the user data table of s->process->pool.
1d78fa4
- * to prevent "leaking" of this data, we use malloc/free
1d78fa4
- * rather than apr_palloc and these wrappers to help make sure
1d78fa4
- * we do not leak the malloc-ed data.
1d78fa4
- */
1d78fa4
-unsigned char *ssl_asn1_table_set(apr_hash_t *table,
1d78fa4
-                                  const char *key,
1d78fa4
-                                  long int length)
1d78fa4
+/* Decrypted private keys are cached to survive restarts.  The cached
1d78fa4
+ * data must have lifetime of the process (hence malloc/free rather
1d78fa4
+ * than pools), and uses raw DER since the EVP_PKEY structure
1d78fa4
+ * internals may not survive across a module reload. */
1d78fa4
+ssl_asn1_t *ssl_asn1_table_set(apr_hash_t *table, const char *key,
1d78fa4
+                               EVP_PKEY *pkey)
1d78fa4
 {
1d78fa4
     apr_ssize_t klen = strlen(key);
1d78fa4
     ssl_asn1_t *asn1 = apr_hash_get(table, key, klen);
1d78fa4
+    apr_size_t length = i2d_PrivateKey(pkey, NULL);
1d78fa4
+    unsigned char *p;
1d78fa4
 
1d78fa4
-    /*
1d78fa4
-     * if a value for this key already exists,
1d78fa4
-     * reuse as much of the already malloc-ed data
1d78fa4
-     * as possible.
1d78fa4
-     */
1d78fa4
+    /* Re-use structure if cached previously. */
1d78fa4
     if (asn1) {
1d78fa4
         if (asn1->nData != length) {
1d78fa4
-            free(asn1->cpData); /* XXX: realloc? */
1d78fa4
-            asn1->cpData = NULL;
1d78fa4
+            asn1->cpData = ap_realloc(asn1->cpData, length);
1d78fa4
         }
1d78fa4
     }
1d78fa4
     else {
1d78fa4
         asn1 = ap_malloc(sizeof(*asn1));
1d78fa4
         asn1->source_mtime = 0; /* used as a note for encrypted private keys */
1d78fa4
-        asn1->cpData = NULL;
1d78fa4
-    }
1d78fa4
-
1d78fa4
-    asn1->nData = length;
1d78fa4
-    if (!asn1->cpData) {
1d78fa4
         asn1->cpData = ap_malloc(length);
1d78fa4
+
1d78fa4
+        apr_hash_set(table, key, klen, asn1);
1d78fa4
     }
1d78fa4
 
1d78fa4
-    apr_hash_set(table, key, klen, asn1);
1d78fa4
+    asn1->nData = length;
1d78fa4
+    p = asn1->cpData;
1d78fa4
+    i2d_PrivateKey(pkey, &p); /* increases p by length */
1d78fa4
 
1d78fa4
-    return asn1->cpData; /* caller will assign a value to this */
1d78fa4
+    return asn1;
1d78fa4
 }
1d78fa4
 
1d78fa4
 ssl_asn1_t *ssl_asn1_table_get(apr_hash_t *table,
a185523
@@ -480,3 +472,13 @@
1d78fa4
 }
1d78fa4
 
1d78fa4
 #endif /* #if APR_HAS_THREADS && MODSSL_USE_OPENSSL_PRE_1_1_API */
1d78fa4
+
1d78fa4
+int modssl_is_engine_id(const char *name)
1d78fa4
+{
1d78fa4
+#if defined(HAVE_OPENSSL_ENGINE_H) && defined(HAVE_ENGINE_INIT)
1d78fa4
+    /* ### Can handle any other special ENGINE key names here? */
1d78fa4
+    return strncmp(name, "pkcs11:", 7) == 0;
1d78fa4
+#else
1d78fa4
+    return 0;
1d78fa4
+#endif
1d78fa4
+}
a185523
--- httpd-2.4.38/modules/ssl/ssl_util_ssl.c.r1830819+
a185523
+++ httpd-2.4.38/modules/ssl/ssl_util_ssl.c
a185523
@@ -74,7 +74,7 @@
1d78fa4
 **  _________________________________________________________________
1d78fa4
 */
1d78fa4
 
1d78fa4
-EVP_PKEY *modssl_read_privatekey(const char* filename, EVP_PKEY **key, pem_password_cb *cb, void *s)
1d78fa4
+EVP_PKEY *modssl_read_privatekey(const char *filename, pem_password_cb *cb, void *s)
1d78fa4
 {
1d78fa4
     EVP_PKEY *rc;
1d78fa4
     BIO *bioS;
a185523
@@ -83,7 +83,7 @@
1d78fa4
     /* 1. try PEM (= DER+Base64+headers) */
1d78fa4
     if ((bioS=BIO_new_file(filename, "r")) == NULL)
1d78fa4
         return NULL;
1d78fa4
-    rc = PEM_read_bio_PrivateKey(bioS, key, cb, s);
1d78fa4
+    rc = PEM_read_bio_PrivateKey(bioS, NULL, cb, s);
1d78fa4
     BIO_free(bioS);
1d78fa4
 
1d78fa4
     if (rc == NULL) {
a185523
@@ -107,41 +107,9 @@
1d78fa4
             BIO_free(bioS);
1d78fa4
         }
1d78fa4
     }
1d78fa4
-    if (rc != NULL && key != NULL) {
1d78fa4
-        if (*key != NULL)
1d78fa4
-            EVP_PKEY_free(*key);
1d78fa4
-        *key = rc;
1d78fa4
-    }
1d78fa4
     return rc;
1d78fa4
 }
1d78fa4
 
1d78fa4
-typedef struct {
1d78fa4
-    const char *pass;
1d78fa4
-    int pass_len;
1d78fa4
-} pass_ctx;
1d78fa4
-
1d78fa4
-static int provide_pass(char *buf, int size, int rwflag, void *baton)
1d78fa4
-{
1d78fa4
-    pass_ctx *ctx = baton;
1d78fa4
-    if (ctx->pass_len > 0) {
1d78fa4
-        if (ctx->pass_len < size) {
1d78fa4
-            size = (int)ctx->pass_len;
1d78fa4
-        }
1d78fa4
-        memcpy(buf, ctx->pass, size);
1d78fa4
-    }
1d78fa4
-    return ctx->pass_len;
1d78fa4
-}
1d78fa4
-
1d78fa4
-EVP_PKEY   *modssl_read_encrypted_pkey(const char *filename, EVP_PKEY **key, 
1d78fa4
-                                       const char *pass, apr_size_t pass_len)
1d78fa4
-{
1d78fa4
-    pass_ctx ctx;
1d78fa4
-    
1d78fa4
-    ctx.pass = pass;
1d78fa4
-    ctx.pass_len = pass_len;
1d78fa4
-    return modssl_read_privatekey(filename, key, provide_pass, &ctx;;
1d78fa4
-}
1d78fa4
-
1d78fa4
 /*  _________________________________________________________________
1d78fa4
 **
1d78fa4
 **  Smart shutdown
a185523
--- httpd-2.4.38/modules/ssl/ssl_util_ssl.h.r1830819+
a185523
+++ httpd-2.4.38/modules/ssl/ssl_util_ssl.h
1d78fa4
@@ -64,8 +64,11 @@
1d78fa4
 void        modssl_init_app_data2_idx(void);
1d78fa4
 void       *modssl_get_app_data2(SSL *);
1d78fa4
 void        modssl_set_app_data2(SSL *, void *);
1d78fa4
-EVP_PKEY   *modssl_read_privatekey(const char *, EVP_PKEY **, pem_password_cb *, void *);
1d78fa4
-EVP_PKEY   *modssl_read_encrypted_pkey(const char *, EVP_PKEY **, const char *, apr_size_t);
1d78fa4
+
1d78fa4
+/* Read private key from filename in either PEM or raw base64(DER)
1d78fa4
+ * format, using password entry callback cb and userdata. */
1d78fa4
+EVP_PKEY   *modssl_read_privatekey(const char *filename, pem_password_cb *cb, void *ud);
1d78fa4
+
1d78fa4
 int         modssl_smart_shutdown(SSL *ssl);
1d78fa4
 BOOL        modssl_X509_getBC(X509 *, int *, int *);
1d78fa4
 char       *modssl_X509_NAME_ENTRY_to_string(apr_pool_t *p, X509_NAME_ENTRY *xsne,