%s\">", (index % 2)? "odd" : "even");
        for (i = 0; i < (int)(sizeof(ocsp_status_infos)/sizeof(ocsp_status_infos[0])); ++i) {
            apr_brigade_puts(ctx->bb, NULL, NULL, "");
        }
        apr_brigade_puts(ctx->bb, NULL, NULL, "");
    } else {
        for (i = 0; i < (int)(sizeof(ocsp_status_infos)/sizeof(ocsp_status_infos[0])); ++i) {
            ctx->prefix = apr_pstrcat(ctx->p, prefix, apr_psprintf(ctx->p, "[%" APR_SIZE_T_FMT "]", index), NULL);
            add_status_cell(ctx, mdj, &ocsp_status_infos[i]);
            ctx->prefix = prefix;
        }
    }
    return 1;
}

int md_ocsp_status_hook(request_rec *r, int flags)
{
    const md_srv_conf_t *sc;
    const md_mod_conf_t *mc;
    int i;
    status_ctx ctx;
    md_json_t *jstatus, *jstock;

    ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r, "server-status for ocsp stapling, start");
    sc = ap_get_module_config(r->server->module_config, &md_module);
    if (!sc) return DECLINED;
    mc = sc->mc;
    if (!mc || !mc->server_status_enabled) return DECLINED;

    ctx.p = r->pool;
    ctx.mc = mc;
    ctx.bb = apr_brigade_create(r->pool, r->connection->bucket_alloc);
    ctx.flags = flags;
    ctx.prefix = "ManagedStaplings";
    ctx.separator = " ";

    if (!HTML_STATUS(&ctx)) {
        int total = 0, good = 0, revoked = 0, unknown = 0;
        ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r, "no-html ocsp stapling status summary");
        if (md_ocsp_count(mc->ocsp) > 0) {
            md_ocsp_get_summary(&jstock, mc->ocsp, r->pool);
            ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r, "got JSON ocsp stapling status summary");
            total = (int)md_json_getl(jstock, MD_KEY_TOTAL, NULL);
            good = (int)md_json_getl(jstock, MD_KEY_GOOD, NULL);
            revoked = (int)md_json_getl(jstock, MD_KEY_REVOKED, NULL);
            unknown = (int)md_json_getl(jstock, MD_KEY_UNKNOWN, NULL);
        }
        apr_brigade_printf(ctx.bb, NULL, NULL, "%sTotal: %d\n", ctx.prefix, total);
        apr_brigade_printf(ctx.bb, NULL, NULL, "%sOK: %d\n", ctx.prefix, good);
        apr_brigade_printf(ctx.bb, NULL, NULL, "%sRenew: %d\n", ctx.prefix, revoked);
        apr_brigade_printf(ctx.bb, NULL, NULL, "%sErrored: %d\n", ctx.prefix, unknown);
    }
    if (md_ocsp_count(mc->ocsp) > 0) {
        md_ocsp_get_status_all(&jstatus, mc->ocsp, r->pool);
        ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r, "got JSON ocsp stapling status");
        if (HTML_STATUS(&ctx)) {
            ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r, "html ocsp stapling status table");
            apr_brigade_puts(ctx.bb, NULL, NULL,
                             "
\n

Managed Staplings

\n
Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Apache/modules/md/   (Apache Software Stiftung Version 2.4.65©)  Datei vom 3.5.2025 mit Größe 33 kB image not shown  
", html, html);
    }
}

static void si_val_cert_valid_time(status_ctx *ctx, md_json_t *mdj, const status_info *info)
{
    md_json_t *jcert;
    status_info sub = *info;

    sub.key = MD_KEY_VALID;
    jcert = md_json_getj(mdj, info->key, NULL);
    if (jcert) si_val_valid_time(ctx, jcert, &sub);
}

static void val_url_print(status_ctx *ctx, const status_info *info,
                          const char*url, const char *proto, int i)
{
    const char *s;

    if (proto && !strcmp(proto, "tailscale")) {
        s = "tailscale";
    }
    else if (url) {
        s = md_get_ca_name_from_url(ctx->p, url);
    }
    else {
        return;
    }
    if (HTML_STATUS(ctx)) {
        apr_brigade_printf(ctx->bb, NULL, NULL, "%s%s",
                           i? " " : "",
                           ap_escape_html2(ctx->p, url, 1),
                           ap_escape_html2(ctx->p, s, 1));
    }
    else if (i == 0) {
        apr_brigade_printf(ctx->bb, NULL, NULL, "%s%sName: %s\n",
                           ctx->prefix, info->label, s);
        apr_brigade_printf(ctx->bb, NULL, NULL, "%s%sURL: %s\n",
                           ctx->prefix, info->label, url);
    }
    else {
        apr_brigade_printf(ctx->bb, NULL, NULL, "%s%sName%d: %s\n",
                           ctx->prefix, info->label, i, s);
        apr_brigade_printf(ctx->bb, NULL, NULL, "%s%sURL%d: %s\n",
                           ctx->prefix, info->label, i, url);
    }
}

static void si_val_ca_urls(status_ctx *ctx, md_json_t *mdj, const status_info *info)
{
    md_json_t *jcert;
    const char *proto, *url;
    apr_array_header_t *urls;
    int i;

    jcert = md_json_getj(mdj, info->key, NULL);
    if (!jcert) {
        return;
    }

    proto = md_json_gets(jcert, MD_KEY_PROTO, NULL);
    url = md_json_gets(jcert, MD_KEY_URL, NULL);
    if (url) {
        /* print the effective CA url used, if set */
        val_url_print(ctx, info, url, proto, 0);
    }
    else {
        /* print the available CA urls configured */
        urls = apr_array_make(ctx->p, 3, sizeof(const char*));
        md_json_getsa(urls, jcert, MD_KEY_URLS, NULL);
        for (i = 0; i < urls->nelts; ++i) {
            url = APR_ARRAY_IDX(urls, i, const char*);
            val_url_print(ctx, info, url, proto, i);
        }
    }
}

static int count_certs(void *baton, const char *key, md_json_t *json)
{
    int *pcount = baton;

    (void)json;
    if (strcmp(key, MD_KEY_VALID)) {
        *pcount += 1;
    }
    return 1;
}

static void print_job_summary(status_ctx *ctx, md_json_t *mdj, const char *key,
                              const char *separator)
{
    apr_bucket_brigade *bb = ctx->bb;
    char buffer[HUGE_STRING_LEN];
    apr_status_t rv;
    int finished, errors, cert_count;
    apr_time_t t;
    const char *s, *line;

    if (!md_json_has_key(mdj, key, NULL)) {
        return;
    }

    finished = md_json_getb(mdj, key, MD_KEY_FINISHED, NULL);
    errors = (int)md_json_getl(mdj, key, MD_KEY_ERRORS, NULL);
    rv = (apr_status_t)md_json_getl(mdj, key, MD_KEY_LAST, MD_KEY_STATUS, NULL);

    line = separator? separator : "";

    if (rv != APR_SUCCESS) {
        char *errstr = apr_strerror(rv, buffer, sizeof(buffer));
        s = md_json_gets(mdj, key, MD_KEY_LAST, MD_KEY_PROBLEM, NULL);
        if (HTML_STATUS(ctx)) {
            line = apr_psprintf(bb->p, "%s Error[%s]: %s", line,
                                errstr, s? s : "");
        }
        else {
            apr_brigade_printf(bb, NULL, NULL, "%sLastStatus: %s\n", ctx->prefix, errstr);
            apr_brigade_printf(bb, NULL, NULL, "%sLastProblem: %s\n", ctx->prefix, s);
        }
    }

    if (!HTML_STATUS(ctx)) {
        apr_brigade_printf(bb, NULL, NULL, "%sFinished: %s\n", ctx->prefix,
                           finished ? "yes" : "no");
    }
    if (finished) {
        cert_count = 0;
        md_json_iterkey(count_certs, &cert_count, mdj, key, MD_KEY_CERT, NULL);
        if (HTML_STATUS(ctx)) {
            if (cert_count > 0) {
                line =apr_psprintf(bb->p, "%s finished, %d new certificate%s staged.",
                                   line, cert_count, cert_count > 1? "s" : "");
            }
            else {
                line = apr_psprintf(bb->p, "%s finished successfully.", line);
            }
        }
        else {
            apr_brigade_printf(bb, NULL, NULL, "%sNewStaged: %d\n", ctx->prefix, cert_count);
        }
    }
    else {
        s = md_json_gets(mdj, key, MD_KEY_LAST, MD_KEY_DETAIL, NULL);
        if (s) {
            if (HTML_STATUS(ctx)) {
                line = apr_psprintf(bb->p, "%s %s", line, s);
            }
            else {
                apr_brigade_printf(bb, NULL, NULL, "%sLastDetail: %s\n", ctx->prefix, s);
            }
        }
    }

    errors = (int)md_json_getl(mdj, MD_KEY_ERRORS, NULL);
    if (errors > 0) {
        if (HTML_STATUS(ctx)) {
            line = apr_psprintf(bb->p, "%s (%d retr%s) ", line,
                                errors, (errors > 1)? "y" : "ies");
        }
        else {
            apr_brigade_printf(bb, NULL, NULL, "%sRetries: %d\n", ctx->prefix, errors);
        }
    }

    if (HTML_STATUS(ctx)) {
        apr_brigade_puts(bb, NULL, NULL, line);
    }

    t = md_json_get_time(mdj, key, MD_KEY_NEXT_RUN, NULL);
    if (t > apr_time_now() && !finished) {
        print_time(ctx,
                   HTML_STATUS(ctx) ? "\nNext run" : "NextRun",
                   t);
    }
    else if (line[0] != '\0') {
        if (HTML_STATUS(ctx)) {
            apr_brigade_puts(bb, NULL, NULL, "\nOngoing...");
        }
        else {
            apr_brigade_printf(bb, NULL, NULL, "%s: Ongoing\n", ctx->prefix);
        }
    }
}

static void si_val_activity(status_ctx *ctx, md_json_t *mdj, const status_info *info)
{
    apr_time_t t;
    const char *prefix = ctx->prefix;

    (void)info;
    if (!HTML_STATUS(ctx)) {
        ctx->prefix = apr_pstrcat(ctx->p, prefix, info->label, NULL);
    }

    if (md_json_has_key(mdj, MD_KEY_RENEWAL, NULL)) {
        print_job_summary(ctx, mdj, MD_KEY_RENEWAL, NULL);
        return;
    }

    t = md_json_get_time(mdj, MD_KEY_RENEW_AT, NULL);
    if (t > apr_time_now()) {
        print_time(ctx, "Renew", t);
    }
    else if (t) {
        if (HTML_STATUS(ctx)) {
            apr_brigade_puts(ctx->bb, NULL, NULL, "Pending");
        }
        else {
            apr_brigade_printf(ctx->bb, NULL, NULL, "%s: %s", ctx->prefix, "Pending\n");
        }
    }
    else if (MD_RENEW_MANUAL == md_json_getl(mdj, MD_KEY_RENEW_MODE, NULL)) {
        if (HTML_STATUS(ctx)) {
            apr_brigade_puts(ctx->bb, NULL, NULL, "Manual renew");
        }
        else {
            apr_brigade_printf(ctx->bb, NULL, NULL, "%s: %s", ctx->prefix, "Manual renew\n");
        }
    }
    if (!HTML_STATUS(ctx)) {
        ctx->prefix = prefix;
    }
}

static int cert_check_iter(void *baton, const char *key, md_json_t *json)
{
    status_ctx *ctx = baton;
    const char *fingerprint;

    fingerprint = md_json_gets(json, MD_KEY_SHA256_FINGERPRINT, NULL);
    if (fingerprint) {
        if (HTML_STATUS(ctx)) {
            apr_brigade_printf(ctx->bb, NULL, NULL,
                               "%s%s\">%s[%s]
"
,
                               ctx->mc->cert_check_url, fingerprint,
                               ctx->mc->cert_check_name, key);
        }
        else {
            apr_brigade_printf(ctx->bb, NULL, NULL,
                               "%sType: %s\n",
                               ctx->prefix,
                               key);
            apr_brigade_printf(ctx->bb, NULL, NULL,
                               "%sName: %s\n",
                               ctx->prefix,
                               ctx->mc->cert_check_name);
            apr_brigade_printf(ctx->bb, NULL, NULL,
                               "%sURL: %s%s\n",
                               ctx->prefix,
                               ctx->mc->cert_check_url, fingerprint);
            apr_brigade_printf(ctx->bb, NULL, NULL,
                               "%sFingerprint: %s\n",
                               ctx->prefix,
                               fingerprint);
        }
    }
    return 1;
}

static void si_val_remote_check(status_ctx *ctx, md_json_t *mdj, const status_info *info)
{
    (void)info;
    if (ctx->mc->cert_check_name && ctx->mc->cert_check_url) {
        const char *prefix = ctx->prefix;
        if (!HTML_STATUS(ctx)) {
            ctx->prefix = apr_pstrcat(ctx->p, prefix, info->label, NULL);
        }
        md_json_iterkey(cert_check_iter, ctx, mdj, MD_KEY_CERT, NULL);
        if (!HTML_STATUS(ctx)) {
            ctx->prefix = prefix;
        }
    }
}

static void si_val_stapling(status_ctx *ctx, md_json_t *mdj, const status_info *info)
{
    (void)info;
    if (!md_json_getb(mdj, MD_KEY_STAPLING, NULL)) return;
    if (HTML_STATUS(ctx)) {
        apr_brigade_puts(ctx->bb, NULL, NULL, "on");
    }
    else {
        apr_brigade_printf(ctx->bb, NULL, NULL, "%sStapling: on\n", ctx->prefix);
    }
}

static int json_iter_val(void *data, size_t index, md_json_t *json)
{
    status_ctx *ctx = data;
    const char *prefix = ctx->prefix;
    if (HTML_STATUS(ctx)) {
        if (index) apr_brigade_puts(ctx->bb, NULL, NULL, ctx->separator);
    }
    else {
        ctx->prefix = apr_pstrcat(ctx->p, prefix, apr_psprintf(ctx->p, "[%" APR_SIZE_T_FMT "]", index), NULL);
    }
    add_json_val(ctx, json);
    if (!HTML_STATUS(ctx)) {
        ctx->prefix = prefix;
    }
    return 1;
}

static void add_json_val(status_ctx *ctx, md_json_t *j)
{
    if (!j) return;
    if (md_json_is(MD_JSON_TYPE_ARRAY, j, NULL)) {
        md_json_itera(json_iter_val, ctx, j, NULL);
        return;
    }
    if (!HTML_STATUS(ctx)) {
        apr_brigade_puts(ctx->bb, NULL, NULL, ctx->prefix);
        apr_brigade_puts(ctx->bb, NULL, NULL, ": ");
    }
    if (md_json_is(MD_JSON_TYPE_INT, j, NULL)) {
        md_json_writeb(j, MD_JSON_FMT_COMPACT, ctx->bb);
    }
    else if (md_json_is(MD_JSON_TYPE_STRING, j, NULL)) {
        apr_brigade_puts(ctx->bb, NULL, NULL, md_json_gets(j, NULL));
    }
    else if (md_json_is(MD_JSON_TYPE_OBJECT, j, NULL)) {
        md_json_writeb(j, MD_JSON_FMT_COMPACT, ctx->bb);
    }
    else if (md_json_is(MD_JSON_TYPE_BOOL, j, NULL)) {
        apr_brigade_puts(ctx->bb, NULL, NULL, md_json_getb(j, NULL)? "on" : "off");
    }
    if (!HTML_STATUS(ctx)) {
        apr_brigade_puts(ctx->bb, NULL, NULL, "\n");
    }
}

static void si_val_names(status_ctx *ctx, md_json_t *mdj, const status_info *info)
{
    const char *prefix = ctx->prefix;
    if (HTML_STATUS(ctx)) {
        apr_brigade_puts(ctx->bb, NULL, NULL, "
max-width:400px;\">");
    }
    else {
        ctx->prefix = apr_pstrcat(ctx->p, prefix, info->label, NULL);
    }
    add_json_val(ctx, md_json_getj(mdj, info->key, NULL));
    if (HTML_STATUS(ctx)) {
        apr_brigade_puts(ctx->bb, NULL, NULL, "
"
);
    }
    else {
        ctx->prefix = prefix;
    }
}

static void add_status_cell(status_ctx *ctx, md_json_t *mdj, const status_info *info)
{
    if (info->fn) {
        info->fn(ctx, mdj, info);
    }
    else {
        const char *prefix = ctx->prefix;
        if (!HTML_STATUS(ctx)) {
            ctx->prefix = apr_pstrcat(ctx->p, prefix, info->label, NULL);
        }
        add_json_val(ctx, md_json_getj(mdj, info->key, NULL));
        if (!HTML_STATUS(ctx)) {
            ctx->prefix = prefix;
        }
    }
}

static const status_info status_infos[] = {
    { "Domain", MD_KEY_NAME, NULL },
    { "Names", MD_KEY_DOMAINS, si_val_names },
    { "Status", MD_KEY_STATE, si_val_status },
    { "Valid", MD_KEY_CERT, si_val_cert_valid_time },
    { "CA", MD_KEY_CA, si_val_ca_urls },
    { "Stapling", MD_KEY_STAPLING, si_val_stapling },
    { "CheckAt", MD_KEY_SHA256_FINGERPRINT, si_val_remote_check },
    { "Activity", MD_KEY_NOTIFIED, si_val_activity },
};

static int add_md_row(void *baton, apr_size_t index, md_json_t *mdj)
{
    status_ctx *ctx = baton;
    const char *prefix = ctx->prefix;
    int i;

    if (HTML_STATUS(ctx)) {
        apr_brigade_printf(ctx->bb, NULL, NULL, "%s\">", (index % 2)? "odd" : "even");
        for (i = 0; i < (int)(sizeof(status_infos)/sizeof(status_infos[0])); ++i) {
            apr_brigade_puts(ctx->bb, NULL, NULL, "");
        }
        apr_brigade_puts(ctx->bb, NULL, NULL, "");
    } else {
        for (i = 0; i < (int)(sizeof(status_infos)/sizeof(status_infos[0])); ++i) {
            ctx->prefix = apr_pstrcat(ctx->p, prefix, apr_psprintf(ctx->p, "[%" APR_SIZE_T_FMT "]", index), NULL);
            add_status_cell(ctx, mdj, &status_infos[i]);
            ctx->prefix = prefix;
        }
    }
    return 1;
}

static int md_name_cmp(const void *v1, const void *v2)
{
    return strcmp((*(const md_t**)v1)->name, (*(const md_t**)v2)->name);
}

int md_domains_status_hook(request_rec *r, int flags)
{
    const md_srv_conf_t *sc;
    const md_mod_conf_t *mc;
    int i;
    status_ctx ctx;
    apr_array_header_t *mds;
    md_json_t *jstatus, *jstock;

    ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r, "server-status for managed domains, start");
    sc = ap_get_module_config(r->server->module_config, &md_module);
    if (!sc) return DECLINED;
    mc = sc->mc;
    if (!mc || !mc->server_status_enabled) return DECLINED;

    ctx.p = r->pool;
    ctx.mc = mc;
    ctx.bb = apr_brigade_create(r->pool, r->connection->bucket_alloc);
    ctx.flags = flags;
    ctx.prefix = "ManagedCertificates";
    ctx.separator = " ";

    mds = apr_array_copy(r->pool, mc->mds);
    qsort(mds->elts, (size_t)mds->nelts, sizeof(md_t *), md_name_cmp);

    if (!HTML_STATUS(&ctx)) {
        int total = 0, complete = 0, renewing = 0, errored = 0, ready = 0;
        ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r, "no-html managed domain status summary");
        if (mc->mds->nelts > 0) {
            md_status_take_stock(&jstock, mds, mc->reg, r->pool);
            ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r, "got JSON managed domain status summary");
            total = (int)md_json_getl(jstock, MD_KEY_TOTAL, NULL);
            complete = (int)md_json_getl(jstock, MD_KEY_COMPLETE, NULL);
            renewing = (int)md_json_getl(jstock, MD_KEY_RENEWING, NULL);
            errored = (int)md_json_getl(jstock, MD_KEY_ERRORED, NULL);
            ready = (int)md_json_getl(jstock, MD_KEY_READY, NULL);
        }
        apr_brigade_printf(ctx.bb, NULL, NULL, "%sTotal: %d\n", ctx.prefix, total);
        apr_brigade_printf(ctx.bb, NULL, NULL, "%sOK: %d\n", ctx.prefix, complete);
        apr_brigade_printf(ctx.bb, NULL, NULL, "%sRenew: %d\n", ctx.prefix, renewing);
        apr_brigade_printf(ctx.bb, NULL, NULL, "%sErrored: %d\n", ctx.prefix, errored);
        apr_brigade_printf(ctx.bb, NULL, NULL, "%sReady: %d\n", ctx.prefix, ready);
    }
    if (mc->mds->nelts > 0) {
        md_status_get_json(&jstatus, mds, mc->reg, mc->ocsp, r->pool);
        ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r, "got JSON managed domain status");
        if (HTML_STATUS(&ctx)) {
            ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r, "html managed domain status table");
            apr_brigade_puts(ctx.bb, NULL, NULL,
                             "
\n

Managed Certificates

\n

Quelle  mod_md_status.c   Sprache: C

 
/* Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */


#include <assert.h>
#include <apr_optional.h>
#include <apr_time.h>
#include <apr_date.h>
#include <apr_strings.h>

#include <httpd.h>
#include <http_core.h>
#include <http_protocol.h>
#include <http_request.h>
#include <http_log.h>

#include "mod_status.h"

#include "md.h"
#include "md_curl.h"
#include "md_crypt.h"
#include "md_http.h"
#include "md_ocsp.h"
#include "md_json.h"
#include "md_status.h"
#include "md_store.h"
#include "md_store_fs.h"
#include "md_log.h"
#include "md_reg.h"
#include "md_util.h"
#include "md_version.h"
#include "md_acme.h"
#include "md_acme_authz.h"

#include "mod_md.h"
#include "mod_md_private.h"
#include "mod_md_config.h"
#include "mod_md_drive.h"
#include "mod_md_status.h"

/**************************************************************************************************/
/* Certificate status */

#define APACHE_PREFIX               "/.httpd/"
#define MD_STATUS_RESOURCE          APACHE_PREFIX"certificate-status"
#define HTML_STATUS(X)              (!((X)->flags & AP_STATUS_SHORT))

int md_http_cert_status(request_rec *r)
{
    int i;
    md_json_t *resp, *mdj, *cj;
    const md_srv_conf_t *sc;
    const md_t *md;
    md_pkey_spec_t *spec;
    const char *keyname;
    apr_bucket_brigade *bb;
    apr_status_t rv;

    if (!r->parsed_uri.path || strcmp(MD_STATUS_RESOURCE, r->parsed_uri.path))
        return DECLINED;

    ap_log_rerror(APLOG_MARK, APLOG_TRACE2, 0, r,
                  "requesting status for: %s", r->hostname);

    /* We are looking for information about a staged certificate */
    sc = ap_get_module_config(r->server->module_config, &md_module);
    if (!sc || !sc->mc || !sc->mc->reg || !sc->mc->certificate_status_enabled) return DECLINED;
    md = md_get_by_domain(sc->mc->mds, r->hostname);
    if (!md) return DECLINED;

    if (r->method_number != M_GET) {
        ap_log_rerror(APLOG_MARK, APLOG_TRACE2, 0, r,
                      "md(%s): status supports only GET", md->name);
        return HTTP_NOT_IMPLEMENTED;
    }

    ap_log_rerror(APLOG_MARK, APLOG_TRACE2, 0, r,
                  "requesting status for MD: %s", md->name);

    rv = md_status_get_md_json(&mdj, md, sc->mc->reg, sc->mc->ocsp, r->pool);
    if (APR_SUCCESS != rv) {
        ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(10204)
                      "loading md status for %s", md->name);
        return HTTP_INTERNAL_SERVER_ERROR;
    }

    ap_log_rerror(APLOG_MARK, APLOG_TRACE2, 0, r,
                  "status for MD: %s is %s", md->name, md_json_writep(mdj, r->pool, MD_JSON_FMT_INDENT));

    resp = md_json_create(r->pool);

    if (md_json_has_key(mdj, MD_KEY_CERT, MD_KEY_VALID, NULL)) {
         md_json_setj(md_json_getj(mdj, MD_KEY_CERT, MD_KEY_VALID, NULL), resp, MD_KEY_VALID, NULL);
     }

    for (i = 0; i < md_cert_count(md); ++i) {
        spec = md_pkeys_spec_get(md->pks, i);
        keyname = md_pkey_spec_name(spec);
        cj = md_json_create(r->pool);

        if (md_json_has_key(mdj, MD_KEY_CERT, keyname, MD_KEY_VALID, NULL)) {
            md_json_setj(md_json_getj(mdj, MD_KEY_CERT, keyname, MD_KEY_VALID, NULL),
                         cj, MD_KEY_VALID, NULL);
        }

        if (md_json_has_key(mdj, MD_KEY_CERT, keyname, MD_KEY_SERIAL, NULL)) {
            md_json_sets(md_json_gets(mdj, MD_KEY_CERT, keyname, MD_KEY_SERIAL, NULL),
                         cj, MD_KEY_SERIAL, NULL);
        }
        if (md_json_has_key(mdj, MD_KEY_CERT, keyname, MD_KEY_SHA256_FINGERPRINT, NULL)) {
            md_json_sets(md_json_gets(mdj, MD_KEY_CERT, keyname, MD_KEY_SHA256_FINGERPRINT, NULL),
                         cj, MD_KEY_SHA256_FINGERPRINT, NULL);
        }
        md_json_setj(cj, resp, keyname, NULL );
    }

    if (md_json_has_key(mdj, MD_KEY_RENEWAL, NULL)) {
           /* copy over the information we want to make public about this:
            *  - when not finished, add an empty object to indicate something is going on
            *  - when a certificate is staged, add the information from that */

           cj = md_json_getj(mdj, MD_KEY_RENEWAL, MD_KEY_CERT, NULL);
           cj = cj? cj : md_json_create(r->pool);
           md_json_setj(cj, resp, MD_KEY_RENEWAL, MD_KEY_CERT, NULL);
     }

    ap_log_rerror(APLOG_MARK, APLOG_TRACE2, 0, r, "md[%s]: sending status", md->name);
    apr_table_set(r->headers_out, "Content-Type""application/json");
    bb = apr_brigade_create(r->pool, r->connection->bucket_alloc);
    md_json_writeb(resp, MD_JSON_FMT_INDENT, bb);
    ap_pass_brigade(r->output_filters, bb);
    apr_brigade_cleanup(bb);

    return DONE;
}

/**************************************************************************************************/
/* Status hook */

typedef struct {
    apr_pool_t *p;
    const md_mod_conf_t *mc;
    apr_bucket_brigade *bb;
    int flags;
    const char *prefix;
    const char *separator;
} status_ctx;

typedef struct status_info status_info;

static void add_json_val(status_ctx *ctx, md_json_t *j);

typedef void add_status_fn(status_ctx *ctx, md_json_t *mdj, const status_info *info);

struct status_info {
    const char *label;
    const char *key;
    add_status_fn *fn;
};

static void si_val_status(status_ctx *ctx, md_json_t *mdj, const status_info *info)
{
    const char *s = "unknown";
    apr_time_t until;
    (void)info;
    switch (md_json_getl(mdj, info->key, NULL)) {
        case MD_S_INCOMPLETE:
            s = md_json_gets(mdj, MD_KEY_STATE_DESCR, NULL);
            s = s? apr_psprintf(ctx->p, "incomplete: %s", s) : "incomplete";
            break;
        case MD_S_EXPIRED_DEPRECATED:
        case MD_S_COMPLETE:
            until = md_json_get_time(mdj, MD_KEY_CERT, MD_KEY_VALID, MD_KEY_UNTIL, NULL);
            s = (!until || until > apr_time_now())? "good" : "expired";
            break;
        case MD_S_ERROR: s = "error"break;
        case MD_S_MISSING_INFORMATION: s = "missing information"break;
        defaultbreak;
    }
    if (HTML_STATUS(ctx)) {
        apr_brigade_puts(ctx->bb, NULL, NULL, s);
    }
    else {
        apr_brigade_printf(ctx->bb, NULL, NULL, "%s%s: %s\n",
                           ctx->prefix, info->label, s);
    }
}

static void si_val_url(status_ctx *ctx, md_json_t *mdj, const status_info *info)
{
    const char *url, *s;

    s = url = md_json_gets(mdj, info->key, NULL);
    if (!url) return;
    s = md_get_ca_name_from_url(ctx->p, url);
    if (HTML_STATUS(ctx)) {
        apr_brigade_printf(ctx->bb, NULL, NULL, "%s",
                           ap_escape_html2(ctx->p, url, 1),
                           ap_escape_html2(ctx->p, s, 1));
    }
    else {
        apr_brigade_printf(ctx->bb, NULL, NULL, "%s%sName: %s\n",
                           ctx->prefix, info->label, s);
        apr_brigade_printf(ctx->bb, NULL, NULL, "%s%sURL: %s\n",
                           ctx->prefix, info->label, url);
    }
}

static void print_date(status_ctx *ctx, apr_time_t timestamp, const char *title)
{
    apr_bucket_brigade *bb = ctx->bb;
    if (timestamp > 0) {
        char ts[128];
        char ts2[128];
        apr_time_exp_t texp;
        apr_size_t len;

        apr_time_exp_gmt(&texp, timestamp);
        apr_strftime(ts, &len, sizeof(ts2)-1, "%Y-%m-%d", &texp);
        ts[len] = '\0';
        if (!title) {
            apr_strftime(ts2, &len, sizeof(ts)-1, "%Y-%m-%dT%H:%M:%SZ", &texp);
            ts2[len] = '\0';
            title = ts2;
        }
        if (HTML_STATUS(ctx)) {
            apr_brigade_printf(bb, NULL, NULL,
                               "%s",
                               ap_escape_html2(bb->p, title, 1), ts);
        }
        else {
            apr_brigade_printf(bb, NULL, NULL, "%s%s: %s\n",
                               ctx->prefix, title, ts);
        }
    }
}

static void print_time(status_ctx *ctx, const char *label, apr_time_t t)
{
    apr_bucket_brigade *bb = ctx->bb;
    apr_time_t now;
    const char *pre, *post, *sep;
    char ts[APR_RFC822_DATE_LEN];
    char ts2[128];
    apr_time_exp_t texp;
    apr_size_t len;
    apr_interval_time_t delta;

    if (t == 0) {
        /* timestamp is 0, we use that for "not set" */
        return;
    }
    apr_time_exp_gmt(&texp, t);
    now = apr_time_now();
    pre = post = "";
    sep = (label && strlen(label))? " " : "";
    delta = 0;
    if (HTML_STATUS(ctx)) {
        apr_rfc822_date(ts, t);
        if (t > now) {
            delta = t - now;
            pre = "in ";
        }
        else {
            delta = now - t;
            post = " ago";
        }
        if (delta >= (4 * apr_time_from_sec(MD_SECS_PER_DAY))) {
            apr_strftime(ts2, &len, sizeof(ts2)-1, "%Y-%m-%d", &texp);
            ts2[len] = '\0';
            apr_brigade_printf(bb, NULL, NULL, "%s%s
                               "style='white-space: nowrap;'>%s"
,
                               label, sep, ts, ts2);
        }
        else {
            apr_brigade_printf(bb, NULL, NULL, "%s%s%s%s%s",
                               label, sep, ts, pre, md_duration_roughly(bb->p, delta), post);
        }
    }
    else {
        delta = t - now;
        apr_brigade_printf(bb, NULL, NULL, "%s%s: %" APR_TIME_T_FMT "\n",
                           ctx->prefix, label, apr_time_sec(delta));
    }
}

static void si_val_valid_time(status_ctx *ctx, md_json_t *mdj, const status_info *info)
{
    const char *sfrom, *suntil, *sep, *title;
    apr_time_t from, until;

    sep = NULL;
    sfrom = md_json_gets(mdj, info->key, MD_KEY_FROM, NULL);
    from = sfrom? apr_date_parse_rfc(sfrom) : 0;
    suntil = md_json_gets(mdj, info->key, MD_KEY_UNTIL, NULL);
    until = suntil?apr_date_parse_rfc(suntil) : 0;

    if (HTML_STATUS(ctx)) {
        if (from > apr_time_now()) {
            apr_brigade_puts(ctx->bb, NULL, NULL, "from ");
            print_date(ctx, from, sfrom);
            sep = " ";
        }
        if (until) {
            if (sep) apr_brigade_puts(ctx->bb, NULL, NULL, sep);
            apr_brigade_puts(ctx->bb, NULL, NULL, "until ");
            title = sfrom? apr_psprintf(ctx->p, "%s - %s", sfrom, suntil) : suntil;
            print_date(ctx, until, title);
        }
    }
    else {
        if (from > apr_time_now()) {
            print_date(ctx, from,
            apr_pstrcat(ctx->p, info->label, "From", NULL));
        }
        if (until) {
            print_date(ctx, until,
            apr_pstrcat(ctx->p, info->label, "Until", NULL));
        }
    }
}

static void si_add_header(status_ctx *ctx, const status_info *info)
{
    if (HTML_STATUS(ctx)) {
        const char *html = ap_escape_html2(ctx->p, info->label, 1);
        apr_brigade_printf(ctx->bb, NULL, NULL, "
%s\">%s
");
            add_status_cell(ctx, mdj, &status_infos[i]);
            apr_brigade_puts(ctx->bb, NULL, NULL, "
\n");
            for (i = 0; i < (int)(sizeof(status_infos)/sizeof(status_infos[0])); ++i) {
                si_add_header(&ctx, &status_infos[i]);
            }
            apr_brigade_puts(ctx.bb, NULL, NULL, "\n");
        }
        else {
            ctx.prefix = "ManagedDomain";
        }
        ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r, "iterating JSON managed domain status");
        md_json_itera(add_md_row, &ctx, jstatus, MD_KEY_MDS, NULL);
        if (HTML_STATUS(&ctx)) {
            apr_brigade_puts(ctx.bb, NULL, NULL, "\n\n
\n");
        }
    }

    ap_pass_brigade(r->output_filters, ctx.bb);
    apr_brigade_cleanup(ctx.bb);
    ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r, "server-status for managed domains, end");

    return OK;
}

static void si_val_ocsp_activity(status_ctx *ctx, md_json_t *mdj, const status_info *info)
{
    apr_time_t t;
    const char *prefix = ctx->prefix;

    (void)info;
    if (!HTML_STATUS(ctx)) {
        ctx->prefix = apr_pstrcat(ctx->p, prefix, info->label, NULL);
    }
    t = md_json_get_time(mdj, MD_KEY_RENEW_AT, NULL);
    print_time(ctx, "Refresh", t);
    print_job_summary(ctx, mdj, MD_KEY_RENEWAL, ": ");
    if (!HTML_STATUS(ctx)) {
        ctx->prefix = prefix;
    }
}

static const status_info ocsp_status_infos[] = {
    { "Domain", MD_KEY_DOMAIN, NULL },
    { "CertificateID", MD_KEY_ID, NULL },
    { "OCSPStatus", MD_KEY_STATUS, NULL },
    { "StaplingValid", MD_KEY_VALID, si_val_valid_time },
    { "Responder", MD_KEY_URL, si_val_url },
    { "Activity", MD_KEY_NOTIFIED, si_val_ocsp_activity },
};

static int add_ocsp_row(void *baton, apr_size_t index, md_json_t *mdj)
{
    status_ctx *ctx = baton;
    const char *prefix = ctx->prefix;
    int i;

    if (HTML_STATUS(ctx)) {
        apr_brigade_printf(ctx->bb, NULL, NULL, "
");
            add_status_cell(ctx, mdj, &ocsp_status_infos[i]);
            apr_brigade_puts(ctx->bb, NULL, NULL, "
\n");
            for (i = 0; i < (int)(sizeof(ocsp_status_infos)/sizeof(ocsp_status_infos[0])); ++i) {
                si_add_header(&ctx, &ocsp_status_infos[i]);
            }
            apr_brigade_puts(ctx.bb, NULL, NULL, "\n");
        }
        else {
            ctx.prefix = "ManagedStapling";
        }
        ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r, "iterating JSON ocsp stapling status");
        md_json_itera(add_ocsp_row, &ctx, jstatus, MD_KEY_OCSPS, NULL);
        if (HTML_STATUS(&ctx)) {
            apr_brigade_puts(ctx.bb, NULL, NULL, "\n\n
\n");
        }
    }

    ap_pass_brigade(r->output_filters, ctx.bb);
    apr_brigade_cleanup(ctx.bb);
    ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r, "server-status for ocsp stapling, end");

    return OK;
}

/**************************************************************************************************/
/* Status handlers */

int md_status_handler(request_rec *r)
{
    const md_srv_conf_t *sc;
    const md_mod_conf_t *mc;
    apr_array_header_t *mds;
    md_json_t *jstatus;
    apr_bucket_brigade *bb;
    const md_t *md;
    const char *name;

    if (strcmp(r->handler, "md-status")) {
        return DECLINED;
    }

    sc = ap_get_module_config(r->server->module_config, &md_module);
    if (!sc) return DECLINED;
    mc = sc->mc;
    if (!mc) return DECLINED;

    if (r->method_number != M_GET) {
        ap_log_rerror(APLOG_MARK, APLOG_TRACE2, 0, r, "md-status supports only GET");
        return HTTP_NOT_IMPLEMENTED;
    }

    jstatus = NULL;
    md = NULL;
    if (r->path_info && r->path_info[0] == '/' && r->path_info[1] != '\0') {
        name = strrchr(r->path_info, '/') + 1;
        md = md_get_by_name(mc->mds, name);
        if (!md) md = md_get_by_domain(mc->mds, name);
    }

    if (md) {
        md_status_get_md_json(&jstatus, md, mc->reg, mc->ocsp, r->pool);
    }
    else {
        mds = apr_array_copy(r->pool, mc->mds);
        qsort(mds->elts, (size_t)mds->nelts, sizeof(md_t *), md_name_cmp);
        md_status_get_json(&jstatus, mds, mc->reg, mc->ocsp, r->pool);
    }

    if (jstatus) {
        apr_table_set(r->headers_out, "Content-Type""application/json");
        bb = apr_brigade_create(r->pool, r->connection->bucket_alloc);
        md_json_writeb(jstatus, MD_JSON_FMT_INDENT, bb);
        ap_pass_brigade(r->output_filters, bb);
        apr_brigade_cleanup(bb);

        return DONE;
    }
    return DECLINED;
}

97%


¤ Dauer der Verarbeitung: 0.24 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung ist noch experimentell.