diff --git a/postfix/HISTORY b/postfix/HISTORY index 915915c95..bc68e68ed 100644 --- a/postfix/HISTORY +++ b/postfix/HISTORY @@ -11405,6 +11405,25 @@ Apologies for any names omitted. Bugfix: new bounce template code did not return after template syntax error. File: bounce/bounce_template.c + Safety: permit_mx_backup now requires that the local MTA + is not listed as primary MX for the recipient domain. This + prevents mail loops when someone points the primary MX + record to Postfix. + +20051119 + + Workaround: some SMTP servers announce multiple but different + lists of SASL methods. Postfix now concatenates the lists + instead of logging a warning and remembering only one. File: + smtp/smtp_sasl_proto.c. + + Bugfix: the queue manager did not write a per-recipient + defer logfile record when the delivery agent crashed between + receiving a delivery request, and reporting the delivery + status to the queue manager. Found while redesigning the + code that handles unavailable transports or destinations. + Files: *qmgr/qmgr_deliver.c. + Open problems: "postsuper -r" no longer resets the message arrival time, diff --git a/postfix/RELEASE_NOTES b/postfix/RELEASE_NOTES index 1ae66908b..a5f0336fd 100644 --- a/postfix/RELEASE_NOTES +++ b/postfix/RELEASE_NOTES @@ -17,6 +17,14 @@ Incompatibility with Postfix 2.1 and earlier If you upgrade from Postfix 2.1 or earlier, read RELEASE_NOTES-2.2 before proceeding. +Incompatibility with snapshot 20051120 +====================================== + +The permit_mx_backup feature now requires that the local MTA is not +listed as primary MX host for the recipient domain. This prevents +mail loop problems when someone points the primary MX record at +Postfix. + Major changes with snapshot 20051113 ==================================== diff --git a/postfix/html/postconf.5.html b/postfix/html/postconf.5.html index d3ff4cfdf..242d8643e 100644 --- a/postfix/html/postconf.5.html +++ b/postfix/html/postconf.5.html @@ -8305,9 +8305,16 @@ system is the final destination. However, the SMTP server will not forward mail with addresses that have sender-specified routing information (example: user@elsewhere@domain). Use the optional permit_mx_backup_networks parameter to require that the primary -MX hosts match a list of network blocks.
Note: prior to +MX hosts match a list of network blocks.
NOTE: prior to Postfix version 2.0, use of permit_mx_backup is not recommended; -mail may be rejected in case of a temporary DNS lookup problem. +mail may be rejected in case of a temporary DNS lookup problem. + +
NOTE: as of Postfix version 2.3, permit_mx_backup requires +that the local MTA is not listed as primary MX for the recipient +domain. This is for safety reasons. + +
NOTE: use of permit_mx_backup is not recommended without +restricting its use with permit_mx_backup_networks.
reject_non_fqdn_recipient
diff --git a/postfix/man/man5/postconf.5 b/postfix/man/man5/postconf.5 index e8d4599fe..14fe1e75b 100644 --- a/postfix/man/man5/postconf.5 +++ b/postfix/man/man5/postconf.5 @@ -4777,9 +4777,16 @@ information (example: user@elsewhere@domain). Use the optional permit_mx_backup_networks parameter to require that the primary MX hosts match a list of network blocks. .br -Note: prior to +NOTE: prior to Postfix version 2.0, use of permit_mx_backup is not recommended; mail may be rejected in case of a temporary DNS lookup problem. +.br +NOTE: as of Postfix version 2.3, permit_mx_backup requires +that the local MTA is not listed as primary MX for the recipient +domain. This is for safety reasons. +.br +NOTE: use of permit_mx_backup is not recommended without +restricting its use with permit_mx_backup_networks. .IP "\fBreject_non_fqdn_recipient\fR" Reject the request when the RCPT TO address is not in fully-qualified domain form, as required by the RFC. diff --git a/postfix/proto/postconf.proto b/postfix/proto/postconf.proto index b9b29e345..fb3172d14 100644 --- a/postfix/proto/postconf.proto +++ b/postfix/proto/postconf.proto @@ -5198,9 +5198,16 @@ system is the final destination. However, the SMTP server will not forward mail with addresses that have sender-specified routing information (example: user@elsewhere@domain). Use the optional permit_mx_backup_networks parameter to require that the primary -MX hosts match a list of network blocks.
Note: prior to +MX hosts match a list of network blocks.
NOTE: prior to Postfix version 2.0, use of permit_mx_backup is not recommended; -mail may be rejected in case of a temporary DNS lookup problem. +mail may be rejected in case of a temporary DNS lookup problem. + +
NOTE: as of Postfix version 2.3, permit_mx_backup requires +that the local MTA is not listed as primary MX for the recipient +domain. This is for safety reasons. + +
NOTE: use of permit_mx_backup is not recommended without +restricting its use with permit_mx_backup_networks.
reject_non_fqdn_recipient
diff --git a/postfix/src/bounce/bounce_template.c b/postfix/src/bounce/bounce_template.c index 90f1e8b07..eb2e0167d 100644 --- a/postfix/src/bounce/bounce_template.c +++ b/postfix/src/bounce/bounce_template.c @@ -47,7 +47,9 @@ /* BOUNCE_TEMPLATE *template; /* DESCRIPTION /* This module implements the built-in and external bounce -/* message template support. +/* message template support. The content of a template are +/* private. To access information within a template, use +/* the API described in this document. /* /* bounce_template_create() creates a template, with the /* specified default settings. The template defaults are not @@ -81,7 +83,7 @@ /* specified stream. /* /* The IS_MUMBLE_TEMPLATE() macros are predicates that -/* return when the template is of the specified type. +/* determine whether the template is of the specified type. /* DIAGNOSTICS /* Fatal error: out of memory, undefined macro name in template. /* SEE ALSO @@ -136,9 +138,9 @@ * Ideally, the bounce template processor would strip the _days etc. suffix * from the parameter name, and use the parameter name to look up the actual * parameter value and its default value (the default value specifies the - * default time unit of that parameter (seconds, minutes, etc.), and allows - * us to convert the parameter string value into the corresponding number of - * seconds). The bounce template processor would then use the _hours etc. + * default time unit of that parameter (seconds, minutes, etc.)), and use + * this to convert the parameter string value into the corresponding number + * of seconds. The bounce template processor would then use the _hours etc. * suffix from the bounce template to divide this number by the number of * seconds in an hour, etc. and produce the number that is needed for the * template. diff --git a/postfix/src/bounce/bounce_templates.c b/postfix/src/bounce/bounce_templates.c index 7038e3f2d..292ea04a7 100644 --- a/postfix/src/bounce/bounce_templates.c +++ b/postfix/src/bounce/bounce_templates.c @@ -6,6 +6,15 @@ /* SYNOPSIS /* #include /* +/* typedef struct { +/* .in +4 +/* BOUNCE_TEMPLATE *failure; +/* BOUNCE_TEMPLATE *delay; +/* BOUNCE_TEMPLATE *success; +/* BOUNCE_TEMPLATE *verify; +/* .in -4 +/* } BOUNCE_TEMPLATES; +/* /* BOUNCE_TEMPLATES *bounce_templates_create(void) /* /* void bounce_templates_free(templates) diff --git a/postfix/src/dns/dns.h b/postfix/src/dns/dns.h index b1db34f37..e84ce8abc 100644 --- a/postfix/src/dns/dns.h +++ b/postfix/src/dns/dns.h @@ -86,7 +86,7 @@ typedef struct DNS_RR { unsigned int ttl; /* always */ unsigned short pref; /* T_MX only */ struct DNS_RR *next; /* linkage */ - size_t data_len; /* actual data size */ + size_t data_len; /* actual data size */ char data[1]; /* actually a bunch of data */ } DNS_RR; @@ -111,6 +111,7 @@ extern void dns_rr_free(DNS_RR *); extern DNS_RR *dns_rr_copy(DNS_RR *); extern DNS_RR *dns_rr_append(DNS_RR *, DNS_RR *); extern DNS_RR *dns_rr_sort(DNS_RR *, int (*) (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 *); diff --git a/postfix/src/dns/dns_rr.c b/postfix/src/dns/dns_rr.c index 01732d0dd..0063a201e 100644 --- a/postfix/src/dns/dns_rr.c +++ b/postfix/src/dns/dns_rr.c @@ -30,6 +30,10 @@ /* DNS_RR *list /* int (*compar)(DNS_RR *, DNS_RR *); /* +/* int dns_rr_compare_pref(DNS_RR *a, DNS_RR *b) +/* DNS_RR *list +/* DNS_RR *list +/* /* DNS_RR *dns_rr_shuffle(list) /* DNS_RR *list; /* @@ -58,6 +62,9 @@ /* order according to a user-specified criterion. The result is the /* sorted list. /* +/* dns_rr_compare_pref() is a dns_rr_sort() helper to sort records +/* by their MX preference. +/* /* dns_rr_shuffle() randomly permutes a list of resource records. /* /* dns_rr_remove() removes the specified record from the specified list. @@ -151,6 +158,23 @@ DNS_RR *dns_rr_append(DNS_RR *list, DNS_RR *rr) return (list); } +/* dns_rr_compare_pref - compare resource records by preference */ + +int dns_rr_compare_pref(DNS_RR *a, DNS_RR *b) +{ + if (a->pref != b->pref) + return (a->pref - b->pref); +#ifdef HAS_IPV6 + if (a->type == b->type) /* 200412 */ + return 0; + if (a->type == T_AAAA) + return (-1); + if (b->type == T_AAAA) + return (+1); +#endif + return 0; +} + /* dns_rr_sort_callback - glue function */ static int (*dns_rr_sort_user) (DNS_RR *, DNS_RR *); diff --git a/postfix/src/global/mail_version.h b/postfix/src/global/mail_version.h index a530b409b..95a6c3009 100644 --- a/postfix/src/global/mail_version.h +++ b/postfix/src/global/mail_version.h @@ -20,7 +20,7 @@ * Patches change both the patchlevel and the release date. Snapshots have no * patchlevel; they change the release date only. */ -#define MAIL_RELEASE_DATE "20051118" +#define MAIL_RELEASE_DATE "20051120" #define MAIL_VERSION_NUMBER "2.3" #ifdef SNAPSHOT diff --git a/postfix/src/oqmgr/qmgr_deliver.c b/postfix/src/oqmgr/qmgr_deliver.c index b3e4cd656..7f392d319 100644 --- a/postfix/src/oqmgr/qmgr_deliver.c +++ b/postfix/src/oqmgr/qmgr_deliver.c @@ -219,6 +219,8 @@ static void qmgr_deliver_update(int unused_event, char *context) static DSN_BUF *dsb; int status; DSN dsn; + RECIPIENT *recipient; + int nrcpt; if (dsb == 0) dsb = dsb_create(); @@ -254,6 +256,21 @@ static void qmgr_deliver_update(int unused_event, char *context) "unknown mail transport error")); msg_warn("transport %s failure -- see a previous warning/fatal/panic logfile record for the problem description", transport->name); + + /* + * Assume the worst and write a defer logfile record for each + * recipient. This omission was already present in the first queue + * manager implementation of 199703, and was fixed 200511. + * + * Don't move this queue entry back to the todo queue so that + * qmgr_defer_transport() can update the defer log. The queue entry + * is still hot, and making it cold would involve duplicating most + * but not all code at the end of this routine. That's too tricky. + */ + for (nrcpt = 0; nrcpt < entry->rcpt_list.len; nrcpt++) { + recipient = entry->rcpt_list.info + nrcpt; + qmgr_defer_recipient(message, recipient, &dsn); + } qmgr_defer_transport(transport, &dsn); } @@ -287,7 +304,7 @@ static void qmgr_deliver_update(int unused_event, char *context) * No problems detected. Mark the transport and queue as alive. The queue * itself won't go away before we dispose of the current queue entry. */ - if (VSTRING_LEN(dsb->reason) == 0) { + if (status != DELIVER_STAT_CRASH && VSTRING_LEN(dsb->reason) == 0) { qmgr_transport_unthrottle(transport); qmgr_queue_unthrottle(queue); } diff --git a/postfix/src/qmgr/qmgr_deliver.c b/postfix/src/qmgr/qmgr_deliver.c index 28f0f2c46..a2d96f511 100644 --- a/postfix/src/qmgr/qmgr_deliver.c +++ b/postfix/src/qmgr/qmgr_deliver.c @@ -224,6 +224,8 @@ static void qmgr_deliver_update(int unused_event, char *context) static DSN_BUF *dsb; int status; DSN dsn; + RECIPIENT *recipient; + int nrcpt; if (dsb == 0) dsb = dsb_create(); @@ -259,6 +261,21 @@ static void qmgr_deliver_update(int unused_event, char *context) "unknown mail transport error")); msg_warn("transport %s failure -- see a previous warning/fatal/panic logfile record for the problem description", transport->name); + + /* + * Assume the worst and write a defer logfile record for each + * recipient. This omission was already present in the first queue + * manager implementation of 199703, and was fixed 200511. + * + * Don't move this queue entry back to the todo queue so that + * qmgr_defer_transport() can update the defer log. The queue entry + * is still hot, and making it cold would involve duplicating most + * but not all code at the end of this routine. That's too tricky. + */ + for (nrcpt = 0; nrcpt < entry->rcpt_list.len; nrcpt++) { + recipient = entry->rcpt_list.info + nrcpt; + qmgr_defer_recipient(message, recipient, &dsn); + } qmgr_defer_transport(transport, &dsn); } @@ -292,7 +309,7 @@ static void qmgr_deliver_update(int unused_event, char *context) * No problems detected. Mark the transport and queue as alive. The queue * itself won't go away before we dispose of the current queue entry. */ - if (VSTRING_LEN(dsb->reason) == 0) { + if (status != DELIVER_STAT_CRASH && VSTRING_LEN(dsb->reason) == 0) { qmgr_transport_unthrottle(transport); qmgr_queue_unthrottle(queue); } diff --git a/postfix/src/smtp/smtp_addr.c b/postfix/src/smtp/smtp_addr.c index 855007efc..ddc06c87e 100644 --- a/postfix/src/smtp/smtp_addr.c +++ b/postfix/src/smtp/smtp_addr.c @@ -338,23 +338,6 @@ static DNS_RR *smtp_truncate_self(DNS_RR *addr_list, unsigned pref) return (addr_list); } -/* smtp_compare_pref - compare resource records by preference */ - -static int smtp_compare_pref(DNS_RR *a, DNS_RR *b) -{ - if (a->pref != b->pref) - return (a->pref - b->pref); -#ifdef HAS_IPV6 - if (a->type == b->type) /* 200412 */ - return 0; - if (a->type == T_AAAA) - return (-1); - if (b->type == T_AAAA) - return (+1); -#endif - return 0; -} - /* smtp_domain_addr - mail exchanger address lookup */ DNS_RR *smtp_domain_addr(char *name, int misc_flags, DSN_BUF *why, @@ -437,7 +420,7 @@ DNS_RR *smtp_domain_addr(char *name, int misc_flags, DSN_BUF *why, addr_list = smtp_host_addr(name, misc_flags, why); break; case DNS_OK: - mx_names = dns_rr_sort(mx_names, smtp_compare_pref); + mx_names = dns_rr_sort(mx_names, dns_rr_compare_pref); best_pref = (mx_names ? mx_names->pref : IMPOSSIBLE_PREFERENCE); addr_list = smtp_addr_list(mx_names, why); dns_rr_free(mx_names); @@ -472,7 +455,7 @@ DNS_RR *smtp_domain_addr(char *name, int misc_flags, DSN_BUF *why, } if (addr_list && addr_list->next && var_smtp_rand_addr) { addr_list = dns_rr_shuffle(addr_list); - addr_list = dns_rr_sort(addr_list, smtp_compare_pref); + addr_list = dns_rr_sort(addr_list, dns_rr_compare_pref); } break; case DNS_INVAL: @@ -521,7 +504,7 @@ DNS_RR *smtp_host_addr(char *host, int misc_flags, DSN_BUF *why) addr_list = dns_rr_shuffle(addr_list); /* The following changes the order of equal-preference hosts. */ if (inet_proto_info()->ai_family_list[1] != 0) - addr_list = dns_rr_sort(addr_list, smtp_compare_pref); + addr_list = dns_rr_sort(addr_list, dns_rr_compare_pref); } if (msg_verbose) smtp_print_addr(host, addr_list); diff --git a/postfix/src/smtp/smtp_sasl_proto.c b/postfix/src/smtp/smtp_sasl_proto.c index 87b049129..7b8ed12f0 100644 --- a/postfix/src/smtp/smtp_sasl_proto.c +++ b/postfix/src/smtp/smtp_sasl_proto.c @@ -115,18 +115,25 @@ static const char *smtp_sasl_compat_mechs(const char *words) void smtp_sasl_helo_auth(SMTP_SESSION *session, const char *words) { const char *mech_list = smtp_sasl_compat_mechs(words); + char *junk; /* * XXX If the server offers no compatible authentication mechanisms, then * pretend that the server doesn't support SASL authentication. + * + * XXX If the server offers multiple different lists, concatenate them. Let + * the SASL library worry about duplicates. */ if (session->sasl_mechanism_list) { - if (strcasecmp(session->sasl_mechanism_list, mech_list) == 0) - return; - myfree(session->sasl_mechanism_list); - msg_warn("%s offered AUTH option multiple times", session->namaddr); - session->sasl_mechanism_list = 0; - session->features &= ~SMTP_FEATURE_AUTH; + if (strcasecmp(session->sasl_mechanism_list, mech_list) != 0 + && strlen(mech_list) > 0 + && strlen(session->sasl_mechanism_list) < var_line_limit) { + junk = concatenate(session->sasl_mechanism_list, " ", mech_list, + (char *) 0); + myfree(session->sasl_mechanism_list); + session->sasl_mechanism_list = junk; + } + return; } if (strlen(mech_list) > 0) { session->sasl_mechanism_list = mystrdup(mech_list); diff --git a/postfix/src/smtpd/smtpd_check.c b/postfix/src/smtpd/smtpd_check.c index a95685d2a..0d9b1c6fa 100644 --- a/postfix/src/smtpd/smtpd_check.c +++ b/postfix/src/smtpd/smtpd_check.c @@ -1515,25 +1515,15 @@ static int permit_mx_primary(SMTPD_STATE *state, DNS_RR *mx_list, { const char *myname = "permit_mx_primary"; DNS_RR *mx; - unsigned int best_pref; if (msg_verbose) msg_info("%s", myname); - /* - * Find the preference of the primary MX hosts. - */ - for (best_pref = 0xffff, mx = mx_list; mx != 0; mx = mx->next) - if (mx->pref < best_pref) - best_pref = mx->pref; - /* * See if each best MX host has all IP addresses in * permit_mx_backup_networks. */ for (mx = mx_list; mx != 0; mx = mx->next) { - if (mx->pref != best_pref) - continue; if (!all_auth_mx_addr(state, (char *) mx->data, reply_name, reply_class)) return (NOPE); } @@ -1553,8 +1543,9 @@ static int permit_mx_backup(SMTPD_STATE *state, const char *recipient, char *myname = "permit_mx_backup"; const RESOLVE_REPLY *reply; const char *domain; - DNS_RR *mx_list; + DNS_RR *middle; + DNS_RR *rest; int dns_status; if (msg_verbose) @@ -1607,9 +1598,11 @@ static int permit_mx_backup(SMTPD_STATE *state, const char *recipient, */ dns_status = dns_lookup(domain, T_MX, 0, &mx_list, (VSTRING *) 0, (VSTRING *) 0); +#if 0 if (dns_status == DNS_NOTFOUND) return (has_my_addr(state, domain, reply_name, reply_class) ? SMTPD_CHECK_OK : SMTPD_CHECK_DUNNO); +#endif if (dns_status != DNS_OK) { /* incl. DNS_INVAL */ if (dns_status == DNS_RETRY) DEFER_IF_REJECT2(state, MAIL_ERROR_POLICY, @@ -1620,28 +1613,50 @@ static int permit_mx_backup(SMTPD_STATE *state, const char *recipient, } /* - * First, see if we match any of the MX host names listed. + * Separate MX list into primaries and backups. */ - if (!i_am_mx(state, mx_list, reply_name, reply_class)) { - dns_rr_free(mx_list); - return (SMTPD_CHECK_DUNNO); + mx_list = dns_rr_sort(mx_list, dns_rr_compare_pref); + for (middle = mx_list; /* see below */ ; middle = rest) { + rest = middle->next; + if (rest == 0) + break; + if (rest->pref != mx_list->pref) { + middle->next = 0; + break; + } } + /* postcondition: middle->next = 0, rest may be 0. */ + +#define PERMIT_MX_BACKUP_RETURN(x) do { \ + middle->next = rest; \ + dns_rr_free(mx_list); \ + return (x); \ + } while (0) + + /* + * First, see if we match any of the primary MX servers. + */ + if (i_am_mx(state, mx_list, reply_name, reply_class)) + PERMIT_MX_BACKUP_RETURN(SMTPD_CHECK_DUNNO); + + /* + * Then, see if we match any of the backup MX servers. + */ + if (rest && !i_am_mx(state, rest, reply_name, reply_class)) + PERMIT_MX_BACKUP_RETURN(SMTPD_CHECK_DUNNO); /* * Optionally, see if the primary MX hosts are in a restricted list of * networks. */ if (*var_perm_mx_networks - && !permit_mx_primary(state, mx_list, reply_name, reply_class)) { - dns_rr_free(mx_list); - return (SMTPD_CHECK_DUNNO); - } + && !permit_mx_primary(state, mx_list, reply_name, reply_class)) + PERMIT_MX_BACKUP_RETURN(SMTPD_CHECK_DUNNO); /* * The destination passed all requirements. */ - dns_rr_free(mx_list); - return (SMTPD_CHECK_OK); + PERMIT_MX_BACKUP_RETURN(SMTPD_CHECK_OK); } /* reject_non_fqdn_address - fail if address is not in fqdn form */