summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorStefan Eissing <icing@apache.org>2017-08-09 13:43:26 +0000
committerStefan Eissing <icing@apache.org>2017-08-09 13:43:26 +0000
commit13ee3b3ecfb9657589b05ac07e37fb5f7422fad4 (patch)
treecc09dc512c8b2951b2a56417d635865b7b1bf419
parent4ee5a66bd6a1993eb1d02a89aada556734c52bda (diff)
downloadhttpd-trunk-md.tar.gz
mod_md: v0.6.1 from githubtrunk-md
git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/branches/trunk-md@1804529 13f79535-47bb-0310-9956-ffa450edef68
-rw-r--r--modules/md/md.h10
-rw-r--r--modules/md/md_acme_drive.c78
-rw-r--r--modules/md/md_core.c19
-rw-r--r--modules/md/md_reg.c21
-rw-r--r--modules/md/md_store.c13
-rw-r--r--modules/md/md_store.h11
-rw-r--r--modules/md/md_store_fs.c49
-rw-r--r--modules/md/md_version.h4
-rw-r--r--modules/md/mod_md.c52
-rw-r--r--modules/md/mod_md_config.c47
-rw-r--r--modules/md/mod_md_config.h2
11 files changed, 217 insertions, 89 deletions
diff --git a/modules/md/md.h b/modules/md/md.h
index 35bab07fdc..7f543a09e6 100644
--- a/modules/md/md.h
+++ b/modules/md/md.h
@@ -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);
diff --git a/modules/md/md_acme_drive.c b/modules/md/md_acme_drive.c
index f72e6bd730..533f60d0ce 100644
--- a/modules/md/md_acme_drive.c
+++ b/modules/md/md_acme_drive.c
@@ -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 */
diff --git a/modules/md/md_core.c b/modules/md/md_core.c
index a65c639f07..0a67e85ebb 100644
--- a/modules/md/md_core.c
+++ b/modules/md/md_core.c
@@ -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);
diff --git a/modules/md/md_reg.c b/modules/md/md_reg.c
index b2953fb909..791565fd7d 100644
--- a/modules/md/md_reg.c
+++ b/modules/md/md_reg.c
@@ -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,
diff --git a/modules/md/md_store.c b/modules/md/md_store.c
index 9028845c6f..f94e959c5b 100644
--- a/modules/md/md_store.c
+++ b/modules/md/md_store.c
@@ -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;
diff --git a/modules/md/md_store.h b/modules/md/md_store.h
index 7806504bee..86a8639603 100644
--- a/modules/md/md_store.h
+++ b/modules/md/md_store.h
@@ -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,
diff --git a/modules/md/md_store_fs.c b/modules/md/md_store_fs.c
index 09a4eb2835..2626fde9f7 100644
--- a/modules/md/md_store_fs.c
+++ b/modules/md/md_store_fs.c
@@ -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;
diff --git a/modules/md/md_version.h b/modules/md/md_version.h
index 405734c2e9..7b2063837c 100644
--- a/modules/md/md_version.h
+++ b/modules/md/md_version.h
@@ -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"
diff --git a/modules/md/mod_md.c b/modules/md/mod_md.c
index caee79261f..b5f391f875 100644
--- a/modules/md/mod_md.c
+++ b/modules/md/mod_md.c
@@ -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;
diff --git a/modules/md/mod_md_config.c b/modules/md/mod_md_config.c
index c5c3d7d7ca..2a706831fd 100644
--- a/modules/md/mod_md_config.c
+++ b/modules/md/mod_md_config.c
@@ -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;
}
diff --git a/modules/md/mod_md_config.h b/modules/md/mod_md_config.h
index 3568f7c6ac..c0ec6cc6cf 100644
--- a/modules/md/mod_md_config.h
+++ b/modules/md/mod_md_config.h
@@ -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;