X-Git-Url: https://git.exim.org/exim.git/blobdiff_plain/9f6b3bf5187562bac4c96e3ed6a17740d01489fa..96751ae76e6c6db435f5a4f141511d6f973b6583:/src/src/tls-openssl.c diff --git a/src/src/tls-openssl.c b/src/src/tls-openssl.c index f8b4db06a..9ddb16fc4 100644 --- a/src/src/tls-openssl.c +++ b/src/src/tls-openssl.c @@ -94,6 +94,10 @@ change this guard and punt the issue for a while longer. */ # 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) @@ -111,11 +115,16 @@ change this guard and punt the issue for a while longer. */ # 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 @@ -942,36 +951,35 @@ Returns: nothing */ 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)); } } @@ -1468,12 +1476,12 @@ return; 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*/ @@ -1565,6 +1573,11 @@ else 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)) { @@ -1909,6 +1922,9 @@ tls_server_creds_invalidate(void) { 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 } @@ -2194,7 +2210,9 @@ already exists. Might even need this selfsame callback, for reneg? */ SSL_CTX * ctx = state_server.lib_state.lib_ctx; SSL_CTX_set_info_callback(server_sni, SSL_CTX_get_info_callback(ctx)); SSL_CTX_set_mode(server_sni, SSL_CTX_get_mode(ctx)); + SSL_CTX_set_min_proto_version(server_sni, SSL3_VERSION); SSL_CTX_set_options(server_sni, SSL_CTX_get_options(ctx)); + SSL_CTX_clear_options(server_sni, ~SSL_CTX_get_options(ctx)); SSL_CTX_set_timeout(server_sni, SSL_CTX_get_timeout(ctx)); SSL_CTX_set_tlsext_servername_callback(server_sni, tls_servername_cb); SSL_CTX_set_tlsext_servername_arg(server_sni, state); @@ -2405,7 +2423,7 @@ BIO_puts(bp, "\n"); } 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; @@ -2415,10 +2433,10 @@ OCSP_BASICRESP * bs; 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; @@ -2476,9 +2494,19 @@ if (!(bs = OCSP_response_get1_basic(rsp))) 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"); @@ -2700,10 +2728,15 @@ if (init_options) } #endif - DEBUG(D_tls) debug_printf("setting SSL CTX options: %#lx\n", init_options); - if (!(SSL_CTX_set_options(ctx, init_options))) - return tls_error(string_sprintf( + SSL_CTX_set_min_proto_version(ctx, SSL3_VERSION); + DEBUG(D_tls) debug_printf("setting SSL CTX options: %016lx\n", init_options); + SSL_CTX_set_options(ctx, init_options); + { + ulong readback = SSL_CTX_clear_options(ctx, ~init_options); + if (readback != init_options) + return tls_error(string_sprintf( "SSL_CTX_set_option(%#lx)", init_options), host, NULL, errstr); + } } else DEBUG(D_tls) debug_printf("no SSL CTX options to set\n"); @@ -2744,7 +2777,7 @@ if (state->lib_state.conn_certs) else { #ifndef DISABLE_OCSP - if (!host) + if (!host) /* server */ { state->u_ocsp.server.file = ocsp_file; state->u_ocsp.server.file_expanded = NULL; @@ -3160,6 +3193,52 @@ tls_dump_keylog(SSL * ssl) } +/* 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 * *************************************************/ @@ -3341,8 +3420,9 @@ if (rc <= 0) 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); @@ -3361,7 +3441,9 @@ if (rc <= 0) || 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; } @@ -3372,7 +3454,9 @@ if (rc <= 0) 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)); @@ -3381,7 +3465,9 @@ if (rc <= 0) 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; } } @@ -3429,6 +3515,7 @@ else DEBUG(D_tls) 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)); @@ -3461,19 +3548,7 @@ DEBUG(D_tls) 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 @@ -4017,7 +4092,12 @@ if (ob->tls_alpn) #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 @@ -4146,18 +4226,7 @@ tlsp->cipher_stdname = cipher_stdname_ssl(exim_client_ctx->ssl); } /*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;