From: Wietse Venema Date: Sun, 7 Nov 2021 05:00:00 +0000 (-0500) Subject: postfix-3.7-20211107 X-Git-Tag: v3.7.0-RC1~9 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=a57e1429450753e2a323bbcdbf4f2d6f37493f49;p=thirdparty%2Fpostfix.git postfix-3.7-20211107 --- diff --git a/postfix/HISTORY b/postfix/HISTORY index b6c8e9eb7..e443a8627 100644 --- a/postfix/HISTORY +++ b/postfix/HISTORY @@ -25811,7 +25811,7 @@ Apologies for any names omitted. 20211022 Bugfix (introduced: Postfix 3.6): the known_tcp_ports setting - had no effect. Reported by Peter. It also wasn't fully + 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. @@ -25825,7 +25825,7 @@ Apologies for any names omitted. postscreen(8) dummy SMTP engine will no longer log a "non-UTF-8 key" warning when a remote SMTP client sends garbage. Instead, postscreen(8) will reject the command with the same server - repsonse as smtpd(8). File: postscreen/p[ostscreen_smtpd.c. + response as smtpd(8). File: postscreen/postscreen_smtpd.c. 20211025 @@ -25857,5 +25857,65 @@ Apologies for any names omitted. 20211030 Bugfix: check_ccert_access worked as expected, but produced - a spurious warning when Postfix is built without SASL + a spurious warning when Postfix was built without SASL support. Fix by Brad Barden. File: smtpd/smtpd_check.c. + +20211102 + + Bugfix for smtp_bind_address_enforce (change 20211026), file + descriptor leak. Found by Viktor. File: smtp/smtp_connect.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. + +20211107 + + Additional postcat flags for debuging a corrupted queue + file (-s: skip to offset; -r: don't follow pointer records). + File: postcat/postcat.c. diff --git a/postfix/WISHLIST b/postfix/WISHLIST index a1e55e8ca..9bbb46f43 100644 --- a/postfix/WISHLIST +++ b/postfix/WISHLIST @@ -1,5 +1,8 @@ Wish list: + Fix the body_edit_lockout safety: turn it on when editing, + and turn if off when done. + Add a pointer to http://mmogilvi.users.sourceforge.net/software/oauthbearer.html in documentation or on-line howtos. diff --git a/postfix/html/postcat.1.html b/postfix/html/postcat.1.html index 3035a634e..2928522d8 100644 --- a/postfix/html/postcat.1.html +++ b/postfix/html/postcat.1.html @@ -53,7 +53,14 @@ POSTCAT(1) POSTCAT(1) This feature is available in Postfix 2.0 and later. - -v Enable verbose logging for debugging purposes. Multiple -v + -r Print records in file order, don't follow pointer records. + + This feature is available in Postfix 3.7 and later. IP "-s off- + set" Skip to the specified queue file offset. + + This feature is available in Postfix 2.0 and later. + + -v Enable verbose logging for debugging purposes. Multiple -v options make the software increasingly verbose. DIAGNOSTICS @@ -64,19 +71,19 @@ POSTCAT(1) POSTCAT(1) Directory with Postfix configuration files. CONFIGURATION PARAMETERS - The following main.cf parameters are especially relevant to this pro- + The following main.cf parameters are especially relevant to this pro- gram. - The text below provides only a parameter summary. See postconf(5) for + The text below provides only a parameter summary. See postconf(5) for more details including examples. config_directory (see 'postconf -d' output) - The default location of the Postfix main.cf and master.cf con- + The default location of the Postfix main.cf and master.cf con- figuration files. import_environment (see 'postconf -d' output) - The list of environment parameters that a privileged Postfix - process will import from a non-Postfix parent process, or + The list of environment parameters that a privileged Postfix + process will import from a non-Postfix parent process, or name=value environment overrides. queue_directory (see 'postconf -d' output) diff --git a/postfix/man/man1/postcat.1 b/postfix/man/man1/postcat.1 index 0606cbfc9..78c7521fc 100644 --- a/postfix/man/man1/postcat.1 +++ b/postfix/man/man1/postcat.1 @@ -51,6 +51,14 @@ Print the queue file offset of each record. Search the Postfix queue for the named \fIfiles\fR instead of taking the names literally. +This feature is available in Postfix 2.0 and later. +.IP \fB\-r\fR +Print records in file order, don't follow pointer records. + +This feature is available in Postfix 3.7 and later. +IP "\fB-s \fIoffset\fR" +Skip to the specified queue file offset. + This feature is available in Postfix 2.0 and later. .IP \fB\-v\fR Enable verbose logging for debugging purposes. Multiple \fB\-v\fR diff --git a/postfix/src/cleanup/cleanup_body_edit.c b/postfix/src/cleanup/cleanup_body_edit.c index 93eadae0b..2fff1bcad 100644 --- a/postfix/src/cleanup/cleanup_body_edit.c +++ b/postfix/src/cleanup/cleanup_body_edit.c @@ -207,7 +207,7 @@ int cleanup_body_edit_write(CLEANUP_STATE *state, int rec_type, /* * 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 --git a/postfix/src/cleanup/cleanup_milter.c b/postfix/src/cleanup/cleanup_milter.c index 556a50895..fc5486fdc 100644 --- a/postfix/src/cleanup/cleanup_milter.c +++ b/postfix/src/cleanup/cleanup_milter.c @@ -1836,7 +1836,8 @@ static const char *cleanup_del_rcpt(void *context, const char *ext_rcpt) /* 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 @@ static const char *cleanup_repl_body(void *context, int cmd, VSTRING *buf) */ 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: @@ -2547,6 +2548,16 @@ int main(int unused_argc, char **argv) } else { msg_warn("bad verbose argument"); } + } else if (strcmp(argv->argv[0], "line_length_limit") == 0) { + if (argv->argc != 2) { + msg_warn("bad line_length_limit argument count: %ld", + (long) argv->argc); + } else if (alldig(argv->argv[1]) == 0) { + msg_warn("bad line_length_limit argument count: %ld", + (long) argv->argc); + } else if ((var_line_limit = atoi(argv->argv[1])) < DEF_LINE_LIMIT) { + msg_warn("bad line_length_limit argument"); + } } else if (strcmp(argv->argv[0], "open") == 0) { if (state->dst != 0) { msg_info("closing %s", VSTREAM_PATH(state->dst)); diff --git a/postfix/src/global/mail_version.h b/postfix/src/global/mail_version.h index 5766e2cd8..b74f005e5 100644 --- a/postfix/src/global/mail_version.h +++ b/postfix/src/global/mail_version.h @@ -20,7 +20,7 @@ * Patches change both the patchlevel and the release date. Snapshots have no * patchlevel; they change the release date only. */ -#define MAIL_RELEASE_DATE "20211030" +#define MAIL_RELEASE_DATE "20211107" #define MAIL_VERSION_NUMBER "3.7" #ifdef SNAPSHOT diff --git a/postfix/src/milter/milter.h b/postfix/src/milter/milter.h index fb59099a6..3a1e3f945 100644 --- a/postfix/src/milter/milter.h +++ b/postfix/src/milter/milter.h @@ -100,7 +100,7 @@ typedef const char *(*MILTER_DEL_HEADER_FN) (void *, ssize_t, const char *); 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 --git a/postfix/src/milter/milter8.c b/postfix/src/milter/milter8.c index 1e6d49616..ab2bc868c 100644 --- a/postfix/src/milter/milter8.c +++ b/postfix/src/milter/milter8.c @@ -1147,10 +1147,12 @@ static const char *milter8_event(MILTER8 *milter, int event, 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 @@ static const char *milter8_event(MILTER8 *milter, int event, 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 @@ static const char *milter8_event(MILTER8 *milter, int event, 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 --git a/postfix/src/postcat/postcat.c b/postfix/src/postcat/postcat.c index 002e02558..2a8257965 100644 --- a/postfix/src/postcat/postcat.c +++ b/postfix/src/postcat/postcat.c @@ -46,6 +46,14 @@ /* of taking the names literally. /* /* This feature is available in Postfix 2.0 and later. +/*.IP \fB-r\fR +/* Print records in file order, don't follow pointer records. +/* +/* This feature is available in Postfix 3.7 and later. +/* IP "\fB-s \fIoffset\fR" +/* Skip to the specified queue file offset. +/* +/* This feature is available in Postfix 2.0 and later. /* .IP \fB-v\fR /* Enable verbose logging for debugging purposes. Multiple \fB-v\fR /* options make the software increasingly verbose. @@ -138,6 +146,7 @@ #define PC_FLAG_PRINT_BODY (1<<4) /* print body records */ #define PC_FLAG_PRINT_RTYPE_DEC (1<<5) /* print decimal record type */ #define PC_FLAG_PRINT_RTYPE_SYM (1<<6) /* print symbolic record type */ +#define PC_FLAG_RAW (1<<7) /* don't follow pointers */ #define PC_MASK_PRINT_TEXT (PC_FLAG_PRINT_HEADER | PC_FLAG_PRINT_BODY) #define PC_MASK_PRINT_ALL (PC_FLAG_PRINT_ENV | PC_MASK_PRINT_TEXT) @@ -149,6 +158,8 @@ #define PC_STATE_HEADER 1 /* primary header */ #define PC_STATE_BODY 2 /* other */ +off_t start_offset = 0; + #define STR vstring_str #define LEN VSTRING_LEN @@ -174,10 +185,27 @@ static void postcat(VSTREAM *fp, VSTRING *buffer, int flags) #define TEXT_RECORD(rec_type) \ (rec_type == REC_TYPE_CONT || rec_type == REC_TYPE_NORM) + /* + * Skip over or absorb some bytes. + */ + if (start_offset > 0) { + if (fp == VSTREAM_IN) { + for (offset = 0; offset < start_offset; offset++) + if (VSTREAM_GETC(fp) == VSTREAM_EOF) + msg_fatal("%s: skip %ld bytes failed after %ld", + VSTREAM_PATH(fp), start_offset, offset); + } else { + if (vstream_fseek(fp, start_offset, SEEK_SET) < 0) + msg_fatal("%s: seek to %ld: %m", + VSTREAM_PATH(fp), start_offset); + } + } + /* * See if this is a plausible file. */ - if ((ch = VSTREAM_GETC(fp)) != VSTREAM_EOF) { + if (start_offset == 0 && (flags & PC_FLAG_RAW) == 0 + && (ch = VSTREAM_GETC(fp)) != VSTREAM_EOF) { if (!strchr(REC_TYPE_ENVELOPE, ch)) { msg_warn("%s: input is not a valid queue file", VSTREAM_PATH(fp)); return; @@ -188,7 +216,7 @@ static void postcat(VSTREAM *fp, VSTRING *buffer, int flags) /* * Other preliminaries. */ - if (flags & PC_FLAG_PRINT_ENV) + if (start_offset == 0 && (flags & PC_FLAG_PRINT_ENV)) vstream_printf("*** ENVELOPE RECORDS %s ***\n", VSTREAM_PATH(fp)); state = PC_STATE_ENV; @@ -293,6 +321,8 @@ static void postcat(VSTREAM *fp, VSTRING *buffer, int flags) /* Optional output. */ if (flags & PC_FLAG_PRINT_ENV) PRINT_MARKER(flags, fp, offset, rec_type, "MESSAGE FILE END"); + if (flags & PC_FLAG_RAW) + continue; /* Terminate the state machine. */ break; } else if (rec_type == REC_TYPE_PTR) { @@ -301,7 +331,8 @@ static void postcat(VSTREAM *fp, VSTRING *buffer, int flags) if (do_print) PRINT_RECORD(flags, offset, rec_type, STR(buffer)); /* Skip to the pointer's target record. */ - if (rec_goto(fp, STR(buffer)) == REC_TYPE_ERROR) + if ((flags & PC_FLAG_RAW) == 0 + && rec_goto(fp, STR(buffer)) == REC_TYPE_ERROR) msg_fatal("bad pointer record, or input is not seekable"); continue; } else if (rec_type == REC_TYPE_SIZE) { @@ -314,7 +345,7 @@ static void postcat(VSTREAM *fp, VSTRING *buffer, int flags) } else { if (sscanf(STR(buffer), "%ld %ld", &data_size, &data_offset) != 2 || data_offset <= 0 || data_size <= 0) - msg_fatal("invalid size record: %.100s", STR(buffer)); + msg_warn("invalid size record: %.100s", STR(buffer)); /* Optimization: skip to the message header. */ if ((flags & PC_FLAG_PRINT_ENV) == 0) { if (vstream_fseek(fp, data_offset, SEEK_SET) < 0) @@ -460,7 +491,7 @@ int main(int argc, char **argv) /* * Parse JCL. */ - while ((ch = GETOPT(argc, argv, "bc:dehoqv")) > 0) { + while ((ch = GETOPT(argc, argv, "bc:dehoqrs:v")) > 0) { switch (ch) { case 'b': flags |= PC_FLAG_PRINT_BODY; @@ -484,6 +515,13 @@ int main(int argc, char **argv) case 'q': flags |= PC_FLAG_SEARCH_QUEUE; break; + case 'r': + flags |= PC_FLAG_RAW; + break; + case 's': + if (!alldig(optarg) || (start_offset = atol(optarg)) <= 0) + msg_fatal("bad offset: %s", optarg); + break; case 'v': msg_verbose++; break; diff --git a/postfix/src/smtp/smtp_connect.c b/postfix/src/smtp/smtp_connect.c index b7bdf253b..a968c8344 100644 --- a/postfix/src/smtp/smtp_connect.c +++ b/postfix/src/smtp/smtp_connect.c @@ -203,6 +203,11 @@ static SMTP_SESSION *smtp_connect_addr(SMTP_ITERATOR *iter, DSN_BUF *why, if ((sock = socket(sa->sa_family, SOCK_STREAM, 0)) < 0) msg_fatal("%s: socket: %m", myname); +#define RETURN_EARLY() do { \ + (void) close(sock); \ + return (0); \ + } while (0) + if (inet_windowsize > 0) set_inet_windowsize(sock, inet_windowsize); @@ -233,7 +238,7 @@ static SMTP_SESSION *smtp_connect_addr(SMTP_ITERATOR *iter, DSN_BUF *why, if (var_smtp_bind_addr_enforce) { freeaddrinfo(res0); dsb_simple(why, "4.4.0", "server configuration error"); - return (0); + RETURN_EARLY(); } } else if (msg_verbose) msg_info("%s: bind %s", myname, bind_addr);