changeset 233:5c3e9bf45bb5 stable-6-0-23

Add whitelisting by regex expression filtering. Add queueid to whitelist extension log message.
author Carl Byington <carl@five-ten-sg.com>
date Mon, 25 May 2009 11:14:32 -0700
parents 768ce0f23149
children 1c45d50cbbc6
files ChangeLog NEWS configure.in dnsbl.conf dnsbl.spec.in src/context.cpp src/context.h src/dnsbl.cpp xml/dnsbl.in
diffstat 9 files changed, 85 insertions(+), 10 deletions(-) [+]
line wrap: on
line diff
--- a/ChangeLog	Fri May 08 15:21:40 2009 -0700
+++ b/ChangeLog	Mon May 25 11:14:32 2009 -0700
@@ -1,3 +1,7 @@
+6.23 2009-05-25
+    Add whitelisting by regex expression filtering.
+    Add queueid to whitelist extension log message.
+
 6.22 2009-05-08
     Prevent auto whitelisting due to outgoing multipart/report
     delivery notifications.
--- a/NEWS	Fri May 08 15:21:40 2009 -0700
+++ b/NEWS	Mon May 25 11:14:32 2009 -0700
@@ -1,3 +1,4 @@
+6.23 2009-05-25 Add whitelisting by regex expression filtering.
 6.22 2009-05-08 Prevent auto whitelisting due to outgoing multipart/report delivery notifications.
 6.21 2009-01-03 Fixes to compile on old systems without memrchr or string::clear().
 6.20 2008-12-27 Never whitelist self addressed mail.
--- a/configure.in	Fri May 08 15:21:40 2009 -0700
+++ b/configure.in	Mon May 25 11:14:32 2009 -0700
@@ -1,6 +1,6 @@
 
 AC_PREREQ(2.59)
-AC_INIT(dnsbl,6.22,carl@five-ten-sg.com)
+AC_INIT(dnsbl,6.23,carl@five-ten-sg.com)
 AC_CONFIG_SRCDIR([config.h.in])
 AC_CONFIG_HEADER([config.h])
 
--- a/dnsbl.conf	Fri May 08 15:21:40 2009 -0700
+++ b/dnsbl.conf	Mon May 25 11:14:32 2009 -0700
@@ -61,6 +61,8 @@
     generic "^dsl.static.*ttnet.net.tr$|(^|[x.-])(ppp|h|host)?([0-9]{1,3}[x.-](Red-|dynamic[x.-])?){4}"
             "your mail server %s seems to have a generic name";
 
+    white_regex "=example.com=user@yourhostingaccount.com$";
+
     env_to {
         # !! replace this with your domain names
         # child contexts are not allowed to specify recipient addresses outside these domains
--- a/dnsbl.spec.in	Fri May 08 15:21:40 2009 -0700
+++ b/dnsbl.spec.in	Mon May 25 11:14:32 2009 -0700
@@ -96,6 +96,9 @@
 
 
 %changelog
+* Mon May 25 2009 Carl Byington <carl@five-ten-sg.com> - 6.23-1
+- Add whitelisting by regex expression filtering.
+
 * Fri May 08 2009 Carl Byington <carl@five-ten-sg.com> - 6.22-1
 - Prevent auto whitelisting due to outgoing multipart/report
   delivery notifications.
--- a/src/context.cpp	Fri May 08 15:21:40 2009 -0700
+++ b/src/context.cpp	Mon May 25 11:14:32 2009 -0700
@@ -62,6 +62,7 @@
 const char *token_uribl;
 const char *token_verify;
 const char *token_white;
+const char *token_white_regex;
 const char *token_yes;
 
 const char *token_myhostname;
@@ -692,6 +693,7 @@
     verifier            = NULL;
     generic_regx        = NULL;
     generic_message     = NULL;
+    white_regx          = NULL;
     autowhite_file      = NULL;
     whitelister         = NULL;
     env_from_default    = (parent) ? token_inherit : token_unknown;
@@ -720,6 +722,7 @@
         delete d;
     }
     if (generic_regx) regfree(&generic_pattern);
+    if (white_regx)   regfree(&white_pattern);
 }
 
 
@@ -738,6 +741,26 @@
 }
 
 
+bool CONTEXT::set_white(const char *regx)
+{
+    int rc = 0;
+    if (white_regx) regfree(&white_pattern);
+    white_regx = regx;
+    if (white_regx) {
+        rc = regcomp(&white_pattern, regx, REG_NOSUB | REG_ICASE | REG_EXTENDED);
+    }
+    return rc;  // true iff bad pattern
+}
+
+
+bool CONTEXT::white_match(const char *from)
+{
+    return (from       &&
+            white_regx &&
+            (0 == regexec(&white_pattern, from, 0, NULL, 0)));
+}
+
+
 bool CONTEXT::set_generic(const char *regx, const char *msg)
 {
     int rc = 0;
@@ -806,7 +829,7 @@
 }
 
 
-const char *CONTEXT::find_from(const char *from, bool update_white) {
+const char *CONTEXT::find_from(const char *from, bool update_white, const char *queueid) {
     WHITELISTERP w = whitelister;
     CONTEXTP     p = parent;
     while (!w && p) {
@@ -814,12 +837,12 @@
         p = p->parent;
     }
     if (w && w->is_white(from)) {
-        if (update_white) {
+        if (update_white && queueid) {
             // update senders timestamp to extend the whitelisting period
             if (debug_syslog > 1) {
                 char buf[maxlen];
                 char msg[maxlen];
-                snprintf(msg, sizeof(msg), "extend whitelist reply from <%s> in context %s", from, get_full_name(buf,maxlen));
+                snprintf(msg, sizeof(msg), "%s: extend whitelist reply from <%s> in context %s", queueid, from, get_full_name(buf,maxlen));
                 my_syslog(msg);
             }
             w->sent(strdup(from));
@@ -844,6 +867,10 @@
             }
         }
     }
+    if ((rc == token_inherit) || (rc == token_unknown)) {
+        bool ok = white_match(from);
+        if (ok) rc = token_white;
+    }
     if ((rc == token_inherit) && parent) return parent->find_from(from);
     return (rc == token_inherit) ? token_unknown : rc;
 }
@@ -1067,6 +1094,10 @@
         printf("%s             \"%s\"; \n", indent, generic_message);
     }
 
+    if (white_regx) {
+        printf("%s     white_regex \"%s\"; \n", indent, white_regx);
+    }
+
     if (autowhite_file && whitelister) {
         printf("%s     autowhite %d %s; \n", indent, whitelister->get_days(), autowhite_file);
     }
@@ -1465,6 +1496,20 @@
 
 ////////////////////////////////////////////////
 //
+bool parse_white(TOKEN &tok, CONFIG &dc, CONTEXT &me);
+bool parse_white(TOKEN &tok, CONFIG &dc, CONTEXT &me) {
+    const char *regx = tok.next();
+    if (!tsa(tok, token_semi)) return false;
+    if (me.set_white(regx)) {
+        tok.token_error("invalid regular expression %s", regx, regx);
+        return false;
+    }
+    return true;
+}
+
+
+////////////////////////////////////////////////
+//
 bool parse_autowhite(TOKEN &tok, CONFIG &dc, CONTEXT &me);
 bool parse_autowhite(TOKEN &tok, CONFIG &dc, CONTEXT &me) {
     int days = tok.nextint();
@@ -1607,6 +1652,9 @@
         else if (have == token_generic) {
             if (!parse_generic(tok, dc, *con)) return false;
         }
+        else if (have == token_white_regex) {
+            if (!parse_white(tok, dc, *con)) return false;
+        }
         else if (have == token_autowhite) {
             if (!parse_autowhite(tok, dc, *con)) return false;
         }
@@ -1708,6 +1756,7 @@
     token_uribl         = register_string("uribl");
     token_verify        = register_string("verify");
     token_white         = register_string("white");
+    token_white_regex   = register_string("white_regex");
     token_yes           = register_string("yes");
 
     if (gethostname(myhostname, HOST_NAME_MAX+1) != 0) {
--- a/src/context.h	Fri May 08 15:21:40 2009 -0700
+++ b/src/context.h	Mon May 25 11:14:32 2009 -0700
@@ -135,6 +135,8 @@
     const char *    generic_regx;       // pointer to generic regular expression
     const char *    generic_message;    // pointer to generic message with one %s
     regex_t         generic_pattern;    // compiled regex pattern
+    const char *    white_regx;         // pointer to whitelist regular expression
+    regex_t         white_pattern;      // compiled regex pattern
     const char *    autowhite_file;     // file to use for automatic whitelisting
     WHITELISTERP    whitelister;        // pointer to the auto whitelister structure
     string_map      env_from;           // map senders to white/black/unknown
@@ -193,7 +195,7 @@
     void        add_from(const char *from, const char *status)    {env_from[from] = status;};
     void        add_from_context(const char *from, CONTEXTP con)  {env_from_context[from] = con;};
     void        set_from_default(const char *status)              {env_from_default = status;};
-    const char* find_from(const char *from, bool update_white = false);
+    const char* find_from(const char *from, bool update_white = false, const char *queueid = NULL);
     CONTEXTP    find_context(const char *from);
     CONTEXTP    find_from_context_name(const char *name);
 
@@ -218,6 +220,9 @@
     void        add_dnsbl(DNSBLP dns)                       {dnsbl_list.push_back(dns);};
     DNSBLP      find_dnsbl(const char *name);
 
+    bool        set_white(const char *regx);
+    bool        white_match(const char *from);
+
     bool        set_generic(const char *regx, const char *msg);
     const char* generic_match(const char *client);
 
@@ -312,6 +317,7 @@
 extern const char *token_uribl;
 extern const char *token_verify;
 extern const char *token_white;
+extern const char *token_white_regex;
 extern const char *token_yes;
 
 extern pthread_mutex_t verifier_mutex;     // protect the verifier map
--- a/src/dnsbl.cpp	Fri May 08 15:21:40 2009 -0700
+++ b/src/dnsbl.cpp	Mon May 25 11:14:32 2009 -0700
@@ -1051,7 +1051,7 @@
     // priv.mailaddr sending original message to loto
     CONTEXT          &con = *(dc.find_context(loto)->find_context(priv.mailaddr));
     VERIFYP           ver = con.find_verify(loto);
-    const char *fromvalue = con.find_from(priv.mailaddr, true);
+    const char *fromvalue = con.find_from(priv.mailaddr, true, priv.queueid);
     // tell spam assassin and dccifd about this recipient
     if (priv.assassin) priv.assassin->mlfi_envrcpt(ctx, loto);
     if (priv.dccifd)   priv.dccifd->mlfi_envrcpt(ctx, loto, con.get_grey() && !priv.authenticated);
--- a/xml/dnsbl.in	Fri May 08 15:21:40 2009 -0700
+++ b/xml/dnsbl.in	Mon May 25 11:14:32 2009 -0700
@@ -19,7 +19,7 @@
 
     <refentry id="@PACKAGE@.1">
         <refentryinfo>
-            <date>2008-06-08</date>
+            <date>2009-05-25</date>
         </refentryinfo>
 
         <refmeta>
@@ -364,6 +364,13 @@
                     parent context.
                 </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 whitelist regular
+                    expression, then we check the envelope from value against that regex.
+                    The mail is accepted if the envelope from value matches the specified regular
+                    expression.
+                </para></listitem>
+                <listitem><para>
                     If the mail has not been accepted or rejected yet, the dns lists
                     specified in the filtering context are checked and the mail is rejected
                     if any list has an A record for the standard dns based lookup scheme
@@ -586,7 +593,7 @@
 
     <refentry id="@PACKAGE@.conf.5">
         <refentryinfo>
-            <date>2008-06-08</date>
+            <date>2009-05-25</date>
         </refentryinfo>
 
         <refmeta>
@@ -620,7 +627,7 @@
 CONFIG     = {CONTEXT ";"}+
 CONTEXT    = "context" NAME "{" {STATEMENT}+ "}"
 STATEMENT  = (DNSBL | DNSBLLIST | CONTENT | ENV-TO   | VERIFY | GENERIC
-                    | AUTOWHITE | CONTEXT | ENV-FROM | RATE-LIMIT) ";"
+             | W_REGEX | AUTOWHITE | CONTEXT | ENV-FROM | RATE-LIMIT) ";"
 
 DNSBL      = "dnsbl" NAME DNSPREFIX ERROR-MSG1
 
@@ -659,6 +666,7 @@
 
 VERIFY     = "verify" HOSTNAME ";"
 GENERIC    = "generic" REGULAREXPRESSION ERROR-MSG4 ";"
+W-REGEX    = "white_regex" REGULAREXPRESSION ";"
 ERROR-MSG4 = string containing exactly one %s replacement token
              which is replaced with the client name
 AUTOWHITE  = "autowhite" DAYS FILENAME ";"
@@ -741,6 +749,8 @@
     generic "^dsl.static.*ttnet.net.tr$|(^|[x.-])(ppp|h|host)?([0-9]{1,3}[x.-](Red-|dynamic[x.-])?){4}"
             "your mail server %s seems to have a generic name";
 
+    white_regex ".*=example.com=user@yourhostingaccount.com";
+
     env_to {
         # !! replace this with your domain names
         # child contexts are not allowed to specify recipient addresses outside these domains