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 2017/09/13 14:16:49 UTC

svn commit: r1808241 - in /httpd/httpd/trunk: ./ modules/md/

Author: icing
Date: Wed Sep 13 14:16:49 2017
New Revision: 1808241

URL: http://svn.apache.org/viewvc?rev=1808241&view=rev
Log:
On the trunk:

mod_md: v0.9.5:
     - New directive (srly: what do you expect at this point?) "MDMustStaple on|off" to control if
       new certificates are requested with the OCSP Must Staple extension.
     - Known limitation: when the server is configured to ditch and restart child processes, for example
       after a certain number of connections/requests, the mod_md watchdog instance might migrate 
       to a new child process. Since not all its state is persisted, some messsages might appear a
       second time in the logs.
     - Adding checks when 'MDRequireHttps' is used. It is considered an error when 'MDPortMap 443:-'
       is used - which negates that a https: port exists. Also, a warning is logged if no 
       VirtualHost can be found for a Managed Domain that has port 443 (or the mapped one) in
       its address list.
     - New directive 'MDRequireHttps' for redirecting http: traffic to a Managed Domain, permanently
       or temporarily.
     - Fix for using a fallback certificate on initial signup of a Managed Domain. Requires also
       a changed mod_ssl patch (v5) to take effect.
     - compatibility with libressl


Modified:
    httpd/httpd/trunk/CHANGES
    httpd/httpd/trunk/modules/md/md.h
    httpd/httpd/trunk/modules/md/md_acme_authz.c
    httpd/httpd/trunk/modules/md/md_core.c
    httpd/httpd/trunk/modules/md/md_crypt.c
    httpd/httpd/trunk/modules/md/md_crypt.h
    httpd/httpd/trunk/modules/md/md_reg.c
    httpd/httpd/trunk/modules/md/md_reg.h
    httpd/httpd/trunk/modules/md/md_store_fs.c
    httpd/httpd/trunk/modules/md/md_util.c
    httpd/httpd/trunk/modules/md/md_util.h
    httpd/httpd/trunk/modules/md/md_version.h
    httpd/httpd/trunk/modules/md/mod_md.c
    httpd/httpd/trunk/modules/md/mod_md_config.c
    httpd/httpd/trunk/modules/md/mod_md_config.h

Modified: httpd/httpd/trunk/CHANGES
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/CHANGES?rev=1808241&r1=1808240&r2=1808241&view=diff
==============================================================================
--- httpd/httpd/trunk/CHANGES [utf-8] (original)
+++ httpd/httpd/trunk/CHANGES [utf-8] Wed Sep 13 14:16:49 2017
@@ -1,6 +1,24 @@
                                                          -*- coding: utf-8 -*-
 Changes with Apache 2.5.0
 
+  *) mod_md: v0.9.5:
+     - New directive (srly: what do you expect at this point?) "MDMustStaple on|off" to control if
+       new certificates are requested with the OCSP Must Staple extension.
+     - Known limitation: when the server is configured to ditch and restart child processes, for example
+       after a certain number of connections/requests, the mod_md watchdog instance might migrate 
+       to a new child process. Since not all its state is persisted, some messsages might appear a
+       second time in the logs.
+     - Adding checks when 'MDRequireHttps' is used. It is considered an error when 'MDPortMap 443:-'
+       is used - which negates that a https: port exists. Also, a warning is logged if no 
+       VirtualHost can be found for a Managed Domain that has port 443 (or the mapped one) in
+       its address list.
+     - New directive 'MDRequireHttps' for redirecting http: traffic to a Managed Domain, permanently
+       or temporarily.
+     - Fix for using a fallback certificate on initial signup of a Managed Domain. Requires also
+       a changed mod_ssl patch (v5) to take effect.
+     - compatibility with libressl
+       [Stefan Eissing]
+
   *) htdigest: prevent a buffer overflow when a string exceeds the allowed max
      length in a password file.
      [Luca Toscano, Hanno Böck <hanno hboeck de>]

Modified: httpd/httpd/trunk/modules/md/md.h
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/md/md.h?rev=1808241&r1=1808240&r2=1808241&view=diff
==============================================================================
--- httpd/httpd/trunk/modules/md/md.h (original)
+++ httpd/httpd/trunk/modules/md/md.h Wed Sep 13 14:16:49 2017
@@ -42,6 +42,13 @@ typedef enum {
 } md_state_t;
 
 typedef enum {
+    MD_REQUIRE_UNSET = -1,
+    MD_REQUIRE_OFF,
+    MD_REQUIRE_TEMPORARY,
+    MD_REQUIRE_PERMANENT,
+} md_require_t;
+
+typedef enum {
     MD_SV_TEXT,
     MD_SV_JSON,
     MD_SV_CERT,
@@ -74,6 +81,8 @@ struct md_t {
     struct apr_array_header_t *contacts;   /* list of contact uris, e.g. mailto:xxx */
 
     int transitive;                 /* != 0 iff VirtualHost names/aliases are auto-added */
+    md_require_t require_https;     /* Iff https: is required for this MD */
+    
     int drive_mode;                 /* mode of obtaining credentials */
     struct md_pkey_spec_t *pkey_spec;/* specification for generating new private keys */
     int must_staple;                /* certificates should set the OCSP Must Staple extension */
@@ -119,16 +128,20 @@ struct md_t {
 #define MD_KEY_KEY              "key"
 #define MD_KEY_KEYAUTHZ         "keyAuthorization"
 #define MD_KEY_LOCATION         "location"
+#define MD_KEY_MUST_STAPLE      "must-staple"
 #define MD_KEY_NAME             "name"
+#define MD_KEY_PERMANENT        "permanent"
 #define MD_KEY_PKEY             "privkey"
 #define MD_KEY_PROTO            "proto"
 #define MD_KEY_REGISTRATION     "registration"
 #define MD_KEY_RENEW            "renew"
 #define MD_KEY_RENEW_WINDOW     "renew-window"
+#define MD_KEY_REQUIRE_HTTPS    "require-https"
 #define MD_KEY_RESOURCE         "resource"
 #define MD_KEY_STATE            "state"
 #define MD_KEY_STATUS           "status"
 #define MD_KEY_STORE            "store"
+#define MD_KEY_TEMPORARY        "temporary"
 #define MD_KEY_TOKEN            "token"
 #define MD_KEY_TRANSITIVE       "transitive"
 #define MD_KEY_TYPE             "type"

Modified: httpd/httpd/trunk/modules/md/md_acme_authz.c
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/md/md_acme_authz.c?rev=1808241&r1=1808240&r2=1808241&view=diff
==============================================================================
--- httpd/httpd/trunk/modules/md/md_acme_authz.c (original)
+++ httpd/httpd/trunk/modules/md/md_acme_authz.c Wed Sep 13 14:16:49 2017
@@ -356,6 +356,7 @@ static apr_status_t cha_tls_sni_01_setup
     const char *cha_dns;
     apr_status_t rv;
     int notify_server;
+    apr_array_header_t *domains;
     
     if (   APR_SUCCESS != (rv = setup_key_authz(cha, authz, acme, p, &notify_server))
         || APR_SUCCESS != (rv = setup_cha_dns(&cha_dns, cha, p))) {
@@ -374,7 +375,9 @@ static apr_status_t cha_tls_sni_01_setup
         }
 
         /* setup a certificate containing the challenge dns */
-        rv = md_cert_self_sign(&cha_cert, authz->domain, cha_dns, cha_key, 
+        domains = apr_array_make(p, 5, sizeof(const char*));
+        APR_ARRAY_PUSH(domains, const char*) = cha_dns;
+        rv = md_cert_self_sign(&cha_cert, authz->domain, domains, cha_key, 
                                apr_time_from_sec(7 * MD_SECS_PER_DAY), p);
         
         if (APR_SUCCESS != rv) {

Modified: httpd/httpd/trunk/modules/md/md_core.c
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/md/md_core.c?rev=1808241&r1=1808240&r2=1808241&view=diff
==============================================================================
--- httpd/httpd/trunk/modules/md/md_core.c (original)
+++ httpd/httpd/trunk/modules/md/md_core.c Wed Sep 13 14:16:49 2017
@@ -85,6 +85,8 @@ md_t *md_create_empty(apr_pool_t *p)
         md->domains = apr_array_make(p, 5, sizeof(const char *));
         md->contacts = apr_array_make(p, 5, sizeof(const char *));
         md->drive_mode = MD_DRIVE_DEFAULT;
+        md->require_https = MD_REQUIRE_UNSET;
+        md->must_staple = -1;
         md->transitive = -1;
         md->defn_name = "unknown";
         md->defn_line_number = 0;
@@ -256,6 +258,8 @@ md_t *md_clone(apr_pool_t *p, const md_t
     if (md) {
         md->state = src->state;
         md->name = apr_pstrdup(p, src->name);
+        md->require_https = src->require_https;
+        md->must_staple = src->must_staple;
         md->drive_mode = src->drive_mode;
         md->domains = md_array_str_compact(p, src->domains, 0);
         md->pkey_spec = src->pkey_spec;
@@ -283,6 +287,8 @@ md_t *md_merge(apr_pool_t *p, const md_t
     n->ca_url = add->ca_url? add->ca_url : base->ca_url;
     n->ca_proto = add->ca_proto? add->ca_proto : base->ca_proto;
     n->ca_agreement = add->ca_agreement? add->ca_agreement : base->ca_agreement;
+    n->require_https = (add->require_https != MD_REQUIRE_UNSET)? add->require_https : base->require_https;
+    n->must_staple = (add->must_staple >= 0)? add->must_staple : base->must_staple;
     n->drive_mode = (add->drive_mode != MD_DRIVE_DEFAULT)? add->drive_mode : base->drive_mode;
     n->pkey_spec = add->pkey_spec? add->pkey_spec : base->pkey_spec;
     n->renew_norm = (add->renew_norm > 0)? add->renew_norm : base->renew_norm;
@@ -344,6 +350,17 @@ md_json_t *md_to_json(const md_t *md, ap
             na = md_array_str_compact(p, md->ca_challenges, 0);
             md_json_setsa(na, json, MD_KEY_CA, MD_KEY_CHALLENGES, NULL);
         }
+        switch (md->require_https) {
+            case MD_REQUIRE_TEMPORARY:
+                md_json_sets(MD_KEY_TEMPORARY, json, MD_KEY_REQUIRE_HTTPS, NULL);
+                break;
+            case MD_REQUIRE_PERMANENT:
+                md_json_sets(MD_KEY_PERMANENT, json, MD_KEY_REQUIRE_HTTPS, NULL);
+                break;
+            default:
+                break;
+        }
+        md_json_setb(md->must_staple > 0, json, MD_KEY_MUST_STAPLE, NULL);
         return json;
     }
     return NULL;
@@ -380,7 +397,7 @@ md_t *md_from_json(md_json_t *json, apr_
         md->renew_norm = 0;
         md->renew_window = apr_time_from_sec(md_json_getl(json, MD_KEY_RENEW_WINDOW, NULL));
         if (md->renew_window <= 0) {
-            const char *s = md_json_gets(json, MD_KEY_RENEW_WINDOW, NULL);
+            s = md_json_gets(json, MD_KEY_RENEW_WINDOW, NULL);
             if (s && strchr(s, '%')) {
                 int percent = atoi(s);
                 if (0 < percent && percent < 100) {
@@ -393,6 +410,16 @@ md_t *md_from_json(md_json_t *json, apr_
             md->ca_challenges = apr_array_make(p, 5, sizeof(const char*));
             md_json_dupsa(md->ca_challenges, p, json, MD_KEY_CA, MD_KEY_CHALLENGES, NULL);
         }
+        md->require_https = MD_REQUIRE_OFF;
+        s = md_json_gets(json, MD_KEY_REQUIRE_HTTPS, NULL);
+        if (s && !strcmp(MD_KEY_TEMPORARY, s)) {
+            md->require_https = MD_REQUIRE_TEMPORARY;
+        }
+        else if (s && !strcmp(MD_KEY_PERMANENT, s)) {
+            md->require_https = MD_REQUIRE_PERMANENT;
+        }
+        md->must_staple = (int)md_json_getb(json, MD_KEY_MUST_STAPLE, NULL);
+        
         return md;
     }
     return NULL;

Modified: httpd/httpd/trunk/modules/md/md_crypt.c
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/md/md_crypt.c?rev=1808241&r1=1808240&r2=1808241&view=diff
==============================================================================
--- httpd/httpd/trunk/modules/md/md_crypt.c (original)
+++ httpd/httpd/trunk/modules/md/md_crypt.c Wed Sep 13 14:16:49 2017
@@ -179,6 +179,66 @@ static int pem_passwd(char *buf, int siz
 }
 
 /**************************************************************************************************/
+/* date time things */
+
+/* Get the apr time (micro seconds, since 1970) from an ASN1 time, as stored in X509
+ * certificates. OpenSSL now has a utility function, but other *SSL derivatives have
+ * not caughts up yet or chose to ignore. An alternative is implemented, we prefer 
+ * however the *SSL to maintain such things.
+ */
+static apr_time_t md_asn1_time_get(const ASN1_TIME* time)
+{
+#ifdef LIBRESSL_VERSION_NUMBER
+    /* courtesy: https://stackoverflow.com/questions/10975542/asn1-time-to-time-t-conversion#11263731
+     * all bugs are mine */
+    apr_time_exp_t t;
+    apr_time_t ts;
+    const char* str = (const char*) time->data;
+    apr_size_t i = 0;
+
+    memset(&t, 0, sizeof(t));
+
+    if (time->type == V_ASN1_UTCTIME) {/* two digit year */
+        t.tm_year = (str[i++] - '0') * 10;
+        t.tm_year += (str[i++] - '0');
+        if (t.tm_year < 70)
+            t.tm_year += 100;
+    } 
+    else if (time->type == V_ASN1_GENERALIZEDTIME) {/* four digit year */
+        t.tm_year = (str[i++] - '0') * 1000;
+        t.tm_year+= (str[i++] - '0') * 100;
+        t.tm_year+= (str[i++] - '0') * 10;
+        t.tm_year+= (str[i++] - '0');
+        t.tm_year -= 1900;
+    }
+    t.tm_mon  = (str[i++] - '0') * 10;
+    t.tm_mon += (str[i++] - '0') - 1; /* -1 since January is 0 not 1. */
+    t.tm_mday = (str[i++] - '0') * 10;
+    t.tm_mday+= (str[i++] - '0');
+    t.tm_hour = (str[i++] - '0') * 10;
+    t.tm_hour+= (str[i++] - '0');
+    t.tm_min  = (str[i++] - '0') * 10;
+    t.tm_min += (str[i++] - '0');
+    t.tm_sec  = (str[i++] - '0') * 10;
+    t.tm_sec += (str[i++] - '0');
+    
+    if (APR_SUCCESS == apr_time_exp_gmt_get(&ts, &t)) {
+        return ts;
+    }
+    return 0;
+#else 
+    int secs, days;
+    apr_time_t ts = apr_time_now();
+    
+    if (ASN1_TIME_diff(&days, &secs, NULL, time)) {
+        ts += apr_time_from_sec((days * MD_SECS_PER_DAY) + secs); 
+    }
+    return ts;
+#endif
+}
+
+
+/**************************************************************************************************/
 /* private keys */
 
 md_json_t *md_pkey_spec_to_json(const md_pkey_spec_t *spec, apr_pool_t *p)
@@ -409,7 +469,7 @@ apr_status_t md_pkey_gen(md_pkey_t **ppk
     }
 }
 
-#if OPENSSL_VERSION_NUMBER < 0x10100000L
+#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER)
 
 #ifndef NID_tlsfeature
 #define NID_tlsfeature          1020
@@ -658,26 +718,12 @@ int md_cert_has_expired(const md_cert_t
 
 apr_time_t md_cert_get_not_after(md_cert_t *cert)
 {
-    int secs, days;
-    apr_time_t time = apr_time_now();
-    ASN1_TIME *not_after = X509_get_notAfter(cert->x509);
-    
-    if (ASN1_TIME_diff(&days, &secs, NULL, not_after)) {
-        time += apr_time_from_sec((days * MD_SECS_PER_DAY) + secs); 
-    }
-    return time;
+    return md_asn1_time_get(X509_get_notAfter(cert->x509));
 }
 
 apr_time_t md_cert_get_not_before(md_cert_t *cert)
 {
-    int secs, days;
-    apr_time_t time = apr_time_now();
-    ASN1_TIME *not_after = X509_get_notBefore(cert->x509);
-    
-    if (ASN1_TIME_diff(&days, &secs, NULL, not_after)) {
-        time += apr_time_from_sec((days * MD_SECS_PER_DAY) + secs); 
-    }
-    return time;
+    return md_asn1_time_get(X509_get_notBefore(cert->x509));
 }
 
 int md_cert_covers_domain(md_cert_t *cert, const char *domain_name)
@@ -991,11 +1037,6 @@ apr_status_t md_chain_fsave(apr_array_he
 /**************************************************************************************************/
 /* certificate signing requests */
 
-static const char *alt_name(const char *domain, apr_pool_t *p)
-{
-    return apr_psprintf(p, "DNS:%s", domain);
-}
-
 static const char *alt_names(apr_array_header_t *domains, apr_pool_t *p)
 {
     const char *alts = "", *sep = "", *domain;
@@ -1051,9 +1092,19 @@ static apr_status_t add_must_staple(STAC
 {
     
     if (md->must_staple) {
-        X509_EXTENSION *x = X509V3_EXT_conf_nid(NULL, NULL, 
-                                                NID_tlsfeature, (char*)"DER:30:03:02:01:05");
+        X509_EXTENSION *x;
+        int nid;
+        
+        nid = OBJ_create("1.3.6.1.5.5.7.1.24", "OCSPReq", "OCSP Request");
+        if (NID_undef == nid) {
+            md_log_perror(MD_LOG_MARK, MD_LOG_ERR, 0, p, 
+                          "%s: unable to get NID for must-staple", md->name);
+            return APR_EGENERAL;
+        }
+        x = X509V3_EXT_conf_nid(NULL, NULL, nid, (char*)"DER:30:03:02:01:05");
         if (NULL == x) {
+            md_log_perror(MD_LOG_MARK, MD_LOG_ERR, 0, p, 
+                          "%s: unable to get x509 extension for must-staple", md->name);
             return APR_EGENERAL;
         }
         sk_X509_EXTENSION_push(exts, x);
@@ -1140,7 +1191,7 @@ out:
 }
 
 apr_status_t md_cert_self_sign(md_cert_t **pcert, const char *cn, 
-                               const char *domain, md_pkey_t *pkey,
+                               apr_array_header_t *domains, md_pkey_t *pkey,
                                apr_interval_time_t valid_for, apr_pool_t *p)
 {
     X509 *x;
@@ -1152,45 +1203,45 @@ apr_status_t md_cert_self_sign(md_cert_t
     ASN1_INTEGER *asn1_rnd = NULL;
     unsigned char rnd[20];
     
-    assert(domain);
+    assert(domains);
     
     if (NULL == (x = X509_new()) 
         || NULL == (n = X509_NAME_new())) {
         rv = APR_ENOMEM;
-        md_log_perror(MD_LOG_MARK, MD_LOG_ERR, 0, p, "%s: openssl alloc X509 things", domain);
+        md_log_perror(MD_LOG_MARK, MD_LOG_ERR, 0, p, "%s: openssl alloc X509 things", cn);
         goto out; 
     }
     
     if (APR_SUCCESS != (rv = md_rand_bytes(rnd, sizeof(rnd), p))
         || !(big_rnd = BN_bin2bn(rnd, sizeof(rnd), NULL))
         || !(asn1_rnd = BN_to_ASN1_INTEGER(big_rnd, NULL))) {
-        md_log_perror(MD_LOG_MARK, MD_LOG_ERR, 0, p, "%s: setup random serial", domain);
+        md_log_perror(MD_LOG_MARK, MD_LOG_ERR, 0, p, "%s: setup random serial", cn);
         rv = APR_EGENERAL; goto out;
     } 
     if (!X509_set_serialNumber(x, asn1_rnd)) {
-        md_log_perror(MD_LOG_MARK, MD_LOG_ERR, 0, p, "%s: set serial number", domain);
+        md_log_perror(MD_LOG_MARK, MD_LOG_ERR, 0, p, "%s: set serial number", cn);
         rv = APR_EGENERAL; goto out;
     }
     /* set common name and issue */
     if (!X509_NAME_add_entry_by_txt(n, "CN", MBSTRING_ASC, (const unsigned char*)cn, -1, -1, 0)
         || !X509_set_subject_name(x, n)
         || !X509_set_issuer_name(x, n)) {
-        md_log_perror(MD_LOG_MARK, MD_LOG_ERR, 0, p, "%s: name add entry", domain);
+        md_log_perror(MD_LOG_MARK, MD_LOG_ERR, 0, p, "%s: name add entry", cn);
         rv = APR_EGENERAL; goto out;
     }
     /* cert are uncontrained (but not very trustworthy) */
     if (APR_SUCCESS != (rv = add_ext(x, NID_basic_constraints, "CA:TRUE, pathlen:0", p))) {
-        md_log_perror(MD_LOG_MARK, MD_LOG_ERR, rv, p, "%s: set basic constraints ext", domain);
+        md_log_perror(MD_LOG_MARK, MD_LOG_ERR, rv, p, "%s: set basic constraints ext", cn);
         goto out;
     }
     /* add the domain as alt name */
-    if (APR_SUCCESS != (rv = add_ext(x, NID_subject_alt_name, alt_name(domain, p), p))) {
-        md_log_perror(MD_LOG_MARK, MD_LOG_ERR, rv, p, "%s: set alt_name ext", domain);
+    if (APR_SUCCESS != (rv = add_ext(x, NID_subject_alt_name, alt_names(domains, p), p))) {
+        md_log_perror(MD_LOG_MARK, MD_LOG_ERR, rv, p, "%s: set alt_name ext", cn);
         goto out;
     }
     /* add our key */
     if (!X509_set_pubkey(x, pkey->pkey)) {
-        md_log_perror(MD_LOG_MARK, MD_LOG_ERR, rv, p, "%s: set pkey in x509", domain);
+        md_log_perror(MD_LOG_MARK, MD_LOG_ERR, rv, p, "%s: set pkey in x509", cn);
         rv = APR_EGENERAL; goto out;
     }
     
@@ -1204,7 +1255,7 @@ apr_status_t md_cert_self_sign(md_cert_t
 
     /* sign with same key */
     if (!X509_sign(x, pkey->pkey, EVP_sha256())) {
-        md_log_perror(MD_LOG_MARK, MD_LOG_ERR, rv, p, "%s: sign x509", domain);
+        md_log_perror(MD_LOG_MARK, MD_LOG_ERR, rv, p, "%s: sign x509", cn);
         rv = APR_EGENERAL; goto out;
     }
 

Modified: httpd/httpd/trunk/modules/md/md_crypt.h
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/md/md_crypt.h?rev=1808241&r1=1808240&r2=1808241&view=diff
==============================================================================
--- httpd/httpd/trunk/modules/md/md_crypt.h (original)
+++ httpd/httpd/trunk/modules/md/md_crypt.h Wed Sep 13 14:16:49 2017
@@ -127,7 +127,7 @@ apr_status_t md_cert_req_create(const ch
                                 md_pkey_t *pkey, apr_pool_t *p);
 
 apr_status_t md_cert_self_sign(md_cert_t **pcert, const char *cn, 
-                               const char *domain, md_pkey_t *pkey,
+                               struct apr_array_header_t *domains, md_pkey_t *pkey,
                                apr_interval_time_t valid_for, apr_pool_t *p);
 
 #endif /* md_crypt_h */

Modified: httpd/httpd/trunk/modules/md/md_reg.c
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/md/md_reg.c?rev=1808241&r1=1808240&r2=1808241&view=diff
==============================================================================
--- httpd/httpd/trunk/modules/md/md_reg.c (original)
+++ httpd/httpd/trunk/modules/md/md_reg.c Wed Sep 13 14:16:49 2017
@@ -506,6 +506,18 @@ static apr_status_t p_md_update(void *ba
             nmd->pkey_spec = apr_pmemdup(p, updates->pkey_spec, sizeof(md_pkey_spec_t));
         }
     }
+    if (MD_UPD_REQUIRE_HTTPS & fields) {
+        md_log_perror(MD_LOG_MARK, MD_LOG_TRACE1, 0, ptemp, "update require-https: %s", name);
+        nmd->require_https = updates->require_https;
+    }
+    if (MD_UPD_TRANSITIVE & fields) {
+        md_log_perror(MD_LOG_MARK, MD_LOG_TRACE1, 0, ptemp, "update transitive: %s", name);
+        nmd->transitive = updates->transitive;
+    }
+    if (MD_UPD_MUST_STAPLE & fields) {
+        md_log_perror(MD_LOG_MARK, MD_LOG_TRACE1, 0, ptemp, "update must-staple: %s", name);
+        nmd->must_staple = updates->must_staple;
+    }
     
     if (fields && APR_SUCCESS == (rv = md_save(reg->store, p, MD_SG_DOMAINS, nmd, 0))) {
         rv = state_init(reg, ptemp, nmd, 0);
@@ -743,6 +755,10 @@ apr_status_t md_reg_sync(md_reg_t *reg,
                     smd->ca_agreement = md->ca_agreement;
                     fields |= MD_UPD_AGREEMENT;
                 }
+                if (MD_VAL_UPDATE(md, smd, transitive)) {
+                    smd->transitive = md->transitive;
+                    fields |= MD_UPD_TRANSITIVE;
+                }
                 if (MD_VAL_UPDATE(md, smd, drive_mode)) {
                     smd->drive_mode = md->drive_mode;
                     fields |= MD_UPD_DRIVE_MODE;
@@ -780,6 +796,14 @@ apr_status_t md_reg_sync(md_reg_t *reg,
                         smd->pkey_spec = apr_pmemdup(p, md->pkey_spec, sizeof(md_pkey_spec_t));
                     }
                 }
+                if (MD_VAL_UPDATE(md, smd, require_https)) {
+                    smd->require_https = md->require_https;
+                    fields |= MD_UPD_REQUIRE_HTTPS;
+                }
+                if (MD_VAL_UPDATE(md, smd, must_staple)) {
+                    smd->must_staple = md->must_staple;
+                    fields |= MD_UPD_MUST_STAPLE;
+                }
                 
                 if (fields) {
                     rv = md_reg_update(reg, ptemp, smd->name, smd, fields);

Modified: httpd/httpd/trunk/modules/md/md_reg.h
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/md/md_reg.h?rev=1808241&r1=1808240&r2=1808241&view=diff
==============================================================================
--- httpd/httpd/trunk/modules/md/md_reg.h (original)
+++ httpd/httpd/trunk/modules/md/md_reg.h Wed Sep 13 14:16:49 2017
@@ -82,18 +82,21 @@ int md_reg_do(md_reg_do_cb *cb, void *ba
 /**
  * Bitmask for fields that are updated.
  */
-#define MD_UPD_DOMAINS      0x0001
-#define MD_UPD_CA_URL       0x0002
-#define MD_UPD_CA_PROTO     0x0004
-#define MD_UPD_CA_ACCOUNT   0x0008
-#define MD_UPD_CONTACTS     0x0010
-#define MD_UPD_AGREEMENT    0x0020
-#define MD_UPD_CERT_URL     0x0040
-#define MD_UPD_DRIVE_MODE   0x0080
-#define MD_UPD_RENEW_WINDOW 0x0100
+#define MD_UPD_DOMAINS       0x0001
+#define MD_UPD_CA_URL        0x0002
+#define MD_UPD_CA_PROTO      0x0004
+#define MD_UPD_CA_ACCOUNT    0x0008
+#define MD_UPD_CONTACTS      0x0010
+#define MD_UPD_AGREEMENT     0x0020
+#define MD_UPD_CERT_URL      0x0040
+#define MD_UPD_DRIVE_MODE    0x0080
+#define MD_UPD_RENEW_WINDOW  0x0100
 #define MD_UPD_CA_CHALLENGES 0x0200
-#define MD_UPD_PKEY_SPEC    0x0400
-#define MD_UPD_ALL          0x7FFFFFFF
+#define MD_UPD_PKEY_SPEC     0x0400
+#define MD_UPD_REQUIRE_HTTPS 0x0800
+#define MD_UPD_TRANSITIVE    0x1000
+#define MD_UPD_MUST_STAPLE   0x2000
+#define MD_UPD_ALL           0x7FFFFFFF
 
 /**
  * Update the given fields for the managed domain. Take the new

Modified: httpd/httpd/trunk/modules/md/md_store_fs.c
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/md/md_store_fs.c?rev=1808241&r1=1808240&r2=1808241&view=diff
==============================================================================
--- httpd/httpd/trunk/modules/md/md_store_fs.c (original)
+++ httpd/httpd/trunk/modules/md/md_store_fs.c Wed Sep 13 14:16:49 2017
@@ -261,57 +261,6 @@ read:
     return rv;
 }
 
-static apr_status_t setup_fallback_cert(void *baton, apr_pool_t *p, apr_pool_t *ptemp, va_list ap)
-{
-    md_store_fs_t *s_fs = baton;
-    md_pkey_t *fallback_key;
-    md_cert_t *fallback_cert;
-    md_pkey_spec_t spec;
-    apr_status_t rv;
-
-    if (APR_SUCCESS == (rv = fs_load(&s_fs->s, MD_SG_NONE, NULL, MD_FN_FALLBACK_PKEY, 
-                                     MD_SV_PKEY, (void**)&fallback_key, ptemp))
-        && APR_SUCCESS == (rv = fs_load(&s_fs->s, MD_SG_NONE, NULL, MD_FN_FALLBACK_CERT, 
-                                        MD_SV_CERT, (void**)&fallback_cert, ptemp))) {
-        apr_time_t not_after = md_cert_get_not_after(fallback_cert);
-        if (not_after > apr_time_now() + apr_time_from_sec(7 * MD_SECS_PER_DAY)) {
-            /* at least a week more valid, expect drive and restart way before that */
-            return APR_SUCCESS;
-        }
-    }
-    
-    spec.type = MD_PKEY_TYPE_RSA;
-    spec.params.rsa.bits = MD_PKEY_RSA_BITS_DEF;
-        
-    if (APR_SUCCESS != (rv = md_pkey_gen(&fallback_key, ptemp, &spec))) {
-        md_log_perror(MD_LOG_MARK, MD_LOG_ERR, rv, ptemp, "create fallback key");
-        return rv;
-    }
-    
-    if (APR_SUCCESS != (rv = md_store_save(&s_fs->s, ptemp, MD_SG_NONE, NULL, 
-                                           MD_FN_FALLBACK_PKEY, MD_SV_PKEY, 
-                                           (void*)fallback_key, 0))) {
-        md_log_perror(MD_LOG_MARK, MD_LOG_ERR, rv, ptemp, "save fallback key");
-        return rv;
-    }
-
-    if (APR_SUCCESS != (rv = md_cert_self_sign(&fallback_cert, "Apache Managed Domain Fallback", 
-                                               "temporary.invalid.certificate", fallback_key, 
-                                               apr_time_from_sec(14 * MD_SECS_PER_DAY), ptemp))) {
-        md_log_perror(MD_LOG_MARK, MD_LOG_ERR, rv, ptemp, "create fallback certificate");
-        return rv;
-    }
-
-    if (APR_SUCCESS != (rv = md_store_save(&s_fs->s, ptemp, MD_SG_NONE, NULL, 
-                                           MD_FN_FALLBACK_CERT, MD_SV_CERT, 
-                                           (void*)fallback_cert, 0))) {
-        md_log_perror(MD_LOG_MARK, MD_LOG_ERR, rv, ptemp, "save fallback certificate");
-        return rv;
-    }
-
-    return rv;
-}
-
 apr_status_t md_store_fs_init(md_store_t **pstore, apr_pool_t *p, const char *path)
 {
     md_store_fs_t *s_fs;
@@ -356,9 +305,6 @@ apr_status_t md_store_fs_init(md_store_t
         }
     }
     rv = md_util_pool_vdo(setup_store_file, s_fs, p, NULL);
-    if (APR_SUCCESS == rv) {
-        rv = md_util_pool_vdo(setup_fallback_cert, s_fs, p, NULL);
-    }
     
     if (APR_SUCCESS != rv) {
         md_log_perror(MD_LOG_MARK, MD_LOG_ERR, rv, p, "init fs store at %s", path);

Modified: httpd/httpd/trunk/modules/md/md_util.c
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/md/md_util.c?rev=1808241&r1=1808240&r2=1808241&view=diff
==============================================================================
--- httpd/httpd/trunk/modules/md/md_util.c (original)
+++ httpd/httpd/trunk/modules/md/md_util.c Wed Sep 13 14:16:49 2017
@@ -647,52 +647,54 @@ const char *md_util_schemify(apr_pool_t
     return apr_psprintf(p, "%s:%s", def_scheme, s);
 }
 
-apr_status_t md_util_abs_uri_check(apr_pool_t *p, const char *uri, const char **perr)
+static apr_status_t uri_check(apr_uri_t *uri_parsed, apr_pool_t *p, 
+                              const char *uri, const char **perr)
 {
     const char *s, *err = NULL;
-    apr_uri_t uri_parsed;
     apr_status_t rv;
     
-    if (APR_SUCCESS != (rv = apr_uri_parse(p, uri, &uri_parsed))) {
+    if (APR_SUCCESS != (rv = apr_uri_parse(p, uri, uri_parsed))) {
         err = "not an uri";
     }
-    else if (!uri_parsed.scheme) {
-        err = "missing uri scheme";
-    }
-    else if (strlen(uri_parsed.scheme) + 1 >= strlen(uri)) {
-        err = "missing uri identifier";
-    }
-    else if (strchr(uri, ' ') || strchr(uri, '\t') ) {
-        err = "whitespace in uri";
-    }
-    else if (!strncmp("http", uri_parsed.scheme, 4)) {
-        if (!uri_parsed.hostname) {
-            err = "missing hostname";
-        }
-        else if (!md_util_is_dns_name(p, uri_parsed.hostname, 0)) {
-            err = "invalid hostname";
-        }
-        if (uri_parsed.port_str && (uri_parsed.port == 0 || uri_parsed.port > 65353)) {
-            err = "invalid port";
+    else if (uri_parsed->scheme) {
+        if (strlen(uri_parsed->scheme) + 1 >= strlen(uri)) {
+            err = "missing uri identifier";
+        }
+        else if (!strncmp("http", uri_parsed->scheme, 4)) {
+            if (!uri_parsed->hostname) {
+                err = "missing hostname";
+            }
+            else if (!md_util_is_dns_name(p, uri_parsed->hostname, 0)) {
+                err = "invalid hostname";
+            }
+            if (uri_parsed->port_str 
+                && (!apr_isdigit(uri_parsed->port_str[0])
+                || uri_parsed->port == 0
+                || uri_parsed->port > 65353)) {
+                err = "invalid port";
+            }
+        }
+        else if (!strcmp("mailto", uri_parsed->scheme)) {
+            s = strchr(uri, '@');
+            if (!s) {
+                err = "missing @";
+            }
+            else if (strchr(s+1, '@')) {
+                err = "duplicate @";
+            }
+            else if (s == uri + strlen(uri_parsed->scheme) + 1) {
+                err = "missing local part";
+            }
+            else if (s == (uri + strlen(uri)-1)) {
+                err = "missing hostname";
+            }
+            else if (strstr(uri, "..")) {
+                err = "double period";
+            }
         }
     }
-    else if (!strcmp("mailto", uri_parsed.scheme)) {
-        s = strchr(uri, '@');
-        if (!s) {
-            err = "missing @";
-        }
-        else if (strchr(s+1, '@')) {
-            err = "duplicate @";
-        }
-        else if (s == uri + strlen(uri_parsed.scheme) + 1) {
-            err = "missing local part";
-        }
-        else if (s == (uri + strlen(uri)-1)) {
-            err = "missing hostname";
-        }
-        else if (strstr(uri, "..")) {
-            err = "double period";
-        }
+    if (strchr(uri, ' ') || strchr(uri, '\t') ) {
+        err = "whitespace in uri";
     }
     
     if (err) {
@@ -702,6 +704,39 @@ apr_status_t md_util_abs_uri_check(apr_p
     return rv;
 }
 
+apr_status_t md_util_abs_uri_check(apr_pool_t *p, const char *uri, const char **perr)
+{
+    apr_uri_t uri_parsed;
+    apr_status_t rv;
+
+    if (APR_SUCCESS == (rv = uri_check(&uri_parsed, p, uri, perr))) {
+        if (!uri_parsed.scheme) {
+            *perr = "missing uri scheme";
+            return APR_EINVAL;
+        }
+    }
+    return rv;
+}
+
+apr_status_t md_util_abs_http_uri_check(apr_pool_t *p, const char *uri, const char **perr)
+{
+    apr_uri_t uri_parsed;
+    apr_status_t rv;
+
+    if (APR_SUCCESS == (rv = uri_check(&uri_parsed, p, uri, perr))) {
+        if (!uri_parsed.scheme) {
+            *perr = "missing uri scheme";
+            return APR_EINVAL;
+        }
+        if (apr_strnatcasecmp("http", uri_parsed.scheme) 
+            && apr_strnatcasecmp("https", uri_parsed.scheme)) {
+            *perr = "uri scheme must be http or https";
+            return APR_EINVAL;
+        }
+    }
+    return rv;
+}
+
 /* retry login ************************************************************************************/
 
 apr_status_t md_util_try(md_util_try_fn *fn, void *baton, int ignore_errs, 

Modified: httpd/httpd/trunk/modules/md/md_util.h
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/md/md_util.h?rev=1808241&r1=1808240&r2=1808241&view=diff
==============================================================================
--- httpd/httpd/trunk/modules/md/md_util.h (original)
+++ httpd/httpd/trunk/modules/md/md_util.h Wed Sep 13 14:16:49 2017
@@ -117,7 +117,8 @@ apr_size_t md_util_base64url_decode(cons
 const char *md_util_schemify(apr_pool_t *p, const char *s, const char *def_scheme);
 
 apr_status_t md_util_abs_uri_check(apr_pool_t *p, const char *s, const char **perr);
- 
+apr_status_t md_util_abs_http_uri_check(apr_pool_t *p, const char *uri, const char **perr);
+
 const char *md_link_find_relation(const struct apr_table_t *headers, 
                                   apr_pool_t *pool, const char *relation);
 

Modified: httpd/httpd/trunk/modules/md/md_version.h
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/md/md_version.h?rev=1808241&r1=1808240&r2=1808241&view=diff
==============================================================================
--- httpd/httpd/trunk/modules/md/md_version.h (original)
+++ httpd/httpd/trunk/modules/md/md_version.h Wed Sep 13 14:16:49 2017
@@ -26,7 +26,7 @@
  * @macro
  * Version number of the md module as c string
  */
-#define MOD_MD_VERSION "0.9.2-git"
+#define MOD_MD_VERSION "0.9.5"
 
 /**
  * @macro
@@ -34,7 +34,7 @@
  * release. This is a 24 bit number with 8 bits for major number, 8 bits
  * for minor and 8 bits for patch. Version 1.2.3 becomes 0x010203.
  */
-#define MOD_MD_VERSION_NUM 0x000902
+#define MOD_MD_VERSION_NUM 0x000905
 
 #define MD_EXPERIMENTAL 0
 #define MD_ACME_DEF_URL    "https://acme-v01.api.letsencrypt.org/directory"

Modified: httpd/httpd/trunk/modules/md/mod_md.c
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/md/mod_md.c?rev=1808241&r1=1808240&r2=1808241&view=diff
==============================================================================
--- httpd/httpd/trunk/modules/md/mod_md.c (original)
+++ httpd/httpd/trunk/modules/md/mod_md.c Wed Sep 13 14:16:49 2017
@@ -14,11 +14,13 @@
  */
 
 #include <assert.h>
+#include <apr_optional.h>
 #include <apr_strings.h>
 
 #include <ap_release.h>
 #include <mpm_common.h>
 #include <httpd.h>
+#include <http_core.h>
 #include <http_protocol.h>
 #include <http_request.h>
 #include <http_log.h>
@@ -41,6 +43,7 @@
 #include "mod_md.h"
 #include "mod_md_config.h"
 #include "mod_md_os.h"
+#include "mod_ssl.h"
 #include "mod_watchdog.h"
 
 static void md_hooks(apr_pool_t *pool);
@@ -92,6 +95,12 @@ static void md_merge_srv(md_t *md, md_sr
         md->pkey_spec = md->sc->pkey_spec;
         
     }
+    if (md->require_https < 0) {
+        md->require_https = md_config_geti(md->sc, MD_CONFIG_REQUIRE_HTTPS);
+    }
+    if (md->must_staple < 0) {
+        md->must_staple = md_config_geti(md->sc, MD_CONFIG_MUST_STAPLE);
+    }
 }
 
 static apr_status_t check_coverage(md_t *md, const char *domain, server_rec *s, apr_pool_t *p)
@@ -113,28 +122,61 @@ static apr_status_t check_coverage(md_t
     }
 }
 
-static apr_status_t apply_to_servers(md_t *md, server_rec *base_server, 
+static apr_status_t md_covers_server(md_t *md, server_rec *s, apr_pool_t *p)
+{
+    apr_status_t rv;
+    const char *name;
+    int i;
+    
+    if (APR_SUCCESS == (rv = check_coverage(md, s->server_hostname, s, p)) && s->names) {
+        for (i = 0; i < s->names->nelts; ++i) {
+            name = APR_ARRAY_IDX(s->names, i, const char*);
+            if (APR_SUCCESS != (rv = check_coverage(md, name, s, p))) {
+                break;
+            }
+        }
+    }
+    return rv;
+}
+
+static int matches_port_somewhere(server_rec *s, int port)
+{
+    server_addr_rec *sa;
+    
+    for (sa = s->addrs; sa; sa = sa->next) {
+        if (sa->host_port == port) {
+            /* host_addr might be general (0.0.0.0) or specific, we count this as match */
+            return 1;
+        }
+        if (sa->host_port == 0) {
+            /* wildcard port, answers to all ports. Rare, but may work. */
+            return 1;
+        }
+    }
+    return 0;
+}
+
+static apr_status_t assign_to_servers(md_t *md, server_rec *base_server, 
                                      apr_pool_t *p, apr_pool_t *ptemp)
 {
-    server_rec *s;
+    server_rec *s, *s_https;
     request_rec r;
     md_srv_conf_t *sc;
     md_mod_conf_t *mc;
-    apr_status_t rv = APR_SUCCESS, rv2;
-    int i, j;
-    const char *domain, *name;
+    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;
-    
-    /* Find the (at most one) managed domain for each vhost/base server and
-     * remember it at our config for it. 
-     * The config is not accepted, if a vhost matches 2 or more managed domains.
+
+    /* 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.
      */
     memset(&r, 0, sizeof(r));
-    sc = NULL;
+    servers = apr_array_make(ptemp, 5, sizeof(server_rec*));
     
-    /* This MD may apply to 0, 1 or more sever_recs */
     for (s = base_server; s; s = s->next) {
         r.server = s;
         
@@ -142,8 +184,7 @@ static apr_status_t apply_to_servers(md_
             domain = APR_ARRAY_IDX(md->domains, i, const char*);
             
             if (ap_matches_request_vhost(&r, domain, s->port)) {
-                /* Create a unique md_srv_conf_t record for this server. 
-                 * We keep local information here. */
+                /* Create a unique md_srv_conf_t record for this server, if there is none yet */
                 sc = md_config_get_unique(s, p);
                 
                 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, base_server, APLOGNO(10041)
@@ -158,54 +199,91 @@ static apr_status_t apply_to_servers(md_
                     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);
-                    rv = APR_EINVAL;
-                    goto next_server;
+                    return APR_EINVAL;
+                }
+                
+                /* If server has name or an alias not covered,
+                 * a generated certificate will not match. 
+                 */
+                if (APR_SUCCESS != (rv = md_covers_server(md, s, ptemp))) {
+                    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);
                 
-                /* If there is a non-default ServerAdmin defined for this vhost, take
-                 * that one as contact info */
+                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)) {
-                    apr_array_clear(md->contacts);
-                    APR_ARRAY_PUSH(md->contacts, const char *) = 
-                    md_util_schemify(p, s->server_admin, "mailto");
-                    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, base_server, APLOGNO(10044)
-                                 "Managed Domain %s assigned server admin %s", md->name,
-                                 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()
+                                 "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;
+                    
                 }
-                /* remember */
-                sc->assigned = md;
                 
-                /* This server matches a managed domain. If it contains names or
-                 * alias that are not in this md, a generated certificate will not match. */
-                if (APR_SUCCESS == (rv2 = check_coverage(md, s->server_hostname, s, p))
-                    && s->names) {
-                    for (j = 0; j < s->names->nelts; ++j) {
-                        name = APR_ARRAY_IDX(s->names, j, const char*);
-                        if (APR_SUCCESS != (rv2 = check_coverage(md, name, s, p))) {
-                            break;
-                        }
+                /* 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 (APR_SUCCESS != rv2) {
-                    rv = rv2;
+                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()
+                                 "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);
                 }
-                goto next_server;
             }
         }
-    next_server:
-        continue;
-    }
-    
-    if (sc == NULL && 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;
+        
     }
     return rv;
 }
@@ -267,9 +345,9 @@ static apr_status_t md_calc_md_list(apr_
             }
         }
 
-        /* Apply to the vhost(s) that this MD matches - if any. Perform some
+        /* Assign MD to the server_rec configs that it matches. Perform some
          * last finishing touches on the MD. */
-        if (APR_SUCCESS != (rv = apply_to_servers(md, base_server, p, ptemp))) {
+        if (APR_SUCCESS != (rv = assign_to_servers(md, base_server, p, ptemp))) {
             return rv;
         }
 
@@ -433,6 +511,16 @@ static void init_setups(apr_pool_t *p, s
 }
 
 /**************************************************************************************************/
+/* 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_"
@@ -442,72 +530,130 @@ static APR_OPTIONAL_FN_TYPE(ap_watchdog_
 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;
     ap_watchdog_t *watchdog;
-    int all_valid;
-    apr_time_t valid_not_before;
-    int error_count;
-    int processed_count;
-
-    int error_runs;
+    
     apr_time_t next_change;
-    apr_time_t next_valid;
     
-    apr_array_header_t *mds;
+    apr_array_header_t *jobs;
     md_reg_t *reg;
 } md_watchdog;
 
-static apr_status_t drive_md(md_watchdog *wd, md_t *md, apr_pool_t *ptemp)
+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 check_job(md_watchdog *wd, md_job_t *job, apr_pool_t *ptemp)
 {
     apr_status_t rv = APR_SUCCESS;
-    apr_time_t renew_time, now, valid_from;
+    apr_time_t valid_from, delay;
     int errored, renew;
     char ts[APR_RFC822_DATE_LEN];
     
-    if (md->state == MD_S_MISSING) {
-        rv = APR_INCOMPLETE;
+    if (apr_time_now() < job->next_check) {
+        /* Job needs to wait */
+        return APR_EAGAIN;
+    }
+    
+    job->next_check = 0;
+    if (job->md->state == MD_S_MISSING) {
+        job->stalled = 1;
     }
-    if (md->state == MD_S_COMPLETE && !md->expires) {
-        /* This is our indicator that we did already renewed this managed domain
-         * successfully and only wait on the next restart for it to activate */
-        now = apr_time_now();
-        ap_log_error( APLOG_MARK, APLOG_INFO, 0, wd->s, APLOGNO(10051) 
-                     "md(%s): has been renewed, should be activated in %s", 
-                     md->name, (md->valid_from <= now)? "about now" : 
-                     md_print_duration(ptemp, md->valid_from - now));
+    
+    if (job->stalled) {
+        /* Missing information, this will not change until configuration
+         * is changed and server restarted */
+         return APR_INCOMPLETE;
+    }
+    else if (job->renewed) {
+        assess_renewal(wd, job, ptemp);
     }
-    else if (APR_SUCCESS == (rv = md_reg_assess(wd->reg, md, &errored, &renew, wd->p))) {
+    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", md->name);
+                         "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", md->name, md->state);
+                         "md(%s): state=%d, driving", job->md->name, job->md->state);
                          
-            rv = md_reg_stage(wd->reg, md, NULL, 0, &valid_from, ptemp);
+            rv = md_reg_stage(wd->reg, job->md, NULL, 0, &valid_from, ptemp);
             
             if (APR_SUCCESS == rv) {
-                md->state = MD_S_COMPLETE;
-                md->expires = 0;
-                md->valid_from = valid_from;
-                ++wd->processed_count;
-                if (!wd->next_valid || wd->next_valid > valid_from) {
-                    wd->next_valid = valid_from;
-                }
+                job->renewed = 1;
+                job->restart_at = valid_from;
+                assess_renewal(wd, job, ptemp);
             }
         }
         else {
-            apr_rfc822_date(ts, md->expires);
+            job->next_check = job->md->expires - job->md->renew_window;
+
+            apr_rfc822_date(ts, job->md->expires);
             ap_log_error( APLOG_MARK, APLOG_DEBUG, 0, wd->s, APLOGNO(10053) 
-                         "md(%s): is complete, cert expires %s", md->name, ts);
-            renew_time = md->expires - md->renew_window;
-            if (renew_time < wd->next_change) {
-                wd->next_change = renew_time;
-            }
+                         "md(%s): is complete, 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));
+    }
+    
+    job->last_rv = rv;
     return rv;
 }
 
@@ -515,97 +661,50 @@ static apr_status_t run_watchdog(int sta
 {
     md_watchdog *wd = baton;
     apr_status_t rv = APR_SUCCESS;
-    md_t *md;
+    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->mds->nelts);
+                         "md watchdog start, auto drive %d mds", wd->jobs->nelts);
             break;
         case AP_WATCHDOG_STATE_RUNNING:
             assert(wd->reg);
             
-            wd->all_valid = 1;
-            wd->valid_not_before = 0;
-            wd->processed_count = 0;
-            wd->error_count = 0;
             wd->next_change = 0;
-            wd->next_valid = 0;
-            
             ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, wd->s, APLOGNO(10055)
-                         "md watchdog run, auto drive %d mds", wd->mds->nelts);
+                         "md watchdog run, auto drive %d mds", wd->jobs->nelts);
                          
-            /* Check if all Managed Domains are ok or if we have to do something */
-            for (i = 0; i < wd->mds->nelts; ++i) {
-                md = APR_ARRAY_IDX(wd->mds, i, md_t *);
-                
-                rv = drive_md(wd, md, ptemp);
+            /* 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 *);
                 
-                if (APR_STATUS_IS_INCOMPLETE(rv)) {
-                    /* configuration not complete, this MD cannot be driven further */
-                    wd->all_valid = 0;
+                rv = check_job(wd, job, ptemp);
+
+                if (job->need_restart && !job->restart_processed) {
+                    restart = 1;
                 }
-                else if (APR_SUCCESS != rv) {
-                    wd->all_valid = 0;
-                    ++wd->error_count;
-                    ap_log_error( APLOG_MARK, APLOG_ERR, rv, wd->s, APLOGNO(10056) 
-                                 "processing %s", md->name);
+                if (job->next_check && job->next_check < next_run) {
+                    next_run = job->next_check;
                 }
             }
 
-            /* Determine when we want to run next */
-            wd->error_runs = wd->error_count? (wd->error_runs + 1) : 0;
-
-            if (wd->all_valid) {
-                ap_log_error(APLOG_MARK, APLOG_TRACE1, 0, wd->s, "all managed domains are valid");
-            }
-            else if (wd->error_count == 0) {
-                ap_log_error(APLOG_MARK, APLOG_INFO, 0, wd->s, APLOGNO() 
-                             "all managed domains driven as far as possible");
-            }
-            
             now = apr_time_now();
-            /* normally, we'd like to run at least twice a day */
-            next_run = now + apr_time_from_sec(MD_SECS_PER_DAY / 2);
-            
-            /* Unless we know of an MD change before that */
-            if (wd->next_change > 0 && wd->next_change < next_run) {
-                next_run = wd->next_change;
-            }
-            
-            /* Or have to activate a new cert even before that */
-            if (wd->next_valid > now && wd->next_valid < next_run) {
-                next_run = wd->next_valid;
-                ap_log_error(APLOG_MARK, APLOG_TRACE1, 0, wd->s, 
-                             "Delaying activation of %d Managed Domain%s by %s", 
-                             wd->processed_count, (wd->processed_count > 1)? "s have" : " has",
-                             md_print_duration(ptemp, next_run - now));
-            }
-            
-            /* Or encountered errors and like to retry even before that */
-            if (wd->error_count > 0) {
-                apr_interval_time_t delay;
-                
-                /* back off duration, depending on the errors we encounter in a row */
-                delay = apr_time_from_sec(5 << (wd->error_runs - 1));
-                if (delay > apr_time_from_sec(60*60)) {
-                    delay = apr_time_from_sec(60*60);
-                }
-                if (now + delay < next_run) {
-                    ap_log_error(APLOG_MARK, APLOG_INFO, 0, wd->s, APLOGNO(10057) 
-                                 "encountered errors for the %d. time, next try by %s",
-                                 wd->error_runs, md_print_duration(ptemp, delay));
-                    next_run = now + delay;
-                }
-            }
-            
             if (APLOGdebug(wd->s)) {
                 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, wd->s, APLOGNO()
                              "next run in %s", md_print_duration(ptemp, next_run - now));
             }
             wd_set_interval(wd->watchdog, next_run - now, wd, run_watchdog);
+
+            for (i = 0; i < wd->jobs->nelts; ++i) {
+                job = APR_ARRAY_IDX(wd->jobs, i, md_job_t *);
+            }
             break;
             
         case AP_WATCHDOG_STATE_STOPPING:
@@ -614,31 +713,31 @@ static apr_status_t run_watchdog(int sta
             break;
     }
 
-    if (wd->processed_count) {
-        now = apr_time_now();
+    if (restart) {
+        const char *action, *names = "";
+        int n;
         
-        if (wd->all_valid) {
-            if (wd->next_valid <= now) {
-                rv = md_server_graceful(ptemp, wd->s);
-                if (APR_ENOTIMPL == rv) {
-                    /* self-graceful restart not supported in this setup */
-                    ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, wd->s, APLOGNO(10059)
-                                 "%d Managed Domain%s been setup and changes will be "
-                                 "activated on next (graceful) server restart.",
-                                 wd->processed_count, (wd->processed_count > 1)? "s have" : " has");
-                }
+        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;
+                job->restart_processed = 1;
+            }
+        }
+
+        if (n > 0) {
+            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 {
-                /* activation is delayed */
+                action = " and server has been asked to restart now.";
             }
-        }
-        else {
-            ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, wd->s, APLOGNO(10060)
-                         "%d Managed Domain%s been setup, while %d%s "
-                         "still being worked on. You may activate the changes made "
-                         "by triggering a (graceful) restart at any time.",
-                         wd->processed_count, (wd->processed_count > 1)? "s have" : " has",
-                         wd->error_count, (wd->error_count > 1)? " are" : " is");
+            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);
         }
     }
     
@@ -654,6 +753,7 @@ static apr_status_t start_watchdog(apr_a
     apr_status_t rv;
     const char *name;
     md_t *md;
+    md_job_t *job;
     int i, errored, renew;
     
     wd_get_instance = APR_RETRIEVE_OPTIONAL_FN(ap_watchdog_get_instance);
@@ -681,7 +781,7 @@ static apr_status_t start_watchdog(apr_a
     wd->reg = reg;
     wd->s = s;
     
-    wd->mds = apr_array_make(wd->p, 10, sizeof(md_t *));
+    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);
@@ -692,14 +792,18 @@ static apr_status_t start_watchdog(apr_a
                              "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;
+
                 ap_log_error( APLOG_MARK, APLOG_DEBUG, 0, wd->s, APLOGNO(10064) 
                              "md(%s): state=%d, driving", name, md->state);
-                APR_ARRAY_PUSH(wd->mds, md_t*) = md;
             }
         }
     }
 
-    if (!wd->mds->nelts) {
+    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");
@@ -819,6 +923,8 @@ static apr_status_t md_post_config(apr_p
         }
     }
     
+    init_ssl();
+    
     /* If there are MDs to drive, start a watchdog to check on them regularly */
     if (drive_names->nelts > 0) {
         ap_log_error(APLOG_MARK, APLOG_DEBUG, rv, s, APLOGNO(10074)
@@ -854,6 +960,34 @@ static int md_is_managed(server_rec *s)
     return 0;
 }
 
+static apr_status_t setup_fallback_cert(md_store_t *store, const md_t *md, apr_pool_t *p)
+{
+    md_pkey_t *pkey;
+    md_cert_t *cert;
+    md_pkey_spec_t spec;
+    apr_status_t rv;
+
+    spec.type = MD_PKEY_TYPE_RSA;
+    spec.params.rsa.bits = MD_PKEY_RSA_BITS_DEF;
+        
+    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))
+        && 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))) {
+        rv = md_store_save(store, p, MD_SG_DOMAINS, md->name, 
+                           MD_FN_FALLBACK_CERT, MD_SV_CERT, (void*)cert, 0);
+    }
+
+    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)
 {
@@ -871,24 +1005,38 @@ static apr_status_t md_get_certificate(s
         assert(sc->mc);
         assert(sc->mc->store);
         if (APR_SUCCESS != (rv = md_reg_init(&reg, p, sc->mc->store, sc->mc->proxy_url))) {
+            ap_log_error(APLOG_MARK, APLOG_ERR, rv, s, APLOGNO() "init registry");
             return rv;
         }
 
         md = md_reg_get(reg, sc->assigned->name, p);
             
         if (APR_SUCCESS != (rv = md_reg_get_cred_files(reg, md, p, pkeyfile, pcertfile))) {
+            ap_log_error(APLOG_MARK, APLOG_ERR, rv, s, APLOGNO() 
+                         "retrieving credentials for MD %s", md->name);
             return rv;
         }
 
-        if (!*pkeyfile || !*pcertfile 
-            || APR_SUCCESS != md_util_is_file(*pkeyfile, p)
-            || APR_SUCCESS != md_util_is_file(*pcertfile, p)) {
+        if (!fexists(*pkeyfile, p) || !fexists(*pcertfile, p)) { 
             /* 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. */
-            md_store_get_fname(pkeyfile, sc->mc->store, MD_SG_NONE, NULL, MD_FN_FALLBACK_PKEY, p);
-            md_store_get_fname(pcertfile, sc->mc->store, MD_SG_NONE, NULL, MD_FN_FALLBACK_CERT, p);
+             
+            md_store_get_fname(pkeyfile, sc->mc->store, MD_SG_DOMAINS, 
+                               md->name, MD_FN_FALLBACK_PKEY, p);
+            md_store_get_fname(pcertfile, sc->mc->store, MD_SG_DOMAINS, 
+                               md->name, MD_FN_FALLBACK_CERT, p);
+            if (!fexists(*pkeyfile, p) || !fexists(*pcertfile, p)) { 
+                if (APR_SUCCESS != (rv = setup_fallback_cert(sc->mc->store, md, p))) {
+                    ap_log_error(APLOG_MARK, APLOG_TRACE1, rv, s,  
+                                 "%s: setup fallback certificate", md->name);
+                    return rv;
+                }
+            }
             
+            ap_log_error(APLOG_MARK, APLOG_TRACE1, 0, s,  
+                         "%s: providing fallback certificate for server %s", 
+                         md->name, s->server_hostname);
             return APR_EAGAIN;
         }
 
@@ -957,7 +1105,8 @@ static int md_is_challenge(conn_rec *c,
 /**************************************************************************************************/
 /* ACME challenge responses */
 
-#define ACME_CHALLENGE_PREFIX       "/.well-known/acme-challenge/"
+#define WELL_KNOWN_PREFIX           "/.well-known/"
+#define ACME_CHALLENGE_PREFIX       WELL_KNOWN_PREFIX"acme-challenge/"
 
 static int md_http_challenge_pr(request_rec *r)
 {
@@ -965,14 +1114,13 @@ static int md_http_challenge_pr(request_
     const md_srv_conf_t *sc;
     const char *name, *data;
     apr_status_t rv;
-            
+    
     if (!strncmp(ACME_CHALLENGE_PREFIX, r->parsed_uri.path, sizeof(ACME_CHALLENGE_PREFIX)-1)) {
         if (r->method_number == M_GET) {
             md_store_t *store;
         
             sc = ap_get_module_config(r->server->module_config, &md_module);
             store = sc? sc->mc->store : NULL;
-            
             name = r->parsed_uri.path + sizeof(ACME_CHALLENGE_PREFIX)-1;
 
             r->status = HTTP_NOT_FOUND;
@@ -1011,6 +1159,51 @@ static int md_http_challenge_pr(request_
     return DECLINED;
 }
 
+/**************************************************************************************************/
+/* Require Https hook */
+
+static int md_require_https_maybe(request_rec *r)
+{
+    const md_srv_conf_t *sc;
+    apr_uri_t uri;
+    const char *s;
+    int status;
+    
+    if (strncmp(WELL_KNOWN_PREFIX, r->parsed_uri.path, sizeof(WELL_KNOWN_PREFIX)-1)) {
+        sc = ap_get_module_config(r->server->module_config, &md_module);
+        if (sc && sc->assigned && sc->assigned->require_https > MD_REQUIRE_OFF 
+            && opt_ssl_is_https && !opt_ssl_is_https(r->connection)) {
+            /* Do not have https:, but require it. Redirect the request accordingly. 
+             */
+            if (r->method_number == M_GET) {
+                /* safe to use the old-fashioned codes */
+                status = ((MD_REQUIRE_PERMANENT == sc->assigned->require_https)? 
+                          HTTP_MOVED_PERMANENTLY : HTTP_MOVED_TEMPORARILY);
+            }
+            else {
+                /* these should keep the method unchanged on retry */
+                status = ((MD_REQUIRE_PERMANENT == sc->assigned->require_https)? 
+                          HTTP_PERMANENT_REDIRECT : HTTP_TEMPORARY_REDIRECT);
+            }
+            
+            s = ap_construct_url(r->pool, r->uri, r);
+            if (APR_SUCCESS == apr_uri_parse(r->pool, s, &uri)) {
+                uri.scheme = (char*)"https";
+                uri.port = 443;
+                uri.port_str = (char*)"443";
+                uri.query = r->parsed_uri.query;
+                uri.fragment = r->parsed_uri.fragment;
+                s = apr_uri_unparse(r->pool, &uri, APR_URI_UNP_OMITUSERINFO);
+                if (s && *s) {
+                    apr_table_setn(r->headers_out, "Location", s);
+                    return status;
+                }
+            }
+        }
+    }
+    return DECLINED;
+}
+
 /* Runs once per created child process. Perform any process 
  * related initionalization here.
  */
@@ -1039,6 +1232,8 @@ static void md_hooks(apr_pool_t *pool)
 
     /* answer challenges *very* early, before any configured authentication may strike */
     ap_hook_post_read_request(md_http_challenge_pr, NULL, NULL, APR_HOOK_MIDDLE);
+    /* redirect to https if configured */
+    ap_hook_fixups(md_require_https_maybe, NULL, NULL, APR_HOOK_MIDDLE);
 
     APR_REGISTER_OPTIONAL_FN(md_is_managed);
     APR_REGISTER_OPTIONAL_FN(md_get_certificate);

Modified: httpd/httpd/trunk/modules/md/mod_md_config.c
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/md/mod_md_config.c?rev=1808241&r1=1808240&r2=1808241&view=diff
==============================================================================
--- httpd/httpd/trunk/modules/md/mod_md_config.c (original)
+++ httpd/httpd/trunk/modules/md/mod_md_config.c Wed Sep 13 14:16:49 2017
@@ -39,10 +39,12 @@
 #define MD_CMD_DRIVEMODE      "MDDriveMode"
 #define MD_CMD_MEMBER         "MDMember"
 #define MD_CMD_MEMBERS        "MDMembers"
+#define MD_CMD_MUST_STAPLE    "MDMustStaple"
 #define MD_CMD_PORTMAP        "MDPortMap"
 #define MD_CMD_PKEYS          "MDPrivateKeys"
 #define MD_CMD_PROXY          "MDHttpProxy"
 #define MD_CMD_RENEWWINDOW    "MDRenewWindow"
+#define MD_CMD_REQUIREHTTPS   "MDRequireHttps"
 #define MD_CMD_STOREDIR       "MDStoreDir"
 
 #define DEF_VAL     (-1)
@@ -67,6 +69,7 @@ static md_srv_conf_t defconf = {
     &defmc,
 
     1,
+    MD_REQUIRE_OFF,
     MD_DRIVE_AUTO,
     0,
     NULL, 
@@ -112,6 +115,7 @@ static md_mod_conf_t *md_mod_conf_get(ap
 static void srv_conf_props_clear(md_srv_conf_t *sc)
 {
     sc->transitive = DEF_VAL;
+    sc->require_https = MD_REQUIRE_UNSET;
     sc->drive_mode = DEF_VAL;
     sc->must_staple = DEF_VAL;
     sc->pkey_spec = NULL;
@@ -126,6 +130,7 @@ static void srv_conf_props_clear(md_srv_
 static void srv_conf_props_copy(md_srv_conf_t *to, const md_srv_conf_t *from)
 {
     to->transitive = from->transitive;
+    to->require_https = from->require_https;
     to->drive_mode = from->drive_mode;
     to->must_staple = from->must_staple;
     to->pkey_spec = from->pkey_spec;
@@ -139,6 +144,7 @@ static void srv_conf_props_copy(md_srv_c
 
 static void srv_conf_props_apply(md_t *md, const md_srv_conf_t *from, apr_pool_t *p)
 {
+    if (from->require_https != MD_REQUIRE_UNSET) md->require_https = from->require_https;
     if (from->transitive != DEF_VAL) md->transitive = from->transitive;
     if (from->drive_mode != DEF_VAL) md->drive_mode = from->drive_mode;
     if (from->must_staple != DEF_VAL) md->must_staple = from->must_staple;
@@ -176,6 +182,7 @@ static void *md_config_merge(apr_pool_t
     nsc->name = name;
 
     nsc->transitive = (add->transitive != DEF_VAL)? add->transitive : base->transitive;
+    nsc->require_https = (add->require_https != MD_REQUIRE_UNSET)? add->require_https : base->require_https;
     nsc->drive_mode = (add->drive_mode != DEF_VAL)? add->drive_mode : base->drive_mode;
     nsc->must_staple = (add->must_staple != DEF_VAL)? add->must_staple : base->must_staple;
     nsc->pkey_spec = add->pkey_spec? add->pkey_spec : base->pkey_spec;
@@ -426,6 +433,55 @@ static const char *md_config_set_drive_m
     return NULL;
 }
 
+static const char *md_config_set_must_staple(cmd_parms *cmd, void *dc, const char *value)
+{
+    md_srv_conf_t *config = md_config_get(cmd->server);
+    const char *err;
+
+    if (!inside_section(cmd, MD_CMD_MD_SECTION)
+        && (err = ap_check_cmd_context(cmd, GLOBAL_ONLY))) {
+        return err;
+    }
+
+    if (!apr_strnatcasecmp("off", value)) {
+        config->must_staple = 0;
+    }
+    else if (!apr_strnatcasecmp("on", value)) {
+        config->must_staple = 1;
+    }
+    else {
+        return apr_pstrcat(cmd->pool, "unknown '", value, 
+                           "', supported parameter values are 'on' and 'off'", NULL);
+    }
+    return NULL;
+}
+
+static const char *md_config_set_require_https(cmd_parms *cmd, void *dc, const char *value)
+{
+    md_srv_conf_t *config = md_config_get(cmd->server);
+    const char *err;
+
+    if (!inside_section(cmd, MD_CMD_MD_SECTION)
+        && (err = ap_check_cmd_context(cmd, GLOBAL_ONLY))) {
+        return err;
+    }
+
+    if (!apr_strnatcasecmp("off", value)) {
+        config->require_https = MD_REQUIRE_OFF;
+    }
+    else if (!apr_strnatcasecmp(MD_KEY_TEMPORARY, value)) {
+        config->require_https = MD_REQUIRE_TEMPORARY;
+    }
+    else if (!apr_strnatcasecmp(MD_KEY_PERMANENT, value)) {
+        config->require_https = MD_REQUIRE_PERMANENT;
+    }
+    else {
+        return apr_pstrcat(cmd->pool, "unknown '", value, 
+                           "', supported parameter values are 'temporary' and 'permanent'", NULL);
+    }
+    return NULL;
+}
+
 static apr_status_t duration_parse(const char *value, apr_interval_time_t *ptimeout, 
                                    const char *def_unit)
 {
@@ -519,6 +575,10 @@ static const char *md_config_set_proxy(c
     if (err) {
         return err;
     }
+    md_util_abs_http_uri_check(cmd->pool, value, &err);
+    if (err) {
+        return err;
+    }
     sc->mc->proxy_url = value;
     (void)arg;
     return NULL;
@@ -690,6 +750,8 @@ const command_rec md_cmds[] = {
     AP_INIT_TAKE_ARGV( MD_CMD_MEMBERS, md_config_sec_add_members, NULL, RSRC_CONF, 
                       "Define domain name(s) part of the Managed Domain. Use 'auto' or "
                       "'manual' to enable/disable auto adding names from virtual hosts."),
+    AP_INIT_TAKE1(     MD_CMD_MUST_STAPLE, md_config_set_must_staple, NULL, RSRC_CONF, 
+                  "Enable/Disable the Must-Staple flag for new certificates."),
     AP_INIT_TAKE12(    MD_CMD_PORTMAP, md_config_set_port_map, NULL, RSRC_CONF, 
                   "Declare the mapped ports 80 and 443 on the local server. E.g. 80:8000 "
                   "to indicate that the server port 8000 is reachable as port 80 from the "
@@ -703,6 +765,8 @@ const command_rec md_cmds[] = {
                   "the directory for file system storage of managed domain data."),
     AP_INIT_TAKE1(     MD_CMD_RENEWWINDOW, md_config_set_renew_window, NULL, RSRC_CONF, 
                   "Time length for renewal before certificate expires (defaults to days)"),
+    AP_INIT_TAKE1(     MD_CMD_REQUIREHTTPS, md_config_set_require_https, NULL, RSRC_CONF, 
+                  "Redirect non-secure requests to the https: equivalent."),
     AP_INIT_TAKE1(NULL, NULL, NULL, RSRC_CONF, NULL)
 };
 
@@ -765,6 +829,10 @@ int md_config_geti(const md_srv_conf_t *
             return sc->mc->local_443;
         case MD_CONFIG_TRANSITIVE:
             return (sc->transitive != DEF_VAL)? sc->transitive : defconf.transitive;
+        case MD_CONFIG_REQUIRE_HTTPS:
+            return (sc->require_https != MD_REQUIRE_UNSET)? sc->require_https : defconf.require_https;
+        case MD_CONFIG_MUST_STAPLE:
+            return (sc->must_staple != DEF_VAL)? sc->must_staple : defconf.must_staple;
         default:
             return 0;
     }

Modified: httpd/httpd/trunk/modules/md/mod_md_config.h
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/md/mod_md_config.h?rev=1808241&r1=1808240&r2=1808241&view=diff
==============================================================================
--- httpd/httpd/trunk/modules/md/mod_md_config.h (original)
+++ httpd/httpd/trunk/modules/md/mod_md_config.h Wed Sep 13 14:16:49 2017
@@ -31,6 +31,8 @@ typedef enum {
     MD_CONFIG_RENEW_WINDOW,
     MD_CONFIG_TRANSITIVE,
     MD_CONFIG_PROXY,
+    MD_CONFIG_REQUIRE_HTTPS,
+    MD_CONFIG_MUST_STAPLE,
 } md_config_var_t;
 
 typedef struct {
@@ -53,6 +55,7 @@ typedef struct md_srv_conf_t {
     md_mod_conf_t *mc;                 /* global config settings */
     
     int transitive;                    /* != 0 iff VirtualHost names/aliases are auto-added */
+    md_require_t require_https;        /* If MDs require https: access */
     int drive_mode;                    /* mode of obtaining credentials */
     int must_staple;                   /* certificates should set the OCSP Must Staple extension */
     struct md_pkey_spec_t *pkey_spec;  /* specification for generating private keys */