2
0
mirror of https://github.com/vdukhovni/postfix synced 2025-08-22 09:57:34 +00:00

postfix-3.10-20240926

This commit is contained in:
Wietse Z Venema 2024-09-26 00:00:00 -05:00 committed by Viktor Dukhovni
parent 67b02e7393
commit c42df8386b
71 changed files with 3929 additions and 261 deletions

1
postfix/.indent.pro vendored
View File

@ -360,6 +360,7 @@
-TTEST_CASE
-TTLSMGR_SCACHE
-TTLSP_STATE
-TTLSRPT_WRAPPER
-TTLS_APPL_STATE
-TTLS_CERTS
-TTLS_CLIENT_INIT_PROPS

View File

@ -28187,6 +28187,27 @@ Apologies for any names omitted.
Typofix in comment. File: global/normalize_mailhost_addr.c.
20240730
Infrastructure: added argv_addv() function to append an
array of strings. File: util/argv.c.
20240809
Infrastructure: added a dns_rr_detach() function to extract
one DNS record from a list. Files: dns/dns_rr.c, dns_rr_test.c.
20240816
Infrastructure: factored out strerror() wrapper that reports
"Application error" instead of "Success" when errno == 0.
Files: util/mystrerror.c, util/vbuf_print.c.
20240822
Infrastructure: added "append to buffer" option to the
hex_encode_opt() function. Files: util/hex_encode.[hc];
20240831
Bugfix: require that stable releases have a three-number
@ -28237,7 +28258,46 @@ Apologies for any names omitted.
OpenSSL 3.2) SSL_get0_group_name() function. Viktor Dukhovni.
Files: src/tls/tls.h, src/tls/tls_dh.c, src/tls/tls_misc.c.
20240923
Cleanup: No user-visible change. Updated TLSRPT related
internal comments and internal identifiers; updated error
logging after changes in libtlsrpt error-to-string conversion
functions; minor changes to improve robustness.
20240924
Misc. cleanups. Viktor Dukhovni. Files: src/tls/tls_dh.c,
proto/postconf.proto, src/global/mail_params.h.
TLSRPT documentation cleanups. File: proto/TLSRPT_README.html.
20240924
Code health: added proper unit tests to hex_code.c.
Code health: deduplicated code instances that convert an
IPv4-in-IPv6 address (::ffff:a.b.c.d) to IPv4 form, but
only if IPv4 support is enabled. Files: myaddrinfo,h,
sane_sockaddr_to_hostaddr.c, global/haproxy_srvr.c,
postscreen/postscreen_endpt.c, smtp/smtp_tlsrpt.c.
20240925
TLSRPT support: add routine logging of TLSRPT 'success' and
'failure' events, as there is no other easy way to find out
what the Postfix TLSRPT client is doing. Document what this
logging looks like, that it is not logged for reused TCP
connections, and how to recognize such connections. Files:
tls/tlsrpt_wrapper.c, proto/TLSRPT_README.html.
TLSRPT support: simplified the handling of reused TLS
sessions. Report all TLS handshakes regardless of whether
or not a session is reused. Note that there is only one TLS
handshake for a reused SMTP connection. Files: smtp/smtp_proto.c,
tls/tls_client.c, proto/TLSRPT_README.html.
Cleanup: pre-release checks for snapshot and nonprod releases.
File: mantools/check-snapshot-nonprod.
First general Postfix release with TLSRPT support.

View File

@ -115,9 +115,9 @@ manpages:
done </dev/null
# Some checks require a bin/postconf executable.
pre-release-checks: typo-check missing-proxy-read-maps-check \
postlink-check postfix-files-check check-spell-history \
check-double-history check-table-proto check-see-postconf-d-output \
pre-release-checks: typo-check double-check missing-proxy-read-maps-check \
postlink-check postfix-files-check \
check-table-proto check-see-postconf-d-output \
check-snapshot-nonprod
postfix-files-check:
@ -130,7 +130,10 @@ missing-proxy-read-maps-check:
$(SHLIB_ENV) mantools/missing-proxy-read-maps | diff /dev/null -
typo-check: spell-cc spell-install-proto-text spell-proto-html \
double-cc double-install-proto-text double-proto-html
check-spell-history
double-check: double-cc double-install-proto-text double-proto-html \
check-double-history
spell-cc:
mantools/check-spell-cc | diff /dev/null -

View File

@ -11,6 +11,7 @@ GGeenneerraall ccoonnffiigguurraattiioonn
* SASL_README: SASL Authentication
* TLS_README: TLS Encryption and authentication
* FORWARD_SECRECY_README: TLS Forward Secrecy
* TLSRPT_README: TLSRPT Protocol Support
* IPV6_README: IP Version 6 Support
* SMTPUTF8_README: SMTPUTF8 Support
* MAILLOG_README: Postfix logging to file or stdout

View File

@ -0,0 +1,277 @@
PPoossttffiixx TTLLSSRRPPTT HHoowwttoo
-------------------------------------------------------------------------------
TTaabbllee ooff CCoonntteennttss
* Introduction
* Building Postfix with TLSRPT support
* Turning on TLSRPT
* TLSRPT Status logging
* Delivering TLSRPT summaries via email
* MTA-STS Support via smtp_tls_policy_maps
* Limitations
* Credits
IInnttrroodduuccttiioonn
The TLSRPT protocol is defined in RFC 8460. With this, an email receiving
domain can publish a policy in DNS, and request daily summary reports for
successful and failed SMTP over TLS connections to that domain's MX hosts.
Support for TLSRPT was added in Postfix 3.10.
A policy example looks like this:
_smtp._tls.example.com. IN TXT "v=TLSRPTv1; rua=mailto:smtp-tls-
report@example.com"
Translation: email sending systems are requested to generate daily summaries of
successful and failed SMTP over TLS connections to domain example.com, and to
report those summaries via email to the specified address. Instead of mailto:,
a policy may specify an https: destination.
The high-level diagram shows how Postfix reports summaries to domains that
publish a TLSRPT policy.
Postfix SMTP and TLSRPT client TLSRPT summary Email or HTTP
TLS client --> library --> generator --> delivery
engines
When Postfix TLSRPT support is enabled (with "smtp_tlsrpt_enable = yes"):
* The Postfix SMTP and TLS client engines will generate a "success" or
"failure" event for each TLS handshake,
* They will pass those events to an in-process TLSRPT client library that
sends data over a local socket to
* A TLSRPT report generator that produces daily summary reports.
The TLSRPT client library and report generator are maintained by sys4.
The Postfix implementation supports both DANE (Postfix built-in) and MTA-STS
(through an smtp_tls_policy_maps plug-in).
The Postfix smtp(8) client process implements the SMTP client engine. With
"smtp_tls_connection_reuse = no", the smtp(8) client process also implements
the TLS client engine. With "smtp_tls_connection_reuse = yes", the smtp(8)
client process delegates TLS processing to a Postfix tlsproxy(8) process.
Either way, Postfix will generate the exact same TLSRPT events.
BBuuiillddiinngg PPoossttffiixx wwiitthh TTLLSSRRPPTT ssuuppppoorrtt
These instructions assume that you build Postfix from source code as described
in the INSTALL document. Some modification may be required if you build Postfix
from a vendor-specific source package.
The Postfix TLSRPT client builds on a TLSRPT client library whose source code
can be obtained from:
https://github.com/sys4/tlsrpt
The library is typically installed as a header file in /usr/local/include/
tlsrpt.h and an object library in /usr/local/lib/libtlsrpt.a or /usr/local/lib/
libtlsrpt.so. The actual pathnames will depend on OS platform conventions.
In order to build Postfix with TLSRPT support, you will need to add compiler
options -DUSE_TLSRPT (to build with TLSRPT support), and -I (with the directory
containing the tlsrpt.h header file), and you will need to add linker options
to link with the TLSRPT client library, for example:
make -f Makefile.init makefiles \
"CCARGS=-DUSE_TLSRPT -I/usr/local/include" \
"AUXLIBS=-L/usr/local/lib -ltlsrpt"
Then, just run 'make'.
Note: if your build command line already has CCARGS or AUXLIBS settings,
then simply append the above settings to the existing CCARGS or AUXLIBS
values.
make -f Makefile.init makefiles \
"CCARGS=existing settings... -DUSE_TLSRPT -I/usr/local/include" \
"AUXLIBS=existing settings... -L/usr/local/lib -ltlsrpt"
TTuurrnniinngg oonn TTLLSSRRPPTT
After installing Postfix TLSRPT support, you can enable TLSRPT support in
main.cf like this:
smtp_tlsrpt_enable = yes
smtp_tlsrpt_socket_name = /path/to/socket
The smtp_tlsrpt_socket_name parameter specifies an absolute pathname, or a
pathname that is relative to $queue_directory.
Note: the recommended socket location is still to be determined. A good
socket location would be under the Postfix queue directory, for example:
"smtp_tlsrpt_socket_name = run/tlsrpt/tlsrpt.sock". The advantage of using
a relative name is that it will work equally well whether or not Postfix
chroot is turned on.
Do not specify a location under a directory such as private or public that is
already used by Postfix programs. Only Postfix programs should create sockets
there.
TTLLSSRRPPTT SSttaattuuss llooggggiinngg
With TLSRPT support turned on, the Postfix TLSRPT client will not only report
an event to an invisible daily success/fail summary queue, but it will also log
a visible record to the mail logfile.
Below are a few examples of logging from a Postfix SMTP client or tlsproxy
daemon:
TLSRPT: status=success, domain=example.com, receiving_mx=mail.example.com
[ipaddr]
TLSRPT: status=failure, domain=example.org, receiving_mx=mail.example.org
[ipaddr],
failure_type=starttls_not_supported
TLSRPT: status=failure, domain=example.net, receiving_mx=mail.example.net
[ipaddr],
failure_type=validation_failure, failure_reason=self-signed_certificate
Note: Postfix logs and reports TLSRPT status only for TLS handshakes on a new
SMTP connection. There is no TLSRPT status logging for a reused SMTP
connection. Such connections have Postfix SMTP client logging like this:
Verified TTLLSS ccoonnnneeccttiioonn rreeuusseedd to mail.example.com[ipaddr]:25:
TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)
Untrusted TTLLSS ccoonnnneeccttiioonn rreeuusseedd to mail.example.com[ipaddr]:25:
TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)
Postfix logs certificate verification failures with a level of detail that is
different for a new or reused TLS session.
* A new TLS session is logged with certificate verification failure details:
TLSRPT: status=failure, domain=example.org, receiving_mx=mail.example.org
[ipaddr],
ffaaiilluurree__ttyyppee==vvaalliiddaattiioonn__ffaaiilluurree, ffaaiilluurree__rreeaassoonn==sseellff--ssiiggnneedd__cceerrttiiffiiccaattee
* A reused TLS session is indicated as shown below, and has no certificate
verification details:
mail.example.org[ipaddr]:25: rree--uussiinngg sseessssiioonn with untrusted peer
credential, look for details earlier in the log
TLSRPT: status=failure, domain=example.org, receiving_mx=mail.example.org
[ipaddr],
ffaaiilluurree__ttyyppee==cceerrttiiffiiccaattee__nnoott__ttrruusstteedd
Some Postfix users may wonder where the difference comes from. So this is why.
DDeelliivveerriinngg TTLLSSRRPPTT ssuummmmaarriieess vviiaa eemmaaiill
RFC 8460 suggests not to enforce strict TLS security when sending daily
success/failure summaries via email, to avoid delivery delays caused by a
failure to enforce TLS security. Postfix currently does not have a mechanism to
disable TLS security enforcement when submitting an email message; this section
provides a workaround.
By design, TLSRPT is not a real-time notification system; it takes on average
12 hours before a failure is reported in a daily success/failure summary. If a
TLS-related delay of a day or more is undesirable, one could set up a transport
map to make TLS security optional for specific TLSRPT email notification email
addresses.
/etc/postfix/main.cf:
transport_maps = hash:/etc/postfix/transport
/etc/postfix/transport:
smtp-tls-report@example.com allow-plaintext:
...
/etc/postfix/master.cf:
# service name type private unpriv chroot wakeup maxproc
command
allow-plaintext unix - - n - - smtp
-o smtp_tls_security_level=may
-o smtp_tls_policy_maps=static:may
MMTTAA--SSTTSS SSuuppppoorrtt vviiaa ssmmttpp__ttllss__ppoolliiccyy__mmaappss
Postfix supports MTA-STS though an smtp_tls_policy_maps policy plugin. Postfix
3.10 and later expect a policy response with the usual security level and
matching requirements, plus any applicable name=value attributes described
below. Specify { name = value } when a value may contain whitespace.
Note 1: Postfix 3.10 and later will accept these attributes in an MTA-STS
response even if TLSRPT support is disabled (at build time or run time).
With TLSRPT support turned off, Postfix will use the ttl and policy_failure
attributes, and will ignore the attributes that are used only for TLSRPT.
Note 2: It is an error to specify these attributes for a non-STS policy.
The examples in the table apply to the MTA-STS policy example given in https://
datatracker.ietf.org/doc/html/rfc8460#section-4.5.
* policy_type=type
Specify sts or no-policy-found.
* policy_domain=name
The domain that the MTA-STS policy applies to.
* policy_ttl=time
How long (in seconds) a Postfix SMTP client process will cache the MTA-STS
plugin response.
* { policy_string = value }
Specify one policy_string instance for each MTA-STS policy feature,
enclosed inside "{" and "}" to protect whitespace in attribute values.
Example:
{ policy_string = version: STSv1 } { policy_string = mode: testing }
...
This form ignores whitespace after the opening "{", around the "=", and
before the closing "}".
* mx_host_pattern=pattern
Specify one mx_host_pattern instance for each "mx:" feature in the MTA-STS
policy.
Example:
mx_host_pattern=mx1.example.com mx_host_pattern=mx2.example.com ...
* policy_failure=type
If specified, forces MTA-STS policy enforcement to fail with the indicated
error, even if a server certificate would satisfy conventional PKI
constraints.
Valid errors are sts-policy-fetch-error, sts-policy-invalid, sts-webpki-
invalid, or the less informative validation-failure.
Example:
policy_failure=sts-webpki-invalid
LLiimmiittaattiioonnss
The Postfix TLSRPT implementation reports at most one final TLS handshake
status (either 'success' or 'failure') per connection. Postfix TLSRPT cannot
report a failure and then later report a final status of 'success' for that
same connection. The reason is that it's too complicated to filter TLS errors
and to report error details from the TLS engine back to the SMTP protocol
engine. It just is not how Postfix works internally.
The Postfix TLSRPT implementation reports only TLS handshake success or
failure. It does not report failure to connect, or connections that break after
a successful TLS handshake.
CCrreeddiittss
* The TLSRPT client library and report generator are implemented and
maintained by sys4.
* Wietse Venema implemented the integration with Postfix.

View File

@ -25,3 +25,13 @@ now also distributed with the more recent Eclipse Public License
(EPL) 2.0. Recipients can choose to take the software under the
license of their choice. Those who are more comfortable with the
IPL can continue with that license.
[Feature 20240926]
Support for the TLSRPT protocol (defined in RFC 8460). With this,
an email receiving domain can publish a policy in DNS, and request
daily summary reports for successful and failed SMTP-over-TLS
connections to that domain's MX hosts.
Postfix supports TLSRPT summaries for DANE (built-in) and MTA-STS
(via an smtp_tls_policy_maps plugin). For details, see TLSRPT_README.

View File

@ -6,6 +6,11 @@ Wish list:
Disable -DSNAPSHOT and -DNONPROD in makedefs.
Add smtp_tlsrpt_allow_list feature (default: static:all) to limit
the domains for which Postfix generates TLSRPT daily sumamries.
Add unit tests for smtp_tlsrpt.c, tlstrpd_wrapper.c, ...
Add tests for Message-ID extraction in the cleanup daemon.
When debug logging is enabled, dict_db_open() logs a newline

View File

@ -330,6 +330,7 @@ $readme_directory/STANDARD_CONFIGURATION_README:f:root:-:644
$readme_directory/STRESS_README:f:root:-:644
$readme_directory/TLS_LEGACY_README:f:root:-:644
$readme_directory/TLS_README:f:root:-:644
$readme_directory/TLSRPT_README:f:root:-:644
$readme_directory/TUNING_README:f:root:-:644
$readme_directory/ULTRIX_README:f:root:-:644
$readme_directory/UUCP_README:f:root:-:644
@ -392,6 +393,7 @@ $html_directory/STANDARD_CONFIGURATION_README.html:f:root:-:644
$html_directory/STRESS_README.html:f:root:-:644
$html_directory/TLS_LEGACY_README.html:f:root:-:644
$html_directory/TLS_README.html:f:root:-:644
$html_directory/TLSRPT_README.html:f:root:-:644
$html_directory/TUNING_README.html:f:root:-:644
$html_directory/ULTRIX_README.html:f:root:-:644:o
$html_directory/UUCP_README.html:f:root:-:644

View File

@ -0,0 +1,410 @@
<!doctype html public "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>Postfix TLSRPT notification Howto</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<link rel='stylesheet' type='text/css' href='postfix-doc.css'>
</head>
<body>
<h1><img src="postfix-logo.jpg" width="203" height="98" ALT="">Postfix TLSRPT Howto</h1>
<hr>
<h2> Table of Contents </h2>
<ul>
<li> <a href="#intro"> Introduction </a> </li>
<li> <a href="#building"> Building Postfix with TLSRPT support </a>
<li> <a href="#using"> Turning on TLSRPT </a> </li>
<li> <a href="#logging"> TLSRPT Status logging </a> </li>
<li> <a href="#delivering"> Delivering TLSRPT summaries via email</a> </li>
<li> <a href="#mta-sts"> MTA-STS Support via smtp_tls_policy_maps </a> </li>
<li> <a href="#limitations"> Limitations </a></li>
<li> <a href="#credits"> Credits </a> </li>
</ul>
<h2> <a name="intro"> Introduction </a> </h2>
<p> The TLSRPT protocol is defined in <a href="https://tools.ietf.org/html/rfc8460">RFC 8460</a>. With this, an email
receiving domain can publish a policy in DNS, and request daily
summary reports for successful and failed SMTP over TLS connections
to that domain's MX hosts. Support for TLSRPT was added in Postfix
3.10. </p>
<p> A policy example looks like this: </p>
<blockquote>
<pre>
_smtp._tls.example.com. IN TXT "v=TLSRPTv1; rua=mailto:smtp-tls-report@example.com"
</pre>
</blockquote>
<p> Translation: email sending systems are requested to generate daily
summaries of successful and failed SMTP over TLS connections to domain
<tt>example.com</tt>, and to report those summaries via email to the
specified address. Instead of <tt>mailto:</tt>, a policy may specify an
<tt>https:</tt> destination. </p>
<p> The high-level diagram shows how Postfix reports summaries to
domains that publish a TLSRPT policy.
<blockquote>
<table>
<tr> <td align="center" bgcolor="#f0f0ff"> Postfix SMTP and<br> TLS
client engines </td> <td> <tt> --&gt; </tt> </td>
<td align="center" bgcolor="#f0f0ff"> TLSRPT client <br> library
</td> <td> <tt> --&gt; </tt> </td>
<td align="center" bgcolor="#f0f0ff"> TLSRPT summary <br> generator
</td> <td> <tt> --&gt; </tt> </td>
<td align="center" bgcolor="#f0f0ff"> Email or HTTP <br> delivery
</td> </tr>
</table>
</blockquote>
<p> When Postfix TLSRPT support is enabled (with "<a href="postconf.5.html#smtp_tlsrpt_enable">smtp_tlsrpt_enable</a>
= yes"): </p>
<ul>
<li> <p> The Postfix SMTP and TLS client engines will generate a
"success" or "failure" event for each TLS handshake, </p>
<li> <p> They will pass those events to an in-process TLSRPT client
library that sends data over a local socket to </p>
<li> <p> A TLSRPT report generator that produces daily summary
reports. </p>
</ul>
<p> The TLSRPT client library and report generator are maintained
by sys4. </p>
<p> The Postfix implementation supports both DANE (Postfix built-in)
and MTA-STS (through an <a href="postconf.5.html#smtp_tls_policy_maps">smtp_tls_policy_maps</a> plug-in).
</p>
<p> The Postfix <a href="smtp.8.html">smtp(8)</a> client process implements the SMTP client
engine. With "<a href="postconf.5.html#smtp_tls_connection_reuse">smtp_tls_connection_reuse</a> = no", the <a href="smtp.8.html">smtp(8)</a> client
process also implements the TLS client engine. With
"<a href="postconf.5.html#smtp_tls_connection_reuse">smtp_tls_connection_reuse</a> = yes", the <a href="smtp.8.html">smtp(8)</a> client process
delegates TLS processing to a Postfix <a href="tlsproxy.8.html">tlsproxy(8)</a> process. Either
way, Postfix will generate the exact same TLSRPT events. </p>
<h2> <a name="building"> Building Postfix with TLSRPT support </a>
</h2>
<p> These instructions assume that you build Postfix from source
code as described in the <a href="INSTALL.html">INSTALL</a> document. Some modification may
be required if you build Postfix from a vendor-specific source
package. </p>
<p> The Postfix TLSRPT client builds on a TLSRPT client library
whose source code can be obtained from: </p>
<blockquote>
<p> <a href="https://github.com/sys4/tlsrpt">https://github.com/sys4/tlsrpt</a> </p>
</blockquote>
<p> The library is typically installed as a header file in
/usr/local/include/tlsrpt.h and an object library in
/usr/local/lib/libtlsrpt.a or /usr/local/lib/libtlsrpt.so. The
actual pathnames will depend on OS platform conventions. </p>
<p> In order to build Postfix with TLSRPT support, you will need
to add compiler options <tt>-DUSE_TLSRPT</tt> (to build with TLSRPT
support), and <tt>-I</tt> (with the directory containing the tlsrpt.h
header file), and you will need to add linker options to link with
the TLSRPT client library, for example: </p>
<blockquote>
<pre>
make -f Makefile.init makefiles \
"CCARGS=-DUSE_TLSRPT -I/usr/local/include" \
"AUXLIBS=-L/usr/local/lib -ltlsrpt"
</pre>
</blockquote>
<p> Then, just run 'make'. </p>
<blockquote>
<p> Note: if your build command line already has CCARGS or AUXLIBS
settings, then simply append the above settings to the existing CCARGS
or AUXLIBS values. </p>
<blockquote>
<pre>
make -f Makefile.init makefiles \
"CCARGS=<i>existing settings...</i> -DUSE_TLSRPT -I/usr/local/include" \
"AUXLIBS=<i>existing settings...</i> -L/usr/local/lib -ltlsrpt"
</pre>
</blockquote>
</blockquote>
<h2> <a name="using"> Turning on TLSRPT </a> </h2>
<p> After installing Postfix TLSRPT support, you can enable TLSRPT
support in <a href="postconf.5.html">main.cf</a> like this: </p>
<blockquote>
<pre>
<a href="postconf.5.html#smtp_tlsrpt_enable">smtp_tlsrpt_enable</a> = yes
<a href="postconf.5.html#smtp_tlsrpt_socket_name">smtp_tlsrpt_socket_name</a> = /path/to/socket
</pre>
</blockquote>
<p> The <a href="postconf.5.html#smtp_tlsrpt_socket_name">smtp_tlsrpt_socket_name</a> parameter specifies an absolute
pathname, or a pathname that is relative to $<a href="postconf.5.html#queue_directory">queue_directory</a>. </p>
<blockquote>
<p> Note: the recommended socket location is still to be determined.
A good socket location would be under the Postfix queue directory,
for example: "<a href="postconf.5.html#smtp_tlsrpt_socket_name">smtp_tlsrpt_socket_name</a> = run/tlsrpt/tlsrpt.sock".
The advantage of using a relative name is that it will work equally
well whether or not Postfix chroot is turned on. </p>
</blockquote>
<p> Do not specify a location under a directory such as <tt>private</tt>
or <tt>public</tt> that is already used by Postfix programs. Only Postfix
programs should create sockets there. </p>
<h2> <a name="logging"> TLSRPT Status logging </a> </h2>
<p> With TLSRPT support turned on, the Postfix TLSRPT client will
not only report an event to an invisible daily success/fail summary
queue, but it will also log a visible record to the mail logfile.
</p>
<p> Below are a few examples of logging from a Postfix SMTP client
or tlsproxy daemon: </p>
<blockquote>
<pre>
TLSRPT: status=success, domain=example.com, receiving_mx=mail.example.com[ipaddr]
&nbsp;
TLSRPT: status=failure, domain=example.org, receiving_mx=mail.example.org[ipaddr],
failure_type=starttls_not_supported
&nbsp;
TLSRPT: status=failure, domain=example.net, receiving_mx=mail.example.net[ipaddr],
failure_type=validation_failure, failure_reason=self-signed_certificate
</pre>
</blockquote>
<p> Note: Postfix logs and reports TLSRPT status only for TLS
handshakes on a new SMTP connection. There is no TLSRPT status
logging for a reused SMTP connection. Such connections have
Postfix SMTP client logging like this: </p>
<blockquote>
<pre>
Verified <b>TLS connection reused</b> to mail.example.com[ipaddr]:25:
TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)
&nbsp;
Untrusted <b>TLS connection reused</b> to mail.example.com[ipaddr]:25:
TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)
</pre>
</blockquote>
<p> Postfix logs certificate verification failures with a level of
detail that is different for a new or reused TLS session. </p>
<ul>
<li> <p> A new TLS session is logged with certificate verification
failure details: </p>
<pre>
TLSRPT: status=failure, domain=example.org, receiving_mx=mail.example.org[ipaddr],
<b>failure_type=validation_failure</b>, <b>failure_reason=self-signed_certificate</b>
</pre>
<li> <p> A reused TLS session is indicated as shown below, and has
no certificate verification details: </p>
<pre>
mail.example.org[ipaddr]:25: <b>re-using session</b> with untrusted peer
credential, look for details earlier in the log
TLSRPT: status=failure, domain=example.org, receiving_mx=mail.example.org[ipaddr],
<b>failure_type=certificate_not_trusted</b>
</pre>
</ul>
<p> Some Postfix users may wonder where the difference comes from.
So this is why. </p>
<h2> <a name="delivering"> Delivering TLSRPT summaries via email</a> </h2>
<p> <a href="https://tools.ietf.org/html/rfc8460">RFC 8460</a> suggests not to enforce strict TLS security when sending
daily success/failure summaries via email, to avoid delivery delays
caused by a failure to enforce TLS security. Postfix currently does
not have a mechanism to disable TLS security enforcement when
submitting an email message; this section provides a workaround. </p>
<p> By design, TLSRPT is not a real-time notification system; it
takes on average 12 hours before a failure is reported in a daily
success/failure summary. If a TLS-related delay of a day or more
is undesirable, one could set up a transport map to make TLS security
optional for specific TLSRPT email notification email addresses.
</p>
<blockquote>
<pre>
/etc/postfix/<a href="postconf.5.html">main.cf</a>:
<a href="postconf.5.html#transport_maps">transport_maps</a> = <a href="DATABASE_README.html#types">hash</a>:/etc/postfix/transport
&nbsp
/etc/postfix/transport:
smtp-tls-report@example.com allow-plaintext:
...
&nbsp
/etc/postfix/<a href="master.5.html">master.cf</a>:
# service name type private unpriv chroot wakeup maxproc command
allow-plaintext unix - - n - - smtp
-o <a href="postconf.5.html#smtp_tls_security_level">smtp_tls_security_level</a>=may
-o <a href="postconf.5.html#smtp_tls_policy_maps">smtp_tls_policy_maps</a>=<a href="DATABASE_README.html#types">static</a>:may
</pre>
</blockquote>
<h2> <a name="mta-sts"> MTA-STS Support via smtp_tls_policy_maps
</a></h2>
<p> Postfix supports MTA-STS though an <a href="postconf.5.html#smtp_tls_policy_maps">smtp_tls_policy_maps</a>
policy plugin. Postfix 3.10 and later expect a policy response with
the usual security level and matching requirements, plus any
applicable name=value attributes described below. Specify <tt>{
name = value }</tt> when a value may contain whitespace. </p>
<blockquote>
<p> Note 1: Postfix 3.10 and later will accept these attributes in
an MTA-STS response even if TLSRPT support is disabled (at build
time or run time). With TLSRPT support turned off, Postfix
will use the <tt>ttl</tt> and <tt>policy_failure</tt> attributes,
and will ignore the attributes that are used only for TLSRPT. </p>
<p> Note 2: It is an error to specify these attributes for a non-STS
policy. </p>
</blockquote>
<p> The examples in the table apply to the MTA-STS policy example
given in <a href="https://datatracker.ietf.org/doc/html/rfc8460#section-4.5">https://datatracker.ietf.org/doc/html/rfc8460#section-4.5</a>.
<p>
<ul>
<li> <p> <tt> policy_type=<i>type</i> </tt>
<p> Specify <tt>sts</tt> or <tt>no-policy-found</tt>. </p> </li>
<li> <p> <tt> policy_domain=<i>name</i> </tt> </p>
<p> The domain that the MTA-STS policy applies to. </p> </li>
<li> <p> <tt> policy_ttl=<i>time</i> </tt> </p>
<p> How long (in seconds) a Postfix SMTP client process will cache
the MTA-STS plugin response. </p> </li>
<li> <p> <tt> { policy_string = <i>value</i> } </tt> </p>
<p> Specify one <tt>policy_string</tt> instance for each MTA-STS
policy feature, enclosed inside "{" and "}" to protect whitespace
in attribute values. </p>
<p> Example: </p>
<blockquote>
<pre>
{ policy_string = version: STSv1 } { policy_string = mode: testing } ...
</pre>
</blockquote>
<p> This form ignores whitespace after the opening "{", around the "=",
and before the closing "}".</p> </li>
<li> <p> <tt> mx_host_pattern=<i>pattern</i> </tt> </p>
<p> Specify one <tt>mx_host_pattern</tt> instance for each "mx:" feature
in the MTA-STS policy. </p>
<p> Example: </p>
<blockquote>
<pre>
mx_host_pattern=mx1.example.com mx_host_pattern=mx2.example.com ...
</pre>
</blockquote>
</li>
<li> <p> <tt> policy_failure=<i>type</i> </tt> </p>
<p> If specified, forces MTA-STS policy enforcement to fail with
the indicated error, even if a server certificate would satisfy
conventional PKI constraints. </p>
<p> Valid errors are <tt>sts-policy-fetch-error, sts-policy-invalid</tt>,
<tt>sts-webpki-invalid</tt>, or the less informative
<tt>validation-failure</tt>. </p>
<p> Example: </p>
<blockquote>
<pre>
policy_failure=sts-webpki-invalid
</pre>
</blockquote>
</li>
</ul>
<h2> <a name="limitations"> Limitations </a></h2>
<p> The Postfix TLSRPT implementation reports at most one final TLS
handshake status (either 'success' or 'failure') per connection.
Postfix TLSRPT cannot report a failure and then later report a final
status of 'success' for that same connection. The reason is that
it's too complicated to filter TLS errors and to report error details
from the TLS engine back to the SMTP protocol engine. It just is
not how Postfix works internally. </p>
<p> The Postfix TLSRPT implementation reports only TLS handshake
success or failure. It does not report failure to connect, or
connections that break after a successful TLS handshake. </p>
<h2> <a name="credits"> Credits </a> </h2>
<ul>
<li> The TLSRPT client library and report generator are implemented
and maintained by sys4. </li>
<li> Wietse Venema implemented the integration with Postfix.
</li>
</ul>
</body>
</html>

View File

@ -45,6 +45,8 @@ configuration examples </a>
<li> <a href="FORWARD_SECRECY_README.html"> TLS Forward Secrecy </a>
<li> <a href="TLSRPT_README.html"> TLSRPT Protocol Support </a>
<li> <a href="IPV6_README.html"> IP Version 6 Support </a>
<li> <a href="SMTPUTF8_README.html"> SMTPUTF8 Support </a>

View File

@ -746,6 +746,13 @@ SMTP,(LMTP) SMTP,(LMTP)
Request that remote SMTP servers send an <a href="https://tools.ietf.org/html/rfc7250">RFC7250</a> raw public key
instead of an X.509 certificate.
<b><a href="postconf.5.html#smtp_tlsrpt_enable">smtp_tlsrpt_enable</a> (no)</b>
Enable support for <a href="https://tools.ietf.org/html/rfc8460">RFC 8460</a> TLSRPT notifications.
<b><a href="postconf.5.html#smtp_tlsrpt_socket_name">smtp_tlsrpt_socket_name</a> (empty)</b>
The pathname of a UNIX-domain datagram socket that is managed by
a local TLSRPT reporting service.
<b>OBSOLETE STARTTLS CONTROLS</b>
The following configuration parameters exist for compatibility with
Postfix versions before 2.3. Support for these will be removed in a

View File

@ -43,6 +43,8 @@ POSTCAT(1) POSTCAT(1)
<b>-f</b> Prepend the file name to each output line.
This feature is available in Postfix 3.10 and later.
<b>-h</b> Show message header content. The <b>-h</b> option produces output from
the beginning of the message up to, but not including, the first
non-header line.

View File

@ -14800,6 +14800,33 @@ Postfix versions. </p>
<p> This feature is available in Postfix 3.0 and later. </p>
</DD>
<DT><b><a name="smtp_tlsrpt_enable">smtp_tlsrpt_enable</a>
(default: no)</b></DT><DD>
<p> Enable support for <a href="https://tools.ietf.org/html/rfc8460">RFC 8460</a> TLSRPT notifications. A mail receiving
domain can publish a TLSRPT policy in DNS, to request periodic
summaries of successful and failed SMTP over TLS connections to
their mail servers. This feature requires that Postfix is built
with a TLSRPT supporting library. </p>
<p> This feature is available in Postfix &ge; 3.10. </p>
</DD>
<DT><b><a name="smtp_tlsrpt_socket_name">smtp_tlsrpt_socket_name</a>
(default: empty)</b></DT><DD>
<p> The pathname of a UNIX-domain datagram socket that is managed
by a local TLSRPT reporting service. This parameter must specify a
pathname (absolute, or relative to $<a href="postconf.5.html#queue_directory">queue_directory</a>) when
"<a href="postconf.5.html#smtp_tlsrpt_enable">smtp_tlsrpt_enable</a> = yes". </p>
<p> This feature is available in Postfix &ge; 3.10. </p>
</DD>
<DT><b><a name="smtp_use_tls">smtp_use_tls</a>

View File

@ -746,6 +746,13 @@ SMTP,(LMTP) SMTP,(LMTP)
Request that remote SMTP servers send an <a href="https://tools.ietf.org/html/rfc7250">RFC7250</a> raw public key
instead of an X.509 certificate.
<b><a href="postconf.5.html#smtp_tlsrpt_enable">smtp_tlsrpt_enable</a> (no)</b>
Enable support for <a href="https://tools.ietf.org/html/rfc8460">RFC 8460</a> TLSRPT notifications.
<b><a href="postconf.5.html#smtp_tlsrpt_socket_name">smtp_tlsrpt_socket_name</a> (empty)</b>
The pathname of a UNIX-domain datagram socket that is managed by
a local TLSRPT reporting service.
<b>OBSOLETE STARTTLS CONTROLS</b>
The following configuration parameters exist for compatibility with
Postfix versions before 2.3. Support for these will be removed in a

View File

@ -41,6 +41,8 @@ Show message envelope content.
This feature is available in Postfix 2.7 and later.
.IP \fB\-f\fR
Prepend the file name to each output line.
.sp
This feature is available in Postfix 3.10 and later.
.IP \fB\-h\fR
Show message header content. The \fB\-h\fR option produces
output from the beginning of the message up to, but not

View File

@ -9860,6 +9860,21 @@ More examples are in TLS_README, including examples for older
Postfix versions.
.PP
This feature is available in Postfix 3.0 and later.
.SH smtp_tlsrpt_enable (default: no)
Enable support for RFC 8460 TLSRPT notifications. A mail receiving
domain can publish a TLSRPT policy in DNS, to request periodic
summaries of successful and failed SMTP over TLS connections to
their mail servers. This feature requires that Postfix is built
with a TLSRPT supporting library.
.PP
This feature is available in Postfix >= 3.10.
.SH smtp_tlsrpt_socket_name (default: empty)
The pathname of a UNIX\-domain datagram socket that is managed
by a local TLSRPT reporting service. This parameter must specify a
pathname (absolute, or relative to $queue_directory) when
"smtp_tlsrpt_enable = yes".
.PP
This feature is available in Postfix >= 3.10.
.SH smtp_use_tls (default: no)
Opportunistic mode: use TLS when a remote SMTP server announces
STARTTLS support, otherwise send the mail in the clear. Beware:

View File

@ -672,6 +672,12 @@ Available in Postfix version 3.9 and later:
.IP "\fBsmtp_tls_enable_rpk (no)\fR"
Request that remote SMTP servers send an RFC7250 raw public key
instead of an X.509 certificate.
.PP Available in Postfix version 3.10 and later:
.IP "\fBsmtp_tlsrpt_enable (no)\fR"
Enable support for RFC 8460 TLSRPT notifications.
.IP "\fBsmtp_tlsrpt_socket_name (empty)\fR"
The pathname of a UNIX\-domain datagram socket that is managed
by a local TLSRPT reporting service.
.SH "OBSOLETE STARTTLS CONTROLS"
.na
.nf

View File

@ -6,9 +6,23 @@ postfix-[0-9]*.[0-9]*.[0-9]*)
test -f conf/makedefs.out || {
echo "Error: no conf/makedefs.out" 1>&2; exit 1; }
grep 'CCARGS.*-DSNAPSHOT' conf/makedefs.out && {
echo "Error: stable release builds with -DSNAPSHOT" 1>&2, exit 1; }
echo "Error: stable release builds with -DSNAPSHOT" 1>&2; exit 1; }
grep 'CCARGS.*-DNONPROD' conf/makedefs.out && {
echo "Error: stable release builds with -DNONPROD" 1>&2, exit 1; }
echo "Error: stable release builds with -DNONPROD" 1>&2; exit 1; }
mail_version=$(sh postfix-env.sh bin/postconf -h mail_version) || exit 1
test "postfix-$mail_version" = "$version" || {
echo "Error: version '$mail_version' in src/global/mail_version.h does not match version in pathname '$(env - pwd)'" 1>&2; exit 1; }
;;
postfix-[0-9]*.[0-9]*-*nonprod*)
grep 'CCARGS.*-DNONPROD' conf/makedefs.out || {
echo "Error: non-prod release builds without -DNONPROD" 1>&2; exit 1; }
mail_version=$(sh postfix-env.sh bin/postconf -h mail_version) || exit 1
test "postfix-$mail_version" = "$version" || {
echo "Error: version '$mail_version' in src/global/mail_version.h does not match version in pathname '$(env - pwd)'" 1>&2; exit 1; }
;;
postfix-[0-9]*.[0-9]*-*)
grep 'CCARGS.*-DNONPROD' conf/makedefs.out && {
echo "Error: snapshot release builds with -DNONPROD" 1>&2; exit 1; }
mail_version=$(sh postfix-env.sh bin/postconf -h mail_version) || exit 1
test "postfix-$mail_version" = "$version" || {
echo "Error: version '$mail_version' in src/global/mail_version.h does not match version in pathname '$(env - pwd)'" 1>&2; exit 1; }

View File

@ -721,6 +721,10 @@ while (<>) {
s;\bdnssec_probe\b;<a href="postconf.5.html#dnssec_probe">$&</a>;g;
s;\bsmtp_tls_connection_reuse\b;<a href="postconf.5.html#smtp_tls_connection_reuse">$&</a>;g;
s;\blmtp_tls_connection_reuse\b;<a href="postconf.5.html#lmtp_tls_connection_reuse">$&</a>;g;
s;\bsmtp_tlsrpt_enable\b;<a href="postconf.5.html#smtp_tlsrpt_enable">$&</a>;g;
s;\bsmtp_tlsrpt_socket_name\b;<a href="postconf.5.html#smtp_tlsrpt_socket_name">$&</a>;g;
s;\blmtp_tlsrpt_enable\b;<a href="postconf.5.html#lmtp_tlsrpt_enable">$&</a>;g;
s;\blmtp_tlsrpt_socket_name\b;<a href="postconf.5.html#lmtp_tlsrpt_socket_name">$&</a>;g;
s;\bsmtpd_enforce_tls\b;<a href="postconf.5.html#smtpd_enforce_tls">$&</a>;g;
s;\bsmtpd_sasl_tls_security_options\b;<a href="postconf.5.html#smtpd_sasl_tls_security_options">$&</a>;g;
s;\bsmtpd_sasl_type\b;<a href="postconf.5.html#smtpd_sasl_type">$&</a>;g;

View File

@ -50,6 +50,7 @@ HTML = ../html/ADDRESS_CLASS_README.html \
../html/STANDARD_CONFIGURATION_README.html \
../html/STRESS_README.html \
../html/TLS_README.html ../html/TLS_LEGACY_README.html \
../html/TLSRPT_README.html \
../html/TUNING_README.html \
../html/UUCP_README.html \
../html/VERP_README.html ../html/VIRTUAL_README.html \
@ -100,6 +101,7 @@ README = ../README_FILES/ADDRESS_CLASS_README \
../README_FILES/STANDARD_CONFIGURATION_README \
../README_FILES/STRESS_README \
../README_FILES/TLS_README ../README_FILES/TLS_LEGACY_README \
../README_FILES/TLSRPT_README \
../README_FILES/TUNING_README \
../README_FILES/UUCP_README \
../README_FILES/VERP_README ../README_FILES/VIRTUAL_README \
@ -343,6 +345,9 @@ clobber:
../html/TLS_LEGACY_README.html: TLS_LEGACY_README.html
$(DETAB) $? | $(POSTLINK) >$@
../html/TLSRPT_README.html: TLSRPT_README.html
$(DETAB) $? | $(POSTLINK) >$@
../README_FILES/ADDRESS_CLASS_README: ADDRESS_CLASS_README.html
$(DETAB) $? | $(HT2READ) >$@
@ -529,6 +534,9 @@ clobber:
../README_FILES/TLS_LEGACY_README: TLS_LEGACY_README.html
$(DETAB) $? | $(HT2READ) >$@
../README_FILES/TLSRPT_README: TLSRPT_README.html
$(DETAB) $? | $(HT2READ) >$@
../README_FILES/AAAREADME: ../html/index.html $(MAKEAAA)
$(MAKEAAA) ../html/index.html | $(HT2READ) | $(DETAB) >$@

View File

@ -0,0 +1,410 @@
<!doctype html public "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>Postfix TLSRPT notification Howto</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<link rel='stylesheet' type='text/css' href='postfix-doc.css'>
</head>
<body>
<h1><img src="postfix-logo.jpg" width="203" height="98" ALT="">Postfix TLSRPT Howto</h1>
<hr>
<h2> Table of Contents </h2>
<ul>
<li> <a href="#intro"> Introduction </a> </li>
<li> <a href="#building"> Building Postfix with TLSRPT support </a>
<li> <a href="#using"> Turning on TLSRPT </a> </li>
<li> <a href="#logging"> TLSRPT Status logging </a> </li>
<li> <a href="#delivering"> Delivering TLSRPT summaries via email</a> </li>
<li> <a href="#mta-sts"> MTA-STS Support via smtp_tls_policy_maps </a> </li>
<li> <a href="#limitations"> Limitations </a></li>
<li> <a href="#credits"> Credits </a> </li>
</ul>
<h2> <a name="intro"> Introduction </a> </h2>
<p> The TLSRPT protocol is defined in RFC 8460. With this, an email
receiving domain can publish a policy in DNS, and request daily
summary reports for successful and failed SMTP over TLS connections
to that domain's MX hosts. Support for TLSRPT was added in Postfix
3.10. </p>
<p> A policy example looks like this: </p>
<blockquote>
<pre>
_smtp._tls.example.com. IN TXT "v=TLSRPTv1; rua=mailto:smtp-tls-report@example.com"
</pre>
</blockquote>
<p> Translation: email sending systems are requested to generate daily
summaries of successful and failed SMTP over TLS connections to domain
<tt>example.com</tt>, and to report those summaries via email to the
specified address. Instead of <tt>mailto:</tt>, a policy may specify an
<tt>https:</tt> destination. </p>
<p> The high-level diagram shows how Postfix reports summaries to
domains that publish a TLSRPT policy.
<blockquote>
<table>
<tr> <td align="center" bgcolor="#f0f0ff"> Postfix SMTP and<br> TLS
client engines </td> <td> <tt> --&gt; </tt> </td>
<td align="center" bgcolor="#f0f0ff"> TLSRPT client <br> library
</td> <td> <tt> --&gt; </tt> </td>
<td align="center" bgcolor="#f0f0ff"> TLSRPT summary <br> generator
</td> <td> <tt> --&gt; </tt> </td>
<td align="center" bgcolor="#f0f0ff"> Email or HTTP <br> delivery
</td> </tr>
</table>
</blockquote>
<p> When Postfix TLSRPT support is enabled (with "smtp_tlsrpt_enable
= yes"): </p>
<ul>
<li> <p> The Postfix SMTP and TLS client engines will generate a
"success" or "failure" event for each TLS handshake, </p>
<li> <p> They will pass those events to an in-process TLSRPT client
library that sends data over a local socket to </p>
<li> <p> A TLSRPT report generator that produces daily summary
reports. </p>
</ul>
<p> The TLSRPT client library and report generator are maintained
by sys4. </p>
<p> The Postfix implementation supports both DANE (Postfix built-in)
and MTA-STS (through an smtp_tls_policy_maps plug-in).
</p>
<p> The Postfix smtp(8) client process implements the SMTP client
engine. With "smtp_tls_connection_reuse = no", the smtp(8) client
process also implements the TLS client engine. With
"smtp_tls_connection_reuse = yes", the smtp(8) client process
delegates TLS processing to a Postfix tlsproxy(8) process. Either
way, Postfix will generate the exact same TLSRPT events. </p>
<h2> <a name="building"> Building Postfix with TLSRPT support </a>
</h2>
<p> These instructions assume that you build Postfix from source
code as described in the INSTALL document. Some modification may
be required if you build Postfix from a vendor-specific source
package. </p>
<p> The Postfix TLSRPT client builds on a TLSRPT client library
whose source code can be obtained from: </p>
<blockquote>
<p> https://github.com/sys4/tlsrpt </p>
</blockquote>
<p> The library is typically installed as a header file in
/usr/local/include/tlsrpt.h and an object library in
/usr/local/lib/libtlsrpt.a or /usr/local/lib/libtlsrpt.so. The
actual pathnames will depend on OS platform conventions. </p>
<p> In order to build Postfix with TLSRPT support, you will need
to add compiler options <tt>-DUSE_TLSRPT</tt> (to build with TLSRPT
support), and <tt>-I</tt> (with the directory containing the tlsrpt.h
header file), and you will need to add linker options to link with
the TLSRPT client library, for example: </p>
<blockquote>
<pre>
make -f Makefile.init makefiles \
"CCARGS=-DUSE_TLSRPT -I/usr/local/include" \
"AUXLIBS=-L/usr/local/lib -ltlsrpt"
</pre>
</blockquote>
<p> Then, just run 'make'. </p>
<blockquote>
<p> Note: if your build command line already has CCARGS or AUXLIBS
settings, then simply append the above settings to the existing CCARGS
or AUXLIBS values. </p>
<blockquote>
<pre>
make -f Makefile.init makefiles \
"CCARGS=<i>existing settings...</i> -DUSE_TLSRPT -I/usr/local/include" \
"AUXLIBS=<i>existing settings...</i> -L/usr/local/lib -ltlsrpt"
</pre>
</blockquote>
</blockquote>
<h2> <a name="using"> Turning on TLSRPT </a> </h2>
<p> After installing Postfix TLSRPT support, you can enable TLSRPT
support in main.cf like this: </p>
<blockquote>
<pre>
smtp_tlsrpt_enable = yes
smtp_tlsrpt_socket_name = /path/to/socket
</pre>
</blockquote>
<p> The smtp_tlsrpt_socket_name parameter specifies an absolute
pathname, or a pathname that is relative to $queue_directory. </p>
<blockquote>
<p> Note: the recommended socket location is still to be determined.
A good socket location would be under the Postfix queue directory,
for example: "smtp_tlsrpt_socket_name = run/tlsrpt/tlsrpt.sock".
The advantage of using a relative name is that it will work equally
well whether or not Postfix chroot is turned on. </p>
</blockquote>
<p> Do not specify a location under a directory such as <tt>private</tt>
or <tt>public</tt> that is already used by Postfix programs. Only Postfix
programs should create sockets there. </p>
<h2> <a name="logging"> TLSRPT Status logging </a> </h2>
<p> With TLSRPT support turned on, the Postfix TLSRPT client will
not only report an event to an invisible daily success/fail summary
queue, but it will also log a visible record to the mail logfile.
</p>
<p> Below are a few examples of logging from a Postfix SMTP client
or tlsproxy daemon: </p>
<blockquote>
<pre>
TLSRPT: status=success, domain=example.com, receiving_mx=mail.example.com[ipaddr]
&nbsp;
TLSRPT: status=failure, domain=example.org, receiving_mx=mail.example.org[ipaddr],
failure_type=starttls_not_supported
&nbsp;
TLSRPT: status=failure, domain=example.net, receiving_mx=mail.example.net[ipaddr],
failure_type=validation_failure, failure_reason=self-signed_certificate
</pre>
</blockquote>
<p> Note: Postfix logs and reports TLSRPT status only for TLS
handshakes on a new SMTP connection. There is no TLSRPT status
logging for a reused SMTP connection. Such connections have
Postfix SMTP client logging like this: </p>
<blockquote>
<pre>
Verified <b>TLS connection reused</b> to mail.example.com[ipaddr]:25:
TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)
&nbsp;
Untrusted <b>TLS connection reused</b> to mail.example.com[ipaddr]:25:
TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)
</pre>
</blockquote>
<p> Postfix logs certificate verification failures with a level of
detail that is different for a new or reused TLS session. </p>
<ul>
<li> <p> A new TLS session is logged with certificate verification
failure details: </p>
<pre>
TLSRPT: status=failure, domain=example.org, receiving_mx=mail.example.org[ipaddr],
<b>failure_type=validation_failure</b>, <b>failure_reason=self-signed_certificate</b>
</pre>
<li> <p> A reused TLS session is indicated as shown below, and has
no certificate verification details: </p>
<pre>
mail.example.org[ipaddr]:25: <b>re-using session</b> with untrusted peer
credential, look for details earlier in the log
TLSRPT: status=failure, domain=example.org, receiving_mx=mail.example.org[ipaddr],
<b>failure_type=certificate_not_trusted</b>
</pre>
</ul>
<p> Some Postfix users may wonder where the difference comes from.
So this is why. </p>
<h2> <a name="delivering"> Delivering TLSRPT summaries via email</a> </h2>
<p> RFC 8460 suggests not to enforce strict TLS security when sending
daily success/failure summaries via email, to avoid delivery delays
caused by a failure to enforce TLS security. Postfix currently does
not have a mechanism to disable TLS security enforcement when
submitting an email message; this section provides a workaround. </p>
<p> By design, TLSRPT is not a real-time notification system; it
takes on average 12 hours before a failure is reported in a daily
success/failure summary. If a TLS-related delay of a day or more
is undesirable, one could set up a transport map to make TLS security
optional for specific TLSRPT email notification email addresses.
</p>
<blockquote>
<pre>
/etc/postfix/main.cf:
transport_maps = hash:/etc/postfix/transport
&nbsp
/etc/postfix/transport:
smtp-tls-report@example.com allow-plaintext:
...
&nbsp
/etc/postfix/master.cf:
# service name type private unpriv chroot wakeup maxproc command
allow-plaintext unix - - n - - smtp
-o smtp_tls_security_level=may
-o smtp_tls_policy_maps=static:may
</pre>
</blockquote>
<h2> <a name="mta-sts"> MTA-STS Support via smtp_tls_policy_maps
</a></h2>
<p> Postfix supports MTA-STS though an smtp_tls_policy_maps
policy plugin. Postfix 3.10 and later expect a policy response with
the usual security level and matching requirements, plus any
applicable name=value attributes described below. Specify <tt>{
name = value }</tt> when a value may contain whitespace. </p>
<blockquote>
<p> Note 1: Postfix 3.10 and later will accept these attributes in
an MTA-STS response even if TLSRPT support is disabled (at build
time or run time). With TLSRPT support turned off, Postfix
will use the <tt>ttl</tt> and <tt>policy_failure</tt> attributes,
and will ignore the attributes that are used only for TLSRPT. </p>
<p> Note 2: It is an error to specify these attributes for a non-STS
policy. </p>
</blockquote>
<p> The examples in the table apply to the MTA-STS policy example
given in https://datatracker.ietf.org/doc/html/rfc8460#section-4.5.
<p>
<ul>
<li> <p> <tt> policy_type=<i>type</i> </tt>
<p> Specify <tt>sts</tt> or <tt>no-policy-found</tt>. </p> </li>
<li> <p> <tt> policy_domain=<i>name</i> </tt> </p>
<p> The domain that the MTA-STS policy applies to. </p> </li>
<li> <p> <tt> policy_ttl=<i>time</i> </tt> </p>
<p> How long (in seconds) a Postfix SMTP client process will cache
the MTA-STS plugin response. </p> </li>
<li> <p> <tt> { policy_string = <i>value</i> } </tt> </p>
<p> Specify one <tt>policy_string</tt> instance for each MTA-STS
policy feature, enclosed inside "{" and "}" to protect whitespace
in attribute values. </p>
<p> Example: </p>
<blockquote>
<pre>
{ policy_string = version: STSv1 } { policy_string = mode: testing } ...
</pre>
</blockquote>
<p> This form ignores whitespace after the opening "{", around the "=",
and before the closing "}".</p> </li>
<li> <p> <tt> mx_host_pattern=<i>pattern</i> </tt> </p>
<p> Specify one <tt>mx_host_pattern</tt> instance for each "mx:" feature
in the MTA-STS policy. </p>
<p> Example: </p>
<blockquote>
<pre>
mx_host_pattern=mx1.example.com mx_host_pattern=mx2.example.com ...
</pre>
</blockquote>
</li>
<li> <p> <tt> policy_failure=<i>type</i> </tt> </p>
<p> If specified, forces MTA-STS policy enforcement to fail with
the indicated error, even if a server certificate would satisfy
conventional PKI constraints. </p>
<p> Valid errors are <tt>sts-policy-fetch-error, sts-policy-invalid</tt>,
<tt>sts-webpki-invalid</tt>, or the less informative
<tt>validation-failure</tt>. </p>
<p> Example: </p>
<blockquote>
<pre>
policy_failure=sts-webpki-invalid
</pre>
</blockquote>
</li>
</ul>
<h2> <a name="limitations"> Limitations </a></h2>
<p> The Postfix TLSRPT implementation reports at most one final TLS
handshake status (either 'success' or 'failure') per connection.
Postfix TLSRPT cannot report a failure and then later report a final
status of 'success' for that same connection. The reason is that
it's too complicated to filter TLS errors and to report error details
from the TLS engine back to the SMTP protocol engine. It just is
not how Postfix works internally. </p>
<p> The Postfix TLSRPT implementation reports only TLS handshake
success or failure. It does not report failure to connect, or
connections that break after a successful TLS handshake. </p>
<h2> <a name="credits"> Credits </a> </h2>
<ul>
<li> The TLSRPT client library and report generator are implemented
and maintained by sys4. </li>
<li> Wietse Venema implemented the integration with Postfix.
</li>
</ul>
</body>
</html>

View File

@ -19408,3 +19408,22 @@ announce 8BITMIME support, or when a message line exceeds the SMTP
length limit. </p>
<p> This feature is available in Postfix &ge; 3.9. </p>
%PARAM smtp_tlsrpt_enable no
<p> Enable support for RFC 8460 TLSRPT notifications. A mail receiving
domain can publish a TLSRPT policy in DNS, to request periodic
summaries of successful and failed SMTP over TLS connections to
their mail servers. This feature requires that Postfix is built
with a TLSRPT supporting library. </p>
<p> This feature is available in Postfix &ge; 3.10. </p>
%PARAM smtp_tlsrpt_socket_name
<p> The pathname of a UNIX-domain datagram socket that is managed
by a local TLSRPT reporting service. This parameter must specify a
pathname (absolute, or relative to $queue_directory) when
"smtp_tlsrpt_enable = yes". </p>
<p> This feature is available in Postfix &ge; 3.10. </p>

View File

@ -1623,3 +1623,31 @@ KEMs
kex
keyshare
pkg
RPT
TLSRPT
TLSRPTv
TODOS
WSP
addv
bugprone
errnum
libtlsrpt
munge
mystrerror
protcol
punycode
pval
rpt
rua
sockname
tlsproxied
tlsrpt
trw
datagram
RPC
datatracker
webpki
parsable
mailto
ipaddr
STS

View File

@ -335,3 +335,10 @@ length length of 0 31 0 127
address address string length
whether the standard End of DATA sequence CRLF CRLF is required and
Require CRLF CRLF
must start with a version field v TLSRPTv1 followed by WSP WSP
policies policy policy type
policies policy policy string Ignored if the tls_policy_type
policies policy policy domain
additional_info additional_info
ignored ignored
USE_TLSRPT USE_TLSRPT

View File

@ -358,3 +358,8 @@ expected to become a list of comma separated names br br This
Postfix Postfix can use MongoDB as a source for any of its lookups aliases 5 virtual 5 canonical 5 etc This allows you to keep information for your mail service in a replicated noSQL database with fine grained access controls By not storing it
CCARGS CCARGS DHAS_MONGODB I usr include libmongoc 1 0
dt dt dd 2 Also enable verbose logging in the Postfix TLS
Postfix Postfix legacy TLS Support
var run tlsrpt tlsrpt sock Relative names will work with and without Postfix chroot support Do not specify a location under a directory such as private or public that is already used by Postfix programs Only Postfix programs should create
Note the recommended socket location is still to be determined A good socket location would be under the Postfix queue directory for example smtp_tlsrpt_socket_name run tlsrpt tlsrpt sock The advantage of using a relative name is that
with cipher ECDHE RSA AES256 GCM SHA384 256 256 bits
TLSv1 2 with cipher ECDHE RSA AES256 GCM SHA384 256 256 bits

View File

@ -1841,4 +1841,9 @@ foqvx
ILP
xxfi
optionsv
rcv
snd
sts
tlsrprt
bdefhnoqv
deduplicated

View File

@ -379,3 +379,11 @@ Dextrous
ar
liveness
superset
ltlsrpt
sts
STS
STSv
Sys
Qsmtp
Qsts
gmail

View File

@ -221,6 +221,7 @@ extern int dns_rr_compare_pref_any(DNS_RR *, DNS_RR *);
extern int dns_rr_compare_pref(DNS_RR *, DNS_RR *);
extern DNS_RR *dns_rr_shuffle(DNS_RR *);
extern DNS_RR *dns_rr_remove(DNS_RR *, DNS_RR *);
extern DNS_RR *dns_rr_detach(DNS_RR *, DNS_RR *);
extern int var_dns_rr_list_limit;
/*

View File

@ -52,6 +52,10 @@
/* DNS_RR *list;
/* DNS_RR *record;
/*
/* DNS_RR *dns_rr_detach(list, record)
/* DNS_RR *list;
/* DNS_RR *record;
/*
/* DNS_RR *dns_srv_rr_sort(list)
/* DNS_RR *list;
/*
@ -118,10 +122,15 @@
/*
/* dns_rr_shuffle() randomly permutes a list of resource records.
/*
/* dns_rr_remove() removes the specified record from the specified list.
/* dns_rr_remove() disconnects the specified record from the
/* specified list and destroys it.
/* The updated list is the result value.
/* The record MUST be a list member.
/*
/* dns_rr_detach() disconnects the specified record from the
/* specified list. The updated list is the result value.
/* The record MUST be a list member.
/*
/* dns_srv_rr_sort() sorts a list of SRV records according to
/* their priority and weight as described in RFC 2782.
/* LICENSE
@ -464,16 +473,24 @@ DNS_RR *dns_rr_shuffle(DNS_RR *list)
/* dns_rr_remove - remove record from list, return new list */
DNS_RR *dns_rr_remove(DNS_RR *list, DNS_RR *record)
{
list = dns_rr_detach(list, record);
dns_rr_free(record);
return (list);
}
/* dns_rr_detach - detach record from list, return new list */
DNS_RR *dns_rr_detach(DNS_RR *list, DNS_RR *record)
{
if (list == 0)
msg_panic("dns_rr_remove: record not found");
msg_panic("dns_rr_detach: record not found");
if (list == record) {
list = record->next;
record->next = 0;
dns_rr_free(record);
} else {
list->next = dns_rr_remove(list->next, record);
list->next = dns_rr_detach(list->next, record);
}
return (list);
}

View File

@ -361,6 +361,93 @@ static int append_to_elem_from_list_exact_fit(void)
return (eq_dns_rr_free(got, want));
}
static int delete_middle_element(void)
{
DNS_RR *a = dns_rr_create_noport("qa", "ra", T_MX, C_IN, 3600, 1, "mxa", 4);
DNS_RR *b = dns_rr_create_noport("qb", "rb", T_MX, C_IN, 3600, 1, "mxb", 4);
DNS_RR *c = dns_rr_create_noport("qc", "rc", T_MX, C_IN, 3600, 1, "mxc", 4);
DNS_RR *got, *want, *list;
((list = a)->next = b)->next = c;
(want = dns_rr_copy(a))->next = dns_rr_copy(c);
got = dns_rr_remove(list, b);
return (eq_dns_rr_free(got, want));
}
static int delete_first_element(void)
{
DNS_RR *a = dns_rr_create_noport("qa", "ra", T_MX, C_IN, 3600, 1, "mxa", 4);
DNS_RR *b = dns_rr_create_noport("qb", "rb", T_MX, C_IN, 3600, 1, "mxb", 4);
DNS_RR *c = dns_rr_create_noport("qc", "rc", T_MX, C_IN, 3600, 1, "mxc", 4);
DNS_RR *got, *want, *list;
((list = a)->next = b)->next = c;
(want = dns_rr_copy(b))->next = dns_rr_copy(c);
got = dns_rr_remove(list, a);
return (eq_dns_rr_free(got, want));
}
static int delete_last_element(void)
{
DNS_RR *a = dns_rr_create_noport("qa", "ra", T_MX, C_IN, 3600, 1, "mxa", 4);
DNS_RR *b = dns_rr_create_noport("qb", "rb", T_MX, C_IN, 3600, 1, "mxb", 4);
DNS_RR *c = dns_rr_create_noport("qc", "rc", T_MX, C_IN, 3600, 1, "mxc", 4);
DNS_RR *got, *want, *list;
((list = a)->next = b)->next = c;
(want = dns_rr_copy(a))->next = dns_rr_copy(b);
got = dns_rr_remove(list, c);
return (eq_dns_rr_free(got, want));
}
static int detach_middle_element(void)
{
DNS_RR *a = dns_rr_create_noport("qa", "ra", T_MX, C_IN, 3600, 1, "mxa", 4);
DNS_RR *b = dns_rr_create_noport("qb", "rb", T_MX, C_IN, 3600, 1, "mxb", 4);
DNS_RR *c = dns_rr_create_noport("qc", "rc", T_MX, C_IN, 3600, 1, "mxc", 4);
DNS_RR *got, *want, *list;
((list = a)->next = b)->next = c;
(want = dns_rr_copy(a))->next = dns_rr_copy(c);
got = dns_rr_detach(list, b);
dns_rr_free(b);
return (eq_dns_rr_free(got, want));
}
static int detach_first_element(void)
{
DNS_RR *a = dns_rr_create_noport("qa", "ra", T_MX, C_IN, 3600, 1, "mxa", 4);
DNS_RR *b = dns_rr_create_noport("qb", "rb", T_MX, C_IN, 3600, 1, "mxb", 4);
DNS_RR *c = dns_rr_create_noport("qc", "rc", T_MX, C_IN, 3600, 1, "mxc", 4);
DNS_RR *got, *want, *list;
((list = a)->next = b)->next = c;
(want = dns_rr_copy(b))->next = dns_rr_copy(c);
got = dns_rr_detach(list, a);
dns_rr_free(a);
return (eq_dns_rr_free(got, want));
}
static int detach_last_element(void)
{
DNS_RR *a = dns_rr_create_noport("qa", "ra", T_MX, C_IN, 3600, 1, "mxa", 4);
DNS_RR *b = dns_rr_create_noport("qb", "rb", T_MX, C_IN, 3600, 1, "mxb", 4);
DNS_RR *c = dns_rr_create_noport("qc", "rc", T_MX, C_IN, 3600, 1, "mxc", 4);
DNS_RR *got, *want, *list;
((list = a)->next = b)->next = c;
(want = dns_rr_copy(a))->next = dns_rr_copy(b);
got = dns_rr_detach(list, c);
dns_rr_free(c);
return (eq_dns_rr_free(got, want));
}
/*
* The test cases.
*/
@ -400,9 +487,15 @@ static const TEST_CASE test_cases[] = {
"append to element from list exact fit", append_to_elem_from_list_exact_fit,
/*
* TODO: tests dns_rr_sort(), dns_rr_srv_sort(), dns_rr_remove(),
* dns_rr_shuffle(), etc.
* TODO: tests for dns_rr_sort(), dns_rr_srv_sort(), dns_rr_shuffle(),
* etc.
*/
"delete element from list (middle)", delete_middle_element,
"delete element from list (first)", delete_first_element,
"delete element from list (last)", delete_last_element,
"detach element from list (middle)", detach_middle_element,
"detach element from list (first)", detach_first_element,
"detach element from list (last)", detach_last_element,
0,
};

View File

@ -250,15 +250,12 @@ static int haproxy_srvr_parse_addr(const char *str, MAI_HOSTADDR_STR *addr,
}
if (err == 0)
err = (hostaddr_to_sockaddr(str, (char *) 0, 0, &res)
|| sockaddr_to_hostaddr(res->ai_addr, res->ai_addrlen,
|| sane_sockaddr_to_hostaddr(res->ai_addr, res->ai_addrlen,
addr, (MAI_SERVPORT_STR *) 0, 0));
if (res)
freeaddrinfo(res);
if (err)
return (-1);
if (addr->buf[0] == ':' && strncasecmp("::ffff:", addr->buf, 7) == 0
&& strchr((char *) proto_info->sa_family_list, AF_INET) != 0)
memmove(addr->buf, addr->buf + 7, strlen(addr->buf) + 1 - 7);
return (0);
}

View File

@ -4459,6 +4459,22 @@ extern bool var_ign_srv_lookup_err;
#define DEF_ALLOW_SRV_FALLBACK 0
extern bool var_allow_srv_fallback;
/*
* TLSRPT notification support. The lmtp_ names must be defined because the
* build system enforces that every smtp_ parameter has an lmtp_ variant.
*/
#define VAR_SMTP_TLSRPT_ENABLE "smtp_tlsrpt_enable"
#define DEF_SMTP_TLSRPT_ENABLE "no"
#define VAR_LMTP_TLSRPT_ENABLE "lmtp_tlsrpt_enable"
#define DEF_LMTP_TLSRPT_ENABLE DEF_SMTP_TLSRPT_ENABLE
extern bool var_smtp_tlsrpt_enable;
#define VAR_SMTP_TLSRPT_SOCKNAME "smtp_tlsrpt_socket_name"
#define DEF_SMTP_TLSRPT_SOCKNAME ""
#define VAR_LMTP_TLSRPT_SOCKNAME "lmtp_tlsrpt_socket_name"
#define DEF_LMTP_TLSRPT_SOCKNAME DEF_SMTP_TLSRPT_SOCKNAME
extern char *var_smtp_tlsrpt_sockname;
/* LICENSE
/* .ad
/* .fi
@ -4473,6 +4489,9 @@ extern bool var_allow_srv_fallback;
/* Google, Inc.
/* 111 8th Avenue
/* New York, NY 10011, USA
/*
/* Wietse Venema
/* porcupine.org
/*--*/
#endif

View File

@ -20,7 +20,7 @@
* Patches change both the patchlevel and the release date. Snapshots have no
* patchlevel; they change the release date only.
*/
#define MAIL_RELEASE_DATE "20240924"
#define MAIL_RELEASE_DATE "20240926"
#define MAIL_VERSION_NUMBER "3.10"
#ifdef SNAPSHOT

View File

@ -35,6 +35,8 @@
/* This feature is available in Postfix 2.7 and later.
/* .IP \fB-f\fR
/* Prepend the file name to each output line.
/* .sp
/* This feature is available in Postfix 3.10 and later.
/* .IP \fB-h\fR
/* Show message header content. The \fB-h\fR option produces
/* output from the beginning of the message up to, but not

View File

@ -110,25 +110,6 @@
static const INET_PROTO_INFO *proto_info;
/* psc_sockaddr_to_hostaddr - transform endpoint address and port to string */
static int psc_sockaddr_to_hostaddr(struct sockaddr *addr_storage,
SOCKADDR_SIZE addr_storage_len,
MAI_HOSTADDR_STR *addr_buf,
MAI_SERVPORT_STR *port_buf,
int socktype)
{
int aierr;
if ((aierr = sockaddr_to_hostaddr(addr_storage, addr_storage_len,
addr_buf, port_buf, socktype)) == 0
&& strncasecmp("::ffff:", addr_buf->buf, 7) == 0
&& strchr((char *) proto_info->sa_family_list, AF_INET) != 0)
memmove(addr_buf->buf, addr_buf->buf + 7,
sizeof(addr_buf->buf) - 7);
return (aierr);
}
/* psc_endpt_local_lookup - look up local system connection information */
void psc_endpt_local_lookup(VSTREAM *smtp_client_stream,
@ -156,7 +137,7 @@ void psc_endpt_local_lookup(VSTREAM *smtp_client_stream,
* Convert the remote SMTP client address and port to printable form for
* logging and access control.
*/
else if ((aierr = psc_sockaddr_to_hostaddr(
else if ((aierr = sane_sockaddr_to_hostaddr(
(struct sockaddr *) &addr_storage,
addr_storage_len, &smtp_client_addr,
&smtp_client_port, SOCK_STREAM)) != 0) {
@ -180,7 +161,7 @@ void psc_endpt_local_lookup(VSTREAM *smtp_client_stream,
* Convert the local SMTP server address and port to printable form for
* logging.
*/
else if ((aierr = psc_sockaddr_to_hostaddr(
else if ((aierr = sane_sockaddr_to_hostaddr(
(struct sockaddr *) &addr_storage,
addr_storage_len, &smtp_server_addr,
&smtp_server_port, SOCK_STREAM)) != 0) {

View File

@ -835,6 +835,8 @@ static int starttls(STATE *state)
= vstring_str(cipher_exclusions),
matchargv = state->match,
mdalg = state->mdalg,
tlsrpt = 0,
ffail_type = 0,
dane = state->ddane ?
state->ddane : state->dane);
@ -939,6 +941,8 @@ static int starttls(STATE *state)
= vstring_str(cipher_exclusions),
matchargv = state->match,
mdalg = state->mdalg,
tlsrpt = 0,
ffail_type = 0,
dane = state->ddane ? state->ddane : state->dane);
} /* tlsproxy_mode */
vstring_free(cipher_exclusions);

View File

@ -2,11 +2,11 @@ SHELL = /bin/sh
SRCS = smtp.c smtp_connect.c smtp_proto.c smtp_chat.c smtp_session.c \
smtp_addr.c smtp_trouble.c smtp_state.c smtp_rcpt.c smtp_tls_policy.c \
smtp_sasl_proto.c smtp_sasl_glue.c smtp_reuse.c smtp_map11.c \
smtp_sasl_auth_cache.c smtp_key.c smtp_misc.c
smtp_sasl_auth_cache.c smtp_key.c smtp_misc.c smtp_tlsrpt.c
OBJS = smtp.o smtp_connect.o smtp_proto.o smtp_chat.o smtp_session.o \
smtp_addr.o smtp_trouble.o smtp_state.o smtp_rcpt.o smtp_tls_policy.o \
smtp_sasl_proto.o smtp_sasl_glue.o smtp_reuse.o smtp_map11.o \
smtp_sasl_auth_cache.o smtp_key.o smtp_misc.o
smtp_sasl_auth_cache.o smtp_key.o smtp_misc.o smtp_tlsrpt.o
HDRS = smtp.h smtp_sasl.h smtp_addr.h smtp_reuse.h smtp_sasl_auth_cache.h
TESTSRC =
DEFS = -I. -I$(INC_DIR) -D$(SYSTYPE)
@ -267,6 +267,7 @@ smtp_connect.o: ../../include/timed_connect.h
smtp_connect.o: ../../include/tls.h
smtp_connect.o: ../../include/tls_proxy.h
smtp_connect.o: ../../include/tok822.h
smtp_connect.o: ../../include/valid_hostname.h
smtp_connect.o: ../../include/vbuf.h
smtp_connect.o: ../../include/vstream.h
smtp_connect.o: ../../include/vstring.h
@ -447,6 +448,7 @@ smtp_proto.o: ../../include/stringops.h
smtp_proto.o: ../../include/sys_defs.h
smtp_proto.o: ../../include/tls.h
smtp_proto.o: ../../include/tls_proxy.h
smtp_proto.o: ../../include/tlsrpt_wrapper.h
smtp_proto.o: ../../include/tok822.h
smtp_proto.o: ../../include/uxtext.h
smtp_proto.o: ../../include/vbuf.h
@ -737,6 +739,7 @@ smtp_state.o: ../../include/string_list.h
smtp_state.o: ../../include/sys_defs.h
smtp_state.o: ../../include/tls.h
smtp_state.o: ../../include/tls_proxy.h
smtp_state.o: ../../include/tlsrpt_wrapper.h
smtp_state.o: ../../include/tok822.h
smtp_state.o: ../../include/vbuf.h
smtp_state.o: ../../include/vstream.h
@ -770,6 +773,7 @@ smtp_tls_policy.o: ../../include/name_mask.h
smtp_tls_policy.o: ../../include/nvtable.h
smtp_tls_policy.o: ../../include/recipient_list.h
smtp_tls_policy.o: ../../include/resolve_clnt.h
smtp_tls_policy.o: ../../include/sane_strtol.h
smtp_tls_policy.o: ../../include/scache.h
smtp_tls_policy.o: ../../include/sock_addr.h
smtp_tls_policy.o: ../../include/string_list.h
@ -777,6 +781,7 @@ smtp_tls_policy.o: ../../include/stringops.h
smtp_tls_policy.o: ../../include/sys_defs.h
smtp_tls_policy.o: ../../include/tls.h
smtp_tls_policy.o: ../../include/tls_proxy.h
smtp_tls_policy.o: ../../include/tlsrpt_wrapper.h
smtp_tls_policy.o: ../../include/tok822.h
smtp_tls_policy.o: ../../include/valid_hostname.h
smtp_tls_policy.o: ../../include/valid_utf8_hostname.h
@ -785,6 +790,48 @@ smtp_tls_policy.o: ../../include/vstream.h
smtp_tls_policy.o: ../../include/vstring.h
smtp_tls_policy.o: smtp.h
smtp_tls_policy.o: smtp_tls_policy.c
smtp_tlsrpt.o: ../../include/argv.h
smtp_tlsrpt.o: ../../include/attr.h
smtp_tlsrpt.o: ../../include/check_arg.h
smtp_tlsrpt.o: ../../include/deliver_request.h
smtp_tlsrpt.o: ../../include/dict.h
smtp_tlsrpt.o: ../../include/dns.h
smtp_tlsrpt.o: ../../include/dsn.h
smtp_tlsrpt.o: ../../include/dsn_buf.h
smtp_tlsrpt.o: ../../include/header_body_checks.h
smtp_tlsrpt.o: ../../include/header_opts.h
smtp_tlsrpt.o: ../../include/hex_code.h
smtp_tlsrpt.o: ../../include/htable.h
smtp_tlsrpt.o: ../../include/inet_proto.h
smtp_tlsrpt.o: ../../include/mail_params.h
smtp_tlsrpt.o: ../../include/maps.h
smtp_tlsrpt.o: ../../include/match_list.h
smtp_tlsrpt.o: ../../include/midna_domain.h
smtp_tlsrpt.o: ../../include/mime_state.h
smtp_tlsrpt.o: ../../include/msg.h
smtp_tlsrpt.o: ../../include/msg_stats.h
smtp_tlsrpt.o: ../../include/myaddrinfo.h
smtp_tlsrpt.o: ../../include/myflock.h
smtp_tlsrpt.o: ../../include/mymalloc.h
smtp_tlsrpt.o: ../../include/name_code.h
smtp_tlsrpt.o: ../../include/name_mask.h
smtp_tlsrpt.o: ../../include/nvtable.h
smtp_tlsrpt.o: ../../include/recipient_list.h
smtp_tlsrpt.o: ../../include/resolve_clnt.h
smtp_tlsrpt.o: ../../include/scache.h
smtp_tlsrpt.o: ../../include/sock_addr.h
smtp_tlsrpt.o: ../../include/string_list.h
smtp_tlsrpt.o: ../../include/stringops.h
smtp_tlsrpt.o: ../../include/sys_defs.h
smtp_tlsrpt.o: ../../include/tls.h
smtp_tlsrpt.o: ../../include/tls_proxy.h
smtp_tlsrpt.o: ../../include/tlsrpt_wrapper.h
smtp_tlsrpt.o: ../../include/tok822.h
smtp_tlsrpt.o: ../../include/vbuf.h
smtp_tlsrpt.o: ../../include/vstream.h
smtp_tlsrpt.o: ../../include/vstring.h
smtp_tlsrpt.o: smtp.h
smtp_tlsrpt.o: smtp_tlsrpt.c
smtp_trouble.o: ../../include/argv.h
smtp_trouble.o: ../../include/attr.h
smtp_trouble.o: ../../include/bounce.h

View File

@ -67,6 +67,7 @@
VAR_TLSPROXY_SERVICE, DEF_TLSPROXY_SERVICE, &var_tlsproxy_service, 1, 0,
VAR_HFROM_FORMAT, DEF_HFROM_FORMAT, &var_hfrom_format, 1, 0,
VAR_USE_SRV_LOOKUP, DEF_USE_SRV_LOOKUP, &var_use_srv_lookup, 0, 0,
VAR_LMTP_TLSRPT_SOCKNAME, DEF_LMTP_TLSRPT_SOCKNAME, &var_smtp_tlsrpt_sockname, 0, 0,
0,
};
static const CONFIG_TIME_TABLE lmtp_time_table[] = {
@ -137,5 +138,6 @@
};
static const CONFIG_NBOOL_TABLE lmtp_nbool_table[] = {
VAR_LMTP_REQ_DEADLINE, DEF_LMTP_REQ_DEADLINE, &var_smtp_req_deadline,
VAR_LMTP_TLSRPT_ENABLE, DEF_LMTP_TLSRPT_ENABLE, &var_smtp_tlsrpt_enable,
0,
};

View File

@ -638,6 +638,12 @@
/* .IP "\fBsmtp_tls_enable_rpk (no)\fR"
/* Request that remote SMTP servers send an RFC7250 raw public key
/* instead of an X.509 certificate.
/* .PP Available in Postfix version 3.10 and later:
/* .IP "\fBsmtp_tlsrpt_enable (no)\fR"
/* Enable support for RFC 8460 TLSRPT notifications.
/* .IP "\fBsmtp_tlsrpt_socket_name (empty)\fR"
/* The pathname of a UNIX-domain datagram socket that is managed
/* by a local TLSRPT reporting service.
/* OBSOLETE STARTTLS CONTROLS
/* .ad
/* .fi
@ -1146,6 +1152,8 @@ int var_smtp_min_data_rate;
char *var_use_srv_lookup;
bool var_ign_srv_lookup_err;
bool var_allow_srv_fallback;
bool var_smtp_tlsrpt_enable;
char *var_smtp_tlsrpt_sockname;
/* Special handling of 535 AUTH errors. */
char *var_smtp_sasl_auth_cache_name;
@ -1399,7 +1407,23 @@ static void post_init(char *unused_name, char **argv)
var_disable_dns = (smtp_dns_support == SMTP_DNS_DISABLED);
}
#if !defined(USE_TLS) || !defined(USE_TLSRPT)
if (var_smtp_tlsrpt_enable)
msg_warn("TLSRPT is selected, but TLSRPT is not compiled in");
#endif
#ifdef USE_TLS
#ifdef USE_TLSRPT
if (var_smtp_tlsrpt_enable) {
if (smtp_mode) {
if (smtp_tlsrpt_post_jail(VAR_SMTP_TLSRPT_SOCKNAME,
var_smtp_tlsrpt_sockname) < 0)
var_smtp_tlsrpt_enable = 0;
} else {
msg_warn("TLSRPT support is not implemented for LMTP");
var_smtp_tlsrpt_enable = 0;
}
}
#endif /* USE_TLSRPT */
if (smtp_mode) {
smtp_tls_insecure_mx_policy =
tls_level_lookup(var_smtp_tls_insecure_mx_policy);

View File

@ -108,8 +108,28 @@ typedef struct SMTP_TLS_POLICY {
char *sni; /* Optional SNI name when not DANE */
int conn_reuse; /* enable connection reuse */
int enable_rpk; /* Enable server->client RPK */
/* External policy info, for TLSRPT. */
int ext_policy_ttl; /* TTL from DNS etc. */
char *ext_policy_type; /* (sts) */
ARGV *ext_policy_strings; /* policy strings from DNS etc. */
char *ext_policy_domain; /* policy scope */
ARGV *ext_mx_host_patterns; /* (sts) MX host patterns */
char *ext_policy_failure; /* (sts) policy failure */
} SMTP_TLS_POLICY;
/*
* Names and values for external policy attributes in smtp_tls_policy_maps.
* These are not #ifdef USE_TLSRPT, so that a TLSRPT-aware STS plugin can be
* used whether or not Postfix was built with TLSRPT support.
*/
#define EXT_POLICY_TTL "policy_ttl"
#define EXT_POLICY_TTL_UNSET (-1)
#define EXT_POLICY_TYPE "policy_type"
#define EXT_POLICY_DOMAIN "policy_domain"
#define EXT_POLICY_STRING "policy_string"
#define EXT_MX_HOST_PATTERN "mx_host_pattern"
#define EXT_POLICY_FAILURE "policy_failure"
/*
* smtp_tls_policy.c
*/
@ -144,6 +164,12 @@ extern void smtp_tls_policy_cache_flush(void);
_tls_policy_init_tmp->sni = 0; \
_tls_policy_init_tmp->conn_reuse = 0; \
_tls_policy_init_tmp->enable_rpk = 0; \
_tls_policy_init_tmp->ext_policy_ttl = EXT_POLICY_TTL_UNSET; \
_tls_policy_init_tmp->ext_policy_type = 0; \
_tls_policy_init_tmp->ext_policy_domain = 0; \
_tls_policy_init_tmp->ext_policy_strings = 0; \
_tls_policy_init_tmp->ext_mx_host_patterns = 0; \
_tls_policy_init_tmp->ext_policy_failure = 0; \
} while (0)
#endif
@ -171,6 +197,9 @@ typedef struct SMTP_STATE {
*/
#ifdef USE_TLS
SMTP_TLS_POLICY tls[1]; /* Usage: state->tls->member */
#ifdef USE_TLSRPT
struct TLSRPT_WRAPPER *tlsrpt;
#endif
#endif
/*
@ -757,6 +786,18 @@ extern void smtp_quote_821_address(VSTRING *, const char *);
*/
extern int smtp_hfrom_format;
/*
* smtp_tlsrpt.c.
*/
#if defined(USE_TLS) && defined(USE_TLSRPT)
extern int smtp_tlsrpt_post_jail(const char *sockname_pname, const char *sockname_pval);
extern void smtp_tlsrpt_create_wrapper(SMTP_STATE *state, const char *domain);
extern void smtp_tlsrpt_set_tls_policy(SMTP_STATE *state);
extern void smtp_tlsrpt_set_tcp_connection(SMTP_STATE *state);
extern void smtp_tlsrpt_set_ehlo_resp(SMTP_STATE *, const char *ehlo_resp);
#endif /* USE_TLSRPT && USE_TLS */
/* LICENSE
/* .ad
/* .fi

View File

@ -104,6 +104,7 @@
#include <mail_error.h>
#include <dsn_buf.h>
#include <mail_addr.h>
#include <valid_hostname.h>
/* DNS library. */
@ -911,6 +912,21 @@ static void smtp_connect_inet(SMTP_STATE *state, const char *nexthop,
SMTP_ITER_INIT(iter, dest, NO_HOST, NO_ADDR, port, state);
/*
* TODO(wietse) If the domain publishes a TLSRPT policy, they expect
* that clients use SMTP over TLS. Should we upgrade a TLS security
* level of "may" to "encrypt"? This would disable falling back to
* plaintext, and could break interoperability with receivers that
* crank up security up to 11.
*/
#ifdef USE_TLSRPT
if (smtp_mode && var_smtp_tlsrpt_enable
&& !valid_hostaddr(domain, DONT_GRIPE))
smtp_tlsrpt_create_wrapper(state, domain);
else
state->tlsrpt = 0;
#endif /* USE_TLSRPT */
/*
* Resolve an SMTP or LMTP server. Skip MX or SRV lookups when a
* quoted domain is specified or when DNS lookups are disabled.
@ -1076,6 +1092,18 @@ static void smtp_connect_inet(SMTP_STATE *state, const char *nexthop,
session->state = state;
#ifdef USE_TLS
session->tls_nexthop = domain;
/*
* Update TLSRPT state even if this is a reused SMTP
* connection. If for some unlikely reason we must report a
* problem, then we must report correct information.
*/
#ifdef USE_TLSRPT
if (state->tlsrpt && state->tls->level > TLS_LEV_NONE) {
smtp_tlsrpt_set_tls_policy(state);
smtp_tlsrpt_set_tcp_connection(state);
}
#endif /* USE_TLSRPT */
#endif
if (addr->pref == domain_best_pref)
session->features |= SMTP_FEATURE_BEST_MX;

View File

@ -68,6 +68,7 @@
VAR_TLSPROXY_SERVICE, DEF_TLSPROXY_SERVICE, &var_tlsproxy_service, 1, 0,
VAR_HFROM_FORMAT, DEF_HFROM_FORMAT, &var_hfrom_format, 1, 0,
VAR_USE_SRV_LOOKUP, DEF_USE_SRV_LOOKUP, &var_use_srv_lookup, 0, 0,
VAR_SMTP_TLSRPT_SOCKNAME, DEF_SMTP_TLSRPT_SOCKNAME, &var_smtp_tlsrpt_sockname, 0, 0,
0,
};
static const CONFIG_TIME_TABLE smtp_time_table[] = {
@ -141,5 +142,6 @@
};
static const CONFIG_NBOOL_TABLE smtp_nbool_table[] = {
VAR_SMTP_REQ_DEADLINE, DEF_SMTP_REQ_DEADLINE, &var_smtp_req_deadline,
VAR_SMTP_TLSRPT_ENABLE, DEF_SMTP_TLSRPT_ENABLE, &var_smtp_tlsrpt_enable,
0,
};

View File

@ -78,6 +78,9 @@
/* 111 8th Avenue
/* New York, NY 10011, USA
/*
/* Wietse Venema
/* porcupine.org
/*
/* Pipelining code in cooperation with:
/* Jon Ribbens
/* Oaktree Internet Solutions Ltd.,
@ -155,6 +158,9 @@
#include <xtext.h>
#include <uxtext.h>
#include <smtputf8.h>
#if defined(USE_TLS) && defined(USE_TLSRPT)
#include <tlsrpt_wrapper.h>
#endif
/* Application-specific. */
@ -475,6 +481,11 @@ int smtp_helo(SMTP_STATE *state)
else
session->features &= ~SMTP_FEATURE_ESMTP;
}
#ifdef USE_TLSRPT
if (state->tlsrpt
&& (state->misc_flags & SMTP_MISC_FLAG_IN_STARTTLS) == 0)
smtp_tlsrpt_set_ehlo_resp(state, resp->str);
#endif
}
if ((session->features & SMTP_FEATURE_ESMTP) == 0) {
where = "performing the HELO handshake";
@ -484,6 +495,10 @@ int smtp_helo(SMTP_STATE *state)
"host %s refused to talk to me: %s",
session->namaddr,
translit(resp->str, "\n", " ")));
#ifdef USE_TLSRPT
if (state->tlsrpt)
trw_set_ehlo_resp(state->tlsrpt, resp->str);
#endif
}
} else {
where = "performing the LHLO handshake";
@ -798,11 +813,19 @@ int smtp_helo(SMTP_STATE *state)
* although support for it was announced in the EHLO response.
*/
session->features &= ~SMTP_FEATURE_STARTTLS;
if (TLS_REQUIRED(state->tls->level))
if (TLS_REQUIRED(state->tls->level)) {
#ifdef USE_TLSRPT
if (state->tlsrpt)
trw_report_failure(state->tlsrpt,
TLSRPT_STARTTLS_NOT_SUPPORTED,
/* additional_info= */ (char *) 0,
/* failure_reason= */ (char *) 0);
#endif
return (smtp_site_fail(state, STR(iter->host), resp,
"TLS is required, but host %s refused to start TLS: %s",
session->namaddr,
translit(resp->str, "\n", " ")));
}
/* Else try to continue in plain-text mode. */
}
@ -815,6 +838,13 @@ int smtp_helo(SMTP_STATE *state)
*/
if (TLS_REQUIRED(state->tls->level)) {
if (!(session->features & SMTP_FEATURE_STARTTLS)) {
#ifdef USE_TLSRPT
if (state->tlsrpt)
trw_report_failure(state->tlsrpt,
TLSRPT_STARTTLS_NOT_SUPPORTED,
/* additional_info= */ (char *) 0,
/* failure_reason= */ (char *) 0);
#endif
return (smtp_site_fail(state, DSN_BY_LOCAL_MTA,
SMTP_RESP_FAKE(&fake, "4.7.4"),
"TLS is required, but was not offered by host %s",
@ -942,6 +972,12 @@ static int smtp_start_tls(SMTP_STATE *state)
= vstring_str(state->tls->exclusions),
matchargv = state->tls->matchargv,
mdalg = var_smtp_tls_fpt_dgst,
#ifdef USE_TLSRPT
tlsrpt = state->tlsrpt,
#else
tlsrpt = 0,
#endif
ffail_type = 0,
dane = state->tls->dane);
/*
@ -1065,6 +1101,12 @@ static int smtp_start_tls(SMTP_STATE *state)
= vstring_str(state->tls->exclusions),
matchargv = state->tls->matchargv,
mdalg = var_smtp_tls_fpt_dgst,
#ifdef USE_TLSRPT
tlsrpt = state->tlsrpt,
#else
tlsrpt = 0,
#endif
ffail_type = state->tls->ext_policy_failure,
dane = state->tls->dane);
/*
@ -1125,10 +1167,43 @@ static int smtp_start_tls(SMTP_STATE *state)
* we must check that here, and not state->tls->level.
*/
if (TLS_MUST_MATCH(session->tls_context->level))
if (!TLS_CERT_IS_MATCHED(session->tls_context))
if (!TLS_CERT_IS_MATCHED(session->tls_context)) {
#ifdef USE_TLSRPT
/*
* Don't create a TLSRPT 'failure' event here, if the TLS engine
* already reported a more specific reason.
*/
if (state->tlsrpt && session->tls_context->rpt_reported == 0) {
if (!TLS_CERT_IS_TRUSTED(session->tls_context)) {
(void) trw_report_failure(state->tlsrpt,
TLSRPT_CERTIFICATE_NOT_TRUSTED,
/* additional_info= */ (char *) 0,
/* failure_reason= */ (char *) 0);
} else {
(void) trw_report_failure(state->tlsrpt,
TLSRPT_CERTIFICATE_HOST_MISMATCH,
/* additional_info= */ (char *) 0,
/* failure_reason= */ (char *) 0);
}
}
#endif
return (smtp_site_fail(state, DSN_BY_LOCAL_MTA,
SMTP_RESP_FAKE(&fake, "4.7.5"),
"Server certificate not verified"));
}
/*
* Create a TLSRPT 'success' event only if the TLS engine has not created
* TLSRPT event. For example, The TLS engine will create a TLSRPT
* 'failure' event when the TLS handshake was be successful, but the
* security level was downgraded from opportunistic "dane" to
* unauthenticated "encrypt".
*/
#ifdef USE_TLSRPT
if (state->tlsrpt && session->tls_context->rpt_reported == 0)
(void) trw_report_success(state->tlsrpt);
#endif
/*
* At this point we have to re-negotiate the "EHLO" to reget the

View File

@ -50,6 +50,13 @@
#include <mail_params.h>
#include <debug_peer.h>
/*
* TLS library.
*/
#if defined(USE_TLS) && defined(USE_TLSRPT)
#include <tlsrpt_wrapper.h>
#endif
/* Application-specific. */
#include "smtp.h"
@ -73,6 +80,9 @@ SMTP_STATE *smtp_state_alloc(void)
state->iterator->host = vstring_alloc(100);
state->iterator->addr = vstring_alloc(100);
state->iterator->saved_dest = vstring_alloc(100);
#ifdef USE_TLSRPT
state->tlsrpt = 0;
#endif
if (var_smtp_cache_conn) {
state->dest_label = vstring_alloc(10);
state->dest_prop = vstring_alloc(10);
@ -105,6 +115,10 @@ void smtp_state_free(SMTP_STATE *state)
vstring_free(state->iterator->host);
vstring_free(state->iterator->addr);
vstring_free(state->iterator->saved_dest);
#ifdef USE_TLSRPT
if (state->tlsrpt)
trw_free(state->tlsrpt);
#endif
if (state->dest_label)
vstring_free(state->dest_label);
if (state->dest_prop)

View File

@ -102,6 +102,7 @@
#include <msg.h>
#include <mymalloc.h>
#include <vstring.h>
#include <sane_strtol.h>
#include <stringops.h>
#include <valid_hostname.h>
#include <valid_utf8_hostname.h>
@ -113,6 +114,10 @@
#include <maps.h>
#include <dsn_buf.h>
/* TLS library. */
#include <tlsrpt_wrapper.h>
/* DNS library. */
#include <dns.h>
@ -221,15 +226,21 @@ static void tls_policy_lookup_one(SMTP_TLS_POLICY *tls, int *site_level,
{
const char *lookup;
char *policy;
char *saved_policy;
char *saved_policy = 0;
char *tok;
const char *err;
char *name;
char *val;
static VSTRING *cbuf;
char *free_me = 0;
#undef FREE_RETURN
#define FREE_RETURN do { myfree(saved_policy); return; } while (0)
#define FREE_RETURN do { \
if (saved_policy) \
myfree(saved_policy); \
if (free_me) \
myfree(free_me); \
return; \
} while (0)
#define INVALID_RETURN(why, levelp) do { \
MARK_INVALID((why), (levelp)); FREE_RETURN; } while (0)
@ -250,7 +261,7 @@ static void tls_policy_lookup_one(SMTP_TLS_POLICY *tls, int *site_level,
}
saved_policy = policy = mystrdup(lookup);
if ((tok = mystrtok(&policy, CHARS_COMMA_SP)) == 0) {
if ((tok = mystrtokq(&policy, CHARS_COMMA_SP, CHARS_BRACE)) == 0) {
msg_warn("%s: invalid empty policy", WHERE);
INVALID_RETURN(tls->why, site_level);
}
@ -265,7 +276,7 @@ static void tls_policy_lookup_one(SMTP_TLS_POLICY *tls, int *site_level,
* Warn about ignored attributes when TLS is disabled.
*/
if (*site_level < TLS_LEV_MAY) {
while ((tok = mystrtok(&policy, CHARS_COMMA_SP)) != 0)
while ((tok = mystrtokq(&policy, CHARS_COMMA_SP, CHARS_BRACE)) != 0)
msg_warn("%s: ignoring attribute \"%s\" with TLS disabled",
WHERE, tok);
FREE_RETURN;
@ -275,8 +286,12 @@ static void tls_policy_lookup_one(SMTP_TLS_POLICY *tls, int *site_level,
* Errors in attributes may have security consequences, don't ignore
* errors that can degrade security.
*/
while ((tok = mystrtok(&policy, CHARS_COMMA_SP)) != 0) {
if ((err = split_nameval(tok, &name, &val)) != 0) {
while ((tok = mystrtokq(&policy, CHARS_COMMA_SP, CHARS_BRACE)) != 0) {
const char *err;
if ((tok[0] == CHARS_BRACE[0]
&& (err = free_me = extpar(&tok, CHARS_BRACE, EXTPAR_FLAG_STRIP)) != 0)
|| (err = split_nameval(tok, &name, &val)) != 0) {
msg_warn("%s: malformed attribute/value pair \"%s\": %s",
WHERE, tok, err);
INVALID_RETURN(tls->why, site_level);
@ -391,6 +406,7 @@ static void tls_policy_lookup_one(SMTP_TLS_POLICY *tls, int *site_level,
}
continue;
}
/* Last one wins. */
if (!strcasecmp(name, "enable_rpk")) {
/* Ultimately ignored at some security levels */
if (strcasecmp(val, "yes") == 0) {
@ -404,10 +420,98 @@ static void tls_policy_lookup_one(SMTP_TLS_POLICY *tls, int *site_level,
}
continue;
}
/* Only one instance per policy. */
if (!strcasecmp(name, EXT_POLICY_TTL)) {
char *end;
long lval;
if (tls->ext_policy_ttl != EXT_POLICY_TTL_UNSET) {
msg_warn("%s: attribute \"%s\" is specified multiple times",
WHERE, name);
INVALID_RETURN(tls->why, site_level);
}
if (!alldig(val) || ((lval = sane_strtol(val, &end, 10)),
((tls->ext_policy_ttl = lval) != lval))
|| *end != 0) {
msg_warn("%s: attribute \"%s\" has a malformed value: \"%s\"",
WHERE, name, val);
INVALID_RETURN(tls->why, site_level);
}
continue;
}
/* Only one instance per policy. */
if (!strcasecmp(name, EXT_POLICY_TYPE)) {
if (tls->ext_policy_type) {
msg_warn("%s: attribute \"%s\" is specified multiple times",
WHERE, name);
INVALID_RETURN(tls->why, site_level);
}
if (!valid_tlsrpt_policy_type(val)) {
msg_warn("%s: attribute \"%s\" has an unexpected value: \"%s\"",
WHERE, name, val);
INVALID_RETURN(tls->why, site_level);
}
tls->ext_policy_type = mystrdup(val);
continue;
}
/* Only one instance per policy. */
if (!strcasecmp(name, EXT_POLICY_DOMAIN)) {
if (tls->ext_policy_domain) {
msg_warn("%s: attribute \"%s\" is specified multiple times",
WHERE, name);
INVALID_RETURN(tls->why, site_level);
}
if (!valid_hostname(val, DO_GRIPE)) {
msg_warn("%s: attribute \"%s\" has a malformed value: \"%s\"",
WHERE, name, val);
INVALID_RETURN(tls->why, site_level);
}
tls->ext_policy_domain = mystrdup(val);
continue;
}
/* Multiple instances per policy are allowed. */
if (!strcasecmp(name, EXT_POLICY_STRING)) {
if (tls->ext_policy_strings == 0)
tls->ext_policy_strings = argv_alloc(1);
argv_add(tls->ext_policy_strings, val, (char *) 0);
continue;
}
/* Multiple instances per policy are allowed. */
if (!strcasecmp(name, EXT_MX_HOST_PATTERN)) {
if (tls->ext_mx_host_patterns == 0)
tls->ext_mx_host_patterns = argv_alloc(1);
argv_add(tls->ext_mx_host_patterns, val, (char *) 0);
continue;
}
/* Only one instance per policy. */
if (!strcasecmp(name, EXT_POLICY_FAILURE)) {
if (tls->ext_policy_failure != 0) {
msg_warn("%s: attribute \"%s\" is specified multiple times",
WHERE, name);
INVALID_RETURN(tls->why, site_level);
}
if (!valid_tlsrpt_policy_failure(val)) {
msg_warn("%s: attribute \"%s\" has an unexpected value: \"%s\"",
WHERE, name, val);
INVALID_RETURN(tls->why, site_level);
}
tls->ext_policy_failure = mystrdup(val);
continue;
}
msg_warn("%s: invalid attribute name: \"%s\"", WHERE, name);
INVALID_RETURN(tls->why, site_level);
}
if (tls->ext_policy_type == 0) {
if (tls->ext_policy_ttl != EXT_POLICY_TTL_UNSET
|| tls->ext_policy_strings
|| tls->ext_policy_domain || tls->ext_mx_host_patterns
|| tls->ext_policy_failure) {
msg_warn("%s: built-in policy has unexpected attribute "
"policy_ttl, policy_domain, policy_string, "
"mx_host_pattern or policy_failure", WHERE);
INVALID_RETURN(tls->why, site_level);
}
}
FREE_RETURN;
}
@ -707,6 +811,16 @@ static void policy_delete(void *item, void *unused_context)
if (tls->dane)
tls_dane_free(tls->dane);
dsb_free(tls->why);
if (tls->ext_policy_type)
myfree(tls->ext_policy_type);
if (tls->ext_policy_domain)
myfree(tls->ext_policy_domain);
if (tls->ext_policy_strings)
argv_free(tls->ext_policy_strings);
if (tls->ext_mx_host_patterns)
argv_free(tls->ext_mx_host_patterns);
if (tls->ext_policy_failure)
myfree(tls->ext_policy_failure);
myfree((void *) tls);
}

View File

@ -0,0 +1,414 @@
/*++
/* NAME
/* smtp_tlsrpt 3
/* SUMMARY
/* TLSRPT support for the SMTP protocol engine
/* SYNOPSIS
/* #include <smtp_tlsrpt.h>
/*
/* int smtp_tlsrpt_post_jail(
/* const char *sockname_pname,
/* const char *sockname_pval)
/*
/* void smtp_tlsrpt_create_wrapper(
/* SMTP_STATE *state,
/* const char *domain)
/*
/* void smtp_tlsrpt_set_tls_policy(
/* SMTP_STATE *state)
/*
/* void smtp_tlsrpt_set_tcp_connection(
/* SMTP_STATE *state)
/*
/* void smtp_tlsrpt_set_ehlo_resp(
/* SMTP_STATE *state,
/* const char *ehlo_resp)
/* DESCRIPTION
/* This module populates a TLSRPT_WRAPPER object with a)
/* remote TLSRPT policy information, b) remote TLSA or STS policy
/* information, and c) selected SMTP connection information. This
/* object is passed to a TLS protocol engine, which may run in a
/* different process than the SMTP protocol engine. The TLS protocol
/* engine uses the TLSRPT_WRAPPER object to report a TLS handshake
/* error to a TLSRPT library. The SMTP protocol engine uses the
/* object to report a TLS handshake error or success.
/*
/* smtp_tls_post_jail() does configuration sanity checks and returns
/* 0 if successful, i.e. TLSRPT support is properly
/* configured. Otherwise it returns -1 and logs a warning. Arguments:
/* .IP sockname_pname
/* The name of a configuration parameter for the endpoint that
/* is managed by TLSRPT infrastructure. This name is used in a
/* diagnostic message.
/* .IP sockname_pval
/* The value of said parameter.
/* .PP
/* smtp_tlsrpt_create_wrapper() destroys a TLSRPT_WRAPPER referenced
/* by state->tlsrpt, and looks for a TLSRPT policy for the specified
/* domain. If one policy exists, smtp_tlsrpt_create_wrapper()
/* attaches a TLSRPT_WRAPPER instance to state->tlsrpt. Otherwise,
/* state->tlsrpt will be null, and other smtp_tlsrpt_* calls must not
/* be made. The TLSRPT_WRAPPER instance may be reused for different
/* SMTP connections for the same TLSRPT policy domain. Arguments:
/* .IP domain
/* The name of a domain that may publish a TLSRPT policy. An
/* internationalized domain name may be in U-label or A-label form
/* (the U-label form will be converted to A-label internally).
/* .PP
/* smtp_tlsrpt_set_tls_policy() updates the TLSRPT_WRAPPER
/* object with DANE or STS TLS policy information, and clears
/* information that was added with smtp_tlsrpt_set_tcp_connection()
/* or smtp_tlsrpt_set_ehlo_resp().
/* .PP
/* smtp_tlsrpt_set_tcp_connection() updates the TLSRPT_WRAPPER
/* object with TCP connection properties.
/* .PP
/* smtp_tlsrpt_set_ehlo_resp() updates the TLSRPT_WRAPPER object
/* with the SMTP server's EHLO response.
/* BUGS
/* This module inherits all limitations from tlsrpt_wrapper(3).
/* SEE ALSO
/* tlsrpt_wrapper(3) TLSRPT support for the TLS protocol engine.
/* LICENSE
/* .ad
/* .fi
/* The Secure Mailer license must be distributed with this software.
/* AUTHOR(S)
/* Wietse Venema
/* porcupine.org
/*--*/
/*
* System library.
*/
#include <sys_defs.h>
#include <sys/socket.h>
/*
* Utility library.
*/
#include <hex_code.h>
#include <midna_domain.h>
#include <msg.h>
#include <myaddrinfo.h>
#include <name_code.h>
#include <stringops.h>
/*
* Global library.
*/
#include <mail_params.h>
/*
* TLS library.
*/
#include <tls.h>
#include <tlsrpt_wrapper.h>
/*
* Application-specific.
*/
#include <smtp.h>
#if defined(USE_TLS) && defined(USE_TLSRPT)
static const char smtp_tlsrpt_support[] = "TLSRPT support";
/* smtp_tlsrpt_post_jail - post-jail configuration sanity check */
int smtp_tlsrpt_post_jail(const char *sockname_pname,
const char *sockname_pval)
{
if (smtp_dns_support == SMTP_DNS_DISABLED) {
msg_warn("Cannot enable %s: DNS is disabled", smtp_tlsrpt_support);
return (-1);
}
if (*sockname_pval == 0) {
msg_warn("%s: parameter %s has empty value -- %s will be disabled",
smtp_tlsrpt_support, sockname_pname, smtp_tlsrpt_support);
return (-1);
}
return (0);
}
/* smtp_tlsrpt_find_policy - look up TLSRPT policy and verify version ID */
static DNS_RR *smtp_tlsrpt_find_policy(const char *adomain)
{
VSTRING *why = vstring_alloc(100);
VSTRING *qname = vstring_alloc(100);
DNS_RR *rr_list = 0;
DNS_RR *rr_result = 0;
DNS_RR *rr;
DNS_RR *next;
int res_opt = 0;
int dns_status;
/*
* Preliminaries.
*/
if (smtp_dns_support == SMTP_DNS_DNSSEC)
res_opt |= RES_USE_DNSSEC;
/*
* Lexical features: As specified in RFC 8460, a TLSRPT policy record
* must start with a version field ("v=TLSRPTv1") followed by *WSP;*WSP
* and at least one other field (we must not assume that the second field
* will be "rua"). We leave further validation to the code that actually
* needs it.
*/
#define TLSRPTv1_MAGIC "v=TLSRPTv1"
#define TLSRPTv1_MAGIC_LEN (sizeof(TLSRPTv1_MAGIC) - 1)
#define RFC5234_WSP " \t"
/*
* Look up TXT records. Ignore records that don't start with the expected
* version ID, and require that there is exactly one such DNS record.
*/
vstring_sprintf(qname, "_smtp._tls.%s", adomain);
dns_status = dns_lookup(STR(qname), T_TXT, res_opt, &rr_list,
(VSTRING *) 0, why);
vstring_free(qname);
if (dns_status != DNS_OK) {
switch (dns_status) {
case DNS_NOTFOUND:
case DNS_POLICY:
/* Expected results. */
break;
default:
/* Unexpected results. */
msg_warn("%s: policy lookup failed for %s: %s",
smtp_tlsrpt_support, adomain, STR(why));
}
} else {
for (rr = rr_list; rr; rr = next) {
char *cp;
next = rr->next;
if (strncmp(rr->data, TLSRPTv1_MAGIC, TLSRPTv1_MAGIC_LEN) != 0)
/* Ignore non-TLSRPTv1 info. */
continue;
cp = rr->data + TLSRPTv1_MAGIC_LEN;
/*
* Should the TLSRPT library validate the entire policy for us?
*/
if (cp[strspn(cp, RFC5234_WSP)] != ';') {
msg_warn("%s: ignoring malformed policy for %s:, \"%s\"",
smtp_tlsrpt_support, adomain, rr->data);
continue;
}
if (rr_result) {
msg_warn("%s: Too many TLSRPT policies for %s",
smtp_tlsrpt_support, adomain);
dns_rr_free(rr_result);
rr_result = 0;
break;
}
rr_result = rr;
rr_list = dns_rr_detach(rr_list, rr);
}
}
vstring_free(why);
if (rr_list)
dns_rr_free(rr_list);
return (rr_result);
}
/* smtp_tlsrpt_create_wrapper - look up policy and attach TLSRPT_WRAPPER */
void smtp_tlsrpt_create_wrapper(SMTP_STATE *state, const char *domain)
{
const char *adomain;
DNS_RR *rr;
/*
* TODO(wietse): document in a suitable place that state->tlsrpt exists
* only if the next-hop domain announces a TLSRPT policy.
*/
if (state->tlsrpt) {
trw_free(state->tlsrpt);
state->tlsrpt = 0;
}
/*
* IDNA support. An internationalized domain name must be in A-label form
* 1) for TLSRPT summaries and 2) for DNS lookups. The A-label lookup
* result comes from a limited-size in-process cache, so it does not
* matter that the SMTP client requests the same mapping later.
*/
#ifndef NO_EAI
if (!allascii(domain) && (adomain = midna_domain_to_ascii(domain)) != 0) {
if (msg_verbose)
msg_info("%s: internationalized domain %s asciified to %s",
smtp_tlsrpt_support, domain, adomain);
} else
#endif
adomain = domain;
if ((rr = smtp_tlsrpt_find_policy(adomain)) != 0) {
if (msg_verbose)
msg_info("%s: domain %s has policy %.100s",
smtp_tlsrpt_support, domain, rr->data);
state->tlsrpt = trw_create(
/* rpt_socket_name= */ var_smtp_tlsrpt_sockname,
/* rpt_policy_domain= */ adomain,
/* rpt_policy_string= */ rr->data);
dns_rr_free(rr);
} else {
if (msg_verbose)
msg_info("%s: no policy for domain %s",
smtp_tlsrpt_support, domain);
}
}
/* smtp_tlsrpt_set_no_policy - no policy found */
static void smtp_tlsrpt_set_no_policy(SMTP_STATE *state)
{
trw_set_tls_policy(state->tlsrpt, TLSRPT_NO_POLICY_FOUND,
/* tls_policy_strings= */ (const char *const *) 0,
/* tls_policy_domain= */ (char *) 0,
/* mx_host_patterns= */ (const char *const *) 0);
}
/* smtp_tlsrpt_set_dane_policy - add DANE policy properties */
static void smtp_tlsrpt_set_dane_policy(SMTP_STATE *state)
{
VSTRING *buf = vstring_alloc(200);
ARGV *argv = argv_alloc(10);
TLS_DANE *dane = state->tls->dane;
TLS_TLSA *tlsa;
for (tlsa = dane->tlsa; tlsa != 0; tlsa = tlsa->next) {
vstring_sprintf(buf, "%d %d %d ", tlsa->usage,
tlsa->selector, tlsa->mtype);
hex_encode_opt(buf, (char *) tlsa->data, tlsa->length,
HEX_ENCODE_FLAG_APPEND);
argv_add(argv, STR(buf), (char *) 0);
}
trw_set_tls_policy(state->tlsrpt, TLSRPT_POLICY_TLSA,
(const char *const *) argv->argv, dane->base_domain,
/* mx_host_patterns= */ (const char *const *) 0);
argv_free(argv);
vstring_free(buf);
}
/* smtp_tlsrpt_set_ext_policy - add external policy from smtp_tls_policy_maps */
static void smtp_tlsrpt_set_ext_policy(SMTP_STATE *state)
{
SMTP_TLS_POLICY *tls = state->tls;
tlsrpt_policy_type_t policy_type_val;
if (tls->ext_policy_type == 0)
msg_panic("smtp_tlsrpt_set_ext_policy: no policy type");
switch (policy_type_val =
convert_tlsrpt_policy_type(tls->ext_policy_type)) {
case TLSRPT_POLICY_STS:
trw_set_tls_policy(state->tlsrpt, policy_type_val,
(const char *const *) tls->ext_policy_strings->argv,
tls->ext_policy_domain,
(const char *const *) tls->ext_mx_host_patterns->argv);
break;
case TLSRPT_NO_POLICY_FOUND:
smtp_tlsrpt_set_no_policy(state);
break;
default:
/* Policy type must be validated in smtp_tls_policy_maps parser. */
msg_panic("unexpected policy type: \"%s\"",
tls->ext_policy_type);
}
/*
* TODO(wietse) propagate tls->policy_failure to force policy enforcement
* to fail with the indicated error, and prevent a false positive match
* when a certificate would satisfy conventional PKI constraints.
*/
}
/* smtp_tlsrpt_set_tls_policy - set built-in or external policy */
void smtp_tlsrpt_set_tls_policy(SMTP_STATE *state)
{
SMTP_TLS_POLICY *tls = state->tls;
if (TLS_DANE_BASED(tls->level)) { /* Desired by local policy */
if (tls->dane != 0) /* Actual policy */
smtp_tlsrpt_set_dane_policy(state);
else /* No policy */
smtp_tlsrpt_set_no_policy(state);
} else if (tls->ext_policy_type) {
smtp_tlsrpt_set_ext_policy(state);
} else {
smtp_tlsrpt_set_no_policy(state);
}
}
/* smtp_tlsrpt_set_tcp_connection - set TCP connection info from SMTP_STATE */
void smtp_tlsrpt_set_tcp_connection(SMTP_STATE *state)
{
SMTP_ITERATOR *iter = state->iterator;
SMTP_SESSION *session = state->session;
MAI_HOSTADDR_STR client_addr;
struct sockaddr_storage addr_storage;
SOCKADDR_SIZE addr_storage_len = sizeof(addr_storage);
int aierr;
/*
* Get the IP client address string. The Postfix SMTP_ITERATOR already
* contains strings with server-side connection information.
*/
if (getsockname(vstream_fileno(session->stream),
(struct sockaddr *) &addr_storage,
&addr_storage_len) < 0) {
msg_warn("%s: getsockname() failed (%m)"
" skipping the ignoring client-side IP address",
smtp_tlsrpt_support);
client_addr.buf[0] = 0;
} else if ((aierr = sane_sockaddr_to_hostaddr(
(struct sockaddr *) &addr_storage,
addr_storage_len, &client_addr,
(MAI_SERVPORT_STR *) 0,
SOCK_STREAM)) != 0) {
msg_warn("%s: cannot convert IP address to string (%s)"
" -- skipping the client-side IP address",
smtp_tlsrpt_support, MAI_STRERROR(aierr));
client_addr.buf[0] = 0;
}
trw_set_tcp_connection(state->tlsrpt, client_addr.buf, STR(iter->host),
STR(iter->addr));
}
/* smtp_tlsrpt_set_ehlo_resp - format and set EHLO response */
void smtp_tlsrpt_set_ehlo_resp(SMTP_STATE *state, const char *reply)
{
ARGV *argv;
VSTRING *buf;
char **cpp;
/*
* Generate SMTP-style line breaks ("\r\n") for a multiline response.
* Internally, smtp_chat_resp() returns a multiline response as text
* separated with "\n". This is because Postfix by design removes
* protocol-specific line endings on input, uses its own internal form to
* represent text lines, and generates protocol-specific line endings on
* output. The conversion to "\r\n" below is such an output conversion.
*/
buf = vstring_alloc(100);
argv = argv_split(reply, "\n");
for (cpp = argv->argv; *cpp; cpp++) {
vstring_strcat(buf, *cpp);
if (cpp[1])
vstring_strcat(buf, "\r\n");
}
argv_free(argv);
trw_set_ehlo_resp(state->tlsrpt, STR(buf));
vstring_free(buf);
}
#endif /* USE_TLSRPT && USE_TLS */

View File

@ -9,7 +9,7 @@ SRCS = tls_prng_dev.c tls_prng_egd.c tls_prng_file.c tls_fprint.c \
tls_proxy_server_init_print.c tls_proxy_server_init_scan.c \
tls_proxy_client_start_print.c tls_proxy_client_start_scan.c \
tls_proxy_server_start_print.c tls_proxy_server_start_scan.c \
tls_proxy_client_misc.c
tls_proxy_client_misc.c tlsrpt_wrapper.c
OBJS = tls_prng_dev.o tls_prng_egd.o tls_prng_file.o tls_fprint.o \
tls_prng_exch.o tls_stream.o tls_bio_ops.o tls_misc.o tls_dh.o \
tls_verify.o tls_dane.o tls_certkey.o tls_session.o \
@ -18,8 +18,8 @@ OBJS = tls_prng_dev.o tls_prng_egd.o tls_prng_file.o tls_fprint.o \
tls_proxy_clnt.o tls_proxy_context_print.o tls_proxy_context_scan.o \
tls_proxy_client_print.o tls_proxy_client_scan.o \
tls_proxy_server_print.o tls_proxy_server_scan.o \
tls_proxy_client_misc.o
HDRS = tls.h tls_prng.h tls_scache.h tls_mgr.h tls_proxy.h
tls_proxy_client_misc.o tlsrpt_wrapper.o
HDRS = tls.h tls_prng.h tls_scache.h tls_mgr.h tls_proxy.h tlsrpt_wrapper.h
TESTSRC =
DEFS = -I. -I$(INC_DIR) -D$(SYSTYPE)
CFLAGS = $(DEBUG) $(OPT) $(DEFS)
@ -182,6 +182,7 @@ tls_client.o: tls.h
tls_client.o: tls_client.c
tls_client.o: tls_mgr.h
tls_client.o: tls_scache.h
tls_client.o: tlsrpt_wrapper.h
tls_dane.o: ../../include/argv.h
tls_dane.o: ../../include/check_arg.h
tls_dane.o: ../../include/ctable.h
@ -366,6 +367,7 @@ tls_proxy_client_print.o: ../../include/vstring.h
tls_proxy_client_print.o: tls.h
tls_proxy_client_print.o: tls_proxy.h
tls_proxy_client_print.o: tls_proxy_client_print.c
tls_proxy_client_print.o: tlsrpt_wrapper.h
tls_proxy_client_scan.o: ../../include/argv.h
tls_proxy_client_scan.o: ../../include/argv_attr.h
tls_proxy_client_scan.o: ../../include/attr.h
@ -387,6 +389,7 @@ tls_proxy_client_scan.o: ../../include/vstring.h
tls_proxy_client_scan.o: tls.h
tls_proxy_client_scan.o: tls_proxy.h
tls_proxy_client_scan.o: tls_proxy_client_scan.c
tls_proxy_client_scan.o: tlsrpt_wrapper.h
tls_proxy_clnt.o: ../../include/argv.h
tls_proxy_clnt.o: ../../include/attr.h
tls_proxy_clnt.o: ../../include/check_arg.h
@ -588,3 +591,15 @@ tls_verify.o: ../../include/vstream.h
tls_verify.o: ../../include/vstring.h
tls_verify.o: tls.h
tls_verify.o: tls_verify.c
tls_verify.o: tlsrpt_wrapper.h
tlsrpt_wrapper.o: ../../include/argv.h
tlsrpt_wrapper.o: ../../include/check_arg.h
tlsrpt_wrapper.o: ../../include/msg.h
tlsrpt_wrapper.o: ../../include/mymalloc.h
tlsrpt_wrapper.o: ../../include/name_code.h
tlsrpt_wrapper.o: ../../include/stringops.h
tlsrpt_wrapper.o: ../../include/sys_defs.h
tlsrpt_wrapper.o: ../../include/vbuf.h
tlsrpt_wrapper.o: ../../include/vstring.h
tlsrpt_wrapper.o: tlsrpt_wrapper.c
tlsrpt_wrapper.o: tlsrpt_wrapper.h

View File

@ -270,6 +270,8 @@ typedef struct {
int errordepth; /* Chain depth of error cert */
int errorcode; /* First error at error depth */
int must_fail; /* Failed to load trust settings */
int rpt_reported; /* Failure was reported with TLSRPT */
char *ffail_type; /* Forced verification failure */
} TLS_SESS_STATE;
/*
@ -502,6 +504,8 @@ typedef struct {
const ARGV *matchargv; /* Cert match patterns */
const char *mdalg; /* default message digest algorithm */
const TLS_DANE *dane; /* DANE TLSA verification */
struct TLSRPT_WRAPPER *tlsrpt; /* RFC 8460 reporting */
char *ffail_type; /* Forced verification failure */
} TLS_CLIENT_START_PROPS;
extern TLS_APPL_STATE *tls_client_init(const TLS_CLIENT_INIT_PROPS *);
@ -525,12 +529,13 @@ extern TLS_SESS_STATE *tls_client_post_connect(TLS_SESS_STATE *,
a6, a7, a8, a9, a10, a11, a12, a13, a14))
#define TLS_CLIENT_START(props, a1, a2, a3, a4, a5, a6, a7, a8, a9, \
a10, a11, a12, a13, a14, a15, a16, a17, a18) \
a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20) \
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)))
((props)->a16), ((props)->a17), ((props)->a18), ((props)->a19), \
((props)->a20), (props)))
/*
* tls_server.c
@ -663,7 +668,7 @@ extern void tls_auto_groups(SSL_CTX *, const char *, const char *);
extern char *tls_peer_CN(X509 *, const TLS_SESS_STATE *);
extern char *tls_issuer_CN(X509 *, const TLS_SESS_STATE *);
extern int tls_verify_certificate_callback(int, X509_STORE_CTX *);
extern void tls_log_verify_error(TLS_SESS_STATE *);
extern void tls_log_verify_error(TLS_SESS_STATE *, struct TLSRPT_WRAPPER *);
/*
* tls_dane.c

View File

@ -152,6 +152,9 @@
/*
/* Victor Duchovni
/* Morgan Stanley
/*
/* Wietse Venema
/* porcupine.org
/*--*/
/* System library. */
@ -160,6 +163,7 @@
#ifdef USE_TLS
#include <string.h>
#include <tlsrpt_wrapper.h>
#ifdef STRCASECMP_IN_STRINGS_H
#include <strings.h>
@ -363,7 +367,7 @@ static void verify_x509(TLS_SESS_STATE *TLScontext, X509 *peercert,
if (!TLS_CERT_IS_MATCHED(TLScontext)
&& (TLScontext->log_mask & TLS_LOG_UNTRUSTED)) {
if (TLScontext->session_reused == 0)
tls_log_verify_error(TLScontext);
tls_log_verify_error(TLScontext, props->tlsrpt);
else
msg_info("%s: re-using session with untrusted peer credential, "
"look for details earlier in the log", props->namaddr);
@ -407,7 +411,7 @@ static void verify_rpk(TLS_SESS_STATE *TLScontext, EVP_PKEY *peerpkey,
if (!TLS_CERT_IS_MATCHED(TLScontext)
&& (TLScontext->log_mask & TLS_LOG_UNTRUSTED)) {
if (TLScontext->session_reused == 0)
tls_log_verify_error(TLScontext);
tls_log_verify_error(TLScontext, props->tlsrpt);
else
msg_info("%s: re-using session with untrusted certificate, "
"look for details earlier in the log", props->namaddr);
@ -579,6 +583,7 @@ static int tls_auth_enable(TLS_SESS_STATE *TLScontext,
* therefore valid for use with SNI.
*/
if (SSL_dane_enable(TLScontext->con, 0) <= 0) {
/* TLSRPT: Local resource error, don't report. */
msg_warn("%s: error enabling DANE-based certificate validation",
TLScontext->namaddr);
tls_print_errors();
@ -595,6 +600,7 @@ static int tls_auth_enable(TLS_SESS_STATE *TLScontext,
case TLS_LEV_FPRINT:
/* Synthetic DANE for fingerprint security */
if (SSL_dane_enable(TLScontext->con, 0) <= 0) {
/* TLSRPT: Local resource error, don't report. */
msg_warn("%s: error enabling fingerprint certificate validation",
props->namaddr);
tls_print_errors();
@ -608,6 +614,7 @@ static int tls_auth_enable(TLS_SESS_STATE *TLScontext,
if (TLScontext->dane != 0 && TLScontext->dane->tlsa != 0) {
/* Synthetic DANE for per-destination trust-anchors */
if (SSL_dane_enable(TLScontext->con, NULL) <= 0) {
/* TLSRPT: Local resource error, don't report. */
msg_warn("%s: error configuring local trust anchors",
props->namaddr);
tls_print_errors();
@ -622,6 +629,7 @@ static int tls_auth_enable(TLS_SESS_STATE *TLScontext,
if (sni) {
if (strlen(sni) > TLSEXT_MAXLEN_host_name) {
/* TLSRPT: Local configuration error, don't report. */
msg_warn("%s: ignoring too long SNI hostname: %.100s",
props->namaddr, sni);
return (0);
@ -633,6 +641,7 @@ static int tls_auth_enable(TLS_SESS_STATE *TLScontext,
* failed to send the SNI name, we have little choice but to abort.
*/
if (!SSL_set_tlsext_host_name(TLScontext->con, sni)) {
/* TLSRPT: Local resource or configuration error, don't report. */
msg_warn("%s: error setting SNI hostname to: %s", props->namaddr,
sni);
return (0);
@ -975,6 +984,7 @@ TLS_SESS_STATE *tls_client_start(const TLS_CLIENT_START_PROPS *props)
*/
protomask = tls_proto_mask_lims(props->protocols, &min_proto, &max_proto);
if (protomask == TLS_PROTOCOL_INVALID) {
/* TLSRPT: Local configuration error, don't report. */
/* tls_protocol_mask() logs no warning. */
msg_warn("%s: Invalid TLS protocol list \"%s\": aborting TLS session",
props->namaddr, props->protocols);
@ -1006,6 +1016,7 @@ TLS_SESS_STATE *tls_client_start(const TLS_CLIENT_START_PROPS *props)
TLScontext->level = props->tls_level;
if ((TLScontext->con = SSL_new(app_ctx->ssl_ctx)) == NULL) {
/* TLSRPT: Local resource error, don't report. */
msg_warn("Could not allocate 'TLScontext->con' with SSL_new()");
tls_print_errors();
tls_free_context(TLScontext);
@ -1022,6 +1033,7 @@ TLS_SESS_STATE *tls_client_start(const TLS_CLIENT_START_PROPS *props)
cipher_list = tls_set_ciphers(TLScontext, props->cipher_grade,
props->cipher_exclusions);
if (cipher_list == 0) {
/* TLSRPT: Local configuration error, don't report. */
/* already warned */
tls_free_context(TLScontext);
return (0);
@ -1036,6 +1048,7 @@ TLS_SESS_STATE *tls_client_start(const TLS_CLIENT_START_PROPS *props)
TLScontext->dane = props->dane;
if (!SSL_set_ex_data(TLScontext->con, TLScontext_index, TLScontext)) {
/* TLSRPT: Local resource error, don't report. */
msg_warn("Could not set application data for 'TLScontext->con'");
tls_print_errors();
tls_free_context(TLScontext);
@ -1069,6 +1082,7 @@ TLS_SESS_STATE *tls_client_start(const TLS_CLIENT_START_PROPS *props)
* early.
*/
if (!tls_auth_enable(TLScontext, props)) {
/* Already warned and reported TLSRPT result. */
tls_free_context(TLScontext);
return (0);
}
@ -1105,6 +1119,13 @@ TLS_SESS_STATE *tls_client_start(const TLS_CLIENT_START_PROPS *props)
switch (TLScontext->level) {
case TLS_LEV_HALF_DANE:
case TLS_LEV_DANE:
#ifdef USE_TLSRPT
if (props->tlsrpt) {
trw_report_failure(props->tlsrpt, TLSRPT_TLSA_INVALID,
/* additional_info= */ (char *) 0,
"all-TLSA-records-unusable");
}
#endif
msg_warn("%s: all TLSA records unusable, fallback to "
"unauthenticated TLS", TLScontext->namaddr);
must_fail = 0;
@ -1112,13 +1133,34 @@ TLS_SESS_STATE *tls_client_start(const TLS_CLIENT_START_PROPS *props)
break;
case TLS_LEV_FPRINT:
#ifdef USE_TLSRPT
if (props->tlsrpt) {
trw_report_failure(props->tlsrpt, TLSRPT_VALIDATION_FAILURE,
/* additional_info= */ (char *) 0,
"all-fingerprints-unusable");
}
#endif
msg_warn("%s: all fingerprints unusable", TLScontext->namaddr);
break;
case TLS_LEV_DANE_ONLY:
#ifdef USE_TLSRPT
if (props->tlsrpt) {
trw_report_failure(props->tlsrpt, TLSRPT_TLSA_INVALID,
/* additional_info= */ (char *) 0,
"all-TLSA-records-unusable");
}
#endif
msg_warn("%s: all TLSA records unusable", TLScontext->namaddr);
break;
case TLS_LEV_SECURE:
case TLS_LEV_VERIFY:
#ifdef USE_TLSRPT
if (props->tlsrpt) {
trw_report_failure(props->tlsrpt, TLSRPT_VALIDATION_FAILURE,
/* additional_info= */ (char *) 0,
"all-trust-anchors-unusable");
}
#endif
msg_warn("%s: all trust anchors unusable", TLScontext->namaddr);
break;
}
@ -1194,6 +1236,7 @@ TLS_SESS_STATE *tls_client_start(const TLS_CLIENT_START_PROPS *props)
*/
if (SSL_set_fd(TLScontext->con, props->stream == 0 ? props->fd :
vstream_fileno(props->stream)) != 1) {
/* TLSRPT: Local resource error, don't report. */
msg_info("SSL_set_fd error to %s", props->namaddr);
tls_print_errors();
uncache_session(app_ctx->ssl_ctx, TLScontext);
@ -1213,6 +1256,16 @@ TLS_SESS_STATE *tls_client_start(const TLS_CLIENT_START_PROPS *props)
if (log_mask & TLS_LOG_TLSPKTS)
tls_set_bio_callback(SSL_get_rbio(TLScontext->con), tls_bio_dump_cb);
/*
* An external (STS) policy signaled a failure. Prevent false (PKI)
* certificate matches in tls_verify.c. TODO(wietse) how was this handled
* historically?
*/
if (props->ffail_type) {
TLScontext->ffail_type = mystrdup(props->ffail_type);
TLScontext->must_fail = 1;
}
/*
* If we don't trigger the handshake in the library, leave control over
* SSL_connect/read/write/etc with the application.
@ -1245,6 +1298,12 @@ TLS_SESS_STATE *tls_client_start(const TLS_CLIENT_START_PROPS *props)
msg_info("SSL_connect error to %s: lost connection",
props->namaddr);
}
#ifdef USE_TLSRPT
if (props->tlsrpt)
trw_report_failure(props->tlsrpt, TLSRPT_VALIDATION_FAILURE,
/* additional_info= */ (char *) 0,
"tls-handshake-failure");
#endif
uncache_session(app_ctx->ssl_ctx, TLScontext);
tls_free_context(TLScontext);
return (0);
@ -1360,6 +1419,19 @@ TLS_SESS_STATE *tls_client_post_connect(TLS_SESS_STATE *TLScontext,
tls_int_seed();
/*
* Precondition: tls_client_start() is called only for a new TCP
* connection. It is never called for a reused TCP connection.
*
* Inform the caller that they should not generate a TLSRPT 'success' or
* 'failure' event: this TLS protocol engine has already generated a
* TLSRPT 'failure' event for this session.
*/
#ifdef USE_TLSRPT
TLScontext->rpt_reported = props->tlsrpt != 0
&& trw_is_reported(props->tlsrpt);
#endif
return (TLScontext);
}

View File

@ -1363,6 +1363,8 @@ TLS_SESS_STATE *tls_alloc_sess_context(int log_mask, const char *namaddr)
TLScontext->errordepth = -1;
TLScontext->errorcode = X509_V_OK;
TLScontext->errorcert = 0;
TLScontext->rpt_reported = 0;
TLScontext->ffail_type = 0;
return (TLScontext);
}
@ -1413,6 +1415,8 @@ void tls_free_context(TLS_SESS_STATE *TLScontext)
myfree((void *) TLScontext->srvr_sig_dgst);
if (TLScontext->errorcert)
X509_free(TLScontext->errorcert);
if (TLScontext->ffail_type)
myfree(TLScontext->ffail_type);
myfree((void *) TLScontext);
}

View File

@ -108,11 +108,12 @@ extern VSTREAM *tls_proxy_open(const char *, int, VSTREAM *, const char *,
((props)->a12), ((props)->a13), ((props)->a14))
#define TLS_PROXY_CLIENT_START_PROPS(props, a1, a2, a3, a4, a5, a6, a7, a8, \
a9, a10, a11, a12, a13, a14, a15) \
a9, a10, a11, a12, a13, a14, a15, a16, a17) \
(((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)->a12), ((props)->a13), ((props)->a14), ((props)->a15), \
((props)->a16), ((props)->a17))
extern TLS_SESS_STATE *tls_proxy_context_receive(VSTREAM *);
extern void tls_proxy_context_free(TLS_SESS_STATE *);
@ -181,6 +182,7 @@ extern void tls_proxy_server_start_free(TLS_SERVER_START_PROPS *);
#define TLS_ATTR_SRVR_SIG_BITS "srvr_signature_bits"
#define TLS_ATTR_SRVR_SIG_DGST "srvr_signature_digest"
#define TLS_ATTR_NAMADDR "namaddr"
#define TLS_ATTR_RPT_REPORTED "rpt_reported"
/*
* TLS_SERVER_INIT_PROPS attributes.
@ -255,6 +257,8 @@ extern void tls_proxy_server_start_free(TLS_SERVER_START_PROPS *);
#define TLS_ATTR_MATCHARGV "matchargv"
#define TLS_ATTR_MDALG "mdalg"
#define TLS_ATTR_DANE "dane"
#define TLS_ATTR_TLSRPT "tlsrpt"
#define TLS_ATTR_FFAIL_TYPE "forced_failure_type"
/*
* TLS_TLSA attributes.

View File

@ -79,6 +79,10 @@
#include <tls.h>
#include <tls_proxy.h>
#ifdef USE_TLSRPT
#define TLSRPT_WRAPPER_INTERNAL
#include <tlsrpt_wrapper.h>
#endif
#define STR(x) vstring_str(x)
#define LEN(x) VSTRING_LEN(x)
@ -242,6 +246,59 @@ static int tls_proxy_client_dane_print(ATTR_PRINT_COMMON_FN print_fn,
return (ret);
}
#ifdef USE_TLSRPT
/* tls_proxy_client_tlsrpt_print - send TLSRPT_WRAPPER over stream */
static int tls_proxy_client_tlsrpt_print(ATTR_PRINT_COMMON_FN print_fn,
VSTREAM *fp, int flags, const void *ptr)
{
const TLSRPT_WRAPPER *trw = (const TLSRPT_WRAPPER *) ptr;
int have_trw = trw != 0;
int ret;
ret = print_fn(fp, flags | ATTR_FLAG_MORE,
SEND_ATTR_INT(TLS_ATTR_TLSRPT, have_trw),
ATTR_TYPE_END);
if (msg_verbose)
msg_info("tls_proxy_client_tlsrpt_print have_trw=%d", have_trw);
if (ret == 0 && have_trw) {
ret = print_fn(fp, flags | ATTR_FLAG_MORE,
SEND_ATTR_STR(TRW_RPT_SOCKET_NAME,
STRING_OR_EMPTY(trw->rpt_socket_name)),
SEND_ATTR_STR(TRW_RPT_POLICY_DOMAIN,
STRING_OR_EMPTY(trw->rpt_policy_domain)),
SEND_ATTR_STR(TRW_RPT_POLICY_STRING,
STRING_OR_EMPTY(trw->rpt_policy_string)),
SEND_ATTR_INT(TRW_TLS_POLICY_TYPE,
(int) trw->tls_policy_type),
SEND_ATTR_FUNC(argv_attr_print,
(const void *) trw->tls_policy_strings),
SEND_ATTR_STR(TRW_TLS_POLICY_DOMAIN,
STRING_OR_EMPTY(trw->tls_policy_domain)),
SEND_ATTR_FUNC(argv_attr_print,
(const void *) trw->mx_host_patterns),
SEND_ATTR_STR(TRW_SRC_MTA_ADDR,
STRING_OR_EMPTY(trw->snd_mta_addr)),
SEND_ATTR_STR(TRW_DST_MTA_NAME,
STRING_OR_EMPTY(trw->rcv_mta_name)),
SEND_ATTR_STR(TRW_DST_MTA_ADDR,
STRING_OR_EMPTY(trw->rcv_mta_addr)),
SEND_ATTR_STR(TRW_DST_MTA_EHLO,
STRING_OR_EMPTY(trw->rcv_mta_ehlo)),
SEND_ATTR_INT(TRW_FLAGS,
trw->flags),
ATTR_TYPE_END);
}
/* Do not flush the stream. */
if (msg_verbose)
msg_info("tls_proxy_client_tlsrpt_print ret=%d", ret);
return (ret);
}
#endif
/* tls_proxy_client_start_print - send TLS_CLIENT_START_PROPS over stream */
int tls_proxy_client_start_print(ATTR_PRINT_COMMON_FN print_fn,
@ -283,6 +340,12 @@ int tls_proxy_client_start_print(ATTR_PRINT_COMMON_FN print_fn,
STRING_OR_EMPTY(props->mdalg)),
SEND_ATTR_FUNC(tls_proxy_client_dane_print,
(const void *) props->dane),
#ifdef USE_TLSRPT
SEND_ATTR_FUNC(tls_proxy_client_tlsrpt_print,
(const void *) props->tlsrpt),
#endif
SEND_ATTR_STR(TLS_ATTR_FFAIL_TYPE,
STRING_OR_EMPTY(props->ffail_type)),
ATTR_TYPE_END);
/* Do not flush the stream. */
if (msg_verbose)

View File

@ -113,6 +113,10 @@
#define TLS_INTERNAL
#include <tls.h>
#include <tls_proxy.h>
#ifdef USE_TLSRPT
#define TLSRPT_WRAPPER_INTERNAL
#include <tlsrpt_wrapper.h>
#endif
#define STR(x) vstring_str(x)
#define LEN(x) VSTRING_LEN(x)
@ -329,6 +333,12 @@ void tls_proxy_client_start_free(TLS_CLIENT_START_PROPS *props)
myfree((void *) props->mdalg);
if (props->dane)
tls_dane_free((TLS_DANE *) props->dane);
#ifdef USE_TLSRPT
if (props->tlsrpt)
trw_free(props->tlsrpt);
#endif
if (props->ffail_type)
myfree(props->ffail_type);
myfree((void *) props);
}
@ -419,6 +429,89 @@ static int tls_proxy_client_dane_scan(ATTR_SCAN_COMMON_FN scan_fn,
return (ret);
}
#define EXPORT_OR_NULL(str, vstr) do { \
if (LEN(vstr) > 0) { \
(str) = vstring_export(vstr); \
} else { \
(str) = 0; \
vstring_free(vstr); \
} \
} while (0)
#ifdef USE_TLSRPT
/* tls_proxy_client_tlsrpt_scan - receive TLSRPT_WRAPPER from stream */
static int tls_proxy_client_tlsrpt_scan(ATTR_SCAN_COMMON_FN scan_fn,
VSTREAM *fp, int flags, void *ptr)
{
TLSRPT_WRAPPER *trw = 0;
int ret;
int have_tlsrpt = 0;
ret = scan_fn(fp, flags | ATTR_FLAG_MORE,
RECV_ATTR_INT(TLS_ATTR_TLSRPT, &have_tlsrpt),
ATTR_TYPE_END);
if (msg_verbose)
msg_info("tls_proxy_client_tlsrpt_scan have_tlsrpt=%d", have_tlsrpt);
if (ret == 1 && have_tlsrpt) {
VSTRING *rpt_socket_name = vstring_alloc(100);
VSTRING *rpt_policy_domain = vstring_alloc(100);
VSTRING *rpt_policy_string = vstring_alloc(100);
int tls_policy_type;
ARGV *tls_policy_strings = 0;
VSTRING *tls_policy_domain = vstring_alloc(100);
ARGV *mx_host_patterns = 0;
VSTRING *snd_mta_addr = vstring_alloc(100);
VSTRING *rcv_mta_name = vstring_alloc(100);
VSTRING *rcv_mta_addr = vstring_alloc(100);
VSTRING *rcv_mta_ehlo = vstring_alloc(100);
int trw_flags;
ret = scan_fn(fp, flags | ATTR_FLAG_MORE,
RECV_ATTR_STR(TRW_RPT_SOCKET_NAME, rpt_socket_name),
RECV_ATTR_STR(TRW_RPT_POLICY_DOMAIN, rpt_policy_domain),
RECV_ATTR_STR(TRW_RPT_POLICY_STRING, rpt_policy_string),
RECV_ATTR_INT(TRW_TLS_POLICY_TYPE, &tls_policy_type),
RECV_ATTR_FUNC(argv_attr_scan, &tls_policy_strings),
RECV_ATTR_STR(TRW_TLS_POLICY_DOMAIN, tls_policy_domain),
RECV_ATTR_FUNC(argv_attr_scan, &mx_host_patterns),
RECV_ATTR_STR(TRW_SRC_MTA_ADDR, snd_mta_addr),
RECV_ATTR_STR(TRW_DST_MTA_NAME, rcv_mta_name),
RECV_ATTR_STR(TRW_DST_MTA_ADDR, rcv_mta_addr),
RECV_ATTR_STR(TRW_DST_MTA_EHLO, rcv_mta_ehlo),
RECV_ATTR_INT(TRW_FLAGS, &trw_flags),
ATTR_TYPE_END);
/* Always construct a well-formed structure. */
trw = (TLSRPT_WRAPPER *) mymalloc(sizeof(*trw));
trw->rpt_socket_name = vstring_export(rpt_socket_name);
trw->rpt_policy_domain = vstring_export(rpt_policy_domain);
trw->rpt_policy_string = vstring_export(rpt_policy_string);
trw->tls_policy_type = tls_policy_type;
trw->tls_policy_strings = tls_policy_strings;
EXPORT_OR_NULL(trw->tls_policy_domain, tls_policy_domain);
trw->mx_host_patterns = mx_host_patterns;
EXPORT_OR_NULL(trw->snd_mta_addr, snd_mta_addr);
EXPORT_OR_NULL(trw->rcv_mta_name, rcv_mta_name);
EXPORT_OR_NULL(trw->rcv_mta_addr, rcv_mta_addr);
EXPORT_OR_NULL(trw->rcv_mta_ehlo, rcv_mta_ehlo);
trw->flags = trw_flags;
ret = (ret == 12 ? 1 : -1);
if (ret != 1) {
trw_free(trw);
trw = 0;
}
}
*(TLSRPT_WRAPPER **) ptr = trw;
if (msg_verbose)
msg_info("tls_proxy_client_tlsrpt_scan ret=%d", ret);
return (ret);
}
#endif
/* tls_proxy_client_start_scan - receive TLS_CLIENT_START_PROPS from stream */
int tls_proxy_client_start_scan(ATTR_SCAN_COMMON_FN scan_fn, VSTREAM *fp,
@ -437,6 +530,13 @@ int tls_proxy_client_start_scan(ATTR_SCAN_COMMON_FN scan_fn, VSTREAM *fp,
VSTRING *cipher_grade = vstring_alloc(25);
VSTRING *cipher_exclusions = vstring_alloc(25);
VSTRING *mdalg = vstring_alloc(25);
VSTRING *ffail_type = vstring_alloc(25);
#ifdef USE_TLSRPT
#define EXPECT_START_SCAN_RETURN 17
#else
#define EXPECT_START_SCAN_RETURN 16
#endif
if (msg_verbose)
msg_info("begin tls_proxy_client_start_scan");
@ -467,6 +567,11 @@ int tls_proxy_client_start_scan(ATTR_SCAN_COMMON_FN scan_fn, VSTREAM *fp,
RECV_ATTR_STR(TLS_ATTR_MDALG, mdalg),
RECV_ATTR_FUNC(tls_proxy_client_dane_scan,
&props->dane),
#ifdef USE_TLSRPT
RECV_ATTR_FUNC(tls_proxy_client_tlsrpt_scan,
&props->tlsrpt),
#endif
RECV_ATTR_STR(TLS_ATTR_FFAIL_TYPE, ffail_type),
ATTR_TYPE_END);
/* Always construct a well-formed structure. */
props->nexthop = vstring_export(nexthop);
@ -479,7 +584,8 @@ int tls_proxy_client_start_scan(ATTR_SCAN_COMMON_FN scan_fn, VSTREAM *fp,
props->cipher_grade = vstring_export(cipher_grade);
props->cipher_exclusions = vstring_export(cipher_exclusions);
props->mdalg = vstring_export(mdalg);
ret = (ret == 15 ? 1 : -1);
EXPORT_OR_NULL(props->ffail_type, ffail_type);
ret = (ret == EXPECT_START_SCAN_RETURN ? 1 : -1);
if (ret != 1) {
tls_proxy_client_start_free(props);
props = 0;

View File

@ -110,6 +110,8 @@ int tls_proxy_context_print(ATTR_PRINT_COMMON_FN print_fn, VSTREAM *fp,
STRING_OR_EMPTY(tp->srvr_sig_dgst)),
SEND_ATTR_STR(TLS_ATTR_NAMADDR,
STRING_OR_EMPTY(tp->namaddr)),
SEND_ATTR_INT(TLS_ATTR_RPT_REPORTED,
tp->rpt_reported),
ATTR_TYPE_END);
/* Do not flush the stream. */
return (ret);

View File

@ -124,6 +124,8 @@ int tls_proxy_context_scan(ATTR_SCAN_COMMON_FN scan_fn, VSTREAM *fp,
RECV_ATTR_INT(TLS_ATTR_SRVR_SIG_BITS, &tls_context->srvr_sig_bits),
RECV_ATTR_STR(TLS_ATTR_SRVR_SIG_DGST, srvr_sig_dgst),
RECV_ATTR_STR(TLS_ATTR_NAMADDR, namaddr),
RECV_ATTR_INT(TLS_ATTR_RPT_REPORTED,
&tls_context->rpt_reported),
ATTR_TYPE_END);
/* Always construct a well-formed structure. */
tls_context->peer_CN = vstring_export(peer_CN);
@ -141,7 +143,7 @@ int tls_proxy_context_scan(ATTR_SCAN_COMMON_FN scan_fn, VSTREAM *fp,
tls_context->srvr_sig_curve = vstring_export(srvr_sig_curve);
tls_context->srvr_sig_dgst = vstring_export(srvr_sig_dgst);
tls_context->namaddr = vstring_export(namaddr);
ret = (ret == 24 ? 1 : -1);
ret = (ret == 25 ? 1 : -1);
if (ret != 1) {
tls_proxy_context_free(tls_context);
tls_context = 0;

View File

@ -1035,7 +1035,7 @@ TLS_SESS_STATE *tls_server_post_accept(TLS_SESS_STATE *TLScontext)
if (!TLS_CERT_IS_TRUSTED(TLScontext)
&& (TLScontext->log_mask & TLS_LOG_UNTRUSTED)) {
if (TLScontext->session_reused == 0)
tls_log_verify_error(TLScontext);
tls_log_verify_error(TLScontext, (struct TLSRPT_WRAPPER *) 0);
else
msg_info("%s: re-using session with untrusted certificate, "
"look for details earlier in the log",

View File

@ -11,8 +11,9 @@
/* int ok;
/* X509_STORE_CTX *ctx;
/*
/* int tls_log_verify_error(TLScontext)
/* int tls_log_verify_error(TLScontext, tlsrpt)
/* TLS_SESS_STATE *TLScontext;
/* struct TLSRPT_WRAPPER *tlsrpt;
/*
/* char *tls_peer_CN(peercert, TLScontext)
/* X509 *peercert;
@ -89,6 +90,9 @@
/*
/* Victor Duchovni
/* Morgan Stanley
/*
/* Wietse Venema
/* porcupine.org
/*--*/
/* System library. */
@ -107,6 +111,10 @@
/* TLS library. */
#ifdef USE_TLSRPT
#include <tlsrpt_wrapper.h>
#endif
#define TLS_INTERNAL
#include <tls.h>
@ -194,18 +202,50 @@ int tls_verify_certificate_callback(int ok, X509_STORE_CTX *ctx)
/* tls_log_verify_error - Report final verification error status */
void tls_log_verify_error(TLS_SESS_STATE *TLScontext)
void tls_log_verify_error(TLS_SESS_STATE *TLScontext,
struct TLSRPT_WRAPPER *tlsrpt)
{
char buf[CCERT_BUFSIZ];
int err = TLScontext->errorcode;
X509 *cert = TLScontext->errorcert;
int depth = TLScontext->errordepth;
#ifdef USE_TLSRPT
VSTRING *err_vstr = vstring_alloc(100);
#define CERT_ERROR_TO_STRING(err) \
translit(vstring_str(vstring_strcpy(err_vstr, \
X509_verify_cert_error_string(err))), \
" ", "_")
#endif
#define PURPOSE ((depth>0) ? "CA": TLScontext->am_server ? "client": "server")
if (err == X509_V_OK)
return;
/*
* If an external policy flagged an error, report that instead.
*/
if (TLScontext->ffail_type) {
msg_info("certificate verification failed for %s: "
"external policy failure (%s)",
TLScontext->namaddr, TLScontext->ffail_type);
#ifdef USE_TLSRPT
if (tlsrpt) {
tlsrpt_failure_t failure_type;
if ((failure_type = convert_tlsrpt_policy_failure(TLScontext->ffail_type)) < 0)
msg_panic("tls_log_verify_error: unexpected failure_reason: %s",
TLScontext->ffail_type);
trw_report_failure(tlsrpt, failure_type,
/* additional_info= */ (char *) 0,
/* failure_reason= */ (char *) 0);
}
#endif
return;
}
/*
* Specific causes for verification failure.
*/
@ -218,10 +258,22 @@ void tls_log_verify_error(TLS_SESS_STATE *TLScontext)
*/
msg_info("certificate verification failed for %s: "
"not trusted by local or TLSA policy", TLScontext->namaddr);
#ifdef USE_TLSRPT
if (tlsrpt)
trw_report_failure(tlsrpt, TLSRPT_CERTIFICATE_NOT_TRUSTED,
/* additional_info= */ (char *) 0,
/* failure_code= */ (char *) 0);
#endif
break;
case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT:
msg_info("certificate verification failed for %s: "
"self-signed certificate", TLScontext->namaddr);
#ifdef USE_TLSRPT
if (tlsrpt)
trw_report_failure(tlsrpt, TLSRPT_VALIDATION_FAILURE,
/* additional_info= */ (char *) 0,
CERT_ERROR_TO_STRING(err));
#endif
break;
case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY:
case X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN:
@ -237,25 +289,55 @@ void tls_log_verify_error(TLS_SESS_STATE *TLScontext)
strcpy(buf, "<unknown>");
msg_info("certificate verification failed for %s: untrusted issuer %s",
TLScontext->namaddr, printable(buf, '?'));
#ifdef USE_TLSRPT
if (tlsrpt)
trw_report_failure(tlsrpt, TLSRPT_VALIDATION_FAILURE,
/* additional_info= */ (char *) 0,
CERT_ERROR_TO_STRING(err));
#endif
break;
case X509_V_ERR_CERT_NOT_YET_VALID:
case X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD:
msg_info("%s certificate verification failed for %s: certificate not"
" yet valid", PURPOSE, TLScontext->namaddr);
#ifdef USE_TLSRPT
if (tlsrpt)
trw_report_failure(tlsrpt, TLSRPT_VALIDATION_FAILURE,
/* additional_info= */ (char *) 0,
CERT_ERROR_TO_STRING(err));
#endif
break;
case X509_V_ERR_CERT_HAS_EXPIRED:
case X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD:
msg_info("%s certificate verification failed for %s: certificate has"
" expired", PURPOSE, TLScontext->namaddr);
#ifdef USE_TLSRPT
if (tlsrpt)
trw_report_failure(tlsrpt, TLSRPT_CERTIFICATE_EXPIRED,
/* additional_info= */ (char *) 0,
/* failure_code= */ (char *) 8);
#endif
break;
case X509_V_ERR_INVALID_PURPOSE:
msg_info("certificate verification failed for %s: not designated for "
"use as a %s certificate", TLScontext->namaddr, PURPOSE);
#ifdef USE_TLSRPT
if (tlsrpt)
trw_report_failure(tlsrpt, TLSRPT_VALIDATION_FAILURE,
/* additional_info= */ (char *) 0,
CERT_ERROR_TO_STRING(err));
#endif
break;
case X509_V_ERR_CERT_CHAIN_TOO_LONG:
msg_info("certificate verification failed for %s: "
"certificate chain longer than limit(%d)",
TLScontext->namaddr, depth - 1);
#ifdef USE_TLSRPT
if (tlsrpt)
trw_report_failure(tlsrpt, TLSRPT_VALIDATION_FAILURE,
/* additional_info= */ (char *) 0,
CERT_ERROR_TO_STRING(err));
#endif
break;
default:
msg_info("%s certificate verification failed for %s: num=%d:%s",
@ -263,6 +345,9 @@ void tls_log_verify_error(TLS_SESS_STATE *TLScontext)
X509_verify_cert_error_string(err));
break;
}
#ifdef USE_TLSRPT
vstring_free(err_vstr);
#endif
}
#ifndef DONT_GRIPE

View File

@ -0,0 +1,698 @@
/*++
/* NAME
/* tlsrpt_wrapper 3
/* SUMMARY
/* TLSRPT support for the SMTP and TLS protocol engines
/* SYNOPSIS
/* #include <tlsrpt_wrapper.h>
/*
/* #ifdef USE_TLS
/* #ifdef USE_TLSRPT
/* TLS_RPT *trw_create(
/* const char *rpt_socket_name,
/* const char *rpt_policy_domain,
/* const char *rpt_policy_string)
/*
/* void trw_free(
/* TLSRPT_WRAPPER *trw)
/*
/* void trw_set_tls_policy(
/* TLSRPT_WRAPPER *trw,
/* tlsrpt_policy_type_t tls_policy_type,
/* const char *const *tls_policy_strings,
/* const char *tls_policy_domain
/* const char *const *mx_host_patterns)
/*
/* void trw_set_tcp_connection(
/* TLSRPT_WRAPPER *trw,
/* const char *snd_mta_addr,
/* const char *rcv_mta_name,
/* const char *rcv_mta_addr)
/*
/* void trw_set_ehlo_resp(
/* TLSRPT_WRAPPER *trw,
/* const char *rcv_mta_ehlo)
/*
/* void trw_report_failure(
/* TLSRPT_WRAPPER *trw,
/* tlsrpt_failure_t failure_type,
/* const char *additional_info,
/* const char *failure_reason)
/*
/* void trw_report_success(
/* TLSRPT_WRAPPER *trw)
/*
/* int trw_is_reported(
/* TLSRPT_WRAPPER *trw)
/*
/* tlsrpt_policy_type_t convert_tlsrpt_policy_type(
/* const char *policy_type)
/*
/* tlsrpt_failure_t convert_tlsrpt_policy_failure(
/* const char *policy_failure)
/* #endif /* USE_TLS_RPT */
/*
/* int valid_tlsrpt_policy_type(
/* const char *type_name)
/*
/* int valid_tlsrpt_policy_failure(
/* const char *failure_name)
/* #endif /* USE_TLS */
/* POSTFIX ARCHITECTURE, BOTTOM-UP VIEW
/* .ad
/* .fi
/* This module encapsulates TLSRPT support for Postfix's
/* multi-process and multi-layer architecture. The text that follows
/* explains the purpose of this software layer.
/*
/* First, Postfix TLSRPT support uses the TLSRPT client library
/* from sys4.de. That library makes the reasonable assumption that
/* all calls concerning one SMTP session will be made from within
/* one process.
/*
/* Second, some TLS errors are detected in the SMTP protocol
/* engine (example: a remote SMTP server does not announce STARTTLS
/* support), while other TLS errors are detected in the TLS protocol
/* engine (example: certificate verification error).
/*
/* Third, the Postfix TLS protocol engine may be located in a
/* different process than the SMTP protocol engine. And even if the
/* two are located in the same process, the TLS protocol engine knows
/* nothing about SMTP. Hence, there needs to be an abstraction that
/* isolates the TLS protocol engine from the SMTP-specific details
/* of TLSRPT.
/*
/* Fourth, Postfix has a pipelined and layered architecture where
/* each process (or architectural layer) handles a problem as it
/* runs into it, instead of reporting problem details back to its
/* pipeline predecessor (or back to a higher architectural layer).
/* TLSRPT_WRAPPER IMPLEMENTATION
/* .ad
/* .fi
/* At a high level, the SMTP protocol engine encapsulates SMTP
/* session and TLS policy information in an opaque TLSRPT_WRAPPER
/* object, and passes that object to the TLS protocol engine. The
/* TLS protocol engine can invoke TLSRPT_WRAPPER methods to report a
/* TLS error through the sys4.de TLSRPT client library. In a similar
/* manner, the SMTP protocol engine can invoke TLSRPT_WRAPPER object
/* methods to report a TLS error or success.
/*
/* At a low level, The Postfix SMTP protocol engine (smtp_proto.c)
/* reports TLS errors when TLS support is required but unavailable,
/* or requests the Postfix TLS protocol engine to perform a TLS
/* protocol handshake over an open SMTP connection. The SMTP
/* protocol engine either calls the TLS protocol engine directly,
/* or calls it over local IPC in a tlsproxy(8) process.
/*
/* The TLS protocol engine may report a TLS error by invoking
/* TLSRPT_WRAPPER methods, and either returns no TLS session object,
/* or a TLS session object for a completed handshake. The TLS session
/* object will indicate if the TLS protocol engine reported any
/* TLS error through TLSRPT (for example an error that resulted in
/* a successful TLS handshake with a downgraded TLS security level).
/*
/* The Postfix SMTP protocol engine reports success or failure
/* by invoking TLSRPT_WRAPPER methods, depending on whether all
/* matching requirements were satisfied. The SMTP protocol engine
/* does not report success or failure by invoking TLSRPT_WRAPPER
/* methods if the TLS protocol engine already reported a failure.
/* TLSRPT_WRAPPER API
/* .ad
/* .fi
/* The functions below must be called in a specific order. All
/* string inputs are copied. If a required call is missing then
/* the request will be ignored, and a warning will be logged,
/* but this not affect email deliveries.
/* .PP
/* trw_create() must be called before other trw_xxx() requests can
/* be made. Arguments:
/* .IP rpt_socket_name
/* The name of a socket that will be managed by local TLSRPT
/* infrastructure.
/* .IP rpt_policy_domain
/* The TLSRPT policy domain name, i.e. the domain that wishes to
/* receive TLSRPT summary reports. An internationalized domain name
/* must be in A-label form (i.e. punycode).
/* .IP rpt_policy_string
/* The TLSRPT policy record content, i.e. how to submit TLSRPT
/* summary reports.
/* .PP
/* trw_free() destroys storage allocated with other trw_xxx()
/* requests.
/* .PP
/* trw_set_tls_policy() must be called by the SMTP protocol engine
/* after it found a DANE, STS, or no policy, and before it tries to
/* establish a new SMTP connection. This function clears information
/* that was specified earlier with trw_set_tls_policy() or
/* trw_set_tcp_connection(), and resets whether trw_report_failure()
/* or trw_report_success() were called. Mapping from arguments to
/* TLSRPT report fields:
/* .IP tls_policy_type
/* policies[].policy.policy-type.
/* .IP tls_policy_strings (may be null)
/* policies[].policy.policy-string[]. Ignored if the tls_policy_type
/* value is TLSRPT_NO_POLICY_FOUND.
/* .IP tls_policy_domain (may be null)
/* policies[].policy.policy-domain.
/* .IP mx_host_patterns (may be null)
/* policies[].policy.mx-host[]. Ignored if the tls_policy_type
/* value is TLSRPT_NO_POLICY_FOUND.
/* .PP
/* trw_set_tcp_connection() and trw_set_ehlo_resp() are optionally
/* called by the SMTP protcol engine, after it has established
/* a new SMTP connection, before it requests a TLS protocol
/* handshake. Mapping from arguments to TLSRPT report fields:
/* .IP snd_mta_addr (may be null)
/* policies[].failure-details[].sending-mta-ip.
/* .IP rcv_mta_name (may be null)
/* policies[].failure-details[].receiving-mx-hostname.
/* .IP rcv_mta_addr (may be null)
/* policies[].failure-details[].receiving-ip.
/* .PP
/* trw_set_ehlo_resp() is optionally called by the SMTP protcol
/* engine to pass on the EHLO response. Presumably this is the EHLO
/* response before STARTTLS (TLSRPT is primarily interested in
/* pre-handshake and handshake errors).
/* .IP rcv_mta_ehlo (may be null)
/* policies[].failure-details[].receiving-mx-helo.
/* .PP
/* trw_report_failure() is called by the TLS protocol engine or
/* SMTP protocol engine to report a TLS error. The result value
/* is 0 for success, -1 for failure as indicated with the errno
/* value. The call is successfully skipped if information is missing
/* or if failure or success were already reported for the
/* connection. Mapping from arguments to TLSRPT report fields:
/* .IP failure_type
/* policies[].failure-details[].result-type.
/* .IP additional_info (may be null)
/* policies[].failure-details[].additional-information.
/* .IP failure_reason (may be null)
/* policies[].failure-details[].failure-reason-code
/* .PP
/* trw_report_success() is called by the SMTP protocol engine
/* to report a successful TLS handshake. The result value is
/* 0 for success, -1 for failure with errno indicating the
/* error type. The call is successfully skipped if information if
/* missing or if failure or success were already reported for
/* the connection.
/* .PP
/* trw_is_reported() returns non-zero when the contents of the
/* specified TLSRPT_WRAPPER have been reported.
/* .PP
/* convert_tlsrpt_policy_type() and convert_tlsrpt_policy_failure()
/* convert a valid policy type or failure name to the corresponding
/* enum value. The result is < 0 if the name is not valid.
/* .PP
/* valid_tlsrpt_policy_type() and valid_tlsrpt_policy_failure()
/* return non-zero if the specified policy type or failure name
/* is valid in TLSRPT. These functions do not require that the
/* module is built with TLSRPT support. This allows the names to
/* be used even if TLSRPT is disabled.
/* DIAGNOSTICS
/* Some functions will log a warning when information is missing.
/* Such warnings will not affect the operation of the SMTP or TLS
/* protocol engine.
/* BUGS
/* This implementation is suitable to report successful TLS policy
/* compliance, and to report a failure that prevents TLS policy
/* compliance (example: all TLSA records are unusable). Do not use
/* this implementation to report other errors (example: some TLSA
/* record is non-parsable).
/* SEE ALSO
/* https://github.com/sys4/tlsrpt, TLSRPT client library
/* LICENSE
/* .ad
/* .fi
/* The Secure Mailer license must be distributed with this software.
/* AUTHOR(S)
/* Wietse Venema
/* porcupine.org
/*--*/
#if defined(USE_TLS)
/*
* System library.
*/
#include <sys_defs.h>
#include <errno.h>
#include <string.h>
#if defined(USE_TLSRPT)
#include <tlsrpt.h>
#endif
/*
* Utility library.
*/
#include <argv.h>
#include <msg.h>
#include <mymalloc.h>
#include <name_code.h>
#include <stringops.h>
/*
* Some functions are not #ifdef USE_TLSRPT.
*/
#define TLSRPT_WRAPPER_INTERNAL
#include <tlsrpt_wrapper.h>
#if defined(USE_TLSRPT)
/*
* Macros to make repetitive code more readable.
*/
#define MYFREE_IF_SET(member) do { \
if (member) \
myfree(member); \
} while (0)
#define MYFREE_IF_SET_AND_CLEAR(member, value) do { \
if (member) { \
myfree(member); \
(member) = 0; \
} \
} while (0)
#define MYFREE_IF_SET_AND_COPY(member, value) do { \
MYFREE_IF_SET(member); \
(member) = (value) ? mystrdup(value) : 0; \
} while (0)
#define ARGV_FREE_IF_SET(member) do { \
if (member) \
argv_free(member); \
} while (0)
#define ARGV_FREE_IF_SET_AND_CLEAR(member) do { \
if (member) { \
argv_free(member); \
(member) = 0; \
} \
} while (0)
#define ARGV_FREE_IF_SET_AND_COPY(member, value) do { \
ARGV_FREE_IF_SET(member); \
(member) = (value) ? argv_addv((ARGV *) 0, value) : 0; \
} while (0)
/* trw_create - create initial TLSRPT_WRAPPER instance */
TLSRPT_WRAPPER *trw_create(const char *rpt_socket_name,
const char *rpt_policy_domain,
const char *rpt_policy_string)
{
TLSRPT_WRAPPER *trw;
/*
* memset() is not portable for pointer etc. types.
*/
trw = (TLSRPT_WRAPPER *) mymalloc(sizeof(*trw));
trw->rpt_socket_name = mystrdup(rpt_socket_name);
trw->rpt_policy_domain = mystrdup(rpt_policy_domain);
trw->rpt_policy_string = mystrdup(rpt_policy_string);;
trw->tls_policy_type = 0;
trw->tls_policy_strings = 0;
trw->tls_policy_domain = 0;
trw->mx_host_patterns = 0;
trw->snd_mta_addr = 0;
trw->rcv_mta_name = 0;
trw->rcv_mta_addr = 0;
trw->rcv_mta_ehlo = 0;
trw->flags = 0;
return (trw);
}
/* trw_free - destroy TLSRPT_WRAPPER instance. */
void trw_free(TLSRPT_WRAPPER *trw)
{
/* Destroy fields set with trw_create(). */
myfree(trw->rpt_socket_name);
myfree(trw->rpt_policy_domain);
myfree(trw->rpt_policy_string);
/* Destroy fields set with trw_set_tls_policy(). */
ARGV_FREE_IF_SET(trw->tls_policy_strings);
MYFREE_IF_SET(trw->tls_policy_domain);
ARGV_FREE_IF_SET(trw->mx_host_patterns);
/* Destroy fields set with trw_set_tcp_connection(). */
trw_set_tcp_connection(trw, (char *) 0, (char *) 0, (char *) 0);
/* Destroy fields set with trw_set_ehlo_resp(). */
trw_set_ehlo_resp(trw, (char *) 0);
/* That's all. */
myfree((void *) trw);
}
/* trw_set_tls_policy - set TLS policy info, clear SMTP info */
void trw_set_tls_policy(TLSRPT_WRAPPER *trw,
tlsrpt_policy_type_t tls_policy_type,
const char *const * tls_policy_strings,
const char *tls_policy_domain,
const char *const * mx_host_patterns)
{
trw->tls_policy_type = tls_policy_type;
MYFREE_IF_SET_AND_COPY(trw->tls_policy_domain, tls_policy_domain);
if (tls_policy_type == TLSRPT_NO_POLICY_FOUND) {
ARGV_FREE_IF_SET_AND_CLEAR(trw->tls_policy_strings);
ARGV_FREE_IF_SET_AND_CLEAR(trw->tls_policy_strings);
} else {
ARGV_FREE_IF_SET_AND_COPY(trw->tls_policy_strings, tls_policy_strings);
ARGV_FREE_IF_SET_AND_COPY(trw->mx_host_patterns, mx_host_patterns);
}
trw->flags = TRW_FLAG_HAVE_TLS_POLICY;
trw_set_tcp_connection(trw, (char *) 0, (char *) 0, (char *) 0);
trw_set_ehlo_resp(trw, (char *) 0);
}
/* trw_set_tcp_connection - set SMTP endpoint info */
void trw_set_tcp_connection(TLSRPT_WRAPPER *trw,
const char *snd_mta_addr,
const char *rcv_mta_name,
const char *rcv_mta_addr)
{
const char myname[] = "trw_set_tcp_connection";
/*
* Sanity check: usage errors are not a show stopper.
*/
if ((snd_mta_addr || rcv_mta_name || rcv_mta_addr)
&& ((trw->flags & TRW_FLAG_HAVE_TLS_POLICY) == 0
|| (trw->flags & TRW_FLAG_REPORTED))) {
msg_warn("%s: missing trw_set_tls_policy call", myname);
return;
}
MYFREE_IF_SET_AND_COPY(trw->snd_mta_addr, snd_mta_addr);
MYFREE_IF_SET_AND_COPY(trw->rcv_mta_name, rcv_mta_name);
MYFREE_IF_SET_AND_COPY(trw->rcv_mta_addr, rcv_mta_addr);
}
/* trw_set_ehlo_resp - set EHLO response */
void trw_set_ehlo_resp(TLSRPT_WRAPPER *trw, const char *rcv_mta_ehlo)
{
const char myname[] = "trw_set_ehlo_resp";
/*
* Sanity check: usage errors are not a show stopper.
*/
if (rcv_mta_ehlo && ((trw->flags & TRW_FLAG_HAVE_TLS_POLICY) == 0
|| (trw->flags & TRW_FLAG_REPORTED))) {
msg_warn("%s: missing trw_set_tls_policy call", myname);
return;
}
MYFREE_IF_SET_AND_COPY(trw->rcv_mta_ehlo, rcv_mta_ehlo);
}
/* trw_munge_report_result - helper to map and log libtlsrpt result value */
static int trw_munge_report_result(int libtlsrpt_errorcode)
{
int err;
/*
* First, deal with the non-error cases.
*/
if (libtlsrpt_errorcode == 0) {
return (0);
}
/*
* Report a tlsrpt library internal error.
*/
else if (tlsrpt_error_code_is_internal(libtlsrpt_errorcode)) {
msg_warn("Could not report TLS handshake result to tlsrpt library:"
" %s (error %d)", tlsrpt_strerror(libtlsrpt_errorcode),
libtlsrpt_errorcode);
return (-1);
}
/*
* Report a libc error. Do not report success if errno was zero. When
* debug logging is enabled, also log some library-internal info.
*/
else {
err = tlsrpt_errno_from_error_code(libtlsrpt_errorcode);
msg_warn("Could not report TLS handshake result to tlsrpt library:"
" %s (errno %d)", mystrerror(err), err);
if (msg_verbose)
msg_warn("Error location in tlsrpt library: %s (error %d)",
tlsrpt_strerror(libtlsrpt_errorcode),
libtlsrpt_errorcode);
errno = err;
return (-1);
}
}
/* trw_tlsrpt_failure_to_string - make debug logging readable */
static const char *trw_failure_type_to_string(tlsrpt_failure_t failure_type)
{
static const NAME_CODE failure_types[] = {
"starttls_not_supported", TLSRPT_STARTTLS_NOT_SUPPORTED,
"certificate_host_mismatch", TLSRPT_CERTIFICATE_HOST_MISMATCH,
"certificate_not_trusted", TLSRPT_CERTIFICATE_NOT_TRUSTED,
"certificate_expired", TLSRPT_CERTIFICATE_EXPIRED,
"validation_failure", TLSRPT_VALIDATION_FAILURE,
"sts_policy_fetch_error", TLSRPT_STS_POLICY_FETCH_ERROR,
"sts_policy_invalid", TLSRPT_STS_POLICY_INVALID,
"sts_webpki_invalid", TLSRPT_STS_WEBPKI_INVALID,
"tlsa_invalid", TLSRPT_TLSA_INVALID,
"dnssec_invalid", TLSRPT_DNSSEC_INVALID,
"dane_required", TLSRPT_DANE_REQUIRED,
"unfinished_policY", TLSRPT_UNFINISHED_POLICY,
0, -1
};
const char *cp;
static VSTRING *buf;
if ((cp = str_name_code(failure_types, failure_type)) == 0) {
if (buf == 0)
buf = vstring_alloc(20);
msg_warn("unknown tlsrpt_failure_t value %d", failure_type);
vstring_sprintf(buf, "failure_type_%d", failure_type);
cp = vstring_str(buf);
}
return (cp);
}
/* trw_report_failure - one-shot failure reporter */
int trw_report_failure(TLSRPT_WRAPPER *trw,
tlsrpt_failure_t failure_type,
const char *additional_info,
const char *failure_reason)
{
const char myname[] = "trw_report_failure";
struct tlsrpt_connection_t *con;
int res;
/*
* Sanity check: usage errors are not a show stopper.
*/
if ((trw->flags & TRW_FLAG_HAVE_TLS_POLICY) == 0) {
msg_warn("%s: missing trw_set_tls_policy call", myname);
return (0);
}
/*
* Report a failure only when it is seen first. If a failure was already
* reported by a lower-level function close to the root cause, then skip
* the less detailed failure report from a later caller who is further
* away from the point where trouble was found.
*
* TODO(wietse) Is it worthwhile to distinguish between failure versus
* success already reported?
*/
if (trw->flags & TRW_FLAG_REPORTED) {
if (msg_verbose)
msg_info("%s: success or failure already reported", myname);
return (0);
}
trw->flags |= TRW_FLAG_REPORTED;
/* Give the local admin a clue. */
msg_info("TLSRPT: status=failure, domain=%s, receiving_mx=%s[%s],"
" failure_type=%s%s%s",
trw->rpt_policy_domain, trw->rcv_mta_name, trw->rcv_mta_addr,
trw_failure_type_to_string(failure_type),
failure_reason ? ", failure_reason=" : "",
failure_reason ? failure_reason : "");
if ((res = tlsrpt_open(&con, trw->rpt_socket_name)) == 0) {
struct tlsrpt_dr_t *dr;
char **cpp;
if ((res = tlsrpt_init_delivery_request(&dr, con,
trw->rpt_policy_domain,
trw->rpt_policy_string)) == 0) {
if ((res = tlsrpt_init_policy(dr, trw->tls_policy_type,
trw->tls_policy_domain)) == 0) {
if (trw->tls_policy_strings)
for (cpp = trw->tls_policy_strings->argv;
res == 0 && *cpp; cpp++)
res = tlsrpt_add_policy_string(dr, *cpp);
if (trw->mx_host_patterns)
for (cpp = trw->mx_host_patterns->argv;
res == 0 && *cpp; cpp++)
res = tlsrpt_add_mx_host_pattern(dr, *cpp);
if (res == 0)
res = tlsrpt_add_delivery_request_failure(dr,
/* failure_code= */ failure_type,
/* sending_mta_ip= */ trw->snd_mta_addr,
/* receiving_mx_hostname= */ trw->rcv_mta_name,
/* receiving_mx_helo= */ trw->rcv_mta_ehlo,
/* receiving_ip= */ trw->rcv_mta_addr,
/* additional_information= */ additional_info,
/* failure_reason_code= */ failure_reason);
if (res == 0)
res = tlsrpt_finish_policy(dr, TLSRPT_FINAL_FAILURE);
}
if (res == 0) {
res = tlsrpt_finish_delivery_request(&dr);
} else {
(void) tlsrpt_cancel_delivery_request(&dr);
}
}
(void) tlsrpt_close(&con);
}
return (trw_munge_report_result(res));
}
/* trw_report_success - one-shot success reporter */
int trw_report_success(TLSRPT_WRAPPER *trw)
{
const char myname[] = "trw_report_success";
struct tlsrpt_connection_t *con;
int res;
/*
* Sanity check: usage errors are not a show stopper.
*/
if ((trw->flags & TRW_FLAG_HAVE_TLS_POLICY) == 0) {
msg_warn("%s: missing trw_set_tls_policy call", myname);
return (0);
}
/* This should not happen. Log a warning. */
if (trw->flags & TRW_FLAG_REPORTED) {
msg_warn("%s: success or failure was already reported", myname);
return (0);
}
trw->flags |= TRW_FLAG_REPORTED;
/* Give the local admin a clue. */
msg_info("TLSRPT: status=success, domain=%s, receiving_mx=%s[%s]",
trw->rpt_policy_domain, trw->rcv_mta_name, trw->rcv_mta_addr);
if ((res = tlsrpt_open(&con, trw->rpt_socket_name)) == 0) {
struct tlsrpt_dr_t *dr;
if ((res = tlsrpt_init_delivery_request(&dr, con,
trw->rpt_policy_domain,
trw->rpt_policy_string)) == 0) {
if ((res = tlsrpt_init_policy(dr, trw->tls_policy_type,
trw->tls_policy_domain)) == 0) {
char **cpp;
if (trw->tls_policy_strings)
for (cpp = trw->tls_policy_strings->argv;
res == 0 && *cpp; cpp++)
res = tlsrpt_add_policy_string(dr, *cpp);
if (trw->mx_host_patterns)
for (cpp = trw->mx_host_patterns->argv;
res == 0 && *cpp; cpp++)
res = tlsrpt_add_mx_host_pattern(dr, *cpp);
if (res == 0)
res = tlsrpt_finish_policy(dr, TLSRPT_FINAL_SUCCESS);
}
if (res == 0) {
res = tlsrpt_finish_delivery_request(&dr);
} else {
(void) tlsrpt_cancel_delivery_request(&dr);
}
}
(void) tlsrpt_close(&con);
}
return (trw_munge_report_result(res));
}
/* trw_is_reported - trw_report_success() or trw_report_failure() called */
int trw_is_reported(const TLSRPT_WRAPPER *trw)
{
return (trw->flags & TRW_FLAG_REPORTED);
}
#endif /* USE_TLS_RPT */
/*
* Dummy definitions for builds without the TLSRPT library, so that we can
* still validate names.
*/
#if !defined(USE_TLSRPT)
#define TLSRPT_POLICY_DANE 0
#define TLSRPT_POLICY_STS 0
#define TLSRPT_NO_POLICY_FOUND 0
#define TLSRPT_VALIDATION_FAILURE 0
#define TLSRPT_STS_POLICY_FETCH_ERROR 0
#define TLSRPT_STS_POLICY_INVALID 0
#define TLSRPT_STS_WEBPKI_INVALID 0
#endif
/*
* Mapping from RFC 8460 string to libtlsrpt enum for policy types and
* policy failures. The mapping assumes that all enum values are
* non-negative.
*/
const NAME_CODE tlsrpt_policy_type_mapping[] = {
"sts", TLSRPT_POLICY_STS,
"no-policy-found", TLSRPT_NO_POLICY_FOUND,
0, -1,
};
const NAME_CODE tlsrpt_policy_failure_mapping[] = {
"sts-policy-fetch-error", TLSRPT_STS_POLICY_FETCH_ERROR,
"sts-policy-invalid", TLSRPT_STS_POLICY_INVALID,
"sts-webpki-invalid", TLSRPT_STS_WEBPKI_INVALID,
"validation-failure", TLSRPT_VALIDATION_FAILURE,
0, -1,
};
/* valid_tlsrpt_policy_type - validate policy_type attribute value */
int valid_tlsrpt_policy_type(const char *policy_type)
{
return (name_code(tlsrpt_policy_type_mapping, NAME_CODE_FLAG_NONE,
policy_type) >= 0);
}
/* valid_tlsrpt_policy_failure - validate policy_failure attribute value */
int valid_tlsrpt_policy_failure(const char *policy_failure)
{
return (name_code(tlsrpt_policy_failure_mapping, NAME_CODE_FLAG_NONE,
policy_failure) >= 0);
}
#if defined(USE_TLSRPT)
/* convert_tlsrpt_policy_type - convert string to enum */
tlsrpt_policy_type_t convert_tlsrpt_policy_type(const char *policy_type)
{
return (name_code(tlsrpt_policy_type_mapping, NAME_CODE_FLAG_NONE,
policy_type));
}
/* convert_tlsrpt_policy_failure - convert string to enum */
tlsrpt_failure_t convert_tlsrpt_policy_failure(const char *policy_failure)
{
return (name_code(tlsrpt_policy_failure_mapping, NAME_CODE_FLAG_NONE,
policy_failure));
}
#endif /* USE_TLSRPT */
#endif /* USE_TLS */

View File

@ -0,0 +1,122 @@
#ifndef _TLSRPT_WRAPPER_INCLUDED_
#define _TLSRPT_WRAPPER_INCLUDED_
/*++
/* NAME
/* tlsrpt_wrapper 3h
/* SUMMARY
/* TLSRPT support for the SMTP and TLS protocol engines
/* SYNOPSIS
/* #include <tlsrpt_wrapper.h>
/* DESCRIPTION
/* .nf
/*
* System library.
*/
#if defined(USE_TLS)
#if defined(USE_TLSRPT)
#include <tlsrpt.h>
/*
* External interface, with convenient setters for each SMTP protocol engine
* stage. Many functions have multiple arguments of the same type. Include
* parameter names in function prototypes here, and in call sites include
* comments before parameter values, to prepare for future clang-tidy
* bugprone-argument-comment checks.
*/
typedef struct TLSRPT_WRAPPER TLSRPT_WRAPPER;
extern TLSRPT_WRAPPER *trw_create(const char *rpt_socket_name,
const char *rpt_policy_domain,
const char *rpt_policy_string);
extern void trw_free(TLSRPT_WRAPPER *trw);
extern void trw_set_tls_policy(TLSRPT_WRAPPER *trw,
tlsrpt_policy_type_t tls_policy_type,
const char *const * tls_policy_strings,
const char *tls_policy_domain,
const char *const * mx_policy_patterns);
extern void trw_set_tcp_connection(TLSRPT_WRAPPER *trw,
const char *snd_mta_addr,
const char *rcv_mta_name,
const char *rcv_mta_addr);
extern void trw_set_ehlo_resp(TLSRPT_WRAPPER *trw,
const char *rcv_mta_ehlo);
extern int trw_report_failure(TLSRPT_WRAPPER *trw,
tlsrpt_failure_t policy_failure,
const char *additional_info,
const char *failure_reason);
extern int trw_report_success(TLSRPT_WRAPPER *trw);
extern int trw_is_reported(const TLSRPT_WRAPPER *trw);
/*
* The internals declarations are also needed for functions that transmit
* and receive TLSRPT_WRAPPER objects.
*/
#ifdef TLSRPT_WRAPPER_INTERNAL
/*
* Utility library.
*/
#include <argv.h>
struct TLSRPT_WRAPPER {
/* Set with trw_create(). */
char *rpt_socket_name;
char *rpt_policy_domain;
char *rpt_policy_string;
/* Set with trw_set_policy(). */
tlsrpt_policy_type_t tls_policy_type;
ARGV *tls_policy_strings;
char *tls_policy_domain;
ARGV *mx_host_patterns;
/* Set with trw_set_tcp_connection(). */
char *snd_mta_addr;
char *rcv_mta_name;
char *rcv_mta_addr;
/* Set with trw_set_ehlo_resp(). */
char *rcv_mta_ehlo;
int flags;
};
#define TRW_FLAG_HAVE_TLS_POLICY (1<<0)
#define TRW_FLAG_HAVE_TCP_CONN (1<<1)
#define TRW_FLAG_HAVE_EHLO_RESP (1<<2)
#define TRW_FLAG_REPORTED (1<<3)
#define TRW_RPT_SOCKET_NAME "rpt_socket_name"
#define TRW_RPT_POLICY_DOMAIN "rpt_policy_domain"
#define TRW_RPT_POLICY_STRING "rpt_policy_string"
#define TRW_TLS_POLICY_TYPE "tls_policy_type"
#define TRW_TLS_POLICY_STRINGS "tls_policy_strings" /* XXX Not checked */
#define TRW_TLS_POLICY_DOMAIN "tls_policy_domain"
#define TRW_MX_HOST_PATTERNS "mx_host_patterns" /* XXX Not checked */
#define TRW_SRC_MTA_ADDR "snd_mta_addr"
#define TRW_DST_MTA_NAME "rcv_mta_name"
#define TRW_DST_MTA_ADDR "rcv_mta_addr"
#define TRW_DST_MTA_EHLO "rcv_mta_ehlo" /* Optional */
#define TRW_FLAGS "flags"
#endif /* TLSRPT_WRAPPER_INTERNAL */
extern tlsrpt_policy_type_t convert_tlsrpt_policy_type(const char *policy_type);
extern tlsrpt_failure_t convert_tlsrpt_policy_failure(const char *policy_failure);
#endif /* USE_TLSRPT */
extern int valid_tlsrpt_policy_type(const char *policy_type);
extern int valid_tlsrpt_policy_failure(const char *policy_failure);
#endif /* USE_TLS */
/* LICENSE
/* .ad
/* .fi
/* The Secure Mailer license must be distributed with this software.
/* AUTHOR(S)
/* Wietse Venema
/*--*/
#endif /* _TLSRPT_WRAPPER_INCLUDED_ */

View File

@ -75,6 +75,7 @@ tlsproxy.o: ../../include/split_at.h
tlsproxy.o: ../../include/sys_defs.h
tlsproxy.o: ../../include/tls.h
tlsproxy.o: ../../include/tls_proxy.h
tlsproxy.o: ../../include/tlsrpt_wrapper.h
tlsproxy.o: ../../include/vbuf.h
tlsproxy.o: ../../include/vstream.h
tlsproxy.o: ../../include/vstring.h

View File

@ -426,6 +426,7 @@
#define TLS_INTERNAL /* XXX */
#include <tls.h>
#include <tls_proxy.h>
#include <tlsrpt_wrapper.h>
/*
* Application-specific.
@ -731,6 +732,20 @@ static int tlsp_eval_tls_error(TLSP_STATE *state, int err)
state->flags |= TLSP_FLAG_NO_MORE_CIPHERTEXT_IO;
return (TLSP_STAT_OK);
}
/*
* Report a generic failure only if a more specific failure wasn't
* already reported.
*/
#ifdef USE_TLSRPT
if (state->client_start_props->tlsrpt
&& (state->flags & TLSP_FLAG_DO_HANDSHAKE)
&& state->is_server_role == 0)
trw_report_failure(state->client_start_props->tlsrpt,
TLSRPT_VALIDATION_FAILURE,
/* additional_info= */ (char *) 0,
"tls-handshake-failure");
#endif
tlsp_state_free(state);
return (TLSP_STAT_ERR);
}

View File

@ -45,7 +45,8 @@ SRCS = alldig.c allprint.c argv.c argv_split.c attr_clnt.c attr_print0.c \
byte_mask.c known_tcp_ports.c argv_split_at.c dict_stream.c \
sane_strtol.c hash_fnv.c ldseed.c mkmap_cdb.c mkmap_db.c mkmap_dbm.c \
mkmap_fail.c mkmap_lmdb.c mkmap_open.c mkmap_sdbm.c inet_prefix_top.c \
inet_addr_sizes.c quote_for_json.c
inet_addr_sizes.c quote_for_json.c mystrerror.c \
sane_sockaddr_to_hostaddr.c
OBJS = alldig.o allprint.o argv.o argv_split.o attr_clnt.o attr_print0.o \
attr_print64.o attr_print_plain.o attr_scan0.o attr_scan64.o \
attr_scan_plain.o auto_clnt.o base64_code.o basename.o binhash.o \
@ -92,7 +93,7 @@ OBJS = alldig.o allprint.o argv.o argv_split.o attr_clnt.o attr_print0.o \
byte_mask.o known_tcp_ports.o argv_split_at.o dict_stream.o \
sane_strtol.o hash_fnv.o ldseed.o mkmap_db.o mkmap_dbm.o \
mkmap_fail.o mkmap_open.o inet_prefix_top.o inet_addr_sizes.o \
quote_for_json.o
quote_for_json.o mystrerror.o sane_sockaddr_to_hostaddr.o
# MAP_OBJ is for maps that may be dynamically loaded with dynamicmaps.cf.
# When hard-linking these, makedefs sets NON_PLUGIN_MAP_OBJ=$(MAP_OBJ),
# otherwise it sets the PLUGIN_* macros.
@ -2444,6 +2445,12 @@ mymalloc.o: sys_defs.h
myrand.o: myrand.c
myrand.o: myrand.h
myrand.o: sys_defs.h
mystrerror.o: check_arg.h
mystrerror.o: mystrerror.c
mystrerror.o: stringops.h
mystrerror.o: sys_defs.h
mystrerror.o: vbuf.h
mystrerror.o: vstring.h
mystrtok.o: check_arg.h
mystrtok.o: msg.h
mystrtok.o: mystrtok.c
@ -2549,9 +2556,11 @@ printable.o: stringops.h
printable.o: sys_defs.h
printable.o: vbuf.h
printable.o: vstring.h
quote_for_json.o: check_arg.h
quote_for_json.o: quote_for_json.c
quote_for_json.o: stringops.h
quote_for_json.o: sys_defs.h
quote_for_json.o: vbuf.h
quote_for_json.o: vstring.h
rand_sleep.o: iostuff.h
rand_sleep.o: msg.h
@ -2617,6 +2626,10 @@ sane_rename.o: sane_fsops.h
sane_rename.o: sane_rename.c
sane_rename.o: sys_defs.h
sane_rename.o: warn_stat.h
sane_sockaddr_to_hostaddr.o: inet_proto.h
sane_sockaddr_to_hostaddr.o: myaddrinfo.h
sane_sockaddr_to_hostaddr.o: sane_sockaddr_to_hostaddr.c
sane_sockaddr_to_hostaddr.o: sys_defs.h
sane_socketpair.o: msg.h
sane_socketpair.o: sane_socketpair.c
sane_socketpair.o: sane_socketpair.h
@ -2883,6 +2896,7 @@ vbuf.o: vbuf.h
vbuf_print.o: check_arg.h
vbuf_print.o: msg.h
vbuf_print.o: mymalloc.h
vbuf_print.o: stringops.h
vbuf_print.o: sys_defs.h
vbuf_print.o: vbuf.h
vbuf_print.o: vbuf_print.c

View File

@ -31,6 +31,10 @@
/* char *arg;
/* ssize_t arg_len;
/*
/* ARGV *argv_addv(argvp, argv)
/* ARGV *argvp;
/* const char **argv;
/*
/* void argv_terminate(argvp);
/* ARGV *argvp;
/*
@ -94,6 +98,10 @@
/* argv_addn() is like argv_add(), but each string is followed
/* by a string length argument.
/*
/* argv_addv() optionally creates an ARGV when the first argument
/* is a null pointer, and appends a null-terminated list of
/* strings. The result is null terminated.
/*
/* argv_free() releases storage for a string array, and conveniently
/* returns a null pointer.
/*
@ -144,6 +152,9 @@
/* Google, Inc.
/* 111 8th Avenue
/* New York, NY 10011, USA
/*
/* Wietse Venema
/* porcupine.org
/*--*/
/* System libraries. */
@ -301,6 +312,23 @@ void argv_addn(ARGV *argvp,...)
argvp->argv[argvp->argc] = 0;
}
/* argv_addv - optionally create ARGV, append string vector */
ARGV *argv_addv(ARGV *argvp, const char *const * argv)
{
const char *const * cpp;
if (argvp == 0) {
for (cpp = argv; *cpp; cpp++)
/* void */ ;
argvp = argv_alloc(cpp - argv);
}
for (cpp = argv; *cpp; cpp++)
argv_add(argvp, *cpp, (char *) 0);
argvp->argv[argvp->argc] = 0;
return (argvp);
}
/* argv_terminate - terminate string array */
void argv_terminate(ARGV *argvp)
@ -602,6 +630,23 @@ static ARGV *test_argv_join(const TEST_CASE *tp, ARGV *argvp)
return (argvp);
}
/* test_argv_addv_appends - populate result */
static ARGV *test_argv_addv_appends(const TEST_CASE *tp, ARGV *argvp)
{
argvp = argv_addv(argvp, tp->inputs);
return (argvp);
}
/* test_argv_addv_creates_appends - populate result */
static ARGV *test_argv_addv_creates(const TEST_CASE *tp, ARGV *argvp)
{
argv_free(argvp);
argvp = argv_addv((ARGV *) 0, tp->inputs);
return (argvp);
}
/* test_argv_verify - verify result */
static int test_argv_verify(const TEST_CASE *tp, ARGV *argvp)
@ -737,6 +782,14 @@ static const TEST_CASE test_cases[] = {
{0}, 0, test_argv_join,
0, 1, {"", 0}, ':'
},
{"argv_addv appends to ARGV",
{"foo", "baz", "bar", 0}, /* ignored */ 0, test_argv_addv_appends,
0, 3, {"foo", "baz", "bar", 0}
},
{"argv_addv creates ARGV",
{"foo", "baz", "bar", 0}, /* ignored */ 0, test_argv_addv_creates,
0, 3, {"foo", "baz", "bar", 0}
},
0,
};

View File

@ -20,7 +20,7 @@ typedef struct ARGV {
char **argv; /* string array */
} ARGV;
typedef int (*ARGV_COMPAR_FN)(const void *, const void *);
typedef int (*ARGV_COMPAR_FN) (const void *, const void *);
extern ARGV *argv_alloc(ssize_t);
extern ARGV *argv_sort(ARGV *); /* backwards compatibility */
@ -28,6 +28,7 @@ extern ARGV *argv_qsort(ARGV *, ARGV_COMPAR_FN);
extern ARGV *argv_uniq(ARGV *, ARGV_COMPAR_FN);
extern void argv_add(ARGV *,...);
extern void argv_addn(ARGV *,...);
extern ARGV *argv_addv(ARGV *, const char *const *);
extern void argv_terminate(ARGV *);
extern void argv_truncate(ARGV *, ssize_t);
extern void argv_insert_one(ARGV *, ssize_t, const char *);

View File

@ -44,6 +44,8 @@
/* functionality.
/* .IP HEX_ENCODE_FLAG_USE_COLON
/* Inserts one ":" between bytes.
/* .IP HEX_ENCODE_FLAG_APPEND
/* Append output to the buffer.
/* .PP
/* hex_decode_opt() enables extended functionality as controlled
/* with \fIflags\fR.
@ -69,6 +71,9 @@
/* Google, Inc.
/* 111 8th Avenue
/* New York, NY 10011, USA
/*
/* Wietse Venema
/* porcupine.org
/*--*/
/* System library. */
@ -107,6 +112,7 @@ VSTRING *hex_encode_opt(VSTRING *result, const char *in, ssize_t len, int flags)
int ch;
ssize_t count;
if ((flags & HEX_ENCODE_FLAG_APPEND) == 0)
VSTRING_RESET(result);
for (cp = UCHAR_PTR(in), count = len; count > 0; count--, cp++) {
ch = *cp;
@ -177,67 +183,121 @@ VSTRING *hex_decode_opt(VSTRING *result, const char *in, ssize_t len, int flags)
}
#ifdef TEST
#include <argv.h>
/*
* Proof-of-concept test program: convert to hexadecimal and back.
*/
#define STR(x) vstring_str(x)
#define LEN(x) VSTRING_LEN(x)
typedef struct TEST_CASE {
const char *label; /* identifies test case */
VSTRING *(*func) (VSTRING *, const char *, ssize_t, int);
const char *input; /* input string */
ssize_t inlen; /* input size */
int flags; /* flags */
const char *exp_output; /* expected output or null */
ssize_t exp_outlen; /* expected size */
} TEST_CASE;
/*
* The test cases.
*/
#define OUTPUT_INIT "thrash:" /* output buffer initial content */
#define OUTPUT_INIT_SZ (sizeof(OUTPUT_INIT) - 1)
static const TEST_CASE test_cases[] = {
{"hex_encode_no_options", hex_encode_opt,
"this is a test",
sizeof("this is a test") - 1,
HEX_ENCODE_FLAG_NONE,
"7468697320697320612074657374",
sizeof("7468697320697320612074657374") - 1,
},
{"hex_decode_no_options", hex_decode_opt,
"7468697320697320612074657374",
sizeof("7468697320697320612074657374") - 1,
HEX_DECODE_FLAG_NONE,
"this is a test",
sizeof("this is a test") - 1,
},
{"hex_decode_no_colon_allow_colon", hex_decode_opt,
"7468697320697320612074657374",
sizeof("7468697320697320612074657374") - 1,
HEX_DECODE_FLAG_ALLOW_COLON,
"this is a test",
sizeof("this is a test") - 1,
},
{"hex_encode_appends", hex_encode_opt,
"this is a test",
sizeof("this is a test") - 1,
HEX_ENCODE_FLAG_APPEND,
OUTPUT_INIT "7468697320697320612074657374",
sizeof(OUTPUT_INIT "7468697320697320612074657374") - 1,
},
{"hex_encode_with_colon", hex_encode_opt,
"this is a test",
sizeof("this is a test") - 1,
HEX_ENCODE_FLAG_USE_COLON,
"74:68:69:73:20:69:73:20:61:20:74:65:73:74",
sizeof("74:68:69:73:20:69:73:20:61:20:74:65:73:74") - 1,
},
{"hex_encode_with_colon_and_append", hex_encode_opt,
"this is a test",
sizeof("this is a test") - 1,
HEX_ENCODE_FLAG_USE_COLON | HEX_ENCODE_FLAG_APPEND,
OUTPUT_INIT "74:68:69:73:20:69:73:20:61:20:74:65:73:74",
sizeof(OUTPUT_INIT "74:68:69:73:20:69:73:20:61:20:74:65:73:74") - 1,
},
{"hex_decode_error", hex_decode_opt,
"this is a test",
sizeof("this is a test") - 1,
HEX_DECODE_FLAG_ALLOW_COLON,
0,
0,
},
{0},
};
int main(int unused_argc, char **unused_argv)
{
VSTRING *b1 = vstring_alloc(1);
VSTRING *b2 = vstring_alloc(1);
char *test = "this is a test";
ARGV *argv;
VSTRING *buf = vstring_alloc(1);
int pass = 0;
int fail = 0;
const TEST_CASE *tp;
#define DECODE(b,x,l) { \
if (hex_decode((b),(x),(l)) == 0) \
msg_panic("bad hex: %s", (x)); \
for (tp = test_cases; tp->label != 0; tp++) {
VSTRING *out;
int ok = 0;
msg_info("RUN %s", tp->label);
vstring_memcpy(buf, OUTPUT_INIT, OUTPUT_INIT_SZ);
out = tp->func(buf, tp->input, tp->inlen, tp->flags);
if (out == 0 && tp->exp_output == 0) {
ok = 1;
} else if (out != buf) {
msg_warn("got result '%p', want: '%p'",
(void *) out, (void *) buf);
} else if (LEN(out) != tp->exp_outlen) {
msg_warn("got result length '%ld', want: '%ld'",
(long) LEN(out), (long) tp->exp_outlen);
} else if (memcmp(STR(out), tp->exp_output, tp->exp_outlen) != 0) {
msg_warn("got result '%*s', want: '%*s'",
(int) LEN(out), STR(out),
(int) tp->exp_outlen, tp->exp_output);
} else {
ok = 1;
}
#define VERIFY(b,t) { \
if (strcmp((b), (t)) != 0) \
msg_panic("bad test: %s", (b)); \
if (ok) {
msg_info("PASS %s", tp->label);
pass++;
} else {
msg_info("FAIL %s", tp->label);
fail++;
}
hex_encode(b1, test, strlen(test));
DECODE(b2, STR(b1), LEN(b1));
VERIFY(STR(b2), test);
hex_encode(b1, test, strlen(test));
hex_encode(b2, STR(b1), LEN(b1));
hex_encode(b1, STR(b2), LEN(b2));
DECODE(b2, STR(b1), LEN(b1));
DECODE(b1, STR(b2), LEN(b2));
DECODE(b2, STR(b1), LEN(b1));
VERIFY(STR(b2), test);
hex_encode(b1, test, strlen(test));
hex_encode(b2, STR(b1), LEN(b1));
hex_encode(b1, STR(b2), LEN(b2));
hex_encode(b2, STR(b1), LEN(b1));
hex_encode(b1, STR(b2), LEN(b2));
DECODE(b2, STR(b1), LEN(b1));
DECODE(b1, STR(b2), LEN(b2));
DECODE(b2, STR(b1), LEN(b1));
DECODE(b1, STR(b2), LEN(b2));
DECODE(b2, STR(b1), LEN(b1));
VERIFY(STR(b2), test);
hex_encode_opt(b1, test, strlen(test), HEX_ENCODE_FLAG_USE_COLON);
argv = argv_split(STR(b1), ":");
if (argv->argc != strlen(test))
msg_panic("HEX_ENCODE_FLAG_USE_COLON");
if (hex_decode_opt(b2, STR(b1), LEN(b1), HEX_DECODE_FLAG_ALLOW_COLON) == 0)
msg_panic("HEX_DECODE_FLAG_ALLOW_COLON");
VERIFY(STR(b2), test);
argv_free(argv);
vstring_free(b1);
vstring_free(b2);
return (0);
}
msg_info("PASS=%d FAIL=%d", pass, fail);
return (fail > 0);
}
#endif

View File

@ -21,6 +21,7 @@
*/
#define HEX_ENCODE_FLAG_NONE (0)
#define HEX_ENCODE_FLAG_USE_COLON (1<<0)
#define HEX_ENCODE_FLAG_APPEND (1<<1)
#define HEX_DECODE_FLAG_NONE (0)
#define HEX_DECODE_FLAG_ALLOW_COLON (1<<0)
@ -49,6 +50,9 @@ extern VSTRING *WARN_UNUSED_RESULT hex_decode_opt(VSTRING *, const char *, ssize
/* Google, Inc.
/* 111 8th Avenue
/* New York, NY 10011, USA
/*
/* Wietse Venema
/* porcupine.org
/*--*/
#endif

View File

@ -210,6 +210,12 @@ extern void myaddrinfo_control(int,...);
msg_fatal("sockaddr_to_hostname: %s", MAI_STRERROR(_aierr)); \
} while (0)
/*
* sane_sockaddr_to_hostaddr.c
*/
extern int WARN_UNUSED_RESULT sane_sockaddr_to_hostaddr(const struct sockaddr *,
SOCKADDR_SIZE, MAI_HOSTADDR_STR *, MAI_SERVPORT_STR *, int);
/* LICENSE
/* .ad
/* .fi

View File

@ -0,0 +1,39 @@
/*++
/* NAME
/* mystrerror 3
/* SUMMARY
/* convert error number to string
/* SYNOPSIS
/* #include <stringops.h>
/*
/* const char *mystrerror()
/* int errnum)
/* DESCRIPTION
/* mystrerror() maps an error number to string. Unlike strerror(3)
/* it returns "Application error" instead of "Success".
/* LICENSE
/* .ad
/* .fi
/* The Secure Mailer license must be distributed with this software.
/* AUTHOR(S)
/* Wietse Venema
/* porcupine.org
/*--*/
/*
* System library.
*/
#include <sys_defs.h>
#include <string.h>
/*
* Utility library.
*/
#include <stringops.h>
/* mystrerror - convert error number to string */
const char *mystrerror(int errnum)
{
return (errnum ? strerror(errnum) : "Application error");
}

View File

@ -0,0 +1,76 @@
/*++
/* NAME
/* sane_sockaddr_to_hostaddr 3
/* SUMMARY
/* Sanitize IPv4 in IPv6 address
/* SYNOPSIS
/* #include <myaddrinfo.h>
/*
/* int sane_sockaddr_to_hostaddr(
/* struct sockaddr *addr_storage,
/* SOCKADDR_SIZE addr_storage_len,
/* MAI_HOSTADDR_STR *addr_buf,
/* MAI_SERVPORT_STR *port_buf,
/* int socktype)
/* DESCRIPTION
/* sane_sockaddr_to_hostaddr() wraps sockaddr_to_hostaddr() and
/* converts an IPv4 in IPv6 address to IPv4 form, but only if IPv4
/* support is available.
/* HISTORY
/* .ad
/* .fi
/* This implementation was taken from postscreen, and consolidates
/* multiple instances of similar code across the Postfix code base.
/* LICENSE
/* .ad
/* .fi
/* The Secure Mailer license must be distributed with this software.
/* AUTHOR(S)
/* Wietse Venema
/* IBM T.J. Watson Research
/* P.O. Box 704
/* Yorktown Heights, NY 10598, USA
/*
/* Wietse Venema
/* Google, Inc.
/* 111 8th Avenue
/* New York, NY 10011, USA
/*
/* Wietse Venema
/* porcupine.org
/*--*/
/*
* System library.
*/
#include <sys_defs.h>
/*
* Utility library.
*/
#include <myaddrinfo.h>
#include <inet_proto.h>
static const INET_PROTO_INFO *proto_info;
/* sane_sockaddr_to_hostaddr - sanitize IPV4 in IPV6 address */
int sane_sockaddr_to_hostaddr(const struct sockaddr *addr_storage,
SOCKADDR_SIZE addr_storage_len,
MAI_HOSTADDR_STR *addr_buf,
MAI_SERVPORT_STR *port_buf,
int socktype)
{
int aierr;
if (proto_info == 0)
proto_info = inet_proto_info();
if ((aierr = sockaddr_to_hostaddr(addr_storage, addr_storage_len,
addr_buf, port_buf, socktype)) == 0
&& strncasecmp("::ffff:", addr_buf->buf, 7) == 0
&& strchr((char *) proto_info->sa_family_list, AF_INET) != 0)
memmove(addr_buf->buf, addr_buf->buf + 7,
sizeof(addr_buf->buf) - 7);
return (aierr);
}

View File

@ -67,6 +67,7 @@ extern int strcasecmp_utf8x(int, const char *, const char *);
extern int strncasecmp_utf8x(int, const char *, const char *, ssize_t);
extern char *quote_for_json(VSTRING *, const char *, ssize_t);
extern char *quote_for_json_append(VSTRING *, const char *, ssize_t);
extern const char *mystrerror(int);
#define EXTPAR_FLAG_NONE (0)
#define EXTPAR_FLAG_STRIP (1<<0) /* "{ text }" -> "text" */

View File

@ -67,6 +67,7 @@
#include "mymalloc.h"
#include "vbuf.h"
#include "vstring.h"
#include "stringops.h"
#include "vbuf_print.h"
/*
@ -290,8 +291,7 @@ VBUF *vbuf_print(VBUF *bp, const char *format, va_list ap)
break;
case 'm':
/* Ignore the 'l' modifier, width and precision. */
VBUF_STRCAT(bp, saved_errno ?
strerror(saved_errno) : "Application error");
VBUF_STRCAT(bp, mystrerror(saved_errno));
break;
case 'p':
if (long_flag)