Check access_file at startup. Compress rcpt to log.

This commit is contained in:
Stuart Gathman
2007-04-13 17:20:09 +00:00
parent 9f40f265cd
commit e505d2bb28
+99 -77
View File
@@ -1,6 +1,9 @@
#!/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.104 2007/04/05 17:59:07 customdesigned
# Stop querying gossip server twice.
#
# Revision 1.103 2007/04/02 18:37:25 customdesigned # Revision 1.103 2007/04/02 18:37:25 customdesigned
# Don't disable gossip for temporary error. # Don't disable gossip for temporary error.
# #
@@ -442,6 +445,10 @@ class SPFPolicy(object):
else: acf = None else: acf = None
self.acf = acf self.acf = acf
def close(self):
if self.acf:
self.acf.close()
def getPolicy(self,pfx): def getPolicy(self,pfx):
acf = self.acf acf = self.acf
if not acf: return None if not acf: return None
@@ -702,6 +709,7 @@ class bmsMilter(Milter.Milter):
if self.user: if self.user:
p = SPFPolicy('%s@%s'%(self.user,domain)) p = SPFPolicy('%s@%s'%(self.user,domain))
policy = p.getPolicy('smtp-auth:') policy = p.getPolicy('smtp-auth:')
p.close()
else: else:
policy = None policy = None
if policy: if policy:
@@ -814,6 +822,7 @@ class bmsMilter(Milter.Milter):
res,code,txt = q.perm_error.ext # extended (lax processing) result res,code,txt = q.perm_error.ext # extended (lax processing) result
txt = 'EXT: ' + txt txt = 'EXT: ' + txt
p = SPFPolicy(q.s) p = SPFPolicy(q.s)
# FIXME: try:finally to close policy db, or reuse with lock
hres = None hres = None
if res not in ('pass','error','temperror'): if res not in ('pass','error','temperror'):
if self.mailfrom != '<>': if self.mailfrom != '<>':
@@ -949,86 +958,91 @@ class bmsMilter(Milter.Milter):
if to.startswith('<MAILER-DAEMON@'): if to.startswith('<MAILER-DAEMON@'):
self.log('REJECT: RCPT TO:',to,str) self.log('REJECT: RCPT TO:',to,str)
return Milter.REJECT return Milter.REJECT
self.log("rcpt to",to,str) try:
t = parse_addr(to) t = parse_addr(to)
newaddr = False newaddr = False
if len(t) == 2: if len(t) == 2:
t[1] = t[1].lower() t[1] = t[1].lower()
user,domain = t user,domain = t
if self.is_bounce and srs and domain in srs_domain: if self.is_bounce and srs and domain in srs_domain:
oldaddr = '@'.join(parse_addr(to)) oldaddr = '@'.join(parse_addr(to))
try: try:
if ses: if ses:
newaddr = ses.verify(oldaddr) newaddr = ses.verify(oldaddr)
else: else:
newaddr = oldaddr, newaddr = oldaddr,
if len(newaddr) > 1: if len(newaddr) > 1:
newaddr = newaddr[0] newaddr = newaddr[0]
self.log("ses rcpt:",newaddr) self.log("ses rcpt:",newaddr)
else: else:
newaddr = srs.reverse(oldaddr) newaddr = srs.reverse(oldaddr)
# Currently, a sendmail map reverses SRS. We just log it here. # Currently, a sendmail map reverses SRS. We just log it here.
self.log("srs rcpt:",newaddr) self.log("srs rcpt:",newaddr)
self.dspam = False # verified as reply to mail we sent self.dspam = False # verified as reply to mail we sent
self.blacklist = False self.blacklist = False
except: except:
if not (self.internal_connection or self.trusted_relay): if not (self.internal_connection or self.trusted_relay):
if srsre.match(oldaddr): if srsre.match(oldaddr):
self.log("REJECT: srs spoofed:",oldaddr) self.log("REJECT: srs spoofed:",oldaddr)
self.setreply('550','5.7.1','Invalid SRS signature') self.setreply('550','5.7.1','Invalid SRS signature')
return Milter.REJECT return Milter.REJECT
if oldaddr.startswith('SES='): if oldaddr.startswith('SES='):
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
# reject for certain recipients are delayed until after DATA # reject for certain recipients are delayed until after DATA
if srs_reject_spoofed \ if srs_reject_spoofed \
and not user.lower() in ('postmaster','abuse'): 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
if not self.internal_connection and domain in private_relay: if not self.internal_connection and domain in private_relay:
self.log('REJECT: RELAY:',to) self.log('REJECT: RELAY:',to)
self.setreply('550','5.7.1','Unauthorized relay for %s' % domain) self.setreply('550','5.7.1','Unauthorized relay for %s' % domain)
return Milter.REJECT
# non DSN mail to SRS address will bounce due to invalid local part
canon_to = '@'.join(t)
if canon_to == 'postmaster@' + self.receiver:
self.postmaster_reply = True
self.recipients.append(canon_to)
# FIXME: use newaddr to check rcpt
users = check_user.get(domain)
if self.discard:
self.del_recipient(to)
# don't check userlist if signed MFROM for now
userl = user.lower()
if users and not newaddr and not userl in users:
self.log('REJECT: RCPT TO:',to)
if gossip and self.umis:
gossip_node.feedback(self.umis,1)
return Milter.REJECT
# FIXME: should dspam_exempt be case insensitive?
if user in block_forward.get(domain,()):
self.forward = False
exempt_users = dspam_exempt.get(domain,())
if user in exempt_users or '' in exempt_users:
if self.blacklist:
self.log('REJECT: BLACKLISTED')
self.setreply('550','5.7.1','Sending domain has been blacklisted')
return Milter.REJECT return Milter.REJECT
self.dspam = False
if userl != 'postmaster' and self.umis \
and self.reputation < -50 and self.confidence > 1:
self.log('REJECT: REPUTATION')
self.setreply('550','5.7.1','Your domain has been sending mostly spam')
return Milter.REJECT
if domain in hide_path: # non DSN mail to SRS address will bounce due to invalid local part
self.hidepath = True canon_to = '@'.join(t)
if not domain in dspam_reject: if canon_to == 'postmaster@' + self.receiver:
self.reject_spam = False self.postmaster_reply = True
self.recipients.append(canon_to)
# FIXME: use newaddr to check rcpt
users = check_user.get(domain)
if self.discard:
self.del_recipient(to)
# don't check userlist if signed MFROM for now
userl = user.lower()
if users and not newaddr and not userl in users:
self.log('REJECT: RCPT TO:',to,str)
if gossip and self.umis:
gossip_node.feedback(self.umis,1)
return Milter.REJECT
# FIXME: should dspam_exempt be case insensitive?
if user in block_forward.get(domain,()):
self.forward = False
exempt_users = dspam_exempt.get(domain,())
if user in exempt_users or '' in exempt_users:
if self.blacklist:
self.log('REJECT: BLACKLISTED, rcpt to',to,str)
self.setreply('550','5.7.1','Sending domain has been blacklisted')
return Milter.REJECT
self.dspam = False
if userl != 'postmaster' and self.umis \
and self.reputation < -50 and self.confidence > 1:
self.log('REJECT: REPUTATION, rcpt to',to,str)
self.setreply('550','5.7.1','Your domain has been sending mostly spam')
return Milter.REJECT
if domain in hide_path:
self.hidepath = True
if not domain in dspam_reject:
self.reject_spam = False
except:
self.log("rcpt to",to,str)
raise
self.log("rcpt to",to,str)
self.smart_alias(to) self.smart_alias(to)
# get recipient after virtusertable aliasing # get recipient after virtusertable aliasing
@@ -1713,6 +1727,13 @@ class bmsMilter(Milter.Milter):
return Milter.CONTINUE return Milter.CONTINUE
def main(): def main():
if access_file:
try:
acf = anydbm.open(access_file,'r')
acf.close()
except:
milter_log.error('Unable to read: %s',access_file)
return
Milter.factory = bmsMilter Milter.factory = bmsMilter
flags = Milter.CHGBODY + Milter.CHGHDRS + Milter.ADDHDRS flags = Milter.CHGBODY + Milter.CHGHDRS + Milter.ADDHDRS
if wiretap_dest or smart_alias or dspam_userdir: if wiretap_dest or smart_alias or dspam_userdir:
@@ -1727,6 +1748,7 @@ def main():
if __name__ == "__main__": if __name__ == "__main__":
read_config(["/etc/mail/pymilter.cfg","milter.cfg"]) read_config(["/etc/mail/pymilter.cfg","milter.cfg"])
if dspam_dict: if dspam_dict:
import dspam # low level spam check import dspam # low level spam check
if dspam_userdir: if dspam_userdir: