From e2905c99ea172c2e54ea419699d8073d23fe7b22 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Mon, 8 Apr 2024 20:56:32 +0200 Subject: Adding upstream version 3.7.11. Signed-off-by: Daniel Baumann --- HISTORY | 80 +++++++++++++++++++++++++++++++++++++ src/cleanup/cleanup_milter.c | 35 +++++++++------- src/dns/dns.h | 7 ++++ src/dns/dns_lookup.c | 12 +++++- src/dns/dns_rr.c | 79 ++++++++++++++++++++++++++++++++---- src/dns/test_dns_lookup.c | 4 +- src/global/mail_version.h | 4 +- src/oqmgr/qmgr_deliver.c | 6 ++- src/postqueue/showq_json.c | 2 +- src/posttls-finger/posttls-finger.c | 21 +++++++--- src/qmgr/qmgr_deliver.c | 6 ++- src/smtp/smtp_addr.c | 13 +++++- src/smtpd/smtpd.c | 19 ++++++++- src/smtpd/smtpd_check.c | 10 +++++ src/smtpd/smtpd_sasl_glue.c | 10 +++-- src/tlsmgr/tlsmgr.c | 17 ++++++++ src/xsasl/xsasl_dovecot_server.c | 2 + 17 files changed, 287 insertions(+), 40 deletions(-) diff --git a/HISTORY b/HISTORY index dd69915..1f1e2c4 100644 --- a/HISTORY +++ b/HISTORY @@ -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. diff --git a/src/cleanup/cleanup_milter.c b/src/cleanup/cleanup_milter.c index 11510b5..beb70f8 100644 --- a/src/cleanup/cleanup_milter.c +++ b/src/cleanup/cleanup_milter.c @@ -119,6 +119,7 @@ #include #include #include +#include /* 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 5f53dbc..0d9c8a3 100644 --- a/src/dns/dns.h +++ b/src/dns/dns.h @@ -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 diff --git a/src/dns/dns_lookup.c b/src/dns/dns_lookup.c index 615902d..fa7fcb0 100644 --- a/src/dns/dns_lookup.c +++ b/src/dns/dns_lookup.c @@ -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; diff --git a/src/dns/dns_rr.c b/src/dns/dns_rr.c index b550788..cf82f9f 100644 --- a/src/dns/dns_rr.c +++ b/src/dns/dns_rr.c @@ -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. @@ -65,9 +67,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 @@ -108,6 +118,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, @@ -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); } 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 2192455..9ff4553 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.7.10" +#define MAIL_RELEASE_DATE "20240304" +#define MAIL_VERSION_NUMBER "3.7.11" #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 502645c..2652ec6 100644 --- a/src/posttls-finger/posttls-finger.c +++ b/src/posttls-finger/posttls-finger.c @@ -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 } 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 2b5c126..fa0af4e 100644 --- a/src/smtp/smtp_addr.c +++ b/src/smtp/smtp_addr.c @@ -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. */ diff --git a/src/smtpd/smtpd.c b/src/smtpd/smtpd.c index 4f4aedd..c2aa428 100644 --- a/src/smtpd/smtpd.c +++ b/src/smtpd/smtpd.c @@ -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; diff --git a/src/smtpd/smtpd_check.c b/src/smtpd/smtpd_check.c index 996a19e..513652d 100644 --- a/src/smtpd/smtpd_check.c +++ b/src/smtpd/smtpd_check.c @@ -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", 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 @@ -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. */ 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 -- cgit v1.2.3