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/24 16:04:33 UTC

svn commit: r1862013 [7/8] - in /httpd/httpd/trunk: ./ modules/md/

Modified: httpd/httpd/trunk/modules/md/mod_md.c
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/md/mod_md.c?rev=1862013&r1=1862012&r2=1862013&view=diff
==============================================================================
--- httpd/httpd/trunk/modules/md/mod_md.c (original)
+++ httpd/httpd/trunk/modules/md/mod_md.c Mon Jun 24 16:04:32 2019
@@ -31,6 +31,8 @@
 #include <http_vhost.h>
 #include <ap_listen.h>
 
+#include "mod_status.h"
+
 #include "md.h"
 #include "md_curl.h"
 #include "md_crypt.h"
@@ -39,6 +41,7 @@
 #include "md_store.h"
 #include "md_store_fs.h"
 #include "md_log.h"
+#include "md_result.h"
 #include "md_reg.h"
 #include "md_util.h"
 #include "md_version.h"
@@ -47,9 +50,10 @@
 
 #include "mod_md.h"
 #include "mod_md_config.h"
+#include "mod_md_drive.h"
 #include "mod_md_os.h"
+#include "mod_md_status.h"
 #include "mod_ssl.h"
-#include "mod_watchdog.h"
 
 static void md_hooks(apr_pool_t *pool);
 
@@ -66,7 +70,144 @@ AP_DECLARE_MODULE(md) = {
 #endif
 };
 
-static void md_merge_srv(md_t *md, md_srv_conf_t *base_sc, apr_pool_t *p)
+/**************************************************************************************************/
+/* logging setup */
+
+static server_rec *log_server;
+
+static int log_is_level(void *baton, apr_pool_t *p, md_log_level_t level)
+{
+    (void)baton;
+    (void)p;
+    if (log_server) {
+        return APLOG_IS_LEVEL(log_server, (int)level);
+    }
+    return level <= MD_LOG_INFO;
+}
+
+#define LOG_BUF_LEN 16*1024
+
+static void log_print(const char *file, int line, md_log_level_t level, 
+                      apr_status_t rv, void *baton, apr_pool_t *p, const char *fmt, va_list ap)
+{
+    if (log_is_level(baton, p, level)) {
+        char buffer[LOG_BUF_LEN];
+        
+        memset(buffer, 0, sizeof(buffer));
+        apr_vsnprintf(buffer, LOG_BUF_LEN-1, fmt, ap);
+        buffer[LOG_BUF_LEN-1] = '\0';
+
+        if (log_server) {
+            ap_log_error(file, line, APLOG_MODULE_INDEX, (int)level, rv, log_server, "%s",buffer);
+        }
+        else {
+            ap_log_perror(file, line, APLOG_MODULE_INDEX, (int)level, rv, p, "%s", buffer);
+        }
+    }
+}
+
+/**************************************************************************************************/
+/* mod_ssl interface */
+
+static APR_OPTIONAL_FN_TYPE(ssl_is_https) *opt_ssl_is_https;
+
+static void init_ssl(void)
+{
+    opt_ssl_is_https = APR_RETRIEVE_OPTIONAL_FN(ssl_is_https);
+}
+
+/**************************************************************************************************/
+/* lifecycle */
+
+static apr_status_t cleanup_setups(void *dummy)
+{
+    (void)dummy;
+    log_server = NULL;
+    return APR_SUCCESS;
+}
+
+static void init_setups(apr_pool_t *p, server_rec *base_server) 
+{
+    log_server = base_server;
+    apr_pool_cleanup_register(p, NULL, cleanup_setups, apr_pool_cleanup_null);
+}
+
+/**************************************************************************************************/
+/* store & registry setup */
+
+static apr_status_t store_file_ev(void *baton, struct md_store_t *store,
+                                    md_store_fs_ev_t ev, unsigned int group, 
+                                    const char *fname, apr_filetype_e ftype,  
+                                    apr_pool_t *p)
+{
+    server_rec *s = baton;
+    apr_status_t rv;
+    
+    (void)store;
+    ap_log_error(APLOG_MARK, APLOG_TRACE3, 0, s, "store event=%d on %s %s (group %d)", 
+                 ev, (ftype == APR_DIR)? "dir" : "file", fname, group);
+                 
+    /* Directories in group CHALLENGES and STAGING are written to under a different user. 
+     * Give him ownership. 
+     */
+    if (ftype == APR_DIR) {
+        switch (group) {
+            case MD_SG_CHALLENGES:
+            case MD_SG_STAGING:
+                rv = md_make_worker_accessible(fname, p);
+                if (APR_ENOTIMPL != rv) {
+                    return rv;
+                }
+                break;
+            default: 
+                break;
+        }
+    }
+    return APR_SUCCESS;
+}
+
+static apr_status_t check_group_dir(md_store_t *store, md_store_group_t group, 
+                                    apr_pool_t *p, server_rec *s)
+{
+    const char *dir;
+    apr_status_t rv;
+    
+    if (APR_SUCCESS == (rv = md_store_get_fname(&dir, store, group, NULL, NULL, p))
+        && APR_SUCCESS == (rv = apr_dir_make_recursive(dir, MD_FPROT_D_UALL_GREAD, p))) {
+        rv = store_file_ev(s, store, MD_S_FS_EV_CREATED, group, dir, APR_DIR, p);
+    }
+    return rv;
+}
+
+static apr_status_t setup_store(md_store_t **pstore, md_mod_conf_t *mc, 
+                                apr_pool_t *p, server_rec *s)
+{
+    const char *base_dir;
+    apr_status_t rv;
+    
+    base_dir = ap_server_root_relative(p, mc->base_dir);
+    
+    if (APR_SUCCESS != (rv = md_store_fs_init(pstore, p, base_dir))) {
+        ap_log_error(APLOG_MARK, APLOG_ERR, rv, s, APLOGNO(10046)"setup store for %s", base_dir);
+        goto out;
+    }
+
+    md_store_fs_set_event_cb(*pstore, store_file_ev, s);
+    if (APR_SUCCESS != (rv = check_group_dir(*pstore, MD_SG_CHALLENGES, p, s))
+        || APR_SUCCESS != (rv = check_group_dir(*pstore, MD_SG_STAGING, p, s))
+        || APR_SUCCESS != (rv = check_group_dir(*pstore, MD_SG_ACCOUNTS, p, s))) {
+        ap_log_error(APLOG_MARK, APLOG_ERR, rv, s, APLOGNO(10047) 
+                     "setup challenges directory");
+    }
+    
+out:
+    return rv;
+}
+
+/**************************************************************************************************/
+/* post config handling */
+
+static void merge_srv_config(md_t *md, md_srv_conf_t *base_sc, apr_pool_t *p)
 {
     if (!md->sc) {
         md->sc = base_sc;
@@ -86,13 +227,11 @@ static void md_merge_srv(md_t *md, md_sr
         APR_ARRAY_PUSH(md->contacts, const char *) = 
         md_util_schemify(p, md->sc->s->server_admin, "mailto");
     }
-    if (md->drive_mode == MD_DRIVE_DEFAULT) {
-        md->drive_mode = md_config_geti(md->sc, MD_CONFIG_DRIVE_MODE);
-    }
-    if (md->renew_norm <= 0 && md->renew_window <= 0) {
-        md->renew_norm = md_config_get_interval(md->sc, MD_CONFIG_RENEW_NORM);
-        md->renew_window = md_config_get_interval(md->sc, MD_CONFIG_RENEW_WINDOW);
+    if (md->renew_mode == MD_RENEW_DEFAULT) {
+        md->renew_mode = md_config_geti(md->sc, MD_CONFIG_DRIVE_MODE);
     }
+    if (!md->renew_window) md_config_get_timespan(&md->renew_window, md->sc, MD_CONFIG_RENEW_WINDOW);
+    if (!md->warn_window) md_config_get_timespan(&md->warn_window, md->sc, MD_CONFIG_WARN_WINDOW);
     if (md->transitive < 0) {
         md->transitive = md_config_geti(md->sc, MD_CONFIG_TRANSITIVE);
     }
@@ -164,7 +303,7 @@ static int matches_port_somewhere(server
     return 0;
 }
 
-static int uses_port_only(server_rec *s, int port)
+static int uses_port(server_rec *s, int port)
 {
     server_addr_rec *sa;
     int match = 0;
@@ -181,20 +320,98 @@ static int uses_port_only(server_rec *s,
     return match;
 }
 
-static apr_status_t assign_to_servers(md_t *md, server_rec *base_server, 
-                                     apr_pool_t *p, apr_pool_t *ptemp)
+static apr_status_t detect_supported_ports(md_mod_conf_t *mc, server_rec *s, 
+                                           apr_pool_t *p, int log_level)
+{
+    ap_listen_rec *lr;
+    apr_sockaddr_t *sa;
+
+    mc->can_http = 0;
+    mc->can_https = 0;
+    for (lr = ap_listeners; lr; lr = lr->next) {
+        for (sa = lr->bind_addr; sa; sa = sa->next) {
+            if  (sa->port == mc->local_80 
+                 && (!lr->protocol || !strncmp("http", lr->protocol, 4))) {
+                mc->can_http = 1;
+            }
+            else if (sa->port == mc->local_443
+                     && (!lr->protocol || !strncmp("http", lr->protocol, 4))) {
+                mc->can_https = 1;
+            }
+        }
+    }
+
+    ap_log_error(APLOG_MARK, log_level, 0, s, APLOGNO(10037)
+                 "server seems%s reachable via http: (port 80->%d) "
+                 "and%s reachable via https: (port 443->%d) ",
+                 mc->can_http? "" : " not", mc->local_80,
+                 mc->can_https? "" : " not", mc->local_443);
+    return md_reg_set_props(mc->reg, p, mc->can_http, mc->can_https); 
+}
+
+static server_rec *get_https_server(const char *domain, server_rec *base_server)
+{
+    md_srv_conf_t *sc;
+    md_mod_conf_t *mc;
+    server_rec *s;
+    request_rec r;
+
+    sc = md_config_get(base_server);
+    mc = sc->mc;
+    memset(&r, 0, sizeof(r));
+    
+    for (s = base_server; s && (mc->local_443 > 0); s = s->next) {
+        if (!mc->manage_base_server && s == base_server) {
+            /* we shall not assign ourselves to the base server */
+            continue;
+        }
+        r.server = s;
+        if (ap_matches_request_vhost(&r, domain, s->port) && uses_port(s, mc->local_443)) {
+            return s;
+        }
+    }
+    return NULL;
+}
+
+static void init_acme_tls_1_domains(md_t *md, server_rec *base_server)
+{
+    server_rec *s;
+    int i;
+    const char *domain;
+    
+    /* Collect those domains that support the "acme-tls/1" protocol. This
+     * is part of the MD (and not tested dynamically), since challenge selection
+     * may be done outside the server, e.g. in the a2md command. */
+     apr_array_clear(md->acme_tls_1_domains);
+    for (i = 0; i < md->domains->nelts; ++i) {
+        domain = APR_ARRAY_IDX(md->domains, i, const char*);
+        if (NULL == (s = get_https_server(domain, base_server))) {
+            ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, base_server, APLOGNO()
+                         "%s: no https server_rec found for %s", md->name, domain);
+            continue;
+        }
+        if (!ap_is_allowed_protocol(NULL, NULL, s, PROTO_ACME_TLS_1)) {
+            ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, base_server, APLOGNO()
+                         "%s: https server_rec for %s does not have protocol %s enabled", 
+                         md->name, domain, PROTO_ACME_TLS_1);
+            continue;
+        }
+        APR_ARRAY_PUSH(md->acme_tls_1_domains, const char*) = domain;
+    }
+}
+
+static apr_status_t link_md_to_servers(md_mod_conf_t *mc, md_t *md, server_rec *base_server, 
+                                       apr_pool_t *p, apr_pool_t *ptemp)
 {
     server_rec *s, *s_https;
     request_rec r;
     md_srv_conf_t *sc;
-    md_mod_conf_t *mc;
     apr_status_t rv = APR_SUCCESS;
     int i;
     const char *domain;
     apr_array_header_t *servers;
     
     sc = md_config_get(base_server);
-    mc = sc->mc;
 
     /* Assign the MD to all server_rec configs that it matches. If there already
      * is an assigned MD not equal this one, the configuration is in error.
@@ -222,765 +439,278 @@ static apr_status_t assign_to_servers(md
                 
                 if (sc->assigned == md) {
                     /* already matched via another domain name */
-                    goto next_server;
-                }
-                else if (sc->assigned) {
-                    ap_log_error(APLOG_MARK, APLOG_ERR, 0, base_server, APLOGNO(10042)
-                                 "conflict: MD %s matches server %s, but MD %s also matches.",
-                                 md->name, s->server_hostname, sc->assigned->name);
-                    return APR_EINVAL;
-                }
-                
-                /* If this server_rec is only for http: requests. Defined
-                 * alias names to not matter for this MD.
-                 * (see gh issue https://github.com/icing/mod_md/issues/57)
-                 * Otherwise, if server has name or an alias not covered,
-                 * it is by default auto-added (config transitive).
-                 * If mode is "manual", a generated certificate will not match
-                 * all necessary names. */
-                if ((!mc->local_80 || !uses_port_only(s, mc->local_80))
-                    && APR_SUCCESS != (rv = md_covers_server(md, s, p))) {
-                    return rv;
-                }
-
-                sc->assigned = md;
-                APR_ARRAY_PUSH(servers, server_rec*) = s;
-                
-                ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, base_server, APLOGNO(10043)
-                             "Managed Domain %s applies to vhost %s:%d", md->name,
-                             s->server_hostname, s->port);
-                
-                goto next_server;
-            }
-        }
-    next_server:
-        continue;
-    }
-
-    if (APR_SUCCESS == rv) {
-        if (apr_is_empty_array(servers)) {
-            if (md->drive_mode != MD_DRIVE_ALWAYS) {
-                /* Not an error, but looks suspicious */
-                ap_log_error(APLOG_MARK, APLOG_WARNING, 0, base_server, APLOGNO(10045)
-                             "No VirtualHost matches Managed Domain %s", md->name);
-                APR_ARRAY_PUSH(mc->unused_names, const char*)  = md->name;
-            }
-        }
-        else {
-            const char *uri;
-            
-            /* Found matching server_rec's. Collect all 'ServerAdmin's into MD's contact list */
-            apr_array_clear(md->contacts);
-            for (i = 0; i < servers->nelts; ++i) {
-                s = APR_ARRAY_IDX(servers, i, server_rec*);
-                if (s->server_admin && strcmp(DEFAULT_ADMIN, s->server_admin)) {
-                    uri = md_util_schemify(p, s->server_admin, "mailto");
-                    if (md_array_str_index(md->contacts, uri, 0, 0) < 0) {
-                        APR_ARRAY_PUSH(md->contacts, const char *) = uri; 
-                        ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, base_server, APLOGNO(10044)
-                                     "%s: added contact %s", md->name, uri);
-                    }
-                }
-            }
-            
-            if (md->require_https > MD_REQUIRE_OFF) {
-                /* We require https for this MD, but do we have port 443 (or a mapped one)
-                 * available? */
-                if (mc->local_443 <= 0) {
-                    ap_log_error(APLOG_MARK, APLOG_ERR, 0, base_server, APLOGNO(10105)
-                                 "MDPortMap says there is no port for https (443), "
-                                 "but MD %s is configured to require https. This "
-                                 "only works when a 443 port is available.", md->name);
-                    return APR_EINVAL;
-                    
-                }
-                
-                /* Ok, we know which local port represents 443, do we have a server_rec
-                 * for MD that has addresses with port 443? */
-                s_https = NULL;
-                for (i = 0; i < servers->nelts; ++i) {
-                    s = APR_ARRAY_IDX(servers, i, server_rec*);
-                    if (matches_port_somewhere(s, mc->local_443)) {
-                        s_https = s;
-                        break;
-                    }
-                }
-                
-                if (!s_https) {
-                    /* Did not find any server_rec that matches this MD *and* has an
-                     * s->addrs match for the https port. Suspicious. */
-                    ap_log_error(APLOG_MARK, APLOG_WARNING, 0, base_server, APLOGNO(10106)
-                                 "MD %s is configured to require https, but there seems to be "
-                                 "no VirtualHost for it that has port %d in its address list. "
-                                 "This looks as if it will not work.", 
-                                 md->name, mc->local_443);
-                }
-            }
-        }
-        
-    }
-    return rv;
-}
-
-static apr_status_t md_calc_md_list(apr_pool_t *p, apr_pool_t *plog,
-                                    apr_pool_t *ptemp, server_rec *base_server)
-{
-    md_srv_conf_t *sc;
-    md_mod_conf_t *mc;
-    md_t *md, *omd;
-    const char *domain;
-    apr_status_t rv = APR_SUCCESS;
-    ap_listen_rec *lr;
-    apr_sockaddr_t *sa;
-    int i, j;
-
-    (void)plog;
-    sc = md_config_get(base_server);
-    mc = sc->mc;
-    
-    mc->can_http = 0;
-    mc->can_https = 0;
-
-    for (lr = ap_listeners; lr; lr = lr->next) {
-        for (sa = lr->bind_addr; sa; sa = sa->next) {
-            if  (sa->port == mc->local_80 
-                 && (!lr->protocol || !strncmp("http", lr->protocol, 4))) {
-                mc->can_http = 1;
-            }
-            else if (sa->port == mc->local_443
-                     && (!lr->protocol || !strncmp("http", lr->protocol, 4))) {
-                mc->can_https = 1;
-            }
-        }
-    }
-    
-    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, base_server, APLOGNO(10037)
-                 "server seems%s reachable via http: (port 80->%d) "
-                 "and%s reachable via https: (port 443->%d) ",
-                 mc->can_http? "" : " not", mc->local_80,
-                 mc->can_https? "" : " not", mc->local_443);
-    
-    /* Complete the properties of the MDs, now that we have the complete, merged
-     * server configurations. 
-     */
-    for (i = 0; i < mc->mds->nelts; ++i) {
-        md = APR_ARRAY_IDX(mc->mds, i, md_t*);
-        md_merge_srv(md, sc, p);
-
-        /* Check that we have no overlap with the MDs already completed */
-        for (j = 0; j < i; ++j) {
-            omd = APR_ARRAY_IDX(mc->mds, j, md_t*);
-            if ((domain = md_common_name(md, omd)) != NULL) {
-                ap_log_error(APLOG_MARK, APLOG_ERR, 0, base_server, APLOGNO(10038)
-                             "two Managed Domains have an overlap in domain '%s'"
-                             ", first definition in %s(line %d), second in %s(line %d)",
-                             domain, md->defn_name, md->defn_line_number,
-                             omd->defn_name, omd->defn_line_number);
-                return APR_EINVAL;
-            }
-        }
-
-        /* Assign MD to the server_rec configs that it matches. Perform some
-         * last finishing touches on the MD. */
-        if (APR_SUCCESS != (rv = assign_to_servers(md, base_server, p, ptemp))) {
-            return rv;
-        }
-
-        ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, base_server, APLOGNO(10039)
-                     "Completed MD[%s, CA=%s, Proto=%s, Agreement=%s, Drive=%d, renew=%ld]",
-                     md->name, md->ca_url, md->ca_proto, md->ca_agreement,
-                     md->drive_mode, (long)md->renew_window);
-    }
-    
-    return rv;
-}
-
-/**************************************************************************************************/
-/* store & registry setup */
-
-static apr_status_t store_file_ev(void *baton, struct md_store_t *store,
-                                    md_store_fs_ev_t ev, unsigned int group, 
-                                    const char *fname, apr_filetype_e ftype,  
-                                    apr_pool_t *p)
-{
-    server_rec *s = baton;
-    apr_status_t rv;
-    
-    (void)store;
-    ap_log_error(APLOG_MARK, APLOG_TRACE3, 0, s, "store event=%d on %s %s (group %d)", 
-                 ev, (ftype == APR_DIR)? "dir" : "file", fname, group);
-                 
-    /* Directories in group CHALLENGES and STAGING are written to by our watchdog,
-     * running on certain mpms in a child process under a different user. Give them
-     * ownership. 
-     */
-    if (ftype == APR_DIR) {
-        switch (group) {
-            case MD_SG_CHALLENGES:
-            case MD_SG_STAGING:
-                rv = md_make_worker_accessible(fname, p);
-                if (APR_ENOTIMPL != rv) {
-                    return rv;
-                }
-                break;
-            default: 
-                break;
-        }
-    }
-    return APR_SUCCESS;
-}
-
-static apr_status_t check_group_dir(md_store_t *store, md_store_group_t group, 
-                                    apr_pool_t *p, server_rec *s)
-{
-    const char *dir;
-    apr_status_t rv;
-    
-    if (APR_SUCCESS == (rv = md_store_get_fname(&dir, store, group, NULL, NULL, p))
-        && APR_SUCCESS == (rv = apr_dir_make_recursive(dir, MD_FPROT_D_UALL_GREAD, p))) {
-        rv = store_file_ev(s, store, MD_S_FS_EV_CREATED, group, dir, APR_DIR, p);
-    }
-    return rv;
-}
-
-static apr_status_t setup_store(md_store_t **pstore, md_mod_conf_t *mc, 
-                                apr_pool_t *p, server_rec *s)
-{
-    const char *base_dir;
-    apr_status_t rv;
-    MD_CHK_VARS;
-    
-    base_dir = ap_server_root_relative(p, mc->base_dir);
-    
-    if (!MD_OK(md_store_fs_init(pstore, p, base_dir))) {
-        ap_log_error(APLOG_MARK, APLOG_ERR, rv, s, APLOGNO(10046)"setup store for %s", base_dir);
-        goto out;
-    }
-
-    md_store_fs_set_event_cb(*pstore, store_file_ev, s);
-    if (   !MD_OK(check_group_dir(*pstore, MD_SG_CHALLENGES, p, s))
-        || !MD_OK(check_group_dir(*pstore, MD_SG_STAGING, p, s))
-        || !MD_OK(check_group_dir(*pstore, MD_SG_ACCOUNTS, p, s))) {
-        ap_log_error(APLOG_MARK, APLOG_ERR, rv, s, APLOGNO(10047) 
-                     "setup challenges directory, call %s", MD_LAST_CHK);
-    }
-    
-out:
-    return rv;
-}
-
-static apr_status_t setup_reg(md_reg_t **preg, apr_pool_t *p, server_rec *s, 
-                              int can_http, int can_https)
-{
-    md_srv_conf_t *sc;
-    md_mod_conf_t *mc;
-    md_store_t *store;
-    apr_status_t rv;
-    MD_CHK_VARS;
-    
-    sc = md_config_get(s);
-    mc = sc->mc;
-    
-    if (   MD_OK(setup_store(&store, mc, p, s))
-        && MD_OK(md_reg_init(preg, p, store, mc->proxy_url))) {
-        mc->reg = *preg;
-        return md_reg_set_props(*preg, p, can_http, can_https); 
-    }
-    return rv;
-}
-
-/**************************************************************************************************/
-/* logging setup */
-
-static server_rec *log_server;
-
-static int log_is_level(void *baton, apr_pool_t *p, md_log_level_t level)
-{
-    (void)baton;
-    (void)p;
-    if (log_server) {
-        return APLOG_IS_LEVEL(log_server, (int)level);
-    }
-    return level <= MD_LOG_INFO;
-}
-
-#define LOG_BUF_LEN 16*1024
-
-static void log_print(const char *file, int line, md_log_level_t level, 
-                      apr_status_t rv, void *baton, apr_pool_t *p, const char *fmt, va_list ap)
-{
-    if (log_is_level(baton, p, level)) {
-        char buffer[LOG_BUF_LEN];
-        
-        memset(buffer, 0, sizeof(buffer));
-        apr_vsnprintf(buffer, LOG_BUF_LEN-1, fmt, ap);
-        buffer[LOG_BUF_LEN-1] = '\0';
-
-        if (log_server) {
-            ap_log_error(file, line, APLOG_MODULE_INDEX, (int)level, rv, log_server, "%s",buffer);
-        }
-        else {
-            ap_log_perror(file, line, APLOG_MODULE_INDEX, (int)level, rv, p, "%s", buffer);
-        }
-    }
-}
-
-/**************************************************************************************************/
-/* lifecycle */
-
-static apr_status_t cleanup_setups(void *dummy)
-{
-    (void)dummy;
-    log_server = NULL;
-    return APR_SUCCESS;
-}
-
-static void init_setups(apr_pool_t *p, server_rec *base_server) 
-{
-    log_server = base_server;
-    apr_pool_cleanup_register(p, NULL, cleanup_setups, apr_pool_cleanup_null);
-}
-
-/**************************************************************************************************/
-/* mod_ssl interface */
-
-static APR_OPTIONAL_FN_TYPE(ssl_is_https) *opt_ssl_is_https;
-
-static void init_ssl(void)
-{
-    opt_ssl_is_https = APR_RETRIEVE_OPTIONAL_FN(ssl_is_https);
-}
-
-/**************************************************************************************************/
-/* watchdog based impl. */
-
-#define MD_WATCHDOG_NAME   "_md_"
-
-static APR_OPTIONAL_FN_TYPE(ap_watchdog_get_instance) *wd_get_instance;
-static APR_OPTIONAL_FN_TYPE(ap_watchdog_register_callback) *wd_register_callback;
-static APR_OPTIONAL_FN_TYPE(ap_watchdog_set_callback_interval) *wd_set_interval;
-
-typedef struct {
-    md_t *md;
-
-    int stalled;
-    int renewed;
-    int renewal_notified;
-    apr_time_t restart_at;
-    int need_restart;
-    int restart_processed;
-
-    apr_status_t last_rv;
-    apr_time_t next_check;
-    int error_runs;
-} md_job_t;
-
-typedef struct {
-    apr_pool_t *p;
-    server_rec *s;
-    md_mod_conf_t *mc;
-    ap_watchdog_t *watchdog;
-    
-    apr_time_t next_change;
-    
-    apr_array_header_t *jobs;
-    md_reg_t *reg;
-} md_watchdog;
-
-static void assess_renewal(md_watchdog *wd, md_job_t *job, apr_pool_t *ptemp) 
-{
-    apr_time_t now = apr_time_now();
-    if (now >= job->restart_at) {
-        job->need_restart = 1;
-        ap_log_error( APLOG_MARK, APLOG_TRACE1, 0, wd->s, 
-                     "md(%s): has been renewed, needs restart now", job->md->name);
-    }
-    else {
-        job->next_check = job->restart_at;
-        
-        if (job->renewal_notified) {
-            ap_log_error(APLOG_MARK, APLOG_TRACE1, 0, wd->s, 
-                         "%s: renewed cert valid in %s", 
-                         job->md->name, md_print_duration(ptemp, job->restart_at - now));
-        }
-        else {
-            char ts[APR_RFC822_DATE_LEN];
-
-            apr_rfc822_date(ts, job->restart_at);
-            ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, wd->s, APLOGNO(10051) 
-                         "%s: has been renewed successfully and should be activated at %s"
-                         " (this requires a server restart latest in %s)", 
-                         job->md->name, ts, md_print_duration(ptemp, job->restart_at - now));
-            job->renewal_notified = 1;
-        }
-    }
-}
-
-static apr_status_t load_job_props(md_reg_t *reg, md_job_t *job, apr_pool_t *p)
-{
-    md_store_t *store = md_reg_store_get(reg);
-    md_json_t *jprops;
-    apr_status_t rv;
-    
-    rv = md_store_load_json(store, MD_SG_STAGING, job->md->name,
-                            MD_FN_JOB, &jprops, p);
-    if (APR_SUCCESS == rv) {
-        job->restart_processed = md_json_getb(jprops, MD_KEY_PROCESSED, NULL);
-        job->error_runs = (int)md_json_getl(jprops, MD_KEY_ERRORS, NULL);
-    }
-    return rv;
-}
-
-static apr_status_t save_job_props(md_reg_t *reg, md_job_t *job, apr_pool_t *p)
-{
-    md_store_t *store = md_reg_store_get(reg);
-    md_json_t *jprops;
-    apr_status_t rv;
-    
-    rv = md_store_load_json(store, MD_SG_STAGING, job->md->name, MD_FN_JOB, &jprops, p);
-    if (APR_STATUS_IS_ENOENT(rv)) {
-        jprops = md_json_create(p);
-        rv = APR_SUCCESS;
-    }
-    if (APR_SUCCESS == rv) {
-        md_json_setb(job->restart_processed, jprops, MD_KEY_PROCESSED, NULL);
-        md_json_setl(job->error_runs, jprops, MD_KEY_ERRORS, NULL);
-        rv = md_store_save_json(store, p, MD_SG_STAGING, job->md->name,
-                                MD_FN_JOB, jprops, 0);
-    }
-    return rv;
-}
-
-static apr_status_t check_job(md_watchdog *wd, md_job_t *job, apr_pool_t *ptemp)
-{
-    apr_status_t rv = APR_SUCCESS;
-    apr_time_t valid_from, delay;
-    int errored, renew, error_runs;
-    char ts[APR_RFC822_DATE_LEN];
-    
-    if (apr_time_now() < job->next_check) {
-        /* Job needs to wait */
-        return APR_EAGAIN;
-    }
-    
-    job->next_check = 0;
-    error_runs = job->error_runs;
-
-    if (job->md->state == MD_S_MISSING) {
-        job->stalled = 1;
-    }
-    
-    if (job->stalled) {
-        /* Missing information, this will not change until configuration
-         * is changed and server restarted */
-        rv = APR_INCOMPLETE;
-        ++job->error_runs;
-        goto out;
-    }
-    else if (job->renewed) {
-        assess_renewal(wd, job, ptemp);
-    }
-    else if (APR_SUCCESS == (rv = md_reg_assess(wd->reg, job->md, &errored, &renew, wd->p))) {
-        if (errored) {
-            ap_log_error( APLOG_MARK, APLOG_DEBUG, 0, wd->s, APLOGNO(10050) 
-                         "md(%s): in error state", job->md->name);
-        }
-        else if (renew) {
-            ap_log_error( APLOG_MARK, APLOG_DEBUG, 0, wd->s, APLOGNO(10052) 
-                         "md(%s): state=%d, driving", job->md->name, job->md->state);
-                         
-            rv = md_reg_stage(wd->reg, job->md, NULL, 0, &valid_from, ptemp);
-            
-            if (APR_SUCCESS == rv) {
-                job->renewed = 1;
-                job->restart_at = valid_from;
-                assess_renewal(wd, job, ptemp);
-            }
-        }
-        else {
-            /* Renew is not necessary yet, leave job->next_check as 0 since 
-             * that keeps the default schedule of running twice a day. */
-            apr_rfc822_date(ts, job->md->expires);
-            ap_log_error( APLOG_MARK, APLOG_DEBUG, 0, wd->s, APLOGNO(10053) 
-                         "md(%s): no need to renew yet, cert expires %s", job->md->name, ts);
-        }
-    }
-    
-    if (APR_SUCCESS == rv) {
-        job->error_runs = 0;
-    }
-    else {
-        ap_log_error( APLOG_MARK, APLOG_ERR, rv, wd->s, APLOGNO(10056) 
-                     "processing %s", job->md->name);
-        ++job->error_runs;
-        /* back off duration, depending on the errors we encounter in a row */
-        delay = apr_time_from_sec(5 << (job->error_runs - 1));
-        if (delay > apr_time_from_sec(60*60)) {
-            delay = apr_time_from_sec(60*60);
-        }
-        job->next_check = apr_time_now() + delay;
-        ap_log_error(APLOG_MARK, APLOG_INFO, 0, wd->s, APLOGNO(10057) 
-                     "%s: encountered error for the %d. time, next run in %s",
-                     job->md->name, job->error_runs, md_print_duration(ptemp, delay));
-    }
-    
-out:
-    if (error_runs != job->error_runs) {
-        apr_status_t rv2 = save_job_props(wd->reg, job, ptemp);
-        ap_log_error(APLOG_MARK, APLOG_TRACE1, rv2, wd->s, "%s: saving job props", job->md->name);
-    }
-
-    job->last_rv = rv;
-    return rv;
-}
-
-static apr_status_t run_watchdog(int state, void *baton, apr_pool_t *ptemp)
-{
-    md_watchdog *wd = baton;
-    apr_status_t rv = APR_SUCCESS;
-    md_job_t *job;
-    apr_time_t next_run, now;
-    int restart = 0;
-    int i;
-    
-    switch (state) {
-        case AP_WATCHDOG_STATE_STARTING:
-            ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, wd->s, APLOGNO(10054)
-                         "md watchdog start, auto drive %d mds", wd->jobs->nelts);
-            assert(wd->reg);
-        
-            for (i = 0; i < wd->jobs->nelts; ++i) {
-                job = APR_ARRAY_IDX(wd->jobs, i, md_job_t *);
-                load_job_props(wd->reg, job, ptemp);
-            }
-            break;
-        case AP_WATCHDOG_STATE_RUNNING:
-        
-            wd->next_change = 0;
-            ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, wd->s, APLOGNO(10055)
-                         "md watchdog run, auto drive %d mds", wd->jobs->nelts);
-                         
-            /* normally, we'd like to run at least twice a day */
-            next_run = apr_time_now() + apr_time_from_sec(MD_SECS_PER_DAY / 2);
-
-            /* Check on all the jobs we have */
-            for (i = 0; i < wd->jobs->nelts; ++i) {
-                job = APR_ARRAY_IDX(wd->jobs, i, md_job_t *);
-                
-                rv = check_job(wd, job, ptemp);
-
-                if (job->need_restart && !job->restart_processed) {
-                    restart = 1;
+                    goto next_server;
                 }
-                if (job->next_check && job->next_check < next_run) {
-                    next_run = job->next_check;
+                else if (sc->assigned) {
+                    ap_log_error(APLOG_MARK, APLOG_ERR, 0, base_server, APLOGNO(10042)
+                                 "conflict: MD %s matches server %s, but MD %s also matches.",
+                                 md->name, s->server_hostname, sc->assigned->name);
+                    return APR_EINVAL;
+                }
+                
+                /* If this server_rec is only for http: requests. Defined
+                 * alias names do not matter for this MD.
+                 * (see gh issue https://github.com/icing/mod_md/issues/57)
+                 * Otherwise, if server has name or an alias not covered,
+                 * it is by default auto-added (config transitive).
+                 * If mode is "manual", a generated certificate will not match
+                 * all necessary names. */
+                if (!mc->local_80 || !uses_port(s, mc->local_80)) {
+                    if (APR_SUCCESS != (rv = md_covers_server(md, s, p))) {
+                        return rv;
+                    }
                 }
-            }
 
-            now = apr_time_now();
-            if (APLOGdebug(wd->s)) {
-                ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, wd->s, APLOGNO(10107)
-                             "next run in %s", md_print_duration(ptemp, next_run - now));
+                sc->assigned = md;
+                APR_ARRAY_PUSH(servers, server_rec*) = s;
+                
+                ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, base_server, APLOGNO(10043)
+                             "Managed Domain %s applies to vhost %s:%d", md->name,
+                             s->server_hostname, s->port);
+                
+                goto next_server;
             }
-            wd_set_interval(wd->watchdog, next_run - now, wd, run_watchdog);
-            break;
-            
-        case AP_WATCHDOG_STATE_STOPPING:
-            ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, wd->s, APLOGNO(10058)
-                         "md watchdog stopping");
-            break;
+        }
+    next_server:
+        continue;
     }
 
-    if (restart) {
-        const char *action, *names = "";
-        int n;
-        
-        for (i = 0, n = 0; i < wd->jobs->nelts; ++i) {
-            job = APR_ARRAY_IDX(wd->jobs, i, md_job_t *);
-            if (job->need_restart && !job->restart_processed) {
-                names = apr_psprintf(ptemp, "%s%s%s", names, n? " " : "", job->md->name);
-                ++n;
+    if (APR_SUCCESS == rv) {
+        if (apr_is_empty_array(servers)) {
+            if (md->renew_mode != MD_RENEW_ALWAYS) {
+                /* Not an error, but looks suspicious */
+                ap_log_error(APLOG_MARK, APLOG_WARNING, 0, base_server, APLOGNO(10045)
+                             "No VirtualHost matches Managed Domain %s", md->name);
+                APR_ARRAY_PUSH(mc->unused_names, const char*)  = md->name;
             }
         }
-
-        if (n > 0) {
-            int notified = 1;
-
-            /* Run notify command for ready MDs (if configured) and persist that
-             * we have done so. This process might be reaped after n requests or die
-             * of another cause. The one taking over the watchdog need to notify again.
-             */
-            if (wd->mc->notify_cmd) {
-                const char * const *argv;
-                const char *cmdline;
-                int exit_code;
-                
-                cmdline = apr_psprintf(ptemp, "%s %s", wd->mc->notify_cmd, names); 
-                apr_tokenize_to_argv(cmdline, (char***)&argv, ptemp);
-                if (APR_SUCCESS == (rv = md_util_exec(ptemp, argv[0], argv, &exit_code))) {
-                    ap_log_error(APLOG_MARK, APLOG_DEBUG, rv, wd->s, APLOGNO(10108) 
-                                 "notify command '%s' returned %d", 
-                                 wd->mc->notify_cmd, exit_code);
-                }
-                else {
-                    if (APR_EINCOMPLETE == rv && exit_code) {
-                        rv = 0;
+        else {
+            const char *uri;
+            
+            /* Found matching server_rec's. Collect all 'ServerAdmin's into MD's contact list */
+            apr_array_clear(md->contacts);
+            for (i = 0; i < servers->nelts; ++i) {
+                s = APR_ARRAY_IDX(servers, i, server_rec*);
+                if (s->server_admin && strcmp(DEFAULT_ADMIN, s->server_admin)) {
+                    uri = md_util_schemify(p, s->server_admin, "mailto");
+                    if (md_array_str_index(md->contacts, uri, 0, 0) < 0) {
+                        APR_ARRAY_PUSH(md->contacts, const char *) = uri; 
+                        ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, base_server, APLOGNO(10044)
+                                     "%s: added contact %s", md->name, uri);
                     }
-                    ap_log_error(APLOG_MARK, APLOG_ERR, rv, wd->s, APLOGNO(10109) 
-                                 "executing MDNotifyCmd %s returned %d", 
-                                  wd->mc->notify_cmd, exit_code);
-                    notified = 0;
-                } 
+                }
             }
             
-            if (notified) {
-                /* persist the jobs that were notified */
-                for (i = 0, n = 0; i < wd->jobs->nelts; ++i) {
-                    job = APR_ARRAY_IDX(wd->jobs, i, md_job_t *);
-                    if (job->need_restart && !job->restart_processed) {
-                        job->restart_processed = 1;
-                        save_job_props(wd->reg, job, ptemp);
+            if (md->require_https > MD_REQUIRE_OFF) {
+                /* We require https for this MD, but do we have port 443 (or a mapped one)
+                 * available? */
+                if (mc->local_443 <= 0) {
+                    ap_log_error(APLOG_MARK, APLOG_ERR, 0, base_server, APLOGNO(10105)
+                                 "MDPortMap says there is no port for https (443), "
+                                 "but MD %s is configured to require https. This "
+                                 "only works when a 443 port is available.", md->name);
+                    return APR_EINVAL;
+                    
+                }
+                
+                /* Ok, we know which local port represents 443, do we have a server_rec
+                 * for MD that has addresses with port 443? */
+                s_https = NULL;
+                for (i = 0; i < servers->nelts; ++i) {
+                    s = APR_ARRAY_IDX(servers, i, server_rec*);
+                    if (matches_port_somewhere(s, mc->local_443)) {
+                        s_https = s;
+                        break;
                     }
                 }
+                
+                if (!s_https) {
+                    /* Did not find any server_rec that matches this MD *and* has an
+                     * s->addrs match for the https port. Suspicious. */
+                    ap_log_error(APLOG_MARK, APLOG_WARNING, 0, base_server, APLOGNO(10106)
+                                 "MD %s is configured to require https, but there seems to be "
+                                 "no VirtualHost for it that has port %d in its address list. "
+                                 "This looks as if it will not work.", 
+                                 md->name, mc->local_443);
+                }
             }
             
-            /* FIXME: the server needs to start gracefully to take the new certificate in.
-             * This poses a variety of problems to solve satisfactory for everyone:
-             * - I myself, have no implementation for Windows 
-             * - on *NIX, child processes run with less privileges, preventing
-             *   the signal based restart trigger to work
-             * - admins want better control of timing windows for restarts, e.g.
-             *   during less busy hours/days.
-             */
-            rv = md_server_graceful(ptemp, wd->s);
-            if (APR_ENOTIMPL == rv) {
-                /* self-graceful restart not supported in this setup */
-                action = " and changes will be activated on next (graceful) server restart.";
-            }
-            else {
-                action = " and server has been asked to restart now.";
-            }
-            ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, wd->s, APLOGNO(10059) 
-                         "The Managed Domain%s %s %s been setup%s",
-                         (n > 1)? "s" : "", names, (n > 1)? "have" : "has", action);
         }
+        
     }
-    
-    return APR_SUCCESS;
+    return rv;
 }
 
-static apr_status_t start_watchdog(apr_array_header_t *names, apr_pool_t *p, 
-                                   md_reg_t *reg, server_rec *s, md_mod_conf_t *mc)
+static apr_status_t link_mds_to_servers(md_mod_conf_t *mc, server_rec *s, 
+                                            apr_pool_t *p, apr_pool_t *ptemp)
 {
-    apr_allocator_t *allocator;
-    md_watchdog *wd;
-    apr_pool_t *wdp;
-    apr_status_t rv;
-    const char *name;
+    int i;
     md_t *md;
-    md_job_t *job;
-    int i, errored, renew;
+    apr_status_t rv = APR_SUCCESS;
     
-    wd_get_instance = APR_RETRIEVE_OPTIONAL_FN(ap_watchdog_get_instance);
-    wd_register_callback = APR_RETRIEVE_OPTIONAL_FN(ap_watchdog_register_callback);
-    wd_set_interval = APR_RETRIEVE_OPTIONAL_FN(ap_watchdog_set_callback_interval);
-    
-    if (!wd_get_instance || !wd_register_callback || !wd_set_interval) {
-        ap_log_error(APLOG_MARK, APLOG_CRIT, 0, s, APLOGNO(10061) "mod_watchdog is required");
-        return !OK;
-    }
-    
-    /* We want our own pool with own allocator to keep data across watchdog invocations */
-    apr_allocator_create(&allocator);
-    apr_allocator_max_free_set(allocator, ap_max_mem_free);
-    rv = apr_pool_create_ex(&wdp, p, NULL, allocator);
-    if (rv != APR_SUCCESS) {
-        ap_log_error(APLOG_MARK, APLOG_ERR, rv, s, APLOGNO(10062) "md_watchdog: create pool");
-        return rv;
+    apr_array_clear(mc->unused_names);
+    for (i = 0; i < mc->mds->nelts; ++i) {
+        md = APR_ARRAY_IDX(mc->mds, i, md_t*);
+        if (APR_SUCCESS != (rv = link_md_to_servers(mc, md, s, p, ptemp))) {
+            goto leave;
+        }
     }
-    apr_allocator_owner_set(allocator, wdp);
-    apr_pool_tag(wdp, "md_watchdog");
+leave:
+    return rv;
+}
 
-    wd = apr_pcalloc(wdp, sizeof(*wd));
-    wd->p = wdp;
-    wd->reg = reg;
-    wd->s = s;
-    wd->mc = mc;
-    
-    wd->jobs = apr_array_make(wd->p, 10, sizeof(md_job_t *));
-    for (i = 0; i < names->nelts; ++i) {
-        name = APR_ARRAY_IDX(names, i, const char *);
-        md = md_reg_get(wd->reg, name, wd->p);
-        if (md) {
-            md_reg_assess(wd->reg, md, &errored, &renew, wd->p);
-            if (errored) {
-                ap_log_error( APLOG_MARK, APLOG_WARNING, 0, wd->s, APLOGNO(10063) 
-                             "md(%s): seems errored. Will not process this any further.", name);
-            }
-            else {
-                job = apr_pcalloc(wd->p, sizeof(*job));
-                
-                job->md = md;
-                APR_ARRAY_PUSH(wd->jobs, md_job_t*) = job;
+static apr_status_t merge_mds_with_conf(md_mod_conf_t *mc, apr_pool_t *p, 
+                                        server_rec *base_server, int log_level)
+{
+    md_srv_conf_t *base_conf;
+    md_t *md, *omd;
+    const char *domain;
+    const md_timeslice_t *ts;
+    apr_status_t rv = APR_SUCCESS;
+    int i, j;
 
-                ap_log_error( APLOG_MARK, APLOG_DEBUG, 0, wd->s, APLOGNO(10064) 
-                             "md(%s): state=%d, driving", name, md->state);
-                
-                load_job_props(reg, job, wd->p);
-                if (job->error_runs) {
-                    /* We are just restarting. If we encounter jobs that had errors
-                     * running the protocol on previous staging runs, we reset
-                     * the staging area for it, in case we persisted something that
-                     * causes a loop. */
-                    md_store_t *store = md_reg_store_get(wd->reg);
-                    
-                    md_store_purge(store, p, MD_SG_STAGING, job->md->name);
-                    md_store_purge(store, p, MD_SG_CHALLENGES, job->md->name);
-                }
+    /* The global module configuration 'mc' keeps a list of all configured MDomains
+     * in the server. This list is collected during configuration processing and,
+     * in the post config phase, get updated from all merged server configurations
+     * before the server starts processing.
+     */ 
+    base_conf = md_config_get(base_server);
+    md_config_get_timespan(&ts, base_conf, MD_CONFIG_RENEW_WINDOW);
+    if (ts) md_reg_set_renew_window_default(mc->reg, ts);
+    md_config_get_timespan(&ts, base_conf, MD_CONFIG_WARN_WINDOW);
+    if (ts) md_reg_set_warn_window_default(mc->reg, ts);
+ 
+    /* Complete the properties of the MDs, now that we have the complete, merged
+     * server configurations.
+     */
+    for (i = 0; i < mc->mds->nelts; ++i) {
+        md = APR_ARRAY_IDX(mc->mds, i, md_t*);
+        merge_srv_config(md, base_conf, p);
+
+        /* Check that we have no overlap with the MDs already completed */
+        for (j = 0; j < i; ++j) {
+            omd = APR_ARRAY_IDX(mc->mds, j, md_t*);
+            if ((domain = md_common_name(md, omd)) != NULL) {
+                ap_log_error(APLOG_MARK, APLOG_ERR, 0, base_server, APLOGNO(10038)
+                             "two Managed Domains have an overlap in domain '%s'"
+                             ", first definition in %s(line %d), second in %s(line %d)",
+                             domain, md->defn_name, md->defn_line_number,
+                             omd->defn_name, omd->defn_line_number);
+                return APR_EINVAL;
             }
         }
+        
+        if (md->cert_file && !md->pkey_file) {
+            ap_log_error(APLOG_MARK, APLOG_ERR, 0, base_server, APLOGNO()
+                         "The Managed Domain '%s', defined in %s(line %d), "
+                         "has a MDCertificateFile but no MDCertificateKeyFile.",
+                         md->name, md->defn_name, md->defn_line_number);
+            return APR_EINVAL;
+        }
+        if (!md->cert_file && md->pkey_file) {
+            ap_log_error(APLOG_MARK, APLOG_ERR, 0, base_server, APLOGNO()
+                         "The Managed Domain '%s', defined in %s(line %d), "
+                         "has a MDCertificateKeyFile but no MDCertificateFile.",
+                         md->name, md->defn_name, md->defn_line_number);
+            return APR_EINVAL;
+        }
+
+        init_acme_tls_1_domains(md, base_server);
+
+        if (APLOG_IS_LEVEL(base_server, log_level)) {
+            ap_log_error(APLOG_MARK, log_level, 0, base_server, APLOGNO(10039)
+                         "Completed MD[%s, CA=%s, Proto=%s, Agreement=%s, renew-mode=%d "
+                         "renew_window=%s, warn_window=%s",
+                         md->name, md->ca_url, md->ca_proto, md->ca_agreement, md->renew_mode,
+                         md->renew_window? md_timeslice_format(md->renew_window, p) : "unset",
+                         md->warn_window? md_timeslice_format(md->warn_window, p) : "unset");
+        }
     }
-
-    if (!wd->jobs->nelts) {
-        ap_log_error( APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(10065)
-                     "no managed domain in state to drive, no watchdog needed, "
-                     "will check again on next server (graceful) restart");
-        apr_pool_destroy(wd->p);
-        return APR_SUCCESS;
-    }
-    
-    if (APR_SUCCESS != (rv = wd_get_instance(&wd->watchdog, MD_WATCHDOG_NAME, 0, 1, wd->p))) {
-        ap_log_error(APLOG_MARK, APLOG_CRIT, rv, s, APLOGNO(10066) 
-                     "create md watchdog(%s)", MD_WATCHDOG_NAME);
-        return rv;
-    }
-    rv = wd_register_callback(wd->watchdog, 0, wd, run_watchdog);
-    ap_log_error(APLOG_MARK, rv? APLOG_CRIT : APLOG_DEBUG, rv, s, APLOGNO(10067) 
-                 "register md watchdog(%s)", MD_WATCHDOG_NAME);
     return rv;
 }
- 
-static void load_stage_sets(apr_array_header_t *names, apr_pool_t *p, 
-                            md_reg_t *reg, server_rec *s)
+
+static void load_staged_data(md_mod_conf_t *mc, server_rec *s, apr_pool_t *p)
 {
-    const char *name; 
     apr_status_t rv;
+    md_t *md;
+    md_result_t *result;
     int i;
     
-    for (i = 0; i < names->nelts; ++i) {
-        name = APR_ARRAY_IDX(names, i, const char*);
-        if (APR_SUCCESS == (rv = md_reg_load(reg, name, p))) {
+    for (i = 0; i < mc->mds->nelts; ++i) {
+        md = APR_ARRAY_IDX(mc->mds, i, md_t *);
+        result = md_result_md_make(p, md);
+        if (APR_SUCCESS == (rv = md_reg_load_staging(mc->reg, md, mc->env, result, p))) {
             ap_log_error( APLOG_MARK, APLOG_INFO, rv, s, APLOGNO(10068) 
-                         "%s: staged set activated", name);
+                         "%s: staged set activated", md->name);
         }
         else if (!APR_STATUS_IS_ENOENT(rv)) {
             ap_log_error( APLOG_MARK, APLOG_ERR, rv, s, APLOGNO(10069)
-                         "%s: error loading staged set", name);
+                         "%s: error loading staged set", md->name);
+        }
+    }
+}
+
+static apr_status_t reinit_mds(md_mod_conf_t *mc, server_rec *s, apr_pool_t *p)
+{
+    md_t *md; 
+    apr_status_t rv = APR_SUCCESS;
+    int i;
+    
+    for (i = 0; i < mc->mds->nelts; ++i) {
+        md = APR_ARRAY_IDX(mc->mds, i, md_t *);
+        if (APR_SUCCESS != (rv = md_reg_reinit_state(mc->reg, (md_t*)md, p))) {
+            ap_log_error( APLOG_MARK, APLOG_ERR, rv, s, APLOGNO()
+                         "%s: error reinitiazing from store", md->name);
+            break;
         }
     }
-    return;
+    return rv;
 }
 
+static void init_watched_names(md_mod_conf_t *mc, apr_pool_t *p, apr_pool_t *ptemp, server_rec *s)
+{
+    const md_t *md;
+    md_result_t *result;
+    int i;
+    
+    /* Calculate the list of MD names which we need to watch:
+     * - all MDs that are used somewhere
+     * - all MDs in drive mode 'AUTO' that are not in 'unused_names'
+     */
+    result = md_result_make(ptemp, APR_SUCCESS);
+    apr_array_clear(mc->watched_names);
+    for (i = 0; i < mc->mds->nelts; ++i) {
+        md = APR_ARRAY_IDX(mc->mds, i, const md_t *);
+        md_result_set(result, APR_SUCCESS, NULL);
+
+        if (md->state == MD_S_ERROR) {
+            md_result_set(result, APR_EGENERAL, 
+                          "in error state, unable to drive forward. This "
+                          "indicates an incomplete or inconsistent configuration. "
+                          "Please check the log for warnings in this regard.");
+            continue;
+        }
+
+        if (md->renew_mode == MD_RENEW_AUTO
+            && md_array_str_index(mc->unused_names, md->name, 0, 0) >= 0) {
+            /* This MD is not used in any virtualhost, do not watch */
+            continue;
+        }
+        
+        if (md_will_renew_cert(md)) {
+            /* make a test init to detect early errors. */
+            md_reg_test_init(mc->reg, md, mc->env, result, p);
+            if (APR_SUCCESS != result->status && result->detail) {
+                apr_hash_set(mc->init_errors, md->name, APR_HASH_KEY_STRING, apr_pstrdup(p, result->detail));
+                ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, APLOGNO() 
+                             "md[%s]: %s", md->name, result->detail);
+            }
+        }
+        
+        APR_ARRAY_PUSH(mc->watched_names, const char *) = md->name; 
+    }
+}   
+
 static apr_status_t md_post_config(apr_pool_t *p, apr_pool_t *plog,
                                    apr_pool_t *ptemp, server_rec *s)
 {
@@ -988,11 +718,9 @@ static apr_status_t md_post_config(apr_p
     const char *mod_md_init_key = "mod_md_init_counter";
     md_srv_conf_t *sc;
     md_mod_conf_t *mc;
-    md_reg_t *reg;
-    const md_t *md;
-    apr_array_header_t *drive_names;
     apr_status_t rv = APR_SUCCESS;
-    int i, dry_run = 0;
+    int dry_run = 0, log_level = APLOG_DEBUG;
+    md_store_t *store;
 
     apr_pool_userdata_get(&data, mod_md_init_key, s->process->pool);
     if (data == NULL) {
@@ -1000,20 +728,16 @@ static apr_status_t md_post_config(apr_p
          * runs all config hooks to check if it can. If so, it does
          * this all again and starts serving requests.
          * 
-         * This is known.
-         *
          * On a dry run, we therefore do all the cheap config things we
-         * need to do. Because otherwise mod_ssl fails because it calls
-         * us unprepared.
-         * But synching our configuration with the md store
-         * and determining which domains to drive and start a watchdog
-         * and all that, we do not.
+         * need to do to find out if the settings are ok. More expensive
+         * things we delay to the real run.
          */
-        ap_log_error( APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(10070)
+        dry_run = 1;
+        log_level = APLOG_TRACE1;
+        ap_log_error( APLOG_MARK, log_level, 0, s, APLOGNO(10070)
                      "initializing post config dry run");
         apr_pool_userdata_set((const void *)1, mod_md_init_key,
                               apr_pool_cleanup_null, s->process->pool);
-        dry_run = 1;
     }
     else {
         ap_log_error( APLOG_MARK, APLOG_INFO, 0, s, APLOGNO(10071)
@@ -1024,81 +748,137 @@ static apr_status_t md_post_config(apr_p
     init_setups(p, s);
     md_log_set(log_is_level, log_print, NULL);
 
-    /* Check uniqueness of MDs, calculate global, configured MD list.
-     * If successful, we have a list of MD definitions that do not overlap. */
-    /* We also need to find out if we can be reached on 80/443 from the outside (e.g. the CA) */
-    if (APR_SUCCESS != (rv =  md_calc_md_list(p, plog, ptemp, s))) {
-        return rv;
-    }
-
     md_config_post_config(s, p);
     sc = md_config_get(s);
     mc = sc->mc;
+    mc->dry_run = dry_run;
 
-    /* Synchronize the definitions we now have with the store via a registry (reg). */
-    if (APR_SUCCESS != (rv = setup_reg(&reg, p, s, mc->can_http, mc->can_https))) {
+    if (APR_SUCCESS != (rv = setup_store(&store, mc, p, s))
+        || APR_SUCCESS != (rv = md_reg_create(&mc->reg, p, store, mc->proxy_url))) {
         ap_log_error(APLOG_MARK, APLOG_ERR, rv, s, APLOGNO(10072)
                      "setup md registry");
-        goto out;
+        goto leave;
     }
     
-    if (APR_SUCCESS != (rv = md_reg_sync(reg, p, ptemp, mc->mds))) {
+    init_ssl();
+
+    /* How to bootstrap this module:
+     * 1. find out if we know where http: and https: requests will arrive
+     * 2. apply the now complete configuration setttings to the MDs
+     * 3. Link MDs to the server_recs they are used in. Detect unused MDs.
+     * 4. Update the store with the MDs. Change domain names, create new MDs, etc.
+     *    Basically all MD properties that are configured directly.
+     *    WARNING: this may change the name of an MD. If an MD loses the first
+     *    of its domain names, it first gets the new first one as name. The 
+     *    store will find the old settings and "recover" the previous name.
+     * 5. Load any staged data from previous driving.
+     * 6. on a dry run, this is all we do
+     * 7. Read back the MD properties that reflect the existance and aspect of
+     *    credentials that are in the store (or missing there). 
+     *    Expiry times, MD state, etc.
+     * 8. Determine the list of MDs that need driving/supervision.
+     * 9. Cleanup any left-overs in registry/store that are no longer needed for
+     *    the list of MDs as we know it now.
+     * 10. If this list is non-empty, setup a watchdog to run. 
+     */
+    /*1*/
+    if (APR_SUCCESS != (rv = detect_supported_ports(mc, s, p, log_level))) goto leave;
+    /*2*/
+    if (APR_SUCCESS != (rv = merge_mds_with_conf(mc, p, s, log_level))) goto leave;
+    /*3*/
+    if (APR_SUCCESS != (rv = link_mds_to_servers(mc, s, p, ptemp))) goto leave;
+    /*4*/
+    if (APR_SUCCESS != (rv = md_reg_sync(mc->reg, p, ptemp, mc->mds))) {
         ap_log_error(APLOG_MARK, APLOG_ERR, rv, s, APLOGNO(10073)
                      "synching %d mds to registry", mc->mds->nelts);
+        goto leave;
     }
+    /*5*/
+    load_staged_data(mc, s, p);
+    /*6*/
+    if (dry_run) goto leave;
+    /*7*/
+    if (APR_SUCCESS != (rv = reinit_mds(mc, s, p))) goto leave;
+    /*8*/
+    init_watched_names(mc, p, ptemp, s);
+    /*9*/
+    md_reg_cleanup_challenges(mc->reg, p, ptemp, mc->mds);
+    
+    /* From here on, the domains in the registry are readonly 
+     * and only staging/challenges may be manipulated */
+    md_reg_freeze_domains(mc->reg, mc->mds);
     
-    /* Determine the managed domains that are in auto drive_mode. For those,
-     * determine in which state they are:
-     *  - UNKNOWN:            should not happen, report, don't drive
-     *  - ERROR:              something we do not know how to fix, report, don't drive
-     *  - INCOMPLETE/EXPIRED: need to drive them right away
-     *  - COMPLETE:           determine when cert expires, drive when the time comes
-     *
-     * Start the watchdog if we have anything, now or in the future.
-     */
-    drive_names = apr_array_make(ptemp, mc->mds->nelts+1, sizeof(const char *));
-    for (i = 0; i < mc->mds->nelts; ++i) {
-        md = APR_ARRAY_IDX(mc->mds, i, const md_t *);
-        switch (md->drive_mode) {
-            case MD_DRIVE_AUTO:
-                if (md_array_str_index(mc->unused_names, md->name, 0, 0) >= 0) {
-                    break;
-                }
-                /* fall through */
-            case MD_DRIVE_ALWAYS:
-                APR_ARRAY_PUSH(drive_names, const char *) = md->name; 
-                break;
-            default:
-                /* leave out */
-                break;
-        }
-    }
-    
-    init_ssl();
-    
-    if (dry_run) {
-        goto out;
-    }
-    
-    /* If there are MDs to drive, start a watchdog to check on them regularly */
-    if (drive_names->nelts > 0) {
+    if (mc->watched_names->nelts > 0) {
+        /*10*/
         ap_log_error(APLOG_MARK, APLOG_DEBUG, rv, s, APLOGNO(10074)
-                     "%d out of %d mds are configured for auto-drive", 
-                     drive_names->nelts, mc->mds->nelts);
+                     "%d out of %d mds need watching", 
+                     mc->watched_names->nelts, mc->mds->nelts);
     
-        load_stage_sets(drive_names, p, reg, s);
         md_http_use_implementation(md_curl_get_impl(p));
-        rv = start_watchdog(drive_names, p, reg, s, mc);
+        rv = md_start_watching(mc, s, p);
     }
     else {
-        ap_log_error( APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(10075)
-                     "no mds to auto drive, no watchdog needed");
+        ap_log_error( APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(10075) "no mds to drive");
     }
-out:
+leave:
     return rv;
 }
 
 /**************************************************************************************************/
+/* connection context */
+
+typedef struct {
+    const char *protocol;
+} md_conn_ctx;
+
+static const char *md_protocol_get(const conn_rec *c)
+{
+    md_conn_ctx *ctx;
+
+    ctx = (md_conn_ctx*)ap_get_module_config(c->conn_config, &md_module);
+    return ctx? ctx->protocol : NULL;
+}
+
+/**************************************************************************************************/
+/* ALPN handling */
+
+static int md_protocol_propose(conn_rec *c, request_rec *r,
+                               server_rec *s,
+                               const apr_array_header_t *offers,
+                               apr_array_header_t *proposals)
+{
+    (void)s;
+    if (!r && offers && opt_ssl_is_https && opt_ssl_is_https(c) 
+        && ap_array_str_contains(offers, PROTO_ACME_TLS_1)) {
+        ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, c,
+                      "proposing protocol '%s'", PROTO_ACME_TLS_1);
+        APR_ARRAY_PUSH(proposals, const char*) = PROTO_ACME_TLS_1;
+        return OK;
+    }
+    return DECLINED;
+}
+
+static int md_protocol_switch(conn_rec *c, request_rec *r, server_rec *s,
+                              const char *protocol)
+{
+    md_conn_ctx *ctx;
+    
+    (void)s;
+    if (!r && opt_ssl_is_https && opt_ssl_is_https(c) && !strcmp(PROTO_ACME_TLS_1, protocol)) {
+        ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, c,
+                      "switching protocol '%s'", PROTO_ACME_TLS_1);
+        ctx = apr_pcalloc(c->pool, sizeof(*ctx));
+        ctx->protocol = PROTO_ACME_TLS_1;
+        ap_set_module_config(c->conn_config, &md_module, ctx);
+
+        c->keepalive = AP_CONN_CLOSE;
+        return OK;
+    }
+    return DECLINED;
+}
+
+ 
+/**************************************************************************************************/
 /* Access API to other httpd components */
 
 static int md_is_managed(server_rec *s)
@@ -1122,29 +902,23 @@ static apr_status_t setup_fallback_cert(
     md_cert_t *cert;
     md_pkey_spec_t spec;
     apr_status_t rv;
-    MD_CHK_VARS;
     
     spec.type = MD_PKEY_TYPE_RSA;
     spec.params.rsa.bits = MD_PKEY_RSA_BITS_DEF;
     
-    if (   !MD_OK(md_pkey_gen(&pkey, p, &spec))
-        || !MD_OK(md_store_save(store, p, MD_SG_DOMAINS, md->name, 
+    if (APR_SUCCESS != (rv = md_pkey_gen(&pkey, p, &spec))
+        || APR_SUCCESS != (rv = md_store_save(store, p, MD_SG_DOMAINS, md->name, 
                                 MD_FN_FALLBACK_PKEY, MD_SV_PKEY, (void*)pkey, 0))
-        || !MD_OK(md_cert_self_sign(&cert, "Apache Managed Domain Fallback", 
+        || APR_SUCCESS != (rv = md_cert_self_sign(&cert, "Apache Managed Domain Fallback", 
                                     md->domains, pkey, apr_time_from_sec(14 * MD_SECS_PER_DAY), p))
-        || !MD_OK(md_store_save(store, p, MD_SG_DOMAINS, md->name, 
+        || APR_SUCCESS != (rv = md_store_save(store, p, MD_SG_DOMAINS, md->name, 
                                 MD_FN_FALLBACK_CERT, MD_SV_CERT, (void*)cert, 0))) {
-        ap_log_error(APLOG_MARK, APLOG_ERR, rv, s,  
-                     "%s: setup fallback certificate, call %s", md->name, MD_LAST_CHK);
+        ap_log_error(APLOG_MARK, APLOG_ERR, rv, s, APLOGNO()
+                     "%s: setup fallback certificate", md->name);
     }
     return rv;
 }
 
-static int fexists(const char *fname, apr_pool_t *p)
-{
-    return (*fname && APR_SUCCESS == md_util_is_file(fname, p));
-}
-
 static apr_status_t md_get_certificate(server_rec *s, apr_pool_t *p,
                                        const char **pkeyfile, const char **pcertfile)
 {
@@ -1153,7 +927,6 @@ static apr_status_t md_get_certificate(s
     md_reg_t *reg;
     md_store_t *store;
     const md_t *md;
-    MD_CHK_VARS;
     
     *pkeyfile = NULL;
     *pcertfile = NULL;
@@ -1188,7 +961,7 @@ static apr_status_t md_get_certificate(s
     reg = sc->mc->reg;
     assert(reg);
     
-    md = md_reg_get(reg, sc->assigned->name, p);
+    md = sc->assigned;
     if (!md) {
         ap_log_error(APLOG_MARK, APLOG_WARNING, 0, s, APLOGNO(10115) 
                      "unable to hand out certificates, as registry can no longer "
@@ -1196,106 +969,100 @@ static apr_status_t md_get_certificate(s
         return APR_ENOENT;
     }
     
-    if (!MD_OK(md_reg_get_cred_files(reg, md, p, pkeyfile, pcertfile))) {
-        ap_log_error(APLOG_MARK, APLOG_ERR, rv, s, APLOGNO(10110) 
-                     "retrieving credentials for MD %s", md->name);
-        return rv;
-    }
-    
-    if (!fexists(*pkeyfile, p) || !fexists(*pcertfile, p)) { 
+    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 (!fexists(*pkeyfile, p) || !fexists(*pcertfile, p)) { 
-            if (!MD_OK(setup_fallback_cert(store, md, s, p))) {
+        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;
     }
-    
-    /* We have key and cert files, but they might no longer be valid or not
-     * match all domain names. Still use these files for now, but indicate that 
-     * resources should no longer be served until we have a new certificate again. */
-    if (md->state != MD_S_COMPLETE) {
-        rv = APR_EAGAIN;
+    else if (APR_SUCCESS != rv) {
+        ap_log_error(APLOG_MARK, APLOG_ERR, rv, s, APLOGNO(10110) 
+                     "retrieving credentials for MD %s", md->name);
+        return rv;
     }
+    
     ap_log_error(APLOG_MARK, APLOG_DEBUG, rv, s, APLOGNO(10077) 
-                 "%s: providing certificate for server %s", md->name, s->server_hostname);
+                 "%s[state=%d]: providing certificate for server %s", 
+                 md->name, md->state, s->server_hostname);
     return rv;
 }
 
-static int compat_warned;
-static apr_status_t md_get_credentials(server_rec *s, apr_pool_t *p,
-                                       const char **pkeyfile, 
-                                       const char **pcertfile, 
-                                       const char **pchainfile)
-{
-    *pchainfile = NULL;
-    if (!compat_warned) {
-        compat_warned = 1;
-        ap_log_error(APLOG_MARK, APLOG_WARNING, 0, s, /* no APLOGNO */
-                     "You are using mod_md with an old patch to mod_ssl. This will "
-                     " work for now, but support will be dropped in a future release.");
-    }
-    return md_get_certificate(s, p, pkeyfile, pcertfile);
-}
-
 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)) {
-        return 0;
-    }
-    
-    sc = md_config_get(c->base_server);
-    if (sc && sc->mc->reg) {
-        md_store_t *store = md_reg_store_get(sc->mc->reg);
-        md_cert_t *mdcert;
-        md_pkey_t *mdpkey;
-        
-        rv = md_store_load(store, MD_SG_CHALLENGES, servername, 
-                           MD_FN_TLSSNI01_CERT, MD_SV_CERT, (void**)&mdcert, c->pool);
-        if (APR_SUCCESS == rv && (*pcert = md_cert_get_X509(mdcert))) {
-            rv = md_store_load(store, MD_SG_CHALLENGES, servername, 
-                               MD_FN_TLSSNI01_PKEY, MD_SV_PKEY, (void**)&mdpkey, c->pool);
-            if (APR_SUCCESS == rv && (*pkey = md_pkey_get_EVP_PKEY(mdpkey))) {
-                ap_log_cerror(APLOG_MARK, APLOG_INFO, 0, c, APLOGNO(10078)
-                              "%s: is a tls-sni-01 challenge host", servername);
-                return 1;
+    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)) {
+        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);
+            md_cert_t *mdcert;
+            md_pkey_t *mdpkey;
+            
+            ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, c, "%s: load certs/keys %s/%s",
+                          servername, cert_name, pkey_name);
+            rv = md_store_load(store, MD_SG_CHALLENGES, servername, cert_name, 
+                               MD_SV_CERT, (void**)&mdcert, c->pool);
+            if (APR_SUCCESS == rv && (*pcert = md_cert_get_X509(mdcert))) {
+                rv = md_store_load(store, MD_SG_CHALLENGES, servername, pkey_name, 
+                                   MD_SV_PKEY, (void**)&mdpkey, c->pool);
+                if (APR_SUCCESS == rv && (*pkey = md_pkey_get_EVP_PKEY(mdpkey))) {
+                    ap_log_cerror(APLOG_MARK, APLOG_INFO, 0, c, APLOGNO(10078)
+                                  "%s: is a %s challenge host", servername, challenge);
+                    return 1;
+                }
+                ap_log_cerror(APLOG_MARK, APLOG_WARNING, rv, c, APLOGNO(10079)
+                              "%s: challenge data not complete, key unavailable", servername);
+            }
+            else {
+                ap_log_cerror(APLOG_MARK, APLOG_INFO, rv, c, APLOGNO(10080)
+                              "%s: unknown %s challenge host", servername, challenge);
             }
-            ap_log_cerror(APLOG_MARK, APLOG_WARNING, rv, c, APLOGNO(10079)
-                          "%s: challenge data not complete, key unavailable", servername);
-        }
-        else {
-            ap_log_cerror(APLOG_MARK, APLOG_INFO, rv, c, APLOGNO(10080)
-                          "%s: unknown TLS SNI challenge host", servername);
         }
     }
+out:
     *pcert = NULL;
     *pkey = NULL;
     return 0;
 }
 
 /**************************************************************************************************/
-/* ACME challenge responses */
+/* ACME 'http-01' challenge responses */
 
 #define WELL_KNOWN_PREFIX           "/.well-known/"
 #define ACME_CHALLENGE_PREFIX       WELL_KNOWN_PREFIX"acme-challenge/"
@@ -1306,7 +1073,7 @@ static int md_http_challenge_pr(request_
     const md_srv_conf_t *sc;
     const char *name, *data;
     md_reg_t *reg;
-    int configured;
+    const md_t *md;
     apr_status_t rv;
     
     if (r->parsed_uri.path 
@@ -1316,7 +1083,7 @@ static int md_http_challenge_pr(request_
             ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r, 
                           "access inside /.well-known/acme-challenge for %s%s", 
                           r->hostname, r->parsed_uri.path);
-            configured = (NULL != md_get_by_domain(sc->mc->mds, r->hostname));
+            md = md_get_by_domain(sc->mc->mds, r->hostname);
             name = r->parsed_uri.path + sizeof(ACME_CHALLENGE_PREFIX)-1;
             reg = sc && sc->mc? sc->mc->reg : NULL;
             
@@ -1345,21 +1112,22 @@ static int md_http_challenge_pr(request_
                     
                     return DONE;
                 }
-                else if (!configured) {
-                    /* The request hostname is not for a configured domain. We are not
+                else if (!md || md->renew_mode == MD_RENEW_MANUAL
+                    || (md->cert_file && md->renew_mode == MD_RENEW_AUTO)) {
+                    /* The request hostname is not for a domain - or at least not for
+                     * a domain that we renew ourselves. We are not
                      * the sole authority here for /.well-known/acme-challenge (see PR62189).
-                     * So, we decline to handle this and let others step in.
+                     * So, we decline to handle this and give others a chance to provide
+                     * the answer.
                      */
                     return DECLINED;
                 }
                 else if (APR_STATUS_IS_ENOENT(rv)) {
                     return HTTP_NOT_FOUND;
                 }
-                else {
-                    ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(10081)
-                                  "loading challenge %s from store", name);
-                    return HTTP_INTERNAL_SERVER_ERROR;
-                }
+                ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(10081)
+                              "loading challenge %s from store", name);
+                return HTTP_INTERNAL_SERVER_ERROR;
             }
         }
     }
@@ -1436,7 +1204,8 @@ static void md_hooks(apr_pool_t *pool)
 {
     static const char *const mod_ssl[] = { "mod_ssl.c", NULL};
 
-    md_acme_init(pool, AP_SERVER_BASEVERSION);
+    /* Leave the ssl initialization to mod_ssl or friends. */
+    md_acme_init(pool, AP_SERVER_BASEVERSION, 0);
         
     ap_log_perror(APLOG_MARK, APLOG_TRACE1, 0, pool, "installing hooks");
     
@@ -1449,12 +1218,20 @@ static void md_hooks(apr_pool_t *pool)
     ap_hook_child_init(md_child_init, NULL, mod_ssl, APR_HOOK_MIDDLE);
 
     /* answer challenges *very* early, before any configured authentication may strike */
-    ap_hook_post_read_request(md_require_https_maybe, NULL, NULL, APR_HOOK_FIRST);
+    ap_hook_post_read_request(md_require_https_maybe, mod_ssl, NULL, APR_HOOK_MIDDLE);
     ap_hook_post_read_request(md_http_challenge_pr, NULL, NULL, APR_HOOK_MIDDLE);
 
+    ap_hook_protocol_propose(md_protocol_propose, NULL, NULL, APR_HOOK_MIDDLE);
+    ap_hook_protocol_switch(md_protocol_switch, NULL, NULL, APR_HOOK_MIDDLE);
+    ap_hook_protocol_get(md_protocol_get, NULL, NULL, APR_HOOK_MIDDLE);
+
+    /* Status request handlers and contributors */
+    ap_hook_post_read_request(md_http_cert_status, NULL, mod_ssl, APR_HOOK_MIDDLE);
+    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);
+
     APR_REGISTER_OPTIONAL_FN(md_is_managed);
     APR_REGISTER_OPTIONAL_FN(md_get_certificate);
     APR_REGISTER_OPTIONAL_FN(md_is_challenge);
-    APR_REGISTER_OPTIONAL_FN(md_get_credentials);
 }
 

Modified: httpd/httpd/trunk/modules/md/mod_md.h
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/md/mod_md.h?rev=1862013&r1=1862012&r2=1862013&view=diff
==============================================================================
--- httpd/httpd/trunk/modules/md/mod_md.h (original)
+++ httpd/httpd/trunk/modules/md/mod_md.h Mon Jun 24 16:04:32 2019
@@ -39,12 +39,4 @@ APR_DECLARE_OPTIONAL_FN(int,
                         md_is_challenge, (struct conn_rec *, const char *,
                                           X509 **pcert, EVP_PKEY **pkey));
 
-/* Backward compatibility to older mod_ssl patches, will generate
- * a WARNING in the logs, use 'md_get_certificate' instead */
-APR_DECLARE_OPTIONAL_FN(apr_status_t, 
-                        md_get_credentials, (struct server_rec *, apr_pool_t *,
-                                             const char **pkeyfile, 
-                                             const char **pcertfile, 
-                                             const char **pchainfile));
-
 #endif /* mod_md_mod_md_h */