You are viewing a plain text version of this content. The canonical link for it is here.
Posted to cvs@httpd.apache.org by ic...@apache.org on 2019/06/25 15:21:10 UTC

svn commit: r1862075 - in /httpd/httpd/trunk: CHANGES modules/md/md.h modules/md/mod_md.c modules/ssl/mod_ssl.h modules/ssl/ssl_engine_init.c

Author: icing
Date: Tue Jun 25 15:21:10 2019
New Revision: 1862075

URL: http://svn.apache.org/viewvc?rev=1862075&view=rev
Log:
  *) mod_ssl/mod_md: reversing dependency by letting mod_ssl offer hooks for
     adding certificates and keys to a virtual host. An additional hook allows
     answering special TLS connections as used in ACME challenges.


Modified:
    httpd/httpd/trunk/CHANGES
    httpd/httpd/trunk/modules/md/md.h
    httpd/httpd/trunk/modules/md/mod_md.c
    httpd/httpd/trunk/modules/ssl/mod_ssl.h
    httpd/httpd/trunk/modules/ssl/ssl_engine_init.c

Modified: httpd/httpd/trunk/CHANGES
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/CHANGES?rev=1862075&r1=1862074&r2=1862075&view=diff
==============================================================================
--- httpd/httpd/trunk/CHANGES [utf-8] (original)
+++ httpd/httpd/trunk/CHANGES [utf-8] Tue Jun 25 15:21:10 2019
@@ -1,6 +1,11 @@
                                                          -*- coding: utf-8 -*-
 Changes with Apache 2.5.1
 
+  *) mod_ssl/mod_md: reversing dependency by letting mod_ssl offer hooks for
+     adding certificates and keys to a virtual host. An additional hook allows
+     answering special TLS connections as used in ACME challenges.
+     [Stefan Eissing]
+  
   *) mod_md: bringing over v2.0.6 from github.
      - supports the ACMEv2 protocol
      - supports the new challenge method 'tls-alpn-01' 

Modified: httpd/httpd/trunk/modules/md/md.h
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/md/md.h?rev=1862075&r1=1862074&r2=1862075&view=diff
==============================================================================
--- httpd/httpd/trunk/modules/md/md.h (original)
+++ httpd/httpd/trunk/modules/md/md.h Tue Jun 25 15:21:10 2019
@@ -29,8 +29,6 @@ struct md_store_t;
 struct md_srv_conf_t;
 struct md_pkey_spec_t;
 
-#define MD_TLSSNI01_DNS_SUFFIX     ".acme.invalid"
-
 #define MD_PKEY_RSA_BITS_MIN       2048
 #define MD_PKEY_RSA_BITS_DEF       2048
 

Modified: httpd/httpd/trunk/modules/md/mod_md.c
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/md/mod_md.c?rev=1862075&r1=1862074&r2=1862075&view=diff
==============================================================================
--- httpd/httpd/trunk/modules/md/mod_md.c (original)
+++ httpd/httpd/trunk/modules/md/mod_md.c Tue Jun 25 15:21:10 2019
@@ -919,8 +919,8 @@ static apr_status_t setup_fallback_cert(
     return rv;
 }
 
-static apr_status_t md_get_certificate(server_rec *s, apr_pool_t *p,
-                                       const char **pkeyfile, const char **pcertfile)
+static apr_status_t get_certificate(server_rec *s, apr_pool_t *p, int fallback,
+                                    const char **pcertfile, const char **pkeyfile)
 {
     apr_status_t rv = APR_ENOENT;    
     md_srv_conf_t *sc;
@@ -932,11 +932,11 @@ static apr_status_t md_get_certificate(s
     *pcertfile = NULL;
 
     ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(10113)
-                 "md_get_certificate called for vhost %s.", s->server_hostname);
+                 "get_certificate called for vhost %s.", s->server_hostname);
 
     sc = md_config_get(s);
     if (!sc) {
-        ap_log_error(APLOG_MARK, APLOG_TRACE1, 0, s,  
+        ap_log_error(APLOG_MARK, APLOG_TRACE2, 0, s,  
                      "asked for certificate of server %s which has no md config", 
                      s->server_hostname);
         return APR_ENOENT;
@@ -971,23 +971,25 @@ static apr_status_t md_get_certificate(s
     
     rv = md_reg_get_cred_files(pkeyfile, pcertfile, reg, MD_SG_DOMAINS, md, p);
     if (APR_STATUS_IS_ENOENT(rv)) {
-        /* Provide temporary, self-signed certificate as fallback, so that
-         * clients do not get obscure TLS handshake errors or will see a fallback
-         * virtual host that is not intended to be served here. */
-        store = md_reg_store_get(reg);
-        assert(store);    
-        
-        md_store_get_fname(pkeyfile, store, MD_SG_DOMAINS, md->name, MD_FN_FALLBACK_PKEY, p);
-        md_store_get_fname(pcertfile, store, MD_SG_DOMAINS, md->name, MD_FN_FALLBACK_CERT, p);
-        if (!md_file_exists(*pkeyfile, p) || !md_file_exists(*pcertfile, p)) { 
-            if (APR_SUCCESS != (rv = setup_fallback_cert(store, md, s, p))) {
-                return rv;
+        if (fallback) {
+            /* Provide temporary, self-signed certificate as fallback, so that
+             * clients do not get obscure TLS handshake errors or will see a fallback
+             * virtual host that is not intended to be served here. */
+            store = md_reg_store_get(reg);
+            assert(store);    
+            
+            md_store_get_fname(pkeyfile, store, MD_SG_DOMAINS, md->name, MD_FN_FALLBACK_PKEY, p);
+            md_store_get_fname(pcertfile, store, MD_SG_DOMAINS, md->name, MD_FN_FALLBACK_CERT, p);
+            if (!md_file_exists(*pkeyfile, p) || !md_file_exists(*pcertfile, p)) { 
+                if (APR_SUCCESS != (rv = setup_fallback_cert(store, md, s, p))) {
+                    return rv;
+                }
             }
+            ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(10116)  
+                         "%s: providing fallback certificate for server %s", 
+                         md->name, s->server_hostname);
+            return APR_EAGAIN;
         }
-        ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(10116)  
-                     "%s: providing fallback certificate for server %s", 
-                     md->name, s->server_hostname);
-        return APR_EAGAIN;
     }
     else if (APR_SUCCESS != rv) {
         ap_log_error(APLOG_MARK, APLOG_ERR, rv, s, APLOGNO(10110) 
@@ -1001,33 +1003,70 @@ static apr_status_t md_get_certificate(s
     return rv;
 }
 
+static apr_status_t md_get_certificate(server_rec *s, apr_pool_t *p,
+                                       const char **pkeyfile, const char **pcertfile)
+{
+    return get_certificate(s, p, 1, pcertfile, pkeyfile);
+}
+
+static int md_add_cert_files(server_rec *s, apr_pool_t *p,
+                             apr_array_header_t *cert_files, 
+                             apr_array_header_t *key_files)
+{
+    const char *certfile, *keyfile;
+    apr_status_t rv;
+    
+    ap_log_error(APLOG_MARK, APLOG_TRACE1, 0, s, "hook ssl_add_cert_files for %s",
+                 s->server_hostname);
+    rv = get_certificate(s, p, 0, &certfile, &keyfile);
+    if (APR_SUCCESS == rv) {
+        if (!apr_is_empty_array(cert_files)) {
+            ap_log_error(APLOG_MARK, APLOG_WARNING, 0, s, APLOGNO(10084)
+                         "host '%s' is covered by a Managed Domain, but "
+                         "certificate/key files are already configured "
+                         "for it (most likely via SSLCertificateFile).", 
+                         s->server_hostname);
+        } 
+        APR_ARRAY_PUSH(cert_files, const char*) = certfile;
+        APR_ARRAY_PUSH(key_files, const char*) = keyfile;
+        return DONE;
+    }
+    return DECLINED;
+}
+
+static int md_add_fallback_cert_files(server_rec *s, apr_pool_t *p,
+                                      apr_array_header_t *cert_files, 
+                                      apr_array_header_t *key_files)
+{
+    const char *certfile, *keyfile;
+    apr_status_t rv;
+    
+    ap_log_error(APLOG_MARK, APLOG_TRACE1, 0, s, "hook ssl_add_fallback_cert_files for %s",
+                 s->server_hostname);
+    rv = get_certificate(s, p, 1, &certfile, &keyfile);
+    if (APR_EAGAIN == rv) {
+        APR_ARRAY_PUSH(cert_files, const char*) = certfile;
+        APR_ARRAY_PUSH(key_files, const char*) = keyfile;
+        return DONE;
+    }
+    return DECLINED;
+}
+
 static int md_is_challenge(conn_rec *c, const char *servername,
                            X509 **pcert, EVP_PKEY **pkey)
 {
     md_srv_conf_t *sc;
-    apr_size_t slen, sufflen = sizeof(MD_TLSSNI01_DNS_SUFFIX) - 1;
     const char *protocol, *challenge, *cert_name, *pkey_name;
     apr_status_t rv;
 
     if (!servername) goto out;
                   
     challenge = NULL;
-    slen = strlen(servername);
-    if (slen > sufflen 
-        && !apr_strnatcasecmp(MD_TLSSNI01_DNS_SUFFIX, servername + slen - sufflen)) {
-        /* server name ends with the tls-sni-01 challenge suffix, answer if
-         * we have prepared a certificate in store under this name */
-        challenge = "tls-sni-01";
-        cert_name = MD_FN_TLSSNI01_CERT;
-        pkey_name = MD_FN_TLSSNI01_PKEY;
-    }
-    else if ((protocol = md_protocol_get(c)) && !strcmp(PROTO_ACME_TLS_1, protocol)) {
+    if ((protocol = md_protocol_get(c)) && !strcmp(PROTO_ACME_TLS_1, protocol)) {
         challenge = "tls-alpn-01";
         cert_name = MD_FN_TLSALPN01_CERT;
         pkey_name = MD_FN_TLSALPN01_PKEY;
-    }
-    
-    if (challenge) {
+
         sc = md_config_get(c->base_server);
         if (sc && sc->mc->reg) {
             md_store_t *store = md_reg_store_get(sc->mc->reg);
@@ -1061,6 +1100,15 @@ out:
     return 0;
 }
 
+static int md_answer_challenge(conn_rec *c, const char *servername,
+                               void **pX509, void **pEVP_PKEY)
+{
+    if (md_is_challenge(c, servername, (X509**)pX509, (EVP_PKEY**)pEVP_PKEY)) {
+        return APR_SUCCESS;
+    }
+    return DECLINED;
+}
+
 /**************************************************************************************************/
 /* ACME 'http-01' challenge responses */
 
@@ -1230,8 +1278,16 @@ static void md_hooks(apr_pool_t *pool)
     APR_OPTIONAL_HOOK(ap, status_hook, md_status_hook, NULL, NULL, APR_HOOK_MIDDLE);
     ap_hook_handler(md_status_handler, NULL, NULL, APR_HOOK_MIDDLE);
 
+#ifdef SSL_CERT_HOOKS
+    (void)md_is_managed;
+    (void)md_get_certificate;
+    APR_OPTIONAL_HOOK(ssl, add_cert_files, md_add_cert_files, NULL, NULL, APR_HOOK_MIDDLE);
+    APR_OPTIONAL_HOOK(ssl, add_fallback_cert_files, md_add_fallback_cert_files, NULL, NULL, APR_HOOK_MIDDLE);
+    APR_OPTIONAL_HOOK(ssl, answer_challenge, md_answer_challenge, NULL, NULL, APR_HOOK_MIDDLE);
+#else
+    APR_REGISTER_OPTIONAL_FN(md_is_challenge);
     APR_REGISTER_OPTIONAL_FN(md_is_managed);
     APR_REGISTER_OPTIONAL_FN(md_get_certificate);
-    APR_REGISTER_OPTIONAL_FN(md_is_challenge);
+#endif
 }
 

Modified: httpd/httpd/trunk/modules/ssl/mod_ssl.h
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/ssl/mod_ssl.h?rev=1862075&r1=1862074&r2=1862075&view=diff
==============================================================================
--- httpd/httpd/trunk/modules/ssl/mod_ssl.h (original)
+++ httpd/httpd/trunk/modules/ssl/mod_ssl.h Tue Jun 25 15:21:10 2019
@@ -30,6 +30,8 @@
 #include "http_config.h"
 #include "apr_optional.h"
 
+struct apr_array_header_t;
+
 /* Create a set of SSL_DECLARE(type), SSL_DECLARE_NONSTD(type) and
  * SSL_DECLARE_DATA with appropriate export and import tags for the platform
  */
@@ -95,6 +97,36 @@ APR_DECLARE_OPTIONAL_FN(int, ssl_engine_
 APR_DECLARE_OPTIONAL_FN(int, ssl_engine_set, (conn_rec *,
                                               ap_conf_vector_t *,
                                               int proxy, int enable));
+                                              
+/* Check for availability of new hooks */
+#define SSL_CERT_HOOKS
+#ifdef SSL_CERT_HOOKS
+
+/** Lets others add certificate and key files to the given server.
+ * For each cert a key must also be added. */
+APR_DECLARE_EXTERNAL_HOOK(ssl, SSL, int, add_cert_files,
+                          (server_rec *s, apr_pool_t *p, 
+                           struct apr_array_header_t *cert_files,
+                           struct apr_array_header_t *key_files))
+
+/** In case no certificates are available for a server, this
+ * lets other modules add a fallback certificate for the time
+ * being. Regular requests against this server will be answered
+ * with a 503. */
+APR_DECLARE_EXTERNAL_HOOK(ssl, SSL, int, add_fallback_cert_files,
+                          (server_rec *s, apr_pool_t *p, 
+                           struct apr_array_header_t *cert_files,
+                           struct apr_array_header_t *key_files))
+
+/** On TLS connections that do not relate to a configured virtual host,
+ * allow other modules to provide a X509 certificate and EVP_PKEY to
+ * be used on the connection. This first hook which does not
+ * return DECLINDED will determine the outcome. */
+APR_DECLARE_EXTERNAL_HOOK(ssl, SSL, int, answer_challenge,
+                          (conn_rec *c, const char *server_name, 
+                          void **pX509, void **pEVP_PKEY))
+                          
+#endif /* SSL_CERT_HOOKS */
 
 #endif /* __MOD_SSL_H__ */
 /** @} */

Modified: httpd/httpd/trunk/modules/ssl/ssl_engine_init.c
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/ssl/ssl_engine_init.c?rev=1862075&r1=1862074&r2=1862075&view=diff
==============================================================================
--- httpd/httpd/trunk/modules/ssl/ssl_engine_init.c (original)
+++ httpd/httpd/trunk/modules/ssl/ssl_engine_init.c Tue Jun 25 15:21:10 2019
@@ -39,6 +39,27 @@ APR_IMPLEMENT_OPTIONAL_HOOK_RUN_ALL(ssl,
                                     (server_rec *s,apr_pool_t *p,int is_proxy,SSL_CTX *ctx),
                                     (s,p,is_proxy,ctx), OK, DECLINED)
 
+/* Implement 'ap_run_ssl_add_cert_files'. */
+APR_IMPLEMENT_OPTIONAL_HOOK_RUN_ALL(ssl, SSL, int, add_cert_files,
+                                    (server_rec *s, apr_pool_t *p, 
+                                    apr_array_header_t *cert_files, apr_array_header_t *key_files),
+                                    (s, p, cert_files, key_files),
+                                    OK, DECLINED)
+
+APR_IMPLEMENT_OPTIONAL_HOOK_RUN_ALL(ssl, SSL, int, add_fallback_cert_files,
+                                    (server_rec *s, apr_pool_t *p, 
+                                    apr_array_header_t *cert_files, apr_array_header_t *key_files),
+                                    (s, p, cert_files, key_files),
+                                    OK, DECLINED)
+
+APR_IMPLEMENT_OPTIONAL_HOOK_RUN_ALL(ssl, SSL, int, answer_challenge,
+                                    (conn_rec *c, const char *server_name, 
+                                    void **pX509, void **pEVP_PKEY),
+                                    (c, server_name, pX509, pEVP_PKEY),
+                                    DECLINED, DECLINED)
+
+
+
 /*  _________________________________________________________________
 **
 **  Module Initialization
@@ -170,19 +191,16 @@ static void ssl_add_version_components(a
 
 /*  _________________________________________________________________
 **
-**  Managed Domains Interface
+**  Let other answer special connection attempts. 
+**  Used in ACME challenge handling by mod_md.
 **  _________________________________________________________________
 */
 
-static APR_OPTIONAL_FN_TYPE(md_is_managed) *md_is_managed;
-static APR_OPTIONAL_FN_TYPE(md_get_certificate) *md_get_certificate;
-static APR_OPTIONAL_FN_TYPE(md_is_challenge) *md_is_challenge;
-
 int ssl_is_challenge(conn_rec *c, const char *servername, 
                      X509 **pcert, EVP_PKEY **pkey)
 {
-    if (md_is_challenge) {
-        return md_is_challenge(c, servername, pcert, pkey);
+    if (APR_SUCCESS == ssl_run_answer_challenge(c, servername, (void**)pcert, (void**)pkey)) {
+        return 1;
     }
     *pcert = NULL;
     *pkey = NULL;
@@ -237,16 +255,6 @@ apr_status_t ssl_init_Module(apr_pool_t
     ssl_config_global_create(base_server); /* just to avoid problems */
     ssl_config_global_fix(mc);
 
-    /* Initialize our interface to mod_md, if it is loaded 
-     */
-    md_is_managed = APR_RETRIEVE_OPTIONAL_FN(md_is_managed);
-    md_get_certificate = APR_RETRIEVE_OPTIONAL_FN(md_get_certificate);
-    md_is_challenge = APR_RETRIEVE_OPTIONAL_FN(md_is_challenge);
-    if (!md_is_managed || !md_get_certificate) {
-        md_is_managed = NULL;
-        md_get_certificate = NULL;
-    }
-
     /*
      *  try to fix the configuration and open the dedicated SSL
      *  logfile as early as possible
@@ -1798,6 +1806,7 @@ static apr_status_t ssl_init_server_ctx(
     SSL_CONF_CTX *cctx = sc->server->ssl_ctx_config;
     int i;
 #endif
+    int n;
 
     /*
      *  Check for problematic re-initializations
@@ -1809,52 +1818,24 @@ static apr_status_t ssl_init_server_ctx(
         return APR_EGENERAL;
     }
 
-    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(10083)
-                 "Init: (%s) mod_md support is %s.", ssl_util_vhostid(p, s),
-                 md_is_managed? "available" : "unavailable");
-    if (md_is_managed && md_get_certificate && md_is_managed(s)) {
-        pks = sc->server->pks;
-        if (pks->cert_files->nelts > 0 || pks->key_files->nelts > 0) {
-            ap_log_error(APLOG_MARK, APLOG_WARNING, 0, s, APLOGNO(10084)
-                         "Init: (%s) You configured certificate/key files on this host, but "
-                         "it is covered by a Managed Domain. You need to remove these directives "
-                         "for the Managed Domain to take over.", ssl_util_vhostid(p, s));
-        }
-        else {
-            const char *key_file = NULL, *cert_file = NULL;
-            int service_unavailable = 0;
-            
-            rv = md_get_certificate(s, p, &key_file, &cert_file);
-            
-            if (APR_STATUS_IS_EAGAIN(rv)) {
-                /* Managed Domain not ready yet. This is not a reason to fail the config */
-                ap_log_error(APLOG_MARK, APLOG_WARNING, 0, s, APLOGNO(10085)
-                             "Init: %s will respond with '503 Service Unavailable' for now. This "
-                             "host is part of a Managed Domain, but no SSL certificate is "
-                             "available (yet).", ssl_util_vhostid(p, s));
-                service_unavailable = 1;
-                rv = APR_SUCCESS;
-            }
-            else if (rv != APR_SUCCESS) {
-                return rv;
-            }
-            
-            /* We install the managed certificate and key in the server_rec config now
-             * and, if those are not ready/fallback ones, mark service as unavailable.
-             */
-            if (APR_SUCCESS == rv && key_file && cert_file) {
-                ap_log_error(APLOG_MARK, APLOG_TRACE1, 0, s, 
-                             "%s: installing key=%s, cert=%s", 
-                             sc->vhost_id, key_file, cert_file);
-                APR_ARRAY_PUSH(pks->key_files, const char *) = key_file;
-                APR_ARRAY_PUSH(pks->cert_files, const char *) = cert_file;
-                sc->server->cert_chain = NULL;
-            }
-            
-            if (service_unavailable) {
-                pks->service_unavailable = 1;
-            }
-        }
+    /* Allow others to provide certificate files */
+    pks = sc->server->pks;
+    n = pks->cert_files->nelts;
+    ssl_run_add_cert_files(s, p, pks->cert_files, pks->key_files);
+
+    if (n < pks->cert_files->nelts) {
+        /* this overrides any old chain configuration */
+        sc->server->cert_chain = NULL;
+    }
+    
+    if (apr_is_empty_array(pks->cert_files) && !sc->server->cert_chain) {
+        ssl_run_add_fallback_cert_files(s, p, pks->cert_files, pks->key_files);
+        
+        pks->service_unavailable = 1;
+        ap_log_error(APLOG_MARK, APLOG_WARNING, 0, s, APLOGNO(10085)
+                     "Init: %s will respond with '503 Service Unavailable' for now. This "
+                     "has no SSL certificate configured and no other module contributed any.",
+                     ssl_util_vhostid(p, s));
     }
     
     if ((rv = ssl_init_ctx(s, p, ptemp, sc->server)) != APR_SUCCESS) {