From: Wietse Venema Date: Mon, 20 Jul 2020 05:00:00 +0000 (-0500) Subject: postfix-3.6-20200720 X-Git-Tag: v3.6.0-RC1~28 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=bf6bdb562df0c82968a4522feba0ab9777bca4d8;p=thirdparty%2Fpostfix.git postfix-3.6-20200720 --- diff --git a/postfix/.indent.pro b/postfix/.indent.pro index 48b655e40..3f054f2ed 100644 --- a/postfix/.indent.pro +++ b/postfix/.indent.pro @@ -128,6 +128,7 @@ -TDICT_THASH -TDICT_UNION -TDICT_UNIX +-TDICT_UTF8_BACKUP -TDICT_WRAPPER -TDNS_FIXED -TDNS_REPLY @@ -143,6 +144,7 @@ -TEC_KEY -TEDIT_FILE -TEVENT_MASK +-TEVP_MD_CTX -TEVP_PKEY -TEXPAND_ATTR -TFILE @@ -402,6 +404,7 @@ -Tcipher_probe_t -Td2i_X509_t -Tdane_digest +-Tdane_mtype -Tfilter_ctx -Tgeneral_name_stack_t -Tiana_digest diff --git a/postfix/HISTORY b/postfix/HISTORY index 43dd50fc9..414a33065 100644 --- a/postfix/HISTORY +++ b/postfix/HISTORY @@ -24903,19 +24903,108 @@ Apologies for any names omitted. 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. diff --git a/postfix/README_FILES/COMPATIBILITY_README b/postfix/README_FILES/COMPATIBILITY_README index 20674ff6c..9a274a09a 100644 --- a/postfix/README_FILES/COMPATIBILITY_README +++ b/postfix/README_FILES/COMPATIBILITY_README @@ -42,6 +42,12 @@ The following messages may be logged: * 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. @@ -210,7 +216,7 @@ domain names. UUssiinngg bbaacckkwwaarrddss--ccoommppaattiibbllee ddeeffaauulltt sseettttiinngg ssmmttppuuttff88__eennaabbllee==nnoo -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 @@ -236,6 +242,70 @@ setting "smtputf8_enable = no" permanent in main.cf: # ppoossttccoonnff ssmmttppuuttff88__eennaabbllee==nnoo # ppoossttffiixx rreellooaadd +UUssiinngg bbaacckkwwaarrddss--ccoommppaattiibbllee ddeeffaauulltt sseettttiinngg ssmmttppdd__ttllss__ffiinnggeerrpprriinntt__ddiiggeesstt==mmdd55 + +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. + + # ppoossttccoonnff ssmmttppdd__ttllss__ffiinnggeerrpprriinntt__ddiiggeesstt==mmdd55 + # ppoossttffiixx rreellooaadd + +UUssiinngg bbaacckkwwaarrddss--ccoommppaattiibbllee ddeeffaauulltt sseettttiinngg ssmmttpp__ttllss__ffiinnggeerrpprriinntt__ddiiggeesstt==mmdd55 + +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. + + # ppoossttccoonnff ''ssmmttpp__ttllss__ffiinnggeerrpprriinntt__ddiiggeesstt == mmdd55'' \\ + ''llmmttpp__ttllss__ffiinnggeerrpprriinntt__ddiiggeesstt == mmdd55'' + # ppoossttffiixx rreellooaadd + TTuurrnniinngg ooffff tthhee bbaacckkwwaarrddss--ccoommppaattiibbiilliittyy ssaaffeettyy nneett Backwards compatibility is turned off by updating the compatibility_level diff --git a/postfix/README_FILES/TLS_README b/postfix/README_FILES/TLS_README index 05105ead4..57ebbea99 100644 --- a/postfix/README_FILES/TLS_README +++ b/postfix/README_FILES/TLS_README @@ -545,7 +545,12 @@ server access control: 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 sshhaa225566 with Postfix >= 3.6 and the ccoommppaattiibbiilliittyy__lleevveell set to 3 or +higher. With Postfix <= 3.5, the default algorithm is mmdd55. The best-practice +algorithm is now sshhaa225566. 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 @@ -596,26 +601,12 @@ command extracts the public key always in "PEM" format. We pipe the result to 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 SSeerrvveerr--ssiiddee cciipphheerr ccoonnttrroollss @@ -1130,6 +1121,14 @@ fingerprints can be combined with a "|" delimiter in a single match attribute, 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 sshhaa225566 with Postfix >= 3.6 and the ccoommppaattiibbiilliittyy__lleevveell +set to 3 or higher; with Postfix <= 3.5, the default algorithm is mmdd55. The +best-practice algorithm is now sshhaa225566. 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 @@ -1139,22 +1138,22 @@ trusted just prior to the transition. 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 @@ -1163,26 +1162,12 @@ command extracts the public key always in "PEM" format. We pipe the result to 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 MMaannddaattoorryy sseerrvveerr cceerrttiiffiiccaattee vveerriiffiiccaattiioonn @@ -1837,7 +1822,7 @@ Example: /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 @@ -1848,8 +1833,8 @@ Example: [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 diff --git a/postfix/RELEASE_NOTES b/postfix/RELEASE_NOTES index 3fc80718b..a6576c3b9 100644 --- a/postfix/RELEASE_NOTES +++ b/postfix/RELEASE_NOTES @@ -25,6 +25,20 @@ more recent Eclipse Public License 2.0. Recipients can choose to take the software under the license of their choice. Those who are more comfortable with the IPL can continue with that license. +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 ========================================== diff --git a/postfix/WISHLIST b/postfix/WISHLIST index 5f69e9fd2..44c37dcec 100644 --- a/postfix/WISHLIST +++ b/postfix/WISHLIST @@ -1,7 +1,10 @@ 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 diff --git a/postfix/conf/main.cf b/postfix/conf/main.cf index 9247ef7be..00a350659 100644 --- a/postfix/conf/main.cf +++ b/postfix/conf/main.cf @@ -27,7 +27,7 @@ # # The level below is what should be used with new (not upgrade) installs. # -compatibility_level = 2 +compatibility_level = 3 # SOFT BOUNCE # diff --git a/postfix/html/COMPATIBILITY_README.html b/postfix/html/COMPATIBILITY_README.html index d527e1981..30e9f2bcb 100644 --- a/postfix/html/COMPATIBILITY_README.html +++ b/postfix/html/COMPATIBILITY_README.html @@ -72,6 +72,15 @@ setting relay_domains=$mydestination

  • 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 @@ -327,7 +336,7 @@ explicit list of domain names.

    Using backwards-compatible default setting smtputf8_enable=no

    -

    The smtputf8_enable default value has changed from "no" to "yes. +

    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 @@ -366,6 +375,92 @@ in main.cf: +

    Using backwards-compatible +default setting smtpd_tls_fingerprint_digest=md5

    + +

    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. +

    + +
    +
    +# postconf smtpd_tls_fingerprint_digest=md5
    +# postfix reload
    +
    +
    + +

    Using backwards-compatible +default setting smtp_tls_fingerprint_digest=md5

    + +

    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. +

    + +
    +
    +# postconf 'smtp_tls_fingerprint_digest = md5' \
    +    'lmtp_tls_fingerprint_digest = md5' 
    +# postfix reload
    +
    +
    +

    Turning off the backwards-compatibility safety net

    Backwards compatibility is turned off by updating the diff --git a/postfix/html/TLS_README.html b/postfix/html/TLS_README.html index ed2dfe15f..4af1be12e 100644 --- a/postfix/html/TLS_README.html +++ b/postfix/html/TLS_README.html @@ -783,8 +783,14 @@ table.

    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.

    +parameter. The default algorithm is sha256 with Postfix ≥ +3.6 and the compatibility_level set to 3 or higher. With +Postfix ≤ 3.5, the default algorithm is md5. The +best-practice algorithm is now sha256. 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 @@ -847,30 +853,15 @@ key always in "PEM" format. We pipe the result to 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
    +    openssl dgst -sha256 -c
    +(stdin)= 64:3f:1f:f6:e5:1e:d4:2a:...:8b:fc:09:1a:61:98:b5:bc:7c:60:58
     
    -

    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.

    Server-side cipher controls

    @@ -1510,6 +1501,15 @@ 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 sha256 with Postfix ≥ 3.6 +and the compatibility_level set to 3 or higher; with Postfix +≤ 3.5, the default algorithm is md5. The +best-practice algorithm is now sha256. 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 @@ -1521,10 +1521,10 @@ another, and both keys are trusted just prior to the transition.

         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
     
    @@ -1534,15 +1534,15 @@ 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
     
    @@ -1554,30 +1554,15 @@ key always in "PEM" format. We pipe the result to 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
    +    openssl dgst -sha256 -c
    +(stdin)= 64:3f:1f:f6:e5:1e:d4:2a:56:...:09:1a:61:98:b5:bc:7c:60:58
     
    -

    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.

    Mandatory server certificate verification

    @@ -2400,7 +2385,7 @@ Example: /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 @@ -2411,8 +2396,8 @@ Example: [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 diff --git a/postfix/html/lmtp.8.html b/postfix/html/lmtp.8.html index f79f4e04e..3c20579ef 100644 --- a/postfix/html/lmtp.8.html +++ b/postfix/html/lmtp.8.html @@ -567,7 +567,7 @@ SMTP(8) SMTP(8) for the "fingerprint" TLS security level (smtp_tls_secu- rity_level = fingerprint). - smtp_tls_fingerprint_digest (md5) + smtp_tls_fingerprint_digest (see 'postconf -d' output) The message digest algorithm used to construct remote SMTP server certificate fingerprints. @@ -826,12 +826,12 @@ SMTP(8) SMTP(8) TROUBLE SHOOTING CONTROLS debug_peer_level (2) - 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 debug_peer_list parameter. debug_peer_list (empty) - 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. diff --git a/postfix/html/mailq.1.html b/postfix/html/mailq.1.html index 02f20fe89..a83307fc6 100644 --- a/postfix/html/mailq.1.html +++ b/postfix/html/mailq.1.html @@ -45,24 +45,27 @@ SENDMAIL(1) SENDMAIL(1) ery attempt will be made until the mail is taken off hold. - This mode of operation is implemented by executing the + # The message is forced to expire. See the postsuper(1) + options -e or -f. + + This mode of operation is implemented by executing the postqueue(1) command. newaliases - Initialize the alias database. If no input file is specified - (with the -oA option, see below), the program processes the - file(s) specified with the alias_database configuration parame- - ter. If no alias database type is specified, the program uses - the type specified with the default_database_type configuration + Initialize the alias database. If no input file is specified + (with the -oA option, see below), the program processes the + file(s) specified with the alias_database configuration parame- + ter. If no alias database type is specified, the program uses + the type specified with the default_database_type configuration parameter. This mode of operation is implemented by running the postalias(1) command. Note: it may take a minute or so before an alias database update - becomes visible. Use the "postfix reload" command to eliminate + becomes visible. Use the "postfix reload" 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 main.cf configuration file. The following options are recognized: @@ -70,13 +73,13 @@ SENDMAIL(1) SENDMAIL(1) -Am (ignored) -Ac (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 body_type The message body MIME type: 7BIT or 8BITMIME. - -bd Go into daemon mode. This mode of operation is implemented by + -bd Go into daemon mode. This mode of operation is implemented by executing the "postfix start" command. -bh (ignored) @@ -86,8 +89,8 @@ SENDMAIL(1) SENDMAIL(1) -bi Initialize alias database. See the newaliases command above. - -bl Go into daemon mode. To accept only local connections as with - Sendmail's -bl option, specify "inet_interfaces = loopback" in + -bl Go into daemon mode. To accept only local connections as with + Sendmail's -bl option, specify "inet_interfaces = loopback" in the Postfix main.cf configuration file. -bm Read mail from standard input and arrange for delivery. This is @@ -95,17 +98,17 @@ SENDMAIL(1) SENDMAIL(1) -bp List the mail queue. See the mailq command above. - -bs 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 + -bs 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 mail_owner user. - This mode of operation is implemented by running the smtpd(8) + This mode of operation is implemented by running the smtpd(8) daemon. - -bv Do not collect or deliver a message. Instead, send an email - report after verifying each recipient address. This is useful + -bv 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. @@ -113,58 +116,58 @@ SENDMAIL(1) SENDMAIL(1) -C config_file -C config_dir - The path name of the Postfix main.cf file, or of its parent - directory. This information is ignored with Postfix versions + The path name of the Postfix main.cf 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 main.cf file, through the alter- + be authorized in the default main.cf file, through the alter- nate_config_directories or multi_instance_directories 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. -F full_name - 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 From: mes- sage header. -f sender - 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 Errors-To: message header overrides the error return + the Errors-To: message header overrides the error return address. - -G 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 + -G 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 remote_header_rewrite_domain. This option is ignored before Postfix version 2.3. -h hop_count (ignored) - Hop count limit. Use the hopcount_limit configuration parameter + Hop count limit. Use the hopcount_limit configuration parameter instead. -I Initialize alias database. See the newaliases command above. - -i When reading a message from standard input, don't treat a line + -i When reading a message from standard input, don't treat a line with only a . character as the end of input. -L label (ignored) - The logging label. Use the syslog_name configuration parameter + The logging label. Use the syslog_name configuration parameter instead. -m (ignored) Backwards compatibility. -N dsn (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 failure (send notifica- - tion when delivery fails), delay (send notification when deliv- - ery is delayed), or success (send notification when the message + tion when delivery fails), delay (send notification when deliv- + ery is delayed), or success (send notification when the message is delivered); or specify never (don't send any notifications at all). @@ -174,50 +177,50 @@ SENDMAIL(1) SENDMAIL(1) Backwards compatibility. -oAalias_database - Non-default alias database. Specify pathname or type:pathname. + Non-default alias database. Specify pathname or type:pathname. See postalias(1) for details. -O option=value (ignored) - Set the named option to value. Use the equivalent configuration + Set the named option to value. Use the equivalent configuration parameter in main.cf instead. -o7 (ignored) -o8 (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 command-line option. - -oi When reading a message from standard input, don't treat a line + -oi When reading a message from standard input, don't treat a line with only a . character as the end of input. -om (ignored) The sender is never eliminated from alias etc. expansions. -o x value (ignored) - Set option x to value. Use the equivalent configuration parame- + Set option x to value. Use the equivalent configuration parame- ter in main.cf instead. -r sender - 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 Errors-To: message header overrides the error return + the Errors-To: message header overrides the error return address. -R return - 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 -R option specifies an upper bound; Postfix will return only - the header, when a full copy would exceed the bounce_size_limit + the header, when a full copy would exceed the bounce_size_limit setting. This option is ignored before Postfix version 2.10. - -q Attempt to deliver all queued mail. This is implemented by exe- + -q Attempt to deliver all queued mail. This is implemented by exe- cuting the postqueue(1) command. - Warning: flushing undeliverable mail frequently will result in + Warning: flushing undeliverable mail frequently will result in poor delivery performance of all other mail. -qinterval (ignored) @@ -226,21 +229,21 @@ SENDMAIL(1) SENDMAIL(1) -qIqueueid Schedule immediate delivery of mail with the specified queue ID. - This option is implemented by executing the postqueue(1) com- + This option is implemented by executing the postqueue(1) com- mand, and is available with Postfix version 2.4 and later. -qRsite - Schedule immediate delivery of all mail that is queued for the - named site. This option accepts only site 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 site. This option accepts only site names that are eligi- + ble for the "fast flush" service, and is implemented by execut- ing the postqueue(1) command. See flush(8) for more information about the "fast flush" service. -qSsite - This command is not implemented. Use the slower "sendmail -q" + This command is not implemented. Use the slower "sendmail -q" command instead. - -t Extract recipients from message headers. These are added to any + -t 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 @@ -256,23 +259,23 @@ SENDMAIL(1) SENDMAIL(1) This feature is available in Postfix 2.3 and later. -XV (Postfix 2.2 and earlier: -V) - Variable Envelope Return Path. Given an envelope sender address - of the form owner-listname@origin, each recipient user@domain + Variable Envelope Return Path. Given an envelope sender address + of the form owner-listname@origin, each recipient user@domain receives mail with a personalized envelope sender address. - By default, the personalized envelope sender address is - owner-listname+user=domain@origin. The default + and = charac- - ters are configurable with the default_verp_delimiters configu- + By default, the personalized envelope sender address is + owner-listname+user=domain@origin. The default + and = charac- + ters are configurable with the default_verp_delimiters configu- ration parameter. -XVxy (Postfix 2.2 and earlier: -Vxy) - As -XV, but uses x and y as the VERP delimiter characters, + As -XV, but uses x and y as the VERP delimiter characters, instead of the characters specified with the default_verp_delim- iters configuration parameter. -v 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 -v options are given, enable verbose log- + sions 2.1 and later). Mail delivery always happens in the back- + ground. When multiple -v options are given, enable verbose log- ging for debugging purposes. -X log_file (ignored) @@ -280,12 +283,42 @@ SENDMAIL(1) SENDMAIL(1) configuration parameters instead. SECURITY - 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. + + o 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. + + o Disable command options processing for all command arguments + that contain user-specified data. For example, the Postfix send- + mail(1) command line MUST be structured as follows: + + /path/to/sendmail system-arguments -- user-arguments + + Here, the "--" disables command option processing for all + user-arguments that follow. + + Without the "--", a malicious user could enable Postfix send- + mail(1) command options, by specifying an email address that + starts with "-". DIAGNOSTICS - Problems are logged to syslogd(8) or postlogd(8), and to the standard + Problems are logged to syslogd(8) or postlogd(8), and to the standard error stream. ENVIRONMENT @@ -299,12 +332,12 @@ SENDMAIL(1) SENDMAIL(1) Enable debugging with an external command, as specified with the debugger_command configuration parameter. - NAME The sender full name. This is used only with messages that have + NAME The sender full name. This is used only with messages that have no From: message header. See also the -F option above. CONFIGURATION PARAMETERS - The following main.cf parameters are especially relevant to this pro- - gram. The text below provides only a parameter summary. See post- + The following main.cf parameters are especially relevant to this pro- + gram. The text below provides only a parameter summary. See post- conf(5) for more details including examples. COMPATIBILITY CONTROLS @@ -315,7 +348,7 @@ SENDMAIL(1) SENDMAIL(1) line endings from <CR><LF> into UNIX format (<LF>). TROUBLE SHOOTING CONTROLS - The DEBUG_README file gives examples of how to troubleshoot a Postfix + The DEBUG_README file gives examples of how to troubleshoot a Postfix system. debugger_command (empty) @@ -323,13 +356,15 @@ SENDMAIL(1) SENDMAIL(1) invoked with the -D option. debug_peer_level (2) - 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 destina- + tion, remote client or server name or network address matches a + pattern given with the debug_peer_list parameter. debug_peer_list (empty) - 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 Available in Postfix version 2.2 and later: @@ -341,13 +376,13 @@ SENDMAIL(1) SENDMAIL(1) List of users who are authorized to view the queue. authorized_submit_users (static:anyone) - List of users who are authorized to submit mail with the send- + List of users who are authorized to submit mail with the send- mail(1) command (and with the privileged postdrop(1) helper com- mand). RESOURCE AND RATE CONTROLS bounce_size_limit (50000) - 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. fork_attempts (5) @@ -361,11 +396,11 @@ SENDMAIL(1) SENDMAIL(1) in the primary message headers. queue_run_delay (300s) - The time between deferred queue scans by the queue manager; + The time between deferred queue scans by the queue manager; prior to Postfix 2.4 the default value was 1000s. FAST FLUSH CONTROLS - The ETRN_README file describes configuration and operation details for + The ETRN_README file describes configuration and operation details for the Postfix "fast flush" service. fast_flush_domains ($relay_domains) @@ -373,26 +408,26 @@ SENDMAIL(1) SENDMAIL(1) tion logfiles with mail that is queued to those destinations. VERP CONTROLS - The VERP_README file describes configuration and operation details of + The VERP_README file describes configuration and operation details of Postfix support for variable envelope return path addresses. default_verp_delimiters (+=) The two default VERP delimiter characters. verp_delimiter_filter (-=+) - The characters Postfix accepts as VERP delimiter characters on + The characters Postfix accepts as VERP delimiter characters on the Postfix sendmail(1) command line and in SMTP commands. MISCELLANEOUS CONTROLS alias_database (see 'postconf -d' output) - The alias databases for local(8) delivery that are updated with + The alias databases for local(8) delivery that are updated with "newaliases" or with "sendmail -bi". command_directory (see 'postconf -d' output) The location of all postfix administrative commands. config_directory (see 'postconf -d' output) - The default location of the Postfix main.cf and master.cf con- + The default location of the Postfix main.cf and master.cf con- figuration files. daemon_directory (see 'postconf -d' output) @@ -403,46 +438,46 @@ SENDMAIL(1) SENDMAIL(1) and postmap(1) commands. delay_warning_time (0h) - 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. import_environment (see 'postconf -d' output) - 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. mail_owner (postfix) - 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. queue_directory (see 'postconf -d' output) The location of the Postfix top-level queue directory. remote_header_rewrite_domain (empty) - 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. syslog_facility (mail) The syslog facility of Postfix logging. syslog_name (see 'postconf -d' output) - 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: alternate_config_directories (empty) A list of non-default Postfix configuration directories that may - be specified with "-c config_directory" on the command line (in - the case of sendmail(1), with the "-C" option), or via the + be specified with "-c config_directory" on the command line (in + the case of sendmail(1), with the "-C" option), or via the MAIL_CONFIG environment parameter. multi_instance_directories (empty) - 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. FILES @@ -463,7 +498,7 @@ SENDMAIL(1) SENDMAIL(1) syslogd(8), system logging README_FILES - Use "postconf readme_directory" or "postconf html_directory" to locate + Use "postconf readme_directory" or "postconf html_directory" to locate this information. DEBUG_README, Postfix debugging howto ETRN_README, Postfix ETRN howto diff --git a/postfix/html/makedefs.1.html b/postfix/html/makedefs.1.html index 62b29d9f6..8129c30fc 100644 --- a/postfix/html/makedefs.1.html +++ b/postfix/html/makedefs.1.html @@ -4,7 +4,7 @@ Postfix manual - makedefs(1)
    -MAKEDEFS(1)                                                        MAKEDEFS(1)
    +MAKEDEFS(1)                 General Commands Manual                MAKEDEFS(1)
     
     NAME
            makedefs - Postfix makefile configuration utility
    @@ -110,45 +110,45 @@ MAKEDEFS(1)                                                        MAKEDEFS(1)
                          Disable support for POSIX getpwnam_r/getpwuid_r.
     
                   -DNO_RES_NCALLS
    -                     Do   not   build  with  the  threadsafe  resolver(5)  API
    +                     Do  not  build  with  the  threadsafe   resolver(5)   API
                          (res_ninit() etc.).
     
                   -DNO_SIGSETJMP
    -                     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.
     
                   -DNO_SNPRINTF
    -                     Use sprintf() instead of snprintf(). By default,  Postfix
    +                     Use  sprintf() instead of snprintf(). By default, Postfix
                          uses snprintf() except on ancient systems.
     
            DEBUG=debug_level
    -              Specifies  a  non-default  debugging  level.  The default is -g.
    +              Specifies a non-default debugging  level.  The  default  is  -g.
                   Specify DEBUG= to turn off debugging.
     
            OPT=optimization_level
    -              Specifies a non-default optimization level. The default  is  -O.
    +              Specifies  a  non-default optimization level. The default is -O.
                   Specify OPT= to turn off optimization.
     
            POSTFIX_INSTALL_OPTS=-option...
    -              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
                   -keep-build-mtime.
     
            SHLIB_CFLAGS=flags
    -              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.
     
            SHLIB_RPATH=rpath
    -              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.
     
            SHLIB_SUFFIX=suffix
    -              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.
    @@ -156,7 +156,7 @@ MAKEDEFS(1)                                                        MAKEDEFS(1)
            shared=yes
     
            shared=no
    -              Enable  (disable)   Postfix   builds   with   dynamically-linked
    +              Enable   (disable)   Postfix   builds   with  dynamically-linked
                   libraries typically named $shlib_directory/libpostfix-*.so.*.
     
                   This feature was introduced with Postfix 3.0.
    @@ -164,39 +164,39 @@ MAKEDEFS(1)                                                        MAKEDEFS(1)
            dynamicmaps=yes
     
            dynamicmaps=no
    -              Enable  (disable)  Postfix  builds  with  the configuration file
    +              Enable (disable) Postfix  builds  with  the  configuration  file
                   $meta_directory/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.
     
            pie=yes
     
    -       pie=no Enable  (disable)  Postfix builds with position-independent exe-
    +       pie=no Enable (disable) Postfix builds with  position-independent  exe-
                   cutables, on platforms where this is supported.
     
                   This feature was introduced with Postfix 3.0.
     
            installation_parameter=value...
    -              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:
     
    -              command_directory config_directory daemon_directory  data_direc-
    -              tory  default_database_type  html_directory mail_spool_directory
    -              mailq_path  manpage_directory   meta_directory   newaliases_path
    -              queue_directory  readme_directory  sendmail_path shlib_directory
    +              command_directory  config_directory daemon_directory data_direc-
    +              tory default_database_type  html_directory  mail_spool_directory
    +              mailq_path   manpage_directory   meta_directory  newaliases_path
    +              queue_directory readme_directory  sendmail_path  shlib_directory
                   openssl_path
     
    -              See the postconf(5) manpage for a description of  these  parame-
    +              See  the  postconf(5) manpage for a description of these parame-
                   ters.
     
                   This feature was introduced with Postfix 3.0.
     
            WARN=warning_flags
    -              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.
     
     LICENSE
    diff --git a/postfix/html/newaliases.1.html b/postfix/html/newaliases.1.html
    index 02f20fe89..a83307fc6 100644
    --- a/postfix/html/newaliases.1.html
    +++ b/postfix/html/newaliases.1.html
    @@ -45,24 +45,27 @@ SENDMAIL(1)                                                        SENDMAIL(1)
                          ery attempt will be made until  the  mail  is  taken  off
                          hold.
     
    -              This   mode   of  operation  is  implemented  by  executing  the
    +              #      The  message  is  forced  to expire. See the postsuper(1)
    +                     options -e or -f.
    +
    +              This  mode  of  operation  is  implemented  by   executing   the
                   postqueue(1) command.
     
            newaliases
    -              Initialize the alias database.  If no input  file  is  specified
    -              (with  the  -oA  option,  see  below), the program processes the
    -              file(s) specified with the alias_database configuration  parame-
    -              ter.   If  no alias database type is specified, the program uses
    -              the type specified with the default_database_type  configuration
    +              Initialize  the  alias  database.  If no input file is specified
    +              (with the -oA option, see  below),  the  program  processes  the
    +              file(s)  specified with the alias_database configuration parame-
    +              ter.  If no alias database type is specified, the  program  uses
    +              the  type specified with the default_database_type configuration
                   parameter.  This mode of operation is implemented by running the
                   postalias(1) command.
     
                   Note: it may take a minute or so before an alias database update
    -              becomes  visible.  Use the "postfix reload" command to eliminate
    +              becomes visible. Use the "postfix reload" 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 main.cf configuration file.
     
            The following options are recognized:
    @@ -70,13 +73,13 @@ SENDMAIL(1)                                                        SENDMAIL(1)
            -Am (ignored)
     
            -Ac (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 body_type
                   The message body MIME type: 7BIT or 8BITMIME.
     
    -       -bd    Go  into  daemon  mode. This mode of operation is implemented by
    +       -bd    Go into daemon mode. This mode of operation  is  implemented  by
                   executing the "postfix start" command.
     
            -bh (ignored)
    @@ -86,8 +89,8 @@ SENDMAIL(1)                                                        SENDMAIL(1)
     
            -bi    Initialize alias database. See the newaliases command above.
     
    -       -bl    Go into daemon mode. To accept only local  connections  as  with
    -              Sendmail's  -bl  option, specify "inet_interfaces = loopback" in
    +       -bl    Go  into  daemon  mode. To accept only local connections as with
    +              Sendmail's -bl option, specify "inet_interfaces =  loopback"  in
                   the Postfix main.cf configuration file.
     
            -bm    Read mail from standard input and arrange for delivery.  This is
    @@ -95,17 +98,17 @@ SENDMAIL(1)                                                        SENDMAIL(1)
     
            -bp    List the mail queue. See the mailq command above.
     
    -       -bs    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
    +       -bs    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
                   mail_owner user.
     
    -              This  mode  of  operation is implemented by running the smtpd(8)
    +              This mode of operation is implemented by  running  the  smtpd(8)
                   daemon.
     
    -       -bv    Do not collect or deliver a  message.  Instead,  send  an  email
    -              report  after  verifying each recipient address.  This is useful
    +       -bv    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.
    @@ -113,58 +116,58 @@ SENDMAIL(1)                                                        SENDMAIL(1)
            -C config_file
     
            -C config_dir
    -              The path name of the Postfix main.cf  file,  or  of  its  parent
    -              directory.  This  information  is  ignored with Postfix versions
    +              The  path  name  of  the  Postfix main.cf 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 main.cf file, through the alter-
    +              be authorized in the default main.cf file,  through  the  alter-
                   nate_config_directories  or  multi_instance_directories  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.
     
            -F full_name
    -              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 From: mes-
                   sage header.
     
            -f sender
    -              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   Errors-To:  message  header  overrides  the  error  return
    +              the  Errors-To:  message  header  overrides  the  error   return
                   address.
     
    -       -G     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
    +       -G     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
                   remote_header_rewrite_domain.
     
                   This option is ignored before Postfix version 2.3.
     
            -h hop_count (ignored)
    -              Hop  count limit. Use the hopcount_limit configuration parameter
    +              Hop count limit. Use the hopcount_limit configuration  parameter
                   instead.
     
            -I     Initialize alias database. See the newaliases command above.
     
    -       -i     When reading a message from standard input, don't treat  a  line
    +       -i     When  reading  a message from standard input, don't treat a line
                   with only a . character as the end of input.
     
            -L label (ignored)
    -              The  logging  label. Use the syslog_name configuration parameter
    +              The logging label. Use the syslog_name  configuration  parameter
                   instead.
     
            -m (ignored)
                   Backwards compatibility.
     
            -N dsn (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 failure (send notifica-
    -              tion when delivery fails), delay (send notification when  deliv-
    -              ery  is delayed), or success (send notification when the message
    +              tion  when delivery fails), delay (send notification when deliv-
    +              ery is delayed), or success (send notification when the  message
                   is delivered); or specify never (don't send any notifications at
                   all).
     
    @@ -174,50 +177,50 @@ SENDMAIL(1)                                                        SENDMAIL(1)
                   Backwards compatibility.
     
            -oAalias_database
    -              Non-default  alias  database. Specify pathname or type:pathname.
    +              Non-default alias database. Specify pathname  or  type:pathname.
                   See postalias(1) for details.
     
            -O option=value (ignored)
    -              Set the named option to value. Use the equivalent  configuration
    +              Set  the named option to value. Use the equivalent configuration
                   parameter in main.cf instead.
     
            -o7 (ignored)
     
            -o8 (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 command-line option.
     
    -       -oi    When reading a message from standard input, don't treat  a  line
    +       -oi    When  reading  a message from standard input, don't treat a line
                   with only a . character as the end of input.
     
            -om (ignored)
                   The sender is never eliminated from alias etc. expansions.
     
            -o x value (ignored)
    -              Set  option x to value. Use the equivalent configuration parame-
    +              Set option x to value. Use the equivalent configuration  parame-
                   ter in main.cf instead.
     
            -r sender
    -              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  Errors-To:  message  header  overrides  the  error   return
    +              the   Errors-To:  message  header  overrides  the  error  return
                   address.
     
            -R return
    -              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 -R option specifies an upper bound; Postfix will return only
    -              the header, when a full copy would exceed the  bounce_size_limit
    +              the  header, when a full copy would exceed the bounce_size_limit
                   setting.
     
                   This option is ignored before Postfix version 2.10.
     
    -       -q     Attempt  to deliver all queued mail. This is implemented by exe-
    +       -q     Attempt to deliver all queued mail. This is implemented by  exe-
                   cuting the postqueue(1) command.
     
    -              Warning: flushing undeliverable mail frequently will  result  in
    +              Warning:  flushing  undeliverable mail frequently will result in
                   poor delivery performance of all other mail.
     
            -qinterval (ignored)
    @@ -226,21 +229,21 @@ SENDMAIL(1)                                                        SENDMAIL(1)
     
            -qIqueueid
                   Schedule immediate delivery of mail with the specified queue ID.
    -              This  option  is  implemented by executing the postqueue(1) com-
    +              This option is implemented by executing  the  postqueue(1)  com-
                   mand, and is available with Postfix version 2.4 and later.
     
            -qRsite
    -              Schedule immediate delivery of all mail that is queued  for  the
    -              named  site. This option accepts only site 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 site. This option accepts only site names that are  eligi-
    +              ble  for the "fast flush" service, and is implemented by execut-
                   ing the postqueue(1) command.  See flush(8) for more information
                   about the "fast flush" service.
     
            -qSsite
    -              This command is not implemented. Use the  slower  "sendmail  -q"
    +              This  command  is  not implemented. Use the slower "sendmail -q"
                   command instead.
     
    -       -t     Extract  recipients from message headers. These are added to any
    +       -t     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
    @@ -256,23 +259,23 @@ SENDMAIL(1)                                                        SENDMAIL(1)
                   This feature is available in Postfix 2.3 and later.
     
            -XV (Postfix 2.2 and earlier: -V)
    -              Variable Envelope Return Path. Given an envelope sender  address
    -              of  the  form  owner-listname@origin, each recipient user@domain
    +              Variable  Envelope Return Path. Given an envelope sender address
    +              of the form owner-listname@origin,  each  recipient  user@domain
                   receives mail with a personalized envelope sender address.
     
    -              By  default,  the  personalized  envelope  sender   address   is
    -              owner-listname+user=domain@origin.  The  default + and = charac-
    -              ters are configurable with the default_verp_delimiters  configu-
    +              By   default,   the  personalized  envelope  sender  address  is
    +              owner-listname+user=domain@origin. The default + and  =  charac-
    +              ters  are configurable with the default_verp_delimiters configu-
                   ration parameter.
     
            -XVxy (Postfix 2.2 and earlier: -Vxy)
    -              As  -XV,  but  uses  x  and  y as the VERP delimiter characters,
    +              As -XV, but uses x and  y  as  the  VERP  delimiter  characters,
                   instead of the characters specified with the default_verp_delim-
                   iters configuration parameter.
     
            -v     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 -v options are given, enable verbose log-
    +              sions  2.1 and later). Mail delivery always happens in the back-
    +              ground. When multiple -v options are given, enable verbose  log-
                   ging for debugging purposes.
     
            -X log_file (ignored)
    @@ -280,12 +283,42 @@ SENDMAIL(1)                                                        SENDMAIL(1)
                   configuration parameters instead.
     
     SECURITY
    -       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.
    +
    +       o      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.
    +
    +       o      Disable  command  options  processing  for all command arguments
    +              that contain user-specified data. For example, the Postfix send-
    +              mail(1) command line MUST be structured as follows:
    +
    +                  /path/to/sendmail system-arguments -- user-arguments
    +
    +              Here,  the  "--"  disables  command  option  processing  for all
    +              user-arguments that follow.
    +
    +              Without the "--", a malicious user could  enable  Postfix  send-
    +              mail(1)  command  options,  by  specifying an email address that
    +              starts with "-".
     
     DIAGNOSTICS
    -       Problems  are  logged to syslogd(8) or postlogd(8), and to the standard
    +       Problems are logged to syslogd(8) or postlogd(8), and to  the  standard
            error stream.
     
     ENVIRONMENT
    @@ -299,12 +332,12 @@ SENDMAIL(1)                                                        SENDMAIL(1)
                   Enable debugging with an external command, as specified with the
                   debugger_command configuration parameter.
     
    -       NAME   The  sender full name. This is used only with messages that have
    +       NAME   The sender full name. This is used only with messages that  have
                   no From: message header. See also the -F option above.
     
     CONFIGURATION PARAMETERS
    -       The following main.cf parameters are especially relevant to  this  pro-
    -       gram.   The  text  below  provides  only a parameter summary. See post-
    +       The  following  main.cf parameters are especially relevant to this pro-
    +       gram.  The text below provides only  a  parameter  summary.  See  post-
            conf(5) for more details including examples.
     
     COMPATIBILITY CONTROLS
    @@ -315,7 +348,7 @@ SENDMAIL(1)                                                        SENDMAIL(1)
                   line endings from <CR><LF> into UNIX format (<LF>).
     
     TROUBLE SHOOTING CONTROLS
    -       The  DEBUG_README  file gives examples of how to troubleshoot a Postfix
    +       The DEBUG_README file gives examples of how to troubleshoot  a  Postfix
            system.
     
            debugger_command (empty)
    @@ -323,13 +356,15 @@ SENDMAIL(1)                                                        SENDMAIL(1)
                   invoked with the -D option.
     
            debug_peer_level (2)
    -              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  destina-
    +              tion,  remote client or server name or network address matches a
    +              pattern given with the debug_peer_list parameter.
     
            debug_peer_list (empty)
    -              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
            Available in Postfix version 2.2 and later:
    @@ -341,13 +376,13 @@ SENDMAIL(1)                                                        SENDMAIL(1)
                   List of users who are authorized to view the queue.
     
            authorized_submit_users (static:anyone)
    -              List of users who are authorized to submit mail with  the  send-
    +              List  of  users who are authorized to submit mail with the send-
                   mail(1) command (and with the privileged postdrop(1) helper com-
                   mand).
     
     RESOURCE AND RATE CONTROLS
            bounce_size_limit (50000)
    -              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.
     
            fork_attempts (5)
    @@ -361,11 +396,11 @@ SENDMAIL(1)                                                        SENDMAIL(1)
                   in the primary message headers.
     
            queue_run_delay (300s)
    -              The time between deferred queue  scans  by  the  queue  manager;
    +              The  time  between  deferred  queue  scans by the queue manager;
                   prior to Postfix 2.4 the default value was 1000s.
     
     FAST FLUSH CONTROLS
    -       The  ETRN_README file describes configuration and operation details for
    +       The ETRN_README file describes configuration and operation details  for
            the Postfix "fast flush" service.
     
            fast_flush_domains ($relay_domains)
    @@ -373,26 +408,26 @@ SENDMAIL(1)                                                        SENDMAIL(1)
                   tion logfiles with mail that is queued to those destinations.
     
     VERP CONTROLS
    -       The  VERP_README  file describes configuration and operation details of
    +       The VERP_README file describes configuration and operation  details  of
            Postfix support for variable envelope return path addresses.
     
            default_verp_delimiters (+=)
                   The two default VERP delimiter characters.
     
            verp_delimiter_filter (-=+)
    -              The characters Postfix accepts as VERP delimiter  characters  on
    +              The  characters  Postfix accepts as VERP delimiter characters on
                   the Postfix sendmail(1) command line and in SMTP commands.
     
     MISCELLANEOUS CONTROLS
            alias_database (see 'postconf -d' output)
    -              The  alias databases for local(8) delivery that are updated with
    +              The alias databases for local(8) delivery that are updated  with
                   "newaliases" or with "sendmail -bi".
     
            command_directory (see 'postconf -d' output)
                   The location of all postfix administrative commands.
     
            config_directory (see 'postconf -d' output)
    -              The default location of the Postfix main.cf and  master.cf  con-
    +              The  default  location of the Postfix main.cf and master.cf con-
                   figuration files.
     
            daemon_directory (see 'postconf -d' output)
    @@ -403,46 +438,46 @@ SENDMAIL(1)                                                        SENDMAIL(1)
                   and postmap(1) commands.
     
            delay_warning_time (0h)
    -              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.
     
            import_environment (see 'postconf -d' output)
    -              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.
     
            mail_owner (postfix)
    -              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.
     
            queue_directory (see 'postconf -d' output)
                   The location of the Postfix top-level queue directory.
     
            remote_header_rewrite_domain (empty)
    -              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.
     
            syslog_facility (mail)
                   The syslog facility of Postfix logging.
     
            syslog_name (see 'postconf -d' output)
    -              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:
     
            alternate_config_directories (empty)
                   A list of non-default Postfix configuration directories that may
    -              be  specified with "-c config_directory" on the command line (in
    -              the case of sendmail(1), with  the  "-C"  option),  or  via  the
    +              be specified with "-c config_directory" on the command line  (in
    +              the  case  of  sendmail(1),  with  the  "-C" option), or via the
                   MAIL_CONFIG environment parameter.
     
            multi_instance_directories (empty)
    -              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.
     
     FILES
    @@ -463,7 +498,7 @@ SENDMAIL(1)                                                        SENDMAIL(1)
            syslogd(8), system logging
     
     README_FILES
    -       Use  "postconf readme_directory" or "postconf html_directory" to locate
    +       Use "postconf readme_directory" or "postconf html_directory" to  locate
            this information.
            DEBUG_README, Postfix debugging howto
            ETRN_README, Postfix ETRN howto
    diff --git a/postfix/html/postconf.5.html b/postfix/html/postconf.5.html
    index a29f9223e..13d621cc6 100644
    --- a/postfix/html/postconf.5.html
    +++ b/postfix/html/postconf.5.html
    @@ -5354,7 +5354,7 @@ configuration parameter.  See there for details. 

    lmtp_tls_fingerprint_digest -(default: md5)
    +(default: see "postconf -d" output)

    The LMTP-specific version of the smtp_tls_fingerprint_digest configuration parameter. See there for details.

    @@ -5721,6 +5721,15 @@ The fingerprint digest algorithm is configurable via the smtpd_tls_fingerprint_digest parameter (hard-coded as md5 prior to Postfix version 2.5).
    +
    The default algorithm is sha256 with Postfix ≥ 3.6 +and the compatibility_level set to 3 or higher. With Postfix +≤ 3.5, the default algorithm is md5. The best-practice +algorithm is now sha256. 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.
    +
    permit_tls_all_clientcerts
    Append the domain name in $myorigin or $mydomain when the @@ -9560,6 +9569,15 @@ feature. The fingerprint digest algorithm is configurable via the smtpd_tls_fingerprint_digest parameter (hard-coded as md5 prior to Postfix version 2.5).

    +

    The default algorithm is sha256 with Postfix ≥ 3.6 +and the compatibility_level set to 3 or higher. With Postfix +≤ 3.5, the default algorithm is md5. The best-practice +algorithm is now sha256. 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.

    +

    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: @@ -9575,10 +9593,6 @@ D7:04:2F:A7:0B:8C:A5:21:FA:31:77:E1:41:8A:EE:80 lutzpc.at.home

    an appropriate access(5) policy for each client. See RESTRICTION_CLASS_README.

    -

    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.

    -

    This feature is available with Postfix version 2.2.

    @@ -12573,6 +12587,10 @@ is a message digest of the server certificate (or public key). The digest algorithm is selected via the smtp_tls_fingerprint_digest parameter.

    +

    The colons between each pair of nibbles in the fingerprint value +are optional (Postfix ≥ 3.6). These were required in earlier +Postfix releases.

    +

    When an smtp_tls_policy_maps table entry specifies the "fingerprint" security level, any "match" attributes in that entry specify the list of valid fingerprints for the corresponding destination. Multiple @@ -12590,10 +12608,10 @@ another, and both keys are trusted just prior to the transition.

     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
     
    @@ -12604,7 +12622,7 @@ 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
     
    @@ -12612,8 +12630,8 @@ As in the example above, we show two matching fingerprints:

     /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
     
    @@ -12623,7 +12641,7 @@ As in the example above, we show two matching fingerprints:

    smtp_tls_fingerprint_digest -(default: md5)
    +(default: see "postconf -d" output)

    The message digest algorithm used to construct remote SMTP server certificate fingerprints. At the "fingerprint" TLS security level @@ -12636,19 +12654,19 @@ algorithm. With a digest algorithm resistant to "second pre-image" 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.

    -

    The default algorithm is md5; this is consistent with -the backwards compatible setting of the digest used to verify client -certificates in the SMTP server.

    +

    The default algorithm is sha256 with Postfix ≥ 3.6 +and the compatibility_level set to 3 or higher. With Postfix +≤ 3.5, the default algorithm is md5.

    -

    The best practice algorithm is now sha1. 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 sha256. 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.

    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.

    To find the fingerprint of a specific certificate file, with a specific digest algorithm, run: @@ -12665,8 +12683,8 @@ For example:

    -$ 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
     
    @@ -12678,29 +12696,16 @@ key always in "PEM" format. We pipe the result to 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.

    +

    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.

    -# 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
    -
    -
    - -
    -
    -# 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
     
    @@ -12708,10 +12713,6 @@ $ openssl x509 -in cert.pem -noout -pubkey | fingerprint and public key fingerprint when the TLS loglevel is 2 or higher.

    -

    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.

    -

    This feature is available in Postfix 2.5 and later.

    @@ -13204,8 +13205,12 @@ Example:
     /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
     
    @@ -13219,8 +13224,8 @@ Example:
         [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
     

    Note: The hostname strategy if listed in a non-default @@ -14249,7 +14254,14 @@ Postfix version 2.5). This feature requires " -
    +

    The default algorithm is sha256 with Postfix ≥ 3.6 +and the compatibility_level set to 3 or higher. With Postfix +≤ 3.5, the default algorithm is md5. The best-practice +algorithm is now sha256. 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.
    Alternatively, check_ccert_access accepts an explicit search order (Postfix 3.5 and later). The default search order as described @@ -14371,6 +14383,15 @@ The fingerprint digest algorithm is configurable via the Postfix version 2.5). This feature requires "smtpd_tls_ask_ccert = yes" and is available with Postfix version 2.2 and later.
    +
    The default algorithm is sha256 with Postfix ≥ 3.6 +and the compatibility_level set to 3 or higher. With Postfix +≤ 3.5, the default algorithm is md5. The best-practice +algorithm is now sha256. 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.
    +
    reject_rbl_client rbl_domain=d.d.d.d
    Reject the request when the reversed client network address is @@ -17174,15 +17195,24 @@ The DSA algorithm is obsolete and should not be used.

    File with DH parameters that the Postfix SMTP server should use with non-export EDH ciphers.

    +

    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.

    +

    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:

    -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
     
    @@ -17220,6 +17250,9 @@ grade is "medium" with Postfix releases after the middle of 2015, and as a result export-grade cipher suites are by default not used.

    +

    With Postfix ≥ 3.6 export-grade Diffie-Hellman key exchange +is no longer supported, and this parameter is silently ignored.

    +

    See also the discussion under the smtpd_tls_dh1024_param_file configuration parameter.

    @@ -17298,32 +17331,32 @@ compiled and linked with OpenSSL 1.0.0 or later.

    (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 +auto value (described below) was chosen. +

    The available choices are:

    +
    auto
    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).
    +
    none
    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.
    -
    strong
    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. -
    +
    strong
    Use EECDH with approximately 128 bits of +security at a reasonable computational cost. This is the default in +Postfix versions 2.8–3.5.
    ultra
    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.
    - -
    auto
    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.
    @@ -17380,24 +17413,25 @@ key exchange with RSA authentication.

    smtpd_tls_fingerprint_digest -(default: md5)
    +(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 check_ccert_access and permit_tls_clientcerts. The -default algorithm is md5, for backwards compatibility with Postfix -releases prior to 2.5.

    +

    The message digest algorithm to construct remote SMTP client-certificate +fingerprints or public key fingerprints (Postfix 2.9 and later) for +check_ccert_access and permit_tls_clientcerts.

    -

    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 default algorithm is sha256 with Postfix ≥ 3.6 +and the compatibility_level set to 3 or higher. With Postfix +≤ 3.5, the default algorithm is md5.

    + +

    The best-practice algorithm is now sha256. 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.

    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.

    To find the fingerprint of a specific certificate file, with a specific digest algorithm, run:

    @@ -17413,8 +17447,8 @@ For example:

    -$ 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
     
    @@ -17426,46 +17460,26 @@ key always in "PEM" format. We pipe the result to 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
    +    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
     
    -
    -
    -# 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
    -
    -
    -

    The Postfix SMTP server and client log the peer (leaf) certificate fingerprint and public key fingerprint when the TLS loglevel is 2 or higher.

    -

    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.

    - -

    Example: client-certificate access table, with sha1 fingerprints:

    +

    Example: client-certificate access table, with sha256 fingerprints:

     /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
    @@ -17473,9 +17487,9 @@ to Postfix 2.9.6 or later. 

     /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
     
    @@ -19423,6 +19437,9 @@ for further details. The default SMTP server cipher grade is "medium" with Postfix releases after the middle of 2015, and as a result export-grade cipher suites are by default not used.

    +

    With Postfix ≥ 3.6 export-grade Diffie-Hellman key exchange +is no longer supported, and this parameter is silently ignored.

    +

    This feature is available in Postfix 2.8 and later.

    diff --git a/postfix/html/posttls-finger.1.html b/postfix/html/posttls-finger.1.html index 4a47a4818..fd9d83baa 100644 --- a/postfix/html/posttls-finger.1.html +++ b/postfix/html/posttls-finger.1.html @@ -4,7 +4,7 @@ Postfix manual - posttls-finger(1)
    -POSTTLS-FINGER(1)                                            POSTTLS-FINGER(1)
    +POSTTLS-FINGER(1)           General Commands Manual          POSTTLS-FINGER(1)
     
     NAME
            posttls-finger - Probe the TLS properties of an ESMTP or LMTP server.
    @@ -104,77 +104,78 @@ POSTTLS-FINGER(1)                                            POSTTLS-FINGER(1)
                   tificates.  To see the actual chain  sent  by  the  remote  SMTP
                   server leave CAfile and CApath unset.
     
    -       -d mdalg (default: sha1)
    +       -d mdalg (default: $smtp_tls_fingerprint_digest)
                   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".
     
    -       -f     Lookup the associated DANE TLSA RRset even when  a  hostname  is
    -              not  an  alias  and its address records lie in an unsigned zone.
    +       -f     Lookup  the  associated  DANE TLSA RRset even when a hostname is
    +              not an alias and its address records lie in  an  unsigned  zone.
                   See smtp_tls_force_insecure_host_tlsa_lookup for details.
     
            -F CAfile.pem (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.
     
            -g grade (default: medium)
    -              The minimum  TLS  cipher  grade  used  by  posttls-finger.   See
    +              The  minimum  TLS  cipher  grade  used  by  posttls-finger.  See
                   smtp_tls_mandatory_ciphers for details.
     
            -h host_lookup (default: dns)
    -              The  hostname  lookup  methods used for the connection.  See the
    +              The hostname lookup methods used for the  connection.   See  the
                   documentation of smtp_host_lookup for syntax and semantics.
     
            -H chainfiles (default: none)
                   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 -k and -K
    +              This option is mutually exclusive  with  the  below  -k  and  -K
                   options.
     
            -k certfile (default: keyfile)
    -              File  with  PEM-encoded  TLS  client  certificate  chain.   This
    +              File   with  PEM-encoded  TLS  client  certificate  chain.  This
                   defaults to keyfile if one is specified.
     
            -K keyfile (default: certfile)
    -              File  with PEM-encoded TLS client private key.  This defaults to
    +              File with PEM-encoded TLS client private key.  This defaults  to
                   certfile if one is specified.
     
            -l level (default: dane or secure)
    -              The security level for the connection, default  dane  or  secure
    +              The  security  level  for the connection, default dane or secure
                   depending on whether DNSSEC is available.  For syntax and seman-
    -              tics, see the documentation  of  smtp_tls_security_level.   When
    -              dane  or dane-only is supported and selected, if no TLSA records
    -              are found, or all the records found  are  unusable,  the  secure
    -              level  will  be  used  instead.   The fingerprint security level
    +              tics,  see  the  documentation of smtp_tls_security_level.  When
    +              dane or dane-only is supported and selected, if no TLSA  records
    +              are  found,  or  all  the records found are unusable, the secure
    +              level will be used  instead.   The  fingerprint  security  level
                   allows you to test certificate or public-key fingerprint matches
                   before you deploy them in the policy table.
     
    -              Note,  since posttls-finger does not actually deliver any email,
    -              the none, may and encrypt security levels are not  very  useful.
    +              Note, since posttls-finger does not actually deliver any  email,
    +              the  none,  may and encrypt security levels are not very useful.
                   Since may and encrypt 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).
     
            -L logopts (default: routine,certmatch)
    -              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:
     
                   0, none
    -                     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
     
                   1, routine, summary
    -                     These  synonymous  values yield a normal one-line summary
    +                     These synonymous values yield a normal  one-line  summary
                          of the TLS connection.
     
                   2, debug
    @@ -182,104 +183,104 @@ POSTTLS-FINGER(1)                                            POSTTLS-FINGER(1)
                          and verbose.
     
                   3, ssl-expert
    -                     These  synonymous  values  combine  debug  with ssl-hand-
    +                     These synonymous  values  combine  debug  with  ssl-hand-
                          shake-packet-dump.  For experts only.
     
                   4, ssl-developer
    -                     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.
     
                   ssl-debug
    -                     Turn on OpenSSL logging of the progress of the SSL  hand-
    +                     Turn  on OpenSSL logging of the progress of the SSL hand-
                          shake.
     
                   ssl-handshake-packet-dump
    -                     Log  hexadecimal  packet  dumps of the SSL handshake; for
    +                     Log hexadecimal packet dumps of the  SSL  handshake;  for
                          experts only.
     
                   ssl-session-packet-dump
    -                     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.
     
                   untrusted
    -                     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.
     
                   peercert
    -                     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.
     
                   certmatch
    -                     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.
     
    -              cache  This logs session cache operations, showing whether  ses-
    -                     sion  caching  is  effective with the remote SMTP server.
    -                     Automatically used when reconnecting with the -r  option;
    +              cache  This  logs session cache operations, showing whether ses-
    +                     sion caching is effective with the  remote  SMTP  server.
    +                     Automatically  used when reconnecting with the -r option;
                          rarely needs to be set explicitly.
     
                   verbose
                          Enables  verbose  logging  in  the  Postfix  TLS  driver;
                          includes all of peercert..cache and more.
     
    -              The default is routine,certmatch. After a  reconnect,  peercert,
    +              The  default  is routine,certmatch. After a reconnect, peercert,
                   certmatch and verbose are automatically disabled while cache and
                   summary are enabled.
     
            -m count (default: 5)
    -              When the -r delay option is specified, the -m 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 -r delay option is specified, the -m 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.
     
            -M insecure_mx_policy (default: dane)
    -              The TLS policy for MX hosts with "secure" TLSA records when  the
    -              nexthop  destination  security  level is dane, but the MX record
    +              The  TLS policy for MX hosts with "secure" TLSA records when the
    +              nexthop destination security level is dane, but  the  MX  record
                   was found via an "insecure" MX lookup.  See the main.cf documen-
                   tation for smtp_tls_insecure_mx_policy for details.
     
            -o name=value
    -              Specify  zero or more times to override the value of the main.cf
    -              parameter name with value.  Possible use-cases include  overrid-
    -              ing  the  values  of  TLS library parameters, or "myhostname" to
    +              Specify zero or more times to override the value of the  main.cf
    +              parameter  name with value.  Possible use-cases include overrid-
    +              ing the values of TLS library  parameters,  or  "myhostname"  to
                   configure the SMTP EHLO name sent to the remote server.
     
            -p protocols (default: !SSLv2)
    -              List of  TLS  protocols  that  posttls-finger  will  exclude  or
    +              List  of  TLS  protocols  that  posttls-finger  will  exclude or
                   include.  See smtp_tls_mandatory_protocols for details.
     
            -P CApath/ (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.
     
            -r delay
    -              With  a  cacheable  TLS  session, disconnect and reconnect after
    +              With a cacheable TLS session,  disconnect  and  reconnect  after
                   delay seconds. Report whether the session is re-used. Retry if a
    -              new  server  is  encountered, up to 5 times or as specified with
    -              the -m option.  By default reconnection is disabled,  specify  a
    +              new server is encountered, up to 5 times or  as  specified  with
    +              the  -m  option.  By default reconnection is disabled, specify a
                   positive delay to enable this behavior.
     
            -s servername
    -              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.
     
    -       -S     Disable  SMTP;  that  is, connect to an LMTP server. The default
    -              port for LMTP over TCP is 24.  Alternative ports  can  specified
    -              by  appending ":servicename" or ":portnumber" to the destination
    +       -S     Disable SMTP; that is, connect to an LMTP  server.  The  default
    +              port  for  LMTP over TCP is 24.  Alternative ports can specified
    +              by appending ":servicename" or ":portnumber" to the  destination
                   argument.
     
            -t timeout (default: 30)
    @@ -287,7 +288,7 @@ POSTTLS-FINGER(1)                                            POSTTLS-FINGER(1)
                   reading the remote server's 220 banner.
     
            -T timeout (default: 30)
    -              The  SMTP/LMTP command timeout for EHLO/LHLO, STARTTLS and QUIT.
    +              The SMTP/LMTP command timeout for EHLO/LHLO, STARTTLS and QUIT.
     
            -v     Enable verbose Postfix  logging.   Specify  more  than  once  to
                   increase the level of verbose logging.
    diff --git a/postfix/html/sendmail.1.html b/postfix/html/sendmail.1.html
    index 02f20fe89..a83307fc6 100644
    --- a/postfix/html/sendmail.1.html
    +++ b/postfix/html/sendmail.1.html
    @@ -45,24 +45,27 @@ SENDMAIL(1)                                                        SENDMAIL(1)
                          ery attempt will be made until  the  mail  is  taken  off
                          hold.
     
    -              This   mode   of  operation  is  implemented  by  executing  the
    +              #      The  message  is  forced  to expire. See the postsuper(1)
    +                     options -e or -f.
    +
    +              This  mode  of  operation  is  implemented  by   executing   the
                   postqueue(1) command.
     
            newaliases
    -              Initialize the alias database.  If no input  file  is  specified
    -              (with  the  -oA  option,  see  below), the program processes the
    -              file(s) specified with the alias_database configuration  parame-
    -              ter.   If  no alias database type is specified, the program uses
    -              the type specified with the default_database_type  configuration
    +              Initialize  the  alias  database.  If no input file is specified
    +              (with the -oA option, see  below),  the  program  processes  the
    +              file(s)  specified with the alias_database configuration parame-
    +              ter.  If no alias database type is specified, the  program  uses
    +              the  type specified with the default_database_type configuration
                   parameter.  This mode of operation is implemented by running the
                   postalias(1) command.
     
                   Note: it may take a minute or so before an alias database update
    -              becomes  visible.  Use the "postfix reload" command to eliminate
    +              becomes visible. Use the "postfix reload" 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 main.cf configuration file.
     
            The following options are recognized:
    @@ -70,13 +73,13 @@ SENDMAIL(1)                                                        SENDMAIL(1)
            -Am (ignored)
     
            -Ac (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 body_type
                   The message body MIME type: 7BIT or 8BITMIME.
     
    -       -bd    Go  into  daemon  mode. This mode of operation is implemented by
    +       -bd    Go into daemon mode. This mode of operation  is  implemented  by
                   executing the "postfix start" command.
     
            -bh (ignored)
    @@ -86,8 +89,8 @@ SENDMAIL(1)                                                        SENDMAIL(1)
     
            -bi    Initialize alias database. See the newaliases command above.
     
    -       -bl    Go into daemon mode. To accept only local  connections  as  with
    -              Sendmail's  -bl  option, specify "inet_interfaces = loopback" in
    +       -bl    Go  into  daemon  mode. To accept only local connections as with
    +              Sendmail's -bl option, specify "inet_interfaces =  loopback"  in
                   the Postfix main.cf configuration file.
     
            -bm    Read mail from standard input and arrange for delivery.  This is
    @@ -95,17 +98,17 @@ SENDMAIL(1)                                                        SENDMAIL(1)
     
            -bp    List the mail queue. See the mailq command above.
     
    -       -bs    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
    +       -bs    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
                   mail_owner user.
     
    -              This  mode  of  operation is implemented by running the smtpd(8)
    +              This mode of operation is implemented by  running  the  smtpd(8)
                   daemon.
     
    -       -bv    Do not collect or deliver a  message.  Instead,  send  an  email
    -              report  after  verifying each recipient address.  This is useful
    +       -bv    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.
    @@ -113,58 +116,58 @@ SENDMAIL(1)                                                        SENDMAIL(1)
            -C config_file
     
            -C config_dir
    -              The path name of the Postfix main.cf  file,  or  of  its  parent
    -              directory.  This  information  is  ignored with Postfix versions
    +              The  path  name  of  the  Postfix main.cf 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 main.cf file, through the alter-
    +              be authorized in the default main.cf file,  through  the  alter-
                   nate_config_directories  or  multi_instance_directories  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.
     
            -F full_name
    -              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 From: mes-
                   sage header.
     
            -f sender
    -              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   Errors-To:  message  header  overrides  the  error  return
    +              the  Errors-To:  message  header  overrides  the  error   return
                   address.
     
    -       -G     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
    +       -G     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
                   remote_header_rewrite_domain.
     
                   This option is ignored before Postfix version 2.3.
     
            -h hop_count (ignored)
    -              Hop  count limit. Use the hopcount_limit configuration parameter
    +              Hop count limit. Use the hopcount_limit configuration  parameter
                   instead.
     
            -I     Initialize alias database. See the newaliases command above.
     
    -       -i     When reading a message from standard input, don't treat  a  line
    +       -i     When  reading  a message from standard input, don't treat a line
                   with only a . character as the end of input.
     
            -L label (ignored)
    -              The  logging  label. Use the syslog_name configuration parameter
    +              The logging label. Use the syslog_name  configuration  parameter
                   instead.
     
            -m (ignored)
                   Backwards compatibility.
     
            -N dsn (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 failure (send notifica-
    -              tion when delivery fails), delay (send notification when  deliv-
    -              ery  is delayed), or success (send notification when the message
    +              tion  when delivery fails), delay (send notification when deliv-
    +              ery is delayed), or success (send notification when the  message
                   is delivered); or specify never (don't send any notifications at
                   all).
     
    @@ -174,50 +177,50 @@ SENDMAIL(1)                                                        SENDMAIL(1)
                   Backwards compatibility.
     
            -oAalias_database
    -              Non-default  alias  database. Specify pathname or type:pathname.
    +              Non-default alias database. Specify pathname  or  type:pathname.
                   See postalias(1) for details.
     
            -O option=value (ignored)
    -              Set the named option to value. Use the equivalent  configuration
    +              Set  the named option to value. Use the equivalent configuration
                   parameter in main.cf instead.
     
            -o7 (ignored)
     
            -o8 (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 command-line option.
     
    -       -oi    When reading a message from standard input, don't treat  a  line
    +       -oi    When  reading  a message from standard input, don't treat a line
                   with only a . character as the end of input.
     
            -om (ignored)
                   The sender is never eliminated from alias etc. expansions.
     
            -o x value (ignored)
    -              Set  option x to value. Use the equivalent configuration parame-
    +              Set option x to value. Use the equivalent configuration  parame-
                   ter in main.cf instead.
     
            -r sender
    -              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  Errors-To:  message  header  overrides  the  error   return
    +              the   Errors-To:  message  header  overrides  the  error  return
                   address.
     
            -R return
    -              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 -R option specifies an upper bound; Postfix will return only
    -              the header, when a full copy would exceed the  bounce_size_limit
    +              the  header, when a full copy would exceed the bounce_size_limit
                   setting.
     
                   This option is ignored before Postfix version 2.10.
     
    -       -q     Attempt  to deliver all queued mail. This is implemented by exe-
    +       -q     Attempt to deliver all queued mail. This is implemented by  exe-
                   cuting the postqueue(1) command.
     
    -              Warning: flushing undeliverable mail frequently will  result  in
    +              Warning:  flushing  undeliverable mail frequently will result in
                   poor delivery performance of all other mail.
     
            -qinterval (ignored)
    @@ -226,21 +229,21 @@ SENDMAIL(1)                                                        SENDMAIL(1)
     
            -qIqueueid
                   Schedule immediate delivery of mail with the specified queue ID.
    -              This  option  is  implemented by executing the postqueue(1) com-
    +              This option is implemented by executing  the  postqueue(1)  com-
                   mand, and is available with Postfix version 2.4 and later.
     
            -qRsite
    -              Schedule immediate delivery of all mail that is queued  for  the
    -              named  site. This option accepts only site 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 site. This option accepts only site names that are  eligi-
    +              ble  for the "fast flush" service, and is implemented by execut-
                   ing the postqueue(1) command.  See flush(8) for more information
                   about the "fast flush" service.
     
            -qSsite
    -              This command is not implemented. Use the  slower  "sendmail  -q"
    +              This  command  is  not implemented. Use the slower "sendmail -q"
                   command instead.
     
    -       -t     Extract  recipients from message headers. These are added to any
    +       -t     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
    @@ -256,23 +259,23 @@ SENDMAIL(1)                                                        SENDMAIL(1)
                   This feature is available in Postfix 2.3 and later.
     
            -XV (Postfix 2.2 and earlier: -V)
    -              Variable Envelope Return Path. Given an envelope sender  address
    -              of  the  form  owner-listname@origin, each recipient user@domain
    +              Variable  Envelope Return Path. Given an envelope sender address
    +              of the form owner-listname@origin,  each  recipient  user@domain
                   receives mail with a personalized envelope sender address.
     
    -              By  default,  the  personalized  envelope  sender   address   is
    -              owner-listname+user=domain@origin.  The  default + and = charac-
    -              ters are configurable with the default_verp_delimiters  configu-
    +              By   default,   the  personalized  envelope  sender  address  is
    +              owner-listname+user=domain@origin. The default + and  =  charac-
    +              ters  are configurable with the default_verp_delimiters configu-
                   ration parameter.
     
            -XVxy (Postfix 2.2 and earlier: -Vxy)
    -              As  -XV,  but  uses  x  and  y as the VERP delimiter characters,
    +              As -XV, but uses x and  y  as  the  VERP  delimiter  characters,
                   instead of the characters specified with the default_verp_delim-
                   iters configuration parameter.
     
            -v     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 -v options are given, enable verbose log-
    +              sions  2.1 and later). Mail delivery always happens in the back-
    +              ground. When multiple -v options are given, enable verbose  log-
                   ging for debugging purposes.
     
            -X log_file (ignored)
    @@ -280,12 +283,42 @@ SENDMAIL(1)                                                        SENDMAIL(1)
                   configuration parameters instead.
     
     SECURITY
    -       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.
    +
    +       o      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.
    +
    +       o      Disable  command  options  processing  for all command arguments
    +              that contain user-specified data. For example, the Postfix send-
    +              mail(1) command line MUST be structured as follows:
    +
    +                  /path/to/sendmail system-arguments -- user-arguments
    +
    +              Here,  the  "--"  disables  command  option  processing  for all
    +              user-arguments that follow.
    +
    +              Without the "--", a malicious user could  enable  Postfix  send-
    +              mail(1)  command  options,  by  specifying an email address that
    +              starts with "-".
     
     DIAGNOSTICS
    -       Problems  are  logged to syslogd(8) or postlogd(8), and to the standard
    +       Problems are logged to syslogd(8) or postlogd(8), and to  the  standard
            error stream.
     
     ENVIRONMENT
    @@ -299,12 +332,12 @@ SENDMAIL(1)                                                        SENDMAIL(1)
                   Enable debugging with an external command, as specified with the
                   debugger_command configuration parameter.
     
    -       NAME   The  sender full name. This is used only with messages that have
    +       NAME   The sender full name. This is used only with messages that  have
                   no From: message header. See also the -F option above.
     
     CONFIGURATION PARAMETERS
    -       The following main.cf parameters are especially relevant to  this  pro-
    -       gram.   The  text  below  provides  only a parameter summary. See post-
    +       The  following  main.cf parameters are especially relevant to this pro-
    +       gram.  The text below provides only  a  parameter  summary.  See  post-
            conf(5) for more details including examples.
     
     COMPATIBILITY CONTROLS
    @@ -315,7 +348,7 @@ SENDMAIL(1)                                                        SENDMAIL(1)
                   line endings from <CR><LF> into UNIX format (<LF>).
     
     TROUBLE SHOOTING CONTROLS
    -       The  DEBUG_README  file gives examples of how to troubleshoot a Postfix
    +       The DEBUG_README file gives examples of how to troubleshoot  a  Postfix
            system.
     
            debugger_command (empty)
    @@ -323,13 +356,15 @@ SENDMAIL(1)                                                        SENDMAIL(1)
                   invoked with the -D option.
     
            debug_peer_level (2)
    -              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  destina-
    +              tion,  remote client or server name or network address matches a
    +              pattern given with the debug_peer_list parameter.
     
            debug_peer_list (empty)
    -              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
            Available in Postfix version 2.2 and later:
    @@ -341,13 +376,13 @@ SENDMAIL(1)                                                        SENDMAIL(1)
                   List of users who are authorized to view the queue.
     
            authorized_submit_users (static:anyone)
    -              List of users who are authorized to submit mail with  the  send-
    +              List  of  users who are authorized to submit mail with the send-
                   mail(1) command (and with the privileged postdrop(1) helper com-
                   mand).
     
     RESOURCE AND RATE CONTROLS
            bounce_size_limit (50000)
    -              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.
     
            fork_attempts (5)
    @@ -361,11 +396,11 @@ SENDMAIL(1)                                                        SENDMAIL(1)
                   in the primary message headers.
     
            queue_run_delay (300s)
    -              The time between deferred queue  scans  by  the  queue  manager;
    +              The  time  between  deferred  queue  scans by the queue manager;
                   prior to Postfix 2.4 the default value was 1000s.
     
     FAST FLUSH CONTROLS
    -       The  ETRN_README file describes configuration and operation details for
    +       The ETRN_README file describes configuration and operation details  for
            the Postfix "fast flush" service.
     
            fast_flush_domains ($relay_domains)
    @@ -373,26 +408,26 @@ SENDMAIL(1)                                                        SENDMAIL(1)
                   tion logfiles with mail that is queued to those destinations.
     
     VERP CONTROLS
    -       The  VERP_README  file describes configuration and operation details of
    +       The VERP_README file describes configuration and operation  details  of
            Postfix support for variable envelope return path addresses.
     
            default_verp_delimiters (+=)
                   The two default VERP delimiter characters.
     
            verp_delimiter_filter (-=+)
    -              The characters Postfix accepts as VERP delimiter  characters  on
    +              The  characters  Postfix accepts as VERP delimiter characters on
                   the Postfix sendmail(1) command line and in SMTP commands.
     
     MISCELLANEOUS CONTROLS
            alias_database (see 'postconf -d' output)
    -              The  alias databases for local(8) delivery that are updated with
    +              The alias databases for local(8) delivery that are updated  with
                   "newaliases" or with "sendmail -bi".
     
            command_directory (see 'postconf -d' output)
                   The location of all postfix administrative commands.
     
            config_directory (see 'postconf -d' output)
    -              The default location of the Postfix main.cf and  master.cf  con-
    +              The  default  location of the Postfix main.cf and master.cf con-
                   figuration files.
     
            daemon_directory (see 'postconf -d' output)
    @@ -403,46 +438,46 @@ SENDMAIL(1)                                                        SENDMAIL(1)
                   and postmap(1) commands.
     
            delay_warning_time (0h)
    -              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.
     
            import_environment (see 'postconf -d' output)
    -              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.
     
            mail_owner (postfix)
    -              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.
     
            queue_directory (see 'postconf -d' output)
                   The location of the Postfix top-level queue directory.
     
            remote_header_rewrite_domain (empty)
    -              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.
     
            syslog_facility (mail)
                   The syslog facility of Postfix logging.
     
            syslog_name (see 'postconf -d' output)
    -              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:
     
            alternate_config_directories (empty)
                   A list of non-default Postfix configuration directories that may
    -              be  specified with "-c config_directory" on the command line (in
    -              the case of sendmail(1), with  the  "-C"  option),  or  via  the
    +              be specified with "-c config_directory" on the command line  (in
    +              the  case  of  sendmail(1),  with  the  "-C" option), or via the
                   MAIL_CONFIG environment parameter.
     
            multi_instance_directories (empty)
    -              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.
     
     FILES
    @@ -463,7 +498,7 @@ SENDMAIL(1)                                                        SENDMAIL(1)
            syslogd(8), system logging
     
     README_FILES
    -       Use  "postconf readme_directory" or "postconf html_directory" to locate
    +       Use "postconf readme_directory" or "postconf html_directory" to  locate
            this information.
            DEBUG_README, Postfix debugging howto
            ETRN_README, Postfix ETRN howto
    diff --git a/postfix/html/smtp.8.html b/postfix/html/smtp.8.html
    index f79f4e04e..3c20579ef 100644
    --- a/postfix/html/smtp.8.html
    +++ b/postfix/html/smtp.8.html
    @@ -567,7 +567,7 @@ SMTP(8)                                                                SMTP(8)
                   for   the   "fingerprint"  TLS  security  level  (smtp_tls_secu-
                   rity_level = fingerprint).
     
    -       smtp_tls_fingerprint_digest (md5)
    +       smtp_tls_fingerprint_digest (see 'postconf -d' output)
                   The message digest  algorithm  used  to  construct  remote  SMTP
                   server certificate fingerprints.
     
    @@ -826,12 +826,12 @@ SMTP(8)                                                                SMTP(8)
     
     TROUBLE SHOOTING CONTROLS
            debug_peer_level (2)
    -              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 debug_peer_list parameter.
     
            debug_peer_list (empty)
    -              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.
    diff --git a/postfix/html/smtpd.8.html b/postfix/html/smtpd.8.html
    index b81b864cd..1258376b7 100644
    --- a/postfix/html/smtpd.8.html
    +++ b/postfix/html/smtpd.8.html
    @@ -516,7 +516,7 @@ SMTPD(8)                                                              SMTPD(8)
     
            Available in Postfix version 2.5 and later:
     
    -       smtpd_tls_fingerprint_digest (md5)
    +       smtpd_tls_fingerprint_digest (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  check_ccert_access   and   per-
    @@ -675,13 +675,15 @@ SMTPD(8)                                                              SMTPD(8)
            or debugger.
     
            debug_peer_level (2)
    -              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 destina-
    +              tion, remote client or server name or network address matches  a
    +              pattern given with the debug_peer_list parameter.
     
            debug_peer_list (empty)
    -              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.
     
            error_notice_recipient (postmaster)
                   The recipient of postmaster notifications  about  mail  delivery
    diff --git a/postfix/man/man1/posttls-finger.1 b/postfix/man/man1/posttls-finger.1
    index 9529c20c1..93995eed6 100644
    --- a/postfix/man/man1/posttls-finger.1
    +++ b/postfix/man/man1/posttls-finger.1
    @@ -102,11 +102,12 @@ certificate block.  If you specify \fB\-F \fICAfile\fR or
     \fB\-P \fICApath\fR, the OpenSSL library may augment the chain with
     missing issuer certificates.  To see the actual chain sent by the
     remote SMTP server leave \fICAfile\fR and \fICApath\fR unset.
    -.IP "\fB\-d \fImdalg\fR (default: \fBsha1\fR)"
    +.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
    diff --git a/postfix/man/man1/sendmail.1 b/postfix/man/man1/sendmail.1
    index e52861b1b..3722a1787 100644
    --- a/postfix/man/man1/sendmail.1
    +++ b/postfix/man/man1/sendmail.1
    @@ -45,6 +45,9 @@ selected for delivery.
     .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
    @@ -258,10 +261,43 @@ Log mailer traffic. Use the \fBdebug_peer_list\fR and
     .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
    @@ -312,12 +348,13 @@ Postfix system.
     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
    diff --git a/postfix/man/man5/postconf.5 b/postfix/man/man5/postconf.5
    index db912da01..9f7e8a782 100644
    --- a/postfix/man/man5/postconf.5
    +++ b/postfix/man/man5/postconf.5
    @@ -3212,7 +3212,7 @@ The LMTP\-specific version of the smtp_tls_fingerprint_cert_match
     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
    @@ -3412,6 +3412,15 @@ The fingerprint digest algorithm is configurable via the
     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
    @@ -5910,6 +5919,15 @@ feature.  The fingerprint digest algorithm is configurable via the
     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:
    @@ -5929,10 +5947,6 @@ For more fine\-grained control, use check_ccert_access to select
     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
    @@ -8079,6 +8093,10 @@ is a message digest of the server certificate (or public key). The
     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
    @@ -8098,10 +8116,10 @@ another, and both keys are trusted just prior to the transition.
     .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
    @@ -8116,7 +8134,7 @@ As in the example above, we show two matching fingerprints:
     .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
    @@ -8128,15 +8146,15 @@ As in the example above, we show two matching fingerprints:
     .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
    @@ -8148,18 +8166,19 @@ algorithm. With a digest algorithm resistant to "second pre\-image"
     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:
    @@ -8181,8 +8200,8 @@ For example:
     .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
    @@ -8196,36 +8215,19 @@ key always in "PEM" format. We pipe the result to another OpenSSL
     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
    @@ -8235,10 +8237,6 @@ 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
     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
    @@ -8682,8 +8680,12 @@ Example:
     .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
    @@ -8701,8 +8703,8 @@ Example:
         [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
    @@ -9532,6 +9534,14 @@ 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
     Alternatively, check_ccert_access accepts an explicit search
     order (Postfix 3.5 and later). The default search order as described
    @@ -9639,6 +9649,15 @@ smtpd_tls_fingerprint_digest parameter (hard\-coded as md5 prior to
     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
    @@ -11852,6 +11871,13 @@ This feature is available in Postfix 2.2 and later.
     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:
    @@ -11860,9 +11886,11 @@ 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
    @@ -11897,6 +11925,9 @@ use with export\-grade EDH ciphers.  The default SMTP server cipher
     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
    @@ -11959,32 +11990,32 @@ This feature is available in Postfix 2.6 and later, when Postfix is
     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
    @@ -12032,22 +12063,24 @@ and "DES\-CBC3\-MD5". The last setting disables ciphers that use "EDH"
     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:
    @@ -12069,8 +12102,8 @@ For example:
     .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
    @@ -12084,57 +12117,33 @@ key always in "PEM" format. We pipe the result to another OpenSSL
     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
    @@ -12146,9 +12155,9 @@ Example: client\-certificate access table, with sha1 fingerprints:
     .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
    @@ -13551,6 +13560,9 @@ for further details.  The default SMTP server cipher 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
     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
    diff --git a/postfix/man/man8/smtp.8 b/postfix/man/man8/smtp.8
    index 1aaae63d3..697f0f653 100644
    --- a/postfix/man/man8/smtp.8
    +++ b/postfix/man/man8/smtp.8
    @@ -527,7 +527,7 @@ Available in Postfix version 2.5 and later:
     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
    @@ -744,11 +744,11 @@ used for DNS lookups.
     .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"
    diff --git a/postfix/man/man8/smtpd.8 b/postfix/man/man8/smtpd.8
    index d7f65af09..b646af00e 100644
    --- a/postfix/man/man8/smtpd.8
    +++ b/postfix/man/man8/smtpd.8
    @@ -470,11 +470,10 @@ The OpenSSL cipherlist for "NULL" grade ciphers that provide
     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"
    @@ -611,12 +610,13 @@ Postfix mail system. The methods vary from making the software log
     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
    diff --git a/postfix/proto/COMPATIBILITY_README.html b/postfix/proto/COMPATIBILITY_README.html
    index 291979d98..a6b027f60 100644
    --- a/postfix/proto/COMPATIBILITY_README.html
    +++ b/postfix/proto/COMPATIBILITY_README.html
    @@ -72,6 +72,15 @@ setting relay_domains=$mydestination  

  • 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 @@ -327,7 +336,7 @@ explicit list of domain names.

    Using backwards-compatible default setting smtputf8_enable=no

    -

    The smtputf8_enable default value has changed from "no" to "yes. +

    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 @@ -366,6 +375,92 @@ in main.cf:

  • +

    Using backwards-compatible +default setting smtpd_tls_fingerprint_digest=md5

    + +

    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. +

    + +
    +
    +# postconf smtpd_tls_fingerprint_digest=md5
    +# postfix reload
    +
    +
    + +

    Using backwards-compatible +default setting smtp_tls_fingerprint_digest=md5

    + +

    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. +

    + +
    +
    +# postconf 'smtp_tls_fingerprint_digest = md5' \
    +    'lmtp_tls_fingerprint_digest = md5' 
    +# postfix reload
    +
    +
    +

    Turning off the backwards-compatibility safety net

    Backwards compatibility is turned off by updating the diff --git a/postfix/proto/TLS_README.html b/postfix/proto/TLS_README.html index 8f1022589..79732e904 100644 --- a/postfix/proto/TLS_README.html +++ b/postfix/proto/TLS_README.html @@ -783,8 +783,14 @@ table.

    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.

    +parameter. The default algorithm is sha256 with Postfix ≥ +3.6 and the compatibility_level set to 3 or higher. With +Postfix ≤ 3.5, the default algorithm is md5. The +best-practice algorithm is now sha256. 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 @@ -847,30 +853,15 @@ key always in "PEM" format. We pipe the result to 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
    +    openssl dgst -sha256 -c
    +(stdin)= 64:3f:1f:f6:e5:1e:d4:2a:...:8b:fc:09:1a:61:98:b5:bc:7c:60:58
     
    -

    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.

    Server-side cipher controls

    @@ -1510,6 +1501,15 @@ 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 sha256 with Postfix ≥ 3.6 +and the compatibility_level set to 3 or higher; with Postfix +≤ 3.5, the default algorithm is md5. The +best-practice algorithm is now sha256. 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 @@ -1521,10 +1521,10 @@ another, and both keys are trusted just prior to the transition.

         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
     
    @@ -1534,15 +1534,15 @@ 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
     
    @@ -1554,30 +1554,15 @@ key always in "PEM" format. We pipe the result to 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
    +    openssl dgst -sha256 -c
    +(stdin)= 64:3f:1f:f6:e5:1e:d4:2a:56:...:09:1a:61:98:b5:bc:7c:60:58
     
    -

    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.

    Mandatory server certificate verification

    @@ -2400,7 +2385,7 @@ Example: /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 @@ -2411,8 +2396,8 @@ Example: [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
    diff --git a/postfix/proto/postconf.proto b/postfix/proto/postconf.proto index b5ea36aed..bf814ce5f 100644 --- a/postfix/proto/postconf.proto +++ b/postfix/proto/postconf.proto @@ -5120,7 +5120,14 @@ Postfix version 2.5). This feature requires "smtpd_tls_ask_ccert = yes" and is available with Postfix version 2.2 and later. -
    +
    The default algorithm is sha256 with Postfix ≥ 3.6 +and the compatibility_level set to 3 or higher. With Postfix +≤ 3.5, the default algorithm is md5. The best-practice +algorithm is now sha256. 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.
    Alternatively, check_ccert_access accepts an explicit search order (Postfix 3.5 and later). The default search order as described @@ -5243,6 +5250,15 @@ smtpd_tls_fingerprint_digest parameter (hard-coded as md5 prior to Postfix version 2.5). This feature requires "smtpd_tls_ask_ccert = yes" and is available with Postfix version 2.2 and later.
    +
    The default algorithm is sha256 with Postfix ≥ 3.6 +and the compatibility_level set to 3 or higher. With Postfix +≤ 3.5, the default algorithm is md5. The best-practice +algorithm is now sha256. 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.
    +
    reject_rbl_client rbl_domain=d.d.d.d
    Reject the request when the reversed client network address is @@ -9275,6 +9291,15 @@ The fingerprint digest algorithm is configurable via the smtpd_tls_fingerprint_digest parameter (hard-coded as md5 prior to Postfix version 2.5).
    +
    The default algorithm is sha256 with Postfix ≥ 3.6 +and the compatibility_level set to 3 or higher. With Postfix +≤ 3.5, the default algorithm is md5. The best-practice +algorithm is now sha256. 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.
    +
    permit_tls_all_clientcerts
    Append the domain name in $myorigin or $mydomain when the @@ -9711,6 +9736,15 @@ feature. The fingerprint digest algorithm is configurable via the smtpd_tls_fingerprint_digest parameter (hard-coded as md5 prior to Postfix version 2.5).

    +

    The default algorithm is sha256 with Postfix ≥ 3.6 +and the compatibility_level set to 3 or higher. With Postfix +≤ 3.5, the default algorithm is md5. The best-practice +algorithm is now sha256. 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.

    +

    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: @@ -9726,10 +9760,6 @@ relay_clientcerts = hash:/etc/postfix/relay_clientcerts an appropriate access(5) policy for each client. See RESTRICTION_CLASS_README.

    -

    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.

    -

    This feature is available with Postfix version 2.2.

    %PARAM smtpd_tls_cipherlist @@ -9754,15 +9784,24 @@ Postfix 2.3 and later; use smtpd_tls_mandatory_ciphers instead.

    File with DH parameters that the Postfix SMTP server should use with non-export EDH ciphers.

    +

    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.

    +

    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:

    -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
     
    @@ -9796,6 +9835,9 @@ grade is "medium" with Postfix releases after the middle of 2015, and as a result export-grade cipher suites are by default not used.

    +

    With Postfix ≥ 3.6 export-grade Diffie-Hellman key exchange +is no longer supported, and this parameter is silently ignored.

    +

    See also the discussion under the smtpd_tls_dh1024_param_file configuration parameter.

    @@ -11226,8 +11268,12 @@ Example:
     /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
     
    @@ -11241,8 +11287,8 @@ Example:
         [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
     

    Note: The hostname strategy if listed in a non-default @@ -12316,7 +12362,7 @@ configuration parameter. See there for details.

    This feature is available in Postfix 2.4 and later.

    -%PARAM smtp_tls_fingerprint_digest md5 +%PARAM smtp_tls_fingerprint_digest see "postconf -d" output

    The message digest algorithm used to construct remote SMTP server certificate fingerprints. At the "fingerprint" TLS security level @@ -12329,19 +12375,19 @@ algorithm. With a digest algorithm resistant to "second pre-image" 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.

    -

    The default algorithm is md5; this is consistent with -the backwards compatible setting of the digest used to verify client -certificates in the SMTP server.

    +

    The default algorithm is sha256 with Postfix ≥ 3.6 +and the compatibility_level set to 3 or higher. With Postfix +≤ 3.5, the default algorithm is md5.

    -

    The best practice algorithm is now sha1. 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 sha256. 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.

    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.

    To find the fingerprint of a specific certificate file, with a specific digest algorithm, run: @@ -12358,8 +12404,8 @@ For example:

    -$ 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
     
    @@ -12371,29 +12417,16 @@ key always in "PEM" format. We pipe the result to 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.

    +

    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.

    -# 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
    -
    -
    - -
    -
    -# 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
     
    @@ -12401,10 +12434,6 @@ $ openssl x509 -in cert.pem -noout -pubkey | fingerprint and public key fingerprint when the TLS loglevel is 2 or higher.

    -

    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.

    -

    This feature is available in Postfix 2.5 and later.

    %PARAM smtp_tls_fingerprint_cert_match @@ -12419,6 +12448,10 @@ is a message digest of the server certificate (or public key). The digest algorithm is selected via the smtp_tls_fingerprint_digest parameter.

    +

    The colons between each pair of nibbles in the fingerprint value +are optional (Postfix ≥ 3.6). These were required in earlier +Postfix releases.

    +

    When an smtp_tls_policy_maps table entry specifies the "fingerprint" security level, any "match" attributes in that entry specify the list of valid fingerprints for the corresponding destination. Multiple @@ -12436,10 +12469,10 @@ another, and both keys are trusted just prior to the transition.

     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
     
    @@ -12450,7 +12483,7 @@ 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
     
    @@ -12458,8 +12491,8 @@ As in the example above, we show two matching fingerprints:

     /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
     
    @@ -12472,31 +12505,32 @@ configuration parameter. See there for details.

    This feature is available in Postfix 2.5 and later.

    -%PARAM lmtp_tls_fingerprint_digest md5 +%PARAM lmtp_tls_fingerprint_digest see "postconf -d" output

    The LMTP-specific version of the smtp_tls_fingerprint_digest configuration parameter. See there for details.

    This feature is available in Postfix 2.5 and later.

    -%PARAM smtpd_tls_fingerprint_digest md5 +%PARAM smtpd_tls_fingerprint_digest 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 check_ccert_access and permit_tls_clientcerts. The -default algorithm is md5, for backwards compatibility with Postfix -releases prior to 2.5.

    +

    The message digest algorithm to construct remote SMTP client-certificate +fingerprints or public key fingerprints (Postfix 2.9 and later) for +check_ccert_access and permit_tls_clientcerts.

    -

    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 default algorithm is sha256 with Postfix ≥ 3.6 +and the compatibility_level set to 3 or higher. With Postfix +≤ 3.5, the default algorithm is md5.

    + +

    The best-practice algorithm is now sha256. 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.

    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.

    To find the fingerprint of a specific certificate file, with a specific digest algorithm, run:

    @@ -12512,8 +12546,8 @@ For example:

    -$ 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
     
    @@ -12525,46 +12559,26 @@ key always in "PEM" format. We pipe the result to 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
    +    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
     
    -
    -
    -# 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
    -
    -
    -

    The Postfix SMTP server and client log the peer (leaf) certificate fingerprint and public key fingerprint when the TLS loglevel is 2 or higher.

    -

    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.

    - -

    Example: client-certificate access table, with sha1 fingerprints:

    +

    Example: client-certificate access table, with sha256 fingerprints:

     /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
    @@ -12572,9 +12586,9 @@ to Postfix 2.9.6 or later. 

     /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
     
    @@ -12859,32 +12873,32 @@ EC algorithms have not been disabled by the vendor.

    %PARAM smtpd_tls_eecdh_grade 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 +auto value (described below) was chosen. +

    The available choices are:

    +
    auto
    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).
    +
    none
    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.
    -
    strong
    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. -
    +
    strong
    Use EECDH with approximately 128 bits of +security at a reasonable computational cost. This is the default in +Postfix versions 2.8–3.5.
    ultra
    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.
    - -
    auto
    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.
    @@ -15207,6 +15221,9 @@ for further details. The default SMTP server cipher grade is "medium" with Postfix releases after the middle of 2015, and as a result export-grade cipher suites are by default not used.

    +

    With Postfix ≥ 3.6 export-grade Diffie-Hellman key exchange +is no longer supported, and this parameter is silently ignored.

    +

    This feature is available in Postfix 2.8 and later.

    %PARAM tlsproxy_tls_dkey_file $smtpd_tls_dkey_file diff --git a/postfix/src/global/mail_params.c b/postfix/src/global/mail_params.c index 91c70f75e..0efd00eda 100644 --- a/postfix/src/global/mail_params.c +++ b/postfix/src/global/mail_params.c @@ -147,6 +147,10 @@ /* 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; @@ -356,14 +360,21 @@ char *var_drop_hdrs; 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. */ @@ -618,6 +629,23 @@ static void check_legacy_defaults(void) * 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. diff --git a/postfix/src/global/mail_params.h b/postfix/src/global/mail_params.h index a6119f1b2..65906f187 100644 --- a/postfix/src/global/mail_params.h +++ b/postfix/src/global/mail_params.h @@ -19,8 +19,8 @@ typedef int bool; #ifdef USE_TLS #include /* OPENSSL_VERSION_NUMBER */ #include /* 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 @@ -52,7 +52,7 @@ extern bool var_show_unk_rcpt_table; */ #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; @@ -64,6 +64,10 @@ extern int warn_compat_break_relay_domains; 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 @@ -1356,7 +1360,8 @@ extern char *var_smtpd_tls_excl_ciph; 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" @@ -1517,9 +1522,11 @@ extern char *var_smtp_tls_excl_ciph; 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" diff --git a/postfix/src/global/mail_version.h b/postfix/src/global/mail_version.h index 1326f7b26..5fc6402a3 100644 --- a/postfix/src/global/mail_version.h +++ b/postfix/src/global/mail_version.h @@ -16,11 +16,11 @@ * 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 diff --git a/postfix/src/posttls-finger/posttls-finger.c b/postfix/src/posttls-finger/posttls-finger.c index a3a9946d3..aa859cb28 100644 --- a/postfix/src/posttls-finger/posttls-finger.c +++ b/postfix/src/posttls-finger/posttls-finger.c @@ -96,11 +96,12 @@ /* \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 @@ -876,16 +877,20 @@ static int starttls(STATE *state) * 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 = @@ -1395,10 +1400,6 @@ static int dane_host_level(STATE *state, DNS_RR *addr) 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) @@ -1423,8 +1424,7 @@ static int dane_host_level(STATE *state, DNS_RR *addr) 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) { @@ -1673,23 +1673,6 @@ static int finger(STATE *state) 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) @@ -1833,7 +1816,7 @@ static void parse_options(STATE *state, int argc, char *argv[]) #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(""); @@ -1888,7 +1871,8 @@ static void parse_options(STATE *state, int argc, char *argv[]) state->print_trust = 1; break; case 'd': - myfree(state->mdalg); + if (state->mdalg) + myfree(state->mdalg); state->mdalg = mystrdup(optarg); break; case 'f': @@ -2011,6 +1995,7 @@ static void parse_options(STATE *state, int argc, char *argv[]) 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); @@ -2024,21 +2009,6 @@ static void parse_options(STATE *state, int argc, char *argv[]) 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 } @@ -2047,9 +2017,10 @@ static void parse_options(STATE *state, int argc, char *argv[]) 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++, ""); @@ -2066,8 +2037,8 @@ static void parse_match(STATE *state, int argc, char *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: @@ -2112,6 +2083,13 @@ int main(int argc, char *argv[]) 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); @@ -2128,8 +2106,31 @@ int main(int argc, char *argv[]) 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); @@ -2155,10 +2156,5 @@ int main(int argc, char *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); } diff --git a/postfix/src/sendmail/sendmail.c b/postfix/src/sendmail/sendmail.c index 575ea4856..40e2d26d4 100644 --- a/postfix/src/sendmail/sendmail.c +++ b/postfix/src/sendmail/sendmail.c @@ -39,6 +39,9 @@ /* .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 @@ -250,10 +253,43 @@ /* 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. @@ -294,12 +330,13 @@ /* 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 @@ -1010,6 +1047,7 @@ int main(int argc, char **argv) const char *dsn_envid = 0; int saved_optind; ARGV *import_env; + char *alias_map_from_args = 0; /* * Fingerprint executables and core dumps. @@ -1279,9 +1317,7 @@ int main(int argc, char **argv) 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': @@ -1431,13 +1467,17 @@ int main(int argc, char **argv) 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 */ diff --git a/postfix/src/smtp/smtp.c b/postfix/src/smtp/smtp.c index 9f1357aa6..906ef9f25 100644 --- a/postfix/src/smtp/smtp.c +++ b/postfix/src/smtp/smtp.c @@ -493,7 +493,7 @@ /* 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 @@ -702,11 +702,11 @@ /* .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" @@ -1443,6 +1443,7 @@ static void pre_init(char *unused_name, char **unused_argv) 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 diff --git a/postfix/src/smtp/smtp_proto.c b/postfix/src/smtp/smtp_proto.c index 665dc9bd4..7fd634863 100644 --- a/postfix/src/smtp/smtp_proto.c +++ b/postfix/src/smtp/smtp_proto.c @@ -1112,18 +1112,17 @@ static int smtp_start_tls(SMTP_STATE *state) * "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"), diff --git a/postfix/src/smtp/smtp_tls_policy.c b/postfix/src/smtp/smtp_tls_policy.c index 4b394a934..92a231df5 100644 --- a/postfix/src/smtp/smtp_tls_policy.c +++ b/postfix/src/smtp/smtp_tls_policy.c @@ -336,8 +336,7 @@ static void tls_policy_lookup_one(SMTP_TLS_POLICY *tls, int *site_level, 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: @@ -618,10 +617,10 @@ static void *policy_create(const char *unused_key, void *context) 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); @@ -640,7 +639,7 @@ static void *policy_create(const char *unused_key, void *context) 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); @@ -909,7 +908,7 @@ static void dane_init(SMTP_TLS_POLICY *tls, SMTP_ITERATOR *iter) /* * 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) { @@ -919,7 +918,7 @@ static void dane_init(SMTP_TLS_POLICY *tls, SMTP_ITERATOR *iter) 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; diff --git a/postfix/src/smtpd/smtpd.c b/postfix/src/smtpd/smtpd.c index 2059bd5e2..4f3e52a20 100644 --- a/postfix/src/smtpd/smtpd.c +++ b/postfix/src/smtpd/smtpd.c @@ -436,11 +436,10 @@ /* 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" @@ -569,12 +568,13 @@ /* 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 diff --git a/postfix/src/smtpd/smtpd_check.c b/postfix/src/smtpd/smtpd_check.c index 485dbb146..019518258 100644 --- a/postfix/src/smtpd/smtpd_check.c +++ b/postfix/src/smtpd/smtpd_check.c @@ -1607,6 +1607,11 @@ static int permit_tls_clientcerts(SMTPD_STATE *state, int permit_all_certs) 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; @@ -3196,9 +3201,17 @@ static int check_ccert_access(SMTPD_STATE *state, const char *acl_spec, 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); @@ -3958,6 +3971,7 @@ static int check_policy_service(SMTPD_STATE *state, const char *server, 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; @@ -3999,6 +4013,23 @@ static int check_policy_service(SMTPD_STATE *state, const char *server, 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, diff --git a/postfix/src/smtpstone/smtp-sink.c b/postfix/src/smtpstone/smtp-sink.c index 4378d5ad3..cd228b621 100644 --- a/postfix/src/smtpstone/smtp-sink.c +++ b/postfix/src/smtpstone/smtp-sink.c @@ -1354,7 +1354,7 @@ static void connect_event(int unused_event, void *unused_context) 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 diff --git a/postfix/src/tls/Makefile.in b/postfix/src/tls/Makefile.in index fec7d4542..948afab9f 100644 --- a/postfix/src/tls/Makefile.in +++ b/postfix/src/tls/Makefile.in @@ -1,7 +1,7 @@ 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 \ @@ -12,7 +12,7 @@ SRCS = tls_prng_dev.c tls_prng_egd.c tls_prng_file.c tls_fprint.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 \ @@ -25,7 +25,7 @@ DEFS = -I. -I$(INC_DIR) -D$(SYSTYPE) 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) \ @@ -121,11 +121,6 @@ tls_mgr: $(LIB) $(LIBS) $(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) @@ -206,6 +201,7 @@ tls_dane.o: ../../include/dns.h 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 @@ -499,19 +495,6 @@ tls_proxy_server_scan.o: ../../include/vstring.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 diff --git a/postfix/src/tls/tls.h b/postfix/src/tls/tls.h index 2a8cc1199..439f805c6 100644 --- a/postfix/src/tls/tls.h +++ b/postfix/src/tls/tls.h @@ -52,7 +52,6 @@ #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) \ @@ -84,36 +83,8 @@ extern const char *str_tls_level(int); #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 /*- @@ -186,52 +157,33 @@ typedef enum { * 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 *); @@ -250,6 +202,7 @@ typedef struct { 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; @@ -280,12 +233,10 @@ typedef struct { 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; /* @@ -329,6 +280,7 @@ extern int tls_log_mask(const char *, const char *); #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 @@ -651,40 +603,35 @@ extern int tls_bio(int, int, TLS_SESS_STATE *, /* * 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 @@ -709,7 +656,7 @@ extern long tls_bug_bits(void); 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 diff --git a/postfix/src/tls/tls_certkey.c b/postfix/src/tls/tls_certkey.c index be8d47000..489c3557b 100644 --- a/postfix/src/tls/tls_certkey.c +++ b/postfix/src/tls/tls_certkey.c @@ -149,7 +149,6 @@ static void init_pem_load_state(pem_load_state_t *st, SSL_CTX *ctx, SSL *ssl, /* 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; @@ -181,54 +180,6 @@ static int use_chain(pem_load_state_t *st) 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, @@ -418,7 +369,7 @@ static int load_pem_object(pem_load_state_t *st) && 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); @@ -697,17 +648,6 @@ int main(int argc, char *argv[]) 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); diff --git a/postfix/src/tls/tls_client.c b/postfix/src/tls/tls_client.c index 139f0204f..00e02dc22 100644 --- a/postfix/src/tls/tls_client.c +++ b/postfix/src/tls/tls_client.c @@ -86,9 +86,8 @@ /* 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. @@ -304,15 +303,323 @@ static void uncache_session(SSL_CTX *ctx, TLS_SESS_STATE *TLScontext) 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; /* @@ -333,17 +640,6 @@ TLS_APPL_STATE *tls_client_init(const TLS_CLIENT_INIT_PROPS *props) */ 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 @@ -361,7 +657,7 @@ TLS_APPL_STATE *tls_client_init(const TLS_CLIENT_INIT_PROPS *props) * 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); } @@ -402,6 +698,21 @@ TLS_APPL_STATE *tls_client_init(const TLS_CLIENT_INIT_PROPS *props) */ 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. @@ -465,19 +776,6 @@ TLS_APPL_STATE *tls_client_init(const TLS_CLIENT_INIT_PROPS *props) 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 @@ -558,309 +856,6 @@ TLS_APPL_STATE *tls_client_init(const TLS_CLIENT_INIT_PROPS *props) 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, @@ -874,8 +869,6 @@ TLS_SESS_STATE *tls_client_start(const TLS_CLIENT_START_PROPS *props) 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; /* @@ -883,8 +876,7 @@ TLS_SESS_STATE *tls_client_start(const TLS_CLIENT_START_PROPS *props) * 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) @@ -903,9 +895,18 @@ TLS_SESS_STATE *tls_client_start(const TLS_CLIENT_START_PROPS *props) 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 @@ -917,6 +918,7 @@ TLS_SESS_STATE *tls_client_start(const TLS_CLIENT_START_PROPS *props) */ 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()"); @@ -942,9 +944,79 @@ TLS_SESS_STATE *tls_client_start(const TLS_CLIENT_START_PROPS *props) 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 @@ -965,33 +1037,22 @@ TLS_SESS_STATE *tls_client_start(const TLS_CLIENT_START_PROPS *props) * 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 @@ -1005,65 +1066,6 @@ TLS_SESS_STATE *tls_client_start(const TLS_CLIENT_START_PROPS *props) 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." 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 @@ -1096,8 +1098,6 @@ TLS_SESS_STATE *tls_client_start(const TLS_CLIENT_START_PROPS *props) 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. @@ -1170,7 +1170,8 @@ TLS_SESS_STATE *tls_client_post_connect(TLS_SESS_STATE *TLScontext, * 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 & @@ -1204,18 +1205,6 @@ TLS_SESS_STATE *tls_client_post_connect(TLS_SESS_STATE *TLScontext, 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. */ diff --git a/postfix/src/tls/tls_dane.c b/postfix/src/tls/tls_dane.c index 013426b11..a31a51374 100644 --- a/postfix/src/tls/tls_dane.c +++ b/postfix/src/tls/tls_dane.c @@ -6,91 +6,96 @@ /* SYNOPSIS /* #include /* +/* 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 @@ -98,6 +103,18 @@ /* 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 @@ -109,6 +126,23 @@ /* 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. @@ -122,26 +156,19 @@ /* 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 @@ -179,6 +206,7 @@ #include #include #include +#include #include #include /* event_time() */ #include @@ -207,7 +235,7 @@ #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; @@ -216,35 +244,15 @@ static int dane_tlsa_support = 0; #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, @@ -255,213 +263,29 @@ static dane_digest *digest_list; #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; @@ -469,342 +293,229 @@ TLS_DANE *tls_dane_alloc(void) 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. @@ -814,150 +525,24 @@ static int parse_tlsa_rr(DNS_RR *rr, filter_ctx *ctx) 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 */ @@ -965,14 +550,14 @@ static DNS_RR *process_rrs(TLS_DANE *dane, DNS_RR *rrset) 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) { @@ -986,15 +571,12 @@ static void *dane_lookup(const char *tlsa_fqdn, void *unused_ctx) 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; @@ -1132,18 +714,11 @@ int tls_dane_load_trustfile(TLS_DANE *dane, const char *tafile) 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 @@ -1157,41 +732,38 @@ int tls_dane_load_trustfile(TLS_DANE *dane, const char *tafile) /* 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); } /* @@ -1219,455 +791,225 @@ int tls_dane_load_trustfile(TLS_DANE *dane, const char *tafile) 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 @@ -1699,14 +1041,13 @@ static int verify_chain(SSL *ssl, x509_stack_t *chain, TLS_SESS_STATE *tctx) } 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); @@ -1714,9 +1055,9 @@ static int verify_chain(SSL *ssl, x509_stack_t *chain, TLS_SESS_STATE *tctx) 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; @@ -1724,7 +1065,7 @@ static void add_tlsa(TLS_DANE *dane, char *argv[]) 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 */ @@ -1745,9 +1086,15 @@ static void add_tlsa(TLS_DANE *dane, char *argv[]) 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]); @@ -1763,26 +1110,49 @@ static void add_tlsa(TLS_DANE *dane, char *argv[]) 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); } @@ -1883,122 +1253,6 @@ static void usage(const char *progname) 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; @@ -2006,11 +1260,6 @@ static SSL_CTX *ctx_init(const char *CAfile) 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); @@ -2023,6 +1272,11 @@ static SSL_CTX *ctx_init(const char *CAfile) 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); @@ -2035,8 +1289,10 @@ static SSL_CTX *ctx_init(const char *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); @@ -2051,28 +1307,49 @@ int main(int argc, char *argv[]) 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 */ diff --git a/postfix/src/tls/tls_dh.c b/postfix/src/tls/tls_dh.c index 70db8e9d9..a39a63908 100644 --- a/postfix/src/tls/tls_dh.c +++ b/postfix/src/tls/tls_dh.c @@ -7,46 +7,27 @@ /* #define TLS_INTERNAL /* #include /* -/* 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. @@ -101,34 +82,15 @@ /* 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 | @@ -138,108 +100,82 @@ static unsigned char dh512_der[] = { * 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) @@ -309,77 +245,16 @@ 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 diff --git a/postfix/src/tls/tls_fprint.c b/postfix/src/tls/tls_fprint.c index 2bb7e21be..268349fd2 100644 --- a/postfix/src/tls/tls_fprint.c +++ b/postfix/src/tls/tls_fprint.c @@ -6,7 +6,8 @@ /* SYNOPSIS /* #include /* -/* 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; @@ -15,11 +16,6 @@ /* 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; @@ -33,11 +29,6 @@ /* 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 @@ -53,12 +44,11 @@ /* 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 @@ -126,43 +116,78 @@ 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; @@ -198,37 +223,35 @@ char *tls_serverid_digest(const TLS_CLIENT_START_PROPS *props, long protomask, digest_string(ciphers); /* - * All we get from the session cache is a single bit telling us whether - * the certificate is trusted or not, but we need to know whether the - * trust is CA-based (in that case we must do name checks) or whether it - * is a direct end-point match. We mustn't confuse the two, so it is - * best to process only TA trust in the verify callback and check the EE - * trust after. This works since re-used sessions always have access to - * the leaf certificate, while only the original session has the leaf and - * the full trust chain. - * - * Only the trust anchor matchlist is hashed into the session key. The end - * entity certs are not used to determine whether a certificate is - * trusted or not, rather these are rechecked against the leaf cert - * outside the verification callback, each time a session is created or - * reused. - * - * Therefore, the security context of the session does not depend on the EE - * matching data, which is checked separately each time. So we exclude - * the EE part of the DANE structure from the serverid digest. - * - * If 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) @@ -282,7 +305,7 @@ char *tls_digest_encode(const unsigned char *md_buf, int md_len) /* 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; @@ -310,13 +333,13 @@ char *tls_data_fprint(const char *buf, int len, const char *mdalg) 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"); @@ -340,17 +363,17 @@ char *tls_pkey_fprint(X509 *peercert, const char *mdalg) 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"); diff --git a/postfix/src/tls/tls_misc.c b/postfix/src/tls/tls_misc.c index 24d8be5c7..ecb932898 100644 --- a/postfix/src/tls/tls_misc.c +++ b/postfix/src/tls/tls_misc.c @@ -109,7 +109,7 @@ /* 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 @@ -202,8 +202,8 @@ /* 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 @@ -239,8 +239,6 @@ #include #include -#ifdef USE_TLS - /* Utility library. */ #include @@ -292,13 +290,11 @@ char *var_tls_tkt_cipher; 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 @@ -519,6 +515,7 @@ static const NAME_MASK tls_log_table[] = { "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, @@ -822,7 +819,6 @@ const char *tls_set_ciphers(TLS_SESS_STATE *TLScontext, const char *grade, 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; @@ -963,6 +959,8 @@ void tls_get_signature_params(TLS_SESS_STATE *TLScontext) */ 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); @@ -983,7 +981,6 @@ void tls_get_signature_params(TLS_SESS_STATE *TLScontext) 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 */ @@ -1121,11 +1118,8 @@ TLS_SESS_STATE *tls_alloc_sess_context(int log_mask, const char *namaddr) 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); } @@ -1158,12 +1152,24 @@ void tls_free_context(TLS_SESS_STATE *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); } @@ -1182,57 +1188,17 @@ static void tls_version_split(unsigned long version, TLS_VINFO *info) * * 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. */ @@ -1494,29 +1460,11 @@ long tls_bio_dump_cb(BIO *bio, int cmd, const char *argp, int argi, 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. @@ -1534,7 +1482,7 @@ int tls_validate_digest(const char *dgst) dgst, md_len); return (0); } - return (1); + return md_alg; } #else diff --git a/postfix/src/tls/tls_proxy.h b/postfix/src/tls/tls_proxy.h index 45058c356..f32f9a0c8 100644 --- a/postfix/src/tls/tls_proxy.h +++ b/postfix/src/tls/tls_proxy.h @@ -160,6 +160,7 @@ extern void tls_proxy_server_start_free(TLS_SERVER_START_PROPS *); #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" @@ -252,30 +253,15 @@ extern void tls_proxy_server_start_free(TLS_SERVER_START_PROPS *); /* * 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 diff --git a/postfix/src/tls/tls_proxy_client_print.c b/postfix/src/tls/tls_proxy_client_print.c index 00e38bf95..ac628502e 100644 --- a/postfix/src/tls/tls_proxy_client_print.c +++ b/postfix/src/tls/tls_proxy_client_print.c @@ -180,110 +180,18 @@ int tls_proxy_client_init_print(ATTR_PRINT_COMMON_FN print_fn, VSTREAM *fp, 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); @@ -291,19 +199,14 @@ static int tls_proxy_client_tlsa_print(ATTR_PRINT_COMMON_FN print_fn, 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); @@ -325,19 +228,12 @@ static int tls_proxy_client_dane_print(ATTR_PRINT_COMMON_FN print_fn, 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. */ diff --git a/postfix/src/tls/tls_proxy_client_scan.c b/postfix/src/tls/tls_proxy_client_scan.c index 0d2b43b2d..06a637651 100644 --- a/postfix/src/tls/tls_proxy_client_scan.c +++ b/postfix/src/tls/tls_proxy_client_scan.c @@ -110,6 +110,7 @@ /* TLS library. */ +#define TLS_INTERNAL #include #include @@ -306,59 +307,6 @@ int tls_proxy_client_init_scan(ATTR_SCAN_COMMON_FN scan_fn, VSTREAM *fp, 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) @@ -376,169 +324,49 @@ 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; @@ -564,41 +392,20 @@ static int tls_proxy_client_dane_scan(ATTR_SCAN_COMMON_FN scan_fn, 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; } } diff --git a/postfix/src/tls/tls_proxy_context_print.c b/postfix/src/tls/tls_proxy_context_print.c index dadd64f81..4e7f9127d 100644 --- a/postfix/src/tls/tls_proxy_context_print.c +++ b/postfix/src/tls/tls_proxy_context_print.c @@ -70,6 +70,8 @@ int tls_proxy_context_print(ATTR_PRINT_COMMON_FN print_fn, VSTREAM *fp, 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, diff --git a/postfix/src/tls/tls_proxy_context_scan.c b/postfix/src/tls/tls_proxy_context_scan.c index af216ac36..1d463ada9 100644 --- a/postfix/src/tls/tls_proxy_context_scan.c +++ b/postfix/src/tls/tls_proxy_context_scan.c @@ -100,6 +100,8 @@ int tls_proxy_context_scan(ATTR_SCAN_COMMON_FN scan_fn, VSTREAM *fp, 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), @@ -137,7 +139,7 @@ int tls_proxy_context_scan(ATTR_SCAN_COMMON_FN scan_fn, VSTREAM *fp, tls_context->srvr_sig_curve = vstring_export(srvr_sig_curve); tls_context->srvr_sig_dgst = vstring_export(srvr_sig_dgst); tls_context->namaddr = vstring_export(namaddr); - ret = (ret == 21 ? 1 : -1); + ret = (ret == 22 ? 1 : -1); if (ret != 1) { tls_proxy_context_free(tls_context); tls_context = 0; diff --git a/postfix/src/tls/tls_rsa.c b/postfix/src/tls/tls_rsa.c index 67f2a2eeb..e69de29bb 100644 --- a/postfix/src/tls/tls_rsa.c +++ b/postfix/src/tls/tls_rsa.c @@ -1,127 +0,0 @@ -/*++ -/* NAME -/* tls_rsa -/* SUMMARY -/* RSA support -/* SYNOPSIS -/* #define TLS_INTERNAL -/* #include -/* -/* 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 -#include - -#ifdef USE_TLS - -/* TLS library. */ - -#define TLS_INTERNAL -#include -#include - - /* - * 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 - -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 diff --git a/postfix/src/tls/tls_scache.c b/postfix/src/tls/tls_scache.c index dcfd3d0a2..408d28681 100644 --- a/postfix/src/tls/tls_scache.c +++ b/postfix/src/tls/tls_scache.c @@ -255,7 +255,8 @@ static int tls_scache_decode(TLS_SCACHE *cp, const char *cache_id, #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); diff --git a/postfix/src/tls/tls_server.c b/postfix/src/tls/tls_server.c index 0b81d2b64..bd3aca228 100644 --- a/postfix/src/tls/tls_server.c +++ b/postfix/src/tls/tls_server.c @@ -166,15 +166,8 @@ static const char server_session_id_context[] = "Postfix/TLS"; #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, @@ -370,17 +363,6 @@ TLS_APPL_STATE *tls_server_init(const TLS_SERVER_INIT_PROPS *props) */ 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. */ @@ -607,20 +589,6 @@ TLS_APPL_STATE *tls_server_init(const TLS_SERVER_INIT_PROPS *props) 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 @@ -629,19 +597,17 @@ TLS_APPL_STATE *tls_server_init(const TLS_SERVER_INIT_PROPS *props) * 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 diff --git a/postfix/src/tls/tls_verify.c b/postfix/src/tls/tls_verify.c index 85d5f649d..f32f32b4a 100644 --- a/postfix/src/tls/tls_verify.c +++ b/postfix/src/tls/tls_verify.c @@ -21,10 +21,6 @@ /* 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 @@ -50,11 +46,6 @@ /* 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 @@ -72,11 +63,10 @@ /* .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 @@ -152,7 +142,6 @@ int tls_verify_certificate_callback(int ok, X509_STORE_CTX *ctx) X509 *cert; int err; int depth; - int max_depth; SSL *con; TLS_SESS_STATE *TLScontext; @@ -163,35 +152,23 @@ int tls_verify_certificate_callback(int ok, X509_STORE_CTX *ctx) 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); @@ -410,77 +387,18 @@ static char *tls_text_name(X509_NAME *name, int nid, const char *label, 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("")); } diff --git a/postfix/src/tlsproxy/tlsproxy.c b/postfix/src/tlsproxy/tlsproxy.c index 7aad011be..b11ce92c3 100644 --- a/postfix/src/tlsproxy/tlsproxy.c +++ b/postfix/src/tlsproxy/tlsproxy.c @@ -996,13 +996,7 @@ static int tlsp_client_start_pre_handshake(TLSP_STATE *state) { 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); @@ -1194,8 +1188,7 @@ static void tlsp_log_config_diff(const char *server_cfg, const char *client_cfg) /* 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; @@ -1228,8 +1221,8 @@ static TLS_APPL_STATE *tlsp_client_init(TLS_CLIENT_PARAMS *tls_params, 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) { @@ -1426,8 +1419,7 @@ static void tlsp_get_request_event(int event, void *context) 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: @@ -1714,7 +1706,6 @@ static void pre_jail_init_client(void) 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); @@ -1741,11 +1732,8 @@ static void pre_jail_init_client(void) 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"); } } diff --git a/postfix/src/util/dict.h b/postfix/src/util/dict.h index 5cba86151..1381bcba1 100644 --- a/postfix/src/util/dict.h +++ b/postfix/src/util/dict.h @@ -257,7 +257,7 @@ typedef struct DICT_UTF8_BACKUP { 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 *); diff --git a/postfix/src/util/hex_code.c b/postfix/src/util/hex_code.c index 9e890353a..3dfcb9818 100644 --- a/postfix/src/util/hex_code.c +++ b/postfix/src/util/hex_code.c @@ -15,6 +15,18 @@ /* 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 @@ -24,6 +36,22 @@ /* 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. @@ -36,6 +64,11 @@ /* 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. */ @@ -57,9 +90,18 @@ static const unsigned char hex_chars[] = "0123456789ABCDEF"; #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; @@ -70,14 +112,25 @@ VSTRING *hex_encode(VSTRING *result, const char *in, ssize_t len) 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; @@ -107,12 +160,24 @@ VSTRING *hex_decode(VSTRING *result, const char *in, ssize_t len) 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 /* * Proof-of-concept test program: convert to hexadecimal and back. @@ -126,6 +191,7 @@ int main(int unused_argc, char **unused_argv) VSTRING *b1 = vstring_alloc(1); VSTRING *b2 = vstring_alloc(1); char *test = "this is a test"; + ARGV *argv; #define DECODE(b,x,l) { \ if (hex_decode((b),(x),(l)) == 0) \ @@ -160,6 +226,15 @@ int main(int unused_argc, char **unused_argv) 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); diff --git a/postfix/src/util/hex_code.h b/postfix/src/util/hex_code.h index 0936cdcb9..720977adb 100644 --- a/postfix/src/util/hex_code.h +++ b/postfix/src/util/hex_code.h @@ -19,8 +19,21 @@ /* * 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 diff --git a/postfix/src/util/mymalloc.c b/postfix/src/util/mymalloc.c index b5ec37d06..94f7bb3e7 100644 --- a/postfix/src/util/mymalloc.c +++ b/postfix/src/util/mymalloc.c @@ -23,8 +23,8 @@ /* 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 @@ -261,7 +261,7 @@ char *mystrndup(const char *str, ssize_t len) /* 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"); diff --git a/postfix/src/util/mymalloc.h b/postfix/src/util/mymalloc.h index f245795cd..e2190f786 100644 --- a/postfix/src/util/mymalloc.h +++ b/postfix/src/util/mymalloc.h @@ -19,7 +19,7 @@ extern void *myrealloc(void *, ssize_t); 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