changeset 451:f2bc221240e8 stable-6-0-70

add unsigned_black for enforcement of dmarc policy
author Carl Byington <carl@five-ten-sg.com>
date Mon, 04 Jun 2018 16:25:06 -0700
parents a0293ef794a7
children 3bbc64cece70
files ChangeLog NEWS configure.in dnsbl.spec.in src/context.cpp src/context.h src/dnsbl.cpp xml/dnsbl.in
diffstat 8 files changed, 81 insertions(+), 40 deletions(-) [+]
line wrap: on
line diff
--- a/ChangeLog	Tue Apr 10 13:00:55 2018 -0700
+++ b/ChangeLog	Mon Jun 04 16:25:06 2018 -0700
@@ -1,3 +1,6 @@
+6.70 2018-06-04
+    add unsigned_black for enforcement of dmarc policy.
+
 6.69 2018-04-10
     fix spf mx:domain.tld token parsing.
 
--- a/NEWS	Tue Apr 10 13:00:55 2018 -0700
+++ b/NEWS	Mon Jun 04 16:25:06 2018 -0700
@@ -1,3 +1,4 @@
+6.70 2018-06-04 add unsigned_black for enforcement of dmarc policy
 6.69 2018-04-10 fix spf mx:domain.tld token parsing
 6.68 2018-02-19 round spamassassin scores; check >= rather than >
 6.67 2018-01-05 always call dcc code so we get log entries
--- a/configure.in	Tue Apr 10 13:00:55 2018 -0700
+++ b/configure.in	Mon Jun 04 16:25:06 2018 -0700
@@ -1,6 +1,6 @@
 
 AC_PREREQ(2.59)
-AC_INIT(dnsbl,6.69,carl@five-ten-sg.com)
+AC_INIT(dnsbl,6.70,carl@five-ten-sg.com)
 AC_CONFIG_SRCDIR([config.h.in])
 AC_CONFIG_HEADER([config.h])
 AC_CONFIG_MACRO_DIR([m4])
--- a/dnsbl.spec.in	Tue Apr 10 13:00:55 2018 -0700
+++ b/dnsbl.spec.in	Mon Jun 04 16:25:06 2018 -0700
@@ -155,6 +155,9 @@
 
 
 %changelog
+* Mon Jun 04 2018 Carl Byington <carl@five-ten-sg.com> - 6.70-1
+- add unsigned_black for enforcement of dmarc policy
+
 * Tue Apr 10 2018 Carl Byington <carl@five-ten-sg.com> - 6.69-1
 - fix spf mx:domain.tld token parsing
 
--- a/src/context.cpp	Tue Apr 10 13:00:55 2018 -0700
+++ b/src/context.cpp	Mon Jun 04 16:25:06 2018 -0700
@@ -73,6 +73,7 @@
 const char *token_dkim_from;
 const char *token_signed_white;
 const char *token_signed_black;
+const char *token_unsigned_black;
 const char *token_require_signed;
 const char *token_myhostname;
 
@@ -1319,6 +1320,7 @@
         DKIMP dk = find_dkim_from(from);
         if (dk) {
             const char *st = dk->action;
+            bool dmarc = false;
             for (string_set::iterator s=signers.begin(); s!=signers.end(); s++) {
                 // signed by a white listed signer
                 if ((st == token_signed_white)   && in_signing_set(*s,dk->signer)) {
@@ -1337,6 +1339,22 @@
                     msg = string(buf);
                     return token_black;
                 }
+                if ((st == token_unsigned_black) && in_signing_set(*s,dk->signer)) {
+                    dmarc = true;
+                }
+            }
+            if (st == token_unsigned_black) {
+                // enforce dmarc
+                if (!dmarc) {
+                    dmarc = resolve_spf(from, ntohl(priv->ip), priv);
+                }
+                if (!dmarc) {
+                    // not signed and does not pass spf, reject it
+                    char buf[maxlen];
+                    snprintf(buf, sizeof(buf), "Mail rejected - not dkim signed by %s", dk->signer);
+                    msg = string(buf);
+                    return token_black;
+                }
             }
             if (st == token_signed_white) {
                 // not signed by a white listed signer, but maybe passes strong spf check
@@ -2349,6 +2367,7 @@
     token_dkim_from         = register_string("dkim_from");
     token_signed_white      = register_string("signed_white");
     token_signed_black      = register_string("signed_black");
+    token_unsigned_black    = register_string("unsigned_black");
     token_require_signed    = register_string("require_signed");
 
     if (gethostname(myhostname, HOST_NAME_MAX+1) != 0) {
--- a/src/context.h	Tue Apr 10 13:00:55 2018 -0700
+++ b/src/context.h	Mon Jun 04 16:25:06 2018 -0700
@@ -16,6 +16,7 @@
 #endif
 
 enum status {oksofar,       // not rejected yet
+             whitesofar,    // probably whitelisted but require_signed or unsigned_black dkim requirements might change that
              white,         // whitelisted
              black,         // blacklisted
              reject};       // rejected by a dns list
@@ -407,6 +408,7 @@
 extern const char *token_dkim_from;
 extern const char *token_signed_white;
 extern const char *token_signed_black;
+extern const char *token_unsigned_black;
 extern const char *token_require_signed;
 extern const char *token_myhostname;
 
--- a/src/dnsbl.cpp	Tue Apr 10 13:00:55 2018 -0700
+++ b/src/dnsbl.cpp	Mon Jun 04 16:25:06 2018 -0700
@@ -518,7 +518,7 @@
         if (debug_syslog > 2) {
             char tmp[maxlen];
             snprintf(tmp, sizeof(tmp), "found %s on %s", hostname, priv.uribl_suffix);
-            my_syslog(tmp);
+            my_syslog(&priv, tmp);
         }
         found = register_string(hosts, hostname);
         return true;
@@ -1424,15 +1424,19 @@
             DKIMP dk = con.find_dkim_from(domain+1);
             if (dk && (dk->action == token_require_signed)) {
                 my_syslog(&priv, "dkim require_signed overrides envelope from whitelist");
-                st = oksofar;
+                st = whitesofar;
+            }
+            else if (dk && (dk->action == token_unsigned_black)) {
+                my_syslog(&priv, "dkim unsigned_black overrides envelope from whitelist");
+                st = whitesofar;
             }
             else st = white;
         }
         else st = white;    // might be <>, envelope from has no @
     }
 
-    if (st == oksofar) {
-        // check the dns based lists, whitelist first
+    if ((st == oksofar) || (st == whitesofar)) {
+        // check the dns based whitelists
         DNSWLP acceptlist = NULL;   // list that caused the whitelisting
         if (check_dnswl(priv, con.get_dnswl_list(), acceptlist)) {
             st = white;
@@ -1442,7 +1446,11 @@
                 my_syslog(&priv, msg);
             }
         }
-        else if (check_dnsbl(priv, con.get_dnsbl_list(), rejectlist)) {
+    }
+
+    if (st == oksofar) {
+        // check the dns based blacklists
+        if (check_dnsbl(priv, con.get_dnsbl_list(), rejectlist)) {
             // reject the recipient based on some dnsbl
             char adr[sizeof "255.255.255.255   "];
             adr[0] = '\0';
@@ -1452,9 +1460,6 @@
             smfi_setreply(ctx, (char*)"550", (char*)"5.7.1", buf);
             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
@@ -1535,7 +1540,7 @@
     // accept the recipient
     if (!con.get_content_filtering()) st = white;
 
-    if (st == oksofar) {
+    if ((st == oksofar) || (st == whitesofar)) {
         // remember first content filtering context
         if (con.get_content_filtering()) {
             if (!priv.content_context) priv.content_context = &con;
@@ -1544,6 +1549,7 @@
                 return SMFIS_TEMPFAIL;
             }
             priv.need_content_filter(con);
+            if (st == oksofar) {
             char bu[maxlen];
             bool uri = false;
             // content filtering implies also checking helo name on uribl (if enabled)
@@ -1568,6 +1574,7 @@
                 return SMFIS_REJECT;
             }
         }
+        }
         // remember the non-whites
         register_string(priv.env_to, rcptaddr, &con);
         priv.only_whites = false;
--- a/xml/dnsbl.in	Tue Apr 10 13:00:55 2018 -0700
+++ b/xml/dnsbl.in	Mon Jun 04 16:25:06 2018 -0700
@@ -25,7 +25,7 @@
 
     <refentry id="@PACKAGE@.1">
         <refentryinfo>
-            <date>2017-11-03</date>
+            <date>2018-06-04</date>
             <author>
                 <firstname>Carl</firstname>
                 <surname>Byington</surname>
@@ -393,7 +393,7 @@
                     If the answer is white, the mail is not from localhost,
                     and the envelope from domain name is
                     listed in the current (or parents) filtering contexts dkim_from with
-                    "required_signed", we downgrade this white answer to unknown.
+                    "required_signed" or "unsigned_black", we downgrade this white answer to unknown.
                     If the answer is still white, mail to this recipient is accepted and the dns
                     lists are not checked.
                 </para></listitem>
@@ -474,14 +474,14 @@
             </para>
             <para>
                 If the header from domain maps to required_signed then:
-                If any of the message signers are in that list, the message is accepted.
-                If the source ip address passes a strong spf check for the header from
+                If any of the message signers are in that list, or if
+                the source ip address passes a strong spf check for the header from
                 domain, the message is accepted. Otherwise, the message is rejected.
             </para>
             <para>
                 If the header from domain maps to signed_white then:
-                If any of the message signers are in that list, the message is accepted.
-                If the source ip address passes a strong spf check for the header from
+                If any of the message signers are in that list, or if
+                the source ip address passes a strong spf check for the header from
                 domain, the message is accepted. Otherwise, processing continues.
             </para>
             <para>
@@ -490,6 +490,12 @@
                 Otherwise, processing continues.
             </para>
             <para>
+                If the header from domain maps to unsigned_black then:
+                If any of the message signers are in that list, or if
+                the source ip address passes a strong spf check for the header from
+                domain, processing continues. Otherwise, the message is rejected.
+            </para>
+            <para>
                 If any of the message signers are blacklisted, the message is rejected.
             </para>
             <para>
@@ -779,7 +785,7 @@
 
     <refentry id="@PACKAGE@.conf.5">
         <refentryinfo>
-            <date>2017-11-03</date>
+            <date>2018-06-04</date>
             <author>
                 <firstname>Carl</firstname>
                 <surname>Byington</surname>
@@ -859,7 +865,7 @@
 
 DKIMSIGNER = "dkim_signer" "{" {SIGNING_DOMAIN DEF [";"]}+ "}"
 DKIMFROM   = "dkim_from"   "{" {HEADER_FROM_DOMAIN DKIMVALUE SIGNERS [";"]}+ "}"
-DKIMVALUE  = "signed_white" | "signed_black" | "require_signed"
+DKIMVALUE  = "signed_white" | "signed_black" | "require_signed" | "unsigned_black"
 SIGNERS    = quoted comma separated SIGNING_DOMAINs no whitespace
 
 ENV-TO     = "env_to"     "{" {(TO-ADDR | DCC-TO)}+ "}"