From 4ee5a66bd6a1993eb1d02a89aada556734c52bda Mon Sep 17 00:00:00 2001 From: Stefan Eissing Date: Tue, 8 Aug 2017 13:50:20 +0000 Subject: mod_md: v0.6.0 from github git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/branches/trunk-md@1804424 13f79535-47bb-0310-9956-ffa450edef68 --- modules/md/config2.m4 | 4 +- modules/md/md.h | 1 + modules/md/md_config.c | 583 -------------------------------------------- modules/md/md_config.h | 77 ------ modules/md/md_json.c | 16 +- modules/md/md_os.c | 82 ------- modules/md/md_os.h | 36 --- modules/md/md_store_fs.c | 53 +++- modules/md/md_version.h | 4 +- modules/md/mod_md.c | 6 +- modules/md/mod_md_config.c | 582 +++++++++++++++++++++++++++++++++++++++++++ modules/md/mod_md_config.h | 77 ++++++ modules/md/mod_md_os.c | 82 +++++++ modules/md/mod_md_os.h | 36 +++ modules/md/mod_md_private.h | 23 ++ 15 files changed, 865 insertions(+), 797 deletions(-) delete mode 100644 modules/md/md_config.c delete mode 100644 modules/md/md_config.h delete mode 100644 modules/md/md_os.c delete mode 100644 modules/md/md_os.h create mode 100644 modules/md/mod_md_config.c create mode 100644 modules/md/mod_md_config.h create mode 100644 modules/md/mod_md_os.c create mode 100644 modules/md/mod_md_os.h create mode 100644 modules/md/mod_md_private.h (limited to 'modules') diff --git a/modules/md/config2.m4 b/modules/md/config2.m4 index 70edaf843a..034819ad7c 100644 --- a/modules/md/config2.m4 +++ b/modules/md/config2.m4 @@ -245,8 +245,8 @@ APACHE_MODPATH_INIT(md) dnl # list of module object files md_objs="dnl mod_md.lo dnl -md_config.lo dnl -md_os.lo dnl +mod_md_config.lo dnl +mod_md_os.lo dnl " dnl # hook module into the Autoconf mechanism (--enable-md) diff --git a/modules/md/md.h b/modules/md/md.h index 23b7543a78..35bab07fdc 100644 --- a/modules/md/md.h +++ b/modules/md/md.h @@ -114,6 +114,7 @@ struct md_t { #define MD_KEY_RESOURCE "resource" #define MD_KEY_STATE "state" #define MD_KEY_STATUS "status" +#define MD_KEY_STORE "store" #define MD_KEY_TOKEN "token" #define MD_KEY_TYPE "type" #define MD_KEY_URL "url" diff --git a/modules/md/md_config.c b/modules/md/md_config.c deleted file mode 100644 index 5a31255372..0000000000 --- a/modules/md/md_config.c +++ /dev/null @@ -1,583 +0,0 @@ -/* Copyright 2017 greenbytes GmbH (https://www.greenbytes.de) - * - * Licensed 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 - -#include -#include - -#include -#include -#include -#include -#include - -#include "md.h" -#include "md_config.h" -#include "md_util.h" - -extern module AP_MODULE_DECLARE_DATA md_module; -APLOG_USE_MODULE(md); - -#define DEF_VAL (-1) - -static md_config_t defconf = { - "default", - NULL, - 80, - 443, - NULL, - MD_ACME_DEF_URL, - "ACME", - NULL, - NULL, - MD_DRIVE_AUTO, - apr_time_from_sec(14 * MD_SECS_PER_DAY), - NULL, - "md", - NULL -}; - -#define CONF_S_NAME(s) (s && s->server_hostname? s->server_hostname : "default") - -void *md_config_create_svr(apr_pool_t *pool, server_rec *s) -{ - md_config_t *conf = (md_config_t *)apr_pcalloc(pool, sizeof(md_config_t)); - - conf->name = apr_pstrcat(pool, "srv[", CONF_S_NAME(s), "]", NULL); - conf->s = s; - conf->local_80 = DEF_VAL; - conf->local_443 = DEF_VAL; - conf->drive_mode = DEF_VAL; - conf->mds = apr_array_make(pool, 5, sizeof(const md_t *)); - conf->renew_window = DEF_VAL; - - return conf; -} - -static void *md_config_merge(apr_pool_t *pool, void *basev, void *addv) -{ - md_config_t *base = (md_config_t *)basev; - md_config_t *add = (md_config_t *)addv; - md_config_t *n = (md_config_t *)apr_pcalloc(pool, sizeof(md_config_t)); - char *name = apr_pstrcat(pool, "[", CONF_S_NAME(add->s), ", ", CONF_S_NAME(base->s), "]", NULL); - md_t *md; - int i; - - n->name = name; - n->local_80 = (add->local_80 != DEF_VAL)? add->local_80 : base->local_80; - n->local_443 = (add->local_443 != DEF_VAL)? add->local_443 : base->local_443; - - /* I think we should not merge md definitions. They should reside where - * they were defined */ - n->mds = apr_array_make(pool, add->mds->nelts, sizeof(const md_t *)); - for (i = 0; i < add->mds->nelts; ++i) { - md = APR_ARRAY_IDX(add->mds, i, md_t*); - APR_ARRAY_PUSH(n->mds, md_t *) = md_clone(pool, md); - } - n->ca_url = add->ca_url? add->ca_url : base->ca_url; - n->ca_proto = add->ca_proto? add->ca_proto : base->ca_proto; - n->ca_agreement = add->ca_agreement? add->ca_agreement : base->ca_agreement; - n->drive_mode = (add->drive_mode != DEF_VAL)? add->drive_mode : base->drive_mode; - n->md = NULL; - n->base_dir = add->base_dir? add->base_dir : base->base_dir; - n->renew_window = (add->renew_window != DEF_VAL)? add->renew_window : base->renew_window; - n->ca_challenges = (add->ca_challenges? apr_array_copy(pool, add->ca_challenges) - : (base->ca_challenges? apr_array_copy(pool, base->ca_challenges) : NULL)); - return n; -} - -void *md_config_merge_svr(apr_pool_t *pool, void *basev, void *addv) -{ - return md_config_merge(pool, basev, addv); -} - -void *md_config_create_dir(apr_pool_t *pool, char *dummy) -{ - md_config_dir_t *conf = apr_pcalloc(pool, sizeof(*conf)); - return conf; -} - -void *md_config_merge_dir(apr_pool_t *pool, void *basev, void *addv) -{ - md_config_dir_t *base = basev; - md_config_dir_t *add = addv; - md_config_dir_t *n = apr_pcalloc(pool, sizeof(*n)); - n->md = add->md? add->md : base->md; - return n; -} - -static int inside_section(cmd_parms *cmd) { - return (cmd->directive->parent - && !ap_cstr_casecmp(cmd->directive->parent->directive, "pool, cmd->cmd->name, - " is only valid inside a directive->parent? cmd->directive->parent->directive : "root", - 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 *md_config_sec_start(cmd_parms *cmd, void *mconfig, const char *arg) -{ - md_config_t *sconf = ap_get_module_config(cmd->server->module_config, &md_module); - const char *endp = ap_strrchr_c(arg, '>'); - ap_conf_vector_t *new_dir_conf = ap_create_per_dir_config(cmd->pool); - int old_overrides = cmd->override; - char *old_path = cmd->path; - const char *err, *name; - md_config_dir_t *dconf; - md_t *md; - - if (NULL != (err = ap_check_cmd_context(cmd, NOT_IN_DIR_LOC_FILE))) { - return err; - } - - if (endp == NULL) { - return apr_pstrcat(cmd->pool, cmd->cmd->name, "> directive missing closing '>'", NULL); - } - - arg = apr_pstrndup(cmd->pool, arg, endp-arg); - if (!arg || !*arg) { - return " block must specify a unique domain name"; - } - - cmd->path = ap_getword_white(cmd->pool, &arg); - name = cmd->path; - - md = md_create_empty(cmd->pool); - md->name = name; - APR_ARRAY_PUSH(md->domains, const char*) = name; - md->drive_mode = DEF_VAL; - - while (*arg != '\0') { - name = ap_getword_white(cmd->pool, &arg); - APR_ARRAY_PUSH(md->domains, const char*) = name; - } - - dconf = ap_set_config_vectors(cmd->server, new_dir_conf, cmd->path, &md_module, cmd->pool); - dconf->md = md; - - if (NULL == (err = ap_walk_config(cmd->directive->first_child, cmd, new_dir_conf))) { - APR_ARRAY_PUSH(sconf->mds, const md_t *) = md; - } - - cmd->path = old_path; - cmd->override = old_overrides; - - return err; -} - -static const char *md_config_sec_add_members(cmd_parms *cmd, void *dc, - int argc, char *const argv[]) -{ - md_config_dir_t *dconfig = dc; - apr_array_header_t *domains; - const char *err; - int i; - - if (NULL != (err = md_section_check(cmd))) { - return err; - } - - domains = dconfig->md->domains; - for (i = 0; i < argc; ++i) { - add_domain_name(domains, argv[i], cmd->pool); - } - return NULL; -} - -static const char *md_config_set_names(cmd_parms *cmd, void *arg, - int argc, char *const argv[]) -{ - md_config_t *config = (md_config_t *)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; - - err = ap_check_cmd_context(cmd, NOT_IN_DIR_LOC_FILE); - if (err) { - return err; - } - - for (i = 0; i < argc; ++i) { - add_domain_name(domains, argv[i], cmd->pool); - } - err = md_create(&md, cmd->pool, domains); - if (err) { - return err; - } - - if (cmd->config_file) { - md->defn_name = cmd->config_file->name; - md->defn_line_number = cmd->config_file->line_number; - } - - APR_ARRAY_PUSH(config->mds, md_t *) = md; - - return NULL; -} - -static const char *md_config_set_ca(cmd_parms *cmd, void *dc, const char *value) -{ - md_config_t *config = (md_config_t *)md_config_get(cmd->server); - const char *err; - - if (inside_section(cmd)) { - md_config_dir_t *dconf = dc; - dconf->md->ca_url = value; - } - else { - if ((err = ap_check_cmd_context(cmd, GLOBAL_ONLY))) { - return err; - } - config->ca_url = value; - } - return NULL; -} - -static const char *md_config_set_ca_proto(cmd_parms *cmd, void *dc, const char *value) -{ - md_config_t *config = (md_config_t *)md_config_get(cmd->server); - const char *err; - - if (inside_section(cmd)) { - md_config_dir_t *dconf = dc; - dconf->md->ca_proto = value; - } - else { - if ((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_config_t *config = (md_config_t *)md_config_get(cmd->server); - const char *err; - - if (inside_section(cmd)) { - md_config_dir_t *dconf = dc; - dconf->md->ca_agreement = value; - } - else { - if ((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_config_t *config = (md_config_t *)md_config_get(cmd->server); - const char *err; - md_drive_mode_t drive_mode; - - 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_section(cmd)) { - md_config_dir_t *dconf = dc; - dconf->md->drive_mode = drive_mode; - } - else { - if ((err = ap_check_cmd_context(cmd, GLOBAL_ONLY))) { - return err; - } - config->drive_mode = drive_mode; - } - 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 == '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 const char *md_config_set_renew_window(cmd_parms *cmd, void *dc, const char *value) -{ - md_config_t *config = (md_config_t *)md_config_get(cmd->server); - const char *err; - apr_interval_time_t timeout; - - /* Inspired by http_core.c */ - if (duration_parse(value, &timeout, "d") != APR_SUCCESS) { - return "MDRenewWindow has wrong format"; - } - - if (inside_section(cmd)) { - md_config_dir_t *dconf = dc; - dconf->md->renew_window = timeout; - } - else { - if ((err = ap_check_cmd_context(cmd, GLOBAL_ONLY))) { - return err; - } - config->renew_window = timeout; - } - return NULL; -} - -static const char *md_config_set_store_dir(cmd_parms *cmd, void *arg, const char *value) -{ - md_config_t *config = (md_config_t *)md_config_get(cmd->server); - const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY); - - if (err) { - return err; - } - config->base_dir = value; - (void)arg; - return NULL; -} - -static const char *set_port_map(md_config_t *config, 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: - config->local_80 = local_port; - break; - case 443: - config->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_config_t *config = (md_config_t *)md_config_get(cmd->server); - const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY); - - (void)arg; - if (!err) { - err = set_port_map(config, v1); - } - if (!err && v2) { - err = set_port_map(config, v2); - } - return err; -} - -static const char *md_config_set_cha_tyes(cmd_parms *cmd, void *dc, - int argc, char *const argv[]) -{ - md_config_t *config = (md_config_t *)md_config_get(cmd->server); - apr_array_header_t **pcha, *ca_challenges; - const char *err; - int i; - - if (inside_section(cmd)) { - md_config_dir_t *dconf = dc; - pcha = &dconf->md->ca_challenges; - } - else { - if (NULL != (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; -} - - -#define AP_END_CMD AP_INIT_TAKE1(NULL, NULL, NULL, RSRC_CONF, NULL) - -const command_rec md_cmds[] = { - AP_INIT_RAW_ARGS("module_config, &md_module); - ap_assert(cfg); - if (cfg->s != s && p) { - cfg = md_config_merge(p, &defconf, cfg); - cfg->name = apr_pstrcat(p, CONF_S_NAME(s), cfg->name, NULL); - ap_set_module_config(s->module_config, &md_module, cfg); - } - return cfg; -} - -const md_config_t *md_config_get(server_rec *s) -{ - return config_get_int(s, NULL); -} - -const md_config_t *md_config_get_unique(server_rec *s, apr_pool_t *p) -{ - assert(p); - return config_get_int(s, p); -} - -const md_config_t *md_config_cget(conn_rec *c) -{ - return md_config_get(c->base_server); -} - -const char *md_config_gets(const md_config_t *config, md_config_var_t var) -{ - switch (var) { - case MD_CONFIG_CA_URL: - return config->ca_url? config->ca_url : defconf.ca_url; - case MD_CONFIG_CA_PROTO: - return config->ca_proto? config->ca_proto : defconf.ca_proto; - case MD_CONFIG_BASE_DIR: - return config->base_dir? config->base_dir : defconf.base_dir; - case MD_CONFIG_CA_AGREEMENT: - return config->ca_agreement? config->ca_agreement : defconf.ca_agreement; - default: - return NULL; - } -} - -int md_config_geti(const md_config_t *config, md_config_var_t var) -{ - switch (var) { - case MD_CONFIG_DRIVE_MODE: - return (config->drive_mode != DEF_VAL)? config->drive_mode : defconf.drive_mode; - case MD_CONFIG_LOCAL_80: - return (config->local_80 != DEF_VAL)? config->local_80 : 80; - case MD_CONFIG_LOCAL_443: - return (config->local_443 != DEF_VAL)? config->local_443 : 443; - default: - return 0; - } -} - -apr_interval_time_t md_config_get_interval(const md_config_t *config, md_config_var_t var) -{ - switch (var) { - case MD_CONFIG_RENEW_WINDOW: - return (config->renew_window != DEF_VAL)? config->renew_window : defconf.renew_window; - default: - return 0; - } -} diff --git a/modules/md/md_config.h b/modules/md/md_config.h deleted file mode 100644 index 3568f7c6ac..0000000000 --- a/modules/md/md_config.h +++ /dev/null @@ -1,77 +0,0 @@ -/* Copyright 2017 greenbytes GmbH (https://www.greenbytes.de) - * - * Licensed 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. - */ - -#ifndef mod_md_md_config_h -#define mod_md_md_config_h - -struct md_store_t; - -typedef enum { - MD_CONFIG_CA_URL, - MD_CONFIG_CA_PROTO, - MD_CONFIG_BASE_DIR, - MD_CONFIG_CA_AGREEMENT, - MD_CONFIG_DRIVE_MODE, - MD_CONFIG_LOCAL_80, - MD_CONFIG_LOCAL_443, - MD_CONFIG_RENEW_WINDOW, -} md_config_var_t; - -typedef struct { - const char *name; - const server_rec *s; - - int local_80; - int local_443; - - apr_array_header_t *mds; /* array of md_t pointers */ - const char *ca_url; - const char *ca_proto; - const char *ca_agreement; - apr_array_header_t *ca_challenges; /* challenge types allowed */ - - int drive_mode; - apr_interval_time_t renew_window; /* time for renewal before expiry */ - - const md_t *md; - const char *base_dir; - struct md_store_t *store; - -} md_config_t; - -typedef struct { - md_t *md; -} md_config_dir_t; - -void *md_config_create_svr(apr_pool_t *pool, server_rec *s); -void *md_config_merge_svr(apr_pool_t *pool, void *basev, void *addv); -void *md_config_create_dir(apr_pool_t *pool, char *dummy); -void *md_config_merge_dir(apr_pool_t *pool, void *basev, void *addv); - -extern const command_rec md_cmds[]; - -/* Get the effective md configuration for the connection */ -const md_config_t *md_config_cget(conn_rec *c); -/* Get the effective md configuration for the server */ -const md_config_t *md_config_get(server_rec *s); -/* Get the effective md configuration for the server, but make it - * unique to this server_rec, so that any changes only affect this server */ -const md_config_t *md_config_get_unique(server_rec *s, apr_pool_t *p); - -const char *md_config_gets(const md_config_t *config, md_config_var_t var); -int md_config_geti(const md_config_t *config, md_config_var_t var); -apr_interval_time_t md_config_get_interval(const md_config_t *config, md_config_var_t var); - -#endif /* md_config_h */ diff --git a/modules/md/md_json.c b/modules/md/md_json.c index fc7b13f8de..6feafa6d26 100644 --- a/modules/md/md_json.c +++ b/modules/md/md_json.c @@ -750,6 +750,16 @@ typedef struct { apr_file_t *f; } j_write_ctx; +/* Convert from md_json_fmt_t to the Jansson json_dumpX flags. */ +static size_t fmt_to_flags(md_json_fmt_t fmt) +{ + /* NOTE: JSON_PRESERVE_ORDER is off by default before Jansson 2.8. It + * doesn't have any semantic effect on the protocol, but it does let the + * md_json_writeX unit tests run deterministically. */ + return JSON_PRESERVE_ORDER | + ((fmt == MD_JSON_FMT_COMPACT) ? JSON_COMPACT : JSON_INDENT(2)); +} + static int dump_cb(const char *buffer, size_t len, void *baton) { apr_bucket_brigade *bb = baton; @@ -761,8 +771,7 @@ static int dump_cb(const char *buffer, size_t len, void *baton) apr_status_t md_json_writeb(md_json_t *json, md_json_fmt_t fmt, apr_bucket_brigade *bb) { - size_t flags = (fmt == MD_JSON_FMT_COMPACT)? JSON_COMPACT : JSON_INDENT(2); - int rv = json_dump_callback(json->j, dump_cb, bb, flags); + int rv = json_dump_callback(json->j, dump_cb, bb, fmt_to_flags(fmt)); return rv? APR_EGENERAL : APR_SUCCESS; } @@ -778,12 +787,11 @@ static int chunk_cb(const char *buffer, size_t len, void *baton) const char *md_json_writep(md_json_t *json, apr_pool_t *p, md_json_fmt_t fmt) { - size_t flags = (fmt == MD_JSON_FMT_COMPACT)? JSON_COMPACT : JSON_INDENT(2); apr_array_header_t *chunks; int rv; chunks = apr_array_make(p, 10, sizeof(char *)); - rv = json_dump_callback(json->j, chunk_cb, chunks, flags); + rv = json_dump_callback(json->j, chunk_cb, chunks, fmt_to_flags(fmt)); if (rv) { md_log_perror(MD_LOG_MARK, MD_LOG_ERR, 0, p, diff --git a/modules/md/md_os.c b/modules/md/md_os.c deleted file mode 100644 index 3379acbc2a..0000000000 --- a/modules/md/md_os.c +++ /dev/null @@ -1,82 +0,0 @@ -/* Copyright 2017 greenbytes GmbH (https://www.greenbytes.de) - * - * Licensed 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 -#include - -#include -#include -#include -#include - -#if APR_HAVE_UNISTD_H -#include -#endif -#ifdef WIN32 -#include "mpm_winnt.h" -#endif -#if AP_NEED_SET_MUTEX_PERMS -#include "unixd.h" -#endif - -#include "md_util.h" -#include "md_os.h" - -apr_status_t md_try_chown(const char *fname, int uid, int gid, apr_pool_t *p) -{ -#if AP_NEED_SET_MUTEX_PERMS - if (-1 == chown(fname, (uid_t)uid, (gid_t)gid)) { - apr_status_t rv = APR_FROM_OS_ERROR(errno); - if (!APR_STATUS_IS_ENOENT(rv)) { - ap_log_perror(APLOG_MARK, APLOG_ERR, rv, p, APLOGNO() - "Can't change owner of %s", fname); - } - return rv; - } - return APR_SUCCESS; -#else - return APR_ENOTIMPL; -#endif -} - -apr_status_t md_make_worker_accessible(const char *fname, apr_pool_t *p) -{ -#if AP_NEED_SET_MUTEX_PERMS - return md_try_chown(fname, ap_unixd_config.user_id, -1, p); -#else - return APR_ENOTIMPL; -#endif -} - -#ifdef WIN32 - -apr_status_t md_server_graceful(apr_pool_t *p, server_rec *s) -{ - return APR_ENOTIMPL; -} - -#else - -apr_status_t md_server_graceful(apr_pool_t *p, server_rec *s) -{ - apr_status_t rv; - - rv = (kill(getppid(), AP_SIG_GRACEFUL) < 0)? APR_ENOTIMPL : APR_SUCCESS; - ap_log_error(APLOG_MARK, APLOG_TRACE1, errno, NULL, "sent signal to parent"); - return rv; -} - -#endif - diff --git a/modules/md/md_os.h b/modules/md/md_os.h deleted file mode 100644 index 9f5c2b6e8f..0000000000 --- a/modules/md/md_os.h +++ /dev/null @@ -1,36 +0,0 @@ -/* Copyright 2017 greenbytes GmbH (https://www.greenbytes.de) - * - * Licensed 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. - */ - -#ifndef mod_md_md_os_h -#define mod_md_md_os_h - -/** - * Try chown'ing the file/directory. Give id -1 to not change uid/gid. - * Will return APR_ENOTIMPL on platforms not supporting this operation. - */ -apr_status_t md_try_chown(const char *fname, int uid, int gid, apr_pool_t *p); - -/** - * Make a file or directory read/write(/searchable) by httpd workers. - */ -apr_status_t md_make_worker_accessible(const char *fname, apr_pool_t *p); - -/** - * Trigger a graceful restart of the server. Depending on the architecture, may - * return APR_ENOTIMPL. - */ -apr_status_t md_server_graceful(apr_pool_t *p, server_rec *s); - -#endif /* mod_md_md_os_h */ diff --git a/modules/md/md_store_fs.c b/modules/md/md_store_fs.c index f456b8fc10..09a4eb2835 100644 --- a/modules/md/md_store_fs.c +++ b/modules/md/md_store_fs.c @@ -37,6 +37,8 @@ /**************************************************************************************************/ /* file system based implementation of md_store_t */ +#define MD_STORE_VERSION 1.0 + typedef struct { apr_fileperms_t dir; apr_fileperms_t file; @@ -99,6 +101,7 @@ static apr_status_t init_store_file(md_store_fs_t *s_fs, const char *fname, int i; md_json_sets(MOD_MD_VERSION, json, MD_KEY_VERSION, NULL); + md_json_setn(MD_STORE_VERSION, json, MD_KEY_STORE, MD_KEY_VERSION, NULL); /*if (APR_SUCCESS != (rv = md_rand_bytes(key, sizeof(key), p))) { return rv; @@ -128,18 +131,21 @@ static apr_status_t read_store_file(md_store_fs_t *s_fs, const char *fname, md_json_t *json; const char *s, *key64; apr_status_t rv; + double store_version; if (APR_SUCCESS == (rv = md_json_readf(&json, p, fname))) { - s = md_json_gets(json, MD_KEY_VERSION, NULL); - if (!s) { - md_log_perror(MD_LOG_MARK, MD_LOG_ERR, 0, p, "missing key: %s", MD_KEY_VERSION); - return APR_EINVAL; + store_version = md_json_getn(json, MD_KEY_STORE, MD_KEY_VERSION, NULL); + if (store_version <= 0.0) { + /* ok, an old one, compatible to 1.0 */ + store_version = 1.0; } - if (strcmp(MOD_MD_VERSION, s) < 0) { + if (store_version > MD_STORE_VERSION) { md_log_perror(MD_LOG_MARK, MD_LOG_ERR, 0, p, "version too new: %s", s); return APR_EINVAL; } - /* TODO: need to migrate store? */ + else if (store_version > MD_STORE_VERSION) { + /* migrate future store version changes */ + } key64 = md_json_dups(p, json, MD_KEY_KEY, NULL); if (!key64) { @@ -668,7 +674,26 @@ static apr_status_t pfs_move(void *baton, apr_pool_t *p, apr_pool_t *ptemp, va_l rv = md_util_path_merge(&arch_dir, ptemp, dir, name, NULL); if (APR_SUCCESS != rv) goto out; - while (1) { +#ifdef WIN32 + /* WIN32 and handling of files/dirs. What can one say? */ + + while (n < 1000) { + narch_dir = apr_psprintf(ptemp, "%s.%d", arch_dir, n); + rv = md_util_is_dir(narch_dir, ptemp); + if (APR_STATUS_IS_ENOENT(rv)) { + md_log_perror(MD_LOG_MARK, MD_LOG_DEBUG, rv, ptemp, "using archive dir: %s", + narch_dir); + break; + } + else { + ++n; + narch_dir = NULL; + } + } + +#else /* ifdef WIN32 */ + + while (n < 1000) { narch_dir = apr_psprintf(ptemp, "%s.%d", arch_dir, n); rv = apr_dir_make(narch_dir, MD_FPROT_D_UONLY, ptemp); if (APR_SUCCESS == rv) { @@ -678,13 +703,25 @@ static apr_status_t pfs_move(void *baton, apr_pool_t *p, apr_pool_t *ptemp, va_l } else if (APR_EEXIST == rv) { ++n; + narch_dir = NULL; } else { md_log_perror(MD_LOG_MARK, MD_LOG_ERR, rv, ptemp, "creating archive dir: %s", narch_dir); goto out; } - } + } + +#endif /* ifdef WIN32 (else part) */ + + if (!narch_dir) { + md_log_perror(MD_LOG_MARK, MD_LOG_ERR, rv, ptemp, "ran out of numbers less than 1000 " + "while looking for an available one in %s to archive the data " + "from %s. Either something is generally wrong or you need to " + "clean up some of those directories.", arch_dir, from_dir); + rv = APR_EGENERAL; + goto out; + } if (APR_SUCCESS != (rv = apr_file_rename(to_dir, narch_dir, ptemp))) { md_log_perror(MD_LOG_MARK, MD_LOG_ERR, rv, ptemp, "rename from %s to %s", diff --git a/modules/md/md_version.h b/modules/md/md_version.h index 60afe0f809..405734c2e9 100644 --- a/modules/md/md_version.h +++ b/modules/md/md_version.h @@ -26,7 +26,7 @@ * @macro * Version number of the md module as c string */ -#define MOD_MD_VERSION "0.5.0-git" +#define MOD_MD_VERSION "0.6.0" /** * @macro @@ -34,7 +34,7 @@ * release. This is a 24 bit number with 8 bits for major number, 8 bits * for minor and 8 bits for patch. Version 1.2.3 becomes 0x010203. */ -#define MOD_MD_VERSION_NUM 0x000500 +#define MOD_MD_VERSION_NUM 0x000600 #define MD_EXPERIMENTAL 1 #define MD_ACME_DEF_URL "https://acme-staging.api.letsencrypt.org/directory" diff --git a/modules/md/mod_md.c b/modules/md/mod_md.c index 691f2fcdea..caee79261f 100644 --- a/modules/md/mod_md.c +++ b/modules/md/mod_md.c @@ -26,8 +26,6 @@ #include #include "md.h" -#include "mod_md.h" -#include "md_config.h" #include "md_curl.h" #include "md_crypt.h" #include "md_http.h" @@ -40,7 +38,9 @@ #include "md_acme.h" #include "md_acme_authz.h" -#include "md_os.h" +#include "mod_md.h" +#include "mod_md_config.h" +#include "mod_md_os.h" #include "mod_watchdog.h" static void md_hooks(apr_pool_t *pool); diff --git a/modules/md/mod_md_config.c b/modules/md/mod_md_config.c new file mode 100644 index 0000000000..c5c3d7d7ca --- /dev/null +++ b/modules/md/mod_md_config.c @@ -0,0 +1,582 @@ +/* Copyright 2017 greenbytes GmbH (https://www.greenbytes.de) + * + * Licensed 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 + +#include +#include + +#include +#include +#include +#include +#include + +#include "md.h" +#include "md_util.h" +#include "mod_md_private.h" +#include "mod_md_config.h" + + +#define DEF_VAL (-1) + +static md_config_t defconf = { + "default", + NULL, + 80, + 443, + NULL, + MD_ACME_DEF_URL, + "ACME", + NULL, + NULL, + MD_DRIVE_AUTO, + apr_time_from_sec(14 * MD_SECS_PER_DAY), + NULL, + "md", + NULL +}; + +#define CONF_S_NAME(s) (s && s->server_hostname? s->server_hostname : "default") + +void *md_config_create_svr(apr_pool_t *pool, server_rec *s) +{ + md_config_t *conf = (md_config_t *)apr_pcalloc(pool, sizeof(md_config_t)); + + conf->name = apr_pstrcat(pool, "srv[", CONF_S_NAME(s), "]", NULL); + conf->s = s; + conf->local_80 = DEF_VAL; + conf->local_443 = DEF_VAL; + conf->drive_mode = DEF_VAL; + conf->mds = apr_array_make(pool, 5, sizeof(const md_t *)); + conf->renew_window = DEF_VAL; + + return conf; +} + +static void *md_config_merge(apr_pool_t *pool, void *basev, void *addv) +{ + md_config_t *base = (md_config_t *)basev; + md_config_t *add = (md_config_t *)addv; + md_config_t *n = (md_config_t *)apr_pcalloc(pool, sizeof(md_config_t)); + char *name = apr_pstrcat(pool, "[", CONF_S_NAME(add->s), ", ", CONF_S_NAME(base->s), "]", NULL); + md_t *md; + int i; + + n->name = name; + n->local_80 = (add->local_80 != DEF_VAL)? add->local_80 : base->local_80; + n->local_443 = (add->local_443 != DEF_VAL)? add->local_443 : base->local_443; + + /* I think we should not merge md definitions. They should reside where + * they were defined */ + n->mds = apr_array_make(pool, add->mds->nelts, sizeof(const md_t *)); + for (i = 0; i < add->mds->nelts; ++i) { + md = APR_ARRAY_IDX(add->mds, i, md_t*); + APR_ARRAY_PUSH(n->mds, md_t *) = md_clone(pool, md); + } + n->ca_url = add->ca_url? add->ca_url : base->ca_url; + n->ca_proto = add->ca_proto? add->ca_proto : base->ca_proto; + n->ca_agreement = add->ca_agreement? add->ca_agreement : base->ca_agreement; + n->drive_mode = (add->drive_mode != DEF_VAL)? add->drive_mode : base->drive_mode; + n->md = NULL; + n->base_dir = add->base_dir? add->base_dir : base->base_dir; + n->renew_window = (add->renew_window != DEF_VAL)? add->renew_window : base->renew_window; + n->ca_challenges = (add->ca_challenges? apr_array_copy(pool, add->ca_challenges) + : (base->ca_challenges? apr_array_copy(pool, base->ca_challenges) : NULL)); + return n; +} + +void *md_config_merge_svr(apr_pool_t *pool, void *basev, void *addv) +{ + return md_config_merge(pool, basev, addv); +} + +void *md_config_create_dir(apr_pool_t *pool, char *dummy) +{ + md_config_dir_t *conf = apr_pcalloc(pool, sizeof(*conf)); + return conf; +} + +void *md_config_merge_dir(apr_pool_t *pool, void *basev, void *addv) +{ + md_config_dir_t *base = basev; + md_config_dir_t *add = addv; + md_config_dir_t *n = apr_pcalloc(pool, sizeof(*n)); + n->md = add->md? add->md : base->md; + return n; +} + +static int inside_section(cmd_parms *cmd) { + return (cmd->directive->parent + && !ap_cstr_casecmp(cmd->directive->parent->directive, "pool, cmd->cmd->name, + " is only valid inside a directive->parent? cmd->directive->parent->directive : "root", + 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 *md_config_sec_start(cmd_parms *cmd, void *mconfig, const char *arg) +{ + md_config_t *sconf = ap_get_module_config(cmd->server->module_config, &md_module); + const char *endp = ap_strrchr_c(arg, '>'); + ap_conf_vector_t *new_dir_conf = ap_create_per_dir_config(cmd->pool); + int old_overrides = cmd->override; + char *old_path = cmd->path; + const char *err, *name; + md_config_dir_t *dconf; + md_t *md; + + if (NULL != (err = ap_check_cmd_context(cmd, NOT_IN_DIR_LOC_FILE))) { + return err; + } + + if (endp == NULL) { + return apr_pstrcat(cmd->pool, cmd->cmd->name, "> directive missing closing '>'", NULL); + } + + arg = apr_pstrndup(cmd->pool, arg, endp-arg); + if (!arg || !*arg) { + return " block must specify a unique domain name"; + } + + cmd->path = ap_getword_white(cmd->pool, &arg); + name = cmd->path; + + md = md_create_empty(cmd->pool); + md->name = name; + APR_ARRAY_PUSH(md->domains, const char*) = name; + md->drive_mode = DEF_VAL; + + while (*arg != '\0') { + name = ap_getword_white(cmd->pool, &arg); + APR_ARRAY_PUSH(md->domains, const char*) = name; + } + + dconf = ap_set_config_vectors(cmd->server, new_dir_conf, cmd->path, &md_module, cmd->pool); + dconf->md = md; + + if (NULL == (err = ap_walk_config(cmd->directive->first_child, cmd, new_dir_conf))) { + APR_ARRAY_PUSH(sconf->mds, const md_t *) = md; + } + + cmd->path = old_path; + cmd->override = old_overrides; + + return err; +} + +static const char *md_config_sec_add_members(cmd_parms *cmd, void *dc, + int argc, char *const argv[]) +{ + md_config_dir_t *dconfig = dc; + apr_array_header_t *domains; + const char *err; + int i; + + if (NULL != (err = md_section_check(cmd))) { + return err; + } + + domains = dconfig->md->domains; + for (i = 0; i < argc; ++i) { + add_domain_name(domains, argv[i], cmd->pool); + } + return NULL; +} + +static const char *md_config_set_names(cmd_parms *cmd, void *arg, + int argc, char *const argv[]) +{ + md_config_t *config = (md_config_t *)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; + + err = ap_check_cmd_context(cmd, NOT_IN_DIR_LOC_FILE); + if (err) { + return err; + } + + for (i = 0; i < argc; ++i) { + add_domain_name(domains, argv[i], cmd->pool); + } + err = md_create(&md, cmd->pool, domains); + if (err) { + return err; + } + + if (cmd->config_file) { + md->defn_name = cmd->config_file->name; + md->defn_line_number = cmd->config_file->line_number; + } + + APR_ARRAY_PUSH(config->mds, md_t *) = md; + + return NULL; +} + +static const char *md_config_set_ca(cmd_parms *cmd, void *dc, const char *value) +{ + md_config_t *config = (md_config_t *)md_config_get(cmd->server); + const char *err; + + if (inside_section(cmd)) { + md_config_dir_t *dconf = dc; + dconf->md->ca_url = value; + } + else { + if ((err = ap_check_cmd_context(cmd, GLOBAL_ONLY))) { + return err; + } + config->ca_url = value; + } + return NULL; +} + +static const char *md_config_set_ca_proto(cmd_parms *cmd, void *dc, const char *value) +{ + md_config_t *config = (md_config_t *)md_config_get(cmd->server); + const char *err; + + if (inside_section(cmd)) { + md_config_dir_t *dconf = dc; + dconf->md->ca_proto = value; + } + else { + if ((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_config_t *config = (md_config_t *)md_config_get(cmd->server); + const char *err; + + if (inside_section(cmd)) { + md_config_dir_t *dconf = dc; + dconf->md->ca_agreement = value; + } + else { + if ((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_config_t *config = (md_config_t *)md_config_get(cmd->server); + const char *err; + md_drive_mode_t drive_mode; + + 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_section(cmd)) { + md_config_dir_t *dconf = dc; + dconf->md->drive_mode = drive_mode; + } + else { + if ((err = ap_check_cmd_context(cmd, GLOBAL_ONLY))) { + return err; + } + config->drive_mode = drive_mode; + } + 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 == '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 const char *md_config_set_renew_window(cmd_parms *cmd, void *dc, const char *value) +{ + md_config_t *config = (md_config_t *)md_config_get(cmd->server); + const char *err; + apr_interval_time_t timeout; + + /* Inspired by http_core.c */ + if (duration_parse(value, &timeout, "d") != APR_SUCCESS) { + return "MDRenewWindow has wrong format"; + } + + if (inside_section(cmd)) { + md_config_dir_t *dconf = dc; + dconf->md->renew_window = timeout; + } + else { + if ((err = ap_check_cmd_context(cmd, GLOBAL_ONLY))) { + return err; + } + config->renew_window = timeout; + } + return NULL; +} + +static const char *md_config_set_store_dir(cmd_parms *cmd, void *arg, const char *value) +{ + md_config_t *config = (md_config_t *)md_config_get(cmd->server); + const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY); + + if (err) { + return err; + } + config->base_dir = value; + (void)arg; + return NULL; +} + +static const char *set_port_map(md_config_t *config, 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: + config->local_80 = local_port; + break; + case 443: + config->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_config_t *config = (md_config_t *)md_config_get(cmd->server); + const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY); + + (void)arg; + if (!err) { + err = set_port_map(config, v1); + } + if (!err && v2) { + err = set_port_map(config, v2); + } + return err; +} + +static const char *md_config_set_cha_tyes(cmd_parms *cmd, void *dc, + int argc, char *const argv[]) +{ + md_config_t *config = (md_config_t *)md_config_get(cmd->server); + apr_array_header_t **pcha, *ca_challenges; + const char *err; + int i; + + if (inside_section(cmd)) { + md_config_dir_t *dconf = dc; + pcha = &dconf->md->ca_challenges; + } + else { + if (NULL != (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; +} + + +#define AP_END_CMD AP_INIT_TAKE1(NULL, NULL, NULL, RSRC_CONF, NULL) + +const command_rec md_cmds[] = { + AP_INIT_RAW_ARGS("module_config, &md_module); + ap_assert(cfg); + if (cfg->s != s && p) { + cfg = md_config_merge(p, &defconf, cfg); + cfg->name = apr_pstrcat(p, CONF_S_NAME(s), cfg->name, NULL); + ap_set_module_config(s->module_config, &md_module, cfg); + } + return cfg; +} + +const md_config_t *md_config_get(server_rec *s) +{ + return config_get_int(s, NULL); +} + +const md_config_t *md_config_get_unique(server_rec *s, apr_pool_t *p) +{ + assert(p); + return config_get_int(s, p); +} + +const md_config_t *md_config_cget(conn_rec *c) +{ + return md_config_get(c->base_server); +} + +const char *md_config_gets(const md_config_t *config, md_config_var_t var) +{ + switch (var) { + case MD_CONFIG_CA_URL: + return config->ca_url? config->ca_url : defconf.ca_url; + case MD_CONFIG_CA_PROTO: + return config->ca_proto? config->ca_proto : defconf.ca_proto; + case MD_CONFIG_BASE_DIR: + return config->base_dir? config->base_dir : defconf.base_dir; + case MD_CONFIG_CA_AGREEMENT: + return config->ca_agreement? config->ca_agreement : defconf.ca_agreement; + default: + return NULL; + } +} + +int md_config_geti(const md_config_t *config, md_config_var_t var) +{ + switch (var) { + case MD_CONFIG_DRIVE_MODE: + return (config->drive_mode != DEF_VAL)? config->drive_mode : defconf.drive_mode; + case MD_CONFIG_LOCAL_80: + return (config->local_80 != DEF_VAL)? config->local_80 : 80; + case MD_CONFIG_LOCAL_443: + return (config->local_443 != DEF_VAL)? config->local_443 : 443; + default: + return 0; + } +} + +apr_interval_time_t md_config_get_interval(const md_config_t *config, md_config_var_t var) +{ + switch (var) { + case MD_CONFIG_RENEW_WINDOW: + return (config->renew_window != DEF_VAL)? config->renew_window : defconf.renew_window; + default: + return 0; + } +} diff --git a/modules/md/mod_md_config.h b/modules/md/mod_md_config.h new file mode 100644 index 0000000000..3568f7c6ac --- /dev/null +++ b/modules/md/mod_md_config.h @@ -0,0 +1,77 @@ +/* Copyright 2017 greenbytes GmbH (https://www.greenbytes.de) + * + * Licensed 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. + */ + +#ifndef mod_md_md_config_h +#define mod_md_md_config_h + +struct md_store_t; + +typedef enum { + MD_CONFIG_CA_URL, + MD_CONFIG_CA_PROTO, + MD_CONFIG_BASE_DIR, + MD_CONFIG_CA_AGREEMENT, + MD_CONFIG_DRIVE_MODE, + MD_CONFIG_LOCAL_80, + MD_CONFIG_LOCAL_443, + MD_CONFIG_RENEW_WINDOW, +} md_config_var_t; + +typedef struct { + const char *name; + const server_rec *s; + + int local_80; + int local_443; + + apr_array_header_t *mds; /* array of md_t pointers */ + const char *ca_url; + const char *ca_proto; + const char *ca_agreement; + apr_array_header_t *ca_challenges; /* challenge types allowed */ + + int drive_mode; + apr_interval_time_t renew_window; /* time for renewal before expiry */ + + const md_t *md; + const char *base_dir; + struct md_store_t *store; + +} md_config_t; + +typedef struct { + md_t *md; +} md_config_dir_t; + +void *md_config_create_svr(apr_pool_t *pool, server_rec *s); +void *md_config_merge_svr(apr_pool_t *pool, void *basev, void *addv); +void *md_config_create_dir(apr_pool_t *pool, char *dummy); +void *md_config_merge_dir(apr_pool_t *pool, void *basev, void *addv); + +extern const command_rec md_cmds[]; + +/* Get the effective md configuration for the connection */ +const md_config_t *md_config_cget(conn_rec *c); +/* Get the effective md configuration for the server */ +const md_config_t *md_config_get(server_rec *s); +/* Get the effective md configuration for the server, but make it + * unique to this server_rec, so that any changes only affect this server */ +const md_config_t *md_config_get_unique(server_rec *s, apr_pool_t *p); + +const char *md_config_gets(const md_config_t *config, md_config_var_t var); +int md_config_geti(const md_config_t *config, md_config_var_t var); +apr_interval_time_t md_config_get_interval(const md_config_t *config, md_config_var_t var); + +#endif /* md_config_h */ diff --git a/modules/md/mod_md_os.c b/modules/md/mod_md_os.c new file mode 100644 index 0000000000..8e9896b53f --- /dev/null +++ b/modules/md/mod_md_os.c @@ -0,0 +1,82 @@ +/* Copyright 2017 greenbytes GmbH (https://www.greenbytes.de) + * + * Licensed 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 +#include + +#include +#include +#include +#include + +#if APR_HAVE_UNISTD_H +#include +#endif +#ifdef WIN32 +#include "mpm_winnt.h" +#endif +#if AP_NEED_SET_MUTEX_PERMS +#include "unixd.h" +#endif + +#include "md_util.h" +#include "mod_md_os.h" + +apr_status_t md_try_chown(const char *fname, int uid, int gid, apr_pool_t *p) +{ +#if AP_NEED_SET_MUTEX_PERMS + if (-1 == chown(fname, (uid_t)uid, (gid_t)gid)) { + apr_status_t rv = APR_FROM_OS_ERROR(errno); + if (!APR_STATUS_IS_ENOENT(rv)) { + ap_log_perror(APLOG_MARK, APLOG_ERR, rv, p, APLOGNO() + "Can't change owner of %s", fname); + } + return rv; + } + return APR_SUCCESS; +#else + return APR_ENOTIMPL; +#endif +} + +apr_status_t md_make_worker_accessible(const char *fname, apr_pool_t *p) +{ +#if AP_NEED_SET_MUTEX_PERMS + return md_try_chown(fname, ap_unixd_config.user_id, -1, p); +#else + return APR_ENOTIMPL; +#endif +} + +#ifdef WIN32 + +apr_status_t md_server_graceful(apr_pool_t *p, server_rec *s) +{ + return APR_ENOTIMPL; +} + +#else + +apr_status_t md_server_graceful(apr_pool_t *p, server_rec *s) +{ + apr_status_t rv; + + rv = (kill(getppid(), AP_SIG_GRACEFUL) < 0)? APR_ENOTIMPL : APR_SUCCESS; + ap_log_error(APLOG_MARK, APLOG_TRACE1, errno, NULL, "sent signal to parent"); + return rv; +} + +#endif + diff --git a/modules/md/mod_md_os.h b/modules/md/mod_md_os.h new file mode 100644 index 0000000000..9f5c2b6e8f --- /dev/null +++ b/modules/md/mod_md_os.h @@ -0,0 +1,36 @@ +/* Copyright 2017 greenbytes GmbH (https://www.greenbytes.de) + * + * Licensed 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. + */ + +#ifndef mod_md_md_os_h +#define mod_md_md_os_h + +/** + * Try chown'ing the file/directory. Give id -1 to not change uid/gid. + * Will return APR_ENOTIMPL on platforms not supporting this operation. + */ +apr_status_t md_try_chown(const char *fname, int uid, int gid, apr_pool_t *p); + +/** + * Make a file or directory read/write(/searchable) by httpd workers. + */ +apr_status_t md_make_worker_accessible(const char *fname, apr_pool_t *p); + +/** + * Trigger a graceful restart of the server. Depending on the architecture, may + * return APR_ENOTIMPL. + */ +apr_status_t md_server_graceful(apr_pool_t *p, server_rec *s); + +#endif /* mod_md_md_os_h */ diff --git a/modules/md/mod_md_private.h b/modules/md/mod_md_private.h new file mode 100644 index 0000000000..d4a4a76d02 --- /dev/null +++ b/modules/md/mod_md_private.h @@ -0,0 +1,23 @@ +/* Copyright 2015 greenbytes GmbH (https://www.greenbytes.de) + * + * Licensed 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. + */ + +#ifndef mod_md_md_private_h +#define mod_md_md_private_h + +extern module AP_MODULE_DECLARE_DATA md_module; + +APLOG_USE_MODULE(md); + +#endif -- cgit v1.2.3