From cdfeb2d792a3b55169e54ed577763b0b450daf91 Mon Sep 17 00:00:00 2001 From: Stuart Gathman <stuart@gathman.org> Date: Mon, 2 Jul 2007 03:06:10 +0000 Subject: [PATCH] Ban ips on bad mailfrom offenses as well as bad rcpts. --- bms.py | 36 ++++++++++++++++++++++++------------ 1 file changed, 24 insertions(+), 12 deletions(-) diff --git a/bms.py b/bms.py index 2549815..df02a92 100644 --- a/bms.py +++ b/bms.py @@ -1,6 +1,10 @@ #!/usr/bin/env python # A simple milter that has grown quite a bit. # $Log$ +# Revision 1.109 2007/06/23 20:53:05 customdesigned +# Ban IPs based on too many invalid recipients in a connection. Requires +# configuring check_user. Tighten HELO best_guess policy. +# # Revision 1.108 2007/04/19 16:02:43 customdesigned # Do not process valid SRS recipients as delayed_failure. # @@ -594,7 +598,6 @@ class bmsMilter(Milter.Milter): self.log("REJECT: BANNED IP") self.setreply('550','5.7.1', 'Banned for dictionary attacks') return Milter.REJECT - self.bad_rcpts = 0 self.hello_name = None self.connecthost = hostname if hostname == 'localhost' and not ipaddr.startswith('127.') \ @@ -602,6 +605,7 @@ class bmsMilter(Milter.Milter): self.log("REJECT: PTR is",hostname) self.setreply('550','5.7.1', '"%s" is not a reasonable PTR name'%hostname) return Milter.REJECT + self.offenses = 0 return Milter.CONTINUE def hello(self,hostname): @@ -650,6 +654,17 @@ class bmsMilter(Milter.Milter): for t in smart_alias[key]: self.add_recipient('<%s>'%t) + def offense(self,inc=1): + self.offenses += inc + if self.offenses > 3: + try: + ip = addr2bin(self.connectip) + if ip not in banned_ips: + banned_ips.add(ip) + print >>open('banned_ips','a'),self.connectip + except: pass + return Milter.REJECT + # multiple messages can be received on a single connection # envfrom (MAIL FROM in the SMTP protocol) seems to mark the start # of each message. @@ -769,7 +784,9 @@ class bmsMilter(Milter.Milter): if not (self.internal_connection or self.trusted_relay) \ and self.connectip and spf: rc = self.check_spf() - if rc != Milter.CONTINUE: return rc + if rc != Milter.CONTINUE: + if rc != Milter.TEMPFAIL: self.offense() + return rc else: rc = Milter.CONTINUE # FIXME: parse Received-SPF from trusted_relay for SPF result @@ -786,6 +803,7 @@ class bmsMilter(Milter.Milter): elif cbv_cache.has_key(self.canon_from) and cbv_cache[self.canon_from] \ or domain in blacklist: if not self.internal_connection: + self.offense() if not dspam_userdir: if domain in blacklist: self.log('REJECT: BLACKLIST',self.canon_from) @@ -866,6 +884,8 @@ class bmsMilter(Milter.Milter): # check hello name via spf unless spf pass h = spf.query(self.connectip,'',self.hello_name,receiver=receiver) hres,hcode,htxt = h.check() + # FIXME: in a few cases, rejecting on HELO neutral causes problems + # for senders forced to use their braindead ISPs email service. if hres in ('deny','fail','neutral','softfail'): self.log('REJECT: hello SPF: %s 550 %s' % (hres,htxt)) self.setreply('550','5.7.1',htxt, @@ -907,7 +927,7 @@ class bmsMilter(Milter.Milter): if policy == 'CBV': if self.mailfrom != '<>': self.cbv_needed = (q,ores) # accept, but inform sender via DSN - self.bad_rcpts = 3 # ban ip if any bad recipient + self.offenses = 3 # ban ip if any bad recipient elif policy != 'OK': self.log('REJECT: no PTR, HELO or SPF') self.setreply('550','5.7.1', @@ -1059,15 +1079,7 @@ class bmsMilter(Milter.Milter): if gossip and self.umis: gossip_node.feedback(self.umis,1) self.umis = None - self.bad_rcpts += 1 - if self.bad_rcpts > 3: - try: - ip = addr2bin(self.connectip) - if ip not in banned_ips: - banned_ips.add(ip) - print >>open('banned_ips','a'),self.connectip - except: pass - return Milter.REJECT + return self.offense() # FIXME: should dspam_exempt be case insensitive? if user in block_forward.get(domain,()): self.forward = False -- GitLab