-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
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:
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
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
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
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
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.
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
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
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
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.
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 \
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"
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
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:
/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
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
[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
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
specified, the certificate CommonName is checked. If you want mandatory
encryption without server certificate verification, see above.
+With Postfix >= 2.11 the "smtp_tls_trust_anchor_file" parameter or more
+typically the corresponding per-destination "tafile" attribute optionally
+modifies trust chain verification. If the parameter is not empty the root CAs
+in CAfile and CApath are no longer trusted. Rather, the Postfix SMTP client
+will only trust certificate-chains signed by one of the trust-anchors contained
+in the chosen files. The specified trust-anchor certificates and public keys
+are not subject to expiration, and need not be (self-signed) root CAs. They
+may, if desired, be intermediate certificates. Therefore, these certificates
+also may be found "in the middle" of the trust chain presented by the remote
+SMTP server, and any untrusted issuing parent certificates will be ignored.
+
Despite the potential for eliminating "man-in-the-middle" and other attacks,
mandatory certificate trust chain and subject name verification is not viable
as a default Internet mail delivery policy. Most MX hosts do not support TLS at
specified, the CommonName is checked. If you want mandatory encryption without
server certificate verification, see above.
+With Postfix >= 2.11 the "smtp_tls_trust_anchor_file" parameter or more
+typically the corresponding per-destination "tafile" attribute optionally
+modifies trust chain verification. If the parameter is not empty the root CAs
+in CAfile and CApath are no longer trusted. Rather, the Postfix SMTP client
+will only trust certificate-chains signed by one of the trust-anchors contained
+in the chosen files. The specified trust-anchor certificates and public keys
+are not subject to expiration, and need not be (self-signed) root CAs. They
+may, if desired, be intermediate certificates. Therefore, these certificates
+also may be found "in the middle" of the trust chain presented by the remote
+SMTP server, and any untrusted issuing parent certificates will be ignored.
+
Despite the potential for eliminating "man-in-the-middle" and other attacks,
mandatory secure server certificate verification is not viable as a default
Internet mail delivery policy. Most MX hosts do not support TLS at all, and a
(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
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
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
/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
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
====================================
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
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.
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>
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 \
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 >$@
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
certificates. </p>
<p> 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. </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>
</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 > 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>
<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 ≥ 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>
<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>
</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
<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)
If you want mandatory encryption without server certificate
verification, see <a href="#client_tls_encrypt">above</a>. </p>
+<p> With Postfix ≥ 2.11 the "<a href="postconf.5.html#smtp_tls_trust_anchor_file">smtp_tls_trust_anchor_file</a>" parameter
+or more typically the corresponding per-destination "tafile" attribute
+optionally modifies trust chain verification. If the parameter is
+not empty the root CAs in CAfile and CApath are no longer trusted.
+Rather, the Postfix SMTP client will only trust certificate-chains
+signed by one of the trust-anchors contained in the chosen files.
+The specified trust-anchor certificates and public keys are not
+subject to expiration, and need not be (self-signed) root CAs. They
+may, if desired, be intermediate certificates. Therefore, these
+certificates also may be found "in the middle" of the trust chain
+presented by the remote SMTP server, and any untrusted issuing
+parent certificates will be ignored. </p>
+
<p> Despite the potential for eliminating "man-in-the-middle" and other
attacks, mandatory certificate trust chain and subject name verification
is not viable as a default Internet mail delivery policy. Most MX hosts
without server certificate verification, see <a
href="#client_tls_encrypt">above</a>. </p>
+<p> With Postfix ≥ 2.11 the "<a href="postconf.5.html#smtp_tls_trust_anchor_file">smtp_tls_trust_anchor_file</a>" parameter
+or more typically the corresponding per-destination "tafile" attribute
+optionally modifies trust chain verification. If the parameter is
+not empty the root CAs in CAfile and CApath are no longer trusted.
+Rather, the Postfix SMTP client will only trust certificate-chains
+signed by one of the trust-anchors contained in the chosen files.
+The specified trust-anchor certificates and public keys are not
+subject to expiration, and need not be (self-signed) root CAs. They
+may, if desired, be intermediate certificates. Therefore, these
+certificates also may be found "in the middle" of the trust chain
+presented by the remote SMTP server, and any untrusted issuing
+parent certificates will be ignored. </p>
+
<p> Despite the potential for eliminating "man-in-the-middle" and other
attacks, mandatory secure server certificate verification is not
viable as a default Internet mail delivery policy. Most MX hosts
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 ≥ 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
"exclude" attribute (Postfix ≥ 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
<dt><b>verify</b></dt> <dd><a href="#client_tls_verify">Mandatory
server certificate verification</a>. Mail is delivered only if the
-TLS handshake
-succeeds, if the remote SMTP server certificate can be validated (not
-expired or revoked, and signed by a trusted certificate authority), and
-if the server certificate name matches the optional "match" attribute (or
-the <a href="postconf.5.html">main.cf</a> <a href="postconf.5.html#smtp_tls_verify_cert_match">smtp_tls_verify_cert_match</a> parameter value when no optional
-"match" attribute is specified). </dd>
+TLS handshake succeeds, if the remote SMTP server certificate can
+be validated (not expired or revoked, and signed by a trusted
+certificate authority), and if the server certificate name matches
+the optional "match" attribute (or the <a href="postconf.5.html">main.cf</a> <a href="postconf.5.html#smtp_tls_verify_cert_match">smtp_tls_verify_cert_match</a>
+parameter value when no optional "match" attribute is specified).
+With Postfix ≥ 2.11 the "tafile" attribute optionally modifies
+trust chain verification in the same manner as the
+"<a href="postconf.5.html#smtp_tls_trust_anchor_file">smtp_tls_trust_anchor_file</a>" parameter. The "tafile" attribute
+may be specified multiple times to load multiple trust-anchor
+files. </dd>
<dt><b>secure</b></dt> <dd><a href="#client_tls_secure">Secure certificate
verification.</a> Mail is delivered only if the TLS handshake succeeds,
or revoked, and signed by a trusted certificate authority), and if the
server certificate name matches the optional "match" attribute (or the
<a href="postconf.5.html">main.cf</a> <a href="postconf.5.html#smtp_tls_secure_cert_match">smtp_tls_secure_cert_match</a> parameter value when no optional
-"match" attribute is specified). </dd>
+"match" attribute is specified). With Postfix ≥ 2.11 the "tafile"
+attribute optionally modifies trust chain verification in the same manner
+as the "<a href="postconf.5.html#smtp_tls_trust_anchor_file">smtp_tls_trust_anchor_file</a>" parameter. The "tafile" attribute
+may be specified multiple times to load multiple trust-anchor
+files. </dd>
</dl>
</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 ≥ 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
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
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
<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>
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>
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
<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"
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
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,
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
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
<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
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
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>
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
<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>
<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>
<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>
--- /dev/null
+<!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>
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
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
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.
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)
(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 - $? >$@
--- /dev/null
+.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
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.
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.
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
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
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
.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"
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
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
(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
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
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
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
.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.
s;\blmtp_tls_security_level\b;<a href="postconf.5.html#lmtp_tls_security_level">$&</a>;g;
s;\blmtp_tls_fingerprint_cert_match\b;<a href="postconf.5.html#lmtp_tls_fingerprint_cert_match">$&</a>;g;
s;\blmtp_tls_verify_cert_match\b;<a href="postconf.5.html#lmtp_tls_verify_cert_match">$&</a>;g;
+ s;\blmtp_tls_trust_anchor_file\b;<a href="postconf.5.html#lmtp_tls_trust_anchor_file">$&</a>;g;
s;\blmtp_tls_per_site\b;<a href="postconf.5.html#lmtp_tls_per_site">$&</a>;g;
s;\blmtp_tls_cert_file\b;<a href="postconf.5.html#lmtp_tls_cert_file">$&</a>;g;
s;\blmtp_tls_key_file\b;<a href="postconf.5.html#lmtp_tls_key_file">$&</a>;g;
s;\bsmtp_tls_fingerprint_cert_match\b;<a href="postconf.5.html#smtp_tls_fingerprint_cert_match">$&</a>;g;
s;\bsmtp_tls_verify_cert_match\b;<a href="postconf.5.html#smtp_tls_verify_cert_match">$&</a>;g;
s;\bsmtp_tls_secure_cert_match\b;<a href="postconf.5.html#smtp_tls_secure_cert_match">$&</a>;g;
+ s;\bsmtp_tls_trust_anchor_file\b;<a href="postconf.5.html#smtp_tls_trust_anchor_file">$&</a>;g;
s;\bsmtp_tls_scert_verifydepth\b;<a href="postconf.5.html#smtp_tls_scert_verifydepth">$&</a>;g;
s;\bsmtp_tls_security_level\b;<a href="postconf.5.html#smtp_tls_security_level">$&</a>;g;
s;\bsmtp_tls_session_cache_database\b;<a href="postconf.5.html#smtp_tls_session_cache_database">$&</a>;g;
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;
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;
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>
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
certificates. </p>
<p> 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. </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>
</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 > 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>
<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 ≥ 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>
<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>
</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
<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)
If you want mandatory encryption without server certificate
verification, see <a href="#client_tls_encrypt">above</a>. </p>
+<p> With Postfix ≥ 2.11 the "smtp_tls_trust_anchor_file" parameter
+or more typically the corresponding per-destination "tafile" attribute
+optionally modifies trust chain verification. If the parameter is
+not empty the root CAs in CAfile and CApath are no longer trusted.
+Rather, the Postfix SMTP client will only trust certificate-chains
+signed by one of the trust-anchors contained in the chosen files.
+The specified trust-anchor certificates and public keys are not
+subject to expiration, and need not be (self-signed) root CAs. They
+may, if desired, be intermediate certificates. Therefore, these
+certificates also may be found "in the middle" of the trust chain
+presented by the remote SMTP server, and any untrusted issuing
+parent certificates will be ignored. </p>
+
<p> Despite the potential for eliminating "man-in-the-middle" and other
attacks, mandatory certificate trust chain and subject name verification
is not viable as a default Internet mail delivery policy. Most MX hosts
without server certificate verification, see <a
href="#client_tls_encrypt">above</a>. </p>
+<p> With Postfix ≥ 2.11 the "smtp_tls_trust_anchor_file" parameter
+or more typically the corresponding per-destination "tafile" attribute
+optionally modifies trust chain verification. If the parameter is
+not empty the root CAs in CAfile and CApath are no longer trusted.
+Rather, the Postfix SMTP client will only trust certificate-chains
+signed by one of the trust-anchors contained in the chosen files.
+The specified trust-anchor certificates and public keys are not
+subject to expiration, and need not be (self-signed) root CAs. They
+may, if desired, be intermediate certificates. Therefore, these
+certificates also may be found "in the middle" of the trust chain
+presented by the remote SMTP server, and any untrusted issuing
+parent certificates will be ignored. </p>
+
<p> Despite the potential for eliminating "man-in-the-middle" and other
attacks, mandatory secure server certificate verification is not
viable as a default Internet mail delivery policy. Most MX hosts
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 ≥ 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
"exclude" attribute (Postfix ≥ 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
<dt><b>verify</b></dt> <dd><a href="#client_tls_verify">Mandatory
server certificate verification</a>. Mail is delivered only if the
-TLS handshake
-succeeds, if the remote SMTP server certificate can be validated (not
-expired or revoked, and signed by a trusted certificate authority), and
-if the server certificate name matches the optional "match" attribute (or
-the main.cf smtp_tls_verify_cert_match parameter value when no optional
-"match" attribute is specified). </dd>
+TLS handshake succeeds, if the remote SMTP server certificate can
+be validated (not expired or revoked, and signed by a trusted
+certificate authority), and if the server certificate name matches
+the optional "match" attribute (or the main.cf smtp_tls_verify_cert_match
+parameter value when no optional "match" attribute is specified).
+With Postfix ≥ 2.11 the "tafile" attribute optionally modifies
+trust chain verification in the same manner as the
+"smtp_tls_trust_anchor_file" parameter. The "tafile" attribute
+may be specified multiple times to load multiple trust-anchor
+files. </dd>
<dt><b>secure</b></dt> <dd><a href="#client_tls_secure">Secure certificate
verification.</a> Mail is delivered only if the TLS handshake succeeds,
or revoked, and signed by a trusted certificate authority), and if the
server certificate name matches the optional "match" attribute (or the
main.cf smtp_tls_secure_cert_match parameter value when no optional
-"match" attribute is specified). </dd>
+"match" attribute is specified). With Postfix ≥ 2.11 the "tafile"
+attribute optionally modifies trust chain verification in the same manner
+as the "smtp_tls_trust_anchor_file" parameter. The "tafile" attribute
+may be specified multiple times to load multiple trust-anchor
+files. </dd>
</dl>
</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 ≥ 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
# 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.
<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"
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
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,
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
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
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
<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
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
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
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
<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
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>
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>
+
unzipping
windowsize
xpostconf
+TLSA
+tafile
+VPN
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 */
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 */
* 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);
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;
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;
}
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);
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
#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"
#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)
#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.
*/
#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"
#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"
* 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"
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
*/
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;
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 */
/*
* 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
--- /dev/null
+../../.indent.pro
\ No newline at end of file
--- /dev/null
+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
--- /dev/null
+/*++
+/* 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, ¶m_name, ¶m_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);
+}
--- /dev/null
+/*++
+/* 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
--- /dev/null
+/*++
+/* 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
+/*--*/
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)
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
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
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
VAR_LMTP_TLS_SEC_CMATCH, DEF_LMTP_TLS_SEC_CMATCH, &var_smtp_tls_sec_cmatch, 1, 0,
VAR_LMTP_TLS_FPT_CMATCH, DEF_LMTP_TLS_FPT_CMATCH, &var_smtp_tls_fpt_cmatch, 0, 0,
VAR_LMTP_TLS_FPT_DGST, DEF_LMTP_TLS_FPT_DGST, &var_smtp_tls_fpt_dgst, 1, 0,
+ VAR_LMTP_TLS_TAFILE, DEF_LMTP_TLS_TAFILE, &var_smtp_tls_tafile, 0, 0,
VAR_LMTP_TLS_PROTO, DEF_LMTP_TLS_PROTO, &var_smtp_tls_proto, 0, 0,
VAR_LMTP_TLS_CIPH, DEF_LMTP_TLS_CIPH, &var_smtp_tls_ciph, 1, 0,
VAR_LMTP_TLS_ECCERT_FILE, DEF_LMTP_TLS_ECCERT_FILE, &var_smtp_tls_eccert_file, 0, 0,
/* Available in Postfix version 2.8 and later:
/* .IP "\fBtls_disable_workarounds (see 'postconf -d' output)\fR"
/* List or bit-mask of OpenSSL bug work-arounds to disable.
+/* .PP
+/* Available in Postfix version 2.11 and later:
+/* .IP "\fBsmtp_tls_trust_anchor_file (empty)\fR"
+/* Zero or more PEM-format files with trust-anchor certificates
+/* and/or public keys.
+/* .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
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;
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;
*/
#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.
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.
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)
/*
*/
#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)
/*
* 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 */
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 )
/*
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.
*/
/* Allgemeine Elektrotechnik
/* Universitaetsplatz 3-4
/* D-03044 Cottbus, Germany
+/*
+/* Victor Duchovni
+/* Morgan Stanley
/*--*/
{
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
* 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;
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";
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;
/*
* 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)
{
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) {
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);
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 */
/* 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);
}
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) {
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;
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
* 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 "
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;
}
/* 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
*
* 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);
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);
}
char *def_service)
{
DELIVER_REQUEST *request = state->request;
+ SMTP_ITERATOR *iter = state->iterator;
ARGV *sites;
char *dest;
char **cpp;
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
* 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
* 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);
}
/*
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;
* 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
/* 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);
--- /dev/null
+/*++
+/* 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);
+}
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,
&& (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.
* 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,
* 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"),
* 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
cipher_exclusions
= vstring_str(session->tls->exclusions),
matchargv = session->tls->matchargv,
- mdalg = var_smtp_tls_fpt_dgst);
+ mdalg = var_smtp_tls_fpt_dgst,
+ dane = session->tls->dane);
vstring_free(serverid);
if (session->tls_context == 0) {
* server, so no need to disable I/O, ... we can even be polite and send
* "QUIT".
*
- * See src/tls/tls_level.c. Levels above encrypt require matching. Levels >=
- * verify require CA trust.
+ * See src/tls/tls_level.c 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);
/* #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;
/*
* 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
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);
}
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
/*
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);
/* 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);
*/
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);
}
/*
* 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
/* 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);
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)
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);
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,
* 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);
/*
* 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);
/* 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";
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 */
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);
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;
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.
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);
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)
--- /dev/null
+/*++
+/* 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
+++ /dev/null
-/*++
-/* 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
/* 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
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. */
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) {
* 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,
(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
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);
SHELL = /bin/sh
SRCS = tls_prng_dev.c tls_prng_egd.c tls_prng_file.c tls_fprint.c \
tls_prng_exch.c tls_stream.c tls_bio_ops.c tls_misc.c tls_dh.c \
- tls_rsa.c tls_verify.c tls_certkey.c tls_session.c \
+ tls_rsa.c tls_verify.c tls_dane.c tls_certkey.c tls_session.c \
tls_client.c tls_server.c tls_scache.c tls_mgr.c tls_seed.c \
tls_level.c \
tls_proxy_clnt.c tls_proxy_print.c tls_proxy_scan.c
OBJS = tls_prng_dev.o tls_prng_egd.o tls_prng_file.o tls_fprint.o \
tls_prng_exch.o tls_stream.o tls_bio_ops.o tls_misc.o tls_dh.o \
- tls_rsa.o tls_verify.o tls_certkey.o tls_session.o \
+ tls_rsa.o tls_verify.o tls_dane.o tls_certkey.o tls_session.o \
tls_client.o tls_server.o tls_scache.o tls_mgr.o tls_seed.o \
tls_level.o \
tls_proxy_clnt.o tls_proxy_print.o tls_proxy_scan.o
tls_client.o: tls.h
tls_client.o: tls_client.c
tls_client.o: tls_mgr.h
+tls_dane.o: ../../include/argv.h
+tls_dane.o: ../../include/dns.h
+tls_dane.o: ../../include/msg.h
+tls_dane.o: ../../include/myaddrinfo.h
+tls_dane.o: ../../include/mymalloc.h
+tls_dane.o: ../../include/name_code.h
+tls_dane.o: ../../include/name_mask.h
+tls_dane.o: ../../include/sock_addr.h
+tls_dane.o: ../../include/stringops.h
+tls_dane.o: ../../include/sys_defs.h
+tls_dane.o: ../../include/vbuf.h
+tls_dane.o: ../../include/vstream.h
+tls_dane.o: ../../include/vstring.h
+tls_dane.o: tls.h
+tls_dane.o: tls_dane.c
tls_dh.o: ../../include/argv.h
tls_dh.o: ../../include/mail_params.h
tls_dh.o: ../../include/msg.h
*
* 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[];
#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
/*
#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.
/* 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;
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;
};
/*
- * 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 *);
/*
#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 *);
const char *cipher_exclusions; /* Ciphers to exclude */
const ARGV *matchargv; /* Cert match patterns */
const char *mdalg; /* default message digest algorithm */
+ const TLS_DANE *dane; /* RFC 6698 verification */
} TLS_CLIENT_START_PROPS;
extern TLS_APPL_STATE *tls_client_init(const TLS_CLIENT_INIT_PROPS *);
((props)->a12), ((props)->a13), (props)))
#define TLS_CLIENT_START(props, a1, a2, a3, a4, a5, a6, a7, a8, a9, \
- a10, a11, a12, a13, a14) \
+ a10, a11, a12, a13, a14, a15) \
tls_client_start((((props)->a1), ((props)->a2), ((props)->a3), \
((props)->a4), ((props)->a5), ((props)->a6), ((props)->a7), \
((props)->a8), ((props)->a9), ((props)->a10), ((props)->a11), \
- ((props)->a12), ((props)->a13), ((props)->a14), (props)))
+ ((props)->a12), ((props)->a13), ((props)->a14), ((props)->a15), (props)))
/*
* tls_server.c
extern char *tls_peer_CN(X509 *, const TLS_SESS_STATE *);
extern char *tls_issuer_CN(X509 *, const TLS_SESS_STATE *);
extern const char *tls_dns_name(const GENERAL_NAME *, const TLS_SESS_STATE *);
+extern int tls_cert_match(TLS_SESS_STATE *, int, X509 *, int);
extern int tls_verify_certificate_callback(int, X509_STORE_CTX *);
extern void tls_log_verify_error(TLS_SESS_STATE *);
* 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 *);
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:
/* .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
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.
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);
}
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? */
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
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)
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;
}
/*
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;
/*
* 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)
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
* not attempt to re-use sessions whose ciphers are too weak. We salt the
* session lookup key with the cipher list, so that sessions found in the
* cache are always acceptable.
+ *
+ * With DANE, (more generally any TLScontext where we specified explicit
+ * trust-anchor or end-entity certificates) the verification status of
+ * the SSL session depends on the specified list. Since we verify the
+ * certificate only during the initial handshake, we must segregate
+ * sessions with different TA lists. Note, that TA re-verification is
+ * not possible with cached sessions, since these don't hold the complete
+ * peer trust chain. Therefore, we compute a digest of the sorted TA
+ * parameters and append it to the serverid.
*/
myserverid = tls_serverid_digest(props, protomask, cipher_list);
TLScontext->stream = props->stream;
TLScontext->mdalg = props->mdalg;
+ /* Alias DANE digest info from props */
+ TLScontext->dane = props->dane;
+
if ((TLScontext->con = SSL_new(app_ctx->ssl_ctx)) == NULL) {
msg_warn("Could not allocate 'TLScontext->con' with SSL_new()");
tls_print_errors();
* 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
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
/*
* 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("");
}
--- /dev/null
+/*++
+/* NAME
+/* tls_dane 3
+/* SUMMARY
+/* Support for RFC 6698 (DANE) certificate matching
+/* SYNOPSIS
+/* #include <tls.h>
+/*
+/* int tls_dane_avail()
+/*
+/* void tls_dane_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
/* 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
/* 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(),
/* .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 */
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);
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, ':');
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;
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);
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;
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);
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;
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);
}
/* 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,
/* 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
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;
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.
*/
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)
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[] = {
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;
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;
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)
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);
}
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)
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,
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);
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,
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);
/* .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
| ((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
*/
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.
}
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)) {
", 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("");
}
/* #define TLS_INTERNAL
/* #include <tls.h>
/*
+/* int tls_cert_match(TLSContext, usage, cert, depth)
+/* TLS_SESS_STATE *TLScontext;
+/* int usage;
+/* X509 *cert;
+/* int depth;
+/*
/* int tls_verify_certificate_callback(ok, ctx)
/* int ok;
/* X509_STORE_CTX *ctx;
/* const GENERAL_NAME *gn;
/* TLS_SESS_STATE *TLScontext;
/* DESCRIPTION
+/* tls_cert_match() matches the full and/or public key digest of
+/* "cert" against each candidate digest in TLScontext->dane. If usage
+/* is TLS_DANE_EE, the match is against end-entity digests, otherwise
+/* it is against trust-anchor digests. Returns true if a match is found,
+/* false otherwise.
+/*
/* tls_verify_certificate_callback() is called several times (directly
/* or indirectly) from crypto/x509/x509_vfy.c. It collects errors
/* and trust information at each element of the trust chain.
/* .IP gn
/* An OpenSSL GENERAL_NAME structure holding a DNS subjectAltName
/* to be decoded and checked for validity.
+/* .IP usage
+/* Trust anchor (TLS_DANE_TA) or end-entity (TLS_DANE_EE) digests?
+/* .IP cert
+/* Certificate from peer trust chain (CA or leaf server).
+/* .IP depth
+/* The certificate depth for logging.
/* .IP peercert
/* Server or client X.509 certificate.
/* .IP TLScontext
X509 *errorcert, int errorcode)
{
/* No news is good news */
- if (TLScontext->errordepth >= 0 && TLScontext->errordepth <= depth)
+ if ((TLScontext->trustdepth >= 0 && TLScontext->trustdepth < depth) ||
+ (TLScontext->errordepth >= 0 && TLScontext->errordepth <= depth))
return;
/*
CRYPTO_add(&errorcert->references, 1, CRYPTO_LOCK_X509);
TLScontext->errorcert = errorcert;
TLScontext->errorcode = errorcode;
+
+ /*
+ * Maintain an invariant, at most one of errordepth and trustdepth is
+ * non-negative at any given time.
+ */
+ TLScontext->errordepth = depth;
+ TLScontext->trustdepth = -1;
+}
+
+/* update_trust_state - safely stash away trust state */
+
+static void update_trust_state(TLS_SESS_STATE *TLScontext, int depth)
+{
+ /* No news is bad news */
+ if ((TLScontext->trustdepth >= 0 && TLScontext->trustdepth <= depth)
+ || (TLScontext->errordepth >= 0 && TLScontext->errordepth <= depth))
+ return;
+
+ /*
+ * Maintain an invariant, at most one of errordepth and trustdepth is
+ * non-negative at any given time.
+ */
+ TLScontext->trustdepth = depth;
+ TLScontext->errordepth = -1;
+}
+
+/* tls_cert_match - match cert against given list of TA or EE digests */
+
+int tls_cert_match(TLS_SESS_STATE *TLScontext, int usage, X509 *cert, int depth)
+{
+ const TLS_DANE *dane = TLScontext->dane;
+ TLS_TLSA *tlsa = (usage == TLS_DANE_EE) ? dane->ee : dane->ta;
+ const char *namaddr = TLScontext->namaddr;
+ const char *ustr = (usage == TLS_DANE_EE) ? "end entity" : "trust anchor";
+ int mixed = (dane->flags & TLS_DANE_FLAG_MIXED);
+ int matched;
+
+ for (matched = 0; tlsa && !matched; tlsa = tlsa->next) {
+ char **dgst;
+ ARGV *certs;
+
+ if (tlsa->pkeys) {
+ char *pkey_dgst = tls_pkey_fprint(cert, tlsa->mdalg);
+
+ for (dgst = tlsa->pkeys->argv; !matched && *dgst; ++dgst)
+ if (strcasecmp(pkey_dgst, *dgst) == 0)
+ matched = 1;
+ if (TLScontext->log_mask & (TLS_LOG_VERBOSE | TLS_LOG_CERTMATCH))
+ msg_info("%s: depth=%d matched=%d %s public-key %s digest=%s",
+ namaddr, depth, matched, ustr, tlsa->mdalg, pkey_dgst);
+ myfree(pkey_dgst);
+ }
+ certs = mixed ? tlsa->pkeys : tlsa->certs;
+ if (certs != 0 && !matched) {
+ char *cert_dgst = tls_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 */
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) {
/* .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.
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 \
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 \
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)
$(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:
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
/* ARGV *argv_alloc(len)
/* ssize_t len;
/*
+/* ARGV *argv_sort(argvp)
+/* ARGV *argvp;
+/*
/* ARGV *argv_free(argvp)
/* ARGV *argvp;
/*
/* length. The result is ready for use by argv_add(). The array
/* is null terminated.
/*
+/* argv_sort() sorts the elements of argvp in place returning
+/* the original array.
+/*
/* argv_add() copies zero or more strings and adds them to the
/* specified string array. The array is null terminated.
/* Terminate the argument list with a null pointer. The manifest
return (argvp);
}
+static int argv_cmp(const void *e1, const void *e2)
+{
+ const char *s1 = *(const char **) e1;
+ const char *s2 = *(const char **) e2;
+
+ return strcmp(s1, s2);
+}
+
+/* argv_sort - sort array in place */
+
+ARGV *argv_sort(ARGV *argvp)
+{
+ qsort(argvp->argv, argvp->argc, sizeof(argvp->argv[0]), argv_cmp);
+ return (argvp);
+}
+
/* argv_extend - extend array */
static void argv_extend(ARGV *argvp)
} ARGV;
extern ARGV *argv_alloc(ssize_t);
+extern ARGV *argv_sort(ARGV *);
extern void argv_add(ARGV *,...);
extern void argv_addn(ARGV *,...);
extern void argv_terminate(ARGV *);
/* 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.
/* 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;
/*
* 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) {
/* 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;
/*
* 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)
#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) \
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);
/*
* 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
/* 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
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 */
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
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 (;;) {
}
ni_free(domain);
+ myfree(query);
return return_val;
}
--- /dev/null
+/*++
+/* 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
--- /dev/null
+#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