]> granicus.if.org Git - apache/commitdiff
mod_ssl: Add support for loading private keys from ENGINEs. Support
authorJoe Orton <jorton@apache.org>
Thu, 3 May 2018 13:06:46 +0000 (13:06 +0000)
committerJoe Orton <jorton@apache.org>
Thu, 3 May 2018 13:06:46 +0000 (13:06 +0000)
for PKCS#11 URIs only, and PIN entry is not threaded through
SSLPassPhraseDialog config yet.

* modules/ssl/ssl_util.c (modssl_is_engine_key): New function.

* modules/ssl/ssl_engine_config.c (ssl_cmd_SSLCertificateKeyFile):
  Use it, skip check for file existence for engine keys.

* modules/ssl/ssl_engine_pphrase.c (modssl_load_engine_pkey):
  New function.

* modules/ssl/ssl_engine_init.c (ssl_init_server_certs):
  For engine keys, load via modssl_load_engine_pkey.

Submitted by: Anderson Sasaki <ansasaki redhat.com>, jorton

git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@1830819 13f79535-47bb-0310-9956-ffa450edef68

CHANGES
docs/log-message-tags/next-number
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

diff --git a/CHANGES b/CHANGES
index 28db0b329371b10fea63ead743a98b8cbee568f8..ebb68e8472ff4657f006dffb8eced9cdea8021c9 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -1,6 +1,9 @@
                                                          -*- coding: utf-8 -*-
 Changes with Apache 2.5.1
 
+  *) mod_ssl: Support loading private keys from an OpenSSL engine using a
+     PKCS#11 URI.  [Anderson Sasaki <ansasaki redhat.com>, Joe Orton]
+
   *) mod_slomem_shm: Handle a generation number when the slotmem size changes,
      modifying the number of proxy balancers or balancer members on restart
      could have prevented the server to load, notably on Windows. PR 62308.
index 6adec3be444c12f25c90ee6bf6698948eb0fd063..5f25bb8e36e452a4acc1257c52cdb1473f61fd4a 100644 (file)
@@ -1 +1 @@
-10130
+10134
index f4e8c34c2c6c81a0db6629ed84030020e24e599a..aa16916095f19c5966e52e7355a6aca9d9ae1f79 100644 (file)
@@ -969,15 +969,17 @@ SSLCertificateFile "/usr/local/apache2/conf/ssl.crt/server.crt"
 <directivesynopsis>
 <name>SSLCertificateKeyFile</name>
 <description>Server PEM-encoded private key file</description>
-<syntax>SSLCertificateKeyFile <em>file-path</em></syntax>
+<syntax>SSLCertificateKeyFile <em>file-path</em>|<var>keyid</var></syntax>
 <contextlist><context>server config</context>
 <context>virtual host</context></contextlist>
+<compatibility><var>keyid</var> available in 2.5.1 and later.</compatibility>
 
 <usage>
 <p>
 This directive points to the PEM-encoded private key file for the
-server. If the contained private key is encrypted, the pass phrase
-dialog is forced at startup time.</p>
+server, or the key ID through a configured cryptographic token. 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)
@@ -993,9 +995,21 @@ is highly discouraged. If it is used, the certificate files using such
 an embedded key must be configured after the certificates using a separate
 key file.</p>
 
+<p>As an alternative to storing private keys in files, a key
+identifier can be specified to identify a private key stored in a
+token.  Currently, only <a href="https://tools.ietf.org/html/rfc7512">PKCS#11 URIs</a> are recognized as private key
+identifiers, and can be used in conjunction with the OpenSSL
+<code>pkcs11</code> engine configured with <directive
+module="mod_ssl">SSLCryptoDevice</directive>.</p>
+
 <example><title>Example</title>
 <highlight language="config">
+# To use a private key from a PEM-encoded file:
 SSLCertificateKeyFile "/usr/local/apache2/conf/ssl.key/server.key"
+# To use a private key from a PKCS#11 token:
+SSLCryptoDevice pkcs11
+...
+SSLCertificateKeyFile "pkcs11:token=My%20Token%20Name;id=45"
 </highlight>
 </example>
 </usage>
index 832bb5ff6a8bdfb231bf828a589f9c943516a731..e1873cdd8e9bc1a3f0be82fd8730d709c7b2f606 100644 (file)
@@ -1032,7 +1032,9 @@ const char *ssl_cmd_SSLCertificateKeyFile(cmd_parms *cmd,
     SSLSrvConfigRec *sc = mySrvConfig(cmd->server);
     const char *err;
 
-    if ((err = ssl_cmd_check_file(cmd, &arg))) {
+    /* Check keyfile exists for non-ENGINE keys. */
+    if (!modssl_is_engine_key(arg)
+        && (err = ssl_cmd_check_file(cmd, &arg))) {
         return err;
     }
 
index cbcadc711fa90f9f74f4a6be9fddf7450e4d70d1..c80d2358fb6878eb882db79e37a6e620addbd9fb 100644 (file)
@@ -1250,6 +1250,8 @@ static apr_status_t ssl_init_server_certs(server_rec *s,
                 (certfile = APR_ARRAY_IDX(mctx->pks->cert_files, i,
                                           const char *));
          i++) {
+        EVP_PKEY *pkey;
+
         key_id = apr_psprintf(ptemp, "%s:%d", vhost_id, i);
 
         ERR_clear_error();
@@ -1284,12 +1286,26 @@ static apr_status_t ssl_init_server_certs(server_rec *s,
 
         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)) {
+        if (modssl_is_engine_key(keyfile)) {
+            apr_status_t rv;
+
+            if ((rv = modssl_load_engine_pkey(s, ptemp, keyfile, &pkey))) {
+                return rv;
+            }
+            
+            if (SSL_CTX_use_PrivateKey(mctx->ssl_ctx, pkey) < 1) {
+                ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(10130)
+                             "Failed to configure private key %s from engine",
+                             keyfile);
+                ssl_log_ssl_error(SSLLOG_MARK, APLOG_EMERG, s);
+                return APR_EGENERAL;
+            }
+        }
+        else 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();
index f28bb8e83ce5bc338cd6398b5d15cdb173707655..76584535908f577e0f366b0286cf0ab3debf5b00 100644 (file)
@@ -600,3 +600,52 @@ int ssl_pphrase_Handle_CB(char *buf, int bufsize, int verify, void *srv)
      */
     return (len);
 }
+
+#if defined(HAVE_OPENSSL_ENGINE_H) && defined(HAVE_ENGINE_INIT)
+apr_status_t modssl_load_engine_pkey(server_rec *s, apr_pool_t *p,
+                                     const char *keyid, EVP_PKEY **ppkey)
+{
+    SSLModConfigRec *mc = myModConfig(s);
+    EVP_PKEY *pPrivateKey = NULL;
+    ENGINE *e;
+    UI_METHOD *ui_method;
+
+    if (!mc->szCryptoDevice) {
+        ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(10131)
+                     "Init: Cannot load private key `%s' without engine",
+                     keyid);
+        return ssl_die(s);
+    }
+
+    /*
+     * Using the builtin OpenSSL UI only, for now...
+     */
+    ui_method = UI_OpenSSL();
+
+    if (!(e = ENGINE_by_id(mc->szCryptoDevice))) {
+        ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(10132)
+                     "Init: Failed to load Crypto Device API `%s'",
+                     mc->szCryptoDevice);
+        ssl_log_ssl_error(SSLLOG_MARK, APLOG_EMERG, s);
+        return ssl_die(s);
+    }
+
+    if (APLOGdebug(s)) {
+        ENGINE_ctrl_cmd_string(e, "VERBOSE", NULL, 0);
+    }
+    
+    pPrivateKey = ENGINE_load_private_key(e, keyid, ui_method, NULL);
+    if (pPrivateKey == NULL) {
+        ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(10133)
+                     "Init: Unable to get the private key");
+        ssl_log_ssl_error(SSLLOG_MARK, APLOG_EMERG, s);
+        return ssl_die(s);
+    }
+
+    *ppkey = pPrivateKey;
+
+    ENGINE_free(e);
+
+    return APR_SUCCESS;
+}
+#endif
index c5182469a57cd8d41156115ff2fc2ebf734c7c14..c43556fb02ab00f759d6f99e32547ff6dc6ffa56 100644 (file)
@@ -996,6 +996,10 @@ BOOL         ssl_util_vhost_matches(const char *servername, server_rec *s);
 /**  Pass Phrase Support  */
 apr_status_t ssl_load_encrypted_pkey(server_rec *, apr_pool_t *, int,
                                      const char *, apr_array_header_t **);
+/* Load private key from the configured ENGINE, returned as **pkey.
+ * Errors logged on failure. */
+apr_status_t modssl_load_engine_pkey(server_rec *s, apr_pool_t *p,
+                                     const char *keyid, EVP_PKEY **ppkey);
 
 /**  Diffie-Hellman Parameter Support  */
 DH           *ssl_dh_GetParamFromFile(const char *);
@@ -1101,6 +1105,10 @@ DH *modssl_get_dh_params(unsigned keylen);
  * corresponding SSLConnRec structure for the connection. */
 int modssl_request_is_tls(const request_rec *r, SSLConnRec **sslconn);
 
+/* Returns non-zero if the cert/key filename should be handled through
+ * the configure ENGINE. */
+int modssl_is_engine_key(const char *name);
+
 #if HAVE_VALGRIND
 extern int ssl_running_on_valgrind;
 #endif
index 098ae6a33766059c631306e84308d100e442e0ae..ebc36051291518e5d95dcb757217b38bcbf82f3c 100644 (file)
@@ -522,3 +522,13 @@ void ssl_util_thread_setup(apr_pool_t *p)
 }
 
 #endif /* #if APR_HAS_THREADS && MODSSL_USE_OPENSSL_PRE_1_1_API */
+
+int modssl_is_engine_key(const char *name)
+{
+#if defined(HAVE_OPENSSL_ENGINE_H) && defined(HAVE_ENGINE_INIT)
+    /* ### Can handle any other special ENGINE key names here? */
+    return strncmp(name, "pkcs11:", 7) == 0;
+#else
+    return 0;
+#endif
+}