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)
|
||||
mxlist = q.dns(domain,'MX')
|
||||
if not mxlist:
|
||||
mxlist = (0,domain),
|
||||
mxlist = (0,domain), # fallback to A record when no MX
|
||||
else:
|
||||
mxlist.sort()
|
||||
smtp = smtplib.SMTP()
|
||||
@@ -151,13 +151,13 @@ def create_msg(q,rcptlist,origmsg=None,template=None):
|
||||
connectip = q.i
|
||||
receiver = q.r
|
||||
sender_domain = q.o
|
||||
result = q.result
|
||||
perm_error = q.perm_error
|
||||
rcpt = '\n\t'.join(rcptlist)
|
||||
try: subject = origmsg['Subject']
|
||||
except: subject = '(none)'
|
||||
try:
|
||||
spf_result = origmsg['Received-SPF']
|
||||
if not spf_result.startswith('softfail'):
|
||||
spf_result = None
|
||||
except: spf_result = None
|
||||
|
||||
msg = Message()
|
||||
@@ -168,8 +168,10 @@ def create_msg(q,rcptlist,origmsg=None,template=None):
|
||||
msg.set_type('text/plain')
|
||||
|
||||
if not template:
|
||||
if spf_result: template = softfail_msg
|
||||
else: template = nospf_msg
|
||||
if spf_result and spf_result.startswith('softfail'):
|
||||
template = softfail_msg
|
||||
else:
|
||||
template = nospf_msg
|
||||
hdrs,body = template.split('\n',1)
|
||||
for ln in hdrs.splitlines():
|
||||
name,val = ln.split(':',1)
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
#!/usr/bin/env python
|
||||
# A simple milter that has grown quite a bit.
|
||||
# $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
|
||||
# Check pydspam version for honeypot, include latest pyspf changes.
|
||||
#
|
||||
@@ -312,6 +315,7 @@ srs_reject_spoofed = False
|
||||
srs_domain = None
|
||||
spf_reject_neutral = ()
|
||||
spf_accept_softfail = ()
|
||||
spf_accept_fail = ()
|
||||
spf_best_guess = False
|
||||
spf_reject_noptr = False
|
||||
multiple_bounce_recipients = True
|
||||
@@ -466,11 +470,12 @@ def read_config(list):
|
||||
|
||||
# spf section
|
||||
global spf_reject_neutral,spf_best_guess,SRS,spf_reject_noptr
|
||||
global spf_accept_softfail
|
||||
global spf_accept_softfail,spf_accept_fail
|
||||
if spf:
|
||||
spf.DELEGATE = cp.getdefault('spf','delegate')
|
||||
spf_reject_neutral = cp.getlist('spf','reject_neutral')
|
||||
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_reject_noptr = cp.getboolean('spf','reject_noptr')
|
||||
srs_config = cp.getdefault('srs','config')
|
||||
@@ -698,11 +703,17 @@ class bmsMilter(Milter.Milter):
|
||||
t = parse_addr(self.mailfrom)
|
||||
if len(t) == 2: t[1] = t[1].lower()
|
||||
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(
|
||||
'SPF fail: see http://openspf.com/why.html?sender=%s&ip=%s' % (q.s,q.i))
|
||||
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 != '<>':
|
||||
# check hello name via spf
|
||||
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')
|
||||
q.set_default_explanation(
|
||||
'SPF guess: see http://spf.pobox.com/why.html')
|
||||
q.strict = False
|
||||
# best_guess should not result in fail
|
||||
if self.missing_ptr:
|
||||
# ignore dynamic PTR for best guess
|
||||
@@ -749,12 +759,16 @@ class bmsMilter(Milter.Milter):
|
||||
q.result = res
|
||||
self.cbv_needed = q
|
||||
if res in ('deny', 'fail'):
|
||||
self.log('REJECT: SPF %s %i %s' % (res,code,txt))
|
||||
self.setreply(str(code),'5.7.1',txt)
|
||||
# A proper SPF fail error message would read:
|
||||
# forger.biz [1.2.3.4] is not allowed to send mail with the domain
|
||||
# "forged.org" in the sender address. Contact <postmaster@forged.org>.
|
||||
return Milter.REJECT
|
||||
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.setreply(str(code),'5.7.1',txt)
|
||||
# A proper SPF fail error message would read:
|
||||
# forger.biz [1.2.3.4] is not allowed to send mail with the domain
|
||||
# "forged.org" in the sender address. Contact <postmaster@forged.org>.
|
||||
return Milter.REJECT
|
||||
if res == 'softfail' and not q.o in spf_accept_softfail:
|
||||
if self.missing_ptr and hres != 'pass':
|
||||
if spf_reject_noptr or q.o in spf_reject_neutral:
|
||||
@@ -1206,8 +1220,10 @@ class bmsMilter(Milter.Milter):
|
||||
else:
|
||||
self.log('CBV:',sender)
|
||||
try:
|
||||
if q.result == 'softfail':
|
||||
if q.result in ('softfail','fail','deny'):
|
||||
template = file('softfail.txt').read()
|
||||
elif q.result == 'unknown':
|
||||
template = file('permerror.txt').read()
|
||||
else:
|
||||
template = file('strike3.txt').read()
|
||||
except IOError: template = None
|
||||
|
||||
@@ -85,6 +85,9 @@ reject_spoofed = 0
|
||||
;reject_noptr = 0
|
||||
# always accept softfail from these domains, or send DSN otherwise
|
||||
;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
|
||||
[scrub]
|
||||
|
||||
@@ -166,6 +166,9 @@ rm -rf $RPM_BUILD_ROOT
|
||||
/usr/share/sendmail-cf/hack/rhsbl.m4
|
||||
|
||||
%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
|
||||
- Limit each CNAME chain independently like PTR and MX
|
||||
* 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.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user