libmicrohttpd2

HTTP server C library (MHD 2.x, alpha)
Log | Files | Refs | README | LICENSE

commit db4354649a0a483d44fb979f06e6d40c55b49edd
parent 03978499e6055c7e416108a3993441f1a33fe7a6
Author: Evgeny Grin (Karlson2k) <k2k@drgrin.dev>
Date:   Wed, 19 Aug 2026 18:47:22 +0200

Added ACME ALPN challenge support for OpenSSL backend

Diffstat:
Msrc/incl_priv/mhd_sys_options.h | 32+++++++++++++++++++++++++++++++-
Msrc/mhd2/tls_open_conn_data.h | 13+++++++++++++
Msrc/mhd2/tls_open_daemon_data.h | 26++++++++++++++++++++++++++
Msrc/mhd2/tls_open_funcs.c | 966++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
Msrc/mhd2/tls_open_funcs.h | 84++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----
5 files changed, 1111 insertions(+), 10 deletions(-)

diff --git a/src/incl_priv/mhd_sys_options.h b/src/incl_priv/mhd_sys_options.h @@ -721,6 +721,33 @@ && ! MHD_FORCE_USE_GNUTLS_MHD_CLIENTHELLO_BODY_PARSE */ # endif /* HAVE_GNUTLS_HANDSHAKE_SET_HOOK_FUNCTION */ # endif /* MHD_SUPPORT_GNUTLS */ +# ifdef MHD_SUPPORT_OPENSSL +# ifndef MHD_NO_OPENSSL_ACME_ALPN +/** + * Indicate that ACME ALPN challenge support is available in the OpenSSL backend + */ +# define mhd_HAVE_OPENSSL_ACME 1 +/* define MHD_FORCE_USE_MHD_CLIENTHELLO_BODY_PARSE to always use MHD internal + parser for ClientHello body instead of all TLS backend parsers */ +/* define MHD_FORCE_USE_OPENSSL_MHD_CLIENTHELLO_BODY_PARSE to use MHD internal + parser for ClientHello body instead of OpenSSL parser */ +# if defined(MHD_FORCE_USE_MHD_CLIENTHELLO_BODY_PARSE) \ + && !defined(MHD_FORCE_USE_OPENSSL_MHD_CLIENTHELLO_BODY_PARSE) +/** + * Use MHD internal parser for ClientHello body instead of OpenSSL parser + */ +# define MHD_FORCE_USE_OPENSSL_MHD_CLIENTHELLO_BODY_PARSE 1 +# endif +# ifndef MHD_FORCE_USE_OPENSSL_MHD_CLIENTHELLO_BODY_PARSE +# define mhd_USE_OPENSSL_CLIENT_HELLO_GET0_EXT 1 +# endif /* ! MHD_FORCE_USE_OPENSSL_MHD_CLIENTHELLO_BODY_PARSE */ +# ifndef mhd_USE_OPENSSL_CLIENT_HELLO_GET0_EXT +# ifndef mhd_HAVE_TLS_MHD_CLIENTHELLO_BODY_PARSE +# define mhd_HAVE_TLS_MHD_CLIENTHELLO_BODY_PARSE 1 +# endif /* ! mhd_HAVE_TLS_MHD_CLIENTHELLO_BODY_PARSE */ +# endif /* MHD_FORCE_USE_OPENSSL_MHD_CLIENTHELLO_BODY_PARSE */ +# endif /* MHD_NO_OPENSSL_ACME_ALPN */ +# endif /* MHD_SUPPORT_OPENSSL */ # else /* ! MHD_SUPPORT_ACME */ # ifdef MHD_FORCE_USE_MHD_CLIENTHELLO_BODY_PARSE # undef MHD_FORCE_USE_MHD_CLIENTHELLO_BODY_PARSE @@ -728,11 +755,14 @@ # ifdef MHD_FORCE_USE_GNUTLS_MHD_CLIENTHELLO_BODY_PARSE # undef MHD_FORCE_USE_GNUTLS_MHD_CLIENTHELLO_BODY_PARSE # endif +# ifdef MHD_FORCE_USE_OPENSSL_MHD_CLIENTHELLO_BODY_PARSE +# undef MHD_FORCE_USE_OPENSSL_MHD_CLIENTHELLO_BODY_PARSE +# endif # endif /* ! MHD_SUPPORT_ACME */ #endif /* MHD_SUPPORT_HTTPS */ #if defined(MHD_SUPPORT_HTTPS) && defined(MHD_SUPPORT_ACME) && \ - defined(mhd_HAVE_GNUTLS_ACME) + (defined(mhd_HAVE_GNUTLS_ACME) || defined(mhd_HAVE_OPENSSL_ACME)) /** * Have support for ACME ALPN challenge * @warning If MHD_SUPPORT_ACME is enabled but this macros is not defined then diff --git a/src/mhd2/tls_open_conn_data.h b/src/mhd2/tls_open_conn_data.h @@ -83,6 +83,19 @@ struct mhd_TlsOpenConnData * 'true' if received EOF (the remote side initiated shutting down) */ bool shut_tls_wr_received; + +#ifdef mhd_HAVE_OPENSSL_ACME + /** + * 'true' if the connection is used for ACME ALPN challenge + */ + bool is_acme; + /** + * 'true' if the ClientHello message has been processed + * Used mainly for detection of second ClientHello after TLS 1.3 HRR + */ + bool clienthello_processed; +#endif /* mhd_HAVE_OPENSSL_ACME */ + #ifndef NDEBUG /** * Debugging data diff --git a/src/mhd2/tls_open_daemon_data.h b/src/mhd2/tls_open_daemon_data.h @@ -54,6 +54,25 @@ #include "tls_open_tls_lib.h" /** + * The OpenSSL credentials data + */ +struct mhd_TlsOpenCredData +{ + /** + * The certificate object + */ + X509 *cert; + /** + * The private key object + */ + EVP_PKEY *key; + /** + * The intermediate certificates chain object + */ + STACK_OF (X509) * chain; +}; + +/** * The structure with daemon-specific OpenSSL data */ struct mhd_TlsOpenDaemonData @@ -79,6 +98,13 @@ struct mhd_TlsOpenDaemonData * The size of the data pointed by @a alpn_prots */ unsigned int alpn_prots_size; + +#ifdef mhd_HAVE_OPENSSL_ACME + /** + * The list of ACME ALPN certificates + */ + struct mhd_TlsCertsList *acme_certs; +#endif /* mhd_HAVE_OPENSSL_ACME */ }; #endif /* ! MHD_TLS_OPEN_DAEMON_DATA_H */ diff --git a/src/mhd2/tls_open_funcs.c b/src/mhd2/tls_open_funcs.c @@ -58,8 +58,10 @@ #include "mhd_assert.h" #include "mhd_unreachable.h" #include "mhd_assume.h" +#include "mhd_predict.h" #include "mhd_str.h" +#include "mhd_str_types.h" #include "mhd_conn_socket.h" #include "mhd_tls_internal.h" @@ -76,6 +78,10 @@ #include "daemon_logger.h" +#ifdef mhd_HAVE_OPENSSL_ACME +# include "mhd_tls_acme_func.h" +#endif /* mhd_HAVE_OPENSSL_ACME */ + #include "microhttpd2_portability.h" #include "mhd_public_api.h" @@ -131,6 +137,16 @@ mhd_tls_open_dbg_print_errs (const char *msg, static bool openssl_lib_inited = false; +#ifdef mhd_HAVE_OPENSSL_ACME +/** + * The index of the pointer to MHD connection TLS data in the "ex_data" storage + * of the TLS session. + * This leaves the "app_data" (index zero of the "ex_data" storage) for other + * possible uses. + */ +static int conn_mhd_ctls_idx = -1; +#endif /* mhd_HAVE_OPENSSL_ACME */ + MHD_INTERNAL void mhd_tls_open_global_init_once (void) { @@ -146,6 +162,18 @@ mhd_tls_open_global_init_once (void) this call would make sure that the library is initialised before used. */ openssl_lib_inited = openssl_lib_inited && (0 < OPENSSL_init_ssl (0, NULL)); + +#ifdef mhd_HAVE_OPENSSL_ACME + if (openssl_lib_inited) + { + conn_mhd_ctls_idx = SSL_get_ex_new_index (0, + NULL, + NULL, + NULL, + NULL); + openssl_lib_inited = (0 <= conn_mhd_ctls_idx); + } +#endif /* mhd_HAVE_OPENSSL_ACME */ } @@ -820,6 +848,11 @@ daemon_deinit_lib_ctx (struct mhd_TlsOpenDaemonData *restrict d_tls) # define mhd_ALPN_CODE_HTTP3 \ 'h', '3' /* Registered value for HTTP/3 */ #endif /* Disabled code */ +#ifdef mhd_HAVE_OPENSSL_ACME +/* Registered value for ACME TLS-ALPN-01 challenge */ +# define mhd_ALPN_CODE_ACME \ + 'a', 'c', 'm', 'e', '-', 't', 'l', 's', '/', '1' +#endif /* mhd_HAVE_OPENSSL_ACME */ #ifdef MHD_SUPPORT_HTTP2 @@ -839,6 +872,13 @@ static const unsigned char alpn_list_http1x_only[] = { , mhd_ALPN_H1_0_LEN, mhd_ALPN_CODE_HTTP1_0 }; +#ifdef mhd_HAVE_OPENSSL_ACME +/* RFC 8737, section 3: the ACME TLS-ALPN-01 challenge is answered with + the "acme-tls/1" protocol and no other protocol */ +static const unsigned char alpn_list_acme_only[] = { + mhd_ALPN_ACME_LEN, mhd_ALPN_CODE_ACME +}; +#endif /* mhd_HAVE_OPENSSL_ACME */ #ifndef OPENSSL_NO_NEXTPROTONEG /** @@ -868,7 +908,7 @@ get_npn_list (SSL *sess, /** * Select protocol from the provided list for ALPN extension - * @param sess the TLS session (ignored) + * @param sess the TLS session * @param[out] out the pointer to get the location of selected protocol value * @param[out] outlen the size of the selected protocol value * @param in the list of protocols values provided by the client @@ -885,9 +925,32 @@ select_alpn_prot (SSL *sess, unsigned int inlen, void *cls) { - struct mhd_TlsOpenDaemonData *const d_tls = + struct mhd_TlsOpenDaemonData *const restrict d_tls = (struct mhd_TlsOpenDaemonData *)cls; - (void)sess; /* Unused */ + +#ifdef mhd_HAVE_OPENSSL_ACME + if (!0) + { + const struct mhd_TlsOpenConnData *const restrict c_tls = + (const struct mhd_TlsOpenConnData *)SSL_get_ex_data (sess, + conn_mhd_ctls_idx); + mhd_assert (NULL != c_tls); + + if (c_tls->is_acme) + { + /* ALPN has been checked: ACME TLS-ALPN-01 only */ + mhd_assert (inlen == (unsigned int)sizeof(alpn_list_acme_only)); + mhd_assert (0 == memcmp (in, alpn_list_acme_only, inlen)); + + *out = alpn_list_acme_only + 1; /* Skip the length byte */ + *outlen = (unsigned char)mhd_ALPN_ACME_LEN; + return SSL_TLSEXT_ERR_OK; /* Success */ + } + } +#else /* ! mhd_HAVE_OPENSSL_ACME */ + (void)sess; /* Unused */ +#endif /* ! mhd_HAVE_OPENSSL_ACME */ + if (OPENSSL_NPN_NEGOTIATED == SSL_select_next_proto ((unsigned char **)mhd_DROP_CONST (out), outlen, @@ -901,6 +964,444 @@ select_alpn_prot (SSL *sess, } +#ifdef mhd_USE_OPENSSL_CLIENT_HELLO_GET0_EXT +# ifndef mhd_HAVE_OPENSSL_ACME +# error mhd_USE_OPENSSL_CLIENT_HELLO_GET0_EXT requires mhd_HAVE_OPENSSL_ACME +# endif /* ! mhd_HAVE_OPENSSL_ACME */ +#endif /* mhd_USE_OPENSSL_CLIENT_HELLO_GET0_EXT */ + +#ifdef mhd_HAVE_OPENSSL_ACME + +/** + * The TLS session ID context for the ACME ALPN challenge connections. + * The value is not interpreted by the TLS library, it only has to differ from + * the context used for the normal connections, which is the empty default one. + */ +static const unsigned char acme_sid_ctx[] = { 'A', 'C', 'M', 'E' }; + + +/** + * Callback to prevent storing of the ACME ALPN challenge session + * @param sess the TLS session (unused) + * @param is_forward_secure set to non-zero if the session is forward secure + * (unused) + * @return always non-zero, the session must not be stored + */ +static int +acme_sess_not_resumable (SSL *sess, + int is_forward_secure) +{ + (void)sess; /* Unused */ + (void)is_forward_secure; /* Unused */ + return !0; +} + + +/** + * Exclude the connection from the TLS sessions resumption. + * + * The ACME certificate is set for the single connection, while a resumed + * handshake sends no certificate at all and the client keeps the certificate + * of the original session. Therefore both directions must be blocked: + * the session of this connection must not be stored, so that no normal + * connection could resume it and get the ACME certificate, and this connection + * must not resume any session stored for the normal connections, as it would + * get the daemon certificate instead of the ACME certificate. + * + * The session ID context must be set before the TLS library looks up + * the session requested by the client, the callback preventing the storing is + * used later. + * + * @param sess the TLS session to exclude from the sessions resumption + * @return 'true' on success, + * 'false' otherwise + */ +static MHD_FN_PAR_NONNULL_ALL_ MHD_FN_MUST_CHECK_RESULT_ bool +mark_acme_no_resume (SSL *sess) +{ + /* Any stored session has another session ID context, which is treated by + the TLS library as a cache miss */ + if (0 == SSL_set_session_id_context (sess, + acme_sid_ctx, + (unsigned int)sizeof(acme_sid_ctx))) + return false; + + SSL_set_not_resumable_session_callback (sess, + &acme_sess_not_resumable); + return true; +} + + +/** + * Result of the application of the ACME ALPN challenge check to a connection + */ +enum mhd_TlsOpenConnAcmeCheckFinishResult +{ + /** + * The connection is prepared and the handshake can be continued, either as + * a normal HTTPS connection or as an ACME ALPN challenge connection + */ + mhd_TLS_OPEN_ACME_CHECK_FINISH_OK = 0, + /** + * The TLS library failed to use the ACME credentials for the connection. + * The handshake must be aborted, the "internal error" alert matches + * the failure. + */ + mhd_TLS_OPEN_ACME_CHECK_FINISH_ERROR, + /** + * The second ClientHello message, received after TLS 1.3 HelloRetryRequest, + * has been recognised differently than the first one. + * The handshake must be aborted, the "illegal parameter" alert matches + * the failure. + */ + mhd_TLS_OPEN_ACME_CHECK_FINISH_MISMATCH_HRR +}; + + +/** + * Apply the result of the ACME ALPN challenge check to the connection. + * + * If the ClientHello message has been recognised as the challenge, the ACME + * certificate, the private key and the certificates chain are set for + * the connection and the connection is excluded from the TLS sessions + * resumption. Otherwise only the state of the connection is updated, as + * the ALPN protocols for a normal HTTPS connection are set for the TLS + * context. + * + * The function must be called for every checked ClientHello message, + * including the second one received after TLS 1.3 HelloRetryRequest. + * + * @param[in,out] c_tls the connection TLS handle + * @param acme_certs the list of the ACME certificates used for the check + * @param acme_cred the credentials selected by the check, + * or NULL if the ClientHello message is not + * an ACME ALPN challenge + * @return #mhd_TLS_OPEN_ACME_CHECK_FINISH_OK if the handshake can be + * continued, + * other values if the handshake must be aborted + * @warning If @p acme_cred is not NULL, the list of the ACME certificates is + * read-locked by the check and is unlocked by this function + */ +static MHD_FN_MUST_CHECK_RESULT_ MHD_FN_PAR_NONNULL_ (1) +MHD_FN_PAR_NONNULL_ (2) enum mhd_TlsOpenConnAcmeCheckFinishResult +mhd_tls_open_conn_acme_check_finish ( + struct mhd_TlsOpenConnData *restrict c_tls, + struct mhd_TlsCertsList *restrict acme_certs, + const union mhd_TlsCredDataPtr *restrict acme_cred) +{ + enum mhd_TlsOpenConnAcmeCheckFinishResult res; + + mhd_assert (NULL != c_tls); + + if (NULL == acme_cred) + { + /* Normal (not ACME ALPN challenge) connection */ + if (c_tls->is_acme) /* Second ClientHello after TLS 1.3 HRR with another result */ + { + mhd_assert (c_tls->clienthello_processed); + c_tls->is_acme = false; /* Reset to non-ACME connection */ + return mhd_TLS_OPEN_ACME_CHECK_FINISH_MISMATCH_HRR; /* Unmatched ClientHello, abort handshake */ + } + + /* Process the connection as a normal HTTPS connection. */ + c_tls->clienthello_processed = true; + return mhd_TLS_OPEN_ACME_CHECK_FINISH_OK; /* Non-ACME connection exit point */ + } + + /* Assume OK unless the checks below fail */ + res = mhd_TLS_OPEN_ACME_CHECK_FINISH_OK; + + if (c_tls->clienthello_processed && !c_tls->is_acme) + res = mhd_TLS_OPEN_ACME_CHECK_FINISH_MISMATCH_HRR; + + if (mhd_TLS_OPEN_ACME_CHECK_FINISH_OK == res) + { + struct mhd_TlsOpenCredData *const restrict acme_open_cred = + acme_cred->open; + + mhd_assert (NULL != acme_open_cred); + + ERR_clear_error (); + /* Remove the daemon certificate: the certificate of another key type + would not be replaced by the ACME certificate and could be selected + by the TLS library instead of it. */ + SSL_certs_clear (c_tls->sess); + /* The TLS library takes its own references for the objects, therefore + the ACME credentials could be removed by the application at any moment + after this point */ + if (!mark_acme_no_resume (c_tls->sess) + || (0 >= SSL_use_certificate (c_tls->sess, + acme_open_cred->cert)) + || (0 >= SSL_set1_chain (c_tls->sess, + acme_open_cred->chain)) + || (0 >= SSL_use_PrivateKey (c_tls->sess, + acme_open_cred->key))) + { + mhd_DBG_PRINT_TLS_ERRS (); + res = mhd_TLS_OPEN_ACME_CHECK_FINISH_ERROR; + } + } + mhd_daemon_acme_cert_r_unlock (acme_certs); + + c_tls->clienthello_processed = true; + c_tls->is_acme = (mhd_TLS_OPEN_ACME_CHECK_FINISH_OK == res); + return res; +} + + +# ifdef mhd_USE_OPENSSL_CLIENT_HELLO_GET0_EXT + + +/** + * Check whether the ClientHello message is the ACME TLS-ALPN-01 challenge and + * select the ACME credentials for the indicated domain. + * + * The extensions are taken from the ClientHello message parsed by the TLS + * library. The TLS library rejects any duplicated extension before this + * function is called. + * + * @param sess the TLS session processing the ClientHello message + * @param acme_certs the list of the ACME certificates to check against + * @return non-NULL if the ClientHello message is recognised as a TLS-ALPN-01 + * challenge and the ACME credentials are selected, + * NULL otherwise + * @warning If a non-NULL pointer is returned, the daemon's ACME certificate + * list remains read-locked and must be unlocked with + * #mhd_daemon_acme_cert_r_unlock(). + */ +static MHD_FN_MUST_CHECK_RESULT_ MHD_FN_PAR_NONNULL_ALL_ +const union mhd_TlsCredDataPtr * +clnthello_check_acme (SSL *sess, + struct mhd_TlsCertsList *restrict acme_certs) +{ + struct mhd_TlsClientHelloAcmeCheckData check; + bool all_checks_ok; + + mhd_tls_acme_check_ext_init (&check, + acme_certs); + + all_checks_ok = true; + + /* The ALPN extension is checked first as it is checked without locking + the list of the ACME certificates. Any typical HTTPS ClientHello is + rejected by this check alone. */ + if (all_checks_ok) + { + static const unsigned int ext_alpn_id = + TLSEXT_TYPE_application_layer_protocol_negotiation; + const unsigned char *ext_alpn_data; + size_t ext_alpn_size; + + all_checks_ok = (0 != SSL_client_hello_get0_ext (sess, + ext_alpn_id, + &ext_alpn_data, + &ext_alpn_size)); + if (all_checks_ok) + all_checks_ok = mhd_tls_acme_check_ext (ext_alpn_id, + ext_alpn_size, + ext_alpn_data, + &check); + } + + if (all_checks_ok) + { + static const unsigned int ext_sni_id = TLSEXT_TYPE_server_name; + const unsigned char *ext_sni_data; + size_t ext_sni_size; + + all_checks_ok = (0 != SSL_client_hello_get0_ext (sess, + ext_sni_id, + &ext_sni_data, + &ext_sni_size)); + if (all_checks_ok) + all_checks_ok = mhd_tls_acme_check_ext (ext_sni_id, + ext_sni_size, + ext_sni_data, + &check); + } + + /* The TLS library parsed the complete ClientHello message before calling + this function, no need to make additional check for parse completeness */ + return mhd_tls_acme_check_ext_finish (&check, + all_checks_ok); +} + + +/** + * The TLS library callback for checking the ClientHello message for + * the ACME ALPN challenge. + * + * The callback is registered for the TLS context as the TLS library has no + * session-specific version of this callback. + * + * @param sess the TLS session processing the ClientHello message + * @param[out] alert the TLS alert to send if the handshake is aborted + * @param cls the closure, the pointer to the daemon TLS settings + * @return #SSL_CLIENT_HELLO_SUCCESS for a normal HTTPS ClientHello or after + * successful ACME setup, + * #SSL_CLIENT_HELLO_ERROR to abort the handshake otherwise + */ +static int +check_clnt_hello_cb (SSL *sess, + int *alert, + void *cls) +{ + struct mhd_TlsOpenDaemonData *const restrict d_tls = + (struct mhd_TlsOpenDaemonData *)cls; + struct mhd_TlsOpenConnData *const restrict c_tls = + (struct mhd_TlsOpenConnData *)SSL_get_ex_data (sess, + conn_mhd_ctls_idx); + const union mhd_TlsCredDataPtr *acme_cred; /* Set to non-NULL if request is recognised ACME challenge */ + enum mhd_TlsOpenConnAcmeCheckFinishResult res; + + mhd_assert (NULL != c_tls); + mhd_assert (c_tls->sess == sess); + + acme_cred = NULL; + if (mhd_daemon_has_acme_certs (d_tls->acme_certs)) + acme_cred = clnthello_check_acme (sess, + d_tls->acme_certs); + + res = mhd_tls_open_conn_acme_check_finish (c_tls, + d_tls->acme_certs, + acme_cred); + + switch (res) + { + case mhd_TLS_OPEN_ACME_CHECK_FINISH_OK: + return SSL_CLIENT_HELLO_SUCCESS; /* Success exit point */ + + case mhd_TLS_OPEN_ACME_CHECK_FINISH_ERROR: + *alert = SSL_AD_INTERNAL_ERROR; + break; + + case mhd_TLS_OPEN_ACME_CHECK_FINISH_MISMATCH_HRR: + *alert = SSL_AD_ILLEGAL_PARAMETER; + break; + + default: + mhd_UNREACHABLE (); + *alert = SSL_AD_INTERNAL_ERROR; + break; + } + + return SSL_CLIENT_HELLO_ERROR; /* Failure exit point */ +} + + +# else /* ! mhd_USE_OPENSSL_CLIENT_HELLO_GET0_EXT */ + +/** + * Callback to abort the TLS handshake. + * + * The messages callback cannot report any failure, therefore this callback is + * set by the messages callback when the handshake must be aborted. The TLS + * library calls it after the ClientHello message has been processed and + * before the response is constructed. + * + * @param sess the TLS session (unused) + * @param cls the closure (unused) + * @return always zero, the handshake is aborted with the "internal error" + * alert + */ +static int +cert_cb_always_fail (SSL *sess, + void *cls) +{ + (void)sess; /* Unused */ + (void)cls; /* Unused */ + return 0; +} + + +/** + * The TLS library callback for checking the ClientHello message for + * the ACME ALPN challenge. + * + * The callback is set for the session, but only when the daemon has any ACME + * certificate. The body of the ClientHello message is parsed by MHD. + * + * The callback removes itself as soon as no ClientHello message can be + * received on the connection any more. + * + * @param write_p zero if the message is received, non-zero if it is sent + * @param version the TLS protocol version (unused) + * @param content_type the type of the record carrying the message + * @param buf the message data, including the handshake header + * @param len the size of the data pointed by @a buf + * @param sess the TLS session processing the message + * @param cls the closure, the list of the ACME certificates + */ +static void +check_hello_msg_acme (int write_p, + int version, + int content_type, + const void *buf, + size_t len, + SSL *sess, + void *cls) +{ + struct mhd_TlsCertsList *const restrict acme_certs = + (struct mhd_TlsCertsList *)cls; + struct mhd_TlsOpenConnData *const restrict c_tls = + (struct mhd_TlsOpenConnData *)SSL_get_ex_data (sess, + conn_mhd_ctls_idx); + const unsigned char *const restrict msg = (const unsigned char *)buf; + const union mhd_TlsCredDataPtr *acme_cred; /* Set to non-NULL if request is recognised ACME challenge */ + + (void)version; /* Unused */ + mhd_assert (NULL != c_tls); + mhd_assert (c_tls->sess == sess); + + if ((0 != write_p) || (SSL3_RT_HANDSHAKE != content_type)) + return; /* Not an incoming handshake message */ + + /* The first byte of the handshake header is the type of the message, + the body of the message follows the header */ + if ((((size_t)SSL3_HM_HEADER_LENGTH) >= len) + || (SSL3_MT_CLIENT_HELLO != msg[0])) + { + /* The client has sent another handshake message, no ClientHello can be + received on this connection any more: TLS1.3 has no renegotiation and + TLS1.2 renegotiation is rejected by #SSL_OP_NO_RENEGOTIATION */ + SSL_set_msg_callback (sess, + NULL); + return; + } + + /* If this is the second ClientHello then stop the callback, + the TLS library accepts no more ClientHello */ + if (c_tls->clienthello_processed) + SSL_set_msg_callback (sess, + NULL); + + acme_cred = + mhd_tls_acme_check_clienthello_body (len - SSL3_HM_HEADER_LENGTH, + msg + SSL3_HM_HEADER_LENGTH, + acme_certs); + + if (mhd_TLS_OPEN_ACME_CHECK_FINISH_OK != + mhd_tls_open_conn_acme_check_finish (c_tls, + acme_certs, + acme_cred)) + { + /* This callback cannot abort the handshake, the abort is performed by + the certificates callback after the ClientHello message is processed. + The alert cannot be selected this way: the client always receives + the "internal error" alert. The "illegal parameter" alert, used for + the unmatched ClientHello when the ClientHello callback is available, + is not delivered to the client. */ + SSL_set_cert_cb (sess, + &cert_cb_always_fail, + NULL); + } +} + + +# endif /* ! mhd_USE_OPENSSL_CLIENT_HELLO_GET0_EXT */ + +#endif /* mhd_HAVE_OPENSSL_ACME */ + /** * Initialise TLS server context * @param d the daemon handle @@ -1009,6 +1510,14 @@ daemon_init_ctx (struct MHD_Daemon *restrict d, SSL_CTX_set_alpn_select_cb (d_tls->ctx, &select_alpn_prot, d_tls); +#ifdef mhd_USE_OPENSSL_CLIENT_HELLO_GET0_EXT + /* The ACME ALPN challenge is detected by the ClientHello message. + The callback is set unconditionally as OpenSSL does not allow to set + it on connection (session) basis. */ + SSL_CTX_set_client_hello_cb (d_tls->ctx, + &check_clnt_hello_cb, + d_tls); +#endif /* mhd_USE_OPENSSL_CLIENT_HELLO_GET0_EXT */ #ifndef OPENSSL_NO_NEXTPROTONEG SSL_CTX_set_next_protos_advertised_cb (d_tls->ctx, &get_npn_list, @@ -1318,6 +1827,10 @@ mhd_tls_open_daemon_init (struct MHD_Daemon *restrict d, if (NULL == d_tls) return MHD_SC_DAEMON_MEM_ALLOC_FAILURE; +#ifdef mhd_HAVE_OPENSSL_ACME + d_tls->acme_certs = mhd_daemon_get_acme_certs (d); +#endif /* mhd_HAVE_OPENSSL_ACME */ + res = daemon_init_lib_ctx (d, d_tls, s); @@ -1365,6 +1878,377 @@ mhd_tls_open_thread_cleanup (struct mhd_TlsOpenDaemonData *restrict d_tls) } +/* ** Credentials creation / destruction ** */ + +#ifdef mhd_HAVE_OPENSSL_ACME + +/** + * Read the end-entity certificate and the chain of the signing certificates + * from the provided PEM data. + * @param d_tls the pointer to the daemon's TLS settings + * @param[in,out] cred the credentials data with the initialised (empty) + * @a chain member, the @a cert member is set on success + * @param cert_len the length of the @p cert buffer + * @param cert the certificates data in PEM format + * @return #mhd_TLS_CRED_CREATE_OK on success, + * other enum mhd_TlsCredCreateResult values on failure + */ +static MHD_FN_PAR_NONNULL_ALL_ MHD_FN_MUST_CHECK_RESULT_ +MHD_FN_PAR_IN_SIZE_ (4, 3) enum mhd_TlsCredCreateResult +cred_read_certs (struct mhd_TlsOpenDaemonData *restrict d_tls, + struct mhd_TlsOpenCredData *restrict cred, + int cert_len, + const char *restrict cert) +{ + BIO *bio; + enum mhd_TlsCredCreateResult res; + + mhd_assert (NULL == cred->cert); + mhd_assert (NULL != cred->chain); + + res = mhd_TLS_CRED_CREATE_OK; + bio = BIO_new_mem_buf (cert, + cert_len); + if (NULL == bio) + { + mhd_DBG_PRINT_TLS_ERRS (); + res = mhd_TLS_CRED_CREATE_ALLOC_FAILED; + } + else + { + cred->cert = X509_new_ex (d_tls->libctx, + NULL); + if (NULL == cred->cert) + { + mhd_DBG_PRINT_TLS_ERRS (); + res = mhd_TLS_CRED_CREATE_ALLOC_FAILED; + } + else + { + if (NULL == PEM_read_bio_X509_AUX (bio, + &(cred->cert), + &null_passwd_cb, + NULL)) + { + mhd_DBG_PRINT_TLS_ERRS (); + res = mhd_TLS_CRED_CREATE_BAD_CRED_DATA; + } + else + { + do + { + X509 *inter_ca; /* Certifying certificate */ + + inter_ca = X509_new_ex (d_tls->libctx, + NULL); + if (NULL == inter_ca) + { + mhd_DBG_PRINT_TLS_ERRS (); + res = mhd_TLS_CRED_CREATE_ALLOC_FAILED; + break; + } + + if (NULL != PEM_read_bio_X509 (bio, + &inter_ca, + &null_passwd_cb, + NULL)) + { + mhd_NOWARN_USED_UNUSED + + if (0 < sk_X509_push (cred->chain, + inter_ca)) + { + /* The certificate 'inter_ca' is owned by the chain now. + Read the next certificate in the chain. */ + continue; + } + + mhd_RESTORE_WARN_USED_UNUSED + + /* The cleanup path */ + res = mhd_TLS_CRED_CREATE_ALLOC_FAILED; + } + else + { + unsigned long err; + err = ERR_peek_last_error (); + + mhd_NOWARN_USED_UNUSED + + if ((ERR_LIB_PEM == ERR_GET_LIB (err)) + && (PEM_R_NO_START_LINE == ERR_GET_REASON (err))) + { + X509_free (inter_ca); /* Allocated but not used, not needed */ + ERR_clear_error (); /* End of data, all certificates are read */ + BIO_free (bio); + return mhd_TLS_CRED_CREATE_OK; /* Success exit point */ + } + + /* The cleanup path */ + res = mhd_TLS_CRED_CREATE_BAD_CRED_DATA; + + mhd_RESTORE_WARN_USED_UNUSED + } + mhd_DBG_PRINT_TLS_ERRS (); + mhd_assert (mhd_TLS_CRED_CREATE_OK != res); + X509_free (inter_ca); /* Empty or unusable, not needed */ + break; + } while (!0); + mhd_assert (mhd_TLS_CRED_CREATE_OK != res); + } + X509_free (cred->cert); + } + BIO_free (bio); + } + + mhd_assert (mhd_TLS_CRED_CREATE_OK != res); + return res; +} + + +/** + * Provide the application-supplied password for the private key. + * + * Unlike the OpenSSL built-in callback, this one uses the length known by MHD + * instead of deriving it from the zero-termination and refuses to truncate + * the password silently. + * @param[out] buf the buffer to put the password to + * @param size the size of the @a buf + * @param rwflag not used, the password is used for decryption only + * @param cls the pointer to the password string + * @return the number of characters put to the @a buf, + * -1 if the password does not fit the @a buf + */ +static int +mem_passwd_cb (char *buf, + int size, + int rwflag, + void *cls) +{ + const struct MHD_String *const pass = (const struct MHD_String *)cls; + + (void)rwflag; /* Unused */ + + if ((0 > size) || (pass->len > (size_t)size)) + return -1; /* The password does not fit, do not truncate it silently */ + + memcpy (buf, + pass->cstr, + pass->len); + return (int)pass->len; +} + + +/** + * Read the private key from the provided PEM data and check that it matches + * the certificate. + * @param d_tls the pointer to the daemon's TLS settings + * @param[in,out] cred the credentials data with the set @a cert member, + * the @a key member is set on success + * @param key_len the length of the @p key buffer + * @param key the private key data in PEM format + * @param pass_len the length of the @p pass buffer, must be zero if the @p pass + * is NULL + * @param pass the password for the private key, zero-terminated, + * may be NULL + * @return #mhd_TLS_CRED_CREATE_OK on success, + * other enum mhd_TlsCredCreateResult values on failure + */ +static MHD_FN_PAR_NONNULL_ (1) MHD_FN_PAR_NONNULL_ (2) +MHD_FN_PAR_NONNULL_ (4) MHD_FN_MUST_CHECK_RESULT_ +MHD_FN_PAR_IN_SIZE_ (4, 3) MHD_FN_PAR_IN_SIZE_ (6, 5) MHD_FN_PAR_CSTR_ (6) +enum mhd_TlsCredCreateResult +cred_read_key (struct mhd_TlsOpenDaemonData *restrict d_tls, + struct mhd_TlsOpenCredData *restrict cred, + int key_len, + const char *restrict key, + size_t pass_len, + const char *restrict pass) +{ + enum mhd_TlsCredCreateResult res; + BIO *bio; + EVP_PKEY *key_obj; + struct MHD_String pass_str; + + mhd_assert (NULL != cred->cert); + mhd_assert (NULL == cred->key); + mhd_assert ((NULL != pass) || (0 == pass_len)); + + res = mhd_TLS_CRED_CREATE_OK; + pass_str.len = pass_len; + pass_str.cstr = pass; + + bio = BIO_new_mem_buf (key, + key_len); + if (NULL == bio) + { + mhd_DBG_PRINT_TLS_ERRS (); + res = mhd_TLS_CRED_CREATE_ALLOC_FAILED; + } + else + { + key_obj = + PEM_read_bio_PrivateKey_ex (bio, + NULL, + (NULL == pass) ? &null_passwd_cb : + &mem_passwd_cb, + (NULL == pass) ? NULL : &pass_str, + d_tls->libctx, + NULL); + BIO_free (bio); + + if (NULL == key_obj) + { + mhd_DBG_PRINT_TLS_ERRS (); + res = mhd_TLS_CRED_CREATE_BAD_CRED_DATA; + } + else + { + ERR_clear_error (); + if (1 != X509_check_private_key (cred->cert, + key_obj)) + { + mhd_DBG_PRINT_TLS_ERRS (); + res = mhd_TLS_CRED_CREATE_BAD_CRED_DATA; + } + else + { + ERR_clear_error (); + cred->key = key_obj; + return mhd_TLS_CRED_CREATE_OK; /* Success exit point */ + } + EVP_PKEY_free (key_obj); + } + } + + return res; /* Failure exit point */ +} + + +MHD_INTERNAL MHD_FN_MUST_CHECK_RESULT_ +MHD_FN_PAR_NONNULL_ (1) MHD_FN_PAR_NONNULL_ (2) +MHD_FN_PAR_NONNULL_ (4) MHD_FN_PAR_NONNULL_ (6) +MHD_FN_PAR_OUT_ (2) +MHD_FN_PAR_IN_SIZE_ (4, 3) MHD_FN_PAR_IN_SIZE_ (6, 5) +MHD_FN_PAR_IN_SIZE_ (8, 7) +MHD_FN_PAR_CSTR_ (4) MHD_FN_PAR_CSTR_ (6) MHD_FN_PAR_CSTR_ (8) +enum mhd_TlsCredCreateResult +mhd_tls_open_cred_create (struct mhd_TlsOpenDaemonData *restrict d_tls, + union mhd_TlsCredDataPtr *restrict pp_cred, + size_t cert_len, + const char *restrict cert, + size_t key_len, + const char *restrict key, + size_t pass_len, + const char *restrict pass) +{ + struct mhd_TlsOpenCredData *cred; + enum mhd_TlsCredCreateResult res; + int cert_len_i; + int key_len_i; + + mhd_assert (0 != cert_len); + mhd_assert (0 != key_len); + mhd_assert ((NULL != pass) || (0 == pass_len)); + + pp_cred->open = NULL; + + cert_len_i = (int)cert_len; + key_len_i = (int)key_len; + if (mhd_COND_HARDLY_EVER (cert_len != (size_t)cert_len_i) + || mhd_COND_HARDLY_EVER (0 > cert_len_i) + || mhd_COND_HARDLY_EVER (key_len != (size_t)key_len_i) + || mhd_COND_HARDLY_EVER (0 > key_len_i)) + return mhd_TLS_CRED_CREATE_BAD_CRED_DATA; /* The data is too large */ + + cred = (struct mhd_TlsOpenCredData *) + mhd_calloc (1, + sizeof(struct mhd_TlsOpenCredData)); + if (NULL == cred) + return mhd_TLS_CRED_CREATE_ALLOC_FAILED; + +# ifndef NDEBUG +# ifndef HAVE_NULL_PTR_ALL_ZEROS + cred->cert = NULL; + cred->key = NULL; + cred->chain = NULL; +# endif /* HAVE_NULL_PTR_ALL_ZEROS */ +# endif /* NDEBUG */ + + ERR_clear_error (); + /* The chain is always allocated (and stays empty if no chain is provided) as + the empty chain must replace the chain inherited from the daemon's context + when the credentials are used for the connection. */ + cred->chain = sk_X509_new_null (); + if (NULL == cred->chain) + { + mhd_DBG_PRINT_TLS_ERRS (); + res = mhd_TLS_CRED_CREATE_ALLOC_FAILED; + } + else + { + ERR_clear_error (); + + res = cred_read_certs (d_tls, + cred, + cert_len_i, + cert); + if (mhd_TLS_CRED_CREATE_OK == res) + { + res = cred_read_key (d_tls, + cred, + key_len_i, + key, + pass_len, + pass); + if (mhd_TLS_CRED_CREATE_OK == res) + { + pp_cred->open = cred; + return mhd_TLS_CRED_CREATE_OK; /* Success exit point */ + } + + /* Below is a clean-up code path */ + X509_free (cred->cert); + } + mhd_NOWARN_USED_UNUSED + sk_X509_pop_free (cred->chain, + X509_free); + + mhd_RESTORE_WARN_USED_UNUSED + } + + free (cred); + mhd_assert (mhd_TLS_CRED_CREATE_OK != res); + return res; /* Failure exit point */ +} + + +MHD_INTERNAL void +mhd_tls_open_cred_destroy_nodmn (union mhd_TlsCredDataPtr cred) +{ + mhd_ASSUME (NULL != cred.open); + mhd_assert (NULL != cred.open->key); + mhd_assert (NULL != cred.open->cert); + mhd_assert (NULL != cred.open->chain); + + /* Release in the reverse order of the creation. + The objects still used by any TLS session or context are not destroyed + here as OpenSSL holds its own references for them. */ + EVP_PKEY_free (cred.open->key); + X509_free (cred.open->cert); + mhd_NOWARN_USED_UNUSED + sk_X509_pop_free (cred.open->chain, + &X509_free); + + mhd_RESTORE_WARN_USED_UNUSED + free (cred.open); + +} + + +#endif /* mhd_HAVE_OPENSSL_ACME */ + + /* ** Connection initialisation / de-initialisation ** */ MHD_INTERNAL size_t @@ -1374,6 +2258,54 @@ mhd_tls_open_conn_get_tls_size_v (void) } +#ifdef mhd_HAVE_OPENSSL_ACME +/** + * Prepare the connection data for the detection of the ACME ALPN challenge + * @param d_tls the daemon TLS settings + * @param[in,out] c_tls the connection TLS handle with the initialised session + * @return 'true' on success, + * 'false' otherwise + */ +static MHD_FN_PAR_NONNULL_ALL_ MHD_FN_MUST_CHECK_RESULT_ bool +conn_init_acme_data (const struct mhd_TlsOpenDaemonData *restrict d_tls, + struct mhd_TlsOpenConnData *restrict c_tls) +{ + bool res; + + /* c_tls is created by calloc(), all bool member must be 'false' */ + mhd_assert (!c_tls->clienthello_processed); + mhd_assert (!c_tls->is_acme); + + /* The additional context for ClientHello and ALPN callbacks */ + res = (0 != SSL_set_ex_data (c_tls->sess, + conn_mhd_ctls_idx, + c_tls)); + +# ifdef mhd_USE_OPENSSL_CLIENT_HELLO_GET0_EXT + (void)d_tls; /* Unused, the ClientHello callback is set for the context */ +# else /* ! mhd_USE_OPENSSL_CLIENT_HELLO_GET0_EXT */ + /* Conditionally process ClientHello message - only if any ACME certificates + are present */ + if (res + && (mhd_daemon_has_acme_certs (d_tls->acme_certs))) + { + /* The unparsed ClientHello message is provided only by the messages + callback */ + (void)SSL_set_msg_callback_arg (c_tls->sess, /* does not actually return a value */ + d_tls->acme_certs); + SSL_set_msg_callback (c_tls->sess, + &check_hello_msg_acme); + } +# endif /* ! mhd_USE_OPENSSL_CLIENT_HELLO_GET0_EXT */ + return res; +} + + +#else /* ! mhd_HAVE_OPENSSL_ACME */ +# define conn_init_acme_data(d_tls, c_tls) \ + (((void) (d_tls)), ((void) (c_tls)), (! 0)) +#endif /* ! mhd_HAVE_OPENSSL_ACME */ + MHD_INTERNAL MHD_FN_MUST_CHECK_RESULT_ MHD_FN_PAR_NONNULL_ALL_ MHD_FN_PAR_OUT_ (3) bool mhd_tls_open_conn_init (const struct mhd_TlsOpenDaemonData *restrict d_tls, @@ -1396,7 +2328,9 @@ mhd_tls_open_conn_init (const struct mhd_TlsOpenDaemonData *restrict d_tls, return false; } - if (0 < SSL_set_fd (c_tls->sess, fd)) + if ((0 < SSL_set_fd (c_tls->sess, fd)) + && conn_init_acme_data (d_tls, + c_tls)) { SSL_set_accept_state (c_tls->sess); /* Force server mode */ @@ -1585,6 +2519,10 @@ mhd_tls_open_conn_recv (struct mhd_TlsOpenConnData *restrict c_tls, mhd_assert (c_tls->dbg.is_tls_handshake_completed); mhd_assert (!c_tls->shut_tls_wr_sent); mhd_assert (!c_tls->dbg.is_failed); +#ifdef mhd_HAVE_OPENSSL_ACME + mhd_assert (!c_tls->is_acme); /* ACME ALPN challenge connection is not used + for the data exchange */ +#endif /* mhd_HAVE_OPENSSL_ACME */ ERR_clear_error (); @@ -1665,6 +2603,10 @@ mhd_tls_open_conn_send4 (struct mhd_TlsOpenConnData *restrict c_tls, mhd_assert (c_tls->dbg.is_tls_handshake_completed); mhd_assert (!c_tls->shut_tls_wr_sent); mhd_assert (!c_tls->dbg.is_failed); +#ifdef mhd_HAVE_OPENSSL_ACME + mhd_assert (!c_tls->is_acme); /* ACME ALPN challenge connection is not used + for the data exchange */ +#endif /* mhd_HAVE_OPENSSL_ACME */ ERR_clear_error (); @@ -1775,6 +2717,9 @@ mhd_tls_open_conn_get_alpn_prot (struct mhd_TlsOpenConnData *restrict c_tls) { const unsigned char *sel_prot; unsigned int sel_prot_len; +#ifdef mhd_HAVE_OPENSSL_ACME + mhd_assert (!c_tls->is_acme); +#endif /* mhd_HAVE_OPENSSL_ACME */ SSL_get0_alpn_selected (c_tls->sess, &sel_prot, @@ -1795,3 +2740,16 @@ mhd_tls_open_conn_get_alpn_prot (struct mhd_TlsOpenConnData *restrict c_tls) return mhd_tls_alpn_decode_n ((size_t)sel_prot_len, sel_prot); } + + +#ifdef mhd_HAVE_TLS_ACME +# ifdef mhd_HAVE_OPENSSL_ACME +MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ bool +mhd_tls_open_conn_is_acme (struct mhd_TlsOpenConnData *restrict c_tls) +{ + return c_tls->is_acme; +} + + +# endif /* mhd_HAVE_OPENSSL_ACME */ +#endif /* mhd_HAVE_TLS_ACME */ diff --git a/src/mhd2/tls_open_funcs.h b/src/mhd2/tls_open_funcs.h @@ -62,6 +62,7 @@ #include "mhd_status_code_int.h" #include "mhd_tls_enums.h" +#include "mhd_tls_cred_ptr.h" #include "mhd_socket_error.h" /** @@ -124,7 +125,11 @@ struct DaemonOptions; /* Forward declaration */ * @return 'true' if the backend supports ACME ALPN challenge protocol, * 'false' otherwise */ -# define mhd_tls_open_is_acme_alpn_supported(s) ((void)(s), (!!0)) +# ifdef mhd_HAVE_OPENSSL_ACME +# define mhd_tls_open_is_acme_alpn_supported(s) ((void)(s), (!0)) +# else /* ! mhd_HAVE_OPENSSL_ACME */ +# define mhd_tls_open_is_acme_alpn_supported(s) ((void)(s), (!!0)) +# endif /* ! mhd_HAVE_OPENSSL_ACME */ #endif /* mhd_HAVE_TLS_ACME */ /** @@ -165,11 +170,73 @@ MHD_FN_PAR_NONNULL_ALL_ MHD_FN_PAR_INOUT_ (1); /* ** Credentials creation / destruction ** */ -#define mhd_tls_open_cred_create(d_tls, pp_c, cert_l, cert, k_l, k, ps_l, ps) \ - ((void)d_tls, (void)pp_c, (void)cert_l, (void)cert, \ +#ifdef mhd_HAVE_OPENSSL_ACME +/** + * Create TLS credentials from the provided certificate and key data. + * + * Created credentials must be destroyed with #mhd_tls_open_cred_destroy_nodmn() + * function before de-initialisation of the daemon's TLS settings. + * + * @param d_tls the pointer to the daemon's TLS settings + * @param[out] pp_cred the backend-specific credentials pointer to initialise; + * the OpenSSL member is set to the allocated credentials on + * success and to NULL on failure + * @param cert_len the length of the @p cert buffer, not including the + * zero-termination byte, + * must not be zero + * @param cert the certificates data in PEM format, zero-terminated; + * the first certificate is the end-entity certificate, any + * following certificates form the chain of the signing + * certificates + * @param key_len the length of the @p key buffer, not including the + * zero-termination byte, + * must not be zero + * @param key the private key data in PEM format, zero-terminated + * @param pass_len the length of the @p pass buffer, not including the + * zero-termination byte, + * must be zero if the @p pass is NULL + * @param pass the password for the private key, zero-terminated, + * may be NULL if the private key is not password-protected + * @return #mhd_TLS_CRED_CREATE_OK on success, + * other enum mhd_TlsCredCreateResult values on failure + */ +MHD_INTERNAL enum mhd_TlsCredCreateResult +mhd_tls_open_cred_create (struct mhd_TlsOpenDaemonData *restrict d_tls, + union mhd_TlsCredDataPtr *restrict pp_cred, + size_t cert_len, + const char *restrict cert, + size_t key_len, + const char *restrict key, + size_t pass_len, + const char *restrict pass) +MHD_FN_MUST_CHECK_RESULT_ +MHD_FN_PAR_NONNULL_ (1) MHD_FN_PAR_NONNULL_ (2) +MHD_FN_PAR_NONNULL_ (4) MHD_FN_PAR_NONNULL_ (6) +MHD_FN_PAR_OUT_ (2) +MHD_FN_PAR_IN_SIZE_ (4, 3) MHD_FN_PAR_IN_SIZE_ (6, 5) +MHD_FN_PAR_IN_SIZE_ (8, 7) +MHD_FN_PAR_CSTR_ (4) MHD_FN_PAR_CSTR_ (6) MHD_FN_PAR_CSTR_ (8); + +/** + * Release TLS credentials and free the allocated memory. + * + * The certificate, the private key and the certificates chain are released. + * The objects still used by any TLS session or context stay alive as OpenSSL + * holds its own references for them. + * + * @param cred the backend-specific credentials pointer to release; the OpenSSL + * member must be non-NULL + */ +MHD_INTERNAL void +mhd_tls_open_cred_destroy_nodmn (union mhd_TlsCredDataPtr cred); + +#else /* ! mhd_HAVE_OPENSSL_ACME */ +# define mhd_tls_open_cred_create(d_tls, pp_c, c_l, c, k_l, k, ps_l, ps) \ + ((void)d_tls, (void)pp_c, (void)c_l, (void)c, \ (void)k_l, (void)k, (void)ps_l, (void)ps, \ mhd_TLS_CRED_CREATE_UNSUPPORTED) -#define mhd_tls_open_cred_destroy_nodmn(cred) ((void)(cred)) +# define mhd_tls_open_cred_destroy_nodmn(cred) ((void)(cred)) +#endif /* ! mhd_HAVE_OPENSSL_ACME */ /** * Release TLS credentials and free the allocated memory when no references @@ -371,7 +438,14 @@ MHD_FN_PAR_NONNULL_ALL_; * @return 'true' if the connection is ACME ALPN challenge connection, * 'false' otherwise */ -# define mhd_tls_open_conn_is_acme(c_tls) (((void)(c_tls)), (!!0)) +# ifdef mhd_HAVE_OPENSSL_ACME +MHD_INTERNAL bool +mhd_tls_open_conn_is_acme (struct mhd_TlsOpenConnData *restrict c_tls) +MHD_FN_PAR_NONNULL_ALL_; + +# else /* ! mhd_HAVE_OPENSSL_ACME */ +# define mhd_tls_open_conn_is_acme(c_tls) (((void)(c_tls)), (!!0)) +# endif /* ! mhd_HAVE_OPENSSL_ACME */ #endif /* mhd_HAVE_TLS_ACME */ #endif /* ! MHD_TLS_OPEN_FUNCS_H */