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.
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 ((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,
*/
#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);
+ 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)
#endif
delim = '.';
- if ((dict = dict_handle(table)) == 0)
- msg_panic("%s: dictionary not found: %s", myname, table);
+ 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)
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.
*
* 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,
}
}
}
+
+#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);
}
* 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)
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",
}
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 */