[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

Bug#1003293: buster-pu: package postfix/3.4.14-0+deb10u1



Debdiff attached.

Scott K
diff -Nru postfix-3.4.14/debian/changelog postfix-3.4.23/debian/changelog
--- postfix-3.4.14/debian/changelog	2020-06-29 21:33:31.000000000 -0400
+++ postfix-3.4.23/debian/changelog	2022-01-07 11:04:17.000000000 -0500
@@ -1,3 +1,247 @@
+postfix (3.4.23-0+deb10u1) buster; urgency=medium
+
+  [Scott Kitterman]
+
+  * Refresh patches
+  * Update d/p/70_postfix-check.diff to exclude makedefs.out from synlink
+    check.  Closes: #926331
+  * Do not override user set default_transport in postinst.  Closes: #988538
+  * Remove left-over ca-certificates.crt file from postfix chroot. 
+    Closes: #991609
+  * Add information about keeping resolv.conf up to date in the chroot with
+    the resolvconf package.  Closes: #964762
+
+  [Sergio Gelato]
+
+  * Correct if-up.d to not error out if postfix can't send mail yet. 
+    Closes: #959864
+
+  [Paride Legovini]
+
+  * d/postfix.postinst: tolerate search domain with a leading dot. 
+    Closes: #991950
+
+  [Wietse Venema]
+
+  * 3.4.15
+    - Bugfix (introduced: Postfix 3.0): minor memory leaks in the
+      Postfix TLS library, found during tests. File: tls/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.
+
+    - Workaround for distros that override Postfix protocol
+      settings in a system-wide OpenSSL configuration file, causing
+      interoperability problems after an OS update. File:
+      tls/tls_client.c, tls/tls_server.c.
+
+  * 3.4.16
+    - Bugfix (introduced: Postfix 3.4.15): part of a memory leak
+      fix was backported to the wrong place. File: tls/tls_misc.c.
+
+    - The Postfix 3.4.15 workaround did not explictly override
+      the system-wide OpenSSL configuration of allowed TLS protocol
+      versions, for sessions where the remote SMTP client sends
+      SNI. It's better to be safe than sorry. File: tls/tls_server.c.
+
+  * 3.4.17
+    - Bugfix (introduced: Postfix 3.4, already fixed in Postfix
+      3.6): tlsproxy(8) was using the wrong DANE macro for
+      connections with DANE trust anchors or with non-DANE trust
+      anchors (WTF: Thorsten Habich found this bug in the use
+      case that has nothing to do with DANE). This resulted in a
+      global certificate verify function pointer race, between
+      TLS handshakes that use TLS trust achors and handshakes
+      that use PKI. No memory was corrupted in the course of all
+      this.  Viktor Dukhovni. File: tlsproxy/tlsproxy.c.
+
+    - Cleanup: the posttls-finger '-X' option reported a false
+      conflict with '-r'.  File: posttls-finger/posttls-finger.c.
+
+  * 3.4.18
+    - Bugfix (introduced: Postfix 2.0): smtp_sasl_mechanism_filter
+      ignored table lookup errors, treating them as 'not found'.
+      Found during Postfix 3.6 development. File: smtp/smtp_sasl_proto.c.
+
+    - Bugfix (introduced: Postfix 2.3): when deleting a recipient
+      with a milter, delete the recipient from the duplicate
+      filter, so that the recipient can be added back. Backported
+      from Postfix 3.6. Files: global/been_here.[hc],
+      cleanup/cleanup_milter.c.
+
+    - Bugfix (introduced: before Postfix alpha): the code that
+      looks for Delivered-To: headers ignored headers longer than
+      $line_length_limit. Backported from Postfix 3.6. File:
+      global/delivered_hdr.c.
+
+    - Bugfix (introduced: Postfix 2.8): save a copy of the
+      postscreen_dnsbl_reply_map lookup result. This has no effect
+      when the recommended texthash: look table is used, but it
+      may avoid stale data with other lookup tables. File:
+      postscreen/postscreen_dnsbl.c.
+
+    - Bugfix (introduced: Postfix 2.2): after processing an
+      XCCLIENT command, the smtps service was waiting for a TLS
+      handshake. Found by Aki Tuomi. File: smtpd/smtpd.c.
+
+    - Bugfix (introduced: Postfix 2.3): static maps did not free
+      their casefolding buffer. File: util/dict_static.c.
+
+  * 3.4.19
+    - Feature: when a Postfix program makes a DNS query that
+      requests DNSSEC validation (usually for Postfix DANE support)
+      but the DNS response is not DNSSEC validated, Postfix will
+      send a DNS query configured with the "dnssec_probe" parameter
+      to determine if DNSSEC support is available, and logs a
+      warning if it is not. By default, the probe has type "ns"
+      and domain name ".". The probe is sent once per process
+      lifetime. Files: dns/dns.h, dns/dns_lookup.c, dns/dns_sec.c,
+      test_dns_lookup.c, global/mail_params.[hc], mantools/postlink.
+
+    - The default "smtp_tls_dane_insecure_mx_policy = dane" was
+      causing unnecessary dnssec_probe activity. The default is now
+      "dane" when smtp_tls_security_level is "dane", otherwise it is
+      "may". File: global/mail_params.h.
+
+  * 3.4.20
+    - Missing null pointer checks (introduced: Postfix 3.4) after
+      an internal I/O error during the smtp(8) to tlsproxy(8)
+      handshake. Found by Coverity, reported by Jaroslav Skarvada.
+      Based on fix by Viktor Dukhovni. File: tls/tls_proxy_client_scan.c.
+
+    - Null pointer bug (introduced: Postfix 3.0) and memory leak
+      (introduced: Postfix 3.4) after an inline: table syntax
+      error in main.cf or master.cf. Found by Coverity, reported
+      by Jaroslav Skarvada. Based on fix by Viktor Dukhovni. File:
+      util/dict_inline.c.
+
+    - Incomplete null pointer check (introduced: Postfix 2.10)
+      after truncated HaProxy version 1 handshake message. Found
+      by Coverity, reported by Jaroslav Skarvada. Fix by Viktor
+      Dukhovni. File: global/haproxy_srvr.c.
+
+    - Missing null pointer check (introduced: Postfix alpha) after
+      null argv[0] value. File: global/mail_task.c.
+
+  * 3.4.21
+    - Bugfix (introduced: Postfix 2.11): the command "postmap
+      lmdb:/file/name" handled duplicate keys ungracefully,
+      discarding entries stored up to and including the duplicate
+      key, and causing a double free() call with lmdb versions
+      0.9.17 and later. Reported by Adi Prasaja; double free()
+      root cause analysis by Howard Chu. File: util/slmdb.c.
+
+    - Typo (introduced: Postfix 3.4): silent_discard should be
+      silent-discard. File: proto/BDAT_README.html.
+
+    - Support for Postfix 3.6 compatibility_level syntax, to avoid
+      fatal runtime errors when rolling back from Postfix 3.6 to
+      an earlier supported version, or when sharing Postfix 3.6
+      configuration files with an earlier supported Postfix
+      version. File: global/mail_params.c.
+
+  * 3.4.22
+    - Bugfix (introduced: Postfix 3.4): the texthash: map
+      implementation did not support "postmap -F" behavior.
+      Reported by Christopher Gurnee, who also found the missing
+      code in the postmap source. File: util/dict_thash.c.
+
+    - Bugfix (introduced: 1999, Postfix 2.11) latent false "Result too
+      large" (ERANGE) errors because an strtol() call had no 'errno
+      = 0' statement before the call. Back-ported from Postfix 3.6.
+      Files: postscreen/postscreen_tests.c, util/mac_expand.c.
+
+    - Bugfix (introduced: Postfix 3.3): "null pointer read" error
+      in the cleanup daemon when "header_from_format = standard"
+      (the default as of Postfix 3.3) and email was submitted
+      with /usr/sbin/sendmail without From: header, and an all-space
+      full name was specified in 1) the password file, 2) with
+      "sendmail -F", or 3) with the NAME environment variable.
+      Found by Renaud Metrich. File: cleanup/cleanup_message.c.
+      (Closes: #968057)
+
+    - Bugfix (introduced: 1999): the Postfix SMTP server was
+      sending all session transcripts to the error_notice_recipient,
+      instead of sending transcripts of bounced mail to the
+      bounce_notice_recipient. File: smtpd/smtpd_chat.c.
+
+    - Bugfix (introduced: Postfix 2.4): false "too many reverse
+      jump" warnings in the showq daemon. The loop detection code
+      was comparing memory addresses instead of queue file names.
+      It now properly compares strings. Reported by Mehmet Avcioglu.
+      File: global/record.c.
+
+  * 3.4.23
+    - Bitrot: OpenSSL 3.x requires const. File: tls/tls_misc.c.
+
+    - Bugfix (bug introduced: Postfix 2.10): postconf -x produced
+      incorrect output, because different functions were implicitly
+      sharing a buffer for intermediate results. Reported
+      by raf, root cause analysis by Viktor Dukhovni. File:
+      postconf/postconf_builtin.c.
+
+    - Bugfix (problem introduced: Postfix 2.11): check_ccert_access
+      worked as expected, but produced a spurious warning when
+      Postfix was built without SASL support. Fix by Brad Barden.
+      File: smtpd/smtpd_check.c.
+
+    - Bugfix (introduced: Postfix 2.4): queue file corruption
+      after a Milter (for example, MIMEDefang) made a request to
+      replace the message body with a copy of that message body
+      plus additional text (for example, a SpamAssassin report).
+
+      The most likely impacts were a) the queue manager reporting
+      a fatal error resulting in email delivery delays, or b) the
+      queue manager reporting the corruption and moving the message
+      to the corrupt queue for damaged messages.
+
+      However, a determined adversary could craft an email message
+      that would trigger the bug, and insert a content filter
+      destination or a redirect email address into its queue file.
+      Postfix would then deliver the message headers there, in
+      most cases without delivering the message body. With enough
+      experimentation, an attacker could make Postfix deliver
+      both the message headers and body.
+
+      The details of a successful attack depend on the Milter
+      implementation, and on the Postfix and Milter configuration
+      details; these can be determined remotely through
+      experimentation.  Failed experiments may be detected when
+      the queue manager terminates with a fatal error, or when
+      the queue manager moves damaged files to the "corrupt" queue
+      as evidence.
+
+      Technical details: when Postfix executes a "replace body"
+      Milter request it will reuse queue file storage that was
+      used by the existing email message body. If the new body
+      is larger, Postfix will append body content to the end of
+      the queue file. The corruption happened when a Milter (for
+      example, MIMEDefang) made a request to replace the body of
+      a message with a new body that contained a copy of the
+      original body plus some new text, and the original body
+      contained a line longer than $line_length_limit bytes (for
+      example, an image encoded in base64 without hard or soft
+      line breaks). In queue files, Postfix stores a long text
+      line as multiple records with up to $line_length_limit bytes
+      each. Unfortunately, Postfix's "replace body" support did
+      not account for the additional queue file space needed to
+      store the second etc.  record headers. And thus, the last
+      record(s) of a long text line could overwrite one or more
+      queue file records immediately after the space that was
+      previously occupied by the original message body.
+
+      Problem report by Benoît Panizzon.
+
+  * Fix duplicate bounce_notice_recipient entries in postconf output. 
+    Closes: #999694
+
+  [Andreas Hasenack]
+
+  * Fix autopkgtest so it works and uses python3
+
+ -- Scott Kitterman <scott@kitterman.com>  Fri, 07 Jan 2022 11:04:17 -0500
+
 postfix (3.4.14-0+deb10u1) buster; urgency=medium
 
   [Cody Brownstein]
diff -Nru postfix-3.4.14/debian/ip-up.d postfix-3.4.23/debian/ip-up.d
--- postfix-3.4.14/debian/ip-up.d	2020-06-29 21:13:50.000000000 -0400
+++ postfix-3.4.23/debian/ip-up.d	2022-01-07 10:56:50.000000000 -0500
@@ -38,6 +38,7 @@
 # hanging around.  Yes, sendmail is a symlink...
 if [ -n "$RUNNING" ]; then
 	if [ -x /usr/sbin/sendmail ]; then
-		/usr/sbin/sendmail -q >/dev/null 2>&1
+		# Don't propagate the exit code on failure; cf. #959864
+		/usr/sbin/sendmail -q >/dev/null 2>&1 || true
 	fi
 fi
diff -Nru postfix-3.4.14/debian/patches/02_kfreebsd_support.diff postfix-3.4.23/debian/patches/02_kfreebsd_support.diff
--- postfix-3.4.14/debian/patches/02_kfreebsd_support.diff	2020-06-29 21:33:10.000000000 -0400
+++ postfix-3.4.23/debian/patches/02_kfreebsd_support.diff	2022-01-07 11:04:17.000000000 -0500
@@ -1,8 +1,8 @@
-Index: postfix-dev/makedefs
+Index: postfix/makedefs
 ===================================================================
---- postfix-dev.orig/makedefs	2019-03-01 11:06:34.581697003 -0500
-+++ postfix-dev/makedefs	2019-03-01 11:06:34.577697003 -0500
-@@ -595,8 +595,8 @@
+--- postfix.orig/makedefs
++++ postfix/makedefs
+@@ -595,8 +595,8 @@ EOF
  		: ${SHLIB_ENV="LD_LIBRARY_PATH=`pwd`/lib"}
  		: ${PLUGIN_LD="${CC-gcc} -shared"}
  		;;
diff -Nru postfix-3.4.14/debian/patches/06_debian_paths.diff postfix-3.4.23/debian/patches/06_debian_paths.diff
--- postfix-3.4.14/debian/patches/06_debian_paths.diff	2020-06-29 21:33:10.000000000 -0400
+++ postfix-3.4.23/debian/patches/06_debian_paths.diff	2022-01-07 11:04:17.000000000 -0500
@@ -46,9 +46,9 @@
  #
 Index: postfix-dev/makedefs
 ===================================================================
---- postfix-dev.orig/makedefs	2019-03-01 11:19:20.961713352 -0500
-+++ postfix-dev/makedefs	2019-03-01 11:19:20.953713352 -0500
-@@ -496,11 +496,18 @@
+--- postfix.orig/makedefs
++++ postfix/makedefs
+@@ -496,11 +496,18 @@ case "$SYSTEM.$RELEASE" in
  			exit 1
  		    fi
  		    SYSLIBS="-ldb"
@@ -68,7 +68,7 @@
  		    do
  			test -e $lib/lib$name.a -o -e $lib/lib$name.so && {
  			    SYSLIBS="$SYSLIBS -l$name"
-@@ -575,11 +582,18 @@
+@@ -575,11 +582,18 @@ EOF
  			exit 1
  		    fi
  		    SYSLIBS="-ldb"
@@ -88,7 +88,7 @@
  		    do
  			test -e $lib/lib$name.a -o -e $lib/lib$name.so && {
  			    SYSLIBS="$SYSLIBS -l$name"
-@@ -613,11 +627,18 @@
+@@ -613,11 +627,18 @@ EOF
  			exit 1
  		    fi
  		    SYSLIBS="-ldb"
diff -Nru postfix-3.4.14/debian/patches/10_openssl_version_check.diff postfix-3.4.23/debian/patches/10_openssl_version_check.diff
--- postfix-3.4.14/debian/patches/10_openssl_version_check.diff	2020-06-29 21:33:10.000000000 -0400
+++ postfix-3.4.23/debian/patches/10_openssl_version_check.diff	2022-01-07 11:04:17.000000000 -0500
@@ -1,8 +1,8 @@
-Index: postfix-dev/src/tls/tls_misc.c
+Index: postfix/src/tls/tls_misc.c
 ===================================================================
---- postfix-dev.orig/src/tls/tls_misc.c	2019-03-01 11:20:21.017714633 -0500
-+++ postfix-dev/src/tls/tls_misc.c	2019-03-01 11:20:21.009714633 -0500
-@@ -1215,26 +1215,7 @@
+--- postfix.orig/src/tls/tls_misc.c
++++ postfix/src/tls/tls_misc.c
+@@ -1258,26 +1258,7 @@ static void tls_version_split(unsigned l
  
  void    tls_check_version(void)
  {
diff -Nru postfix-3.4.14/debian/patches/12_add_bind_now_and_relro_to_pie.diff postfix-3.4.23/debian/patches/12_add_bind_now_and_relro_to_pie.diff
--- postfix-3.4.14/debian/patches/12_add_bind_now_and_relro_to_pie.diff	2020-06-29 21:33:10.000000000 -0400
+++ postfix-3.4.23/debian/patches/12_add_bind_now_and_relro_to_pie.diff	2022-01-07 11:04:17.000000000 -0500
@@ -13,9 +13,9 @@
 
 Index: postfix-dev/makedefs
 ===================================================================
---- postfix-dev.orig/makedefs	2019-03-01 11:42:07.053742494 -0500
-+++ postfix-dev/makedefs	2019-03-01 11:42:07.045742494 -0500
-@@ -1213,7 +1213,7 @@
+--- postfix.orig/makedefs
++++ postfix/makedefs
+@@ -1213,7 +1213,7 @@ case "$pie" in
         case " $CCARGS " in
           *" $CCARGS_PIE "*) CCARGS_PIE=;;
         esac
diff -Nru postfix-3.4.14/debian/patches/30_shared_libs.diff postfix-3.4.23/debian/patches/30_shared_libs.diff
--- postfix-3.4.14/debian/patches/30_shared_libs.diff	2020-06-29 21:33:10.000000000 -0400
+++ postfix-3.4.23/debian/patches/30_shared_libs.diff	2022-01-07 11:04:17.000000000 -0500
@@ -1,8 +1,8 @@
-Index: postfix-dev/src/dns/Makefile.in
+Index: postfix/src/dns/Makefile.in
 ===================================================================
---- postfix-dev.orig/src/dns/Makefile.in	2019-03-01 11:42:16.641742699 -0500
-+++ postfix-dev/src/dns/Makefile.in	2019-03-01 11:42:16.637742699 -0500
-@@ -63,7 +63,7 @@
+--- postfix.orig/src/dns/Makefile.in
++++ postfix/src/dns/Makefile.in
+@@ -63,7 +63,7 @@ root_tests:
  $(LIB):	$(OBJS)
  	$(AR) $(ARFL) $(LIB) $?
  	$(RANLIB) $(LIB)
@@ -11,11 +11,11 @@
  
  $(LIB_DIR)/$(LIB): $(LIB)
  	cp $(LIB) $(LIB_DIR)
-Index: postfix-dev/src/global/Makefile.in
+Index: postfix/src/global/Makefile.in
 ===================================================================
---- postfix-dev.orig/src/global/Makefile.in	2019-03-01 11:42:16.641742699 -0500
-+++ postfix-dev/src/global/Makefile.in	2019-03-01 11:42:16.637742699 -0500
-@@ -144,7 +144,7 @@
+--- postfix.orig/src/global/Makefile.in
++++ postfix/src/global/Makefile.in
+@@ -144,7 +144,7 @@ test:	$(TESTPROG)
  $(LIB):	$(OBJS)
  	$(AR) $(ARFL) $(LIB) $?
  	$(RANLIB) $(LIB)
@@ -24,11 +24,11 @@
  
  $(LIB_DIR)/$(LIB): $(LIB)
  	cp $(LIB) $(LIB_DIR)
-Index: postfix-dev/src/master/Makefile.in
+Index: postfix/src/master/Makefile.in
 ===================================================================
---- postfix-dev.orig/src/master/Makefile.in	2019-03-01 11:42:16.641742699 -0500
-+++ postfix-dev/src/master/Makefile.in	2019-03-01 11:42:16.637742699 -0500
-@@ -44,7 +44,8 @@
+--- postfix.orig/src/master/Makefile.in
++++ postfix/src/master/Makefile.in
+@@ -44,7 +44,8 @@ root_tests:
  $(LIB):	$(LIB_OBJ)
  	$(AR) $(ARFL) $(LIB) $?
  	$(RANLIB) $(LIB)
@@ -38,11 +38,11 @@
  
  $(LIB_DIR)/$(LIB): $(LIB)
  	cp $(LIB) $(LIB_DIR)/$(LIB)
-Index: postfix-dev/src/tls/Makefile.in
+Index: postfix/src/tls/Makefile.in
 ===================================================================
---- postfix-dev.orig/src/tls/Makefile.in	2019-03-01 11:42:16.641742699 -0500
-+++ postfix-dev/src/tls/Makefile.in	2019-03-01 11:42:16.637742699 -0500
-@@ -71,7 +71,8 @@
+--- postfix.orig/src/tls/Makefile.in
++++ postfix/src/tls/Makefile.in
+@@ -81,7 +81,8 @@ root_tests:
  $(LIB):	$(OBJS)
  	$(AR) $(ARFL) $(LIB) $?
  	$(RANLIB) $(LIB)
diff -Nru postfix-3.4.14/debian/patches/70_postfix-check.diff postfix-3.4.23/debian/patches/70_postfix-check.diff
--- postfix-3.4.14/debian/patches/70_postfix-check.diff	2020-06-29 21:13:50.000000000 -0400
+++ postfix-3.4.23/debian/patches/70_postfix-check.diff	2022-01-07 11:04:17.000000000 -0500
@@ -1,8 +1,8 @@
-Index: postfix-dev/conf/postfix-script
+Index: postfix/conf/postfix-script
 ===================================================================
---- postfix-dev.orig/conf/postfix-script	2019-03-01 11:47:22.941749233 -0500
-+++ postfix-dev/conf/postfix-script	2019-03-01 11:47:22.937749233 -0500
-@@ -341,9 +341,14 @@
+--- postfix.orig/conf/postfix-script
++++ postfix/conf/postfix-script
+@@ -341,9 +341,17 @@ check-warn)
  	find $todo ! -user root \
  	    -exec $WARN not owned by root: {} \;
  
@@ -12,7 +12,10 @@
  	    -exec $WARN group or other writable: {} \;
  
 +	find $todo -type l | while read f; do \
-+	    readlink "$f" | grep -q / && $WARN symlink leaves directory: "$f"; \
++	    # makedefs out known to be a symlink and OK
++	    if [ "$f" != "/etc/postfix/./makedefs.out" ]; then \
++	      readlink "$f" | grep -q / && $WARN symlink leaves directory: "$f"; \
++	    fi \
 +	done; \
 +
  	# Check Postfix mail_owner-owned directory tree owner/permissions.
diff -Nru postfix-3.4.14/debian/patches/postfix-dup-postconf.patch postfix-3.4.23/debian/patches/postfix-dup-postconf.patch
--- postfix-3.4.14/debian/patches/postfix-dup-postconf.patch	1969-12-31 19:00:00.000000000 -0500
+++ postfix-3.4.23/debian/patches/postfix-dup-postconf.patch	2022-01-07 11:04:17.000000000 -0500
@@ -0,0 +1,20 @@
+Description: Fix duplicate bounce_notice_recipient entries in postconf output.
+ Bug introduced on 2021-07-08. Reported by Vincent Lefevre.
+ https://marc.info/?l=postfix-users&m=163698504624352&w=2
+Bug-Debian: https://bugs.debian.org/999694
+Author: Wietse Venema <wietse@porcupine.org>
+Last-Update: 2021-11-15
+
+Index: postfix/src/smtpd/smtpd.c
+===================================================================
+--- postfix.orig/src/smtpd/smtpd.c
++++ postfix/src/smtpd/smtpd.c
+@@ -6391,7 +6391,7 @@ int     main(int argc, char **argv)
+ 	VAR_EOD_CHECKS, DEF_EOD_CHECKS, &var_eod_checks, 0, 0,
+ 	VAR_MAPS_RBL_DOMAINS, DEF_MAPS_RBL_DOMAINS, &var_maps_rbl_domains, 0, 0,
+ 	VAR_RBL_REPLY_MAPS, DEF_RBL_REPLY_MAPS, &var_rbl_reply_maps, 0, 0,
+-	VAR_BOUNCE_RCPT, DEF_ERROR_RCPT, &var_bounce_rcpt, 1, 0,
++	VAR_BOUNCE_RCPT, DEF_BOUNCE_RCPT, &var_bounce_rcpt, 1, 0,
+ 	VAR_ERROR_RCPT, DEF_ERROR_RCPT, &var_error_rcpt, 1, 0,
+ 	VAR_REST_CLASSES, DEF_REST_CLASSES, &var_rest_classes, 0, 0,
+ 	VAR_CANONICAL_MAPS, DEF_CANONICAL_MAPS, &var_canonical_maps, 0, 0,
diff -Nru postfix-3.4.14/debian/patches/series postfix-3.4.23/debian/patches/series
--- postfix-3.4.14/debian/patches/series	2020-06-29 21:13:50.000000000 -0400
+++ postfix-3.4.23/debian/patches/series	2022-01-07 11:04:17.000000000 -0500
@@ -1,3 +1,4 @@
+postfix-dup-postconf.patch
 02_kfreebsd_support.diff
 03_ldap3_by_default.diff
 04_remove_gdbm_support.diff
diff -Nru postfix-3.4.14/debian/patches/tls_version.diff postfix-3.4.23/debian/patches/tls_version.diff
--- postfix-3.4.14/debian/patches/tls_version.diff	2020-06-29 21:13:50.000000000 -0400
+++ postfix-3.4.23/debian/patches/tls_version.diff	2022-01-07 11:04:17.000000000 -0500
@@ -1,10 +1,10 @@
-Index: postfix-dev/src/tls/tls_client.c
+Index: postfix/src/tls/tls_client.c
 ===================================================================
---- postfix-dev.orig/src/tls/tls_client.c	2019-03-01 11:47:27.949749340 -0500
-+++ postfix-dev/src/tls/tls_client.c	2019-03-01 11:47:27.945749340 -0500
-@@ -409,6 +409,9 @@
-     off |= tls_bug_bits();
-     SSL_CTX_set_options(client_ctx, off);
+--- postfix.orig/src/tls/tls_client.c
++++ postfix/src/tls/tls_client.c
+@@ -414,6 +414,9 @@ TLS_APPL_STATE *tls_client_init(const TL
+     SSL_CTX_set_min_proto_version(client_ctx, 0);
+ #endif
  
 +    /* Enable all supported protocols */
 +    SSL_CTX_set_min_proto_version(client_ctx, 0);
@@ -12,11 +12,11 @@
      /*
       * Set the call-back routine for verbose logging.
       */
-Index: postfix-dev/src/tls/tls_server.c
+Index: postfix/src/tls/tls_server.c
 ===================================================================
---- postfix-dev.orig/src/tls/tls_server.c	2019-03-01 11:47:27.949749340 -0500
-+++ postfix-dev/src/tls/tls_server.c	2019-03-01 11:47:27.945749340 -0500
-@@ -533,6 +533,9 @@
+--- postfix.orig/src/tls/tls_server.c
++++ postfix/src/tls/tls_server.c
+@@ -539,6 +539,9 @@ TLS_APPL_STATE *tls_server_init(const TL
      if (protomask != 0)
  	SSL_CTX_set_options(server_ctx, TLS_SSL_OP_PROTOMASK(protomask));
  
diff -Nru postfix-3.4.14/debian/postfix.postinst postfix-3.4.23/debian/postfix.postinst
--- postfix-3.4.14/debian/postfix.postinst	2020-06-29 21:33:10.000000000 -0400
+++ postfix-3.4.23/debian/postfix.postinst	2022-01-07 11:04:17.000000000 -0500
@@ -49,7 +49,7 @@
 	if [ $myhostname = ${myhostname%.*} ]; then
 	    if [ -f /etc/resolv.conf ]; then
 		# The resolver uses the last one found, and ignores the rest
-		mydom=$(sed -n 's/^search[[:space:]]*\([^[:space:]]*\).*/\1/p;s/^domain[[:space:]]*\([^[:space:]]*\).*/\1/p' /etc/resolv.conf | tail -1)
+		mydom=$(sed -n 's/^search[[:space:]]*\.*\([^[:space:]]*\).*/\1/p;s/^domain[[:space:]]*\.*\([^[:space:]]*\).*/\1/p' /etc/resolv.conf | tail -1)
 		myhostname="$myhostname${mydom:+.$mydom}"
 	    else
 		myhostname="$myhostname.UNKNOWN"
@@ -293,6 +293,14 @@
     rmdir /etc/postfix/maildrop 2>/dev/null
 fi
 
+# cleanup old ca-certificates.crt that should never have been in chroot #991609
+# can be removed after bookworm release
+ca_bundle=/var/spool/postfix/etc/ssl/certs/ca-certificates.crt
+if [ -f $ca_bundle ]; then
+    echo Removing unneeded chroot file $ca_bundle
+    rm -f $ca_bundle
+fi
+
 set_maildrop_perms postdrop
 
 if [ "$mailer" != "No configuration" ]; then	# [
@@ -429,7 +437,11 @@
 
     db_fget postfix/main_mailer_type changed
     if [ "$RET" = "true" ]; then
-	dtrans=smtp
+	# If the user has picked something other than sntp, keep it
+	dtrans=$(postconf -hx default_transport)
+	if [ $(postconf -hx default_transport) = error ]; then
+	    dtrans=smtp
+	fi
 	# already have mailer
 	case "$mailer" in
 	    "Local only")	val=loopback-only; dtrans=error;;
diff -Nru postfix-3.4.14/debian/README.Debian postfix-3.4.23/debian/README.Debian
--- postfix-3.4.14/debian/README.Debian	2020-06-29 21:33:10.000000000 -0400
+++ postfix-3.4.23/debian/README.Debian	2022-01-07 11:04:17.000000000 -0500
@@ -28,6 +28,10 @@
     described in the upstream documentation is order to problem integrate with
     Debian features such as using the system CA certificate bundle and proper
     chroot configuration with system libraries and services.
+2A. In the standard Debian networking configuration, postfix is not notified
+    if /etc/resolv.conf is updated, so the copy in the postfix chroot may
+    become stale.  Installation of the resolvconf package should prevent this
+    problem for networking configurations where it is an issue.
 3.  For policy reasons:
   a. SASL configuration goes in /etc/postfix/sasl
   b. myhostname=/path/to/file is supported (and used) in main.cf
diff -Nru postfix-3.4.14/debian/tests/control postfix-3.4.23/debian/tests/control
--- postfix-3.4.14/debian/tests/control	2020-06-29 21:33:10.000000000 -0400
+++ postfix-3.4.23/debian/tests/control	2022-01-07 11:04:17.000000000 -0500
@@ -1,3 +1,3 @@
 Tests: postfix
-Depends: procmail, sasl2-bin, python-pexpect, lsb-release, python2.7
+Depends: procmail, sasl2-bin, python3-pexpect, lsb-release, python3, libsasl2-modules
 Restrictions: needs-root
diff -Nru postfix-3.4.14/debian/tests/postfix postfix-3.4.23/debian/tests/postfix
--- postfix-3.4.14/debian/tests/postfix	2020-06-29 21:33:10.000000000 -0400
+++ postfix-3.4.23/debian/tests/postfix	2022-01-07 11:04:17.000000000 -0500
@@ -13,4 +13,4 @@
 apt-get install -y postfix 2>&1
 hostname --fqdn > /etc/mailname
 service postfix restart 2>&1
-python `dirname $0`/test-postfix.py 2>&1
+python3 `dirname $0`/test-postfix.py 2>&1
diff -Nru postfix-3.4.14/debian/tests/testlib.py postfix-3.4.23/debian/tests/testlib.py
--- postfix-3.4.14/debian/tests/testlib.py	2020-06-29 21:33:10.000000000 -0400
+++ postfix-3.4.23/debian/tests/testlib.py	2022-01-07 11:04:17.000000000 -0500
@@ -22,7 +22,6 @@
 import string, random, crypt, subprocess, pwd, grp,  signal, time, unittest, tempfile, shutil, os, os.path, re, glob
 import sys, socket, gzip
 from stat import *
-from encodings import string_escape
 
 import warnings
 warnings.filterwarnings('ignore', message=r'.*apt_pkg\.TagFile.*', category=DeprecationWarning)
@@ -77,7 +76,7 @@
 
 def config_copydir(path):
     if os.path.exists(path) and not os.path.isdir(path):
-        raise OSError, "'%s' is not a directory" % (path)
+        raise OSError("'%s' is not a directory" % (path))
     _restore_backup(path)
 
     pathbackup = path + '.autotest'
@@ -90,19 +89,25 @@
     if os.path.exists(path):
         _save_backup(path)
         if append:
-            contents = file(path).read() + contents
-    open(path, 'w').write(contents)
+            with open(path) as fh:
+                contents = fh.read() + contents
+    with open(path, 'w') as fh:
+        fh.write(contents)
+
 
 def config_comment(path, field):
     _save_backup(path)
     contents = ""
-    for line in file(path):
-        if re.search("^\s*%s\s*=" % (field), line):
-            line = "#" + line
-        contents += line
+    with open(path) as fh:
+        for line in fh:
+            if re.search("^\s*%s\s*=" % (field), line):
+                line = "#" + line
+            contents += line
+
+    with open(path + '.new', 'w') as new_fh:
+        new_fh.write(contents)
+    os.rename(path + '.new', path)
 
-    open(path+'.new', 'w').write(contents)
-    os.rename(path+'.new', path)
 
 def config_set(path, field, value, spaces=True):
     _save_backup(path)
@@ -112,16 +117,19 @@
     else:
         setting = '%s=%s\n' % (field, value)
     found = False
-    for line in file(path):
-        if re.search("^\s*%s\s*=" % (field), line):
-            found = True
-            line = setting
-        contents += line
+    with open(path) as fh:
+        for line in fh:
+            if re.search("^\s*%s\s*=" % (field), line):
+                found = True
+                line = setting
+            contents += line
     if not found:
         contents += setting
 
-    open(path+'.new', 'w').write(contents)
-    os.rename(path+'.new', path)
+    with open(path + '.new', 'w') as new_config:
+        new_config.write(contents)
+    os.rename(path + '.new', path)
+
 
 def config_patch(path, patch, depth=1):
     '''Patch a config file'''
@@ -155,20 +163,22 @@
 
 def require_nonroot():
     if os.geteuid() == 0:
-        print >>sys.stderr, "This series of tests should be run as a regular user with sudo access, not as root."
+        print("This series of tests should be run as a regular user with sudo access, not as root.", file=sys.stderr)
         sys.exit(1)
 
+
 def require_root():
     if os.geteuid() != 0:
-        print >>sys.stderr, "This series of tests should be run with root privileges (e.g. via sudo)."
+        print("This series of tests should be run with root privileges (e.g. via sudo).", file=sys.stderr)
         sys.exit(1)
 
+
 def require_sudo():
     if os.geteuid() != 0 or os.environ.get('SUDO_USER', None) == None:
-        print >>sys.stderr, "This series of tests must be run under sudo."
+        print("This series of tests must be run under sudo.", file=sys.stderr)
         sys.exit(1)
     if os.environ['SUDO_USER'] == 'root':
-        print >>sys.stderr, 'Please run this test using sudo from a regular user. (You ran sudo from root.)'
+        print('Please run this test using sudo from a regular user. (You ran sudo from root.)', file=sys.stderr)
         sys.exit(1)
 
 def random_string(length,lower=False):
@@ -176,9 +186,9 @@
     length.'''
 
     s = ''
-    selection = string.letters
+    selection = string.ascii_letters
     if lower:
-        selection = string.lowercase
+        selection = string.ascii_lowercase
     maxind = len(selection)-1
     for l in range(length):
         s += selection[random.randint(0, maxind)]
@@ -190,14 +200,14 @@
 
     handle, name = tempfile.mkstemp(suffix=suffix,prefix=prefix,dir=dir)
     os.close(handle)
-    handle = file(name,"w+")
+    handle = open(name,"w+")
     handle.write(contents)
     handle.flush()
     handle.seek(0)
 
     return handle, name
 
-def create_fill(path, contents, mode=0644):
+def create_fill(path, contents, mode=0o644):
     '''Safely create a page'''
     # make the temp file in the same dir as the destination file so we
     # don't get invalid cross-device link errors when we rename
@@ -243,14 +253,14 @@
 
     # get the pid
     try:
-        fd = open(pidfile, 'r')
-        pid = fd.readline().rstrip('\n')
-        fd.close()
+        with open(pidfile, 'r') as fd:
+            pid = fd.readline().rstrip('\n')
     except:
         return False
 
     return check_pid(exe, pid)
 
+
 def check_pid(exe, pid):
     '''Checks if pid is running'''
     cmdline = "/proc/%s/cmdline" % (str(pid))
@@ -259,9 +269,8 @@
 
     # get the command line
     try:
-        fd = open(cmdline, 'r')
-        tmp = fd.readline().split('\0')
-        fd.close()
+        with open(cmdline, 'r') as fd:
+            tmp = fd.readline().split('\0')
     except:
         return False
 
@@ -342,15 +351,10 @@
         return "UNKNOWN"
 
     if size > 1024*1024:
-        raise IOError, 'Could not open "%s" (too big)' % f
+        raise IOError('Could not open "%s" (too big)' % f)
 
-    try:
-        fh = open("/etc/lsb-release", 'r')
-    except:
-        raise
-
-    lines = fh.readlines()
-    fh.close()
+    with open("/etc/lsb-release", 'r') as fh:
+        lines = fh.readlines()
 
     pat = re.compile(r'DISTRIB_CODENAME')
     for line in lines:
@@ -364,8 +368,8 @@
     a textual error if it failed.'''
 
     try:
-        sp = subprocess.Popen(command, stdin=stdin, stdout=stdout, stderr=stderr, close_fds=True, preexec_fn=subprocess_setup)
-    except OSError, e:
+        sp = subprocess.Popen(command, stdin=stdin, stdout=stdout, stderr=stderr, close_fds=True, preexec_fn=subprocess_setup, universal_newlines=True)
+    except OSError as e:
         return [127, str(e)]
 
     out, outerr = sp.communicate(input)
@@ -382,7 +386,7 @@
     try:
         sp1 = subprocess.Popen(command1, stdin=stdin, stdout=subprocess.PIPE, stderr=stderr, close_fds=True)
         sp2 = subprocess.Popen(command2, stdin=sp1.stdout, stdout=subprocess.PIPE, stderr=stderr, close_fds=True)
-    except OSError, e:
+    except OSError as e:
         return [127, str(e)]
 
     out = sp2.communicate(input)[0]
@@ -468,7 +472,7 @@
            self.tmpdir = tempfile.mkdtemp(prefix='testlib', dir='/tmp')
            self.builder = testlib.TestUser()
            testlib.cmd(['chgrp', self.builder.login, self.tmpdir])
-           os.chmod(self.tmpdir, 0775)
+           os.chmod(self.tmpdir, 0o775)
 
        def tearDown(self):
            ...
@@ -488,21 +492,21 @@
            os.chdir(build_dir)
 
            # Example for typical build, adjust as necessary
-           print ""
-           print "  make clean"
+           print("")
+           print("  make clean")
            rc, report = testlib.cmd(['sudo', '-u', self.builder.login, 'make', 'clean'])
 
-           print "  configure"
+           print("  configure")
            rc, report = testlib.cmd(['sudo', '-u', self.builder.login, './configure', '--prefix=%s' % self.tmpdir, '--enable-debug'])
 
-           print "  make (will take a while)"
+           print("  make (will take a while)")
            rc, report = testlib.cmd(['sudo', '-u', self.builder.login, 'make'])
 
-           print "  make check (will take a while)",
+           print("  make check (will take a while)",)
            rc, report = testlib.cmd(['sudo', '-u', self.builder.login, 'make', 'check'])
            expected = 0
            result = 'Got exit code %d, expected %d\n' % (rc, expected)
-           self.assertEquals(expected, rc, result + report)
+           self.assertEqual(expected, rc, result + report)
 
         def test_suite_cleanup(self):
             ...
@@ -605,7 +609,7 @@
 
         expected = 0
         result = 'Got exit code %d, expected %d\n' % (rc, expected)
-        self.assertEquals(expected, rc, result + report)
+        self.assertEqual(expected, rc, result + report)
      '''
     global manager
     rc = -1
@@ -650,7 +654,7 @@
     # check for it.
     rc, report = cmd(['ps', 'x'])
     if 'kdeinit4 Running' not in report:
-        print >>sys.stderr, ("kdeinit not running (you may start/stop any KDE application then run this script again)")
+        print("kdeinit not running (you may start/stop any KDE application then run this script again)", file=sys.stderr)
         return False
     return True
 
@@ -660,7 +664,7 @@
     rc, pkg_config = cmd(['pkg-config', '--cflags', '--libs'] + libs)
     expected = 0
     if rc != expected:
-        print >>sys.stderr, 'Got exit code %d, expected %d\n' % (rc, expected)
+        print('Got exit code %d, expected %d\n' % (rc, expected), file=sys.stderr)
     assert(rc == expected)
     return pkg_config.split()
 
@@ -750,8 +754,8 @@
         # Load LSB release file
         self.lsb_release = dict()
         if not os.path.exists('/usr/bin/lsb_release') and not os.path.exists('/bin/lsb_release'):
-            raise OSError, "Please install 'lsb-release'"
-        for line in subprocess.Popen(['lsb_release','-a'],stdout=subprocess.PIPE,stderr=subprocess.PIPE).communicate()[0].splitlines():
+            raise OSError("Please install 'lsb-release'")
+        for line in subprocess.Popen(['lsb_release','-a'],stdout=subprocess.PIPE,stderr=subprocess.PIPE,universal_newlines=True).communicate()[0].splitlines():
             field, value = line.split(':',1)
             value=value.strip()
             field=field.strip()
@@ -768,7 +772,7 @@
                 self.lsb_release['Distributor ID'] = "Ubuntu"
                 self.lsb_release['Release'] = 8.04
             else:
-                raise OSError, "Unknown version of HP MIE"
+                raise OSError("Unknown version of HP MIE")
 
         # FIXME: hack to assume a most-recent release if we're not
         # running under Ubuntu.
@@ -789,7 +793,7 @@
             elif machine.startswith('arm'):
                 self.dpkg_arch = 'armel'
             else:
-                raise ValueError, "Unknown machine type '%s'" % (machine)
+                raise ValueError("Unknown machine type '%s'" % (machine))
         else:
             self.dpkg_arch = cmd(['dpkg','--print-architecture'])[1].strip()
 
@@ -800,7 +804,7 @@
         versig = '/proc/version_signature'
         if os.path.exists(versig):
             self.kernel_is_ubuntu = True
-            self.kernel_version_signature = file(versig).read().strip()
+            self.kernel_version_signature = open(versig).read().strip()
             self.kernel_version_ubuntu = self.kernel_version
         elif os.path.exists('/usr/bin/dpkg'):
             # this can easily be inaccurate but is only an issue for Dapper
@@ -829,14 +833,14 @@
             kernel = self.kernel_version_ubuntu
             if kernel != self.kernel_version_signature:
                 kernel += " (%s)" % (self.kernel_version_signature)
-            print >>sys.stdout, "Running test: '%s' distro: '%s %.2f' kernel: '%s' arch: '%s' uid: %d/%d SUDO_USER: '%s')" % ( \
+            print("Running test: '%s' distro: '%s %.2f' kernel: '%s' arch: '%s' uid: %d/%d SUDO_USER: '%s')" % (
                 sys.argv[0],
                 self.lsb_release['Distributor ID'],
                 self.lsb_release['Release'],
                 kernel,
                 self.dpkg_arch,
                 os.geteuid(), os.getuid(),
-                os.environ.get('SUDO_USER', ''))
+                os.environ.get('SUDO_USER', '')), file=sys.stdout)
             sys.stdout.flush()
 
         # Additional heuristics
@@ -848,7 +852,7 @@
         #    time.sleep(0.5)
 
     def hello(self, msg):
-        print >>sys.stderr, "Hello from %s" % (msg)
+        print("Hello from %s" % (msg), file=sys.stderr)
 # The central instance
 manager = TestlibManager()
 
@@ -885,7 +889,7 @@
         if self.lsb_release['Release'] == 8.04 and rc == 255 and len(out) > 0:
             rc = 0
         result = 'Got exit code %d, expected %d:\n%s\n' % (rc, expected, report)
-        self.assertEquals(expected, rc, result)
+        self.assertEqual(expected, rc, result)
 
         filetype = '^%s$' % (filetype)
         result = 'File type reported by file: [%s], expected regex: [%s]\n' % (out, filetype)
@@ -905,12 +909,12 @@
 
     def announce(self, text):
         if self.my_verbosity:
-            print >>sys.stdout, "(%s) " % (text),
+            print("(%s) " % (text), file=sys.stderr, end='')
             sys.stdout.flush()
 
     def make_clean(self):
         rc, output = self.shell_cmd(['make','clean'])
-        self.assertEquals(rc, 0, output)
+        self.assertEqual(rc, 0, output)
 
     def get_makefile_compiler(self):
         # Find potential compiler name
@@ -929,7 +933,7 @@
 
         compiler = self.get_makefile_compiler()
         rc, output = self.shell_cmd(['make',target])
-        self.assertEquals(rc, expected, 'rc(%d)!=%d:\n' % (rc, expected) + output)
+        self.assertEqual(rc, expected, 'rc(%d)!=%d:\n' % (rc, expected) + output)
         self.assertTrue('%s ' % (compiler) in output, 'Expected "%s":' % (compiler) + output)
         return output
 
@@ -954,7 +958,7 @@
         '''Test a shell command matches a specific exit code'''
         rc, report, out = self._testlib_shell_cmd(args, stdin=stdin, stdout=stdout, stderr=stderr)
         result = 'Got exit code %d, expected %d\n' % (rc, expected)
-        self.assertEquals(expected, rc, msg + result + report)
+        self.assertEqual(expected, rc, msg + result + report)
 
     def assertShellExitNotEquals(self, unwanted, args, stdin=None, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, msg=""):
         '''Test a shell command doesn't match a specific exit code'''
@@ -976,12 +980,12 @@
         rc, report, out = self._testlib_shell_cmd(args, stdin=stdin, stdout=stdout, stderr=stderr)
         result = 'Got exit code %d. Looking for exact text "%s" (%s)\n' % (rc, text, " ".join(args))
         if not invert:
-            self.assertEquals(text, out, msg + result + report)
+            self.assertEqual(text, out, msg + result + report)
         else:
             self.assertNotEquals(text, out, msg + result + report)
         if expected != None:
             result = 'Got exit code %d. Expected %d (%s)\n' % (rc, expected, " ".join(args))
-            self.assertEquals(rc, expected, msg + result + report)
+            self.assertEqual(rc, expected, msg + result + report)
 
     def _word_find(self, report, content, invert=False):
         '''Check for a specific string'''
@@ -994,20 +998,22 @@
 
     def _test_sysctl_value(self, path, expected, msg=None, exists=True):
         sysctl = '/proc/sys/%s' % (path)
-        self.assertEquals(exists, os.path.exists(sysctl), sysctl)
+        self.assertEqual(exists, os.path.exists(sysctl), sysctl)
         value = None
         if exists:
-            value = int(file(sysctl).read())
+            with open(sysctl) as sysctl_fd:
+                value = int(sysctl_fd.read())
             report = "%s is not %d: %d" % (sysctl, expected, value)
             if msg:
                 report += " (%s)" % (msg)
-            self.assertEquals(value, expected, report)
+            self.assertEqual(value, expected, report)
         return value
 
     def set_sysctl_value(self, path, desired):
         sysctl = '/proc/sys/%s' % (path)
         self.assertTrue(os.path.exists(sysctl),"%s does not exist" % (sysctl))
-        file(sysctl,'w').write(str(desired))
+        with open(sysctl, 'w') as sysctl_fh:
+            sysctl_fh.write(str(desired))
         self._test_sysctl_value(path, desired)
 
     def kernel_at_least(self, introduced):
@@ -1031,7 +1037,7 @@
         self.group = None
         if group:
             if group_exists(group):
-                raise ValueError, 'group name already exists'
+                raise ValueError('group name already exists')
         else:
             while(True):
                 group = random_string(7,lower=lower)
@@ -1063,11 +1069,11 @@
         self.login = None
 
         if os.geteuid() != 0:
-            raise ValueError, "You must be root to run this test"
+            raise ValueError("You must be root to run this test")
 
         if login:
             if login_exists(login):
-                raise ValueError, 'login name already exists'
+                raise ValueError('login name already exists')
         else:
             while(True):
                 login = 't' + random_string(7,lower=lower)
@@ -1114,7 +1120,7 @@
         '''Add user to the specified group name'''
         rc, report = cmd(['usermod', '-G', group, self.login])
         if rc != 0:
-            print report
+            print(report)
         assert rc == 0
 
 # Timeout handler using alarm() from John P. Speno's Pythonic Avocado
@@ -1139,6 +1145,7 @@
         signal.alarm(0)
         return result
 
+
 def main():
-    print "hi"
+    print("hi")
     unittest.main()
diff -Nru postfix-3.4.14/debian/tests/test-postfix.py postfix-3.4.23/debian/tests/test-postfix.py
--- postfix-3.4.14/debian/tests/test-postfix.py	2020-06-29 21:33:10.000000000 -0400
+++ postfix-3.4.23/debian/tests/test-postfix.py	2022-01-07 11:04:17.000000000 -0500
@@ -49,13 +49,13 @@
 
         # Move listener to localhost:2525
         conf_file = '/etc/postfix/master.cf'
-        lines = open(conf_file)
         contents = ''
-        for cfline in lines:
-            if cfline.startswith('smtp') and 'smtpd' in cfline and 'inet' in cfline:
-                contents += '127.0.0.1:2525      inet  n       -       -       -       -       smtpd\n'
-            else:
-                contents += "%s\n" % cfline
+        with open(conf_file) as fh:
+            for cfline in fh:
+                if cfline.startswith('smtp') and 'smtpd' in cfline and 'inet' in cfline:
+                    contents += '127.0.0.1:2525      inet  n       -       -       -       -       smtpd\n'
+                else:
+                    contents += "%s\n" % cfline
         testlib.config_replace(conf_file, contents, append=False)
 
         conf_file = '/etc/postfix/main.cf'
@@ -166,7 +166,7 @@
             rc, report = testlib.cmd(['postconf', '-h', 'myhostname'])
             expected = 0
             result = 'Got exit code %d, expected %d\n' % (rc, expected)
-            self.assertEquals(expected, rc, result + report)
+            self.assertEqual(expected, rc, result + report)
 
             child = pexpect.spawn('saslpasswd2 -c -u %s %s' % (report.strip(), self.user.login))
             time.sleep(0.2)
@@ -180,15 +180,15 @@
             time.sleep(0.2)
             rc = child.expect('\n', timeout=5)
             time.sleep(0.2)
-            self.assertEquals(rc, expected, "passwd returned %d" %(rc))
+            self.assertEqual(rc, expected, "passwd returned %d" %(rc))
 
             child.kill(0)
 
-            os.chmod("/etc/sasldb2", 0640)
+            os.chmod("/etc/sasldb2", 0o640)
             rc, report = testlib.cmd(['chgrp', 'postfix', '/etc/sasldb2'])
             expected = 0
             result = 'Got exit code %d, expected %d\n' % (rc, expected)
-            self.assertEquals(expected, rc, result + report)
+            self.assertEqual(expected, rc, result + report)
 
             # Force crackful perms so chroot'd postfix can talk to saslauthd
             subprocess.call(['mv', '-f', '/etc/sasldb2', '/var/spool/postfix/etc'])
@@ -205,7 +205,7 @@
         s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
         s.settimeout(5)
         s.connect(('localhost',2525))
-        greeting = s.recv(1024)
+        greeting = s.recv(1024).decode('utf-8')
         # 220 gorgon.outflux.net ESMTP Postfix (Ubuntu)
         self.assertTrue(greeting.startswith('220 '),greeting)
         self.assertTrue('ESMTP' in greeting,greeting)
@@ -225,12 +225,12 @@
         code, msg = self.s.getreply()
         reply = '%d %s' % (code, msg)
         if valid:
-            self.assertEquals(code, 252, reply)
-            self.assertTrue(address in msg, reply)
+            self.assertEqual(code, 252, reply)
+            self.assertTrue(address.encode('utf-8') in msg, reply)
         else:
-            self.assertEquals(code, 550, reply)
-            self.assertTrue('Recipient address rejected' in msg, reply)
-            self.assertTrue('<%s>' % (address) in msg, reply)
+            self.assertEqual(code, 550, reply)
+            self.assertTrue(b'Recipient address rejected' in msg, reply)
+            self.assertTrue(('<%s>' % (address)).encode('utf-8') in msg, reply)
 
     def test_10_commands(self):
         '''Basic SMTP commands'''
@@ -239,15 +239,15 @@
         # EHLO
         code, msg = self.s.ehlo()
         reply = '%d %s' % (code, msg)
-        self.assertEquals(code, 250, reply)
-        self.assertEquals(self.s.does_esmtp, 1, reply)
-        self.assertTrue('8BITMIME' in self.s.ehlo_resp, reply)
+        self.assertEqual(code, 250, reply)
+        self.assertEqual(self.s.does_esmtp, 1, reply)
+        self.assertTrue(b'8BITMIME' in self.s.ehlo_resp, reply)
         # No help available
         self.s.putcmd("help")
         code, msg = self.s.getreply()
         reply = '%d %s' % (code, msg)
-        self.assertEquals(code, 502, reply)
-        self.assertTrue('Error' in msg, reply)
+        self.assertEqual(code, 502, reply)
+        self.assertTrue(b'Error' in msg, reply)
         # VRFY addresses
         self._vrfy('address@example.com', valid=True)
         self._vrfy('does-not-exist', valid=False)
@@ -268,9 +268,9 @@
 ''' % (user_sent_to.gecos, user_sent_to.login))
         #for addr in failed.keys():
         #    print '%s %d %s' % (addr, failed[addr][0], failed[addr][1])
-        self.assertEquals(len(failed),1,failed)
-        self.assertTrue(failed.has_key('does-not-exist'),failed)
-        self.assertEquals(failed['does-not-exist'][0],550,failed)
+        self.assertEqual(len(failed),1,failed)
+        self.assertTrue('does-not-exist' in failed,failed)
+        self.assertEqual(failed['does-not-exist'][0],550,failed)
 
         # Frighteningly, postfix seems to accept email before confirming
         # a successful write to disk for the recipient!
@@ -286,7 +286,8 @@
         if spool_file == None:
             spool_file = '/var/mail/%s' % (target_spool_user.login)
         time.sleep(1)
-        contents = open(spool_file).read()
+        with open(spool_file) as fh:
+            contents = fh.read()
         # Server-side added headers...
         self.assertTrue('\nReceived: ' in contents, contents)
         if use_tls and self.lsb_release['Release'] > 6.06:
@@ -335,7 +336,8 @@
 
     def _write_forward(self, user, contents):
         forward_filename = '/home/%s/.forward' % (user.login)
-        open(forward_filename,'w').write(contents)
+        with open(forward_filename,'w') as fh:
+            fh.write(contents)
         os.chown(forward_filename, user.uid, user.gid)
 
     def test_10_sending_mail_forward_normal(self):
@@ -358,7 +360,7 @@
 /bin/cat > "%s"
 ''' % (mbox_name),prefix='test-postfix.redir-')
         redir.close()
-        os.chmod(redir_name,0755)
+        os.chmod(redir_name,0o755)
     
         self._write_forward(self.user,'|%s\n' % (redir_name))
 
@@ -373,15 +375,16 @@
 
         # First, create our "target" file
         secret = '/root/secret.txt'
-        open(secret,'w').write('Secret information\n')
-        os.chmod(secret, 0700)
+        with open(secret,'w') as fh:
+            fh.write('Secret information\n')
+        os.chmod(secret, 0o700)
 
         # Now, create a symlink to the target (we're going to use /var/tmp
         # since we're assuming it, /root, /var/mail are on the same filesystem.
         # For most chroot testing, /tmp is mounted from the real machine.
         if os.path.exists('/var/tmp/secret.link'):
             os.unlink('/var/tmp/secret.link')
-        self.assertEquals(subprocess.call(['su','-c','ln -s /root/secret.txt /var/tmp/secret.link',self.user.login]),0,"Symlink creation")
+        self.assertEqual(subprocess.call(['su','-c','ln -s /root/secret.txt /var/tmp/secret.link',self.user.login]),0,"Symlink creation")
 
         # Now, the hardlink, which in ubuntu's case needs to be done by root.
         os.link('/var/tmp/secret.link','/var/mail/%s' % (self.user.login))
@@ -394,12 +397,13 @@
 
 Hello, nice to pwn you.
 ''' % (self.user.gecos, self.user.login))
-        self.assertEquals(len(failed),0,failed)
+        self.assertEqual(len(failed),0,failed)
 
         # Pause for delivery
         time.sleep(2)
 
-        contents = open(secret).read()
+        with open(secret) as fh:
+            contents = fh.read()
         # Clean up before possible failures
         os.unlink('/var/mail/%s' % (self.user.login))
         os.unlink('/var/tmp/secret.link')
@@ -421,9 +425,9 @@
         # has mech
         code, msg = self.s.ehlo()
         reply = '%d %s' % (code, msg)
-        self.assertEquals(code, 250, reply)
-        self.assertEquals(self.s.does_esmtp, 1, reply)
-        self.assertTrue('%s' % mech in self.s.ehlo_resp, reply)
+        self.assertEqual(code, 250, reply)
+        self.assertEqual(self.s.does_esmtp, 1, reply)
+        self.assertTrue(mech.encode('utf-8') in self.s.ehlo_resp, reply)
         return reply
 
     def test_20_sasldb_cram_md5(self):
@@ -496,14 +500,14 @@
         rc, report = testlib.cmd(['postconf', 'smtpd_sasl_auth_enable'])
         expected = 0
         result = 'Got exit code %d, expected %d\n' % (rc, expected)
-        self.assertEquals(expected, rc, result + report)
+        self.assertEqual(expected, rc, result + report)
         self.assertTrue('yes' in report, "Could not find 'yes' in report:\n%s" % report)
 
         if self.lsb_release['Release'] > 6.06:
             rc, report = testlib.cmd(['postconf', 'smtpd_sasl_type'])
             expected = 0
             result = 'Got exit code %d, expected %d\n' % (rc, expected)
-            self.assertEquals(expected, rc, result + report)
+            self.assertEqual(expected, rc, result + report)
             self.assertTrue('cyrus' in report, "Could not find 'cyrus' in report:\n%s" % report)
 
         # ehlo
@@ -512,11 +516,11 @@
 
         code, msg = self.s.docmd("AUTH", "CRAM-MD5")
         reply = '%d %s' % (code, msg)
-        self.assertEquals(code, 334, reply)
+        self.assertEqual(code, 334, reply)
 
         code, msg = self.s.docmd("*")
         reply = '%d %s' % (code, msg)
-        self.assertEquals(code, 501, reply)
+        self.assertEqual(code, 501, reply)
 
         error = False
         try:
@@ -525,7 +529,7 @@
             error = True
         self.assertFalse(error, "server disconnected")
         reply = '%d %s' % (code, msg)
-        self.assertEquals(code, 334, reply)
+        self.assertEqual(code, 334, reply)
 
     def test_99_restore(self):
         '''Restore configuration'''
diff -Nru postfix-3.4.14/HISTORY postfix-3.4.23/HISTORY
--- postfix-3.4.14/HISTORY	2020-06-27 17:15:17.000000000 -0400
+++ postfix-3.4.23/HISTORY	2021-11-07 18:09:13.000000000 -0500
@@ -24466,3 +24466,259 @@
 	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.
+
+20200710
+
+	Bugfix (introduced: Postfix 3.0): minor memory leaks in the
+	Postfix TLS library, found during tests. File: tls/tls_misc.c.
+
+20200712
+
+	Bugfix (introduced: Postfix 3.0): 4kbyte per session memory
+	leak in the Postfix TLS library, found during tests. File:
+	tls/tls_misc.c.
+
+20200724
+
+	Workaround for distros that override Postfix protocol
+	settings in a system-wide OpenSSL configuration file, causing
+	interoperability problems after an OS update. File:
+	tls/tls_client.c, tls/tls_server.c.
+
+20200726
+
+	Bugfix (introduced: Postfix 3.4.15): part of a memory leak
+	fix was backported to the wrong place. File: tls/tls_misc.c.
+
+	The Postfix 3.4.15 workaround did not explictly override
+	the system-wide OpenSSL configuration of allowed TLS protocol
+	versions, for sessions where the remote SMTP client sends
+	SNI. It's better to be safe than sorry. File: tls/tls_server.c.
+
+20200821
+
+	Bugfix (introduced: Postfix 3.4, already fixed in Postfix
+	3.6): tlsproxy(8) was using the wrong DANE macro for
+	connections with DANE trust anchors or with non-DANE trust
+	anchors (WTF: Thorsten Habich found this bug in the use
+	case that has nothing to do with DANE). This resulted in a
+	global certificate verify function pointer race, between
+	TLS handshakes that use TLS trust achors and handshakes
+	that use PKI. No memory was corrupted in the course of all
+	this.  Viktor Dukhovni. File: tlsproxy/tlsproxy.c.
+
+	Cleanup: the posttls-finger '-X' option reported a false
+	conflict with '-r'.  File: posttls-finger/posttls-finger.c.
+
+20200830
+
+	Bugfix (introduced: Postfix 2.0): smtp_sasl_mechanism_filter
+	ignored table lookup errors, treating them as 'not found'.
+	Found during Postfix 3.6 development. File: smtp/smtp_sasl_proto.c.
+
+202000920
+
+	Bugfix (introduced: Postfix 2.3): when deleting a recipient
+	with a milter, delete the recipient from the duplicate
+	filter, so that the recipient can be added back. Backported
+	from Postfix 3.6. Files: global/been_here.[hc],
+	cleanup/cleanup_milter.c.
+
+20200925
+
+	Bugfix (introduced: before Postfix alpha): the code that
+	looks for Delivered-To: headers ignored headers longer than
+	$line_length_limit. Backported from Postfix 3.6. File:
+	global/delivered_hdr.c.
+
+20201011
+
+	Bugfix (introduced: Postfix 2.8): save a copy of the
+	postscreen_dnsbl_reply_map lookup result. This has no effect
+	when the recommended texthash: look table is used, but it
+	may avoid stale data with other lookup tables. File:
+	postscreen/postscreen_dnsbl.c.
+
+20201022
+
+	Bugfix (introduced: Postfix 2.2): after processing an
+	XCCLIENT command, the smtps service was waiting for a TLS
+	handshake. Found by Aki Tuomi. File: smtpd/smtpd.c.
+
+20201025
+
+	Bugfix (introduced: Postfix 2.3): static maps did not free
+	their casefolding buffer. File: util/dict_static.c.
+
+20210116
+
+	Feature: when a Postfix program makes a DNS query that
+	requests DNSSEC validation (usually for Postfix DANE support)
+	but the DNS response is not DNSSEC validated, Postfix will
+	send a DNS query configured with the "dnssec_probe" parameter
+	to determine if DNSSEC support is available, and logs a
+	warning if it is not. By default, the probe has type "ns"
+	and domain name ".". The probe is sent once per process
+	lifetime. Files: dns/dns.h, dns/dns_lookup.c, dns/dns_sec.c,
+	test_dns_lookup.c, global/mail_params.[hc], mantools/postlink.
+
+	The makedefs script no longer disables DNSSEC when Postfix
+	is built with libc-musl. Instead Postfix will rely on the
+	new dnssec_probe feature, and will log a warning when Postfix
+	requests DNSSEC validation, but the infrastructure does not
+	validate DNSSEC signatures. File: makedefs.
+
+        The default "smtp_tls_dane_insecure_mx_policy = dane" was
+        causing unnecessary dnssec_probe activity. The default is now
+        "dane" when smtp_tls_security_level is "dane", otherwise it is
+        "may". File: global/mail_params.h.
+
+20210411
+
+	Missing null pointer checks (introduced: Postfix 3.4) after
+	an internal I/O error during the smtp(8) to tlsproxy(8)
+	handshake. Found by Coverity, reported by Jaroslav Skarvada.
+	Based on fix by Viktor Dukhovni. File: tls/tls_proxy_client_scan.c.
+
+	Null pointer bug (introduced: Postfix 3.0) and memory leak
+	(introduced: Postfix 3.4) after an inline: table syntax
+	error in main.cf or master.cf. Found by Coverity, reported
+	by Jaroslav Skarvada. Based on fix by Viktor Dukhovni. File:
+	util/dict_inline.c.
+
+	Incomplete null pointer check (introduced: Postfix 2.10)
+	after truncated HaProxy version 1 handshake message. Found
+	by Coverity, reported by Jaroslav Skarvada. Fix by Viktor
+	Dukhovni. File: global/haproxy_srvr.c.
+
+	Missing null pointer check (introduced: Postfix alpha) after
+	null argv[0] value. File: global/mail_task.c.
+
+20210601
+
+	Bugfix (introduced: Postfix 2.11): the command "postmap
+	lmdb:/file/name" handled duplicate keys ungracefully,
+	discarding entries stored up to and including the duplicate
+	key, and causing a double free() call with lmdb versions
+	0.9.17 and later. Reported by Adi Prasaja; double free()
+	root cause analysis by Howard Chu. File: util/slmdb.c.
+
+20210609
+
+	Typo (introduced: Postfix 3.4): silent_discard should be
+	silent-discard. File: proto/BDAT_README.html.
+
+20210612
+
+	Support for Postfix 3.6 compatibility_level syntax, to avoid
+	fatal runtime errors when rolling back from Postfix 3.6 to
+	an earlier supported version, or when sharing Postfix 3.6
+	configuration files with an earlier supported Postfix
+	version. File: global/mail_params.c.
+
+20210615
+
+	Bugfix (introduced: Postfix 3.4): the texthash: map
+	implementation did not support "postmap -F" behavior.
+	Reported by Christopher Gurnee, who also found the missing
+	code in the postmap source. File: util/dict_thash.c.
+
+20210623
+
+	Bugfix (introduced: 1999, Postfix 2.11) latent false "Result too
+	large" (ERANGE) errors because an strtol() call had no 'errno
+	= 0' statement before the call. Back-ported from Postfix 3.6.
+	Files: postscreen/postscreen_tests.c, util/mac_expand.c.
+
+20210705
+
+	Bugfix (introduced: Postfix 3.3): "null pointer read" error
+	in the cleanup daemon when "header_from_format = standard"
+	(the default as of Postfix 3.3) and email was submitted
+	with /usr/sbin/sendmail without From: header, and an all-space
+	full name was specified in 1) the password file, 2) with
+	"sendmail -F", or 3) with the NAME environment variable.
+	Found by Renaud Metrich. File: cleanup/cleanup_message.c.
+
+20210708
+
+	Bugfix (introduced: 1999): the Postfix SMTP server was
+	sending all session transcripts to the error_notice_recipient,
+	instead of sending transcripts of bounced mail to the
+	bounce_notice_recipient. File: smtpd/smtpd_chat.c.
+
+20210713
+
+	Bugfix (introduced: Postfix 2.4): false "too many reverse
+	jump" warnings in the showq daemon. The loop detection code
+	was comparing memory addresses instead of queue file names.
+	It now properly compares strings. Reported by Mehmet Avcioglu.
+	File: global/record.c.
+
+20210811
+
+	Bitrot: OpenSSL 3.x requires const. File: tls/tls_misc.c.
+
+20210925
+
+	Bugfix (bug introduced: Postfix 2.10): postconf -x produced
+	incorrect output, because different functions were implicitly
+	sharing a buffer for intermediate results. Reported
+	by raf, root cause analysis by Viktor Dukhovni. File:
+	postconf/postconf_builtin.c.
+
+20211030
+
+	Bugfix (problem introduced: Postfix 2.11): check_ccert_access
+	worked as expected, but produced a spurious warning when
+	Postfix was built without SASL support. Fix by Brad Barden.
+	File: smtpd/smtpd_check.c.
+
+20211105
+
+	Bugfix (introduced: Postfix 2.4): queue file corruption
+	after a Milter (for example, MIMEDefang) made a request to
+	replace the message body with a copy of that message body
+	plus additional text (for example, a SpamAssassin report).
+
+	The most likely impacts were a) the queue manager reporting
+	a fatal error resulting in email delivery delays, or b) the
+	queue manager reporting the corruption and moving the message
+	to the corrupt queue for damaged messages.
+
+	However, a determined adversary could craft an email message
+	that would trigger the bug, and insert a content filter
+	destination or a redirect email address into its queue file.
+	Postfix would then deliver the message headers there, in
+	most cases without delivering the message body. With enough
+	experimentation, an attacker could make Postfix deliver
+	both the message headers and body.
+
+	The details of a successful attack depend on the Milter
+	implementation, and on the Postfix and Milter configuration
+	details; these can be determined remotely through
+	experimentation.  Failed experiments may be detected when
+	the queue manager terminates with a fatal error, or when
+	the queue manager moves damaged files to the "corrupt" queue
+	as evidence.
+
+	Technical details: when Postfix executes a "replace body"
+	Milter request it will reuse queue file storage that was
+	used by the existing email message body. If the new body
+	is larger, Postfix will append body content to the end of
+	the queue file. The corruption happened when a Milter (for
+	example, MIMEDefang) made a request to replace the body of
+	a message with a new body that contained a copy of the
+	original body plus some new text, and the original body
+	contained a line longer than $line_length_limit bytes (for
+	example, an image encoded in base64 without hard or soft
+	line breaks). In queue files, Postfix stores a long text
+	line as multiple records with up to $line_length_limit bytes
+	each. Unfortunately, Postfix's "replace body" support did
+	not account for the additional queue file space needed to
+	store the second etc.  record headers. And thus, the last
+	record(s) of a long text line could overwrite one or more
+	queue file records immediately after the space that was
+	previously occupied by the original message body.
+
+	Problem report by Benoît Panizzon.
diff -Nru postfix-3.4.14/html/BDAT_README.html postfix-3.4.23/html/BDAT_README.html
--- postfix-3.4.14/html/BDAT_README.html	2019-02-10 17:47:44.000000000 -0500
+++ postfix-3.4.23/html/BDAT_README.html	2021-06-13 15:03:55.000000000 -0400
@@ -51,7 +51,7 @@
     # The logging alternative:
     <a href="postconf.5.html#smtpd_discard_ehlo_keywords">smtpd_discard_ehlo_keywords</a> = chunking
     # The non-logging alternative:
-    <a href="postconf.5.html#smtpd_discard_ehlo_keywords">smtpd_discard_ehlo_keywords</a> = chunking, silent_discard
+    <a href="postconf.5.html#smtpd_discard_ehlo_keywords">smtpd_discard_ehlo_keywords</a> = chunking, silent-discard
 </pre>
 </blockquote>
 
diff -Nru postfix-3.4.14/html/lmtp.8.html postfix-3.4.23/html/lmtp.8.html
--- postfix-3.4.14/html/lmtp.8.html	2019-06-29 19:30:31.000000000 -0400
+++ postfix-3.4.23/html/lmtp.8.html	2021-01-16 18:52:00.000000000 -0500
@@ -309,6 +309,13 @@
               IPv6 addresses, ensure that the Postfix SMTP client can try both
               address types before it runs into the <a href="postconf.5.html#smtp_mx_address_limit">smtp_mx_address_limit</a>.
 
+       Available in Postfix 3.4.19 and later:
+
+       <b><a href="postconf.5.html#dnssec_probe">dnssec_probe</a> (ns:.)</b>
+              The  DNS query type (default: "ns") and DNS query name (default:
+              ".") that Postfix may use to determine whether DNSSEC validation
+              is available.
+
 <b>MIME PROCESSING CONTROLS</b>
        Available in Postfix version 2.0 and later:
 
diff -Nru postfix-3.4.14/html/postconf.5.html postfix-3.4.23/html/postconf.5.html
--- postfix-3.4.14/html/postconf.5.html	2020-05-09 16:21:56.000000000 -0400
+++ postfix-3.4.23/html/postconf.5.html	2021-01-17 10:13:32.000000000 -0500
@@ -3029,6 +3029,66 @@
 
 </DD>
 
+<DT><b><a name="dnssec_probe">dnssec_probe</a>
+(default: ns:.)</b></DT><DD>
+
+<p> The DNS query type (default: "ns") and DNS query name (default:
+".") that Postfix may use to determine whether DNSSEC validation
+is available.
+</p>
+
+<p> Background: DNSSEC validation is needed for Postfix DANE support;
+this ensures that Postfix receives TLSA records with secure TLS
+server certificate info. When DNSSEC validation is unavailable,
+mail deliveries using <i>opportunistic</i> DANE will not be protected
+by server certificate info in TLSA records, and mail deliveries
+using <i>mandatory</i> DANE will not be made at all. </p>
+
+<p> By default, a Postfix process will send a DNSSEC probe after
+1) the process made a DNS query that requested DNSSEC validation,
+2) the process did not receive a DNSSEC validated response to this
+query or to an earlier query, and 3) the process did not already
+send a DNSSEC probe. <p>
+
+<p> When the DNSSEC probe has no response, or when the response is
+not DNSSEC validated, Postfix logs a warning that DNSSEC validation
+may be unavailable. </p>
+
+<p> Example: </p>
+
+<pre>
+warning: DNSSEC validation may be unavailable
+warning: reason: <a href="postconf.5.html#dnssec_probe">dnssec_probe</a> 'ns:.' received a response that is not DNSSEC validated
+warning: reason: <a href="postconf.5.html#dnssec_probe">dnssec_probe</a> 'ns:.' received no response: Server failure
+</pre>
+
+<p> Possible reasons why DNSSEC validation may be unavailable: </p>
+
+<ul>
+
+<li> The local /etc/resolv.conf file specifies a DNS resolver that
+does not validate DNSSEC signatures (that's
+$<a href="postconf.5.html#queue_directory">queue_directory</a>/etc/resolv.conf when a Postfix daemon runs in a
+chroot jail).
+
+<li> The local system library does not pass on the "DNSSEC validated"
+bit to Postfix, or Postfix does not know how to ask the library to
+do that.
+
+</ul>
+
+<p> By default, the DNSSEC probe asks for the DNS root zone NS
+records, because resolvers should always have that information
+cached. If Postfix runs on a network where the DNS root zone is not
+reachable, specify a different probe, or specify an empty <a href="postconf.5.html#dnssec_probe">dnssec_probe</a>
+value to disable the feature. </p>
+
+<p> This feature was backported from Postfix 3.6 to Postfix versions
+3.5.9, 3.4.19, 3.3.16. 3.2.21. </p>
+
+
+</DD>
+
 <DT><b><a name="dont_remove">dont_remove</a>
 (default: 0)</b></DT><DD>
 
@@ -12329,7 +12389,7 @@
 </DD>
 
 <DT><b><a name="smtp_tls_dane_insecure_mx_policy">smtp_tls_dane_insecure_mx_policy</a>
-(default: dane)</b></DT><DD>
+(default: see "postconf -d" output)</b></DT><DD>
 
 <p> The TLS policy for MX hosts with "secure" TLSA records when the
 nexthop destination security level is <b>dane</b>, but the MX
@@ -12353,6 +12413,12 @@
 "Verified", because the MX host name could have been forged.  </dd>
 </dl>
 
+<p> The default setting for Postfix &ge; 3.6 is "dane" with
+"<a href="postconf.5.html#smtp_tls_security_level">smtp_tls_security_level</a> = dane", otherwise "may". This behavior
+was backported to Postfix versions 3.5.9, 3.4.19, 3.3.16. 3.2.21.
+With earlier
+Postfix versions the default setting was always "dane". </p>
+
 <p> Though with "insecure" MX records an active attacker can
 compromise SMTP transport security by returning forged MX records,
 such attacks are "tamper-evident" since any forged MX hostnames
diff -Nru postfix-3.4.14/html/smtp.8.html postfix-3.4.23/html/smtp.8.html
--- postfix-3.4.14/html/smtp.8.html	2019-06-29 19:30:31.000000000 -0400
+++ postfix-3.4.23/html/smtp.8.html	2021-01-16 18:52:00.000000000 -0500
@@ -309,6 +309,13 @@
               IPv6 addresses, ensure that the Postfix SMTP client can try both
               address types before it runs into the <a href="postconf.5.html#smtp_mx_address_limit">smtp_mx_address_limit</a>.
 
+       Available in Postfix 3.4.19 and later:
+
+       <b><a href="postconf.5.html#dnssec_probe">dnssec_probe</a> (ns:.)</b>
+              The  DNS query type (default: "ns") and DNS query name (default:
+              ".") that Postfix may use to determine whether DNSSEC validation
+              is available.
+
 <b>MIME PROCESSING CONTROLS</b>
        Available in Postfix version 2.0 and later:
 
diff -Nru postfix-3.4.14/makedefs postfix-3.4.23/makedefs
--- postfix-3.4.14/makedefs	2020-05-06 10:10:41.000000000 -0400
+++ postfix-3.4.23/makedefs	2021-01-16 18:36:30.000000000 -0500
@@ -228,19 +228,6 @@
  *) echo usage: $0 [system release] 1>&2; exit 1;;
 esac
 
-case "$SYSTEM" in
- Linux)
-    case "`PATH=/bin:/usr/bin ldd /bin/sh`" in
-     *-musl-*)
-	case "$CCARGS" in
-	 *-DNO_DNSSEC*) ;;
-	 *) echo Warning: libc-musl breaks DANE/TLSA security. 1>&2
-	    echo This build will not support DANE/TLSA. 1>&2
-	    CCARGS="$CCARGS -DNO_DNSSEC";;
-	esac;;
-    esac;;
-esac
-
 case "$SYSTEM.$RELEASE" in
    SCO_SV.3.2)	SYSTYPE=SCO5
 		# Use the native compiler by default
diff -Nru postfix-3.4.14/man/man5/postconf.5 postfix-3.4.23/man/man5/postconf.5
--- postfix-3.4.14/man/man5/postconf.5	2020-05-12 19:29:36.000000000 -0400
+++ postfix-3.4.23/man/man5/postconf.5	2021-01-17 10:13:32.000000000 -0500
@@ -1895,6 +1895,60 @@
 service performs DNS white/blacklist lookups.
 .PP
 This feature is available in Postfix 2.8 and later.
+.SH dnssec_probe (default: ns:.)
+The DNS query type (default: "ns") and DNS query name (default:
+".") that Postfix may use to determine whether DNSSEC validation
+is available.
+.PP
+Background: DNSSEC validation is needed for Postfix DANE support;
+this ensures that Postfix receives TLSA records with secure TLS
+server certificate info. When DNSSEC validation is unavailable,
+mail deliveries using \fIopportunistic\fR DANE will not be protected
+by server certificate info in TLSA records, and mail deliveries
+using \fImandatory\fR DANE will not be made at all.
+.PP
+By default, a Postfix process will send a DNSSEC probe after
+1) the process made a DNS query that requested DNSSEC validation,
+2) the process did not receive a DNSSEC validated response to this
+query or to an earlier query, and 3) the process did not already
+send a DNSSEC probe.
+.PP
+When the DNSSEC probe has no response, or when the response is
+not DNSSEC validated, Postfix logs a warning that DNSSEC validation
+may be unavailable.
+.PP
+Example:
+.PP
+.nf
+.na
+.ft C
+warning: DNSSEC validation may be unavailable
+warning: reason: dnssec_probe 'ns:.' received a response that is not DNSSEC validated
+warning: reason: dnssec_probe 'ns:.' received no response: Server failure
+.fi
+.ad
+.ft R
+.PP
+Possible reasons why DNSSEC validation may be unavailable:
+.IP \(bu
+The local /etc/resolv.conf file specifies a DNS resolver that
+does not validate DNSSEC signatures (that's
+$queue_directory/etc/resolv.conf when a Postfix daemon runs in a
+chroot jail).
+.IP \(bu
+The local system library does not pass on the "DNSSEC validated"
+bit to Postfix, or Postfix does not know how to ask the library to
+do that.
+.br
+.PP
+By default, the DNSSEC probe asks for the DNS root zone NS
+records, because resolvers should always have that information
+cached. If Postfix runs on a network where the DNS root zone is not
+reachable, specify a different probe, or specify an empty dnssec_probe
+value to disable the feature.
+.PP
+This feature was backported from Postfix 3.6 to Postfix versions
+3.5.9, 3.4.19, 3.3.16. 3.2.21.
 .SH dont_remove (default: 0)
 Don't remove queue files and save them to the "saved" mail queue.
 This is a debugging aid.  To inspect the envelope information and
@@ -7871,7 +7925,7 @@
 TLS connection reuse" for background details.
 .PP
 This feature is available in Postfix 3.4 and later.
-.SH smtp_tls_dane_insecure_mx_policy (default: dane)
+.SH smtp_tls_dane_insecure_mx_policy (default: see "postconf \-d" output)
 The TLS policy for MX hosts with "secure" TLSA records when the
 nexthop destination security level is \fBdane\fR, but the MX
 record was found via an "insecure" MX lookup.  The choices are:
@@ -7892,6 +7946,12 @@
 "Verified", because the MX host name could have been forged.
 .br
 .br
+The default setting for Postfix >= 3.6 is "dane" with
+"smtp_tls_security_level = dane", otherwise "may". This behavior
+was backported to Postfix versions 3.5.9, 3.4.19, 3.3.16. 3.2.21.
+With earlier
+Postfix versions the default setting was always "dane".
+.PP
 Though with "insecure" MX records an active attacker can
 compromise SMTP transport security by returning forged MX records,
 such attacks are "tamper\-evident" since any forged MX hostnames
diff -Nru postfix-3.4.14/man/man8/smtp.8 postfix-3.4.23/man/man8/smtp.8
--- postfix-3.4.14/man/man8/smtp.8	2019-06-29 09:33:39.000000000 -0400
+++ postfix-3.4.23/man/man8/smtp.8	2021-01-16 18:42:53.000000000 -0500
@@ -291,6 +291,12 @@
 When a remote destination resolves to a combination of IPv4 and
 IPv6 addresses, ensure that the Postfix SMTP client can try both
 address types before it runs into the smtp_mx_address_limit.
+.PP
+Available in Postfix 3.4.19 and later:
+.IP "\fBdnssec_probe (ns:.)\fR"
+The DNS query type (default: "ns") and DNS query name (default:
+".") that Postfix may use to determine whether DNSSEC validation
+is available.
 .SH "MIME PROCESSING CONTROLS"
 .na
 .nf
diff -Nru postfix-3.4.14/mantools/postlink postfix-3.4.23/mantools/postlink
--- postfix-3.4.14/mantools/postlink	2019-06-25 08:05:54.000000000 -0400
+++ postfix-3.4.23/mantools/postlink	2021-01-16 18:36:30.000000000 -0500
@@ -695,6 +695,7 @@
     s;\bsmtp_per_record_deadline\b;<a href="postconf.5.html#smtp_per_record_deadline">$&</a>;g;
     s;\bsmtp_send_dummy_mail_auth\b;<a href="postconf.5.html#smtp_send_dummy_mail_auth">$&</a>;g;
     s;\bsmtp_balance_inet_protocols\b;<a href="postconf.5.html#smtp_balance_inet_protocols">$&</a>;g;
+    s;\bdnssec_probe\b;<a href="postconf.5.html#dnssec_probe">$&</a>;g;
     s;\bsmtp_tls_connection_reuse\b;<a href="postconf.5.html#smtp_tls_connection_reuse">$&</a>;g;
     s;\blmtp_tls_connection_reuse\b;<a href="postconf.5.html#lmtp_tls_connection_reuse">$&</a>;g;
     s;\bsmtpd_enforce_tls\b;<a href="postconf.5.html#smtpd_enforce_tls">$&</a>;g;
diff -Nru postfix-3.4.14/proto/BDAT_README.html postfix-3.4.23/proto/BDAT_README.html
--- postfix-3.4.14/proto/BDAT_README.html	2019-02-10 17:47:33.000000000 -0500
+++ postfix-3.4.23/proto/BDAT_README.html	2021-06-09 08:47:01.000000000 -0400
@@ -51,7 +51,7 @@
     # The logging alternative:
     smtpd_discard_ehlo_keywords = chunking
     # The non-logging alternative:
-    smtpd_discard_ehlo_keywords = chunking, silent_discard
+    smtpd_discard_ehlo_keywords = chunking, silent-discard
 </pre>
 </blockquote>
 
diff -Nru postfix-3.4.14/proto/postconf.proto postfix-3.4.23/proto/postconf.proto
--- postfix-3.4.14/proto/postconf.proto	2020-05-09 16:21:56.000000000 -0400
+++ postfix-3.4.23/proto/postconf.proto	2021-01-17 10:13:27.000000000 -0500
@@ -16799,7 +16799,7 @@
 This feature is available in Postfix 3.1 and later.
 </p>
 
-%PARAM smtp_tls_dane_insecure_mx_policy dane
+%PARAM smtp_tls_dane_insecure_mx_policy see "postconf -d" output
 
 <p> The TLS policy for MX hosts with "secure" TLSA records when the
 nexthop destination security level is <b>dane</b>, but the MX
@@ -16823,6 +16823,12 @@
 "Verified", because the MX host name could have been forged.  </dd>
 </dl>
 
+<p> The default setting for Postfix &ge; 3.6 is "dane" with
+"smtp_tls_security_level = dane", otherwise "may". This behavior
+was backported to Postfix versions 3.5.9, 3.4.19, 3.3.16. 3.2.21.
+With earlier
+Postfix versions the default setting was always "dane". </p>
+
 <p> Though with "insecure" MX records an active attacker can
 compromise SMTP transport security by returning forged MX records,
 such attacks are "tamper-evident" since any forged MX hostnames
@@ -17619,3 +17625,59 @@
 </p>
 
 <p> This feature is available in Postfix 3.4 and later. </p>
+
+%PARAM dnssec_probe ns:.
+
+<p> The DNS query type (default: "ns") and DNS query name (default:
+".") that Postfix may use to determine whether DNSSEC validation
+is available.
+</p>
+
+<p> Background: DNSSEC validation is needed for Postfix DANE support;
+this ensures that Postfix receives TLSA records with secure TLS
+server certificate info. When DNSSEC validation is unavailable,
+mail deliveries using <i>opportunistic</i> DANE will not be protected
+by server certificate info in TLSA records, and mail deliveries
+using <i>mandatory</i> DANE will not be made at all. </p>
+
+<p> By default, a Postfix process will send a DNSSEC probe after
+1) the process made a DNS query that requested DNSSEC validation,
+2) the process did not receive a DNSSEC validated response to this
+query or to an earlier query, and 3) the process did not already
+send a DNSSEC probe. <p>
+
+<p> When the DNSSEC probe has no response, or when the response is
+not DNSSEC validated, Postfix logs a warning that DNSSEC validation
+may be unavailable. </p>
+
+<p> Example: </p>
+
+<pre>
+warning: DNSSEC validation may be unavailable
+warning: reason: dnssec_probe 'ns:.' received a response that is not DNSSEC validated
+warning: reason: dnssec_probe 'ns:.' received no response: Server failure
+</pre>
+
+<p> Possible reasons why DNSSEC validation may be unavailable: </p>
+
+<ul>
+
+<li> The local /etc/resolv.conf file specifies a DNS resolver that
+does not validate DNSSEC signatures (that's
+$queue_directory/etc/resolv.conf when a Postfix daemon runs in a
+chroot jail).
+
+<li> The local system library does not pass on the "DNSSEC validated"
+bit to Postfix, or Postfix does not know how to ask the library to
+do that.
+
+</ul>
+
+<p> By default, the DNSSEC probe asks for the DNS root zone NS
+records, because resolvers should always have that information
+cached. If Postfix runs on a network where the DNS root zone is not
+reachable, specify a different probe, or specify an empty dnssec_probe
+value to disable the feature. </p>
+
+<p> This feature was backported from Postfix 3.6 to Postfix versions
+3.5.9, 3.4.19, 3.3.16. 3.2.21. </p>
diff -Nru postfix-3.4.14/README_FILES/BDAT_README postfix-3.4.23/README_FILES/BDAT_README
--- postfix-3.4.14/README_FILES/BDAT_README	2019-02-10 17:47:44.000000000 -0500
+++ postfix-3.4.23/README_FILES/BDAT_README	2021-06-13 15:03:55.000000000 -0400
@@ -23,7 +23,7 @@
         # The logging alternative:
         smtpd_discard_ehlo_keywords = chunking
         # The non-logging alternative:
-        smtpd_discard_ehlo_keywords = chunking, silent_discard
+        smtpd_discard_ehlo_keywords = chunking, silent-discard
 
 Specify '-o smtpd_discard_ehlo_keywords=' in master.cf for the submission and
 smtps services, if you have clients that benefit from CHUNKING support.
diff -Nru postfix-3.4.14/README_FILES/RELEASE_NOTES postfix-3.4.23/README_FILES/RELEASE_NOTES
--- postfix-3.4.14/README_FILES/RELEASE_NOTES	2020-05-16 17:21:36.000000000 -0400
+++ postfix-3.4.23/README_FILES/RELEASE_NOTES	2021-01-16 18:39:10.000000000 -0500
@@ -16,6 +16,47 @@
 If you upgrade from Postfix 3.2 or earlier, read RELEASE_NOTES-3.3
 before proceeding.
 
+Runtime detection of DNSSEC support
+-----------------------------------
+
+The Postfix build system will no longer automatically disable DNSSEC
+support when it determines that Postfix will use libc-musl. This removes
+the earlier libc-musl workaround for Postfix 3.2.15, 3.3.10, 3.4.12,
+and 3.5.2.
+
+Now, when a Postfix process requests DNSSEC support (typically, for
+Postfix DANE support), the process may do a runtime test to determine if
+DNSSEC validation is available. DNSSEC support may be broken because of
+local configuration, libc incompatibility, or other infrastructure issues.
+
+Background: DNSSEC validation is needed for Postfix DANE support;
+this ensures that Postfix receives TLSA records with secure TLS
+server certificate info. When DNSSEC validation is unavailable,
+mail deliveries using opportunistic DANE will not be protected by
+server certificate info in TLSA records, and mail deliveries using
+mandatory DANE will not be made at all.
+
+The dnssec_probe parameter specifies the DNS query type (default:
+"ns") and DNS query name (default: ".") that Postfix may use to
+determine whether DNSSEC validation is available. Specify an empty
+value to disable this feature.
+
+By default, a Postfix process will send a DNSSEC probe after 1) the
+process made a DNS query that requested DNSSEC validation, 2) the
+process did not receive a DNSSEC validated response to this query
+or to an earlier query, and 3) the process did not already send a
+DNSSEC probe.
+
+When the DNSSEC probe has no response, or when the response is not
+DNSSEC validated, Postfix logs a warning that DNSSEC validation may
+be unavailable. Examples:
+
+warning: DNSSEC validation may be unavailable
+warning: reason: dnssec_probe 'ns:.' received a response that is not DNSSEC validated
+warning: reason: dnssec_probe 'ns:.' received no response: Server failure
+
+This feature was backported from Postfix 3.6.
+
 libc-musl workaround for Postfix 3.2.15, 3.3.10, 3.4.12, and 3.5.2
 ------------------------------------------------------------------
 
diff -Nru postfix-3.4.14/RELEASE_NOTES postfix-3.4.23/RELEASE_NOTES
--- postfix-3.4.14/RELEASE_NOTES	2020-05-16 17:21:36.000000000 -0400
+++ postfix-3.4.23/RELEASE_NOTES	2021-01-16 18:39:10.000000000 -0500
@@ -16,6 +16,47 @@
 If you upgrade from Postfix 3.2 or earlier, read RELEASE_NOTES-3.3
 before proceeding.
 
+Runtime detection of DNSSEC support
+-----------------------------------
+
+The Postfix build system will no longer automatically disable DNSSEC
+support when it determines that Postfix will use libc-musl. This removes
+the earlier libc-musl workaround for Postfix 3.2.15, 3.3.10, 3.4.12,
+and 3.5.2.
+
+Now, when a Postfix process requests DNSSEC support (typically, for
+Postfix DANE support), the process may do a runtime test to determine if
+DNSSEC validation is available. DNSSEC support may be broken because of
+local configuration, libc incompatibility, or other infrastructure issues.
+
+Background: DNSSEC validation is needed for Postfix DANE support;
+this ensures that Postfix receives TLSA records with secure TLS
+server certificate info. When DNSSEC validation is unavailable,
+mail deliveries using opportunistic DANE will not be protected by
+server certificate info in TLSA records, and mail deliveries using
+mandatory DANE will not be made at all.
+
+The dnssec_probe parameter specifies the DNS query type (default:
+"ns") and DNS query name (default: ".") that Postfix may use to
+determine whether DNSSEC validation is available. Specify an empty
+value to disable this feature.
+
+By default, a Postfix process will send a DNSSEC probe after 1) the
+process made a DNS query that requested DNSSEC validation, 2) the
+process did not receive a DNSSEC validated response to this query
+or to an earlier query, and 3) the process did not already send a
+DNSSEC probe.
+
+When the DNSSEC probe has no response, or when the response is not
+DNSSEC validated, Postfix logs a warning that DNSSEC validation may
+be unavailable. Examples:
+
+warning: DNSSEC validation may be unavailable
+warning: reason: dnssec_probe 'ns:.' received a response that is not DNSSEC validated
+warning: reason: dnssec_probe 'ns:.' received no response: Server failure
+
+This feature was backported from Postfix 3.6.
+
 libc-musl workaround for Postfix 3.2.15, 3.3.10, 3.4.12, and 3.5.2
 ------------------------------------------------------------------
 
diff -Nru postfix-3.4.14/src/cleanup/cleanup_body_edit.c postfix-3.4.23/src/cleanup/cleanup_body_edit.c
--- postfix-3.4.14/src/cleanup/cleanup_body_edit.c	2017-12-27 17:29:44.000000000 -0500
+++ postfix-3.4.23/src/cleanup/cleanup_body_edit.c	2021-11-05 18:41:44.000000000 -0400
@@ -207,7 +207,7 @@
     /*
      * Finally, output the queue file record.
      */
-    CLEANUP_OUT_BUF(state, REC_TYPE_NORM, buf);
+    CLEANUP_OUT_BUF(state, rec_type, buf);
     curr_rp->write_offs = vstream_ftell(state->dst);
 
     return (0);
diff -Nru postfix-3.4.14/src/cleanup/cleanup_message.c postfix-3.4.23/src/cleanup/cleanup_message.c
--- postfix-3.4.14/src/cleanup/cleanup_message.c	2018-01-07 11:54:46.000000000 -0500
+++ postfix-3.4.23/src/cleanup/cleanup_message.c	2021-07-24 18:31:05.000000000 -0400
@@ -773,9 +773,12 @@
 		} else {
 		    token = tok822_alloc(TOK822_QSTRING, state->fullname);
 		}
-		tok822_externalize(state->temp2, token, TOK822_STR_NONE);
-		tok822_free(token);
-		vstring_sprintf_append(state->temp2, " <%s>",
+		if (token) {
+		    tok822_externalize(state->temp2, token, TOK822_STR_NONE);
+		    tok822_free(token);
+		    vstring_strcat(state->temp2, " ");
+		}
+		vstring_sprintf_append(state->temp2, "<%s>",
 				       vstring_str(state->temp1));
 		break;
 
diff -Nru postfix-3.4.14/src/cleanup/cleanup_milter.c postfix-3.4.23/src/cleanup/cleanup_milter.c
--- postfix-3.4.14/src/cleanup/cleanup_milter.c	2017-12-23 19:13:13.000000000 -0500
+++ postfix-3.4.23/src/cleanup/cleanup_milter.c	2021-11-05 18:41:44.000000000 -0400
@@ -1800,6 +1800,10 @@
 		}
 		count++;
 	    }
+	    if (var_enable_orcpt)
+		been_here_drop(state->dups, "%s\n%d\n%s\n%s",
+			       dsn_orcpt ? dsn_orcpt : "", dsn_notify,
+			     orig_rcpt ? orig_rcpt : "", STR(int_rcpt_buf));
 	    /* FALLTHROUGH */
 	case REC_TYPE_DRCP:			/* canceled recipient */
 	case REC_TYPE_DONE:			/* can't happen */
@@ -1815,6 +1819,8 @@
 	    break;
 	}
     }
+    if (var_enable_orcpt == 0 && count > 0)
+	been_here_drop_fixed(state->dups, STR(int_rcpt_buf));
 
     if (msg_verbose)
 	msg_info("%s: deleted %d records for recipient \"%s\"",
@@ -1825,7 +1831,8 @@
 
 /* cleanup_repl_body - replace message body */
 
-static const char *cleanup_repl_body(void *context, int cmd, VSTRING *buf)
+static const char *cleanup_repl_body(void *context, int cmd, int rec_type,
+				             VSTRING *buf)
 {
     const char *myname = "cleanup_repl_body";
     CLEANUP_STATE *state = (CLEANUP_STATE *) context;
@@ -1837,7 +1844,7 @@
      */
     switch (cmd) {
     case MILTER_BODY_LINE:
-	if (cleanup_body_edit_write(state, REC_TYPE_NORM, buf) < 0)
+	if (cleanup_body_edit_write(state, rec_type, buf) < 0)
 	    return (cleanup_milter_error(state, errno));
 	break;
     case MILTER_BODY_START:
diff -Nru postfix-3.4.14/src/dns/dns.h postfix-3.4.23/src/dns/dns.h
--- postfix-3.4.14/src/dns/dns.h	2020-04-18 11:22:54.000000000 -0400
+++ postfix-3.4.23/src/dns/dns.h	2021-01-16 18:36:30.000000000 -0500
@@ -244,7 +244,12 @@
 	(lflags), (ltype))
 
  /*
-  * Request flags.
+  * The dns_lookup() rflag that requests DNSSEC validation.
+  */
+#define DNS_WANT_DNSSEC_VALIDATION(rflags)	((rflags) & RES_USE_DNSSEC)
+
+ /*
+  * lflags.
   */
 #define DNS_REQ_FLAG_STOP_OK	(1<<0)
 #define DNS_REQ_FLAG_STOP_INVAL	(1<<1)
@@ -309,6 +314,18 @@
   */
 const char *dns_str_resflags(unsigned long);
 
+ /*
+  * dns_sec.c.
+  */
+#define DNS_SEC_FLAG_AVAILABLE	(1<<0)	/* got some DNSSEC validated reply */
+#define DNS_SEC_FLAG_DONT_PROBE	(1<<1)	/* probe already sent, or disabled */
+
+#define DNS_SEC_STATS_SET(flags) (dns_sec_stats |= (flags))
+#define DNS_SEC_STATS_TEST(flags) (dns_sec_stats & (flags))
+
+extern int dns_sec_stats;		/* See DNS_SEC_FLAG_XXX above */
+extern void dns_sec_probe(int);
+
 /* LICENSE
 /* .ad
 /* .fi
diff -Nru postfix-3.4.14/src/dns/dns_lookup.c postfix-3.4.23/src/dns/dns_lookup.c
--- postfix-3.4.14/src/dns/dns_lookup.c	2020-04-18 11:22:54.000000000 -0400
+++ postfix-3.4.23/src/dns/dns_lookup.c	2021-01-16 18:36:30.000000000 -0500
@@ -169,6 +169,12 @@
 /*	Pointer to storage for the reply RCODE value. This gives
 /*	more detailed information than DNS_FAIL, DNS_RETRY, etc.
 /* DIAGNOSTICS
+/*	If DNSSEC validation is requested but the response is not
+/*	DNSSEC validated, dns_lookup() will send a one-time probe
+/*	query as configured with the \fBdnssec_probe\fR configuration
+/*	parameter, and will log a warning when the probe response
+/*	was not DNSSEC validated.
+/* .PP
 /*	dns_lookup() returns one of the following codes and sets the
 /*	\fIwhy\fR argument accordingly:
 /* .IP DNS_OK
@@ -458,7 +464,7 @@
      */
 #define XTRA_FLAGS (RES_USE_EDNS0 | RES_TRUSTAD)
 
-    if (flags & RES_USE_DNSSEC)
+    if (DNS_WANT_DNSSEC_VALIDATION(flags))
 	flags |= (RES_USE_EDNS0 | RES_TRUSTAD);
 
     /*
@@ -487,6 +493,8 @@
 	_res.options |= saved_options;
 	reply_header = (HEADER *) reply->buf;
 	reply->rcode = reply_header->rcode;
+	if ((reply->dnssec_ad = !!reply_header->ad) != 0)
+	    DNS_SEC_STATS_SET(DNS_SEC_FLAG_AVAILABLE);
 	if (h_errno != 0) {
 	    if (why)
 		vstring_sprintf(why, "Host or domain name not found. "
@@ -538,13 +546,8 @@
 
     /*
      * Initialize the reply structure. Some structure members are filled on
-     * the fly while the reply is being parsed.  Coerce AD bit to boolean.
+     * the fly while the reply is being parsed.
      */
-#if RES_USE_DNSSEC != 0
-    reply->dnssec_ad = (flags & RES_USE_DNSSEC) ? !!reply_header->ad : 0;
-#else
-    reply->dnssec_ad = 0;
-#endif
     SET_HAVE_DNS_REPLY_PACKET(reply, len);
     reply->query_start = reply->buf + sizeof(HEADER);
     reply->answer_start = 0;
@@ -862,7 +865,9 @@
 	    CORRUPT(DNS_RETRY);
 	if ((status = dns_get_fixed(pos, &fixed)) != DNS_OK)
 	    CORRUPT(status);
-	if (!valid_rr_name(rr_name, "resource name", fixed.type, reply))
+	if (strcmp(orig_name, ".") == 0 && *rr_name == 0)
+	     /* Allow empty response name for root queries. */ ;
+	else if (!valid_rr_name(rr_name, "resource name", fixed.type, reply))
 	    CORRUPT(DNS_INVAL);
 	if (fqdn)
 	    vstring_strcpy(fqdn, rr_name);
@@ -950,7 +955,7 @@
     /*
      * The Linux resolver misbehaves when given an invalid domain name.
      */
-    if (!valid_hostname(name, DONT_GRIPE)) {
+    if (strcmp(name, ".") && !valid_hostname(name, DONT_GRIPE)) {
 	if (why)
 	    vstring_sprintf(why,
 		   "Name service error for %s: invalid host or domain name",
@@ -987,6 +992,10 @@
 		(void) dns_get_answer(orig_name, &reply, T_SOA, rrlist, fqdn,
 				      cname, c_len, &maybe_secure);
 	    }
+	    if (DNS_WANT_DNSSEC_VALIDATION(flags)
+		&& !DNS_SEC_STATS_TEST(DNS_SEC_FLAG_AVAILABLE | \
+				       DNS_SEC_FLAG_DONT_PROBE))
+		dns_sec_probe(flags);		/* XXX Clobbers 'reply' */
 	    return (status);
 	}
 
@@ -996,6 +1005,10 @@
 	 */
 	status = dns_get_answer(orig_name, &reply, type, rrlist, fqdn,
 				cname, c_len, &maybe_secure);
+	if (DNS_WANT_DNSSEC_VALIDATION(flags)
+	    && !DNS_SEC_STATS_TEST(DNS_SEC_FLAG_AVAILABLE | \
+				   DNS_SEC_FLAG_DONT_PROBE))
+	    dns_sec_probe(flags);		/* XXX Clobbers 'reply' */
 	switch (status) {
 	default:
 	    if (why)
diff -Nru postfix-3.4.14/src/dns/dns_sec.c postfix-3.4.23/src/dns/dns_sec.c
--- postfix-3.4.14/src/dns/dns_sec.c	1969-12-31 19:00:00.000000000 -0500
+++ postfix-3.4.23/src/dns/dns_sec.c	2021-01-11 18:32:06.000000000 -0500
@@ -0,0 +1,144 @@
+/*++
+/* NAME
+/*	dns_sec 3
+/* SUMMARY
+/*	DNSSEC validation availability
+/* SYNOPSIS
+/*	#include <dns.h>
+/*
+/*	DNS_SEC_STATS_SET(
+/*	int	flags)
+/*
+/*	DNS_SEC_STATS_TEST(
+/*	int	flags)
+/*
+/*	void	dns_sec_probe(
+/*	int	rflags)
+/* DESCRIPTION
+/*	This module maintains information about the availability of
+/*	DNSSEC validation, in global flags that summarize
+/*	process-lifetime history.
+/* .IP DNS_SEC_FLAG_AVAILABLE
+/*	The process has received at least one DNSSEC validated
+/*	response to a query that requested DNSSEC validation.
+/* .IP DNS_SEC_FLAG_DONT_PROBE
+/*	The process has sent a DNSSEC probe (see below), or DNSSEC
+/*	probing is disabled by configuration.
+/* .PP
+/*	DNS_SEC_STATS_SET() sets one or more DNS_SEC_FLAG_* flags,
+/*	and DNS_SEC_STATS_TEST() returns non-zero if any of the
+/*	specified flags is set.
+/*
+/*	dns_sec_probe() generates a query to the target specified
+/*	with the \fBdnssec_probe\fR configuration parameter. It
+/*	sets the DNS_SEC_FLAG_DONT_PROBE flag, and it calls
+/*	dns_lookup() which sets DNS_SEC_FLAG_AVAILABLE if it receives
+/*	a DNSSEC validated response. Preconditions:
+/* .IP \(bu
+/*	The rflags argument must request DNSSEC validation (in the
+/*	same manner as dns_lookup() rflags argument).
+/* .IP \(bu
+/*	The DNS_SEC_FLAG_AVAILABLE and DNS_SEC_FLAG_DONT_PROBE
+/*	flags must be false.
+/* LICENSE
+/* .ad
+/* .fi
+/*	The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/*	Wietse Venema
+/*	Google, Inc.
+/*	111 8th Avenue
+/*	New York, NY 10011, USA
+/*--*/
+
+#include <sys_defs.h>
+
+ /*
+  * Utility library.
+  */
+#include <msg.h>
+#include <mymalloc.h>
+#include <split_at.h>
+#include <vstring.h>
+
+ /*
+  * Global library.
+  */
+#include <mail_params.h>
+
+ /*
+  * DNS library.
+  */
+#include <dns.h>
+
+int     dns_sec_stats;
+
+/* dns_sec_probe - send a probe to establish DNSSEC viability */
+
+void    dns_sec_probe(int rflags)
+{
+    const char myname[] = "dns_sec_probe";
+    char   *saved_dnssec_probe;
+    char   *qname;
+    int     qtype;
+    DNS_RR *rrlist = 0;
+    int     dns_status;
+    VSTRING *why;
+
+    /*
+     * Sanity checks.
+     */
+    if (!DNS_WANT_DNSSEC_VALIDATION(rflags))
+	msg_panic("%s: DNSSEC is not requested", myname);
+    if (DNS_SEC_STATS_TEST(DNS_SEC_FLAG_DONT_PROBE))
+	msg_panic("%s: DNSSEC probe was already sent, or probing is disabled",
+		  myname);
+    if (DNS_SEC_STATS_TEST(DNS_SEC_FLAG_AVAILABLE))
+	msg_panic("%s: already have validated DNS response", myname);
+
+    /*
+     * Don't recurse.
+     */
+    DNS_SEC_STATS_SET(DNS_SEC_FLAG_DONT_PROBE);
+
+    /*
+     * Don't probe.
+     */
+    if (*var_dnssec_probe == 0)
+	return;
+
+    /*
+     * Parse the probe spec. Format is type:resource.
+     */
+    saved_dnssec_probe = mystrdup(var_dnssec_probe);
+    if ((qname = split_at(saved_dnssec_probe, ':')) == 0 || *qname == 0
+	|| (qtype = dns_type(saved_dnssec_probe)) == 0)
+	msg_fatal("malformed %s value: %s format is qtype:qname",
+		  VAR_DNSSEC_PROBE, var_dnssec_probe);
+
+    why = vstring_alloc(100);
+    dns_status = dns_lookup(qname, qtype, rflags, &rrlist, (VSTRING *) 0, why);
+    if (!DNS_SEC_STATS_TEST(DNS_SEC_FLAG_AVAILABLE))
+	msg_warn("DNSSEC validation may be unavailable");
+    else if (msg_verbose)
+	msg_info(VAR_DNSSEC_PROBE
+		 " '%s' received a response that is DNSSEC validated",
+		 var_dnssec_probe);
+    switch (dns_status) {
+    default:
+	if (!DNS_SEC_STATS_TEST(DNS_SEC_FLAG_AVAILABLE))
+	    msg_warn("reason: " VAR_DNSSEC_PROBE
+		     " '%s' received a response that is not DNSSEC validated",
+		     var_dnssec_probe);
+	if (rrlist)
+	    dns_rr_free(rrlist);
+	break;
+    case DNS_RETRY:
+    case DNS_FAIL:
+	msg_warn("reason: " VAR_DNSSEC_PROBE " '%s' received no response: %s",
+		 var_dnssec_probe, vstring_str(why));
+	break;
+    }
+    myfree(saved_dnssec_probe);
+    vstring_free(why);
+}
diff -Nru postfix-3.4.14/src/dns/Makefile.in postfix-3.4.23/src/dns/Makefile.in
--- postfix-3.4.14/src/dns/Makefile.in	2019-01-27 17:17:10.000000000 -0500
+++ postfix-3.4.23/src/dns/Makefile.in	2021-01-16 18:36:30.000000000 -0500
@@ -1,10 +1,10 @@
 SHELL	= /bin/sh
 SRCS	= dns_lookup.c dns_rr.c dns_strerror.c dns_strtype.c dns_rr_to_pa.c \
 	dns_sa_to_rr.c dns_rr_eq_sa.c dns_rr_to_sa.c dns_strrecord.c \
-	dns_rr_filter.c dns_str_resflags.c
+	dns_rr_filter.c dns_str_resflags.c dns_sec.c
 OBJS	= dns_lookup.o dns_rr.o dns_strerror.o dns_strtype.o dns_rr_to_pa.o \
 	dns_sa_to_rr.o dns_rr_eq_sa.o dns_rr_to_sa.o dns_strrecord.o \
-	dns_rr_filter.o dns_str_resflags.o
+	dns_rr_filter.o dns_str_resflags.o dns_sec.o
 HDRS	= dns.h
 TESTSRC	= test_dns_lookup.c test_alias_token.c
 DEFS	= -I. -I$(INC_DIR) -D$(SYSTYPE)
@@ -76,7 +76,7 @@
 	done
 	cd $(INC_DIR); chmod 644 $(HDRS)
 
-test_dns_lookup: test_dns_lookup.c $(LIB) $(LIBS)
+test_dns_lookup: test_dns_lookup.c all $(LIB) $(LIBS)
 	$(CC) $(CFLAGS) -o $@ $@.c $(LIB) $(LIBS) $(SYSLIBS)
 
 dns_rr_to_pa: $(LIB) $(LIBS)
@@ -354,6 +354,18 @@
 dns_sa_to_rr.o: ../../include/vstring.h
 dns_sa_to_rr.o: dns.h
 dns_sa_to_rr.o: dns_sa_to_rr.c
+dns_sec.o: ../../include/check_arg.h
+dns_sec.o: ../../include/mail_params.h
+dns_sec.o: ../../include/msg.h
+dns_sec.o: ../../include/myaddrinfo.h
+dns_sec.o: ../../include/mymalloc.h
+dns_sec.o: ../../include/sock_addr.h
+dns_sec.o: ../../include/split_at.h
+dns_sec.o: ../../include/sys_defs.h
+dns_sec.o: ../../include/vbuf.h
+dns_sec.o: ../../include/vstring.h
+dns_sec.o: dns.h
+dns_sec.o: dns_sec.c
 dns_str_resflags.o: ../../include/check_arg.h
 dns_str_resflags.o: ../../include/myaddrinfo.h
 dns_str_resflags.o: ../../include/name_mask.h
diff -Nru postfix-3.4.14/src/dns/test_dns_lookup.c postfix-3.4.23/src/dns/test_dns_lookup.c
--- postfix-3.4.14/src/dns/test_dns_lookup.c	2016-02-21 18:06:59.000000000 -0500
+++ postfix-3.4.23/src/dns/test_dns_lookup.c	2021-01-16 18:36:30.000000000 -0500
@@ -77,6 +77,9 @@
     int     ch;
     int     lflags = DNS_REQ_FLAG_NONE;
 
+    if (var_dnssec_probe == 0)
+	var_dnssec_probe = mystrdup(DEF_DNSSEC_PROBE);
+
     msg_vstream_init(argv[0], VSTREAM_ERR);
     while ((ch = GETOPT(argc, argv, "f:npv")) > 0) {
 	switch (ch) {
diff -Nru postfix-3.4.14/src/global/been_here.c postfix-3.4.23/src/global/been_here.c
--- postfix-3.4.14/src/global/been_here.c	2019-02-09 18:26:28.000000000 -0500
+++ postfix-3.4.23/src/global/been_here.c	2020-11-04 17:06:38.000000000 -0500
@@ -26,6 +26,14 @@
 /*	BH_TABLE *dup_filter;
 /*	char	*format;
 /*
+/*	int	been_here_drop_fixed(dup_filter, string)
+/*	BH_TABLE *dup_filter;
+/*	char	*string;
+/*
+/*	int	been_here_drop(dup_filter, format, ...)
+/*	BH_TABLE *dup_filter;
+/*	char	*format;
+/*
 /*	void	been_here_free(dup_filter)
 /*	BH_TABLE *dup_filter;
 /* DESCRIPTION
@@ -46,6 +54,16 @@
 /*	been_here_check_fixed() and been_here_check() are similar
 /*	but do not update the duplicate filter.
 /*
+/*	been_here_drop_fixed() looks up a fixed string in the given
+/*	table, and deletes the entry if the string was found. The
+/*	result is non-zero (true) if the string was found, zero
+/*	(false) otherwise.
+/*
+/*	been_here_drop() formats its arguments, looks up the result
+/*	in the given table, and removes the entry if the formatted
+/*	result was found. The result is non-zero (true) if the
+/*	formatted result was found, zero (false) otherwise.
+/*
 /*	been_here_free() releases storage for a duplicate filter.
 /*
 /*	Arguments:
@@ -243,6 +261,67 @@
 
     /*
      * Cleanup.
+     */
+    if (folded_string)
+	vstring_free(folded_string);
+
+    return (status);
+}
+
+/* been_here_drop - remove filter entry with finer control */
+
+int     been_here_drop(BH_TABLE *dup_filter, const char *fmt,...)
+{
+    VSTRING *buf = vstring_alloc(100);
+    int     status;
+    va_list ap;
+
+    /*
+     * Construct the string to be dropped.
+     */
+    va_start(ap, fmt);
+    vstring_vsprintf(buf, fmt, ap);
+    va_end(ap);
+
+    /*
+     * Drop the filter entry.
+     */
+    status = been_here_drop_fixed(dup_filter, vstring_str(buf));
+
+    /*
+     * Cleanup.
+     */
+    vstring_free(buf);
+    return (status);
+}
+
+/* been_here_drop_fixed - remove filter entry */
+
+int     been_here_drop_fixed(BH_TABLE *dup_filter, const char *string)
+{
+    VSTRING *folded_string;
+    const char *lookup_key;
+    int     status;
+
+    /*
+     * Special processing: case insensitive lookup.
+     */
+    if (dup_filter->flags & BH_FLAG_FOLD) {
+	folded_string = vstring_alloc(100);
+	lookup_key = casefold(folded_string, string);
+    } else {
+	folded_string = 0;
+	lookup_key = string;
+    }
+
+    /*
+     * Drop the filter entry.
+     */
+    if ((status = been_here_check_fixed(dup_filter, lookup_key)) != 0)
+	htable_delete(dup_filter->table, lookup_key, (void (*) (void *)) 0);
+
+    /*
+     * Cleanup.
      */
     if (folded_string)
 	vstring_free(folded_string);
diff -Nru postfix-3.4.14/src/global/been_here.h postfix-3.4.23/src/global/been_here.h
--- postfix-3.4.14/src/global/been_here.h	2019-02-09 18:26:58.000000000 -0500
+++ postfix-3.4.23/src/global/been_here.h	2020-11-04 17:06:38.000000000 -0500
@@ -35,6 +35,8 @@
 extern int PRINTFLIKE(2, 3) been_here(BH_TABLE *, const char *,...);
 extern int been_here_check_fixed(BH_TABLE *, const char *);
 extern int PRINTFLIKE(2, 3) been_here_check(BH_TABLE *, const char *,...);
+extern int been_here_drop_fixed(BH_TABLE *, const char *);
+extern int PRINTFLIKE(2, 3) been_here_drop(BH_TABLE *, const char *,...);
 
 /* LICENSE
 /* .ad
diff -Nru postfix-3.4.14/src/global/delivered_hdr.c postfix-3.4.23/src/global/delivered_hdr.c
--- postfix-3.4.14/src/global/delivered_hdr.c	2015-01-27 10:22:33.000000000 -0500
+++ postfix-3.4.23/src/global/delivered_hdr.c	2020-11-04 17:07:27.000000000 -0500
@@ -115,6 +115,8 @@
     char   *cp;
     DELIVERED_HDR_INFO *info;
     const HEADER_OPTS *hdr;
+    int     curr_type;
+    int     prev_type;
 
     /*
      * Sanity check.
@@ -130,15 +132,20 @@
 
     /*
      * XXX Assume that mail_copy() produces delivered-to headers that fit in
-     * a REC_TYPE_NORM record. Lowercase the delivered-to addresses for
-     * consistency.
+     * a REC_TYPE_NORM or REC_TYPE_CONT record. Lowercase the delivered-to
+     * addresses for consistency.
      * 
      * XXX Don't get bogged down by gazillions of delivered-to headers.
      */
 #define DELIVERED_HDR_LIMIT	1000
 
-    while (rec_get(fp, info->buf, 0) == REC_TYPE_NORM
-	   && info->table->used < DELIVERED_HDR_LIMIT) {
+    for (prev_type = REC_TYPE_NORM;
+	 info->table->used < DELIVERED_HDR_LIMIT
+	 && ((curr_type = rec_get(fp, info->buf, 0)) == REC_TYPE_NORM
+	     || curr_type == REC_TYPE_CONT);
+	 prev_type = curr_type) {
+	if (prev_type != REC_TYPE_NORM)
+	    continue;
 	if (is_header(STR(info->buf))) {
 	    if ((hdr = header_opts_find(STR(info->buf))) != 0
 		&& hdr->type == HDR_DELIVERED_TO) {
diff -Nru postfix-3.4.14/src/global/haproxy_srvr.c postfix-3.4.23/src/global/haproxy_srvr.c
--- postfix-3.4.14/src/global/haproxy_srvr.c	2012-06-17 13:47:09.000000000 -0400
+++ postfix-3.4.23/src/global/haproxy_srvr.c	2021-04-04 13:01:15.000000000 -0400
@@ -59,6 +59,8 @@
 
 static INET_PROTO_INFO *proto_info;
 
+#define STR_OR_NULL(str) ((str) ? (str) : "(null)")
+
 /* haproxy_srvr_parse_lit - extract and validate string literal */
 
 static int haproxy_srvr_parse_lit(const char *str,...)
@@ -68,7 +70,7 @@
     int     result = -1;
 
     if (msg_verbose)
-	msg_info("haproxy_srvr_parse: %s", str);
+	msg_info("haproxy_srvr_parse: %s", STR_OR_NULL(str));
 
     if (str != 0) {
 	va_start(ap, str);
@@ -85,8 +87,10 @@
 static int haproxy_srvr_parse_proto(const char *str, int *addr_family)
 {
     if (msg_verbose)
-	msg_info("haproxy_srvr_parse: proto=%s", str);
+	msg_info("haproxy_srvr_parse: proto=%s", STR_OR_NULL(str));
 
+    if (str == 0)
+	return (-1);
 #ifdef AF_INET6
     if (strcasecmp(str, "TCP6") == 0) {
 	if (strchr((char *) proto_info->sa_family_list, AF_INET6) != 0) {
@@ -110,7 +114,8 @@
 				           int addr_family)
 {
     if (msg_verbose)
-	msg_info("haproxy_srvr_parse: addr=%s proto=%d", str, addr_family);
+	msg_info("haproxy_srvr_parse: addr=%s proto=%d",
+		 STR_OR_NULL(str), addr_family);
 
     if (str == 0 || strlen(str) >= sizeof(MAI_HOSTADDR_STR))
 	return (-1);
@@ -145,7 +150,7 @@
 static int haproxy_srvr_parse_port(const char *str, MAI_SERVPORT_STR *port)
 {
     if (msg_verbose)
-	msg_info("haproxy_srvr_parse: port=%s", str);
+	msg_info("haproxy_srvr_parse: port=%s", STR_OR_NULL(str));
     if (str == 0 || strlen(str) >= sizeof(MAI_SERVPORT_STR)
 	|| !valid_hostport(str, DONT_GRIPE)) {
 	return (-1);
diff -Nru postfix-3.4.14/src/global/mail_params.c postfix-3.4.23/src/global/mail_params.c
--- postfix-3.4.14/src/global/mail_params.c	2020-05-12 19:15:37.000000000 -0400
+++ postfix-3.4.23/src/global/mail_params.c	2021-06-12 11:04:20.000000000 -0400
@@ -151,6 +151,8 @@
 /*	char	*var_maillog_file_comp;
 /*	char	*var_maillog_file_stamp;
 /*	char	*var_postlog_service;
+/*
+/*	char	*var_dnssec_probe;
 /* DESCRIPTION
 /*	This module (actually the associated include file) defines
 /*	the names and defaults of all mail configuration parameters.
@@ -360,6 +362,8 @@
 char	*var_maillog_file_stamp;
 char	*var_postlog_service;
 
+char   *var_dnssec_probe;
+
 const char null_format_string[1] = "";
 
  /*
@@ -687,6 +691,7 @@
 	VAR_MAILLOG_FILE_COMP, DEF_MAILLOG_FILE_COMP, &var_maillog_file_comp, 1, 0,
 	VAR_MAILLOG_FILE_STAMP, DEF_MAILLOG_FILE_STAMP, &var_maillog_file_stamp, 1, 0,
 	VAR_POSTLOG_SERVICE, DEF_POSTLOG_SERVICE, &var_postlog_service, 1, 0,
+	VAR_DNSSEC_PROBE, DEF_DNSSEC_PROBE, &var_dnssec_probe, 0, 0,
 	0,
     };
     static const CONFIG_BOOL_TABLE first_bool_defaults[] = {
@@ -827,6 +832,17 @@
     const char *cp;
 
     /*
+     * Ignore the Postfix >= 3.6 compatibility_level's minor and patch
+     * fields, to allow rollback from Postfix >= 3.6, and to allow
+     * configuration sharing with Postfix >= 3.6.
+     */
+    const char *compat_level_str;
+
+    if ((compat_level_str = mail_conf_lookup(VAR_COMPAT_LEVEL)) != 0
+      && ISDIGIT(compat_level_str[0]) && strchr(compat_level_str, '.') != 0)
+	set_mail_conf_int(VAR_COMPAT_LEVEL, atoi(compat_level_str));
+
+    /*
      * Extract compatibility level first, so that we can determine what
      * parameters of interest are left at their legacy defaults.
      */
diff -Nru postfix-3.4.14/src/global/mail_params.h postfix-3.4.23/src/global/mail_params.h
--- postfix-3.4.14/src/global/mail_params.h	2020-05-09 16:21:56.000000000 -0400
+++ postfix-3.4.23/src/global/mail_params.h	2021-01-17 08:26:30.000000000 -0500
@@ -1617,7 +1617,7 @@
 
  /* SMTP only */
 #define VAR_SMTP_TLS_INSECURE_MX_POLICY "smtp_tls_dane_insecure_mx_policy"
-#define DEF_SMTP_TLS_INSECURE_MX_POLICY "dane"
+#define DEF_SMTP_TLS_INSECURE_MX_POLICY "${{$smtp_tls_security_level} == {dane} ? {dane} : {may}}"
 extern char *var_smtp_tls_insecure_mx_policy;
 
  /*
@@ -4189,6 +4189,13 @@
 #define DEF_POSTLOGD_WATCHDOG	"10s"
 extern int var_postlogd_watchdog;
 
+ /*
+  * DNSSEC probing, to find out if DNSSEC validation is available.
+  */
+#define VAR_DNSSEC_PROBE	"dnssec_probe"
+#define DEF_DNSSEC_PROBE	"ns:."
+extern char *var_dnssec_probe;
+
 /* LICENSE
 /* .ad
 /* .fi
diff -Nru postfix-3.4.14/src/global/mail_task.c postfix-3.4.23/src/global/mail_task.c
--- postfix-3.4.14/src/global/mail_task.c	2019-01-29 17:24:42.000000000 -0500
+++ postfix-3.4.23/src/global/mail_task.c	2021-04-04 16:18:38.000000000 -0400
@@ -17,8 +17,8 @@
 /*
 /*	The result is overwritten with each call.
 /*
-/*	A null argv0 argument requests that the current
-/*	result is returned.
+/*	A null argv0 argument requests that the current result is
+/*	returned, or "unknown" when no current result exists.
 /* LICENSE
 /* .ad
 /* .fi
@@ -59,6 +59,8 @@
     const char *slash;
     const char *tag;
 
+    if (argv0 == 0 && canon_name == 0)
+	argv0 = "unknown";
     if (argv0) {
 	if (canon_name == 0)
 	    canon_name = vstring_alloc(10);
diff -Nru postfix-3.4.14/src/global/mail_version.h postfix-3.4.23/src/global/mail_version.h
--- postfix-3.4.14/src/global/mail_version.h	2020-06-27 17:26:28.000000000 -0400
+++ postfix-3.4.23/src/global/mail_version.h	2021-11-07 17:38:53.000000000 -0500
@@ -20,8 +20,8 @@
   * 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_VERSION_NUMBER	"3.4.14"
+#define MAIL_RELEASE_DATE	"20211107"
+#define MAIL_VERSION_NUMBER	"3.4.23"
 
 #ifdef SNAPSHOT
 #define MAIL_VERSION_DATE	"-" MAIL_RELEASE_DATE
diff -Nru postfix-3.4.14/src/global/record.c postfix-3.4.23/src/global/record.c
--- postfix-3.4.14/src/global/record.c	2018-11-27 17:39:42.000000000 -0500
+++ postfix-3.4.23/src/global/record.c	2021-07-24 19:05:33.000000000 -0400
@@ -323,7 +323,7 @@
 int     rec_goto(VSTREAM *stream, const char *buf)
 {
     off_t   offset;
-    static const char *saved_path;
+    static char *saved_path;
     static off_t saved_offset;
     static int reverse_count;
 
@@ -336,11 +336,12 @@
      * is likely to insert 10000 message headers, but someone might append
      * 10000 recipients.
      */
-#define STREQ(x,y) ((x) == (y) && strcmp((x), (y)) == 0)
 #define REVERSE_JUMP_LIMIT	10000
 
-    if (!STREQ(saved_path, VSTREAM_PATH(stream))) {
-	saved_path = VSTREAM_PATH(stream);
+    if (saved_path == 0 || strcmp(saved_path, VSTREAM_PATH(stream)) != 0) {
+	if (saved_path)
+	    myfree(saved_path);
+	saved_path = mystrdup(VSTREAM_PATH(stream));
 	reverse_count = 0;
 	saved_offset = 0;
     }
diff -Nru postfix-3.4.14/src/milter/milter8.c postfix-3.4.23/src/milter/milter8.c
--- postfix-3.4.14/src/milter/milter8.c	2020-02-02 12:37:46.000000000 -0500
+++ postfix-3.4.23/src/milter/milter8.c	2021-11-05 18:41:44.000000000 -0400
@@ -1147,10 +1147,12 @@
 	    if (edit_resp == 0 && LEN(body_line_buf) > 0)
 		edit_resp = parent->repl_body(parent->chg_context,
 					      MILTER_BODY_LINE,
+					      REC_TYPE_NORM,
 					      body_line_buf);
 	    if (edit_resp == 0)
 		edit_resp = parent->repl_body(parent->chg_context,
 					      MILTER_BODY_END,
+					      /* unused*/ 0,
 					      (VSTRING *) 0);
 	    body_edit_lockout = 1;
 	    vstring_free(body_line_buf);
@@ -1546,6 +1548,7 @@
 			body_line_buf = vstring_alloc(var_line_limit);
 			edit_resp = parent->repl_body(parent->chg_context,
 						      MILTER_BODY_START,
+						      /* unused */ 0,
 						      (VSTRING *) 0);
 		    }
 		    /* Extract lines from the on-the-wire CRLF format. */
@@ -1559,9 +1562,18 @@
 						 LEN(body_line_buf) - 1);
 			    edit_resp = parent->repl_body(parent->chg_context,
 							  MILTER_BODY_LINE,
+							  REC_TYPE_NORM,
 							  body_line_buf);
 			    VSTRING_RESET(body_line_buf);
 			} else {
+			    /* Preserves \r if not followed by \n. */
+			    if (LEN(body_line_buf) == var_line_limit) {
+				edit_resp = parent->repl_body(parent->chg_context,
+							   MILTER_BODY_LINE,
+							      REC_TYPE_CONT,
+							      body_line_buf);
+				VSTRING_RESET(body_line_buf);
+			    }
 			    VSTRING_ADDCH(body_line_buf, ch);
 			}
 		    }
diff -Nru postfix-3.4.14/src/milter/milter.h postfix-3.4.23/src/milter/milter.h
--- postfix-3.4.14/src/milter/milter.h	2020-02-02 12:37:46.000000000 -0500
+++ postfix-3.4.23/src/milter/milter.h	2021-11-05 18:41:44.000000000 -0400
@@ -100,7 +100,7 @@
 typedef const char *(*MILTER_EDIT_FROM_FN) (void *, const char *, const char *);
 typedef const char *(*MILTER_EDIT_RCPT_FN) (void *, const char *);
 typedef const char *(*MILTER_EDIT_RCPT_PAR_FN) (void *, const char *, const char *);
-typedef const char *(*MILTER_EDIT_BODY_FN) (void *, int, VSTRING *);
+typedef const char *(*MILTER_EDIT_BODY_FN) (void *, int, int, VSTRING *);
 
 typedef struct MILTERS {
     MILTER *milter_list;		/* linked list of Milters */
diff -Nru postfix-3.4.14/src/postconf/postconf_builtin.c postfix-3.4.23/src/postconf/postconf_builtin.c
--- postfix-3.4.14/src/postconf/postconf_builtin.c	2018-01-14 20:30:12.000000000 -0500
+++ postfix-3.4.23/src/postconf/postconf_builtin.c	2021-09-25 19:02:02.000000000 -0400
@@ -244,6 +244,7 @@
 static const char *pcf_mynetworks(void)
 {
     static const char *networks;
+    VSTRING *exp_buf;
     const char *junk;
 
     /*
@@ -252,10 +253,12 @@
     if (networks)
 	return (networks);
 
+    exp_buf = vstring_alloc(100);
+
     if (var_inet_interfaces == 0) {
 	if ((pcf_cmd_mode & PCF_SHOW_DEFS)
 	    || (junk = mail_conf_lookup_eval(VAR_INET_INTERFACES)) == 0)
-	    junk = pcf_expand_parameter_value((VSTRING *) 0, pcf_cmd_mode,
+	    junk = pcf_expand_parameter_value(exp_buf, pcf_cmd_mode,
 					      DEF_INET_INTERFACES,
 					      (PCF_MASTER_ENT *) 0);
 	var_inet_interfaces = mystrdup(junk);
@@ -263,7 +266,7 @@
     if (var_mynetworks_style == 0) {
 	if ((pcf_cmd_mode & PCF_SHOW_DEFS)
 	    || (junk = mail_conf_lookup_eval(VAR_MYNETWORKS_STYLE)) == 0)
-	    junk = pcf_expand_parameter_value((VSTRING *) 0, pcf_cmd_mode,
+	    junk = pcf_expand_parameter_value(exp_buf, pcf_cmd_mode,
 					      DEF_MYNETWORKS_STYLE,
 					      (PCF_MASTER_ENT *) 0);
 	var_mynetworks_style = mystrdup(junk);
@@ -271,12 +274,13 @@
     if (var_inet_protocols == 0) {
 	if ((pcf_cmd_mode & PCF_SHOW_DEFS)
 	    || (junk = mail_conf_lookup_eval(VAR_INET_PROTOCOLS)) == 0)
-	    junk = pcf_expand_parameter_value((VSTRING *) 0, pcf_cmd_mode,
+	    junk = pcf_expand_parameter_value(exp_buf, pcf_cmd_mode,
 					      DEF_INET_PROTOCOLS,
 					      (PCF_MASTER_ENT *) 0);
 	var_inet_protocols = mystrdup(junk);
 	(void) inet_proto_init(VAR_INET_PROTOCOLS, var_inet_protocols);
     }
+    vstring_free(exp_buf);
     return (networks = mystrdup(mynetworks()));
 }
 
diff -Nru postfix-3.4.14/src/postscreen/postscreen_dnsbl.c postfix-3.4.23/src/postscreen/postscreen_dnsbl.c
--- postfix-3.4.14/src/postscreen/postscreen_dnsbl.c	2017-02-18 20:58:20.000000000 -0500
+++ postfix-3.4.23/src/postscreen/postscreen_dnsbl.c	2020-11-04 17:09:43.000000000 -0500
@@ -231,6 +231,7 @@
     int     weight;
     HTABLE_INFO *ht;
     char   *parse_err;
+    const char  *safe_dnsbl;
 
     /*
      * Parse the required DNSBL domain name, the optional reply filter and
@@ -271,8 +272,9 @@
 	ht = htable_enter(dnsbl_site_cache, saved_site, (void *) head);
 	/* Translate the DNSBL name into a safe name if available. */
 	if (psc_dnsbl_reply == 0
-	 || (head->safe_dnsbl = dict_get(psc_dnsbl_reply, saved_site)) == 0)
-	    head->safe_dnsbl = ht->key;
+	    || (safe_dnsbl = dict_get(psc_dnsbl_reply, saved_site)) == 0)
+	    safe_dnsbl = ht->key;
+	head->safe_dnsbl = mystrdup(safe_dnsbl);
 	if (psc_dnsbl_reply && psc_dnsbl_reply->error)
 	    msg_fatal("%s:%s lookup error", psc_dnsbl_reply->type,
 		      psc_dnsbl_reply->name);
diff -Nru postfix-3.4.14/src/postscreen/postscreen_tests.c postfix-3.4.23/src/postscreen/postscreen_tests.c
--- postfix-3.4.14/src/postscreen/postscreen_tests.c	2017-12-27 17:29:45.000000000 -0500
+++ postfix-3.4.23/src/postscreen/postscreen_tests.c	2021-07-24 18:31:05.000000000 -0400
@@ -175,6 +175,7 @@
      * at the time that the cache entry was written.
      */
     for (sp = time_stamps; sp < time_stamps + PSC_TINDX_COUNT; sp++) {
+	errno = 0;
 	*sp = strtoul(start, &cp, 10);
 	if (*start == 0 || (*cp != '\0' && *cp != ';') || errno == ERANGE)
 	    *sp = PSC_TIME_STAMP_DISABLED;
diff -Nru postfix-3.4.14/src/posttls-finger/posttls-finger.c postfix-3.4.23/src/posttls-finger/posttls-finger.c
--- postfix-3.4.14/src/posttls-finger/posttls-finger.c	2019-02-12 08:17:45.000000000 -0500
+++ postfix-3.4.23/src/posttls-finger/posttls-finger.c	2020-08-30 09:32:26.000000000 -0400
@@ -1988,7 +1988,7 @@
 	msg_fatal("bad '-a' option value: %s", state->options.addr_pref);
 
 #ifdef USE_TLS
-    if (state->tlsproxy_mode && state->reconnect)
+    if (state->tlsproxy_mode && state->reconnect >= 0)
 	msg_fatal("The -X and -r options are mutually exclusive");
 #endif
 
diff -Nru postfix-3.4.14/src/smtp/smtp.c postfix-3.4.23/src/smtp/smtp.c
--- postfix-3.4.14/src/smtp/smtp.c	2019-06-29 09:33:39.000000000 -0400
+++ postfix-3.4.23/src/smtp/smtp.c	2021-01-16 18:41:41.000000000 -0500
@@ -269,6 +269,12 @@
 /*	When a remote destination resolves to a combination of IPv4 and
 /*	IPv6 addresses, ensure that the Postfix SMTP client can try both
 /*	address types before it runs into the smtp_mx_address_limit.
+/* .PP
+/*	Available in Postfix 3.4.19 and later:
+/* .IP "\fBdnssec_probe (ns:.)\fR"
+/*	The DNS query type (default: "ns") and DNS query name (default:
+/*	".") that Postfix may use to determine whether DNSSEC validation
+/*	is available.
 /* MIME PROCESSING CONTROLS
 /* .ad
 /* .fi
diff -Nru postfix-3.4.14/src/smtp/smtp_sasl_proto.c postfix-3.4.23/src/smtp/smtp_sasl_proto.c
--- postfix-3.4.14/src/smtp/smtp_sasl_proto.c	2014-12-12 14:33:27.000000000 -0500
+++ postfix-3.4.23/src/smtp/smtp_sasl_proto.c	2020-11-04 18:38:42.000000000 -0500
@@ -102,6 +102,8 @@
 	    if (VSTRING_LEN(buf) > 0)
 		VSTRING_ADDCH(buf, ' ');
 	    vstring_strcat(buf, mech);
+	} else if (smtp_sasl_mechs->error) {
+	    msg_fatal("SASL mechanism filter failed for: '%s'", mech);
 	}
     }
     myfree(save_mech);
diff -Nru postfix-3.4.14/src/smtpd/smtpd.c postfix-3.4.23/src/smtpd/smtpd.c
--- postfix-3.4.14/src/smtpd/smtpd.c	2020-03-12 10:43:18.000000000 -0400
+++ postfix-3.4.23/src/smtpd/smtpd.c	2021-07-24 18:31:05.000000000 -0400
@@ -1283,6 +1283,7 @@
 int     var_defer_code;
 int     var_smtpd_err_sleep;
 int     var_non_fqdn_code;
+char   *var_bounce_rcpt;
 char   *var_error_rcpt;
 int     var_smtpd_delay_reject;
 char   *var_rest_classes;
@@ -5437,7 +5438,8 @@
 	 * obsolete, so we don't have to provide perfect support.
 	 */
 #ifdef USE_TLS
-	if (SMTPD_STAND_ALONE(state) == 0 && var_smtpd_tls_wrappermode) {
+	if (SMTPD_STAND_ALONE(state) == 0 && var_smtpd_tls_wrappermode
+	    && state->tls_context == 0) {
 #ifdef USE_TLSPROXY
 	    /* We garbage-collect the VSTREAM in smtpd_state_reset() */
 	    state->tlsproxy =
@@ -6389,6 +6391,7 @@
 	VAR_EOD_CHECKS, DEF_EOD_CHECKS, &var_eod_checks, 0, 0,
 	VAR_MAPS_RBL_DOMAINS, DEF_MAPS_RBL_DOMAINS, &var_maps_rbl_domains, 0, 0,
 	VAR_RBL_REPLY_MAPS, DEF_RBL_REPLY_MAPS, &var_rbl_reply_maps, 0, 0,
+	VAR_BOUNCE_RCPT, DEF_ERROR_RCPT, &var_bounce_rcpt, 1, 0,
 	VAR_ERROR_RCPT, DEF_ERROR_RCPT, &var_error_rcpt, 1, 0,
 	VAR_REST_CLASSES, DEF_REST_CLASSES, &var_rest_classes, 0, 0,
 	VAR_CANONICAL_MAPS, DEF_CANONICAL_MAPS, &var_canonical_maps, 0, 0,
diff -Nru postfix-3.4.14/src/smtpd/smtpd_chat.c postfix-3.4.23/src/smtpd/smtpd_chat.c
--- postfix-3.4.14/src/smtpd/smtpd_chat.c	2018-08-27 17:54:59.000000000 -0400
+++ postfix-3.4.23/src/smtpd/smtpd_chat.c	2021-07-24 18:31:05.000000000 -0400
@@ -316,7 +316,8 @@
 #define INDENT	4
 
     notice = post_mail_fopen_nowait(mail_addr_double_bounce(),
-				    var_error_rcpt,
+				    (state->error_mask & MAIL_ERROR_BOUNCE) ?
+				    var_bounce_rcpt : var_error_rcpt,
 				    MAIL_SRC_MASK_NOTIFY, NULL_TRACE_FLAGS,
 				    SMTPUTF8_FLAG_NONE, NO_QUEUE_ID);
     if (notice == 0) {
diff -Nru postfix-3.4.14/src/smtpd/smtpd_check.c postfix-3.4.23/src/smtpd/smtpd_check.c
--- postfix-3.4.14/src/smtpd/smtpd_check.c	2020-05-05 18:34:05.000000000 -0400
+++ postfix-3.4.23/src/smtpd/smtpd_check.c	2021-11-06 19:45:18.000000000 -0400
@@ -4249,8 +4249,8 @@
 	    }
 	} else if (is_map_command(state, name, CHECK_CCERT_ACL, &cpp)) {
 	    status = check_ccert_access(state, *cpp, def_acl);
-#ifdef USE_SASL_AUTH
 	} else if (is_map_command(state, name, CHECK_SASL_ACL, &cpp)) {
+#ifdef USE_SASL_AUTH
 	    if (var_smtpd_sasl_enable) {
 		if (state->sasl_username && state->sasl_username[0])
 		    status = check_sasl_access(state, *cpp, def_acl);
diff -Nru postfix-3.4.14/src/tls/tls_client.c postfix-3.4.23/src/tls/tls_client.c
--- postfix-3.4.14/src/tls/tls_client.c	2020-06-27 17:43:46.000000000 -0400
+++ postfix-3.4.23/src/tls/tls_client.c	2020-07-24 19:47:04.000000000 -0400
@@ -409,6 +409,11 @@
     off |= tls_bug_bits();
     SSL_CTX_set_options(client_ctx, off);
 
+    /* Enable all supported protocols */
+#if OPENSSL_VERSION_NUMBER >= 0x1010000fUL
+    SSL_CTX_set_min_proto_version(client_ctx, 0);
+#endif
+
     /*
      * Set the call-back routine for verbose logging.
      */
diff -Nru postfix-3.4.14/src/tls/tls_misc.c postfix-3.4.23/src/tls/tls_misc.c
--- postfix-3.4.14/src/tls/tls_misc.c	2020-06-10 17:16:49.000000000 -0400
+++ postfix-3.4.23/src/tls/tls_misc.c	2021-08-11 15:09:56.000000000 -0400
@@ -839,7 +839,7 @@
     EVP_PKEY *pkey = 0;
 
 #ifndef OPENSSL_NO_EC
-    EC_KEY *eckey;
+    const EC_KEY *eckey;
 
 #endif
 
@@ -964,6 +964,8 @@
 	 */
 	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);
@@ -1159,6 +1161,22 @@
 	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)
diff -Nru postfix-3.4.14/src/tls/tls_proxy_client_scan.c postfix-3.4.23/src/tls/tls_proxy_client_scan.c
--- postfix-3.4.14/src/tls/tls_proxy_client_scan.c	2019-02-11 08:32:27.000000000 -0500
+++ postfix-3.4.23/src/tls/tls_proxy_client_scan.c	2021-04-04 12:53:49.000000000 -0400
@@ -430,7 +430,8 @@
     if (buf)
 	vstring_free(buf);
     if (ret != 1) {
-	tls_proxy_client_certs_free(head);
+	if (head)
+	    tls_proxy_client_certs_free(head);
 	head = 0;
     }
     *(TLS_CERTS **) ptr = head;
@@ -489,7 +490,8 @@
     if (buf)
 	vstring_free(buf);
     if (ret != 1) {
-	tls_proxy_client_pkeys_free(head);
+	if (head)
+	    tls_proxy_client_pkeys_free(head);
 	head = 0;
     }
     *(TLS_PKEYS **) ptr = head;
@@ -538,7 +540,8 @@
 	ret = (ret == 3 ? 1 : -1);
     }
     if (ret != 1) {
-	tls_proxy_client_tlsa_free(head);
+	if (head)
+	    tls_proxy_client_tlsa_free(head);
 	head = 0;
     }
     *(TLS_TLSA **) ptr = head;
diff -Nru postfix-3.4.14/src/tls/tls_server.c postfix-3.4.23/src/tls/tls_server.c
--- postfix-3.4.14/src/tls/tls_server.c	2019-02-18 18:03:54.000000000 -0500
+++ postfix-3.4.23/src/tls/tls_server.c	2020-07-26 13:47:16.000000000 -0400
@@ -527,6 +527,12 @@
 
     SSL_CTX_set_options(server_ctx, off);
 
+    /* Enable all supported protocols */
+#if OPENSSL_VERSION_NUMBER >= 0x1010000fUL
+    SSL_CTX_set_min_proto_version(server_ctx, 0);
+    SSL_CTX_set_min_proto_version(sni_ctx, 0);
+#endif
+
     /*
      * Global protocol selection.
      */
diff -Nru postfix-3.4.14/src/tlsproxy/tlsproxy.c postfix-3.4.23/src/tlsproxy/tlsproxy.c
--- postfix-3.4.14/src/tlsproxy/tlsproxy.c	2020-06-20 15:32:27.000000000 -0400
+++ postfix-3.4.23/src/tlsproxy/tlsproxy.c	2020-08-30 09:32:26.000000000 -0400
@@ -994,8 +994,17 @@
     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",
+
+    /*
+     * Why not test for TLS_DANE_BASED()? Because the tlsproxy(8) client has
+     * already converted its DANE TLSA records into trust anchors, and
+     * therefore TLS_DANE_HASTA() will be true instead. That exercises the
+     * code path that updates the shared SSL_CTX with custom X.509
+     * verification callbacks for trust anchors.
+     */
+	&&TLS_DANE_HASTA(state->client_start_props->dane))
+	msg_warn("%s: DANE or local trust anchor based chain"
+		 " verification requested, but not available",
 		 state->client_start_props->namaddr);
     else
 	state->tls_context = tls_client_start(state->client_start_props);
@@ -1423,7 +1432,15 @@
 	}
 	state->appl_state = tlsp_client_init(state->tls_params,
 					     state->client_init_props,
-		      TLS_DANE_BASED(state->client_start_props->tls_level));
+
+	/*
+	 * Why not test for TLS_DANE_BASED()? Because the tlsproxy(8) client
+	 * has already converted its DANE TLSA records into trust anchors,
+	 * and therefore TLS_DANE_HASTA() will be true instead. That
+	 * exercises the code path that updates the shared SSL_CTX with
+	 * custom X.509 verification callbacks for trust anchors.
+	 */
+		      TLS_DANE_HASTA(state->client_start_props->dane) != 0);
 	ready = state->appl_state != 0;
 	break;
     case TLS_PROXY_FLAG_ROLE_SERVER:
diff -Nru postfix-3.4.14/src/util/dict_inline.c postfix-3.4.23/src/util/dict_inline.c
--- postfix-3.4.14/src/util/dict_inline.c	2018-11-05 19:25:30.000000000 -0500
+++ postfix-3.4.23/src/util/dict_inline.c	2021-04-04 12:53:49.000000000 -0400
@@ -113,9 +113,9 @@
     dict = dict_open3(DICT_TYPE_HT, name, open_flags, dict_flags);
     dict_type_override(dict, DICT_TYPE_INLINE);
     while ((nameval = mystrtokq(&cp, CHARS_COMMA_SP, CHARS_BRACE)) != 0) {
-	if ((nameval[0] != CHARS_BRACE[0]
-	     || (err = free_me = extpar(&nameval, CHARS_BRACE, EXTPAR_FLAG_STRIP)) == 0)
-	    && (err = split_qnameval(nameval, &vname, &value)) != 0)
+	if (nameval[0] == CHARS_BRACE[0])
+	    err = free_me = extpar(&nameval, CHARS_BRACE, EXTPAR_FLAG_STRIP);
+	if (err != 0 || (err = split_qnameval(nameval, &vname, &value)) != 0)
 	    break;
 
 	if ((dict->flags & DICT_FLAG_SRC_RHS_IS_FILE) != 0) {
diff -Nru postfix-3.4.14/src/util/dict_static.c postfix-3.4.23/src/util/dict_static.c
--- postfix-3.4.14/src/util/dict_static.c	2018-11-05 19:25:30.000000000 -0500
+++ postfix-3.4.23/src/util/dict_static.c	2020-11-04 17:12:53.000000000 -0500
@@ -73,6 +73,8 @@
 
     if (dict_static->value)
 	myfree(dict_static->value);
+    if (dict->fold_buf)
+	vstring_free(dict->fold_buf);
     dict_free(dict);
 }
 
diff -Nru postfix-3.4.14/src/util/dict_thash.c postfix-3.4.23/src/util/dict_thash.c
--- postfix-3.4.14/src/util/dict_thash.c	2017-12-27 17:29:45.000000000 -0500
+++ postfix-3.4.23/src/util/dict_thash.c	2021-06-14 16:20:18.000000000 -0400
@@ -46,6 +46,7 @@
 /* Utility library. */
 
 #include <msg.h>
+#include <mymalloc.h>
 #include <iostuff.h>
 #include <vstring.h>
 #include <stringops.h>
@@ -180,6 +181,24 @@
 			 " is this an alias file?", path, lineno);
 
 	    /*
+	     * Optionally treat the value as a filename, and replace the value
+	     * with the BASE64-encoded content of the named file.
+	     */
+	    if (dict_flags & DICT_FLAG_SRC_RHS_IS_FILE) {
+		VSTRING *base64_buf;
+		char   *err;
+
+		if ((base64_buf = dict_file_to_b64(dict, value)) == 0) {
+		    err = dict_file_get_error(dict);
+		    msg_warn("%s, line %d: %s: skipping this entry",
+			     VSTREAM_PATH(fp), lineno, err);
+		    myfree(err);
+		    continue;
+		}
+		value = vstring_str(base64_buf);
+	    }
+
+	    /*
 	     * Store the value under the key. Handle duplicates
 	     * appropriately. XXX Move this into dict_ht, but 1) that map
 	     * ignores duplicates by default and we would have to check that
diff -Nru postfix-3.4.14/src/util/mac_expand.c postfix-3.4.23/src/util/mac_expand.c
--- postfix-3.4.14/src/util/mac_expand.c	2018-01-21 13:13:34.000000000 -0500
+++ postfix-3.4.23/src/util/mac_expand.c	2021-07-24 18:31:05.000000000 -0400
@@ -226,6 +226,7 @@
     long    result;
     char   *remainder;
 
+    errno = 0;
     result = strtol(strval, &remainder, 10);
     if (*strval == 0 /* can't happen */ || *remainder != 0 || errno == ERANGE)
 	msg_fatal("mac_exp_eval: bad conversion: %s", strval);
diff -Nru postfix-3.4.14/src/util/slmdb.c postfix-3.4.23/src/util/slmdb.c
--- postfix-3.4.14/src/util/slmdb.c	2017-02-18 20:58:21.000000000 -0500
+++ postfix-3.4.23/src/util/slmdb.c	2021-06-01 17:02:52.000000000 -0400
@@ -386,12 +386,16 @@
      * - With a bulk-mode transaction we commit when the database is closed.
      */
     if (slmdb->open_flags & O_TRUNC) {
-	if ((status = mdb_drop(slmdb->txn, slmdb->dbi, 0)) != 0)
+	if ((status = mdb_drop(slmdb->txn, slmdb->dbi, 0)) != 0) {
+	    mdb_txn_abort(slmdb->txn);
+	    slmdb->txn = 0;
 	    return (status);
+	}
 	if ((slmdb->slmdb_flags & SLMDB_FLAG_BULK) == 0) {
-	    if ((status = mdb_txn_commit(slmdb->txn)) != 0)
-		return (status);
+	    status = mdb_txn_commit(slmdb->txn);
 	    slmdb->txn = 0;
+	    if (status != 0)
+		return (status);
 	}
     } else if ((slmdb->lmdb_flags & MDB_RDONLY) != 0
 	       || (slmdb->slmdb_flags & SLMDB_FLAG_BULK) == 0) {
@@ -582,11 +586,15 @@
      * Do the update.
      */
     if ((status = mdb_put(txn, slmdb->dbi, mdb_key, mdb_value, flags)) != 0) {
-	mdb_txn_abort(txn);
 	if (status != MDB_KEYEXIST) {
+	    mdb_txn_abort(txn);
 	    if ((status = slmdb_recover(slmdb, status)) == 0)
 		status = slmdb_put(slmdb, mdb_key, mdb_value, flags);
 	    SLMDB_API_RETURN(slmdb, status);
+	} else {
+	    /* Abort non-bulk transaction only. */
+	    if (slmdb->txn == 0)
+		mdb_txn_abort(txn);
 	}
     }
 

Attachment: signature.asc
Description: This is a digitally signed message part.


Reply to: