Another lame DSN heuristic. Block PTR cache poisoning attack.

This commit is contained in:
Stuart Gathman
2006-11-04 22:09:39 +00:00
parent a7e98f411e
commit dd0125b641
4 changed files with 45 additions and 125 deletions
+8 -1
View File
@@ -1,7 +1,14 @@
Generate DSNs according to RFC 3464
Parse incoming 3464 DSNs for "Action: failed" to recognize delayed
failures. This works regardless of Subject.
Get temperror policy from access file.
When training with spam, REJECT after data so that mistakenly blacklisted
senders at least get an error.
Reporting explanation for failure should show source of sender
Reporting explanation for failure should show source if sender
provided explanation.
Reports PROBATION even when rejecting message (works, but confusing in log).
+11 -2
View File
@@ -1,6 +1,9 @@
#!/usr/bin/env python
# A simple milter that has grown quite a bit.
# $Log$
# Revision 1.68 2006/10/04 03:46:01 customdesigned
# Fix defaults.
#
# Revision 1.67 2006/10/01 01:44:06 customdesigned
# case_sensitive_localpart option, more delayed bounce heuristics,
# optional smart_alias section.
@@ -1278,13 +1281,19 @@ class bmsMilter(Milter.Milter):
# if confirmed by finding our signed Message-ID,
# original sender (encoded in Message-ID) is blacklisted
elif lname == 'from' and val.lower().startswith('postmaster@'):
# Yes, if From header comes last, this might not help much.
# But this is a heuristic - if MTAs would send proper DSNs in
# the first place, none of this would be needed.
self.is_bounce = True
# check for invalid message id
if lname == 'message-id' and len(val) < 4:
elif lname == 'message-id' and len(val) < 4:
self.log('REJECT: %s: %s' % (name,val))
return Milter.REJECT
# check for common bulk mailers
if lname == 'x-mailer':
elif lname == 'x-mailer':
mailer = val.lower()
if mailer in ('direct email','calypso','mail bomber') \
or mailer.find('optin') >= 0:
+1 -1
View File
@@ -92,7 +92,7 @@ cat >$RPM_BUILD_ROOT/etc/cron.daily/milter <<'EOF'
find /var/log/milter/save -mtime +7 | xargs $R rm
# work around memory leak
/etc/init.d/milter restart
/etc/init.d/milter condrestart
EOF
chmod a+x $RPM_BUILD_ROOT/etc/cron.daily/milter
+24 -120
View File
@@ -47,124 +47,8 @@ For news, bugfixes, etc. visit the home page for this implementation at
# Development taken over by Stuart Gathman <stuart@bmsi.com>.
#
# $Log$
# Revision 1.105 2006/10/07 22:06:28 kitterma
# Pass strict status to DNSLookup - will be needed for TCP failover.
#
# Revision 1.104 2006/10/07 21:59:37 customdesigned
# long/empty label tests and fix.
#
# Revision 1.103 2006/10/07 18:16:20 customdesigned
# Add tests for and fix RE_TOPLAB.
#
# Revision 1.102 2006/10/05 13:57:15 customdesigned
# Remove isSPF and make missing space after version tag a warning.
#
# Revision 1.101 2006/10/05 13:39:11 customdesigned
# SPF version tag is case insensitive.
#
# Revision 1.100 2006/10/04 02:14:04 customdesigned
# Remove incomplete saving of result. Was messing up bmsmilter. Would
# be useful if done consistently - and disabled when passing spf= to check().
#
# Revision 1.99 2006/10/03 21:00:26 customdesigned
# Correct fat fingered merge error.
#
# Revision 1.98 2006/10/03 17:35:45 customdesigned
# Provide python inet_ntop and inet_pton when not socket.has_ipv6
#
# Revision 1.97 2006/10/02 17:10:13 customdesigned
# Test and fix for uppercase macros.
#
# Revision 1.96 2006/10/01 01:27:54 customdesigned
# Switch to pymilter lax processing convention:
# Always return strict result, extended result in q.perm_error.ext
#
# Revision 1.95 2006/09/30 22:53:44 customdesigned
# Fix getp to obey SHOULDs in RFC.
#
# Revision 1.94 2006/09/30 22:23:25 customdesigned
# p macro tests and fixes
#
# Revision 1.93 2006/09/30 20:57:06 customdesigned
# Remove generator expression for compatibility with python2.3.
#
# Revision 1.92 2006/09/30 19:52:52 customdesigned
# Removed redundant flag and unneeded global.
#
# Revision 1.91 2006/09/30 19:37:49 customdesigned
# Missing L
#
# Revision 1.90 2006/09/30 19:29:58 customdesigned
# pydns returns AAAA RR as binary string
#
# Revision 1.89 2006/09/29 20:23:11 customdesigned
# Optimize cidrmatch
#
# Revision 1.88 2006/09/29 19:44:10 customdesigned
# Fix ptr with ip6 for harsh mode.
#
# Revision 1.87 2006/09/29 19:26:53 customdesigned
# Add PTR tests and fix ip6 ptr
#
# Revision 1.86 2006/09/29 17:55:22 customdesigned
# Pass ip6 tests
#
# Revision 1.85 2006/09/29 15:58:02 customdesigned
# Pass self test on non IP6 python.
# PTR accepts no cidr.
#
# Revision 1.83 2006/09/27 18:09:40 kitterma
# Converted spf.check to return pre-MARID result codes for drop in
# compatibility with pySPF 1.6/1.7. Added new procedure, spf.check2 to
# return RFC4408 results in a two part answer (result, explanation).
# This is the external API for pySPF 2.0. No longer any need to branch
# for 'classic' and RFC compliant pySPF libraries.
#
# Revision 1.82 2006/09/27 18:02:21 kitterma
# Converted max MX limit to ambiguity warning for validator.
#
# Revision 1.81 2006/09/27 17:38:14 kitterma
# Updated initial comments and moved pre-1.7 changes to spf_changelog.
#
# Revision 1.80 2006/09/27 17:33:53 kitterma
# Fixed indentation error in check0.
#
# Revision 1.79 2006/09/26 18:05:44 kitterma
# Removed unused receiver policy definitions.
#
# Revision 1.78 2006/09/26 16:15:50 kitterma
# added additional IP4 and CIDR validation tests - no code changes.
#
# Revision 1.77 2006/09/25 19:42:32 customdesigned
# Fix unknown macro sentinel
#
# Revision 1.76 2006/09/25 19:10:40 customdesigned
# Fix exp= error and add another failing test.
#
# Revision 1.75 2006/09/25 02:02:30 kitterma
# Fixed redirect-cancels-exp test suite failure.
#
# Revision 1.74 2006/09/24 04:04:08 kitterma
# Implemented check for macro 'c' - Macro unimplimented.
#
# Revision 1.73 2006/09/24 02:08:35 kitterma
# Fixed invalid-macro-char test failure.
#
# Revision 1.72 2006/09/23 05:45:52 kitterma
# Fixed domain-name-truncation test failure
#
# Revision 1.71 2006/09/22 01:02:54 kitterma
# pySPF correction for nolocalpart in rfc4408-tests.yml failed, 4.3/2.
# Added comments to testspf.py on where to get YAML.
#
# Revision 1.70 2006/09/18 02:13:27 kitterma
# Worked through a large number of pylint issues - all 4 spaces, not a mix
# of 4 spaces, 2 spaces, and tabs. Caught a few minor errors in the process.
# All built in tests still pass.
#
# Revision 1.69 2006/09/17 18:44:25 kitterma
# Fixed validation mode only crash bug when rDNS check had no PTR record
#
# Revision 1.107 2006/11/04 21:58:12 customdesigned
# Prevent cache poisoning by bogus additional RRs in PTR DNS response.
#
# See spf_changelog.txt for earlier changes.
@@ -303,8 +187,9 @@ MAX_CNAME = 10 # analogous interpretation to MAX_PTR
MAX_RECURSION = 20
ALL_MECHANISMS = ('a', 'mx', 'ptr', 'exists', 'include', 'ip4', 'ip6', 'all')
COMMON_MISTAKES = { 'prt': 'ptr', 'ip': 'ip4', 'ipv4': 'ip4', 'ipv6': 'ip6' }
COMMON_MISTAKES = {
'prt': 'ptr', 'ip': 'ip4', 'ipv4': 'ip4', 'ipv6': 'ip6', 'all.': 'all'
}
#If harsh processing, for the validator, is invoked, warn if results
#likely deviate from the publishers intention.
@@ -1163,6 +1048,22 @@ class query(object):
"""Get a list of domain names for an IP address."""
return self.dns('%s.%s.arpa'%(reverse_dots(i),self.v), 'PTR')
# We have to be careful which additional DNS RRs we cache. For
# instance, PTR records are controlled by the connecting IP, and they
# could poison our local cache with bogus A and MX records.
SAFE2CACHE = {
('MX','A'): None,
('MX','MX'): None,
('CNAME','A'): None,
('CNAME','CNAME'): None,
('A','A'): None,
('AAAA','AAAA'): None,
('PTR','PTR'): None,
('TXT','TXT'): None,
('SPF','SPF'): None
}
def dns(self, name, qtype, cnames=None):
"""DNS query.
@@ -1179,10 +1080,13 @@ class query(object):
"""
result = self.cache.get( (name, qtype) )
cname = None
if not result:
safe2cache = query.SAFE2CACHE
for k, v in DNSLookup(name, qtype, self.strict):
if k == (name, 'CNAME'):
cname = v
if (qtype,k[1]) in safe2cache:
self.cache.setdefault(k, []).append(v)
result = self.cache.get( (name, qtype), [])
if not result and cname: