changeset 268:f941563c2a95 stable-6-0-34

Add require_rdns checking
author Carl Byington <carl@five-ten-sg.com>
date Wed, 22 May 2013 11:29:44 -0700
parents db12f6028f8b
children 6d2a11f0ae41
files ChangeLog NEWS configure.in dnsbl.conf dnsbl.spec.in src/context.cpp src/context.h src/dnsbl.cpp src/dnsbl.h xml/dnsbl.in
diffstat 10 files changed, 70 insertions(+), 11 deletions(-) [+]
line wrap: on
line diff
--- a/ChangeLog	Sat Jul 21 13:13:07 2012 -0700
+++ b/ChangeLog	Wed May 22 11:29:44 2013 -0700
@@ -1,3 +1,6 @@
+6.34 2013-05-22
+    Add require_rdns checking.
+
 6.33 2012-07-21
     Fix unauthenticated rate limit bug for empty mail from.
     Move unauthenticated rate limit checks after spam filtering.
--- a/NEWS	Sat Jul 21 13:13:07 2012 -0700
+++ b/NEWS	Wed May 22 11:29:44 2013 -0700
@@ -1,3 +1,4 @@
+6.34 2013-05-22 Add require_rdns checking.
 6.33 2012-07-21 Fix unauthenticated rate limit bug for empty mail from. Move unauthenticated rate limit checks after spam filtering.
 6.32 2012-07-21 Allow rate limiting for unauthentication connections by mail from address or domain.
 6.31 2012-07-01 Fix uribl lookups on client dns name.
--- a/configure.in	Sat Jul 21 13:13:07 2012 -0700
+++ b/configure.in	Wed May 22 11:29:44 2013 -0700
@@ -1,6 +1,6 @@
 
 AC_PREREQ(2.59)
-AC_INIT(dnsbl,6.33,carl@five-ten-sg.com)
+AC_INIT(dnsbl,6.34,carl@five-ten-sg.com)
 AC_CONFIG_SRCDIR([config.h.in])
 AC_CONFIG_HEADER([config.h])
 
--- a/dnsbl.conf	Sat Jul 21 13:13:07 2012 -0700
+++ b/dnsbl.conf	Wed May 22 11:29:44 2013 -0700
@@ -48,6 +48,7 @@
     dnswl   dnswl.org  list.dnswl.org   2;
     dnsbl_list  local sbl;
     dnswl_list  dnswl.org;
+    require_rdns    yes;
 
     content on {
         filter    sbl-xbl.spamhaus.org        "Mail containing %s rejected - sbl; see http://www.spamhaus.org/query/bl?ip=%s";
--- a/dnsbl.spec.in	Sat Jul 21 13:13:07 2012 -0700
+++ b/dnsbl.spec.in	Wed May 22 11:29:44 2013 -0700
@@ -103,6 +103,9 @@
 
 
 %changelog
+* Wed May 22 2013 Carl Byington <carl@five-ten-sg.com> - 6.34-1
+- Add require_rdns checking.
+
 * Sat Jul 21 2012 Carl Byington <carl@five-ten-sg.com> - 6.33-1
 - Fix unauthenticated rate limit bug for empty mail from.
   Move unauthenticated rate limit checks after spam filtering.
--- a/src/context.cpp	Sat Jul 21 13:13:07 2012 -0700
+++ b/src/context.cpp	Wed May 22 11:29:44 2013 -0700
@@ -55,6 +55,7 @@
 const char *token_rate;
 const char *token_rbrace;
 const char *token_require;
+const char *token_requirerdns;
 const char *token_semi;
 const char *token_soft;
 const char *token_spamassassin;
@@ -725,6 +726,7 @@
     tag_limit_message   = NULL;
     spamassassin_limit  = (parent) ? parent->spamassassin_limit : 0;
     require_match       = (parent) ? parent->require_match      : false;
+    require_rdns        = (parent) ? parent->require_rdns       : false;
     dcc_greylist        = (parent) ? parent->dcc_greylist       : false;
     dcc_bulk_threshold  = (parent) ? parent->dcc_bulk_threshold : 0;
     dnsbl_list_parsed   = false;
@@ -1074,6 +1076,7 @@
             printf(" %s", d.name);
         }
         printf("; \n");
+        printf("%s     require_rdns  %s; \n", indent, (require_rdns)  ? "yes" : "no");
     }
 
     {
@@ -1350,6 +1353,22 @@
 
 ////////////////////////////////////////////////
 //
+bool parse_requirerdns(TOKEN &tok, CONFIG &dc, CONTEXT &me);
+bool parse_requirerdns(TOKEN &tok, CONFIG &dc, CONTEXT &me) {
+    const char *have = tok.next();
+         if (have == token_yes) me.set_requirerdns(true);
+    else if (have == token_no)  me.set_requirerdns(false);
+    else {
+        tok.token_error("yes/no", have);
+        return false;
+    }
+    if (!tsa(tok, token_semi)) return false;
+    return true;
+}
+
+
+////////////////////////////////////////////////
+//
 bool parse_content(TOKEN &tok, CONFIG &dc, CONTEXT &me);
 bool parse_content(TOKEN &tok, CONFIG &dc, CONTEXT &me) {
     const char *setting = tok.next();
@@ -1768,6 +1787,9 @@
         else if (have == token_dnswll) {
             if (!parse_dnswll(tok, dc, *con)) return false;
         }
+        else if (have == token_requirerdns) {
+            if (!parse_requirerdns(tok, dc, *con)) return false;
+        }
         else if (have == token_content) {
             if (!parse_content(tok, dc, *con)) return false;
         }
@@ -1877,6 +1899,7 @@
     token_rate          = register_string("rate_limit");
     token_rbrace        = register_string("}");
     token_require       = register_string("require_match");
+    token_requirerdns   = register_string("require_rdns");
     token_semi          = register_string(";");
     token_soft          = register_string("soft");
     token_spamassassin  = register_string("spamassassin");
--- a/src/context.h	Sat Jul 21 13:13:07 2012 -0700
+++ b/src/context.h	Wed May 22 11:29:44 2013 -0700
@@ -171,6 +171,7 @@
     const char *    tag_limit_message;  // error message for excessive bad html tags
     int             spamassassin_limit; // max score from spamassassin
     bool            require_match;      // require matching context filtering context
+    bool            require_rdns;       // require proper rdns on client ip
     bool            dcc_greylist;       // should we do dcc greylisting?
     int             dcc_bulk_threshold; // off = 0, many = 1000
     dnsblp_map      dnsbl_names;        // name to dnsbl mapping for lists that are available in this context and children
@@ -254,11 +255,13 @@
     const char* generic_match(const char *client);
 
     void        set_require(bool r)                         {require_match      = r; };
+    void        set_requirerdns(bool r)                     {require_rdns       = r; };
     void        set_grey(bool g)                            {dcc_greylist       = g; };
     void        set_bulk(int b)                             {dcc_bulk_threshold = b; };
 
     bool            get_content_filtering()                 {return content_filtering; };
     bool            get_require()                           {return content_filtering && require_match;     };
+    bool            get_requirerdns()                       {return require_rdns;                           };
     bool            get_grey()                              {return content_filtering && dcc_greylist;      };
     int             get_bulk()                              {return (content_filtering) ? dcc_bulk_threshold : 0;};
     int             get_host_limit()                        {return (content_filtering) ? host_limit         : 0;};
@@ -338,6 +341,7 @@
 extern const char *token_rate;
 extern const char *token_rbrace;
 extern const char *token_require;
+extern const char *token_requirerdns;
 extern const char *token_semi;
 extern const char *token_soft;
 extern const char *token_spamassassin;
--- a/src/dnsbl.cpp	Sat Jul 21 13:13:07 2012 -0700
+++ b/src/dnsbl.cpp	Wed May 22 11:29:44 2013 -0700
@@ -488,6 +488,7 @@
     authenticated           = NULL;
     client_name             = NULL;
     client_dns_name         = NULL;
+    client_dns_forged       = false;
     host_uribl              = NULL;
     helo_uribl              = false;
     client_uribl            = false;
@@ -1149,6 +1150,15 @@
             //snprintf(text, sizeof(text), "found simple dns client name %s", priv.client_dns_name);
             //my_syslog(text);
         }
+        p = strstr(priv.client_name, "] (may be forged)");
+        if (p) {
+            priv.client_dns_forged = true;
+            if (priv.client_dns_name) {
+                char text[500];
+                snprintf(text, sizeof(text), "forged dns client name %s", priv.client_dns_name);
+                my_syslog(text);
+            }
+        }
     }
     if (spamc != spamc_empty) {
         priv.assassin  = new SpamAssassin(&priv, priv.ip, priv.helo, priv.mailaddr, priv.queueid);
@@ -1247,6 +1257,15 @@
         return SMFIS_REJECT;
     }
     if (st == oksofar) {
+        // check forged rdns
+        if (con.get_requirerdns() && (!priv.client_dns_name || priv.client_dns_forged)) {
+            // reject the recipient based on forged reverse dns
+            char buf[maxlen];
+            snprintf(buf, sizeof(buf), "%s is not acceptable", priv.client_name);
+            smfi_setreply(ctx, (char*)"550", (char*)"5.7.1", buf);
+            return SMFIS_REJECT;
+        }
+        // check generic rdns
         const char *msg = con.generic_match(priv.client_name);
         if (msg) {
             // reject the recipient based on generic reverse dns
@@ -1323,12 +1342,12 @@
                 snprintf(bu, sizeof(bu), "(helo %s)", priv.host_uribl);
                 uri = true;
             }
-            // content filterint implies also checking client reverse dns name on uribl (if enabled)
+            // content filtering implies also checking client reverse dns name on uribl (if enabled)
             if (priv.client_uribl) {
                 snprintf(bu, sizeof(bu), "(rdns %s)", priv.host_uribl);
                 uri = true;
             }
-            // content filterint implies also checking mail from domain name on uribl (if enabled)
+            // content filtering implies also checking mail from domain name on uribl (if enabled)
             if (priv.from_uribl) {
                 snprintf(bu, sizeof(bu), "(from %s)", priv.host_uribl);
                 uri = true;
--- a/src/dnsbl.h	Sat Jul 21 13:13:07 2012 -0700
+++ b/src/dnsbl.h	Wed May 22 11:29:44 2013 -0700
@@ -42,6 +42,7 @@
     const char      *authenticated;         // client authenticated? if so, suppress all dnsbl checks, but check rate limits
     const char      *client_name;           // fully qualified host name of the smtp client xxx [ip.ad.dr.es] (may be forged)
           char      *client_dns_name;       // fully qualified host name of the smtp client xxx
+    bool            client_dns_forged;      // rdns mismatch
     const char      *host_uribl;            // pointer to helo/client/from host name if found on uribl
     string_set      hosts_uribl;            // string set to hold the helo/client/from host name if found on uribl
     bool            helo_uribl;             // helo value on uribl
--- a/xml/dnsbl.in	Sat Jul 21 13:13:07 2012 -0700
+++ b/xml/dnsbl.in	Wed May 22 11:29:44 2013 -0700
@@ -335,8 +335,8 @@
             </para>
             <para>
                 If the client has authenticated with sendmail, the rate limits are
-                checked.  If the authenticated user has not exceeded the hourly rate
-                limit, then the mail is accepted, the filtering contexts are not used,
+                checked.  If the authenticated user has not exceeded the hourly or daily rate
+                limits, then the mail is accepted, the filtering contexts are not used,
                 the dns lists are not checked, and the body content is not scanned.  If
                 the client has not authenticated with sendmail, we follow these steps
                 for each recipient.
@@ -405,6 +405,11 @@
                 </para></listitem>
                 <listitem><para>
                     If the mail has not been accepted or rejected yet, and the filtering
+                    context (or any ancestor context) requires matching reverse dns client
+                    name, the mail is rejected if the client name is empty or forged.
+                </para></listitem>
+                <listitem><para>
+                    If the mail has not been accepted or rejected yet, and the filtering
                     context (or any ancestor context) specifies a non-empty generic regular
                     expression, then we check the fully qualified client name (obtained via
                     the sendmail macro "_").  The mail is rejected if the client name
@@ -597,10 +602,6 @@
                 Add the ability to use the DBL for content filtering. We need to avoid
                 checking bare ip addresses against that list.
             </para>
-            <para>
-                Add daily recipient limits based on some fixed multiple (perhaps 3?)
-                of the hourly limit.
-            </para>
         </refsect1>
 
         <refsect1 id='copyright.1'>
@@ -672,7 +673,7 @@
 CONTEXT    = "context" NAME "{" {STATEMENT}+ "}"
 STATEMENT  = (DNSBL    | DNSBLLIST | DNSWL   | DNSWLLIST | CONTENT | ENV-TO
              | VERIFY  | GENERIC   | W_REGEX | AUTOWHITE | CONTEXT | ENV-FROM
-             | RATE-LIMIT) ";"
+             | RATE-LIMIT | REQUIRERDNS) ";"
 
 DNSBL      = "dnsbl" NAME DNSPREFIX ERROR-MSG1
 DNSBLLIST  = "dnsbl_list" {NAME}*
@@ -681,6 +682,8 @@
 DNSWLLIST  = "dnswl_list" {NAME}*
 LEVEL      = INTEGER
 
+REQUIRERDNS = "require_rdns" ("yes" | "no")
+
 CONTENT    = "content" ("on" | "off") "{" {CONTENT-ST}+ "}"
 CONTENT-ST = (FILTER | URIBL | IGNORE | TLD     | CCTLD   | HTML-TAGS |
               HTML-LIMIT | HOST-LIMIT | SPAMASS | REQUIRE | DCCGREY   |
@@ -787,6 +790,7 @@
     dnswl   dnswl.org  list.dnswl.org   2;
     dnsbl_list  local sbl;
     dnswl_list  dnswl.org;
+    require_rdns    yes;
 
     content on {
         filter    sbl-xbl.spamhaus.org        "Mail containing %s rejected - sbl; see http://www.spamhaus.org/query/bl?ip=%s";