Gitweb:
https://git.exim.org/exim.git/commitdiff/42f1855e94bd87f98bc6c74255be53ed6d805ba6
Commit: 42f1855e94bd87f98bc6c74255be53ed6d805ba6
Parent: 31c546c4d0c3baf1b1e0ab292b4d096cffe64c34
Author: Jeremy Harris <jgh146exb@???>
AuthorDate: Sat Jan 7 00:17:08 2023 +0000
Committer: Jeremy Harris <jgh146exb@???>
CommitDate: Sat Jan 7 16:00:19 2023 +0000
OpenSSL: tls_eccurves list support. Bug 2955
---
doc/doc-docbook/spec.xfpt | 17 +++--
doc/doc-txt/NewStuff | 2 +
src/src/tls-openssl.c | 146 +++++++++++++++++++++++++----------------
src/src/tls.c | 3 +-
test/confs/2149 | 13 +---
test/log/2149 | 41 +++++++-----
test/scripts/2100-OpenSSL/2149 | 25 ++++---
7 files changed, 146 insertions(+), 101 deletions(-)
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@??? 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@??? U=CALLER P=local S=sss
-1999-03-02 09:44:33 10HmbB-0005vi-00 => explicitempty@??? 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@??? 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@??? U=CALLER P=local S=sss
-1999-03-02 09:44:33 10HmbD-0005vi-00 => prime256v1@??? 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@??? 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@??? U=CALLER P=local S=sss
-1999-03-02 09:44:33 10HmbF-0005vi-00 => secp384r1@??? 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@??? 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@???: retry timeout exceeded
+1999-03-02 09:44:33 10HmbF-0005vi-00 user_fail@???: error ignored
1999-03-02 09:44:33 10HmbF-0005vi-00 Completed
-1999-03-02 09:44:33 10HmbH-0005vi-00 <= CALLER@??? 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@??? 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@???: retry timeout exceeded
-1999-03-02 09:44:33 10HmbH-0005vi-00 user_fail@???: error ignored
-1999-03-02 09:44:33 10HmbH-0005vi-00 Completed
+1999-03-02 09:44:33 10HmbG-0005vi-00 <= CALLER@??? U=CALLER P=local S=sss
+1999-03-02 09:44:33 10HmbG-0005vi-00 => user_list2@??? 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@??? U=CALLER P=local S=sss
+1999-03-02 09:44:33 10HmbI-0005vi-00 => user_list_auto@??? 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@???
-1999-03-02 09:44:33 10HmaY-0005vi-00 => optnotpresent <optnotpresent@???> R=server T=local_delivery
+1999-03-02 09:44:33 10HmaY-0005vi-00 => :blackhole: <optnotpresent@???> 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@???
-1999-03-02 09:44:33 10HmbA-0005vi-00 => explicitauto <explicitauto@???> R=server T=local_delivery
+1999-03-02 09:44:33 10HmbA-0005vi-00 => :blackhole: <explicitauto@???> 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@???
-1999-03-02 09:44:33 10HmbC-0005vi-00 => explicitempty <explicitempty@???> R=server T=local_delivery
+1999-03-02 09:44:33 10HmbC-0005vi-00 => :blackhole: <prime256v1@???> 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@???
-1999-03-02 09:44:33 10HmbE-0005vi-00 => prime256v1 <prime256v1@???> R=server T=local_delivery
+1999-03-02 09:44:33 10HmbE-0005vi-00 => :blackhole: <secp384r1@???> 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@???
-1999-03-02 09:44:33 10HmbG-0005vi-00 => secp384r1 <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@???
+1999-03-02 09:44:33 10HmbH-0005vi-00 => :blackhole: <user_list2@???> 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@???
+1999-03-02 09:44:33 10HmbJ-0005vi-00 => :blackhole: <user_list_auto@???> 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@???
****
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@???
-****
-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@???
****
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@???
+****
+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@???
+****
+killdaemon
#
no_message_check