From fc4dcb55fe51bfa50484331932585cb2b106a01c Mon Sep 17 00:00:00 2001 From: kflux Date: Wed, 26 Feb 2014 01:49:29 +0100 Subject: [PATCH] added additional/optional S/MIME encryption --- gpg-mailgate.py | 73 ++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 69 insertions(+), 4 deletions(-) diff --git a/gpg-mailgate.py b/gpg-mailgate.py index b7ebef5..fe6ba48 100755 --- a/gpg-mailgate.py +++ b/gpg-mailgate.py @@ -28,6 +28,13 @@ import GnuPG import smtplib import sys import syslog +import traceback +import email.utils +import os + +# imports for S/MIME +from M2Crypto import BIO, Rand, SMIME, X509 +from email.mime.message import MIMEMessage # Read configuration from /etc/gpg-mailgate.conf _cfg = RawConfigParser() @@ -49,6 +56,10 @@ def log(msg): verbose=cfg.has_key('logging') and cfg['logging'].has_key('verbose') and cfg['logging']['verbose'] == 'yes' +passthrough_emails = file("/etc/postfix/passthrough_emails.cf").read().split() + +CERT_PATH = cfg['smime']['cert_path']+"/" + # Read e-mail from stdin raw = sys.stdin.read() raw_message = email.message_from_string( raw ) @@ -61,7 +72,7 @@ def send_msg( message, recipients = None ): log("Sending email to: <%s>" % '> <'.join( recipients )) relay = (cfg['relay']['host'], int(cfg['relay']['port'])) smtp = smtplib.SMTP(relay[0], relay[1]) - smtp.sendmail( from_addr, recipients, message.as_string() ) + smtp.sendmail( from_addr, recipients, message ) def encrypt_payload( payload, gpg_to_cmdline ): raw_payload = payload.get_payload(decode=True) @@ -105,6 +116,17 @@ def get_msg( message ): if not message.is_multipart(): return message.get_payload() return '\n\n'.join( [str(m) for m in message.get_payload()] ) + +def get_cert_for_email(to_addr): + simple_path = os.path.join(CERT_PATH, to_addr) + if os.path.exists(simple_path): return (simple_path, to_addr) + # support foo+ignore@bar.com -> foo@bar.com + multi_email = re.match('^([^\+]+)\+([^@]+)@(.*)$', to_addr) + if multi_email: + fixed_up_email = "%s@%s"%(multi_email.group(1), multi_email.group(3)) + log("Multi-email %s converted to %s"%(to_addr, fixed_up_email)) + return get_cert_for_email(fixed_up_email) + return None keys = GnuPG.public_keys( cfg['gpg']['keyhome'] ) gpg_to = list() @@ -125,11 +147,11 @@ if gpg_to == list(): raw_message['X-GPG-Mailgate'] = 'Not encrypted, public key not found' if verbose: log("No encrypted recipients.") - send_msg( raw_message ) + send_msg( raw_message.as_string() ) exit() if ungpg_to != list(): - send_msg( raw_message, ungpg_to ) + send_msg( raw_message.as_string(), ungpg_to ) log("Encrypting email to: %s" % ' '.join( map(lambda x: x[0], gpg_to) )) @@ -145,4 +167,47 @@ for rcpt in gpg_to: encrypted_payloads = encrypt_all_payloads( raw_message, gpg_to_cmdline ) raw_message.set_payload( encrypted_payloads ) -send_msg( raw_message, gpg_to_smtp ) +did_passthru = False +if len(to_addrs) == 1: + for pt_email in passthrough_emails: + if pt_email.lower() == email.utils.parseaddr(to_addrs[0])[1].lower(): + did_passthru = True + send_msg(raw_message.as_string(), gpg_to_smtp) + break + +if not did_passthru: + s = SMIME.SMIME() + sk = X509.X509_Stack() + + normalized_recipient = [] + + for addr in to_addrs: + addr_addr = email.utils.parseaddr(addr)[1].lower() + cert_and_email = get_cert_for_email(addr_addr) + if cert_and_email: + (to_cert, normal_email) = cert_and_email + log("Found cert "+to_cert+" for "+addr+": "+normal_email) + normalized_recipient.append((email.utils.parseaddr(addr)[0], normal_email)) + x509 = X509.load_cert(to_cert, format=X509.FORMAT_PEM) + sk.push(x509) + + if len(normalized_recipient): + s.set_x509_stack(sk) + s.set_cipher(SMIME.Cipher('aes_192_cbc')) + p7 = s.encrypt( BIO.MemoryBuffer(raw_message.as_string()) ) + + # Output p7 in mail-friendly format. + out = BIO.MemoryBuffer() + out.write('From: '+from_addr+'\n') + to_list = ",".join([email.utils.formataddr(x) for x in normalized_recipient]) + out.write('To: '+to_list+'\n') + if raw_message['Subject']: + out.write('Subject: '+raw_message['Subject']+'\n') + s.write(out, p7) + + log("Sending message from "+from_addr+" to "+str(to_addrs)) + raw_msg = out.read() + send_msg(raw_msg, gpg_to_smtp) + else: + log("Unable to find valid S/MIME recipient") + send_msg( raw_message.as_string(), gpg_to_smtp )