/* 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.
*/
AP_DECLARE_MODULE(md) = {
STANDARD20_MODULE_STUFF,
NULL, /* func to create per dir config */
NULL, /* func to merge per dir config */
md_config_create_svr, /* func to create per server config */
md_config_merge_svr, /* func to merge per server config */
md_cmds, /* command handlers */
md_hooks, #ifdefined(AP_MODULE_FLAG_NONE)
AP_MODULE_FLAG_ALWAYS_MERGE #endif
};
typedefstruct { constchar *reason; /* what the notification is about */
apr_time_t min_interim; /* minimum time between notifying for this reason */
} notify_rate;
static notify_rate notify_rates[] = {
{ "renewing", apr_time_from_sec(MD_SECS_PER_HOUR) }, /* once per hour */
{ "renewed", apr_time_from_sec(MD_SECS_PER_DAY) }, /* once per day */
{ "installed", apr_time_from_sec(MD_SECS_PER_DAY) }, /* once per day */
{ "expiring", apr_time_from_sec(MD_SECS_PER_DAY) }, /* once per day */
{ "errored", apr_time_from_sec(MD_SECS_PER_HOUR) }, /* once per hour */
{ "ocsp-renewed", apr_time_from_sec(MD_SECS_PER_DAY) }, /* once per day */
{ "ocsp-errored", apr_time_from_sec(MD_SECS_PER_HOUR) }, /* once per hour */
};
log_msg_reason = apr_psprintf(p, "message-%s", reason); for (i = 0; i < (int)(sizeof(notify_rates)/sizeof(notify_rates[0])); ++i) { if (!strcmp(reason, notify_rates[i].reason)) {
min_interim = notify_rates[i].min_interim;
}
} if (min_interim > 0) {
since_last.start = md_job_log_get_time_of_latest(job, log_msg_reason);
since_last.end = apr_time_now(); if (since_last.start > 0 && md_timeperiod_length(&since_last) < min_interim) { /* not enough time has passed since we sent the last notification
* for this reason. */
md_log_perror(MD_LOG_MARK, MD_LOG_DEBUG, 0, p, APLOGNO(10267) "%s: rate limiting notification about '%s'", job->mdomain, reason); return APR_SUCCESS;
}
}
/* Directories in group CHALLENGES, STAGING and OCSP are written to * under a different user. Give her ownership.
*/ if (ftype == APR_DIR) { switch (group) { case MD_SG_CHALLENGES: case MD_SG_STAGING: case MD_SG_OCSP:
rv = md_make_worker_accessible(fname, p); if (APR_ENOTIMPL != rv) { return rv;
} break; default: break;
}
} return APR_SUCCESS;
}
static apr_status_t check_coverage(md_t *md, constchar *domain, server_rec *s, int *pupdates, apr_pool_t *p)
{ if (md_contains(md, domain, 0)) { return APR_SUCCESS;
} elseif (md->transitive) {
APR_ARRAY_PUSH(md->domains, constchar*) = apr_pstrdup(p, domain);
*pupdates |= MD_UPD_DOMAINS; return APR_SUCCESS;
} else {
ap_log_error(APLOG_MARK, APLOG_WARNING, 0, s, APLOGNO(10040) "Virtual Host %s:%d matches Managed Domain '%s', but the " "name/alias %s itself is not managed. A requested MD certificate " "will not match ServerName.",
s->server_hostname, s->port, md->name, domain); return APR_SUCCESS;
}
}
static apr_status_t md_cover_server(md_t *md, server_rec *s, int *pupdates, apr_pool_t *p)
{
apr_status_t rv; constchar *name; int i;
if (APR_SUCCESS == (rv = check_coverage(md, s->server_hostname, s, pupdates, p))) {
ap_log_error(APLOG_MARK, APLOG_TRACE1, 0, s, "md[%s]: auto add, covers name %s", md->name, s->server_hostname); for (i = 0; s->names && i < s->names->nelts; ++i) {
name = APR_ARRAY_IDX(s->names, i, constchar*); if (APR_SUCCESS != (rv = check_coverage(md, name, s, pupdates, p))) { break;
}
ap_log_error(APLOG_MARK, APLOG_TRACE1, 0, s, "md[%s]: auto add, covers alias %s", md->name, name);
}
} return rv;
}
staticint uses_port(server_rec *s, int port)
{
server_addr_rec *sa; int match = 0; for (sa = s->addrs; sa; sa = sa->next) { if (sa->host_port == port) { /* host_addr might be general (0.0.0.0) or specific, we count this as match */
match = 1;
} else { /* uses other port/wildcard */ return 0;
}
} return match;
}
static apr_status_t detect_supported_protocols(md_mod_conf_t *mc, server_rec *s,
apr_pool_t *p, int log_level)
{
ap_listen_rec *lr;
apr_sockaddr_t *sa; int can_http, can_https;
if (mc->can_http >= 0 && mc->can_https >= 0) goto set_and_leave;
can_http = can_https = 0; for (lr = ap_listeners; lr; lr = lr->next) { for (sa = lr->bind_addr; sa; sa = sa->next) { if (sa->port == mc->local_80
&& (!lr->protocol || !strncmp("http", lr->protocol, 4))) {
can_http = 1;
} elseif (sa->port == mc->local_443
&& (!lr->protocol || !strncmp("http", lr->protocol, 4))) {
can_https = 1;
}
}
} if (mc->can_http < 0) mc->can_http = can_http; if (mc->can_https < 0) mc->can_https = can_https;
ap_log_error(APLOG_MARK, log_level, 0, s, APLOGNO(10037) "server seems%s reachable via http: and%s reachable via https:",
mc->can_http? "" : " not", mc->can_https? "" : " not");
set_and_leave: return md_reg_set_props(mc->reg, p, mc->can_http, mc->can_https);
}
sc = md_config_get(base_server);
mc = sc->mc;
memset(&r, 0, sizeof(r));
if (md->ca_challenges && md->ca_challenges->nelts > 0) { /* skip the port check if "tls-alpn-01" is pre-configured */
check_port = !(md_array_str_index(md->ca_challenges, MD_AUTHZ_TYPE_TLSALPN01, 0, 0) >= 0);
}
if (check_port && !mc->can_https) return NULL;
/* find an ssl server matching domain from MD */ for (s = base_server; s; s = s->next) {
sc = md_config_get(s); if (!sc || !sc->is_ssl || !sc->assigned) continue; if (base_server == s && !mc->manage_base_server) continue; if (base_server != s && check_port && mc->local_443 > 0 && !uses_port(s, mc->local_443)) continue; for (i = 0; i < sc->assigned->nelts; ++i) { if (md == APR_ARRAY_IDX(sc->assigned, i, md_t*)) {
r.server = s; if (ap_matches_request_vhost(&r, domain, s->port)) { if (check_port) { return s;
} else { /* there may be multiple matching servers because we ignore the port.
if possible, choose a server that supports the acme-tls/1 protocol */ if (ap_is_allowed_protocol(NULL, NULL, s, PROTO_ACME_TLS_1)) { return s;
}
res = s;
}
}
}
}
} return res;
}
/* Collect those domains that support the "acme-tls/1" protocol. This * is part of the MD (and not tested dynamically), since challenge selection
* may be done outside the server, e.g. in the a2md command. */
sc = md_config_get(base_server);
mc = sc->mc;
apr_array_clear(md->acme_tls_1_domains); for (i = 0; i < md->domains->nelts; ++i) {
domain = APR_ARRAY_IDX(md->domains, i, constchar*);
s = get_public_https_server(md, domain, base_server); /* If we did not find a specific virtualhost for md and manage
* the base_server, that one is inspected */ if (NULL == s && mc->manage_base_server) s = base_server; if (NULL == s) {
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, base_server, APLOGNO(10168) "%s: no https server_rec found for %s", md->name, domain); continue;
} if (!ap_is_allowed_protocol(NULL, NULL, s, PROTO_ACME_TLS_1)) {
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, base_server, APLOGNO(10169) "%s: https server_rec for %s does not have protocol %s enabled",
md->name, domain, PROTO_ACME_TLS_1); continue;
}
APR_ARRAY_PUSH(md->acme_tls_1_domains, constchar*) = domain;
}
}
/* Assign the MD to all server_rec configs that it matches. If there already * is an assigned MD not equal this one, the configuration is in error.
*/
memset(&r, 0, sizeof(r)); for (s = base_server; s; s = s->next) { if (!mc->manage_base_server && s == base_server) { /* we shall not assign ourselves to the base server */ continue;
}
r.server = s; for (i = 0; i < md->domains->nelts; ++i) {
domain = APR_ARRAY_IDX(md->domains, i, constchar*);
if ((mc->match_mode == MD_MATCH_ALL &&
ap_matches_request_vhost(&r, domain, s->port))
|| (((mc->match_mode == MD_MATCH_SERVERNAMES) || md_dns_is_wildcard(p, domain)) &&
md_dns_matches(domain, s->server_hostname))) { /* Create a unique md_srv_conf_t record for this server, if there is none yet */
sc = md_config_get_unique(s, p); if (!sc->assigned) sc->assigned = apr_array_make(p, 2, sizeof(md_t*)); if (sc->assigned->nelts == 1 && mc->match_mode == MD_MATCH_SERVERNAMES) { /* there is already an MD assigned for this server. But in
* this match mode, wildcard matches are pre-empted by non-wildcards */ int existing_wild = md_is_wild_match(
APR_ARRAY_IDX(sc->assigned, 0, const md_t*)->domains,
s->server_hostname); if (!existing_wild && md_dns_is_wildcard(p, domain)) continue; /* do not add */ if (existing_wild && !md_dns_is_wildcard(p, domain))
sc->assigned->nelts = 0; /* overwrite existing */
}
APR_ARRAY_PUSH(sc->assigned, md_t*) = md;
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, base_server, APLOGNO(10041) "Server %s:%d matches md %s (config %s, match-mode=%d) " "for domain %s, has now %d MDs",
s->server_hostname, s->port, md->name, sc->name,
mc->match_mode, domain, (int)sc->assigned->nelts);
apr_array_clear(mc->unused_names); for (i = 0; i < mc->mds->nelts; ++i) {
md = APR_ARRAY_IDX(mc->mds, i, md_t*); if (APR_SUCCESS != (rv = link_md_to_servers(mc, md, s, p))) { goto leave;
}
}
leave: return rv;
}
static apr_status_t merge_mds_with_conf(md_mod_conf_t *mc, apr_pool_t *p,
server_rec *base_server, int log_level)
{
md_srv_conf_t *base_conf;
md_t *md, *omd; constchar *domain;
md_timeslice_t *ts;
apr_status_t rv = APR_SUCCESS; int i, j;
/* The global module configuration 'mc' keeps a list of all configured MDomains * in the server. This list is collected during configuration processing and, * in the post config phase, get updated from all merged server configurations * before the server starts processing.
*/
base_conf = md_config_get(base_server);
md_config_get_timespan(&ts, base_conf, MD_CONFIG_RENEW_WINDOW); if (ts) md_reg_set_renew_window_default(mc->reg, ts);
md_config_get_timespan(&ts, base_conf, MD_CONFIG_WARN_WINDOW); if (ts) md_reg_set_warn_window_default(mc->reg, ts);
/* Complete the properties of the MDs, now that we have the complete, merged * server configurations.
*/ for (i = 0; i < mc->mds->nelts; ++i) {
md = APR_ARRAY_IDX(mc->mds, i, md_t*);
merge_srv_config(md, base_conf, p);
if (mc->match_mode == MD_MATCH_ALL) { /* Check that we have no overlap with the MDs already completed */ for (j = 0; j < i; ++j) {
omd = APR_ARRAY_IDX(mc->mds, j, md_t*); if ((domain = md_common_name(md, omd)) != NULL) {
ap_log_error(APLOG_MARK, APLOG_ERR, 0, base_server, APLOGNO(10038) "two Managed Domains have an overlap in domain '%s'" ", first definition in %s(line %d), second in %s(line %d)",
domain, md->defn_name, md->defn_line_number,
omd->defn_name, omd->defn_line_number); return APR_EINVAL;
}
}
}
if (md->cert_files && md->cert_files->nelts) { if (!md->pkey_files || (md->cert_files->nelts != md->pkey_files->nelts)) {
ap_log_error(APLOG_MARK, APLOG_ERR, 0, base_server, APLOGNO(10170) "The Managed Domain '%s' " "needs one MDCertificateKeyFile for each MDCertificateFile.",
md->name); return APR_EINVAL;
}
} elseif (md->pkey_files && md->pkey_files->nelts
&& (!md->cert_files || !md->cert_files->nelts)) {
ap_log_error(APLOG_MARK, APLOG_ERR, 0, base_server, APLOGNO(10171) "The Managed Domain '%s' " "has MDCertificateKeyFile(s) but no MDCertificateFile.",
md->name); return APR_EINVAL;
}
ap_log_error( APLOG_MARK, APLOG_TRACE1, 0, base_server, "checking duplicate ssl assignments"); for (s = base_server; s; s = s->next) {
sc = md_config_get(s); if (!sc || !sc->assigned) continue;
if (sc->assigned->nelts > 1 && sc->is_ssl) { /* duplicate assignment to SSL VirtualHost, not allowed */
ap_log_error(APLOG_MARK, APLOG_ERR, 0, base_server, APLOGNO(10042) "conflict: %d MDs match to SSL VirtualHost %s, there can at most be one.",
(int)sc->assigned->nelts, s->server_hostname); return APR_EINVAL;
}
} return APR_SUCCESS;
}
(void)p;
servers = apr_array_make(ptemp, 5, sizeof(server_rec*));
has_ssl = 0; for (s = base_server; s; s = s->next) {
sc = md_config_get(s); if (!sc || !sc->assigned) continue; for (i = 0; i < sc->assigned->nelts; ++i) { if (md == APR_ARRAY_IDX(sc->assigned, i, md_t*)) {
APR_ARRAY_PUSH(servers, server_rec*) = s; if (sc->is_ssl) has_ssl = 1;
}
}
}
if (!has_ssl && md->require_https > MD_REQUIRE_OFF) { /* We require https for this MD, but do we have a SSL vhost? */
ap_log_error(APLOG_MARK, APLOG_WARNING, 0, base_server, APLOGNO(10105) "MD %s does not match any VirtualHost with 'SSLEngine on', " "but is configured to require https. This cannot work.", md->name);
} if (apr_is_empty_array(servers)) { if (md->renew_mode != MD_RENEW_ALWAYS) { /* Not an error, but looks suspicious */
ap_log_error(APLOG_MARK, APLOG_WARNING, 0, base_server, APLOGNO(10045) "No VirtualHost matches Managed Domain %s", md->name);
APR_ARRAY_PUSH(mc->unused_names, constchar*) = md->name;
}
} return rv;
}
staticint init_cert_watch_status(md_mod_conf_t *mc, apr_pool_t *p, apr_pool_t *ptemp, server_rec *s)
{
md_t *md;
md_result_t *result; int i, count;
/* Calculate the list of MD names which we need to watch: * - all MDs that are used somewhere * - all MDs in drive mode 'AUTO' that are not in 'unused_names'
*/
count = 0;
result = md_result_make(ptemp, APR_SUCCESS); for (i = 0; i < mc->mds->nelts; ++i) {
md = APR_ARRAY_IDX(mc->mds, i, md_t*);
md_result_set(result, APR_SUCCESS, NULL);
md->watched = 0; if (md->state == MD_S_ERROR) {
md_result_set(result, APR_EGENERAL, "in error state, unable to drive forward. This " "indicates an incomplete or inconsistent configuration. " "Please check the log for warnings in this regard."); continue;
}
if (md->renew_mode == MD_RENEW_AUTO
&& md_array_str_index(mc->unused_names, md->name, 0, 0) >= 0) { /* This MD is not used in any virtualhost, do not watch */ continue;
}
if (md_will_renew_cert(md)) { /* make a test init to detect early errors. */
md_reg_test_init(mc->reg, md, mc->env, result, p); if (APR_SUCCESS != result->status && result->detail) {
apr_hash_set(mc->init_errors, md->name, APR_HASH_KEY_STRING, apr_pstrdup(p, result->detail));
ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, APLOGNO(10173) "md[%s]: %s", md->name, result->detail);
}
}
apr_pool_userdata_get(&data, mod_md_init_key, s->process->pool); if (data == NULL) { /* At the first start, httpd makes a config check dry run. It * runs all config hooks to check if it can. If so, it does * this all again and starts serving requests. * * On a dry run, we therefore do all the cheap config things we * need to do to find out if the settings are ok. More expensive * things we delay to the real run.
*/
dry_run = 1;
log_level = APLOG_TRACE1;
ap_log_error( APLOG_MARK, log_level, 0, s, APLOGNO(10070) "initializing post config dry run");
apr_pool_userdata_set((constvoid *)1, mod_md_init_key,
apr_pool_cleanup_null, s->process->pool);
} else {
ap_log_error( APLOG_MARK, APLOG_INFO, 0, s, APLOGNO(10071) "mod_md (v%s), initializing...", MOD_MD_VERSION);
}
/* How to bootstrap this module: * 1. find out if we know if http: and/or https: requests will arrive * 2. apply the now complete configuration settings to the MDs * 3. Link MDs to the server_recs they are used in. Detect unused MDs. * 4. Update the store with the MDs. Change domain names, create new MDs, etc. * Basically all MD properties that are configured directly. * WARNING: this may change the name of an MD. If an MD loses the first * of its domain names, it first gets the new first one as name. The * store will find the old settings and "recover" the previous name. * 5. Load any staged data from previous driving. * 6. on a dry run, this is all we do * 7. Read back the MD properties that reflect the existence and aspect of * credentials that are in the store (or missing there). * Expiry times, MD state, etc. * 8. Determine the list of MDs that need driving/supervision. * 9. Cleanup any left-overs in registry/store that are no longer needed for * the list of MDs as we know it now. * 10. If this list is non-empty, setup a watchdog to run.
*/ /*1*/ if (APR_SUCCESS != (rv = detect_supported_protocols(mc, s, p, log_level))) goto leave; /*2*/ if (APR_SUCCESS != (rv = merge_mds_with_conf(mc, p, s, log_level))) goto leave; /*3*/ if (APR_SUCCESS != (rv = link_mds_to_servers(mc, s, p))) goto leave; /*4*/ if (APR_SUCCESS != (rv = md_reg_lock_global(mc->reg, ptemp))) {
ap_log_error(APLOG_MARK, APLOG_ERR, rv, s, APLOGNO(10398) "unable to obtain global registry lock, " "renewed certificates may remain inactive on " "this httpd instance!"); /* FIXME: or should we fail the server start/reload here? */
rv = APR_SUCCESS; goto leave;
} if (APR_SUCCESS != (rv = md_reg_sync_start(mc->reg, mc->mds, ptemp))) {
ap_log_error(APLOG_MARK, APLOG_ERR, rv, s, APLOGNO(10073) "syncing %d mds to registry", mc->mds->nelts); goto leave;
} /*5*/
md_reg_load_stagings(mc->reg, mc->mds, mc->env, p);
leave: if (mc->reg)
md_reg_unlock_global(mc->reg, ptemp); return rv;
}
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(10113) "get_certificates called for vhost %s.", s->server_hostname);
sc = md_config_get(s); if (!sc) {
ap_log_error(APLOG_MARK, APLOG_TRACE2, 0, s, "asked for certificate of server %s which has no md config",
s->server_hostname); return APR_ENOENT;
}
assert(sc->mc);
reg = sc->mc->reg;
assert(reg);
sc->is_ssl = 1;
if (!sc->assigned) { /* With the new hooks in mod_ssl, we are invoked for all server_rec. It is
* therefore normal, when we have nothing to add here. */ return APR_ENOENT;
} elseif (sc->assigned->nelts != 1) { if (!fallback) {
ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, APLOGNO(10238) "conflict: %d MDs match Virtualhost %s which uses SSL, however " "there can be at most 1.",
(int)sc->assigned->nelts, s->server_hostname);
} return APR_EINVAL;
}
md = APR_ARRAY_IDX(sc->assigned, 0, const md_t*);
for (i = 0; i < md_cert_count(md); ++i) {
spec = md_pkeys_spec_get(md->pks, i);
rv = md_reg_get_cred_files(&keyfile, &chainfile, reg, MD_SG_DOMAINS, md, spec, p); if (APR_SUCCESS == rv) {
APR_ARRAY_PUSH(key_files, constchar*) = keyfile;
APR_ARRAY_PUSH(chain_files, constchar*) = chainfile;
} elseif (APR_STATUS_IS_ENOENT(rv)) { /* certificate for this pkey is not available, others might * if pkeys have been added for a running mdomain.
* see issue #260 */
rv = APR_SUCCESS;
} elseif (!APR_STATUS_IS_ENOENT(rv)) {
ap_log_error(APLOG_MARK, APLOG_ERR, rv, s, APLOGNO(10110) "retrieving credentials for MD %s (%s)",
md->name, md_pkey_spec_name(spec)); return rv;
}
}
if (md_array_is_empty(key_files)) { if (fallback) { /* Provide temporary, self-signed certificate as fallback, so that * clients do not get obscure TLS handshake errors or will see a fallback
* virtual host that is not intended to be served here. */ char *kfn, *cfn;
store = md_reg_store_get(reg);
assert(store);
for (i = 0; i < md_cert_count(md); ++i) {
spec = md_pkeys_spec_get(md->pks, i);
fallback_fnames(p, spec, &kfn, &cfn);
ap_log_error(APLOG_MARK, APLOG_TRACE1, 0, s, "hook ssl_add_cert_files for %s",
s->server_hostname);
rv = get_certificates(s, p, 0, &md_cert_files, &md_key_files); if (APR_SUCCESS == rv) { if (!apr_is_empty_array(cert_files)) { /* downgraded fromm WARNING to DEBUG, since installing separate certificates
* may be a valid use case. */
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(10084) "host '%s' is covered by a Managed Domain, but " "certificate/key files are already configured " "for it (most likely via SSLCertificateFile).",
s->server_hostname);
}
ap_log_error(APLOG_MARK, APLOG_TRACE1, 0, s, "host '%s' is covered by a Managed Domain and " "is being provided with %d key/certificate files.",
s->server_hostname, md_cert_files->nelts);
apr_array_cat(cert_files, md_cert_files);
apr_array_cat(key_files, md_key_files); return DONE;
} return DECLINED;
}
ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, c, "Answer challenge[tls-alpn-01] for %s", servername);
/* A challenge for TLS-ALPN-01 used to have a single certificate, * overriding the single fallback certificate already installed in * the connections SSL* instance. * Since the addition of `MDPrivateKeys`, there can be more than one, * but the server API for a challenge cert can return only one. This * is a short coming of the API. * This means we cannot override all fallback certificates present, just * a single one. If there is an RSA cert in fallback, we need to override * that, because the ACME server has a preference for that (at least LE * has). So we look for an RSA challenge cert first. * The fallback is an EC cert and that works since without RSA challenges, * there should also be no RSA fallbacks.
* Bit of a mess. */
hook_rv = md_get_challenge_cert(c, servername, sc, MD_PKEY_TYPE_DEFAULT,
pcert_pem, pkey_pem); if (hook_rv == DECLINED)
hook_rv = md_get_challenge_cert(c, servername, sc, MD_PKEY_TYPE_RSA,
pcert_pem, pkey_pem); if (hook_rv == DECLINED)
hook_rv = md_get_challenge_cert(c, servername, sc, MD_PKEY_TYPE_EC,
pcert_pem, pkey_pem);
if (md && md->ca_challenges
&& md_array_str_index(md->ca_challenges, MD_AUTHZ_CHA_HTTP_01, 0, 1) < 0) { /* The MD this challenge is for does not allow http-01 challanges, * we have to decline. See #279 for a setup example where this * is necessary.
*/ return DECLINED;
}
rv = md_store_load(store, MD_SG_CHALLENGES, r->hostname,
MD_FN_HTTP01, MD_SV_TEXT, (void**)&data, r->pool);
ap_log_rerror(APLOG_MARK, APLOG_DEBUG, rv, r, "loading challenge for %s (%s)", r->hostname, r->uri); if (APR_SUCCESS == rv) {
apr_size_t len = strlen(data);
if (r->method_number != M_GET) { return HTTP_NOT_IMPLEMENTED;
} /* A GET on a challenge resource for a hostname we are
* configured for. Let's send the content back */
r->status = HTTP_OK;
apr_table_setn(r->headers_out, "Content-Length", apr_ltoa(r->pool, (long)len));
return DONE;
} elseif (!md || md->renew_mode == MD_RENEW_MANUAL
|| (md->cert_files && md->cert_files->nelts
&& md->renew_mode == MD_RENEW_AUTO)) { /* The request hostname is not for a domain - or at least not for * a domain that we renew ourselves. We are not * the sole authority here for /.well-known/acme-challenge (see PR62189). * So, we decline to handle this and give others a chance to provide * the answer.
*/ return DECLINED;
} elseif (APR_STATUS_IS_ENOENT(rv)) { return HTTP_NOT_FOUND;
}
ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(10081) "loading challenge %s from store", name); return HTTP_INTERNAL_SERVER_ERROR;
}
}
} return DECLINED;
}
if (ap_ssl_conn_is_ssl(r->connection)) { /* Using https:
* if 'permanent' and no one else set a HSTS header already, do it */ if (md->require_https == MD_REQUIRE_PERMANENT
&& sc->mc->hsts_header && !apr_table_get(r->headers_out, MD_HSTS_HEADER)) {
apr_table_setn(r->headers_out, MD_HSTS_HEADER, sc->mc->hsts_header);
}
} else { if (md->require_https > MD_REQUIRE_OFF) { /* Not using https:, but require it. Redirect. */ if (r->method_number == M_GET) { /* safe to use the old-fashioned codes */
status = ((MD_REQUIRE_PERMANENT == md->require_https)?
HTTP_MOVED_PERMANENTLY : HTTP_MOVED_TEMPORARILY);
} else { /* these should keep the method unchanged on retry */
status = ((MD_REQUIRE_PERMANENT == md->require_https)?
HTTP_PERMANENT_REDIRECT : HTTP_TEMPORARY_REDIRECT);
}
s = ap_construct_url(r->pool, r->uri, r); if (APR_SUCCESS == apr_uri_parse(r->pool, s, &uri)) {
uri.scheme = (char*)"https";
uri.port = 443;
uri.port_str = (char*)"443";
uri.query = r->parsed_uri.query;
uri.fragment = r->parsed_uri.fragment;
s = apr_uri_unparse(r->pool, &uri, APR_URI_UNP_OMITUSERINFO); if (s && *s) {
apr_table_setn(r->headers_out, "Location", s); return status;
}
}
}
}
declined: return DECLINED;
}
/* Runs once per created child process. Perform any process * related initialization here.
*/ staticvoid md_child_init(apr_pool_t *pool, server_rec *s)
{
(void)pool;
(void)s;
}
/* Install this module into the apache2 infrastructure.
*/ staticvoid md_hooks(apr_pool_t *pool)
{ staticconstchar *const mod_ssl[] = { "mod_ssl.c", "mod_tls.c", NULL}; staticconstchar *const mod_wd[] = { "mod_watchdog.c", NULL};
/* Leave the ssl initialization to mod_ssl or friends. */
md_acme_init(pool, AP_SERVER_BASEVERSION, 0);
ap_log_perror(APLOG_MARK, APLOG_TRACE1, 0, pool, "installing hooks");
/* Run once after configuration is set, before mod_ssl. * Run again after mod_ssl is done.
*/
ap_hook_post_config(md_post_config_before_ssl, NULL, mod_ssl, APR_HOOK_FIRST);
ap_hook_post_config(md_post_config_after_ssl, mod_ssl, mod_wd, APR_HOOK_LAST);
/* Run once after a child process has been created.
*/
ap_hook_child_init(md_child_init, NULL, mod_ssl, APR_HOOK_MIDDLE);
/* answer challenges *very* early, before any configured authentication may strike */
ap_hook_post_read_request(md_require_https_maybe, mod_ssl, NULL, APR_HOOK_MIDDLE);
ap_hook_post_read_request(md_http_challenge_pr, NULL, NULL, APR_HOOK_MIDDLE);
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.