Prereq: "2.5.7" diff -cr --new-file /var/tmp/postfix-2.5.7/src/global/mail_version.h ./src/global/mail_version.h *** /var/tmp/postfix-2.5.7/src/global/mail_version.h Tue May 12 11:15:18 2009 --- ./src/global/mail_version.h Wed Aug 26 20:26:27 2009 *************** *** 20,27 **** * Patches change both the patchlevel and the release date. Snapshots have no * patchlevel; they change the release date only. */ ! #define MAIL_RELEASE_DATE "20090512" ! #define MAIL_VERSION_NUMBER "2.5.7" #ifdef SNAPSHOT # define MAIL_VERSION_DATE "-" MAIL_RELEASE_DATE --- 20,27 ---- * Patches change both the patchlevel and the release date. Snapshots have no * patchlevel; they change the release date only. */ ! #define MAIL_RELEASE_DATE "20090826" ! #define MAIL_VERSION_NUMBER "2.5.8" #ifdef SNAPSHOT # define MAIL_VERSION_DATE "-" MAIL_RELEASE_DATE diff -cr --new-file /var/tmp/postfix-2.5.7/HISTORY ./HISTORY *** /var/tmp/postfix-2.5.7/HISTORY Tue May 12 10:28:07 2009 --- ./HISTORY Tue Aug 25 20:20:03 2009 *************** *** 14514,14516 **** --- 14514,14560 ---- Bugfix: don't disable MIME parsing with smtp_header_checks, smtp_mime_header_checks, smtp_nested_header_checks or with smtp_body_checks. Bug reported by Victor. File: smtp/smtp_proto.c. + + 20090710 + + Bugfix (introduced Postfix 2.3): Postfix got out of sync + with a Milter application after the application sent a + "quarantine" request at end-of-message time. The milter + application would still be in the end-of-message state, + while Postfix would already be working on the next SMTP + event (typically, QUIT or MAIL FROM). Problem diagnosed + with help from Alban Deniz. File: milter/milter8.c. + + 20090805 + + 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 inconsistency in DNS lookup results would allow spammers + to circumvent 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. + + To address this inconsistency, check_{client,etc}_ns_access + now require that a known-in-DNS domain name (or parent + thereof) always resolves to at least one name server IP + address. + + For consistency, check_{client,etc}_mx_access now require + that a known-in-DNS domain name always resolves to at least + one mail server IP address. + + These measures merely raise the difficulty level for spammers. + The IP address information thus obtained is not necessarily + "correct". There is little to stop an uncooperative DNS + server from lying, especially when the owner of the domain + has no desire to receive email. File: smtpd/smtpd_check.c. + + Problem reported by MXTools.com. diff -cr --new-file /var/tmp/postfix-2.5.7/RELEASE_NOTES ./RELEASE_NOTES *** /var/tmp/postfix-2.5.7/RELEASE_NOTES Sun Jul 27 16:27:56 2008 --- ./RELEASE_NOTES Tue Aug 25 20:20:54 2009 *************** *** 11,16 **** --- 11,38 ---- The mail_release_date configuration parameter (format: yyyymmdd) specifies the release date of a stable release or snapshot release. + Incompatibility with Postfix 2.5.8 + ================================== + + With some domain names, NS record lookups always fail while other + 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 these apply only to NS records that are found 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) always + 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 always resolves to at least one mail server + IP address. + + These measures provide no hard assurances that the IP address + information thus obtained is correct. There is little to stop an + uncooperative DNS server from lying, especially when the owner of + the domain has no desire to receive email. + Incompatibility with Postfix 2.5.3 ================================== diff -cr --new-file /var/tmp/postfix-2.5.7/src/milter/milter8.c ./src/milter/milter8.c *** /var/tmp/postfix-2.5.7/src/milter/milter8.c Fri Feb 8 18:58:42 2008 --- ./src/milter/milter8.c Sat Jul 11 20:28:52 2009 *************** *** 1292,1298 **** /* * Decision: quarantine. In Sendmail 8.13 this does not imply a * transition in the receiver state (reply, reject, tempfail, ! * accept, discard). */ case SMFIR_QUARANTINE: /* XXX What to do with the "reason" text? */ --- 1292,1299 ---- /* * Decision: quarantine. In Sendmail 8.13 this does not imply a * transition in the receiver state (reply, reject, tempfail, ! * accept, discard). We should not transition, either, otherwise ! * we get out of sync. */ case SMFIR_QUARANTINE: /* XXX What to do with the "reason" text? */ *************** *** 1300,1306 **** MILTER8_DATA_BUFFER, milter->buf, MILTER8_DATA_END) != 0) MILTER8_EVENT_BREAK(milter->def_reply); ! MILTER8_EVENT_BREAK("H"); /* * Decision: skip further events of this type. --- 1301,1308 ---- MILTER8_DATA_BUFFER, milter->buf, MILTER8_DATA_END) != 0) MILTER8_EVENT_BREAK(milter->def_reply); ! milter8_def_reply(milter, "H"); ! continue; /* * Decision: skip further events of this type. diff -cr --new-file /var/tmp/postfix-2.5.7/src/smtpd/smtpd_check.c ./src/smtpd/smtpd_check.c *** /var/tmp/postfix-2.5.7/src/smtpd/smtpd_check.c Fri Apr 11 15:46:54 2008 --- ./src/smtpd/smtpd_check.c Wed Aug 26 20:24:48 2009 *************** *** 2295,2302 **** if (msg_verbose) msg_info("%s: %s", myname, name); ! if ((dict = dict_handle(table)) == 0) ! msg_panic("%s: dictionary not found: %s", myname, table); if (flags == 0 || (flags & dict->flags) != 0) { if ((value = dict_get(dict, name)) != 0) CHK_ACCESS_RETURN(check_table_result(state, table, value, name, --- 2295,2307 ---- if (msg_verbose) msg_info("%s: %s", myname, name); ! if ((dict = dict_handle(table)) == 0) { ! msg_warn("%s: unexpected dictionary: %s", myname, table); ! value = "451 4.3.5 Server configuration error"; ! CHK_ACCESS_RETURN(check_table_result(state, table, value, name, ! reply_name, reply_class, ! def_acl), FOUND); ! } if (flags == 0 || (flags & dict->flags) != 0) { if ((value = dict_get(dict, name)) != 0) CHK_ACCESS_RETURN(check_table_result(state, table, value, name, *************** *** 2340,2347 **** */ #define CHK_DOMAIN_RETURN(x,y) { *found = y; return(x); } ! if ((dict = dict_handle(table)) == 0) ! msg_panic("%s: dictionary not found: %s", myname, table); for (name = domain; *name != 0; name = next) { if (flags == 0 || (flags & dict->flags) != 0) { if ((value = dict_get(dict, name)) != 0) --- 2345,2357 ---- */ #define CHK_DOMAIN_RETURN(x,y) { *found = y; return(x); } ! if ((dict = dict_handle(table)) == 0) { ! msg_warn("%s: unexpected dictionary: %s", myname, table); ! value = "451 4.3.5 Server configuration error"; ! CHK_DOMAIN_RETURN(check_table_result(state, table, value, ! domain, reply_name, reply_class, ! def_acl), FOUND); ! } for (name = domain; *name != 0; name = next) { if (flags == 0 || (flags & dict->flags) != 0) { if ((value = dict_get(dict, name)) != 0) *************** *** 2399,2406 **** #endif delim = '.'; ! if ((dict = dict_handle(table)) == 0) ! msg_panic("%s: dictionary not found: %s", myname, table); do { if (flags == 0 || (flags & dict->flags) != 0) { if ((value = dict_get(dict, addr)) != 0) --- 2409,2421 ---- #endif delim = '.'; ! if ((dict = dict_handle(table)) == 0) { ! msg_warn("%s: unexpected dictionary: %s", myname, table); ! value = "451 4.3.5 Server configuration error"; ! CHK_ADDR_RETURN(check_table_result(state, table, value, address, ! reply_name, reply_class, ! def_acl), FOUND); ! } do { if (flags == 0 || (flags & dict->flags) != 0) { if ((value = dict_get(dict, addr)) != 0) *************** *** 2480,2485 **** --- 2495,2504 ---- 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. *************** *** 2534,2548 **** * * If the domain name exists but no NS record exists, look up parent domain * NS records. */ dns_status = dns_lookup(domain, type, 0, &server_list, (VSTRING *) 0, (VSTRING *) 0); ! if (dns_status == DNS_NOTFOUND && 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) { while ((domain = strchr(domain, '.')) != 0 && domain[1]) { domain += 1; dns_status = dns_lookup(domain, type, 0, &server_list, --- 2553,2578 ---- * * 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); ! 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 && h_errno == NO_DATA) { while ((domain = strchr(domain, '.')) != 0 && domain[1]) { domain += 1; dns_status = dns_lookup(domain, type, 0, &server_list, *************** *** 2552,2560 **** --- 2582,2612 ---- } } } + + #ifndef VAR_MAP_DEFER_CODE + #define var_map_reject_code var_reject_code + #define var_map_defer_code var_defer_code + #endif + 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, + 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); } *************** *** 2567,2576 **** --- 2619,2637 ---- * 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) + CHECK_SERVER_RETURN(status); + continue; + } if ((status = check_domain_access(state, table, (char *) server->data, FULL, &found, reply_name, reply_class, def_acl)) != 0 || found) *************** *** 2580,2587 **** --- 2641,2651 ---- 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", *************** *** 2605,2611 **** } freeaddrinfo(res0); /* 200412 */ } ! CHECK_SERVER_RETURN(SMTPD_CHECK_DUNNO); } /* check_ccert_access - access for TLS clients by certificate fingerprint */ --- 2669,2683 ---- } freeaddrinfo(res0); /* 200412 */ } ! 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 */