X-Git-Url: https://git.exim.org/exim.git/blobdiff_plain/e1aca33756f73c22b00a98d40ce2be8ed94464b1..HEAD:/src/src/tls-openssl.c diff --git a/src/src/tls-openssl.c b/src/src/tls-openssl.c index 513ba0d3a..302404b6c 100644 --- a/src/src/tls-openssl.c +++ b/src/src/tls-openssl.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) The Exim Maintainers 2020 - 2022 */ +/* Copyright (c) The Exim Maintainers 2020 - 2024 */ /* Copyright (c) University of Cambridge 1995 - 2019 */ /* See the file NOTICE for conditions of use and distribution. */ /* SPDX-License-Identifier: GPL-2.0-or-later */ @@ -77,9 +77,9 @@ change this guard and punt the issue for a while longer. */ # define EXIM_HAVE_OPENSSL_KEYLOG # define EXIM_HAVE_OPENSSL_CIPHER_GET_ID # define EXIM_HAVE_SESSION_TICKET -# define EXIM_HAVE_OPESSL_TRACE -# define EXIM_HAVE_OPESSL_GET0_SERIAL -# define EXIM_HAVE_OPESSL_OCSP_RESP_GET0_CERTS +# define EXIM_HAVE_OPENSSL_TRACE +# define EXIM_HAVE_OPENSSL_GET0_SERIAL +# define EXIM_HAVE_OPENSSL_OCSP_RESP_GET0_CERTS # define EXIM_HAVE_SSL_GET0_VERIFIED_CHAIN # ifndef DISABLE_OCSP # define EXIM_HAVE_OCSP @@ -97,6 +97,9 @@ change this guard and punt the issue for a while longer. */ #if LIBRESSL_VERSION_NUMBER >= 0x3040000fL # define EXIM_HAVE_OPENSSL_CIPHER_GET_ID #endif +#if LIBRESSL_VERSION_NUMBER >= 0x3050000fL +# define EXIM_HAVE_OPENSSL_OCSP_RESP_GET0_CERTS +#endif #if !defined(LIBRESSL_VERSION_NUMBER) && (OPENSSL_VERSION_NUMBER >= 0x030000000L) # define EXIM_HAVE_EXPORT_CHNL_BNGNG @@ -122,6 +125,7 @@ change this guard and punt the issue for a while longer. */ # define EXIM_HAVE_OPENSSL_CIPHER_STD_NAME # define EXIM_HAVE_EXP_CHNL_BNGNG # define EXIM_HAVE_OPENSSL_OCSP_RESP_GET0_SIGNER +# define EXIM_HAVE_OPENSSL_SET1_GROUPS # else # define OPENSSL_BAD_SRVR_OURCERT # endif @@ -700,6 +704,41 @@ return TRUE; * Initialize for ECDH * *************************************************/ +/* "auto" needs to be handled carefully. +OpenSSL < 1.0.2: we do not select anything, but fallback to prime256v1 +OpenSSL < 1.1.0: we have to call SSL_CTX_set_ecdh_auto + (openssl/ssl.h defines SSL_CTRL_SET_ECDH_AUTO) +OpenSSL >= 1.1.0: we do not set anything, the libray does autoselection + https://github.com/openssl/openssl/commit/fe6ef2472db933f01b59cad82aa925736935984b + +*/ + +static uschar * +init_ecdh_auto(SSL_CTX * sctx) +{ +#if OPENSSL_VERSION_NUMBER < 0x10002000L +DEBUG(D_tls) debug_printf( + " ECDH OpenSSL < 1.0.2: temp key parameter settings: overriding \"auto\" with \"prime256v1\"\n"); +return US"prime256v1"; + +#else +# if defined SSL_CTRL_SET_ECDH_AUTO + +DEBUG(D_tls) debug_printf( + " ECDH OpenSSL 1.0.2+: temp key parameter settings: autoselection\n"); +SSL_CTX_set_ecdh_auto(sctx, 1); +return NULL; + +# else + +DEBUG(D_tls) debug_printf( + " ECDH OpenSSL 1.1.0+: temp key parameter settings: library default selection\n"); +return NULL; + +# endif +#endif +} + /* Load parameters for ECDH encryption. Server only. For now, we stick to NIST P-256 because: it's simple and easy to configure; @@ -730,72 +769,76 @@ init_ecdh(SSL_CTX * sctx, uschar ** errstr) return TRUE; #else -uschar * exp_curve; -int nid, rc; - # ifndef EXIM_HAVE_ECDH DEBUG(D_tls) debug_printf(" No OpenSSL API to define ECDH parameters, skipping\n"); return TRUE; # else +uschar * exp_curve; +int ngroups, rc, sep; +const uschar * curves_list, * curve; +# ifdef EXIM_HAVE_OPENSSL_SET1_GROUPS +int nids[16]; +# else +int nids[1]; +# endif + if (!expand_check(tls_eccurve, US"tls_eccurve", &exp_curve, errstr)) return FALSE; /* Is the option deliberately empty? */ if (!exp_curve || !*exp_curve) - { -#if OPENSSL_VERSION_NUMBER >= 0x10002000L - DEBUG(D_tls) debug_printf( " ECDH OpenSSL 1.0.2+: clearing curves list\n"); - (void) SSL_CTX_set1_curves(sctx, &nid, 0); -#endif return TRUE; - } -/* "auto" needs to be handled carefully. - * OpenSSL < 1.0.2: we do not select anything, but fallback to prime256v1 - * OpenSSL < 1.1.0: we have to call SSL_CTX_set_ecdh_auto - * (openssl/ssl.h defines SSL_CTRL_SET_ECDH_AUTO) - * OpenSSL >= 1.1.0: we do not set anything, the libray does autoselection - * https://github.com/openssl/openssl/commit/fe6ef2472db933f01b59cad82aa925736935984b - */ -if (Ustrcmp(exp_curve, "auto") == 0) - { -#if OPENSSL_VERSION_NUMBER < 0x10002000L - DEBUG(D_tls) debug_printf( - " ECDH OpenSSL < 1.0.2: temp key parameter settings: overriding \"auto\" with \"prime256v1\"\n"); - exp_curve = US"prime256v1"; -#else -# if defined SSL_CTRL_SET_ECDH_AUTO - DEBUG(D_tls) debug_printf( - " ECDH OpenSSL 1.0.2+: temp key parameter settings: autoselection\n"); - SSL_CTX_set_ecdh_auto(sctx, 1); - return TRUE; -# else - DEBUG(D_tls) debug_printf( - " ECDH OpenSSL 1.1.0+: temp key parameter settings: library default selection\n"); - return TRUE; -# endif -#endif - } +/* Limit the list to hardwired array size. Drop out if any element is "suto". */ -if ( (nid = OBJ_sn2nid (CCS exp_curve)) == NID_undef -# ifdef EXIM_HAVE_OPENSSL_EC_NIST2NID - && (nid = EC_curve_nist2nid(CCS exp_curve)) == NID_undef -# endif - ) - { - uschar * s = string_sprintf("Unknown curve name tls_eccurve '%s'", exp_curve); - DEBUG(D_tls) debug_printf("TLS error '%s'\n", s); - if (errstr) *errstr = s; - return FALSE; - } +curves_list = exp_curve; +sep = 0; +for (ngroups = 0; + ngroups < nelem(nids) + && (curve = string_nextinlist(&curves_list, &sep, NULL, 0)); + ) + if (Ustrcmp(curve, "auto") == 0) + { + DEBUG(D_tls) if (ngroups > 0) + debug_printf(" tls_eccurve 'auto' item takes precedence\n"); + if ((exp_curve = init_ecdh_auto(sctx))) break; /* have a curve name to set */ + return TRUE; /* all done */ + } + else + ngroups++; -# if OPENSSL_VERSION_NUMBER < 0x30000000L +/* Translate to NIDs */ + +curves_list = exp_curve; +for (ngroups = 0; curve = string_nextinlist(&curves_list, &sep, NULL, 0); + ngroups++) + if ( (nids[ngroups] = OBJ_sn2nid (CCS curve)) == NID_undef +# ifdef EXIM_HAVE_OPENSSL_EC_NIST2NID + && (nids[ngroups] = EC_curve_nist2nid(CCS curve)) == NID_undef +# endif + ) + { + uschar * s = string_sprintf("Unknown curve name in tls_eccurve '%s'", curve); + DEBUG(D_tls) debug_printf("TLS error: %s\n", s); + if (errstr) *errstr = s; + return FALSE; + } + +# ifdef EXIM_HAVE_OPENSSL_SET1_GROUPS +/* Set the groups */ + +if ((rc = SSL_CTX_set1_groups(sctx, nids, ngroups)) == 0) + tls_error(string_sprintf("Error enabling '%s' group(s)", exp_curve), NULL, NULL, errstr); +else + DEBUG(D_tls) debug_printf(" ECDH: enabled '%s' group(s)\n", exp_curve); + +# else /* Cannot handle a list; only 1 element nids array */ { EC_KEY * ecdh; - if (!(ecdh = EC_KEY_new_by_curve_name(nid))) + if (!(ecdh = EC_KEY_new_by_curve_name(nids[0]))) { tls_error(US"Unable to create ec curve", NULL, NULL, errstr); return FALSE; @@ -810,15 +853,7 @@ if ( (nid = OBJ_sn2nid (CCS exp_curve)) == NID_undef DEBUG(D_tls) debug_printf(" ECDH: enabled '%s' curve\n", exp_curve); EC_KEY_free(ecdh); } - -#else /* v 3.0.0 + */ - -if ((rc = SSL_CTX_set1_groups(sctx, &nid, 1)) == 0) - tls_error(string_sprintf("Error enabling '%s' group", exp_curve), NULL, NULL, errstr); -else - DEBUG(D_tls) debug_printf(" ECDH: enabled '%s' group\n", exp_curve); - -#endif +# endif /*!EXIM_HAVE_OPENSSL_SET1_GROUPS*/ return !!rc; @@ -969,7 +1004,7 @@ Returns: nothing */ static void -info_callback(SSL * s, int where, int ret) +info_callback(const SSL * s, int where, int ret) { DEBUG(D_tls) { @@ -1160,6 +1195,8 @@ else uschar * name; int rc; while ((name = string_nextinlist(&list, &sep, NULL, 0))) + { + DEBUG(D_tls|D_lookup) debug_printf_indent("%s suitable for cert, per OpenSSL?", name); if ((rc = X509_check_host(cert, CCS name, 0, X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS | X509_CHECK_FLAG_SINGLE_LABEL_SUBDOMAINS, @@ -1171,8 +1208,11 @@ else tlsp == &tls_out ? deliver_host_address : sender_host_address); name = NULL; } + DEBUG(D_tls|D_lookup) debug_printf_indent(" yes\n"); break; } + else DEBUG(D_tls|D_lookup) debug_printf_indent(" no\n"); + } if (!name) #else if (!tls_is_name_for_cert(verify_cert_hostnames, cert)) @@ -1401,7 +1441,7 @@ SNI handling. Separately we might try to replace using OCSP_basic_verify() - which seems to not be a public interface into the OpenSSL library (there's no manual entry) - -(in 3.0.0 + is is public) +(in 3.0.0 + it is public) But what with? We also use OCSP_basic_verify in the client stapling callback. And there we NEED it; we must verify that status... unless the library does it for us anyway? */ @@ -1718,13 +1758,13 @@ level. */ DEBUG(D_tls) { - SSL_CTX_set_info_callback(ctx, (void (*)())info_callback); -#if defined(EXIM_HAVE_OPESSL_TRACE) && !defined(OPENSSL_NO_SSL_TRACE) + SSL_CTX_set_info_callback(ctx, info_callback); +#if defined(EXIM_HAVE_OPENSSL_TRACE) && !defined(OPENSSL_NO_SSL_TRACE) /* this needs a debug build of OpenSSL */ - SSL_CTX_set_msg_callback(ctx, (void (*)())SSL_trace); + SSL_CTX_set_msg_callback(ctx, SSL_trace); #endif #ifdef OPENSSL_HAVE_KEYLOG_CB - SSL_CTX_set_keylog_callback(ctx, (void (*)())keylog_callback); + SSL_CTX_set_keylog_callback(ctx, keylog_callback); #endif } @@ -1867,7 +1907,8 @@ a queue-run startup with watch clear. */ static void tls_client_creds_init(transport_instance * t, BOOL watch) { -smtp_transport_options_block * ob = t->options_block; +smtp_transport_options_block * ob = t->drinst.options_block; +const uschar * trname = t->drinst.name; exim_openssl_state_st tpt_dummy_state; host_item * dummy_host = (host_item *)1; uschar * dummy_errstr; @@ -1894,7 +1935,7 @@ if ( opt_set_and_noexpand(ob->tls_certificate) uschar * pkey = ob->tls_privatekey; DEBUG(D_tls) - debug_printf("TLS: preloading client certs for transport '%s'\n",t->name); + debug_printf("TLS: preloading client certs for transport '%s'\n", trname); if ( tls_add_certfile(ctx, &tpt_dummy_state, ob->tls_certificate, &dummy_errstr) == 0 @@ -1907,7 +1948,7 @@ if ( opt_set_and_noexpand(ob->tls_certificate) } else DEBUG(D_tls) - debug_printf("TLS: not preloading client certs, for transport '%s'\n", t->name); + debug_printf("TLS: not preloading client certs, for transport '%s'\n", trname); if ( opt_set_and_noexpand(ob->tls_verify_certificates) @@ -1921,7 +1962,7 @@ if ( opt_set_and_noexpand(ob->tls_verify_certificates) { uschar * v_certs = ob->tls_verify_certificates; DEBUG(D_tls) - debug_printf("TLS: preloading CA bundle for transport '%s'\n", t->name); + debug_printf("TLS: preloading CA bundle for transport '%s'\n", trname); if (setup_certs(ctx, &v_certs, ob->tls_crl, dummy_host, &dummy_errstr) == OK) @@ -1930,7 +1971,7 @@ if ( opt_set_and_noexpand(ob->tls_verify_certificates) } else DEBUG(D_tls) - debug_printf("TLS: not preloading CA bundle, for transport '%s'\n", t->name); + debug_printf("TLS: not preloading CA bundle, for transport '%s'\n", trname); #endif /*EXIM_HAVE_INOTIFY*/ } @@ -1954,21 +1995,11 @@ state_server.u_ocsp.server.file_expanded = NULL; static void tls_client_creds_invalidate(transport_instance * t) { -smtp_transport_options_block * ob = t->options_block; +smtp_transport_options_block * ob = t->drinst.options_block; SSL_CTX_free(ob->tls_preload.lib_ctx); ob->tls_preload = null_tls_preload; } -#else - -static void -tls_server_creds_invalidate(void) -{ return; } - -static void -tls_client_creds_invalidate(transport_instance * t) -{ return; } - #endif /*EXIM_HAVE_INOTIFY*/ @@ -2367,7 +2398,7 @@ for (int pos = 0, siz; pos < inlen; pos += siz+1) if (pos + 1 + siz > inlen) siz = inlen - pos - 1; g = string_append_listele_n(g, ':', in + pos + 1, siz); } -log_write(0, LOG_MAIN, "TLS ALPN (%s) rejected", string_from_gstring(g)); +log_write(0, LOG_MAIN, "TLS ALPN (%Y) rejected", g); gstring_release_unused(g); return SSL_TLSEXT_ERR_ALERT_FATAL; } @@ -2405,7 +2436,7 @@ tls_in.ocsp = OCSP_NOT_RESP; if (!olist) return SSL_TLSEXT_ERR_NOACK; -#ifdef EXIM_HAVE_OPESSL_GET0_SERIAL +#ifdef EXIM_HAVE_OPENSSL_GET0_SERIAL { const X509 * cert_sent = SSL_get_certificate(s); const ASN1_INTEGER * cert_serial = X509_get0_serialNumber(cert_sent); @@ -2568,7 +2599,7 @@ if (!(bs = OCSP_response_get1_basic(rsp))) asking for certificate-status under DANE, so this callback won't run for that combination. It still will for non-DANE. */ -#ifdef EXIM_HAVE_OPENSSL_OCSP_RESP_GET0_SIGNER +#if defined(EXIM_HAVE_OPENSSL_OCSP_RESP_GET0_SIGNER) && defined(SUPPORT_DANE) X509 * signer; if ( tls_out.dane_verified @@ -2609,7 +2640,7 @@ if (!(bs = OCSP_response_get1_basic(rsp))) debug_printf("certs contained in basicresp:\n"); x509_stack_dump_cert_s_names( -#ifdef EXIM_HAVE_OPESSL_OCSP_RESP_GET0_CERTS +#ifdef EXIM_HAVE_OPENSSL_OCSP_RESP_GET0_CERTS OCSP_resp_get0_certs(bs) #else bs->certs @@ -3467,7 +3498,7 @@ static uschar peerdn[256]; if (tls_in.active.sock >= 0) { tls_error(US"STARTTLS received after TLS started", NULL, US"", errstr); - smtp_printf("554 Already in TLS\r\n", FALSE); + smtp_printf("554 Already in TLS\r\n", SP_NO_MORE); return FAIL; } @@ -3587,7 +3618,7 @@ mode, the fflush() happens when smtp_getc() is called. */ SSL_set_session_id_context(ssl, sid_ctx, Ustrlen(sid_ctx)); if (!tls_in.on_connect) { - smtp_printf("220 TLS go ahead\r\n", FALSE); + smtp_printf("220 TLS go ahead\r\n", SP_NO_MORE); fflush(smtp_out); } @@ -3903,7 +3934,7 @@ if (tlsp->host_resumable) tlsp->resumption |= RESUME_CLIENT_REQUESTED; DEBUG(D_tls) debug_printf("checking for resumable session for %s\n", tlsp->resume_index); - if ((dbm_file = dbfn_open(US"tls", O_RDWR, &dbblock, FALSE, FALSE))) + if ((dbm_file = dbfn_open(US"tls", O_RDWR|O_CREAT, &dbblock, FALSE, FALSE))) { if ((dt = dbfn_read_with_length(dbm_file, tlsp->resume_index, &len))) { @@ -3986,7 +4017,7 @@ if (SSL_SESSION_is_resumable(ss)) /* 1.1.1 */ dt->ocsp = tlsp->ocsp; (void) i2d_SSL_SESSION(ss, &s); /* s gets bumped to end */ - if ((dbm_file = dbfn_open(US"tls", O_RDWR, &dbblock, FALSE, FALSE))) + if ((dbm_file = dbfn_open(US"tls", O_RDWR|O_CREAT, &dbblock, FALSE, FALSE))) { dbfn_write(dbm_file, tlsp->resume_index, dt, dlen); dbfn_close(dbm_file); @@ -4120,7 +4151,7 @@ tls_client_start(client_conn_ctx * cctx, smtp_connect_args * conn_args, host_item * host = conn_args->host; /* for msgs and option-tests */ transport_instance * tb = conn_args->tblock; /* always smtp or NULL */ smtp_transport_options_block * ob = tb - ? (smtp_transport_options_block *)tb->options_block + ? tb->drinst.options_block : &smtp_transport_option_defaults; exim_openssl_client_tls_ctx * exim_client_ctx; uschar * expciphers; @@ -4495,10 +4526,15 @@ switch(error) /* Handle genuine errors */ case SSL_ERROR_SSL: + { + uschar * conn_info = smtp_get_connection_info(); + if (Ustrncmp(conn_info, US"SMTP ", 5) == 0) conn_info += 5; + /* I'd like to get separated H= here, but too hard for now */ ERR_error_string_n(ERR_get_error(), ssl_errstring, sizeof(ssl_errstring)); - log_write(0, LOG_MAIN, "TLS error (SSL_read): %s", ssl_errstring); + log_write(0, LOG_MAIN, "TLS error (SSL_read): on %s %s", conn_info, ssl_errstring); ssl_xfer_error = TRUE; return FALSE; + } default: DEBUG(D_tls) debug_printf("Got SSL error %d\n", error); @@ -4509,7 +4545,7 @@ switch(error) } #ifndef DISABLE_DKIM -dkim_exim_verify_feed(ssl_xfer_buffer, inbytes); +smtp_verify_feed(ssl_xfer_buffer, inbytes); #endif ssl_xfer_buffer_hwm = inbytes; ssl_xfer_buffer_lwm = 0; @@ -4579,7 +4615,7 @@ int n = ssl_xfer_buffer_hwm - ssl_xfer_buffer_lwm; if (n > lim) n = lim; if (n > 0) - dkim_exim_verify_feed(ssl_xfer_buffer+ssl_xfer_buffer_lwm, n); + smtp_verify_feed(ssl_xfer_buffer+ssl_xfer_buffer_lwm, n); #endif } @@ -5125,8 +5161,7 @@ if (!expand_check(option_spec, US"openssl_options", &exp, &end)) for (uschar * s = exp; *s; /**/) { - while (isspace(*s)) ++s; - if (*s == '\0') + if (!Uskip_whitespace(&s)) break; if (*s != '+' && *s != '-') { @@ -5135,7 +5170,8 @@ for (uschar * s = exp; *s; /**/) return FALSE; } adding = *s++ == '+'; - for (end = s; *end && !isspace(*end); ) end++; + end = s; + Uskip_nonwhite(&end); item_parsed = tls_openssl_one_option_parse(string_copyn(s, end-s), &item); if (!item_parsed) {