Apache HTTPD
md_acme_drive.c
Go to the documentation of this file.
1/* Licensed to the Apache Software Foundation (ASF) under one or more
2 * contributor license agreements. See the NOTICE file distributed with
3 * this work for additional information regarding copyright ownership.
4 * The ASF licenses this file to You under the Apache License, Version 2.0
5 * (the "License"); you may not use this file except in compliance with
6 * the License. You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include <assert.h>
18#include <stdlib.h>
19
20#include <apr_lib.h>
21#include <apr_strings.h>
22#include <apr_buckets.h>
23#include <apr_hash.h>
24#include <apr_uri.h>
25
26#include "md.h"
27#include "md_crypt.h"
28#include "md_json.h"
29#include "md_jws.h"
30#include "md_http.h"
31#include "md_log.h"
32#include "md_result.h"
33#include "md_reg.h"
34#include "md_store.h"
35#include "md_util.h"
36
37#include "md_acme.h"
38#include "md_acme_acct.h"
39#include "md_acme_authz.h"
40#include "md_acme_order.h"
41
42#include "md_acme_drive.h"
43#include "md_acmev2_drive.h"
44
45/**************************************************************************************************/
46/* account setup */
47
49 const md_t *md, apr_pool_t *p)
50{
51 md_acme_acct_t *acct;
53 apr_status_t rv;
54
55 if (APR_SUCCESS == (rv = md_acme_acct_load(&acct, &pkey, store,
56 MD_SG_STAGING, md->name, acme->p))) {
57 acme->acct_id = NULL;
58 acme->acct = acct;
59 acme->acct_key = pkey;
60 rv = md_acme_acct_validate(acme, NULL, p);
61 }
62 return rv;
63}
64
66 const char *md_name, apr_pool_t *p)
67{
69 apr_status_t rv;
70
72
73 rv = md_store_save(store, p, MD_SG_STAGING, md_name, MD_FN_ACCOUNT, MD_SV_JSON, jacct, 0);
74 if (APR_SUCCESS == rv) {
75 rv = md_store_save(store, p, MD_SG_STAGING, md_name, MD_FN_ACCT_KEY,
76 MD_SV_PKEY, acme->acct_key, 0);
77 }
78 return rv;
79}
80
82{
83 md_acme_driver_t *ad = d->baton;
84 md_t *md = ad->md;
86 int update_md = 0, update_acct = 0;
87
88 md_result_activity_printf(result, "Selecting account to use for %s", d->md->name);
90
91 /* Do we have a staged (modified) account? */
92 if (APR_SUCCESS == (rv = use_staged_acct(ad->acme, d->store, md, d->p))) {
93 md_log_perror(MD_LOG_MARK, MD_LOG_DEBUG, rv, d->p, "re-using staged account");
94 }
95 else if (!APR_STATUS_IS_ENOENT(rv)) {
96 goto leave;
97 }
98
99 /* Get an account for the ACME server for this MD */
100 if (!ad->acme->acct && md->ca_account) {
101 md_log_perror(MD_LOG_MARK, MD_LOG_DEBUG, rv, d->p, "re-use account '%s'", md->ca_account);
102 rv = md_acme_use_acct_for_md(ad->acme, d->store, d->p, md->ca_account, md);
104 md_log_perror(MD_LOG_MARK, MD_LOG_DEBUG, rv, d->p, "rejected %s", md->ca_account);
105 md->ca_account = NULL;
106 update_md = 1;
107 }
108 else if (APR_SUCCESS != rv) {
109 goto leave;
110 }
111 }
112
113 if (!ad->acme->acct && !md->ca_account) {
114 /* Find a local account for server, store at MD */
115 md_log_perror(MD_LOG_MARK, MD_LOG_DEBUG, rv, d->p, "%s: looking at existing accounts",
116 d->proto->protocol);
117 if (APR_SUCCESS == (rv = md_acme_find_acct_for_md(ad->acme, d->store, md))) {
118 md->ca_account = md_acme_acct_id_get(ad->acme);
119 update_md = 1;
120 md_log_perror(MD_LOG_MARK, MD_LOG_DEBUG, rv, d->p, "%s: using account %s (id=%s)",
121 d->proto->protocol, ad->acme->acct->url, md->ca_account);
122 }
123 }
124
125 if (!ad->acme->acct) {
126 /* No account staged, no suitable found in store, register a new one */
127 md_result_activity_printf(result, "Creating new ACME account for %s", d->md->name);
128 md_log_perror(MD_LOG_MARK, MD_LOG_DEBUG, rv, d->p, "%s: creating new account",
129 d->proto->protocol);
130
131 if (!ad->md->contacts || apr_is_empty_array(md->contacts)) {
132 rv = APR_EINVAL;
133 md_result_printf(result, rv, "No contact information is available for MD %s. "
134 "Configure one using the MDContactEmail or ServerAdmin directive.", md->name);
136 goto leave;
137 }
138
139 /* ACMEv1 allowed registration of accounts without accepted Terms-of-Service.
140 * ACMEv2 requires it. Fail early in this case with a meaningful error message.
141 */
142 if (!md->ca_agreement) {
144 "the CA requires you to accept the terms-of-service "
145 "as specified in <%s>. "
146 "Please read the document that you find at that URL and, "
147 "if you agree to the conditions, configure "
148 "\"MDCertificateAgreement accepted\" "
149 "in your Apache. Then (graceful) restart the server to activate.",
150 ad->acme->ca_agreement);
152 rv = result->status;
153 goto leave;
154 }
155
156 if (ad->acme->eab_required && (!md->ca_eab_kid || !strcmp("none", md->ca_eab_kid))) {
158 "the CA requires 'External Account Binding' which is not "
159 "configured. This means you need to obtain a 'Key ID' and a "
160 "'HMAC' from the CA and configure that using the "
161 "MDExternalAccountBinding directive in your config. "
162 "The creation of a new ACME account will most likely fail, "
163 "but an attempt is made anyway.",
164 ad->acme->ca_agreement);
166 }
167
168 rv = md_acme_acct_register(ad->acme, d->store, md, d->p);
169 if (APR_SUCCESS != rv) {
170 if (APR_SUCCESS != ad->acme->last->status) {
171 md_result_dup(result, ad->acme->last);
173 }
174 goto leave;
175 }
176
177 md->ca_account = NULL;
178 update_md = 1;
179 update_acct = 1;
180 }
181
182leave:
183 /* Persist MD changes in STAGING, so we pick them up on next run */
184 if (APR_SUCCESS == rv && update_md) {
185 rv = md_save(d->store, d->p, MD_SG_STAGING, ad->md, 0);
186 }
187 /* Persist account changes in STAGING, so we pick them up on next run */
188 if (APR_SUCCESS == rv && update_acct) {
189 rv = save_acct_staged(ad->acme, d->store, md->name, d->p);
190 }
191 return rv;
192}
193
194/**************************************************************************************************/
195/* poll cert */
196
198{
199 md_acme_driver_t *ad = d->baton;
200
201 ad->chain_up_link = md_link_find_relation(headers, d->p, "up");
202 if (ad->chain_up_link) {
204 "server reports up link as %s", ad->chain_up_link);
205 }
206}
207
209 const md_http_response_t *res)
210{
212 const char *ct;
213
214 ct = apr_table_get(res->headers, "Content-Type");
215 ct = md_util_parse_ct(res->req->pool, ct);
217 "parse certs from %s -> %d (%s)", res->req->url, res->status, ct);
218 if (ct && !strcmp("application/x-pkcs7-mime", ct)) {
219 /* this looks like a root cert and we do not want those in our chain */
220 goto out;
221 }
222
223 /* Lets try to read one or more certificates */
224 if (APR_SUCCESS != (rv = md_cert_chain_read_http(chain, p, res))
225 && APR_STATUS_IS_ENOENT(rv)) {
226 rv = APR_EAGAIN;
228 "cert not in response from %s", res->req->url);
229 }
230out:
231 return rv;
232}
233
235{
237 md_acme_driver_t *ad = d->baton;
239 int count;
240
241 (void)acme;
242 count = ad->cred->chain->nelts;
243 if (APR_SUCCESS == (rv = add_http_certs(ad->cred->chain, d->p, res))) {
244 md_log_perror(MD_LOG_MARK, MD_LOG_DEBUG, rv, d->p, "%d certs parsed",
245 ad->cred->chain->nelts - count);
246 get_up_link(d, res->headers);
247 }
248 return rv;
249}
250
251static apr_status_t get_cert(void *baton, int attempt)
252{
254 md_acme_driver_t *ad = d->baton;
255
256 (void)attempt;
257 md_log_perror(MD_LOG_MARK, MD_LOG_TRACE1, 0, d->p, "retrieving cert from %s",
258 ad->order->certificate);
259 return md_acme_GET(ad->acme, ad->order->certificate, NULL, NULL, on_add_cert, NULL, d);
260}
261
263{
264 md_acme_driver_t *ad = d->baton;
265 apr_status_t rv;
266
267 assert(ad->md);
268 assert(ad->acme);
269 assert(ad->order);
270 assert(ad->order->certificate);
271
272 if (only_once) {
273 rv = get_cert(d, 0);
274 }
275 else {
276 rv = md_util_try(get_cert, d, 1, ad->cert_poll_timeout, 0, 0, 1);
277 }
278
279 md_log_perror(MD_LOG_MARK, MD_LOG_DEBUG, 0, d->p, "poll for cert at %s", ad->order->certificate);
280 return rv;
281}
282
283/**************************************************************************************************/
284/* order finalization */
285
287{
289 md_acme_driver_t *ad = d->baton;
291
292 jpayload = md_json_create(req->p);
293 md_json_sets(ad->csr_der_64, jpayload, MD_KEY_CSR, NULL);
294
295 return md_acme_req_body_init(req, jpayload);
296}
297
299{
301 md_acme_driver_t *ad = d->baton;
302 const char *location;
305
306 (void)acme;
307 location = apr_table_get(res->headers, "location");
308 if (!location) {
310 "cert created without giving its location header");
311 return APR_EINVAL;
312 }
313 ad->order->certificate = apr_pstrdup(d->p, location);
314 if (APR_SUCCESS != (rv = md_acme_order_save(d->store, d->p, MD_SG_STAGING,
315 d->md->name, ad->order, 0))) {
317 "%s: saving cert url %s", d->md->name, location);
318 return rv;
319 }
320
321 /* Check if it already was sent with this response */
322 ad->chain_up_link = NULL;
323 if (APR_SUCCESS == (rv = md_cert_read_http(&cert, d->p, res))) {
324 md_log_perror(MD_LOG_MARK, MD_LOG_DEBUG, rv, d->p, "cert parsed");
325 apr_array_clear(ad->cred->chain);
326 APR_ARRAY_PUSH(ad->cred->chain, md_cert_t*) = cert;
327 get_up_link(d, res->headers);
328 }
329 else if (APR_STATUS_IS_ENOENT(rv)) {
330 rv = APR_SUCCESS;
331 if (location) {
333 "cert not in response, need to poll %s", location);
334 }
335 }
336
337 return rv;
338}
339
355{
356 md_acme_driver_t *ad = d->baton;
357 md_pkey_spec_t *spec;
359 apr_status_t rv;
360
361 md_result_activity_printf(result, "Finalizing order for %s", ad->md->name);
362
363 assert(ad->cred);
364 spec = ad->cred->spec;
365
366 rv = md_pkey_load(d->store, MD_SG_STAGING, d->md->name, spec, &privkey, d->p);
367 if (APR_STATUS_IS_ENOENT(rv)) {
368 if (APR_SUCCESS == (rv = md_pkey_gen(&privkey, d->p, spec))) {
369 rv = md_pkey_save(d->store, d->p, MD_SG_STAGING, d->md->name, spec, privkey, 1);
370 }
372 "%s: generate %s privkey", d->md->name, md_pkey_spec_name(spec));
373 }
374 if (APR_SUCCESS != rv) goto leave;
375
376 md_result_activity_printf(result, "Creating %s CSR", md_pkey_spec_name(spec));
377 rv = md_cert_req_create(&ad->csr_der_64, d->md->name, ad->domains,
378 ad->md->must_staple, privkey, d->p);
379 md_log_perror(MD_LOG_MARK, MD_LOG_DEBUG, rv, d->p, "%s: create %s CSR",
380 d->md->name, md_pkey_spec_name(spec));
381 if (APR_SUCCESS != rv) goto leave;
382
383 md_result_activity_printf(result, "Submitting %s CSR to CA", md_pkey_spec_name(spec));
384 assert(ad->order->finalize);
385 rv = md_acme_POST(ad->acme, ad->order->finalize, on_init_csr_req, NULL, csr_req, NULL, d);
386
387leave:
388 md_acme_report_result(ad->acme, rv, result);
389 return rv;
390}
391
392/**************************************************************************************************/
393/* cert chain retrieval */
394
396{
398 md_acme_driver_t *ad = d->baton;
400 const char *ct;
401
402 (void)acme;
403 ct = apr_table_get(res->headers, "Content-Type");
404 ct = md_util_parse_ct(res->req->pool, ct);
405 if (ct && !strcmp("application/x-pkcs7-mime", ct)) {
406 /* root cert most likely, end it here */
407 return APR_SUCCESS;
408 }
409
410 if (APR_SUCCESS == (rv = add_http_certs(ad->cred->chain, d->p, res))) {
411 md_log_perror(MD_LOG_MARK, MD_LOG_DEBUG, rv, d->p, "chain cert parsed");
412 get_up_link(d, res->headers);
413 }
414 return rv;
415}
416
417static apr_status_t get_chain(void *baton, int attempt)
418{
420 md_acme_driver_t *ad = d->baton;
421 const char *prev_link = NULL;
423
424 while (APR_SUCCESS == rv && ad->cred->chain->nelts < 10) {
425 int nelts = ad->cred->chain->nelts;
426
427 if (ad->chain_up_link && (!prev_link || strcmp(prev_link, ad->chain_up_link))) {
428 prev_link = ad->chain_up_link;
429
431 "next chain cert at %s", ad->chain_up_link);
432 rv = md_acme_GET(ad->acme, ad->chain_up_link, NULL, NULL, on_add_chain, NULL, d);
433
434 if (APR_SUCCESS == rv && nelts == ad->cred->chain->nelts) {
435 break;
436 }
437 else if (APR_SUCCESS != rv) {
439 "error retrieving certificate from %s", ad->chain_up_link);
440 return rv;
441 }
442 }
443 else if (ad->cred->chain->nelts <= 1) {
444 /* This cannot be the complete chain (no one signs new web certs with their root)
445 * and we did not see a "Link: ...rel=up", so we do not know how to continue. */
447 "no link header 'up' for new certificate, unable to retrieve chain");
448 rv = APR_EINVAL;
449 break;
450 }
451 else {
452 rv = APR_SUCCESS;
453 break;
454 }
455 }
457 "got chain with %d certs (%d. attempt)", ad->cred->chain->nelts, attempt);
458 return rv;
459}
460
462{
463 md_acme_driver_t *ad = d->baton;
464 apr_status_t rv;
465
466 /* This may be called repeatedly and needs to progress. The relevant state is in
467 * ad->cred->chain the certificate chain, starting with the new cert for the md
468 * ad->order->certificate the url where ACME offers us the new md certificate. This may
469 * be a single one or even the complete chain
470 * ad->chain_up_link in case the last certificate retrieval did not end the chain,
471 * the link header with relation "up" gives us the location
472 * for the next cert in the chain
473 */
474 if (md_array_is_empty(ad->cred->chain)) {
475 /* Need to start at the order */
477 if (!ad->order) {
478 rv = APR_EGENERAL;
480 "%s: asked to retrieve chain, but no order in context", d->md->name);
481 goto out;
482 }
483 if (!ad->order->certificate) {
484 rv = APR_EGENERAL;
486 "%s: asked to retrieve chain, but no certificate url part of order", d->md->name);
487 goto out;
488 }
489
490 if (APR_SUCCESS != (rv = md_acme_drive_cert_poll(d, 0))) {
491 goto out;
492 }
493 }
494
495 rv = md_util_try(get_chain, d, 0, ad->cert_poll_timeout, 0, 0, 0);
496 md_log_perror(MD_LOG_MARK, MD_LOG_DEBUG, rv, d->p, "chain retrieved");
497
498out:
499 return rv;
500}
501
502/**************************************************************************************************/
503/* ACME driver init */
504
506{
508 md_credentials_t *cred;
509 int i;
510
512
513 ad = apr_pcalloc(d->p, sizeof(*ad));
514
515 d->baton = ad;
516
517 ad->driver = d;
518 ad->authz_monitor_timeout = apr_time_from_sec(30);
519 ad->cert_poll_timeout = apr_time_from_sec(30);
520 ad->ca_challenges = apr_array_make(d->p, 3, sizeof(const char*));
521
522 /* We want to obtain credentials (key+certificate) for every key spec in this MD */
523 ad->creds = apr_array_make(d->p, md_pkeys_spec_count(d->md->pks), sizeof(md_credentials_t*));
524 for (i = 0; i < md_pkeys_spec_count(d->md->pks); ++i) {
525 cred = apr_pcalloc(d->p, sizeof(*cred));
526 cred->spec = md_pkeys_spec_get(d->md->pks, i);
527 cred->chain = apr_array_make(d->p, 5, sizeof(md_cert_t*));
528 APR_ARRAY_PUSH(ad->creds, md_credentials_t*) = cred;
529 }
530
532 "%s: init_base driver", d->md->name);
533 return result->status;
534}
535
537{
540 const char *challenge;
541
544 if (APR_SUCCESS != result->status) goto leave;
545
546 ad = d->baton;
547
548 /* We can only support challenges if the server is reachable from the outside
549 * via port 80 and/or 443. These ports might be mapped for httpd to something
550 * else, but a mapping needs to exist. */
551 challenge = apr_table_get(d->env, MD_KEY_CHALLENGE);
552 if (challenge) {
553 APR_ARRAY_PUSH(ad->ca_challenges, const char*) = apr_pstrdup(d->p, challenge);
554 }
555 else if (d->md->ca_challenges && d->md->ca_challenges->nelts > 0) {
556 /* pre-configured set for this managed domain */
557 apr_array_cat(ad->ca_challenges, d->md->ca_challenges);
558 }
559 else {
560 /* free to chose. Add all we support and see what we get offered */
561 APR_ARRAY_PUSH(ad->ca_challenges, const char*) = MD_AUTHZ_TYPE_TLSALPN01;
562 APR_ARRAY_PUSH(ad->ca_challenges, const char*) = MD_AUTHZ_TYPE_HTTP01;
563 APR_ARRAY_PUSH(ad->ca_challenges, const char*) = MD_AUTHZ_TYPE_DNS01;
564
565 if (!d->can_http && !d->can_https
566 && md_array_str_index(ad->ca_challenges, MD_AUTHZ_TYPE_DNS01, 0, 0) < 0) {
568 "the server seems neither reachable via http (port 80) nor https (port 443). "
569 "Please look at the MDPortMap configuration directive on how to correct this. "
570 "The ACME protocol needs at least one of those so the CA can talk to the server "
571 "and verify a domain ownership. Alternatively, you may configure support "
572 "for the %s challenge directive.", MD_AUTHZ_TYPE_DNS01);
573 goto leave;
574 }
575
577 if (!d->can_http && md_array_str_index(ad->ca_challenges, MD_AUTHZ_TYPE_HTTP01, 0, 1) >= 0) {
578 ad->ca_challenges = md_array_str_remove(d->p, ad->ca_challenges, MD_AUTHZ_TYPE_HTTP01, 0);
579 dis_http = 1;
580 }
581 if (!d->can_https && md_array_str_index(ad->ca_challenges, MD_AUTHZ_TYPE_TLSALPN01, 0, 1) >= 0) {
582 ad->ca_challenges = md_array_str_remove(d->p, ad->ca_challenges, MD_AUTHZ_TYPE_TLSALPN01, 0);
583 dis_https = 1;
584 }
585 if (apr_is_empty_array(d->md->acme_tls_1_domains)
586 && md_array_str_index(ad->ca_challenges, MD_AUTHZ_TYPE_TLSALPN01, 0, 1) >= 0) {
587 ad->ca_challenges = md_array_str_remove(d->p, ad->ca_challenges, MD_AUTHZ_TYPE_TLSALPN01, 0);
588 dis_alpn_acme = 1;
589 }
591 && NULL == d->md->dns01_cmd
592 && md_array_str_index(ad->ca_challenges, MD_AUTHZ_TYPE_DNS01, 0, 1) >= 0) {
593 ad->ca_challenges = md_array_str_remove(d->p, ad->ca_challenges, MD_AUTHZ_TYPE_DNS01, 0);
594 dis_dns = 1;
595 }
596
597 if (apr_is_empty_array(ad->ca_challenges)) {
599 "None of the ACME challenge methods configured for this domain are suitable.%s%s%s%s",
600 dis_http? " The http: challenge 'http-01' is disabled because the server seems not reachable on public port 80." : "",
601 dis_https? " The https: challenge 'tls-alpn-01' is disabled because the server seems not reachable on public port 443." : "",
602 dis_alpn_acme? " The https: challenge 'tls-alpn-01' is disabled because the Protocols configuration does not include the 'acme-tls/1' protocol." : "",
603 dis_dns? " The DNS challenge 'dns-01' is disabled because the directive 'MDChallengeDns01' is not configured." : ""
604 );
605 goto leave;
606 }
607 }
608
609 md_result_printf(result, 0, "MDomain %s initialized with support for ACME challenges %s",
610 d->md->name, apr_array_pstrcat(d->p, ad->ca_challenges, ' '));
611
612leave:
613 md_log_perror(MD_LOG_MARK, MD_LOG_TRACE1, result->status, d->p, "%s: init driver", d->md->name);
614 return result->status;
615}
616
617/**************************************************************************************************/
618/* ACME staging */
619
621{
622 md_acme_driver_t *ad = d->baton;
623 md_credentials_t *cred;
624 apr_array_header_t *chain;
625 int i, complete;
626 apr_status_t rv;
627
628 complete = 1;
629 for (i = 0; i < ad->creds->nelts; ++i) {
630 rv = APR_SUCCESS;
631 cred = APR_ARRAY_IDX(ad->creds, i, md_credentials_t*);
632 if (!cred->pkey) {
633 rv = md_pkey_load(d->store, MD_SG_STAGING, d->md->name, cred->spec, &cred->pkey, d->p);
634 }
635 if (APR_SUCCESS == rv && md_array_is_empty(cred->chain)) {
636 rv = md_pubcert_load(d->store, MD_SG_STAGING, d->md->name, cred->spec, &chain, d->p);
637 if (APR_SUCCESS == rv) {
638 apr_array_cat(cred->chain, chain);
639 }
640 }
641 if (APR_SUCCESS == rv) {
642 md_log_perror(MD_LOG_MARK, MD_LOG_TRACE1, 0, d->p, "%s: credentials staged for %s certificate",
643 d->md->name, md_pkey_spec_name(cred->spec));
644 }
645 else {
646 complete = 0;
647 }
648 }
649 return complete? APR_SUCCESS : APR_EAGAIN;
650}
651
653{
654 md_acme_driver_t *ad = d->baton;
655 int reset_staging = d->reset;
657 apr_time_t now, t, t2;
658 md_credentials_t *cred;
659 const char *ca_effective = NULL;
660 char ts[APR_RFC822_DATE_LEN];
661 int i, first = 0;
662
663 if (!d->md->ca_urls || d->md->ca_urls->nelts <= 0) {
664 /* No CA defined? This is checked in several other places, but lets be sure */
666 "The managed domain %s is missing MDCertificateAuthority", d->md->name);
667 goto out;
668 }
669
670 /* When not explicitly told to reset, we check the existing data. If
671 * it is incomplete or old, we trigger the reset for a clean start. */
672 if (!reset_staging) {
673 md_result_activity_setn(result, "Checking staging area");
674 rv = md_load(d->store, MD_SG_STAGING, d->md->name, &ad->md, d->p);
675 if (APR_SUCCESS == rv) {
676 /* So, we have a copy in staging, but is it a recent or an old one? */
677 if (md_is_newer(d->store, MD_SG_DOMAINS, MD_SG_STAGING, d->md->name, d->p)) {
678 reset_staging = 1;
679 }
680 }
681 else if (APR_STATUS_IS_ENOENT(rv)) {
682 reset_staging = 1;
683 rv = APR_SUCCESS;
684 }
685 }
686
687 /* What CA are we using this time? */
688 if (ad->md && ad->md->ca_effective) {
689 /* There was one chosen on the previous run. Do we stick to it? */
690 ca_effective = ad->md->ca_effective;
691 if (d->md->ca_urls->nelts > 1 && d->attempt >= d->retry_failover) {
692 /* We have more than one CA to choose from and this is the (at least)
693 * third attempt with the same CA. Let's switch to the next one. */
694 int last_idx = md_array_str_index(d->md->ca_urls, ca_effective, 0, 1);
695 if (last_idx >= 0) {
696 int next_idx = (last_idx+1) % d->md->ca_urls->nelts;
697 ca_effective = APR_ARRAY_IDX(d->md->ca_urls, next_idx, const char*);
698 }
699 else {
700 /* not part of current configuration? */
701 ca_effective = NULL;
702 }
703 /* switching CA means we need to wipe the staging area */
704 reset_staging = 1;
705 }
706 }
707
708 if (!ca_effective) {
709 /* None chosen yet, pick the first one configured */
710 ca_effective = APR_ARRAY_IDX(d->md->ca_urls, 0, const char*);
711 }
712
713 if (md_log_is_level(d->p, MD_LOG_DEBUG)) {
714 md_log_perror(MD_LOG_MARK, MD_LOG_DEBUG, 0, d->p, "%s: staging started, "
715 "state=%d, attempt=%d, acme=%s, challenges='%s'",
716 d->md->name, d->md->state, d->attempt, ca_effective,
717 apr_array_pstrcat(d->p, ad->ca_challenges, ' '));
718 }
719
720 if (reset_staging) {
721 md_result_activity_setn(result, "Resetting staging area");
722 /* reset the staging area for this domain */
723 rv = md_store_purge(d->store, d->p, MD_SG_STAGING, d->md->name);
725 "%s: reset staging area", d->md->name);
726 if (APR_SUCCESS != rv && !APR_STATUS_IS_ENOENT(rv)) {
727 md_result_printf(result, rv, "resetting staging area");
728 goto out;
729 }
730 rv = APR_SUCCESS;
731 ad->md = NULL;
732 ad->order = NULL;
733 }
734
735 md_result_activity_setn(result, "Assessing current status");
736 if (ad->md && ad->md->state == MD_S_MISSING_INFORMATION) {
737 /* ToS agreement is missing. It makes no sense to drive this MD further */
739 "The managed domain %s is missing required information", d->md->name);
740 goto out;
741 }
742
743 if (ad->md && APR_SUCCESS == load_missing_creds(d)) {
744 md_log_perror(MD_LOG_MARK, MD_LOG_DEBUG, 0, d->p, "%s: all credentials staged", d->md->name);
745 goto ready;
746 }
747
748 /* Need to renew */
749 if (!ad->md || !md_array_str_eq(ad->md->ca_urls, d->md->ca_urls, 1)) {
750 md_result_activity_printf(result, "Resetting staging for %s", d->md->name);
751 /* re-initialize staging */
752 md_log_perror(MD_LOG_MARK, MD_LOG_DEBUG, 0, d->p, "%s: setup staging", d->md->name);
753 md_store_purge(d->store, d->p, MD_SG_STAGING, d->md->name);
754 ad->md = md_copy(d->p, d->md);
755 ad->md->ca_effective = ca_effective;
756 ad->md->ca_account = NULL;
757 ad->order = NULL;
758 rv = md_save(d->store, d->p, MD_SG_STAGING, ad->md, 0);
759 if (APR_SUCCESS != rv) {
760 md_result_printf(result, rv, "Saving MD information in staging area.");
762 goto out;
763 }
764 }
765 if (!ad->domains) {
766 ad->domains = md_dns_make_minimal(d->p, ad->md->domains);
767 }
768
769 md_result_activity_printf(result, "Contacting ACME server for %s at %s",
770 d->md->name, ca_effective);
771 if (APR_SUCCESS != (rv = md_acme_create(&ad->acme, d->p, ca_effective,
772 d->proxy_url, d->ca_file))) {
773 md_result_printf(result, rv, "setup ACME communications");
775 goto out;
776 }
777 if (APR_SUCCESS != (rv = md_acme_setup(ad->acme, result))) {
779 goto out;
780 }
781
783 for (i = 0; i < ad->creds->nelts; ++i) {
784 ad->cred = APR_ARRAY_IDX(ad->creds, i, md_credentials_t*);
785 if (!ad->cred->pkey || md_array_is_empty(ad->cred->chain)) {
786 md_result_activity_printf(result, "Driving ACME to renew %s certificate for %s",
787 md_pkey_spec_name(ad->cred->spec),d->md->name);
788 /* The process of setting up challenges and verifying domain
789 * names differs between ACME versions. */
790 switch (MD_ACME_VERSION_MAJOR(ad->acme->version)) {
791 case 1:
793 "ACME server speaks version 1, an obsolete version of the ACME "
794 "protocol that is no longer supported.");
795 rv = result->status;
796 break;
797 default:
798 /* In principle, we only know ACME version 2. But we assume
799 that a new protocol which announces a directory with all members
800 from version 2 will act backward compatible.
801 This is, of course, an assumption...
802 */
804 break;
805 }
806 if (APR_SUCCESS != rv) goto out;
807
808 if (md_array_is_empty(ad->cred->chain) || ad->chain_up_link) {
809 md_result_activity_printf(result, "Retrieving %s certificate chain for %s",
810 md_pkey_spec_name(ad->cred->spec), d->md->name);
812 "%s: retrieving %s certificate chain",
813 d->md->name, md_pkey_spec_name(ad->cred->spec));
814 rv = ad_chain_retrieve(d);
815 if (APR_SUCCESS != rv) {
816 md_result_printf(result, rv, "Unable to retrieve %s certificate chain.",
817 md_pkey_spec_name(ad->cred->spec));
818 goto out;
819 }
820
821 if (!md_array_is_empty(ad->cred->chain)) {
822
823 if (!ad->cred->pkey) {
824 rv = md_pkey_load(d->store, MD_SG_STAGING, d->md->name, ad->cred->spec, &ad->cred->pkey, d->p);
825 if (APR_SUCCESS != rv) {
826 md_result_printf(result, rv, "Loading the private key.");
827 goto out;
828 }
829 }
830
831 if (ad->cred->pkey) {
832 rv = md_check_cert_and_pkey(ad->cred->chain, ad->cred->pkey);
833 if (APR_SUCCESS != rv) {
834 md_result_printf(result, rv, "Certificate and private key do not match.");
835
836 /* Delete the order */
837 md_acme_order_purge(d->store, d->p, MD_SG_STAGING, d->md, d->env);
838
839 goto out;
840 }
841 }
842
843 rv = md_pubcert_save(d->store, d->p, MD_SG_STAGING, d->md->name,
844 ad->cred->spec, ad->cred->chain, 0);
845 if (APR_SUCCESS != rv) {
846 md_result_printf(result, rv, "Saving new %s certificate chain.",
847 md_pkey_spec_name(ad->cred->spec));
848 goto out;
849 }
850 }
851 }
852
853 /* Clean up the order, so the next pkey spec sets up a new one */
854 md_acme_order_purge(d->store, d->p, MD_SG_STAGING, d->md, d->env);
855 }
856 }
857 }
858
859
860 /* As last step, cleanup any order we created so that challenge data
861 * may be removed asap. */
862 md_acme_order_purge(d->store, d->p, MD_SG_STAGING, d->md, d->env);
863
864 /* first time this job ran through */
865 first = 1;
866ready:
868 /* we should have the complete cert chain now */
871 "%s: certificates ready, activation delay set to %s",
872 d->md->name, md_duration_format(d->p, d->activation_delay));
873
874 /* determine when it should be activated */
875 t = apr_time_now();
876 for (i = 0; i < ad->creds->nelts; ++i) {
877 cred = APR_ARRAY_IDX(ad->creds, i, md_credentials_t*);
879 if (t2 > t) t = t2;
880 }
882
883 /* If the existing MD is complete and un-expired, delay the activation
884 * to 24 hours after new cert is valid (if there is enough time left), so
885 * that cients with skewed clocks do not see a problem. */
886 now = apr_time_now();
887 if (d->md->state == MD_S_COMPLETE) {
889
891 "%s: state is COMPLETE, checking existing certificates", d->md->name);
892 valid_until = md_reg_valid_until(d->reg, d->md, d->p);
893 if (d->activation_delay < 0) {
894 /* special simulation for test case */
895 if (first) {
897 "%s: delay ready_at to now+1s", d->md->name);
899 }
900 }
901 else if (valid_until > now) {
902 delay_activation = d->activation_delay;
903 if (delay_activation > (valid_until - now)) {
905 }
907 }
908 }
909
910 /* There is a full set staged, to be loaded */
911 apr_rfc822_date(ts, result->ready_at);
912 if (result->ready_at > now) {
914 "The certificate for the managed domain has been renewed successfully and can "
915 "be used from %s on.", ts);
916 }
917 else {
919 "The certificate for the managed domain has been renewed successfully and can "
920 "be used (valid since %s). A graceful server restart now is recommended.", ts);
921 }
922
923out:
924 return rv;
925}
926
935
936/**************************************************************************************************/
937/* ACME preload */
938
940 const char *name, md_result_t *result)
941{
942 apr_status_t rv;
943 md_pkey_t *acct_key;
944 md_t *md;
946 md_credentials_t *creds;
948 struct md_acme_acct_t *acct;
949 const char *id;
950 int i;
951
952 md_log_perror(MD_LOG_MARK, MD_LOG_DEBUG, 0, d->p, "%s: preload start", name);
953 /* Load data from MD_SG_STAGING and save it into "load_group".
954 * This serves several purposes:
955 * 1. It's a format check on the input data.
956 * 2. We write back what we read, creating data with our own access permissions
957 * 3. We ignore any other accumulated data in STAGING
958 * 4. Once "load_group" is complete an ok, we can swap/archive groups with a rename
959 * 5. Reading/Writing the data will apply/remove any group specific data encryption.
960 */
961 if (APR_SUCCESS != (rv = md_load(d->store, MD_SG_STAGING, name, &md, d->p))) {
962 md_result_set(result, rv, "loading staged md.json");
963 goto leave;
964 }
965 if (!md->ca_effective) {
966 rv = APR_ENOENT;
967 md_result_set(result, rv, "effective CA url not set");
968 goto leave;
969 }
970
971 all_creds = apr_array_make(d->p, 5, sizeof(md_credentials_t*));
972 for (i = 0; i < md_pkeys_spec_count(md->pks); ++i) {
974 if (APR_SUCCESS != (rv = md_creds_load(d->store, MD_SG_STAGING, name, pkspec, &creds, d->p))) {
975 md_result_printf(result, rv, "loading staged credentials #%d", i);
976 goto leave;
977 }
978 if (!creds->chain) {
979 rv = APR_ENOENT;
980 md_result_printf(result, rv, "no certificate in staged credentials #%d", i);
981 goto leave;
982 }
983 if (APR_SUCCESS != (rv = md_check_cert_and_pkey(creds->chain, creds->pkey))) {
984 md_result_printf(result, rv, "certificate and private key do not match in staged credentials #%d", i);
985 goto leave;
986 }
988 }
989
990 /* See if staging holds a new or modified account data */
991 rv = md_acme_acct_load(&acct, &acct_key, d->store, MD_SG_STAGING, name, d->p);
992 if (APR_STATUS_IS_ENOENT(rv)) {
993 acct = NULL;
994 acct_key = NULL;
995 rv = APR_SUCCESS;
996 }
997 else if (APR_SUCCESS != rv) {
998 md_result_set(result, rv, "loading staged account");
999 goto leave;
1000 }
1001
1002 md_result_activity_setn(result, "purging order information");
1003 md_acme_order_purge(d->store, d->p, MD_SG_STAGING, md, d->env);
1004
1005 md_result_activity_setn(result, "purging store tmp space");
1006 rv = md_store_purge(d->store, d->p, load_group, name);
1007 if (APR_SUCCESS != rv) {
1009 goto leave;
1010 }
1011
1012 if (acct) {
1013 md_acme_t *acme;
1014
1015 /* We may have STAGED the same account several times. This happens when
1016 * several MDs are renewed at once and need a new account. They will all store
1017 * the new account in their own STAGING area. By checking for accounts with
1018 * the same url, we save them all into a single one.
1019 */
1020 md_result_activity_setn(result, "saving staged account");
1021 id = md->ca_account;
1022 if (!id) {
1023 rv = md_acme_acct_id_for_md(&id, d->store, MD_SG_ACCOUNTS, md, d->p);
1024 if (APR_STATUS_IS_ENOENT(rv)) {
1025 id = NULL;
1026 }
1027 else if (APR_SUCCESS != rv) {
1028 md_result_set(result, rv, "error searching for existing account by url");
1029 goto leave;
1030 }
1031 }
1032
1033 if (APR_SUCCESS != (rv = md_acme_create(&acme, d->p, md->ca_effective,
1034 d->proxy_url, d->ca_file))) {
1035 md_result_set(result, rv, "error setting up acme");
1036 goto leave;
1037 }
1038
1039 if (APR_SUCCESS != (rv = md_acme_acct_save(d->store, d->p, acme, &id, acct, acct_key))) {
1040 md_result_set(result, rv, "error saving account");
1041 goto leave;
1042 }
1043 md->ca_account = id;
1044 }
1045 else if (!md->ca_account) {
1046 /* staging reused another account and did not create a new one. find
1047 * the account, if it is already there */
1048 rv = md_acme_acct_id_for_md(&id, d->store, MD_SG_ACCOUNTS, md, d->p);
1049 if (APR_SUCCESS == rv) {
1050 md->ca_account = id;
1051 }
1052 }
1053
1054 md_result_activity_setn(result, "saving staged md/privkey/pubcert");
1055 if (APR_SUCCESS != (rv = md_save(d->store, d->p, load_group, md, 1))) {
1056 md_result_set(result, rv, "writing md.json");
1057 goto leave;
1058 }
1059
1060 for (i = 0; i < all_creds->nelts; ++i) {
1062 if (APR_SUCCESS != (rv = md_creds_save(d->store, d->p, load_group, name, creds, 1))) {
1063 md_result_printf(result, rv, "writing credentials #%d", i);
1064 goto leave;
1065 }
1066 }
1067
1068 md_result_set(result, APR_SUCCESS, "saved staged data successfully");
1069
1070leave:
1072 return rv;
1073}
1074
1077{
1078 apr_status_t rv;
1079
1080 rv = acme_preload(d, group, d->md->name, result);
1082 return rv;
1083}
1084
1086{
1087 (void)p;
1088 if (!md->ca_urls || apr_is_empty_array(md->ca_urls)) {
1089 md->ca_urls = apr_array_make(p, 3, sizeof(const char *));
1090 APR_ARRAY_PUSH(md->ca_urls, const char*) = MD_ACME_DEF_URL;
1091 }
1092 return APR_SUCCESS;
1093}
1094
1100
1102{
1103 (void)p;
1104 apr_hash_set(protos, MD_PROTO_ACME, sizeof(MD_PROTO_ACME)-1, &ACME_PROTO);
1105 return APR_SUCCESS;
1106}
APR-UTIL Buckets/Bucket Brigades.
APR Hash Tables.
APR general purpose library routines.
apr_size_t const unsigned char unsigned int unsigned int d
Definition apr_siphash.h:72
APR Strings library.
APR-UTIL URI Routines.
ap_vhost_iterate_conn_cb void * baton
Definition http_vhost.h:87
#define APR_EAGAIN
Definition apr_errno.h:730
#define APR_EGENERAL
Definition apr_errno.h:313
#define APR_INCOMPLETE
Definition apr_errno.h:452
#define APR_ENOENT
Definition apr_errno.h:662
#define APR_EINVAL
Definition apr_errno.h:711
unsigned int count
Definition apr_md5.h:152
#define APR_STATUS_IS_EINVAL(s)
Definition apr_errno.h:1266
#define APR_STATUS_IS_ENOENT(s)
Definition apr_errno.h:1246
apr_pool_t apr_dbd_t apr_dbd_results_t ** res
Definition apr_dbd.h:287
apr_datum_t * pkey
Definition apr_dbm.h:158
apr_size_t size
#define APR_SUCCESS
Definition apr_errno.h:225
int apr_status_t
Definition apr_errno.h:44
apr_array_header_t ** result
apr_interval_time_t t
const char apr_uint32_t * id
#define apr_pcalloc(p, size)
Definition apr_pools.h:465
#define APR_ARRAY_PUSH(ary, type)
Definition apr_tables.h:150
int nelts
Definition apr_tables.h:122
#define APR_ARRAY_IDX(ary, i, type)
Definition apr_tables.h:141
const apr_array_header_t * first
Definition apr_tables.h:207
#define APR_RFC822_DATE_LEN
Definition apr_time.h:186
apr_int64_t apr_time_t
Definition apr_time.h:45
#define apr_time_from_sec(sec)
Definition apr_time.h:78
#define MD_KEY_CHALLENGE
Definition md.h:127
@ MD_S_COMPLETE
Definition md.h:55
@ MD_S_MISSING_INFORMATION
Definition md.h:58
#define MD_KEY_CSR
Definition md.h:134
#define MD_KEY_CMD_DNS01
Definition md.h:129
md_t * md_copy(apr_pool_t *p, const md_t *src)
Definition md_core.c:210
void md_acme_clear_acct(md_acme_t *acme)
Definition md_acme.c:526
apr_status_t md_acme_use_acct_for_md(md_acme_t *acme, struct md_store_t *store, apr_pool_t *p, const char *acct_id, const md_t *md)
Definition md_acme.c:567
const char * md_acme_acct_id_get(md_acme_t *acme)
Definition md_acme.c:533
apr_status_t md_acme_req_body_init(md_acme_req_t *req, md_json_t *payload)
Definition md_acme.c:250
apr_status_t md_acme_setup(md_acme_t *acme, md_result_t *result)
Definition md_acme.c:756
apr_status_t md_acme_GET(md_acme_t *acme, const char *url, md_acme_req_init_cb *on_init, md_acme_req_json_cb *on_json, md_acme_req_res_cb *on_res, md_acme_req_err_cb *on_err, void *baton)
Definition md_acme.c:455
apr_status_t md_acme_POST(md_acme_t *acme, const char *url, md_acme_req_init_cb *on_init, md_acme_req_json_cb *on_json, md_acme_req_res_cb *on_res, md_acme_req_err_cb *on_err, void *baton)
Definition md_acme.c:432
apr_status_t md_acme_create(md_acme_t **pacme, apr_pool_t *p, const char *url, const char *proxy_url, const char *ca_file)
Definition md_acme.c:620
void md_acme_report_result(md_acme_t *acme, apr_status_t rv, struct md_result_t *result)
Definition md_acme.c:478
#define MD_ACME_VERSION_MAJOR(i)
Definition md_acme.h:42
#define MD_PROTO_ACME
Definition md_acme.h:33
apr_status_t md_acme_acct_save(md_store_t *store, apr_pool_t *p, md_acme_t *acme, const char **pid, md_acme_acct_t *acct, md_pkey_t *acct_key)
apr_status_t md_acme_acct_load(md_acme_acct_t **pacct, md_pkey_t **ppkey, md_store_t *store, md_store_group_t group, const char *name, apr_pool_t *p)
apr_status_t md_acme_acct_validate(md_acme_t *acme, md_store_t *store, apr_pool_t *p)
apr_status_t md_acme_acct_id_for_md(const char **pid, md_store_t *store, md_store_group_t group, const md_t *md, apr_pool_t *p)
md_json_t * md_acme_acct_to_json(md_acme_acct_t *acct, apr_pool_t *p)
apr_status_t md_acme_find_acct_for_md(md_acme_t *acme, md_store_t *store, const md_t *md)
apr_status_t md_acme_acct_register(md_acme_t *acme, md_store_t *store, const md_t *md, apr_pool_t *p)
#define MD_FN_ACCT_KEY
#define MD_FN_ACCOUNT
#define MD_AUTHZ_TYPE_DNS01
#define MD_AUTHZ_TYPE_HTTP01
#define MD_AUTHZ_TYPE_TLSALPN01
static apr_status_t acme_driver_preload_init(md_proto_driver_t *d, md_result_t *result)
static apr_status_t on_add_chain(md_acme_t *acme, const md_http_response_t *res, void *baton)
static apr_status_t use_staged_acct(md_acme_t *acme, struct md_store_t *store, const md_t *md, apr_pool_t *p)
static apr_status_t acme_complete_md(md_t *md, apr_pool_t *p)
static apr_status_t acme_driver_renew(md_proto_driver_t *d, md_result_t *result)
static apr_status_t acme_driver_init(md_proto_driver_t *d, md_result_t *result)
apr_status_t md_acme_protos_add(apr_hash_t *protos, apr_pool_t *p)
static apr_status_t get_chain(void *baton, int attempt)
static apr_status_t csr_req(md_acme_t *acme, const md_http_response_t *res, void *baton)
static apr_status_t add_http_certs(apr_array_header_t *chain, apr_pool_t *p, const md_http_response_t *res)
static apr_status_t save_acct_staged(md_acme_t *acme, md_store_t *store, const char *md_name, apr_pool_t *p)
static apr_status_t on_init_csr_req(md_acme_req_t *req, void *baton)
static md_proto_t ACME_PROTO
static apr_status_t get_cert(void *baton, int attempt)
apr_status_t md_acme_drive_setup_cred_chain(md_proto_driver_t *d, md_result_t *result)
static void get_up_link(md_proto_driver_t *d, apr_table_t *headers)
static apr_status_t acme_driver_preload(md_proto_driver_t *d, md_store_group_t group, md_result_t *result)
static apr_status_t acme_preload(md_proto_driver_t *d, md_store_group_t load_group, const char *name, md_result_t *result)
apr_status_t md_acme_drive_cert_poll(md_proto_driver_t *d, int only_once)
static apr_status_t load_missing_creds(md_proto_driver_t *d)
apr_status_t md_acme_drive_set_acct(md_proto_driver_t *d, md_result_t *result)
static apr_status_t ad_chain_retrieve(md_proto_driver_t *d)
static apr_status_t on_add_cert(md_acme_t *acme, const md_http_response_t *res, void *baton)
static apr_status_t acme_renew(md_proto_driver_t *d, md_result_t *result)
apr_status_t md_acme_order_save(struct md_store_t *store, apr_pool_t *p, md_store_group_t group, const char *md_name, md_acme_order_t *authz_set, int create)
apr_status_t md_acme_order_purge(md_store_t *store, apr_pool_t *p, md_store_group_t group, const md_t *md, apr_table_t *env)
apr_status_t md_acmev2_drive_renew(md_acme_driver_t *ad, md_proto_driver_t *d, md_result_t *result)
apr_status_t md_cert_chain_read_http(struct apr_array_header_t *chain, apr_pool_t *p, const struct md_http_response_t *res)
Definition md_crypt.c:1540
md_pkey_spec_t * md_pkeys_spec_get(const md_pkeys_spec_t *pks, int index)
Definition md_crypt.c:562
const char * md_pkey_spec_name(const md_pkey_spec_t *spec)
Definition md_crypt.c:520
apr_time_t md_cert_get_not_before(const md_cert_t *cert)
Definition md_crypt.c:1217
apr_status_t md_cert_req_create(const char **pcsr_der_64, const char *name, apr_array_header_t *domains, int must_staple, md_pkey_t *pkey, apr_pool_t *p)
Definition md_crypt.c:1779
int md_pkeys_spec_count(const md_pkeys_spec_t *pks)
Definition md_crypt.c:555
apr_status_t md_pkey_gen(md_pkey_t **ppkey, apr_pool_t *p, md_pkey_spec_t *spec)
Definition md_crypt.c:933
apr_status_t md_check_cert_and_pkey(struct apr_array_header_t *certs, md_pkey_t *pkey)
Definition md_crypt.c:2131
apr_status_t md_cert_read_http(md_cert_t **pcert, apr_pool_t *p, const md_http_response_t *res)
Definition md_crypt.c:1498
apr_pool_t * p
Definition md_event.c:32
md_json_t * md_json_create(apr_pool_t *pool)
Definition md_json.c:92
apr_status_t md_json_sets(const char *value, md_json_t *json,...)
Definition md_json.c:430
void md_log_perror(const char *file, int line, md_log_level_t level, apr_status_t rv, apr_pool_t *p, const char *fmt,...)
Definition md_log.c:68
int md_log_is_level(apr_pool_t *p, md_log_level_t level)
Definition md_log.c:60
#define MD_LOG_MARK
Definition md_log.h:39
@ MD_LOG_TRACE1
Definition md_log.h:29
@ MD_LOG_ERR
Definition md_log.h:24
@ MD_LOG_DEBUG
Definition md_log.h:28
@ MD_LOG_INFO
Definition md_log.h:27
apr_time_t md_reg_valid_until(md_reg_t *reg, const md_t *md, apr_pool_t *p)
Definition md_reg.c:650
void md_result_activity_printf(md_result_t *result, const char *fmt,...)
Definition md_result.c:82
void md_result_activity_setn(md_result_t *result, const char *activity)
Definition md_result.c:74
void md_result_dup(md_result_t *dest, const md_result_t *src)
Definition md_result.c:216
void md_result_printf(md_result_t *result, apr_status_t status, const char *fmt,...)
Definition md_result.c:126
void md_result_log(md_result_t *result, unsigned int level)
Definition md_result.c:227
void md_result_set(md_result_t *result, apr_status_t status, const char *detail)
Definition md_result.c:91
void md_result_delay_set(md_result_t *result, apr_time_t ready_at)
Definition md_result.c:138
apr_status_t md_store_save(md_store_t *store, apr_pool_t *p, md_store_group_t group, const char *name, const char *aspect, md_store_vtype_t vtype, void *data, int create)
Definition md_store.c:78
apr_status_t md_creds_save(md_store_t *store, apr_pool_t *p, md_store_group_t group, const char *name, md_credentials_t *creds, int create)
Definition md_store.c:329
apr_status_t md_creds_load(md_store_t *store, md_store_group_t group, const char *name, md_pkey_spec_t *spec, md_credentials_t **pcreds, apr_pool_t *p)
Definition md_store.c:311
apr_status_t md_pubcert_load(md_store_t *store, md_store_group_t group, const char *name, md_pkey_spec_t *spec, struct apr_array_header_t **ppubcert, apr_pool_t *p)
Definition md_store.c:295
apr_status_t md_pubcert_save(md_store_t *store, apr_pool_t *p, md_store_group_t group, const char *name, md_pkey_spec_t *spec, struct apr_array_header_t *pubcert, int create)
Definition md_store.c:303
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)
Definition md_store.c:245
apr_status_t md_save(md_store_t *store, apr_pool_t *p, md_store_group_t group, md_t *md, int create)
Definition md_store.c:211
apr_status_t md_store_purge(md_store_t *store, apr_pool_t *p, md_store_group_t group, const char *name)
Definition md_store.c:93
apr_status_t md_pkey_save(md_store_t *store, apr_pool_t *p, md_store_group_t group, const char *name, md_pkey_spec_t *spec, struct md_pkey_t *pkey, int create)
Definition md_store.c:288
apr_status_t md_load(md_store_t *store, md_store_group_t group, const char *name, md_t **pmd, apr_pool_t *p)
Definition md_store.c:179
apr_status_t md_pkey_load(md_store_t *store, md_store_group_t group, const char *name, md_pkey_spec_t *spec, md_pkey_t **ppkey, apr_pool_t *p)
Definition md_store.c:281
md_store_group_t
Definition md_store.h:62
@ MD_SG_ACCOUNTS
Definition md_store.h:64
@ MD_SG_STAGING
Definition md_store.h:67
@ MD_SG_DOMAINS
Definition md_store.h:66
@ MD_SV_PKEY
Definition md_store.h:56
@ MD_SV_JSON
Definition md_store.h:54
const char * md_duration_format(apr_pool_t *p, apr_interval_time_t duration)
Definition md_time.c:154
const char * md_link_find_relation(const apr_table_t *headers, apr_pool_t *pool, const char *relation)
Definition md_util.c:1533
apr_status_t md_util_try(md_util_try_fn *fn, void *baton, int ignore_errs, apr_interval_time_t timeout, apr_interval_time_t start_delay, apr_interval_time_t max_delay, int backoff)
Definition md_util.c:1040
int md_array_is_empty(const struct apr_array_header_t *array)
Definition md_util.c:235
int md_array_str_eq(const struct apr_array_header_t *a1, const struct apr_array_header_t *a2, int case_sensitive)
Definition md_util.c:268
int md_array_str_index(const apr_array_header_t *array, const char *s, int start, int case_sensitive)
Definition md_util.c:250
const char * md_util_parse_ct(apr_pool_t *pool, const char *cth)
Definition md_util.c:1547
apr_array_header_t * md_array_str_remove(apr_pool_t *p, apr_array_header_t *src, const char *exclude, int case_sensitive)
Definition md_util.c:318
apr_array_header_t * md_dns_make_minimal(apr_pool_t *p, apr_array_header_t *domains)
Definition md_util.c:870
#define MD_ACME_DEF_URL
Definition md_version.h:40
static apr_file_t * out
Definition mod_info.c:85
return NULL
Definition mod_so.c:359
int i
Definition mod_so.c:347
char * name
const char * chain_up_link
struct md_credentials_t * cred
apr_pool_t * p
Definition md_acme.h:249
apr_pool_t * p
Definition md_acme.h:97
struct md_acme_acct_t * acct
Definition md_acme.h:103
struct md_pkey_t * acct_key
Definition md_acme.h:104
const char * acct_id
Definition md_acme.h:102
struct apr_array_header_t * chain
Definition md_store.h:269
struct md_pkey_spec_t * spec
Definition md_store.h:267
struct md_pkey_t * pkey
Definition md_store.h:268
Definition md.h:76
const char * name
Definition md.h:77
const char * ca_effective
Definition md.h:87
struct apr_array_header_t * ca_urls
Definition md.h:86
struct md_pkeys_spec_t * pks
Definition md.h:81
const char * ca_eab_kid
Definition md.h:93
const char * ca_account
Definition md.h:88
const char * ca_agreement
Definition md.h:89
struct apr_array_header_t * contacts
Definition md.h:79
static apr_time_t now
Definition testtime.c:33
apr_status_t apr_rfc822_date(char *date_str, apr_time_t t)
Definition timestr.c:42