From: Jeremy Harris Date: Sat, 7 Jan 2023 00:17:08 +0000 (+0000) Subject: OpenSSL: tls_eccurves list support. Bug 2955 X-Git-Tag: exim-4.97-RC0~147 X-Git-Url: https://git.exim.org/exim.git/commitdiff_plain/42f1855e94bd87f98bc6c74255be53ed6d805ba6 OpenSSL: tls_eccurves list support. Bug 2955 --- diff --git a/doc/doc-docbook/spec.xfpt b/doc/doc-docbook/spec.xfpt index 9243bd3f9..7c8dee36f 100644 --- a/doc/doc-docbook/spec.xfpt +++ b/doc/doc-docbook/spec.xfpt @@ -18454,20 +18454,25 @@ prior to the 4.80 release, as Debian used to patch Exim to raise the minimum acceptable bound from 1024 to 2048. -.option tls_eccurve main string&!! &`auto`& +.option tls_eccurve main string list&!! &`auto`& .cindex TLS "EC cryptography" -This option selects a EC curve for use by Exim when used with OpenSSL. +This option selects EC curves for use by Exim when used with OpenSSL. It has no effect when Exim is used with GnuTLS. -After expansion it must contain a valid EC curve parameter, such as -&`prime256v1`&, &`secp384r1`&, or &`P-512`&. Consult your OpenSSL manual -for valid selections. +After expansion it must contain +.new +one or (only for OpenSSL versiona 1.1.1 onwards) more +.wen +EC curve names, such as &`prime256v1`&, &`secp384r1`&, or &`P-521`&. +Consult your OpenSSL manual for valid curve names. For OpenSSL versions before (and not including) 1.0.2, the string &`auto`& selects &`prime256v1`&. For more recent OpenSSL versions &`auto`& tells the library to choose. -If the option expands to an empty string, no EC curves will be enabled. +.new +If the option expands to an empty string, the effect is undefined. +.wen .option tls_ocsp_file main string&!! unset diff --git a/doc/doc-txt/NewStuff b/doc/doc-txt/NewStuff index b00399511..c1e139e35 100644 --- a/doc/doc-txt/NewStuff +++ b/doc/doc-txt/NewStuff @@ -19,6 +19,8 @@ Version 4.97 5. The smtp transport option "max_rcpt" is now expanded before use. + 6. The tls_eccurve option for OpenSSL now takes a list of group names + Version 4.96 ------------ diff --git a/src/src/tls-openssl.c b/src/src/tls-openssl.c index 513ba0d3a..96be7c4a2 100644 --- a/src/src/tls-openssl.c +++ b/src/src/tls-openssl.c @@ -122,6 +122,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 +701,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 * ctx) +{ +#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 +766,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 +850,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; diff --git a/src/src/tls.c b/src/src/tls.c index 4a23aaae9..f7be5293d 100644 --- a/src/src/tls.c +++ b/src/src/tls.c @@ -106,7 +106,8 @@ Returns: TRUE if OK; result may still be NULL after forced failure */ static BOOL -expand_check(const uschar *s, const uschar *name, uschar **result, uschar ** errstr) +expand_check(const uschar * s, const uschar * name, + uschar ** result, uschar ** errstr) { if (!s) *result = NULL; diff --git a/test/confs/2149 b/test/confs/2149 index 3369288bb..1782391de 100644 --- a/test/confs/2149 +++ b/test/confs/2149 @@ -30,22 +30,13 @@ client: errors_to = "" server: - driver = accept - retry_use_local_part - transport = local_delivery - + driver = redirect + data = :blackhole: # ----- Transports ----- begin transports -local_delivery: - driver = appendfile - file = DIR/test-mail/$local_part - create_file = DIR/test-mail - headers_add = TLS: cipher=$tls_cipher peerdn=$tls_peerdn - user = CALLER - send_to_server: driver = smtp allow_localhost diff --git a/test/log/2149 b/test/log/2149 index 3832ba076..91b48eee4 100644 --- a/test/log/2149 +++ b/test/log/2149 @@ -5,41 +5,48 @@ 1999-03-02 09:44:33 10HmaZ-0005vi-00 => explicitauto@test.ex R=client T=send_to_server H=127.0.0.1 [127.0.0.1] X=TLS1.x:ke-RSA-AES256-SHAnnn:xxx CV=yes C="250 OK id=10HmbA-0005vi-00" 1999-03-02 09:44:33 10HmaZ-0005vi-00 Completed 1999-03-02 09:44:33 10HmbB-0005vi-00 <= CALLER@myhost.test.ex U=CALLER P=local S=sss -1999-03-02 09:44:33 10HmbB-0005vi-00 => explicitempty@test.ex R=client T=send_to_server H=127.0.0.1 [127.0.0.1] X=TLS1.x:ke-RSA-AES256-SHAnnn:xxx CV=yes C="250 OK id=10HmbC-0005vi-00" +1999-03-02 09:44:33 10HmbB-0005vi-00 => prime256v1@test.ex R=client T=send_to_server H=127.0.0.1 [127.0.0.1] X=TLS1.x:ke-RSA-AES256-SHAnnn:xxx CV=yes C="250 OK id=10HmbC-0005vi-00" 1999-03-02 09:44:33 10HmbB-0005vi-00 Completed 1999-03-02 09:44:33 10HmbD-0005vi-00 <= CALLER@myhost.test.ex U=CALLER P=local S=sss -1999-03-02 09:44:33 10HmbD-0005vi-00 => prime256v1@test.ex R=client T=send_to_server H=127.0.0.1 [127.0.0.1] X=TLS1.x:ke-RSA-AES256-SHAnnn:xxx CV=yes C="250 OK id=10HmbE-0005vi-00" +1999-03-02 09:44:33 10HmbD-0005vi-00 => secp384r1@test.ex R=client T=send_to_server H=127.0.0.1 [127.0.0.1] X=TLS1.x:ke-RSA-AES256-SHAnnn:xxx CV=yes C="250 OK id=10HmbE-0005vi-00" 1999-03-02 09:44:33 10HmbD-0005vi-00 Completed 1999-03-02 09:44:33 10HmbF-0005vi-00 <= CALLER@myhost.test.ex U=CALLER P=local S=sss -1999-03-02 09:44:33 10HmbF-0005vi-00 => secp384r1@test.ex R=client T=send_to_server H=127.0.0.1 [127.0.0.1] X=TLS1.x:ke-RSA-AES256-SHAnnn:xxx CV=yes C="250 OK id=10HmbG-0005vi-00" +1999-03-02 09:44:33 10HmbF-0005vi-00 H=127.0.0.1 [127.0.0.1]: a TLS session is required, but an attempt to start TLS failed +1999-03-02 09:44:33 10HmbF-0005vi-00 == user_fail@test.ex R=client T=send_to_server defer (-38) H=127.0.0.1 [127.0.0.1]: a TLS session is required, but an attempt to start TLS failed +1999-03-02 09:44:33 10HmbF-0005vi-00 ** user_fail@test.ex: retry timeout exceeded +1999-03-02 09:44:33 10HmbF-0005vi-00 user_fail@test.ex: error ignored 1999-03-02 09:44:33 10HmbF-0005vi-00 Completed -1999-03-02 09:44:33 10HmbH-0005vi-00 <= CALLER@myhost.test.ex U=CALLER P=local S=sss -1999-03-02 09:44:33 10HmbH-0005vi-00 H=127.0.0.1 [127.0.0.1]: a TLS session is required, but an attempt to start TLS failed -1999-03-02 09:44:33 10HmbH-0005vi-00 == user_fail@test.ex R=client T=send_to_server defer (-38) H=127.0.0.1 [127.0.0.1]: a TLS session is required, but an attempt to start TLS failed -1999-03-02 09:44:33 10HmbH-0005vi-00 ** user_fail@test.ex: retry timeout exceeded -1999-03-02 09:44:33 10HmbH-0005vi-00 user_fail@test.ex: error ignored -1999-03-02 09:44:33 10HmbH-0005vi-00 Completed +1999-03-02 09:44:33 10HmbG-0005vi-00 <= CALLER@myhost.test.ex U=CALLER P=local S=sss +1999-03-02 09:44:33 10HmbG-0005vi-00 => user_list2@test.ex R=client T=send_to_server H=127.0.0.1 [127.0.0.1] X=TLS1.x:ke-RSA-AES256-SHAnnn:xxx CV=yes C="250 OK id=10HmbH-0005vi-00" +1999-03-02 09:44:33 10HmbG-0005vi-00 Completed +1999-03-02 09:44:33 10HmbI-0005vi-00 <= CALLER@myhost.test.ex U=CALLER P=local S=sss +1999-03-02 09:44:33 10HmbI-0005vi-00 => user_list_auto@test.ex R=client T=send_to_server H=127.0.0.1 [127.0.0.1] X=TLS1.x:ke-RSA-AES256-SHAnnn:xxx CV=yes C="250 OK id=10HmbJ-0005vi-00" +1999-03-02 09:44:33 10HmbI-0005vi-00 Completed ******** SERVER ******** 1999-03-02 09:44:33 exim x.yz daemon started: pid=p1234, no queue runs, listening for SMTP on port PORT_D 1999-03-02 09:44:33 10HmaY-0005vi-00 <= <> H=localhost (myhost.test.ex) [127.0.0.1] P=esmtps X=TLS1.x:ke-RSA-AES256-SHAnnn:xxx CV=no S=sss id=E10HmaX-0005vi-00@myhost.test.ex -1999-03-02 09:44:33 10HmaY-0005vi-00 => optnotpresent R=server T=local_delivery +1999-03-02 09:44:33 10HmaY-0005vi-00 => :blackhole: R=server 1999-03-02 09:44:33 10HmaY-0005vi-00 Completed 1999-03-02 09:44:33 exim x.yz daemon started: pid=p1235, no queue runs, listening for SMTP on port PORT_D 1999-03-02 09:44:33 10HmbA-0005vi-00 <= <> H=localhost (myhost.test.ex) [127.0.0.1] P=esmtps X=TLS1.x:ke-RSA-AES256-SHAnnn:xxx CV=no S=sss id=E10HmaZ-0005vi-00@myhost.test.ex -1999-03-02 09:44:33 10HmbA-0005vi-00 => explicitauto R=server T=local_delivery +1999-03-02 09:44:33 10HmbA-0005vi-00 => :blackhole: R=server 1999-03-02 09:44:33 10HmbA-0005vi-00 Completed 1999-03-02 09:44:33 exim x.yz daemon started: pid=p1236, no queue runs, listening for SMTP on port PORT_D 1999-03-02 09:44:33 10HmbC-0005vi-00 <= <> H=localhost (myhost.test.ex) [127.0.0.1] P=esmtps X=TLS1.x:ke-RSA-AES256-SHAnnn:xxx CV=no S=sss id=E10HmbB-0005vi-00@myhost.test.ex -1999-03-02 09:44:33 10HmbC-0005vi-00 => explicitempty R=server T=local_delivery +1999-03-02 09:44:33 10HmbC-0005vi-00 => :blackhole: R=server 1999-03-02 09:44:33 10HmbC-0005vi-00 Completed 1999-03-02 09:44:33 exim x.yz daemon started: pid=p1237, no queue runs, listening for SMTP on port PORT_D 1999-03-02 09:44:33 10HmbE-0005vi-00 <= <> H=localhost (myhost.test.ex) [127.0.0.1] P=esmtps X=TLS1.x:ke-RSA-AES256-SHAnnn:xxx CV=no S=sss id=E10HmbD-0005vi-00@myhost.test.ex -1999-03-02 09:44:33 10HmbE-0005vi-00 => prime256v1 R=server T=local_delivery +1999-03-02 09:44:33 10HmbE-0005vi-00 => :blackhole: R=server 1999-03-02 09:44:33 10HmbE-0005vi-00 Completed 1999-03-02 09:44:33 exim x.yz daemon started: pid=p1238, no queue runs, listening for SMTP on port PORT_D -1999-03-02 09:44:33 10HmbG-0005vi-00 <= <> H=localhost (myhost.test.ex) [127.0.0.1] P=esmtps X=TLS1.x:ke-RSA-AES256-SHAnnn:xxx CV=no S=sss id=E10HmbF-0005vi-00@myhost.test.ex -1999-03-02 09:44:33 10HmbG-0005vi-00 => secp384r1 R=server T=local_delivery -1999-03-02 09:44:33 10HmbG-0005vi-00 Completed +1999-03-02 09:44:33 TLS error on connection from localhost (myhost.test.ex) [127.0.0.1] Unknown curve name in tls_eccurve 'bogus' 1999-03-02 09:44:33 exim x.yz daemon started: pid=p1239, no queue runs, listening for SMTP on port PORT_D -1999-03-02 09:44:33 TLS error on connection from localhost (myhost.test.ex) [127.0.0.1] Unknown curve name tls_eccurve 'bogus' +1999-03-02 09:44:33 10HmbH-0005vi-00 <= <> H=localhost (myhost.test.ex) [127.0.0.1] P=esmtps X=TLS1.x:ke-RSA-AES256-SHAnnn:xxx CV=no S=sss id=E10HmbG-0005vi-00@myhost.test.ex +1999-03-02 09:44:33 10HmbH-0005vi-00 => :blackhole: R=server +1999-03-02 09:44:33 10HmbH-0005vi-00 Completed +1999-03-02 09:44:33 exim x.yz daemon started: pid=p1240, no queue runs, listening for SMTP on port PORT_D +1999-03-02 09:44:33 10HmbJ-0005vi-00 <= <> H=localhost (myhost.test.ex) [127.0.0.1] P=esmtps X=TLS1.x:ke-RSA-AES256-SHAnnn:xxx CV=no S=sss id=E10HmbI-0005vi-00@myhost.test.ex +1999-03-02 09:44:33 10HmbJ-0005vi-00 => :blackhole: R=server +1999-03-02 09:44:33 10HmbJ-0005vi-00 Completed diff --git a/test/scripts/2100-OpenSSL/2149 b/test/scripts/2100-OpenSSL/2149 index f1af49907..18b43bd5e 100644 --- a/test/scripts/2100-OpenSSL/2149 +++ b/test/scripts/2100-OpenSSL/2149 @@ -17,16 +17,8 @@ exim -odf explicitauto@test.ex **** killdaemon # -# Explicit tls_eccurve setting of "" -# - unclear this works. At least with OpenSSL 3.0.5 we still get an x25519 keyshare in the Server Hello -exim -DSERVER=server -DDATA= -bd -oX PORT_D -**** -exim -odf explicitempty@test.ex -**** -killdaemon -# # prime256v1 -# Oddly, 3.0.5 packets show an EC-groups negotiation of C:x255519 S:secp256r1 C:secp384r1 S:secp384r1. +# Oddly, 3.0.5 packets show an EC-groups negotiation of C:x255519 S:secp256r1 C:secp256r1 S:secp256r1. # Hoever, note that RFC 8446 (TLS1.3) does NOT include prime256v1 as one of the allowable # supported groups (and it's not in the client "supported groups" extension, so what we see seems good. exim -DSERVER=server -DDATA=prime256v1 -bd -oX PORT_D @@ -50,5 +42,20 @@ exim -odf user_fail@test.ex **** killdaemon # +# Two-element list - will fail for pre- 1.1.1 OpenSSL +# - the Hello Retry Req goes out with the earliest one from the list which matches the client's Supported Groups +exim -DSERVER=server -DDATA=P-521:secp384r1 -bd -oX PORT_D +**** +exim -odf user_list2@test.ex +**** +killdaemon +# +# +# List with an "auto" element embedded, which should override. +exim -DSERVER=server '-DDATA= P-521 : P-384 : auto : P-256' -bd -oX PORT_D +**** +exim -odf user_list_auto@test.ex +**** +killdaemon # no_message_check