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() |