Extend DMARC case insensitivity to CNAMEs.

parent 0efc831a
......@@ -207,17 +207,17 @@ def is_reject_or_quarantine(mlist, email, dmarc_domain, org=False):
# Check all the TXT records returned by DNS. Keep track of the CNAMEs for
# checking later on. Ignore any other non-TXT records.
for txt_rec in txt_recs.response.answer:
# Don't be fooled by an answer with uppercase in the name.
name = txt_rec.name.to_text().lower()
if txt_rec.rdtype == dns.rdatatype.CNAME:
cnames[txt_rec.name.to_text()] = (
cnames[name] = (
txt_rec.items[0].target.to_text())
if txt_rec.rdtype != dns.rdatatype.TXT:
continue
result = EMPTYSTRING.join(
str(record, encoding='utf-8')
for record in txt_rec.items[0].strings)
name = txt_rec.name.to_text()
# Don't be fooled by an answer with uppercase in the name.
results_by_name.setdefault(name.lower(), []).append(result)
results_by_name.setdefault(name, []).append(result)
expands = list(want_names)
seen = set(expands)
while expands:
......
......@@ -51,7 +51,9 @@ def get_dns_resolver(
cmult=False,
cloop=False,
cmiss=False,
ucase=False):
ucase=False,
cnmok=False,
cnuc=False):
"""Create a dns.resolver.Resolver mock.
This is used to return a predictable response to a _dmarc query. It
......@@ -143,6 +145,32 @@ def get_dns_resolver(
self.answer = [Ans_e(), Ans_e(rdata=b'v=DMARC1; p=none;')]
elif ucase:
self.answer = [Ans_e(name='_dmarc.EXAMPLE.biz.')]
elif cnmok:
self.answer = [
Ans_e(
rtype=CNAME,
name='_dmarc.example.biz.',
cname='_dmarc.example.net.'
),
Ans_e(
rtype=TXT,
name='_dmarc.example.net.',
rdata=b'v=DMARC1; p=reject;'
),
]
elif cnuc:
self.answer = [
Ans_e(
rtype=CNAME,
name='_dmarc.EXAMPLE.biz.',
cname='_dmarc.example.net.'
),
Ans_e(
rtype=TXT,
name='_dmarc.example.net.',
rdata=b'v=DMARC1; p=reject;'
),
]
else:
self.answer = [Ans_e()]
......@@ -209,6 +237,21 @@ class TestDMARCRules(TestCase):
dmarc.get_organizational_domain('ssub.sub.city.kobe.jp'),
'city.kobe.jp')
def test_straightforward_cname(self):
# Test that we can recognize an answer with case mismatch in the
# domain.
mlist = create_list('[email protected]')
# Use action reject. The rule only hits on reject and discard.
mlist.dmarc_mitigate_action = DMARCMitigateAction.reject
msg = mfs("""\
From: [email protected]
To: [email protected]
""")
rule = dmarc.DMARCMitigation()
with get_dns_resolver(cnmok=True):
self.assertTrue(rule.check(mlist, msg, {}))
def test_uppercase_in_returned_domain(self):
# Test that we can recognize an answer with case mismatch in the
# domain.
......@@ -224,6 +267,21 @@ To: [email protected]
with get_dns_resolver(ucase=True):
self.assertTrue(rule.check(mlist, msg, {}))
def test_uppercase_in_returned_cname(self):
# Test that we can recognize an answer with case mismatch in the
# domain.
mlist = create_list('[email protected]')
# Use action reject. The rule only hits on reject and discard.
mlist.dmarc_mitigate_action = DMARCMitigateAction.reject
msg = mfs("""\
From: [email protected]
To: [email protected]
""")
rule = dmarc.DMARCMitigation()
with get_dns_resolver(cnuc=True):
self.assertTrue(rule.check(mlist, msg, {}))
def test_no_at_sign_in_from_address(self):
# If there's no @ sign in the From: address, the rule can't hit.
mlist = create_list('[email protected]')
......
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