diff options
Diffstat (limited to 'mail/src/leap')
| -rw-r--r-- | mail/src/leap/mail/outgoing/service.py | 42 | ||||
| -rw-r--r-- | mail/src/leap/mail/outgoing/tests/test_outgoing.py | 55 | ||||
| -rw-r--r-- | mail/src/leap/mail/smtp/gateway.py | 1 | 
3 files changed, 92 insertions, 6 deletions
| diff --git a/mail/src/leap/mail/outgoing/service.py b/mail/src/leap/mail/outgoing/service.py index 88b8895..9777187 100644 --- a/mail/src/leap/mail/outgoing/service.py +++ b/mail/src/leap/mail/outgoing/service.py @@ -18,6 +18,8 @@ import re  from StringIO import StringIO  from email.parser import Parser  from email.mime.application import MIMEApplication +from email.mime.multipart import MIMEMultipart +from email.mime.text import MIMEText  from OpenSSL import SSL @@ -250,10 +252,48 @@ class OutgoingMail:                  % (to_address, from_address))          signal(proto.SMTP_START_ENCRYPT_AND_SIGN,                 "%s,%s" % (self._from_address, to_address)) -        d = self._encrypt_and_sign(origmsg, to_address, from_address) +        d = self._maybe_attach_key(origmsg, from_address, to_address) +        d.addCallback(self._encrypt_and_sign, to_address, from_address)          d.addCallbacks(signal_encrypt_sign, if_key_not_found_send_unencrypted)          return d +    def _maybe_attach_key(self, origmsg, from_address, to_address): +        filename = "%s-email-key.asc" % (from_address,) + +        def attach_if_address_hasnt_encrypted(to_key): +            # if the sign_used flag is true that means that we got an encrypted +            # email from this address, because we conly check signatures on +            # encrypted emails. In this case we don't attach. +            # XXX: this might not be true some time in the future +            if to_key.sign_used: +                return origmsg + +            d = self._keymanager.get_key(from_address, OpenPGPKey, +                                         fetch_remote=False) +            d.addCallback(attach_key) +            return d + +        def attach_key(from_key): +            msg = origmsg +            if not origmsg.is_multipart(): +                msg = MIMEMultipart() +                for h, v in origmsg.items(): +                    msg.add_header(h, v) +                msg.attach(MIMEText(origmsg.get_payload())) + +            keymsg = MIMEApplication(from_key.key_data, _subtype='pgp-keys', +                                     _encoder=lambda x: x) +            keymsg.add_header('content-disposition', 'attachment', +                              filename=filename) +            msg.attach(keymsg) +            return msg + +        d = self._keymanager.get_key(to_address, OpenPGPKey, +                                     fetch_remote=False) +        d.addCallback(attach_if_address_hasnt_encrypted) +        d.addErrback(lambda _: origmsg) +        return d +      def _encrypt_and_sign(self, origmsg, encrypt_address, sign_address):          """          Create an RFC 3156 compliang PGP encrypted and signed message using diff --git a/mail/src/leap/mail/outgoing/tests/test_outgoing.py b/mail/src/leap/mail/outgoing/tests/test_outgoing.py index d7423b6..0eb05c8 100644 --- a/mail/src/leap/mail/outgoing/tests/test_outgoing.py +++ b/mail/src/leap/mail/outgoing/tests/test_outgoing.py @@ -21,6 +21,7 @@ SMTP gateway tests.  """  import re +from email.parser import Parser  from datetime import datetime  from twisted.internet.defer import fail  from twisted.mail.smtp import User @@ -33,10 +34,14 @@ from leap.mail.tests import (      TestCaseWithKeyManager,      ADDRESS,      ADDRESS_2, +    PUBLIC_KEY_2,  )  from leap.keymanager import openpgp, errors +BEGIN_PUBLIC_KEY = "-----BEGIN PGP PUBLIC KEY BLOCK-----" + +  class TestOutgoingMail(TestCaseWithKeyManager):      EMAIL_DATA = ['HELO gateway.leap.se',                    'MAIL FROM: <%s>' % ADDRESS_2, @@ -71,7 +76,7 @@ class TestOutgoingMail(TestCaseWithKeyManager):                  self._km,                  self._config['encrypted_only'],                  self.outgoing_mail).buildProtocol(('127.0.0.1', 0)) -            self.dest = User(ADDRESS, 'gateway.leap.se', self.proto, ADDRESS) +            self.dest = User(ADDRESS, 'gateway.leap.se', self.proto, ADDRESS_2)          d = TestCaseWithKeyManager.setUp(self)          d.addCallback(init_outgoing_and_proto) @@ -88,7 +93,10 @@ class TestOutgoingMail(TestCaseWithKeyManager):                  decrypted,                  'Decrypted text differs from plaintext.') -        d = self.outgoing_mail._maybe_encrypt_and_sign(self.raw, self.dest) +        d = self._set_sign_used(ADDRESS) +        d.addCallback( +            lambda _: +            self.outgoing_mail._maybe_encrypt_and_sign(self.raw, self.dest))          d.addCallback(self._assert_encrypted)          d.addCallback(lambda message: self._km.decrypt(              message.get_payload(1).get_payload(), ADDRESS, openpgp.OpenPGPKey)) @@ -109,7 +117,10 @@ class TestOutgoingMail(TestCaseWithKeyManager):              self.assertTrue(ADDRESS_2 in signkey.address,                              "Verification failed") -        d = self.outgoing_mail._maybe_encrypt_and_sign(self.raw, self.dest) +        d = self._set_sign_used(ADDRESS) +        d.addCallback( +            lambda _: +            self.outgoing_mail._maybe_encrypt_and_sign(self.raw, self.dest))          d.addCallback(self._assert_encrypted)          d.addCallback(lambda message: self._km.decrypt(              message.get_payload(1).get_payload(), ADDRESS, openpgp.OpenPGPKey, @@ -158,7 +169,7 @@ class TestOutgoingMail(TestCaseWithKeyManager):              def assert_verify(key):                  self.assertTrue(ADDRESS_2 in key.address, -                                 'Signature could not be verified.') +                                'Signature could not be verified.')              d = self._km.verify(                  signed_text, ADDRESS_2, openpgp.OpenPGPKey, @@ -171,6 +182,42 @@ class TestOutgoingMail(TestCaseWithKeyManager):          d.addCallback(verify)          return d +    def test_attach_key(self): +        def check_headers(message): +            msgstr = message.as_string(unixfrom=False) +            for header in self.EMAIL_DATA[4:8]: +                self.assertTrue(header in msgstr, +                                "Missing header: %s" % (header,)) +            return message + +        def check_attachment((decrypted, _)): +            msg = Parser().parsestr(decrypted) +            for payload in msg.get_payload(): +                if 'application/pgp-keys' == payload.get_content_type(): +                    keylines = PUBLIC_KEY_2.split('\n') +                    key = BEGIN_PUBLIC_KEY + '\n\n' + '\n'.join(keylines[4:-1]) +                    self.assertTrue(key in payload.get_payload(), +                                    "Key attachment don't match") +                    return +            self.fail("No public key attachment found") + +        d = self.outgoing_mail._maybe_encrypt_and_sign(self.raw, self.dest) +        d.addCallback(self._assert_encrypted) +        d.addCallback(check_headers) +        d.addCallback(lambda message: self._km.decrypt( +            message.get_payload(1).get_payload(), ADDRESS, openpgp.OpenPGPKey)) +        d.addCallback(check_attachment) +        return d + +    def _set_sign_used(self, address): +        def set_sign(key): +            key.sign_used = True +            return self._km.put_key(key, address) + +        d = self._km.get_key(address, openpgp.OpenPGPKey, fetch_remote=False) +        d.addCallback(set_sign) +        return d +      def _assert_encrypted(self, res):          message, _ = res          self.assertTrue('Content-Type' in message) diff --git a/mail/src/leap/mail/smtp/gateway.py b/mail/src/leap/mail/smtp/gateway.py index 9d78474..954a7d0 100644 --- a/mail/src/leap/mail/smtp/gateway.py +++ b/mail/src/leap/mail/smtp/gateway.py @@ -309,5 +309,4 @@ class EncryptedMessage(object):          signal(proto.SMTP_CONNECTION_LOST, self._user.dest.addrstr)          # unexpected loss of connection; don't save -          self._lines = [] | 
