From ffaee4c577dd9e7cc2e1d3fb19307f330a34a3c8 Mon Sep 17 00:00:00 2001 From: Taylor Hornby Date: Tue, 8 Oct 2013 17:00:55 -0600 Subject: [PATCH 01/23] Correctly encode 'keyhome' into a gpg command. --- GnuPG/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/GnuPG/__init__.py b/GnuPG/__init__.py index 7a358d5..7f9c27d 100644 --- a/GnuPG/__init__.py +++ b/GnuPG/__init__.py @@ -2,8 +2,8 @@ import os import subprocess def public_keys( keyhome ): - cmd = '/usr/bin/gpg --homedir %s --list-keys --with-colons' % keyhome - p = subprocess.Popen( cmd.split(' '), stdin=None, stdout=subprocess.PIPE, stderr=subprocess.PIPE ) + cmd = ['/usr/bin/gpg', '--homedir', keyhome, '--list-keys', '--with-colons'] + p = subprocess.Popen( cmd, stdin=None, stdout=subprocess.PIPE, stderr=subprocess.PIPE ) p.wait() keys = list() for line in p.stdout.readlines(): From 42caa47f5b8f60c36c04d33409b78fc764f68ad4 Mon Sep 17 00:00:00 2001 From: uragit Date: Sat, 12 Oct 2013 01:26:55 -0700 Subject: [PATCH 02/23] Added syslog and verbose options for config file. --- gpg-mailgate.conf.sample | 2 ++ gpg-mailgate.py | 34 ++++++++++++++++++++++++++-------- 2 files changed, 28 insertions(+), 8 deletions(-) diff --git a/gpg-mailgate.conf.sample b/gpg-mailgate.conf.sample index 75102ed..d191fa0 100644 --- a/gpg-mailgate.conf.sample +++ b/gpg-mailgate.conf.sample @@ -6,7 +6,9 @@ domains = example.com,corp.org keyhome = /var/gpg/.gnupg [logging] +# For logging to syslog. 'file = syslog' file = /tmp/gpg-mailgate.log +#verbose = yes [relay] host = 127.0.0.1 diff --git a/gpg-mailgate.py b/gpg-mailgate.py index 01765b1..f5de56e 100755 --- a/gpg-mailgate.py +++ b/gpg-mailgate.py @@ -8,6 +8,7 @@ import re import GnuPG import smtplib import sys +import syslog # Read configuration from /etc/gpg-mailgate.conf _cfg = RawConfigParser() @@ -18,11 +19,24 @@ for sect in _cfg.sections(): for (name, value) in _cfg.items(sect): cfg[sect][name] = value +def log(msg): + if cfg.has_key('logging') and cfg['logging'].has_key('file'): + if cfg['logging']['file'] == "syslog": + syslog.syslog(syslog.LOG_INFO | syslog.LOG_MAIL, msg) + else: + logfile = open(cfg['logging']['file'], 'a') + logfile.write(msg + "\n") + logfile.close() + +verbose=cfg.has_key('logging') and cfg['logging'].has_key('verbose') and cfg['logging']['verbose'] == 'yes' + # Read e-mail from stdin raw = sys.stdin.read() raw_message = email.message_from_string( raw ) from_addr = raw_message['From'] to_addrs = sys.argv[1:] +if verbose: + log("to_addrs: '%s'" % "', '".join(to_addrs)) encrypted_to_addrs = list() if raw_message.has_key('X-GPG-Encrypt-Cc'): @@ -32,10 +46,7 @@ if raw_message.has_key('X-GPG-Encrypt-Cc'): def send_msg( message, recipients = None ): if recipients == None: recipients = to_addrs - if cfg.has_key('logging') and cfg['logging'].has_key('file'): - log = open(cfg['logging']['file'], 'a') - log.write("Sending email to: <%s>\n" % '> <'.join( recipients )) - log.close() + 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() ) @@ -91,27 +102,34 @@ for to in to_addrs: domain = to.split('@')[1] if domain in cfg['default']['domains'].split(','): if to in keys: + if verbose: + log("Found public key for '%s' on keyring." % to) gpg_to.append( (to, to) ) elif cfg.has_key('keymap') and cfg['keymap'].has_key(to): + if verbose: + log("Found public key for '%s' in keymap (%s)." % (to, cfg['keymap'][to])) gpg_to.append( (to, cfg['keymap'][to]) ) else: + if verbose: + log("Found no public key for '%s'." % to) ungpg_to.append(to); else: + if verbose: + log("Recipient (%s) not in domain list." % to) ungpg_to.append(to) if gpg_to == list(): if cfg['default'].has_key('add_header') and cfg['default']['add_header'] == 'yes': raw_message['X-GPG-Mailgate'] = 'Not encrypted, public key not found' + if verbose: + log("No encrypted recipients.") send_msg( raw_message ) exit() if ungpg_to != list(): send_msg( raw_message, ungpg_to ) -if cfg.has_key('logging') and cfg['logging'].has_key('file'): - log = open(cfg['logging']['file'], 'a') - log.write("Encrypting email to: %s\n" % ' '.join( map(lambda x: x[0], gpg_to) )) - log.close() +log("Encrypting email to: %s" % ' '.join( map(lambda x: x[0], gpg_to) )) if cfg['default'].has_key('add_header') and cfg['default']['add_header'] == 'yes': raw_message['X-GPG-Mailgate'] = 'Encrypted by GPG Mailgate' From 9c6a1ecfc888c28c8bbf4d28cda1d74a91ed0bbc Mon Sep 17 00:00:00 2001 From: perennate Date: Fri, 9 Aug 2013 14:41:01 +0800 Subject: [PATCH 03/23] Update README. --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 3546025..c93b60e 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,8 @@ # gpg-mailgate -I'm not the author of this tool, only bugfixer. Original code can be found on [project google code page](http://code.google.com/p/gpg-mailgate/). +gpg-mailgate is a content filter for Postfix that automatically encrypts unencrypted incoming email using PGP for select recipients. # TODO -- ~~attachments support~~ +- fix attachments support - multipart messages support - testing From 6e92ded37c4162177f3ebfcefb1488c486812c34 Mon Sep 17 00:00:00 2001 From: perennate Date: Fri, 9 Aug 2013 14:41:06 +0800 Subject: [PATCH 04/23] Greatly improve recipient selection mechanism. This resolves many errors, such as forwarding loops arising from mailing lists. --- gpg-mailgate.py | 7 ------- 1 file changed, 7 deletions(-) diff --git a/gpg-mailgate.py b/gpg-mailgate.py index f5de56e..00ab3cf 100755 --- a/gpg-mailgate.py +++ b/gpg-mailgate.py @@ -35,13 +35,6 @@ raw = sys.stdin.read() raw_message = email.message_from_string( raw ) from_addr = raw_message['From'] to_addrs = sys.argv[1:] -if verbose: - log("to_addrs: '%s'" % "', '".join(to_addrs)) - -encrypted_to_addrs = list() -if raw_message.has_key('X-GPG-Encrypt-Cc'): - encrypted_to_addrs.extend( [e[1] for e in email.utils.getaddresses([raw_message['X-GPG-Encrypt-Cc']])] ) - del raw_message['X-GPG-Encrypt-Cc'] def send_msg( message, recipients = None ): if recipients == None: From d468766f8a94c6205945512e8251a3705d25b216 Mon Sep 17 00:00:00 2001 From: Colin Moller Date: Mon, 17 Jun 2013 17:51:31 -0700 Subject: [PATCH 05/23] Patch GnuPG library to trust keys we've already got to avoid having to manually add them --- GnuPG/__init__.py | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/GnuPG/__init__.py b/GnuPG/__init__.py index 7f9c27d..55afa59 100644 --- a/GnuPG/__init__.py +++ b/GnuPG/__init__.py @@ -33,16 +33,5 @@ class GPGEncryptor: return encdata def _command(self): - cmd = ["/usr/bin/gpg", "--trust-model", "always", "--homedir", self._keyhome, "--batch", "--yes", "--pgp7", "--no-secmem-warning", "-a", "-e"] - - # add recipients - for recipient in self._recipients: - cmd.append("-r") - cmd.append(recipient) - - # add on the charset, if set - if self._charset: - cmd.append("--comment") - cmd.append('Charset: ' + self._charset) - - return cmd + cmd = "/usr/bin/gpg --trust-model always --homedir %s --batch --yes --pgp7 --no-secmem-warning -a -e -r %s" % (self._keyhome, ' -r '.join(self._recipients)) + return cmd.split() From fbda6a6e7265d74bcc7c965ab7927b26af1ce59d Mon Sep 17 00:00:00 2001 From: Colin Moller Date: Tue, 18 Jun 2013 18:46:41 -0700 Subject: [PATCH 06/23] Rudimentary support for not re-encrypting already encrypted messages --- gpg-mailgate.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/gpg-mailgate.py b/gpg-mailgate.py index 00ab3cf..89266ce 100755 --- a/gpg-mailgate.py +++ b/gpg-mailgate.py @@ -50,6 +50,8 @@ def encrypt_payload( payload, gpg_to_cmdline ): return payload gpg = GnuPG.GPGEncryptor( cfg['gpg']['keyhome'], gpg_to_cmdline, payload.get_content_charset() ) gpg.update( raw_payload ) + if "-----BEGIN PGP MESSAGE-----" in raw_payload and "-----END PGP MESSAGE-----" in raw_payload: + return payload payload.set_payload( gpg.encrypt() ) if payload['Content-Disposition']: payload.replace_header( 'Content-Disposition', re.sub(r'filename="([^"]+)"', r'filename="\1.pgp"', payload['Content-Disposition']) ) From 7110b9a303aefb0ba724da9a15137517ac3a1e81 Mon Sep 17 00:00:00 2001 From: perennate Date: Fri, 9 Aug 2013 15:15:51 +0800 Subject: [PATCH 07/23] Remove encryptod_to_addrs. --- gpg-mailgate.py | 9 --------- 1 file changed, 9 deletions(-) diff --git a/gpg-mailgate.py b/gpg-mailgate.py index 89266ce..2953614 100755 --- a/gpg-mailgate.py +++ b/gpg-mailgate.py @@ -83,15 +83,6 @@ def get_msg( message ): keys = GnuPG.public_keys( cfg['gpg']['keyhome'] ) gpg_to = list() ungpg_to = list() -for enc in encrypted_to_addrs: - domain = enc.split('@')[1] - if domain in cfg['default']['domains'].split(','): - if enc in keys: - gpg_to.append( (enc, enc) ) - elif cfg.has_key('keymap') and cfg['keymap'].has_key(enc): - gpg_to.append( (enc, cfg['keymap'][enc]) ) - else: - ungpg_to.append(enc); for to in to_addrs: domain = to.split('@')[1] From 4aa366dea50852c3516041a88be5e31e65aee0f7 Mon Sep 17 00:00:00 2001 From: perennate Date: Sun, 11 Aug 2013 20:35:48 +0800 Subject: [PATCH 08/23] Fix bug causing messages to have extra characters or failing to encode properly. --- GnuPG/__init__.py | 15 +++++++++++++-- gpg-mailgate.py | 42 +++++++++++++++++++++++++++--------------- 2 files changed, 40 insertions(+), 17 deletions(-) diff --git a/GnuPG/__init__.py b/GnuPG/__init__.py index 55afa59..7f9c27d 100644 --- a/GnuPG/__init__.py +++ b/GnuPG/__init__.py @@ -33,5 +33,16 @@ class GPGEncryptor: return encdata def _command(self): - cmd = "/usr/bin/gpg --trust-model always --homedir %s --batch --yes --pgp7 --no-secmem-warning -a -e -r %s" % (self._keyhome, ' -r '.join(self._recipients)) - return cmd.split() + cmd = ["/usr/bin/gpg", "--trust-model", "always", "--homedir", self._keyhome, "--batch", "--yes", "--pgp7", "--no-secmem-warning", "-a", "-e"] + + # add recipients + for recipient in self._recipients: + cmd.append("-r") + cmd.append(recipient) + + # add on the charset, if set + if self._charset: + cmd.append("--comment") + cmd.append('Charset: ' + self._charset) + + return cmd diff --git a/gpg-mailgate.py b/gpg-mailgate.py index 2953614..5c90b09 100755 --- a/gpg-mailgate.py +++ b/gpg-mailgate.py @@ -45,6 +45,7 @@ def send_msg( message, recipients = None ): smtp.sendmail( from_addr, recipients, message.as_string() ) def encrypt_payload( payload, gpg_to_cmdline ): + gpg = GnuPG.GPGEncryptor( cfg['gpg']['keyhome'], gpg_to_cmdline, payload.get_content_charset() ) raw_payload = payload.get_payload(decode=True) if "-----BEGIN PGP MESSAGE-----" in raw_payload and "-----END PGP MESSAGE-----" in raw_payload: return payload @@ -53,24 +54,35 @@ def encrypt_payload( payload, gpg_to_cmdline ): if "-----BEGIN PGP MESSAGE-----" in raw_payload and "-----END PGP MESSAGE-----" in raw_payload: return payload payload.set_payload( gpg.encrypt() ) - if payload['Content-Disposition']: - payload.replace_header( 'Content-Disposition', re.sub(r'filename="([^"]+)"', r'filename="\1.pgp"', payload['Content-Disposition']) ) - if payload['Content-Type']: - payload.replace_header( 'Content-Type', re.sub(r'name="([^"]+)"', r'name="\1.pgp"', payload['Content-Type']) ) - if 'name="' in payload['Content-Type']: - payload.replace_header( 'Content-Type', re.sub(r'^[a-z/]+;', r'application/octet-stream;', payload['Content-Type']) ) - payload.set_payload( "\n".join( filter( lambda x:re.search(r'^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=|[A-Za-z0-9+/]{4})$',x), payload.get_payload().split("\n") ) ) ) + + isAttachment = payload.get_param( 'attachment', None, 'Content-Disposition' ) is not None + + if isAttachment: + filename = payload.get_filename() + + if filename: + pgpFilename = filename + ".pgp" + + if payload.get('Content-Disposition') is not None: + payload.set_param( 'filename', pgpFilename, 'Content-Disposition' ) + if payload.get('Content-Type') is not None: + if payload.get_param( 'name' ) is not None: + payload.set_param( 'name', pgpFilename ) + + payload.set_payload( "\n".join( filter( lambda x:re.search(r'^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=|[A-Za-z0-9+/]{4})$',x), payload.get_payload().split("\n") ) ) ) + + if payload.get('Content-Transfer-Encoding') is not None: + payload['Content-Transfer-Encoding'] = "quoted-printable" + return payload -def encrypt_all_payloads( payloads, gpg_to_cmdline ): +def encrypt_all_payloads( message, gpg_to_cmdline ): encrypted_payloads = list() - if type( payloads ) == str: - msg = email.message.Message() - msg.set_payload( payloads ) - return encrypt_payload( msg, gpg_to_cmdline ).as_string() - for payload in payloads: + if type( message.get_payload() ) == str: + return encrypt_payload( message, gpg_to_cmdline ).get_payload() + for payload in message.get_payload(): if( type( payload.get_payload() ) == list ): - encrypted_payloads.append( encrypt_all_payloads( payload.get_payload(), gpg_to_cmdline ) ) + encrypted_payloads.extend( encrypt_all_payloads( payload, gpg_to_cmdline ) ) else: encrypted_payloads.append( [encrypt_payload( payload, gpg_to_cmdline )] ) return sum(encrypted_payloads, []) @@ -126,7 +138,7 @@ for rcpt in gpg_to: gpg_to_smtp.append(rcpt[0]) gpg_to_cmdline.extend(rcpt[1].split(',')) -encrypted_payloads = encrypt_all_payloads( raw_message.get_payload(), gpg_to_cmdline ) +encrypted_payloads = encrypt_all_payloads( raw_message, gpg_to_cmdline ) raw_message.set_payload( encrypted_payloads ) send_msg( raw_message, gpg_to_smtp ) From 9bd941b752239f0dd38c354bcd1f23fe7be65547 Mon Sep 17 00:00:00 2001 From: perennate Date: Sun, 11 Aug 2013 23:06:27 +0800 Subject: [PATCH 09/23] Fix problem with previous commit. --- gpg-mailgate.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/gpg-mailgate.py b/gpg-mailgate.py index 5c90b09..cf6462b 100755 --- a/gpg-mailgate.py +++ b/gpg-mailgate.py @@ -82,10 +82,10 @@ def encrypt_all_payloads( message, gpg_to_cmdline ): return encrypt_payload( message, gpg_to_cmdline ).get_payload() for payload in message.get_payload(): if( type( payload.get_payload() ) == list ): - encrypted_payloads.extend( encrypt_all_payloads( payload, gpg_to_cmdline ) ) + encrypted_payloads.append( encrypt_all_payloads( payload, gpg_to_cmdline ) ) else: - encrypted_payloads.append( [encrypt_payload( payload, gpg_to_cmdline )] ) - return sum(encrypted_payloads, []) + encrypted_payloads.append( encrypt_payload( payload, gpg_to_cmdline ) ) + return encrypted_payloads def get_msg( message ): if not message.is_multipart(): From e4b5a0e979b6066e82d074950d6626667c2df8e8 Mon Sep 17 00:00:00 2001 From: perennate Date: Sun, 11 Aug 2013 23:17:46 +0800 Subject: [PATCH 10/23] Use replace header instead of add header to avoid header duplication. --- gpg-mailgate.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gpg-mailgate.py b/gpg-mailgate.py index cf6462b..57d1379 100755 --- a/gpg-mailgate.py +++ b/gpg-mailgate.py @@ -72,7 +72,7 @@ def encrypt_payload( payload, gpg_to_cmdline ): payload.set_payload( "\n".join( filter( lambda x:re.search(r'^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=|[A-Za-z0-9+/]{4})$',x), payload.get_payload().split("\n") ) ) ) if payload.get('Content-Transfer-Encoding') is not None: - payload['Content-Transfer-Encoding'] = "quoted-printable" + payload.replace_header( 'Content-Transfer-Encoding', "quoted-printable" ) return payload From 04afea12cf1692475ad0b493ec365d551aa64217 Mon Sep 17 00:00:00 2001 From: perennate Date: Mon, 12 Aug 2013 10:20:12 +0800 Subject: [PATCH 11/23] Use extend instead of append to fix list problem. --- gpg-mailgate.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gpg-mailgate.py b/gpg-mailgate.py index 57d1379..079e8a5 100755 --- a/gpg-mailgate.py +++ b/gpg-mailgate.py @@ -82,7 +82,7 @@ def encrypt_all_payloads( message, gpg_to_cmdline ): return encrypt_payload( message, gpg_to_cmdline ).get_payload() for payload in message.get_payload(): if( type( payload.get_payload() ) == list ): - encrypted_payloads.append( encrypt_all_payloads( payload, gpg_to_cmdline ) ) + encrypted_payloads.extend( encrypt_all_payloads( payload, gpg_to_cmdline ) ) else: encrypted_payloads.append( encrypt_payload( payload, gpg_to_cmdline ) ) return encrypted_payloads From 5d8e8202a6b588c80d59056ef5127fec4531a171 Mon Sep 17 00:00:00 2001 From: perennate Date: Tue, 13 Aug 2013 11:32:13 +0800 Subject: [PATCH 12/23] Remove attachment content filter because it seems to make PGP-encrypted data invalid. --- gpg-mailgate.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/gpg-mailgate.py b/gpg-mailgate.py index 079e8a5..e5276d0 100755 --- a/gpg-mailgate.py +++ b/gpg-mailgate.py @@ -69,8 +69,6 @@ def encrypt_payload( payload, gpg_to_cmdline ): if payload.get_param( 'name' ) is not None: payload.set_param( 'name', pgpFilename ) - payload.set_payload( "\n".join( filter( lambda x:re.search(r'^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=|[A-Za-z0-9+/]{4})$',x), payload.get_payload().split("\n") ) ) ) - if payload.get('Content-Transfer-Encoding') is not None: payload.replace_header( 'Content-Transfer-Encoding', "quoted-printable" ) From f8711583a323aedd912d4ca3eaff80f486e7c805 Mon Sep 17 00:00:00 2001 From: perennate Date: Sun, 22 Sep 2013 15:13:15 -0400 Subject: [PATCH 13/23] Remove useless domains configuration setting. --- gpg-mailgate.conf.sample | 1 - gpg-mailgate.py | 28 +++++++++++++--------------- 2 files changed, 13 insertions(+), 16 deletions(-) diff --git a/gpg-mailgate.conf.sample b/gpg-mailgate.conf.sample index d191fa0..20cf3a2 100644 --- a/gpg-mailgate.conf.sample +++ b/gpg-mailgate.conf.sample @@ -1,6 +1,5 @@ [default] add_header = yes -domains = example.com,corp.org [gpg] keyhome = /var/gpg/.gnupg diff --git a/gpg-mailgate.py b/gpg-mailgate.py index e5276d0..456420f 100755 --- a/gpg-mailgate.py +++ b/gpg-mailgate.py @@ -93,22 +93,20 @@ def get_msg( message ): keys = GnuPG.public_keys( cfg['gpg']['keyhome'] ) gpg_to = list() ungpg_to = list() - + +for enc in encrypted_to_addrs: + if enc in keys: + gpg_to.append( (enc, enc) ) + elif cfg.has_key('keymap') and cfg['keymap'].has_key(enc): + gpg_to.append( (enc, cfg['keymap'][enc]) ) + else: + ungpg_to.append(enc) + for to in to_addrs: - domain = to.split('@')[1] - if domain in cfg['default']['domains'].split(','): - if to in keys: - if verbose: - log("Found public key for '%s' on keyring." % to) - gpg_to.append( (to, to) ) - elif cfg.has_key('keymap') and cfg['keymap'].has_key(to): - if verbose: - log("Found public key for '%s' in keymap (%s)." % (to, cfg['keymap'][to])) - gpg_to.append( (to, cfg['keymap'][to]) ) - else: - if verbose: - log("Found no public key for '%s'." % to) - ungpg_to.append(to); + if to in keys: + gpg_to.append( (to, to) ) + elif cfg.has_key('keymap') and cfg['keymap'].has_key(to): + gpg_to.append( (to, cfg['keymap'][to]) ) else: if verbose: log("Recipient (%s) not in domain list." % to) From 52bdb3163776f7281346bbe50d368de5f2cf215a Mon Sep 17 00:00:00 2001 From: perennate Date: Sun, 22 Sep 2013 15:14:09 -0400 Subject: [PATCH 14/23] Don't encrypt if it's a signed message, since that would mess with the signature. --- gpg-mailgate.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gpg-mailgate.py b/gpg-mailgate.py index 456420f..8e132ef 100755 --- a/gpg-mailgate.py +++ b/gpg-mailgate.py @@ -47,7 +47,7 @@ def send_msg( message, recipients = None ): def encrypt_payload( payload, gpg_to_cmdline ): gpg = GnuPG.GPGEncryptor( cfg['gpg']['keyhome'], gpg_to_cmdline, payload.get_content_charset() ) raw_payload = payload.get_payload(decode=True) - if "-----BEGIN PGP MESSAGE-----" in raw_payload and "-----END PGP MESSAGE-----" in raw_payload: + if ("-----BEGIN PGP MESSAGE-----" in raw_payload and "-----END PGP MESSAGE-----" in raw_payload) or ("-----BEGIN PGP SIGNED MESSAGE-----" in raw_payload): return payload gpg = GnuPG.GPGEncryptor( cfg['gpg']['keyhome'], gpg_to_cmdline, payload.get_content_charset() ) gpg.update( raw_payload ) From c1538cfff7faa8525da5d5aea17f8bc1d522f376 Mon Sep 17 00:00:00 2001 From: perennate Date: Sun, 22 Sep 2013 15:14:48 -0400 Subject: [PATCH 15/23] Use correct content transfer encoding (7bit). --- gpg-mailgate.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gpg-mailgate.py b/gpg-mailgate.py index 8e132ef..9d99d04 100755 --- a/gpg-mailgate.py +++ b/gpg-mailgate.py @@ -70,7 +70,7 @@ def encrypt_payload( payload, gpg_to_cmdline ): payload.set_param( 'name', pgpFilename ) if payload.get('Content-Transfer-Encoding') is not None: - payload.replace_header( 'Content-Transfer-Encoding', "quoted-printable" ) + payload.replace_header( 'Content-Transfer-Encoding', "7bit" ) return payload From cfeaa79c2a7a65aaf0169eb4cd2f9819d42722d6 Mon Sep 17 00:00:00 2001 From: perennate Date: Sun, 22 Sep 2013 15:40:33 -0400 Subject: [PATCH 16/23] Add description in configuration file of each setting. --- gpg-mailgate.conf.sample | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/gpg-mailgate.conf.sample b/gpg-mailgate.conf.sample index 20cf3a2..e38c98f 100644 --- a/gpg-mailgate.conf.sample +++ b/gpg-mailgate.conf.sample @@ -1,15 +1,22 @@ [default] +# whether gpg-mailgate should add a header after it has processed an email +# this may be useful for debugging purposes add_header = yes [gpg] +# the directory where gpg-mailgate public keys are stored +# (see INSTALL for details) keyhome = /var/gpg/.gnupg [logging] -# For logging to syslog. 'file = syslog' +# For logging to syslog. 'file = syslog', otherwise use path to the file. file = /tmp/gpg-mailgate.log #verbose = yes [relay] +# the relay settings to use for Postfix +# gpg-mailgate will submit email to this relay after it is done processing +# unless you alter the default Postfix configuration, you won't have to modify this host = 127.0.0.1 port = 10028 From 1f70a634b8fd5cc51612c925d59795485a916c24 Mon Sep 17 00:00:00 2001 From: perennate Date: Mon, 23 Sep 2013 18:28:33 -0400 Subject: [PATCH 17/23] Add addendum detailing how to create a dedicated user for gpg-mailgate public keys. --- INSTALL | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/INSTALL b/INSTALL index 350eba4..77e3979 100644 --- a/INSTALL +++ b/INSTALL @@ -24,3 +24,15 @@ gpg-mailgate unix - n n - - pipe content_filter = gpg-mailgate 7) Restart postfix. + +Note 1: it is also possible to create a dedicated user to store the PGP public keys + + 1) useradd -s /bin/false -d /var/gpg -M gpgmap + 2) mkdir -p /var/gpg/.gnupg + 3) chown -R gpgmap /var/gpg + 4) chmod 700 /var/gpg/.gnupg + 5) sudo -u gpgmap /usr/bin/gpg --import /home/youruser/public.key --homedir=/var/gpg/.gnupg + a) replace the path with the location of your public key + b) the path can be deleted after importation + 6) Confirm that it's working: sudo -u gpgmap /usr/bin/gpg --list-keys --homedir=/var/gpg/.gnupg + 7) Use keyhome = /var/gpg/.gnupg in gpg-mailgate.conf From d42082b72f22f97e7fbc5e45fdcd3663a39d5f6b Mon Sep 17 00:00:00 2001 From: perennate Date: Mon, 23 Sep 2013 18:32:36 -0400 Subject: [PATCH 18/23] Update README file. --- README.md | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index c93b60e..2726445 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,10 @@ gpg-mailgate is a content filter for Postfix that automatically encrypts unencrypted incoming email using PGP for select recipients. -# TODO -- fix attachments support -- multipart messages support -- testing +For installation instructions, please refer to the included INSTALL file. + +# Features +- Correctly displays attachments and general email content; currently will only display first part of multipart messages +- Public keys can be stored in a dedicated gpg-home-directory (see Note 1 in INSTALL) +- Encrypts both matching incoming and outgoing mail (this means gpg-mailgate can be used to encrypt outgoing mail for software that doesn't support PGP) +- Easy installation From 119b6115574df02358051ca4a488480dc178b144 Mon Sep 17 00:00:00 2001 From: perennate Date: Mon, 23 Sep 2013 23:16:06 -0400 Subject: [PATCH 19/23] Remove the encrypted to addrs, since that was gpg-mailgate-specific and is no longer used. --- gpg-mailgate.py | 8 -------- 1 file changed, 8 deletions(-) diff --git a/gpg-mailgate.py b/gpg-mailgate.py index 9d99d04..98c7669 100755 --- a/gpg-mailgate.py +++ b/gpg-mailgate.py @@ -94,14 +94,6 @@ keys = GnuPG.public_keys( cfg['gpg']['keyhome'] ) gpg_to = list() ungpg_to = list() -for enc in encrypted_to_addrs: - if enc in keys: - gpg_to.append( (enc, enc) ) - elif cfg.has_key('keymap') and cfg['keymap'].has_key(enc): - gpg_to.append( (enc, cfg['keymap'][enc]) ) - else: - ungpg_to.append(enc) - for to in to_addrs: if to in keys: gpg_to.append( (to, to) ) From b6e4a321e63984314eaad9a3c4e32de8e0eb40da Mon Sep 17 00:00:00 2001 From: perennate Date: Mon, 23 Sep 2013 23:28:35 -0400 Subject: [PATCH 20/23] Add keymap_only configuration, to ignore public_keys list. This means the keymap will be exclusively used to determine which email addresses to sign with which keys. --- gpg-mailgate.conf.sample | 5 +++++ gpg-mailgate.py | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/gpg-mailgate.conf.sample b/gpg-mailgate.conf.sample index e38c98f..5d4837e 100644 --- a/gpg-mailgate.conf.sample +++ b/gpg-mailgate.conf.sample @@ -3,6 +3,11 @@ # this may be useful for debugging purposes add_header = yes +# whether we should only sign emails if they are explicitly defined in +# the key mappings below ([keymap] section) +# this means gpg-mailgate won't automatically detect PGP recipients +keymap_only = no + [gpg] # the directory where gpg-mailgate public keys are stored # (see INSTALL for details) diff --git a/gpg-mailgate.py b/gpg-mailgate.py index 98c7669..dc9123c 100755 --- a/gpg-mailgate.py +++ b/gpg-mailgate.py @@ -95,7 +95,7 @@ gpg_to = list() ungpg_to = list() for to in to_addrs: - if to in keys: + if to in keys and not ( cfg['default'].has_key('keymap_only') and cfg['default']['keymap_only'] == 'yes' ): gpg_to.append( (to, to) ) elif cfg.has_key('keymap') and cfg['keymap'].has_key(to): gpg_to.append( (to, cfg['keymap'][to]) ) From 44d94c85adae9d360cc88647423cdff1333271e2 Mon Sep 17 00:00:00 2001 From: perennate Date: Sat, 28 Sep 2013 10:28:00 -0400 Subject: [PATCH 21/23] Enable encryption for signed messages. --- gpg-mailgate.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gpg-mailgate.py b/gpg-mailgate.py index dc9123c..00f60b5 100755 --- a/gpg-mailgate.py +++ b/gpg-mailgate.py @@ -47,7 +47,7 @@ def send_msg( message, recipients = None ): def encrypt_payload( payload, gpg_to_cmdline ): gpg = GnuPG.GPGEncryptor( cfg['gpg']['keyhome'], gpg_to_cmdline, payload.get_content_charset() ) raw_payload = payload.get_payload(decode=True) - if ("-----BEGIN PGP MESSAGE-----" in raw_payload and "-----END PGP MESSAGE-----" in raw_payload) or ("-----BEGIN PGP SIGNED MESSAGE-----" in raw_payload): + if "-----BEGIN PGP MESSAGE-----" in raw_payload and "-----END PGP MESSAGE-----" in raw_payload: return payload gpg = GnuPG.GPGEncryptor( cfg['gpg']['keyhome'], gpg_to_cmdline, payload.get_content_charset() ) gpg.update( raw_payload ) From 78c6ecf44e9b24b0814214a325ada497d9ba0042 Mon Sep 17 00:00:00 2001 From: perennate Date: Sat, 28 Sep 2013 17:17:39 -0400 Subject: [PATCH 22/23] Add link to original gpg-mailgate googlecode. --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 2726445..5f91c5f 100644 --- a/README.md +++ b/README.md @@ -9,3 +9,5 @@ For installation instructions, please refer to the included INSTALL file. - Public keys can be stored in a dedicated gpg-home-directory (see Note 1 in INSTALL) - Encrypts both matching incoming and outgoing mail (this means gpg-mailgate can be used to encrypt outgoing mail for software that doesn't support PGP) - Easy installation + +This is forked from the original project at http://code.google.com/p/gpg-mailgate/ From da8559e31b049fdeff08a524257324afff0cf25e Mon Sep 17 00:00:00 2001 From: Igor Rzegocki Date: Sun, 3 Nov 2013 15:29:16 +0100 Subject: [PATCH 23/23] Added authors info, and removed duplicated code --- README.md | 11 +++++++++++ gpg-mailgate.py | 3 --- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 5f91c5f..bd97737 100644 --- a/README.md +++ b/README.md @@ -11,3 +11,14 @@ For installation instructions, please refer to the included INSTALL file. - Easy installation This is forked from the original project at http://code.google.com/p/gpg-mailgate/ + +# Authors + +This is a combined work of many developers: + +* mcmaster +* Igor Rzegocki - [GitHub](https://github.com/ajgon/gpg-mailgate) +* perennate - [GitHub](https://github.com/uakfdotb/gpg-mailgate) +* Colin Moller - [GitHub](https://github.com/LeftyBC/gpg-mailgate) +* Taylor Hornby - [GitHub](https://github.com/defuse/gpg-mailgate) +* Martin (uragit) - [GitHub](https://github.com/uragit/gpg-mailgate) diff --git a/gpg-mailgate.py b/gpg-mailgate.py index 00f60b5..74b5dc0 100755 --- a/gpg-mailgate.py +++ b/gpg-mailgate.py @@ -45,14 +45,11 @@ def send_msg( message, recipients = None ): smtp.sendmail( from_addr, recipients, message.as_string() ) def encrypt_payload( payload, gpg_to_cmdline ): - gpg = GnuPG.GPGEncryptor( cfg['gpg']['keyhome'], gpg_to_cmdline, payload.get_content_charset() ) raw_payload = payload.get_payload(decode=True) if "-----BEGIN PGP MESSAGE-----" in raw_payload and "-----END PGP MESSAGE-----" in raw_payload: return payload gpg = GnuPG.GPGEncryptor( cfg['gpg']['keyhome'], gpg_to_cmdline, payload.get_content_charset() ) gpg.update( raw_payload ) - if "-----BEGIN PGP MESSAGE-----" in raw_payload and "-----END PGP MESSAGE-----" in raw_payload: - return payload payload.set_payload( gpg.encrypt() ) isAttachment = payload.get_param( 'attachment', None, 'Content-Disposition' ) is not None