Treat fail like softfail for selected (braindead) domains.
Treat mail according to extended processing results, but report any PermError that would officially result via DSN.
This commit is contained in:
+7
-5
@@ -103,7 +103,7 @@ def send_dsn(mailfrom,receiver,msg=None):
|
|||||||
q = spf.query(None,None,None)
|
q = spf.query(None,None,None)
|
||||||
mxlist = q.dns(domain,'MX')
|
mxlist = q.dns(domain,'MX')
|
||||||
if not mxlist:
|
if not mxlist:
|
||||||
mxlist = (0,domain),
|
mxlist = (0,domain), # fallback to A record when no MX
|
||||||
else:
|
else:
|
||||||
mxlist.sort()
|
mxlist.sort()
|
||||||
smtp = smtplib.SMTP()
|
smtp = smtplib.SMTP()
|
||||||
@@ -151,13 +151,13 @@ def create_msg(q,rcptlist,origmsg=None,template=None):
|
|||||||
connectip = q.i
|
connectip = q.i
|
||||||
receiver = q.r
|
receiver = q.r
|
||||||
sender_domain = q.o
|
sender_domain = q.o
|
||||||
|
result = q.result
|
||||||
|
perm_error = q.perm_error
|
||||||
rcpt = '\n\t'.join(rcptlist)
|
rcpt = '\n\t'.join(rcptlist)
|
||||||
try: subject = origmsg['Subject']
|
try: subject = origmsg['Subject']
|
||||||
except: subject = '(none)'
|
except: subject = '(none)'
|
||||||
try:
|
try:
|
||||||
spf_result = origmsg['Received-SPF']
|
spf_result = origmsg['Received-SPF']
|
||||||
if not spf_result.startswith('softfail'):
|
|
||||||
spf_result = None
|
|
||||||
except: spf_result = None
|
except: spf_result = None
|
||||||
|
|
||||||
msg = Message()
|
msg = Message()
|
||||||
@@ -168,8 +168,10 @@ def create_msg(q,rcptlist,origmsg=None,template=None):
|
|||||||
msg.set_type('text/plain')
|
msg.set_type('text/plain')
|
||||||
|
|
||||||
if not template:
|
if not template:
|
||||||
if spf_result: template = softfail_msg
|
if spf_result and spf_result.startswith('softfail'):
|
||||||
else: template = nospf_msg
|
template = softfail_msg
|
||||||
|
else:
|
||||||
|
template = nospf_msg
|
||||||
hdrs,body = template.split('\n',1)
|
hdrs,body = template.split('\n',1)
|
||||||
for ln in hdrs.splitlines():
|
for ln in hdrs.splitlines():
|
||||||
name,val = ln.split(':',1)
|
name,val = ln.split(':',1)
|
||||||
|
|||||||
@@ -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.20 2005/08/02 18:04:35 customdesigned
|
||||||
|
# Keep screened honeypot mail, but optionally discard honeypot only mail.
|
||||||
|
#
|
||||||
# Revision 1.19 2005/07/20 03:30:04 customdesigned
|
# Revision 1.19 2005/07/20 03:30:04 customdesigned
|
||||||
# Check pydspam version for honeypot, include latest pyspf changes.
|
# Check pydspam version for honeypot, include latest pyspf changes.
|
||||||
#
|
#
|
||||||
@@ -312,6 +315,7 @@ srs_reject_spoofed = False
|
|||||||
srs_domain = None
|
srs_domain = None
|
||||||
spf_reject_neutral = ()
|
spf_reject_neutral = ()
|
||||||
spf_accept_softfail = ()
|
spf_accept_softfail = ()
|
||||||
|
spf_accept_fail = ()
|
||||||
spf_best_guess = False
|
spf_best_guess = False
|
||||||
spf_reject_noptr = False
|
spf_reject_noptr = False
|
||||||
multiple_bounce_recipients = True
|
multiple_bounce_recipients = True
|
||||||
@@ -466,11 +470,12 @@ def read_config(list):
|
|||||||
|
|
||||||
# spf section
|
# spf section
|
||||||
global spf_reject_neutral,spf_best_guess,SRS,spf_reject_noptr
|
global spf_reject_neutral,spf_best_guess,SRS,spf_reject_noptr
|
||||||
global spf_accept_softfail
|
global spf_accept_softfail,spf_accept_fail
|
||||||
if spf:
|
if spf:
|
||||||
spf.DELEGATE = cp.getdefault('spf','delegate')
|
spf.DELEGATE = cp.getdefault('spf','delegate')
|
||||||
spf_reject_neutral = cp.getlist('spf','reject_neutral')
|
spf_reject_neutral = cp.getlist('spf','reject_neutral')
|
||||||
spf_accept_softfail = cp.getlist('spf','accept_softfail')
|
spf_accept_softfail = cp.getlist('spf','accept_softfail')
|
||||||
|
spf_accept_fail = cp.getlist('spf','accept_fail')
|
||||||
spf_best_guess = cp.getboolean('spf','best_guess')
|
spf_best_guess = cp.getboolean('spf','best_guess')
|
||||||
spf_reject_noptr = cp.getboolean('spf','reject_noptr')
|
spf_reject_noptr = cp.getboolean('spf','reject_noptr')
|
||||||
srs_config = cp.getdefault('srs','config')
|
srs_config = cp.getdefault('srs','config')
|
||||||
@@ -698,11 +703,17 @@ class bmsMilter(Milter.Milter):
|
|||||||
t = parse_addr(self.mailfrom)
|
t = parse_addr(self.mailfrom)
|
||||||
if len(t) == 2: t[1] = t[1].lower()
|
if len(t) == 2: t[1] = t[1].lower()
|
||||||
receiver = self.receiver
|
receiver = self.receiver
|
||||||
q = spf.query(self.connectip,'@'.join(t),self.hello_name,receiver=receiver)
|
q = spf.query(self.connectip,'@'.join(t),self.hello_name,receiver=receiver,
|
||||||
|
strict=False)
|
||||||
q.set_default_explanation(
|
q.set_default_explanation(
|
||||||
'SPF fail: see http://openspf.com/why.html?sender=%s&ip=%s' % (q.s,q.i))
|
'SPF fail: see http://openspf.com/why.html?sender=%s&ip=%s' % (q.s,q.i))
|
||||||
res,code,txt = q.check()
|
res,code,txt = q.check()
|
||||||
if res in ('none', 'softfail'):
|
if res == 'unknown' and q.perm_error:
|
||||||
|
q.result = res
|
||||||
|
self.cbv_needed = q # report SPF syntax error to sender
|
||||||
|
res,code,txt = q.perm_error.ext # extended (lax processing) result
|
||||||
|
txt = 'EXT: ' + txt
|
||||||
|
if res in ('none','softfail','deny','fail'):
|
||||||
if self.mailfrom != '<>':
|
if self.mailfrom != '<>':
|
||||||
# check hello name via spf
|
# check hello name via spf
|
||||||
h = spf.query(self.connectip,'',self.hello_name,receiver=receiver)
|
h = spf.query(self.connectip,'',self.hello_name,receiver=receiver)
|
||||||
@@ -724,7 +735,6 @@ class bmsMilter(Milter.Milter):
|
|||||||
#self.log('SPF: no record published, guessing')
|
#self.log('SPF: no record published, guessing')
|
||||||
q.set_default_explanation(
|
q.set_default_explanation(
|
||||||
'SPF guess: see http://spf.pobox.com/why.html')
|
'SPF guess: see http://spf.pobox.com/why.html')
|
||||||
q.strict = False
|
|
||||||
# best_guess should not result in fail
|
# best_guess should not result in fail
|
||||||
if self.missing_ptr:
|
if self.missing_ptr:
|
||||||
# ignore dynamic PTR for best guess
|
# ignore dynamic PTR for best guess
|
||||||
@@ -749,6 +759,10 @@ class bmsMilter(Milter.Milter):
|
|||||||
q.result = res
|
q.result = res
|
||||||
self.cbv_needed = q
|
self.cbv_needed = q
|
||||||
if res in ('deny', 'fail'):
|
if res in ('deny', 'fail'):
|
||||||
|
if hres == 'pass' and q.o in spf_accept_fail:
|
||||||
|
q.result = res
|
||||||
|
self.cbv_needed = q
|
||||||
|
else:
|
||||||
self.log('REJECT: SPF %s %i %s' % (res,code,txt))
|
self.log('REJECT: SPF %s %i %s' % (res,code,txt))
|
||||||
self.setreply(str(code),'5.7.1',txt)
|
self.setreply(str(code),'5.7.1',txt)
|
||||||
# A proper SPF fail error message would read:
|
# A proper SPF fail error message would read:
|
||||||
@@ -1206,8 +1220,10 @@ class bmsMilter(Milter.Milter):
|
|||||||
else:
|
else:
|
||||||
self.log('CBV:',sender)
|
self.log('CBV:',sender)
|
||||||
try:
|
try:
|
||||||
if q.result == 'softfail':
|
if q.result in ('softfail','fail','deny'):
|
||||||
template = file('softfail.txt').read()
|
template = file('softfail.txt').read()
|
||||||
|
elif q.result == 'unknown':
|
||||||
|
template = file('permerror.txt').read()
|
||||||
else:
|
else:
|
||||||
template = file('strike3.txt').read()
|
template = file('strike3.txt').read()
|
||||||
except IOError: template = None
|
except IOError: template = None
|
||||||
|
|||||||
@@ -85,6 +85,9 @@ reject_spoofed = 0
|
|||||||
;reject_noptr = 0
|
;reject_noptr = 0
|
||||||
# always accept softfail from these domains, or send DSN otherwise
|
# always accept softfail from these domains, or send DSN otherwise
|
||||||
;accept_softfail = bounces.amazon.com
|
;accept_softfail = bounces.amazon.com
|
||||||
|
# treat fail from these domains like softfail: because their SPF record
|
||||||
|
# or an important sender is screwed up. Must have valid HELO, however.
|
||||||
|
;accept_fail = custhelp.com
|
||||||
|
|
||||||
# features intended to clean up outgoing mail
|
# features intended to clean up outgoing mail
|
||||||
[scrub]
|
[scrub]
|
||||||
|
|||||||
@@ -166,6 +166,9 @@ rm -rf $RPM_BUILD_ROOT
|
|||||||
/usr/share/sendmail-cf/hack/rhsbl.m4
|
/usr/share/sendmail-cf/hack/rhsbl.m4
|
||||||
|
|
||||||
%changelog
|
%changelog
|
||||||
|
* Fri Jul 15 2005 Stuart Gathman <stuart@bmsi.com> 0.8.3-1
|
||||||
|
- Keep screened honeypot mail, but optionally discard honeypot only mail.
|
||||||
|
- spf_accept_fail option for braindead SPF senders
|
||||||
* Fri Jul 15 2005 Stuart Gathman <stuart@bmsi.com> 0.8.2-4
|
* Fri Jul 15 2005 Stuart Gathman <stuart@bmsi.com> 0.8.2-4
|
||||||
- Limit each CNAME chain independently like PTR and MX
|
- Limit each CNAME chain independently like PTR and MX
|
||||||
* Fri Jul 15 2005 Stuart Gathman <stuart@bmsi.com> 0.8.2-3
|
* Fri Jul 15 2005 Stuart Gathman <stuart@bmsi.com> 0.8.2-3
|
||||||
|
|||||||
@@ -0,0 +1,31 @@
|
|||||||
|
Subject: Critical SPF configuration error
|
||||||
|
|
||||||
|
This is an automatically generated Delivery Status Notification.
|
||||||
|
|
||||||
|
THIS IS A WARNING MESSAGE ONLY.
|
||||||
|
|
||||||
|
YOU DO *NOT* NEED TO RESEND YOUR MESSAGE.
|
||||||
|
|
||||||
|
Delivery to the following recipients has been delayed.
|
||||||
|
|
||||||
|
%(rcpt)s
|
||||||
|
|
||||||
|
Subject: %(subject)s
|
||||||
|
|
||||||
|
Your spf record has a permanent error. The error was:
|
||||||
|
|
||||||
|
%(perm_error)s
|
||||||
|
|
||||||
|
We will reinterpret your record using "lax" processing heuristics
|
||||||
|
which may result in your mail being accepted anyway. But you or your
|
||||||
|
mail administrator need to fix your SPF record as soon as possible.
|
||||||
|
|
||||||
|
We are sending you this message to alert you to the fact that
|
||||||
|
you have problems with your email configuration.
|
||||||
|
|
||||||
|
If you need further assistance, please do not hesitate to
|
||||||
|
contact me again.
|
||||||
|
|
||||||
|
Kind regards,
|
||||||
|
|
||||||
|
postmaster@%(receiver)s
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
Subject: SPF softfail (POSSIBLE FORGERY)
|
Subject: SPF %(result)s (POSSIBLE FORGERY)
|
||||||
|
|
||||||
This is an automatically generated Delivery Status Notification.
|
This is an automatically generated Delivery Status Notification.
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user