changeset 407:29d54e7028f6 stable-6-0-54

document dmarc vs dnsbl dkim/spf; switch to . rather than " " for dkim impossible signer
author Carl Byington <carl@five-ten-sg.com>
date Thu, 30 Mar 2017 10:26:30 -0700 (2017-03-30)
parents ca4ef407588e
children 067963dc142f
files ChangeLog NEWS configure.in dnsbl.spec.in src/context.cpp xml/Makefile.am xml/dnsbl.in
diffstat 7 files changed, 105 insertions(+), 15 deletions(-) [+]
line wrap: on
line diff
--- a/ChangeLog	Fri Mar 17 15:29:40 2017 -0700
+++ b/ChangeLog	Thu Mar 30 10:26:30 2017 -0700
@@ -1,3 +1,7 @@
+6.54 2017-03-30
+    document dmarc vs dnsbl dkim/spf. switch to . rather than
+    " " for dkim impossible signer.
+
 6.53 2017-03-17
     suppress duplicate calls to acceptable_content for
     messages with multiple recipients using the same filtering
--- a/NEWS	Fri Mar 17 15:29:40 2017 -0700
+++ b/NEWS	Thu Mar 30 10:26:30 2017 -0700
@@ -1,3 +1,4 @@
+6.54 2017-03-30 document dmarc vs dnsbl dkim/spf; switch to . rather than " " for dkim impossible signer
 6.53 2017-03-17 suppress duplicate calls to acceptable_content(); redirect= in spf
 6.52 2017-03-09 document dkim/spf processing, handle a and a: elements
 6.51 2017-03-06 parse spf txt records for required dkim signers
--- a/configure.in	Fri Mar 17 15:29:40 2017 -0700
+++ b/configure.in	Thu Mar 30 10:26:30 2017 -0700
@@ -1,6 +1,6 @@
 
 AC_PREREQ(2.59)
-AC_INIT(dnsbl,6.53,carl@five-ten-sg.com)
+AC_INIT(dnsbl,6.54,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	Fri Mar 17 15:29:40 2017 -0700
+++ b/dnsbl.spec.in	Thu Mar 30 10:26:30 2017 -0700
@@ -3,7 +3,7 @@
 Summary:            Sendmail milter for spam control
 Name:               @PACKAGE@
 Version:            @VERSION@
-Release:            2%{?dist}
+Release:            1%{?dist}
 License:            GPLv3+
 Group:              System Environment/Daemons
 Source:             http://www.five-ten-sg.com/%{name}/packages/%{name}-%{version}.tar.gz
@@ -155,6 +155,10 @@
 
 
 %changelog
+* Thu Mar 30 2017 Carl Byington <carl@five-ten-sg.com> - 6.54-1
+- document dmarc vs dnsbl dkim/spf; switch to . rather than
+  " " for dkim impossible signer.
+
 * Fri Mar 17 2017 Carl Byington <carl@five-ten-sg.com> - 6.53-2
 - handle redirect= elements in spf txt records.
 
--- a/src/context.cpp	Fri Mar 17 15:29:40 2017 -0700
+++ b/src/context.cpp	Thu Mar 30 10:26:30 2017 -0700
@@ -1205,8 +1205,6 @@
 
 
 const char *CONTEXT::acceptable_content(recorder &memory, int score, int bulk, const char *queueid, string_set &signers, const char *from, mlfiPriv *priv, string& msg) {
-    DKIMP dk = find_dkim_from(from);
-
     for (string_set::iterator s=signers.begin(); s!=signers.end(); s++) {
         const char *st = find_dkim_signer(*s);
         // signed by a white listed signer
@@ -1216,6 +1214,7 @@
         }
     }
 
+    DKIMP dk = find_dkim_from(from);
     if (dk) {
         const char *st = dk->action;
         for (string_set::iterator s=signers.begin(); s!=signers.end(); s++) {
@@ -1246,11 +1245,13 @@
         }
         if (st == token_require_signed) {
             // not signed by a required signer, but maybe passes strong spf check
-            // only check spf if the list of required signers is not a single blank.
-            if (strcmp(dk->signer, " ") && resolve_spf(from, ntohl(priv->ip), priv)) {
+            // only check spf if the list of required signers is not a single dot.
+            if (strcmp(dk->signer, ".") && resolve_spf(from, ntohl(priv->ip), priv)) {
                 log(queueid, "spf pass for %s rather than required dkim signer", from);
                 return token_white;
             }
+            // todo - we could also check spf for the rfc5321 envelope from domain,
+            // if it is dmarc aligned (relaxed) with the rfc5322 header from domain.
             char buf[maxlen];
             snprintf(buf, sizeof(buf), "Mail rejected - not dkim signed by %s", dk->signer);
             msg = string(buf);
--- a/xml/Makefile.am	Fri Mar 17 15:29:40 2017 -0700
+++ b/xml/Makefile.am	Thu Mar 30 10:26:30 2017 -0700
@@ -1,6 +1,6 @@
 all: dnsbl
 	   cat header.xml  dnsbl >dnsbl.xml
-	   cat header.sgml dnsbl >dnsbl.sgml
+	   cat header.sgml dnsbl | grep -v personblurb >dnsbl.sgml
 	   rm -f ../html/*html
 	   rm -f ../html/*pdf
 	   xmlto        -o ../man  man   dnsbl.xml
--- a/xml/dnsbl.in	Fri Mar 17 15:29:40 2017 -0700
+++ b/xml/dnsbl.in	Thu Mar 30 10:26:30 2017 -0700
@@ -25,11 +25,12 @@
 
     <refentry id="@PACKAGE@.1">
         <refentryinfo>
-            <date>2017-03-07</date>
+            <date>2017-03-30</date>
             <author>
                 <firstname>Carl</firstname>
                 <surname>Byington</surname>
                 <affiliation><orgname>510 Software Group</orgname></affiliation>
+                <personblurb><para></para></personblurb>
             </author>
         </refentryinfo>
 
@@ -297,7 +298,7 @@
                 milter, then connections from clients that use SMTP AUTH are never
                 subject to greylisting. As part of this per-user greylisting, you need
                 to move the dnsblnogrey file from the config directory to something
-                like /var/dcc/userdirs/local/dnsblnogrey/whiteclnt so the dccifd will
+                like /var/dcc/userdirs/dnsblnogrey/whiteclnt so the dccifd will
                 properly ignore greylisting for those recipients that don't want it.
             </para>
         </refsect1>
@@ -389,11 +390,11 @@
                     user", and the dns lists are not checked.
                 </para></listitem>
                 <listitem><para>
-                    If the answer is white, mail to this recipient is accepted and the dns
-                    lists are not checked. However, if the envelope from domain name is
-                    listed in the current filtering context (or parents) dkim_from with
-                    "required_signed",
-                    we downgrade this to white answer to unknown.
+                    If the answer is white, 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.
+                    If the answer is still white, mail to this recipient is accepted and the dns
+                    lists are not checked.
                 </para></listitem>
                 <listitem><para>
                     If the answer is unknown, we don't reject yet, but the dns lists will be
@@ -533,6 +534,84 @@
             </para>
         </refsect1>
 
+        <refsect1 id='dmarc.1'>
+            <title>DMARC vs dkim_from require_signed</title>
+            <para>
+                Note that DNSBL does not implement rfc7489 DMARC. We do not look for
+                _dmarc.$DOMAIN txt records.
+            </para>
+            <para>
+                The restrictions imposed by require_signed are similar but not
+                identical to a DMARC reject policy with strict identifier alignment.
+                When doing SPF fallback, DMARC checks SPF based on the rfc5321
+                envelope from domain. DNSBL checks SPF based on the rfc5322 header
+                from domain.  DMARC does not allow mail from good.example.com to be
+                signed by trusted.example.net - which is a common case. Both Microsoft
+                Office365 and Google run mail for customer domains, but use DKIM
+                signing domains in onmicrosoft.com and gappssmtp.com, which are
+                unrelated to the customer domain. DMARC in the default relaxed
+                alignment mode allows evil.example.com to sign mail from
+                good.example.com. DNSBL specifies the exact list of acceptable signing
+                domains, rather than inferring it from child/parent relationships, or
+                using public
+                suffix lists to find the organizational domain.  We can block mail
+                from marketing.example.com while accepting mail from
+                billing.example.com, even if both are DKIM signed by example.com.
+            </para>
+            <para>
+                Suppose we have:
+<literallayout class="monospaced"><![CDATA[
+rfc5321 envelope from       = one@evil.example.com
+rfc5322 header from         = two@good.example.com
+authentication results      = dkim pass header.d=other.example.com
+_dmarc.good.example.com txt = "v=DMARC1; p=reject; adkim:s aspf:s"
+dkim_from {good.example.com require_signed other.example.com;}
+]]></literallayout>
+                DMARC would fail the strict identifier alignment. DNSBL allows
+                us to require DKIM signatures that are unrelated
+                to the rfc5322 header from, so we accept this message.
+            </para>
+            <para>
+                Suppose we have:
+<literallayout class="monospaced"><![CDATA[
+rfc5321 envelope from       = one@evil.example.com
+rfc5322 header from         = two@good.example.com
+authentication results      = dkim pass header.d=other.example.net
+_dmarc.good.example.com txt = "v=DMARC1; p=reject; adkim:r aspf:r"
+dkim_from {good.example.com require_signed other.example.net;}
+]]></literallayout>
+                DMARC would pass the relaxed spf identifier alignments,
+                and would check the evil.example.com spf record. If that
+                allowed the source ip, DMARC would accept the message.
+                DMARC would not check DKIM since example.com and example.net
+                do not pass even the relaxed identifer alignment requirement.
+                DNSBL allows us to require DKIM signatures that are not
+                related to the rfc5322 header from domain, so we accept
+                the message based on the DKIM signature and don't need to
+                fall back to SPF.
+            </para>
+            <para>
+                Suppose we have:
+<literallayout class="monospaced"><![CDATA[
+rfc5321 envelope from       = one@evil.example.com
+rfc5322 header from         = two@good.example.com
+authentication results      = dkim fail header.d=other.example.net
+_dmarc.good.example.com txt = "v=DMARC1; p=reject; adkim:r aspf:r"
+evil.example.com txt        = "v=spf1 ... including the source ip
+good.example.com txt        = "v=spf1 ... not including the source ip
+dkim_from {good.example.com require_signed other.example.net;}
+]]></literallayout>
+                DNSBL allows us to require DKIM signatures that are not
+                related to the rfc5322 header from domain. In this case
+                the signature fails, so we fall back to an SPF check.
+                We check SPF based on the rfc5322 header from, and
+                good.example.com does not allow the source ip, so we reject
+                this message.
+                DMARC would accept that message based on the SPF check
+                for evil.example.com
+            </para>
+        </refsect1>
+
         <refsect1 id='access.1'>
             <title>Sendmail access vs. DNSBL</title>
             <para>
@@ -690,11 +769,12 @@
 
     <refentry id="@PACKAGE@.conf.5">
         <refentryinfo>
-            <date>2017-03-07</date>
+            <date>2017-03-30</date>
             <author>
                 <firstname>Carl</firstname>
                 <surname>Byington</surname>
                 <affiliation><orgname>510 Software Group</orgname></affiliation>
+                <personblurb><para></para></personblurb>
             </author>
         </refentryinfo>