]> git.ipfire.org Git - thirdparty/postfix.git/commitdiff
postfix-2.11-20130602
authorWietse Venema <wietse@porcupine.org>
Sun, 2 Jun 2013 05:00:00 +0000 (00:00 -0500)
committerViktor Dukhovni <postfix-users@dukhovni.org>
Sun, 2 Jun 2013 23:39:20 +0000 (19:39 -0400)
86 files changed:
postfix/.indent.pro
postfix/AAAREADME
postfix/HISTORY
postfix/Makefile.in
postfix/README_FILES/DATABASE_README
postfix/README_FILES/TLS_README
postfix/RELEASE_NOTES
postfix/WISHLIST
postfix/html/DATABASE_README.html
postfix/html/Makefile.in
postfix/html/TLS_README.html
postfix/html/cidr_table.5.html
postfix/html/lmtp.8.html
postfix/html/postconf.5.html
postfix/html/posttls-finger.1.html [new file with mode: 0644]
postfix/html/smtp.8.html
postfix/html/smtpd.8.html
postfix/html/trivial-rewrite.8.html
postfix/man/Makefile.in
postfix/man/man1/posttls-finger.1 [new file with mode: 0644]
postfix/man/man5/cidr_table.5
postfix/man/man5/postconf.5
postfix/man/man8/smtp.8
postfix/man/man8/smtpd.8
postfix/man/man8/trivial-rewrite.8
postfix/mantools/postlink
postfix/proto/DATABASE_README.html
postfix/proto/TLS_README.html
postfix/proto/cidr_table
postfix/proto/postconf.proto
postfix/proto/stop
postfix/src/dns/dns.h
postfix/src/dns/dns_lookup.c
postfix/src/dns/dns_rr.c
postfix/src/dns/test_dns_lookup.c
postfix/src/global/mail_params.h
postfix/src/global/mail_proto.h
postfix/src/global/mail_version.h
postfix/src/local/forward.c
postfix/src/postscreen/postscreen_early.c
postfix/src/posttls-finger/.indent.pro [new symlink]
postfix/src/posttls-finger/Makefile.in [new file with mode: 0644]
postfix/src/posttls-finger/posttls-finger.c [new file with mode: 0644]
postfix/src/posttls-finger/tlsmgrmem.c [new file with mode: 0644]
postfix/src/posttls-finger/tlsmgrmem.h [new file with mode: 0644]
postfix/src/smtp/Makefile.in
postfix/src/smtp/lmtp_params.c
postfix/src/smtp/smtp.c
postfix/src/smtp/smtp.h
postfix/src/smtp/smtp_addr.c
postfix/src/smtp/smtp_connect.c
postfix/src/smtp/smtp_key.c [new file with mode: 0644]
postfix/src/smtp/smtp_params.c
postfix/src/smtp/smtp_proto.c
postfix/src/smtp/smtp_reuse.c
postfix/src/smtp/smtp_reuse.h
postfix/src/smtp/smtp_session.c
postfix/src/smtp/smtp_state.c
postfix/src/smtp/smtp_tls_policy.c [new file with mode: 0644]
postfix/src/smtp/smtp_tls_sess.c [deleted file]
postfix/src/smtpd/smtpd.c
postfix/src/smtpd/smtpd_check.c
postfix/src/tls/Makefile.in
postfix/src/tls/tls.h
postfix/src/tls/tls_bio_ops.c
postfix/src/tls/tls_client.c
postfix/src/tls/tls_dane.c [new file with mode: 0644]
postfix/src/tls/tls_fprint.c
postfix/src/tls/tls_level.c
postfix/src/tls/tls_misc.c
postfix/src/tls/tls_proxy_clnt.c
postfix/src/tls/tls_proxy_print.c
postfix/src/tls/tls_proxy_scan.c
postfix/src/tls/tls_server.c
postfix/src/tls/tls_verify.c
postfix/src/trivial-rewrite/trivial-rewrite.c
postfix/src/util/Makefile.in
postfix/src/util/argv.c
postfix/src/util/argv.h
postfix/src/util/base64_code.c
postfix/src/util/base64_code.h
postfix/src/util/ctable.c
postfix/src/util/ctable.h
postfix/src/util/dict_ni.c
postfix/src/util/timecmp.c [new file with mode: 0644]
postfix/src/util/timecmp.h [new file with mode: 0644]

index 8979235c9f5bc8ddbacf87d8a64554b3b7f01a52..bd70a8bcbdb78d6703cdc28b00e2fe76a9c80738 100644 (file)
 -TDSN_STAT
 -TEDIT_FILE
 -TEVENT_MASK
+-TEVP_PKEY
 -TEXPAND_ATTR
 -TFILE
 -TFORWARD_INFO
 -TNAME_CODE
 -TNAME_MASK
 -TNBBIO
+-TOPTIONS
 -TPC_DBMS_INFO
 -TPC_EVAL_CTX
 -TPC_MASTER_ENT
 -TSMTPD_XFORWARD_ATTR
 -TSMTP_ADDR
 -TSMTP_CMD
+-TSMTP_ITERATOR
 -TSMTP_RESP
 -TSMTP_SASL_AUTH_CACHE
 -TSMTP_SESSION
 -TSMTP_STATE
+-TSMTP_TLS_POLICY
 -TSMTP_TLS_SESS
 -TSMTP_TLS_SITE_POLICY
 -TSM_STATE
 -TSSL
 -TSSL_CTX
 -TSSL_SESSION
+-TSTATE
 -TSTRING_LIST
 -TSTRING_TABLE
 -TSYS_EXITS_DETAIL
 -TTLSMGR_SCACHE
 -TTLSP_STATE
 -TTLS_APPL_STATE
+-TTLS_CERTS
 -TTLS_CLIENT_INIT_PROPS
 -TTLS_CLIENT_START_PROPS
+-TTLS_DANE
+-TTLS_PKEYS
 -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
index cb11a0ada40af91aa4ff25932faadb59a1ce1567..e20091c87981dce81b375d85942fde22279a6c78 100644 (file)
@@ -164,6 +164,7 @@ Postfix daemons:
 Test programs:
 
     src/fsstone/       Measure file system overhead
+    src/posttls-finger/        Postfix SMTP/LMTP TLS probe utility
     src/smtpstone/     SMTP and QMQP server torture test
 
 Miscellaneous:
index 506134bbf00c9f1afa60cbc7e796bae9a89fc566..f235f6c1516ba1319fad1a6229eda75dff31dda8 100644 (file)
@@ -5825,7 +5825,7 @@ Apologies for any names omitted.
        If your access maps cannot store or look up null string
        key values, specify "smtpd_null_access_lookup_key = <>"
        and the null sender address will be looked up as <> instead.
-       File:  src/smtpd_access.c.
+       File:  smtpd/smtpd_access.c.
 
 20011223
 
@@ -6418,11 +6418,11 @@ Apologies for any names omitted.
 
        Cleanup: the SMTP client logged and bounced the CNAME
        expanded recipient address, and thereby complicated trouble
-       shooting.  File:  src/smtp_proto.c.
+       shooting.  File:  smtp/smtp_proto.c.
 
        Bugfix: the SMTP and LMTP clients bounced the quoted
        recipient address, resulting in too much quoting in bounce
-       reports.  Files:  src/smtp_proto.c, lmtp/lmtp_proto.c.
+       reports.  Files:  smtp/smtp_proto.c, lmtp/lmtp_proto.c.
 
 20020513
 
@@ -18327,25 +18327,24 @@ Apologies for any names omitted.
        tls/tls_verify.c.
 
        Feature: support for DNSSEC-validated lookups and TLSA
-       RRsets.  Viktor Dukhovni. Files: src/dns/Makefile.in,
-       src/dns/dns.h, src/dns/dns_lookup.c, src/dns/dns_rr.c,
-       src/dns/dns_strtype.c, src/dns/test_dns_lookup.c,
+       RRsets.  Viktor Dukhovni. Files: dns/Makefile.in, dns/dns.h,
+       dns/dns_lookup.c, dns/dns_rr.c, dns/dns_strtype.c,
+       dns/test_dns_lookup.c,
 
        Cleanup: the personality switch between "smtp" and "lmtp".
        This streamlines the switch in the SMTP/LMTP protocol, DNS
        MX lookups, and configuration parameter names in error
-       messages.  Viktor Dukhovni. Files: src/smtp/smtp.c,
-       src/smtp/smtp.h, src/smtp/smtp_chat.c, src/smtp/smtp_connect.c,
-       src/smtp/smtp_proto.c, src/smtp/smtp_rcpt.c,
-       src/smtp/smtp_sasl_glue.c, src/smtp/smtp_sasl_proto.c,
-       src/smtp/smtp_session.c, src/smtp/smtp_state.c.
+       messages.  Viktor Dukhovni. Files: smtp/smtp.c, smtp/smtp.h,
+       smtp/smtp_chat.c, smtp/smtp_connect.c, smtp/smtp_proto.c,
+       smtp/smtp_rcpt.c, smtp/smtp_sasl_glue.c, smtp/smtp_sasl_proto.c,
+       smtp/smtp_session.c, smtp/smtp_state.c.
 
        Feature: replace disable_dns_lookups with smtp_dns_support_level,
-       enable secure DNSSEC lookups in the Postfix SMTP client, and use
-       the DNSSEC-validated remote SMTP server name to select the SMTP
-       and TLS policies.  Viktor Dukhovni. Files: src/dns/Makefile.in,
-       src/dns/dns.h, src/dns/dns_lookup.c, src/dns/dns_rr.c,
-       src/dns/dns_strtype.c, src/dns/test_dns_lookup.c.
+       enable secure DNSSEC lookups in the Postfix SMTP client,
+       and use the DNSSEC-validated remote SMTP server name to
+       select the SMTP and TLS policies.  Viktor Dukhovni. Files:
+       dns/Makefile.in, dns/dns.h, dns/dns_lookup.c, dns/dns_rr.c,
+       dns/dns_strtype.c, dns/test_dns_lookup.c.
 
 20130325
 
@@ -18356,7 +18355,7 @@ Apologies for any names omitted.
        util/read_wait.c, util/write_wait.c.
 
        Portability: support for NetBSD 5.x, NetBSD 6.x and DragonFly
-       BSD. Viktor Dukhovni. Files: makedefs, src/util/sys_defs.h.
+       BSD. Viktor Dukhovni. Files: makedefs, util/sys_defs.h.
 
 20130326
 
@@ -18369,34 +18368,32 @@ Apologies for any names omitted.
        util/write_wait.c.
 
        Cleanup: refactor TLS digest functions, improved signature
-       for TLS session cache. Viktor Dukhovni. Files: src/smtp/smtp.c,
-       src/smtp/smtp_proto.c, src/smtpd/smtpd.c, src/tls/Makefile.in,
-       src/tls/tls.h, src/tls/tls_client.c, src/tls/tls_fprint.c,
-       src/tls/tls_level.c, src/tls/tls_misc.c, src/tls/tls_server.c,
-       src/tls/tls_verify.c, src/tlsproxy/tlsproxy.c.
+       for TLS session cache. Viktor Dukhovni. Files: smtp/smtp.c,
+       smtp/smtp_proto.c, smtpd/smtpd.c, tls/Makefile.in, tls/tls.h,
+       tls/tls_client.c, tls/tls_fprint.c, tls/tls_level.c,
+       tls/tls_misc.c, tls/tls_server.c, tls/tls_verify.c,
+       tlsproxy/tlsproxy.c.
 
 20130327
 
        Cleanup: final polish for MacOSX workarounds; replaced
-       #ifdef MacOSX by feature test as required by PORTING
-       document.  Files: util/poll_fd.c, util/open_limit.c.
+       #ifdef MacOSX by feature test as required by PORTING document.
+       Files: util/poll_fd.c, util/open_limit.c.
 
        Export tls_fprint() and tls_digest_encode() for use in DANE.
-       Viktor Dukhovni. Files: src/tls/tls.h, src/tls/tls_fprint.c.
+       Viktor Dukhovni. Files: tls/tls.h, tls/tls_fprint.c.
 
 20130331
 
        Refactoring: TLS verification callback processing in
        preparation for DANE support. Viktor Dukhovni. Files:
-       src/tls/tls.h, src/tls/tls_client.c, src/tls/tls_misc.c,
-       src/tls/tls_verify.c.
+       tls/tls.h, tls/tls_client.c, tls/tls_misc.c, tls/tls_verify.c.
 
        Refactoring: split off SMTP client per-session TLS policy
        data and code in preparation for DANE support.  Viktor
-       Dukhovni.  Files: src/smtp/Makefile.in, src/smtp/smtp.h,
-       src/smtp/smtp_connect.c, src/smtp/smtp_proto.c,
-       src/smtp/smtp_reuse.c, src/smtp/smtp_session.c,
-       src/smtp/smtp_tls_sess.c.
+       Dukhovni.  Files: smtp/Makefile.in, smtp/smtp.h,
+       smtp/smtp_connect.c, smtp/smtp_proto.c, smtp/smtp_reuse.c,
+       smtp/smtp_session.c, smtp/smtp_tls_sess.c.
 
        Cleanup: "zero time limit" corner case in read_wait() and
        write_wait() emulation. Files: util/poll_fd.c, util/iostuff.h.
@@ -18432,6 +18429,19 @@ Apologies for any names omitted.
        src/smtpd/smtpd_check.c, src/trivial-rewrite/transport.c,
        src/trivial-rewrite/trivial-rewrite.c.
 
+       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,
+       global/mail_params.h, smtp/lmtp_params.c, smtp/smtp.c,
+       smtp/smtp.h, smtp/smtp_params.c, smtp/smtp_proto.c,
+       smtp/smtp_session.c, smtp/smtp_state.c, smtp/smtp_tls_sess.c,
+       tls/Makefile.in, tls/tls.h, tls/tls_client.c, tls/tls_dane.c,
+       tls/tls_fprint.c, tls/tls_misc.c, tls/tls_verify.c,
+       util/argv.c, util/argv.h.
+
 20130409
 
        Documentation: pointers to other actions under "ACCEPT
@@ -18447,6 +18457,98 @@ Apologies for any names omitted.
        Documentation: clarified text about result formats. Files:
        proto/canonical, proto/virtual.
 
+20130414
+
+       Cleanup: the SMTP client connection management code now
+       maintains iterator state with a structure that contains
+       next-hop, host name, address, port and other information.
+       This iterator structure replaces random variables that were
+       updated by add-hoc code, and replaces random function
+       argument lists. The more structured approach is easier to
+       maintain and has already paid off by exposing opportunities
+       to improve SMTP connection cache usage.  Wietse Venema.
+       Files: smtp/smtp.h, smtp/smtp_connect.c, smtp/smtp_session.c,
+       smtp_reuse.c.
+
+       Cleanup: eliminated minor false SMTP connection cache-sharing
+       problems due to mis-aligned lookup keys for caches and
+       lookup tables (for example some used the nexthop, and some
+       the domain name).  Information that is used in more than
+       one lookup key is now generated by a centralized function.
+       This replaces ad-hoc code in random places that was
+       concatenating ad-hoc data to construct lookup keys. The
+       more structured approach is easier to maintain and makes
+       future cache-sharing issues easier to prevent.  Wietse
+       Venema. Files: smtp/smtp.h, smtp/smtp_connect.c, smtp_reuse.c,
+       smtp_key.c, smtp_tls_sess.c.
+
+       Cleanup and fix of non-production code: the trust anchor-digest
+       code and smtp_sess_tls_required() function. Victor Duchovni.
+       Files: smtp/smtp_connect.c, smtp/smtp_proto.c,
+       smtp/smtp_tls_sess.c, tls/tls.h, tls/tls_client.c,
+       tls/tls_dane.c, tls/tls_level.c, tls/tls_verify.c.
+
+20130417
+
+       Cleanup and fix of non-production code: add the SASL
+       credentials or absence thereof to the connection cache
+       endpoint label; better reuse of SASL-authenticated connections
+       over UNIX-domains sockets, however unlikely these may be;
+       a first step towards refinement of connection cache lookup
+       by IP addres for plaintext or SASL-unauthenticated connections.
+       Files: smtp/smtp.h smtp/smtp_connect.c, smtp/smtp_reuse.c,
+       smtp/smtp_key.c, smtp/smtp_tls_sess.s.
+
+20130418
+
+       Cleanup: configurable field delimiter and optional "not
+       available" field place holder for cache and table lookup
+       keys; automatic base64 encoding for key fields that contain
+       these. Files: smtp/smtp_key,c, smtp/smtp_reuse.c,
+       smtp/smtp_proto.c, smtp/smtp_tls_sess.c.
+
+20130420-21
+
+       Documentation: "dane" TLS security level and parameters.
+       Viktor Dukhovni. Files: mantools/postlink, proto/TLS_README.html,
+       proto/postconf.proto.
+
+       Feature: implemented and enabled DNS-based DANE security
+       level.  Viktor Dukhovni. Files: global/mail_params.h,
+       smtp/lmtp_params.c, smtp/smtp.c, smtp/smtp.h, smtp/smtp_params.c,
+       smtp/smtp_proto.c, smtp/smtp_tls_sess.c, tls/tls.h,
+       tls/tls_client.c, tls/tls_dane.c, tls/tls_fprint.c,
+       tls/tls_level.c, tls/tls_misc.c, util/Makefile.in,
+       util/ctable.c, util/ctable.h, util/timecmp.c, util/timecmp.h.
+
+       Cleanup: rename (unchanged) smtp_tls_sess.c to smtp_tls_policy.c.
+       Viktor Dukhovni. Files: smtp/Makefile.in, smtp/smtp_tls_policy.c,
+       smtp/smtp_tls_sess.c.
+
+       Portability: OpenSSL workarounds for versions before 0.9.7
+       are removed from the source code. Viktor Dukhovni. Files:
+       tls/tls.h, tls/tls_bio_ops.c, tls/tls_client.c.
+
+       Non-production fixes: when falling back from opportunistic
+       TLS to plaintext, don't modify the cached TLS policy "retry
+       as plaintext" and "level" members.  Files: smtp/smtp_session.c.
+
+       Non-production fixes: move TLS policy lookup to the main
+       connection iterator loop, so that the policy is known before
+       attempting connection reuse and before SMTP connection
+       creation. Temporarily link session->tls to state->tls.
+       Files: smtp/smtp.h, smtp/smtp_connect.c, smtp/smtp_reuse.c,
+       smtp/smtp_tls_policy.c.
+
+20130422
+
+       Feature: smtptls-finger test program for SMTP over TLS.
+       Viktor Dukhovni. Files: Makefile.in, html/Makefile.in,
+       man/Makefile.in, mantools/postlink, posttls-finger/.indent.pro,
+       posttls-finger/Makefile.in, posttls-finger/posttls-finger.c,
+       posttls-finger/tlsmgrmem.c, posttls-finger/tlsmgrmem.h,
+       tls/tls.h, tls/tls_misc.c.
+
 20130423
 
        Bugfix (introduced: Postfix 2.0): when myhostname is not
@@ -18458,6 +18560,49 @@ Apologies for any names omitted.
        address-class label.  Problem reported by Quanah Gibson-Mount.
        File: trivial-rewrite/resolve.c.
 
+20130425
+
+       Non-production fixes: revert to using proxies (sender,
+       nexthop, hostname) to distinguish between different SASL
+       credentials for connections to the same IP address and port.
+       Files: smtp/smtp.h smtp/smtp_connect.c, smtp/smtp_key.c.
+
+       Non-production cleanup: documentation, identifiers.  Viktor
+       Dukhovni. Files: proto/postconf.proto, src/dns/dns.h,
+       src/dns/dns_lookup.c, src/dns/dns_rr.c, src/dns/test_dns_lookup.c,
+       src/global/mail_proto.h, src/posttls-finger/posttls-finger.c,
+       src/smtp/smtp.h, src/smtp/smtp_addr.c, src/smtp/smtp_connect.c,
+       src/smtp/smtp_session.c, src/smtp/smtp_tls_policy.c,
+       src/smtpd/smtpd_check.c, 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_proxy_clnt.c, src/tls/tls_proxy_print.c,
+       src/tls/tls_proxy_scan.c, src/tls/tls_server.c,
+       src/tls/tls_verify.c.
+
+20130426
+
+       Non-production fixes: refinement of SASL-dependent context
+       for connection-cache reuse, documentation. Viktor Dukhovni
+       and Wietse Venema. Files: smtp/smtp.h, smtp/smtp_key.c,
+       tls/tls_client.c.
+
+20130506
+
+       Non-production bugfix: macros must use distinct names for
+       temporary variables, to avoid name collision problems.
+       Problem report: Ralf Hildebrandt. Problem fix: Viktor
+       Dukhovni.  File: smtp/smtp.h.
+
+       Non-production cleanup: simplified "dane" user interface,
+       replacing one "dane" security level plus multiple fall-back
+       options, with two "dane" security levels, one opportunistic
+       and one mandatory. Viktor Dukhovni.  Files: proto/TLS_README.html,
+       proto/postconf.proto, mantools/postlink, proto/TLS_README.html,
+       proto/postconf.proto, global/mail_params.h,
+       posttls-finger/posttls-finger.c, smtp/lmtp_params.c,
+       smtp/smtp.c, smtp/smtp.h, smtp/smtp_params.c,
+       smtp/smtp_tls_policy.c, tls/tls.h, tls/tls_level.c.
+
 20130512
 
        Feature: allow an SMTP client to skip postscreen(8) tests
@@ -18487,3 +18632,57 @@ Apologies for any names omitted.
        the postscreen_dnsbl_whitelist_treshold feature will now
        use partial scores instead of ignoring them.  File:
        postscreen/postscreen_early.c.
+
+20130518
+
+       Bugfix (introduced: 1997): memory leak after error while
+       forwarding mail through the cleanup server. Viktor found
+       one, Wietse eliminated the rest.  File: local/forward.c.
+
+       Feature: posttls-finger protocol and cipher grade selection
+       options.  Leave protocol debug flags active across reconnects,
+       only suppress redundant logging of the certificate details.
+       Viktor Dukhovni. File: posttls-finger/posttls-finger.c.
+
+       Robustness: send SNI even when trying to reuse a DANE
+       session, because a new session may be negotiated anyway.
+       Viktor Dukhovni. File: tls/tls_client.c.
+
+       Cleanup: eliminate variable that is redundant with respect
+       to more authoritative state.  Viktor Dukhovni. File:
+       posttls-finger/posttls-finger.c.
+
+       Feature: new tls_ssl_options parameter to enable OpenSSL
+       features (as opposed to tls_disable_workarounds which is
+       disables bug workarounds that are on by default). Viktor
+       Dukhovni. Files: proto/TLS_README.html, proto/postconf.proto,
+       src/global/mail_params.h, src/tls/tls.h, src/tls/tls_client.c,
+       src/tls/tls_misc.c.
+
+20130520
+
+       Documentation: removed resolve_null_domain from the list
+       of smtpd(8) parameters. File: smtpd/smtpd.c.
+
+20130523
+
+       Documentation: add cidr: and texthash: to the list of maps
+       that don't have automatic change detection. File:
+       proto/DATABASE_README.html.
+
+       Documentation: define the netmask format of CIDR maps.
+       File: proto/cidr_table.
+
+20130530
+
+       Cleanup: replace alloca() with mymalloc()/myfree() for
+       better error handling. Reported by Bill Parker. File:
+       util/dict_ni.c (does anyone still use this code?).
+
+20130531
+
+       Feature: tls_wildcard_matches_multiple_labels (default:
+       yes) to match multiple DNS labels with "*" in wildcard
+       certificates. Viktor Dukhovni. Files: proto/postconf.proto,
+       mantools/postlink, global/mail_params.h, tls/tls_client.c,
+       tls/tls_misc.c.
index a16c89fdfc6ed7746ad0e535ff16d3bc51ff22cb..7145bc4375dde36aa815d149a4fbbe551db82d89 100644 (file)
@@ -9,7 +9,8 @@ DIRS    = src/util src/global src/dns src/tls src/xsasl src/milter src/master \
        src/postkick src/postlock src/postlog src/postmap src/postqueue \
        src/postsuper src/qmqpd src/spawn src/flush src/verify \
        src/virtual src/proxymap src/anvil src/scache src/discard src/tlsmgr \
-       src/postmulti src/postscreen src/dnsblog src/tlsproxy
+       src/postmulti src/postscreen src/dnsblog src/tlsproxy \
+       src/posttls-finger
 MANDIRS        = proto man html
 LIBEXEC        = libexec/post-install libexec/postfix-files libexec/postfix-script \
        libexec/postfix-wrapper libexec/main.cf libexec/master.cf \
index a9fe9b06aa42147aa0e7988038af1a60b12acbb2..6ba7772fdd63a698dda769dc677aafb64a50b248 100644 (file)
@@ -99,9 +99,9 @@ performance.
     read/write access conflicts and gives the new data to Postfix once that
     data is available.
 
-  * If you change a regexp: or pcre: file then Postfix may or may not pick up
-    the file changes immediately. This is because a Postfix process reads the
-    entire file into memory once and never examines the file again.
+  * If you change a regexp:, pcre:, cidr: or texthash: file then Postfix may
+    not pick up the file changes immediately. This is because a Postfix process
+    reads the entire file into memory once and never examines the file again.
 
       o If the file is used by a short-running process such as smtpd(8),
         cleanup(8) or local(8), there is no need to execute "postfix reload"
index 893b2c95bb1fcef17158a10db975263e61d4b040..6ec9312148d0cd785829c5f03925c33e3f167443 100644 (file)
@@ -86,14 +86,15 @@ a private key. Both must be in "PEM" format. The private key must not be
 encrypted, meaning: the key must be accessible without a password. The
 certificate and private key may be in the same file, in which case the
 certificate file should be owned by "root" and not be readable by any other
-user. If the key is stored separately, this applies to the key file only, and
-the certificate file may be "world-readable".
+user. If the key is stored separately, this access restriction applies to the
+key file only, and the certificate file may be "world-readable".
 
-Public Internet MX hosts without certificates signed by a "reputable" CA must
-generate, and be prepared to present to most clients, a self-signed or private-
-CA signed certificate. The remote SMTP client will generally not be able to
-authenticate the self-signed certificate, but unless the client is running
-Postfix or similar software, it will still insist on a server certificate.
+Public Internet MX hosts without certificates signed by a well-known public CA
+must still generate, and be prepared to present to most clients, a self-signed
+or private-CA signed certificate. The remote SMTP client will generally not be
+able to verify the self-signed certificate, but unless the client is running
+Postfix or similar software, it will only negotiate TLS ciphersuites that
+require a server certificate.
 
 For servers that are n\bno\bot\bt public Internet MX hosts, Postfix supports
 configurations with no certificates. This entails the use of just the anonymous
@@ -106,34 +107,79 @@ administrator explicitly sets "smtpd_tls_cert_file = none". This ensures that
 new Postfix SMTP server configurations will not accidentally run with no
 certificates.
 
-RSA, DSA and ECDSA (Postfix >= 2.6) certificates are supported. Typically you
-will only have RSA certificates issued by a commercial CA. In addition, the
-tools supplied with OpenSSL will by default issue RSA certificates. You can
-configure all three at the same time, in which case the cipher used determines
-which certificate is presented. For Netscape and OpenSSL clients without
-special cipher choices, the RSA certificate is preferred.
-
-To enable a remote SMTP client to verify the Postfix SMTP server certificate,
-the issuing CA certificates must be made available to the client. You should
-include the required certificates in the server certificate file, the server
-certificate first, then the issuing CA(s) (bottom-up order).
-
-Example: the certificate for "server.example.com" was issued by "intermediate
-CA" which itself has a certificate issued by "root CA". Create the server.pem
-file with:
-
-    % c\bca\bat\bt s\bse\ber\brv\bve\ber\br_\b_c\bce\ber\brt\bt.\b.p\bpe\bem\bm i\bin\bnt\bte\ber\brm\bme\bed\bdi\bia\bat\bte\be_\b_C\bCA\bA.\b.p\bpe\bem\bm >\b> s\bse\ber\brv\bve\ber\br.\b.p\bpe\bem\bm
-
-A Postfix SMTP server certificate supplied here must be usable as SSL server
+RSA, DSA and ECDSA (Postfix >= 2.6) certificates are supported. Most sites only
+have RSA certificates. You can configure all three at the same time, in which
+case the ciphersuite negotiated with the remote SMTP client determines which
+certificate is used. If your DNS zone is signed, and you want to publish RFC
+6698 TLSA records, these must match any of the configured certificates. Since
+the best practice is to publish "3 1 1" certificate associations, create a
+separate TLSA record for each public-key certificate digest.
+
+C\bCr\bre\bea\bat\bti\bin\bng\bg t\bth\bhe\be s\bse\ber\brv\bve\ber\br c\bce\ber\brt\bti\bif\bfi\bic\bca\bat\bte\be f\bfi\bil\ble\be
+
+To verify the Postfix SMTP server certificate, the remote SMTP client must
+receive the issuing CA certificates via the TLS handshake or via public-key
+infrastructure. This means that the Postfix server public-key certificate file
+must include the server certificate first, then the issuing CA(s) (bottom-up
+order). The Postfix SMTP server certificate must be usable as SSL server
 certificate and hence pass the "openssl verify -purpose sslserver ..." test.
 
-A client that trusts the root CA has a local copy of the root CA certificate,
-so it is not necessary to include the root CA certificate here. Leaving it out
-of the "server.pem" file reduces the overhead of the TLS exchange.
+The examples that follow show how to create a server certificate file. We
+assume that the certificate for "server.example.com" was issued by
+"intermediate CA" which itself has a certificate issued by "root CA".
+
+  * With legacy public CA trust verification, you can omit the root certificate
+    from the "server.pem" certificate file. If the client trusts the root CA,
+    it will already have a local copy of the root CA certificate. Omitting the
+    root CA certificate reduces the size of the server TLS handshake.
+
+        % c\bca\bat\bt s\bse\ber\brv\bve\ber\br_\b_c\bce\ber\brt\bt.\b.p\bpe\bem\bm i\bin\bnt\bte\ber\brm\bme\bed\bdi\bia\bat\bte\be_\b_C\bCA\bA.\b.p\bpe\bem\bm >\b> s\bse\ber\brv\bve\ber\br.\b.p\bpe\bem\bm
+
+  * If you use RFC 6698 TLSA "2 0 1" or "2 1 1" records to specify root CA
+    certificate digests, you must include the corresponding root CA
+    certificates in the "server.pem" certificate file.
+
+        % c\bca\bat\bt s\bse\ber\brv\bve\ber\br_\b_c\bce\ber\brt\bt.\b.p\bpe\bem\bm i\bin\bnt\bte\ber\brm\bme\bed\bdi\bia\bat\bte\be_\b_C\bCA\bA.\b.p\bpe\bem\bm r\bro\boo\bot\bt.\b.p\bpe\bem\bm >\b> s\bse\ber\brv\bve\ber\br.\b.p\bpe\bem\bm
+
+    Remote SMTP clients will be able to use the TLSA record you publish (which
+    only contains the certificate digest) only if they have access to the
+    corresponding certificate. Failure to verify certificates per the server's
+    published TLSA records will typically cause the SMTP client to defer mail
+    delivery. The foregoing also applies to "2 0 2" and "2 1 2" TLSA records or
+    any other digest of a CA certificate, but it is expected that SHA256 will
+    be by far the most common digest for TLSA. You are strongly urged to
+    likewise include the root CA certificate in your server certificate file
+    even if you use "0 0 1" or "0 1 1" TLSA records. Some DANE implementations
+    in SMTP clients (Postfix among them) may treat RFC 6698 certificate usages
+    "0" and "2" interchangeably.
+
+    By default, support for TLSA records that specify certificate usage "0" via
+    a digest is disabled in the Postfix SMTP client. As a best practice,
+    publish either "3 0 1" or "3 1 1" TLSA associations that specify the SHA256
+    digest of the server certificate public key with the alias-expanded
+    hostname of each STARTTLS capable SMTP server. These continue to work when
+    a certificate is renewed with the same public/private key pair. See the
+    documentation of the tls_dane_trust_anchor_digest_enable main.cf parameter
+    for details.
+
+For instructions on how to compute the digest of a certificate or its public
+key for use in TLSA records, see the documentation of the
+smtpd_tls_fingerprint_digest main.cf parameter.
+
+When a new key or certificate is generated, an additional TLSA record with the
+new digest must be published in advance of the actual deployment of the new key
+or certificate on the server. You must allow sufficient time for any TLSA
+RRsets with only the old digest to expire from DNS caches. The safest practice
+is to wait until the DNSSEC signature on the previous TLSA RRset expires, and
+only then switch the server to use new keys published in the updated TLSA
+RRset. Once the new certificate trust chain and private key are in effect, the
+DNS should be updated once again to remove the old digest from the TLSA RRset.
 
 If you want the Postfix SMTP server to accept remote SMTP client certificates
-issued by these CAs, append the root certificate to $smtpd_tls_CAfile or
-install it in the $smtpd_tls_CApath directory.
+issued by one or more root CAs, append the root certificate to
+$smtpd_tls_CAfile or install it in the $smtpd_tls_CApath directory.
+
+C\bCo\bon\bnf\bfi\big\bgu\bur\bri\bin\bng\bg t\bth\bhe\be s\bse\ber\brv\bve\ber\br c\bce\ber\brt\bti\bif\bfi\bic\bca\bat\bte\be a\ban\bnd\bd k\bke\bey\by f\bfi\bil\ble\bes\bs
 
 RSA key and certificate examples:
 
@@ -619,11 +665,35 @@ Example:
 
     /etc/postfix/main.cf:
         tls_disable_workarounds = 0xFFFFFFFF
-        tls_disable_workarounds = CVE-2010-4180, LEGACY_SERVER_CONNECT
+        tls_disable_workarounds = CVE-2010-4180
+
+With Postfix >= 2.11, the tls_ssl_options parameter specifies a list or bit-
+mask of OpenSSL options to enable. Specify one or more of the named options
+below, or a hexadecimal bitmask of options found in the ssl.h file
+corresponding to the run-time OpenSSL library. While it may be reasonable to
+turn off all bug workarounds (see above), it is not a good idea to attempt to
+turn on all features.
+
+L\bLE\bEG\bGA\bAC\bCY\bY_\b_S\bSE\bER\bRV\bVE\bER\bR_\b_C\bCO\bON\bNN\bNE\bEC\bCT\bT
+    See SSL_CTX_set_options(3).
+N\bNO\bO_\b_T\bTI\bIC\bCK\bKE\bET\bT
+    See SSL_CTX_set_options(3).
+N\bNO\bO_\b_C\bCO\bOM\bMP\bPR\bRE\bES\bSS\bSI\bIO\bON\bN
+    Disable SSL compression even if supported by the OpenSSL library.
+    Compression is CPU-intensive, and compression before encryption does not
+    always improve security.
+Example:
 
-Note: Disabling LEGACY_SERVER_CONNECT is not wise at this time, lots of servers
-are still unpatched and Postfix is not significantly vulnerable to the
-renegotiation issue in the TLS protocol.
+    /etc/postfix/main.cf:
+        tls_ssl_options = no_ticket, no_compression
+
+You should only enable features via the hexadecimal mask when the need to
+control the feature is critical (to deal with a new vulnerability or a serious
+interoperability problem). Postfix DOES NOT promise backwards compatible
+behavior with respect to the mask bits. A feature enabled via the mask in one
+release may be enabled by other means in a later release, and the mask bit will
+then be ignored. Therefore, use of the hexadecimal mask is only a temporary
+measure until a new Postfix or OpenSSL release provides a better solution.
 
 S\bSM\bMT\bTP\bP C\bCl\bli\bie\ben\bnt\bt s\bsp\bpe\bec\bci\bif\bfi\bic\bc s\bse\bet\btt\bti\bin\bng\bgs\bs
 
@@ -653,6 +723,10 @@ m\bma\bay\by
     Opportunistic TLS.
 e\ben\bnc\bcr\bry\byp\bpt\bt
     Mandatory TLS encryption.
+d\bda\ban\bne\be
+    Opportunistic DANE TLS.
+d\bda\ban\bne\be-\b-o\bon\bnl\bly\by
+    Mandatory DANE TLS.
 f\bfi\bin\bng\bge\ber\brp\bpr\bri\bin\bnt\bt
     Certificate fingerprint verification.
 v\bve\ber\bri\bif\bfy\by
@@ -805,15 +879,195 @@ not specified consistently.
         [example.net]:msa encrypt protocols=TLSv1 ciphers=high
         [example.net]:submission encrypt protocols=TLSv1 ciphers=high
 
+D\bDA\bAN\bNE\bE T\bTL\bLS\bS a\bau\but\bth\bhe\ben\bnt\bti\bic\bca\bat\bti\bio\bon\bn.\b.
+
+The Postfix SMTP client supports two TLS security levels based on RFC6698 DANE
+TLSA records. The opportunistic "dane" level and the mandatory "dane-only"
+level.
+
+The "dane" level is a stronger form of opportunistic TLS that is resistant to
+man in the middle and downgrade attacks when the destination domain uses DNSSEC
+to publish DANE TLSA records for its MX hosts. If a remote SMTP server has
+usable DANE TLSA records, these will be used to authenticate the server. If
+TLSA records are published for a given remote SMTP server (implying TLS
+support), but are not usable due to unsupported parameters, the Postfix SMTP
+client will use mandatory unauthenticated TLS. Otherwise, the Postfix SMTP
+client behavior is the same as with may.
+
+The "dane-only" level is a form of secure-channel TLS based on the DANE PKI. If
+usable TLSA records are present these are used to authenticate the remote SMTP
+server. Otherwise, or when server certificate verification fails, delivery via
+the server in question tempfails.
+
+At both security levels, the TLS policy for the destination is obtained via
+TLSA records validated with DNSSEC. For TLSA policy to be in effect, the
+destination domain's containing DNS zone must be signed and the Postfix SMTP
+client's operating system must be configured to send its DNS queries to a
+recursive DNS nameserver that is able to validate the signed records. Each MX
+host's DNS zone should also be signed, and should publish DANE TLSA (RFC 6698)
+records that specify how that MX host's TLS certificate is to be verified. TLSA
+records do not preempt the normal SMTP MX host selection algorithm, if some MX
+hosts support TLSA and others do not, TLS security will vary from delivery to
+delivery. It is up to the domain owner to configure their MX hosts and their
+DNS sensibly. To configure the Postfix SMTP client for DNSSEC lookups see the
+documentation for the smtp_dns_support_level main.cf parameter. The
+tls_dane_trust_anchor_digest_enable main.cf parameter controls optional support
+for trust-anchor digest TLSA records.
+
+The Postfix SMTP client deviates from RFC 6698 in cases where strict adherence
+is likely to create opportunities for delayed (or eventually bounced) email
+with no substantive security gain. Most notably, it is not expected that
+smtp_tls_CAfile and smtp_tls_CApath can reasonably include every public CA that
+a remote SMTP server's administrator may believe to be well-known. Therefore,
+certificate usages "0" and "2" from RFC 6698 which are intended to "constrain"
+existing PKI trust, are instead treated as "trust assertions" and mapped to "1"
+and "3" respectively. That is, with certificate usage "0" and "2", Postfix will
+not require the remote SMTP server's certificate to be trusted with respect to
+any locally defined public CAs, it is the domain owner's responsibility to
+ensure that the certificate associations in their TLSA records are appropriate
+to authenticate their SMTP servers.
+
+In addition, the Postfix SMTP client does not assume that the remote SMTP
+server will necessarily be configured to present the certificate of any usage
+"0" root CA in its TLS protocol certificate_list. Therefore, support for usage
+"0" certificate and public-key digests is disabled by default, see
+tls_dane_trust_anchor_digest_enable for details. While undigested trust-anchor
+certificates in TLSA records are supported, publishing complete certificates in
+DNS is unwise given the resulting excessively large DNS record sizes.
+Therefore, in its default configuration the Postfix SMTP client essentially
+supports only certificate usages "1", "2" and "3" (with "1" treated as though
+it were "3").
+
+Finally, the interaction of DANE with MX hostnames that are CNAMEs differs from
+the draft specification in the names used to construct the associated TLSA
+queries. When the MX hostname is a CNAME, the draft proposal to use the
+unexpanded MX hostname in TLSA lookups is fragile and unintuitive. For this to
+work, the domain owner has to either duplicate the TLSA records of the target
+(host, service) pair in his own DNS or furnish the target host with an
+additional certificate and private key that would be negotiated via SNI.
+Neither of these are robust or easy to manage. It is far better to expand the
+CNAME recursively to the underlying target hostname (keeping track of DNSSEC
+validation along the way) and to use the expanded name to construct the TLSA
+query and, if appropriate, in server certificate name checks. This is the
+approach taken by the Postfix SMTP client, and if sanity prevails will also be
+the approach taken in the final standard.
+
+When usable TLSA records are obtained for the remote SMTP server the Postfix
+SMTP client is obligated to include the SNI TLS extension in its SSL client
+hello message. This may help the remote SMTP server live up to its promise to
+provide a certificate that matches its TLSA records. Since TLS extensions
+require TLS 1.0 or later, the Postfix SMTP client must disable SSLv2 and SSLv3
+when SNI is required. If you use "dane" or "dane-only", do not disable TLSv1,
+except perhaps via the policy table for destinations which you are sure will
+support TLSv1.1 or TLSv1.2.
+
+For purposes of protocol and cipher selection, the "dane" security level is
+treated like a "mandatory" TLS security level, and weak ciphers and protocols
+are disabled. Since DANE authenticates server certificates the "aNULL" cipher-
+suites are transparently excluded at this level, no need to configure this
+manually. RFC 6698 (DANE) TLS authentication is available with Postfix 2.11 and
+later.
+
+When a DANE TLSA record specifies a trust-anchor (TA) certificate (that is an
+issuing CA), the strategy used to verify the peername of the server certificate
+is unconditionally "nexthop, hostname". Both the nexthop domain and the
+hostname obtained from the DNSSEC-validated MX lookup are safe from forgery and
+the server certificate must contain at least one of these names.
+
+When a DANE TLSA record specifies an end-entity (EE) certificate, (that is the
+actual server certificate), as with the fingerprint security level below, no
+name checks or certificate expiration checks are applied. The server
+certificate (or its public key) either matches the DANE record or not. Server
+administrators should publish such EE records in preference to all other types.
+
+The pre-requisites for DANE support in the Postfix SMTP client are:
+
+  * A compile-time OpenSSL library that supports the TLS SNI extension and the
+    "sha256" and "sha512" message digests.
+  * A compile-time DNS resolver library that supports DNSSEC. Postfix binaries
+    built on an older system will not support DNSSEC even if deployed on a
+    system with an updated resolver library.
+  * The "smtp_dns_support_level" must be set to "dnssec".
+  * The "smtp_host_lookup" parameter must include "dns".
+  * A DNSSEC-validating recursive resolver (see note below).
+
+The above client pre-requisites do not apply to the Postfix SMTP server. It
+will support DANE provided it supports TLSv1 and its TLSA records are published
+in a DNSSEC signed zone. To receive DANE secured mail for multiple domains, use
+the same hostname to add the server to each domain's MX records. There are no
+plans to implement SNI in the Postfix SMTP server.
+
+Note: The Postfix SMTP client's internal stub DNS resolver is DNSSEC-aware, but
+it does not itself validate DNSSEC records, rather it delegates DNSSEC
+validation to the operating system's configured recursive DNS nameserver. The
+Postfix DNS client relies on a secure channel to the resolver's cache for
+DNSSEC integrity, but does not support TSIG to protect the transmission channel
+between itself and the nameserver. Therefore, it is strongly recommended (DANE
+security guarantee void otherwise) that each MTA run a local DNSSEC-validating
+recursive resolver ("unbound" from nlnetlabs.nl is a reasonable choice)
+listening on the loopback interface, and that the system be configured to use
+only this local nameserver. The local nameserver may forward queries to an
+upstream recursive resolver on another host if desired.
+
+Note: When the operating system's recursive nameserver is not local, enabling
+EDNS0 expanded DNS packet sizes and turning on the DNSSEC "DO" bit in the DNS
+request and/or the new DNSSEC-specific records returned in the nameserver's
+replies may cause problems with older or buggy firewall and DNS server
+implementations. Therefore, Postfix does not enable DNSSEC by default. Since MX
+lookups happen before the security level is determined, DANE support is
+disabled for all destinations unless you set "smtp_dns_support_level = dnssec".
+To enable DNSSEC lookups selectively, define a new dedicated transport with a
+"-o smtp_dns_support_level=dnssec" override in master.cf and route selected
+domains to that transport. If DNSSEC proves to be sufficiently reliable for
+these domains, you can enable it for all destinations by changing the global
+smtp_dns_support_level in main.cf.
+
+E\bEx\bxa\bam\bmp\bpl\ble\be: "dane" security for selected destinations, with opportunistic TLS by
+default. This is the recommended configuration for early adopters.
+
+  * The "example.com" destination uses DANE, but if TLSA records are not
+    present or are unusable, mail is deferred.
+
+  * The "example.org" destination uses DANE if possible, but if no TLSA records
+    are found opportunistic TLS is used.
+
+    main.cf:
+        indexed = ${default_database_type}:${config_directory}/
+        #
+        # default: Opportunistic TLS with no DNSSEC lookups.
+        #
+        smtp_tls_security_level = may
+        smtp_dns_support_level = enabled
+        #
+        # Per-destination TLS policy
+        #
+        smtp_tls_policy_maps = ${indexed}tls_policy
+        #
+        # default_transport = smtp, but some destinations are special:
+        #
+        transport_maps = ${indexed}transport
+
+    transport:
+        example.com dane
+        example.org dane
+
+    tls_policy:
+        example.com dane-only
+
+    master.cf:
+        dane       unix  -       -       n       -       -       smtp
+          -o smtp_dns_support_level=dnssec
+          -o smtp_tls_security_level=dane
+
 C\bCe\ber\brt\bti\bif\bfi\bic\bca\bat\bte\be f\bfi\bin\bng\bge\ber\brp\bpr\bri\bin\bnt\bt v\bve\ber\bri\bif\bfi\bic\bca\bat\bti\bio\bon\bn
 
-Certificate fingerprint verification is available with Postfix 2.5 and later.
-At this security level ("smtp_tls_security_level = fingerprint"), no trusted
-certificate authorities are used or required. The certificate trust chain,
-expiration date, ... are not checked. Instead, the
-smtp_tls_fingerprint_cert_match parameter or the "match" attribute in the
-policy table lists the remote SMTP server certificate fingerprint or public key
-fingerprint (Postfix 2.9 and later).
+At the fingerprint security level, no trusted certificate authorities are used
+or required. The certificate trust chain, expiration date, etc., are not
+checked. Instead, the smtp_tls_fingerprint_cert_match parameter or the "match"
+attribute in the policy table lists the remote SMTP server certificate
+fingerprint or public key fingerprint. Certificate fingerprint verification is
+available with Postfix 2.5 and later, public-key fingerprint support is
+available with Postfix 2.9 and later.
 
 If certificate fingerprints are exchanged securely, this is the strongest, and
 least scalable security level. The administrator needs to securely collect the
@@ -892,7 +1146,7 @@ use public-key fingerprints, upgrade to Postfix 2.9.6 or later.
 
 M\bMa\ban\bnd\bda\bat\bto\bor\bry\by s\bse\ber\brv\bve\ber\br c\bce\ber\brt\bti\bif\bfi\bic\bca\bat\bte\be v\bve\ber\bri\bif\bfi\bic\bca\bat\bti\bio\bon\bn
 
-At the "verify" TLS security level, messages are sent only over TLS encrypted
+At the verify TLS security level, messages are sent only over TLS encrypted
 sessions if the remote SMTP server certificate is valid (not expired or
 revoked, and signed by a trusted certificate authority) and where the server
 certificate name matches a known pattern. Mandatory server certificate
@@ -908,6 +1162,17 @@ extension are used to verify the remote SMTP server name. If no DNS names are
 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
@@ -958,6 +1223,17 @@ extension are used to verify the remote SMTP server name. If no DNS names are
 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
@@ -1262,6 +1538,12 @@ these are usually unauthenticated - to obtain the domain's SMTP server hostname
 (s). When, as is current practice, the client verifies the insecurely obtained
 MX hostname, it is subject to a DNS man-in-the-middle attack.
 
+Adoption of DNSSEC and RFC6698 (DANE) may gradually (as domains implement
+DNSSEC and publish TLSA records for their MX hosts) address the DNS man-in-the-
+middle risk and provide scalable key management for SMTP with TLS. Postfix >=
+2.11 supports the new dane and dane-only security levels that take advantage of
+these standards.
+
 If clients instead attempted to verify the recipient domain name, an SMTP
 server for multiple domains would need to list all its email domain names in
 its certificate, and generate a new certificate each time a new domain were
@@ -1341,6 +1623,26 @@ e\ben\bnc\bcr\bry\byp\bpt\bt
     overrides the main.cf smtp_tls_mandatory_ciphers parameter, and the
     optional "exclude" attribute (Postfix >= 2.6) overrides the main.cf
     smtp_tls_mandatory_exclude_ciphers parameter.
+d\bda\ban\bne\be
+    Opportunistic DANE TLS. The TLS policy for the destination is obtained via
+    TLSA records in DNSSEC. If no TLSA records are found, the effective
+    security level used is may. If TLSA records are found, but none are usable,
+    the effective security level is encrypt. When usable TLSA records are
+    obtained for the remote SMTP server, SSLv2 and SSLv3 are automatically
+    disabled (see smtp_tls_mandatory_protocols), and the server certificate
+    must match the TLSA records. The tls_dane_trust_anchor_digest_enable
+    parameter controls optional support for trust-anchor digest TLSA records.
+    RFC 6698 (DANE) TLS authentication and DNSSEC support is available with
+    Postfix 2.11 and later.
+d\bda\ban\bne\be-\b-o\bon\bnl\bly\by
+    Mandatory DANE TLS. The TLS policy for the destination is obtained via TLSA
+    records in DNSSEC. If no TLSA records are found, or none are usable, no
+    connection is made to the server. When usable TLSA records are obtained for
+    the remote SMTP server, SSLv2 and SSLv3 are automatically disabled (see
+    smtp_tls_mandatory_protocols), and the server certificate must match the
+    TLSA records. The tls_dane_trust_anchor_digest_enable parameter controls
+    optional support for trust-anchor digest TLSA records. RFC 6698 (DANE) TLS
+    authentication and DNSSEC support is available with Postfix 2.11 and later.
 f\bfi\bin\bng\bge\ber\brp\bpr\bri\bin\bnt\bt
     Certificate fingerprint verification. Available with Postfix 2.5 and later.
     At this security level, there are no trusted certificate authorities. The
@@ -1360,14 +1662,21 @@ v\bve\ber\bri\bif\bfy\by
     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
@@ -1572,6 +1881,49 @@ Example:
     /etc/postfix/main.cf:
         smtp_starttls_timeout = 300s
 
+With Postfix 2.8 and later, the tls_disable_workarounds parameter specifies a
+list or bit-mask of OpenSSL bug work-arounds to disable. This may be necessary
+if one of the work-arounds enabled by default in OpenSSL proves to pose a
+security risk, or introduces an unexpected interoperability issue. Some bug
+work-arounds known to be problematic are disabled in the default value of the
+parameter when linked with an OpenSSL library that could be vulnerable.
+
+Example:
+
+    /etc/postfix/main.cf:
+        tls_disable_workarounds = 0xFFFFFFFF
+        tls_disable_workarounds = CVE-2010-4180, LEGACY_SERVER_CONNECT
+
+Note: Disabling LEGACY_SERVER_CONNECT is not wise at this time, lots of servers
+are still unpatched and Postfix is not significantly vulnerable to the
+renegotiation issue in the TLS protocol.
+
+With Postfix >= 2.11, the tls_ssl_options parameter specifies a list or bit-
+mask of OpenSSL options to enable. Specify one or more of the named options
+below, or a hexadecimal bitmask of options found in the ssl.h file
+corresponding to the run-time OpenSSL library. While it may be reasonable to
+turn off all bug workarounds (see above), it is not a good idea to attempt to
+turn on all features.
+
+A future version of OpenSSL may by default no longer allow connections to
+servers that don't support secure renegotiation. Since the exposure for SMTP is
+minimal, and some SMTP servers may remain unpatched, you can add
+LEGACY_SERVER_CONNECT to the options to restore the more permissive default of
+current OpenSSL releases.
+
+Example:
+
+    /etc/postfix/main.cf:
+        tls_ssl_options = NO_TICKET, NO_COMPRESSION, LEGACY_SERVER_CONNECT
+
+You should only enable features via the hexadecimal mask when the need to
+control the feature is critical (to deal with a new vulnerability or a serious
+interoperability problem). Postfix DOES NOT promise backwards compatible
+behavior with respect to the mask bits. A feature enabled via the mask in one
+release may be enabled by other means in a later release, and the mask bit will
+then be ignored. Therefore, use of the hexadecimal mask is only a temporary
+measure until a new Postfix or OpenSSL release provides a better solution.
+
 T\bTL\bLS\bS m\bma\ban\bna\bag\bge\ber\br s\bsp\bpe\bec\bci\bif\bfi\bic\bc s\bse\bet\btt\bti\bin\bng\bgs\bs
 
 The security of cryptographic software such as TLS depends critically on the
index 4ca9e12dcae81b9edad847a7be4c4a18bca9acac..4e204ae0d04296ddfc89731d46096e2a7b0f17ff 100644 (file)
@@ -14,6 +14,30 @@ specifies the release date of a stable release or snapshot release.
 If you upgrade from Postfix 2.9 or earlier, read RELEASE_NOTES-2.10
 before proceeding.
 
+Major changes with snapshot 20130602
+====================================
+
+Support for PKI-less TLS server certificate verification, where the
+CA public key is identified via DNSSEC lookup. 
+
+This feature introduces a new TLS security level called "dane"
+(DNS-based Authentication of Named Entities) that uses DNSSEC to
+look up CA information for a server TLS certificate.  The details
+of DANE core protocols are still evolving, as are the details of
+how DANE should be used in the context of SMTP. Postfix implements
+what appears to be a "rational" subset of the DANE profiles.
+
+The problem with PKI is that there are literally hundreds of
+organizations world-wide that can provide a certificate in anyone's
+name. There have been widely-published incidents in recent history
+where a certificate authority gave out an inappropriate certificate
+(e.g., a certificate in the name of Microsoft to someone who did
+not represent Microsoft), where a CA was compromised (e.g., DigiNotar,
+Comodo), or where a CA made operational mistakes (e.g., TURKTRUST).
+Another concern is that a legitimate CA might be coerced to provide
+a certificate that allows its government to play man-in-the-middle
+on TLS traffic and observe the plaintext.
+
 Major changes with snapshot 20130512
 ====================================
 
index b61e913e76cc60f9eed5dc43136552a87359400c..a5d39ed747542d6cd33e2efbbe42156a12cf8dc8 100644 (file)
@@ -2,23 +2,28 @@ Wish list:
 
        Things to do before the stable release:
 
+       Spell-check, double-word check, and HTML validator check.
+
+       Discourage the use of "after 220" tests in POSTSCREEN_README
+       and the documentation of individual parameter settings.
+
        Remove this file from the stable release.
 
        Things to do after the stable release:
 
-       Spellcheck and double-word check.
-
        Begin code revision, after DANE support stabilizes.  This
        should be one pass that changes only names and no code.
 
-       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
-       and constants. Avoid 1-letter names except "for i=1 to
-       some_bound". Instead of bare numbers use named constants
-       in function argument lists.
-
-       Code clarity: replace "valid" with or "dnssec_valid".
+       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.
 
        Code clarity: replace obscure macro/function names: for
        example SMTP_X(XXX) -> VAR_SMTP(XXX), as the purpose is to
@@ -28,29 +33,6 @@ Wish list:
        contents of a buffer with the specified length). Replace r
        with res_opt, ditto for other 1-letter names.
 
-       Code consistency: replace the VSTRING-based digest output
-       loop with a tls_digest_encode() call.
-
-       Code clarity: rename tls_fingerprint() to tls_cert_fprint()
-       (compute certifate fingerprint).  Keep tls_pkey_fprint()
-       (compute public-key fingerprint).  Rename tls_fprint() to
-       tls_data_fprint() (compute fingerprint for arbitrary data).
-
-       Collect SMTP client connection-management state in one
-       iterator object, that provides the same information for
-       SMTP reuse policy, TLS policy, and SASL password lookups.
-
-       Unnecessary complexity: the SMTP_SESSION "tls" field is
-       mandatory (always allocated) therefore the content can be
-       a permanent part of the SMTP_SESSION structure, just like
-       SASL-related information.  This avoids silly indirections
-       all over the code, as well as awkward smtp_tls_sess_alloc()
-       error semantics.
-
-       Provide an iterator object API that provides consistent
-       search key generation for SMTP reuse policy, TLS policy,
-       and SASL password lookups.
-
        We have smtp_host_lookup, smtp_dns_resolver_options, and
        now smtp_dns_support_level.  Of these, smtp_dns_resolver_options
        is orthogonal but the rest has overlap.
index 775db144522b05b9caf1da77bb20a5b65ed76e4f..68606057acc5818bd28ff33f197e7f7bc11a53fc 100644 (file)
@@ -156,7 +156,8 @@ SQL, there is no need to execute "postfix reload".  The LDAP, NIS
 or SQL server takes care of read/write access conflicts and gives
 the new data to Postfix once that data is available.  </p>
 
-<li> <p> If you change a <a href="regexp_table.5.html">regexp</a>: or <a href="pcre_table.5.html">pcre</a>: file then Postfix may or
+<li> <p> If you change a <a href="regexp_table.5.html">regexp</a>:, <a href="pcre_table.5.html">pcre</a>:, <a href="cidr_table.5.html">cidr</a>: or <a href="DATABASE_README.html#types">texthash</a>: file
+then Postfix 
 may not pick up the file changes immediately. This is because a
 Postfix process reads the entire file into memory once and never
 examines the file again.  </p>
index 2778e7bac97ccfb337833029f14b23469a4f26db..39b47d70712b9022c8c6d893fc15ce0a4024dda2 100644 (file)
@@ -13,7 +13,7 @@ COMMANDS= mailq.1.html newaliases.1.html postalias.1.html postcat.1.html \
        postconf.1.html postfix.1.html postkick.1.html postlock.1.html \
        postlog.1.html postdrop.1.html postmap.1.html postmulti.1.html \
        postqueue.1.html postsuper.1.html sendmail.1.html \
-       smtp-source.1.html smtp-sink.1.html \
+       smtp-source.1.html smtp-sink.1.html posttls-finger.1.html \
        qmqp-source.1.html qmqp-sink.1.html \
        qshape.1.html
 CONFIG = access.5.html aliases.5.html canonical.5.html relocated.5.html \
@@ -225,6 +225,10 @@ smtp-sink.1.html: ../src/smtpstone/smtp-sink.c
        PATH=../mantools:$$PATH; \
        srctoman $? | $(AWK) | nroff -man | uniq | $(MAN2HTML) | postlink >$@
 
+posttls-finger.1.html: ../src/posttls-finger/posttls-finger.c
+       PATH=../mantools:$$PATH; \
+       srctoman $? | $(AWK) | nroff -man | uniq | $(MAN2HTML) | postlink >$@
+
 qmqp-source.1.html: ../src/smtpstone/qmqp-source.c
        PATH=../mantools:$$PATH; \
        srctoman $? | $(AWK) | nroff -man | uniq | $(MAN2HTML) | postlink >$@
index aaeb6296f5069c1fd7978c6d7cba556ef4eda698..942e77165f3712d0afa55559446b6ad1b6e2f007 100644 (file)
@@ -163,16 +163,16 @@ private key must not be encrypted, meaning:  the key must be accessible
 without a password.  The certificate and private key may be in the same
 file, in which case the certificate file should be owned by "root" and
 not be readable by any other user. If the key is stored separately,
-this applies to the key file only, and the certificate file may be
-"world-readable". </p>
+this access restriction applies to the key file only, and the
+certificate file may be "world-readable". </p>
 
-<p> Public Internet MX hosts without certificates signed by a "reputable"
-CA must generate, and be prepared to present to most clients, a
-self-signed or private-CA signed certificate. The remote SMTP client
-will generally not be
-able to authenticate the self-signed certificate, but unless the
-client is running Postfix or
-similar software, it will still insist on a server certificate. </p>
+<p> Public Internet MX hosts without certificates signed by a
+well-known public CA must still generate, and be prepared to present
+to most clients, a self-signed or private-CA signed certificate.
+The remote SMTP client will generally not be able to verify the
+self-signed certificate, but unless the client is running Postfix
+or similar software, it will only negotiate TLS ciphersuites that
+require a server certificate.  </p>
 
 <p> For servers that are <b>not</b> public Internet MX hosts, Postfix
 supports configurations with no certificates. This entails the
@@ -188,23 +188,37 @@ SMTP server configurations will not accidentally run with no
 certificates. </p>
 
 <p> RSA, DSA and ECDSA (Postfix &ge; 2.6) certificates are supported.
-Typically you will
-only have RSA certificates issued by a commercial CA. In addition,
-the tools supplied with OpenSSL will by default issue RSA certificates.
-You can configure all three at the same time, in which case the cipher used
-determines which certificate is presented. For Netscape and OpenSSL
-clients without special cipher choices, the RSA certificate is
-preferred. </p>
-
-<p> To enable a remote SMTP client to verify the Postfix SMTP server
-certificate, the issuing CA certificates must be made available to the
-client. You should include the required certificates in the server
-certificate file, the server certificate first, then the issuing
-CA(s) (bottom-up order). </p>
+Most sites only have RSA certificates.  You can configure all three
+at the same time, in which case the ciphersuite negotiated with the
+remote SMTP client determines which certificate is used.  If your
+DNS zone is signed, and you want to publish <a href="http://tools.ietf.org/html/rfc6698">RFC 6698</a> TLSA records,
+these must match any of the configured certificates.  Since the
+best practice is to publish "3 1 1" certificate associations, create
+a separate TLSA record for each public-key certificate digest.  </p>
+
+<h4> Creating the server certificate file </h4>
+
+<p> To verify the Postfix SMTP server certificate, the remote SMTP
+client must receive the issuing CA certificates via the TLS handshake
+or via public-key infrastructure. This means that the Postfix server
+public-key certificate file must include the server certificate
+first, then the issuing CA(s) (bottom-up order). The Postfix SMTP
+server certificate must be usable as SSL server certificate and
+hence pass the "<tt>openssl verify -purpose sslserver ...</tt>" test.
+</p>
+
+<p> The examples that follow show how to create a server certificate
+file. We assume that the certificate for "server.example.com" was
+issued by "intermediate CA" which itself has a certificate issued
+by "root CA". </p>
 
-<p> Example: the certificate for "server.example.com" was issued by
-"intermediate CA" which itself has a certificate issued by "root
-CA".  Create the server.pem file with: </p>
+<ul>
+
+<li> <p> With legacy public CA trust verification, you can omit the
+root certificate from the "server.pem" certificate file.  If the
+client trusts the root CA, it will already have a local copy of the
+root CA certificate.  Omitting the root CA certificate reduces the
+size of the server TLS handshake.  </p>
 
 <blockquote>
 <pre>
@@ -212,18 +226,63 @@ CA".  Create the server.pem file with: </p>
 </pre>
 </blockquote>
 
-<p> A Postfix SMTP server certificate supplied here must be usable
-as SSL server certificate and hence pass the "openssl verify -purpose
-sslserver ..." test. </p>
+<li> <p> If you use <a href="http://tools.ietf.org/html/rfc6698">RFC 6698</a> TLSA "2 0 1" or "2 1 1" records to
+specify root CA certificate digests, you must include the corresponding
+root CA certificates in the "server.pem" certificate file. </p>
 
-<p> A client that trusts the root CA has a local copy of the root
-CA certificate, so it is not necessary to include the root CA
-certificate here.  Leaving it out of the "server.pem" file reduces
-the overhead of the TLS exchange. </p>
+<blockquote>
+<pre>
+% <b>cat server_cert.pem intermediate_CA.pem root.pem &gt; server.pem</b>
+</pre>
+</blockquote>
+
+<p> Remote SMTP clients will
+be able to use the TLSA record you publish (which only contains the
+certificate digest) only if they have access to the corresponding
+certificate.  Failure to verify certificates per the server's
+published TLSA records will typically cause the SMTP client to defer
+mail delivery.  The foregoing also applies to "2 0 2" and "2 1 2"
+TLSA records or any other digest of a CA certificate, but it is
+expected that SHA256 will be by far the most common digest for TLSA.
+You are <i>strongly</i> urged to likewise include the root CA
+certificate in your server certificate file even if you use "0 0
+1" or "0 1 1" TLSA records.  Some DANE implementations in SMTP
+clients (Postfix among them) may treat <a href="http://tools.ietf.org/html/rfc6698">RFC 6698</a> certificate usages
+"0" and "2" interchangeably.  </p>
+
+<p>By default, support for TLSA records that specify certificate
+usage "0" via a digest is disabled in the Postfix SMTP client.  As
+a best practice, publish either "3 0 1" or "3 1 1" TLSA associations
+that specify the SHA256 digest of the server certificate public key
+with the alias-expanded hostname of each STARTTLS capable SMTP
+server.  These continue to work when a certificate is renewed with
+the same public/private key pair.  See the documentation of the
+<a href="postconf.5.html#tls_dane_trust_anchor_digest_enable">tls_dane_trust_anchor_digest_enable</a> <a href="postconf.5.html">main.cf</a> parameter for details.
+</p>
+
+</ul>
+
+<p> For instructions on how to compute the digest of a certificate
+or its public key for use in TLSA records, see the documentation of
+the <a href="postconf.5.html#smtpd_tls_fingerprint_digest">smtpd_tls_fingerprint_digest</a> <a href="postconf.5.html">main.cf</a> parameter. </p>
+
+<p> When a new key or certificate is generated, an additional TLSA
+record with the new digest must be published in advance of the
+actual deployment of the new key or certificate on the server.  You
+must allow sufficient time for any TLSA RRsets with only the old
+digest to expire from DNS caches.  The safest practice is to wait
+until the DNSSEC signature on the previous TLSA RRset expires, and
+only then switch the server to use new keys published in the updated
+TLSA RRset.  Once the new certificate trust chain and private key
+are in effect, the DNS should be updated once again to remove the
+old digest from the TLSA RRset.  </p>
 
 <p> If you want the Postfix SMTP server to accept remote SMTP client
-certificates issued by these CAs, append the root certificate to
-$<a href="postconf.5.html#smtpd_tls_CAfile">smtpd_tls_CAfile</a> or install it in the $<a href="postconf.5.html#smtpd_tls_CApath">smtpd_tls_CApath</a> directory. </p>
+certificates issued by one or more root CAs, append the root
+certificate to $<a href="postconf.5.html#smtpd_tls_CAfile">smtpd_tls_CAfile</a> or install it in the $<a href="postconf.5.html#smtpd_tls_CApath">smtpd_tls_CApath</a>
+directory. </p>
+
+<h4> Configuring the server certificate and key files </h4>
 
 <p> RSA key and certificate examples: </p>
 
@@ -872,15 +931,48 @@ an OpenSSL library that could be vulnerable. </p>
 <pre>
 /etc/postfix/<a href="postconf.5.html">main.cf</a>:
     <a href="postconf.5.html#tls_disable_workarounds">tls_disable_workarounds</a> = 0xFFFFFFFF
-    <a href="postconf.5.html#tls_disable_workarounds">tls_disable_workarounds</a> = CVE-2010-4180, LEGACY_SERVER_CONNECT
+    <a href="postconf.5.html#tls_disable_workarounds">tls_disable_workarounds</a> = CVE-2010-4180
 </pre>
 </blockquote>
 
-<p> Note: Disabling LEGACY_SERVER_CONNECT is not wise at this
-time, lots of servers are still unpatched and Postfix is <a
-href="http://www.postfix.org/wip.html#tls-renegotiation">not
-significantly vulnerable</a> to the renegotiation issue in the TLS
-protocol. </p>
+<p> With Postfix &ge; 2.11, the tls_ssl_options parameter specifies
+a list or bit-mask of OpenSSL options to enable.  Specify one or
+more of the named options below, or a hexadecimal bitmask of options
+found in the ssl.h file corresponding to the run-time OpenSSL
+library.  While it may be reasonable to turn off all bug workarounds
+(see above), it is not a good idea to attempt to turn on all features.
+</p>
+
+<dl>
+
+<dt><b>LEGACY_SERVER_CONNECT</b></dt> <dd>See SSL_CTX_set_options(3).</dd>
+
+<dt><b>NO_TICKET</b></dt> <dd>See SSL_CTX_set_options(3).</dd>
+
+<dt><b>NO_COMPRESSION</b></dt> <dd>Disable SSL compression even if
+supported by the OpenSSL library.  Compression is CPU-intensive,
+and compression before encryption does not always improve security.  </dd>
+
+</dl>
+
+<p> Example: </p>
+<blockquote>
+<pre>
+/etc/postfix/<a href="postconf.5.html">main.cf</a>:
+    tls_ssl_options = no_ticket, no_compression
+</pre>
+</blockquote>
+
+<p> You should only enable features via the hexadecimal mask when
+the need to control the feature is critical (to deal with a new
+vulnerability or a serious interoperability problem).  Postfix DOES
+NOT promise backwards compatible behavior with respect to the mask
+bits.  A feature enabled via the mask in one release may be enabled
+by other means in a later release, and the mask bit will then be
+ignored.  Therefore, use of the hexadecimal mask is only a temporary
+measure until a new Postfix or OpenSSL release provides a better
+solution.  </p>
 
 <h2> <a name="client_tls">SMTP Client specific settings</a> </h2>
 
@@ -927,6 +1019,10 @@ in more detail in the sections that follow.</p>
 <dd><a href="#client_tls_may">Opportunistic TLS.</a></dd>
 <dt><b>encrypt</b></dt>
 <dd><a href="#client_tls_encrypt">Mandatory TLS encryption.</a>
+<dt><b>dane</b></dt>
+<dd><a href="#client_tls_dane">Opportunistic DANE TLS.</a>
+<dt><b>dane-only</b></dt>
+<dd><a href="#client_tls_dane">Mandatory DANE TLS.</a>
 <dt><b>fingerprint</b></dt>
 <dd><a href="#client_tls_fprint">Certificate fingerprint verification.</a>
 <dt><b>verify</b></dt>
@@ -1111,16 +1207,238 @@ just in case the transport table entries are not specified consistently. </p>
 </pre>
 </blockquote>
 
+<h4><a name="client_tls_dane">DANE TLS authentication.</a> </h4>
+
+<p> The Postfix SMTP client supports two TLS security levels based
+on <a href="http://tools.ietf.org/html/rfc6698">RFC6698</a> DANE TLSA records.  The opportunistic "dane" level and
+the mandatory "dane-only" level. </p>
+
+<p> The "dane" level is a stronger form of <a
+href="#client_tls_may">opportunistic</a> TLS that is resistant to
+man in the middle and downgrade attacks when the destination domain
+uses DNSSEC to publish DANE TLSA records for its MX hosts.  If a
+remote SMTP server has usable DANE TLSA records, these will be used
+to authenticate the server.  If TLSA records are published for a
+given remote SMTP server (implying TLS support), but are not usable
+due to unsupported parameters, the Postfix SMTP client will use <a
+href="#client_tls_encrypt">mandatory</a> unauthenticated TLS.
+Otherwise, the Postfix SMTP client behavior is the same as with <a
+href="#client_tls_may">may</a>.  </p>
+
+<p> The "dane-only" level is a form of <a
+href="#client_tls_secure">secure-channel</a> TLS based on the DANE PKI.
+If usable TLSA records are present these are used to authenticate the
+remote SMTP server.  Otherwise, or when server certificate verification
+fails, delivery via the server in question tempfails.  </p>
+
+<p> At both security levels, the TLS policy for the destination is
+obtained via TLSA records validated with DNSSEC.  For TLSA policy
+to be in effect, the destination domain's containing DNS zone must
+be signed and the Postfix SMTP client's operating system must be
+configured to send its DNS queries to a recursive DNS nameserver
+that is able to validate the signed records.  Each MX host's DNS
+zone should also be signed, and should publish DANE TLSA (<a href="http://tools.ietf.org/html/rfc6698">RFC 6698</a>)
+records that specify how that MX host's TLS certificate is to be
+verified.  TLSA records do not preempt the normal SMTP MX host
+selection algorithm, if some MX hosts support TLSA and others do
+not, TLS security will vary from delivery to delivery.  It is up
+to the domain owner to configure their MX hosts and their DNS
+sensibly.  To configure the Postfix SMTP client for DNSSEC lookups
+see the documentation for the <a href="postconf.5.html#smtp_dns_support_level">smtp_dns_support_level</a> <a href="postconf.5.html">main.cf</a>
+parameter.  The <a href="postconf.5.html#tls_dane_trust_anchor_digest_enable">tls_dane_trust_anchor_digest_enable</a> <a href="postconf.5.html">main.cf</a> parameter
+controls optional support for trust-anchor digest TLSA records.
+</p>
+
+<p> The Postfix SMTP client deviates from <a href="http://tools.ietf.org/html/rfc6698">RFC 6698</a> in cases where
+strict adherence is likely to create opportunities for delayed (or
+eventually bounced) email with no substantive security gain.  Most
+notably, it is not expected that <a href="postconf.5.html#smtp_tls_CAfile">smtp_tls_CAfile</a> and <a href="postconf.5.html#smtp_tls_CApath">smtp_tls_CApath</a>
+can reasonably include every public CA that a remote SMTP server's
+administrator may believe to be well-known.  Therefore, certificate
+usages "0" and "2" from <a href="http://tools.ietf.org/html/rfc6698">RFC 6698</a> which are intended to "constrain"
+existing PKI trust, are instead treated as "trust assertions" and
+mapped to "1" and "3" respectively.  That is, with certificate usage
+"0" and "2", Postfix will not require the remote SMTP server's
+certificate to be trusted with respect to any locally defined public
+CAs, it is the domain owner's responsibility to ensure that the
+certificate associations in their TLSA records are appropriate
+to authenticate their SMTP servers.  </p>
+
+<p> In addition, the Postfix SMTP client does not assume that the
+remote SMTP server will necessarily be configured to present the
+certificate of any usage "0" root CA in its TLS protocol <a
+href="https://tools.ietf.org/html/rfc2246#section-7.4.2">certificate_list</a>.
+Therefore, support for usage "0" certificate and public-key digests
+is disabled by default, see <a href="postconf.5.html#tls_dane_trust_anchor_digest_enable">tls_dane_trust_anchor_digest_enable</a> for
+details.  While undigested trust-anchor certificates in TLSA records
+are supported, publishing complete certificates in DNS is unwise
+given the resulting excessively large DNS record sizes.  Therefore,
+in its default configuration the Postfix SMTP client essentially
+supports only certificate usages "1", "2" and "3" (with "1" treated as
+though it were "3").  </p>
+
+<p> Finally, the interaction of DANE with MX hostnames that are
+CNAMEs differs from the draft specification in the names used to
+construct the associated <a
+href="https://tools.ietf.org/html/draft-ietf-dane-srv-02#section-3.2">TLSA
+queries</a>.  When the MX hostname is a CNAME, the draft proposal
+to use the unexpanded MX hostname in TLSA lookups is fragile and
+unintuitive.  For this to work, the domain owner has to either
+duplicate the TLSA records of the target (host, service) pair in
+his own DNS or furnish the target host with an additional
+certificate and private key that would be negotiated via SNI.
+Neither of these are robust or easy to manage.  It is far better
+to expand the CNAME recursively to the underlying target hostname
+(keeping track of DNSSEC validation along the way) and to use the
+expanded name to construct the TLSA query and, if appropriate, in
+server certificate name checks.  This is the approach taken by the
+Postfix SMTP client, and if sanity prevails will also be the approach
+taken in the final standard.  </p>
+
+<p> When usable TLSA records are obtained for the remote SMTP server
+the Postfix SMTP client is obligated to include the SNI TLS extension
+in its SSL client hello message.  This may help the remote SMTP
+server live up to its promise to provide a certificate that matches
+its TLSA records.  Since TLS extensions require TLS 1.0 or later,
+the Postfix SMTP client must disable SSLv2 and SSLv3 when SNI is
+required.  If you use "dane" or "dane-only", do not disable TLSv1,
+except perhaps via the policy table for destinations which you are
+sure will support TLSv1.1 or TLSv1.2.  </p>
+
+<p> For purposes of protocol and cipher selection, the "dane"
+security level is treated like a "mandatory" TLS security level,
+and weak ciphers and protocols are disabled.  Since DANE authenticates
+server certificates the "aNULL" cipher-suites are transparently
+excluded at this level, no need to configure this manually.  <a href="http://tools.ietf.org/html/rfc6698">RFC</a>
+<a href="http://tools.ietf.org/html/rfc6698">6698</a> (DANE) TLS authentication is available with Postfix 2.11 and
+later.  </p>
+
+<p> When a DANE TLSA record specifies a trust-anchor (TA) certificate
+(that is an issuing CA), the strategy used to verify the peername
+of the server certificate is unconditionally "nexthop, hostname".
+Both the nexthop domain and the hostname obtained from the
+DNSSEC-validated MX lookup are safe from forgery and the server
+certificate must contain at least one of these names.  </p>
+
+<p> When a DANE TLSA record specifies an end-entity (EE) certificate,
+(that is the actual server certificate), as with the fingerprint
+security level below, no name checks or certificate expiration checks
+are applied. The server certificate (or its public key) either matches
+the DANE record or not.  Server administrators should publish such
+EE records in preference to all other types. </p>
+
+<p> The pre-requisites for DANE support in the Postfix SMTP client are: </p>
+<ul>
+<li> A <i>compile-time</i> OpenSSL library that supports the TLS SNI
+extension and the "sha256" and "sha512" message digests.
+<li> A <i>compile-time</i> DNS resolver library that supports DNSSEC.
+Postfix binaries built on an older system will not support DNSSEC even
+if deployed on a system with an updated resolver library.
+<li> The "<a href="postconf.5.html#smtp_dns_support_level">smtp_dns_support_level</a>" must be set to "dnssec".
+<li> The "<a href="postconf.5.html#smtp_host_lookup">smtp_host_lookup</a>" parameter must include "dns".
+<li> A DNSSEC-validating recursive resolver (see note below).
+</ul>
+<p> The above client pre-requisites do not apply to the Postfix SMTP server.
+It will support DANE provided it supports TLSv1 and its TLSA records are
+published in a DNSSEC signed zone.  To receive DANE secured mail for multiple
+domains, use the same hostname to add the server to each domain's MX
+records.  There are no plans to implement SNI in the Postfix SMTP server.  </p>
+
+<p> Note: The Postfix SMTP client's internal stub DNS resolver is
+DNSSEC-aware, but it does not itself validate DNSSEC records, rather
+it delegates DNSSEC validation to the operating system's configured
+recursive DNS nameserver.  The Postfix DNS client relies on a secure
+channel to the resolver's cache for DNSSEC integrity, but does not
+support TSIG to protect the transmission channel between itself and
+the nameserver.  Therefore, it is strongly recommended (DANE security
+guarantee void otherwise) that each MTA run a local DNSSEC-validating
+recursive resolver ("unbound" from nlnetlabs.nl is a reasonable
+choice) listening on the loopback interface, and that the system
+be configured to use <i>only</i> this local nameserver.  The local
+nameserver may forward queries to an upstream recursive resolver
+on another host if desired.  </p>
+
+<p> Note: When the operating system's recursive nameserver is not
+local, enabling EDNS0 expanded DNS packet sizes and turning on the
+DNSSEC "DO" bit in the DNS request and/or the new DNSSEC-specific
+records returned in the nameserver's replies may cause problems
+with older or buggy firewall and DNS server implementations.
+Therefore, Postfix does not enable DNSSEC by default.  Since MX
+lookups happen before the security level is determined, DANE support
+is disabled for all destinations unless you set "<a href="postconf.5.html#smtp_dns_support_level">smtp_dns_support_level</a>
+= dnssec".  To enable DNSSEC lookups selectively, define a new
+dedicated transport with a "-o <a href="postconf.5.html#smtp_dns_support_level">smtp_dns_support_level</a>=dnssec"
+override in <a href="master.5.html">master.cf</a> and route selected domains to that transport.
+If DNSSEC proves to be sufficiently reliable for these domains, you
+can enable it for all destinations by changing the global
+<a href="postconf.5.html#smtp_dns_support_level">smtp_dns_support_level</a> in <a href="postconf.5.html">main.cf</a>.  </p>
+
+<p><b>Example</b>: "dane" security for selected destinations, with
+opportunistic TLS by default.  This is the recommended configuration
+for early adopters. <p>
+<ul>
+<li> <p> The "example.com" destination uses DANE, but if TLSA records
+are not present or are unusable, mail is deferred. </p>
+
+<li> <p> The "example.org" destination uses DANE if possible, but if no TLSA
+records are found opportunistic TLS is used. </p>
+</ul>
+
+<blockquote>
+<pre>
+<a href="postconf.5.html">main.cf</a>:
+    indexed = ${<a href="postconf.5.html#default_database_type">default_database_type</a>}:${<a href="postconf.5.html#config_directory">config_directory</a>}/
+    #
+    # default: Opportunistic TLS with no DNSSEC lookups.
+    #
+    <a href="postconf.5.html#smtp_tls_security_level">smtp_tls_security_level</a> = may
+    <a href="postconf.5.html#smtp_dns_support_level">smtp_dns_support_level</a> = enabled
+    #
+    # Per-destination TLS policy
+    #
+    <a href="postconf.5.html#smtp_tls_policy_maps">smtp_tls_policy_maps</a> = ${indexed}tls_policy
+    #
+    # <a href="postconf.5.html#default_transport">default_transport</a> = smtp, but some destinations are special:
+    #
+    <a href="postconf.5.html#transport_maps">transport_maps</a> = ${indexed}transport
+</pre>
+</blockquote>
+
+<blockquote>
+<pre>
+transport:
+    example.com dane
+    example.org dane
+</pre>
+</blockquote>
+
+<blockquote>
+<pre>
+tls_policy:
+    example.com dane-only
+</pre>
+</blockquote>
+
+<blockquote>
+<pre>
+<a href="master.5.html">master.cf</a>:
+    dane       unix  -       -       n       -       -       smtp
+      -o <a href="postconf.5.html#smtp_dns_support_level">smtp_dns_support_level</a>=dnssec
+      -o <a href="postconf.5.html#smtp_tls_security_level">smtp_tls_security_level</a>=dane
+</pre>
+</blockquote>
+
 <h4><a name="client_tls_fprint"> Certificate fingerprint verification </a> </h4>
 
-<p> Certificate fingerprint verification is available with Postfix
-2.5 and later. At this security level ("<a href="postconf.5.html#smtp_tls_security_level">smtp_tls_security_level</a> =
-fingerprint"), no trusted certificate authorities are used or
-required.  The certificate trust chain, expiration date, ... are
-not checked. Instead, the <a href="postconf.5.html#smtp_tls_fingerprint_cert_match">smtp_tls_fingerprint_cert_match</a> parameter
-or the "match" attribute in the <a href="#client_tls_policy">policy</a>
-table lists the remote SMTP server certificate fingerprint or
-public key fingerprint (Postfix 2.9 and later). </p>
+<p> At the <i>fingerprint</i> security level, no trusted certificate
+authorities are used or required.  The certificate trust chain,
+expiration date, etc., are not checked. Instead, the
+<a href="postconf.5.html#smtp_tls_fingerprint_cert_match">smtp_tls_fingerprint_cert_match</a> parameter or the "match" attribute
+in the <a href="#client_tls_policy">policy</a> table lists the
+remote SMTP server certificate fingerprint or public key fingerprint.
+Certificate fingerprint verification is available with Postfix 2.5
+and later, public-key fingerprint support is available with Postfix
+2.9 and later.  </p>
 
 <p> If certificate fingerprints are exchanged securely, this is the
 strongest, and least scalable security level. The administrator needs
@@ -1217,7 +1535,7 @@ to Postfix 2.9.6 or later. </p>
 
 <h4><a name="client_tls_verify"> Mandatory server certificate verification </a> </h4>
 
-<p> At the "verify" TLS security level, messages are sent only over
+<p> At the <i>verify</i> TLS security level, messages are sent only over
 TLS encrypted sessions if the remote SMTP server certificate is
 valid (not
 expired or revoked, and signed by a trusted certificate authority)
@@ -1239,6 +1557,19 @@ DNS names are specified, the certificate CommonName is checked.
 If you want mandatory encryption without server certificate
 verification, see <a href="#client_tls_encrypt">above</a>. </p>
 
+<p> With Postfix &ge; 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
@@ -1300,6 +1631,19 @@ specified, the CommonName is checked. If you want mandatory encryption
 without server certificate verification, see <a
 href="#client_tls_encrypt">above</a>. </p>
 
+<p> With Postfix &ge; 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
@@ -1694,6 +2038,13 @@ hostname(s). When, as is current practice, the client verifies the
 insecurely obtained MX hostname, it is subject to a DNS man-in-the-middle
 attack. </p>
 
+<p> Adoption of DNSSEC and <a href="http://tools.ietf.org/html/rfc6698">RFC6698</a> (DANE) may gradually (as domains
+implement DNSSEC and publish TLSA records for their MX hosts) address
+the DNS man-in-the-middle risk and provide scalable key management
+for SMTP with TLS.  Postfix &ge; 2.11 supports the new <a
+href="#client_tls_dane">dane</a> and <a href="#client_tls_dane">dane-only</a>
+security levels that take advantage of these standards.  </p>
+
 <p> If clients instead attempted to verify the recipient domain name,
 an SMTP server for multiple domains would need to
 list all its email domain names in its certificate, and generate a
@@ -1789,6 +2140,31 @@ parameter, the optional "ciphers" attribute overrides the
 "exclude" attribute (Postfix &ge; 2.6) overrides the <a href="postconf.5.html">main.cf</a>
 <a href="postconf.5.html#smtp_tls_mandatory_exclude_ciphers">smtp_tls_mandatory_exclude_ciphers</a> parameter.  </dd>
 
+<dt><b>dane</b></dt> <dd><a href="#client_tls_dane">Opportunistic DANE TLS</a>.
+The TLS policy for the destination is obtained via TLSA records in
+DNSSEC.  If no TLSA records are found, the effective security level
+used is <a href="#client_tls_may">may</a>.  If TLSA records are
+found, but none are usable, the effective security level is <a
+href="#client_tls_encrypt">encrypt</a>.  When usable TLSA records
+are obtained for the remote SMTP server, SSLv2 and SSLv3 are
+automatically disabled (see <a href="postconf.5.html#smtp_tls_mandatory_protocols">smtp_tls_mandatory_protocols</a>), and the
+server certificate must match the TLSA records.  The
+<a href="postconf.5.html#tls_dane_trust_anchor_digest_enable">tls_dane_trust_anchor_digest_enable</a> parameter controls optional
+support for trust-anchor digest TLSA records.  <a href="http://tools.ietf.org/html/rfc6698">RFC 6698</a> (DANE) TLS
+authentication and DNSSEC support is available with Postfix 2.11
+and later.  </dd>
+
+<dt><b>dane-only</b></dt> <dd><a href="#client_tls_dane">Mandatory DANE TLS</a>.
+The TLS policy for the destination is obtained via TLSA records in
+DNSSEC.  If no TLSA records are found, or none are usable, no
+connection is made to the server.  When usable TLSA records are
+obtained for the remote SMTP server, SSLv2 and SSLv3 are automatically
+disabled (see <a href="postconf.5.html#smtp_tls_mandatory_protocols">smtp_tls_mandatory_protocols</a>), and the server certificate
+must match the TLSA records.  The <a href="postconf.5.html#tls_dane_trust_anchor_digest_enable">tls_dane_trust_anchor_digest_enable</a>
+parameter controls optional support for trust-anchor digest TLSA
+records.  <a href="http://tools.ietf.org/html/rfc6698">RFC 6698</a> (DANE) TLS authentication and DNSSEC support is
+available with Postfix 2.11 and later.  </dd>
+
 <dt><b>fingerprint</b></dt> <dd><a href="#client_tls_fprint">Certificate
 fingerprint verification.</a> Available with Postfix 2.5 and
 later. At this security level, there are no trusted certificate
@@ -1806,12 +2182,16 @@ digits. </dd>
 
 <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 &ge; 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,
@@ -1819,7 +2199,11 @@ 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_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 &ge; 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>
 
@@ -2084,6 +2468,64 @@ defers delivery if no alternative server is available. </p>
 </pre>
 </blockquote>
 
+<p> With Postfix 2.8 and later, the <a href="postconf.5.html#tls_disable_workarounds">tls_disable_workarounds</a> parameter
+specifies a list or bit-mask of OpenSSL bug work-arounds to disable. This
+may be necessary if one of the work-arounds enabled by default in
+OpenSSL proves to pose a security risk, or introduces an unexpected
+interoperability issue. Some bug work-arounds known to be problematic
+are disabled in the default value of the parameter when linked with
+an OpenSSL library that could be vulnerable. </p>
+
+<p> Example: </p>
+<blockquote>
+<pre>
+/etc/postfix/<a href="postconf.5.html">main.cf</a>:
+    <a href="postconf.5.html#tls_disable_workarounds">tls_disable_workarounds</a> = 0xFFFFFFFF
+    <a href="postconf.5.html#tls_disable_workarounds">tls_disable_workarounds</a> = CVE-2010-4180, LEGACY_SERVER_CONNECT
+</pre>
+</blockquote>
+
+<p> Note: Disabling LEGACY_SERVER_CONNECT is not wise at this
+time, lots of servers are still unpatched and Postfix is <a
+href="http://www.postfix.org/wip.html#tls-renegotiation">not
+significantly vulnerable</a> to the renegotiation issue in the TLS
+protocol. </p>
+
+<p> With Postfix &ge; 2.11, the tls_ssl_options parameter specifies
+a list or bit-mask of OpenSSL options to enable.  Specify one or
+more of the named options below, or a hexadecimal bitmask of options
+found in the ssl.h file corresponding to the run-time OpenSSL
+library.  While it may be reasonable to turn off all bug workarounds
+(see above), it is not a good idea to attempt to turn on all features.
+</p>
+
+<p> A future version of OpenSSL may by default no longer allow
+connections to servers that don't support secure renegotiation.
+Since the exposure for SMTP is minimal, and some SMTP servers may
+remain unpatched, you can add LEGACY_SERVER_CONNECT to the
+options to restore the more permissive default of current OpenSSL
+releases.  </p>
+
+<p> Example: </p>
+
+<blockquote>
+<pre>
+/etc/postfix/<a href="postconf.5.html">main.cf</a>:
+    tls_ssl_options = NO_TICKET, NO_COMPRESSION, LEGACY_SERVER_CONNECT
+</pre>
+</blockquote>
+
+<p> You should only enable features via the hexadecimal mask when
+the need to control the feature is critical (to deal with a new
+vulnerability or a serious interoperability problem).  Postfix DOES
+NOT promise backwards compatible behavior with respect to the mask
+bits.  A feature enabled via the mask in one release may be enabled
+by other means in a later release, and the mask bit will then be
+ignored.  Therefore, use of the hexadecimal mask is only a temporary
+measure until a new Postfix or OpenSSL release provides a better
+solution.  </p>
+
 <h2><a name="tlsmgr_controls"> TLS manager specific settings </a> </h2>
 
 <p> The security of cryptographic software such as TLS depends
index 09689d910f9f53aeb6dbedc10587e6e4ef64df67..18cb8206a5b02e5e3fd74564879b4affc9582fd2 100644 (file)
@@ -43,6 +43,10 @@ CIDR_TABLE(5)                                                    CIDR_TABLE(5)
               address is a sequence of three to eight hexadecimal
               octet pairs separated by ":".
 
+              The  <i>network</i><b>_</b><i>mask</i>  is the number of high-order bits
+              in the <i>network</i><b>_</b><i>address</i> that the search string  must
+              match.
+
               Before  comparisons are made, lookup keys and table
               entries are converted from string to binary. There-
               fore  table  entries  will be matched regardless of
index 20bed174b8025922ef4381986cb8406163d8a2e3..631eeb74fe5df158c6b29537203d859dc0d7b2e2 100644 (file)
@@ -587,6 +587,27 @@ SMTP(8)                                                                SMTP(8)
               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>smtp_tls_dane_notfound_tlsa_level (may)</b>
+              The "degraded" security level when the "dane" secu-
+              rity level is specified, but no validated DANE TLSA
+              records are published.
+
+       <b>smtp_tls_dane_unusable_tlsa_level (encrypt)</b>
+              The "degraded" security level when the "dane" secu-
+              rity   level  is  specified,  validated  DANE  TLSA
+              records are present, but none are usable.
+
+       <b><a href="postconf.5.html#tls_dane_trust_anchor_digest_enable">tls_dane_trust_anchor_digest_enable</a>   (trust-anchor-asser-</b>
+       <b>tion)</b>
+              <a href="http://tools.ietf.org/html/rfc6698">RFC 6698</a> trust-anchor digest support in the Postfix
+              TLS library.
+
 <b>OBSOLETE STARTTLS CONTROLS</b>
        The  following configuration parameters exist for compati-
        bility with Postfix versions before 2.3. Support for these
index fbddb59c3e8242a67f8256c241d44400e3d559da..09cacecca07ebd022b93dadd12664a34657b1021 100644 (file)
@@ -4967,6 +4967,17 @@ configuration parameter.  See there for details. </p>
 <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>
@@ -9855,10 +9866,11 @@ and "inet:host:port" destinations, as LMTP hostnames are never
 subject to MX lookups.  </p>
 
 <p>The "dnssec" setting is recommended only if you plan to use the
-"<a href="TLS_README.html#client_tls_dane">dane</a>" TLS security
+<a href="TLS_README.html#client_tls_dane">dane</a> or <a
+href="TLS_README.html#client_tls_dane">dane-only</a> TLS security
 level, otherwise enabling DNSSEC support in Postfix offers no
-additional security. Postfix DNSSEC support relies on an up-stream
-recursive nameserver that validates DNSSEC signatures. Such a DNS
+additional security.  Postfix DNSSEC support relies on an upstream
+recursive nameserver that validates DNSSEC signatures.  Such a DNS
 server will always filter out forged DNS responses, even when Postfix
 itself is not configured to use DNSSEC. </p>
 
@@ -11420,6 +11432,18 @@ whitespace, commas or colons. In the policy table "protocols" attribute
 empty value means allow all protocols. The valid protocol names, (see
 <b>SSL_get_version(3)</b>), are "SSLv2", "SSLv3" and "TLSv1". </p>
 
+<p> At the <a href="TLS_README.html#client_tls_dane">dane</a> and
+<a href="TLS_README.html#client_tls_dane">dane-only</a> security
+levels, when usable TLSA records are obtained for the remote SMTP
+server, the Postfix SMTP client is obligated to include the SNI TLS
+extension in its SSL client hello message.  This may help the remote
+SMTP server live up to its promise to provide a certificate that
+matches its TLSA records.  Since TLS extensions require TLS 1.0 or
+later, the Postfix SMTP client must disable SSLv2 and SSLv3 when
+SNI is required.  If you use "dane" or "dane-only" do not disable
+TLSv1, except perhaps via the policy table for destinations which
+you are sure will support TLSv1.1 or TLSv1.2.  </p>
+
 <p> Note: As of OpenSSL 1.0.1 two new protocols are defined, "TLSv1.1"
 and "TLSv1.2". If an older Postfix version is linked against OpenSSL
 1.0.1 or later, these, or any other new protocol versions, are
@@ -11588,10 +11612,10 @@ security are: </p>
 
 <dl>
 
-<dt><b>none</b></dt>
+<dt><b><a href="TLS_README.html#client_tls_none">none</a></b></dt>
 <dd>No TLS. No additional attributes are supported at this level. </dd>
 
-<dt><b>may</b></dt>
+<dt><b><a href="TLS_README.html#client_tls_may">may</a></b></dt>
 <dd>Opportunistic TLS. Since sending in the clear is acceptable,
 demanding stronger than default TLS security merely reduces
 inter-operability. The optional "ciphers", "exclude" and "protocols"
@@ -11602,7 +11626,8 @@ handshakes fail, Postfix retries the connection with TLS disabled.
 This allows mail delivery to sites with non-interoperable TLS
 implementations.</dd>
 
-<dt><b>encrypt</b></dt> <dd>Mandatory TLS encryption. At this level
+<dt><b><a href="TLS_README.html#client_tls_encrypt">encrypt</a></b></dt>
+<dd>Mandatory TLS encryption. At this level
 and higher, the optional "protocols" attribute overrides the <a href="postconf.5.html">main.cf</a>
 <a href="postconf.5.html#smtp_tls_mandatory_protocols">smtp_tls_mandatory_protocols</a> parameter, the optional "ciphers" attribute
 overrides the <a href="postconf.5.html">main.cf</a> <a href="postconf.5.html#smtp_tls_mandatory_ciphers">smtp_tls_mandatory_ciphers</a> parameter, and the
@@ -11611,7 +11636,35 @@ optional "exclude" attribute (Postfix &ge; 2.6) overrides the <a href="postconf.
 multiple protocols or excluded ciphers must be separated by colons,
 as attribute values may not contain whitespace or commas. </dd>
 
-<dt><b>fingerprint</b></dt> <dd>Certificate fingerprint
+<dt><b><a href="TLS_README.html#client_tls_dane">dane</a></b></dt>
+<dd>Opportunistic DANE TLS.  The TLS policy for the destination is
+obtained via TLSA records in DNSSEC.  If no TLSA records are found,
+the effective security level used is <a
+href="TLS_README.html#client_tls_may">may</a>.  If TLSA records are
+found, but none are usable, the effective security level is <a
+href="TLS_README.html#client_tls_encrypt">encrypt</a>.  When usable
+TLSA records are obtained for the remote SMTP server, SSLv2 and
+SSLv3 are automatically disabled (see <a href="postconf.5.html#smtp_tls_mandatory_protocols">smtp_tls_mandatory_protocols</a>),
+and the server certificate must match the TLSA records.  The
+<a href="postconf.5.html#tls_dane_trust_anchor_digest_enable">tls_dane_trust_anchor_digest_enable</a> parameter controls optional
+support for trust-anchor digest TLSA records.  <a href="http://tools.ietf.org/html/rfc6698">RFC 6698</a> (DANE) TLS
+authentication and DNSSEC support is available with Postfix 2.11
+and later.  </dd>
+
+<dt><b><a href="TLS_README.html#client_tls_dane">dane-only</a></b></dt>
+<dd>Mandatory DANE TLS.  The TLS policy for the destination is
+obtained via TLSA records in DNSSEC.  If no TLSA records are found,
+or none are usable, no connection is made to the server.  When
+usable TLSA records are obtained for the remote SMTP server, SSLv2
+and SSLv3 are automatically disabled (see <a href="postconf.5.html#smtp_tls_mandatory_protocols">smtp_tls_mandatory_protocols</a>),
+and the server certificate must match the TLSA records.  The
+<a href="postconf.5.html#tls_dane_trust_anchor_digest_enable">tls_dane_trust_anchor_digest_enable</a> parameter controls optional
+support for trust-anchor digest TLSA records.  <a href="http://tools.ietf.org/html/rfc6698">RFC 6698</a> (DANE) TLS
+authentication and DNSSEC support is available with Postfix 2.11
+and later.  </dd>
+
+<dt><b><a href="TLS_README.html#client_tls_fingerprint">fingerprint</a></b></dt>
+<dd>Certificate fingerprint
 verification. Available with Postfix 2.5 and later. At this security
 level, there are no trusted certificate authorities. The certificate
 trust chain, expiration date, ... are not checked. Instead,
@@ -11626,7 +11679,8 @@ match attributes can be employed. The ":" character is not used as a
 delimiter as it occurs between each pair of fingerprint (hexadecimal)
 digits. </dd>
 
-<dt><b>verify</b></dt> <dd>Mandatory TLS verification.  At this security
+<dt><b><a href="TLS_README.html#client_tls_verify">verify</a></b></dt>
+<dd>Mandatory TLS verification.  At this security
 level, DNS MX lookups are trusted to be secure enough, and the name
 verified in the server certificate is usually obtained indirectly via
 unauthenticated DNS MX lookups.  The optional "match" attribute overrides
@@ -11635,7 +11689,8 @@ multiple match patterns and strategies must be separated by colons.
 In practice explicit control over matching is more common with the
 "secure" policy, described below. </dd>
 
-<dt><b>secure</b></dt> <dd>Secure-channel TLS. At this security level, DNS
+<dt><b><a href="TLS_README.html#client_tls_secure">secure</a></b></dt>
+<dd>Secure-channel TLS. At this security level, DNS
 MX lookups, though potentially used to determine the candidate next-hop
 gateway IP addresses, are <b>not</b> trusted to be secure enough for TLS
 peername verification. Instead, the default name verified in the server
@@ -11806,10 +11861,11 @@ parameters <a href="postconf.5.html#smtp_use_tls">smtp_use_tls</a>, <a href="pos
 
 <dl>
 
-<dt><b>none</b></dt> <dd> TLS will not be used unless enabled for specific
+<dt><b><a href="TLS_README.html#client_tls_none">none</a></b></dt>
+<dd> No TLS. TLS will not be used unless enabled for specific
 destinations via <a href="postconf.5.html#smtp_tls_policy_maps">smtp_tls_policy_maps</a>. </dd>
 
-<dt><b>may</b></dt>
+<dt><b><a href="TLS_README.html#client_tls_may">may</a></b></dt>
 <dd> Opportunistic TLS. Use TLS if this is supported by the remote
 SMTP server, otherwise use plaintext. Since
 sending in the clear is acceptable, demanding stronger than default TLS
@@ -11823,7 +11879,8 @@ When TLS handshakes fail, the connection is retried with TLS disabled.
 This allows mail delivery to sites with non-interoperable TLS
 implementations. </dd>
 
-<dt><b>encrypt</b></dt> <dd>Mandatory TLS encryption. Since a minimum
+<dt><b><a href="TLS_README.html#client_tls_encrypt">encrypt</a></b></dt>
+<dd>Mandatory TLS encryption. Since a minimum
 level of security is intended, it is reasonable to be specific about
 sufficiently secure protocol versions and ciphers. At this security level
 and higher, the <a href="postconf.5.html">main.cf</a> parameters <a href="postconf.5.html#smtp_tls_mandatory_protocols">smtp_tls_mandatory_protocols</a> and
@@ -11832,17 +11889,53 @@ cipher grade which the administrator considers secure enough for
 mandatory encrypted sessions. This security level is not an appropriate
 default for systems delivering mail to the Internet. </dd>
 
-<dt><b>fingerprint</b></dt> <dd>Certificate fingerprint
-verification. Available with Postfix 2.5 and later. At this security
-level, there are no trusted certificate authorities. The certificate
-trust chain, expiration date, ... are not checked. Instead, the
-<b><a href="postconf.5.html#smtp_tls_fingerprint_cert_match">smtp_tls_fingerprint_cert_match</a></b> parameter lists the certificate
-fingerprint or public key fingerprint (Postfix 2.9 and later) of
-the valid server certificate. The digest
+<dt><b><a href="TLS_README.html#client_tls_dane">dane</a></b></dt>
+<dd>Opportunistic DANE TLS.  At this security level, the TLS policy
+for the destination is obtained via DNSSEC.  For TLSA policy to be
+in effect, the destination domain's containing DNS zone must be
+signed and the Postfix SMTP client's operating system must be
+configured to send its DNS queries to a recursive DNS nameserver
+that is able to validate the signed records.  Each MX host's DNS
+zone should also be signed, and should publish DANE TLSA (<a href="http://tools.ietf.org/html/rfc6698">RFC 6698</a>)
+records that specify how that MX host's TLS certificate is to be
+verified.  TLSA records do not preempt the normal SMTP MX host
+selection algorithm, if some MX hosts support TLSA and others do
+not, TLS security will vary from delivery to delivery.  It is up
+to the domain owner to configure their MX hosts and their DNS
+sensibly.  To configure the Postfix SMTP client for DNSSEC lookups
+see the documentation for the <a href="postconf.5.html#smtp_dns_support_level">smtp_dns_support_level</a> <a href="postconf.5.html">main.cf</a>
+parameter.  When TLSA records are not found or are all unusable the
+effective security level is "may" or "encrypt" respectively.  For
+purposes of protocol and cipher selection, the "dane" security level
+is treated like a "mandatory" TLS security level, and weak ciphers
+and protocols are disabled.  SSLv2 and SSLv3 are automatically
+disabled when usable TLSA records are obtained for the remote SMTP
+server, see <a href="postconf.5.html#smtp_tls_mandatory_protocols">smtp_tls_mandatory_protocols</a> for details.  Since DANE
+authenticates server certificates the "aNULL" cipher-suites are
+transparently excluded at this level, no need to configure this
+manually.  The <a href="postconf.5.html#tls_dane_trust_anchor_digest_enable">tls_dane_trust_anchor_digest_enable</a> <a href="postconf.5.html">main.cf</a> parameter
+controls optional support for trust-anchor digest TLSA records.
+<a href="http://tools.ietf.org/html/rfc6698">RFC 6698</a> (DANE) TLS authentication is available with Postfix 2.11
+and later.  </dd>
+
+<dt><b><a href="TLS_README.html#client_tls_dane">dane-only</a></b></dt>
+<dd>Mandatory DANE TLS.  This is just like "dane" above, only DANE
+TLSA authentication is mandatory.  <a href="http://tools.ietf.org/html/rfc6698">RFC 6698</a> (DANE) TLS authentication
+is available with Postfix 2.11 and later.  </dd>
+
+<dt><b><a href="TLS_README.html#client_tls_fingerprint">fingerprint</a></b></dt>
+<dd>Certificate fingerprint verification.
+At this security level, there are no trusted certificate authorities.
+The certificate trust chain, expiration date, etc., are
+not checked. Instead, the <b><a href="postconf.5.html#smtp_tls_fingerprint_cert_match">smtp_tls_fingerprint_cert_match</a></b>
+parameter lists the certificate fingerprint or public key fingerprint
+(Postfix 2.9 and later) of the valid server certificate. The digest
 algorithm used to calculate the fingerprint is selected by the
-<b><a href="postconf.5.html#smtp_tls_fingerprint_digest">smtp_tls_fingerprint_digest</a></b> parameter. </dd>
+<b><a href="postconf.5.html#smtp_tls_fingerprint_digest">smtp_tls_fingerprint_digest</a></b> parameter. Available with Postfix
+2.5 and later.  </dd>
 
-<dt><b>verify</b></dt> <dd>Mandatory TLS verification. At this security
+<dt><b><a href="TLS_README.html#client_tls_verify">verify</a></b></dt>
+<dd>Mandatory TLS verification. At this security
 level, DNS MX lookups are trusted to be secure enough, and the name
 verified in the server certificate is usually obtained indirectly
 via unauthenticated DNS MX lookups. The <a href="postconf.5.html#smtp_tls_verify_cert_match">smtp_tls_verify_cert_match</a>
@@ -11851,7 +11944,8 @@ control over matching is more common at the "secure" level, described
 below. This security level is not an appropriate default for systems
 delivering mail to the Internet. </dd>
 
-<dt><b>secure</b></dt> <dd>Secure-channel TLS.  At this security level,
+<dt><b><a href="TLS_README.html#client_tls_secure">secure</a></b></dt>
+<dd>Secure-channel TLS.  At this security level,
 DNS MX lookups, though potentially used to determine the candidate
 next-hop gateway IP addresses, are <b>not</b> trusted to be secure enough
 for TLS peername verification. Instead, the default name verified in
@@ -11974,6 +12068,56 @@ are not possible. </p>
 <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>  Whether specified in <a href="postconf.5.html">main.cf</a>, or on a per-destination basis,
+the trust-anchor PEM file must be accessible to the Postfix SMTP
+client in the chroot jail if applicable.  The trust-anchor file
+should contain only certificates and public keys, no private key
+material, 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 <a href="postconf.5.html">main.cf</a> parameter supports single-purpose Postfix installations
+that send mail to a fixed set of SMTP peers.  At most sites, if
+trust-anchor files are used at all, they will be specified on a
+per-destination basis via the "tafile" attribute of the "verify"
+and "secure" levels in <a href="postconf.5.html#smtp_tls_policy_maps">smtp_tls_policy_maps</a>.  </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>
@@ -15990,6 +16134,57 @@ bytes (equivalent to 256 bits) is sufficient to generate a 128bit
 <p> This feature is available in Postfix 2.2 and later.  </p>
 
 
+</DD>
+
+<DT><b><a name="tls_dane_trust_anchor_digest_enable">tls_dane_trust_anchor_digest_enable</a>
+(default: trust-anchor-assertion)</b></DT><DD>
+
+<p> <a href="http://tools.ietf.org/html/rfc6698">RFC 6698</a> trust-anchor digest support in the Postfix TLS library.
+Specify zero or more of the following options, separated by comma or
+whitespace.  Option names are case-insensitive.  </p>
+
+<dl>
+
+<dt><b>ca-constraint</b></dt>
+
+<dd> Enable support for <a href="http://tools.ietf.org/html/rfc6698">RFC 6698</a> (DANE TLSA) DNS records that
+contain digests of trust-anchors with certificate usage "0".
+These are often public root CAs, and server operators may
+expect that clients will have the corresponding root certificate
+in their CAfile or CApath.  Most SSL/TLS servers do not send public
+root CA certificate in their certificate chain, so if Postfix does
+not have the CA certificate locally, there is no way to associate
+the digest with the trust chain from the server.  These TLSA records
+are fragile, and only work when you have a complete (whatever that
+means) set of public CAs in your CAfile or CApath.  Enable this
+with caution if at all.  </dd>
+
+<dt><b>trust-anchor-assertion</b></dt>
+
+<dd> Enable support for <a href="http://tools.ietf.org/html/rfc6698">RFC 6698</a> (DANE TLSA) DNS records that contain
+digests of trust-anchors with certificate usage "2".  In this case
+the certificate usage logically requires the server administrator
+to configure the server to include the trust-anchor certificate in
+the server's SSL certificate chain.  These TLSA records are less
+fragile than "ca-constraint" TLSA records, and are enabled by
+default.  Having a "complete" CAfile or CApath does not help in
+this case, you are at the mercy of the remote server administrator's
+competence.  </dd>
+
+</dl>
+
+<p> At the <a href="TLS_README.html#client_tls_dane">dane</a>
+security level, when a TLSA RRset includes only unusable associations,
+the Postfix SMTP client will automatically switch the connection
+to the <a href="TLS_README.html#client_tls_encrypt">encrypt</a>
+security level.  At the <a
+href="TLS_README.html#client_tls_dane">dane-only</a> security level,
+the server in question is skipped and delivery is deferred if no
+secure servers are found.  </p>
+
+<p> This feature is available in Postfix 2.11 and later.  </p>
+
+
 </DD>
 
 <DT><b><a name="tls_disable_workarounds">tls_disable_workarounds</a>
@@ -16332,6 +16527,109 @@ gives timeout errors.  </p>
 <p> This feature is available in Postfix 2.2 and later.  </p>
 
 
+</DD>
+
+<DT><b><a name="tls_ssl_options">tls_ssl_options</a>
+(default: empty)</b></DT><DD>
+
+<p> List or bit-mask of OpenSSL options to enable. </p>
+
+<p> The OpenSSL toolkit provides a set of options that applications
+can enable to tune the OpenSSL behavior.  Some of these work around
+bugs in other implementations and are on by default.  You can use
+the <a href="postconf.5.html#tls_disable_workarounds">tls_disable_workarounds</a> parameter to selectively disable some
+or all of the bug work-arounds, making OpenSSL more strict at the
+cost of non-interoperability with SSL clients or servers that exhibit
+the bugs.  </p>
+
+<p> Other options are off by default, and typically enable or disable
+features rather than bug work-arounds.  These may be turned on (with
+care) via the tls_ssl_options parameter.  The value is a white-space
+or comma separated list of named options chosen from the list below.
+The names are not case-sensitive, you can use lower-case if you
+prefer.  The upper case values below match the corresponding macro
+name in the ssl.h header file with the SSL_OP_ prefix removed.  It
+is possible that your OpenSSL version includes new options added
+after your Postfix source code was last updated, in that case you
+can only enable one of these via the hexadecimal syntax below.  </p>
+
+<p> You should only enable features via the hexadecimal mask when
+the need to control the feature is critical (to deal with a new
+vulnerability or a serious interoperability problem).  Postfix DOES
+NOT promise backwards compatible behavior with respect to the mask
+bits.  A feature enabled via the mask in one release may be enabled
+by other means in a later release, and the mask bit will then be
+ignored.  Therefore, use of the hexadecimal mask is only a temporary
+measure until a new Postfix or OpenSSL release provides a better
+solution.  </p>
+
+<p> If the value of the parameter is a hexadecimal long integer
+starting with "0x", the options corresponding to the bits specified
+in its value are enabled (see openssl/ssl.h and SSL_CTX_set_options(3)).
+You can only enable options not already controlled by other Postfix
+settings.  For example, you cannot disable protocols or enable
+server cipher preference.  Do not attempt to turn all features by
+specifying 0xFFFFFFFF, this is unlikely to be a good idea.  </p>
+
+<dl>
+
+<dt><b>LEGACY_SERVER_CONNECT</b></dt> <dd>See SSL_CTX_set_options(3).</dd>
+
+<dt><b>NO_TICKET</b></dt> <dd>See SSL_CTX_set_options(3).</dd>
+
+<dt><b>NO_COMPRESSION</b></dt> <dd>Disable SSL compression even if
+supported by the OpenSSL library.  Compression is CPU-intensive,
+and compression before encryption does not always improve security.  </dd>
+
+</dl>
+
+<p> This feature is available in Postfix 2.11 and later.  </p>
+
+
+</DD>
+
+<DT><b><a name="tls_wildcard_matches_multiple_labels">tls_wildcard_matches_multiple_labels</a>
+(default: yes)</b></DT><DD>
+
+<p> Match multiple DNS labels with "*" in wildcard certificates.
+</p>
+
+<p> Some mail service providers prepend the customer domain name
+to a base domain for which they have a wildcard TLS certificate.
+For example, the MX records for example.com hosted by example.net
+may be: </p>
+
+<blockquote>
+<pre>
+example.com. IN MX 0 example.com.mx1.example.net.
+example.com. IN MX 0 example.com.mx2.example.net.
+</pre>
+</blockquote>
+
+<p> and the TLS certificate may be for "*.example.net". The "*"
+then corresponds with multiple labels in the mail server domain
+name.  While multi-label wildcards are not widely supported, and
+are not blessed by any standard, there is little to be gained by
+disallowing their use in this context.  </p>
+
+<p> Notes: <p>
+
+<ul>
+
+<li> <p> In a certificate name, the "*" is special only when it is
+used as the first label. </p>
+
+<li> <p> While Postfix (2.11 or later) can match "*" with multiple
+domain name labels, other implementations likely will not.  </p>
+
+<li> <p> Earlier Postfix implementations behave as if
+"<a href="postconf.5.html#tls_wildcard_matches_multiple_labels">tls_wildcard_matches_multiple_labels</a> = no". </p>
+
+</ul>
+
+<p> This feature is available in Postfix 2.11 and later.  </p>
+
+
 </DD>
 
 <DT><b><a name="tlsproxy_enforce_tls">tlsproxy_enforce_tls</a>
diff --git a/postfix/html/posttls-finger.1.html b/postfix/html/posttls-finger.1.html
new file mode 100644 (file)
index 0000000..81705a8
--- /dev/null
@@ -0,0 +1,356 @@
+<!doctype html public "-//W3C//DTD HTML 4.01 Transitional//EN"
+        "http://www.w3.org/TR/html4/loose.dtd">
+<html> <head>
+<meta http-equiv="Content-Type" content="text/html; charset=us-ascii">
+<title> Postfix manual - posttls-finger(1) </title>
+</head> <body> <pre>
+POSTTLS-FINGER(1)                                            POSTTLS-FINGER(1)
+
+<b>NAME</b>
+       posttls-finger  -  Probe the TLS properties of an ESMTP or
+       LMTP server.
+
+<b>SYNOPSIS</b>
+       <b>posttls-finger</b> [<i>options</i>] [<b>inet:</b>]<i>domain</i>[:<i>port</i>] [<i>match ...</i>]
+       <b>posttls-finger</b> -S [<i>options</i>] <b>unix:</b><i>pathname</i> [<i>match ...</i>]
+
+<b>DESCRIPTION</b>
+       <a href="posttls-finger.1.html"><b>posttls-finger</b>(1)</a> connects to  the  specified  destination
+       and reports TLS-related information about the server. With
+       SMTP, the destination is a domainname;  with  LMTP  it  is
+       either a domainname prefixed with <b>inet:</b> or a pathname pre-
+       fixed with <b>unix:</b>.  If Postfix is built  without  TLS  sup-
+       port,  the  resulting posttls-finger program has very lim-
+       ited functionality, and only the <b>-a</b>, <b>-c</b>, <b>-h</b>, <b>-o</b>,  <b>-S</b>,  <b>-t</b>,
+       <b>-T</b> and <b>-v</b> options are available.
+
+       Note:  this  is an unsupported test program. No attempt is
+       made to maintain  compatibility  between  successive  ver-
+       sions.
+
+       For SMTP servers that don't support ESMTP, only the greet-
+       ing banner and the negative EHLO  response  are  reported.
+       Otherwise,  the  reported  EHLO  response  details further
+       server capabilities.
+
+       If TLS support is enabled when <a href="posttls-finger.1.html"><b>posttls-finger</b>(1)</a>  is  com-
+       piled,  and  the server supports <b>STARTTLS</b>, a TLS handshake
+       is attempted.
+
+       If DNSSEC support is available, the connection  TLS  secu-
+       rity  level  (<b>-l</b>  option) defaults to <b>dane</b>; see <a href="TLS_README.html">TLS_README</a>
+       for details. Otherwise, it defaults to <b>secure</b>.  This  set-
+       ting determines the certificate matching policy.
+
+       If  TLS  negotiation succeeds, the TLS protocol and cipher
+       details are reported. The server certificate is then veri-
+       fied  in  accordance  with  the  policy  at the chosen (or
+       default) security level.  With public CA-based trust, when
+       the  <b>-L</b>  option includes <b>certmatch</b>, (true by default) name
+       matching is performed even if the certificate chain is not
+       trusted.   This  logs  the  names found in the remote SMTP
+       server certificate and which if any would match, were  the
+       certificate chain trusted.
+
+       Note:   <a href="posttls-finger.1.html"><b>posttls-finger</b>(1)</a>   does  not  perform  any  table
+       lookups, so the TLS policy  table  and  obsolete  per-site
+       tables  are  not  consulted.  It does not communicate with
+       the <a href="tlsmgr.8.html"><b>tlsmgr</b>(8)</a> daemon (or any other Postfix  daemons);  its
+       TLS  session  cache  is held in private memory, and disap-
+       pears when the process exits.
+
+       With the <b>-r</b> <i>delay</i> option, if the server assigns a TLS ses-
+       sion id, the TLS session is cached. The connection is then
+       closed and re-opened after the specified delay, and  <a href="posttls-finger.1.html"><b>post-</b></a>
+       <a href="posttls-finger.1.html"><b>tls-finger</b>(1)</a>  then reports whether the cached TLS session
+       was re-used.
+
+       When the destination is a load-balancer, it  may  be  dis-
+       tributing  load between multiple server caches. Typically,
+       each server returns its unique name in its EHLO  response.
+       If,  upon  reconnecting  with  <b>-r</b>,  a  new  server name is
+       detected, another session is cached for  the  new  server,
+       and  the  reconnect  is repeated up to a maximum number of
+       times (default 5) that can be specified via the <b>-m</b> option.
+
+       The choice of SMTP or LMTP (<b>-S</b> option) determines the syn-
+       tax of the destination argument. With SMTP, one can  spec-
+       ify  a  service on a non-default port as <i>host</i>:<i>service</i>, and
+       disable MX (mail exchanger) DNS  lookups  with  [<i>host</i>]  or
+       [<i>host</i>]:<i>port</i>.   The [] form is required when you specify an
+       IP address instead of a hostname.  An IPv6  address  takes
+       the  form  [<b>ipv6:</b><i>address</i>].   The  default port for SMTP is
+       taken from the <b>smtp/tcp</b> entry in /etc/services, defaulting
+       to 25 if the entry is not found.
+
+       With  LMTP,  specify  <b>unix:</b><i>pathname</i>  to connect to a local
+       server listening on a  unix-domain  socket  bound  to  the
+       specified  pathname;  otherwise, specify an optional <b>inet:</b>
+       prefix followed by a <i>domain</i> and an optional port, with the
+       same  syntax as for SMTP. The default TCP port for LMTP is
+       24.
+
+       Arguments:
+
+       <b>-a</b> <i>family</i> (default: <b>any</b>)
+              Address family preference: <b>ipv4</b>, <b>ipv6</b> or <b>any</b>.  When
+              using  <b>any</b>, posttls-finger will randomly select one
+              of the two as the more preferred, and  exhaust  all
+              MX  preferences for the first address family before
+              trying any addresses for the other.
+
+       <b>-A</b> <i>trust-anchor.pem</i> (default: none)
+              A list of PEM  trust-anchor  files  that  overrides
+              CAfile  and CApath trust chain verification.  Spec-
+              ify the option multiple times to  specify  multiple
+              files.    See   the   <a href="postconf.5.html">main.cf</a>   documentation   for
+              <a href="postconf.5.html#smtp_tls_trust_anchor_file">smtp_tls_trust_anchor_file</a> for details.
+
+       <b>-c</b>     Disable SMTP chat logging; only TLS-related  infor-
+              mation is logged.
+
+       <b>-C</b>     Print  the  remote  SMTP  server  certificate trust
+              chain in PEM format.  The issuer  DN,  subject  DN,
+              certificate  and  public  key  fingerprints (see <b>-d</b>
+              <i>mdalg</i> option below) are printed above each PEM cer-
+              tificate  block.   If  you  specify <b>-F</b> <i>CAfile</i> or <b>-P</b>
+              <i>CApath</i>, the OpenSSL library may augment  the  chain
+              with  missing  issuer  certificates.   To  see  the
+              actual chain sent by the remote SMTP  server  leave
+              <i>CAfile</i> and <i>CApath</i> unset.
+
+       <b>-d</b> <i>mdalg</i> (default: <b>sha1</b>)
+              The  message  digest algorithm to use for reporting
+              remote  SMTP  server  fingerprints   and   matching
+              against   user  provided  certificate  fingerprints
+              (with DANE TLSA records the algorithm is  specified
+              in the DNS).
+
+       <b>-F</b> <i>CAfile.pem</i> (default: none)
+              The  PEM  formatted  CAfile  for remote SMTP server
+              certificate verification.  By default no CAfile  is
+              used and no public CAs are trusted.
+
+       <b>-g</b> <i>grade</i> (default: medium)
+              The  minimum  TLS cipher grade used by posttls-fin-
+              ger.  See <a href="postconf.5.html#smtp_tls_mandatory_ciphers">smtp_tls_mandatory_ciphers</a> for details.
+
+       <b>-h</b> <i>host</i><b>_</b><i>lookup</i> (default: <b>dns</b>)
+              The hostname lookup methods used  for  the  connec-
+              tion.   See  the  documentation of <a href="postconf.5.html#smtp_host_lookup">smtp_host_lookup</a>
+              for syntax and semantics.
+
+       <b>-l</b> <i>level</i> (default: <b>dane</b> or <b>secure</b>)
+              The security level for the connection, default <b>dane</b>
+              or <b>secure</b> depending on whether DNSSEC is available.
+              For syntax and semantics, see the documentation  of
+              <a href="postconf.5.html#smtp_tls_security_level">smtp_tls_security_level</a>.  When <b>dane</b> or <b>dane-only</b> is
+              supported and selected,  if  no  TLSA  records  are
+              found,  or  all the records found are unusable, the
+              <i>secure</i> level will be used instead.  The <b>fingerprint</b>
+              security  level  allows  you to test certificate or
+              public-key fingerprint matches  before  you  deploy
+              them in the policy table.
+
+              Note,   since   <b>posttls-finger</b>  does  not  actually
+              deliver any email, the <b>none</b>, <b>may</b> and <b>encrypt</b>  secu-
+              rity  levels  are  not  very useful.  Since <b>may</b> and
+              <b>encrypt</b> don't require peer certificates, they  will
+              often  negotiate anonymous TLS ciphersuites, so you
+              won't learn much about  the  remote  SMTP  server's
+              certificates  at  these  levels if it also supports
+              anonymous TLS (though you may learn that the server
+              supports anonymous TLS).
+
+       <b>-L</b> <i>logopts</i> (default: <b>routine,certmatch</b>)
+              Fine-grained  TLS  logging options. To tune the TLS
+              features logged during the TLS  handshake,  specify
+              one or more of:
+
+              <b>0, none</b>
+                     These yield no TLS logging; you'll generally
+                     want more, but this is  handy  if  you  just
+                     want the trust chain:
+                     $ posttls-finger -cC -L none destination
+
+              <b>1, routine, summary</b>
+                     These  synonymous values yield a normal one-
+                     line summary of the TLS connection.
+
+              <b>2, debug</b>
+                     These  synonymous  values  combine  routine,
+                     ssl-debug, cache and verbose.
+
+              <b>3, ssl-expert</b>
+                     These  synonymous  values combine debug with
+                     ssl-handshake-packet-dump.    For    experts
+                     only.
+
+              <b>4, ssl-developer</b>
+                     These  synonymous  values combine ssl-expert
+                     with ssl-session-packet-dump.   For  experts
+                     only,  and  in  most  cases,  use  wireshark
+                     instead.
+
+              <b>ssl-debug</b>
+                     Turn on OpenSSL logging of the  progress  of
+                     the SSL handshake.
+
+              <b>ssl-handshake-packet-dump</b>
+                     Log  hexadecimal  packet  dumps  of  the SSL
+                     handshake; for experts only.
+
+              <b>ssl-session-packet-dump</b>
+                     Log hexadecimal packet dumps of  the  entire
+                     SSL  session;  only  useful to those who can
+                     debug SSL protocol problems from hex  dumps.
+
+              <b>untrusted</b>
+                     Logs   trust  chain  verification  problems.
+                     This is turned on automatically at  security
+                     levels  that  use  peer names signed by cer-
+                     tificate authorities  to  validate  certifi-
+                     cates.  So while this setting is recognized,
+                     you should never need to set it  explicitly.
+
+              <b>peercert</b>
+                     This  logs  a one line summary of the remote
+                     SMTP server certificate subject, issuer, and
+                     fingerprints.
+
+              <b>certmatch</b>
+                     This  logs  remote  SMTP  server certificate
+                     matching, showing the CN  and  each  subjec-
+                     tAltName and which name matched.  With DANE,
+                     logs matching of  TLSA  record  trust-anchor
+                     and end-entity certificates.
+
+              <b>cache</b>  This  logs session cache operations, showing
+                     whether session caching  is  effective  with
+                     the  remote SMTP server.  Automatically used
+                     when reconnecting with the <b>-r</b> option; rarely
+                     needs to be set explicitly.
+
+              <b>verbose</b>
+                     Enables  verbose  logging in the Postfix TLS
+                     driver; includes all of peercert..cache  and
+                     more.
+
+              The  default  is  <b>routine,certmatch</b>. After a recon-
+              nect, <b>peercert</b>, <b>certmatch</b> and <b>verbose</b> are automati-
+              cally disabled while <b>cache</b> and <b>summary</b> are enabled.
+
+       <b>-m</b> <i>count</i> (default: <b>5</b>)
+              When the <b>-r</b>  <i>delay</i>  option  is  specified,  the  <b>-m</b>
+              option  determines  the maximum number of reconnect
+              attempts to use with a server  behind  a  load-bal-
+              acer,  to  see whether connection caching is likely
+              to be effective for this  destination.   Some  MTAs
+              don't  expose  the  underlying  server  identity in
+              their EHLO response; with these servers there  will
+              never be more than 1 reconnection attempt.
+
+       <b>-o</b> <i>name=value</i>
+              Specify zero or more times to override the value of
+              the <a href="postconf.5.html">main.cf</a> parameter <i>name</i>  with  <i>value</i>.   Possible
+              use-cases  include  overriding  the  values  of TLS
+              library parameters, or  "<a href="postconf.5.html#myhostname">myhostname</a>"  to  configure
+              the SMTP EHLO name sent to the remote server.
+
+       <b>-p</b> <i>protocols</i> (default: !SSLv2)
+              List  of  TLS  protocols  that  posttls-finger will
+              exclude or include.  See  smtp_tls_mandatory_proto-
+              cols for details.
+
+       <b>-P</b> <i>CApath/</i> (default: none)
+              The   OpenSSL   CApath/   directory   (indexed  via
+              c_rehash(1)) for  remote  SMTP  server  certificate
+              verification.   By default no CApath is used and no
+              public CAs are trusted.
+
+       <b>-r</b> <i>delay</i>
+              With a cachable TLS session, disconnect and  recon-
+              nect  after  <i>delay</i> seconds. Report whether the ses-
+              sion is re-used. Retry if a new server  is  encoun-
+              tered,  up  to  5 times or as specified with the <b>-m</b>
+              option.  By default reconnection is disabled, spec-
+              ify a positive delay to enable this behavior.
+
+       <b>-S</b>     Disable  SMTP;  that is, connect to an LMTP server.
+              The default port for LMTP over TCP is 24.  Alterna-
+              tive  ports  can  specified by appending "<i>:service-</i>
+              <i>name</i>" or ":<i>portnumber</i>" to the destination argument.
+
+       <b>-t</b> <i>timeout</i> (default: <b>5</b>)
+              The  TCP  connection  timeout to use.  This is also
+              the timeout for reading  the  remote  server's  220
+              banner.
+
+       <b>-T</b> <i>timeout</i> (default: <b>30</b>)
+              The SMTP/LMTP command timeout for EHLO/LHLO, START-
+              TLS and QUIT.
+
+       <b>-v</b>     Enable verose Postfix logging.  Specify  more  than
+              once to increase the level of verbose logging.
+
+       [<b>inet:</b>]<i>domain</i>[:<i>port</i>]
+              Connect  via  TCP  to domain <i>domain</i>, port <i>port</i>. The
+              default port is <b>smtp</b> (or 24 with LMTP).  With  SMTP
+              an  MX lookup is performed to resolve the domain to
+              a host, unless the domain is enclosed  in  <b>[]</b>.   If
+              you  want  to  connect  to  a specific MX host, for
+              instance <i>mx1.example.com</i>, specify [<i>mx1.example.com</i>]
+              as the destination and <i>example.com</i> as a <b>match</b> argu-
+              ment.  When using DNS, the  destination  domain  is
+              assumed  fully  qualified  and no <a href="ADDRESS_CLASS_README.html#default_domain_class">default domain</a> or
+              search suffixes are applied; you  must  use  fully-
+              qualified  names or also enable <b>native</b> host lookups
+              (these don't support <b>dane</b> or <b>dane-only</b> as no DNSSEC
+              validation  information  is  available  via  <b>native</b>
+              lookups).
+
+       <b>unix:</b><i>pathname</i>
+              Connect to the UNIX-domain socket at <i>pathname</i>. LMTP
+              only.
+
+       <b>match ...</b>
+              With  no  match  arguments  specified,  certificate
+              peername  matching  uses  the  compiled-in  default
+              strategies for each security level.  If you specify
+              one or more arguments, these will be  used  as  the
+              list  of certificate or public-key digests to match
+              for the <b>fingerprint</b> level, or as the  list  of  DNS
+              names to match in the certificate at the <b>verify</b> and
+              <b>secure</b> levels.  If the security level is  <b>dane</b>,  or
+              <b>dane-only</b>  the  match  names are ignored, and <b>host-</b>
+              <b>name, nexthop</b> strategies are used.
+
+<b>ENVIRONMENT</b>
+       <b>MAIL_CONFIG</b>
+              Read configuration parameters  from  a  non-default
+              location.
+
+       <b>MAIL_VERBOSE</b>
+              Same as <b>-v</b> option.
+
+<b>SEE ALSO</b>
+       <a href="smtp-source.1.html">smtp-source(1)</a>, SMTP/LMTP message source
+       <a href="smtp-sink.1.html">smtp-sink(1)</a>, SMTP/LMTP message dump
+
+<b>README FILES</b>
+       <a href="TLS_README.html">TLS_README</a>, Postfix STARTTLS howto
+
+<b>LICENSE</b>
+       The Secure Mailer license must be  distributed  with  this
+       software.
+
+<b>AUTHOR(S)</b>
+       Wietse Venema
+       IBM T.J. Watson Research
+       P.O. Box 704
+       Yorktown Heights, NY 10598, USA
+
+       Viktor Dukhovni
+
+                                                             POSTTLS-FINGER(1)
+</pre> </body> </html>
index 20bed174b8025922ef4381986cb8406163d8a2e3..631eeb74fe5df158c6b29537203d859dc0d7b2e2 100644 (file)
@@ -587,6 +587,27 @@ SMTP(8)                                                                SMTP(8)
               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>smtp_tls_dane_notfound_tlsa_level (may)</b>
+              The "degraded" security level when the "dane" secu-
+              rity level is specified, but no validated DANE TLSA
+              records are published.
+
+       <b>smtp_tls_dane_unusable_tlsa_level (encrypt)</b>
+              The "degraded" security level when the "dane" secu-
+              rity   level  is  specified,  validated  DANE  TLSA
+              records are present, but none are usable.
+
+       <b><a href="postconf.5.html#tls_dane_trust_anchor_digest_enable">tls_dane_trust_anchor_digest_enable</a>   (trust-anchor-asser-</b>
+       <b>tion)</b>
+              <a href="http://tools.ietf.org/html/rfc6698">RFC 6698</a> trust-anchor digest support in the Postfix
+              TLS library.
+
 <b>OBSOLETE STARTTLS CONTROLS</b>
        The  following configuration parameters exist for compati-
        bility with Postfix versions before 2.3. Support for these
index b879d1418b91c10cb8c05614d2b4fef82dc0e10d..4b87f2212dafa247093d4eab3e08b98ca6b551b2 100644 (file)
@@ -102,11 +102,6 @@ SMTPD(8)                                                              SMTPD(8)
 
        Available in Postfix version 2.1 and later:
 
-       <b><a href="postconf.5.html#resolve_null_domain">resolve_null_domain</a> (no)</b>
-              Resolve an address that ends in the "@" null domain
-              as if the local hostname were specified, instead of
-              rejecting the address as invalid.
-
        <b><a href="postconf.5.html#smtpd_reject_unlisted_sender">smtpd_reject_unlisted_sender</a> (no)</b>
               Request  that  the Postfix SMTP server rejects mail
               from  unknown  sender  addresses,  even   when   no
index e47d39e0ecb0873a36d511cf0ff702cf94e1adb0..e417e9d082171f0ce7a1d4b6927cd8ea2299ace8 100644 (file)
@@ -97,11 +97,15 @@ TRIVIAL-REWRITE(8)                                          TRIVIAL-REWRITE(8)
               Resolve  a recipient address safely instead of cor-
               rectly, by looking inside quotes.
 
+       Available with Postfix version 2.1 and later:
+
        <b><a href="postconf.5.html#resolve_null_domain">resolve_null_domain</a> (no)</b>
               Resolve an address that ends in the "@" null domain
               as if the local hostname were specified, instead of
               rejecting the address as invalid.
 
+       Available with Postfix version 2.3 and later:
+
        <b><a href="postconf.5.html#resolve_numeric_domain">resolve_numeric_domain</a> (no)</b>
               Resolve  "user@ipaddress"  as   "user@[ipaddress]",
               instead of rejecting the address as invalid.
index c8a80e53bcdb12aae7746c918c6a888a8fed4f57..e096a364ed3f12c3a5a88812e29431147fbdd416 100644 (file)
@@ -22,7 +22,7 @@ CONFIG        = man5/access.5 man5/aliases.5 man5/canonical.5 man5/relocated.5 \
        man5/generic.5 man5/bounce.5 man5/postfix-wrapper.5 \
        man5/sqlite_table.5
 TOOLS  = man1/smtp-sink.1 man1/smtp-source.1 man1/qmqp-sink.1 \
-       man1/qmqp-source.1 man1/qshape.1
+       man1/qmqp-source.1 man1/qshape.1 man1/posttls-finger.1
 
 update:        $(DAEMONS) $(COMMANDS) $(CONFIG) $(TOOLS)
 
@@ -322,6 +322,11 @@ man1/smtp-source.1: ../src/smtpstone/smtp-source.c
            (cmp -s junk $? || mv junk $?) && rm -f junk
        ../mantools/srctoman $? >$@
 
+man1/posttls-finger.1: ../src/posttls-finger/posttls-finger.c
+       ../mantools/fixman ../proto/postconf.proto $? >junk && \
+           (cmp -s junk $? || mv junk $?) && rm -f junk
+       ../mantools/srctoman $? >$@
+
 man5/tcp_table.5: ../proto/tcp_table
        ../mantools/srctoman - $? >$@
 
diff --git a/postfix/man/man1/posttls-finger.1 b/postfix/man/man1/posttls-finger.1
new file mode 100644 (file)
index 0000000..f418e54
--- /dev/null
@@ -0,0 +1,297 @@
+.TH POSTTLS-FINGER 1 
+.ad
+.fi
+.SH NAME
+posttls-finger
+\-
+Probe the TLS properties of an ESMTP or LMTP server.
+.SH "SYNOPSIS"
+.na
+.nf
+\fBposttls-finger\fR [\fIoptions\fR] [\fBinet:\fR]\fIdomain\fR[:\fIport\fR] [\fImatch ...\fR]
+.br
+\fBposttls-finger\fR -S [\fIoptions\fR] \fBunix:\fIpathname\fR [\fImatch ...\fR]
+.SH DESCRIPTION
+.ad
+.fi
+\fBposttls-finger\fR(1) connects to the specified destination
+and reports TLS-related information about the server. With SMTP, the
+destination is a domainname; with LMTP it is either a domainname
+prefixed with \fBinet:\fR or a pathname prefixed with \fBunix:\fR.  If
+Postfix is built without TLS support, the resulting posttls-finger
+program has very limited functionality, and only the \fB-a\fR, \fB-c\fR,
+\fB-h\fR, \fB-o\fR, \fB-S\fR, \fB-t\fR, \fB-T\fR and \fB-v\fR options
+are available.
+
+Note: this is an unsupported test program. No attempt is made
+to maintain compatibility between successive versions.
+
+For SMTP servers that don't support ESMTP, only the greeting banner
+and the negative EHLO response are reported. Otherwise, the reported
+EHLO response details further server capabilities.
+
+If TLS support is enabled when \fBposttls-finger\fR(1) is compiled, and
+the server supports \fBSTARTTLS\fR, a TLS handshake is attempted.
+
+If DNSSEC support is available, the connection TLS security level
+(\fB-l\fR option) defaults to \fBdane\fR; see TLS_README for
+details. Otherwise, it defaults to \fBsecure\fR.  This setting
+determines the certificate matching policy.
+
+If TLS negotiation succeeds, the TLS protocol and cipher details are
+reported. The server certificate is then verified in accordance with
+the policy at the chosen (or default) security level.  With public
+CA-based trust, when the \fB-L\fR option includes \fBcertmatch\fR,
+(true by default) name matching is performed even if the certificate
+chain is not trusted.  This logs the names found in the remote SMTP
+server certificate and which if any would match, were the certificate
+chain trusted.
+
+Note: \fBposttls-finger\fR(1) does not perform any table lookups, so
+the TLS policy table and obsolete per-site tables are not consulted.
+It does not communicate with the \fBtlsmgr\fR(8) daemon (or any other
+Postfix daemons); its TLS session cache is held in private memory, and
+disappears when the process exits.
+
+With the \fB-r \fIdelay\fR option, if the server assigns a TLS
+session id, the TLS session is cached. The connection is then closed
+and re-opened after the specified delay, and \fBposttls-finger\fR(1)
+then reports whether the cached TLS session was re-used.
+
+When the destination is a load-balancer, it may be distributing
+load between multiple server caches. Typically, each server returns
+its unique name in its EHLO response. If, upon reconnecting with
+\fB-r\fR, a new server name is detected, another session is cached
+for the new server, and the reconnect is repeated up to a maximum
+number of times (default 5) that can be specified via the \fB-m\fR
+option.
+
+The choice of SMTP or LMTP (\fB-S\fR option) determines the syntax of
+the destination argument. With SMTP, one can specify a service on a
+non-default port as \fIhost\fR:\fIservice\fR, and disable MX (mail
+exchanger) DNS lookups with [\fIhost\fR] or [\fIhost\fR]:\fIport\fR.
+The [] form is required when you specify an IP address instead of a
+hostname.  An IPv6 address takes the form [\fBipv6:\fIaddress\fR].
+The default port for SMTP is taken from the \fBsmtp/tcp\fR entry in
+/etc/services, defaulting to 25 if the entry is not found.
+
+With LMTP, specify \fBunix:\fIpathname\fR to connect to a local server
+listening on a unix-domain socket bound to the specified pathname;
+otherwise, specify an optional \fBinet:\fR prefix followed by a
+\fIdomain\fR and an optional port, with the same syntax as for
+SMTP. The default TCP port for LMTP is 24.
+
+Arguments:
+.IP "\fB-a\fR \fIfamily\fR (default: \fBany\fR)"
+Address family preference: \fBipv4\fR, \fBipv6\fR or \fBany\fR.  When
+using \fBany\fR, posttls-finger will randomly select one of the two as
+the more preferred, and exhaust all MX preferences for the first
+address family before trying any addresses for the other.
+.IP "\fB-A\fR \fItrust-anchor.pem\fR (default: none)"
+A list of PEM trust-anchor files that overrides CAfile and CApath
+trust chain verification.  Specify the option multiple times to
+specify multiple files.  See the main.cf documentation for
+smtp_tls_trust_anchor_file for details.
+.IP "\fB-c\fR"
+Disable SMTP chat logging; only TLS-related information is logged.
+.IP "\fB-C\fR"
+Print the remote SMTP server certificate trust chain in PEM format.
+The issuer DN, subject DN, certificate and public key fingerprints
+(see \fB-d \fImdalg\fR option below) are printed above each PEM
+certificate block.  If you specify \fB-F \fICAfile\fR or
+\fB-P \fICApath\fR, the OpenSSL library may augment the chain with
+missing issuer certificates.  To see the actual chain sent by the
+remote SMTP server leave \fICAfile\fR and \fICApath\fR unset.
+.IP "\fB-d \fImdalg\fR (default: \fBsha1\fR)"
+The message digest algorithm to use for reporting remote SMTP server
+fingerprints and matching against user provided certificate
+fingerprints (with DANE TLSA records the algorithm is specified
+in the DNS).
+.IP "\fB-F \fICAfile.pem\fR (default: none)"
+The PEM formatted CAfile for remote SMTP server certificate
+verification.  By default no CAfile is used and no public CAs
+are trusted.
+.IP "\fB-g \fIgrade\fR (default: medium)"
+The minimum TLS cipher grade used by posttls-finger.  See
+smtp_tls_mandatory_ciphers for details.
+.IP "\fB-h \fIhost_lookup\fR (default: \fBdns\fR)"
+The hostname lookup methods used for the connection.  See the
+documentation of smtp_host_lookup for syntax and semantics.
+.IP "\fB-l \fIlevel\fR (default: \fBdane\fR or \fBsecure\fR)"
+The security level for the connection, default \fBdane\fR or
+\fBsecure\fR depending on whether DNSSEC is available.  For syntax
+and semantics, see the documentation of smtp_tls_security_level.
+When \fBdane\fR or \fBdane-only\fR is supported and selected, if no
+TLSA records are found, or all the records found are unusable, the
+\fIsecure\fR level will be used instead.  The \fBfingerprint\fR
+security level allows you to test certificate or public-key
+fingerprint matches before you deploy them in the policy table.
+.IP
+Note, since \fBposttls-finger\fR does not actually deliver any email,
+the \fBnone\fR, \fBmay\fR and \fBencrypt\fR security levels are not
+very useful.  Since \fBmay\fR and \fBencrypt\fR don't require peer
+certificates, they will often negotiate anonymous TLS ciphersuites,
+so you won't learn much about the remote SMTP server's certificates
+at these levels if it also supports anonymous TLS (though you may
+learn that the server supports anonymous TLS).
+.IP "\fB-L \fIlogopts\fR (default: \fBroutine,certmatch\fR)"
+Fine-grained TLS logging options. To tune the TLS features logged
+during the TLS handshake, specify one or more of:
+.RS
+.IP "\fB0, none\fR"
+These yield no TLS logging; you'll generally want more, but this
+is handy if you just want the trust chain:
+.RS
+.ad
+.nf
+$ posttls-finger -cC -L none destination
+.fi
+.RE
+.IP "\fB1, routine, summary\fR"
+These synonymous values yield a normal one-line summary of the TLS
+connection.
+.IP "\fB2, debug\fR"
+These synonymous values combine routine, ssl-debug, cache and verbose.
+.IP "\fB3, ssl-expert\fR"
+These synonymous values combine debug with ssl-handshake-packet-dump.
+For experts only.
+.IP "\fB4, ssl-developer\fR"
+These synonymous values combine ssl-expert with ssl-session-packet-dump.
+For experts only, and in most cases, use wireshark instead.
+.IP "\fBssl-debug\fR"
+Turn on OpenSSL logging of the progress of the SSL handshake.
+.IP "\fBssl-handshake-packet-dump\fR"
+Log hexadecimal packet dumps of the SSL handshake; for experts only.
+.IP "\fBssl-session-packet-dump\fR"
+Log hexadecimal packet dumps of the entire SSL session; only useful
+to those who can debug SSL protocol problems from hex dumps.
+.IP "\fBuntrusted\fR"
+Logs trust chain verification problems.  This is turned on
+automatically at security levels that use peer names signed
+by certificate authorities to validate certificates.  So while
+this setting is recognized, you should never need to set it
+explicitly.
+.IP "\fBpeercert\fR"
+This logs a one line summary of the remote SMTP server certificate
+subject, issuer, and fingerprints.
+.IP "\fBcertmatch\fR"
+This logs remote SMTP server certificate matching, showing the CN
+and each subjectAltName and which name matched.  With DANE, logs
+matching of TLSA record trust-anchor and end-entity certificates.
+.IP "\fBcache\fR"
+This logs session cache operations, showing whether session caching
+is effective with the remote SMTP server.  Automatically used when
+reconnecting with the \fB-r\fR option; rarely needs to be set
+explicitly.
+.IP "\fBverbose\fR"
+Enables verbose logging in the Postfix TLS driver; includes all of
+peercert..cache and more.
+.RE
+.IP
+The default is \fBroutine,certmatch\fR. After a reconnect,
+\fBpeercert\fR, \fBcertmatch\fR and \fBverbose\fR are automatically
+disabled while \fBcache\fR and \fBsummary\fR are enabled.
+.IP "\fB-m \fIcount\fR (default: \fB5\fR)"
+When the \fB-r \fIdelay\fR option is specified, the \fB-m\fR option
+determines the maximum number of reconnect attempts to use with
+a server behind a load-balacer, to see whether connection caching
+is likely to be effective for this destination.  Some MTAs
+don't expose the underlying server identity in their EHLO
+response; with these servers there will never be more than
+1 reconnection attempt.
+.IP "\fB-o \fIname=value\fR"
+Specify zero or more times to override the value of the main.cf
+parameter \fIname\fR with \fIvalue\fR.  Possible use-cases include
+overriding the values of TLS library parameters, or "myhostname" to
+configure the SMTP EHLO name sent to the remote server.
+.IP "\fB-p \fIprotocols\fR (default: !SSLv2)"
+List of TLS protocols that posttls-finger will exclude or include.  See
+smtp_tls_mandatory_protocols for details.
+.IP "\fB-P \fICApath/\fR (default: none)"
+The OpenSSL CApath/ directory (indexed via c_rehash(1)) for remote
+SMTP server certificate verification.  By default no CApath is used
+and no public CAs are trusted.
+.IP "\fB-r \fIdelay\fR"
+With a cachable TLS session, disconnect and reconnect after \fIdelay\fR
+seconds. Report whether the session is re-used. Retry if a new server
+is encountered, up to 5 times or as specified with the \fB-m\fR option.
+By default reconnection is disabled, specify a positive delay to
+enable this behavior.
+.IP "\fB-S\fR"
+Disable SMTP; that is, connect to an LMTP server. The default port for
+LMTP over TCP is 24.  Alternative ports can specified by appending
+"\fI:servicename\fR" or ":\fIportnumber\fR" to the destination
+argument.
+.IP "\fB-t \fItimeout\fR (default: \fB5\fR)"
+The TCP connection timeout to use.  This is also the timeout for
+reading the remote server's 220 banner.
+.IP "\fB-T \fItimeout\fR (default: \fB30\fR)"
+The SMTP/LMTP command timeout for EHLO/LHLO, STARTTLS and QUIT.
+.IP "\fB-v\fR"
+Enable verose Postfix logging.  Specify more than once to increase
+the level of verbose logging.
+.IP "[\fBinet:\fR]\fIdomain\fR[:\fIport\fR]"
+Connect via TCP to domain \fIdomain\fR, port \fIport\fR. The default
+port is \fBsmtp\fR (or 24 with LMTP).  With SMTP an MX lookup is
+performed to resolve the domain to a host, unless the domain is
+enclosed in \fB[]\fR.  If you want to connect to a specific MX host,
+for instance \fImx1.example.com\fR, specify [\fImx1.example.com\fR]
+as the destination and \fIexample.com\fR as a \fBmatch\fR argument.
+When using DNS, the destination domain is assumed fully qualified
+and no default domain or search suffixes are applied; you must use
+fully-qualified names or also enable \fBnative\fR host lookups
+(these don't support \fBdane\fR or \fBdane-only\fR as no DNSSEC
+validation information is available via \fBnative\fR lookups).
+.IP "\fBunix:\fIpathname\fR"
+Connect to the UNIX-domain socket at \fIpathname\fR. LMTP only.
+.IP "\fBmatch ...\fR"
+With no match arguments specified, certificate peername matching uses
+the compiled-in default strategies for each security level.  If you
+specify one or more arguments, these will be used as the list of
+certificate or public-key digests to match for the \fBfingerprint\fR
+level, or as the list of DNS names to match in the certificate at the
+\fBverify\fR and \fBsecure\fR levels.  If the security level is
+\fBdane\fR, or \fBdane-only\fR the match names are ignored, and
+\fBhostname, nexthop\fR strategies are used.
+.ad
+.fi
+.SH "ENVIRONMENT"
+.na
+.nf
+.ad
+.fi
+.IP \fBMAIL_CONFIG\fR
+Read configuration parameters from a non-default location.
+.IP \fBMAIL_VERBOSE\fR
+Same as \fB-v\fR option.
+.SH "SEE ALSO"
+.na
+.nf
+smtp-source(1), SMTP/LMTP message source
+smtp-sink(1), SMTP/LMTP message dump
+
+.SH "README FILES"
+.na
+.nf
+.ad
+.fi
+Use "\fBpostconf readme_directory\fR" or "\fBpostconf
+html_directory\fR" to locate this information.
+.na
+.nf
+TLS_README, Postfix STARTTLS howto
+.SH "LICENSE"
+.na
+.nf
+.ad
+.fi
+The Secure Mailer license must be distributed with this software.
+.SH "AUTHOR(S)"
+.na
+.nf
+Wietse Venema
+IBM T.J. Watson Research
+P.O. Box 704
+Yorktown Heights, NY 10598, USA
+
+Viktor Dukhovni
index f25ab45cd791caf47645ed5f41a1cade24a6d92e..5619b2b09da71132d47f13217ebefbd3de57bed1 100644 (file)
@@ -43,6 +43,9 @@ An IPv4 network address is a sequence of four decimal octets
 separated by ".", and an IPv6 network address is a sequence
 of three to eight hexadecimal octet pairs separated by ":".
 
+The \fInetwork_mask\fR is the number of high-order bits in
+the \fInetwork_address\fR that the search string must match.
+
 Before comparisons are made, lookup keys and table entries
 are converted from string to binary. Therefore table entries
 will be matched regardless of redundant zero characters.
index 24013f4a6134dbdb233d91512890d22f8bc25fdb..f7a9d7a0e70d7a30f6efc3a7e5d7f5a9985170e6 100644 (file)
@@ -2822,6 +2822,11 @@ The LMTP-specific version of the smtp_tls_session_cache_timeout
 configuration parameter.  See there for details.
 .PP
 This feature is available in Postfix 2.3 and later.
+.SH lmtp_tls_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.
@@ -6016,10 +6021,10 @@ and "inet:host:port" destinations, as LMTP hostnames are never
 subject to MX lookups.
 .PP
 The "dnssec" setting is recommended only if you plan to use the
-"dane" TLS security
+dane or dane-only TLS security
 level, otherwise enabling DNSSEC support in Postfix offers no
-additional security. Postfix DNSSEC support relies on an up-stream
-recursive nameserver that validates DNSSEC signatures. Such a DNS
+additional security.  Postfix DNSSEC support relies on an upstream
+recursive nameserver that validates DNSSEC signatures.  Such a DNS
 server will always filter out forged DNS responses, even when Postfix
 itself is not configured to use DNSSEC.
 .PP
@@ -7185,6 +7190,18 @@ whitespace, commas or colons. In the policy table "protocols" attribute
 empty value means allow all protocols. The valid protocol names, (see
 \\fBfBSSL_get_version\fR(3)\fR), are "SSLv2", "SSLv3" and "TLSv1".
 .PP
+At the dane and
+dane-only security
+levels, when usable TLSA records are obtained for the remote SMTP
+server, the Postfix SMTP client is obligated to include the SNI TLS
+extension in its SSL client hello message.  This may help the remote
+SMTP server live up to its promise to provide a certificate that
+matches its TLSA records.  Since TLS extensions require TLS 1.0 or
+later, the Postfix SMTP client must disable SSLv2 and SSLv3 when
+SNI is required.  If you use "dane" or "dane-only" do not disable
+TLSv1, except perhaps via the policy table for destinations which
+you are sure will support TLSv1.1 or TLSv1.2.
+.PP
 Note: As of OpenSSL 1.0.1 two new protocols are defined, "TLSv1.1"
 and "TLSv1.2". If an older Postfix version is linked against OpenSSL
 1.0.1 or later, these, or any other new protocol versions, are
@@ -7363,6 +7380,31 @@ smtp_tls_mandatory_exclude_ciphers parameter. In the policy table,
 multiple protocols or excluded ciphers must be separated by colons,
 as attribute values may not contain whitespace or commas.
 .br
+.IP "\fBdane\fR"
+Opportunistic DANE TLS.  The TLS policy for the destination is
+obtained via TLSA records in DNSSEC.  If no TLSA records are found,
+the effective security level used is may.  If TLSA records are
+found, but none are usable, the effective security level is encrypt.  When usable
+TLSA records are obtained for the remote SMTP server, SSLv2 and
+SSLv3 are automatically disabled (see smtp_tls_mandatory_protocols),
+and the server certificate must match the TLSA records.  The
+tls_dane_trust_anchor_digest_enable parameter controls optional
+support for trust-anchor digest TLSA records.  RFC 6698 (DANE) TLS
+authentication and DNSSEC support is available with Postfix 2.11
+and later.
+.br
+.IP "\fBdane-only\fR"
+Mandatory DANE TLS.  The TLS policy for the destination is
+obtained via TLSA records in DNSSEC.  If no TLSA records are found,
+or none are usable, no connection is made to the server.  When
+usable TLSA records are obtained for the remote SMTP server, SSLv2
+and SSLv3 are automatically disabled (see smtp_tls_mandatory_protocols),
+and the server certificate must match the TLSA records.  The
+tls_dane_trust_anchor_digest_enable parameter controls optional
+support for trust-anchor digest TLSA records.  RFC 6698 (DANE) TLS
+authentication and DNSSEC support is available with Postfix 2.11
+and later.
+.br
 .IP "\fBfingerprint\fR"
 Certificate fingerprint
 verification. Available with Postfix 2.5 and later. At this security
@@ -7548,7 +7590,7 @@ parameters smtp_use_tls, smtp_enforce_tls, and smtp_tls_enforce_peername.
 .PP
 Specify one of the following security levels:
 .IP "\fBnone\fR"
-TLS will not be used unless enabled for specific
+No TLS. TLS will not be used unless enabled for specific
 destinations via smtp_tls_policy_maps.
 .br
 .IP "\fBmay\fR"
@@ -7575,16 +7617,50 @@ cipher grade which the administrator considers secure enough for
 mandatory encrypted sessions. This security level is not an appropriate
 default for systems delivering mail to the Internet.
 .br
+.IP "\fBdane\fR"
+Opportunistic DANE TLS.  At this security level, the TLS policy
+for the destination is obtained via DNSSEC.  For TLSA policy to be
+in effect, the destination domain's containing DNS zone must be
+signed and the Postfix SMTP client's operating system must be
+configured to send its DNS queries to a recursive DNS nameserver
+that is able to validate the signed records.  Each MX host's DNS
+zone should also be signed, and should publish DANE TLSA (RFC 6698)
+records that specify how that MX host's TLS certificate is to be
+verified.  TLSA records do not preempt the normal SMTP MX host
+selection algorithm, if some MX hosts support TLSA and others do
+not, TLS security will vary from delivery to delivery.  It is up
+to the domain owner to configure their MX hosts and their DNS
+sensibly.  To configure the Postfix SMTP client for DNSSEC lookups
+see the documentation for the smtp_dns_support_level main.cf
+parameter.  When TLSA records are not found or are all unusable the
+effective security level is "may" or "encrypt" respectively.  For
+purposes of protocol and cipher selection, the "dane" security level
+is treated like a "mandatory" TLS security level, and weak ciphers
+and protocols are disabled.  SSLv2 and SSLv3 are automatically
+disabled when usable TLSA records are obtained for the remote SMTP
+server, see smtp_tls_mandatory_protocols for details.  Since DANE
+authenticates server certificates the "aNULL" cipher-suites are
+transparently excluded at this level, no need to configure this
+manually.  The tls_dane_trust_anchor_digest_enable main.cf parameter
+controls optional support for trust-anchor digest TLSA records.
+RFC 6698 (DANE) TLS authentication is available with Postfix 2.11
+and later.
+.br
+.IP "\fBdane-only\fR"
+Mandatory DANE TLS.  This is just like "dane" above, only DANE
+TLSA authentication is mandatory.  RFC 6698 (DANE) TLS authentication
+is available with Postfix 2.11 and later.
+.br
 .IP "\fBfingerprint\fR"
-Certificate fingerprint
-verification. Available with Postfix 2.5 and later. At this security
-level, there are no trusted certificate authorities. The certificate
-trust chain, expiration date, ... are not checked. Instead, the
-\fBsmtp_tls_fingerprint_cert_match\fR parameter lists the certificate
-fingerprint or public key fingerprint (Postfix 2.9 and later) of
-the valid server certificate. The digest
+Certificate fingerprint verification.
+At this security level, there are no trusted certificate authorities.
+The certificate trust chain, expiration date, etc., are
+not checked. Instead, the \fBsmtp_tls_fingerprint_cert_match\fR
+parameter lists the certificate fingerprint or public key fingerprint
+(Postfix 2.9 and later) of the valid server certificate. The digest
 algorithm used to calculate the fingerprint is selected by the
-\fBsmtp_tls_fingerprint_digest\fR parameter.
+\fBsmtp_tls_fingerprint_digest\fR parameter. Available with Postfix
+2.5 and later.
 .br
 .IP "\fBverify\fR"
 Mandatory TLS verification. At this security
@@ -7732,6 +7808,53 @@ $smtp_tls_session_cache_database, this parameter is implemented in the
 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
+Whether specified in main.cf, or on a per-destination basis,
+the trust-anchor PEM file must be accessible to the Postfix SMTP
+client in the chroot jail if applicable.  The trust-anchor file
+should contain only certificates and public keys, no private key
+material, 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 main.cf parameter supports single-purpose Postfix installations
+that send mail to a fixed set of SMTP peers.  At most sites, if
+trust-anchor files are used at all, they will be specified on a
+per-destination basis via the "tafile" attribute of the "verify"
+and "secure" levels in smtp_tls_policy_maps.
+.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
@@ -10825,6 +10948,45 @@ bytes (equivalent to 256 bits) is sufficient to generate a 128bit
 (or 168bit) session key.
 .PP
 This feature is available in Postfix 2.2 and later.
+.SH tls_dane_trust_anchor_digest_enable (default: trust-anchor-assertion)
+RFC 6698 trust-anchor digest support in the Postfix TLS library.
+Specify zero or more of the following options, separated by comma or
+whitespace.  Option names are case-insensitive.
+.IP "\fBca-constraint\fR"
+Enable support for RFC 6698 (DANE TLSA) DNS records that
+contain digests of trust-anchors with certificate usage "0".
+These are often public root CAs, and server operators may
+expect that clients will have the corresponding root certificate
+in their CAfile or CApath.  Most SSL/TLS servers do not send public
+root CA certificate in their certificate chain, so if Postfix does
+not have the CA certificate locally, there is no way to associate
+the digest with the trust chain from the server.  These TLSA records
+are fragile, and only work when you have a complete (whatever that
+means) set of public CAs in your CAfile or CApath.  Enable this
+with caution if at all.
+.br
+.IP "\fBtrust-anchor-assertion\fR"
+Enable support for RFC 6698 (DANE TLSA) DNS records that contain
+digests of trust-anchors with certificate usage "2".  In this case
+the certificate usage logically requires the server administrator
+to configure the server to include the trust-anchor certificate in
+the server's SSL certificate chain.  These TLSA records are less
+fragile than "ca-constraint" TLSA records, and are enabled by
+default.  Having a "complete" CAfile or CApath does not help in
+this case, you are at the mercy of the remote server administrator's
+competence.
+.br
+.br
+.PP
+At the dane
+security level, when a TLSA RRset includes only unusable associations,
+the Postfix SMTP client will automatically switch the connection
+to the encrypt
+security level.  At the dane-only security level,
+the server in question is skipped and delivery is deferred if no
+secure servers are found.
+.PP
+This feature is available in Postfix 2.11 and later.
 .SH tls_disable_workarounds (default: see "postconf -d" output)
 List or bit-mask of OpenSSL bug work-arounds to disable.
 .PP
@@ -11087,6 +11249,97 @@ Note: on OpenBSD systems specify /dev/arandom when /dev/urandom
 gives timeout errors.
 .PP
 This feature is available in Postfix 2.2 and later.
+.SH tls_ssl_options (default: empty)
+List or bit-mask of OpenSSL options to enable.
+.PP
+The OpenSSL toolkit provides a set of options that applications
+can enable to tune the OpenSSL behavior.  Some of these work around
+bugs in other implementations and are on by default.  You can use
+the tls_disable_workarounds parameter to selectively disable some
+or all of the bug work-arounds, making OpenSSL more strict at the
+cost of non-interoperability with SSL clients or servers that exhibit
+the bugs.
+.PP
+Other options are off by default, and typically enable or disable
+features rather than bug work-arounds.  These may be turned on (with
+care) via the tls_ssl_options parameter.  The value is a white-space
+or comma separated list of named options chosen from the list below.
+The names are not case-sensitive, you can use lower-case if you
+prefer.  The upper case values below match the corresponding macro
+name in the ssl.h header file with the SSL_OP_ prefix removed.  It
+is possible that your OpenSSL version includes new options added
+after your Postfix source code was last updated, in that case you
+can only enable one of these via the hexadecimal syntax below.
+.PP
+You should only enable features via the hexadecimal mask when
+the need to control the feature is critical (to deal with a new
+vulnerability or a serious interoperability problem).  Postfix DOES
+NOT promise backwards compatible behavior with respect to the mask
+bits.  A feature enabled via the mask in one release may be enabled
+by other means in a later release, and the mask bit will then be
+ignored.  Therefore, use of the hexadecimal mask is only a temporary
+measure until a new Postfix or OpenSSL release provides a better
+solution.
+.PP
+If the value of the parameter is a hexadecimal long integer
+starting with "0x", the options corresponding to the bits specified
+in its value are enabled (see openssl/ssl.h and SSL_CTX_\fBset_options\fR(3)).
+You can only enable options not already controlled by other Postfix
+settings.  For example, you cannot disable protocols or enable
+server cipher preference.  Do not attempt to turn all features by
+specifying 0xFFFFFFFF, this is unlikely to be a good idea.
+.IP "\fBLEGACY_SERVER_CONNECT\fR"
+See SSL_CTX_\fBset_options\fR(3).
+.br
+.IP "\fBNO_TICKET\fR"
+See SSL_CTX_\fBset_options\fR(3).
+.br
+.IP "\fBNO_COMPRESSION\fR"
+Disable SSL compression even if
+supported by the OpenSSL library.  Compression is CPU-intensive,
+and compression before encryption does not always improve security.
+.br
+.br
+.PP
+This feature is available in Postfix 2.11 and later.
+.SH tls_wildcard_matches_multiple_labels (default: yes)
+Match multiple DNS labels with "*" in wildcard certificates.
+.PP
+Some mail service providers prepend the customer domain name
+to a base domain for which they have a wildcard TLS certificate.
+For example, the MX records for example.com hosted by example.net
+may be:
+.sp
+.in +4
+.nf
+.na
+.ft C
+example.com. IN MX 0 example.com.mx1.example.net.
+example.com. IN MX 0 example.com.mx2.example.net.
+.fi
+.ad
+.ft R
+.in -4
+.PP
+and the TLS certificate may be for "*.example.net". The "*"
+then corresponds with multiple labels in the mail server domain
+name.  While multi-label wildcards are not widely supported, and
+are not blessed by any standard, there is little to be gained by
+disallowing their use in this context.
+.PP
+Notes:
+.IP \(bu
+In a certificate name, the "*" is special only when it is
+used as the first label.
+.IP \(bu
+While Postfix (2.11 or later) can match "*" with multiple
+domain name labels, other implementations likely will not.
+.IP \(bu
+Earlier Postfix implementations behave as if
+"tls_wildcard_matches_multiple_labels = no".
+.br
+.PP
+This feature is available in Postfix 2.11 and later.
 .SH tlsproxy_enforce_tls (default: $smtpd_enforce_tls)
 Mandatory TLS: announce STARTTLS support to remote SMTP clients, and
 require that clients use TLS encryption. See smtpd_enforce_tls for
index 57c0c915971935494b70e38a2d30d95788747a51..603bfa80f8a29afeccbc7727ddb6772d5201f45a 100644 (file)
@@ -471,6 +471,20 @@ HELO, MAIL, RCPT, DATA commands to a Postfix SMTP client TLS session.
 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.
+.IP "\fBsmtp_tls_dane_notfound_tlsa_level (may)\fR"
+The "degraded" security level when the "dane" security level
+is specified, but no validated DANE TLSA records are published.
+.IP "\fBsmtp_tls_dane_unusable_tlsa_level (encrypt)\fR"
+The "degraded" security level when the "dane" security level
+is specified, validated DANE TLSA records are present, but none are
+usable.
+.IP "\fBtls_dane_trust_anchor_digest_enable (trust-anchor-assertion)\fR"
+RFC 6698 trust-anchor digest support in the Postfix TLS library.
 .SH "OBSOLETE STARTTLS CONTROLS"
 .na
 .nf
index fd71de239f1d6d1b840916fde42c900a8a6131d6..22e7b6603dc0ecf030a1a2e5c9b2378086b0d610 100644 (file)
@@ -106,10 +106,6 @@ commands are enclosed with <>, and that those addresses do
 not contain RFC 822 style comments or phrases.
 .PP
 Available in Postfix version 2.1 and later:
-.IP "\fBresolve_null_domain (no)\fR"
-Resolve an address that ends in the "@" null domain as if the
-local hostname were specified, instead of rejecting the address as
-invalid.
 .IP "\fBsmtpd_reject_unlisted_sender (no)\fR"
 Request that the Postfix SMTP server rejects mail from unknown
 sender addresses, even when no explicit reject_unlisted_sender
index 134c108be5c0127bc8dd86eb6743ab4571cb66bc..ff0b25a9965d82454178f4abc0d7eddf057d015e 100644 (file)
@@ -101,10 +101,14 @@ The text below provides only a parameter summary. See
 .IP "\fBresolve_dequoted_address (yes)\fR"
 Resolve a recipient address safely instead of correctly, by
 looking inside quotes.
+.PP
+Available with Postfix version 2.1 and later:
 .IP "\fBresolve_null_domain (no)\fR"
 Resolve an address that ends in the "@" null domain as if the
 local hostname were specified, instead of rejecting the address as
 invalid.
+.PP
+Available with Postfix version 2.3 and later:
 .IP "\fBresolve_numeric_domain (no)\fR"
 Resolve "user@ipaddress" as "user@[ipaddress]", instead of
 rejecting the address as invalid.
index 4ae0b9964bc0226380a9ee6f966455a6a9178d26..60d37c6b1af279894dba3bb9adbc27900eeff7ca 100755 (executable)
@@ -242,6 +242,7 @@ while (<>) {
     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;
@@ -638,6 +639,7 @@ while (<>) {
     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;
@@ -709,6 +711,8 @@ while (<>) {
     s;\btls_disable_workarounds\b;<a href="postconf.5.html#tls_disable_workarounds">$&</a>;g;
     s;\btls_append_default_CA\b;<a href="postconf.5.html#tls_append_default_CA">$&</a>;g;
     s;\btls_legacy_public_key_fingerprints\b;<a href="postconf.5.html#tls_legacy_public_key_fingerprint">$&</a>;g;
+    s;\btls_dane_trust_anchor_digest_enable\b;<a href="postconf.5.html#tls_dane_trust_anchor_digest_enable">$&</a>;g;
+    s;\btls_wildcard_matches_multiple_labels\b;<a href="postconf.5.html#tls_wildcard_matches_multiple_labels">$&</a>;g;
  
     s;\bfrozen_delivered_to\b;<a href="postconf.5.html#frozen_delivered_to">$&</a>;g;
     s;\breset_owner_alias\b;<a href="postconf.5.html#reset_owner_alias">$&</a>;g;
@@ -783,6 +787,7 @@ while (<>) {
     s/[<bB>]*postmulti[<\/bB>]*\(1\)/<a href="postmulti.1.html">$&<\/a>/g;
     s/[<bB>]*postqueue[<\/bB>]*\(1\)/<a href="postqueue.1.html">$&<\/a>/g;
     s/[<bB>]*postsuper[<\/bB>]*\(1\)/<a href="postsuper.1.html">$&<\/a>/g;
+    s/[<bB>]*post[-<\/bB>]*\n*[ <bB>]*tls-finger[<\/bB>]*\(1\)/<a href="posttls-finger.1.html">$&<\/a>/g;
     s/[<bB>]*send[-<\/bB>]*\n*[ <bB>]*mail[<\/bB>]*\(1\)/<a href="sendmail.1.html">$&<\/a>/g;
     s/[<bB>]*smtp-[<\/bB>]*\n* *[<bB>]*source[<\/bB>]*\(1\)/<a href="smtp-source.1.html">$&<\/a>/g;
     s/[<bB>]*smtp-[<\/bB>]*\n* *[<bB>]*sink[<\/bB>]*\(1\)/<a href="smtp-sink.1.html">$&<\/a>/g;
index 591cf0d029c17f245c07881a20a65308474f9066..af0c2de95aca68863745e2ba4ab35d413dee91c4 100644 (file)
@@ -156,7 +156,8 @@ SQL, there is no need to execute "postfix reload".  The LDAP, NIS
 or SQL server takes care of read/write access conflicts and gives
 the new data to Postfix once that data is available.  </p>
 
-<li> <p> If you change a regexp: or pcre: file then Postfix may or
+<li> <p> If you change a regexp:, pcre:, cidr: or texthash: file
+then Postfix 
 may not pick up the file changes immediately. This is because a
 Postfix process reads the entire file into memory once and never
 examines the file again.  </p>
index 833e445d6fab72f1a8f890545f4d587e55d6aad3..28c1f42f821ab7e02f84b6562be23c8e40fffdb5 100644 (file)
@@ -163,16 +163,16 @@ private key must not be encrypted, meaning:  the key must be accessible
 without a password.  The certificate and private key may be in the same
 file, in which case the certificate file should be owned by "root" and
 not be readable by any other user. If the key is stored separately,
-this applies to the key file only, and the certificate file may be
-"world-readable". </p>
+this access restriction applies to the key file only, and the
+certificate file may be "world-readable". </p>
 
-<p> Public Internet MX hosts without certificates signed by a "reputable"
-CA must generate, and be prepared to present to most clients, a
-self-signed or private-CA signed certificate. The remote SMTP client
-will generally not be
-able to authenticate the self-signed certificate, but unless the
-client is running Postfix or
-similar software, it will still insist on a server certificate. </p>
+<p> Public Internet MX hosts without certificates signed by a
+well-known public CA must still generate, and be prepared to present
+to most clients, a self-signed or private-CA signed certificate.
+The remote SMTP client will generally not be able to verify the
+self-signed certificate, but unless the client is running Postfix
+or similar software, it will only negotiate TLS ciphersuites that
+require a server certificate.  </p>
 
 <p> For servers that are <b>not</b> public Internet MX hosts, Postfix
 supports configurations with no certificates. This entails the
@@ -188,23 +188,37 @@ SMTP server configurations will not accidentally run with no
 certificates. </p>
 
 <p> RSA, DSA and ECDSA (Postfix &ge; 2.6) certificates are supported.
-Typically you will
-only have RSA certificates issued by a commercial CA. In addition,
-the tools supplied with OpenSSL will by default issue RSA certificates.
-You can configure all three at the same time, in which case the cipher used
-determines which certificate is presented. For Netscape and OpenSSL
-clients without special cipher choices, the RSA certificate is
-preferred. </p>
-
-<p> To enable a remote SMTP client to verify the Postfix SMTP server
-certificate, the issuing CA certificates must be made available to the
-client. You should include the required certificates in the server
-certificate file, the server certificate first, then the issuing
-CA(s) (bottom-up order). </p>
+Most sites only have RSA certificates.  You can configure all three
+at the same time, in which case the ciphersuite negotiated with the
+remote SMTP client determines which certificate is used.  If your
+DNS zone is signed, and you want to publish RFC 6698 TLSA records,
+these must match any of the configured certificates.  Since the
+best practice is to publish "3 1 1" certificate associations, create
+a separate TLSA record for each public-key certificate digest.  </p>
+
+<h4> Creating the server certificate file </h4>
+
+<p> To verify the Postfix SMTP server certificate, the remote SMTP
+client must receive the issuing CA certificates via the TLS handshake
+or via public-key infrastructure. This means that the Postfix server
+public-key certificate file must include the server certificate
+first, then the issuing CA(s) (bottom-up order). The Postfix SMTP
+server certificate must be usable as SSL server certificate and
+hence pass the "<tt>openssl verify -purpose sslserver ...</tt>" test.
+</p>
+
+<p> The examples that follow show how to create a server certificate
+file. We assume that the certificate for "server.example.com" was
+issued by "intermediate CA" which itself has a certificate issued
+by "root CA". </p>
 
-<p> Example: the certificate for "server.example.com" was issued by
-"intermediate CA" which itself has a certificate issued by "root
-CA".  Create the server.pem file with: </p>
+<ul>
+
+<li> <p> With legacy public CA trust verification, you can omit the
+root certificate from the "server.pem" certificate file.  If the
+client trusts the root CA, it will already have a local copy of the
+root CA certificate.  Omitting the root CA certificate reduces the
+size of the server TLS handshake.  </p>
 
 <blockquote>
 <pre>
@@ -212,18 +226,63 @@ CA".  Create the server.pem file with: </p>
 </pre>
 </blockquote>
 
-<p> A Postfix SMTP server certificate supplied here must be usable
-as SSL server certificate and hence pass the "openssl verify -purpose
-sslserver ..." test. </p>
+<li> <p> If you use RFC 6698 TLSA "2 0 1" or "2 1 1" records to
+specify root CA certificate digests, you must include the corresponding
+root CA certificates in the "server.pem" certificate file. </p>
 
-<p> A client that trusts the root CA has a local copy of the root
-CA certificate, so it is not necessary to include the root CA
-certificate here.  Leaving it out of the "server.pem" file reduces
-the overhead of the TLS exchange. </p>
+<blockquote>
+<pre>
+% <b>cat server_cert.pem intermediate_CA.pem root.pem &gt; server.pem</b>
+</pre>
+</blockquote>
+
+<p> Remote SMTP clients will
+be able to use the TLSA record you publish (which only contains the
+certificate digest) only if they have access to the corresponding
+certificate.  Failure to verify certificates per the server's
+published TLSA records will typically cause the SMTP client to defer
+mail delivery.  The foregoing also applies to "2 0 2" and "2 1 2"
+TLSA records or any other digest of a CA certificate, but it is
+expected that SHA256 will be by far the most common digest for TLSA.
+You are <i>strongly</i> urged to likewise include the root CA
+certificate in your server certificate file even if you use "0 0
+1" or "0 1 1" TLSA records.  Some DANE implementations in SMTP
+clients (Postfix among them) may treat RFC 6698 certificate usages
+"0" and "2" interchangeably.  </p>
+
+<p>By default, support for TLSA records that specify certificate
+usage "0" via a digest is disabled in the Postfix SMTP client.  As
+a best practice, publish either "3 0 1" or "3 1 1" TLSA associations
+that specify the SHA256 digest of the server certificate public key
+with the alias-expanded hostname of each STARTTLS capable SMTP
+server.  These continue to work when a certificate is renewed with
+the same public/private key pair.  See the documentation of the
+tls_dane_trust_anchor_digest_enable main.cf parameter for details.
+</p>
+
+</ul>
+
+<p> For instructions on how to compute the digest of a certificate
+or its public key for use in TLSA records, see the documentation of
+the smtpd_tls_fingerprint_digest main.cf parameter. </p>
+
+<p> When a new key or certificate is generated, an additional TLSA
+record with the new digest must be published in advance of the
+actual deployment of the new key or certificate on the server.  You
+must allow sufficient time for any TLSA RRsets with only the old
+digest to expire from DNS caches.  The safest practice is to wait
+until the DNSSEC signature on the previous TLSA RRset expires, and
+only then switch the server to use new keys published in the updated
+TLSA RRset.  Once the new certificate trust chain and private key
+are in effect, the DNS should be updated once again to remove the
+old digest from the TLSA RRset.  </p>
 
 <p> If you want the Postfix SMTP server to accept remote SMTP client
-certificates issued by these CAs, append the root certificate to
-$smtpd_tls_CAfile or install it in the $smtpd_tls_CApath directory. </p>
+certificates issued by one or more root CAs, append the root
+certificate to $smtpd_tls_CAfile or install it in the $smtpd_tls_CApath
+directory. </p>
+
+<h4> Configuring the server certificate and key files </h4>
 
 <p> RSA key and certificate examples: </p>
 
@@ -872,15 +931,48 @@ an OpenSSL library that could be vulnerable. </p>
 <pre>
 /etc/postfix/main.cf:
     tls_disable_workarounds = 0xFFFFFFFF
-    tls_disable_workarounds = CVE-2010-4180, LEGACY_SERVER_CONNECT
+    tls_disable_workarounds = CVE-2010-4180
 </pre>
 </blockquote>
 
-<p> Note: Disabling LEGACY_SERVER_CONNECT is not wise at this
-time, lots of servers are still unpatched and Postfix is <a
-href="http://www.postfix.org/wip.html#tls-renegotiation">not
-significantly vulnerable</a> to the renegotiation issue in the TLS
-protocol. </p>
+<p> With Postfix &ge; 2.11, the tls_ssl_options parameter specifies
+a list or bit-mask of OpenSSL options to enable.  Specify one or
+more of the named options below, or a hexadecimal bitmask of options
+found in the ssl.h file corresponding to the run-time OpenSSL
+library.  While it may be reasonable to turn off all bug workarounds
+(see above), it is not a good idea to attempt to turn on all features.
+</p>
+
+<dl>
+
+<dt><b>LEGACY_SERVER_CONNECT</b></dt> <dd>See SSL_CTX_set_options(3).</dd>
+
+<dt><b>NO_TICKET</b></dt> <dd>See SSL_CTX_set_options(3).</dd>
+
+<dt><b>NO_COMPRESSION</b></dt> <dd>Disable SSL compression even if
+supported by the OpenSSL library.  Compression is CPU-intensive,
+and compression before encryption does not always improve security.  </dd>
+
+</dl>
+
+<p> Example: </p>
+<blockquote>
+<pre>
+/etc/postfix/main.cf:
+    tls_ssl_options = no_ticket, no_compression
+</pre>
+</blockquote>
+
+<p> You should only enable features via the hexadecimal mask when
+the need to control the feature is critical (to deal with a new
+vulnerability or a serious interoperability problem).  Postfix DOES
+NOT promise backwards compatible behavior with respect to the mask
+bits.  A feature enabled via the mask in one release may be enabled
+by other means in a later release, and the mask bit will then be
+ignored.  Therefore, use of the hexadecimal mask is only a temporary
+measure until a new Postfix or OpenSSL release provides a better
+solution.  </p>
 
 <h2> <a name="client_tls">SMTP Client specific settings</a> </h2>
 
@@ -927,6 +1019,10 @@ in more detail in the sections that follow.</p>
 <dd><a href="#client_tls_may">Opportunistic TLS.</a></dd>
 <dt><b>encrypt</b></dt>
 <dd><a href="#client_tls_encrypt">Mandatory TLS encryption.</a>
+<dt><b>dane</b></dt>
+<dd><a href="#client_tls_dane">Opportunistic DANE TLS.</a>
+<dt><b>dane-only</b></dt>
+<dd><a href="#client_tls_dane">Mandatory DANE TLS.</a>
 <dt><b>fingerprint</b></dt>
 <dd><a href="#client_tls_fprint">Certificate fingerprint verification.</a>
 <dt><b>verify</b></dt>
@@ -1111,16 +1207,238 @@ just in case the transport table entries are not specified consistently. </p>
 </pre>
 </blockquote>
 
+<h4><a name="client_tls_dane">DANE TLS authentication.</a> </h4>
+
+<p> The Postfix SMTP client supports two TLS security levels based
+on RFC6698 DANE TLSA records.  The opportunistic "dane" level and
+the mandatory "dane-only" level. </p>
+
+<p> The "dane" level is a stronger form of <a
+href="#client_tls_may">opportunistic</a> TLS that is resistant to
+man in the middle and downgrade attacks when the destination domain
+uses DNSSEC to publish DANE TLSA records for its MX hosts.  If a
+remote SMTP server has usable DANE TLSA records, these will be used
+to authenticate the server.  If TLSA records are published for a
+given remote SMTP server (implying TLS support), but are not usable
+due to unsupported parameters, the Postfix SMTP client will use <a
+href="#client_tls_encrypt">mandatory</a> unauthenticated TLS.
+Otherwise, the Postfix SMTP client behavior is the same as with <a
+href="#client_tls_may">may</a>.  </p>
+
+<p> The "dane-only" level is a form of <a
+href="#client_tls_secure">secure-channel</a> TLS based on the DANE PKI.
+If usable TLSA records are present these are used to authenticate the
+remote SMTP server.  Otherwise, or when server certificate verification
+fails, delivery via the server in question tempfails.  </p>
+
+<p> At both security levels, the TLS policy for the destination is
+obtained via TLSA records validated with DNSSEC.  For TLSA policy
+to be in effect, the destination domain's containing DNS zone must
+be signed and the Postfix SMTP client's operating system must be
+configured to send its DNS queries to a recursive DNS nameserver
+that is able to validate the signed records.  Each MX host's DNS
+zone should also be signed, and should publish DANE TLSA (RFC 6698)
+records that specify how that MX host's TLS certificate is to be
+verified.  TLSA records do not preempt the normal SMTP MX host
+selection algorithm, if some MX hosts support TLSA and others do
+not, TLS security will vary from delivery to delivery.  It is up
+to the domain owner to configure their MX hosts and their DNS
+sensibly.  To configure the Postfix SMTP client for DNSSEC lookups
+see the documentation for the smtp_dns_support_level main.cf
+parameter.  The tls_dane_trust_anchor_digest_enable main.cf parameter
+controls optional support for trust-anchor digest TLSA records.
+</p>
+
+<p> The Postfix SMTP client deviates from RFC 6698 in cases where
+strict adherence is likely to create opportunities for delayed (or
+eventually bounced) email with no substantive security gain.  Most
+notably, it is not expected that smtp_tls_CAfile and smtp_tls_CApath
+can reasonably include every public CA that a remote SMTP server's
+administrator may believe to be well-known.  Therefore, certificate
+usages "0" and "2" from RFC 6698 which are intended to "constrain"
+existing PKI trust, are instead treated as "trust assertions" and
+mapped to "1" and "3" respectively.  That is, with certificate usage
+"0" and "2", Postfix will not require the remote SMTP server's
+certificate to be trusted with respect to any locally defined public
+CAs, it is the domain owner's responsibility to ensure that the
+certificate associations in their TLSA records are appropriate
+to authenticate their SMTP servers.  </p>
+
+<p> In addition, the Postfix SMTP client does not assume that the
+remote SMTP server will necessarily be configured to present the
+certificate of any usage "0" root CA in its TLS protocol <a
+href="https://tools.ietf.org/html/rfc2246#section-7.4.2">certificate_list</a>.
+Therefore, support for usage "0" certificate and public-key digests
+is disabled by default, see tls_dane_trust_anchor_digest_enable for
+details.  While undigested trust-anchor certificates in TLSA records
+are supported, publishing complete certificates in DNS is unwise
+given the resulting excessively large DNS record sizes.  Therefore,
+in its default configuration the Postfix SMTP client essentially
+supports only certificate usages "1", "2" and "3" (with "1" treated as
+though it were "3").  </p>
+
+<p> Finally, the interaction of DANE with MX hostnames that are
+CNAMEs differs from the draft specification in the names used to
+construct the associated <a
+href="https://tools.ietf.org/html/draft-ietf-dane-srv-02#section-3.2">TLSA
+queries</a>.  When the MX hostname is a CNAME, the draft proposal
+to use the unexpanded MX hostname in TLSA lookups is fragile and
+unintuitive.  For this to work, the domain owner has to either
+duplicate the TLSA records of the target (host, service) pair in
+his own DNS or furnish the target host with an additional
+certificate and private key that would be negotiated via SNI.
+Neither of these are robust or easy to manage.  It is far better
+to expand the CNAME recursively to the underlying target hostname
+(keeping track of DNSSEC validation along the way) and to use the
+expanded name to construct the TLSA query and, if appropriate, in
+server certificate name checks.  This is the approach taken by the
+Postfix SMTP client, and if sanity prevails will also be the approach
+taken in the final standard.  </p>
+
+<p> When usable TLSA records are obtained for the remote SMTP server
+the Postfix SMTP client is obligated to include the SNI TLS extension
+in its SSL client hello message.  This may help the remote SMTP
+server live up to its promise to provide a certificate that matches
+its TLSA records.  Since TLS extensions require TLS 1.0 or later,
+the Postfix SMTP client must disable SSLv2 and SSLv3 when SNI is
+required.  If you use "dane" or "dane-only", do not disable TLSv1,
+except perhaps via the policy table for destinations which you are
+sure will support TLSv1.1 or TLSv1.2.  </p>
+
+<p> For purposes of protocol and cipher selection, the "dane"
+security level is treated like a "mandatory" TLS security level,
+and weak ciphers and protocols are disabled.  Since DANE authenticates
+server certificates the "aNULL" cipher-suites are transparently
+excluded at this level, no need to configure this manually.  RFC
+6698 (DANE) TLS authentication is available with Postfix 2.11 and
+later.  </p>
+
+<p> When a DANE TLSA record specifies a trust-anchor (TA) certificate
+(that is an issuing CA), the strategy used to verify the peername
+of the server certificate is unconditionally "nexthop, hostname".
+Both the nexthop domain and the hostname obtained from the
+DNSSEC-validated MX lookup are safe from forgery and the server
+certificate must contain at least one of these names.  </p>
+
+<p> When a DANE TLSA record specifies an end-entity (EE) certificate,
+(that is the actual server certificate), as with the fingerprint
+security level below, no name checks or certificate expiration checks
+are applied. The server certificate (or its public key) either matches
+the DANE record or not.  Server administrators should publish such
+EE records in preference to all other types. </p>
+
+<p> The pre-requisites for DANE support in the Postfix SMTP client are: </p>
+<ul>
+<li> A <i>compile-time</i> OpenSSL library that supports the TLS SNI
+extension and the "sha256" and "sha512" message digests.
+<li> A <i>compile-time</i> DNS resolver library that supports DNSSEC.
+Postfix binaries built on an older system will not support DNSSEC even
+if deployed on a system with an updated resolver library.
+<li> The "smtp_dns_support_level" must be set to "dnssec".
+<li> The "smtp_host_lookup" parameter must include "dns".
+<li> A DNSSEC-validating recursive resolver (see note below).
+</ul>
+<p> The above client pre-requisites do not apply to the Postfix SMTP server.
+It will support DANE provided it supports TLSv1 and its TLSA records are
+published in a DNSSEC signed zone.  To receive DANE secured mail for multiple
+domains, use the same hostname to add the server to each domain's MX
+records.  There are no plans to implement SNI in the Postfix SMTP server.  </p>
+
+<p> Note: The Postfix SMTP client's internal stub DNS resolver is
+DNSSEC-aware, but it does not itself validate DNSSEC records, rather
+it delegates DNSSEC validation to the operating system's configured
+recursive DNS nameserver.  The Postfix DNS client relies on a secure
+channel to the resolver's cache for DNSSEC integrity, but does not
+support TSIG to protect the transmission channel between itself and
+the nameserver.  Therefore, it is strongly recommended (DANE security
+guarantee void otherwise) that each MTA run a local DNSSEC-validating
+recursive resolver ("unbound" from nlnetlabs.nl is a reasonable
+choice) listening on the loopback interface, and that the system
+be configured to use <i>only</i> this local nameserver.  The local
+nameserver may forward queries to an upstream recursive resolver
+on another host if desired.  </p>
+
+<p> Note: When the operating system's recursive nameserver is not
+local, enabling EDNS0 expanded DNS packet sizes and turning on the
+DNSSEC "DO" bit in the DNS request and/or the new DNSSEC-specific
+records returned in the nameserver's replies may cause problems
+with older or buggy firewall and DNS server implementations.
+Therefore, Postfix does not enable DNSSEC by default.  Since MX
+lookups happen before the security level is determined, DANE support
+is disabled for all destinations unless you set "smtp_dns_support_level
+= dnssec".  To enable DNSSEC lookups selectively, define a new
+dedicated transport with a "-o smtp_dns_support_level=dnssec"
+override in master.cf and route selected domains to that transport.
+If DNSSEC proves to be sufficiently reliable for these domains, you
+can enable it for all destinations by changing the global
+smtp_dns_support_level in main.cf.  </p>
+
+<p><b>Example</b>: "dane" security for selected destinations, with
+opportunistic TLS by default.  This is the recommended configuration
+for early adopters. <p>
+<ul>
+<li> <p> The "example.com" destination uses DANE, but if TLSA records
+are not present or are unusable, mail is deferred. </p>
+
+<li> <p> The "example.org" destination uses DANE if possible, but if no TLSA
+records are found opportunistic TLS is used. </p>
+</ul>
+
+<blockquote>
+<pre>
+main.cf:
+    indexed = ${default_database_type}:${config_directory}/
+    #
+    # default: Opportunistic TLS with no DNSSEC lookups.
+    #
+    smtp_tls_security_level = may
+    smtp_dns_support_level = enabled
+    #
+    # Per-destination TLS policy
+    #
+    smtp_tls_policy_maps = ${indexed}tls_policy
+    #
+    # default_transport = smtp, but some destinations are special:
+    #
+    transport_maps = ${indexed}transport
+</pre>
+</blockquote>
+
+<blockquote>
+<pre>
+transport:
+    example.com dane
+    example.org dane
+</pre>
+</blockquote>
+
+<blockquote>
+<pre>
+tls_policy:
+    example.com dane-only
+</pre>
+</blockquote>
+
+<blockquote>
+<pre>
+master.cf:
+    dane       unix  -       -       n       -       -       smtp
+      -o smtp_dns_support_level=dnssec
+      -o smtp_tls_security_level=dane
+</pre>
+</blockquote>
+
 <h4><a name="client_tls_fprint"> Certificate fingerprint verification </a> </h4>
 
-<p> Certificate fingerprint verification is available with Postfix
-2.5 and later. At this security level ("smtp_tls_security_level =
-fingerprint"), no trusted certificate authorities are used or
-required.  The certificate trust chain, expiration date, ... are
-not checked. Instead, the smtp_tls_fingerprint_cert_match parameter
-or the "match" attribute in the <a href="#client_tls_policy">policy</a>
-table lists the remote SMTP server certificate fingerprint or
-public key fingerprint (Postfix 2.9 and later). </p>
+<p> At the <i>fingerprint</i> security level, no trusted certificate
+authorities are used or required.  The certificate trust chain,
+expiration date, etc., are not checked. Instead, the
+smtp_tls_fingerprint_cert_match parameter or the "match" attribute
+in the <a href="#client_tls_policy">policy</a> table lists the
+remote SMTP server certificate fingerprint or public key fingerprint.
+Certificate fingerprint verification is available with Postfix 2.5
+and later, public-key fingerprint support is available with Postfix
+2.9 and later.  </p>
 
 <p> If certificate fingerprints are exchanged securely, this is the
 strongest, and least scalable security level. The administrator needs
@@ -1217,7 +1535,7 @@ to Postfix 2.9.6 or later. </p>
 
 <h4><a name="client_tls_verify"> Mandatory server certificate verification </a> </h4>
 
-<p> At the "verify" TLS security level, messages are sent only over
+<p> At the <i>verify</i> TLS security level, messages are sent only over
 TLS encrypted sessions if the remote SMTP server certificate is
 valid (not
 expired or revoked, and signed by a trusted certificate authority)
@@ -1239,6 +1557,19 @@ DNS names are specified, the certificate CommonName is checked.
 If you want mandatory encryption without server certificate
 verification, see <a href="#client_tls_encrypt">above</a>. </p>
 
+<p> With Postfix &ge; 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
@@ -1300,6 +1631,19 @@ specified, the CommonName is checked. If you want mandatory encryption
 without server certificate verification, see <a
 href="#client_tls_encrypt">above</a>. </p>
 
+<p> With Postfix &ge; 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
@@ -1694,6 +2038,13 @@ hostname(s). When, as is current practice, the client verifies the
 insecurely obtained MX hostname, it is subject to a DNS man-in-the-middle
 attack. </p>
 
+<p> Adoption of DNSSEC and RFC6698 (DANE) may gradually (as domains
+implement DNSSEC and publish TLSA records for their MX hosts) address
+the DNS man-in-the-middle risk and provide scalable key management
+for SMTP with TLS.  Postfix &ge; 2.11 supports the new <a
+href="#client_tls_dane">dane</a> and <a href="#client_tls_dane">dane-only</a>
+security levels that take advantage of these standards.  </p>
+
 <p> If clients instead attempted to verify the recipient domain name,
 an SMTP server for multiple domains would need to
 list all its email domain names in its certificate, and generate a
@@ -1789,6 +2140,31 @@ main.cf smtp_tls_mandatory_ciphers parameter, and the optional
 "exclude" attribute (Postfix &ge; 2.6) overrides the main.cf
 smtp_tls_mandatory_exclude_ciphers parameter.  </dd>
 
+<dt><b>dane</b></dt> <dd><a href="#client_tls_dane">Opportunistic DANE TLS</a>.
+The TLS policy for the destination is obtained via TLSA records in
+DNSSEC.  If no TLSA records are found, the effective security level
+used is <a href="#client_tls_may">may</a>.  If TLSA records are
+found, but none are usable, the effective security level is <a
+href="#client_tls_encrypt">encrypt</a>.  When usable TLSA records
+are obtained for the remote SMTP server, SSLv2 and SSLv3 are
+automatically disabled (see smtp_tls_mandatory_protocols), and the
+server certificate must match the TLSA records.  The
+tls_dane_trust_anchor_digest_enable parameter controls optional
+support for trust-anchor digest TLSA records.  RFC 6698 (DANE) TLS
+authentication and DNSSEC support is available with Postfix 2.11
+and later.  </dd>
+
+<dt><b>dane-only</b></dt> <dd><a href="#client_tls_dane">Mandatory DANE TLS</a>.
+The TLS policy for the destination is obtained via TLSA records in
+DNSSEC.  If no TLSA records are found, or none are usable, no
+connection is made to the server.  When usable TLSA records are
+obtained for the remote SMTP server, SSLv2 and SSLv3 are automatically
+disabled (see smtp_tls_mandatory_protocols), and the server certificate
+must match the TLSA records.  The tls_dane_trust_anchor_digest_enable
+parameter controls optional support for trust-anchor digest TLSA
+records.  RFC 6698 (DANE) TLS authentication and DNSSEC support is
+available with Postfix 2.11 and later.  </dd>
+
 <dt><b>fingerprint</b></dt> <dd><a href="#client_tls_fprint">Certificate
 fingerprint verification.</a> Available with Postfix 2.5 and
 later. At this security level, there are no trusted certificate
@@ -1806,12 +2182,16 @@ digits. </dd>
 
 <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 &ge; 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,
@@ -1819,7 +2199,11 @@ 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).  </dd>
+"match" attribute is specified).  With Postfix &ge; 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>
 
@@ -2084,6 +2468,64 @@ defers delivery if no alternative server is available. </p>
 </pre>
 </blockquote>
 
+<p> With Postfix 2.8 and later, the tls_disable_workarounds parameter
+specifies a list or bit-mask of OpenSSL bug work-arounds to disable. This
+may be necessary if one of the work-arounds enabled by default in
+OpenSSL proves to pose a security risk, or introduces an unexpected
+interoperability issue. Some bug work-arounds known to be problematic
+are disabled in the default value of the parameter when linked with
+an OpenSSL library that could be vulnerable. </p>
+
+<p> Example: </p>
+<blockquote>
+<pre>
+/etc/postfix/main.cf:
+    tls_disable_workarounds = 0xFFFFFFFF
+    tls_disable_workarounds = CVE-2010-4180, LEGACY_SERVER_CONNECT
+</pre>
+</blockquote>
+
+<p> Note: Disabling LEGACY_SERVER_CONNECT is not wise at this
+time, lots of servers are still unpatched and Postfix is <a
+href="http://www.postfix.org/wip.html#tls-renegotiation">not
+significantly vulnerable</a> to the renegotiation issue in the TLS
+protocol. </p>
+
+<p> With Postfix &ge; 2.11, the tls_ssl_options parameter specifies
+a list or bit-mask of OpenSSL options to enable.  Specify one or
+more of the named options below, or a hexadecimal bitmask of options
+found in the ssl.h file corresponding to the run-time OpenSSL
+library.  While it may be reasonable to turn off all bug workarounds
+(see above), it is not a good idea to attempt to turn on all features.
+</p>
+
+<p> A future version of OpenSSL may by default no longer allow
+connections to servers that don't support secure renegotiation.
+Since the exposure for SMTP is minimal, and some SMTP servers may
+remain unpatched, you can add LEGACY_SERVER_CONNECT to the
+options to restore the more permissive default of current OpenSSL
+releases.  </p>
+
+<p> Example: </p>
+
+<blockquote>
+<pre>
+/etc/postfix/main.cf:
+    tls_ssl_options = NO_TICKET, NO_COMPRESSION, LEGACY_SERVER_CONNECT
+</pre>
+</blockquote>
+
+<p> You should only enable features via the hexadecimal mask when
+the need to control the feature is critical (to deal with a new
+vulnerability or a serious interoperability problem).  Postfix DOES
+NOT promise backwards compatible behavior with respect to the mask
+bits.  A feature enabled via the mask in one release may be enabled
+by other means in a later release, and the mask bit will then be
+ignored.  Therefore, use of the hexadecimal mask is only a temporary
+measure until a new Postfix or OpenSSL release provides a better
+solution.  </p>
+
 <h2><a name="tlsmgr_controls"> TLS manager specific settings </a> </h2>
 
 <p> The security of cryptographic software such as TLS depends
index 4a24d05311cb823e6af519695faf3df8ef4a6296..0f1706ad11da3de0ba8c3873d20d3fd04533f3db 100644 (file)
@@ -35,6 +35,9 @@
 #      separated by ".", and an IPv6 network address is a sequence
 #      of three to eight hexadecimal octet pairs separated by ":".
 #
+#      The \fInetwork_mask\fR is the number of high-order bits in
+#      the \fInetwork_address\fR that the search string must match.
+#
 #      Before comparisons are made, lookup keys and table entries
 #      are converted from string to binary. Therefore table entries
 #      will be matched regardless of redundant zero characters.
index 4cbe12e58dd402734ba2d8a4e7b0384694692a11..df5433977646587601faab4b5db04a530b524f51 100644 (file)
@@ -10614,10 +10614,10 @@ security are: </p>
 
 <dl>
 
-<dt><b>none</b></dt>         
+<dt><b><a href="TLS_README.html#client_tls_none">none</a></b></dt>
 <dd>No TLS. No additional attributes are supported at this level. </dd>
 
-<dt><b>may</b></dt>
+<dt><b><a href="TLS_README.html#client_tls_may">may</a></b></dt>
 <dd>Opportunistic TLS. Since sending in the clear is acceptable,
 demanding stronger than default TLS security merely reduces
 inter-operability. The optional "ciphers", "exclude" and "protocols"
@@ -10628,7 +10628,8 @@ handshakes fail, Postfix retries the connection with TLS disabled.
 This allows mail delivery to sites with non-interoperable TLS
 implementations.</dd>
 
-<dt><b>encrypt</b></dt> <dd>Mandatory TLS encryption. At this level
+<dt><b><a href="TLS_README.html#client_tls_encrypt">encrypt</a></b></dt>
+<dd>Mandatory TLS encryption. At this level
 and higher, the optional "protocols" attribute overrides the main.cf
 smtp_tls_mandatory_protocols parameter, the optional "ciphers" attribute
 overrides the main.cf smtp_tls_mandatory_ciphers parameter, and the
@@ -10637,7 +10638,35 @@ smtp_tls_mandatory_exclude_ciphers parameter. In the policy table,
 multiple protocols or excluded ciphers must be separated by colons,
 as attribute values may not contain whitespace or commas. </dd>
 
-<dt><b>fingerprint</b></dt> <dd>Certificate fingerprint
+<dt><b><a href="TLS_README.html#client_tls_dane">dane</a></b></dt>
+<dd>Opportunistic DANE TLS.  The TLS policy for the destination is
+obtained via TLSA records in DNSSEC.  If no TLSA records are found,
+the effective security level used is <a
+href="TLS_README.html#client_tls_may">may</a>.  If TLSA records are
+found, but none are usable, the effective security level is <a
+href="TLS_README.html#client_tls_encrypt">encrypt</a>.  When usable
+TLSA records are obtained for the remote SMTP server, SSLv2 and
+SSLv3 are automatically disabled (see smtp_tls_mandatory_protocols),
+and the server certificate must match the TLSA records.  The
+tls_dane_trust_anchor_digest_enable parameter controls optional
+support for trust-anchor digest TLSA records.  RFC 6698 (DANE) TLS
+authentication and DNSSEC support is available with Postfix 2.11
+and later.  </dd>
+
+<dt><b><a href="TLS_README.html#client_tls_dane">dane-only</a></b></dt>
+<dd>Mandatory DANE TLS.  The TLS policy for the destination is
+obtained via TLSA records in DNSSEC.  If no TLSA records are found,
+or none are usable, no connection is made to the server.  When
+usable TLSA records are obtained for the remote SMTP server, SSLv2
+and SSLv3 are automatically disabled (see smtp_tls_mandatory_protocols),
+and the server certificate must match the TLSA records.  The
+tls_dane_trust_anchor_digest_enable parameter controls optional
+support for trust-anchor digest TLSA records.  RFC 6698 (DANE) TLS
+authentication and DNSSEC support is available with Postfix 2.11
+and later.  </dd>
+
+<dt><b><a href="TLS_README.html#client_tls_fingerprint">fingerprint</a></b></dt>
+<dd>Certificate fingerprint
 verification. Available with Postfix 2.5 and later. At this security
 level, there are no trusted certificate authorities. The certificate
 trust chain, expiration date, ... are not checked. Instead,
@@ -10652,7 +10681,8 @@ match attributes can be employed. The ":" character is not used as a
 delimiter as it occurs between each pair of fingerprint (hexadecimal)
 digits. </dd>
 
-<dt><b>verify</b></dt> <dd>Mandatory TLS verification.  At this security
+<dt><b><a href="TLS_README.html#client_tls_verify">verify</a></b></dt>
+<dd>Mandatory TLS verification.  At this security
 level, DNS MX lookups are trusted to be secure enough, and the name
 verified in the server certificate is usually obtained indirectly via
 unauthenticated DNS MX lookups.  The optional "match" attribute overrides
@@ -10661,7 +10691,8 @@ multiple match patterns and strategies must be separated by colons.
 In practice explicit control over matching is more common with the
 "secure" policy, described below. </dd>
 
-<dt><b>secure</b></dt> <dd>Secure-channel TLS. At this security level, DNS
+<dt><b><a href="TLS_README.html#client_tls_secure">secure</a></b></dt>
+<dd>Secure-channel TLS. At this security level, DNS
 MX lookups, though potentially used to determine the candidate next-hop
 gateway IP addresses, are <b>not</b> trusted to be secure enough for TLS
 peername verification. Instead, the default name verified in the server
@@ -10723,6 +10754,18 @@ whitespace, commas or colons. In the policy table "protocols" attribute
 empty value means allow all protocols. The valid protocol names, (see
 <b>SSL_get_version(3)</b>), are "SSLv2", "SSLv3" and "TLSv1". </p>
 
+<p> At the <a href="TLS_README.html#client_tls_dane">dane</a> and
+<a href="TLS_README.html#client_tls_dane">dane-only</a> security
+levels, when usable TLSA records are obtained for the remote SMTP
+server, the Postfix SMTP client is obligated to include the SNI TLS
+extension in its SSL client hello message.  This may help the remote
+SMTP server live up to its promise to provide a certificate that
+matches its TLSA records.  Since TLS extensions require TLS 1.0 or
+later, the Postfix SMTP client must disable SSLv2 and SSLv3 when
+SNI is required.  If you use "dane" or "dane-only" do not disable
+TLSv1, except perhaps via the policy table for destinations which
+you are sure will support TLSv1.1 or TLSv1.2.  </p>
+
 <p> Note: As of OpenSSL 1.0.1 two new protocols are defined, "TLSv1.1"
 and "TLSv1.2". If an older Postfix version is linked against OpenSSL
 1.0.1 or later, these, or any other new protocol versions, are
@@ -10950,10 +10993,11 @@ parameters smtp_use_tls, smtp_enforce_tls, and smtp_tls_enforce_peername.
 
 <dl>
 
-<dt><b>none</b></dt> <dd> TLS will not be used unless enabled for specific
+<dt><b><a href="TLS_README.html#client_tls_none">none</a></b></dt>
+<dd> No TLS. TLS will not be used unless enabled for specific
 destinations via smtp_tls_policy_maps. </dd>
 
-<dt><b>may</b></dt>
+<dt><b><a href="TLS_README.html#client_tls_may">may</a></b></dt>
 <dd> Opportunistic TLS. Use TLS if this is supported by the remote
 SMTP server, otherwise use plaintext. Since
 sending in the clear is acceptable, demanding stronger than default TLS
@@ -10967,7 +11011,8 @@ When TLS handshakes fail, the connection is retried with TLS disabled.
 This allows mail delivery to sites with non-interoperable TLS
 implementations. </dd>
 
-<dt><b>encrypt</b></dt> <dd>Mandatory TLS encryption. Since a minimum
+<dt><b><a href="TLS_README.html#client_tls_encrypt">encrypt</a></b></dt>
+<dd>Mandatory TLS encryption. Since a minimum
 level of security is intended, it is reasonable to be specific about
 sufficiently secure protocol versions and ciphers. At this security level
 and higher, the main.cf parameters smtp_tls_mandatory_protocols and
@@ -10976,17 +11021,54 @@ cipher grade which the administrator considers secure enough for
 mandatory encrypted sessions. This security level is not an appropriate
 default for systems delivering mail to the Internet. </dd>
 
-<dt><b>fingerprint</b></dt> <dd>Certificate fingerprint
-verification. Available with Postfix 2.5 and later. At this security
-level, there are no trusted certificate authorities. The certificate
-trust chain, expiration date, ... are not checked. Instead, the
-<b>smtp_tls_fingerprint_cert_match</b> parameter lists the certificate
-fingerprint or public key fingerprint (Postfix 2.9 and later) of
-the valid server certificate. The digest
+<dt><b><a href="TLS_README.html#client_tls_dane">dane</a></b></dt>
+<dd>Opportunistic DANE TLS.  At this security level, the TLS policy
+for the destination is obtained via DNSSEC.  For TLSA policy to be
+in effect, the destination domain's containing DNS zone must be
+signed and the Postfix SMTP client's operating system must be
+configured to send its DNS queries to a recursive DNS nameserver
+that is able to validate the signed records.  Each MX host's DNS
+zone should also be signed, and should publish DANE TLSA (RFC 6698)
+records that specify how that MX host's TLS certificate is to be
+verified.  TLSA records do not preempt the normal SMTP MX host
+selection algorithm, if some MX hosts support TLSA and others do
+not, TLS security will vary from delivery to delivery.  It is up
+to the domain owner to configure their MX hosts and their DNS
+sensibly.  To configure the Postfix SMTP client for DNSSEC lookups
+see the documentation for the smtp_dns_support_level main.cf
+parameter.  When TLSA records are not found or are all unusable the
+effective security level is "may" or "encrypt" respectively.  For
+purposes of protocol and cipher selection, the "dane" security level
+is treated like a "mandatory" TLS security level, and weak ciphers
+and protocols are disabled.  SSLv2 and SSLv3 are automatically
+disabled when usable TLSA records are obtained for the remote SMTP
+server, see smtp_tls_mandatory_protocols for details.  Since DANE
+authenticates server certificates the "aNULL" cipher-suites are
+transparently excluded at this level, no need to configure this
+manually.  The tls_dane_trust_anchor_digest_enable main.cf parameter
+controls optional support for trust-anchor digest TLSA records.
+RFC 6698 (DANE) TLS authentication is available with Postfix 2.11
+and later.  </dd>
+
+
+<dt><b><a href="TLS_README.html#client_tls_dane">dane-only</a></b></dt>
+<dd>Mandatory DANE TLS.  This is just like "dane" above, only DANE
+TLSA authentication is mandatory.  RFC 6698 (DANE) TLS authentication
+is available with Postfix 2.11 and later.  </dd>
+
+<dt><b><a href="TLS_README.html#client_tls_fingerprint">fingerprint</a></b></dt>
+<dd>Certificate fingerprint verification.
+At this security level, there are no trusted certificate authorities.
+The certificate trust chain, expiration date, etc., are
+not checked. Instead, the <b>smtp_tls_fingerprint_cert_match</b>
+parameter lists the certificate fingerprint or public key fingerprint
+(Postfix 2.9 and later) of the valid server certificate. The digest
 algorithm used to calculate the fingerprint is selected by the
-<b>smtp_tls_fingerprint_digest</b> parameter. </dd>
+<b>smtp_tls_fingerprint_digest</b> parameter. Available with Postfix
+2.5 and later.  </dd>
 
-<dt><b>verify</b></dt> <dd>Mandatory TLS verification. At this security
+<dt><b><a href="TLS_README.html#client_tls_verify">verify</a></b></dt>
+<dd>Mandatory TLS verification. At this security
 level, DNS MX lookups are trusted to be secure enough, and the name
 verified in the server certificate is usually obtained indirectly
 via unauthenticated DNS MX lookups. The smtp_tls_verify_cert_match
@@ -10995,7 +11077,8 @@ control over matching is more common at the "secure" level, described
 below. This security level is not an appropriate default for systems
 delivering mail to the Internet. </dd>
 
-<dt><b>secure</b></dt> <dd>Secure-channel TLS.  At this security level,
+<dt><b><a href="TLS_README.html#client_tls_secure">secure</a></b></dt>
+<dd>Secure-channel TLS.  At this security level,
 DNS MX lookups, though potentially used to determine the candidate
 next-hop gateway IP addresses, are <b>not</b> trusted to be secure enough
 for TLS peername verification. Instead, the default name verified in
@@ -14971,7 +15054,7 @@ limited to 13 over the lifetime of a daemon process. </p>
 
 <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
@@ -15058,10 +15141,11 @@ and "inet:host:port" destinations, as LMTP hostnames are never
 subject to MX lookups.  </p>
 
 <p>The "dnssec" setting is recommended only if you plan to use the
-"<a href="TLS_README.html#client_tls_dane">dane</a>" TLS security
+<a href="TLS_README.html#client_tls_dane">dane</a> or <a
+href="TLS_README.html#client_tls_dane">dane-only</a> TLS security
 level, otherwise enabling DNSSEC support in Postfix offers no
-additional security. Postfix DNSSEC support relies on an up-stream
-recursive nameserver that validates DNSSEC signatures. Such a DNS
+additional security.  Postfix DNSSEC support relies on an upstream
+recursive nameserver that validates DNSSEC signatures.  Such a DNS
 server will always filter out forged DNS responses, even when Postfix
 itself is not configured to use DNSSEC. </p>
 
@@ -15094,3 +15178,199 @@ RES_USE_DNSSEC and RES_USE_EDNS0 resolver options. </p>
 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>  Whether specified in main.cf, or on a per-destination basis,
+the trust-anchor PEM file must be accessible to the Postfix SMTP
+client in the chroot jail if applicable.  The trust-anchor file
+should contain only certificates and public keys, no private key
+material, 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 main.cf parameter supports single-purpose Postfix installations
+that send mail to a fixed set of SMTP peers.  At most sites, if
+trust-anchor files are used at all, they will be specified on a
+per-destination basis via the "tafile" attribute of the "verify"
+and "secure" levels in smtp_tls_policy_maps.  </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>
+
+%PARAM tls_dane_trust_anchor_digest_enable trust-anchor-assertion
+
+<p> RFC 6698 trust-anchor digest support in the Postfix TLS library.
+Specify zero or more of the following options, separated by comma or
+whitespace.  Option names are case-insensitive.  </p>
+
+<dl>
+
+<dt><b>ca-constraint</b></dt>
+
+<dd> Enable support for RFC 6698 (DANE TLSA) DNS records that
+contain digests of trust-anchors with certificate usage "0".
+These are often public root CAs, and server operators may
+expect that clients will have the corresponding root certificate
+in their CAfile or CApath.  Most SSL/TLS servers do not send public
+root CA certificate in their certificate chain, so if Postfix does
+not have the CA certificate locally, there is no way to associate
+the digest with the trust chain from the server.  These TLSA records
+are fragile, and only work when you have a complete (whatever that
+means) set of public CAs in your CAfile or CApath.  Enable this
+with caution if at all.  </dd>
+
+<dt><b>trust-anchor-assertion</b></dt>
+
+<dd> Enable support for RFC 6698 (DANE TLSA) DNS records that contain
+digests of trust-anchors with certificate usage "2".  In this case
+the certificate usage logically requires the server administrator
+to configure the server to include the trust-anchor certificate in
+the server's SSL certificate chain.  These TLSA records are less
+fragile than "ca-constraint" TLSA records, and are enabled by
+default.  Having a "complete" CAfile or CApath does not help in
+this case, you are at the mercy of the remote server administrator's
+competence.  </dd>
+
+</dl>
+
+<p> At the <a href="TLS_README.html#client_tls_dane">dane</a>
+security level, when a TLSA RRset includes only unusable associations,
+the Postfix SMTP client will automatically switch the connection
+to the <a href="TLS_README.html#client_tls_encrypt">encrypt</a>
+security level.  At the <a
+href="TLS_README.html#client_tls_dane">dane-only</a> security level,
+the server in question is skipped and delivery is deferred if no
+secure servers are found.  </p>
+
+<p> This feature is available in Postfix 2.11 and later.  </p>
+
+%PARAM tls_wildcard_matches_multiple_labels yes
+
+<p> Match multiple DNS labels with "*" in wildcard certificates.
+</p>
+
+<p> Some mail service providers prepend the customer domain name
+to a base domain for which they have a wildcard TLS certificate.
+For example, the MX records for example.com hosted by example.net
+may be: </p>
+
+<blockquote>
+<pre>
+example.com. IN MX 0 example.com.mx1.example.net.
+example.com. IN MX 0 example.com.mx2.example.net.
+</pre>
+</blockquote>
+
+<p> and the TLS certificate may be for "*.example.net". The "*"
+then corresponds with multiple labels in the mail server domain
+name.  While multi-label wildcards are not widely supported, and
+are not blessed by any standard, there is little to be gained by
+disallowing their use in this context.  </p>
+
+<p> Notes: <p>
+
+<ul>
+
+<li> <p> In a certificate name, the "*" is special only when it is
+used as the first label. </p>
+
+<li> <p> While Postfix (2.11 or later) can match "*" with multiple
+domain name labels, other implementations likely will not.  </p>
+
+<li> <p> Earlier Postfix implementations behave as if
+"tls_wildcard_matches_multiple_labels = no". </p>
+
+</ul>
+
+<p> This feature is available in Postfix 2.11 and later.  </p>
+
+%PARAM tls_ssl_options
+
+<p> List or bit-mask of OpenSSL options to enable. </p>
+
+<p> The OpenSSL toolkit provides a set of options that applications
+can enable to tune the OpenSSL behavior.  Some of these work around
+bugs in other implementations and are on by default.  You can use
+the tls_disable_workarounds parameter to selectively disable some
+or all of the bug work-arounds, making OpenSSL more strict at the
+cost of non-interoperability with SSL clients or servers that exhibit
+the bugs.  </p>
+
+<p> Other options are off by default, and typically enable or disable
+features rather than bug work-arounds.  These may be turned on (with
+care) via the tls_ssl_options parameter.  The value is a white-space
+or comma separated list of named options chosen from the list below.
+The names are not case-sensitive, you can use lower-case if you
+prefer.  The upper case values below match the corresponding macro
+name in the ssl.h header file with the SSL_OP_ prefix removed.  It
+is possible that your OpenSSL version includes new options added
+after your Postfix source code was last updated, in that case you
+can only enable one of these via the hexadecimal syntax below.  </p>
+
+<p> You should only enable features via the hexadecimal mask when
+the need to control the feature is critical (to deal with a new
+vulnerability or a serious interoperability problem).  Postfix DOES
+NOT promise backwards compatible behavior with respect to the mask
+bits.  A feature enabled via the mask in one release may be enabled
+by other means in a later release, and the mask bit will then be
+ignored.  Therefore, use of the hexadecimal mask is only a temporary
+measure until a new Postfix or OpenSSL release provides a better
+solution.  </p>
+
+<p> If the value of the parameter is a hexadecimal long integer
+starting with "0x", the options corresponding to the bits specified
+in its value are enabled (see openssl/ssl.h and SSL_CTX_set_options(3)).
+You can only enable options not already controlled by other Postfix
+settings.  For example, you cannot disable protocols or enable
+server cipher preference.  Do not attempt to turn all features by
+specifying 0xFFFFFFFF, this is unlikely to be a good idea.  </p>
+
+<dl>
+
+<dt><b>LEGACY_SERVER_CONNECT</b></dt> <dd>See SSL_CTX_set_options(3).</dd>
+
+<dt><b>NO_TICKET</b></dt> <dd>See SSL_CTX_set_options(3).</dd>
+
+<dt><b>NO_COMPRESSION</b></dt> <dd>Disable SSL compression even if
+supported by the OpenSSL library.  Compression is CPU-intensive,
+and compression before encryption does not always improve security.  </dd>
+
+</dl>
+
+<p> This feature is available in Postfix 2.11 and later.  </p>
+
index 396e2ff021a1f754c3b083a3805f8d795806a6aa..2d1867267e256471b0c8f8f7c3330d5641112449 100644 (file)
@@ -1256,3 +1256,6 @@ uncached
 unzipping
 windowsize
 xpostconf
+TLSA
+tafile
+VPN
index bedbbdddcb123a58a84811ce8c6816f99b0704e5..345495a9b599b10a25fc1da22fbe64c068125866 100644 (file)
@@ -132,7 +132,7 @@ typedef struct DNS_RR {
     unsigned short type;               /* T_A, T_CNAME, etc. */
     unsigned short class;              /* C_IN, etc. */
     unsigned int ttl;                  /* always */
-    unsigned int validated;            /* DNSSEC */
+    unsigned int dnssec_valid;         /* DNSSEC validated */
     unsigned short pref;               /* T_MX only */
     struct DNS_RR *next;               /* linkage */
     size_t  data_len;                  /* actual data size */
index daaaa9e18be43938d0842b0e8a29cde6964d876a..43b50ba16172633ea78e0532132fb2dc30e36eb5 100644 (file)
 typedef struct DNS_REPLY {
     unsigned char *buf;                        /* raw reply data */
     size_t  buf_len;                   /* reply buffer length */
-    int     validated;                 /* DNSSEC AD bit */
+    int     dnssec_valid;              /* DNSSEC AD bit */
     int     query_count;               /* number of queries */
     int     answer_count;              /* number of answers */
     unsigned char *query_start;                /* start of query data */
@@ -281,9 +281,9 @@ static int dns_query(const char *name, int type, int flags,
      * the fly while the reply is being parsed.
      */
 #if RES_USE_DNSSEC != 0
-    reply->validated = (flags & RES_USE_DNSSEC) ? reply_header->ad : 0;
+    reply->dnssec_valid = (flags & RES_USE_DNSSEC) ? reply_header->ad : 0;
 #else
-    reply->validated = 0;
+    reply->dnssec_valid = 0;
 #endif
     reply->end = reply->buf + len;
     reply->query_start = reply->buf + sizeof(HEADER);
@@ -573,7 +573,7 @@ static int dns_get_answer(const char *orig_name, DNS_REPLY *reply, int type,
                if ((status = dns_get_rr(&rr, orig_name, reply, pos, rr_name,
                                         &fixed)) == DNS_OK) {
                    resource_found++;
-                   rr->validated = (reply->validated & *validate_mask);
+                   rr->dnssec_valid = (reply->dnssec_valid & *validate_mask);
                    *rrlist = dns_rr_append(*rrlist, rr);
                } else if (not_found_status != DNS_RETRY)
                    not_found_status = status;
@@ -584,7 +584,7 @@ static int dns_get_answer(const char *orig_name, DNS_REPLY *reply, int type,
            if (cname && c_len > 0)
                if ((status = dns_get_alias(reply, pos, &fixed, cname, c_len)) != DNS_OK)
                    CORRUPT(status);
-           *validate_mask &= reply->validated;
+           *validate_mask &= reply->dnssec_valid;
        }
        pos += fixed.length;
     }
index 8804cfd97756f9b60ab26e70a6fc8bd575a2bacf..4ae4eff6e169f80dc90c99e6654b57afea377099 100644 (file)
@@ -123,7 +123,7 @@ DNS_RR *dns_rr_create(const char *qname, const char *rname,
     rr->type = type;
     rr->class = class;
     rr->ttl = ttl;
-    rr->validated = 0;
+    rr->dnssec_valid = 0;
     rr->pref = pref;
     if (data && data_len > 0)
        memcpy(rr->data, data, data_len);
index da3b3758c2e475cf91a57ad41fedf7e452a1b866..49ed9025bc20406ddf84c1002caf1b517d771a29 100644 (file)
@@ -46,7 +46,7 @@ static void print_rr(DNS_RR *rr)
     size_t  i;
 
     while (rr) {
-       printf("%s: ad: %d, ttl: %9d ", rr->rname, rr->validated, rr->ttl);
+       printf("%s: ad: %d, ttl: %9d ", rr->rname, rr->dnssec_valid, rr->ttl);
        switch (rr->type) {
        case T_A:
 #ifdef T_AAAA
index 274f654b8fba4623acf128056b8ec017943d78ac..a1951ec8a085a9c87e7befa068b001abb94f7da8 100644 (file)
@@ -1440,6 +1440,12 @@ extern char *var_smtp_tls_mand_excl;
 #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"
@@ -3043,6 +3049,10 @@ extern char *var_tls_eecdh_ultra;
 #define DEF_TLS_PREEMPT_CLIST  0
 extern bool var_tls_preempt_clist;
 
+#define VAR_TLS_MULTI_WILDCARD "tls_wildcard_matches_multiple_labels"
+#define DEF_TLS_MULTI_WILDCARD 1
+extern bool var_tls_multi_wildcard;
+
  /* The tweak for CVE-2010-4180 is needed in some versions prior to 1.0.1 */
  /* The tweak for CVE-2005-2969 is needed in some versions prior to 1.0.0 */
 #if defined(USE_TLS) && (OPENSSL_VERSION_NUMBER < 0x1000100fL)
@@ -3059,10 +3069,25 @@ extern bool var_tls_preempt_clist;
 #define DEF_TLS_BUG_TWEAKS     TLS_BUG_TWEAKS
 extern char *var_tls_bug_tweaks;
 
+#define VAR_TLS_SSL_OPTIONS    "tls_ssl_options"
+#define DEF_TLS_SSL_OPTIONS    ""
+extern char *var_tls_ssl_options;
+
 #define VAR_TLS_BC_PKEY_FPRINT "tls_legacy_public_key_fingerprints"
 #define DEF_TLS_BC_PKEY_FPRINT 0
 extern bool var_tls_bc_pkey_fprint;
 
+ /*
+  * External interface for enabling trust-anchor digests, which are risky
+  * when the corresponding certificate is missing from the peer chain (this
+  * can't happend with the leaf certificate).
+  */
+#define TLS_DANE_CC            "ca-constraint"
+#define TLS_DANE_TAA           "trust-anchor-assertion"
+#define VAR_TLS_DANE_TA_DGST   "tls_dane_trust_anchor_digest_enable"
+#define DEF_TLS_DANE_TA_DGST   TLS_DANE_TAA
+extern char *var_tls_dane_ta_dgst;
+
  /*
   * Sendmail-style mail filter support.
   */
index a4dd8d4c4b3eaf42fba36f38513be1c79d8e2e66..976b12590af392d10503a48f203602e7605cfc1d 100644 (file)
@@ -144,7 +144,7 @@ extern char *mail_pathname(const char *, const char *);
 #define MAIL_ATTR_FUNC         "function"
 #define MAIL_ATTR_CCERT_SUBJECT        "ccert_subject"
 #define MAIL_ATTR_CCERT_ISSUER "ccert_issuer"
-#define MAIL_ATTR_CCERT_FINGERPRINT "ccert_fingerprint"
+#define MAIL_ATTR_CCERT_CERT_FPRINT "ccert_fingerprint"
 #define MAIL_ATTR_CCERT_PKEY_FPRINT "ccert_pubkey_fingerprint"
 #define MAIL_ATTR_CRYPTO_PROTOCOL "encryption_protocol"
 #define MAIL_ATTR_CRYPTO_CIPHER        "encryption_cipher"
@@ -255,7 +255,7 @@ extern char *mail_pathname(const char *, const char *);
 #define MAIL_ATTR_TIMEOUT      "timeout"
 #define MAIL_ATTR_PEER_CN      "peer_CN"
 #define MAIL_ATTR_ISSUER_CN    "issuer_CN"
-#define MAIL_ATTR_PEER_FPT     "peer_fingerprint"
+#define MAIL_ATTR_PEER_CERT_FPT        "peer_fingerprint"
 #define MAIL_ATTR_PEER_PKEY_FPT        "peer_pubkey_fingerprint"
 #define MAIL_ATTR_PEER_STATUS  "peer_status"
 #define MAIL_ATTR_CIPHER_PROTOCOL "cipher_protocol"
index 761a073570cee396f8bfbaedf4a6a38a261907f4..876475864308fb8f17b0d38188b88117f592712c 100644 (file)
   * Patches change both the patchlevel and the release date. Snapshots have no
   * patchlevel; they change the release date only.
   */
-#define MAIL_RELEASE_DATE      "20130517"
+#define MAIL_RELEASE_DATE      "20130602"
 #define MAIL_VERSION_NUMBER    "2.11"
 
 #ifdef SNAPSHOT
-# define MAIL_VERSION_DATE     "-" MAIL_RELEASE_DATE
+#define MAIL_VERSION_DATE      "-" MAIL_RELEASE_DATE
 #else
-# define MAIL_VERSION_DATE     ""
+#define MAIL_VERSION_DATE      ""
 #endif
 
 #ifdef NONPROD
-# define MAIL_VERSION_PROD     "-nonprod"
+#define MAIL_VERSION_PROD      "-nonprod"
 #else
-# define MAIL_VERSION_PROD     ""
+#define MAIL_VERSION_PROD      ""
 #endif
 
 #define VAR_MAIL_VERSION       "mail_version"
index 6ebe74f18d56f09722db787c4c9aa7bf2022a712..fb7da4f2ec82996bb211fd0b27b52f2b6191c489 100644 (file)
@@ -118,6 +118,11 @@ static FORWARD_INFO *forward_open(DELIVER_REQUEST *request, const char *sender)
     FORWARD_INFO *info;
     VSTREAM *cleanup;
 
+#define FORWARD_OPEN_RETURN(res) do { \
+       vstring_free(buffer); \
+       return (res); \
+    } while (0)
+
     /*
      * Contact the cleanup service and save the new mail queue id. Request
      * that the cleanup service bounces bad messages to the sender so that we
@@ -129,13 +134,13 @@ static FORWARD_INFO *forward_open(DELIVER_REQUEST *request, const char *sender)
      */
     cleanup = mail_connect(MAIL_CLASS_PUBLIC, var_cleanup_service, BLOCKING);
     if (cleanup == 0)
-       return (0);
+       FORWARD_OPEN_RETURN(0);
     close_on_exec(vstream_fileno(cleanup), CLOSE_ON_EXEC);
     if (attr_scan(cleanup, ATTR_FLAG_STRICT,
                  ATTR_TYPE_STR, MAIL_ATTR_QUEUEID, buffer,
                  ATTR_TYPE_END) != 1) {
        vstream_fclose(cleanup);
-       return (0);
+       FORWARD_OPEN_RETURN(0);
     }
     info = (FORWARD_INFO *) mymalloc(sizeof(FORWARD_INFO));
     info->cleanup = cleanup;
@@ -190,8 +195,7 @@ static FORWARD_INFO *forward_open(DELIVER_REQUEST *request, const char *sender)
     PASS_ATTR(cleanup, MAIL_ATTR_LOG_IDENT, request->log_ident);
     PASS_ATTR(cleanup, MAIL_ATTR_RWR_CONTEXT, request->rewrite_context);
 
-    vstring_free(buffer);
-    return (info);
+    FORWARD_OPEN_RETURN(info);
 }
 
 /* forward_append - append recipient to message envelope */
index 0bd71350f699e6d64279cb580b9dc748b6af3871..a540703d9cdbec563694efad8efcdf86ed99a0e1 100644 (file)
@@ -60,9 +60,13 @@ static void psc_whitelist_non_dnsbl(PSC_STATE *state)
 
     /*
      * If no tests failed (we can't undo those), and if the whitelist
-     * threshold is met, flag all other pending or disabled tests as
+     * threshold is met, flag non-dnsbl tests that are pending or disabled as
      * successfully completed, and set their expiration times equal to the
      * DNSBL expiration time, except for tests that would expire later.
+     * 
+     * Why flag disabled tests as passed? When a disabled test is turned on,
+     * postscreen should not apply that test to clients that are already
+     * whitelisted based on their combined DNSBL score.
      */
     if ((state->flags & PSC_STATE_MASK_ANY_FAIL) == 0
        && state->dnsbl_score < var_psc_dnsbl_thresh
diff --git a/postfix/src/posttls-finger/.indent.pro b/postfix/src/posttls-finger/.indent.pro
new file mode 120000 (symlink)
index 0000000..5c837ec
--- /dev/null
@@ -0,0 +1 @@
+../../.indent.pro
\ No newline at end of file
diff --git a/postfix/src/posttls-finger/Makefile.in b/postfix/src/posttls-finger/Makefile.in
new file mode 100644 (file)
index 0000000..9be2550
--- /dev/null
@@ -0,0 +1,99 @@
+SHELL  = /bin/sh
+SRCS   = posttls-finger.c tlsmgrmem.c
+OBJS   = posttls-finger.o tlsmgrmem.o
+HDRS   = tlsmgrmem.h
+TESTSRC        = 
+DEFS   = -I. -I$(INC_DIR) -D$(SYSTYPE)
+CFLAGS = $(DEBUG) $(OPT) $(DEFS)
+TESTPROG= 
+INC_DIR        = ../../include
+PROG   = posttls-finger
+LIBS   = ../../lib/libtls.a ../../lib/libdns.a \
+       ../../lib/libglobal.a ../../lib/libutil.a
+
+.c.o:; $(CC) $(CFLAGS) -c $*.c
+
+all:   $(PROG)
+
+$(OBJS): ../../conf/makedefs.out $(HDRS)
+
+Makefile: Makefile.in
+       cat ../../conf/makedefs.out $? >$@
+
+posttls-finger: $(OBJS) $(LIBS)
+       $(CC) $(CFLAGS) -o $@ $(OBJS) $(LIBS) $(SYSLIBS)
+
+test:  $(TESTPROG)
+
+tests:
+
+root_tests:
+
+update: ../../bin/posttls-finger
+
+../../bin/posttls-finger: posttls-finger
+       cp $? $@
+
+printfck: $(OBJS) $(PROG)
+       rm -rf printfck
+       mkdir printfck
+       sed '1,/^# do not edit/!d' Makefile >printfck/Makefile
+       set -e; for i in *.c; do printfck -f .printfck $$i >printfck/$$i; done
+       cd printfck; make "INC_DIR=../../../include" `cd ..; ls *.o`
+
+lint:
+       lint $(DEFS) $(SRCS) $(LINTFIX)
+
+clean:
+       rm -f *.o *core $(PROG) $(TESTPROG) junk
+       rm -rf printfck
+
+tidy:  clean
+
+depend: $(MAKES)
+       (sed '1,/^# do not edit/!d' Makefile.in; \
+       set -e; for i in [a-z][a-z0-9]*.c; do \
+           $(CC) -E $(DEFS) $(INCL) $$i | grep -v '[<>]' | sed -n -e '/^# *1 *"\([^"]*\)".*/{' \
+           -e 's//'`echo $$i|sed 's/c$$/o/'`': \1/' \
+           -e 's/o: \.\//o: /' -e p -e '}' ; \
+       done | sort -u) | grep -v '[.][o][:][ ][/]' >$$$$ && mv $$$$ Makefile.in
+       @$(EXPORT) make -f Makefile.in Makefile 1>&2
+
+# do not edit below this line - it is generated by 'make depend'
+posttls-finger.o: ../../include/argv.h
+posttls-finger.o: ../../include/chroot_uid.h
+posttls-finger.o: ../../include/dns.h
+posttls-finger.o: ../../include/dsn.h
+posttls-finger.o: ../../include/dsn_buf.h
+posttls-finger.o: ../../include/host_port.h
+posttls-finger.o: ../../include/inet_proto.h
+posttls-finger.o: ../../include/iostuff.h
+posttls-finger.o: ../../include/mail_conf.h
+posttls-finger.o: ../../include/mail_params.h
+posttls-finger.o: ../../include/mail_server.h
+posttls-finger.o: ../../include/msg.h
+posttls-finger.o: ../../include/msg_vstream.h
+posttls-finger.o: ../../include/myaddrinfo.h
+posttls-finger.o: ../../include/mymalloc.h
+posttls-finger.o: ../../include/name_code.h
+posttls-finger.o: ../../include/name_mask.h
+posttls-finger.o: ../../include/sane_connect.h
+posttls-finger.o: ../../include/smtp_stream.h
+posttls-finger.o: ../../include/sock_addr.h
+posttls-finger.o: ../../include/stringops.h
+posttls-finger.o: ../../include/sys_defs.h
+posttls-finger.o: ../../include/timed_connect.h
+posttls-finger.o: ../../include/tls.h
+posttls-finger.o: ../../include/vbuf.h
+posttls-finger.o: ../../include/vstream.h
+posttls-finger.o: ../../include/vstring.h
+posttls-finger.o: ../../include/vstring_vstream.h
+posttls-finger.o: posttls-finger.c
+posttls-finger.o: tlsmgrmem.h
+tlsmgrmem.o: ../../include/htable.h
+tlsmgrmem.o: ../../include/sys_defs.h
+tlsmgrmem.o: ../../include/tls_mgr.h
+tlsmgrmem.o: ../../include/vbuf.h
+tlsmgrmem.o: ../../include/vstring.h
+tlsmgrmem.o: tlsmgrmem.c
+tlsmgrmem.o: tlsmgrmem.h
diff --git a/postfix/src/posttls-finger/posttls-finger.c b/postfix/src/posttls-finger/posttls-finger.c
new file mode 100644 (file)
index 0000000..3ca448a
--- /dev/null
@@ -0,0 +1,1796 @@
+/*++
+/* NAME
+/*     posttls-finger 1
+/* SUMMARY
+/*     Probe the TLS properties of an ESMTP or LMTP server.
+/* SYNOPSIS
+/*     \fBposttls-finger\fR [\fIoptions\fR] [\fBinet:\fR]\fIdomain\fR[:\fIport\fR] [\fImatch ...\fR]
+/* .br
+/*     \fBposttls-finger\fR -S [\fIoptions\fR] \fBunix:\fIpathname\fR [\fImatch ...\fR]
+/* DESCRIPTION
+/*     \fBposttls-finger\fR(1) connects to the specified destination
+/*     and reports TLS-related information about the server. With SMTP, the
+/*     destination is a domainname; with LMTP it is either a domainname
+/*     prefixed with \fBinet:\fR or a pathname prefixed with \fBunix:\fR.  If
+/*     Postfix is built without TLS support, the resulting posttls-finger
+/*     program has very limited functionality, and only the \fB-a\fR, \fB-c\fR,
+/*     \fB-h\fR, \fB-o\fR, \fB-S\fR, \fB-t\fR, \fB-T\fR and \fB-v\fR options
+/*     are available.
+/*
+/*     Note: this is an unsupported test program. No attempt is made
+/*     to maintain compatibility between successive versions.
+/*
+/*     For SMTP servers that don't support ESMTP, only the greeting banner
+/*     and the negative EHLO response are reported. Otherwise, the reported
+/*     EHLO response details further server capabilities.
+/*
+/*     If TLS support is enabled when \fBposttls-finger\fR(1) is compiled, and
+/*     the server supports \fBSTARTTLS\fR, a TLS handshake is attempted.
+/*
+/*     If DNSSEC support is available, the connection TLS security level
+/*     (\fB-l\fR option) defaults to \fBdane\fR; see TLS_README for
+/*     details. Otherwise, it defaults to \fBsecure\fR.  This setting
+/*     determines the certificate matching policy.
+/*
+/*     If TLS negotiation succeeds, the TLS protocol and cipher details are
+/*     reported. The server certificate is then verified in accordance with
+/*     the policy at the chosen (or default) security level.  With public
+/*     CA-based trust, when the \fB-L\fR option includes \fBcertmatch\fR,
+/*     (true by default) name matching is performed even if the certificate
+/*     chain is not trusted.  This logs the names found in the remote SMTP
+/*     server certificate and which if any would match, were the certificate
+/*     chain trusted.
+/*
+/*     Note: \fBposttls-finger\fR(1) does not perform any table lookups, so
+/*     the TLS policy table and obsolete per-site tables are not consulted.
+/*     It does not communicate with the \fBtlsmgr\fR(8) daemon (or any other
+/*     Postfix daemons); its TLS session cache is held in private memory, and
+/*     disappears when the process exits.
+/*
+/*     With the \fB-r \fIdelay\fR option, if the server assigns a TLS
+/*     session id, the TLS session is cached. The connection is then closed
+/*     and re-opened after the specified delay, and \fBposttls-finger\fR(1)
+/*     then reports whether the cached TLS session was re-used.
+/*
+/*     When the destination is a load-balancer, it may be distributing
+/*     load between multiple server caches. Typically, each server returns
+/*     its unique name in its EHLO response. If, upon reconnecting with
+/*     \fB-r\fR, a new server name is detected, another session is cached
+/*     for the new server, and the reconnect is repeated up to a maximum
+/*     number of times (default 5) that can be specified via the \fB-m\fR
+/*     option.
+/*
+/*     The choice of SMTP or LMTP (\fB-S\fR option) determines the syntax of
+/*     the destination argument. With SMTP, one can specify a service on a
+/*     non-default port as \fIhost\fR:\fIservice\fR, and disable MX (mail
+/*     exchanger) DNS lookups with [\fIhost\fR] or [\fIhost\fR]:\fIport\fR.
+/*     The [] form is required when you specify an IP address instead of a
+/*     hostname.  An IPv6 address takes the form [\fBipv6:\fIaddress\fR].
+/*     The default port for SMTP is taken from the \fBsmtp/tcp\fR entry in
+/*     /etc/services, defaulting to 25 if the entry is not found.
+/*
+/*     With LMTP, specify \fBunix:\fIpathname\fR to connect to a local server
+/*     listening on a unix-domain socket bound to the specified pathname;
+/*     otherwise, specify an optional \fBinet:\fR prefix followed by a
+/*     \fIdomain\fR and an optional port, with the same syntax as for
+/*     SMTP. The default TCP port for LMTP is 24.
+/*
+/*     Arguments:
+/* .IP "\fB-a\fR \fIfamily\fR (default: \fBany\fR)"
+/*     Address family preference: \fBipv4\fR, \fBipv6\fR or \fBany\fR.  When
+/*     using \fBany\fR, posttls-finger will randomly select one of the two as
+/*     the more preferred, and exhaust all MX preferences for the first
+/*     address family before trying any addresses for the other.
+/* .IP "\fB-A\fR \fItrust-anchor.pem\fR (default: none)"
+/*     A list of PEM trust-anchor files that overrides CAfile and CApath
+/*     trust chain verification.  Specify the option multiple times to
+/*     specify multiple files.  See the main.cf documentation for
+/*     smtp_tls_trust_anchor_file for details.
+/* .IP "\fB-c\fR"
+/*     Disable SMTP chat logging; only TLS-related information is logged.
+/* .IP "\fB-C\fR"
+/*     Print the remote SMTP server certificate trust chain in PEM format.
+/*     The issuer DN, subject DN, certificate and public key fingerprints
+/*     (see \fB-d \fImdalg\fR option below) are printed above each PEM
+/*     certificate block.  If you specify \fB-F \fICAfile\fR or
+/*     \fB-P \fICApath\fR, the OpenSSL library may augment the chain with
+/*     missing issuer certificates.  To see the actual chain sent by the
+/*     remote SMTP server leave \fICAfile\fR and \fICApath\fR unset.
+/* .IP "\fB-d \fImdalg\fR (default: \fBsha1\fR)"
+/*     The message digest algorithm to use for reporting remote SMTP server
+/*     fingerprints and matching against user provided certificate
+/*     fingerprints (with DANE TLSA records the algorithm is specified
+/*     in the DNS).
+/* .IP "\fB-F \fICAfile.pem\fR (default: none)"
+/*     The PEM formatted CAfile for remote SMTP server certificate
+/*     verification.  By default no CAfile is used and no public CAs
+/*     are trusted.
+/* .IP "\fB-g \fIgrade\fR (default: medium)"
+/*     The minimum TLS cipher grade used by posttls-finger.  See
+/*     smtp_tls_mandatory_ciphers for details.
+/* .IP "\fB-h \fIhost_lookup\fR (default: \fBdns\fR)"
+/*     The hostname lookup methods used for the connection.  See the
+/*     documentation of smtp_host_lookup for syntax and semantics.
+/* .IP "\fB-l \fIlevel\fR (default: \fBdane\fR or \fBsecure\fR)"
+/*     The security level for the connection, default \fBdane\fR or
+/*     \fBsecure\fR depending on whether DNSSEC is available.  For syntax
+/*     and semantics, see the documentation of smtp_tls_security_level.
+/*     When \fBdane\fR or \fBdane-only\fR is supported and selected, if no
+/*     TLSA records are found, or all the records found are unusable, the
+/*     \fIsecure\fR level will be used instead.  The \fBfingerprint\fR
+/*     security level allows you to test certificate or public-key
+/*     fingerprint matches before you deploy them in the policy table.
+/* .IP
+/*     Note, since \fBposttls-finger\fR does not actually deliver any email,
+/*     the \fBnone\fR, \fBmay\fR and \fBencrypt\fR security levels are not
+/*     very useful.  Since \fBmay\fR and \fBencrypt\fR don't require peer
+/*     certificates, they will often negotiate anonymous TLS ciphersuites,
+/*     so you won't learn much about the remote SMTP server's certificates
+/*     at these levels if it also supports anonymous TLS (though you may
+/*     learn that the server supports anonymous TLS).
+/* .IP "\fB-L \fIlogopts\fR (default: \fBroutine,certmatch\fR)"
+/*     Fine-grained TLS logging options. To tune the TLS features logged
+/*     during the TLS handshake, specify one or more of:
+/* .RS
+/* .IP "\fB0, none\fR"
+/*     These yield no TLS logging; you'll generally want more, but this
+/*     is handy if you just want the trust chain:
+/* .RS
+/* .ad
+/* .nf
+/*     $ posttls-finger -cC -L none destination
+/* .fi
+/* .RE
+/* .IP "\fB1, routine, summary\fR"
+/*     These synonymous values yield a normal one-line summary of the TLS
+/*     connection.
+/* .IP "\fB2, debug\fR"
+/*     These synonymous values combine routine, ssl-debug, cache and verbose.
+/* .IP "\fB3, ssl-expert\fR"
+/*     These synonymous values combine debug with ssl-handshake-packet-dump.
+/*     For experts only.
+/* .IP "\fB4, ssl-developer\fR"
+/*     These synonymous values combine ssl-expert with ssl-session-packet-dump.
+/*     For experts only, and in most cases, use wireshark instead.
+/* .IP "\fBssl-debug\fR"
+/*     Turn on OpenSSL logging of the progress of the SSL handshake.
+/* .IP "\fBssl-handshake-packet-dump\fR"
+/*     Log hexadecimal packet dumps of the SSL handshake; for experts only.
+/* .IP "\fBssl-session-packet-dump\fR"
+/*     Log hexadecimal packet dumps of the entire SSL session; only useful
+/*     to those who can debug SSL protocol problems from hex dumps.
+/* .IP "\fBuntrusted\fR"
+/*     Logs trust chain verification problems.  This is turned on
+/*     automatically at security levels that use peer names signed
+/*     by certificate authorities to validate certificates.  So while
+/*     this setting is recognized, you should never need to set it
+/*     explicitly.
+/* .IP "\fBpeercert\fR"
+/*     This logs a one line summary of the remote SMTP server certificate
+/*     subject, issuer, and fingerprints.
+/* .IP "\fBcertmatch\fR"
+/*     This logs remote SMTP server certificate matching, showing the CN
+/*     and each subjectAltName and which name matched.  With DANE, logs
+/*     matching of TLSA record trust-anchor and end-entity certificates.
+/* .IP "\fBcache\fR"
+/*     This logs session cache operations, showing whether session caching
+/*     is effective with the remote SMTP server.  Automatically used when
+/*     reconnecting with the \fB-r\fR option; rarely needs to be set
+/*     explicitly.
+/* .IP "\fBverbose\fR"
+/*     Enables verbose logging in the Postfix TLS driver; includes all of
+/*     peercert..cache and more.
+/* .RE
+/* .IP
+/*     The default is \fBroutine,certmatch\fR. After a reconnect,
+/*     \fBpeercert\fR, \fBcertmatch\fR and \fBverbose\fR are automatically
+/*     disabled while \fBcache\fR and \fBsummary\fR are enabled.
+/* .IP "\fB-m \fIcount\fR (default: \fB5\fR)"
+/*     When the \fB-r \fIdelay\fR option is specified, the \fB-m\fR option
+/*     determines the maximum number of reconnect attempts to use with
+/*     a server behind a load-balacer, to see whether connection caching
+/*     is likely to be effective for this destination.  Some MTAs
+/*     don't expose the underlying server identity in their EHLO
+/*     response; with these servers there will never be more than
+/*     1 reconnection attempt.
+/* .IP "\fB-o \fIname=value\fR"
+/*     Specify zero or more times to override the value of the main.cf
+/*     parameter \fIname\fR with \fIvalue\fR.  Possible use-cases include
+/*     overriding the values of TLS library parameters, or "myhostname" to
+/*     configure the SMTP EHLO name sent to the remote server.
+/* .IP "\fB-p \fIprotocols\fR (default: !SSLv2)"
+/*     List of TLS protocols that posttls-finger will exclude or include.  See
+/*     smtp_tls_mandatory_protocols for details.
+/* .IP "\fB-P \fICApath/\fR (default: none)"
+/*     The OpenSSL CApath/ directory (indexed via c_rehash(1)) for remote
+/*     SMTP server certificate verification.  By default no CApath is used
+/*     and no public CAs are trusted.
+/* .IP "\fB-r \fIdelay\fR"
+/*     With a cachable TLS session, disconnect and reconnect after \fIdelay\fR
+/*     seconds. Report whether the session is re-used. Retry if a new server
+/*     is encountered, up to 5 times or as specified with the \fB-m\fR option.
+/*     By default reconnection is disabled, specify a positive delay to
+/*     enable this behavior.
+/* .IP "\fB-S\fR"
+/*     Disable SMTP; that is, connect to an LMTP server. The default port for
+/*     LMTP over TCP is 24.  Alternative ports can specified by appending
+/*     "\fI:servicename\fR" or ":\fIportnumber\fR" to the destination
+/*     argument.
+/* .IP "\fB-t \fItimeout\fR (default: \fB5\fR)"
+/*     The TCP connection timeout to use.  This is also the timeout for
+/*     reading the remote server's 220 banner.
+/* .IP "\fB-T \fItimeout\fR (default: \fB30\fR)"
+/*     The SMTP/LMTP command timeout for EHLO/LHLO, STARTTLS and QUIT.
+/* .IP "\fB-v\fR"
+/*     Enable verose Postfix logging.  Specify more than once to increase
+/*     the level of verbose logging.
+/* .IP "[\fBinet:\fR]\fIdomain\fR[:\fIport\fR]"
+/*     Connect via TCP to domain \fIdomain\fR, port \fIport\fR. The default
+/*     port is \fBsmtp\fR (or 24 with LMTP).  With SMTP an MX lookup is
+/*     performed to resolve the domain to a host, unless the domain is
+/*     enclosed in \fB[]\fR.  If you want to connect to a specific MX host,
+/*     for instance \fImx1.example.com\fR, specify [\fImx1.example.com\fR]
+/*     as the destination and \fIexample.com\fR as a \fBmatch\fR argument.
+/*     When using DNS, the destination domain is assumed fully qualified
+/*     and no default domain or search suffixes are applied; you must use
+/*     fully-qualified names or also enable \fBnative\fR host lookups
+/*     (these don't support \fBdane\fR or \fBdane-only\fR as no DNSSEC
+/*     validation information is available via \fBnative\fR lookups).
+/* .IP "\fBunix:\fIpathname\fR"
+/*     Connect to the UNIX-domain socket at \fIpathname\fR. LMTP only.
+/* .IP "\fBmatch ...\fR"
+/*     With no match arguments specified, certificate peername matching uses
+/*     the compiled-in default strategies for each security level.  If you
+/*     specify one or more arguments, these will be used as the list of
+/*     certificate or public-key digests to match for the \fBfingerprint\fR
+/*     level, or as the list of DNS names to match in the certificate at the
+/*     \fBverify\fR and \fBsecure\fR levels.  If the security level is
+/*     \fBdane\fR, or \fBdane-only\fR the match names are ignored, and
+/*     \fBhostname, nexthop\fR strategies are used.
+/* .ad
+/* .fi
+/* ENVIRONMENT
+/* .ad
+/* .fi
+/* .IP \fBMAIL_CONFIG\fR
+/*     Read configuration parameters from a non-default location.
+/* .IP \fBMAIL_VERBOSE\fR
+/*     Same as \fB-v\fR option.
+/* SEE ALSO
+/*     smtp-source(1), SMTP/LMTP message source
+/*     smtp-sink(1), SMTP/LMTP message dump
+/*
+/* README FILES
+/* .ad
+/* .fi
+/*     Use "\fBpostconf readme_directory\fR" or "\fBpostconf
+/*     html_directory\fR" to locate this information.
+/* .na
+/* .nf
+/*     TLS_README, Postfix STARTTLS howto
+/* LICENSE
+/* .ad
+/* .fi
+/*     The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/*     Wietse Venema
+/*     IBM T.J. Watson Research
+/*     P.O. Box 704
+/*     Yorktown Heights, NY 10598, USA
+/*
+/*     Viktor Dukhovni
+/*--*/
+
+ /*
+  * System library.
+  */
+#include <sys_defs.h>
+#include <stdarg.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <signal.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+ /*
+  * Utility library.
+  */
+#include <msg.h>
+#include <msg_vstream.h>
+#include <vstring.h>
+#include <vstream.h>
+#include <vstring_vstream.h>
+#include <mymalloc.h>
+#include <stringops.h>
+#include <argv.h>
+#include <name_mask.h>
+#include <name_code.h>
+#include <chroot_uid.h>
+#include <host_port.h>
+#include <inet_proto.h>
+#include <iostuff.h>
+#include <timed_connect.h>
+#include <sane_connect.h>
+#include <myaddrinfo.h>
+#include <sock_addr.h>
+
+#define STR(x)         vstring_str(x)
+
+ /*
+  * Global library.
+  */
+#include <mail_params.h>
+#include <mail_conf.h>
+#include <smtp_stream.h>
+#include <dsn_buf.h>
+
+/* DNS library. */
+
+#include <dns.h>
+
+ /*
+  * master library
+  */
+#include <mail_server.h>
+
+ /*
+  * TLS Library
+  */
+#define TLS_INTERNAL
+#include <tls.h>
+
+#ifdef USE_TLS
+#include <openssl/engine.h>
+#endif
+
+ /*
+  * Application specific
+  */
+#include "tlsmgrmem.h"
+
+static int conn_tmout = 5;
+static int smtp_tmout = 30;
+
+#define HOST_FLAG_DNS          (1<<0)
+#define HOST_FLAG_NATIVE       (1<<1)
+
+#define MISC_FLAG_PREF_IPV6    (1<<0)
+#define MISC_FLAG_PREF_IPV4    (1<<1)
+
+static const NAME_MASK lookup_masks[] = {
+    "dns", HOST_FLAG_DNS,
+    "native", HOST_FLAG_NATIVE,
+    0,
+};
+
+static const NAME_CODE addr_pref_map[] = {
+    INET_PROTO_NAME_IPV6, MISC_FLAG_PREF_IPV6,
+    INET_PROTO_NAME_IPV4, MISC_FLAG_PREF_IPV4,
+    INET_PROTO_NAME_ANY, 0,
+    0, -1,
+};
+
+typedef struct OPTIONS {
+    char   *logopts;
+    char   *level;
+    ARGV   *tas;
+    char   *host_lookup;
+    char   *addr_pref;
+} OPTIONS;
+
+ /*
+  * Per-session data structure with state.
+  * 
+  * This software can maintain multiple parallel connections to the same SMTP
+  * server. However, it makes no more than one connection request at a time
+  * to avoid overwhelming the server with SYN packets and having to back off.
+  * Back-off would screw up the benchmark. Pending connection requests are
+  * kept in a linear list.
+  */
+typedef struct STATE {
+    int     smtp;                      /* SMTP or LMTP? */
+    int     host_lookup;               /* dns|native|dns,native */
+    int     addr_pref;                 /* v4, v6, both */
+    int     log_mask;                  /* via tls_log_mask() */
+    int     reconnect;                 /* -r option */
+    int     max_reconnect;             /* -m option */
+    unsigned port;                     /* TCP port */
+    char   *dest;                      /* Full destination spec */
+    char   *addrport;                  /* [addr]:port */
+    char   *namaddrport;               /* name[addr]:port */
+    char   *nexthop;                   /* Nexthop domain for verification */
+    char   *hostname;                  /* Hostname for verification */
+    DNS_RR *addr;                      /* IPv[46] Address to (re)connect to */
+    int     pass;                      /* Pass number, 2 for reconnect */
+    int     nochat;                    /* disable chat logging */
+    char   *helo;                      /* Server name from EHLO reply */
+    DSN_BUF *why;                      /* SMTP-style error message */
+    VSTRING *buffer;                   /* Response buffer */
+    VSTREAM *stream;                   /* Open connection */
+    int     level;                     /* TLS security level */
+#ifdef USE_TLS
+    char   *mdalg;                     /* fingerprint digest algorithm */
+    char   *CAfile;                    /* Trusted public CAs */
+    char   *CApath;                    /* Trusted public CAs */
+    ARGV   *match;                     /* match arguments */
+    int     print_trust;               /* -C option */
+    BIO    *tls_bio;                   /* BIO wrapper for stdout */
+    TLS_APPL_STATE *tls_ctx;           /* Application TLS context */
+    TLS_SESS_STATE *tls_context;       /* Session TLS context */
+    TLS_DANE *dane;                    /* DANE TLSA validation structure */
+    TLS_DANE *ddane;                   /* DANE TLSA from DNS */
+    char   *grade;                     /* Minimum cipher grade */
+    char   *protocols;                 /* Protocol inclusion/exclusion */
+#endif
+    OPTIONS options;                   /* JCL */
+} STATE;
+
+static DNS_RR *host_addr(STATE *, const char *);
+
+#define HNAME(addr) (addr->dnssec_valid ? addr->rname : addr->qname)
+
+ /*
+  * Structure with broken-up SMTP server response.
+  */
+typedef struct {                       /* server response */
+    int     code;                      /* status */
+    char   *str;                       /* text */
+    VSTRING *buf;                      /* origin of text */
+} RESPONSE;
+
+
+/* command - send an SMTP command */
+
+static void command(STATE *state, int verbose, char *fmt,...)
+{
+    VSTREAM *stream = state->stream;
+    VSTRING *buf;
+    va_list ap;
+    char   *line;
+
+    buf = vstring_alloc(100);
+    va_start(ap, fmt);
+    vstring_vsprintf(buf, fmt, ap);
+    va_end(ap);
+    line = vstring_str(buf);
+
+    while (line && *line) {
+       char   *nextline = strchr(line, '\n');
+
+       if (nextline)
+           *nextline++ = '\0';
+       if (verbose && !state->nochat)
+           msg_info("> %s", line);
+       smtp_printf(stream, "%s", line);
+       line = nextline;
+    }
+
+    vstring_free(buf);
+}
+
+/* response - read and process SMTP server response */
+
+static RESPONSE *response(STATE *state, int verbose)
+{
+    VSTREAM *stream = state->stream;
+    VSTRING *buf = state->buffer;
+    static RESPONSE rdata;
+    int     more;
+    char   *cp;
+
+    /*
+     * Initialize the response data buffer. Defend against a denial of
+     * service attack by limiting the amount of multi-line text that we are
+     * willing to store.
+     */
+    if (rdata.buf == 0) {
+       rdata.buf = vstring_alloc(100);
+       vstring_ctl(rdata.buf, VSTRING_CTL_MAXLEN, (ssize_t) var_line_limit, 0);
+    }
+
+    /*
+     * Censor out non-printable characters in server responses. Concatenate
+     * multi-line server responses. Separate the status code from the text.
+     * Leave further parsing up to the application.
+     */
+#define BUF ((char *) vstring_str(buf))
+    VSTRING_RESET(rdata.buf);
+    for (;;) {
+       smtp_get(buf, stream, var_line_limit, SMTP_GET_FLAG_SKIP);
+       for (cp = BUF; *cp != 0; cp++)
+           if (!ISPRINT(*cp) && !ISSPACE(*cp))
+               *cp = '?';
+       cp = BUF;
+       if (verbose && !state->nochat)
+           msg_info("< %s", cp);
+       while (ISDIGIT(*cp))
+           cp++;
+       rdata.code = (cp - BUF == 3 ? atoi(BUF) : 0);
+       if ((more = (*cp == '-')) != 0)
+           cp++;
+       while (ISSPACE(*cp))
+           cp++;
+       vstring_strcat(rdata.buf, cp);
+       if (more == 0)
+           break;
+       VSTRING_ADDCH(rdata.buf, '\n');
+    }
+    VSTRING_TERMINATE(rdata.buf);
+    rdata.str = vstring_str(rdata.buf);
+    return (&rdata);
+}
+
+/* exception_text - translate exceptions from the smtp_stream module */
+
+static char *exception_text(int except)
+{
+    switch (except) {
+       case SMTP_ERR_EOF:
+       return ("lost connection");
+    case SMTP_ERR_TIME:
+       return ("timeout");
+    default:
+       msg_panic("exception_text: unknown exception %d", except);
+    }
+}
+
+/* ehlo - send EHLO/LHLO */
+
+static RESPONSE *ehlo(STATE *state)
+{
+    int     except;
+    int     verbose;
+    volatile char *ehlo = state->smtp ? "EHLO" : "LHLO";
+    VSTREAM *stream = state->stream;
+    RESPONSE *resp;
+
+#ifdef USE_TLS
+    verbose = (state->pass == 1 && state->nochat == 0);
+#else
+    verbose = 1;
+#endif
+
+    /*
+     * Send the standard greeting with our hostname
+     */
+    if ((except = vstream_setjmp(stream)) != 0) {
+       msg_info("%s while sending %s", exception_text(except), ehlo);
+       return (0);
+    }
+    command(state, verbose, "%s %s", ehlo, var_myhostname);
+
+    resp = response(state, verbose);
+    if (resp->code / 100 != 2) {
+       msg_info("%s rejected: %d %s", ehlo, resp->code, resp->str);
+       return (0);
+    }
+    return resp;
+}
+
+#ifdef USE_TLS
+
+static void print_trust_info(STATE *state)
+{
+    STACK_OF(X509) *sk = SSL_get_peer_cert_chain(state->tls_context->con);
+
+    if (sk != NULL) {
+       int     i;
+
+       BIO_printf(state->tls_bio, "---\nCertificate chain\n");
+       for (i = 0; i < sk_X509_num(sk); i++) {
+           X509   *cert = sk_X509_value(sk, i);
+           char    buf[CCERT_BUFSIZ];
+           X509_NAME *xn;
+           char   *digest;
+
+           if ((xn = X509_get_subject_name(cert)) != 0) {
+               X509_NAME_oneline(xn, buf, sizeof buf);
+               BIO_printf(state->tls_bio, "%2d subject: %s\n", i, buf);
+           }
+           if ((xn = X509_get_issuer_name(cert)) != 0) {
+               X509_NAME_oneline(xn, buf, sizeof buf);
+               BIO_printf(state->tls_bio, "    issuer: %s\n", buf);
+           }
+           digest = tls_cert_fprint(cert, state->mdalg);
+           BIO_printf(state->tls_bio, "   cert digest=%s\n", digest);
+           myfree(digest);
+
+           digest = tls_pkey_fprint(cert, state->mdalg);
+           BIO_printf(state->tls_bio, "   pkey digest=%s\n", digest);
+           myfree(digest);
+
+           PEM_write_bio_X509(state->tls_bio, cert);
+       }
+    }
+}
+
+/* starttls - SMTP STARTTLS handshake */
+
+static int starttls(STATE *state)
+{
+    VSTRING *cipher_exclusions;
+    VSTRING *serverid;
+    int     except;
+    RESPONSE *resp;
+    VSTREAM *stream = state->stream;
+    TLS_CLIENT_START_PROPS tls_props;
+
+    /* SMTP stream with deadline timeouts */
+    smtp_stream_setup(stream, smtp_tmout, 1);
+    if ((except = vstream_setjmp(stream)) != 0) {
+       msg_fatal("%s while sending STARTTLS", exception_text(except));
+       return (1);
+    }
+    command(state, state->pass == 1, "STARTTLS");
+
+    resp = response(state, state->pass == 1);
+    if (resp->code / 100 != 2) {
+       msg_info("STARTTLS rejected: %d %s", resp->code, resp->str);
+       return (1);
+    }
+
+    /*
+     * Discard any plain-text data that may be piggybacked after the server's
+     * 220 STARTTLS reply. Should we abort the session instead?
+     */
+    vstream_fpurge(stream, VSTREAM_PURGE_READ);
+
+#define ADD_EXCLUDE(vstr, str) \
+    do { \
+       if (*(str)) \
+           vstring_sprintf_append((vstr), "%s%s", \
+                                  VSTRING_LEN(vstr) ? " " : "", (str)); \
+    } while (0)
+
+    cipher_exclusions = vstring_alloc(10);
+    ADD_EXCLUDE(cipher_exclusions, DEF_SMTP_TLS_EXCL_CIPH);
+    if (TLS_REQUIRED(state->level))
+       ADD_EXCLUDE(cipher_exclusions, DEF_SMTP_TLS_MAND_EXCL);
+
+    /*
+     * If we're authenticating suppress anonymous ciphersuites, otherwise at
+     * least encrypt, not much point in doing neither.
+     */
+    if (TLS_MUST_MATCH(state->level))
+       ADD_EXCLUDE(cipher_exclusions, "aNULL");
+    else
+       ADD_EXCLUDE(cipher_exclusions, "eNULL");
+
+    serverid = vstring_alloc(10);
+    vstring_sprintf(serverid, "%s:%s", var_procname, state->addrport);
+
+    state->tls_context =
+       TLS_CLIENT_START(&tls_props,
+                        ctx = state->tls_ctx,
+                        stream = stream,
+                        timeout = smtp_tmout,
+                        tls_level = state->level,
+                        nexthop = state->nexthop,
+                        host = state->hostname,
+                        namaddr = state->namaddrport,
+                        serverid = STR(serverid),
+                        helo = state->helo ? state->helo : "",
+                        protocols = state->protocols,
+                        cipher_grade = state->grade,
+                        cipher_exclusions
+                        = vstring_str(cipher_exclusions),
+                        matchargv = state->match,
+                        mdalg = state->mdalg,
+                        dane = state->ddane ? state->ddane : state->dane);
+    vstring_free(cipher_exclusions);
+    vstring_free(serverid);
+    if (state->helo) {
+       myfree(state->helo);
+       state->helo = 0;
+    }
+    if (state->tls_context == 0) {
+       /* We must avoid further I/O, the peer is in an undefined state. */
+       (void) vstream_fpurge(stream, VSTREAM_PURGE_BOTH);
+       (void) vstream_fclose(stream);
+       state->stream = 0;
+       return (1);
+    }
+    if (state->pass == 1) {
+       ehlo(state);
+       if (!TLS_CERT_IS_PRESENT(state->tls_context))
+           msg_info("Server is anonymous");
+       else if (state->print_trust)
+           print_trust_info(state);
+       state->log_mask &= ~(TLS_LOG_CERTMATCH | TLS_LOG_PEERCERT |
+                            TLS_LOG_VERBOSE | TLS_LOG_UNTRUSTED);
+       state->log_mask |= TLS_LOG_CACHE | TLS_LOG_SUMMARY;
+       tls_update_app_logmask(state->tls_ctx, state->log_mask);
+    }
+    return (0);
+}
+
+#endif
+
+/* doproto - do SMTP handshake */
+
+static int doproto(STATE *state)
+{
+    VSTREAM *stream = state->stream;
+    RESPONSE *resp;
+    int     except;
+    int     n;
+    char   *lines;
+    char   *words;
+    char   *word;
+
+    /*
+     * Prepare for disaster.
+     */
+    smtp_stream_setup(stream, conn_tmout, 1);
+    if ((except = vstream_setjmp(stream)) != 0)
+       msg_fatal("%s while reading server greeting", exception_text(except));
+
+    /*
+     * Read and parse the server's SMTP greeting banner.
+     */
+    if (((resp = response(state, 1))->code / 100) != 2) {
+       msg_info("SMTP service not available: %d %s", resp->code, resp->str);
+       return (1);
+    }
+
+    /*
+     * Send the standard greeting with our hostname
+     */
+    if ((resp = ehlo(state)) == 0)
+       return (1);
+
+    lines = resp->str;
+    for (n = 0; (words = mystrtok(&lines, "\n")) != 0; ++n) {
+       if ((word = mystrtok(&words, " \t=")) != 0) {
+           if (n == 0)
+               state->helo = mystrdup(word);
+           if (strcasecmp(word, "STARTTLS") == 0)
+               break;
+       }
+    }
+
+#ifdef USE_TLS
+    if (words && state->tls_ctx)
+       if (starttls(state))
+           return (1);
+#endif
+
+    /*
+     * Prepare for disaster.
+     */
+    smtp_stream_setup(stream, smtp_tmout, 1);
+    if ((except = vstream_setjmp(stream)) != 0) {
+       msg_warn("%s while sending QUIT command", exception_text(except));
+       return (0);
+    }
+    command(state, 1, "QUIT");
+    (void) response(state, 1);
+
+    return (0);
+}
+
+/* connect_sock - connect a socket over some transport */
+
+static VSTREAM *connect_sock(int sock, struct sockaddr * sa, int salen,
+                          const char *name, const char *addr, STATE *state)
+{
+    DSN_BUF *why = state->why;
+    int     conn_stat;
+    int     saved_errno;
+    VSTREAM *stream;
+
+    if (conn_tmout > 0) {
+       non_blocking(sock, NON_BLOCKING);
+       conn_stat = timed_connect(sock, sa, salen, conn_tmout);
+       saved_errno = errno;
+       non_blocking(sock, BLOCKING);
+       errno = saved_errno;
+    } else {
+       conn_stat = sane_connect(sock, sa, salen);
+    }
+    if (conn_stat < 0) {
+       if (state->port)
+           dsb_simple(why, "4.4.1", "connect to %s[%s]:%d: %m",
+                      name, addr, ntohs(state->port));
+       else
+           dsb_simple(why, "4.4.1", "connect to %s[%s]: %m", name, addr);
+       close(sock);
+       return (0);
+    }
+    stream = vstream_fdopen(sock, O_RDWR);
+    state->namaddrport =
+       vstring_export(state->port == 0 ?
+                 vstring_sprintf(vstring_alloc(10), "%s[%s]", name, addr) :
+                      vstring_sprintf(vstring_alloc(10), "%s[%s]:%u",
+                                      name, addr, ntohs(state->port)));
+    state->addrport =
+       vstring_export(state->port == 0 ?
+                      vstring_sprintf(vstring_alloc(10), "%s", addr) :
+                      vstring_sprintf(vstring_alloc(10), "[%s]:%u",
+                                      addr, ntohs(state->port)));
+
+    /*
+     * Avoid poor performance when TCP MSS > VSTREAM_BUFSIZE.
+     */
+    if (sa->sa_family == AF_INET
+#ifdef AF_INET6
+       || sa->sa_family == AF_INET6
+#endif
+       )
+       vstream_tweak_tcp(stream);
+
+    return (stream);
+}
+
+/* connect_unix - connect to a unix-domain socket */
+
+static VSTREAM *connect_unix(STATE *state, const char *path)
+{
+    static const char *myname = "connect_unix";
+    DSN_BUF *why = state->why;
+    struct sockaddr_un sock_un;
+    int     len = strlen(path);
+    int     sock;
+
+    if (!state->nexthop)
+       state->nexthop = mystrdup(var_myhostname);
+    state->hostname = mystrdup(var_myhostname);
+
+    dsb_reset(why);                            /* Paranoia */
+
+    /*
+     * Sanity checks.
+     */
+    if (len >= (int) sizeof(sock_un.sun_path)) {
+       dsb_simple(why, "4.3.5", "unix-domain name too long: %s", path);
+       return (0);
+    }
+
+    /*
+     * Initialize.
+     */
+    memset((char *) &sock_un, 0, sizeof(sock_un));
+    sock_un.sun_family = AF_UNIX;
+#ifdef HAS_SUN_LEN
+    sock_un.sun_len = len + 1;
+#endif
+    memcpy(sock_un.sun_path, path, len + 1);
+
+    /*
+     * Create a client socket.
+     */
+    if ((sock = socket(AF_UNIX, SOCK_STREAM, 0)) < 0)
+       msg_fatal("%s: socket: %m", myname);
+
+    /*
+     * Connect to the server.
+     */
+    if (msg_verbose)
+       msg_info("%s: trying: %s...", myname, path);
+
+    return (connect_sock(sock, (struct sockaddr *) & sock_un, sizeof(sock_un),
+                        var_myhostname, path, state));
+}
+
+/* connect_addr - connect to explicit address */
+
+static VSTREAM *connect_addr(STATE *state, DNS_RR *addr)
+{
+    static const char *myname = "connect_addr";
+    DSN_BUF *why = state->why;
+    struct sockaddr_storage ss;                /* remote */
+    struct sockaddr *sa = (struct sockaddr *) & ss;
+    SOCKADDR_SIZE salen = sizeof(ss);
+    MAI_HOSTADDR_STR hostaddr;
+    int     sock;
+
+    dsb_reset(why);                            /* Paranoia */
+
+    /*
+     * Sanity checks.
+     */
+    if (dns_rr_to_sa(addr, state->port, sa, &salen) != 0) {
+       msg_warn("%s: skip address type %s: %m",
+                myname, dns_strtype(addr->type));
+       dsb_simple(why, "4.4.0", "network address conversion failed: %m");
+       return (0);
+    }
+
+    /*
+     * Initialize.
+     */
+    if ((sock = socket(sa->sa_family, SOCK_STREAM, 0)) < 0)
+       msg_fatal("%s: socket: %m", myname);
+
+    if (inet_windowsize > 0)
+       set_inet_windowsize(sock, inet_windowsize);
+
+    /*
+     * Connect to the server.
+     */
+    SOCKADDR_TO_HOSTADDR(sa, salen, &hostaddr, (MAI_SERVPORT_STR *) 0, 0);
+    if (msg_verbose)
+       msg_info("%s: trying: %s[%s] port %d...",
+                myname, HNAME(addr), hostaddr.buf, ntohs(state->port));
+
+    return (connect_sock(sock, sa, salen, HNAME(addr), hostaddr.buf, state));
+}
+
+#define HAS_DSN(why)           (STR((why)->status)[0] != 0)
+#define HAS_SOFT_DSN(why)      (STR((why)->status)[0] == '4')
+#define HAS_HARD_DSN(why)      (STR((why)->status)[0] == '5')
+#define HAS_LOOP_DSN(why) \
+    (HAS_DSN(why) && strcmp(STR((why)->status) + 1, ".4.6") == 0)
+
+#define SET_SOFT_DSN(why)      (STR((why)->status)[0] = '4')
+#define SET_HARD_DSN(why)      (STR((why)->status)[0] = '5')
+
+/* addr_one - address lookup for one host name */
+
+static DNS_RR *addr_one(STATE *state, DNS_RR *addr_list, const char *host,
+                               int res_opt, unsigned pref)
+{
+    static const char *myname = "addr_one";
+    DSN_BUF *why = state->why;
+    DNS_RR *addr = 0;
+    DNS_RR *rr;
+    int     aierr;
+    struct addrinfo *res0;
+    struct addrinfo *res;
+    INET_PROTO_INFO *proto_info = inet_proto_info();
+    int     found;
+
+    if (msg_verbose)
+       msg_info("%s: host %s", myname, host);
+
+    /*
+     * Interpret a numerical name as an address.
+     */
+    if (hostaddr_to_sockaddr(host, (char *) 0, 0, &res0) == 0
+     && strchr((char *) proto_info->sa_family_list, res0->ai_family) != 0) {
+       if ((addr = dns_sa_to_rr(host, pref, res0->ai_addr)) == 0)
+           msg_fatal("host %s: conversion error for address family %d: %m",
+                   host, ((struct sockaddr *) (res0->ai_addr))->sa_family);
+       addr_list = dns_rr_append(addr_list, addr);
+       freeaddrinfo(res0);
+       return (addr_list);
+    }
+
+    /*
+     * Use DNS lookup, but keep the option open to use native name service.
+     * 
+     * XXX A soft error dominates past and future hard errors. Therefore we
+     * should not clobber a soft error text and status code.
+     */
+    if (state->host_lookup & HOST_FLAG_DNS) {
+       switch (dns_lookup_v(host, res_opt, &addr, (VSTRING *) 0,
+                            why->reason, DNS_REQ_FLAG_NONE,
+                            proto_info->dns_atype_list)) {
+       case DNS_OK:
+           for (rr = addr; rr; rr = rr->next)
+               rr->pref = pref;
+           addr_list = dns_rr_append(addr_list, addr);
+           return (addr_list);
+       default:
+           dsb_status(why, "4.4.3");
+           return (addr_list);
+       case DNS_FAIL:
+           dsb_status(why, HAS_SOFT_DSN(why) ? "4.4.3" : "5.4.3");
+           return (addr_list);
+       case DNS_INVAL:
+           dsb_status(why, HAS_SOFT_DSN(why) ? "4.4.4" : "5.4.4");
+           return (addr_list);
+       case DNS_NOTFOUND:
+           dsb_status(why, HAS_SOFT_DSN(why) ? "4.4.4" : "5.4.4");
+           /* maybe native naming service will succeed */
+           break;
+       }
+    }
+
+    /*
+     * Use the native name service which also looks in /etc/hosts.
+     * 
+     * XXX A soft error dominates past and future hard errors. Therefore we
+     * should not clobber a soft error text and status code.
+     */
+#define RETRY_AI_ERROR(e) \
+        ((e) == EAI_AGAIN || (e) == EAI_MEMORY || (e) == EAI_SYSTEM)
+#ifdef EAI_NODATA
+#define DSN_NOHOST(e) \
+       ((e) == EAI_AGAIN || (e) == EAI_NODATA || (e) == EAI_NONAME)
+#else
+#define DSN_NOHOST(e) \
+       ((e) == EAI_AGAIN || (e) == EAI_NONAME)
+#endif
+
+    if (state->host_lookup & HOST_FLAG_NATIVE) {
+       if ((aierr = hostname_to_sockaddr(host, (char *) 0, 0, &res0)) != 0) {
+           dsb_simple(why, (HAS_SOFT_DSN(why) || RETRY_AI_ERROR(aierr)) ?
+                      (DSN_NOHOST(aierr) ? "4.4.4" : "4.3.0") :
+                      (DSN_NOHOST(aierr) ? "5.4.4" : "5.3.0"),
+                      "unable to look up host %s: %s",
+                      host, MAI_STRERROR(aierr));
+       } else {
+           for (found = 0, res = res0; res != 0; res = res->ai_next) {
+               if (strchr((char *) proto_info->sa_family_list, res->ai_family) == 0) {
+                   msg_info("skipping address family %d for host %s",
+                            res->ai_family, host);
+                   continue;
+               }
+               found++;
+               if ((addr = dns_sa_to_rr(host, pref, res->ai_addr)) == 0)
+                   msg_fatal("host %s: conversion error for address family %d: %m",
+                   host, ((struct sockaddr *) (res0->ai_addr))->sa_family);
+               addr_list = dns_rr_append(addr_list, addr);
+           }
+           freeaddrinfo(res0);
+           if (found == 0) {
+               dsb_simple(why, HAS_SOFT_DSN(why) ? "4.4.4" : "5.4.4",
+                          "%s: host not found", host);
+           }
+           return (addr_list);
+       }
+    }
+
+    /*
+     * No further alternatives for host lookup.
+     */
+    return (addr_list);
+}
+
+/* mx_addr_list - address lookup for a list of mail exchangers */
+
+static DNS_RR *mx_addr_list(STATE *state, DNS_RR *mx_names)
+{
+    static const char *myname = "mx_addr_list";
+    DNS_RR *addr_list = 0;
+    DNS_RR *rr;
+    int     res_opt = mx_names->dnssec_valid ? RES_USE_DNSSEC : 0;
+
+    for (rr = mx_names; rr; rr = rr->next) {
+       if (rr->type != T_MX)
+           msg_panic("%s: bad resource type: %d", myname, rr->type);
+       addr_list = addr_one(state, addr_list, (char *) rr->data, res_opt,
+                            rr->pref);
+    }
+    return (addr_list);
+}
+
+/* smtp_domain_addr - mail exchanger address lookup */
+
+static DNS_RR *domain_addr(STATE *state, char *domain)
+{
+    DNS_RR *mx_names;
+    DNS_RR *addr_list = 0;
+    int     r = 0;                     /* Resolver flags */
+
+    dsb_reset(state->why);
+
+#if (RES_USE_DNSSEC != 0) && (RES_USE_EDNS0 != 0)
+    r |= RES_USE_DNSSEC;
+#endif
+
+    switch (dns_lookup(domain, T_MX, r, &mx_names, (VSTRING *) 0,
+                      state->why->reason)) {
+    default:
+       dsb_status(state->why, "4.4.3");
+       break;
+    case DNS_INVAL:
+       dsb_status(state->why, "5.4.4");
+       break;
+    case DNS_FAIL:
+       dsb_status(state->why, "5.4.3");
+       break;
+    case DNS_OK:
+       mx_names = dns_rr_sort(mx_names, dns_rr_compare_pref_any);
+       addr_list = mx_addr_list(state, mx_names);
+       dns_rr_free(mx_names);
+       if (addr_list == 0) {
+           msg_warn("no MX host for %s has a valid address record", domain);
+           break;
+       }
+#define COMPARE_ADDR(flags) \
+       ((flags & MISC_FLAG_PREF_IPV6) ? dns_rr_compare_pref_ipv6 : \
+        (flags & MISC_FLAG_PREF_IPV4) ? dns_rr_compare_pref_ipv4 : \
+        dns_rr_compare_pref_any)
+       if (addr_list && addr_list->next) {
+           addr_list = dns_rr_shuffle(addr_list);
+           addr_list = dns_rr_sort(addr_list, COMPARE_ADDR(state->addr_pref));
+       }
+       break;
+    case DNS_NOTFOUND:
+       addr_list = host_addr(state, domain);
+       break;
+    }
+
+    return (addr_list);
+}
+
+/* host_addr - direct host lookup */
+
+static DNS_RR *host_addr(STATE *state, const char *host)
+{
+    DSN_BUF *why = state->why;
+    DNS_RR *addr_list;
+    int     res_opt = 0;
+
+    dsb_reset(why);                            /* Paranoia */
+
+#if (RES_USE_DNSSEC != 0) && (RES_USE_EDNS0 != 0)
+    res_opt |= RES_USE_DNSSEC;
+#endif
+
+#define PREF0  0
+    addr_list = addr_one(state, (DNS_RR *) 0, host, res_opt, PREF0);
+    if (addr_list && addr_list->next) {
+       addr_list = dns_rr_shuffle(addr_list);
+       if (inet_proto_info()->ai_family_list[1] != 0)
+           addr_list = dns_rr_sort(addr_list, COMPARE_ADDR(state->addr_pref));
+    }
+    return (addr_list);
+}
+
+/* dane_host_level - canidate host "dane" or degraded security level */
+
+static int dane_host_level(STATE *state, DNS_RR *addr)
+{
+    int     level = state->level;
+
+#ifdef USE_TLS
+    if (level == TLS_LEV_DANE) {
+       if (addr->dnssec_valid) {
+           if (state->log_mask & (TLS_LOG_CERTMATCH | TLS_LOG_VERBOSE))
+               tls_dane_verbose(1);
+           else
+               tls_dane_verbose(0);
+
+           /* See addr loop in connect_remote() */
+           if (state->ddane)
+               tls_dane_free(state->ddane);
+
+           /* When TLSA lookups fail, next host */
+           state->ddane = tls_dane_resolve(HNAME(addr), "tcp", state->port);
+           if (!state->ddane) {
+               dsb_simple(state->why, "4.7.5",
+                          "TLSA lookup error for %s:%u",
+                          HNAME(addr), ntohs(state->port));
+               return (TLS_LEV_INVALID);
+           }
+           /* If unusable or not found, same fallback to "secure" */
+           if (tls_dane_notfound(state->ddane)
+               || tls_dane_unusable(state->ddane)) {
+               if (msg_verbose)
+                   msg_info("no %sTLSA records found, "
+                            "resorting to \"secure\"",
+                            tls_dane_unusable(state->ddane) ?
+                            "usable " : "");
+               level = TLS_LEV_SECURE;
+           } else {
+               if (state->match)
+                   argv_free(state->match);
+               argv_add(state->match = argv_alloc(2),
+                        "hostname", "nexthop", ARGV_END);
+           }
+       } else {
+           level = TLS_LEV_SECURE;
+       }
+    }
+#endif
+
+    return (level);
+}
+
+/* parse_destination - parse host/port destination */
+
+static char *parse_destination(char *destination, char *def_service,
+                                      char **hostp, unsigned *portp)
+{
+    char   *buf = mystrdup(destination);
+    char   *service;
+    struct servent *sp;
+    char   *protocol = "tcp";
+    unsigned port;
+    const char *err;
+
+    if (msg_verbose)
+       msg_info("parse_destination: %s %s", destination, def_service);
+
+    /*
+     * Parse the host/port information. We're working with a copy of the
+     * destination argument so the parsing can be destructive.
+     */
+    if ((err = host_port(buf, hostp, (char *) 0, &service, def_service)) != 0)
+       msg_fatal("%s in server description: %s", err, destination);
+
+    /*
+     * Convert service to port number, network byte order.
+     */
+    if (alldig(service)) {
+       if ((port = atoi(service)) >= 65536 || port == 0)
+           msg_fatal("bad network port in destination: %s", destination);
+       *portp = htons(port);
+    } else {
+       if ((sp = getservbyname(service, protocol)) != 0)
+           *portp = sp->s_port;
+       else if (strcmp(service, "smtp") == 0)
+           *portp = htons(25);
+       else
+           msg_fatal("unknown service: %s/%s", service, protocol);
+    }
+    return (buf);
+}
+
+/* connect_remote - connect to TCP destination or log an error */
+
+static void connect_remote(STATE *state, char *dest)
+{
+    DNS_RR *addr;
+    char   *buf;
+    char   *domain;
+
+    /* When reconnecting use IP address of previous session */
+    if (state->addr == 0) {
+       buf = parse_destination(dest, state->smtp ? "smtp" : "24",
+                               &domain, &state->port);
+       if (!state->nexthop)
+           state->nexthop = mystrdup(domain);
+       if (state->smtp == 0 || *dest == '[')
+           state->addr = host_addr(state, domain);
+       else
+           state->addr = domain_addr(state, domain);
+       myfree(buf);
+
+       if (state->addr == 0) {
+           msg_info("Destination address lookup failed: %s",
+                    vstring_str(state->why->reason));
+           return;
+       }
+    }
+    for (addr = state->addr; addr; addr = addr->next) {
+       int     level = dane_host_level(state, addr);
+
+       if (level == TLS_LEV_INVALID
+           || (state->stream = connect_addr(state, addr)) == 0) {
+           msg_info("Failed to establish session to %s via %s: %s",
+                    dest, HNAME(addr), vstring_str(state->why->reason));
+           continue;
+       }
+       /* We have a connection */
+       state->level = level;
+       state->hostname = mystrdup(HNAME(addr));
+
+       /* We use the same address when reconnecting, so flush the rest. */
+       addr = dns_rr_copy(addr);
+       dns_rr_free(state->addr);
+       state->addr = addr;
+       break;
+    }
+}
+
+/* connect_dest - connect to given inet: or unix: destination */
+
+static int connect_dest(STATE *state)
+{
+    char   *dest = state->dest;
+
+    /*
+     * With LMTP we have direct-to-host delivery only. The destination may
+     * have multiple IP addresses.
+     */
+    if (state->smtp == 0) {
+       if (strncmp(dest, "unix:", 5) == 0) {
+           connect_unix(state, dest + 5);
+           if (!state->stream)
+               msg_info("Failed to establish session to %s: %s",
+                        dest, vstring_str(state->why->reason));
+           return (1);
+       }
+       if (strncmp(dest, "inet:", 5) == 0)
+           dest += 5;
+    }
+    connect_remote(state, dest);
+
+    return (state->stream == 0);
+}
+
+static void disconnect_dest(STATE *state)
+{
+#ifdef USE_TLS
+    if (state->tls_context)
+       tls_client_stop(state->tls_ctx, state->stream,
+                       smtp_tmout, 0, state->tls_context);
+    state->tls_context = 0;
+    if (state->ddane)
+       tls_dane_free(state->ddane);
+    state->ddane = 0;
+#endif
+
+    if (state->stream)
+       vstream_fclose(state->stream);
+    state->stream = 0;
+
+    if (state->namaddrport)
+       myfree(state->namaddrport);
+    state->namaddrport = 0;
+
+    if (state->addrport)
+       myfree(state->addrport);
+    state->addrport = 0;
+
+    /* Reused on reconnect */
+    if (state->reconnect <= 0) {
+       if (state->addr)
+           dns_rr_free(state->addr);
+       state->addr = 0;
+
+       if (state->nexthop)
+           myfree(state->nexthop);
+       state->nexthop = 0;
+    }
+    if (state->hostname)
+       myfree(state->hostname);
+    state->hostname = 0;
+
+    dsb_free(state->why);
+    vstring_free(state->buffer);
+}
+
+static int finger(STATE *state)
+{
+    int     err;
+
+    /*
+     * Make sure the SMTP server cannot run us out of memory by sending
+     * never-ending lines of text.
+     */
+    state->buffer = vstring_alloc(100);
+    vstring_ctl(state->buffer, VSTRING_CTL_MAXLEN,
+               (ssize_t) var_line_limit, 0);
+    state->why = dsb_create();
+
+    if (!(err = connect_dest(state))) {
+       if (state->pass == 1)
+           msg_info("Connected to %s", state->namaddrport);
+       err = doproto(state);
+    }
+    disconnect_dest(state);
+
+    if (err != 0)
+       return (1);
+
+#ifdef USE_TLS
+    if (state->reconnect > 0) {
+       int     cache_enabled;
+       int     cache_count;
+       int     cache_hits;
+
+       tlsmgrmem_status(&cache_enabled, &cache_count, &cache_hits);
+       if (cache_enabled && cache_count == 0) {
+           msg_info("Server declined session caching. Done reconnecting.");
+           state->reconnect = 0;
+       } else if (cache_hits > 0) {
+           msg_info("Found a previously used server.  Done reconnecting.");
+           state->reconnect = 0;
+       } else if (state->max_reconnect-- <= 0) {
+           msg_info("Maximum reconnect count reached.");
+           state->reconnect = 0;
+       }
+    }
+#endif
+
+    return (0);
+}
+
+#ifdef USE_TLS
+
+/* ssl_cleanup - free memory allocated in the OpenSSL library */
+
+static void ssl_cleanup(void)
+{
+    ERR_remove_state(0);
+    ENGINE_cleanup();
+    CONF_modules_unload(1);
+    ERR_free_strings();
+    EVP_cleanup();
+    CRYPTO_cleanup_all_ex_data();
+}
+
+#endif
+
+/* run - do what we were asked to do. */
+
+static int run(STATE *state)
+{
+
+    while (1) {
+       if (finger(state) != 0)
+           break;
+       if (state->reconnect <= 0)
+           break;
+       msg_info("Reconnecting after %d seconds", state->reconnect);
+       ++state->pass;
+       sleep(state->reconnect);
+    }
+
+    return (0);
+}
+
+/* cleanup - free memory allocated in main */
+
+static void cleanup(STATE *state)
+{
+#ifdef USE_TLS
+    if (state->tls_ctx != 0)
+       tls_free_app_context(state->tls_ctx);
+    if (state->tls_bio)
+       (void) BIO_free(state->tls_bio);
+    state->tls_bio = 0;
+
+    myfree(state->mdalg);
+    myfree(state->CApath);
+    myfree(state->CAfile);
+    if (state->options.level)
+       myfree(state->options.level);
+    myfree(state->options.logopts);
+    if (state->match)
+       argv_free(state->match);
+    if (state->options.tas)
+       argv_free(state->options.tas);
+    if (state->dane)
+       tls_dane_free(state->dane);
+
+    /* Flush and free DANE TLSA cache */
+    tls_dane_flush();
+    /* Flush and free memory tlsmgr cache */
+    tlsmgrmem_flush();
+    myfree(state->grade);
+    myfree(state->protocols);
+#endif
+    myfree(state->options.host_lookup);
+    myfree(state->dest);
+
+    mail_conf_flush();
+}
+
+/* usage - explain */
+
+static void usage(void)
+{
+#ifdef USE_TLS
+    fprintf(stderr, "usage: %s %s \\\n\t%s \\\n\t%s destination [match ...]\n",
+           var_procname, "[-acCStTv] [-d mdalg] [-g grade] [-p protocols] [-F CAfile.pem]",
+           "[-h host_lookup] [-l level] [-L logopts] [-m count]",
+           "[-o name=value] [-P CApath/] [-r delay]");
+#else
+    fprintf(stderr, "usage: %s [-acStTv] [-h host_lookup] [-o name=value] destination\n",
+           var_procname);
+#endif
+    exit(1);
+}
+
+/* tls_init - initialize application TLS library context */
+
+static void tls_init(STATE *state)
+{
+#ifdef USE_TLS
+    TLS_CLIENT_INIT_PROPS props;
+
+    if (state->level <= TLS_LEV_NONE)
+       return;
+
+    state->tls_ctx =
+       TLS_CLIENT_INIT(&props,
+                       log_param = "-L option",
+                       log_level = state->options.logopts,
+                       verifydepth = DEF_SMTP_TLS_SCERT_VD,
+                       cache_type = "memory",
+                       cert_file = "",
+                       key_file = "",
+                       dcert_file = "",
+                       dkey_file = "",
+                       eccert_file = "",
+                       eckey_file = "",
+                       CAfile = state->CAfile,
+                       CApath = state->CApath,
+                       mdalg = state->mdalg);
+#endif
+}
+
+/* override - update main.cf parameter */
+
+static void override(const char *nameval)
+{
+    char   *param_name;
+    char   *param_value;
+    char   *save = mystrdup(nameval);
+
+    if (split_nameval(save, &param_name, &param_value) != 0)
+       usage();
+    mail_conf_update(param_name, param_value);
+    myfree(save);
+}
+
+/* parse_options - (argc, argv) -> state */
+
+static void parse_options(STATE *state, int argc, char *argv[])
+{
+    int     c;
+
+    state->smtp = 1;
+    state->pass = 1;
+    state->reconnect = -1;
+    state->max_reconnect = 5;
+#ifdef USE_TLS
+    state->protocols = mystrdup("!SSLv2");
+    state->grade = mystrdup("medium");
+#endif
+    memset((char *) &state->options, 0, sizeof(state->options));
+    state->options.host_lookup = mystrdup("dns");
+
+#define OPTS "a:ch:o:St:T:v"
+#ifdef USE_TLS
+#define TLSOPTS "A:Cd:F:g:l:L:m:p:P:r:"
+
+    state->mdalg = mystrdup("sha1");
+    state->CApath = mystrdup("");
+    state->CAfile = mystrdup("");
+    state->options.tas = argv_alloc(1);
+    state->options.logopts = 0;
+    state->level = TLS_LEV_DANE;
+#else
+#define TLSOPTS ""
+    state->level = TLS_LEV_NONE;
+#endif
+
+    while ((c = GETOPT(argc, argv, OPTS TLSOPTS)) > 0) {
+       switch (c) {
+       default:
+           usage();
+           break;
+       case 'a':
+           state->options.addr_pref = mystrdup(optarg);
+           break;
+       case 'c':
+           state->nochat = 1;
+           break;
+       case 'h':
+           myfree(state->options.host_lookup);
+           state->options.host_lookup = mystrdup(optarg);
+           break;
+       case 'o':
+           override(optarg);
+           break;
+       case 'S':
+           state->smtp = 0;
+           break;
+       case 't':
+           conn_tmout = atoi(optarg);
+           break;
+       case 'T':
+           smtp_tmout = atoi(optarg);
+           break;
+       case 'v':
+           msg_verbose++;
+           break;
+#ifdef USE_TLS
+       case 'A':
+           argv_add(state->options.tas, optarg, ARGV_END);
+           break;
+       case 'C':
+           state->print_trust = 1;
+           break;
+       case 'd':
+           myfree(state->mdalg);
+           state->mdalg = mystrdup(optarg);
+           break;
+       case 'F':
+           myfree(state->CAfile);
+           state->CAfile = mystrdup(optarg);
+           break;
+       case 'g':
+           myfree(state->grade);
+           state->grade = mystrdup(optarg);
+           break;
+       case 'l':
+           if (state->options.level)
+               myfree(state->options.level);
+           state->options.level = mystrdup(optarg);
+           break;
+       case 'L':
+           if (state->options.logopts)
+               myfree(state->options.logopts);
+           state->options.logopts = mystrdup(optarg);
+           break;
+       case 'm':
+           state->max_reconnect = atoi(optarg);
+           break;
+       case 'p':
+           myfree(state->protocols);
+           state->protocols = mystrdup(optarg);
+           break;
+       case 'P':
+           myfree(state->CApath);
+           state->CApath = mystrdup(optarg);
+           break;
+       case 'r':
+           state->reconnect = atoi(optarg);
+           break;
+#endif
+       }
+    }
+
+    /*
+     * Address family preference.
+     */
+    state->addr_pref =
+       name_code(addr_pref_map, NAME_CODE_FLAG_NONE, state->options.addr_pref ?
+                 state->options.addr_pref : "any");
+    if (state->addr_pref < 0)
+       msg_fatal("bad '-a' option value: %s", state->options.addr_pref);
+
+    /*
+     * Select hostname lookup mechanisms.
+     */
+    state->host_lookup =
+       name_mask("-h option", lookup_masks, state->options.host_lookup ?
+                 state->options.host_lookup : "dns");
+
+#ifdef USE_TLS
+
+    if (state->reconnect < 0)
+       tlsmgrmem_disable();
+
+    if (state->options.logopts == 0)
+       state->options.logopts = mystrdup("routine,certmatch");
+    state->log_mask = tls_log_mask("-L option", state->options.logopts);
+
+    if (state->options.level) {
+       state->level = tls_level_lookup(state->options.level);
+
+       switch (state->level) {
+       case TLS_LEV_DANE_ONLY:
+           state->level = TLS_LEV_DANE;
+           break;
+       case TLS_LEV_NONE:
+           return;
+       case TLS_LEV_INVALID:
+           msg_fatal("Invalid TLS level \"%s\"", state->options.level);
+       }
+    }
+
+    /*
+     * We first call tls_init(), which ultimately calls SSL_library_init(),
+     * since otherwise we can't tell whether we have the message digests
+     * required for DANE support.
+     */
+    tls_init(state);
+    if (state->level == TLS_LEV_DANE && !tls_dane_avail()) {
+       msg_warn("The \"dane\" TLS security level is not available");
+       state->level = TLS_LEV_SECURE;
+    }
+    state->tls_bio = 0;
+    if (state->print_trust)
+       state->tls_bio = BIO_new_fp(stdout, BIO_NOCLOSE);
+
+#endif
+}
+
+/* parse_match - process match arguments */
+
+static void parse_match(STATE *state, int argc, char *argv[])
+{
+#ifdef USE_TLS
+
+    argc -= optind;
+    argv += optind;
+
+    switch (state->level) {
+    case TLS_LEV_SECURE:
+       state->match = argv_alloc(2);
+       while (*argv)
+           argv_split_append(state->match, *argv++, "");
+       if (state->match->argc == 0)
+           argv_add(state->match, "nexthop", "dot-nexthop", ARGV_END);
+       break;
+    case TLS_LEV_VERIFY:
+       state->match = argv_alloc(1);
+       while (*argv)
+           argv_split_append(state->match, *argv++, "");
+       if (state->match->argc == 0)
+           argv_add(state->match, "hostname", ARGV_END);
+       break;
+    case TLS_LEV_FPRINT:
+       state->dane = tls_dane_alloc(TLS_DANE_FLAG_MIXED);
+       while (*argv)
+           tls_dane_split((TLS_DANE *) state->dane, TLS_DANE_EE, TLS_DANE_PKEY,
+                          state->mdalg, *argv++, "");
+       tls_dane_final((TLS_DANE *) state->dane);
+       break;
+    case TLS_LEV_DANE:
+       state->match = argv_alloc(2);
+       argv_add(state->match, "nexthop", "hostname", ARGV_END);
+       break;
+    }
+#endif
+}
+
+/* parse_tas - process '-A' trust anchor file option */
+
+static void parse_tas(STATE *state)
+{
+#ifdef USE_TLS
+    char  **file;
+
+    if (!state->options.tas->argc)
+       return;
+
+    switch (state->level) {
+    default:
+       return;
+    case TLS_LEV_SECURE:
+    case TLS_LEV_VERIFY:
+       state->dane = tls_dane_alloc(TLS_DANE_FLAG_MIXED);
+       for (file = state->options.tas->argv; *file; ++file) {
+           if (!tls_dane_load_trustfile((TLS_DANE *) state->dane, *file))
+               break;
+       }
+       if (*file)
+           msg_fatal("Failed to load trust anchor file: %s", *file);
+       tls_dane_final((TLS_DANE *) state->dane);
+       break;
+    }
+#endif
+}
+
+
+int     main(int argc, char *argv[])
+{
+    static STATE state;
+    char   *loopenv = getenv("VALGRINDLOOP");
+    int     loop = loopenv ? atoi(loopenv) : 1;
+
+    /* Don't die when a peer goes away unexpectedly. */
+    signal(SIGPIPE, SIG_IGN);
+
+    /* We're a diagnostic utility, so diagnostic messages go to stdout. */
+    var_procname = mystrdup(basename(argv[0]));
+    set_mail_conf_str(VAR_PROCNAME, var_procname);
+    msg_vstream_init(var_procname, VSTREAM_OUT);
+
+    /*
+     * Load main.cf, parse command-line options, then process main.cf
+     * settings plus any command-line "-o" overrides.
+     */
+    mail_conf_suck();
+    parse_options(&state, argc, argv);
+    mail_params_init();
+
+    parse_match(&state, argc, argv);
+    parse_tas(&state);
+
+    /* The first non-option argument is the destination. */
+    if (argc < optind)
+       usage();
+    state.dest = mystrdup(argv[optind]);
+
+    /* Don't talk to remote systems as root */
+    if (!geteuid())
+       chroot_uid(0, var_mail_owner);
+
+    while (loop-- > 0)
+       run(&state);
+
+    /* Be valgrind friendly and clean-up */
+    cleanup(&state);
+#ifdef USE_TLS
+    ssl_cleanup();
+#endif
+
+    return (0);
+}
diff --git a/postfix/src/posttls-finger/tlsmgrmem.c b/postfix/src/posttls-finger/tlsmgrmem.c
new file mode 100644 (file)
index 0000000..d6b09d8
--- /dev/null
@@ -0,0 +1,142 @@
+/*++
+/* NAME
+/*     tlsmgrmem 3
+/* SUMMARY
+/*     Memory-based TLS manager interface for tlsfinger(1).
+/* SYNOPSIS
+/*     #ifdef  USE_TLS
+/*     #include <tlsmgrmem.h>
+/*
+/*     void    tlsmgrmem_disable()
+/*
+/*     void    tlsmgrmem_status(enable, count, hits)
+/*     int     *enable;
+/*     int     *count;
+/*     int     *hits;
+/*
+/*     void    tlsmgrmem_flush()
+/*     #endif
+/* DESCRIPTION
+/*     tlsmgrmem_disable() disables the in-memory TLS session cache.
+/*
+/*     tlsmgrmem_status() reports whether the cache is enabled, the
+/*     number of entries in the cache, and the number of cache hits.
+/*     If any of the return pointers are null, that item is not reported.
+/*
+/*     tlsmgrmem_flush() flushes any cached data and frees the cache.
+/* LICENSE
+/* .ad
+/* .fi
+/*     The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/*     Wietse Venema
+/*     IBM T.J. Watson Research
+/*     P.O. Box 704
+/*     Yorktown Heights, NY 10598, USA
+/*
+/*     Viktor Dukhovni
+/*--*/
+
+#include <sys_defs.h>
+
+#ifdef USE_TLS
+#include <htable.h>
+#include <vstring.h>
+#include <tls_mgr.h>
+
+#include "tlsmgrmem.h"
+
+static HTABLE *tls_cache;
+static int cache_enabled = 1;
+static int cache_count;
+static int cache_hits;
+typedef void (*free_func) (char *);
+static free_func free_value = (free_func) vstring_free;
+
+void    tlsmgrmem_disable(void)
+{
+    cache_enabled = 0;
+}
+
+void    tlsmgrmem_flush(void)
+{
+    if (!tls_cache)
+       return;
+    htable_free(tls_cache, free_value);
+}
+
+void    tlsmgrmem_status(int *enabled, int *count, int *hits)
+{
+    if (enabled)
+       *enabled = cache_enabled;
+    if (count)
+       *count = cache_count;
+    if (hits)
+       *hits = cache_hits;
+}
+
+/* tls_mgr_* - Local cache and stubs that do not talk to the TLS manager */
+
+int     tls_mgr_seed(VSTRING *buf, int len)
+{
+    return (TLS_MGR_STAT_OK);
+}
+
+int     tls_mgr_policy(const char *unused_type, int *cachable)
+{
+    if (cache_enabled && tls_cache == 0)
+       tls_cache = htable_create(1);
+    *cachable = cache_enabled;
+    return (TLS_MGR_STAT_OK);
+}
+
+int     tls_mgr_lookup(const char *unused_type, const char *key, VSTRING *buf)
+{
+    VSTRING *s;
+
+    if (tls_cache == 0)
+       return TLS_MGR_STAT_ERR;
+
+    if ((s = (VSTRING *) htable_find(tls_cache, key)) == 0)
+       return TLS_MGR_STAT_ERR;
+
+    vstring_memcpy(buf, vstring_str(s), VSTRING_LEN(s));
+
+    ++cache_hits;
+    return (TLS_MGR_STAT_OK);
+}
+
+int     tls_mgr_update(const char *unused_type, const char *key,
+                              const char *buf, ssize_t len)
+{
+    HTABLE_INFO *ent;
+    VSTRING *s;
+
+    if (tls_cache == 0)
+       return TLS_MGR_STAT_ERR;
+
+    if ((ent = htable_locate(tls_cache, key)) == 0) {
+       s = vstring_alloc(len);
+       ent = htable_enter(tls_cache, key, (char *) s);
+    } else {
+       s = (VSTRING *) ent->value;
+    }
+    vstring_memcpy(s, buf, len);
+
+    ++cache_count;
+    return (TLS_MGR_STAT_OK);
+}
+
+int     tls_mgr_delete(const char *unused_type, const char *key)
+{
+    if (tls_cache == 0)
+       return TLS_MGR_STAT_ERR;
+
+    if (htable_locate(tls_cache, key)) {
+       htable_delete(tls_cache, key, free_value);
+       --cache_count;
+    }
+    return (TLS_MGR_STAT_OK);
+}
+
+#endif
diff --git a/postfix/src/posttls-finger/tlsmgrmem.h b/postfix/src/posttls-finger/tlsmgrmem.h
new file mode 100644 (file)
index 0000000..706b206
--- /dev/null
@@ -0,0 +1,28 @@
+/*++
+/* NAME
+/*     tlsmgrmem 3
+/* SUMMARY
+/*     Memory-based TLS manager interface for tlsfinger(1).
+/* SYNOPSIS
+/*     #include <tlsmgrmem.h>
+/* DESCRIPTION
+/* .nf
+
+ /* External interface. */
+
+extern void tlsmgrmem_disable(void);
+extern void tlsmgrmem_status(int *, int *, int *);
+extern void tlsmgrmem_flush(void);
+
+/* LICENSE
+/* .ad
+/* .fi
+/*     The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/*     Wietse Venema
+/*     IBM T.J. Watson Research
+/*     P.O. Box 704
+/*     Yorktown Heights, NY 10598, USA
+/*
+/*     Viktor Dukhovni
+/*--*/
index 98ba32cbfceb1e8159763a4961e0726b9ea9ebbb..35fa14e83e2bba59cdb310b6009fdbd2cc05384e 100644 (file)
@@ -1,12 +1,12 @@
 SHELL  = /bin/sh
 SRCS   = smtp.c smtp_connect.c smtp_proto.c smtp_chat.c smtp_session.c \
-       smtp_addr.c smtp_trouble.c smtp_state.c smtp_rcpt.c smtp_tls_sess.c \
+       smtp_addr.c smtp_trouble.c smtp_state.c smtp_rcpt.c smtp_tls_policy.c \
        smtp_sasl_proto.c smtp_sasl_glue.c smtp_reuse.c smtp_map11.c \
-       smtp_sasl_auth_cache.c
+       smtp_sasl_auth_cache.c smtp_key.c
 OBJS   = smtp.o smtp_connect.o smtp_proto.o smtp_chat.o smtp_session.o \
-       smtp_addr.o smtp_trouble.o smtp_state.o smtp_rcpt.o smtp_tls_sess.o \
+       smtp_addr.o smtp_trouble.o smtp_state.o smtp_rcpt.o smtp_tls_policy.o \
        smtp_sasl_proto.o smtp_sasl_glue.o smtp_reuse.o smtp_map11.o \
-       smtp_sasl_auth_cache.o
+       smtp_sasl_auth_cache.o smtp_key.o
 HDRS   = smtp.h smtp_sasl.h smtp_addr.h smtp_reuse.h smtp_sasl_auth_cache.h
 TESTSRC        = 
 DEFS   = -I. -I$(INC_DIR) -D$(SYSTYPE)
@@ -260,6 +260,37 @@ smtp_connect.o: smtp.h
 smtp_connect.o: smtp_addr.h
 smtp_connect.o: smtp_connect.c
 smtp_connect.o: smtp_reuse.h
+smtp_key.o: ../../include/argv.h
+smtp_key.o: ../../include/attr.h
+smtp_key.o: ../../include/base64_code.h
+smtp_key.o: ../../include/deliver_request.h
+smtp_key.o: ../../include/dict.h
+smtp_key.o: ../../include/dsn.h
+smtp_key.o: ../../include/dsn_buf.h
+smtp_key.o: ../../include/header_body_checks.h
+smtp_key.o: ../../include/header_opts.h
+smtp_key.o: ../../include/htable.h
+smtp_key.o: ../../include/mail_params.h
+smtp_key.o: ../../include/maps.h
+smtp_key.o: ../../include/match_list.h
+smtp_key.o: ../../include/mime_state.h
+smtp_key.o: ../../include/msg.h
+smtp_key.o: ../../include/msg_stats.h
+smtp_key.o: ../../include/myflock.h
+smtp_key.o: ../../include/name_code.h
+smtp_key.o: ../../include/name_mask.h
+smtp_key.o: ../../include/recipient_list.h
+smtp_key.o: ../../include/resolve_clnt.h
+smtp_key.o: ../../include/scache.h
+smtp_key.o: ../../include/string_list.h
+smtp_key.o: ../../include/sys_defs.h
+smtp_key.o: ../../include/tls.h
+smtp_key.o: ../../include/tok822.h
+smtp_key.o: ../../include/vbuf.h
+smtp_key.o: ../../include/vstream.h
+smtp_key.o: ../../include/vstring.h
+smtp_key.o: smtp.h
+smtp_key.o: smtp_key.c
 smtp_map11.o: ../../include/argv.h
 smtp_map11.o: ../../include/attr.h
 smtp_map11.o: ../../include/deliver_request.h
@@ -546,13 +577,11 @@ smtp_session.o: ../../include/name_mask.h
 smtp_session.o: ../../include/recipient_list.h
 smtp_session.o: ../../include/resolve_clnt.h
 smtp_session.o: ../../include/scache.h
-smtp_session.o: ../../include/smtp_stream.h
 smtp_session.o: ../../include/string_list.h
 smtp_session.o: ../../include/stringops.h
 smtp_session.o: ../../include/sys_defs.h
 smtp_session.o: ../../include/tls.h
 smtp_session.o: ../../include/tok822.h
-smtp_session.o: ../../include/valid_hostname.h
 smtp_session.o: ../../include/vbuf.h
 smtp_session.o: ../../include/vstream.h
 smtp_session.o: ../../include/vstring.h
@@ -591,39 +620,40 @@ smtp_state.o: ../../include/vstring.h
 smtp_state.o: smtp.h
 smtp_state.o: smtp_sasl.h
 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/deliver_request.h
-smtp_tls_sess.o: ../../include/dict.h
-smtp_tls_sess.o: ../../include/dsn.h
-smtp_tls_sess.o: ../../include/dsn_buf.h
-smtp_tls_sess.o: ../../include/header_body_checks.h
-smtp_tls_sess.o: ../../include/header_opts.h
-smtp_tls_sess.o: ../../include/htable.h
-smtp_tls_sess.o: ../../include/mail_params.h
-smtp_tls_sess.o: ../../include/maps.h
-smtp_tls_sess.o: ../../include/match_list.h
-smtp_tls_sess.o: ../../include/mime_state.h
-smtp_tls_sess.o: ../../include/msg.h
-smtp_tls_sess.o: ../../include/msg_stats.h
-smtp_tls_sess.o: ../../include/myflock.h
-smtp_tls_sess.o: ../../include/mymalloc.h
-smtp_tls_sess.o: ../../include/name_code.h
-smtp_tls_sess.o: ../../include/name_mask.h
-smtp_tls_sess.o: ../../include/recipient_list.h
-smtp_tls_sess.o: ../../include/resolve_clnt.h
-smtp_tls_sess.o: ../../include/scache.h
-smtp_tls_sess.o: ../../include/string_list.h
-smtp_tls_sess.o: ../../include/stringops.h
-smtp_tls_sess.o: ../../include/sys_defs.h
-smtp_tls_sess.o: ../../include/tls.h
-smtp_tls_sess.o: ../../include/tok822.h
-smtp_tls_sess.o: ../../include/valid_hostname.h
-smtp_tls_sess.o: ../../include/vbuf.h
-smtp_tls_sess.o: ../../include/vstream.h
-smtp_tls_sess.o: ../../include/vstring.h
-smtp_tls_sess.o: smtp.h
-smtp_tls_sess.o: smtp_tls_sess.c
+smtp_tls_policy.o: ../../include/argv.h
+smtp_tls_policy.o: ../../include/attr.h
+smtp_tls_policy.o: ../../include/ctable.h
+smtp_tls_policy.o: ../../include/deliver_request.h
+smtp_tls_policy.o: ../../include/dict.h
+smtp_tls_policy.o: ../../include/dsn.h
+smtp_tls_policy.o: ../../include/dsn_buf.h
+smtp_tls_policy.o: ../../include/header_body_checks.h
+smtp_tls_policy.o: ../../include/header_opts.h
+smtp_tls_policy.o: ../../include/htable.h
+smtp_tls_policy.o: ../../include/mail_params.h
+smtp_tls_policy.o: ../../include/maps.h
+smtp_tls_policy.o: ../../include/match_list.h
+smtp_tls_policy.o: ../../include/mime_state.h
+smtp_tls_policy.o: ../../include/msg.h
+smtp_tls_policy.o: ../../include/msg_stats.h
+smtp_tls_policy.o: ../../include/myflock.h
+smtp_tls_policy.o: ../../include/mymalloc.h
+smtp_tls_policy.o: ../../include/name_code.h
+smtp_tls_policy.o: ../../include/name_mask.h
+smtp_tls_policy.o: ../../include/recipient_list.h
+smtp_tls_policy.o: ../../include/resolve_clnt.h
+smtp_tls_policy.o: ../../include/scache.h
+smtp_tls_policy.o: ../../include/string_list.h
+smtp_tls_policy.o: ../../include/stringops.h
+smtp_tls_policy.o: ../../include/sys_defs.h
+smtp_tls_policy.o: ../../include/tls.h
+smtp_tls_policy.o: ../../include/tok822.h
+smtp_tls_policy.o: ../../include/valid_hostname.h
+smtp_tls_policy.o: ../../include/vbuf.h
+smtp_tls_policy.o: ../../include/vstream.h
+smtp_tls_policy.o: ../../include/vstring.h
+smtp_tls_policy.o: smtp.h
+smtp_tls_policy.o: smtp_tls_policy.c
 smtp_trouble.o: ../../include/argv.h
 smtp_trouble.o: ../../include/attr.h
 smtp_trouble.o: ../../include/bounce.h
index cb645d5679cd65c2e7f4d2bbc2ebe4080f0814db..40b7b70bf2dcfc7c45f72a56e186a5024b0f0530 100644 (file)
@@ -22,6 +22,7 @@
        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,
index 841910b0b949cea5185ce01ad4ff8550cbd8914a..eb27db1904300cece4ff97b649ffece2bb9a9fca 100644 (file)
 /*     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.
+/* .IP "\fBsmtp_tls_dane_notfound_tlsa_level (may)\fR"
+/*     The "degraded" security level when the "dane" security level
+/*     is specified, but no validated DANE TLSA records are published.
+/* .IP "\fBsmtp_tls_dane_unusable_tlsa_level (encrypt)\fR"
+/*     The "degraded" security level when the "dane" security level
+/*     is specified, validated DANE TLSA records are present, but none are
+/*     usable.
+/* .IP "\fBtls_dane_trust_anchor_digest_enable (trust-anchor-assertion)\fR"
+/*     RFC 6698 trust-anchor digest support in the Postfix TLS library.
 /* OBSOLETE STARTTLS CONTROLS
 /* .ad
 /* .fi
@@ -825,6 +839,7 @@ int     var_smtp_tls_scert_vd;
 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;
@@ -1064,10 +1079,12 @@ static void pre_init(char *unused_name, char **unused_argv)
        switch (tls_level_lookup(var_smtp_tls_level)) {
        case TLS_LEV_SECURE:
        case TLS_LEV_VERIFY:
+       case TLS_LEV_DANE_ONLY:
        case TLS_LEV_FPRINT:
        case TLS_LEV_ENCRYPT:
            var_smtp_use_tls = var_smtp_enforce_tls = 1;
            break;
+       case TLS_LEV_DANE:
        case TLS_LEV_MAY:
            var_smtp_use_tls = 1;
            var_smtp_enforce_tls = 0;
index 7ccd29bfb8f9618044c36204bff141f51cbc43ff..fbf9b0ab53493893fdbba1f083626eea5a871a94 100644 (file)
   */
 #include <tls.h>
 
+ /*
+  * Global iterator support. This is updated by the connection-management
+  * loop, and contains dynamic context that appears in lookup keys for SASL
+  * passwords, TLS policy, cached SMTP connections, and cached TLS session
+  * keys.
+  * 
+  * For consistency and maintainability, context that is used in more than one
+  * lookup key is formatted with smtp_key_format().
+  */
+typedef struct SMTP_ITERATOR {
+    /* Public members. */
+    VSTRING *request_nexthop;          /* request nexhop or empty */
+    VSTRING *dest;                     /* current nexthop */
+    VSTRING *host;                     /* hostname or empty */
+    VSTRING *addr;                     /* printable address or empty */
+    unsigned port;                     /* network byte order or null */
+    struct DNS_RR *rr;                 /* DNS resource record or null */
+    /* Private members. */
+    VSTRING *saved_dest;               /* saved current nexthop */
+    struct SMTP_STATE *parent;         /* parent linkage */
+} SMTP_ITERATOR;
+
+#define SMTP_ITER_INIT(iter, _dest, _host, _addr, _port, _rr, state) do { \
+       vstring_strcpy((iter)->dest, (_dest)); \
+       vstring_strcpy((iter)->host, (_host)); \
+       vstring_strcpy((iter)->addr, (_addr)); \
+       (iter)->port = (_port); \
+       (iter)->rr = (_rr); \
+       vstring_strcpy((iter)->saved_dest, ""); \
+       (iter)->parent = (state); \
+    } while (0)
+
+#define SMTP_ITER_CLOBBER(iter, _dest, _host, _addr) do { \
+       vstring_strcpy((iter)->dest, (_dest)); \
+       vstring_strcpy((iter)->host, (_host)); \
+       vstring_strcpy((iter)->addr, (_addr)); \
+    } while (0)
+
+#define SMTP_ITER_SAVE_DEST(iter) do { \
+       vstring_strcpy((iter)->saved_dest, STR((iter)->dest)); \
+    } while (0)
+
+#define SMTP_ITER_RESTORE_DEST(iter) do { \
+       vstring_strcpy((iter)->dest, STR((iter)->saved_dest)); \
+    } while (0)
+
+ /*
+  * TLS Policy support.
+  */
+#ifdef USE_TLS
+
+typedef struct SMTP_TLS_POLICY {
+    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 */
+    DSN_BUF *why;                      /* Lookup error status */
+    TLS_DANE *dane;                    /* DANE TLSA digests */
+} SMTP_TLS_POLICY;
+
+ /*
+  * smtp_tls_policy.c
+  */
+extern void smtp_tls_list_init(void);
+extern int smtp_tls_policy_cache_query(DSN_BUF *, SMTP_TLS_POLICY *, SMTP_ITERATOR *);
+extern void smtp_tls_policy_cache_flush(void);
+
+ /*
+  * Macros must use distinct names for local temporary variables, otherwise
+  * there will be bugs due to shadowing. This happened when an earlier
+  * version of smtp_tls_policy_dummy() invoked smtp_tls_policy_init(), but it
+  * could also happen without macro nesting.
+  * 
+  * General principle: use all or part of the macro name in each temporary
+  * variable name. Then, append suffixes to the names if needed.
+  */
+#define smtp_tls_policy_dummy(t) do { \
+       SMTP_TLS_POLICY *_tls_policy_dummy_tmp = (t); \
+       smtp_tls_policy_init(_tls_policy_dummy_tmp, (DSN_BUF *) 0); \
+       _tls_policy_dummy_tmp->level = TLS_LEV_NONE; \
+    } while (0)
+
+ /* This macro is not part of the module external interface. */
+#define smtp_tls_policy_init(t, w) do { \
+       SMTP_TLS_POLICY *_tls_policy_init_tmp = (t); \
+       _tls_policy_init_tmp->protocols = 0; \
+       _tls_policy_init_tmp->grade = 0; \
+       _tls_policy_init_tmp->exclusions = 0; \
+       _tls_policy_init_tmp->matchargv = 0; \
+       _tls_policy_init_tmp->why = (w); \
+       _tls_policy_init_tmp->dane = 0; \
+    } while (0)
+
+#endif
+
  /*
   * State information associated with each SMTP delivery request.
   * Session-specific state is stored separately.
@@ -52,23 +148,25 @@ typedef struct SMTP_STATE {
     ssize_t space_left;                        /* output length control */
 
     /*
-     * Connection cache support. The (nexthop_lookup_mx, nexthop_domain,
-     * nexthop_port) triple is a parsed next-hop specification, and should be
-     * a data type by itself. The (service, nexthop_mumble) members specify
-     * the name under which the first good connection should be cached. The
-     * nexthop_mumble members are initialized by the connection management
-     * module. nexthop_domain is reset to null after one connection is saved
-     * under the (service, nexthop_mumble) label, or upon exit from the
-     * connection management module.
+     * Global iterator.
+     */
+    SMTP_ITERATOR iterator[1];         /* Usage: state->iterator->member */
+
+    /*
+     * Global iterator.
+     */
+#ifdef USE_TLS
+    SMTP_TLS_POLICY tls[1];            /* Usage: state->tls->member */
+#endif
+
+    /*
+     * Connection cache support.
      */
     HTABLE *cache_used;                        /* cached addresses that were used */
     VSTRING *dest_label;               /* cached logical/physical binding */
     VSTRING *dest_prop;                        /* binding properties, passivated */
     VSTRING *endp_label;               /* cached session physical endpoint */
     VSTRING *endp_prop;                        /* endpoint properties, passivated */
-    int     nexthop_lookup_mx;         /* do/don't MX expand nexthop_domain */
-    char   *nexthop_domain;            /* next-hop name or bare address */
-    unsigned nexthop_port;             /* next-hop TCP port, network order */
 
     /*
      * Flags and counters to control the handling of mail delivery errors.
@@ -85,18 +183,18 @@ typedef struct SMTP_STATE {
     DSN_BUF *why;                      /* on-the-fly formatting buffer */
 } SMTP_STATE;
 
-#define SET_NEXTHOP_STATE(state, lookup_mx, domain, port) { \
-       (state)->nexthop_lookup_mx = lookup_mx; \
-       (state)->nexthop_domain = mystrdup(domain); \
-       (state)->nexthop_port = port; \
+ /*
+  * TODO: use the new SMTP_ITER name space.
+  */
+#define SET_NEXTHOP_STATE(state, nexthop) { \
+       vstring_strcpy((state)->iterator->request_nexthop, nexthop); \
     }
 
 #define FREE_NEXTHOP_STATE(state) { \
-       myfree((state)->nexthop_domain); \
-       (state)->nexthop_domain = 0; \
+       STR((state)->iterator->request_nexthop)[0] = 0; \
     }
 
-#define HAVE_NEXTHOP_STATE(state) ((state)->nexthop_domain != 0)
+#define HAVE_NEXTHOP_STATE(state) (STR((state)->iterator->request_nexthop) != 0)
 
 
  /*
@@ -140,16 +238,14 @@ typedef struct SMTP_STATE {
   */
 #define SMTP_MISC_FLAG_LOOP_DETECT     (1<<0)
 #define        SMTP_MISC_FLAG_IN_STARTTLS      (1<<1)
-#define SMTP_MISC_FLAG_TLSA_HOST       (1<<2)
-#define SMTP_MISC_FLAG_FIRST_NEXTHOP   (1<<3)
-#define SMTP_MISC_FLAG_FINAL_NEXTHOP   (1<<4)
-#define SMTP_MISC_FLAG_FINAL_SERVER    (1<<5)
-#define SMTP_MISC_FLAG_CONN_LOAD       (1<<6)
-#define SMTP_MISC_FLAG_CONN_STORE      (1<<7)
-#define SMTP_MISC_FLAG_COMPLETE_SESSION        (1<<8)
-#define SMTP_MISC_FLAG_PREF_IPV6       (1<<9)
-#define SMTP_MISC_FLAG_PREF_IPV4       (1<<10)
-#define SMTP_MISC_FLAG_NO_TLS          (1<<11)
+#define SMTP_MISC_FLAG_FIRST_NEXTHOP   (1<<2)
+#define SMTP_MISC_FLAG_FINAL_NEXTHOP   (1<<3)
+#define SMTP_MISC_FLAG_FINAL_SERVER    (1<<4)
+#define SMTP_MISC_FLAG_CONN_LOAD       (1<<5)
+#define SMTP_MISC_FLAG_CONN_STORE      (1<<6)
+#define SMTP_MISC_FLAG_COMPLETE_SESSION        (1<<7)
+#define SMTP_MISC_FLAG_PREF_IPV6       (1<<8)
+#define SMTP_MISC_FLAG_PREF_IPV4       (1<<9)
 
 #define SMTP_MISC_FLAG_CONN_CACHE_MASK \
        (SMTP_MISC_FLAG_CONN_LOAD | SMTP_MISC_FLAG_CONN_STORE)
@@ -201,16 +297,6 @@ extern HBC_CHECKS *smtp_body_checks;       /* limited body checks */
  /*
   * smtp_session.c
   */
-#ifdef USE_TLS
-typedef struct SMTP_TLS_SESS {
-    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;
-
-#endif
 
 typedef struct SMTP_SESSION {
     VSTREAM *stream;                   /* network connection */
@@ -254,36 +340,23 @@ typedef struct SMTP_SESSION {
     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;              /* TEMPORARY */
 #endif
 
     SMTP_STATE *state;                 /* back link */
 } SMTP_SESSION;
 
-extern SMTP_SESSION *smtp_session_alloc(DSN_BUF *, const char *, const char *,
-                                               const char *, unsigned, int);
+extern SMTP_SESSION *smtp_session_alloc(VSTREAM *, SMTP_ITERATOR *, time_t, int);
 extern void smtp_session_new_stream(SMTP_SESSION *, VSTREAM *, time_t, int);
-extern int smtp_sess_tls_check(const char *, const char *, unsigned, int);
+extern int smtp_sess_plaintext_ok(SMTP_ITERATOR *, int);
 extern void smtp_session_free(SMTP_SESSION *);
 extern int smtp_session_passivate(SMTP_SESSION *, VSTRING *, VSTRING *);
-extern SMTP_SESSION *smtp_session_activate(int, VSTRING *, VSTRING *);
-
-#ifdef USE_TLS
-
- /*
-  * 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 *);
-
-#endif
+extern SMTP_SESSION *smtp_session_activate(int, SMTP_ITERATOR *, VSTRING *, VSTRING *);
 
  /*
   * What's in a name?  With DANE TLSA we need the rr->rname (if validated).
   */
-#define SMTP_HNAME(rr) ( (var_smtp_cname_overr || rr->validated) ? \
+#define SMTP_HNAME(rr) ( (var_smtp_cname_overr || rr->dnssec_valid) ? \
                         (rr)->rname : (rr)->qname )
 
  /*
@@ -484,6 +557,64 @@ extern int smtp_map11_external(VSTRING *, MAPS *, int);
 extern int smtp_map11_tree(TOK822 *, MAPS *, int);
 extern int smtp_map11_internal(VSTRING *, MAPS *, int);
 
+ /*
+  * smtp_key.c
+  */
+char   *smtp_key_prefix(VSTRING *, const char *, SMTP_ITERATOR *, int);
+
+#define SMTP_KEY_FLAG_SERVICE          (1<<0)  /* service name */
+#define SMTP_KEY_FLAG_SENDER           (1<<1)  /* sender address */
+#define SMTP_KEY_FLAG_REQ_NEXTHOP      (1<<2)  /* request nexthop */
+#define SMTP_KEY_FLAG_NEXTHOP          (1<<3)  /* current nexthop */
+#define SMTP_KEY_FLAG_HOSTNAME         (1<<4)  /* remote host name */
+#define SMTP_KEY_FLAG_ADDR             (1<<5)  /* remote address */
+#define SMTP_KEY_FLAG_PORT             (1<<6)  /* remote port */
+
+#define SMTP_KEY_MASK_ALL \
+       (SMTP_KEY_FLAG_SERVICE | SMTP_KEY_FLAG_SENDER | \
+       SMTP_KEY_FLAG_REQ_NEXTHOP | \
+       SMTP_KEY_FLAG_NEXTHOP | SMTP_KEY_FLAG_HOSTNAME | \
+       SMTP_KEY_FLAG_ADDR | SMTP_KEY_FLAG_PORT)
+
+ /*
+  * Conditional lookup-key flags for cached connections that may be
+  * SASL-authenticated with a per-{sender, nexthop, or hostname} credential.
+  * Each bit corresponds to one type of smtp_sasl_password_file lookup key,
+  * and is turned on only when the corresponding main.cf parameter is turned
+  * on.
+  */
+#define COND_SASL_SMTP_KEY_FLAG_SENDER \
+       ((var_smtp_sender_auth && *var_smtp_sasl_passwd) ? \
+           SMTP_KEY_FLAG_SENDER : 0)
+
+#define COND_SASL_SMTP_KEY_FLAG_NEXTHOP \
+       (*var_smtp_sasl_passwd ? SMTP_KEY_FLAG_NEXTHOP : 0)
+
+#define COND_SASL_SMTP_KEY_FLAG_HOSTNAME \
+       (*var_smtp_sasl_passwd ? SMTP_KEY_FLAG_HOSTNAME : 0)
+
+ /*
+  * Connection-cache destination lookup key. The SENDER attribute is a proxy
+  * for sender-dependent SASL credentials (or absence thereof), and prevents
+  * false connection sharing when different SASL credentials may be required
+  * for different deliveries to the same domain and port. The SERVICE
+  * attribute is a proxy for all request-independent configuration details.
+  */
+#define SMTP_KEY_MASK_SCACHE_DEST_LABEL \
+       (SMTP_KEY_FLAG_SERVICE | COND_SASL_SMTP_KEY_FLAG_SENDER \
+       | SMTP_KEY_FLAG_REQ_NEXTHOP)
+
+ /*
+  * Connection-cache endpoint lookup key. The SENDER, NEXTHOP, and HOSTNAME
+  * attributes are proxies for SASL credentials (or absence thereof), and
+  * prevent false connection sharing when different SASL credentials may be
+  * required for different deliveries to the same IP address and port.
+  */
+#define SMTP_KEY_MASK_SCACHE_ENDP_LABEL \
+       (SMTP_KEY_FLAG_SERVICE | COND_SASL_SMTP_KEY_FLAG_SENDER \
+       | COND_SASL_SMTP_KEY_FLAG_NEXTHOP | COND_SASL_SMTP_KEY_FLAG_HOSTNAME \
+       | SMTP_KEY_FLAG_ADDR | SMTP_KEY_FLAG_PORT)
+
  /*
   * Silly little macros.
   */
@@ -511,4 +642,7 @@ extern int smtp_mode;
 /*     Allgemeine Elektrotechnik
 /*     Universitaetsplatz 3-4
 /*     D-03044 Cottbus, Germany
+/*
+/*     Victor Duchovni
+/*     Morgan Stanley
 /*--*/
index 4f9853e138ff5326da88ee40546c8db3fe509974..b4b253e7be1669709371c232e40b2bfa0712c7ec 100644 (file)
@@ -237,7 +237,7 @@ static DNS_RR *smtp_addr_list(DNS_RR *mx_names, DSN_BUF *why)
 {
     DNS_RR *addr_list = 0;
     DNS_RR *rr;
-    int     res_opt = mx_names->validated ? RES_USE_DNSSEC : 0;
+    int     res_opt = mx_names->dnssec_valid ? RES_USE_DNSSEC : 0;
 
     /*
      * As long as we are able to look up any host address, we ignore problems
index aba88009ab9fc4149db9e24ec99fca60f998f845..337a8371bce1fb0a1baeee239346ff201683522e 100644 (file)
   * Forward declaration.
   */
 static SMTP_SESSION *smtp_connect_sock(int, struct sockaddr *, int,
-                                              const char *, const char *,
-                                              unsigned,
-                                              const char *, DSN_BUF *,
+                                              SMTP_ITERATOR *, DSN_BUF *,
                                               int);
 
 /* smtp_connect_unix - connect to UNIX-domain address */
 
-static SMTP_SESSION *smtp_connect_unix(const char *addr,
-                                              DSN_BUF *why,
+static SMTP_SESSION *smtp_connect_unix(SMTP_ITERATOR *iter, DSN_BUF *why,
                                               int sess_flags)
 {
     const char *myname = "smtp_connect_unix";
     struct sockaddr_un sock_un;
+    const char *addr = STR(iter->addr);
     int     len = strlen(addr);
     int     sock;
 
@@ -162,14 +160,12 @@ static SMTP_SESSION *smtp_connect_unix(const char *addr,
        msg_info("%s: trying: %s...", myname, addr);
 
     return (smtp_connect_sock(sock, (struct sockaddr *) & sock_un,
-                             sizeof(sock_un), var_myhostname, addr,
-                             0, addr, why, sess_flags));
+                             sizeof(sock_un), iter, why, sess_flags));
 }
 
 /* smtp_connect_addr - connect to explicit address */
 
-static SMTP_SESSION *smtp_connect_addr(const char *destination, DNS_RR *addr,
-                                              unsigned port, DSN_BUF *why,
+static SMTP_SESSION *smtp_connect_addr(SMTP_ITERATOR *iter, DSN_BUF *why,
                                               int sess_flags)
 {
     const char *myname = "smtp_connect_addr";
@@ -177,6 +173,8 @@ static SMTP_SESSION *smtp_connect_addr(const char *destination, DNS_RR *addr,
     struct sockaddr *sa = (struct sockaddr *) & ss;
     SOCKADDR_SIZE salen = sizeof(ss);
     MAI_HOSTADDR_STR hostaddr;
+    DNS_RR *addr = iter->rr;
+    unsigned port = iter->port;
     int     sock;
     char   *bind_addr;
     char   *bind_var;
@@ -267,25 +265,18 @@ static SMTP_SESSION *smtp_connect_addr(const char *destination, DNS_RR *addr,
     /*
      * Connect to the server.
      */
-    SOCKADDR_TO_HOSTADDR(sa, salen, &hostaddr, (MAI_SERVPORT_STR *) 0, 0);
     if (msg_verbose)
        msg_info("%s: trying: %s[%s] port %d...",
-                myname, SMTP_HNAME(addr), hostaddr.buf, ntohs(port));
-
-    if (addr->validated)
-       sess_flags |= SMTP_MISC_FLAG_TLSA_HOST;
+                myname, STR(iter->host), STR(iter->addr), ntohs(port));
 
-    return (smtp_connect_sock(sock, sa, salen, SMTP_HNAME(addr), hostaddr.buf,
-                             port, destination, why, sess_flags));
+    return (smtp_connect_sock(sock, sa, salen, iter, why, sess_flags));
 }
 
 /* smtp_connect_sock - connect a socket over some transport */
 
 static SMTP_SESSION *smtp_connect_sock(int sock, struct sockaddr * sa,
-                                              int salen, const char *name,
-                                              const char *addr,
-                                              unsigned port,
-                                              const char *destination,
+                                              int salen,
+                                              SMTP_ITERATOR *iter,
                                               DSN_BUF *why,
                                               int sess_flags)
 {
@@ -293,17 +284,9 @@ static SMTP_SESSION *smtp_connect_sock(int sock, struct sockaddr * sa,
     int     saved_errno;
     VSTREAM *stream;
     time_t  start_time;
-    SMTP_SESSION *session;
-
-    /*
-     * Session construction is cheap, and can now tempfail when TLSA lookups
-     * don't work at the DANE security level. This also handles table lookup
-     * errors more gracefully. So construct the session, and then connect. If
-     * the connection fails, tear down the session.
-     */
-    if ((session = smtp_session_alloc(why, destination, name, addr,
-                                     port, sess_flags)) == 0)
-       return (0);
+    const char *name = STR(iter->host);
+    const char *addr = STR(iter->addr);
+    unsigned port = iter->port;
 
     start_time = time((time_t *) 0);
     if (var_smtp_conn_tmout > 0) {
@@ -322,7 +305,6 @@ static SMTP_SESSION *smtp_connect_sock(int sock, struct sockaddr * sa,
        else
            dsb_simple(why, "4.4.1", "connect to %s[%s]: %m", name, addr);
        close(sock);
-       smtp_session_free(session);
        return (0);
     }
     stream = vstream_fdopen(sock, O_RDWR);
@@ -338,13 +320,9 @@ static SMTP_SESSION *smtp_connect_sock(int sock, struct sockaddr * sa,
        vstream_tweak_tcp(stream);
 
     /*
-     * Update the SMTP_SESSION state with this newly-created stream, and make
-     * it subject to the new-stream connection caching policy (as opposed to
-     * the reused-stream caching policy).
+     * Bundle up what we have into a nice SMTP_SESSION object.
      */
-    smtp_session_new_stream(session, stream, start_time, sess_flags);
-
-    return (session);
+    return (smtp_session_alloc(stream, iter, start_time, sess_flags));
 }
 
 /* smtp_parse_destination - parse host/port destination */
@@ -426,7 +404,8 @@ static void smtp_cleanup_session(SMTP_STATE *state)
     /* Redundant tests for safety... */
        && vstream_ferror(session->stream) == 0
        && vstream_feof(session->stream) == 0) {
-       smtp_save_session(state);
+       smtp_save_session(state, SMTP_KEY_MASK_SCACHE_DEST_LABEL,
+                         SMTP_KEY_MASK_SCACHE_ENDP_LABEL);
     } else {
        smtp_session_free(session);
     }
@@ -474,15 +453,6 @@ static void smtp_cache_policy(SMTP_STATE *state, const char *dest)
 
     state->misc_flags &= ~SMTP_MISC_FLAG_CONN_CACHE_MASK;
 
-    /*
-     * XXX Disable connection caching when sender-dependent authentication is
-     * enabled. We must not send someone elses mail over an authenticated
-     * connection, and we must not send mail that requires authentication
-     * over a connection that wasn't authenticated.
-     */
-    if (var_smtp_sender_auth)
-       return;
-
     if (smtp_cache_dest && string_list_match(smtp_cache_dest, dest)) {
        state->misc_flags |= SMTP_MISC_FLAG_CONN_CACHE_MASK;
     } else if (var_smtp_cache_demand) {
@@ -498,6 +468,7 @@ static void smtp_cache_policy(SMTP_STATE *state, const char *dest)
 static void smtp_connect_local(SMTP_STATE *state, const char *path)
 {
     const char *myname = "smtp_connect_local";
+    SMTP_ITERATOR *iter = state->iterator;
     SMTP_SESSION *session;
     DSN_BUF *why = state->why;
 
@@ -511,12 +482,17 @@ static void smtp_connect_local(SMTP_STATE *state, const char *path)
     smtp_cache_policy(state, path);
 
     /*
-     * XXX We assume that the session->addr member refers to a copy of the
+     * Here we ensure that the iter->addr member refers to a copy of the
      * UNIX-domain pathname, so that smtp_save_session() will cache the
      * connection using the pathname as the physical endpoint name.
+     * 
+     * We set dest=path for backwards compatibility.
      */
+#define NO_RR  ((DNS_RR *) 0)
 #define NO_PORT        0
 
+    SMTP_ITER_INIT(iter, path, var_myhostname, path, NO_PORT, NO_RR, state);
+
     /*
      * Opportunistic TLS for unix domain sockets does not make much sense,
      * since the channel is private, mere encryption without authentication
@@ -527,13 +503,27 @@ static void smtp_connect_local(SMTP_STATE *state, const char *path)
      * We don't know who is authenticating whom, so if a client cert is
      * available, "encrypt" may be a sensible policy. Otherwise, we also
      * downgrade "encrypt" to "none", this time just to avoid waste.
+     * 
+     * We use smtp_reuse_nexthop() instead of smtp_reuse_addr(), so that we can
+     * reuse a SASL-authenticated connection (however unlikely this scenario
+     * may be). The smtp_reuse_addr() interface currently supports only reuse
+     * of SASL-unauthenticated connections.
      */
+#ifdef USE_TLS
+    if (!smtp_tls_policy_cache_query(why, state->tls, iter)) {
+       msg_info("TLS policy lookup error for %s/%s: %s",
+                STR(iter->host), STR(iter->addr), STR(why->reason));
+       return;
+    }
+#endif
     if ((state->misc_flags & SMTP_MISC_FLAG_CONN_LOAD) == 0
-       || (session = smtp_reuse_addr(state, path, NO_PORT)) == 0)
-       session = smtp_connect_unix(path, why, state->misc_flags);
+       || (session = smtp_reuse_nexthop(state,
+                                    SMTP_KEY_MASK_SCACHE_DEST_LABEL)) == 0)
+       session = smtp_connect_unix(iter, why, state->misc_flags);
     if ((state->session = session) != 0) {
        session->state = state;
 #ifdef USE_TLS
+       session->tls = state->tls;              /* TEMPORARY */
        session->tls_nexthop = var_myhostname;  /* for TLS_LEV_SECURE */
        if (session->tls->level == TLS_LEV_MAY) {
            msg_warn("%s: opportunistic TLS encryption is not appropriate "
@@ -585,7 +575,7 @@ static void smtp_scrub_addr_list(HTABLE *cached_addr, DNS_RR **addr_list)
     for (addr = *addr_list; addr; addr = next) {
        next = addr->next;
        if (dns_rr_to_pa(addr, &hostaddr) == 0) {
-           msg_warn("cannot convert type %s resource record to socket address",
+           msg_warn("cannot convert type %s record to printable address",
                     dns_strtype(addr->type));
            continue;
        }
@@ -645,33 +635,51 @@ static void smtp_update_addr_list(DNS_RR **addr_list, const char *server_addr,
 
 /* smtp_reuse_session - try to use existing connection, return session count */
 
-static int smtp_reuse_session(SMTP_STATE *state, int lookup_mx,
-                                     const char *domain, unsigned port,
-                                  DNS_RR **addr_list, int domain_best_pref)
+static int smtp_reuse_session(SMTP_STATE *state, DNS_RR **addr_list,
+                                     int domain_best_pref)
 {
     int     session_count = 0;
     DNS_RR *addr;
     DNS_RR *next;
     MAI_HOSTADDR_STR hostaddr;
     SMTP_SESSION *session;
+    SMTP_ITERATOR *iter = state->iterator;
+    DSN_BUF *why = state->why;
 
     /*
-     * First, search the cache by logical destination. We truncate the server
+     * First, search the cache by request nexthop. We truncate the server
      * address list when all the sessions for this destination are used up,
      * to reduce the number of variables that need to be checked later.
      * 
      * Note: lookup by logical destination restores the "best MX" bit.
+     * 
+     * smtp_reuse_nexthop() clobbers the iterators's "dest" attribute. We save
+     * and restore it here, so that subsequent connections will use the
+     * proper nexthop information.
+     * 
+     * We request a dummy "TLS disabled" policy for connection-cache lookup by
+     * request nexthop only. If we find a saved connection, then we know that
+     * plaintext was permitted, because we never save a connection after
+     * turning on TLS.
      */
+#ifdef USE_TLS
+    smtp_tls_policy_dummy(state->tls);
+#endif
+    SMTP_ITER_SAVE_DEST(state->iterator);
     if (*addr_list && SMTP_RCPT_LEFT(state) > 0
-    && (session = smtp_reuse_domain(state, lookup_mx, domain, port)) != 0) {
+       && (session = smtp_reuse_nexthop(state, SMTP_KEY_MASK_SCACHE_DEST_LABEL)) != 0) {
        session_count = 1;
        smtp_update_addr_list(addr_list, session->addr, session_count);
        if ((state->misc_flags & SMTP_MISC_FLAG_FINAL_NEXTHOP)
            && *addr_list == 0)
            state->misc_flags |= SMTP_MISC_FLAG_FINAL_SERVER;
+#ifdef USE_TLS
+       session->tls = state->tls;              /* TEMPORARY */
+#endif
        smtp_xfer(state);
        smtp_cleanup_session(state);
     }
+    SMTP_ITER_RESTORE_DEST(state->iterator);
 
     /*
      * Second, search the cache by primary MX address. Again, we use address
@@ -679,13 +687,44 @@ static int smtp_reuse_session(SMTP_STATE *state, int lookup_mx,
      * 
      * XXX This loop is safe because smtp_update_addr_list() either truncates
      * the list to zero length, or removes at most one list element.
+     * 
+     * Currently, we use smtp_reuse_addr() only for SASL-unauthenticated
+     * connections. Furthermore, we rely on smtp_reuse_addr() to look up an
+     * existing SASL-unauthenticated connection only when a new connection
+     * would be guaranteed not to require SASL authentication.
+     * 
+     * In addition, we rely on smtp_reuse_addr() to look up an existing
+     * plaintext connection only when a new connection would be guaranteed
+     * not to use TLS.
+     * 
+     * For more precise control over reuse, the iterator should look up SASL and
+     * TLS policy as it evaluates mail exchangers in order, instead of
+     * relying on duplicate lookup request code in smtp_reuse(3) and
+     * smtp_session(3).
      */
     for (addr = *addr_list; SMTP_RCPT_LEFT(state) > 0 && addr; addr = next) {
        if (addr->pref != domain_best_pref)
            break;
        next = addr->next;
-       if (dns_rr_to_pa(addr, &hostaddr) != 0
-           && (session = smtp_reuse_addr(state, hostaddr.buf, port)) != 0) {
+       if (dns_rr_to_pa(addr, &hostaddr) == 0) {
+           msg_warn("cannot convert type %s record to printable address",
+                    dns_strtype(addr->type));
+           /* XXX Assume there is no code at the end of this loop. */
+           continue;
+       }
+       vstring_strcpy(iter->addr, hostaddr.buf);
+       vstring_strcpy(iter->host, SMTP_HNAME(addr));
+       iter->rr = addr;
+#ifdef USE_TLS
+       if (!smtp_tls_policy_cache_query(why, state->tls, iter)) {
+           msg_info("TLS policy lookup error for %s/%s: %s",
+                    STR(iter->dest), STR(iter->host), STR(why->reason));
+           continue;
+           /* XXX Assume there is no code at the end of this loop. */
+       }
+#endif
+       if ((session = smtp_reuse_addr(state,
+                                  SMTP_KEY_MASK_SCACHE_ENDP_LABEL)) != 0) {
            session->features |= SMTP_FEATURE_BEST_MX;
            session_count += 1;
            smtp_update_addr_list(addr_list, session->addr, session_count);
@@ -694,6 +733,9 @@ static int smtp_reuse_session(SMTP_STATE *state, int lookup_mx,
            if ((state->misc_flags & SMTP_MISC_FLAG_FINAL_NEXTHOP)
                && next == 0)
                state->misc_flags |= SMTP_MISC_FLAG_FINAL_SERVER;
+#ifdef USE_TLS
+           session->tls = state->tls;          /* TEMPORARY */
+#endif
            smtp_xfer(state);
            smtp_cleanup_session(state);
        }
@@ -707,6 +749,7 @@ static void smtp_connect_inet(SMTP_STATE *state, const char *nexthop,
                                      char *def_service)
 {
     DELIVER_REQUEST *request = state->request;
+    SMTP_ITERATOR *iter = state->iterator;
     ARGV   *sites;
     char   *dest;
     char  **cpp;
@@ -788,6 +831,11 @@ static void smtp_connect_inet(SMTP_STATE *state, const char *nexthop,
            msg_info("CLIENT wrappermode (port smtps/465) is unimplemented");
            msg_info("instead, send to (port submission/587) with STARTTLS");
        }
+#define NO_HOST        ""                              /* safety */
+#define NO_ADDR        ""                              /* safety */
+#define NO_RR  ((DNS_RR *) 0)                  /* safety */
+
+       SMTP_ITER_INIT(iter, dest, NO_HOST, NO_ADDR, port, NO_RR, state);
 
        /*
         * Resolve an SMTP server. Skip mail exchanger lookups when a quoted
@@ -835,10 +883,6 @@ static void smtp_connect_inet(SMTP_STATE *state, const char *nexthop,
         * good sessions will be stored under their specific server IP
         * address.
         * 
-        * XXX Replace sites->argv by (lookup_mx, domain, port) triples so we
-        * don't have to make clumsy ad-hoc copies and keep track of who
-        * free()s the memory.
-        * 
         * XXX smtp_session_cache_destinations specifies domain names without
         * :port, because : is already used for maptype:mapname. Because of
         * this limitation we use the bare domain without the optional [] or
@@ -847,16 +891,11 @@ static void smtp_connect_inet(SMTP_STATE *state, const char *nexthop,
         * Opportunistic (a.k.a. on-demand) session caching on request by the
         * queue manager. This is turned temporarily when a destination has a
         * high volume of mail in the active queue.
-        * 
-        * XXX Disable connection caching when sender-dependent authentication
-        * is enabled. We must not send someone elses mail over an
-        * authenticated connection, and we must not send mail that requires
-        * authentication over a connection that wasn't authenticated.
         */
        if (addr_list && (state->misc_flags & SMTP_MISC_FLAG_FIRST_NEXTHOP)) {
            smtp_cache_policy(state, domain);
            if (state->misc_flags & SMTP_MISC_FLAG_CONN_STORE)
-               SET_NEXTHOP_STATE(state, lookup_mx, domain, port);
+               SET_NEXTHOP_STATE(state, dest);
        }
 
        /*
@@ -873,8 +912,7 @@ static void smtp_connect_inet(SMTP_STATE *state, const char *nexthop,
            if (state->cache_used->used > 0)
                smtp_scrub_addr_list(state->cache_used, &addr_list);
            sess_count = addr_count =
-               smtp_reuse_session(state, lookup_mx, domain, port,
-                                  &addr_list, domain_best_pref);
+               smtp_reuse_session(state, &addr_list, domain_best_pref);
        } else
            sess_count = addr_count = 0;
 
@@ -895,35 +933,59 @@ static void smtp_connect_inet(SMTP_STATE *state, const char *nexthop,
         * Don't query the session cache for primary MX hosts. We already did
         * that in smtp_reuse_session(), and if any were found in the cache,
         * they were already deleted from the address list.
+        * 
+        * Currently, we use smtp_reuse_addr() only for SASL-unauthenticated
+        * connections. Furthermore, we rely on smtp_reuse_addr() to look up
+        * an existing SASL-unauthenticated connection only when a new
+        * connection would be guaranteed not to require SASL authentication.
+        * 
+        * In addition, we rely on smtp_reuse_addr() to look up an existing
+        * plaintext connection only when a new connection would be
+        * guaranteed not to use TLS.
         */
        for (addr = addr_list; SMTP_RCPT_LEFT(state) > 0 && addr; addr = next) {
            next = addr->next;
            if (++addr_count == var_smtp_mxaddr_limit)
                next = 0;
+           if (dns_rr_to_pa(addr, &hostaddr) == 0) {
+               msg_warn("cannot convert type %s record to printable address",
+                        dns_strtype(addr->type));
+               /* XXX Assume there is no code at the end of this loop. */
+               continue;
+           }
+           vstring_strcpy(iter->addr, hostaddr.buf);
+           vstring_strcpy(iter->host, SMTP_HNAME(addr));
+           iter->rr = addr;
+#ifdef USE_TLS
+           if (!smtp_tls_policy_cache_query(why, state->tls, iter)) {
+               msg_info("TLS policy lookup for %s/%s: %s",
+                        STR(iter->dest), STR(iter->host), STR(why->reason));
+               continue;
+               /* XXX Assume there is no code at the end of this loop. */
+           }
+           /* Disable TLS when retrying after a handshake failure */
+           if (retry_plain) {
+               state->tls->level = TLS_LEV_NONE;
+               retry_plain = 0;
+           }
+#endif
            if ((state->misc_flags & SMTP_MISC_FLAG_CONN_LOAD) == 0
                || addr->pref == domain_best_pref
-               || dns_rr_to_pa(addr, &hostaddr) == 0
-               || !(session = smtp_reuse_addr(state, hostaddr.buf, port)))
-               session = smtp_connect_addr(dest, addr, port, why,
-                                           state->misc_flags);
+               || !(session = smtp_reuse_addr(state,
+                                         SMTP_KEY_MASK_SCACHE_ENDP_LABEL)))
+               session = smtp_connect_addr(iter, why, state->misc_flags);
            if ((state->session = session) != 0) {
                session->state = state;
+#ifdef USE_TLS
+               session->tls = state->tls;      /* TEMPORARY */
+               session->tls_nexthop = domain;  /* for TLS_LEV_SECURE */
+#endif
                if (addr->pref == domain_best_pref)
                    session->features |= SMTP_FEATURE_BEST_MX;
                /* Don't count handshake errors towards the session limit. */
                if ((state->misc_flags & SMTP_MISC_FLAG_FINAL_NEXTHOP)
                    && next == 0)
                    state->misc_flags |= SMTP_MISC_FLAG_FINAL_SERVER;
-#ifdef USE_TLS
-               /* Disable TLS when retrying after a handshake failure */
-               if (retry_plain) {
-                   if (session->tls->level >= TLS_LEV_ENCRYPT)
-                       msg_panic("Plain-text retry wrong for mandatory TLS");
-                   session->tls->level = TLS_LEV_NONE;
-                   retry_plain = 0;
-               }
-               session->tls_nexthop = domain;  /* for TLS_LEV_SECURE */
-#endif
                if ((session->features & SMTP_FEATURE_FROM_CACHE) == 0
                    && smtp_helo(state) != 0) {
 #ifdef USE_TLS
@@ -961,7 +1023,7 @@ static void smtp_connect_inet(SMTP_STATE *state, const char *nexthop,
                /* The reason already includes the IP address and TCP port. */
                msg_info("%s", STR(why->reason));
            }
-           /* Insert: test if we must skip the remaining MX hosts. */
+           /* XXX Code above assumes there is no code at this loop ending. */
        }
        dns_rr_free(addr_list);
        myfree(dest_buf);
diff --git a/postfix/src/smtp/smtp_key.c b/postfix/src/smtp/smtp_key.c
new file mode 100644 (file)
index 0000000..1eb9996
--- /dev/null
@@ -0,0 +1,199 @@
+/*++
+/* NAME
+/*     smtp_key 3
+/* SUMMARY
+/*     cache/table lookup key management
+/* SYNOPSIS
+/*     #include "smtp.h"
+/*
+/*     char    *smtp_key_prefix(buffer, delim_na, iterator, context_flags)
+/*     VSTRING *buffer;
+/*     const char *delim_na;
+/*     SMTP_ITERATOR *iterator;
+/*     int     context_flags;
+/* DESCRIPTION
+/*     The Postfix SMTP server accesses caches and lookup tables,
+/*     using lookup keys that contain information from various
+/*     contexts: per-server configuration, per-request envelope,
+/*     and results from DNS queries.
+/*
+/*     These lookup keys sometimes share the same context information.
+/*     The primary purpose of this API is to ensure that this
+/*     shared context is used consistently, and that its use is
+/*     made explicit (both are needed to verify that there is no
+/*     false cache sharing).
+/*
+/*     smtp_key_prefix() constructs a lookup key prefix from context
+/*     that may be shared with other lookup keys. The user is free
+/*     to append additional application-specific context. The result
+/*     value is a pointer to the result text.
+/*
+/*     Arguments:
+/* .IP buffer
+/*     Storage for the result.
+/* .IP delim_na
+/*     The field delimiter character, and the optional place holder
+/*     character for a) information that is unavailable, b)
+/*     information that is inapplicable, or c) that would result
+/*     in an empty field.  Key fields that contain "delim_na"
+/*     characters will be base64-encoded.
+/*     Do not specify "delim_na" characters that are part of the
+/*     base64 character set.
+/* .IP iterator
+/*     Information that will be selected by the specified flags.
+/* .IP context_flags
+/*     Bit-wise OR of one or more of the following.
+/* .RS
+/* .IP SMTP_KEY_FLAG_SERVICE
+/*     The global service name. This is a proxy for
+/*     destination-independent and request-independent context.
+/* .IP SMTP_KEY_FLAG_SENDER
+/*     The envelope sender address. This is a proxy for sender-dependent
+/*     context, such as per-sender SASL authentication.
+/* .IP SMTP_KEY_FLAG_REQ_NEXTHOP
+/*     The request nexthop destination. This is a proxy for
+/*     destination-dependent, but host-independent context.
+/* .IP SMTP_KEY_FLAG_NEXTHOP
+/*     The current iterator's nexthop destination (request nexthop
+/*     or fallback nexthop, including optional [] and :port). This
+/*     is the form that users specify in a SASL or TLS lookup
+/*     tables.
+/* .IP SMTP_KEY_FLAG_HOSTNAME
+/*     The current iterator's remote hostname.
+/* .IP SMTP_KEY_FLAG_ADDR
+/*     The current iterator's remote address.
+/* .IP SMTP_KEY_FLAG_PORT
+/*     The current iterator's remote port.
+/* .RE
+/* DIAGNOSTICS
+/*     Panic: undefined flag or zero flags. Fatal: out of memory.
+/* LICENSE
+/* .ad
+/* .fi
+/*     The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/*     Wietse Venema
+/*     IBM T.J. Watson Research
+/*     P.O. Box 704
+/*     Yorktown Heights, NY 10598, USA
+/*--*/
+
+ /*
+  * System library.
+  */
+#include <sys_defs.h>
+#include <netinet/in.h>                        /* ntohs() for Solaris or BSD */
+#include <arpa/inet.h>                 /* ntohs() for Linux or BSD */
+#include <string.h>
+
+ /*
+  * Utility library.
+  */
+#include <msg.h>
+#include <vstring.h>
+#include <base64_code.h>
+
+ /*
+  * Global library.
+  */
+#include <mail_params.h>
+
+ /*
+  * Application-specific.
+  */
+#include <smtp.h>
+
+ /*
+  * We use a configurable field terminator and optional place holder for data
+  * that is unavailable or inapplicable. We base64-encode content that
+  * contains these characters, and content that needs obfuscation.
+  */
+
+/* smtp_key_append_na - append place-holder key field */
+
+static void smtp_key_append_na(VSTRING *buffer, const char *delim_na)
+{
+    if (delim_na[1] != 0)
+       VSTRING_ADDCH(buffer, delim_na[1]);
+    VSTRING_ADDCH(buffer, delim_na[0]);
+}
+
+/* smtp_key_append_str - append string-valued key field */
+
+static void smtp_key_append_str(VSTRING *buffer, const char *str,
+                                       const char *delim_na)
+{
+    if (str == 0 || str[0] == 0) {
+       smtp_key_append_na(buffer, delim_na);
+    } else if (str[strcspn(str, delim_na)] != 0) {
+       base64_encode_opt(buffer, str, strlen(str), BASE64_FLAG_APPEND);
+       VSTRING_ADDCH(buffer, delim_na[0]);
+    } else {
+       vstring_sprintf_append(buffer, "%s%c", str, delim_na[0]);
+    }
+}
+
+/* smtp_key_append_uint - append unsigned-valued key field */
+
+static void smtp_key_append_uint(VSTRING *buffer, unsigned num,
+                                        const char *delim_na)
+{
+    vstring_sprintf_append(buffer, "%u%c", num, delim_na[0]);
+}
+
+/* smtp_key_prefix - format common elements in lookup key */
+
+char   *smtp_key_prefix(VSTRING *buffer, const char *delim_na,
+                               SMTP_ITERATOR *iter, int flags)
+{
+    const char myname[] = "smtp_key_prefix";
+    SMTP_STATE *state = iter->parent;  /* private member */
+
+    /*
+     * Sanity checks.
+     */
+    if (state == 0)
+       msg_panic("%s: no parent state", myname);
+    if (flags & ~SMTP_KEY_MASK_ALL)
+       msg_panic("%s: unknown key flags 0x%x",
+                 myname, flags & ~SMTP_KEY_MASK_ALL);
+    if (flags == 0)
+       msg_panic("%s: zero flags", myname);
+
+    /*
+     * Initialize.
+     */
+    VSTRING_RESET(buffer);
+
+    /*
+     * Per-service and per-request context.
+     */
+    if (flags & SMTP_KEY_FLAG_SERVICE)
+       smtp_key_append_str(buffer, state->service, delim_na);
+    if (flags & SMTP_KEY_FLAG_SENDER)
+       smtp_key_append_str(buffer, state->request->sender, delim_na);
+
+    /*
+     * Per-destination context, non-canonicalized form.
+     */
+    if (flags & SMTP_KEY_FLAG_REQ_NEXTHOP)
+       smtp_key_append_str(buffer, STR(iter->request_nexthop), delim_na);
+    if (flags & SMTP_KEY_FLAG_NEXTHOP)
+       smtp_key_append_str(buffer, STR(iter->dest), delim_na);
+
+    /*
+     * Per-host context, canonicalized form.
+     */
+    if (flags & SMTP_KEY_FLAG_HOSTNAME)
+       smtp_key_append_str(buffer, STR(iter->host), delim_na);
+    if (flags & SMTP_KEY_FLAG_ADDR)
+       smtp_key_append_str(buffer, STR(iter->addr), delim_na);
+    if (flags & SMTP_KEY_FLAG_PORT)
+       smtp_key_append_uint(buffer, ntohs(iter->port), delim_na);
+
+    /* Similarly, provide unique TLS fingerprint when applicable. */
+
+    VSTRING_TERMINATE(buffer);
+
+    return STR(buffer);
+}
index cd25fdc4faef354a65f79e1fd9fa9ea3baec85b8..13f8723fa5f6d73501898ed2cf45f0926d2989a9 100644 (file)
@@ -23,6 +23,7 @@
        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,
index 929c74a670871102221d7a653699329ffd8a9e6e..7a69a5d9393b9657d86483431051f51e80979db0 100644 (file)
@@ -236,8 +236,12 @@ char   *xfer_request[SMTP_STATE_LAST] = {
      && (session->features & SMTP_FEATURE_8BITMIME) == 0 \
      && strcmp(request->encoding, MAIL_ATTR_ENC_7BIT) != 0)
 
+#ifdef USE_TLS
+
 static int smtp_start_tls(SMTP_STATE *);
 
+#endif
+
  /*
   * Call-back information for header/body checks. We don't provide call-backs
   * for actions that change the message delivery time or destination.
@@ -685,7 +689,7 @@ int     smtp_helo(SMTP_STATE *state)
             * although support for it was announced in the EHLO response.
             */
            session->features &= ~SMTP_FEATURE_STARTTLS;
-           if (session->tls->level >= TLS_LEV_ENCRYPT)
+           if (TLS_REQUIRED(session->tls->level))
                return (smtp_site_fail(state, session->host, resp,
                    "TLS is required, but host %s refused to start TLS: %s",
                                       session->namaddr,
@@ -700,7 +704,7 @@ int     smtp_helo(SMTP_STATE *state)
         * block. When TLS is required we must never, ever, end up in
         * plain-text mode.
         */
-       if (session->tls->level >= TLS_LEV_ENCRYPT) {
+       if (TLS_REQUIRED(session->tls->level)) {
            if (!(session->features & SMTP_FEATURE_STARTTLS)) {
                return (smtp_site_fail(state, DSN_BY_LOCAL_MTA,
                                       SMTP_RESP_FAKE(&fake, "4.7.4"),
@@ -776,8 +780,9 @@ static int smtp_start_tls(SMTP_STATE *state)
      * SSL session lookup key lengths.
      */
     serverid = vstring_alloc(10);
-    vstring_sprintf(serverid, "%s:%s:%u",
-                   state->service, session->addr, ntohs(session->port));
+    smtp_key_prefix(serverid, ":", state->iterator, SMTP_KEY_FLAG_SERVICE
+                   | SMTP_KEY_FLAG_ADDR
+                   | SMTP_KEY_FLAG_PORT);
 
     /*
      * As of Postfix 2.5, tls_client_start() tries hard to always complete
@@ -808,7 +813,8 @@ static int smtp_start_tls(SMTP_STATE *state)
                         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) {
@@ -848,21 +854,24 @@ static int smtp_start_tls(SMTP_STATE *state)
      * 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 and src/tls/tls.h. Levels above "encrypt" require
+     * matching.  Levels >= "dane" require CA or DNSSEC trust.
+     * 
+     * When DANE TLSA records specify an end-entity certificate, the trust and
+     * match bits always coincide, but it is fine to report the wrong
+     * end-entity certificate as untrusted rather than unmatched.
      */
-    if (session->tls->level >= TLS_LEV_VERIFY)
+    if (TLS_MUST_TRUST(session->tls->level))
        if (!TLS_CERT_IS_TRUSTED(session->tls_context))
            return (smtp_site_fail(state, DSN_BY_LOCAL_MTA,
                                   SMTP_RESP_FAKE(&fake, "4.7.5"),
                                   "Server certificate not trusted"));
-    if (session->tls->level >= TLS_LEV_DANE)
+    if (TLS_MUST_MATCH(session->tls->level))
        if (!TLS_CERT_IS_MATCHED(session->tls_context))
            return (smtp_site_fail(state, DSN_BY_LOCAL_MTA,
                                   SMTP_RESP_FAKE(&fake, "4.7.5"),
                                   "Server certificate not verified"));
 
-
     /* At this point there must not be any pending plaintext. */
     vstream_fpurge(session->stream, VSTREAM_PURGE_BOTH);
 
index fad09e1da2f1d23721abb5caee8d826d1dc7c47f..eeadbb20d4678445516843ad6d0ee048c05d4abe 100644 (file)
@@ -7,54 +7,54 @@
 /*     #include <smtp.h>
 /*     #include <smtp_reuse.h>
 /*
-/*     void    smtp_save_session(state)
+/*     void    smtp_save_session(state, name_key_flags, endp_key_flags)
 /*     SMTP_STATE *state;
+/*     int     name_key_flags;
+/*     int     endp_key_flags;
 /*
-/*     SMTP_SESSION *smtp_reuse_domain(state, lookup_mx, domain, port)
+/*     SMTP_SESSION *smtp_reuse_nexthop(state, name_key_flags)
 /*     SMTP_STATE *state;
-/*     int     lookup_mx;
-/*     char    *domain;
-/*     unsigned port;
+/*     int     name_key_flags;
 /*
-/*     SMTP_SESSION *smtp_reuse_addr(state, addr, port)
+/*     SMTP_SESSION *smtp_reuse_addr(state, endp_key_flags)
 /*     SMTP_STATE *state;
-/*     const char *addr;
-/*     unsigned port;
+/*     int     endp_key_flags;
 /* DESCRIPTION
 /*     This module implements the SMTP client specific interface to
 /*     the generic session cache infrastructure.
 /*
-/*     Each cached connection identifier includes the name of the
-/*     mail delivery service. Thus, cached connections are not
-/*     shared between different services.
+/*     A cached connection is closed when the TLS policy requires
+/*     that TLS is enabled.
 /*
 /*     smtp_save_session() stores the current session under the
 /*     next-hop logical destination (if available) and under the
 /*     remote server address.  The SMTP_SESSION object is destroyed.
 /*
-/*     smtp_reuse_domain() looks up a cached session by its logical
+/*     smtp_reuse_nexthop() looks up a cached session by its logical
 /*     destination, and verifies that the session is still alive.
-/*     The restored session information includes the "best MX" bit.
+/*     The restored session information includes the "best MX" bit
+/*     and overrides the iterator dest, host and addr fields.
 /*     The result is null in case of failure.
 /*
 /*     smtp_reuse_addr() looks up a cached session by its server
 /*     address, and verifies that the session is still alive.
-/*     This operation is disabled when the legacy tls_per_site
-/*     or smtp_sasl_password_maps features are enabled.
+/*     The restored session information does not include the "best
+/*     MX" bit, and does not override the iterator dest, host and
+/*     addr fields.
 /*     The result is null in case of failure.
 /*
 /*     Arguments:
 /* .IP state
 /*     SMTP client state, including the current session, the original
 /*     next-hop domain, etc.
-/* .IP lookup_mx
-/*     Whether or not the domain is subject to MX lookup.
-/* .IP domain
-/*     Domain name or bare numerical address.
-/* .IP addr
-/*     The remote server address as printable text.
-/* .IP port
-/*     The remote server port, network byte order.
+/* .IP name_key_flags
+/*     Explicit declaration of context that should be used to look
+/*     up a cached connection by its logical destination.
+/*     See smtp_key(3) for details.
+/* .IP endp_key_flags
+/*     Explicit declaration of context that should be used to look
+/*     up a cached connection by its server address.
+/*     See smtp_key(3) for details.
 /* LICENSE
 /* .ad
 /* .fi
 #include <smtp_reuse.h>
 
  /*
-  * We encode the MX lookup/A lookup method into the name under which SMTP
-  * session information is cached. The following macros serve to make the
-  * remainder of the code less obscure.
+  * Key field delimiter, and place holder field value for
+  * unavailable/inapplicable information.
   */
-#define NO_MX_LOOKUP   0
-
-#define SMTP_SCACHE_LABEL(mx_lookup_flag) \
-       ((mx_lookup_flag) ? "%s:%s:%u" : "%s:[%s]:%u")
+#define SMTP_REUSE_KEY_DELIM_NA        "\n*"
 
 /* smtp_save_session - save session under next-hop name and server address */
 
-void    smtp_save_session(SMTP_STATE *state)
+void    smtp_save_session(SMTP_STATE *state, int name_key_flags,
+                                 int endp_key_flags)
 {
     SMTP_SESSION *session = state->session;
     int     fd;
@@ -114,26 +111,17 @@ void    smtp_save_session(SMTP_STATE *state)
     /*
      * Encode the next-hop logical destination, if available. Reuse storage
      * that is also used for cache lookup queries.
-     * 
-     * Note: if the label needs to be made more specific (with e.g., SASL login
-     * information), just append the text with vstring_sprintf_append().
      */
     if (HAVE_NEXTHOP_STATE(state))
-       vstring_sprintf(state->dest_label,
-                       SMTP_SCACHE_LABEL(state->nexthop_lookup_mx),
-                       state->service, state->nexthop_domain,
-                       ntohs(state->nexthop_port));
+       smtp_key_prefix(state->dest_label, SMTP_REUSE_KEY_DELIM_NA,
+                       state->iterator, name_key_flags);
 
     /*
      * Encode the physical endpoint name. Reuse storage that is also used for
      * cache lookup queries.
-     * 
-     * Note: if the label needs to be made more specific (with e.g., SASL login
-     * information), just append the text with vstring_sprintf_append().
      */
-    vstring_sprintf(state->endp_label,
-                   SMTP_SCACHE_LABEL(NO_MX_LOOKUP),
-                   state->service, session->addr, ntohs(session->port));
+    smtp_key_prefix(state->endp_label, SMTP_REUSE_KEY_DELIM_NA,
+                   state->iterator, endp_key_flags);
 
     /*
      * Passivate the SMTP_SESSION object, destroying the object in the
@@ -169,10 +157,21 @@ static SMTP_SESSION *smtp_reuse_common(SMTP_STATE *state, int fd,
     const char *myname = "smtp_reuse_common";
     SMTP_SESSION *session;
 
+    /*
+     * Can't happen. Both smtp_reuse_nexthop() and smtp_reuse_addr() decline
+     * the request when the TLS policy is not TLS_LEV_NONE.
+     */
+#ifdef USE_TLS
+    if (state->tls->level > TLS_LEV_NONE)
+       msg_panic("%s: unexpected plain-text cached session to %s",
+                 myname, label);
+#endif
+
     /*
      * Re-activate the SMTP_SESSION object.
      */
-    session = smtp_session_activate(fd, state->dest_prop, state->endp_prop);
+    session = smtp_session_activate(fd, state->iterator, state->dest_prop,
+                                   state->endp_prop);
     if (session == 0) {
        msg_warn("%s: bad cached session attribute for %s", myname, label);
        (void) close(fd);
@@ -180,32 +179,8 @@ static SMTP_SESSION *smtp_reuse_common(SMTP_STATE *state, int fd,
     }
     state->session = session;
     session->state = state;
-
-    /*
-     * XXX Temporary fix.
-     * 
-     * Cached connections are always plaintext. They must never be reused when
-     * TLS encryption is required.
-     * 
-     * As long as we support the legacy smtp_tls_per_site feature, we must
-     * search the connection cache before making TLS policy decisions. This
-     * is because the policy can depend on the server name. For example, a
-     * site could have a global policy that requires encryption, with
-     * per-server exceptions that allow plaintext.
-     * 
-     * With the newer smtp_tls_policy_maps feature, the policy depends on the
-     * next-hop destination only. We can avoid unnecessary connection cache
-     * lookups, because we can compute the TLS policy much earlier.
-     */
 #ifdef USE_TLS
-    if (session->tls->level >= TLS_LEV_ENCRYPT) {
-       if (msg_verbose)
-           msg_info("%s: skipping plain-text cached session to %s",
-                    myname, label);
-       smtp_quit(state);                       /* Close politely */
-       smtp_session_free(session);             /* And avoid leaks */
-       return (state->session = 0);
-    }
+    session->tls = state->tls;                 /* TEMPORARY */
 #endif
 
     /*
@@ -230,22 +205,27 @@ static SMTP_SESSION *smtp_reuse_common(SMTP_STATE *state, int fd,
     return (session);
 }
 
-/* smtp_reuse_domain - reuse session cached under domain name */
+/* smtp_reuse_nexthop - reuse session cached under nexthop name */
 
-SMTP_SESSION *smtp_reuse_domain(SMTP_STATE *state, int lookup_mx,
-                                       const char *domain, unsigned port)
+SMTP_SESSION *smtp_reuse_nexthop(SMTP_STATE *state, int name_key_flags)
 {
     SMTP_SESSION *session;
     int     fd;
 
+    /*
+     * Don't look up an existing plaintext connection when a new connection
+     * would (try to) use TLS.
+     */
+#ifdef USE_TLS
+    if (state->tls->level > TLS_LEV_NONE)
+       return (0);
+#endif
+
     /*
      * Look up the session by its logical name.
-     * 
-     * Note: if the label needs to be made more specific (with e.g., SASL login
-     * information), just append the text with vstring_sprintf_append().
      */
-    vstring_sprintf(state->dest_label, SMTP_SCACHE_LABEL(lookup_mx),
-                   state->service, domain, ntohs(port));
+    smtp_key_prefix(state->dest_label, SMTP_REUSE_KEY_DELIM_NA,
+                   state->iterator, name_key_flags);
     if ((fd = scache_find_dest(smtp_scache, STR(state->dest_label),
                               state->dest_prop, state->endp_prop)) < 0)
        return (0);
@@ -260,33 +240,26 @@ SMTP_SESSION *smtp_reuse_domain(SMTP_STATE *state, int lookup_mx,
 
 /* smtp_reuse_addr - reuse session cached under numerical address */
 
-SMTP_SESSION *smtp_reuse_addr(SMTP_STATE *state, const char *addr,
-                                     unsigned port)
+SMTP_SESSION *smtp_reuse_addr(SMTP_STATE *state, int endp_key_flags)
 {
     SMTP_SESSION *session;
     int     fd;
 
     /*
-     * XXX Disable connection cache lookup by server IP address when the
-     * tls_per_site policy or smtp_sasl_password_maps features are enabled.
-     * This connection may have been created under a different hostname that
-     * resolves to the same IP address. We don't want to use the wrong SASL
-     * credentials or the wrong TLS policy.
+     * Don't look up an existing plaintext connection when a new connection
+     * would (try to) use TLS.
      */
-    if ((var_smtp_tls_per_site && *var_smtp_tls_per_site)
-       || (var_smtp_tls_policy && *var_smtp_tls_policy)
-       || (var_smtp_sasl_passwd && *var_smtp_sasl_passwd))
+#ifdef USE_TLS
+    if (state->tls->level > TLS_LEV_NONE)
        return (0);
+#endif
 
     /*
      * Look up the session by its IP address. This means that we have no
      * destination-to-address binding properties.
-     * 
-     * Note: if the label needs to be made more specific (with e.g., SASL login
-     * information), just append the text with vstring_sprintf_append().
      */
-    vstring_sprintf(state->endp_label, SMTP_SCACHE_LABEL(NO_MX_LOOKUP),
-                   state->service, addr, ntohs(port));
+    smtp_key_prefix(state->endp_label, SMTP_REUSE_KEY_DELIM_NA,
+                   state->iterator, endp_key_flags);
     if ((fd = scache_find_endp(smtp_scache, STR(state->endp_label),
                               state->endp_prop)) < 0)
        return (0);
@@ -299,13 +272,5 @@ SMTP_SESSION *smtp_reuse_addr(SMTP_STATE *state, const char *addr,
      */
     session = smtp_reuse_common(state, fd, STR(state->endp_label));
 
-    /*
-     * XXX What if hostnames don't match (addr->name versus session->name),
-     * or if the SASL login name for this host does not match the SASL login
-     * name that was used when opening this session? If something depends
-     * critically on such information being identical, then that information
-     * should be included in the logical and physical labels under which a
-     * session is cached.
-     */
     return (session);
 }
index bf005c043c41a33c4172313ebd8b1f18976e6879..8c6cb48ccd00d9e49b9c64e279c5fb97e721506d 100644 (file)
@@ -11,9 +11,9 @@
  /*
   * Internal interfaces.
   */
-extern void smtp_save_session(SMTP_STATE *);
-extern SMTP_SESSION *smtp_reuse_domain(SMTP_STATE *, int, const char *, unsigned);
-extern SMTP_SESSION *smtp_reuse_addr(SMTP_STATE *, const char *, unsigned);
+extern void smtp_save_session(SMTP_STATE *, int, int);
+extern SMTP_SESSION *smtp_reuse_nexthop(SMTP_STATE *, int);
+extern SMTP_SESSION *smtp_reuse_addr(SMTP_STATE *, int);
 
 /* LICENSE
 /* .ad
index 62937926ac62485346ee30594c8e3e66a1a07ba1..2336f0b0a7d2545a2a34f2c51e9d59928f4dad0c 100644 (file)
@@ -6,27 +6,12 @@
 /* SYNOPSIS
 /*     #include "smtp.h"
 /*
-/*     SMTP_SESSION *smtp_session_alloc(why, dest, host, addr,
-/*                                     port, flags)
-/*     DSN_BUF *why;
-/*     char    *dest;
-/*     char    *host;
-/*     char    *addr;
-/*     unsigned port;
-/*     int     flags;
-/*
-/*     void    smtp_session_new_stream(session, stream, start, flags)
-/*     SMTP_SESSION *session;
+/*     SMTP_SESSION *smtp_session_alloc(stream, iter, start, flags)
 /*     VSTREAM *stream;
+/*     SMTP_ITERATOR *iter;
 /*     time_t  start;
 /*     int     flags;
 /*
-/*     int     smtp_sess_tls_check(dest, host, port, valid)
-/*     char    *dest;
-/*     char    *host;
-/*     unsigned port;
-/*     int     valid;
-/*
 /*     void    smtp_session_free(session)
 /*     SMTP_SESSION *session;
 /*
 /*     VSTRING *dest_prop;
 /*     VSTRING *endp_prop;
 /*
-/*     SMTP_SESSION *smtp_session_activate(fd, dest_prop, endp_prop)
+/*     SMTP_SESSION *smtp_session_activate(fd, iter, dest_prop, endp_prop)
 /*     int     fd;
+/*     SMTP_ITERATOR *iter;
 /*     VSTRING *dest_prop;
 /*     VSTRING *endp_prop;
 /* DESCRIPTION
 /*     smtp_session_alloc() allocates memory for an SMTP_SESSION structure
-/*     and initializes it with the given destination, host name and address
-/*     information.  The host name and address strings are copied. The port
-/*     is in network byte order.  When TLS is enabled, smtp_session_alloc()
-/*     looks up the per-site TLS policies for TLS enforcement and certificate
-/*     verification.  The resulting policy is stored into the SMTP_SESSION
-/*     object.  Table and DNS lookups can fail during TLS policy creation,
-/*     when this happens, "why" is updated with the error reason and a null
-/*     session pointer is returned.
-/*
-/*     smtp_session_new_stream() updates an SMTP_SESSION structure
-/*     with a newly-created stream that was created at the specified
-/*     start time, and makes it subject to the specified connection
-/*     caching policy.
-/*
-/*     smtp_sess_tls_check() returns true if TLS use is mandatory, invalid
-/*     or indeterminate. The return value is false only if TLS is optional.
-/*     This is not yet used anywhere, it can be used to safely enable TLS
-/*     policy with smtp_reuse_addr().
+/*     and initializes it with the given stream and destination, host name
+/*     and address information.  The host name and address strings are
+/*     copied. The port is in network byte order.
 /*
 /*     smtp_session_free() destroys an SMTP_SESSION structure and its
 /*     members, making memory available for reuse. It will handle the
 /*     it can be cached. The SMTP_SESSION structure is destroyed.
 /*
 /*     smtp_session_activate() inflates a flattened SMTP session
-/*     so that it can be used. The input is modified.
+/*     so that it can be used. The input property arguments are
+/*     modified.
 /*
 /*     Arguments:
 /* .IP stream
 /*     A full-duplex stream.
-/* .IP dest
-/*     The unmodified next-hop or fall-back destination including
-/*     the optional [] and including the optional port or service.
-/* .IP host
-/*     The name of the host that we are connected to.
-/* .IP addr
-/*     The address of the host that we are connected to.
-/* .IP port
-/*     The remote port, network byte order.
-/* .IP valid
-/*     The DNSSEC validation status of the host name.
+/* .IP iter
+/*     The literal next-hop or fall-back destination including
+/*     the optional [] and including the :port or :service;
+/*     the name of the remote host;
+/*     the printable address of the remote host;
+/*     the remote port in network byte order.
 /* .IP start
 /*     The time when this connection was opened.
 /* .IP flags
 /*     Zero or more of the following:
 /* .RS
-/* .IP SMTP_MISC_FLAG_TLSA_HOST
-/*     The hostname is DNSSEC-validated.
 /* .IP SMTP_MISC_FLAG_CONN_LOAD
 /*     Enable re-use of cached SMTP or LMTP connections.
 /* .IP SMTP_MISC_FLAG_CONN_STORE
 /*     Enable saving of cached SMTP or LMTP connections.
-/* .IP SMTP_MISC_FLAG_NO_TLS
-/*     Used only internally in smtp_session.c
 /* .RE
 /*     SMTP_MISC_FLAG_CONN_MASK corresponds with both _LOAD and _STORE.
 /* .IP dest_prop
 /*     Destination specific session properties: the server is the
-/*     best MX host for the current logical destination.
+/*     best MX host for the current logical destination, the dest,
+/*     host, and addr properties. When dest_prop is non-empty, it
+/*     overrides the iterator dest, host, and addr properties.  It
+/*     is the caller's responsibility to save the current nexthop
+/*     with SMTP_ITER_SAVE_DEST() and to restore it afterwards
+/*     with SMTP_ITER_RESTORE_DEST() before trying alternatives.
 /* .IP endp_prop
 /*     Endpoint specific session properties: all the features
 /*     advertised by the remote server.
 /*     P.O. Box 704
 /*     Yorktown Heights, NY 10598, USA
 /*
-/*     TLS support originally by:
-/*     Lutz Jaenicke
-/*     BTU Cottbus
-/*     Allgemeine Elektrotechnik
-/*     Universitaetsplatz 3-4
-/*     D-03044 Cottbus, Germany
+/*     Viktor Dukhovni
 /*--*/
 
 /* System library. */
 #include <vstring.h>
 #include <vstream.h>
 #include <stringops.h>
-#include <valid_hostname.h>
 
 /* Global library. */
 
 #include <mime_state.h>
 #include <debug_peer.h>
 #include <mail_params.h>
-#include <maps.h>
-#include <smtp_stream.h>
 
 /* Application-specific. */
 
 
 /* smtp_session_alloc - allocate and initialize SMTP_SESSION structure */
 
-SMTP_SESSION *smtp_session_alloc(DSN_BUF *why, const char *dest,
-                                        const char *host, const char *addr,
-                                        unsigned port, int flags)
+SMTP_SESSION *smtp_session_alloc(VSTREAM *stream, SMTP_ITERATOR *iter,
+                                        time_t start, int flags)
 {
-    const char *myname = "smtp_session_alloc";
     SMTP_SESSION *session;
-    int     valid = (flags & SMTP_MISC_FLAG_TLSA_HOST);
+    const char *dest = STR(iter->dest);
+    const char *host = STR(iter->host);
+    const char *addr = STR(iter->addr);
+    unsigned port = iter->port;
 
     session = (SMTP_SESSION *) mymalloc(sizeof(*session));
-    session->stream = 0;
+    session->stream = stream;
     session->dest = mystrdup(dest);
     session->host = mystrdup(host);
     session->addr = mystrdup(addr);
@@ -194,75 +155,28 @@ SMTP_SESSION *smtp_session_alloc(DSN_BUF *why, const char *dest,
 
     session->send_proto_helo = 0;
 
+    if (flags & SMTP_MISC_FLAG_CONN_STORE)
+       CACHE_THIS_SESSION_UNTIL(start + var_smtp_reuse_time);
+    else
+       DONT_CACHE_THIS_SESSION;
+    session->reuse_count = 0;
     USE_NEWBORN_SESSION;                       /* He's not dead Jim! */
 
 #ifdef USE_SASL_AUTH
     smtp_sasl_connect(session);
 #endif
 
-    /*
-     * 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.
-     */
-#define NO_DSN_BUF     (DSN_BUF *) 0
-#define NO_DEST                (char *) 0
-#define NO_HOST                (char *) 0
-#define NO_PORT                0
-#define NO_FLAGS       0
-
 #ifdef USE_TLS
     session->tls_context = 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);
-    else {
-       if (why == 0)
-           msg_panic("%s: null error buffer", myname);
-       session->tls = smtp_tls_sess_alloc(why, dest, host, port, valid);
-    }
-    if (!session->tls) {
-       smtp_session_free(session);
-       return (0);
-    }
+    session->tls = 0;                          /* TEMPORARY */
 #endif
     session->state = 0;
     debug_peer_check(host, addr);
     return (session);
 }
 
-/* smtp_session_new_stream - finalize session with newly-created connection */
-
-void    smtp_session_new_stream(SMTP_SESSION *session, VSTREAM *stream,
-                                       time_t start, int flags)
-{
-    const char *myname = "smtp_session_new_stream";
-
-    /*
-     * Sanity check.
-     */
-    if (session->stream != 0)
-       msg_panic("%s: session exists", myname);
-
-    session->stream = stream;
-
-    /*
-     * Make the session subject to the new-stream connection caching policy,
-     * as opposed to the reused-stream connection caching policy at the
-     * bottom of this module. Both policies are enforced in this file, not
-     * one policy here and the other at some random place in smtp_connect.c.
-     */
-    if (flags & SMTP_MISC_FLAG_CONN_STORE)
-       CACHE_THIS_SESSION_UNTIL(start + var_smtp_reuse_time);
-    else
-       DONT_CACHE_THIS_SESSION;
-    session->reuse_count = 0;
-}
-
 /* smtp_session_free - destroy SMTP_SESSION structure and contents */
 
 void    smtp_session_free(SMTP_SESSION *session)
@@ -274,8 +188,6 @@ void    smtp_session_free(SMTP_SESSION *session)
            tls_client_stop(smtp_tls_ctx, session->stream,
                          var_smtp_starttls_tmout, 0, session->tls_context);
     }
-    if (session->tls)
-       (void) smtp_tls_sess_free(session->tls);
 #endif
     if (session->stream)
        vstream_fclose(session->stream);
@@ -304,32 +216,6 @@ void    smtp_session_free(SMTP_SESSION *session)
     myfree((char *) session);
 }
 
-/* smtp_sess_tls_check - does session require tls */
-
-int     smtp_sess_tls_check(const char *dest, const char *host, unsigned port,
-                                   int valid)
-{
-#ifdef USE_TLS
-    static DSN_BUF *why;
-    SMTP_TLS_SESS *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);
-#else
-    return (0);
-#endif
-}
-
-
 /* smtp_session_passivate - passivate an SMTP_SESSION object */
 
 int     smtp_session_passivate(SMTP_SESSION *session, VSTRING *dest_prop,
@@ -346,8 +232,13 @@ int     smtp_session_passivate(SMTP_SESSION *session, VSTRING *dest_prop,
      * XXX It would be nice to have a VSTRING to VSTREAM adapter so that we can
      * serialize the properties with attr_print() instead of using ad-hoc,
      * non-reusable, code and hard-coded format strings.
+     * 
+     * TODO: save SASL username and password information so that we can
+     * correctly save a reused authenticated connection.
+     * 
      */
-    vstring_sprintf(dest_prop, "%u",
+    vstring_sprintf(dest_prop, "%s\n%s\n%s\n%u",
+                   session->dest, session->host, session->addr,
                    session->features & SMTP_FEATURE_DESTINATION_MASK);
 
     /*
@@ -366,10 +257,8 @@ int     smtp_session_passivate(SMTP_SESSION *session, VSTRING *dest_prop,
      * XXX Be sure to use unsigned types in the format string. Sign characters
      * would be rejected by the alldig() test on the reading end.
      */
-    vstring_sprintf(endp_prop, "%u\n%s\n%s\n%s\n%u\n%u\n%lu",
+    vstring_sprintf(endp_prop, "%u\n%u\n%lu",
                    session->reuse_count,
-                   session->dest, session->host,
-                   session->addr, session->port,
                    session->features & SMTP_FEATURE_ENDPOINT_MASK,
                    (long) session->expire_time);
 
@@ -395,7 +284,8 @@ int     smtp_session_passivate(SMTP_SESSION *session, VSTRING *dest_prop,
 
 /* smtp_session_activate - re-activate a passivated SMTP_SESSION object */
 
-SMTP_SESSION *smtp_session_activate(int fd, VSTRING *dest_prop,
+SMTP_SESSION *smtp_session_activate(int fd, SMTP_ITERATOR *iter,
+                                           VSTRING *dest_prop,
                                            VSTRING *endp_prop)
 {
     const char *myname = "smtp_session_activate";
@@ -406,7 +296,6 @@ SMTP_SESSION *smtp_session_activate(int fd, VSTRING *dest_prop,
     const char *dest;
     const char *host;
     const char *addr;
-    unsigned port;
     unsigned features;                 /* server features */
     time_t  expire_time;               /* session re-use expiration time */
     unsigned reuse_count;              /* # times reused */
@@ -425,30 +314,11 @@ SMTP_SESSION *smtp_session_activate(int fd, VSTRING *dest_prop,
        return (0);
     }
     reuse_count = atoi(prop);
-    if ((dest = mystrtok(&endp_props, "\n")) == 0) {
-       msg_warn("%s: missing cached session destination property", myname);
-       return (0);
-    }
-    if ((host = mystrtok(&endp_props, "\n")) == 0) {
-       msg_warn("%s: missing cached session hostname property", myname);
-       return (0);
-    }
-    if ((addr = mystrtok(&endp_props, "\n")) == 0) {
-       msg_warn("%s: missing cached session address property", myname);
-       return (0);
-    }
-    if ((prop = mystrtok(&endp_props, "\n")) == 0 || !alldig(prop)) {
-       msg_warn("%s: bad cached session port property", myname);
-       return (0);
-    }
-    port = atoi(prop);
-
     if ((prop = mystrtok(&endp_props, "\n")) == 0 || !alldig(prop)) {
        msg_warn("%s: bad cached session features property", myname);
        return (0);
     }
     features = atoi(prop);
-
     if ((prop = mystrtok(&endp_props, "\n")) == 0 || !alldig(prop)) {
        msg_warn("%s: bad cached session expiration time property", myname);
        return (0);
@@ -459,26 +329,47 @@ SMTP_SESSION *smtp_session_activate(int fd, VSTRING *dest_prop,
     expire_time = strtoul(prop, 0, 10);
 #endif
 
+    /*
+     * Clobber the iterator's current nexthop, host and address fields with
+     * cached-connection information. This is done when a session is looked
+     * up by request nexthop instead of address and port. It is the caller's
+     * responsibility to save and restore the request nexthop with
+     * SMTP_ITER_SAVE_DEST() and SMTP_ITER_RESTORE_DEST().
+     * 
+     * TODO: Eliminate the duplication between SMTP_ITERATOR and SMTP_SESSION.
+     * 
+     * TODO: restore SASL username and password information so that we can
+     * correctly save a reused authenticated connection.
+     */
     if (dest_prop && VSTRING_LEN(dest_prop)) {
        dest_props = STR(dest_prop);
+       if ((dest = mystrtok(&dest_props, "\n")) == 0) {
+           msg_warn("%s: missing cached session destination property", myname);
+           return (0);
+       }
+       if ((host = mystrtok(&dest_props, "\n")) == 0) {
+           msg_warn("%s: missing cached session hostname property", myname);
+           return (0);
+       }
+       if ((addr = mystrtok(&dest_props, "\n")) == 0) {
+           msg_warn("%s: missing cached session address property", myname);
+           return (0);
+       }
        if ((prop = mystrtok(&dest_props, "\n")) == 0 || !alldig(prop)) {
            msg_warn("%s: bad cached destination features property", myname);
            return (0);
        }
        features |= atoi(prop);
+       SMTP_ITER_CLOBBER(iter, dest, host, addr);
     }
 
     /*
      * Allright, bundle up what we have sofar.
-     * 
-     * Caller is responsible for not reusing plain-text connections when TLS is
-     * required.  With TLS disabled, a trivial TLS policy (level "none") is
-     * returned without fail, with no other ways for session setup to fail,
-     * we assume it must succeed.
      */
-    session = smtp_session_alloc(NO_DSN_BUF, dest, host, addr, port,
-                                SMTP_MISC_FLAG_NO_TLS);
-    session->stream = vstream_fdopen(fd, O_RDWR);
+#define NO_FLAGS       0
+
+    session = smtp_session_alloc(vstream_fdopen(fd, O_RDWR), iter,
+                                (time_t) 0, NO_FLAGS);
     session->features = (features | SMTP_FEATURE_FROM_CACHE);
     CACHE_THIS_SESSION_UNTIL(expire_time);
     session->reuse_count = ++reuse_count;
@@ -486,8 +377,10 @@ SMTP_SESSION *smtp_session_activate(int fd, VSTRING *dest_prop,
     if (msg_verbose)
        msg_info("%s: dest=%s host=%s addr=%s port=%u features=0x%x, "
                 "ttl=%ld, reuse=%d",
-                myname, dest, host, addr, ntohs(port), features,
-                (long) (expire_time - time((time_t *) 0)), reuse_count);
+                myname, STR(iter->dest), STR(iter->host),
+                STR(iter->addr), ntohs(iter->port), features,
+                (long) (expire_time - time((time_t *) 0)),
+                reuse_count);
 
     /*
      * Re-activate the SASL attributes.
index 9699a053b91a162cc8f38681d33b8604948085f7..7e95462eb1d558741d39b9fa99d947a57ad69938 100644 (file)
@@ -62,7 +62,11 @@ SMTP_STATE *smtp_state_alloc(void)
     state->session = 0;
     state->status = 0;
     state->space_left = 0;
-    state->nexthop_domain = 0;
+    state->iterator->request_nexthop = vstring_alloc(100);
+    state->iterator->dest = vstring_alloc(100);
+    state->iterator->host = vstring_alloc(100);
+    state->iterator->addr = vstring_alloc(100);
+    state->iterator->saved_dest = vstring_alloc(100);
     if (var_smtp_cache_conn) {
        state->dest_label = vstring_alloc(10);
        state->dest_prop = vstring_alloc(10);
@@ -84,6 +88,15 @@ SMTP_STATE *smtp_state_alloc(void)
 
 void    smtp_state_free(SMTP_STATE *state)
 {
+#ifdef USE_TLS
+    /* The TLS policy cache lifetime is one delivery. */
+    smtp_tls_policy_cache_flush();
+#endif
+    vstring_free(state->iterator->request_nexthop);
+    vstring_free(state->iterator->dest);
+    vstring_free(state->iterator->host);
+    vstring_free(state->iterator->addr);
+    vstring_free(state->iterator->saved_dest);
     if (state->dest_label)
        vstring_free(state->dest_label);
     if (state->dest_prop)
diff --git a/postfix/src/smtp/smtp_tls_policy.c b/postfix/src/smtp/smtp_tls_policy.c
new file mode 100644 (file)
index 0000000..152763a
--- /dev/null
@@ -0,0 +1,832 @@
+/*++
+/* NAME
+/*     smtp_tls_policy 3
+/* SUMMARY
+/*     SMTP_TLS_POLICY structure management
+/* SYNOPSIS
+/*     #include "smtp.h"
+/*
+/*     void    smtp_tls_list_init()
+/*
+/*     int     smtp_tls_policy_cache_query(why, tls, iter)
+/*     DSN_BUF *why;
+/*     SMTP_TLS_POLICY *tls;
+/*     SMTP_ITERATOR *iter;
+/*
+/*     void    smtp_tls_policy_dummy(tls)
+/*     SMTP_TLS_POLICY *tls;
+/*
+/*     void    smtp_tls_policy_cache_flush()
+/* DESCRIPTION
+/*     smtp_tls_list_init() initializes lookup tables used by the TLS
+/*     policy engine.
+/*
+/*     smtp_tls_policy_cache_query() returns a shallow copy of the
+/*     cached SMTP_TLS_POLICY structure for the iterator's
+/*     destination, host, port and DNSSEC validation status.
+/*     This copy is guaranteed to be valid until the next
+/*     smtp_tls_policy_cache_query() or smtp_tls_policy_cache_flush()
+/*     call.  The caller can override the TLS security level without
+/*     corrupting the policy cache.
+/*     When any required table or DNS lookups fail, the TLS level
+/*     is set to TLS_LEV_INVALID, the "why" argument is updated
+/*     with the error reason and the result value is zero (false).
+/*
+/*     smtp_tls_policy_dummy() initializes a trivial, non-cached,
+/*     policy with TLS disabled.
+/*
+/*     smtp_tls_policy_cache_flush() destroys the TLS policy cache
+/*     and contents.
+/*
+/*     Arguments:
+/* .IP why
+/*     A pointer to a DSN_BUF which holds error status information when
+/*     the TLS policy lookup fails.
+/* .IP tls
+/*     Pointer to TLS policy storage.
+/* .IP iter
+/*     The literal next-hop or fall-back destination including
+/*     the optional [] and including the :port or :service;
+/*     the name of the remote host after MX and CNAME expansions
+/*     (see smtp_cname_overrides_servername for the handling
+/*     of hostnames that resolve to a CNAME record);
+/*     the printable address of the remote host;
+/*     the remote port in network byte order;
+/*     the DNSSEC validation status of the host name lookup after
+/*     MX and CNAME expansions.
+/* 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)
+/*     TLS support originally by:
+/*     Lutz Jaenicke
+/*     BTU Cottbus
+/*     Allgemeine Elektrotechnik
+/*     Universitaetsplatz 3-4
+/*     D-03044 Cottbus, Germany
+/*
+/*     Updated by:
+/*     Wietse Venema
+/*     IBM T.J. Watson Research
+/*     P.O. Box 704
+/*     Yorktown Heights, NY 10598, USA
+/*
+/*     Viktor Dukhovni
+/*--*/
+
+/* System library. */
+
+#include <sys_defs.h>
+
+#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>
+
+#ifdef STRCASECMP_IN_STRINGS_H
+#include <strings.h>
+#endif
+
+/* Utility library. */
+
+#include <msg.h>
+#include <mymalloc.h>
+#include <vstring.h>
+#include <stringops.h>
+#include <valid_hostname.h>
+#include <ctable.h>
+
+/* Global library. */
+
+#include <mail_params.h>
+#include <maps.h>
+
+/* DNS library. */
+
+#include <dns.h>
+
+/* Application-specific. */
+
+#include "smtp.h"
+
+/* XXX Cache size should scale with [sl]mtp_mx_address_limit. */
+#define CACHE_SIZE 20
+static CTABLE *policy_cache;
+
+static int global_tls_level(void);
+static void dane_init(SMTP_TLS_POLICY *, SMTP_ITERATOR *);
+
+static MAPS *tls_policy;               /* lookup table(s) */
+static MAPS *tls_per_site;             /* lookup table(s) */
+
+/* smtp_tls_list_init - initialize per-site policy lists */
+
+void    smtp_tls_list_init(void)
+{
+    if (*var_smtp_tls_policy) {
+       tls_policy = maps_create(SMTP_X(TLS_POLICY), var_smtp_tls_policy,
+                                DICT_FLAG_LOCK | DICT_FLAG_FOLD_FIX);
+       if (*var_smtp_tls_per_site)
+           msg_warn("%s ignored when %s is not empty.",
+                    SMTP_X(TLS_PER_SITE), SMTP_X(TLS_POLICY));
+       return;
+    }
+    if (*var_smtp_tls_per_site) {
+       tls_per_site = maps_create(SMTP_X(TLS_PER_SITE), var_smtp_tls_per_site,
+                                  DICT_FLAG_LOCK | DICT_FLAG_FOLD_FIX);
+    }
+}
+
+/* policy_name - printable tls policy level */
+
+static const char *policy_name(int tls_level)
+{
+    const char *name = str_tls_level(tls_level);
+
+    if (name == 0)
+       name = "unknown";
+    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_POLICY *tls, int *site_level,
+                             const char *site_name, const char *site_class)
+{
+    const char *lookup;
+
+    /*
+     * Look up a non-default policy. In case of multiple lookup results, the
+     * precedence order is a permutation of the TLS enforcement level order:
+     * VERIFY, ENCRYPT, NONE, MAY, NOTFOUND. I.e. we override MAY with a more
+     * specific policy including NONE, otherwise we choose the stronger
+     * enforcement level.
+     */
+    if ((lookup = maps_find(tls_per_site, site_name, 0)) != 0) {
+       if (!strcasecmp(lookup, "NONE")) {
+           /* NONE overrides MAY or NOTFOUND. */
+           if (*site_level <= TLS_LEV_MAY)
+               *site_level = TLS_LEV_NONE;
+       } else if (!strcasecmp(lookup, "MAY")) {
+           /* MAY overrides NOTFOUND but not NONE. */
+           if (*site_level < TLS_LEV_NONE)
+               *site_level = TLS_LEV_MAY;
+       } else if (!strcasecmp(lookup, "MUST_NOPEERMATCH")) {
+           if (*site_level < TLS_LEV_ENCRYPT)
+               *site_level = TLS_LEV_ENCRYPT;
+       } else if (!strcasecmp(lookup, "MUST")) {
+           if (*site_level < TLS_LEV_VERIFY)
+               *site_level = TLS_LEV_VERIFY;
+       } else {
+           msg_warn("%s: unknown TLS policy '%s' for %s %s",
+                    tls_per_site->title, lookup, site_class, site_name);
+           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(tls->why, "4.3.0", "Temporary lookup error");
+       *site_level = TLS_LEV_INVALID;
+       return;
+    }
+    return;
+}
+
+/* tls_policy_lookup_one - look up destination TLS policy */
+
+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;
+    char   *saved_policy;
+    char   *tok;
+    const char *err;
+    char   *name;
+    char   *val;
+    static VSTRING *cbuf;
+
+#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 \
+    STR(vstring_sprintf(cbuf, "%s, %s \"%s\"", \
+               tls_policy->title, site_class, site_name))
+
+    if (cbuf == 0)
+       cbuf = vstring_alloc(10);
+
+    if ((lookup = maps_find(tls_policy, site_name, 0)) == 0) {
+       if (tls_policy->error) {
+           msg_warn("%s: policy table lookup error", WHERE);
+           MARK_INVALID(tls->why, site_level);
+       }
+       return;
+    }
+    saved_policy = policy = mystrdup(lookup);
+
+    if ((tok = mystrtok(&policy, "\t\n\r ,")) == 0) {
+       msg_warn("%s: invalid empty policy", WHERE);
+       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);
+       INVALID_RETURN(tls->why, site_level);
+    }
+
+    /*
+     * Warn about ignored attributes when TLS is disabled.
+     */
+    if (*site_level < TLS_LEV_MAY) {
+       while ((tok = mystrtok(&policy, "\t\n\r ,")) != 0)
+           msg_warn("%s: ignoring attribute \"%s\" with TLS disabled",
+                    WHERE, tok);
+       FREE_RETURN;
+    }
+
+    /*
+     * Errors in attributes may have security consequences, don't ignore
+     * errors that can degrade security.
+     */
+    while ((tok = mystrtok(&policy, "\t\n\r ,")) != 0) {
+       if ((err = split_nameval(tok, &name, &val)) != 0) {
+           msg_warn("%s: malformed attribute/value pair \"%s\": %s",
+                    WHERE, tok, err);
+           INVALID_RETURN(tls->why, site_level);
+       }
+       /* Only one instance per policy. */
+       if (!strcasecmp(name, "ciphers")) {
+           if (*val == 0) {
+               msg_warn("%s: attribute \"%s\" has empty value", WHERE, name);
+               INVALID_RETURN(tls->why, site_level);
+           }
+           if (tls->grade) {
+               msg_warn("%s: attribute \"%s\" is specified multiple times",
+                        WHERE, name);
+               INVALID_RETURN(tls->why, site_level);
+           }
+           tls->grade = mystrdup(val);
+           continue;
+       }
+       /* Only one instance per policy. */
+       if (!strcasecmp(name, "protocols")) {
+           if (tls->protocols) {
+               msg_warn("%s: attribute \"%s\" is specified multiple times",
+                        WHERE, name);
+               INVALID_RETURN(tls->why, site_level);
+           }
+           tls->protocols = mystrdup(val);
+           continue;
+       }
+       /* Multiple instances per policy. */
+       if (!strcasecmp(name, "match")) {
+           if (*val == 0) {
+               msg_warn("%s: attribute \"%s\" has empty value", WHERE, name);
+               INVALID_RETURN(tls->why, site_level);
+           }
+           switch (*site_level) {
+           default:
+               msg_warn("%s: attribute \"%s\" invalid at security level "
+                        "\"%s\"", WHERE, name, policy_name(*site_level));
+               INVALID_RETURN(tls->why, site_level);
+               break;
+           case TLS_LEV_FPRINT:
+               if (!tls->dane)
+                   tls->dane = tls_dane_alloc(TLS_DANE_FLAG_MIXED);
+               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;
+           }
+           continue;
+       }
+       /* Only one instance per policy. */
+       if (!strcasecmp(name, "exclude")) {
+           if (tls->exclusions) {
+               msg_warn("%s: attribute \"%s\" is specified multiple times",
+                        WHERE, name);
+               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 (!TLS_MUST_PKIX(*site_level)) {
+               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)
+               tls->dane = tls_dane_alloc(TLS_DANE_FLAG_MIXED);
+           if (!tls_dane_load_trustfile(tls->dane, val)) {
+               INVALID_RETURN(tls->why, site_level);
+           }
+           continue;
+       }
+       msg_warn("%s: invalid attribute name: \"%s\"", WHERE, name);
+       INVALID_RETURN(tls->why, site_level);
+    }
+
+    FREE_RETURN;
+}
+
+/* tls_policy_lookup - look up destination TLS policy */
+
+static void tls_policy_lookup(SMTP_TLS_POLICY *tls, int *site_level,
+                                     const char *site_name,
+                                     const char *site_class)
+{
+
+    /*
+     * Only one lookup with [nexthop]:port, [nexthop] or nexthop:port These
+     * are never the domain part of localpart@domain, rather they are
+     * explicit nexthops from transport:nexthop, and match only the
+     * corresponding policy. Parent domain matching (below) applies only to
+     * sub-domains of the recipient domain.
+     * 
+     * XXX UNIX-domain connections query with the pathname as destination.
+     */
+    if (!valid_hostname(site_name, DONT_GRIPE)) {
+       tls_policy_lookup_one(tls, site_level, site_name, site_class);
+       return;
+    }
+    do {
+       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_POLICY *tls)
+{
+    const char *mand_exclude = "";
+    const char *also_exclude = "";
+
+    /*
+     * Use main.cf cipher level if no per-destination value specified. With
+     * mandatory encryption at least encrypt, and with mandatory verification
+     * at least authenticate!
+     */
+    switch (tls->level) {
+    case TLS_LEV_INVALID:
+    case TLS_LEV_NONE:
+       return;
+
+    case TLS_LEV_MAY:
+       if (tls->grade == 0)
+           tls->grade = mystrdup(var_smtp_tls_ciph);
+       break;
+
+    case TLS_LEV_ENCRYPT:
+       if (tls->grade == 0)
+           tls->grade = mystrdup(var_smtp_tls_mand_ciph);
+       mand_exclude = var_smtp_tls_mand_excl;
+       also_exclude = "eNULL";
+       break;
+
+    case TLS_LEV_DANE:
+    case TLS_LEV_FPRINT:
+    case TLS_LEV_VERIFY:
+    case TLS_LEV_SECURE:
+       if (tls->grade == 0)
+           tls->grade = mystrdup(var_smtp_tls_mand_ciph);
+       mand_exclude = var_smtp_tls_mand_excl;
+       also_exclude = "aNULL";
+       break;
+    }
+
+#define ADD_EXCLUDE(vstr, str) \
+    do { \
+       if (*(str)) \
+           vstring_sprintf_append((vstr), "%s%s", \
+                                  VSTRING_LEN(vstr) ? " " : "", (str)); \
+    } while (0)
+
+    /*
+     * The "exclude" policy table attribute overrides main.cf exclusion
+     * lists.
+     */
+    if (tls->exclusions == 0) {
+       tls->exclusions = vstring_alloc(10);
+       ADD_EXCLUDE(tls->exclusions, var_smtp_tls_excl_ciph);
+       ADD_EXCLUDE(tls->exclusions, mand_exclude);
+    }
+    ADD_EXCLUDE(tls->exclusions, also_exclude);
+}
+
+/* policy_create - create SMTP TLS policy cache object (ctable call-back) */
+
+static void *policy_create(const char *unused_key, void *context)
+{
+    SMTP_ITERATOR *iter = (SMTP_ITERATOR *) context;
+    int     site_level;
+    const char *dest = STR(iter->dest);
+    const char *host = STR(iter->host);
+
+    /*
+     * Prepare a pristine policy object.
+     */
+    SMTP_TLS_POLICY *tls = (SMTP_TLS_POLICY *) mymalloc(sizeof(*tls));
+
+    smtp_tls_policy_init(tls, dsb_create());
+
+    /*
+     * 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");
+    } else if (tls_per_site) {
+       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");
+
+       /*
+        * Override a wild-card per-site policy with a more specific global
+        * policy.
+        * 
+        * With the original TLS patch, 1) a per-site ENCRYPT could not override
+        * a global VERIFY, and 2) a combined per-site (NONE+MAY) policy
+        * produced inconsistent results: it changed a global VERIFY into
+        * NONE, while producing MAY with all weaker global policy settings.
+        * 
+        * With the current implementation, a combined per-site (NONE+MAY)
+        * consistently overrides global policy with NONE, and global policy
+        * can override only a per-site MAY wildcard. That is, specific
+        * policies consistently override wildcard policies, and
+        * (non-wildcard) per-site policies consistently override global
+        * policies.
+        */
+       if (site_level == TLS_LEV_MAY && tls->level > TLS_LEV_MAY)
+           site_level = tls->level;
+    }
+    switch (site_level) {
+    default:
+       tls->level = site_level;
+    case TLS_LEV_NOTFOUND:
+       break;
+    case TLS_LEV_INVALID:
+       return ((void *) tls);
+    }
+
+    /*
+     * DANE initialization may change the security level to something else,
+     * so do this early, so that we use the right level below.  Note that
+     * "dane-only" changes to "dane" after any fallback strategies are
+     * applied.
+     */
+    if (tls->level == TLS_LEV_DANE || tls->level == TLS_LEV_DANE_ONLY)
+       dane_init(tls, iter);
+    if (tls->level == TLS_LEV_INVALID)
+       return ((void *) tls);
+
+    /*
+     * Use main.cf protocols setting if not set in per-destination table.
+     */
+    if (tls->level > TLS_LEV_NONE && tls->protocols == 0)
+       tls->protocols =
+           mystrdup((tls->level == TLS_LEV_MAY) ?
+                    var_smtp_tls_proto : var_smtp_tls_mand_proto);
+
+    /*
+     * Compute cipher grade (if set in per-destination table, else
+     * set_cipher() uses main.cf settings) and security level dependent
+     * cipher exclusion list.
+     */
+    set_cipher_grade(tls);
+
+    /*
+     * Use main.cf cert_match setting if not set in per-destination table.
+     */
+    switch (tls->level) {
+    case TLS_LEV_INVALID:
+    case TLS_LEV_NONE:
+    case TLS_LEV_MAY:
+    case TLS_LEV_ENCRYPT:
+    case TLS_LEV_DANE:
+       break;
+    case TLS_LEV_FPRINT:
+       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 ((void *) tls);
+           }
+       }
+       tls_dane_final(tls->dane);
+       break;
+    case TLS_LEV_VERIFY:
+    case TLS_LEV_SECURE:
+       if (tls->matchargv == 0)
+           tls->matchargv =
+               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 ((void *) tls);
+               }
+           }
+       }
+       break;
+    default:
+       msg_panic("unexpected TLS security level: %d", tls->level);
+    }
+
+    if (msg_verbose && tls->level != global_tls_level())
+       msg_info("%s TLS level: %s", "effective", policy_name(tls->level));
+
+    return ((void *) tls);
+}
+
+/* policy_delete - free no longer cached policy (ctable call-back) */
+
+static void policy_delete(void *item, void *unused_context)
+{
+    SMTP_TLS_POLICY *tls = (SMTP_TLS_POLICY *) item;
+
+    if (tls->protocols)
+       myfree(tls->protocols);
+    if (tls->grade)
+       myfree(tls->grade);
+    if (tls->exclusions)
+       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);
+}
+
+/* smtp_tls_policy_cache_query - cached lookup of TLS policy */
+
+int     smtp_tls_policy_cache_query(DSN_BUF *why, SMTP_TLS_POLICY *tls,
+                                           SMTP_ITERATOR *iter)
+{
+    VSTRING *key;
+    int     valid = iter->rr && iter->rr->dnssec_valid;
+
+    /*
+     * Create an empty TLS Policy cache on the fly.
+     */
+    if (policy_cache == 0)
+       policy_cache =
+           ctable_create(CACHE_SIZE, policy_create, policy_delete, (void *) 0);
+
+    /*
+     * Query the TLS Policy cache, with a search key that reflects our shared
+     * values that also appear in other cache and table search keys.
+     */
+    key = vstring_alloc(100);
+    smtp_key_prefix(key, ":", iter, SMTP_KEY_FLAG_NEXTHOP
+                   | SMTP_KEY_FLAG_HOSTNAME
+                   | SMTP_KEY_FLAG_PORT);
+    vstring_sprintf_append(key, "%d", !!valid);
+    ctable_newcontext(policy_cache, (void *) iter);
+    *tls = *(SMTP_TLS_POLICY *) ctable_locate(policy_cache, STR(key));
+    vstring_free(key);
+
+    /*
+     * Report errors. Both error and non-error results are cached. We must
+     * therefore copy the cached DSN buffer content to the caller's buffer.
+     */
+    if (tls->level == TLS_LEV_INVALID) {
+       /* XXX Simplify this by implementing a "copy" primitive. */
+       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);
+    } else {
+       return (1);
+    }
+}
+
+/* smtp_tls_policy_cache_flush - flush TLS policy cache */
+
+void    smtp_tls_policy_cache_flush(void)
+{
+    if (policy_cache != 0) {
+       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;
+}
+
+/* dane_init - special initialization for "dane" security level */
+
+static void dane_init(SMTP_TLS_POLICY *tls, SMTP_ITERATOR *iter)
+{
+    TLS_DANE *dane;
+    int     valid = iter->rr && iter->rr->dnssec_valid;
+
+    if (!iter->port) {
+       msg_warn("%s: the \"dane\" security level is invalid for delivery via"
+                " unix-domain sockets", STR(iter->dest));
+       MARK_INVALID(tls->why, &tls->level);
+       return;
+    }
+
+    /*
+     * To use DANE security the requisite features must be provided by the
+     * OpenSSL (runtime) and DNS resolver (compile-time) libraries. Also,
+     * "smtp_host_lookup = dns" and "smtp_dns_support_level = dnssec" are
+     * required.
+     */
+#define DANEMSG(msg_func, reason, iter, tls) \
+       msg_func("%s: %s, the security level for delivery via %s:%u will " \
+                "degrade to \"%s\"", STR((iter)->dest), reason, \
+                STR((iter)->host), ntohs((iter)->port), \
+                policy_name((tls)->level))
+#define DANEWARN(reason, iter, tls) DANEMSG(msg_warn, (reason), (iter), (tls))
+
+    if (!tls_dane_avail()) {
+       if (tls->level == TLS_LEV_DANE) {
+           tls->level = TLS_LEV_MAY;
+           DANEWARN("DANE not available", iter, tls);
+       } else {
+           msg_warn("%s: \"%s\" requested without requisite library support",
+                    STR(iter->dest), policy_name(tls->level));
+           MARK_INVALID(tls->why, &tls->level);
+       }
+       return;
+    }
+    if (!(smtp_host_lookup_mask & SMTP_HOST_FLAG_DNS)
+       || smtp_dns_support != SMTP_DNS_DNSSEC) {
+       if (tls->level == TLS_LEV_DANE) {
+           tls->level = TLS_LEV_MAY;
+           DANEWARN("DNSSEC hostname lookups not enabled", iter, tls);
+       } else {
+           msg_warn("%s: \"%s\" requested with dnssec lookups disabled",
+                    STR(iter->dest), policy_name(tls->level));
+           MARK_INVALID(tls->why, &tls->level);
+       }
+       return;
+    }
+    if (!valid) {
+       if (tls->level == TLS_LEV_DANE) {
+           tls->level = TLS_LEV_MAY;
+           if (msg_verbose)
+               DANEMSG(msg_info, "non DNSSEC destination", iter, tls);
+       } else {
+           tls->level = TLS_LEV_INVALID;
+           dsb_simple(tls->why, "4.7.5", "non DNSSEC destination");
+       }
+       return;
+    }
+    /* When TLSA lookups fail, we defer the message */
+    if ((dane = tls_dane_resolve(STR(iter->host), "tcp", iter->port)) == 0) {
+       tls->level = TLS_LEV_INVALID;
+       dsb_simple(tls->why, "4.7.5", "TLSA lookup error for %s:%u",
+                  STR(iter->host), ntohs(iter->port));
+       return;
+    }
+    if (tls_dane_notfound(dane)) {
+       if (tls->level == TLS_LEV_DANE) {
+           tls->level = TLS_LEV_MAY;
+           if (msg_verbose)
+               DANEMSG(msg_info, "no TLSA records found", iter, tls);
+       } else {
+           tls->level = TLS_LEV_INVALID;
+           dsb_simple(tls->why, "4.7.5", "no TLSA records found");
+       }
+       tls_dane_free(dane);
+       return;
+    }
+
+    /*
+     * Some TLSA records found, but none usable, per
+     * 
+     * https://tools.ietf.org/html/draft-ietf-dane-srv-02#section-4
+     * 
+     * we MUST use TLS, and SHALL use full PKIX certificate checks.  The latter
+     * would be unwise for SMTP: no human present to "click ok" and risk of
+     * non-delivery in most cases exceeds risk of interception.
+     * 
+     * We also have a form of Goedel's incompleteness theorem in play: any list
+     * of public root CA certs is either incomplete or inconsistent (for any
+     * given verifier some of the CAs are surely not trustworthy).
+     */
+    if (tls_dane_unusable(dane)) {
+       if (tls->level == TLS_LEV_DANE) {
+           tls->level = TLS_LEV_ENCRYPT;
+           if (msg_verbose)
+               DANEWARN("TLSA records unusable", iter, tls);
+       } else {
+           tls->level = TLS_LEV_INVALID;
+           dsb_simple(tls->why, "4.7.5", "TLSA records unusable");
+       }
+       tls_dane_free(dane);
+       return;
+    }
+
+    /*
+     * With DANE trust anchors, peername matching is not configurable.
+     */
+    if (TLS_DANE_HASTA(dane)) {
+       tls->matchargv = argv_alloc(2);
+       argv_add(tls->matchargv, "hostname", "nexthop", ARGV_END);
+    }
+    tls->dane = dane;
+    tls->level = TLS_LEV_DANE;
+    return;
+}
+
+#endif
diff --git a/postfix/src/smtp/smtp_tls_sess.c b/postfix/src/smtp/smtp_tls_sess.c
deleted file mode 100644 (file)
index 74467da..0000000
+++ /dev/null
@@ -1,560 +0,0 @@
-/*++
-/* NAME
-/*     smtp_tls_sess 3
-/* SUMMARY
-/*     SMTP_TLS_SESS structure management
-/* SYNOPSIS
-/*     #include "smtp.h"
-/*
-/*     void    smtp_tls_list_init()
-/*
-/*     SMTP_TLS_SESS *smtp_tls_sess_alloc(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;
-/* 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_sess_free() destroys an SMTP_TLS_SESS structure and its
-/*     members.  A null pointer is returned for convenience.
-/*
-/*     Arguments:
-/* .IP dest
-/*     The unmodified next-hop or fall-back destination including
-/*     the optional [] and including the optional port or service.
-/* .IP host
-/*     The name of the host that we are connected to.  If the name was
-/*     obtained via an MX lookup and/or CNAME expansion (any indirection),
-/*     all those lookups must also have been validated.  If the hostname
-/*     is validated, it must be the final name after all CNAME expansion.
-/*     Otherwise, it is generally the original name or that returned by
-/*     the MX lookup (see smtp_cname_overrides_servername).
-/* .IP port
-/*     The remote port, network byte order.
-/* .IP valid
-/*     The DNSSEC validation status of the host name.
-/* LICENSE
-/* .ad
-/* .fi
-/*     The Secure Mailer license must be distributed with this software.
-/* AUTHOR(S)
-/*     Wietse Venema
-/*     IBM T.J. Watson Research
-/*     P.O. Box 704
-/*     Yorktown Heights, NY 10598, USA
-/*
-/*     Viktor Dukhovni
-/*
-/*     TLS support originally by:
-/*     Lutz Jaenicke
-/*     BTU Cottbus
-/*     Allgemeine Elektrotechnik
-/*     Universitaetsplatz 3-4
-/*     D-03044 Cottbus, Germany
-/*--*/
-
-/* System library. */
-
-#include <sys_defs.h>
-
-#ifdef USE_TLS
-
-#include <stdlib.h>
-#include <string.h>
-
-#ifdef STRCASECMP_IN_STRINGS_H
-#include <strings.h>
-#endif
-
-/* Utility library. */
-
-#include <msg.h>
-#include <mymalloc.h>
-#include <vstring.h>
-#include <stringops.h>
-#include <valid_hostname.h>
-
-/* Global library. */
-
-#include <mail_params.h>
-#include <maps.h>
-
-/* Application-specific. */
-
-#include "smtp.h"
-
-static MAPS *tls_policy;               /* lookup table(s) */
-static MAPS *tls_per_site;             /* lookup table(s) */
-
-/* smtp_tls_list_init - initialize per-site policy lists */
-
-void    smtp_tls_list_init(void)
-{
-    if (*var_smtp_tls_policy) {
-       tls_policy = maps_create(SMTP_X(TLS_POLICY), var_smtp_tls_policy,
-                                DICT_FLAG_LOCK | DICT_FLAG_FOLD_FIX);
-       if (*var_smtp_tls_per_site)
-           msg_warn("%s ignored when %s is not empty.",
-                    SMTP_X(TLS_PER_SITE), SMTP_X(TLS_POLICY));
-       return;
-    }
-    if (*var_smtp_tls_per_site) {
-       tls_per_site = maps_create(SMTP_X(TLS_PER_SITE), var_smtp_tls_per_site,
-                                  DICT_FLAG_LOCK | DICT_FLAG_FOLD_FIX);
-    }
-}
-
-/* policy_name - printable tls policy level */
-
-static const char *policy_name(int tls_level)
-{
-    const char *name = str_tls_level(tls_level);
-
-    if (name == 0)
-       name = "unknown";
-    return name;
-}
-
-/* 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)
-{
-    const char *lookup;
-
-    /*
-     * Look up a non-default policy. In case of multiple lookup results, the
-     * precedence order is a permutation of the TLS enforcement level order:
-     * VERIFY, ENCRYPT, NONE, MAY, NOTFOUND. I.e. we override MAY with a more
-     * specific policy including NONE, otherwise we choose the stronger
-     * enforcement level.
-     */
-    if ((lookup = maps_find(tls_per_site, site_name, 0)) != 0) {
-       if (!strcasecmp(lookup, "NONE")) {
-           /* NONE overrides MAY or NOTFOUND. */
-           if (*site_level <= TLS_LEV_MAY)
-               *site_level = TLS_LEV_NONE;
-       } else if (!strcasecmp(lookup, "MAY")) {
-           /* MAY overrides NOTFOUND but not NONE. */
-           if (*site_level < TLS_LEV_NONE)
-               *site_level = TLS_LEV_MAY;
-       } else if (!strcasecmp(lookup, "MUST_NOPEERMATCH")) {
-           if (*site_level < TLS_LEV_ENCRYPT)
-               *site_level = TLS_LEV_ENCRYPT;
-       } else if (!strcasecmp(lookup, "MUST")) {
-           if (*site_level < TLS_LEV_VERIFY)
-               *site_level = TLS_LEV_VERIFY;
-       } 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;
-           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");
-       *site_level = TLS_LEV_INVALID;
-       return;
-    }
-    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)
-{
-    const char *lookup;
-    char   *policy;
-    char   *saved_policy;
-    char   *tok;
-    const char *err;
-    char   *name;
-    char   *val;
-    static VSTRING *cbuf;
-
-#undef FREE_RETURN
-#define FREE_RETURN do { myfree(saved_policy); return; } while (0)
-
-#define WHERE \
-    vstring_str(vstring_sprintf(cbuf, "%s, %s \"%s\"", \
-               tls_policy->title, site_class, site_name))
-
-    if (cbuf == 0)
-       cbuf = vstring_alloc(10);
-
-    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;
-       }
-       return;
-    }
-    saved_policy = policy = mystrdup(lookup);
-
-    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;
-    }
-    *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;
-    }
-
-    /*
-     * Warn about ignored attributes when TLS is disabled.
-     */
-    if (*site_level < TLS_LEV_MAY) {
-       while ((tok = mystrtok(&policy, "\t\n\r ,")) != 0)
-           msg_warn("%s: ignoring attribute \"%s\" with TLS disabled",
-                    WHERE, tok);
-       FREE_RETURN;
-    }
-
-    /*
-     * Errors in attributes may have security consequences, don't ignore
-     * errors that can degrade security.
-     */
-    while ((tok = mystrtok(&policy, "\t\n\r ,")) != 0) {
-       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;
-       }
-       /* 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;
-           }
-           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;
-           }
-           tls->grade = mystrdup(val);
-           continue;
-       }
-       /* Only one instance per policy. */
-       if (!strcasecmp(name, "protocols")) {
-           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;
-           }
-           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;
-           }
-           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;
-           }
-           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 (!strcasecmp(name, "exclude")) {
-           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;
-           }
-           tls->exclusions = vstring_strcpy(vstring_alloc(10), val);
-           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;
-    }
-
-    FREE_RETURN;
-}
-
-/* tls_policy_lookup - look up destination TLS policy */
-
-static void tls_policy_lookup(SMTP_TLS_SESS *tls, int *site_level,
-                                     const char *site_name,
-                                     const char *site_class, DSN_BUF *why)
-{
-
-    /*
-     * Only one lookup with [nexthop]:port, [nexthop] or nexthop:port These
-     * are never the domain part of localpart@domain, rather they are
-     * explicit nexthops from transport:nexthop, and match only the
-     * corresponding policy. Parent domain matching (below) applies only to
-     * 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);
-       return;
-    }
-    do {
-       tls_policy_lookup_one(tls, site_level, site_name, site_class, why);
-    } while (*site_level == TLS_LEV_NOTFOUND
-            && (site_name = strchr(site_name + 1, '.')) != 0);
-}
-
-/* set_cipher_grade - Set cipher grade and exclusions */
-
-static void set_cipher_grade(SMTP_TLS_SESS *tls)
-{
-    const char *mand_exclude = "";
-    const char *also_exclude = "";
-
-    /*
-     * Use main.cf cipher level if no per-destination value specified. With
-     * mandatory encryption at least encrypt, and with mandatory verification
-     * at least authenticate!
-     */
-    switch (tls->level) {
-    case TLS_LEV_INVALID:
-    case TLS_LEV_NONE:
-       return;
-
-    case TLS_LEV_MAY:
-       if (tls->grade == 0)
-           tls->grade = mystrdup(var_smtp_tls_ciph);
-       break;
-
-    case TLS_LEV_ENCRYPT:
-       if (tls->grade == 0)
-           tls->grade = mystrdup(var_smtp_tls_mand_ciph);
-       mand_exclude = var_smtp_tls_mand_excl;
-       also_exclude = "eNULL";
-       break;
-
-    case TLS_LEV_FPRINT:
-    case TLS_LEV_VERIFY:
-    case TLS_LEV_SECURE:
-       if (tls->grade == 0)
-           tls->grade = mystrdup(var_smtp_tls_mand_ciph);
-       mand_exclude = var_smtp_tls_mand_excl;
-       also_exclude = "aNULL";
-       break;
-    }
-
-#define ADD_EXCLUDE(vstr, str) \
-    do { \
-       if (*(str)) \
-           vstring_sprintf_append((vstr), "%s%s", \
-                                  VSTRING_LEN(vstr) ? " " : "", (str)); \
-    } while (0)
-
-    /*
-     * The "exclude" policy table attribute overrides main.cf exclusion
-     * lists.
-     */
-    if (tls->exclusions == 0) {
-       tls->exclusions = vstring_alloc(10);
-       ADD_EXCLUDE(tls->exclusions, var_smtp_tls_excl_ciph);
-       ADD_EXCLUDE(tls->exclusions, mand_exclude);
-    }
-    ADD_EXCLUDE(tls->exclusions, also_exclude);
-}
-
-/* smtp_tls_sess_alloc - session TLS policy parameters */
-
-SMTP_TLS_SESS *smtp_tls_sess_alloc(DSN_BUF *why, const char *dest,
-                                const char *host, unsigned port, int valid)
-{
-    const char *myname = "smtp_tls_sess_alloc";
-    int     global_level;
-    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;
-    }
-    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.
-     */
-    site_level = TLS_LEV_NOTFOUND;
-
-    if (tls_policy) {
-       tls_policy_lookup(tls, &site_level, dest, "next-hop destination", why);
-    } else if (tls_per_site) {
-       tls_site_lookup(tls, &site_level, dest, "next-hop destination", why);
-       if (site_level != TLS_LEV_INVALID
-           && strcasecmp(dest, host) != 0)
-           tls_site_lookup(tls, &site_level, host, "server hostname", why);
-
-       /*
-        * Override a wild-card per-site policy with a more specific global
-        * policy.
-        * 
-        * With the original TLS patch, 1) a per-site ENCRYPT could not override
-        * a global VERIFY, and 2) a combined per-site (NONE+MAY) policy
-        * produced inconsistent results: it changed a global VERIFY into
-        * NONE, while producing MAY with all weaker global policy settings.
-        * 
-        * With the current implementation, a combined per-site (NONE+MAY)
-        * consistently overrides global policy with NONE, and global policy
-        * can override only a per-site MAY wildcard. That is, specific
-        * policies consistently override wildcard policies, and
-        * (non-wildcard) per-site policies consistently override global
-        * policies.
-        */
-       if (site_level == TLS_LEV_MAY && global_level > TLS_LEV_MAY)
-           site_level = global_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;
-       break;
-    }
-
-    /*
-     * Use main.cf protocols setting if not set in per-destination table.
-     */
-    if (tls->level > TLS_LEV_NONE && tls->protocols == 0)
-       tls->protocols =
-           mystrdup((tls->level == TLS_LEV_MAY) ?
-                    var_smtp_tls_proto : var_smtp_tls_mand_proto);
-
-    /*
-     * Compute cipher grade (if set in per-destination table, else
-     * set_cipher() uses main.cf settings) and security level dependent
-     * cipher exclusion list.
-     */
-    set_cipher_grade(tls);
-
-    /*
-     * Use main.cf cert_match setting if not set in per-destination table.
-     */
-    switch (tls->level) {
-    case TLS_LEV_INVALID:
-    case TLS_LEV_NONE:
-    case TLS_LEV_MAY:
-    case TLS_LEV_ENCRYPT:
-    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, |");
-       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, :");
-       break;
-    default:
-       msg_panic("unexpected TLS security level: %d",
-                 tls->level);
-    }
-
-    if (msg_verbose && (tls_policy || tls_per_site))
-       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 */
-
-SMTP_TLS_SESS *smtp_tls_sess_free(SMTP_TLS_SESS *tls)
-{
-
-    if (tls->protocols)
-       myfree(tls->protocols);
-    if (tls->grade)
-       myfree(tls->grade);
-    if (tls->exclusions)
-       vstring_free(tls->exclusions);
-    if (tls->matchargv)
-       argv_free(tls->matchargv);
-
-    myfree((char *) tls);
-    return (0);
-}
-
-#endif
index fae7358ba02ef1711b53c355ef71ad58c4e379e1..ce2b31d26cac96df6d5206a6d1f688183a8219d3 100644 (file)
 /*     not contain RFC 822 style comments or phrases.
 /* .PP
 /*     Available in Postfix version 2.1 and later:
-/* .IP "\fBresolve_null_domain (no)\fR"
-/*     Resolve an address that ends in the "@" null domain as if the
-/*     local hostname were specified, instead of rejecting the address as
-/*     invalid.
 /* .IP "\fBsmtpd_reject_unlisted_sender (no)\fR"
 /*     Request that the Postfix SMTP server rejects mail from unknown
 /*     sender addresses, even when no explicit reject_unlisted_sender
index 3ee7ee12fc44244703e010655270fdac262299eb..a27dc701225acd7cb0ed83bdbfa49839314b2064 100644 (file)
@@ -1336,7 +1336,7 @@ static int permit_tls_clientcerts(SMTPD_STATE *state, int permit_all_certs)
        int     i;
        char   *prints[2];
 
-       prints[0] = state->tls_context->peer_fingerprint;
+       prints[0] = state->tls_context->peer_cert_fprint;
        prints[1] = state->tls_context->peer_pkey_fprint;
 
        /* After lookup error, leave relay_ccerts->error at non-zero value. */
@@ -2794,7 +2794,7 @@ static int check_ccert_access(SMTPD_STATE *state, const char *table,
        int     i;
        char   *prints[2];
 
-       prints[0] = state->tls_context->peer_fingerprint;
+       prints[0] = state->tls_context->peer_cert_fprint;
        prints[1] = state->tls_context->peer_pkey_fprint;
 
        for (i = 0; i < 2; ++i) {
@@ -3614,8 +3614,8 @@ static int check_policy_service(SMTPD_STATE *state, const char *server,
      * When directly checking the fingerprint, it is OK if the issuing CA is
      * not trusted.
      */
-                         ATTR_TYPE_STR, MAIL_ATTR_CCERT_FINGERPRINT,
-                    IF_ENCRYPTED(state->tls_context->peer_fingerprint, ""),
+                         ATTR_TYPE_STR, MAIL_ATTR_CCERT_CERT_FPRINT,
+                    IF_ENCRYPTED(state->tls_context->peer_cert_fprint, ""),
                          ATTR_TYPE_STR, MAIL_ATTR_CCERT_PKEY_FPRINT,
                     IF_ENCRYPTED(state->tls_context->peer_pkey_fprint, ""),
                          ATTR_TYPE_STR, MAIL_ATTR_CRYPTO_PROTOCOL,
@@ -5689,14 +5689,14 @@ int     main(int argc, char **argv)
                        (TLS_SESS_STATE *) mymalloc(sizeof(*state.tls_context));
                    memset((char *) state.tls_context, 0,
                           sizeof(*state.tls_context));
-                   state.tls_context->peer_fingerprint =
+                   state.tls_context->peer_cert_fprint =
                        state.tls_context->peer_pkey_fprint = 0;
                }
                state.tls_context->peer_status |= TLS_CERT_FLAG_PRESENT;
-               UPDATE_STRING(state.tls_context->peer_fingerprint,
+               UPDATE_STRING(state.tls_context->peer_cert_fprint,
                              args->argv[1]);
                state.tls_context->peer_pkey_fprint =
-                   state.tls_context->peer_fingerprint;
+                   state.tls_context->peer_cert_fprint;
                resp = "OK";
                break;
 #endif
@@ -5738,7 +5738,7 @@ int     main(int argc, char **argv)
     FREE_STRING(state.helo_name);
     FREE_STRING(state.sender);
     if (state.tls_context) {
-       FREE_STRING(state.tls_context->peer_fingerprint);
+       FREE_STRING(state.tls_context->peer_cert_fprint);
        myfree((char *) state.tls_context);
     }
     exit(0);
index f1565f3bb8ddfa05527839f6097b07dc0eef8617..92bf678180ddcf129a822fb27c41c849369a0b2f 100644 (file)
@@ -1,13 +1,13 @@
 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
@@ -128,6 +128,21 @@ tls_client.o: ../../include/vstring.h
 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
index a89f456ac324c3668e9394fa8cbf745def6c2893..5519dcc5459fc26d358e3f6d1e425d6feaf26986 100644 (file)
   * 
   * XXX TLS_LEV_NOTFOUND no longer belongs in this list. The SMTP client will
   * have to use something else to report that policy table lookup failed.
+  * 
+  * The order of levels matters, but we hide most of the details in macros.
+  * 
+  * "dane" vs. "fingerprint", both must lie between "encrypt" and "verify".
+  * 
+  * - With "may" and higher, TLS is enabled.
+  * 
+  * - With "encrypt" and higher, TLS encryption must be applied.
+  * 
+  * - Strictly above "encrypt", the peer certificate must match.
+  * 
+  * - At "dane" and higher, the peer certificate must also be trusted. With
+  * "dane" the trust may be self-asserted, so we only log trust verification
+  * errors when TA associations are involved.
   */
 #define TLS_LEV_INVALID                -2      /* sentinel */
 #define TLS_LEV_NOTFOUND       -1      /* XXX not in policy table */
 #define TLS_LEV_NONE           0       /* plain-text only */
 #define TLS_LEV_MAY            1       /* wildcard */
 #define TLS_LEV_ENCRYPT                2       /* encrypted connection */
-#define TLS_LEV_DANE           3       /* "peer" CA-less verification */
-#define TLS_LEV_FPRINT         4       /* "peer" CA-less verification */
-#define TLS_LEV_VERIFY         5       /* certificate verified */
-#define TLS_LEV_SECURE         6       /* "secure" verification */
+#define TLS_LEV_FPRINT         3       /* "peer" CA-less verification */
+#define TLS_LEV_DANE           4       /* Opportunistic TLSA policy */
+#define TLS_LEV_DANE_ONLY      5       /* Required TLSA policy */
+#define TLS_LEV_VERIFY         6       /* certificate verified */
+#define TLS_LEV_SECURE         7       /* "secure" verification */
+
+#define TLS_REQUIRED(l)                ((l) > TLS_LEV_MAY)
+#define TLS_MUST_MATCH(l)      ((l) > TLS_LEV_ENCRYPT)
+#define TLS_MUST_TRUST(l)      ((l) >= TLS_LEV_DANE)
+#define TLS_MUST_PKIX(l)       ((l) >= TLS_LEV_VERIFY)
 
 extern const NAME_CODE tls_level_table[];
 
@@ -53,8 +73,8 @@ extern const NAME_CODE tls_level_table[];
 #include <openssl/rand.h>
 #include <openssl/ssl.h>
 
-#if (OPENSSL_VERSION_NUMBER < 0x00905100L)
-#error "need OpenSSL version 0.9.5 or later"
+#if (OPENSSL_VERSION_NUMBER < 0x00090700f)
+#error "need OpenSSL version 0.9.7 or later"
 #endif
 
  /*
@@ -71,6 +91,85 @@ extern const NAME_CODE tls_level_table[];
 #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 */
+#define TLS_DANE_FLAG_NORRS    (1<<2)  /* Nothing found in DNS */
+#define TLS_DANE_FLAG_EMPTY    (1<<3)  /* Nothing usable found in DNS */
+#define TLS_DANE_FLAG_ERROR    (1<<4)  /* TLSA record lookup error */
+
+#define tls_dane_unusable(dane)        ((dane)->flags & TLS_DANE_FLAG_EMPTY)
+#define tls_dane_notfound(dane)        ((dane)->flags & TLS_DANE_FLAG_NORRS)
+
+#define TLS_DANE_CACHE_TTL_MIN 1       /* A lot can happen in ~2 seconds */
+#define TLS_DANE_CACHE_TTL_MAX 100     /* Comparable to max_idle */
+
+ /*
+  * 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;
+
+ /*
+  * Linked list of full X509 trust-anchor certs.
+  */
+typedef struct TLS_CERTS {
+    X509   *cert;
+    struct TLS_CERTS *next;
+} TLS_CERTS;
+
+ /*
+  * Linked list of full EVP_PKEY trust-anchor public keys.
+  */
+typedef struct TLS_PKEYS {
+    EVP_PKEY *pkey;
+    struct TLS_PKEYS *next;
+} TLS_PKEYS;
+
+ /*
+  * 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_CERTS *certs;                  /* Full trust-anchor certificates */
+    TLS_PKEYS *pkeys;                  /* Full trust-anchor public keys */
+    int     flags;                     /* Conflate cert and pkey digests */
+    time_t  expires;                   /* Expiration time of this record */
+    int     refs;                      /* Reference count */
+} TLS_DANE;
+
+#define TLS_DANE_HASTA(d)      ((d) ? (d)->ta : 0)
+#define TLS_DANE_HASEE(d)      ((d) ? (d)->ee : 0)
+
+ /*
+  * tls_dane.c
+  */
+extern int tls_dane_avail(void);
+extern void tls_dane_flush(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 TLS_DANE *tls_dane_resolve(const char *, const char *, unsigned);
+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.
@@ -83,7 +182,7 @@ typedef struct {
     /* Public, read-only. */
     char   *peer_CN;                   /* Peer Common Name */
     char   *issuer_CN;                 /* Issuer Common Name */
-    char   *peer_fingerprint;          /* ASCII fingerprint */
+    char   *peer_cert_fprint;          /* ASCII certificate fingerprint */
     char   *peer_pkey_fprint;          /* ASCII public key fingerprint */
     int     peer_status;               /* Certificate and match status */
     const char *protocol;
@@ -101,7 +200,11 @@ typedef struct {
     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;
@@ -160,8 +263,9 @@ struct TLS_APPL_STATE {
 };
 
  /*
-  * tls_misc.c One time finalization of application context.
+  * tls_misc.c Application-context update and disposal.
   */
+extern void tls_update_app_logmask(TLS_APPL_STATE *, int);
 extern void tls_free_app_context(TLS_APPL_STATE *);
 
  /*
@@ -191,9 +295,22 @@ extern void tls_param_init(void);
 #undef  SSL_OP_NO_TLSv1_2
 #define SSL_OP_NO_TLSv1_2      0L      /* Noop */
 #endif
-#define TLS_KNOWN_PROTOCOLS    \
+#define TLS_KNOWN_PROTOCOLS \
        ( TLS_PROTOCOL_SSLv2 | TLS_PROTOCOL_SSLv3 | TLS_PROTOCOL_TLSv1 \
           | TLS_PROTOCOL_TLSv1_1 | TLS_PROTOCOL_TLSv1_2 )
+#define TLS_SSL_OP_PROTOMASK(m) \
+           ((((m) & TLS_PROTOCOL_SSLv2) ? SSL_OP_NO_SSLv2 : 0L) \
+            | (((m) & TLS_PROTOCOL_SSLv3) ? SSL_OP_NO_SSLv3 : 0L) \
+            | (((m) & TLS_PROTOCOL_TLSv1) ? SSL_OP_NO_TLSv1 : 0L) \
+            | (((m) & TLS_PROTOCOL_TLSv1_1) ? SSL_OP_NO_TLSv1_1 : 0L) \
+            | (((m) & TLS_PROTOCOL_TLSv1_2) ? SSL_OP_NO_TLSv1_2 : 0L))
+
+/*
+ * SSL options that are managed via dedicated Postfix features, rather than
+ * just exposed via hex codes or named elements of tls_ssl_options.
+ */
+#define TLS_SSL_OP_MANAGED_BITS \
+       (SSL_OP_CIPHER_SERVER_PREFERENCE | TLS_SSL_OP_PROTOMASK(~0))
 
 extern int tls_protocol_mask(const char *);
 
@@ -256,6 +373,7 @@ typedef struct {
     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 *);
@@ -272,11 +390,11 @@ extern TLS_SESS_STATE *tls_client_start(const TLS_CLIENT_START_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
@@ -402,6 +520,7 @@ extern RSA *tls_tmp_rsa_cb(SSL *, int, int);
 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 *);
 
@@ -409,8 +528,8 @@ extern void tls_log_verify_error(TLS_SESS_STATE *);
   * tls_fprint.c
   */
 extern char *tls_digest_encode(const unsigned char *, int);
-extern char *tls_fprint(const char *, int, const char *);
-extern char *tls_fingerprint(X509 *, const char *);
+extern char *tls_data_fprint(const char *, int, const char *);
+extern char *tls_cert_fprint(X509 *, const char *);
 extern char *tls_pkey_fprint(X509 *, const char *);
 extern char *tls_serverid_digest(const TLS_CLIENT_START_PROPS *, long,
                                         const char *);
index 075b113aabe30456b103e5649cb70f5918973358..1f4ec41f17ea1e704428f5d6a777b991fc04a3b4 100644 (file)
@@ -204,41 +204,6 @@ int     tls_bio(int fd, int timeout, TLS_SESS_STATE *TLScontext,
            msg_panic("%s: nothing to do here", myname);
        err = SSL_get_error(TLScontext->con, status);
 
-#if (OPENSSL_VERSION_NUMBER <= 0x0090581fL)
-
-       /*
-        * There is a bug up to and including OpenSSL-0.9.5a: if an error
-        * occurs while checking the peers certificate due to some
-        * certificate error (e.g. as happend with a RSA-padding error), the
-        * error is put onto the error stack. If verification is not
-        * enforced, this error should be ignored, but the error-queue is not
-        * cleared, so we can find this error here. The bug has been fixed on
-        * May 28, 2000.
-        * 
-        * This bug so far has only manifested as 4800:error:0407006A:rsa
-        * routines:RSA_padding_check_PKCS1_type_1:block type is not
-        * 01:rsa_pk1.c:100: 4800:error:04067072:rsa
-        * routines:RSA_EAY_PUBLIC_DECRYPT:padding check
-        * failed:rsa_eay.c:396: 4800:error:0D079006:asn1 encoding
-        * routines:ASN1_verify:bad get asn1 object call:a_verify.c:109: so
-        * that we specifically test for this error. We print the errors to
-        * the logfile and automatically clear the error queue. Then we retry
-        * to get another error code. We cannot do better, since we can only
-        * retrieve the last entry of the error-queue without actually
-        * cleaning it on the way.
-        * 
-        * This workaround is secure, as verify_result is set to "failed"
-        * anyway.
-        */
-       if (err == SSL_ERROR_SSL) {
-           if (ERR_peek_error() == 0x0407006AL) {
-               tls_print_errors();
-               msg_info("OpenSSL <= 0.9.5a workaround called: certificate errors ignored");
-               err = SSL_get_error(TLScontext->con, status);
-           }
-       }
-#endif
-
        /*
         * Correspondence between SSL_ERROR_* error codes and tls_bio_(read,
         * write, accept, connect, shutdown) return values (for brevity:
index 2282c448903b63abc75a73380e887fffb82a73d2..43d0fb2ed46dd7e03ba66c2eca0536a0f81beebd 100644 (file)
@@ -91,7 +91,7 @@
 /* .IP TLScontext->issuer_CN
 /*     Extracted CommonName of the issuer, or zero-length string if the
 /*     information could not be extracted.
-/* .IP TLScontext->peer_fingerprint
+/* .IP TLScontext->peer_cert_fprint
 /*     At the fingerprint security level, if the peer presented a certificate
 /*     the fingerprint of the certificate.
 /* .PP
@@ -234,21 +234,6 @@ static int new_client_session_cb(SSL *ssl, SSL_SESSION *session)
        msg_info("save session %s to %s cache",
                 TLScontext->serverid, TLScontext->cache_type);
 
-#if (OPENSSL_VERSION_NUMBER < 0x00906011L) || (OPENSSL_VERSION_NUMBER == 0x00907000L)
-
-    /*
-     * Ugly Hack: OpenSSL before 0.9.6a does not store the verify result in
-     * sessions for the client side. We modify the session directly which is
-     * version specific, but this bug is version specific, too.
-     * 
-     * READ: 0-09-06-01-1 = 0-9-6-a-beta1: all versions before beta1 have this
-     * bug, it has been fixed during development of 0.9.6a. The development
-     * version of 0.9.7 can have this bug, too. It has been fixed on
-     * 2000/11/29.
-     */
-    session->verify_result = SSL_get_verify_result(TLScontext->con);
-#endif
-
     /*
      * Passivate and save the session object. Errors are non-fatal, since
      * caching is only an optimization.
@@ -503,66 +488,70 @@ TLS_APPL_STATE *tls_client_init(const TLS_CLIENT_INIT_PROPS *props)
     return (app_ctx);
 }
 
-/* match_hostname -  match hostname against pattern */
+/* match_servername -  match servername against pattern */
 
-static int match_hostname(const char *peerid,
+static int match_servername(const char *certid,
                                  const TLS_CLIENT_START_PROPS *props)
 {
     const ARGV *cmatch_argv;
     const char *nexthop = props->nexthop;
     const char *hname = props->host;
-    const char *pattern;
-    const char *pattern_left;
-    int     sub;
+    const char *domain;
+    const char *parent;
+    int     match_subdomain;
     int     i;
     int     idlen;
-    int     patlen;
+    int     domlen;
 
     if ((cmatch_argv = props->matchargv) == 0)
        return 0;
 
     /*
-     * Match the peerid against each pattern until we find a match.
+     * Match the certid against each pattern until we find a match.
      */
     for (i = 0; i < cmatch_argv->argc; ++i) {
-       sub = 0;
+       match_subdomain = 0;
        if (!strcasecmp(cmatch_argv->argv[i], "nexthop"))
-           pattern = nexthop;
+           domain = nexthop;
        else if (!strcasecmp(cmatch_argv->argv[i], "hostname"))
-           pattern = hname;
+           domain = hname;
        else if (!strcasecmp(cmatch_argv->argv[i], "dot-nexthop")) {
-           pattern = nexthop;
-           sub = 1;
+           domain = nexthop;
+           match_subdomain = 1;
        } else {
-           pattern = cmatch_argv->argv[i];
-           if (*pattern == '.' && pattern[1] != '\0') {
-               ++pattern;
-               sub = 1;
+           domain = cmatch_argv->argv[i];
+           if (*domain == '.' && domain[1] != '\0') {
+               ++domain;
+               match_subdomain = 1;
            }
        }
 
        /*
-        * Sub-domain match: peerid is any sub-domain of pattern.
+        * Sub-domain match: certid is any sub-domain of hostname.
         */
-       if (sub) {
-           if ((idlen = strlen(peerid)) > (patlen = strlen(pattern)) + 1
-               && peerid[idlen - patlen - 1] == '.'
-               && !strcasecmp(peerid + (idlen - patlen), pattern))
+       if (match_subdomain) {
+           if ((idlen = strlen(certid)) > (domlen = strlen(domain)) + 1
+               && certid[idlen - domlen - 1] == '.'
+               && !strcasecmp(certid + (idlen - domlen), domain))
                return (1);
            else
                continue;
        }
 
        /*
-        * Exact match and initial "*" match. The initial "*" in a peerid
-        * matches exactly one hostname component, under the condition that
-        * the peerid contains multiple hostname components.
+        * Exact match and initial "*" match. The initial "*" in a certid
+        * matches one (if var_tls_multi_label is false) or more hostname
+        * components under the condition that the certid contains multiple
+        * hostname components.
         */
-       if (!strcasecmp(peerid, pattern)
-           || (peerid[0] == '*' && peerid[1] == '.' && peerid[2] != 0
-               && (pattern_left = strchr(pattern, '.')) != 0
-               && strcasecmp(pattern_left + 1, peerid + 2) == 0))
-           return (1);
+       if (!strcasecmp(certid, domain)
+           || (certid[0] == '*' && certid[1] == '.' && certid[2] != 0
+                && (parent = strchr(domain, '.')) != 0
+                && (idlen = strlen(certid + 1)) <= (domlen = strlen(parent))
+                && strcasecmp(var_tls_multi_wildcard == 0 ? parent :
+                              parent + domlen - idlen,
+                              certid + 1) == 0))
+            return (1);
     }
     return (0);
 }
@@ -595,7 +584,13 @@ static void verify_extract_name(TLS_SESS_STATE *TLScontext, X509 *peercert,
     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)
+    /*
+     * With fingerprint or dane we may already be done. Otherwise, verify the
+     * peername if using traditional PKI or DANE with trust-anchors.
+     */
+    if (!TLS_CERT_IS_MATCHED(TLScontext)
+       && TLS_CERT_IS_TRUSTED(TLScontext)
+       && TLS_MUST_TRUST(props->tls_level))
        verify_peername = 1;
 
     /* Force cert processing so we can log the data? */
@@ -646,7 +641,7 @@ static void verify_extract_name(TLS_SESS_STATE *TLScontext, X509 *peercert,
                TLScontext->peer_status |= TLS_CERT_FLAG_ALTNAME;
                dnsname = tls_dns_name(gn, TLScontext);
                if (dnsname && *dnsname) {
-                   if ((dnsname_match = match_hostname(dnsname, props)) != 0)
+                   if ((dnsname_match = match_servername(dnsname, props)) != 0)
                        matched++;
                    /* Keep the first matched name. */
                    if (TLScontext->peer_CN
@@ -680,7 +675,7 @@ static void verify_extract_name(TLS_SESS_STATE *TLScontext, X509 *peercert,
        if (TLScontext->peer_CN == 0) {
            TLScontext->peer_CN = tls_peer_CN(peercert, TLScontext);
            if (*TLScontext->peer_CN)
-               matched = match_hostname(TLScontext->peer_CN, props);
+               matched = match_servername(TLScontext->peer_CN, props);
            if (verify_peername && matched)
                TLScontext->peer_status |= TLS_CERT_FLAG_MATCHED;
            if (verbose)
@@ -720,26 +715,22 @@ static void verify_extract_name(TLS_SESS_STATE *TLScontext, X509 *peercert,
 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_cert_fprint = tls_cert_fprint(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.
+     * Whether the level is "dane" or "fingerprint" when the peer certificate
+     * is matched without resorting to a separate CA, we set both the trusted
+     * and matched bits.  This simplifies logic in smtp_proto.c where "dane"
+     * must be trusted and matched, since some "dane" TLSA RRsets do use CAs.
+     * 
+     * This also suppresses spurious logging of the peer certificate as
+     * untrusted in verify_extract_name().
      */
-    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;
 }
 
  /*
@@ -752,7 +743,7 @@ TLS_SESS_STATE *tls_client_start(const TLS_CLIENT_START_PROPS *props)
     int     sts;
     int     protomask;
     const char *cipher_list;
-    SSL_SESSION *session;
+    SSL_SESSION *session = 0;
     const SSL_CIPHER *cipher;
     X509   *peercert;
     TLS_SESS_STATE *TLScontext;
@@ -762,9 +753,11 @@ TLS_SESS_STATE *tls_client_start(const TLS_CLIENT_START_PROPS *props)
 
     /*
      * When certificate verification is required, log trust chain validation
-     * errors even when disabled by default for opportunistic sessions.
+     * errors even when disabled by default for opportunistic sessions. For
+     * "dane" this only applies when using trust-anchor associations.
      */
-    if (props->tls_level >= TLS_LEV_VERIFY)
+    if (TLS_MUST_TRUST(props->tls_level)
+       && (props->tls_level != TLS_LEV_DANE || TLS_DANE_HASTA(props->dane)))
        log_mask |= TLS_LOG_UNTRUSTED;
 
     if (log_mask & TLS_LOG_VERBOSE)
@@ -783,6 +776,9 @@ TLS_SESS_STATE *tls_client_start(const TLS_CLIENT_START_PROPS *props)
                 props->namaddr, props->protocols);
        return (0);
     }
+    /* The DANE level requires TLS 1.0 or later, not SSLv2 or SSLv3. */
+    if (props->tls_level == TLS_LEV_DANE)
+       protomask |= TLS_PROTOCOL_SSLv3 | TLS_PROTOCOL_SSLv2;
 
     /*
      * Per session cipher selection for sessions with mandatory encryption
@@ -814,6 +810,15 @@ TLS_SESS_STATE *tls_client_start(const TLS_CLIENT_START_PROPS *props)
      * 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);
 
@@ -832,6 +837,9 @@ TLS_SESS_STATE *tls_client_start(const TLS_CLIENT_START_PROPS *props)
     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();
@@ -849,12 +857,7 @@ TLS_SESS_STATE *tls_client_start(const TLS_CLIENT_START_PROPS *props)
      * Apply session protocol restrictions.
      */
     if (protomask != 0)
-       SSL_set_options(TLScontext->con,
-                  ((protomask & TLS_PROTOCOL_TLSv1) ? SSL_OP_NO_TLSv1 : 0L)
-            | ((protomask & TLS_PROTOCOL_TLSv1_1) ? SSL_OP_NO_TLSv1_1 : 0L)
-            | ((protomask & TLS_PROTOCOL_TLSv1_2) ? SSL_OP_NO_TLSv1_2 : 0L)
-                | ((protomask & TLS_PROTOCOL_SSLv3) ? SSL_OP_NO_SSLv3 : 0L)
-              | ((protomask & TLS_PROTOCOL_SSLv2) ? SSL_OP_NO_SSLv2 : 0L));
+       SSL_set_options(TLScontext->con, TLS_SSL_OP_PROTOMASK(protomask));
 
     /*
      * XXX To avoid memory leaks we must always call SSL_SESSION_free() after
@@ -866,24 +869,38 @@ TLS_SESS_STATE *tls_client_start(const TLS_CLIENT_START_PROPS *props)
        if (session) {
            SSL_set_session(TLScontext->con, session);
            SSL_SESSION_free(session);          /* 200411 */
-#if (OPENSSL_VERSION_NUMBER < 0x00906011L) || (OPENSSL_VERSION_NUMBER == 0x00907000L)
-
-           /*
-            * Ugly Hack: OpenSSL before 0.9.6a does not store the verify
-            * result in sessions for the client side. We modify the session
-            * directly which is version specific, but this bug is version
-            * specific, too.
-            * 
-            * READ: 0-09-06-01-1 = 0-9-6-a-beta1: all versions before beta1
-            * have this bug, it has been fixed during development of 0.9.6a.
-            * The development version of 0.9.7 can have this bug, too. It
-            * has been fixed on 2000/11/29.
-            */
-           SSL_set_verify_result(TLScontext->con, session->verify_result);
-#endif
+       }
+    }
+#ifdef TLSEXT_MAXLEN_host_name
+    if (props->tls_level == TLS_LEV_DANE
+       && strlen(props->host) <= TLSEXT_MAXLEN_host_name) {
 
+       /*
+        * With DANE sessions, send an SNI hint.  We don't care whether the
+        * server reports finding a matching certificate or not, so no
+        * callback is required to process the server response.  Our use of
+        * SNI is limited to giving servers that are (mis)configured to use
+        * SNI the best opportunity to find the certificate they promised via
+        * the associated TLSA RRs.  (Generally, server administrators should
+        * avoid SNI, and there are no plans to support SNI in the Postfix
+        * SMTP server).
+        * 
+        * Since the hostname is DNSSEC-validated, it must be a DNS FQDN and
+        * thererefore valid for use with SNI.  Failure to set a valid SNI
+        * hostname is a memory allocation error, and thus transient.  Since
+        * we must not cache the session if we failed to send the SNI name,
+        * we have little choice but to abort.
+        */
+       if (!SSL_set_tlsext_host_name(TLScontext->con, props->host)) {
+           msg_warn("%s: error setting SNI hostname to: %s", props->namaddr,
+                    props->host);
+           tls_free_context(TLScontext);
+           return (0);
        }
+       if (log_mask & TLS_LOG_DEBUG)
+           msg_info("%s: SNI hostname: %s", props->namaddr, props->host);
     }
+#endif
 
     /*
      * Before really starting anything, try to seed the PRNG a little bit
@@ -972,23 +989,25 @@ TLS_SESS_STATE *tls_client_start(const TLS_CLIENT_START_PROPS *props)
 
        /*
         * Peer name or fingerprint verification as requested.
-        * Unconditionally set peer_CN, issuer_CN and peer_fingerprint.
+        * Unconditionally set peer_CN, issuer_CN and peer_cert_fprint. 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))
            msg_info("%s: subject_CN=%s, issuer_CN=%s, "
                     "fingerprint=%s, pkey_fingerprint=%s", props->namaddr,
                     TLScontext->peer_CN, TLScontext->issuer_CN,
-                    TLScontext->peer_fingerprint,
+                    TLScontext->peer_cert_fprint,
                     TLScontext->peer_pkey_fprint);
        X509_free(peercert);
     } else {
        TLScontext->issuer_CN = mystrdup("");
        TLScontext->peer_CN = mystrdup("");
-       TLScontext->peer_fingerprint = mystrdup("");
+       TLScontext->peer_cert_fprint = mystrdup("");
        TLScontext->peer_pkey_fprint = mystrdup("");
     }
 
diff --git a/postfix/src/tls/tls_dane.c b/postfix/src/tls/tls_dane.c
new file mode 100644 (file)
index 0000000..e9c94da
--- /dev/null
@@ -0,0 +1,832 @@
+/*++
+/* NAME
+/*     tls_dane 3
+/* SUMMARY
+/*     Support for RFC 6698 (DANE) certificate matching
+/* SYNOPSIS
+/*     #include <tls.h>
+/*
+/*     int     tls_dane_avail()
+/*
+/*     void    tls_dane_flush()
+/*
+/*     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;
+/*
+/*     TLS_DANE *tls_dane_resolve(host, proto, port)
+/*     const char *host;
+/*     const char *proto;
+/*     unsigned port;
+/*
+/*     int     tls_dane_unusable(dane)
+/*     const TLS_DANE *dane;
+/*
+/*     int     tls_dane_notfound(dane)
+/*     const 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_flush() flushes all entries from the cache, and deletes
+/*     the cache.
+/*
+/*     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.
+/*
+/*     tls_dane_resolve() maps a (host, protocol, port) triple to a
+/*     a corresponding TLS_DANE policy structure found in the DNS.  The port
+/*     argument is in network byte order.  A null pointer is returned when
+/*     the DNS query for the TLSA record tempfailed.  In all other cases the
+/*     return value is a pointer to the corresponding TLS_DANE structure.
+/*     The caller must free the structure via tls_dane_free().
+/*
+/*     tls_dane_unusable() checks whether a cached TLS_DANE record is
+/*     the result of a validated RRset, with no usable elements.  In
+/*     this case, TLS is mandatory, but certificate verification is
+/*     not DANE-based.
+/*
+/*     tls_dane_notfound() checks whether a cached TLS_DANE record is
+/*     the result of a validated DNS lookup returning NODATA. In
+/*     this case, TLS is not required by RFC, though users may elect
+/*     a mandatory TLS fallback policy.
+/*
+/*     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>
+
+#ifdef USE_TLS
+#include <string.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <mymalloc.h>
+#include <stringops.h>
+#include <vstring.h>
+#include <events.h>                    /* event_time() */
+#include <timecmp.h>
+#include <ctable.h>
+
+#define STR(x) vstring_str(x)
+
+/* Global library */
+
+#include <mail_params.h>
+
+/* 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 int digest_mask;
+static TLS_TLSA **dane_locate(TLS_TLSA **, const char *);
+
+#define TLS_DANE_ENABLE_CC     (1<<0)  /* ca-constraint digests OK */
+#define TLS_DANE_ENABLE_TAA    (1<<1)  /* trust-anchor-assertion digests OK */
+
+/*
+ * This is not intended to be a long-term cache of pre-parsed TLSA data,
+ * rather we primarily want to avoid fetching and parsing the TLSA records
+ * for a single multi-homed MX host more than once per delivery. Therefore,
+ * we keep the table reasonably small.
+ */
+#define CACHE_SIZE 20
+static CTABLE *dane_cache;
+
+/* 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)
+{
+#ifdef TLSEXT_MAXLEN_host_name         /* DANE mandates client SNI. */
+    static int avail = -1;
+    const EVP_MD *sha256md;
+    const EVP_MD *sha512md;
+    static NAME_MASK ta_dgsts[] = {
+       TLS_DANE_CC, TLS_DANE_ENABLE_CC,
+       TLS_DANE_TAA, TLS_DANE_ENABLE_TAA,
+       0,
+    };
+
+    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);
+
+    digest_mask =
+       name_mask_opt(VAR_TLS_DANE_TA_DGST, ta_dgsts, var_tls_dane_ta_dgst,
+                     NAME_MASK_ANY_CASE | NAME_MASK_FATAL);
+
+    sha256len = EVP_MD_size(sha256md);
+    sha512len = EVP_MD_size(sha512md);
+
+    return (avail = 1);
+#else
+            return (0);
+#endif
+}
+
+/* tls_dane_flush - flush the cache */
+
+void    tls_dane_flush(void)
+{
+    if (dane_cache)
+       ctable_free(dane_cache);
+    dane_cache = 0;
+}
+
+/* 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->certs = 0;
+    dane->pkeys = 0;
+    dane->flags = flags;
+    dane->expires = 0;
+    dane->refs = 1;
+    return (dane);
+}
+
+static void ta_cert_insert(TLS_DANE *d, X509 *x)
+{
+    TLS_CERTS *new = (TLS_CERTS *) mymalloc(sizeof(*new));
+
+    CRYPTO_add(&x->references, 1, CRYPTO_LOCK_X509);
+    new->cert = x;
+    new->next = d->certs;
+    d->certs = new;
+}
+
+static void free_ta_certs(TLS_DANE *d)
+{
+    TLS_CERTS *head;
+    TLS_CERTS *next;
+
+    for (head = d->certs; head; head = next) {
+       next = head->next;
+       X509_free(head->cert);
+       myfree((char *) head);
+    }
+}
+
+static void ta_pkey_insert(TLS_DANE *d, EVP_PKEY *k)
+{
+    TLS_PKEYS *new = (TLS_PKEYS *) mymalloc(sizeof(*new));
+
+    CRYPTO_add(&k->references, 1, CRYPTO_LOCK_EVP_PKEY);
+    new->pkey = k;
+    new->next = d->pkeys;
+    d->pkeys = new;
+}
+
+static void free_ta_pkeys(TLS_DANE *d)
+{
+    TLS_PKEYS *head;
+    TLS_PKEYS *next;
+
+    for (head = d->pkeys; head; head = next) {
+       next = head->next;
+       EVP_PKEY_free(head->pkey);
+       myfree((char *) head);
+    }
+}
+
+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;
+
+    if (--dane->refs > 0)
+       return;
+
+    /* 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);
+    }
+
+    /* De-allocate full trust-anchor certs and pkeys */
+    free_ta_certs(dane);
+    free_ta_pkeys(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);
+}
+
+/* parse_tlsa_rrs - parse a validated TLSA RRset */
+
+static void parse_tlsa_rrs(TLS_DANE *dane, DNS_RR *rr)
+{
+    uint8_t usage;
+    uint8_t selector;
+    uint8_t mtype;
+    int     mlen;
+    const unsigned char *p;
+    X509   *x = 0;                     /* OpenSSL tries to re-use *x if x!=0 */
+    EVP_PKEY *k = 0;                   /* OpenSSL tries to re-use *k if k!=0 */
+
+    if (rr == 0)
+       msg_panic("null TLSA rr");
+
+    for ( /* nop */ ; rr; rr = rr->next) {
+       const char *mdalg = 0;
+       int     mdlen;
+       char   *digest;
+       int     same = (strcasecmp(rr->rname, rr->qname) == 0);
+       uint8_t *ip = (uint8_t *) rr->data;
+
+#define rcname(rr) (same ? "" : rr->qname)
+#define rarrow(rr) (same ? "" : " -> ")
+
+       if (rr->type != T_TLSA)
+           msg_panic("unexpected non-TLSA RR type %u for %s%s%s", rr->type,
+                     rcname(rr), rarrow(rr), rr->rname);
+
+       /* Skip malformed */
+       if ((mlen = rr->data_len - 3) < 0) {
+           msg_warn("truncated length %u RR: %s%s%s IN TLSA ...",
+               (unsigned) rr->data_len, rcname(rr), rarrow(rr), rr->rname);
+           continue;
+       }
+       switch (usage = *ip++) {
+       default:
+           msg_warn("unsupported certificate usage %u in RR: "
+                    "%s%s%s IN TLSA %u ...", usage,
+                    rcname(rr), rarrow(rr), rr->rname, usage);
+           continue;
+       case DNS_TLSA_USAGE_CA_CONSTRAINT:
+       case DNS_TLSA_USAGE_TRUST_ANCHOR_ASSERTION:
+       case DNS_TLSA_USAGE_SERVICE_CERTIFICATE_CONSTRAINT:
+       case DNS_TLSA_USAGE_DOMAIN_ISSUED_CERTIFICATE:
+           break;
+       }
+
+       switch (selector = *ip++) {
+       default:
+           msg_warn("unsupported selector %u in RR: "
+                    "%s%s%s IN TLSA %u %u ...", selector,
+                    rcname(rr), rarrow(rr), rr->rname, usage, selector);
+           continue;
+       case DNS_TLSA_SELECTOR_FULL_CERTIFICATE:
+       case DNS_TLSA_SELECTOR_SUBJECTPUBLICKEYINFO:
+           break;
+       }
+
+       switch (mtype = *ip++) {
+       default:
+           msg_warn("unsupported matching type %u in RR: "
+                    "%s%s%s IN TLSA %u %u %u ...", mtype, rcname(rr),
+                    rarrow(rr), rr->rname, usage, selector, mtype);
+           continue;
+       case DNS_TLSA_MATCHING_TYPE_SHA256:
+           if (!mdalg) {
+               mdalg = sha256;
+               mdlen = sha256len;
+           }
+           /* FALLTHROUGH */
+       case DNS_TLSA_MATCHING_TYPE_SHA512:
+           if (!mdalg) {
+               mdalg = sha512;
+               mdlen = sha512len;
+           }
+           if (mlen != mdlen) {
+               msg_warn("malformed %s digest, length %u, in RR: "
+                        "%s%s%s IN TLSA %u %u %u ...", mdalg, mlen,
+                        rcname(rr), rarrow(rr), rr->rname,
+                        usage, selector, mtype);
+               continue;
+           }
+           switch (usage) {
+           case DNS_TLSA_USAGE_CA_CONSTRAINT:
+               if (!(digest_mask & TLS_DANE_ENABLE_CC)) {
+                   msg_warn("%s trust-anchor %s digests disabled, in RR: "
+                            "%s%s%s IN TLSA %u %u %u ...", TLS_DANE_CC,
+                            mdalg, rcname(rr), rarrow(rr), rr->rname,
+                            usage, selector, mtype);
+                   continue;
+               }
+               break;
+           case DNS_TLSA_USAGE_TRUST_ANCHOR_ASSERTION:
+               if (!(digest_mask & TLS_DANE_ENABLE_TAA)) {
+                   msg_warn("%s trust-anchor %s digests disabled, in RR: "
+                            "%s%s%s IN TLSA %u %u %u ...", TLS_DANE_TAA,
+                            mdalg, rcname(rr), rarrow(rr), rr->rname,
+                            usage, selector, mtype);
+                   continue;
+               }
+               break;
+           }
+           dane_add(dane, usage, selector, mdalg,
+                  digest = tls_digest_encode((unsigned char *) ip, mdlen));
+           break;
+
+       case DNS_TLSA_MATCHING_TYPE_NO_HASH_USED:
+           p = (unsigned char *) ip;
+
+           /* Validate the cert or public key via d2i_mumble() */
+           switch (selector) {
+           case DNS_TLSA_SELECTOR_FULL_CERTIFICATE:
+               if (!d2i_X509(&x, &p, mlen)) {
+                   msg_warn("malformed %s in RR: "
+                            "%s%s%s IN TLSA %u %u %u ...", "certificate",
+                            rcname(rr), rarrow(rr), rr->rname,
+                            usage, selector, mtype);
+                   continue;
+               }
+
+               /*
+                * When a full trust-anchor certificate is published via DNS,
+                * we may need to use it to validate the server trust chain.
+                * Store it away for later use.  We collapse certificate
+                * usage 0/2 because MTAs don't stock a complete list of the
+                * usual browser-trusted CAs.  Thus, here (and in the public
+                * key case below) we treat the usages identically.
+                */
+               switch (usage) {
+               case DNS_TLSA_USAGE_CA_CONSTRAINT:
+               case DNS_TLSA_USAGE_TRUST_ANCHOR_ASSERTION:
+                   ta_cert_insert(dane, x);
+                   break;
+               }
+               X509_free(x);
+               break;
+           case DNS_TLSA_SELECTOR_SUBJECTPUBLICKEYINFO:
+               if (!d2i_PUBKEY(&k, &p, mlen)) {
+                   msg_warn("malformed %s in RR: "
+                            "%s%s%s IN TLSA %u %u %u ...", "public key",
+                            rcname(rr), rarrow(rr), rr->rname,
+                            usage, selector, mtype);
+                   continue;
+               }
+               /* See full cert case above */
+               switch (usage) {
+               case DNS_TLSA_USAGE_CA_CONSTRAINT:
+               case DNS_TLSA_USAGE_TRUST_ANCHOR_ASSERTION:
+                   ta_pkey_insert(dane, k);
+                   break;
+               }
+               EVP_PKEY_free(k);
+               break;
+           }
+
+           /*
+            * The cert or key was valid, just digest the raw object, and
+            * encode the digest value.  We choose SHA256.
+            */
+           dane_add(dane, usage, selector, sha256,
+                    digest = tls_data_fprint((char *) ip, mlen, sha256));
+           break;
+       }
+       if (msg_verbose || dane_verbose)
+           msg_info("using DANE RR: %s%s%s IN TLSA %u %u %u %s",
+                    rcname(rr), rarrow(rr), rr->rname,
+                    usage, selector, mtype, digest);
+       myfree(digest);
+    }
+
+    if (dane->ta == 0 && dane->ee == 0)
+       dane->flags |= TLS_DANE_FLAG_EMPTY;
+}
+
+/* dane_lookup - TLSA record lookup, ctable style */
+
+static void *dane_lookup(const char *tlsa_fqdn, void *unused_ctx)
+{
+    static VSTRING *why = 0;
+    int     ret;
+    DNS_RR *rrs = 0;
+    TLS_DANE *dane;
+
+    if (why == 0)
+       why = vstring_alloc(10);
+
+    dane = tls_dane_alloc(0);
+    ret = dns_lookup(tlsa_fqdn, T_TLSA, RES_USE_DNSSEC, &rrs, 0, why);
+
+    switch (ret) {
+    case DNS_OK:
+       if (TLS_DANE_CACHE_TTL_MIN && rrs->ttl < TLS_DANE_CACHE_TTL_MIN)
+           rrs->ttl = TLS_DANE_CACHE_TTL_MIN;
+       if (TLS_DANE_CACHE_TTL_MAX && rrs->ttl > TLS_DANE_CACHE_TTL_MAX)
+           rrs->ttl = TLS_DANE_CACHE_TTL_MAX;
+
+       /* One more second to account for discrete time */
+       dane->expires = 1 + event_time() + rrs->ttl;
+
+       if (rrs->dnssec_valid)
+           parse_tlsa_rrs(dane, rrs);
+
+       dns_rr_free(rrs);
+       break;
+
+    case DNS_NOTFOUND:
+       dane->flags |= TLS_DANE_FLAG_NORRS;
+       dane->expires = 1 + event_time() + TLS_DANE_CACHE_TTL_MIN;
+       break;
+
+    default:
+       msg_warn("DANE TLSA lookup problem: %s", STR(why));
+       dane->flags |= TLS_DANE_FLAG_ERROR;
+       break;
+    }
+
+    return ((void *) tls_dane_final(dane));
+}
+
+/* tls_dane_resolve - cached map: (host, proto, port) -> TLS_DANE */
+
+TLS_DANE *tls_dane_resolve(const char *host, const char *proto,
+                                  unsigned port)
+{
+    static VSTRING *qname;
+    TLS_DANE *dane;
+
+    if (!tls_dane_avail())
+       return (0);
+
+    if (!dane_cache)
+       dane_cache = ctable_create(CACHE_SIZE, dane_lookup, dane_free, 0);
+
+    if (qname == 0)
+       qname = vstring_alloc(64);
+    vstring_sprintf(qname, "_%u._%s.%s", ntohs(port), proto, host);
+    dane = (TLS_DANE *) ctable_locate(dane_cache, STR(qname));
+    if (timecmp(event_time(), dane->expires) > 0)
+       dane = (TLS_DANE *) ctable_refresh(dane_cache, STR(qname));
+
+    if (dane->flags & TLS_DANE_FLAG_ERROR)
+       return (0);
+
+    ++dane->refs;
+    return (dane);
+}
+
+/* tls_dane_load_trustfile - load trust anchor certs or keys from file */
+
+int     tls_dane_load_trustfile(TLS_DANE *dane, const char *tafile)
+{
+    BIO    *bp;
+    char   *name = 0;
+    char   *header = 0;
+    unsigned char *data = 0;
+    long    len;
+    int     tacount;
+    char   *errtype = 0;               /* if error: cert or pkey? */
+
+    /* nop */
+    if (tafile == 0 || *tafile == 0)
+       return (1);
+
+    /*
+     * On each call, PEM_read() wraps a stdio file in a BIO_NOCLOSE bio,
+     * calls PEM_read_bio() and then frees the bio.  It is just as easy to
+     * open a BIO as a stdio file, so we use BIOs and call PEM_read_bio()
+     * directly.
+     */
+    if ((bp = BIO_new_file(tafile, "r")) == NULL) {
+       msg_warn("error opening trust anchor file: %s: %m", tafile);
+       return (0);
+    }
+    /* Don't report old news */
+    ERR_clear_error();
+
+    for (tacount = 0;
+        errtype == 0 && PEM_read_bio(bp, &name, &header, &data, &len);
+        ++tacount) {
+       const unsigned char *p = data;
+       int     usage = DNS_TLSA_USAGE_TRUST_ANCHOR_ASSERTION;
+       int     selector;
+       char   *digest;
+
+       if (strcmp(name, PEM_STRING_X509) == 0
+           || strcmp(name, PEM_STRING_X509_OLD) == 0) {
+           X509   *cert = d2i_X509(0, &p, len);
+
+           if (cert && (p - data) == len) {
+               selector = DNS_TLSA_SELECTOR_FULL_CERTIFICATE;
+               digest = tls_data_fprint((char *) data, len, sha256);
+               dane_add(dane, usage, selector, sha256, digest);
+               myfree(digest);
+               ta_cert_insert(dane, cert);
+           } else
+               errtype = "certificate";
+           if (cert)
+               X509_free(cert);
+       } else if (strcmp(name, PEM_STRING_PUBLIC) == 0) {
+           EVP_PKEY *pkey = d2i_PUBKEY(0, &p, len);
+
+           if (pkey && (p - data) == len) {
+               selector = DNS_TLSA_SELECTOR_SUBJECTPUBLICKEYINFO;
+               digest = tls_data_fprint((char *) data, len, sha256);
+               dane_add(dane, usage, selector, sha256, digest);
+               myfree(digest);
+               ta_pkey_insert(dane, pkey);
+           } else
+               errtype = "public key";
+           if (pkey)
+               EVP_PKEY_free(pkey);
+       }
+
+       /*
+        * If any of these were null, PEM_read() would have failed.
+        */
+       OPENSSL_free(name);
+       OPENSSL_free(header);
+       OPENSSL_free(data);
+    }
+    BIO_free(bp);
+
+    if (errtype) {
+       tls_print_errors();
+       msg_warn("error reading: %s: malformed trust-anchor %s",
+                tafile, errtype);
+       return (0);
+    }
+    if (ERR_GET_REASON(ERR_peek_last_error()) == PEM_R_NO_START_LINE) {
+       /* Reached end of PEM file */
+       ERR_clear_error();
+       return (tacount > 0);
+    }
+    /* Some other PEM read error */
+    tls_print_errors();
+    return (0);
+}
+
+#endif
index 182b71b6e313349120197e8cc674688fb3855b48..8f731e6eade40e64f80fdf8eec5ba6ff9c5c7aca 100644 (file)
 /*     const unsigned char *md_buf;
 /*     const char *md_len;
 /*
-/*     char    *tls_fprint(buf, len, mdalg)
+/*     char    *tls_data_fprint(buf, len, mdalg)
 /*     const char *buf;
 /*     int     len;
 /*     const char *mdalg;
 /*
-/*     char    *tls_fingerprint(peercert, mdalg)
+/*     char    *tls_cert_fprint(peercert, mdalg)
 /*     X509    *peercert;
 /*     const char *mdalg;
 /*
 /*     The return value is dynamically allocated with mymalloc(),
 /*     and the caller must eventually free it with myfree().
 /*
-/*     tls_fprint() digests unstructured data, and encodes the digested
-/*     result via tls_digest_encode().
-/*     The return value is dynamically allocated with mymalloc(),
-/*     and the caller must eventually free it with myfree().
+/*     tls_data_fprint() digests unstructured data, and encodes the digested
+/*     result via tls_digest_encode().  The return value is dynamically
+/*     allocated with mymalloc(), and the caller must eventually free it
+/*     with myfree().
 /*
-/*     tls_fingerprint() returns a fingerprint of the the given
+/*     tls_cert_fprint() returns a fingerprint of the the given
 /*     certificate using the requested message digest, formatted
 /*     with tls_digest_encode(). Panics if the
 /*     (previously verified) digest algorithm is not found. The return
@@ -46,7 +46,7 @@
 /*     must eventually free it with myfree().
 /*
 /*     tls_pkey_fprint() returns a public-key fingerprint; in all
-/*     other respects the function behaves as tls_fingerprint().
+/*     other respects the function behaves as tls_cert_fprint().
 /*     The var_tls_bc_pkey_fprint variable enables an incorrect
 /*     algorithm that was used in Postfix versions 2.9.[0-5].
 /*     The return value is dynamically allocated with mymalloc(),
@@ -76,8 +76,9 @@
 /* .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
 
 static const char hexcodes[] = "0123456789ABCDEF";
 
-#define chknonzero(ret)        (ok &= ((ret) ? 1 : 0))
-#define digestpl(p, l) chknonzero(EVP_DigestUpdate(mdctx, (char *)(p), (l)))
-#define digestptr(p) digestpl((p), sizeof(*(p)))
-#define digeststr(s) digestpl((s), strlen(s)+1)
+#define checkok(ret)   (ok &= ((ret) ? 1 : 0))
+#define digest_data(p, l) checkok(EVP_DigestUpdate(mdctx, (char *)(p), (l)))
+#define digest_object(p) digest_data((p), sizeof(*(p)))
+#define digest_string(s) digest_data((s), strlen(s)+1)
+
+#define digest_dane(dane, memb) do { \
+       if ((dane)->memb != 0) \
+           checkok(digest_tlsa_usage(mdctx, (dane)->memb, #memb)); \
+    } while (0)
+
+#define digest_tlsa_argv(tlsa, memb) do { \
+       if ((tlsa)->memb) { \
+           digest_string(#memb); \
+           for (dgst = (tlsa)->memb->argv; *dgst; ++dgst) \
+               digest_string(*dgst); \
+       } \
+    } while (0)
+
+/* digest_tlsa_usage - digest TA or EE match list sorted by alg and value */
+
+static int digest_tlsa_usage(EVP_MD_CTX * mdctx, TLS_TLSA *tlsa,
+                                    const char *usage)
+{
+    char  **dgst;
+    int     ok = 1;
+
+    for (digest_string(usage); tlsa; tlsa = tlsa->next) {
+       digest_string(tlsa->mdalg);
+       digest_tlsa_argv(tlsa, pkeys);
+       digest_tlsa_argv(tlsa, certs);
+    }
+    return (ok);
+}
 
 /* tls_serverid_digest - suffix props->serverid with parameter digest */
 
@@ -161,12 +191,50 @@ char   *tls_serverid_digest(const TLS_CLIENT_START_PROPS *props, long protomask,
     sslversion = SSLeay();
 
     mdctx = EVP_MD_CTX_create();
-    chknonzero(EVP_DigestInit_ex(mdctx, md, NULL));
-    digeststr(props->helo ? props->helo : "");
-    digestptr(&sslversion);
-    digestptr(&protomask);
-    digeststr(ciphers);
-    chknonzero(EVP_DigestFinal_ex(mdctx, md_buf, &md_len));
+    checkok(EVP_DigestInit_ex(mdctx, md, NULL));
+    digest_string(props->helo ? props->helo : "");
+    digest_object(&sslversion);
+    digest_object(&protomask);
+    digest_string(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 the security level is "dane", we send SNI information to the peer.
+     * This may cause it to respond with a non-default certificate.  Since
+     * certificates for sessions with no or different SNI data may not match,
+     * we must include the SNI name in the session id.
+     */
+    if (props->dane) {
+       int     mixed = (props->dane->flags & TLS_DANE_FLAG_MIXED);
+
+       digest_object(&mixed);
+       digest_dane(props->dane, ta);
+#if 0
+       digest_dane(props->dane, ee);           /* See above */
+#endif
+       digest_string(props->tls_level == TLS_LEV_DANE ? props->host : "");
+    }
+    checkok(EVP_DigestFinal_ex(mdctx, md_buf, &md_len));
     EVP_MD_CTX_destroy(mdctx);
     if (!ok)
        msg_fatal("error computing %s message digest", mdalg);
@@ -175,7 +243,17 @@ char   *tls_serverid_digest(const TLS_CLIENT_START_PROPS *props, long protomask,
     if (md_len > EVP_MAX_MD_SIZE)
        msg_panic("unexpectedly large %s digest size: %u", mdalg, md_len);
 
-    /* Append the digest to the serverid */
+    /*
+     * Append the digest to the serverid.  We don't compare this digest to
+     * any user-specified fingerprints.  Therefore, we don't need to use a
+     * colon-separated format, which saves space in the TLS session cache and
+     * makes logging of session cache lookup keys more readable.
+     * 
+     * This does however duplicate a few lines of code from the digest encoder
+     * for colon-separated cert and pkey fingerprints. If that is a
+     * compelling reason to consolidate, we could use that and append the
+     * result.
+     */
     result = vstring_alloc(strlen(props->serverid) + 1 + 2 * md_len);
     vstring_strcpy(result, props->serverid);
     VSTRING_ADDCH(result, ':');
@@ -207,9 +285,9 @@ char   *tls_digest_encode(const unsigned char *md_buf, int md_len)
     return (result);
 }
 
-/* tls_fprint - compute and encode digest of DER-encoded object */
+/* tls_data_fprint - compute and encode digest of binary object */
 
-char   *tls_fprint(const char *buf, int len, const char *mdalg)
+char   *tls_data_fprint(const char *buf, int len, const char *mdalg)
 {
     EVP_MD_CTX *mdctx;
     const EVP_MD *md;
@@ -222,9 +300,9 @@ char   *tls_fprint(const char *buf, int len, const char *mdalg)
        msg_panic("digest algorithm \"%s\" not found", mdalg);
 
     mdctx = EVP_MD_CTX_create();
-    chknonzero(EVP_DigestInit_ex(mdctx, md, NULL));
-    digestpl(buf, len);
-    chknonzero(EVP_DigestFinal_ex(mdctx, md_buf, &md_len));
+    checkok(EVP_DigestInit_ex(mdctx, md, NULL));
+    digest_data(buf, len);
+    checkok(EVP_DigestFinal_ex(mdctx, md_buf, &md_len));
     EVP_MD_CTX_destroy(mdctx);
     if (!ok)
        msg_fatal("error computing %s message digest", mdalg);
@@ -232,9 +310,9 @@ char   *tls_fprint(const char *buf, int len, const char *mdalg)
     return (tls_digest_encode(md_buf, md_len));
 }
 
-/* tls_fingerprint - extract certificate fingerprint */
+/* tls_cert_fprint - extract certificate fingerprint */
 
-char   *tls_fingerprint(X509 *peercert, const char *mdalg)
+char   *tls_cert_fprint(X509 *peercert, const char *mdalg)
 {
     int     len;
     char   *buf;
@@ -247,7 +325,7 @@ char   *tls_fingerprint(X509 *peercert, const char *mdalg)
     if (buf2 - buf != len)
        msg_panic("i2d_X509 invalid result length");
 
-    result = tls_fprint(buf, len, mdalg);
+    result = tls_data_fprint(buf, len, mdalg);
     myfree(buf);
 
     return (result);
@@ -267,7 +345,7 @@ char   *tls_pkey_fprint(X509 *peercert, const char *mdalg)
            msg_fatal("%s: error extracting legacy public-key fingerprint: %m",
                      myname);
 
-       result = tls_fprint((char *) key->data, key->length, mdalg);
+       result = tls_data_fprint((char *) key->data, key->length, mdalg);
        return (result);
     } else {
        int     len;
@@ -281,7 +359,7 @@ char   *tls_pkey_fprint(X509 *peercert, const char *mdalg)
        if (buf2 - buf != len)
            msg_panic("i2d_X509_PUBKEY invalid result length");
 
-       result = tls_fprint(buf, len, mdalg);
+       result = tls_data_fprint(buf, len, mdalg);
        myfree(buf);
        return (result);
     }
index d87ec0cd9b8cf3fd849dddb6c9217c331c9c9056..bf2fb8558ec92f824d25b0e04962fafa85f8f9cf 100644 (file)
 /* Application-specific. */
 
  /*
-  * Order is critical:
+  * Numerical order of levels is critical (see tls.h):
   * 
-  * Levels > "encrypt" are expected to match a peer certificate.
+  * - With "may" and higher, TLS is enabled.
   * 
-  * Levels >= "verify" are expected to require a valid CA trust-chain
+  * - With "encrypt" and higher, TLS is required.
   * 
-  * This forces "fingerprint" between "encrypt" and "verify".
+  * - With "fingerprint" and higher, the peer certificate must match.
+  * 
+  * - With "dane" and higher, the peer certificate must also be trusted,
+  * possibly via TLSA RRs that make it its own authority.
+  * 
+  * The smtp(8) client will report trust failure in preference to reporting
+  * failure to match, so we make "dane" larger than "fingerprint".
   */
 const NAME_CODE tls_level_table[] = {
     "none", TLS_LEV_NONE,
     "may", TLS_LEV_MAY,
     "encrypt", TLS_LEV_ENCRYPT,
-#if 0                                  /* Not yet */
-    "dane", TLS_LEV_DANE,
-#endif
     "fingerprint", TLS_LEV_FPRINT,
+    "dane", TLS_LEV_DANE,
+    "dane-only", TLS_LEV_DANE_ONLY,
     "verify", TLS_LEV_VERIFY,
     "secure", TLS_LEV_SECURE,
     0, TLS_LEV_INVALID,
index 8143da29607bafe474861414b3d778214aee0b62..fdbac74b5576a7dc0d23e328e39f141f6048dc92 100644 (file)
 /*     char    *var_tls_null_clist;
 /*     char    *var_tls_eecdh_strong;
 /*     char    *var_tls_eecdh_ultra;
+/*     char    *var_tls_dane_ta_dgst;
 /*     int     var_tls_daemon_rand_bytes;
-/*     bool    var_tls_append_def_CA;
+/*     bool    var_tls_append_def_CA;
 /*     bool    var_tls_preempt_clist;
 /*     bool    var_tls_bc_pkey_fprint;
+/*     bool    var_tls_multi_wildcard;
 /*
 /*     TLS_APPL_STATE *tls_alloc_app_context(ssl_ctx, log_mask)
 /*     SSL_CTX *ssl_ctx;
 /*     const char *log_param;
 /*     const char *log_level;
 /*
+/*     void     tls_update_app_logmask(app_ctx, log_mask)
+/*     TLS_APPL_STATE *app_ctx;
+/*     int      log_mask;
+/*
 /*     int     tls_validate_digest(dgst)
 /*     const char *dgst;
 /* DESCRIPTION
 /*     to mask.  The main.cf parameter name is passed along for
 /*     diagnostics.
 /*
+/*     tls_update_app_logmask() changes the log mask of the
+/*     application TLS context to the new setting.
+/*
 /*     tls_validate_digest() returns non-zero if the named digest
 /*     is usable and zero otherwise.
 /* LICENSE
@@ -210,9 +219,12 @@ char   *var_tls_null_clist;
 int     var_tls_daemon_rand_bytes;
 char   *var_tls_eecdh_strong;
 char   *var_tls_eecdh_ultra;
+char   *var_tls_dane_ta_dgst;
 bool    var_tls_append_def_CA;
 char   *var_tls_bug_tweaks;
+char   *var_tls_ssl_options;
 bool    var_tls_bc_pkey_fprint;
+bool    var_tls_multi_wildcard;
 
 #ifdef VAR_TLS_PREEMPT_CLIST
 bool    var_tls_preempt_clist;
@@ -303,6 +315,28 @@ static const LONG_NAME_MASK ssl_bug_tweaks[] = {
     0, 0,
 };
 
+ /*
+  * SSL_OP_MUMBLE option name <=> mask conversion for options that are not
+  * (or may in the future not be) in SSL_OP_ALL.  These enable optional
+  * behavior, rather than bug interoperability work-arounds.
+  */
+#define NAME_SSL_OP(x) #x, SSL_OP_##x
+static const LONG_NAME_MASK ssl_op_tweaks[] = {
+
+#if defined(SSL_OP_LEGACY_SERVER_CONNECT)
+    NAME_SSL_OP(LEGACY_SERVER_CONNECT),
+#endif
+
+#if defined(SSL_OP_NO_TICKET)
+    NAME_SSL_OP(NO_TICKET),
+#endif
+
+#if defined(SSL_OP_NO_COMPRESSION)
+    NAME_SSL_OP(NO_COMPRESSION),
+#endif
+    0, 0,
+};
+
  /*
   * Ciphersuite name <=> code conversion.
   */
@@ -391,6 +425,13 @@ int     tls_log_mask(const char *log_param, const char *log_level)
     return (mask);
 }
 
+/* tls_update_app_logmask - update log level after init */
+
+void    tls_update_app_logmask(TLS_APPL_STATE *app_ctx, int log_mask)
+{
+    app_ctx->log_mask = log_mask;
+}
+
 /* tls_exclude_missing - Append exclusions for missing ciphers */
 
 static const char *tls_exclude_missing(SSL_CTX *ctx, VSTRING *buf)
@@ -551,6 +592,8 @@ void    tls_param_init(void)
        VAR_TLS_EECDH_STRONG, DEF_TLS_EECDH_STRONG, &var_tls_eecdh_strong, 1, 0,
        VAR_TLS_EECDH_ULTRA, DEF_TLS_EECDH_ULTRA, &var_tls_eecdh_ultra, 1, 0,
        VAR_TLS_BUG_TWEAKS, DEF_TLS_BUG_TWEAKS, &var_tls_bug_tweaks, 0, 0,
+       VAR_TLS_SSL_OPTIONS, DEF_TLS_SSL_OPTIONS, &var_tls_ssl_options, 0, 0,
+       VAR_TLS_DANE_TA_DGST, DEF_TLS_DANE_TA_DGST, &var_tls_dane_ta_dgst, 0, 0,
        0,
     };
     static const CONFIG_INT_TABLE int_table[] = {
@@ -560,9 +603,8 @@ void    tls_param_init(void)
     static const CONFIG_BOOL_TABLE bool_table[] = {
        VAR_TLS_APPEND_DEF_CA, DEF_TLS_APPEND_DEF_CA, &var_tls_append_def_CA,
        VAR_TLS_BC_PKEY_FPRINT, DEF_TLS_BC_PKEY_FPRINT, &var_tls_bc_pkey_fprint,
-#if OPENSSL_VERSION_NUMBER >= 0x0090700fL      /* OpenSSL 0.9.7 and later */
        VAR_TLS_PREEMPT_CLIST, DEF_TLS_PREEMPT_CLIST, &var_tls_preempt_clist,
-#endif
+       VAR_TLS_MULTI_WILDCARD, DEF_TLS_MULTI_WILDCARD, &var_tls_multi_wildcard,
        0,
     };
     static int init_done;
@@ -740,13 +782,17 @@ TLS_SESS_STATE *tls_alloc_sess_context(int log_mask, const char *namaddr)
     TLScontext->serverid = 0;
     TLScontext->peer_CN = 0;
     TLScontext->issuer_CN = 0;
-    TLScontext->peer_fingerprint = 0;
+    TLScontext->peer_cert_fprint = 0;
     TLScontext->peer_pkey_fprint = 0;
     TLScontext->protocol = 0;
     TLScontext->cipher_name = 0;
     TLScontext->log_mask = log_mask;
     TLScontext->namaddr = lowercase(mystrdup(namaddr));
     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;
@@ -776,8 +822,8 @@ void    tls_free_context(TLS_SESS_STATE *TLScontext)
        myfree(TLScontext->peer_CN);
     if (TLScontext->issuer_CN)
        myfree(TLScontext->issuer_CN);
-    if (TLScontext->peer_fingerprint)
-       myfree(TLScontext->peer_fingerprint);
+    if (TLScontext->peer_cert_fprint)
+       myfree(TLScontext->peer_cert_fprint);
     if (TLScontext->peer_pkey_fprint)
        myfree(TLScontext->peer_pkey_fprint);
     if (TLScontext->errorcert)
@@ -905,6 +951,20 @@ long    tls_bug_bits(void)
                                    var_tls_bug_tweaks, NAME_MASK_ANY_CASE |
                                    NAME_MASK_NUMBER | NAME_MASK_WARN);
     }
+
+    /*
+     * Allow users to set options not in SSL_OP_ALL, and not already managed
+     * via other Postfix parameters.
+     */
+    if (*var_tls_ssl_options) {
+       long    enable;
+
+       enable = long_name_mask_opt(VAR_TLS_SSL_OPTIONS, ssl_op_tweaks,
+                                   var_tls_ssl_options, NAME_MASK_ANY_CASE |
+                                   NAME_MASK_NUMBER | NAME_MASK_WARN);
+       enable &= ~(SSL_OP_ALL | TLS_SSL_OP_MANAGED_BITS);
+       bits |= enable;
+    }
     return (bits);
 }
 
index cf7c32f3e037c28a95dccabca5f22206ebbcda60..2cacf44412e4b845cb92dab93bbf8aa068f94d0f 100644 (file)
@@ -233,8 +233,8 @@ void    tls_proxy_context_free(TLS_SESS_STATE *tls_context)
        myfree(tls_context->peer_CN);
     if (tls_context->issuer_CN)
        myfree(tls_context->issuer_CN);
-    if (tls_context->peer_fingerprint)
-       myfree(tls_context->peer_fingerprint);
+    if (tls_context->peer_cert_fprint)
+       myfree(tls_context->peer_cert_fprint);
     if (tls_context->protocol)
        myfree((char *) tls_context->protocol);
     if (tls_context->cipher_name)
index e919a9ee85468e565c4568a4167bc42379eadb25..a850cb1d429d54c1a422bcfd3fed7572da5255fd 100644 (file)
@@ -65,8 +65,8 @@ int     tls_proxy_context_print(ATTR_PRINT_MASTER_FN print_fn, VSTREAM *fp,
                   STRING_OR_EMPTY(tp->peer_CN),
                   ATTR_TYPE_STR, MAIL_ATTR_ISSUER_CN,
                   STRING_OR_EMPTY(tp->issuer_CN),
-                  ATTR_TYPE_STR, MAIL_ATTR_PEER_FPT,
-                  STRING_OR_EMPTY(tp->peer_fingerprint),
+                  ATTR_TYPE_STR, MAIL_ATTR_PEER_CERT_FPT,
+                  STRING_OR_EMPTY(tp->peer_cert_fprint),
                   ATTR_TYPE_STR, MAIL_ATTR_PEER_PKEY_FPT,
                   STRING_OR_EMPTY(tp->peer_pkey_fprint),
                   ATTR_TYPE_INT, MAIL_ATTR_PEER_STATUS,
index 33db215dd0c530585d35e64f2333c7bf9fa959bd..428f24a57b1bae2e90aac497d00894bd0b64f9bf 100644 (file)
@@ -59,7 +59,7 @@ int     tls_proxy_context_scan(ATTR_SCAN_MASTER_FN scan_fn, VSTREAM *fp,
     int     ret;
     VSTRING *peer_CN = vstring_alloc(25);
     VSTRING *issuer_CN = vstring_alloc(25);
-    VSTRING *peer_fingerprint = vstring_alloc(60);     /* 60 for SHA-1 */
+    VSTRING *peer_cert_fprint = vstring_alloc(60);     /* 60 for SHA-1 */
     VSTRING *peer_pkey_fprint = vstring_alloc(60);     /* 60 for SHA-1 */
     VSTRING *protocol = vstring_alloc(25);
     VSTRING *cipher_name = vstring_alloc(25);
@@ -71,7 +71,7 @@ int     tls_proxy_context_scan(ATTR_SCAN_MASTER_FN scan_fn, VSTREAM *fp,
     ret = scan_fn(fp, flags | ATTR_FLAG_MORE,
                  ATTR_TYPE_STR, MAIL_ATTR_PEER_CN, peer_CN,
                  ATTR_TYPE_STR, MAIL_ATTR_ISSUER_CN, issuer_CN,
-                 ATTR_TYPE_STR, MAIL_ATTR_PEER_FPT, peer_fingerprint,
+                 ATTR_TYPE_STR, MAIL_ATTR_PEER_CERT_FPT, peer_cert_fprint,
                  ATTR_TYPE_STR, MAIL_ATTR_PEER_PKEY_FPT, peer_pkey_fprint,
                  ATTR_TYPE_INT, MAIL_ATTR_PEER_STATUS,
                  &tls_context->peer_status,
@@ -84,7 +84,7 @@ int     tls_proxy_context_scan(ATTR_SCAN_MASTER_FN scan_fn, VSTREAM *fp,
                  ATTR_TYPE_END);
     tls_context->peer_CN = vstring_export(peer_CN);
     tls_context->issuer_CN = vstring_export(issuer_CN);
-    tls_context->peer_fingerprint = vstring_export(peer_fingerprint);
+    tls_context->peer_cert_fprint = vstring_export(peer_cert_fprint);
     tls_context->peer_pkey_fprint = vstring_export(peer_pkey_fprint);
     tls_context->protocol = vstring_export(protocol);
     tls_context->cipher_name = vstring_export(cipher_name);
index c1bf8174041c2ca51cfd42952942ca68d4275e72..37cfb8975b490e4a32c90861a61baabf5dba8e01 100644 (file)
@@ -70,7 +70,7 @@
 /* .IP TLScontext->issuer_CN
 /*     Extracted CommonName of the issuer, or zero-length string
 /*     when information could not be extracted.
-/* .IP TLScontext->peer_fingerprint
+/* .IP TLScontext->peer_cert_fprint
 /*     Fingerprint of the certificate, or zero-length string when no peer
 /*     certificate is available.
 /* .PP
@@ -397,8 +397,6 @@ TLS_APPL_STATE *tls_server_init(const TLS_SERVER_INIT_PROPS *props)
                 | ((protomask & TLS_PROTOCOL_SSLv3) ? SSL_OP_NO_SSLv3 : 0L)
               | ((protomask & TLS_PROTOCOL_SSLv2) ? SSL_OP_NO_SSLv2 : 0L));
 
-#if OPENSSL_VERSION_NUMBER >= 0x0090700fL
-
     /*
      * Some sites may want to give the client less rope. On the other hand,
      * this could trigger inter-operability issues, the client should not
@@ -410,7 +408,6 @@ TLS_APPL_STATE *tls_server_init(const TLS_SERVER_INIT_PROPS *props)
      */
     if (var_tls_preempt_clist)
        SSL_CTX_set_options(server_ctx, SSL_OP_CIPHER_SERVER_PREFERENCE);
-#endif
 
     /*
      * Set the call-back routine to debug handshake progress.
@@ -764,7 +761,7 @@ TLS_SESS_STATE *tls_server_post_accept(TLS_SESS_STATE *TLScontext)
        }
        TLScontext->peer_CN = tls_peer_CN(peer, TLScontext);
        TLScontext->issuer_CN = tls_issuer_CN(peer, TLScontext);
-       TLScontext->peer_fingerprint = tls_fingerprint(peer, TLScontext->mdalg);
+       TLScontext->peer_cert_fprint = tls_cert_fprint(peer, TLScontext->mdalg);
        TLScontext->peer_pkey_fprint = tls_pkey_fprint(peer, TLScontext->mdalg);
 
        if (TLScontext->log_mask & (TLS_LOG_VERBOSE | TLS_LOG_PEERCERT)) {
@@ -772,14 +769,14 @@ TLS_SESS_STATE *tls_server_post_accept(TLS_SESS_STATE *TLScontext)
                     ", pkey_fingerprint=%s",
                     TLScontext->namaddr,
                     TLScontext->peer_CN, TLScontext->issuer_CN,
-                    TLScontext->peer_fingerprint,
+                    TLScontext->peer_cert_fprint,
                     TLScontext->peer_pkey_fprint);
        }
        X509_free(peer);
     } else {
        TLScontext->peer_CN = mystrdup("");
        TLScontext->issuer_CN = mystrdup("");
-       TLScontext->peer_fingerprint = mystrdup("");
+       TLScontext->peer_cert_fprint = mystrdup("");
        TLScontext->peer_pkey_fprint = mystrdup("");
     }
 
index f3daf2a5c31a93da53465bdefe3e87e7dd6bcefa..b1fe441ce890e2e9ed3ecdf7e86e7d41ceec9d6d 100644 (file)
@@ -7,6 +7,12 @@
 /*     #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
@@ -126,7 +144,8 @@ static void update_error_state(TLS_SESS_STATE *TLScontext, int depth,
                                       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;
 
     /*
@@ -141,6 +160,171 @@ static void update_error_state(TLS_SESS_STATE *TLScontext, int depth,
        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_cert_fprint(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_PKEYS *k;
+       TLS_CERTS *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 = dane->certs; !matched && x; x = x->next) {
+           if (X509_check_issued(x->cert, cert) == X509_V_OK) {
+               EVP_PKEY *pk = X509_get_pubkey(x->cert);
+
+               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 = dane->pkeys; !matched && k; k = k->next)
+           matched = X509_verify(cert, k->pkey) > 0;
+
+       if (matched)
+           update_trust_state(TLScontext, depth);
+       ++TLScontext->chaindepth;
+    }
+    return (matched);
 }
 
 /* tls_verify_certificate_callback - verify peer certificate info */
@@ -193,16 +377,89 @@ int     tls_verify_certificate_callback(int ok, X509_STORE_CTX *ctx)
     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) {
index cafb1aceeae331b350ca404406f886a5f6c69088..c5dbe066333a4d49b41e7d720031837a2344ace4 100644 (file)
 /* .IP "\fBresolve_dequoted_address (yes)\fR"
 /*     Resolve a recipient address safely instead of correctly, by
 /*     looking inside quotes.
+/* .PP
+/*     Available with Postfix version 2.1 and later:
 /* .IP "\fBresolve_null_domain (no)\fR"
 /*     Resolve an address that ends in the "@" null domain as if the
 /*     local hostname were specified, instead of rejecting the address as
 /*     invalid.
+/* .PP
+/*     Available with Postfix version 2.3 and later:
 /* .IP "\fBresolve_numeric_domain (no)\fR"
 /*     Resolve "user@ipaddress" as "user@[ipaddress]", instead of
 /*     rejecting the address as invalid.
index 7be09a31141bd9cf7fa5045e32194a504c21a5c9..74323ffa5af38c0e03799f2b0648b5d082850c3d 100644 (file)
@@ -36,7 +36,7 @@ SRCS  = alldig.c allprint.c argv.c argv_split.c attr_clnt.c attr_print0.c \
        ip_match.c nbbio.c base32_code.c dict_test.c \
        dict_fail.c msg_rate_delay.c dict_surrogate.c warn_stat.c \
        dict_sockmap.c line_number.c recv_pass_attr.c pass_accept.c \
-       poll_fd.c
+       poll_fd.c timecmp.c
 OBJS   = alldig.o allprint.o argv.o argv_split.o attr_clnt.o attr_print0.o \
        attr_print64.o attr_print_plain.o attr_scan0.o attr_scan64.o \
        attr_scan_plain.o auto_clnt.o base64_code.o basename.o binhash.o \
@@ -74,7 +74,7 @@ OBJS  = alldig.o allprint.o argv.o argv_split.o attr_clnt.o attr_print0.o \
        ip_match.o nbbio.o base32_code.o dict_test.o \
        dict_fail.o msg_rate_delay.o dict_surrogate.o warn_stat.o \
        dict_sockmap.o line_number.o recv_pass_attr.o pass_accept.o \
-       poll_fd.o
+       poll_fd.o timecmp.o
 HDRS   = argv.h attr.h attr_clnt.h auto_clnt.h base64_code.h binhash.h \
        chroot_uid.h cidr_match.h clean_env.h connect.h ctable.h dict.h \
        dict_cdb.h dict_cidr.h dict_db.h dict_dbm.h dict_env.h dict_ht.h \
@@ -95,7 +95,7 @@ HDRS  = argv.h attr.h attr_clnt.h auto_clnt.h base64_code.h binhash.h \
        username.h valid_hostname.h vbuf.h vbuf_print.h vstream.h vstring.h \
        vstring_vstream.h watchdog.h format_tv.h load_file.h killme_after.h \
        edit_file.h dict_cache.h dict_thash.h ip_match.h nbbio.h base32_code.h \
-       dict_fail.h warn_stat.h dict_sockmap.h line_number.h
+       dict_fail.h warn_stat.h dict_sockmap.h line_number.h timecmp.h
 TESTSRC        = fifo_open.c fifo_rdwr_bug.c fifo_rdonly_bug.c select_bug.c \
        stream_test.c dup2_pass_on_exec.c
 DEFS   = -I. -D$(SYSTYPE)
@@ -454,12 +454,17 @@ vstream: $(LIB)
        $(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIB) $(SYSLIBS)
        mv junk $@.o
 
+timecmp: $(LIB)
+       mv $@.o junk
+       $(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIB) $(SYSLIBS)
+       mv junk $@.o
+
 tests: valid_hostname_test mac_expand_test dict_test unescape_test \
        hex_quote_test ctable_test inet_addr_list_test base64_code_test \
        attr_scan64_test attr_scan0_test dict_pcre_test host_port_test \
        dict_cidr_test attr_scan_plain_test htable_test hex_code_test \
        myaddrinfo_test format_tv_test ip_match_test name_mask_tests \
-       base32_code_test dict_thash_test surrogate_test
+       base32_code_test dict_thash_test surrogate_test timecmp_test
 
 root_tests:
 
@@ -561,6 +566,9 @@ htable_test: htable /usr/share/dict/words
 hex_code_test: hex_code
        ./hex_code
 
+timecmp_test: timecmp
+       ./timecmp
+
 myaddrinfo_test: myaddrinfo myaddrinfo.ref myaddrinfo.ref2
        ./myaddrinfo all belly.porcupine.org 168.100.189.2 >myaddrinfo.tmp 2>&1
        diff myaddrinfo.ref myaddrinfo.tmp
index 7a0ce1f152b98df4dadb5fbaf5b8698facc0950f..5b2eec3eb6934bca50479066c486d145d673ca6b 100644 (file)
@@ -9,6 +9,9 @@
 /*     ARGV    *argv_alloc(len)
 /*     ssize_t len;
 /*
+/*     ARGV    *argv_sort(argvp)
+/*     ARGV    *argvp;
+/*
 /*     ARGV    *argv_free(argvp)
 /*     ARGV    *argvp;
 /*
@@ -56,6 +59,9 @@
 /*     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
@@ -148,6 +154,22 @@ ARGV   *argv_alloc(ssize_t len)
     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)
index 7c56f581a04e6e787b37d2274e6231fe91ba40bb..fe0322422b6d34510b93ed419474d4d8e2387263 100644 (file)
@@ -21,6 +21,7 @@ typedef struct ARGV {
 } 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 *);
index fa9355528fbfcf8de29c136ec69f43635ba79b43..40544cdbac43622849ba6898207c1f1b23f5e38c 100644 (file)
 /*     VSTRING *result;
 /*     const char *in;
 /*     ssize_t len;
+/*
+/*     VSTRING *base64_encode_opt(result, in, len, flags)
+/*     VSTRING *result;
+/*     const char *in;
+/*     ssize_t len;
+/*     int     flags;
+/*
+/*     VSTRING *base64_decode_opt(result, in, len, flags)
+/*     VSTRING *result;
+/*     const char *in;
+/*     ssize_t len;
+/*     int     flags;
 /* DESCRIPTION
 /*     base64_encode() takes a block of len bytes and encodes it as one
 /*     null-terminated string.  The result value is the result argument.
 /*     base64_decode() performs the opposite transformation. The result
 /*     value is the result argument. The result is null terminated, whether
 /*     or not that makes sense.
+/*
+/*     base64_encode_opt() and base64_decode_opt() provide extended
+/*     interfaces.  In both cases the flags arguments is the bit-wise
+/*     OR of zero or more the following:
+/* .IP BASE64_FLAG_APPEND
+/*     Append the result, instead of overwriting the result buffer.
+/* .PP
+/*     For convenience, BASE64_FLAG_NONE specifies none of the above.
 /* DIAGNOSTICS
 /*     base64_decode () returns a null pointer when the input contains
 /*     characters not in the base 64 alphabet.
@@ -63,7 +83,17 @@ static unsigned char to_b64[] =
 
 /* base64_encode - raw data to encoded */
 
+#undef base64_encode
+
+extern VSTRING *base64_encode(VSTRING *, const char *, ssize_t);
+
 VSTRING *base64_encode(VSTRING *result, const char *in, ssize_t len)
+{
+    return (base64_encode_opt(result, in, len, BASE64_FLAG_NONE));
+}
+
+VSTRING *base64_encode_opt(VSTRING *result, const char *in, ssize_t len,
+                                  int flags)
 {
     const unsigned char *cp;
     ssize_t count;
@@ -71,7 +101,8 @@ VSTRING *base64_encode(VSTRING *result, const char *in, ssize_t len)
     /*
      * Encode 3 -> 4.
      */
-    VSTRING_RESET(result);
+    if ((flags & BASE64_FLAG_APPEND) == 0)
+       VSTRING_RESET(result);
     for (cp = UNSIG_CHAR_PTR(in), count = len; count > 0; count -= 3, cp += 3) {
        VSTRING_ADDCH(result, to_b64[cp[0] >> 2]);
        if (count > 1) {
@@ -97,7 +128,17 @@ VSTRING *base64_encode(VSTRING *result, const char *in, ssize_t len)
 
 /* base64_decode - encoded data to raw */
 
+#undef base64_decode
+
+extern VSTRING *base64_decode(VSTRING *, const char *, ssize_t);
+
 VSTRING *base64_decode(VSTRING *result, const char *in, ssize_t len)
+{
+    return (base64_decode_opt(result, in, len, BASE64_FLAG_NONE));
+}
+
+VSTRING *base64_decode_opt(VSTRING *result, const char *in, ssize_t len,
+                                  int flags)
 {
     static unsigned char *un_b64 = 0;
     const unsigned char *cp;
@@ -129,7 +170,8 @@ VSTRING *base64_decode(VSTRING *result, const char *in, ssize_t len)
     /*
      * Decode 4 -> 3.
      */
-    VSTRING_RESET(result);
+    if ((flags & BASE64_FLAG_APPEND) == 0)
+       VSTRING_RESET(result);
     for (cp = UNSIG_CHAR_PTR(in), count = 0; count < len; count += 4) {
        if ((ch0 = un_b64[*cp++]) == INVALID
            || (ch1 = un_b64[*cp++]) == INVALID)
@@ -159,11 +201,14 @@ VSTRING *base64_decode(VSTRING *result, const char *in, ssize_t len)
 #define STR(x) vstring_str(x)
 #define LEN(x) VSTRING_LEN(x)
 
+#define TESXT  "this is a test!"
+
 int     main(int unused_argc, char **unused_argv)
 {
     VSTRING *b1 = vstring_alloc(1);
     VSTRING *b2 = vstring_alloc(1);
-    char   *test = "this is a test";
+    char   *test = TESXT;
+    char   *test2 = TESXT TESXT;
 
 #define DECODE(b,x,l) { \
        if (base64_decode((b),(x),(l)) == 0) \
@@ -198,6 +243,11 @@ int     main(int unused_argc, char **unused_argv)
     DECODE(b2, STR(b1), LEN(b1));
     VERIFY(STR(b2), test);
 
+    base64_encode(b1, test, strlen(test));
+    base64_encode_opt(b1, test, strlen(test), BASE64_FLAG_APPEND);
+    DECODE(b2, STR(b1), LEN(b1));
+    VERIFY(STR(b2), test2);
+
     vstring_free(b1);
     vstring_free(b2);
     return (0);
index e2f938ac4bccd45b5606361309b8ee72f7bc76a0..006ff312678cb7b6d02ddeabc108330b7f4e108e 100644 (file)
  /*
   * External interface.
   */
-extern VSTRING *base64_encode(VSTRING *, const char *, ssize_t);
-extern VSTRING *base64_decode(VSTRING *, const char *, ssize_t);
+extern VSTRING *base64_encode_opt(VSTRING *, const char *, ssize_t, int);
+extern VSTRING *base64_decode_opt(VSTRING *, const char *, ssize_t, int);
+
+#define BASE64_FLAG_NONE       0
+#define BASE64_FLAG_APPEND     (1<<0)
+
+#define base64_encode(bp, cp, ln) \
+       base64_encode_opt((bp), (cp), (ln), BASE64_FLAG_NONE)
+#define base64_decode(bp, cp, ln) \
+       base64_decode_opt((bp), (cp), (ln), BASE64_FLAG_NONE)
 
 /* LICENSE
 /* .ad
index cd323921c00bfe0e3e492633ea13ba9f39712f36..8fe621c65197b9b96eb29cd3469b0c86a56bef96 100644 (file)
 /*     CTABLE  *cache;
 /*     const char *key;
 /*
+/*     const void *ctable_refresh(cache, key)
+/*     CTABLE  *cache;
+/*     const char *key;
+/*
+/*     const void *ctable_newcontext(cache, context)
+/*     CTABLE  *cache;
+/*     void    *context;
+/*
 /*     void    ctable_free(cache)
 /*     CTABLE  *cache;
 /*
 /*     specify pointers to call-back functions that create a value, given
 /*     a key, and delete a given value, respectively. The context argument
 /*     is passed on to the call-back routines.
+/*     The create() and delete() functions must not modify the cache.
 /*
 /*     ctable_locate() looks up or generates the value that corresponds to
 /*     the specified key, and returns that value.
 /*
+/*     ctable_refresh() flushes the value (if any) associated with
+/*     the specified key, and returns the same result as ctable_locate().
+/*
+/*     ctable_newcontext() updates the context that is passed on
+/*     to call-back routines.
+/*
 /*     ctable_free() destroys the specified cache, including its contents.
 /*
 /*     ctable_walk() iterates over all elements in the cache, and invokes
 /*     the action function for each cache element with the corresponding
 /*     key and value as arguments. This function is useful mainly for
 /*     cache performance debugging.
+/*     Note: the action() function must not modify the cache.
 /* DIAGNOSTICS
 /*     Fatal errors: out of memory. Panic: interface violation.
 /* LICENSE
@@ -161,6 +177,38 @@ const void *ctable_locate(CTABLE *cache, const char *key)
     return (entry->value);
 }
 
+/* ctable_refresh - page-in fresh data for given key */
+
+const void *ctable_refresh(CTABLE *cache, const char *key)
+{
+    const char *myname = "ctable_refresh";
+    CTABLE_ENTRY *entry;
+
+    /* Materialize entry if missing. */
+    if ((entry = (CTABLE_ENTRY *) htable_find(cache->table, key)) == 0)
+       return ctable_locate(cache, key);
+
+    /* Otherwise, refresh its content. */
+    cache->delete(entry->value, cache->context);
+    entry->value = cache->create(key, cache->context);
+
+    /* Update its MRU linkage. */
+    if (entry != RING_TO_CTABLE_ENTRY(ring_succ(RING_PTR_OF(cache)))) {
+       ring_detach(RING_PTR_OF(entry));
+       ring_append(RING_PTR_OF(cache), RING_PTR_OF(entry));
+    }
+    if (msg_verbose)
+       msg_info("%s: refresh entry key %s", myname, entry->key);
+    return (entry->value);
+}
+
+/* ctable_newcontext - update call-back context */
+
+void    ctable_newcontext(CTABLE *cache, void *context)
+{
+    cache->context = context;
+}
+
 static CTABLE *ctable_free_cache;
 
 /* ctable_free_callback - callback function */
index 7e3d899b3f0c369b949d036e2e881f34993f8092..19cbdaef962bace0a5312dae8408af8d6030179b 100644 (file)
@@ -24,6 +24,8 @@ extern CTABLE *ctable_create(int, CTABLE_CREATE_FN, CTABLE_DELETE_FN, void *);
 extern void ctable_free(CTABLE *);
 extern void ctable_walk(CTABLE *, void (*) (const char *, const void *));
 extern const void *ctable_locate(CTABLE *, const char *);
+extern const void *ctable_refresh(CTABLE *, const char *);
+extern void ctable_newcontext(CTABLE *, void *);
 
 /* LICENSE
 /* .ad
index 4210c0b64c8eccd6b8bf536587a53dc34d6068f1..3f625599f4059d93e49fd3b3fd86d818c212f30a 100644 (file)
@@ -88,7 +88,7 @@ static const char *dict_ni_do_lookup(char *path, char *key_prop,
        msg_warn("ni_open `.': %d", r);
        return NULL;
     }
-    query = alloca(strlen(path) + strlen(key_prop) + 3 + strlen(key_value));
+    query = mymalloc(strlen(path) + strlen(key_prop) + 3 + strlen(key_value));
     sprintf(query, "%s/%s=%s", path, key_prop, key_value);
 
     for (;;) {
@@ -138,6 +138,7 @@ static const char *dict_ni_do_lookup(char *path, char *key_prop,
     }
 
     ni_free(domain);
+    myfree(query);
 
     return return_val;
 }
diff --git a/postfix/src/util/timecmp.c b/postfix/src/util/timecmp.c
new file mode 100644 (file)
index 0000000..406e929
--- /dev/null
@@ -0,0 +1,93 @@
+/*++
+/* NAME
+/*     timecmp 3
+/* SUMMARY
+/*     compare two time_t values
+/* SYNOPSIS
+/*     #include <timecmp.h>
+/*
+/*     int     timecmp(t1, t2)
+/*     time_t  t1;
+/*     time_t  t2;
+/* DESCRIPTION
+/*     The timecmp() function return an integer greater than, equal to, or
+/*     less than 0, according as the time t1 is greater than, equal to, or
+/*     less than the time t2.  The comparison is made in a manner that is
+/*     insensitive to clock wrap-around, provided the underlying times are
+/*     within half of the time interval between the smallest and largest
+/*     representable time values.
+/* LICENSE
+/* .ad
+/* .fi
+/*     The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/*     Wietse Venema
+/*     IBM T.J. Watson Research
+/*     P.O. Box 704
+/*     Yorktown Heights, NY 10598, USA
+/*
+/*     Viktor Dukhovni
+/*--*/
+
+#include "timecmp.h"
+
+/* timecmp - wrap-safe time_t comparison */
+
+int     timecmp(time_t t1, time_t t2)
+{
+    time_t  delta = t1 - t2;
+
+    if (delta == 0)
+       return 0;
+
+#define UNSIGNED(type) ( ((type)-1) > ((type)0) )
+
+    /*
+     * With a constant switch value, the compiler will emit only the code for
+     * the correct case, so the signed/unsigned test happens at compile time.
+     */
+    switch (UNSIGNED(time_t) ? 0 : 1) {
+    case 0:
+       return ((2 * delta > delta) ? 1 : -1);
+    case 1:
+       return ((delta > (time_t) 0) ? 1 : -1);
+    }
+}
+
+#ifdef TEST
+#include <assert.h>
+
+ /*
+  * Bit banging!! There is no official constant that defines the INT_MAX
+  * equivalent of the off_t type. Wietse came up with the following macro
+  * that works as long as off_t is some two's complement number.
+  * 
+  * Note, however, that C99 permits signed integer representations other than
+  * two's complement.
+  */
+#include <limits.h>
+#define __MAXINT__(T) ((T) (((((T) 1) << ((sizeof(T) * CHAR_BIT) - 1)) ^ ((T) -1))))
+
+int     main()
+{
+    time_t  now = time((time_t *) 0);
+
+    /* Test that it works for normal times */
+    assert(timecmp(now + 10, now) > 0);
+    assert(timecmp(now, now) == 0);
+    assert(timecmp(now - 10, now) < 0);
+
+    /* Test that it works at a boundary time */
+    if (UNSIGNED(time_t))
+       now = (time_t) -1;
+    else
+       now = __MAXINT__(time_t);
+
+    assert(timecmp(now + 10, now) > 0);
+    assert(timecmp(now, now) == 0);
+    assert(timecmp(now - 10, now) < 0);
+
+    return (0);
+}
+
+#endif
diff --git a/postfix/src/util/timecmp.h b/postfix/src/util/timecmp.h
new file mode 100644 (file)
index 0000000..b6efeab
--- /dev/null
@@ -0,0 +1,37 @@
+#ifndef _TIMECMP_H_INCLUDED_
+#define _TIMECMP_H_INCLUDED_
+
+#include <time.h>
+
+/*++
+/* NAME
+/*     timecmp 3h
+/* SUMMARY
+/*     compare two time_t values
+/* SYNOPSIS
+/*     #include <timecmp.h>
+/*
+/*     int     timecmp(t1, t2)
+/*     time_t  t1;
+/*     time_t  t2;
+/* DESCRIPTION
+/* .nf
+
+ /* External interface. */
+
+extern int timecmp(time_t, time_t);
+
+/* LICENSE
+/* .ad
+/* .fi
+/*     The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/*     Wietse Venema
+/*     IBM T.J. Watson Research
+/*     P.O. Box 704
+/*     Yorktown Heights, NY 10598, USA
+/*
+/*     Viktor Dukhovni
+/*--*/
+
+#endif