# define EXIM_HAVE_OPENSSL_CIPHER_GET_ID
#endif
+#if !defined(LIBRESSL_VERSION_NUMBER) && (OPENSSL_VERSION_NUMBER >= 0x030000000L)
+# define EXIM_HAVE_EXPORT_CHNL_BNGNG
+#endif
+
#if !defined(LIBRESSL_VERSION_NUMBER) \
|| LIBRESSL_VERSION_NUMBER >= 0x20010000L
# if !defined(OPENSSL_NO_ECDH)
# define OPENSSL_HAVE_KEYLOG_CB
# define OPENSSL_HAVE_NUM_TICKETS
# define EXIM_HAVE_OPENSSL_CIPHER_STD_NAME
+# define EXIM_HAVE_EXP_CHNL_BNGNG
# else
# define OPENSSL_BAD_SRVR_OURCERT
# endif
#endif
+#if !defined(LIBRESSL_VERSION_NUMBER) && (OPENSSL_VERSION_NUMBER >= 0x010002000L)
+# define EXIM_HAVE_EXPORT_CHNL_BNGNG
+#endif
+
#if !defined(EXIM_HAVE_OPENSSL_TLSEXT) && !defined(DISABLE_OCSP)
# warning "OpenSSL library version too old; define DISABLE_OCSP in Makefile"
# define DISABLE_OCSP
*/
static void
-info_callback(SSL *s, int where, int ret)
+info_callback(SSL * s, int where, int ret)
{
DEBUG(D_tls)
{
- const uschar * str;
-
- if (where & SSL_ST_CONNECT)
- str = US"SSL_connect";
- else if (where & SSL_ST_ACCEPT)
- str = US"SSL_accept";
- else
- str = US"SSL info (undefined)";
+ gstring * g = NULL;
+
+ if (where & SSL_ST_CONNECT) g = string_append_listele(g, ',', US"SSL_connect");
+ if (where & SSL_ST_ACCEPT) g = string_append_listele(g, ',', US"SSL_accept");
+ if (where & SSL_CB_LOOP) g = string_append_listele(g, ',', US"state_chg");
+ if (where & SSL_CB_EXIT) g = string_append_listele(g, ',', US"hshake_exit");
+ if (where & SSL_CB_READ) g = string_append_listele(g, ',', US"read");
+ if (where & SSL_CB_WRITE) g = string_append_listele(g, ',', US"write");
+ if (where & SSL_CB_ALERT) g = string_append_listele(g, ',', US"alert");
+ if (where & SSL_CB_HANDSHAKE_START) g = string_append_listele(g, ',', US"hshake_start");
+ if (where & SSL_CB_HANDSHAKE_DONE) g = string_append_listele(g, ',', US"hshake_done");
if (where & SSL_CB_LOOP)
- debug_printf("%s: %s\n", str, SSL_state_string_long(s));
+ debug_printf("SSL %s: %s\n", g->s, SSL_state_string_long(s));
else if (where & SSL_CB_ALERT)
- debug_printf("SSL3 alert %s:%s:%s\n",
- str = where & SSL_CB_READ ? US"read" : US"write",
+ debug_printf("SSL %s %s:%s\n", g->s,
SSL_alert_type_string_long(ret), SSL_alert_desc_string_long(ret));
else if (where & SSL_CB_EXIT)
{
- if (ret == 0)
- debug_printf("%s: failed in %s\n", str, SSL_state_string_long(s));
- else if (ret < 0)
- debug_printf("%s: error in %s\n", str, SSL_state_string_long(s));
+ if (ret <= 0)
+ debug_printf("SSL %s: %s in %s\n", g->s,
+ ret == 0 ? "failed" : "error", SSL_state_string_long(s));
}
- else if (where & SSL_CB_HANDSHAKE_START)
- debug_printf("%s: hshake start: %s\n", str, SSL_state_string_long(s));
- else if (where & SSL_CB_HANDSHAKE_DONE)
- debug_printf("%s: hshake done: %s\n", str, SSL_state_string_long(s));
+ else if (where & (SSL_CB_HANDSHAKE_START | SSL_CB_HANDSHAKE_DONE))
+ debug_printf("SSL %s: %s\n", g->s, SSL_state_string_long(s));
}
}
static void
-ocsp_free_response_list(exim_openssl_state_st * cbinfo)
+ocsp_free_response_list(exim_openssl_state_st * state)
{
-for (ocsp_resplist * olist = cbinfo->u_ocsp.server.olist; olist;
+for (ocsp_resplist * olist = state->u_ocsp.server.olist; olist;
olist = olist->next)
OCSP_RESPONSE_free(olist->resp);
-cbinfo->u_ocsp.server.olist = NULL;
+state->u_ocsp.server.olist = NULL;
}
#endif /*!DISABLE_OCSP*/
if (olist && !*olist)
olist = NULL;
+ /* If doing a re-expand after SNI, avoid reloading the OCSP
+ responses when the list of filenames has not changed.
+ The creds-invali on content change wipes file_expanded, so that
+ always reloads here. */
+
if ( state->u_ocsp.server.file_expanded && olist
&& (Ustrcmp(olist, state->u_ocsp.server.file_expanded) == 0))
{
{
SSL_CTX_free(state_server.lib_state.lib_ctx);
state_server.lib_state = null_tls_preload;
+#ifndef DISABLE_OCSP
+state_server.u_ocsp.server.file_expanded = NULL;
+#endif
}
}
static int
-tls_client_stapling_cb(SSL *s, void *arg)
+tls_client_stapling_cb(SSL * ssl, void * arg)
{
exim_openssl_state_st * cbinfo = arg;
const unsigned char * p;
int i;
DEBUG(D_tls) debug_printf("Received TLS status callback (OCSP stapling):\n");
-len = SSL_get_tlsext_status_ocsp_resp(s, &p);
+len = SSL_get_tlsext_status_ocsp_resp(ssl, &p);
if(!p)
{ /* Expect this when we requested ocsp but got none */
- if (SSL_session_reused(s) && tls_out.ocsp == OCSP_VFIED)
+ if (SSL_session_reused(ssl) && tls_out.ocsp == OCSP_VFIED)
{
DEBUG(D_tls) debug_printf(" null, but resumed; ocsp vfy stored with session is good\n");
return 1;
if (ERR_peek_error())
{
tls_out.ocsp = OCSP_FAILED;
- if (LOGGING(tls_cipher)) log_write(0, LOG_MAIN,
- "Received TLS cert status response, itself unverifiable: %s",
- ERR_reason_error_string(ERR_peek_error()));
+ if (LOGGING(tls_cipher))
+ {
+ const uschar * errstr = CUS ERR_reason_error_string(ERR_peek_error());
+ static uschar peerdn[256];
+ X509_NAME_oneline(X509_get_subject_name(SSL_get_peer_certificate(ssl)),
+ CS peerdn, sizeof(peerdn));
+ log_write(0, LOG_MAIN,
+ "[%s] %s Received TLS cert (DN: '%.*s') status response, "
+ "itself unverifiable: %s",
+ sender_host_address, sender_host_name,
+ (int)sizeof(peerdn), peerdn,
+ errstr);
+ }
DEBUG(D_tls)
{
BIO_printf(bp, "OCSP response verify failure\n");
else
{
#ifndef DISABLE_OCSP
- if (!host)
+ if (!host) /* server */
{
state->u_ocsp.server.file = ocsp_file;
state->u_ocsp.server.file_expanded = NULL;
}
+/* Channel-binding info for authenticators
+See description in https://paquier.xyz/postgresql-2/channel-binding-openssl/
+for pre-TLS1.3
+*/
+
+static void
+tls_get_channel_binding(SSL * ssl, tls_support * tlsp, const void * taintval)
+{
+uschar c, * s;
+size_t len;
+
+#ifdef EXIM_HAVE_EXPORT_CHNL_BNGNG
+if (SSL_version(ssl) > TLS1_2_VERSION)
+ {
+ /* It's not documented by OpenSSL how big the output buffer must be.
+ The OpenSSL testcases use 80 bytes but don't say why. The GnuTLS impl only
+ serves out 32B. RFC 9266 says it is 32B.
+ Interop fails unless we use the same each end. */
+ len = 32;
+
+ tlsp->channelbind_exporter = TRUE;
+ taintval = GET_UNTAINTED;
+ if (SSL_export_keying_material(ssl,
+ s = store_get((int)len, taintval), len,
+ "EXPORTER-Channel-Binding", (size_t) 24,
+ NULL, 0, 0) != 1)
+ len = 0;
+ }
+else
+#endif
+ {
+ len = SSL_get_peer_finished(ssl, &c, 0);
+ len = SSL_get_peer_finished(ssl, s = store_get((int)len, taintval), len);
+ }
+
+if (len > 0)
+ {
+ int old_pool = store_pool;
+ store_pool = POOL_PERM;
+ tlsp->channelbinding = b64encode_taint(CUS s, (int)len, taintval);
+ store_pool = old_pool;
+ DEBUG(D_tls) debug_printf("Have channel bindings cached for possible auth usage %p %p\n", tlsp->channelbinding, tlsp);
+ }
+}
+
+
/*************************************************
* Start a TLS session in a server *
*************************************************/
case SSL_ERROR_ZERO_RETURN:
DEBUG(D_tls) debug_printf("Got SSL_ERROR_ZERO_RETURN\n");
(void) tls_error(US"SSL_accept", NULL, sigalrm_seen ? US"timed out" : NULL, errstr);
+#ifndef DISABLE_EVENT
(void) event_raise(event_action, US"tls:fail:connect", *errstr, NULL);
-
+#endif
if (SSL_get_shutdown(ssl) == SSL_RECEIVED_SHUTDOWN)
SSL_shutdown(ssl);
|| r == SSL_R_UNKNOWN_PROTOCOL || r == SSL_R_UNSUPPORTED_PROTOCOL)
s = string_sprintf("(%s)", SSL_get_version(ssl));
(void) tls_error(US"SSL_accept", NULL, sigalrm_seen ? US"timed out" : s, errstr);
+#ifndef DISABLE_EVENT
(void) event_raise(event_action, US"tls:fail:connect", *errstr, NULL);
+#endif
return FAIL;
}
if (!errno)
{
*errstr = US"SSL_accept: TCP connection closed by peer";
+#ifndef DISABLE_EVENT
(void) event_raise(event_action, US"tls:fail:connect", *errstr, NULL);
+#endif
return FAIL;
}
DEBUG(D_tls) debug_printf(" - syscall %s\n", strerror(errno));
sigalrm_seen ? US"timed out"
: ERR_peek_error() ? NULL : string_sprintf("ret %d", error),
errstr);
+#ifndef DISABLE_EVENT
(void) event_raise(event_action, US"tls:fail:connect", *errstr, NULL);
+#endif
return FAIL;
}
}
adjust the input functions to read via TLS, and initialize things. */
#ifdef SSL_get_extms_support
+/*XXX what does this return for tls1.3 ? */
tls_in.ext_master_secret = SSL_get_extms_support(ssl) == 1;
#endif
peer_cert(ssl, &tls_in, peerdn, sizeof(peerdn));
tls_in.ourcert = crt ? X509_dup(crt) : NULL;
}
-/* Channel-binding info for authenticators
-See description in https://paquier.xyz/postgresql-2/channel-binding-openssl/ */
- {
- uschar c, * s;
- size_t len = SSL_get_peer_finished(ssl, &c, 0);
- int old_pool = store_pool;
-
- SSL_get_peer_finished(ssl, s = store_get((int)len, GET_UNTAINTED), len);
- store_pool = POOL_PERM;
- tls_in.channelbinding = b64encode_taint(CUS s, (int)len, GET_UNTAINTED);
- store_pool = old_pool;
- DEBUG(D_tls) debug_printf("Have channel bindings cached for possible auth usage %p\n", tls_in.channelbinding);
- }
+tls_get_channel_binding(ssl, &tls_in, GET_UNTAINTED);
/* Only used by the server-side tls (tls_in), including tls_getc.
Client-side (tls_out) reads (seem to?) go via
#endif
#ifndef DISABLE_TLS_RESUME
-if (verify_check_given_host(CUSS &ob->tls_resumption_hosts, host) == OK)
+/*XXX have_lbserver: another cmdline arg possibly, for continued-conn, but use
+will be very low. */
+
+if (!conn_args->have_lbserver) /* wanted for tls_client_resmption_key() */
+ { DEBUG(D_tls) debug_printf("resumption not supported on continued-connection\n"); }
+else if (verify_check_given_host(CUSS &ob->tls_resumption_hosts, host) == OK)
tls_client_ctx_resume_prehandshake(exim_client_ctx, conn_args, tlsp, ob);
#endif
}
/*XXX will this work with continued-TLS? */
-/* Channel-binding info for authenticators */
- {
- uschar c, * s;
- size_t len = SSL_get_finished(exim_client_ctx->ssl, &c, 0);
- int old_pool = store_pool;
-
- SSL_get_finished(exim_client_ctx->ssl, s = store_get((int)len, GET_TAINTED), len);
- store_pool = POOL_PERM;
- tlsp->channelbinding = b64encode_taint(CUS s, (int)len, GET_TAINTED);
- store_pool = old_pool;
- DEBUG(D_tls) debug_printf("Have channel bindings cached for possible auth usage %p %p\n", tlsp->channelbinding, tlsp);
- }
+tls_get_channel_binding(exim_client_ctx->ssl, tlsp, GET_TAINTED);
tlsp->active.sock = cctx->sock;
tlsp->active.tls_ctx = exim_client_ctx;