Another lame DSN heuristic. Block PTR cache poisoning attack.
This commit is contained in:
@@ -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
|
When training with spam, REJECT after data so that mistakenly blacklisted
|
||||||
senders at least get an error.
|
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.
|
provided explanation.
|
||||||
|
|
||||||
Reports PROBATION even when rejecting message (works, but confusing in log).
|
Reports PROBATION even when rejecting message (works, but confusing in log).
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
# A simple milter that has grown quite a bit.
|
# A simple milter that has grown quite a bit.
|
||||||
# $Log$
|
# $Log$
|
||||||
|
# Revision 1.68 2006/10/04 03:46:01 customdesigned
|
||||||
|
# Fix defaults.
|
||||||
|
#
|
||||||
# Revision 1.67 2006/10/01 01:44:06 customdesigned
|
# Revision 1.67 2006/10/01 01:44:06 customdesigned
|
||||||
# case_sensitive_localpart option, more delayed bounce heuristics,
|
# case_sensitive_localpart option, more delayed bounce heuristics,
|
||||||
# optional smart_alias section.
|
# optional smart_alias section.
|
||||||
@@ -1278,13 +1281,19 @@ class bmsMilter(Milter.Milter):
|
|||||||
# if confirmed by finding our signed Message-ID,
|
# if confirmed by finding our signed Message-ID,
|
||||||
# original sender (encoded in Message-ID) is blacklisted
|
# 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
|
# 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))
|
self.log('REJECT: %s: %s' % (name,val))
|
||||||
return Milter.REJECT
|
return Milter.REJECT
|
||||||
|
|
||||||
# check for common bulk mailers
|
# check for common bulk mailers
|
||||||
if lname == 'x-mailer':
|
elif lname == 'x-mailer':
|
||||||
mailer = val.lower()
|
mailer = val.lower()
|
||||||
if mailer in ('direct email','calypso','mail bomber') \
|
if mailer in ('direct email','calypso','mail bomber') \
|
||||||
or mailer.find('optin') >= 0:
|
or mailer.find('optin') >= 0:
|
||||||
|
|||||||
+1
-1
@@ -92,7 +92,7 @@ cat >$RPM_BUILD_ROOT/etc/cron.daily/milter <<'EOF'
|
|||||||
|
|
||||||
find /var/log/milter/save -mtime +7 | xargs $R rm
|
find /var/log/milter/save -mtime +7 | xargs $R rm
|
||||||
# work around memory leak
|
# work around memory leak
|
||||||
/etc/init.d/milter restart
|
/etc/init.d/milter condrestart
|
||||||
EOF
|
EOF
|
||||||
chmod a+x $RPM_BUILD_ROOT/etc/cron.daily/milter
|
chmod a+x $RPM_BUILD_ROOT/etc/cron.daily/milter
|
||||||
|
|
||||||
|
|||||||
@@ -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>.
|
# Development taken over by Stuart Gathman <stuart@bmsi.com>.
|
||||||
#
|
#
|
||||||
# $Log$
|
# $Log$
|
||||||
# Revision 1.105 2006/10/07 22:06:28 kitterma
|
# Revision 1.107 2006/11/04 21:58:12 customdesigned
|
||||||
# Pass strict status to DNSLookup - will be needed for TCP failover.
|
# Prevent cache poisoning by bogus additional RRs in PTR DNS response.
|
||||||
#
|
|
||||||
# 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
|
|
||||||
#
|
|
||||||
#
|
#
|
||||||
# See spf_changelog.txt for earlier changes.
|
# See spf_changelog.txt for earlier changes.
|
||||||
|
|
||||||
@@ -303,8 +187,9 @@ MAX_CNAME = 10 # analogous interpretation to MAX_PTR
|
|||||||
MAX_RECURSION = 20
|
MAX_RECURSION = 20
|
||||||
|
|
||||||
ALL_MECHANISMS = ('a', 'mx', 'ptr', 'exists', 'include', 'ip4', 'ip6', 'all')
|
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
|
#If harsh processing, for the validator, is invoked, warn if results
|
||||||
#likely deviate from the publishers intention.
|
#likely deviate from the publishers intention.
|
||||||
@@ -1163,6 +1048,22 @@ class query(object):
|
|||||||
"""Get a list of domain names for an IP address."""
|
"""Get a list of domain names for an IP address."""
|
||||||
return self.dns('%s.%s.arpa'%(reverse_dots(i),self.v), 'PTR')
|
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):
|
def dns(self, name, qtype, cnames=None):
|
||||||
"""DNS query.
|
"""DNS query.
|
||||||
|
|
||||||
@@ -1179,10 +1080,13 @@ class query(object):
|
|||||||
"""
|
"""
|
||||||
result = self.cache.get( (name, qtype) )
|
result = self.cache.get( (name, qtype) )
|
||||||
cname = None
|
cname = None
|
||||||
|
|
||||||
if not result:
|
if not result:
|
||||||
|
safe2cache = query.SAFE2CACHE
|
||||||
for k, v in DNSLookup(name, qtype, self.strict):
|
for k, v in DNSLookup(name, qtype, self.strict):
|
||||||
if k == (name, 'CNAME'):
|
if k == (name, 'CNAME'):
|
||||||
cname = v
|
cname = v
|
||||||
|
if (qtype,k[1]) in safe2cache:
|
||||||
self.cache.setdefault(k, []).append(v)
|
self.cache.setdefault(k, []).append(v)
|
||||||
result = self.cache.get( (name, qtype), [])
|
result = self.cache.get( (name, qtype), [])
|
||||||
if not result and cname:
|
if not result and cname:
|
||||||
|
|||||||
Reference in New Issue
Block a user