]> git.ipfire.org Git - thirdparty/postfix.git/commitdiff
postfix-2.6.4-RC2 v2.6.4-RC2
authorWietse Venema <wietse@porcupine.org>
Fri, 7 Aug 2009 05:00:00 +0000 (00:00 -0500)
committerViktor Dukhovni <postfix-users@dukhovni.org>
Sat, 10 Feb 2018 19:36:34 +0000 (14:36 -0500)
postfix/HISTORY
postfix/RELEASE_NOTES
postfix/src/global/mail_version.h
postfix/src/smtpd/smtpd_check.c

index 7067bd124ce9454bde2a2cf02a90e09970823dee..a01fca1c58a3b10888c99a47f1055d457f8d0330 100644 (file)
@@ -15270,13 +15270,33 @@ Apologies for any names omitted.
        feature can be used meaningfully at any protocol stage.
        File: proto/postconf.proto.
 
-20090803
-
-       Workaround: with some local DNS servers including BIND, it
-       is possible that A or MX lookups succeed, while NS lookups
-       for the same domains time out.  Spammers use this to avoid
-       access restrictions.  To deal with future variations of
-       this, check_{client,helo,sender,etc}_{mx,ns,etc}_access no
-       longer tolerate any lookup failures. Instead, they reply
-       with $access_map_defer_code or $access_map_reject_code as
-       appropriate. File: smtpd/smtpd_check.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 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
+        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.
+
+        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.
index 8c720bbef9f3ef8483621a2852076863a7c4b774..f17d914c993f10ebe02c8bc9ddf6df0ecb72f7d4 100644 (file)
@@ -17,18 +17,23 @@ before proceeding.
 Incompatibility with Postfix 2.6.4
 ==================================
 
-The check_{client,helo,sender,etc}_{mx,ns,etc}_access features no
-longer tolerate any lookup failures. Instead, they now reply with
-$access_map_defer_code or $access_map_reject_code as appropriate.
-
-The reason for this change is that spammers are using tricks where
-A or MX lookups succeed while NS lookups for the same domains fail,
-depending local DNS infrastructure details.  The change deals with
-future variants of this anomalous behavior.
-
-As a side effect, non-existent domain names in HELO commands will
-now trigger a REJECT action with check_helo_{mx,ns}_access, where
-previously such commands were silently permitted.
+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 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.
+
+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.
 
 Major changes - multi-instance support
 --------------------------------------
index dce91ae4b006dbfd6d8e6a96c1fe20f3297f04a4..52f91ed27131536a96136754e8d75e1858a2f6eb 100644 (file)
@@ -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      "20090803"
-#define MAIL_VERSION_NUMBER    "2.6.4-RC1"
+#define MAIL_RELEASE_DATE      "20090807"
+#define MAIL_VERSION_NUMBER    "2.6.4-RC2"
 
 #ifdef SNAPSHOT
 # define MAIL_VERSION_DATE     "-" MAIL_RELEASE_DATE
index 74d86ec4782cd335aa1855a17521a6b447270134..a2e04f4cbb0151ee8d32b4644cd5a5c8796e1713 100644 (file)
@@ -2315,8 +2315,13 @@ static int check_access(SMTPD_STATE *state, const char *table, const char *name,
     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,
@@ -2360,8 +2365,13 @@ static int check_domain_access(SMTPD_STATE *state, const char *table,
      */
 #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)
@@ -2419,8 +2429,13 @@ static int check_addr_access(SMTPD_STATE *state, const char *table,
 #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)
@@ -2500,6 +2515,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.
@@ -2554,15 +2573,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,
@@ -2575,14 +2605,23 @@ 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));
-       /* No mercy for DNS failure. */
-       return (smtpd_check_reject(state, MAIL_ERROR_POLICY,
-                                  dns_status == DNS_NOTFOUND ?
-                                  var_map_reject_code : var_map_defer_code,
-                                  smtpd_dsn_fix("4.1.8", reply_class),
-                                  "<%s>: %s rejected: %s",
-                                  reply_name, reply_class,
-                                  "Domain not found"));
+       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);
     }
 
     /*
@@ -2594,10 +2633,19 @@ 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)
+               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)
@@ -2607,17 +2655,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));
-           /* No mercy for DNS failure. */
-           status = smtpd_check_reject(state,
-                                       MAIL_ERROR_POLICY,
-                                       aierr == EAI_NONAME ?
-                                  var_map_reject_code : var_map_defer_code,
-                                       smtpd_dsn_fix("4.1.8", reply_class),
-                                       "<%s>: %s rejected: %s",
-                                       reply_name, reply_class,
-                                       "Domain not found");
-           CHECK_SERVER_RETURN(status);
+           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",
@@ -2641,7 +2683,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 */