Apache HTTPD
md_ocsp.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 <stdio.h>
19#include <stdlib.h>
20
21#include <apr_lib.h>
22#include <apr_buckets.h>
23#include <apr_hash.h>
24#include <apr_time.h>
25#include <apr_date.h>
26#include <apr_strings.h>
27#include <apr_thread_mutex.h>
28
29#include <openssl/err.h>
30#include <openssl/evp.h>
31#include <openssl/ocsp.h>
32#include <openssl/pem.h>
33#include <openssl/x509v3.h>
34
35#include "md.h"
36#include "md_crypt.h"
37#include "md_event.h"
38#include "md_json.h"
39#include "md_log.h"
40#include "md_http.h"
41#include "md_json.h"
42#include "md_result.h"
43#include "md_status.h"
44#include "md_store.h"
45#include "md_util.h"
46#include "md_ocsp.h"
47
48#define MD_OCSP_ID_LENGTH SHA_DIGEST_LENGTH
49
63
67 const char *hexid;
68 const char *hex_sha256;
70 const char *responder_url;
71
72 apr_time_t next_run; /* when the responder shall be asked again */
73 int errors; /* consecutive failed attempts */
74
78
82
83 const char *md_name;
84 const char *file_name;
85
88};
89
95
96static void md_openssl_free(void *d)
97{
99}
100
102{
103 switch (stat) {
104 case MD_OCSP_CERT_ST_GOOD: return "good";
105 case MD_OCSP_CERT_ST_REVOKED: return "revoked";
106 default: return "unknown";
107 }
108}
109
111{
112 if (name && !strcmp("good", name)) return MD_OCSP_CERT_ST_GOOD;
113 if (name && !strcmp("revoked", name)) return MD_OCSP_CERT_ST_REVOKED;
115}
116
118{
119 unsigned char iddata[SHA_DIGEST_LENGTH];
121 unsigned int ulen = 0;
122
123 md_data_null(id);
124 if (X509_digest(x, EVP_sha1(), iddata, &ulen) != 1) {
125 return APR_EGENERAL;
126 }
127 md_data_assign_pcopy(id, (const char*)iddata, ulen, p);
128 return APR_SUCCESS;
129}
130
132{
133 if (ostat->ocsp_req) {
135 ostat->ocsp_req = NULL;
136 }
137 md_data_clear(&ostat->req_der);
138}
139
140static int ostat_cleanup(void *ctx, const void *key, apr_ssize_t klen, const void *val)
141{
142 md_ocsp_reg_t *reg = ctx;
144
145 (void)reg;
146 (void)key;
147 (void)klen;
148 ostat_req_cleanup(ostat);
149 if (ostat->certid) {
150 OCSP_CERTID_free(ostat->certid);
151 ostat->certid = NULL;
152 }
153 md_data_clear(&ostat->resp_der);
154 return 1;
155}
156
164
167{
168 apr_status_t rv;
169
170 rv = md_data_assign_copy(&ostat->resp_der, der->data, der->len);
171 if (APR_SUCCESS != rv) goto cleanup;
172
173 ostat->resp_stat = stat;
174 ostat->resp_valid = *valid;
175 ostat->resp_mtime = mtime;
176
177 ostat->errors = 0;
179 &ostat->resp_valid, &ostat->reg->renew_window).start;
180
181cleanup:
182 return rv;
183}
184
186 md_data_t *resp_der, md_timeperiod_t *resp_valid,
187 md_json_t *json, apr_pool_t *p)
188{
189 const char *s;
190 md_timeperiod_t valid;
192
193 memset(resp_der, 0, sizeof(*resp_der));
194 memset(resp_valid, 0, sizeof(*resp_valid));
196 if (s && *s) valid.start = apr_date_parse_rfc(s);
198 if (s && *s) valid.end = apr_date_parse_rfc(s);
200 if (!s || !*s) goto cleanup;
201 md_util_base64url_decode(resp_der, s, p);
203 *resp_valid = valid;
204 rv = APR_SUCCESS;
205cleanup:
206 return rv;
207}
208
210 const md_data_t *resp_der, const md_timeperiod_t *resp_valid,
211 apr_pool_t *p)
212{
213 const char *s = NULL;
214
215 if (resp_der->len > 0) {
218 if (s) md_json_sets(s, json, MD_KEY_STATUS, NULL);
219 md_json_set_timeperiod(resp_valid, json, MD_KEY_VALID, NULL);
220 }
221}
222
224{
225 md_store_t *store = ostat->reg->store;
229 md_data_t resp_der;
230 md_timeperiod_t resp_valid;
231 md_ocsp_cert_stat_t resp_stat;
232 /* Check if the store holds a newer response than the one we have */
233 mtime = md_store_get_modified(store, MD_SG_OCSP, ostat->md_name, ostat->file_name, ptemp);
234 if (mtime <= ostat->resp_mtime) goto cleanup;
235 rv = md_store_load_json(store, MD_SG_OCSP, ostat->md_name, ostat->file_name, &jprops, ptemp);
236 if (APR_SUCCESS != rv) goto cleanup;
237 rv = ostat_from_json(&resp_stat, &resp_der, &resp_valid, jprops, ptemp);
238 if (APR_SUCCESS != rv) goto cleanup;
239 rv = ostat_set(ostat, resp_stat, &resp_der, &resp_valid, mtime);
240 if (APR_SUCCESS != rv) goto cleanup;
241cleanup:
242 return rv;
243}
244
245
247 const md_timeperiod_t *resp_valid,
248 md_ocsp_status_t *ostat, apr_pool_t *ptemp)
249{
250 md_store_t *store = ostat->reg->store;
253 apr_status_t rv;
254
255 jprops = md_json_create(ptemp);
256 ostat_to_json(jprops, stat, resp_der, resp_valid, ptemp);
257 rv = md_store_save_json(store, ptemp, MD_SG_OCSP, ostat->md_name, ostat->file_name, jprops, 0);
258 if (APR_SUCCESS != rv) goto cleanup;
259 mtime = md_store_get_modified(store, MD_SG_OCSP, ostat->md_name, ostat->file_name, ptemp);
260 if (mtime) ostat->resp_mtime = mtime;
261cleanup:
262 return rv;
263}
264
266{
267 md_ocsp_reg_t *reg = data;
268
269 /* free all OpenSSL structures that we hold */
271 return APR_SUCCESS;
272}
273
275 const md_timeslice_t *renew_window,
276 const char *user_agent, const char *proxy_url,
277 apr_time_t min_delay)
278{
279 md_ocsp_reg_t *reg;
281
282 reg = apr_palloc(p, sizeof(*reg));
283 if (!reg) {
284 rv = APR_ENOMEM;
285 goto cleanup;
286 }
287 reg->p = p;
288 reg->store = store;
289 reg->user_agent = user_agent;
290 reg->proxy_url = proxy_url;
293 reg->renew_window = *renew_window;
294 reg->min_delay = min_delay;
295
297 if (APR_SUCCESS != rv) goto cleanup;
298
300cleanup:
301 *preg = (APR_SUCCESS == rv)? reg : NULL;
302 return rv;
303}
304
306 md_cert_t *cert, md_cert_t *issuer, const md_t *md)
307{
308 md_ocsp_status_t *ostat;
309 const char *name;
312
313 /* Called during post_config. no mutex protection needed */
314 name = md? md->name : MD_OTHER;
316 "md[%s]: priming OCSP status", name);
317
318 rv = md_ocsp_init_id(&id, reg->p, cert);
319 if (APR_SUCCESS != rv) goto cleanup;
320
321 ostat = apr_hash_get(reg->ostat_by_id, id.data, (apr_ssize_t)id.len);
322 if (ostat) goto cleanup; /* already seen it, cert is used in >1 server_rec */
323
324 ostat = apr_pcalloc(reg->p, sizeof(*ostat));
325 ostat->id = id;
326 ostat->reg = reg;
327 ostat->md_name = name;
328 md_data_to_hex(&ostat->hexid, 0, reg->p, &ostat->id);
329 ostat->file_name = apr_psprintf(reg->p, "ocsp-%s.json", ostat->hexid);
330 rv = md_cert_to_sha256_fingerprint(&ostat->hex_sha256, cert, reg->p);
331 if (APR_SUCCESS != rv) goto cleanup;
332
334 "md[%s]: getting ocsp responder from cert", name);
336 if (APR_SUCCESS != rv) {
338 "md[%s]: certificate with serial %s has no OCSP responder URL",
340 goto cleanup;
341 }
342
344 if (!ostat->certid) {
345 rv = APR_EGENERAL;
347 "md[%s]: unable to create OCSP certid for certificate with serial %s",
349 goto cleanup;
350 }
351
352 /* See, if we have something in store */
353 ocsp_status_refresh(ostat, reg->p);
355 "md[%s]: adding ocsp info (responder=%s)",
356 name, ostat->responder_url);
357 apr_hash_set(reg->ostat_by_id, ostat->id.data, (apr_ssize_t)ostat->id.len, ostat);
358 if (ext_id) {
360
361 id_map = apr_pcalloc(reg->p, sizeof(*id_map));
362 id_map->id = id;
363 md_data_assign_pcopy(&id_map->external_id, ext_id, ext_id_len, reg->p);
364 /* check for collision/uniqness? */
365 apr_hash_set(reg->id_by_external_id, id_map->external_id.data,
366 (apr_ssize_t)id_map->external_id.len, id_map);
367 }
368 rv = APR_SUCCESS;
369cleanup:
370 return rv;
371}
372
374 const char *ext_id, apr_size_t ext_id_len,
375 apr_pool_t *p, const md_t *md)
376{
377 md_ocsp_status_t *ostat;
378 const char *name;
381 const char *id;
382 apr_size_t id_len;
383 int locked = 0;
384
385 (void)p;
386 (void)md;
387 name = md? md->name : MD_OTHER;
389 "md[%s]: OCSP, get_status", name);
390
392 id = id_map? id_map->id.data : ext_id;
393 id_len = id_map? id_map->id.len : ext_id_len;
394 ostat = apr_hash_get(reg->ostat_by_id, id, (apr_ssize_t)id_len);
395 if (!ostat) {
396 rv = APR_ENOENT;
397 goto cleanup;
398 }
399
400 /* While the ostat instance itself always exists, the response data it holds
401 * may vary over time and we need locked access to make a copy. */
403 locked = 1;
404
405 if (ostat->resp_der.len <= 0) {
406 /* No response known, check store for new response. */
407 ocsp_status_refresh(ostat, p);
408 if (ostat->resp_der.len <= 0) {
410 "md[%s]: OCSP, no response available", name);
411 cb(NULL, 0, userdata);
412 goto cleanup;
413 }
414 }
415 /* We have a response */
416 if (ostat_should_renew(ostat)) {
417 /* But it is up for renewal. A watchdog should be busy with
418 * retrieving a new one. In case of outages, this might take
419 * a while, however. Pace the frequency of checks with the
420 * urgency of a new response based on the remaining time. */
423
424 /* every hour, every minute, every second */
426 apr_time_from_sec(60 * 60) : ((secs >= 60)?
428 if ((apr_time_now() - ostat->resp_last_check) >= waiting_time) {
429 ostat->resp_last_check = apr_time_now();
430 ocsp_status_refresh(ostat, p);
431 }
432 }
433
434 cb((const unsigned char*)ostat->resp_der.data, ostat->resp_der.len, userdata);
436 "md[%s]: OCSP, provided %ld bytes of response",
437 name, (long)ostat->resp_der.len);
438cleanup:
439 if (locked) apr_thread_mutex_unlock(reg->mutex);
440 return rv;
441}
442
445{
447 if (ostat->resp_der.len <= 0) {
448 /* No response known, check the store if out watchdog retrieved one
449 * in the meantime. */
450 ocsp_status_refresh(ostat, p);
451 }
452 *pvalid = ostat->resp_valid;
453 *pstat = ostat->resp_stat;
455}
456
458 md_ocsp_reg_t *reg, const md_cert_t *cert,
459 apr_pool_t *p, const md_t *md)
460{
461 md_ocsp_status_t *ostat;
462 const char *name;
463 apr_status_t rv;
464 md_timeperiod_t valid;
467
468 (void)p;
469 (void)md;
470 name = md? md->name : MD_OTHER;
471 memset(&valid, 0, sizeof(valid));
474 "md[%s]: OCSP, get_status", name);
475
476 rv = md_ocsp_init_id(&id, p, cert);
477 if (APR_SUCCESS != rv) goto cleanup;
478
479 ostat = apr_hash_get(reg->ostat_by_id, id.data, (apr_ssize_t)id.len);
480 if (!ostat) {
481 rv = APR_ENOENT;
482 goto cleanup;
483 }
484 ocsp_get_meta(&stat, &valid, reg, ostat, p);
485cleanup:
486 *pstat = stat;
487 *pvalid = valid;
488 return rv;
489}
490
495
496static const char *certid_as_hex(const OCSP_CERTID *certid, apr_pool_t *p)
497{
498 md_data_t der;
499 const char *hex;
500
501 memset(&der, 0, sizeof(der));
502 der.len = (apr_size_t)i2d_OCSP_CERTID((OCSP_CERTID*)certid, (unsigned char**)&der.data);
504 md_data_to_hex(&hex, 0, p, &der);
505 md_data_clear(&der);
506 return hex;
507}
508
509static const char *certid_summary(const OCSP_CERTID *certid, apr_pool_t *p)
510{
511 const char *serial, *issuer, *key, *s;
515 BIGNUM *bn;
517
518 serial = issuer = key = "???";
520 if (aname_hash) {
521 data.len = (apr_size_t)aname_hash->length;
522 data.data = (const char*)aname_hash->data;
523 md_data_to_hex(&issuer, 0, p, &data);
524 }
525 if (akey_hash) {
526 data.len = (apr_size_t)akey_hash->length;
527 data.data = (const char*)akey_hash->data;
528 md_data_to_hex(&key, 0, p, &data);
529 }
530 if (aserial) {
532 s = BN_bn2hex(bn);
533 serial = apr_pstrdup(p, s);
534 OPENSSL_free((void*)bn);
535 OPENSSL_free((void*)s);
536 }
537 return apr_psprintf(p, "certid[der=%s, issuer=%s, key=%s, serial=%s]",
538 certid_as_hex(certid, p), issuer, key, serial);
539}
540
541static const char *certstatus_string(int status)
542{
543 switch (status) {
544 case V_OCSP_CERTSTATUS_GOOD: return "good";
545 case V_OCSP_CERTSTATUS_REVOKED: return "revoked";
546 case V_OCSP_CERTSTATUS_UNKNOWN: return "unknown";
547 default: return "???";
548 }
549
550}
551
553{
554 const OCSP_CERTID *certid;
555 int status, reason = 0;
557 md_timeperiod_t valid;
558
559#if OPENSSL_VERSION_NUMBER < 0x10100000L \
560 || (defined(LIBRESSL_VERSION_NUMBER) \
561 && LIBRESSL_VERSION_NUMBER < 0x2070000f)
562 certid = resp->certId;
563#else
565#endif
569
570 return apr_psprintf(p, "ocsp-single-resp[%s, status=%s, reason=%d, valid=%s]",
571 certid_summary(certid, p),
573 md_timeperiod_print(p, &valid));
574}
575
582
584{
585 md_ocsp_update_t *update = baton;
586 md_ocsp_status_t *ostat = update->ostat;
587 md_http_request_t *req = resp->req;
592 int n, breason = 0, bstatus;
594 md_data_t der, new_der;
595 md_timeperiod_t valid;
597
598 der.data = new_der.data = NULL;
599 der.len = new_der.len = 0;
600
601 md_result_activity_printf(update->result, "status of certid %s, reading response",
602 ostat->hexid);
603 if (APR_SUCCESS != (rv = apr_brigade_pflatten(resp->body, (char**)&der.data,
604 &der.len, req->pool))) {
605 goto cleanup;
606 }
607 if (NULL == (ocsp_resp = d2i_OCSP_RESPONSE(NULL, (const unsigned char**)&der.data,
608 (long)der.len))) {
609 rv = APR_EINVAL;
610
611 md_result_set(update->result, rv,
612 apr_psprintf(req->pool, "req[%d] response body does not parse as "
613 "OCSP response, status=%d, body brigade length=%ld",
614 resp->req->id, resp->status, (long)der.len));
616 goto cleanup;
617 }
618 /* got a response! but what does it say? */
621 rv = APR_EINVAL;
622 md_result_printf(update->result, rv, "OCSP response status is, unsuccessfully, %d", n);
624 goto cleanup;
625 }
627 if (!basic_resp) {
628 rv = APR_EINVAL;
629 md_result_set(update->result, rv, "OCSP response has no basicresponse");
631 goto cleanup;
632 }
633 /* The notion of nonce enabled freshness in OCSP responses, e.g. that the response
634 * contains the signed nonce we sent to the responder, does not scale well. Responders
635 * like to return cached response bytes and therefore do not add a nonce to it.
636 * So, in reality, we can only detect a mismatch when present and otherwise have
637 * to accept it. */
638 switch ((n = OCSP_check_nonce(ostat->ocsp_req, basic_resp))) {
639 case 1:
641 "req[%d]: OCSP response nonce does match", req->id);
642 break;
643 case 0:
644 rv = APR_EINVAL;
645 md_result_printf(update->result, rv, "OCSP nonce mismatch in response", n);
647 goto cleanup;
648
649 case -1:
651 "req[%d]: OCSP response did not return the nonce", req->id);
652 break;
653 default:
654 break;
655 }
656
658 &breason, NULL, &bup, &bnextup)) {
659 const char *prefix, *slist = "", *sep = "";
660 int i;
661
662 rv = APR_EINVAL;
663 prefix = apr_psprintf(req->pool, "OCSP response, no matching status reported for %s",
664 certid_summary(ostat->certid, req->pool));
665 for (i = 0; i < OCSP_resp_count(basic_resp); ++i) {
667 slist = apr_psprintf(req->pool, "%s%s%s", slist, sep,
669 sep = ", ";
670 }
671 md_result_printf(update->result, rv, "%s, status list [%s]", prefix, slist);
673 goto cleanup;
674 }
676 rv = APR_ENOENT;
677 md_result_set(update->result, rv, "OCSP basicresponse says cert is unknown");
679 goto cleanup;
680 }
681
682 /* Coming here, we have a response for our certid and it is either GOOD
683 * or REVOKED. Both cases we want to remember and use in stapling. */
684 n = i2d_OCSP_RESPONSE(ocsp_resp, (unsigned char**)&new_der.data);
685 if (n <= 0) {
686 rv = APR_EGENERAL;
687 md_result_set(update->result, rv, "error DER encoding OCSP response");
689 goto cleanup;
690 }
691 new_der.len = (apr_size_t)n;
692 new_der.free_data = md_openssl_free;
695 if (bnextup) {
697 }
698 else {
699 /* nextUpdate not set; default to 12 hours.
700 * Refresh attempts will be started some time earlier. */
701 valid.end = valid.start + apr_time_from_sec(MD_SECS_PER_DAY / 2);
702 }
703
704 /* First, update the instance with a copy */
706 ostat_set(ostat, nstat, &new_der, &valid, apr_time_now());
708
709 /* Next, save the original response */
710 rv = ocsp_status_save(nstat, &new_der, &valid, ostat, req->pool);
711 if (APR_SUCCESS != rv) {
712 md_result_set(update->result, rv, "error saving OCSP status");
714 goto cleanup;
715 }
716
717 md_result_printf(update->result, rv, "certificate status is %s, status valid %s",
718 (nstat == MD_OCSP_CERT_ST_GOOD)? "GOOD" : "REVOKED",
719 md_timeperiod_print(req->pool, &ostat->resp_valid));
721
722cleanup:
726 return rv;
727}
728
730 void *baton)
731{
732 md_ocsp_update_t *update = baton;
733 md_ocsp_status_t *ostat = update->ostat;
734
735 (void)req;
736 md_job_end_run(update->job, update->result);
737 if (APR_SUCCESS != status) {
738 ++ostat->errors;
739 ostat->next_run = apr_time_now() + md_job_delay_on_errors(update->job, ostat->errors, NULL);
740 md_result_printf(update->result, status, "OCSP status update failed (%d. time)",
741 ostat->errors);
743 md_job_log_append(update->job, "ocsp-error",
744 update->result->problem, update->result->detail);
745 md_event_holler("ocsp-errored", update->job->mdomain, update->job, update->result, update->p);
746 goto cleanup;
747 }
748 md_event_holler("ocsp-renewed", update->job->mdomain, update->job, update->result, update->p);
749
750cleanup:
751 md_job_save(update->job, update->result, update->p);
752 ostat_req_cleanup(ostat);
753 return APR_SUCCESS;
754}
755
763
765{
766 OCSP_REQUEST *req = NULL;
769
770 req = OCSP_REQUEST_new();
771 if (!req) goto cleanup;
772 id_copy = OCSP_CERTID_dup(certid);
773 if (!id_copy) goto cleanup;
774 if (!OCSP_request_add0_id(req, id_copy)) goto cleanup;
775 id_copy = NULL;
776 OCSP_request_add1_nonce(req, 0, -1);
777 rv = APR_SUCCESS;
778cleanup:
780 if (APR_SUCCESS != rv && req) {
782 req = NULL;
783 }
784 *pocsp_req = req;
785 return rv;
786}
787
789{
790 int len;
791
793 len = i2d_OCSP_REQUEST(ocsp_req, (unsigned char**)&d->data);
794 if (len < 0) return APR_ENOMEM;
795 d->len = (apr_size_t)len;
796 d->free_data = md_openssl_free;
797 return APR_SUCCESS;
798}
799
801 md_http_t *http, int in_flight)
802{
804 md_ocsp_update_t *update, **pupdate;
805 md_ocsp_status_t *ostat;
806 md_http_request_t *req = NULL;
808 apr_table_t *headers;
809
810 if (in_flight < ctx->max_parallel) {
811 pupdate = apr_array_pop(ctx->todos);
812 if (pupdate) {
813 update = *pupdate;
814 ostat = update->ostat;
815
816 update->job = md_ocsp_job_make(ctx->reg, ostat->md_name, update->p);
817 md_job_load(update->job);
818 md_job_start_run(update->job, update->result, ctx->reg->store);
819
820 if (!ostat->ocsp_req) {
821 rv = ocsp_req_make(&ostat->ocsp_req, ostat->certid);
822 if (APR_SUCCESS != rv) goto cleanup;
823 }
824 if (0 == ostat->req_der.len) {
825 rv = ocsp_req_assign_der(&ostat->req_der, ostat->ocsp_req);
826 if (APR_SUCCESS != rv) goto cleanup;
827 }
828 md_result_activity_printf(update->result, "status of certid %s, "
829 "contacting %s", ostat->hexid, ostat->responder_url);
830 headers = apr_table_make(ctx->ptemp, 5);
831 apr_table_set(headers, "Expect", "");
832 rv = md_http_POSTd_create(&req, http, ostat->responder_url, headers,
833 "application/ocsp-request", &ostat->req_der);
834 if (APR_SUCCESS != rv) goto cleanup;
837 rv = APR_SUCCESS;
839 "scheduling OCSP request[%d] for %s, %d request in flight",
840 req->id, ostat->md_name, in_flight);
841 }
842 }
843cleanup:
844 *preq = (APR_SUCCESS == rv)? req : NULL;
845 return rv;
846}
847
848static int select_updates(void *baton, const void *key, apr_ssize_t klen, const void *val)
849{
852 md_ocsp_update_t *update;
853
854 (void)key;
855 (void)klen;
856 if (ostat->next_run <= ctx->time) {
857 update = apr_pcalloc(ctx->ptemp, sizeof(*update));
858 update->p = ctx->ptemp;
859 update->ostat = ostat;
860 update->result = md_result_md_make(update->p, ostat->md_name);
861 update->job = NULL;
862 APR_ARRAY_PUSH(ctx->todos, md_ocsp_update_t*) = update;
863 }
864 return 1;
865}
866
867static int select_next_run(void *baton, const void *key, apr_ssize_t klen, const void *val)
868{
871
872 (void)key;
873 (void)klen;
874 if (ostat->next_run < ctx->time && ostat->next_run > apr_time_now()) {
875 ctx->time = ostat->next_run;
876 }
877 return 1;
878}
879
881{
883 md_http_t *http;
885
886 (void)p;
887 (void)pnext_run;
888
889 ctx.reg = reg;
890 ctx.ptemp = ptemp;
891 ctx.todos = apr_array_make(ptemp, (int)md_ocsp_count(reg), sizeof(md_ocsp_status_t*));
892 ctx.max_parallel = 6; /* the magic number in HTTP */
893
894 /* Create a list of update tasks that are needed now or in the next minute */
895 ctx.time = apr_time_now() + apr_time_from_sec(60);;
898 "OCSP status updates due: %d", ctx.todos->nelts);
899 if (!ctx.todos->nelts) goto cleanup;
900
901 rv = md_http_create(&http, ptemp, reg->user_agent, reg->proxy_url);
902 if (APR_SUCCESS != rv) goto cleanup;
903
904 rv = md_http_multi_perform(http, next_todo, &ctx);
905
906cleanup:
907 /* When do we need to run next? *pnext_run contains the planned schedule from
908 * the watchdog. We can make that earlier if we need it. */
909 ctx.time = *pnext_run;
911
912 /* sanity check and return */
913 if (ctx.time < apr_time_now()) ctx.time = apr_time_now() + apr_time_from_sec(1);
914 *pnext_run = ctx.time;
915
916 if (APR_SUCCESS != rv && APR_ENOENT != rv) {
917 md_log_perror(MD_LOG_MARK, MD_LOG_DEBUG, rv, p, "ocsp_renew done");
918 }
919 return;
920}
921
923 apr_time_t timestamp)
924{
925 return md_store_remove_not_modified_since(reg->store, p, timestamp,
926 MD_SG_OCSP, "*", "ocsp*.json");
927}
928
936
937static int add_to_summary(void *baton, const void *key, apr_ssize_t klen, const void *val)
938{
942 md_timeperiod_t valid;
943
944 (void)key;
945 (void)klen;
946 ocsp_get_meta(&stat, &valid, ctx->reg, ostat, ctx->p);
947 switch (stat) {
948 case MD_OCSP_CERT_ST_GOOD: ++ctx->good; break;
949 case MD_OCSP_CERT_ST_REVOKED: ++ctx->revoked; break;
950 case MD_OCSP_CERT_ST_UNKNOWN: ++ctx->unknown; break;
951 }
952 return 1;
953}
954
956{
957 md_json_t *json;
959
960 memset(&ctx, 0, sizeof(ctx));
961 ctx.p = p;
962 ctx.reg = reg;
964
965 json = md_json_create(p);
966 md_json_setl(ctx.good+ctx.revoked+ctx.unknown, json, MD_KEY_TOTAL, NULL);
967 md_json_setl(ctx.good, json, MD_KEY_GOOD, NULL);
968 md_json_setl(ctx.revoked, json, MD_KEY_REVOKED, NULL);
969 md_json_setl(ctx.unknown, json, MD_KEY_UNKNOWN, NULL);
970 *pjson = json;
971}
972
975{
977}
978
984
986{
989 md_json_t *json, *jobj;
990 apr_status_t rv;
991
992 json = md_json_create(p);
993 md_json_sets(ostat->md_name, json, MD_KEY_DOMAIN, NULL);
994 md_json_sets(ostat->hexid, json, MD_KEY_ID, NULL);
995 ocsp_get_meta(&stat, &valid, reg, ostat, p);
1002 if ((MD_OCSP_CERT_ST_UNKNOWN == stat) || renewal.start < apr_time_now()) {
1003 /* We have no answer yet, or it should be in renew now. Add job information */
1004 rv = job_loadj(&jobj, ostat->md_name, reg, p);
1005 if (APR_SUCCESS == rv) {
1007 }
1008 }
1009 return json;
1010}
1011
1012static int add_ostat(void *baton, const void *key, apr_ssize_t klen, const void *val)
1013{
1015 const md_ocsp_status_t *ostat = val;
1016
1017 (void)key;
1018 (void)klen;
1019 APR_ARRAY_PUSH(ctx->ostats, const md_ocsp_status_t*) = ostat;
1020 return 1;
1021}
1022
1023static int md_ostat_cmp(const void *v1, const void *v2)
1024{
1025 int n;
1026 n = strcmp((*(md_ocsp_status_t**)v1)->md_name, (*(md_ocsp_status_t**)v2)->md_name);
1027 if (!n) {
1028 n = strcmp((*(md_ocsp_status_t**)v1)->hexid, (*(md_ocsp_status_t**)v2)->hexid);
1029 }
1030 return n;
1031}
1032
1034{
1035 md_json_t *json;
1037 md_ocsp_status_t *ostat;
1038 int i;
1039
1040 memset(&ctx, 0, sizeof(ctx));
1041 ctx.p = p;
1042 ctx.reg = reg;
1043 ctx.ostats = apr_array_make(p, (int)apr_hash_count(reg->ostat_by_id), sizeof(md_ocsp_status_t*));
1044 json = md_json_create(p);
1045
1047 qsort(ctx.ostats->elts, (size_t)ctx.ostats->nelts, sizeof(md_json_t*), md_ostat_cmp);
1048
1049 for (i = 0; i < ctx.ostats->nelts; ++i) {
1050 ostat = APR_ARRAY_IDX(ctx.ostats, i, md_ocsp_status_t*);
1051 md_json_addj(mk_jstat(ostat, reg, p), json, MD_KEY_OCSPS, NULL);
1052 }
1053 *pjson = json;
1054}
1055
1057{
1058 return md_job_make(p, ocsp->store, MD_SG_OCSP, mdomain, ocsp->min_delay);
1059}
int n
Definition ap_regex.h:278
const ap_regex_t * preg
Definition ap_regex.h:197
const char apr_size_t len
Definition ap_regex.h:187
APR-UTIL Buckets/Bucket Brigades.
APR-UTIL date routines.
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 Thread Mutex Routines.
APR Time Library.
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_ENOMEM
Definition apr_errno.h:683
#define APR_ENOENT
Definition apr_errno.h:662
#define APR_EINVAL
Definition apr_errno.h:711
apr_brigade_flush void * ctx
apr_size_t size
apr_uint32_t val
Definition apr_atomic.h:66
#define APR_SUCCESS
Definition apr_errno.h:225
int apr_status_t
Definition apr_errno.h:44
const char * key
void * data
void const char apr_status_t(* cleanup)(void *))
apr_time_t mtime
apr_ssize_t * klen
Definition apr_hash.h:71
const char apr_uint32_t * id
#define apr_pcalloc(p, size)
Definition apr_pools.h:465
const char * sep
const char * s
Definition apr_strings.h:95
#define APR_ARRAY_PUSH(ary, type)
Definition apr_tables.h:150
#define APR_ARRAY_IDX(ary, i, type)
Definition apr_tables.h:141
int reason
int int status
apr_int64_t apr_time_t
Definition apr_time.h:45
#define apr_time_sec(time)
Definition apr_time.h:63
#define apr_time_from_sec(sec)
Definition apr_time.h:78
apr_status_t md_job_notify_cb(struct md_job_t *job, const char *reason, struct md_result_t *result, apr_pool_t *p, void *baton)
Definition md.h:314
#define MD_KEY_RENEWAL
Definition md.h:184
#define MD_KEY_UNTIL
Definition md.h:206
#define MD_KEY_OCSPS
Definition md.h:172
#define MD_KEY_GOOD
Definition md.h:151
#define MD_KEY_REVOKED
Definition md.h:190
#define MD_KEY_RENEW_AT
Definition md.h:182
#define MD_KEY_TOTAL
Definition md.h:202
#define MD_KEY_ID
Definition md.h:155
#define MD_KEY_URL
Definition md.h:207
#define MD_KEY_RESPONSE
Definition md.h:189
#define MD_KEY_FROM
Definition md.h:150
#define MD_OTHER
Definition md.h:50
#define MD_KEY_STATUS
Definition md.h:196
#define MD_KEY_SHA256_FINGERPRINT
Definition md.h:192
#define MD_KEY_DOMAIN
Definition md.h:139
#define MD_KEY_VALID
Definition md.h:210
#define MD_KEY_CERT
Definition md.h:124
#define MD_KEY_UNKNOWN
Definition md.h:205
apr_status_t md_cert_to_sha256_fingerprint(const char **pfinger, const md_cert_t *cert, apr_pool_t *p)
Definition md_crypt.c:1443
const char * md_cert_get_serial_number(const md_cert_t *cert, apr_pool_t *p)
Definition md_crypt.c:1180
apr_status_t md_cert_get_ocsp_responder_url(const char **purl, apr_pool_t *p, const md_cert_t *cert)
Definition md_crypt.c:2111
apr_time_t md_asn1_generalized_time_get(void *ASN1_GENERALIZEDTIME)
Definition md_crypt.c:256
void * md_cert_get_X509(const md_cert_t *cert)
Definition md_crypt.c:1175
apr_pool_t * p
Definition md_event.c:32
void md_event_holler(const char *event, const char *mdomain, struct md_job_t *job, struct md_result_t *result, apr_pool_t *p)
Definition md_event.c:78
apr_status_t md_http_POSTd_create(md_http_request_t **preq, md_http_t *http, const char *url, struct apr_table_t *headers, const char *content_type, const struct md_data_t *body)
Definition md_http.c:328
apr_status_t md_http_multi_perform(md_http_t *http, md_http_next_req *nextreq, void *baton)
Definition md_http.c:282
void md_http_set_on_status_cb(md_http_request_t *req, md_http_status_cb *cb, void *baton)
Definition md_http.c:252
void md_http_set_on_response_cb(md_http_request_t *req, md_http_response_cb *cb, void *baton)
Definition md_http.c:258
apr_status_t md_http_create(md_http_t **phttp, apr_pool_t *p, const char *user_agent, const char *proxy_url)
Definition md_http.c:61
md_json_t * md_json_create(apr_pool_t *pool)
Definition md_json.c:92
apr_status_t md_json_set_timeperiod(const md_timeperiod_t *tp, md_json_t *json,...)
Definition md_json.c:1259
apr_status_t md_json_sets(const char *value, md_json_t *json,...)
Definition md_json.c:430
const char * md_json_gets(const md_json_t *json,...)
Definition md_json.c:406
apr_status_t md_json_addj(const md_json_t *value, md_json_t *json,...)
Definition md_json.c:555
apr_status_t md_json_set_time(apr_time_t value, md_json_t *json,...)
Definition md_json.c:457
apr_status_t md_json_setj(const md_json_t *value, md_json_t *json,...)
Definition md_json.c:527
const char * md_json_dups(apr_pool_t *p, const md_json_t *json,...)
Definition md_json.c:418
apr_status_t md_json_setl(long value, md_json_t *json,...)
Definition md_json.c:392
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
#define MD_LOG_MARK
Definition md_log.h:39
@ MD_LOG_TRACE2
Definition md_log.h:30
@ MD_LOG_ERR
Definition md_log.h:24
@ MD_LOG_WARNING
Definition md_log.h:25
@ MD_LOG_TRACE3
Definition md_log.h:31
@ MD_LOG_DEBUG
Definition md_log.h:28
static const char * single_resp_summary(OCSP_SINGLERESP *resp, apr_pool_t *p)
Definition md_ocsp.c:552
static const char * certid_summary(const OCSP_CERTID *certid, apr_pool_t *p)
Definition md_ocsp.c:509
static apr_status_t ocsp_req_make(OCSP_REQUEST **pocsp_req, OCSP_CERTID *certid)
Definition md_ocsp.c:764
static int select_next_run(void *baton, const void *key, apr_ssize_t klen, const void *val)
Definition md_ocsp.c:867
static apr_status_t ostat_on_resp(const md_http_response_t *resp, void *baton)
Definition md_ocsp.c:583
static apr_status_t ocsp_status_save(md_ocsp_cert_stat_t stat, const md_data_t *resp_der, const md_timeperiod_t *resp_valid, md_ocsp_status_t *ostat, apr_pool_t *ptemp)
Definition md_ocsp.c:246
static apr_status_t job_loadj(md_json_t **pjson, const char *name, md_ocsp_reg_t *reg, apr_pool_t *p)
Definition md_ocsp.c:973
static int ostat_should_renew(md_ocsp_status_t *ostat)
Definition md_ocsp.c:157
static apr_status_t ostat_on_req_status(const md_http_request_t *req, apr_status_t status, void *baton)
Definition md_ocsp.c:729
const char * md_ocsp_cert_stat_name(md_ocsp_cert_stat_t stat)
Definition md_ocsp.c:101
static apr_status_t ostat_from_json(md_ocsp_cert_stat_t *pstat, md_data_t *resp_der, md_timeperiod_t *resp_valid, md_json_t *json, apr_pool_t *p)
Definition md_ocsp.c:185
static void ostat_req_cleanup(md_ocsp_status_t *ostat)
Definition md_ocsp.c:131
static void ostat_to_json(md_json_t *json, md_ocsp_cert_stat_t stat, const md_data_t *resp_der, const md_timeperiod_t *resp_valid, apr_pool_t *p)
Definition md_ocsp.c:209
void md_ocsp_get_status_all(md_json_t **pjson, md_ocsp_reg_t *reg, apr_pool_t *p)
Definition md_ocsp.c:1033
apr_size_t md_ocsp_count(md_ocsp_reg_t *reg)
Definition md_ocsp.c:491
apr_status_t md_ocsp_init_id(md_data_t *id, apr_pool_t *p, const md_cert_t *cert)
Definition md_ocsp.c:117
static apr_status_t ostat_set(md_ocsp_status_t *ostat, md_ocsp_cert_stat_t stat, md_data_t *der, md_timeperiod_t *valid, apr_time_t mtime)
Definition md_ocsp.c:165
static int add_ostat(void *baton, const void *key, apr_ssize_t klen, const void *val)
Definition md_ocsp.c:1012
static void ocsp_get_meta(md_ocsp_cert_stat_t *pstat, md_timeperiod_t *pvalid, md_ocsp_reg_t *reg, md_ocsp_status_t *ostat, apr_pool_t *p)
Definition md_ocsp.c:443
md_job_t * md_ocsp_job_make(md_ocsp_reg_t *ocsp, const char *mdomain, apr_pool_t *p)
Definition md_ocsp.c:1056
void md_ocsp_renew(md_ocsp_reg_t *reg, apr_pool_t *p, apr_pool_t *ptemp, apr_time_t *pnext_run)
Definition md_ocsp.c:880
static apr_status_t ocsp_req_assign_der(md_data_t *d, OCSP_REQUEST *ocsp_req)
Definition md_ocsp.c:788
static md_json_t * mk_jstat(md_ocsp_status_t *ostat, md_ocsp_reg_t *reg, apr_pool_t *p)
Definition md_ocsp.c:985
static apr_status_t ocsp_reg_cleanup(void *data)
Definition md_ocsp.c:265
static void md_openssl_free(void *d)
Definition md_ocsp.c:96
static int ostat_cleanup(void *ctx, const void *key, apr_ssize_t klen, const void *val)
Definition md_ocsp.c:140
apr_status_t md_ocsp_remove_responses_older_than(md_ocsp_reg_t *reg, apr_pool_t *p, apr_time_t timestamp)
Definition md_ocsp.c:922
void md_ocsp_get_summary(md_json_t **pjson, md_ocsp_reg_t *reg, apr_pool_t *p)
Definition md_ocsp.c:955
apr_status_t md_ocsp_prime(md_ocsp_reg_t *reg, const char *ext_id, apr_size_t ext_id_len, md_cert_t *cert, md_cert_t *issuer, const md_t *md)
Definition md_ocsp.c:305
static int md_ostat_cmp(const void *v1, const void *v2)
Definition md_ocsp.c:1023
static const char * certstatus_string(int status)
Definition md_ocsp.c:541
static int select_updates(void *baton, const void *key, apr_ssize_t klen, const void *val)
Definition md_ocsp.c:848
static const char * certid_as_hex(const OCSP_CERTID *certid, apr_pool_t *p)
Definition md_ocsp.c:496
apr_status_t md_ocsp_get_status(md_ocsp_copy_der *cb, void *userdata, md_ocsp_reg_t *reg, const char *ext_id, apr_size_t ext_id_len, apr_pool_t *p, const md_t *md)
Definition md_ocsp.c:373
md_ocsp_cert_stat_t md_ocsp_cert_stat_value(const char *name)
Definition md_ocsp.c:110
apr_status_t md_ocsp_get_meta(md_ocsp_cert_stat_t *pstat, md_timeperiod_t *pvalid, md_ocsp_reg_t *reg, const md_cert_t *cert, apr_pool_t *p, const md_t *md)
Definition md_ocsp.c:457
static apr_status_t next_todo(md_http_request_t **preq, void *baton, md_http_t *http, int in_flight)
Definition md_ocsp.c:800
static int add_to_summary(void *baton, const void *key, apr_ssize_t klen, const void *val)
Definition md_ocsp.c:937
static apr_status_t ocsp_status_refresh(md_ocsp_status_t *ostat, apr_pool_t *ptemp)
Definition md_ocsp.c:223
apr_status_t md_ocsp_reg_make(md_ocsp_reg_t **preg, apr_pool_t *p, md_store_t *store, const md_timeslice_t *renew_window, const char *user_agent, const char *proxy_url, apr_time_t min_delay)
Definition md_ocsp.c:274
md_ocsp_cert_stat_t
Definition md_ocsp.h:27
@ MD_OCSP_CERT_ST_GOOD
Definition md_ocsp.h:29
@ MD_OCSP_CERT_ST_REVOKED
Definition md_ocsp.h:30
@ MD_OCSP_CERT_ST_UNKNOWN
Definition md_ocsp.h:28
void md_ocsp_copy_der(const unsigned char *der, apr_size_t der_len, void *userdata)
Definition md_ocsp.h:49
void md_result_activity_printf(md_result_t *result, const char *fmt,...)
Definition md_result.c:82
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
md_result_t * md_result_md_make(apr_pool_t *p, const char *md_name)
Definition md_result.c:50
md_job_t * md_job_make(apr_pool_t *p, md_store_t *store, md_store_group_t group, const char *name, apr_time_t min_delay)
Definition md_status.c:288
void md_job_start_run(md_job_t *job, md_result_t *result, md_store_t *store)
Definition md_status.c:570
apr_status_t md_job_load(md_job_t *job)
Definition md_status.c:358
void md_job_end_run(md_job_t *job, md_result_t *result)
Definition md_status.c:611
apr_status_t md_job_save(md_job_t *job, md_result_t *result, apr_pool_t *p)
Definition md_status.c:370
apr_time_t md_job_delay_on_errors(md_job_t *job, int err_count, const char *last_problem)
Definition md_status.c:578
void md_job_log_append(md_job_t *job, const char *type, const char *status, const char *detail)
Definition md_status.c:382
apr_status_t md_store_save_json(md_store_t *store, apr_pool_t *p, md_store_group_t group, const char *name, const char *aspect, struct md_json_t *data, int create)
Definition md_store.c:113
apr_time_t md_store_get_modified(md_store_t *store, md_store_group_t group, const char *name, const char *aspect, apr_pool_t *p)
Definition md_store.c:144
apr_status_t md_store_remove_not_modified_since(md_store_t *store, apr_pool_t *p, apr_time_t modified, md_store_group_t group, const char *name, const char *aspect)
Definition md_store.c:156
apr_status_t md_store_load_json(md_store_t *store, md_store_group_t group, const char *name, const char *aspect, struct md_json_t **pdata, apr_pool_t *p)
Definition md_store.c:106
@ MD_SG_OCSP
Definition md_store.h:70
#define MD_FN_JOB
Definition md_store.h:75
apr_interval_time_t md_timeperiod_remaining(const md_timeperiod_t *period, apr_time_t time)
Definition md_time.c:47
int md_timeperiod_has_started(const md_timeperiod_t *period, apr_time_t time)
Definition md_time.c:37
char * md_timeperiod_print(apr_pool_t *p, const md_timeperiod_t *period)
Definition md_time.c:54
md_timeperiod_t md_timeperiod_slice_before_end(const md_timeperiod_t *period, const md_timeslice_t *ts)
Definition md_time.c:286
#define MD_SECS_PER_DAY
Definition md_time.h:23
apr_size_t md_util_base64url_decode(md_data_t *decoded, const char *encoded, apr_pool_t *pool)
Definition md_util.c:1157
void md_data_clear(md_data_t *d)
Definition md_util.c:112
void md_data_assign_pcopy(md_data_t *dest, const char *src, apr_size_t src_len, apr_pool_t *p)
Definition md_util.c:143
const char * md_util_base64url_encode(const md_data_t *data, apr_pool_t *pool)
Definition md_util.c:1207
apr_status_t md_data_assign_copy(md_data_t *dest, const char *src, apr_size_t src_len)
Definition md_util.c:130
apr_status_t md_data_to_hex(const char **phex, char separator, apr_pool_t *p, const md_data_t *data)
Definition md_util.c:169
void md_data_null(md_data_t *d)
Definition md_util.c:107
return NULL
Definition mod_so.c:359
int i
Definition mod_so.c:347
char * name
md_data_free_fn * free_data
Definition md_util.h:44
const char * data
Definition md_util.h:42
apr_size_t len
Definition md_util.h:43
apr_pool_t * pool
Definition md_http.h:60
const char * mdomain
Definition md_status.h:54
md_data_t external_id
Definition md_ocsp.c:93
md_data_t id
Definition md_ocsp.c:92
apr_hash_t * ostat_by_id
Definition md_ocsp.c:56
apr_hash_t * id_by_external_id
Definition md_ocsp.c:55
apr_time_t min_delay
Definition md_ocsp.c:61
md_job_notify_cb * notify
Definition md_ocsp.c:59
md_timeslice_t renew_window
Definition md_ocsp.c:58
apr_thread_mutex_t * mutex
Definition md_ocsp.c:57
const char * proxy_url
Definition md_ocsp.c:54
md_store_t * store
Definition md_ocsp.c:52
void * notify_ctx
Definition md_ocsp.c:60
const char * user_agent
Definition md_ocsp.c:53
apr_pool_t * p
Definition md_ocsp.c:51
const char * hex_sha256
Definition md_ocsp.c:68
apr_time_t resp_last_check
Definition md_ocsp.c:87
apr_time_t resp_mtime
Definition md_ocsp.c:86
md_timeperiod_t resp_valid
Definition md_ocsp.c:77
const char * hexid
Definition md_ocsp.c:67
md_ocsp_reg_t * reg
Definition md_ocsp.c:81
apr_time_t next_run
Definition md_ocsp.c:72
md_ocsp_cert_stat_t resp_stat
Definition md_ocsp.c:75
md_data_t id
Definition md_ocsp.c:66
md_data_t req_der
Definition md_ocsp.c:79
OCSP_CERTID * certid
Definition md_ocsp.c:69
md_data_t resp_der
Definition md_ocsp.c:76
OCSP_REQUEST * ocsp_req
Definition md_ocsp.c:80
const char * md_name
Definition md_ocsp.c:83
const char * responder_url
Definition md_ocsp.c:70
const char * file_name
Definition md_ocsp.c:84
apr_time_t time
Definition md_ocsp.c:760
apr_pool_t * ptemp
Definition md_ocsp.c:759
md_ocsp_reg_t * reg
Definition md_ocsp.c:757
apr_array_header_t * todos
Definition md_ocsp.c:758
md_result_t * result
Definition md_ocsp.c:579
apr_pool_t * p
Definition md_ocsp.c:577
md_ocsp_status_t * ostat
Definition md_ocsp.c:578
md_job_t * job
Definition md_ocsp.c:580
const char * detail
Definition md_result.h:34
const char * problem
Definition md_result.h:33
Definition md.h:76
const char * name
Definition md.h:77
apr_time_t end
Definition md_time.h:29
apr_time_t start
Definition md_time.h:28
apr_pool_t * p
Definition md_ocsp.c:980
md_ocsp_reg_t * reg
Definition md_ocsp.c:981
apr_array_header_t * ostats
Definition md_ocsp.c:982
apr_pool_t * p
Definition md_ocsp.c:930
md_ocsp_reg_t * reg
Definition md_ocsp.c:931