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/10/25 13:27:13 UTC

svn commit: r1868930 [4/5] - in /httpd/httpd/branches/2.4.x: ./ build/ docs/manual/mod/ modules/md/

Modified: httpd/httpd/branches/2.4.x/modules/md/md_store_fs.c
URL: http://svn.apache.org/viewvc/httpd/httpd/branches/2.4.x/modules/md/md_store_fs.c?rev=1868930&r1=1868929&r2=1868930&view=diff
==============================================================================
--- httpd/httpd/branches/2.4.x/modules/md/md_store_fs.c (original)
+++ httpd/httpd/branches/2.4.x/modules/md/md_store_fs.c Fri Oct 25 13:27:12 2019
@@ -55,8 +55,7 @@ struct md_store_fs_t {
     md_store_fs_cb *event_cb;
     void *event_baton;
     
-    const unsigned char *key;
-    apr_size_t key_len;
+    md_data_t key;
     int plain_pkey[MD_SG_COUNT];
     
     int port_80;
@@ -78,9 +77,14 @@ static apr_status_t fs_remove(md_store_t
                               apr_pool_t *p, int force);
 static apr_status_t fs_purge(md_store_t *store, apr_pool_t *p, 
                              md_store_group_t group, const char *name);
+static apr_status_t fs_remove_nms(md_store_t *store, apr_pool_t *p, 
+                                  apr_time_t modified, md_store_group_t group, 
+                                  const char *name, const char *aspect);
 static apr_status_t fs_move(md_store_t *store, apr_pool_t *p, 
                             md_store_group_t from, md_store_group_t to, 
                             const char *name, int archive);
+static apr_status_t fs_rename(md_store_t *store, apr_pool_t *p, 
+                            md_store_group_t group, const char *from, const char *to);
 static apr_status_t fs_iterate(md_store_inspect *inspect, void *baton, md_store_t *store, 
                                apr_pool_t *p, md_store_group_t group,  const char *pattern,
                                const char *aspect, md_store_vtype_t vtype);
@@ -94,23 +98,25 @@ static apr_status_t fs_get_fname(const c
 static int fs_is_newer(md_store_t *store, md_store_group_t group1, md_store_group_t group2,  
                        const char *name, const char *aspect, apr_pool_t *p);
 
+static apr_time_t fs_get_modified(md_store_t *store, md_store_group_t group,  
+                                  const char *name, const char *aspect, apr_pool_t *p);
+
 static apr_status_t init_store_file(md_store_fs_t *s_fs, const char *fname, 
                                     apr_pool_t *p, apr_pool_t *ptemp)
 {
     md_json_t *json = md_json_create(p);
     const char *key64;
-    unsigned char *key;
     apr_status_t rv;
     
     md_json_setn(MD_STORE_VERSION, json, MD_KEY_STORE, MD_KEY_VERSION, NULL);
 
-    s_fs->key_len = FS_STORE_KLEN;
-    s_fs->key = key = apr_pcalloc(p, FS_STORE_KLEN);
-    if (APR_SUCCESS != (rv = md_rand_bytes(key, s_fs->key_len, p))) {
+    s_fs->key.len = FS_STORE_KLEN;
+    s_fs->key.data = apr_pcalloc(p, FS_STORE_KLEN);
+    if (APR_SUCCESS != (rv = md_rand_bytes((unsigned char*)s_fs->key.data, s_fs->key.len, p))) {
         return rv;
     }
         
-    key64 = md_util_base64url_encode((char *)key, s_fs->key_len, ptemp);
+    key64 = md_util_base64url_encode(&s_fs->key, ptemp);
     md_json_sets(key64, json, MD_KEY_KEY, NULL);
     rv = md_json_fcreatex(json, ptemp, MD_JSON_FMT_INDENT, fname, MD_FPROT_F_UONLY);
     memset((char*)key64, 0, strlen(key64));
@@ -193,7 +199,7 @@ static apr_status_t read_store_file(md_s
                                     apr_pool_t *p, apr_pool_t *ptemp)
 {
     md_json_t *json;
-    const char *key64, *key;
+    const char *key64;
     apr_status_t rv;
     double store_version;
     
@@ -214,11 +220,10 @@ static apr_status_t read_store_file(md_s
             return APR_EINVAL;
         }
         
-        s_fs->key_len = md_util_base64url_decode(&key, key64, p);
-        s_fs->key = (const unsigned char*)key;
-        if (s_fs->key_len != FS_STORE_KLEN) {
+        md_util_base64url_decode(&s_fs->key, key64, p);
+        if (s_fs->key.len != FS_STORE_KLEN) {
             md_log_perror(MD_LOG_MARK, MD_LOG_ERR, 0, p, "key length unexpected: %" APR_SIZE_T_FMT, 
-                          s_fs->key_len);
+                          s_fs->key.len);
             return APR_EINVAL;
         }
 
@@ -279,11 +284,14 @@ apr_status_t md_store_fs_init(md_store_t
     s_fs->s.save = fs_save;
     s_fs->s.remove = fs_remove;
     s_fs->s.move = fs_move;
+    s_fs->s.rename = fs_rename;
     s_fs->s.purge = fs_purge;
     s_fs->s.iterate = fs_iterate;
     s_fs->s.iterate_names = fs_iterate_names;
     s_fs->s.get_fname = fs_get_fname;
     s_fs->s.is_newer = fs_is_newer;
+    s_fs->s.get_modified = fs_get_modified;
+    s_fs->s.remove_nms = fs_remove_nms;
     
     /* by default, everything is only readable by the current user */ 
     s_fs->def_perms.dir = MD_FPROT_D_UONLY;
@@ -298,6 +306,9 @@ apr_status_t md_store_fs_init(md_store_t
     /* challenges dir and files are readable by all, no secrets involved */ 
     s_fs->group_perms[MD_SG_CHALLENGES].dir = MD_FPROT_D_UALL_WREAD;
     s_fs->group_perms[MD_SG_CHALLENGES].file = MD_FPROT_F_UALL_WREAD;
+    /* OCSP data is readable by all, no secrets involved */ 
+    s_fs->group_perms[MD_SG_OCSP].dir = MD_FPROT_D_UALL_WREAD;
+    s_fs->group_perms[MD_SG_OCSP].file = MD_FPROT_F_UALL_WREAD;
 
     s_fs->base = apr_pstrdup(p, path);
     
@@ -392,8 +403,8 @@ static void get_pass(const char **ppass,
         *plen = 0;
     }
     else {
-        *ppass = (const char *)s_fs->key;
-        *plen = s_fs->key_len;
+        *ppass = (const char *)s_fs->key.data;
+        *plen = s_fs->key.len;
     }
 }
  
@@ -522,7 +533,6 @@ static apr_status_t pfs_is_newer(void *b
     return rv;
 }
 
- 
 static int fs_is_newer(md_store_t *store, md_store_group_t group1, md_store_group_t group2,  
                        const char *name, const char *aspect, apr_pool_t *p)
 {
@@ -537,6 +547,44 @@ static int fs_is_newer(md_store_t *store
     return 0;
 }
 
+static apr_status_t pfs_get_modified(void *baton, apr_pool_t *p, apr_pool_t *ptemp, va_list ap)
+{
+    md_store_fs_t *s_fs = baton;
+    const char *fname, *name, *aspect;
+    md_store_group_t group;
+    apr_finfo_t inf;
+    apr_time_t *pmtime;
+    apr_status_t rv;
+    
+    (void)p;
+    group = (md_store_group_t)va_arg(ap, int);
+    name = va_arg(ap, const char*);
+    aspect = va_arg(ap, const char*);
+    pmtime = va_arg(ap, apr_time_t*);
+    
+    *pmtime = 0;
+    if (   MD_OK(fs_get_fname(&fname, &s_fs->s, group, name, aspect, ptemp))
+        && MD_OK(apr_stat(&inf, fname, APR_FINFO_MTIME, ptemp))) {
+        *pmtime = inf.mtime;
+    }
+
+    return rv;
+}
+
+static apr_time_t fs_get_modified(md_store_t *store, md_store_group_t group,  
+                                  const char *name, const char *aspect, apr_pool_t *p)
+{
+    md_store_fs_t *s_fs = FS_STORE(store);
+    apr_time_t mtime;
+    apr_status_t rv;
+    
+    rv = md_util_pool_vdo(pfs_get_modified, s_fs, p, group, name, aspect, &mtime, NULL);
+    if (APR_SUCCESS == rv) {
+        return mtime;
+    }
+    return 0;
+}
+ 
 static apr_status_t pfs_save(void *baton, apr_pool_t *p, apr_pool_t *ptemp, va_list ap)
 {
     md_store_fs_t *s_fs = baton;
@@ -700,6 +748,7 @@ typedef struct {
     md_store_inspect *inspect;
     const char *dirname;
     void *baton;
+    apr_time_t ts;
 } inspect_ctx;
 
 static apr_status_t insp(void *baton, apr_pool_t *p, apr_pool_t *ptemp, 
@@ -796,6 +845,66 @@ static apr_status_t fs_iterate_names(md_
     return rv;
 }
 
+static apr_status_t remove_nms_file(void *baton, apr_pool_t *p, apr_pool_t *ptemp, 
+                                    const char *dir, const char *name, apr_filetype_e ftype)
+{
+    inspect_ctx *ctx = baton;
+    const char *fname;
+    apr_finfo_t inf;
+    apr_status_t rv = APR_SUCCESS;
+
+    (void)p;
+    if (APR_DIR == ftype) goto leave;
+    if (APR_SUCCESS != (rv = md_util_path_merge(&fname, ptemp, dir, name, NULL))) goto leave;
+    if (APR_SUCCESS != (rv = apr_stat(&inf, fname, APR_FINFO_MTIME, ptemp))) goto leave;
+    if (inf.mtime >= ctx->ts) goto leave;
+
+    md_log_perror(MD_LOG_MARK, MD_LOG_TRACE3, 0, ptemp, "remove_nms file: %s/%s", dir, name);
+    rv = apr_file_remove(fname, ptemp);
+
+leave:
+    return rv;
+}
+
+static apr_status_t remove_nms_dir(void *baton, apr_pool_t *p, apr_pool_t *ptemp, 
+                                   const char *dir, const char *name, apr_filetype_e ftype)
+{
+    inspect_ctx *ctx = baton;
+    apr_status_t rv;
+    const char *fpath;
+ 
+    (void)ftype;
+    md_log_perror(MD_LOG_MARK, MD_LOG_TRACE3, 0, ptemp, "remove_nms dir at: %s/%s", dir, name);
+    if (MD_OK(md_util_path_merge(&fpath, p, dir, name, NULL))) {
+        ctx->dirname = name;
+        rv = md_util_files_do(remove_nms_file, ctx, p, fpath, ctx->aspect, NULL);
+        if (APR_STATUS_IS_ENOENT(rv)) {
+            rv = APR_SUCCESS;
+        }
+    } 
+    return rv;
+}
+
+static apr_status_t fs_remove_nms(md_store_t *store, apr_pool_t *p, 
+                                  apr_time_t modified, md_store_group_t group, 
+                                  const char *name, const char *aspect)
+{
+    const char *groupname;
+    apr_status_t rv;
+    inspect_ctx ctx;
+    
+    ctx.s_fs = FS_STORE(store);
+    ctx.group = group;
+    ctx.pattern = name;
+    ctx.aspect = aspect;
+    ctx.ts = modified;
+    groupname = md_store_group_name(group);
+
+    rv = md_util_files_do(remove_nms_dir, &ctx, p, ctx.s_fs->base, groupname, name, NULL);
+    
+    return rv;
+}
+
 /**************************************************************************************************/
 /* moving */
 
@@ -926,3 +1035,38 @@ static apr_status_t fs_move(md_store_t *
     md_store_fs_t *s_fs = FS_STORE(store);
     return md_util_pool_vdo(pfs_move, s_fs, p, from, to, name, archive, NULL);
 }
+
+static apr_status_t pfs_rename(void *baton, apr_pool_t *p, apr_pool_t *ptemp, va_list ap)
+{
+    md_store_fs_t *s_fs = baton;
+    const char *group_name, *from_dir, *to_dir;
+    md_store_group_t group;
+    const char *from, *to;
+    apr_status_t rv;
+    
+    (void)p;
+    group = (md_store_group_t)va_arg(ap, int);
+    from = va_arg(ap, const char*);
+    to = va_arg(ap, const char*);
+    
+    group_name = md_store_group_name(group);
+    if (   !MD_OK(md_util_path_merge(&from_dir, ptemp, s_fs->base, group_name, from, NULL))
+        || !MD_OK(md_util_path_merge(&to_dir, ptemp, s_fs->base, group_name, to, NULL))) {
+        goto out;
+    }
+    
+    if (APR_SUCCESS != (rv = apr_file_rename(from_dir, to_dir, ptemp))) {
+        md_log_perror(MD_LOG_MARK, MD_LOG_ERR, rv, ptemp, "rename from %s to %s", 
+                      from_dir, to_dir);
+        goto out;
+    }
+out:
+    return rv;
+}
+
+static apr_status_t fs_rename(md_store_t *store, apr_pool_t *p, 
+                            md_store_group_t group, const char *from, const char *to)
+{
+    md_store_fs_t *s_fs = FS_STORE(store);
+    return md_util_pool_vdo(pfs_rename, s_fs, p, group, from, to, NULL);
+}

Modified: httpd/httpd/branches/2.4.x/modules/md/md_time.c
URL: http://svn.apache.org/viewvc/httpd/httpd/branches/2.4.x/modules/md/md_time.c?rev=1868930&r1=1868929&r2=1868930&view=diff
==============================================================================
--- httpd/httpd/branches/2.4.x/modules/md/md_time.c (original)
+++ httpd/httpd/branches/2.4.x/modules/md/md_time.c Fri Oct 25 13:27:12 2019
@@ -44,6 +44,13 @@ int md_timeperiod_has_ended(const md_tim
     return (time >= period->start) && (time <= period->end);
 }
 
+apr_interval_time_t md_timeperiod_remaining(const md_timeperiod_t *period, apr_time_t time)
+{
+    if (time < period->start) return md_timeperiod_length(period);
+    if (time < period->end) return period->end - time;
+    return 0;
+}
+
 char *md_timeperiod_print(apr_pool_t *p, const md_timeperiod_t *period)
 {
     char tstart[APR_RFC822_DATE_LEN];
@@ -54,31 +61,38 @@ char *md_timeperiod_print(apr_pool_t *p,
     return apr_pstrcat(p, tstart, " - ", tend, NULL);
 }
 
-const char *md_duration_print(apr_pool_t *p, apr_interval_time_t duration)
+static const char *duration_print(apr_pool_t *p, int roughly, apr_interval_time_t duration)
 {
     const char *s = "", *sep = "";
     long days = (long)(apr_time_sec(duration) / MD_SECS_PER_DAY);
     int rem = (int)(apr_time_sec(duration) % MD_SECS_PER_DAY);
     
+    s = roughly? "~" : "";
     if (days > 0) {
-        s = apr_psprintf(p, "%ld days", days);
-        sep = " "; 
+        s = apr_psprintf(p, "%s%ld days", s, days);
+        if (roughly) return s;
+        sep = " ";
     }
     if (rem > 0) {
         int hours = (rem / MD_SECS_PER_HOUR);
         rem = (rem % MD_SECS_PER_HOUR);
         if (hours > 0) {
-            s = apr_psprintf(p, "%s%s%02d hours", s, sep, hours); 
+            s = apr_psprintf(p, "%s%s%d hours", s, sep, hours); 
+        if (roughly) return s;
             sep = " "; 
         }
         if (rem > 0) {
             int minutes = (rem / 60);
             rem = (rem % 60);
             if (minutes > 0) {
-                s = apr_psprintf(p, "%s%s%02d minutes", s, sep, minutes); 
+                s = apr_psprintf(p, "%s%s%d minutes", s, sep, minutes); 
+                if (roughly) return s;
+                sep = " "; 
             }
             if (rem > 0) {
-                s = apr_psprintf(p, "%s%s%02d seconds", s, sep, rem); 
+                s = apr_psprintf(p, "%s%s%d seconds", s, sep, rem); 
+                if (roughly) return s;
+                sep = " "; 
             }
         }
     }
@@ -91,6 +105,16 @@ const char *md_duration_print(apr_pool_t
     return s;
 }
 
+const char *md_duration_print(apr_pool_t *p, apr_interval_time_t duration)
+{
+    return duration_print(p, 0, duration);
+}
+
+const char *md_duration_roughly(apr_pool_t *p, apr_interval_time_t duration)
+{
+    return duration_print(p, 1, duration);
+}
+
 static const char *duration_format(apr_pool_t *p, apr_interval_time_t duration)
 {
     const char *s = "0";
@@ -127,6 +151,11 @@ static const char *duration_format(apr_p
     return s;
 }
 
+const char *md_duration_format(apr_pool_t *p, apr_interval_time_t duration)
+{
+    return duration_format(p, duration);
+}
+
 apr_status_t md_duration_parse(apr_interval_time_t *ptimeout, const char *value, 
                                const char *def_unit)
 {
@@ -203,7 +232,7 @@ static apr_status_t percentage_parse(con
     return APR_EINVAL;
 }
 
-apr_status_t md_timeslice_create(const md_timeslice_t **pts, apr_pool_t *p,
+apr_status_t md_timeslice_create(md_timeslice_t **pts, apr_pool_t *p,
                                  apr_interval_time_t norm, apr_interval_time_t len)
 {
     md_timeslice_t *ts;
@@ -215,7 +244,7 @@ apr_status_t md_timeslice_create(const m
     return APR_SUCCESS;
 }
 
-const char *md_timeslice_parse(const md_timeslice_t **pts, apr_pool_t *p, 
+const char *md_timeslice_parse(md_timeslice_t **pts, apr_pool_t *p, 
                                const char *val, apr_interval_time_t norm)
 {
     md_timeslice_t *ts;

Modified: httpd/httpd/branches/2.4.x/modules/md/md_time.h
URL: http://svn.apache.org/viewvc/httpd/httpd/branches/2.4.x/modules/md/md_time.h?rev=1868930&r1=1868929&r2=1868930&view=diff
==============================================================================
--- httpd/httpd/branches/2.4.x/modules/md/md_time.h (original)
+++ httpd/httpd/branches/2.4.x/modules/md/md_time.h Fri Oct 25 13:27:12 2019
@@ -22,16 +22,19 @@
 #define MD_SECS_PER_HOUR      (60*60)
 #define MD_SECS_PER_DAY       (24*MD_SECS_PER_HOUR)
 
-typedef struct {
+typedef struct md_timeperiod_t md_timeperiod_t;
+
+struct md_timeperiod_t {
     apr_time_t start;
     apr_time_t end;
-} md_timeperiod_t;
+};
 
 apr_time_t md_timeperiod_length(const md_timeperiod_t *period);
 
 int md_timeperiod_contains(const md_timeperiod_t *period, apr_time_t time);
 int md_timeperiod_has_started(const md_timeperiod_t *period, apr_time_t time);
 int md_timeperiod_has_ended(const md_timeperiod_t *period, apr_time_t time);
+apr_interval_time_t md_timeperiod_remaining(const md_timeperiod_t *period, apr_time_t time);
 
 char *md_timeperiod_print(apr_pool_t *p, const md_timeperiod_t *period);
 
@@ -39,6 +42,7 @@ char *md_timeperiod_print(apr_pool_t *p,
  * Print a human readable form of the give duration in days/hours/min/sec 
  */
 const char *md_duration_print(apr_pool_t *p, apr_interval_time_t duration);
+const char *md_duration_roughly(apr_pool_t *p, apr_interval_time_t duration);
 
 /**
  * Parse a machine readable string duration in the form of NN[unit], where
@@ -46,18 +50,19 @@ const char *md_duration_print(apr_pool_t
  */
 apr_status_t md_duration_parse(apr_interval_time_t *ptimeout, const char *value, 
                                const char *def_unit);
+const char *md_duration_format(apr_pool_t *p, apr_interval_time_t duration);
 
 typedef struct {
     apr_interval_time_t norm; /* if > 0, normalized base length */
     apr_interval_time_t len;  /* length of the timespan */
 } md_timeslice_t;
 
-apr_status_t md_timeslice_create(const md_timeslice_t **pts, apr_pool_t *p,
+apr_status_t md_timeslice_create(md_timeslice_t **pts, apr_pool_t *p,
                                  apr_interval_time_t norm, apr_interval_time_t len); 
 
 int md_timeslice_eq(const md_timeslice_t *ts1, const md_timeslice_t *ts2);
 
-const char *md_timeslice_parse(const md_timeslice_t **pts, apr_pool_t *p, 
+const char *md_timeslice_parse(md_timeslice_t **pts, apr_pool_t *p, 
                               const char *val, apr_interval_time_t defnorm);
 const char *md_timeslice_format(const md_timeslice_t *ts, apr_pool_t *p);
 

Modified: httpd/httpd/branches/2.4.x/modules/md/md_util.c
URL: http://svn.apache.org/viewvc/httpd/httpd/branches/2.4.x/modules/md/md_util.c?rev=1868930&r1=1868929&r2=1868930&view=diff
==============================================================================
--- httpd/httpd/branches/2.4.x/modules/md/md_util.c (original)
+++ httpd/httpd/branches/2.4.x/modules/md/md_util.c Fri Oct 25 13:27:12 2019
@@ -14,6 +14,7 @@
  * limitations under the License.
  */
  
+#include <assert.h>
 #include <stdio.h>
 
 #include <apr_lib.h>
@@ -70,9 +71,9 @@ apr_status_t md_util_pool_vdo(md_util_va
 /**************************************************************************************************/
 /* data chunks */
 
-md_data *md_data_create(apr_pool_t *p, const char *data, apr_size_t len)
+md_data_t *md_data_create(apr_pool_t *p, const char *data, apr_size_t len)
 {
-    md_data *d;
+    md_data_t *d;
     
     d = apr_palloc(p, sizeof(*d));
     d->len = len;
@@ -80,6 +81,22 @@ md_data *md_data_create(apr_pool_t *p, c
     return d;
 }
 
+md_data_t *md_data_make(apr_pool_t *p, apr_size_t len)
+{
+    md_data_t *d;
+    
+    d = apr_palloc(p, sizeof(*d));
+    d->len = len;
+    d->data = apr_pcalloc(p, len);
+    return d;
+}
+
+void md_data_assign_pcopy(md_data_t *dest, const md_data_t *src, apr_pool_t *p)
+{
+    dest->data = (src->data && src->len)? apr_pmemdup(p, src->data, src->len) : NULL;
+    dest->len = dest->data? src->len : 0;
+}
+
 static const char * const hex_const[] = {
     "00", "01", "02", "03", "04", "05", "06", "07", "08", "09", "0a", "0b", "0c", "0d", "0e", "0f", 
     "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "1a", "1b", "1c", "1d", "1e", "1f", 
@@ -100,7 +117,7 @@ static const char * const hex_const[] =
 };
 
 apr_status_t md_data_to_hex(const char **phex, char separator,
-                            apr_pool_t *p, const md_data *data)
+                            apr_pool_t *p, const md_data_t *data)
 {
     char *hex, *cp;
     const char * x;
@@ -122,6 +139,47 @@ apr_status_t md_data_to_hex(const char *
 }
 
 /**************************************************************************************************/
+/* generic arrays */
+
+int md_array_remove_at(struct apr_array_header_t *a, int idx)
+{
+    char *ps, *pe;
+
+    if (idx < 0 || idx >= a->nelts) return 0;
+    if (idx+1 == a->nelts) {
+        --a->nelts;
+    }
+    else {
+        ps = (a->elts + (idx * a->elt_size));
+        pe = ps + a->elt_size;
+        memmove(ps, pe, (a->nelts - (idx+1)) * a->elt_size);
+        --a->nelts;
+    }
+    return 1;
+}
+
+int md_array_remove(struct apr_array_header_t *a, void *elem)
+{
+    int i, n, m;
+    void **pe;
+    
+    assert(sizeof(void*) == a->elt_size);
+    n = i = 0;
+    while (i < a->nelts) {
+        pe = &APR_ARRAY_IDX(a, i, void*);
+        if (*pe == elem) {
+            m = a->nelts - (i+1);
+            if (m > 0) memmove(pe, pe+1, (unsigned)m*sizeof(void*));
+            a->nelts--;
+            n++;
+            continue;
+        }
+        ++i;
+    }
+    return n;
+}
+
+/**************************************************************************************************/
 /* string related */
 
 int md_array_is_empty(const struct apr_array_header_t *array)
@@ -325,7 +383,7 @@ apr_status_t md_util_freplace(const char
 creat:
     while (i < max && APR_EEXIST == (rv = md_util_fcreatex(&f, tmp, perms, p))) {
         ++i;
-        apr_sleep(apr_time_msec(50));
+        apr_sleep(apr_time_from_msec(50));
     } 
     if (APR_EEXIST == rv 
         && APR_SUCCESS == (rv = apr_file_remove(tmp, p))
@@ -503,7 +561,7 @@ static apr_status_t match_and_do(md_util
                           "candidate=%s matches pattern", finfo.name);
             if (ndepth < ctx->patterns->nelts) {
                 md_log_perror(MD_LOG_MARK, MD_LOG_TRACE4, 0, ptemp, "match_and_do "
-                              "need to go deepter");
+                              "need to go deeper");
                 if (APR_DIR == finfo.filetype) { 
                     /* deeper and deeper, irgendwo in der tiefe leuchtet ein licht */
                     rv = md_util_path_merge(&npath, ptemp, path, finfo.name, NULL);
@@ -1023,7 +1081,7 @@ static const unsigned char BASE64URL_CHA
 
 #define BASE64URL_CHAR(x)    BASE64URL_CHARS[ (unsigned int)(x) & 0x3fu ]
    
-apr_size_t md_util_base64url_decode(const char **decoded, const char *encoded, 
+apr_size_t md_util_base64url_decode(md_data_t *decoded, const char *encoded, 
                                     apr_pool_t *pool)
 {
     const unsigned char *e = (const unsigned char *)encoded;
@@ -1037,10 +1095,10 @@ apr_size_t md_util_base64url_decode(cons
     }
     len = (int)(p - e);
     mlen = (len/4)*4;
-    *decoded = apr_pcalloc(pool, (apr_size_t)len + 1);
+    decoded->data = apr_pcalloc(pool, (apr_size_t)len + 1);
     
     i = 0;
-    d = (unsigned char*)*decoded;
+    d = (unsigned char*)decoded->data;
     for (; i < mlen; i += 4) {
         n = ((BASE64URL_UINT6[ e[i+0] ] << 18) +
              (BASE64URL_UINT6[ e[i+1] ] << 12) +
@@ -1069,14 +1127,15 @@ apr_size_t md_util_base64url_decode(cons
         default: /* do nothing */
             break;
     }
-    return (apr_size_t)(mlen/4*3 + remain);
+    decoded->len = (apr_size_t)(mlen/4*3 + remain);
+    return decoded->len; 
 }
 
-const char *md_util_base64url_encode(const char *data, apr_size_t dlen, apr_pool_t *pool)
+const char *md_util_base64url_encode(const md_data_t *data, apr_pool_t *pool)
 {
-    int i, len = (int)dlen;
-    apr_size_t slen = ((dlen+2)/3)*4 + 1; /* 0 terminated */
-    const unsigned char *udata = (const unsigned char*)data;
+    int i, len = (int)data->len;
+    apr_size_t slen = ((data->len+2)/3)*4 + 1; /* 0 terminated */
+    const unsigned char *udata = (const unsigned char*)data->data;
     unsigned char *enc, *p = apr_pcalloc(pool, slen);
     
     enc = p;

Modified: httpd/httpd/branches/2.4.x/modules/md/md_util.h
URL: http://svn.apache.org/viewvc/httpd/httpd/branches/2.4.x/modules/md/md_util.h?rev=1868930&r1=1868929&r2=1868930&view=diff
==============================================================================
--- httpd/httpd/branches/2.4.x/modules/md/md_util.h (original)
+++ httpd/httpd/branches/2.4.x/modules/md/md_util.h Fri Oct 25 13:27:12 2019
@@ -35,16 +35,36 @@ apr_status_t md_util_pool_vdo(md_util_va
 /**************************************************************************************************/
 /* data chunks */
 
-typedef struct md_data md_data;
-struct md_data {
+typedef struct md_data_t md_data_t;
+struct md_data_t {
     const char *data;
     apr_size_t len;
 };
 
-md_data *md_data_create(apr_pool_t *p, const char *data, apr_size_t len);
+#define MD_DATA_CWRAP(d, buffer)       md_data_t d = { buffer, sizeof(buffer) }
+
+md_data_t *md_data_make(apr_pool_t *p, apr_size_t len);
+md_data_t *md_data_create(apr_pool_t *p, const char *data, apr_size_t len);
+
+void md_data_assign_pcopy(md_data_t *dest, const md_data_t *src, apr_pool_t *p);
 
 apr_status_t md_data_to_hex(const char **phex, char separator,
-                            apr_pool_t *p, const md_data *data);
+                            apr_pool_t *p, const md_data_t *data);
+
+/**************************************************************************************************/
+/* generic arrays */
+
+/**
+ * In an array of pointers, remove all entries == elem. Returns the number
+ * of entries removed.
+ */
+int md_array_remove(struct apr_array_header_t *a, void *elem);
+
+/* 
+ * Remove the ith entry from the array.
+ * @return != 0 iff an entry was removed, e.g. idx was not outside range 
+ */
+int md_array_remove_at(struct apr_array_header_t *a, int idx);
 
 /**************************************************************************************************/
 /* string related */
@@ -173,9 +193,8 @@ apr_status_t md_text_freplace(const char
 
 /**************************************************************************************************/
 /* base64 url encodings */
-const char *md_util_base64url_encode(const char *data, 
-                                     apr_size_t len, apr_pool_t *pool);
-apr_size_t md_util_base64url_decode(const char **decoded, const char *encoded, 
+const char *md_util_base64url_encode(const md_data_t *data, apr_pool_t *pool);
+apr_size_t md_util_base64url_decode(md_data_t *decoded, const char *encoded, 
                                     apr_pool_t *pool);
 
 /**************************************************************************************************/

Modified: httpd/httpd/branches/2.4.x/modules/md/md_version.h
URL: http://svn.apache.org/viewvc/httpd/httpd/branches/2.4.x/modules/md/md_version.h?rev=1868930&r1=1868929&r2=1868930&view=diff
==============================================================================
--- httpd/httpd/branches/2.4.x/modules/md/md_version.h (original)
+++ httpd/httpd/branches/2.4.x/modules/md/md_version.h Fri Oct 25 13:27:12 2019
@@ -27,7 +27,7 @@
  * @macro
  * Version number of the md module as c string
  */
-#define MOD_MD_VERSION "2.0.10"
+#define MOD_MD_VERSION "2.2.1"
 
 /**
  * @macro
@@ -35,7 +35,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 0x02000a
+#define MOD_MD_VERSION_NUM 0x020201
 
 #define MD_ACME_DEF_URL    "https://acme-v02.api.letsencrypt.org/directory"
 

Modified: httpd/httpd/branches/2.4.x/modules/md/mod_md.c
URL: http://svn.apache.org/viewvc/httpd/httpd/branches/2.4.x/modules/md/mod_md.c?rev=1868930&r1=1868929&r2=1868930&view=diff
==============================================================================
--- httpd/httpd/branches/2.4.x/modules/md/mod_md.c (original)
+++ httpd/httpd/branches/2.4.x/modules/md/mod_md.c Fri Oct 25 13:27:12 2019
@@ -37,8 +37,10 @@
 #include "md_store.h"
 #include "md_store_fs.h"
 #include "md_log.h"
+#include "md_ocsp.h"
 #include "md_result.h"
 #include "md_reg.h"
+#include "md_status.h"
 #include "md_util.h"
 #include "md_version.h"
 #include "md_acme.h"
@@ -47,6 +49,7 @@
 #include "mod_md.h"
 #include "mod_md_config.h"
 #include "mod_md_drive.h"
+#include "mod_md_ocsp.h"
 #include "mod_md_os.h"
 #include "mod_md_status.h"
 #include "mod_ssl_openssl.h"
@@ -129,7 +132,92 @@ static void init_setups(apr_pool_t *p, s
 }
 
 /**************************************************************************************************/
-/* store & registry setup */
+/* notification handling */
+
+typedef struct {
+    const char *reason;         /* what the notification is about */
+    apr_time_t min_interim;     /* minimum time between notifying for this reason */
+} notify_rate;
+
+static notify_rate notify_rates[] = {
+    { "renewed", apr_time_from_sec(28 * MD_SECS_PER_DAY) }, /* once per month */
+    { "installed", apr_time_from_sec(MD_SECS_PER_DAY) }, /* once per day */
+    { "expiring", apr_time_from_sec(MD_SECS_PER_DAY) },     /* once per day */
+    { "errored", apr_time_from_sec(MD_SECS_PER_HOUR) },     /* once per hour */
+    { "ocsp-renewed", apr_time_from_sec(MD_SECS_PER_DAY) }, /* once per day */
+    { "ocsp-errored", apr_time_from_sec(MD_SECS_PER_HOUR) }, /* once per hour */
+};
+
+static apr_status_t notify(md_job_t *job, const char *reason, 
+                           md_result_t *result, apr_pool_t *p, void *baton)
+{
+    md_mod_conf_t *mc = baton;
+    const char * const *argv;
+    const char *cmdline;
+    int exit_code;
+    apr_status_t rv = APR_SUCCESS;
+    apr_time_t min_interim = 0;
+    md_timeperiod_t since_last;
+    const char *log_msg_reason;
+    int i;
+    
+    log_msg_reason = apr_psprintf(p, "message-%s", reason);
+    for (i = 0; i < (int)(sizeof(notify_rates)/sizeof(notify_rates[0])); ++i) {
+        if (!strcmp(reason, notify_rates[i].reason)) {
+            min_interim = notify_rates[i].min_interim;
+        }
+    }
+    if (min_interim > 0) {
+        since_last.start = md_job_log_get_time_of_latest(job, log_msg_reason);
+        since_last.end = apr_time_now();
+        if (md_timeperiod_length(&since_last) < min_interim) {
+            /* not enough time has passed since we sent the last notification
+             * for this reason. */
+            return APR_SUCCESS;
+        }
+    }
+    
+    if (!strcmp("renewed", reason)) {
+        if (mc->notify_cmd) {
+            cmdline = apr_psprintf(p, "%s %s", mc->notify_cmd, job->mdomain); 
+            apr_tokenize_to_argv(cmdline, (char***)&argv, p);
+            rv = md_util_exec(p, argv[0], argv, &exit_code);
+            
+            if (APR_SUCCESS == rv && exit_code) rv = APR_EGENERAL;
+            if (APR_SUCCESS != rv) {
+                md_result_problem_printf(result, rv, MD_RESULT_LOG_ID(APLOGNO(10108)), 
+                                         "MDNotifyCmd %s failed with exit code %d.", 
+                                         mc->notify_cmd, exit_code);
+                md_result_log(result, MD_LOG_ERR);
+                md_job_log_append(job, "notify-error", result->problem, result->detail);
+                return rv;
+            }
+        }
+        md_log_perror(MD_LOG_MARK, MD_LOG_NOTICE, 0, p, APLOGNO(10059) 
+                     "The Managed Domain %s has been setup and changes "
+                     "will be activated on next (graceful) server restart.", job->mdomain);
+    }
+    if (mc->message_cmd) {
+        cmdline = apr_psprintf(p, "%s %s %s", mc->message_cmd, reason, job->mdomain); 
+        apr_tokenize_to_argv(cmdline, (char***)&argv, p);
+        rv = md_util_exec(p, argv[0], argv, &exit_code);
+        
+        if (APR_SUCCESS == rv && exit_code) rv = APR_EGENERAL;
+        if (APR_SUCCESS != rv) {
+            md_result_problem_printf(result, rv, MD_RESULT_LOG_ID(APLOGNO(10109)), 
+                                     "MDMessageCmd %s failed with exit code %d.", 
+                                     mc->message_cmd, exit_code);
+            md_result_log(result, MD_LOG_ERR);
+            md_job_log_append(job, "message-error", reason, result->detail);
+            return rv;
+        }
+    }
+    md_job_log_append(job, log_msg_reason, NULL, NULL);
+    return APR_SUCCESS;
+}
+
+/**************************************************************************************************/
+/* store setup */
 
 static apr_status_t store_file_ev(void *baton, struct md_store_t *store,
                                     md_store_fs_ev_t ev, unsigned int group, 
@@ -150,6 +238,7 @@ static apr_status_t store_file_ev(void *
         switch (group) {
             case MD_SG_CHALLENGES:
             case MD_SG_STAGING:
+            case MD_SG_OCSP:
                 rv = md_make_worker_accessible(fname, p);
                 if (APR_ENOTIMPL != rv) {
                     return rv;
@@ -185,19 +274,21 @@ static apr_status_t setup_store(md_store
     
     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;
+        goto leave;
     }
 
     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))
+        || APR_SUCCESS != (rv = check_group_dir(*pstore, MD_SG_OCSP, p, s))
         ) {
         ap_log_error(APLOG_MARK, APLOG_ERR, rv, s, APLOGNO(10047) 
                      "setup challenges directory");
+        goto leave;
     }
     
-out:
+leave:
     return rv;
 }
 
@@ -245,15 +336,20 @@ static void merge_srv_config(md_t *md, m
     if (md->must_staple < 0) {
         md->must_staple = md_config_geti(md->sc, MD_CONFIG_MUST_STAPLE);
     }
+    if (md->stapling < 0) {
+        md->stapling = md_config_geti(md->sc, MD_CONFIG_STAPLING);
+    }
 }
 
-static apr_status_t check_coverage(md_t *md, const char *domain, server_rec *s, apr_pool_t *p)
+static apr_status_t check_coverage(md_t *md, const char *domain, server_rec *s, 
+                                   int *pupdates, apr_pool_t *p)
 {
     if (md_contains(md, domain, 0)) {
         return APR_SUCCESS;
     }
     else if (md->transitive) {
         APR_ARRAY_PUSH(md->domains, const char*) = apr_pstrdup(p, domain);
+        *pupdates |= MD_UPD_DOMAINS;
         return APR_SUCCESS;
     }
     else {
@@ -266,40 +362,27 @@ static apr_status_t check_coverage(md_t
     }
 }
 
-static apr_status_t md_covers_server(md_t *md, server_rec *s, apr_pool_t *p)
+static apr_status_t md_cover_server(md_t *md, server_rec *s, int *pupdates, 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) {
+    if (APR_SUCCESS == (rv = check_coverage(md, s->server_hostname, s, pupdates, p))) {
+        ap_log_error(APLOG_MARK, APLOG_TRACE1, 0, s, 
+                     "md[%s]: auto add, covers name %s", md->name, s->server_hostname);
+        for (i = 0; s->names && i < s->names->nelts; ++i) {
             name = APR_ARRAY_IDX(s->names, i, const char*);
-            if (APR_SUCCESS != (rv = check_coverage(md, name, s, p))) {
+            if (APR_SUCCESS != (rv = check_coverage(md, name, s, pupdates, p))) {
                 break;
             }
+            ap_log_error(APLOG_MARK, APLOG_TRACE1, 0, s, 
+                         "md[%s]: auto add, covers alias %s", md->name, name);
         }
     }
     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 int uses_port(server_rec *s, int port)
 {
     server_addr_rec *sa;
@@ -317,61 +400,94 @@ static int uses_port(server_rec *s, int
     return match;
 }
 
-static apr_status_t detect_supported_ports(md_mod_conf_t *mc, server_rec *s, 
-                                           apr_pool_t *p, int log_level)
+static apr_status_t detect_supported_protocols(md_mod_conf_t *mc, server_rec *s, 
+                                               apr_pool_t *p, int log_level)
 {
     ap_listen_rec *lr;
     apr_sockaddr_t *sa;
+    int can_http, can_https;
 
-    mc->can_http = 0;
-    mc->can_https = 0;
+    if (mc->can_http >= 0 && mc->can_https >= 0) goto set_and_leave;
+    
+    can_http = 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;
+                can_http = 1;
             }
             else if (sa->port == mc->local_443
                      && (!lr->protocol || !strncmp("http", lr->protocol, 4))) {
-                mc->can_https = 1;
+                can_https = 1;
             }
         }
     }
-
+    if (mc->can_http < 0) mc->can_http = can_http; 
+    if (mc->can_https < 0) mc->can_https = can_https;
     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);
+                 "server seems%s reachable via http: and%s reachable via https:",
+                 mc->can_http? "" : " not", mc->can_https? "" : " not");
+set_and_leave:
     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)
+static server_rec *get_public_https_server(md_t *md, const char *domain, server_rec *base_server)
 {
     md_srv_conf_t *sc;
     md_mod_conf_t *mc;
     server_rec *s;
     request_rec r;
+    int i;
 
     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;
+    if (!mc->can_https) return NULL;
+    /* find an ssl server matching domain from MD */
+    for (s = base_server; s; s = s->next) {
+        sc = md_config_get(s);
+        if (!sc || !sc->is_ssl || !sc->assigned) continue;
+        if (base_server == s && !mc->manage_base_server) continue;
+        if (base_server != s && mc->local_443 > 0 && !uses_port(s, mc->local_443)) continue;
+        for (i = 0; i < sc->assigned->nelts; ++i) {
+            if (md == APR_ARRAY_IDX(sc->assigned, i, md_t*)) {
+                r.server = s;
+                if (ap_matches_request_vhost(&r, domain, s->port)) {
+                    return s;
+                }
+            }
         }
     }
     return NULL;
 }
 
+static apr_status_t auto_add_domains(md_t *md, server_rec *base_server, apr_pool_t *p)
+{
+    md_srv_conf_t *sc;
+    server_rec *s;
+    apr_status_t rv = APR_SUCCESS;
+    int updates;
+    
+    /* Ad all domain names used in SSL VirtualHosts, if not already there */
+    ap_log_error(APLOG_MARK, APLOG_TRACE1, 0, base_server, 
+                 "md[%s]: auto add domains", md->name);
+    updates = 0;
+    for (s = base_server; s; s = s->next) {
+        sc = md_config_get(s);
+        if (!sc || !sc->is_ssl || !sc->assigned || sc->assigned->nelts != 1) continue;
+        if (md != APR_ARRAY_IDX(sc->assigned, 0, md_t*)) continue;
+        if (APR_SUCCESS != (rv = md_cover_server(md, s, &updates, p))) {
+            return rv;
+        }
+    }
+    return rv;
+}
+
 static void init_acme_tls_1_domains(md_t *md, server_rec *base_server)
 {
+    md_srv_conf_t *sc;
+    md_mod_conf_t *mc;
     server_rec *s;
     int i;
     const char *domain;
@@ -379,10 +495,16 @@ static void init_acme_tls_1_domains(md_t
     /* 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);
+    sc = md_config_get(base_server);
+    mc = sc->mc;    
+    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))) {
+        s = get_public_https_server(md, domain, base_server);
+        /* If we did not find a specific virtualhost for md and manage
+         * the base_server, that one is inspected */
+        if (NULL == s && mc->manage_base_server) s = base_server;
+        if (NULL == s) {
             ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, base_server, APLOGNO(10168)
                          "%s: no https server_rec found for %s", md->name, domain);
             continue;
@@ -398,15 +520,13 @@ static void init_acme_tls_1_domains(md_t
 }
 
 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)
+                                       apr_pool_t *p)
 {
-    server_rec *s, *s_https;
+    server_rec *s;
     request_rec r;
     md_srv_conf_t *sc;
-    apr_status_t rv = APR_SUCCESS;
     int i;
-    const char *domain;
-    apr_array_header_t *servers;
+    const char *domain, *uri;
     
     sc = md_config_get(base_server);
 
@@ -414,8 +534,6 @@ static apr_status_t link_md_to_servers(m
      * is an assigned MD not equal this one, the configuration is in error.
      */
     memset(&r, 0, sizeof(r));
-    servers = apr_array_make(ptemp, 5, sizeof(server_rec*));
-    
     for (s = base_server; s; s = s->next) {
         if (!mc->manage_base_server && s == base_server) {
             /* we shall not assign ourselves to the base server */
@@ -429,65 +547,15 @@ static apr_status_t link_md_to_servers(m
             if (ap_matches_request_vhost(&r, domain, s->port)) {
                 /* Create a unique md_srv_conf_t record for this server, if there is none yet */
                 sc = md_config_get_unique(s, p);
+                if (!sc->assigned) sc->assigned = apr_array_make(p, 2, sizeof(md_t*));
                 
+                APR_ARRAY_PUSH(sc->assigned, md_t*) = md;
                 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, base_server, APLOGNO(10041)
-                             "Server %s:%d matches md %s (config %s)", 
-                             s->server_hostname, s->port, md->name, sc->name);
+                             "Server %s:%d matches md %s (config %s) for domain %s, "
+                             "has now %d MDs", 
+                             s->server_hostname, s->port, md->name, sc->name,
+                             domain, (int)sc->assigned->nelts);
                 
-                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 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;
-                    }
-                }
-
-                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->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;
-            }
-        }
-        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) {
@@ -496,50 +564,14 @@ static apr_status_t link_md_to_servers(m
                                      "%s: added contact %s", md->name, uri);
                     }
                 }
+                break;
             }
-            
-            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;
+    return APR_SUCCESS;
 }
 
-static apr_status_t link_mds_to_servers(md_mod_conf_t *mc, server_rec *s, 
-                                            apr_pool_t *p, apr_pool_t *ptemp)
+static apr_status_t link_mds_to_servers(md_mod_conf_t *mc, server_rec *s, apr_pool_t *p)
 {
     int i;
     md_t *md;
@@ -548,7 +580,7 @@ static apr_status_t link_mds_to_servers(
     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))) {
+        if (APR_SUCCESS != (rv = link_md_to_servers(mc, md, s, p))) {
             goto leave;
         }
     }
@@ -562,7 +594,7 @@ static apr_status_t merge_mds_with_conf(
     md_srv_conf_t *base_conf;
     md_t *md, *omd;
     const char *domain;
-    const md_timeslice_t *ts;
+    md_timeslice_t *ts;
     apr_status_t rv = APR_SUCCESS;
     int i, j;
 
@@ -612,8 +644,6 @@ static apr_status_t merge_mds_with_conf(
             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 "
@@ -635,7 +665,7 @@ static void load_staged_data(md_mod_conf
     
     for (i = 0; i < mc->mds->nelts; ++i) {
         md = APR_ARRAY_IDX(mc->mds, i, md_t *);
-        result = md_result_md_make(p, md);
+        result = md_result_md_make(p, md->name);
         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", md->name);
@@ -647,39 +677,84 @@ static void load_staged_data(md_mod_conf
     }
 }
 
-static apr_status_t reinit_mds(md_mod_conf_t *mc, server_rec *s, apr_pool_t *p)
+static apr_status_t check_invalid_duplicates(server_rec *base_server)
 {
-    md_t *md; 
-    apr_status_t rv = APR_SUCCESS;
-    int i;
+    server_rec *s;
+    md_srv_conf_t *sc;
     
-    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(10172)
-                         "%s: error reinitiazing from store", md->name);
-            break;
+    ap_log_error( APLOG_MARK, APLOG_TRACE1, 0, base_server, 
+                 "cecking duplicate ssl assignments");
+    for (s = base_server; s; s = s->next) {
+        sc = md_config_get(s);
+        if (!sc || !sc->assigned) continue;
+        
+        if (sc->assigned->nelts > 1 && sc->is_ssl) {
+            /* duplicate assignment to SSL VirtualHost, not allowed */
+            ap_log_error(APLOG_MARK, APLOG_ERR, 0, base_server, APLOGNO(10042)
+                         "conflict: %d MDs match to SSL VirtualHost %s, there can at most be one.",
+                         (int)sc->assigned->nelts, s->server_hostname);
+            return APR_EINVAL;
+        }
+    }
+    return APR_SUCCESS;
+}
+
+static apr_status_t check_usage(md_mod_conf_t *mc, md_t *md, server_rec *base_server, 
+                                apr_pool_t *p, apr_pool_t *ptemp)
+{
+    server_rec *s;
+    md_srv_conf_t *sc;
+    apr_status_t rv = APR_SUCCESS;
+    int i, has_ssl;
+    apr_array_header_t *servers;
+
+    (void)p;
+    servers = apr_array_make(ptemp, 5, sizeof(server_rec*));
+    has_ssl = 0;
+    for (s = base_server; s; s = s->next) {
+        sc = md_config_get(s);
+        if (!sc || !sc->assigned) continue;
+        for (i = 0; i < sc->assigned->nelts; ++i) {
+            if (md == APR_ARRAY_IDX(sc->assigned, i, md_t*)) {
+                APR_ARRAY_PUSH(servers, server_rec*) = s;
+                if (sc->is_ssl) has_ssl = 1;
+            }
+        }
+    }
+
+    if (!has_ssl && md->require_https > MD_REQUIRE_OFF) {
+        /* We require https for this MD, but do we have a SSL vhost? */
+        ap_log_error(APLOG_MARK, APLOG_WARNING, 0, base_server, APLOGNO(10105)
+                     "MD %s does not match any VirtualHost with 'SSLEngine on', "
+                     "but is configured to require https. This cannot work.", md->name);
+    }
+    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;
         }
     }
     return rv;
 }
 
-static void init_watched_names(md_mod_conf_t *mc, apr_pool_t *p, apr_pool_t *ptemp, server_rec *s)
+static int init_cert_watch_status(md_mod_conf_t *mc, apr_pool_t *p, apr_pool_t *ptemp, server_rec *s)
 {
-    const md_t *md;
+    md_t *md;
     md_result_t *result;
-    int i;
+    int i, count;
     
     /* 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'
      */
+    count = 0;
     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 = APR_ARRAY_IDX(mc->mds, i, md_t*);
         md_result_set(result, APR_SUCCESS, NULL);
-
+        md->watched = 0;
         if (md->state == MD_S_ERROR) {
             md_result_set(result, APR_EGENERAL, 
                           "in error state, unable to drive forward. This "
@@ -704,8 +779,10 @@ static void init_watched_names(md_mod_co
             }
         }
         
-        APR_ARRAY_PUSH(mc->watched_names, const char *) = md->name; 
+        md->watched = 1;
+        ++count;
     }
+    return count;
 }   
 
 static apr_status_t md_post_config_before_ssl(apr_pool_t *p, apr_pool_t *plog,
@@ -755,11 +832,21 @@ static apr_status_t md_post_config_befor
         ap_log_error(APLOG_MARK, APLOG_ERR, rv, s, APLOGNO(10072) "setup md registry");
         goto leave;
     }
+    md_reg_set_notify_cb(mc->reg, notify, mc);
 
+    /* renew on 30% remaining /*/
+    rv = md_ocsp_reg_make(&mc->ocsp, p, store, mc->ocsp_renew_window,
+                          AP_SERVER_BASEVERSION, mc->proxy_url);
+    if (APR_SUCCESS != rv) {
+        ap_log_error(APLOG_MARK, APLOG_ERR, rv, s, APLOGNO(10196) "setup ocsp registry");
+        goto leave;
+    }
+    md_ocsp_set_notify_cb(mc->ocsp, notify, mc);
+    
     init_ssl();
 
     /* How to bootstrap this module:
-     * 1. find out if we know where http: and https: requests will arrive
+     * 1. find out if we know if http: and/or 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.
@@ -778,25 +865,63 @@ static apr_status_t md_post_config_befor
      * 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;
+    if (APR_SUCCESS != (rv = detect_supported_protocols(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;
+    if (APR_SUCCESS != (rv = link_mds_to_servers(mc, s, p))) goto leave;
     /*4*/
-    if (APR_SUCCESS != (rv = md_reg_sync(mc->reg, p, ptemp, mc->mds))) {
+    if (APR_SUCCESS != (rv = md_reg_sync_start(mc->reg, mc->mds, ptemp))) {
         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);
+leave:
+    return rv;
+}
+
+static apr_status_t md_post_config_after_ssl(apr_pool_t *p, apr_pool_t *plog,
+                                             apr_pool_t *ptemp, server_rec *s)
+{
+    md_srv_conf_t *sc;
+    apr_status_t rv = APR_SUCCESS;
+    md_mod_conf_t *mc;
+    int watched, i;
+    md_t *md;
+
+    (void)ptemp;
+    (void)plog;
+    sc = md_config_get(s);
+
     /*6*/
-    if (dry_run) goto leave;
+    if (!sc || !sc->mc || sc->mc->dry_run) goto leave;
+    mc = sc->mc;
+    
     /*7*/
-    if (APR_SUCCESS != (rv = reinit_mds(mc, s, p))) goto leave;
+    if (APR_SUCCESS != (rv = check_invalid_duplicates(s))) {
+        goto leave;
+    }
+    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 = auto_add_domains(md, s, p))) {
+            goto leave;
+        }
+        init_acme_tls_1_domains(md, s);
+        if (APR_SUCCESS != (rv = check_usage(mc, md, s, p, ptemp))) {
+            goto leave;
+        }
+        if (APR_SUCCESS != (rv = md_reg_sync_finish(mc->reg, md, p, ptemp))) {
+            ap_log_error( APLOG_MARK, APLOG_ERR, rv, s, APLOGNO(10172)
+                         "md[%s]: error synching to store", md->name);
+            goto leave;
+        }
+    }
     /*8*/
-    init_watched_names(mc, p, ptemp, s);
+    watched = init_cert_watch_status(mc, p, ptemp, s);
     /*9*/
     md_reg_cleanup_challenges(mc->reg, p, ptemp, mc->mds);
     
@@ -804,18 +929,23 @@ static apr_status_t md_post_config_befor
      * and only staging/challenges may be manipulated */
     md_reg_freeze_domains(mc->reg, mc->mds);
     
-    if (mc->watched_names->nelts > 0) {
+    if (watched) {
         /*10*/
         ap_log_error(APLOG_MARK, APLOG_DEBUG, rv, s, APLOGNO(10074)
-                     "%d out of %d mds need watching", 
-                     mc->watched_names->nelts, mc->mds->nelts);
+                     "%d out of %d mds need watching", watched, mc->mds->nelts);
     
         md_http_use_implementation(md_curl_get_impl(p));
-        rv = md_start_watching(mc, s, p);
+        rv = md_renew_start_watching(mc, s, p);
     }
     else {
-        ap_log_error( APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(10075) "no mds to drive");
+        ap_log_error( APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(10075) "no mds to supervise");
     }
+
+    if (!mc->ocsp || md_ocsp_count(mc->ocsp) == 0) goto leave;
+    
+    md_http_use_implementation(md_curl_get_impl(p));
+    rv = md_ocsp_start_watching(mc, s, p);
+    
 leave:
     return rv;
 }
@@ -877,20 +1007,6 @@ static int md_protocol_switch(conn_rec *
 /**************************************************************************************************/
 /* Access API to other httpd components */
 
-static int md_is_managed(server_rec *s)
-{
-    md_srv_conf_t *conf = md_config_get(s);
-
-    if (conf && conf->assigned) {
-        ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(10076) 
-                     "%s: manages server %s", conf->assigned->name, s->server_hostname);
-        return 1;
-    }
-    ap_log_error(APLOG_MARK, APLOG_TRACE1, 0, s,  
-                 "server %s is not managed", s->server_hostname);
-    return 0;
-}
-
 static apr_status_t setup_fallback_cert(md_store_t *store, const md_t *md, 
                                         server_rec *s, apr_pool_t *p)
 {
@@ -938,23 +1054,27 @@ static apr_status_t get_certificate(serv
         return APR_ENOENT;
     }
     
+    assert(sc->mc);
+    reg = sc->mc->reg;
+    assert(reg);
+
+    sc->is_ssl = 1;
+
     if (!sc->assigned) {
         /* With the new hooks in mod_ssl, we are invoked for all server_rec. It is
          * therefore normal, when we have nothing to add here. */
         return APR_ENOENT;
     }
-    
-    assert(sc->mc);
-    reg = sc->mc->reg;
-    assert(reg);
-    
-    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 "
-                     "find MD '%s'.", sc->assigned->name);
-        return APR_ENOENT;
+    else if (sc->assigned->nelts != 1) {
+        if (!fallback) {
+            ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, APLOGNO(10207)
+                         "conflict: %d MDs match Virtualhost %s which uses SSL, however "
+                         "there can be at most 1.",
+                         (int)sc->assigned->nelts, s->server_hostname);
+        }
+        return APR_EINVAL;
     }
+    md = APR_ARRAY_IDX(sc->assigned, 0, const md_t*);
     
     rv = md_reg_get_cred_files(pkeyfile, pcertfile, reg, MD_SG_DOMAINS, md, p);
     if (APR_STATUS_IS_ENOENT(rv)) {
@@ -990,12 +1110,6 @@ static apr_status_t get_certificate(serv
     return rv;
 }
 
-static apr_status_t md_get_certificate(server_rec *s, apr_pool_t *p,
-                                       const char **pkeyfile, const char **pcertfile)
-{
-    return get_certificate(s, p, 1, pcertfile, pkeyfile);
-}
-
 static int md_add_cert_files(server_rec *s, apr_pool_t *p,
                              apr_array_header_t *cert_files, 
                              apr_array_header_t *key_files)
@@ -1176,51 +1290,62 @@ static int md_require_https_maybe(reques
 {
     const md_srv_conf_t *sc;
     apr_uri_t uri;
-    const char *s;
+    const char *s, *host;
+    const md_t *md;
     int status;
     
-    if (opt_ssl_is_https && r->parsed_uri.path
-        && strncmp(WELL_KNOWN_PREFIX, r->parsed_uri.path, sizeof(WELL_KNOWN_PREFIX)-1)) {
+    /* Requests outside the /.well-known path are subject to possible
+     * https: redirects or HSTS header additions.
+     */
+    sc = ap_get_module_config(r->server->module_config, &md_module);
+    if (!sc || !sc->assigned || !sc->assigned->nelts 
+        || !opt_ssl_is_https || !r->parsed_uri.path
+        || !strncmp(WELL_KNOWN_PREFIX, r->parsed_uri.path, sizeof(WELL_KNOWN_PREFIX)-1)) {
+        goto declined;
+    }
         
-        sc = ap_get_module_config(r->server->module_config, &md_module);
-        if (sc && sc->assigned && sc->assigned->require_https > MD_REQUIRE_OFF) {
-            if (opt_ssl_is_https(r->connection)) {
-                /* Using https:
-                 * if 'permanent' and no one else set a HSTS header already, do it */
-                if (sc->assigned->require_https == MD_REQUIRE_PERMANENT 
-                    && sc->mc->hsts_header && !apr_table_get(r->headers_out, MD_HSTS_HEADER)) {
-                    apr_table_setn(r->headers_out, MD_HSTS_HEADER, sc->mc->hsts_header);
-                }
+    host = ap_get_server_name_for_url(r);
+    md = md_get_for_domain(r->server, host);
+    if (!md) goto declined;
+    
+    if (opt_ssl_is_https(r->connection)) {
+        /* Using https:
+         * if 'permanent' and no one else set a HSTS header already, do it */
+        if (md->require_https == MD_REQUIRE_PERMANENT 
+            && sc->mc->hsts_header && !apr_table_get(r->headers_out, MD_HSTS_HEADER)) {
+            apr_table_setn(r->headers_out, MD_HSTS_HEADER, sc->mc->hsts_header);
+        }
+    }
+    else {
+        if (md->require_https > MD_REQUIRE_OFF) {
+            /* Not using https:, but require it. Redirect. */
+            if (r->method_number == M_GET) {
+                /* safe to use the old-fashioned codes */
+                status = ((MD_REQUIRE_PERMANENT == md->require_https)? 
+                          HTTP_MOVED_PERMANENTLY : HTTP_MOVED_TEMPORARILY);
             }
             else {
-                /* Not using https:, but require it. Redirect. */
-                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;
-                    }
+                /* these should keep the method unchanged on retry */
+                status = ((MD_REQUIRE_PERMANENT == md->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;
                 }
             }
         }
     }
+declined:
     return DECLINED;
 }
 
@@ -1248,6 +1373,7 @@ static void md_hooks(apr_pool_t *pool)
      * Run again after mod_ssl is done.
      */
     ap_hook_post_config(md_post_config_before_ssl, NULL, mod_ssl, APR_HOOK_MIDDLE);
+    ap_hook_post_config(md_post_config_after_ssl, mod_ssl, NULL, APR_HOOK_MIDDLE);
     
     /* Run once after a child process has been created.
      */
@@ -1263,22 +1389,18 @@ static void md_hooks(apr_pool_t *pool)
 
     /* 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);
+    APR_OPTIONAL_HOOK(ap, status_hook, md_domains_status_hook, NULL, NULL, APR_HOOK_MIDDLE);
+    APR_OPTIONAL_HOOK(ap, status_hook, md_ocsp_status_hook, NULL, NULL, APR_HOOK_MIDDLE);
     ap_hook_handler(md_status_handler, NULL, NULL, APR_HOOK_MIDDLE);
 
-#ifdef SSL_CERT_HOOKS
-    (void)md_is_managed;
-    (void)md_get_certificate;
+
+#ifndef SSL_CERT_HOOKS
+#error "This version of mod_md requires Apache httpd 2.4.41 or newer."
+#endif
     APR_OPTIONAL_HOOK(ssl, add_cert_files, md_add_cert_files, NULL, NULL, APR_HOOK_MIDDLE);
     APR_OPTIONAL_HOOK(ssl, add_fallback_cert_files, md_add_fallback_cert_files, NULL, NULL, APR_HOOK_MIDDLE);
     APR_OPTIONAL_HOOK(ssl, answer_challenge, md_answer_challenge, NULL, NULL, APR_HOOK_MIDDLE);
-#else
-    (void)md_add_cert_files;
-    (void)md_add_fallback_cert_files;
-    (void)md_answer_challenge;
-    APR_REGISTER_OPTIONAL_FN(md_is_challenge);
-    APR_REGISTER_OPTIONAL_FN(md_is_managed);
-    APR_REGISTER_OPTIONAL_FN(md_get_certificate);
-#endif
+    APR_OPTIONAL_HOOK(ssl, init_stapling_status, md_ocsp_init_stapling_status, NULL, NULL, APR_HOOK_MIDDLE);
+    APR_OPTIONAL_HOOK(ssl, get_stapling_status, md_ocsp_get_stapling_status, NULL, NULL, APR_HOOK_MIDDLE);
 }
 

Modified: httpd/httpd/branches/2.4.x/modules/md/mod_md.dsp
URL: http://svn.apache.org/viewvc/httpd/httpd/branches/2.4.x/modules/md/mod_md.dsp?rev=1868930&r1=1868929&r2=1868930&view=diff
==============================================================================
--- httpd/httpd/branches/2.4.x/modules/md/mod_md.dsp (original)
+++ httpd/httpd/branches/2.4.x/modules/md/mod_md.dsp Fri Oct 25 13:27:12 2019
@@ -113,6 +113,10 @@ SOURCE=./mod_md_drive.c
 # End Source File
 # Begin Source File
 
+SOURCE=./mod_md_ocsp.c
+# End Source File
+# Begin Source File
+
 SOURCE=./mod_md_os.c
 # End Source File
 # Begin Source File
@@ -177,6 +181,10 @@ SOURCE=./md_log.c
 # End Source File
 # Begin Source File
 
+SOURCE=./md_ocsp.c
+# End Source File
+# Begin Source File
+
 SOURCE=./md_reg.c
 # End Source File
 # Begin Source File

Modified: httpd/httpd/branches/2.4.x/modules/md/mod_md_config.c
URL: http://svn.apache.org/viewvc/httpd/httpd/branches/2.4.x/modules/md/mod_md_config.c?rev=1868930&r1=1868929&r2=1868930&view=diff
==============================================================================
--- httpd/httpd/branches/2.4.x/modules/md/mod_md_config.c (original)
+++ httpd/httpd/branches/2.4.x/modules/md/mod_md_config.c Fri Oct 25 13:27:12 2019
@@ -41,25 +41,35 @@
 #define MD_DEFAULT_BASE_DIR "md"
 #endif
 
+static md_timeslice_t def_ocsp_keep_window = {
+    0,
+    MD_TIME_OCSP_KEEP_NORM,
+};
+
+static md_timeslice_t def_ocsp_renew_window = {
+    MD_TIME_LIFE_NORM,
+    MD_TIME_RENEW_WINDOW_DEF,
+};
+
 /* Default settings for the global conf */
 static md_mod_conf_t defmc = {
     NULL,                      /* list of mds */
 #if AP_MODULE_MAGIC_AT_LEAST(20180906, 2)
-    NULL,                      /* base dir by default state-dir-relative */
+    NULL,                      /* base dirm by default state-dir-relative */
 #else
     MD_DEFAULT_BASE_DIR,
 #endif
     NULL,                      /* proxy url for outgoing http */
-    NULL,                      /* md_reg */
+    NULL,                      /* md_reg_t */
+    NULL,                      /* md_ocsp_reg_t */
     80,                        /* local http: port */
     443,                       /* local https: port */
-    0,                         /* can http: */
-    0,                         /* can https: */
+    -1,                        /* can http: */
+    -1,                        /* can https: */
     0,                         /* manage base server */
     MD_HSTS_MAX_AGE_DEFAULT,   /* hsts max-age */
     NULL,                      /* hsts headers */
     NULL,                      /* unused names */
-    NULL,                      /* watched names */
     NULL,                      /* init errors hash */
     NULL,                      /* notify cmd */
     NULL,                      /* message cmd */
@@ -67,6 +77,10 @@ static md_mod_conf_t defmc = {
     0,                         /* dry_run flag */
     1,                         /* server_status_enabled */
     1,                         /* certificate_status_enabled */
+    &def_ocsp_keep_window,     /* default time to keep ocsp responses */
+    &def_ocsp_renew_window,    /* default time to renew ocsp responses */
+    "crt.sh",                  /* default cert checker site name */
+    "https://crt.sh?q=",       /* default cert checker site url */
 };
 
 static md_timeslice_t def_renew_window = {
@@ -94,8 +108,11 @@ static md_srv_conf_t defconf = {
     "ACME",                    /* ca protocol */
     NULL,                      /* ca agreemnent */
     NULL,                      /* ca challenges array */
+    0,                         /* stapling */
+    1,                         /* staple others */
     NULL,                      /* currently defined md */
     NULL,                      /* assigned md, post config */
+    0,                         /* is_ssl, set during mod_ssl post_config */
 };
 
 static md_mod_conf_t *mod_md_config;
@@ -118,7 +135,6 @@ static md_mod_conf_t *md_mod_conf_get(ap
         memcpy(mod_md_config, &defmc, sizeof(*mod_md_config));
         mod_md_config->mds = apr_array_make(pool, 5, sizeof(const md_t *));
         mod_md_config->unused_names = apr_array_make(pool, 5, sizeof(const md_t *));
-        mod_md_config->watched_names = apr_array_make(pool, 5, sizeof(const md_t *));
         mod_md_config->env = apr_table_make(pool, 10);
         mod_md_config->init_errors = apr_hash_make(pool);
          
@@ -143,6 +159,8 @@ static void srv_conf_props_clear(md_srv_
     sc->ca_proto = NULL;
     sc->ca_agreement = NULL;
     sc->ca_challenges = NULL;
+    sc->stapling = DEF_VAL;
+    sc->staple_others = DEF_VAL;
 }
 
 static void srv_conf_props_copy(md_srv_conf_t *to, const md_srv_conf_t *from)
@@ -158,6 +176,8 @@ static void srv_conf_props_copy(md_srv_c
     to->ca_proto = from->ca_proto;
     to->ca_agreement = from->ca_agreement;
     to->ca_challenges = from->ca_challenges;
+    to->stapling = from->stapling;
+    to->staple_others = from->staple_others;
 }
 
 static void srv_conf_props_apply(md_t *md, const md_srv_conf_t *from, apr_pool_t *p)
@@ -173,6 +193,7 @@ static void srv_conf_props_apply(md_t *m
     if (from->ca_proto) md->ca_proto = from->ca_proto;
     if (from->ca_agreement) md->ca_agreement = from->ca_agreement;
     if (from->ca_challenges) md->ca_challenges = apr_array_copy(p, from->ca_challenges);
+    if (from->stapling != DEF_VAL) md->stapling = from->stapling;
 }
 
 void *md_config_create_svr(apr_pool_t *pool, server_rec *s)
@@ -198,7 +219,6 @@ static void *md_config_merge(apr_pool_t
     nsc = (md_srv_conf_t *)apr_pcalloc(pool, sizeof(md_srv_conf_t));
     nsc->name = name;
     nsc->mc = add->mc? add->mc : base->mc;
-    nsc->assigned = add->assigned? add->assigned : base->assigned;
 
     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;
@@ -213,8 +233,9 @@ static void *md_config_merge(apr_pool_t
     nsc->ca_agreement = add->ca_agreement? add->ca_agreement : base->ca_agreement;
     nsc->ca_challenges = (add->ca_challenges? apr_array_copy(pool, add->ca_challenges) 
                     : (base->ca_challenges? apr_array_copy(pool, base->ca_challenges) : NULL));
+    nsc->stapling = (add->stapling != DEF_VAL)? add->stapling : base->stapling;
+    nsc->staple_others = (add->staple_others != DEF_VAL)? add->staple_others : base->staple_others;
     nsc->current = NULL;
-    nsc->assigned = NULL;
     
     return nsc;
 }
@@ -487,6 +508,30 @@ static const char *md_config_set_must_st
     return set_on_off(&config->must_staple, value, cmd->pool);
 }
 
+static const char *md_config_set_stapling(cmd_parms *cmd, void *dc, const char *value)
+{
+    md_srv_conf_t *config = md_config_get(cmd->server);
+    const char *err;
+
+    (void)dc;
+    if (!inside_md_section(cmd) && (err = ap_check_cmd_context(cmd, GLOBAL_ONLY))) {
+        return err;
+    }
+    return set_on_off(&config->stapling, value, cmd->pool);
+}
+
+static const char *md_config_set_staple_others(cmd_parms *cmd, void *dc, const char *value)
+{
+    md_srv_conf_t *config = md_config_get(cmd->server);
+    const char *err;
+
+    (void)dc;
+    if ((err = ap_check_cmd_context(cmd, GLOBAL_ONLY))) {
+        return err;
+    }
+    return set_on_off(&config->staple_others, value, cmd->pool);
+}
+
 static const char *md_config_set_base_server(cmd_parms *cmd, void *dc, const char *value)
 {
     md_srv_conf_t *config = md_config_get(cmd->server);
@@ -820,6 +865,69 @@ static const char *md_config_set_certifi
     return set_on_off(&sc->mc->certificate_status_enabled, value, cmd->pool);
 }
 
+static const char *md_config_set_ocsp_keep_window(cmd_parms *cmd, void *dc, const char *value)
+{
+    md_srv_conf_t *sc = md_config_get(cmd->server);
+    const char *err;
+
+    (void)dc;
+    if (!inside_md_section(cmd) && (err = ap_check_cmd_context(cmd, GLOBAL_ONLY))) {
+        return err;
+    }
+    err = md_timeslice_parse(&sc->mc->ocsp_keep_window, cmd->pool, value, MD_TIME_OCSP_KEEP_NORM);
+    if (err) return apr_psprintf(cmd->pool, "MDStaplingKeepResponse %s", err);
+    return NULL;
+}
+
+static const char *md_config_set_ocsp_renew_window(cmd_parms *cmd, void *dc, const char *value)
+{
+    md_srv_conf_t *sc = md_config_get(cmd->server);
+    const char *err;
+
+    (void)dc;
+    if (!inside_md_section(cmd) && (err = ap_check_cmd_context(cmd, GLOBAL_ONLY))) {
+        return err;
+    }
+    err = md_timeslice_parse(&sc->mc->ocsp_renew_window, cmd->pool, value, MD_TIME_LIFE_NORM);
+    if (!err && sc->mc->ocsp_renew_window->norm 
+        && (sc->mc->ocsp_renew_window->len >= sc->mc->ocsp_renew_window->norm)) {
+        err = "with a length of 100% or more is not allowed.";
+    }
+    if (err) return apr_psprintf(cmd->pool, "MDStaplingRenewWindow %s", err);
+    return NULL;
+}
+
+static const char *md_config_set_cert_check(cmd_parms *cmd, void *dc, 
+                                            const char *name, const char *url)
+{
+    md_srv_conf_t *sc = md_config_get(cmd->server);
+    const char *err;
+
+    (void)dc;
+    if (!inside_md_section(cmd) && (err = ap_check_cmd_context(cmd, GLOBAL_ONLY))) {
+        return err;
+    }
+    sc->mc->cert_check_name = name;
+    sc->mc->cert_check_url = url;
+    return NULL;
+}
+
+static const char *md_config_set_activation_delay(cmd_parms *cmd, void *mconfig, const char *arg)
+{
+    md_srv_conf_t *sc = md_config_get(cmd->server);
+    const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
+    apr_interval_time_t delay;
+
+    (void)mconfig;
+    if (err) {
+        return err;
+    }
+    if (md_duration_parse(&delay, arg, "d") != APR_SUCCESS) {
+        return "unrecognized duration format";
+    }
+    apr_table_set(sc->mc->env, MD_KEY_ACTIVATION_DELAY, md_duration_format(cmd->pool, delay));
+    return NULL;
+}
 
 const command_rec md_cmds[] = {
     AP_INIT_TAKE1("MDCertificateAuthority", md_config_set_ca, NULL, RSRC_CONF, 
@@ -860,7 +968,7 @@ const command_rec md_cmds[] = {
     AP_INIT_TAKE1("MDStoreDir", md_config_set_store_dir, NULL, RSRC_CONF, 
                   "the directory for file system storage of managed domain data."),
     AP_INIT_TAKE1("MDRenewWindow", md_config_set_renew_window, NULL, RSRC_CONF, 
-                  "Time length for renewal before certificate expires (defaults to days)"),
+                  "Time length for renewal before certificate expires (defaults to days)."),
     AP_INIT_TAKE1("MDRequireHttps", md_config_set_require_https, NULL, RSRC_CONF, 
                   "Redirect non-secure requests to the https: equivalent."),
     AP_INIT_RAW_ARGS("MDNotifyCmd", md_config_set_notify_cmd, NULL, RSRC_CONF, 
@@ -881,6 +989,18 @@ const command_rec md_cmds[] = {
                   "When less time remains for a certificate, send our/log a warning (defaults to days)"),
     AP_INIT_RAW_ARGS("MDMessageCmd", md_config_set_msg_cmd, NULL, RSRC_CONF, 
                   "Set the command run when a message about a domain is issued."),
+    AP_INIT_TAKE1("MDStapling", md_config_set_stapling, NULL, RSRC_CONF, 
+                  "Enable/Disable OCSP Stapling for this/all Managed Domain(s)."),
+    AP_INIT_TAKE1("MDStapleOthers", md_config_set_staple_others, NULL, RSRC_CONF, 
+                  "Enable/Disable OCSP Stapling for certificates not in Managed Domains."),
+    AP_INIT_TAKE1("MDStaplingKeepResponse", md_config_set_ocsp_keep_window, NULL, RSRC_CONF, 
+                  "The amount of time to keep an OCSP response in the store."),
+    AP_INIT_TAKE1("MDStaplingRenewWindow", md_config_set_ocsp_renew_window, NULL, RSRC_CONF, 
+                  "Time length for renewal before OCSP responses expire (defaults to days)."),
+    AP_INIT_TAKE2("MDCertificateCheck", md_config_set_cert_check, NULL, RSRC_CONF, 
+                  "Set name and URL pattern for a certificate monitoring site."),
+    AP_INIT_TAKE1("MDActivationDelay", md_config_set_activation_delay, NULL, RSRC_CONF, 
+                  "How long to delay activation of new certificates"),
 
     AP_INIT_TAKE1(NULL, NULL, NULL, RSRC_CONF, NULL)
 };
@@ -897,7 +1017,7 @@ apr_status_t md_config_post_config(serve
     if (mc->hsts_max_age > 0) {
         mc->hsts_header = apr_psprintf(p, "max-age=%d", mc->hsts_max_age);
     }
-    
+
 #if AP_MODULE_MAGIC_AT_LEAST(20180906, 2)
     if (mc->base_dir == NULL) {
         mc->base_dir = ap_state_dir_relative(p, MD_DEFAULT_BASE_DIR);
@@ -913,6 +1033,7 @@ static md_srv_conf_t *config_get_int(ser
     ap_assert(sc);
     if (sc->s != s && p) {
         sc = md_config_merge(p, &defconf, sc);
+        sc->s = s;
         sc->name = apr_pstrcat(p, CONF_S_NAME(s), sc->name, NULL);
         sc->mc = md_mod_conf_get(p, 1);
         ap_set_module_config(s->module_config, &md_module, sc);
@@ -961,22 +1082,22 @@ int md_config_geti(const md_srv_conf_t *
     switch (var) {
         case MD_CONFIG_DRIVE_MODE:
             return (sc->renew_mode != DEF_VAL)? sc->renew_mode : defconf.renew_mode;
-        case MD_CONFIG_LOCAL_80:
-            return sc->mc->local_80;
-        case MD_CONFIG_LOCAL_443:
-            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;
+        case MD_CONFIG_STAPLING:
+            return (sc->stapling != DEF_VAL)? sc->stapling : defconf.stapling;
+        case MD_CONFIG_STAPLE_OTHERS:
+            return (sc->staple_others != DEF_VAL)? sc->staple_others : defconf.staple_others;
         default:
             return 0;
     }
 }
 
-void md_config_get_timespan(const md_timeslice_t **pspan, const md_srv_conf_t *sc, md_config_var_t var)
+void md_config_get_timespan(md_timeslice_t **pspan, const md_srv_conf_t *sc, md_config_var_t var)
 {
     switch (var) {
         case MD_CONFIG_RENEW_WINDOW:
@@ -990,3 +1111,19 @@ void md_config_get_timespan(const md_tim
     }
 }
 
+const md_t *md_get_for_domain(server_rec *s, const char *domain)
+{
+    md_srv_conf_t *sc;
+    const md_t *md;
+    int i;
+    
+    sc = md_config_get(s);
+    for (i = 0; sc && sc->assigned && i < sc->assigned->nelts; ++i) {
+        md = APR_ARRAY_IDX(sc->assigned, i, const md_t*);
+        if (md_contains(md, domain, 0)) goto leave;
+    }
+    md = NULL;
+leave:
+    return md;
+}
+

Modified: httpd/httpd/branches/2.4.x/modules/md/mod_md_config.h
URL: http://svn.apache.org/viewvc/httpd/httpd/branches/2.4.x/modules/md/mod_md_config.h?rev=1868930&r1=1868929&r2=1868930&view=diff
==============================================================================
--- httpd/httpd/branches/2.4.x/modules/md/mod_md_config.h (original)
+++ httpd/httpd/branches/2.4.x/modules/md/mod_md_config.h Fri Oct 25 13:27:12 2019
@@ -20,6 +20,7 @@
 struct apr_hash_t;
 struct md_store_t;
 struct md_reg_t;
+struct md_ocsp_reg_t;
 struct md_pkey_spec_t;
 
 typedef enum {
@@ -28,8 +29,6 @@ typedef enum {
     MD_CONFIG_BASE_DIR,
     MD_CONFIG_CA_AGREEMENT,
     MD_CONFIG_DRIVE_MODE,
-    MD_CONFIG_LOCAL_80,
-    MD_CONFIG_LOCAL_443,
     MD_CONFIG_RENEW_WINDOW,
     MD_CONFIG_WARN_WINDOW,
     MD_CONFIG_TRANSITIVE,
@@ -38,6 +37,8 @@ typedef enum {
     MD_CONFIG_MUST_STAPLE,
     MD_CONFIG_NOTIFY_CMD,
     MD_CONFIG_MESSGE_CMD,
+    MD_CONFIG_STAPLING,
+    MD_CONFIG_STAPLE_OTHERS,
 } md_config_var_t;
 
 typedef struct md_mod_conf_t md_mod_conf_t;
@@ -45,7 +46,8 @@ struct md_mod_conf_t {
     apr_array_header_t *mds;           /* all md_t* defined in the config, shared */
     const char *base_dir;              /* base dir for store */
     const char *proxy_url;             /* proxy url to use (or NULL) */
-    struct md_reg_t *reg;              /* md registry instance, singleton, shared */
+    struct md_reg_t *reg;              /* md registry instance */
+    struct md_ocsp_reg_t *ocsp;        /* ocsp status registry */
 
     int local_80;                      /* On which port http:80 arrives */
     int local_443;                     /* On which port https:443 arrives */
@@ -55,7 +57,6 @@ struct md_mod_conf_t {
     int hsts_max_age;                  /* max-age of HSTS (rfc6797) header */
     const char *hsts_header;           /* computed HTST header to use or NULL */
     apr_array_header_t *unused_names;  /* post config, names of all MDs not assigned to a vhost */
-    apr_array_header_t *watched_names; /* post config, names of all MDs that we need to watch */
     struct apr_hash_t *init_errors;    /* init errors reported with MD name as key */
 
     const char *notify_cmd;            /* notification command to execute on signup/renew */
@@ -64,6 +65,10 @@ struct md_mod_conf_t {
     int dry_run;                       /* != 0 iff config dry run */
     int server_status_enabled;         /* if module should add to server-status handler */
     int certificate_status_enabled;    /* if module should expose /.httpd/certificate-status */
+    md_timeslice_t *ocsp_keep_window;  /* time that we keep ocsp responses around */
+    md_timeslice_t *ocsp_renew_window; /* time before exp. that we start renewing ocsp resp. */
+    const char *cert_check_name;       /* name of the linked certificate check site */
+    const char *cert_check_url;        /* url "template for" checking a certificate */
 };
 
 typedef struct md_srv_conf_t {
@@ -76,16 +81,20 @@ typedef struct md_srv_conf_t {
     int renew_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 */
-    const md_timeslice_t *renew_window; /* time before expiration that starts renewal */
-    const md_timeslice_t *warn_window;  /* time before expiration that warning are sent out */
+    md_timeslice_t *renew_window; /* time before expiration that starts renewal */
+    md_timeslice_t *warn_window;  /* time before expiration that warning are sent out */
     
     const char *ca_url;                /* url of CA certificate service */
     const char *ca_proto;              /* protocol used vs CA (e.g. ACME) */
     const char *ca_agreement;          /* accepted agreement uri between CA and user */ 
     struct apr_array_header_t *ca_challenges; /* challenge types configured */
+    
+    int stapling;                      /* OCSP stapling enabled */
+    int staple_others;                 /* Provide OCSP stapling for non-MD certificates */
 
     md_t *current;                     /* md currently defined in <MDomainSet xxx> section */
-    md_t *assigned;                    /* post_config: MD that applies to this server or NULL */
+    struct apr_array_header_t *assigned; /* post_config: MDs that apply to this server */
+    int is_ssl;                        /* SSLEngine is enabled here */
 } md_srv_conf_t;
 
 void *md_config_create_svr(apr_pool_t *pool, server_rec *s);
@@ -106,7 +115,8 @@ md_srv_conf_t *md_config_get_unique(serv
 const char *md_config_gets(const md_srv_conf_t *config, md_config_var_t var);
 int md_config_geti(const md_srv_conf_t *config, md_config_var_t var);
 
-void md_config_get_timespan(const md_timeslice_t **pspan, const md_srv_conf_t *sc, md_config_var_t var);
+void md_config_get_timespan(md_timeslice_t **pspan, const md_srv_conf_t *sc, md_config_var_t var);
 
+const md_t *md_get_for_domain(server_rec *s, const char *domain);
 
 #endif /* md_config_h */