-TTLS_APPL_STATE
-TTLS_CLIENT_INIT_PROPS
-TTLS_CLIENT_START_PROPS
+-TTLS_DANE
-TTLS_PRNG_SEED_INFO
-TTLS_PRNG_SRC
-TTLS_SCACHE
-TTLS_SERVER_INIT_PROPS
-TTLS_SERVER_START_PROPS
-TTLS_SESS_STATE
+-TTLS_TLSA
-TTLS_VINFO
-TTLScontext_t
-TTOK822
Documentation: in smtpd.c, the comment that justifies the
454 reply for "TLS unavailable" cited the wrong RFC.
+
+20130405
+
+ Feature: support for trust anchors, i.e. CA certificates
+ or public keys that will be used instead of conventional
+ root certificates, and revised fingerprint support. This
+ can be used by itself, and this provides support for an
+ upcoming DANE implementation. Victor Duchovni. Files:
+ mantools/postlink, proto/TLS_README.html, proto/postconf.proto,
+ src/global/mail_params.h, src/smtp/lmtp_params.c,
+ src/smtp/smtp.c, src/smtp/smtp.h, src/smtp/smtp_params.c,
+ src/smtp/smtp_proto.c, src/smtp/smtp_session.c,
+ src/smtp/smtp_state.c, src/smtp/smtp_tls_sess.c,
+ src/tls/Makefile.in, src/tls/tls.h, src/tls/tls_client.c,
+ src/tls/tls_dane.c, src/tls/tls_fprint.c, src/tls/tls_misc.c,
+ src/tls/tls_verify.c, src/util/argv.c, src/util/argv.h.
specified, the certificate CommonName is checked. If you want mandatory
encryption without server certificate verification, see above.
+With Postfix >= 2.11 the "smtp_tls_trust_anchor_file" parameter or more
+typically the corresponding per-destination "tafile" attribute optionally
+modifies trust chain verification. If the parameter is not empty the root CAs
+in CAfile and CApath are no longer trusted. Rather, the Postfix SMTP client
+will only trust certificate-chains signed by one of the trust-anchors contained
+in the chosen files. The specified trust-anchor certificates and public keys
+are not subject to expiration, and need not be (self-signed) root CAs. They
+may, if desired, be intermediate certificates. Therefore, these certificates
+also may be found "in the middle" of the trust chain presented by the remote
+SMTP server, and any untrusted issuing parent certificates will be ignored.
+
Despite the potential for eliminating "man-in-the-middle" and other attacks,
mandatory certificate trust chain and subject name verification is not viable
as a default Internet mail delivery policy. Most MX hosts do not support TLS at
specified, the CommonName is checked. If you want mandatory encryption without
server certificate verification, see above.
+With Postfix >= 2.11 the "smtp_tls_trust_anchor_file" parameter or more
+typically the corresponding per-destination "tafile" attribute optionally
+modifies trust chain verification. If the parameter is not empty the root CAs
+in CAfile and CApath are no longer trusted. Rather, the Postfix SMTP client
+will only trust certificate-chains signed by one of the trust-anchors contained
+in the chosen files. The specified trust-anchor certificates and public keys
+are not subject to expiration, and need not be (self-signed) root CAs. They
+may, if desired, be intermediate certificates. Therefore, these certificates
+also may be found "in the middle" of the trust chain presented by the remote
+SMTP server, and any untrusted issuing parent certificates will be ignored.
+
Despite the potential for eliminating "man-in-the-middle" and other attacks,
mandatory secure server certificate verification is not viable as a default
Internet mail delivery policy. Most MX hosts do not support TLS at all, and a
validated (not expired or revoked, and signed by a trusted certificate
authority), and if the server certificate name matches the optional "match"
attribute (or the main.cf smtp_tls_verify_cert_match parameter value when
- no optional "match" attribute is specified).
+ no optional "match" attribute is specified). With Postfix >= 2.11 the
+ "tafile" attribute optionally modifies trust chain verification in the same
+ manner as the "smtp_tls_trust_anchor_file" parameter. The "tafile"
+ attribute may be specified multiple times to load multiple trust-anchor
+ files.
s\bse\bec\bcu\bur\bre\be
Secure certificate verification. Mail is delivered only if the TLS
handshake succeeds, if the remote SMTP server certificate can be validated
(not expired or revoked, and signed by a trusted certificate authority),
and if the server certificate name matches the optional "match" attribute
(or the main.cf smtp_tls_secure_cert_match parameter value when no optional
- "match" attribute is specified).
+ "match" attribute is specified). With Postfix >= 2.11 the "tafile"
+ attribute optionally modifies trust chain verification in the same manner
+ as the "smtp_tls_trust_anchor_file" parameter. The "tafile" attribute may
+ be specified multiple times to load multiple trust-anchor files.
Notes:
* The "match" attribute is especially useful to verify TLS certificates for
Begin code revision, after DANE support stabilizes. This
should be one pass that changes only names and no code.
+ Run new source files through ccformat. If I do it now,
+ almost every block of code or comments is changed. Having
+ different formatting styles in the same project is PROBLEMATIC.
+ There is a reason why ccformat is included with source code.
+
+ Embed all statement-like macros in do { ... } while (0).
+ This is especially necessary with macros that contain an
+ "if" statement, or that contain multiple statements.
+
+ Spell-check, double-word check, and HTML validator check.
+
+ Use make(1) and cc(1) to convert the C++ like templates
+ into debuggable source code, such that each statement has
+ its own distinct line number (what a revolutionary idea).
+
+ All source code must specify its original author and
+ license statement. Some code modules specify Lutz Jaenicke
+ as the original author and fall under his liberal license.
+ Code that is added to such a module has the same license
+ (or at least something that is not more restrictive). Code
+ modules without input from Lutz Jaenicke must state its
+ original author and license (preferably no more restrictive
+ than Postfix's own license). Currently, too many files list
+ Wietse as the original author, and Lutz Jaenicke's license,
+ which is wrong.
+
Generally, macro and function names should make a program
more clear, not merely reduce the number of programmer
keystrokes; similar considerations hold for variable names
If you want mandatory encryption without server certificate
verification, see <a href="#client_tls_encrypt">above</a>. </p>
+<p> With Postfix ≥ 2.11 the "<a href="postconf.5.html#smtp_tls_trust_anchor_file">smtp_tls_trust_anchor_file</a>" parameter
+or more typically the corresponding per-destination "tafile" attribute
+optionally modifies trust chain verification. If the parameter is
+not empty the root CAs in CAfile and CApath are no longer trusted.
+Rather, the Postfix SMTP client will only trust certificate-chains
+signed by one of the trust-anchors contained in the chosen files.
+The specified trust-anchor certificates and public keys are not
+subject to expiration, and need not be (self-signed) root CAs. They
+may, if desired, be intermediate certificates. Therefore, these
+certificates also may be found "in the middle" of the trust chain
+presented by the remote SMTP server, and any untrusted issuing
+parent certificates will be ignored. </p>
+
<p> Despite the potential for eliminating "man-in-the-middle" and other
attacks, mandatory certificate trust chain and subject name verification
is not viable as a default Internet mail delivery policy. Most MX hosts
without server certificate verification, see <a
href="#client_tls_encrypt">above</a>. </p>
+<p> With Postfix ≥ 2.11 the "<a href="postconf.5.html#smtp_tls_trust_anchor_file">smtp_tls_trust_anchor_file</a>" parameter
+or more typically the corresponding per-destination "tafile" attribute
+optionally modifies trust chain verification. If the parameter is
+not empty the root CAs in CAfile and CApath are no longer trusted.
+Rather, the Postfix SMTP client will only trust certificate-chains
+signed by one of the trust-anchors contained in the chosen files.
+The specified trust-anchor certificates and public keys are not
+subject to expiration, and need not be (self-signed) root CAs. They
+may, if desired, be intermediate certificates. Therefore, these
+certificates also may be found "in the middle" of the trust chain
+presented by the remote SMTP server, and any untrusted issuing
+parent certificates will be ignored. </p>
+
<p> Despite the potential for eliminating "man-in-the-middle" and other
attacks, mandatory secure server certificate verification is not
viable as a default Internet mail delivery policy. Most MX hosts
<dt><b>verify</b></dt> <dd><a href="#client_tls_verify">Mandatory
server certificate verification</a>. Mail is delivered only if the
-TLS handshake
-succeeds, if the remote SMTP server certificate can be validated (not
-expired or revoked, and signed by a trusted certificate authority), and
-if the server certificate name matches the optional "match" attribute (or
-the <a href="postconf.5.html">main.cf</a> <a href="postconf.5.html#smtp_tls_verify_cert_match">smtp_tls_verify_cert_match</a> parameter value when no optional
-"match" attribute is specified). </dd>
+TLS handshake succeeds, if the remote SMTP server certificate can
+be validated (not expired or revoked, and signed by a trusted
+certificate authority), and if the server certificate name matches
+the optional "match" attribute (or the <a href="postconf.5.html">main.cf</a> <a href="postconf.5.html#smtp_tls_verify_cert_match">smtp_tls_verify_cert_match</a>
+parameter value when no optional "match" attribute is specified).
+With Postfix ≥ 2.11 the "tafile" attribute optionally modifies
+trust chain verification in the same manner as the
+"<a href="postconf.5.html#smtp_tls_trust_anchor_file">smtp_tls_trust_anchor_file</a>" parameter. The "tafile" attribute
+may be specified multiple times to load multiple trust-anchor
+files. </dd>
<dt><b>secure</b></dt> <dd><a href="#client_tls_secure">Secure certificate
verification.</a> Mail is delivered only if the TLS handshake succeeds,
or revoked, and signed by a trusted certificate authority), and if the
server certificate name matches the optional "match" attribute (or the
<a href="postconf.5.html">main.cf</a> <a href="postconf.5.html#smtp_tls_secure_cert_match">smtp_tls_secure_cert_match</a> parameter value when no optional
-"match" attribute is specified). </dd>
+"match" attribute is specified). With Postfix ≥ 2.11 the "tafile"
+attribute optionally modifies trust chain verification in the same manner
+as the "<a href="postconf.5.html#smtp_tls_trust_anchor_file">smtp_tls_trust_anchor_file</a>" parameter. The "tafile" attribute
+may be specified multiple times to load multiple trust-anchor
+files. </dd>
</dl>
List or bit-mask of OpenSSL bug work-arounds to
disable.
+ Available in Postfix version 2.11 and later:
+
+ <b><a href="postconf.5.html#smtp_tls_trust_anchor_file">smtp_tls_trust_anchor_file</a> (empty)</b>
+ Zero or more PEM-format files with trust-anchor
+ certificates and/or public keys.
+
<b>OBSOLETE STARTTLS CONTROLS</b>
- The following configuration parameters exist for compati-
+ The following configuration parameters exist for compati-
bility with Postfix versions before 2.3. Support for these
will be removed in a future release.
<b><a href="postconf.5.html#smtp_use_tls">smtp_use_tls</a> (no)</b>
- Opportunistic mode: use TLS when a remote SMTP
- server announces STARTTLS support, otherwise send
+ Opportunistic mode: use TLS when a remote SMTP
+ server announces STARTTLS support, otherwise send
the mail in the clear.
<b><a href="postconf.5.html#smtp_enforce_tls">smtp_enforce_tls</a> (no)</b>
- Enforcement mode: require that remote SMTP servers
- use TLS encryption, and never send mail in the
+ Enforcement mode: require that remote SMTP servers
+ use TLS encryption, and never send mail in the
clear.
<b><a href="postconf.5.html#smtp_tls_enforce_peername">smtp_tls_enforce_peername</a> (yes)</b>
- With mandatory TLS encryption, require that the
+ With mandatory TLS encryption, require that the
remote SMTP server hostname matches the information
in the remote SMTP server certificate.
<b><a href="postconf.5.html#smtp_tls_per_site">smtp_tls_per_site</a> (empty)</b>
Optional lookup tables with the Postfix SMTP client
- TLS usage policy by next-hop destination and by
+ TLS usage policy by next-hop destination and by
remote SMTP server hostname.
<b><a href="postconf.5.html#smtp_tls_cipherlist">smtp_tls_cipherlist</a> (empty)</b>
<b>RESOURCE AND RATE CONTROLS</b>
<b><a href="postconf.5.html#smtp_destination_concurrency_limit">smtp_destination_concurrency_limit</a> ($<a href="postconf.5.html#default_destination_concurrency_limit">default_destina</a>-</b>
<b><a href="postconf.5.html#default_destination_concurrency_limit">tion_concurrency_limit</a>)</b>
- The maximal number of parallel deliveries to the
- same destination via the smtp message delivery
+ The maximal number of parallel deliveries to the
+ same destination via the smtp message delivery
transport.
<b><a href="postconf.5.html#smtp_destination_recipient_limit">smtp_destination_recipient_limit</a> ($<a href="postconf.5.html#default_destination_recipient_limit">default_destina</a>-</b>
<b><a href="postconf.5.html#default_destination_recipient_limit">tion_recipient_limit</a>)</b>
- The maximal number of recipients per message for
+ The maximal number of recipients per message for
the smtp message delivery transport.
<b><a href="postconf.5.html#smtp_connect_timeout">smtp_connect_timeout</a> (30s)</b>
The Postfix SMTP client time limit for completing a
- TCP connection, or zero (use the operating system
+ TCP connection, or zero (use the operating system
built-in time limit).
<b><a href="postconf.5.html#smtp_helo_timeout">smtp_helo_timeout</a> (300s)</b>
- The Postfix SMTP client time limit for sending the
+ The Postfix SMTP client time limit for sending the
HELO or EHLO command, and for receiving the initial
remote SMTP server response.
<b><a href="postconf.5.html#lmtp_lhlo_timeout">lmtp_lhlo_timeout</a> (300s)</b>
- The Postfix LMTP client time limit for sending the
- LHLO command, and for receiving the initial remote
+ The Postfix LMTP client time limit for sending the
+ LHLO command, and for receiving the initial remote
LMTP server response.
<b><a href="postconf.5.html#smtp_xforward_timeout">smtp_xforward_timeout</a> (300s)</b>
- The Postfix SMTP client time limit for sending the
+ The Postfix SMTP client time limit for sending the
XFORWARD command, and for receiving the remote SMTP
server response.
<b><a href="postconf.5.html#smtp_mail_timeout">smtp_mail_timeout</a> (300s)</b>
- The Postfix SMTP client time limit for sending the
- MAIL FROM command, and for receiving the remote
+ The Postfix SMTP client time limit for sending the
+ MAIL FROM command, and for receiving the remote
SMTP server response.
<b><a href="postconf.5.html#smtp_rcpt_timeout">smtp_rcpt_timeout</a> (300s)</b>
- The Postfix SMTP client time limit for sending the
- SMTP RCPT TO command, and for receiving the remote
+ The Postfix SMTP client time limit for sending the
+ SMTP RCPT TO command, and for receiving the remote
SMTP server response.
<b><a href="postconf.5.html#smtp_data_init_timeout">smtp_data_init_timeout</a> (120s)</b>
- The Postfix SMTP client time limit for sending the
- SMTP DATA command, and for receiving the remote
+ The Postfix SMTP client time limit for sending the
+ SMTP DATA command, and for receiving the remote
SMTP server response.
<b><a href="postconf.5.html#smtp_data_xfer_timeout">smtp_data_xfer_timeout</a> (180s)</b>
- The Postfix SMTP client time limit for sending the
+ The Postfix SMTP client time limit for sending the
SMTP message content.
<b><a href="postconf.5.html#smtp_data_done_timeout">smtp_data_done_timeout</a> (600s)</b>
- The Postfix SMTP client time limit for sending the
- SMTP ".", and for receiving the remote SMTP server
+ The Postfix SMTP client time limit for sending the
+ SMTP ".", and for receiving the remote SMTP server
response.
<b><a href="postconf.5.html#smtp_quit_timeout">smtp_quit_timeout</a> (300s)</b>
- The Postfix SMTP client time limit for sending the
- QUIT command, and for receiving the remote SMTP
+ The Postfix SMTP client time limit for sending the
+ QUIT command, and for receiving the remote SMTP
server response.
Available in Postfix version 2.1 and later:
<b><a href="postconf.5.html#smtp_mx_address_limit">smtp_mx_address_limit</a> (5)</b>
The maximal number of MX (mail exchanger) IP
- addresses that can result from Postfix SMTP client
+ addresses that can result from Postfix SMTP client
mail exchanger lookups, or zero (no limit).
<b><a href="postconf.5.html#smtp_mx_session_limit">smtp_mx_session_limit</a> (2)</b>
- The maximal number of SMTP sessions per delivery
- request before the Postfix SMTP client gives up or
- delivers to a fall-back <a href="postconf.5.html#relayhost">relay host</a>, or zero (no
+ The maximal number of SMTP sessions per delivery
+ request before the Postfix SMTP client gives up or
+ delivers to a fall-back <a href="postconf.5.html#relayhost">relay host</a>, or zero (no
limit).
<b><a href="postconf.5.html#smtp_rset_timeout">smtp_rset_timeout</a> (20s)</b>
- The Postfix SMTP client time limit for sending the
- RSET command, and for receiving the remote SMTP
+ The Postfix SMTP client time limit for sending the
+ RSET command, and for receiving the remote SMTP
server response.
Available in Postfix version 2.2 and earlier:
Available in Postfix version 2.2 and later:
<b><a href="postconf.5.html#smtp_connection_cache_destinations">smtp_connection_cache_destinations</a> (empty)</b>
- Permanently enable SMTP connection caching for the
+ Permanently enable SMTP connection caching for the
specified destinations.
<b><a href="postconf.5.html#smtp_connection_cache_on_demand">smtp_connection_cache_on_demand</a> (yes)</b>
- Temporarily enable SMTP connection caching while a
+ Temporarily enable SMTP connection caching while a
destination has a high volume of mail in the active
queue.
<b><a href="postconf.5.html#smtp_connection_cache_time_limit">smtp_connection_cache_time_limit</a> (2s)</b>
When SMTP connection caching is enabled, the amount
- of time that an unused SMTP client socket is kept
+ of time that an unused SMTP client socket is kept
open before it is closed.
Available in Postfix version 2.3 and later:
<b><a href="postconf.5.html#connection_cache_protocol_timeout">connection_cache_protocol_timeout</a> (5s)</b>
- Time limit for connection cache connect, send or
+ Time limit for connection cache connect, send or
receive operations.
Available in Postfix version 2.9 and later:
<b><a href="postconf.5.html#smtp_per_record_deadline">smtp_per_record_deadline</a> (no)</b>
Change the behavior of the smtp_*_timeout time lim-
- its, from a time limit per read or write system
+ its, from a time limit per read or write system
call, to a time limit to send or receive a complete
- record (an SMTP command line, SMTP response line,
- SMTP message content line, or TLS protocol mes-
+ record (an SMTP command line, SMTP response line,
+ SMTP message content line, or TLS protocol mes-
sage).
<b>TROUBLE SHOOTING CONTROLS</b>
<b><a href="postconf.5.html#debug_peer_level">debug_peer_level</a> (2)</b>
- The increment in verbose logging level when a
- remote client or server matches a pattern in the
+ The increment in verbose logging level when a
+ remote client or server matches a pattern in the
<a href="postconf.5.html#debug_peer_list">debug_peer_list</a> parameter.
<b><a href="postconf.5.html#debug_peer_list">debug_peer_list</a> (empty)</b>
- Optional list of remote client or server hostname
- or network address patterns that cause the verbose
- logging level to increase by the amount specified
+ Optional list of remote client or server hostname
+ or network address patterns that cause the verbose
+ logging level to increase by the amount specified
in $<a href="postconf.5.html#debug_peer_level">debug_peer_level</a>.
<b><a href="postconf.5.html#error_notice_recipient">error_notice_recipient</a> (postmaster)</b>
- The recipient of postmaster notifications about
- mail delivery problems that are caused by policy,
+ The recipient of postmaster notifications about
+ mail delivery problems that are caused by policy,
resource, software or protocol errors.
<b><a href="postconf.5.html#internal_mail_filter_classes">internal_mail_filter_classes</a> (empty)</b>
- What categories of Postfix-generated mail are sub-
- ject to before-queue content inspection by
+ What categories of Postfix-generated mail are sub-
+ ject to before-queue content inspection by
<a href="postconf.5.html#non_smtpd_milters">non_smtpd_milters</a>, <a href="postconf.5.html#header_checks">header_checks</a> and <a href="postconf.5.html#body_checks">body_checks</a>.
<b><a href="postconf.5.html#notify_classes">notify_classes</a> (resource, software)</b>
- The list of error classes that are reported to the
+ The list of error classes that are reported to the
postmaster.
<b>MISCELLANEOUS CONTROLS</b>
<b><a href="postconf.5.html#best_mx_transport">best_mx_transport</a> (empty)</b>
- Where the Postfix SMTP client should deliver mail
+ Where the Postfix SMTP client should deliver mail
when it detects a "mail loops back to myself" error
condition.
<b><a href="postconf.5.html#config_directory">config_directory</a> (see 'postconf -d' output)</b>
- The default location of the Postfix <a href="postconf.5.html">main.cf</a> and
+ The default location of the Postfix <a href="postconf.5.html">main.cf</a> and
<a href="master.5.html">master.cf</a> configuration files.
<b><a href="postconf.5.html#daemon_timeout">daemon_timeout</a> (18000s)</b>
- How much time a Postfix daemon process may take to
- handle a request before it is terminated by a
+ How much time a Postfix daemon process may take to
+ handle a request before it is terminated by a
built-in watchdog timer.
<b><a href="postconf.5.html#delay_logging_resolution_limit">delay_logging_resolution_limit</a> (2)</b>
- The maximal number of digits after the decimal
+ The maximal number of digits after the decimal
point when logging sub-second delay values.
<b><a href="postconf.5.html#disable_dns_lookups">disable_dns_lookups</a> (no)</b>
- Disable DNS lookups in the Postfix SMTP and LMTP
+ Disable DNS lookups in the Postfix SMTP and LMTP
clients.
<b><a href="postconf.5.html#inet_interfaces">inet_interfaces</a> (all)</b>
tem receives mail on.
<b><a href="postconf.5.html#inet_protocols">inet_protocols</a> (all)</b>
- The Internet protocols Postfix will attempt to use
+ The Internet protocols Postfix will attempt to use
when making or accepting connections.
<b><a href="postconf.5.html#ipc_timeout">ipc_timeout</a> (3600s)</b>
<b><a href="postconf.5.html#lmtp_assume_final">lmtp_assume_final</a> (no)</b>
When a remote LMTP server announces no DSN support,
assume that the server performs final delivery, and
- send "delivered" delivery status notifications
+ send "delivered" delivery status notifications
instead of "relayed".
<b><a href="postconf.5.html#lmtp_tcp_port">lmtp_tcp_port</a> (24)</b>
- The default TCP port that the Postfix LMTP client
+ The default TCP port that the Postfix LMTP client
connects to.
<b><a href="postconf.5.html#max_idle">max_idle</a> (100s)</b>
- The maximum amount of time that an idle Postfix
- daemon process waits for an incoming connection
+ The maximum amount of time that an idle Postfix
+ daemon process waits for an incoming connection
before terminating voluntarily.
<b><a href="postconf.5.html#max_use">max_use</a> (100)</b>
- The maximal number of incoming connections that a
- Postfix daemon process will service before termi-
+ The maximal number of incoming connections that a
+ Postfix daemon process will service before termi-
nating voluntarily.
<b><a href="postconf.5.html#process_id">process_id</a> (read-only)</b>
- The process ID of a Postfix command or daemon
+ The process ID of a Postfix command or daemon
process.
<b><a href="postconf.5.html#process_name">process_name</a> (read-only)</b>
- The process name of a Postfix command or daemon
+ The process name of a Postfix command or daemon
process.
<b><a href="postconf.5.html#proxy_interfaces">proxy_interfaces</a> (empty)</b>
The network interface addresses that this mail sys-
- tem receives mail on by way of a proxy or network
+ tem receives mail on by way of a proxy or network
address translation unit.
<b><a href="postconf.5.html#smtp_address_preference">smtp_address_preference</a> (any)</b>
The address type ("ipv6", "ipv4" or "any") that the
Postfix SMTP client will try first, when a destina-
- tion has IPv6 and IPv4 addresses with equal MX
+ tion has IPv6 and IPv4 addresses with equal MX
preference.
<b><a href="postconf.5.html#smtp_bind_address">smtp_bind_address</a> (empty)</b>
- An optional numerical network address that the
- Postfix SMTP client should bind to when making an
+ An optional numerical network address that the
+ Postfix SMTP client should bind to when making an
IPv4 connection.
<b><a href="postconf.5.html#smtp_bind_address6">smtp_bind_address6</a> (empty)</b>
- An optional numerical network address that the
- Postfix SMTP client should bind to when making an
+ An optional numerical network address that the
+ Postfix SMTP client should bind to when making an
IPv6 connection.
<b><a href="postconf.5.html#smtp_helo_name">smtp_helo_name</a> ($<a href="postconf.5.html#myhostname">myhostname</a>)</b>
- The hostname to send in the SMTP EHLO or HELO com-
+ The hostname to send in the SMTP EHLO or HELO com-
mand.
<b><a href="postconf.5.html#lmtp_lhlo_name">lmtp_lhlo_name</a> ($<a href="postconf.5.html#myhostname">myhostname</a>)</b>
The hostname to send in the LMTP LHLO command.
<b><a href="postconf.5.html#smtp_host_lookup">smtp_host_lookup</a> (dns)</b>
- What mechanisms the Postfix SMTP client uses to
+ What mechanisms the Postfix SMTP client uses to
look up a host's IP address.
<b><a href="postconf.5.html#smtp_randomize_addresses">smtp_randomize_addresses</a> (yes)</b>
- Randomize the order of equal-preference MX host
+ Randomize the order of equal-preference MX host
addresses.
<b><a href="postconf.5.html#syslog_facility">syslog_facility</a> (mail)</b>
The syslog facility of Postfix logging.
<b><a href="postconf.5.html#syslog_name">syslog_name</a> (see 'postconf -d' output)</b>
- The mail system name that is prepended to the
- process name in syslog records, so that "smtpd"
+ The mail system name that is prepended to the
+ process name in syslog records, so that "smtpd"
becomes, for example, "postfix/smtpd".
Available with Postfix 2.2 and earlier:
<b><a href="postconf.5.html#fallback_relay">fallback_relay</a> (empty)</b>
- Optional list of relay hosts for SMTP destinations
+ Optional list of relay hosts for SMTP destinations
that can't be found or that are unreachable.
Available with Postfix 2.3 and later:
<b><a href="postconf.5.html#smtp_fallback_relay">smtp_fallback_relay</a> ($<a href="postconf.5.html#fallback_relay">fallback_relay</a>)</b>
- Optional list of relay hosts for SMTP destinations
+ Optional list of relay hosts for SMTP destinations
that can't be found or that are unreachable.
<b>SEE ALSO</b>
<a href="TLS_README.html">TLS_README</a>, Postfix STARTTLS howto
<b>LICENSE</b>
- The Secure Mailer license must be distributed with this
+ The Secure Mailer license must be distributed with this
software.
<b>AUTHOR(S)</b>
<p> This feature is available in Postfix 2.3 and later. </p>
+</DD>
+
+<DT><b><a name="lmtp_tls_trust_anchor_file">lmtp_tls_trust_anchor_file</a>
+(default: empty)</b></DT><DD>
+
+<p> The LMTP-specific version of the <a href="postconf.5.html#smtp_tls_trust_anchor_file">smtp_tls_trust_anchor_file</a>
+configuration parameter. See there for details. </p>
+
+<p> This feature is available in Postfix 2.11 and later. </p>
+
+
</DD>
<DT><b><a name="lmtp_tls_verify_cert_match">lmtp_tls_verify_cert_match</a>
<p> This feature is available in Postfix 2.2 and later. </p>
+</DD>
+
+<DT><b><a name="smtp_tls_trust_anchor_file">smtp_tls_trust_anchor_file</a>
+(default: empty)</b></DT><DD>
+
+<p> Zero or more PEM-format files with trust-anchor certificates
+and/or public keys. If the parameter is not empty the root CAs in
+CAfile and CApath are no longer trusted. Rather, the Postfix SMTP
+client will only trust certificate-chains signed by one of the
+trust-anchors contained in the chosen files. The specified
+trust-anchor certificates and public keys are not subject to
+expiration, and need not be (self-signed) root CAs. They may, if
+desired, be intermediate certificates. Therefore, these certificates
+also may be found "in the middle" of the trust chain presented by
+the remote SMTP server, and any untrusted issuing parent certificates
+will be ignored. Specify a list of pathnames separated by comma
+or whitespace. </p>
+
+<p> This feature is implemented for completeness, to allow installations
+with a small set of SMTP peers to set global policy in <a href="postconf.5.html">main.cf</a>,
+that at most sites would be set via <a href="postconf.5.html#smtp_tls_policy_maps">smtp_tls_policy_maps</a>. In almost
+all cases it is better to use it on a per-destination basis via the
+"tafile" policy attribute of the "verify" and "secure" levels leaving
+the global <a href="postconf.5.html">main.cf</a> setting empty. </p>
+
+<p> When used on a per-destination basis, each "tafile" PEM file
+must be accessible to the Postfix SMTP client in the chroot jail
+if applicable. The files should not contain any sensitive data,
+and must be readable by the non-privileged $<a href="postconf.5.html#mail_owner">mail_owner</a> user. This
+allows destinations to be bound to a set of specific CAs or public
+keys without trusting the same CAs for all destinations. </p>
+
+<p> The underlying mechanism is in support of <a href="http://tools.ietf.org/html/rfc6698">RFC 6698</a> (DANE TLSA),
+which defines mechanisms for a client to securely determine server
+TLS certificates via DNS. </p>
+
+<p> If you want your trust anchors to be public keys, with OpenSSL
+you can extract a single PEM public key from a PEM X.509 file
+containing a single certificate, as follows: </p>
+
+<blockquote>
+<pre>
+$ openssl x509 -in cert.pem -out ta-key.pem -noout -pubkey
+</pre>
+</blockquote>
+
+<p> This feature is available in Postfix 2.11 and later. </p>
+
+
</DD>
<DT><b><a name="smtp_tls_verify_cert_match">smtp_tls_verify_cert_match</a>
List or bit-mask of OpenSSL bug work-arounds to
disable.
+ Available in Postfix version 2.11 and later:
+
+ <b><a href="postconf.5.html#smtp_tls_trust_anchor_file">smtp_tls_trust_anchor_file</a> (empty)</b>
+ Zero or more PEM-format files with trust-anchor
+ certificates and/or public keys.
+
<b>OBSOLETE STARTTLS CONTROLS</b>
- The following configuration parameters exist for compati-
+ The following configuration parameters exist for compati-
bility with Postfix versions before 2.3. Support for these
will be removed in a future release.
<b><a href="postconf.5.html#smtp_use_tls">smtp_use_tls</a> (no)</b>
- Opportunistic mode: use TLS when a remote SMTP
- server announces STARTTLS support, otherwise send
+ Opportunistic mode: use TLS when a remote SMTP
+ server announces STARTTLS support, otherwise send
the mail in the clear.
<b><a href="postconf.5.html#smtp_enforce_tls">smtp_enforce_tls</a> (no)</b>
- Enforcement mode: require that remote SMTP servers
- use TLS encryption, and never send mail in the
+ Enforcement mode: require that remote SMTP servers
+ use TLS encryption, and never send mail in the
clear.
<b><a href="postconf.5.html#smtp_tls_enforce_peername">smtp_tls_enforce_peername</a> (yes)</b>
- With mandatory TLS encryption, require that the
+ With mandatory TLS encryption, require that the
remote SMTP server hostname matches the information
in the remote SMTP server certificate.
<b><a href="postconf.5.html#smtp_tls_per_site">smtp_tls_per_site</a> (empty)</b>
Optional lookup tables with the Postfix SMTP client
- TLS usage policy by next-hop destination and by
+ TLS usage policy by next-hop destination and by
remote SMTP server hostname.
<b><a href="postconf.5.html#smtp_tls_cipherlist">smtp_tls_cipherlist</a> (empty)</b>
<b>RESOURCE AND RATE CONTROLS</b>
<b><a href="postconf.5.html#smtp_destination_concurrency_limit">smtp_destination_concurrency_limit</a> ($<a href="postconf.5.html#default_destination_concurrency_limit">default_destina</a>-</b>
<b><a href="postconf.5.html#default_destination_concurrency_limit">tion_concurrency_limit</a>)</b>
- The maximal number of parallel deliveries to the
- same destination via the smtp message delivery
+ The maximal number of parallel deliveries to the
+ same destination via the smtp message delivery
transport.
<b><a href="postconf.5.html#smtp_destination_recipient_limit">smtp_destination_recipient_limit</a> ($<a href="postconf.5.html#default_destination_recipient_limit">default_destina</a>-</b>
<b><a href="postconf.5.html#default_destination_recipient_limit">tion_recipient_limit</a>)</b>
- The maximal number of recipients per message for
+ The maximal number of recipients per message for
the smtp message delivery transport.
<b><a href="postconf.5.html#smtp_connect_timeout">smtp_connect_timeout</a> (30s)</b>
The Postfix SMTP client time limit for completing a
- TCP connection, or zero (use the operating system
+ TCP connection, or zero (use the operating system
built-in time limit).
<b><a href="postconf.5.html#smtp_helo_timeout">smtp_helo_timeout</a> (300s)</b>
- The Postfix SMTP client time limit for sending the
+ The Postfix SMTP client time limit for sending the
HELO or EHLO command, and for receiving the initial
remote SMTP server response.
<b><a href="postconf.5.html#lmtp_lhlo_timeout">lmtp_lhlo_timeout</a> (300s)</b>
- The Postfix LMTP client time limit for sending the
- LHLO command, and for receiving the initial remote
+ The Postfix LMTP client time limit for sending the
+ LHLO command, and for receiving the initial remote
LMTP server response.
<b><a href="postconf.5.html#smtp_xforward_timeout">smtp_xforward_timeout</a> (300s)</b>
- The Postfix SMTP client time limit for sending the
+ The Postfix SMTP client time limit for sending the
XFORWARD command, and for receiving the remote SMTP
server response.
<b><a href="postconf.5.html#smtp_mail_timeout">smtp_mail_timeout</a> (300s)</b>
- The Postfix SMTP client time limit for sending the
- MAIL FROM command, and for receiving the remote
+ The Postfix SMTP client time limit for sending the
+ MAIL FROM command, and for receiving the remote
SMTP server response.
<b><a href="postconf.5.html#smtp_rcpt_timeout">smtp_rcpt_timeout</a> (300s)</b>
- The Postfix SMTP client time limit for sending the
- SMTP RCPT TO command, and for receiving the remote
+ The Postfix SMTP client time limit for sending the
+ SMTP RCPT TO command, and for receiving the remote
SMTP server response.
<b><a href="postconf.5.html#smtp_data_init_timeout">smtp_data_init_timeout</a> (120s)</b>
- The Postfix SMTP client time limit for sending the
- SMTP DATA command, and for receiving the remote
+ The Postfix SMTP client time limit for sending the
+ SMTP DATA command, and for receiving the remote
SMTP server response.
<b><a href="postconf.5.html#smtp_data_xfer_timeout">smtp_data_xfer_timeout</a> (180s)</b>
- The Postfix SMTP client time limit for sending the
+ The Postfix SMTP client time limit for sending the
SMTP message content.
<b><a href="postconf.5.html#smtp_data_done_timeout">smtp_data_done_timeout</a> (600s)</b>
- The Postfix SMTP client time limit for sending the
- SMTP ".", and for receiving the remote SMTP server
+ The Postfix SMTP client time limit for sending the
+ SMTP ".", and for receiving the remote SMTP server
response.
<b><a href="postconf.5.html#smtp_quit_timeout">smtp_quit_timeout</a> (300s)</b>
- The Postfix SMTP client time limit for sending the
- QUIT command, and for receiving the remote SMTP
+ The Postfix SMTP client time limit for sending the
+ QUIT command, and for receiving the remote SMTP
server response.
Available in Postfix version 2.1 and later:
<b><a href="postconf.5.html#smtp_mx_address_limit">smtp_mx_address_limit</a> (5)</b>
The maximal number of MX (mail exchanger) IP
- addresses that can result from Postfix SMTP client
+ addresses that can result from Postfix SMTP client
mail exchanger lookups, or zero (no limit).
<b><a href="postconf.5.html#smtp_mx_session_limit">smtp_mx_session_limit</a> (2)</b>
- The maximal number of SMTP sessions per delivery
- request before the Postfix SMTP client gives up or
- delivers to a fall-back <a href="postconf.5.html#relayhost">relay host</a>, or zero (no
+ The maximal number of SMTP sessions per delivery
+ request before the Postfix SMTP client gives up or
+ delivers to a fall-back <a href="postconf.5.html#relayhost">relay host</a>, or zero (no
limit).
<b><a href="postconf.5.html#smtp_rset_timeout">smtp_rset_timeout</a> (20s)</b>
- The Postfix SMTP client time limit for sending the
- RSET command, and for receiving the remote SMTP
+ The Postfix SMTP client time limit for sending the
+ RSET command, and for receiving the remote SMTP
server response.
Available in Postfix version 2.2 and earlier:
Available in Postfix version 2.2 and later:
<b><a href="postconf.5.html#smtp_connection_cache_destinations">smtp_connection_cache_destinations</a> (empty)</b>
- Permanently enable SMTP connection caching for the
+ Permanently enable SMTP connection caching for the
specified destinations.
<b><a href="postconf.5.html#smtp_connection_cache_on_demand">smtp_connection_cache_on_demand</a> (yes)</b>
- Temporarily enable SMTP connection caching while a
+ Temporarily enable SMTP connection caching while a
destination has a high volume of mail in the active
queue.
<b><a href="postconf.5.html#smtp_connection_cache_time_limit">smtp_connection_cache_time_limit</a> (2s)</b>
When SMTP connection caching is enabled, the amount
- of time that an unused SMTP client socket is kept
+ of time that an unused SMTP client socket is kept
open before it is closed.
Available in Postfix version 2.3 and later:
<b><a href="postconf.5.html#connection_cache_protocol_timeout">connection_cache_protocol_timeout</a> (5s)</b>
- Time limit for connection cache connect, send or
+ Time limit for connection cache connect, send or
receive operations.
Available in Postfix version 2.9 and later:
<b><a href="postconf.5.html#smtp_per_record_deadline">smtp_per_record_deadline</a> (no)</b>
Change the behavior of the smtp_*_timeout time lim-
- its, from a time limit per read or write system
+ its, from a time limit per read or write system
call, to a time limit to send or receive a complete
- record (an SMTP command line, SMTP response line,
- SMTP message content line, or TLS protocol mes-
+ record (an SMTP command line, SMTP response line,
+ SMTP message content line, or TLS protocol mes-
sage).
<b>TROUBLE SHOOTING CONTROLS</b>
<b><a href="postconf.5.html#debug_peer_level">debug_peer_level</a> (2)</b>
- The increment in verbose logging level when a
- remote client or server matches a pattern in the
+ The increment in verbose logging level when a
+ remote client or server matches a pattern in the
<a href="postconf.5.html#debug_peer_list">debug_peer_list</a> parameter.
<b><a href="postconf.5.html#debug_peer_list">debug_peer_list</a> (empty)</b>
- Optional list of remote client or server hostname
- or network address patterns that cause the verbose
- logging level to increase by the amount specified
+ Optional list of remote client or server hostname
+ or network address patterns that cause the verbose
+ logging level to increase by the amount specified
in $<a href="postconf.5.html#debug_peer_level">debug_peer_level</a>.
<b><a href="postconf.5.html#error_notice_recipient">error_notice_recipient</a> (postmaster)</b>
- The recipient of postmaster notifications about
- mail delivery problems that are caused by policy,
+ The recipient of postmaster notifications about
+ mail delivery problems that are caused by policy,
resource, software or protocol errors.
<b><a href="postconf.5.html#internal_mail_filter_classes">internal_mail_filter_classes</a> (empty)</b>
- What categories of Postfix-generated mail are sub-
- ject to before-queue content inspection by
+ What categories of Postfix-generated mail are sub-
+ ject to before-queue content inspection by
<a href="postconf.5.html#non_smtpd_milters">non_smtpd_milters</a>, <a href="postconf.5.html#header_checks">header_checks</a> and <a href="postconf.5.html#body_checks">body_checks</a>.
<b><a href="postconf.5.html#notify_classes">notify_classes</a> (resource, software)</b>
- The list of error classes that are reported to the
+ The list of error classes that are reported to the
postmaster.
<b>MISCELLANEOUS CONTROLS</b>
<b><a href="postconf.5.html#best_mx_transport">best_mx_transport</a> (empty)</b>
- Where the Postfix SMTP client should deliver mail
+ Where the Postfix SMTP client should deliver mail
when it detects a "mail loops back to myself" error
condition.
<b><a href="postconf.5.html#config_directory">config_directory</a> (see 'postconf -d' output)</b>
- The default location of the Postfix <a href="postconf.5.html">main.cf</a> and
+ The default location of the Postfix <a href="postconf.5.html">main.cf</a> and
<a href="master.5.html">master.cf</a> configuration files.
<b><a href="postconf.5.html#daemon_timeout">daemon_timeout</a> (18000s)</b>
- How much time a Postfix daemon process may take to
- handle a request before it is terminated by a
+ How much time a Postfix daemon process may take to
+ handle a request before it is terminated by a
built-in watchdog timer.
<b><a href="postconf.5.html#delay_logging_resolution_limit">delay_logging_resolution_limit</a> (2)</b>
- The maximal number of digits after the decimal
+ The maximal number of digits after the decimal
point when logging sub-second delay values.
<b><a href="postconf.5.html#disable_dns_lookups">disable_dns_lookups</a> (no)</b>
- Disable DNS lookups in the Postfix SMTP and LMTP
+ Disable DNS lookups in the Postfix SMTP and LMTP
clients.
<b><a href="postconf.5.html#inet_interfaces">inet_interfaces</a> (all)</b>
tem receives mail on.
<b><a href="postconf.5.html#inet_protocols">inet_protocols</a> (all)</b>
- The Internet protocols Postfix will attempt to use
+ The Internet protocols Postfix will attempt to use
when making or accepting connections.
<b><a href="postconf.5.html#ipc_timeout">ipc_timeout</a> (3600s)</b>
<b><a href="postconf.5.html#lmtp_assume_final">lmtp_assume_final</a> (no)</b>
When a remote LMTP server announces no DSN support,
assume that the server performs final delivery, and
- send "delivered" delivery status notifications
+ send "delivered" delivery status notifications
instead of "relayed".
<b><a href="postconf.5.html#lmtp_tcp_port">lmtp_tcp_port</a> (24)</b>
- The default TCP port that the Postfix LMTP client
+ The default TCP port that the Postfix LMTP client
connects to.
<b><a href="postconf.5.html#max_idle">max_idle</a> (100s)</b>
- The maximum amount of time that an idle Postfix
- daemon process waits for an incoming connection
+ The maximum amount of time that an idle Postfix
+ daemon process waits for an incoming connection
before terminating voluntarily.
<b><a href="postconf.5.html#max_use">max_use</a> (100)</b>
- The maximal number of incoming connections that a
- Postfix daemon process will service before termi-
+ The maximal number of incoming connections that a
+ Postfix daemon process will service before termi-
nating voluntarily.
<b><a href="postconf.5.html#process_id">process_id</a> (read-only)</b>
- The process ID of a Postfix command or daemon
+ The process ID of a Postfix command or daemon
process.
<b><a href="postconf.5.html#process_name">process_name</a> (read-only)</b>
- The process name of a Postfix command or daemon
+ The process name of a Postfix command or daemon
process.
<b><a href="postconf.5.html#proxy_interfaces">proxy_interfaces</a> (empty)</b>
The network interface addresses that this mail sys-
- tem receives mail on by way of a proxy or network
+ tem receives mail on by way of a proxy or network
address translation unit.
<b><a href="postconf.5.html#smtp_address_preference">smtp_address_preference</a> (any)</b>
The address type ("ipv6", "ipv4" or "any") that the
Postfix SMTP client will try first, when a destina-
- tion has IPv6 and IPv4 addresses with equal MX
+ tion has IPv6 and IPv4 addresses with equal MX
preference.
<b><a href="postconf.5.html#smtp_bind_address">smtp_bind_address</a> (empty)</b>
- An optional numerical network address that the
- Postfix SMTP client should bind to when making an
+ An optional numerical network address that the
+ Postfix SMTP client should bind to when making an
IPv4 connection.
<b><a href="postconf.5.html#smtp_bind_address6">smtp_bind_address6</a> (empty)</b>
- An optional numerical network address that the
- Postfix SMTP client should bind to when making an
+ An optional numerical network address that the
+ Postfix SMTP client should bind to when making an
IPv6 connection.
<b><a href="postconf.5.html#smtp_helo_name">smtp_helo_name</a> ($<a href="postconf.5.html#myhostname">myhostname</a>)</b>
- The hostname to send in the SMTP EHLO or HELO com-
+ The hostname to send in the SMTP EHLO or HELO com-
mand.
<b><a href="postconf.5.html#lmtp_lhlo_name">lmtp_lhlo_name</a> ($<a href="postconf.5.html#myhostname">myhostname</a>)</b>
The hostname to send in the LMTP LHLO command.
<b><a href="postconf.5.html#smtp_host_lookup">smtp_host_lookup</a> (dns)</b>
- What mechanisms the Postfix SMTP client uses to
+ What mechanisms the Postfix SMTP client uses to
look up a host's IP address.
<b><a href="postconf.5.html#smtp_randomize_addresses">smtp_randomize_addresses</a> (yes)</b>
- Randomize the order of equal-preference MX host
+ Randomize the order of equal-preference MX host
addresses.
<b><a href="postconf.5.html#syslog_facility">syslog_facility</a> (mail)</b>
The syslog facility of Postfix logging.
<b><a href="postconf.5.html#syslog_name">syslog_name</a> (see 'postconf -d' output)</b>
- The mail system name that is prepended to the
- process name in syslog records, so that "smtpd"
+ The mail system name that is prepended to the
+ process name in syslog records, so that "smtpd"
becomes, for example, "postfix/smtpd".
Available with Postfix 2.2 and earlier:
<b><a href="postconf.5.html#fallback_relay">fallback_relay</a> (empty)</b>
- Optional list of relay hosts for SMTP destinations
+ Optional list of relay hosts for SMTP destinations
that can't be found or that are unreachable.
Available with Postfix 2.3 and later:
<b><a href="postconf.5.html#smtp_fallback_relay">smtp_fallback_relay</a> ($<a href="postconf.5.html#fallback_relay">fallback_relay</a>)</b>
- Optional list of relay hosts for SMTP destinations
+ Optional list of relay hosts for SMTP destinations
that can't be found or that are unreachable.
<b>SEE ALSO</b>
<a href="TLS_README.html">TLS_README</a>, Postfix STARTTLS howto
<b>LICENSE</b>
- The Secure Mailer license must be distributed with this
+ The Secure Mailer license must be distributed with this
software.
<b>AUTHOR(S)</b>
# Non-production: needs thorough testing, or major changes are still
# needed before the code stabilizes.
-#CCARGS="$CCARGS -DNONPROD"
+CCARGS="$CCARGS -DNONPROD"
sed 's/ / /g' <<EOF
SYSTYPE = $SYSTYPE
configuration parameter. See there for details.
.PP
This feature is available in Postfix 2.3 and later.
+.SH lmtp_tls_trust_anchor_file (default: empty)
+The LMTP-specific version of the smtp_tls_trust_anchor_file
+configuration parameter. See there for details.
+.PP
+This feature is available in Postfix 2.11 and later.
.SH lmtp_tls_verify_cert_match (default: hostname)
The LMTP-specific version of the smtp_tls_verify_cert_match
configuration parameter. See there for details.
are not possible.
.PP
This feature is available in Postfix 2.2 and later.
+.SH smtp_tls_trust_anchor_file (default: empty)
+Zero or more PEM-format files with trust-anchor certificates
+and/or public keys. If the parameter is not empty the root CAs in
+CAfile and CApath are no longer trusted. Rather, the Postfix SMTP
+client will only trust certificate-chains signed by one of the
+trust-anchors contained in the chosen files. The specified
+trust-anchor certificates and public keys are not subject to
+expiration, and need not be (self-signed) root CAs. They may, if
+desired, be intermediate certificates. Therefore, these certificates
+also may be found "in the middle" of the trust chain presented by
+the remote SMTP server, and any untrusted issuing parent certificates
+will be ignored. Specify a list of pathnames separated by comma
+or whitespace.
+.PP
+This feature is implemented for completeness, to allow installations
+with a small set of SMTP peers to set global policy in main.cf,
+that at most sites would be set via smtp_tls_policy_maps. In almost
+all cases it is better to use it on a per-destination basis via the
+"tafile" policy attribute of the "verify" and "secure" levels leaving
+the global main.cf setting empty.
+.PP
+When used on a per-destination basis, each "tafile" PEM file
+must be accessible to the Postfix SMTP client in the chroot jail
+if applicable. The files should not contain any sensitive data,
+and must be readable by the non-privileged $mail_owner user. This
+allows destinations to be bound to a set of specific CAs or public
+keys without trusting the same CAs for all destinations.
+.PP
+The underlying mechanism is in support of RFC 6698 (DANE TLSA),
+which defines mechanisms for a client to securely determine server
+TLS certificates via DNS.
+.PP
+If you want your trust anchors to be public keys, with OpenSSL
+you can extract a single PEM public key from a PEM X.509 file
+containing a single certificate, as follows:
+.sp
+.in +4
+.nf
+.na
+.ft C
+$ openssl x509 -in cert.pem -out ta-key.pem -noout -pubkey
+.fi
+.ad
+.ft R
+.in -4
+.PP
+This feature is available in Postfix 2.11 and later.
.SH smtp_tls_verify_cert_match (default: hostname)
How the Postfix SMTP client verifies the server certificate
peername for the
Available in Postfix version 2.8 and later:
.IP "\fBtls_disable_workarounds (see 'postconf -d' output)\fR"
List or bit-mask of OpenSSL bug work-arounds to disable.
+.PP
+Available in Postfix version 2.11 and later:
+.IP "\fBsmtp_tls_trust_anchor_file (empty)\fR"
+Zero or more PEM-format files with trust-anchor certificates
+and/or public keys.
.SH "OBSOLETE STARTTLS CONTROLS"
.na
.nf
s;\blmtp_tls_security_level\b;<a href="postconf.5.html#lmtp_tls_security_level">$&</a>;g;
s;\blmtp_tls_fingerprint_cert_match\b;<a href="postconf.5.html#lmtp_tls_fingerprint_cert_match">$&</a>;g;
s;\blmtp_tls_verify_cert_match\b;<a href="postconf.5.html#lmtp_tls_verify_cert_match">$&</a>;g;
+ s;\blmtp_tls_trust_anchor_file\b;<a href="postconf.5.html#lmtp_tls_trust_anchor_file">$&</a>;g;
s;\blmtp_tls_per_site\b;<a href="postconf.5.html#lmtp_tls_per_site">$&</a>;g;
s;\blmtp_tls_cert_file\b;<a href="postconf.5.html#lmtp_tls_cert_file">$&</a>;g;
s;\blmtp_tls_key_file\b;<a href="postconf.5.html#lmtp_tls_key_file">$&</a>;g;
s;\bsmtp_tls_fingerprint_cert_match\b;<a href="postconf.5.html#smtp_tls_fingerprint_cert_match">$&</a>;g;
s;\bsmtp_tls_verify_cert_match\b;<a href="postconf.5.html#smtp_tls_verify_cert_match">$&</a>;g;
s;\bsmtp_tls_secure_cert_match\b;<a href="postconf.5.html#smtp_tls_secure_cert_match">$&</a>;g;
+ s;\bsmtp_tls_trust_anchor_file\b;<a href="postconf.5.html#smtp_tls_trust_anchor_file">$&</a>;g;
s;\bsmtp_tls_scert_verifydepth\b;<a href="postconf.5.html#smtp_tls_scert_verifydepth">$&</a>;g;
s;\bsmtp_tls_security_level\b;<a href="postconf.5.html#smtp_tls_security_level">$&</a>;g;
s;\bsmtp_tls_session_cache_database\b;<a href="postconf.5.html#smtp_tls_session_cache_database">$&</a>;g;
If you want mandatory encryption without server certificate
verification, see <a href="#client_tls_encrypt">above</a>. </p>
+<p> With Postfix ≥ 2.11 the "smtp_tls_trust_anchor_file" parameter
+or more typically the corresponding per-destination "tafile" attribute
+optionally modifies trust chain verification. If the parameter is
+not empty the root CAs in CAfile and CApath are no longer trusted.
+Rather, the Postfix SMTP client will only trust certificate-chains
+signed by one of the trust-anchors contained in the chosen files.
+The specified trust-anchor certificates and public keys are not
+subject to expiration, and need not be (self-signed) root CAs. They
+may, if desired, be intermediate certificates. Therefore, these
+certificates also may be found "in the middle" of the trust chain
+presented by the remote SMTP server, and any untrusted issuing
+parent certificates will be ignored. </p>
+
<p> Despite the potential for eliminating "man-in-the-middle" and other
attacks, mandatory certificate trust chain and subject name verification
is not viable as a default Internet mail delivery policy. Most MX hosts
without server certificate verification, see <a
href="#client_tls_encrypt">above</a>. </p>
+<p> With Postfix ≥ 2.11 the "smtp_tls_trust_anchor_file" parameter
+or more typically the corresponding per-destination "tafile" attribute
+optionally modifies trust chain verification. If the parameter is
+not empty the root CAs in CAfile and CApath are no longer trusted.
+Rather, the Postfix SMTP client will only trust certificate-chains
+signed by one of the trust-anchors contained in the chosen files.
+The specified trust-anchor certificates and public keys are not
+subject to expiration, and need not be (self-signed) root CAs. They
+may, if desired, be intermediate certificates. Therefore, these
+certificates also may be found "in the middle" of the trust chain
+presented by the remote SMTP server, and any untrusted issuing
+parent certificates will be ignored. </p>
+
<p> Despite the potential for eliminating "man-in-the-middle" and other
attacks, mandatory secure server certificate verification is not
viable as a default Internet mail delivery policy. Most MX hosts
<dt><b>verify</b></dt> <dd><a href="#client_tls_verify">Mandatory
server certificate verification</a>. Mail is delivered only if the
-TLS handshake
-succeeds, if the remote SMTP server certificate can be validated (not
-expired or revoked, and signed by a trusted certificate authority), and
-if the server certificate name matches the optional "match" attribute (or
-the main.cf smtp_tls_verify_cert_match parameter value when no optional
-"match" attribute is specified). </dd>
+TLS handshake succeeds, if the remote SMTP server certificate can
+be validated (not expired or revoked, and signed by a trusted
+certificate authority), and if the server certificate name matches
+the optional "match" attribute (or the main.cf smtp_tls_verify_cert_match
+parameter value when no optional "match" attribute is specified).
+With Postfix ≥ 2.11 the "tafile" attribute optionally modifies
+trust chain verification in the same manner as the
+"smtp_tls_trust_anchor_file" parameter. The "tafile" attribute
+may be specified multiple times to load multiple trust-anchor
+files. </dd>
<dt><b>secure</b></dt> <dd><a href="#client_tls_secure">Secure certificate
verification.</a> Mail is delivered only if the TLS handshake succeeds,
or revoked, and signed by a trusted certificate authority), and if the
server certificate name matches the optional "match" attribute (or the
main.cf smtp_tls_secure_cert_match parameter value when no optional
-"match" attribute is specified). </dd>
+"match" attribute is specified). With Postfix ≥ 2.11 the "tafile"
+attribute optionally modifies trust chain verification in the same manner
+as the "smtp_tls_trust_anchor_file" parameter. The "tafile" attribute
+may be specified multiple times to load multiple trust-anchor
+files. </dd>
</dl>
<p> This feature is available in Postfix 2.9 and later. </p>
-%PARAM smtpd_log_access_permit_actions empty
+%PARAM smtpd_log_access_permit_actions
<p> Enable logging of the named "permit" actions in SMTP server
access lists (by default, the SMTP server logs "reject" actions but
configuration parameter. See there for details. </p>
<p> This feature is available in Postfix 2.11 and later. </p>
+
+%PARAM smtp_tls_trust_anchor_file
+
+<p> Zero or more PEM-format files with trust-anchor certificates
+and/or public keys. If the parameter is not empty the root CAs in
+CAfile and CApath are no longer trusted. Rather, the Postfix SMTP
+client will only trust certificate-chains signed by one of the
+trust-anchors contained in the chosen files. The specified
+trust-anchor certificates and public keys are not subject to
+expiration, and need not be (self-signed) root CAs. They may, if
+desired, be intermediate certificates. Therefore, these certificates
+also may be found "in the middle" of the trust chain presented by
+the remote SMTP server, and any untrusted issuing parent certificates
+will be ignored. Specify a list of pathnames separated by comma
+or whitespace. </p>
+
+<p> This feature is implemented for completeness, to allow installations
+with a small set of SMTP peers to set global policy in main.cf,
+that at most sites would be set via smtp_tls_policy_maps. In almost
+all cases it is better to use it on a per-destination basis via the
+"tafile" policy attribute of the "verify" and "secure" levels leaving
+the global main.cf setting empty. </p>
+
+<p> When used on a per-destination basis, each "tafile" PEM file
+must be accessible to the Postfix SMTP client in the chroot jail
+if applicable. The files should not contain any sensitive data,
+and must be readable by the non-privileged $mail_owner user. This
+allows destinations to be bound to a set of specific CAs or public
+keys without trusting the same CAs for all destinations. </p>
+
+<p> The underlying mechanism is in support of RFC 6698 (DANE TLSA),
+which defines mechanisms for a client to securely determine server
+TLS certificates via DNS. </p>
+
+<p> If you want your trust anchors to be public keys, with OpenSSL
+you can extract a single PEM public key from a PEM X.509 file
+containing a single certificate, as follows: </p>
+
+<blockquote>
+<pre>
+$ openssl x509 -in cert.pem -out ta-key.pem -noout -pubkey
+</pre>
+</blockquote>
+
+<p> This feature is available in Postfix 2.11 and later. </p>
+
+%PARAM lmtp_tls_trust_anchor_file
+
+<p> The LMTP-specific version of the smtp_tls_trust_anchor_file
+configuration parameter. See there for details. </p>
+
+<p> This feature is available in Postfix 2.11 and later. </p>
unzipping
windowsize
xpostconf
+TLSA
+tafile
+VPN
#define DEF_LMTP_TLS_FPT_DGST "md5"
extern char *var_smtp_tls_fpt_dgst;
+#define VAR_SMTP_TLS_TAFILE "smtp_tls_trust_anchor_file"
+#define DEF_SMTP_TLS_TAFILE ""
+#define VAR_LMTP_TLS_TAFILE "lmtp_tls_trust_anchor_file"
+#define DEF_LMTP_TLS_TAFILE ""
+extern char *var_smtp_tls_tafile;
+
#define VAR_SMTP_TLS_LOGLEVEL "smtp_tls_loglevel"
#define DEF_SMTP_TLS_LOGLEVEL "0"
#define VAR_LMTP_TLS_LOGLEVEL "lmtp_tls_loglevel"
* Patches change both the patchlevel and the release date. Snapshots have no
* patchlevel; they change the release date only.
*/
-#define MAIL_RELEASE_DATE "20130403"
+#define MAIL_RELEASE_DATE "20130405"
#define MAIL_VERSION_NUMBER "2.11"
#ifdef SNAPSHOT
smtp_state.o: smtp_state.c
smtp_tls_sess.o: ../../include/argv.h
smtp_tls_sess.o: ../../include/attr.h
+smtp_tls_sess.o: ../../include/ctable.h
smtp_tls_sess.o: ../../include/deliver_request.h
smtp_tls_sess.o: ../../include/dict.h
smtp_tls_sess.o: ../../include/dsn.h
VAR_LMTP_TLS_SEC_CMATCH, DEF_LMTP_TLS_SEC_CMATCH, &var_smtp_tls_sec_cmatch, 1, 0,
VAR_LMTP_TLS_FPT_CMATCH, DEF_LMTP_TLS_FPT_CMATCH, &var_smtp_tls_fpt_cmatch, 0, 0,
VAR_LMTP_TLS_FPT_DGST, DEF_LMTP_TLS_FPT_DGST, &var_smtp_tls_fpt_dgst, 1, 0,
+ VAR_LMTP_TLS_TAFILE, DEF_LMTP_TLS_TAFILE, &var_smtp_tls_tafile, 0, 0,
VAR_LMTP_TLS_PROTO, DEF_LMTP_TLS_PROTO, &var_smtp_tls_proto, 0, 0,
VAR_LMTP_TLS_CIPH, DEF_LMTP_TLS_CIPH, &var_smtp_tls_ciph, 1, 0,
VAR_LMTP_TLS_ECCERT_FILE, DEF_LMTP_TLS_ECCERT_FILE, &var_smtp_tls_eccert_file, 0, 0,
/* Available in Postfix version 2.8 and later:
/* .IP "\fBtls_disable_workarounds (see 'postconf -d' output)\fR"
/* List or bit-mask of OpenSSL bug work-arounds to disable.
+/* .PP
+/* Available in Postfix version 2.11 and later:
+/* .IP "\fBsmtp_tls_trust_anchor_file (empty)\fR"
+/* Zero or more PEM-format files with trust-anchor certificates
+/* and/or public keys.
/* OBSOLETE STARTTLS CONTROLS
/* .ad
/* .fi
char *var_smtp_tls_vfy_cmatch;
char *var_smtp_tls_fpt_cmatch;
char *var_smtp_tls_fpt_dgst;
+char *var_smtp_tls_tafile;
char *var_smtp_tls_proto;
char *var_smtp_tls_ciph;
char *var_smtp_tls_eccert_file;
* smtp_session.c
*/
#ifdef USE_TLS
-typedef struct SMTP_TLS_SESS {
+typedef struct SMTP_TLS_POLICY {
+ int refs; /* Reference count */
int level; /* TLS enforcement level */
char *protocols; /* Acceptable SSL protocols */
char *grade; /* Cipher grade: "export", ... */
VSTRING *exclusions; /* Excluded SSL ciphers */
ARGV *matchargv; /* Cert match patterns */
-} SMTP_TLS_SESS;
+ DSN_BUF *why; /* Lookup error status */
+ TLS_DANE *dane; /* DANE TLSA digests */
+} SMTP_TLS_POLICY;
#endif
TLS_SESS_STATE *tls_context; /* TLS library session state */
char *tls_nexthop; /* Nexthop domain for cert checks */
int tls_retry_plain; /* Try plain when TLS handshake fails */
- SMTP_TLS_SESS *tls; /* SMTP session TLS policy */
+ SMTP_TLS_POLICY *tls; /* SMTP session TLS policy */
#endif
SMTP_STATE *state; /* back link */
* smtp_tls_sess.c
*/
extern void smtp_tls_list_init(void);
-extern SMTP_TLS_SESS *smtp_tls_sess_alloc(DSN_BUF *, const char *, const char *,
- unsigned, int);
-extern SMTP_TLS_SESS *smtp_tls_sess_free(SMTP_TLS_SESS *);
-
+extern SMTP_TLS_POLICY *smtp_tls_policy(DSN_BUF *, const char *, const char *,
+ unsigned, int);
+extern void smtp_tls_policy_free(SMTP_TLS_POLICY *);
+extern void smtp_tls_policy_flush(void);
#endif
/*
VAR_SMTP_TLS_SEC_CMATCH, DEF_SMTP_TLS_SEC_CMATCH, &var_smtp_tls_sec_cmatch, 1, 0,
VAR_SMTP_TLS_FPT_CMATCH, DEF_SMTP_TLS_FPT_CMATCH, &var_smtp_tls_fpt_cmatch, 0, 0,
VAR_SMTP_TLS_FPT_DGST, DEF_SMTP_TLS_FPT_DGST, &var_smtp_tls_fpt_dgst, 1, 0,
+ VAR_SMTP_TLS_TAFILE, DEF_SMTP_TLS_TAFILE, &var_smtp_tls_tafile, 0, 0,
VAR_SMTP_TLS_PROTO, DEF_SMTP_TLS_PROTO, &var_smtp_tls_proto, 0, 0,
VAR_SMTP_TLS_CIPH, DEF_SMTP_TLS_CIPH, &var_smtp_tls_ciph, 1, 0,
VAR_SMTP_TLS_ECCERT_FILE, DEF_SMTP_TLS_ECCERT_FILE, &var_smtp_tls_eccert_file, 0, 0,
cipher_exclusions
= vstring_str(session->tls->exclusions),
matchargv = session->tls->matchargv,
- mdalg = var_smtp_tls_fpt_dgst);
+ mdalg = var_smtp_tls_fpt_dgst,
+ dane = session->tls->dane);
vstring_free(serverid);
if (session->tls_context == 0) {
* server, so no need to disable I/O, ... we can even be polite and send
* "QUIT".
*
- * See src/tls/tls_level.c. Levels above encrypt require matching. Levels >=
- * verify require CA trust.
+ * See src/tls/tls_level.c. Levels above encrypt require matching.
+ * Levels >= verify require CA trust.
+ *
+ * The DANE level is a hybrid of verify and fingerprint, if we have
+ * trust-anchors, we must do name checking, so we treat like verify.
+ * We also do fingerprint verification against any end-entity certs,
+ * so it'll work for that too.
+ *
+ * If we only have end-entity certs, then it is just like fingerprint.
+ *
+ * If we have no usable certs at all, but TLSA records were found,
+ * we do "encrypt". Contrary to draft-ietf-dane-srv, we can't
+ * do standard PKIX as a fallback, it will almost always fail,
+ * with no human present to "click ok".
+ *
+ * The DANE security level is for now still disabled in tls_level.c
+ * When it is enabled, we're ready to enforce its constraints.
*/
if (session->tls->level >= TLS_LEV_VERIFY)
if (!TLS_CERT_IS_TRUSTED(session->tls_context))
/*
* When the destination argument of smtp_tls_sess_alloc() is null, a
* trivial TLS policy (level = "none") is returned unconditionally and
- * the other arguments are not used. Soon the DSN_BUF "why" argument
- * will be optional when the caller is not interested in the error
- * status.
+ * the other arguments are not used. The DSN_BUF "why" argument is
+ * optional when the caller is not interested in the error status.
*/
#define NO_DSN_BUF (DSN_BUF *) 0
#define NO_DEST (char *) 0
session->tls_retry_plain = 0;
session->tls_nexthop = 0;
if (flags & SMTP_MISC_FLAG_NO_TLS)
- session->tls = smtp_tls_sess_alloc(NO_DSN_BUF, NO_DEST, NO_HOST,
- NO_PORT, NO_FLAGS);
+ session->tls = smtp_tls_policy(NO_DSN_BUF, NO_DEST, NO_HOST,
+ NO_PORT, NO_FLAGS);
else {
if (why == 0)
msg_panic("%s: null error buffer", myname);
- session->tls = smtp_tls_sess_alloc(why, dest, host, port, valid);
+ session->tls = smtp_tls_policy(why, dest, host, port, valid);
}
if (!session->tls) {
smtp_session_free(session);
var_smtp_starttls_tmout, 0, session->tls_context);
}
if (session->tls)
- (void) smtp_tls_sess_free(session->tls);
+ smtp_tls_policy_free(session->tls);
#endif
if (session->stream)
vstream_fclose(session->stream);
int valid)
{
#ifdef USE_TLS
- static DSN_BUF *why;
- SMTP_TLS_SESS *tls;
+ int needed = 1;
+ SMTP_TLS_POLICY *tls;
- if (!why)
- why = dsb_create();
-
- tls = smtp_tls_sess_alloc(why, dest, host, ntohs(port), valid);
- dsb_reset(why);
-
- if (tls && tls->level >= TLS_LEV_NONE && tls->level <= TLS_LEV_MAY)
- return (0);
- if (tls)
- smtp_tls_sess_free(tls);
- return (1);
+ /* Say "no" only when we're sure. Otherwise, "yes". */
+ if ((tls = smtp_tls_policy(0, dest, host, ntohs(port), valid)) != 0) {
+ needed = tls->level >= TLS_LEV_NONE && tls->level <= TLS_LEV_MAY;
+ smtp_tls_policy_free(tls);
+ }
+ return (needed);
#else
return (0);
#endif
void smtp_state_free(SMTP_STATE *state)
{
+#ifdef USE_TLS
+ /* The TLS policy cache lifetime is one delivery. */
+ smtp_tls_policy_flush();
+#endif
if (state->dest_label)
vstring_free(state->dest_label);
if (state->dest_prop)
/*++
/* NAME
-/* smtp_tls_sess 3
+/* smtp_tls_policy 3
/* SUMMARY
-/* SMTP_TLS_SESS structure management
+/* SMTP_TLS_POLICY structure management
/* SYNOPSIS
/* #include "smtp.h"
/*
/* void smtp_tls_list_init()
/*
-/* SMTP_TLS_SESS *smtp_tls_sess_alloc(why, dest, host, port, valid)
+/* SMTP_TLS_POLICY *smtp_tls_policy(why, dest, host, port, valid)
/* DSN_BUF *why;
/* char *dest;
/* char *host;
/* unsigned port;
/* int valid;
/*
-/* SMTP_TLS_SESS *smtp_tls_sess_free(tls)
-/* SMTP_TLS_SESS *tls;
+/* void smtp_tls_policy_free(tls)
+/* SMTP_TLS_POLICY *tls;
+/*
+/* void smtp_tls_policy_flush()
/* DESCRIPTION
/* smtp_tls_list_init() initializes lookup tables used by the TLS
/* policy engine.
/*
-/* smtp_tls_sess_alloc() allocates memory for an SMTP_TLS_SESS structure
-/* and initializes it based on the given information. Any required
-/* table and DNS lookups can fail. When this happens, "why" is updated
-/* with the error reason and a null pointer is returned. NOTE: the
-/* port is in network byte order. If "dest" is null, no policy checks are
-/* made, rather a trivial policy with tls disabled is returned (the
-/* remaining arguments are unused in this case and may be null).
+/* smtp_tls_policy() returns the SMTP_TLS_POLICY structure for
+/* the destination, host, port and DNSSEC validation status. Any of
+/* the required table and DNS lookups can fail. When this happens, and
+/* "why" is non-null, it is updated with the error reason and a null
+/* policy is returned. NOTE: the port is in network byte order. If
+/* "dest" is null, no policy checks are made, rather a trivial policy
+/* with TLS disabled is returned. The caller must free the policy via
+/* smtp_tls_policy_free().
+/*
+/* smtp_tls_policy_free() frees the SMTP_TLS_POLICY object. The
+/* objects are reference counted, so storage is deallocated when
+/* the reference count drops to zero. Since the objects are also
+/* cached, this typically happens when the cached is flushed, provided
+/* all other references have been released.
/*
-/* smtp_tls_sess_free() destroys an SMTP_TLS_SESS structure and its
-/* members. A null pointer is returned for convenience.
+/* smtp_tls_policy_flush() frees the cache contents and cache object.
/*
/* Arguments:
+/* .IP why
+/* A pointer to a DSN_BUF which holds error status information when
+/* the TLS policy lookup fails.
/* .IP dest
/* The unmodified next-hop or fall-back destination including
/* the optional [] and including the optional port or service.
/* The remote port, network byte order.
/* .IP valid
/* The DNSSEC validation status of the host name.
+/* .IP tls
+/* An SMTP_TLS_POLICY object.
/* LICENSE
/* .ad
/* .fi
#ifdef USE_TLS
+#include <netinet/in.h> /* ntohs() for Solaris or BSD */
+#include <arpa/inet.h> /* ntohs() for Linux or BSD */
#include <stdlib.h>
#include <string.h>
#include <vstring.h>
#include <stringops.h>
#include <valid_hostname.h>
+#include <ctable.h>
/* Global library. */
#include "smtp.h"
+#define CACHE_SIZE 20
+static CTABLE *policy_cache;
+
+static int global_tls_level(void);
static MAPS *tls_policy; /* lookup table(s) */
static MAPS *tls_per_site; /* lookup table(s) */
return name;
}
+#define MARK_INVALID(why, levelp) do { \
+ dsb_simple((why), "4.7.5", "client TLS configuration problem"); \
+ *(levelp) = TLS_LEV_INVALID; } while (0)
+
/* tls_site_lookup - look up per-site TLS security level */
-static void tls_site_lookup(SMTP_TLS_SESS *tls, int *site_level,
- const char *site_name, const char *site_class,
- DSN_BUF *why)
+static void tls_site_lookup(SMTP_TLS_POLICY *tls, int *site_level,
+ const char *site_name, const char *site_class)
{
const char *lookup;
} else {
msg_warn("%s: unknown TLS policy '%s' for %s %s",
tls_per_site->title, lookup, site_class, site_name);
- dsb_simple(why, "4.7.5", "client TLS configuration problem");
- *site_level = TLS_LEV_INVALID;
+ MARK_INVALID(tls->why, site_level);
return;
}
} else if (tls_per_site->error) {
msg_warn("%s: %s \"%s\": per-site table lookup error",
tls_per_site->title, site_class, site_name);
- dsb_simple(why, "4.3.0", "Temporary lookup error");
+ dsb_simple(tls->why, "4.3.0", "Temporary lookup error");
*site_level = TLS_LEV_INVALID;
return;
}
/* tls_policy_lookup_one - look up destination TLS policy */
-static void tls_policy_lookup_one(SMTP_TLS_SESS *tls, int *site_level,
- const char *site_name,
- const char *site_class, DSN_BUF *why)
+static void tls_policy_lookup_one(SMTP_TLS_POLICY *tls, int *site_level,
+ const char *site_name,
+ const char *site_class)
{
const char *lookup;
char *policy;
#undef FREE_RETURN
#define FREE_RETURN do { myfree(saved_policy); return; } while (0)
+#define INVALID_RETURN(why, levelp) do { \
+ MARK_INVALID((why), (levelp)); FREE_RETURN; } while (0)
+
#define WHERE \
vstring_str(vstring_sprintf(cbuf, "%s, %s \"%s\"", \
tls_policy->title, site_class, site_name))
if ((lookup = maps_find(tls_policy, site_name, 0)) == 0) {
if (tls_policy->error) {
msg_warn("%s: policy table lookup error", WHERE);
- dsb_simple(why, "4.3.0", "Temporary lookup error");
- *site_level = TLS_LEV_INVALID;
+ MARK_INVALID(tls->why, site_level);
}
return;
}
if ((tok = mystrtok(&policy, "\t\n\r ,")) == 0) {
msg_warn("%s: invalid empty policy", WHERE);
- dsb_simple(why, "4.7.5", "client TLS configuration problem");
- *site_level = TLS_LEV_INVALID;
- FREE_RETURN;
+ INVALID_RETURN(tls->why, site_level);
}
*site_level = tls_level_lookup(tok);
if (*site_level == TLS_LEV_INVALID) {
/* tls_level_lookup() logs no warning. */
msg_warn("%s: invalid security level \"%s\"", WHERE, tok);
- dsb_simple(why, "4.7.5", "client TLS configuration problem");
- FREE_RETURN;
+ INVALID_RETURN(tls->why, site_level);
}
/*
FREE_RETURN;
}
+ /*
+ * The fingerprint, verify and secure levels require or
+ * support explicit TA or EE certificate digest match lists.
+ */
+ if (*site_level >= TLS_LEV_FPRINT)
+ tls->dane = tls_dane_alloc(TLS_DANE_FLAG_MIXED);
+
/*
* Errors in attributes may have security consequences, don't ignore
* errors that can degrade security.
if ((err = split_nameval(tok, &name, &val)) != 0) {
msg_warn("%s: malformed attribute/value pair \"%s\": %s",
WHERE, tok, err);
- dsb_simple(why, "4.7.5", "client TLS configuration problem");
- *site_level = TLS_LEV_INVALID;
- FREE_RETURN;
+ INVALID_RETURN(tls->why, site_level);
}
/* Only one instance per policy. */
if (!strcasecmp(name, "ciphers")) {
if (*val == 0) {
msg_warn("%s: attribute \"%s\" has empty value", WHERE, name);
- dsb_simple(why, "4.7.5", "client TLS configuration problem");
- *site_level = TLS_LEV_INVALID;
- FREE_RETURN;
+ INVALID_RETURN(tls->why, site_level);
}
if (tls->grade) {
msg_warn("%s: attribute \"%s\" is specified multiple times",
WHERE, name);
- dsb_simple(why, "4.7.5", "client TLS configuration problem");
- *site_level = TLS_LEV_INVALID;
- FREE_RETURN;
+ INVALID_RETURN(tls->why, site_level);
}
tls->grade = mystrdup(val);
continue;
if (tls->protocols) {
msg_warn("%s: attribute \"%s\" is specified multiple times",
WHERE, name);
- dsb_simple(why, "4.7.5", "client TLS configuration problem");
- *site_level = TLS_LEV_INVALID;
- FREE_RETURN;
+ INVALID_RETURN(tls->why, site_level);
}
tls->protocols = mystrdup(val);
continue;
}
/* Multiple instances per policy. */
if (!strcasecmp(name, "match")) {
- char *delim = *site_level == TLS_LEV_FPRINT ? "|" : ":";
-
if (*site_level <= TLS_LEV_ENCRYPT) {
msg_warn("%s: attribute \"%s\" invalid at security level "
"\"%s\"", WHERE, name, policy_name(*site_level));
- dsb_simple(why, "4.7.5", "client TLS configuration problem");
- *site_level = TLS_LEV_INVALID;
- FREE_RETURN;
+ INVALID_RETURN(tls->why, site_level);
}
if (*val == 0) {
msg_warn("%s: attribute \"%s\" has empty value", WHERE, name);
- dsb_simple(why, "4.7.5", "client TLS configuration problem");
- *site_level = TLS_LEV_INVALID;
- FREE_RETURN;
+ INVALID_RETURN(tls->why, site_level);
+ }
+ switch (*site_level) {
+ case TLS_LEV_FPRINT:
+ tls_dane_split(tls->dane, TLS_DANE_EE, TLS_DANE_PKEY,
+ var_smtp_tls_fpt_dgst, val, "|");
+ break;
+ case TLS_LEV_VERIFY:
+ case TLS_LEV_SECURE:
+ if (tls->matchargv == 0)
+ tls->matchargv = argv_split(val, ":");
+ else
+ argv_split_append(tls->matchargv, val, ":");
+ break;
}
- if (tls->matchargv == 0)
- tls->matchargv = argv_split(val, delim);
- else
- argv_split_append(tls->matchargv, val, delim);
continue;
}
/* Only one instance per policy. */
if (tls->exclusions) {
msg_warn("%s: attribute \"%s\" is specified multiple times",
WHERE, name);
- dsb_simple(why, "4.7.5", "client TLS configuration problem");
- *site_level = TLS_LEV_INVALID;
- FREE_RETURN;
+ INVALID_RETURN(tls->why, site_level);
}
tls->exclusions = vstring_strcpy(vstring_alloc(10), val);
continue;
}
+ /* Multiple instances per policy. */
+ if (!strcasecmp(name, "tafile")) {
+ /* Only makes sense if we're using CA-based trust */
+ if (*site_level <= TLS_LEV_ENCRYPT) {
+ msg_warn("%s: attribute \"%s\" invalid at security level"
+ " \"%s\"", WHERE, name, policy_name(*site_level));
+ INVALID_RETURN(tls->why, site_level);
+ }
+ if (*val == 0) {
+ msg_warn("%s: attribute \"%s\" has empty value", WHERE, name);
+ INVALID_RETURN(tls->why, site_level);
+ }
+ if (!tls_dane_load_trustfile(tls->dane, val)) {
+ INVALID_RETURN(tls->why, site_level);
+ }
+ continue;
+ }
+
msg_warn("%s: invalid attribute name: \"%s\"", WHERE, name);
- dsb_simple(why, "4.7.5", "client TLS configuration problem");
- *site_level = TLS_LEV_INVALID;
- FREE_RETURN;
+ INVALID_RETURN(tls->why, site_level);
}
FREE_RETURN;
/* tls_policy_lookup - look up destination TLS policy */
-static void tls_policy_lookup(SMTP_TLS_SESS *tls, int *site_level,
+static void tls_policy_lookup(SMTP_TLS_POLICY *tls, int *site_level,
const char *site_name,
- const char *site_class, DSN_BUF *why)
+ const char *site_class)
{
/*
* sub-domains of the recipient domain.
*/
if (!valid_hostname(site_name, DONT_GRIPE)) {
- tls_policy_lookup_one(tls, site_level, site_name, site_class, why);
+ tls_policy_lookup_one(tls, site_level, site_name, site_class);
return;
}
do {
- tls_policy_lookup_one(tls, site_level, site_name, site_class, why);
+ tls_policy_lookup_one(tls, site_level, site_name, site_class);
} while (*site_level == TLS_LEV_NOTFOUND
&& (site_name = strchr(site_name + 1, '.')) != 0);
}
+/* load_tas - load one or more ta files */
+
+static int load_tas(TLS_DANE *dane, const char *files)
+{
+ int ret = 0;
+ char *save = mystrdup(files);
+ char *buf = save;
+ char *file;
+
+ do {
+ if ((file = mystrtok(&buf, "\t\n\r ,")) != 0)
+ ret = tls_dane_load_trustfile(dane, file);
+ } while (file && ret);
+
+ myfree(save);
+ return (ret);
+}
+
/* set_cipher_grade - Set cipher grade and exclusions */
-static void set_cipher_grade(SMTP_TLS_SESS *tls)
+static void set_cipher_grade(SMTP_TLS_POLICY *tls)
{
const char *mand_exclude = "";
const char *also_exclude = "";
ADD_EXCLUDE(tls->exclusions, also_exclude);
}
-/* smtp_tls_sess_alloc - session TLS policy parameters */
+/* tls_policy_init - initialize policy in an embryonic cache entry */
-SMTP_TLS_SESS *smtp_tls_sess_alloc(DSN_BUF *why, const char *dest,
- const char *host, unsigned port, int valid)
+static void tls_policy_init(SMTP_TLS_POLICY *tls, const char *dest,
+ const char *host, unsigned port, int valid)
{
- const char *myname = "smtp_tls_sess_alloc";
- int global_level;
+ const char *myname = "tls_policy_init";
int site_level;
- SMTP_TLS_SESS *tls = (SMTP_TLS_SESS *) mymalloc(sizeof(*tls));
-
- tls->level = TLS_LEV_NONE;
- tls->protocols = 0;
- tls->grade = 0;
- tls->exclusions = 0;
- tls->matchargv = 0;
-
- if (!dest)
- return (tls);
- /*
- * Compute the global TLS policy. This is the default policy level when
- * no per-site policy exists. It also is used to override a wild-card
- * per-site policy.
- */
- if (*var_smtp_tls_level) {
- /* Require that var_smtp_tls_level is sanitized upon startup. */
- global_level = tls_level_lookup(var_smtp_tls_level);
- if (global_level == TLS_LEV_INVALID)
- msg_panic("%s: invalid TLS security level: \"%s\"",
- myname, var_smtp_tls_level);
- } else if (var_smtp_enforce_tls) {
- global_level = var_smtp_tls_enforce_peername ?
- TLS_LEV_VERIFY : TLS_LEV_ENCRYPT;
- } else {
- global_level = var_smtp_use_tls ?
- TLS_LEV_MAY : TLS_LEV_NONE;
+ /* Caller requested trivial policy */
+ if (!dest) {
+ tls->level = TLS_LEV_NONE;
+ return;
}
- if (msg_verbose)
- msg_info("%s TLS level: %s", "global", policy_name(global_level));
/*
* Compute the per-site TLS enforcement level. For compatibility with the
* original TLS patch, this algorithm is gives equal precedence to host
* and next-hop policies.
*/
+ tls->level = global_tls_level();
site_level = TLS_LEV_NOTFOUND;
if (tls_policy) {
- tls_policy_lookup(tls, &site_level, dest, "next-hop destination", why);
+ tls_policy_lookup(tls, &site_level, dest, "next-hop destination");
} else if (tls_per_site) {
- tls_site_lookup(tls, &site_level, dest, "next-hop destination", why);
+ tls_site_lookup(tls, &site_level, dest, "next-hop destination");
if (site_level != TLS_LEV_INVALID
&& strcasecmp(dest, host) != 0)
- tls_site_lookup(tls, &site_level, host, "server hostname", why);
+ tls_site_lookup(tls, &site_level, host, "server hostname");
/*
* Override a wild-card per-site policy with a more specific global
* (non-wildcard) per-site policies consistently override global
* policies.
*/
- if (site_level == TLS_LEV_MAY && global_level > TLS_LEV_MAY)
- site_level = global_level;
+ if (site_level == TLS_LEV_MAY && tls->level > TLS_LEV_MAY)
+ site_level = tls->level;
}
switch (site_level) {
- case TLS_LEV_INVALID:
- return (smtp_tls_sess_free(tls));
- case TLS_LEV_NOTFOUND:
- tls->level = global_level;
- break;
default:
tls->level = site_level;
+ case TLS_LEV_NOTFOUND:
break;
+ case TLS_LEV_INVALID:
+ return;
}
/*
case TLS_LEV_DANE:
break;
case TLS_LEV_FPRINT:
- if (tls->matchargv == 0)
- tls->matchargv =
- argv_split(var_smtp_tls_fpt_cmatch, "\t\n\r, |");
+ if (tls->dane == 0)
+ tls->dane = tls_dane_alloc(TLS_DANE_FLAG_MIXED);
+ if (!TLS_DANE_HASEE(tls->dane)) {
+ tls_dane_split(tls->dane, TLS_DANE_EE, TLS_DANE_PKEY,
+ var_smtp_tls_fpt_dgst, var_smtp_tls_fpt_cmatch,
+ "\t\n\r, ");
+ if (!TLS_DANE_HASEE(tls->dane)) {
+ msg_warn("nexthop domain %s: configured at fingerprint "
+ "security level, but with no fingerprints to match.",
+ dest);
+ MARK_INVALID(tls->why, &tls->level);
+ return;
+ }
+ }
+ tls_dane_final(tls->dane);
break;
case TLS_LEV_VERIFY:
- if (tls->matchargv == 0)
- tls->matchargv =
- argv_split(var_smtp_tls_vfy_cmatch, "\t\n\r, :");
- break;
case TLS_LEV_SECURE:
if (tls->matchargv == 0)
tls->matchargv =
- argv_split(var_smtp_tls_sec_cmatch, "\t\n\r, :");
+ argv_split(tls->level == TLS_LEV_VERIFY ?
+ var_smtp_tls_vfy_cmatch : var_smtp_tls_sec_cmatch,
+ "\t\n\r, :");
+ if (*var_smtp_tls_tafile) {
+ if (tls->dane == 0)
+ tls->dane = tls_dane_alloc(TLS_DANE_FLAG_MIXED);
+ if (!TLS_DANE_HASTA(tls->dane)) {
+ if (load_tas(tls->dane, var_smtp_tls_tafile))
+ tls_dane_final(tls->dane);
+ else {
+ MARK_INVALID(tls->why, &tls->level);
+ return;
+ }
+ }
+ }
break;
default:
- msg_panic("unexpected TLS security level: %d",
- tls->level);
+ msg_panic("unexpected TLS security level: %d", tls->level);
}
- if (msg_verbose && (tls_policy || tls_per_site))
+ if (msg_verbose && tls->level != global_tls_level())
msg_info("%s TLS level: %s", "effective", policy_name(tls->level));
-
- return (tls);
}
-/* smtp_sess_tls_free - free and return null pointer of same type */
+/* tls_policy_free - free no longer cached policy */
-SMTP_TLS_SESS *smtp_tls_sess_free(SMTP_TLS_SESS *tls)
+void smtp_tls_policy_free(SMTP_TLS_POLICY *tls)
{
+ if (--tls->refs > 0)
+ return;
+
if (tls->protocols)
myfree(tls->protocols);
if (tls->grade)
vstring_free(tls->exclusions);
if (tls->matchargv)
argv_free(tls->matchargv);
+ if (tls->dane)
+ tls_dane_free(tls->dane);
+ dsb_free(tls->why);
myfree((char *) tls);
+}
+
+/* policy_create - create embryonic SMTP TLS policy, ctable glue. */
+
+static void *policy_create(const char *key, void *unused_context)
+{
+ SMTP_TLS_POLICY *tls = (SMTP_TLS_POLICY *)mymalloc(sizeof(*tls));
+
+ tls->refs = 1;
+
+ /* First use will update level to a real level or TLS_LEV_INVALID */
+ tls->level = TLS_LEV_NOTFOUND;
+ tls->protocols = 0;
+ tls->grade = 0;
+ tls->exclusions = 0;
+ tls->matchargv = 0;
+ tls->why = dsb_create();
+ tls->dane = 0;
+
+ return ((void *) tls);
+}
+
+/* policy_delete - delete SMTP TLS policy, ctable glue. */
+
+static void policy_delete(void *item, void *unused_context)
+{
+ smtp_tls_policy_free((SMTP_TLS_POLICY *)item);
+}
+
+/* smtp_tls_policy - cached lookup of TLS policy */
+
+SMTP_TLS_POLICY *smtp_tls_policy(DSN_BUF *why, const char *dest,
+ const char *host, unsigned port, int valid)
+{
+ SMTP_TLS_POLICY *tls;
+ VSTRING *key;
+
+ if (policy_cache == 0)
+ policy_cache =
+ ctable_create(CACHE_SIZE, policy_create, policy_delete, 0);
+
+ if (dest) {
+ key = vstring_alloc(strlen(dest) + strlen(host) + 10);
+ vstring_sprintf(key, "%s %s:%u %d", dest, host, ntohs(port), !!valid);
+ tls = (SMTP_TLS_POLICY *)ctable_locate(policy_cache, vstring_str(key));
+ vstring_free(key);
+ } else {
+ tls = (SMTP_TLS_POLICY *)ctable_locate(policy_cache, "");
+ }
+
+ /* One-time initialization */
+ if (tls->level == TLS_LEV_NOTFOUND)
+ tls_policy_init(tls, dest, host, port, valid);
+
+ if (tls->level != TLS_LEV_INVALID) {
+ ++tls->refs;
+ return (tls);
+ }
+
+ if (why)
+ dsb_update(why,
+ STR(tls->why->status), STR(tls->why->action),
+ STR(tls->why->mtype), STR(tls->why->mname),
+ STR(tls->why->dtype), STR(tls->why->dtext),
+ "%s", STR(tls->why->reason));
return (0);
}
+/* smtp_tls_policy_flush - flush TLS policy cache */
+
+void smtp_tls_policy_flush(void)
+{
+ if (policy_cache == 0)
+ return;
+ ctable_free(policy_cache);
+ policy_cache = 0;
+}
+
+/* global_tls_level - parse and cache var_smtp_tls_level */
+
+static int global_tls_level(void)
+{
+ static int l = TLS_LEV_NOTFOUND;
+
+ if (l != TLS_LEV_NOTFOUND)
+ return l;
+
+ /*
+ * Compute the global TLS policy. This is the default policy level when
+ * no per-site policy exists. It also is used to override a wild-card
+ * per-site policy.
+ *
+ * We require that the global level is valid on startup.
+ */
+ if (*var_smtp_tls_level) {
+ if ((l = tls_level_lookup(var_smtp_tls_level)) == TLS_LEV_INVALID)
+ msg_fatal("invalid tls security level: \"%s\"", var_smtp_tls_level);
+ } else if (var_smtp_enforce_tls)
+ l = var_smtp_tls_enforce_peername ? TLS_LEV_VERIFY : TLS_LEV_ENCRYPT;
+ else
+ l = var_smtp_use_tls ? TLS_LEV_MAY : TLS_LEV_NONE;
+
+ if (msg_verbose)
+ msg_info("%s TLS level: %s", "global", policy_name(l));
+
+ return l;
+}
+
#endif
SHELL = /bin/sh
SRCS = tls_prng_dev.c tls_prng_egd.c tls_prng_file.c tls_fprint.c \
tls_prng_exch.c tls_stream.c tls_bio_ops.c tls_misc.c tls_dh.c \
- tls_rsa.c tls_verify.c tls_certkey.c tls_session.c \
+ tls_rsa.c tls_verify.c tls_dane.c tls_certkey.c tls_session.c \
tls_client.c tls_server.c tls_scache.c tls_mgr.c tls_seed.c \
tls_level.c \
tls_proxy_clnt.c tls_proxy_print.c tls_proxy_scan.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_rsa.o tls_verify.o tls_certkey.o tls_session.o \
+ tls_rsa.o tls_verify.o tls_dane.o tls_certkey.o tls_session.o \
tls_client.o tls_server.o tls_scache.o tls_mgr.o tls_seed.o \
tls_level.o \
tls_proxy_clnt.o tls_proxy_print.o tls_proxy_scan.o
tls_client.o: tls.h
tls_client.o: tls_client.c
tls_client.o: tls_mgr.h
+tls_dane.o: ../../include/argv.h
+tls_dane.o: ../../include/dns.h
+tls_dane.o: ../../include/msg.h
+tls_dane.o: ../../include/myaddrinfo.h
+tls_dane.o: ../../include/mymalloc.h
+tls_dane.o: ../../include/name_code.h
+tls_dane.o: ../../include/name_mask.h
+tls_dane.o: ../../include/sock_addr.h
+tls_dane.o: ../../include/stringops.h
+tls_dane.o: ../../include/sys_defs.h
+tls_dane.o: ../../include/vbuf.h
+tls_dane.o: ../../include/vstream.h
+tls_dane.o: ../../include/vstring.h
+tls_dane.o: tls.h
+tls_dane.o: tls_dane.c
tls_dh.o: ../../include/argv.h
tls_dh.o: ../../include/mail_params.h
tls_dh.o: ../../include/msg.h
#define TLS_MGR_SCACHE_SMTP "smtp"
#define TLS_MGR_SCACHE_LMTP "lmtp"
+ /*
+ * RFC 6698 DANE
+ */
+#define TLS_DANE_TA 0 /* Match trust-anchor digests */
+#define TLS_DANE_EE 1 /* Match end-entity digests */
+
+#define TLS_DANE_CERT 0 /* Match the certificate digest */
+#define TLS_DANE_PKEY 1 /* Match the public key digest */
+
+#define TLS_DANE_FLAG_MIXED (1<<0) /* Combined pkeys and certs */
+#define TLS_DANE_FLAG_FINAL (1<<1) /* No further changes */
+
+ /*
+ * Linked list of either t = X509 certs or t = EVP_PKEY public keys
+ */
+#define TLS_DANE_L(t) TLS_DANE_##t##_L
+
+#define DECL_TLS_DANE_L(t) \
+ typedef struct TLS_DANE_L(t) { \
+ t *item; \
+ struct TLS_DANE_L(t) *next; \
+ } TLS_DANE_L(t)
+
+#define TLS_DANE_Z(t) ((TLS_DANE_L(t) *)0)
+#define TLS_DANE_H(t) t##_head
+#define TLS_DANE_HEAD(d, t) ((d) ? (d)->TLS_DANE_H(t) : TLS_DANE_Z(t))
+#define TLS_DANE_MEMB(t) TLS_DANE_L(t) *TLS_DANE_H(t)
+
+DECL_TLS_DANE_L(X509);
+DECL_TLS_DANE_L(EVP_PKEY);
+
+ /*
+ * Certificate and public key digests (typically from TLSA RRs),
+ * grouped by algorithm.
+ */
+typedef struct TLS_TLSA {
+ char *mdalg; /* Algorithm for this digest list */
+ ARGV *certs; /* Complete certificate digests */
+ ARGV *pkeys; /* SubjectPublicKeyInfo digests */
+ struct TLS_TLSA *next; /* Chain to next algorithm */
+} TLS_TLSA;
+
+ /*
+ * When TLS_DANE_FLAG_MIXED is set, the pkeys digest list is not allocated
+ * separately, and aliases the certs digest list for each algorithm.
+ */
+typedef struct TLS_DANE {
+ TLS_TLSA *ta; /* Trust-anchor cert/pubkey digests */
+ TLS_TLSA *ee; /* End-entity cert/pubkey digests */
+ TLS_DANE_MEMB(X509); /* Full trust-anchor certificates */
+ TLS_DANE_MEMB(EVP_PKEY); /* Full trust-anchor public keys */
+ int flags; /* Conflate cert and pkey digests */
+} TLS_DANE;
+
+#define TLS_DANE_HASTA(d) ((d) ? (d)->ta : 0)
+#define TLS_DANE_HASEE(d) ((d) ? (d)->ee : 0)
+
+#define TLS_DANE_LINSERT(t) dane_##t##_list_insert
+#define TLS_DANE_LFREE(t) dane_##t##_list_free
+#define IMPL_TLS_DANE_L(t) \
+ static void TLS_DANE_LINSERT(t)(TLS_DANE *d, t *i) \
+ { \
+ TLS_DANE_L(t) *new = (TLS_DANE_L(t) *) mymalloc(sizeof(*new)); \
+ CRYPTO_add(&i->references, 1, CRYPTO_LOCK_##t); \
+ new->item = i; \
+ new->next = d->TLS_DANE_H(t); \
+ d->TLS_DANE_H(t) = new; \
+ } \
+ static void TLS_DANE_LFREE(t)(TLS_DANE *d) \
+ { \
+ TLS_DANE_L(t) *head; \
+ TLS_DANE_L(t) *next; \
+ for (head = TLS_DANE_HEAD(d, t); head; head = next) { \
+ next = head->next; \
+ t##_free(head->item); \
+ } \
+ }
+
+ /*
+ * tls_dane.c
+ */
+extern int tls_dane_avail(void);
+extern void tls_dane_verbose(int);
+extern TLS_DANE *tls_dane_alloc(int);
+extern void tls_dane_split(TLS_DANE *, int, int, const char *, const char *,
+ const char *);
+extern TLS_DANE *tls_dane_final(TLS_DANE *);
+extern void tls_dane_free(TLS_DANE *);
+extern int tls_dane_load_trustfile(TLS_DANE *, const char *);
+
/*
* TLS session context, also used by the VSTREAM call-back routines for SMTP
* input/output, and by OpenSSL call-back routines for key verification.
const char *mdalg; /* default message digest algorithm */
/* Built-in vs external SSL_accept/read/write/shutdown support. */
VSTREAM *stream; /* Blocking-mode SMTP session */
+ /* RFC 6698 DANE trust input and verification state */
+ const TLS_DANE *dane; /* DANE TLSA digests */
+ int trustdepth; /* Chain depth of trusted cert */
int errordepth; /* Chain depth of error cert */
+ int chaindepth; /* Chain depth of top cert */
int errorcode; /* First error at error depth */
X509 *errorcert; /* Error certificate closest to leaf */
} TLS_SESS_STATE;
const char *cipher_exclusions; /* Ciphers to exclude */
const ARGV *matchargv; /* Cert match patterns */
const char *mdalg; /* default message digest algorithm */
+ const TLS_DANE *dane; /* RFC 6698 verification */
} TLS_CLIENT_START_PROPS;
extern TLS_APPL_STATE *tls_client_init(const TLS_CLIENT_INIT_PROPS *);
((props)->a12), ((props)->a13), (props)))
#define TLS_CLIENT_START(props, a1, a2, a3, a4, a5, a6, a7, a8, a9, \
- a10, a11, a12, a13, a14) \
+ a10, a11, a12, a13, a14, a15) \
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)))
+ ((props)->a12), ((props)->a13), ((props)->a14), ((props)->a15), (props)))
/*
* tls_server.c
extern char *tls_peer_CN(X509 *, const TLS_SESS_STATE *);
extern char *tls_issuer_CN(X509 *, const TLS_SESS_STATE *);
extern const char *tls_dns_name(const GENERAL_NAME *, const TLS_SESS_STATE *);
+extern int tls_cert_match(TLS_SESS_STATE *, int, X509 *, int);
extern int tls_verify_certificate_callback(int, X509_STORE_CTX *);
extern void tls_log_verify_error(TLS_SESS_STATE *);
if (SSL_get_verify_result(TLScontext->con) == X509_V_OK)
TLScontext->peer_status |= TLS_CERT_FLAG_TRUSTED;
- if (TLS_CERT_IS_TRUSTED(TLScontext) && props->tls_level >= TLS_LEV_VERIFY)
+ if (TLS_CERT_IS_TRUSTED(TLScontext)
+ && (props->tls_level >= TLS_LEV_VERIFY || TLS_DANE_HASTA(props->dane)))
verify_peername = 1;
/* Force cert processing so we can log the data? */
static void verify_extract_print(TLS_SESS_STATE *TLScontext, X509 *peercert,
const TLS_CLIENT_START_PROPS *props)
{
- char **cpp;
-
- /* Non-null by contract */
TLScontext->peer_fingerprint = tls_fingerprint(peercert, props->mdalg);
TLScontext->peer_pkey_fprint = tls_pkey_fprint(peercert, props->mdalg);
- /*
- * Compare the fingerprint against each acceptable value, ignoring
- * upper/lower case differences.
- */
- if (props->tls_level == TLS_LEV_FPRINT) {
- for (cpp = props->matchargv->argv; *cpp; ++cpp) {
- if (strcasecmp(TLScontext->peer_fingerprint, *cpp) == 0
- || strcasecmp(TLScontext->peer_pkey_fprint, *cpp) == 0) {
- TLScontext->peer_status |=
- TLS_CERT_FLAG_TRUSTED | TLS_CERT_FLAG_MATCHED;
- break;
- }
- }
- }
+ if (TLS_DANE_HASEE(props->dane)
+ && tls_cert_match(TLScontext, TLS_DANE_EE, peercert, 0))
+ TLScontext->peer_status |=
+ TLS_CERT_FLAG_TRUSTED | TLS_CERT_FLAG_MATCHED;
}
/*
* When certificate verification is required, log trust chain validation
* errors even when disabled by default for opportunistic sessions.
*/
- if (props->tls_level >= TLS_LEV_VERIFY)
+ if (props->tls_level >= TLS_LEV_VERIFY || TLS_DANE_HASTA(props->dane))
log_mask |= TLS_LOG_UNTRUSTED;
if (log_mask & TLS_LOG_VERBOSE)
* not attempt to re-use sessions whose ciphers are too weak. We salt the
* session lookup key with the cipher list, so that sessions found in the
* cache are always acceptable.
+ *
+ * With DANE, (more generally any TLScontext where we specified explicit
+ * trust-anchor or end-entity certificates) the verification status of
+ * the SSL session depends on the specified list. Since we verify the
+ * certificate only during the initial handshake, we must segregate
+ * sessions with different TA lists. Note, that TA re-verification
+ * is not possible with cached sessions, since these don't hold the complete
+ * peer trust chain. Therefore, we compute a digest of the sorted TA
+ * parameters and append it to the serverid.
*/
myserverid = tls_serverid_digest(props, protomask, cipher_list);
TLScontext->stream = props->stream;
TLScontext->mdalg = props->mdalg;
+ /* Alias DANE digest info from props */
+ TLScontext->dane = props->dane;
+
if ((TLScontext->con = SSL_new(app_ctx->ssl_ctx)) == NULL) {
msg_warn("Could not allocate 'TLScontext->con' with SSL_new()");
tls_print_errors();
/*
* Peer name or fingerprint verification as requested.
* Unconditionally set peer_CN, issuer_CN and peer_fingerprint.
+ * Check fingerprint first, and avoid logging verified as untrusted
+ * in the call to verify_extract_name().
*/
- verify_extract_name(TLScontext, peercert, props);
verify_extract_print(TLScontext, peercert, props);
+ verify_extract_name(TLScontext, peercert, props);
if (TLScontext->log_mask &
(TLS_LOG_CERTMATCH | TLS_LOG_VERBOSE | TLS_LOG_PEERCERT))
--- /dev/null
+/*++
+/* NAME
+/* tls_dane 3
+/* SUMMARY
+/* Support for RFC 6698 (DANE) certificate matching
+/* SYNOPSIS
+/* #include <tls.h>
+/*
+/* int tls_dane_avail()
+/*
+/* void tls_dane_verbose(on)
+/* int on;
+/*
+/* TLS_DANE *tls_dane_alloc(flags)
+/* int flags;
+/*
+/* void tls_dane_free(dane)
+/* TLS_DANE *dane;
+/*
+/* void tls_dane_split(dane, certusage, selector, mdalg, digest, delim)
+/* TLS_DANE *dane;
+/* int certusage;
+/* int selector;
+/* const char *mdalg;
+/* const char *digest;
+/* const char *delim;
+/*
+/* int tls_dane_load_trustfile(dane, tafile)
+/* TLS_DANE *dane;
+/* const char *tafile;
+/*
+/* TLS_DANE *tls_dane_final(dane)
+/* TLS_DANE *dane;
+/* DESCRIPTION
+/* tls_dane_avail() returns true if the features required to support DANE
+/* are present in OpenSSL's libcrypto and in libresolv. Since OpenSSL's
+/* libcrypto is not initialized until we call tls_client_init(), calls
+/* to tls_dane_avail() must be deferred until this initialization is
+/* completed successufully.
+/*
+/* tls_dane_verbose() turns on verbose logging of TLSA record lookups.
+/*
+/* tls_dane_alloc() returns a pointer to a newly allocated TLS_DANE
+/* structure with null ta and ee digest sublists. If "flags" includes
+/* TLS_DANE_FLAG_MIXED, the cert and pkey digests are stored together on
+/* the pkeys list with the certs list empty, otherwise they are stored
+/* separately.
+/*
+/* tls_dane_free() frees the structure allocated by tls_dane_alloc().
+/*
+/* tls_dane_split() splits "digest" using the characters in "delim" as
+/* delimiters and stores the results with the requested "certusage"
+/* and "selector". This is an incremental interface, that builds a
+/* TLS_DANE structure outside the cache by manually adding entries.
+/* Once all the entries have been added, the caller must call
+/* tls_dane_final() to complete its construction.
+/*
+/* tls_dane_load_trustfile() imports trust-anchor certificates and
+/* public keys from a file (rather than DNS TLSA records).
+/*
+/* tls_dane_final() completes the construction of a TLS_DANE structure,
+/* obtained via tls_dane_alloc() and populated via tls_dane_split() or
+/* tls_dane_load_trustfile(). After tls_dane_final() is called, the
+/* structure must not be modified. The return value is the input
+/* argument.
+/*
+/* Arguments:
+/* .IP dane
+/* Pointer to a TLS_DANE structure that lists the valid trust-anchor
+/* and end-entity full-certificate and/or public-key digests.
+/* .IP host
+/* DNSSEC validated (cname resolved) FQDN of target service.
+/* .IP proto
+/* Almost certainly "tcp".
+/* .IP port
+/* The TCP port in network byte order.
+/* .IP flags
+/* Only one flag is part of the public interface at this time:
+/* .RS
+/* .IP TLS_DANE_FLAG_MIXED
+/* Don't distinguish between certificate and public-key digests.
+/* A single digest list for both certificates and keys with be
+/* stored for each algorithm in the "pkeys" field, the "certs"
+/* field will be null.
+/* .RE
+/* .IP certusage
+/* Trust anchor (TLS_DANE_TA) or end-entity (TLS_DANE_EE) digests?
+/* .IP selector
+/* Full certificate (TLS_DANE_CERT) or pubkey (TLS_DANE_PKEY) digests?
+/* .IP mdalg
+/* Name of a message digest algorithm suitable for computing secure
+/* (1st pre-image resistant) message digests of certificates. For now,
+/* md5, sha1, or member of SHA-2 family if supported by OpenSSL.
+/* .IP digest
+/* The digest (or list of digests concatenated with characters from
+/* "delim") to be added to the TLS_DANE record.
+/* .IP delim
+/* The set of delimiter characters used above.
+/* LICENSE
+/* .ad
+/* .fi
+/* This software is free. You can do with it whatever you want.
+/* The original author kindly requests that you acknowledge
+/* the use of his software.
+/* AUTHOR(S)
+/* Wietse Venema
+/* IBM T.J. Watson Research
+/* P.O. Box 704
+/* Yorktown Heights, NY 10598, USA
+/*
+/* Viktor Dukhovni
+/*--*/
+
+/* System library. */
+
+#include <sys_defs.h>
+#include <ctype.h>
+#include <stdint.h>
+
+#ifdef USE_TLS
+#include <string.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <mymalloc.h>
+#include <stringops.h>
+#include <vstring.h>
+
+#define STR(x) vstring_str(x)
+
+/* DNS library. */
+
+#include <dns.h>
+
+/* TLS library. */
+
+#define TLS_INTERNAL
+#include <tls.h>
+
+/* Application-specific. */
+
+static const char *sha256 = "sha256";
+static const char *sha512 = "sha512";
+static int sha256len;
+static int sha512len;
+static int dane_verbose;
+static TLS_TLSA **dane_locate(TLS_TLSA **, const char *);
+
+IMPL_TLS_DANE_L(X509)
+IMPL_TLS_DANE_L(EVP_PKEY)
+
+/* tls_dane_verbose - enable/disable verbose logging */
+
+void tls_dane_verbose(int on)
+{
+ dane_verbose = on;
+}
+
+/* tls_dane_avail - check for availability of dane required digests */
+
+int tls_dane_avail(void)
+{
+ static int avail = -1;
+ const EVP_MD *sha256md;
+ const EVP_MD *sha512md;
+
+ if (avail >= 0)
+ return avail;
+
+ sha256md = EVP_get_digestbyname(sha256);
+ sha512md = EVP_get_digestbyname(sha512);
+
+ if (sha256md == 0 || sha512md == 0
+ || RES_USE_DNSSEC == 0 || RES_USE_EDNS0 == 0)
+ return (avail = 0);
+
+ sha256len = EVP_MD_size(sha256md);
+ sha512len = EVP_MD_size(sha512md);
+
+ return (avail = 1);
+}
+
+/* tls_dane_alloc - allocate a TLS_DANE structure */
+
+TLS_DANE *tls_dane_alloc(int flags)
+{
+ TLS_DANE *dane = (TLS_DANE *)mymalloc(sizeof(*dane));
+
+ dane->ta = 0;
+ dane->ee = 0;
+ dane->TLS_DANE_H(X509) = 0;
+ dane->TLS_DANE_H(EVP_PKEY) = 0;
+ dane->flags = flags;
+ return (dane);
+}
+
+static void tlsa_free(TLS_TLSA *tlsa)
+{
+
+ myfree(tlsa->mdalg);
+ if (tlsa->certs)
+ argv_free(tlsa->certs);
+ if (tlsa->pkeys)
+ argv_free(tlsa->pkeys);
+ myfree((char *)tlsa);
+}
+
+/* tls_dane_free - free a TLS_DANE structure */
+
+void tls_dane_free(TLS_DANE *dane)
+{
+ TLS_TLSA *tlsa;
+ TLS_TLSA *next;
+
+ /* De-allocate TA and EE lists */
+ for (tlsa = dane->ta; tlsa; tlsa = next) {
+ next = tlsa->next;
+ tlsa_free(tlsa);
+ }
+ for (tlsa = dane->ee; tlsa; tlsa = next) {
+ next = tlsa->next;
+ tlsa_free(tlsa);
+ }
+ TLS_DANE_LFREE(X509)(dane);
+ TLS_DANE_LFREE(EVP_PKEY)(dane);
+ myfree((char *)dane);
+}
+
+/* dane_free - ctable style */
+
+static void dane_free(void *dane, void *unused_context)
+{
+ tls_dane_free((TLS_DANE *)dane);
+}
+
+/* tlsa_sort - sort digests for a single certusage */
+
+static void tlsa_sort(TLS_TLSA *tlsa)
+{
+ for (; tlsa; tlsa = tlsa->next) {
+ if (tlsa->pkeys)
+ argv_sort(tlsa->pkeys);
+ if (tlsa->certs)
+ argv_sort(tlsa->certs);
+ }
+}
+
+/* tls_dane_final - finish by sorting into canonical order */
+
+TLS_DANE *tls_dane_final(TLS_DANE *dane)
+{
+ /*
+ * We only sort the trust anchors, see tls_serverid_digest().
+ */
+ if (dane->ta)
+ tlsa_sort(dane->ta);
+ dane->flags |= TLS_DANE_FLAG_FINAL;
+ return (dane);
+}
+
+/* dane_locate - list head address of TLSA sublist for given algorithm */
+
+static TLS_TLSA **dane_locate(TLS_TLSA **tlsap, const char *mdalg)
+{
+ TLS_TLSA *new;
+
+ /*
+ * Correct computation of the session cache serverid requires a TLSA
+ * digest list that is sorted by algorithm name. Below we maintain
+ * the sort order (by algorithm name canonicalized to lowercase).
+ */
+ for (; *tlsap; tlsap = &(*tlsap)->next) {
+ int cmp = strcasecmp(mdalg, (*tlsap)->mdalg);
+
+ if (cmp == 0)
+ return (tlsap);
+ if (cmp < 0)
+ break;
+ }
+
+ new = (TLS_TLSA *)mymalloc(sizeof(*new));
+ new->mdalg = lowercase(mystrdup(mdalg));
+ new->certs = 0;
+ new->pkeys = 0;
+ new->next = *tlsap;
+ *tlsap = new;
+
+ return (tlsap);
+}
+
+/* tls_dane_split - split and append digests */
+
+void tls_dane_split(TLS_DANE *dane, int certusage, int selector,
+ const char *mdalg, const char *digest, const char *delim)
+{
+ TLS_TLSA **tlsap;
+ TLS_TLSA *tlsa;
+ ARGV **argvp;
+
+ if (dane->flags & TLS_DANE_FLAG_FINAL)
+ msg_panic("updating frozen TLS_DANE object");
+
+ tlsap = (certusage == TLS_DANE_EE) ? &dane->ee : &dane->ta;
+ tlsa = *(tlsap = dane_locate(tlsap, mdalg));
+ argvp = ((dane->flags & TLS_DANE_FLAG_MIXED) || selector == TLS_DANE_PKEY) ?
+ &tlsa->pkeys : &tlsa->certs;
+
+ /* Delimited append, may append nothing */
+ if (*argvp == 0)
+ *argvp = argv_split(digest, delim);
+ else
+ argv_split_append(*argvp, digest, delim);
+
+ if ((*argvp)->argc == 0) {
+ argv_free(*argvp);
+ *argvp = 0;
+
+ /* Remove empty elements from the list */
+ if (tlsa->pkeys == 0 && tlsa->certs == 0) {
+ *tlsap = tlsa->next;
+ tlsa_free(tlsa);
+ }
+ }
+}
+
+/* dane_add - add a digest entry */
+
+static void dane_add(TLS_DANE *dane, int certusage, int selector,
+ const char *mdalg, char *digest)
+{
+ TLS_TLSA **tlsap;
+ TLS_TLSA *tlsa;
+ ARGV **argvp;
+
+ if (dane->flags & TLS_DANE_FLAG_FINAL)
+ msg_panic("updating frozen TLS_DANE object");
+
+ switch (certusage) {
+ case DNS_TLSA_USAGE_CA_CONSTRAINT:
+ case DNS_TLSA_USAGE_TRUST_ANCHOR_ASSERTION:
+ certusage = TLS_DANE_TA; /* Collapse 0/2 -> 2 */
+ break;
+ case DNS_TLSA_USAGE_SERVICE_CERTIFICATE_CONSTRAINT:
+ case DNS_TLSA_USAGE_DOMAIN_ISSUED_CERTIFICATE:
+ certusage = TLS_DANE_EE; /* Collapse 1/3 -> 3 */
+ break;
+ }
+
+ switch (selector) {
+ case DNS_TLSA_SELECTOR_FULL_CERTIFICATE:
+ selector = TLS_DANE_CERT;
+ break;
+ case DNS_TLSA_SELECTOR_SUBJECTPUBLICKEYINFO:
+ selector = TLS_DANE_PKEY;
+ break;
+ }
+
+ tlsap = (certusage == TLS_DANE_EE) ? &dane->ee : &dane->ta;
+ tlsa = *(tlsap = dane_locate(tlsap, mdalg));
+ argvp = ((dane->flags & TLS_DANE_FLAG_MIXED) || selector == TLS_DANE_PKEY) ?
+ &tlsa->pkeys : &tlsa->certs;
+
+ if (*argvp == 0)
+ *argvp = argv_alloc(1);
+ argv_add(*argvp, digest, ARGV_END);
+}
+
+/* tls_dane_load_trustfile - load trust anchor certs or keys from file */
+
+int tls_dane_load_trustfile(TLS_DANE *dane, const char *tafile)
+{
+ FILE *fp;
+ char *name = 0;
+ char *header = 0;
+ long error = 0;
+ long len;
+ int ret = 0;
+ unsigned char *data = 0;
+
+ /* nop */
+ if (tafile == 0 || *tafile == 0)
+ return (1);
+
+ if ((fp = fopen(tafile, "r")) == NULL) {
+ msg_warn("error opening trust anchor file: %s: %m", tafile);
+ return (0);
+ }
+
+ /* Don't report old news */
+ ERR_clear_error();
+
+ while (PEM_read(fp, &name, &header, &data, &len)) {
+ const unsigned char *p = data;
+ int selector = 0;
+
+ if (strcmp(name, PEM_STRING_X509) == 0
+ || strcmp(name, PEM_STRING_X509_OLD) == 0) {
+ X509 *cert = d2i_X509(0, &p, len);
+
+ if (!(ret = (cert && (p - data) == len))) {
+ msg_warn("error reading: %s: malformed trust-anchor %s",
+ "certificate", tafile);
+ tls_print_errors();
+ break;
+ }
+ TLS_DANE_LINSERT(X509)(dane, cert);
+ X509_free(cert);
+ selector = DNS_TLSA_SELECTOR_FULL_CERTIFICATE;
+ } else if (strcmp(name, PEM_STRING_PUBLIC) == 0) {
+ EVP_PKEY *pkey = d2i_PUBKEY(0, &p, len);
+
+ if (!(ret = (pkey && (p - data) == len))) {
+ msg_warn("error reading: %s: malformed trust-anchor %s",
+ "public key", tafile);
+ tls_print_errors();
+ break;
+ }
+ TLS_DANE_LINSERT(EVP_PKEY)(dane, pkey);
+ EVP_PKEY_free(pkey);
+ selector = DNS_TLSA_SELECTOR_SUBJECTPUBLICKEYINFO;
+ }
+
+ if (ret) {
+ int usage = DNS_TLSA_USAGE_TRUST_ANCHOR_ASSERTION;
+ char *digest = tls_fprint((char *)data, len, sha256);
+
+ dane_add(dane, usage, selector, sha256, digest);
+ myfree(digest);
+ }
+ if (name)
+ OPENSSL_free(name);
+ if (header)
+ OPENSSL_free(header);
+ if (data)
+ OPENSSL_free(data);
+ name = header = (char *) (data = 0);
+ }
+
+ if (fclose(fp) == EOF) {
+ msg_warn("error reading trust anchor file: %s: %m", tafile);
+ return (0);
+ }
+
+ switch(ERR_GET_REASON(ERR_peek_last_error())) {
+ case PEM_R_NO_START_LINE:
+ ERR_clear_error();
+ break;
+ default: /* Something to report */
+ tls_print_errors();
+ case 0: /* All errors reported */
+ ret = 0;
+ break;
+ }
+ return (ret);
+}
+
+#endif
/* .IP len
/* The length of the input data.
/* .IP props
-/* The client start properties for the session, which include the
-/* initial serverid from the SMTP client.
+/* The client start properties for the session, which contains the
+/* initial serverid from the SMTP client and the DANE verification
+/* parameters.
/* .IP protomask
/* The mask of protocol exclusions.
/* .IP ciphers
#define digestptr(p) digestpl((p), sizeof(*(p)))
#define digeststr(s) digestpl((s), strlen(s)+1)
+#define digestdane(dane, memb) do { \
+ if ((dane)->memb != 0) \
+ chknonzero(tlsa_digest(mdctx, (dane)->memb, #memb)); \
+ } while (0)
+
+#define digesttlsa(tlsa, memb) do { \
+ if ((tlsa)->memb) { \
+ digeststr(#memb); \
+ for (dgst = (tlsa)->memb->argv; *dgst; ++dgst) \
+ digeststr(*dgst); \
+ } \
+ } while (0)
+
+/* tlsa_digest - digest a pre-sorted by caller TLSA match list */
+
+static int tlsa_digest(EVP_MD_CTX *mdctx, TLS_TLSA *tlsa, const char *usage)
+{
+ char **dgst;
+ int ok = 1;
+
+ for (digeststr(usage); tlsa; tlsa = tlsa->next) {
+ digeststr(tlsa->mdalg);
+ digesttlsa(tlsa, pkeys);
+ digesttlsa(tlsa, certs);
+ }
+ return (ok);
+}
+
/* tls_serverid_digest - suffix props->serverid with parameter digest */
char *tls_serverid_digest(const TLS_CLIENT_START_PROPS *props, long protomask,
digestptr(&sslversion);
digestptr(&protomask);
digeststr(ciphers);
+
+ /*
+ * All we get from the session cache is a single bit telling us whether
+ * the certificate is trusted or not, but we need to know whether the
+ * trust is CA-based (in that case we must do name checks) or whether it
+ * is a direct end-point match. We mustn't confuse the two, so it is best
+ * to process only TA trust in the verify callback and check the EE trust
+ * after. This works since re-used sessions always have access to the leaf
+ * certificate, while only the original session has the leaf and the full
+ * trust chain.
+ *
+ * Only the trust anchor matchlist is hashed into the session key.
+ * The end entity certs are not used to determine whether a certificate
+ * is trusted or not, rather these are rechecked against the leaf cert
+ * outside the verification callback, each time a session is created or
+ * reused.
+ *
+ * Therefore, the security context of the session does not depend on the
+ * EE matching data, which is checked separately each time. So we exclude
+ * the EE part of the DANE structure from the serverid digest.
+ *
+ * If this changes, also update tls_dane_final() in tls_dane.c.
+ */
+ if (props->dane) {
+ int mixed = (props->dane->flags & TLS_DANE_FLAG_MIXED);
+ digestptr(&mixed);
+ digestdane(props->dane, ta);
+#if 0
+ digestdane(props->dane, ee); /* See above */
+#endif
+ }
chknonzero(EVP_DigestFinal_ex(mdctx, md_buf, &md_len));
EVP_MD_CTX_destroy(mdctx);
if (!ok)
TLScontext->cipher_name = 0;
TLScontext->log_mask = log_mask;
TLScontext->namaddr = lowercase(mystrdup(namaddr));
- TLScontext->mdalg = 0; /* Alias for props->mdalg */
+ TLScontext->mdalg = 0; /* Alias for props->mdalg */
+ TLScontext->dane = 0; /* Alias for client props->dane */
+ TLScontext->trustdepth = -1;
+ TLScontext->chaindepth = -1;
TLScontext->errordepth = -1;
TLScontext->errorcode = X509_V_OK;
TLScontext->errorcert = 0;
/* #define TLS_INTERNAL
/* #include <tls.h>
/*
+/* int tls_cert_match(TLSContext, usage, cert, depth)
+/* TLS_SESS_STATE *TLScontext;
+/* int usage;
+/* X509 *cert;
+/* int depth;
+/*
/* int tls_verify_certificate_callback(ok, ctx)
/* int ok;
/* X509_STORE_CTX *ctx;
/* const GENERAL_NAME *gn;
/* TLS_SESS_STATE *TLScontext;
/* DESCRIPTION
+/* tls_cert_match() matches the full and/or public key digest of
+/* "cert" against each candidate digest in TLScontext->dane. If usage
+/* is TLS_DANE_EE, the match is against end-entity digests, otherwise
+/* it is against trust-anchor digests. Returns true if a match is found,
+/* false otherwise.
+/*
/* tls_verify_certificate_callback() is called several times (directly
/* or indirectly) from crypto/x509/x509_vfy.c. It collects errors
/* and trust information at each element of the trust chain.
/* .IP gn
/* An OpenSSL GENERAL_NAME structure holding a DNS subjectAltName
/* to be decoded and checked for validity.
+/* .IP usage
+/* Trust anchor (TLS_DANE_TA) or end-entity (TLS_DANE_EE) digests?
+/* .IP cert
+/* Certificate from peer trust chain (CA or leaf server).
+/* .IP depth
+/* The certificate depth for logging.
/* .IP peercert
/* Server or client X.509 certificate.
/* .IP TLScontext
X509 *errorcert, int errorcode)
{
/* No news is good news */
- if (TLScontext->errordepth >= 0 && TLScontext->errordepth <= depth)
+ if ((TLScontext->trustdepth >= 0 && TLScontext->trustdepth < depth) ||
+ (TLScontext->errordepth >= 0 && TLScontext->errordepth <= depth))
return;
/*
CRYPTO_add(&errorcert->references, 1, CRYPTO_LOCK_X509);
TLScontext->errorcert = errorcert;
TLScontext->errorcode = errorcode;
+
+ /*
+ * Maintain an invariant, at most one of errordepth and trustdepth
+ * is non-negative at any given time.
+ */
+ TLScontext->errordepth = depth;
+ TLScontext->trustdepth = -1;
+}
+
+/* update_trust_state - safely stash away trust state */
+
+static void update_trust_state(TLS_SESS_STATE *TLScontext, int depth)
+{
+ /* No news is bad news */
+ if ((TLScontext->trustdepth >= 0 && TLScontext->trustdepth <= depth)
+ || (TLScontext->errordepth >= 0 && TLScontext->errordepth <= depth))
+ return;
+
+ /*
+ * Maintain an invariant, at most one of errordepth and trustdepth
+ * is non-negative at any given time.
+ */
+ TLScontext->trustdepth = depth;
+ TLScontext->errordepth = -1;
+}
+
+/* tls_cert_match - match cert against given list of TA or EE digests */
+
+int tls_cert_match(TLS_SESS_STATE *TLScontext, int usage, X509 *cert, int depth)
+{
+ const TLS_DANE *dane = TLScontext->dane;
+ TLS_TLSA *tlsa = (usage == TLS_DANE_EE) ? dane->ee : dane->ta;
+ const char *namaddr = TLScontext->namaddr;
+ const char *ustr = (usage == TLS_DANE_EE) ? "end entity" : "trust anchor";
+ int mixed = (dane->flags & TLS_DANE_FLAG_MIXED);
+ int matched;
+
+ for (matched = 0; tlsa && !matched; tlsa = tlsa->next) {
+ char **dgst;
+ ARGV *certs;
+
+ if (tlsa->pkeys) {
+ char *pkey_dgst = tls_pkey_fprint(cert, tlsa->mdalg);
+ for (dgst = tlsa->pkeys->argv; !matched && *dgst; ++dgst)
+ if (strcasecmp(pkey_dgst, *dgst) == 0)
+ matched = 1;
+ if (TLScontext->log_mask & (TLS_LOG_VERBOSE|TLS_LOG_CERTMATCH))
+ msg_info("%s: depth=%d matched=%d %s public-key %s digest=%s",
+ namaddr, depth, matched, ustr, tlsa->mdalg, pkey_dgst);
+ myfree(pkey_dgst);
+ }
+
+ certs = mixed ? tlsa->pkeys : tlsa->certs;
+ if (certs != 0 && !matched) {
+ char *cert_dgst = tls_fingerprint(cert, tlsa->mdalg);
+ for (dgst = certs->argv; !matched && *dgst; ++dgst)
+ if (strcasecmp(cert_dgst, *dgst) == 0)
+ matched = 1;
+ if (TLScontext->log_mask & (TLS_LOG_VERBOSE|TLS_LOG_CERTMATCH))
+ msg_info("%s: depth=%d matched=%d %s certificate %s digest %s",
+ namaddr, depth, matched, ustr, tlsa->mdalg, cert_dgst);
+ myfree(cert_dgst);
+ }
+ }
+
+ return (matched);
+}
+
+/* ta_match - match cert against out-of-band TA keys or digests */
+
+static int ta_match(TLS_SESS_STATE *TLScontext, X509_STORE_CTX *ctx,
+ X509 *cert, int depth, int expired)
+{
+ const TLS_DANE *dane = TLScontext->dane;
+ int matched = tls_cert_match(TLScontext, TLS_DANE_TA, cert, depth);
+
+ /*
+ * If we are the TA, the first trusted certificate is one level below!
+ * As a degenerate case a self-signed TA at depth 0 is also treated as
+ * a TA validated trust chain, (even if the certificate is expired).
+ *
+ * Note: OpenSSL will flag an error when the chain contains just one
+ * certificate that is not self-issued.
+ */
+ if (matched) {
+ if (--depth < 0)
+ depth = 0;
+ update_trust_state(TLScontext, depth);
+ return (1);
+ }
+
+ /*
+ * If expired, no need to check for a trust-anchor signature. The TA
+ * itself is matched by its digest, so we're at best looking at some
+ * other expired certificate issued by the TA, which we don't accept.
+ */
+ if (expired)
+ return (0);
+
+ /*
+ * Compute the index of the topmost chain certificate; it may need to be
+ * verified via one of our out-of-band trust-anchors. Since we're here,
+ * the chain contains at least one certificate.
+ *
+ * Optimization: if the top is self-issued, we don't need to try to check
+ * whether it is signed by any ancestor TAs. If it is trusted, it will be
+ * matched by its fingerprint.
+ */
+ if (TLScontext->trustdepth < 0 && TLScontext->chaindepth < 0) {
+ STACK_OF(X509) *chain = X509_STORE_CTX_get_chain(ctx);
+ int i = sk_X509_num(chain) - 1;
+ X509 *top = sk_X509_value(chain, i);
+
+ if (X509_check_issued(top, top) == X509_V_OK)
+ TLScontext->chaindepth = i + 1;
+ else
+ TLScontext->chaindepth = i;
+ }
+
+ /*
+ * Last resort, check whether signed by out-of-band TA public key.
+ *
+ * Only the top certificate of the server chain needs this logic, since
+ * any certs below are signed by their parent, which we checked against
+ * the TA list more cheaply. Do this at most once (by incrementing the
+ * depth when we're done).
+ */
+ if (depth == TLScontext->chaindepth) {
+ TLS_DANE_L(EVP_PKEY) *k;
+ TLS_DANE_L(X509) *x;
+
+ /*
+ * First check whether issued and signed by a TA cert, this is cheaper
+ * than the bare-public key checks below, since we can determine
+ * whether the candidate TA certificate issued the certificate
+ * to be checked first (name comparisons), before we bother with
+ * signature checks (public key operations).
+ */
+ for (x = TLS_DANE_HEAD(dane, X509); !matched && x; x = x->next) {
+ if (X509_check_issued(x->item, cert) == X509_V_OK) {
+ EVP_PKEY *pk = X509_get_pubkey(x->item);
+ matched = pk && X509_verify(cert, pk) > 0;
+ EVP_PKEY_free(pk);
+ }
+ }
+
+ /*
+ * With bare TA public keys, we can't check whether the trust chain
+ * is issued by the key, but we can determine whether it is signed
+ * by the key, so we go with that. Ideally, the corresponding
+ * certificate was presented in the chain, and we matched it by
+ * its public key digest one level up. This code is here to handle
+ * adverse conditions imposed by sloppy administrators of receiving
+ * systems with poorly constructed chains.
+ */
+ for (k = TLS_DANE_HEAD(dane, EVP_PKEY); !matched && k; k = k->next)
+ matched = X509_verify(cert, k->item) > 0;
+
+ if (matched)
+ update_trust_state(TLScontext, depth);
+ ++TLScontext->chaindepth;
+ }
+ return (matched);
}
/* tls_verify_certificate_callback - verify peer certificate info */
if (max_depth >= 0 && depth > max_depth) {
update_error_state(TLScontext, depth, cert,
X509_V_ERR_CERT_CHAIN_TOO_LONG);
- ok = 0;
+ return (1);
+ }
+
+ /*
+ * Per RFC 5280 and its upstream ITU documents, a trust anchor is just
+ * a public key, no more no less, and thus certificates bearing the
+ * trust-anchor public key are just public keys in X.509v3 garb. Any
+ * meaning attached to their expiration, ... is simply local policy.
+ *
+ * We don't punish server administrators for including an expired optional
+ * TA certificate in their chain. Had they left it out, and provided us
+ * instead with only the TA public-key via a "2 1 0" TLSA record, there'd
+ * be no TA certificate from which to learn the expiration dates.
+ *
+ * Therefore, in the interests of consistent behavior, we only enforce
+ * expiration dates BELOW the TA signature. When we find an expired
+ * certificate, we only check whether it is a TA, and not whether it is
+ * signed by a TA.
+ *
+ * Other than allowing TA certificate expiration, the only errors we
+ * allow are failure to chain to a trusted root. Our TA set includes
+ * out-of-band data not available to the X509_STORE_CTX.
+ *
+ * More than one of the allowed errors may be reported at a given depth,
+ * trap all instances, but run the matching code at most once. If the
+ * current cert is ok, we have a trusted ancestor, and we're not verbose,
+ * don't bother with matching.
+ */
+ if (cert != 0
+ && (ok == 0
+ || TLScontext->trustdepth < 0
+ || (TLScontext->log_mask & (TLS_LOG_VERBOSE | TLS_LOG_CERTMATCH)))
+ && TLS_DANE_HASTA(TLScontext->dane)
+ && (TLScontext->trustdepth == -1 || depth <= TLScontext->trustdepth)
+ && (TLScontext->errordepth == -1 || depth < TLScontext->errordepth)) {
+ int expired = 0; /* or not yet valid */
+
+ switch (ok ? X509_V_OK : err) {
+ case X509_V_ERR_CERT_NOT_YET_VALID:
+ case X509_V_ERR_CERT_HAS_EXPIRED:
+ expired = 1;
+ /* FALLTHROUGH */
+ case X509_V_OK:
+ case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT:
+ case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY:
+ case X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN:
+ case X509_V_ERR_CERT_UNTRUSTED:
+ if ((!expired && depth == TLScontext->trustdepth)
+ || ta_match(TLScontext, ctx, cert, depth, expired))
+ ok = 1;
+ break;
+ }
}
if (ok == 0)
update_error_state(TLScontext, depth, cert, err);
/*
- * The final depth zero call sets the verification status.
+ * Perhaps the chain is verified, or perhaps we'll get called again,
+ * either way the best we know is that if trust depth is below error
+ * depth we win and otherwise we lose. Set the error state accordingly.
+ *
+ * If we are given explicit TA match list, we must match one of them
+ * at a non-negative depth below any errors, otherwise we just need
+ * no errors.
*/
if (depth == 0) {
- ok = TLScontext->errordepth < 0 ? 1 : 0;
+ ok = 0;
+ if (TLScontext->trustdepth < 0 && TLS_DANE_HASTA(TLScontext->dane)) {
+ /* Required Policy or DANE certs not present */
+ if (TLScontext->errordepth < 0) {
+ /*
+ * For lack of a better choice log the trust problem against
+ * the leaf cert when PKI says yes, but local policy or DANE
+ * says no. Logging a root cert as untrusted would far more
+ * likely confuse users!
+ */
+ update_error_state(TLScontext, depth, cert,
+ X509_V_ERR_CERT_UNTRUSTED);
+ }
+ } else if (TLScontext->errordepth < 0) {
+ /* No PKI trust errors, or only above a good policy or DANE CA. */
+ ok = 1;
+ }
X509_STORE_CTX_set_error(ctx, ok ? X509_V_OK : TLScontext->errorcode);
}
+
if (TLScontext->log_mask & TLS_LOG_VERBOSE) {
if (cert)
X509_NAME_oneline(X509_get_subject_name(cert), buf, sizeof(buf));
/* ARGV *argv_alloc(len)
/* ssize_t len;
/*
+/* ARGV *argv_sort(argvp)
+/* ARGV *argvp;
+/*
/* ARGV *argv_free(argvp)
/* ARGV *argvp;
/*
/* length. The result is ready for use by argv_add(). The array
/* is null terminated.
/*
+/* argv_sort() sorts the elements of argvp in place returning
+/* the original array.
+/*
/* argv_add() copies zero or more strings and adds them to the
/* specified string array. The array is null terminated.
/* Terminate the argument list with a null pointer. The manifest
return (argvp);
}
+static int argv_cmp(const void *e1, const void *e2)
+{
+ const char *s1 = *(const char **)e1;
+ const char *s2 = *(const char **)e2;
+ return strcmp(s1, s2);
+}
+
+/* argv_sort - sort array in place */
+
+ARGV *argv_sort(ARGV *argvp)
+{
+ qsort(argvp->argv, argvp->argc, sizeof(argvp->argv[0]), argv_cmp);
+ return (argvp);
+}
+
/* argv_extend - extend array */
static void argv_extend(ARGV *argvp)
} ARGV;
extern ARGV *argv_alloc(ssize_t);
+extern ARGV *argv_sort(ARGV *);
extern void argv_add(ARGV *,...);
extern void argv_addn(ARGV *,...);
extern void argv_terminate(ARGV *);