]> granicus.if.org Git - apache/commitdiff
mod_md: v0.6.1 from github trunk-md trunk-md
authorStefan Eissing <icing@apache.org>
Wed, 9 Aug 2017 13:43:26 +0000 (13:43 +0000)
committerStefan Eissing <icing@apache.org>
Wed, 9 Aug 2017 13:43:26 +0000 (13:43 +0000)
git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/branches/trunk-md@1804529 13f79535-47bb-0310-9956-ffa450edef68

modules/md/md.h
modules/md/md_acme_drive.c
modules/md/md_core.c
modules/md/md_reg.c
modules/md/md_store.c
modules/md/md_store.h
modules/md/md_store_fs.c
modules/md/md_version.h
modules/md/mod_md.c
modules/md/mod_md_config.c
modules/md/mod_md_config.h

index 35bab07fdc2954708aa963b9e14798c0fc65c590..7f543a09e6b5676672b2749b848f8b73bc5dd814 100644 (file)
@@ -69,6 +69,7 @@ struct md_t {
     apr_interval_time_t renew_window;/* time before expiration that starts renewal */
     
     struct apr_array_header_t *domains; /* all DNS names this MD includes */
+    int transitive;                 /* != 0 iff VirtualHost names/aliases are auto-added */
     md_drive_mode_t drive_mode;     /* mode of obtaining credentials */
     int must_staple;                /* certificates should set the OCSP Must Staple extension */
     
@@ -116,6 +117,7 @@ struct md_t {
 #define MD_KEY_STATUS           "status"
 #define MD_KEY_STORE            "store"
 #define MD_KEY_TOKEN            "token"
+#define MD_KEY_TRANSITIVE       "transitive"
 #define MD_KEY_TYPE             "type"
 #define MD_KEY_URL              "url"
 #define MD_KEY_URI              "uri"
@@ -140,7 +142,7 @@ struct md_t {
 /**
  * Determine if the Managed Domain contains a specific domain name.
  */
-int md_contains(const md_t *md, const char *domain);
+int md_contains(const md_t *md, const char *domain, int case_sensitive);
 
 /**
  * Determine if the names of the two managed domains overlap.
@@ -150,7 +152,7 @@ int md_domains_overlap(const md_t *md1, const md_t *md2);
 /**
  * Determine if the domain names are equal.
  */
-int md_equal_domains(const md_t *md1, const md_t *md2);
+int md_equal_domains(const md_t *md1, const md_t *md2, int case_sensitive);
 
 /**
  * Determine if the domains in md1 contain all domains of md2.
@@ -184,8 +186,8 @@ md_t *md_get_by_domain(struct apr_array_header_t *mds, const char *domain);
 md_t *md_get_by_dns_overlap(struct apr_array_header_t *mds, const md_t *md);
 
 /**
- * Find the managed domain in the list that has the most overlaps in domains to the
- * given md.
+ * Find the managed domain in the list that, for the given md, 
+ * has the same name, or the most number of overlaps in domains
  */
 md_t *md_find_closest_match(apr_array_header_t *mds, const md_t *md);
 
index f72e6bd7307cc8c85c7ff843918033d08c86f9f8..533f60d0ce237b55e905b883569cdc0f2d6d4885 100644 (file)
@@ -186,7 +186,7 @@ static apr_status_t ad_setup_authz(md_proto_driver_t *d)
     /* Remove anything we no longer need */
     for (i = 0; i < ad->authz_set->authzs->nelts; ++i) {
         authz = APR_ARRAY_IDX(ad->authz_set->authzs, i, md_acme_authz_t*);
-        if (!md_contains(md, authz->domain)) {
+        if (!md_contains(md, authz->domain, 0)) {
             md_acme_authz_set_remove(ad->authz_set, authz->domain);
             changed = 1;
         }
@@ -589,7 +589,7 @@ static apr_status_t ad_chain_install(md_proto_driver_t *d)
 static apr_status_t acme_driver_init(md_proto_driver_t *d)
 {
     md_acme_driver_t *ad;
-    apr_status_t rv;
+    apr_status_t rv = APR_SUCCESS;
 
     ad = apr_pcalloc(d->p, sizeof(*ad));
     
@@ -627,43 +627,6 @@ static apr_status_t acme_driver_init(md_proto_driver_t *d)
     
     md_log_perror(MD_LOG_MARK, MD_LOG_TRACE1, 0, d->p, "%s: init driver", d->md->name);
     
-    rv = md_load(d->store, MD_SG_STAGING, d->md->name, &ad->md, d->p);
-    md_log_perror(MD_LOG_MARK, MD_LOG_TRACE1, rv, d->p, "%s: checked stage md", d->md->name);
-    if (d->reset || APR_STATUS_IS_ENOENT(rv)) {
-        /* reset the staging area for this domain */
-        rv = md_store_purge(d->store, d->p, MD_SG_STAGING, d->md->name);
-        if (APR_SUCCESS != rv && !APR_STATUS_IS_ENOENT(rv)) {
-            return rv;
-        }
-        rv = APR_SUCCESS;
-    }
-    
-    if (ad->md) {
-        /* staging in progress.
-         * There are certain md properties that are updated in staging, others are only
-         * updated in the domains store. Are these still the same? If not, we better
-         * start anew.
-         */
-        if (strcmp(d->md->ca_url, ad->md->ca_url)
-            || strcmp(d->md->ca_proto, ad->md->ca_proto)) {
-            /* reject staging info in this case */
-            ad->md = NULL;
-            return APR_SUCCESS;
-        }
-        
-        if (d->md->ca_agreement 
-            && (!ad->md->ca_agreement || strcmp(d->md->ca_agreement, ad->md->ca_agreement))) {
-            ad->md->ca_agreement = d->md->ca_agreement;
-        }
-        
-        /* look for new ACME account information collected there */
-        rv = md_reg_creds_get(&ad->ncreds, d->reg, MD_SG_STAGING, d->md, d->p);
-        md_log_perror(MD_LOG_MARK, MD_LOG_DEBUG, rv, d->p, "%s: checked creds", d->md->name);
-        if (APR_STATUS_IS_ENOENT(rv)) {
-            rv = APR_SUCCESS;
-        }
-    }
-    
     return rv;
 }
 
@@ -673,6 +636,7 @@ static apr_status_t acme_driver_init(md_proto_driver_t *d)
 static apr_status_t acme_stage(md_proto_driver_t *d)
 {
     md_acme_driver_t *ad = d->baton;
+    int reset_staging = d->reset;
     apr_status_t rv = APR_SUCCESS;
     int renew = 1;
 
@@ -683,6 +647,42 @@ static apr_status_t acme_stage(md_proto_driver_t *d)
                       apr_array_pstrcat(d->p, ad->ca_challenges, ' '));
     }
 
+    if (!reset_staging) {
+        rv = md_load(d->store, MD_SG_STAGING, d->md->name, &ad->md, d->p);
+        if (APR_SUCCESS == rv) {
+            /* So, we have a copy in staging, but is it a recent or an old one? */
+            if (!md_is_newer(d->store, MD_SG_STAGING, MD_SG_DOMAINS, d->md->name, d->p)) {
+                reset_staging = 1;
+            }
+        }
+        else if (APR_STATUS_IS_ENOENT(rv)) {
+            reset_staging = 1;
+            rv = APR_SUCCESS;
+        }
+        md_log_perror(MD_LOG_MARK, MD_LOG_TRACE1, rv, d->p, 
+                      "%s: checked staging area, will%s reset",
+                      d->md->name, reset_staging? "" : " not");
+    }
+    
+    if (reset_staging) {
+        /* reset the staging area for this domain */
+        rv = md_store_purge(d->store, d->p, MD_SG_STAGING, d->md->name);
+        if (APR_SUCCESS != rv && !APR_STATUS_IS_ENOENT(rv)) {
+            return rv;
+        }
+        rv = APR_SUCCESS;
+        ad->md = NULL;
+    }
+    
+    if (ad->md) {
+        /* staging in progress. look for new ACME account information collected there */
+        rv = md_reg_creds_get(&ad->ncreds, d->reg, MD_SG_STAGING, d->md, d->p);
+        md_log_perror(MD_LOG_MARK, MD_LOG_DEBUG, rv, d->p, "%s: checked creds", d->md->name);
+        if (APR_STATUS_IS_ENOENT(rv)) {
+            rv = APR_SUCCESS;
+        }
+    }
+    
     /* Find out where we're at with this managed domain */
     if (ad->ncreds && ad->ncreds->pkey && ad->ncreds->cert && ad->ncreds->chain) {
         /* There is a full set staged, to be loaded */
index a65c639f07cfc651e31ac985f325c9f5bc08108e..0a67e85ebb5bf208aa48b1bb092dc68fff4454cf 100644 (file)
@@ -29,9 +29,9 @@
 #include "md_util.h"
 
 
-int md_contains(const md_t *md, const char *domain)
+int md_contains(const md_t *md, const char *domain, int case_sensitive)
 {
-   return md_array_str_index(md->domains, domain, 0, 0) >= 0;
+   return md_array_str_index(md->domains, domain, 0, case_sensitive) >= 0;
 }
 
 const char *md_common_name(const md_t *md1, const md_t *md2)
@@ -45,7 +45,7 @@ const char *md_common_name(const md_t *md1, const md_t *md2)
     
     for (i = 0; i < md1->domains->nelts; ++i) {
         const char *name1 = APR_ARRAY_IDX(md1->domains, i, const char*);
-        if (md_contains(md2, name1)) {
+        if (md_contains(md2, name1, 0)) {
             return name1;
         }
     }
@@ -70,7 +70,7 @@ apr_size_t md_common_name_count(const md_t *md1, const md_t *md2)
     hits = 0;
     for (i = 0; i < md1->domains->nelts; ++i) {
         const char *name1 = APR_ARRAY_IDX(md1->domains, i, const char*);
-        if (md_contains(md2, name1)) {
+        if (md_contains(md2, name1, 0)) {
             ++hits;
         }
     }
@@ -84,19 +84,20 @@ 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->transitive = -1;
         md->defn_name = "unknown";
         md->defn_line_number = 0;
     }
     return md;
 }
 
-int md_equal_domains(const md_t *md1, const md_t *md2)
+int md_equal_domains(const md_t *md1, const md_t *md2, int case_sensitive)
 {
     int i;
     if (md1->domains->nelts == md2->domains->nelts) {
         for (i = 0; i < md1->domains->nelts; ++i) {
             const char *name1 = APR_ARRAY_IDX(md1->domains, i, const char*);
-            if (!md_contains(md2, name1)) {
+            if (!md_contains(md2, name1, case_sensitive)) {
                 return 0;
             }
         }
@@ -111,7 +112,7 @@ int md_contains_domains(const md_t *md1, const md_t *md2)
     if (md1->domains->nelts >= md2->domains->nelts) {
         for (i = 0; i < md2->domains->nelts; ++i) {
             const char *name2 = APR_ARRAY_IDX(md2->domains, i, const char*);
-            if (!md_contains(md1, name2)) {
+            if (!md_contains(md1, name2, 0)) {
                 return 0;
             }
         }
@@ -169,7 +170,7 @@ md_t *md_get_by_domain(struct apr_array_header_t *mds, const char *domain)
     int i;
     for (i = 0; i < mds->nelts; ++i) {
         md_t *md = APR_ARRAY_IDX(mds, i, md_t *);
-        if (md_contains(md, domain)) {
+        if (md_contains(md, domain, 0)) {
             return md;
         }
     }
@@ -264,6 +265,7 @@ md_json_t *md_to_json(const md_t *md, apr_pool_t *p)
         md_json_sets(md->name, json, MD_KEY_NAME, NULL);
         md_json_setsa(domains, json, MD_KEY_DOMAINS, NULL);
         md_json_setsa(md->contacts, json, MD_KEY_CONTACTS, NULL);
+        md_json_setl(md->transitive, json, MD_KEY_TRANSITIVE, NULL);
         md_json_sets(md->ca_account, json, MD_KEY_CA, MD_KEY_ACCOUNT, NULL);
         md_json_sets(md->ca_proto, json, MD_KEY_CA, MD_KEY_PROTO, NULL);
         md_json_sets(md->ca_url, json, MD_KEY_CA, MD_KEY_URL, NULL);
@@ -305,6 +307,7 @@ md_t *md_from_json(md_json_t *json, apr_pool_t *p)
         md->state = (int)md_json_getl(json, MD_KEY_STATE, NULL);
         md->drive_mode = (int)md_json_getl(json, MD_KEY_DRIVE_MODE, NULL);
         md->domains = md_array_str_compact(p, md->domains, 0);
+        md->transitive = (int)md_json_getl(json, MD_KEY_TRANSITIVE, NULL);
         s = md_json_dups(p, json, MD_KEY_CERT, MD_KEY_EXPIRES, NULL);
         if (s && *s) {
             md->expires = apr_date_parse_rfc(s);
index b2953fb909a571ea9e3b4c8ba1842c747365a3a6..791565fd7d13f83477e0ad2b72f560d3313b90cc 100644 (file)
@@ -357,7 +357,7 @@ static int find_domain(void *baton, md_reg_t *reg, md_t *md)
 {
     find_domain_ctx *ctx = baton;
     
-    if (md_contains(md, ctx->domain)) {
+    if (md_contains(md, ctx->domain, 0)) {
         ctx->md = md;
         return 0;
     }
@@ -685,7 +685,7 @@ apr_status_t md_reg_sync(md_reg_t *reg, apr_pool_t *p, apr_pool_t *ptemp,
     md_log_perror(MD_LOG_MARK, MD_LOG_DEBUG, rv, p, 
                   "sync: found %d mds in store", ctx.store_mds->nelts);
     if (APR_SUCCESS == rv) {
-        int i, added, fields;
+        int i, fields;
         md_t *md, *config_md, *smd, *omd;
         const char *common;
         
@@ -696,11 +696,18 @@ apr_status_t md_reg_sync(md_reg_t *reg, apr_pool_t *p, apr_pool_t *ptemp,
             smd = md_find_closest_match(ctx.store_mds, md);
             if (smd) {
                 fields = 0;
-                /* add any newly configured domains to the store md */
-                added = md_array_str_add_missing(smd->domains, md->domains, 0);
-                if (added) {
+                
+                /* Once stored, we keep the name */
+                if (strcmp(md->name, smd->name)) {
+                    md->name = apr_pstrdup(p, smd->name);
+                }
+                
+                /* Make the stored domain list *exactly* the same, even if
+                 * someone only changed upper/lowercase, we'd like to persist that. */
+                if (!md_equal_domains(md, smd, 1)) {
                     md_log_perror(MD_LOG_MARK, MD_LOG_DEBUG, rv, p, 
-                                 "%s: %d domains added", smd->name, added);
+                                 "%s: %d domains changed", smd->name);
+                    smd->domains = md_array_str_clone(ptemp, md->domains);
                     fields |= MD_UPD_DOMAINS;
                 }
                 
@@ -712,7 +719,7 @@ apr_status_t md_reg_sync(md_reg_t *reg, apr_pool_t *p, apr_pool_t *ptemp,
                     
                     /* Is this md still configured or has it been abandoned in the config? */
                     config_md = md_get_by_name(ctx.conf_mds, omd->name);
-                    if (config_md && md_contains(config_md, common)) {
+                    if (config_md && md_contains(config_md, common, 0)) {
                         /* domain used in two configured mds, not allowed */
                         rv = APR_EINVAL;
                         md_log_perror(MD_LOG_MARK, MD_LOG_ERR, rv, p, 
index 9028845c6fc7920eec519be3a7f4b683a2361f3a..f94e959c5b15935b3eee77808dd682061b6f6919 100644 (file)
@@ -138,6 +138,12 @@ apr_status_t md_store_get_fname(const char **pfname,
     return APR_ENOTIMPL;
 }
 
+int md_store_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)
+{
+    return store->is_newer(store, group1, group2, name, aspect, p);
+}
+
 /**************************************************************************************************/
 /* convenience */
 
@@ -211,6 +217,13 @@ apr_status_t md_remove(md_store_t *store, apr_pool_t *p,
     return md_util_pool_vdo(p_remove, &ctx, p, name, force, NULL);
 }
 
+int md_is_newer(md_store_t *store, md_store_group_t group1, md_store_group_t group2,  
+                      const char *name, apr_pool_t *p)
+{
+    return md_store_is_newer(store, group1, group2, name, MD_FN_MD, p);
+}
+
+
 typedef struct {
     apr_pool_t *p;
     apr_array_header_t *mds;
index 7806504bee8c10bb25a1c727fb54b49ce28fea5c..86a8639603c28a2c733331b0f41dd542f707de41 100644 (file)
@@ -56,6 +56,10 @@ typedef apr_status_t md_store_get_fname_cb(const char **pfname,
                                            const char *name, const char *aspect, 
                                            apr_pool_t *p);
 
+typedef int md_store_is_newer_cb(md_store_t *store, 
+                                 md_store_group_t group1, md_store_group_t group2,  
+                                 const char *name, const char *aspect, apr_pool_t *p);
+
 struct md_store_t {
     md_store_destroy_cb *destroy;
 
@@ -66,6 +70,7 @@ struct md_store_t {
     md_store_iter_cb *iterate;
     md_store_purge_cb *purge;
     md_store_get_fname_cb *get_fname;
+    md_store_is_newer_cb *is_newer;
 };
 
 void md_store_destroy(md_store_t *store);
@@ -106,6 +111,9 @@ apr_status_t md_store_get_fname(const char **pfname,
                                 const char *name, const char *aspect, 
                                 apr_pool_t *p);
 
+int md_store_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);
+
 /**************************************************************************************************/
 /* Storage handling utils */
 
@@ -116,6 +124,9 @@ apr_status_t md_save(struct md_store_t *store, apr_pool_t *p, md_store_group_t g
 apr_status_t md_remove(md_store_t *store, apr_pool_t *p, md_store_group_t group, 
                      const char *name, int force);
 
+int md_is_newer(md_store_t *store, md_store_group_t group1, md_store_group_t group2,  
+                const char *name, apr_pool_t *p);
+
 typedef int md_store_md_inspect(void *baton, md_store_t *store, md_t *md, apr_pool_t *ptemp);
 
 apr_status_t md_store_md_iter(md_store_md_inspect *inspect, void *baton, md_store_t *store, 
index 09a4eb28350110751d2327423c241e063b95724b..2626fde9f70f06c8ff4da6107c2b3ebf55b5376b 100644 (file)
@@ -90,6 +90,8 @@ static apr_status_t fs_get_fname(const char **pfname,
                                  md_store_t *store, md_store_group_t group, 
                                  const char *name, const char *aspect, 
                                  apr_pool_t *p);
+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_status_t init_store_file(md_store_fs_t *s_fs, const char *fname, 
                                     apr_pool_t *p, apr_pool_t *ptemp)
@@ -129,7 +131,7 @@ static apr_status_t read_store_file(md_store_fs_t *s_fs, const char *fname,
                                     apr_pool_t *p, apr_pool_t *ptemp)
 {
     md_json_t *json;
-    const char *s, *key64;
+    const char *key64;
     apr_status_t rv;
     double store_version;
     
@@ -140,7 +142,7 @@ static apr_status_t read_store_file(md_store_fs_t *s_fs, const char *fname,
             store_version = 1.0;
         }
         if (store_version > MD_STORE_VERSION) {
-            md_log_perror(MD_LOG_MARK, MD_LOG_ERR, 0, p, "version too new: %s", s);
+            md_log_perror(MD_LOG_MARK, MD_LOG_ERR, 0, p, "version too new: %s", store_version);
             return APR_EINVAL;
         }
         else if (store_version > MD_STORE_VERSION) {
@@ -204,6 +206,7 @@ apr_status_t md_store_fs_init(md_store_t **pstore, apr_pool_t *p, const char *pa
     s_fs->s.purge = fs_purge;
     s_fs->s.iterate = fs_iterate;
     s_fs->s.get_fname = fs_get_fname;
+    s_fs->s.is_newer = fs_is_newer;
     
     /* by default, everything is only readable by the current user */ 
     s_fs->def_perms.dir = MD_FPROT_D_UONLY;
@@ -425,8 +428,48 @@ static apr_status_t mk_group_dir(const char **pdir, md_store_fs_t *s_fs,
     md_log_perror(MD_LOG_MARK, MD_LOG_TRACE3, 0, p, "mk_group_dir %d %s", group, name);
     return rv;
 }
+
+static apr_status_t pfs_is_newer(void *baton, apr_pool_t *p, apr_pool_t *ptemp, va_list ap)
+{
+    md_store_fs_t *s_fs = baton;
+    const char *fname1, *fname2, *name, *aspect;
+    md_store_group_t group1, group2;
+    apr_finfo_t inf1, inf2;
+    int *pnewer;
+    apr_status_t rv;
+    
+    group1 = va_arg(ap, int);
+    group2 = va_arg(ap, int);
+    name = va_arg(ap, const char*);
+    aspect = va_arg(ap, const char*);
+    pnewer = va_arg(ap, int*);
+    
+    *pnewer = 0;
+    if (   APR_SUCCESS == (rv = fs_get_fname(&fname1, &s_fs->s, group1, name, aspect, ptemp))
+        && APR_SUCCESS == (rv = fs_get_fname(&fname2, &s_fs->s, group2, name, aspect, ptemp))
+        && APR_SUCCESS == (rv = apr_stat(&inf1, fname1, APR_FINFO_MTIME, ptemp))
+        && APR_SUCCESS == (rv = apr_stat(&inf2, fname2, APR_FINFO_MTIME, ptemp))) {
+        *pnewer = inf1.mtime > inf2.mtime;
+    }
+
+    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)
+{
+    md_store_fs_t *s_fs = FS_STORE(store);
+    int newer = 0;
+    apr_status_t rv;
+    
+    rv = md_util_pool_vdo(pfs_is_newer, s_fs, p, group1, group2, name, aspect, &newer, NULL);
+    if (APR_SUCCESS == rv) {
+        return newer;
+    }
+    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;
index 405734c2e97b2b286b60717564df741b532c00db..7b2063837c27d4037a0ec4f6ecc9872ff1f76e6a 100644 (file)
@@ -26,7 +26,7 @@
  * @macro
  * Version number of the md module as c string
  */
-#define MOD_MD_VERSION "0.6.0"
+#define MOD_MD_VERSION "0.6.1"
 
 /**
  * @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 0x000600
+#define MOD_MD_VERSION_NUM 0x000601
 
 #define MD_EXPERIMENTAL 1
 #define MD_ACME_DEF_URL    "https://acme-staging.api.letsencrypt.org/directory"
index caee79261f05a15b8b3d5fc4f05a6cab62874f25..b5f391f8753ecd94a109224c43141cb6617658d4 100644 (file)
@@ -149,6 +149,9 @@ static apr_status_t md_calc_md_list(md_ctx *ctx, apr_pool_t *p, apr_pool_t *plog
                 if (nmd->renew_window <= 0) {
                     nmd->renew_window = md_config_get_interval(config, MD_CONFIG_RENEW_WINDOW);
                 }
+                if (nmd->transitive < 0) {
+                    nmd->transitive = md_config_geti(config, MD_CONFIG_TRANSITIVE);
+                }
                 if (!nmd->ca_challenges && config->ca_challenges) {
                     nmd->ca_challenges = apr_array_copy(p, config->ca_challenges);
                 }
@@ -161,17 +164,37 @@ static apr_status_t md_calc_md_list(md_ctx *ctx, apr_pool_t *p, apr_pool_t *plog
             }
         }
     }
+    
     ctx->mds = (APR_SUCCESS == rv)? mds : NULL;
     return rv;
 }
 
+static apr_status_t check_coverage(md_t *md, const char *domain, server_rec *s, 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);
+        return APR_SUCCESS;
+    }
+    else {
+        ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, APLOGNO()
+                     "Virtual Host %s:%d matches Managed Domain '%s', but the "
+                     "name/alias %s itself is not managed. A requested MD certificate "
+                     "will not match ServerName.",
+                     s->server_hostname, s->port, md->name, domain);
+        return APR_EINVAL;
+    }
+}
+
 static apr_status_t md_check_vhost_mapping(md_ctx *ctx, apr_pool_t *p, apr_pool_t *plog,
                                            apr_pool_t *ptemp, server_rec *base_server)
 {
     server_rec *s;
     request_rec r;
     md_config_t *config;
-    apr_status_t rv = APR_SUCCESS;
+    apr_status_t rv = APR_SUCCESS, rv2;
     md_t *md;
     int i, j, k;
     const char *domain, *name;
@@ -227,30 +250,17 @@ static apr_status_t md_check_vhost_mapping(md_ctx *ctx, apr_pool_t *p, apr_pool_
 
                     /* 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 (!md_contains(md, s->server_hostname)) {
-                        ap_log_error(APLOG_MARK, APLOG_ERR, 0, base_server, APLOGNO()
-                                     "Virtual Host %s:%d matches Managed Domain '%s', but the name"
-                                     " itself is not managed. A requested MD certificate will "
-                                     "not match ServerName.",
-                                     s->server_hostname, s->port, md->name);
-                        rv = APR_EINVAL;
-                        goto next_server;
-                    }
-                    else {
+                    if (APR_SUCCESS == (rv2 = check_coverage(md, s->server_hostname, s, p))) {
                         for (k = 0; k < s->names->nelts; ++k) {
                             name = APR_ARRAY_IDX(s->names, k, const char*);
-                            if (!md_contains(md, name)) {
-                                ap_log_error(APLOG_MARK, APLOG_ERR, 0, base_server, APLOGNO()
-                                             "Virtual Host %s:%d matches Managed Domain '%s', but "
-                                             "the ServerAlias %s is not covered by the MD. "
-                                             "A requested MD certificate will not match this " 
-                                             "alias.", s->server_hostname, s->port, md->name,
-                                             name);
-                                rv = APR_EINVAL;
-                                goto next_server;
+                            if (APR_SUCCESS != (rv2 = check_coverage(md, name, s, p))) {
+                                break;
                             }
                         }
                     }
+                    if (APR_SUCCESS != rv2) {
+                        rv = rv2;
+                    }
                     goto next_server;
                 }
             }
@@ -470,7 +480,9 @@ static apr_status_t drive_md(md_watchdog *wd, md_t *md, apr_pool_t *ptemp)
         else if (renew) {
             ap_log_error( APLOG_MARK, APLOG_DEBUG, 0, wd->s, APLOGNO() 
                          "md(%s): state=%d, driving", md->name, md->state);
+                         
             rv = md_reg_stage(wd->reg, md, NULL, 0, ptemp);
+            
             if (APR_SUCCESS == rv) {
                 md->state = MD_S_COMPLETE;
                 md->expires = 0;
index c5c3d7d7cad63c511fde4726471c655d5e6246f6..2a706831fd5464ac50fed491d3ac364998e8b542 100644 (file)
@@ -43,7 +43,8 @@ static md_config_t defconf = {
     NULL, 
     NULL,
     MD_DRIVE_AUTO,
-    apr_time_from_sec(14 * MD_SECS_PER_DAY), 
+    apr_time_from_sec(14 * MD_SECS_PER_DAY),
+    1,  
     NULL, 
     "md",
     NULL
@@ -62,6 +63,7 @@ void *md_config_create_svr(apr_pool_t *pool, server_rec *s)
     conf->drive_mode = DEF_VAL;
     conf->mds = apr_array_make(pool, 5, sizeof(const md_t *));
     conf->renew_window = DEF_VAL;
+    conf->transitive = DEF_VAL;
     
     return conf;
 }
@@ -95,6 +97,7 @@ static void *md_config_merge(apr_pool_t *pool, void *basev, void *addv)
     n->renew_window = (add->renew_window != DEF_VAL)? add->renew_window : base->renew_window;
     n->ca_challenges = (add->ca_challenges? apr_array_copy(pool, add->ca_challenges) 
                     : (base->ca_challenges? apr_array_copy(pool, base->ca_challenges) : NULL));
+    n->transitive = (add->transitive != DEF_VAL)? add->transitive : base->transitive;
     return n;
 }
 
@@ -190,21 +193,41 @@ static const char *md_config_sec_start(cmd_parms *cmd, void *mconfig, const char
     return err;
 }
 
+static const char *set_transitive(int *ptransitive, const char *value)
+{
+    if (!apr_strnatcasecmp("auto", value)) {
+        *ptransitive = 1;
+        return NULL;
+    }
+    else if (!apr_strnatcasecmp("manual", value)) {
+        *ptransitive = 0;
+        return NULL;
+    }
+    return "unknown value, use \"auto|manual\"";
+}
+
 static const char *md_config_sec_add_members(cmd_parms *cmd, void *dc, 
                                              int argc, char *const argv[])
 {
+    md_config_t *config = (md_config_t *)md_config_get(cmd->server);
     md_config_dir_t *dconfig = dc;
     apr_array_header_t *domains;
     const char *err;
     int i;
     
     if (NULL != (err = md_section_check(cmd))) {
+        if (argc == 1) {
+            /* only allowed value outside a section */
+            return set_transitive(&config->transitive, argv[0]);
+        }
         return err;
     }
     
     domains = dconfig->md->domains;
     for (i = 0; i < argc; ++i) {
-        add_domain_name(domains, argv[i], cmd->pool);
+        if (NULL != set_transitive(&dconfig->md->transitive, argv[i])) {
+            add_domain_name(domains, argv[i], cmd->pool);
+        }
     }
     return NULL;
 }
@@ -216,7 +239,7 @@ static const char *md_config_set_names(cmd_parms *cmd, void *arg,
     apr_array_header_t *domains = apr_array_make(cmd->pool, 5, sizeof(const char *));
     const char *err;
     md_t *md;
-    int i;
+    int i, transitive = -1;
 
     err = ap_check_cmd_context(cmd, NOT_IN_DIR_LOC_FILE);
     if (err) {
@@ -224,12 +247,18 @@ static const char *md_config_set_names(cmd_parms *cmd, void *arg,
     }
 
     for (i = 0; i < argc; ++i) {
-        add_domain_name(domains, argv[i], cmd->pool);
+        if (NULL != set_transitive(&transitive, argv[i])) {
+            add_domain_name(domains, argv[i], cmd->pool);
+        }
     }
     err = md_create(&md, cmd->pool, domains);
     if (err) {
         return err;
     }
+
+    if (transitive >= 0) {
+        md->transitive = transitive;
+    }
     
     if (cmd->config_file) {
         md->defn_name = cmd->config_file->name;
@@ -486,8 +515,12 @@ static const char *md_config_set_cha_tyes(cmd_parms *cmd, void *dc,
 const command_rec md_cmds[] = {
     AP_INIT_RAW_ARGS("<ManagedDomain", md_config_sec_start, NULL, RSRC_CONF, 
                       "Container for a manged domain with common settings and certificate."),
-    AP_INIT_TAKE_ARGV("MDMember", md_config_sec_add_members, NULL, OR_ALL, 
-                      "Define domain name(s) part of the Managed Domain"),
+    AP_INIT_TAKE_ARGV("MDMember", 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_TAKE_ARGV("MDMembers", 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_TAKE_ARGV("ManagedDomain", md_config_set_names, NULL, RSRC_CONF, 
                       "A group of server names with one certificate"),
     AP_INIT_TAKE1("MDCertificateAuthority", md_config_set_ca, NULL, RSRC_CONF, 
@@ -566,6 +599,8 @@ int md_config_geti(const md_config_t *config, md_config_var_t var)
             return (config->local_80 != DEF_VAL)? config->local_80 : 80;
         case MD_CONFIG_LOCAL_443:
             return (config->local_443 != DEF_VAL)? config->local_443 : 443;
+        case MD_CONFIG_TRANSITIVE:
+            return (config->transitive != DEF_VAL)? config->transitive : defconf.transitive;
         default:
             return 0;
     }
index 3568f7c6ac01836f7084f908cdc65682897e04cf..c0ec6cc6cf77b926b115fa7f0f727b8e0c6f6732 100644 (file)
@@ -27,6 +27,7 @@ typedef enum {
     MD_CONFIG_LOCAL_80,
     MD_CONFIG_LOCAL_443,
     MD_CONFIG_RENEW_WINDOW,
+    MD_CONFIG_TRANSITIVE,
 } md_config_var_t;
 
 typedef struct {
@@ -44,6 +45,7 @@ typedef struct {
     
     int drive_mode;
     apr_interval_time_t renew_window;  /* time for renewal before expiry */
+    int transitive;
     
     const md_t *md;
     const char *base_dir;