From: Stefan Eissing Date: Wed, 9 Aug 2017 13:43:26 +0000 (+0000) Subject: mod_md: v0.6.1 from github X-Git-Tag: 2.5.0-alpha~234^2 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=refs%2Fremotes%2Ftrunk-md;p=apache mod_md: v0.6.1 from github git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/branches/trunk-md@1804529 13f79535-47bb-0310-9956-ffa450edef68 --- 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("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;