From: Wietse Z Venema
When Postfix TLSRPT support is enabled (with "smtp_tlsrpt_enable -= yes"), the Postfix SMTP and TLS clients engines will generate -"success" and "failure" events, and will pass those events to a -TLSRPT client library and report generator that are maintained by -sys4. The Postfix implementation supports both DANE (Postfix built-in) -and MTA-STS (through an smtp_tls_policy_maps plug-in).
+A policy example looks like this:
-The high-level diagram shows how Postfix events are reported -to domains that publish a TLSRPT policy. +
++ ++_smtp._tls.example.com. IN TXT "v=TLSRPTv1; rua=mailto:smtp-tls-report@example.com" ++
Translation: email sending systems are requested to generate daily +summaries of successful and failed SMTP over TLS connections to domain +example.com, and to report those summaries via email to the +specified address. Instead of mailto:, a policy may specify an +https: destination.
+ +The high-level diagram shows how Postfix reports summaries to +domains that publish a TLSRPT policy.
@@ -67,8 +74,15 @@ to domains that publish a TLSRPT policy.+
When Postfix TLSRPT support is enabled (with "smtp_tlsrpt_enable += yes"), the Postfix SMTP and TLS clients engines will generate +"success" and "failure" events, and will pass those events to a +TLSRPT client library and report generator that are maintained by +sys4. The Postfix implementation supports both DANE (Postfix built-in) +and MTA-STS (through an smtp_tls_policy_maps plug-in).
+The Postfix smtp(8) client process implements the SMTP client -engine. With "smtp_tls_connection_reuse = no", the smtp(8) client +engine. With "smtp_tls_connection_reuse = no", the smtp(8) client process also implements the TLS client engine. With "smtp_tls_connection_reuse = yes", the smtp(8) client process delegates TLS processing to a Postfix tlsproxy(8) process. Either @@ -130,32 +144,64 @@ support in main.cf like this:
-The smtp_tlsrpt_socket_name parameter specifies an absolute -pathname, or a pathname that is relative to $queue_directory.
+The smtp_tlsrpt_socket_name parameter specifies an +absolute pathname, or a pathname that is relative to +$queue_directory.
-A good socket location would be under $queue_directory/run/tlsrpt -or $queue_directory/var/run/tlsrpt. These can then be configured -in Postfix as a relative pathname (run/tlsrpt/tlsrpt.sock or -var/run/tlsrpt/tlsrpt.sock) so that the same name will work with -and without Postfix chroot support. Do not specify a location under -directory that is already used by Postfix programs. Only Postfix +
+ ++ +Note: the socket location is still to be determined. A good +socket location would be under $queue_directory, for +example: (smtp_tlsrpt_socket_name = run/tlsrpt/tlsrpt.sock +or smtp_tlsrpt_socket_name = var/run/tlsrpt/tlsrpt.sock). +Such names will work with and without Postfix chroot support. Do +not specify a location under a directory (private, public, +...) that is already used by Postfix programs. Only Postfix programs should create sockets there.
+
For obvious reasons, RFC 8460 suggests not to enforce strict +TLS security when sending daily success/failure summaries via email. Postfix +currently does not have a mechanism to request this when submitting +an email message. For initial tests a transport map may take care +of this.
+ +++ ++/etc/postfix/main.cf: + transport_maps = hash:/etc/postfix/transport +  +/etc/postfix/transport: + /^\Qsts-reports@gmail.com\E$/ allow-plaintext + /^\Qsmtp-tls-report@sys4.de\E$/ allow-plaintext + ... +  +/etc/postfix/master.cf: + allow-plaintext .. .. .. .. .. .. .. smtp + -o smtp_tls_security_level=may + -o smtp_tls_policy_maps= ++
The ^ and $ prevent false matches, and the +\Q and \E disable special characters.
+Postfix supports MTA-STS though an smtp_tls_policy_maps policy -plugin. Postfix TLSRPT support expects a response with the usual -security level and matching requirements, plus any applicable -name=value attributes described below. Specify { name = value } -when a value may contain whitespace.
+plugin. Postfix expects a response with the usual security level and +matching requirements, plus any applicable name=value attributes described +below. Specify { name = value } when a value may contain whitespace.-Note 1: Postfix 3.10 and later will accept these attributes in an MTA-STS response even if TLSRPT support is disabled (at build -time or run time), but it will not use most attributes except -ttl and policy_failure.
+time or run time). With TLSRPT support turned off, Postfix +will use the ttl and policy_failure attributes, +and will ignore the attributes that are used only for TLSRPT.Note 2: It is an error to specify these attributes for a non-STS policy.
@@ -179,7 +225,7 @@ given in htt+the MTA-STS plugin response. policy_ttl=time
How long (in seconds) a Postfix SMTP client process will cache -the MTA-STS plugin response.
{ policy_string = value }
@@ -235,29 +281,17 @@ policy_failure=sts-webpki-invalidLimitations
-The following limitations exist primarily because some errors -may be reported by a Postfix smtp(8) client process, and some errors -by a Postfix tlsproxy(8) process. It is too difficult to propagate -TLSRPT client library state between processes.
- -The Postfix TLSRPT client reports one final status (either -'success' or 'failure') for each MTA that it is able to connect to. -It cannot report a final status 'success' with some recoverable -'failure'. Specifically:
- -- -
+- - -
The Postfix TLSRPT client can report either successful TLS -policy compliance, or an unrecoverable failure that prevents TLS -policy compliance (examples: all TLSA records are unusable, or some -PKI error during certificate verification).
- - -
The Postfix TLSRPT client must not be used to report a -potentially recoverable failure such as a non-parsable TLSA record, -because some other TLSA record for the same host may still allow -successful TLS policy compliance.
The Postfix TLSRPT implementation reports at most one final TLS +handshake status (either 'success' or 'failure') per connection. +Postfix TLSRPT cannot report a failure and then later report a final +status of 'success' for that same connection. The reason is that +it's too complicated to filter TLS errors and to report error details +from the TLS engine back to the SMTP protocol engine. It just is +not how Postfix works internally.
+ +The Postfix TLSRPT implementation reports only TLS handshake +success or failure. It does not report failure to connect, or +connections that break after a successful TLS handshake.
Credits
diff --git a/postfix/html/index.html b/postfix/html/index.html index a7da6cb51..610fac87c 100644 --- a/postfix/html/index.html +++ b/postfix/html/index.html @@ -45,6 +45,8 @@ configuration examplesTLS Forward Secrecy + TLSRPT Protocol Support + IP Version 6 Support SMTPUTF8 Support diff --git a/postfix/html/postconf.5.html b/postfix/html/postconf.5.html index c254ab97c..3f9a53d75 100644 --- a/postfix/html/postconf.5.html +++ b/postfix/html/postconf.5.html @@ -20151,21 +20151,27 @@ be using 0.9.6! tls_eecdh_auto_curves (default: see "postconf -d" output) - The prioritized list of elliptic curves supported by the Postfix -SMTP client and server. These curves are used by the Postfix SMTP -server when "smtpd_tls_eecdh_grade = auto". The selected curves must be -implemented by OpenSSL and be standardized for use in TLS (RFC 8422). -It is unwise to list only "bleeding-edge" curves supported by a small -subset of clients. The default list is suitable for most users.
- -Postfix skips curve names that are unknown to OpenSSL, or that -are known but not yet implemented. This makes it possible to -"anticipate" support for curves that should be used once they become -available. In particular, in some OpenSSL versions, the new RFC -8031 curves "X25519" and "X448" may be known by name, but ECDH -support for either or both may be missing. These curves may appear -in the default value of this parameter, even though they'll only -be usable with later versions of OpenSSL.
+The prioritized list of elliptic curves, that should be enabled in the +Postfix SMTP client and server. These are used by the Postfix SMTP server when +"smtpd_tls_eecdh_grade = auto". The selected curves should be implemented +by OpenSSL and be standardized for use in the TLS "supported groups" extension +(RFC8422, RFC8446 and RFC8447). Be sure to include at least "x25519" and +"prime256v1" (the OpenSSL name for "secp256r1", a.k.a. "P-256"). The default +list is suitable for most users.
+ +On the client side, the first curve listed will be used to construct the +client's initial TLS 1.3 "keyshare". If this is not supported by the server, +the TLS handshake may require an additional round-trip after the server issues +a HelloRetryRequest (HRR) indicating a suitable mutually supported curve.
+ +Postfix skips curve names that are unknown to OpenSSL, or that are known +but not yet implemented. This makes it possible to "anticipate" support for +curves that should be used once they become available, or to deploy the same +setting on a server "farm" where not all servers support the same curves.
+ +As of Postfix 3.10, when compiled with OpenSSL 3.0 or later, the "curve" +names can be more general key encapsulation mechanisms (KEMs), and/or may be +loaded from an external "provider" (via a suitable tls_config_file).
See also the "tls_ffdhe_auto_groups" parameter, which supports customizing the list of FFDHE groups enabled with TLS 1.3. That setting @@ -20320,10 +20326,10 @@ EC key agreement in OpenSSL 3.0 and later. Note that at least one of this is required by OpenSSL 3.0. If both are inadvertently set empty, Postfix will fall back to the compiled-in defaults.
-All the default groups and EC curves should sufficiently strong -to make "pruning" the defaults unwise. At a minimum, "X25519" and -"P-256" (a.k.a. "prime256v1") should be among the enabled EC curves, -while "dhe2048" and "dhe3072" should be among the FFDHE groups.
+All the default groups and EC curves should sufficiently strong to make +"pruning" the defaults unwise. At a minimum, "x25519" and "prime256v1" (the +OpenSSL name for "secp256r1", a.k.a. "P-256") should be among the enabled EC +curves, while "dhe2048" and "dhe3072" should be among the FFDHE groups.
This feature is available in Postfix 3.8 and later, when it is compiled and linked with OpenSSL 3.0 or later.
diff --git a/postfix/man/man5/postconf.5 b/postfix/man/man5/postconf.5 index 05bf424aa..571e624bc 100644 --- a/postfix/man/man5/postconf.5 +++ b/postfix/man/man5/postconf.5 @@ -13999,21 +13999,27 @@ Postfix >= 3.4. See \fBSSL_CTX_set_options\fR(3). .PP This feature is available in Postfix 2.8 and later. .SH tls_eecdh_auto_curves (default: see "postconf \-d" output) -The prioritized list of elliptic curves supported by the Postfix -SMTP client and server. These curves are used by the Postfix SMTP -server when "smtpd_tls_eecdh_grade = auto". The selected curves must be -implemented by OpenSSL and be standardized for use in TLS (RFC 8422). -It is unwise to list only "bleeding\-edge" curves supported by a small -subset of clients. The default list is suitable for most users. -.PP -Postfix skips curve names that are unknown to OpenSSL, or that -are known but not yet implemented. This makes it possible to -"anticipate" support for curves that should be used once they become -available. In particular, in some OpenSSL versions, the new RFC -8031 curves "X25519" and "X448" may be known by name, but ECDH -support for either or both may be missing. These curves may appear -in the default value of this parameter, even though they'll only -be usable with later versions of OpenSSL. +The prioritized list of elliptic curves, that should be enabled in the +Postfix SMTP client and server. These are used by the Postfix SMTP server when +"smtpd_tls_eecdh_grade = auto". The selected curves should be implemented +by OpenSSL and be standardized for use in the TLS "supported groups" extension +(RFC8422, RFC8446 and RFC8447). Be sure to include at least "x25519" and +"prime256v1" (the OpenSSL name for "secp256r1", a.k.a. "P\-256"). The default +list is suitable for most users. +.PP +On the client side, the first curve listed will be used to construct the +client's initial TLS 1.3 "keyshare". If this is not supported by the server, +the TLS handshake may require an additional round\-trip after the server issues +a HelloRetryRequest (HRR) indicating a suitable mutually supported curve. +.PP +Postfix skips curve names that are unknown to OpenSSL, or that are known +but not yet implemented. This makes it possible to "anticipate" support for +curves that should be used once they become available, or to deploy the same +setting on a server "farm" where not all servers support the same curves. +.PP +As of Postfix 3.10, when compiled with OpenSSL 3.0 or later, the "curve" +names can be more general key encapsulation mechanisms (KEMs), and/or may be +loaded from an external "provider" (via a suitable tls_config_file). .PP See also the "tls_ffdhe_auto_groups" parameter, which supports customizing the list of FFDHE groups enabled with TLS 1.3. That setting @@ -14130,10 +14136,10 @@ EC key agreement in OpenSSL 3.0 and later. Note that at least one of this is required by OpenSSL 3.0. If both are inadvertently set empty, Postfix will fall back to the compiled\-in defaults. .PP -All the default groups and EC curves should sufficiently strong -to make "pruning" the defaults unwise. At a minimum, "X25519" and -"P\-256" (a.k.a. "prime256v1") should be among the enabled EC curves, -while "dhe2048" and "dhe3072" should be among the FFDHE groups. +All the default groups and EC curves should sufficiently strong to make +"pruning" the defaults unwise. At a minimum, "x25519" and "prime256v1" (the +OpenSSL name for "secp256r1", a.k.a. "P\-256") should be among the enabled EC +curves, while "dhe2048" and "dhe3072" should be among the FFDHE groups. .PP This feature is available in Postfix 3.8 and later, when it is compiled and linked with OpenSSL 3.0 or later. diff --git a/postfix/proto/TLSRPT_README.html b/postfix/proto/TLSRPT_README.html index f5f9f238e..e869a2e81 100644 --- a/postfix/proto/TLSRPT_README.html +++ b/postfix/proto/TLSRPT_README.html @@ -38,15 +38,22 @@ receiving domain can publish a policy in DNS to request daily summary reports for successful and failed TLS connections to that domain. Support for TLSRPT was added in Postfix 3.10. -When Postfix TLSRPT support is enabled (with "smtp_tlsrpt_enable -= yes"), the Postfix SMTP and TLS clients engines will generate -"success" and "failure" events, and will pass those events to a -TLSRPT client library and report generator that are maintained by -sys4. The Postfix implementation supports both DANE (Postfix built-in) -and MTA-STS (through an smtp_tls_policy_maps plug-in).
+A policy example looks like this:
-The high-level diagram shows how Postfix events are reported -to domains that publish a TLSRPT policy. +
++ ++_smtp._tls.example.com. IN TXT "v=TLSRPTv1; rua=mailto:smtp-tls-report@example.com" ++Translation: email sending systems are requested to generate daily +summaries of successful and failed SMTP over TLS connections to domain +example.com, and to report those summaries via email to the +specified address. Instead of mailto:, a policy may specify an +https: destination.
+ +The high-level diagram shows how Postfix reports summaries to +domains that publish a TLSRPT policy.
@@ -67,8 +74,15 @@ to domains that publish a TLSRPT policy.+When Postfix TLSRPT support is enabled (with "smtp_tlsrpt_enable += yes"), the Postfix SMTP and TLS clients engines will generate +"success" and "failure" events, and will pass those events to a +TLSRPT client library and report generator that are maintained by +sys4. The Postfix implementation supports both DANE (Postfix built-in) +and MTA-STS (through an smtp_tls_policy_maps plug-in).
+The Postfix smtp(8) client process implements the SMTP client -engine. With "smtp_tls_connection_reuse = no", the smtp(8) client +engine. With "smtp_tls_connection_reuse = no", the smtp(8) client process also implements the TLS client engine. With "smtp_tls_connection_reuse = yes", the smtp(8) client process delegates TLS processing to a Postfix tlsproxy(8) process. Either @@ -130,32 +144,64 @@ smtp_tlsrpt_socket_name = /path/to/socket
The smtp_tlsrpt_socket_name parameter specifies an absolute -pathname, or a pathname that is relative to $queue_directory.
+The smtp_tlsrpt_socket_name parameter specifies an +absolute pathname, or a pathname that is relative to +$queue_directory.
-A good socket location would be under $queue_directory/run/tlsrpt -or $queue_directory/var/run/tlsrpt. These can then be configured -in Postfix as a relative pathname (run/tlsrpt/tlsrpt.sock or -var/run/tlsrpt/tlsrpt.sock) so that the same name will work with -and without Postfix chroot support. Do not specify a location under -directory that is already used by Postfix programs. Only Postfix +
+ ++ +Note: the socket location is still to be determined. A good +socket location would be under $queue_directory, for +example: (smtp_tlsrpt_socket_name = run/tlsrpt/tlsrpt.sock +or smtp_tlsrpt_socket_name = var/run/tlsrpt/tlsrpt.sock). +Such names will work with and without Postfix chroot support. Do +not specify a location under a directory (private, public, +...) that is already used by Postfix programs. Only Postfix programs should create sockets there.
+
For obvious reasons, RFC 8460 suggests not to enforce strict +TLS security when sending daily success/failure summaries via email. Postfix +currently does not have a mechanism to request this when submitting +an email message. For initial tests a transport map may take care +of this.
+ +++ ++/etc/postfix/main.cf: + transport_maps = hash:/etc/postfix/transport +  +/etc/postfix/transport: + /^\Qsts-reports@gmail.com\E$/ allow-plaintext + /^\Qsmtp-tls-report@sys4.de\E$/ allow-plaintext + ... +  +/etc/postfix/master.cf: + allow-plaintext .. .. .. .. .. .. .. smtp + -o smtp_tls_security_level=may + -o smtp_tls_policy_maps= ++
The ^ and $ prevent false matches, and the +\Q and \E disable special characters.
+Postfix supports MTA-STS though an smtp_tls_policy_maps policy -plugin. Postfix TLSRPT support expects a response with the usual -security level and matching requirements, plus any applicable -name=value attributes described below. Specify { name = value } -when a value may contain whitespace.
+plugin. Postfix expects a response with the usual security level and +matching requirements, plus any applicable name=value attributes described +below. Specify { name = value } when a value may contain whitespace.Note 1: Postfix 3.10 and later will accept these attributes in an MTA-STS response even if TLSRPT support is disabled (at build -time or run time), but it will not use most attributes except -ttl and policy_failure.
+time or run time). With TLSRPT support turned off, Postfix +will use the ttl and policy_failure attributes, +and will ignore the attributes that are used only for TLSRPT.Note 2: It is an error to specify these attributes for a non-STS policy.
@@ -179,7 +225,7 @@ given in https://datatracker.ietf.org/doc/html/rfc8460#section-4.5.+the MTA-STS plugin response. policy_ttl=time
How long (in seconds) a Postfix SMTP client process will cache -the MTA-STS plugin response.
{ policy_string = value }
@@ -235,29 +281,17 @@ policy_failure=sts-webpki-invalidLimitations
-The following limitations exist primarily because some errors -may be reported by a Postfix smtp(8) client process, and some errors -by a Postfix tlsproxy(8) process. It is too difficult to propagate -TLSRPT client library state between processes.
- -The Postfix TLSRPT client reports one final status (either -'success' or 'failure') for each MTA that it is able to connect to. -It cannot report a final status 'success' with some recoverable -'failure'. Specifically:
- -- -
+- - -
The Postfix TLSRPT client can report either successful TLS -policy compliance, or an unrecoverable failure that prevents TLS -policy compliance (examples: all TLSA records are unusable, or some -PKI error during certificate verification).
- - -
The Postfix TLSRPT client must not be used to report a -potentially recoverable failure such as a non-parsable TLSA record, -because some other TLSA record for the same host may still allow -successful TLS policy compliance.
The Postfix TLSRPT implementation reports at most one final TLS +handshake status (either 'success' or 'failure') per connection. +Postfix TLSRPT cannot report a failure and then later report a final +status of 'success' for that same connection. The reason is that +it's too complicated to filter TLS errors and to report error details +from the TLS engine back to the SMTP protocol engine. It just is +not how Postfix works internally.
+ +The Postfix TLSRPT implementation reports only TLS handshake +success or failure. It does not report failure to connect, or +connections that break after a successful TLS handshake.
Credits
diff --git a/postfix/proto/postconf.proto b/postfix/proto/postconf.proto index c8704c0d0..92cdba2c1 100644 --- a/postfix/proto/postconf.proto +++ b/postfix/proto/postconf.proto @@ -13364,21 +13364,27 @@ parameter. See there for details. %PARAM tls_eecdh_auto_curves see "postconf -d" output -The prioritized list of elliptic curves supported by the Postfix -SMTP client and server. These curves are used by the Postfix SMTP -server when "smtpd_tls_eecdh_grade = auto". The selected curves must be -implemented by OpenSSL and be standardized for use in TLS (RFC 8422). -It is unwise to list only "bleeding-edge" curves supported by a small -subset of clients. The default list is suitable for most users.
- -Postfix skips curve names that are unknown to OpenSSL, or that -are known but not yet implemented. This makes it possible to -"anticipate" support for curves that should be used once they become -available. In particular, in some OpenSSL versions, the new RFC -8031 curves "X25519" and "X448" may be known by name, but ECDH -support for either or both may be missing. These curves may appear -in the default value of this parameter, even though they'll only -be usable with later versions of OpenSSL.
+The prioritized list of elliptic curves, that should be enabled in the +Postfix SMTP client and server. These are used by the Postfix SMTP server when +"smtpd_tls_eecdh_grade = auto". The selected curves should be implemented +by OpenSSL and be standardized for use in the TLS "supported groups" extension +(RFC8422, RFC8446 and RFC8447). Be sure to include at least "x25519" and +"prime256v1" (the OpenSSL name for "secp256r1", a.k.a. "P-256"). The default +list is suitable for most users.
+ +On the client side, the first curve listed will be used to construct the +client's initial TLS 1.3 "keyshare". If this is not supported by the server, +the TLS handshake may require an additional round-trip after the server issues +a HelloRetryRequest (HRR) indicating a suitable mutually supported curve.
+ +Postfix skips curve names that are unknown to OpenSSL, or that are known +but not yet implemented. This makes it possible to "anticipate" support for +curves that should be used once they become available, or to deploy the same +setting on a server "farm" where not all servers support the same curves.
+ +As of Postfix 3.10, when compiled with OpenSSL 3.0 or later, the "curve" +names can be more general key encapsulation mechanisms (KEMs), and/or may be +loaded from an external "provider" (via a suitable tls_config_file).
See also the "tls_ffdhe_auto_groups" parameter, which supports customizing the list of FFDHE groups enabled with TLS 1.3. That setting @@ -13420,10 +13426,10 @@ EC key agreement in OpenSSL 3.0 and later. Note that at least one of this is required by OpenSSL 3.0. If both are inadvertently set empty, Postfix will fall back to the compiled-in defaults.
-All the default groups and EC curves should sufficiently strong -to make "pruning" the defaults unwise. At a minimum, "X25519" and -"P-256" (a.k.a. "prime256v1") should be among the enabled EC curves, -while "dhe2048" and "dhe3072" should be among the FFDHE groups.
+All the default groups and EC curves should sufficiently strong to make +"pruning" the defaults unwise. At a minimum, "x25519" and "prime256v1" (the +OpenSSL name for "secp256r1", a.k.a. "P-256") should be among the enabled EC +curves, while "dhe2048" and "dhe3072" should be among the FFDHE groups.
This feature is available in Postfix 3.8 and later, when it is compiled and linked with OpenSSL 3.0 or later.
diff --git a/postfix/proto/stop b/postfix/proto/stop index 171198f88..20f62cf10 100644 --- a/postfix/proto/stop +++ b/postfix/proto/stop @@ -1615,6 +1615,14 @@ milterfrom canonicalization Orlitzky Typofix +Deduplicate +KEM +HelloRetryRequest +HRR +KEMs +kex +keyshare +pkg RPT TLSRPT TLSRPTv @@ -1640,3 +1648,4 @@ RPC datatracker webpki parsable +mailto diff --git a/postfix/proto/stop.double-cc b/postfix/proto/stop.double-cc index 62bac0323..d0e35864a 100644 --- a/postfix/proto/stop.double-cc +++ b/postfix/proto/stop.double-cc @@ -339,6 +339,6 @@ address address string length policies policy policy type policies policy policy string Ignored if the tls_policy_type policies policy policy domain -additional_detail additional_detail +additional_info additional_info ignored ignored USE_TLSRPT USE_TLSRPT diff --git a/postfix/proto/stop.double-history b/postfix/proto/stop.double-history index 3e1bff20c..70a09313a 100644 --- a/postfix/proto/stop.double-history +++ b/postfix/proto/stop.double-history @@ -130,3 +130,4 @@ proto proto mysql_table proto pgsql_table proto ldap_table unimplemented commands in the SMTP server File smtpd smtpd c cleanup cleanup h cleanup cleanup_extracted c File postcat postcat c + Files src tls tls h src tls tls_dh c src tls tls_misc c diff --git a/postfix/proto/stop.double-proto-html b/postfix/proto/stop.double-proto-html index fbf3bda6b..0c6b3e7aa 100644 --- a/postfix/proto/stop.double-proto-html +++ b/postfix/proto/stop.double-proto-html @@ -359,4 +359,4 @@ Postfix Postfix can use MongoDB as a source for any of its lookups aliases 5 vi CCARGS CCARGS DHAS_MONGODB I usr include libmongoc 1 0 dt dt dd 2 Also enable verbose logging in the Postfix TLS Postfix Postfix legacy TLS Support - A good socket location would be under queue_directory run tlsrpt or queue_directory var run tlsrpt These can then be configured in Postfix as a relative pathname run tlsrpt tlsrpt sock or var run tlsrpt tlsrpt sock so that the same name will + Note the socket location is still to be determined A good socket location would be under queue_directory for example smtp_tlsrpt_socket_name run tlsrpt tlsrpt sock or smtp_tlsrpt_socket_name var run tlsrpt tlsrpt sock Such names diff --git a/postfix/proto/stop.spell-proto-html b/postfix/proto/stop.spell-proto-html index 65f9fa062..0bb4f3963 100644 --- a/postfix/proto/stop.spell-proto-html +++ b/postfix/proto/stop.spell-proto-html @@ -384,3 +384,6 @@ sts STS STSv Sys +Qsmtp +Qsts +gmail diff --git a/postfix/src/global/mail_params.h b/postfix/src/global/mail_params.h index 8b52145b0..70add837c 100644 --- a/postfix/src/global/mail_params.h +++ b/postfix/src/global/mail_params.h @@ -3409,13 +3409,13 @@ extern char *var_tls_null_clist; #else #define DEF_TLS_EECDH_AUTO_3 "" #endif -#if defined(SN_secp521r1) && defined(NID_secp521r1) -#define DEF_TLS_EECDH_AUTO_4 SN_secp521r1 " " +#if defined(SN_secp384r1) && defined(NID_secp384r1) +#define DEF_TLS_EECDH_AUTO_4 SN_secp384r1 #else #define DEF_TLS_EECDH_AUTO_4 "" #endif -#if defined(SN_secp384r1) && defined(NID_secp384r1) -#define DEF_TLS_EECDH_AUTO_5 SN_secp384r1 +#if defined(SN_secp521r1) && defined(NID_secp521r1) +#define DEF_TLS_EECDH_AUTO_5 SN_secp521r1 " " #else #define DEF_TLS_EECDH_AUTO_5 "" #endif diff --git a/postfix/src/global/mail_version.h b/postfix/src/global/mail_version.h index 52a5cc1ad..95fbd9bfd 100644 --- a/postfix/src/global/mail_version.h +++ b/postfix/src/global/mail_version.h @@ -20,7 +20,7 @@ * Patches change both the patchlevel and the release date. Snapshots have no * patchlevel; they change the release date only. */ -#define MAIL_RELEASE_DATE "20240917" +#define MAIL_RELEASE_DATE "20240923" #define MAIL_VERSION_NUMBER "3.10" #ifdef SNAPSHOT diff --git a/postfix/src/posttls-finger/posttls-finger.c b/postfix/src/posttls-finger/posttls-finger.c index 193588ef4..75571f7f2 100644 --- a/postfix/src/posttls-finger/posttls-finger.c +++ b/postfix/src/posttls-finger/posttls-finger.c @@ -836,7 +836,7 @@ static int starttls(STATE *state) matchargv = state->match, mdalg = state->mdalg, tlsrpt = 0, - fail_type = 0, + ffail_type = 0, dane = state->ddane ? state->ddane : state->dane); @@ -942,7 +942,7 @@ static int starttls(STATE *state) matchargv = state->match, mdalg = state->mdalg, tlsrpt = 0, - fail_type = 0, + ffail_type = 0, dane = state->ddane ? state->ddane : state->dane); } /* tlsproxy_mode */ vstring_free(cipher_exclusions); diff --git a/postfix/src/smtp/smtp_connect.c b/postfix/src/smtp/smtp_connect.c index df26d64ba..ea943fe77 100644 --- a/postfix/src/smtp/smtp_connect.c +++ b/postfix/src/smtp/smtp_connect.c @@ -1092,6 +1092,12 @@ static void smtp_connect_inet(SMTP_STATE *state, const char *nexthop, session->state = state; #ifdef USE_TLS session->tls_nexthop = domain; + + /* + * Update TLSRPT state even if this is a reused SMTP + * connection. If for some unlikely reason we must report a + * problem, then we must report correct information. + */ #ifdef USE_TLSRPT if (state->tlsrpt && state->tls->level > TLS_LEV_NONE) { smtp_tlsrpt_set_tls_policy(state); diff --git a/postfix/src/smtp/smtp_proto.c b/postfix/src/smtp/smtp_proto.c index bdca86ce8..976598998 100644 --- a/postfix/src/smtp/smtp_proto.c +++ b/postfix/src/smtp/smtp_proto.c @@ -78,6 +78,9 @@ /* 111 8th Avenue /* New York, NY 10011, USA /* +/* Wietse Venema +/* porcupine.org +/* /* Pipelining code in cooperation with: /* Jon Ribbens /* Oaktree Internet Solutions Ltd., @@ -481,7 +484,7 @@ int smtp_helo(SMTP_STATE *state) #ifdef USE_TLSRPT if (state->tlsrpt && (state->misc_flags & SMTP_MISC_FLAG_IN_STARTTLS) == 0) - trw_set_ehlo_resp(state->tlsrpt, resp->str); + smtp_tlsrpt_set_ehlo_resp(state, resp->str); #endif } if ((session->features & SMTP_FEATURE_ESMTP) == 0) { @@ -815,7 +818,7 @@ int smtp_helo(SMTP_STATE *state) if (state->tlsrpt) trw_report_failure(state->tlsrpt, TLSRPT_STARTTLS_NOT_SUPPORTED, - /* additional_detail= */ (char *) 0, + /* additional_info= */ (char *) 0, /* failure_reason= */ (char *) 0); #endif return (smtp_site_fail(state, STR(iter->host), resp, @@ -839,7 +842,7 @@ int smtp_helo(SMTP_STATE *state) if (state->tlsrpt) trw_report_failure(state->tlsrpt, TLSRPT_STARTTLS_NOT_SUPPORTED, - /* additional_detail= */ (char *) 0, + /* additional_info= */ (char *) 0, /* failure_reason= */ (char *) 0); #endif return (smtp_site_fail(state, DSN_BY_LOCAL_MTA, @@ -974,7 +977,7 @@ static int smtp_start_tls(SMTP_STATE *state) #else tlsrpt = 0, #endif - fail_type = 0, + ffail_type = 0, dane = state->tls->dane); /* @@ -1103,7 +1106,7 @@ static int smtp_start_tls(SMTP_STATE *state) #else tlsrpt = 0, #endif - fail_type = state->tls->ext_policy_failure, + ffail_type = state->tls->ext_policy_failure, dane = state->tls->dane); /* @@ -1175,12 +1178,12 @@ static int smtp_start_tls(SMTP_STATE *state) if (!TLS_CERT_IS_TRUSTED(session->tls_context)) { (void) trw_report_failure(state->tlsrpt, TLSRPT_CERTIFICATE_NOT_TRUSTED, - /* additional_detail= */ (char *) 0, + /* additional_info= */ (char *) 0, /* failure_reason= */ (char *) 0); } else { (void) trw_report_failure(state->tlsrpt, TLSRPT_CERTIFICATE_HOST_MISMATCH, - /* additional_detail= */ (char *) 0, + /* additional_info= */ (char *) 0, /* failure_reason= */ (char *) 0); } } @@ -1194,10 +1197,12 @@ static int smtp_start_tls(SMTP_STATE *state) * Create a TLSRPT success report only if the TLS engine has not reported * a failure (For example, the TLS handshake may be successful, but the * security level was downgraded from opportunistic or half "dane" to - * "encrypt"). + * "encrypt"), and only if the TLS session and SMTP connection are new. */ #ifdef USE_TLSRPT - if (state->tlsrpt && session->tls_context->rpt_reported == 0) + if (state->tlsrpt && session->tls_context->rpt_reported == 0 + && session->tls_context->session_reused == 0 /* New TLS */ + && session->reuse_count == 0) /* New SMTP */ (void) trw_report_success(state->tlsrpt); #endif diff --git a/postfix/src/smtp/smtp_tls_policy.c b/postfix/src/smtp/smtp_tls_policy.c index f22e51410..501509496 100644 --- a/postfix/src/smtp/smtp_tls_policy.c +++ b/postfix/src/smtp/smtp_tls_policy.c @@ -454,6 +454,7 @@ static void tls_policy_lookup_one(SMTP_TLS_POLICY *tls, int *site_level, tls->ext_policy_type = mystrdup(val); continue; } + /* Only one instance per policy. */ if (!strcasecmp(name, EXT_POLICY_DOMAIN)) { if (tls->ext_policy_domain) { msg_warn("%s: attribute \"%s\" is specified multiple times", diff --git a/postfix/src/smtp/smtp_tlsrpt.c b/postfix/src/smtp/smtp_tlsrpt.c index f81f60872..954bf9298 100644 --- a/postfix/src/smtp/smtp_tlsrpt.c +++ b/postfix/src/smtp/smtp_tlsrpt.c @@ -24,45 +24,47 @@ /* SMTP_STATE *state, /* const char *ehlo_resp) /* DESCRIPTION -/* This module collects TLSRPT policy information and selected SMTP -/* protocol engine state in a TLSRPT_WRAPPER object. This will be -/* passed to (possibly remote) TLS protocol engine, so that it -/* can report a TLS error to a TLSRPT library. The SMTP protocol -/* engine uses the information to report a TLS error or success. +/* This module populates a TLSRPT_WRAPPER object with a) +/* remote TLSRPT policy information, b) remote TLSA or STS policy +/* information, and c) selected SMTP connection information. This +/* object is passed to a TLS protocol engine, which may run in a +/* different process than the SMTP protocol engine. The TLS protocol +/* engine uses the TLSRPT_WRAPPER object to report a TLS handshake +/* error to a TLSRPT library. The SMTP protocol engine uses the +/* object to report a TLS handshake error or success. /* -/* smtp_tls_post_jail() does configuration sanity checks and -/* returns 0 if successful, i.e. TLSRPT support is properly +/* smtp_tls_post_jail() does configuration sanity checks and returns +/* 0 if successful, i.e. TLSRPT support is properly /* configured. Otherwise it returns -1 and logs a warning. Arguments: /* .IP sockname_pname -/* The name of a configuration parameter for the endpoint that is -/* managed by TLSRPT infrastructure. This is used in a diagnostic -/* message. +/* The name of a configuration parameter for the endpoint that +/* is managed by TLSRPT infrastructure. This name is used in a +/* diagnostic message. /* .IP sockname_pval /* The value of said parameter. /* .PP -/* smtp_tlsrpt_create_wrapper() destroys any TLSRPT_WRAPPER -/* referenced by state->tlsrpt, and looks for a TLSRPT -/* policy for the specified domain. If one policy exists, -/* smtp_tlsrpt_create_wrapper() attaches a TLSRPT_WRAPPER instance -/* to state->tlsrpt. Otherwise, state->tlsrpt will be null, and -/* other smtp_tlsrpt_* calls must not be made. The TLSRPT_WRAPPER -/* instance may be reused for different SMTP connections with the -/* same TLSRPT policy domain. Arguments: +/* smtp_tlsrpt_create_wrapper() destroys a TLSRPT_WRAPPER referenced +/* by state->tlsrpt, and looks for a TLSRPT policy for the specified +/* domain. If one policy exists, smtp_tlsrpt_create_wrapper() +/* attaches a TLSRPT_WRAPPER instance to state->tlsrpt. Otherwise, +/* state->tlsrpt will be null, and other smtp_tlsrpt_* calls must not +/* be made. The TLSRPT_WRAPPER instance may be reused for different +/* SMTP connections for the same TLSRPT policy domain. Arguments: /* .IP domain /* The name of a domain that may publish a TLSRPT policy. An /* internationalized domain name may be in U-label or A-label form -/* (it will be converted to A-label internally). +/* (the U-label form will be converted to A-label internally). /* .PP -/* smtp_tlsrpt_set_tls_policy() updates the TLSRPT_WRAPPER with -/* DANE or STS TLS policy information, and clears information -/* that was added with smtp_tlsrpt_set_tcp_connection() or -/* smtp_tlsrpt_set_ehlo_resp(). +/* smtp_tlsrpt_set_tls_policy() updates the TLSRPT_WRAPPER +/* object with DANE or STS TLS policy information, and clears +/* information that was added with smtp_tlsrpt_set_tcp_connection() +/* or smtp_tlsrpt_set_ehlo_resp(). /* .PP -/* smtp_tlsrpt_set_tcp_connection() updates the TLSRPT_WRAPPER with -/* TCP connection properties. +/* smtp_tlsrpt_set_tcp_connection() updates the TLSRPT_WRAPPER +/* object with TCP connection properties. /* .PP -/* smtp_tlsrpt_set_ehlo_resp() updates the TLSRPT_WRAPPER with the -/* SMTP server's EHLO response. +/* smtp_tlsrpt_set_ehlo_resp() updates the TLSRPT_WRAPPER object +/* with the SMTP server's EHLO response. /* BUGS /* This module inherits all limitations from tlsrpt_wrapper(3). /* SEE ALSO @@ -118,7 +120,7 @@ int smtp_tlsrpt_post_jail(const char *sockname_pname, const char *sockname_pval) { if (smtp_dns_support == SMTP_DNS_DISABLED) { - msg_warn("Cannot enable TLRPT support: DNS is disabled"); + msg_warn("Cannot enable %s: DNS is disabled", smtp_tlsrpt_support); return (-1); } if (*sockname_pval == 0) { @@ -152,8 +154,8 @@ static DNS_RR *smtp_tlsrpt_find_policy(const char *adomain) * Lexical features: As specified in RFC 8460, a TLSRPT policy record * must start with a version field ("v=TLSRPTv1") followed by *WSP;*WSP * and at least one other field (we must not assume that the second field - * will be "rua"). We leave further validation to the TLSRPT library, - * where it belongs. + * will be "rua"). We leave further validation to the code that actually + * needs it. */ #define TLSRPTv1_MAGIC "v=TLSRPTv1" #define TLSRPTv1_MAGIC_LEN (sizeof(TLSRPTv1_MAGIC) - 1) @@ -184,7 +186,7 @@ static DNS_RR *smtp_tlsrpt_find_policy(const char *adomain) next = rr->next; if (strncmp(rr->data, TLSRPTv1_MAGIC, TLSRPTv1_MAGIC_LEN) != 0) - /* Ignore non-TLSRPT info. */ + /* Ignore non-TLSRPTv1 info. */ continue; cp = rr->data + TLSRPTv1_MAGIC_LEN; @@ -197,9 +199,10 @@ static DNS_RR *smtp_tlsrpt_find_policy(const char *adomain) continue; } if (rr_result) { - msg_warn("%s: Too many policies for %s", + msg_warn("%s: Too many TLSRPT policies for %s", smtp_tlsrpt_support, adomain); dns_rr_free(rr_result); + rr_result = 0; break; } rr_result = rr; @@ -383,7 +386,7 @@ void smtp_tlsrpt_set_tcp_connection(SMTP_STATE *state) /* * Get the IP client address string. The Postfix SMTP_ITERATOR already - * contains strings with other connection information. + * contains strings with server-side connection information. */ if (getsockname(vstream_fileno(session->stream), (struct sockaddr *) &addr_storage, diff --git a/postfix/src/tls/Makefile.in b/postfix/src/tls/Makefile.in index 3d3bf6e29..ce1a37ee9 100644 --- a/postfix/src/tls/Makefile.in +++ b/postfix/src/tls/Makefile.in @@ -208,6 +208,7 @@ tls_dane.o: ../../include/vstring.h tls_dane.o: tls.h tls_dane.o: tls_dane.c tls_dh.o: ../../include/argv.h +tls_dh.o: ../../include/been_here.h tls_dh.o: ../../include/check_arg.h tls_dh.o: ../../include/dns.h tls_dh.o: ../../include/mail_params.h diff --git a/postfix/src/tls/tls.h b/postfix/src/tls/tls.h index b3e5b7cb2..96eb5a4b3 100644 --- a/postfix/src/tls/tls.h +++ b/postfix/src/tls/tls.h @@ -123,6 +123,15 @@ extern const char *str_tls_level(int); #define TLS_PEEK_PEER_CERT(ssl) SSL_get_peer_certificate(ssl) #define TLS_FREE_PEER_CERT(x) X509_free(x) #define tls_set_bio_callback BIO_set_callback +#endif + +#if OPENSSL_VERSION_PREREQ(3,2) +#define TLS_GROUP_NAME(ssl) SSL_get0_group_name(ssl) +#elif OPENSSL_VERSION_PREREQ(3,0) +#define TLS_GROUP_NAME(ssl) \ + SSL_group_to_name((ssl), SSL_get_negotiated_group(ssl)) +#else +#define TLS_GROUP_NAME(ssl) ((const char *)0) #endif /* @@ -262,7 +271,7 @@ typedef struct { int errorcode; /* First error at error depth */ int must_fail; /* Failed to load trust settings */ int rpt_reported; /* Failure was reported with TLSRPT */ - char *fail_type; /* Doomed by policy */ + char *ffail_type; /* Forced verification failure */ } TLS_SESS_STATE; /* @@ -496,7 +505,7 @@ typedef struct { const char *mdalg; /* default message digest algorithm */ const TLS_DANE *dane; /* DANE TLSA verification */ struct TLSRPT_WRAPPER *tlsrpt; /* RFC 8460 reporting */ - char *fail_type; /* verification must fail by policy */ + char *ffail_type; /* Forced verification failure */ } TLS_CLIENT_START_PROPS; extern TLS_APPL_STATE *tls_client_init(const TLS_CLIENT_INIT_PROPS *); diff --git a/postfix/src/tls/tls_client.c b/postfix/src/tls/tls_client.c index 7bf7e5f48..e35b4e926 100644 --- a/postfix/src/tls/tls_client.c +++ b/postfix/src/tls/tls_client.c @@ -152,6 +152,9 @@ /* /* Victor Duchovni /* Morgan Stanley +/* +/* Wietse Venema +/* porcupine.org /*--*/ /* System library. */ @@ -369,7 +372,7 @@ static void verify_x509(TLS_SESS_STATE *TLScontext, X509 *peercert, msg_info("%s: re-using session with untrusted peer credential, " "look for details earlier in the log", props->namaddr); } - /* TODO(wietse) In the non-reuse case, tlsrprt the root cause? */ + /* TODO(wietse) Don't log TLSRPT success/failure for reused session. */ } /* verify_rpk - process RFC7250 raw public key verification status */ @@ -414,7 +417,7 @@ static void verify_rpk(TLS_SESS_STATE *TLScontext, EVP_PKEY *peerpkey, msg_info("%s: re-using session with untrusted certificate, " "look for details earlier in the log", props->namaddr); } - /* TODO(wietse) In the non-reuse case, tlsrprt the root cause? */ + /* TODO(wietse) Don't log TLSRPT success/failure for reused session. */ } /* add_namechecks - tell OpenSSL what names to check */ @@ -1121,7 +1124,7 @@ TLS_SESS_STATE *tls_client_start(const TLS_CLIENT_START_PROPS *props) #ifdef USE_TLSRPT if (props->tlsrpt) { trw_report_failure(props->tlsrpt, TLSRPT_TLSA_INVALID, - /* additional_detail= */ (char *) 0, + /* additional_info= */ (char *) 0, "all-TLSA-records-unusable"); } #endif @@ -1135,7 +1138,7 @@ TLS_SESS_STATE *tls_client_start(const TLS_CLIENT_START_PROPS *props) #ifdef USE_TLSRPT if (props->tlsrpt) { trw_report_failure(props->tlsrpt, TLSRPT_VALIDATION_FAILURE, - /* additional_detail= */ (char *) 0, + /* additional_info= */ (char *) 0, "all-fingerprints-unusable"); } #endif @@ -1145,7 +1148,7 @@ TLS_SESS_STATE *tls_client_start(const TLS_CLIENT_START_PROPS *props) #ifdef USE_TLSRPT if (props->tlsrpt) { trw_report_failure(props->tlsrpt, TLSRPT_TLSA_INVALID, - /* additional_detail= */ (char *) 0, + /* additional_info= */ (char *) 0, "all-TLSA-records-unusable"); } #endif @@ -1156,7 +1159,7 @@ TLS_SESS_STATE *tls_client_start(const TLS_CLIENT_START_PROPS *props) #ifdef USE_TLSRPT if (props->tlsrpt) { trw_report_failure(props->tlsrpt, TLSRPT_VALIDATION_FAILURE, - /* additional_detail= */ (char *) 0, + /* additional_info= */ (char *) 0, "all-trust-anchors-unusable"); } #endif @@ -1260,8 +1263,8 @@ TLS_SESS_STATE *tls_client_start(const TLS_CLIENT_START_PROPS *props) * certificate matches in tls_verify.c. TODO(wietse) how was this handled * historically? */ - if (props->fail_type) { - TLScontext->fail_type = mystrdup(props->fail_type); + if (props->ffail_type) { + TLScontext->ffail_type = mystrdup(props->ffail_type); TLScontext->must_fail = 1; } @@ -1300,7 +1303,7 @@ TLS_SESS_STATE *tls_client_start(const TLS_CLIENT_START_PROPS *props) #ifdef USE_TLSRPT if (props->tlsrpt) trw_report_failure(props->tlsrpt, TLSRPT_VALIDATION_FAILURE, - /* additional_detail= */ (char *) 0, + /* additional_info= */ (char *) 0, "tls-handshake-failure"); #endif uncache_session(app_ctx->ssl_ctx, TLScontext); diff --git a/postfix/src/tls/tls_dh.c b/postfix/src/tls/tls_dh.c index f47b95461..6165b9390 100644 --- a/postfix/src/tls/tls_dh.c +++ b/postfix/src/tls/tls_dh.c @@ -75,6 +75,7 @@ /* * Global library */ +#include#include /* TLS library. */ @@ -313,68 +314,75 @@ static int setup_auto_groups(SSL_CTX *ctx, const char *origin, { #ifndef OPENSSL_NO_ECDH SSL_CTX *tmpctx; - int *nids; - int space = 10; - int n = 0; + BH_TABLE *seen; char *save; char *groups; char *group; + static VSTRING *names; if ((tmpctx = SSL_CTX_new(TLS_method())) == 0) { msg_warn("cannot allocate temp SSL_CTX"); tls_print_errors(); return (AG_STAT_NO_RETRY); } - nids = mymalloc(space * sizeof(int)); + if (!names) + names = vstring_alloc(sizeof DEF_TLS_EECDH_AUTO + + sizeof DEF_TLS_FFDHE_AUTO); + VSTRING_RESET(names); + /* + * OpenSSL does not tolerate duplicate groups in the requested list. + * Deduplicate case-insensitively, just in case OpenSSL some day supports + * case-insensitive group lookup. Users who specify the group name twice + * and get the case wrong the first time deserve to be unhappy. :-) + * + * OpenSSL 3.3 supports "? " as a syntax for optionally ignoring + * unsupported groups, so we could skip checking against the throw-away + * CTX when linked against 3.3 or higher, but the cost savings don't + * justify the #ifdef overhead for now. + */ + seen = been_here_init(0, BH_FLAG_FOLD); + +#define GROUPS_SEP CHARS_COMMA_SP ":" #define SETUP_AG_RETURN(val) do { \ + been_here_free(seen); \ myfree(save); \ - myfree(nids); \ SSL_CTX_free(tmpctx); \ return (val); \ } while (0) groups = save = concatenate(eecdh, " ", ffdhe, NULL); - if ((group = mystrtok(&groups, CHARS_COMMA_SP)) == 0) { + if ((group = mystrtok(&groups, GROUPS_SEP)) == 0) { msg_warn("no %s key exchange group - OpenSSL requires at least one", origin); SETUP_AG_RETURN(AG_STAT_NO_GROUP); } - for (; group != 0; group = mystrtok(&groups, CHARS_COMMA_SP)) { - int nid = EC_curve_nist2nid(group); - - if (nid == NID_undef) - nid = OBJ_sn2nid(group); - if (nid == NID_undef) - nid = OBJ_ln2nid(group); - if (nid == NID_undef) { - msg_warn("ignoring unknown key exchange group \"%s\"", group); + for (; group != 0; group = mystrtok(&groups, GROUPS_SEP)) { + if (been_here_fixed(seen, group)) continue; - } - /* - * Validate the NID by trying it as the group for a throw-away SSL - * context. Silently skip unsupported code points. This way, we can - * list X25519 and X448 as soon as the nids are assigned, and before - * the supporting code is implemented. They'll be silently skipped - * when not yet supported. + * Validate the group name by trying it as the group for a throw-away + * SSL context. This way, we can ask for new groups that may not yet be + * supported by the underlying OpenSSL runtime. Unsupported groups are + * silently ignored. */ - if (SSL_CTX_set1_curves(tmpctx, &nid, 1) <= 0) { - continue; + ERR_set_mark(); + if (SSL_CTX_set1_curves_list(tmpctx, group) > 0) { + if (VSTRING_LEN(names) > 0) + VSTRING_ADDCH(names, ':'); + vstring_strcat(names, group); } - if (++n > space) { - space *= 2; - nids = myrealloc(nids, space * sizeof(int)); - } - nids[n - 1] = nid; + ERR_pop_to_mark(); } - if (n == 0) { + if (VSTRING_LEN(names) == 0) { /* The names may be case-sensitive */ msg_warn("none of the %s key exchange groups are supported", origin); SETUP_AG_RETURN(AG_STAT_NO_GROUP); } - if (SSL_CTX_set1_curves(ctx, nids, n) <= 0) { + VSTRING_TERMINATE(names); + + if (SSL_CTX_set1_curves_list(ctx, vstring_str(names)) <= 0) { msg_warn("failed to set up the %s key exchange groups", origin); tls_print_errors(); SETUP_AG_RETURN(AG_STAT_NO_RETRY); diff --git a/postfix/src/tls/tls_misc.c b/postfix/src/tls/tls_misc.c index 3d31cbbe4..3b699ed5d 100644 --- a/postfix/src/tls/tls_misc.c +++ b/postfix/src/tls/tls_misc.c @@ -1057,6 +1057,13 @@ void tls_get_signature_params(TLS_SESS_STATE *TLScontext) kex_name = OBJ_nid2sn(EVP_PKEY_type(nid)); break; +#if defined(EVP_PKEY_KEYMGMT) + case EVP_PKEY_KEYMGMT: + kex_name = EVP_PKEY_get0_type_name(dh_pkey); + TLScontext->kex_bits = 0; + break; +#endif + case EVP_PKEY_DH: kex_name = "DHE"; TLScontext->kex_bits = EVP_PKEY_bits(dh_pkey); @@ -1072,6 +1079,16 @@ void tls_get_signature_params(TLS_SESS_STATE *TLScontext) EVP_PKEY_free(dh_pkey); } + /* + * On the client side, a TLS 1.3 KEM has no server key, just ciphertext to + * decapsulate, but, as of OpenSSL 3.0, the client can still obtain the + * negotiated group name directly. We nevertheless still try to get the + * group details from the peer key first, which works with OpenSSL 1.1.1 + * and retains the original output format for the (EC)DH groups. + */ + if (!kex_name) + kex_name = TLS_GROUP_NAME(ssl); + /* * On the client end, the certificate may be present, but not used, so we * check via SSL_get_signature_nid(). This means that local signature @@ -1347,7 +1364,7 @@ TLS_SESS_STATE *tls_alloc_sess_context(int log_mask, const char *namaddr) TLScontext->errorcode = X509_V_OK; TLScontext->errorcert = 0; TLScontext->rpt_reported = 0; - TLScontext->fail_type = 0; + TLScontext->ffail_type = 0; return (TLScontext); } @@ -1398,8 +1415,8 @@ void tls_free_context(TLS_SESS_STATE *TLScontext) myfree((void *) TLScontext->srvr_sig_dgst); if (TLScontext->errorcert) X509_free(TLScontext->errorcert); - if (TLScontext->fail_type) - myfree(TLScontext->fail_type); + if (TLScontext->ffail_type) + myfree(TLScontext->ffail_type); myfree((void *) TLScontext); } diff --git a/postfix/src/tls/tls_proxy.h b/postfix/src/tls/tls_proxy.h index 8e0c67088..ae2a8381c 100644 --- a/postfix/src/tls/tls_proxy.h +++ b/postfix/src/tls/tls_proxy.h @@ -258,7 +258,7 @@ extern void tls_proxy_server_start_free(TLS_SERVER_START_PROPS *); #define TLS_ATTR_MDALG "mdalg" #define TLS_ATTR_DANE "dane" #define TLS_ATTR_TLSRPT "tlsrpt" -#define FAIL_TYPE "fail_type" +#define TLS_ATTR_FFAIL_TYPE "forced_failure_type" /* * TLS_TLSA attributes. diff --git a/postfix/src/tls/tls_proxy_client_print.c b/postfix/src/tls/tls_proxy_client_print.c index b2502ff43..ae378cb7e 100644 --- a/postfix/src/tls/tls_proxy_client_print.c +++ b/postfix/src/tls/tls_proxy_client_print.c @@ -344,8 +344,8 @@ int tls_proxy_client_start_print(ATTR_PRINT_COMMON_FN print_fn, SEND_ATTR_FUNC(tls_proxy_client_tlsrpt_print, (const void *) props->tlsrpt), #endif - SEND_ATTR_STR(FAIL_TYPE, - STRING_OR_EMPTY(props->fail_type)), + SEND_ATTR_STR(TLS_ATTR_FFAIL_TYPE, + STRING_OR_EMPTY(props->ffail_type)), ATTR_TYPE_END); /* Do not flush the stream. */ if (msg_verbose) diff --git a/postfix/src/tls/tls_proxy_client_scan.c b/postfix/src/tls/tls_proxy_client_scan.c index c6f08e80c..7b25f71af 100644 --- a/postfix/src/tls/tls_proxy_client_scan.c +++ b/postfix/src/tls/tls_proxy_client_scan.c @@ -337,8 +337,8 @@ void tls_proxy_client_start_free(TLS_CLIENT_START_PROPS *props) if (props->tlsrpt) trw_free(props->tlsrpt); #endif - if (props->fail_type) - myfree(props->fail_type); + if (props->ffail_type) + myfree(props->ffail_type); myfree((void *) props); } @@ -530,7 +530,7 @@ int tls_proxy_client_start_scan(ATTR_SCAN_COMMON_FN scan_fn, VSTREAM *fp, VSTRING *cipher_grade = vstring_alloc(25); VSTRING *cipher_exclusions = vstring_alloc(25); VSTRING *mdalg = vstring_alloc(25); - VSTRING *fail_type = vstring_alloc(25); + VSTRING *ffail_type = vstring_alloc(25); #ifdef USE_TLSRPT #define EXPECT_START_SCAN_RETURN 17 @@ -571,7 +571,7 @@ int tls_proxy_client_start_scan(ATTR_SCAN_COMMON_FN scan_fn, VSTREAM *fp, RECV_ATTR_FUNC(tls_proxy_client_tlsrpt_scan, &props->tlsrpt), #endif - RECV_ATTR_STR(FAIL_TYPE, fail_type), + RECV_ATTR_STR(TLS_ATTR_FFAIL_TYPE, ffail_type), ATTR_TYPE_END); /* Always construct a well-formed structure. */ props->nexthop = vstring_export(nexthop); @@ -584,7 +584,7 @@ int tls_proxy_client_start_scan(ATTR_SCAN_COMMON_FN scan_fn, VSTREAM *fp, props->cipher_grade = vstring_export(cipher_grade); props->cipher_exclusions = vstring_export(cipher_exclusions); props->mdalg = vstring_export(mdalg); - EXPORT_OR_NULL(props->fail_type, fail_type); + EXPORT_OR_NULL(props->ffail_type, ffail_type); ret = (ret == EXPECT_START_SCAN_RETURN ? 1 : -1); if (ret != 1) { tls_proxy_client_start_free(props); diff --git a/postfix/src/tls/tls_verify.c b/postfix/src/tls/tls_verify.c index a719c2ca9..0f78e24f4 100644 --- a/postfix/src/tls/tls_verify.c +++ b/postfix/src/tls/tls_verify.c @@ -90,6 +90,9 @@ /* /* Victor Duchovni /* Morgan Stanley +/* +/* Wietse Venema +/* porcupine.org /*--*/ /* System library. */ @@ -224,19 +227,19 @@ void tls_log_verify_error(TLS_SESS_STATE *TLScontext, /* * If an external policy flagged an error, report that instead. */ - if (TLScontext->fail_type) { + if (TLScontext->ffail_type) { msg_info("certificate verification failed for %s: " - "external policy failure (%s)", - TLScontext->namaddr, TLScontext->fail_type); + "external policy failure (%s)", + TLScontext->namaddr, TLScontext->ffail_type); #ifdef USE_TLSRPT if (tlsrpt) { tlsrpt_failure_t failure_type; - if ((failure_type = convert_tlsrpt_policy_failure(TLScontext->fail_type)) < 0) + if ((failure_type = convert_tlsrpt_policy_failure(TLScontext->ffail_type)) < 0) msg_panic("tls_log_verify_error: unexpected failure_reason: %s", - TLScontext->fail_type); + TLScontext->ffail_type); trw_report_failure(tlsrpt, failure_type, - /* additional_detail= */ (char *) 0, + /* additional_info= */ (char *) 0, /* failure_reason= */ (char *) 0); } #endif @@ -258,7 +261,7 @@ void tls_log_verify_error(TLS_SESS_STATE *TLScontext, #ifdef USE_TLSRPT if (tlsrpt) trw_report_failure(tlsrpt, TLSRPT_CERTIFICATE_NOT_TRUSTED, - /* additional_detail= */ (char *) 0, + /* additional_info= */ (char *) 0, /* failure_code= */ (char *) 0); #endif break; @@ -268,7 +271,7 @@ void tls_log_verify_error(TLS_SESS_STATE *TLScontext, #ifdef USE_TLSRPT if (tlsrpt) trw_report_failure(tlsrpt, TLSRPT_VALIDATION_FAILURE, - /* additional_detail= */ (char *) 0, + /* additional_info= */ (char *) 0, CERT_ERROR_TO_STRING(err)); #endif break; @@ -289,7 +292,7 @@ void tls_log_verify_error(TLS_SESS_STATE *TLScontext, #ifdef USE_TLSRPT if (tlsrpt) trw_report_failure(tlsrpt, TLSRPT_VALIDATION_FAILURE, - /* additional_detail= */ (char *) 0, + /* additional_info= */ (char *) 0, CERT_ERROR_TO_STRING(err)); #endif break; @@ -300,7 +303,7 @@ void tls_log_verify_error(TLS_SESS_STATE *TLScontext, #ifdef USE_TLSRPT if (tlsrpt) trw_report_failure(tlsrpt, TLSRPT_VALIDATION_FAILURE, - /* additional_detail= */ (char *) 0, + /* additional_info= */ (char *) 0, CERT_ERROR_TO_STRING(err)); #endif break; @@ -311,7 +314,7 @@ void tls_log_verify_error(TLS_SESS_STATE *TLScontext, #ifdef USE_TLSRPT if (tlsrpt) trw_report_failure(tlsrpt, TLSRPT_CERTIFICATE_EXPIRED, - /* additional_detail= */ (char *) 0, + /* additional_info= */ (char *) 0, /* failure_code= */ (char *) 8); #endif break; @@ -321,7 +324,7 @@ void tls_log_verify_error(TLS_SESS_STATE *TLScontext, #ifdef USE_TLSRPT if (tlsrpt) trw_report_failure(tlsrpt, TLSRPT_VALIDATION_FAILURE, - /* additional_detail= */ (char *) 0, + /* additional_info= */ (char *) 0, CERT_ERROR_TO_STRING(err)); #endif break; @@ -332,7 +335,7 @@ void tls_log_verify_error(TLS_SESS_STATE *TLScontext, #ifdef USE_TLSRPT if (tlsrpt) trw_report_failure(tlsrpt, TLSRPT_VALIDATION_FAILURE, - /* additional_detail= */ (char *) 0, + /* additional_info= */ (char *) 0, CERT_ERROR_TO_STRING(err)); #endif break; diff --git a/postfix/src/tls/tlsrpt_wrapper.c b/postfix/src/tls/tlsrpt_wrapper.c index 91112cb71..03858fd13 100644 --- a/postfix/src/tls/tlsrpt_wrapper.c +++ b/postfix/src/tls/tlsrpt_wrapper.c @@ -2,7 +2,7 @@ /* NAME /* tlsrpt_wrapper 3 /* SUMMARY -/* TLSRPT support for the TLS protocol engine +/* TLSRPT support for the SMTP and TLS protocol engines /* SYNOPSIS /* #include /* @@ -36,7 +36,7 @@ /* void trw_report_failure( /* TLSRPT_WRAPPER *trw, /* tlsrpt_failure_t failure_type, -/* const char *additional_detail, +/* const char *additional_info, /* const char *failure_reason) /* /* void trw_report_success( @@ -58,50 +58,71 @@ /* int valid_tlsrpt_policy_failure( /* const char *failure_name) /* #endif /* USE_TLS */ -/* ARCHITECTURE, BOTTOM-UP VIEW -/* Postfix TLSRPT support uses the TLSRPT client library from -/* sys4.de. That library makes the reasonable assumption that +/* POSTFIX ARCHITECTURE, BOTTOM-UP VIEW +/* .ad +/* .fi +/* This module encapsulates TLSRPT support for Postfix's +/* multi-process and multi-layer architecture. The text that follows +/* explains the purpose of this software layer. +/* +/* First, Postfix TLSRPT support uses the TLSRPT client library +/* from sys4.de. That library makes the reasonable assumption that /* all calls concerning one SMTP session will be made from within /* one process. /* -/* With Postfix, the TLS protocol engine may be located in a -/* different process than the SMTP protocol engine, and both -/* processes need the ability to report a TLS error. +/* Second, some TLS errors are detected in the SMTP protocol +/* engine (example: a remote SMTP server does not announce STARTTLS +/* support), while other TLS errors are detected in the TLS protocol +/* engine (example: certificate verification error). /* -/* To bridge this gap, the SMTP protocol engine forwards SMTP -/* session and TLS policy information to the TLS protocol engine in -/* the form of a TLSRPT_WRAPPER object that contains SMTP and other -/* information that the TLSRPT client library needs. The TLS engine -/* can pass that information to the TLSRPT client library to report -/* a TLS error. The SMTP protocol engine can use that same -/* information to report a TLS error or success. -/* IMPLEMENTATION +/* Third, the Postfix TLS protocol engine may be located in a +/* different process than the SMTP protocol engine. And even if the +/* two are located in the same process, the TLS protocol engine knows +/* nothing about SMTP. Hence, there needs to be an abstraction that +/* isolates the TLS protocol engine from the SMTP-specific details +/* of TLSRPT. +/* +/* Fourth, Postfix has a pipelined and layered architecture where +/* each process (or architectural layer) handles a problem as it +/* runs into it, instead of reporting problem details back to its +/* pipeline predecessor (or back to a higher architectural layer). +/* TLSRPT_WRAPPER IMPLEMENTATION /* .ad /* .fi -/* The Postfix SMTP protocol engine (smtp_proto.c) reports TLS -/* errors when TLS support is required but unavailable, or requests -/* the Postfix TLS protocol engine to perform a TLS protocol -/* handshake over an open SMTP connection. The SMTP protocol engine -/* either calls the TLS protocol engine directly, or calls it over -/* RPC in a tlsproxy(8) process. +/* At a high level, the SMTP protocol engine encapsulates SMTP +/* session and TLS policy information in an opaque TLSRPT_WRAPPER +/* object, and passes that object to the TLS protocol engine. The +/* TLS protocol engine can invoke TLSRPT_WRAPPER methods to report a +/* TLS error through the sys4.de TLSRPT client library. In a similar +/* manner, the SMTP protocol engine can invoke TLSRPT_WRAPPER object +/* methods to report a TLS error or success. +/* +/* At a low level, The Postfix SMTP protocol engine (smtp_proto.c) +/* reports TLS errors when TLS support is required but unavailable, +/* or requests the Postfix TLS protocol engine to perform a TLS +/* protocol handshake over an open SMTP connection. The SMTP +/* protocol engine either calls the TLS protocol engine directly, +/* or calls it over local IPC in a tlsproxy(8) process. /* -/* The TLS protocol engine may report a TLS error through the -/* tlsrpt_wrapper library, and either returns no TLS session object, -/* or a TLS session object for a completed handshake. The TLS -/* session object will indicate if the TLS protocol engine reported -/* any TLS error through TLSRPT. +/* The TLS protocol engine may report a TLS error by invoking +/* TLSRPT_WRAPPER methods, and either returns no TLS session object, +/* or a TLS session object for a completed handshake. The TLS session +/* object will indicate if the TLS protocol engine reported any +/* TLS error through TLSRPT (for example an error that resulted in +/* a successful TLS handshake with a downgraded TLS security level). /* /* The Postfix SMTP protocol engine reports success or failure -/* through the tlsrpt_wrapper library, depending on whether all -/* matching requirements were satisfied. SMTP protocol engine -/* does not report success or failure through the tlsrpt_wrapper -/* library if the TLS protocol engine already reported a failure. -/* WRAPPER API +/* by invoking TLSRPT_WRAPPER methods, depending on whether all +/* matching requirements were satisfied. The SMTP protocol engine +/* does not report success or failure by invoking TLSRPT_WRAPPER +/* methods if the TLS protocol engine already reported a failure. +/* TLSRPT_WRAPPER API /* .ad /* .fi /* The functions below must be called in a specific order. All /* string inputs are copied. If a required call is missing then -/* the request will be ignored, and a warning will be logged. +/* the request will be ignored, and a warning will be logged, +/* but this not affect email deliveries. /* .PP /* trw_create() must be called before other trw_xxx() requests can /* be made. Arguments: @@ -119,19 +140,19 @@ /* trw_free() destroys storage allocated with other trw_xxx() /* requests. /* .PP -/* trw_set_tls_policy() must be called by the SMTP protocol -/* engine after it found a DANE, STS, or no policy, and before it -/* tries to establish a new SMTP connection. This function clears -/* information that was specified earlier with trw_set_tls_policy() -/* or trw_set_tcp_connection(), and whether trw_report_failure() +/* trw_set_tls_policy() must be called by the SMTP protocol engine +/* after it found a DANE, STS, or no policy, and before it tries to +/* establish a new SMTP connection. This function clears information +/* that was specified earlier with trw_set_tls_policy() or +/* trw_set_tcp_connection(), and resets whether trw_report_failure() /* or trw_report_success() were called. Mapping from arguments to /* TLSRPT report fields: /* .IP tls_policy_type /* policies[].policy.policy-type. -/* .IP tls_policy_strings +/* .IP tls_policy_strings (may be null) /* policies[].policy.policy-string[]. Ignored if the tls_policy_type /* value is TLSRPT_NO_POLICY_FOUND. -/* .IP tls_policy_domain +/* .IP tls_policy_domain (may be null) /* policies[].policy.policy-domain. /* .IP mx_host_patterns (may be null) /* policies[].policy.mx-host[]. Ignored if the tls_policy_type @@ -163,7 +184,7 @@ /* connection. Mapping from arguments to TLSRPT report fields: /* .IP failure_type /* policies[].failure-details[].result-type. -/* .IP additional_detail (may be null) +/* .IP additional_info (may be null) /* policies[].failure-details[].additional-information. /* .IP failure_reason (may be null) /* policies[].failure-details[].failure-reason-code @@ -188,8 +209,9 @@ /* module is built with TLSRPT support. This allows the names to /* be used even if TLSRPT is disabled. /* DIAGNOSTICS -/* Some functions will log a a warning when information is missing, -/* but such warnings will not affect the SMTP or TLS protocol engine. +/* Some functions will log a warning when information is missing. +/* Such warnings will not affect the operation of the SMTP or TLS +/* protocol engine. /* BUGS /* This implementation is suitable to report successful TLS policy /* compliance, and to report a failure that prevents TLS policy @@ -405,12 +427,17 @@ static int trw_munge_report_result(int libtlsrpt_errorcode) } /* - * Report a libc error. Do not report success if errno was zero. + * Report a libc error. Do not report success if errno was zero. When + * debug logging is enabled, also log some library-internal info. */ else { err = tlsrpt_errno_from_error_code(libtlsrpt_errorcode); msg_warn("Could not report TLS handshake result to tlsrpt library:" " %s (errno %d)", mystrerror(err), err); + if (msg_verbose) + msg_warn("Error location in tlsrpt library: %s (error %d)", + tlsrpt_strerror(libtlsrpt_errorcode), + libtlsrpt_errorcode); errno = err; return (-1); } @@ -452,7 +479,7 @@ static const char *trw_failure_type_to_string(tlsrpt_failure_t failure_type) int trw_report_failure(TLSRPT_WRAPPER *trw, tlsrpt_failure_t failure_type, - const char *additional_detail, + const char *additional_info, const char *failure_reason) { const char myname[] = "trw_report_failure"; @@ -514,7 +541,7 @@ int trw_report_failure(TLSRPT_WRAPPER *trw, /* receiving_mx_hostname= */ trw->rcv_mta_name, /* receiving_mx_helo= */ trw->rcv_mta_ehlo, /* receiving_ip= */ trw->rcv_mta_addr, - /* additional_information= */ additional_detail, + /* additional_information= */ additional_info, /* failure_reason_code= */ failure_reason); if (res == 0) res = tlsrpt_finish_policy(dr, TLSRPT_FINAL_FAILURE); diff --git a/postfix/src/tls/tlsrpt_wrapper.h b/postfix/src/tls/tlsrpt_wrapper.h index 2107d3a3d..57daff16f 100644 --- a/postfix/src/tls/tlsrpt_wrapper.h +++ b/postfix/src/tls/tlsrpt_wrapper.h @@ -5,7 +5,7 @@ /* NAME /* tlsrpt_wrapper 3h /* SUMMARY -/* TLSRPT support for the TLS protocol engine +/* TLSRPT support for the SMTP and TLS protocol engines /* SYNOPSIS /* #include /* DESCRIPTION @@ -46,7 +46,7 @@ extern void trw_set_ehlo_resp(TLSRPT_WRAPPER *trw, const char *rcv_mta_ehlo); extern int trw_report_failure(TLSRPT_WRAPPER *trw, tlsrpt_failure_t policy_failure, - const char *additional_detail, + const char *additional_info, const char *failure_reason); extern int trw_report_success(TLSRPT_WRAPPER *trw); extern int trw_is_reported(const TLSRPT_WRAPPER *trw); diff --git a/postfix/src/tlsproxy/tlsproxy.c b/postfix/src/tlsproxy/tlsproxy.c index 346415daf..1bf449d37 100644 --- a/postfix/src/tlsproxy/tlsproxy.c +++ b/postfix/src/tlsproxy/tlsproxy.c @@ -743,7 +743,7 @@ static int tlsp_eval_tls_error(TLSP_STATE *state, int err) && state->is_server_role == 0) trw_report_failure(state->client_start_props->tlsrpt, TLSRPT_VALIDATION_FAILURE, - /* additional_detail= */ (char *) 0, + /* additional_info= */ (char *) 0, "tls-handshake-failure"); #endif tlsp_state_free(state); diff --git a/postfix/src/util/argv.c b/postfix/src/util/argv.c index 112603164..0816430bc 100644 --- a/postfix/src/util/argv.c +++ b/postfix/src/util/argv.c @@ -152,6 +152,9 @@ /* Google, Inc. /* 111 8th Avenue /* New York, NY 10011, USA +/* +/* Wietse Venema +/* porcupine.org /*--*/ /* System libraries. */