def examine(self, suspect): if not HAVE_SRS: return DUNNO forward_domain = self.config.get(self.section, 'forward_domain') if suspect.to_domain != forward_domain: self.logger.debug('SRS: ignoring mail to %s - only accepting %s' % (suspect.to_address, forward_domain)) return DUNNO action = DUNNO message = None srs = self._init_srs() if suspect.to_address.lower().startswith('srs'): orig_rcpt = suspect.to_address try: recipient = srs.reverse(orig_rcpt) self.logger.info('SRS: decrypted bounce address %s to %s' % (orig_rcpt, recipient)) except Exception as e: self.logger.error('SRS: Failed to decrypt %s reason: %s' % (orig_rcpt, str(e))) action = REJECT message = apply_template( self.config.get(self.section, 'messagetemplate'), suspect) else: self.logger.debug('SRS: ignoring unsigned address %s' % (suspect.to_address)) if not self.config.getboolean(self.section, 'accept_unsigned'): action = REJECT message = apply_template( self.config.get(self.section, 'messagetemplate'), suspect) return action, message
def examine(self, suspect): if not HAVE_DNS: return DUNNO from_address = suspect.get_value('sender') if from_address is None: self.logger.warning('No FROM address found') return DEFER_IF_PERMIT, 'internal policy error (no from address)' from_address = strip_address(from_address) from_domain = extract_domain(from_address) if self._is_whitelisted(from_domain): return DUNNO from_address = self._email_normalise(from_address) addr_hash = self._create_hash(from_address) listed, message = self._ebl_lookup(addr_hash) if listed: values = { 'dnszone': self.config.get(self.section, 'dnszone', '').strip(), 'message': message, } message = apply_template( self.config.get(self.section, 'messagetemplate'), suspect, values) return REJECT, message else: return DUNNO
def examine(self,suspect): helo_name=suspect.get_value('helo_name') if helo_name is None : self.logger.error('missing helo') return DUNNO helo_tld=helo_name.split('.')[-1].lower() #initialize loaders tld_file=self.config.get(self.section,'tldfile') if self.tld_loader is None: self.tld_loader=FileList(tld_file,lowercase=True,minimum_time_between_reloads=3600) if helo_tld in self.tld_loader.get_list(): return DUNNO,'' exceptionfile=self.config.get(self.section,'exceptionfile') if self.exception_loader is None: self.exception_loader=FileList(exceptionfile,lowercase=True,minimum_time_between_reloads=10) if helo_tld in self.exception_loader.get_list(): return DUNNO,'' message = apply_template(self.config.get(self.section,'messagetemplate'),suspect,dict(helo_tld=helo_tld)) action=self.config.get(self.section,"on_fail") return action, message
def examine(self, suspect): helo_name = suspect.get_value('helo_name') if helo_name is None: self.logger.error('missing helo') return DUNNO helo_tld = helo_name.split('.')[-1].lower() #initialize loaders tld_file = self.config.get(self.section, 'tldfile') if self.tld_loader is None: self.tld_loader = FileList(tld_file, lowercase=True, minimum_time_between_reloads=3600) if helo_tld in self.tld_loader.get_list(): return DUNNO, '' exceptionfile = self.config.get(self.section, 'exceptionfile') if self.exception_loader is None: self.exception_loader = FileList(exceptionfile, lowercase=True, minimum_time_between_reloads=10) if helo_tld in self.exception_loader.get_list(): return DUNNO, '' message = apply_template( self.config.get(self.section, 'messagetemplate'), suspect, dict(helo_tld=helo_tld)) action = self.config.get(self.section, "on_fail") return action, message
def examine(self, suspect): if not DNSQUERY_EXTENSION_ENABLED: return DUNNO from_address=suspect.get_value('sender') if from_address is None: self.logger.warning('No FROM address found') return DEFER_IF_PERMIT,'internal policy error (no from address)' from_address=strip_address(from_address) if self.config.getboolean(self.section,'check_srs_only') and not self._is_srs(from_address): self.logger.info('skipping non SRS address %s' % from_address) return DUNNO if HAVE_SRS and self.config.getboolean(self.section,'decode_srs'): from_address = self._decode_srs(from_address) from_domain=extract_domain(from_address) if self._is_whitelisted(from_domain): return DUNNO from_address = self._email_normalise(from_address) addr_hash = self._create_hash(from_address) listed, message = self._ebl_lookup(addr_hash) if listed: values = { 'dnszone': self.config.get(self.section,'dnszone','').strip(), 'message': message, } message = apply_template(self.config.get(self.section,'messagetemplate'),suspect, values) return REJECT, message else: return DUNNO
def examine(self, suspect): if not HAVE_SPF: return DUNNO client_address = suspect.get_value('client_address') helo_name = suspect.get_value('helo_name') sender = suspect.get_value('sender') if client_address is None or helo_name is None or sender is None: self.logger.error('missing client_address or helo or sender') return DUNNO if self.ip_whitelisted(client_address): self.logger.info("Client %s is whitelisted - no SPF check" % client_address) return DUNNO sender_email = strip_address(sender) if sender_email == '' or sender_email is None: return DUNNO sender_domain = extract_domain(sender_email) if sender_domain is None: self.logger.error('no domain found in sender address %s' % sender_email) return DUNNO if not self.check_this_domain(sender_domain): self.logger.debug('skipping SPF check for %s' % sender_domain) return DUNNO result, explanation = spf.check2(client_address, sender_email, helo_name) suspect.tags['spf'] = result if result != 'none': self.logger.info( 'SPF client=%s, sender=%s, h=%s result=%s : %s' % (client_address, sender_email, helo_name, result, explanation)) action = DUNNO message = apply_template( self.config.get(self.section, 'messagetemplate'), suspect, dict(result=result, explanation=explanation)) configopt = 'on_%s' % result if self.config.has_option(self.section, configopt): action = string_to_actioncode( self.config.get(self.section, configopt)) return action, message
def examine(self,suspect): if not have_spf: return DUNNO client_address=suspect.get_value('client_address') helo_name=suspect.get_value('helo_name') sender=suspect.get_value('sender') if client_address is None or helo_name is None or sender is None: self.logger.error('missing client_address or helo or sender') return DUNNO if self.ip_whitelisted(client_address): self.logger.info("Client %s is whitelisted - no SPF check"%client_address) return DUNNO sender_email = strip_address(sender) if sender_email=='' or sender_email is None: return DUNNO selective_sender_domain_file=self.config.get(self.section,'domain_selective_spf_file') if selective_sender_domain_file!='': if self.selective_domain_loader is None: self.selective_domain_loader=FileList(selective_sender_domain_file,lowercase=True) try: sender_domain = extract_domain(sender_email) if sender_domain is None: return DUNNO except ValueError as e: self.logger.warning(str(e)) return DUNNO if not sender_domain.lower() in self.selective_domain_loader.get_list(): return DUNNO result, explanation = spf.check2(client_address, sender_email, helo_name) suspect.tags['spf']=result if result!='none': self.logger.info('SPF client=%s, sender=%s, h=%s result=%s : %s' % (client_address, sender_email, helo_name, result,explanation)) action = DUNNO message = apply_template(self.config.get(self.section,'messagetemplate'),suspect,dict(result=result,explanation=explanation)) configopt='on_%s'%result if self.config.has_option(self.section,configopt): action=self.config.get(self.section,configopt) return action, message
def examine(self, suspect): encryption_protocol = suspect.get_value('encryption_protocol') recipient=suspect.get_value('recipient') rcpt_email = strip_address(recipient) if rcpt_email=='' or rcpt_email is None: return DUNNO enforce = self.enforce_domain(extract_domain(rcpt_email)) action = DUNNO message = None if enforce and encryption_protocol == '': action=string_to_actioncode(self.config.get(self.section, 'action')) message = apply_template(self.config.get(self.section,'messagetemplate'),suspect) return action, message
def examine(self,suspect): if not HAVE_SPF: return DUNNO client_address=suspect.get_value('client_address') helo_name=suspect.get_value('helo_name') sender=suspect.get_value('sender') if client_address is None or helo_name is None or sender is None: self.logger.error('missing client_address or helo or sender') return DUNNO if self.ip_whitelisted(client_address): self.logger.info("Client %s is whitelisted - no SPF check"%client_address) return DUNNO sender_email = strip_address(sender) if sender_email=='' or sender_email is None: return DUNNO sender_domain = extract_domain(sender_email) if sender_domain is None: self.logger.error('no domain found in sender address %s' % sender_email) return DUNNO if not self.check_this_domain(sender_domain): self.logger.debug('skipping SPF check for %s' % sender_domain) return DUNNO result, explanation = spf.check2(client_address, sender_email, helo_name) suspect.tags['spf'] = result if result != 'none': self.logger.info('SPF client=%s, sender=%s, h=%s result=%s : %s' % (client_address, sender_email, helo_name, result,explanation)) action = DUNNO message = apply_template(self.config.get(self.section, 'messagetemplate'), suspect, dict(result=result, explanation=explanation)) configopt = 'on_%s' % result if self.config.has_option(self.section, configopt): action=string_to_actioncode(self.config.get(self.section, configopt)) return action, message
def examine(self,suspect): starttime=time.time() retaction=DUNNO retmessage=None self.reload_if_necessary() to_domain=suspect.to_domain to_address=suspect.to_address for rec in [to_address,to_domain,'global']: if rec in self.ruledict: lg.debug("Found rules for %s"%rec) for recrule in self.ruledict[rec]: result=recrule.hit(suspect) if result: return recrule.action,apply_template(recrule.message,suspect) endtime=time.time() difftime=endtime-starttime suspect.tags['RecipientRules.time']="%.4f"%difftime return retaction,retmessage
def examine(self, suspect): if HAVE_GEOIP == LIB_GEOIP_NONE: return DUNNO database = self.config.get(self.section, 'database') if not os.path.exists(database): return DUNNO self.geoip.filename = database self.geoip.reloadifnecessary(database) client_address = suspect.get_value('client_address') if client_address is None: self.logger.info('No client address found') return DUNNO blacklist = self._get_list('blacklist') whitelist = self._get_list('whitelist') on_unknown = self.config.get(self.section, 'on_unknown') unknown = DUNNO if on_unknown.strip().upper() == 'REJECT': unknown = REJECT cc = self.geoip.country_code(client_address) cn = self.geoip.country_name(cc) action = DUNNO message = None if cn == 'unknown': action = unknown elif cc in blacklist or (whitelist and cc not in whitelist): action = REJECT if action == REJECT: rejmsg = self.config.get(self.section, 'reject_message').strip() message = apply_template(rejmsg, suspect, dict(cn=cn, cc=cc)) self.logger.debug('IP: %s country: %s action: %s' % (client_address, cc, action)) return action, message
def examine(self, suspect): starttime = time.time() retaction = DUNNO retmessage = None self.reload_if_necessary() to_domain = suspect.to_domain to_address = suspect.to_address for rec in [to_address, to_domain, 'global']: if rec in self.ruledict: lg.debug("Found rules for %s" % rec) for recrule in self.ruledict[rec]: result = recrule.hit(suspect) if result: return recrule.action, apply_template( recrule.message, suspect) endtime = time.time() difftime = endtime - starttime suspect.tags['RecipientRules.time'] = "%.4f" % difftime return retaction, retmessage
def examine(self, suspect): if not DNSQUERY_EXTENSION_ENABLED: return DUNNO from_address = suspect.get_value('sender') if from_address is None: self.logger.warning('No FROM address found') return DEFER_IF_PERMIT, 'internal policy error (no from address)' from_address = strip_address(from_address) if self.config.getboolean( self.section, 'check_srs_only') and not self._is_srs(from_address): self.logger.info('skipping non SRS address %s' % from_address) return DUNNO if HAVE_SRS and self.config.getboolean(self.section, 'decode_srs'): from_address = self._decode_srs(from_address) from_domain = extract_domain(from_address) if self._is_whitelisted(from_domain): return DUNNO from_address = self._email_normalise(from_address) addr_hash = self._create_hash(from_address) listed, message = self._ebl_lookup(addr_hash) if listed: values = { 'dnszone': self.config.get(self.section, 'dnszone', '').strip(), 'message': message, } message = apply_template( self.config.get(self.section, 'messagetemplate'), suspect, values) return REJECT, message else: return DUNNO
def examine(self,suspect): if HAVE_GEOIP == LIB_GEOIP_NONE: return DUNNO database = self.config.get(self.section, 'database') if not os.path.exists(database): return DUNNO self.geoip.filename = database self.geoip._reload_if_necessary() client_address=suspect.get_value('client_address') if client_address is None: self.logger.info('No client address found') return DUNNO blacklist = self._get_list('blacklist') whitelist = self._get_list('whitelist') on_unknown = self.config.get(self.section, 'on_unknown') unknown = DUNNO if on_unknown.strip().upper() == 'REJECT': unknown = REJECT cc = self.geoip.country_code(client_address) cn = self.geoip.country_name(cc) action = DUNNO message = None if cn == 'unknown': action = unknown elif cc in blacklist or (whitelist and cc not in whitelist): action = REJECT if action == REJECT: rejmsg = self.config.get(self.section, 'reject_message').strip() message = apply_template(rejmsg, suspect, dict(cn=cn, cc=cc)) self.logger.debug('IP: %s country: %s action: %s' % (client_address, cc, action)) return action, message
def examine(self, suspect): if self.limiters is None: filename = self.config.get(self.section, 'limiterfile') if not os.path.exists(filename): self.logger.error('Limiter config file %s not found', filename) return with open(filename) as filehandle: limiterconfig = filehandle.read() limiters = self.load_limiter_config(limiterconfig) self.limiters = limiters self.logger.info('Found %d limiter configurations', len(limiters)) self.load_backends() skiplist = [] for limiter in self.limiters: if limiter.name in skiplist: # check if this limiter is skipped by a previous one self.logger.debug('limiter %s skipped due to previous match', limiter.name) continue #get field values allfieldsavailable = True fieldvalues = [] for fieldname in limiter.fields: if hasattr(suspect, fieldname): fieldvalues.append(getattr(suspect, fieldname)) elif suspect.get_tag(fieldname): fieldvalues.append(str(suspect.get_tag(fieldname))) else: allfieldsavailable = False self.logger.debug('Skipping limiter %s - suspect field or tag %s not available', limiter.name, fieldname) break if not allfieldsavailable: #rate limit can not be applied continue checkval = ','.join(fieldvalues) if limiter.regex is not None: if re.match(limiter.regex, checkval): if limiter.skip is not None: skiplist.extend(limiter.skip) else: #no match, skip this limiter self.logger.debug('Skipping limiter %s - regex does not match', limiter.name) continue eventname = limiter.name+checkval timespan = limiter.timespan if limiter.max < 0: #no limit continue try: (allow, count) = self.backends[limiter.strategy].check_allowed(eventname, limiter.max, timespan) self.logger.debug('Limiter event %s count: %d', eventname, count) if not allow: return limiter.action, apply_template(limiter.message, suspect) except Exception as ex: error = type(ex).__name__, ex.message self.logger.error('Failed to run limitter backend for strategy "%s" eventname %s error %s', limiter.strategy, eventname, error)
def examine(self, suspect): if self.limiters is None: filename = self.config.get(self.section, 'limiterfile') if not os.path.exists(filename): self.logger.error("Limiter config file %s not found" % filename) return with open(filename) as fp: limiterconfig = fp.read() limiters = self.load_limiter_config(limiterconfig) self.limiters = limiters self.logger.info("Found %s limiter configurations" % (len(limiters))) if self.backend_instance is None: btype = self.config.get(self.section, 'backendtype') if btype not in AVAILABLE_RATELIMIT_BACKENDS: self.logger.error('ratelimit backend %s not available' % (btype)) return self.backend_instance = AVAILABLE_RATELIMIT_BACKENDS[btype]( self.config.get(self.section, 'backendconfig')) skiplist = [] for limiter in self.limiters: if limiter.name in skiplist: # check if this limiter is skipped by a previous one self.logger.debug('limiter %s skipped due to previous match' % limiter.name) continue #get field values allfieldsavailable = True fieldvalues = [] for fieldname in limiter.fields: if hasattr(suspect, fieldname): fieldvalues.append(getattr(suspect, fieldname)) else: allfieldsavailable = False self.logger.debug( 'Skipping limiter %s - field %s not available' % (limiter.name, fieldname)) break if not allfieldsavailable: #rate limit can not be applied continue checkval = ','.join(fieldvalues) if limiter.regex is not None: if re.match(limiter.regex, checkval): if limiter.skip is not None: skiplist.extend(limiter.skip) else: #no match, skip this limiter self.logger.debug( 'Skipping limiter %s - regex does not match' % (limiter.name)) continue #self.logger.debug("check %s"%str(limiter)) eventname = limiter.name + checkval timespan = limiter.timespan max = limiter.max if max < 0: #no limit continue event_count = self.backend_instance.check_count( eventname, timespan) self.logger.debug("Limiter event %s count: %s" % (eventname, event_count)) if event_count > max: return limiter.action, apply_template(limiter.message, suspect)