From 50f0659459169d297fa28ec8f42b7541da970175 Mon Sep 17 00:00:00 2001 From: Giovane Date: Tue, 19 Jan 2016 17:45:52 -0200 Subject: [feat] Verify plain text signed email - Extract message serialization to a method - Add new condition to verify signature on plain text mail - Return InvalidSignature if cannot verify --- src/leap/mail/incoming/service.py | 43 ++++++++++++++++++++++++++++++++------- 1 file changed, 36 insertions(+), 7 deletions(-) diff --git a/src/leap/mail/incoming/service.py b/src/leap/mail/incoming/service.py index 3896c17..1716816 100644 --- a/src/leap/mail/incoming/service.py +++ b/src/leap/mail/incoming/service.py @@ -440,6 +440,7 @@ class IncomingMail(Service): fromHeader = msg.get('from', None) senderAddress = None + if (fromHeader is not None and (msg.get_content_type() == MULTIPART_ENCRYPTED or msg.get_content_type() == MULTIPART_SIGNED)): @@ -466,6 +467,8 @@ class IncomingMail(Service): if msg.get_content_type() == MULTIPART_ENCRYPTED: d = self._decrypt_multipart_encrypted_msg( msg, encoding, senderAddress) + elif msg.get_content_type() == MULTIPART_SIGNED: + d = self._verify_signature_not_encrypted_msg(msg, senderAddress) else: d = self._maybe_decrypt_inline_encrypted_msg( msg, encoding, senderAddress) @@ -522,8 +525,8 @@ class IncomingMail(Service): return (msg, signkey) d = self._keymanager.decrypt( - encdata, self._userid, OpenPGPKey, - verify=senderAddress) + encdata, self._userid, OpenPGPKey, + verify=senderAddress) d.addCallbacks(build_msg, self._decryption_error, errbackArgs=(msg,)) return d @@ -545,11 +548,8 @@ class IncomingMail(Service): :rtype: Deferred """ log.msg('maybe decrypting inline encrypted msg') - # serialize the original message - buf = StringIO() - g = Generator(buf) - g.flatten(origmsg) - data = buf.getvalue() + + data = self._serialize_msg(origmsg) def decrypted_data(res): decrdata, signkey = res @@ -578,6 +578,35 @@ class IncomingMail(Service): d.addCallback(encode_and_return) return d + def _verify_signature_not_encrypted_msg(self, origmsg, sender_address): + """ + Possibly decrypt an inline OpenPGP encrypted message. + + :param origmsg: The original, possibly encrypted message. + :type origmsg: Message + :param sender_address: The email address of the sender of the message. + :type sender_address: str + + :return: A Deferred that will be fired with a tuple containing a + signed Message and the signing OpenPGPKey if the signature + is valid or InvalidSignature. + :rtype: Deferred + """ + msg = copy.deepcopy(origmsg) + data = msg.get_payload()[0].as_string() + detached_sig = msg.get_payload()[1].get_payload() + d = self._keymanager.verify(data, sender_address, OpenPGPKey, detached_sig) + + d.addCallback(lambda sign_key: (msg, sign_key)) + d.addErrback(lambda _: (msg, keymanager_errors.InvalidSignature())) + return d + + def _serialize_msg(self, origmsg): + buf = StringIO() + g = Generator(buf) + g.flatten(origmsg) + return buf.getvalue() + def _decryption_error(self, failure, msg): """ Check for known decryption errors -- cgit v1.2.3 From 1c3b8d861db49ee723a7470301a63a8ca2994f5c Mon Sep 17 00:00:00 2001 From: Giovane Date: Thu, 21 Jan 2016 16:31:14 -0200 Subject: [feat] Validate signature with attachments - Create a new Generator that doesn't trim the headers - Extract detached signature from message - Convert message to the body an attachments level - Add coment to the generator workaround and shows which python version has the patch --- src/leap/mail/generator.py | 21 +++++++++++++++++++++ src/leap/mail/incoming/service.py | 25 ++++++++++++++++++------- 2 files changed, 39 insertions(+), 7 deletions(-) create mode 100644 src/leap/mail/generator.py diff --git a/src/leap/mail/generator.py b/src/leap/mail/generator.py new file mode 100644 index 0000000..28db8da --- /dev/null +++ b/src/leap/mail/generator.py @@ -0,0 +1,21 @@ +from email.generator import Generator as EmailGenerator + +class Generator(EmailGenerator): + """ + Generates output from a Message object tree, keeping signatures. + + This code was extracted from Mailman.Generator.Generator, version 2.1.4: + + Most other Generator will be created not setting the foldheader flag, + as we do not overwrite clone(). The original clone() does not + set foldheaders. + + So you need to set foldheaders if you want the toplevel to fold headers + + TODO: Python 3.3 is patched against this problems. See issue 1590744 on python bug tracker. + """ + def _write_headers(self, msg): + for h, v in msg.items(): + print >> self._fp, '%s:' % h, + print >> self._fp, v + print >> self._fp diff --git a/src/leap/mail/incoming/service.py b/src/leap/mail/incoming/service.py index 1716816..49bca50 100644 --- a/src/leap/mail/incoming/service.py +++ b/src/leap/mail/incoming/service.py @@ -24,7 +24,6 @@ import time import warnings from email.parser import Parser -from email.generator import Generator from email.utils import parseaddr from email.utils import formatdate from StringIO import StringIO @@ -43,6 +42,7 @@ from leap.common.mail import get_email_charset from leap.keymanager import errors as keymanager_errors from leap.keymanager.openpgp import OpenPGPKey from leap.mail.adaptors import soledad_indexes as fields +from leap.mail.generator import Generator from leap.mail.utils import json_loads, empty from leap.soledad.client import Soledad from leap.soledad.common.crypto import ENC_SCHEME_KEY, ENC_JSON_KEY @@ -394,7 +394,7 @@ class IncomingMail(Service): # ok, this is an incoming message rawmsg = msg.get(self.CONTENT_KEY, None) - if not rawmsg: + if rawmsg is None: return "" return self._maybe_decrypt_msg(rawmsg) @@ -525,8 +525,8 @@ class IncomingMail(Service): return (msg, signkey) d = self._keymanager.decrypt( - encdata, self._userid, OpenPGPKey, - verify=senderAddress) + encdata, self._userid, OpenPGPKey, + verify=senderAddress) d.addCallbacks(build_msg, self._decryption_error, errbackArgs=(msg,)) return d @@ -593,9 +593,10 @@ class IncomingMail(Service): :rtype: Deferred """ msg = copy.deepcopy(origmsg) - data = msg.get_payload()[0].as_string() - detached_sig = msg.get_payload()[1].get_payload() - d = self._keymanager.verify(data, sender_address, OpenPGPKey, detached_sig) + data = self._serialize_msg(msg.get_payload(0)) + detached_sig = self._extract_signature(msg) + d = self._keymanager.verify(data, sender_address, OpenPGPKey, + detached_sig) d.addCallback(lambda sign_key: (msg, sign_key)) d.addErrback(lambda _: (msg, keymanager_errors.InvalidSignature())) @@ -607,6 +608,16 @@ class IncomingMail(Service): g.flatten(origmsg) return buf.getvalue() + def _extract_signature(self, msg): + body = msg.get_payload(0).get_payload() + + if isinstance(body, str): + body = msg.get_payload(0) + + detached_sig = msg.get_payload(1).get_payload() + msg.set_payload(body) + return detached_sig + def _decryption_error(self, failure, msg): """ Check for known decryption errors -- cgit v1.2.3