You are viewing a plain text version of this content. The canonical link for it is here.
Posted to cvs@httpd.apache.org by ic...@apache.org on 2021/03/22 15:09:05 UTC

svn commit: r1887923 - in /httpd/httpd/trunk: ./ modules/md/

Author: icing
Date: Mon Mar 22 15:09:05 2021
New Revision: 1887923

URL: http://svn.apache.org/viewvc?rev=1887923&view=rev
Log:
mod_md:
     - MDCertificateFile and MDCertificateKeyFile can now be specified several
     times to add multiple, static certificates to a MDomain.


Modified:
    httpd/httpd/trunk/CHANGES
    httpd/httpd/trunk/modules/md/md.h
    httpd/httpd/trunk/modules/md/md_core.c
    httpd/httpd/trunk/modules/md/md_crypt.c
    httpd/httpd/trunk/modules/md/md_reg.c
    httpd/httpd/trunk/modules/md/md_reg.h
    httpd/httpd/trunk/modules/md/md_status.c
    httpd/httpd/trunk/modules/md/mod_md.c
    httpd/httpd/trunk/modules/md/mod_md_config.c
    httpd/httpd/trunk/modules/md/mod_md_drive.c
    httpd/httpd/trunk/modules/md/mod_md_status.c

Modified: httpd/httpd/trunk/CHANGES
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/CHANGES?rev=1887923&r1=1887922&r2=1887923&view=diff
==============================================================================
--- httpd/httpd/trunk/CHANGES [utf-8] (original)
+++ httpd/httpd/trunk/CHANGES [utf-8] Mon Mar 22 15:09:05 2021
@@ -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]

Modified: httpd/httpd/trunk/modules/md/md.h
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/md/md.h?rev=1887923&r1=1887922&r2=1887923&view=diff
==============================================================================
--- httpd/httpd/trunk/modules/md/md.h (original)
+++ httpd/httpd/trunk/modules/md/md.h Mon Mar 22 15:09:05 2021
@@ -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 *jso
 
 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"
 

Modified: httpd/httpd/trunk/modules/md/md_core.c
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/md/md_core.c?rev=1887923&r1=1887922&r2=1887923&view=diff
==============================================================================
--- httpd/httpd/trunk/modules/md/md_core.c (original)
+++ httpd/httpd/trunk/modules/md/md_core.c Mon Mar 22 15:09:05 2021
@@ -183,6 +183,15 @@ md_t *md_get_by_dns_overlap(struct apr_a
     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
         }
         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, ap
         }
         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_
         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;

Modified: httpd/httpd/trunk/modules/md/md_crypt.c
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/md/md_crypt.c?rev=1887923&r1=1887922&r2=1887923&view=diff
==============================================================================
--- httpd/httpd/trunk/modules/md/md_crypt.c (original)
+++ httpd/httpd/trunk/modules/md/md_crypt.c Mon Mar 22 15:09:05 2021
@@ -22,6 +22,8 @@
 #include <apr_buckets.h>
 #include <apr_file_io.h>
 #include <apr_strings.h>
+#include <httpd.h>
+#include <http_core.h>
 
 #include <openssl/err.h>
 #include <openssl/evp.h>

Modified: httpd/httpd/trunk/modules/md/md_reg.c
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/md/md_reg.c?rev=1887923&r1=1887922&r2=1887923&view=diff
==============================================================================
--- httpd/httpd/trunk/modules/md/md_reg.c (original)
+++ httpd/httpd/trunk/modules/md/md_reg.c Mon Mar 22 15:09:05 2021
@@ -201,43 +201,40 @@ static apr_status_t state_init(md_reg_t
     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 *b
     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 *b
     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
 {
     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 *
 {
     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
     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, co
     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, co
             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
     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
     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
             && 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_re
     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;
         }
     }

Modified: httpd/httpd/trunk/modules/md/md_reg.h
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/md/md_reg.h?rev=1887923&r1=1887922&r2=1887923&view=diff
==============================================================================
--- httpd/httpd/trunk/modules/md/md_reg.h (original)
+++ httpd/httpd/trunk/modules/md/md_reg.h Mon Mar 22 15:09:05 2021
@@ -113,7 +113,7 @@ apr_status_t md_reg_update(md_reg_t *reg
  * 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.

Modified: httpd/httpd/trunk/modules/md/md_status.c
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/md/md_status.c?rev=1887923&r1=1887922&r2=1887923&view=diff
==============================================================================
--- httpd/httpd/trunk/modules/md/md_status.c (original)
+++ httpd/httpd/trunk/modules/md/md_status.c Mon Mar 22 15:09:05 2021
@@ -116,7 +116,7 @@ static apr_status_t status_get_certs_jso
     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_js
     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(m
     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;

Modified: httpd/httpd/trunk/modules/md/mod_md.c
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/md/mod_md.c?rev=1887923&r1=1887922&r2=1887923&view=diff
==============================================================================
--- httpd/httpd/trunk/modules/md/mod_md.c (original)
+++ httpd/httpd/trunk/modules/md/mod_md.c Mon Mar 22 15:09:05 2021
@@ -671,17 +671,20 @@ static apr_status_t merge_mds_with_conf(
             }
         }
 
-        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
     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
         }
     }
     /*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
     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(ser
     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(ser
     }
     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_
                     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).

Modified: httpd/httpd/trunk/modules/md/mod_md_config.c
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/md/mod_md_config.c?rev=1887923&r1=1887922&r2=1887923&view=diff
==============================================================================
--- httpd/httpd/trunk/modules/md/mod_md_config.c (original)
+++ httpd/httpd/trunk/modules/md/mod_md_config.c Mon Mar 22 15:09:05 2021
@@ -878,27 +878,37 @@ static const char *md_config_set_dns01_c
     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."),

Modified: httpd/httpd/trunk/modules/md/mod_md_drive.c
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/md/mod_md_drive.c?rev=1887923&r1=1887922&r2=1887923&view=diff
==============================================================================
--- httpd/httpd/trunk/modules/md/mod_md_drive.c (original)
+++ httpd/httpd/trunk/modules/md/mod_md_drive.c Mon Mar 22 15:09:05 2021
@@ -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;

Modified: httpd/httpd/trunk/modules/md/mod_md_status.c
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/md/mod_md_status.c?rev=1887923&r1=1887922&r2=1887923&view=diff
==============================================================================
--- httpd/httpd/trunk/modules/md/mod_md_status.c (original)
+++ httpd/httpd/trunk/modules/md/mod_md_status.c Mon Mar 22 15:09:05 2021
@@ -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);