From: Wietse Venema Date: Fri, 7 Aug 2009 05:00:00 +0000 (-0500) Subject: postfix-2.7-20090807 X-Git-Tag: v2.7.0-RC1~13 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=fb6283acfcd7fd2d3f52812f68203b116e6b247d;p=thirdparty%2Fpostfix.git postfix-2.7-20090807 --- diff --git a/postfix/HISTORY b/postfix/HISTORY index 7996d16b0..b7925f75c 100644 --- a/postfix/HISTORY +++ b/postfix/HISTORY @@ -15336,22 +15336,33 @@ Apologies for any names omitted. Bugfix: don't panic when an unexpected smtpd access map is specified. File: smtpd/smtpd_check.c. +20090807 + Workaround: NS record lookups for certain domains always fail, while other queries for those domains always succeed (and even return replies with NS records as additional information). - This specific inconsistency would allow spammers to avoid - the Postfix check_{client,helo,sender,etc}_ns_access - restrictions, because those restrictions have effect only - for NS records that can be looked up in the DNS. + This inconsistency would allow spammers to avoid the Postfix + check_{client,helo,sender,etc}_ns_access restrictions, + because those restrictions have effect only for names that + are known in the DNS. To address this specific inconsistency, the Postfix - reject_unknown_helo_hostname, reject_unknown_sender_domain - and reject_unknown_recipient_domain restrictions now require - that a domain has NS records that resolve to at least one - IP address, and they now accept only MX records that resolve - to at least one IP address; those addresses may or may not - be "correct". Postfix has no code to determine whether the - SMTP client name has a resolvable NS or MX record. File: - smtpd/smtpd_check.c. + check_{client,etc}_ns_access feature now requires that a + known-in-DNS domain name (or parent thereof) resolves to + at least one name server IP address. + + For consistency, check_{client,etc}_mx_access now requires + that a known-in-DNS domain name resolves to at least one + mail server IP address. + + In addition, reject_unknown_helo_hostname, + reject_unknown_sender_domain now require that an MX record + resolves to at least one IP address. Until now, the existence + of an MX record was sufficient. + + The IP addresses thus obtained may or may not be "correct". + There is little to stop an uncooperative DNS server from + lying, especially when the owner of the domain has no + intention to receive email. File: smtpd/smtpd_check.c. diff --git a/postfix/RELEASE_NOTES b/postfix/RELEASE_NOTES index 5fabd2574..8875c6b3c 100644 --- a/postfix/RELEASE_NOTES +++ b/postfix/RELEASE_NOTES @@ -14,22 +14,31 @@ specifies the release date of a stable release or snapshot release. If you upgrade from Postfix 2.5 or earlier, read RELEASE_NOTES-2.6 before proceeding. -Incompatibility with snapshot 20090805 +Incompatibility with snapshot 20090807 ====================================== With some domain names, NS record lookups always fail while other -lookups always succeed (and often return NS records as additional +lookups always succeed (and may even return NS records as additional information). This anomaly could be used by evil elements to skip Postfix check_{client,helo,sender,recipient}_ns_access checks, -because those apply only domains with an NS record in the DNS. - -To address this specific problem, the reject_unknown_helo_hostname, -reject_unknown_sender_domain and reject_unknown_recipient_domain -features now require that a domain name has NS records that resolve -to at least one IP address, and they now accept only MX records -that resolve to at least one IP address; those addresses may or may -not be "correct". Postfix has no code to determine whether the -SMTP client name has a resolvable NS or MX record. +because these apply only to domains that are known in the DNS. + +To address this specific problem, check_{client,etc}_ns_access now +requires that a known-in-DNS domain name (or parent thereof) resolves +to at least one name server IP address. + +For consistency, check_{client,etc}_mx_access now requires that a +known-in-DNS domain name resolves to at least one mail server IP +address. + +In addition, reject_unknown_helo_hostname, reject_unknown_sender_domain +and reject_unknown_recipient_domain now require that an MX record +resolves to at least one IP address. Until now, the existence of +an MX record was sufficient. + +Keep in mind that these measures provide no hard assurances. There +is little to stop an uncooperative DNS server from lying, especially +when the owner of the domain has no intention to receive email. Incompatibility with snapshot 20090606 ====================================== diff --git a/postfix/src/global/mail_version.h b/postfix/src/global/mail_version.h index ce299ad39..b4977fc56 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 "20090805" +#define MAIL_RELEASE_DATE "20090807" #define MAIL_VERSION_NUMBER "2.7" #ifdef SNAPSHOT diff --git a/postfix/src/smtpd/smtpd_check.c b/postfix/src/smtpd/smtpd_check.c index 33bc09237..939429dee 100644 --- a/postfix/src/smtpd/smtpd_check.c +++ b/postfix/src/smtpd/smtpd_check.c @@ -1166,12 +1166,12 @@ static int resolve_server_list(const char *name, int type, */ dns_status = dns_lookup(domain, type, 0, &server_list, (VSTRING *) 0, (VSTRING *) 0); - if (dns_status == DNS_NOTFOUND && h_errno == NO_DATA) { + if (dns_status == DNS_NOTFOUND /* Not: h_errno == NO_DATA */ ) { if (type == T_MX) { server_list = dns_rr_create(domain, domain, type, C_IN, 0, 0, domain, strlen(domain) + 1); dns_status = DNS_OK; - } else if (type == T_NS) { + } else if (type == T_NS && h_errno == NO_DATA) { while ((domain = strchr(domain, '.')) != 0 && domain[1]) { domain += 1; dns_status = dns_lookup(domain, type, 0, &server_list, @@ -1232,8 +1232,10 @@ static int reject_unknown_hostname(SMTPD_STATE *state, char *name, * check_server_access() wants to check. */ dns_status = resolve_server_list(name, T_MX, reply_name, reply_class); +#ifdef REQUIRE_NS_FOR_REJECT_UNKNOWN_DOMAIN_CHECK if (dns_status == DNS_OK) dns_status = resolve_server_list(name, T_NS, reply_name, reply_class); +#endif if (dns_status != DNS_OK) { /* incl. DNS_INVAL */ if (dns_status != DNS_RETRY) return (smtpd_check_reject(state, MAIL_ERROR_POLICY, @@ -1268,8 +1270,10 @@ static int reject_unknown_mailhost(SMTPD_STATE *state, const char *name, * check_server_access() wants to check. */ dns_status = resolve_server_list(name, T_MX, reply_name, reply_class); +#ifdef REQUIRE_NS_FOR_REJECT_UNKNOWN_DOMAIN_CHECK if (dns_status == DNS_OK) dns_status = resolve_server_list(name, T_NS, reply_name, reply_class); +#endif if (dns_status != DNS_OK) { /* incl. DNS_INVAL */ if (dns_status != DNS_RETRY) return (smtpd_check_reject(state, MAIL_ERROR_POLICY, @@ -2590,6 +2594,10 @@ static int check_server_access(SMTPD_STATE *state, const char *table, struct addrinfo *res; int status; INET_PROTO_INFO *proto_info; + const char *saved_domain; + int non_err, soft_err; + int known_name_in_dns; + int ping_status; /* * Sanity check. @@ -2644,15 +2652,26 @@ static int check_server_access(SMTPD_STATE *state, const char *table, * * If the domain name exists but no NS record exists, look up parent domain * NS records. + * + * After the initial lookup fails, do one final DNS sanity check. Reject + * mail when the name exists, but MX lookup produces no valid response or + * NS lookup fails for any reason. Beware, this sanity check provides no + * hard assurance. An uncooperative DNS server may lie about everything, + * including non-existence. */ +#define SOME_DNS_RR_EXISTS(stat, herr) \ + ((stat) == DNS_OK || (stat) == DNS_INVAL || (herr) == NO_DATA) + + saved_domain = domain; dns_status = dns_lookup(domain, type, 0, &server_list, (VSTRING *) 0, (VSTRING *) 0); - if (dns_status == DNS_NOTFOUND && h_errno == NO_DATA) { + known_name_in_dns = SOME_DNS_RR_EXISTS(dns_status, h_errno); + if (dns_status == DNS_NOTFOUND /* Not: h_errno == NO_DATA */ ) { if (type == T_MX) { server_list = dns_rr_create(domain, domain, type, C_IN, 0, 0, domain, strlen(domain) + 1); dns_status = DNS_OK; - } else if (type == T_NS) { + } else if (type == T_NS && h_errno == NO_DATA) { while ((domain = strchr(domain, '.')) != 0 && domain[1]) { domain += 1; dns_status = dns_lookup(domain, type, 0, &server_list, @@ -2665,6 +2684,22 @@ static int check_server_access(SMTPD_STATE *state, const char *table, if (dns_status != DNS_OK) { msg_warn("Unable to look up %s host for %s: %s", dns_strtype(type), domain && domain[1] ? domain : name, dns_strerror(h_errno)); + if (known_name_in_dns == 0) { + /* With hostile DNS, an address query is more likely to work. */ + ping_status = dns_lookup_l(saved_domain, 0, (DNS_RR **) 0, + (VSTRING *) 0, (VSTRING *) 0, + DNS_REQ_FLAG_STOP_OK, + CHECK_RR_ADDR_TYPES, 0); + known_name_in_dns = SOME_DNS_RR_EXISTS(ping_status, h_errno); + } + if (known_name_in_dns) + return (smtpd_check_reject(state, MAIL_ERROR_POLICY, + dns_status == DNS_RETRY ? + var_map_defer_code : var_map_reject_code, + smtpd_dsn_fix("4.1.8", reply_class), + "<%s>: %s rejected: %s", + reply_name, reply_class, + "Domain not found")); return (SMTPD_CHECK_DUNNO); } @@ -2677,11 +2712,13 @@ static int check_server_access(SMTPD_STATE *state, const char *table, * Check the hostnames first, then the addresses. */ proto_info = inet_proto_info(); + non_err = soft_err = 0; for (server = server_list; server != 0; server = server->next) { if (msg_verbose) msg_info("%s: %s hostname check: %s", myname, dns_strtype(type), (char *) server->data); if (valid_hostaddr((char *) server->data, DONT_GRIPE)) { + non_err = 1; if ((status = check_addr_access(state, table, (char *) server->data, FULL, &found, reply_name, reply_class, def_acl)) != 0 || found) @@ -2697,8 +2734,11 @@ static int check_server_access(SMTPD_STATE *state, const char *table, msg_warn("Unable to look up %s host %s for %s %s: %s", dns_strtype(type), (char *) server->data, reply_class, reply_name, MAI_STRERROR(aierr)); + if (aierr == EAI_AGAIN || aierr == EAI_SYSTEM) + soft_err = 1; continue; } + non_err = 1; /* Now we must also free the addrinfo result. */ if (msg_verbose) msg_info("%s: %s host address check: %s", @@ -2722,7 +2762,15 @@ static int check_server_access(SMTPD_STATE *state, const char *table, } freeaddrinfo(res0); /* 200412 */ } - CHECK_SERVER_RETURN(SMTPD_CHECK_DUNNO); + status = non_err ? SMTPD_CHECK_DUNNO : + smtpd_check_reject(state, MAIL_ERROR_POLICY, + soft_err ? var_map_defer_code : + var_map_reject_code, + smtpd_dsn_fix("4.1.8", reply_class), + "<%s>: %s rejected: %s", + reply_name, reply_class, + "Domain not found"); + CHECK_SERVER_RETURN(status); } /* check_ccert_access - access for TLS clients by certificate fingerprint */