diff options
-rw-r--r-- | CHANGES | 11 | ||||
-rw-r--r-- | include/ap_mmn.h | 3 | ||||
-rw-r--r-- | include/http_protocol.h | 85 | ||||
-rw-r--r-- | modules/md/mod_md.c | 2 | ||||
-rw-r--r-- | modules/ssl/ssl_engine_init.c | 15 | ||||
-rw-r--r-- | modules/ssl/ssl_engine_kernel.c | 35 | ||||
-rw-r--r-- | modules/ssl/ssl_private.h | 3 | ||||
-rw-r--r-- | server/protocol.c | 36 |
8 files changed, 177 insertions, 13 deletions
@@ -23,6 +23,17 @@ Changes with Apache 2.5.1 server/connection/request. - Hooks for 'ssl_conn_is_ssl' and 'ssl_var_lookup' where modules providing SSL can install their own value supplying functions. + - ap_ssl_add_cert_files() to enable other modules like mod_md to provide + certificate and keys for an SSL module like mod_ssl. + - ap_ssl_add_fallback_cert_files() to enable other modules like mod_md to + provide a fallback certificate in case no 'proper' certificate is + available for an SSL module like mod_ssl. + - ap_ssl_answer_challenge() to enable other modules like mod_md to + provide a certificate as used in the RFC 8555 'tls-alpn-01' challenge + for the ACME protocol for an SSL module like mod_ssl. + - Hooks for 'ssl_add_cert_files', 'ssl_add_fallback_cert_files' and + 'ssl_answer_challenge' where modules like mod_md can provide providers + to the above mentioned functions. [Stefan Eissing] *) mod_http2: new option 'H2OutputBuffering on/off' which controls the diff --git a/include/ap_mmn.h b/include/ap_mmn.h index f6a6d2597a..1106c2f37e 100644 --- a/include/ap_mmn.h +++ b/include/ap_mmn.h @@ -665,6 +665,7 @@ * 20200705.4 (2.5.1-dev) Add ap_get_status_line_ex() * 20201214.0 (2.5.1-dev) Axe struct core_net_rec * 20201214.1 (2.5.1-dev) Add ap_ssl_conn_is_ssl()/ap_ssl_var_lookup() and hooks + * 20201214.2 (2.5.1-dev) Add ap_ssl_add_cert_files, ap_ssl_add_fallback_cert_files */ #define MODULE_MAGIC_COOKIE 0x41503235UL /* "AP25" */ @@ -672,7 +673,7 @@ #ifndef MODULE_MAGIC_NUMBER_MAJOR #define MODULE_MAGIC_NUMBER_MAJOR 20201214 #endif -#define MODULE_MAGIC_NUMBER_MINOR 1 /* 0...n */ +#define MODULE_MAGIC_NUMBER_MINOR 2 /* 0...n */ /** * Determine if the server's current MODULE_MAGIC_NUMBER is at least a diff --git a/include/http_protocol.h b/include/http_protocol.h index 5c57a85181..c4f064a7c8 100644 --- a/include/http_protocol.h +++ b/include/http_protocol.h @@ -1108,6 +1108,91 @@ AP_DECLARE(const char *) ap_ssl_var_lookup(apr_pool_t *p, server_rec *s, conn_rec *c, request_rec *r, const char *name); +/** + * Register to provide certificate/key files for servers. Certificate files are + * exepcted to contain the certificate chain, beginning with the server's certificate, + * excluding the trust anchor, in PEM format. + * They must be accompanied by a private key file, also in PEM format. + * + * @param s the server certificates are collected for + * @param p the pool to use for allocations + * @param cert_file and array of const char* with the path to the certificate chain + * @param key_file and array of const char* with the path to the private key file + * @return OK if files were added, DECLINED if not, or other for error. + */ + +AP_DECLARE_HOOK(int, ssl_add_cert_files, (server_rec *s, apr_pool_t *p, + apr_array_header_t *cert_files, + apr_array_header_t *key_files)) + +/** + * Collect certificate/key files from all providers registered. This includes + * providers registered at the global 'ssl_add_cert_files', as well as those + * installed in the OPTIONAL 'ssl_add_cert_files' hook as may be provided by + * ssl modules. + * + * @param s the server certificates are collected for + * @param p the pool to use for allocations + * @param cert_file and array of const char* with the path to the certificate chain + * @param key_file and array of const char* with the path to the private key file + */ +AP_DECLARE(apr_status_t) ap_ssl_add_cert_files(server_rec *s, apr_pool_t *p, + apr_array_header_t *cert_files, + apr_array_header_t *key_files); + + +/** + * Register to provide 'fallback' certificates in case no 'real' certificates + * have been configured/added by other providers. Modules using these certificates + * are encouraged to answer requests to this server with a 503 response code. + * + * @param s the server certificates are collected for + * @param p the pool to use for allocations + * @param cert_file and array of const char* with the path to the certificate chain + * @param key_file and array of const char* with the path to the private key file + * @return OK if files were added, DECLINED if not, or other for error. + */ +AP_DECLARE_HOOK(int, ssl_add_fallback_cert_files, (server_rec *s, apr_pool_t *p, + apr_array_header_t *cert_files, + apr_array_header_t *key_files)) + +/** + * Collect 'fallback' certificate/key files from all registered providers, either + * in the global 'ssl_add_fallback_cert_files' hook or the optional one of similar + * name as provided by mod_ssl and sorts. + * Certificates obtained this way are commonly self signed, temporary crutches. + * To be used to the time it takes to retrieve a 'read', trusted certificate. + * A module using fallbacks is encouraged to answer all requests with a 503. + * + * @param s the server certificates are collected for + * @param p the pool to use for allocations + * @param cert_file and array of const char* with the path to the certificate chain + * @param key_file and array of const char* with the path to the private key file + */ +AP_DECLARE(apr_status_t) ap_ssl_add_fallback_cert_files(server_rec *s, apr_pool_t *p, + apr_array_header_t *cert_files, + apr_array_header_t *key_files); + + +/** + * On TLS connections that do not relate to a configured virtual host, + * allow modules to provide a certificate and key to + * be used on the connection. + */ +AP_DECLARE_HOOK(int, ssl_answer_challenge, (conn_rec *c, const char *server_name, + const char **pcert_file, const char **pkey_file)) + +/** + * Returns != 0 iff the connection is a challenge to the server, for example + * as defined in RFC 8555 for the 'tls-alpn-01' domain verification, and needs + * a specific certificate as answer in the handshake. + * ALPN protocol negotiation via the hooks 'protocol_propose' and 'protocol_switch' + * need to have run before this call is made. + */ +AP_DECLARE(int) ap_ssl_answer_challenge(conn_rec *c, const char *server_name, + const char **pcert_file, const char **pkey_file); + + #ifdef __cplusplus } #endif diff --git a/modules/md/mod_md.c b/modules/md/mod_md.c index 55413f5b67..dac239527f 100644 --- a/modules/md/mod_md.c +++ b/modules/md/mod_md.c @@ -1237,7 +1237,7 @@ static int md_answer_challenge(conn_rec *c, const char *servername, X509 **pcert, EVP_PKEY **pkey) { if (md_is_challenge(c, servername, pcert, pkey)) { - return APR_SUCCESS; + return OK; } return DECLINED; } diff --git a/modules/ssl/ssl_engine_init.c b/modules/ssl/ssl_engine_init.c index bec4d7b255..3dbdda6544 100644 --- a/modules/ssl/ssl_engine_init.c +++ b/modules/ssl/ssl_engine_init.c @@ -198,13 +198,18 @@ static void ssl_add_version_components(apr_pool_t *ptemp, apr_pool_t *pconf, */ int ssl_is_challenge(conn_rec *c, const char *servername, - X509 **pcert, EVP_PKEY **pkey) + X509 **pcert, EVP_PKEY **pkey, + const char **pcert_file, const char **pkey_file) { - if (APR_SUCCESS == ssl_run_answer_challenge(c, servername, pcert, pkey)) { - return 1; - } *pcert = NULL; *pkey = NULL; + *pcert_file = *pkey_file = NULL; + if (ap_ssl_answer_challenge(c, servername, pcert_file, pkey_file)) { + return 1; + } + else if (OK == ssl_run_answer_challenge(c, servername, pcert, pkey)) { + return 1; + } return 0; } @@ -1973,10 +1978,12 @@ static apr_status_t ssl_init_server_ctx(server_rec *s, /* Allow others to provide certificate files */ pks = sc->server->pks; n = pks->cert_files->nelts; + ap_ssl_add_cert_files(s, p, pks->cert_files, pks->key_files); ssl_run_add_cert_files(s, p, pks->cert_files, pks->key_files); if (apr_is_empty_array(pks->cert_files)) { /* does someone propose a certiciate to fall back on here? */ + ap_ssl_add_fallback_cert_files(s, p, pks->cert_files, pks->key_files); ssl_run_add_fallback_cert_files(s, p, pks->cert_files, pks->key_files); if (n < pks->cert_files->nelts) { pks->service_unavailable = 1; diff --git a/modules/ssl/ssl_engine_kernel.c b/modules/ssl/ssl_engine_kernel.c index 0b22c2de28..9306ec07a2 100644 --- a/modules/ssl/ssl_engine_kernel.c +++ b/modules/ssl/ssl_engine_kernel.c @@ -2316,11 +2316,29 @@ void ssl_callback_Info(const SSL *ssl, int where, int rc) #ifdef HAVE_TLSEXT static apr_status_t set_challenge_creds(conn_rec *c, const char *servername, - SSL *ssl, X509 *cert, EVP_PKEY *key) + SSL *ssl, X509 *cert, EVP_PKEY *key, + const char *cert_file, const char *key_file) { SSLConnRec *sslcon = myConnConfig(c); sslcon->service_unavailable = 1; + if (cert_file) { + if (SSL_use_certificate_chain_file(ssl, cert_file) < 1) { + ap_log_cerror(APLOG_MARK, APLOG_WARNING, 0, c, APLOGNO() + "Failed to configure challenge certificate %s", + servername); + return APR_EGENERAL; + } + if (key_file == NULL) key_file = cert_file; + if (SSL_use_PrivateKey_file(ssl, key_file, SSL_FILETYPE_PEM) < 1) { + ap_log_cerror(APLOG_MARK, APLOG_WARNING, 0, c, APLOGNO() + "Failed to configure challenge private key %s", + servername); + return APR_EGENERAL; + } + goto check; + } + if ((SSL_use_certificate(ssl, cert) < 1)) { ap_log_cerror(APLOG_MARK, APLOG_WARNING, 0, c, APLOGNO(10086) "Failed to configure challenge certificate %s", @@ -2336,6 +2354,7 @@ static apr_status_t set_challenge_creds(conn_rec *c, const char *servername, return APR_EGENERAL; } +check: if (SSL_check_private_key(ssl) < 1) { ap_log_cerror(APLOG_MARK, APLOG_WARNING, 0, c, APLOGNO(10088) "Challenge certificate and private key %s " @@ -2353,6 +2372,7 @@ static apr_status_t init_vhost(conn_rec *c, SSL *ssl, const char *servername) { X509 *cert; EVP_PKEY *key; + const char *cert_file, *key_file; if (c) { SSLConnRec *sslcon = myConnConfig(c); @@ -2376,11 +2396,12 @@ static apr_status_t init_vhost(conn_rec *c, SSL *ssl, const char *servername) sslcon->vhost_found = +1; return APR_SUCCESS; } - else if (ssl_is_challenge(c, servername, &cert, &key)) { + else if (ssl_is_challenge(c, servername, &cert, &key, &cert_file, &key_file)) { /* With ACMEv1 we can have challenge connections to a unknown domains * that need to be answered with a special certificate and will * otherwise not answer any requests. */ - if (set_challenge_creds(c, servername, ssl, cert, key) != APR_SUCCESS) { + if (set_challenge_creds(c, servername, ssl, cert, key, + cert_file, key_file) != APR_SUCCESS) { return APR_EGENERAL; } SSL_set_verify(ssl, SSL_VERIFY_NONE, ssl_callback_SSLVerify); @@ -2771,9 +2792,11 @@ int ssl_callback_alpn_select(SSL *ssl, const char *servername = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name); X509 *cert; EVP_PKEY *key; - - if (ssl_is_challenge(c, servername, &cert, &key)) { - if (set_challenge_creds(c, servername, ssl, cert, key) != APR_SUCCESS) { + const char *cert_file, *key_file; + + if (ssl_is_challenge(c, servername, &cert, &key, &cert_file, &key_file)) { + if (set_challenge_creds(c, servername, ssl, cert, key, + cert_file, key_file) != APR_SUCCESS) { return SSL_TLSEXT_ERR_ALERT_FATAL; } SSL_set_verify(ssl, SSL_VERIFY_NONE, ssl_callback_SSLVerify); diff --git a/modules/ssl/ssl_private.h b/modules/ssl/ssl_private.h index 1fd6b7bb72..417105d387 100644 --- a/modules/ssl/ssl_private.h +++ b/modules/ssl/ssl_private.h @@ -1169,7 +1169,8 @@ extern int ssl_running_on_valgrind; #endif int ssl_is_challenge(conn_rec *c, const char *servername, - X509 **pcert, EVP_PKEY **pkey); + X509 **pcert, EVP_PKEY **pkey, + const char **pcert_file, const char **pkey_file); /* Set the renegotation state for connection. */ void modssl_set_reneg_state(SSLConnRec *sslconn, modssl_reneg_state state); diff --git a/server/protocol.c b/server/protocol.c index afd76aa07c..4ce2b6172a 100644 --- a/server/protocol.c +++ b/server/protocol.c @@ -72,6 +72,9 @@ APR_HOOK_STRUCT( APR_HOOK_LINK(protocol_get) APR_HOOK_LINK(ssl_conn_is_ssl) APR_HOOK_LINK(ssl_var_lookup) + APR_HOOK_LINK(ssl_add_cert_files) + APR_HOOK_LINK(ssl_add_fallback_cert_files) + APR_HOOK_LINK(ssl_answer_challenge) ) AP_DECLARE_DATA ap_filter_rec_t *ap_old_write_func = NULL; @@ -2697,6 +2700,27 @@ AP_DECLARE(void) ap_setup_ssl_optional_fns(apr_pool_t *pool) APR_REGISTER_OPTIONAL_FN(ssl_var_lookup); } +AP_DECLARE(apr_status_t) ap_ssl_add_cert_files(server_rec *s, apr_pool_t *p, + apr_array_header_t *cert_files, + apr_array_header_t *key_files) +{ + int rv = ap_run_ssl_add_cert_files(s, p, cert_files, key_files); + return (rv == OK || rv == DECLINED)? APR_SUCCESS : APR_EGENERAL; +} + +AP_DECLARE(apr_status_t) ap_ssl_add_fallback_cert_files(server_rec *s, apr_pool_t *p, + apr_array_header_t *cert_files, + apr_array_header_t *key_files) +{ + int rv = ap_run_ssl_add_fallback_cert_files(s, p, cert_files, key_files); + return (rv == OK || rv == DECLINED)? APR_SUCCESS : APR_EGENERAL; +} + +AP_DECLARE(int) ap_ssl_answer_challenge(conn_rec *c, const char *server_name, + const char **pcert_file, const char **pkey_file) +{ + return (ap_run_ssl_answer_challenge(c, server_name, pcert_file, pkey_file) == OK); +} AP_IMPLEMENT_HOOK_VOID(pre_read_request, (request_rec *r, conn_rec *c), @@ -2728,3 +2752,15 @@ AP_IMPLEMENT_HOOK_RUN_FIRST(int, ssl_conn_is_ssl, AP_IMPLEMENT_HOOK_RUN_FIRST(const char *,ssl_var_lookup, (apr_pool_t *p, server_rec *s, conn_rec *c, request_rec *r, const char *name), (p, s, c, r, name), NULL) +AP_IMPLEMENT_HOOK_RUN_ALL(int, ssl_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) +AP_IMPLEMENT_HOOK_RUN_ALL(int, ssl_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) +AP_IMPLEMENT_HOOK_RUN_FIRST(int, ssl_answer_challenge, + (conn_rec *c, const char *server_name, const char **pcert_file, const char **pkey_file), + (c, server_name, pcert_file, pkey_file), DECLINED) + |