]> git.ipfire.org Git - thirdparty/postfix.git/commitdiff
postfix-3.7-20211107
authorWietse Venema <wietse@porcupine.org>
Sun, 7 Nov 2021 05:00:00 +0000 (00:00 -0500)
committerViktor Dukhovni <postfix-users@dukhovni.org>
Wed, 19 Jan 2022 06:36:07 +0000 (01:36 -0500)
postfix/HISTORY
postfix/WISHLIST
postfix/html/postcat.1.html
postfix/man/man1/postcat.1
postfix/src/cleanup/cleanup_body_edit.c
postfix/src/cleanup/cleanup_milter.c
postfix/src/global/mail_version.h
postfix/src/milter/milter.h
postfix/src/milter/milter8.c
postfix/src/postcat/postcat.c
postfix/src/smtp/smtp_connect.c

index b6c8e9eb74617f3a60c15d9d64ddabe2a992e2f6..e443a86270ac8cfa2c6e5d76d4afa0ec3499b03f 100644 (file)
@@ -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.
index a1e55e8ca124732d799d984b45397e19ec194540..9bbb46f433e227caa00247266ac38767d95de8de 100644 (file)
@@ -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.
index 3035a634e3f4d0feb0b69d16d79924acb6eec839..2928522d8464c489346a498adb71d1cfa6ffe533 100644 (file)
@@ -53,7 +53,14 @@ POSTCAT(1)                                                          POSTCAT(1)
 
               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>
@@ -64,19 +71,19 @@ POSTCAT(1)                                                          POSTCAT(1)
               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>
index 0606cbfc9df3354a955400180cdbd6fa3823c4fa..78c7521fc556982438c5f5fe84c744f7b13ebc0e 100644 (file)
@@ -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
index 93eadae0b82542d36491ff4c8254b1d4ea6f7732..2fff1bcadd4788a92ce4c81daac3d37a6b35be90 100644 (file)
@@ -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);
index 556a508950a6b1b60a2990e3bbce9fd100d9a9f8..fc5486fdc1199779d8ad9f89b143a7bec525905b 100644 (file)
@@ -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));
index 5766e2cd8b91cc2cee8e563246bbea30748647b7..b74f005e5176884e38da791e6137d9a43f8e0a97 100644 (file)
@@ -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
index fb59099a6659d5fbec72ade8b0222e86bcda195c..3a1e3f94524dbe99745e24969413105d126cba38 100644 (file)
@@ -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 */
index 1e6d496167f645b75a191a745fcdcc5b15a389e2..ab2bc868c488b747065e7565e90bfcd930c47878 100644 (file)
@@ -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);
                        }
                    }
index 002e0255807110d1b5300c5eaf56e74ea0db1281..2a825796558b38663cbd9daa78f61d5e8c946c26 100644 (file)
 /*     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
 
@@ -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;
index b7bdf253bb7d4e2fae1cb31b9c558f8c709de2b3..a968c83446c8ce920aa382721f4e09a4d8f3adc8 100644 (file)
@@ -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);