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.
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
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.
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.
This feature is available in Postfix 2.0 and later.
- <b>-v</b> Enable verbose logging for debugging purposes. Multiple <b>-v</b>
+ <b>-r</b> Print records in file order, don't follow pointer records.
+
+ This feature is available in Postfix 3.7 and later. IP "<b>-s</b> <i>off-</i>
+ <i>set</i>" Skip to the specified queue file offset.
+
+ This feature is available in Postfix 2.0 and later.
+
+ <b>-v</b> Enable verbose logging for debugging purposes. Multiple <b>-v</b>
options make the software increasingly verbose.
<b>DIAGNOSTICS</b>
Directory with Postfix configuration files.
<b>CONFIGURATION PARAMETERS</b>
- The following <a href="postconf.5.html"><b>main.cf</b></a> parameters are especially relevant to this pro-
+ The following <a href="postconf.5.html"><b>main.cf</b></a> parameters are especially relevant to this pro-
gram.
- The text below provides only a parameter summary. See <a href="postconf.5.html"><b>postconf</b>(5)</a> for
+ The text below provides only a parameter summary. See <a href="postconf.5.html"><b>postconf</b>(5)</a> for
more details including examples.
<b><a href="postconf.5.html#config_directory">config_directory</a> (see 'postconf -d' output)</b>
- The default location of the Postfix <a href="postconf.5.html">main.cf</a> and <a href="master.5.html">master.cf</a> con-
+ The default location of the Postfix <a href="postconf.5.html">main.cf</a> and <a href="master.5.html">master.cf</a> con-
figuration files.
<b><a href="postconf.5.html#import_environment">import_environment</a> (see 'postconf -d' output)</b>
- The list of environment parameters that a privileged Postfix
- process will import from a non-Postfix parent process, or
+ The list of environment parameters that a privileged Postfix
+ process will import from a non-Postfix parent process, or
name=value environment overrides.
<b><a href="postconf.5.html#queue_directory">queue_directory</a> (see 'postconf -d' output)</b>
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
/*
* 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);
/* 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;
*/
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:
} 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));
* 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
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 */
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);
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. */
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);
}
}
/* 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.
#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)
#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
#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;
/*
* 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;
/* 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) {
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) {
} 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)
/*
* 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;
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;
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);
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);