def _check_rbl(self, msg, rbl_server, qtype="A", subtest=None): """Checks all the IPs of this message on the specified list. :param msg: The message that we perform the check on. :param rbl_server: The RBL list to check :param qtype: The DNS record type to check :param subtest: If specified then an additional check is done on the result of the DNS lookup by matching this regular expression against the result. :return: True if there is a match and the subtest passes and False otherwise. """ if self.ctxt.skip_rbl_checks: return False if subtest is not None: try: subtest = Regex(subtest) except re.error as e: self.ctxt.err("Invalid regex %s: %s", subtest, e) return False for ip in msg.get_untrusted_ips(): rev = self.ctxt.dns.reverse_ip(ip) results = self.ctxt.dns.query("%s.%s" % (rev, rbl_server), qtype) if results and not subtest: return True for result in results: if subtest.match(str(result)): return True return False
def _check_for_forged_received(self, msg): mismatch_from = 0 mismatch_ip_helo = 0 hostname_re = Regex(r"^\w+(?:[\w.-]+\.)+\w+$") ip_re = Regex(r"^(\d+\.\d+)\.\d+\.\d+") for index, relay in enumerate(msg.untrusted_relays): from_ip = relay.get("ip") from_host = self.hostname_to_domain(relay.get("rdns")) by_host = self.hostname_to_domain(relay.get("by")) helo_host = self.hostname_to_domain(relay.get("helo")) if not by_host or not hostname_re.match(by_host): continue if from_host and from_ip == '127.0.0.1': from_host = "undef" self.ctxt.log.debug("eval: forged-HELO: from=%s helo=%s by=%s" % ( from_host if from_host else "(undef)", helo_host if helo_host else "(undef)", by_host if by_host else "(undef)" )) try: ip_netmask_16 = ipaddress.IPv4Network(from_ip).supernet(16) except ValueError: ip_netmask_16 = "" try: helo_netmask_16 = ipaddress.IPv4Network(helo_host).supernet(16) except ValueError: helo_netmask_16 = "" if ip_netmask_16 and helo_netmask_16 and from_ip != helo_host: if (ip_netmask_16 != helo_netmask_16 and not IP_PRIVATE.match(helo_host)): self.ctxt.log.debug("eval: forged-HELO: massive mismatch " "on IP-addr HELO: %s != %s" % (helo_host, from_ip)) mismatch_ip_helo += 1 prev = msg.untrusted_relays[index - 1] if prev and index > 0: prev_from_host = prev.get("rdns") if (hostname_re.match(prev_from_host) and by_host != prev_from_host and not self._helo_forgery_whitelisted(by_host, prev_from_host)): self.ctxt.log.debug("eval: forged-HELO: mismatch on from: " "%s != %s" % (prev_from_host, by_host)) mismatch_from += 1 self.set_global("mismatch_from", mismatch_from) self.set_global("mismatch_ip_helo", mismatch_ip_helo)
def check_for_forged_gw05_received_headers(self, msg, target=None): gw05_re = Regex(r"from\s(\S+)\sby\s(\S+)\swith\sESMTP\;\s+\S\S\S," r"\s+\d+\s+\S\S\S\s+\d{4}\s+\d\d:\d\d:\d\d\s+[-+]*" r"\d{4}", re.X | re.I) for rcv in msg.get_decoded_header("Received"): h1 = "" h2 = "" try: match = gw05_re.match(rcv) if match: h1, h2 = match.groups() if h1 and h2 and h2 != ".": return True except IndexError: continue return False
def check_for_no_rdns_dotcom_helo(self, msg, option=None, target=None): """Check untrusted relays and verify if latest relay it has helo from a big email provider like lycos, hotmail, excite, caramail, cs, aol, msn, yahoo, drizzle""" no_rdns_dotcom_helo = False for relay in msg.untrusted_relays: if IP_PRIVATE.match(relay.get("ip")): continue from_host = relay.get("rdns") helo_host = relay.get("helo") if not helo_host: continue no_rdns_dotcom_helo = False big_isp_re = Regex( r".*(?:\.|^)(lycos\.com|lycos\.co\.uk|hotmail\.com" r"|localhost\.com|excite\.com|caramail\.com|" r"cs\.com|aol\.com|msn\.com|yahoo\.com|" r"drizzle\.com)$") if big_isp_re.match(helo_host): if not from_host: no_rdns_dotcom_helo = True return no_rdns_dotcom_helo
def _check_rbl_addr(self, addresses, rbl_server, subtest=None): """Checks the specified addresses on the specified list. :param addresses: A list of addresses to check :param rbl_server: The RBL list to check :param subtest: If specified then an additional check is done on the result of the DNS lookup by matching this regular expression against the result. :return: True if there is a match and the subtest passes and False otherwise. """ if self.ctxt.skip_rbl_checks: return False if subtest is not None: try: subtest = Regex(subtest) except re.error as e: self.ctxt.err("Invalid regex %s: %s", subtest, e) return False for addr in addresses: if "@" in addr: domain = addr.rsplit("@", 1)[1].strip() else: domain = addr.strip() results = self.ctxt.dns.query("%s.%s" % (domain, rbl_server), "A") if results and not subtest: return True for result in results: if subtest.match(str(result)): return True return False