Prereq: "3.6.2"
diff -ur --new-file /var/tmp/postfix-3.6.2/src/global/mail_version.h ./src/global/mail_version.h
--- /var/tmp/postfix-3.6.2/src/global/mail_version.h	2021-07-24 19:16:27.000000000 -0400
+++ ./src/global/mail_version.h	2021-11-07 17:32:00.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	"20210724"
-#define MAIL_VERSION_NUMBER	"3.6.2"
+#define MAIL_RELEASE_DATE	"20211107"
+#define MAIL_VERSION_NUMBER	"3.6.3"
 
 #ifdef SNAPSHOT
 #define MAIL_VERSION_DATE	"-" MAIL_RELEASE_DATE
diff -ur --new-file /var/tmp/postfix-3.6.2/HISTORY ./HISTORY
--- /var/tmp/postfix-3.6.2/HISTORY	2021-07-24 18:41:41.000000000 -0400
+++ ./HISTORY	2021-11-07 18:12:28.000000000 -0500
@@ -25612,3 +25612,85 @@
 	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.
+
+20211022
+
+	Bugfix (introduced: Postfix 3.6): the known_tcp_ports setting
+	had no effect. Reported by Peter. The feature wasn't fully
+	implemented. Files: config_known_tcp_ports.c, mail_params.c,
+	posttls-finger/posttls-finger.c, smtp/smtp_connect.c,
+	util/find_inet.c, util/myaddrinfo.c.
+
+20211025
+
+	Bugfix (introduced: Postfix 3.6): mangled warning where a
+	hostname and warning message run together. Viktor Dukhovni.
+	File: tls/tls_dane.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 -ur --new-file /var/tmp/postfix-3.6.2/src/cleanup/cleanup_body_edit.c ./src/cleanup/cleanup_body_edit.c
--- /var/tmp/postfix-3.6.2/src/cleanup/cleanup_body_edit.c	2017-12-27 17:29:44.000000000 -0500
+++ ./src/cleanup/cleanup_body_edit.c	2021-11-05 18:29:08.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 -ur --new-file /var/tmp/postfix-3.6.2/src/cleanup/cleanup_milter.c ./src/cleanup/cleanup_milter.c
--- /var/tmp/postfix-3.6.2/src/cleanup/cleanup_milter.c	2020-09-25 16:11:17.000000000 -0400
+++ ./src/cleanup/cleanup_milter.c	2021-11-05 18:29:08.000000000 -0400
@@ -1836,7 +1836,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;
@@ -1848,7 +1849,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 -ur --new-file /var/tmp/postfix-3.6.2/src/global/config_known_tcp_ports.c ./src/global/config_known_tcp_ports.c
--- /var/tmp/postfix-3.6.2/src/global/config_known_tcp_ports.c	2021-04-19 14:56:10.000000000 -0400
+++ ./src/global/config_known_tcp_ports.c	2021-11-06 20:09:01.000000000 -0400
@@ -58,6 +58,8 @@
     ARGV   *association;
     char  **cpp;
 
+    clear_known_tcp_ports();
+
     /*
      * The settings is in the form of associations separated by comma. Split
      * it into separate associations.
diff -ur --new-file /var/tmp/postfix-3.6.2/src/global/mail_params.c ./src/global/mail_params.c
--- /var/tmp/postfix-3.6.2/src/global/mail_params.c	2021-04-18 17:10:45.000000000 -0400
+++ ./src/global/mail_params.c	2021-11-06 20:20:40.000000000 -0400
@@ -237,6 +237,7 @@
 #include <own_inet_addr.h>
 #include <mail_params.h>
 #include <compat_level.h>
+#include <config_known_tcp_ports.h>
 
  /*
   * Special configuration variables.
@@ -923,6 +924,11 @@
     util_utf8_enable = var_smtputf8_enable;
 
     /*
+     * Configure the known TCP port mappings.
+     */
+    config_known_tcp_ports(VAR_KNOWN_TCP_PORTS, var_known_tcp_ports);
+
+    /*
      * What protocols should we attempt to support? The result is stored in
      * the global inet_proto_table variable.
      */
diff -ur --new-file /var/tmp/postfix-3.6.2/src/milter/milter.h ./src/milter/milter.h
--- /var/tmp/postfix-3.6.2/src/milter/milter.h	2020-06-08 12:34:32.000000000 -0400
+++ ./src/milter/milter.h	2021-11-05 18:29:08.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 -ur --new-file /var/tmp/postfix-3.6.2/src/milter/milter8.c ./src/milter/milter8.c
--- /var/tmp/postfix-3.6.2/src/milter/milter8.c	2020-02-02 15:49:15.000000000 -0500
+++ ./src/milter/milter8.c	2021-11-05 18:29:08.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 -ur --new-file /var/tmp/postfix-3.6.2/src/postconf/postconf_builtin.c ./src/postconf/postconf_builtin.c
--- /var/tmp/postfix-3.6.2/src/postconf/postconf_builtin.c	2021-02-18 14:44:05.000000000 -0500
+++ ./src/postconf/postconf_builtin.c	2021-09-25 19:01:35.000000000 -0400
@@ -247,6 +247,7 @@
 static const char *pcf_mynetworks(void)
 {
     static const char *networks;
+    VSTRING *exp_buf;
     const char *junk;
 
     /*
@@ -255,10 +256,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);
@@ -266,7 +269,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);
@@ -274,12 +277,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 -ur --new-file /var/tmp/postfix-3.6.2/src/posttls-finger/posttls-finger.c ./src/posttls-finger/posttls-finger.c
--- /var/tmp/postfix-3.6.2/src/posttls-finger/posttls-finger.c	2021-04-18 16:44:01.000000000 -0400
+++ ./src/posttls-finger/posttls-finger.c	2021-11-06 20:09:01.000000000 -0400
@@ -1488,12 +1488,14 @@
     /*
      * Convert service to port number, network byte order.
      */
+    service = (char *) filter_known_tcp_port(service);
     if (alldig(service)) {
 	if ((port = atoi(service)) >= 65536 || port == 0)
-	    msg_fatal("bad network port in destination: %s", destination);
+	    msg_fatal("bad network port: %s for destination: %s",
+		      service, destination);
 	*portp = htons(port);
     } else {
-	if ((sp = getservbyname(filter_known_tcp_port(service), protocol)) != 0)
+	if ((sp = getservbyname(service, protocol)) != 0)
 	    *portp = sp->s_port;
 	else if (strcmp(service, "smtp") == 0)
 	    *portp = htons(25);
diff -ur --new-file /var/tmp/postfix-3.6.2/src/smtp/smtp_connect.c ./src/smtp/smtp_connect.c
--- /var/tmp/postfix-3.6.2/src/smtp/smtp_connect.c	2021-04-18 16:42:40.000000000 -0400
+++ ./src/smtp/smtp_connect.c	2021-11-06 20:09:01.000000000 -0400
@@ -356,12 +356,14 @@
     /*
      * Convert service to port number, network byte order.
      */
+    service = (char *) filter_known_tcp_port(service);
     if (alldig(service)) {
 	if ((port = atoi(service)) >= 65536 || port == 0)
-	    msg_fatal("bad network port in destination: %s", destination);
+	    msg_fatal("bad network port: %s for destination: %s",
+		      service, destination);
 	*portp = htons(port);
     } else {
-	if ((sp = getservbyname(filter_known_tcp_port(service), protocol)) == 0)
+	if ((sp = getservbyname(service, protocol)) == 0)
 	    msg_fatal("unknown service: %s/%s", service, protocol);
 	*portp = sp->s_port;
     }
diff -ur --new-file /var/tmp/postfix-3.6.2/src/smtpd/smtpd_check.c ./src/smtpd/smtpd_check.c
--- /var/tmp/postfix-3.6.2/src/smtpd/smtpd_check.c	2021-04-04 11:54:29.000000000 -0400
+++ ./src/smtpd/smtpd_check.c	2021-11-06 19:43:54.000000000 -0400
@@ -4374,8 +4374,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 -ur --new-file /var/tmp/postfix-3.6.2/src/tls/tls_dane.c ./src/tls/tls_dane.c
--- /var/tmp/postfix-3.6.2/src/tls/tls_dane.c	2020-07-19 12:18:48.000000000 -0400
+++ ./src/tls/tls_dane.c	2021-10-25 09:35:37.000000000 -0400
@@ -392,7 +392,7 @@
 	vstring_sprintf(top, "...");
     }
 
-    msg_warn("%s%s%s%s: %u %u %u %s%s%s", s1, s2, s3, s4, u, s, m, STR(top),
+    msg_warn("%s%s%s %s: %u %u %u %s%s%s", s1, s2, s3, s4, u, s, m, STR(top),
 	     dlen > MAX_DUMP_BYTES ? "..." : "",
 	     dlen > MAX_DUMP_BYTES ? STR(bot) : "");
 }
@@ -807,13 +807,13 @@
 	    continue;
 	}
 	if (ret == 0) {
-	    tlsa_carp(TLScontext->namaddr, ": ", "", "unusable TLSA RR",
+	    tlsa_carp(TLScontext->namaddr, ":", "", "unusable TLSA RR",
 		      tp->usage, tp->selector, tp->mtype, tp->data,
 		      tp->length);
 	    continue;
 	}
 	/* Internal problem in OpenSSL */
-	tlsa_carp(TLScontext->namaddr, ": ", "", "error loading trust settings",
+	tlsa_carp(TLScontext->namaddr, ":", "", "error loading trust settings",
 		  tp->usage, tp->selector, tp->mtype, tp->data, tp->length);
 	tls_print_errors();
 	return (-1);
diff -ur --new-file /var/tmp/postfix-3.6.2/src/tls/tls_misc.c ./src/tls/tls_misc.c
--- /var/tmp/postfix-3.6.2/src/tls/tls_misc.c	2020-07-26 17:27:35.000000000 -0400
+++ ./src/tls/tls_misc.c	2021-08-11 15:10:08.000000000 -0400
@@ -883,7 +883,7 @@
     EVP_PKEY *peer_pkey = 0;
 
 #ifndef OPENSSL_NO_EC
-    EC_KEY *eckey;
+    const EC_KEY *eckey;
 
 #endif
 
diff -ur --new-file /var/tmp/postfix-3.6.2/src/util/find_inet.c ./src/util/find_inet.c
--- /var/tmp/postfix-3.6.2/src/util/find_inet.c	2021-04-18 16:05:04.000000000 -0400
+++ ./src/util/find_inet.c	2021-11-06 20:29:34.000000000 -0400
@@ -85,12 +85,13 @@
     struct servent *sp;
     int     port;
 
+    service = filter_known_tcp_port(service);
     if (alldig(service) && (port = atoi(service)) != 0) {
 	if (port < 0 || port > 65535)
 	    msg_fatal("bad port number: %s", service);
 	return (htons(port));
     } else {
-	if ((sp = getservbyname(filter_known_tcp_port(service), protocol)) == 0)
+	if ((sp = getservbyname(service, protocol)) == 0)
 	    msg_fatal("unknown service: %s/%s", service, protocol);
 	return (sp->s_port);
     }
diff -ur --new-file /var/tmp/postfix-3.6.2/src/util/myaddrinfo.c ./src/util/myaddrinfo.c
--- /var/tmp/postfix-3.6.2/src/util/myaddrinfo.c	2021-04-18 16:03:51.000000000 -0400
+++ ./src/util/myaddrinfo.c	2021-11-06 20:09:01.000000000 -0400
@@ -271,6 +271,7 @@
     const char *proto;
     unsigned port;
 
+    service = filter_known_tcp_port(service);
     if (alldig(service)) {
 	port = atoi(service);
 	return (port < 65536 ? htons(port) : -1);
@@ -282,7 +283,7 @@
     } else {
 	return (-1);
     }
-    if ((sp = getservbyname(filter_known_tcp_port(service), proto)) != 0) {
+    if ((sp = getservbyname(service, proto)) != 0) {
 	return (sp->s_port);
     } else {
 	return (-1);
@@ -445,7 +446,12 @@
 	}
 #endif
     }
-    err = getaddrinfo(hostname, filter_known_tcp_port(service), &hints, res);
+    if (service) {
+	service = filter_known_tcp_port(service);
+	if (alldig(service))
+	    hints.ai_flags |= AI_NUMERICSERV;
+    }
+    err = getaddrinfo(hostname, service, &hints, res);
 #if defined(BROKEN_AI_NULL_SERVICE)
     if (service == 0 && err == 0) {
 	struct addrinfo *r;
@@ -561,7 +567,12 @@
 	}
 #endif
     }
-    err = getaddrinfo(hostaddr, filter_known_tcp_port(service), &hints, res);
+    if (service) {
+	service = filter_known_tcp_port(service);
+	if (alldig(service))
+	    hints.ai_flags |= AI_NUMERICSERV;
+    }
+    err = getaddrinfo(hostaddr, service, &hints, res);
 #if defined(BROKEN_AI_NULL_SERVICE)
     if (service == 0 && err == 0) {
 	struct addrinfo *r;