Commit 378be176 authored by Mark Sapiro's avatar Mark Sapiro

Updated the DMARC rule to treat certain DNS lookup failures as needing mitigation.

parent a3bfbe1d
......@@ -30,6 +30,8 @@ Bugs
additions to the ``-bounces`` and ``-confirm`` addresses. (Closes #401)
* Address confirmation notices are now properly encoded in the character set
of the list's preferred_language. (Closes #395 and #413)
* Thanks to Jim Popovitch, certain failures in DNS lookups of DMARC policy
will now result in mitigations being applied. (Closes #415)
Command line
------------
......
......@@ -183,11 +183,23 @@ def is_reject_or_quarantine(mlist, email, dmarc_domain, org=False):
txt_recs = resolver.query(dmarc_domain, dns.rdatatype.TXT)
except (dns.resolver.NXDOMAIN, dns.resolver.NoAnswer):
return KEEP_LOOKING
except (dns.resolver.NoNameservers):
elog.error(
'DNSException: No Nameservers available for %s (%s).',
email, dmarc_domain)
# Typically this means a dnssec validation error. Clients that don't
# perform validation *may* successfully see a _dmarc RR whereas a
# validating mailman server wont see the _dmarc RR. We should mitigate
# this email to be safe.
return True
except DNSException as error:
elog.error(
'DNSException: Unable to query DMARC policy for %s (%s). %s',
email, dmarc_domain, error.__doc__)
return KEEP_LOOKING
# While we can't be sure what caused the error, there is potentially
# a DMARC policy record that we missed and that a receiver of the mail
# might see. Thus, we should err on the side of caution and mitigate.
return True
# Be as robust as possible in parsing the result.
results_by_name = {}
cnames = {}
......
......@@ -24,7 +24,7 @@ from contextlib import ExitStack
from datetime import timedelta
from dns.exception import DNSException
from dns.rdatatype import CNAME, TXT
from dns.resolver import NXDOMAIN, NoAnswer
from dns.resolver import NXDOMAIN, NoAnswer, NoNameservers
from http.server import BaseHTTPRequestHandler, HTTPServer
from lazr.config import as_timedelta
from mailman.app.lifecycle import create_list
......@@ -156,6 +156,8 @@ def get_dns_resolver(
raise NoAnswer
if dparts[2] == 'info':
raise DNSException('no internet')
if dparts[2] == 'home':
raise NoNameservers
if dparts[1] != 'example' or dparts[2] != 'biz':
raise NXDOMAIN
self.response = Answer()
......@@ -227,7 +229,7 @@ To: ant@example.com
mark = LogFileMark('mailman.error')
rule = dmarc.DMARCMitigation()
with get_dns_resolver():
self.assertFalse(rule.check(mlist, msg, {}))
self.assertTrue(rule.check(mlist, msg, {}))
line = mark.readline()
self.assertEqual(
line[-144:],
......@@ -235,6 +237,25 @@ To: ant@example.com
'anne@example.info (_dmarc.example.info). '
'Abstract base class shared by all dnspython exceptions.\n')
def test_dmarc_nonameservers_exception(self):
mlist = create_list('ant@example.com')
# Use action reject. The rule only hits on reject and discard.
mlist.dmarc_mitigate_action = DMARCMitigateAction.reject
msg = mfs("""\
From: anne@example.home
To: ant@example.com
""")
mark = LogFileMark('mailman.error')
rule = dmarc.DMARCMitigation()
with get_dns_resolver():
self.assertTrue(rule.check(mlist, msg, {}))
line = mark.readline()
self.assertEqual(
line[-84:],
'DNSException: No Nameservers available for '
'anne@example.home (_dmarc.example.home).\n')
def test_cname_wrong_txt_name(self):
mlist = create_list('ant@example.com')
# Use action reject. The rule only hits on reject and discard.
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment