On 02.12.2023 11:20, Graham Leggett via dev wrote:
> On 27 Nov 2023, at 15:02, Ingo Franzki <ifran...@linux.ibm.com> wrote:
> 
>> The mod_ssl module has support for loading keys and certificates from 
>> OpenSSL engines via PKCS#11 URIs at SSLCertificateFile and 
>> SSLCertificateKeyFile, e.g. using the PKCS#11 engine part of libp11 
>> (https://github.com/OpenSC/libp11). 
>>
>> This works fine, but with OpenSSL 3.0 engines got deprecated, and a new 
>> provider concept is used.
>> OpenSSL 1.1.1 is no longer supported by the OpenSSL organization 
>> (https://www.openssl.org/blog/blog/2023/09/11/eol-111/), 
>> and newer distributions all have OpenSSL 3.x included.
>> Currently, engines do still work, bit since they are deprecated, they will 
>> at some point in time no longer be working.
>>
>> With OpenSSL 3.x providers one can implements loading of keys and 
>> certificates by implementing a STORE method.
>> With this, keys and certificates can be loaded for example from PKCS#11 
>> modules via PKCS#11 URIs, just like it was possible with an PKCS#11 engine. 
>>
>> Please find below some code changes required to support loading the server 
>> private key and certificates from a PKCS#11 provider using OpenSSL STORE 
>> providers. 
> 
> Definite +1 in principle.
> 
>> Index: docs/manual/mod/mod_ssl.html.en.utf8
>> ===================================================================
>> --- docs/manual/mod/mod_ssl.html.en.utf8     (revision 1914150)
>> +++ docs/manual/mod/mod_ssl.html.en.utf8     (working copy)
>> @@ -666,7 +666,7 @@
> 
> Would it be possible to patch mod_ssl.xml instead of the html file, the html 
> is autogenerated.
Sure, see updated patch attached.
> 
>> Index: modules/ssl/ssl_engine_config.c
>> ===================================================================
>> --- modules/ssl/ssl_engine_config.c  (revision 1914150)
>> +++ modules/ssl/ssl_engine_config.c  (working copy)
>> @@ -689,6 +689,11 @@
>>     if (strcEQ(arg, "builtin")) {
>>         mc->szCryptoDevice = NULL;
>>     }
>> +#if MODSSL_USE_OPENSSL_STORE
>> +    else if (strcEQ(arg, "provider")) {
>> +        mc->szCryptoDevice = arg;
>> +    }
>> +#endif
>> #if MODSSL_HAVE_ENGINE_API
> 
> This patch isn’t applying for me, looks like the leading spaces have been 
> lost. Would it be possible to try attach it as a file?
Please see the patch file attached.
I also fixed to minor bugs that I found during testing. 

You can also look at the patch here:
https://github.com/ifranzki/httpd/commit/4bb3ea191bc2c77608b4811817ad7f63177dd931

If you want, I can even submit a pull request to 
https://github.com/apache/httpd.
Let me know what you prefer.

> 
> Regards,
> Graham
> —
> 

-- 
Ingo Franzki
eMail: ifran...@linux.ibm.com  
Tel: ++49 (0)7031-16-4648
Linux on IBM Z Development, Schoenaicher Str. 220, 71032 Boeblingen, Germany

IBM Deutschland Research & Development GmbH
Vorsitzender des Aufsichtsrats: Gregor Pillen
Geschäftsführung: David Faller
Sitz der Gesellschaft: Böblingen / Registergericht: Amtsgericht Stuttgart, HRB 
243294
IBM DATA Privacy Statement: https://www.ibm.com/privacy/us/en/
Index: docs/manual/mod/mod_ssl.xml
===================================================================
--- docs/manual/mod/mod_ssl.xml (revision 1914150)
+++ docs/manual/mod/mod_ssl.xml (working copy)
@@ -955,7 +955,7 @@
 stored in a token.  Currently, only <a
 href="https://tools.ietf.org/html/rfc7512";>PKCS#11 URIs</a> are
 recognized as certificate identifiers, and can be used in conjunction
-with the OpenSSL <code>pkcs11</code> engine.  If <directive
+with the OpenSSL <code>pkcs11</code> engine or provider.  If <directive
 module="mod_ssl">SSLCertificateKeyFile</directive> is omitted, the
 certificate and private key can be loaded through the single
 identifier specified with <directive
@@ -1048,7 +1048,7 @@
 identifier can be used 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.</p>
+<code>pkcs11</code> engine or provider.</p>
 
 <example><title>Example</title>
 <highlight language="config">
@@ -2442,6 +2442,14 @@
 SSLCryptoDevice ubsec
 </highlight>
 </example>
+
+<p>
+With OpenSSL 3.0 or later, specify <code>provider</code> to load keys and
+certificates from a provider using <a 
href="https://tools.ietf.org/html/rfc7512";>PKCS#11 URIs</a>.
+The provider to use must be defined and configured in the OpenSSL config file,
+and it must support the <a 
href="https://www.openssl.org/docs/man3.0/man7/provider-storemgmt.html";>STORE 
method</a>
+for <a href="https://tools.ietf.org/html/rfc7512";>PKCS#11 URIs</a>.
+</p>
 </usage>
 </directivesynopsis>
 
Index: modules/ssl/ssl_engine_config.c
===================================================================
--- modules/ssl/ssl_engine_config.c     (revision 1914150)
+++ modules/ssl/ssl_engine_config.c     (working copy)
@@ -689,6 +689,11 @@
     if (strcEQ(arg, "builtin")) {
         mc->szCryptoDevice = NULL;
     }
+#if MODSSL_USE_OPENSSL_STORE
+    else if (strcEQ(arg, "provider")) {
+        mc->szCryptoDevice = arg;
+    }
+#endif
 #if MODSSL_HAVE_ENGINE_API
     else if ((e = ENGINE_by_id(arg))) {
         mc->szCryptoDevice = arg;
@@ -697,7 +702,11 @@
 #endif
     else {
         err = "SSLCryptoDevice: Invalid argument; must be one of: "
+#if MODSSL_USE_OPENSSL_STORE
+              "'builtin' (none), 'provider' (use OpenSSL >= 3.0 provider 
STORE)";
+#else
               "'builtin' (none)";
+#endif
 #if MODSSL_HAVE_ENGINE_API
         e = ENGINE_get_first();
         while (e) {
Index: modules/ssl/ssl_engine_init.c
===================================================================
--- modules/ssl/ssl_engine_init.c       (revision 1914150)
+++ modules/ssl/ssl_engine_init.c       (working copy)
@@ -506,7 +506,11 @@
     SSLModConfigRec *mc = myModConfig(s);
     ENGINE *e;
 
+#if MODSSL_USE_OPENSSL_STORE
+    if (mc->szCryptoDevice && !strcEQ(mc->szCryptoDevice, "provider")) {
+#else
     if (mc->szCryptoDevice) {
+#endif
         if (!(e = ENGINE_by_id(mc->szCryptoDevice))) {
             ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(01888)
                          "Init: Failed to load Crypto Device API `%s'",
@@ -1476,7 +1480,11 @@
             if (cert) {
                 if (SSL_CTX_use_certificate(mctx->ssl_ctx, cert) < 1) {
                     ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(10137)
+#if MODSSL_USE_OPENSSL_STORE
+                                 "Failed to configure engine/provider 
certificate %s, check %s",
+#else
                                  "Failed to configure engine certificate %s, 
check %s",
+#endif
                                  key_id, certfile);
                     ssl_log_ssl_error(SSLLOG_MARK, APLOG_EMERG, s);
                     return APR_EGENERAL;
@@ -1488,7 +1496,11 @@
             
             if (SSL_CTX_use_PrivateKey(mctx->ssl_ctx, pkey) < 1) {
                 ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(10130)
+#if MODSSL_USE_OPENSSL_STORE
+                             "Failed to configure private key %s from 
engine/provider",
+#else
                              "Failed to configure private key %s from engine",
+#endif
                              keyfile);
                 ssl_log_ssl_error(SSLLOG_MARK, APLOG_EMERG, s);
                 return APR_EGENERAL;
Index: modules/ssl/ssl_engine_pphrase.c
===================================================================
--- modules/ssl/ssl_engine_pphrase.c    (revision 1914150)
+++ modules/ssl/ssl_engine_pphrase.c    (working copy)
@@ -31,6 +31,9 @@
 #include "ssl_private.h"
 
 #include <openssl/ui.h>
+#if MODSSL_USE_OPENSSL_STORE
+#include <openssl/store.h>
+#endif
 
 typedef struct {
     server_rec         *s;
@@ -576,7 +579,7 @@
     return (len);
 }
 
-#if MODSSL_HAVE_ENGINE_API
+#if MODSSL_HAVE_ENGINE_API || MODSSL_USE_OPENSSL_STORE
 
 /* OpenSSL UI implementation for passphrase entry; largely duplicated
  * from ssl_pphrase_Handle_CB but adjusted for UI API. TODO: Might be
@@ -793,13 +796,14 @@
 }
 #endif
 
-
-apr_status_t modssl_load_engine_keypair(server_rec *s, apr_pool_t *p,
-                                        const char *vhostid,
-                                        const char *certid, const char *keyid,
-                                        X509 **pubkey, EVP_PKEY **privkey)
-{
 #if MODSSL_HAVE_ENGINE_API
+static apr_status_t modssl_load_keypair_engine(server_rec *s, apr_pool_t *p,
+                                               const char *vhostid,
+                                               const char *certid,
+                                               const char *keyid,
+                                               X509 **pubkey,
+                                               EVP_PKEY **privkey)
+{
     const char *c, *scheme;
     ENGINE *e;
     UI_METHOD *ui_method = get_passphrase_ui(p);
@@ -873,6 +877,133 @@
     ENGINE_free(e);
 
     return APR_SUCCESS;
+}
+#endif
+
+#if MODSSL_USE_OPENSSL_STORE
+static apr_status_t modssl_load_keypair_store(server_rec *s, apr_pool_t *p,
+                                              const char *vhostid,
+                                              const char *certid,
+                                              const char *keyid,
+                                              X509 **pubkey,
+                                              EVP_PKEY **privkey)
+{
+    OSSL_STORE_CTX *sctx;
+    UI_METHOD *ui_method = get_passphrase_ui(p);
+    pphrase_cb_arg_t ppcb;
+
+    memset(&ppcb, 0, sizeof ppcb);
+    ppcb.s = s;
+    ppcb.p = p;
+    ppcb.bPassPhraseDialogOnce = TRUE;
+    ppcb.key_id = vhostid;
+    ppcb.pkey_file = keyid;
+
+    *privkey = NULL;
+    sctx = OSSL_STORE_open(keyid, ui_method, &ppcb, NULL, NULL);
+    if (!sctx) {
+        ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(10131)
+                     "Init: OSSL_STORE_open failed for private key identifier 
`%s'",
+                     keyid);
+        return ssl_die(s);
+    }
+
+    while (!OSSL_STORE_eof(sctx)) {
+        OSSL_STORE_INFO *info = OSSL_STORE_load(sctx);
+        if (!info) {
+            ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(10131)
+                         "Init: OSSL_STORE_load failed for private key 
identifier `%s'",
+                         keyid);
+            OSSL_STORE_close(sctx);
+            return ssl_die(s);
+        }
+
+        switch (OSSL_STORE_INFO_get_type(info)) {
+        case OSSL_STORE_INFO_PKEY:
+            *privkey = OSSL_STORE_INFO_get1_PKEY(info);
+            break;
+        default:
+            OSSL_STORE_INFO_free(info);
+            continue;
+        }
+
+        OSSL_STORE_INFO_free(info);
+        break;
+    }
+
+    OSSL_STORE_close(sctx);
+
+    if (!*privkey) {
+        ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(10131)
+                     "Init: OSSL_STORE_INFO_PKEY lookup failed for private key 
identifier `%s'",
+                     keyid);
+        OSSL_STORE_close(sctx);
+        return ssl_die(s);
+    }
+
+    if (certid) {
+        *pubkey = NULL;
+
+        sctx = OSSL_STORE_open(certid, ui_method, &ppcb, NULL, NULL);
+        if (!sctx) {
+            ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(10131)
+                         "Init: OSSL_STORE_open failed for certificate 
identifier `%s'",
+                         certid);
+            return ssl_die(s);
+        }
+
+        while (!OSSL_STORE_eof(sctx)) {
+            OSSL_STORE_INFO *info = OSSL_STORE_load(sctx);
+            if (!info) {
+                ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(10131)
+                             "Init: OSSL_STORE_load failed for certificate 
identifier `%s'",
+                             certid);
+                OSSL_STORE_close(sctx);
+                return ssl_die(s);
+            }
+
+            switch (OSSL_STORE_INFO_get_type(info)) {
+            case OSSL_STORE_INFO_CERT:
+                *pubkey = OSSL_STORE_INFO_get1_CERT(info);
+                break;
+            default:
+                OSSL_STORE_INFO_free(info);
+                continue;
+            }
+
+            OSSL_STORE_INFO_free(info);
+            break;
+        }
+
+        OSSL_STORE_close(sctx);
+
+        if (!*pubkey) {
+            ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(10131)
+                     "Init: OSSL_STORE_INFO_PKEY lookup failed for certificate 
identifier `%s'",
+                     certid);
+            return ssl_die(s);
+        }
+    }
+
+    return APR_SUCCESS;
+}
+#endif
+
+apr_status_t modssl_load_engine_keypair(server_rec *s, apr_pool_t *p,
+                                        const char *vhostid,
+                                        const char *certid, const char *keyid,
+                                        X509 **pubkey, EVP_PKEY **privkey)
+{
+#if MODSSL_USE_OPENSSL_STORE
+    SSLModConfigRec *mc = myModConfig(s);
+
+    if (strcEQ(mc->szCryptoDevice, "provider"))
+        return modssl_load_keypair_store(s, p, vhostid, certid, keyid,
+                                         pubkey, privkey);
+#endif
+#if MODSSL_HAVE_ENGINE_API
+    return modssl_load_keypair_engine(s, p, vhostid, certid, keyid,
+                                      pubkey, privkey);
 #else
     return APR_ENOTIMPL;
 #endif
Index: modules/ssl/ssl_private.h
===================================================================
--- modules/ssl/ssl_private.h   (revision 1914150)
+++ modules/ssl/ssl_private.h   (working copy)
@@ -118,6 +118,13 @@
 #define MODSSL_HAVE_ENGINE_API 0
 #endif
 
+/* Use OpenSSL 3.x STORE for loading URI keys and certificates starting with
+ * OpenSSL 3.0
+ */
+#if OPENSSL_VERSION_NUMBER >= 0x30000000
+#define MODSSL_USE_OPENSSL_STORE 1
+#endif
+
 #if (OPENSSL_VERSION_NUMBER < 0x0090801f)
 #error mod_ssl requires OpenSSL 0.9.8a or later
 #endif
Index: modules/ssl/ssl_util.c
===================================================================
--- modules/ssl/ssl_util.c      (revision 1914150)
+++ modules/ssl/ssl_util.c      (working copy)
@@ -500,7 +500,7 @@
 
 int modssl_is_engine_id(const char *name)
 {
-#if MODSSL_USE_ENGINE_API
+#if MODSSL_USE_ENGINE_API || MODSSL_USE_OPENSSL_STORE
     /* ### Can handle any other special ENGINE key names here? */
     return strncmp(name, "pkcs11:", 7) == 0;
 #else

Reply via email to