From: Wietse Z Venema
Date: Thu, 14 May 2026 05:00:00 +0000 (-0500)
Subject: postfix-3.12-20260514
X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=refs%2Fheads%2Fmaster;p=thirdparty%2Fpostfix.git
postfix-3.12-20260514
---
diff --git a/postfix/HISTORY b/postfix/HISTORY
index 53a20f718..c934825e9 100644
--- a/postfix/HISTORY
+++ b/postfix/HISTORY
@@ -31107,7 +31107,7 @@ Apologies for any names omitted.
Bitrot fixes: deprecation warning with OpenSSL 4.0
(tls/tls_dane.c); race condition fix in a test script
- (tls/dls_dane.sh). Viktor Dukhovni.
+ (tls/tls_dane.sh). Viktor Dukhovni.
20260510
@@ -31119,11 +31119,48 @@ Apologies for any names omitted.
master/multi_server.c, master/single_server.c,
src/master/trigger_server.c.
-TODO
- Reorganize PTEST_LIB, PMOCK_LIB, TESTLIB, TESTLIBS, etc.
+20260513
+
+ Bitrot: builds with musl libc were using the obsolete
+ NO_SNPRINTF code path in vbuf_print.c. File: util/sys_defs.h.
+
+20260514
+
+ Bitrot: the obsolete NO_SNPRINTF code path in vbuf_print.c
+ wasn't updated for Claude Code findings. File: util/vbuf_print.c.
+
+ Feature: TLS protocol trace via SSL_trace, enabled with
+ "trace" in *_tls_loglevel or *_tls_loglevel_maps. smtp(8),
+ smtpd(8), and tlsproxy(8) write the transcript to a
+ per-connection file under $queue_directory/tlstrace/, capped
+ at *_tls_trace_size_limit bytes per file. posttls-finger(1)
+ writes to a file in the current directory; one file per
+ run, with separator lines between reconnects. The path is
+ logged via msg_info() when the trace begins. Files:
+ proto/postconf.proto, global/mail_params.h,
+ postscreen/postscreen.c, postscreen/postscreen_tls_conf.c,
+ posttls-finger/posttls-finger.c, smtp/lmtp_params.c,
+ smtp/smtp.c, smtp/smtp_params.c, smtp/smtp_proto.c,
+ smtpd/smtpd.c, tls/tls.h, tls/tls_client.c, tls/tls_misc.c,
+ tls/tls_proxy_attr.h, tls/tls_proxy_client_start_proto.[hc],
+ tls/tls_proxy_server_start_proto.[hc], tls/tls_server.c,
+ tlsproxy/tlsproxy.c, tlsproxy/tlsproxy_client.c,
+ tlsproxy/tlsproxy_server.c.
+
+ The above feature was implemented in three iterations. In
+ the first two, it was designed by Wietse and Viktor, with
+ preliminary implementations by Claude Code supervised by
+ Viktor. With minor changes, the Claude Code implementation
+ was finished by humans.
+
+ Feature: Postfix daemons create at most tls_trace_rate_limit
+ trace files (default: 1) per anvil_rate_time_unit interval
+ (default: 60s). This limit applies to the combined trace
+ file output from all Postfix daemon processes. Files:
+ anvil/anvil.c, global/anvil_clnt.[hc], proto/postconf.proto,
+ mantools/postlink.
- Document TLS parameters in tlsproxy(8) and postscreen(8).
+TODO
- Why are process_name and service_name implemented in different
- ways?
+ Reorganize PTEST_LIB, PMOCK_LIB, TESTLIB, TESTLIBS, etc.
diff --git a/postfix/INSTALL b/postfix/INSTALL
index 623dabf31..649560d43 100644
--- a/postfix/INSTALL
+++ b/postfix/INSTALL
@@ -617,6 +617,14 @@ The following is an extensive list of names and values.
||-DNO_SNPRINTF |default, Postfix uses snprintf() except on |
|| |ancient systems. |
||______________________________|_____________________________________________|
+|| |Do not build with support for OpenSSL TLS |
+|| |traces. Some vendor OpenSSL runtime libraries|
+|| |are built without support for tracing, and |
+||-DNO_TLS_TRACE |Postfix software built on a system with TLS |
+|| |trace support would not work when installed |
+|| |on one without. See smtp_tls_loglevel in the |
+|| |postconf(5) manual. |
+||______________________________|_____________________________________________|
| |Specifies a non-default compiler debugging |
|DEBUG=debug_level |level. The default is "-g". Specify DEBUG= to|
| |turn off debugging. |
diff --git a/postfix/README_FILES/INSTALL b/postfix/README_FILES/INSTALL
index 956e0ea4e..d11826fd9 100644
--- a/postfix/README_FILES/INSTALL
+++ b/postfix/README_FILES/INSTALL
@@ -617,6 +617,14 @@ The following is an extensive list of names and values.
||-DNO_SNPRINTF |default, Postfix uses snprintf() except on |
|| |ancient systems. |
|_|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |
+|| |Do not build with support for OpenSSL TLS |
+|| |traces. Some vendor OpenSSL runtime libraries|
+|| |are built without support for tracing, and |
+||-DNO_TLS_TRACE |Postfix software built on a system with TLS |
+|| |trace support would not work when installed |
+|| |on one without. See smtp_tls_loglevel in the |
+|| |postconf(5) manual. |
+|_|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |
| |Specifies a non-default compiler debugging |
|DEBUG=debug_level |level. The default is "-g". Specify DEBUG= to|
| |turn off debugging. |
diff --git a/postfix/RELEASE_NOTES b/postfix/RELEASE_NOTES
index 91e619056..db94d6490 100644
--- a/postfix/RELEASE_NOTES
+++ b/postfix/RELEASE_NOTES
@@ -26,6 +26,44 @@ now also distributed with the more recent Eclipse Public License
license of their choice. Those who are more comfortable with the
IPL can continue with that license.
+Major changes with snapshot 20260514
+====================================
+
+Support for per-session TLS traces with detailed TLS protocol
+information but without any plaintext SMTP content. This feature
+can be enabled by appending ",trace" to a TLS loglevel.
+
+Postfix daemons will record one trace per TLS session under
+$queue_directory/tlstrace. A trace file name is the concatenation
+of the application name ("smtpd", "smtp", etc.), a time stamp
+formatted as yyyymmddhhmmss, the microsecond portion of system time,
+the peer IP address, and a six-character unique string to avoid
+file name collisions.
+
+Safety measures: the size of each trace file is limited with {lmtp,
+postscreen, smtp_, smtpd}_tls_trace_size_limit (default: 102400),
+and all Postfix daemons combined will create no more than
+tls_trace_rate_limit trace files (default: 1) per anvil_rate_time_unit
+interval (default: 60s).
+
+The posttls-finger command will create trace files (using the same
+name format as daemons) in the current directory, for example:
+"posttls-finger -L 1,trace example.com". It is not subject to the
+tls_trace_rate_limit or trace file size constraints.
+
+This feature is based on a design by Wietse and Viktor, initially
+implemented by Claude Code, then simplified and completed by Wietse
+and Viktor.
+
+Incompatible changes with snapshot 20260514
+===========================================
+
+The internal protocol between tlsproxy(8) and its clients (smtp(8),
+postscreen(8), smtpd(8) in tlsproxy mode, and "posttls-finger -X")
+gained a new attribute. Run "postfix reload" after the upgrade. If
+this step is skipped, TLS sessions through tlsproxy(8) will fail,
+because the old and new processes disagree on the protocol shape.
+
Incompatible changes with snapshot 20260312
===========================================
diff --git a/postfix/conf/postfix-files b/postfix/conf/postfix-files
index 7db6b3c3b..68412e6a7 100644
--- a/postfix/conf/postfix-files
+++ b/postfix/conf/postfix-files
@@ -65,6 +65,7 @@ $queue_directory/maildrop:d:$mail_owner:$setgid_group:730:uc
$queue_directory/public:d:$mail_owner:$setgid_group:710:uc
$queue_directory/pid:d:root:-:755:uc
$queue_directory/saved:d:$mail_owner:-:700:ucr
+$queue_directory/tlstrace:d:$mail_owner:-:700:ucr
$queue_directory/trace:d:$mail_owner:-:700:ucr
# Update shared libraries and plugins before daemon or command-line programs.
$shlib_directory/lib${LIB_PREFIX}util${LIB_SUFFIX}:f:root:-:755
diff --git a/postfix/html/INSTALL.html b/postfix/html/INSTALL.html
index f20762361..c64f3bcff 100644
--- a/postfix/html/INSTALL.html
+++ b/postfix/html/INSTALL.html
@@ -891,6 +891,12 @@ they are known to be available.
instead of snprintf(). By default, Postfix uses
snprintf() except on ancient systems.
+ | | -DNO_TLS_TRACE | Do not build with support
+for OpenSSL TLS traces. Some vendor OpenSSL runtime libraries are built
+without support for tracing, and Postfix software built on a system with
+TLS trace support would not work when installed on one without. See
+smtp_tls_loglevel in the postconf(5) manual. |
+
| DEBUG=debug_level | Specifies a
non-default compiler debugging level. The default is "-g".
Specify DEBUG= to turn off debugging. |
diff --git a/postfix/html/anvil.8.html b/postfix/html/anvil.8.html
index 0647075d3..d8a927d5e 100644
--- a/postfix/html/anvil.8.html
+++ b/postfix/html/anvil.8.html
@@ -120,6 +120,19 @@ ANVIL(8) ANVIL(8)
status=0
rate=number
+TLS TRACE RATE CONTROL
+ To register a TLS trace event send the following request to the
+ anvil(8) server:
+
+ request=tlstr
+ ident=string
+
+ The anvil(8) server answers with the number of TLS trace requests per
+ unit time for the (service, client) combination specified with ident:
+
+ status=0
+ rate=number
+
SECURITY
The anvil(8) server does not talk to the network or to local users, and
can run chrooted at fixed low privilege.
diff --git a/postfix/html/lmtp.8.html b/postfix/html/lmtp.8.html
index 04e8ed84a..97be3c227 100644
--- a/postfix/html/lmtp.8.html
+++ b/postfix/html/lmtp.8.html
@@ -794,6 +794,16 @@ SMTP(8) SMTP(8)
Optional TLS loglevel override that depends on the remote peer
host name or IP address.
+ smtp_tls_trace_size_limit (102400)
+ Size limit, in bytes, for the TLS protocol transcript that the
+ Postfix SMTP client writes when the "trace" keyword is included
+ in the TLS loglevel for a peer (smtp_tls_loglevel or
+ smtp_tls_loglevel_maps).
+
+ tls_trace_rate_limit (1)
+ The maximum number of TLS traces per anvil_rate_time_unit that
+ all Postfix daemons combined will create.
+
OBSOLETE TLS CONTROLS
The following configuration parameters exist for compatibility with
Postfix versions before 2.3. Support for these will be removed in a
diff --git a/postfix/html/makedefs.1.html b/postfix/html/makedefs.1.html
index c5afd48f9..f9a3ba2e4 100644
--- a/postfix/html/makedefs.1.html
+++ b/postfix/html/makedefs.1.html
@@ -131,33 +131,36 @@ MAKEDEFS(1) MAKEDEFS(1)
-DNO_STDBOOL
Don't use <stdbool.h>. This is usually auto-detected.
+ -DNO_TLS_TRACE
+ Build without OpenSSL 3 (and later) debug trace support.
+
DEBUG=debug_level
- Specifies a non-default debugging level. The default is -g.
+ Specifies a non-default debugging level. The default is -g.
Specify DEBUG= to turn off debugging.
OPT=optimization_level
- Specifies a non-default optimization level. The default is -O.
+ Specifies a non-default optimization level. The default is -O.
Specify OPT= to turn off optimization.
POSTFIX_INSTALL_OPTS=-option...
- Specifies options for the postfix-install command, separated by
- whitespace. Currently, the only supported option is
+ Specifies options for the postfix-install command, separated by
+ whitespace. Currently, the only supported option is
-keep-build-mtime.
SHLIB_CFLAGS=flags
- Override the compiler flags (typically, "-fPIC") for Postfix
+ Override the compiler flags (typically, "-fPIC") for Postfix
dynamically-linked libraries and database plugins.
This feature was introduced with Postfix 3.0.
SHLIB_RPATH=rpath
- Override the runpath (typically, "'-Wl,-rpath,${SHLIB_DIR}'")
+ Override the runpath (typically, "'-Wl,-rpath,${SHLIB_DIR}'")
for Postfix dynamically-linked libraries.
This feature was introduced with Postfix 3.0.
SHLIB_SUFFIX=suffix
- Override the filename suffix (typically, ".so") for Postfix
+ Override the filename suffix (typically, ".so") for Postfix
dynamically-linked libraries and database plugins.
This feature was introduced with Postfix 3.0.
@@ -165,7 +168,7 @@ MAKEDEFS(1) MAKEDEFS(1)
shared=yes
shared=no
- Enable (disable) Postfix builds with dynamically-linked
+ Enable (disable) Postfix builds with dynamically-linked
libraries typically named $shlib_directory/libpostfix-*.so.*.
This feature was introduced with Postfix 3.0.
@@ -173,39 +176,39 @@ MAKEDEFS(1) MAKEDEFS(1)
dynamicmaps=yes
dynamicmaps=no
- Enable (disable) Postfix builds with the configuration file
+ Enable (disable) Postfix builds with the configuration file
$meta_directory/dynamicmaps.cf and dynamically-loadable database
- plugins typically named postfix-*.so.*. The setting "dynam-
- icmaps=yes" implicitly enables Postfix dynamically-linked
+ plugins typically named postfix-*.so.*. The setting "dynam-
+ icmaps=yes" implicitly enables Postfix dynamically-linked
libraries.
This feature was introduced with Postfix 3.0.
pie=yes
- pie=no Enable (disable) Postfix builds with position-independent exe-
+ pie=no Enable (disable) Postfix builds with position-independent exe-
cutables, on platforms where this is supported.
This feature was introduced with Postfix 3.0.
installation_parameter=value...
- Override the compiled-in default value of the specified instal-
- lation parameter(s). The following parameters are supported in
+ Override the compiled-in default value of the specified instal-
+ lation parameter(s). The following parameters are supported in
this context:
- command_directory config_directory daemon_directory data_direc-
- tory default_cache_db_type default_database_type html_directory
+ command_directory config_directory daemon_directory data_direc-
+ tory default_cache_db_type default_database_type html_directory
mail_spool_directory mailq_path manpage_directory meta_directory
- newaliases_path queue_directory readme_directory sendmail_path
+ newaliases_path queue_directory readme_directory sendmail_path
shlib_directory openssl_path
- See the postconf(5) manpage for a description of these parame-
+ See the postconf(5) manpage for a description of these parame-
ters.
This feature was introduced with Postfix 3.0.
WARN=warning_flags
- Specifies non-default gcc compiler warning options for use when
+ Specifies non-default gcc compiler warning options for use when
"make" is invoked in a source subdirectory only.
LICENSE
diff --git a/postfix/html/postconf.5.html b/postfix/html/postconf.5.html
index 7631a7cbc..c71d03260 100644
--- a/postfix/html/postconf.5.html
+++ b/postfix/html/postconf.5.html
@@ -6130,6 +6130,16 @@ configuration parameter. See there for details.
This feature is available in Postfix 2.3 and later.
+
+
+lmtp_tls_trace_size_limit
+(default: 102400)
+
+ The lmtp(8) equivalent of smtp_tls_trace_size_limit.
+
+ This feature is available in Postfix 3.12 and later.
+
+
lmtp_tls_trust_anchor_file
@@ -9912,6 +9922,18 @@ for details.
This feature is available in Postfix 2.8 and later.
+
+
+postscreen_tls_trace_size_limit
+(default: $smtpd_tls_trace_size_limit)
+
+ The postscreen(8) equivalent of smtpd_tls_trace_size_limit.
+postscreen(8) generates the trace via tlsproxy(8); the trace file
+name starts with "tlsproxy-".
+
+ This feature is available in Postfix 3.12 and later.
+
+
postscreen_upstream_proxy_protocol
@@ -15005,6 +15027,19 @@ transmission after STARTTLS.
Do not use "smtp_tls_loglevel = 2" or higher except in case of
problems. Use of loglevel 4 is strongly discouraged.
+ With Postfix 3.12 and later, any of the levels above may be
+followed by ",trace" (for example "smtp_tls_loglevel = 1,trace").
+The "trace" keyword writes a protocol message trace (this does not
+include the content of application data messages) of the TLS
+session to a per-connection file under $queue_directory/tlstrace/,
+capped at smtp_tls_trace_size_limit bytes per file. The path is
+written to the system log at the start of each trace. The "trace"
+keyword is intended for occasional use via smtp_tls_loglevel_maps
+for a specific peer; setting it globally will produce a trace file
+for every TLS session. Old trace files are not removed
+automatically; operators who enable the feature should arrange
+periodic cleanup (find(1), logrotate(8), or similar).
+
This feature is available in Postfix 2.2 and later.
@@ -16029,6 +16064,23 @@ The default time unit is s (seconds).
This feature is available in Postfix 2.2 and later.
+
+
+smtp_tls_trace_size_limit
+(default: 102400)
+
+ Size limit, in bytes, for the TLS protocol transcript that the
+Postfix SMTP client writes when the "trace" keyword is included in
+the TLS loglevel for a peer (smtp_tls_loglevel or
+smtp_tls_loglevel_maps). The transcript is written to a per-
+connection file under $queue_directory/tlstrace/, named
+smtp-pid-time-peer.txt. Once the limit is reached the trace is
+truncated with a one-line note. A value of 0 disables tracing.
+
+
+ This feature is available in Postfix 3.12 and later.
+
+
smtp_tls_trust_anchor_file
@@ -20600,6 +20652,19 @@ transmission after STARTTLS.
Do not use "smtpd_tls_loglevel = 2" or higher except in case
of problems. Use of loglevel 4 is strongly discouraged.
+ With Postfix 3.12 and later, any of the levels above may be
+followed by ",trace" (for example "smtpd_tls_loglevel = 1,trace").
+The "trace" keyword writes a protocol message trace (this does not
+include the content of application data messages) of the TLS
+session to a per-connection file under $queue_directory/tlstrace/,
+capped at smtpd_tls_trace_size_limit bytes per file. The path is
+written to the system log at the start of each trace. The "trace"
+keyword is intended for occasional use via smtpd_tls_loglevel_maps
+for a specific peer; setting it globally will produce a trace file
+for every TLS session. Old trace files are not removed
+automatically; operators who enable the feature should arrange
+periodic cleanup (find(1), logrotate(8), or similar).
+
This feature is available in Postfix 2.2 and later.
@@ -20998,6 +21063,17 @@ The default time unit is s (seconds).
for TLS session ticket support in Postfix 2.11.
+
+
+smtpd_tls_trace_size_limit
+(default: 102400)
+
+ The Postfix SMTP server equivalent of smtp_tls_trace_size_limit.
+File names start with "smtpd-" instead of "smtp-".
+
+ This feature is available in Postfix 3.12 and later.
+
+
smtpd_tls_wrappermode
@@ -22451,6 +22527,18 @@ SSL_CTX_set_options(3).
This feature is available in Postfix 2.11 and later.
+
+
+tls_trace_rate_limit
+(default: 1)
+
+ The maximum number of TLS traces per anvil_rate_time_unit that
+all Postfix daemons combined will create. Specify a value ≤0 to
+disable the limit.
+
+ This feature is available in Postfix 3.12 and later.
+
+
tls_trust_server_ccerts
diff --git a/postfix/html/postscreen.8.html b/postfix/html/postscreen.8.html
index 1f603e910..034a7e506 100644
--- a/postfix/html/postscreen.8.html
+++ b/postfix/html/postscreen.8.html
@@ -459,6 +459,9 @@ POSTSCREEN(8) POSTSCREEN(8)
Optional TLS loglevel override that depends on the remote peer
host name or IP address.
+ postscreen_tls_trace_size_limit ($smtpd_tls_trace_size_limit)
+ The postscreen(8) equivalent of smtpd_tls_trace_size_limit.
+
postscreen_tls_mandatory_ciphers ($smtpd_tls_mandatory_ciphers)
The postscreen(8) equivalent of smtpd_tls_mandatory_ciphers.
@@ -476,12 +479,16 @@ POSTSCREEN(8) POSTSCREEN(8)
postscreen_tls_req_ccert ($smtpd_tls_req_ccert)
The postscreen(8) equivalent of smtpd_tls_req_ccert.
+ tls_trace_rate_limit (1)
+ The maximum number of TLS traces per anvil_rate_time_unit that
+ all Postfix daemons combined will create.
+
OBSOLETE STARTTLS SUPPORT CONTROLS
- These parameters are supported for compatibility with smtpd(8) legacy
+ These parameters are supported for compatibility with smtpd(8) legacy
parameters.
postscreen_use_tls ($smtpd_use_tls)
- Opportunistic TLS: announce STARTTLS support to remote SMTP
+ Opportunistic TLS: announce STARTTLS support to remote SMTP
clients, but do not require that clients use TLS encryption.
postscreen_enforce_tls ($smtpd_enforce_tls)
@@ -490,18 +497,18 @@ POSTSCREEN(8) POSTSCREEN(8)
MISCELLANEOUS CONTROLS
config_directory (see 'postconf -d' output)
- The default location of the Postfix main.cf and master.cf con-
+ The default location of the Postfix main.cf and master.cf con-
figuration files.
delay_logging_resolution_limit (2)
- The maximal number of digits after the decimal point when log-
+ The maximal number of digits after the decimal point when log-
ging delay values.
command_directory (see 'postconf -d' output)
The location of all postfix administrative commands.
max_idle (100s)
- The maximum amount of time that an idle Postfix daemon process
+ The maximum amount of time that an idle Postfix daemon process
waits for an incoming connection before terminating voluntarily.
process_id (read-only)
@@ -514,7 +521,7 @@ POSTSCREEN(8) POSTSCREEN(8)
The syslog facility of Postfix logging.
syslog_name (see 'postconf -d' output)
- A prefix that is prepended to the process name in syslog
+ A prefix that is prepended to the process name in syslog
records, so that, for example, "smtpd" becomes "prefix/smtpd".
Available in Postfix 3.3 and later:
@@ -525,7 +532,7 @@ POSTSCREEN(8) POSTSCREEN(8)
Available in Postfix 3.5 and later:
info_log_address_format (external)
- The email address form that will be used in non-debug logging
+ The email address form that will be used in non-debug logging
(info, warning, etc.).
SEE ALSO
@@ -544,7 +551,7 @@ POSTSCREEN(8) POSTSCREEN(8)
HISTORY
This service was introduced with Postfix version 2.8.
- Many ideas in postscreen(8) were explored in earlier work by Michael
+ Many ideas in postscreen(8) were explored in earlier work by Michael
Tokarev, in OpenBSD spamd, and in MailChannels Traffic Control.
AUTHOR(S)
diff --git a/postfix/html/posttls-finger.1.html b/postfix/html/posttls-finger.1.html
index d52b306fb..5c27d704f 100644
--- a/postfix/html/posttls-finger.1.html
+++ b/postfix/html/posttls-finger.1.html
@@ -20,7 +20,7 @@ POSTTLS-FINGER(1) POSTTLS-FINGER(1)
a domainname; with LMTP it is either a domainname prefixed with inet:
or a pathname prefixed with unix:. If Postfix is built without TLS
support, the resulting posttls-finger(1) program has very limited func-
- tionality, and only the -a, -c, -h, -o, -S, -t, -T and -v options are
+ tionality, and only the -a, -c, -h, -S, -t, -T and -v options are
available.
Note: this is an unsupported test program. No attempt is made to main-
@@ -206,85 +206,98 @@ POSTTLS-FINGER(1) POSTTLS-FINGER(1)
only useful to those who can debug SSL protocol problems
from hex dumps.
+ trace Available with Postfix 3.12 and later. Write a
+ human-readable protocol transcript of the TLS session to
+ a file in the current directory. The file is named
+ process_name-date_time.usec-peer-XXXXXX, where XXXXXX is
+ replaced with a unique string to avoid filename con-
+ flicts. All reconnect attempts in a single command-line
+ run share the same trace file, separated by "=== post-
+ tls-finger reconnect ===" lines. When -X is in effect,
+ tlsproxy(8) generates the trace file under $queue_direc-
+ tory/tlstrace/ instead. The keyword is ignored with a
+ warning if Postfix or OpenSSL was built without TLS trace
+ support.
+
untrusted
- Logs trust chain verification problems. This is turned
- on automatically at security levels that use peer names
- signed by Certification Authorities to validate certifi-
- cates. So while this setting is recognized, you should
+ Logs trust chain verification problems. This is turned
+ on automatically at security levels that use peer names
+ signed by Certification Authorities to validate certifi-
+ cates. So while this setting is recognized, you should
never need to set it explicitly.
peercert
- This logs a one line summary of the remote SMTP server
+ This logs a one line summary of the remote SMTP server
certificate subject, issuer, and fingerprints.
certmatch
- This logs remote SMTP server certificate matching, show-
+ This logs remote SMTP server certificate matching, show-
ing the CN and each subjectAltName and which name
- matched. With DANE, logs matching of TLSA record
+ matched. With DANE, logs matching of TLSA record
trust-anchor and end-entity certificates.
- cache This logs session cache operations, showing whether ses-
- sion caching is effective with the remote SMTP server.
- Automatically used when reconnecting with the -r option;
+ cache This logs session cache operations, showing whether ses-
+ sion caching is effective with the remote SMTP server.
+ Automatically used when reconnecting with the -r option;
rarely needs to be set explicitly.
verbose
Enables verbose logging in the Postfix TLS driver;
includes all of peercert..cache and more.
- The default is routine,certmatch. After a reconnect, peercert,
+ The default is routine,certmatch. After a reconnect, peercert,
certmatch and verbose are automatically disabled while cache and
summary are enabled.
-m count (default: 5)
- When the -r delay option is specified, the -m option determines
- the maximum number of reconnect attempts to use with a server
- behind a load balancer, to see whether connection caching is
- likely to be effective for this destination. Some MTAs don't
- expose the underlying server identity in their EHLO response;
- with these servers there will never be more than 1 reconnection
+ When the -r delay option is specified, the -m option determines
+ the maximum number of reconnect attempts to use with a server
+ behind a load balancer, to see whether connection caching is
+ likely to be effective for this destination. Some MTAs don't
+ expose the underlying server identity in their EHLO response;
+ with these servers there will never be more than 1 reconnection
attempt.
-M insecure_mx_policy (default: dane)
- The TLS policy for MX hosts with "secure" TLSA records when the
- nexthop destination security level is dane, but the MX record
+ The TLS policy for MX hosts with "secure" TLSA records when the
+ nexthop destination security level is dane, but the MX record
was found via an "insecure" MX lookup. See the main.cf documen-
tation for smtp_tls_dane_insecure_mx_policy for details.
-o name=value
- Specify zero or more times to override the value of the main.cf
- parameter name with value. Possible use-cases include overrid-
- ing the values of TLS library parameters, or "myhostname" to
+ Specify zero or more times to override the value of the main.cf
+ parameter name with value. Possible use-cases include overrid-
+ ing the values of TLS library parameters, or "myhostname" to
configure the SMTP EHLO name sent to the remote server.
-p protocols (default: >=TLSv1)
- TLS protocols that posttls-finger(1) will exclude or include.
+ TLS protocols that posttls-finger(1) will exclude or include.
See smtp_tls_mandatory_protocols for details.
-P CApath/ (default: none)
- The OpenSSL CApath/ directory (indexed via c_rehash(1)) for
+ The OpenSSL CApath/ directory (indexed via c_rehash(1)) for
remote SMTP server certificate verification. By default no CAp-
ath is used and no public CAs are trusted.
-r delay
- With a cacheable TLS session, disconnect and reconnect after
+ With a cacheable TLS session, disconnect and reconnect after
delay seconds. Report whether the session is re-used. Retry if a
- new server is encountered, up to 5 times or as specified with
- the -m option. By default reconnection is disabled, specify a
+ new server is encountered, up to 5 times or as specified with
+ the -m option. By default reconnection is disabled, specify a
positive delay to enable this behavior.
-R Use SRV lookup instead of MX.
-s servername
- The server name to send with the TLS Server Name Indication
- (SNI) extension. When the server has DANE TLSA records, this
- parameter is ignored and the TLSA base domain is used instead.
- Otherwise, SNI is not used by default, but can be enabled by
+ The server name to send with the TLS Server Name Indication
+ (SNI) extension. When the server has DANE TLSA records, this
+ parameter is ignored and the TLSA base domain is used instead.
+ Otherwise, SNI is not used by default, but can be enabled by
specifying the desired value with this option.
- -S Disable SMTP; that is, connect to an LMTP server. The default
- port for LMTP over TCP is 24. Alternative ports can specified
- by appending ":servicename" or ":portnumber" to the destination
+ -S Disable SMTP; that is, connect to an LMTP server. The default
+ port for LMTP over TCP is 24. Alternative ports can specified
+ by appending ":servicename" or ":portnumber" to the destination
argument.
-t timeout (default: 30)
@@ -292,41 +305,41 @@ POSTTLS-FINGER(1) POSTTLS-FINGER(1)
reading the remote server's 220 banner.
-T timeout (default: 30)
- The SMTP/LMTP command timeout for EHLO/LHLO, STARTTLS and QUIT.
+ The SMTP/LMTP command timeout for EHLO/LHLO, STARTTLS and QUIT.
- -v Enable verbose Postfix logging. Specify more than once to
+ -v Enable verbose Postfix logging. Specify more than once to
increase the level of verbose logging.
- -w Enable outgoing TLS wrapper mode, or SUBMISSIONS/SMTPS support.
- This is typically provided on port 465 by servers that are com-
- patible with the SMTP-in-SSL protocol, rather than the STARTTLS
- protocol. The destination domain:port must of course provide
+ -w Enable outgoing TLS wrapper mode, or SUBMISSIONS/SMTPS support.
+ This is typically provided on port 465 by servers that are com-
+ patible with the SMTP-in-SSL protocol, rather than the STARTTLS
+ protocol. The destination domain:port must of course provide
such a service.
- -x Prefer RFC7250 non-X.509 raw public key (RPK) server creden-
- tials. By default only X.509 certificates are accepted. This
+ -x Prefer RFC7250 non-X.509 raw public key (RPK) server creden-
+ tials. By default only X.509 certificates are accepted. This
is analogous to setting smtp_tls_enable_rpk = yes in the smtp(8)
client. At the fingerprint security level, when raw public keys
- are enabled, only public key (and not certificate) fingerprints
- will be compared against the specified list of match arguments.
- Certificate fingerprints are fragile when raw public keys are
- solicited, the server may at some point in time start returning
+ are enabled, only public key (and not certificate) fingerprints
+ will be compared against the specified list of match arguments.
+ Certificate fingerprints are fragile when raw public keys are
+ solicited, the server may at some point in time start returning
only the public key.
- -X Enable tlsproxy(8) mode. This is an unsupported mode, for pro-
+ -X Enable tlsproxy(8) mode. This is an unsupported mode, for pro-
gram development only.
[inet:]domain[:port]
Connect via TCP to domain domain, port port. The default port is
- smtp (or 24 with LMTP). With SMTP an MX lookup is performed to
- resolve the domain to a host, unless the domain is enclosed in
- []. If you want to connect to a specific MX host, for instance
- mx1.example.com, specify [mx1.example.com] as the destination
+ smtp (or 24 with LMTP). With SMTP an MX lookup is performed to
+ resolve the domain to a host, unless the domain is enclosed in
+ []. If you want to connect to a specific MX host, for instance
+ mx1.example.com, specify [mx1.example.com] as the destination
and example.com as a match argument. When using DNS, the desti-
- nation domain is assumed fully qualified and no default domain
- or search suffixes are applied; you must use fully-qualified
- names or also enable native host lookups (these don't support
- dane or dane-only as no DNSSEC validation information is avail-
+ nation domain is assumed fully qualified and no default domain
+ or search suffixes are applied; you must use fully-qualified
+ names or also enable native host lookups (these don't support
+ dane or dane-only as no DNSSEC validation information is avail-
able via native lookups).
unix:pathname
@@ -335,8 +348,8 @@ POSTTLS-FINGER(1) POSTTLS-FINGER(1)
match ...
With no match arguments specified, certificate peername matching
uses the compiled-in default strategies for each security level.
- If you specify one or more arguments, these will be used as the
- list of certificate or public-key digests to match for the fin-
+ If you specify one or more arguments, these will be used as the
+ list of certificate or public-key digests to match for the fin-
gerprint level, or as the list of DNS names to match in the cer-
tificate at the verify and secure levels. If the security level
is dane, or dane-only the match names are ignored, and hostname,
diff --git a/postfix/html/smtp.8.html b/postfix/html/smtp.8.html
index 04e8ed84a..97be3c227 100644
--- a/postfix/html/smtp.8.html
+++ b/postfix/html/smtp.8.html
@@ -794,6 +794,16 @@ SMTP(8) SMTP(8)
Optional TLS loglevel override that depends on the remote peer
host name or IP address.
+ smtp_tls_trace_size_limit (102400)
+ Size limit, in bytes, for the TLS protocol transcript that the
+ Postfix SMTP client writes when the "trace" keyword is included
+ in the TLS loglevel for a peer (smtp_tls_loglevel or
+ smtp_tls_loglevel_maps).
+
+ tls_trace_rate_limit (1)
+ The maximum number of TLS traces per anvil_rate_time_unit that
+ all Postfix daemons combined will create.
+
OBSOLETE TLS CONTROLS
The following configuration parameters exist for compatibility with
Postfix versions before 2.3. Support for these will be removed in a
diff --git a/postfix/html/smtpd.8.html b/postfix/html/smtpd.8.html
index 75800d0e9..34d70a154 100644
--- a/postfix/html/smtpd.8.html
+++ b/postfix/html/smtpd.8.html
@@ -678,6 +678,13 @@ SMTPD(8) SMTPD(8)
Optional TLS loglevel override that depends on the remote peer
host name or IP address.
+ smtpd_tls_trace_size_limit (102400)
+ The Postfix SMTP server equivalent of smtp_tls_trace_size_limit.
+
+ tls_trace_rate_limit (1)
+ The maximum number of TLS traces per anvil_rate_time_unit that
+ all Postfix daemons combined will create.
+
OBSOLETE TLS CONTROLS
The following configuration parameters exist for compatibility with
Postfix versions before 2.3. Support for these will be removed in a
diff --git a/postfix/makedefs b/postfix/makedefs
index d21749a23..2ec81ccc4 100644
--- a/postfix/makedefs
+++ b/postfix/makedefs
@@ -109,6 +109,8 @@
# uses snprintf() except on ancient systems.
# .IP \fB-DNO_STDBOOL\fR
# Don't use . This is usually auto-detected.
+# .IP \fB-DNO_TLS_TRACE\fR
+# Build without OpenSSL 3 (and later) debug trace support.
# .RE
# .IP \fBDEBUG=\fIdebug_level\fR
# Specifies a non-default debugging level. The default is \fB-g\fR.
@@ -885,7 +887,7 @@ CCARGS="$CCARGS -DSNAPSHOT"
# Non-production: needs thorough testing, or major changes are still
# needed before the code stabilizes.
-#CCARGS="$CCARGS -DNONPROD"
+#CCARGS="$CCARGS -DNONPROD='\"featurename-nonprod\"'"
# Workaround: prepend Postfix include files before other include files.
CCARGS="-I. -I../../include $CCARGS"
diff --git a/postfix/man/man1/makedefs.1 b/postfix/man/man1/makedefs.1
index d479e80a5..15e8a0b6f 100644
--- a/postfix/man/man1/makedefs.1
+++ b/postfix/man/man1/makedefs.1
@@ -112,6 +112,8 @@ Use sprintf() instead of snprintf(). By default, Postfix
uses snprintf() except on ancient systems.
.IP \fB\-DNO_STDBOOL\fR
Don't use . This is usually auto\-detected.
+.IP \fB\-DNO_TLS_TRACE\fR
+Build without OpenSSL 3 (and later) debug trace support.
.RE
.IP \fBDEBUG=\fIdebug_level\fR
Specifies a non\-default debugging level. The default is \fB\-g\fR.
diff --git a/postfix/man/man1/posttls-finger.1 b/postfix/man/man1/posttls-finger.1
index ee8bd8a68..66cb9ea32 100644
--- a/postfix/man/man1/posttls-finger.1
+++ b/postfix/man/man1/posttls-finger.1
@@ -20,7 +20,7 @@ destination is a domainname; with LMTP it is either a domainname
prefixed with \fBinet:\fR or a pathname prefixed with \fBunix:\fR. If
Postfix is built without TLS support, the resulting \fBposttls\-finger\fR(1)
program has very limited functionality, and only the \fB\-a\fR, \fB\-c\fR,
-\fB\-h\fR, \fB\-o\fR, \fB\-S\fR, \fB\-t\fR, \fB\-T\fR and \fB\-v\fR options
+\fB\-h\fR, \fB\-S\fR, \fB\-t\fR, \fB\-T\fR and \fB\-v\fR options
are available.
Note: this is an unsupported test program. No attempt is made
@@ -187,6 +187,19 @@ Log hexadecimal packet dumps of the SSL handshake; for experts only.
.IP "\fBssl\-session\-packet\-dump\fR"
Log hexadecimal packet dumps of the entire SSL session; only useful
to those who can debug SSL protocol problems from hex dumps.
+.IP "\fBtrace\fR"
+Available with Postfix 3.12 and later. Write a human\-readable
+protocol transcript of the TLS session to a file in the current
+directory. The file is named
+\fIprocess_name\fR\-\fIdate_time\fR.\fIusec\fR\-\fIpeer\fR\-\fIXXXXXX\fR,
+where \fIXXXXXX\fR is replaced with a unique string to avoid
+filename conflicts. All reconnect attempts in a single
+command\-line run share the same trace file, separated by "===
+posttls\-finger reconnect ===" lines. When \fB\-X\fR is in effect,
+tlsproxy(8) generates the trace file under
+\fI$queue_directory\fR/tlstrace/ instead. The keyword is
+ignored with a warning if Postfix or OpenSSL was built without
+TLS trace support.
.IP "\fBuntrusted\fR"
Logs trust chain verification problems. This is turned on
automatically at security levels that use peer names signed
diff --git a/postfix/man/man5/postconf.5 b/postfix/man/man5/postconf.5
index fc5997b19..a5a7a28a4 100644
--- a/postfix/man/man5/postconf.5
+++ b/postfix/man/man5/postconf.5
@@ -3702,6 +3702,10 @@ The LMTP\-specific version of the smtp_tls_session_cache_timeout
configuration parameter. See there for details.
.PP
This feature is available in Postfix 2.3 and later.
+.SH lmtp_tls_trace_size_limit (default: 102400)
+The \fBlmtp\fR(8) equivalent of smtp_tls_trace_size_limit.
+.PP
+This feature is available in Postfix 3.12 and later.
.SH lmtp_tls_trust_anchor_file (default: empty)
The LMTP\-specific version of the smtp_tls_trust_anchor_file
configuration parameter. See there for details.
@@ -6143,6 +6147,12 @@ postscreen_use_tls and postscreen_enforce_tls. See smtpd_tls_security_level
for details.
.PP
This feature is available in Postfix 2.8 and later.
+.SH postscreen_tls_trace_size_limit (default: $smtpd_tls_trace_size_limit)
+The \fBpostscreen\fR(8) equivalent of smtpd_tls_trace_size_limit.
+\fBpostscreen\fR(8) generates the trace via \fBtlsproxy\fR(8); the trace file
+name starts with "tlsproxy\-".
+.PP
+This feature is available in Postfix 3.12 and later.
.SH postscreen_upstream_proxy_protocol (default: empty)
The name of the proxy protocol used by an optional before\-postscreen
proxy agent. When a proxy agent is used, this protocol conveys local
@@ -9894,6 +9904,19 @@ transmission after STARTTLS.
Do not use "smtp_tls_loglevel = 2" or higher except in case of
problems. Use of loglevel 4 is strongly discouraged.
.PP
+With Postfix 3.12 and later, any of the levels above may be
+followed by ",trace" (for example "smtp_tls_loglevel = 1,trace").
+The "trace" keyword writes a protocol message trace (this does not
+include the \fBcontent\fR of application data messages) of the TLS
+session to a per\-connection file under $queue_directory/tlstrace/,
+capped at smtp_tls_trace_size_limit bytes per file. The path is
+written to the system log at the start of each trace. The "trace"
+keyword is intended for occasional use via smtp_tls_loglevel_maps
+for a specific peer; setting it globally will produce a trace file
+for every TLS session. Old trace files are not removed
+automatically; operators who enable the feature should arrange
+periodic cleanup (\fBfind\fR(1), \fBlogrotate\fR(8), or similar).
+.PP
This feature is available in Postfix 2.2 and later.
.SH smtp_tls_loglevel_maps (default: empty)
Optional TLS loglevel override that depends on the remote peer
@@ -10833,6 +10856,16 @@ one\-letter suffix that specifies the time unit). Time units: s
The default time unit is s (seconds).
.PP
This feature is available in Postfix 2.2 and later.
+.SH smtp_tls_trace_size_limit (default: 102400)
+Size limit, in bytes, for the TLS protocol transcript that the
+Postfix SMTP client writes when the "trace" keyword is included in
+the TLS loglevel for a peer (smtp_tls_loglevel or
+smtp_tls_loglevel_maps). The transcript is written to a per\-
+connection file under $queue_directory/tlstrace/, named
+smtp\-pid\-time\-peer.txt. Once the limit is reached the trace is
+truncated with a one\-line note. A value of 0 disables tracing.
+.PP
+This feature is available in Postfix 3.12 and later.
.SH smtp_tls_trust_anchor_file (default: empty)
Zero or more PEM\-format files with trust\-anchor certificates
and/or public keys. If the parameter is not empty the root CAs in
@@ -14422,6 +14455,19 @@ transmission after STARTTLS.
Do not use "smtpd_tls_loglevel = 2" or higher except in case
of problems. Use of loglevel 4 is strongly discouraged.
.PP
+With Postfix 3.12 and later, any of the levels above may be
+followed by ",trace" (for example "smtpd_tls_loglevel = 1,trace").
+The "trace" keyword writes a protocol message trace (this does not
+include the \fBcontent\fR of application data messages) of the TLS
+session to a per\-connection file under $queue_directory/tlstrace/,
+capped at smtpd_tls_trace_size_limit bytes per file. The path is
+written to the system log at the start of each trace. The "trace"
+keyword is intended for occasional use via smtpd_tls_loglevel_maps
+for a specific peer; setting it globally will produce a trace file
+for every TLS session. Old trace files are not removed
+automatically; operators who enable the feature should arrange
+periodic cleanup (\fBfind\fR(1), \fBlogrotate\fR(8), or similar).
+.PP
This feature is available in Postfix 2.2 and later.
.SH smtpd_tls_loglevel_maps (default: empty)
Optional TLS loglevel override that depends on the remote peer
@@ -14768,6 +14814,11 @@ The default time unit is s (seconds).
.PP
This feature is available in Postfix 2.2 and later, and updated
for TLS session ticket support in Postfix 2.11.
+.SH smtpd_tls_trace_size_limit (default: 102400)
+The Postfix SMTP server equivalent of smtp_tls_trace_size_limit.
+File names start with "smtpd\-" instead of "smtp\-".
+.PP
+This feature is available in Postfix 3.12 and later.
.SH smtpd_tls_wrappermode (default: no)
Run the Postfix SMTP server in TLS "wrapper" mode,
instead of using the STARTTLS command.
@@ -15894,6 +15945,12 @@ Postfix >= 3.4. See \fBSSL_CTX_set_options\fR(3).
.br
.PP
This feature is available in Postfix 2.11 and later.
+.SH tls_trace_rate_limit (default: 1)
+The maximum number of TLS traces per anvil_rate_time_unit that
+all Postfix daemons combined will create. Specify a value <=0 to
+disable the limit.
+.PP
+This feature is available in Postfix 3.12 and later.
.SH tls_trust_server_ccerts (default: no)
Whether to trust client certificates whose extended key usage (EKU) lists
only \fBserverAuth\fR and not \fBclientAuth\fR as valid TLS client
diff --git a/postfix/man/man8/anvil.8 b/postfix/man/man8/anvil.8
index 89ea9a6c3..a98503b42 100644
--- a/postfix/man/man8/anvil.8
+++ b/postfix/man/man8/anvil.8
@@ -161,6 +161,27 @@ The \fBanvil\fR(8) server answers with the number of auth
requests per unit time for the (service, client) combination
specified with \fBident\fR:
+.nf
+ \fBstatus=0\fR
+ \fBrate=\fInumber\fR
+.fi
+.SH "TLS TRACE RATE CONTROL"
+.na
+.nf
+.ad
+.fi
+To register a TLS trace event send the following request
+to the \fBanvil\fR(8) server:
+
+.nf
+ \fBrequest=tlstr\fR
+ \fBident=\fIstring\fR
+.fi
+
+The \fBanvil\fR(8) server answers with the number of TLS trace
+requests per unit time for the (service, client) combination
+specified with \fBident\fR:
+
.nf
\fBstatus=0\fR
\fBrate=\fInumber\fR
diff --git a/postfix/man/man8/postscreen.8 b/postfix/man/man8/postscreen.8
index 73a338e77..0adf0c110 100644
--- a/postfix/man/man8/postscreen.8
+++ b/postfix/man/man8/postscreen.8
@@ -434,6 +434,8 @@ The \fBpostscreen\fR(8) equivalent of smtpd_tls_loglevel.
.IP "\fBpostscreen_tls_loglevel_maps ($smtpd_tls_loglevel_maps)\fR"
Optional TLS loglevel override that depends on the remote peer
host name or IP address.
+.IP "\fBpostscreen_tls_trace_size_limit ($smtpd_tls_trace_size_limit)\fR"
+The \fBpostscreen\fR(8) equivalent of smtpd_tls_trace_size_limit.
.IP "\fBpostscreen_tls_mandatory_ciphers ($smtpd_tls_mandatory_ciphers)\fR"
The \fBpostscreen\fR(8) equivalent of smtpd_tls_mandatory_ciphers.
.IP "\fBpostscreen_tls_mandatory_exclude_ciphers ($smtpd_tls_mandatory_exclude_ciphers)\fR"
@@ -444,6 +446,9 @@ The \fBpostscreen\fR(8) equivalent of smtpd_tls_mandatory_protocols.
The \fBpostscreen\fR(8) equivalent of smtpd_tls_protocols.
.IP "\fBpostscreen_tls_req_ccert ($smtpd_tls_req_ccert)\fR"
The \fBpostscreen\fR(8) equivalent of smtpd_tls_req_ccert.
+.IP "\fBtls_trace_rate_limit (1)\fR"
+The maximum number of TLS traces per anvil_rate_time_unit that
+all Postfix daemons combined will create.
.SH "OBSOLETE STARTTLS SUPPORT CONTROLS"
.na
.nf
diff --git a/postfix/man/man8/smtp.8 b/postfix/man/man8/smtp.8
index 11da44d31..e8de6fdb9 100644
--- a/postfix/man/man8/smtp.8
+++ b/postfix/man/man8/smtp.8
@@ -709,6 +709,14 @@ Available in Postfix version 3.12 and later:
.IP "\fBsmtp_tls_loglevel_maps (empty)\fR"
Optional TLS loglevel override that depends on the remote peer
host name or IP address.
+.IP "\fBsmtp_tls_trace_size_limit (102400)\fR"
+Size limit, in bytes, for the TLS protocol transcript that the
+Postfix SMTP client writes when the "trace" keyword is included in
+the TLS loglevel for a peer (smtp_tls_loglevel or
+smtp_tls_loglevel_maps).
+.IP "\fBtls_trace_rate_limit (1)\fR"
+The maximum number of TLS traces per anvil_rate_time_unit that
+all Postfix daemons combined will create.
.SH "OBSOLETE TLS CONTROLS"
.na
.nf
diff --git a/postfix/man/man8/smtpd.8 b/postfix/man/man8/smtpd.8
index f79feab31..03c24b45b 100644
--- a/postfix/man/man8/smtpd.8
+++ b/postfix/man/man8/smtpd.8
@@ -596,6 +596,11 @@ Available in Postfix version 3.12 and later:
.IP "\fBsmtpd_tls_loglevel_maps (empty)\fR"
Optional TLS loglevel override that depends on the remote peer
host name or IP address.
+.IP "\fBsmtpd_tls_trace_size_limit (102400)\fR"
+The Postfix SMTP server equivalent of smtp_tls_trace_size_limit.
+.IP "\fBtls_trace_rate_limit (1)\fR"
+The maximum number of TLS traces per anvil_rate_time_unit that
+all Postfix daemons combined will create.
.SH "OBSOLETE TLS CONTROLS"
.na
.nf
diff --git a/postfix/mantools/postlink b/postfix/mantools/postlink
index 159e05a12..064684fa6 100755
--- a/postfix/mantools/postlink
+++ b/postfix/mantools/postlink
@@ -820,6 +820,12 @@ while (<>) {
s;\btls_fast_shutdown_enable\b;$&;g;
s;\btls_trust_server_ccerts\b;$&;g;
+ s;\blmtp_tls_trace_size_limit\b;$&;g;
+ s;\bpostscreen_tls_trace_size_limit\b;$&;g;
+ s;\bsmtp_tls_trace_size_limit\b;$&;g;
+ s;\bsmtpd_tls_trace_size_limit\b;$&;g;
+ s;\btls_trace_rate_limit\b;$&;g;
+
s;\bfrozen_delivered_to\b;$&;g;
s;\breset_owner_alias\b;$&;g;
s;\benable_long_queue_ids\b;$&;g;
diff --git a/postfix/proto/INSTALL.html b/postfix/proto/INSTALL.html
index 898d5298b..cd6180470 100644
--- a/postfix/proto/INSTALL.html
+++ b/postfix/proto/INSTALL.html
@@ -891,6 +891,12 @@ they are known to be available.
instead of snprintf(). By default, Postfix uses
snprintf() except on ancient systems.
+ | | -DNO_TLS_TRACE | Do not build with support
+for OpenSSL TLS traces. Some vendor OpenSSL runtime libraries are built
+without support for tracing, and Postfix software built on a system with
+TLS trace support would not work when installed on one without. See
+smtp_tls_loglevel in the postconf(5) manual. |
+
| DEBUG=debug_level | Specifies a
non-default compiler debugging level. The default is "-g".
Specify DEBUG= to turn off debugging. |
diff --git a/postfix/proto/postconf.proto b/postfix/proto/postconf.proto
index 77683ff39..7dfaf4845 100644
--- a/postfix/proto/postconf.proto
+++ b/postfix/proto/postconf.proto
@@ -10102,6 +10102,19 @@ transmission after STARTTLS.
Do not use "smtpd_tls_loglevel = 2" or higher except in case
of problems. Use of loglevel 4 is strongly discouraged.
+ With Postfix 3.12 and later, any of the levels above may be
+followed by ",trace" (for example "smtpd_tls_loglevel = 1,trace").
+The "trace" keyword writes a protocol message trace (this does not
+include the content of application data messages) of the TLS
+session to a per-connection file under $queue_directory/tlstrace/,
+capped at smtpd_tls_trace_size_limit bytes per file. The path is
+written to the system log at the start of each trace. The "trace"
+keyword is intended for occasional use via smtpd_tls_loglevel_maps
+for a specific peer; setting it globally will produce a trace file
+for every TLS session. Old trace files are not removed
+automatically; operators who enable the feature should arrange
+periodic cleanup (find(1), logrotate(8), or similar).
+
This feature is available in Postfix 2.2 and later.
%PARAM smtpd_tls_received_header no
@@ -10610,6 +10623,19 @@ transmission after STARTTLS.
Do not use "smtp_tls_loglevel = 2" or higher except in case of
problems. Use of loglevel 4 is strongly discouraged.
+ With Postfix 3.12 and later, any of the levels above may be
+followed by ",trace" (for example "smtp_tls_loglevel = 1,trace").
+The "trace" keyword writes a protocol message trace (this does not
+include the content of application data messages) of the TLS
+session to a per-connection file under $queue_directory/tlstrace/,
+capped at smtp_tls_trace_size_limit bytes per file. The path is
+written to the system log at the start of each trace. The "trace"
+keyword is intended for occasional use via smtp_tls_loglevel_maps
+for a specific peer; setting it globally will produce a trace file
+for every TLS session. Old trace files are not removed
+automatically; operators who enable the feature should arrange
+periodic cleanup (find(1), logrotate(8), or similar).
+
This feature is available in Postfix 2.2 and later.
%PARAM smtp_tls_session_cache_database
@@ -20956,3 +20982,45 @@ until a match is found or until all subnetworks have been tried.
The lmtp(8) equivalent of smtp_tls_loglevel_maps.
This feature is available in Postfix 3.12 and later.
+
+%PARAM smtp_tls_trace_size_limit 102400
+
+ Size limit, in bytes, for the TLS protocol transcript that the
+Postfix SMTP client writes when the "trace" keyword is included in
+the TLS loglevel for a peer (smtp_tls_loglevel or
+smtp_tls_loglevel_maps). The transcript is written to a per-
+connection file under $queue_directory/tlstrace/, named
+smtp-pid-time-peer.txt. Once the limit is reached the trace is
+truncated with a one-line note. A value of 0 disables tracing.
+
+
+ This feature is available in Postfix 3.12 and later.
+
+%PARAM lmtp_tls_trace_size_limit 102400
+
+ The lmtp(8) equivalent of smtp_tls_trace_size_limit.
+
+ This feature is available in Postfix 3.12 and later.
+
+%PARAM smtpd_tls_trace_size_limit 102400
+
+ The Postfix SMTP server equivalent of smtp_tls_trace_size_limit.
+File names start with "smtpd-" instead of "smtp-".
+
+ This feature is available in Postfix 3.12 and later.
+
+%PARAM postscreen_tls_trace_size_limit $smtpd_tls_trace_size_limit
+
+ The postscreen(8) equivalent of smtpd_tls_trace_size_limit.
+postscreen(8) generates the trace via tlsproxy(8); the trace file
+name starts with "tlsproxy-".
+
+ This feature is available in Postfix 3.12 and later.
+
+%PARAM tls_trace_rate_limit 1
+
+ The maximum number of TLS traces per anvil_rate_time_unit that
+all Postfix daemons combined will create. Specify a value ≤0 to
+disable the limit.
+
+ This feature is available in Postfix 3.12 and later.
diff --git a/postfix/proto/stop b/postfix/proto/stop
index f1ed8411c..e7e6efe2f 100644
--- a/postfix/proto/stop
+++ b/postfix/proto/stop
@@ -1718,3 +1718,5 @@ yana
YANA
substrings
Substring
+tlstrace
+yyyymmddhhmmss
diff --git a/postfix/proto/stop.double-history b/postfix/proto/stop.double-history
index 7b282060f..c463b4d8f 100644
--- a/postfix/proto/stop.double-history
+++ b/postfix/proto/stop.double-history
@@ -254,3 +254,8 @@ proto proto stop proto stop double cc
postfix postfix c postmap postmap c postmulti postmulti c
stutter File postmulti postmulti c
with other command line tools File postlog postlog c
+ auxiliary collate collate pl
+ global mail_params h postscreen postscreen c
+ smtpd smtpd c tls tls h tls tls_client c tls tls_misc c
+ smtp smtp c smtp smtp_params c smtp smtp_proto c
+ tlsproxy tlsproxy c tlsproxy tlsproxy_client c
diff --git a/postfix/proto/stop.spell-cc b/postfix/proto/stop.spell-cc
index 77f4e7417..0fb87a2ca 100644
--- a/postfix/proto/stop.spell-cc
+++ b/postfix/proto/stop.spell-cc
@@ -1982,3 +1982,14 @@ openUTS
xff
nameN
valueN
+XXXXXX
+cwd
+mkstemp
+tlstr
+tlstrace
+tlstrs
+yyyymmddhhmmss
+datetime
+getpeername
+overshift
+Sayre
diff --git a/postfix/src/anvil/anvil.c b/postfix/src/anvil/anvil.c
index 884be28b9..c8abec76c 100644
--- a/postfix/src/anvil/anvil.c
+++ b/postfix/src/anvil/anvil.c
@@ -149,6 +149,25 @@
/* \fBstatus=0\fR
/* \fBrate=\fInumber\fR
/* .fi
+/* TLS TRACE RATE CONTROL
+/* .ad
+/* .fi
+/* To register a TLS trace event send the following request
+/* to the \fBanvil\fR(8) server:
+/*
+/* .nf
+/* \fBrequest=tlstr\fR
+/* \fBident=\fIstring\fR
+/* .fi
+/*
+/* The \fBanvil\fR(8) server answers with the number of TLS trace
+/* requests per unit time for the (service, client) combination
+/* specified with \fBident\fR:
+/*
+/* .nf
+/* \fBstatus=0\fR
+/* \fBrate=\fInumber\fR
+/* .fi
/* SECURITY
/* .ad
/* .fi
@@ -318,6 +337,7 @@ typedef struct {
int rcpt; /* recipient rate */
int ntls; /* new TLS session rate */
int auth; /* AUTH request rate */
+ int tlstr; /* TLS trace event rate */
time_t start; /* time of first rate sample */
} ANVIL_REMOTE;
@@ -349,6 +369,7 @@ typedef struct {
(remote)->rcpt = 0; \
(remote)->ntls = 0; \
(remote)->auth = 0; \
+ (remote)->tlstr = 0; \
(remote)->start = event_time(); \
} while(0)
@@ -369,6 +390,7 @@ typedef struct {
(remote)->rcpt = 0; \
(remote)->ntls = 0; \
(remote)->auth = 0; \
+ (remote)->tlstr = 0; \
(remote)->start = _start; \
} while(0)
@@ -399,6 +421,8 @@ typedef struct {
#define ANVIL_REMOTE_INCR_AUTH(remote) ANVIL_REMOTE_INCR_RATE((remote), auth)
+#define ANVIL_REMOTE_INCR_TLSTR(remote) ANVIL_REMOTE_INCR_RATE((remote), tlstr)
+
/* Drop connection from (service, client) state. */
#define ANVIL_REMOTE_DROP_ONE(remote) \
@@ -476,6 +500,7 @@ static ANVIL_MAX max_mail_rate; /* peak message rate */
static ANVIL_MAX max_rcpt_rate; /* peak recipient rate */
static ANVIL_MAX max_ntls_rate; /* peak new TLS session rate */
static ANVIL_MAX max_auth_rate; /* peak AUTH request rate */
+static ANVIL_MAX max_tlstr_rate; /* peak TLS trace request rate */
static int max_cache_size; /* peak cache size */
static time_t max_cache_time; /* time of peak size */
@@ -584,6 +609,7 @@ static void anvil_remote_lookup(VSTREAM *client_stream, const char *ident)
SEND_ATTR_INT(ANVIL_ATTR_RCPT, anvil_remote->rcpt),
SEND_ATTR_INT(ANVIL_ATTR_NTLS, anvil_remote->ntls),
SEND_ATTR_INT(ANVIL_ATTR_AUTH, anvil_remote->auth),
+ SEND_ATTR_INT(ANVIL_ATTR_AUTH, anvil_remote->tlstr),
ATTR_TYPE_END);
}
}
@@ -755,6 +781,35 @@ static void anvil_remote_auth(VSTREAM *client_stream, const char *ident)
ANVIL_MAX_UPDATE(max_auth_rate, anvil_remote->auth, anvil_remote->ident);
}
+/* anvil_remote_tlstr - register TLS trace request event */
+
+static void anvil_remote_tlstr(VSTREAM *client_stream, const char *ident)
+{
+ ANVIL_REMOTE *anvil_remote;
+
+ /*
+ * Be prepared for "postfix reload" after "connect".
+ */
+ if ((anvil_remote =
+ (ANVIL_REMOTE *) htable_find(anvil_remote_map, ident)) == 0)
+ anvil_remote = anvil_remote_conn_update(client_stream, ident);
+
+ /*
+ * Update TLS trace request rate and respond to requesting server.
+ */
+ ANVIL_REMOTE_INCR_TLSTR(anvil_remote);
+ attr_print_plain(client_stream, ATTR_FLAG_NONE,
+ SEND_ATTR_INT(ANVIL_ATTR_STATUS, ANVIL_STAT_OK),
+ SEND_ATTR_INT(ANVIL_ATTR_RATE, anvil_remote->tlstr),
+ ATTR_TYPE_END);
+
+ /*
+ * Update peak statistics.
+ */
+ if (anvil_remote->tlstr > max_tlstr_rate.value)
+ ANVIL_MAX_UPDATE(max_tlstr_rate, anvil_remote->tlstr, anvil_remote->ident);
+}
+
/* anvil_remote_newtls - register newtls event */
static void anvil_remote_newtls(VSTREAM *client_stream, const char *ident)
@@ -893,6 +948,7 @@ static void anvil_status_dump(char *unused_name, char **unused_argv)
ANVIL_MAX_RATE_REPORT(max_rcpt_rate, "recipient");
ANVIL_MAX_RATE_REPORT(max_ntls_rate, "newtls");
ANVIL_MAX_RATE_REPORT(max_auth_rate, "auth");
+ ANVIL_MAX_RATE_REPORT(max_tlstr_rate, "tlstrace");
if (max_cache_size > 0) {
msg_info("statistics: max cache size %d at %.15s",
@@ -923,6 +979,7 @@ static void anvil_service(VSTREAM *client_stream, char *unused_service, char **a
ANVIL_REQ_DISC, anvil_remote_disconnect,
ANVIL_REQ_NTLS_STAT, anvil_remote_newtls_stat,
ANVIL_REQ_AUTH, anvil_remote_auth,
+ ANVIL_REQ_TLSTR, anvil_remote_tlstr,
ANVIL_REQ_LOOKUP, anvil_remote_lookup,
0, 0,
};
diff --git a/postfix/src/global/anvil_clnt.c b/postfix/src/global/anvil_clnt.c
index fff9ec7f4..e536c52d0 100644
--- a/postfix/src/global/anvil_clnt.c
+++ b/postfix/src/global/anvil_clnt.c
@@ -49,13 +49,20 @@
/* const char *addr;
/* int *auths;
/*
+/* int anvil_clnt_tlstr(anvil_clnt, service, addr, tlstrs)
+/* ANVIL_CLNT *anvil_clnt;
+/* const char *service;
+/* const char *addr;
+/* int *tlstrs;
+/*
/* int anvil_clnt_disconnect(anvil_clnt, service, addr)
/* ANVIL_CLNT *anvil_clnt;
/* const char *service;
/* const char *addr;
/*
/* int anvil_clnt_lookup(anvil_clnt, service, addr, count,
-/* rate, msgs, rcpts, ntls, auths)
+/* rate, msgs, rcpts, ntls, auths,
+/* tlstrs)
/* ANVIL_CLNT *anvil_clnt;
/* const char *service;
/* const char *addr;
@@ -65,6 +72,7 @@
/* int *rcpts;
/* int *ntls;
/* int *auths;
+/* int *tlstrs;
/* DESCRIPTION
/* anvil_clnt_create() instantiates a local anvil service
/* client endpoint.
@@ -91,6 +99,9 @@
/* anvil_clnt_auth() registers an AUTH event and returns the
/* current AUTH event rate for the specified remote client.
/*
+/* anvil_clnt_tlstr() registers a TLS trace event and returns the
+/* current TLS trace event rate for the specified service and peer.
+/*
/* anvil_clnt_disconnect() informs the anvil server that a remote
/* client has disconnected.
/*
@@ -212,7 +223,8 @@ void anvil_clnt_free(ANVIL_CLNT *anvil_clnt)
int anvil_clnt_lookup(ANVIL_CLNT *anvil_clnt, const char *service,
const char *addr, int *count, int *rate,
- int *msgs, int *rcpts, int *newtls, int *auths)
+ int *msgs, int *rcpts, int *newtls,
+ int *auths, int *tlstrs)
{
char *ident = ANVIL_IDENT(service, addr);
int status;
@@ -230,7 +242,8 @@ int anvil_clnt_lookup(ANVIL_CLNT *anvil_clnt, const char *service,
RECV_ATTR_INT(ANVIL_ATTR_RCPT, rcpts),
RECV_ATTR_INT(ANVIL_ATTR_NTLS, newtls),
RECV_ATTR_INT(ANVIL_ATTR_AUTH, auths),
- ATTR_TYPE_END) != 7)
+ RECV_ATTR_INT(ANVIL_ATTR_TLSTR, tlstrs),
+ ATTR_TYPE_END) != 8)
status = ANVIL_STAT_FAIL;
else if (status != ANVIL_STAT_OK)
status = ANVIL_STAT_FAIL;
@@ -383,6 +396,30 @@ int anvil_clnt_auth(ANVIL_CLNT *anvil_clnt, const char *service,
return (status);
}
+/* anvil_clnt_tlstr - heads-up and status query */
+
+int anvil_clnt_tlstr(ANVIL_CLNT *anvil_clnt, const char *service,
+ const char *addr, int *tlstrs)
+{
+ char *ident = ANVIL_IDENT(service, addr);
+ int status;
+
+ if (attr_clnt_request((ATTR_CLNT *) anvil_clnt,
+ ATTR_FLAG_NONE, /* Query attributes. */
+ SEND_ATTR_STR(ANVIL_ATTR_REQ, ANVIL_REQ_TLSTR),
+ SEND_ATTR_STR(ANVIL_ATTR_IDENT, ident),
+ ATTR_TYPE_END,
+ ATTR_FLAG_MISSING, /* Reply attributes. */
+ RECV_ATTR_INT(ANVIL_ATTR_STATUS, &status),
+ RECV_ATTR_INT(ANVIL_ATTR_RATE, tlstrs),
+ ATTR_TYPE_END) != 2)
+ status = ANVIL_STAT_FAIL;
+ else if (status != ANVIL_STAT_OK)
+ status = ANVIL_STAT_FAIL;
+ myfree(ident);
+ return (status);
+}
+
/* anvil_clnt_disconnect - heads-up only */
int anvil_clnt_disconnect(ANVIL_CLNT *anvil_clnt, const char *service,
diff --git a/postfix/src/global/anvil_clnt.h b/postfix/src/global/anvil_clnt.h
index d060155a6..ca93812b9 100644
--- a/postfix/src/global/anvil_clnt.h
+++ b/postfix/src/global/anvil_clnt.h
@@ -36,6 +36,7 @@
#define ANVIL_REQ_NTLS_STAT "newtls_status"
#define ANVIL_REQ_AUTH "auth"
#define ANVIL_REQ_LOOKUP "lookup"
+#define ANVIL_REQ_TLSTR "tlstr"
#define ANVIL_ATTR_IDENT "ident"
#define ANVIL_ATTR_COUNT "count"
#define ANVIL_ATTR_RATE "rate"
@@ -43,6 +44,7 @@
#define ANVIL_ATTR_RCPT "rcpt"
#define ANVIL_ATTR_NTLS "newtls"
#define ANVIL_ATTR_AUTH "auth"
+#define ANVIL_ATTR_TLSTR "tlstr"
#define ANVIL_ATTR_STATUS "status"
#define ANVIL_STAT_OK 0
@@ -60,7 +62,8 @@ extern int anvil_clnt_rcpt(ANVIL_CLNT *, const char *, const char *, int *);
extern int anvil_clnt_newtls(ANVIL_CLNT *, const char *, const char *, int *);
extern int anvil_clnt_newtls_stat(ANVIL_CLNT *, const char *, const char *, int *);
extern int anvil_clnt_auth(ANVIL_CLNT *, const char *, const char *, int *);
-extern int anvil_clnt_lookup(ANVIL_CLNT *, const char *, const char *, int *, int *, int *, int *, int *, int *);
+extern int anvil_clnt_tlstr(ANVIL_CLNT *, const char *, const char *, int *);
+extern int anvil_clnt_lookup(ANVIL_CLNT *, const char *, const char *, int *, int *, int *, int *, int *, int *, int *);
extern int anvil_clnt_disconnect(ANVIL_CLNT *, const char *, const char *);
extern void anvil_clnt_free(ANVIL_CLNT *);
diff --git a/postfix/src/global/mail_params.h b/postfix/src/global/mail_params.h
index f35f81faa..ec934d4a1 100644
--- a/postfix/src/global/mail_params.h
+++ b/postfix/src/global/mail_params.h
@@ -1445,6 +1445,10 @@ extern char *var_smtpd_tls_loglevel;
#define DEF_SMTPD_TLS_LOGLEVEL_MAPS ""
extern char *var_smtpd_tls_loglevel_maps;
+#define VAR_SMTPD_TLS_TRACE_SIZE_LIMIT "smtpd_tls_trace_size_limit"
+#define DEF_SMTPD_TLS_TRACE_SIZE_LIMIT 102400
+extern int var_smtpd_tls_trace_size_limit;
+
#define VAR_SMTPD_TLS_RECHEAD "smtpd_tls_received_header"
#define DEF_SMTPD_TLS_RECHEAD 0
extern bool var_smtpd_tls_received_header;
@@ -1626,6 +1630,12 @@ extern char *var_lmtp_tls_loglevel; /* In tlsmgr(8) */
extern char *var_smtp_tls_loglevel_maps;
extern char *var_lmtp_tls_loglevel_maps;
+#define VAR_SMTP_TLS_TRACE_SIZE_LIMIT "smtp_tls_trace_size_limit"
+#define DEF_SMTP_TLS_TRACE_SIZE_LIMIT 102400
+#define VAR_LMTP_TLS_TRACE_SIZE_LIMIT "lmtp_tls_trace_size_limit"
+#define DEF_LMTP_TLS_TRACE_SIZE_LIMIT 102400
+extern int var_smtp_tls_trace_size_limit;
+
#define VAR_SMTP_TLS_NOTEOFFER "smtp_tls_note_starttls_offer"
#define DEF_SMTP_TLS_NOTEOFFER 0
#define VAR_LMTP_TLS_NOTEOFFER "lmtp_tls_note_starttls_offer"
@@ -4757,6 +4767,10 @@ extern char *var_psc_tls_loglevel;
#define DEF_PSC_TLS_LOGLEVEL_MAPS "$" VAR_SMTPD_TLS_LOGLEVEL_MAPS
extern char *var_psc_tls_loglevel_maps;
+#define VAR_PSC_TLS_TRACE_SIZE_LIMIT "postscreen_tls_trace_size_limit"
+#define DEF_PSC_TLS_TRACE_SIZE_LIMIT "$" VAR_SMTPD_TLS_TRACE_SIZE_LIMIT
+extern int var_psc_tls_trace_size_limit;
+
#define VAR_PSC_TLS_MAND_CIPH "postscreen_tls_mandatory_ciphers"
#define DEF_PSC_TLS_MAND_CIPH "$" VAR_SMTPD_TLS_MAND_CIPH
extern char *var_psc_tls_mand_ciph;
@@ -4781,6 +4795,13 @@ extern int var_psc_tls_ccert_vd;
#define DEF_PSC_STARTTLS_TMOUT "$" VAR_SMTPD_STARTTLS_TMOUT
extern int var_psc_starttls_tmout;
+ /*
+ * How many TLS traces per anvil(8) time unit.
+ */
+#define VAR_TLS_TRACE_ANVIL_RATE "tls_trace_rate_limit"
+#define DEF_TLS_TRACE_ANVIL_RATE 1
+extern int var_tls_trace_anvil_rate;
+
/* LICENSE
/* .ad
/* .fi
diff --git a/postfix/src/global/mail_version.h b/postfix/src/global/mail_version.h
index 96e4ad8b9..a53144448 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 "20260510"
+#define MAIL_RELEASE_DATE "20260514"
#define MAIL_VERSION_NUMBER "3.12"
#ifdef SNAPSHOT
@@ -30,7 +30,7 @@
#endif
#ifdef NONPROD
-#define MAIL_VERSION_PROD "-nonprod"
+#define MAIL_VERSION_PROD "-" NONPROD
#else
#define MAIL_VERSION_PROD ""
#endif
diff --git a/postfix/src/postscreen/postscreen.c b/postfix/src/postscreen/postscreen.c
index 9614608da..22ab219fd 100644
--- a/postfix/src/postscreen/postscreen.c
+++ b/postfix/src/postscreen/postscreen.c
@@ -398,6 +398,8 @@
/* .IP "\fBpostscreen_tls_loglevel_maps ($smtpd_tls_loglevel_maps)\fR"
/* Optional TLS loglevel override that depends on the remote peer
/* host name or IP address.
+/* .IP "\fBpostscreen_tls_trace_size_limit ($smtpd_tls_trace_size_limit)\fR"
+/* The \fBpostscreen\fR(8) equivalent of smtpd_tls_trace_size_limit.
/* .IP "\fBpostscreen_tls_mandatory_ciphers ($smtpd_tls_mandatory_ciphers)\fR"
/* The \fBpostscreen\fR(8) equivalent of smtpd_tls_mandatory_ciphers.
/* .IP "\fBpostscreen_tls_mandatory_exclude_ciphers ($smtpd_tls_mandatory_exclude_ciphers)\fR"
@@ -408,6 +410,9 @@
/* The \fBpostscreen\fR(8) equivalent of smtpd_tls_protocols.
/* .IP "\fBpostscreen_tls_req_ccert ($smtpd_tls_req_ccert)\fR"
/* The \fBpostscreen\fR(8) equivalent of smtpd_tls_req_ccert.
+/* .IP "\fBtls_trace_rate_limit (1)\fR"
+/* The maximum number of TLS traces per anvil_rate_time_unit that
+/* all Postfix daemons combined will create.
/* OBSOLETE STARTTLS SUPPORT CONTROLS
/* .ad
/* .fi
@@ -646,6 +651,7 @@ char *var_smtpd_tls_proto;
int var_smtpd_tls_ccert_vd;
int var_smtpd_starttls_tmout;
+int var_smtpd_tls_trace_size_limit;
bool var_psc_tls_ask_ccert;
bool var_psc_tls_enable_rpk;
@@ -676,6 +682,7 @@ char *var_psc_tls_proto;
int var_psc_tls_ccert_vd;
int var_psc_starttls_tmout;
+int var_psc_tls_trace_size_limit;
/*
* Global variables.
@@ -1369,6 +1376,7 @@ int main(int argc, char **argv)
VAR_PSC_CMD_COUNT, DEF_PSC_CMD_COUNT, &var_psc_cmd_count, 1, 0,
VAR_SMTPD_CCONN_LIMIT, DEF_SMTPD_CCONN_LIMIT, &var_smtpd_cconn_limit, 0, 0,
VAR_SMTPD_TLS_CCERT_VD, DEF_SMTPD_TLS_CCERT_VD, &var_smtpd_tls_ccert_vd, 0, 0,
+ VAR_SMTPD_TLS_TRACE_SIZE_LIMIT, DEF_SMTPD_TLS_TRACE_SIZE_LIMIT, &var_smtpd_tls_trace_size_limit, 0, 0,
0,
};
static const CONFIG_NINT_TABLE nint_table[] = {
@@ -1377,6 +1385,7 @@ int main(int argc, char **argv)
VAR_PSC_CCONN_LIMIT, DEF_PSC_CCONN_LIMIT, &var_psc_cconn_limit, 0, 0,
VAR_PSC_DNSBL_ALTHRESH, DEF_PSC_DNSBL_ALTHRESH, &var_psc_dnsbl_althresh, 0, 0,
VAR_PSC_TLS_CCERT_VD, DEF_PSC_TLS_CCERT_VD, &var_psc_tls_ccert_vd, 0, 0,
+ VAR_PSC_TLS_TRACE_SIZE_LIMIT, DEF_PSC_TLS_TRACE_SIZE_LIMIT, &var_psc_tls_trace_size_limit, 0, 0,
0,
};
static const CONFIG_TIME_TABLE time_table[] = {
diff --git a/postfix/src/postscreen/postscreen_tls_conf.c b/postfix/src/postscreen/postscreen_tls_conf.c
index 331d29826..3ba6d0908 100644
--- a/postfix/src/postscreen/postscreen_tls_conf.c
+++ b/postfix/src/postscreen/postscreen_tls_conf.c
@@ -310,7 +310,9 @@ bool psc_tls_pre_start(const PSC_STATE *state,
namaddr = state->smtp_client_addr_port,
cipher_grade = cipher_grade,
cipher_exclusions = STR(cipher_exclusions),
- mdalg = var_psc_tls_fpt_dgst);
+ mdalg = var_psc_tls_fpt_dgst,
+ trace_size_limit = var_psc_tls_trace_size_limit,
+ trace_peer = state->smtp_client_addr);
return (true);
}
diff --git a/postfix/src/posttls-finger/posttls-finger.c b/postfix/src/posttls-finger/posttls-finger.c
index da3cfe469..690aedb40 100644
--- a/postfix/src/posttls-finger/posttls-finger.c
+++ b/postfix/src/posttls-finger/posttls-finger.c
@@ -14,7 +14,7 @@
/* prefixed with \fBinet:\fR or a pathname prefixed with \fBunix:\fR. If
/* Postfix is built without TLS support, the resulting \fBposttls-finger\fR(1)
/* program has very limited functionality, and only the \fB-a\fR, \fB-c\fR,
-/* \fB-h\fR, \fB-o\fR, \fB-S\fR, \fB-t\fR, \fB-T\fR and \fB-v\fR options
+/* \fB-h\fR, \fB-S\fR, \fB-t\fR, \fB-T\fR and \fB-v\fR options
/* are available.
/*
/* Note: this is an unsupported test program. No attempt is made
@@ -181,6 +181,19 @@
/* .IP "\fBssl-session-packet-dump\fR"
/* Log hexadecimal packet dumps of the entire SSL session; only useful
/* to those who can debug SSL protocol problems from hex dumps.
+/* .IP "\fBtrace\fR"
+/* Available with Postfix 3.12 and later. Write a human-readable
+/* protocol transcript of the TLS session to a file in the current
+/* directory. The file is named
+/* \fIprocess_name\fR-\fIdate_time\fR.\fIusec\fR-\fIpeer\fR-\fIXXXXXX\fR,
+/* where \fIXXXXXX\fR is replaced with a unique string to avoid
+/* filename conflicts. All reconnect attempts in a single
+/* command-line run share the same trace file, separated by "===
+/* posttls-finger reconnect ===" lines. When \fB-X\fR is in effect,
+/* tlsproxy(8) generates the trace file under
+/* \fI$queue_directory\fR/tlstrace/ instead. The keyword is
+/* ignored with a warning if Postfix or OpenSSL was built without
+/* TLS trace support.
/* .IP "\fBuntrusted\fR"
/* Logs trust chain verification problems. This is turned on
/* automatically at security levels that use peer names signed
@@ -352,6 +365,7 @@
#include
#include
#include
+#include /* INT_MAX */
#include
#include
#include
@@ -509,12 +523,18 @@ typedef struct STATE {
char *protocols; /* Protocol inclusion/exclusion */
int mxinsec_level; /* DANE for insecure MX RRs? */
int tlsproxy_mode;
+ char *trace_file; /* full path; built once per run */
+ int trace_failed; /* give up after first open failure */
#endif
OPTIONS options; /* JCL */
} STATE;
static DNS_RR *host_addr(STATE *, const char *);
+#if defined(USE_TLS) && defined(HAVE_SSL_TRACE)
+static BIO *posttls_trace_open(void *, const char *);
+#endif
+
#define HNAME(addr) (addr->qname)
/*
@@ -840,7 +860,9 @@ static int starttls(STATE *state)
tlsrpt = 0,
ffail_type = 0,
dane = state->ddane ?
- state->ddane : state->dane);
+ state->ddane : state->dane,
+ trace_size_limit = INT_MAX,
+ trace_peer = state->paddr);
#define PROXY_OPEN_FLAGS \
(TLS_PROXY_FLAG_ROLE_CLIENT | TLS_PROXY_FLAG_SEND_CONTEXT)
@@ -947,7 +969,15 @@ static int starttls(STATE *state)
mdalg = state->mdalg,
tlsrpt = 0,
ffail_type = 0,
- dane = state->ddane ? state->ddane : state->dane);
+ dane = state->ddane ? state->ddane : state->dane,
+ trace_size_limit = INT_MAX,
+#ifdef HAVE_SSL_TRACE
+ trace_open = posttls_trace_open,
+#else
+ trace_open = 0,
+#endif
+ trace_arg = (void *) state,
+ trace_peer = state->paddr);
} /* tlsproxy_mode */
vstring_free(cipher_exclusions);
if (state->helo) {
@@ -1831,6 +1861,8 @@ static void cleanup(STATE *state)
myfree(state->certfile);
myfree(state->keyfile);
myfree(state->sni);
+ if (state->trace_file)
+ myfree(state->trace_file);
if (state->options.level)
myfree(state->options.level);
myfree(state->options.logopts);
@@ -1873,20 +1905,66 @@ static void usage(void)
exit(1);
}
-#ifdef USE_TLS
-#ifndef OPENSSL_NO_SSL_TRACE
-static void ssl_trace(int write_p, int version, int content_type,
- const void *buf, size_t msglen, SSL *ssl, void *arg)
+#if defined(USE_TLS) && defined(HAVE_SSL_TRACE)
+
+/* posttls_trace_open - open or reopen the SSL_trace destination file */
+
+static BIO *posttls_trace_open(void *arg, const char *trace_peer)
{
- BIO *out = (BIO *) arg;
+ STATE *state = (STATE *) arg;
+ struct timeval tv;
+ FILE *fp;
+ BIO *bio;
+
+ /*
+ * If the destination became non-writable on a previous attempt, do not
+ * keep retrying: the resulting trace file would silently miss the
+ * earlier sessions.
+ */
+ if (state->trace_failed)
+ return (0);
+
+ /*
+ * Build the path lazily on the first connection of a posttls-finger run.
+ * All subsequent reconnects (-r/-m) reuse the same file, so one CLI
+ * invocation produces one trace file with each handshake's transcript
+ * appended after a separator line.
+ */
+ if (state->trace_file == 0) {
+ VSTRING *path = vstring_alloc(64);
+
+ bio = tls_trace_create_file(path, trace_peer);
+ state->trace_file = vstring_export(path);
+ if (bio == 0) {
+ /* Warning is already logged. */
+ state->trace_failed = 1;
+ return (0);
+ }
+ msg_info("TLS protocol trace saved to %s", state->trace_file);
+ }
- /* Avoid mixing BIO and vstream/stdio buffers */
- vstream_fflush(VSTREAM_OUT);
- SSL_trace(write_p, version, content_type, buf, msglen, ssl, out);
- (void) BIO_flush(out);
+ /*
+ * Truncate on the first connection so the file reflects only the current
+ * invocation; append on subsequent reconnects with a delimiter line in
+ * between.
+ */
+ else {
+ if ((fp = fopen(state->trace_file, "a")) == 0) {
+ msg_warn("TLS trace: cannot open %s: %m", state->trace_file);
+ state->trace_failed = 1;
+ return (0);
+ }
+ if ((bio = BIO_new_fp(fp, BIO_CLOSE)) == 0) {
+ msg_warn("TLS trace: BIO_new_fp() failed for %s", state->trace_file);
+ (void) fclose(fp);
+ state->trace_failed = 1;
+ return (0);
+ }
+ (void) fputs("\n=== posttls-finger reconnect ===\n\n", fp);
+ }
+ return (bio);
}
-#endif
#endif
/* tls_init - initialize application TLS library context */
@@ -1916,13 +1994,6 @@ static void tls_init(STATE *state)
CAfile = state->CAfile,
CApath = state->CApath,
mdalg = state->mdalg);
-#ifndef OPENSSL_NO_SSL_TRACE
- if (state->tls_ctx != 0
- && (state->log_mask & TLS_LOG_DEBUG)) {
- SSL_CTX_set_msg_callback(state->tls_ctx->ssl_ctx, ssl_trace);
- SSL_CTX_set_msg_callback_arg(state->tls_ctx->ssl_ctx, state->tls_bio);
- }
-#endif
#endif
}
@@ -1976,6 +2047,7 @@ static void parse_options(STATE *state, int argc, char *argv[])
state->level = TLS_LEV_DANE;
state->mxinsec_level = TLS_LEV_DANE;
state->tlsproxy_mode = 0;
+ state->trace_file = 0; /* lazily constructed at trace_open */
#else
#define TLSOPTS ""
state->level = TLS_LEV_NONE;
diff --git a/postfix/src/smtp/lmtp_params.c b/postfix/src/smtp/lmtp_params.c
index 7e76bf56b..5c7d9c8e9 100644
--- a/postfix/src/smtp/lmtp_params.c
+++ b/postfix/src/smtp/lmtp_params.c
@@ -103,6 +103,7 @@
VAR_LMTP_REUSE_COUNT, DEF_LMTP_REUSE_COUNT, &var_smtp_reuse_count, 0, 0,
#ifdef USE_TLS
VAR_LMTP_TLS_SCERT_VD, DEF_LMTP_TLS_SCERT_VD, &var_smtp_tls_scert_vd, 0, 0,
+ VAR_LMTP_TLS_TRACE_SIZE_LIMIT, DEF_LMTP_TLS_TRACE_SIZE_LIMIT, &var_smtp_tls_trace_size_limit, 0, 0,
#endif
VAR_LMTP_MIN_DATA_RATE, DEF_LMTP_MIN_DATA_RATE, &var_smtp_min_data_rate, 1, 0,
0,
diff --git a/postfix/src/smtp/smtp.c b/postfix/src/smtp/smtp.c
index b2d38d4a9..5f9be87d4 100644
--- a/postfix/src/smtp/smtp.c
+++ b/postfix/src/smtp/smtp.c
@@ -675,6 +675,14 @@
/* .IP "\fBsmtp_tls_loglevel_maps (empty)\fR"
/* Optional TLS loglevel override that depends on the remote peer
/* host name or IP address.
+/* .IP "\fBsmtp_tls_trace_size_limit (102400)\fR"
+/* Size limit, in bytes, for the TLS protocol transcript that the
+/* Postfix SMTP client writes when the "trace" keyword is included in
+/* the TLS loglevel for a peer (smtp_tls_loglevel or
+/* smtp_tls_loglevel_maps).
+/* .IP "\fBtls_trace_rate_limit (1)\fR"
+/* The maximum number of TLS traces per anvil_rate_time_unit that
+/* all Postfix daemons combined will create.
/* OBSOLETE TLS CONTROLS
/* .ad
/* .fi
@@ -1143,6 +1151,7 @@ bool var_smtp_tls_enforce_peername;
char *var_smtp_tls_key_file;
char *var_smtp_tls_loglevel;
char *var_smtp_tls_loglevel_maps;
+int var_smtp_tls_trace_size_limit;
bool var_smtp_tls_note_starttls_offer;
char *var_smtp_tls_mand_proto;
char *var_smtp_tls_sec_cmatch;
diff --git a/postfix/src/smtp/smtp_params.c b/postfix/src/smtp/smtp_params.c
index e206e4201..a866fa949 100644
--- a/postfix/src/smtp/smtp_params.c
+++ b/postfix/src/smtp/smtp_params.c
@@ -104,6 +104,7 @@
VAR_SMTP_REUSE_COUNT, DEF_SMTP_REUSE_COUNT, &var_smtp_reuse_count, 0, 0,
#ifdef USE_TLS
VAR_SMTP_TLS_SCERT_VD, DEF_SMTP_TLS_SCERT_VD, &var_smtp_tls_scert_vd, 0, 0,
+ VAR_SMTP_TLS_TRACE_SIZE_LIMIT, DEF_SMTP_TLS_TRACE_SIZE_LIMIT, &var_smtp_tls_trace_size_limit, 0, 0,
#endif
VAR_SMTP_MIN_DATA_RATE, DEF_SMTP_MIN_DATA_RATE, &var_smtp_min_data_rate, 1, 0,
0,
diff --git a/postfix/src/smtp/smtp_proto.c b/postfix/src/smtp/smtp_proto.c
index f67360882..1e7a7521e 100644
--- a/postfix/src/smtp/smtp_proto.c
+++ b/postfix/src/smtp/smtp_proto.c
@@ -1097,7 +1097,9 @@ static int smtp_start_tls(SMTP_STATE *state)
tlsrpt = 0,
#endif
ffail_type = 0,
- dane = state->tls->dane);
+ dane = state->tls->dane,
+ trace_size_limit = var_smtp_tls_trace_size_limit,
+ trace_peer = STR(iter->addr));
/*
* The tlsproxy(8) server enforces timeouts that are larger than
@@ -1228,7 +1230,11 @@ static int smtp_start_tls(SMTP_STATE *state)
tlsrpt = 0,
#endif
ffail_type = state->tls->ext_policy_failure,
- dane = state->tls->dane);
+ dane = state->tls->dane,
+ trace_size_limit = var_smtp_tls_trace_size_limit,
+ trace_open = 0,
+ trace_arg = 0,
+ trace_peer = STR(iter->addr));
/*
* At this point there must not be any pending data in the stream
diff --git a/postfix/src/smtpd/smtpd.c b/postfix/src/smtpd/smtpd.c
index 4480eb954..091f0f549 100644
--- a/postfix/src/smtpd/smtpd.c
+++ b/postfix/src/smtpd/smtpd.c
@@ -562,6 +562,11 @@
/* .IP "\fBsmtpd_tls_loglevel_maps (empty)\fR"
/* Optional TLS loglevel override that depends on the remote peer
/* host name or IP address.
+/* .IP "\fBsmtpd_tls_trace_size_limit (102400)\fR"
+/* The Postfix SMTP server equivalent of smtp_tls_trace_size_limit.
+/* .IP "\fBtls_trace_rate_limit (1)\fR"
+/* The maximum number of TLS traces per anvil_rate_time_unit that
+/* all Postfix daemons combined will create.
/* OBSOLETE TLS CONTROLS
/* .ad
/* .fi
@@ -1529,6 +1534,7 @@ char *var_smtpd_tls_dkey_file;
char *var_smtpd_tls_key_file;
char *var_smtpd_tls_loglevel;
char *var_smtpd_tls_loglevel_maps;
+int var_smtpd_tls_trace_size_limit;
char *var_smtpd_tls_mand_proto;
bool var_smtpd_tls_received_header;
bool var_smtpd_tls_req_ccert;
@@ -5345,7 +5351,9 @@ static void smtpd_start_tls(SMTPD_STATE *state)
namaddr = state->namaddr,
cipher_grade = cipher_grade,
cipher_exclusions = STR(cipher_exclusions),
- mdalg = var_smtpd_tls_fpt_dgst);
+ mdalg = var_smtpd_tls_fpt_dgst,
+ trace_size_limit = var_smtpd_tls_trace_size_limit,
+ trace_peer = state->addr);
/*
* Note: state->tlsproxy is left open when smtp_flush() calls longjmp(),
@@ -5394,7 +5402,11 @@ static void smtpd_start_tls(SMTPD_STATE *state)
namaddr = state->namaddr,
cipher_grade = cipher_grade,
cipher_exclusions = STR(cipher_exclusions),
- mdalg = var_smtpd_tls_fpt_dgst);
+ mdalg = var_smtpd_tls_fpt_dgst,
+ trace_size_limit = var_smtpd_tls_trace_size_limit,
+ trace_open = 0,
+ trace_arg = 0,
+ trace_peer = state->addr);
#endif /* USE_TLSPROXY */
@@ -6885,6 +6897,7 @@ int main(int argc, char **argv)
VAR_SMTPD_CIPV6_PREFIX, DEF_SMTPD_CIPV6_PREFIX, &var_smtpd_cipv6_prefix, 0, MAX_SMTPD_CIPV6_PREFIX,
#ifdef USE_TLS
VAR_SMTPD_TLS_CCERT_VD, DEF_SMTPD_TLS_CCERT_VD, &var_smtpd_tls_ccert_vd, 0, 0,
+ VAR_SMTPD_TLS_TRACE_SIZE_LIMIT, DEF_SMTPD_TLS_TRACE_SIZE_LIMIT, &var_smtpd_tls_trace_size_limit, 0, 0,
#endif
VAR_SMTPD_SASL_RESP_LIMIT, DEF_SMTPD_SASL_RESP_LIMIT, &var_smtpd_sasl_resp_limit, DEF_SMTPD_SASL_RESP_LIMIT, 0,
VAR_SMTPD_POLICY_REQ_LIMIT, DEF_SMTPD_POLICY_REQ_LIMIT, &var_smtpd_policy_req_limit, 0, 0,
diff --git a/postfix/src/tls/Makefile.in b/postfix/src/tls/Makefile.in
index aa11f78fd..d443c3832 100644
--- a/postfix/src/tls/Makefile.in
+++ b/postfix/src/tls/Makefile.in
@@ -329,6 +329,7 @@ tls_mgr.o: tls_mgr.c
tls_mgr.o: tls_mgr.h
tls_mgr.o: tls_scache.h
tls_misc.o: ../../include/argv.h
+tls_misc.o: ../../include/been_here.h
tls_misc.o: ../../include/check_arg.h
tls_misc.o: ../../include/dict.h
tls_misc.o: ../../include/dns.h
diff --git a/postfix/src/tls/tls.h b/postfix/src/tls/tls.h
index 8ce9c213c..80f4bc57e 100644
--- a/postfix/src/tls/tls.h
+++ b/postfix/src/tls/tls.h
@@ -141,6 +141,16 @@ extern const char *str_tls_level(int);
#else
#define TLS_ADD1_HOST SSL_add1_host
#define TLS_SET1_HOST SSL_set1_host
+#endif
+
+ /*
+ * SSL_trace() is built into OpenSSL only when the library is configured
+ * with "enable-ssl-trace". This is the upstream default, but some
+ * distributions disable it. Postfix can also opt out at build time with
+ * CCARGS=-DNO_TLS_TRACE.
+ */
+#if !defined(OPENSSL_NO_SSL_TRACE) && !defined(NO_TLS_TRACE)
+#define HAVE_SSL_TRACE
#endif
/*
@@ -281,6 +291,9 @@ typedef struct {
int errorcode; /* First error at error depth */
int must_fail; /* Failed to load trust settings */
char *ffail_type; /* Forced verification failure */
+ /* SSL protocol trace; populated when log_mask has TLS_LOG_TRACE. */
+ BIO *trace_bio; /* destination BIO, or NULL */
+ int trace_size_limit; /* size cap; <= 0 means "stop now" */
/* End of Private members. */
} TLS_SESS_STATE;
@@ -330,6 +343,7 @@ extern int tls_log_mask(const char *, const char *);
#define TLS_LOG_TLSPKTS (1<<8)
#define TLS_LOG_ALLPKTS (1<<9)
#define TLS_LOG_DANE (1<<10)
+#define TLS_LOG_TRACE (1<<11)
/*
* Client and Server application contexts
@@ -517,6 +531,10 @@ typedef struct {
const TLS_DANE *dane; /* DANE TLSA verification */
struct TLSRPT_WRAPPER *tlsrpt; /* RFC 8460 reporting */
char *ffail_type; /* Forced verification failure */
+ int trace_size_limit; /* TLS protocol trace size limit */
+ BIO *(*trace_open) (void *, const char *); /* override dest */
+ void *trace_arg; /* opaque trace_open argument */
+ char *trace_peer; /* Printable peer IP address */
} TLS_CLIENT_START_PROPS;
extern TLS_APPL_STATE *tls_client_init(const TLS_CLIENT_INIT_PROPS *);
@@ -540,13 +558,15 @@ extern TLS_SESS_STATE *tls_client_post_connect(TLS_SESS_STATE *,
a6, a7, a8, a9, a10, a11, a12, a13, a14))
#define TLS_CLIENT_START(props, a1, a2, a3, a4, a5, a6, a7, a8, a9, \
- a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22) \
+ a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, \
+ a23, a24, a25, a26) \
tls_client_start((((props)->a1), ((props)->a2), ((props)->a3), \
((props)->a4), ((props)->a5), ((props)->a6), ((props)->a7), \
((props)->a8), ((props)->a9), ((props)->a10), ((props)->a11), \
((props)->a12), ((props)->a13), ((props)->a14), ((props)->a15), \
((props)->a16), ((props)->a17), ((props)->a18), ((props)->a19), \
- ((props)->a20), ((props)->a21), ((props)->a22), (props)))
+ ((props)->a20), ((props)->a21), ((props)->a22), ((props)->a23), \
+ ((props)->a24), ((props)->a25), ((props)->a26), (props)))
/*
* tls_server.c
@@ -588,6 +608,10 @@ typedef struct {
const char *cipher_grade;
const char *cipher_exclusions;
const char *mdalg; /* default message digest algorithm */
+ int trace_size_limit; /* TLS protocol trace size limit */
+ BIO *(*trace_open) (void *, const char *); /* override dest */
+ void *trace_arg; /* opaque trace_open argument */
+ char *trace_peer; /* Printable peer IP address */
} TLS_SERVER_START_PROPS;
extern TLS_APPL_STATE *tls_server_init(const TLS_SERVER_INIT_PROPS *);
@@ -612,11 +636,12 @@ extern TLS_SESS_STATE *tls_server_post_accept(TLS_SESS_STATE *);
a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20))
#define TLS_SERVER_START(props, a1, a2, a3, a4, a5, a6, a7, a8, a9, \
- a10, a11, a12, a13) \
+ a10, a11, a12, a13, a14, a15, a16, a17) \
tls_server_start((((props)->a1), ((props)->a2), ((props)->a3), \
((props)->a4), ((props)->a5), ((props)->a6), ((props)->a7), \
((props)->a8), ((props)->a9), ((props)->a10), ((props)->a11), \
- ((props)->a12), ((props)->a13), (props)))
+ ((props)->a12), ((props)->a13), ((props)->a14), ((props)->a15), \
+ ((props)->a16), ((props)->a17), (props)))
/*
* tls_session.c
@@ -743,6 +768,28 @@ extern const EVP_MD *tls_validate_digest(const char *);
extern void tls_enable_client_rpk(SSL_CTX *, SSL *);
extern void tls_enable_server_rpk(SSL_CTX *, SSL *);
+#ifdef HAVE_SSL_TRACE
+extern void tls_msg_callback(int, int, int, const void *, size_t,
+ SSL *, void *);
+
+ /*
+ * Default trace destination: a file under tlstrace/. This is the default
+ * for daemon programs that do not supply start_props->trace_open. The real
+ * work is done in tls_trace_create_file().
+ */
+#define TLS_TRACE_QDIR "tlstrace"
+extern bool tls_trace_rate_ok(int);
+extern BIO *tls_trace_create_qfile(const char *);
+
+ /*
+ * Append -.--XXXXXX to the path
+ * buffer, then create the named file with mkstemp() and wrap it in an
+ * OpenSSL file BIO.
+ */
+extern BIO *tls_trace_create_file(VSTRING *, const char *);
+
+#endif
+
/*
* tls_seed.c
*/
diff --git a/postfix/src/tls/tls_client.c b/postfix/src/tls/tls_client.c
index 23e66964b..fc33c5e34 100644
--- a/postfix/src/tls/tls_client.c
+++ b/postfix/src/tls/tls_client.c
@@ -1286,6 +1286,38 @@ TLS_SESS_STATE *tls_client_start(const TLS_CLIENT_START_PROPS *props)
if (log_mask & TLS_LOG_TLSPKTS)
tls_set_bio_callback(SSL_get_rbio(TLScontext->con), tls_bio_dump_cb);
+#ifdef HAVE_SSL_TRACE
+
+ /*
+ * If "trace" is included in the TLS loglevel, open a destination BIO
+ * (either the application's override or the libtls default file under
+ * $queue_directory/tlstrace/) and install the SSL message callback.
+ * SSL_trace() writes records straight into the BIO; the budget is
+ * enforced via BIO_tell() in tls_msg_callback(). Enforce a global trace
+ * creation rate limit for requests from a daemon process.
+ */
+ if ((log_mask & TLS_LOG_TRACE) && props->trace_size_limit > 0) {
+ BIO *bio;
+
+ if (props->trace_open != 0) {
+ bio = props->trace_open(props->trace_arg, props->trace_peer);
+ } else if (tls_trace_rate_ok(var_tls_trace_anvil_rate)) {
+ bio = tls_trace_create_qfile(props->trace_peer);
+ } else {
+ msg_info("skipping TLS trace output - rate limit (%d) exceeded",
+ var_tls_trace_anvil_rate);
+ bio = 0;
+ }
+
+ if (bio != 0) {
+ TLScontext->trace_bio = bio;
+ TLScontext->trace_size_limit = props->trace_size_limit;
+ SSL_set_msg_callback(TLScontext->con, tls_msg_callback);
+ SSL_set_msg_callback_arg(TLScontext->con, TLScontext);
+ }
+ }
+#endif
+
/*
* An external (STS) policy signaled a failure. Prevent false (PKI)
* certificate matches in tls_verify.c. TODO(wietse) how was this handled
diff --git a/postfix/src/tls/tls_misc.c b/postfix/src/tls/tls_misc.c
index dad8cb54f..1c5a5a6aa 100644
--- a/postfix/src/tls/tls_misc.c
+++ b/postfix/src/tls/tls_misc.c
@@ -122,6 +122,14 @@
/* void tls_enable_server_rpk(ctx, ssl)
/* SSL_CTX *ctx;
/* SSL *ssl;
+/*
+/* bool tls_trace_rate_ok(int tls_trace_rate_limit)
+/*
+/* BIO *tls_trace_create_file(
+/* VSTRING *path,
+/* const char *trace_peer)
+/*
+/* BIO *tls_trace_create_qfile(const char *trace_peer)
/* DESCRIPTION
/* This module implements public and internal routines that
/* support the TLS client and server.
@@ -225,6 +233,20 @@
/*
/* tls_enable_server_rpk() enables the use of raw public keys in the
/* server to client direction, if supported by the OpenSSL library.
+/*
+/* tls_trace_create_file() fills in a TLS trace file name template
+/* -.--XXXXXX, appends
+/* the result to the path argument, opens the named file with
+/* mkstemp(), and returns it as a file BIO with BIO_CLOSE
+/* enabled. The result value is null in case of failure; all errors
+/* are logged.
+/*
+/* tls_trace_rate_ok() queries the anvil(8) service and enforces the
+/* tls_trace_rate_limit value.
+/*
+/* tls_trace_create_qfile() creates a TLS trace file for a daemon
+/* process. This function initializes a path buffer with "tlstrace/",
+/* and delegates the remaining work to tls_trace_create_file().
/* LICENSE
/* .ad
/* .fi
@@ -259,6 +281,8 @@
#include
#include
#include
+#include /* O_WRONLY etc. */
+#include
/* Utility library. */
@@ -276,6 +300,8 @@
/*
* Global library.
*/
+#include
+#include
#include
#include
#include
@@ -299,6 +325,7 @@ char *var_tls_low_ignored;
char *var_tls_export_ignored;
char *var_tls_null_clist;
int var_tls_daemon_rand_bytes;
+int var_tls_trace_anvil_rate;
char *var_tls_eecdh_auto;
char *var_tls_eecdh_strong;
char *var_tls_eecdh_ultra;
@@ -556,6 +583,7 @@ static const NAME_MASK tls_log_table[] = {
"ssl-debug", TLS_LOG_DEBUG, /* SSL library debug/verbose */
"ssl-handshake-packet-dump", TLS_LOG_TLSPKTS,
"ssl-session-packet-dump", TLS_LOG_TLSPKTS | TLS_LOG_ALLPKTS,
+ "trace", TLS_LOG_TRACE, /* SSL_trace() to a file or email */
0, 0,
};
@@ -578,9 +606,144 @@ int tls_log_mask(const char *log_param, const char *log_level)
mask = name_mask_opt(log_param, tls_log_table, log_level,
NAME_MASK_ANY_CASE | NAME_MASK_RETURN);
+#ifndef HAVE_SSL_TRACE
+ if (mask & TLS_LOG_TRACE) {
+ static BH_TABLE *log_spam_filter;
+
+ if (log_spam_filter == 0)
+ log_spam_filter = been_here_init(BH_BOUND_NONE, BH_FLAG_NONE);
+ if (!been_here(log_spam_filter, "%s=%s", log_param, log_level)) {
+ msg_warn("%s: ignoring \"trace\" log level: "
+ "SSL_trace() is not available in this build",
+ log_param);
+ mask &= ~TLS_LOG_TRACE;
+ }
+ }
+#endif
return (mask);
}
+#ifdef HAVE_SSL_TRACE
+
+/* tls_msg_callback - SSL message callback for TLS_LOG_TRACE */
+
+void tls_msg_callback(int write_p, int version, int content_type,
+ const void *buf, size_t msglen,
+ SSL *ssl, void *arg)
+{
+ TLS_SESS_STATE *TLScontext = (TLS_SESS_STATE *) arg;
+ BIO *bio = TLScontext->trace_bio;
+
+ /*
+ * trace_size_limit <= 0 means either tracing is off (never started)
+ * or we already crossed the budget and emitted the truncation marker.
+ * Either way, do not write further.
+ */
+ if (bio == 0 || TLScontext->trace_size_limit <= 0)
+ return;
+
+ SSL_trace(write_p, version, content_type, buf, msglen, ssl, bio);
+
+ if (BIO_tell(bio) >= TLScontext->trace_size_limit) {
+ static const char trailer[] =
+ "\n[TLS trace truncated: size limit reached]\n";
+
+ (void) BIO_write(bio, trailer, sizeof(trailer) - 1);
+ TLScontext->trace_size_limit = -1;
+ }
+}
+
+/* tls_trace_rate_ok - enforce tls_trace_rate_limit */
+
+bool tls_trace_rate_ok(int tls_trace_rate_limit)
+{
+ ANVIL_CLNT *client;
+ int rate;
+ int ret;
+
+ if (tls_trace_rate_limit > 0) {
+ client = anvil_clnt_create();
+ if (anvil_clnt_tlstr(client, "any", "any", &rate) == ANVIL_STAT_OK) {
+ ret = rate <= tls_trace_rate_limit;
+ } else {
+ ret = true;
+ }
+ anvil_clnt_free(client);
+ } else {
+ ret = true;
+ }
+ return (ret);
+}
+
+/* tls_trace_create_qfile - default trace destination for daemons */
+
+BIO *tls_trace_create_qfile(const char *trace_peer)
+{
+ VSTRING *path;
+ BIO *bio;
+
+ /*
+ * Create the file under "tlstrace/". The cwd of every Postfix daemon is
+ * $queue_directory, so the relative path resolves correctly with or
+ * without chroot. The directory itself is expected to exist;
+ * postfix-script and postfix-files arrange for it, and most Postfix
+ * daemons would not have sufficient privileges to create it.
+ */
+ path = vstring_alloc(100);
+ vstring_strcpy(path, TLS_TRACE_QDIR "/");
+ bio = tls_trace_create_file(path, trace_peer);
+ msg_info("TLS protocol trace for %s saved to %s",
+ trace_peer, vstring_str(path));
+ vstring_free(path);
+ return (bio);
+}
+
+/* tls_trace_create_file - trace destination for CLI or daemon */
+
+BIO *tls_trace_create_file(VSTRING *path, const char *trace_peer)
+{
+ struct timeval tv;
+ struct tm *lt;
+ FILE *fp;
+ BIO *bio;
+
+ int newfd;
+
+ /*
+ * Append "-.--XXXXXX" to the path. Open
+ * with mkstemp() so that this will never clobber an existing file;
+ * fdopen(3) hands the descriptor to stdio, and BIO_new_fp() with
+ * BIO_CLOSE() wraps the stream so that BIO_free_all() at session
+ * teardown also fclose()s it.
+ */
+ vstring_sprintf_append(path, "%s-", var_procname);
+ GETTIMEOFDAY(&tv);
+ lt = localtime(&tv.tv_sec);
+ while (strftime(vstring_end(path), vstring_avail(path),
+ "%Y%m%d%H%M%S", lt) == 0)
+ VSTRING_SPACE(path, vstring_avail(path) + 100);
+ VSTRING_SKIP(path);
+ vstring_sprintf_append(path, ".%06d-%s-XXXXXX",
+ (int) tv.tv_usec, trace_peer);
+ if ((newfd = mkstemp(vstring_str(path))) < 0) {
+ msg_warn("TLS trace: cannot open %s: %m", vstring_str(path));
+ return (0);
+ }
+ if ((fp = fdopen(newfd, "w")) == 0) {
+ msg_warn("TLS trace: fdopen(%s) failed: %m", vstring_str(path));
+ (void) close(newfd);
+ return (0);
+ }
+ if ((bio = BIO_new_fp(fp, BIO_CLOSE)) == 0) {
+ msg_warn("TLS trace: BIO_new_fp() failed for %s", vstring_str(path));
+ (void) fclose(fp);
+ return (0);
+ }
+ return (bio);
+}
+
+#endif /* HAVE_SSL_TRACE */
+
/* tls_update_app_logmask - update log level after init */
void tls_update_app_logmask(TLS_APPL_STATE *app_ctx, int log_mask)
@@ -685,6 +848,7 @@ void tls_param_init(void)
/* If this changes, update TLS_*_PARAMS* in tls_*.h. */
static const CONFIG_INT_TABLE int_table[] = {
VAR_TLS_DAEMON_RAND_BYTES, DEF_TLS_DAEMON_RAND_BYTES, &var_tls_daemon_rand_bytes, 1, 0,
+ VAR_TLS_TRACE_ANVIL_RATE, DEF_TLS_TRACE_ANVIL_RATE, &var_tls_trace_anvil_rate, 0, 0,
0,
};
@@ -1372,6 +1536,8 @@ TLS_SESS_STATE *tls_alloc_sess_context(int log_mask, const char *namaddr)
TLScontext->errorcert = 0;
TLScontext->rpt_reported = 0;
TLScontext->ffail_type = 0;
+ TLScontext->trace_bio = 0;
+ TLScontext->trace_size_limit = 0;
return (TLScontext);
}
@@ -1389,6 +1555,24 @@ void tls_free_context(TLS_SESS_STATE *TLScontext)
if (TLScontext->con != 0)
SSL_free(TLScontext->con);
+ /*
+ * Release the protocol trace, if one was opened. Order matters:
+ * SSL_free() above can still drive the message callback during
+ * teardown (close-notify processing in particular), so trace_bio
+ * must stay valid until after SSL_free() returns. Closing it
+ * first would let tls_msg_callback() write through a freed BIO.
+ *
+ * Always BIO_flush() before close, so any stdio buffering inside
+ * BIO_new_fp() lands on disk before the FILE * is fclose()d. An
+ * application override may release the BIO; otherwise BIO_free_all
+ * walks the chain and (with BIO_CLOSE) closes the underlying file.
+ */
+ if (TLScontext->trace_bio != 0) {
+ (void) BIO_flush(TLScontext->trace_bio);
+ BIO_free_all(TLScontext->trace_bio);
+ TLScontext->trace_bio = 0;
+ }
+
if (TLScontext->namaddr)
myfree(TLScontext->namaddr);
if (TLScontext->serverid)
diff --git a/postfix/src/tls/tls_proxy_attr.h b/postfix/src/tls/tls_proxy_attr.h
index f539de6a4..1fd02fc5f 100644
--- a/postfix/src/tls/tls_proxy_attr.h
+++ b/postfix/src/tls/tls_proxy_attr.h
@@ -59,6 +59,7 @@
#define TLS_ATTR_SRVR_SIG_DGST "srvr_signature_digest"
#define TLS_ATTR_NAMADDR "namaddr"
#define TLS_ATTR_RPT_REPORTED "rpt_reported"
+#define TLS_ATTR_TRACE_PEER "trace_peer"
/*
* TLS_SERVER_INIT_PROPS attributes.
@@ -95,6 +96,7 @@
#define TLS_ATTR_CIPHER_GRADE "cipher_grade"
#define TLS_ATTR_CIPHER_EXCLUSIONS "cipher_exclusions"
#define TLS_ATTR_MDALG "mdalg"
+#define TLS_ATTR_TRACE_PEER "trace_peer"
/*
* TLS_CLIENT_INIT_PROPS attributes.
@@ -136,6 +138,8 @@
#define TLS_ATTR_DANE "dane"
#define TLS_ATTR_TLSRPT "tlsrpt"
#define TLS_ATTR_FFAIL_TYPE "forced_failure_type"
+#define TLS_ATTR_TRACE_SIZE_LIMIT "trace_size_limit"
+#define TLS_ATTR_TRACE_PEER "trace_peer"
/*
* TLS_TLSA attributes.
diff --git a/postfix/src/tls/tls_proxy_client_start_proto.c b/postfix/src/tls/tls_proxy_client_start_proto.c
index beae3a7c6..b34264e03 100644
--- a/postfix/src/tls/tls_proxy_client_start_proto.c
+++ b/postfix/src/tls/tls_proxy_client_start_proto.c
@@ -267,6 +267,9 @@ int tls_proxy_client_start_print(ATTR_PRINT_COMMON_FN print_fn,
#endif
SEND_ATTR_STR(TLS_ATTR_FFAIL_TYPE,
STRING_OR_EMPTY(props->ffail_type)),
+ SEND_ATTR_INT(TLS_ATTR_TRACE_SIZE_LIMIT,
+ props->trace_size_limit),
+ SEND_ATTR_STR(TLS_ATTR_TRACE_PEER, props->trace_peer),
ATTR_TYPE_END);
/* Do not flush the stream. */
if (msg_verbose)
@@ -289,6 +292,7 @@ void tls_proxy_client_start_free(TLS_CLIENT_START_PROPS *props)
myfree((void *) props->protocols);
myfree((void *) props->cipher_grade);
myfree((void *) props->cipher_exclusions);
+ myfree((void *) props->trace_peer);
if (props->matchargv)
argv_free((ARGV *) props->matchargv);
myfree((void *) props->mdalg);
@@ -497,11 +501,12 @@ int tls_proxy_client_start_scan(ATTR_SCAN_COMMON_FN scan_fn, VSTREAM *fp,
VSTRING *cipher_exclusions = vstring_alloc(25);
VSTRING *mdalg = vstring_alloc(25);
VSTRING *ffail_type = vstring_alloc(25);
+ VSTRING *trace_peer = vstring_alloc(25);
#ifdef USE_TLSRPT
-#define EXPECT_START_SCAN_RETURN 19
+#define EXPECT_START_SCAN_RETURN 21
#else
-#define EXPECT_START_SCAN_RETURN 18
+#define EXPECT_START_SCAN_RETURN 20
#endif
if (msg_verbose)
@@ -540,6 +545,9 @@ int tls_proxy_client_start_scan(ATTR_SCAN_COMMON_FN scan_fn, VSTREAM *fp,
&props->tlsrpt),
#endif
RECV_ATTR_STR(TLS_ATTR_FFAIL_TYPE, ffail_type),
+ RECV_ATTR_INT(TLS_ATTR_TRACE_SIZE_LIMIT,
+ &props->trace_size_limit),
+ RECV_ATTR_STR(TLS_ATTR_TRACE_PEER, trace_peer),
ATTR_TYPE_END);
/* Always construct a well-formed structure. */
props->log_param = vstring_export(log_param);
@@ -555,6 +563,9 @@ int tls_proxy_client_start_scan(ATTR_SCAN_COMMON_FN scan_fn, VSTREAM *fp,
props->cipher_exclusions = vstring_export(cipher_exclusions);
props->mdalg = vstring_export(mdalg);
EXPORT_OR_NULL(props->ffail_type, ffail_type);
+ props->trace_open = 0;
+ props->trace_arg = 0;
+ props->trace_peer = vstring_export(trace_peer);
ret = (ret == EXPECT_START_SCAN_RETURN ? 1 : -1);
if (ret != 1) {
tls_proxy_client_start_free(props);
diff --git a/postfix/src/tls/tls_proxy_client_start_proto.h b/postfix/src/tls/tls_proxy_client_start_proto.h
index f1859ff7d..613180a0f 100644
--- a/postfix/src/tls/tls_proxy_client_start_proto.h
+++ b/postfix/src/tls/tls_proxy_client_start_proto.h
@@ -25,12 +25,13 @@
#ifdef USE_TLS
#define TLS_PROXY_CLIENT_START_PROPS(props, a1, a2, a3, a4, a5, a6, a7, a8, \
- a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19) \
+ a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21) \
(((props)->a1), ((props)->a2), ((props)->a3), \
((props)->a4), ((props)->a5), ((props)->a6), ((props)->a7), \
((props)->a8), ((props)->a9), ((props)->a10), ((props)->a11), \
((props)->a12), ((props)->a13), ((props)->a14), ((props)->a15), \
- ((props)->a16), ((props)->a17), ((props)->a18), ((props)->a19))
+ ((props)->a16), ((props)->a17), ((props)->a18), ((props)->a19), \
+ ((props)->a20), ((props)->a21))
extern int tls_proxy_client_start_print(ATTR_PRINT_COMMON_FN, VSTREAM *, int, const void *);
extern void tls_proxy_client_start_free(TLS_CLIENT_START_PROPS *);
diff --git a/postfix/src/tls/tls_proxy_context_print.c b/postfix/src/tls/tls_proxy_context_print.c
index 808abbe73..fb94673c8 100644
--- a/postfix/src/tls/tls_proxy_context_print.c
+++ b/postfix/src/tls/tls_proxy_context_print.c
@@ -112,6 +112,8 @@ int tls_proxy_context_print(ATTR_PRINT_COMMON_FN print_fn, VSTREAM *fp,
STRING_OR_EMPTY(tp->namaddr)),
SEND_ATTR_INT(TLS_ATTR_RPT_REPORTED,
tp->rpt_reported),
+ SEND_ATTR_INT(TLS_ATTR_TRACE_SIZE_LIMIT,
+ tp->trace_size_limit),
ATTR_TYPE_END);
/* Do not flush the stream. */
return (ret);
diff --git a/postfix/src/tls/tls_proxy_context_scan.c b/postfix/src/tls/tls_proxy_context_scan.c
index fc23c528a..949c90831 100644
--- a/postfix/src/tls/tls_proxy_context_scan.c
+++ b/postfix/src/tls/tls_proxy_context_scan.c
@@ -126,6 +126,8 @@ int tls_proxy_context_scan(ATTR_SCAN_COMMON_FN scan_fn, VSTREAM *fp,
RECV_ATTR_STR(TLS_ATTR_NAMADDR, namaddr),
RECV_ATTR_INT(TLS_ATTR_RPT_REPORTED,
&tls_context->rpt_reported),
+ RECV_ATTR_INT(TLS_ATTR_TRACE_SIZE_LIMIT,
+ &tls_context->trace_size_limit),
ATTR_TYPE_END);
/* Always construct a well-formed structure. */
tls_context->peer_CN = vstring_export(peer_CN);
@@ -143,7 +145,7 @@ int tls_proxy_context_scan(ATTR_SCAN_COMMON_FN scan_fn, VSTREAM *fp,
tls_context->srvr_sig_curve = vstring_export(srvr_sig_curve);
tls_context->srvr_sig_dgst = vstring_export(srvr_sig_dgst);
tls_context->namaddr = vstring_export(namaddr);
- ret = (ret == 25 ? 1 : -1);
+ ret = (ret == 26 ? 1 : -1);
if (ret != 1) {
tls_proxy_context_free(tls_context);
tls_context = 0;
diff --git a/postfix/src/tls/tls_proxy_server_start_proto.c b/postfix/src/tls/tls_proxy_server_start_proto.c
index db2f6c4ce..21ce1e079 100644
--- a/postfix/src/tls/tls_proxy_server_start_proto.c
+++ b/postfix/src/tls/tls_proxy_server_start_proto.c
@@ -118,6 +118,10 @@ int tls_proxy_server_start_print(ATTR_PRINT_COMMON_FN print_fn, VSTREAM *fp,
STRING_OR_EMPTY(props->cipher_exclusions)),
SEND_ATTR_STR(TLS_ATTR_MDALG,
STRING_OR_EMPTY(props->mdalg)),
+ SEND_ATTR_INT(TLS_ATTR_TRACE_SIZE_LIMIT,
+ props->trace_size_limit),
+ SEND_ATTR_STR(TLS_ATTR_TRACE_PEER,
+ STRING_OR_EMPTY(props->trace_peer)),
ATTR_TYPE_END);
/* Do not flush the stream. */
return (ret);
@@ -138,6 +142,7 @@ int tls_proxy_server_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 *trace_peer = vstring_alloc(25);
/*
* Note: memset() is not a portable way to initialize non-integer types.
@@ -158,6 +163,9 @@ int tls_proxy_server_start_scan(ATTR_SCAN_COMMON_FN scan_fn, VSTREAM *fp,
RECV_ATTR_STR(TLS_ATTR_CIPHER_EXCLUSIONS,
cipher_exclusions),
RECV_ATTR_STR(TLS_ATTR_MDALG, mdalg),
+ RECV_ATTR_INT(TLS_ATTR_TRACE_SIZE_LIMIT,
+ &props->trace_size_limit),
+ RECV_ATTR_STR(TLS_ATTR_TRACE_PEER, trace_peer),
ATTR_TYPE_END);
/* Always construct a well-formed structure. */
props->log_param = vstring_export(log_param);
@@ -167,7 +175,10 @@ int tls_proxy_server_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);
- ret = (ret == 10 ? 1 : -1);
+ props->trace_open = 0;
+ props->trace_arg = 0;
+ props->trace_peer = vstring_export(trace_peer);
+ ret = (ret == 12 ? 1 : -1);
if (ret != 1) {
tls_proxy_server_start_free(props);
props = 0;
@@ -188,6 +199,7 @@ void tls_proxy_server_start_free(TLS_SERVER_START_PROPS *props)
myfree((void *) props->cipher_grade);
myfree((void *) props->cipher_exclusions);
myfree((void *) props->mdalg);
+ myfree((void *) props->trace_peer);
myfree((void *) props);
}
diff --git a/postfix/src/tls/tls_proxy_server_start_proto.h b/postfix/src/tls/tls_proxy_server_start_proto.h
index 2062d78fb..fb3d8f6f9 100644
--- a/postfix/src/tls/tls_proxy_server_start_proto.h
+++ b/postfix/src/tls/tls_proxy_server_start_proto.h
@@ -25,10 +25,11 @@
#ifdef USE_TLS
#define TLS_PROXY_SERVER_START_PROPS(props, a1, a2, a3, a4, a5, a6, a7, a8, \
- a9, a10) \
+ a9, a10, a11, a12) \
(((props)->a1), ((props)->a2), ((props)->a3), \
((props)->a4), ((props)->a5), ((props)->a6), ((props)->a7), \
- ((props)->a8), ((props)->a9), ((props)->a10))
+ ((props)->a8), ((props)->a9), ((props)->a10), ((props)->a11), \
+ ((props)->a12))
extern int tls_proxy_server_start_print(ATTR_PRINT_COMMON_FN, VSTREAM *, int, const void *);
extern void tls_proxy_server_start_free(TLS_SERVER_START_PROPS *);
diff --git a/postfix/src/tls/tls_server.c b/postfix/src/tls/tls_server.c
index 122628501..04066142c 100644
--- a/postfix/src/tls/tls_server.c
+++ b/postfix/src/tls/tls_server.c
@@ -931,6 +931,38 @@ TLS_SESS_STATE *tls_server_start(const TLS_SERVER_START_PROPS *props)
if (log_mask & TLS_LOG_TLSPKTS)
tls_set_bio_callback(SSL_get_rbio(TLScontext->con), tls_bio_dump_cb);
+#ifdef HAVE_SSL_TRACE
+
+ /*
+ * If "trace" is included in the TLS loglevel, open a destination BIO
+ * (either the application's override or the libtls default file under
+ * $queue_directory/tlstrace/) and install the SSL message callback.
+ * SSL_trace() writes records straight into the BIO; the budget is
+ * enforced via BIO_tell() in tls_msg_callback(). Enforce a global trace
+ * creation rate limit for requests from a daemon process.
+ */
+ if ((log_mask & TLS_LOG_TRACE) && props->trace_size_limit > 0) {
+ BIO *bio;
+
+ if (props->trace_open != 0) {
+ bio = props->trace_open(props->trace_arg, props->trace_peer);
+ } else if (tls_trace_rate_ok(var_tls_trace_anvil_rate)) {
+ bio = tls_trace_create_qfile(props->trace_peer);
+ } else {
+ msg_info("skipping TLS trace output - rate limit (%d) exceeded",
+ var_tls_trace_anvil_rate);
+ bio = 0;
+ }
+
+ if (bio != 0) {
+ TLScontext->trace_bio = bio;
+ TLScontext->trace_size_limit = props->trace_size_limit;
+ SSL_set_msg_callback(TLScontext->con, tls_msg_callback);
+ SSL_set_msg_callback_arg(TLScontext->con, TLScontext);
+ }
+ }
+#endif
+
/*
* If we don't trigger the handshake in the library, leave control over
* SSL_accept/read/write/etc with the application.
diff --git a/postfix/src/util/sys_defs.h b/postfix/src/util/sys_defs.h
index ebda90a69..871e48c5e 100644
--- a/postfix/src/util/sys_defs.h
+++ b/postfix/src/util/sys_defs.h
@@ -797,8 +797,6 @@ extern int initgroups(const char *, int);
#if HAVE_GLIBC_API_VERSION_SUPPORT(2, 1)
#define SOCKADDR_SIZE socklen_t
#define SOCKOPT_SIZE socklen_t
-#else
-#define NO_SNPRINTF
#endif
#ifndef NO_IPV6
#define HAS_IPV6
diff --git a/postfix/src/util/vbuf_print.c b/postfix/src/util/vbuf_print.c
index 6e14cc0e9..368d0da74 100644
--- a/postfix/src/util/vbuf_print.c
+++ b/postfix/src/util/vbuf_print.c
@@ -129,8 +129,11 @@
VBUF_SKIP(bp); \
} while (0)
#else
-#define VBUF_SNPRINTF(bp, sz, fmt, arg) do { \
- if (VBUF_SPACE((bp), (sz)) != 0) \
+#define VBUF_SNPRINTF(bp, width_or_prec, type_space, fmt, arg) do { \
+ if ((width_or_prec) > INT_MAX - (type_space)) \
+ msg_panic("vbuf_print: field width (%d + %lu) > INT_MAX", \
+ (width_or_prec), (unsigned long) (type_space)); \
+ if (VBUF_SPACE((bp), (width_or_prec) + (type_space)) != 0) \
return (bp); \
sprintf((char *) (bp)->ptr, (fmt), (arg)); \
VBUF_SKIP(bp); \