diff --git a/NEWS b/NEWS index 05b1baf9800352a125e9a2a031fc8f67244b0cf4..f0c7c0b2dfc18123f5588049087b373c36d53be6 100644 --- a/NEWS +++ b/NEWS @@ -1,5 +1,6 @@ Here is a history of user visible changes to Python milter. +0.8.6 0.8.5 Simple trusted_forwarder implementation. Fix access_file neutral policy Move Received-SPF header to beginning of headers diff --git a/TODO b/TODO index cf3e57cc8337c77a16e9a098384ef2a51c10f674..b38a7db69a45400fe4e561ac864d8de6d0976e90 100644 --- a/TODO +++ b/TODO @@ -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 rfc ignorant failure message, blacklist sender. diff --git a/bms.py b/bms.py index 11d50f7a208acedc43f3a0833d0aacefafbfdc86..642a4d896d4f33c65ce865598d73545de22fa268 100644 --- a/bms.py +++ b/bms.py @@ -1,6 +1,11 @@ #!/usr/bin/env python # A simple milter that has grown quite a bit. # $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 # Use SRS sign domain list. # Accept but do not use for training whitelisted senders without SPF pass. @@ -184,7 +189,6 @@ import mime import email.Errors import Milter import tempfile -import traceback import ConfigParser import time import socket @@ -800,6 +804,7 @@ class bmsMilter(Milter.Milter): self.blacklist = False self.reject_spam = True self.data_allowed = True + self.delayed_failure = None self.trust_received = self.trusted_relay self.trust_spf = self.trusted_relay self.redirect_list = [] @@ -1082,7 +1087,9 @@ class bmsMilter(Milter.Milter): self.log("REJECT: ses spoofed:",oldaddr) self.setreply('550','5.7.1','Invalid SES signature') 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() 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') 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 lval = val.lower().strip() 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') 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 if lname == 'message-id' and len(val) < 4: self.log('REJECT: %s: %s' % (name,val)) @@ -1192,12 +1215,6 @@ class bmsMilter(Milter.Milter): # decode near ascii text to unobfuscate val = parse_header(hval) 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) if rc != Milter.CONTINUE: return rc 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 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 spam_checked = self.check_spam() # tag or quarantine for spam if not self.fp: diff --git a/milter.spec b/milter.spec index 546f3d0204181393ff45279a597ef61f219c3cf6..6167f9fbae4efe31a1d17318c33300839db4b178 100644 --- a/milter.spec +++ b/milter.spec @@ -174,6 +174,19 @@ rm -rf $RPM_BUILD_ROOT /usr/share/sendmail-cf/hack/rhsbl.m4 %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 - Simple trusted_forwarder implementation. - Fix access_file neutral policy