Apache HTTPD
mod_md_status.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 <apr_optional.h>
19#include <apr_time.h>
20#include <apr_date.h>
21#include <apr_strings.h>
22
23#include <httpd.h>
24#include <http_core.h>
25#include <http_protocol.h>
26#include <http_request.h>
27#include <http_log.h>
28
29#include "mod_status.h"
30
31#include "md.h"
32#include "md_curl.h"
33#include "md_crypt.h"
34#include "md_http.h"
35#include "md_ocsp.h"
36#include "md_json.h"
37#include "md_status.h"
38#include "md_store.h"
39#include "md_store_fs.h"
40#include "md_log.h"
41#include "md_reg.h"
42#include "md_util.h"
43#include "md_version.h"
44#include "md_acme.h"
45#include "md_acme_authz.h"
46
47#include "mod_md.h"
48#include "mod_md_private.h"
49#include "mod_md_config.h"
50#include "mod_md_drive.h"
51#include "mod_md_status.h"
52
53/**************************************************************************************************/
54/* Certificate status */
55
56#define APACHE_PREFIX "/.httpd/"
57#define MD_STATUS_RESOURCE APACHE_PREFIX"certificate-status"
58#define HTML_STATUS(X) (!((X)->flags & AP_STATUS_SHORT))
59
61{
62 int i;
63 md_json_t *resp, *mdj, *cj;
64 const md_srv_conf_t *sc;
65 const md_t *md;
66 md_pkey_spec_t *spec;
67 const char *keyname;
69 apr_status_t rv;
70
72 return DECLINED;
73
75 "requesting status for: %s", r->hostname);
76
77 /* We are looking for information about a staged certificate */
78 sc = ap_get_module_config(r->server->module_config, &md_module);
79 if (!sc || !sc->mc || !sc->mc->reg || !sc->mc->certificate_status_enabled) return DECLINED;
80 md = md_get_by_domain(sc->mc->mds, r->hostname);
81 if (!md) return DECLINED;
82
83 if (r->method_number != M_GET) {
85 "md(%s): status supports only GET", md->name);
87 }
88
90 "requesting status for MD: %s", md->name);
91
92 rv = md_status_get_md_json(&mdj, md, sc->mc->reg, sc->mc->ocsp, r->pool);
93 if (APR_SUCCESS != rv) {
95 "loading md status for %s", md->name);
97 }
98
100 "status for MD: %s is %s", md->name, md_json_writep(mdj, r->pool, MD_JSON_FMT_INDENT));
101
103
106 }
107
108 for (i = 0; i < md_cert_count(md); ++i) {
109 spec = md_pkeys_spec_get(md->pks, i);
112
116 }
117
121 }
125 }
127 }
128
130 /* copy over the information we want to make public about this:
131 * - when not finished, add an empty object to indicate something is going on
132 * - when a certificate is staged, add the information from that */
134 cj = cj? cj : md_json_create(r->pool);
136 }
137
138 ap_log_rerror(APLOG_MARK, APLOG_TRACE2, 0, r, "md[%s]: sending status", md->name);
139 apr_table_set(r->headers_out, "Content-Type", "application/json");
144
145 return DONE;
146}
147
148/**************************************************************************************************/
149/* Status hook */
150
151typedef struct {
155 int flags;
156 const char *prefix;
157 const char *separator;
158} status_ctx;
159
161
162static void add_json_val(status_ctx *ctx, md_json_t *j);
163
165
167 const char *label;
168 const char *key;
170};
171
173{
174 const char *s = "unknown";
176 (void)info;
177 switch (md_json_getl(mdj, info->key, NULL)) {
178 case MD_S_INCOMPLETE:
180 s = s? apr_psprintf(ctx->p, "incomplete: %s", s) : "incomplete";
181 break;
183 case MD_S_COMPLETE:
185 s = (!until || until > apr_time_now())? "good" : "expired";
186 break;
187 case MD_S_ERROR: s = "error"; break;
188 case MD_S_MISSING_INFORMATION: s = "missing information"; break;
189 default: break;
190 }
191 if (HTML_STATUS(ctx)) {
193 }
194 else {
195 apr_brigade_printf(ctx->bb, NULL, NULL, "%s%s: %s\n",
196 ctx->prefix, info->label, s);
197 }
198}
199
201{
202 const char *url, *s;
203
204 s = url = md_json_gets(mdj, info->key, NULL);
205 if (!url) return;
207 if (HTML_STATUS(ctx)) {
208 apr_brigade_printf(ctx->bb, NULL, NULL, "<a href='%s'>%s</a>",
209 ap_escape_html2(ctx->p, url, 1),
210 ap_escape_html2(ctx->p, s, 1));
211 }
212 else {
213 apr_brigade_printf(ctx->bb, NULL, NULL, "%s%sName: %s\n",
214 ctx->prefix, info->label, s);
215 apr_brigade_printf(ctx->bb, NULL, NULL, "%s%sURL: %s\n",
216 ctx->prefix, info->label, url);
217 }
218}
219
220static void print_date(status_ctx *ctx, apr_time_t timestamp, const char *title)
221{
222 apr_bucket_brigade *bb = ctx->bb;
223 if (timestamp > 0) {
224 char ts[128];
225 char ts2[128];
228
229 apr_time_exp_gmt(&texp, timestamp);
230 apr_strftime(ts, &len, sizeof(ts2)-1, "%Y-%m-%d", &texp);
231 ts[len] = '\0';
232 if (!title) {
233 apr_strftime(ts2, &len, sizeof(ts)-1, "%Y-%m-%dT%H:%M:%SZ", &texp);
234 ts2[len] = '\0';
235 title = ts2;
236 }
237 if (HTML_STATUS(ctx)) {
239 "<span title='%s' style='white-space: nowrap;'>%s</span>",
240 ap_escape_html2(bb->p, title, 1), ts);
241 }
242 else {
243 apr_brigade_printf(bb, NULL, NULL, "%s%s: %s\n",
244 ctx->prefix, title, ts);
245 }
246 }
247}
248
249static void print_time(status_ctx *ctx, const char *label, apr_time_t t)
250{
251 apr_bucket_brigade *bb = ctx->bb;
253 const char *pre, *post, *sep;
254 char ts[APR_RFC822_DATE_LEN];
255 char ts2[128];
259
260 if (t == 0) {
261 /* timestamp is 0, we use that for "not set" */
262 return;
263 }
265 now = apr_time_now();
266 pre = post = "";
267 sep = (label && strlen(label))? " " : "";
268 delta = 0;
269 if (HTML_STATUS(ctx)) {
270 apr_rfc822_date(ts, t);
271 if (t > now) {
272 delta = t - now;
273 pre = "in ";
274 }
275 else {
276 delta = now - t;
277 post = " ago";
278 }
280 apr_strftime(ts2, &len, sizeof(ts2)-1, "%Y-%m-%d", &texp);
281 ts2[len] = '\0';
282 apr_brigade_printf(bb, NULL, NULL, "%s%s<span title='%s' "
283 "style='white-space: nowrap;'>%s</span>",
284 label, sep, ts, ts2);
285 }
286 else {
287 apr_brigade_printf(bb, NULL, NULL, "%s%s<span title='%s'>%s%s%s</span>",
288 label, sep, ts, pre, md_duration_roughly(bb->p, delta), post);
289 }
290 }
291 else {
292 delta = t - now;
293 apr_brigade_printf(bb, NULL, NULL, "%s%s: %" APR_TIME_T_FMT "\n",
294 ctx->prefix, label, apr_time_sec(delta));
295 }
296}
297
299{
300 const char *sfrom, *suntil, *sep, *title;
301 apr_time_t from, until;
302
303 sep = NULL;
305 from = sfrom? apr_date_parse_rfc(sfrom) : 0;
308
309 if (HTML_STATUS(ctx)) {
310 if (from > apr_time_now()) {
311 apr_brigade_puts(ctx->bb, NULL, NULL, "from ");
312 print_date(ctx, from, sfrom);
313 sep = " ";
314 }
315 if (until) {
316 if (sep) apr_brigade_puts(ctx->bb, NULL, NULL, sep);
317 apr_brigade_puts(ctx->bb, NULL, NULL, "until ");
318 title = sfrom? apr_psprintf(ctx->p, "%s - %s", sfrom, suntil) : suntil;
320 }
321 }
322 else {
323 if (from > apr_time_now()) {
324 print_date(ctx, from,
325 apr_pstrcat(ctx->p, info->label, "From", NULL));
326 }
327 if (until) {
329 apr_pstrcat(ctx->p, info->label, "Until", NULL));
330 }
331 }
332}
333
335{
336 if (HTML_STATUS(ctx)) {
337 const char *html = ap_escape_html2(ctx->p, info->label, 1);
338 apr_brigade_printf(ctx->bb, NULL, NULL, "<th class=\"%s\">%s</th>", html, html);
339 }
340}
341
343{
345 status_info sub = *info;
346
347 sub.key = MD_KEY_VALID;
348 jcert = md_json_getj(mdj, info->key, NULL);
349 if (jcert) si_val_valid_time(ctx, jcert, &sub);
350}
351
353 const char*url, const char *proto, int i)
354{
355 const char *s;
356
357 if (proto && !strcmp(proto, "tailscale")) {
358 s = "tailscale";
359 }
360 else if (url) {
362 }
363 else {
364 return;
365 }
366 if (HTML_STATUS(ctx)) {
367 apr_brigade_printf(ctx->bb, NULL, NULL, "%s<a href='%s'>%s</a>",
368 i? " " : "",
369 ap_escape_html2(ctx->p, url, 1),
370 ap_escape_html2(ctx->p, s, 1));
371 }
372 else if (i == 0) {
373 apr_brigade_printf(ctx->bb, NULL, NULL, "%s%sName: %s\n",
374 ctx->prefix, info->label, s);
375 apr_brigade_printf(ctx->bb, NULL, NULL, "%s%sURL: %s\n",
376 ctx->prefix, info->label, url);
377 }
378 else {
379 apr_brigade_printf(ctx->bb, NULL, NULL, "%s%sName%d: %s\n",
380 ctx->prefix, info->label, i, s);
381 apr_brigade_printf(ctx->bb, NULL, NULL, "%s%sURL%d: %s\n",
382 ctx->prefix, info->label, i, url);
383 }
384}
385
387{
389 const char *proto, *url;
391 int i;
392
393 jcert = md_json_getj(mdj, info->key, NULL);
394 if (!jcert) {
395 return;
396 }
397
400 if (url) {
401 /* print the effective CA url used, if set */
402 val_url_print(ctx, info, url, proto, 0);
403 }
404 else {
405 /* print the available CA urls configured */
406 urls = apr_array_make(ctx->p, 3, sizeof(const char*));
408 for (i = 0; i < urls->nelts; ++i) {
409 url = APR_ARRAY_IDX(urls, i, const char*);
410 val_url_print(ctx, info, url, proto, i);
411 }
412 }
413}
414
415static int count_certs(void *baton, const char *key, md_json_t *json)
416{
417 int *pcount = baton;
418
419 (void)json;
420 if (strcmp(key, MD_KEY_VALID)) {
421 *pcount += 1;
422 }
423 return 1;
424}
425
426static void print_job_summary(status_ctx *ctx, md_json_t *mdj, const char *key,
427 const char *separator)
428{
429 apr_bucket_brigade *bb = ctx->bb;
431 apr_status_t rv;
432 int finished, errors, cert_count;
434 const char *s, *line;
435
436 if (!md_json_has_key(mdj, key, NULL)) {
437 return;
438 }
439
440 finished = md_json_getb(mdj, key, MD_KEY_FINISHED, NULL);
443
444 line = separator? separator : "";
445
446 if (rv != APR_SUCCESS) {
447 char *errstr = apr_strerror(rv, buffer, sizeof(buffer));
449 if (HTML_STATUS(ctx)) {
450 line = apr_psprintf(bb->p, "%s Error[%s]: %s", line,
451 errstr, s? s : "");
452 }
453 else {
454 apr_brigade_printf(bb, NULL, NULL, "%sLastStatus: %s\n", ctx->prefix, errstr);
455 apr_brigade_printf(bb, NULL, NULL, "%sLastProblem: %s\n", ctx->prefix, s);
456 }
457 }
458
459 if (!HTML_STATUS(ctx)) {
460 apr_brigade_printf(bb, NULL, NULL, "%sFinished: %s\n", ctx->prefix,
461 finished ? "yes" : "no");
462 }
463 if (finished) {
464 cert_count = 0;
466 if (HTML_STATUS(ctx)) {
467 if (cert_count > 0) {
468 line =apr_psprintf(bb->p, "%s finished, %d new certificate%s staged.",
469 line, cert_count, cert_count > 1? "s" : "");
470 }
471 else {
472 line = apr_psprintf(bb->p, "%s finished successfully.", line);
473 }
474 }
475 else {
476 apr_brigade_printf(bb, NULL, NULL, "%sNewStaged: %d\n", ctx->prefix, cert_count);
477 }
478 }
479 else {
481 if (s) {
482 if (HTML_STATUS(ctx)) {
483 line = apr_psprintf(bb->p, "%s %s", line, s);
484 }
485 else {
486 apr_brigade_printf(bb, NULL, NULL, "%sLastDetail: %s\n", ctx->prefix, s);
487 }
488 }
489 }
490
492 if (errors > 0) {
493 if (HTML_STATUS(ctx)) {
494 line = apr_psprintf(bb->p, "%s (%d retr%s) ", line,
495 errors, (errors > 1)? "y" : "ies");
496 }
497 else {
498 apr_brigade_printf(bb, NULL, NULL, "%sRetries: %d\n", ctx->prefix, errors);
499 }
500 }
501
502 if (HTML_STATUS(ctx)) {
503 apr_brigade_puts(bb, NULL, NULL, line);
504 }
505
507 if (t > apr_time_now() && !finished) {
509 HTML_STATUS(ctx) ? "\nNext run" : "NextRun",
510 t);
511 }
512 else if (line[0] != '\0') {
513 if (HTML_STATUS(ctx)) {
514 apr_brigade_puts(bb, NULL, NULL, "\nOngoing...");
515 }
516 else {
517 apr_brigade_printf(bb, NULL, NULL, "%s: Ongoing\n", ctx->prefix);
518 }
519 }
520}
521
523{
525 const char *prefix = ctx->prefix;
526
527 (void)info;
528 if (!HTML_STATUS(ctx)) {
529 ctx->prefix = apr_pstrcat(ctx->p, prefix, info->label, NULL);
530 }
531
534 return;
535 }
536
538 if (t > apr_time_now()) {
539 print_time(ctx, "Renew", t);
540 }
541 else if (t) {
542 if (HTML_STATUS(ctx)) {
543 apr_brigade_puts(ctx->bb, NULL, NULL, "Pending");
544 }
545 else {
546 apr_brigade_printf(ctx->bb, NULL, NULL, "%s: %s", ctx->prefix, "Pending");
547 }
548 }
550 if (HTML_STATUS(ctx)) {
551 apr_brigade_puts(ctx->bb, NULL, NULL, "Manual renew");
552 }
553 else {
554 apr_brigade_printf(ctx->bb, NULL, NULL, "%s: %s", ctx->prefix, "Manual renew");
555 }
556 }
557 if (!HTML_STATUS(ctx)) {
558 ctx->prefix = prefix;
559 }
560}
561
562static int cert_check_iter(void *baton, const char *key, md_json_t *json)
563{
565 const char *fingerprint;
566
568 if (fingerprint) {
569 if (HTML_STATUS(ctx)) {
571 "<a href=\"%s%s\">%s[%s]</a><br>",
572 ctx->mc->cert_check_url, fingerprint,
573 ctx->mc->cert_check_name, key);
574 }
575 else {
577 "%sType: %s\n",
578 ctx->prefix,
579 key);
581 "%sName: %s\n",
582 ctx->prefix,
583 ctx->mc->cert_check_name);
585 "%sURL: %s%s\n",
586 ctx->prefix,
587 ctx->mc->cert_check_url, fingerprint);
589 "%sFingerprint: %s\n",
590 ctx->prefix,
592 }
593 }
594 return 1;
595}
596
598{
599 (void)info;
600 if (ctx->mc->cert_check_name && ctx->mc->cert_check_url) {
601 const char *prefix = ctx->prefix;
602 if (!HTML_STATUS(ctx)) {
603 ctx->prefix = apr_pstrcat(ctx->p, prefix, info->label, NULL);
604 }
606 if (!HTML_STATUS(ctx)) {
607 ctx->prefix = prefix;
608 }
609 }
610}
611
613{
614 (void)info;
615 if (!md_json_getb(mdj, MD_KEY_STAPLING, NULL)) return;
616 if (HTML_STATUS(ctx)) {
617 apr_brigade_puts(ctx->bb, NULL, NULL, "on");
618 }
619 else {
620 apr_brigade_printf(ctx->bb, NULL, NULL, "%s: on", ctx->prefix);
621 }
622}
623
624static int json_iter_val(void *data, size_t index, md_json_t *json)
625{
627 const char *prefix = ctx->prefix;
628 if (HTML_STATUS(ctx)) {
629 if (index) apr_brigade_puts(ctx->bb, NULL, NULL, ctx->separator);
630 }
631 else {
632 ctx->prefix = apr_pstrcat(ctx->p, prefix, apr_psprintf(ctx->p, "[%" APR_SIZE_T_FMT "]", index), NULL);
633 }
634 add_json_val(ctx, json);
635 if (!HTML_STATUS(ctx)) {
636 ctx->prefix = prefix;
637 }
638 return 1;
639}
640
642{
643 if (!j) return;
646 return;
647 }
648 if (!HTML_STATUS(ctx)) {
649 apr_brigade_puts(ctx->bb, NULL, NULL, ctx->prefix);
650 apr_brigade_puts(ctx->bb, NULL, NULL, ": ");
651 }
654 }
655 else if (md_json_is(MD_JSON_TYPE_STRING, j, NULL)) {
657 }
658 else if (md_json_is(MD_JSON_TYPE_OBJECT, j, NULL)) {
660 }
661 else if (md_json_is(MD_JSON_TYPE_BOOL, j, NULL)) {
662 apr_brigade_puts(ctx->bb, NULL, NULL, md_json_getb(j, NULL)? "on" : "off");
663 }
664 if (!HTML_STATUS(ctx)) {
665 apr_brigade_puts(ctx->bb, NULL, NULL, "\n");
666 }
667}
668
670{
671 const char *prefix = ctx->prefix;
672 if (HTML_STATUS(ctx)) {
673 apr_brigade_puts(ctx->bb, NULL, NULL, "<div style=\"max-width:400px;\">");
674 }
675 else {
676 ctx->prefix = apr_pstrcat(ctx->p, prefix, info->label, NULL);
677 }
679 if (HTML_STATUS(ctx)) {
680 apr_brigade_puts(ctx->bb, NULL, NULL, "</div>");
681 }
682 else {
683 ctx->prefix = prefix;
684 }
685}
686
688{
689 if (info->fn) {
690 info->fn(ctx, mdj, info);
691 }
692 else {
693 const char *prefix = ctx->prefix;
694 if (!HTML_STATUS(ctx)) {
695 ctx->prefix = apr_pstrcat(ctx->p, prefix, info->label, NULL);
696 }
698 if (!HTML_STATUS(ctx)) {
699 ctx->prefix = prefix;
700 }
701 }
702}
703
704static const status_info status_infos[] = {
705 { "Domain", MD_KEY_NAME, NULL },
706 { "Names", MD_KEY_DOMAINS, si_val_names },
707 { "Status", MD_KEY_STATE, si_val_status },
709 { "CA", MD_KEY_CA, si_val_ca_urls },
710 { "Stapling", MD_KEY_STAPLING, si_val_stapling },
712 { "Activity", MD_KEY_NOTIFIED, si_val_activity },
713};
714
715static int add_md_row(void *baton, apr_size_t index, md_json_t *mdj)
716{
718 const char *prefix = ctx->prefix;
719 int i;
720
721 if (HTML_STATUS(ctx)) {
722 apr_brigade_printf(ctx->bb, NULL, NULL, "<tr class=\"%s\">", (index % 2)? "odd" : "even");
723 for (i = 0; i < (int)(sizeof(status_infos)/sizeof(status_infos[0])); ++i) {
724 apr_brigade_puts(ctx->bb, NULL, NULL, "<td>");
726 apr_brigade_puts(ctx->bb, NULL, NULL, "</td>");
727 }
728 apr_brigade_puts(ctx->bb, NULL, NULL, "</tr>");
729 } else {
730 for (i = 0; i < (int)(sizeof(status_infos)/sizeof(status_infos[0])); ++i) {
731 ctx->prefix = apr_pstrcat(ctx->p, prefix, apr_psprintf(ctx->p, "[%" APR_SIZE_T_FMT "]", index), NULL);
733 ctx->prefix = prefix;
734 }
735 }
736 return 1;
737}
738
739static int md_name_cmp(const void *v1, const void *v2)
740{
741 return strcmp((*(const md_t**)v1)->name, (*(const md_t**)v2)->name);
742}
743
745{
746 const md_srv_conf_t *sc;
747 const md_mod_conf_t *mc;
748 int i;
752
753 ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r, "server-status for managed domains, start");
754 sc = ap_get_module_config(r->server->module_config, &md_module);
755 if (!sc) return DECLINED;
756 mc = sc->mc;
757 if (!mc || !mc->server_status_enabled) return DECLINED;
758
759 ctx.p = r->pool;
760 ctx.mc = mc;
762 ctx.flags = flags;
763 ctx.prefix = "ManagedCertificates";
764 ctx.separator = " ";
765
766 mds = apr_array_copy(r->pool, mc->mds);
767 qsort(mds->elts, (size_t)mds->nelts, sizeof(md_t *), md_name_cmp);
768
769 if (!HTML_STATUS(&ctx)) {
770 int total = 0, complete = 0, renewing = 0, errored = 0, ready = 0;
771 ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r, "no-html managed domain status summary");
772 if (mc->mds->nelts > 0) {
773 md_status_take_stock(&jstock, mds, mc->reg, r->pool);
774 ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r, "got JSON managed domain status summary");
780 }
781 apr_brigade_printf(ctx.bb, NULL, NULL, "%sTotal: %d\n", ctx.prefix, total);
782 apr_brigade_printf(ctx.bb, NULL, NULL, "%sOK: %d\n", ctx.prefix, complete);
783 apr_brigade_printf(ctx.bb, NULL, NULL, "%sRenew: %d\n", ctx.prefix, renewing);
784 apr_brigade_printf(ctx.bb, NULL, NULL, "%sErrored: %d\n", ctx.prefix, errored);
785 apr_brigade_printf(ctx.bb, NULL, NULL, "%sReady: %d\n", ctx.prefix, ready);
786 }
787 if (mc->mds->nelts > 0) {
788 md_status_get_json(&jstatus, mds, mc->reg, mc->ocsp, r->pool);
789 ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r, "got JSON managed domain status");
790 if (HTML_STATUS(&ctx)) {
791 ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r, "html managed domain status table");
793 "<hr>\n<h3>Managed Certificates</h3>\n<table class='md_status'><thead><tr>\n");
794 for (i = 0; i < (int)(sizeof(status_infos)/sizeof(status_infos[0])); ++i) {
796 }
797 apr_brigade_puts(ctx.bb, NULL, NULL, "</tr>\n</thead><tbody>");
798 }
799 else {
800 ctx.prefix = "ManagedDomain";
801 }
802 ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r, "iterating JSON managed domain status");
804 if (HTML_STATUS(&ctx)) {
805 apr_brigade_puts(ctx.bb, NULL, NULL, "</td></tr>\n</tbody>\n</table>\n");
806 }
807 }
808
811 ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r, "server-status for managed domains, end");
812
813 return OK;
814}
815
817{
819 const char *prefix = ctx->prefix;
820
821 (void)info;
822 if (!HTML_STATUS(ctx)) {
823 ctx->prefix = apr_pstrcat(ctx->p, prefix, info->label, NULL);
824 }
826 print_time(ctx, "Refresh", t);
828 if (!HTML_STATUS(ctx)) {
829 ctx->prefix = prefix;
830 }
831}
832
834 { "Domain", MD_KEY_DOMAIN, NULL },
835 { "CertificateID", MD_KEY_ID, NULL },
836 { "OCSPStatus", MD_KEY_STATUS, NULL },
837 { "StaplingValid", MD_KEY_VALID, si_val_valid_time },
838 { "Responder", MD_KEY_URL, si_val_url },
839 { "Activity", MD_KEY_NOTIFIED, si_val_ocsp_activity },
840};
841
842static int add_ocsp_row(void *baton, apr_size_t index, md_json_t *mdj)
843{
845 const char *prefix = ctx->prefix;
846 int i;
847
848 if (HTML_STATUS(ctx)) {
849 apr_brigade_printf(ctx->bb, NULL, NULL, "<tr class=\"%s\">", (index % 2)? "odd" : "even");
850 for (i = 0; i < (int)(sizeof(ocsp_status_infos)/sizeof(ocsp_status_infos[0])); ++i) {
851 apr_brigade_puts(ctx->bb, NULL, NULL, "<td>");
853 apr_brigade_puts(ctx->bb, NULL, NULL, "</td>");
854 }
855 apr_brigade_puts(ctx->bb, NULL, NULL, "</tr>");
856 } else {
857 for (i = 0; i < (int)(sizeof(ocsp_status_infos)/sizeof(ocsp_status_infos[0])); ++i) {
858 ctx->prefix = apr_pstrcat(ctx->p, prefix, apr_psprintf(ctx->p, "[%" APR_SIZE_T_FMT "]", index), NULL);
860 ctx->prefix = prefix;
861 }
862 }
863 return 1;
864}
865
867{
868 const md_srv_conf_t *sc;
869 const md_mod_conf_t *mc;
870 int i;
873
874 ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r, "server-status for ocsp stapling, start");
875 sc = ap_get_module_config(r->server->module_config, &md_module);
876 if (!sc) return DECLINED;
877 mc = sc->mc;
878 if (!mc || !mc->server_status_enabled) return DECLINED;
879
880 ctx.p = r->pool;
881 ctx.mc = mc;
883 ctx.flags = flags;
884 ctx.prefix = "ManagedStaplings";
885 ctx.separator = " ";
886
887 if (!HTML_STATUS(&ctx)) {
888 int total = 0, good = 0, revoked = 0, unknown = 0;
889 ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r, "no-html ocsp stapling status summary");
890 if (md_ocsp_count(mc->ocsp) > 0) {
892 ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r, "got JSON ocsp stapling status summary");
897 }
898 apr_brigade_printf(ctx.bb, NULL, NULL, "%sTotal: %d\n", ctx.prefix, total);
899 apr_brigade_printf(ctx.bb, NULL, NULL, "%sOK: %d\n", ctx.prefix, good);
900 apr_brigade_printf(ctx.bb, NULL, NULL, "%sRenew: %d\n", ctx.prefix, revoked);
901 apr_brigade_printf(ctx.bb, NULL, NULL, "%sErrored: %d\n", ctx.prefix, unknown);
902 }
903 if (md_ocsp_count(mc->ocsp) > 0) {
905 ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r, "got JSON ocsp stapling status");
906 if (HTML_STATUS(&ctx)) {
907 ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r, "html ocsp stapling status table");
909 "<hr>\n<h3>Managed Staplings</h3>\n<table class='md_ocsp_status'><thead><tr>\n");
910 for (i = 0; i < (int)(sizeof(ocsp_status_infos)/sizeof(ocsp_status_infos[0])); ++i) {
912 }
913 apr_brigade_puts(ctx.bb, NULL, NULL, "</tr>\n</thead><tbody>");
914 }
915 else {
916 ctx.prefix = "ManagedStapling";
917 }
918 ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r, "iterating JSON ocsp stapling status");
920 if (HTML_STATUS(&ctx)) {
921 apr_brigade_puts(ctx.bb, NULL, NULL, "</td></tr>\n</tbody>\n</table>\n");
922 }
923 }
924
927 ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r, "server-status for ocsp stapling, end");
928
929 return OK;
930}
931
932/**************************************************************************************************/
933/* Status handlers */
934
936{
937 const md_srv_conf_t *sc;
938 const md_mod_conf_t *mc;
942 const md_t *md;
943 const char *name;
944
945 if (strcmp(r->handler, "md-status")) {
946 return DECLINED;
947 }
948
949 sc = ap_get_module_config(r->server->module_config, &md_module);
950 if (!sc) return DECLINED;
951 mc = sc->mc;
952 if (!mc) return DECLINED;
953
954 if (r->method_number != M_GET) {
955 ap_log_rerror(APLOG_MARK, APLOG_TRACE2, 0, r, "md-status supports only GET");
957 }
958
959 jstatus = NULL;
960 md = NULL;
961 if (r->path_info && r->path_info[0] == '/' && r->path_info[1] != '\0') {
962 name = strrchr(r->path_info, '/') + 1;
963 md = md_get_by_name(mc->mds, name);
964 if (!md) md = md_get_by_domain(mc->mds, name);
965 }
966
967 if (md) {
968 md_status_get_md_json(&jstatus, md, mc->reg, mc->ocsp, r->pool);
969 }
970 else {
971 mds = apr_array_copy(r->pool, mc->mds);
972 qsort(mds->elts, (size_t)mds->nelts, sizeof(md_t *), md_name_cmp);
973 md_status_get_json(&jstatus, mds, mc->reg, mc->ocsp, r->pool);
974 }
975
976 if (jstatus) {
977 apr_table_set(r->headers_out, "Content-Type", "application/json");
982
983 return DONE;
984 }
985 return DECLINED;
986}
987
const char apr_size_t len
Definition ap_regex.h:187
APR-UTIL date routines.
APR-UTIL registration of functions exported by modules.
APR Strings library.
APR Time Library.
#define ap_get_module_config(v, m)
request_rec * r
#define HUGE_STRING_LEN
Definition httpd.h:303
#define DECLINED
Definition httpd.h:457
#define OK
Definition httpd.h:456
#define DONE
Definition httpd.h:458
apr_status_t ap_pass_brigade(ap_filter_t *filter, apr_bucket_brigade *bucket)
#define APLOGNO(n)
Definition http_log.h:117
#define ap_log_rerror
Definition http_log.h:454
#define APLOG_ERR
Definition http_log.h:67
#define APLOG_MARK
Definition http_log.h:283
#define APLOG_TRACE2
Definition http_log.h:73
#define APLOG_TRACE1
Definition http_log.h:72
ap_vhost_iterate_conn_cb void * baton
Definition http_vhost.h:87
apr_brigade_flush void * ctx
apr_pool_t apr_dbd_t const char const char * label
Definition apr_dbd.h:397
const char apr_ssize_t int flags
Definition apr_encode.h:168
const char * url
Definition apr_escape.h:120
apr_memcache_t * mc
#define HTTP_INTERNAL_SERVER_ERROR
Definition httpd.h:535
#define HTTP_NOT_IMPLEMENTED
Definition httpd.h:536
#define M_GET
Definition httpd.h:592
char * ap_escape_html2(apr_pool_t *p, const char *s, int toasc)
Definition util.c:2131
apr_size_t size
#define APR_SUCCESS
Definition apr_errno.h:225
int apr_status_t
Definition apr_errno.h:44
const char * key
void * data
char * buffer
apr_interval_time_t t
const char * sep
const char * s
Definition apr_strings.h:95
#define APR_ARRAY_IDX(ary, i, type)
Definition apr_tables.h:141
#define APR_RFC822_DATE_LEN
Definition apr_time.h:186
#define APR_TIME_T_FMT
Definition apr_time.h:52
apr_int64_t apr_interval_time_t
Definition apr_time.h:55
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
CORE HTTP Daemon.
Apache Logging library.
HTTP protocol handling.
Apache Request library.
HTTP Daemon routines.
int md_cert_count(const md_t *md)
Definition md_core.c:187
#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_STATE_DESCR
Definition md.h:195
#define MD_KEY_RENEW_MODE
Definition md.h:183
#define MD_KEY_GOOD
Definition md.h:151
@ MD_S_INCOMPLETE
Definition md.h:54
@ MD_S_ERROR
Definition md.h:57
@ MD_S_COMPLETE
Definition md.h:55
@ MD_S_EXPIRED_DEPRECATED
Definition md.h:56
@ MD_S_MISSING_INFORMATION
Definition md.h:58
#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_NAME
Definition md.h:167
#define MD_KEY_ID
Definition md.h:155
#define MD_KEY_COMPLETE
Definition md.h:131
#define MD_KEY_URL
Definition md.h:207
#define MD_KEY_SERIAL
Definition md.h:191
#define MD_KEY_ERRORED
Definition md.h:144
#define MD_KEY_FROM
Definition md.h:150
#define MD_KEY_PROBLEM
Definition md.h:177
#define MD_KEY_STAPLING
Definition md.h:193
#define MD_KEY_PROTO
Definition md.h:178
md_t * md_get_by_name(struct apr_array_header_t *mds, const char *name)
Definition md_core.c:151
#define MD_KEY_READY
Definition md.h:179
md_t * md_get_by_domain(struct apr_array_header_t *mds, const char *domain)
Definition md_core.c:163
#define MD_KEY_ERRORS
Definition md.h:146
#define MD_KEY_STATUS
Definition md.h:196
#define MD_KEY_SHA256_FINGERPRINT
Definition md.h:192
#define MD_KEY_FINISHED
Definition md.h:149
#define MD_KEY_RENEWING
Definition md.h:185
#define MD_KEY_LAST
Definition md.h:160
#define MD_KEY_NOTIFIED
Definition md.h:169
#define MD_KEY_DOMAIN
Definition md.h:139
#define MD_KEY_STATE
Definition md.h:194
#define MD_KEY_CA
Definition md.h:122
#define MD_KEY_NEXT_RUN
Definition md.h:168
#define MD_KEY_VALID
Definition md.h:210
#define MD_KEY_URLS
Definition md.h:208
const char * md_get_ca_name_from_url(apr_pool_t *p, const char *url)
Definition md_core.c:417
@ MD_RENEW_MANUAL
Definition md.h:70
#define MD_KEY_CERT
Definition md.h:124
#define MD_KEY_MDS
Definition md.h:164
#define MD_KEY_DETAIL
Definition md.h:136
#define MD_KEY_UNKNOWN
Definition md.h:205
#define MD_KEY_DOMAINS
Definition md.h:140
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
md_json_t * md_json_create(apr_pool_t *pool)
Definition md_json.c:92
apr_status_t md_json_getsa(apr_array_header_t *a, const md_json_t *json,...)
Definition md_json.c:864
int md_json_getb(const md_json_t *json,...)
Definition md_json.c:330
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
long md_json_getl(const md_json_t *json,...)
Definition md_json.c:381
md_json_t * md_json_getj(md_json_t *json,...)
Definition md_json.c:473
int md_json_iterkey(md_json_iterkey_cb *cb, void *baton, md_json_t *json,...)
Definition md_json.c:835
int md_json_is(const md_json_type_t jtype, md_json_t *json,...)
Definition md_json.c:294
apr_status_t md_json_setj(const md_json_t *value, md_json_t *json,...)
Definition md_json.c:527
int md_json_has_key(const md_json_t *json,...)
Definition md_json.c:279
int md_json_itera(md_json_itera_cb *cb, void *baton, md_json_t *json,...)
Definition md_json.c:809
apr_time_t md_json_get_time(const md_json_t *json,...)
Definition md_json.c:444
apr_status_t md_json_writeb(const md_json_t *json, md_json_fmt_t fmt, apr_bucket_brigade *bb)
Definition md_json.c:972
const char * md_json_writep(const md_json_t *json, apr_pool_t *p, md_json_fmt_t fmt)
Definition md_json.c:992
@ MD_JSON_FMT_COMPACT
Definition md_json.h:43
@ MD_JSON_FMT_INDENT
Definition md_json.h:44
@ MD_JSON_TYPE_INT
Definition md_json.h:36
@ MD_JSON_TYPE_ARRAY
Definition md_json.h:33
@ MD_JSON_TYPE_BOOL
Definition md_json.h:37
@ MD_JSON_TYPE_STRING
Definition md_json.h:34
@ MD_JSON_TYPE_OBJECT
Definition md_json.h:32
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
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_status_get_json(md_json_t **pjson, apr_array_header_t *mds, md_reg_t *reg, md_ocsp_reg_t *ocsp, apr_pool_t *p)
Definition md_status.c:267
apr_status_t md_status_get_md_json(md_json_t **pjson, const md_t *md, md_reg_t *reg, md_ocsp_reg_t *ocsp, apr_pool_t *p)
Definition md_status.c:261
void md_status_take_stock(md_json_t **pjson, apr_array_header_t *mds, md_reg_t *reg, apr_pool_t *p)
Definition md_status.c:446
const char * md_duration_roughly(apr_pool_t *p, apr_interval_time_t duration)
Definition md_time.c:113
#define MD_SECS_PER_DAY
Definition md_time.h:23
static void add_status_cell(status_ctx *ctx, md_json_t *mdj, const status_info *info)
static void si_val_names(status_ctx *ctx, md_json_t *mdj, const status_info *info)
static void val_url_print(status_ctx *ctx, const status_info *info, const char *url, const char *proto, int i)
static void print_date(status_ctx *ctx, apr_time_t timestamp, const char *title)
int md_status_handler(request_rec *r)
#define HTML_STATUS(X)
static void si_val_remote_check(status_ctx *ctx, md_json_t *mdj, const status_info *info)
static int add_ocsp_row(void *baton, apr_size_t index, md_json_t *mdj)
#define MD_STATUS_RESOURCE
static const status_info status_infos[]
static void add_json_val(status_ctx *ctx, md_json_t *j)
static int cert_check_iter(void *baton, const char *key, md_json_t *json)
static void si_val_ca_urls(status_ctx *ctx, md_json_t *mdj, const status_info *info)
static void si_val_ocsp_activity(status_ctx *ctx, md_json_t *mdj, const status_info *info)
int md_http_cert_status(request_rec *r)
static void print_time(status_ctx *ctx, const char *label, apr_time_t t)
static int json_iter_val(void *data, size_t index, md_json_t *json)
int md_domains_status_hook(request_rec *r, int flags)
static void si_add_header(status_ctx *ctx, const status_info *info)
static void si_val_valid_time(status_ctx *ctx, md_json_t *mdj, const status_info *info)
static void si_val_activity(status_ctx *ctx, md_json_t *mdj, const status_info *info)
static void si_val_cert_valid_time(status_ctx *ctx, md_json_t *mdj, const status_info *info)
static int add_md_row(void *baton, apr_size_t index, md_json_t *mdj)
static void si_val_url(status_ctx *ctx, md_json_t *mdj, const status_info *info)
static int md_name_cmp(const void *v1, const void *v2)
void add_status_fn(status_ctx *ctx, md_json_t *mdj, const status_info *info)
static void si_val_stapling(status_ctx *ctx, md_json_t *mdj, const status_info *info)
static void print_job_summary(status_ctx *ctx, md_json_t *mdj, const char *key, const char *separator)
static void si_val_status(status_ctx *ctx, md_json_t *mdj, const status_info *info)
static int count_certs(void *baton, const char *key, md_json_t *json)
static const status_info ocsp_status_infos[]
int md_ocsp_status_hook(request_rec *r, int flags)
return NULL
Definition mod_so.c:359
int i
Definition mod_so.c:347
Status Report Extension Module to Apache.
char * name
char * path
Definition apr_uri.h:99
struct apr_bucket_alloc_t * bucket_alloc
Definition httpd.h:1201
apr_array_header_t * mds
int certificate_status_enabled
struct md_reg_t * reg
struct md_ocsp_reg_t * ocsp
md_mod_conf_t * mc
Definition md.h:76
const char * name
Definition md.h:77
struct md_pkeys_spec_t * pks
Definition md.h:81
A structure that represents the current request.
Definition httpd.h:845
struct ap_filter_t * output_filters
Definition httpd.h:1070
const char * handler
Definition httpd.h:994
const char * hostname
Definition httpd.h:883
int method_number
Definition httpd.h:898
apr_pool_t * pool
Definition httpd.h:847
apr_uri_t parsed_uri
Definition httpd.h:1092
conn_rec * connection
Definition httpd.h:849
server_rec * server
Definition httpd.h:851
char * path_info
Definition httpd.h:1024
apr_table_t * headers_out
Definition httpd.h:978
struct ap_conf_vector_t * module_config
Definition httpd.h:1341
apr_bucket_brigade * bb
const char * separator
const char * prefix
const md_mod_conf_t * mc
apr_pool_t * p
add_status_fn * fn
const char * label
const char * key
static apr_time_t now
Definition testtime.c:33
apr_status_t apr_strftime(char *s, apr_size_t *retsize, apr_size_t max, const char *format, apr_time_exp_t *xt)
Definition timestr.c:132
apr_status_t apr_rfc822_date(char *date_str, apr_time_t t)
Definition timestr.c:42
typedef int(WSAAPI *apr_winapi_fpt_WSAPoll)(IN OUT LPWSAPOLLFD fdArray
INT info