]> git.ipfire.org Git - thirdparty/postfix.git/commitdiff
postfix-3.5.25 postfix-3.5 v3.5.25
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:24:08 +0000 (13:24 -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 715ba3cabf9f6f2d570b33a961000a38a0a51e10..3871beecf9dcd1370f5f3ba4ca58b6b8bad90f19 100644 (file)
@@ -25433,3 +25433,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 b6a1ec42666431472f9b419cfab7d0348b2f5f3f..afd2054679ba5b5976c4268442f70c7fc31ec144 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 3631d231868b354afb1b44b9192d5687bf99d76b..27d2ab92aeca19bcb28bc033776a3cfd67313624 100644 (file)
@@ -148,11 +148,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
   */
@@ -186,6 +192,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 c5f7ad90cf300d5fafa248e745c436f93491f674..30bfc9e57889e6d20f84400aef0174742c425394 100644 (file)
@@ -911,6 +911,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)
@@ -1135,8 +1137,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;
@@ -1187,8 +1192,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 8366cf7c048f8c1faad592cf3f32cab04f7cb9d8..6778064dbeb81c401680531b0b547e4ec1be0f50 100644 (file)
@@ -119,9 +119,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 c9b0ad3f5473c8c13be78c153b00d01a01b44a5e..07320b990c207a55994aef54021b51299c3a5c15 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.5.24"
+#define MAIL_RELEASE_DATE      "20240304"
+#define MAIL_VERSION_NUMBER    "3.5.25"
 
 #ifdef SNAPSHOT
 #define MAIL_VERSION_DATE      "-" MAIL_RELEASE_DATE
index d8ec7bb00accf9ead847f10408424814243ef3c9..0e26403275737c1c8a1d6dfcd743b68b13489464 100644 (file)
@@ -285,6 +285,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
@@ -319,6 +320,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.
@@ -333,7 +335,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 e9d4fb5703c6c3a12235c583dc4d7b47f40c3412..40bebdd39736a8a1a4406dab5972c771ff36f637 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 849b05da17e6b5b75dc5ba27e1d490b104945185..ce5d2c0f1465e7ec160854ae862984a955cf1202 100644 (file)
@@ -1244,6 +1244,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) {
@@ -1281,6 +1283,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);
 }
@@ -2048,8 +2052,20 @@ static void parse_match(STATE *state, int argc, char *argv[])
 {
 #ifdef USE_TLS
 
+    /*
+     * DANE match names are configured late, once the TLSA records are in
+     * hand. For now, prepare to fall back to "secure".
+     */
     switch (state->level) {
-       case TLS_LEV_SECURE:
+    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)
            argv_split_append(state->match, *argv++, "");
@@ -2069,11 +2085,6 @@ static void parse_match(STATE *state, int argc, char *argv[])
            tls_dane_add_ee_digests((TLS_DANE *) state->dane,
                                    state->mdalg, *argv++, "");
        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 fe4cf0ca5be14826f438ebb5f4af6cf9a09296c9..ae7ba7436a493d49744fa9339d3a5e5f89189e5a 100644 (file)
@@ -290,6 +290,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
@@ -324,6 +325,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.
@@ -338,7 +340,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 2210ff7d81e033f1129d3a5beab681cece1a22ae..7509edcaa00d40010fe8bb1233da8db48cd3cc37 100644 (file)
@@ -230,6 +230,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;
            }
            freeaddrinfo(res0);
            if (found == 0) {
@@ -287,6 +289,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);
 }
@@ -381,6 +385,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 c46bdf8855acef0ef42922e265b197e3db337f71..f9812af842d8fa1d9522ef79ee284ca8c0ab49aa 100644 (file)
@@ -4053,14 +4053,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 ed0e3ffb1ddac8f3ce20a32c9cd7ac4599acaa42..69119adb3f734a57bfc18d2c05779abb04de505b 100644 (file)
@@ -2985,6 +2985,7 @@ static int check_server_access(SMTPD_STATE *state, const char *table,
     struct addrinfo *res;
     int     status;
     INET_PROTO_INFO *proto_info;
+    int     server_addr_count = 0;
 
     /*
      * Sanity check.
@@ -3134,6 +3135,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 ee5c04116af0d7753776e35fd6aa0549719ec290..2c24edc4cfc613025ae8609dc9a75502275e8312 100644 (file)
@@ -321,18 +321,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 db48ffbc2c12d4f7fe5886843cd89d6aef41d4e2..6f124187fa2bcf38ff11dfe006dbc112f5b9bbda 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 601f7874bc914d2d05438bf5a5c13f2e20360c37..e97af30d47ec298fcb40abf027b8e80fc503813d 100644 (file)
@@ -542,6 +542,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