diff options
Diffstat (limited to 'src/leap/mail/smtp')
| -rw-r--r-- | src/leap/mail/smtp/__init__.py | 6 | ||||
| -rw-r--r-- | src/leap/mail/smtp/gateway.py | 16 | ||||
| -rw-r--r-- | src/leap/mail/smtp/rfc3156.py | 31 | ||||
| -rw-r--r-- | src/leap/mail/smtp/tests/test_gateway.py | 60 | 
4 files changed, 75 insertions, 38 deletions
diff --git a/src/leap/mail/smtp/__init__.py b/src/leap/mail/smtp/__init__.py index d3eb9e8..bbd4064 100644 --- a/src/leap/mail/smtp/__init__.py +++ b/src/leap/mail/smtp/__init__.py @@ -30,7 +30,7 @@ from leap.mail.smtp.gateway import SMTPFactory  def setup_smtp_gateway(port, userid, keymanager, smtp_host, smtp_port, -                     smtp_cert, smtp_key, encrypted_only): +                       smtp_cert, smtp_key, encrypted_only):      """      Setup SMTP gateway to run with Twisted. @@ -52,8 +52,8 @@ def setup_smtp_gateway(port, userid, keymanager, smtp_host, smtp_port,      :type smtp_cert: str      :param smtp_key: The client key for authentication.      :type smtp_key: str -    :param encrypted_only: Whether the SMTP gateway should send unencrypted mail -                           or not. +    :param encrypted_only: Whether the SMTP gateway should send unencrypted +                           mail or not.      :type encrypted_only: bool      :returns: tuple of SMTPFactory, twisted.internet.tcp.Port diff --git a/src/leap/mail/smtp/gateway.py b/src/leap/mail/smtp/gateway.py index a78bd55..ef398d1 100644 --- a/src/leap/mail/smtp/gateway.py +++ b/src/leap/mail/smtp/gateway.py @@ -52,6 +52,7 @@ from leap.common.events import proto, signal  from leap.keymanager import KeyManager  from leap.keymanager.openpgp import OpenPGPKey  from leap.keymanager.errors import KeyNotFound +from leap.mail import __version__  from leap.mail.smtp.rfc3156 import (      MultipartSigned,      MultipartEncrypted, @@ -492,7 +493,7 @@ class EncryptedMessage(object):              heloFallback=True,              requireAuthentication=False,              requireTransportSecurity=True) -        factory.domain = LOCAL_FQDN +        factory.domain = __version__          signal(proto.SMTP_SEND_MESSAGE_START, self._user.dest.addrstr)          reactor.connectSSL(              self._host, self._port, factory, @@ -599,13 +600,16 @@ class EncryptedMessage(object):              self._msg = self._origmsg              return -        # add a nice footer to the outgoing message          from_address = validate_address(self._fromAddress.addrstr)          username, domain = from_address.split('@') -        self.lines.append('--') -        self.lines.append('%s - https://%s/key/%s.' % -                          (self.FOOTER_STRING, domain, username)) -        self.lines.append('') + +        # add a nice footer to the outgoing message +        if self._origmsg.get_content_type() == 'text/plain': +            self.lines.append('--') +            self.lines.append('%s - https://%s/key/%s' % +                              (self.FOOTER_STRING, domain, username)) +            self.lines.append('') +          self._origmsg = self.parseMessage()          # get sender and recipient data diff --git a/src/leap/mail/smtp/rfc3156.py b/src/leap/mail/smtp/rfc3156.py index dd48475..62a0675 100644 --- a/src/leap/mail/smtp/rfc3156.py +++ b/src/leap/mail/smtp/rfc3156.py @@ -24,6 +24,7 @@ import base64  from abc import ABCMeta, abstractmethod  from StringIO import StringIO +from twisted.python import log  from email.mime.application import MIMEApplication  from email.mime.multipart import MIMEMultipart  from email import errors @@ -145,14 +146,26 @@ def encode_base64(msg):      :param msg: The non-multipart message to be encoded.      :type msg: email.message.Message      """ -    orig = msg.get_payload() -    encdata = _bencode(orig) -    msg.set_payload(encdata) -    # replace or set the Content-Transfer-Encoding header. -    try: -        msg.replace_header('Content-Transfer-Encoding', 'base64') -    except KeyError: -        msg['Content-Transfer-Encoding'] = 'base64' +    encoding = msg.get('Content-Transfer-Encoding', None) +    if encoding is not None: +        encoding = encoding.lower() +    # XXX Python's email module can only decode quoted-printable, base64 and +    # uuencoded data, so we might have to implement other decoding schemes in +    # order to support RFC 3156 properly and correctly calculate signatures +    # for multipart attachments (eg. 7bit or 8bit encoded attachments). For +    # now, if content is already encoded as base64 or if it is encoded with +    # some unknown encoding, we just pass. +    if encoding in [None, 'quoted-printable', 'x-uuencode', 'uue', 'x-uue']: +        orig = msg.get_payload(decode=True) +        encdata = _bencode(orig) +        msg.set_payload(encdata) +        # replace or set the Content-Transfer-Encoding header. +        try: +            msg.replace_header('Content-Transfer-Encoding', 'base64') +        except KeyError: +            msg['Content-Transfer-Encoding'] = 'base64' +    elif encoding is not 'base64': +        log.err('Unknown content-transfer-encoding: %s' % encoding)  def encode_base64_rec(msg): @@ -361,7 +374,7 @@ class PGPSignature(MIMEApplication):      """      def __init__(self, _data, name='signature.asc'):          MIMEApplication.__init__(self, _data, 'pgp-signature', -            _encoder=lambda x: x, name=name) +                                 _encoder=lambda x: x, name=name)          self.add_header('Content-Description', 'OpenPGP Digital Signature') diff --git a/src/leap/mail/smtp/tests/test_gateway.py b/src/leap/mail/smtp/tests/test_gateway.py index 4c2f04f..88ee5f7 100644 --- a/src/leap/mail/smtp/tests/test_gateway.py +++ b/src/leap/mail/smtp/tests/test_gateway.py @@ -101,10 +101,16 @@ class TestSmtpGateway(TestCaseWithKeyManager):                          '250 Sender address accepted',                          '250 Recipient address accepted',                          '354 Continue'] -        proto = SMTPFactory(u'anotheruser@leap.se', -            self._km, self._config['host'], self._config['port'], + +        # XXX this bit can be refactored away in a helper +        # method... +        proto = SMTPFactory( +            u'anotheruser@leap.se', +            self._km, self._config['host'], +            self._config['port'],              self._config['cert'], self._config['key'],              self._config['encrypted_only']).buildProtocol(('127.0.0.1', 0)) +        # snip...          transport = proto_helpers.StringTransport()          proto.makeConnection(transport)          for i, line in enumerate(self.EMAIL_DATA): @@ -118,8 +124,10 @@ class TestSmtpGateway(TestCaseWithKeyManager):          """          Test if message gets encrypted to destination email.          """ -        proto = SMTPFactory(u'anotheruser@leap.se', -            self._km, self._config['host'], self._config['port'], +        proto = SMTPFactory( +            u'anotheruser@leap.se', +            self._km, self._config['host'], +            self._config['port'],              self._config['cert'], self._config['key'],              self._config['encrypted_only']).buildProtocol(('127.0.0.1', 0))          fromAddr = Address(ADDRESS_2) @@ -129,7 +137,8 @@ class TestSmtpGateway(TestCaseWithKeyManager):              self._config['port'], self._config['cert'], self._config['key'])          for line in self.EMAIL_DATA[4:12]:              m.lineReceived(line) -        m.eomReceived() +        #m.eomReceived()  # this includes a defer, so we avoid calling it here +        m.lines.append('')  # add a trailing newline          # we need to call the following explicitelly because it was deferred          # inside the previous method          m._maybe_encrypt_and_sign() @@ -149,7 +158,7 @@ class TestSmtpGateway(TestCaseWithKeyManager):              m._msg.get_payload(1).get_payload(), privkey)          self.assertEqual(              '\n' + '\r\n'.join(self.EMAIL_DATA[9:12]) + '\r\n\r\n--\r\n' + -            'I prefer encrypted email - https://leap.se/key/anotheruser.\r\n', +            'I prefer encrypted email - https://leap.se/key/anotheruser\r\n',              decrypted,              'Decrypted text differs from plaintext.') @@ -158,8 +167,10 @@ class TestSmtpGateway(TestCaseWithKeyManager):          Test if message gets encrypted to destination email and signed with          sender key.          """ -        proto = SMTPFactory(u'anotheruser@leap.se', -            self._km, self._config['host'], self._config['port'], +        proto = SMTPFactory( +            u'anotheruser@leap.se', +            self._km, self._config['host'], +            self._config['port'],              self._config['cert'], self._config['key'],              self._config['encrypted_only']).buildProtocol(('127.0.0.1', 0))          user = User(ADDRESS, 'gateway.leap.se', proto, ADDRESS) @@ -170,7 +181,8 @@ class TestSmtpGateway(TestCaseWithKeyManager):          for line in self.EMAIL_DATA[4:12]:              m.lineReceived(line)          # trigger encryption and signing -        m.eomReceived() +        #m.eomReceived()  # this includes a defer, so we avoid calling it here +        m.lines.append('')  # add a trailing newline          # we need to call the following explicitelly because it was deferred          # inside the previous method          m._maybe_encrypt_and_sign() @@ -192,7 +204,7 @@ class TestSmtpGateway(TestCaseWithKeyManager):              m._msg.get_payload(1).get_payload(), privkey, verify=pubkey)          self.assertEqual(              '\n' + '\r\n'.join(self.EMAIL_DATA[9:12]) + '\r\n\r\n--\r\n' + -            'I prefer encrypted email - https://leap.se/key/anotheruser.\r\n', +            'I prefer encrypted email - https://leap.se/key/anotheruser\r\n',              decrypted,              'Decrypted text differs from plaintext.') @@ -202,11 +214,14 @@ class TestSmtpGateway(TestCaseWithKeyManager):          """          # mock the key fetching          self._km.fetch_keys_from_server = Mock(return_value=[]) -        proto = SMTPFactory(u'anotheruser@leap.se', -            self._km, self._config['host'], self._config['port'], +        proto = SMTPFactory( +            u'anotheruser@leap.se', +            self._km, self._config['host'], +            self._config['port'],              self._config['cert'], self._config['key'],              self._config['encrypted_only']).buildProtocol(('127.0.0.1', 0)) -        user = User('ihavenopubkey@nonleap.se', 'gateway.leap.se', proto, ADDRESS) +        user = User('ihavenopubkey@nonleap.se', +                    'gateway.leap.se', proto, ADDRESS)          fromAddr = Address(ADDRESS_2)          m = EncryptedMessage(              fromAddr, user, self._km, self._config['host'], @@ -214,7 +229,8 @@ class TestSmtpGateway(TestCaseWithKeyManager):          for line in self.EMAIL_DATA[4:12]:              m.lineReceived(line)          # trigger signing -        m.eomReceived() +        #m.eomReceived()  # this includes a defer, so we avoid calling it here +        m.lines.append('')  # add a trailing newline          # we need to call the following explicitelly because it was deferred          # inside the previous method          m._maybe_encrypt_and_sign() @@ -226,8 +242,8 @@ class TestSmtpGateway(TestCaseWithKeyManager):          self.assertEqual('pgp-sha512', m._msg.get_param('micalg'))          # assert content of message          self.assertEqual( -            '\r\n'.join(self.EMAIL_DATA[9:13])+'\r\n--\r\n' + -            'I prefer encrypted email - https://leap.se/key/anotheruser.\r\n', +            '\r\n'.join(self.EMAIL_DATA[9:13]) + '\r\n--\r\n' + +            'I prefer encrypted email - https://leap.se/key/anotheruser\r\n',              m._msg.get_payload(0).get_payload(decode=True))          # assert content of signature          self.assertTrue( @@ -262,8 +278,10 @@ class TestSmtpGateway(TestCaseWithKeyManager):          # mock the key fetching          self._km.fetch_keys_from_server = Mock(return_value=[])          # prepare the SMTP factory -        proto = SMTPFactory(u'anotheruser@leap.se', -            self._km, self._config['host'], self._config['port'], +        proto = SMTPFactory( +            u'anotheruser@leap.se', +            self._km, self._config['host'], +            self._config['port'],              self._config['cert'], self._config['key'],              self._config['encrypted_only']).buildProtocol(('127.0.0.1', 0))          transport = proto_helpers.StringTransport() @@ -291,8 +309,10 @@ class TestSmtpGateway(TestCaseWithKeyManager):          # mock the key fetching          self._km.fetch_keys_from_server = Mock(return_value=[])          # prepare the SMTP factory with encrypted only equal to false -        proto = SMTPFactory(u'anotheruser@leap.se', -            self._km, self._config['host'], self._config['port'], +        proto = SMTPFactory( +            u'anotheruser@leap.se', +            self._km, self._config['host'], +            self._config['port'],              self._config['cert'], self._config['key'],              False).buildProtocol(('127.0.0.1', 0))          transport = proto_helpers.StringTransport()  | 
