diff options
Diffstat (limited to 'modules/md/mod_md_config.c')
-rw-r--r-- | modules/md/mod_md_config.c | 1048 |
1 files changed, 765 insertions, 283 deletions
diff --git a/modules/md/mod_md_config.c b/modules/md/mod_md_config.c index 336a21b..31d06b4 100644 --- a/modules/md/mod_md_config.c +++ b/modules/md/mod_md_config.c @@ -26,70 +26,105 @@ #include <http_vhost.h> #include "md.h" +#include "md_acme.h" #include "md_crypt.h" +#include "md_log.h" +#include "md_json.h" #include "md_util.h" #include "mod_md_private.h" #include "mod_md_config.h" -#define MD_CMD_MD "MDomain" -#define MD_CMD_OLD_MD "ManagedDomain" #define MD_CMD_MD_SECTION "<MDomainSet" -#define MD_CMD_MD_OLD_SECTION "<ManagedDomain" -#define MD_CMD_BASE_SERVER "MDBaseServer" -#define MD_CMD_CA "MDCertificateAuthority" -#define MD_CMD_CAAGREEMENT "MDCertificateAgreement" -#define MD_CMD_CACHALLENGES "MDCAChallenges" -#define MD_CMD_CAPROTO "MDCertificateProtocol" -#define MD_CMD_DRIVEMODE "MDDriveMode" -#define MD_CMD_MEMBER "MDMember" -#define MD_CMD_MEMBERS "MDMembers" -#define MD_CMD_MUSTSTAPLE "MDMustStaple" -#define MD_CMD_NOTIFYCMD "MDNotifyCmd" -#define MD_CMD_PORTMAP "MDPortMap" -#define MD_CMD_PKEYS "MDPrivateKeys" -#define MD_CMD_PROXY "MDHttpProxy" -#define MD_CMD_RENEWWINDOW "MDRenewWindow" -#define MD_CMD_REQUIREHTTPS "MDRequireHttps" -#define MD_CMD_STOREDIR "MDStoreDir" +#define MD_CMD_MD2_SECTION "<MDomain" #define DEF_VAL (-1) +#ifndef MD_DEFAULT_BASE_DIR +#define MD_DEFAULT_BASE_DIR "md" +#endif + +static md_timeslice_t def_ocsp_keep_window = { + 0, + MD_TIME_OCSP_KEEP_NORM, +}; + +static md_timeslice_t def_ocsp_renew_window = { + MD_TIME_LIFE_NORM, + MD_TIME_RENEW_WINDOW_DEF, +}; + /* Default settings for the global conf */ static md_mod_conf_t defmc = { - NULL, - "md", - NULL, - NULL, - 80, - 443, - 0, - 0, - 0, - MD_HSTS_MAX_AGE_DEFAULT, - NULL, - NULL, - NULL, + NULL, /* list of mds */ +#if AP_MODULE_MAGIC_AT_LEAST(20180906, 2) + NULL, /* base dirm by default state-dir-relative */ +#else + MD_DEFAULT_BASE_DIR, +#endif + NULL, /* proxy url for outgoing http */ + NULL, /* md_reg_t */ + NULL, /* md_ocsp_reg_t */ + 80, /* local http: port */ + 443, /* local https: port */ + -1, /* can http: */ + -1, /* can https: */ + 0, /* manage base server */ + MD_HSTS_MAX_AGE_DEFAULT, /* hsts max-age */ + NULL, /* hsts headers */ + NULL, /* unused names */ + NULL, /* init errors hash */ + NULL, /* notify cmd */ + NULL, /* message cmd */ + NULL, /* env table */ + 0, /* dry_run flag */ + 1, /* server_status_enabled */ + 1, /* certificate_status_enabled */ + &def_ocsp_keep_window, /* default time to keep ocsp responses */ + &def_ocsp_renew_window, /* default time to renew ocsp responses */ + "crt.sh", /* default cert checker site name */ + "https://crt.sh?q=", /* default cert checker site url */ + NULL, /* CA cert file to use */ + apr_time_from_sec(5), /* minimum delay for retries */ + 13, /* retry_failover after 14 errors, with 5s delay ~ half a day */ + 0, /* store locks, disabled by default */ + apr_time_from_sec(5), /* max time to wait to obaint a store lock */ + MD_MATCH_ALL, /* match vhost severname and aliases */ +}; + +static md_timeslice_t def_renew_window = { + MD_TIME_LIFE_NORM, + MD_TIME_RENEW_WINDOW_DEF, +}; +static md_timeslice_t def_warn_window = { + MD_TIME_LIFE_NORM, + MD_TIME_WARN_WINDOW_DEF, }; /* Default server specific setting */ static md_srv_conf_t defconf = { - "default", - NULL, - &defmc, - - 1, - MD_REQUIRE_OFF, - MD_DRIVE_AUTO, - 0, - NULL, - apr_time_from_sec(90 * MD_SECS_PER_DAY), /* If the cert lifetime were 90 days, renew */ - apr_time_from_sec(30 * MD_SECS_PER_DAY), /* 30 days before. Adjust to actual lifetime */ - MD_ACME_DEF_URL, - "ACME", - NULL, - NULL, - NULL, - NULL, + "default", /* name */ + NULL, /* server_rec */ + &defmc, /* mc */ + 1, /* transitive */ + MD_REQUIRE_OFF, /* require https */ + MD_RENEW_AUTO, /* renew mode */ + 0, /* must staple */ + NULL, /* pkey spec */ + &def_renew_window, /* renew window */ + &def_warn_window, /* warn window */ + NULL, /* ca urls */ + NULL, /* ca contact (email) */ + MD_PROTO_ACME, /* ca protocol */ + NULL, /* ca agreemnent */ + NULL, /* ca challenges array */ + NULL, /* ca eab kid */ + NULL, /* ca eab hmac */ + 0, /* stapling */ + 1, /* staple others */ + NULL, /* dns01_cmd */ + NULL, /* currently defined md */ + NULL, /* assigned md, post config */ + 0, /* is_ssl, set during mod_ssl post_config */ }; static md_mod_conf_t *mod_md_config; @@ -112,7 +147,9 @@ static md_mod_conf_t *md_mod_conf_get(apr_pool_t *pool, int create) memcpy(mod_md_config, &defmc, sizeof(*mod_md_config)); mod_md_config->mds = apr_array_make(pool, 5, sizeof(const md_t *)); mod_md_config->unused_names = apr_array_make(pool, 5, sizeof(const md_t *)); - + mod_md_config->env = apr_table_make(pool, 10); + mod_md_config->init_errors = apr_hash_make(pool); + apr_pool_cleanup_register(pool, NULL, cleanup_mod_config, apr_pool_cleanup_null); } @@ -125,46 +162,66 @@ static void srv_conf_props_clear(md_srv_conf_t *sc) { sc->transitive = DEF_VAL; sc->require_https = MD_REQUIRE_UNSET; - sc->drive_mode = DEF_VAL; + sc->renew_mode = DEF_VAL; sc->must_staple = DEF_VAL; - sc->pkey_spec = NULL; - sc->renew_norm = DEF_VAL; - sc->renew_window = DEF_VAL; - sc->ca_url = NULL; + sc->pks = NULL; + sc->renew_window = NULL; + sc->warn_window = NULL; + sc->ca_urls = NULL; + sc->ca_contact = NULL; sc->ca_proto = NULL; sc->ca_agreement = NULL; sc->ca_challenges = NULL; + sc->ca_eab_kid = NULL; + sc->ca_eab_hmac = NULL; + sc->stapling = DEF_VAL; + sc->staple_others = DEF_VAL; + sc->dns01_cmd = NULL; } static void srv_conf_props_copy(md_srv_conf_t *to, const md_srv_conf_t *from) { to->transitive = from->transitive; to->require_https = from->require_https; - to->drive_mode = from->drive_mode; + to->renew_mode = from->renew_mode; to->must_staple = from->must_staple; - to->pkey_spec = from->pkey_spec; - to->renew_norm = from->renew_norm; + to->pks = from->pks; + to->warn_window = from->warn_window; to->renew_window = from->renew_window; - to->ca_url = from->ca_url; + to->ca_urls = from->ca_urls; + to->ca_contact = from->ca_contact; to->ca_proto = from->ca_proto; to->ca_agreement = from->ca_agreement; to->ca_challenges = from->ca_challenges; + to->ca_eab_kid = from->ca_eab_kid; + to->ca_eab_hmac = from->ca_eab_hmac; + to->stapling = from->stapling; + to->staple_others = from->staple_others; + to->dns01_cmd = from->dns01_cmd; } static void srv_conf_props_apply(md_t *md, const md_srv_conf_t *from, apr_pool_t *p) { if (from->require_https != MD_REQUIRE_UNSET) md->require_https = from->require_https; if (from->transitive != DEF_VAL) md->transitive = from->transitive; - if (from->drive_mode != DEF_VAL) md->drive_mode = from->drive_mode; + if (from->renew_mode != DEF_VAL) md->renew_mode = from->renew_mode; if (from->must_staple != DEF_VAL) md->must_staple = from->must_staple; - if (from->pkey_spec) md->pkey_spec = from->pkey_spec; - if (from->renew_norm != DEF_VAL) md->renew_norm = from->renew_norm; - if (from->renew_window != DEF_VAL) md->renew_window = from->renew_window; - - if (from->ca_url) md->ca_url = from->ca_url; + if (from->pks) md->pks = md_pkeys_spec_clone(p, from->pks); + if (from->renew_window) md->renew_window = from->renew_window; + if (from->warn_window) md->warn_window = from->warn_window; + if (from->ca_urls) md->ca_urls = apr_array_copy(p, from->ca_urls); if (from->ca_proto) md->ca_proto = from->ca_proto; if (from->ca_agreement) md->ca_agreement = from->ca_agreement; + if (from->ca_contact) { + apr_array_clear(md->contacts); + APR_ARRAY_PUSH(md->contacts, const char *) = + md_util_schemify(p, from->ca_contact, "mailto"); + } if (from->ca_challenges) md->ca_challenges = apr_array_copy(p, from->ca_challenges); + if (from->ca_eab_kid) md->ca_eab_kid = from->ca_eab_kid; + if (from->ca_eab_hmac) md->ca_eab_hmac = from->ca_eab_hmac; + if (from->stapling != DEF_VAL) md->stapling = from->stapling; + if (from->dns01_cmd) md->dns01_cmd = from->dns01_cmd; } void *md_config_create_svr(apr_pool_t *pool, server_rec *s) @@ -190,23 +247,28 @@ static void *md_config_merge(apr_pool_t *pool, void *basev, void *addv) nsc = (md_srv_conf_t *)apr_pcalloc(pool, sizeof(md_srv_conf_t)); nsc->name = name; nsc->mc = add->mc? add->mc : base->mc; - nsc->assigned = add->assigned? add->assigned : base->assigned; nsc->transitive = (add->transitive != DEF_VAL)? add->transitive : base->transitive; nsc->require_https = (add->require_https != MD_REQUIRE_UNSET)? add->require_https : base->require_https; - nsc->drive_mode = (add->drive_mode != DEF_VAL)? add->drive_mode : base->drive_mode; + nsc->renew_mode = (add->renew_mode != DEF_VAL)? add->renew_mode : base->renew_mode; nsc->must_staple = (add->must_staple != DEF_VAL)? add->must_staple : base->must_staple; - nsc->pkey_spec = add->pkey_spec? add->pkey_spec : base->pkey_spec; - nsc->renew_window = (add->renew_norm != DEF_VAL)? add->renew_norm : base->renew_norm; - nsc->renew_window = (add->renew_window != DEF_VAL)? add->renew_window : base->renew_window; + nsc->pks = (!md_pkeys_spec_is_empty(add->pks))? add->pks : base->pks; + nsc->renew_window = add->renew_window? add->renew_window : base->renew_window; + nsc->warn_window = add->warn_window? add->warn_window : base->warn_window; - nsc->ca_url = add->ca_url? add->ca_url : base->ca_url; + nsc->ca_urls = add->ca_urls? apr_array_copy(pool, add->ca_urls) + : (base->ca_urls? apr_array_copy(pool, base->ca_urls) : NULL); + nsc->ca_contact = add->ca_contact? add->ca_contact : base->ca_contact; nsc->ca_proto = add->ca_proto? add->ca_proto : base->ca_proto; nsc->ca_agreement = add->ca_agreement? add->ca_agreement : base->ca_agreement; nsc->ca_challenges = (add->ca_challenges? apr_array_copy(pool, add->ca_challenges) : (base->ca_challenges? apr_array_copy(pool, base->ca_challenges) : NULL)); + nsc->ca_eab_kid = add->ca_eab_kid? add->ca_eab_kid : base->ca_eab_kid; + nsc->ca_eab_hmac = add->ca_eab_hmac? add->ca_eab_hmac : base->ca_eab_hmac; + nsc->stapling = (add->stapling != DEF_VAL)? add->stapling : base->stapling; + nsc->staple_others = (add->staple_others != DEF_VAL)? add->staple_others : base->staple_others; + nsc->dns01_cmd = (add->dns01_cmd)? add->dns01_cmd : base->dns01_cmd; nsc->current = NULL; - nsc->assigned = NULL; return nsc; } @@ -227,7 +289,7 @@ static int inside_section(cmd_parms *cmd, const char *section) { } static int inside_md_section(cmd_parms *cmd) { - return (inside_section(cmd, MD_CMD_MD_SECTION) || inside_section(cmd, MD_CMD_MD_OLD_SECTION)); + return (inside_section(cmd, MD_CMD_MD_SECTION) || inside_section(cmd, MD_CMD_MD2_SECTION)); } static const char *md_section_check(cmd_parms *cmd) { @@ -238,6 +300,46 @@ static const char *md_section_check(cmd_parms *cmd) { return NULL; } +#define MD_LOC_GLOBAL (0x01) +#define MD_LOC_MD (0x02) +#define MD_LOC_ELSE (0x04) +#define MD_LOC_ALL (0x07) +#define MD_LOC_NOT_MD (0x102) + +static const char *md_conf_check_location(cmd_parms *cmd, int flags) +{ + if (MD_LOC_GLOBAL == flags) { + return ap_check_cmd_context(cmd, GLOBAL_ONLY); + } + if (MD_LOC_NOT_MD == flags && inside_md_section(cmd)) { + return apr_pstrcat(cmd->pool, cmd->cmd->name, " is not allowed inside an '", + MD_CMD_MD_SECTION, "' context", NULL); + } + if (MD_LOC_MD == flags) { + return md_section_check(cmd); + } + else if ((MD_LOC_MD & flags) && inside_md_section(cmd)) { + return NULL; + } + return ap_check_cmd_context(cmd, NOT_IN_DIRECTORY|NOT_IN_LOCATION); +} + +static const char *set_on_off(int *pvalue, const char *s, apr_pool_t *p) +{ + if (!apr_strnatcasecmp("off", s)) { + *pvalue = 0; + } + else if (!apr_strnatcasecmp("on", s)) { + *pvalue = 1; + } + else { + return apr_pstrcat(p, "unknown '", s, + "', supported parameter values are 'on' and 'off'", NULL); + } + return NULL; +} + + static void add_domain_name(apr_array_header_t *domains, const char *name, apr_pool_t *p) { if (md_array_str_index(domains, name, 0, 0) < 0) { @@ -269,7 +371,7 @@ static const char *md_config_sec_start(cmd_parms *cmd, void *mconfig, const char int transitive = -1; (void)mconfig; - if ((err = ap_check_cmd_context(cmd, GLOBAL_ONLY))) { + if ((err = md_conf_check_location(cmd, MD_LOC_NOT_MD))) { return err; } @@ -284,11 +386,11 @@ static const char *md_config_sec_start(cmd_parms *cmd, void *mconfig, const char return MD_CMD_MD_SECTION " > section must specify a unique domain name"; } - name = ap_getword_white(cmd->pool, &arg); + name = ap_getword_conf(cmd->pool, &arg); domains = apr_array_make(cmd->pool, 5, sizeof(const char *)); add_domain_name(domains, name, cmd->pool); while (*arg != '\0') { - name = ap_getword_white(cmd->pool, &arg); + name = ap_getword_conf(cmd->pool, &arg); if (NULL != set_transitive(&transitive, name)) { add_domain_name(domains, name, cmd->pool); } @@ -355,8 +457,7 @@ static const char *md_config_set_names(cmd_parms *cmd, void *dc, int i, transitive = -1; (void)dc; - err = ap_check_cmd_context(cmd, NOT_IN_DIR_LOC_FILE); - if (err) { + if ((err = md_conf_check_location(cmd, MD_LOC_NOT_MD))) { return err; } @@ -385,16 +486,42 @@ static const char *md_config_set_names(cmd_parms *cmd, void *dc, return NULL; } -static const char *md_config_set_ca(cmd_parms *cmd, void *dc, const char *value) +static const char *md_config_set_ca(cmd_parms *cmd, void *dc, + int argc, char *const argv[]) +{ + md_srv_conf_t *sc = md_config_get(cmd->server); + const char *err, *url; + int i; + + (void)dc; + if ((err = md_conf_check_location(cmd, MD_LOC_ALL))) { + return err; + } + if (!sc->ca_urls) { + sc->ca_urls = apr_array_make(cmd->pool, 3, sizeof(const char *)); + } + else { + apr_array_clear(sc->ca_urls); + } + for (i = 0; i < argc; ++i) { + if (APR_SUCCESS != md_get_ca_url_from_name(&url, cmd->pool, argv[i])) { + return url; + } + APR_ARRAY_PUSH(sc->ca_urls, const char *) = url; + } + return NULL; +} + +static const char *md_config_set_contact(cmd_parms *cmd, void *dc, const char *value) { md_srv_conf_t *sc = md_config_get(cmd->server); const char *err; (void)dc; - if (!inside_md_section(cmd) && (err = ap_check_cmd_context(cmd, GLOBAL_ONLY))) { + if ((err = md_conf_check_location(cmd, MD_LOC_ALL))) { return err; } - sc->ca_url = value; + sc->ca_contact = value; return NULL; } @@ -404,7 +531,7 @@ static const char *md_config_set_ca_proto(cmd_parms *cmd, void *dc, const char * const char *err; (void)dc; - if (!inside_md_section(cmd) && (err = ap_check_cmd_context(cmd, GLOBAL_ONLY))) { + if ((err = md_conf_check_location(cmd, MD_LOC_ALL))) { return err; } config->ca_proto = value; @@ -417,37 +544,37 @@ static const char *md_config_set_agreement(cmd_parms *cmd, void *dc, const char const char *err; (void)dc; - if (!inside_md_section(cmd) && (err = ap_check_cmd_context(cmd, GLOBAL_ONLY))) { + if ((err = md_conf_check_location(cmd, MD_LOC_ALL))) { return err; } config->ca_agreement = value; return NULL; } -static const char *md_config_set_drive_mode(cmd_parms *cmd, void *dc, const char *value) +static const char *md_config_set_renew_mode(cmd_parms *cmd, void *dc, const char *value) { md_srv_conf_t *config = md_config_get(cmd->server); const char *err; - md_drive_mode_t drive_mode; + md_renew_mode_t renew_mode; (void)dc; if (!apr_strnatcasecmp("auto", value) || !apr_strnatcasecmp("automatic", value)) { - drive_mode = MD_DRIVE_AUTO; + renew_mode = MD_RENEW_AUTO; } else if (!apr_strnatcasecmp("always", value)) { - drive_mode = MD_DRIVE_ALWAYS; + renew_mode = MD_RENEW_ALWAYS; } else if (!apr_strnatcasecmp("manual", value) || !apr_strnatcasecmp("stick", value)) { - drive_mode = MD_DRIVE_MANUAL; + renew_mode = MD_RENEW_MANUAL; } else { return apr_pstrcat(cmd->pool, "unknown MDDriveMode ", value, NULL); } - if (!inside_md_section(cmd) && (err = ap_check_cmd_context(cmd, GLOBAL_ONLY))) { + if ((err = md_conf_check_location(cmd, MD_LOC_ALL))) { return err; } - config->drive_mode = drive_mode; + config->renew_mode = renew_mode; return NULL; } @@ -457,54 +584,137 @@ static const char *md_config_set_must_staple(cmd_parms *cmd, void *dc, const cha const char *err; (void)dc; - if (!inside_md_section(cmd) && (err = ap_check_cmd_context(cmd, GLOBAL_ONLY))) { + if ((err = md_conf_check_location(cmd, MD_LOC_ALL))) { return err; } + return set_on_off(&config->must_staple, value, cmd->pool); +} - if (!apr_strnatcasecmp("off", value)) { - config->must_staple = 0; +static const char *md_config_set_stapling(cmd_parms *cmd, void *dc, const char *value) +{ + md_srv_conf_t *config = md_config_get(cmd->server); + const char *err; + + (void)dc; + if ((err = md_conf_check_location(cmd, MD_LOC_ALL))) { + return err; } - else if (!apr_strnatcasecmp("on", value)) { - config->must_staple = 1; + return set_on_off(&config->stapling, value, cmd->pool); +} + +static const char *md_config_set_staple_others(cmd_parms *cmd, void *dc, const char *value) +{ + md_srv_conf_t *config = md_config_get(cmd->server); + const char *err; + + (void)dc; + if ((err = md_conf_check_location(cmd, MD_LOC_ALL))) { + return err; } - else { - return apr_pstrcat(cmd->pool, "unknown '", value, - "', supported parameter values are 'on' and 'off'", NULL); + return set_on_off(&config->staple_others, value, cmd->pool); +} + +static const char *md_config_set_base_server(cmd_parms *cmd, void *dc, const char *value) +{ + md_srv_conf_t *config = md_config_get(cmd->server); + const char *err = md_conf_check_location(cmd, MD_LOC_NOT_MD); + + (void)dc; + if (err) return err; + return set_on_off(&config->mc->manage_base_server, value, cmd->pool); +} + +static const char *md_config_set_min_delay(cmd_parms *cmd, void *dc, const char *value) +{ + md_srv_conf_t *config = md_config_get(cmd->server); + const char *err = md_conf_check_location(cmd, MD_LOC_NOT_MD); + apr_time_t delay; + + (void)dc; + if (err) return err; + if (md_duration_parse(&delay, value, "s") != APR_SUCCESS) { + return "unrecognized duration format"; } + config->mc->min_delay = delay; return NULL; } -static const char *md_config_set_base_server(cmd_parms *cmd, void *dc, const char *value) +static const char *md_config_set_retry_failover(cmd_parms *cmd, void *dc, const char *value) { md_srv_conf_t *config = md_config_get(cmd->server); - const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY); + const char *err = md_conf_check_location(cmd, MD_LOC_NOT_MD); + int retry_failover; (void)dc; - if (!err) { - if (!apr_strnatcasecmp("off", value)) { - config->mc->manage_base_server = 0; - } - else if (!apr_strnatcasecmp("on", value)) { - config->mc->manage_base_server = 1; - } - else { - err = apr_pstrcat(cmd->pool, "unknown '", value, - "', supported parameter values are 'on' and 'off'", NULL); + if (err) return err; + retry_failover = atoi(value); + if (retry_failover <= 0) { + return "invalid argument, must be a number > 0"; + } + config->mc->retry_failover = retry_failover; + return NULL; +} + +static const char *md_config_set_store_locks(cmd_parms *cmd, void *dc, const char *s) +{ + md_srv_conf_t *config = md_config_get(cmd->server); + const char *err = md_conf_check_location(cmd, MD_LOC_NOT_MD); + int use_store_locks; + apr_time_t wait_time = 0; + + (void)dc; + if (err) { + return err; + } + else if (!apr_strnatcasecmp("off", s)) { + use_store_locks = 0; + } + else if (!apr_strnatcasecmp("on", s)) { + use_store_locks = 1; + } + else { + if (md_duration_parse(&wait_time, s, "s") != APR_SUCCESS) { + return "neither 'on', 'off' or a duration specified"; } + use_store_locks = (wait_time != 0); } - return err; + config->mc->use_store_locks = use_store_locks; + if (wait_time) { + config->mc->lock_wait_timeout = wait_time; + } + return NULL; } -static const char *md_config_set_require_https(cmd_parms *cmd, void *dc, const char *value) +static const char *md_config_set_match_mode(cmd_parms *cmd, void *dc, const char *s) { md_srv_conf_t *config = md_config_get(cmd->server); - const char *err; + const char *err = md_conf_check_location(cmd, MD_LOC_NOT_MD); (void)dc; - if (!inside_md_section(cmd) && (err = ap_check_cmd_context(cmd, GLOBAL_ONLY))) { + if (err) { return err; } + else if (!apr_strnatcasecmp("all", s)) { + config->mc->match_mode = MD_MATCH_ALL; + } + else if (!apr_strnatcasecmp("servernames", s)) { + config->mc->match_mode = MD_MATCH_SERVERNAMES; + } + else { + return "invalid argument, must be a 'all' or 'servernames'"; + } + return NULL; +} +static const char *md_config_set_require_https(cmd_parms *cmd, void *dc, const char *value) +{ + md_srv_conf_t *config = md_config_get(cmd->server); + const char *err; + + if ((err = md_conf_check_location(cmd, MD_LOC_ALL))) { + return err; + } + (void)dc; if (!apr_strnatcasecmp("off", value)) { config->require_https = MD_REQUIRE_OFF; } @@ -521,98 +731,48 @@ static const char *md_config_set_require_https(cmd_parms *cmd, void *dc, const c return NULL; } -static apr_status_t duration_parse(const char *value, apr_interval_time_t *ptimeout, - const char *def_unit) -{ - char *endp; - long funits = 1; - apr_status_t rv; - apr_int64_t n; - - n = apr_strtoi64(value, &endp, 10); - if (errno) { - return errno; - } - if (!endp || !*endp) { - if (strcmp(def_unit, "d") == 0) { - def_unit = "s"; - funits = MD_SECS_PER_DAY; - } - } - else if (endp == value) { - return APR_EINVAL; - } - else if (*endp == 'd') { - *ptimeout = apr_time_from_sec(n * MD_SECS_PER_DAY); - return APR_SUCCESS; - } - else { - def_unit = endp; - } - rv = ap_timeout_parameter_parse(value, ptimeout, def_unit); - if (APR_SUCCESS == rv && funits > 1) { - *ptimeout *= funits; - } - return rv; -} - -static apr_status_t percentage_parse(const char *value, int *ppercent) +static const char *md_config_set_renew_window(cmd_parms *cmd, void *dc, const char *value) { - char *endp; - apr_int64_t n; + md_srv_conf_t *config = md_config_get(cmd->server); + const char *err; - n = apr_strtoi64(value, &endp, 10); - if (errno) { - return errno; + (void)dc; + if ((err = md_conf_check_location(cmd, MD_LOC_ALL))) { + return err; } - if (*endp == '%') { - if (n < 0 || n >= 100) { - return APR_BADARG; - } - *ppercent = (int)n; - return APR_SUCCESS; + err = md_timeslice_parse(&config->renew_window, cmd->pool, value, MD_TIME_LIFE_NORM); + if (!err && config->renew_window->norm + && (config->renew_window->len >= config->renew_window->norm)) { + err = "a length of 100% or more is not allowed."; } - return APR_EINVAL; + if (err) return apr_psprintf(cmd->pool, "MDRenewWindow %s", err); + return NULL; } -static const char *md_config_set_renew_window(cmd_parms *cmd, void *dc, const char *value) +static const char *md_config_set_warn_window(cmd_parms *cmd, void *dc, const char *value) { md_srv_conf_t *config = md_config_get(cmd->server); const char *err; - apr_interval_time_t timeout; - int percent = 0; (void)dc; - if (!inside_md_section(cmd) - && (err = ap_check_cmd_context(cmd, GLOBAL_ONLY))) { + if ((err = md_conf_check_location(cmd, MD_LOC_ALL))) { return err; } - - /* Inspired by http_core.c */ - if (duration_parse(value, &timeout, "d") == APR_SUCCESS) { - config->renew_norm = 0; - config->renew_window = timeout; - return NULL; - } - else { - switch (percentage_parse(value, &percent)) { - case APR_SUCCESS: - config->renew_norm = apr_time_from_sec(100 * MD_SECS_PER_DAY); - config->renew_window = apr_time_from_sec(percent * MD_SECS_PER_DAY); - return NULL; - case APR_BADARG: - return "MDRenewWindow as percent must be less than 100"; - } + err = md_timeslice_parse(&config->warn_window, cmd->pool, value, MD_TIME_LIFE_NORM); + if (!err && config->warn_window->norm + && (config->warn_window->len >= config->warn_window->norm)) { + err = "a length of 100% or more is not allowed."; } - return "MDRenewWindow has unrecognized format"; + if (err) return apr_psprintf(cmd->pool, "MDWarnWindow %s", err); + return NULL; } static const char *md_config_set_proxy(cmd_parms *cmd, void *arg, const char *value) { md_srv_conf_t *sc = md_config_get(cmd->server); - const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY); + const char *err; - if (err) { + if ((err = md_conf_check_location(cmd, MD_LOC_NOT_MD))) { return err; } md_util_abs_http_uri_check(cmd->pool, value, &err); @@ -627,9 +787,9 @@ static const char *md_config_set_proxy(cmd_parms *cmd, void *arg, const char *va static const char *md_config_set_store_dir(cmd_parms *cmd, void *arg, const char *value) { md_srv_conf_t *sc = md_config_get(cmd->server); - const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY); + const char *err; - if (err) { + if ((err = md_conf_check_location(cmd, MD_LOC_NOT_MD))) { return err; } sc->mc->base_dir = value; @@ -640,11 +800,19 @@ static const char *md_config_set_store_dir(cmd_parms *cmd, void *arg, const char static const char *set_port_map(md_mod_conf_t *mc, const char *value) { int net_port, local_port; - char *endp; + const char *endp; - net_port = (int)apr_strtoi64(value, &endp, 10); - if (errno) { - return "unable to parse first port number"; + if (!strncmp("http:", value, sizeof("http:") - 1)) { + net_port = 80; endp = value + sizeof("http") - 1; + } + else if (!strncmp("https:", value, sizeof("https:") - 1)) { + net_port = 443; endp = value + sizeof("https") - 1; + } + else { + net_port = (int)apr_strtoi64(value, (char**)&endp, 10); + if (errno) { + return "unable to parse first port number"; + } } if (!endp || *endp != ':') { return "no ':' after first port number"; @@ -654,7 +822,7 @@ static const char *set_port_map(md_mod_conf_t *mc, const char *value) local_port = 0; } else { - local_port = (int)apr_strtoi64(endp, &endp, 10); + local_port = (int)apr_strtoi64(endp, (char**)&endp, 10); if (errno) { return "unable to parse second port number"; } @@ -679,10 +847,10 @@ static const char *md_config_set_port_map(cmd_parms *cmd, void *arg, const char *v1, const char *v2) { md_srv_conf_t *sc = md_config_get(cmd->server); - const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY); + const char *err; (void)arg; - if (!err) { + if (!(err = md_conf_check_location(cmd, MD_LOC_NOT_MD))) { err = set_port_map(sc->mc, v1); } if (!err && v2) { @@ -700,14 +868,16 @@ static const char *md_config_set_cha_tyes(cmd_parms *cmd, void *dc, int i; (void)dc; - if (!inside_md_section(cmd) - && (err = ap_check_cmd_context(cmd, GLOBAL_ONLY))) { + if ((err = md_conf_check_location(cmd, MD_LOC_ALL))) { return err; } pcha = &config->ca_challenges; ca_challenges = *pcha; - if (!ca_challenges) { + if (ca_challenges) { + apr_array_clear(ca_challenges); + } + else { *pcha = ca_challenges = apr_array_make(cmd->pool, 5, sizeof(const char *)); } for (i = 0; i < argc; ++i) { @@ -723,60 +893,81 @@ static const char *md_config_set_pkeys(cmd_parms *cmd, void *dc, md_srv_conf_t *config = md_config_get(cmd->server); const char *err, *ptype; apr_int64_t bits; + int i; (void)dc; - if (!inside_md_section(cmd) - && (err = ap_check_cmd_context(cmd, GLOBAL_ONLY))) { + if ((err = md_conf_check_location(cmd, MD_LOC_ALL))) { return err; } if (argc <= 0) { return "needs to specify the private key type"; } - ptype = argv[0]; - if (!apr_strnatcasecmp("Default", ptype)) { - if (argc > 1) { - return "type 'Default' takes no parameter"; - } - if (!config->pkey_spec) { - config->pkey_spec = apr_pcalloc(cmd->pool, sizeof(*config->pkey_spec)); + config->pks = md_pkeys_spec_make(cmd->pool); + for (i = 0; i < argc; ++i) { + ptype = argv[i]; + if (!apr_strnatcasecmp("Default", ptype)) { + if (argc > 1) { + return "'Default' allows no other parameter"; + } + md_pkeys_spec_add_default(config->pks); } - config->pkey_spec->type = MD_PKEY_TYPE_DEFAULT; - return NULL; - } - else if (!apr_strnatcasecmp("RSA", ptype)) { - if (argc == 1) { - bits = MD_PKEY_RSA_BITS_DEF; + else if (strlen(ptype) > 3 + && (ptype[0] == 'R' || ptype[0] == 'r') + && (ptype[1] == 'S' || ptype[1] == 's') + && (ptype[2] == 'A' || ptype[2] == 'a') + && isdigit(ptype[3])) { + bits = (int)apr_atoi64(ptype+3); + if (bits < MD_PKEY_RSA_BITS_MIN) { + return apr_psprintf(cmd->pool, + "must be %d or higher in order to be considered safe.", + MD_PKEY_RSA_BITS_MIN); + } + if (bits >= INT_MAX) { + return apr_psprintf(cmd->pool, "is too large for an RSA key length."); + } + if (md_pkeys_spec_contains_rsa(config->pks)) { + return "two keys of type 'RSA' are not possible."; + } + md_pkeys_spec_add_rsa(config->pks, (unsigned int)bits); } - else if (argc == 2) { - bits = (int)apr_atoi64(argv[1]); - if (bits < MD_PKEY_RSA_BITS_MIN || bits >= INT_MAX) { - return apr_psprintf(cmd->pool, "must be %d or higher in order to be considered " - "safe. Too large a value will slow down everything. Larger then 4096 probably does " - "not make sense unless quantum cryptography really changes spin.", - MD_PKEY_RSA_BITS_MIN); + else if (!apr_strnatcasecmp("RSA", ptype)) { + if (i+1 >= argc || !isdigit(argv[i+1][0])) { + bits = MD_PKEY_RSA_BITS_DEF; + } + else { + ++i; + bits = (int)apr_atoi64(argv[i]); + if (bits < MD_PKEY_RSA_BITS_MIN) { + return apr_psprintf(cmd->pool, + "must be %d or higher in order to be considered safe.", + MD_PKEY_RSA_BITS_MIN); + } + if (bits >= INT_MAX) { + return apr_psprintf(cmd->pool, "is too large for an RSA key length."); + } + } + if (md_pkeys_spec_contains_rsa(config->pks)) { + return "two keys of type 'RSA' are not possible."; } + md_pkeys_spec_add_rsa(config->pks, (unsigned int)bits); } else { - return "key type 'RSA' has only one optional parameter, the number of bits"; - } - - if (!config->pkey_spec) { - config->pkey_spec = apr_pcalloc(cmd->pool, sizeof(*config->pkey_spec)); + if (md_pkeys_spec_contains_ec(config->pks, argv[i])) { + return apr_psprintf(cmd->pool, "two keys of type '%s' are not possible.", argv[i]); + } + md_pkeys_spec_add_ec(config->pks, argv[i]); } - config->pkey_spec->type = MD_PKEY_TYPE_RSA; - config->pkey_spec->params.rsa.bits = (unsigned int)bits; - return NULL; } - return apr_pstrcat(cmd->pool, "unsupported private key type \"", ptype, "\"", NULL); + return NULL; } static const char *md_config_set_notify_cmd(cmd_parms *cmd, void *mconfig, const char *arg) { md_srv_conf_t *sc = md_config_get(cmd->server); - const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY); + const char *err; - if (err) { + if ((err = md_conf_check_location(cmd, MD_LOC_NOT_MD))) { return err; } sc->mc->notify_cmd = arg; @@ -784,70 +975,335 @@ static const char *md_config_set_notify_cmd(cmd_parms *cmd, void *mconfig, const return NULL; } -static const char *md_config_set_names_old(cmd_parms *cmd, void *dc, - int argc, char *const argv[]) +static const char *md_config_set_msg_cmd(cmd_parms *cmd, void *mconfig, const char *arg) { - ap_log_error( APLOG_MARK, APLOG_WARNING, 0, cmd->server, - "mod_md: directive 'ManagedDomain' is deprecated, replace with 'MDomain'."); - return md_config_set_names(cmd, dc, argc, argv); + md_srv_conf_t *sc = md_config_get(cmd->server); + const char *err; + + if ((err = md_conf_check_location(cmd, MD_LOC_NOT_MD))) { + return err; + } + sc->mc->message_cmd = arg; + (void)mconfig; + return NULL; } -static const char *md_config_sec_start_old(cmd_parms *cmd, void *mconfig, const char *arg) +static const char *md_config_set_dns01_cmd(cmd_parms *cmd, void *mconfig, const char *arg) { - ap_log_error( APLOG_MARK, APLOG_WARNING, 0, cmd->server, - "mod_md: directive '<ManagedDomain' is deprecated, replace with '<MDomainSet'."); - return md_config_sec_start(cmd, mconfig, arg); + md_srv_conf_t *sc = md_config_get(cmd->server); + const char *err; + + if ((err = md_conf_check_location(cmd, MD_LOC_ALL))) { + return err; + } + + if (inside_md_section(cmd)) { + sc->dns01_cmd = arg; + } else { + apr_table_set(sc->mc->env, MD_KEY_CMD_DNS01, arg); + } + + (void)mconfig; + return NULL; +} + +static const char *md_config_set_dns01_version(cmd_parms *cmd, void *mconfig, const char *value) +{ + md_srv_conf_t *sc = md_config_get(cmd->server); + const char *err; + + (void)mconfig; + if ((err = md_conf_check_location(cmd, MD_LOC_NOT_MD))) { + return err; + } + if (!strcmp("1", value) || !strcmp("2", value)) { + apr_table_set(sc->mc->env, MD_KEY_DNS01_VERSION, value); + } + else { + return "Only versions `1` and `2` are supported"; + } + return NULL; +} + +static const char *md_config_add_cert_file(cmd_parms *cmd, void *mconfig, const char *arg) +{ + md_srv_conf_t *sc = md_config_get(cmd->server); + const char *err, *fpath; + + (void)mconfig; + if ((err = md_conf_check_location(cmd, MD_LOC_MD))) return err; + assert(sc->current); + fpath = ap_server_root_relative(cmd->pool, arg); + if (!fpath) return apr_psprintf(cmd->pool, "certificate file not found: %s", arg); + if (!sc->current->cert_files) { + sc->current->cert_files = apr_array_make(cmd->pool, 3, sizeof(char*)); + } + APR_ARRAY_PUSH(sc->current->cert_files, const char*) = fpath; + return NULL; +} + +static const char *md_config_add_key_file(cmd_parms *cmd, void *mconfig, const char *arg) +{ + md_srv_conf_t *sc = md_config_get(cmd->server); + const char *err, *fpath; + + (void)mconfig; + if ((err = md_conf_check_location(cmd, MD_LOC_MD))) return err; + assert(sc->current); + fpath = ap_server_root_relative(cmd->pool, arg); + if (!fpath) return apr_psprintf(cmd->pool, "certificate key file not found: %s", arg); + if (!sc->current->pkey_files) { + sc->current->pkey_files = apr_array_make(cmd->pool, 3, sizeof(char*)); + } + APR_ARRAY_PUSH(sc->current->pkey_files, const char*) = fpath; + return NULL; +} + +static const char *md_config_set_server_status(cmd_parms *cmd, void *dc, const char *value) +{ + md_srv_conf_t *sc = md_config_get(cmd->server); + const char *err; + + (void)dc; + if ((err = md_conf_check_location(cmd, MD_LOC_ALL))) { + return err; + } + return set_on_off(&sc->mc->server_status_enabled, value, cmd->pool); +} + +static const char *md_config_set_certificate_status(cmd_parms *cmd, void *dc, const char *value) +{ + md_srv_conf_t *sc = md_config_get(cmd->server); + const char *err; + + (void)dc; + if ((err = md_conf_check_location(cmd, MD_LOC_ALL))) { + return err; + } + return set_on_off(&sc->mc->certificate_status_enabled, value, cmd->pool); +} + +static const char *md_config_set_ocsp_keep_window(cmd_parms *cmd, void *dc, const char *value) +{ + md_srv_conf_t *sc = md_config_get(cmd->server); + const char *err; + + (void)dc; + if ((err = md_conf_check_location(cmd, MD_LOC_ALL))) { + return err; + } + err = md_timeslice_parse(&sc->mc->ocsp_keep_window, cmd->pool, value, MD_TIME_OCSP_KEEP_NORM); + if (err) return apr_psprintf(cmd->pool, "MDStaplingKeepResponse %s", err); + return NULL; +} + +static const char *md_config_set_ocsp_renew_window(cmd_parms *cmd, void *dc, const char *value) +{ + md_srv_conf_t *sc = md_config_get(cmd->server); + const char *err; + + (void)dc; + if ((err = md_conf_check_location(cmd, MD_LOC_ALL))) { + return err; + } + err = md_timeslice_parse(&sc->mc->ocsp_renew_window, cmd->pool, value, MD_TIME_LIFE_NORM); + if (!err && sc->mc->ocsp_renew_window->norm + && (sc->mc->ocsp_renew_window->len >= sc->mc->ocsp_renew_window->norm)) { + err = "with a length of 100% or more is not allowed."; + } + if (err) return apr_psprintf(cmd->pool, "MDStaplingRenewWindow %s", err); + return NULL; +} + +static const char *md_config_set_cert_check(cmd_parms *cmd, void *dc, + const char *name, const char *url) +{ + md_srv_conf_t *sc = md_config_get(cmd->server); + const char *err; + + (void)dc; + if ((err = md_conf_check_location(cmd, MD_LOC_ALL))) { + return err; + } + sc->mc->cert_check_name = name; + sc->mc->cert_check_url = url; + return NULL; +} + +static const char *md_config_set_activation_delay(cmd_parms *cmd, void *mconfig, const char *arg) +{ + md_srv_conf_t *sc = md_config_get(cmd->server); + const char *err; + apr_interval_time_t delay; + + (void)mconfig; + if ((err = md_conf_check_location(cmd, MD_LOC_NOT_MD))) { + return err; + } + if (md_duration_parse(&delay, arg, "d") != APR_SUCCESS) { + return "unrecognized duration format"; + } + apr_table_set(sc->mc->env, MD_KEY_ACTIVATION_DELAY, md_duration_format(cmd->pool, delay)); + return NULL; +} + +static const char *md_config_set_ca_certs(cmd_parms *cmd, void *dc, const char *path) +{ + md_srv_conf_t *sc = md_config_get(cmd->server); + + (void)dc; + sc->mc->ca_certs = path; + return NULL; +} + +static const char *md_config_set_eab(cmd_parms *cmd, void *dc, + const char *keyid, const char *hmac) +{ + md_srv_conf_t *sc = md_config_get(cmd->server); + const char *err; + + (void)dc; + if ((err = md_conf_check_location(cmd, MD_LOC_ALL))) { + return err; + } + if (!hmac) { + if (!apr_strnatcasecmp("None", keyid)) { + keyid = "none"; + } + else { + /* a JSON file keeping keyid and hmac */ + const char *fpath; + apr_status_t rv; + md_json_t *json; + + /* If only dumping the config, don't verify the file */ + if (ap_state_query(AP_SQ_RUN_MODE) == AP_SQ_RM_CONFIG_DUMP) { + goto leave; + } + + fpath = ap_server_root_relative(cmd->pool, keyid); + if (!fpath) { + return apr_pstrcat(cmd->pool, cmd->cmd->name, + ": Invalid file path ", keyid, NULL); + } + if (!md_file_exists(fpath, cmd->pool)) { + return apr_pstrcat(cmd->pool, cmd->cmd->name, + ": file not found: ", fpath, NULL); + } + + rv = md_json_readf(&json, cmd->pool, fpath); + if (APR_SUCCESS != rv) { + return apr_pstrcat(cmd->pool, cmd->cmd->name, + ": error reading JSON file ", fpath, NULL); + } + keyid = md_json_gets(json, MD_KEY_KID, NULL); + if (!keyid || !*keyid) { + return apr_pstrcat(cmd->pool, cmd->cmd->name, + ": JSON does not contain '", MD_KEY_KID, + "' element in file ", fpath, NULL); + } + hmac = md_json_gets(json, MD_KEY_HMAC, NULL); + if (!hmac || !*hmac) { + return apr_pstrcat(cmd->pool, cmd->cmd->name, + ": JSON does not contain '", MD_KEY_HMAC, + "' element in file ", fpath, NULL); + } + } + } +leave: + sc->ca_eab_kid = keyid; + sc->ca_eab_hmac = hmac; + return NULL; } const command_rec md_cmds[] = { - AP_INIT_TAKE1( MD_CMD_CA, md_config_set_ca, NULL, RSRC_CONF, - "URL of CA issuing the certificates"), - AP_INIT_TAKE1( MD_CMD_CAAGREEMENT, md_config_set_agreement, NULL, RSRC_CONF, - "URL of CA Terms-of-Service agreement you accept"), - AP_INIT_TAKE_ARGV( MD_CMD_CACHALLENGES, md_config_set_cha_tyes, NULL, RSRC_CONF, + AP_INIT_TAKE_ARGV("MDCertificateAuthority", md_config_set_ca, NULL, RSRC_CONF, + "URL(s) or known name(s) of CA issuing the certificates"), + AP_INIT_TAKE1("MDCertificateAgreement", md_config_set_agreement, NULL, RSRC_CONF, + "either 'accepted' or the URL of CA Terms-of-Service agreement you accept"), + AP_INIT_TAKE_ARGV("MDCAChallenges", md_config_set_cha_tyes, NULL, RSRC_CONF, "A list of challenge types to be used."), - AP_INIT_TAKE1( MD_CMD_CAPROTO, md_config_set_ca_proto, NULL, RSRC_CONF, + AP_INIT_TAKE1("MDCertificateProtocol", md_config_set_ca_proto, NULL, RSRC_CONF, "Protocol used to obtain/renew certificates"), - AP_INIT_TAKE1( MD_CMD_DRIVEMODE, md_config_set_drive_mode, NULL, RSRC_CONF, - "method of obtaining certificates for the managed domain"), - AP_INIT_TAKE_ARGV( MD_CMD_MD, md_config_set_names, NULL, RSRC_CONF, + AP_INIT_TAKE1("MDContactEmail", md_config_set_contact, NULL, RSRC_CONF, + "Email address used for account registration"), + AP_INIT_TAKE1("MDDriveMode", md_config_set_renew_mode, NULL, RSRC_CONF, + "deprecated, older name for MDRenewMode"), + AP_INIT_TAKE1("MDRenewMode", md_config_set_renew_mode, NULL, RSRC_CONF, + "Controls how renewal of Managed Domain certificates shall be handled."), + AP_INIT_TAKE_ARGV("MDomain", md_config_set_names, NULL, RSRC_CONF, "A group of server names with one certificate"), - AP_INIT_RAW_ARGS( MD_CMD_MD_SECTION, md_config_sec_start, NULL, RSRC_CONF, + AP_INIT_RAW_ARGS(MD_CMD_MD_SECTION, md_config_sec_start, NULL, RSRC_CONF, "Container for a managed domain with common settings and certificate."), - AP_INIT_TAKE_ARGV( MD_CMD_MEMBER, md_config_sec_add_members, NULL, RSRC_CONF, + AP_INIT_RAW_ARGS(MD_CMD_MD2_SECTION, md_config_sec_start, NULL, RSRC_CONF, + "Short form for <MDomainSet> container."), + AP_INIT_TAKE_ARGV("MDMember", md_config_sec_add_members, NULL, RSRC_CONF, "Define domain name(s) part of the Managed Domain. Use 'auto' or " "'manual' to enable/disable auto adding names from virtual hosts."), - AP_INIT_TAKE_ARGV( MD_CMD_MEMBERS, md_config_sec_add_members, NULL, RSRC_CONF, + AP_INIT_TAKE_ARGV("MDMembers", md_config_sec_add_members, NULL, RSRC_CONF, "Define domain name(s) part of the Managed Domain. Use 'auto' or " "'manual' to enable/disable auto adding names from virtual hosts."), - AP_INIT_TAKE1( MD_CMD_MUSTSTAPLE, md_config_set_must_staple, NULL, RSRC_CONF, + AP_INIT_TAKE1("MDMustStaple", md_config_set_must_staple, NULL, RSRC_CONF, "Enable/Disable the Must-Staple flag for new certificates."), - AP_INIT_TAKE12( MD_CMD_PORTMAP, md_config_set_port_map, NULL, RSRC_CONF, + AP_INIT_TAKE12("MDPortMap", md_config_set_port_map, NULL, RSRC_CONF, "Declare the mapped ports 80 and 443 on the local server. E.g. 80:8000 " "to indicate that the server port 8000 is reachable as port 80 from the " "internet. Use 80:- to indicate that port 80 is not reachable from " "the outside."), - AP_INIT_TAKE_ARGV( MD_CMD_PKEYS, md_config_set_pkeys, NULL, RSRC_CONF, + AP_INIT_TAKE_ARGV("MDPrivateKeys", md_config_set_pkeys, NULL, RSRC_CONF, "set the type and parameters for private key generation"), - AP_INIT_TAKE1( MD_CMD_PROXY, md_config_set_proxy, NULL, RSRC_CONF, + AP_INIT_TAKE1("MDHttpProxy", md_config_set_proxy, NULL, RSRC_CONF, "URL of a HTTP(S) proxy to use for outgoing connections"), - AP_INIT_TAKE1( MD_CMD_STOREDIR, md_config_set_store_dir, NULL, RSRC_CONF, + AP_INIT_TAKE1("MDStoreDir", md_config_set_store_dir, NULL, RSRC_CONF, "the directory for file system storage of managed domain data."), - AP_INIT_TAKE1( MD_CMD_RENEWWINDOW, md_config_set_renew_window, NULL, RSRC_CONF, - "Time length for renewal before certificate expires (defaults to days)"), - AP_INIT_TAKE1( MD_CMD_REQUIREHTTPS, md_config_set_require_https, NULL, RSRC_CONF, + AP_INIT_TAKE1("MDRenewWindow", md_config_set_renew_window, NULL, RSRC_CONF, + "Time length for renewal before certificate expires (defaults to days)."), + AP_INIT_TAKE1("MDRequireHttps", md_config_set_require_https, NULL, RSRC_CONF|OR_AUTHCFG, "Redirect non-secure requests to the https: equivalent."), - AP_INIT_RAW_ARGS(MD_CMD_NOTIFYCMD, md_config_set_notify_cmd, NULL, RSRC_CONF, - "set the command and optional arguments to run when signup/renew of domain is complete."), - AP_INIT_TAKE1( MD_CMD_BASE_SERVER, md_config_set_base_server, NULL, RSRC_CONF, - "allow managing of base server outside virtual hosts."), - -/* This will disappear soon */ - AP_INIT_TAKE_ARGV( MD_CMD_OLD_MD, md_config_set_names_old, NULL, RSRC_CONF, - "Deprecated, replace with 'MDomain'."), - AP_INIT_RAW_ARGS( MD_CMD_MD_OLD_SECTION, md_config_sec_start_old, NULL, RSRC_CONF, - "Deprecated, replace with '<MDomainSet'."), -/* */ + AP_INIT_RAW_ARGS("MDNotifyCmd", md_config_set_notify_cmd, NULL, RSRC_CONF, + "Set the command to run when signup/renew of domain is complete."), + AP_INIT_TAKE1("MDBaseServer", md_config_set_base_server, NULL, RSRC_CONF, + "Allow managing of base server outside virtual hosts."), + AP_INIT_RAW_ARGS("MDChallengeDns01", md_config_set_dns01_cmd, NULL, RSRC_CONF, + "Set the command for setup/teardown of dns-01 challenges"), + AP_INIT_TAKE1("MDChallengeDns01Version", md_config_set_dns01_version, NULL, RSRC_CONF, + "Set the type of arguments to call `MDChallengeDns01` with"), + AP_INIT_TAKE1("MDCertificateFile", md_config_add_cert_file, NULL, RSRC_CONF, + "set the static certificate (chain) file to use for this domain."), + AP_INIT_TAKE1("MDCertificateKeyFile", md_config_add_key_file, NULL, RSRC_CONF, + "set the static private key file to use for this domain."), + AP_INIT_TAKE1("MDServerStatus", md_config_set_server_status, NULL, RSRC_CONF, + "On to see Managed Domains in server-status."), + AP_INIT_TAKE1("MDCertificateStatus", md_config_set_certificate_status, NULL, RSRC_CONF, + "On to see Managed Domain expose /.httpd/certificate-status."), + AP_INIT_TAKE1("MDWarnWindow", md_config_set_warn_window, NULL, RSRC_CONF, + "When less time remains for a certificate, send our/log a warning (defaults to days)"), + AP_INIT_RAW_ARGS("MDMessageCmd", md_config_set_msg_cmd, NULL, RSRC_CONF, + "Set the command run when a message about a domain is issued."), + AP_INIT_TAKE1("MDStapling", md_config_set_stapling, NULL, RSRC_CONF, + "Enable/Disable OCSP Stapling for this/all Managed Domain(s)."), + AP_INIT_TAKE1("MDStapleOthers", md_config_set_staple_others, NULL, RSRC_CONF, + "Enable/Disable OCSP Stapling for certificates not in Managed Domains."), + AP_INIT_TAKE1("MDStaplingKeepResponse", md_config_set_ocsp_keep_window, NULL, RSRC_CONF, + "The amount of time to keep an OCSP response in the store."), + AP_INIT_TAKE1("MDStaplingRenewWindow", md_config_set_ocsp_renew_window, NULL, RSRC_CONF, + "Time length for renewal before OCSP responses expire (defaults to days)."), + AP_INIT_TAKE2("MDCertificateCheck", md_config_set_cert_check, NULL, RSRC_CONF, + "Set name and URL pattern for a certificate monitoring site."), + AP_INIT_TAKE1("MDActivationDelay", md_config_set_activation_delay, NULL, RSRC_CONF, + "How long to delay activation of new certificates"), + AP_INIT_TAKE1("MDCACertificateFile", md_config_set_ca_certs, NULL, RSRC_CONF, + "Set the CA file to use for connections"), + AP_INIT_TAKE12("MDExternalAccountBinding", md_config_set_eab, NULL, RSRC_CONF, + "Set the external account binding keyid and hmac values to use at CA"), + AP_INIT_TAKE1("MDRetryDelay", md_config_set_min_delay, NULL, RSRC_CONF, + "Time length for first retry, doubled on every consecutive error."), + AP_INIT_TAKE1("MDRetryFailover", md_config_set_retry_failover, NULL, RSRC_CONF, + "The number of errors before a failover to another CA is triggered."), + AP_INIT_TAKE1("MDStoreLocks", md_config_set_store_locks, NULL, RSRC_CONF, + "Configure locking of store for updates."), + AP_INIT_TAKE1("MDMatchNames", md_config_set_match_mode, NULL, RSRC_CONF, + "Determines how DNS names are matched to vhosts."), AP_INIT_TAKE1(NULL, NULL, NULL, RSRC_CONF, NULL) }; @@ -864,6 +1320,12 @@ apr_status_t md_config_post_config(server_rec *s, apr_pool_t *p) if (mc->hsts_max_age > 0) { mc->hsts_header = apr_psprintf(p, "max-age=%d", mc->hsts_max_age); } + +#if AP_MODULE_MAGIC_AT_LEAST(20180906, 2) + if (mc->base_dir == NULL) { + mc->base_dir = ap_state_dir_relative(p, MD_DEFAULT_BASE_DIR); + } +#endif return APR_SUCCESS; } @@ -874,6 +1336,7 @@ static md_srv_conf_t *config_get_int(server_rec *s, apr_pool_t *p) ap_assert(sc); if (sc->s != s && p) { sc = md_config_merge(p, &defconf, sc); + sc->s = s; sc->name = apr_pstrcat(p, CONF_S_NAME(s), sc->name, NULL); sc->mc = md_mod_conf_get(p, 1); ap_set_module_config(s->module_config, &md_module, sc); @@ -900,8 +1363,8 @@ md_srv_conf_t *md_config_cget(conn_rec *c) const char *md_config_gets(const md_srv_conf_t *sc, md_config_var_t var) { switch (var) { - case MD_CONFIG_CA_URL: - return sc->ca_url? sc->ca_url : defconf.ca_url; + case MD_CONFIG_CA_CONTACT: + return sc->ca_contact? sc->ca_contact : defconf.ca_contact; case MD_CONFIG_CA_PROTO: return sc->ca_proto? sc->ca_proto : defconf.ca_proto; case MD_CONFIG_BASE_DIR: @@ -921,30 +1384,49 @@ int md_config_geti(const md_srv_conf_t *sc, md_config_var_t var) { switch (var) { case MD_CONFIG_DRIVE_MODE: - return (sc->drive_mode != DEF_VAL)? sc->drive_mode : defconf.drive_mode; - case MD_CONFIG_LOCAL_80: - return sc->mc->local_80; - case MD_CONFIG_LOCAL_443: - return sc->mc->local_443; + return (sc->renew_mode != DEF_VAL)? sc->renew_mode : defconf.renew_mode; case MD_CONFIG_TRANSITIVE: return (sc->transitive != DEF_VAL)? sc->transitive : defconf.transitive; case MD_CONFIG_REQUIRE_HTTPS: return (sc->require_https != MD_REQUIRE_UNSET)? sc->require_https : defconf.require_https; case MD_CONFIG_MUST_STAPLE: return (sc->must_staple != DEF_VAL)? sc->must_staple : defconf.must_staple; + case MD_CONFIG_STAPLING: + return (sc->stapling != DEF_VAL)? sc->stapling : defconf.stapling; + case MD_CONFIG_STAPLE_OTHERS: + return (sc->staple_others != DEF_VAL)? sc->staple_others : defconf.staple_others; default: return 0; } } -apr_interval_time_t md_config_get_interval(const md_srv_conf_t *sc, md_config_var_t var) +void md_config_get_timespan(md_timeslice_t **pspan, const md_srv_conf_t *sc, md_config_var_t var) { switch (var) { - case MD_CONFIG_RENEW_NORM: - return (sc->renew_norm != DEF_VAL)? sc->renew_norm : defconf.renew_norm; case MD_CONFIG_RENEW_WINDOW: - return (sc->renew_window != DEF_VAL)? sc->renew_window : defconf.renew_window; + *pspan = sc->renew_window? sc->renew_window : defconf.renew_window; + break; + case MD_CONFIG_WARN_WINDOW: + *pspan = sc->warn_window? sc->warn_window : defconf.warn_window; + break; default: - return 0; + break; + } +} + +const md_t *md_get_for_domain(server_rec *s, const char *domain) +{ + md_srv_conf_t *sc; + const md_t *md; + int i; + + sc = md_config_get(s); + for (i = 0; sc && sc->assigned && i < sc->assigned->nelts; ++i) { + md = APR_ARRAY_IDX(sc->assigned, i, const md_t*); + if (md_contains(md, domain, 0)) goto leave; } + md = NULL; +leave: + return md; } + |