diff options
Diffstat (limited to '')
-rw-r--r-- | modules/md/mod_md_config.c | 950 |
1 files changed, 950 insertions, 0 deletions
diff --git a/modules/md/mod_md_config.c b/modules/md/mod_md_config.c new file mode 100644 index 0000000..336a21b --- /dev/null +++ b/modules/md/mod_md_config.c @@ -0,0 +1,950 @@ +/* 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_lib.h> +#include <apr_strings.h> + +#include <httpd.h> +#include <http_core.h> +#include <http_config.h> +#include <http_log.h> +#include <http_vhost.h> + +#include "md.h" +#include "md_crypt.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 DEF_VAL (-1) + +/* 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, +}; + +/* 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, +}; + +static md_mod_conf_t *mod_md_config; + +static apr_status_t cleanup_mod_config(void *dummy) +{ + (void)dummy; + mod_md_config = NULL; + return APR_SUCCESS; +} + +static md_mod_conf_t *md_mod_conf_get(apr_pool_t *pool, int create) +{ + if (mod_md_config) { + return mod_md_config; /* reused for lifetime of the pool */ + } + + if (create) { + mod_md_config = apr_pcalloc(pool, sizeof(*mod_md_config)); + 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 *)); + + apr_pool_cleanup_register(pool, NULL, cleanup_mod_config, apr_pool_cleanup_null); + } + + return mod_md_config; +} + +#define CONF_S_NAME(s) (s && s->server_hostname? s->server_hostname : "default") + +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->must_staple = DEF_VAL; + sc->pkey_spec = NULL; + sc->renew_norm = DEF_VAL; + sc->renew_window = DEF_VAL; + sc->ca_url = NULL; + sc->ca_proto = NULL; + sc->ca_agreement = NULL; + sc->ca_challenges = 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->must_staple = from->must_staple; + to->pkey_spec = from->pkey_spec; + to->renew_norm = from->renew_norm; + to->renew_window = from->renew_window; + to->ca_url = from->ca_url; + to->ca_proto = from->ca_proto; + to->ca_agreement = from->ca_agreement; + to->ca_challenges = from->ca_challenges; +} + +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->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->ca_proto) md->ca_proto = from->ca_proto; + if (from->ca_agreement) md->ca_agreement = from->ca_agreement; + if (from->ca_challenges) md->ca_challenges = apr_array_copy(p, from->ca_challenges); +} + +void *md_config_create_svr(apr_pool_t *pool, server_rec *s) +{ + md_srv_conf_t *conf = (md_srv_conf_t *)apr_pcalloc(pool, sizeof(md_srv_conf_t)); + + conf->name = apr_pstrcat(pool, "srv[", CONF_S_NAME(s), "]", NULL); + conf->s = s; + conf->mc = md_mod_conf_get(pool, 1); + + srv_conf_props_clear(conf); + + return conf; +} + +static void *md_config_merge(apr_pool_t *pool, void *basev, void *addv) +{ + md_srv_conf_t *base = (md_srv_conf_t *)basev; + md_srv_conf_t *add = (md_srv_conf_t *)addv; + md_srv_conf_t *nsc; + char *name = apr_pstrcat(pool, "[", CONF_S_NAME(add->s), ", ", CONF_S_NAME(base->s), "]", NULL); + + 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->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->ca_url = add->ca_url? add->ca_url : base->ca_url; + 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->current = NULL; + nsc->assigned = NULL; + + return nsc; +} + +void *md_config_merge_svr(apr_pool_t *pool, void *basev, void *addv) +{ + return md_config_merge(pool, basev, addv); +} + +static int inside_section(cmd_parms *cmd, const char *section) { + ap_directive_t *d; + for (d = cmd->directive->parent; d; d = d->parent) { + if (!ap_cstr_casecmp(d->directive, section)) { + return 1; + } + } + return 0; +} + +static int inside_md_section(cmd_parms *cmd) { + return (inside_section(cmd, MD_CMD_MD_SECTION) || inside_section(cmd, MD_CMD_MD_OLD_SECTION)); +} + +static const char *md_section_check(cmd_parms *cmd) { + if (!inside_md_section(cmd)) { + return apr_pstrcat(cmd->pool, cmd->cmd->name, " is only valid inside a '", + MD_CMD_MD_SECTION, "' context, not here", 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) { + APR_ARRAY_PUSH(domains, char *) = md_util_str_tolower(apr_pstrdup(p, name)); + } +} + +static const char *set_transitive(int *ptransitive, const char *value) +{ + if (!apr_strnatcasecmp("auto", value)) { + *ptransitive = 1; + return NULL; + } + else if (!apr_strnatcasecmp("manual", value)) { + *ptransitive = 0; + return NULL; + } + return "unknown value, use \"auto|manual\""; +} + +static const char *md_config_sec_start(cmd_parms *cmd, void *mconfig, const char *arg) +{ + md_srv_conf_t *sc; + md_srv_conf_t save; + const char *endp; + const char *err, *name; + apr_array_header_t *domains; + md_t *md; + int transitive = -1; + + (void)mconfig; + if ((err = ap_check_cmd_context(cmd, GLOBAL_ONLY))) { + return err; + } + + sc = md_config_get(cmd->server); + endp = ap_strrchr_c(arg, '>'); + if (endp == NULL) { + return MD_CMD_MD_SECTION "> directive missing closing '>'"; + } + + arg = apr_pstrndup(cmd->pool, arg, (apr_size_t)(endp-arg)); + if (!arg || !*arg) { + return MD_CMD_MD_SECTION " > section must specify a unique domain name"; + } + + name = ap_getword_white(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); + if (NULL != set_transitive(&transitive, name)) { + add_domain_name(domains, name, cmd->pool); + } + } + + if (domains->nelts == 0) { + return "needs at least one domain name"; + } + + md = md_create(cmd->pool, domains); + if (transitive >= 0) { + md->transitive = transitive; + } + + /* Save the current settings in this srv_conf and apply+restore at the + * end of this section */ + memcpy(&save, sc, sizeof(save)); + srv_conf_props_clear(sc); + sc->current = md; + + if (NULL == (err = ap_walk_config(cmd->directive->first_child, cmd, cmd->context))) { + srv_conf_props_apply(md, sc, cmd->pool); + APR_ARRAY_PUSH(sc->mc->mds, const md_t *) = md; + } + + sc->current = NULL; + srv_conf_props_copy(sc, &save); + + return err; +} + +static const char *md_config_sec_add_members(cmd_parms *cmd, void *dc, + int argc, char *const argv[]) +{ + md_srv_conf_t *sc = md_config_get(cmd->server); + const char *err; + int i; + + (void)dc; + if (NULL != (err = md_section_check(cmd))) { + if (argc == 1) { + /* only these values are allowed outside a section */ + return set_transitive(&sc->transitive, argv[0]); + } + return err; + } + + assert(sc->current); + for (i = 0; i < argc; ++i) { + if (NULL != set_transitive(&sc->transitive, argv[i])) { + add_domain_name(sc->current->domains, argv[i], cmd->pool); + } + } + return NULL; +} + +static const char *md_config_set_names(cmd_parms *cmd, void *dc, + int argc, char *const argv[]) +{ + md_srv_conf_t *sc = md_config_get(cmd->server); + apr_array_header_t *domains = apr_array_make(cmd->pool, 5, sizeof(const char *)); + const char *err; + md_t *md; + int i, transitive = -1; + + (void)dc; + err = ap_check_cmd_context(cmd, NOT_IN_DIR_LOC_FILE); + if (err) { + return err; + } + + for (i = 0; i < argc; ++i) { + if (NULL != set_transitive(&transitive, argv[i])) { + add_domain_name(domains, argv[i], cmd->pool); + } + } + + if (domains->nelts == 0) { + return "needs at least one domain name"; + } + md = md_create(cmd->pool, domains); + + if (transitive >= 0) { + md->transitive = transitive; + } + + if (cmd->config_file) { + md->defn_name = cmd->config_file->name; + md->defn_line_number = cmd->config_file->line_number; + } + + APR_ARRAY_PUSH(sc->mc->mds, md_t *) = md; + + return NULL; +} + +static const char *md_config_set_ca(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))) { + return err; + } + sc->ca_url = value; + return NULL; +} + +static const char *md_config_set_ca_proto(cmd_parms *cmd, void *dc, const char *value) +{ + md_srv_conf_t *config = md_config_get(cmd->server); + const char *err; + + (void)dc; + if (!inside_md_section(cmd) && (err = ap_check_cmd_context(cmd, GLOBAL_ONLY))) { + return err; + } + config->ca_proto = value; + return NULL; +} + +static const char *md_config_set_agreement(cmd_parms *cmd, void *dc, const char *value) +{ + md_srv_conf_t *config = md_config_get(cmd->server); + const char *err; + + (void)dc; + if (!inside_md_section(cmd) && (err = ap_check_cmd_context(cmd, GLOBAL_ONLY))) { + return err; + } + config->ca_agreement = value; + return NULL; +} + +static const char *md_config_set_drive_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; + + (void)dc; + if (!apr_strnatcasecmp("auto", value) || !apr_strnatcasecmp("automatic", value)) { + drive_mode = MD_DRIVE_AUTO; + } + else if (!apr_strnatcasecmp("always", value)) { + drive_mode = MD_DRIVE_ALWAYS; + } + else if (!apr_strnatcasecmp("manual", value) || !apr_strnatcasecmp("stick", value)) { + drive_mode = MD_DRIVE_MANUAL; + } + else { + return apr_pstrcat(cmd->pool, "unknown MDDriveMode ", value, NULL); + } + + if (!inside_md_section(cmd) && (err = ap_check_cmd_context(cmd, GLOBAL_ONLY))) { + return err; + } + config->drive_mode = drive_mode; + return NULL; +} + +static const char *md_config_set_must_staple(cmd_parms *cmd, void *dc, const char *value) +{ + md_srv_conf_t *config = md_config_get(cmd->server); + const char *err; + + (void)dc; + if (!inside_md_section(cmd) && (err = ap_check_cmd_context(cmd, GLOBAL_ONLY))) { + return err; + } + + if (!apr_strnatcasecmp("off", value)) { + config->must_staple = 0; + } + else if (!apr_strnatcasecmp("on", value)) { + config->must_staple = 1; + } + else { + return apr_pstrcat(cmd->pool, "unknown '", value, + "', supported parameter values are 'on' and 'off'", NULL); + } + return NULL; +} + +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 = ap_check_cmd_context(cmd, GLOBAL_ONLY); + + (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); + } + } + return err; +} + +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; + + (void)dc; + if (!inside_md_section(cmd) && (err = ap_check_cmd_context(cmd, GLOBAL_ONLY))) { + return err; + } + + if (!apr_strnatcasecmp("off", value)) { + config->require_https = MD_REQUIRE_OFF; + } + else if (!apr_strnatcasecmp(MD_KEY_TEMPORARY, value)) { + config->require_https = MD_REQUIRE_TEMPORARY; + } + else if (!apr_strnatcasecmp(MD_KEY_PERMANENT, value)) { + config->require_https = MD_REQUIRE_PERMANENT; + } + else { + return apr_pstrcat(cmd->pool, "unknown '", value, + "', supported parameter values are 'temporary' and 'permanent'", NULL); + } + 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) +{ + char *endp; + apr_int64_t n; + + n = apr_strtoi64(value, &endp, 10); + if (errno) { + return errno; + } + if (*endp == '%') { + if (n < 0 || n >= 100) { + return APR_BADARG; + } + *ppercent = (int)n; + return APR_SUCCESS; + } + return APR_EINVAL; +} + +static const char *md_config_set_renew_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))) { + 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"; + } + } + return "MDRenewWindow has unrecognized format"; +} + +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); + + if (err) { + return err; + } + md_util_abs_http_uri_check(cmd->pool, value, &err); + if (err) { + return err; + } + sc->mc->proxy_url = value; + (void)arg; + return NULL; +} + +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); + + if (err) { + return err; + } + sc->mc->base_dir = value; + (void)arg; + return NULL; +} + +static const char *set_port_map(md_mod_conf_t *mc, const char *value) +{ + int net_port, local_port; + char *endp; + + net_port = (int)apr_strtoi64(value, &endp, 10); + if (errno) { + return "unable to parse first port number"; + } + if (!endp || *endp != ':') { + return "no ':' after first port number"; + } + ++endp; + if (*endp == '-') { + local_port = 0; + } + else { + local_port = (int)apr_strtoi64(endp, &endp, 10); + if (errno) { + return "unable to parse second port number"; + } + if (local_port <= 0 || local_port > 65535) { + return "invalid number for port map, must be in ]0,65535]"; + } + } + switch (net_port) { + case 80: + mc->local_80 = local_port; + break; + case 443: + mc->local_443 = local_port; + break; + default: + return "mapped port number must be 80 or 443"; + } + return NULL; +} + +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); + + (void)arg; + if (!err) { + err = set_port_map(sc->mc, v1); + } + if (!err && v2) { + err = set_port_map(sc->mc, v2); + } + return err; +} + +static const char *md_config_set_cha_tyes(cmd_parms *cmd, void *dc, + int argc, char *const argv[]) +{ + md_srv_conf_t *config = md_config_get(cmd->server); + apr_array_header_t **pcha, *ca_challenges; + const char *err; + int i; + + (void)dc; + if (!inside_md_section(cmd) + && (err = ap_check_cmd_context(cmd, GLOBAL_ONLY))) { + return err; + } + pcha = &config->ca_challenges; + + ca_challenges = *pcha; + if (!ca_challenges) { + *pcha = ca_challenges = apr_array_make(cmd->pool, 5, sizeof(const char *)); + } + for (i = 0; i < argc; ++i) { + APR_ARRAY_PUSH(ca_challenges, const char *) = argv[i]; + } + + return NULL; +} + +static const char *md_config_set_pkeys(cmd_parms *cmd, void *dc, + int argc, char *const argv[]) +{ + md_srv_conf_t *config = md_config_get(cmd->server); + const char *err, *ptype; + apr_int64_t bits; + + (void)dc; + if (!inside_md_section(cmd) + && (err = ap_check_cmd_context(cmd, GLOBAL_ONLY))) { + 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->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 (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 { + 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)); + } + 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); +} + +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); + + if (err) { + return err; + } + sc->mc->notify_cmd = arg; + (void)mconfig; + return NULL; +} + +static const char *md_config_set_names_old(cmd_parms *cmd, void *dc, + int argc, char *const argv[]) +{ + 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); +} + +static const char *md_config_sec_start_old(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); +} + +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, + "A list of challenge types to be used."), + AP_INIT_TAKE1( MD_CMD_CAPROTO, 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, + "A group of server names with one certificate"), + 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, + "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, + "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, + "Enable/Disable the Must-Staple flag for new certificates."), + AP_INIT_TAKE12( MD_CMD_PORTMAP, 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, + "set the type and parameters for private key generation"), + AP_INIT_TAKE1( MD_CMD_PROXY, 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, + "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, + "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_TAKE1(NULL, NULL, NULL, RSRC_CONF, NULL) +}; + +apr_status_t md_config_post_config(server_rec *s, apr_pool_t *p) +{ + md_srv_conf_t *sc; + md_mod_conf_t *mc; + + sc = md_config_get(s); + mc = sc->mc; + + mc->hsts_header = NULL; + if (mc->hsts_max_age > 0) { + mc->hsts_header = apr_psprintf(p, "max-age=%d", mc->hsts_max_age); + } + + return APR_SUCCESS; +} + +static md_srv_conf_t *config_get_int(server_rec *s, apr_pool_t *p) +{ + md_srv_conf_t *sc = (md_srv_conf_t *)ap_get_module_config(s->module_config, &md_module); + ap_assert(sc); + if (sc->s != s && p) { + sc = md_config_merge(p, &defconf, sc); + 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); + } + return sc; +} + +md_srv_conf_t *md_config_get(server_rec *s) +{ + return config_get_int(s, NULL); +} + +md_srv_conf_t *md_config_get_unique(server_rec *s, apr_pool_t *p) +{ + assert(p); + return config_get_int(s, p); +} + +md_srv_conf_t *md_config_cget(conn_rec *c) +{ + return md_config_get(c->base_server); +} + +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_PROTO: + return sc->ca_proto? sc->ca_proto : defconf.ca_proto; + case MD_CONFIG_BASE_DIR: + return sc->mc->base_dir; + case MD_CONFIG_PROXY: + return sc->mc->proxy_url; + case MD_CONFIG_CA_AGREEMENT: + return sc->ca_agreement? sc->ca_agreement : defconf.ca_agreement; + case MD_CONFIG_NOTIFY_CMD: + return sc->mc->notify_cmd; + default: + return NULL; + } +} + +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; + 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; + default: + return 0; + } +} + +apr_interval_time_t md_config_get_interval(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; + default: + return 0; + } +} |