summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/leap/mail/generator.py21
-rw-r--r--src/leap/mail/incoming/service.py54
2 files changed, 68 insertions, 7 deletions
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 3896c17..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)
@@ -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)
@@ -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,46 @@ 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 = 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()))
+ return d
+
+ def _serialize_msg(self, origmsg):
+ buf = StringIO()
+ g = Generator(buf)
+ 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