Skip to content
Snippets Groups Projects
Commit b0286bff authored by Stuart Gathman's avatar Stuart Gathman
Browse files

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.
parent a9663a23
No related branches found
No related tags found
No related merge requests found
...@@ -103,7 +103,7 @@ def send_dsn(mailfrom,receiver,msg=None): ...@@ -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): ...@@ -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): ...@@ -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)
......
#!/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 ...@@ -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): ...@@ -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): ...@@ -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): ...@@ -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): ...@@ -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): ...@@ -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 ...@@ -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 ...@@ -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
......
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
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.
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please to comment