diff options
author | Stefan Eissing <icing@apache.org> | 2019-06-25 17:21:10 +0200 |
---|---|---|
committer | Stefan Eissing <icing@apache.org> | 2019-06-25 17:21:10 +0200 |
commit | 00eb58b75fe2722d3350acfde32dd7098d206d81 (patch) | |
tree | b59ec3e63097af8ebcba651db7d238308ea8d935 /modules | |
parent | * server/main.c (main): Don't create a separate subpool for pcommands (diff) | |
download | apache2-00eb58b75fe2722d3350acfde32dd7098d206d81.tar.xz apache2-00eb58b75fe2722d3350acfde32dd7098d206d81.zip |
*) mod_ssl/mod_md: reversing dependency by letting mod_ssl offer hooks for
adding certificates and keys to a virtual host. An additional hook allows
answering special TLS connections as used in ACME challenges.
git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@1862075 13f79535-47bb-0310-9956-ffa450edef68
Diffstat (limited to 'modules')
-rw-r--r-- | modules/md/md.h | 2 | ||||
-rw-r--r-- | modules/md/mod_md.c | 124 | ||||
-rw-r--r-- | modules/ssl/mod_ssl.h | 32 | ||||
-rw-r--r-- | modules/ssl/ssl_engine_init.c | 107 |
4 files changed, 166 insertions, 99 deletions
diff --git a/modules/md/md.h b/modules/md/md.h index a2d874551a..f617dd31db 100644 --- a/modules/md/md.h +++ b/modules/md/md.h @@ -29,8 +29,6 @@ struct md_store_t; struct md_srv_conf_t; struct md_pkey_spec_t; -#define MD_TLSSNI01_DNS_SUFFIX ".acme.invalid" - #define MD_PKEY_RSA_BITS_MIN 2048 #define MD_PKEY_RSA_BITS_DEF 2048 diff --git a/modules/md/mod_md.c b/modules/md/mod_md.c index 02ab599203..e4f247ac0a 100644 --- a/modules/md/mod_md.c +++ b/modules/md/mod_md.c @@ -919,8 +919,8 @@ static apr_status_t setup_fallback_cert(md_store_t *store, const md_t *md, return rv; } -static apr_status_t md_get_certificate(server_rec *s, apr_pool_t *p, - const char **pkeyfile, const char **pcertfile) +static apr_status_t get_certificate(server_rec *s, apr_pool_t *p, int fallback, + const char **pcertfile, const char **pkeyfile) { apr_status_t rv = APR_ENOENT; md_srv_conf_t *sc; @@ -932,11 +932,11 @@ static apr_status_t md_get_certificate(server_rec *s, apr_pool_t *p, *pcertfile = NULL; ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(10113) - "md_get_certificate called for vhost %s.", s->server_hostname); + "get_certificate called for vhost %s.", s->server_hostname); sc = md_config_get(s); if (!sc) { - ap_log_error(APLOG_MARK, APLOG_TRACE1, 0, s, + ap_log_error(APLOG_MARK, APLOG_TRACE2, 0, s, "asked for certificate of server %s which has no md config", s->server_hostname); return APR_ENOENT; @@ -971,23 +971,25 @@ static apr_status_t md_get_certificate(server_rec *s, apr_pool_t *p, rv = md_reg_get_cred_files(pkeyfile, pcertfile, reg, MD_SG_DOMAINS, md, p); if (APR_STATUS_IS_ENOENT(rv)) { - /* Provide temporary, self-signed certificate as fallback, so that - * clients do not get obscure TLS handshake errors or will see a fallback - * virtual host that is not intended to be served here. */ - store = md_reg_store_get(reg); - assert(store); - - md_store_get_fname(pkeyfile, store, MD_SG_DOMAINS, md->name, MD_FN_FALLBACK_PKEY, p); - md_store_get_fname(pcertfile, store, MD_SG_DOMAINS, md->name, MD_FN_FALLBACK_CERT, p); - if (!md_file_exists(*pkeyfile, p) || !md_file_exists(*pcertfile, p)) { - if (APR_SUCCESS != (rv = setup_fallback_cert(store, md, s, p))) { - return rv; + if (fallback) { + /* Provide temporary, self-signed certificate as fallback, so that + * clients do not get obscure TLS handshake errors or will see a fallback + * virtual host that is not intended to be served here. */ + store = md_reg_store_get(reg); + assert(store); + + md_store_get_fname(pkeyfile, store, MD_SG_DOMAINS, md->name, MD_FN_FALLBACK_PKEY, p); + md_store_get_fname(pcertfile, store, MD_SG_DOMAINS, md->name, MD_FN_FALLBACK_CERT, p); + if (!md_file_exists(*pkeyfile, p) || !md_file_exists(*pcertfile, p)) { + if (APR_SUCCESS != (rv = setup_fallback_cert(store, md, s, p))) { + return rv; + } } + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(10116) + "%s: providing fallback certificate for server %s", + md->name, s->server_hostname); + return APR_EAGAIN; } - ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(10116) - "%s: providing fallback certificate for server %s", - md->name, s->server_hostname); - return APR_EAGAIN; } else if (APR_SUCCESS != rv) { ap_log_error(APLOG_MARK, APLOG_ERR, rv, s, APLOGNO(10110) @@ -1001,33 +1003,70 @@ static apr_status_t md_get_certificate(server_rec *s, apr_pool_t *p, return rv; } +static apr_status_t md_get_certificate(server_rec *s, apr_pool_t *p, + const char **pkeyfile, const char **pcertfile) +{ + return get_certificate(s, p, 1, pcertfile, pkeyfile); +} + +static int md_add_cert_files(server_rec *s, apr_pool_t *p, + apr_array_header_t *cert_files, + apr_array_header_t *key_files) +{ + const char *certfile, *keyfile; + apr_status_t rv; + + ap_log_error(APLOG_MARK, APLOG_TRACE1, 0, s, "hook ssl_add_cert_files for %s", + s->server_hostname); + rv = get_certificate(s, p, 0, &certfile, &keyfile); + if (APR_SUCCESS == rv) { + if (!apr_is_empty_array(cert_files)) { + ap_log_error(APLOG_MARK, APLOG_WARNING, 0, s, APLOGNO(10084) + "host '%s' is covered by a Managed Domain, but " + "certificate/key files are already configured " + "for it (most likely via SSLCertificateFile).", + s->server_hostname); + } + APR_ARRAY_PUSH(cert_files, const char*) = certfile; + APR_ARRAY_PUSH(key_files, const char*) = keyfile; + return DONE; + } + return DECLINED; +} + +static int md_add_fallback_cert_files(server_rec *s, apr_pool_t *p, + apr_array_header_t *cert_files, + apr_array_header_t *key_files) +{ + const char *certfile, *keyfile; + apr_status_t rv; + + ap_log_error(APLOG_MARK, APLOG_TRACE1, 0, s, "hook ssl_add_fallback_cert_files for %s", + s->server_hostname); + rv = get_certificate(s, p, 1, &certfile, &keyfile); + if (APR_EAGAIN == rv) { + APR_ARRAY_PUSH(cert_files, const char*) = certfile; + APR_ARRAY_PUSH(key_files, const char*) = keyfile; + return DONE; + } + return DECLINED; +} + static int md_is_challenge(conn_rec *c, const char *servername, X509 **pcert, EVP_PKEY **pkey) { md_srv_conf_t *sc; - apr_size_t slen, sufflen = sizeof(MD_TLSSNI01_DNS_SUFFIX) - 1; const char *protocol, *challenge, *cert_name, *pkey_name; apr_status_t rv; if (!servername) goto out; challenge = NULL; - slen = strlen(servername); - if (slen > sufflen - && !apr_strnatcasecmp(MD_TLSSNI01_DNS_SUFFIX, servername + slen - sufflen)) { - /* server name ends with the tls-sni-01 challenge suffix, answer if - * we have prepared a certificate in store under this name */ - challenge = "tls-sni-01"; - cert_name = MD_FN_TLSSNI01_CERT; - pkey_name = MD_FN_TLSSNI01_PKEY; - } - else if ((protocol = md_protocol_get(c)) && !strcmp(PROTO_ACME_TLS_1, protocol)) { + if ((protocol = md_protocol_get(c)) && !strcmp(PROTO_ACME_TLS_1, protocol)) { challenge = "tls-alpn-01"; cert_name = MD_FN_TLSALPN01_CERT; pkey_name = MD_FN_TLSALPN01_PKEY; - } - - if (challenge) { + sc = md_config_get(c->base_server); if (sc && sc->mc->reg) { md_store_t *store = md_reg_store_get(sc->mc->reg); @@ -1061,6 +1100,15 @@ out: return 0; } +static int md_answer_challenge(conn_rec *c, const char *servername, + void **pX509, void **pEVP_PKEY) +{ + if (md_is_challenge(c, servername, (X509**)pX509, (EVP_PKEY**)pEVP_PKEY)) { + return APR_SUCCESS; + } + return DECLINED; +} + /**************************************************************************************************/ /* ACME 'http-01' challenge responses */ @@ -1230,8 +1278,16 @@ static void md_hooks(apr_pool_t *pool) APR_OPTIONAL_HOOK(ap, status_hook, md_status_hook, NULL, NULL, APR_HOOK_MIDDLE); ap_hook_handler(md_status_handler, NULL, NULL, APR_HOOK_MIDDLE); +#ifdef SSL_CERT_HOOKS + (void)md_is_managed; + (void)md_get_certificate; + APR_OPTIONAL_HOOK(ssl, add_cert_files, md_add_cert_files, NULL, NULL, APR_HOOK_MIDDLE); + APR_OPTIONAL_HOOK(ssl, add_fallback_cert_files, md_add_fallback_cert_files, NULL, NULL, APR_HOOK_MIDDLE); + APR_OPTIONAL_HOOK(ssl, answer_challenge, md_answer_challenge, NULL, NULL, APR_HOOK_MIDDLE); +#else + APR_REGISTER_OPTIONAL_FN(md_is_challenge); APR_REGISTER_OPTIONAL_FN(md_is_managed); APR_REGISTER_OPTIONAL_FN(md_get_certificate); - APR_REGISTER_OPTIONAL_FN(md_is_challenge); +#endif } diff --git a/modules/ssl/mod_ssl.h b/modules/ssl/mod_ssl.h index f96f3c82a8..5ae7ba6a71 100644 --- a/modules/ssl/mod_ssl.h +++ b/modules/ssl/mod_ssl.h @@ -30,6 +30,8 @@ #include "http_config.h" #include "apr_optional.h" +struct apr_array_header_t; + /* Create a set of SSL_DECLARE(type), SSL_DECLARE_NONSTD(type) and * SSL_DECLARE_DATA with appropriate export and import tags for the platform */ @@ -95,6 +97,36 @@ APR_DECLARE_OPTIONAL_FN(int, ssl_engine_disable, (conn_rec *)); APR_DECLARE_OPTIONAL_FN(int, ssl_engine_set, (conn_rec *, ap_conf_vector_t *, int proxy, int enable)); + +/* Check for availability of new hooks */ +#define SSL_CERT_HOOKS +#ifdef SSL_CERT_HOOKS + +/** Lets others add certificate and key files to the given server. + * For each cert a key must also be added. */ +APR_DECLARE_EXTERNAL_HOOK(ssl, SSL, int, add_cert_files, + (server_rec *s, apr_pool_t *p, + struct apr_array_header_t *cert_files, + struct apr_array_header_t *key_files)) + +/** In case no certificates are available for a server, this + * lets other modules add a fallback certificate for the time + * being. Regular requests against this server will be answered + * with a 503. */ +APR_DECLARE_EXTERNAL_HOOK(ssl, SSL, int, add_fallback_cert_files, + (server_rec *s, apr_pool_t *p, + struct apr_array_header_t *cert_files, + struct apr_array_header_t *key_files)) + +/** On TLS connections that do not relate to a configured virtual host, + * allow other modules to provide a X509 certificate and EVP_PKEY to + * be used on the connection. This first hook which does not + * return DECLINDED will determine the outcome. */ +APR_DECLARE_EXTERNAL_HOOK(ssl, SSL, int, answer_challenge, + (conn_rec *c, const char *server_name, + void **pX509, void **pEVP_PKEY)) + +#endif /* SSL_CERT_HOOKS */ #endif /* __MOD_SSL_H__ */ /** @} */ diff --git a/modules/ssl/ssl_engine_init.c b/modules/ssl/ssl_engine_init.c index b54bc91f75..bb9b4ab385 100644 --- a/modules/ssl/ssl_engine_init.c +++ b/modules/ssl/ssl_engine_init.c @@ -39,6 +39,27 @@ APR_IMPLEMENT_OPTIONAL_HOOK_RUN_ALL(ssl, SSL, int, init_server, (server_rec *s,apr_pool_t *p,int is_proxy,SSL_CTX *ctx), (s,p,is_proxy,ctx), OK, DECLINED) +/* Implement 'ap_run_ssl_add_cert_files'. */ +APR_IMPLEMENT_OPTIONAL_HOOK_RUN_ALL(ssl, SSL, int, add_cert_files, + (server_rec *s, apr_pool_t *p, + apr_array_header_t *cert_files, apr_array_header_t *key_files), + (s, p, cert_files, key_files), + OK, DECLINED) + +APR_IMPLEMENT_OPTIONAL_HOOK_RUN_ALL(ssl, SSL, int, add_fallback_cert_files, + (server_rec *s, apr_pool_t *p, + apr_array_header_t *cert_files, apr_array_header_t *key_files), + (s, p, cert_files, key_files), + OK, DECLINED) + +APR_IMPLEMENT_OPTIONAL_HOOK_RUN_ALL(ssl, SSL, int, answer_challenge, + (conn_rec *c, const char *server_name, + void **pX509, void **pEVP_PKEY), + (c, server_name, pX509, pEVP_PKEY), + DECLINED, DECLINED) + + + /* _________________________________________________________________ ** ** Module Initialization @@ -170,19 +191,16 @@ static void ssl_add_version_components(apr_pool_t *p, /* _________________________________________________________________ ** -** Managed Domains Interface +** Let other answer special connection attempts. +** Used in ACME challenge handling by mod_md. ** _________________________________________________________________ */ -static APR_OPTIONAL_FN_TYPE(md_is_managed) *md_is_managed; -static APR_OPTIONAL_FN_TYPE(md_get_certificate) *md_get_certificate; -static APR_OPTIONAL_FN_TYPE(md_is_challenge) *md_is_challenge; - int ssl_is_challenge(conn_rec *c, const char *servername, X509 **pcert, EVP_PKEY **pkey) { - if (md_is_challenge) { - return md_is_challenge(c, servername, pcert, pkey); + if (APR_SUCCESS == ssl_run_answer_challenge(c, servername, (void**)pcert, (void**)pkey)) { + return 1; } *pcert = NULL; *pkey = NULL; @@ -237,16 +255,6 @@ apr_status_t ssl_init_Module(apr_pool_t *p, apr_pool_t *plog, ssl_config_global_create(base_server); /* just to avoid problems */ ssl_config_global_fix(mc); - /* Initialize our interface to mod_md, if it is loaded - */ - md_is_managed = APR_RETRIEVE_OPTIONAL_FN(md_is_managed); - md_get_certificate = APR_RETRIEVE_OPTIONAL_FN(md_get_certificate); - md_is_challenge = APR_RETRIEVE_OPTIONAL_FN(md_is_challenge); - if (!md_is_managed || !md_get_certificate) { - md_is_managed = NULL; - md_get_certificate = NULL; - } - /* * try to fix the configuration and open the dedicated SSL * logfile as early as possible @@ -1798,6 +1806,7 @@ static apr_status_t ssl_init_server_ctx(server_rec *s, SSL_CONF_CTX *cctx = sc->server->ssl_ctx_config; int i; #endif + int n; /* * Check for problematic re-initializations @@ -1809,52 +1818,24 @@ static apr_status_t ssl_init_server_ctx(server_rec *s, return APR_EGENERAL; } - ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(10083) - "Init: (%s) mod_md support is %s.", ssl_util_vhostid(p, s), - md_is_managed? "available" : "unavailable"); - if (md_is_managed && md_get_certificate && md_is_managed(s)) { - pks = sc->server->pks; - if (pks->cert_files->nelts > 0 || pks->key_files->nelts > 0) { - ap_log_error(APLOG_MARK, APLOG_WARNING, 0, s, APLOGNO(10084) - "Init: (%s) You configured certificate/key files on this host, but " - "it is covered by a Managed Domain. You need to remove these directives " - "for the Managed Domain to take over.", ssl_util_vhostid(p, s)); - } - else { - const char *key_file = NULL, *cert_file = NULL; - int service_unavailable = 0; - - rv = md_get_certificate(s, p, &key_file, &cert_file); - - if (APR_STATUS_IS_EAGAIN(rv)) { - /* Managed Domain not ready yet. This is not a reason to fail the config */ - ap_log_error(APLOG_MARK, APLOG_WARNING, 0, s, APLOGNO(10085) - "Init: %s will respond with '503 Service Unavailable' for now. This " - "host is part of a Managed Domain, but no SSL certificate is " - "available (yet).", ssl_util_vhostid(p, s)); - service_unavailable = 1; - rv = APR_SUCCESS; - } - else if (rv != APR_SUCCESS) { - return rv; - } - - /* We install the managed certificate and key in the server_rec config now - * and, if those are not ready/fallback ones, mark service as unavailable. - */ - if (APR_SUCCESS == rv && key_file && cert_file) { - ap_log_error(APLOG_MARK, APLOG_TRACE1, 0, s, - "%s: installing key=%s, cert=%s", - sc->vhost_id, key_file, cert_file); - APR_ARRAY_PUSH(pks->key_files, const char *) = key_file; - APR_ARRAY_PUSH(pks->cert_files, const char *) = cert_file; - sc->server->cert_chain = NULL; - } - - if (service_unavailable) { - pks->service_unavailable = 1; - } - } + /* Allow others to provide certificate files */ + pks = sc->server->pks; + n = pks->cert_files->nelts; + ssl_run_add_cert_files(s, p, pks->cert_files, pks->key_files); + + if (n < pks->cert_files->nelts) { + /* this overrides any old chain configuration */ + sc->server->cert_chain = NULL; + } + + if (apr_is_empty_array(pks->cert_files) && !sc->server->cert_chain) { + ssl_run_add_fallback_cert_files(s, p, pks->cert_files, pks->key_files); + + pks->service_unavailable = 1; + ap_log_error(APLOG_MARK, APLOG_WARNING, 0, s, APLOGNO(10085) + "Init: %s will respond with '503 Service Unavailable' for now. This " + "has no SSL certificate configured and no other module contributed any.", + ssl_util_vhostid(p, s)); } if ((rv = ssl_init_ctx(s, p, ptemp, sc->server)) != APR_SUCCESS) { |