-TDICT_THASH
-TDICT_UNION
-TDICT_UNIX
+-TDICT_UTF8_BACKUP
-TDICT_WRAPPER
-TDNS_FIXED
-TDNS_REPLY
-TEC_KEY
-TEDIT_FILE
-TEVENT_MASK
+-TEVP_MD_CTX
-TEVP_PKEY
-TEXPAND_ATTR
-TFILE
-Tcipher_probe_t
-Td2i_X509_t
-Tdane_digest
+-Tdane_mtype
-Tfilter_ctx
-Tgeneral_name_stack_t
-Tiana_digest
20200626
Typo: in postconf(5) documentation, AAAAA should be AAAA.
- Christian Franke. File: proto/postconf.proto.
+ Christian Franke. File: proto/postconf.proto.
Bugfix (introduced: Postfix 2.11): The Postfix smtp(8)
client did not send the right SNI name when the TLSA base
domain was a secure CNAME expansion of the MX hostname (or
- non-MX nexthop domain). Domains with CNAME expanded MX
- hosts are not conformant with RFC5321, and so are rare.
- Even more rare are MX hosts with TLSA records for their
- CNAME expansion. For this to matter, the remote SMTP server
- would also have to select its certificate based on the SNI
- name in such a way that the original MX host would yield a
- different certificate. Among the ~2 million hosts in the
- DANE survey, none meet the conditions for returning a
- different certificate for the expanded CNAME. Therefore,
- sending the correct SNI name should not break existing mail
- flows. Fixed by Viktor Dukhovni. File: src/tls/tls_client.c.
+ non-MX nexthop domain). Domains with CNAME expanded MX hosts
+ are not conformant with RFC5321, and so are rare. Even more
+ rare are MX hosts with TLSA records for their CNAME expansion.
+ For this to matter, the remote SMTP server would also have
+ to select its certificate based on the SNI name in such a
+ way that the original MX host would yield a different
+ certificate. Among the ~2 million hosts in the DANE survey,
+ none meet the conditions for returning a different certificate
+ for the expanded CNAME. Therefore, sending the correct SNI
+ name should not break existing mail flows. Fixed by Viktor
+ Dukhovni. File: src/tls/tls_client.c.
+
+20200705
+
+ Cleanup: OpenSSL-1.1.1 is the minimum supported version.
+ This is an LTS (long-term support) version that will reach
+ the end of life by 2023-09-11. This removes support for
+ export ciphers.
+
+ This also changes the Postfix default fingerprint digest
+ from MD5 to SHA256, but only when the compatibility_level
+ is set to '3' or higher.
+
+ Code by Viktor Dukhovni. Files: global/mail_params.c,
+ global/mail_params.h, posttls-finger/posttls-finger.c,
+ proto/COMPATIBILITY_README.html, proto/TLS_README.html,
+ proto/postconf.proto, smtp/smtp.c, smtp/smtp_tls_policy.c,
+ smtpd/smtpd.c, smtpd/smtpd_check.c, tls/Makefile.in,
+ tls/tls.h, tls/tls_certkey.c, tls/tls_client.c, tls/tls_dane.c,
+ tls/tls_dh.c, tls/tls_misc.c, tls/tls_rsa.c, tls/tls_server.c,
+ tls/tls_verify.c.
+
+20200710
+
+ Security: added a section to the sendmail(1) manpage for
+ security researchers and application developers, with an
+ example of using '--' to disable command option processing
+ for user-specified data. File sendmail/sendmail.c.
+
+ Error reporting: added '--' to a postalias command line to
+ make an obsecure error message less confusing. File
+ sendmail/sendmail.c.
+
+ Conversion from Postfix built-in DANE support to OpenSSL
+ DANE support. Code by Viktor Dukhovni. Files:
+ posttls-finger/posttls-finger.c, proto/postconf.proto,
+ smtp/smtp.c, smtp/smtp_proto.c, smtp/smtp_tls_policy.c,
+ tls/Makefile.in, tlsproxy/tlsproxy.c, tls/tls_client.c,
+ tls/tls_dane.c, tls/tls_fprint.c, tls/tls.h, tls/tls_misc.c,
+ tls/tls_proxy_client_print.c, tls/tls_proxy_client_scan.c,
+ tls/tls_proxy_context_print.c, tls/tls_proxy_context_scan.c,
+ tls/tls_proxy.h, tls/tls_verify.c, util/hex_code.c.
+
+ Bugfix (introduced: Postfix 3.0): minor memory leaks in the
+ Postfix TLS library, found during tests. File: tls/tls_misc.c.
+
+20200712
+
+ Cleanup: non-TLS builds were failing. File: util/tls_misc.c.
+
+ Bugfix (introduced: Postfix 3.0): 4kbyte per session memory
+ leak in the Postfix TLS library, found during tests. File:
+ tls/tls_misc.c.
+
+20200718
+
+ Cleanup TLS library: coding style, additional error message,
+ additional handling of internationalized domain name, and
+ dropping an unused variable. Files: tls.h, tls_dane.c,
+ tls_proxy_client_scan.c, tls_client.c.
+
+ Noise suppression: shut up compilers that warn about
+ sizeof("text"). File: smtpstone/smtp-sink.c.
+
+20200719
+
+ Cleanup old API: mymemdup() should return "void *", the
+ same value type as its main argument, and the same result
+ type as mymalloc(). In a future update we can remove all
+ the noisy but unnecessary casts of their result values to
+ character pointer. Files: util/mymalloc.c, util/mymalloc.h.
+
+ Cleanup: don't split the sendmail -oA option value on comma
+ or whitespace, before passing the value to the postalias
+ command line. This results in unexpected behavior. File:
+ sendmail/sendmail.c.
+
+ Documentation: updated the manpage of the unprivileged(!)
+ sendmail(1) command with instructions to avoid privilege
+ esclation attacks in naive programs that run Postfix programs
+ with user-specified arguments. File: sendmail/sendmail.c.
+
+20200720
+
+ Bugfix (introduced: postfix 3.4): nullpointer dereference
+ in debug logging when tlsproxy is unavailable. File:
+ posttls-finger/posttls-finger.c.
+
+ Final cleanups of the peername matching code. File:
+ tls/tls_client.c.
* Using backwards-compatible default setting smtputf8_enable=no
+ * Using backwards-compatible default setting smtpd_tls_fingerprint_digest=md5
+
+ * Using backwards-compatible default setting smtp_tls_fingerprint_digest=md5
+
+ * Using backwards-compatible default setting lmtp_tls_fingerprint_digest=md5
+
If such a message is logged in the context of a legitimate request, the system
administrator should make the backwards-compatible setting permanent in main.cf
or master.cf, as detailed in the sections that follow.
U\bUs\bsi\bin\bng\bg b\bba\bac\bck\bkw\bwa\bar\brd\bds\bs-\b-c\bco\bom\bmp\bpa\bat\bti\bib\bbl\ble\be d\bde\bef\bfa\bau\bul\blt\bt s\bse\bet\btt\bti\bin\bng\bg s\bsm\bmt\btp\bpu\but\btf\bf8\b8_\b_e\ben\bna\bab\bbl\ble\be=\b=n\bno\bo
-The smtputf8_enable default value has changed from "no" to "yes. With the new
+The smtputf8_enable default value has changed from "no" to "yes". With the new
"yes" setting, the Postfix SMTP server rejects non-ASCII addresses from clients
that don't request SMTPUTF8 support, after Postfix is updated from an older
version. The backwards-compatibility safety net is designed to prevent such
# p\bpo\bos\bst\btc\bco\bon\bnf\bf s\bsm\bmt\btp\bpu\but\btf\bf8\b8_\b_e\ben\bna\bab\bbl\ble\be=\b=n\bno\bo
# p\bpo\bos\bst\btf\bfi\bix\bx r\bre\bel\blo\boa\bad\bd
+U\bUs\bsi\bin\bng\bg b\bba\bac\bck\bkw\bwa\bar\brd\bds\bs-\b-c\bco\bom\bmp\bpa\bat\bti\bib\bbl\ble\be d\bde\bef\bfa\bau\bul\blt\bt s\bse\bet\btt\bti\bin\bng\bg s\bsm\bmt\btp\bpd\bd_\b_t\btl\bls\bs_\b_f\bfi\bin\bng\bge\ber\brp\bpr\bri\bin\bnt\bt_\b_d\bdi\big\bge\bes\bst\bt=\b=m\bmd\bd5\b5
+
+The smtpd_tls_fingerprint_digest default value has changed from "md5" to
+"sha256". With the new "sha256" setting, the Postfix SMTP server avoids using
+the deprecated "md5" algorithm and computes a more secure digest of the client
+certificate.
+
+If you're using the default "md5" setting, or even an explicit "sha1" (also
+deprecated) setting, you should consider switching to "sha256". This will
+require updating any associated lookup table keys with the "sha256" digests of
+the expected client certificate or public key.
+
+As long as the smtpd_tls_fingerprint_digest parameter is left at its implicit
+default value, and the compatibility_level setting is less than 3, Postfix logs
+a warning each time a client certificate or public key fingerprint is
+(potentially) used for access control:
+
+ postfix/smtpd[27560]: using backwards-compatible default setting
+ smtpd_tls_fingerprint_digest=md5 to compute certificate fingerprints
+
+Since any client certificate fingerprints are passed in policy service lookups,
+and Postfix doesn't know whether the fingerprint will be used, the warning may
+also be logged when policy lookups are performed for connections that used a
+client certificate, even if the policy service does not in fact examine the
+client certificate. To reduce the noise somewhat, such warnings are issued at
+most once per smtpd(8) process instance.
+
+If you prefer to stick with "md5", you can suppress the warnings by making that
+setting explicit. After addressing any other compatibility warnings, you can
+update your compatibility level.
+
+ # p\bpo\bos\bst\btc\bco\bon\bnf\bf s\bsm\bmt\btp\bpd\bd_\b_t\btl\bls\bs_\b_f\bfi\bin\bng\bge\ber\brp\bpr\bri\bin\bnt\bt_\b_d\bdi\big\bge\bes\bst\bt=\b=m\bmd\bd5\b5
+ # p\bpo\bos\bst\btf\bfi\bix\bx r\bre\bel\blo\boa\bad\bd
+
+U\bUs\bsi\bin\bng\bg b\bba\bac\bck\bkw\bwa\bar\brd\bds\bs-\b-c\bco\bom\bmp\bpa\bat\bti\bib\bbl\ble\be d\bde\bef\bfa\bau\bul\blt\bt s\bse\bet\btt\bti\bin\bng\bg s\bsm\bmt\btp\bp_\b_t\btl\bls\bs_\b_f\bfi\bin\bng\bge\ber\brp\bpr\bri\bin\bnt\bt_\b_d\bdi\big\bge\bes\bst\bt=\b=m\bmd\bd5\b5
+
+The smtp_tls_fingerprint_digest and lmtp_tls_fingerprint_digest default values
+have changed from "md5" to "sha256". With the new "sha256" setting, the Postfix
+SMTP and LMTP client avoids using the deprecated "md5" algorithm and computes a
+more secure digest of the server certificate.
+
+If you're using the default "md5" setting, or even an explicit "sha1" (also
+deprecated) setting, you should consider switching to "sha256". This will
+require updating any "fingerprint" security level policies in the TLS policy
+table to specify matching "sha256" digests of the expected server certificates
+or public keys.
+
+As long as the smtp_tls_fingerprint_digest (or LMTP equivalent) parameter is
+left at its implicit default value, and the compatibility_level setting is less
+than 3, Postfix logs a warning each time the "fingerprint" security level is
+used to specify matching "md5" digests of trusted server certificates or public
+keys:
+
+ postfix/smtp[27560]: using backwards-compatible default setting
+ smtp_tls_fingerprint_digest=md5 to compute certificate fingerprints
+
+If you prefer to stick with "md5", you can suppress the warnings by making that
+setting explicit. After addressing any other compatibility warnings, you can
+update your compatibility level.
+
+ # p\bpo\bos\bst\btc\bco\bon\bnf\bf '\b's\bsm\bmt\btp\bp_\b_t\btl\bls\bs_\b_f\bfi\bin\bng\bge\ber\brp\bpr\bri\bin\bnt\bt_\b_d\bdi\big\bge\bes\bst\bt =\b= m\bmd\bd5\b5'\b' \\b\
+ '\b'l\blm\bmt\btp\bp_\b_t\btl\bls\bs_\b_f\bfi\bin\bng\bge\ber\brp\bpr\bri\bin\bnt\bt_\b_d\bdi\big\bge\bes\bst\bt =\b= m\bmd\bd5\b5'\b'
+ # p\bpo\bos\bst\btf\bfi\bix\bx r\bre\bel\blo\boa\bad\bd
+
T\bTu\bur\brn\bni\bin\bng\bg o\bof\bff\bf t\bth\bhe\be b\bba\bac\bck\bkw\bwa\bar\brd\bds\bs-\b-c\bco\bom\bmp\bpa\bat\bti\bib\bbi\bil\bli\bit\bty\by s\bsa\baf\bfe\bet\bty\by n\bne\bet\bt
Backwards compatibility is turned off by updating the compatibility_level
The digest algorithm used to compute the client certificate fingerprints is
specified with the main.cf smtpd_tls_fingerprint_digest parameter. The default
-is "md5", for compatibility with Postfix versions < 2.5.
+algorithm is s\bsh\bha\ba2\b25\b56\b6 with Postfix >= 3.6 and the c\bco\bom\bmp\bpa\bat\bti\bib\bbi\bil\bli\bit\bty\by_\b_l\ble\bev\bve\bel\bl set to 3 or
+higher. With Postfix <= 3.5, the default algorithm is m\bmd\bd5\b5. The best-practice
+algorithm is now s\bsh\bha\ba2\b25\b56\b6. Recent advances in hash function cryptanalysis have
+led to md5 and sha1 being deprecated in favor of sha256. However, as long as
+there are no known "second pre-image" attacks against the older algorithms,
+their use in this context, though not recommended, is still likely safe.
The permit_tls_all_clientcerts feature must be used with caution, because it
can result in too many access permissions. Use this feature only if a special
another OpenSSL command that converts the key to DER and then to the "dgst"
command to compute the fingerprint.
-The actual command to transform the key to DER format depends on the version of
-OpenSSL used. With OpenSSL 1.0.0 and later, the "pkey" command supports all key
-types. With OpenSSL 0.9.8 and earlier, the key type is always RSA (nobody uses
-DSA, and EC keys are not fully supported by 0.9.8), so the "rsa" command is
-used.
+Example:
- # OpenSSL 1.0 with all certificates and SHA-1 fingerprints.
$ openssl x509 -in cert.pem -noout -pubkey |
openssl pkey -pubin -outform DER |
- openssl dgst -sha1 -c
- (stdin)= 64:3f:1f:f6:e5:1e:d4:2a:56:8b:fc:09:1a:61:98:b5:bc:7c:60:58
-
- # OpenSSL 0.9.8 with RSA certificates and MD5 fingerprints.
- $ openssl x509 -in cert.pem -noout -pubkey |
- openssl rsa -pubin -outform DER |
- openssl dgst -md5 -c
- (stdin)= f4:62:60:f6:12:8f:d5:8d:28:4d:13:a7:db:b2:ff:50
-
-Note: Postfix 2.9.0-2.9.5 computed the public key fingerprint incorrectly. To
-use public-key fingerprints, upgrade to Postfix 2.9.6 or later.
+ openssl dgst -sha256 -c
+ (stdin)= 64:3f:1f:f6:e5:1e:d4:2a:...:8b:fc:09:1a:61:98:b5:bc:7c:60:58
S\bSe\ber\brv\bve\ber\br-\b-s\bsi\bid\bde\be c\bci\bip\bph\bhe\ber\br c\bco\bon\bnt\btr\bro\bol\bls\bs
or multiple match attributes can be employed. The ":" character is not used as
a delimiter as it occurs between each pair of fingerprint (hexadecimal) digits.
+The default algorithm is s\bsh\bha\ba2\b25\b56\b6 with Postfix >= 3.6 and the c\bco\bom\bmp\bpa\bat\bti\bib\bbi\bil\bli\bit\bty\by_\b_l\ble\bev\bve\bel\bl
+set to 3 or higher; with Postfix <= 3.5, the default algorithm is m\bmd\bd5\b5. The
+best-practice algorithm is now s\bsh\bha\ba2\b25\b56\b6. Recent advances in hash function
+cryptanalysis have led to md5 and sha1 being deprecated in favor of sha256.
+However, as long as there are no known "second pre-image" attacks against the
+older algorithms, their use in this context, though not recommended, is still
+likely safe.
+
Example: fingerprint TLS security with an internal mailhub. Two matching
fingerprints are listed. The relayhost may be multiple physical hosts behind a
load-balancer, each with its own private/public key and self-signed
relayhost = [mailhub.example.com]
smtp_tls_security_level = fingerprint
- smtp_tls_fingerprint_digest = md5
+ smtp_tls_fingerprint_digest = sha256
smtp_tls_fingerprint_cert_match =
- 3D:95:34:51:24:66:33:B9:D2:40:99:C0:C1:17:0B:D1
- EC:3B:2D:B0:5B:B1:FB:6D:20:A3:9D:72:F6:8D:12:35
+ 51:e9:af:2e:1e:40:1f:de:64:...:30:35:2d:09:16:31:5a:eb:82:76
+ b6:b4:72:34:e2:59:cd:fb:c2:...:63:0d:4d:cc:2c:7d:84:de:e6:2f
Example: Certificate fingerprint verification with selected destinations. As in
the example above, we show two matching fingerprints:
/etc/postfix/main.cf:
smtp_tls_policy_maps = hash:/etc/postfix/tls_policy
- smtp_tls_fingerprint_digest = md5
+ smtp_tls_fingerprint_digest = sha256
/etc/postfix/tls_policy:
example.com fingerprint
- match=3D:95:34:51:24:66:33:B9:D2:40:99:C0:C1:17:0B:D1
- match=EC:3B:2D:B0:5B:B1:FB:6D:20:A3:9D:72:F6:8D:12:35
+ match=51:e9:af:2e:1e:40:1f:de:...:35:2d:09:16:31:5a:eb:82:76
+ match=b6:b4:72:34:e2:59:cd:fb:...:0d:4d:cc:2c:7d:84:de:e6:2f
To extract the public key fingerprint from an X.509 certificate, you need to
extract the public key from the certificate and compute the appropriate digest
another OpenSSL command that converts the key to DER and then to the "dgst"
command to compute the fingerprint.
-The actual command to transform the key to DER format depends on the version of
-OpenSSL used. With OpenSSL 1.0.0 and later, the "pkey" command supports all key
-types. With OpenSSL 0.9.8 and earlier, the key type is always RSA (nobody uses
-DSA, and EC keys are not fully supported by 0.9.8), so the "rsa" command is
-used.
+Example:
- # OpenSSL 1.0 with all certificates and SHA-1 fingerprints.
$ openssl x509 -in cert.pem -noout -pubkey |
openssl pkey -pubin -outform DER |
- openssl dgst -sha1 -c
- (stdin)= 64:3f:1f:f6:e5:1e:d4:2a:56:8b:fc:09:1a:61:98:b5:bc:7c:60:58
-
- # OpenSSL 0.9.8 with RSA certificates and MD5 fingerprints.
- $ openssl x509 -in cert.pem -noout -pubkey |
- openssl rsa -pubin -outform DER |
- openssl dgst -md5 -c
- (stdin)= f4:62:60:f6:12:8f:d5:8d:28:4d:13:a7:db:b2:ff:50
-
-Note: Postfix 2.9.0-2.9.5 computed the public key fingerprint incorrectly. To
-use public-key fingerprints, upgrade to Postfix 2.9.6 or later.
+ openssl dgst -sha256 -c
+ (stdin)= 64:3f:1f:f6:e5:1e:d4:2a:56:...:09:1a:61:98:b5:bc:7c:60:58
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
/etc/postfix/main.cf:
smtp_tls_policy_maps = hash:/etc/postfix/tls_policy
# Postfix 2.5 and later
- smtp_tls_fingerprint_digest = md5
+ smtp_tls_fingerprint_digest = sha256
/etc/postfix/tls_policy:
example.edu none
example.mil may
[mail.example.org]:587 secure match=nexthop
# Postfix 2.5 and later
[thumb.example.org] fingerprint
- match=EC:3B:2D:B0:5B:B1:FB:6D:20:A3:9D:72:F6:8D:12:35
- match=3D:95:34:51:24:66:33:B9:D2:40:99:C0:C1:17:0B:D1
+ match=b6:b4:72:34:e2:59:cd:fb:...:0d:4d:cc:2c:7d:84:de:e6:2f
+ match=51:e9:af:2e:1e:40:1f:de:...:35:2d:09:16:31:5a:eb:82:76
# Postfix 2.6 and later
example.info may protocols=!SSLv2 ciphers=medium
exclude=3DES
the software under the license of their choice. Those who are more
comfortable with the IPL can continue with that license.
+Incompatible change with snapshot 20200705
+==========================================
+
+The minimum OpenSSL version is 1.1.1, which will reach the end
+of life by 20203-09-11.
+
+The default digest has changed from md5 to sha256 (Postfix 3.6 with
+compatibility_level >= 3). With a lower compatibility_level setting,
+Postfix defaults to using md5, and logs a warning when a Postfix
+configuration specifies no explicit digest type.
+
+Export-grade Diffie-Hellman key exchange is no longer supported,
+and the tlsproxy_tls_dh512_param_file parameter is ignored,
+
Incompatible change with snapshot 20200531
==========================================
Wish list:
- Move the tls_dane_avail() and DANE-requested test into
- tls_client_start().
+ Update makedefs and sys-defs.h for current Linux kernels and
+ *BSD releases.
+
+ When deleting a recipient with a milter, delete the recipient from
+ the duplicate filter, so that the recipient can be added back.
DNS wrapper class, like XSASL, to support different stub
resolvers without contaminating Postfix programs with the
#
# The level below is what should be used with new (not upgrade) installs.
#
-compatibility_level = 2
+compatibility_level = 3
# SOFT BOUNCE
#
<li> <p> <a href="#smtputf8_enable"> Using backwards-compatible
default setting smtputf8_enable=no</a> </p>
+<li> <p> <a href="#smtpd_digest"> Using backwards-compatible
+default setting smtpd_tls_fingerprint_digest=md5</a> </p>
+
+<li> <p> <a href="#smtp_digest"> Using backwards-compatible
+default setting smtp_tls_fingerprint_digest=md5</a> </p>
+
+<li> <p> <a href="#smtp_digest"> Using backwards-compatible
+default setting lmtp_tls_fingerprint_digest=md5</a> </p>
+
</ul>
<p> If such a message is logged in the context of a legitimate
<h2> <a name="smtputf8_enable"> Using backwards-compatible default
setting smtputf8_enable=no</a> </h2>
-<p> The <a href="postconf.5.html#smtputf8_enable">smtputf8_enable</a> default value has changed from "no" to "yes.
+<p> The <a href="postconf.5.html#smtputf8_enable">smtputf8_enable</a> default value has changed from "no" to "yes".
With the new "yes" setting, the Postfix SMTP server rejects non-ASCII
addresses from clients that don't request SMTPUTF8 support, after
Postfix is updated from an older version. The backwards-compatibility
</pre>
</blockquote>
+<h2> <a name="smtpd_digest"> Using backwards-compatible
+default setting smtpd_tls_fingerprint_digest=md5</a> </h2>
+
+<p> The <a href="postconf.5.html#smtpd_tls_fingerprint_digest">smtpd_tls_fingerprint_digest</a> default value has changed from
+"md5" to "sha256". With the new "sha256" setting, the Postfix SMTP
+server avoids using the deprecated "md5" algorithm and computes a more
+secure digest of the client certificate. </p>
+
+<p> If you're using the default "md5" setting, or even an explicit
+"sha1" (also deprecated) setting, you should consider switching to
+"sha256". This will require updating any associated lookup table keys
+with the "sha256" digests of the expected client certificate or public
+key. </p>
+
+<p> As long as the <a href="postconf.5.html#smtpd_tls_fingerprint_digest">smtpd_tls_fingerprint_digest</a> parameter is left at its
+implicit default value, and the <a href="postconf.5.html#compatibility_level">compatibility_level</a> setting is less than
+3, Postfix logs a warning each time a client certificate or public key
+fingerprint is (potentially) used for access control: </p>
+
+<blockquote>
+<pre>
+postfix/smtpd[27560]: using backwards-compatible default setting
+ <a href="postconf.5.html#smtpd_tls_fingerprint_digest">smtpd_tls_fingerprint_digest</a>=md5 to compute certificate fingerprints
+</pre>
+</blockquote>
+
+<p> Since any client certificate fingerprints are passed in policy service
+lookups, and Postfix doesn't know whether the fingerprint will be used, the
+warning may also be logged when policy lookups are performed for connections
+that used a client certificate, even if the policy service does not in fact
+examine the client certificate. To reduce the noise somewhat, such warnings
+are issued at most once per <a href="smtpd.8.html">smtpd(8)</a> process instance. </p>
+
+<p> If you prefer to stick with "md5", you can suppress the warnings by
+making that setting explicit. After addressing any other compatibility
+warnings, you can <a href="#turnoff">update</a> your compatibility level.
+</p>
+
+<blockquote>
+<pre>
+# <b>postconf <a href="postconf.5.html#smtpd_tls_fingerprint_digest">smtpd_tls_fingerprint_digest</a>=md5</b>
+# <b>postfix reload</b>
+</pre>
+</blockquote>
+
+<h2> <a name="smtp_digest"> Using backwards-compatible
+default setting smtp_tls_fingerprint_digest=md5</a> </h2>
+
+<p> The <a href="postconf.5.html#smtp_tls_fingerprint_digest">smtp_tls_fingerprint_digest</a> and <a href="postconf.5.html#lmtp_tls_fingerprint_digest">lmtp_tls_fingerprint_digest</a>
+default values have changed from "md5" to "sha256". With the new
+"sha256" setting, the Postfix SMTP and LMTP client avoids using the
+deprecated "md5" algorithm and computes a more secure digest of the
+server certificate. </p>
+
+<p> If you're using the default "md5" setting, or even an explicit
+"sha1" (also deprecated) setting, you should consider switching to
+"sha256". This will require updating any "fingerprint" security level
+policies in the TLS policy table to specify matching "sha256" digests of
+the expected server certificates or public keys. </p>
+
+<p> As long as the <a href="postconf.5.html#smtp_tls_fingerprint_digest">smtp_tls_fingerprint_digest</a> (or LMTP equivalent)
+parameter is left at its implicit default value, and the
+<a href="postconf.5.html#compatibility_level">compatibility_level</a> setting is less than 3, Postfix logs a warning each
+time the "fingerprint" security level is used to specify matching "md5"
+digests of trusted server certificates or public keys: </p>
+
+<blockquote>
+<pre>
+postfix/smtp[27560]: using backwards-compatible default setting
+ <a href="postconf.5.html#smtp_tls_fingerprint_digest">smtp_tls_fingerprint_digest</a>=md5 to compute certificate fingerprints
+</pre>
+</blockquote>
+
+<p> If you prefer to stick with "md5", you can suppress the warnings by
+making that setting explicit. After addressing any other compatibility
+warnings, you can <a href="#turnoff">update</a> your compatibility level.
+</p>
+
+<blockquote>
+<pre>
+# <b>postconf '<a href="postconf.5.html#smtp_tls_fingerprint_digest">smtp_tls_fingerprint_digest</a> = md5' \
+ '<a href="postconf.5.html#lmtp_tls_fingerprint_digest">lmtp_tls_fingerprint_digest</a> = md5' </b>
+# <b>postfix reload</b>
+</pre>
+</blockquote>
+
<h2> <a name="turnoff">Turning off the backwards-compatibility safety net</a> </h2>
<p> Backwards compatibility is turned off by updating the
<p> The digest algorithm used to compute the client certificate
fingerprints is specified with the <a href="postconf.5.html">main.cf</a> <a href="postconf.5.html#smtpd_tls_fingerprint_digest">smtpd_tls_fingerprint_digest</a>
-parameter. The default is "md5", for compatibility with Postfix
-versions < 2.5. </p>
+parameter. The default algorithm is <b>sha256</b> with Postfix ≥
+3.6 and the <b><a href="postconf.5.html#compatibility_level">compatibility_level</a></b> set to 3 or higher. With
+Postfix ≤ 3.5, the default algorithm is <b>md5</b>. The
+best-practice algorithm is now <b>sha256</b>. Recent advances in hash
+function cryptanalysis have led to md5 and sha1 being deprecated in
+favor of sha256. However, as long as there are no known "second
+pre-image" attacks against the older algorithms, their use in this
+context, though not recommended, is still likely safe. </p>
<p> The <a href="postconf.5.html#permit_tls_all_clientcerts">permit_tls_all_clientcerts</a> feature must be used with caution,
because it can result in too many access permissions. Use this
command that converts the key to DER and then to the "dgst" command
to compute the fingerprint. </p>
-<p> The actual command to transform the key to DER format depends
-on the version of OpenSSL used. With OpenSSL 1.0.0 and later, the
-"pkey" command supports all key types. With OpenSSL 0.9.8 and
-earlier, the key type is always RSA (nobody uses DSA, and EC
-keys are not fully supported by 0.9.8), so the "rsa" command is
-used. </p>
+<p> Example: </p>
<blockquote>
<pre>
-# OpenSSL 1.0 with all certificates and SHA-1 fingerprints.
$ openssl x509 -in cert.pem -noout -pubkey |
openssl pkey -pubin -outform DER |
- openssl dgst -sha1 -c
-(stdin)= 64:3f:1f:f6:e5:1e:d4:2a:56:8b:fc:09:1a:61:98:b5:bc:7c:60:58
-
-# OpenSSL 0.9.8 with RSA certificates and MD5 fingerprints.
-$ openssl x509 -in cert.pem -noout -pubkey |
- openssl rsa -pubin -outform DER |
- openssl dgst -md5 -c
-(stdin)= f4:62:60:f6:12:8f:d5:8d:28:4d:13:a7:db:b2:ff:50
+ openssl dgst -sha256 -c
+(stdin)= 64:3f:1f:f6:e5:1e:d4:2a:...:8b:fc:09:1a:61:98:b5:bc:7c:60:58
</pre>
</blockquote>
-<p> Note: Postfix 2.9.0–2.9.5 computed the public key
-fingerprint incorrectly. To use public-key fingerprints, upgrade
-to Postfix 2.9.6 or later. </p>
<h3><a name="server_cipher">Server-side cipher controls</a> </h3>
delimiter as it occurs between each pair of fingerprint (hexadecimal)
digits. </p>
+<p> The default algorithm is <b>sha256</b> with Postfix ≥ 3.6
+and the <b><a href="postconf.5.html#compatibility_level">compatibility_level</a></b> set to 3 or higher; with Postfix
+≤ 3.5, the default algorithm is <b>md5</b>. The
+best-practice algorithm is now <b>sha256</b>. Recent advances in hash
+function cryptanalysis have led to md5 and sha1 being deprecated in
+favor of sha256. However, as long as there are no known "second
+pre-image" attacks against the older algorithms, their use in this
+context, though not recommended, is still likely safe. </p>
+
<p> Example: fingerprint TLS security with an internal mailhub.
Two matching fingerprints are listed. The <a href="postconf.5.html#relayhost">relayhost</a> may be multiple
physical hosts behind a load-balancer, each with its own private/public
<pre>
<a href="postconf.5.html#relayhost">relayhost</a> = [mailhub.example.com]
<a href="postconf.5.html#smtp_tls_security_level">smtp_tls_security_level</a> = fingerprint
- <a href="postconf.5.html#smtp_tls_fingerprint_digest">smtp_tls_fingerprint_digest</a> = md5
+ <a href="postconf.5.html#smtp_tls_fingerprint_digest">smtp_tls_fingerprint_digest</a> = sha256
<a href="postconf.5.html#smtp_tls_fingerprint_cert_match">smtp_tls_fingerprint_cert_match</a> =
- 3D:95:34:51:24:66:33:B9:D2:40:99:C0:C1:17:0B:D1
- EC:3B:2D:B0:5B:B1:FB:6D:20:A3:9D:72:F6:8D:12:35
+ 51:e9:af:2e:1e:40:1f:de:64:...:30:35:2d:09:16:31:5a:eb:82:76
+ b6:b4:72:34:e2:59:cd:fb:c2:...:63:0d:4d:cc:2c:7d:84:de:e6:2f
</pre>
</blockquote>
<pre>
/etc/postfix/<a href="postconf.5.html">main.cf</a>:
<a href="postconf.5.html#smtp_tls_policy_maps">smtp_tls_policy_maps</a> = <a href="DATABASE_README.html#types">hash</a>:/etc/postfix/tls_policy
- <a href="postconf.5.html#smtp_tls_fingerprint_digest">smtp_tls_fingerprint_digest</a> = md5
+ <a href="postconf.5.html#smtp_tls_fingerprint_digest">smtp_tls_fingerprint_digest</a> = sha256
</pre>
</blockquote>
<blockquote>
<pre>
/etc/postfix/tls_policy:
example.com fingerprint
- match=3D:95:34:51:24:66:33:B9:D2:40:99:C0:C1:17:0B:D1
- match=EC:3B:2D:B0:5B:B1:FB:6D:20:A3:9D:72:F6:8D:12:35
+ match=51:e9:af:2e:1e:40:1f:de:...:35:2d:09:16:31:5a:eb:82:76
+ match=b6:b4:72:34:e2:59:cd:fb:...:0d:4d:cc:2c:7d:84:de:e6:2f
</pre>
</blockquote>
command that converts the key to DER and then to the "dgst" command
to compute the fingerprint. </p>
-<p> The actual command to transform the key to DER format depends
-on the version of OpenSSL used. With OpenSSL 1.0.0 and later, the
-"pkey" command supports all key types. With OpenSSL 0.9.8 and
-earlier, the key type is always RSA (nobody uses DSA, and EC
-keys are not fully supported by 0.9.8), so the "rsa" command is
-used. </p>
+<p> Example: </p>
<blockquote>
<pre>
-# OpenSSL 1.0 with all certificates and SHA-1 fingerprints.
$ openssl x509 -in cert.pem -noout -pubkey |
openssl pkey -pubin -outform DER |
- openssl dgst -sha1 -c
-(stdin)= 64:3f:1f:f6:e5:1e:d4:2a:56:8b:fc:09:1a:61:98:b5:bc:7c:60:58
-
-# OpenSSL 0.9.8 with RSA certificates and MD5 fingerprints.
-$ openssl x509 -in cert.pem -noout -pubkey |
- openssl rsa -pubin -outform DER |
- openssl dgst -md5 -c
-(stdin)= f4:62:60:f6:12:8f:d5:8d:28:4d:13:a7:db:b2:ff:50
+ openssl dgst -sha256 -c
+(stdin)= 64:3f:1f:f6:e5:1e:d4:2a:56:...:09:1a:61:98:b5:bc:7c:60:58
</pre>
</blockquote>
-<p> Note: Postfix 2.9.0–2.9.5 computed the public key
-fingerprint incorrectly. To use public-key fingerprints, upgrade
-to Postfix 2.9.6 or later. </p>
<h4><a name="client_tls_verify"> Mandatory server certificate verification </a> </h4>
/etc/postfix/<a href="postconf.5.html">main.cf</a>:
<a href="postconf.5.html#smtp_tls_policy_maps">smtp_tls_policy_maps</a> = <a href="DATABASE_README.html#types">hash</a>:/etc/postfix/tls_policy
# Postfix 2.5 and later
- <a href="postconf.5.html#smtp_tls_fingerprint_digest">smtp_tls_fingerprint_digest</a> = md5
+ <a href="postconf.5.html#smtp_tls_fingerprint_digest">smtp_tls_fingerprint_digest</a> = sha256
/etc/postfix/tls_policy:
example.edu none
example.mil may
[mail.example.org]:587 secure match=nexthop
# Postfix 2.5 and later
[thumb.example.org] fingerprint
- match=EC:3B:2D:B0:5B:B1:FB:6D:20:A3:9D:72:F6:8D:12:35
- match=3D:95:34:51:24:66:33:B9:D2:40:99:C0:C1:17:0B:D1
+ match=b6:b4:72:34:e2:59:cd:fb:...:0d:4d:cc:2c:7d:84:de:e6:2f
+ match=51:e9:af:2e:1e:40:1f:de:...:35:2d:09:16:31:5a:eb:82:76
# Postfix 2.6 and later
example.info may protocols=!SSLv2 ciphers=medium exclude=3DES
</pre>
for the "fingerprint" TLS security level (<b><a href="postconf.5.html#smtp_tls_security_level">smtp_tls_secu</a>-</b>
<b><a href="postconf.5.html#smtp_tls_security_level">rity_level</a></b> = fingerprint).
- <b><a href="postconf.5.html#smtp_tls_fingerprint_digest">smtp_tls_fingerprint_digest</a> (md5)</b>
+ <b><a href="postconf.5.html#smtp_tls_fingerprint_digest">smtp_tls_fingerprint_digest</a> (see 'postconf -d' output)</b>
The message digest algorithm used to construct remote SMTP
server certificate fingerprints.
<b>TROUBLE SHOOTING CONTROLS</b>
<b><a href="postconf.5.html#debug_peer_level">debug_peer_level</a> (2)</b>
- The increment in verbose logging level when a next-hop destina-
+ The increment in verbose logging level when a nexthop destina-
tion, remote client or server name or network address matches a
pattern given with the <a href="postconf.5.html#debug_peer_list">debug_peer_list</a> parameter.
<b><a href="postconf.5.html#debug_peer_list">debug_peer_list</a> (empty)</b>
- Optional list of next-hop destination, remote client or server
+ Optional list of nexthop destination, remote client or server
name or network address patterns that, if matched, cause the
verbose logging level to increase by the amount specified in
$<a href="postconf.5.html#debug_peer_level">debug_peer_level</a>.
ery attempt will be made until the mail is taken off
hold.
- This mode of operation is implemented by executing the
+ <b>#</b> The message is forced to expire. See the <a href="postsuper.1.html"><b>postsuper</b>(1)</a>
+ options <b>-e</b> or <b>-f</b>.
+
+ This mode of operation is implemented by executing the
<a href="postqueue.1.html"><b>postqueue</b>(1)</a> command.
<b>newaliases</b>
- Initialize the alias database. If no input file is specified
- (with the <b>-oA</b> option, see below), the program processes the
- file(s) specified with the <b><a href="postconf.5.html#alias_database">alias_database</a></b> configuration parame-
- ter. If no alias database type is specified, the program uses
- the type specified with the <b><a href="postconf.5.html#default_database_type">default_database_type</a></b> configuration
+ Initialize the alias database. If no input file is specified
+ (with the <b>-oA</b> option, see below), the program processes the
+ file(s) specified with the <b><a href="postconf.5.html#alias_database">alias_database</a></b> configuration parame-
+ ter. If no alias database type is specified, the program uses
+ the type specified with the <b><a href="postconf.5.html#default_database_type">default_database_type</a></b> configuration
parameter. This mode of operation is implemented by running the
<a href="postalias.1.html"><b>postalias</b>(1)</a> command.
Note: it may take a minute or so before an alias database update
- becomes visible. Use the "<b>postfix reload</b>" command to eliminate
+ becomes visible. Use the "<b>postfix reload</b>" command to eliminate
this delay.
- These and other features can be selected by specifying the appropriate
- combination of command-line options. Some features are controlled by
+ These and other features can be selected by specifying the appropriate
+ combination of command-line options. Some features are controlled by
parameters in the <a href="postconf.5.html"><b>main.cf</b></a> configuration file.
The following options are recognized:
<b>-Am</b> (ignored)
<b>-Ac</b> (ignored)
- Postfix sendmail uses the same configuration file regardless of
+ Postfix sendmail uses the same configuration file regardless of
whether or not a message is an initial submission.
<b>-B</b> <i>body</i><b>_</b><i>type</i>
The message body MIME type: <b>7BIT</b> or <b>8BITMIME</b>.
- <b>-bd</b> Go into daemon mode. This mode of operation is implemented by
+ <b>-bd</b> Go into daemon mode. This mode of operation is implemented by
executing the "<b>postfix start</b>" command.
<b>-bh</b> (ignored)
<b>-bi</b> Initialize alias database. See the <b>newaliases</b> command above.
- <b>-bl</b> Go into daemon mode. To accept only local connections as with
- Sendmail's <b>-bl</b> option, specify "<b><a href="postconf.5.html#inet_interfaces">inet_interfaces</a> = loopback</b>" in
+ <b>-bl</b> Go into daemon mode. To accept only local connections as with
+ Sendmail's <b>-bl</b> option, specify "<b><a href="postconf.5.html#inet_interfaces">inet_interfaces</a> = loopback</b>" in
the Postfix <a href="postconf.5.html"><b>main.cf</b></a> configuration file.
<b>-bm</b> Read mail from standard input and arrange for delivery. This is
<b>-bp</b> List the mail queue. See the <b>mailq</b> command above.
- <b>-bs</b> Stand-alone SMTP server mode. Read SMTP commands from standard
- input, and write responses to standard output. In stand-alone
- SMTP server mode, mail relaying and other access controls are
- disabled by default. To enable them, run the process as the
+ <b>-bs</b> Stand-alone SMTP server mode. Read SMTP commands from standard
+ input, and write responses to standard output. In stand-alone
+ SMTP server mode, mail relaying and other access controls are
+ disabled by default. To enable them, run the process as the
<b><a href="postconf.5.html#mail_owner">mail_owner</a></b> user.
- This mode of operation is implemented by running the <a href="smtpd.8.html"><b>smtpd</b>(8)</a>
+ This mode of operation is implemented by running the <a href="smtpd.8.html"><b>smtpd</b>(8)</a>
daemon.
- <b>-bv</b> Do not collect or deliver a message. Instead, send an email
- report after verifying each recipient address. This is useful
+ <b>-bv</b> Do not collect or deliver a message. Instead, send an email
+ report after verifying each recipient address. This is useful
for testing address rewriting and routing configurations.
This feature is available in Postfix version 2.1 and later.
<b>-C</b> <i>config</i><b>_</b><i>file</i>
<b>-C</b> <i>config</i><b>_</b><i>dir</i>
- The path name of the Postfix <a href="postconf.5.html"><b>main.cf</b></a> file, or of its parent
- directory. This information is ignored with Postfix versions
+ The path name of the Postfix <a href="postconf.5.html"><b>main.cf</b></a> file, or of its parent
+ directory. This information is ignored with Postfix versions
before 2.3.
With Postfix version 3.2 and later, a non-default directory must
- be authorized in the default <a href="postconf.5.html"><b>main.cf</b></a> file, through the alter-
+ be authorized in the default <a href="postconf.5.html"><b>main.cf</b></a> file, through the alter-
nate_config_directories or <a href="postconf.5.html#multi_instance_directories">multi_instance_directories</a> parame-
ters.
- With all Postfix versions, you can specify a directory pathname
- with the MAIL_CONFIG environment variable to override the loca-
+ With all Postfix versions, you can specify a directory pathname
+ with the MAIL_CONFIG environment variable to override the loca-
tion of configuration files.
<b>-F</b> <i>full</i><b>_</b><i>name</i>
- Set the sender full name. This overrides the NAME environment
+ Set the sender full name. This overrides the NAME environment
variable, and is used only with messages that have no <b>From:</b> mes-
sage header.
<b>-f</b> <i>sender</i>
- Set the envelope sender address. This is the address where
+ Set the envelope sender address. This is the address where
delivery problems are sent to. With Postfix versions before 2.1,
- the <b>Errors-To:</b> message header overrides the error return
+ the <b>Errors-To:</b> message header overrides the error return
address.
- <b>-G</b> Gateway (relay) submission, as opposed to initial user submis-
- sion. Either do not rewrite addresses at all, or update incom-
- plete addresses with the domain information specified with
+ <b>-G</b> Gateway (relay) submission, as opposed to initial user submis-
+ sion. Either do not rewrite addresses at all, or update incom-
+ plete addresses with the domain information specified with
<b><a href="postconf.5.html#remote_header_rewrite_domain">remote_header_rewrite_domain</a></b>.
This option is ignored before Postfix version 2.3.
<b>-h</b> <i>hop</i><b>_</b><i>count</i> (ignored)
- Hop count limit. Use the <b><a href="postconf.5.html#hopcount_limit">hopcount_limit</a></b> configuration parameter
+ Hop count limit. Use the <b><a href="postconf.5.html#hopcount_limit">hopcount_limit</a></b> configuration parameter
instead.
<b>-I</b> Initialize alias database. See the <b>newaliases</b> command above.
- <b>-i</b> When reading a message from standard input, don't treat a line
+ <b>-i</b> When reading a message from standard input, don't treat a line
with only a <b>.</b> character as the end of input.
<b>-L</b> <i>label</i> (ignored)
- The logging label. Use the <b><a href="postconf.5.html#syslog_name">syslog_name</a></b> configuration parameter
+ The logging label. Use the <b><a href="postconf.5.html#syslog_name">syslog_name</a></b> configuration parameter
instead.
<b>-m</b> (ignored)
Backwards compatibility.
<b>-N</b> <i>dsn</i> (default: 'delay, failure')
- Delivery status notification control. Specify either a
+ Delivery status notification control. Specify either a
comma-separated list with one or more of <b>failure</b> (send notifica-
- tion when delivery fails), <b>delay</b> (send notification when deliv-
- ery is delayed), or <b>success</b> (send notification when the message
+ tion when delivery fails), <b>delay</b> (send notification when deliv-
+ ery is delayed), or <b>success</b> (send notification when the message
is delivered); or specify <b>never</b> (don't send any notifications at
all).
Backwards compatibility.
<b>-oA</b><i>alias</i><b>_</b><i>database</i>
- Non-default alias database. Specify <i>pathname</i> or <i>type</i>:<i>pathname</i>.
+ Non-default alias database. Specify <i>pathname</i> or <i>type</i>:<i>pathname</i>.
See <a href="postalias.1.html"><b>postalias</b>(1)</a> for details.
<b>-O</b> <i>option=value</i> (ignored)
- Set the named <i>option</i> to <i>value</i>. Use the equivalent configuration
+ Set the named <i>option</i> to <i>value</i>. Use the equivalent configuration
parameter in <a href="postconf.5.html"><b>main.cf</b></a> instead.
<b>-o7</b> (ignored)
<b>-o8</b> (ignored)
- To send 8-bit or binary content, use an appropriate MIME encap-
+ To send 8-bit or binary content, use an appropriate MIME encap-
sulation and specify the appropriate <b>-B</b> command-line option.
- <b>-oi</b> When reading a message from standard input, don't treat a line
+ <b>-oi</b> When reading a message from standard input, don't treat a line
with only a <b>.</b> character as the end of input.
<b>-om</b> (ignored)
The sender is never eliminated from alias etc. expansions.
<b>-o</b> <i>x value</i> (ignored)
- Set option <i>x</i> to <i>value</i>. Use the equivalent configuration parame-
+ Set option <i>x</i> to <i>value</i>. Use the equivalent configuration parame-
ter in <a href="postconf.5.html"><b>main.cf</b></a> instead.
<b>-r</b> <i>sender</i>
- Set the envelope sender address. This is the address where
+ Set the envelope sender address. This is the address where
delivery problems are sent to. With Postfix versions before 2.1,
- the <b>Errors-To:</b> message header overrides the error return
+ the <b>Errors-To:</b> message header overrides the error return
address.
<b>-R</b> <i>return</i>
- Delivery status notification control. Specify "hdrs" to return
- only the header when a message bounces, "full" to return a full
+ Delivery status notification control. Specify "hdrs" to return
+ only the header when a message bounces, "full" to return a full
copy (the default behavior).
The <b>-R</b> option specifies an upper bound; Postfix will return only
- the header, when a full copy would exceed the <a href="postconf.5.html#bounce_size_limit">bounce_size_limit</a>
+ the header, when a full copy would exceed the <a href="postconf.5.html#bounce_size_limit">bounce_size_limit</a>
setting.
This option is ignored before Postfix version 2.10.
- <b>-q</b> Attempt to deliver all queued mail. This is implemented by exe-
+ <b>-q</b> Attempt to deliver all queued mail. This is implemented by exe-
cuting the <a href="postqueue.1.html"><b>postqueue</b>(1)</a> command.
- Warning: flushing undeliverable mail frequently will result in
+ Warning: flushing undeliverable mail frequently will result in
poor delivery performance of all other mail.
<b>-q</b><i>interval</i> (ignored)
<b>-qI</b><i>queueid</i>
Schedule immediate delivery of mail with the specified queue ID.
- This option is implemented by executing the <a href="postqueue.1.html"><b>postqueue</b>(1)</a> com-
+ This option is implemented by executing the <a href="postqueue.1.html"><b>postqueue</b>(1)</a> com-
mand, and is available with Postfix version 2.4 and later.
<b>-qR</b><i>site</i>
- Schedule immediate delivery of all mail that is queued for the
- named <i>site</i>. This option accepts only <i>site</i> names that are eligi-
- ble for the "fast flush" service, and is implemented by execut-
+ Schedule immediate delivery of all mail that is queued for the
+ named <i>site</i>. This option accepts only <i>site</i> names that are eligi-
+ ble for the "fast flush" service, and is implemented by execut-
ing the <a href="postqueue.1.html"><b>postqueue</b>(1)</a> command. See <a href="flush.8.html"><b>flush</b>(8)</a> for more information
about the "fast flush" service.
<b>-qS</b><i>site</i>
- This command is not implemented. Use the slower "<b>sendmail -q</b>"
+ This command is not implemented. Use the slower "<b>sendmail -q</b>"
command instead.
- <b>-t</b> Extract recipients from message headers. These are added to any
+ <b>-t</b> Extract recipients from message headers. These are added to any
recipients specified on the command line.
With Postfix versions prior to 2.1, this option requires that no
This feature is available in Postfix 2.3 and later.
<b>-XV</b> (Postfix 2.2 and earlier: <b>-V</b>)
- Variable Envelope Return Path. Given an envelope sender address
- of the form <i>owner-listname</i>@<i>origin</i>, each recipient <i>user</i>@<i>domain</i>
+ Variable Envelope Return Path. Given an envelope sender address
+ of the form <i>owner-listname</i>@<i>origin</i>, each recipient <i>user</i>@<i>domain</i>
receives mail with a personalized envelope sender address.
- By default, the personalized envelope sender address is
- <i>owner-listname</i><b>+</b><i>user</i><b>=</b><i>domain</i>@<i>origin</i>. The default <b>+</b> and <b>=</b> charac-
- ters are configurable with the <b><a href="postconf.5.html#default_verp_delimiters">default_verp_delimiters</a></b> configu-
+ By default, the personalized envelope sender address is
+ <i>owner-listname</i><b>+</b><i>user</i><b>=</b><i>domain</i>@<i>origin</i>. The default <b>+</b> and <b>=</b> charac-
+ ters are configurable with the <b><a href="postconf.5.html#default_verp_delimiters">default_verp_delimiters</a></b> configu-
ration parameter.
<b>-XV</b><i>xy</i> (Postfix 2.2 and earlier: <b>-V</b><i>xy</i>)
- As <b>-XV</b>, but uses <i>x</i> and <i>y</i> as the VERP delimiter characters,
+ As <b>-XV</b>, but uses <i>x</i> and <i>y</i> as the VERP delimiter characters,
instead of the characters specified with the <b><a href="postconf.5.html#default_verp_delimiters">default_verp_delim</a>-</b>
<b><a href="postconf.5.html#default_verp_delimiters">iters</a></b> configuration parameter.
<b>-v</b> Send an email report of the first delivery attempt (Postfix ver-
- sions 2.1 and later). Mail delivery always happens in the back-
- ground. When multiple <b>-v</b> options are given, enable verbose log-
+ sions 2.1 and later). Mail delivery always happens in the back-
+ ground. When multiple <b>-v</b> options are given, enable verbose log-
ging for debugging purposes.
<b>-X</b> <i>log</i><b>_</b><i>file</i> (ignored)
configuration parameters instead.
<b>SECURITY</b>
- By design, this program is not set-user (or group) id. However, it must
- handle data from untrusted, possibly remote, users. Thus, the usual
- precautions need to be taken against malicious inputs.
+ By design, this program is not set-user (or group) id. It is prepared
+ to handle message content from untrusted, possibly remote, users.
+
+ However, like most Postfix programs, this program does not enforce a
+ security policy on its command-line arguments. Instead, it relies on
+ the UNIX system to enforce access policies based on the effective user
+ and group IDs of the process. Concretely, this means that running Post-
+ fix commands as root (from sudo or equivalent) on behalf of a non-root
+ user is likely to create privilege escalation opportunities.
+
+ If an application runs any Postfix programs on behalf of users that do
+ not have normal shell access to Postfix commands, then that application
+ MUST restrict user-specified command-line arguments to avoid privilege
+ escalation.
+
+ <b>o</b> Filter all command-line arguments, for example arguments that
+ contain a pathname or that specify a database access method.
+ These pathname checks must reject user-controlled symlinks or
+ hardlinks to sensitive files, and must not be vulnerable to TOC-
+ TOU race attacks.
+
+ <b>o</b> Disable command options processing for all command arguments
+ that contain user-specified data. For example, the Postfix <a href="sendmail.1.html"><b>send-</b></a>
+ <a href="sendmail.1.html"><b>mail</b>(1)</a> command line MUST be structured as follows:
+
+ <b>/path/to/sendmail</b> <i>system-arguments</i> <b>--</b> <i>user-arguments</i>
+
+ Here, the "<b>--</b>" disables command option processing for all
+ <i>user-arguments</i> that follow.
+
+ Without the "<b>--</b>", a malicious user could enable Postfix <a href="sendmail.1.html"><b>send-</b></a>
+ <a href="sendmail.1.html"><b>mail</b>(1)</a> command options, by specifying an email address that
+ starts with "<b>-</b>".
<b>DIAGNOSTICS</b>
- Problems are logged to <b>syslogd</b>(8) or <a href="postlogd.8.html"><b>postlogd</b>(8)</a>, and to the standard
+ Problems are logged to <b>syslogd</b>(8) or <a href="postlogd.8.html"><b>postlogd</b>(8)</a>, and to the standard
error stream.
<b>ENVIRONMENT</b>
Enable debugging with an external command, as specified with the
<b><a href="postconf.5.html#debugger_command">debugger_command</a></b> configuration parameter.
- <b>NAME</b> The sender full name. This is used only with messages that have
+ <b>NAME</b> The sender full name. This is used only with messages that have
no <b>From:</b> message header. See also the <b>-F</b> option above.
<b>CONFIGURATION PARAMETERS</b>
- The following <a href="postconf.5.html"><b>main.cf</b></a> parameters are especially relevant to this pro-
- gram. The text below provides only a parameter summary. See <a href="postconf.5.html"><b>post-</b></a>
+ The following <a href="postconf.5.html"><b>main.cf</b></a> parameters are especially relevant to this pro-
+ gram. The text below provides only a parameter summary. See <a href="postconf.5.html"><b>post-</b></a>
<a href="postconf.5.html"><b>conf</b>(5)</a> for more details including examples.
<b>COMPATIBILITY CONTROLS</b>
line endings from <CR><LF> into UNIX format (<LF>).
<b>TROUBLE SHOOTING CONTROLS</b>
- The <a href="DEBUG_README.html">DEBUG_README</a> file gives examples of how to troubleshoot a Postfix
+ The <a href="DEBUG_README.html">DEBUG_README</a> file gives examples of how to troubleshoot a Postfix
system.
<b><a href="postconf.5.html#debugger_command">debugger_command</a> (empty)</b>
invoked with the -D option.
<b><a href="postconf.5.html#debug_peer_level">debug_peer_level</a> (2)</b>
- The increment in verbose logging level when a remote client or
- server matches a pattern in the <a href="postconf.5.html#debug_peer_list">debug_peer_list</a> parameter.
+ The increment in verbose logging level when a nexthop destina-
+ tion, remote client or server name or network address matches a
+ pattern given with the <a href="postconf.5.html#debug_peer_list">debug_peer_list</a> parameter.
<b><a href="postconf.5.html#debug_peer_list">debug_peer_list</a> (empty)</b>
- Optional list of remote client or server hostname or network
- address patterns that cause the verbose logging level to
- increase by the amount specified in $<a href="postconf.5.html#debug_peer_level">debug_peer_level</a>.
+ Optional list of nexthop destination, remote client or server
+ name or network address patterns that, if matched, cause the
+ verbose logging level to increase by the amount specified in
+ $<a href="postconf.5.html#debug_peer_level">debug_peer_level</a>.
<b>ACCESS CONTROLS</b>
Available in Postfix version 2.2 and later:
List of users who are authorized to view the queue.
<b><a href="postconf.5.html#authorized_submit_users">authorized_submit_users</a> (<a href="DATABASE_README.html#types">static</a>:anyone)</b>
- List of users who are authorized to submit mail with the <a href="sendmail.1.html"><b>send-</b></a>
+ List of users who are authorized to submit mail with the <a href="sendmail.1.html"><b>send-</b></a>
<a href="sendmail.1.html"><b>mail</b>(1)</a> command (and with the privileged <a href="postdrop.1.html"><b>postdrop</b>(1)</a> helper com-
mand).
<b>RESOURCE AND RATE CONTROLS</b>
<b><a href="postconf.5.html#bounce_size_limit">bounce_size_limit</a> (50000)</b>
- The maximal amount of original message text that is sent in a
+ The maximal amount of original message text that is sent in a
non-delivery notification.
<b><a href="postconf.5.html#fork_attempts">fork_attempts</a> (5)</b>
in the primary message headers.
<b><a href="postconf.5.html#queue_run_delay">queue_run_delay</a> (300s)</b>
- The time between <a href="QSHAPE_README.html#deferred_queue">deferred queue</a> scans by the queue manager;
+ The time between <a href="QSHAPE_README.html#deferred_queue">deferred queue</a> scans by the queue manager;
prior to Postfix 2.4 the default value was 1000s.
<b>FAST FLUSH CONTROLS</b>
- The <a href="ETRN_README.html">ETRN_README</a> file describes configuration and operation details for
+ The <a href="ETRN_README.html">ETRN_README</a> file describes configuration and operation details for
the Postfix "fast flush" service.
<b><a href="postconf.5.html#fast_flush_domains">fast_flush_domains</a> ($<a href="postconf.5.html#relay_domains">relay_domains</a>)</b>
tion logfiles with mail that is queued to those destinations.
<b>VERP CONTROLS</b>
- The <a href="VERP_README.html">VERP_README</a> file describes configuration and operation details of
+ The <a href="VERP_README.html">VERP_README</a> file describes configuration and operation details of
Postfix support for variable envelope return path addresses.
<b><a href="postconf.5.html#default_verp_delimiters">default_verp_delimiters</a> (+=)</b>
The two default VERP delimiter characters.
<b><a href="postconf.5.html#verp_delimiter_filter">verp_delimiter_filter</a> (-=+)</b>
- The characters Postfix accepts as VERP delimiter characters on
+ The characters Postfix accepts as VERP delimiter characters on
the Postfix <a href="sendmail.1.html"><b>sendmail</b>(1)</a> command line and in SMTP commands.
<b>MISCELLANEOUS CONTROLS</b>
<b><a href="postconf.5.html#alias_database">alias_database</a> (see 'postconf -d' output)</b>
- The alias databases for <a href="local.8.html"><b>local</b>(8)</a> delivery that are updated with
+ The alias databases for <a href="local.8.html"><b>local</b>(8)</a> delivery that are updated with
"<b>newaliases</b>" or with "<b>sendmail -bi</b>".
<b><a href="postconf.5.html#command_directory">command_directory</a> (see 'postconf -d' output)</b>
The location of all postfix administrative commands.
<b><a href="postconf.5.html#config_directory">config_directory</a> (see 'postconf -d' output)</b>
- The default location of the Postfix <a href="postconf.5.html">main.cf</a> and <a href="master.5.html">master.cf</a> con-
+ The default location of the Postfix <a href="postconf.5.html">main.cf</a> and <a href="master.5.html">master.cf</a> con-
figuration files.
<b><a href="postconf.5.html#daemon_directory">daemon_directory</a> (see 'postconf -d' output)</b>
and <a href="postmap.1.html"><b>postmap</b>(1)</a> commands.
<b><a href="postconf.5.html#delay_warning_time">delay_warning_time</a> (0h)</b>
- The time after which the sender receives a copy of the message
+ The time after which the sender receives a copy of the message
headers of mail that is still queued.
<b><a href="postconf.5.html#import_environment">import_environment</a> (see 'postconf -d' output)</b>
- The list of environment parameters that a privileged Postfix
- process will import from a non-Postfix parent process, or
+ The list of environment parameters that a privileged Postfix
+ process will import from a non-Postfix parent process, or
name=value environment overrides.
<b><a href="postconf.5.html#mail_owner">mail_owner</a> (postfix)</b>
- The UNIX system account that owns the Postfix queue and most
+ The UNIX system account that owns the Postfix queue and most
Postfix daemon processes.
<b><a href="postconf.5.html#queue_directory">queue_directory</a> (see 'postconf -d' output)</b>
The location of the Postfix top-level queue directory.
<b><a href="postconf.5.html#remote_header_rewrite_domain">remote_header_rewrite_domain</a> (empty)</b>
- Don't rewrite message headers from remote clients at all when
- this parameter is empty; otherwise, rewrite message headers and
+ Don't rewrite message headers from remote clients at all when
+ this parameter is empty; otherwise, rewrite message headers and
append the specified domain name to incomplete addresses.
<b><a href="postconf.5.html#syslog_facility">syslog_facility</a> (mail)</b>
The syslog facility of Postfix logging.
<b><a href="postconf.5.html#syslog_name">syslog_name</a> (see 'postconf -d' output)</b>
- A prefix that is prepended to the process name in syslog
+ A prefix that is prepended to the process name in syslog
records, so that, for example, "smtpd" becomes "prefix/smtpd".
Postfix 3.2 and later:
<b><a href="postconf.5.html#alternate_config_directories">alternate_config_directories</a> (empty)</b>
A list of non-default Postfix configuration directories that may
- be specified with "-c <a href="postconf.5.html#config_directory">config_directory</a>" on the command line (in
- the case of <a href="sendmail.1.html"><b>sendmail</b>(1)</a>, with the "-C" option), or via the
+ be specified with "-c <a href="postconf.5.html#config_directory">config_directory</a>" on the command line (in
+ the case of <a href="sendmail.1.html"><b>sendmail</b>(1)</a>, with the "-C" option), or via the
MAIL_CONFIG environment parameter.
<b><a href="postconf.5.html#multi_instance_directories">multi_instance_directories</a> (empty)</b>
- An optional list of non-default Postfix configuration directo-
- ries; these directories belong to additional Postfix instances
- that share the Postfix executable files and documentation with
- the default Postfix instance, and that are started, stopped,
+ An optional list of non-default Postfix configuration directo-
+ ries; these directories belong to additional Postfix instances
+ that share the Postfix executable files and documentation with
+ the default Postfix instance, and that are started, stopped,
etc., together with the default Postfix instance.
<b>FILES</b>
syslogd(8), system logging
<b>README_FILES</b>
- Use "<b>postconf <a href="postconf.5.html#readme_directory">readme_directory</a></b>" or "<b>postconf <a href="postconf.5.html#html_directory">html_directory</a></b>" to locate
+ Use "<b>postconf <a href="postconf.5.html#readme_directory">readme_directory</a></b>" or "<b>postconf <a href="postconf.5.html#html_directory">html_directory</a></b>" to locate
this information.
<a href="DEBUG_README.html">DEBUG_README</a>, Postfix debugging howto
<a href="ETRN_README.html">ETRN_README</a>, Postfix ETRN howto
<meta http-equiv="Content-Type" content="text/html; charset=us-ascii">
<title> Postfix manual - makedefs(1) </title>
</head> <body> <pre>
-MAKEDEFS(1) MAKEDEFS(1)
+MAKEDEFS(1) General Commands Manual MAKEDEFS(1)
<b>NAME</b>
makedefs - Postfix makefile configuration utility
Disable support for POSIX getpwnam_r/getpwuid_r.
<b>-DNO_RES_NCALLS</b>
- Do not build with the threadsafe resolver(5) API
+ Do not build with the threadsafe resolver(5) API
(res_ninit() etc.).
<b>-DNO_SIGSETJMP</b>
- Use setjmp()/longjmp() instead of sigsetjmp()/sig-
- longjmp(). By default, Postfix uses sigsetjmp()/sig-
+ Use setjmp()/longjmp() instead of sigsetjmp()/sig-
+ longjmp(). By default, Postfix uses sigsetjmp()/sig-
longjmp() when they appear to work.
<b>-DNO_SNPRINTF</b>
- Use sprintf() instead of snprintf(). By default, Postfix
+ Use sprintf() instead of snprintf(). By default, Postfix
uses snprintf() except on ancient systems.
<b>DEBUG=</b><i>debug</i><b>_</b><i>level</i>
- Specifies a non-default debugging level. The default is <b>-g</b>.
+ Specifies a non-default debugging level. The default is <b>-g</b>.
Specify <b>DEBUG=</b> to turn off debugging.
<b>OPT=</b><i>optimization</i><b>_</b><i>level</i>
- Specifies a non-default optimization level. The default is <b>-O</b>.
+ Specifies a non-default optimization level. The default is <b>-O</b>.
Specify <b>OPT=</b> to turn off optimization.
<b>POSTFIX_INSTALL_OPTS=</b><i>-option...</i>
- Specifies options for the postfix-install command, separated by
- whitespace. Currently, the only supported option is
+ Specifies options for the postfix-install command, separated by
+ whitespace. Currently, the only supported option is
<b>-keep-build-mtime</b>.
<b>SHLIB_CFLAGS=</b><i>flags</i>
- Override the compiler flags (typically, "-fPIC") for Postfix
+ Override the compiler flags (typically, "-fPIC") for Postfix
dynamically-linked libraries and database plugins.
This feature was introduced with Postfix 3.0.
<b>SHLIB_RPATH=</b><i>rpath</i>
- Override the runpath (typically, "'-Wl,-rpath,${SHLIB_DIR}'")
+ Override the runpath (typically, "'-Wl,-rpath,${SHLIB_DIR}'")
for Postfix dynamically-linked libraries.
This feature was introduced with Postfix 3.0.
<b>SHLIB_SUFFIX=</b><i>suffix</i>
- Override the filename suffix (typically, ".so") for Postfix
+ Override the filename suffix (typically, ".so") for Postfix
dynamically-linked libraries and database plugins.
This feature was introduced with Postfix 3.0.
<b>shared=yes</b>
<b>shared=no</b>
- Enable (disable) Postfix builds with dynamically-linked
+ Enable (disable) Postfix builds with dynamically-linked
libraries typically named $<a href="postconf.5.html#shlib_directory">shlib_directory</a>/libpostfix-*.so.*.
This feature was introduced with Postfix 3.0.
<b>dynamicmaps=yes</b>
<b>dynamicmaps=no</b>
- Enable (disable) Postfix builds with the configuration file
+ Enable (disable) Postfix builds with the configuration file
$<a href="postconf.5.html#meta_directory">meta_directory</a>/dynamicmaps.cf and dynamically-loadable database
- plugins typically named postfix-*.so.*. The setting "dynam-
- icmaps=yes" implicitly enables Postfix dynamically-linked
+ plugins typically named postfix-*.so.*. The setting "dynam-
+ icmaps=yes" implicitly enables Postfix dynamically-linked
libraries.
This feature was introduced with Postfix 3.0.
<b>pie=yes</b>
- <b>pie=no</b> Enable (disable) Postfix builds with position-independent exe-
+ <b>pie=no</b> Enable (disable) Postfix builds with position-independent exe-
cutables, on platforms where this is supported.
This feature was introduced with Postfix 3.0.
<i>installation</i><b>_</b><i>parameter</i><b>=</b><i>value</i>...
- Override the compiled-in default value of the specified instal-
- lation parameter(s). The following parameters are supported in
+ Override the compiled-in default value of the specified instal-
+ lation parameter(s). The following parameters are supported in
this context:
- <a href="postconf.5.html#command_directory">command_directory</a> <a href="postconf.5.html#config_directory">config_directory</a> <a href="postconf.5.html#daemon_directory">daemon_directory</a> <a href="postconf.5.html#data_directory">data_direc</a>-
- <a href="postconf.5.html#data_directory">tory</a> <a href="postconf.5.html#default_database_type">default_database_type</a> <a href="postconf.5.html#html_directory">html_directory</a> <a href="postconf.5.html#mail_spool_directory">mail_spool_directory</a>
- <a href="postconf.5.html#mailq_path">mailq_path</a> <a href="postconf.5.html#manpage_directory">manpage_directory</a> <a href="postconf.5.html#meta_directory">meta_directory</a> <a href="postconf.5.html#newaliases_path">newaliases_path</a>
- <a href="postconf.5.html#queue_directory">queue_directory</a> <a href="postconf.5.html#readme_directory">readme_directory</a> <a href="postconf.5.html#sendmail_path">sendmail_path</a> <a href="postconf.5.html#shlib_directory">shlib_directory</a>
+ <a href="postconf.5.html#command_directory">command_directory</a> <a href="postconf.5.html#config_directory">config_directory</a> <a href="postconf.5.html#daemon_directory">daemon_directory</a> <a href="postconf.5.html#data_directory">data_direc</a>-
+ <a href="postconf.5.html#data_directory">tory</a> <a href="postconf.5.html#default_database_type">default_database_type</a> <a href="postconf.5.html#html_directory">html_directory</a> <a href="postconf.5.html#mail_spool_directory">mail_spool_directory</a>
+ <a href="postconf.5.html#mailq_path">mailq_path</a> <a href="postconf.5.html#manpage_directory">manpage_directory</a> <a href="postconf.5.html#meta_directory">meta_directory</a> <a href="postconf.5.html#newaliases_path">newaliases_path</a>
+ <a href="postconf.5.html#queue_directory">queue_directory</a> <a href="postconf.5.html#readme_directory">readme_directory</a> <a href="postconf.5.html#sendmail_path">sendmail_path</a> <a href="postconf.5.html#shlib_directory">shlib_directory</a>
<a href="postconf.5.html#openssl_path">openssl_path</a>
- See the <a href="postconf.5.html">postconf(5)</a> manpage for a description of these parame-
+ See the <a href="postconf.5.html">postconf(5)</a> manpage for a description of these parame-
ters.
This feature was introduced with Postfix 3.0.
<b>WARN=</b><i>warning</i><b>_</b><i>flags</i>
- Specifies non-default gcc compiler warning options for use when
+ Specifies non-default gcc compiler warning options for use when
"make" is invoked in a source subdirectory only.
<b>LICENSE</b>
ery attempt will be made until the mail is taken off
hold.
- This mode of operation is implemented by executing the
+ <b>#</b> The message is forced to expire. See the <a href="postsuper.1.html"><b>postsuper</b>(1)</a>
+ options <b>-e</b> or <b>-f</b>.
+
+ This mode of operation is implemented by executing the
<a href="postqueue.1.html"><b>postqueue</b>(1)</a> command.
<b>newaliases</b>
- Initialize the alias database. If no input file is specified
- (with the <b>-oA</b> option, see below), the program processes the
- file(s) specified with the <b><a href="postconf.5.html#alias_database">alias_database</a></b> configuration parame-
- ter. If no alias database type is specified, the program uses
- the type specified with the <b><a href="postconf.5.html#default_database_type">default_database_type</a></b> configuration
+ Initialize the alias database. If no input file is specified
+ (with the <b>-oA</b> option, see below), the program processes the
+ file(s) specified with the <b><a href="postconf.5.html#alias_database">alias_database</a></b> configuration parame-
+ ter. If no alias database type is specified, the program uses
+ the type specified with the <b><a href="postconf.5.html#default_database_type">default_database_type</a></b> configuration
parameter. This mode of operation is implemented by running the
<a href="postalias.1.html"><b>postalias</b>(1)</a> command.
Note: it may take a minute or so before an alias database update
- becomes visible. Use the "<b>postfix reload</b>" command to eliminate
+ becomes visible. Use the "<b>postfix reload</b>" command to eliminate
this delay.
- These and other features can be selected by specifying the appropriate
- combination of command-line options. Some features are controlled by
+ These and other features can be selected by specifying the appropriate
+ combination of command-line options. Some features are controlled by
parameters in the <a href="postconf.5.html"><b>main.cf</b></a> configuration file.
The following options are recognized:
<b>-Am</b> (ignored)
<b>-Ac</b> (ignored)
- Postfix sendmail uses the same configuration file regardless of
+ Postfix sendmail uses the same configuration file regardless of
whether or not a message is an initial submission.
<b>-B</b> <i>body</i><b>_</b><i>type</i>
The message body MIME type: <b>7BIT</b> or <b>8BITMIME</b>.
- <b>-bd</b> Go into daemon mode. This mode of operation is implemented by
+ <b>-bd</b> Go into daemon mode. This mode of operation is implemented by
executing the "<b>postfix start</b>" command.
<b>-bh</b> (ignored)
<b>-bi</b> Initialize alias database. See the <b>newaliases</b> command above.
- <b>-bl</b> Go into daemon mode. To accept only local connections as with
- Sendmail's <b>-bl</b> option, specify "<b><a href="postconf.5.html#inet_interfaces">inet_interfaces</a> = loopback</b>" in
+ <b>-bl</b> Go into daemon mode. To accept only local connections as with
+ Sendmail's <b>-bl</b> option, specify "<b><a href="postconf.5.html#inet_interfaces">inet_interfaces</a> = loopback</b>" in
the Postfix <a href="postconf.5.html"><b>main.cf</b></a> configuration file.
<b>-bm</b> Read mail from standard input and arrange for delivery. This is
<b>-bp</b> List the mail queue. See the <b>mailq</b> command above.
- <b>-bs</b> Stand-alone SMTP server mode. Read SMTP commands from standard
- input, and write responses to standard output. In stand-alone
- SMTP server mode, mail relaying and other access controls are
- disabled by default. To enable them, run the process as the
+ <b>-bs</b> Stand-alone SMTP server mode. Read SMTP commands from standard
+ input, and write responses to standard output. In stand-alone
+ SMTP server mode, mail relaying and other access controls are
+ disabled by default. To enable them, run the process as the
<b><a href="postconf.5.html#mail_owner">mail_owner</a></b> user.
- This mode of operation is implemented by running the <a href="smtpd.8.html"><b>smtpd</b>(8)</a>
+ This mode of operation is implemented by running the <a href="smtpd.8.html"><b>smtpd</b>(8)</a>
daemon.
- <b>-bv</b> Do not collect or deliver a message. Instead, send an email
- report after verifying each recipient address. This is useful
+ <b>-bv</b> Do not collect or deliver a message. Instead, send an email
+ report after verifying each recipient address. This is useful
for testing address rewriting and routing configurations.
This feature is available in Postfix version 2.1 and later.
<b>-C</b> <i>config</i><b>_</b><i>file</i>
<b>-C</b> <i>config</i><b>_</b><i>dir</i>
- The path name of the Postfix <a href="postconf.5.html"><b>main.cf</b></a> file, or of its parent
- directory. This information is ignored with Postfix versions
+ The path name of the Postfix <a href="postconf.5.html"><b>main.cf</b></a> file, or of its parent
+ directory. This information is ignored with Postfix versions
before 2.3.
With Postfix version 3.2 and later, a non-default directory must
- be authorized in the default <a href="postconf.5.html"><b>main.cf</b></a> file, through the alter-
+ be authorized in the default <a href="postconf.5.html"><b>main.cf</b></a> file, through the alter-
nate_config_directories or <a href="postconf.5.html#multi_instance_directories">multi_instance_directories</a> parame-
ters.
- With all Postfix versions, you can specify a directory pathname
- with the MAIL_CONFIG environment variable to override the loca-
+ With all Postfix versions, you can specify a directory pathname
+ with the MAIL_CONFIG environment variable to override the loca-
tion of configuration files.
<b>-F</b> <i>full</i><b>_</b><i>name</i>
- Set the sender full name. This overrides the NAME environment
+ Set the sender full name. This overrides the NAME environment
variable, and is used only with messages that have no <b>From:</b> mes-
sage header.
<b>-f</b> <i>sender</i>
- Set the envelope sender address. This is the address where
+ Set the envelope sender address. This is the address where
delivery problems are sent to. With Postfix versions before 2.1,
- the <b>Errors-To:</b> message header overrides the error return
+ the <b>Errors-To:</b> message header overrides the error return
address.
- <b>-G</b> Gateway (relay) submission, as opposed to initial user submis-
- sion. Either do not rewrite addresses at all, or update incom-
- plete addresses with the domain information specified with
+ <b>-G</b> Gateway (relay) submission, as opposed to initial user submis-
+ sion. Either do not rewrite addresses at all, or update incom-
+ plete addresses with the domain information specified with
<b><a href="postconf.5.html#remote_header_rewrite_domain">remote_header_rewrite_domain</a></b>.
This option is ignored before Postfix version 2.3.
<b>-h</b> <i>hop</i><b>_</b><i>count</i> (ignored)
- Hop count limit. Use the <b><a href="postconf.5.html#hopcount_limit">hopcount_limit</a></b> configuration parameter
+ Hop count limit. Use the <b><a href="postconf.5.html#hopcount_limit">hopcount_limit</a></b> configuration parameter
instead.
<b>-I</b> Initialize alias database. See the <b>newaliases</b> command above.
- <b>-i</b> When reading a message from standard input, don't treat a line
+ <b>-i</b> When reading a message from standard input, don't treat a line
with only a <b>.</b> character as the end of input.
<b>-L</b> <i>label</i> (ignored)
- The logging label. Use the <b><a href="postconf.5.html#syslog_name">syslog_name</a></b> configuration parameter
+ The logging label. Use the <b><a href="postconf.5.html#syslog_name">syslog_name</a></b> configuration parameter
instead.
<b>-m</b> (ignored)
Backwards compatibility.
<b>-N</b> <i>dsn</i> (default: 'delay, failure')
- Delivery status notification control. Specify either a
+ Delivery status notification control. Specify either a
comma-separated list with one or more of <b>failure</b> (send notifica-
- tion when delivery fails), <b>delay</b> (send notification when deliv-
- ery is delayed), or <b>success</b> (send notification when the message
+ tion when delivery fails), <b>delay</b> (send notification when deliv-
+ ery is delayed), or <b>success</b> (send notification when the message
is delivered); or specify <b>never</b> (don't send any notifications at
all).
Backwards compatibility.
<b>-oA</b><i>alias</i><b>_</b><i>database</i>
- Non-default alias database. Specify <i>pathname</i> or <i>type</i>:<i>pathname</i>.
+ Non-default alias database. Specify <i>pathname</i> or <i>type</i>:<i>pathname</i>.
See <a href="postalias.1.html"><b>postalias</b>(1)</a> for details.
<b>-O</b> <i>option=value</i> (ignored)
- Set the named <i>option</i> to <i>value</i>. Use the equivalent configuration
+ Set the named <i>option</i> to <i>value</i>. Use the equivalent configuration
parameter in <a href="postconf.5.html"><b>main.cf</b></a> instead.
<b>-o7</b> (ignored)
<b>-o8</b> (ignored)
- To send 8-bit or binary content, use an appropriate MIME encap-
+ To send 8-bit or binary content, use an appropriate MIME encap-
sulation and specify the appropriate <b>-B</b> command-line option.
- <b>-oi</b> When reading a message from standard input, don't treat a line
+ <b>-oi</b> When reading a message from standard input, don't treat a line
with only a <b>.</b> character as the end of input.
<b>-om</b> (ignored)
The sender is never eliminated from alias etc. expansions.
<b>-o</b> <i>x value</i> (ignored)
- Set option <i>x</i> to <i>value</i>. Use the equivalent configuration parame-
+ Set option <i>x</i> to <i>value</i>. Use the equivalent configuration parame-
ter in <a href="postconf.5.html"><b>main.cf</b></a> instead.
<b>-r</b> <i>sender</i>
- Set the envelope sender address. This is the address where
+ Set the envelope sender address. This is the address where
delivery problems are sent to. With Postfix versions before 2.1,
- the <b>Errors-To:</b> message header overrides the error return
+ the <b>Errors-To:</b> message header overrides the error return
address.
<b>-R</b> <i>return</i>
- Delivery status notification control. Specify "hdrs" to return
- only the header when a message bounces, "full" to return a full
+ Delivery status notification control. Specify "hdrs" to return
+ only the header when a message bounces, "full" to return a full
copy (the default behavior).
The <b>-R</b> option specifies an upper bound; Postfix will return only
- the header, when a full copy would exceed the <a href="postconf.5.html#bounce_size_limit">bounce_size_limit</a>
+ the header, when a full copy would exceed the <a href="postconf.5.html#bounce_size_limit">bounce_size_limit</a>
setting.
This option is ignored before Postfix version 2.10.
- <b>-q</b> Attempt to deliver all queued mail. This is implemented by exe-
+ <b>-q</b> Attempt to deliver all queued mail. This is implemented by exe-
cuting the <a href="postqueue.1.html"><b>postqueue</b>(1)</a> command.
- Warning: flushing undeliverable mail frequently will result in
+ Warning: flushing undeliverable mail frequently will result in
poor delivery performance of all other mail.
<b>-q</b><i>interval</i> (ignored)
<b>-qI</b><i>queueid</i>
Schedule immediate delivery of mail with the specified queue ID.
- This option is implemented by executing the <a href="postqueue.1.html"><b>postqueue</b>(1)</a> com-
+ This option is implemented by executing the <a href="postqueue.1.html"><b>postqueue</b>(1)</a> com-
mand, and is available with Postfix version 2.4 and later.
<b>-qR</b><i>site</i>
- Schedule immediate delivery of all mail that is queued for the
- named <i>site</i>. This option accepts only <i>site</i> names that are eligi-
- ble for the "fast flush" service, and is implemented by execut-
+ Schedule immediate delivery of all mail that is queued for the
+ named <i>site</i>. This option accepts only <i>site</i> names that are eligi-
+ ble for the "fast flush" service, and is implemented by execut-
ing the <a href="postqueue.1.html"><b>postqueue</b>(1)</a> command. See <a href="flush.8.html"><b>flush</b>(8)</a> for more information
about the "fast flush" service.
<b>-qS</b><i>site</i>
- This command is not implemented. Use the slower "<b>sendmail -q</b>"
+ This command is not implemented. Use the slower "<b>sendmail -q</b>"
command instead.
- <b>-t</b> Extract recipients from message headers. These are added to any
+ <b>-t</b> Extract recipients from message headers. These are added to any
recipients specified on the command line.
With Postfix versions prior to 2.1, this option requires that no
This feature is available in Postfix 2.3 and later.
<b>-XV</b> (Postfix 2.2 and earlier: <b>-V</b>)
- Variable Envelope Return Path. Given an envelope sender address
- of the form <i>owner-listname</i>@<i>origin</i>, each recipient <i>user</i>@<i>domain</i>
+ Variable Envelope Return Path. Given an envelope sender address
+ of the form <i>owner-listname</i>@<i>origin</i>, each recipient <i>user</i>@<i>domain</i>
receives mail with a personalized envelope sender address.
- By default, the personalized envelope sender address is
- <i>owner-listname</i><b>+</b><i>user</i><b>=</b><i>domain</i>@<i>origin</i>. The default <b>+</b> and <b>=</b> charac-
- ters are configurable with the <b><a href="postconf.5.html#default_verp_delimiters">default_verp_delimiters</a></b> configu-
+ By default, the personalized envelope sender address is
+ <i>owner-listname</i><b>+</b><i>user</i><b>=</b><i>domain</i>@<i>origin</i>. The default <b>+</b> and <b>=</b> charac-
+ ters are configurable with the <b><a href="postconf.5.html#default_verp_delimiters">default_verp_delimiters</a></b> configu-
ration parameter.
<b>-XV</b><i>xy</i> (Postfix 2.2 and earlier: <b>-V</b><i>xy</i>)
- As <b>-XV</b>, but uses <i>x</i> and <i>y</i> as the VERP delimiter characters,
+ As <b>-XV</b>, but uses <i>x</i> and <i>y</i> as the VERP delimiter characters,
instead of the characters specified with the <b><a href="postconf.5.html#default_verp_delimiters">default_verp_delim</a>-</b>
<b><a href="postconf.5.html#default_verp_delimiters">iters</a></b> configuration parameter.
<b>-v</b> Send an email report of the first delivery attempt (Postfix ver-
- sions 2.1 and later). Mail delivery always happens in the back-
- ground. When multiple <b>-v</b> options are given, enable verbose log-
+ sions 2.1 and later). Mail delivery always happens in the back-
+ ground. When multiple <b>-v</b> options are given, enable verbose log-
ging for debugging purposes.
<b>-X</b> <i>log</i><b>_</b><i>file</i> (ignored)
configuration parameters instead.
<b>SECURITY</b>
- By design, this program is not set-user (or group) id. However, it must
- handle data from untrusted, possibly remote, users. Thus, the usual
- precautions need to be taken against malicious inputs.
+ By design, this program is not set-user (or group) id. It is prepared
+ to handle message content from untrusted, possibly remote, users.
+
+ However, like most Postfix programs, this program does not enforce a
+ security policy on its command-line arguments. Instead, it relies on
+ the UNIX system to enforce access policies based on the effective user
+ and group IDs of the process. Concretely, this means that running Post-
+ fix commands as root (from sudo or equivalent) on behalf of a non-root
+ user is likely to create privilege escalation opportunities.
+
+ If an application runs any Postfix programs on behalf of users that do
+ not have normal shell access to Postfix commands, then that application
+ MUST restrict user-specified command-line arguments to avoid privilege
+ escalation.
+
+ <b>o</b> Filter all command-line arguments, for example arguments that
+ contain a pathname or that specify a database access method.
+ These pathname checks must reject user-controlled symlinks or
+ hardlinks to sensitive files, and must not be vulnerable to TOC-
+ TOU race attacks.
+
+ <b>o</b> Disable command options processing for all command arguments
+ that contain user-specified data. For example, the Postfix <a href="sendmail.1.html"><b>send-</b></a>
+ <a href="sendmail.1.html"><b>mail</b>(1)</a> command line MUST be structured as follows:
+
+ <b>/path/to/sendmail</b> <i>system-arguments</i> <b>--</b> <i>user-arguments</i>
+
+ Here, the "<b>--</b>" disables command option processing for all
+ <i>user-arguments</i> that follow.
+
+ Without the "<b>--</b>", a malicious user could enable Postfix <a href="sendmail.1.html"><b>send-</b></a>
+ <a href="sendmail.1.html"><b>mail</b>(1)</a> command options, by specifying an email address that
+ starts with "<b>-</b>".
<b>DIAGNOSTICS</b>
- Problems are logged to <b>syslogd</b>(8) or <a href="postlogd.8.html"><b>postlogd</b>(8)</a>, and to the standard
+ Problems are logged to <b>syslogd</b>(8) or <a href="postlogd.8.html"><b>postlogd</b>(8)</a>, and to the standard
error stream.
<b>ENVIRONMENT</b>
Enable debugging with an external command, as specified with the
<b><a href="postconf.5.html#debugger_command">debugger_command</a></b> configuration parameter.
- <b>NAME</b> The sender full name. This is used only with messages that have
+ <b>NAME</b> The sender full name. This is used only with messages that have
no <b>From:</b> message header. See also the <b>-F</b> option above.
<b>CONFIGURATION PARAMETERS</b>
- The following <a href="postconf.5.html"><b>main.cf</b></a> parameters are especially relevant to this pro-
- gram. The text below provides only a parameter summary. See <a href="postconf.5.html"><b>post-</b></a>
+ The following <a href="postconf.5.html"><b>main.cf</b></a> parameters are especially relevant to this pro-
+ gram. The text below provides only a parameter summary. See <a href="postconf.5.html"><b>post-</b></a>
<a href="postconf.5.html"><b>conf</b>(5)</a> for more details including examples.
<b>COMPATIBILITY CONTROLS</b>
line endings from <CR><LF> into UNIX format (<LF>).
<b>TROUBLE SHOOTING CONTROLS</b>
- The <a href="DEBUG_README.html">DEBUG_README</a> file gives examples of how to troubleshoot a Postfix
+ The <a href="DEBUG_README.html">DEBUG_README</a> file gives examples of how to troubleshoot a Postfix
system.
<b><a href="postconf.5.html#debugger_command">debugger_command</a> (empty)</b>
invoked with the -D option.
<b><a href="postconf.5.html#debug_peer_level">debug_peer_level</a> (2)</b>
- The increment in verbose logging level when a remote client or
- server matches a pattern in the <a href="postconf.5.html#debug_peer_list">debug_peer_list</a> parameter.
+ The increment in verbose logging level when a nexthop destina-
+ tion, remote client or server name or network address matches a
+ pattern given with the <a href="postconf.5.html#debug_peer_list">debug_peer_list</a> parameter.
<b><a href="postconf.5.html#debug_peer_list">debug_peer_list</a> (empty)</b>
- Optional list of remote client or server hostname or network
- address patterns that cause the verbose logging level to
- increase by the amount specified in $<a href="postconf.5.html#debug_peer_level">debug_peer_level</a>.
+ Optional list of nexthop destination, remote client or server
+ name or network address patterns that, if matched, cause the
+ verbose logging level to increase by the amount specified in
+ $<a href="postconf.5.html#debug_peer_level">debug_peer_level</a>.
<b>ACCESS CONTROLS</b>
Available in Postfix version 2.2 and later:
List of users who are authorized to view the queue.
<b><a href="postconf.5.html#authorized_submit_users">authorized_submit_users</a> (<a href="DATABASE_README.html#types">static</a>:anyone)</b>
- List of users who are authorized to submit mail with the <a href="sendmail.1.html"><b>send-</b></a>
+ List of users who are authorized to submit mail with the <a href="sendmail.1.html"><b>send-</b></a>
<a href="sendmail.1.html"><b>mail</b>(1)</a> command (and with the privileged <a href="postdrop.1.html"><b>postdrop</b>(1)</a> helper com-
mand).
<b>RESOURCE AND RATE CONTROLS</b>
<b><a href="postconf.5.html#bounce_size_limit">bounce_size_limit</a> (50000)</b>
- The maximal amount of original message text that is sent in a
+ The maximal amount of original message text that is sent in a
non-delivery notification.
<b><a href="postconf.5.html#fork_attempts">fork_attempts</a> (5)</b>
in the primary message headers.
<b><a href="postconf.5.html#queue_run_delay">queue_run_delay</a> (300s)</b>
- The time between <a href="QSHAPE_README.html#deferred_queue">deferred queue</a> scans by the queue manager;
+ The time between <a href="QSHAPE_README.html#deferred_queue">deferred queue</a> scans by the queue manager;
prior to Postfix 2.4 the default value was 1000s.
<b>FAST FLUSH CONTROLS</b>
- The <a href="ETRN_README.html">ETRN_README</a> file describes configuration and operation details for
+ The <a href="ETRN_README.html">ETRN_README</a> file describes configuration and operation details for
the Postfix "fast flush" service.
<b><a href="postconf.5.html#fast_flush_domains">fast_flush_domains</a> ($<a href="postconf.5.html#relay_domains">relay_domains</a>)</b>
tion logfiles with mail that is queued to those destinations.
<b>VERP CONTROLS</b>
- The <a href="VERP_README.html">VERP_README</a> file describes configuration and operation details of
+ The <a href="VERP_README.html">VERP_README</a> file describes configuration and operation details of
Postfix support for variable envelope return path addresses.
<b><a href="postconf.5.html#default_verp_delimiters">default_verp_delimiters</a> (+=)</b>
The two default VERP delimiter characters.
<b><a href="postconf.5.html#verp_delimiter_filter">verp_delimiter_filter</a> (-=+)</b>
- The characters Postfix accepts as VERP delimiter characters on
+ The characters Postfix accepts as VERP delimiter characters on
the Postfix <a href="sendmail.1.html"><b>sendmail</b>(1)</a> command line and in SMTP commands.
<b>MISCELLANEOUS CONTROLS</b>
<b><a href="postconf.5.html#alias_database">alias_database</a> (see 'postconf -d' output)</b>
- The alias databases for <a href="local.8.html"><b>local</b>(8)</a> delivery that are updated with
+ The alias databases for <a href="local.8.html"><b>local</b>(8)</a> delivery that are updated with
"<b>newaliases</b>" or with "<b>sendmail -bi</b>".
<b><a href="postconf.5.html#command_directory">command_directory</a> (see 'postconf -d' output)</b>
The location of all postfix administrative commands.
<b><a href="postconf.5.html#config_directory">config_directory</a> (see 'postconf -d' output)</b>
- The default location of the Postfix <a href="postconf.5.html">main.cf</a> and <a href="master.5.html">master.cf</a> con-
+ The default location of the Postfix <a href="postconf.5.html">main.cf</a> and <a href="master.5.html">master.cf</a> con-
figuration files.
<b><a href="postconf.5.html#daemon_directory">daemon_directory</a> (see 'postconf -d' output)</b>
and <a href="postmap.1.html"><b>postmap</b>(1)</a> commands.
<b><a href="postconf.5.html#delay_warning_time">delay_warning_time</a> (0h)</b>
- The time after which the sender receives a copy of the message
+ The time after which the sender receives a copy of the message
headers of mail that is still queued.
<b><a href="postconf.5.html#import_environment">import_environment</a> (see 'postconf -d' output)</b>
- The list of environment parameters that a privileged Postfix
- process will import from a non-Postfix parent process, or
+ The list of environment parameters that a privileged Postfix
+ process will import from a non-Postfix parent process, or
name=value environment overrides.
<b><a href="postconf.5.html#mail_owner">mail_owner</a> (postfix)</b>
- The UNIX system account that owns the Postfix queue and most
+ The UNIX system account that owns the Postfix queue and most
Postfix daemon processes.
<b><a href="postconf.5.html#queue_directory">queue_directory</a> (see 'postconf -d' output)</b>
The location of the Postfix top-level queue directory.
<b><a href="postconf.5.html#remote_header_rewrite_domain">remote_header_rewrite_domain</a> (empty)</b>
- Don't rewrite message headers from remote clients at all when
- this parameter is empty; otherwise, rewrite message headers and
+ Don't rewrite message headers from remote clients at all when
+ this parameter is empty; otherwise, rewrite message headers and
append the specified domain name to incomplete addresses.
<b><a href="postconf.5.html#syslog_facility">syslog_facility</a> (mail)</b>
The syslog facility of Postfix logging.
<b><a href="postconf.5.html#syslog_name">syslog_name</a> (see 'postconf -d' output)</b>
- A prefix that is prepended to the process name in syslog
+ A prefix that is prepended to the process name in syslog
records, so that, for example, "smtpd" becomes "prefix/smtpd".
Postfix 3.2 and later:
<b><a href="postconf.5.html#alternate_config_directories">alternate_config_directories</a> (empty)</b>
A list of non-default Postfix configuration directories that may
- be specified with "-c <a href="postconf.5.html#config_directory">config_directory</a>" on the command line (in
- the case of <a href="sendmail.1.html"><b>sendmail</b>(1)</a>, with the "-C" option), or via the
+ be specified with "-c <a href="postconf.5.html#config_directory">config_directory</a>" on the command line (in
+ the case of <a href="sendmail.1.html"><b>sendmail</b>(1)</a>, with the "-C" option), or via the
MAIL_CONFIG environment parameter.
<b><a href="postconf.5.html#multi_instance_directories">multi_instance_directories</a> (empty)</b>
- An optional list of non-default Postfix configuration directo-
- ries; these directories belong to additional Postfix instances
- that share the Postfix executable files and documentation with
- the default Postfix instance, and that are started, stopped,
+ An optional list of non-default Postfix configuration directo-
+ ries; these directories belong to additional Postfix instances
+ that share the Postfix executable files and documentation with
+ the default Postfix instance, and that are started, stopped,
etc., together with the default Postfix instance.
<b>FILES</b>
syslogd(8), system logging
<b>README_FILES</b>
- Use "<b>postconf <a href="postconf.5.html#readme_directory">readme_directory</a></b>" or "<b>postconf <a href="postconf.5.html#html_directory">html_directory</a></b>" to locate
+ Use "<b>postconf <a href="postconf.5.html#readme_directory">readme_directory</a></b>" or "<b>postconf <a href="postconf.5.html#html_directory">html_directory</a></b>" to locate
this information.
<a href="DEBUG_README.html">DEBUG_README</a>, Postfix debugging howto
<a href="ETRN_README.html">ETRN_README</a>, Postfix ETRN howto
</DD>
<DT><b><a name="lmtp_tls_fingerprint_digest">lmtp_tls_fingerprint_digest</a>
-(default: md5)</b></DT><DD>
+(default: see "postconf -d" output)</b></DT><DD>
<p> The LMTP-specific version of the <a href="postconf.5.html#smtp_tls_fingerprint_digest">smtp_tls_fingerprint_digest</a>
configuration parameter. See there for details. </p>
<a href="postconf.5.html#smtpd_tls_fingerprint_digest">smtpd_tls_fingerprint_digest</a> parameter (hard-coded as md5 prior to
Postfix version 2.5). </dd>
+<dd> The default algorithm is <b>sha256</b> with Postfix ≥ 3.6
+and the <b><a href="postconf.5.html#compatibility_level">compatibility_level</a></b> set to 3 or higher. With Postfix
+≤ 3.5, the default algorithm is <b>md5</b>. The best-practice
+algorithm is now <b>sha256</b>. Recent advances in hash function
+cryptanalysis have led to md5 and sha1 being deprecated in favor of
+sha256. However, as long as there are no known "second pre-image"
+attacks against the older algorithms, their use in this context, though
+not recommended, is still likely safe. </dd>
+
<dt><b><a href="postconf.5.html#permit_tls_all_clientcerts">permit_tls_all_clientcerts</a> </b></dt>
<dd> Append the domain name in $<a href="postconf.5.html#myorigin">myorigin</a> or $<a href="postconf.5.html#mydomain">mydomain</a> when the
<a href="postconf.5.html#smtpd_tls_fingerprint_digest">smtpd_tls_fingerprint_digest</a> parameter (hard-coded as md5 prior to
Postfix version 2.5). </p>
+<p> The default algorithm is <b>sha256</b> with Postfix ≥ 3.6
+and the <b><a href="postconf.5.html#compatibility_level">compatibility_level</a></b> set to 3 or higher. With Postfix
+≤ 3.5, the default algorithm is <b>md5</b>. The best-practice
+algorithm is now <b>sha256</b>. Recent advances in hash function
+cryptanalysis have led to md5 and sha1 being deprecated in favor of
+sha256. However, as long as there are no known "second pre-image"
+attacks against the older algorithms, their use in this context, though
+not recommended, is still likely safe. </p>
+
<p> Postfix lookup tables are in the form of (key, value) pairs.
Since we only need the key, the value can be chosen freely, e.g.
the name of the user or host:
an appropriate <a href="access.5.html">access(5)</a> policy for each client.
See <a href="RESTRICTION_CLASS_README.html">RESTRICTION_CLASS_README</a>.</p>
-<p> <b>Note:</b> Postfix 2.9.0–2.9.5 computed the public key
-fingerprint incorrectly. To use public-key fingerprints, upgrade
-to Postfix 2.9.6 or later. </p>
-
<p>This feature is available with Postfix version 2.2.</p>
digest algorithm is selected via the <b><a href="postconf.5.html#smtp_tls_fingerprint_digest">smtp_tls_fingerprint_digest</a></b>
parameter. </p>
+<p> The colons between each pair of nibbles in the fingerprint value
+are optional (Postfix ≥ 3.6). These were required in earlier
+Postfix releases. </p>
+
<p> When an <b><a href="postconf.5.html#smtp_tls_policy_maps">smtp_tls_policy_maps</a></b> table entry specifies the
"fingerprint" security level, any "match" attributes in that entry specify
the list of valid fingerprints for the corresponding destination. Multiple
<pre>
<a href="postconf.5.html#relayhost">relayhost</a> = [mailhub.example.com]
<a href="postconf.5.html#smtp_tls_security_level">smtp_tls_security_level</a> = fingerprint
-<a href="postconf.5.html#smtp_tls_fingerprint_digest">smtp_tls_fingerprint_digest</a> = md5
+<a href="postconf.5.html#smtp_tls_fingerprint_digest">smtp_tls_fingerprint_digest</a> = sha256
<a href="postconf.5.html#smtp_tls_fingerprint_cert_match">smtp_tls_fingerprint_cert_match</a> =
- 3D:95:34:51:24:66:33:B9:D2:40:99:C0:C1:17:0B:D1
- EC:3B:2D:B0:5B:B1:FB:6D:20:A3:9D:72:F6:8D:12:35
+ cd:fc:d8:db:f8:c4:82:96:6c:...:28:71:e8:f5:8d:a5:0d:9b:d4:a6
+ dd:5c:ef:f5:c3:bc:64:25:36:...:99:36:06:ce:40:ef:de:2e:ad:a4
</pre>
</blockquote>
<pre>
/etc/postfix/<a href="postconf.5.html">main.cf</a>:
<a href="postconf.5.html#smtp_tls_policy_maps">smtp_tls_policy_maps</a> = <a href="DATABASE_README.html#types">hash</a>:/etc/postfix/tls_policy
- <a href="postconf.5.html#smtp_tls_fingerprint_digest">smtp_tls_fingerprint_digest</a> = md5
+ <a href="postconf.5.html#smtp_tls_fingerprint_digest">smtp_tls_fingerprint_digest</a> = sha256
</pre>
</blockquote>
<pre>
/etc/postfix/tls_policy:
example.com fingerprint
- match=3D:95:34:51:24:66:33:B9:D2:40:99:C0:C1:17:0B:D1
- match=EC:3B:2D:B0:5B:B1:FB:6D:20:A3:9D:72:F6:8D:12:35
+ match=51:e9:af:2e:1e:40:1f:...:64:0a:30:35:2d:09:16:31:5a:eb:82:76
+ match=b6:b4:72:34:e2:59:cd:...:c2:ca:63:0d:4d:cc:2c:7d:84:de:e6:2f
</pre>
</blockquote>
</DD>
<DT><b><a name="smtp_tls_fingerprint_digest">smtp_tls_fingerprint_digest</a>
-(default: md5)</b></DT><DD>
+(default: see "postconf -d" output)</b></DT><DD>
<p> The message digest algorithm used to construct remote SMTP server
certificate fingerprints. At the "fingerprint" TLS security level
attacks, it is not feasible to create a new public key and a matching
certificate (or public/private key-pair) that has the same fingerprint. </p>
-<p> The default algorithm is <b>md5</b>; this is consistent with
-the backwards compatible setting of the digest used to verify client
-certificates in the SMTP server. </p>
+<p> The default algorithm is <b>sha256</b> with Postfix ≥ 3.6
+and the <b><a href="postconf.5.html#compatibility_level">compatibility_level</a></b> set to 3 or higher. With Postfix
+≤ 3.5, the default algorithm is <b>md5</b>. </p>
-<p> The best practice algorithm is now <b>sha1</b>. Recent advances in hash
-function cryptanalysis have led to md5 being deprecated in favor of sha1.
-However, as long as there are no known "second pre-image" attacks
-against md5, its use in this context can still be considered safe.
-</p>
+<p> The best-practice algorithm is now <b>sha256</b>. Recent advances in hash
+function cryptanalysis have led to md5 and sha1 being deprecated in favor of
+sha256. However, as long as there are no known "second pre-image" attacks
+against the older algorithms, their use in this context, though not
+recommended, is still likely safe. </p>
<p> While additional digest algorithms are often available with OpenSSL's
libcrypto, only those used by libssl in SSL cipher suites are available to
-Postfix. For now this means just md5 or sha1. </p>
+Postfix. You'll likely find support for md5, sha1, sha256 and sha512. </p>
<p> To find the fingerprint of a specific certificate file, with a
specific digest algorithm, run:
<blockquote>
<pre>
-$ openssl x509 -noout -fingerprint -sha1 -in cert.pem
-SHA1 Fingerprint=D4:6A:AB:19:24:79:F8:32:BB:A6:CB:66:82:C0:8E:9B:EE:29:A8:1A
+$ openssl x509 -noout -fingerprint -sha256 -in cert.pem
+SHA256 Fingerprint=D4:6A:AB:19:24:...:BB:A6:CB:66:82:C0:8E:9B:EE:29:A8:1A
</pre>
</blockquote>
command that converts the key to DER and then to the "dgst" command
to compute the fingerprint. </p>
-<p> The actual command to transform the key to DER format depends
-on the version of OpenSSL used. With OpenSSL 1.0.0 and later, the
-"pkey" command supports all key types. With OpenSSL 0.9.8 and
-earlier, the key type is always RSA (nobody uses DSA, and EC
-keys are not fully supported by 0.9.8), so the "rsa" command is
-used. </p>
+<p> The actual command to transform the key to DER format depends on the
+version of OpenSSL used. As of OpenSSL 1.0.0, the "pkey" command supports
+all key types. </p>
<blockquote>
<pre>
-# OpenSSL 1.0 with all certificates and SHA-1 fingerprints.
+# OpenSSL ≥ 1.0 with SHA-256 fingerprints.
$ openssl x509 -in cert.pem -noout -pubkey |
openssl pkey -pubin -outform DER |
- openssl dgst -sha1 -c
-(stdin)= 64:3f:1f:f6:e5:1e:d4:2a:56:8b:fc:09:1a:61:98:b5:bc:7c:60:58
-</pre>
-</blockquote>
-
-<blockquote>
-<pre>
-# OpenSSL 0.9.8 with RSA certificates and MD5 fingerprints.
-$ openssl x509 -in cert.pem -noout -pubkey |
- openssl rsa -pubin -outform DER |
- openssl dgst -md5 -c
-(stdin)= f4:62:60:f6:12:8f:d5:8d:28:4d:13:a7:db:b2:ff:50
+ openssl dgst -sha256 -c
+(stdin)= 64:3f:1f:f6:e5:1e:d4:2a:56:...:fc:09:1a:61:98:b5:bc:7c:60:58
</pre>
</blockquote>
fingerprint and public key fingerprint when the TLS loglevel is 2 or
higher. </p>
-<p> <b>Note:</b> Postfix 2.9.0–2.9.5 computed the public key
-fingerprint incorrectly. To use public-key fingerprints, upgrade
-to Postfix 2.9.6 or later. </p>
-
<p> This feature is available in Postfix 2.5 and later. </p>
<pre>
/etc/postfix/<a href="postconf.5.html">main.cf</a>:
<a href="postconf.5.html#smtp_tls_policy_maps">smtp_tls_policy_maps</a> = <a href="DATABASE_README.html#types">hash</a>:/etc/postfix/tls_policy
- # Postfix 2.5 and later
- <a href="postconf.5.html#smtp_tls_fingerprint_digest">smtp_tls_fingerprint_digest</a> = md5
+ # Postfix 2.5 and later.
+ #
+ # The default digest is sha256 with Postfix ≥ 3.6 and
+ # compatibility level ≥ 3.
+ #
+ <a href="postconf.5.html#smtp_tls_fingerprint_digest">smtp_tls_fingerprint_digest</a> = sha256
</pre>
<pre>
[mail.example.org]:587 secure match=nexthop
# Postfix 2.5 and later
[thumb.example.org] fingerprint
- match=EC:3B:2D:B0:5B:B1:FB:6D:20:A3:9D:72:F6:8D:12:35
- match=3D:95:34:51:24:66:33:B9:D2:40:99:C0:C1:17:0B:D1
+ match=b6:b4:72:34:e2:59:cd:...:c2:ca:63:0d:4d:cc:2c:7d:84:de:e6:2f
+ match=51:e9:af:2e:1e:40:1f:...:64:0a:30:35:2d:09:16:31:5a:eb:82:76
</pre>
<p> <b>Note:</b> The <b>hostname</b> strategy if listed in a non-default
= yes" and is available with Postfix version
2.2 and later. </dd>
-<br>
+<dd> The default algorithm is <b>sha256</b> with Postfix ≥ 3.6
+and the <b><a href="postconf.5.html#compatibility_level">compatibility_level</a></b> set to 3 or higher. With Postfix
+≤ 3.5, the default algorithm is <b>md5</b>. The best-practice
+algorithm is now <b>sha256</b>. Recent advances in hash function
+cryptanalysis have led to md5 and sha1 being deprecated in favor of
+sha256. However, as long as there are no known "second pre-image"
+attacks against the older algorithms, their use in this context, though
+not recommended, is still likely safe. </dd>
<dd> Alternatively, <a href="postconf.5.html#check_ccert_access">check_ccert_access</a> accepts an explicit search
order (Postfix 3.5 and later). The default search order as described
Postfix version 2.5). This feature requires "<a href="postconf.5.html#smtpd_tls_ask_ccert">smtpd_tls_ask_ccert</a>
= yes" and is available with Postfix version 2.2 and later.</dd>
+<dd> The default algorithm is <b>sha256</b> with Postfix ≥ 3.6
+and the <b><a href="postconf.5.html#compatibility_level">compatibility_level</a></b> set to 3 or higher. With Postfix
+≤ 3.5, the default algorithm is <b>md5</b>. The best-practice
+algorithm is now <b>sha256</b>. Recent advances in hash function
+cryptanalysis have led to md5 and sha1 being deprecated in favor of
+sha256. However, as long as there are no known "second pre-image"
+attacks against the older algorithms, their use in this context, though
+not recommended, is still likely safe. </dd>
+
<dt><b><a name="reject_rbl_client">reject_rbl_client <i>rbl_domain=d.d.d.d</i></a></b></dt>
<dd>Reject the request when the reversed client network address is
<p> File with DH parameters that the Postfix SMTP server should
use with non-export EDH ciphers. </p>
+<p> The best-practice choice of parameters uses a 2048-bit prime. This is fine,
+despite the historical "1024" in the parameter name. Do not be tempted to use
+much larger values, performance degrades quickly, and you may also cease to
+interoperate with some mainstream SMTP clients. As of Postfix 3.1, the
+compiled-in default prime is 2048-bits, and it is not strictly necessary,
+though perhaps somewhat beneficial to generate custom DH parameters. </p>
+
<p> Instead of using the exact same parameter sets as distributed
with other TLS packages, it is more secure to generate your own
set of parameters with something like the following commands: </p>
<blockquote>
<pre>
-openssl dhparam -out /etc/postfix/dh512.pem 512
-openssl dhparam -out /etc/postfix/dh1024.pem 1024
openssl dhparam -out /etc/postfix/dh2048.pem 2048
+openssl dhparam -out /etc/postfix/dh1024.pem 1024
+# As of Postfix 3.6, export-grade 512-bit DH parameters are no longer
+# supported or needed.
+openssl dhparam -out /etc/postfix/dh512.pem 512
</pre>
</blockquote>
and as a result export-grade cipher suites are by default not used.
</p>
+<p> With Postfix ≥ 3.6 export-grade Diffie-Hellman key exchange
+is no longer supported, and this parameter is silently ignored. </p>
+
<p> See also the discussion under the <a href="postconf.5.html#smtpd_tls_dh1024_param_file">smtpd_tls_dh1024_param_file</a>
configuration parameter. </p>
(default: see "postconf -d" output)</b></DT><DD>
<p> The Postfix SMTP server security grade for ephemeral elliptic-curve
-Diffie-Hellman (EECDH) key exchange. </p>
+Diffie-Hellman (EECDH) key exchange. As of Postfix 3.6, the value of
+this parameter is always ignored, and Postfix behaves as though th
+<b>auto</b> value (described below) was chosen.
+</p>
<p> The available choices are: </p>
<dl>
+<dt><b>auto</b></dt> <dd> Use the most preferred curve that is
+supported by both the client and the server. This setting requires
+Postfix ≥ 3.2 compiled and linked with OpenSSL ≥ 1.0.2. This
+is the default setting under the above conditions (and the only
+setting used with Postfix ≥ 3.6). </dd>
+
<dt><b>none</b></dt> <dd> Don't use EECDH. Ciphers based on EECDH key
exchange will be disabled. This is the default in Postfix versions
2.6 and 2.7. </dd>
-<dt><b>strong</b></dt> <dd> Use EECDH with approximately 128
-bits of security at a reasonable computational cost. This is the
-current best-practice trade-off between security and computational
-efficiency. This is the default in Postfix version 2.8 and later.
-</dd>
+<dt><b>strong</b></dt> <dd> Use EECDH with approximately 128 bits of
+security at a reasonable computational cost. This is the default in
+Postfix versions 2.8–3.5. </dd>
<dt><b>ultra</b></dt> <dd> Use EECDH with approximately 192 bits of
security at computational cost that is approximately twice as high
-as 128 bit strength ECC. Barring significant progress in attacks on
-elliptic curve crypto-systems, the "strong" curve is sufficient for most
-users. </dd>
-
-<dt><b>auto</b></dt> <dd> Use the most preferred curve that is
-supported by both the client and the server. This setting requires
-Postfix ≥ 3.2 compiled and linked with OpenSSL ≥ 1.0.2. This
-is the default setting under the above conditions. </dd>
+as 128 bit strength ECC. </dd>
</dl>
</DD>
<DT><b><a name="smtpd_tls_fingerprint_digest">smtpd_tls_fingerprint_digest</a>
-(default: md5)</b></DT><DD>
+(default: see "postconf -d" output)</b></DT><DD>
-<p> The message digest algorithm to construct remote SMTP
-client-certificate
-fingerprints or public key fingerprints (Postfix 2.9 and later)
-for <b><a href="postconf.5.html#check_ccert_access">check_ccert_access</a></b> and <b><a href="postconf.5.html#permit_tls_clientcerts">permit_tls_clientcerts</a></b>. The
-default algorithm is <b>md5</b>, for backwards compatibility with Postfix
-releases prior to 2.5. </p>
+<p> The message digest algorithm to construct remote SMTP client-certificate
+fingerprints or public key fingerprints (Postfix 2.9 and later) for
+<b><a href="postconf.5.html#check_ccert_access">check_ccert_access</a></b> and <b><a href="postconf.5.html#permit_tls_clientcerts">permit_tls_clientcerts</a></b>. </p>
-<p> Advances in hash
-function cryptanalysis have led to md5 being deprecated in favor of sha1.
-However, as long as there are no known "second pre-image" attacks
-against md5, its use in this context can still be considered safe.
-</p>
+<p> The default algorithm is <b>sha256</b> with Postfix ≥ 3.6
+and the <b><a href="postconf.5.html#compatibility_level">compatibility_level</a></b> set to 3 or higher. With Postfix
+≤ 3.5, the default algorithm is <b>md5</b>. </p>
+
+<p> The best-practice algorithm is now <b>sha256</b>. Recent advances in hash
+function cryptanalysis have led to md5 and sha1 being deprecated in favor of
+sha256. However, as long as there are no known "second pre-image" attacks
+against the older algorithms, their use in this context, though not
+recommended, is still likely safe. </p>
<p> While additional digest algorithms are often available with OpenSSL's
libcrypto, only those used by libssl in SSL cipher suites are available to
-Postfix. </p>
+Postfix. You'll likely find support for md5, sha1, sha256 and sha512. </p>
<p> To find the fingerprint of a specific certificate file, with a
specific digest algorithm, run: </p>
<blockquote>
<pre>
-$ openssl x509 -noout -fingerprint -sha1 -in cert.pem
-SHA1 Fingerprint=D4:6A:AB:19:24:79:F8:32:BB:A6:CB:66:82:C0:8E:9B:EE:29:A8:1A
+$ openssl x509 -noout -fingerprint -sha256 -in cert.pem
+SHA256 Fingerprint=D4:6A:AB:19:24:...:A6:CB:66:82:C0:8E:9B:EE:29:A8:1A
</pre>
</blockquote>
command that converts the key to DER and then to the "dgst" command
to compute the fingerprint. </p>
-<p> The actual command to transform the key to DER format depends
-on the version of OpenSSL used. With OpenSSL 1.0.0 and later, the
-"pkey" command supports all key types. With OpenSSL 0.9.8 and
-earlier, the key type is always RSA (nobody uses DSA, and EC
-keys are not fully supported by 0.9.8), so the "rsa" command is
-used. </p>
+<p> Example: </p>
<blockquote>
<pre>
-# OpenSSL 1.0 with all certificates and SHA-1 fingerprints.
$ openssl x509 -in cert.pem -noout -pubkey |
openssl pkey -pubin -outform DER |
- openssl dgst -sha1 -c
+ openssl dgst -sha256 -c
(stdin)= 64:3f:1f:f6:e5:1e:d4:2a:56:8b:fc:09:1a:61:98:b5:bc:7c:60:58
</pre>
</blockquote>
-<blockquote>
-<pre>
-# OpenSSL 0.9.8 with RSA certificates and MD5 fingerprints.
-$ openssl x509 -in cert.pem -noout -pubkey |
- openssl rsa -pubin -outform DER |
- openssl dgst -md5 -c
-(stdin)= f4:62:60:f6:12:8f:d5:8d:28:4d:13:a7:db:b2:ff:50
-</pre>
-</blockquote>
-
<p> The Postfix SMTP server and client log the peer (leaf) certificate
fingerprint and public key fingerprint when the TLS loglevel is 2 or
higher. </p>
-<p> <b>Note:</b> Postfix 2.9.0–2.9.5 computed the public key
-fingerprint incorrectly. To use public-key fingerprints, upgrade
-to Postfix 2.9.6 or later. </p>
-
-<p> Example: client-certificate access table, with sha1 fingerprints: </p>
+<p> Example: client-certificate access table, with sha256 fingerprints: </p>
<blockquote>
<pre>
/etc/postfix/<a href="postconf.5.html">main.cf</a>:
- <a href="postconf.5.html#smtpd_tls_fingerprint_digest">smtpd_tls_fingerprint_digest</a> = sha1
+ <a href="postconf.5.html#smtpd_tls_fingerprint_digest">smtpd_tls_fingerprint_digest</a> = sha256
<a href="postconf.5.html#smtpd_client_restrictions">smtpd_client_restrictions</a> =
<a href="postconf.5.html#check_ccert_access">check_ccert_access</a> <a href="DATABASE_README.html#types">hash</a>:/etc/postfix/access,
reject
<pre>
/etc/postfix/access:
# Action folded to next line...
- AF:88:7C:AD:51:95:6F:36:96:F6:01:FB:2E:48:CD:AB:49:25:A2:3B
+ AF:88:7C:AD:51:95:6F:36:96:...:01:FB:2E:48:CD:AB:49:25:A2:3B
OK
- 85:16:78:FD:73:6E:CE:70:E0:31:5F:0D:3C:C8:6D:C4:2C:24:59:E1
+ 85:16:78:FD:73:6E:CE:70:E0:...:5F:0D:3C:C8:6D:C4:2C:24:59:E1
<a href="postconf.5.html#permit_auth_destination">permit_auth_destination</a>
</pre>
</blockquote>
"medium" with Postfix releases after the middle of 2015, and as a
result export-grade cipher suites are by default not used. </p>
+<p> With Postfix ≥ 3.6 export-grade Diffie-Hellman key exchange
+is no longer supported, and this parameter is silently ignored. </p>
+
<p> This feature is available in Postfix 2.8 and later. </p>
<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)
+POSTTLS-FINGER(1) General Commands Manual POSTTLS-FINGER(1)
<b>NAME</b>
posttls-finger - Probe the TLS properties of an ESMTP or LMTP server.
tificates. 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>)
+ <b>-d</b> <i>mdalg</i> (default: <b>$<a href="postconf.5.html#smtp_tls_fingerprint_digest">smtp_tls_fingerprint_digest</a></b>)
The message digest algorithm to use for reporting remote SMTP
server fingerprints and matching against user provided certifi-
cate fingerprints (with DANE TLSA records the algorithm is spec-
- ified in the DNS).
+ ified in the DNS). In Postfix versions prior to 3.6, the
+ default value was "sha1".
- <b>-f</b> Lookup the associated DANE TLSA RRset even when a hostname is
- not an alias and its address records lie in an unsigned zone.
+ <b>-f</b> Lookup the associated DANE TLSA RRset even when a hostname is
+ not an alias and its address records lie in an unsigned zone.
See <a href="postconf.5.html#smtp_tls_force_insecure_host_tlsa_lookup">smtp_tls_force_insecure_host_tlsa_lookup</a> for details.
<b>-F</b> <i>CAfile.pem</i> (default: none)
The PEM formatted CAfile for remote SMTP server certificate ver-
- ification. By default no CAfile is used and no public CAs are
+ ification. 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-finger. See
+ The minimum TLS cipher grade used by posttls-finger. 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 connection. See the
+ The hostname lookup methods used for the connection. See the
documentation of <a href="postconf.5.html#smtp_host_lookup">smtp_host_lookup</a> for syntax and semantics.
<b>-H</b> <i>chainfiles</i> (default: <i>none</i>)
List of files with a sequence PEM-encoded TLS client certificate
- chains. The list can be built-up incrementally, by specifying
- the option multiple times, or all at once via a comma or white-
- space separated list of filenames. Each chain starts with a
- private key, which is followed immediately by the corresponding
- certificate, and optionally by additional issuer certificates.
+ chains. The list can be built-up incrementally, by specifying
+ the option multiple times, or all at once via a comma or white-
+ space separated list of filenames. Each chain starts with a
+ private key, which is followed immediately by the corresponding
+ certificate, and optionally by additional issuer certificates.
Each new key begins a new chain for the corresponding algorithm.
- This option is mutually exclusive with the below <b>-k</b> and <b>-K</b>
+ This option is mutually exclusive with the below <b>-k</b> and <b>-K</b>
options.
<b>-k</b> <i>certfile</i> (default: <i>keyfile</i>)
- File with PEM-encoded TLS client certificate chain. This
+ File with PEM-encoded TLS client certificate chain. This
defaults to <i>keyfile</i> if one is specified.
<b>-K</b> <i>keyfile</i> (default: <i>certfile</i>)
- File with PEM-encoded TLS client private key. This defaults to
+ File with PEM-encoded TLS client private key. This defaults to
<i>certfile</i> if one is specified.
<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>
+ The security level for the connection, default <b>dane</b> or <b>secure</b>
depending on whether DNSSEC is available. For syntax and seman-
- tics, 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
+ tics, 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> security levels are not very useful.
+ Note, since <b>posttls-finger</b> does not actually deliver any email,
+ the <b>none</b>, <b>may</b> and <b>encrypt</b> security 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
+ 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
+ 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,
+ 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
+ These synonymous values yield a normal one-line summary
of the TLS connection.
<b>2, debug</b>
and verbose.
<b>3, ssl-expert</b>
- These synonymous values combine debug with ssl-hand-
+ These synonymous values combine debug with ssl-hand-
shake-packet-dump. For experts only.
<b>4, ssl-developer</b>
- These synonymous values combine ssl-expert with ssl-ses-
- sion-packet-dump. For experts only, and in most cases,
+ These synonymous values combine ssl-expert with ssl-ses-
+ sion-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 hand-
+ Turn on OpenSSL logging of the progress of the SSL hand-
shake.
<b>ssl-handshake-packet-dump</b>
- Log hexadecimal packet dumps of the SSL handshake; for
+ 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
+ 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 Certification Authorities to validate certifi-
- cates. So while this setting is recognized, you should
+ Logs trust chain verification problems. This is turned
+ on automatically at security levels that use peer names
+ signed by Certification Authorities to validate certifi-
+ cates. So while this setting is recognized, you should
never need to set it explicitly.
<b>peercert</b>
- This logs a one line summary of the remote SMTP server
+ This logs a one line summary of the remote SMTP server
certificate subject, issuer, and fingerprints.
<b>certmatch</b>
- This logs remote SMTP server certificate matching, show-
+ This logs remote SMTP server certificate matching, show-
ing the CN and each subjectAltName and which name
- matched. With DANE, logs matching of TLSA record
+ matched. With DANE, logs matching of TLSA record
trust-anchor and end-entity certificates.
- <b>cache</b> This logs session cache operations, showing whether ses-
- sion caching is effective with the remote SMTP server.
- Automatically used when reconnecting with the <b>-r</b> option;
+ <b>cache</b> This logs session cache operations, showing whether ses-
+ sion caching is effective with the remote SMTP server.
+ Automatically used when reconnecting with the <b>-r</b> option;
rarely needs to be set explicitly.
<b>verbose</b>
Enables verbose logging in the Postfix TLS driver;
includes all of peercert..cache and more.
- The default is <b>routine,certmatch</b>. After a reconnect, <b>peercert</b>,
+ The default is <b>routine,certmatch</b>. After a reconnect, <b>peercert</b>,
<b>certmatch</b> and <b>verbose</b> are automatically disabled while <b>cache</b> and
<b>summary</b> are enabled.
<b>-m</b> <i>count</i> (default: <b>5</b>)
- When the <b>-r</b> <i>delay</i> option is specified, the <b>-m</b> option determines
- the maximum number of reconnect attempts to use with a server
- behind a load balancer, to see whether connection caching is
- likely to be effective for this destination. Some MTAs don't
- expose the underlying server identity in their EHLO response;
- with these servers there will never be more than 1 reconnection
+ When the <b>-r</b> <i>delay</i> option is specified, the <b>-m</b> option determines
+ the maximum number of reconnect attempts to use with a server
+ behind a load balancer, to see whether connection caching is
+ likely to be effective for this destination. Some MTAs don't
+ expose the underlying server identity in their EHLO response;
+ with these servers there will never be more than 1 reconnection
attempt.
<b>-M</b> <i>insecure</i><b>_</b><i>mx</i><b>_</b><i>policy</i> (default: <b>dane</b>)
- The TLS policy for MX hosts with "secure" TLSA records when the
- nexthop destination security level is <b>dane</b>, but the MX record
+ The TLS policy for MX hosts with "secure" TLSA records when the
+ nexthop destination security level is <b>dane</b>, but the MX record
was found via an "insecure" MX lookup. See the <a href="postconf.5.html">main.cf</a> documen-
tation for smtp_tls_insecure_mx_policy for details.
<b>-o</b> <i>name=value</i>
- Specify zero or more times to override the value of the <a href="postconf.5.html">main.cf</a>
- parameter <i>name</i> with <i>value</i>. Possible use-cases include overrid-
- ing the values of TLS library parameters, or "<a href="postconf.5.html#myhostname">myhostname</a>" to
+ Specify zero or more times to override the value of the <a href="postconf.5.html">main.cf</a>
+ parameter <i>name</i> with <i>value</i>. Possible use-cases include overrid-
+ ing the values of TLS library parameters, or "<a href="postconf.5.html#myhostname">myhostname</a>" to
configure the SMTP EHLO name sent to the remote server.
<b>-p</b> <i>protocols</i> (default: !SSLv2)
- List of TLS protocols that posttls-finger will exclude or
+ List of TLS protocols that posttls-finger will exclude or
include. See <a href="postconf.5.html#smtp_tls_mandatory_protocols">smtp_tls_mandatory_protocols</a> for details.
<b>-P</b> <i>CApath/</i> (default: none)
- The OpenSSL CApath/ directory (indexed via c_rehash(1)) for
+ The OpenSSL CApath/ directory (indexed via c_rehash(1)) for
remote SMTP server certificate verification. By default no CAp-
ath is used and no public CAs are trusted.
<b>-r</b> <i>delay</i>
- With a cacheable TLS session, disconnect and reconnect after
+ With a cacheable TLS session, disconnect and reconnect after
<i>delay</i> seconds. Report whether the session is re-used. Retry if a
- new server is encountered, up to 5 times or as specified with
- the <b>-m</b> option. By default reconnection is disabled, specify a
+ new server is encountered, up to 5 times or as specified with
+ the <b>-m</b> option. By default reconnection is disabled, specify a
positive delay to enable this behavior.
<b>-s</b> <i>servername</i>
- The server name to send with the TLS Server Name Indication
- (SNI) extension. When the server has DANE TLSA records, this
- parameter is ignored and the TLSA base domain is used instead.
- Otherwise, SNI is not used by default, but can be enabled by
+ The server name to send with the TLS Server Name Indication
+ (SNI) extension. When the server has DANE TLSA records, this
+ parameter is ignored and the TLSA base domain is used instead.
+ Otherwise, SNI is not used by default, but can be enabled by
specifying the desired value with this option.
- <b>-S</b> Disable SMTP; that is, connect to an LMTP server. The default
- port for LMTP over TCP is 24. Alternative ports can specified
- by appending "<i>:servicename</i>" or ":<i>portnumber</i>" to the destination
+ <b>-S</b> Disable SMTP; that is, connect to an LMTP server. The default
+ port for LMTP over TCP is 24. Alternative ports can specified
+ by appending "<i>:servicename</i>" or ":<i>portnumber</i>" to the destination
argument.
<b>-t</b> <i>timeout</i> (default: <b>30</b>)
reading the remote server's 220 banner.
<b>-T</b> <i>timeout</i> (default: <b>30</b>)
- The SMTP/LMTP command timeout for EHLO/LHLO, STARTTLS and QUIT.
+ The SMTP/LMTP command timeout for EHLO/LHLO, STARTTLS and QUIT.
<b>-v</b> Enable verbose Postfix logging. Specify more than once to
increase the level of verbose logging.
ery attempt will be made until the mail is taken off
hold.
- This mode of operation is implemented by executing the
+ <b>#</b> The message is forced to expire. See the <a href="postsuper.1.html"><b>postsuper</b>(1)</a>
+ options <b>-e</b> or <b>-f</b>.
+
+ This mode of operation is implemented by executing the
<a href="postqueue.1.html"><b>postqueue</b>(1)</a> command.
<b>newaliases</b>
- Initialize the alias database. If no input file is specified
- (with the <b>-oA</b> option, see below), the program processes the
- file(s) specified with the <b><a href="postconf.5.html#alias_database">alias_database</a></b> configuration parame-
- ter. If no alias database type is specified, the program uses
- the type specified with the <b><a href="postconf.5.html#default_database_type">default_database_type</a></b> configuration
+ Initialize the alias database. If no input file is specified
+ (with the <b>-oA</b> option, see below), the program processes the
+ file(s) specified with the <b><a href="postconf.5.html#alias_database">alias_database</a></b> configuration parame-
+ ter. If no alias database type is specified, the program uses
+ the type specified with the <b><a href="postconf.5.html#default_database_type">default_database_type</a></b> configuration
parameter. This mode of operation is implemented by running the
<a href="postalias.1.html"><b>postalias</b>(1)</a> command.
Note: it may take a minute or so before an alias database update
- becomes visible. Use the "<b>postfix reload</b>" command to eliminate
+ becomes visible. Use the "<b>postfix reload</b>" command to eliminate
this delay.
- These and other features can be selected by specifying the appropriate
- combination of command-line options. Some features are controlled by
+ These and other features can be selected by specifying the appropriate
+ combination of command-line options. Some features are controlled by
parameters in the <a href="postconf.5.html"><b>main.cf</b></a> configuration file.
The following options are recognized:
<b>-Am</b> (ignored)
<b>-Ac</b> (ignored)
- Postfix sendmail uses the same configuration file regardless of
+ Postfix sendmail uses the same configuration file regardless of
whether or not a message is an initial submission.
<b>-B</b> <i>body</i><b>_</b><i>type</i>
The message body MIME type: <b>7BIT</b> or <b>8BITMIME</b>.
- <b>-bd</b> Go into daemon mode. This mode of operation is implemented by
+ <b>-bd</b> Go into daemon mode. This mode of operation is implemented by
executing the "<b>postfix start</b>" command.
<b>-bh</b> (ignored)
<b>-bi</b> Initialize alias database. See the <b>newaliases</b> command above.
- <b>-bl</b> Go into daemon mode. To accept only local connections as with
- Sendmail's <b>-bl</b> option, specify "<b><a href="postconf.5.html#inet_interfaces">inet_interfaces</a> = loopback</b>" in
+ <b>-bl</b> Go into daemon mode. To accept only local connections as with
+ Sendmail's <b>-bl</b> option, specify "<b><a href="postconf.5.html#inet_interfaces">inet_interfaces</a> = loopback</b>" in
the Postfix <a href="postconf.5.html"><b>main.cf</b></a> configuration file.
<b>-bm</b> Read mail from standard input and arrange for delivery. This is
<b>-bp</b> List the mail queue. See the <b>mailq</b> command above.
- <b>-bs</b> Stand-alone SMTP server mode. Read SMTP commands from standard
- input, and write responses to standard output. In stand-alone
- SMTP server mode, mail relaying and other access controls are
- disabled by default. To enable them, run the process as the
+ <b>-bs</b> Stand-alone SMTP server mode. Read SMTP commands from standard
+ input, and write responses to standard output. In stand-alone
+ SMTP server mode, mail relaying and other access controls are
+ disabled by default. To enable them, run the process as the
<b><a href="postconf.5.html#mail_owner">mail_owner</a></b> user.
- This mode of operation is implemented by running the <a href="smtpd.8.html"><b>smtpd</b>(8)</a>
+ This mode of operation is implemented by running the <a href="smtpd.8.html"><b>smtpd</b>(8)</a>
daemon.
- <b>-bv</b> Do not collect or deliver a message. Instead, send an email
- report after verifying each recipient address. This is useful
+ <b>-bv</b> Do not collect or deliver a message. Instead, send an email
+ report after verifying each recipient address. This is useful
for testing address rewriting and routing configurations.
This feature is available in Postfix version 2.1 and later.
<b>-C</b> <i>config</i><b>_</b><i>file</i>
<b>-C</b> <i>config</i><b>_</b><i>dir</i>
- The path name of the Postfix <a href="postconf.5.html"><b>main.cf</b></a> file, or of its parent
- directory. This information is ignored with Postfix versions
+ The path name of the Postfix <a href="postconf.5.html"><b>main.cf</b></a> file, or of its parent
+ directory. This information is ignored with Postfix versions
before 2.3.
With Postfix version 3.2 and later, a non-default directory must
- be authorized in the default <a href="postconf.5.html"><b>main.cf</b></a> file, through the alter-
+ be authorized in the default <a href="postconf.5.html"><b>main.cf</b></a> file, through the alter-
nate_config_directories or <a href="postconf.5.html#multi_instance_directories">multi_instance_directories</a> parame-
ters.
- With all Postfix versions, you can specify a directory pathname
- with the MAIL_CONFIG environment variable to override the loca-
+ With all Postfix versions, you can specify a directory pathname
+ with the MAIL_CONFIG environment variable to override the loca-
tion of configuration files.
<b>-F</b> <i>full</i><b>_</b><i>name</i>
- Set the sender full name. This overrides the NAME environment
+ Set the sender full name. This overrides the NAME environment
variable, and is used only with messages that have no <b>From:</b> mes-
sage header.
<b>-f</b> <i>sender</i>
- Set the envelope sender address. This is the address where
+ Set the envelope sender address. This is the address where
delivery problems are sent to. With Postfix versions before 2.1,
- the <b>Errors-To:</b> message header overrides the error return
+ the <b>Errors-To:</b> message header overrides the error return
address.
- <b>-G</b> Gateway (relay) submission, as opposed to initial user submis-
- sion. Either do not rewrite addresses at all, or update incom-
- plete addresses with the domain information specified with
+ <b>-G</b> Gateway (relay) submission, as opposed to initial user submis-
+ sion. Either do not rewrite addresses at all, or update incom-
+ plete addresses with the domain information specified with
<b><a href="postconf.5.html#remote_header_rewrite_domain">remote_header_rewrite_domain</a></b>.
This option is ignored before Postfix version 2.3.
<b>-h</b> <i>hop</i><b>_</b><i>count</i> (ignored)
- Hop count limit. Use the <b><a href="postconf.5.html#hopcount_limit">hopcount_limit</a></b> configuration parameter
+ Hop count limit. Use the <b><a href="postconf.5.html#hopcount_limit">hopcount_limit</a></b> configuration parameter
instead.
<b>-I</b> Initialize alias database. See the <b>newaliases</b> command above.
- <b>-i</b> When reading a message from standard input, don't treat a line
+ <b>-i</b> When reading a message from standard input, don't treat a line
with only a <b>.</b> character as the end of input.
<b>-L</b> <i>label</i> (ignored)
- The logging label. Use the <b><a href="postconf.5.html#syslog_name">syslog_name</a></b> configuration parameter
+ The logging label. Use the <b><a href="postconf.5.html#syslog_name">syslog_name</a></b> configuration parameter
instead.
<b>-m</b> (ignored)
Backwards compatibility.
<b>-N</b> <i>dsn</i> (default: 'delay, failure')
- Delivery status notification control. Specify either a
+ Delivery status notification control. Specify either a
comma-separated list with one or more of <b>failure</b> (send notifica-
- tion when delivery fails), <b>delay</b> (send notification when deliv-
- ery is delayed), or <b>success</b> (send notification when the message
+ tion when delivery fails), <b>delay</b> (send notification when deliv-
+ ery is delayed), or <b>success</b> (send notification when the message
is delivered); or specify <b>never</b> (don't send any notifications at
all).
Backwards compatibility.
<b>-oA</b><i>alias</i><b>_</b><i>database</i>
- Non-default alias database. Specify <i>pathname</i> or <i>type</i>:<i>pathname</i>.
+ Non-default alias database. Specify <i>pathname</i> or <i>type</i>:<i>pathname</i>.
See <a href="postalias.1.html"><b>postalias</b>(1)</a> for details.
<b>-O</b> <i>option=value</i> (ignored)
- Set the named <i>option</i> to <i>value</i>. Use the equivalent configuration
+ Set the named <i>option</i> to <i>value</i>. Use the equivalent configuration
parameter in <a href="postconf.5.html"><b>main.cf</b></a> instead.
<b>-o7</b> (ignored)
<b>-o8</b> (ignored)
- To send 8-bit or binary content, use an appropriate MIME encap-
+ To send 8-bit or binary content, use an appropriate MIME encap-
sulation and specify the appropriate <b>-B</b> command-line option.
- <b>-oi</b> When reading a message from standard input, don't treat a line
+ <b>-oi</b> When reading a message from standard input, don't treat a line
with only a <b>.</b> character as the end of input.
<b>-om</b> (ignored)
The sender is never eliminated from alias etc. expansions.
<b>-o</b> <i>x value</i> (ignored)
- Set option <i>x</i> to <i>value</i>. Use the equivalent configuration parame-
+ Set option <i>x</i> to <i>value</i>. Use the equivalent configuration parame-
ter in <a href="postconf.5.html"><b>main.cf</b></a> instead.
<b>-r</b> <i>sender</i>
- Set the envelope sender address. This is the address where
+ Set the envelope sender address. This is the address where
delivery problems are sent to. With Postfix versions before 2.1,
- the <b>Errors-To:</b> message header overrides the error return
+ the <b>Errors-To:</b> message header overrides the error return
address.
<b>-R</b> <i>return</i>
- Delivery status notification control. Specify "hdrs" to return
- only the header when a message bounces, "full" to return a full
+ Delivery status notification control. Specify "hdrs" to return
+ only the header when a message bounces, "full" to return a full
copy (the default behavior).
The <b>-R</b> option specifies an upper bound; Postfix will return only
- the header, when a full copy would exceed the <a href="postconf.5.html#bounce_size_limit">bounce_size_limit</a>
+ the header, when a full copy would exceed the <a href="postconf.5.html#bounce_size_limit">bounce_size_limit</a>
setting.
This option is ignored before Postfix version 2.10.
- <b>-q</b> Attempt to deliver all queued mail. This is implemented by exe-
+ <b>-q</b> Attempt to deliver all queued mail. This is implemented by exe-
cuting the <a href="postqueue.1.html"><b>postqueue</b>(1)</a> command.
- Warning: flushing undeliverable mail frequently will result in
+ Warning: flushing undeliverable mail frequently will result in
poor delivery performance of all other mail.
<b>-q</b><i>interval</i> (ignored)
<b>-qI</b><i>queueid</i>
Schedule immediate delivery of mail with the specified queue ID.
- This option is implemented by executing the <a href="postqueue.1.html"><b>postqueue</b>(1)</a> com-
+ This option is implemented by executing the <a href="postqueue.1.html"><b>postqueue</b>(1)</a> com-
mand, and is available with Postfix version 2.4 and later.
<b>-qR</b><i>site</i>
- Schedule immediate delivery of all mail that is queued for the
- named <i>site</i>. This option accepts only <i>site</i> names that are eligi-
- ble for the "fast flush" service, and is implemented by execut-
+ Schedule immediate delivery of all mail that is queued for the
+ named <i>site</i>. This option accepts only <i>site</i> names that are eligi-
+ ble for the "fast flush" service, and is implemented by execut-
ing the <a href="postqueue.1.html"><b>postqueue</b>(1)</a> command. See <a href="flush.8.html"><b>flush</b>(8)</a> for more information
about the "fast flush" service.
<b>-qS</b><i>site</i>
- This command is not implemented. Use the slower "<b>sendmail -q</b>"
+ This command is not implemented. Use the slower "<b>sendmail -q</b>"
command instead.
- <b>-t</b> Extract recipients from message headers. These are added to any
+ <b>-t</b> Extract recipients from message headers. These are added to any
recipients specified on the command line.
With Postfix versions prior to 2.1, this option requires that no
This feature is available in Postfix 2.3 and later.
<b>-XV</b> (Postfix 2.2 and earlier: <b>-V</b>)
- Variable Envelope Return Path. Given an envelope sender address
- of the form <i>owner-listname</i>@<i>origin</i>, each recipient <i>user</i>@<i>domain</i>
+ Variable Envelope Return Path. Given an envelope sender address
+ of the form <i>owner-listname</i>@<i>origin</i>, each recipient <i>user</i>@<i>domain</i>
receives mail with a personalized envelope sender address.
- By default, the personalized envelope sender address is
- <i>owner-listname</i><b>+</b><i>user</i><b>=</b><i>domain</i>@<i>origin</i>. The default <b>+</b> and <b>=</b> charac-
- ters are configurable with the <b><a href="postconf.5.html#default_verp_delimiters">default_verp_delimiters</a></b> configu-
+ By default, the personalized envelope sender address is
+ <i>owner-listname</i><b>+</b><i>user</i><b>=</b><i>domain</i>@<i>origin</i>. The default <b>+</b> and <b>=</b> charac-
+ ters are configurable with the <b><a href="postconf.5.html#default_verp_delimiters">default_verp_delimiters</a></b> configu-
ration parameter.
<b>-XV</b><i>xy</i> (Postfix 2.2 and earlier: <b>-V</b><i>xy</i>)
- As <b>-XV</b>, but uses <i>x</i> and <i>y</i> as the VERP delimiter characters,
+ As <b>-XV</b>, but uses <i>x</i> and <i>y</i> as the VERP delimiter characters,
instead of the characters specified with the <b><a href="postconf.5.html#default_verp_delimiters">default_verp_delim</a>-</b>
<b><a href="postconf.5.html#default_verp_delimiters">iters</a></b> configuration parameter.
<b>-v</b> Send an email report of the first delivery attempt (Postfix ver-
- sions 2.1 and later). Mail delivery always happens in the back-
- ground. When multiple <b>-v</b> options are given, enable verbose log-
+ sions 2.1 and later). Mail delivery always happens in the back-
+ ground. When multiple <b>-v</b> options are given, enable verbose log-
ging for debugging purposes.
<b>-X</b> <i>log</i><b>_</b><i>file</i> (ignored)
configuration parameters instead.
<b>SECURITY</b>
- By design, this program is not set-user (or group) id. However, it must
- handle data from untrusted, possibly remote, users. Thus, the usual
- precautions need to be taken against malicious inputs.
+ By design, this program is not set-user (or group) id. It is prepared
+ to handle message content from untrusted, possibly remote, users.
+
+ However, like most Postfix programs, this program does not enforce a
+ security policy on its command-line arguments. Instead, it relies on
+ the UNIX system to enforce access policies based on the effective user
+ and group IDs of the process. Concretely, this means that running Post-
+ fix commands as root (from sudo or equivalent) on behalf of a non-root
+ user is likely to create privilege escalation opportunities.
+
+ If an application runs any Postfix programs on behalf of users that do
+ not have normal shell access to Postfix commands, then that application
+ MUST restrict user-specified command-line arguments to avoid privilege
+ escalation.
+
+ <b>o</b> Filter all command-line arguments, for example arguments that
+ contain a pathname or that specify a database access method.
+ These pathname checks must reject user-controlled symlinks or
+ hardlinks to sensitive files, and must not be vulnerable to TOC-
+ TOU race attacks.
+
+ <b>o</b> Disable command options processing for all command arguments
+ that contain user-specified data. For example, the Postfix <a href="sendmail.1.html"><b>send-</b></a>
+ <a href="sendmail.1.html"><b>mail</b>(1)</a> command line MUST be structured as follows:
+
+ <b>/path/to/sendmail</b> <i>system-arguments</i> <b>--</b> <i>user-arguments</i>
+
+ Here, the "<b>--</b>" disables command option processing for all
+ <i>user-arguments</i> that follow.
+
+ Without the "<b>--</b>", a malicious user could enable Postfix <a href="sendmail.1.html"><b>send-</b></a>
+ <a href="sendmail.1.html"><b>mail</b>(1)</a> command options, by specifying an email address that
+ starts with "<b>-</b>".
<b>DIAGNOSTICS</b>
- Problems are logged to <b>syslogd</b>(8) or <a href="postlogd.8.html"><b>postlogd</b>(8)</a>, and to the standard
+ Problems are logged to <b>syslogd</b>(8) or <a href="postlogd.8.html"><b>postlogd</b>(8)</a>, and to the standard
error stream.
<b>ENVIRONMENT</b>
Enable debugging with an external command, as specified with the
<b><a href="postconf.5.html#debugger_command">debugger_command</a></b> configuration parameter.
- <b>NAME</b> The sender full name. This is used only with messages that have
+ <b>NAME</b> The sender full name. This is used only with messages that have
no <b>From:</b> message header. See also the <b>-F</b> option above.
<b>CONFIGURATION PARAMETERS</b>
- The following <a href="postconf.5.html"><b>main.cf</b></a> parameters are especially relevant to this pro-
- gram. The text below provides only a parameter summary. See <a href="postconf.5.html"><b>post-</b></a>
+ The following <a href="postconf.5.html"><b>main.cf</b></a> parameters are especially relevant to this pro-
+ gram. The text below provides only a parameter summary. See <a href="postconf.5.html"><b>post-</b></a>
<a href="postconf.5.html"><b>conf</b>(5)</a> for more details including examples.
<b>COMPATIBILITY CONTROLS</b>
line endings from <CR><LF> into UNIX format (<LF>).
<b>TROUBLE SHOOTING CONTROLS</b>
- The <a href="DEBUG_README.html">DEBUG_README</a> file gives examples of how to troubleshoot a Postfix
+ The <a href="DEBUG_README.html">DEBUG_README</a> file gives examples of how to troubleshoot a Postfix
system.
<b><a href="postconf.5.html#debugger_command">debugger_command</a> (empty)</b>
invoked with the -D option.
<b><a href="postconf.5.html#debug_peer_level">debug_peer_level</a> (2)</b>
- The increment in verbose logging level when a remote client or
- server matches a pattern in the <a href="postconf.5.html#debug_peer_list">debug_peer_list</a> parameter.
+ The increment in verbose logging level when a nexthop destina-
+ tion, remote client or server name or network address matches a
+ pattern given with the <a href="postconf.5.html#debug_peer_list">debug_peer_list</a> parameter.
<b><a href="postconf.5.html#debug_peer_list">debug_peer_list</a> (empty)</b>
- Optional list of remote client or server hostname or network
- address patterns that cause the verbose logging level to
- increase by the amount specified in $<a href="postconf.5.html#debug_peer_level">debug_peer_level</a>.
+ Optional list of nexthop destination, remote client or server
+ name or network address patterns that, if matched, cause the
+ verbose logging level to increase by the amount specified in
+ $<a href="postconf.5.html#debug_peer_level">debug_peer_level</a>.
<b>ACCESS CONTROLS</b>
Available in Postfix version 2.2 and later:
List of users who are authorized to view the queue.
<b><a href="postconf.5.html#authorized_submit_users">authorized_submit_users</a> (<a href="DATABASE_README.html#types">static</a>:anyone)</b>
- List of users who are authorized to submit mail with the <a href="sendmail.1.html"><b>send-</b></a>
+ List of users who are authorized to submit mail with the <a href="sendmail.1.html"><b>send-</b></a>
<a href="sendmail.1.html"><b>mail</b>(1)</a> command (and with the privileged <a href="postdrop.1.html"><b>postdrop</b>(1)</a> helper com-
mand).
<b>RESOURCE AND RATE CONTROLS</b>
<b><a href="postconf.5.html#bounce_size_limit">bounce_size_limit</a> (50000)</b>
- The maximal amount of original message text that is sent in a
+ The maximal amount of original message text that is sent in a
non-delivery notification.
<b><a href="postconf.5.html#fork_attempts">fork_attempts</a> (5)</b>
in the primary message headers.
<b><a href="postconf.5.html#queue_run_delay">queue_run_delay</a> (300s)</b>
- The time between <a href="QSHAPE_README.html#deferred_queue">deferred queue</a> scans by the queue manager;
+ The time between <a href="QSHAPE_README.html#deferred_queue">deferred queue</a> scans by the queue manager;
prior to Postfix 2.4 the default value was 1000s.
<b>FAST FLUSH CONTROLS</b>
- The <a href="ETRN_README.html">ETRN_README</a> file describes configuration and operation details for
+ The <a href="ETRN_README.html">ETRN_README</a> file describes configuration and operation details for
the Postfix "fast flush" service.
<b><a href="postconf.5.html#fast_flush_domains">fast_flush_domains</a> ($<a href="postconf.5.html#relay_domains">relay_domains</a>)</b>
tion logfiles with mail that is queued to those destinations.
<b>VERP CONTROLS</b>
- The <a href="VERP_README.html">VERP_README</a> file describes configuration and operation details of
+ The <a href="VERP_README.html">VERP_README</a> file describes configuration and operation details of
Postfix support for variable envelope return path addresses.
<b><a href="postconf.5.html#default_verp_delimiters">default_verp_delimiters</a> (+=)</b>
The two default VERP delimiter characters.
<b><a href="postconf.5.html#verp_delimiter_filter">verp_delimiter_filter</a> (-=+)</b>
- The characters Postfix accepts as VERP delimiter characters on
+ The characters Postfix accepts as VERP delimiter characters on
the Postfix <a href="sendmail.1.html"><b>sendmail</b>(1)</a> command line and in SMTP commands.
<b>MISCELLANEOUS CONTROLS</b>
<b><a href="postconf.5.html#alias_database">alias_database</a> (see 'postconf -d' output)</b>
- The alias databases for <a href="local.8.html"><b>local</b>(8)</a> delivery that are updated with
+ The alias databases for <a href="local.8.html"><b>local</b>(8)</a> delivery that are updated with
"<b>newaliases</b>" or with "<b>sendmail -bi</b>".
<b><a href="postconf.5.html#command_directory">command_directory</a> (see 'postconf -d' output)</b>
The location of all postfix administrative commands.
<b><a href="postconf.5.html#config_directory">config_directory</a> (see 'postconf -d' output)</b>
- The default location of the Postfix <a href="postconf.5.html">main.cf</a> and <a href="master.5.html">master.cf</a> con-
+ The default location of the Postfix <a href="postconf.5.html">main.cf</a> and <a href="master.5.html">master.cf</a> con-
figuration files.
<b><a href="postconf.5.html#daemon_directory">daemon_directory</a> (see 'postconf -d' output)</b>
and <a href="postmap.1.html"><b>postmap</b>(1)</a> commands.
<b><a href="postconf.5.html#delay_warning_time">delay_warning_time</a> (0h)</b>
- The time after which the sender receives a copy of the message
+ The time after which the sender receives a copy of the message
headers of mail that is still queued.
<b><a href="postconf.5.html#import_environment">import_environment</a> (see 'postconf -d' output)</b>
- The list of environment parameters that a privileged Postfix
- process will import from a non-Postfix parent process, or
+ The list of environment parameters that a privileged Postfix
+ process will import from a non-Postfix parent process, or
name=value environment overrides.
<b><a href="postconf.5.html#mail_owner">mail_owner</a> (postfix)</b>
- The UNIX system account that owns the Postfix queue and most
+ The UNIX system account that owns the Postfix queue and most
Postfix daemon processes.
<b><a href="postconf.5.html#queue_directory">queue_directory</a> (see 'postconf -d' output)</b>
The location of the Postfix top-level queue directory.
<b><a href="postconf.5.html#remote_header_rewrite_domain">remote_header_rewrite_domain</a> (empty)</b>
- Don't rewrite message headers from remote clients at all when
- this parameter is empty; otherwise, rewrite message headers and
+ Don't rewrite message headers from remote clients at all when
+ this parameter is empty; otherwise, rewrite message headers and
append the specified domain name to incomplete addresses.
<b><a href="postconf.5.html#syslog_facility">syslog_facility</a> (mail)</b>
The syslog facility of Postfix logging.
<b><a href="postconf.5.html#syslog_name">syslog_name</a> (see 'postconf -d' output)</b>
- A prefix that is prepended to the process name in syslog
+ A prefix that is prepended to the process name in syslog
records, so that, for example, "smtpd" becomes "prefix/smtpd".
Postfix 3.2 and later:
<b><a href="postconf.5.html#alternate_config_directories">alternate_config_directories</a> (empty)</b>
A list of non-default Postfix configuration directories that may
- be specified with "-c <a href="postconf.5.html#config_directory">config_directory</a>" on the command line (in
- the case of <a href="sendmail.1.html"><b>sendmail</b>(1)</a>, with the "-C" option), or via the
+ be specified with "-c <a href="postconf.5.html#config_directory">config_directory</a>" on the command line (in
+ the case of <a href="sendmail.1.html"><b>sendmail</b>(1)</a>, with the "-C" option), or via the
MAIL_CONFIG environment parameter.
<b><a href="postconf.5.html#multi_instance_directories">multi_instance_directories</a> (empty)</b>
- An optional list of non-default Postfix configuration directo-
- ries; these directories belong to additional Postfix instances
- that share the Postfix executable files and documentation with
- the default Postfix instance, and that are started, stopped,
+ An optional list of non-default Postfix configuration directo-
+ ries; these directories belong to additional Postfix instances
+ that share the Postfix executable files and documentation with
+ the default Postfix instance, and that are started, stopped,
etc., together with the default Postfix instance.
<b>FILES</b>
syslogd(8), system logging
<b>README_FILES</b>
- Use "<b>postconf <a href="postconf.5.html#readme_directory">readme_directory</a></b>" or "<b>postconf <a href="postconf.5.html#html_directory">html_directory</a></b>" to locate
+ Use "<b>postconf <a href="postconf.5.html#readme_directory">readme_directory</a></b>" or "<b>postconf <a href="postconf.5.html#html_directory">html_directory</a></b>" to locate
this information.
<a href="DEBUG_README.html">DEBUG_README</a>, Postfix debugging howto
<a href="ETRN_README.html">ETRN_README</a>, Postfix ETRN howto
for the "fingerprint" TLS security level (<b><a href="postconf.5.html#smtp_tls_security_level">smtp_tls_secu</a>-</b>
<b><a href="postconf.5.html#smtp_tls_security_level">rity_level</a></b> = fingerprint).
- <b><a href="postconf.5.html#smtp_tls_fingerprint_digest">smtp_tls_fingerprint_digest</a> (md5)</b>
+ <b><a href="postconf.5.html#smtp_tls_fingerprint_digest">smtp_tls_fingerprint_digest</a> (see 'postconf -d' output)</b>
The message digest algorithm used to construct remote SMTP
server certificate fingerprints.
<b>TROUBLE SHOOTING CONTROLS</b>
<b><a href="postconf.5.html#debug_peer_level">debug_peer_level</a> (2)</b>
- The increment in verbose logging level when a next-hop destina-
+ The increment in verbose logging level when a nexthop destina-
tion, remote client or server name or network address matches a
pattern given with the <a href="postconf.5.html#debug_peer_list">debug_peer_list</a> parameter.
<b><a href="postconf.5.html#debug_peer_list">debug_peer_list</a> (empty)</b>
- Optional list of next-hop destination, remote client or server
+ Optional list of nexthop destination, remote client or server
name or network address patterns that, if matched, cause the
verbose logging level to increase by the amount specified in
$<a href="postconf.5.html#debug_peer_level">debug_peer_level</a>.
Available in Postfix version 2.5 and later:
- <b><a href="postconf.5.html#smtpd_tls_fingerprint_digest">smtpd_tls_fingerprint_digest</a> (md5)</b>
+ <b><a href="postconf.5.html#smtpd_tls_fingerprint_digest">smtpd_tls_fingerprint_digest</a> (see 'postconf -d' output)</b>
The message digest algorithm to construct remote SMTP
client-certificate fingerprints or public key fingerprints
(Postfix 2.9 and later) for <b><a href="postconf.5.html#check_ccert_access">check_ccert_access</a></b> and <b>per-</b>
or debugger.
<b><a href="postconf.5.html#debug_peer_level">debug_peer_level</a> (2)</b>
- The increment in verbose logging level when a remote client or
- server matches a pattern in the <a href="postconf.5.html#debug_peer_list">debug_peer_list</a> parameter.
+ The increment in verbose logging level when a nexthop destina-
+ tion, remote client or server name or network address matches a
+ pattern given with the <a href="postconf.5.html#debug_peer_list">debug_peer_list</a> parameter.
<b><a href="postconf.5.html#debug_peer_list">debug_peer_list</a> (empty)</b>
- Optional list of remote client or server hostname or network
- address patterns that cause the verbose logging level to
- increase by the amount specified in $<a href="postconf.5.html#debug_peer_level">debug_peer_level</a>.
+ Optional list of nexthop destination, remote client or server
+ name or network address patterns that, if matched, cause the
+ verbose logging level to increase by the amount specified in
+ $<a href="postconf.5.html#debug_peer_level">debug_peer_level</a>.
<b><a href="postconf.5.html#error_notice_recipient">error_notice_recipient</a> (postmaster)</b>
The recipient of postmaster notifications about mail delivery
\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)"
+.IP "\fB\-d \fImdalg\fR (default: \fB$smtp_tls_fingerprint_digest\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).
+in the DNS). In Postfix versions prior to 3.6, the default value
+was "sha1".
.IP "\fB\-f\fR"
Lookup the associated DANE TLSA RRset even when a hostname is not an
alias and its address records lie in an unsigned zone. See
.IP \fB!\fR
The message is in the \fBhold\fR queue, i.e. no further delivery
attempt will be made until the mail is taken off hold.
+.IP \fB#\fR
+The message is forced to expire. See the \fBpostsuper\fR(1)
+options \fB\-e\fR or \fB\-f\fR.
.RE
.IP
This mode of operation is implemented by executing the
.nf
.ad
.fi
-By design, this program is not set\-user (or group) id. However,
-it must handle data from untrusted, possibly remote, users.
-Thus, the usual precautions need to be taken against malicious
-inputs.
+By design, this program is not set\-user (or group) id.
+It is prepared to handle message content from untrusted,
+possibly remote, users.
+
+However, like most Postfix programs, this program does not
+enforce a security policy on its command\-line arguments.
+Instead, it relies on the UNIX system to enforce access
+policies based on the effective user and group IDs of the
+process. Concretely, this means that running Postfix commands
+as root (from sudo or equivalent) on behalf of a non\-root
+user is likely to create privilege escalation opportunities.
+
+If an application runs any Postfix programs on behalf of
+users that do not have normal shell access to Postfix
+commands, then that application MUST restrict user\-specified
+command\-line arguments to avoid privilege escalation.
+.IP \(bu
+Filter all command\-line arguments, for example arguments
+that contain a pathname or that specify a database access
+method. These pathname checks must reject user\-controlled
+symlinks or hardlinks to sensitive files, and must not be
+vulnerable to TOCTOU race attacks.
+.IP \(bu
+Disable command options processing for all command arguments
+that contain user\-specified data. For example, the Postfix
+\fBsendmail\fR(1) command line MUST be structured as follows:
+
+.nf
+ \fB/path/to/sendmail\fR \fIsystem\-arguments\fR \fB\-\-\fR \fIuser\-arguments\fR
+.fi
+
+Here, the "\fB\-\-\fR" disables command option processing for
+all \fIuser\-arguments\fR that follow.
+.IP
+Without the "\fB\-\-\fR", a malicious user could enable Postfix
+\fBsendmail\fR(1) command options, by specifying an email
+address that starts with "\fB\-\fR".
.SH DIAGNOSTICS
.ad
.fi
The external command to execute when a Postfix daemon program is
invoked with the \-D option.
.IP "\fBdebug_peer_level (2)\fR"
-The increment in verbose logging level when a remote client or
-server matches a pattern in the debug_peer_list parameter.
+The increment in verbose logging level when a nexthop destination,
+remote client or server name or network address matches a pattern
+given with the debug_peer_list parameter.
.IP "\fBdebug_peer_list (empty)\fR"
-Optional list of remote client or server hostname or network
-address patterns that cause the verbose logging level to increase
-by the amount specified in $debug_peer_level.
+Optional list of nexthop destination, remote client or server
+name or network address patterns that, if matched, cause the verbose
+logging level to increase by the amount specified in $debug_peer_level.
.SH "ACCESS CONTROLS"
.na
.nf
configuration parameter. See there for details.
.PP
This feature is available in Postfix 2.5 and later.
-.SH lmtp_tls_fingerprint_digest (default: md5)
+.SH lmtp_tls_fingerprint_digest (default: see "postconf \-d" output)
The LMTP\-specific version of the smtp_tls_fingerprint_digest
configuration parameter. See there for details.
.PP
smtpd_tls_fingerprint_digest parameter (hard\-coded as md5 prior to
Postfix version 2.5).
.br
+The default algorithm is \fBsha256\fR with Postfix >= 3.6
+and the \fBcompatibility_level\fR set to 3 or higher. With Postfix
+<= 3.5, the default algorithm is \fBmd5\fR. The best\-practice
+algorithm is now \fBsha256\fR. Recent advances in hash function
+cryptanalysis have led to md5 and sha1 being deprecated in favor of
+sha256. However, as long as there are no known "second pre\-image"
+attacks against the older algorithms, their use in this context, though
+not recommended, is still likely safe.
+.br
.IP "\fBpermit_tls_all_clientcerts \fR"
Append the domain name in $myorigin or $mydomain when the
remote SMTP client TLS certificate is successfully verified, regardless of
smtpd_tls_fingerprint_digest parameter (hard\-coded as md5 prior to
Postfix version 2.5).
.PP
+The default algorithm is \fBsha256\fR with Postfix >= 3.6
+and the \fBcompatibility_level\fR set to 3 or higher. With Postfix
+<= 3.5, the default algorithm is \fBmd5\fR. The best\-practice
+algorithm is now \fBsha256\fR. Recent advances in hash function
+cryptanalysis have led to md5 and sha1 being deprecated in favor of
+sha256. However, as long as there are no known "second pre\-image"
+attacks against the older algorithms, their use in this context, though
+not recommended, is still likely safe.
+.PP
Postfix lookup tables are in the form of (key, value) pairs.
Since we only need the key, the value can be chosen freely, e.g.
the name of the user or host:
an appropriate \fBaccess\fR(5) policy for each client.
See RESTRICTION_CLASS_README.
.PP
-\fBNote:\fR Postfix 2.9.0-2.9.5 computed the public key
-fingerprint incorrectly. To use public\-key fingerprints, upgrade
-to Postfix 2.9.6 or later.
-.PP
This feature is available with Postfix version 2.2.
.SH relay_destination_concurrency_limit (default: $default_destination_concurrency_limit)
The maximal number of parallel deliveries to the same destination
digest algorithm is selected via the \fBsmtp_tls_fingerprint_digest\fR
parameter.
.PP
+The colons between each pair of nibbles in the fingerprint value
+are optional (Postfix >= 3.6). These were required in earlier
+Postfix releases.
+.PP
When an \fBsmtp_tls_policy_maps\fR table entry specifies the
"fingerprint" security level, any "match" attributes in that entry specify
the list of valid fingerprints for the corresponding destination. Multiple
.ft C
relayhost = [mailhub.example.com]
smtp_tls_security_level = fingerprint
-smtp_tls_fingerprint_digest = md5
+smtp_tls_fingerprint_digest = sha256
smtp_tls_fingerprint_cert_match =
- 3D:95:34:51:24:66:33:B9:D2:40:99:C0:C1:17:0B:D1
- EC:3B:2D:B0:5B:B1:FB:6D:20:A3:9D:72:F6:8D:12:35
+ cd:fc:d8:db:f8:c4:82:96:6c:...:28:71:e8:f5:8d:a5:0d:9b:d4:a6
+ dd:5c:ef:f5:c3:bc:64:25:36:...:99:36:06:ce:40:ef:de:2e:ad:a4
.fi
.ad
.ft R
.ft C
/etc/postfix/main.cf:
smtp_tls_policy_maps = hash:/etc/postfix/tls_policy
- smtp_tls_fingerprint_digest = md5
+ smtp_tls_fingerprint_digest = sha256
.fi
.ad
.ft R
.ft C
/etc/postfix/tls_policy:
example.com fingerprint
- match=3D:95:34:51:24:66:33:B9:D2:40:99:C0:C1:17:0B:D1
- match=EC:3B:2D:B0:5B:B1:FB:6D:20:A3:9D:72:F6:8D:12:35
+ match=51:e9:af:2e:1e:40:1f:...:64:0a:30:35:2d:09:16:31:5a:eb:82:76
+ match=b6:b4:72:34:e2:59:cd:...:c2:ca:63:0d:4d:cc:2c:7d:84:de:e6:2f
.fi
.ad
.ft R
.in -4
.PP
This feature is available in Postfix 2.5 and later.
-.SH smtp_tls_fingerprint_digest (default: md5)
+.SH smtp_tls_fingerprint_digest (default: see "postconf \-d" output)
The message digest algorithm used to construct remote SMTP server
certificate fingerprints. At the "fingerprint" TLS security level
(\fBsmtp_tls_security_level\fR = fingerprint), the server certificate is
attacks, it is not feasible to create a new public key and a matching
certificate (or public/private key\-pair) that has the same fingerprint.
.PP
-The default algorithm is \fBmd5\fR; this is consistent with
-the backwards compatible setting of the digest used to verify client
-certificates in the SMTP server.
+The default algorithm is \fBsha256\fR with Postfix >= 3.6
+and the \fBcompatibility_level\fR set to 3 or higher. With Postfix
+<= 3.5, the default algorithm is \fBmd5\fR.
.PP
-The best practice algorithm is now \fBsha1\fR. Recent advances in hash
-function cryptanalysis have led to md5 being deprecated in favor of sha1.
-However, as long as there are no known "second pre\-image" attacks
-against md5, its use in this context can still be considered safe.
+The best\-practice algorithm is now \fBsha256\fR. Recent advances in hash
+function cryptanalysis have led to md5 and sha1 being deprecated in favor of
+sha256. However, as long as there are no known "second pre\-image" attacks
+against the older algorithms, their use in this context, though not
+recommended, is still likely safe.
.PP
While additional digest algorithms are often available with OpenSSL's
libcrypto, only those used by libssl in SSL cipher suites are available to
-Postfix. For now this means just md5 or sha1.
+Postfix. You'll likely find support for md5, sha1, sha256 and sha512.
.PP
To find the fingerprint of a specific certificate file, with a
specific digest algorithm, run:
.nf
.na
.ft C
-$ openssl x509 \-noout \-fingerprint \-sha1 \-in cert.pem
-SHA1 Fingerprint=D4:6A:AB:19:24:79:F8:32:BB:A6:CB:66:82:C0:8E:9B:EE:29:A8:1A
+$ openssl x509 \-noout \-fingerprint \-sha256 \-in cert.pem
+SHA256 Fingerprint=D4:6A:AB:19:24:...:BB:A6:CB:66:82:C0:8E:9B:EE:29:A8:1A
.fi
.ad
.ft R
command that converts the key to DER and then to the "dgst" command
to compute the fingerprint.
.PP
-The actual command to transform the key to DER format depends
-on the version of OpenSSL used. With OpenSSL 1.0.0 and later, the
-"pkey" command supports all key types. With OpenSSL 0.9.8 and
-earlier, the key type is always RSA (nobody uses DSA, and EC
-keys are not fully supported by 0.9.8), so the "rsa" command is
-used.
+The actual command to transform the key to DER format depends on the
+version of OpenSSL used. As of OpenSSL 1.0.0, the "pkey" command supports
+all key types.
.sp
.in +4
.nf
.na
.ft C
-# OpenSSL 1.0 with all certificates and SHA\-1 fingerprints.
+# OpenSSL >= 1.0 with SHA\-256 fingerprints.
$ openssl x509 \-in cert.pem \-noout \-pubkey |
openssl pkey \-pubin \-outform DER |
- openssl dgst \-sha1 \-c
-(stdin)= 64:3f:1f:f6:e5:1e:d4:2a:56:8b:fc:09:1a:61:98:b5:bc:7c:60:58
-.fi
-.ad
-.ft R
-.in -4
-.sp
-.in +4
-.nf
-.na
-.ft C
-# OpenSSL 0.9.8 with RSA certificates and MD5 fingerprints.
-$ openssl x509 \-in cert.pem \-noout \-pubkey |
- openssl rsa \-pubin \-outform DER |
- openssl dgst \-md5 \-c
-(stdin)= f4:62:60:f6:12:8f:d5:8d:28:4d:13:a7:db:b2:ff:50
+ openssl dgst \-sha256 \-c
+(stdin)= 64:3f:1f:f6:e5:1e:d4:2a:56:...:fc:09:1a:61:98:b5:bc:7c:60:58
.fi
.ad
.ft R
fingerprint and public key fingerprint when the TLS loglevel is 2 or
higher.
.PP
-\fBNote:\fR Postfix 2.9.0-2.9.5 computed the public key
-fingerprint incorrectly. To use public\-key fingerprints, upgrade
-to Postfix 2.9.6 or later.
-.PP
This feature is available in Postfix 2.5 and later.
.SH smtp_tls_force_insecure_host_tlsa_lookup (default: no)
Lookup the associated DANE TLSA RRset even when a hostname is
.ft C
/etc/postfix/main.cf:
smtp_tls_policy_maps = hash:/etc/postfix/tls_policy
- # Postfix 2.5 and later
- smtp_tls_fingerprint_digest = md5
+ # Postfix 2.5 and later.
+ #
+ # The default digest is sha256 with Postfix >= 3.6 and
+ # compatibility level >= 3.
+ #
+ smtp_tls_fingerprint_digest = sha256
.fi
.ad
.ft R
[mail.example.org]:587 secure match=nexthop
# Postfix 2.5 and later
[thumb.example.org] fingerprint
- match=EC:3B:2D:B0:5B:B1:FB:6D:20:A3:9D:72:F6:8D:12:35
- match=3D:95:34:51:24:66:33:B9:D2:40:99:C0:C1:17:0B:D1
+ match=b6:b4:72:34:e2:59:cd:...:c2:ca:63:0d:4d:cc:2c:7d:84:de:e6:2f
+ match=51:e9:af:2e:1e:40:1f:...:64:0a:30:35:2d:09:16:31:5a:eb:82:76
.fi
.ad
.ft R
= yes" and is available with Postfix version
2.2 and later.
.br
+The default algorithm is \fBsha256\fR with Postfix >= 3.6
+and the \fBcompatibility_level\fR set to 3 or higher. With Postfix
+<= 3.5, the default algorithm is \fBmd5\fR. The best\-practice
+algorithm is now \fBsha256\fR. Recent advances in hash function
+cryptanalysis have led to md5 and sha1 being deprecated in favor of
+sha256. However, as long as there are no known "second pre\-image"
+attacks against the older algorithms, their use in this context, though
+not recommended, is still likely safe.
.br
Alternatively, check_ccert_access accepts an explicit search
order (Postfix 3.5 and later). The default search order as described
Postfix version 2.5). This feature requires "smtpd_tls_ask_ccert
= yes" and is available with Postfix version 2.2 and later.
.br
+The default algorithm is \fBsha256\fR with Postfix >= 3.6
+and the \fBcompatibility_level\fR set to 3 or higher. With Postfix
+<= 3.5, the default algorithm is \fBmd5\fR. The best\-practice
+algorithm is now \fBsha256\fR. Recent advances in hash function
+cryptanalysis have led to md5 and sha1 being deprecated in favor of
+sha256. However, as long as there are no known "second pre\-image"
+attacks against the older algorithms, their use in this context, though
+not recommended, is still likely safe.
+.br
.IP "\fBreject_rbl_client \fIrbl_domain=d.d.d.d\fR\fR"
Reject the request when the reversed client network address is
listed with the A record "\fId.d.d.d\fR" under \fIrbl_domain\fR
File with DH parameters that the Postfix SMTP server should
use with non\-export EDH ciphers.
.PP
+The best\-practice choice of parameters uses a 2048\-bit prime. This is fine,
+despite the historical "1024" in the parameter name. Do not be tempted to use
+much larger values, performance degrades quickly, and you may also cease to
+interoperate with some mainstream SMTP clients. As of Postfix 3.1, the
+compiled\-in default prime is 2048\-bits, and it is not strictly necessary,
+though perhaps somewhat beneficial to generate custom DH parameters.
+.PP
Instead of using the exact same parameter sets as distributed
with other TLS packages, it is more secure to generate your own
set of parameters with something like the following commands:
.nf
.na
.ft C
-openssl dhparam \-out /etc/postfix/dh512.pem 512
-openssl dhparam \-out /etc/postfix/dh1024.pem 1024
openssl dhparam \-out /etc/postfix/dh2048.pem 2048
+openssl dhparam \-out /etc/postfix/dh1024.pem 1024
+# As of Postfix 3.6, export\-grade 512\-bit DH parameters are no longer
+# supported or needed.
+openssl dhparam \-out /etc/postfix/dh512.pem 512
.fi
.ad
.ft R
grade is "medium" with Postfix releases after the middle of 2015,
and as a result export\-grade cipher suites are by default not used.
.PP
+With Postfix >= 3.6 export\-grade Diffie\-Hellman key exchange
+is no longer supported, and this parameter is silently ignored.
+.PP
See also the discussion under the smtpd_tls_dh1024_param_file
configuration parameter.
.PP
compiled and linked with OpenSSL 1.0.0 or later.
.SH smtpd_tls_eecdh_grade (default: see "postconf \-d" output)
The Postfix SMTP server security grade for ephemeral elliptic\-curve
-Diffie\-Hellman (EECDH) key exchange.
+Diffie\-Hellman (EECDH) key exchange. As of Postfix 3.6, the value of
+this parameter is always ignored, and Postfix behaves as though th
+\fBauto\fR value (described below) was chosen.
.PP
The available choices are:
+.IP "\fBauto\fR"
+Use the most preferred curve that is
+supported by both the client and the server. This setting requires
+Postfix >= 3.2 compiled and linked with OpenSSL >= 1.0.2. This
+is the default setting under the above conditions (and the only
+setting used with Postfix >= 3.6).
+.br
.IP "\fBnone\fR"
Don't use EECDH. Ciphers based on EECDH key
exchange will be disabled. This is the default in Postfix versions
2.6 and 2.7.
.br
.IP "\fBstrong\fR"
-Use EECDH with approximately 128
-bits of security at a reasonable computational cost. This is the
-current best\-practice trade\-off between security and computational
-efficiency. This is the default in Postfix version 2.8 and later.
+Use EECDH with approximately 128 bits of
+security at a reasonable computational cost. This is the default in
+Postfix versions 2.8-3.5.
.br
.IP "\fBultra\fR"
Use EECDH with approximately 192 bits of
security at computational cost that is approximately twice as high
-as 128 bit strength ECC. Barring significant progress in attacks on
-elliptic curve crypto\-systems, the "strong" curve is sufficient for most
-users.
-.br
-.IP "\fBauto\fR"
-Use the most preferred curve that is
-supported by both the client and the server. This setting requires
-Postfix >= 3.2 compiled and linked with OpenSSL >= 1.0.2. This
-is the default setting under the above conditions.
+as 128 bit strength ECC.
.br
.br
.PP
key exchange with RSA authentication.
.PP
This feature is available in Postfix 2.3 and later.
-.SH smtpd_tls_fingerprint_digest (default: md5)
-The message digest algorithm to construct remote SMTP
-client\-certificate
-fingerprints or public key fingerprints (Postfix 2.9 and later)
-for \fBcheck_ccert_access\fR and \fBpermit_tls_clientcerts\fR. The
-default algorithm is \fBmd5\fR, for backwards compatibility with Postfix
-releases prior to 2.5.
+.SH smtpd_tls_fingerprint_digest (default: see "postconf \-d" output)
+The message digest algorithm to construct remote SMTP client\-certificate
+fingerprints or public key fingerprints (Postfix 2.9 and later) for
+\fBcheck_ccert_access\fR and \fBpermit_tls_clientcerts\fR.
+.PP
+The default algorithm is \fBsha256\fR with Postfix >= 3.6
+and the \fBcompatibility_level\fR set to 3 or higher. With Postfix
+<= 3.5, the default algorithm is \fBmd5\fR.
.PP
-Advances in hash
-function cryptanalysis have led to md5 being deprecated in favor of sha1.
-However, as long as there are no known "second pre\-image" attacks
-against md5, its use in this context can still be considered safe.
+The best\-practice algorithm is now \fBsha256\fR. Recent advances in hash
+function cryptanalysis have led to md5 and sha1 being deprecated in favor of
+sha256. However, as long as there are no known "second pre\-image" attacks
+against the older algorithms, their use in this context, though not
+recommended, is still likely safe.
.PP
While additional digest algorithms are often available with OpenSSL's
libcrypto, only those used by libssl in SSL cipher suites are available to
-Postfix.
+Postfix. You'll likely find support for md5, sha1, sha256 and sha512.
.PP
To find the fingerprint of a specific certificate file, with a
specific digest algorithm, run:
.nf
.na
.ft C
-$ openssl x509 \-noout \-fingerprint \-sha1 \-in cert.pem
-SHA1 Fingerprint=D4:6A:AB:19:24:79:F8:32:BB:A6:CB:66:82:C0:8E:9B:EE:29:A8:1A
+$ openssl x509 \-noout \-fingerprint \-sha256 \-in cert.pem
+SHA256 Fingerprint=D4:6A:AB:19:24:...:A6:CB:66:82:C0:8E:9B:EE:29:A8:1A
.fi
.ad
.ft R
command that converts the key to DER and then to the "dgst" command
to compute the fingerprint.
.PP
-The actual command to transform the key to DER format depends
-on the version of OpenSSL used. With OpenSSL 1.0.0 and later, the
-"pkey" command supports all key types. With OpenSSL 0.9.8 and
-earlier, the key type is always RSA (nobody uses DSA, and EC
-keys are not fully supported by 0.9.8), so the "rsa" command is
-used.
+Example:
.sp
.in +4
.nf
.na
.ft C
-# OpenSSL 1.0 with all certificates and SHA\-1 fingerprints.
$ openssl x509 \-in cert.pem \-noout \-pubkey |
openssl pkey \-pubin \-outform DER |
- openssl dgst \-sha1 \-c
+ openssl dgst \-sha256 \-c
(stdin)= 64:3f:1f:f6:e5:1e:d4:2a:56:8b:fc:09:1a:61:98:b5:bc:7c:60:58
.fi
.ad
.ft R
.in -4
-.sp
-.in +4
-.nf
-.na
-.ft C
-# OpenSSL 0.9.8 with RSA certificates and MD5 fingerprints.
-$ openssl x509 \-in cert.pem \-noout \-pubkey |
- openssl rsa \-pubin \-outform DER |
- openssl dgst \-md5 \-c
-(stdin)= f4:62:60:f6:12:8f:d5:8d:28:4d:13:a7:db:b2:ff:50
-.fi
-.ad
-.ft R
-.in -4
.PP
The Postfix SMTP server and client log the peer (leaf) certificate
fingerprint and public key fingerprint when the TLS loglevel is 2 or
higher.
.PP
-\fBNote:\fR Postfix 2.9.0-2.9.5 computed the public key
-fingerprint incorrectly. To use public\-key fingerprints, upgrade
-to Postfix 2.9.6 or later.
-.PP
-Example: client\-certificate access table, with sha1 fingerprints:
+Example: client\-certificate access table, with sha256 fingerprints:
.sp
.in +4
.nf
.na
.ft C
/etc/postfix/main.cf:
- smtpd_tls_fingerprint_digest = sha1
+ smtpd_tls_fingerprint_digest = sha256
smtpd_client_restrictions =
check_ccert_access hash:/etc/postfix/access,
reject
.ft C
/etc/postfix/access:
# Action folded to next line...
- AF:88:7C:AD:51:95:6F:36:96:F6:01:FB:2E:48:CD:AB:49:25:A2:3B
+ AF:88:7C:AD:51:95:6F:36:96:...:01:FB:2E:48:CD:AB:49:25:A2:3B
OK
- 85:16:78:FD:73:6E:CE:70:E0:31:5F:0D:3C:C8:6D:C4:2C:24:59:E1
+ 85:16:78:FD:73:6E:CE:70:E0:...:5F:0D:3C:C8:6D:C4:2C:24:59:E1
permit_auth_destination
.fi
.ad
"medium" with Postfix releases after the middle of 2015, and as a
result export\-grade cipher suites are by default not used.
.PP
+With Postfix >= 3.6 export\-grade Diffie\-Hellman key exchange
+is no longer supported, and this parameter is silently ignored.
+.PP
This feature is available in Postfix 2.8 and later.
.SH tlsproxy_tls_dkey_file (default: $smtpd_tls_dkey_file)
File with the Postfix \fBtlsproxy\fR(8) server DSA private key in PEM
List of acceptable remote SMTP server certificate fingerprints for
the "fingerprint" TLS security level (\fBsmtp_tls_security_level\fR =
fingerprint).
-.IP "\fBsmtp_tls_fingerprint_digest (md5)\fR"
+.IP "\fBsmtp_tls_fingerprint_digest (see 'postconf -d' output)\fR"
The message digest algorithm used to construct remote SMTP server
certificate fingerprints.
.PP
.ad
.fi
.IP "\fBdebug_peer_level (2)\fR"
-The increment in verbose logging level when a next\-hop destination,
+The increment in verbose logging level when a nexthop destination,
remote client or server name or network address matches a pattern
given with the debug_peer_list parameter.
.IP "\fBdebug_peer_list (empty)\fR"
-Optional list of next\-hop destination, remote client or server
+Optional list of nexthop destination, remote client or server
name or network address patterns that, if matched, cause the verbose
logging level to increase by the amount specified in $debug_peer_level.
.IP "\fBerror_notice_recipient (postmaster)\fR"
authentication without encryption.
.PP
Available in Postfix version 2.5 and later:
-.IP "\fBsmtpd_tls_fingerprint_digest (md5)\fR"
-The message digest algorithm to construct remote SMTP
-client\-certificate
-fingerprints or public key fingerprints (Postfix 2.9 and later)
-for \fBcheck_ccert_access\fR and \fBpermit_tls_clientcerts\fR.
+.IP "\fBsmtpd_tls_fingerprint_digest (see 'postconf -d' output)\fR"
+The message digest algorithm to construct remote SMTP client\-certificate
+fingerprints or public key fingerprints (Postfix 2.9 and later) for
+\fBcheck_ccert_access\fR and \fBpermit_tls_clientcerts\fR.
.PP
Available in Postfix version 2.6 and later:
.IP "\fBsmtpd_tls_protocols (!SSLv2, !SSLv3)\fR"
a lot of detail, to running some daemon processes under control of
a call tracer or debugger.
.IP "\fBdebug_peer_level (2)\fR"
-The increment in verbose logging level when a remote client or
-server matches a pattern in the debug_peer_list parameter.
+The increment in verbose logging level when a nexthop destination,
+remote client or server name or network address matches a pattern
+given with the debug_peer_list parameter.
.IP "\fBdebug_peer_list (empty)\fR"
-Optional list of remote client or server hostname or network
-address patterns that cause the verbose logging level to increase
-by the amount specified in $debug_peer_level.
+Optional list of nexthop destination, remote client or server
+name or network address patterns that, if matched, cause the verbose
+logging level to increase by the amount specified in $debug_peer_level.
.IP "\fBerror_notice_recipient (postmaster)\fR"
The recipient of postmaster notifications about mail delivery
problems that are caused by policy, resource, software or protocol
<li> <p> <a href="#smtputf8_enable"> Using backwards-compatible
default setting smtputf8_enable=no</a> </p>
+<li> <p> <a href="#smtpd_digest"> Using backwards-compatible
+default setting smtpd_tls_fingerprint_digest=md5</a> </p>
+
+<li> <p> <a href="#smtp_digest"> Using backwards-compatible
+default setting smtp_tls_fingerprint_digest=md5</a> </p>
+
+<li> <p> <a href="#smtp_digest"> Using backwards-compatible
+default setting lmtp_tls_fingerprint_digest=md5</a> </p>
+
</ul>
<p> If such a message is logged in the context of a legitimate
<h2> <a name="smtputf8_enable"> Using backwards-compatible default
setting smtputf8_enable=no</a> </h2>
-<p> The smtputf8_enable default value has changed from "no" to "yes.
+<p> The smtputf8_enable default value has changed from "no" to "yes".
With the new "yes" setting, the Postfix SMTP server rejects non-ASCII
addresses from clients that don't request SMTPUTF8 support, after
Postfix is updated from an older version. The backwards-compatibility
</pre>
</blockquote>
+<h2> <a name="smtpd_digest"> Using backwards-compatible
+default setting smtpd_tls_fingerprint_digest=md5</a> </h2>
+
+<p> The smtpd_tls_fingerprint_digest default value has changed from
+"md5" to "sha256". With the new "sha256" setting, the Postfix SMTP
+server avoids using the deprecated "md5" algorithm and computes a more
+secure digest of the client certificate. </p>
+
+<p> If you're using the default "md5" setting, or even an explicit
+"sha1" (also deprecated) setting, you should consider switching to
+"sha256". This will require updating any associated lookup table keys
+with the "sha256" digests of the expected client certificate or public
+key. </p>
+
+<p> As long as the smtpd_tls_fingerprint_digest parameter is left at its
+implicit default value, and the compatibility_level setting is less than
+3, Postfix logs a warning each time a client certificate or public key
+fingerprint is (potentially) used for access control: </p>
+
+<blockquote>
+<pre>
+postfix/smtpd[27560]: using backwards-compatible default setting
+ smtpd_tls_fingerprint_digest=md5 to compute certificate fingerprints
+</pre>
+</blockquote>
+
+<p> Since any client certificate fingerprints are passed in policy service
+lookups, and Postfix doesn't know whether the fingerprint will be used, the
+warning may also be logged when policy lookups are performed for connections
+that used a client certificate, even if the policy service does not in fact
+examine the client certificate. To reduce the noise somewhat, such warnings
+are issued at most once per smtpd(8) process instance. </p>
+
+<p> If you prefer to stick with "md5", you can suppress the warnings by
+making that setting explicit. After addressing any other compatibility
+warnings, you can <a href="#turnoff">update</a> your compatibility level.
+</p>
+
+<blockquote>
+<pre>
+# <b>postconf smtpd_tls_fingerprint_digest=md5</b>
+# <b>postfix reload</b>
+</pre>
+</blockquote>
+
+<h2> <a name="smtp_digest"> Using backwards-compatible
+default setting smtp_tls_fingerprint_digest=md5</a> </h2>
+
+<p> The smtp_tls_fingerprint_digest and lmtp_tls_fingerprint_digest
+default values have changed from "md5" to "sha256". With the new
+"sha256" setting, the Postfix SMTP and LMTP client avoids using the
+deprecated "md5" algorithm and computes a more secure digest of the
+server certificate. </p>
+
+<p> If you're using the default "md5" setting, or even an explicit
+"sha1" (also deprecated) setting, you should consider switching to
+"sha256". This will require updating any "fingerprint" security level
+policies in the TLS policy table to specify matching "sha256" digests of
+the expected server certificates or public keys. </p>
+
+<p> As long as the smtp_tls_fingerprint_digest (or LMTP equivalent)
+parameter is left at its implicit default value, and the
+compatibility_level setting is less than 3, Postfix logs a warning each
+time the "fingerprint" security level is used to specify matching "md5"
+digests of trusted server certificates or public keys: </p>
+
+<blockquote>
+<pre>
+postfix/smtp[27560]: using backwards-compatible default setting
+ smtp_tls_fingerprint_digest=md5 to compute certificate fingerprints
+</pre>
+</blockquote>
+
+<p> If you prefer to stick with "md5", you can suppress the warnings by
+making that setting explicit. After addressing any other compatibility
+warnings, you can <a href="#turnoff">update</a> your compatibility level.
+</p>
+
+<blockquote>
+<pre>
+# <b>postconf 'smtp_tls_fingerprint_digest = md5' \
+ 'lmtp_tls_fingerprint_digest = md5' </b>
+# <b>postfix reload</b>
+</pre>
+</blockquote>
+
<h2> <a name="turnoff">Turning off the backwards-compatibility safety net</a> </h2>
<p> Backwards compatibility is turned off by updating the
<p> The digest algorithm used to compute the client certificate
fingerprints is specified with the main.cf smtpd_tls_fingerprint_digest
-parameter. The default is "md5", for compatibility with Postfix
-versions < 2.5. </p>
+parameter. The default algorithm is <b>sha256</b> with Postfix ≥
+3.6 and the <b>compatibility_level</b> set to 3 or higher. With
+Postfix ≤ 3.5, the default algorithm is <b>md5</b>. The
+best-practice algorithm is now <b>sha256</b>. Recent advances in hash
+function cryptanalysis have led to md5 and sha1 being deprecated in
+favor of sha256. However, as long as there are no known "second
+pre-image" attacks against the older algorithms, their use in this
+context, though not recommended, is still likely safe. </p>
<p> The permit_tls_all_clientcerts feature must be used with caution,
because it can result in too many access permissions. Use this
command that converts the key to DER and then to the "dgst" command
to compute the fingerprint. </p>
-<p> The actual command to transform the key to DER format depends
-on the version of OpenSSL used. With OpenSSL 1.0.0 and later, the
-"pkey" command supports all key types. With OpenSSL 0.9.8 and
-earlier, the key type is always RSA (nobody uses DSA, and EC
-keys are not fully supported by 0.9.8), so the "rsa" command is
-used. </p>
+<p> Example: </p>
<blockquote>
<pre>
-# OpenSSL 1.0 with all certificates and SHA-1 fingerprints.
$ openssl x509 -in cert.pem -noout -pubkey |
openssl pkey -pubin -outform DER |
- openssl dgst -sha1 -c
-(stdin)= 64:3f:1f:f6:e5:1e:d4:2a:56:8b:fc:09:1a:61:98:b5:bc:7c:60:58
-
-# OpenSSL 0.9.8 with RSA certificates and MD5 fingerprints.
-$ openssl x509 -in cert.pem -noout -pubkey |
- openssl rsa -pubin -outform DER |
- openssl dgst -md5 -c
-(stdin)= f4:62:60:f6:12:8f:d5:8d:28:4d:13:a7:db:b2:ff:50
+ openssl dgst -sha256 -c
+(stdin)= 64:3f:1f:f6:e5:1e:d4:2a:...:8b:fc:09:1a:61:98:b5:bc:7c:60:58
</pre>
</blockquote>
-<p> Note: Postfix 2.9.0–2.9.5 computed the public key
-fingerprint incorrectly. To use public-key fingerprints, upgrade
-to Postfix 2.9.6 or later. </p>
<h3><a name="server_cipher">Server-side cipher controls</a> </h3>
delimiter as it occurs between each pair of fingerprint (hexadecimal)
digits. </p>
+<p> The default algorithm is <b>sha256</b> with Postfix ≥ 3.6
+and the <b>compatibility_level</b> set to 3 or higher; with Postfix
+≤ 3.5, the default algorithm is <b>md5</b>. The
+best-practice algorithm is now <b>sha256</b>. Recent advances in hash
+function cryptanalysis have led to md5 and sha1 being deprecated in
+favor of sha256. However, as long as there are no known "second
+pre-image" attacks against the older algorithms, their use in this
+context, though not recommended, is still likely safe. </p>
+
<p> Example: fingerprint TLS security with an internal mailhub.
Two matching fingerprints are listed. The relayhost may be multiple
physical hosts behind a load-balancer, each with its own private/public
<pre>
relayhost = [mailhub.example.com]
smtp_tls_security_level = fingerprint
- smtp_tls_fingerprint_digest = md5
+ smtp_tls_fingerprint_digest = sha256
smtp_tls_fingerprint_cert_match =
- 3D:95:34:51:24:66:33:B9:D2:40:99:C0:C1:17:0B:D1
- EC:3B:2D:B0:5B:B1:FB:6D:20:A3:9D:72:F6:8D:12:35
+ 51:e9:af:2e:1e:40:1f:de:64:...:30:35:2d:09:16:31:5a:eb:82:76
+ b6:b4:72:34:e2:59:cd:fb:c2:...:63:0d:4d:cc:2c:7d:84:de:e6:2f
</pre>
</blockquote>
<pre>
/etc/postfix/main.cf:
smtp_tls_policy_maps = hash:/etc/postfix/tls_policy
- smtp_tls_fingerprint_digest = md5
+ smtp_tls_fingerprint_digest = sha256
</pre>
</blockquote>
<blockquote>
<pre>
/etc/postfix/tls_policy:
example.com fingerprint
- match=3D:95:34:51:24:66:33:B9:D2:40:99:C0:C1:17:0B:D1
- match=EC:3B:2D:B0:5B:B1:FB:6D:20:A3:9D:72:F6:8D:12:35
+ match=51:e9:af:2e:1e:40:1f:de:...:35:2d:09:16:31:5a:eb:82:76
+ match=b6:b4:72:34:e2:59:cd:fb:...:0d:4d:cc:2c:7d:84:de:e6:2f
</pre>
</blockquote>
command that converts the key to DER and then to the "dgst" command
to compute the fingerprint. </p>
-<p> The actual command to transform the key to DER format depends
-on the version of OpenSSL used. With OpenSSL 1.0.0 and later, the
-"pkey" command supports all key types. With OpenSSL 0.9.8 and
-earlier, the key type is always RSA (nobody uses DSA, and EC
-keys are not fully supported by 0.9.8), so the "rsa" command is
-used. </p>
+<p> Example: </p>
<blockquote>
<pre>
-# OpenSSL 1.0 with all certificates and SHA-1 fingerprints.
$ openssl x509 -in cert.pem -noout -pubkey |
openssl pkey -pubin -outform DER |
- openssl dgst -sha1 -c
-(stdin)= 64:3f:1f:f6:e5:1e:d4:2a:56:8b:fc:09:1a:61:98:b5:bc:7c:60:58
-
-# OpenSSL 0.9.8 with RSA certificates and MD5 fingerprints.
-$ openssl x509 -in cert.pem -noout -pubkey |
- openssl rsa -pubin -outform DER |
- openssl dgst -md5 -c
-(stdin)= f4:62:60:f6:12:8f:d5:8d:28:4d:13:a7:db:b2:ff:50
+ openssl dgst -sha256 -c
+(stdin)= 64:3f:1f:f6:e5:1e:d4:2a:56:...:09:1a:61:98:b5:bc:7c:60:58
</pre>
</blockquote>
-<p> Note: Postfix 2.9.0–2.9.5 computed the public key
-fingerprint incorrectly. To use public-key fingerprints, upgrade
-to Postfix 2.9.6 or later. </p>
<h4><a name="client_tls_verify"> Mandatory server certificate verification </a> </h4>
/etc/postfix/main.cf:
smtp_tls_policy_maps = hash:/etc/postfix/tls_policy
# Postfix 2.5 and later
- smtp_tls_fingerprint_digest = md5
+ smtp_tls_fingerprint_digest = sha256
/etc/postfix/tls_policy:
example.edu none
example.mil may
[mail.example.org]:587 secure match=nexthop
# Postfix 2.5 and later
[thumb.example.org] fingerprint
- match=EC:3B:2D:B0:5B:B1:FB:6D:20:A3:9D:72:F6:8D:12:35
- match=3D:95:34:51:24:66:33:B9:D2:40:99:C0:C1:17:0B:D1
+ match=b6:b4:72:34:e2:59:cd:fb:...:0d:4d:cc:2c:7d:84:de:e6:2f
+ match=51:e9:af:2e:1e:40:1f:de:...:35:2d:09:16:31:5a:eb:82:76
# Postfix 2.6 and later
example.info may protocols=!SSLv2 ciphers=medium exclude=3DES
</pre>
= yes" and is available with Postfix version
2.2 and later. </dd>
-<br>
+<dd> The default algorithm is <b>sha256</b> with Postfix ≥ 3.6
+and the <b>compatibility_level</b> set to 3 or higher. With Postfix
+≤ 3.5, the default algorithm is <b>md5</b>. The best-practice
+algorithm is now <b>sha256</b>. Recent advances in hash function
+cryptanalysis have led to md5 and sha1 being deprecated in favor of
+sha256. However, as long as there are no known "second pre-image"
+attacks against the older algorithms, their use in this context, though
+not recommended, is still likely safe. </dd>
<dd> Alternatively, check_ccert_access accepts an explicit search
order (Postfix 3.5 and later). The default search order as described
Postfix version 2.5). This feature requires "smtpd_tls_ask_ccert
= yes" and is available with Postfix version 2.2 and later.</dd>
+<dd> The default algorithm is <b>sha256</b> with Postfix ≥ 3.6
+and the <b>compatibility_level</b> set to 3 or higher. With Postfix
+≤ 3.5, the default algorithm is <b>md5</b>. The best-practice
+algorithm is now <b>sha256</b>. Recent advances in hash function
+cryptanalysis have led to md5 and sha1 being deprecated in favor of
+sha256. However, as long as there are no known "second pre-image"
+attacks against the older algorithms, their use in this context, though
+not recommended, is still likely safe. </dd>
+
<dt><b><a name="reject_rbl_client">reject_rbl_client <i>rbl_domain=d.d.d.d</i></a></b></dt>
<dd>Reject the request when the reversed client network address is
smtpd_tls_fingerprint_digest parameter (hard-coded as md5 prior to
Postfix version 2.5). </dd>
+<dd> The default algorithm is <b>sha256</b> with Postfix ≥ 3.6
+and the <b>compatibility_level</b> set to 3 or higher. With Postfix
+≤ 3.5, the default algorithm is <b>md5</b>. The best-practice
+algorithm is now <b>sha256</b>. Recent advances in hash function
+cryptanalysis have led to md5 and sha1 being deprecated in favor of
+sha256. However, as long as there are no known "second pre-image"
+attacks against the older algorithms, their use in this context, though
+not recommended, is still likely safe. </dd>
+
<dt><b>permit_tls_all_clientcerts </b></dt>
<dd> Append the domain name in $myorigin or $mydomain when the
smtpd_tls_fingerprint_digest parameter (hard-coded as md5 prior to
Postfix version 2.5). </p>
+<p> The default algorithm is <b>sha256</b> with Postfix ≥ 3.6
+and the <b>compatibility_level</b> set to 3 or higher. With Postfix
+≤ 3.5, the default algorithm is <b>md5</b>. The best-practice
+algorithm is now <b>sha256</b>. Recent advances in hash function
+cryptanalysis have led to md5 and sha1 being deprecated in favor of
+sha256. However, as long as there are no known "second pre-image"
+attacks against the older algorithms, their use in this context, though
+not recommended, is still likely safe. </p>
+
<p> Postfix lookup tables are in the form of (key, value) pairs.
Since we only need the key, the value can be chosen freely, e.g.
the name of the user or host:
an appropriate access(5) policy for each client.
See RESTRICTION_CLASS_README.</p>
-<p> <b>Note:</b> Postfix 2.9.0–2.9.5 computed the public key
-fingerprint incorrectly. To use public-key fingerprints, upgrade
-to Postfix 2.9.6 or later. </p>
-
<p>This feature is available with Postfix version 2.2.</p>
%PARAM smtpd_tls_cipherlist
<p> File with DH parameters that the Postfix SMTP server should
use with non-export EDH ciphers. </p>
+<p> The best-practice choice of parameters uses a 2048-bit prime. This is fine,
+despite the historical "1024" in the parameter name. Do not be tempted to use
+much larger values, performance degrades quickly, and you may also cease to
+interoperate with some mainstream SMTP clients. As of Postfix 3.1, the
+compiled-in default prime is 2048-bits, and it is not strictly necessary,
+though perhaps somewhat beneficial to generate custom DH parameters. </p>
+
<p> Instead of using the exact same parameter sets as distributed
with other TLS packages, it is more secure to generate your own
set of parameters with something like the following commands: </p>
<blockquote>
<pre>
-openssl dhparam -out /etc/postfix/dh512.pem 512
-openssl dhparam -out /etc/postfix/dh1024.pem 1024
openssl dhparam -out /etc/postfix/dh2048.pem 2048
+openssl dhparam -out /etc/postfix/dh1024.pem 1024
+# As of Postfix 3.6, export-grade 512-bit DH parameters are no longer
+# supported or needed.
+openssl dhparam -out /etc/postfix/dh512.pem 512
</pre>
</blockquote>
and as a result export-grade cipher suites are by default not used.
</p>
+<p> With Postfix ≥ 3.6 export-grade Diffie-Hellman key exchange
+is no longer supported, and this parameter is silently ignored. </p>
+
<p> See also the discussion under the smtpd_tls_dh1024_param_file
configuration parameter. </p>
<pre>
/etc/postfix/main.cf:
smtp_tls_policy_maps = hash:/etc/postfix/tls_policy
- # Postfix 2.5 and later
- smtp_tls_fingerprint_digest = md5
+ # Postfix 2.5 and later.
+ #
+ # The default digest is sha256 with Postfix ≥ 3.6 and
+ # compatibility level ≥ 3.
+ #
+ smtp_tls_fingerprint_digest = sha256
</pre>
<pre>
[mail.example.org]:587 secure match=nexthop
# Postfix 2.5 and later
[thumb.example.org] fingerprint
- match=EC:3B:2D:B0:5B:B1:FB:6D:20:A3:9D:72:F6:8D:12:35
- match=3D:95:34:51:24:66:33:B9:D2:40:99:C0:C1:17:0B:D1
+ match=b6:b4:72:34:e2:59:cd:...:c2:ca:63:0d:4d:cc:2c:7d:84:de:e6:2f
+ match=51:e9:af:2e:1e:40:1f:...:64:0a:30:35:2d:09:16:31:5a:eb:82:76
</pre>
<p> <b>Note:</b> The <b>hostname</b> strategy if listed in a non-default
<p> This feature is available in Postfix 2.4 and later. </p>
-%PARAM smtp_tls_fingerprint_digest md5
+%PARAM smtp_tls_fingerprint_digest see "postconf -d" output
<p> The message digest algorithm used to construct remote SMTP server
certificate fingerprints. At the "fingerprint" TLS security level
attacks, it is not feasible to create a new public key and a matching
certificate (or public/private key-pair) that has the same fingerprint. </p>
-<p> The default algorithm is <b>md5</b>; this is consistent with
-the backwards compatible setting of the digest used to verify client
-certificates in the SMTP server. </p>
+<p> The default algorithm is <b>sha256</b> with Postfix ≥ 3.6
+and the <b>compatibility_level</b> set to 3 or higher. With Postfix
+≤ 3.5, the default algorithm is <b>md5</b>. </p>
-<p> The best practice algorithm is now <b>sha1</b>. Recent advances in hash
-function cryptanalysis have led to md5 being deprecated in favor of sha1.
-However, as long as there are no known "second pre-image" attacks
-against md5, its use in this context can still be considered safe.
-</p>
+<p> The best-practice algorithm is now <b>sha256</b>. Recent advances in hash
+function cryptanalysis have led to md5 and sha1 being deprecated in favor of
+sha256. However, as long as there are no known "second pre-image" attacks
+against the older algorithms, their use in this context, though not
+recommended, is still likely safe. </p>
<p> While additional digest algorithms are often available with OpenSSL's
libcrypto, only those used by libssl in SSL cipher suites are available to
-Postfix. For now this means just md5 or sha1. </p>
+Postfix. You'll likely find support for md5, sha1, sha256 and sha512. </p>
<p> To find the fingerprint of a specific certificate file, with a
specific digest algorithm, run:
<blockquote>
<pre>
-$ openssl x509 -noout -fingerprint -sha1 -in cert.pem
-SHA1 Fingerprint=D4:6A:AB:19:24:79:F8:32:BB:A6:CB:66:82:C0:8E:9B:EE:29:A8:1A
+$ openssl x509 -noout -fingerprint -sha256 -in cert.pem
+SHA256 Fingerprint=D4:6A:AB:19:24:...:BB:A6:CB:66:82:C0:8E:9B:EE:29:A8:1A
</pre>
</blockquote>
command that converts the key to DER and then to the "dgst" command
to compute the fingerprint. </p>
-<p> The actual command to transform the key to DER format depends
-on the version of OpenSSL used. With OpenSSL 1.0.0 and later, the
-"pkey" command supports all key types. With OpenSSL 0.9.8 and
-earlier, the key type is always RSA (nobody uses DSA, and EC
-keys are not fully supported by 0.9.8), so the "rsa" command is
-used. </p>
+<p> The actual command to transform the key to DER format depends on the
+version of OpenSSL used. As of OpenSSL 1.0.0, the "pkey" command supports
+all key types. </p>
<blockquote>
<pre>
-# OpenSSL 1.0 with all certificates and SHA-1 fingerprints.
+# OpenSSL ≥ 1.0 with SHA-256 fingerprints.
$ openssl x509 -in cert.pem -noout -pubkey |
openssl pkey -pubin -outform DER |
- openssl dgst -sha1 -c
-(stdin)= 64:3f:1f:f6:e5:1e:d4:2a:56:8b:fc:09:1a:61:98:b5:bc:7c:60:58
-</pre>
-</blockquote>
-
-<blockquote>
-<pre>
-# OpenSSL 0.9.8 with RSA certificates and MD5 fingerprints.
-$ openssl x509 -in cert.pem -noout -pubkey |
- openssl rsa -pubin -outform DER |
- openssl dgst -md5 -c
-(stdin)= f4:62:60:f6:12:8f:d5:8d:28:4d:13:a7:db:b2:ff:50
+ openssl dgst -sha256 -c
+(stdin)= 64:3f:1f:f6:e5:1e:d4:2a:56:...:fc:09:1a:61:98:b5:bc:7c:60:58
</pre>
</blockquote>
fingerprint and public key fingerprint when the TLS loglevel is 2 or
higher. </p>
-<p> <b>Note:</b> Postfix 2.9.0–2.9.5 computed the public key
-fingerprint incorrectly. To use public-key fingerprints, upgrade
-to Postfix 2.9.6 or later. </p>
-
<p> This feature is available in Postfix 2.5 and later. </p>
%PARAM smtp_tls_fingerprint_cert_match
digest algorithm is selected via the <b>smtp_tls_fingerprint_digest</b>
parameter. </p>
+<p> The colons between each pair of nibbles in the fingerprint value
+are optional (Postfix ≥ 3.6). These were required in earlier
+Postfix releases. </p>
+
<p> When an <b>smtp_tls_policy_maps</b> table entry specifies the
"fingerprint" security level, any "match" attributes in that entry specify
the list of valid fingerprints for the corresponding destination. Multiple
<pre>
relayhost = [mailhub.example.com]
smtp_tls_security_level = fingerprint
-smtp_tls_fingerprint_digest = md5
+smtp_tls_fingerprint_digest = sha256
smtp_tls_fingerprint_cert_match =
- 3D:95:34:51:24:66:33:B9:D2:40:99:C0:C1:17:0B:D1
- EC:3B:2D:B0:5B:B1:FB:6D:20:A3:9D:72:F6:8D:12:35
+ cd:fc:d8:db:f8:c4:82:96:6c:...:28:71:e8:f5:8d:a5:0d:9b:d4:a6
+ dd:5c:ef:f5:c3:bc:64:25:36:...:99:36:06:ce:40:ef:de:2e:ad:a4
</pre>
</blockquote>
<pre>
/etc/postfix/main.cf:
smtp_tls_policy_maps = hash:/etc/postfix/tls_policy
- smtp_tls_fingerprint_digest = md5
+ smtp_tls_fingerprint_digest = sha256
</pre>
</blockquote>
<pre>
/etc/postfix/tls_policy:
example.com fingerprint
- match=3D:95:34:51:24:66:33:B9:D2:40:99:C0:C1:17:0B:D1
- match=EC:3B:2D:B0:5B:B1:FB:6D:20:A3:9D:72:F6:8D:12:35
+ match=51:e9:af:2e:1e:40:1f:...:64:0a:30:35:2d:09:16:31:5a:eb:82:76
+ match=b6:b4:72:34:e2:59:cd:...:c2:ca:63:0d:4d:cc:2c:7d:84:de:e6:2f
</pre>
</blockquote>
<p> This feature is available in Postfix 2.5 and later. </p>
-%PARAM lmtp_tls_fingerprint_digest md5
+%PARAM lmtp_tls_fingerprint_digest see "postconf -d" output
<p> The LMTP-specific version of the smtp_tls_fingerprint_digest
configuration parameter. See there for details. </p>
<p> This feature is available in Postfix 2.5 and later. </p>
-%PARAM smtpd_tls_fingerprint_digest md5
+%PARAM smtpd_tls_fingerprint_digest see "postconf -d" output
-<p> The message digest algorithm to construct remote SMTP
-client-certificate
-fingerprints or public key fingerprints (Postfix 2.9 and later)
-for <b>check_ccert_access</b> and <b>permit_tls_clientcerts</b>. The
-default algorithm is <b>md5</b>, for backwards compatibility with Postfix
-releases prior to 2.5. </p>
+<p> The message digest algorithm to construct remote SMTP client-certificate
+fingerprints or public key fingerprints (Postfix 2.9 and later) for
+<b>check_ccert_access</b> and <b>permit_tls_clientcerts</b>. </p>
-<p> Advances in hash
-function cryptanalysis have led to md5 being deprecated in favor of sha1.
-However, as long as there are no known "second pre-image" attacks
-against md5, its use in this context can still be considered safe.
-</p>
+<p> The default algorithm is <b>sha256</b> with Postfix ≥ 3.6
+and the <b>compatibility_level</b> set to 3 or higher. With Postfix
+≤ 3.5, the default algorithm is <b>md5</b>. </p>
+
+<p> The best-practice algorithm is now <b>sha256</b>. Recent advances in hash
+function cryptanalysis have led to md5 and sha1 being deprecated in favor of
+sha256. However, as long as there are no known "second pre-image" attacks
+against the older algorithms, their use in this context, though not
+recommended, is still likely safe. </p>
<p> While additional digest algorithms are often available with OpenSSL's
libcrypto, only those used by libssl in SSL cipher suites are available to
-Postfix. </p>
+Postfix. You'll likely find support for md5, sha1, sha256 and sha512. </p>
<p> To find the fingerprint of a specific certificate file, with a
specific digest algorithm, run: </p>
<blockquote>
<pre>
-$ openssl x509 -noout -fingerprint -sha1 -in cert.pem
-SHA1 Fingerprint=D4:6A:AB:19:24:79:F8:32:BB:A6:CB:66:82:C0:8E:9B:EE:29:A8:1A
+$ openssl x509 -noout -fingerprint -sha256 -in cert.pem
+SHA256 Fingerprint=D4:6A:AB:19:24:...:A6:CB:66:82:C0:8E:9B:EE:29:A8:1A
</pre>
</blockquote>
command that converts the key to DER and then to the "dgst" command
to compute the fingerprint. </p>
-<p> The actual command to transform the key to DER format depends
-on the version of OpenSSL used. With OpenSSL 1.0.0 and later, the
-"pkey" command supports all key types. With OpenSSL 0.9.8 and
-earlier, the key type is always RSA (nobody uses DSA, and EC
-keys are not fully supported by 0.9.8), so the "rsa" command is
-used. </p>
+<p> Example: </p>
<blockquote>
<pre>
-# OpenSSL 1.0 with all certificates and SHA-1 fingerprints.
$ openssl x509 -in cert.pem -noout -pubkey |
openssl pkey -pubin -outform DER |
- openssl dgst -sha1 -c
+ openssl dgst -sha256 -c
(stdin)= 64:3f:1f:f6:e5:1e:d4:2a:56:8b:fc:09:1a:61:98:b5:bc:7c:60:58
</pre>
</blockquote>
-<blockquote>
-<pre>
-# OpenSSL 0.9.8 with RSA certificates and MD5 fingerprints.
-$ openssl x509 -in cert.pem -noout -pubkey |
- openssl rsa -pubin -outform DER |
- openssl dgst -md5 -c
-(stdin)= f4:62:60:f6:12:8f:d5:8d:28:4d:13:a7:db:b2:ff:50
-</pre>
-</blockquote>
-
<p> The Postfix SMTP server and client log the peer (leaf) certificate
fingerprint and public key fingerprint when the TLS loglevel is 2 or
higher. </p>
-<p> <b>Note:</b> Postfix 2.9.0–2.9.5 computed the public key
-fingerprint incorrectly. To use public-key fingerprints, upgrade
-to Postfix 2.9.6 or later. </p>
-
-<p> Example: client-certificate access table, with sha1 fingerprints: </p>
+<p> Example: client-certificate access table, with sha256 fingerprints: </p>
<blockquote>
<pre>
/etc/postfix/main.cf:
- smtpd_tls_fingerprint_digest = sha1
+ smtpd_tls_fingerprint_digest = sha256
smtpd_client_restrictions =
check_ccert_access hash:/etc/postfix/access,
reject
<pre>
/etc/postfix/access:
# Action folded to next line...
- AF:88:7C:AD:51:95:6F:36:96:F6:01:FB:2E:48:CD:AB:49:25:A2:3B
+ AF:88:7C:AD:51:95:6F:36:96:...:01:FB:2E:48:CD:AB:49:25:A2:3B
OK
- 85:16:78:FD:73:6E:CE:70:E0:31:5F:0D:3C:C8:6D:C4:2C:24:59:E1
+ 85:16:78:FD:73:6E:CE:70:E0:...:5F:0D:3C:C8:6D:C4:2C:24:59:E1
permit_auth_destination
</pre>
</blockquote>
%PARAM smtpd_tls_eecdh_grade see "postconf -d" output
<p> The Postfix SMTP server security grade for ephemeral elliptic-curve
-Diffie-Hellman (EECDH) key exchange. </p>
+Diffie-Hellman (EECDH) key exchange. As of Postfix 3.6, the value of
+this parameter is always ignored, and Postfix behaves as though th
+<b>auto</b> value (described below) was chosen.
+</p>
<p> The available choices are: </p>
<dl>
+<dt><b>auto</b></dt> <dd> Use the most preferred curve that is
+supported by both the client and the server. This setting requires
+Postfix ≥ 3.2 compiled and linked with OpenSSL ≥ 1.0.2. This
+is the default setting under the above conditions (and the only
+setting used with Postfix ≥ 3.6). </dd>
+
<dt><b>none</b></dt> <dd> Don't use EECDH. Ciphers based on EECDH key
exchange will be disabled. This is the default in Postfix versions
2.6 and 2.7. </dd>
-<dt><b>strong</b></dt> <dd> Use EECDH with approximately 128
-bits of security at a reasonable computational cost. This is the
-current best-practice trade-off between security and computational
-efficiency. This is the default in Postfix version 2.8 and later.
-</dd>
+<dt><b>strong</b></dt> <dd> Use EECDH with approximately 128 bits of
+security at a reasonable computational cost. This is the default in
+Postfix versions 2.8–3.5. </dd>
<dt><b>ultra</b></dt> <dd> Use EECDH with approximately 192 bits of
security at computational cost that is approximately twice as high
-as 128 bit strength ECC. Barring significant progress in attacks on
-elliptic curve crypto-systems, the "strong" curve is sufficient for most
-users. </dd>
-
-<dt><b>auto</b></dt> <dd> Use the most preferred curve that is
-supported by both the client and the server. This setting requires
-Postfix ≥ 3.2 compiled and linked with OpenSSL ≥ 1.0.2. This
-is the default setting under the above conditions. </dd>
+as 128 bit strength ECC. </dd>
</dl>
"medium" with Postfix releases after the middle of 2015, and as a
result export-grade cipher suites are by default not used. </p>
+<p> With Postfix ≥ 3.6 export-grade Diffie-Hellman key exchange
+is no longer supported, and this parameter is silently ignored. </p>
+
<p> This feature is available in Postfix 2.8 and later. </p>
%PARAM tlsproxy_tls_dkey_file $smtpd_tls_dkey_file
/* int warn_compat_break_flush_domains;
/* int warn_compat_break_mynetworks_style;
/*
+/* int warn_compat_break_smtpd_tls_fpt_dgst;
+/* int warn_compat_break_smtp_tls_fpt_dgst;
+/* int warn_compat_break_lmtp_tls_fpt_dgst;
+/*
/* char *var_maillog_file;
/* char *var_maillog_file_pfxs;
/* char *var_maillog_file_comp;
char *var_info_log_addr_form;
bool var_enable_orcpt;
-char *var_maillog_file;
-char *var_maillog_file_pfxs;
-char *var_maillog_file_comp;
-char *var_maillog_file_stamp;
-char *var_postlog_service;
+char *var_maillog_file;
+char *var_maillog_file_pfxs;
+char *var_maillog_file_comp;
+char *var_maillog_file_stamp;
+char *var_postlog_service;
const char null_format_string[1] = "";
+ /*
+ * Compatibility level 3.
+ */
+int warn_compat_break_smtpd_tls_fpt_dgst;
+int warn_compat_break_smtp_tls_fpt_dgst;
+int warn_compat_break_lmtp_tls_fpt_dgst;
+
/*
* Compatibility level 2.
*/
* bits.
*/
+ /*
+ * Look for specific parameters whose default changed when the
+ * compatibility level changed to 3.
+ */
+ if (var_compat_level < 3) {
+ if (mail_conf_lookup(VAR_SMTPD_TLS_FPT_DGST) == 0)
+ warn_compat_break_smtpd_tls_fpt_dgst = 1;
+ if (mail_conf_lookup(VAR_SMTP_TLS_FPT_DGST) == 0)
+ warn_compat_break_smtp_tls_fpt_dgst = 1;
+ if (mail_conf_lookup(VAR_LMTP_TLS_FPT_DGST) == 0)
+ warn_compat_break_lmtp_tls_fpt_dgst = 1;
+ } else {
+ warn_compat_break_smtpd_tls_fpt_dgst = 0;
+ warn_compat_break_smtp_tls_fpt_dgst = 0;
+ warn_compat_break_lmtp_tls_fpt_dgst = 0;
+ }
+
/*
* Look for specific parameters whose default changed when the
* compatibility level changed to 2.
#ifdef USE_TLS
#include <openssl/opensslv.h> /* OPENSSL_VERSION_NUMBER */
#include <openssl/objects.h> /* SN_* and NID_* macros */
-#if OPENSSL_VERSION_NUMBER < 0x1000200fUL
-#error "OpenSSL releases prior to 1.0.2 are no longer supported"
+#if OPENSSL_VERSION_NUMBER < 0x1010100fUL
+#error "OpenSSL releases prior to 1.1.1 are no longer supported"
#endif
#endif
*/
#define VAR_COMPAT_LEVEL "compatibility_level"
#define DEF_COMPAT_LEVEL 0
-#define CUR_COMPAT_LEVEL 2
+#define CUR_COMPAT_LEVEL 3
extern int var_compat_level;
extern int warn_compat_break_app_dot_mydomain;
extern int warn_compat_break_flush_domains;
extern int warn_compat_break_mynetworks_style;
+extern int warn_compat_break_smtpd_tls_fpt_dgst;
+extern int warn_compat_break_smtp_tls_fpt_dgst;
+extern int warn_compat_break_lmtp_tls_fpt_dgst;
+
/*
* What problem classes should be reported to the postmaster via email.
* Default is bad problems only. See mail_error(3). Even when mail notices
extern char *var_smtpd_tls_mand_excl;
#define VAR_SMTPD_TLS_FPT_DGST "smtpd_tls_fingerprint_digest"
-#define DEF_SMTPD_TLS_FPT_DGST "md5"
+#define DEF_SMTPD_TLS_FPT_DGST "${{$compatibility_level} < {3} ? " \
+ "{md5} : {sha256}}"
extern char *var_smtpd_tls_fpt_dgst;
#define VAR_SMTPD_TLS_512_FILE "smtpd_tls_dh512_param_file"
extern char *var_smtp_tls_mand_excl;
#define VAR_SMTP_TLS_FPT_DGST "smtp_tls_fingerprint_digest"
-#define DEF_SMTP_TLS_FPT_DGST "md5"
+#define DEF_SMTP_TLS_FPT_DGST "${{$compatibility_level} < {3} ? " \
+ "{md5} : {sha256}}"
#define VAR_LMTP_TLS_FPT_DGST "lmtp_tls_fingerprint_digest"
-#define DEF_LMTP_TLS_FPT_DGST "md5"
+#define DEF_LMTP_TLS_FPT_DGST "${{$compatibility_level} < {3} ? " \
+ "{md5} : {sha256}}"
extern char *var_smtp_tls_fpt_dgst;
#define VAR_SMTP_TLS_TAFILE "smtp_tls_trust_anchor_file"
* snapshots are called a.b-yyyymmdd, where a=major release number, b=minor
* release number, c=patchlevel, and yyyymmdd is the release date:
* yyyy=year, mm=month, dd=day.
- *
+ *
* Patches change both the patchlevel and the release date. Snapshots have no
* patchlevel; they change the release date only.
*/
-#define MAIL_RELEASE_DATE "20200627"
+#define MAIL_RELEASE_DATE "20200720"
#define MAIL_VERSION_NUMBER "3.6"
#ifdef SNAPSHOT
/* \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)"
+/* .IP "\fB-d \fImdalg\fR (default: \fB$smtp_tls_fingerprint_digest\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).
+/* in the DNS). In Postfix versions prior to 3.6, the default value
+/* was "sha1".
/* .IP "\fB-f\fR"
/* Lookup the associated DANE TLSA RRset even when a hostname is not an
/* alias and its address records lie in an unsigned zone. See
* context attributes.
*/
state->tls_context = tls_proxy_context_receive(state->stream);
- if (state->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",
- state->namaddrport, state->tls_context->peer_CN,
- state->tls_context->issuer_CN,
- state->tls_context->peer_cert_fprint,
- state->tls_context->peer_pkey_fprint);
- tls_log_summary(TLS_ROLE_CLIENT, TLS_USAGE_NEW,
- state->tls_context);
+ if (state->tls_context) {
+ if (state->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",
+ state->namaddrport, state->tls_context->peer_CN,
+ state->tls_context->issuer_CN,
+ state->tls_context->peer_cert_fprint,
+ state->tls_context->peer_pkey_fprint);
+ tls_log_summary(TLS_ROLE_CLIENT, TLS_USAGE_NEW,
+ state->tls_context);
+ } else {
+ msg_warn("error receiving TLS proxy context");
+ }
}
} else { /* tls_proxy_mode */
state->tls_context =
if (TLS_DANE_BASED(level)) {
if (state->mx == 0 || state->mx->dnssec_valid ||
state->mxinsec_level > TLS_LEV_MAY) {
- 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_unusable(state->ddane) ?
"usable " : "");
level = TLS_LEV_SECURE;
- } else if (!TLS_DANE_HASTA(state->ddane)
- && !TLS_DANE_HASEE(state->ddane)) {
+ } else if (state->ddane->tlsa == 0) {
msg_panic("DANE activated with no TLSA records to match");
} else if (state->mx && !state->mx->dnssec_valid &&
state->mxinsec_level == TLS_LEV_ENCRYPT) {
return (0);
}
-#if defined(USE_TLS) && OPENSSL_VERSION_NUMBER < 0x10100000L
-
-/* ssl_cleanup - free memory allocated in the OpenSSL library */
-
-static void ssl_cleanup(void)
-{
- ERR_remove_thread_state(0); /* Thread-id is now a pointer */
- ENGINE_cleanup();
- CONF_modules_unload(1);
- ERR_free_strings();
- EVP_cleanup();
- CRYPTO_cleanup_all_ex_data();
-}
-
-#endif /* USE_TLS && OPENSSL_VERSION_NUMBER
- * < 0x10100000L */
-
/* run - do what we were asked to do. */
static int run(STATE *state)
#ifdef USE_TLS
#define TLSOPTS "A:Cd:fF:g:H:k:K:l:L:m:M:p:P:r:s:wX"
- state->mdalg = mystrdup("sha1");
+ state->mdalg = 0;
state->CApath = mystrdup("");
state->CAfile = mystrdup("");
state->chains = mystrdup("");
state->print_trust = 1;
break;
case 'd':
- myfree(state->mdalg);
+ if (state->mdalg)
+ myfree(state->mdalg);
state->mdalg = mystrdup(optarg);
break;
case 'f':
if (state->options.logopts == 0)
state->options.logopts = mystrdup("routine,certmatch");
state->log_mask = tls_log_mask("-L option", state->options.logopts);
+ tls_dane_loglevel("-L option", state->options.logopts);
if (state->options.level) {
state->level = tls_level_lookup(state->options.level);
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 (TLS_DANE_BASED(state->level) && !tls_dane_avail()) {
- msg_warn("DANE TLS support is not available, resorting to \"secure\"");
- state->level = TLS_LEV_SECURE;
- }
- state->tls_bio = 0;
- if (state->print_trust)
- state->tls_bio = BIO_new_fp(stdout, BIO_NOCLOSE);
-
#endif
}
static void parse_match(STATE *state, int argc, char *argv[])
{
#ifdef USE_TLS
+ int smtp_mode = 1;
switch (state->level) {
- case TLS_LEV_SECURE:
+ case TLS_LEV_SECURE:
state->match = argv_alloc(2);
while (*argv)
argv_split_append(state->match, *argv++, "");
case TLS_LEV_FPRINT:
state->dane = tls_dane_alloc();
while (*argv)
- tls_dane_add_ee_digests((TLS_DANE *) state->dane,
- state->mdalg, *argv++, "");
+ tls_dane_add_fpt_digests((TLS_DANE *) state->dane, *argv++, "",
+ smtp_mode);
break;
case TLS_LEV_DANE:
case TLS_LEV_DANE_ONLY:
char *loopenv = getenv("VALGRINDLOOP");
int loop = loopenv ? atoi(loopenv) : 1;
ARGV *import_env;
+ static char *var_smtp_tls_fpt_dgst;
+ static const CONFIG_STR_TABLE smtp_str_table[] = {
+#ifdef USE_TLS
+ VAR_SMTP_TLS_FPT_DGST, DEF_SMTP_TLS_FPT_DGST, &var_smtp_tls_fpt_dgst, 1, 0,
+#endif
+ 0,
+ };
/* Don't die when a peer goes away unexpectedly. */
signal(SIGPIPE, SIG_IGN);
mail_conf_suck();
parse_options(&state, argc, argv);
mail_params_init();
+ get_mail_conf_str_table(smtp_str_table);
parse_tas(&state);
+#ifdef USE_TLS
+ /* Less surprising to default to the same fingerprint digest as smtp(8) */
+ if (state.mdalg)
+ warn_compat_break_smtp_tls_fpt_dgst = 0;
+ else
+ state.mdalg = mystrdup(var_smtp_tls_fpt_dgst);
+
+ /*
+ * 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 (TLS_DANE_BASED(state.level) && !tls_dane_avail()) {
+ msg_warn("DANE TLS support is not available, resorting to \"secure\"");
+ state.level = TLS_LEV_SECURE;
+ }
+ state.tls_bio = 0;
+ if (state.print_trust)
+ state.tls_bio = BIO_new_fp(stdout, BIO_NOCLOSE);
+#endif
+
/* Enforce consistent operation of different Postfix parts. */
import_env = mail_parm_split(VAR_IMPORT_ENVIRON, var_import_environ);
update_env(import_env->argv);
/* Be valgrind friendly and clean-up */
cleanup(&state);
- /* OpenSSL 1.1.0 and later (de)initialization is implicit */
-#if defined(USE_TLS) && OPENSSL_VERSION_NUMBER < 0x10100000L
- ssl_cleanup();
-#endif
-
return (0);
}
/* .IP \fB!\fR
/* The message is in the \fBhold\fR queue, i.e. no further delivery
/* attempt will be made until the mail is taken off hold.
+/* .IP \fB#\fR
+/* The message is forced to expire. See the \fBpostsuper\fR(1)
+/* options \fB-e\fR or \fB-f\fR.
/* .RE
/* .IP
/* This mode of operation is implemented by executing the
/* SECURITY
/* .ad
/* .fi
-/* By design, this program is not set-user (or group) id. However,
-/* it must handle data from untrusted, possibly remote, users.
-/* Thus, the usual precautions need to be taken against malicious
-/* inputs.
+/* By design, this program is not set-user (or group) id.
+/* It is prepared to handle message content from untrusted,
+/* possibly remote, users.
+/*
+/* However, like most Postfix programs, this program does not
+/* enforce a security policy on its command-line arguments.
+/* Instead, it relies on the UNIX system to enforce access
+/* policies based on the effective user and group IDs of the
+/* process. Concretely, this means that running Postfix commands
+/* as root (from sudo or equivalent) on behalf of a non-root
+/* user is likely to create privilege escalation opportunities.
+/*
+/* If an application runs any Postfix programs on behalf of
+/* users that do not have normal shell access to Postfix
+/* commands, then that application MUST restrict user-specified
+/* command-line arguments to avoid privilege escalation.
+/* .IP \(bu
+/* Filter all command-line arguments, for example arguments
+/* that contain a pathname or that specify a database access
+/* method. These pathname checks must reject user-controlled
+/* symlinks or hardlinks to sensitive files, and must not be
+/* vulnerable to TOCTOU race attacks.
+/* .IP \(bu
+/* Disable command options processing for all command arguments
+/* that contain user-specified data. For example, the Postfix
+/* \fBsendmail\fR(1) command line MUST be structured as follows:
+/*
+/* .nf
+/* \fB/path/to/sendmail\fR \fIsystem-arguments\fR \fB--\fR \fIuser-arguments\fR
+/* .fi
+/*
+/* Here, the "\fB--\fR" disables command option processing for
+/* all \fIuser-arguments\fR that follow.
+/* .IP
+/* Without the "\fB--\fR", a malicious user could enable Postfix
+/* \fBsendmail\fR(1) command options, by specifying an email
+/* address that starts with "\fB-\fR".
/* DIAGNOSTICS
/* Problems are logged to \fBsyslogd\fR(8) or \fBpostlogd\fR(8),
/* and to the standard error stream.
/* The external command to execute when a Postfix daemon program is
/* invoked with the -D option.
/* .IP "\fBdebug_peer_level (2)\fR"
-/* The increment in verbose logging level when a remote client or
-/* server matches a pattern in the debug_peer_list parameter.
+/* The increment in verbose logging level when a nexthop destination,
+/* remote client or server name or network address matches a pattern
+/* given with the debug_peer_list parameter.
/* .IP "\fBdebug_peer_list (empty)\fR"
-/* Optional list of remote client or server hostname or network
-/* address patterns that cause the verbose logging level to increase
-/* by the amount specified in $debug_peer_level.
+/* Optional list of nexthop destination, remote client or server
+/* name or network address patterns that, if matched, cause the verbose
+/* logging level to increase by the amount specified in $debug_peer_level.
/* ACCESS CONTROLS
/* .ad
/* .fi
const char *dsn_envid = 0;
int saved_optind;
ARGV *import_env;
+ char *alias_map_from_args = 0;
/*
* Fingerprint executables and core dumps.
case 'A':
if (optarg[1] == 0)
msg_fatal_status(EX_USAGE, "-oA requires pathname");
- myfree(var_alias_db_map);
- var_alias_db_map = mystrdup(optarg + 1);
- set_mail_conf_str(VAR_ALIAS_DB_MAP, var_alias_db_map);
+ alias_map_from_args = optarg + 1;
break;
case '7':
case '8':
if (argv[OPTIND])
msg_fatal_status(EX_USAGE,
"alias initialization mode requires no recipient");
- if (*var_alias_db_map == 0)
+ if (alias_map_from_args == 0 && *var_alias_db_map == 0)
return (0);
- ext_argv = argv_alloc(2);
+ ext_argv = argv_alloc(3);
argv_add(ext_argv, "postalias", (char *) 0);
for (n = 0; n < msg_verbose; n++)
argv_add(ext_argv, "-v", (char *) 0);
- argv_split_append(ext_argv, var_alias_db_map, CHARS_COMMA_SP);
+ argv_add(ext_argv, "--", (char *) 0);
+ if (alias_map_from_args != 0)
+ argv_add(ext_argv, alias_map_from_args, (char *) 0);
+ else
+ argv_split_append(ext_argv, var_alias_db_map, CHARS_COMMA_SP);
argv_terminate(ext_argv);
mail_run_replace(var_command_dir, ext_argv->argv);
/* NOTREACHED */
/* List of acceptable remote SMTP server certificate fingerprints for
/* the "fingerprint" TLS security level (\fBsmtp_tls_security_level\fR =
/* fingerprint).
-/* .IP "\fBsmtp_tls_fingerprint_digest (md5)\fR"
+/* .IP "\fBsmtp_tls_fingerprint_digest (see 'postconf -d' output)\fR"
/* The message digest algorithm used to construct remote SMTP server
/* certificate fingerprints.
/* .PP
/* .ad
/* .fi
/* .IP "\fBdebug_peer_level (2)\fR"
-/* The increment in verbose logging level when a next-hop destination,
+/* The increment in verbose logging level when a nexthop destination,
/* remote client or server name or network address matches a pattern
/* given with the debug_peer_list parameter.
/* .IP "\fBdebug_peer_list (empty)\fR"
-/* Optional list of next-hop destination, remote client or server
+/* Optional list of nexthop destination, remote client or server
/* name or network address patterns that, if matched, cause the verbose
/* logging level to increase by the amount specified in $debug_peer_level.
/* .IP "\fBerror_notice_recipient (postmaster)\fR"
CApath = var_smtp_tls_CApath,
mdalg = var_smtp_tls_fpt_dgst);
smtp_tls_list_init();
+ tls_dane_loglevel(VAR_LMTP_SMTP(TLS_LOGLEVEL), var_smtp_tls_loglevel);
#else
msg_warn("TLS has been selected, but TLS support is not compiled in");
#endif
* "QUIT".
*
* See src/tls/tls_level.c and src/tls/tls.h. Levels above "encrypt" require
- * matching. Levels >= "dane" require CA or DNSSEC trust.
+ * matching.
*
- * 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.
+ * NOTE: We use "IS_MATCHED" to satisfy policy, but "IS_SECURED" to log
+ * effective security. Thus "half-dane" is never "Verified" only
+ * "Trusted", but matching is enforced here.
+ *
+ * NOTE: When none of the TLSA records were usable, "dane" and "half-dane"
+ * fall back to "encrypt", updating the tls_context level accordingly, so
+ * we must check that here, and not state->tls->level.
*/
- if (TLS_MUST_TRUST(state->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 (TLS_MUST_MATCH(state->tls->level))
+ if (TLS_MUST_MATCH(session->tls_context->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"),
case TLS_LEV_FPRINT:
if (!tls->dane)
tls->dane = tls_dane_alloc();
- tls_dane_add_ee_digests(tls->dane,
- var_smtp_tls_fpt_dgst, val, "|");
+ tls_dane_add_fpt_digests(tls->dane, val, "|", smtp_mode);
break;
case TLS_LEV_VERIFY:
case TLS_LEV_SECURE:
case TLS_LEV_FPRINT:
if (tls->dane == 0)
tls->dane = tls_dane_alloc();
- if (!TLS_DANE_HASEE(tls->dane)) {
- tls_dane_add_ee_digests(tls->dane, var_smtp_tls_fpt_dgst,
- var_smtp_tls_fpt_cmatch, CHARS_COMMA_SP);
- if (!TLS_DANE_HASEE(tls->dane)) {
+ if (tls->dane->tlsa == 0) {
+ tls_dane_add_fpt_digests(tls->dane, var_smtp_tls_fpt_cmatch,
+ CHARS_COMMA_SP, smtp_mode);
+ if (tls->dane->tlsa == 0) {
msg_warn("nexthop domain %s: configured at fingerprint "
"security level, but with no fingerprints to match.",
dest);
if (*var_smtp_tls_tafile) {
if (tls->dane == 0)
tls->dane = tls_dane_alloc();
- if (!TLS_DANE_HASTA(tls->dane)
+ if (tls->dane->tlsa == 0
&& !load_tas(tls->dane, var_smtp_tls_tafile)) {
MARK_INVALID(tls->why, &tls->level);
return ((void *) tls);
/*
* With DANE trust anchors, peername matching is not configurable.
*/
- if (TLS_DANE_HASTA(dane)) {
+ if (dane->tlsa != 0) {
tls->matchargv = argv_alloc(2);
argv_add(tls->matchargv, dane->base_domain, ARGV_END);
if (iter->mx) {
argv_add(tls->matchargv, iter->mx->rname,
iter->mx->qname, ARGV_END);
}
- } else if (!TLS_DANE_HASEE(dane))
+ } else
msg_panic("empty DANE match list");
tls->dane = dane;
return;
/* authentication without encryption.
/* .PP
/* Available in Postfix version 2.5 and later:
-/* .IP "\fBsmtpd_tls_fingerprint_digest (md5)\fR"
-/* The message digest algorithm to construct remote SMTP
-/* client-certificate
-/* fingerprints or public key fingerprints (Postfix 2.9 and later)
-/* for \fBcheck_ccert_access\fR and \fBpermit_tls_clientcerts\fR.
+/* .IP "\fBsmtpd_tls_fingerprint_digest (see 'postconf -d' output)\fR"
+/* The message digest algorithm to construct remote SMTP client-certificate
+/* fingerprints or public key fingerprints (Postfix 2.9 and later) for
+/* \fBcheck_ccert_access\fR and \fBpermit_tls_clientcerts\fR.
/* .PP
/* Available in Postfix version 2.6 and later:
/* .IP "\fBsmtpd_tls_protocols (!SSLv2, !SSLv3)\fR"
/* a lot of detail, to running some daemon processes under control of
/* a call tracer or debugger.
/* .IP "\fBdebug_peer_level (2)\fR"
-/* The increment in verbose logging level when a remote client or
-/* server matches a pattern in the debug_peer_list parameter.
+/* The increment in verbose logging level when a nexthop destination,
+/* remote client or server name or network address matches a pattern
+/* given with the debug_peer_list parameter.
/* .IP "\fBdebug_peer_list (empty)\fR"
-/* Optional list of remote client or server hostname or network
-/* address patterns that cause the verbose logging level to increase
-/* by the amount specified in $debug_peer_level.
+/* Optional list of nexthop destination, remote client or server
+/* name or network address patterns that, if matched, cause the verbose
+/* logging level to increase by the amount specified in $debug_peer_level.
/* .IP "\fBerror_notice_recipient (postmaster)\fR"
/* The recipient of postmaster notifications about mail delivery
/* problems that are caused by policy, resource, software or protocol
int i;
char *prints[2];
+ if (warn_compat_break_smtpd_tls_fpt_dgst)
+ msg_info("using backwards-compatible default setting "
+ VAR_SMTPD_TLS_FPT_DGST "=md5 to compute certificate "
+ "fingerprints");
+
prints[0] = state->tls_context->peer_cert_fprint;
prints[1] = state->tls_context->peer_pkey_fprint;
switch (*action) {
case SMTPD_ACL_SEARCH_CODE_CERT_FPRINT:
match_this = state->tls_context->peer_cert_fprint;
+ if (warn_compat_break_smtpd_tls_fpt_dgst)
+ msg_info("using backwards-compatible default setting "
+ VAR_SMTPD_TLS_FPT_DGST "=md5 to compute "
+ "certificate fingerprints");
break;
case SMTPD_ACL_SEARCH_CODE_PKEY_FPRINT:
match_this = state->tls_context->peer_pkey_fprint;
+ if (warn_compat_break_smtpd_tls_fpt_dgst)
+ msg_info("using backwards-compatible default setting "
+ VAR_SMTPD_TLS_FPT_DGST "=md5 to compute "
+ "certificate fingerprints");
break;
default:
known_action = str_name_code(search_actions, *action);
const char *reply_name, const char *reply_class,
const char *def_acl)
{
+ static int warned = 0;
static VSTRING *action = 0;
SMTPD_POLICY_CLNT *policy_clnt;
ENCODE_CN(subject, subject_buf, state->tls_context->peer_CN);
ENCODE_CN(issuer, issuer_buf, state->tls_context->issuer_CN);
+
+ /*
+ * XXX: Too noisy to warn for each policy lookup, especially because we
+ * don't even know whether the policy server will use the fingerprint. So
+ * warn at most once per process, though on only lightly loaded servers,
+ * it might come close to one warning per inbound message.
+ */
+ if (!warned
+ && warn_compat_break_smtpd_tls_fpt_dgst
+ && state->tls_context
+ && state->tls_context->peer_cert_fprint
+ && *state->tls_context->peer_cert_fprint) {
+ warned = 1;
+ msg_info("using backwards-compatible default setting "
+ VAR_SMTPD_TLS_FPT_DGST "=md5 to compute certificate "
+ "fingerprints");
+ }
#endif
if (attr_clnt_request(policy_clnt->client,
SOCKADDR_TO_HOSTADDR(sa, len, &state->client_addr,
(MAI_SERVPORT_STR *) 0, sa->sa_family);
else
- strncpy(state->client_addr.buf, "local", sizeof("local"));
+ strncpy(state->client_addr.buf, "local", sizeof("local") + 0);
if (msg_verbose)
msg_info("connect (%s %s)",
#ifdef AF_LOCAL
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_dane.c tls_certkey.c tls_session.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_context_print.c tls_proxy_context_scan.c \
tls_proxy_client_misc.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_dane.o tls_certkey.o tls_session.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_context_print.o tls_proxy_context_scan.o \
CFLAGS = $(DEBUG) $(OPT) $(DEFS)
INCL =
LIB = lib$(LIB_PREFIX)tls$(LIB_SUFFIX)
-TESTPROG= tls_dh tls_mgr tls_rsa tls_dane tls_certkey
+TESTPROG= tls_dh tls_mgr tls_dane tls_certkey
LIBS = ../../lib/lib$(LIB_PREFIX)dns$(LIB_SUFFIX) \
../../lib/lib$(LIB_PREFIX)global$(LIB_SUFFIX) \
$(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIB) $(LIBS) $(SYSLIBS)
mv junk $@.o
-tls_rsa: $(LIB) $(LIBS)
- mv $@.o junk
- $(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIB) $(LIBS) $(SYSLIBS)
- mv junk $@.o
-
tls_dane: $(LIB) $(LIBS)
mv $@.o junk
$(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIB) $(LIBS) $(SYSLIBS)
tls_dane.o: ../../include/events.h
tls_dane.o: ../../include/hex_code.h
tls_dane.o: ../../include/mail_params.h
+tls_dane.o: ../../include/midna_domain.h
tls_dane.o: ../../include/msg.h
tls_dane.o: ../../include/myaddrinfo.h
tls_dane.o: ../../include/mymalloc.h
tls_proxy_server_scan.o: tls.h
tls_proxy_server_scan.o: tls_proxy.h
tls_proxy_server_scan.o: tls_proxy_server_scan.c
-tls_rsa.o: ../../include/argv.h
-tls_rsa.o: ../../include/check_arg.h
-tls_rsa.o: ../../include/dns.h
-tls_rsa.o: ../../include/msg.h
-tls_rsa.o: ../../include/myaddrinfo.h
-tls_rsa.o: ../../include/name_code.h
-tls_rsa.o: ../../include/name_mask.h
-tls_rsa.o: ../../include/sock_addr.h
-tls_rsa.o: ../../include/sys_defs.h
-tls_rsa.o: ../../include/vbuf.h
-tls_rsa.o: ../../include/vstream.h
-tls_rsa.o: ../../include/vstring.h
-tls_rsa.o: tls.h
tls_rsa.o: tls_rsa.c
tls_scache.o: ../../include/argv.h
tls_scache.o: ../../include/check_arg.h
#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_HALF_DANE)
#define TLS_MUST_PKIX(l) ((l) >= TLS_LEV_VERIFY)
#define TLS_OPPORTUNISTIC(l) ((l) == TLS_LEV_MAY || (l) == TLS_LEV_DANE)
#define TLS_DANE_BASED(l) \
#define ssl_cipher_stack_t STACK_OF(SSL_CIPHER)
#define ssl_comp_stack_t STACK_OF(SSL_COMP)
-#if (OPENSSL_VERSION_NUMBER < 0x1000200fUL)
-#error "OpenSSL releases prior to 1.0.2 are no longer supported"
-#endif
-
- /* Backwards compatibility with OpenSSL < 1.1.0 */
-#if OPENSSL_VERSION_NUMBER < 0x10100000L
-#define OpenSSL_version_num SSLeay
-#define OpenSSL_version SSLeay_version
-#define OPENSSL_VERSION SSLEAY_VERSION
-#define X509_STORE_up_ref(store) \
- CRYPTO_add(&((store)->references), 1, CRYPTO_LOCK_X509)
-#define X509_up_ref(x) \
- CRYPTO_add(&((x)->references), 1, CRYPTO_LOCK_X509)
-#define EVP_PKEY_up_ref(k) \
- CRYPTO_add(&((k)->references), 1, CRYPTO_LOCK_EVP_PKEY)
-#define X509_STORE_CTX_get0_cert(ctx) ((ctx)->cert)
-#define X509_STORE_CTX_get0_untrusted(ctx) ((ctx)->untrusted)
-#define X509_STORE_CTX_set0_untrusted X509_STORE_CTX_set_chain
-#define X509_STORE_CTX_set0_trusted_stack X509_STORE_CTX_trusted_stack
-#define ASN1_STRING_get0_data ASN1_STRING_data
-#define X509_getm_notBefore X509_get_notBefore
-#define X509_getm_notAfter X509_get_notAfter
-#define TLS_method SSLv23_method
-#define TLS_client_method SSLv23_client_method
-#define TLS_server_method SSLv23_server_method
-#endif
-
- /* Backwards compatibility with OpenSSL < 1.1.1 */
-#if OPENSSL_VERSION_NUMBER < 0x1010100fUL
-#define SSL_CTX_set_num_tickets(ctx, num) ((void)0)
+#if (OPENSSL_VERSION_NUMBER < 0x1010100fUL)
+#error "OpenSSL releases prior to 1.1.1 are no longer supported"
#endif
/*-
* algorithm.
*/
typedef struct TLS_TLSA {
- char *mdalg; /* Algorithm for this digest list */
- ARGV *certs; /* Complete certificate digests */
- ARGV *pkeys; /* SubjectPublicKeyInfo digests */
+ uint8_t usage; /* DANE certificate usage */
+ uint8_t selector; /* DANE selector */
+ uint8_t mtype; /* Algorithm for this digest list */
+ uint16_t length; /* Length of associated data */
+ unsigned char *data; /* Associated data */
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;
-
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 */
+ TLS_TLSA *tlsa; /* TLSA records */
char *base_domain; /* Base domain of TLSA RRset */
int flags; /* Lookup status */
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_loglevel(const char *, const char *);
extern void tls_dane_flush(void);
-extern void tls_dane_verbose(int);
extern TLS_DANE *tls_dane_alloc(void);
-extern void tls_dane_add_ee_digests(TLS_DANE *, const char *, const char *,
- const char *);
+extern void tls_tlsa_free(TLS_TLSA *);
extern void tls_dane_free(TLS_DANE *);
+extern void tls_dane_add_fpt_digests(TLS_DANE *, const char *, const char *,
+ int);
extern TLS_DANE *tls_dane_resolve(unsigned, const char *, DNS_RR *, int);
extern int tls_dane_load_trustfile(TLS_DANE *, const char *);
char *peer_sni; /* SNI sent to or by the peer */
char *peer_cert_fprint; /* ASCII certificate fingerprint */
char *peer_pkey_fprint; /* ASCII public key fingerprint */
+ int level; /* Effective security level */
int peer_status; /* Certificate and match status */
const char *protocol;
const char *cipher_name;
VSTREAM *stream; /* Blocking-mode SMTP session */
/* DANE TLSA trust input and verification state */
const TLS_DANE *dane; /* DANE TLSA digests */
+ X509 *errorcert; /* Error certificate closest to leaf */
int errordepth; /* Chain depth of error cert */
- int tadepth; /* Chain depth of trust anchor */
int errorcode; /* First error at error depth */
- X509 *errorcert; /* Error certificate closest to leaf */
- x509_stack_t *untrusted; /* Certificate chain fodder */
- x509_stack_t *trusted; /* Internal root CA list */
+ int must_fail; /* Failed to load trust settings */
} TLS_SESS_STATE;
/*
#define TLS_LOG_DEBUG (1<<7)
#define TLS_LOG_TLSPKTS (1<<8)
#define TLS_LOG_ALLPKTS (1<<9)
+#define TLS_LOG_DANE (1<<10)
/*
* Client and Server application contexts
/*
* tls_dh.c
*/
-extern void tls_set_dh_from_file(const char *, int);
-extern DH *tls_tmp_dh_cb(SSL *, int, int);
-extern void tls_set_eecdh_curve(SSL_CTX *, const char *);
+extern void tls_set_dh_from_file(const char *);
+extern void tls_tmp_dh(SSL_CTX *);
extern void tls_auto_eecdh_curves(SSL_CTX *, const char *);
- /*
- * tls_rsa.c
- */
-extern RSA *tls_tmp_rsa_cb(SSL *, int, int);
-
/*
* tls_verify.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_verify_certificate_callback(int, X509_STORE_CTX *);
extern void tls_log_verify_error(TLS_SESS_STATE *);
/*
* tls_dane.c
*/
-extern int tls_dane_match(TLS_SESS_STATE *, int, X509 *, int);
-extern void tls_dane_set_callback(SSL_CTX *, TLS_SESS_STATE *);
+extern void tls_dane_log(TLS_SESS_STATE *);
+extern void tls_dane_digest_init(SSL_CTX *, const EVP_MD *);
+extern int tls_dane_enable(TLS_SESS_STATE *);
+extern TLS_TLSA *tlsa_prepend(TLS_TLSA *, uint8_t, uint8_t, uint8_t,
+ const unsigned char *, uint16_t);
/*
* tls_fprint.c
*/
extern char *tls_digest_encode(const unsigned char *, int);
-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 *);
+extern char *tls_serverid_digest(TLS_SESS_STATE *,
+ const TLS_CLIENT_START_PROPS *, long, const char *);
/*
* tls_certkey.c
extern void tls_print_errors(void);
extern void tls_info_callback(const SSL *, int, int);
extern long tls_bio_dump_cb(BIO *, int, const char *, int, long, long);
-extern int tls_validate_digest(const char *);
+extern const EVP_MD *tls_validate_digest(const char *);
/*
* tls_seed.c
/* use_chain - load cert, key and chain into ctx or ssl */
-#if OPENSSL_VERSION_NUMBER >= 0x1010100fUL
static int use_chain(pem_load_state_t *st)
{
int ret;
return ret;
}
-#else
-
-/* Legacy OpenSSL 1.0.2 and 1.1.0 interface */
-static int use_chain(pem_load_state_t *st)
-{
- int ret = 1;
-
-#define TRY(op, o) \
- ((st->ctx && SSL_CTX_##op(st->ctx, st->o)) || \
- (st->ssl && SSL_##op(st->ssl, st->o)))
-
- /*
- * This ensures the cert and key have the same type and match. A similar
- * check is performed in use_PrivateKey(), but only if if the key and
- * cert are of the same type.
- */
- if (!X509_check_private_key(st->cert, st->pkey))
- ret = 0;
-
- /*
- * XXX: With OpenSSL 1.0.2, setting the certificate clears any previous
- * mismatched key of the same type, so we don't detect conflicting chains
- * for the same algorithm, and silently use the last one.
- */
-
- /* use_certificate() increments the refcount */
- if (ret && !TRY(use_certificate, cert))
- ret = 0;
- X509_free(st->cert);
- st->cert = 0;
-
- /* use_PrivateKey() increments the refcount */
- if (ret && !TRY(use_PrivateKey, pkey))
- ret = 0;
- EVP_PKEY_free(st->pkey);
- st->pkey = 0;
-
- /* set0_chain() does not increment the refcount */
- if (!ret || !(ret = TRY(set0_chain, chain)))
- sk_X509_pop_free(st->chain, X509_free);
- /* The chain is now owned by the SSL library or freed, zero for next use */
- st->chain = 0;
-
- return ret;
-}
-
-#endif
-
/* load_cert - decode and load a DER-encoded X509 certificate */
static void load_cert(pem_load_state_t *st, unsigned char *buf,
&& strcmp(name, PEM_STRING_DSA) == 0)) {
load_pkey(st, pkey_type, buf, buflen);
} else if (!st->mixed) {
- msg_warn("loading %s: ignoring PEM type: %s", st->source, name);
+ msg_warn("loading %s: ignoring PEM type: %s", st->source, name);
}
OPENSSL_free(name);
OPENSSL_free(header);
char *key_file = 0;
SSL_CTX *ctx;
-#if OPENSSL_VERSION_NUMBER < 0x10100000L
-
- /*
- * Initialize the OpenSSL library by the book! To start with, we must
- * initialize the algorithms. We want cleartext error messages instead of
- * just error codes, so we load the error_strings.
- */
- SSL_load_error_strings();
- OpenSSL_add_ssl_algorithms();
-#endif
-
if (!(ctx = SSL_CTX_new(TLS_client_method()))) {
tls_print_errors();
exit(1);
/* available as:
/* .IP TLScontext->peer_status
/* A bitmask field that records the status of the peer certificate
-/* verification. This consists of one or more of
-/* TLS_CERT_FLAG_PRESENT, TLS_CERT_FLAG_ALTNAME, TLS_CERT_FLAG_TRUSTED,
-/* TLS_CERT_FLAG_MATCHED and TLS_CERT_FLAG_SECURED.
+/* verification. This consists of one or more of TLS_CERT_FLAG_PRESENT,
+/* TLS_CERT_FLAG_TRUSTED, TLS_CERT_FLAG_MATCHED and TLS_CERT_FLAG_SECURED.
/* .IP TLScontext->peer_CN
/* Extracted CommonName of the peer, or zero-length string if the
/* information could not be extracted.
tls_mgr_delete(TLScontext->cache_type, TLScontext->serverid);
}
+/* verify_extract_name - verify peer name and extract peer information */
+
+static void verify_extract_name(TLS_SESS_STATE *TLScontext, X509 *peercert,
+ const TLS_CLIENT_START_PROPS *props)
+{
+ int verbose;
+
+ verbose = TLScontext->log_mask &
+ (TLS_LOG_CERTMATCH | TLS_LOG_VERBOSE | TLS_LOG_PEERCERT);
+
+ /*
+ * On exit both peer_CN and issuer_CN should be set.
+ */
+ TLScontext->issuer_CN = tls_issuer_CN(peercert, TLScontext);
+ TLScontext->peer_CN = tls_peer_CN(peercert, TLScontext);
+
+ /*
+ * Is the certificate trust chain trusted and matched? Any required name
+ * checks are now performed internally in OpenSSL.
+ */
+ if (SSL_get_verify_result(TLScontext->con) == X509_V_OK) {
+ if (TLScontext->must_fail) {
+ msg_panic("%s: cert valid despite trust init failure",
+ TLScontext->namaddr);
+ } else if (TLS_MUST_MATCH(TLScontext->level)) {
+
+ /*
+ * Fully secured only if not insecure like half-dane. We use
+ * TLS_CERT_FLAG_MATCHED to satisfy policy, but
+ * TLS_CERT_FLAG_SECURED to log the effective security.
+ *
+ * Would ideally also exclude "verify" (as opposed to "secure")
+ * here, because that can be subject to insecure MX indirection,
+ * but that's rather incompatible (and not even the case with
+ * explicitly chosen non-default match patterns). Users have
+ * been warned.
+ */
+ if (!TLS_NEVER_SECURED(TLScontext->level))
+ TLScontext->peer_status |= TLS_CERT_FLAG_SECURED;
+ TLScontext->peer_status |= TLS_CERT_FLAG_MATCHED;
+
+ if (verbose) {
+ const char *peername = SSL_get0_peername(TLScontext->con);
+
+ if (peername)
+ msg_info("%s: matched peername: %s",
+ TLScontext->namaddr, peername);
+ tls_dane_log(TLScontext);
+ }
+ } else
+ TLScontext->peer_status |= TLS_CERT_FLAG_TRUSTED;
+ }
+
+ /*
+ * Give them a clue. Problems with trust chain verification are logged
+ * when the session is first negotiated, before the session is stored
+ * into the cache. We don't want mystery failures, so log the fact the
+ * real problem is to be found in the past.
+ */
+ if (!TLS_CERT_IS_MATCHED(TLScontext)
+ && (TLScontext->log_mask & TLS_LOG_UNTRUSTED)) {
+ if (TLScontext->session_reused == 0)
+ tls_log_verify_error(TLScontext);
+ else
+ msg_info("%s: re-using session with untrusted certificate, "
+ "look for details earlier in the log", props->namaddr);
+ }
+}
+
+/* add_namechecks - tell OpenSSL what names to check */
+
+static void add_namechecks(TLS_SESS_STATE *TLScontext,
+ const TLS_CLIENT_START_PROPS *props)
+{
+ SSL *ssl = TLScontext->con;
+ int namechecks_count = 0;
+ int i;
+
+ /* RFC6125: No part-label 'foo*bar.example.com' wildcards for SMTP */
+ SSL_set_hostflags(ssl, X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS);
+
+ for (i = 0; i < props->matchargv->argc; ++i) {
+ const char *name = props->matchargv->argv[i];
+ const char *aname;
+ int match_subdomain = 0;
+
+ if (strcasecmp(name, "nexthop") == 0) {
+ name = props->nexthop;
+ } else if (strcasecmp(name, "dot-nexthop") == 0) {
+ name = props->nexthop;
+ match_subdomain = 1;
+ } else if (strcasecmp(name, "hostname") == 0) {
+ name = props->host;
+ } else {
+ if (*name == '.') {
+ if (*++name == 0) {
+ msg_warn("%s: ignoring invalid match name: \".\"",
+ TLScontext->namaddr);
+ continue;
+ }
+ match_subdomain = 1;
+ }
+#ifndef NO_EAI
+ else {
+
+ /*
+ * Besides U+002E (full stop) IDNA2003 allows labels to be
+ * separated by any of the Unicode variants U+3002
+ * (ideographic full stop), U+FF0E (fullwidth full stop), and
+ * U+FF61 (halfwidth ideographic full stop). Their respective
+ * UTF-8 encodings are: E38082, EFBC8E and EFBDA1.
+ *
+ * IDNA2008 does not permit (upper) case and other variant
+ * differences in U-labels. The midna_domain_to_ascii()
+ * function, based on UTS46, normalizes such differences
+ * away.
+ *
+ * The IDNA to_ASCII conversion does not allow empty leading
+ * labels, so we handle these explicitly here.
+ */
+ unsigned char *cp = (unsigned char *) name;
+
+ if ((cp[0] == 0xe3 && cp[1] == 0x80 && cp[2] == 0x82)
+ || (cp[0] == 0xef && cp[1] == 0xbc && cp[2] == 0x8e)
+ || (cp[0] == 0xef && cp[1] == 0xbd && cp[2] == 0xa1)) {
+ if (name[3]) {
+ name = name + 3;
+ match_subdomain = 1;
+ }
+ }
+ }
+#endif
+ }
+
+ /*
+ * DNS subjectAltNames are required to be ASCII.
+ *
+ * Per RFC 6125 Section 6.4.4 Matching the CN-ID, follows the same rules
+ * (6.4.1, 6.4.2 and 6.4.3) that apply to subjectAltNames. In
+ * particular, 6.4.2 says that the reference identifier is coerced to
+ * ASCII, but no conversion is stated or implied for the CN-ID, so it
+ * seems it only matches if it is all ASCII. Otherwise, it is some
+ * other sort of name.
+ */
+#ifndef NO_EAI
+ if (!allascii(name) && (aname = midna_domain_to_ascii(name)) != 0) {
+ if (msg_verbose)
+ msg_info("%s asciified to %s", name, aname);
+ name = aname;
+ }
+#endif
+
+ if (!match_subdomain) {
+ if (SSL_add1_host(ssl, name))
+ ++namechecks_count;
+ else
+ msg_warn("%s: error loading match name: \"%s\"",
+ TLScontext->namaddr, name);
+ } else {
+ char *dot_name = concatenate(".", name, (char *) 0);
+
+ if (SSL_add1_host(ssl, dot_name))
+ ++namechecks_count;
+ else
+ msg_warn("%s: error loading match name: \"%s\"",
+ TLScontext->namaddr, dot_name);
+ myfree(dot_name);
+ }
+ }
+
+ /*
+ * If we failed to add any names, OpenSSL will perform no namechecks, so
+ * we set the "must_fail" bit to avoid verification false-positives.
+ */
+ if (namechecks_count == 0) {
+ msg_warn("%s: could not configure peer name checks",
+ TLScontext->namaddr);
+ TLScontext->must_fail = 1;
+ }
+}
+
+/* tls_auth_enable - set up TLS authentication */
+
+static int tls_auth_enable(TLS_SESS_STATE *TLScontext,
+ const TLS_CLIENT_START_PROPS *props)
+{
+ const char *sni = 0;
+
+ if (props->sni && *props->sni) {
+#ifndef NO_EAI
+ const char *aname;
+
+#endif
+
+ /*
+ * MTA-STS policy plugin compatibility: with servername=hostname,
+ * Postfix must send the MX hostname (not CNAME expanded).
+ */
+ if (strcmp(props->sni, "hostname") == 0)
+ sni = props->host;
+ else if (strcmp(props->sni, "nexthop") == 0)
+ sni = props->nexthop;
+ else
+ sni = props->sni;
+
+ /*
+ * The SSL_set_tlsext_host_name() documentation does not promise that
+ * every implementation will convert U-label form to A-label form.
+ */
+#ifndef NO_EAI
+ if (!allascii(sni) && (aname = midna_domain_to_ascii(sni)) != 0) {
+ if (msg_verbose)
+ msg_info("%s asciified to %s", sni, aname);
+ sni = aname;
+ }
+#endif
+ }
+ switch (TLScontext->level) {
+ case TLS_LEV_HALF_DANE:
+ case TLS_LEV_DANE:
+ case TLS_LEV_DANE_ONLY:
+
+ /*
+ * 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 make use of SNI the best
+ * opportunity to find the certificate they promised via the
+ * associated TLSA RRs.
+ *
+ * Since the hostname is DNSSEC-validated, it must be a DNS FQDN and
+ * thererefore valid for use with SNI.
+ */
+ if (SSL_dane_enable(TLScontext->con, 0) <= 0) {
+ msg_warn("%s: error enabling DANE-based certificate validation",
+ TLScontext->namaddr);
+ tls_print_errors();
+ return (0);
+ }
+ /* RFC7672 Section 3.1.1 specifies no name checks for DANE-EE(3) */
+ SSL_dane_set_flags(TLScontext->con, DANE_FLAG_NO_DANE_EE_NAMECHECKS);
+
+ /* Per RFC7672 the SNI name is the TLSA base domain */
+ sni = props->dane->base_domain;
+ add_namechecks(TLScontext, props);
+ break;
+
+ case TLS_LEV_FPRINT:
+ /* Synthetic DANE for fingerprint security */
+ if (SSL_dane_enable(TLScontext->con, 0) <= 0) {
+ msg_warn("%s: error enabling fingerprint certificate validation",
+ props->namaddr);
+ tls_print_errors();
+ return (0);
+ }
+ SSL_dane_set_flags(TLScontext->con, DANE_FLAG_NO_DANE_EE_NAMECHECKS);
+ break;
+
+ case TLS_LEV_SECURE:
+ case TLS_LEV_VERIFY:
+ if (TLScontext->dane != 0 && TLScontext->dane->tlsa != 0) {
+ /* Synthetic DANE for per-destination trust-anchors */
+ if (SSL_dane_enable(TLScontext->con, NULL) <= 0) {
+ msg_warn("%s: error configuring local trust anchors",
+ props->namaddr);
+ tls_print_errors();
+ return (0);
+ }
+ }
+ add_namechecks(TLScontext, props);
+ break;
+ default:
+ break;
+ }
+
+ if (sni) {
+ if (strlen(sni) > TLSEXT_MAXLEN_host_name) {
+ msg_warn("%s: ignoring too long SNI hostname: %.100s",
+ props->namaddr, sni);
+ return (0);
+ }
+
+ /*
+ * 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, sni)) {
+ msg_warn("%s: error setting SNI hostname to: %s", props->namaddr,
+ sni);
+ return (0);
+ }
+
+ /*
+ * The saved value is not presently used client-side, but could later
+ * be logged if acked by the server (requires new client-side
+ * callback to detect the ack). For now this just maintains symmetry
+ * with the server code, where do record the received SNI for
+ * logging.
+ */
+ TLScontext->peer_sni = mystrdup(sni);
+ if (TLScontext->log_mask & TLS_LOG_DEBUG)
+ msg_info("%s: SNI hostname: %s", props->namaddr, sni);
+ }
+ return (1);
+}
+
/* tls_client_init - initialize client-side TLS engine */
TLS_APPL_STATE *tls_client_init(const TLS_CLIENT_INIT_PROPS *props)
{
+ SSL_CTX *client_ctx;
+ TLS_APPL_STATE *app_ctx;
+ const EVP_MD *fpt_alg;
long off = 0;
int cachable;
int scache_timeout;
- SSL_CTX *client_ctx;
- TLS_APPL_STATE *app_ctx;
int log_mask;
/*
*/
tls_check_version();
-#if OPENSSL_VERSION_NUMBER < 0x10100000L
-
- /*
- * Initialize the OpenSSL library by the book! To start with, we must
- * initialize the algorithms. We want cleartext error messages instead of
- * just error codes, so we load the error_strings.
- */
- SSL_load_error_strings();
- OpenSSL_add_ssl_algorithms();
-#endif
-
/*
* Create an application data index for SSL objects, so that we can
* attach TLScontext information; this information is needed inside
* If the administrator specifies an unsupported digest algorithm, fail
* now, rather than in the middle of a TLS handshake.
*/
- if (!tls_validate_digest(props->mdalg)) {
+ if ((fpt_alg = tls_validate_digest(props->mdalg)) == 0) {
msg_warn("disabling TLS support");
return (0);
}
*/
SSL_CTX_set_verify_depth(client_ctx, props->verifydepth + 1);
+ /*
+ * This is a prerequisite for enabling DANE support in OpenSSL, but not a
+ * commitment to use DANE, thus suitable for both DANE and non-DANE TLS
+ * connections. Indeed we need this not just for DANE, but aslo for
+ * fingerprint and "tafile" support. Since it just allocates memory, it
+ * should never fail except when we're likely to fail anyway. Rather
+ * than try to run with crippled TLS support, just give up using TLS.
+ */
+ if (SSL_CTX_dane_enable(client_ctx) <= 0) {
+ msg_warn("OpenSSL DANE initialization failed: disabling TLS support");
+ tls_print_errors();
+ return (0);
+ }
+ tls_dane_digest_init(client_ctx, fpt_alg);
+
/*
* Protocol selection is destination dependent, so we delay the protocol
* selection options to the per-session SSL object.
return (0);
}
- /*
- * 2015-12-05: Ephemeral RSA removed from OpenSSL 1.1.0-dev
- */
-#if OPENSSL_VERSION_NUMBER < 0x10100000L
-
- /*
- * According to the OpenSSL documentation, temporary RSA key is needed
- * export ciphers are in use. We have to provide one, so well, we just do
- * it.
- */
- SSL_CTX_set_tmp_rsa_callback(client_ctx, tls_tmp_rsa_cb);
-#endif
-
/*
* With OpenSSL 1.0.2 and later the client EECDH curve list becomes
* configurable with the preferred curve negotiated via the supported
return (app_ctx);
}
-/* match_servername - match servername against pattern */
-
-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 *domain;
- const char *parent;
- const char *aname;
- int match_subdomain;
- int i;
- int idlen;
- int domlen;
-
- if ((cmatch_argv = props->matchargv) == 0)
- return 0;
-
-#ifndef NO_EAI
-
- /*
- * DNS subjectAltNames are required to be ASCII.
- *
- * Per RFC 6125 Section 6.4.4 Matching the CN-ID, follows the same rules
- * (6.4.1, 6.4.2 and 6.4.3) that apply to subjectAltNames. In
- * particular, 6.4.2 says that the reference identifier is coerced to
- * ASCII, but no conversion is stated or implied for the CN-ID, so it
- * seems it only matches if it is all ASCII. Otherwise, it is some other
- * sort of name.
- */
- if (!allascii(certid))
- return (0);
- if (!allascii(nexthop) && (aname = midna_domain_to_ascii(nexthop)) != 0) {
- if (msg_verbose)
- msg_info("%s asciified to %s", nexthop, aname);
- nexthop = aname;
- }
-#endif
-
- /*
- * Match the certid against each pattern until we find a match.
- */
- for (i = 0; i < cmatch_argv->argc; ++i) {
- match_subdomain = 0;
- if (!strcasecmp(cmatch_argv->argv[i], "nexthop"))
- domain = nexthop;
- else if (!strcasecmp(cmatch_argv->argv[i], "hostname"))
- domain = hname;
- else if (!strcasecmp(cmatch_argv->argv[i], "dot-nexthop")) {
- domain = nexthop;
- match_subdomain = 1;
- } else {
- domain = cmatch_argv->argv[i];
- if (*domain == '.') {
- if (domain[1]) {
- ++domain;
- match_subdomain = 1;
- }
- }
-#ifndef NO_EAI
-
- /*
- * Besides U+002E (full stop) IDNA2003 allows labels to be
- * separated by any of the Unicode variants U+3002 (ideographic
- * full stop), U+FF0E (fullwidth full stop), and U+FF61
- * (halfwidth ideographic full stop). Their respective UTF-8
- * encodings are: E38082, EFBC8E and EFBDA1.
- *
- * IDNA2008 does not permit (upper) case and other variant
- * differences in U-labels. The midna_domain_to_ascii() function,
- * based on UTS46, normalizes such differences away.
- *
- * The IDNA to_ASCII conversion does not allow empty leading labels,
- * so we handle these explicitly here.
- */
- else {
- unsigned char *cp = (unsigned char *) domain;
-
- if ((cp[0] == 0xe3 && cp[1] == 0x80 && cp[2] == 0x82)
- || (cp[0] == 0xef && cp[1] == 0xbc && cp[2] == 0x8e)
- || (cp[0] == 0xef && cp[1] == 0xbd && cp[2] == 0xa1)) {
- if (domain[3]) {
- domain = domain + 3;
- match_subdomain = 1;
- }
- }
- }
- if (!allascii(domain)
- && (aname = midna_domain_to_ascii(domain)) != 0) {
- if (msg_verbose)
- msg_info("%s asciified to %s", domain, aname);
- domain = aname;
- }
-#endif
- }
-
- /*
- * Sub-domain match: certid is any sub-domain of hostname.
- */
- 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 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(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);
-}
-
-/* verify_extract_name - verify peer name and extract peer information */
-
-static void verify_extract_name(TLS_SESS_STATE *TLScontext, X509 *peercert,
- const TLS_CLIENT_START_PROPS *props)
-{
- int i;
- int r;
- int matched = 0;
- int dnsname_match;
- int verify_peername = 0;
- int log_certmatch;
- int verbose;
- const char *dnsname;
- const GENERAL_NAME *gn;
- general_name_stack_t *gens;
-
- /*
- * On exit both peer_CN and issuer_CN should be set.
- */
- TLScontext->issuer_CN = tls_issuer_CN(peercert, TLScontext);
-
- /*
- * Is the certificate trust chain valid and trusted?
- */
- if (SSL_get_verify_result(TLScontext->con) == X509_V_OK)
- TLScontext->peer_status |= TLS_CERT_FLAG_TRUSTED;
-
- /*
- * 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? */
- log_certmatch = TLScontext->log_mask & TLS_LOG_CERTMATCH;
-
- /* Log cert details when processing? */
- verbose = log_certmatch || (TLScontext->log_mask & TLS_LOG_VERBOSE);
-
- if (verify_peername || log_certmatch) {
-
- /*
- * Verify the dNSName(s) in the peer certificate against the nexthop
- * and hostname.
- *
- * If DNS names are present, we use the first matching (or else simply
- * the first) DNS name as the subject CN. The CommonName in the
- * issuer DN is obsolete when SubjectAltName is available. This
- * yields much less surprising logs, because we log the name we
- * verified or a name we checked and failed to match.
- *
- * XXX: The nexthop and host name may both be the same network address
- * rather than a DNS name. In this case we really should be looking
- * for GEN_IPADD entries, not GEN_DNS entries.
- *
- * XXX: In ideal world the caller who used the address to build the
- * connection would tell us that the nexthop is the connection
- * address, but if that is not practical, we can parse the nexthop
- * again here.
- */
- gens = X509_get_ext_d2i(peercert, NID_subject_alt_name, 0, 0);
- if (gens) {
- r = sk_GENERAL_NAME_num(gens);
- for (i = 0; i < r; ++i) {
- gn = sk_GENERAL_NAME_value(gens, i);
- if (gn->type != GEN_DNS)
- continue;
-
- /*
- * Even if we have an invalid DNS name, we still ultimately
- * ignore the CommonName, because subjectAltName:DNS is
- * present (though malformed). Replace any previous peer_CN
- * if empty or we get a match.
- *
- * We always set at least an empty peer_CN if the ALTNAME cert
- * flag is set. If not, we set peer_CN from the cert
- * CommonName below, so peer_CN is always non-null on return.
- */
- TLScontext->peer_status |= TLS_CERT_FLAG_ALTNAME;
- dnsname = tls_dns_name(gn, TLScontext);
- if (dnsname && *dnsname) {
- if ((dnsname_match = match_servername(dnsname, props)) != 0)
- matched++;
- /* Keep the first matched name. */
- if (TLScontext->peer_CN
- && ((dnsname_match && matched == 1)
- || *TLScontext->peer_CN == 0)) {
- myfree(TLScontext->peer_CN);
- TLScontext->peer_CN = 0;
- }
- if (verbose)
- msg_info("%s: %ssubjectAltName: %s", props->namaddr,
- dnsname_match ? "Matched " : "", dnsname);
- }
- if (TLScontext->peer_CN == 0)
- TLScontext->peer_CN = mystrdup(dnsname ? dnsname : "");
- if (matched && !log_certmatch)
- break;
- }
- if (verify_peername && matched)
- TLScontext->peer_status |= TLS_CERT_FLAG_MATCHED;
-
- /*
- * (Sam Rushing, Ironport) Free stack *and* member GENERAL_NAME
- * objects
- */
- sk_GENERAL_NAME_pop_free(gens, GENERAL_NAME_free);
- }
-
- /*
- * No subjectAltNames, peer_CN is taken from CommonName.
- */
- if (TLScontext->peer_CN == 0) {
- TLScontext->peer_CN = tls_peer_CN(peercert, TLScontext);
- if (*TLScontext->peer_CN)
- matched = match_servername(TLScontext->peer_CN, props);
- if (verify_peername && matched)
- TLScontext->peer_status |= TLS_CERT_FLAG_MATCHED;
- if (verbose)
- msg_info("%s %sCommonName %s", props->namaddr,
- matched ? "Matched " : "", TLScontext->peer_CN);
- } else if (verbose) {
- char *tmpcn = tls_peer_CN(peercert, TLScontext);
-
- /*
- * Though the CommonName was superceded by a subjectAltName, log
- * it when certificate match debugging was requested.
- */
- msg_info("%s CommonName %s", TLScontext->namaddr, tmpcn);
- myfree(tmpcn);
- }
- } else
- TLScontext->peer_CN = tls_peer_CN(peercert, TLScontext);
-
- /*
- * Give them a clue. Problems with trust chain verification are logged
- * when the session is first negotiated, before the session is stored
- * into the cache. We don't want mystery failures, so log the fact the
- * real problem is to be found in the past.
- */
- if (!TLS_CERT_IS_TRUSTED(TLScontext)
- && (TLScontext->log_mask & TLS_LOG_UNTRUSTED)) {
- if (TLScontext->session_reused == 0)
- tls_log_verify_error(TLScontext);
- else
- msg_info("%s: re-using session with untrusted certificate, "
- "look for details earlier in the log", props->namaddr);
- }
-}
-
-/* verify_extract_print - extract and verify peer fingerprint */
-
-static void verify_extract_print(TLS_SESS_STATE *TLScontext, X509 *peercert,
- const TLS_CLIENT_START_PROPS *props)
-{
- TLScontext->peer_cert_fprint = tls_cert_fprint(peercert, props->mdalg);
- TLScontext->peer_pkey_fprint = tls_pkey_fprint(peercert, props->mdalg);
-
- /*
- * 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 (TLS_DANE_HASEE(props->dane)
- && tls_dane_match(TLScontext, TLS_DANE_EE, peercert, 0))
- TLScontext->peer_status |=
- TLS_CERT_FLAG_TRUSTED | TLS_CERT_FLAG_MATCHED;
-}
-
/*
* This is the actual startup routine for the connection. We expect that the
* buffers are flushed and the "220 Ready to start TLS" was received by us,
SSL_SESSION *session = 0;
TLS_SESS_STATE *TLScontext;
TLS_APPL_STATE *app_ctx = props->ctx;
- const char *sni = 0;
- char *myserverid;
int log_mask = app_ctx->log_mask;
/*
* errors even when disabled by default for opportunistic sessions. For
* DANE this only applies when using trust-anchor associations.
*/
- if (TLS_MUST_TRUST(props->tls_level)
- && (!TLS_DANE_BASED(props->tls_level) || TLS_DANE_HASTA(props->dane)))
+ if (TLS_MUST_MATCH(props->tls_level))
log_mask |= TLS_LOG_UNTRUSTED;
if (log_mask & TLS_LOG_VERBOSE)
props->namaddr, props->protocols);
return (0);
}
- /* DANE requires SSLv3 or later, not SSLv2. */
+
+ /*
+ * Though RFC7672 set the floor at SSLv3, we really can and should
+ * require TLS 1.0, since e.g. we send SNI, which is a TLS 1.0 extension.
+ * No DANE domains have been observed to support only SSLv3.
+ *
+ * XXX: Would be nice to make that TLS 1.2 at some point. Users can choose
+ * to exclude TLS 1.0 and TLS 1.1 if they find they don't run into any
+ * problems doing that.
+ */
if (TLS_DANE_BASED(props->tls_level))
- protomask |= TLS_PROTOCOL_SSLv2;
+ protomask |= TLS_PROTOCOL_SSLv2 | TLS_PROTOCOL_SSLv3;
/*
* Allocate a new TLScontext for the new connection and get an SSL
*/
TLScontext = tls_alloc_sess_context(log_mask, props->namaddr);
TLScontext->cache_type = app_ctx->cache_type;
+ TLScontext->level = props->tls_level;
if ((TLScontext->con = SSL_new(app_ctx->ssl_ctx)) == NULL) {
msg_warn("Could not allocate 'TLScontext->con' with SSL_new()");
if (log_mask & TLS_LOG_VERBOSE)
msg_info("%s: TLS cipher list \"%s\"", props->namaddr, cipher_list);
+ TLScontext->stream = props->stream;
+ TLScontext->mdalg = props->mdalg;
+
+ /* Alias DANE digest info from props */
+ TLScontext->dane = props->dane;
+
+ if (!SSL_set_ex_data(TLScontext->con, TLScontext_index, TLScontext)) {
+ msg_warn("Could not set application data for 'TLScontext->con'");
+ tls_print_errors();
+ tls_free_context(TLScontext);
+ return (0);
+ }
+
+ /*
+ * Apply session protocol restrictions.
+ */
+ if (protomask != 0)
+ SSL_set_options(TLScontext->con, TLS_SSL_OP_PROTOMASK(protomask));
+
+ /*
+ * When applicable, configure DNS-based or synthetic (fingerprint or
+ * local trust anchor) DANE authentication, enable an appropriate SNI
+ * name and peer name matching.
+ *
+ * NOTE, this can change the effective security level, and needs to happen
+ * early.
+ */
+ if (!tls_auth_enable(TLScontext, props)) {
+ tls_free_context(TLScontext);
+ return (0);
+ }
+
+ /*
+ * Try to convey the configured TLSA records for this connection to the
+ * OpenSSL library. If none are "usable", we'll fall back to "encrypt"
+ * when authentication is not mandatory, otherwise we must arrange to
+ * ensure authentication failure.
+ */
+ if (TLScontext->dane && TLScontext->dane->tlsa) {
+ int usable = tls_dane_enable(TLScontext);
+ int must_fail = usable <= 0;
+
+ if (usable == 0) {
+ switch (TLScontext->level) {
+ case TLS_LEV_HALF_DANE:
+ case TLS_LEV_DANE:
+ msg_warn("%s: all TLSA records unusable, fallback to "
+ "unauthenticated TLS", TLScontext->namaddr);
+ must_fail = 0;
+ TLScontext->level = TLS_LEV_ENCRYPT;
+ break;
+
+ case TLS_LEV_FPRINT:
+ msg_warn("%s: all fingerprints unusable", TLScontext->namaddr);
+ break;
+ case TLS_LEV_DANE_ONLY:
+ msg_warn("%s: all TLSA records unusable", TLScontext->namaddr);
+ break;
+ case TLS_LEV_SECURE:
+ case TLS_LEV_VERIFY:
+ msg_warn("%s: all trust anchors unusable", TLScontext->namaddr);
+ break;
+ }
+ }
+ TLScontext->must_fail |= must_fail;
+ }
+
/*
- * OpenSSL will ignore cached sessions that use the wrong protocol. So we
- * do not need to filter out cached sessions with the "wrong" protocol,
+ * We compute the policy digest after we compute the SNI name in
+ * tls_auth_enable() and possibly update the TLScontext security level.
+ *
+ * OpenSSL will ignore cached sessions that use the wrong protocol. So we do
+ * not need to filter out cached sessions with the "wrong" protocol,
* rather OpenSSL will simply negotiate a new session.
*
* We salt the session lookup key with the protocol list, so that sessions
* 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->serverid = myserverid;
- TLScontext->stream = props->stream;
- TLScontext->mdalg = props->mdalg;
-
- /* Alias DANE digest info from props */
- TLScontext->dane = props->dane;
-
- if (!SSL_set_ex_data(TLScontext->con, TLScontext_index, TLScontext)) {
- msg_warn("Could not set application data for 'TLScontext->con'");
- tls_print_errors();
- tls_free_context(TLScontext);
- return (0);
- }
+ TLScontext->serverid =
+ tls_serverid_digest(TLScontext, props, protomask, cipher_list);
/*
- * Apply session protocol restrictions.
+ * When authenticating the peer, use 80-bit plus OpenSSL security level
+ *
+ * XXX: We should perhaps use security level 1 also for mandatory
+ * encryption, with only "may" tolerating weaker algorithms. But that
+ * could mean no TLS 1.0 with OpenSSL >= 3.0 and encrypt, unless I get my
+ * patch in on time to conditionally re-enable SHA1 at security level 1,
+ * and we add code to make it so.
+ *
+ * That said, with "encrypt", we could reasonably require TLS 1.2?
*/
- if (protomask != 0)
- SSL_set_options(TLScontext->con, TLS_SSL_OP_PROTOMASK(protomask));
-
-#ifdef SSL_SECOP_PEER
- /* When authenticating the peer, use 80-bit plus OpenSSL security level */
- if (TLS_MUST_MATCH(props->tls_level))
+ if (TLS_MUST_MATCH(TLScontext->level))
SSL_set_security_level(TLScontext->con, 1);
-#endif
/*
* XXX To avoid memory leaks we must always call SSL_SESSION_free() after
SSL_SESSION_free(session); /* 200411 */
}
}
-#ifdef TLSEXT_MAXLEN_host_name
- if (TLS_DANE_BASED(props->tls_level)) {
-
- /*
- * 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).
- *
- * Per RFC7672, the required SNI name is the TLSA "base domain" (the one
- * used to construct the "_25._tcp.<fqdn>" TLSA record DNS query).
- *
- * Since the hostname is DNSSEC-validated, it must be a DNS FQDN and
- * thererefore valid for use with SNI.
- */
- sni = props->dane->base_domain;
- } else if (props->sni && *props->sni) {
-
- /*
- * MTA-STS policy plugin compatibility: with servername=hostname,
- * Postfix must send the MX hostname (not CNAME expanded).
- */
- if (strcmp(props->sni, "hostname") == 0)
- sni = props->host;
- else if (strcmp(props->sni, "nexthop") == 0)
- sni = props->nexthop;
- else
- sni = props->sni;
- }
- if (sni && strlen(sni) <= TLSEXT_MAXLEN_host_name) {
-
- /*
- * 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, sni)) {
- msg_warn("%s: error setting SNI hostname to: %s", props->namaddr,
- sni);
- tls_free_context(TLScontext);
- return (0);
- }
-
- /*
- * The saved value is not presently used client-side, but could later
- * be logged if acked by the server (requires new client-side
- * callback to detect the ack). For now this just maintains symmetry
- * with the server code, where do record the received SNI for
- * logging.
- */
- TLScontext->peer_sni = mystrdup(sni);
- if (log_mask & TLS_LOG_DEBUG)
- msg_info("%s: SNI hostname: %s", props->namaddr, sni);
- }
-#endif
/*
* Before really starting anything, try to seed the PRNG a little bit
if (log_mask & TLS_LOG_TLSPKTS)
BIO_set_callback(SSL_get_rbio(TLScontext->con), tls_bio_dump_cb);
- tls_dane_set_callback(app_ctx->ssl_ctx, TLScontext);
-
/*
* If we don't trigger the handshake in the library, leave control over
* SSL_connect/read/write/etc with the application.
* fingerprint first, and avoid logging verified as untrusted in the
* call to verify_extract_name().
*/
- verify_extract_print(TLScontext, peercert, props);
+ TLScontext->peer_cert_fprint = tls_cert_fprint(peercert, props->mdalg);
+ TLScontext->peer_pkey_fprint = tls_pkey_fprint(peercert, props->mdalg);
verify_extract_name(TLScontext, peercert, props);
if (TLScontext->log_mask &
if (TLScontext->stream != 0)
tls_stream_start(props->stream, TLScontext);
- /*
- * Fully secured only if trusted, matched and not insecure like halfdane.
- * Should perhaps also exclude "verify" (as opposed to "secure") here,
- * because that can be subject to insecure MX indirection, but that's
- * rather incompatible. Users have been warned.
- */
- if (TLS_CERT_IS_PRESENT(TLScontext)
- && TLS_CERT_IS_TRUSTED(TLScontext)
- && TLS_CERT_IS_MATCHED(TLScontext)
- && !TLS_NEVER_SECURED(props->tls_level))
- TLScontext->peer_status |= TLS_CERT_FLAG_SECURED;
-
/*
* With the handshake done, extract TLS 1.3 signature metadata.
*/
/* SYNOPSIS
/* #include <tls.h>
/*
+/* void tls_dane_loglevel(log_param, log_level);
+/* const char *log_param;
+/* const char *log_level;
+/*
/* int tls_dane_avail()
/*
/* void tls_dane_flush()
/*
-/* void tls_dane_verbose(on)
-/* int on;
-/*
/* TLS_DANE *tls_dane_alloc()
/*
+/* void tls_tlsa_free(tlsa)
+/* TLS_TLSA *tlsa;
+/*
/* void tls_dane_free(dane)
/* TLS_DANE *dane;
/*
-/* void tls_dane_add_ee_digests(dane, mdalg, digest, delim)
+/* void tls_dane_add_fpt_digests(dane, digest, delim, smtp_mode)
/* TLS_DANE *dane;
-/* const char *mdalg;
/* const char *digest;
/* const char *delim;
+/* int smtp_mode;
+/*
+/* TLS_TLSA *tlsa_prepend(tlsa, usage, selector, mtype, data, len)
+/* TLS_TLSA *tlsa;
+/* uint8_t usage;
+/* uint8_t selector;
+/* uint8_t mtype;
+/* const unsigned char *data;
+/* uint16_t length;
/*
/* int tls_dane_load_trustfile(dane, tafile)
/* TLS_DANE *dane;
/* const char *tafile;
/*
-/* int tls_dane_match(TLSContext, usage, cert, depth)
-/* TLS_SESS_STATE *TLScontext;
-/* int usage;
-/* X509 *cert;
-/* int depth;
-/*
-/* void tls_dane_set_callback(ssl_ctx, TLScontext)
-/* SSL_CTX *ssl_ctx;
-/* TLS_SESS_STATE *TLScontext;
-/*
/* TLS_DANE *tls_dane_resolve(port, proto, hostrr, forcetlsa)
/* unsigned port;
/* const char *proto;
/* DNS_RR *hostrr;
/* int forcetlsa;
/*
+/* void tls_dane_digest_init(ctx, fpt_alg)
+/* SSL_CTX *ctx;
+/* const EVP_MD *fpt_alg;
+/*
+/* void tls_dane_enable(TLScontext)
+/* TLS_SESS_STATE *TLScontext;
+/*
+/* void tls_dane_log(TLScontext)
+/* TLS_SESS_STATE *TLScontext;
+/*
/* int tls_dane_unusable(dane)
/* const TLS_DANE *dane;
/*
/* int tls_dane_notfound(dane)
/* const TLS_DANE *dane;
/* DESCRIPTION
+/* tls_dane_loglevel() allows the policy lookup functions in the DANE
+/* library to examine the application's TLS loglevel in and possibly
+/* produce a more detailed activity log.
+/*
/* 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.
+/* are present in libresolv.
/*
/* 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.
/*
+/* tls_tlsa_free() frees a TLSA record linked list.
+/*
/* tls_dane_free() frees the structure allocated by tls_dane_alloc().
/*
-/* tls_dane_add_ee_digests() splits "digest" using the characters in
-/* "delim" as delimiters and stores the results on the EE match list
-/* to match either a certificate or a public key. This is an incremental
+/* tls_dane_digest_init() configures OpenSSL to support the configured
+/* DANE TLSA digests and private-use fingerprint digest.
+/*
+/* tlsa_prepend() prepends a TLSA record to the head of a linked list
+/* which may be null when the list is empty. The result value is the
+/* new list head.
+/*
+/* tls_dane_add_fpt_digests() splits "digest" using the characters in
+/* "delim" as delimiters and generates corresponding synthetic DANE TLSA
+/* records with matching type 255 (private-use), which we associated with
+/* the configured fingerprint digest algorithm. This is an incremental
/* interface, that builds a TLS_DANE structure outside the cache by
/* manually adding entries.
/*
/* tls_dane_load_trustfile() imports trust-anchor certificates and
/* public keys from a file (rather than DNS TLSA records).
/*
-/* tls_dane_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_dane_set_callback() wraps the SSL certificate verification logic
-/* in a function that modifies the input trust chain and trusted
-/* certificate store to map DANE TA validation onto the existing PKI
-/* verification model. When TLScontext is NULL the callback is
-/* cleared, otherwise it is set. This callback should only be set
-/* when out-of-band trust-anchors (via DNSSEC DANE TLSA records or
-/* per-destination local configuration) are provided. Such trust
-/* anchors always override the legacy public CA PKI. Otherwise, the
-/* callback MUST be cleared.
-/*
/* tls_dane_resolve() maps a (port, protocol, hostrr) tuple to a
/* corresponding TLS_DANE policy structure found in the DNS. The port
/* argument is in network byte order. A null pointer is returned when
/* return value is a pointer to the corresponding TLS_DANE structure.
/* The caller must free the structure via tls_dane_free().
/*
+/* tls_dane_enable() enables DANE-style certificate checks for connections
+/* that are configured with TLSA records. The TLSA records may be from
+/* DNS (at the "dane", "dane-only" and "half-dane" security levels), or be
+/* synthetic in support of either the "fingerprint" level or local trust
+/* anchor based validation with the "secure" and "verify" levels. The
+/* return value is the number of "usable" TLSA records loaded, or negative
+/* if a record failed to load due to an internal OpenSSL problems, rather
+/* than an issue with the record making that record "unusable".
+/*
+/* tls_dane_log() logs successful verification via DNS-based or
+/* synthetic DANE TLSA RRs (fingerprint or "tafile").
+/*
/* 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
/* a mandatory TLS fallback policy.
/*
/* Arguments:
+/* .IP ctx
+/* SSL context to be configured with the chosen digest algorithms.
+/* .IP fpt_alg
+/* The OpenSSL EVP digest algorithm handle for the fingerprint digest.
+/* .IP tlsa
+/* TLSA record linked list head, initially NULL.
+/* .IP usage
+/* DANE TLSA certificate usage field.
+/* .IP selector
+/* DANE TLSA selector field.
+/* .IP mtype
+/* DANE TLSA matching type field
+/* .IP data
+/* DANE TLSA associated data field (raw binary form), copied for internal
+/* use. The caller is responsible for freeing his own copy.
+/* .IP length
+/* Length of DANE TLSA associated DATA field.
/* .IP dane
/* Pointer to a TLS_DANE structure that lists the valid trust-anchor
/* and end-entity full-certificate and/or public-key digests.
/* When true, TLSA lookups are performed even when the qname and rname
/* are insecure. This is only useful in the unlikely case that DLV is
/* used to secure the TLSA RRset in an otherwise insecure zone.
-/* .IP TLScontext
-/* Client context with TA/EE matching data and related state.
-/* .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 ssl_ctx
-/* The global SSL_CTX structure used to initialize child SSL
-/* conenctions.
-/* .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 log_param
+/* The TLS log level parameter name whose value is the log_level argument.
+/* .IP log_level
+/* The application TLS log level, which may affect dane lookup verbosity.
/* .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.
+/* .IP smtp_mode
+/* Is the caller an SMTP client or an LMTP client?
+/* .IP tafile;
+/* A file with trust anchor certificates or public keys in PEM format.
/* LICENSE
/* .ad
/* .fi
#include <msg.h>
#include <mymalloc.h>
#include <stringops.h>
+#include <midna_domain.h>
#include <vstring.h>
#include <events.h> /* event_time() */
#include <timecmp.h>
#undef DANE_TLSA_SUPPORT
-#if defined(TLSEXT_MAXLEN_host_name) && RES_USE_DNSSEC && RES_USE_EDNS0
+#if RES_USE_DNSSEC && RES_USE_EDNS0
#define DANE_TLSA_SUPPORT
static int dane_tlsa_support = 1;
#endif
-static const char *signalg;
-static ASN1_OBJECT *serverAuth;
-
/*
- * https://www.iana.org/assignments/dane-parameters/dane-parameters.xhtml
+ * A NULL alg field disables the algorithm at the codepoint passed to the
+ * SSL_CTX_dane_mtype_set(3) function. The ordinals are used for digest
+ * agility, higher is "better" (presumed stronger).
*/
-typedef struct {
- const char *mdalg;
- uint8_t dane_id;
-} iana_digest;
-
-static iana_digest iana_table[] = {
- {"", DNS_TLSA_MATCHING_TYPE_NO_HASH_USED},
- {"sha256", DNS_TLSA_MATCHING_TYPE_SHA256},
- {"sha512", DNS_TLSA_MATCHING_TYPE_SHA512},
- {0, 0}
-};
-
-typedef struct dane_digest {
- struct dane_digest *next; /* linkage */
- const char *mdalg; /* OpenSSL name */
- const EVP_MD *md; /* OpenSSL EVP handle */
- int len; /* digest octet length */
- int pref; /* tls_dane_digests index or -1 */
- uint8_t dane_id; /* IANA id */
-} dane_digest;
-
-#define MAXDIGESTS 256 /* RFC limit */
-static dane_digest *digest_list;
+typedef struct dane_mtype {
+ const EVP_MD *alg;
+ uint8_t ord;
+} dane_mtype;
/*
* This is not intended to be a long-term cache of pre-parsed TLSA data,
#define CACHE_SIZE 20
static CTABLE *dane_cache;
-static int dane_initialized;
-static int dane_verbose;
-
-/* tls_dane_verbose - enable/disable verbose logging */
-
-void tls_dane_verbose(int on)
-{
- dane_verbose = on;
-}
-
-/* add_digest - validate and append digest to digest list */
-
-static dane_digest *add_digest(char *mdalg, int pref)
-{
- iana_digest *i;
- dane_digest *d;
- int dane_id = -1;
- const char *dane_mdalg = mdalg;
- char *value = split_at(mdalg, '=');
- const EVP_MD *md = 0;
- size_t mdlen = 0;
-
- if (value && *value) {
- unsigned long l;
- char *endcp;
-
- /*
- * XXX: safe_strtoul() does not flag empty or white-space only input.
- * Since we get idbuf by splitting white-space/comma delimited
- * tokens, this is not a problem here. Fixed as of 210131209.
- */
- l = safe_strtoul(value, &endcp, 10);
- if ((l == 0 && (errno == EINVAL || endcp == value))
- || l >= MAXDIGESTS
- || *endcp) {
- msg_warn("Invalid matching type number in %s: %s=%s",
- VAR_TLS_DANE_DIGESTS, mdalg, value);
- return (0);
- }
- dane_id = l;
- }
-
- /*
- * Check for known IANA conflicts
- */
- for (i = iana_table; i->mdalg; ++i) {
- if (*mdalg && strcasecmp(i->mdalg, mdalg) == 0) {
- if (dane_id >= 0 && i->dane_id != dane_id) {
- msg_warn("Non-standard value in %s: %s%s%s",
- VAR_TLS_DANE_DIGESTS, mdalg,
- value ? "=" : "", value ? value : "");
- return (0);
- }
- dane_id = i->dane_id;
- } else if (i->dane_id == dane_id) {
- if (*mdalg) {
- msg_warn("Non-standard algorithm in %s: %s%s%s",
- VAR_TLS_DANE_DIGESTS, mdalg,
- value ? "=" : "", value ? value : "");
- return (0);
- }
- dane_mdalg = i->mdalg;
- }
- }
-
- /*
- * Check for unknown implicit digest or value
- */
- if (dane_id < 0 || (dane_id > 0 && !*dane_mdalg)) {
- msg_warn("Unknown incompletely specified element in %s: %s%s%s",
- VAR_TLS_DANE_DIGESTS, mdalg,
- value ? "=" : "", value ? value : "");
- return 0;
- }
-
- /*
- * Check for duplicate entries
- */
- for (d = digest_list; d; d = d->next) {
- if (strcasecmp(d->mdalg, dane_mdalg) == 0
- || d->dane_id == dane_id) {
- msg_warn("Duplicate element in %s: %s%s%s",
- VAR_TLS_DANE_DIGESTS, mdalg,
- value ? "=" : "", value ? value : "");
- return (0);
- }
- }
-
- if (*dane_mdalg
- && ((md = EVP_get_digestbyname(dane_mdalg)) == 0
- || (mdlen = EVP_MD_size(md)) <= 0
- || mdlen > EVP_MAX_MD_SIZE)) {
- msg_warn("Unimplemented digest algorithm in %s: %s%s%s",
- VAR_TLS_DANE_DIGESTS, mdalg,
- value ? "=" : "", value ? value : "");
- return (0);
- }
- d = (dane_digest *) mymalloc(sizeof(*d));
- d->next = digest_list;
- d->mdalg = mystrdup(dane_mdalg);
- d->md = md;
- d->len = mdlen;
- d->pref = pref;
- d->dane_id = dane_id;
-
- return (digest_list = d);
-}
-
-/* digest_byid - locate digest_table entry for given IANA id */
-
-static dane_digest *digest_byid(uint8_t dane_id)
-{
- dane_digest *d;
-
- for (d = digest_list; d; d = d->next)
- if (d->dane_id == dane_id)
- return (d);
- return (0);
-}
+static int log_mask;
-/* digest_pref_byid - digest preference by IANA id */
+/* tls_dane_logmask - configure policy lookup logging */
-static int digest_pref_byid(uint8_t dane_id)
+void tls_dane_loglevel(const char *log_param, const char *log_level)
{
- dane_digest *d = digest_byid(dane_id);
-
- return (d ? (d->pref) : (MAXDIGESTS + dane_id));
-}
-
-/* dane_init - initialize DANE parameters */
-
-static void dane_init(void)
-{
- int digest_pref = 0;
- char *cp;
- char *save;
- char *tok;
- static char fullmtype[] = "=0";
- dane_digest *d;
-
- /*
- * Add the full matching type at highest preference and then the users
- * configured list.
- *
- * The most preferred digest will be used for hashing full values for
- * comparison.
- */
- if (add_digest(fullmtype, 0)) {
- save = cp = mystrdup(var_tls_dane_digests);
- while ((tok = mystrtok(&cp, CHARS_COMMA_SP)) != 0) {
- if ((d = add_digest(tok, ++digest_pref)) == 0) {
- signalg = 0;
- break;
- }
- if (digest_pref == 1) {
- signalg = d->mdalg;
- }
- }
- myfree(save);
- }
- /* Don't report old news */
- ERR_clear_error();
-
- /*
- * DANE TLSA support requires working DANE digests.
- */
- if ((serverAuth = OBJ_nid2obj(NID_server_auth)) == 0) {
- msg_warn("cannot designate intermediate TA certificates, "
- "no DANE support");
- tls_print_errors();
- dane_tlsa_support = 0;
- } else if (signalg == 0) {
- msg_warn("digest algorithm initializaton failed, no DANE support");
- tls_print_errors();
- dane_tlsa_support = 0;
- }
- dane_initialized = 1;
+ log_mask = tls_log_mask(log_param, log_level);
}
/* tls_dane_avail - check for availability of dane required digests */
int tls_dane_avail(void)
{
- if (!dane_initialized)
- dane_init();
return (dane_tlsa_support);
}
-/* 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(void)
{
TLS_DANE *dane = (TLS_DANE *) mymalloc(sizeof(*dane));
- dane->ta = 0;
- dane->ee = 0;
- dane->certs = 0;
- dane->pkeys = 0;
+ dane->tlsa = 0;
dane->base_domain = 0;
dane->flags = 0;
dane->expires = 0;
return (dane);
}
-static void ta_cert_insert(TLS_DANE *d, X509 *x)
-{
- TLS_CERTS *new = (TLS_CERTS *) mymalloc(sizeof(*new));
-
- X509_up_ref(x);
- 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((void *) head);
- }
-}
-
-static void ta_pkey_insert(TLS_DANE *d, EVP_PKEY *k)
-{
- TLS_PKEYS *new = (TLS_PKEYS *) mymalloc(sizeof(*new));
-
- EVP_PKEY_up_ref(k);
- new->pkey = k;
- new->next = d->pkeys;
- d->pkeys = new;
-}
+/* tls_tlsa_free - free a TLSA RR linked list */
-static void free_ta_pkeys(TLS_DANE *d)
+void tls_tlsa_free(TLS_TLSA *tlsa)
{
- TLS_PKEYS *head;
- TLS_PKEYS *next;
+ TLS_TLSA *next;
- for (head = d->pkeys; head; head = next) {
- next = head->next;
- EVP_PKEY_free(head->pkey);
- myfree((void *) head);
+ for (; tlsa; tlsa = next) {
+ next = tlsa->next;
+ myfree(tlsa->data);
+ myfree(tlsa);
}
}
-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((void *) 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);
if (dane->base_domain)
myfree(dane->base_domain);
-
+ if (dane->tlsa)
+ tls_tlsa_free(dane->tlsa);
myfree((void *) dane);
}
-/* dane_free - ctable style */
+/* tlsa_prepend - Prepend internal-form TLSA record to the RRset linked list */
-static void dane_free(void *dane, void *unused_context)
+TLS_TLSA *tlsa_prepend(TLS_TLSA *tlsa, uint8_t usage, uint8_t selector,
+ uint8_t mtype, const unsigned char *data,
+ uint16_t data_len)
{
- tls_dane_free((TLS_DANE *) dane);
+ TLS_TLSA *head;
+
+ head = (TLS_TLSA *) mymalloc(sizeof(*head));
+ head->usage = usage;
+ head->selector = selector;
+ head->mtype = mtype;
+ head->length = data_len;
+ head->data = (unsigned char *) mymemdup(data, data_len);
+ head->next = tlsa;
+ return (head);
}
-/* dane_locate - list head address of TLSA sublist for given algorithm */
+#define MAX_HEAD_BYTES 32
+#define MAX_TAIL_BYTES 32
+#define MAX_DUMP_BYTES (MAX_HEAD_BYTES + MAX_TAIL_BYTES)
-static TLS_TLSA **dane_locate(TLS_TLSA **tlsap, const char *mdalg)
-{
- TLS_TLSA *new;
+/* tlsa_info - log import of a particular TLSA record */
- /*
- * 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_add_ee_digests - split and append digests */
-
-void tls_dane_add_ee_digests(TLS_DANE *dane, const char *mdalg,
- const char *digest, const char *delim)
+static void tlsa_info(const char *tag, const char *msg,
+ uint8_t u, uint8_t s, uint8_t m,
+ const unsigned char *data, ssize_t dlen)
{
- TLS_TLSA **tlsap = dane_locate(&dane->ee, mdalg);
- TLS_TLSA *tlsa = *tlsap;
-
- /* Delimited append, may append nothing */
- if (tlsa->pkeys == 0)
- tlsa->pkeys = argv_split(digest, delim);
- else
- argv_split_append(tlsa->pkeys, digest, delim);
-
- /* Remove empty elements from the list */
- if (tlsa->pkeys->argc == 0) {
- argv_free(tlsa->pkeys);
- tlsa->pkeys = 0;
-
- if (tlsa->certs == 0) {
- *tlsap = tlsa->next;
- tlsa_free(tlsa);
- }
- return;
+ static VSTRING *top;
+ static VSTRING *bot;
+
+ if (top == 0)
+ top = vstring_alloc(2 * MAX_HEAD_BYTES);
+ if (bot == 0)
+ bot = vstring_alloc(2 * MAX_TAIL_BYTES);
+
+ if (dlen > MAX_DUMP_BYTES) {
+ hex_encode(top, (char *) data, MAX_HEAD_BYTES);
+ hex_encode(bot, (char *) data + dlen - MAX_TAIL_BYTES, MAX_TAIL_BYTES);
+ } else if (dlen > 0) {
+ hex_encode(top, (char *) data, dlen);
+ } else {
+ vstring_sprintf(top, "...");
}
- /*
- * At the "fingerprint" security level certificate digests and public key
- * digests are interchangeable. Each leaf certificate is matched via
- * either the public key digest or full certificate digest. The DER
- * encoding of a certificate is not a valid public key, and conversely,
- * the DER encoding of a public key is not a valid certificate. An
- * attacker would need a 2nd-preimage that is feasible across types
- * (given cert digest == some pkey digest) and yet presumably difficult
- * within a type (e.g. given cert digest == some other cert digest). No
- * such attacks are known at this time, and it is expected that if any
- * are found they would work within as well as across the cert/pkey data
- * types.
- */
- if (tlsa->certs == 0)
- tlsa->certs = argv_split(digest, delim);
- else
- argv_split_append(tlsa->certs, digest, delim);
+ msg_info("%s: %s: %u %u %u %s%s%s", tag, msg, u, s, m, STR(top),
+ dlen > MAX_DUMP_BYTES ? "..." : "",
+ dlen > MAX_DUMP_BYTES ? STR(bot) : "");
}
-/* dane_add - add a digest entry */
+/* tlsa_carp - carp about a particular TLSA record */
-static void dane_add(TLS_DANE *dane, int certusage, int selector,
- const char *mdalg, char *digest)
+static void tlsa_carp(const char *s1, const char *s2, const char *s3,
+ const char *s4, uint8_t u, uint8_t s, uint8_t m,
+ const unsigned char *data, ssize_t dlen)
{
- TLS_TLSA **tlsap;
- TLS_TLSA *tlsa;
- ARGV **argvp;
-
- switch (certusage) {
- case DNS_TLSA_USAGE_TRUST_ANCHOR_ASSERTION:
- certusage = TLS_DANE_TA;
- break;
- case DNS_TLSA_USAGE_DOMAIN_ISSUED_CERTIFICATE:
- certusage = TLS_DANE_EE; /* Collapse 1/3 -> 3 */
- break;
- default:
- msg_panic("Unsupported DANE certificate usage: %d", certusage);
- }
-
- switch (selector) {
- case DNS_TLSA_SELECTOR_FULL_CERTIFICATE:
- selector = TLS_DANE_CERT;
- break;
- case DNS_TLSA_SELECTOR_SUBJECTPUBLICKEYINFO:
- selector = TLS_DANE_PKEY;
- break;
- default:
- msg_panic("Unsupported DANE selector: %d", selector);
+ static VSTRING *top;
+ static VSTRING *bot;
+
+ if (top == 0)
+ top = vstring_alloc(2 * MAX_HEAD_BYTES);
+ if (bot == 0)
+ bot = vstring_alloc(2 * MAX_TAIL_BYTES);
+
+ if (dlen > MAX_DUMP_BYTES) {
+ hex_encode(top, (char *) data, MAX_HEAD_BYTES);
+ hex_encode(bot, (char *) data + dlen - MAX_TAIL_BYTES, MAX_TAIL_BYTES);
+ } else if (dlen > 0) {
+ hex_encode(top, (char *) data, dlen);
+ } else {
+ vstring_sprintf(top, "...");
}
- tlsap = (certusage == TLS_DANE_EE) ? &dane->ee : &dane->ta;
- tlsa = *(tlsap = dane_locate(tlsap, mdalg));
- argvp = (selector == TLS_DANE_PKEY) ? &tlsa->pkeys : &tlsa->certs;
-
- if (*argvp == 0)
- *argvp = argv_alloc(1);
- argv_add(*argvp, digest, ARGV_END);
+ msg_warn("%s%s%s%s: %u %u %u %s%s%s", s1, s2, s3, s4, u, s, m, STR(top),
+ dlen > MAX_DUMP_BYTES ? "..." : "",
+ dlen > MAX_DUMP_BYTES ? STR(bot) : "");
}
-#define FILTER_CTX_AGILITY_OK (1<<0)
-#define FILTER_CTX_APPLY_AGILITY (1<<1)
-#define FILTER_CTX_PARSE_DATA (1<<2)
-
-#define FILTER_RR_DROP 0
-#define FILTER_RR_KEEP 1
-
-typedef struct filter_ctx {
- TLS_DANE *dane; /* Parsed result */
- int count; /* Digest mtype count */
- int target; /* Digest mtype target count */
- int flags; /* Action/result bitmask */
-} filter_ctx;
-
-typedef int (*tlsa_filter) (DNS_RR *, filter_ctx *);
-
-/* tlsa_apply - apply filter to each rr in turn */
+/* tls_dane_flush - flush the cache */
-static DNS_RR *tlsa_apply(DNS_RR *rr, tlsa_filter filter, filter_ctx *ctx)
+void tls_dane_flush(void)
{
- DNS_RR *head = 0; /* First retained RR */
- DNS_RR *tail = 0; /* Last retained RR */
- DNS_RR *next;
-
- /*
- * XXX Code that modifies or destroys DNS_RR lists or entries belongs in
- * the DNS library, not here.
- */
- for ( /* nop */ ; rr; rr = next) {
- next = rr->next;
-
- if (filter(rr, ctx) == FILTER_RR_KEEP) {
- tail = rr;
- if (!head)
- head = rr;
- } else {
- if (tail)
- tail->next = rr->next;
- rr->next = 0;
- dns_rr_free(rr);
- }
- }
- return (head);
+ if (dane_cache)
+ ctable_free(dane_cache);
+ dane_cache = 0;
}
-/* usmdelta - packed usage/selector/mtype bits changing in next record */
+/* dane_free - ctable style */
-static unsigned int usmdelta(uint8_t u, uint8_t s, uint8_t m, DNS_RR *next)
+static void dane_free(void *dane, void *unused_context)
{
- uint8_t *ip = (next && next->data_len >= 3) ? (uint8_t *) next->data : 0;
- uint8_t nu = ip ? *ip++ : ~u;
- uint8_t ns = ip ? *ip++ : ~s;
- uint8_t nm = ip ? *ip++ : ~m;
-
- return (((u ^ nu) << 16) | ((s ^ ns) << 8) | (m ^ nm));
+ tls_dane_free((TLS_DANE *) dane);
}
-/* tlsa_rr_cmp - qsort TLSA rrs in case shuffled by name server */
+/* tls_dane_add_fpt_digests - map fingerprint list to DANE TLSA RRset */
-static int tlsa_rr_cmp(DNS_RR *a, DNS_RR *b)
+void tls_dane_add_fpt_digests(TLS_DANE *dane, const char *digest,
+ const char *delim, int smtp_mode)
{
- int cmp;
+ ARGV *values = argv_split(digest, delim);
+ ssize_t i;
+
+ if (smtp_mode) {
+ if (warn_compat_break_smtp_tls_fpt_dgst)
+ msg_info("using backwards-compatible default setting "
+ VAR_SMTP_TLS_FPT_DGST "=md5 to compute certificate "
+ "fingerprints");
+ } else {
+ if (warn_compat_break_lmtp_tls_fpt_dgst)
+ msg_info("using backwards-compatible default setting "
+ VAR_LMTP_TLS_FPT_DGST "=md5 to compute certificate "
+ "fingerprints");
+ }
- /*
- * Sort in ascending order, by usage, selector, matching type preference
- * and payload. The usage, selector and matching type are the first
- * three unsigned octets of the RR data.
- */
- if (a->data_len > 2 && b->data_len > 2) {
- uint8_t *ai = (uint8_t *) a->data;
- uint8_t *bi = (uint8_t *) b->data;
+ for (i = 0; i < values->argc; ++i) {
+ const char *cp = values->argv[i];
+ size_t ilen = strlen(cp);
+ VSTRING *raw;
-#define signedcmp(x, y) (((int)(x)) - ((int)(y)))
+ /*
+ * Decode optionally colon-separated hex-encoded string, the input
+ * value requires at most 3 bytes per byte of payload, which must not
+ * exceed the size of the widest supported hash function.
+ */
+ if (ilen > 3 * EVP_MAX_MD_SIZE) {
+ msg_warn("malformed fingerprint value: %.100s...",
+ values->argv[i]);
+ continue;
+ }
+ raw = vstring_alloc(ilen / 2);
+ if (hex_decode_opt(raw, cp, ilen, HEX_DECODE_FLAG_ALLOW_COLON) == 0) {
+ myfree(raw);
+ msg_warn("malformed fingerprint value: %.384s", values->argv[i]);
+ continue;
+ }
- if ((cmp = signedcmp(ai[0], bi[0])) != 0
- || (cmp = signedcmp(ai[1], bi[1])) != 0
- || (cmp = digest_pref_byid(ai[2]) -
- digest_pref_byid(bi[2])) != 0)
- return (cmp);
+ /*
+ * At the "fingerprint" security level certificate digests and public
+ * key digests are interchangeable. Each leaf certificate is matched
+ * via either the public key digest or full certificate digest. The
+ * DER encoding of a certificate is not a valid public key, and
+ * conversely, the DER encoding of a public key is not a valid
+ * certificate. An attacker would need a 2nd-preimage that is
+ * feasible across types (given cert digest == some pkey digest) and
+ * yet presumably difficult within a type (e.g. given cert digest ==
+ * some other cert digest). No such attacks are known at this time,
+ * and it is expected that if any are found they would work within as
+ * well as across the cert/pkey data types.
+ *
+ * The private-use matching type "255" is mapped to the configured
+ * fingerprint digest, which may (harmlessly) coincide with one of
+ * the standard DANE digest algorithms. The private code point is
+ * however unconditionally enabled.
+ */
+ if (log_mask & (TLS_LOG_VERBOSE | TLS_LOG_DANE))
+ tlsa_info("fingerprint", "digest as private-use TLSA record",
+ 3, 0, 255, (unsigned char *) STR(raw), VSTRING_LEN(raw));
+ dane->tlsa = tlsa_prepend(dane->tlsa, 3, 0, 255,
+ (unsigned char *) STR(raw), VSTRING_LEN(raw));
+ dane->tlsa = tlsa_prepend(dane->tlsa, 3, 1, 255,
+ (unsigned char *) STR(raw), VSTRING_LEN(raw));
+ vstring_free(raw);
}
- if ((cmp = a->data_len - b->data_len) != 0)
- return (cmp);
- return (memcmp(a->data, b->data, a->data_len));
+ argv_free(values);
}
/* parse_tlsa_rr - parse a validated TLSA RRset */
-static int parse_tlsa_rr(DNS_RR *rr, filter_ctx *ctx)
+static int parse_tlsa_rr(TLS_DANE *dane, DNS_RR *rr)
{
- uint8_t *ip;
+ const uint8_t *ip;
uint8_t usage;
uint8_t selector;
uint8_t mtype;
ssize_t dlen;
- const unsigned char *data;
- const unsigned char *p;
+ unsigned const char *data;
int iscname = strcasecmp(rr->rname, rr->qname);
- const char *q = (iscname) ? (rr)->qname : "";
- const char *a = (iscname) ? " -> " : "";
+ const char *q = iscname ? rr->qname : "";
+ const char *a = iscname ? " -> " : "";
const char *r = rr->rname;
- unsigned int change;
if (rr->type != T_TLSA)
- msg_panic("unexpected non-TLSA RR type %u for %s%s%s", rr->type,
- q, a, r);
+ msg_panic("%s%s%s: unexpected non-TLSA RR type: %u",
+ q, a, r, rr->type);
/* Drop truncated records */
if ((dlen = rr->data_len - 3) < 0) {
- msg_warn("truncated length %u RR: %s%s%s IN TLSA ...",
- (unsigned) rr->data_len, q, a, r);
- ctx->flags &= ~FILTER_CTX_AGILITY_OK;
- return (FILTER_RR_DROP);
+ msg_warn("%s%s%s: truncated TLSA RR length == %u",
+ q, a, r, (unsigned) rr->data_len);
+ return (0);
}
- ip = (uint8_t *) rr->data;
+ ip = (const uint8_t *) rr->data;
usage = *ip++;
selector = *ip++;
mtype = *ip++;
- change = usmdelta(usage, selector, mtype, rr->next);
- p = data = (const unsigned char *) ip;
+ data = (const unsigned char *) ip;
- /*
- * Handle digest agility for non-zero matching types.
- */
- if (mtype) {
- if (ctx->count && (ctx->flags & FILTER_CTX_APPLY_AGILITY)) {
- if (change & 0xffff00) /* New usage/selector, */
- ctx->count = 0; /* disable drop */
- return (FILTER_RR_DROP);
- }
- }
/*-
* Drop unsupported usages.
* Note: NO SUPPORT for usages 0/1 which do not apply to SMTP.
case DNS_TLSA_USAGE_DOMAIN_ISSUED_CERTIFICATE:
break;
default:
- msg_warn("unsupported certificate usage %u in RR: "
- "%s%s%s IN TLSA %u ...", usage,
- q, a, r, usage);
- return (FILTER_RR_DROP);
+ tlsa_carp(q, a, r, "unsupported TLSA certificate usage",
+ usage, selector, mtype, data, dlen);
+ return (0);
}
/*
- * Drop unsupported selectors
+ * Drop private-use matching type, reserved for fingerprint matching.
*/
- switch (selector) {
- case DNS_TLSA_SELECTOR_FULL_CERTIFICATE:
- case DNS_TLSA_SELECTOR_SUBJECTPUBLICKEYINFO:
- break;
- default:
- msg_warn("unsupported selector %u in RR: "
- "%s%s%s IN TLSA %u %u ...", selector,
- q, a, r, usage, selector);
- return (FILTER_RR_DROP);
- }
-
- if (mtype) {
- dane_digest *d = digest_byid(mtype);
-
- if (d == 0) {
- msg_warn("unsupported matching type %u in RR: "
- "%s%s%s IN TLSA %u %u %u ...", mtype,
- q, a, r, usage, selector, mtype);
- return (FILTER_RR_DROP);
- }
- if (dlen != d->len) {
- msg_warn("malformed %s digest, length %lu, in RR: "
- "%s%s%s IN TLSA %u %u %u ...",
- d->mdalg, (unsigned long) dlen,
- q, a, r, usage, selector, mtype);
- ctx->flags &= ~FILTER_CTX_AGILITY_OK;
- return (FILTER_RR_DROP);
- }
- /* New digest mtype next? Prepare to drop following RRs */
- if (change && (change & 0xffff00) == 0
- && (ctx->flags & FILTER_CTX_APPLY_AGILITY))
- ++ctx->count;
-
- if (ctx->flags & FILTER_CTX_PARSE_DATA) {
- char *digest = tls_digest_encode(data, dlen);
-
- dane_add(ctx->dane, usage, selector, d->mdalg, digest);
- if (msg_verbose || dane_verbose)
- msg_info("using DANE RR: %s%s%s IN TLSA %u %u %u %s",
- q, a, r, usage, selector, mtype, digest);
- myfree(digest);
- }
- } else {
- X509 *x = 0; /* OpenSSL re-uses *x if x!=0 */
- EVP_PKEY *k = 0; /* OpenSSL re-uses *k if k!=0 */
-
- /* Validate the cert or public key via d2i_mumble() */
- switch (selector) {
- case DNS_TLSA_SELECTOR_FULL_CERTIFICATE:
- if (!d2i_X509(&x, &p, dlen) || dlen != p - data) {
- msg_warn("malformed %s in RR: "
- "%s%s%s IN TLSA %u %u %u ...", "certificate",
- q, a, r, usage, selector, mtype);
- if (x)
- X509_free(x);
- return (FILTER_RR_DROP);
- }
- /* Also unusable if public key is malformed or unsupported */
- k = X509_get_pubkey(x);
- EVP_PKEY_free(k);
- if (k == 0) {
- msg_warn("malformed %s in RR: %s%s%s IN TLSA %u %u %u ...",
- "or unsupported certificate public key",
- q, a, r, usage, selector, mtype);
- X509_free(x);
- return (FILTER_RR_DROP);
- }
-
- /*
- * 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.
- */
- if (usage == DNS_TLSA_USAGE_TRUST_ANCHOR_ASSERTION
- && (ctx->flags & FILTER_CTX_PARSE_DATA))
- ta_cert_insert(ctx->dane, x);
- X509_free(x);
- break;
-
- case DNS_TLSA_SELECTOR_SUBJECTPUBLICKEYINFO:
- if (!d2i_PUBKEY(&k, &p, dlen) || dlen != p - data) {
- msg_warn("malformed %s in RR: %s%s%s IN TLSA %u %u %u ...",
- "public key", q, a, r, usage, selector, mtype);
- if (k)
- EVP_PKEY_free(k);
- return (FILTER_RR_DROP);
- }
-
- /*
- * When a full trust-anchor public key is published via DNS, we
- * may need to use it to validate the server trust chain. Store
- * it away for later use.
- */
- if (usage == DNS_TLSA_USAGE_TRUST_ANCHOR_ASSERTION
- && (ctx->flags & FILTER_CTX_PARSE_DATA))
- ta_pkey_insert(ctx->dane, k);
- EVP_PKEY_free(k);
- break;
- }
-
- /*
- * The cert or key was valid, just digest the raw object, and encode
- * the digest value.
- */
- if (ctx->flags & FILTER_CTX_PARSE_DATA) {
- char *digest = tls_data_fprint((char *) data, dlen, signalg);
-
- dane_add(ctx->dane, usage, selector, signalg, digest);
- if (msg_verbose || dane_verbose)
- msg_info("using DANE RR: %s%s%s IN TLSA %u %u %u <%s>; "
- "%s digest %s", q, a, r, usage, selector, mtype,
- (selector == DNS_TLSA_SELECTOR_FULL_CERTIFICATE) ?
- "certificate" : "public key", signalg, digest);
- myfree(digest);
- }
+ if (mtype == 255) {
+ tlsa_carp(q, a, r, "reserved private-use matching type",
+ usage, selector, mtype, data, dlen);
+ return (0);
}
- return (FILTER_RR_KEEP);
-}
-
-/* process_rrs - filter and parse the TLSA RRset */
-
-static DNS_RR *process_rrs(TLS_DANE *dane, DNS_RR *rrset)
-{
- filter_ctx ctx;
-
- ctx.dane = dane;
- ctx.count = ctx.target = 0;
- ctx.flags = FILTER_CTX_APPLY_AGILITY | FILTER_CTX_PARSE_DATA;
-
- rrset = tlsa_apply(rrset, parse_tlsa_rr, &ctx);
-
- if (dane->ta == 0 && dane->ee == 0)
- dane->flags |= TLS_DANE_FLAG_EMPTY;
-
- return (rrset);
+ if (log_mask & (TLS_LOG_VERBOSE | TLS_LOG_DANE))
+ tlsa_info("DNSSEC-signed TLSA record", r,
+ usage, selector, mtype, data, dlen);
+ dane->tlsa = tlsa_prepend(dane->tlsa, usage, selector, mtype, data, dlen);
+ return (1);
}
/* 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;
+ DNS_RR *rr;
+ TLS_DANE *dane = tls_dane_alloc();
+ int ret;
if (why == 0)
why = vstring_alloc(10);
- dane = tls_dane_alloc();
ret = dns_lookup(tlsa_fqdn, T_TLSA, RES_USE_DNSSEC, &rrs, 0, why);
switch (ret) {
dane->expires = 1 + event_time() + rrs->ttl;
if (rrs->dnssec_valid) {
+ int n = 0;
- /*
- * Sort for deterministic digest in session cache lookup key. In
- * addition we must arrange for more preferred matching types
- * (full value or digest) to precede less preferred ones for the
- * same usage and selector.
- */
- rrs = dns_rr_sort(rrs, tlsa_rr_cmp);
- rrs = process_rrs(dane, rrs);
+ for (rr = rrs; rr != 0; rr = rr->next)
+ n += parse_tlsa_rr(dane, rr);
+ if (n == 0)
+ dane->flags |= TLS_DANE_FLAG_EMPTY;
} else
dane->flags |= TLS_DANE_FLAG_NORRS;
long len;
int tacount;
char *errtype = 0; /* if error: cert or pkey? */
- const char *mdalg;
/* nop */
if (tafile == 0 || *tafile == 0)
return (1);
- if (!dane_initialized)
- dane_init();
-
- /* Per-destination TA support is available even when DANE is not */
- mdalg = signalg ? signalg : "sha1";
-
/*
* 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
/* Don't report old news */
ERR_clear_error();
+ /*
+ * OpenSSL implements DANE strictly, with DANE-TA(2) only matching issuer
+ * certificates, and never the leaf cert. We also allow the
+ * trust-anchors to directly match the leaf certificate or public key.
+ */
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;
+ uint8_t daneta = DNS_TLSA_USAGE_TRUST_ANCHOR_ASSERTION;
+ uint8_t daneee = DNS_TLSA_USAGE_DOMAIN_ISSUED_CERTIFICATE;
+ uint8_t mtype = DNS_TLSA_MATCHING_TYPE_NO_HASH_USED;
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, mdalg);
- dane_add(dane, usage, selector, mdalg, digest);
- myfree(digest);
- ta_cert_insert(dane, cert);
- } else
- errtype = "certificate";
- if (cert)
- X509_free(cert);
+ uint8_t selector = DNS_TLSA_SELECTOR_FULL_CERTIFICATE;
+
+ if (log_mask & (TLS_LOG_VERBOSE | TLS_LOG_DANE))
+ tlsa_info("TA cert as TLSA record", tafile,
+ daneta, selector, mtype, data, len);
+ dane->tlsa =
+ tlsa_prepend(dane->tlsa, daneta, selector, mtype, data, len);
+ dane->tlsa =
+ tlsa_prepend(dane->tlsa, daneee, selector, mtype, data, len);
} 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, mdalg);
- dane_add(dane, usage, selector, mdalg, digest);
- myfree(digest);
- ta_pkey_insert(dane, pkey);
- } else
- errtype = "public key";
- if (pkey)
- EVP_PKEY_free(pkey);
+ uint8_t selector = DNS_TLSA_SELECTOR_SUBJECTPUBLICKEYINFO;
+
+ if (log_mask & (TLS_LOG_VERBOSE | TLS_LOG_DANE))
+ tlsa_info("TA pkey as TLSA record", tafile,
+ daneta, selector, mtype, data, len);
+ dane->tlsa =
+ tlsa_prepend(dane->tlsa, daneta, selector, mtype, data, len);
+ dane->tlsa = tlsa_prepend(dane->tlsa, daneee, selector, mtype, data, len);
}
/*
return (0);
}
-/* tls_dane_match - match cert against given list of TA or EE digests */
-
-int tls_dane_match(TLS_SESS_STATE *TLScontext, int usage,
- X509 *cert, int depth)
+int tls_dane_enable(TLS_SESS_STATE *TLScontext)
{
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 matched;
-
- for (matched = 0; tlsa && !matched; tlsa = tlsa->next) {
- char **dgst;
+ TLS_TLSA *tp;
+ SSL *ssl = TLScontext->con;
+ int usable = 0;
+ int ret;
- /*
- * Note, set_trust() needs to know whether the match was for a pkey
- * digest or a certificate digest. We return MATCHED_PKEY or
- * MATCHED_CERT accordingly.
- */
-#define MATCHED_CERT 1
-#define MATCHED_PKEY 2
-
- 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 = MATCHED_PKEY;
- if (TLScontext->log_mask & (TLS_LOG_VERBOSE | TLS_LOG_CERTMATCH)
- && matched)
- msg_info("%s: depth=%d matched %s public-key %s digest=%s",
- namaddr, depth, ustr, tlsa->mdalg, pkey_dgst);
- myfree(pkey_dgst);
+ for (tp = dane->tlsa; tp != 0; tp = tp->next) {
+ ret = SSL_dane_tlsa_add(ssl, tp->usage, tp->selector,
+ tp->mtype, tp->data, tp->length);
+ if (ret > 0) {
+ ++usable;
+ continue;
}
- if (tlsa->certs != 0 && !matched) {
- char *cert_dgst = tls_cert_fprint(cert, tlsa->mdalg);
-
- for (dgst = tlsa->certs->argv; !matched && *dgst; ++dgst)
- if (strcasecmp(cert_dgst, *dgst) == 0)
- matched = MATCHED_CERT;
- if (TLScontext->log_mask & (TLS_LOG_VERBOSE | TLS_LOG_CERTMATCH)
- && matched)
- msg_info("%s: depth=%d matched %s certificate %s digest %s",
- namaddr, depth, ustr, tlsa->mdalg, cert_dgst);
- myfree(cert_dgst);
+ if (ret == 0) {
+ tlsa_carp(TLScontext->namaddr, ": ", "", "unusable TLSA RR",
+ tp->usage, tp->selector, tp->mtype, tp->data,
+ tp->length);
+ continue;
}
+ /* Internal problem in OpenSSL */
+ tlsa_carp(TLScontext->namaddr, ": ", "", "error loading trust settings",
+ tp->usage, tp->selector, tp->mtype, tp->data, tp->length);
+ tls_print_errors();
+ return (-1);
}
-
- return (matched);
-}
-
-/* add_ext - add simple extension (no config section references) */
-
-static int add_ext(X509 *issuer, X509 *subject, int ext_nid, char *ext_val)
-{
- int ret = 0;
- X509V3_CTX v3ctx;
- X509_EXTENSION *ext;
-
- X509V3_set_ctx(&v3ctx, issuer, subject, 0, 0, 0);
- if ((ext = X509V3_EXT_conf_nid(0, &v3ctx, ext_nid, ext_val)) != 0) {
- ret = X509_add_ext(subject, ext, -1);
- X509_EXTENSION_free(ext);
- }
- return ret;
+ return (usable);
}
-/* set_serial - set serial number to match akid or use subject's plus 1 */
+/* tls_dane_digest_init - configure supported DANE digests */
-static int set_serial(X509 *cert, AUTHORITY_KEYID *akid, X509 *subject)
+void tls_dane_digest_init(SSL_CTX *ctx, const EVP_MD * fpt_alg)
{
- int ret = 0;
- BIGNUM *bn;
-
- if (akid && akid->serial)
- return (X509_set_serialNumber(cert, akid->serial));
-
- /*
- * Add one to subject's serial to avoid collisions between TA serial and
- * serial of signing root.
- */
- if ((bn = ASN1_INTEGER_to_BN(X509_get_serialNumber(subject), 0)) != 0
- && BN_add_word(bn, 1)
- && BN_to_ASN1_INTEGER(bn, X509_get_serialNumber(cert)))
- ret = 1;
-
- if (bn)
- BN_free(bn);
- return (ret);
-}
+ dane_mtype mtypes[256];
+ char *cp;
+ char *save;
+ char *algname;
+ uint8_t m;
+ uint8_t ord = 0;
+ uint8_t maxtype;
-/* add_akid - add authority key identifier */
-
-static int add_akid(X509 *cert, AUTHORITY_KEYID *akid)
-{
- ASN1_OCTET_STRING *id;
- unsigned char c = 0;
- int nid = NID_authority_key_identifier;
- int ret = 0;
+ memset((char *) mtypes, 0, sizeof(mtypes));
/*
- * 0 will never be our subject keyid from a SHA-1 hash, but it could be
- * our subject keyid if forced from child's akid. If so, set our
- * authority keyid to 1. This way we are never self-signed, and thus
- * exempt from any potential (off by default for now in OpenSSL)
- * self-signature checks!
+ * The DANE SHA2-256(1) and SHA2-512(2) algorithms are disabled, unless
+ * explicitly enabled. Other codepoints can be disabled explicitly by
+ * giving them an empty digest name, which also implicitly disables all
+ * smaller codepoints that are not explicitly assigned.
+ *
+ * We reserve the private-use code point (255) for use with fingerprint
+ * matching. It MUST NOT be accepted in DNS replies.
*/
- id = ((akid && akid->keyid) ? akid->keyid : 0);
- if (id && ASN1_STRING_length(id) == 1 && *ASN1_STRING_get0_data(id) == c)
- c = 1;
-
- if ((akid = AUTHORITY_KEYID_new()) != 0
- && (akid->keyid = ASN1_OCTET_STRING_new()) != 0
- && ASN1_OCTET_STRING_set(akid->keyid, (void *) &c, 1)
- && X509_add1_ext_i2d(cert, nid, akid, 0, X509V3_ADD_DEFAULT) > 0)
- ret = 1;
- if (akid)
- AUTHORITY_KEYID_free(akid);
- return (ret);
-}
-
-/* add_skid - add subject key identifier to match child's akid */
-
-static int add_skid(X509 *cert, AUTHORITY_KEYID *akid)
-{
- int nid = NID_subject_key_identifier;
-
- if (!akid || !akid->keyid)
- return (add_ext(0, cert, nid, "hash"));
- else
- return (X509_add1_ext_i2d(cert, nid, akid->keyid, 0,
- X509V3_ADD_DEFAULT) > 0);
-}
+ mtypes[1].alg = NULL;
+ mtypes[2].alg = NULL;
+ mtypes[255].alg = fpt_alg;
+ maxtype = 2;
-/* akid_issuer_name - get akid issuer directory name */
+ save = cp = mystrdup(var_tls_dane_digests);
+ while ((algname = mystrtok(&cp, CHARS_COMMA_SP)) != 0) {
+ char *algcode = split_at(algname, '=');
+ int codepoint = -1;
-static X509_NAME *akid_issuer_name(AUTHORITY_KEYID *akid)
-{
- if (akid && akid->issuer) {
- int i;
- general_name_stack_t *gens = akid->issuer;
-
- for (i = 0; i < sk_GENERAL_NAME_num(gens); ++i) {
- GENERAL_NAME *gn = sk_GENERAL_NAME_value(gens, i);
+ if (algcode && *algcode) {
+ unsigned long l;
+ char *endcp;
- if (gn->type == GEN_DIRNAME)
- return (gn->d.dirn);
+ /*
+ * XXX: safe_strtoul() does not flag empty or white-space only
+ * input. Since we get algcode by splitting white-space/comma
+ * delimited tokens, this is not a problem here.
+ */
+ l = safe_strtoul(algcode, &endcp, 10);
+ if ((l == 0 && (errno == EINVAL || endcp == algcode))
+ || l >= 255 || *endcp) {
+ msg_warn("Invalid matching type number in %s: %s=%s",
+ VAR_TLS_DANE_DIGESTS, algname, algcode);
+ continue;
+ }
+ if (l == 0 || l == 255) {
+ msg_warn("Reserved matching type number in %s: %s=%s",
+ VAR_TLS_DANE_DIGESTS, algname, algcode);
+ continue;
+ }
+ codepoint = l;
}
- }
- return (0);
-}
-
-/* set_issuer - set issuer DN to match akid if specified */
-
-static int set_issuer_name(X509 *cert, AUTHORITY_KEYID *akid, X509_NAME *subj)
-{
- X509_NAME *name = akid_issuer_name(akid);
-
- /*
- * If subject's akid specifies an authority key identifier issuer name,
- * we must use that.
- */
- if (name)
- return (X509_set_issuer_name(cert, name));
- return (X509_set_issuer_name(cert, subj));
-}
-
-/* grow_chain - add certificate to trusted or untrusted chain */
-
-static void grow_chain(TLS_SESS_STATE *TLScontext, int trusted, X509 *cert)
-{
- x509_stack_t **xs = trusted ? &TLScontext->trusted : &TLScontext->untrusted;
-
-#define UNTRUSTED 0
-#define TRUSTED 1
-
- if (!*xs && (*xs = sk_X509_new_null()) == 0)
- msg_fatal("out of memory");
- if (cert) {
- if (trusted && !X509_add1_trust_object(cert, serverAuth))
- msg_fatal("out of memory");
- X509_up_ref(cert);
- if (!sk_X509_push(*xs, cert))
- msg_fatal("out of memory");
- }
-}
-
-/* wrap_key - wrap TA "key" as issuer of "subject" */
-
-static void wrap_key(TLS_SESS_STATE *TLScontext, int depth,
- EVP_PKEY *key, X509 *subject)
-{
- X509 *cert = 0;
- AUTHORITY_KEYID *akid;
- X509_NAME *name = X509_get_issuer_name(subject);
-
- /*
- * The subject name is never a NULL object unless we run out of memory.
- * It may be an empty sequence, but the containing object always exists
- * and its storage is owned by the certificate itself.
- */
- if (name == 0 || (cert = X509_new()) == 0)
- msg_fatal("Out of memory");
-
- /*
- * Record the depth of the intermediate wrapper certificate, logged in
- * the verify callback.
- */
- if (TLScontext->tadepth < 0) {
- TLScontext->tadepth = depth + 1;
- if (TLScontext->log_mask & (TLS_LOG_VERBOSE | TLS_LOG_CERTMATCH))
- msg_info("%s: depth=%d chain is trust-anchor signed",
- TLScontext->namaddr, depth);
- }
- akid = X509_get_ext_d2i(subject, NID_authority_key_identifier, 0, 0);
-
- ERR_clear_error();
-
- /* CA cert valid for +/- 30 days. */
- if (!X509_set_version(cert, 2)
- || !set_serial(cert, akid, subject)
- || !set_issuer_name(cert, akid, name)
- || !X509_gmtime_adj(X509_getm_notBefore(cert), -30 * 86400L)
- || !X509_gmtime_adj(X509_getm_notAfter(cert), 30 * 86400L)
- || !X509_set_subject_name(cert, name)
- || !X509_set_pubkey(cert, key)
- || !add_ext(0, cert, NID_basic_constraints, "CA:TRUE")
- || (key && !add_akid(cert, akid))
- || !add_skid(cert, akid)) {
- tls_print_errors();
- msg_fatal("error generating DANE wrapper certificate");
- }
- if (akid)
- AUTHORITY_KEYID_free(akid);
- grow_chain(TLScontext, TRUSTED, cert);
- if (cert)
- X509_free(cert);
-}
-
-/* wrap_cert - wrap "tacert" as trust-anchor. */
-
-static void wrap_cert(TLS_SESS_STATE *TLScontext, X509 *tacert, int depth)
-{
- if (TLScontext->tadepth < 0)
- TLScontext->tadepth = depth + 1;
-
- if (TLScontext->log_mask & (TLS_LOG_VERBOSE | TLS_LOG_CERTMATCH))
- msg_info("%s: depth=%d trust-anchor certificate",
- TLScontext->namaddr, depth);
-
- grow_chain(TLScontext, TRUSTED, tacert);
- return;
-}
-
-/* ta_signed - is certificate signed by a TLSA cert or pkey */
-
-static int ta_signed(TLS_SESS_STATE *TLScontext, X509 *cert, int depth)
-{
- const TLS_DANE *dane = TLScontext->dane;
- EVP_PKEY *pk;
- TLS_PKEYS *k;
- TLS_CERTS *x;
- int done = 0;
-
- /*
- * 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; !done && x; x = x->next) {
- if (X509_check_issued(x->cert, cert) == X509_V_OK) {
- if ((pk = X509_get_pubkey(x->cert)) == 0)
+ /* Disable any codepoint gaps */
+ if (codepoint > maxtype) {
+ while (++maxtype < codepoint)
+ mtypes[codepoint].alg = NULL;
+ maxtype = codepoint;
+ }
+ /* Handle explicitly disabled codepoints */
+ if (*algname == 0) {
+ /* Skip empty specifiers */
+ if (codepoint < 0)
continue;
- /* Check signature, since some other TA may work if not this. */
- if ((done = (X509_verify(cert, pk) > 0)) != 0)
- wrap_cert(TLScontext, x->cert, depth);
- EVP_PKEY_free(pk);
+ mtypes[codepoint].alg = NULL;
+ continue;
}
- }
-
- /*
- * 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.
- *
- * We'd like to optimize out keys that should not match when the cert's
- * authority key id does not match the key id of this key computed via
- * the RFC keyid algorithm (SHA-1 digest of public key bit-string sans
- * ASN1 tag and length thus also excluding the unused bits field that is
- * logically part of the length). However, some CAs have a non-standard
- * authority keyid, so we lose. Too bad.
- *
- * This may push errors onto the stack when the certificate signature is not
- * of the right type or length, throw these away.
- */
- for (k = dane->pkeys; !done && k; k = k->next)
- if ((done = (X509_verify(cert, k->pkey) > 0)) != 0)
- wrap_key(TLScontext, depth, k->pkey, cert);
- else
- ERR_clear_error();
-
- return (done);
-}
-
-/* set_trust - configure for DANE validation */
-
-static void set_trust(TLS_SESS_STATE *TLScontext, X509_STORE_CTX *ctx)
-{
- int n;
- int i;
- int match;
- int depth = 0;
- EVP_PKEY *takey;
- X509 *ca;
- X509 *cert = X509_STORE_CTX_get0_cert(ctx);
- x509_stack_t *in = X509_STORE_CTX_get0_untrusted(ctx);
-
- /* shallow copy */
- if ((in = sk_X509_dup(in)) == 0)
- msg_fatal("out of memory");
-
- /*
- * At each iteration we consume the issuer of the current cert. This
- * reduces the length of the "in" chain by one. If no issuer is found,
- * we are done. We also stop when a certificate matches a TA in the
- * peer's TLSA RRset.
- *
- * Caller ensures that the initial certificate is not self-signed.
- */
- for (n = sk_X509_num(in); n > 0; --n, ++depth) {
- for (i = 0; i < n; ++i)
- if (X509_check_issued(sk_X509_value(in, i), cert) == X509_V_OK)
- break;
-
- /*
- * Final untrusted element with no issuer in the peer's chain, it may
- * however be signed by a pkey or cert obtained via a TLSA RR.
- */
- if (i == n)
+ switch (codepoint) {
+ case -1:
+ if (strcasecmp(algname, LN_sha256) == 0)
+ codepoint = 1; /* SHA2-256(1) */
+ else if (strcasecmp(algname, LN_sha512) == 0)
+ codepoint = 2; /* SHA2-512(2) */
+ else {
+ msg_warn("%s: digest algorithm %s needs an explicit number",
+ VAR_TLS_DANE_DIGESTS, algname);
+ continue;
+ }
break;
-
- /* Peer's chain contains an issuer ca. */
- ca = sk_X509_delete(in, i);
-
- /* Is it a trust anchor? */
- match = tls_dane_match(TLScontext, TLS_DANE_TA, ca, depth + 1);
- if (match) {
- switch (match) {
- case MATCHED_CERT:
- wrap_cert(TLScontext, ca, depth);
- break;
- case MATCHED_PKEY:
- if ((takey = X509_get_pubkey(ca)) == 0)
- msg_panic("trust-anchor certificate has null pkey");
- wrap_key(TLScontext, depth, takey, cert);
- EVP_PKEY_free(takey);
- break;
- default:
- msg_panic("unexpected tls_dane_match result: %d", match);
+ case 1:
+ if (strcasecmp(algname, LN_sha256) != 0) {
+ msg_warn("%s: matching type 1 can only be %s",
+ VAR_TLS_DANE_DIGESTS, LN_sha256);
+ continue;
+ }
+ algname = LN_sha256;
+ break;
+ case 2:
+ if (strcasecmp(algname, LN_sha512) != 0) {
+ msg_warn("%s: matching type 2 can only be %s",
+ VAR_TLS_DANE_DIGESTS, LN_sha512);
+ continue;
}
- cert = 0;
+ algname = LN_sha512;
+ break;
+ default:
break;
}
- /* Add untrusted ca. */
- grow_chain(TLScontext, UNTRUSTED, ca);
- /* Final untrusted self-signed element? */
- if (X509_check_issued(ca, ca) == X509_V_OK) {
- cert = 0;
- break;
+ if (mtypes[codepoint].ord != 0) {
+ msg_warn("%s: matching type %d specified more than once",
+ VAR_TLS_DANE_DIGESTS, codepoint);
+ continue;
+ }
+ mtypes[codepoint].ord = ++ord;
+
+ if ((mtypes[codepoint].alg = EVP_get_digestbyname(algname)) == 0) {
+ msg_warn("%s: digest algorithm \"%s\"(%d) unknown",
+ VAR_TLS_DANE_DIGESTS, algname, codepoint);
+ continue;
}
- /* Restart with issuer as subject */
- cert = ca;
}
+ myfree(save);
- /*
- * When the loop exits, if "cert" is set, it is not self-signed and has
- * no issuer in the chain, we check for a possible signature via a DNS
- * obtained TA cert or public key. Otherwise, we found no TAs and no
- * issuer, so set an empty list of TAs.
- */
- if (!cert || !ta_signed(TLScontext, cert, depth)) {
- /* Create empty trust list if null, else NOP */
- grow_chain(TLScontext, TRUSTED, 0);
+ for (m = 1; m != 0; m = m != maxtype ? m + 1 : 255) {
+
+ /*
+ * In OpenSSL higher order ordinals are preferred, but we list the
+ * most preferred algorithms first, so the last ordinal becomes 1,
+ * next-to-last, 2, ...
+ *
+ * The ordinals of non-disabled algorithms are always positive, and the
+ * computed value cannot overflow 254 (the largest possible value of
+ * 'ord' after loading each valid codepoint at most once).
+ */
+ if (SSL_CTX_dane_mtype_set(ctx, mtypes[m].alg, m,
+ ord - mtypes[m].ord + 1) <= 0) {
+ msg_warn("%s: error configuring matching type %d",
+ VAR_TLS_DANE_DIGESTS, m);
+ tls_print_errors();
+ }
}
- /* shallow free */
- if (in)
- sk_X509_free(in);
}
-/* dane_cb - wrap chain verification for DANE */
+/* tls_dane_log - log DANE-based verification success */
-static int dane_cb(X509_STORE_CTX *ctx, void *app_ctx)
+void tls_dane_log(TLS_SESS_STATE *TLScontext)
{
- const char *myname = "dane_cb";
- TLS_SESS_STATE *TLScontext = (TLS_SESS_STATE *) app_ctx;
- X509 *cert = X509_STORE_CTX_get0_cert(ctx);
+ static VSTRING *top;
+ static VSTRING *bot;
+ EVP_PKEY *mspki = 0;
+ int depth = SSL_get0_dane_authority(TLScontext->con, NULL, &mspki);
+ uint8_t u, s, m;
+ unsigned const char *data;
+ size_t dlen;
+
+ if (depth < 0)
+ return; /* No DANE auth */
+
+ switch (TLScontext->level) {
+ case TLS_LEV_SECURE:
+ case TLS_LEV_VERIFY:
+ msg_info("%s: Matched trust anchor at depth %d",
+ TLScontext->namaddr, depth);
+ return;
+ }
- /*
- * Degenerate case: depth 0 self-signed cert.
- *
- * XXX: Should we suppress name checks, ... when the leaf certificate is a
- * TA. After all they could sign any name they want. However, this
- * requires a bit of additional code. For now we allow depth 0 TAs, but
- * then the peer name has to match.
- */
- if (X509_check_issued(cert, cert) == X509_V_OK) {
+ if (top == 0)
+ top = vstring_alloc(2 * MAX_HEAD_BYTES);
+ if (bot == 0)
+ bot = vstring_alloc(2 * MAX_TAIL_BYTES);
- /*
- * Empty untrusted chain, could be NULL, but then ABI check less
- * reliable, we may zero some other field, ...
- */
- grow_chain(TLScontext, UNTRUSTED, 0);
- if (tls_dane_match(TLScontext, TLS_DANE_TA, cert, 0)) {
- TLScontext->tadepth = 0;
- grow_chain(TLScontext, TRUSTED, cert);
- } else
- grow_chain(TLScontext, TRUSTED, 0);
+ (void) SSL_get0_dane_tlsa(TLScontext->con, &u, &s, &m, &data, &dlen);
+ if (dlen > MAX_DUMP_BYTES) {
+ hex_encode(top, (char *) data, MAX_HEAD_BYTES);
+ hex_encode(bot, (char *) data + dlen - MAX_TAIL_BYTES, MAX_TAIL_BYTES);
} else {
- set_trust(TLScontext, ctx);
+ hex_encode(top, (char *) data, dlen);
}
- /*
- * Check that setting the untrusted chain updates the expected structure
- * member at the expected offset.
- */
- X509_STORE_CTX_set0_trusted_stack(ctx, TLScontext->trusted);
- X509_STORE_CTX_set0_untrusted(ctx, TLScontext->untrusted);
- if (X509_STORE_CTX_get0_untrusted(ctx) != TLScontext->untrusted)
- msg_panic("%s: OpenSSL ABI change", myname);
-
- return X509_verify_cert(ctx);
-}
-
-/* tls_dane_set_callback - set or clear verification wrapper callback */
+ switch (TLScontext->level) {
+ case TLS_LEV_FPRINT:
+ msg_info("%s: Matched fingerprint: %s%s%s", TLScontext->namaddr,
+ STR(top), dlen > MAX_DUMP_BYTES ? "..." : "",
+ dlen > MAX_DUMP_BYTES ? STR(bot) : "");
+ return;
-void tls_dane_set_callback(SSL_CTX *ctx, TLS_SESS_STATE *TLScontext)
-{
- if (TLS_DANE_HASTA(TLScontext->dane))
- SSL_CTX_set_cert_verify_callback(ctx, dane_cb, (void *) TLScontext);
- else
- SSL_CTX_set_cert_verify_callback(ctx, 0, 0);
+ default:
+ msg_info("%s: Matched DANE %s at depth %d: %u %u %u %s%s%s",
+ TLScontext->namaddr, mspki ?
+ "TA public key verified certificate" : depth ?
+ "TA certificate" : "EE certificate", depth, u, s, m,
+ STR(top), dlen > MAX_DUMP_BYTES ? "..." : "",
+ dlen > MAX_DUMP_BYTES ? STR(bot) : "");
+ return;
+ }
}
#ifdef TEST
}
X509_STORE_CTX_set_ex_data(store_ctx, store_ctx_idx, ssl);
+ /* We're *verifying* a server chain */
X509_STORE_CTX_set_default(store_ctx, "ssl_server");
X509_VERIFY_PARAM_set1(X509_STORE_CTX_get0_param(store_ctx),
SSL_get0_param(ssl));
+ X509_STORE_CTX_set0_dane(store_ctx, SSL_get0_dane(ssl));
- if (SSL_get_verify_callback(ssl))
- X509_STORE_CTX_set_verify_cb(store_ctx, SSL_get_verify_callback(ssl));
-
- ret = dane_cb(store_ctx, tctx);
+ ret = X509_verify_cert(store_ctx);
SSL_set_verify_result(ssl, X509_STORE_CTX_get_error(store_ctx));
X509_STORE_CTX_free(store_ctx);
return (ret);
}
-static void add_tlsa(TLS_DANE *dane, char *argv[])
+static void load_tlsa_args(SSL *ssl, char *argv[])
{
- char *digest;
+ const EVP_MD *md = 0;
X509 *cert = 0;
BIO *bp;
unsigned char *buf;
int len;
uint8_t u = atoi(argv[1]);
uint8_t s = atoi(argv[2]);
- const char *mdname = argv[3];
+ uint8_t m = atoi(argv[3]);
EVP_PKEY *pkey;
/* Unsupported usages are fatal */
msg_fatal("unsupported selector %u", s);
}
- /* Unsupported digests are fatal */
- if (*mdname && !tls_validate_digest(mdname))
- msg_fatal("unsupported digest algorithm: %s", mdname);
+ /* Unsupported selectors are fatal */
+ switch (m) {
+ case DNS_TLSA_MATCHING_TYPE_NO_HASH_USED:
+ case DNS_TLSA_MATCHING_TYPE_SHA256:
+ case DNS_TLSA_MATCHING_TYPE_SHA512:
+ break;
+ default:
+ msg_fatal("unsupported matching type %u", m);
+ }
if ((bp = BIO_new_file(argv[4], "r")) == NULL)
msg_fatal("error opening %s: %m", argv[4]);
switch (s) {
case DNS_TLSA_SELECTOR_FULL_CERTIFICATE:
len = i2d_X509(cert, NULL);
+ if (len > 0xffff)
+ msg_fatal("certificate too long: %d", len);
buf2 = buf = (unsigned char *) mymalloc(len);
i2d_X509(cert, &buf2);
- if (!*mdname)
- ta_cert_insert(dane, cert);
break;
case DNS_TLSA_SELECTOR_SUBJECTPUBLICKEYINFO:
pkey = X509_get_pubkey(cert);
len = i2d_PUBKEY(pkey, NULL);
+ if (len > 0xffff)
+ msg_fatal("public key too long: %d", len);
buf2 = buf = (unsigned char *) mymalloc(len);
i2d_PUBKEY(pkey, &buf2);
- if (!*mdname)
- ta_pkey_insert(dane, pkey);
EVP_PKEY_free(pkey);
break;
}
+ X509_free(cert);
OPENSSL_assert(buf2 - buf == len);
- digest = tls_data_fprint((char *) buf, len, *mdname ? mdname : signalg);
- dane_add(dane, u, s, *mdname ? mdname : signalg, digest);
- myfree((void *) digest);
+ switch (m) {
+ case 0:
+ break;
+ case 1:
+ if ((md = EVP_get_digestbyname(LN_sha256)) == 0)
+ msg_fatal("Digest %s not found", LN_sha256);
+ break;
+ case 2:
+ if ((md = EVP_get_digestbyname(LN_sha512)) == 0)
+ msg_fatal("Digest %s not found", LN_sha512);
+ break;
+ default:
+ msg_fatal("Unsupported DANE mtype: %d", m);
+ }
+
+ if (md != 0) {
+ unsigned char mdbuf[EVP_MAX_MD_SIZE];
+ unsigned int mdlen = sizeof(mdbuf);
+
+ if (!EVP_Digest(buf, len, mdbuf, &mdlen, md, 0))
+ msg_fatal("Digest failure for mtype: %d", m);
+ myfree(buf);
+ buf = (unsigned char *) mymemdup(mdbuf, len = mdlen);
+ }
+ SSL_dane_tlsa_add(ssl, u, s, m, buf, len);
myfree((void *) buf);
}
exit(1);
}
-/* match_servername - match servername against pattern */
-
-static int match_servername(const char *certid, ARGV *margv)
-{
- const char *domain;
- const char *parent;
- int match_subdomain;
- int i;
- int idlen;
- int domlen;
-
- /*
- * XXX EAI support.
- */
-
- /*
- * Match the certid against each pattern until we find a match.
- */
- for (i = 0; i < margv->argc; ++i) {
- match_subdomain = 0;
- domain = margv->argv[i];
- if (*domain == '.' && domain[1] != '\0') {
- ++domain;
- match_subdomain = 1;
- }
-
- /*
- * Sub-domain match: certid is any sub-domain of hostname.
- */
- 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 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(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);
-}
-
-static void check_name(TLS_SESS_STATE *tctx, X509 *cert, ARGV *margs)
-{
- char *cn;
- int matched = 0;
- general_name_stack_t *gens;
-
- if (SSL_get_verify_result(tctx->con) != X509_V_OK)
- return;
-
- tctx->peer_status |= TLS_CERT_FLAG_TRUSTED;
-
- gens = X509_get_ext_d2i(cert, NID_subject_alt_name, 0, 0);
- if (gens) {
- int has_dnsname = 0;
- int num_gens = sk_GENERAL_NAME_num(gens);
- int i;
-
- for (i = 0; !matched && i < num_gens; ++i) {
- const GENERAL_NAME *gn = sk_GENERAL_NAME_value(gens, i);
- const char *dnsname;
-
- if (gn->type != GEN_DNS)
- continue;
- has_dnsname = 1;
- tctx->peer_status |= TLS_CERT_FLAG_ALTNAME;
- dnsname = tls_dns_name(gn, tctx);
- if (dnsname && *dnsname
- && (matched = match_servername(dnsname, margs)) != 0)
- tctx->peer_status |= TLS_CERT_FLAG_MATCHED;
- }
- sk_GENERAL_NAME_pop_free(gens, GENERAL_NAME_free);
- if (has_dnsname)
- return;
- }
- cn = tls_peer_CN(cert, tctx);
- if (match_servername(cn, margs))
- tctx->peer_status |= TLS_CERT_FLAG_MATCHED;
- myfree(cn);
-}
-
-static void check_print(TLS_SESS_STATE *tctx, X509 *cert)
-{
- if (TLS_DANE_HASEE(tctx->dane)
- && tls_dane_match(tctx, TLS_DANE_EE, cert, 0))
- tctx->peer_status |= TLS_CERT_FLAG_TRUSTED | TLS_CERT_FLAG_MATCHED;
-}
-
-static void check_peer(TLS_SESS_STATE *tctx, X509 *cert, int argc, char **argv)
-{
- ARGV match;
-
- tctx->peer_status |= TLS_CERT_FLAG_PRESENT;
- check_print(tctx, cert);
- if (!TLS_CERT_IS_MATCHED(tctx)) {
- match.argc = argc;
- match.argv = argv;
- check_name(tctx, cert, &match);
- }
-}
-
static SSL_CTX *ctx_init(const char *CAfile)
{
SSL_CTX *client_ctx;
tls_param_init();
tls_check_version();
-#if OPENSSL_VERSION_NUMBER < 0x10100000L
- SSL_load_error_strings();
- SSL_library_init();
-#endif
-
if (!tls_validate_digest(LN_sha1))
msg_fatal("%s digest algorithm not available", LN_sha1);
msg_fatal("cannot allocate client SSL_CTX");
SSL_CTX_set_verify_depth(client_ctx, 5);
+ /* Enable DANE support in OpenSSL */
+ if (SSL_CTX_dane_enable(client_ctx) <= 0) {
+ tls_print_errors();
+ msg_fatal("OpenSSL DANE initialization failed");
+ }
if (tls_set_ca_certificate_info(client_ctx, CAfile, "") < 0) {
tls_print_errors();
msg_fatal("cannot load CAfile: %s", CAfile);
int main(int argc, char *argv[])
{
SSL_CTX *ssl_ctx;
+ const EVP_MD *fpt_alg;
TLS_SESS_STATE *tctx;
x509_stack_t *chain;
+ int i;
var_procname = mystrdup(basename(argv[0]));
set_mail_conf_str(VAR_PROCNAME, var_procname);
tctx = tls_alloc_sess_context(TLS_LOG_NONE, argv[7]);
tctx->namaddr = argv[7];
- tctx->mdalg = LN_sha1;
+ tctx->mdalg = LN_sha256;
tctx->dane = tls_dane_alloc();
+ if ((fpt_alg = tls_validate_digest(tctx->mdalg)) == 0)
+ msg_fatal("fingerprint digest algorithm %s not found",
+ tctx->mdalg);
+ tls_dane_digest_init(ssl_ctx, fpt_alg);
+
if ((tctx->con = SSL_new(ssl_ctx)) == 0
|| !SSL_set_ex_data(tctx->con, TLScontext_index, tctx)) {
tls_print_errors();
msg_fatal("Error allocating SSL connection");
}
+ if (SSL_dane_enable(tctx->con, 0) <= 0) {
+ tls_print_errors();
+ msg_fatal("Error enabling DANE for SSL handle");
+ }
+ SSL_dane_set_flags(tctx->con, DANE_FLAG_NO_DANE_EE_NAMECHECKS);
+ SSL_dane_set_flags(tctx->con, X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS);
+ for (i = 7; i < argc; ++i)
+ if (!SSL_add1_host(tctx->con, argv[i]))
+ msg_fatal("error adding hostname: %s", argv[i]);
+ load_tlsa_args(tctx->con, argv);
SSL_set_connect_state(tctx->con);
- add_tlsa((TLS_DANE *) tctx->dane, argv);
- tls_dane_set_callback(ssl_ctx, tctx);
/* Verify saved server chain */
chain = load_chain(argv[6]);
- verify_chain(tctx->con, chain, tctx);
- check_peer(tctx, sk_X509_value(chain, 0), argc - 7, argv + 7);
+ i = verify_chain(tctx->con, chain, tctx);
tls_print_errors();
- msg_info("%s %s", TLS_CERT_IS_MATCHED(tctx) ? "Verified" :
- TLS_CERT_IS_TRUSTED(tctx) ? "Trusted" : "Untrusted", argv[7]);
+ if (i > 0) {
+ const char *peername = SSL_get0_peername(tctx->con);
+
+ if (peername == 0)
+ peername = argv[7];
+ msg_info("Verified %s", peername);
+ } else {
+ i = SSL_get_verify_result(tctx->con);
+ msg_info("certificate verification failed for %s:%s: num=%d:%s",
+ argv[6], argv[7], i, X509_verify_cert_error_string(i));
+ }
- return (TLS_CERT_IS_MATCHED(tctx) ? 0 : 1);
+ return (i <= 0);
}
#endif /* TEST */
/* #define TLS_INTERNAL
/* #include <tls.h>
/*
-/* void tls_set_dh_from_file(path, bits)
+/* void tls_set_dh_from_file(path)
/* const char *path;
-/* int bits;
/*
/* void tls_auto_eecdh_curves(ctx, configured)
/* SSL_CTX *ctx;
/* char *configured;
/*
-/* void tls_set_eecdh_curve(server_ctx, grade)
-/* SSL_CTX *server_ctx;
-/* const char *grade;
-/*
-/* DH *tls_tmp_dh_cb(ssl, export, keylength)
-/* SSL *ssl; /* unused */
-/* int export;
-/* int keylength;
+/* void tls_tmp_dh(ctx)
+/* SSL_CTX *ctx;
/* DESCRIPTION
/* This module maintains parameters for Diffie-Hellman key generation.
/*
-/* tls_tmp_dh_cb() is a call-back routine for the
-/* SSL_CTX_set_tmp_dh_callback() function.
+/* tls_tmp_dh() returns the configured or compiled-in FFDHE
+/* group parameters.
/*
/* tls_set_dh_from_file() overrides compiled-in DH parameters
/* with those specified in the named files. The file format
-/* is as expected by the PEM_read_DHparams() routine. The
-/* "bits" argument must be 512 or 1024.
+/* is as expected by the PEM_read_DHparams() routine.
/*
/* tls_auto_eecdh_curves() enables negotiation of the most preferred curve
/* among the curves specified by the "configured" argument.
-/*
-/* tls_set_eecdh_curve() enables ephemeral Elliptic-Curve DH
-/* key exchange algorithms by instantiating in the server SSL
-/* context a suitable curve (corresponding to the specified
-/* EECDH security grade) from the set of named curves in RFC
-/* 4492 Section 5.1.1. Errors generate warnings, but do not
-/* disable TLS, rather we continue without EECDH. A zero
-/* result indicates that the grade is invalid or the corresponding
-/* curve could not be used. The "auto" grade enables multiple
-/* curves, with the actual curve chosen as the most preferred
-/* among those supported by both the server and the client.
/* DIAGNOSTICS
/* In case of error, tls_set_dh_from_file() logs a warning and
/* ignores the request.
/* Application-specific. */
/*
- * Compiled-in DH parameters. Used when no parameters are explicitly loaded
- * from a site-specific file. Using an ASN.1 DER encoding avoids the need
- * to explicitly manipulate the internal representation of DH parameter
- * objects.
+ * Compiled-in FFDHE (finite-field ephemeral Diffie-Hellman) parameters.
+ * Used when no parameters are explicitly loaded from a site-specific file.
+ * Using an ASN.1 DER encoding avoids the need to explicitly manipulate the
+ * internal representation of DH parameter objects.
*
- * 512-bit parameters are used for export ciphers, and 2048-bit parameters are
- * used for non-export ciphers. The non-export group is now 2048-bit, as
- * 1024 bits is increasingly considered to weak by clients. When greater
- * security is required, use EECDH.
+ * The FFDHE group is now 2048-bit, as 1024 bits is increasingly considered to
+ * weak by clients. When greater security is required, use EECDH.
*/
- /*-
- * Generated via:
- * $ openssl dhparam -2 -outform DER 512 2>/dev/null |
- * hexdump -ve '/1 "0x%02x, "' | fmt
- * TODO: generate at compile-time. But that is no good for the majority of
- * sites that install pre-compiled binaries, and breaks reproducible builds.
- * Instead, generate at installation time and use main.cf configuration.
- */
-static unsigned char dh512_der[] = {
- 0x30, 0x46, 0x02, 0x41, 0x00, 0xd8, 0xbf, 0x11, 0xd6, 0x41, 0x2a, 0x7a,
- 0x9c, 0x78, 0xb2, 0xaa, 0x41, 0x23, 0x0a, 0xdc, 0xcf, 0xb7, 0x19, 0xc5,
- 0x16, 0x4c, 0xcb, 0x4a, 0xd0, 0xd2, 0x1f, 0x1f, 0x70, 0x24, 0x86, 0x6f,
- 0x51, 0x52, 0xc6, 0x5b, 0x28, 0xbb, 0x82, 0xe1, 0x24, 0x91, 0x3d, 0x4d,
- 0x95, 0x56, 0xf8, 0x0b, 0x2c, 0xe0, 0x36, 0x67, 0x88, 0x64, 0x15, 0x1f,
- 0x45, 0xd5, 0xb8, 0x0a, 0x00, 0x03, 0x76, 0x32, 0x0b, 0x02, 0x01, 0x02,
-};
-
/*-
* Generated via:
* $ openssl dhparam -2 -outform DER 2048 2>/dev/null |
* Instead, generate at installation time and use main.cf configuration.
*/
static unsigned char dh2048_der[] = {
- 0x30, 0x82, 0x01, 0x08, 0x02, 0x82, 0x01, 0x01, 0x00, 0xbf, 0x28, 0x1b,
- 0x68, 0x69, 0x90, 0x2f, 0x37, 0x9f, 0x5a, 0x50, 0x23, 0x73, 0x2c, 0x11,
- 0xf2, 0xac, 0x7c, 0x3e, 0x58, 0xb9, 0x23, 0x3e, 0x02, 0x07, 0x4d, 0xba,
- 0xd9, 0x2c, 0xc1, 0x9e, 0xf9, 0xc4, 0x2f, 0xbc, 0x8d, 0x86, 0x4b, 0x2a,
- 0x87, 0x86, 0x93, 0x32, 0x0f, 0x72, 0x40, 0xfe, 0x7e, 0xa2, 0xc1, 0x32,
- 0xf0, 0x65, 0x9c, 0xc3, 0x19, 0x25, 0x2d, 0xeb, 0x6a, 0x49, 0x94, 0x79,
- 0x2d, 0xa1, 0xbe, 0x05, 0x26, 0xac, 0x8d, 0x69, 0xdc, 0x2e, 0x7e, 0xb5,
- 0xfd, 0x3c, 0x2b, 0x7d, 0x43, 0x22, 0x53, 0xf6, 0x1e, 0x04, 0x45, 0xd7,
- 0x53, 0x84, 0xfd, 0x6b, 0x12, 0x72, 0x47, 0x04, 0xaf, 0xa4, 0xac, 0x4b,
- 0x55, 0xb6, 0x79, 0x42, 0x40, 0x88, 0x54, 0x48, 0xd5, 0x4d, 0x3a, 0xb2,
- 0xbf, 0x6c, 0x26, 0x95, 0x29, 0xdd, 0x8b, 0x9e, 0xed, 0xb8, 0x60, 0x8e,
- 0xb5, 0x35, 0xb6, 0x22, 0x44, 0x1f, 0xfb, 0x56, 0x74, 0xfe, 0xf0, 0x2c,
- 0xe6, 0x0c, 0x22, 0xc9, 0x35, 0xb3, 0x1b, 0x96, 0xbb, 0x0a, 0x5a, 0xc3,
- 0x09, 0xa0, 0xcc, 0xa5, 0x40, 0x90, 0x0f, 0x59, 0xa2, 0x89, 0x69, 0x2a,
- 0x69, 0x79, 0xe4, 0xd3, 0x24, 0xc6, 0x8c, 0xda, 0xbc, 0x98, 0x3a, 0x5b,
- 0x16, 0xae, 0x63, 0x6c, 0x0b, 0x43, 0x4f, 0xf3, 0x2e, 0xc8, 0xa9, 0x6b,
- 0x58, 0x6a, 0xa9, 0x8e, 0x64, 0x09, 0x3d, 0x88, 0x44, 0x4f, 0x97, 0x2c,
- 0x1d, 0x98, 0xb0, 0xa9, 0xc0, 0xb6, 0x8d, 0x19, 0x37, 0x1f, 0xb7, 0xc9,
- 0x86, 0xa8, 0xdc, 0x37, 0x4d, 0x64, 0x27, 0xf3, 0xf5, 0x2b, 0x7b, 0x6b,
- 0x76, 0x84, 0x3f, 0xc1, 0x23, 0x97, 0x2d, 0x71, 0xf7, 0xb6, 0xc2, 0x35,
- 0x28, 0x10, 0x96, 0xd6, 0x69, 0x0c, 0x2e, 0x1f, 0x9f, 0xdf, 0x82, 0x81,
- 0x57, 0x57, 0x39, 0xa5, 0xf2, 0x81, 0x29, 0x57, 0xf9, 0x2f, 0xd0, 0x03,
- 0xab, 0x02, 0x01, 0x02,
+ 0x30, 0x82, 0x01, 0x08, 0x02, 0x82, 0x01, 0x01, 0x00, 0x9e, 0x28, 0x15,
+ 0xc5, 0xcc, 0x9b, 0x5a, 0xb0, 0xe9, 0xab, 0x74, 0x8b, 0x2a, 0x23, 0xce,
+ 0xea, 0x87, 0xa0, 0x18, 0x09, 0xd0, 0x40, 0x2c, 0x93, 0x23, 0x5d, 0xc0,
+ 0xe9, 0x78, 0x2c, 0x53, 0xd9, 0x3e, 0x21, 0x14, 0x89, 0x5c, 0x79, 0x73,
+ 0x1e, 0xbd, 0x23, 0x1e, 0x18, 0x65, 0x6d, 0xd2, 0x3c, 0xeb, 0x41, 0xca,
+ 0xbb, 0xa9, 0x99, 0x55, 0x84, 0xae, 0x9e, 0x70, 0x57, 0x25, 0x21, 0x42,
+ 0xaa, 0xdb, 0x82, 0xc6, 0xe6, 0xf1, 0xcf, 0xb7, 0xbc, 0x2a, 0x56, 0xcc,
+ 0x55, 0x1f, 0xad, 0xe9, 0x68, 0x18, 0x22, 0xfc, 0x09, 0x62, 0xc3, 0x32,
+ 0x1b, 0x05, 0x1f, 0xce, 0xec, 0xe3, 0x6d, 0xb5, 0x79, 0xe0, 0x89, 0x45,
+ 0xf3, 0xf3, 0x26, 0xa3, 0x81, 0xd9, 0x59, 0xee, 0xed, 0x78, 0xbe, 0x0e,
+ 0xdd, 0xf7, 0xef, 0xcb, 0x81, 0x3f, 0x01, 0xb7, 0x10, 0x8f, 0x0d, 0xbe,
+ 0x29, 0x21, 0x13, 0xff, 0x2a, 0x13, 0x25, 0x75, 0x99, 0xec, 0xf5, 0x2d,
+ 0x49, 0x01, 0x1d, 0xa4, 0x13, 0xe8, 0x2c, 0xc8, 0x13, 0x60, 0x57, 0x98,
+ 0xb1, 0x06, 0x45, 0x77, 0xa4, 0x24, 0xf9, 0x27, 0x3f, 0x08, 0xe6, 0x9b,
+ 0x4b, 0x20, 0x3b, 0x43, 0x69, 0xa3, 0xcc, 0x9a, 0xc4, 0x3c, 0x1e, 0xec,
+ 0xb7, 0x35, 0xe4, 0x59, 0x6b, 0x6d, 0x2a, 0xdf, 0xf7, 0x0b, 0xd4, 0x5a,
+ 0x0f, 0x79, 0x80, 0xe1, 0x75, 0x4c, 0x10, 0xea, 0x26, 0xf0, 0xd5, 0xf3,
+ 0xa6, 0x15, 0xa9, 0x3e, 0x3d, 0x0d, 0xb8, 0x53, 0x50, 0x49, 0x77, 0x49,
+ 0x47, 0x43, 0x39, 0xee, 0xb8, 0x8a, 0xe5, 0x14, 0xc4, 0xe3, 0x10, 0xfb,
+ 0xf5, 0x52, 0xef, 0xa5, 0x8f, 0xa4, 0x7e, 0x57, 0xb9, 0x5f, 0xda, 0x00,
+ 0x18, 0xf0, 0x72, 0x29, 0xd4, 0xfe, 0x90, 0x5a, 0x1f, 0x1a, 0x40, 0xee,
+ 0x4e, 0xfa, 0x3e, 0xf3, 0x72, 0x4b, 0xea, 0x44, 0x53, 0x43, 0x53, 0x57,
+ 0x9b, 0x02, 0x01, 0x02,
};
/*
* Cached results.
*/
-static DH *dh_1024 = 0;
-static DH *dh_512 = 0;
+static DH *dh_2048 = 0;
/* tls_set_dh_from_file - set Diffie-Hellman parameters from file */
-void tls_set_dh_from_file(const char *path, int bits)
+void tls_set_dh_from_file(const char *path)
{
FILE *paramfile;
- DH **dhPtr;
-
- switch (bits) {
- case 512:
- dhPtr = &dh_512;
- break;
- case 1024:
- dhPtr = &dh_1024;
- break;
- default:
- msg_panic("Invalid DH parameters size %d, file %s", bits, path);
- }
/*
* This function is the first to set the DH parameters, but free any
* prior value just in case the call sequence changes some day.
*/
- if (*dhPtr) {
- DH_free(*dhPtr);
- *dhPtr = 0;
+ if (dh_2048) {
+ DH_free(dh_2048);
+ dh_2048 = 0;
}
if ((paramfile = fopen(path, "r")) != 0) {
- if ((*dhPtr = PEM_read_DHparams(paramfile, 0, 0, 0)) == 0) {
- msg_warn("cannot load %d-bit DH parameters from file %s"
- " -- using compiled-in defaults", bits, path);
+ if ((dh_2048 = PEM_read_DHparams(paramfile, 0, 0, 0)) == 0) {
+ msg_warn("cannot load DH parameters from file %s"
+ " -- using compiled-in defaults", path);
tls_print_errors();
}
(void) fclose(paramfile); /* 200411 */
} else {
- msg_warn("cannot load %d-bit DH parameters from file %s: %m"
- " -- using compiled-in defaults", bits, path);
+ msg_warn("cannot load DH parameters from file %s: %m"
+ " -- using compiled-in defaults", path);
}
}
-/* tls_get_dh - get compiled-in DH parameters */
+/* tls_tmp_dh - configure FFDHE group */
-static DH *tls_get_dh(const unsigned char *p, size_t plen)
+void tls_tmp_dh(SSL_CTX *ctx)
{
- const unsigned char *endp = p;
- DH *dh = 0;
-
- if (d2i_DHparams(&dh, &endp, plen) && plen == endp - p)
- return (dh);
-
- msg_warn("cannot load compiled-in DH parameters");
- if (dh)
- DH_free(dh);
- return (0);
-}
-
-/* tls_tmp_dh_cb - call-back for Diffie-Hellman parameters */
-
-DH *tls_tmp_dh_cb(SSL *unused_ssl, int export, int keylength)
-{
- DH *dh_tmp;
-
- if (export && keylength == 512) { /* 40-bit export cipher */
- if (dh_512 == 0)
- dh_512 = tls_get_dh(dh512_der, sizeof(dh512_der));
- dh_tmp = dh_512;
- } else { /* ADH, DHE-RSA or DSA */
- if (dh_1024 == 0)
- dh_1024 = tls_get_dh(dh2048_der, sizeof(dh2048_der));
- dh_tmp = dh_1024;
+ if (dh_2048 == 0) {
+ const unsigned char *endp = dh2048_der;
+ DH *dh = 0;
+
+ if (d2i_DHparams(&dh, &endp, sizeof(dh2048_der))
+ && sizeof(dh2048_der) == endp - dh2048_der) {
+ dh_2048 = dh;
+ } else {
+ DH_free(dh); /* Unlikely non-zero, but by
+ * the book */
+ msg_warn("error loading compiled-in DH parameters");
+ }
}
- return (dh_tmp);
+ if (ctx != 0 && dh_2048 != 0)
+ SSL_CTX_set_tmp_dh(ctx, dh_2048);
}
void tls_auto_eecdh_curves(SSL_CTX *ctx, const char *configured)
tls_print_errors();
RETURN;
}
-
- /*
- * This is a NOP in OpenSSL 1.1.0 and later, where curves are always
- * auto-negotiated.
- */
-#if OPENSSL_VERSION_NUMBER < 0x10100000UL
- if (SSL_CTX_set_ecdh_auto(ctx, 1) <= 0) {
- msg_warn("failed to enable automatic ECDHE curve selection");
- tls_print_errors();
- RETURN;
- }
-#endif
RETURN;
#endif
}
-#define TLS_EECDH_INVALID 0
-#define TLS_EECDH_NONE 1
-#define TLS_EECDH_STRONG 2
-#define TLS_EECDH_ULTRA 3
-#define TLS_EECDH_AUTO 4
-
-void tls_set_eecdh_curve(SSL_CTX *server_ctx, const char *grade)
-{
-#ifndef OPENSSL_NO_ECDH
- int g;
- static NAME_CODE eecdh_table[] = {
- "none", TLS_EECDH_NONE,
- "strong", TLS_EECDH_STRONG,
- "ultra", TLS_EECDH_ULTRA,
- "auto", TLS_EECDH_AUTO,
- 0, TLS_EECDH_INVALID,
- };
-
- switch (g = name_code(eecdh_table, NAME_CODE_FLAG_NONE, grade)) {
- default:
- msg_panic("Invalid eecdh grade code: %d", g);
- case TLS_EECDH_INVALID:
- msg_warn("Invalid TLS eecdh grade \"%s\": EECDH disabled", grade);
- return;
- case TLS_EECDH_STRONG:
- tls_auto_eecdh_curves(server_ctx, var_tls_eecdh_strong);
- return;
- case TLS_EECDH_ULTRA:
- tls_auto_eecdh_curves(server_ctx, var_tls_eecdh_ultra);
- return;
- case TLS_EECDH_NONE:
-
- /*
- * Pretend "none" is "auto", the former is no longer supported or
- * wise
- */
- msg_warn("The \"none\" eecdh grade is no longer supported, "
- "using \"auto\" instead");
- case TLS_EECDH_AUTO:
- tls_auto_eecdh_curves(server_ctx, var_tls_eecdh_auto);
- return;
- }
-#endif
- return;
-}
-
#ifdef TEST
int main(int unused_argc, char **unused_argv)
{
- tls_tmp_dh_cb(0, 1, 512);
- tls_tmp_dh_cb(0, 1, 1024);
- tls_tmp_dh_cb(0, 1, 2048);
- tls_tmp_dh_cb(0, 0, 512);
- return (0);
+ tls_tmp_dh(0);
+ return (dh_2048 == 0);
}
#endif
/* SYNOPSIS
/* #include <tls.h>
/*
-/* char *tls_serverid_digest(props, protomask, ciphers)
+/* char *tls_serverid_digest(TLScontext, props, protomask, ciphers)
+/* TLS_SESS_STATE *TLScontext;
/* const TLS_CLIENT_START_PROPS *props;
/* long protomask;
/* const char *ciphers;
/* const unsigned char *md_buf;
/* const char *md_len;
/*
-/* char *tls_data_fprint(buf, len, mdalg)
-/* const char *buf;
-/* int len;
-/* const char *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_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_cert_fprint() returns a fingerprint of the the given
/* certificate using the requested message digest, formatted
/* with tls_digest_encode(). Panics if the
/* and the caller must eventually free it with myfree().
/*
/* tls_serverid_digest() suffixes props->serverid computed by the SMTP
-/* client with "&" plus a digest of additional parameters
-/* needed to ensure that re-used sessions are more likely to
-/* be reused and that they will satisfy all protocol and
-/* security requirements.
-/* The return value is dynamically allocated with mymalloc(),
-/* and the caller must eventually free it with myfree().
+/* client with "&" plus a digest of additional parameters needed to ensure
+/* that re-used sessions are more likely to be reused and that they will
+/* satisfy all protocol and security requirements. The return value is
+/* dynamically allocated with mymalloc(), and the caller must eventually
+/* free it with myfree().
/*
/* Arguments:
/* .IP peercert
static const char hexcodes[] = "0123456789ABCDEF";
#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)
+#define digest_object(p) digest_data((unsigned char *)(p), sizeof(*(p)))
+#define digest_data(p, l) checkok(digest_bytes(mdctx, (p), (l)))
+#define digest_string(s) checkok(digest_chars(mdctx, (s)))
+#define digest_dane(tlsa) checkok(tls_digest_tlsa(mdctx, tlsa))
+
+/* digest_bytes - hash octet string of given length */
+
+static int digest_bytes(EVP_MD_CTX *ctx, const unsigned char *buf, size_t len)
{
- char **dgst;
+ return (EVP_DigestUpdate(ctx, buf, len));
+}
+
+/* digest_chars - hash string including trailing NUL */
+
+static int digest_chars(EVP_MD_CTX *ctx, const char *s)
+{
+ return (EVP_DigestUpdate(ctx, s, strlen(s) + 1));
+}
+
+/* tlsa_cmp - compare TLSA RRs for sorting to canonical order */
+
+static int tlsa_cmp(const void *a, const void *b)
+{
+ TLS_TLSA *p = *(TLS_TLSA **) a;
+ TLS_TLSA *q = *(TLS_TLSA **) b;
+ int d;
+
+ if ((d = (int) p->usage - (int) q->usage) != 0)
+ return d;
+ if ((d = (int) p->selector - (int) q->selector) != 0)
+ return d;
+ if ((d = (int) p->mtype - (int) q->mtype) != 0)
+ return d;
+ if ((d = (int) p->length - (int) q->length) != 0)
+ return d;
+ return (memcmp(p->data, q->data, p->length));
+}
+
+/* tls_digest_tlsa - fold in digest of sorced TLSA records */
+
+static int tls_digest_tlsa(EVP_MD_CTX *mdctx, TLS_TLSA *tlsa)
+{
+ TLS_TLSA *p;
+ TLS_TLSA **arr;
int ok = 1;
+ int n;
+ int i;
- for (digest_string(usage); tlsa; tlsa = tlsa->next) {
- digest_string(tlsa->mdalg);
- digest_tlsa_argv(tlsa, pkeys);
- digest_tlsa_argv(tlsa, certs);
+ for (n = 0, p = tlsa; p != 0; p = p->next)
+ ++n;
+ arr = (TLS_TLSA **) mymalloc(n * sizeof(*arr));
+ for (i = 0, p = tlsa; p; p = p->next)
+ arr[i++] = (void *) p;
+ qsort(arr, n, sizeof(arr[0]), tlsa_cmp);
+
+ digest_object(&n);
+ for (i = 0; i < n; ++i) {
+ digest_object(&arr[i]->usage);
+ digest_object(&arr[i]->selector);
+ digest_object(&arr[i]->mtype);
+ digest_object(&arr[i]->length);
+ digest_data(arr[i]->data, arr[i]->length);
}
+ myfree((void *) arr);
return (ok);
}
/* tls_serverid_digest - suffix props->serverid with parameter digest */
-char *tls_serverid_digest(const TLS_CLIENT_START_PROPS *props, long protomask,
- const char *ciphers)
+char *tls_serverid_digest(TLS_SESS_STATE *TLScontext,
+ const TLS_CLIENT_START_PROPS *props,
+ long protomask, const char *ciphers)
{
EVP_MD_CTX *mdctx;
const EVP_MD *md;
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 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.
+ * Ensure separation of caches for sessions where DANE trust
+ * configuration succeeded from those where it did not. The latter
+ * should always see a certificate validation failure, both on initial
+ * handshake and on resumption.
*/
- if (props->dane) {
- digest_dane(props->dane, ta);
-#if 0
- digest_dane(props->dane, ee); /* See above */
-#endif
- digest_string(TLS_DANE_BASED(props->tls_level) ? props->host : "");
+ digest_object(&TLScontext->must_fail);
+
+ /*
+ * DNS-based or synthetic DANE trust settings are potentially used at all
+ * levels above "encrypt".
+ */
+ if (TLScontext->level > TLS_LEV_ENCRYPT
+ && props->dane && props->dane->tlsa) {
+ digest_dane(props->dane->tlsa);
+ } else {
+ int none = 0; /* Record a TLSA RR count of zero */
+
+ digest_object(&none);
}
+
+ /*
+ * Include the chosen SNI name, which can affect server certificate
+ * selection.
+ */
+ if (TLScontext->level > TLS_LEV_ENCRYPT && TLScontext->peer_sni)
+ digest_string(TLScontext->peer_sni);
+ else
+ digest_string("");
+
checkok(EVP_DigestFinal_ex(mdctx, md_buf, &md_len));
EVP_MD_CTX_destroy(mdctx);
if (!ok)
/* tls_data_fprint - compute and encode digest of binary object */
-char *tls_data_fprint(const char *buf, int len, const char *mdalg)
+static char *tls_data_fprint(const unsigned char *buf, int len, const char *mdalg)
{
EVP_MD_CTX *mdctx;
const EVP_MD *md;
char *tls_cert_fprint(X509 *peercert, const char *mdalg)
{
int len;
- char *buf;
- char *buf2;
+ unsigned char *buf;
+ unsigned char *buf2;
char *result;
len = i2d_X509(peercert, NULL);
buf2 = buf = mymalloc(len);
- i2d_X509(peercert, (unsigned char **) &buf2);
+ i2d_X509(peercert, &buf2);
if (buf2 - buf != len)
msg_panic("i2d_X509 invalid result length");
msg_fatal("%s: error extracting legacy public-key fingerprint: %m",
myname);
- result = tls_data_fprint((char *) key->data, key->length, mdalg);
+ result = tls_data_fprint(key->data, key->length, mdalg);
return (result);
} else {
int len;
- char *buf;
- char *buf2;
+ unsigned char *buf;
+ unsigned char *buf2;
char *result;
len = i2d_X509_PUBKEY(X509_get_X509_PUBKEY(peercert), NULL);
buf2 = buf = mymalloc(len);
- i2d_X509_PUBKEY(X509_get_X509_PUBKEY(peercert), (unsigned char **) &buf2);
+ i2d_X509_PUBKEY(X509_get_X509_PUBKEY(peercert), &buf2);
if (buf2 - buf != len)
msg_panic("i2d_X509_PUBKEY invalid result length");
/* TLS_APPL_STATE *app_ctx;
/* int log_mask;
/*
-/* int tls_validate_digest(dgst)
+/* const EVP_MD *tls_validate_digest(dgst)
/* const char *dgst;
/* DESCRIPTION
/* This module implements public and internal routines that
/* 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.
+/* tls_validate_digest() returns a static handle for the named
+/* digest algorithm, or NULL on error.
/* LICENSE
/* .ad
/* .fi
#include <ctype.h>
#include <string.h>
-#ifdef USE_TLS
-
/* Utility library. */
#include <vstream.h>
char *var_openssl_path;
char *var_tls_server_sni_maps;
bool var_tls_fast_shutdown;
-
-static MAPS *tls_server_sni_maps;
-
-#ifdef VAR_TLS_PREEMPT_CLIST
bool var_tls_preempt_clist;
-#endif
+#ifdef USE_TLS
+
+static MAPS *tls_server_sni_maps;
/*
* Index to attach TLScontext pointers to SSL objects, so that they can be
"certmatch", TLS_LOG_CERTMATCH,
"verbose", TLS_LOG_VERBOSE, /* Postfix TLS library verbose */
"cache", TLS_LOG_CACHE,
+ "dane", TLS_LOG_DANE, /* DANE policy construction */
"ssl-debug", TLS_LOG_DEBUG, /* SSL library debug/verbose */
"ssl-handshake-packet-dump", TLS_LOG_TLSPKTS,
"ssl-session-packet-dump", TLS_LOG_TLSPKTS | TLS_LOG_ALLPKTS,
void tls_get_signature_params(TLS_SESS_STATE *TLScontext)
{
-#if OPENSSL_VERSION_NUMBER >= 0x1010100fUL && defined(TLS1_3_VERSION)
const char *kex_name = 0;
const char *kex_curve = 0;
const char *locl_sig_name = 0;
*/
if (SSL_get_peer_signature_nid(ssl, &nid) && nid != NID_undef)
peer_sig_dgst = OBJ_nid2sn(nid);
+
+ X509_free(cert);
}
if (kex_name) {
TLScontext->kex_name = mystrdup(kex_name);
if (peer_sig_dgst)
SIG_PROP(TLScontext, !srvr, dgst) = mystrdup(peer_sig_dgst);
}
-#endif /* OPENSSL_VERSION_NUMBER ... */
}
/* tls_log_summary - TLS loglevel 1 one-liner, embellished with TLS 1.3 details */
TLScontext->mdalg = 0; /* Alias for props->mdalg */
TLScontext->dane = 0; /* Alias for props->dane */
TLScontext->errordepth = -1;
- TLScontext->tadepth = -1;
TLScontext->errorcode = X509_V_OK;
TLScontext->errorcert = 0;
- TLScontext->untrusted = 0;
- TLScontext->trusted = 0;
return (TLScontext);
}
myfree(TLScontext->peer_cert_fprint);
if (TLScontext->peer_pkey_fprint)
myfree(TLScontext->peer_pkey_fprint);
+ if (TLScontext->kex_name)
+ myfree((void *) TLScontext->kex_name);
+ if (TLScontext->kex_curve)
+ myfree((void *) TLScontext->kex_curve);
+ if (TLScontext->clnt_sig_name)
+ myfree((void *) TLScontext->clnt_sig_name);
+ if (TLScontext->clnt_sig_curve)
+ myfree((void *) TLScontext->clnt_sig_curve);
+ if (TLScontext->clnt_sig_dgst)
+ myfree((void *) TLScontext->clnt_sig_dgst);
+ if (TLScontext->srvr_sig_name)
+ myfree((void *) TLScontext->srvr_sig_name);
+ if (TLScontext->srvr_sig_curve)
+ myfree((void *) TLScontext->srvr_sig_curve);
+ if (TLScontext->srvr_sig_dgst)
+ myfree((void *) TLScontext->srvr_sig_dgst);
if (TLScontext->errorcert)
X509_free(TLScontext->errorcert);
- if (TLScontext->untrusted)
- sk_X509_pop_free(TLScontext->untrusted, X509_free);
- if (TLScontext->trusted)
- sk_X509_pop_free(TLScontext->trusted, X509_free);
myfree((void *) TLScontext);
}
*
* The status nibble has one of the values 0 for development, 1 to e for
* betas 1 to 14, and f for release. Parsed OpenSSL version number. for
- * example
- *
- * 0x000906000 == 0.9.6 dev 0x000906023 == 0.9.6b beta 3 0x00090605f ==
- * 0.9.6e release
- *
- * Versions prior to 0.9.3 have identifiers < 0x0930. Versions between
- * 0.9.3 and 0.9.5 had a version identifier with this interpretation:
- *
- * MMNNFFRBB major minor fix final beta/patch
- *
- * for example
- *
- * 0x000904100 == 0.9.4 release 0x000905000 == 0.9.5 dev
- *
- * Version 0.9.5a had an interim interpretation that is like the current
- * one, except the patch level got the highest bit set, to keep continu-
- * ity. The number was therefore 0x0090581f.
+ * example: 0x1010103f == 1.1.1c.
*/
-
- if (version < 0x0930) {
- info->status = 0;
- info->patch = version & 0x0f;
- version >>= 4;
- info->micro = version & 0x0f;
- version >>= 4;
- info->minor = version & 0x0f;
- version >>= 4;
- info->major = version & 0x0f;
- } else if (version < 0x00905800L) {
- info->patch = version & 0xff;
- version >>= 8;
- info->status = version & 0xf;
- version >>= 4;
- info->micro = version & 0xff;
- version >>= 8;
- info->minor = version & 0xff;
- version >>= 8;
- info->major = version & 0xff;
- } else {
- info->status = version & 0xf;
- version >>= 4;
- info->patch = version & 0xff;
- version >>= 8;
- info->micro = version & 0xff;
- version >>= 8;
- info->minor = version & 0xff;
- version >>= 8;
- info->major = version & 0xff;
- if (version < 0x00906000L)
- info->patch &= ~0x80;
- }
+ info->status = version & 0xf;
+ version >>= 4;
+ info->patch = version & 0xff;
+ version >>= 8;
+ info->micro = version & 0xff;
+ version >>= 8;
+ info->minor = version & 0xff;
+ version >>= 8;
+ info->major = version & 0xff;
}
/* tls_check_version - Detect mismatch between headers and library. */
return (ret);
}
-int tls_validate_digest(const char *dgst)
+const EVP_MD *tls_validate_digest(const char *dgst)
{
const EVP_MD *md_alg;
unsigned int md_len;
- /*
- * Register SHA-2 digests, if implemented and not already registered.
- * Improves interoperability with clients and servers that prematurely
- * deploy SHA-2 certificates. Also facilitates DANE and TA support.
- */
-#if defined(LN_sha256) && defined(NID_sha256) && !defined(OPENSSL_NO_SHA256)
- if (!EVP_get_digestbyname(LN_sha224))
- EVP_add_digest(EVP_sha224());
- if (!EVP_get_digestbyname(LN_sha256))
- EVP_add_digest(EVP_sha256());
-#endif
-#if defined(LN_sha512) && defined(NID_sha512) && !defined(OPENSSL_NO_SHA512)
- if (!EVP_get_digestbyname(LN_sha384))
- EVP_add_digest(EVP_sha384());
- if (!EVP_get_digestbyname(LN_sha512))
- EVP_add_digest(EVP_sha512());
-#endif
-
/*
* If the administrator specifies an unsupported digest algorithm, fail
* now, rather than in the middle of a TLS handshake.
dgst, md_len);
return (0);
}
- return (1);
+ return md_alg;
}
#else
#define TLS_ATTR_ISSUER_CN "issuer_CN"
#define TLS_ATTR_PEER_CERT_FPT "peer_fingerprint"
#define TLS_ATTR_PEER_PKEY_FPT "peer_pubkey_fingerprint"
+#define TLS_ATTR_SEC_LEVEL "level"
#define TLS_ATTR_PEER_STATUS "peer_status"
#define TLS_ATTR_CIPHER_PROTOCOL "cipher_protocol"
#define TLS_ATTR_CIPHER_NAME "cipher_name"
/*
* TLS_TLSA attributes.
*/
-#define TLS_ATTR_MDALG "mdalg"
-#define TLS_ATTR_CERTS "certs"
-#define TLS_ATTR_PKEYS "pkeys"
-
- /*
- * TLS_CERTS attributes.
- */
-#define TLS_ATTR_CERT "cert"
-
- /*
- * TLS_PKEYS attributes.
- */
-#define TLS_ATTR_PKEY "pkey"
+#define TLS_ATTR_USAGE "usage"
+#define TLS_ATTR_SELECTOR "selector"
+#define TLS_ATTR_MTYPE "mtype"
+#define TLS_ATTR_DATA "data"
/*
* TLS_DANE attributes.
*/
-#define TLS_ATTR_TA "ta"
-#define TLS_ATTR_EE "ee"
-#define TLS_ATTR_CERTS "certs"
-#define TLS_ATTR_PKEYS "pkeys"
#define TLS_ATTR_DOMAIN "domain"
-#define TLS_ATTR_FLAGS "flags"
-#define TLS_ATTR_EXP "exp"
#endif
return (ret);
}
-/* tls_proxy_client_certs_print - send x509 certificates over stream */
-
-static int tls_proxy_client_certs_print(ATTR_PRINT_COMMON_FN print_fn,
- VSTREAM *fp, int flags, void *ptr)
-{
- TLS_CERTS *tls_certs = (TLS_CERTS *) ptr;
- TLS_CERTS *tp;
- int count;
- int ret;
-
- for (tp = tls_certs, count = 0; tp != 0; tp = tp->next, count++)
- /* void */ ;
- if (msg_verbose)
- msg_info("tls_proxy_client_certs_print count=%d", count);
-
- ret = print_fn(fp, flags | ATTR_FLAG_MORE,
- SEND_ATTR_INT(TLS_ATTR_COUNT, count),
- ATTR_TYPE_END);
-
- if (ret == 0 && count > 0) {
- VSTRING *buf = vstring_alloc(100);
- int n;
-
- for (tp = tls_certs, n = 0; ret == 0 && n < count; tp = tp->next, n++) {
- size_t len = i2d_X509(tp->cert, (unsigned char **) 0);
- unsigned char *bp;
-
- VSTRING_RESET(buf);
- VSTRING_SPACE(buf, len);
- bp = (unsigned char *) STR(buf);
- i2d_X509(tp->cert, &bp);
- if ((char *) bp - STR(buf) != len)
- msg_panic("i2d_X509 failed to encode certificate");
- vstring_set_payload_size(buf, len);
- ret = print_fn(fp, flags | ATTR_FLAG_MORE,
- SEND_ATTR_DATA(TLS_ATTR_CERT, LEN(buf), STR(buf)),
- ATTR_TYPE_END);
- }
- vstring_free(buf);
- }
- /* Do not flush the stream. */
- if (msg_verbose)
- msg_info("tls_proxy_client_certs_print ret=%d", count);
- return (ret);
-}
-
-/* tls_proxy_client_pkeys_print - send public keys over stream */
-
-static int tls_proxy_client_pkeys_print(ATTR_PRINT_COMMON_FN print_fn,
- VSTREAM *fp, int flags, void *ptr)
-{
- TLS_PKEYS *tls_pkeys = (TLS_PKEYS *) ptr;
- TLS_PKEYS *tp;
- int count;
- int ret;
-
- for (tp = tls_pkeys, count = 0; tp != 0; tp = tp->next, count++)
- /* void */ ;
- if (msg_verbose)
- msg_info("tls_proxy_client_pkeys_print count=%d", count);
-
- ret = print_fn(fp, flags | ATTR_FLAG_MORE,
- SEND_ATTR_INT(TLS_ATTR_COUNT, count),
- ATTR_TYPE_END);
-
- if (ret == 0 && count > 0) {
- VSTRING *buf = vstring_alloc(100);
- int n;
-
- for (tp = tls_pkeys, n = 0; ret == 0 && n < count; tp = tp->next, n++) {
- size_t len = i2d_PUBKEY(tp->pkey, (unsigned char **) 0);
- unsigned char *bp;
-
- VSTRING_RESET(buf);
- VSTRING_SPACE(buf, len);
- bp = (unsigned char *) STR(buf);
- i2d_PUBKEY(tp->pkey, &bp);
- if ((char *) bp - STR(buf) != len)
- msg_panic("i2d_PUBKEY failed to encode public key");
- vstring_set_payload_size(buf, len);
- ret = print_fn(fp, flags | ATTR_FLAG_MORE,
- SEND_ATTR_DATA(TLS_ATTR_PKEY, LEN(buf), STR(buf)),
- ATTR_TYPE_END);
- }
- vstring_free(buf);
- }
- /* Do not flush the stream. */
- if (msg_verbose)
- msg_info("tls_proxy_client_pkeys_print ret=%d", count);
- return (ret);
-}
-
/* tls_proxy_client_tlsa_print - send TLS_TLSA over stream */
static int tls_proxy_client_tlsa_print(ATTR_PRINT_COMMON_FN print_fn,
VSTREAM *fp, int flags, void *ptr)
{
- TLS_TLSA *tls_tlsa = (TLS_TLSA *) ptr;
+ TLS_TLSA *head = (TLS_TLSA *) ptr;
TLS_TLSA *tp;
int count;
int ret;
- for (tp = tls_tlsa, count = 0; tp != 0; tp = tp->next, count++)
- /* void */ ;
+ for (tp = head, count = 0; tp != 0; tp = tp->next)
+ ++count;
if (msg_verbose)
msg_info("tls_proxy_client_tlsa_print count=%d", count);
SEND_ATTR_INT(TLS_ATTR_COUNT, count),
ATTR_TYPE_END);
- if (ret == 0 && count > 0) {
- int n;
-
- for (tp = tls_tlsa, n = 0; ret == 0 && n < count; tp = tp->next, n++) {
- ret = print_fn(fp, flags | ATTR_FLAG_MORE,
- SEND_ATTR_STR(TLS_ATTR_MDALG, tp->mdalg),
- SEND_ATTR_FUNC(argv_attr_print,
- (void *) tp->certs),
- SEND_ATTR_FUNC(argv_attr_print,
- (void *) tp->pkeys),
- ATTR_TYPE_END);
- }
- }
+ for (tp = head; ret == 0 && tp != 0; tp = tp->next)
+ ret = print_fn(fp, flags | ATTR_FLAG_MORE,
+ SEND_ATTR_INT(TLS_ATTR_USAGE, tp->usage),
+ SEND_ATTR_INT(TLS_ATTR_SELECTOR, tp->selector),
+ SEND_ATTR_INT(TLS_ATTR_MTYPE, tp->mtype),
+ SEND_ATTR_DATA(TLS_ATTR_DATA, tp->length, tp->data),
+ ATTR_TYPE_END);
+
/* Do not flush the stream. */
if (msg_verbose)
msg_info("tls_proxy_client_tlsa_print ret=%d", count);
msg_info("tls_proxy_client_dane_print dane=%d", dane != 0);
if (ret == 0 && dane != 0) {
+ /* Send the base_domain and RRs, we don't need the other fields */
ret = print_fn(fp, flags | ATTR_FLAG_MORE,
- SEND_ATTR_FUNC(tls_proxy_client_tlsa_print,
- (void *) dane->ta),
- SEND_ATTR_FUNC(tls_proxy_client_tlsa_print,
- (void *) dane->ee),
- SEND_ATTR_FUNC(tls_proxy_client_certs_print,
- (void *) dane->certs),
- SEND_ATTR_FUNC(tls_proxy_client_pkeys_print,
- (void *) dane->pkeys),
SEND_ATTR_STR(TLS_ATTR_DOMAIN,
STRING_OR_EMPTY(dane->base_domain)),
- SEND_ATTR_INT(TLS_ATTR_FLAGS, dane->flags),
- SEND_ATTR_LONG(TLS_ATTR_EXP, dane->expires),
+ SEND_ATTR_FUNC(tls_proxy_client_tlsa_print,
+ (void *) dane->tlsa),
ATTR_TYPE_END);
}
/* Do not flush the stream. */
/* TLS library. */
+#define TLS_INTERNAL
#include <tls.h>
#include <tls_proxy.h>
return (ret);
}
-/* tls_proxy_client_certs_free - destroy TLS_PKEYS from stream */
-
-static void tls_proxy_client_certs_free(TLS_CERTS *tp)
-{
- if (tp->next)
- tls_proxy_client_certs_free(tp->next);
- if (tp->cert)
- X509_free(tp->cert);
- myfree((void *) tp);
-}
-
-/* tls_proxy_client_pkeys_free - destroy TLS_PKEYS from stream */
-
-static void tls_proxy_client_pkeys_free(TLS_PKEYS *tp)
-{
- if (tp->next)
- tls_proxy_client_pkeys_free(tp->next);
- if (tp->pkey)
- EVP_PKEY_free(tp->pkey);
- myfree((void *) tp);
-}
-
-/* tls_proxy_client_tlsa_free - destroy TLS_TLSA from stream */
-
-static void tls_proxy_client_tlsa_free(TLS_TLSA *tp)
-{
- if (tp->next)
- tls_proxy_client_tlsa_free(tp->next);
- myfree(tp->mdalg);
- if (tp->certs)
- argv_free(tp->certs);
- if (tp->pkeys)
- argv_free(tp->pkeys);
- myfree((void *) tp);
-}
-
-/* tls_proxy_client_dane_free - destroy TLS_DANE from stream */
-
-static void tls_proxy_client_dane_free(TLS_DANE *dane)
-{
- if (dane->ta)
- tls_proxy_client_tlsa_free(dane->ta);
- if (dane->ee)
- tls_proxy_client_tlsa_free(dane->ee);
- if (dane->certs)
- tls_proxy_client_certs_free(dane->certs);
- if (dane->pkeys)
- tls_proxy_client_pkeys_free(dane->pkeys);
- myfree(dane->base_domain);
- if (dane->refs-- == 1)
- myfree((void *) dane);
-}
-
/* tls_proxy_client_start_free - destroy TLS_CLIENT_START_PROPS structure */
void tls_proxy_client_start_free(TLS_CLIENT_START_PROPS *props)
argv_free((ARGV *) props->matchargv);
myfree((void *) props->mdalg);
if (props->dane)
- tls_proxy_client_dane_free((TLS_DANE *) props->dane);
+ tls_dane_free((TLS_DANE *) props->dane);
myfree((void *) props);
}
-/* tls_proxy_client_certs_scan - receive TLS_CERTS from stream */
-
-static int tls_proxy_client_certs_scan(ATTR_SCAN_COMMON_FN scan_fn,
- VSTREAM *fp, int flags, void *ptr)
-{
- int ret;
- int count;
- VSTRING *buf = 0;
- TLS_CERTS **tpp;
- TLS_CERTS *head = 0;
- TLS_CERTS *tp;
- int n;
-
- ret = scan_fn(fp, flags | ATTR_FLAG_MORE,
- RECV_ATTR_INT(TLS_ATTR_COUNT, &count),
- ATTR_TYPE_END);
- if (msg_verbose)
- msg_info("tls_proxy_client_certs_scan count=%d", count);
-
- for (tpp = &head, n = 0; ret == 1 && n < count; n++, tpp = &tp->next) {
- *tpp = tp = (TLS_CERTS *) mymalloc(sizeof(*tp));
- const unsigned char *bp;
-
- if (buf == 0)
- buf = vstring_alloc(100);
-
- /*
- * Note: memset() is not a portable way to initialize non-integer
- * types.
- */
- memset(tp, 0, sizeof(*tp));
- ret = scan_fn(fp, flags | ATTR_FLAG_MORE,
- RECV_ATTR_DATA(TLS_ATTR_CERT, buf),
- ATTR_TYPE_END);
- /* Always construct a well-formed structure. */
- if (ret == 1) {
- bp = (const unsigned char *) STR(buf);
- if (d2i_X509(&tp->cert, &bp, LEN(buf)) == 0
- || LEN(buf) != ((char *) bp) - STR(buf)) {
- msg_warn("malformed certificate in TLS_CERTS");
- ret = -1;
- }
- } else {
- tp->cert = 0;
- }
- tp->next = 0;
- }
- if (buf)
- vstring_free(buf);
- if (ret != 1) {
- tls_proxy_client_certs_free(head);
- head = 0;
- }
- *(TLS_CERTS **) ptr = head;
- if (msg_verbose)
- msg_info("tls_proxy_client_certs_scan ret=%d", ret);
- return (ret);
-}
-
-/* tls_proxy_client_pkeys_scan - receive TLS_PKEYS from stream */
-
-static int tls_proxy_client_pkeys_scan(ATTR_SCAN_COMMON_FN scan_fn,
- VSTREAM *fp, int flags, void *ptr)
-{
- int ret;
- int count;
- VSTRING *buf = vstring_alloc(100);
- TLS_PKEYS **tpp;
- TLS_PKEYS *head = 0;
- TLS_PKEYS *tp;
- int n;
-
- ret = scan_fn(fp, flags | ATTR_FLAG_MORE,
- RECV_ATTR_INT(TLS_ATTR_COUNT, &count),
- ATTR_TYPE_END);
- if (msg_verbose)
- msg_info("tls_proxy_client_pkeys_scan count=%d", count);
-
- for (tpp = &head, n = 0; ret == 1 && n < count; n++, tpp = &tp->next) {
- *tpp = tp = (TLS_PKEYS *) mymalloc(sizeof(*tp));
- const unsigned char *bp;
-
- if (buf == 0)
- buf = vstring_alloc(100);
-
- /*
- * Note: memset() is not a portable way to initialize non-integer
- * types.
- */
- memset(tp, 0, sizeof(*tp));
- ret = scan_fn(fp, flags | ATTR_FLAG_MORE,
- RECV_ATTR_DATA(TLS_ATTR_PKEY, buf),
- ATTR_TYPE_END);
- /* Always construct a well-formed structure. */
- if (ret == 1) {
- bp = (const unsigned char *) STR(buf);
- if (d2i_PUBKEY(&tp->pkey, &bp, LEN(buf)) == 0
- || LEN(buf) != (char *) bp - STR(buf)) {
- msg_warn("malformed public key in TLS_PKEYS");
- ret = -1;
- }
- } else {
- tp->pkey = 0;
- }
- tp->next = 0;
- }
- if (buf)
- vstring_free(buf);
- if (ret != 1) {
- tls_proxy_client_pkeys_free(head);
- head = 0;
- }
- *(TLS_PKEYS **) ptr = head;
- if (msg_verbose)
- msg_info("tls_proxy_client_pkeys_scan ret=%d", ret);
- return (ret);
-}
-
/* tls_proxy_client_tlsa_scan - receive TLS_TLSA from stream */
static int tls_proxy_client_tlsa_scan(ATTR_SCAN_COMMON_FN scan_fn,
VSTREAM *fp, int flags, void *ptr)
{
- int ret;
+ static VSTRING *data;
+ TLS_TLSA *head;
int count;
- TLS_TLSA **tpp;
- TLS_TLSA *head = 0;
- TLS_TLSA *tp;
- int n;
+ int ret;
+
+ if (data == 0)
+ data = vstring_alloc(64);
ret = scan_fn(fp, flags | ATTR_FLAG_MORE,
RECV_ATTR_INT(TLS_ATTR_COUNT, &count),
ATTR_TYPE_END);
- if (msg_verbose)
+ if (ret == 1 && msg_verbose)
msg_info("tls_proxy_client_tlsa_scan count=%d", count);
- for (tpp = &head, n = 0; ret == 1 && n < count; n++, tpp = &tp->next) {
- *tpp = tp = (TLS_TLSA *) mymalloc(sizeof(*tp));
- VSTRING *mdalg = vstring_alloc(25);
+ for (head = 0; ret == 1 && count > 0; --count) {
+ int u, s, m;
- /*
- * Note: memset() is not a portable way to initialize non-integer
- * types.
- */
- memset(tp, 0, sizeof(*tp));
- /* Always construct a well-formed structure. */
- tp->certs = 0; /* scan_fn may return early */
- tp->pkeys = 0;
ret = scan_fn(fp, flags | ATTR_FLAG_MORE,
- RECV_ATTR_STR(TLS_ATTR_MDALG, mdalg),
- RECV_ATTR_FUNC(argv_attr_scan, &tp->certs),
- RECV_ATTR_FUNC(argv_attr_scan, &tp->pkeys),
+ RECV_ATTR_INT(TLS_ATTR_USAGE, &u),
+ RECV_ATTR_INT(TLS_ATTR_SELECTOR, &s),
+ RECV_ATTR_INT(TLS_ATTR_MTYPE, &m),
+ RECV_ATTR_DATA(TLS_ATTR_DATA, data),
ATTR_TYPE_END);
- tp->mdalg = vstring_export(mdalg);
- tp->next = 0;
- ret = (ret == 3 ? 1 : -1);
+ if (ret == 4) {
+ ret = 1;
+ /* This makes a copy of the static vstring content */
+ head = tlsa_prepend(head, u, s, m, (unsigned char *) STR(data),
+ LEN(data));
+ } else
+ ret = -1;
}
+
if (ret != 1) {
- tls_proxy_client_tlsa_free(head);
+ tls_tlsa_free(head);
head = 0;
}
*(TLS_TLSA **) ptr = head;
if (ret == 1 && have_dane) {
VSTRING *base_domain = vstring_alloc(25);
- long expires;
- dane = (TLS_DANE *) mymalloc(sizeof(*dane));
-
- /*
- * Note: memset() is not a portable way to initialize non-integer
- * types.
- */
- memset(dane, 0, sizeof(*dane));
- /* Always construct a well-formed structure. */
- dane->ta = 0; /* scan_fn may return early */
- dane->ee = 0;
- dane->certs = 0;
- dane->pkeys = 0;
+ dane = tls_dane_alloc();
+ /* We only need the base domain and TLSA RRs */
ret = scan_fn(fp, flags | ATTR_FLAG_MORE,
- RECV_ATTR_FUNC(tls_proxy_client_tlsa_scan,
- &dane->ta),
- RECV_ATTR_FUNC(tls_proxy_client_tlsa_scan,
- &dane->ee),
- RECV_ATTR_FUNC(tls_proxy_client_certs_scan,
- &dane->certs),
- RECV_ATTR_FUNC(tls_proxy_client_pkeys_scan,
- &dane->pkeys),
RECV_ATTR_STR(TLS_ATTR_DOMAIN, base_domain),
- RECV_ATTR_INT(TLS_ATTR_FLAGS, &dane->flags),
- RECV_ATTR_LONG(TLS_ATTR_EXP, &expires),
+ RECV_ATTR_FUNC(tls_proxy_client_tlsa_scan,
+ &dane->tlsa),
ATTR_TYPE_END);
+
/* Always construct a well-formed structure. */
dane->base_domain = vstring_export(base_domain);
- dane->expires = expires;
- dane->refs = 1;
- ret = (ret == 7 ? 1 : -1);
- /* XXX if scan_fn() completed normally, sanity check dane->flags. */
+ ret = (ret == 2 ? 1 : -1);
if (ret != 1) {
- tls_proxy_client_dane_free(dane);
+ tls_dane_free(dane);
dane = 0;
}
}
STRING_OR_EMPTY(tp->peer_cert_fprint)),
SEND_ATTR_STR(TLS_ATTR_PEER_PKEY_FPT,
STRING_OR_EMPTY(tp->peer_pkey_fprint)),
+ SEND_ATTR_INT(TLS_ATTR_SEC_LEVEL,
+ tp->level),
SEND_ATTR_INT(TLS_ATTR_PEER_STATUS,
tp->peer_status),
SEND_ATTR_STR(TLS_ATTR_CIPHER_PROTOCOL,
RECV_ATTR_STR(TLS_ATTR_ISSUER_CN, issuer_CN),
RECV_ATTR_STR(TLS_ATTR_PEER_CERT_FPT, peer_cert_fprint),
RECV_ATTR_STR(TLS_ATTR_PEER_PKEY_FPT, peer_pkey_fprint),
+ RECV_ATTR_INT(TLS_ATTR_SEC_LEVEL,
+ &tls_context->level),
RECV_ATTR_INT(TLS_ATTR_PEER_STATUS,
&tls_context->peer_status),
RECV_ATTR_STR(TLS_ATTR_CIPHER_PROTOCOL, protocol),
tls_context->srvr_sig_curve = vstring_export(srvr_sig_curve);
tls_context->srvr_sig_dgst = vstring_export(srvr_sig_dgst);
tls_context->namaddr = vstring_export(namaddr);
- ret = (ret == 21 ? 1 : -1);
+ ret = (ret == 22 ? 1 : -1);
if (ret != 1) {
tls_proxy_context_free(tls_context);
tls_context = 0;
-/*++
-/* NAME
-/* tls_rsa
-/* SUMMARY
-/* RSA support
-/* SYNOPSIS
-/* #define TLS_INTERNAL
-/* #include <tls.h>
-/*
-/* RSA *tls_tmp_rsa_cb(ssl, export, keylength)
-/* SSL *ssl; /* unused */
-/* int export;
-/* int keylength;
-/* DESCRIPTION
-/* tls_tmp_rsa_cb() is a call-back routine for the
-/* SSL_CTX_set_tmp_rsa_callback() function.
-/*
-/* This implementation will generate only 512-bit ephemeral
-/* RSA keys for export ciphersuites. It will log a warning in
-/* all other usage contexts.
-/* 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)
-/* Originally written 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>
-#include <msg.h>
-
-#ifdef USE_TLS
-
-/* TLS library. */
-
-#define TLS_INTERNAL
-#include <tls.h>
-#include <openssl/rsa.h>
-
- /*
- * 2015-12-05: Ephemeral RSA removed from OpenSSL 1.1.0-dev
- */
-#if OPENSSL_VERSION_NUMBER < 0x10100000L
-
-/* tls_tmp_rsa_cb - call-back to generate ephemeral RSA key */
-
-RSA *tls_tmp_rsa_cb(SSL *unused_ssl, int export, int keylength)
-{
- static RSA *rsa_tmp;
-
- /*
- * We generate ephemeral RSA keys only for export ciphersuites. In all
- * other contexts use of ephemeral RSA keys violates the SSL/TLS
- * protocol, and only takes place when applications ask for trouble and
- * set the SSL_OP_EPHEMERAL_RSA option. Postfix should never do that.
- */
- if (!export || keylength != 512) {
- msg_warn("%sexport %d-bit ephemeral RSA key requested",
- export ? "" : "non-", keylength);
- return 0;
- }
- if (rsa_tmp == 0) {
- BIGNUM *e = BN_new();
-
- if (e != 0 && BN_set_word(e, RSA_F4) && (rsa_tmp = RSA_new()) != 0)
- if (!RSA_generate_key_ex(rsa_tmp, keylength, e, 0)) {
- RSA_free(rsa_tmp);
- rsa_tmp = 0;
- }
- if (e)
- BN_free(e);
- }
- return (rsa_tmp);
-}
-
-#endif /* OPENSSL_VERSION_NUMBER */
-
-#ifdef TEST
-
-#include <msg_vstream.h>
-
-int main(int unused_argc, char *const argv[])
-{
- int ok = 0;
-
- /*
- * 2015-12-05: Ephemeral RSA removed from OpenSSL 1.1.0-dev
- */
-#if OPENSSL_VERSION_NUMBER < 0x10100000L
- RSA *rsa;
-
- msg_vstream_init(argv[0], VSTREAM_ERR);
-
- /* Export at 512-bits should work */
- rsa = tls_tmp_rsa_cb(0, 1, 512);
- ok = rsa != 0 && RSA_size(rsa) == 512 / 8;
- ok = ok && PEM_write_RSAPrivateKey(stdout, rsa, 0, 0, 0, 0, 0);
- tls_print_errors();
-
- /* Non-export or unexpected bit length should fail */
- ok = ok && tls_tmp_rsa_cb(0, 0, 512) == 0;
- ok = ok && tls_tmp_rsa_cb(0, 1, 1024) == 0;
-#endif
-
- return ok ? 0 : 1;
-}
-
-#endif
-
-#endif
#define FREE_AND_RETURN(ptr, x) { vstring_free(ptr); return (x); }
bin_data = vstring_alloc(hex_data_len / 2 + 1);
- if (hex_decode(bin_data, hex_data, hex_data_len) == 0) {
+ if (hex_decode_opt(bin_data, hex_data, hex_data_len,
+ HEX_DECODE_FLAG_ALLOW_COLON) == 0) {
msg_warn("%s TLS cache: malformed entry for %s: %.100s",
cp->cache_label, cache_id, hex_data);
FREE_AND_RETURN(bin_data, 0);
#define GET_SID(s, v, lptr) ((v) = SSL_SESSION_get_id((s), (lptr)))
- /* OpenSSL 1.1.0 bitrot */
-#if OPENSSL_VERSION_NUMBER >= 0x10100000L
typedef const unsigned char *session_id_t;
-#else
-typedef unsigned char *session_id_t;
-
-#endif
-
/* get_server_session_cb - callback to retrieve session from server cache */
static SSL_SESSION *get_server_session_cb(SSL *ssl, session_id_t session_id,
*/
tls_check_version();
-#if OPENSSL_VERSION_NUMBER < 0x10100000L
-
- /*
- * Initialize the OpenSSL library by the book! To start with, we must
- * initialize the algorithms. We want cleartext error messages instead of
- * just error codes, so we load the error_strings.
- */
- SSL_load_error_strings();
- OpenSSL_add_ssl_algorithms();
-#endif
-
/*
* First validate the protocols. If these are invalid, we can't continue.
*/
return (0);
}
- /*
- * 2015-12-05: Ephemeral RSA removed from OpenSSL 1.1.0-dev
- */
-#if OPENSSL_VERSION_NUMBER < 0x10100000L
-
- /*
- * According to OpenSSL documentation, a temporary RSA key is needed when
- * export ciphers are in use, because the certified key cannot be
- * directly used.
- */
- SSL_CTX_set_tmp_rsa_callback(server_ctx, tls_tmp_rsa_cb);
- SSL_CTX_set_tmp_rsa_callback(sni_ctx, tls_tmp_rsa_cb);
-#endif
-
/*
* Diffie-Hellman key generation parameters can either be loaded from
* files (preferred) or taken from compiled in values. First, set the
* the error handling, since we do have default values compiled in, so we
* will not abort but just log the error message.
*/
- SSL_CTX_set_tmp_dh_callback(server_ctx, tls_tmp_dh_cb);
- SSL_CTX_set_tmp_dh_callback(sni_ctx, tls_tmp_dh_cb);
if (*props->dh1024_param_file != 0)
- tls_set_dh_from_file(props->dh1024_param_file, 1024);
- if (*props->dh512_param_file != 0)
- tls_set_dh_from_file(props->dh512_param_file, 512);
+ tls_set_dh_from_file(props->dh1024_param_file);
+ tls_tmp_dh(server_ctx);
+ tls_tmp_dh(sni_ctx);
/*
* Enable EECDH if available, errors are not fatal, we just keep going
* with any remaining key-exchange algorithms.
*/
- tls_set_eecdh_curve(server_ctx, props->eecdh_grade);
- tls_set_eecdh_curve(sni_ctx, props->eecdh_grade);
+ tls_auto_eecdh_curves(server_ctx, var_tls_eecdh_auto);
+ tls_auto_eecdh_curves(sni_ctx, var_tls_eecdh_auto);
/*
* If we want to check client certificates, we have to indicate it in
/* char *tls_issuer_CN(peercert, TLScontext)
/* X509 *peercert;
/* TLS_SESS_STATE *TLScontext;
-/*
-/* const char *tls_dns_name(gn, TLScontext)
-/* const GENERAL_NAME *gn;
-/* TLS_SESS_STATE *TLScontext;
/* DESCRIPTION
/* tls_verify_certificate_callback() is called several times (directly
/* or indirectly) from crypto/x509/x509_vfy.c. It collects errors
/* freed by the caller; it contains UTF-8 without non-printable
/* ASCII characters.
/*
-/* tls_dns_name() returns the string value of a GENERAL_NAME
-/* from a DNS subjectAltName extension. If non-printable characters
-/* are found, a null string is returned instead. Further sanity
-/* checks may be added if the need arises.
-/*
/* Arguments:
/* .IP ok
/* Result of prior verification: non-zero means success. In
/* .IP TLScontext
/* Server or client context for warning messages.
/* DIAGNOSTICS
-/* tls_peer_CN(), tls_issuer_CN() and tls_dns_name() log a warning
-/* when 1) the requested information is not available in the specified
-/* certificate, 2) the result exceeds a fixed limit, 3) the result
-/* contains NUL characters or the result contains non-printable or
-/* non-ASCII characters.
+/* tls_peer_CN() and tls_issuer_CN() log a warning when 1) the requested
+/* information is not available in the specified certificate, 2) the
+/* result exceeds a fixed limit, 3) the result contains NUL characters or
+/* the result contains non-printable or non-ASCII characters.
/* LICENSE
/* .ad
/* .fi
X509 *cert;
int err;
int depth;
- int max_depth;
SSL *con;
TLS_SESS_STATE *TLScontext;
TLScontext = SSL_get_ex_data(con, TLScontext_index);
depth = X509_STORE_CTX_get_error_depth(ctx);
- /* Don't log the internal root CA unless there's an unexpected error. */
- if (ok && TLScontext->tadepth > 0 && depth > TLScontext->tadepth)
- return (1);
-
/*
- * Certificate chain depth limit violations are mis-reported by the
- * OpenSSL library, from SSL_CTX_set_verify(3):
+ * Transient failures to load the (DNS or synthetic TLSA) trust settings
+ * must poison certificate verification, since otherwise the default
+ * trust store may bless a certificate that would have failed
+ * verification with the preferred trust anchors (or fingerprints).
*
- * The certificate verification depth set with SSL[_CTX]_verify_depth()
- * stops the verification at a certain depth. The error message produced
- * will be that of an incomplete certificate chain and not
- * X509_V_ERR_CERT_CHAIN_TOO_LONG as may be expected.
- *
- * We set a limit that is one higher than the user requested limit. If this
- * higher limit is reached, we raise an error even a trusted root CA is
- * present at this depth. This disambiguates trust chain truncation from
- * an incomplete trust chain.
- */
- max_depth = SSL_get_verify_depth(con) - 1;
-
- /*
- * We never terminate the SSL handshake in the verification callback,
- * rather we allow the TLS handshake to continue, but mark the session as
- * unverified. The application is responsible for closing any sessions
- * with unverified credentials.
+ * Since we unconditionally continue, or in any case if verification is
+ * about to succeed, there is eventually a final depth 0 callback, at
+ * which point we force an "unspecified" error. The failure to load the
+ * trust settings was logged earlier.
*/
- if (max_depth >= 0 && depth > max_depth) {
- X509_STORE_CTX_set_error(ctx, err = X509_V_ERR_CERT_CHAIN_TOO_LONG);
- ok = 0;
+ if (TLScontext->must_fail) {
+ if (depth == 0) {
+ X509_STORE_CTX_set_error(ctx, err = X509_V_ERR_UNSPECIFIED);
+ update_error_state(TLScontext, depth, cert, err);
+ }
+ return (1);
}
if (ok == 0)
update_error_state(TLScontext, depth, cert, err);
TLS_TEXT_NAME_RETURN(mystrdup((char *) utf8_value));
}
-/* tls_dns_name - Extract valid DNS name from subjectAltName value */
-
-const char *tls_dns_name(const GENERAL_NAME * gn,
- const TLS_SESS_STATE *TLScontext)
-{
- const char *myname = "tls_dns_name";
- char *cp;
- const char *dnsname;
- int len;
-
- /*
- * Peername checks are security sensitive, carefully scrutinize the
- * input!
- */
- if (gn->type != GEN_DNS)
- msg_panic("%s: Non DNS input argument", myname);
-
- /*
- * We expect the OpenSSL library to construct GEN_DNS extension objects as
- * ASN1_IA5STRING values. Check we got the right union member.
- */
- if (ASN1_STRING_type(gn->d.ia5) != V_ASN1_IA5STRING) {
- msg_warn("%s: %s: invalid ASN1 value type in subjectAltName",
- myname, TLScontext->namaddr);
- return (0);
- }
-
- /*
- * Safe to treat as an ASCII string possibly holding a DNS name
- */
- dnsname = (const char *) ASN1_STRING_get0_data(gn->d.ia5);
- len = ASN1_STRING_length(gn->d.ia5);
- TRIM0(dnsname, len);
-
- /*
- * Per Dr. Steven Henson of the OpenSSL development team, ASN1_IA5STRING
- * values can have internal ASCII NUL values in this context because
- * their length is taken from the decoded ASN1 buffer, a trailing NUL is
- * always appended to make sure that the string is terminated, but the
- * ASN.1 length may differ from strlen().
- */
- if (len != strlen(dnsname)) {
- msg_warn("%s: %s: internal NUL in subjectAltName",
- myname, TLScontext->namaddr);
- return 0;
- }
-
- /*
- * XXX: Should we be more strict and call valid_hostname()? So long as
- * the name is safe to handle, if it is not a valid hostname, it will not
- * compare equal to the expected peername, so being more strict than
- * "printable" is likely excessive...
- */
- if (*dnsname && !allprint(dnsname)) {
- cp = mystrdup(dnsname);
- msg_warn("%s: %s: non-printable characters in subjectAltName: %.100s",
- myname, TLScontext->namaddr, printable(cp, '?'));
- myfree(cp);
- return 0;
- }
- return (dnsname);
-}
-
/* tls_peer_CN - extract peer common name from certificate */
char *tls_peer_CN(X509 *peercert, const TLS_SESS_STATE *TLScontext)
{
char *cn;
+ const char *san;
+ /* Absent a commonName, return a validated DNS-ID SAN */
cn = tls_text_name(X509_get_subject_name(peercert), NID_commonName,
"subject CN", TLScontext, DONT_GRIPE);
+ if (cn == 0 && (san = SSL_get0_peername(TLScontext->con)) != 0)
+ cn = mystrdup(san);
return (cn ? cn : mystrdup(""));
}
{
state->client_start_props->ctx = state->appl_state;
state->client_start_props->fd = state->ciphertext_fd;
- /* These predicates and warning belong inside tls_client_start(). */
- if (!tls_dane_avail() /* mandatory side effects!! */
- &&TLS_DANE_BASED(state->client_start_props->tls_level))
- msg_warn("%s: DANE requested, but not available",
- state->client_start_props->namaddr);
- else
- state->tls_context = tls_client_start(state->client_start_props);
+ state->tls_context = tls_client_start(state->client_start_props);
if (state->tls_context != 0)
return (TLSP_STAT_OK);
/* tlsp_client_init - initialize a TLS client engine */
static TLS_APPL_STATE *tlsp_client_init(TLS_CLIENT_PARAMS *tls_params,
- TLS_CLIENT_INIT_PROPS *init_props,
- int dane_based)
+ TLS_CLIENT_INIT_PROPS *init_props)
{
TLS_APPL_STATE *appl_state;
VSTRING *param_buf;
init_key = tls_proxy_client_init_with_names_to_string(
init_buf, init_props);
init_buf_for_hashing = vstring_alloc(100);
- init_key_for_hashing = STR(vstring_sprintf(init_buf_for_hashing, "%s%d\n",
- init_key, dane_based));
+ init_key_for_hashing = STR(vstring_sprintf(init_buf_for_hashing, "%s\n",
+ init_key));
if (tlsp_pre_jail_done == 0) {
if (tlsp_pre_jail_client_param_key == 0
|| tlsp_pre_jail_client_init_key == 0) {
return;
}
state->appl_state = tlsp_client_init(state->tls_params,
- state->client_init_props,
- TLS_DANE_BASED(state->client_start_props->tls_level));
+ state->client_init_props);
ready = state->appl_state != 0;
break;
case TLS_PROXY_FLAG_ROLE_SERVER:
if (clnt_use_tls || var_tlsp_clnt_per_site[0] || var_tlsp_clnt_policy[0]) {
TLS_CLIENT_PARAMS tls_params;
TLS_CLIENT_INIT_PROPS init_props;
- int dane_based_mode;
tls_pre_jail_init(TLS_ROLE_CLIENT);
CAfile = var_tlsp_clnt_CAfile,
CApath = var_tlsp_clnt_CApath,
mdalg = var_tlsp_clnt_fpt_dgst);
- for (dane_based_mode = 0; dane_based_mode < 2; dane_based_mode++) {
- if (tlsp_client_init(&tls_params, &init_props,
- dane_based_mode) == 0)
- msg_warn("TLS client initialization failed");
- }
+ if (tlsp_client_init(&tls_params, &init_props) == 0)
+ msg_warn("TLS client initialization failed");
}
}
const char *(*lookup) (struct DICT *, const char *);
int (*update) (struct DICT *, const char *, const char *);
int (*delete) (struct DICT *, const char *);
-} DICT_UTF8_BACKUP;
+} DICT_UTF8_BACKUP;
extern DICT *dict_utf8_activate(DICT *);
/* VSTRING *result;
/* const char *in;
/* ssize_t len;
+/*
+/* VSTRING *hex_encode_opt(result, in, len, flags)
+/* VSTRING *result;
+/* const char *in;
+/* ssize_t len;
+/* int flags;
+/*
+/* VSTRING *hex_decode_opt(result, in, len, flags)
+/* VSTRING *result;
+/* const char *in;
+/* ssize_t len;
+/* int flags;
/* DESCRIPTION
/* hex_encode() takes a block of len bytes and encodes it as one
/* upper-case null-terminated string. The result value is
/* lower-case, upper-case or mixed-case input. The result
/* value is the result argument. The result is null terminated,
/* whether or not that makes sense.
+/*
+/* hex_encode_opt() enables extended functionality as controlled
+/* with \fIflags\fR.
+/* .IP HEX_ENCODE_FLAG_NONE
+/* The default: a self-documenting flag that enables no
+/* functionality.
+/* .IP HEX_ENCODE_FLAG_USE_COLON
+/* Inserts one ":" between bytes.
+/* .PP
+/* hex_decode_opt() enables extended functionality as controlled
+/* with \fIflags\fR.
+/* .IP HEX_DECODE_FLAG_NONE
+/* The default: a self-documenting flag that enables no
+/* functionality.
+/* .IP HEX_DECODE_FLAG_ALLOW_COLON
+/* Allows, but does not require, one ":" between bytes.
/* DIAGNOSTICS
/* hex_decode() returns a null pointer when the input contains
/* characters not in the hexadecimal alphabet.
/* IBM T.J. Watson Research
/* P.O. Box 704
/* Yorktown Heights, NY 10598, USA
+/*
+/* Wietse Venema
+/* Google, Inc.
+/* 111 8th Avenue
+/* New York, NY 10011, USA
/*--*/
/* System library. */
#define UCHAR_PTR(x) ((const unsigned char *)(x))
-/* hex_encode - raw data to encoded */
+/* hex_encode - ABI compatibility */
+
+#undef hex_encode
VSTRING *hex_encode(VSTRING *result, const char *in, ssize_t len)
+{
+ return (hex_encode_opt(result, in, len, HEX_ENCODE_FLAG_NONE));
+}
+
+/* hex_encode_opt - raw data to encoded */
+
+VSTRING *hex_encode_opt(VSTRING *result, const char *in, ssize_t len, int flags)
{
const unsigned char *cp;
int ch;
ch = *cp;
VSTRING_ADDCH(result, hex_chars[(ch >> 4) & 0xf]);
VSTRING_ADDCH(result, hex_chars[ch & 0xf]);
+ if ((flags & HEX_ENCODE_FLAG_USE_COLON) && count > 1)
+ VSTRING_ADDCH(result, ':');
}
VSTRING_TERMINATE(result);
return (result);
}
-/* hex_decode - encoded data to raw */
+/* hex_decode - ABI compatibility wrapper */
+
+#undef hex_decode
VSTRING *hex_decode(VSTRING *result, const char *in, ssize_t len)
+{
+ return (hex_decode_opt(result, in, len, HEX_DECODE_FLAG_NONE));
+}
+
+/* hex_decode_opt - encoded data to raw */
+
+VSTRING *hex_decode_opt(VSTRING *result, const char *in, ssize_t len, int flags)
{
const unsigned char *cp;
ssize_t count;
else
return (0);
VSTRING_ADDCH(result, bin);
+
+ /*
+ * Support *colon-separated* input (no leading or trailing colons).
+ * After decoding "xx", skip a possible ':' preceding "yy" in
+ * "xx:yy".
+ */
+ if ((flags & HEX_DECODE_FLAG_ALLOW_COLON)
+ && count > 4 && cp[2] == ':') {
+ ++cp;
+ --count;
+ }
}
VSTRING_TERMINATE(result);
return (result);
}
#ifdef TEST
+#include <argv.h>
/*
* Proof-of-concept test program: convert to hexadecimal and back.
VSTRING *b1 = vstring_alloc(1);
VSTRING *b2 = vstring_alloc(1);
char *test = "this is a test";
+ ARGV *argv;
#define DECODE(b,x,l) { \
if (hex_decode((b),(x),(l)) == 0) \
DECODE(b2, STR(b1), LEN(b1));
VERIFY(STR(b2), test);
+ hex_encode_opt(b1, test, strlen(test), HEX_ENCODE_FLAG_USE_COLON);
+ argv = argv_split(STR(b1), ":");
+ if (argv->argc != strlen(test))
+ msg_panic("HEX_ENCODE_FLAG_USE_COLON");
+ if (hex_decode_opt(b2, STR(b1), LEN(b1), HEX_DECODE_FLAG_ALLOW_COLON) == 0)
+ msg_panic("HEX_DECODE_FLAG_ALLOW_COLON");
+ VERIFY(STR(b2), test);
+ argv_free(argv);
+
vstring_free(b1);
vstring_free(b2);
return (0);
/*
* External interface.
*/
+#define HEX_ENCODE_FLAG_NONE (0)
+#define HEX_ENCODE_FLAG_USE_COLON (1<<0)
+
+#define HEX_DECODE_FLAG_NONE (0)
+#define HEX_DECODE_FLAG_ALLOW_COLON (1<<0)
+
extern VSTRING *hex_encode(VSTRING *, const char *, ssize_t);
extern VSTRING *WARN_UNUSED_RESULT hex_decode(VSTRING *, const char *, ssize_t);
+extern VSTRING *hex_encode_opt(VSTRING *, const char *, ssize_t, int);
+extern VSTRING *WARN_UNUSED_RESULT hex_decode_opt(VSTRING *, const char *, ssize_t, int);
+
+#define hex_encode(res, in, len) \
+ hex_encode_opt((res), (in), (len), HEX_ENCODE_FLAG_NONE)
+#define hex_decode(res, in, len) \
+ hex_decode_opt((res), (in), (len), HEX_DECODE_FLAG_NONE)
/* LICENSE
/* .ad
/* const char *str;
/* ssize_t len;
/*
-/* char *mymemdup(ptr, len)
-/* const char *ptr;
+/* void *mymemdup(ptr, len)
+/* const void *ptr;
/* ssize_t len;
/* DESCRIPTION
/* This module performs low-level memory management with error
/* mymemdup - copy memory */
-char *mymemdup(const void *ptr, ssize_t len)
+void *mymemdup(const void *ptr, ssize_t len)
{
if (ptr == 0)
msg_panic("mymemdup: null pointer argument");
extern void myfree(void *);
extern char *mystrdup(const char *);
extern char *mystrndup(const char *, ssize_t);
-extern char *mymemdup(const void *, ssize_t);
+extern void *mymemdup(const void *, ssize_t);
/* LICENSE
/* .ad