Use signed Message-ID in delayed reject to blacklist senders
This commit is contained in:
@@ -1,5 +1,6 @@
|
|||||||
Here is a history of user visible changes to Python milter.
|
Here is a history of user visible changes to Python milter.
|
||||||
|
|
||||||
|
0.8.6
|
||||||
0.8.5 Simple trusted_forwarder implementation.
|
0.8.5 Simple trusted_forwarder implementation.
|
||||||
Fix access_file neutral policy
|
Fix access_file neutral policy
|
||||||
Move Received-SPF header to beginning of headers
|
Move Received-SPF header to beginning of headers
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
|
Allow unsigned DSNs from selected domains (that don't accept signed MFROM,
|
||||||
|
e.g. verizon.net).
|
||||||
|
|
||||||
Added Message-ID header to DSN with SRS signed sender. When seen on incoming
|
Added Message-ID header to DSN with SRS signed sender. When seen on incoming
|
||||||
rfc ignorant failure message, blacklist sender.
|
rfc ignorant failure message, blacklist sender.
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,11 @@
|
|||||||
#!/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.56 2006/02/24 02:12:54 customdesigned
|
||||||
|
# Properly report hard PermError (lax mode fails also) by always setting
|
||||||
|
# perm_error attribute with PermError exception. Improve reporting of
|
||||||
|
# invalid domain PermError.
|
||||||
|
#
|
||||||
# Revision 1.55 2006/02/17 05:04:29 customdesigned
|
# Revision 1.55 2006/02/17 05:04:29 customdesigned
|
||||||
# Use SRS sign domain list.
|
# Use SRS sign domain list.
|
||||||
# Accept but do not use for training whitelisted senders without SPF pass.
|
# Accept but do not use for training whitelisted senders without SPF pass.
|
||||||
@@ -184,7 +189,6 @@ import mime
|
|||||||
import email.Errors
|
import email.Errors
|
||||||
import Milter
|
import Milter
|
||||||
import tempfile
|
import tempfile
|
||||||
import traceback
|
|
||||||
import ConfigParser
|
import ConfigParser
|
||||||
import time
|
import time
|
||||||
import socket
|
import socket
|
||||||
@@ -800,6 +804,7 @@ class bmsMilter(Milter.Milter):
|
|||||||
self.blacklist = False
|
self.blacklist = False
|
||||||
self.reject_spam = True
|
self.reject_spam = True
|
||||||
self.data_allowed = True
|
self.data_allowed = True
|
||||||
|
self.delayed_failure = None
|
||||||
self.trust_received = self.trusted_relay
|
self.trust_received = self.trusted_relay
|
||||||
self.trust_spf = self.trusted_relay
|
self.trust_spf = self.trusted_relay
|
||||||
self.redirect_list = []
|
self.redirect_list = []
|
||||||
@@ -1082,7 +1087,9 @@ class bmsMilter(Milter.Milter):
|
|||||||
self.log("REJECT: ses spoofed:",oldaddr)
|
self.log("REJECT: ses spoofed:",oldaddr)
|
||||||
self.setreply('550','5.7.1','Invalid SES signature')
|
self.setreply('550','5.7.1','Invalid SES signature')
|
||||||
return Milter.REJECT
|
return Milter.REJECT
|
||||||
if srs_reject_spoofed: # FIXME: srs_reject_immed?
|
# reject for certain recipients are delayed until after DATA
|
||||||
|
if srs_reject_spoofed \
|
||||||
|
and not user.lower() in ('postmaster','abuse'):
|
||||||
return self.forged_bounce()
|
return self.forged_bounce()
|
||||||
self.data_allowed = not srs_reject_spoofed
|
self.data_allowed = not srs_reject_spoofed
|
||||||
|
|
||||||
@@ -1125,6 +1132,13 @@ class bmsMilter(Milter.Milter):
|
|||||||
self.setreply('550','5.7.1','That subject is not allowed')
|
self.setreply('550','5.7.1','That subject is not allowed')
|
||||||
return Milter.REJECT
|
return Milter.REJECT
|
||||||
|
|
||||||
|
# even if we wanted the Taiwanese spam, we can't read Chinese
|
||||||
|
if block_chinese:
|
||||||
|
if val.startswith('=?big5') or val.startswith('=?ISO-2022-JP'):
|
||||||
|
self.log('REJECT: %s: %s' % (name,val))
|
||||||
|
self.setreply('550','5.7.1',"We don't understand chinese")
|
||||||
|
return Milter.REJECT
|
||||||
|
|
||||||
# check for spam that claims to be legal
|
# check for spam that claims to be legal
|
||||||
lval = val.lower().strip()
|
lval = val.lower().strip()
|
||||||
for adv in ("adv:","adv.","adv ","[adv]","(adv)","advt:","advert:"):
|
for adv in ("adv:","adv.","adv ","[adv]","(adv)","advt:","advert:"):
|
||||||
@@ -1152,6 +1166,15 @@ class bmsMilter(Milter.Milter):
|
|||||||
self.setreply('550','5.7.1','I find unedited forwards annoying')
|
self.setreply('550','5.7.1','I find unedited forwards annoying')
|
||||||
return Milter.REJECT
|
return Milter.REJECT
|
||||||
|
|
||||||
|
# check for delayed bounce of CBV
|
||||||
|
if self.is_bounce and srs:
|
||||||
|
for w in ("delivery failure", "failure notice",
|
||||||
|
"returned mail", "undeliverable"):
|
||||||
|
if lval.startswith(w):
|
||||||
|
self.delayed_failure = val.strip()
|
||||||
|
# if confirmed by finding our signed Message-ID,
|
||||||
|
# original sender (encoded in Message-ID) is blacklisted
|
||||||
|
|
||||||
# check for invalid message id
|
# check for invalid message id
|
||||||
if lname == 'message-id' and len(val) < 4:
|
if lname == 'message-id' and len(val) < 4:
|
||||||
self.log('REJECT: %s: %s' % (name,val))
|
self.log('REJECT: %s: %s' % (name,val))
|
||||||
@@ -1192,12 +1215,6 @@ class bmsMilter(Milter.Milter):
|
|||||||
# decode near ascii text to unobfuscate
|
# decode near ascii text to unobfuscate
|
||||||
val = parse_header(hval)
|
val = parse_header(hval)
|
||||||
if not self.internal_connection and not (self.blacklist or self.whitelist):
|
if not self.internal_connection and not (self.blacklist or self.whitelist):
|
||||||
# even if we wanted the Taiwanese spam, we can't read Chinese
|
|
||||||
if block_chinese and lname == 'subject':
|
|
||||||
if val.startswith('=?big5') or val.startswith('=?ISO-2022-JP'):
|
|
||||||
self.log('REJECT: %s: %s' % (name,val))
|
|
||||||
self.setreply('550','5.7.1',"We don't understand chinese")
|
|
||||||
return Milter.REJECT
|
|
||||||
rc = self.check_header(name,val)
|
rc = self.check_header(name,val)
|
||||||
if rc != Milter.CONTINUE: return rc
|
if rc != Milter.CONTINUE: return rc
|
||||||
elif self.whitelist_sender and lname == 'subject':
|
elif self.whitelist_sender and lname == 'subject':
|
||||||
@@ -1490,6 +1507,27 @@ class bmsMilter(Milter.Milter):
|
|||||||
return Milter.ACCEPT # no message collected - so no eom processing
|
return Milter.ACCEPT # no message collected - so no eom processing
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
# check for delayed bounce
|
||||||
|
if self.delayed_failure:
|
||||||
|
self.fp.seek(0)
|
||||||
|
for ln in self.fp:
|
||||||
|
if ln.lower().startswith('message-id:'):
|
||||||
|
name,val = ln.split(None,1)
|
||||||
|
if val.startswith('<SRS'):
|
||||||
|
try:
|
||||||
|
sender = srs.reverse(val[1:-1])
|
||||||
|
cbv_cache[sender] = 500,self.delayed_failure,time.time()
|
||||||
|
try:
|
||||||
|
# save message for debugging
|
||||||
|
fname = tempfile.mktemp(".dsn")
|
||||||
|
os.rename(self.tempname,fname)
|
||||||
|
except:
|
||||||
|
fname = self.tempname
|
||||||
|
self.tempname = None
|
||||||
|
self.log('BLACKLIST:',sender,fname)
|
||||||
|
return Milter.DISCARD
|
||||||
|
except: continue
|
||||||
|
|
||||||
# analyze external mail for spam
|
# analyze external mail for spam
|
||||||
spam_checked = self.check_spam() # tag or quarantine for spam
|
spam_checked = self.check_spam() # tag or quarantine for spam
|
||||||
if not self.fp:
|
if not self.fp:
|
||||||
|
|||||||
+13
@@ -174,6 +174,19 @@ rm -rf $RPM_BUILD_ROOT
|
|||||||
/usr/share/sendmail-cf/hack/rhsbl.m4
|
/usr/share/sendmail-cf/hack/rhsbl.m4
|
||||||
|
|
||||||
%changelog
|
%changelog
|
||||||
|
* Thu Feb 23 2006 Stuart Gathman <stuart@bmsi.com> 0.8.6-1
|
||||||
|
- Delay reject of unsigned RCPT for postmaster and abuse only
|
||||||
|
- Fix dsn reporting of hard permerror
|
||||||
|
- Resolve FIXME for wrap_close in miltermodule.c
|
||||||
|
- Add Message-ID to DSNs
|
||||||
|
- Use signed Message-ID in delayed reject to blacklist senders
|
||||||
|
- Auto-train via blacklist and auto-whitelist
|
||||||
|
- Don't check userlist for signed MFROM
|
||||||
|
- Accept but skip DSPAM and training for whitelisted senders without SPF PASS
|
||||||
|
- Report GC stats
|
||||||
|
- Support CIDR matching for IP lists
|
||||||
|
- Support pysrs sign feature
|
||||||
|
- Support localpart specific SPF policy in access file
|
||||||
* Thu Dec 29 2005 Stuart Gathman <stuart@bmsi.com> 0.8.5-1
|
* Thu Dec 29 2005 Stuart Gathman <stuart@bmsi.com> 0.8.5-1
|
||||||
- Simple trusted_forwarder implementation.
|
- Simple trusted_forwarder implementation.
|
||||||
- Fix access_file neutral policy
|
- Fix access_file neutral policy
|
||||||
|
|||||||
Reference in New Issue
Block a user