summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--HISTORY80
-rw-r--r--src/cleanup/cleanup_milter.c35
-rw-r--r--src/dns/dns.h7
-rw-r--r--src/dns/dns_lookup.c12
-rw-r--r--src/dns/dns_rr.c79
-rw-r--r--src/dns/test_dns_lookup.c4
-rw-r--r--src/global/mail_version.h4
-rw-r--r--src/oqmgr/qmgr_deliver.c6
-rw-r--r--src/postqueue/showq_json.c2
-rw-r--r--src/posttls-finger/posttls-finger.c21
-rw-r--r--src/qmgr/qmgr_deliver.c6
-rw-r--r--src/smtp/smtp_addr.c15
-rw-r--r--src/smtpd/smtpd.c19
-rw-r--r--src/smtpd/smtpd_check.c10
-rw-r--r--src/smtpd/smtpd_sasl_glue.c10
-rw-r--r--src/tlsmgr/tlsmgr.c17
-rw-r--r--src/xsasl/xsasl_dovecot_server.c2
17 files changed, 288 insertions, 41 deletions
diff --git a/HISTORY b/HISTORY
index 4041f3d..3892273 100644
--- a/HISTORY
+++ b/HISTORY
@@ -27246,3 +27246,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.
diff --git a/src/cleanup/cleanup_milter.c b/src/cleanup/cleanup_milter.c
index b71a6dd..491de25 100644
--- a/src/cleanup/cleanup_milter.c
+++ b/src/cleanup/cleanup_milter.c
@@ -119,6 +119,7 @@
#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));
diff --git a/src/dns/dns.h b/src/dns/dns.h
index 2b77015..987b988 100644
--- a/src/dns/dns.h
+++ b/src/dns/dns.h
@@ -161,12 +161,18 @@ typedef struct DNS_RR {
unsigned short pref; /* T_MX and T_SRV record related */
unsigned short weight; /* T_SRV related, defined in rfc2782 */
unsigned short port; /* T_SRV related, defined in rfc2782 */
+ /* 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; /* a bunch of data */
/* Add new fields at the end, for ABI forward compatibility. */
} DNS_RR;
+#define DNS_RR_FLAG_TRUNCATED (1<<0)
+
+#define DNS_RR_IS_TRUNCATED(rr) ((rr)->flags & DNS_RR_FLAG_TRUNCATED)
+
/*
* dns_strerror.c
*/
@@ -215,6 +221,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
diff --git a/src/dns/dns_lookup.c b/src/dns/dns_lookup.c
index 06028c8..c21b619 100644
--- a/src/dns/dns_lookup.c
+++ b/src/dns/dns_lookup.c
@@ -978,6 +978,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 || status == DNS_NULLSRV) {
CORRUPT(status); /* TODO: use better name */
} else if (not_found_status != DNS_RETRY)
@@ -1208,8 +1210,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;
@@ -1260,8 +1265,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;
diff --git a/src/dns/dns_rr.c b/src/dns/dns_rr.c
index 3fde10e..882a42f 100644
--- a/src/dns/dns_rr.c
+++ b/src/dns/dns_rr.c
@@ -54,6 +54,8 @@
/*
/* DNS_RR *dns_srv_rr_sort(list)
/* DNS_RR *list;
+/*
+/* int var_dns_rr_list_limit;
/* AUXILIARY FUNCTIONS
/* DNS_RR *dns_rr_create_nopref(qname, rname, type, class, ttl,
/* data, data_len)
@@ -95,9 +97,17 @@
/*
/* 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
@@ -150,6 +160,16 @@
#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,
@@ -181,6 +201,7 @@ DNS_RR *dns_rr_create(const char *qname, const char *rname,
}
rr->data_len = data_len;
rr->next = 0;
+ rr->flags = 0;
return (rr);
}
@@ -218,14 +239,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);
}
diff --git a/src/dns/test_dns_lookup.c b/src/dns/test_dns_lookup.c
index e25f523..f07c3ef 100644
--- a/src/dns/test_dns_lookup.c
+++ b/src/dns/test_dns_lookup.c
@@ -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);
diff --git a/src/global/mail_version.h b/src/global/mail_version.h
index 03f23f3..9eda667 100644
--- a/src/global/mail_version.h
+++ b/src/global/mail_version.h
@@ -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.8.5"
+#define MAIL_RELEASE_DATE "20240304"
+#define MAIL_VERSION_NUMBER "3.8.6"
#ifdef SNAPSHOT
#define MAIL_VERSION_DATE "-" MAIL_RELEASE_DATE
diff --git a/src/oqmgr/qmgr_deliver.c b/src/oqmgr/qmgr_deliver.c
index 100ccc7..03e0340 100644
--- a/src/oqmgr/qmgr_deliver.c
+++ b/src/oqmgr/qmgr_deliver.c
@@ -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. */
diff --git a/src/postqueue/showq_json.c b/src/postqueue/showq_json.c
index fc205c7..db79404 100644
--- a/src/postqueue/showq_json.c
+++ b/src/postqueue/showq_json.c
@@ -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 {
diff --git a/src/posttls-finger/posttls-finger.c b/src/posttls-finger/posttls-finger.c
index b9a4699..d64c355 100644
--- a/src/posttls-finger/posttls-finger.c
+++ b/src/posttls-finger/posttls-finger.c
@@ -1260,6 +1260,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) {
@@ -1297,6 +1299,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, rr->port);
+ if (addr_list && DNS_RR_IS_TRUNCATED(addr_list))
+ break;
}
return (addr_list);
}
@@ -2114,7 +2118,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)
@@ -2135,11 +2151,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
}
diff --git a/src/qmgr/qmgr_deliver.c b/src/qmgr/qmgr_deliver.c
index 07e89d4..577bb98 100644
--- a/src/qmgr/qmgr_deliver.c
+++ b/src/qmgr/qmgr_deliver.c
@@ -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. */
diff --git a/src/smtp/smtp_addr.c b/src/smtp/smtp_addr.c
index 5e23388..f30ce73 100644
--- a/src/smtp/smtp_addr.c
+++ b/src/smtp/smtp_addr.c
@@ -179,10 +179,10 @@ static DNS_RR *smtp_addr_one(DNS_RR *addr_list, const char *host, int res_opt,
if ((addr = dns_sa_to_rr(host, pref, res0->ai_addr)) == 0)
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);
addr->pref = pref;
addr->port = port;
- if (msg_verbose)
+ addr_list = dns_rr_append(addr_list, addr);
+ if (msg_verbose && !DNS_RR_IS_TRUNCATED(addr_list))
msg_info("%s: using numerical host %s", myname, host);
freeaddrinfo(res0);
return (addr_list);
@@ -262,6 +262,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;
@@ -327,6 +329,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, rr->port, why);
+ if (addr_list && DNS_RR_IS_TRUNCATED(addr_list))
+ break;
}
return (addr_list);
}
@@ -421,6 +425,13 @@ static DNS_RR *smtp_balance_inet_proto(DNS_RR *addr_list, int misc_flags,
*/
/*
+ * 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.
*/
for (v4_count = v6_count = 0, rr = addr_list; rr != 0; rr = rr->next) {
diff --git a/src/smtpd/smtpd.c b/src/smtpd/smtpd.c
index e84128b..6a2cf01 100644
--- a/src/smtpd/smtpd.c
+++ b/src/smtpd/smtpd.c
@@ -4129,14 +4129,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;
diff --git a/src/smtpd/smtpd_check.c b/src/smtpd/smtpd_check.c
index 7212ccf..093aa06 100644
--- a/src/smtpd/smtpd_check.c
+++ b/src/smtpd/smtpd_check.c
@@ -2994,6 +2994,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.
@@ -3145,6 +3146,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",
diff --git a/src/smtpd/smtpd_sasl_glue.c b/src/smtpd/smtpd_sasl_glue.c
index 2dc6aad..d9db7b0 100644
--- a/src/smtpd/smtpd_sasl_glue.c
+++ b/src/smtpd/smtpd_sasl_glue.c
@@ -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. */
diff --git a/src/tlsmgr/tlsmgr.c b/src/tlsmgr/tlsmgr.c
index 28ca961..d32e63d 100644
--- a/src/tlsmgr/tlsmgr.c
+++ b/src/tlsmgr/tlsmgr.c
@@ -819,6 +819,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.
*/
else {
diff --git a/src/xsasl/xsasl_dovecot_server.c b/src/xsasl/xsasl_dovecot_server.c
index 1d1c570..4a0c085 100644
--- a/src/xsasl/xsasl_dovecot_server.c
+++ b/src/xsasl/xsasl_dovecot_server.c
@@ -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