From: Stefan Eissing Date: Mon, 22 Mar 2021 15:09:05 +0000 (+0000) Subject: mod_md: X-Git-Tag: 2.5.0-alpha2-ci-test-only~986 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=cd1ff445fbb0857f84664b688cebde3e753a0280;p=thirdparty%2Fapache%2Fhttpd.git mod_md: - MDCertificateFile and MDCertificateKeyFile can now be specified several times to add multiple, static certificates to a MDomain. git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@1887923 13f79535-47bb-0310-9956-ffa450edef68 --- diff --git a/CHANGES b/CHANGES index 52933fcfd46..076a8c711b4 100644 --- a/CHANGES +++ b/CHANGES @@ -60,6 +60,8 @@ Changes with Apache 2.5.1 - Account Update transactions to V2 CAs now use the correct POST-AS-GET method. Previously, an empty JSON object was sent - which apparently LE accepted, but others reject. + - MDCertificateFile and MDCertificateKeyFile can now be specified several + times to add multiple, static certificates to a MDomain. [Stefan Eissing, @tlhackque, Andreas Ulm] *) mod_session: Improve session parsing. [Yann Yalvic] diff --git a/modules/md/md.h b/modules/md/md.h index d2b3af00b9a..78b7ef863be 100644 --- a/modules/md/md.h +++ b/modules/md/md.h @@ -92,8 +92,8 @@ struct md_t { const char *ca_account; /* account used at CA */ const char *ca_agreement; /* accepted agreement uri between CA and user */ struct apr_array_header_t *ca_challenges; /* challenge types configured for this MD */ - const char *cert_file; /* != NULL iff pubcert file explicitly configured */ - const char *pkey_file; /* != NULL iff privkey file explicitly configured */ + struct apr_array_header_t *cert_files; /* != NULL iff pubcerts explicitly configured */ + struct apr_array_header_t *pkey_files; /* != NULL iff privkeys explicitly configured */ md_state_t state; /* state of this MD */ @@ -118,7 +118,7 @@ struct md_t { #define MD_KEY_CA "ca" #define MD_KEY_CA_URL "ca-url" #define MD_KEY_CERT "cert" -#define MD_KEY_CERT_FILE "cert-file" +#define MD_KEY_CERT_FILES "cert-files" #define MD_KEY_CERTIFICATE "certificate" #define MD_KEY_CHALLENGE "challenge" #define MD_KEY_CHALLENGES "challenges" @@ -164,7 +164,7 @@ struct md_t { #define MD_KEY_ORDERS "orders" #define MD_KEY_PERMANENT "permanent" #define MD_KEY_PKEY "privkey" -#define MD_KEY_PKEY_FILE "pkey-file" +#define MD_KEY_PKEY_FILES "pkey-files" #define MD_KEY_PROBLEM "problem" #define MD_KEY_PROTO "proto" #define MD_KEY_READY "ready" @@ -285,6 +285,9 @@ md_t *md_from_json(struct md_json_t *json, apr_pool_t *p); int md_is_covered_by_alt_names(const md_t *md, const struct apr_array_header_t* alt_names); +/* how many certificates this domain has/will eventually have. */ +int md_cert_count(const md_t *md); + #define LE_ACMEv1_PROD "https://acme-v01.api.letsencrypt.org/directory" #define LE_ACMEv1_STAGING "https://acme-staging.api.letsencrypt.org/directory" diff --git a/modules/md/md_core.c b/modules/md/md_core.c index f4b8ebd052a..9b696e2b995 100644 --- a/modules/md/md_core.c +++ b/modules/md/md_core.c @@ -183,6 +183,15 @@ md_t *md_get_by_dns_overlap(struct apr_array_header_t *mds, const md_t *md) return NULL; } +int md_cert_count(const md_t *md) +{ + /* cert are defined as a list of static files or a list of private key specs */ + if (md->cert_files && md->cert_files->nelts) { + return md->cert_files->nelts; + } + return md_pkeys_spec_count(md->pks); +} + md_t *md_create(apr_pool_t *p, apr_array_header_t *domains) { md_t *md; @@ -242,8 +251,8 @@ md_t *md_clone(apr_pool_t *p, const md_t *src) } md->acme_tls_1_domains = md_array_str_compact(p, src->acme_tls_1_domains, 0); md->stapling = src->stapling; - if (src->cert_file) md->cert_file = apr_pstrdup(p, src->cert_file); - if (src->pkey_file) md->pkey_file = apr_pstrdup(p, src->pkey_file); + if (src->cert_files) md->cert_files = md_array_str_clone(p, src->cert_files); + if (src->pkey_files) md->pkey_files = md_array_str_clone(p, src->pkey_files); } return md; } @@ -290,8 +299,8 @@ md_json_t *md_to_json(const md_t *md, apr_pool_t *p) } md_json_setb(md->must_staple > 0, json, MD_KEY_MUST_STAPLE, NULL); md_json_setsa(md->acme_tls_1_domains, json, MD_KEY_PROTO, MD_KEY_ACME_TLS_1, NULL); - md_json_sets(md->cert_file, json, MD_KEY_CERT_FILE, NULL); - md_json_sets(md->pkey_file, json, MD_KEY_PKEY_FILE, NULL); + if (md->cert_files) md_json_setsa(md->cert_files, json, MD_KEY_CERT_FILES, NULL); + if (md->pkey_files) md_json_setsa(md->pkey_files, json, MD_KEY_PKEY_FILES, NULL); md_json_setb(md->stapling > 0, json, MD_KEY_STAPLING, NULL); return json; } @@ -337,8 +346,12 @@ md_t *md_from_json(md_json_t *json, apr_pool_t *p) md->must_staple = (int)md_json_getb(json, MD_KEY_MUST_STAPLE, NULL); md_json_dupsa(md->acme_tls_1_domains, p, json, MD_KEY_PROTO, MD_KEY_ACME_TLS_1, NULL); - md->cert_file = md_json_dups(p, json, MD_KEY_CERT_FILE, NULL); - md->pkey_file = md_json_dups(p, json, MD_KEY_PKEY_FILE, NULL); + if (md_json_has_key(json, MD_KEY_CERT_FILES, NULL)) { + md->cert_files = apr_array_make(p, 3, sizeof(char*)); + md->pkey_files = apr_array_make(p, 3, sizeof(char*)); + md_json_dupsa(md->cert_files, p, json, MD_KEY_CERT_FILES, NULL); + md_json_dupsa(md->pkey_files, p, json, MD_KEY_PKEY_FILES, NULL); + } md->stapling = (int)md_json_getb(json, MD_KEY_STAPLING, NULL); return md; diff --git a/modules/md/md_crypt.c b/modules/md/md_crypt.c index a804c51fb67..4c97f4375ba 100644 --- a/modules/md/md_crypt.c +++ b/modules/md/md_crypt.c @@ -22,6 +22,8 @@ #include #include #include +#include +#include #include #include diff --git a/modules/md/md_reg.c b/modules/md/md_reg.c index 7b039b2f031..19e5dbefc91 100644 --- a/modules/md/md_reg.c +++ b/modules/md/md_reg.c @@ -201,43 +201,40 @@ static apr_status_t state_init(md_reg_t *reg, apr_pool_t *p, md_t *md) const md_pubcert_t *pub; const md_cert_t *cert; apr_status_t rv = APR_SUCCESS; - md_pkey_spec_t *spec; int i; if (md->renew_window == NULL) md->renew_window = reg->renew_window; if (md->warn_window == NULL) md->warn_window = reg->warn_window; - for (i = 0; i < md_pkeys_spec_count(md->pks); ++i) { - spec = md_pkeys_spec_get(md->pks, i); - if (APR_SUCCESS == (rv = md_reg_get_pubcert(&pub, reg, md, spec, p))) { + for (i = 0; i < md_cert_count(md); ++i) { + md_log_perror(MD_LOG_MARK, MD_LOG_TRACE2, rv, p, "md{%s}: check cert %d", md->name, i); + if (APR_SUCCESS == (rv = md_reg_get_pubcert(&pub, reg, md, i, p))) { cert = APR_ARRAY_IDX(pub->certs, 0, const md_cert_t*); if (!md_is_covered_by_alt_names(md, pub->alt_names)) { state = MD_S_INCOMPLETE; md_log_perror(MD_LOG_MARK, MD_LOG_DEBUG, rv, p, - "md{%s}: incomplete, certificate(%s) does not cover all domains.", - md->name, md_pkey_spec_name(spec)); + "md{%s}: incomplete, certificate(%d) does not cover all domains.", + md->name, i); goto out; } if (!md->must_staple != !md_cert_must_staple(cert)) { state = MD_S_INCOMPLETE; md_log_perror(MD_LOG_MARK, MD_LOG_DEBUG, rv, p, "md{%s}: incomplete, OCSP Stapling is%s requested, but " - "certificate(%s) has it%s enabled.", - md->name, md_pkey_spec_name(spec), - md->must_staple? "" : " not", + "certificate(%d) has it%s enabled.", + md->name, md->must_staple? "" : " not", i, !md->must_staple? "" : " not"); goto out; } state = MD_S_COMPLETE; - md_log_perror(MD_LOG_MARK, MD_LOG_DEBUG, rv, p, "md{%s}: certificate(%s) is ok", - md->name, md_pkey_spec_name(spec)); + md_log_perror(MD_LOG_MARK, MD_LOG_DEBUG, rv, p, "md{%s}: certificate(%d) is ok", + md->name, i); } else if (APR_STATUS_IS_ENOENT(rv)) { state = MD_S_INCOMPLETE; rv = APR_SUCCESS; md_log_perror(MD_LOG_MARK, MD_LOG_DEBUG, rv, p, - "md{%s}: incomplete, certificate(%s) is missing", md->name, - md_pkey_spec_name(spec)); + "md{%s}: incomplete, certificate(%d) is missing", md->name, i); goto out; } } @@ -247,6 +244,7 @@ out: state = MD_S_ERROR; md_log_perror(MD_LOG_MARK, MD_LOG_WARNING, rv, p, "md{%s}: error", md->name); } + md_log_perror(MD_LOG_MARK, MD_LOG_TRACE2, rv, p, "md{%s}: state==%d", md->name, state); md->state = state; return rv; } @@ -532,7 +530,7 @@ static apr_status_t pubcert_load(void *baton, apr_pool_t *p, apr_pool_t *ptemp, apr_array_header_t *certs; md_pubcert_t *pubcert, **ppubcert; const md_t *md; - md_pkey_spec_t *spec; + int index; const md_cert_t *cert; md_cert_state_t cert_state; md_store_group_t group; @@ -541,12 +539,13 @@ static apr_status_t pubcert_load(void *baton, apr_pool_t *p, apr_pool_t *ptemp, ppubcert = va_arg(ap, md_pubcert_t **); group = (md_store_group_t)va_arg(ap, int); md = va_arg(ap, const md_t *); - spec = va_arg(ap, md_pkey_spec_t *); + index = va_arg(ap, int); - if (md->cert_file) { - rv = md_chain_fload(&certs, p, md->cert_file); + if (md->cert_files && md->cert_files->nelts) { + rv = md_chain_fload(&certs, p, APR_ARRAY_IDX(md->cert_files, index, const char *)); } else { + md_pkey_spec_t *spec = md_pkeys_spec_get(md->pks, index);; rv = md_pubcert_load(reg->store, group, md->name, spec, &certs, p); } if (APR_SUCCESS != rv) goto leave; @@ -571,16 +570,16 @@ leave: } apr_status_t md_reg_get_pubcert(const md_pubcert_t **ppubcert, md_reg_t *reg, - const md_t *md, md_pkey_spec_t *spec, apr_pool_t *p) + const md_t *md, int i, apr_pool_t *p) { apr_status_t rv = APR_SUCCESS; const md_pubcert_t *pubcert; const char *name; - name = apr_pstrcat(p, md->name, "[", md_pkey_spec_name(spec), "]", NULL); + name = apr_psprintf(p, "%s[%d]", md->name, i, NULL); pubcert = apr_hash_get(reg->certs, name, (apr_ssize_t)strlen(name)); if (!pubcert && !reg->domains_frozen) { - rv = md_util_pool_vdo(pubcert_load, reg, reg->p, &pubcert, MD_SG_DOMAINS, md, spec, NULL); + rv = md_util_pool_vdo(pubcert_load, reg, reg->p, &pubcert, MD_SG_DOMAINS, md, i, NULL); if (APR_STATUS_IS_ENOENT(rv)) { /* We cache it missing with an empty record */ pubcert = apr_pcalloc(reg->p, sizeof(*pubcert)); @@ -603,12 +602,6 @@ apr_status_t md_reg_get_cred_files(const char **pkeyfile, const char **pcertfile { apr_status_t rv; - if (md->cert_file) { - /* With fixed files configured, we use those without further checking them ourself */ - *pcertfile = md->cert_file; - *pkeyfile = md->pkey_file; - return APR_SUCCESS; - } rv = md_store_get_fname(pkeyfile, reg->store, group, md->name, md_pkey_filename(spec, p), p); if (APR_SUCCESS != rv) return rv; if (!md_file_exists(*pkeyfile, p)) return APR_ENOENT; @@ -622,14 +615,12 @@ apr_time_t md_reg_valid_until(md_reg_t *reg, const md_t *md, apr_pool_t *p) { const md_pubcert_t *pub; const md_cert_t *cert; - md_pkey_spec_t *spec; int i; apr_time_t t, valid_until = 0; apr_status_t rv; - for (i = 0; i < md_pkeys_spec_count(md->pks); ++i) { - spec = md_pkeys_spec_get(md->pks, i); - rv = md_reg_get_pubcert(&pub, reg, md, spec, p); + for (i = 0; i < md_cert_count(md); ++i) { + rv = md_reg_get_pubcert(&pub, reg, md, i, p); if (APR_SUCCESS == rv) { cert = APR_ARRAY_IDX(pub->certs, 0, const md_cert_t*); t = md_cert_get_not_after(cert); @@ -652,9 +643,8 @@ apr_time_t md_reg_renew_at(md_reg_t *reg, const md_t *md, apr_pool_t *p) apr_status_t rv; if (md->state == MD_S_INCOMPLETE) return apr_time_now(); - for (i = 0; i < md_pkeys_spec_count(md->pks); ++i) { - spec = md_pkeys_spec_get(md->pks, i); - rv = md_reg_get_pubcert(&pub, reg, md, spec, p); + for (i = 0; i < md_cert_count(md); ++i) { + rv = md_reg_get_pubcert(&pub, reg, md, i, p); if (APR_STATUS_IS_ENOENT(rv)) return apr_time_now(); if (APR_SUCCESS == rv) { cert = APR_ARRAY_IDX(pub->certs, 0, const md_cert_t*); @@ -691,14 +681,12 @@ int md_reg_should_warn(md_reg_t *reg, const md_t *md, apr_pool_t *p) const md_pubcert_t *pub; const md_cert_t *cert; md_timeperiod_t certlife, warn; - md_pkey_spec_t *spec; int i; apr_status_t rv; if (md->state == MD_S_INCOMPLETE) return 0; - for (i = 0; i < md_pkeys_spec_count(md->pks); ++i) { - spec = md_pkeys_spec_get(md->pks, i); - rv = md_reg_get_pubcert(&pub, reg, md, spec, p); + for (i = 0; i < md_cert_count(md); ++i) { + rv = md_reg_get_pubcert(&pub, reg, md, i, p); if (APR_STATUS_IS_ENOENT(rv)) return 0; if (APR_SUCCESS == rv) { cert = APR_ARRAY_IDX(pub->certs, 0, const md_cert_t*); @@ -708,8 +696,8 @@ int md_reg_should_warn(md_reg_t *reg, const md_t *md, apr_pool_t *p) warn = md_timeperiod_slice_before_end(&certlife, md->warn_window); if (md_log_is_level(p, MD_LOG_TRACE1)) { md_log_perror(MD_LOG_MARK, MD_LOG_TRACE2, 0, p, - "md[%s]: certificate(%s) life[%s] warn[%s]", - md->name, md_pkey_spec_name(spec), + "md[%s]: certificate(%d) life[%s] warn[%s]", + md->name, i, md_timeperiod_print(p, &certlife), md_timeperiod_print(p, &warn)); } @@ -898,8 +886,6 @@ apr_status_t md_reg_sync_finish(md_reg_t *reg, md_t *md, apr_pool_t *p, apr_pool apr_status_t rv; int changed = 1; - md_log_perror(MD_LOG_MARK, MD_LOG_DEBUG, 0, ptemp, "sync MDs, finish start"); - if (!md->ca_url) { md->ca_url = MD_ACME_DEF_URL; md->ca_proto = MD_PROTO_ACME; @@ -908,7 +894,9 @@ apr_status_t md_reg_sync_finish(md_reg_t *reg, md_t *md, apr_pool_t *p, apr_pool rv = state_init(reg, ptemp, md); if (APR_SUCCESS != rv) goto leave; + md_log_perror(MD_LOG_MARK, MD_LOG_DEBUG, rv, ptemp, "loading md %s", md->name); if (APR_SUCCESS == md_load(reg->store, MD_SG_DOMAINS, md->name, &old, ptemp)) { + md_log_perror(MD_LOG_MARK, MD_LOG_DEBUG, rv, ptemp, "loaded md %s", md->name); /* Some parts are kept from old, lacking new values */ if ((!md->contacts || apr_is_empty_array(md->contacts)) && old->contacts) { md->contacts = md_array_str_clone(p, old->contacts); @@ -938,11 +926,14 @@ apr_status_t md_reg_sync_finish(md_reg_t *reg, md_t *md, apr_pool_t *p, apr_pool && md_array_str_eq(md->acme_tls_1_domains, old->acme_tls_1_domains, 0) && !MD_VAL_UPDATE(md, old, stapling) && md_array_str_eq(md->contacts, old->contacts, 0) + && md_array_str_eq(md->cert_files, old->cert_files, 0) + && md_array_str_eq(md->pkey_files, old->pkey_files, 0) && md_array_str_eq(md->ca_challenges, old->ca_challenges, 0)) { changed = 0; } } if (changed) { + md_log_perror(MD_LOG_MARK, MD_LOG_DEBUG, rv, ptemp, "saving md %s", md->name); rv = md_save(reg->store, ptemp, MD_SG_DOMAINS, md, 0); } leave: @@ -1202,16 +1193,14 @@ apr_status_t md_reg_freeze_domains(md_reg_t *reg, apr_array_header_t *mds) apr_status_t rv = APR_SUCCESS; md_t *md; const md_pubcert_t *pubcert; - md_pkey_spec_t *spec; int i, j; assert(!reg->domains_frozen); /* prefill the certs cache for all mds */ for (i = 0; i < mds->nelts; ++i) { md = APR_ARRAY_IDX(mds, i, md_t*); - for (j = 0; j < md_pkeys_spec_count(md->pks); ++j) { - spec = md_pkeys_spec_get(md->pks, j); - rv = md_reg_get_pubcert(&pubcert, reg, md, spec, reg->p); + for (j = 0; j < md_cert_count(md); ++j) { + rv = md_reg_get_pubcert(&pubcert, reg, md, i, reg->p); if (APR_SUCCESS != rv && !APR_STATUS_IS_ENOENT(rv)) goto leave; } } diff --git a/modules/md/md_reg.h b/modules/md/md_reg.h index c677d606870..78c5b023e82 100644 --- a/modules/md/md_reg.h +++ b/modules/md/md_reg.h @@ -113,7 +113,7 @@ apr_status_t md_reg_update(md_reg_t *reg, apr_pool_t *p, * of the domain and going up the issuers. Returns APR_ENOENT when not available. */ apr_status_t md_reg_get_pubcert(const md_pubcert_t **ppubcert, md_reg_t *reg, - const md_t *md, struct md_pkey_spec_t *spec, apr_pool_t *p); + const md_t *md, int i, apr_pool_t *p); /** * Get the filenames of private key and pubcert of the MD - if they exist. diff --git a/modules/md/md_status.c b/modules/md/md_status.c index 0bddd7eb184..ecc35c66d09 100644 --- a/modules/md/md_status.c +++ b/modules/md/md_status.c @@ -116,7 +116,7 @@ static apr_status_t status_get_certs_json(md_json_t **pjson, apr_array_header_t apr_status_t rv = APR_SUCCESS; json = md_json_create(p); - for (i = 0; i < md_pkeys_spec_count(md->pks); ++i) { + for (i = 0; i < md_cert_count(md); ++i) { spec = md_pkeys_spec_get(md->pks, i); cert = APR_ARRAY_IDX(certs, i, md_cert_t*); if (!cert) continue; @@ -157,7 +157,7 @@ static apr_status_t get_staging_certs_json(md_json_t **pjson, const md_t *md, apr_status_t rv; certs = apr_array_make(p, 5, sizeof(md_cert_t*)); - for (i = 0; i < md_pkeys_spec_count(md->pks); ++i) { + for (i = 0; i < md_cert_count(md); ++i) { spec = md_pkeys_spec_get(md->pks, i); cert = NULL; rv = md_pubcert_load(md_reg_store_get(reg), MD_SG_STAGING, md->name, spec, &chain, p); @@ -180,15 +180,13 @@ static apr_status_t status_get_md_json(md_json_t **pjson, const md_t *md, apr_array_header_t *certs; apr_status_t rv = APR_SUCCESS; apr_time_t renew_at; - md_pkey_spec_t *spec; int i; mdj = md_to_json(md, p); certs = apr_array_make(p, 5, sizeof(md_cert_t*)); - for (i = 0; i < md_pkeys_spec_count(md->pks); ++i) { - spec = md_pkeys_spec_get(md->pks, i); + for (i = 0; i < md_cert_count(md); ++i) { cert = NULL; - if (APR_SUCCESS == md_reg_get_pubcert(&pubcert, reg, md, spec, p)) { + if (APR_SUCCESS == md_reg_get_pubcert(&pubcert, reg, md, i, p)) { cert = APR_ARRAY_IDX(pubcert->certs, 0, const md_cert_t*); } APR_ARRAY_PUSH(certs, const md_cert_t*) = cert; diff --git a/modules/md/mod_md.c b/modules/md/mod_md.c index 11dc0aa6693..2987d0449ce 100644 --- a/modules/md/mod_md.c +++ b/modules/md/mod_md.c @@ -671,17 +671,20 @@ static apr_status_t merge_mds_with_conf(md_mod_conf_t *mc, apr_pool_t *p, } } - if (md->cert_file && !md->pkey_file) { - ap_log_error(APLOG_MARK, APLOG_ERR, 0, base_server, APLOGNO(10170) - "The Managed Domain '%s', defined in %s(line %d), " - "has a MDCertificateFile but no MDCertificateKeyFile.", - md->name, md->defn_name, md->defn_line_number); - return APR_EINVAL; + if (md->cert_files && md->cert_files->nelts) { + if (!md->pkey_files || (md->cert_files->nelts != md->pkey_files->nelts)) { + ap_log_error(APLOG_MARK, APLOG_ERR, 0, base_server, APLOGNO(10170) + "The Managed Domain '%s', defined in %s(line %d), " + "needs one MDCertificateKeyFile for each MDCertificateFile.", + md->name, md->defn_name, md->defn_line_number); + return APR_EINVAL; + } } - if (!md->cert_file && md->pkey_file) { + else if (md->pkey_files && md->pkey_files->nelts + && (!md->cert_files || !md->cert_files->nelts)) { ap_log_error(APLOG_MARK, APLOG_ERR, 0, base_server, APLOGNO(10171) "The Managed Domain '%s', defined in %s(line %d), " - "has a MDCertificateKeyFile but no MDCertificateFile.", + "has MDCertificateKeyFile(s) but no MDCertificateFile.", md->name, md->defn_name, md->defn_line_number); return APR_EINVAL; } @@ -950,13 +953,16 @@ static apr_status_t md_post_config_after_ssl(apr_pool_t *p, apr_pool_t *plog, for (i = 0; i < mc->mds->nelts; ++i) { md = APR_ARRAY_IDX(mc->mds, i, md_t *); + ap_log_error( APLOG_MARK, APLOG_TRACE2, rv, s, "md{%s}: auto_add", md->name); if (APR_SUCCESS != (rv = auto_add_domains(md, s, p))) { goto leave; } init_acme_tls_1_domains(md, s); + ap_log_error( APLOG_MARK, APLOG_TRACE2, rv, s, "md{%s}: check_usage", md->name); if (APR_SUCCESS != (rv = check_usage(mc, md, s, p, ptemp))) { goto leave; } + ap_log_error( APLOG_MARK, APLOG_TRACE2, rv, s, "md{%s}: sync_finish", md->name); 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 syncing to store", md->name); @@ -964,8 +970,10 @@ static apr_status_t md_post_config_after_ssl(apr_pool_t *p, apr_pool_t *plog, } } /*8*/ + ap_log_error( APLOG_MARK, APLOG_TRACE2, rv, s, "init_cert_watch"); watched = init_cert_watch_status(mc, p, ptemp, s); /*9*/ + ap_log_error( APLOG_MARK, APLOG_TRACE2, rv, s, "cleanup challenges"); md_reg_cleanup_challenges(mc->reg, p, ptemp, mc->mds); /* From here on, the domains in the registry are readonly @@ -990,6 +998,7 @@ static apr_status_t md_post_config_after_ssl(apr_pool_t *p, apr_pool_t *plog, rv = md_ocsp_start_watching(mc, s, p); leave: + ap_log_error( APLOG_MARK, APLOG_TRACE2, rv, s, "post_config done"); return rv; } @@ -1087,7 +1096,6 @@ static apr_status_t get_certificates(server_rec *s, apr_pool_t *p, int fallback, const md_t *md; apr_array_header_t *key_files, *chain_files; const char *keyfile, *chainfile; - md_pkey_spec_t *spec; int i; *pkey_files = *pcert_files = NULL; @@ -1127,53 +1135,61 @@ static apr_status_t get_certificates(server_rec *s, apr_pool_t *p, int fallback, } md = APR_ARRAY_IDX(sc->assigned, 0, const md_t*); - for (i = 0; i < md_pkeys_spec_count(md->pks); ++i) { - spec = md_pkeys_spec_get(md->pks, i); - rv = md_reg_get_cred_files(&keyfile, &chainfile, reg, MD_SG_DOMAINS, md, spec, p); - if (APR_SUCCESS == rv) { - APR_ARRAY_PUSH(key_files, const char*) = keyfile; - APR_ARRAY_PUSH(chain_files, const char*) = chainfile; - } - else if (!APR_STATUS_IS_ENOENT(rv)) { - ap_log_error(APLOG_MARK, APLOG_ERR, rv, s, APLOGNO(10110) - "retrieving credentials for MD %s (%s)", - md->name, md_pkey_spec_name(spec)); - return rv; - } + if (md->cert_files && md->cert_files->nelts) { + apr_array_cat(chain_files, md->cert_files); + apr_array_cat(key_files, md->pkey_files); + rv = APR_SUCCESS; } + else { + md_pkey_spec_t *spec; + + for (i = 0; i < md_cert_count(md); ++i) { + spec = md_pkeys_spec_get(md->pks, i); + rv = md_reg_get_cred_files(&keyfile, &chainfile, reg, MD_SG_DOMAINS, md, spec, p); + if (APR_SUCCESS == rv) { + APR_ARRAY_PUSH(key_files, const char*) = keyfile; + APR_ARRAY_PUSH(chain_files, const char*) = chainfile; + } + else if (!APR_STATUS_IS_ENOENT(rv)) { + ap_log_error(APLOG_MARK, APLOG_ERR, rv, s, APLOGNO(10110) + "retrieving credentials for MD %s (%s)", + md->name, md_pkey_spec_name(spec)); + return rv; + } + } - if (md_array_is_empty(key_files)) { - if (fallback) { - /* Provide temporary, self-signed certificate as fallback, so that - * clients do not get obscure TLS handshake errors or will see a fallback - * virtual host that is not intended to be served here. */ - char *kfn, *cfn; - - store = md_reg_store_get(reg); - assert(store); - - for (i = 0; i < md_pkeys_spec_count(md->pks); ++i) { - spec = md_pkeys_spec_get(md->pks, i); - fallback_fnames(p, spec, &kfn, &cfn); - - md_store_get_fname(&keyfile, store, MD_SG_DOMAINS, md->name, kfn, p); - md_store_get_fname(&chainfile, store, MD_SG_DOMAINS, md->name, cfn, p); - if (!md_file_exists(keyfile, p) || !md_file_exists(chainfile, p)) { - if (APR_SUCCESS != (rv = make_fallback_cert(store, md, spec, s, p, kfn, cfn))) { - return rv; + if (md_array_is_empty(key_files)) { + if (fallback) { + /* Provide temporary, self-signed certificate as fallback, so that + * clients do not get obscure TLS handshake errors or will see a fallback + * virtual host that is not intended to be served here. */ + char *kfn, *cfn; + + store = md_reg_store_get(reg); + assert(store); + + for (i = 0; i < md_cert_count(md); ++i) { + spec = md_pkeys_spec_get(md->pks, i); + fallback_fnames(p, spec, &kfn, &cfn); + + md_store_get_fname(&keyfile, store, MD_SG_DOMAINS, md->name, kfn, p); + md_store_get_fname(&chainfile, store, MD_SG_DOMAINS, md->name, cfn, p); + if (!md_file_exists(keyfile, p) || !md_file_exists(chainfile, p)) { + if (APR_SUCCESS != (rv = make_fallback_cert(store, md, spec, s, p, kfn, cfn))) { + return rv; + } } + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(10116) + "%s: providing %s fallback certificate for server %s", + md->name, md_pkey_spec_name(spec), s->server_hostname); + APR_ARRAY_PUSH(key_files, const char*) = keyfile; + APR_ARRAY_PUSH(chain_files, const char*) = chainfile; } - ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(10116) - "%s: providing %s fallback certificate for server %s", - md->name, md_pkey_spec_name(spec), s->server_hostname); - APR_ARRAY_PUSH(key_files, const char*) = keyfile; - APR_ARRAY_PUSH(chain_files, const char*) = chainfile; + rv = APR_EAGAIN; + goto leave; } - rv = APR_EAGAIN; - goto leave; } } - ap_log_error(APLOG_MARK, APLOG_DEBUG, rv, s, APLOGNO(10077) "%s[state=%d]: providing certificates for server %s", md->name, md->state, s->server_hostname); @@ -1345,7 +1361,8 @@ static int md_http_challenge_pr(request_rec *r) return DONE; } else if (!md || md->renew_mode == MD_RENEW_MANUAL - || (md->cert_file && md->renew_mode == MD_RENEW_AUTO)) { + || (md->cert_files && md->cert_files->nelts + && md->renew_mode == MD_RENEW_AUTO)) { /* The request hostname is not for a domain - or at least not for * a domain that we renew ourselves. We are not * the sole authority here for /.well-known/acme-challenge (see PR62189). diff --git a/modules/md/mod_md_config.c b/modules/md/mod_md_config.c index 0f31b919c51..63b2e7ab811 100644 --- a/modules/md/mod_md_config.c +++ b/modules/md/mod_md_config.c @@ -878,27 +878,37 @@ static const char *md_config_set_dns01_cmd(cmd_parms *cmd, void *mconfig, const return NULL; } -static const char *md_config_set_cert_file(cmd_parms *cmd, void *mconfig, const char *arg) +static const char *md_config_add_cert_file(cmd_parms *cmd, void *mconfig, const char *arg) { md_srv_conf_t *sc = md_config_get(cmd->server); - const char *err; + const char *err, *fpath; (void)mconfig; if ((err = md_conf_check_location(cmd, MD_LOC_MD))) return err; assert(sc->current); - sc->current->cert_file = arg; + fpath = ap_server_root_relative(cmd->pool, arg); + if (!fpath) return apr_psprintf(cmd->pool, "certificate file not found: %s", arg); + if (!sc->current->cert_files) { + sc->current->cert_files = apr_array_make(cmd->pool, 3, sizeof(char*)); + } + APR_ARRAY_PUSH(sc->current->cert_files, const char*) = fpath; return NULL; } -static const char *md_config_set_key_file(cmd_parms *cmd, void *mconfig, const char *arg) +static const char *md_config_add_key_file(cmd_parms *cmd, void *mconfig, const char *arg) { md_srv_conf_t *sc = md_config_get(cmd->server); - const char *err; + const char *err, *fpath; (void)mconfig; if ((err = md_conf_check_location(cmd, MD_LOC_MD))) return err; assert(sc->current); - sc->current->pkey_file = arg; + fpath = ap_server_root_relative(cmd->pool, arg); + if (!fpath) return apr_psprintf(cmd->pool, "certificate key file not found: %s", arg); + if (!sc->current->pkey_files) { + sc->current->pkey_files = apr_array_make(cmd->pool, 3, sizeof(char*)); + } + APR_ARRAY_PUSH(sc->current->pkey_files, const char*) = fpath; return NULL; } @@ -1049,9 +1059,9 @@ const command_rec md_cmds[] = { "Allow managing of base server outside virtual hosts."), AP_INIT_RAW_ARGS("MDChallengeDns01", md_config_set_dns01_cmd, NULL, RSRC_CONF, "Set the command for setup/teardown of dns-01 challenges"), - AP_INIT_TAKE1("MDCertificateFile", md_config_set_cert_file, NULL, RSRC_CONF, + AP_INIT_TAKE1("MDCertificateFile", md_config_add_cert_file, NULL, RSRC_CONF, "set the static certificate (chain) file to use for this domain."), - AP_INIT_TAKE1("MDCertificateKeyFile", md_config_set_key_file, NULL, RSRC_CONF, + AP_INIT_TAKE1("MDCertificateKeyFile", md_config_add_key_file, NULL, RSRC_CONF, "set the static private key file to use for this domain."), AP_INIT_TAKE1("MDServerStatus", md_config_set_server_status, NULL, RSRC_CONF, "On to see Managed Domains in server-status."), diff --git a/modules/md/mod_md_drive.c b/modules/md/mod_md_drive.c index 87b06f6b514..942256f3cfd 100644 --- a/modules/md/mod_md_drive.c +++ b/modules/md/mod_md_drive.c @@ -173,7 +173,7 @@ int md_will_renew_cert(const md_t *md) if (md->renew_mode == MD_RENEW_MANUAL) { return 0; } - else if (md->renew_mode == MD_RENEW_AUTO && md->cert_file) { + else if (md->renew_mode == MD_RENEW_AUTO && md->cert_files && md->cert_files->nelts) { return 0; } return 1; diff --git a/modules/md/mod_md_status.c b/modules/md/mod_md_status.c index af0eed5c24c..cb0e2677f06 100644 --- a/modules/md/mod_md_status.c +++ b/modules/md/mod_md_status.c @@ -104,7 +104,7 @@ int md_http_cert_status(request_rec *r) md_json_setj(md_json_getj(mdj, MD_KEY_CERT, MD_KEY_VALID, NULL), resp, MD_KEY_VALID, NULL); } - for (i = 0; i < md_pkeys_spec_count(md->pks); ++i) { + for (i = 0; i < md_cert_count(md); ++i) { spec = md_pkeys_spec_get(md->pks, i); keyname = md_pkey_spec_name(spec); cj = md_json_create(r->pool);