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
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.
||-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. |
||-DNO_SNPRINTF |default, Postfix uses snprintf() except on |
|| |ancient systems. |
|_\b|_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b|_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b |
+|| |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. |
+|_\b|_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b|_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b |
| |Specifies a non-default compiler debugging |
|DEBUG=debug_level |level. The default is "-g". Specify DEBUG= to|
| |turn off debugging. |
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
===========================================
$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
instead of <tt>snprintf()</tt>. By default, Postfix uses
<tt>snprintf()</tt> except on ancient systems. </td> </tr>
+<tr> <td> </td> <td> -DNO_TLS_TRACE </td> <td> 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
+<tt><a href="postconf.5.html#smtp_tls_loglevel">smtp_tls_loglevel</a></tt> in the <a href="postconf.5.html">postconf(5)</a> manual. </td> </tr>
+
<tr> <td colspan="2"> DEBUG=debug_level </td> <td> Specifies a
non-default compiler debugging level. The default is "<tt>-g</tt>".
Specify DEBUG= to turn off debugging. </td> </tr>
<b>status=0</b>
<b>rate=</b><i>number</i>
+<b><a name="tls_trace_rate_control">TLS TRACE RATE CONTROL</a></b>
+ To register a TLS trace event send the following request to the
+ <a href="anvil.8.html"><b>anvil</b>(8)</a> server:
+
+ <b>request=tlstr</b>
+ <b>ident=</b><i>string</i>
+
+ The <a href="anvil.8.html"><b>anvil</b>(8)</a> server answers with the number of TLS trace requests per
+ unit time for the (service, client) combination specified with <b>ident</b>:
+
+ <b>status=0</b>
+ <b>rate=</b><i>number</i>
+
<b><a name="security">SECURITY</a></b>
The <a href="anvil.8.html"><b>anvil</b>(8)</a> server does not talk to the network or to local users, and
can run chrooted at fixed low privilege.
Optional TLS loglevel override that depends on the remote peer
host name or IP address.
+ <b><a href="postconf.5.html#smtp_tls_trace_size_limit">smtp_tls_trace_size_limit</a> (102400)</b>
+ 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 (<a href="postconf.5.html#smtp_tls_loglevel">smtp_tls_loglevel</a> or
+ <a href="postconf.5.html#smtp_tls_loglevel_maps">smtp_tls_loglevel_maps</a>).
+
+ <b><a href="postconf.5.html#tls_trace_rate_limit">tls_trace_rate_limit</a> (1)</b>
+ The maximum number of TLS traces per <a href="postconf.5.html#anvil_rate_time_unit">anvil_rate_time_unit</a> that
+ all Postfix daemons combined will create.
+
<b><a name="obsolete_tls_controls">OBSOLETE TLS CONTROLS</a></b>
The following configuration parameters exist for compatibility with
Postfix versions before 2.3. Support for these will be removed in a
<b>-DNO_STDBOOL</b>
Don't use <stdbool.h>. This is usually auto-detected.
+ <b>-DNO_TLS_TRACE</b>
+ Build without OpenSSL 3 (and later) debug trace support.
+
<b>DEBUG=</b><i>debug</i><b>_</b><i>level</i>
- Specifies a non-default debugging level. The default is <b>-g</b>.
+ Specifies a non-default debugging level. The default is <b>-g</b>.
Specify <b>DEBUG=</b> to turn off debugging.
<b>OPT=</b><i>optimization</i><b>_</b><i>level</i>
- Specifies a non-default optimization level. The default is <b>-O</b>.
+ Specifies a non-default optimization level. The default is <b>-O</b>.
Specify <b>OPT=</b> to turn off optimization.
<b>POSTFIX_INSTALL_OPTS=</b><i>-option...</i>
- 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
<b>-keep-build-mtime</b>.
<b>SHLIB_CFLAGS=</b><i>flags</i>
- 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.
<b>SHLIB_RPATH=</b><i>rpath</i>
- 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.
<b>SHLIB_SUFFIX=</b><i>suffix</i>
- 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.
<b>shared=yes</b>
<b>shared=no</b>
- Enable (disable) Postfix builds with dynamically-linked
+ Enable (disable) Postfix builds with dynamically-linked
libraries typically named $<a href="postconf.5.html#shlib_directory">shlib_directory</a>/libpostfix-*.so.*.
This feature was introduced with Postfix 3.0.
<b>dynamicmaps=yes</b>
<b>dynamicmaps=no</b>
- Enable (disable) Postfix builds with the configuration file
+ Enable (disable) Postfix builds with the configuration file
$<a href="postconf.5.html#meta_directory">meta_directory</a>/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.
<b>pie=yes</b>
- <b>pie=no</b> Enable (disable) Postfix builds with position-independent exe-
+ <b>pie=no</b> Enable (disable) Postfix builds with position-independent exe-
cutables, on platforms where this is supported.
This feature was introduced with Postfix 3.0.
<i>installation</i><b>_</b><i>parameter</i><b>=</b><i>value</i>...
- 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:
- <a href="postconf.5.html#command_directory">command_directory</a> <a href="postconf.5.html#config_directory">config_directory</a> <a href="postconf.5.html#daemon_directory">daemon_directory</a> <a href="postconf.5.html#data_directory">data_direc</a>-
- <a href="postconf.5.html#data_directory">tory</a> <a href="postconf.5.html#default_cache_db_type">default_cache_db_type</a> <a href="postconf.5.html#default_database_type">default_database_type</a> <a href="postconf.5.html#html_directory">html_directory</a>
+ <a href="postconf.5.html#command_directory">command_directory</a> <a href="postconf.5.html#config_directory">config_directory</a> <a href="postconf.5.html#daemon_directory">daemon_directory</a> <a href="postconf.5.html#data_directory">data_direc</a>-
+ <a href="postconf.5.html#data_directory">tory</a> <a href="postconf.5.html#default_cache_db_type">default_cache_db_type</a> <a href="postconf.5.html#default_database_type">default_database_type</a> <a href="postconf.5.html#html_directory">html_directory</a>
<a href="postconf.5.html#mail_spool_directory">mail_spool_directory</a> <a href="postconf.5.html#mailq_path">mailq_path</a> <a href="postconf.5.html#manpage_directory">manpage_directory</a> <a href="postconf.5.html#meta_directory">meta_directory</a>
- <a href="postconf.5.html#newaliases_path">newaliases_path</a> <a href="postconf.5.html#queue_directory">queue_directory</a> <a href="postconf.5.html#readme_directory">readme_directory</a> <a href="postconf.5.html#sendmail_path">sendmail_path</a>
+ <a href="postconf.5.html#newaliases_path">newaliases_path</a> <a href="postconf.5.html#queue_directory">queue_directory</a> <a href="postconf.5.html#readme_directory">readme_directory</a> <a href="postconf.5.html#sendmail_path">sendmail_path</a>
<a href="postconf.5.html#shlib_directory">shlib_directory</a> <a href="postconf.5.html#openssl_path">openssl_path</a>
- See the <a href="postconf.5.html">postconf(5)</a> manpage for a description of these parame-
+ See the <a href="postconf.5.html">postconf(5)</a> manpage for a description of these parame-
ters.
This feature was introduced with Postfix 3.0.
<b>WARN=</b><i>warning</i><b>_</b><i>flags</i>
- 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.
<b><a name="license">LICENSE</a></b>
<p> This feature is available in Postfix 2.3 and later. </p>
+</DD>
+
+<DT><b><a name="lmtp_tls_trace_size_limit">lmtp_tls_trace_size_limit</a>
+(default: 102400)</b></DT><DD>
+
+<p> The <a href="lmtp.8.html">lmtp(8)</a> equivalent of <a href="postconf.5.html#smtp_tls_trace_size_limit">smtp_tls_trace_size_limit</a>. </p>
+
+<p> This feature is available in Postfix 3.12 and later. </p>
+
+
</DD>
<DT><b><a name="lmtp_tls_trust_anchor_file">lmtp_tls_trust_anchor_file</a>
<p> This feature is available in Postfix 2.8 and later. </p>
+</DD>
+
+<DT><b><a name="postscreen_tls_trace_size_limit">postscreen_tls_trace_size_limit</a>
+(default: $<a href="postconf.5.html#smtpd_tls_trace_size_limit">smtpd_tls_trace_size_limit</a>)</b></DT><DD>
+
+<p> The <a href="postscreen.8.html">postscreen(8)</a> equivalent of <a href="postconf.5.html#smtpd_tls_trace_size_limit">smtpd_tls_trace_size_limit</a>.
+<a href="postscreen.8.html">postscreen(8)</a> generates the trace via <a href="tlsproxy.8.html">tlsproxy(8)</a>; the trace file
+name starts with "tlsproxy-". </p>
+
+<p> This feature is available in Postfix 3.12 and later. </p>
+
+
</DD>
<DT><b><a name="postscreen_upstream_proxy_protocol">postscreen_upstream_proxy_protocol</a>
<p> Do not use "<a href="postconf.5.html#smtp_tls_loglevel">smtp_tls_loglevel</a> = 2" or higher except in case of
problems. Use of loglevel 4 is strongly discouraged. </p>
+<p> With Postfix 3.12 and later, any of the levels above may be
+followed by ",trace" (for example "<a href="postconf.5.html#smtp_tls_loglevel">smtp_tls_loglevel</a> = 1,trace").
+The "trace" keyword writes a protocol message trace (this does not
+include the <b>content</b> of application data messages) of the TLS
+session to a per-connection file under $<a href="postconf.5.html#queue_directory">queue_directory</a>/tlstrace/,
+capped at <a href="postconf.5.html#smtp_tls_trace_size_limit">smtp_tls_trace_size_limit</a> 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 <a href="postconf.5.html#smtp_tls_loglevel_maps">smtp_tls_loglevel_maps</a>
+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). </p>
+
<p> This feature is available in Postfix 2.2 and later. </p>
<p> This feature is available in Postfix 2.2 and later. </p>
+</DD>
+
+<DT><b><a name="smtp_tls_trace_size_limit">smtp_tls_trace_size_limit</a>
+(default: 102400)</b></DT><DD>
+
+<p> 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 (<a href="postconf.5.html#smtp_tls_loglevel">smtp_tls_loglevel</a> or
+<a href="postconf.5.html#smtp_tls_loglevel_maps">smtp_tls_loglevel_maps</a>). The transcript is written to a per-
+connection file under $<a href="postconf.5.html#queue_directory">queue_directory</a>/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.
+</p>
+
+<p> This feature is available in Postfix 3.12 and later. </p>
+
+
</DD>
<DT><b><a name="smtp_tls_trust_anchor_file">smtp_tls_trust_anchor_file</a>
<p> Do not use "<a href="postconf.5.html#smtpd_tls_loglevel">smtpd_tls_loglevel</a> = 2" or higher except in case
of problems. Use of loglevel 4 is strongly discouraged. </p>
+<p> With Postfix 3.12 and later, any of the levels above may be
+followed by ",trace" (for example "<a href="postconf.5.html#smtpd_tls_loglevel">smtpd_tls_loglevel</a> = 1,trace").
+The "trace" keyword writes a protocol message trace (this does not
+include the <b>content</b> of application data messages) of the TLS
+session to a per-connection file under $<a href="postconf.5.html#queue_directory">queue_directory</a>/tlstrace/,
+capped at <a href="postconf.5.html#smtpd_tls_trace_size_limit">smtpd_tls_trace_size_limit</a> 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 <a href="postconf.5.html#smtpd_tls_loglevel_maps">smtpd_tls_loglevel_maps</a>
+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). </p>
+
<p> This feature is available in Postfix 2.2 and later. </p>
for TLS session ticket support in Postfix 2.11. </p>
+</DD>
+
+<DT><b><a name="smtpd_tls_trace_size_limit">smtpd_tls_trace_size_limit</a>
+(default: 102400)</b></DT><DD>
+
+<p> The Postfix SMTP server equivalent of <a href="postconf.5.html#smtp_tls_trace_size_limit">smtp_tls_trace_size_limit</a>.
+File names start with "smtpd-" instead of "smtp-". </p>
+
+<p> This feature is available in Postfix 3.12 and later. </p>
+
+
</DD>
<DT><b><a name="smtpd_tls_wrappermode">smtpd_tls_wrappermode</a>
<p> This feature is available in Postfix 2.11 and later. </p>
+</DD>
+
+<DT><b><a name="tls_trace_rate_limit">tls_trace_rate_limit</a>
+(default: 1)</b></DT><DD>
+
+<p> The maximum number of TLS traces per <a href="postconf.5.html#anvil_rate_time_unit">anvil_rate_time_unit</a> that
+all Postfix daemons combined will create. Specify a value ≤0 to
+disable the limit. </p>
+
+<p> This feature is available in Postfix 3.12 and later. </p>
+
+
</DD>
<DT><b><a name="tls_trust_server_ccerts">tls_trust_server_ccerts</a>
Optional TLS loglevel override that depends on the remote peer
host name or IP address.
+ <b><a href="postconf.5.html#postscreen_tls_trace_size_limit">postscreen_tls_trace_size_limit</a> ($<a href="postconf.5.html#smtpd_tls_trace_size_limit">smtpd_tls_trace_size_limit</a>)</b>
+ The <a href="postscreen.8.html"><b>postscreen</b>(8)</a> equivalent of <a href="postconf.5.html#smtpd_tls_trace_size_limit">smtpd_tls_trace_size_limit</a>.
+
<b><a href="postconf.5.html#postscreen_tls_mandatory_ciphers">postscreen_tls_mandatory_ciphers</a> ($<a href="postconf.5.html#smtpd_tls_mandatory_ciphers">smtpd_tls_mandatory_ciphers</a>)</b>
The <a href="postscreen.8.html"><b>postscreen</b>(8)</a> equivalent of <a href="postconf.5.html#smtpd_tls_mandatory_ciphers">smtpd_tls_mandatory_ciphers</a>.
<b><a href="postconf.5.html#postscreen_tls_req_ccert">postscreen_tls_req_ccert</a> ($<a href="postconf.5.html#smtpd_tls_req_ccert">smtpd_tls_req_ccert</a>)</b>
The <a href="postscreen.8.html"><b>postscreen</b>(8)</a> equivalent of <a href="postconf.5.html#smtpd_tls_req_ccert">smtpd_tls_req_ccert</a>.
+ <b><a href="postconf.5.html#tls_trace_rate_limit">tls_trace_rate_limit</a> (1)</b>
+ The maximum number of TLS traces per <a href="postconf.5.html#anvil_rate_time_unit">anvil_rate_time_unit</a> that
+ all Postfix daemons combined will create.
+
<b><a name="obsolete_starttls_support_controls">OBSOLETE STARTTLS SUPPORT CONTROLS</a></b>
- These parameters are supported for compatibility with <a href="smtpd.8.html"><b>smtpd</b>(8)</a> legacy
+ These parameters are supported for compatibility with <a href="smtpd.8.html"><b>smtpd</b>(8)</a> legacy
parameters.
<b><a href="postconf.5.html#postscreen_use_tls">postscreen_use_tls</a> ($<a href="postconf.5.html#smtpd_use_tls">smtpd_use_tls</a>)</b>
- 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.
<b><a href="postconf.5.html#postscreen_enforce_tls">postscreen_enforce_tls</a> ($<a href="postconf.5.html#smtpd_enforce_tls">smtpd_enforce_tls</a>)</b>
<b><a name="miscellaneous_controls">MISCELLANEOUS CONTROLS</a></b>
<b><a href="postconf.5.html#config_directory">config_directory</a> (see 'postconf -d' output)</b>
- The default location of the Postfix <a href="postconf.5.html">main.cf</a> and <a href="master.5.html">master.cf</a> con-
+ The default location of the Postfix <a href="postconf.5.html">main.cf</a> and <a href="master.5.html">master.cf</a> con-
figuration files.
<b><a href="postconf.5.html#delay_logging_resolution_limit">delay_logging_resolution_limit</a> (2)</b>
- 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.
<b><a href="postconf.5.html#command_directory">command_directory</a> (see 'postconf -d' output)</b>
The location of all postfix administrative commands.
<b><a href="postconf.5.html#max_idle">max_idle</a> (100s)</b>
- 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.
<b><a href="postconf.5.html#process_id">process_id</a> (read-only)</b>
The syslog facility of Postfix logging.
<b><a href="postconf.5.html#syslog_name">syslog_name</a> (see 'postconf -d' output)</b>
- 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:
Available in Postfix 3.5 and later:
<b><a href="postconf.5.html#info_log_address_format">info_log_address_format</a> (external)</b>
- 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.).
<b><a name="see_also">SEE ALSO</a></b>
<b><a name="history">HISTORY</a></b>
This service was introduced with Postfix version 2.8.
- Many ideas in <a href="postscreen.8.html"><b>postscreen</b>(8)</a> were explored in earlier work by Michael
+ Many ideas in <a href="postscreen.8.html"><b>postscreen</b>(8)</a> were explored in earlier work by Michael
Tokarev, in OpenBSD spamd, and in MailChannels Traffic Control.
<b>AUTHOR(S)</b>
a domainname; with LMTP it is either a domainname prefixed with <b>inet:</b>
or a pathname prefixed with <b>unix:</b>. If Postfix is built without TLS
support, the resulting <a href="posttls-finger.1.html"><b>posttls-finger</b>(1)</a> program has very limited func-
- tionality, and only the <b>-a</b>, <b>-c</b>, <b>-h</b>, <b>-o</b>, <b>-S</b>, <b>-t</b>, <b>-T</b> and <b>-v</b> options are
+ tionality, and only the <b>-a</b>, <b>-c</b>, <b>-h</b>, <b>-S</b>, <b>-t</b>, <b>-T</b> and <b>-v</b> options are
available.
Note: this is an unsupported test program. No attempt is made to main-
only useful to those who can debug SSL protocol problems
from hex dumps.
+ <b>trace</b> 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
+ <i>process</i><b>_</b><i>name</i>-<i>date</i><b>_</b><i>time</i>.<i>usec</i>-<i>peer</i>-<i>XXXXXX</i>, where <i>XXXXXX</i> 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 <b>-X</b> is in effect,
+ <a href="tlsproxy.8.html">tlsproxy(8)</a> generates the trace file under <i>$queue</i><b>_</b><i>direc-</i>
+ <i>tory</i>/tlstrace/ instead. The keyword is ignored with a
+ warning if Postfix or OpenSSL was built without TLS trace
+ support.
+
<b>untrusted</b>
- 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.
<b>peercert</b>
- 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.
<b>certmatch</b>
- 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.
- <b>cache</b> This logs session cache operations, showing whether ses-
- sion caching is effective with the remote SMTP server.
- Automatically used when reconnecting with the <b>-r</b> option;
+ <b>cache</b> This logs session cache operations, showing whether ses-
+ sion caching is effective with the remote SMTP server.
+ Automatically used when reconnecting with the <b>-r</b> option;
rarely needs to be set explicitly.
<b>verbose</b>
Enables verbose logging in the Postfix TLS driver;
includes all of peercert..cache and more.
- The default is <b>routine,certmatch</b>. After a reconnect, <b>peercert</b>,
+ The default is <b>routine,certmatch</b>. After a reconnect, <b>peercert</b>,
<b>certmatch</b> and <b>verbose</b> are automatically disabled while <b>cache</b> and
<b>summary</b> are enabled.
<b>-m</b> <i>count</i> (default: <b>5</b>)
- When the <b>-r</b> <i>delay</i> option is specified, the <b>-m</b> 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 <b>-r</b> <i>delay</i> option is specified, the <b>-m</b> 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.
<b>-M</b> <i>insecure</i><b>_</b><i>mx</i><b>_</b><i>policy</i> (default: <b>dane</b>)
- The TLS policy for MX hosts with "secure" TLSA records when the
- nexthop destination security level is <b>dane</b>, but the MX record
+ The TLS policy for MX hosts with "secure" TLSA records when the
+ nexthop destination security level is <b>dane</b>, but the MX record
was found via an "insecure" MX lookup. See the <a href="postconf.5.html">main.cf</a> documen-
tation for <a href="postconf.5.html#smtp_tls_dane_insecure_mx_policy">smtp_tls_dane_insecure_mx_policy</a> for details.
<b>-o</b> <i>name=value</i>
- Specify zero or more times to override the value of the <a href="postconf.5.html">main.cf</a>
- parameter <i>name</i> with <i>value</i>. Possible use-cases include overrid-
- ing the values of TLS library parameters, or "<a href="postconf.5.html#myhostname">myhostname</a>" to
+ Specify zero or more times to override the value of the <a href="postconf.5.html">main.cf</a>
+ parameter <i>name</i> with <i>value</i>. Possible use-cases include overrid-
+ ing the values of TLS library parameters, or "<a href="postconf.5.html#myhostname">myhostname</a>" to
configure the SMTP EHLO name sent to the remote server.
<b>-p</b> <i>protocols</i> (default: >=TLSv1)
- TLS protocols that <a href="posttls-finger.1.html"><b>posttls-finger</b>(1)</a> will exclude or include.
+ TLS protocols that <a href="posttls-finger.1.html"><b>posttls-finger</b>(1)</a> will exclude or include.
See <a href="postconf.5.html#smtp_tls_mandatory_protocols">smtp_tls_mandatory_protocols</a> for details.
<b>-P</b> <i>CApath/</i> (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.
<b>-r</b> <i>delay</i>
- With a cacheable TLS session, disconnect and reconnect after
+ With a cacheable TLS session, disconnect and reconnect after
<i>delay</i> seconds. Report whether the session is re-used. Retry if a
- new server is encountered, up to 5 times or as specified with
- the <b>-m</b> option. By default reconnection is disabled, specify a
+ new server is encountered, up to 5 times or as specified with
+ the <b>-m</b> option. By default reconnection is disabled, specify a
positive delay to enable this behavior.
<b>-R</b> Use SRV lookup instead of MX.
<b>-s</b> <i>servername</i>
- 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.
- <b>-S</b> Disable SMTP; that is, connect to an LMTP server. The default
- port for LMTP over TCP is 24. Alternative ports can specified
- by appending "<i>:servicename</i>" or ":<i>portnumber</i>" to the destination
+ <b>-S</b> Disable SMTP; that is, connect to an LMTP server. The default
+ port for LMTP over TCP is 24. Alternative ports can specified
+ by appending "<i>:servicename</i>" or ":<i>portnumber</i>" to the destination
argument.
<b>-t</b> <i>timeout</i> (default: <b>30</b>)
reading the remote server's 220 banner.
<b>-T</b> <i>timeout</i> (default: <b>30</b>)
- The SMTP/LMTP command timeout for EHLO/LHLO, STARTTLS and QUIT.
+ The SMTP/LMTP command timeout for EHLO/LHLO, STARTTLS and QUIT.
- <b>-v</b> Enable verbose Postfix logging. Specify more than once to
+ <b>-v</b> Enable verbose Postfix logging. Specify more than once to
increase the level of verbose logging.
- <b>-w</b> 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 <i>domain</i>:<i>port</i> must of course provide
+ <b>-w</b> 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 <i>domain</i>:<i>port</i> must of course provide
such a service.
- <b>-x</b> Prefer <a href="https://tools.ietf.org/html/rfc7250">RFC7250</a> non-X.509 raw public key (RPK) server creden-
- tials. By default only X.509 certificates are accepted. This
+ <b>-x</b> Prefer <a href="https://tools.ietf.org/html/rfc7250">RFC7250</a> non-X.509 raw public key (RPK) server creden-
+ tials. By default only X.509 certificates are accepted. This
is analogous to setting <b><a href="postconf.5.html#smtp_tls_enable_rpk">smtp_tls_enable_rpk</a> = yes</b> in the <a href="smtp.8.html">smtp(8)</a>
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 <i>match</i> 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 <i>match</i> 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.
- <b>-X</b> Enable <a href="tlsproxy.8.html"><b>tlsproxy</b>(8)</a> mode. This is an unsupported mode, for pro-
+ <b>-X</b> Enable <a href="tlsproxy.8.html"><b>tlsproxy</b>(8)</a> mode. This is an unsupported mode, for pro-
gram development only.
[<b>inet:</b>]<i>domain</i>[:<i>port</i>]
Connect via TCP to domain <i>domain</i>, port <i>port</i>. The default port is
- <b>smtp</b> (or 24 with LMTP). With SMTP an MX lookup is performed to
- resolve the domain to a host, unless the domain is enclosed in
- <b>[]</b>. If you want to connect to a specific MX host, for instance
- <i>mx1.example.com</i>, specify [<i>mx1.example.com</i>] as the destination
+ <b>smtp</b> (or 24 with LMTP). With SMTP an MX lookup is performed to
+ resolve the domain to a host, unless the domain is enclosed in
+ <b>[]</b>. If you want to connect to a specific MX host, for instance
+ <i>mx1.example.com</i>, specify [<i>mx1.example.com</i>] as the destination
and <i>example.com</i> as a <b>match</b> 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 <b>native</b> host lookups (these don't support
- <b>dane</b> or <b>dane-only</b> as no DNSSEC validation information is avail-
+ nation domain is assumed fully qualified and no <a href="ADDRESS_CLASS_README.html#default_domain_class">default domain</a>
+ or search suffixes are applied; you must use fully-qualified
+ names or also enable <b>native</b> host lookups (these don't support
+ <b>dane</b> or <b>dane-only</b> as no DNSSEC validation information is avail-
able via <b>native</b> lookups).
<b>unix:</b><i>pathname</i>
<b>match ...</b>
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 <b>fin-</b>
+ If you specify one or more arguments, these will be used as the
+ list of certificate or public-key digests to match for the <b>fin-</b>
<b>gerprint</b> level, or as the list of DNS names to match in the cer-
tificate at the <b>verify</b> and <b>secure</b> levels. If the security level
is <b>dane</b>, or <b>dane-only</b> the match names are ignored, and <b>hostname,</b>
Optional TLS loglevel override that depends on the remote peer
host name or IP address.
+ <b><a href="postconf.5.html#smtp_tls_trace_size_limit">smtp_tls_trace_size_limit</a> (102400)</b>
+ 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 (<a href="postconf.5.html#smtp_tls_loglevel">smtp_tls_loglevel</a> or
+ <a href="postconf.5.html#smtp_tls_loglevel_maps">smtp_tls_loglevel_maps</a>).
+
+ <b><a href="postconf.5.html#tls_trace_rate_limit">tls_trace_rate_limit</a> (1)</b>
+ The maximum number of TLS traces per <a href="postconf.5.html#anvil_rate_time_unit">anvil_rate_time_unit</a> that
+ all Postfix daemons combined will create.
+
<b><a name="obsolete_tls_controls">OBSOLETE TLS CONTROLS</a></b>
The following configuration parameters exist for compatibility with
Postfix versions before 2.3. Support for these will be removed in a
Optional TLS loglevel override that depends on the remote peer
host name or IP address.
+ <b><a href="postconf.5.html#smtpd_tls_trace_size_limit">smtpd_tls_trace_size_limit</a> (102400)</b>
+ The Postfix SMTP server equivalent of <a href="postconf.5.html#smtp_tls_trace_size_limit">smtp_tls_trace_size_limit</a>.
+
+ <b><a href="postconf.5.html#tls_trace_rate_limit">tls_trace_rate_limit</a> (1)</b>
+ The maximum number of TLS traces per <a href="postconf.5.html#anvil_rate_time_unit">anvil_rate_time_unit</a> that
+ all Postfix daemons combined will create.
+
<b><a name="obsolete_tls_controls">OBSOLETE TLS CONTROLS</a></b>
The following configuration parameters exist for compatibility with
Postfix versions before 2.3. Support for these will be removed in a
# uses snprintf() except on ancient systems.
# .IP \fB-DNO_STDBOOL\fR
# Don't use <stdbool.h>. 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.
# 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"
uses snprintf() except on ancient systems.
.IP \fB\-DNO_STDBOOL\fR
Don't use <stdbool.h>. 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.
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
.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
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.
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
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
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
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
.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.
.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
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
.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"
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
.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
.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
s;\btls_fast_shutdown_enable\b;<a href="postconf.5.html#tls_fast_shutdown_enable">$&</a>;g;
s;\btls_trust_server_ccerts\b;<a href="postconf.5.html#tls_trust_server_ccerts">$&</a>;g;
+ s;\blmtp_tls_trace_size_limit\b;<a href="postconf.5.html#lmtp_tls_trace_size_limit">$&</a>;g;
+ s;\bpostscreen_tls_trace_size_limit\b;<a href="postconf.5.html#postscreen_tls_trace_size_limit">$&</a>;g;
+ s;\bsmtp_tls_trace_size_limit\b;<a href="postconf.5.html#smtp_tls_trace_size_limit">$&</a>;g;
+ s;\bsmtpd_tls_trace_size_limit\b;<a href="postconf.5.html#smtpd_tls_trace_size_limit">$&</a>;g;
+ s;\btls_trace_rate_limit\b;<a href="postconf.5.html#tls_trace_rate_limit">$&</a>;g;
+
s;\bfrozen_delivered_to\b;<a href="postconf.5.html#frozen_delivered_to">$&</a>;g;
s;\breset_owner_alias\b;<a href="postconf.5.html#reset_owner_alias">$&</a>;g;
s;\benable_long_queue_ids\b;<a href="postconf.5.html#enable_long_queue_ids">$&</a>;g;
instead of <tt>snprintf()</tt>. By default, Postfix uses
<tt>snprintf()</tt> except on ancient systems. </td> </tr>
+<tr> <td> </td> <td> -DNO_TLS_TRACE </td> <td> 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
+<tt>smtp_tls_loglevel</tt> in the postconf(5) manual. </td> </tr>
+
<tr> <td colspan="2"> DEBUG=debug_level </td> <td> Specifies a
non-default compiler debugging level. The default is "<tt>-g</tt>".
Specify DEBUG= to turn off debugging. </td> </tr>
<p> Do not use "smtpd_tls_loglevel = 2" or higher except in case
of problems. Use of loglevel 4 is strongly discouraged. </p>
+<p> 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 <b>content</b> 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). </p>
+
<p> This feature is available in Postfix 2.2 and later. </p>
%PARAM smtpd_tls_received_header no
<p> Do not use "smtp_tls_loglevel = 2" or higher except in case of
problems. Use of loglevel 4 is strongly discouraged. </p>
+<p> 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 <b>content</b> 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). </p>
+
<p> This feature is available in Postfix 2.2 and later. </p>
%PARAM smtp_tls_session_cache_database
<p> The lmtp(8) equivalent of smtp_tls_loglevel_maps. </p>
<p> This feature is available in Postfix 3.12 and later. </p>
+
+%PARAM smtp_tls_trace_size_limit 102400
+
+<p> 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.
+</p>
+
+<p> This feature is available in Postfix 3.12 and later. </p>
+
+%PARAM lmtp_tls_trace_size_limit 102400
+
+<p> The lmtp(8) equivalent of smtp_tls_trace_size_limit. </p>
+
+<p> This feature is available in Postfix 3.12 and later. </p>
+
+%PARAM smtpd_tls_trace_size_limit 102400
+
+<p> The Postfix SMTP server equivalent of smtp_tls_trace_size_limit.
+File names start with "smtpd-" instead of "smtp-". </p>
+
+<p> This feature is available in Postfix 3.12 and later. </p>
+
+%PARAM postscreen_tls_trace_size_limit $smtpd_tls_trace_size_limit
+
+<p> 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-". </p>
+
+<p> This feature is available in Postfix 3.12 and later. </p>
+
+%PARAM tls_trace_rate_limit 1
+
+<p> 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. </p>
+
+<p> This feature is available in Postfix 3.12 and later. </p>
YANA
substrings
Substring
+tlstrace
+yyyymmddhhmmss
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
xff
nameN
valueN
+XXXXXX
+cwd
+mkstemp
+tlstr
+tlstrace
+tlstrs
+yyyymmddhhmmss
+datetime
+getpeername
+overshift
+Sayre
/* \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
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;
(remote)->rcpt = 0; \
(remote)->ntls = 0; \
(remote)->auth = 0; \
+ (remote)->tlstr = 0; \
(remote)->start = event_time(); \
} while(0)
(remote)->rcpt = 0; \
(remote)->ntls = 0; \
(remote)->auth = 0; \
+ (remote)->tlstr = 0; \
(remote)->start = _start; \
} while(0)
#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) \
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 */
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);
}
}
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)
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",
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,
};
/* 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;
/* int *rcpts;
/* int *ntls;
/* int *auths;
+/* int *tlstrs;
/* DESCRIPTION
/* anvil_clnt_create() instantiates a local anvil service
/* client endpoint.
/* 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.
/*
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;
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;
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,
#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"
#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
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 *);
#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;
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"
#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;
#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
* 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
#endif
#ifdef NONPROD
-#define MAIL_VERSION_PROD "-nonprod"
+#define MAIL_VERSION_PROD "-" NONPROD
#else
#define MAIL_VERSION_PROD ""
#endif
/* .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"
/* 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
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;
int var_psc_tls_ccert_vd;
int var_psc_starttls_tmout;
+int var_psc_tls_trace_size_limit;
/*
* Global variables.
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[] = {
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[] = {
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);
}
/* 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
/* .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
#include <signal.h>
#include <fcntl.h>
#include <errno.h>
+#include <limits.h> /* INT_MAX */
#include <sys/socket.h>
#include <sys/un.h>
#include <netinet/in.h>
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)
/*
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)
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) {
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);
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 */
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
}
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;
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,
/* .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
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;
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,
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
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
/* .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
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;
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(),
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 */
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,
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
#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
/*
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;
#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
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 *);
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
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 *);
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
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 <process_name>-<yyyymmddhhmmss>.<usec>-<peer>-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
*/
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
/* 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.
/*
/* 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
+/* <process_name>-<date_time>.<usec>-<trace_peer>-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
#include <sys_defs.h>
#include <ctype.h>
#include <string.h>
+#include <fcntl.h> /* O_WRONLY etc. */
+#include <time.h>
/* Utility library. */
/*
* Global library.
*/
+#include <anvil_clnt.h>
+#include <been_here.h>
#include <mail_params.h>
#include <mail_conf.h>
#include <maps.h>
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;
"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,
};
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 "<app>-<yyyymmddhhmmss>.<usec>-<peer>-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)
/* 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,
};
TLScontext->errorcert = 0;
TLScontext->rpt_reported = 0;
TLScontext->ffail_type = 0;
+ TLScontext->trace_bio = 0;
+ TLScontext->trace_size_limit = 0;
return (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)
#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.
#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.
#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.
#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)
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);
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)
&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);
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);
#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 *);
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);
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);
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;
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);
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.
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);
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;
myfree((void *) props->cipher_grade);
myfree((void *) props->cipher_exclusions);
myfree((void *) props->mdalg);
+ myfree((void *) props->trace_peer);
myfree((void *) props);
}
#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 *);
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.
#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
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); \