]> git.ipfire.org Git - thirdparty/apache/httpd.git/commitdiff
Changed ap_ssl_answer_challenge() and its hook to provide PEM data for
authorStefan Eissing <icing@apache.org>
Wed, 3 Mar 2021 15:52:18 +0000 (15:52 +0000)
committerStefan Eissing <icing@apache.org>
Wed, 3 Mar 2021 15:52:18 +0000 (15:52 +0000)
certificate and key instead of file names.

Added support for this in mod_ssl and verified with a local mod_md
version that uses it.

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

CHANGES
include/http_protocol.h
modules/ssl/ssl_engine_init.c
modules/ssl/ssl_engine_kernel.c
modules/ssl/ssl_util_ssl.c
modules/ssl/ssl_util_ssl.h
server/protocol.c

diff --git a/CHANGES b/CHANGES
index b444557f8ab51403688140f0d1572976b5108d67..66c13a0b0e328ad9866984cb42d4296df3a9d7b0 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -30,7 +30,8 @@ Changes with Apache 2.5.1
        available for an SSL module like mod_ssl.
      - ap_ssl_answer_challenge() to enable other modules like mod_md to
        provide a certificate as used in the RFC 8555 'tls-alpn-01' challenge
-       for the ACME protocol for an SSL module like mod_ssl.
+       for the ACME protocol for an SSL module like mod_ssl. The function
+       and its hook provide PEM encoded data instead of file names.
      - Hooks for 'ssl_add_cert_files', 'ssl_add_fallback_cert_files' and
        'ssl_answer_challenge' where modules like mod_md can provide providers
        to the above mentioned functions.
index c4f064a7c87f38e5c5a916bce13f0f70edf09180..30faa131a9cc9e625fba0224796581dfb2b912c1 100644 (file)
@@ -1174,23 +1174,34 @@ AP_DECLARE(apr_status_t) ap_ssl_add_fallback_cert_files(server_rec *s, apr_pool_
                                                         apr_array_header_t *key_files);         
 
 
-/** 
- * On TLS connections that do not relate to a configured virtual host,
- * allow modules to provide a certificate and key to
- * be used on the connection. 
+/**
+ * On TLS connections that do not relate to a configured virtual host
+ * allow modules to provide a certificate and key to be used on the connection.
+ *
+ * A Certificate PEM added must be accompanied by a private key PEM. The private
+ * key PEM may be given by a NULL pointer, in which case it is expected to be found in
+ * the certificate PEM string.
  */
-AP_DECLARE_HOOK(int, ssl_answer_challenge, (conn_rec *c, const char *server_name, 
-                                            const char **pcert_file, const char **pkey_file))
+AP_DECLARE_HOOK(int, ssl_answer_challenge, (conn_rec *c, const char *server_name,
+                                            const char **pcert_pem, const char **pkey_pem))
 
 /**
  * Returns != 0 iff the connection is a challenge to the server, for example
  * as defined in RFC 8555 for the 'tls-alpn-01' domain verification, and needs
  * a specific certificate as answer in the handshake.
+ *
  * ALPN protocol negotiation via the hooks 'protocol_propose' and 'protocol_switch'
  * need to have run before this call is made.
+ *
+ * Certificate PEMs added must be accompanied by a private key PEM. The private
+ * key PEM may be given by a NULL pointer, in which case it is expected to be found in
+ * the certificate PEM string.
+ *
+ * A certificate provided this way needs to replace any other certificates selected
+ * by configuration or 'ssl_add_cert_pems` on this connection.
  */
-AP_DECLARE(int) ap_ssl_answer_challenge(conn_rec *c, const char *server_name, 
-                                        const char **pcert_file, const char **pkey_file);
+AP_DECLARE(int) ap_ssl_answer_challenge(conn_rec *c, const char *server_name,
+                                        const char **pcert_pem, const char **pkey_pem);
 
 
 #ifdef __cplusplus
index 3dbdda65448585583ae66c0fbaf96a5b9bbe29c4..6ecc5df69bc8cc3194646b6c3aa3d0793750b136 100644 (file)
@@ -199,12 +199,12 @@ static void ssl_add_version_components(apr_pool_t *ptemp, apr_pool_t *pconf,
 
 int ssl_is_challenge(conn_rec *c, const char *servername, 
                      X509 **pcert, EVP_PKEY **pkey,
-                     const char **pcert_file, const char **pkey_file)
+                     const char **pcert_pem, const char **pkey_pem)
 {
     *pcert = NULL;
     *pkey = NULL;
-    *pcert_file = *pkey_file = NULL;
-    if (ap_ssl_answer_challenge(c, servername, pcert_file, pkey_file)) {
+    *pcert_pem = *pkey_pem = NULL;
+    if (ap_ssl_answer_challenge(c, servername, pcert_pem, pkey_pem)) {
         return 1;
     }
     else if (OK == ssl_run_answer_challenge(c, servername, pcert, pkey)) {
index 30dd5ee338fcbc2e004313b786a269460b48832a..9df2b9600b551912c382f864aeecb35e578b1438 100644 (file)
@@ -2317,33 +2317,32 @@ void ssl_callback_Info(const SSL *ssl, int where, int rc)
 
 static apr_status_t set_challenge_creds(conn_rec *c, const char *servername,
                                         SSL *ssl, X509 *cert, EVP_PKEY *key,
-                                        const char *cert_file, const char *key_file)
+                                        const char *cert_pem, const char *key_pem)
 {
     SSLConnRec *sslcon = myConnConfig(c);
+    apr_status_t rv = APR_SUCCESS;
+    int our_data = 0;
     
     sslcon->service_unavailable = 1;
-    if (cert_file) {
-        if (SSL_use_certificate_file(ssl, cert_file, SSL_FILETYPE_PEM) < 1) {
-            ap_log_cerror(APLOG_MARK, APLOG_WARNING, 0, c, APLOGNO(10264)
-                          "Failed to configure challenge certificate %s",
-                          servername);
-            return APR_EGENERAL;
-        }
-        if (key_file == NULL) key_file = cert_file;
-        if (SSL_use_PrivateKey_file(ssl, key_file, SSL_FILETYPE_PEM) < 1) {
-            ap_log_cerror(APLOG_MARK, APLOG_WARNING, 0, c, APLOGNO(10265)
-                          "Failed to configure challenge private key %s",
+    if (cert_pem) {
+        cert = NULL;
+        key = NULL;
+        our_data = 1;
+        
+        rv = modssl_read_cert(c->pool, cert_pem, key_pem, NULL, NULL, &cert, &key);
+        if (rv != APR_SUCCESS) {
+            ap_log_cerror(APLOG_MARK, APLOG_WARNING, 0, c, APLOGNO()
+                          "Failed to parse PEM of challenge certificate %s",
                           servername);
-            return APR_EGENERAL;
+            goto cleanup;
         }
-        goto check;
     }
     
     if ((SSL_use_certificate(ssl, cert) < 1)) {
         ap_log_cerror(APLOG_MARK, APLOG_WARNING, 0, c, APLOGNO(10086)
                       "Failed to configure challenge certificate %s",
                       servername);
-        return APR_EGENERAL;
+        rv = APR_EGENERAL; goto cleanup;
     }
     
     if (!SSL_use_PrivateKey(ssl, key)) {
@@ -2351,16 +2350,19 @@ static apr_status_t set_challenge_creds(conn_rec *c, const char *servername,
                       "error '%s' using Challenge key: %s",
                       ERR_error_string(ERR_peek_last_error(), NULL), 
                       servername);
-        return APR_EGENERAL;
+        rv = APR_EGENERAL; goto cleanup;
     }
     
-check:
     if (SSL_check_private_key(ssl) < 1) {
         ap_log_cerror(APLOG_MARK, APLOG_WARNING, 0, c, APLOGNO(10088)
                       "Challenge certificate and private key %s "
                       "do not match", servername);
-        return APR_EGENERAL;
+        rv = APR_EGENERAL; goto cleanup;
     }
+    
+cleanup:
+    if (our_data && cert) X509_free(cert);
+    if (our_data && key) EVP_PKEY_free(key);
     return APR_SUCCESS;
 }
   
@@ -2370,10 +2372,6 @@ check:
  */
 static apr_status_t init_vhost(conn_rec *c, SSL *ssl, const char *servername)
 {
-    X509 *cert;
-    EVP_PKEY *key;
-    const char *cert_file, *key_file;
-    
     if (c) {
         SSLConnRec *sslcon = myConnConfig(c);
 
@@ -2396,16 +2394,6 @@ static apr_status_t init_vhost(conn_rec *c, SSL *ssl, const char *servername)
                 sslcon->vhost_found = +1;
                 return APR_SUCCESS;
             }
-            else if (ssl_is_challenge(c, servername, &cert, &key, &cert_file, &key_file)) {
-                /* With ACMEv1 we can have challenge connections to a unknown domains
-                 * that need to be answered with a special certificate and will
-                 * otherwise not answer any requests. */
-                if (set_challenge_creds(c, servername, ssl, cert, key, 
-                                        cert_file, key_file) != APR_SUCCESS) {
-                    return APR_EGENERAL;
-                }
-                SSL_set_verify(ssl, SSL_VERIFY_NONE, ssl_callback_SSLVerify);
-            }
             else {
                 ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c, APLOGNO(02044)
                               "No matching SSL virtual host for servername "
@@ -2792,11 +2780,11 @@ int ssl_callback_alpn_select(SSL *ssl,
             const char *servername = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name);
             X509 *cert;
             EVP_PKEY *key;
-            const char *cert_file, *key_file;
+            const char *cert_pem, *key_pem;
 
-            if (ssl_is_challenge(c, servername, &cert, &key, &cert_file, &key_file)) {
+            if (ssl_is_challenge(c, servername, &cert, &key, &cert_pem, &key_pem)) {
                 if (set_challenge_creds(c, servername, ssl, cert, key, 
-                                        cert_file, key_file) != APR_SUCCESS) {
+                                        cert_pem, key_pem) != APR_SUCCESS) {
                     return SSL_TLSEXT_ERR_ALERT_FATAL;
                 }
                 SSL_set_verify(ssl, SSL_VERIFY_NONE, ssl_callback_SSLVerify);
index 2209ef44b27471be7c190067bfc5c9ca9a05344d..d6448957f691151c85377a1c616c2b4a3416a51a 100644 (file)
@@ -527,3 +527,54 @@ void modssl_set_reneg_state(SSLConnRec *sslconn, modssl_reneg_state state)
     sslconn->reneg_state = state;
 #endif
 }
+
+/*  _________________________________________________________________
+**
+**  Certficate/Key Stuff
+**  _________________________________________________________________
+*/
+
+apr_status_t modssl_read_cert(apr_pool_t *p, 
+                              const char *cert_pem, const char *key_pem,
+                              pem_password_cb *cb, void *ud,
+                              X509 **pcert, EVP_PKEY **pkey)
+{
+    BIO *in;
+    X509 *x = NULL;
+    EVP_PKEY *key = NULL;
+    apr_status_t rv = APR_SUCCESS;
+
+    in = BIO_new_mem_buf(cert_pem, -1);
+    if (in == NULL) {
+        rv = APR_ENOMEM; goto cleanup;
+    }
+    
+    x = PEM_read_bio_X509(in, NULL, cb, ud);
+    if (x == NULL) {
+        rv = APR_ENOENT; goto cleanup;
+    }
+    
+    BIO_free(in);
+    in = BIO_new_mem_buf(key_pem? key_pem : cert_pem, -1);
+    if (in == NULL) {
+        rv = APR_ENOMEM; goto cleanup;
+    }
+    key = PEM_read_bio_PrivateKey(in, NULL, cb, ud);
+    if (key == NULL) {
+        rv = APR_ENOENT; goto cleanup;
+    }
+    
+cleanup:
+    if (rv == APR_SUCCESS) {
+        *pcert = x;
+        *pkey = key;
+    }
+    else {
+        *pcert = NULL;
+        *pkey = NULL;
+        if (x) X509_free(x);
+        if (key) EVP_PKEY_free(key);
+    }
+    if (in != NULL) BIO_free(in);
+    return rv;
+}
index ec89185b1b0845c3e0dbfc4103588f019ceabbef..335d6fdde716cdd335e59af6e3241be79cf8dc0b 100644 (file)
@@ -82,7 +82,15 @@ char       *modssl_SSL_SESSION_id2sz(IDCONST unsigned char *, int, char *, int);
  * pool-allocated string.  If empty, returns NULL.  BIO_free(bio) is
  * called for both cases. */
 char *modssl_bio_free_read(apr_pool_t *p, BIO *bio);
-    
+
+/* Read a single certificate and its private key from the give string in PEM format.
+ * If `key_pem` is NULL, it will expect the key in `cert_pem`.
+ */
+apr_status_t modssl_read_cert(apr_pool_t *p, 
+                              const char *cert_pem, const char *key_pem,
+                              pem_password_cb *cb, void *ud, 
+                              X509 **pcert, EVP_PKEY **pkey);
+                              
 #endif /* __SSL_UTIL_SSL_H__ */
 /** @} */
 
index 4ce2b6172a058b223aee9036ec4a0e53a60489f2..d18ca01b95604450cf057771ccfe1de532913688 100644 (file)
@@ -2717,9 +2717,9 @@ AP_DECLARE(apr_status_t) ap_ssl_add_fallback_cert_files(server_rec *s, apr_pool_
 }         
 
 AP_DECLARE(int) ap_ssl_answer_challenge(conn_rec *c, const char *server_name, 
-                                        const char **pcert_file, const char **pkey_file)
+                                        const char **pcert_pem, const char **pkey_pem)
 {
-    return (ap_run_ssl_answer_challenge(c, server_name, pcert_file, pkey_file) == OK);
+    return (ap_run_ssl_answer_challenge(c, server_name, pcert_pem, pkey_pem) == OK);
 }
 
 AP_IMPLEMENT_HOOK_VOID(pre_read_request,
@@ -2761,6 +2761,6 @@ AP_IMPLEMENT_HOOK_RUN_ALL(int, ssl_add_fallback_cert_files,
          apr_array_header_t *cert_files, apr_array_header_t *key_files),
         (s, p, cert_files, key_files), OK, DECLINED)
 AP_IMPLEMENT_HOOK_RUN_FIRST(int, ssl_answer_challenge, 
-        (conn_rec *c, const char *server_name, const char **pcert_file, const char **pkey_file),
-        (c, server_name, pcert_file, pkey_file), DECLINED)
+        (conn_rec *c, const char *server_name, const char **pcert_pem, const char **pkey_pem),
+        (c, server_name, pcert_pem, pkey_pem), DECLINED)