changeset 326:5e4b5540c8cc

allow multiple dkim signers in authentication results
author Carl Byington <carl@five-ten-sg.com>
date Sun, 18 Dec 2016 16:51:33 -0800
parents 28b6e0d97c5b
children 51846836ec92
files src/context.cpp src/context.h src/dnsbl.cpp src/dnsbl.h
diffstat 4 files changed, 54 insertions(+), 36 deletions(-) [+]
line wrap: on
line diff
--- a/src/context.cpp	Sat Dec 17 21:59:23 2016 -0800
+++ b/src/context.cpp	Sun Dec 18 16:51:33 2016 -0800
@@ -1099,36 +1099,43 @@
 }
 
 
-bool CONTEXT::acceptable_content(recorder &memory, int score, int bulk, const char *signer, const char *from, string& msg) {
-    const char *st = find_dkim_signer(signer);
+bool CONTEXT::acceptable_content(recorder &memory, int score, int bulk, string_set &signers, const char *from, string& msg) {
+    DKIMP dk = find_dkim_from(from);
+    bool requirement = false;
+    for (string_set::iterator s=signers.begin(); s!=signers.end(); s++) {
+        const char *st = find_dkim_signer(*s);
+        // signed by a white listed signer
     if (st == token_white) return true;
+        // signed by a black listed signer
     if (st == token_black) {
         char buf[maxlen];
-        snprintf(buf, sizeof(buf), "Mail rejected - dkim signed by %s", signer);
+            snprintf(buf, sizeof(buf), "Mail rejected - dkim signed by %s", *s);
         msg = string(buf);
         return false;
     }
 
-    DKIMP dk = find_dkim_from(from);
     if (dk) {
         st = dk->action;
         // signed by a white listed signer
-        if ((st == token_signed_white)   && (strcasecmp(signer,dk->signer) == 0)) return true;
-        // not signed by the required signer
-        if ((st == token_require_signed) && (strcasecmp(signer,dk->signer) != 0)) {
-            char buf[maxlen];
-            snprintf(buf, sizeof(buf), "Mail rejected - not dkim signed by %s", dk->signer);
-            msg = string(buf);
-            return false;
-        }
+            if ((st == token_signed_white)   && (strcasecmp(*s,dk->signer) == 0)) return true;
+            // is it signed by the required signer
+            if ((st == token_require_signed) && (strcasecmp(*s,dk->signer) == 0)) requirement = true;
         // signed by a black listed signer
-        if ((st == token_signed_black)   && (strcasecmp(signer,dk->signer) == 0)) {
+            if ((st == token_signed_black)   && (strcasecmp(*s,dk->signer) == 0)) {
             char buf[maxlen];
             snprintf(buf, sizeof(buf), "Mail rejected - dkim signed by %s", dk->signer);
             msg = string(buf);
             return false;
         }
     }
+    }
+
+    if (dk && (dk->action == token_require_signed) && !requirement) {
+        char buf[maxlen];
+        snprintf(buf, sizeof(buf), "Mail rejected - not dkim signed by %s", dk->signer);
+        msg = string(buf);
+        return false;
+    }
 
     if (spamassassin_limit && (score > spamassassin_limit)) {
         char buf[maxlen];
--- a/src/context.h	Sat Dec 17 21:59:23 2016 -0800
+++ b/src/context.h	Sun Dec 18 16:51:33 2016 -0800
@@ -312,7 +312,7 @@
     dnsblp_list&    get_dnsbl_list();
     dnswlp_list&    get_dnswl_list();
 
-    bool        acceptable_content(recorder &memory, int score, int bulk, const char *signer, const char *from, string& msg);
+    bool        acceptable_content(recorder &memory, int score, int bulk, string_set &signers, const char *from, string& msg);
     bool        ignore_host(const char *host);
 
     void        dump(bool isdefault, bool &spamass, int level = 0);
--- a/src/dnsbl.cpp	Sat Dec 17 21:59:23 2016 -0800
+++ b/src/dnsbl.cpp	Sun Dec 18 16:51:33 2016 -0800
@@ -525,7 +525,6 @@
     fromaddr                = NULL;
     header_count            = 0;
     dkim_ok                 = true;
-    dkim_signer             = NULL;
     queueid                 = NULL;
     authenticated           = NULL;
     client_name             = NULL;
@@ -574,11 +573,11 @@
     }
     if (mailaddr)        free((void*)mailaddr);
     if (fromaddr)        free((void*)fromaddr);
-    if (dkim_signer)     free((void*)dkim_signer);
     if (queueid)         free((void*)queueid);
     if (authenticated)   free((void*)authenticated);
     if (client_name)     free((void*)client_name);
     if (client_dns_name) free((void*)client_dns_name);
+    discard(dkim_signers);
     discard(hosts_uribl);
     delayer.clear();
     discard(env_to);
@@ -593,7 +592,6 @@
         fromaddr                = NULL;
         header_count            = 0;
         dkim_ok                 = true;
-        dkim_signer             = NULL;
         queueid                 = NULL;
         authenticated           = NULL;
         client_name             = NULL;
@@ -1474,14 +1472,17 @@
             if (priv.dkim_ok) {
                 const int nmatch = 2;
                 regmatch_t match[nmatch];
+                while (true) {
                 if (0 == regexec(&dkim_pattern, msg, nmatch, match, 0)) {
                     int s1 = match[1].rm_so;    // domain
                     int e1 = match[1].rm_eo;
                     if (s1 != -1) {
                         msg[e1] = '\0';
-                        priv.dkim_signer = strdup(msg+s1);
+                            priv.dkim_signers.insert(strdup(msg+s1));
                     }
                 }
+                    else break;
+                }
             }
         }
         if ((priv.header_count > 2) && (strcasecmp(headerf, "from"))) {
@@ -1582,13 +1583,23 @@
         string_set alive;
         bool random = false;
         int  limit  = 0;
-        snprintf(buf, sizeof(buf), "acceptable content from %s signer %s", (priv.fromaddr) ? priv.fromaddr : token_asterisk, (priv.dkim_signer) ? priv.dkim_signer : token_asterisk);
+        if (priv.dkim_signers.empty()) {
+            snprintf(buf, sizeof(buf), "acceptable content from %s signer *",
+                (priv.fromaddr) ? priv.fromaddr : token_asterisk);
         my_syslog(&priv, buf);
+        }
+        else {
+            for (string_set::iterator s=priv.dkim_signers.begin(); s!=priv.dkim_signers.end(); s++) {
+                snprintf(buf, sizeof(buf), "acceptable content from %s signer %s",
+                    (priv.fromaddr) ? priv.fromaddr : token_asterisk, *s);
+                my_syslog(&priv, buf);
+            }
+        }
 
         for (context_map::iterator i=priv.env_to.begin(); i!=priv.env_to.end(); i++) {
             const char *rcpt   = (*i).first;
             CONTEXT &con = *((*i).second);
-            if (!con.acceptable_content(*priv.memory, score, bulk, priv.dkim_signer, priv.fromaddr, msg)) {
+            if (!con.acceptable_content(*priv.memory, score, bulk, priv.dkim_signers, priv.fromaddr, msg)) {
                 // bad html tags or excessive hosts or
                 // high spam assassin score or dcc bulk threshold exceedeed
                 // or signed by a dkim signer that we don't like
--- a/src/dnsbl.h	Sat Dec 17 21:59:23 2016 -0800
+++ b/src/dnsbl.h	Sun Dec 18 16:51:33 2016 -0800
@@ -42,7 +42,7 @@
     const char      *fromaddr;              // header from value, set by mlfi_header()
     int             header_count;           // count of headers already seen
     bool            dkim_ok;                // ok to proceed with dkim checking
-    const char      *dkim_signer;           // non null if message was validly signed
+    string_set      dkim_signers;           // non empty if message was validly signed, set of signers
     const char      *queueid;               // sendmail queue id
     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)