]> git.ipfire.org Git - thirdparty/postfix.git/commitdiff
postfix-3.7.11 v3.7.11
authorWietse Z Venema <wietse@porcupine.org>
Mon, 4 Mar 2024 05:00:00 +0000 (00:00 -0500)
committerViktor Dukhovni <ietf-dane@dukhovni.org>
Tue, 5 Mar 2024 18:35:17 +0000 (13:35 -0500)
17 files changed:
postfix/HISTORY
postfix/src/cleanup/cleanup_milter.c
postfix/src/dns/dns.h
postfix/src/dns/dns_lookup.c
postfix/src/dns/dns_rr.c
postfix/src/dns/test_dns_lookup.c
postfix/src/global/mail_version.h
postfix/src/oqmgr/qmgr_deliver.c
postfix/src/postqueue/showq_json.c
postfix/src/posttls-finger/posttls-finger.c
postfix/src/qmgr/qmgr_deliver.c
postfix/src/smtp/smtp_addr.c
postfix/src/smtpd/smtpd.c
postfix/src/smtpd/smtpd_check.c
postfix/src/smtpd/smtpd_sasl_glue.c
postfix/src/tlsmgr/tlsmgr.c
postfix/src/xsasl/xsasl_dovecot_server.c

index dd699159effbb9e75f19caf2402a823741065665..1f1e2c4b3f1c221b5e5e1b9cd053f02d77f49fda 100644 (file)
@@ -26681,3 +26681,83 @@ Apologies for any names omitted.
        Files: mantools/postlink, proto/postconf.proto,
        global/mail_params.h, global/smtp_stream.c, global/smtp_stream.h,
        smtpd/smtpd.c, smtpd/smtpd_check.[hc].
+
+20231102
+
+       Bugfix (defect introduced: Postfix 2.3, date 20051222): the
+       Dovecot auth client did not reset the 'reason' from  a
+       previous Dovecot auth service response, before parsing the
+       next Dovecot auth server response in the same SMTP session.
+       Reported by Stephan Bosch, File: xsasl/xsasl_dovecot_server.c.
+
+20231105
+
+       Cleanup: Postfix SMTP server response with an empty
+       authentication failure reason. File: smtpd/smtpd_sasl_glue.c.
+
+20231208
+
+       Bugfix (defect introduced: Postfix 3.1, date: 20151128):
+       "postqueue -j" produced broken JSON when escaping a control
+       character as \uXXXX. Found during code maintenance. File:
+       postqueue/showq_json.c.
+
+20231211
+
+       Cleanup: posttls-finger certificate match expectations for
+       all TLS security levels, including warnings for levels that
+       don't implement certificate matching. Viktor Dukhovni.
+       File: posttls-finger.c.
+
+20231213
+
+       Bugfix (defect introduced: Postfix 2.3): after prepending
+       a message header with a Postfix access table PREPEND action,
+       a Milter request to delete or update an existing header
+       could have no effect, or it could target the wrong instance
+       of an existing header. Root cause: the fix dated 20141018
+       for the Postfix Milter client was incomplete. The client
+       did correctly hide the first, Postfix-generated, Received:
+       header when sending message header information to a Milter
+       with the smfi_header() application callback function, but
+       it was still hiding the first header (instead of the first
+       Received: header) when handling requests from a Milter to
+       delete or update an existing header. Problem report by
+       Carlos Velasco. This change was verified to have no effect
+       on requests from a Milter to add or insert a header. File:
+       cleanup/cleanup_milter.c.
+
+20240124
+
+       Workaround: tlsmgr logfile spam. Some OS lies under load:
+       it says that a socket is readable, then it says that the
+       socket has unread data, and then it says that read returns
+       EOF, causing Postfix to spam the log with a warning message.
+       File: tlsmgr/tlsmgr.c.
+
+       Bugfix (defect introduced: Postfix 3.4): the SMTP server's
+       BDAT command handler could be tricked to read $message_size_limit
+       bytes into memory. Found during code maintenance. File:
+       smtpd/smtpd.c.
+
+20240209
+
+       Performance: eliminate worst-case behavior where the queue
+       manager defers delivery to all destinations over a specific
+       delivery transport, after only a single delivery agent
+       failure. The scheduler now throttles one destination, and
+       allows deliveries to other destinations to keep making
+       progress. Files: *qmgr/qmgr_deliver.c.
+
+20240226
+
+       Safety: drop and log over-size DNS responses resulting in
+       more than 100 records. This 20x larger than the number of
+       server addresses that the Postfix SMTP client is willing
+       to consider when delivering mail, and is well below the
+       number of records that could cause a tail recursion crash
+       in dns_rr_append() as reported by Toshifumi Sakaguchi. This
+       also limits the number of DNS requests from check_*_*_access
+       restrictions. Files: dns/dns.h, dns/dns_lookup.c, dns/dns_rr.c,
+       dns/test_dns_lookup.c, posttls-finger/posttls-finger.c,
+       smtp/smtp_addr.c, smtpd/smtpd_check.c.
index 11510b55959ebf36e3c9d078c8ebc151655f34f8..beb70f82b229fedb0762893e2fcdf36890129621 100644 (file)
 #include <dsn_util.h>
 #include <xtext.h>
 #include <info_log_addr_form.h>
+#include <header_opts.h>
 
 /* Application-specific. */
 
@@ -754,14 +755,26 @@ static const char *cleanup_add_header(void *context, const char *name,
      */
 }
 
+/* hidden_header - respect milter header hiding protocol */
+
+static int hidden_header(VSTRING *buf, ARGV *auto_hdrs, int *hide_done)
+{
+    char  **cpp;
+    int     mask;
+
+    for (cpp = auto_hdrs->argv, mask = 1; *cpp; cpp++, mask <<= 1)
+       if ((*hide_done & mask) == 0 && strncmp(*cpp, STR(buf), LEN(buf)) == 0)
+           return (*hide_done |= mask);
+    return (0);
+}
+
 /* cleanup_find_header_start - find specific header instance */
 
 static off_t cleanup_find_header_start(CLEANUP_STATE *state, ssize_t index,
                                               const char *header_label,
                                               VSTRING *buf,
                                               int *prec_type,
-                                              int allow_ptr_backup,
-                                              int skip_headers)
+                                              int allow_ptr_backup)
 {
     const char *myname = "cleanup_find_header_start";
     off_t   curr_offset;               /* offset after found record */
@@ -770,7 +783,7 @@ static off_t cleanup_find_header_start(CLEANUP_STATE *state, ssize_t index,
     int     rec_type = REC_TYPE_ERROR;
     int     last_type;
     ssize_t len;
-    int     hdr_count = 0;
+    int     hide_done = 0;
 
     if (msg_verbose)
        msg_info("%s: index %ld name \"%s\"",
@@ -912,11 +925,10 @@ static off_t cleanup_find_header_start(CLEANUP_STATE *state, ssize_t index,
            break;
        }
        /* This the start of a message header. */
-       else if (hdr_count++ < skip_headers)
-            /* Reset the saved PTR record and update last_type. */ ;
        else if ((header_label == 0
                  || (strncasecmp(header_label, STR(buf), len) == 0
-                     && (strlen(header_label) == len)))
+                     && strlen(header_label) == len
+                     && !hidden_header(buf, state->auto_hdrs, &hide_done)))
                 && --index == 0) {
            /* If we have a saved PTR record, it points to start of header. */
            break;
@@ -1182,15 +1194,12 @@ static const char *cleanup_ins_header(void *context, ssize_t index,
      */
 #define NO_HEADER_NAME ((char *) 0)
 #define ALLOW_PTR_BACKUP       1
-#define SKIP_ONE_HEADER                1
-#define DONT_SKIP_HEADERS      0
 
     if (index < 1)
        index = 1;
     old_rec_offset = cleanup_find_header_start(state, index, NO_HEADER_NAME,
                                               old_rec_buf, &old_rec_type,
-                                              ALLOW_PTR_BACKUP,
-                                              DONT_SKIP_HEADERS);
+                                              ALLOW_PTR_BACKUP);
     if (old_rec_offset == CLEANUP_FIND_HEADER_IOERROR)
        /* Warning and errno->error mapping are done elsewhere. */
        CLEANUP_INS_HEADER_RETURN(cleanup_milter_error(state, 0));
@@ -1270,8 +1279,7 @@ static const char *cleanup_upd_header(void *context, ssize_t index,
     rec_buf = vstring_alloc(100);
     old_rec_offset = cleanup_find_header_start(state, index, new_hdr_name,
                                               rec_buf, &last_type,
-                                              NO_PTR_BACKUP,
-                                              SKIP_ONE_HEADER);
+                                              NO_PTR_BACKUP);
     if (old_rec_offset == CLEANUP_FIND_HEADER_IOERROR)
        /* Warning and errno->error mapping are done elsewhere. */
        CLEANUP_UPD_HEADER_RETURN(cleanup_milter_error(state, 0));
@@ -1333,8 +1341,7 @@ static const char *cleanup_del_header(void *context, ssize_t index,
 
     rec_buf = vstring_alloc(100);
     header_offset = cleanup_find_header_start(state, index, hdr_name, rec_buf,
-                                             &last_type, NO_PTR_BACKUP,
-                                             SKIP_ONE_HEADER);
+                                             &last_type, NO_PTR_BACKUP);
     if (header_offset == CLEANUP_FIND_HEADER_IOERROR)
        /* Warning and errno->error mapping are done elsewhere. */
        CLEANUP_DEL_HEADER_RETURN(cleanup_milter_error(state, 0));
index 5f53dbc8ffaf3ed09f770b4d1ec9c9f3dc5c5423..0d9c8a38baa9c858c9de1d5b8977f5fc98acfed0 100644 (file)
@@ -159,11 +159,17 @@ typedef struct DNS_RR {
     unsigned int ttl;                  /* always */
     unsigned int dnssec_valid;         /* DNSSEC validated */
     unsigned short pref;               /* T_MX only */
+    /* Assume that flags lives in what was previously padding */
+    unsigned short flags;              /* DNS_RR_FLAG_XX, see below */
     struct DNS_RR *next;               /* linkage */
     size_t  data_len;                  /* actual data size */
     char    data[1];                   /* actually a bunch of data */
 } DNS_RR;
 
+#define DNS_RR_FLAG_TRUNCATED  (1<<0)
+
+#define DNS_RR_IS_TRUNCATED(rr)        ((rr)->flags & DNS_RR_FLAG_TRUNCATED)
+
  /*
   * dns_strerror.c
   */
@@ -197,6 +203,7 @@ extern int dns_rr_compare_pref_any(DNS_RR *, DNS_RR *);
 extern int dns_rr_compare_pref(DNS_RR *, DNS_RR *);
 extern DNS_RR *dns_rr_shuffle(DNS_RR *);
 extern DNS_RR *dns_rr_remove(DNS_RR *, DNS_RR *);
+extern int var_dns_rr_list_limit;
 
  /*
   * dns_rr_to_pa.c
index 615902d4139d1ae6555909fdaf71699e813df7f5..fa7fcb015984a0adf65b7e844b22fd2072fe6fe1 100644 (file)
@@ -960,6 +960,8 @@ static int dns_get_answer(const char *orig_name, DNS_REPLY *reply, int type,
                    resource_found++;
                    rr->dnssec_valid = *maybe_secure ? reply->dnssec_ad : 0;
                    *rrlist = dns_rr_append(*rrlist, rr);
+                   if (DNS_RR_IS_TRUNCATED(*rrlist))
+                       break;
                } else if (status == DNS_NULLMX) {
                    CORRUPT(status);            /* TODO: use better name */
                } else if (not_found_status != DNS_RETRY)
@@ -1184,8 +1186,11 @@ int     dns_lookup_rl(const char *name, unsigned flags, DNS_RR **rrlist,
                     name, dns_strtype(type), dns_str_resflags(flags));
        status = dns_lookup_x(name, type, flags, rrlist ? &rr : (DNS_RR **) 0,
                              fqdn, why, rcode, lflags);
-       if (rrlist && rr)
+       if (rrlist && rr) {
            *rrlist = dns_rr_append(*rrlist, rr);
+           if (DNS_RR_IS_TRUNCATED(*rrlist))
+               break;
+       }
        if (status == DNS_OK) {
            if (lflags & DNS_REQ_FLAG_STOP_OK)
                break;
@@ -1236,8 +1241,11 @@ int     dns_lookup_rv(const char *name, unsigned flags, DNS_RR **rrlist,
                     name, dns_strtype(type), dns_str_resflags(flags));
        status = dns_lookup_x(name, type, flags, rrlist ? &rr : (DNS_RR **) 0,
                              fqdn, why, rcode, lflags);
-       if (rrlist && rr)
+       if (rrlist && rr) {
            *rrlist = dns_rr_append(*rrlist, rr);
+           if (DNS_RR_IS_TRUNCATED(*rrlist))
+               break;
+       }
        if (status == DNS_OK) {
            if (lflags & DNS_REQ_FLAG_STOP_OK)
                break;
index b550788b992277174ff733157d03c3b26d3bcfdf..cf82f9f04f29ab7cc5e83d100bfa49af4dceeb16 100644 (file)
@@ -49,6 +49,8 @@
 /*     DNS_RR  *dns_rr_remove(list, record)
 /*     DNS_RR  *list;
 /*     DNS_RR  *record;
+/*
+/*     int     var_dns_rr_list_limit;
 /* DESCRIPTION
 /*     The routines in this module maintain memory for DNS resource record
 /*     information, and maintain lists of DNS resource records.
 /*
 /*     dns_rr_copy() makes a copy of a resource record.
 /*
-/*     dns_rr_append() appends a resource record to a (list of) resource
-/*     record(s).
-/*     A null input list is explicitly allowed.
+/*     dns_rr_append() appends an input resource record list to
+/*     an output list. Null arguments are explicitly allowed.
+/*     When the result would be longer than var_dns_rr_list_limit
+/*     (default: 100), dns_rr_append() logs a warning, flags the
+/*     output list as truncated, and discards the excess elements.
+/*     Once an output list is flagged as truncated (test with
+/*     DNS_RR_IS_TRUNCATED()), the caller is expected to stop
+/*     trying to append records to that list. Note: the 'truncated'
+/*     flag is transitive, i.e. when appending a input list that
+/*     was flagged as truncated to an output list, the output list
+/*     will also be flagged as truncated.
 /*
 /*     dns_rr_sort() sorts a list of resource records into ascending
 /*     order according to a user-specified criterion. The result is the
 
 #include "dns.h"
 
+ /*
+  * A generous safety limit for the number of DNS resource records that the
+  * Postfix DNS client library will admit into a list. The default value 100
+  * is 20x the default limit on the number address records that the Postfix
+  * SMTP client is willing to consider.
+  * 
+  * Mutable, to make code testable.
+  */
+int     var_dns_rr_list_limit = 100;
+
 /* dns_rr_create - fill in resource record structure */
 
 DNS_RR *dns_rr_create(const char *qname, const char *rname,
@@ -129,6 +149,7 @@ DNS_RR *dns_rr_create(const char *qname, const char *rname,
        memcpy(rr->data, data, data_len);
     rr->data_len = data_len;
     rr->next = 0;
+    rr->flags = 0;
     return (rr);
 }
 
@@ -163,14 +184,58 @@ DNS_RR *dns_rr_copy(DNS_RR *src)
     return (dst);
 }
 
-/* dns_rr_append - append resource record to list */
+/* dns_rr_append_with_limit - append resource record to limited list */
+
+static void dns_rr_append_with_limit(DNS_RR *list, DNS_RR *rr, int limit)
+{
+
+    /*
+     * Pre: list != 0, all lists are concatenated with dns_rr_append().
+     * 
+     * Post: all elements have the DNS_RR_FLAG_TRUNCATED flag value set, or all
+     * elements have it cleared, so that there is no need to update code in
+     * legacy stable releases that deletes or reorders elements.
+     */
+    if (limit <= 1) {
+       if (list->next || rr) {
+           msg_warn("DNS record count limit (%d) exceeded -- dropping"
+                    " excess record(s) after qname=%s qtype=%s",
+                    var_dns_rr_list_limit, list->qname,
+                    dns_strtype(list->type));
+           list->flags |= DNS_RR_FLAG_TRUNCATED;
+           dns_rr_free(list->next);
+           dns_rr_free(rr);
+           list->next = 0;
+       }
+    } else {
+       if (list->next == 0 && rr) {
+           list->next = rr;
+           rr = 0;
+       }
+       if (list->next) {
+           dns_rr_append_with_limit(list->next, rr, limit - 1);
+           list->flags |= list->next->flags;
+       }
+    }
+}
+
+/* dns_rr_append - append resource record(s) to list, or discard */
 
 DNS_RR *dns_rr_append(DNS_RR *list, DNS_RR *rr)
 {
-    if (list == 0) {
-       list = rr;
+
+    /*
+     * Note: rr is not length checked; when multiple lists are concatenated,
+     * the output length may be a small multiple of var_dns_rr_list_limit.
+     */
+    if (rr == 0)
+       return (list);
+    if (list == 0)
+       return (rr);
+    if (!DNS_RR_IS_TRUNCATED(list)) {
+       dns_rr_append_with_limit(list, rr, var_dns_rr_list_limit);
     } else {
-       list->next = dns_rr_append(list->next, rr);
+       dns_rr_free(rr);
     }
     return (list);
 }
index e25f523377e963751fd5c14cd8076a75c2693d58..f07c3ef50a8b1c4af2a633c3c29b423e8f55155b 100644 (file)
@@ -121,9 +121,11 @@ int     main(int argc, char **argv)
            vstream_printf("%s: fqdn: %s\n", name, vstring_str(fqdn));
            buf = vstring_alloc(100);
            print_rr(buf, rr);
+           vstream_fflush(VSTREAM_OUT);
+           if (DNS_RR_IS_TRUNCATED(rr))
+               msg_warn("one or more excess DNS_RR records were dropped");
            dns_rr_free(rr);
            vstring_free(buf);
-           vstream_fflush(VSTREAM_OUT);
        }
     }
     myfree((void *) types);
index 2192455868df46b4896d33245c31f2e07edca6f0..9ff4553134ccdb0c56eaa1572dcd5f815bd3a84e 100644 (file)
@@ -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      "20240121"
-#define MAIL_VERSION_NUMBER    "3.7.10"
+#define MAIL_RELEASE_DATE      "20240304"
+#define MAIL_VERSION_NUMBER    "3.7.11"
 
 #ifdef SNAPSHOT
 #define MAIL_VERSION_DATE      "-" MAIL_RELEASE_DATE
index 100ccc73dd824659065988e6a604799176ea20f2..03e03404b1538bac0ed6e571e28f82ad52507e8f 100644 (file)
@@ -283,6 +283,7 @@ static void qmgr_deliver_update(int unused_event, void *context)
      * The queue itself won't go away before we dispose of the current queue
      * entry.
      */
+#if 0
     if (status == DELIVER_STAT_CRASH) {
        message->flags |= DELIVER_STAT_DEFER;
 #if 0
@@ -317,6 +318,7 @@ static void qmgr_deliver_update(int unused_event, void *context)
        qmgr_defer_transport(transport, &dsb->dsn);
        return;
     }
+#endif
 
     /*
      * This message must be tried again.
@@ -331,7 +333,9 @@ static void qmgr_deliver_update(int unused_event, void *context)
      */
 #define SUSPENDED      "delivery temporarily suspended: "
 
-    if (status == DELIVER_STAT_DEFER) {
+    if (status == DELIVER_STAT_CRASH)
+       DSN_SIMPLE(&dsb->dsn, "4.3.0", "unknown mail transport error");
+    if (status == DELIVER_STAT_CRASH || status == DELIVER_STAT_DEFER) {
        message->flags |= DELIVER_STAT_DEFER;
        if (VSTRING_LEN(dsb->status)) {
            /* Sanitize the DSN status/reason from the delivery agent. */
index fc205c72608af3746b3ff7475a24b5ae1b478503..db7940462ec4738afd018219b69e4e429673abdc 100644 (file)
@@ -96,7 +96,7 @@ static char *json_quote(VSTRING *result, const char *text)
                VSTRING_ADDCH(result, 't');
                break;
            default:
-               vstring_sprintf(result, "\\u%04X", ch);
+               vstring_sprintf_append(result, "\\u%04X", ch);
                break;
            }
        } else {
index 502645cdc26bb1286c55110cfdfafd8b6bda0d21..2652ec6f9c3498e378b626091efc4311ff261f14 100644 (file)
@@ -1252,6 +1252,8 @@ static DNS_RR *addr_one(STATE *state, DNS_RR *addr_list, const char *host,
                    msg_fatal("host %s: conversion error for address family %d: %m",
                    host, ((struct sockaddr *) (res0->ai_addr))->sa_family);
                addr_list = dns_rr_append(addr_list, addr);
+               if (DNS_RR_IS_TRUNCATED(addr_list))
+                   break;
            }
            freeaddrinfo(res0);
            if (found == 0) {
@@ -1289,6 +1291,8 @@ static DNS_RR *mx_addr_list(STATE *state, DNS_RR *mx_names)
            msg_panic("%s: bad resource type: %d", myname, rr->type);
        addr_list = addr_one(state, addr_list, (char *) rr->data, res_opt,
                             rr->pref);
+       if (addr_list && DNS_RR_IS_TRUNCATED(addr_list))
+           break;
     }
     return (addr_list);
 }
@@ -2024,7 +2028,19 @@ static void parse_match(STATE *state, int argc, char *argv[])
 #ifdef USE_TLS
     int     smtp_mode = 1;
 
+    /*
+     * DANE match names are configured late, once the TLSA records are in
+     * hand. For now, prepare to fall back to "secure".
+     */
     switch (state->level) {
+    default:
+       state->match = 0;
+       if (*argv)
+           msg_warn("TLS level '%s' does not implement certificate matching",
+                    str_tls_level(state->level));
+       break;
+    case TLS_LEV_DANE:
+    case TLS_LEV_DANE_ONLY:
     case TLS_LEV_SECURE:
        state->match = argv_alloc(2);
        while (*argv)
@@ -2045,11 +2061,6 @@ static void parse_match(STATE *state, int argc, char *argv[])
            tls_dane_add_fpt_digests((TLS_DANE *) state->dane, *argv++, "",
                                     smtp_mode);
        break;
-    case TLS_LEV_DANE:
-    case TLS_LEV_DANE_ONLY:
-       state->match = argv_alloc(2);
-       argv_add(state->match, "nexthop", "hostname", ARGV_END);
-       break;
     }
 #endif
 }
index 07e89d448f788cadd306b3b68b4cedd5e48c2de5..577bb98d8045d0e31d11cfee9c0184aea8c7a564 100644 (file)
@@ -288,6 +288,7 @@ static void qmgr_deliver_update(int unused_event, void *context)
      * The queue itself won't go away before we dispose of the current queue
      * entry.
      */
+#if 0
     if (status == DELIVER_STAT_CRASH) {
        message->flags |= DELIVER_STAT_DEFER;
 #if 0
@@ -322,6 +323,7 @@ static void qmgr_deliver_update(int unused_event, void *context)
        qmgr_defer_transport(transport, &dsb->dsn);
        return;
     }
+#endif
 
     /*
      * This message must be tried again.
@@ -336,7 +338,9 @@ static void qmgr_deliver_update(int unused_event, void *context)
      */
 #define SUSPENDED      "delivery temporarily suspended: "
 
-    if (status == DELIVER_STAT_DEFER) {
+    if (status == DELIVER_STAT_CRASH)
+       DSN_SIMPLE(&dsb->dsn, "4.3.0", "unknown mail transport error");
+    if (status == DELIVER_STAT_CRASH || status == DELIVER_STAT_DEFER) {
        message->flags |= DELIVER_STAT_DEFER;
        if (VSTRING_LEN(dsb->status)) {
            /* Sanitize the DSN status/reason from the delivery agent. */
index 2b5c126e5ad18767b504c95c44fa696264d7a925..fa0af4e1401ad556db55f4a8fdb158fd732bc425 100644 (file)
@@ -154,7 +154,7 @@ static DNS_RR *smtp_addr_one(DNS_RR *addr_list, const char *host, int res_opt,
                msg_fatal("host %s: conversion error for address family "
                          "%d: %m", host, res0->ai_addr->sa_family);
            addr_list = dns_rr_append(addr_list, addr);
-           if (msg_verbose)
+           if (msg_verbose && !DNS_RR_IS_TRUNCATED(addr_list))
                msg_info("%s: using numerical host %s", myname, host);
            freeaddrinfo(res0);
            return (addr_list);
@@ -232,6 +232,8 @@ static DNS_RR *smtp_addr_one(DNS_RR *addr_list, const char *host, int res_opt,
                    msg_fatal("host %s: conversion error for address family "
                              "%d: %m", host, res0->ai_addr->sa_family);
                addr_list = dns_rr_append(addr_list, addr);
+               if (DNS_RR_IS_TRUNCATED(addr_list))
+                   break;
                if (msg_verbose) {
                    MAI_HOSTADDR_STR hostaddr_str;
 
@@ -297,6 +299,8 @@ static DNS_RR *smtp_addr_list(DNS_RR *mx_names, DSN_BUF *why)
            msg_panic("smtp_addr_list: bad resource type: %d", rr->type);
        addr_list = smtp_addr_one(addr_list, (char *) rr->data, res_opt,
                                  rr->pref, why);
+       if (addr_list && DNS_RR_IS_TRUNCATED(addr_list))
+           break;
     }
     return (addr_list);
 }
@@ -390,6 +394,13 @@ static DNS_RR *smtp_balance_inet_proto(DNS_RR *addr_list, int misc_flags,
      * relative list order is unchanged, but some elements are removed.
      */
 
+    /*
+     * Ensure that dns_rr_append() won't interfere with the protocol
+     * balancing goals.
+     */
+    if (addr_limit > var_dns_rr_list_limit)
+       addr_limit = var_dns_rr_list_limit;
+
     /*
      * Count the number of IPv6 and IPv4 addresses.
      */
index 4f4aedd1569aef03b414b2c9d5ba80189b0eaf60..c2aa428ab0de22ade59f0963b36b8c87625d0ae7 100644 (file)
@@ -4114,14 +4114,31 @@ static int bdat_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *argv)
        /*
         * Read lines from the fragment. The last line may continue in the
         * next fragment, or in the next chunk.
+        * 
+        * If smtp_get_noexcept() stopped after var_line_limit bytes and did not
+        * emit a queue file record, then that means smtp_get_noexcept()
+        * stopped after CR and hit EOF as it tried to find out if the next
+        * byte is LF. In that case, read the first byte from the next
+        * fragment or chunk, and if that first byte is LF, then
+        * smtp_get_noexcept() strips off the trailing CRLF and returns '\n'
+        * as it always does after reading a complete line.
         */
        do {
+           int     can_read = var_line_limit - LEN(state->bdat_get_buffer);
+
            if (smtp_get_noexcept(state->bdat_get_buffer,
                                  state->bdat_get_stream,
-                                 var_line_limit,
+                                 can_read > 0 ? can_read : 1,  /* Peek one */
                                  SMTP_GET_FLAG_APPEND) == '\n') {
                /* Stopped at end-of-line. */
                curr_rec_type = REC_TYPE_NORM;
+           } else if (LEN(state->bdat_get_buffer) > var_line_limit) {
+               /* Undo peeking, and output the buffer as REC_TYPE_CONT. */
+               vstream_ungetc(state->bdat_get_stream,
+                              vstring_end(state->bdat_get_buffer)[-1]);
+               vstring_truncate(state->bdat_get_buffer,
+                                LEN(state->bdat_get_buffer) - 1);
+               curr_rec_type = REC_TYPE_CONT;
            } else if (!vstream_feof(state->bdat_get_stream)) {
                /* Stopped at var_line_limit. */
                curr_rec_type = REC_TYPE_CONT;
index 996a19e1c8fea10293424cb7013d77264de1da37..513652de538ac12f5dbbe7b438fcfbfd80b83fe6 100644 (file)
@@ -2993,6 +2993,7 @@ static int check_server_access(SMTPD_STATE *state, const char *table,
     struct addrinfo *res;
     int     status;
     const INET_PROTO_INFO *proto_info;
+    int     server_addr_count = 0;
 
     /*
      * Sanity check.
@@ -3144,6 +3145,15 @@ static int check_server_access(SMTPD_STATE *state, const char *table,
            msg_info("%s: %s host address check: %s",
                     myname, dns_strtype(type), (char *) server->data);
        for (res = res0; res != 0; res = res->ai_next) {
+           server_addr_count += 1;
+           if (server_addr_count > var_dns_rr_list_limit) {
+               msg_warn("%s: %s server address count limit (%d) exceeded"
+                        " for %s %s -- ignoring the remainder", myname,
+                        dns_strtype(type), var_dns_rr_list_limit,
+                        reply_class, reply_name);
+               freeaddrinfo(res0);
+               CHECK_SERVER_RETURN(SMTPD_CHECK_DUNNO);
+           }
            if (strchr((char *) proto_info->sa_family_list, res->ai_family) == 0) {
                if (msg_verbose)
                    msg_info("skipping address family %d for host %s",
index 2dc6aad310f5a92c1f55644720f817485ea3162a..d9db7b0c7d1d5207f48f92f918d7e846e9f78651 100644 (file)
@@ -340,18 +340,20 @@ int     smtpd_sasl_authenticate(SMTPD_STATE *state,
        }
     }
     if (status != XSASL_AUTH_DONE) {
+       const char *reason = (*STR(state->sasl_reply) ? STR(state->sasl_reply) :
+                             "(reason unavailable)");
+
        sasl_username = xsasl_server_get_username(state->sasl_server);
        msg_warn("%s: SASL %.100s authentication failed: %s, sasl_username=%.100s",
-                state->namaddr, sasl_method, *STR(state->sasl_reply) ?
-                STR(state->sasl_reply) : "(reason unavailable)",
+                state->namaddr, sasl_method, reason,
                 sasl_username ? sasl_username : "(unavailable)");
        /* RFC 4954 Section 6. */
        if (status == XSASL_AUTH_TEMP)
            smtpd_chat_reply(state, "454 4.7.0 Temporary authentication failure: %s",
-                            STR(state->sasl_reply));
+                            reason);
        else
            smtpd_chat_reply(state, "535 5.7.8 Error: authentication failed: %s",
-                            STR(state->sasl_reply));
+                            reason);
        return (-1);
     }
     /* RFC 4954 Section 6. */
index 28ca96128efb2dd8cd300a563763952092cf8ca4..d32e63d4b9d3f42423403457e28213f18b178d17 100644 (file)
@@ -818,6 +818,23 @@ static void tlsmgr_service(VSTREAM *client_stream, char *unused_service,
        }
     }
 
+    /*
+     * Workaround: some OS lies under load. It tells the Postfix event
+     * handler that a server socket is readable, then it tells peekfd() that
+     * the socket has unread data, and then it tells vstring_get_null() that
+     * there is none, causing Postfix to spam the log with warning messages.
+     * Close the stream to stop such nonsense; the client can reconnect if it
+     * still wants to talk to us.
+     * 
+     * XXX Why is this problem not reported for the other five
+     * multi_server-based Postfix services?
+     */
+    else if (vstream_ferror(client_stream) || vstream_feof(client_stream)) {
+       multi_server_disconnect(client_stream);
+       return;
+       /* Note: client_stream is now a dangling pointer. */
+    }
+
     /*
      * Protocol error.
      */
index 1d1c570637cd48342eef22ab369fae45eaa1d6ed..4a0c085ccea4c7f5ced1165cdba4300184d8e5e7 100644 (file)
@@ -543,6 +543,8 @@ static void xsasl_dovecot_parse_reply_args(XSASL_DOVECOT_SERVER *server,
        myfree(server->username);
        server->username = 0;
     }
+    VSTRING_RESET(reply);
+    VSTRING_TERMINATE(reply);
 
     /*
      * Note: TAB is part of the Dovecot protocol and must not appear in