commit 2c5e0a55a146a6c2ecb1bc73ae596a3b9a51fe57 Author: Markus Birth Date: Thu Jun 29 00:23:51 2017 +0200 Initial commit diff --git a/qmail-arc.py b/qmail-arc.py new file mode 100755 index 0000000..e874155 --- /dev/null +++ b/qmail-arc.py @@ -0,0 +1,93 @@ +#!/usr/bin/env python2 +"""Qmail mail validator + +This is a script meant to be used in qmail before forwarding mail to e.g. Gmail. It will verify +the incoming mail against SPF, DKIM and more and add the appropriate ARC headers. This makes Gmail +(probably) accept mails for which there's a DMARC rule with "reject" set. + +Inspiration taken from http://bazaar.launchpad.net/~dkimpy-hackers/dkimpy/trunk/view/head:/arcsign.py +""" + +from __future__ import print_function + +__author__ = "Markus Birth" + +import os +import re +import socket +import sys + +import authres +import dkim +import spf + +AUTHSERV_ID = "uberspace.de" # domain or hostname of mail server +DKIM_DOMAIN = "birth-online.de" +DKIM_SELECTOR = "mbirth" + +# pylint: disable=C0103 + +if sys.version_info[0] >= 3: + # Make sys.stdin and stdout binary streams. + sys.stdin = sys.stdin.detach() + sys.stdout = sys.stdout.detach() + +privkey = open('.dkim-privkey', 'rb').read() + +message = sys.stdin.read() + +up_srv_ip_match = re.search(r"Received: from .* \(HELO (.*)\) \(([0-9.]+)\).*by ", message, re.MULTILINE | re.DOTALL) +up_srv_helo = up_srv_ip_match.group(1).lower() +up_srv_ip = up_srv_ip_match.group(2) + +sender_address = os.getenv('SENDER') + +### REV IP LOOKUP + +iprev_res = "fail" +iprev_hn = "Lookup error" + +try: + up_srv_hostn = socket.gethostbyaddr(up_srv_ip) + if up_srv_helo == up_srv_hostn[0]: + iprev_res = "pass" + iprev_hn = up_srv_hostn[0] + else: + iprev_res = "fail" +except: + iprev_res = "temperror" + +iprev_result = authres.IPRevAuthenticationResult(result=iprev_res, policy_iprev=up_srv_ip, policy_iprev_comment=iprev_hn) + + +### SPF CHECK + +# Find this line: +# Received: from unknown (HELO sv3-smtp2.lithium.com) (208.74.204.9) +# by serpens.uberspace.de with SMTP; 23 Jun 2017 18:43:18 -0000 + +spf_result = spf.check2(i=up_srv_ip, s=sender_address, h=up_srv_helo) + +# TODO: Received-SPF + + + +### PREP AUTH RESULT +spf_res = authres.SPFAuthenticationResult(result=spf_result[0], smtp_mailfrom=sender_address, smtp_helo=up_srv_helo, reason=spf_result[1]) +auth_res = authres.AuthenticationResultsHeader(authserv_id=AUTHSERV_ID, results=[spf_res, iprev_result]) + +sys.stdout.write(str(auth_res)) + +### ARC SIGNATURE + +cv = dkim.CV_None +if re.search('arc-seal', message, re.IGNORECASE): + cv = dkim.CV_Pass + +# parameters: message, selector, domain, privkey, auth_results, chain_validation_status +sig = dkim.arc_sign(message, DKIM_SELECTOR, DKIM_DOMAIN, privkey, str(auth_res)[24:], cv) + +for line in sig: + sys.stdout.write(line) +sys.exit(0) +sys.stdout.write(message) diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..06bf4b9 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,6 @@ +dkimpy +authres +pyspf +PyDNS +ipaddr +# UNINSTALL: ipaddress