From 569449e633abbe22b63b9c6b8c680119a3bdceda Mon Sep 17 00:00:00 2001 From: Kali Kaneko Date: Mon, 30 Nov 2015 15:37:41 -0400 Subject: [feat] make events multi-user aware - Resolves: #7656 - Releases: 0.4.1 --- src/leap/mail/outgoing/service.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) (limited to 'src/leap/mail/outgoing') diff --git a/src/leap/mail/outgoing/service.py b/src/leap/mail/outgoing/service.py index 7cc5a24..3bd0ea2 100644 --- a/src/leap/mail/outgoing/service.py +++ b/src/leap/mail/outgoing/service.py @@ -135,7 +135,8 @@ class OutgoingMail: """ dest_addrstr = smtp_sender_result[1][0][0] log.msg('Message sent to %s' % dest_addrstr) - emit_async(catalog.SMTP_SEND_MESSAGE_SUCCESS, dest_addrstr) + emit_async(catalog.SMTP_SEND_MESSAGE_SUCCESS, + self._from_address, dest_addrstr) def sendError(self, failure): """ @@ -145,7 +146,8 @@ class OutgoingMail: :type e: anything """ # XXX: need to get the address from the exception to send signal - # emit_async(catalog.SMTP_SEND_MESSAGE_ERROR, self._user.dest.addrstr) + # emit_async(catalog.SMTP_SEND_MESSAGE_ERROR, self._from_address, + # self._user.dest.addrstr) err = failure.value log.err(err) raise err @@ -178,7 +180,8 @@ class OutgoingMail: requireAuthentication=False, requireTransportSecurity=True) factory.domain = __version__ - emit_async(catalog.SMTP_SEND_MESSAGE_START, recipient.dest.addrstr) + emit_async(catalog.SMTP_SEND_MESSAGE_START, + self._from_address, recipient.dest.addrstr) reactor.connectSSL( self._host, self._port, factory, contextFactory=SSLContextFactory(self._cert, self._key)) @@ -241,6 +244,7 @@ class OutgoingMail: def signal_encrypt_sign(newmsg): emit_async(catalog.SMTP_END_ENCRYPT_AND_SIGN, + self._from_address, "%s,%s" % (self._from_address, to_address)) return newmsg, recipient @@ -248,7 +252,7 @@ class OutgoingMail: failure.trap(KeyNotFound, KeyAddressMismatch) log.msg('Will send unencrypted message to %s.' % to_address) - emit_async(catalog.SMTP_START_SIGN, self._from_address) + emit_async(catalog.SMTP_START_SIGN, self._from_address, to_address) d = self._sign(message, from_address) d.addCallback(signal_sign) return d @@ -260,6 +264,7 @@ class OutgoingMail: log.msg("Will encrypt the message with %s and sign with %s." % (to_address, from_address)) emit_async(catalog.SMTP_START_ENCRYPT_AND_SIGN, + self._from_address, "%s,%s" % (self._from_address, to_address)) d = self._maybe_attach_key(origmsg, from_address, to_address) d.addCallback(maybe_encrypt_and_sign) -- cgit v1.2.3 From 8ddddd77478984777be79e817458183a41f0a978 Mon Sep 17 00:00:00 2001 From: Kali Kaneko Date: Thu, 26 Nov 2015 21:27:23 -0400 Subject: [feat] credentials handling: use twisted.cred --- src/leap/mail/outgoing/service.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/leap/mail/outgoing') diff --git a/src/leap/mail/outgoing/service.py b/src/leap/mail/outgoing/service.py index 3bd0ea2..7943c12 100644 --- a/src/leap/mail/outgoing/service.py +++ b/src/leap/mail/outgoing/service.py @@ -71,7 +71,7 @@ class OutgoingMail: def __init__(self, from_address, keymanager, cert, key, host, port): """ - Initialize the mail service. + Initialize the outgoing mail service. :param from_address: The sender address. :type from_address: str -- cgit v1.2.3 From ea41bb44afee34e8ad6baf917ba461a7e95bf70d Mon Sep 17 00:00:00 2001 From: Kali Kaneko Date: Wed, 16 Dec 2015 01:12:30 -0400 Subject: [feat] cred authentication for SMTP service --- src/leap/mail/outgoing/service.py | 32 ++++++++++++++++++++++++++++---- 1 file changed, 28 insertions(+), 4 deletions(-) (limited to 'src/leap/mail/outgoing') diff --git a/src/leap/mail/outgoing/service.py b/src/leap/mail/outgoing/service.py index 7943c12..3e14fbd 100644 --- a/src/leap/mail/outgoing/service.py +++ b/src/leap/mail/outgoing/service.py @@ -14,6 +14,14 @@ # # You should have received a copy of the GNU General Public License # along with this program. If not, see . + +""" +OutgoingMail module. + +The OutgoingMail class allows to send mail, and encrypts/signs it if needed. +""" + +import os.path import re from StringIO import StringIO from copy import deepcopy @@ -35,6 +43,7 @@ from leap.common.events import emit_async, catalog from leap.keymanager.openpgp import OpenPGPKey from leap.keymanager.errors import KeyNotFound, KeyAddressMismatch from leap.mail import __version__ +from leap.mail import errors from leap.mail.utils import validate_address from leap.mail.rfc3156 import MultipartEncrypted from leap.mail.rfc3156 import MultipartSigned @@ -64,9 +73,23 @@ class SSLContextFactory(ssl.ClientContextFactory): return ctx -class OutgoingMail: +def outgoingFactory(userid, keymanager, opts): + + cert = unicode(opts.cert) + key = unicode(opts.key) + hostname = str(opts.hostname) + port = opts.port + + if not os.path.isfile(cert): + raise errors.ConfigurationError( + 'No valid SMTP certificate could be found for %s!' % userid) + + return OutgoingMail(str(userid), keymanager, cert, key, hostname, port) + + +class OutgoingMail(object): """ - A service for handling encrypted outgoing mail. + Sends Outgoing Mail, encrypting and signing if needed. """ def __init__(self, from_address, keymanager, cert, key, host, port): @@ -134,9 +157,10 @@ class OutgoingMail: :type smtp_sender_result: tuple(int, list(tuple)) """ dest_addrstr = smtp_sender_result[1][0][0] - log.msg('Message sent to %s' % dest_addrstr) + fromaddr = self._from_address + log.msg('Message sent from %s to %s' % (fromaddr, dest_addrstr)) emit_async(catalog.SMTP_SEND_MESSAGE_SUCCESS, - self._from_address, dest_addrstr) + fromaddr, dest_addrstr) def sendError(self, failure): """ -- cgit v1.2.3 From f87b25c7b942f509372a14ed0ee7073f8f17e053 Mon Sep 17 00:00:00 2001 From: Kali Kaneko Date: Thu, 17 Dec 2015 01:34:26 -0400 Subject: [tests] make tests use dummy authentication --- src/leap/mail/outgoing/service.py | 9 +++++---- src/leap/mail/outgoing/tests/test_outgoing.py | 24 ++++++++++++------------ 2 files changed, 17 insertions(+), 16 deletions(-) (limited to 'src/leap/mail/outgoing') diff --git a/src/leap/mail/outgoing/service.py b/src/leap/mail/outgoing/service.py index 3e14fbd..8d7c0f8 100644 --- a/src/leap/mail/outgoing/service.py +++ b/src/leap/mail/outgoing/service.py @@ -73,16 +73,17 @@ class SSLContextFactory(ssl.ClientContextFactory): return ctx -def outgoingFactory(userid, keymanager, opts): +def outgoingFactory(userid, keymanager, opts, check_cert=True): cert = unicode(opts.cert) key = unicode(opts.key) hostname = str(opts.hostname) port = opts.port - if not os.path.isfile(cert): - raise errors.ConfigurationError( - 'No valid SMTP certificate could be found for %s!' % userid) + if check_cert: + if not os.path.isfile(cert): + raise errors.ConfigurationError( + 'No valid SMTP certificate could be found for %s!' % userid) return OutgoingMail(str(userid), keymanager, cert, key, hostname, port) diff --git a/src/leap/mail/outgoing/tests/test_outgoing.py b/src/leap/mail/outgoing/tests/test_outgoing.py index 5518b33..79eafd9 100644 --- a/src/leap/mail/outgoing/tests/test_outgoing.py +++ b/src/leap/mail/outgoing/tests/test_outgoing.py @@ -29,20 +29,19 @@ from twisted.mail.smtp import User from mock import Mock -from leap.mail.smtp.gateway import SMTPFactory from leap.mail.rfc3156 import RFC3156CompliantGenerator from leap.mail.outgoing.service import OutgoingMail -from leap.mail.tests import ( - TestCaseWithKeyManager, - ADDRESS, - ADDRESS_2, - PUBLIC_KEY_2, -) +from leap.mail.tests import TestCaseWithKeyManager +from leap.mail.tests import ADDRESS, ADDRESS_2, PUBLIC_KEY_2 +from leap.mail.smtp.tests.test_gateway import getSMTPFactory + from leap.keymanager import openpgp, errors BEGIN_PUBLIC_KEY = "-----BEGIN PGP PUBLIC KEY BLOCK-----" +TEST_USER = u'anotheruser@leap.se' + class TestOutgoingMail(TestCaseWithKeyManager): EMAIL_DATA = ['HELO gateway.leap.se', @@ -73,11 +72,12 @@ class TestOutgoingMail(TestCaseWithKeyManager): self.fromAddr, self._km, self._config['cert'], self._config['key'], self._config['host'], self._config['port']) - self.proto = SMTPFactory( - u'anotheruser@leap.se', - self._km, - self._config['encrypted_only'], - self.outgoing_mail).buildProtocol(('127.0.0.1', 0)) + + user = TEST_USER + + # TODO -- this shouldn't need SMTP to be tested!? or does it? + self.proto = getSMTPFactory( + {user: None}, {user: self._km}, {user: None}) self.dest = User(ADDRESS, 'gateway.leap.se', self.proto, ADDRESS_2) d = TestCaseWithKeyManager.setUp(self) -- cgit v1.2.3 From b962e9bc7973f3a826dccfa9aa417b0139b94104 Mon Sep 17 00:00:00 2001 From: Ruben Pollan Date: Tue, 2 Feb 2016 18:10:26 +0100 Subject: [bug] Use the right succeed function for passthrough encrypted email - Resolves #7861 --- src/leap/mail/outgoing/service.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/leap/mail/outgoing') diff --git a/src/leap/mail/outgoing/service.py b/src/leap/mail/outgoing/service.py index 8d7c0f8..8e06bd4 100644 --- a/src/leap/mail/outgoing/service.py +++ b/src/leap/mail/outgoing/service.py @@ -254,7 +254,7 @@ class OutgoingMail(object): origmsg = Parser().parsestr(raw) if origmsg.get_content_type() == 'multipart/encrypted': - return defer.success((origmsg, recipient)) + return defer.succeed((origmsg, recipient)) from_address = validate_address(self._from_address) username, domain = from_address.split('@') -- cgit v1.2.3 From 8a828b7a2158c01215c3cbab006caa694a41af03 Mon Sep 17 00:00:00 2001 From: Ruben Pollan Date: Mon, 21 Dec 2015 15:35:56 +0100 Subject: [feat] use fingerprint instead of key_id to address keys --- src/leap/mail/outgoing/service.py | 2 +- src/leap/mail/outgoing/tests/test_outgoing.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'src/leap/mail/outgoing') diff --git a/src/leap/mail/outgoing/service.py b/src/leap/mail/outgoing/service.py index 8e06bd4..eeb5d32 100644 --- a/src/leap/mail/outgoing/service.py +++ b/src/leap/mail/outgoing/service.py @@ -487,7 +487,7 @@ class OutgoingMail(object): def add_openpgp_header(signkey): username, domain = sign_address.split('@') newmsg.add_header( - 'OpenPGP', 'id=%s' % signkey.key_id, + 'OpenPGP', 'id=%s' % signkey.fingerprint, url='https://%s/key/%s' % (domain, username), preference='signencrypt') return newmsg, origmsg diff --git a/src/leap/mail/outgoing/tests/test_outgoing.py b/src/leap/mail/outgoing/tests/test_outgoing.py index 79eafd9..ad7803d 100644 --- a/src/leap/mail/outgoing/tests/test_outgoing.py +++ b/src/leap/mail/outgoing/tests/test_outgoing.py @@ -236,7 +236,7 @@ class TestOutgoingMail(TestCaseWithKeyManager): def _set_sign_used(self, address): def set_sign(key): key.sign_used = True - return self._km.put_key(key, address) + return self._km.put_key(key) d = self._km.get_key(address, openpgp.OpenPGPKey, fetch_remote=False) d.addCallback(set_sign) -- cgit v1.2.3 From f05b0ec45ac667e85a610219d77906f049eb58cc Mon Sep 17 00:00:00 2001 From: Kali Kaneko Date: Tue, 29 Mar 2016 17:21:43 -0400 Subject: [feature] SMTP delivery bounces We catch any error on SMTP delivery and format it as a bounce message delivered to the user Inbox. this doesn't comply with the bounce format, but it's a nice first start. leaving proper structuring of the delivery failure report for future iterations. - Resolves: #7263 --- src/leap/mail/outgoing/service.py | 44 ++++++++++++++++++++++++++++----------- 1 file changed, 32 insertions(+), 12 deletions(-) (limited to 'src/leap/mail/outgoing') diff --git a/src/leap/mail/outgoing/service.py b/src/leap/mail/outgoing/service.py index eeb5d32..335cae4 100644 --- a/src/leap/mail/outgoing/service.py +++ b/src/leap/mail/outgoing/service.py @@ -73,7 +73,7 @@ class SSLContextFactory(ssl.ClientContextFactory): return ctx -def outgoingFactory(userid, keymanager, opts, check_cert=True): +def outgoingFactory(userid, keymanager, opts, check_cert=True, bouncer=None): cert = unicode(opts.cert) key = unicode(opts.key) @@ -85,7 +85,9 @@ def outgoingFactory(userid, keymanager, opts, check_cert=True): raise errors.ConfigurationError( 'No valid SMTP certificate could be found for %s!' % userid) - return OutgoingMail(str(userid), keymanager, cert, key, hostname, port) + return OutgoingMail( + str(userid), keymanager, cert, key, hostname, port, + bouncer) class OutgoingMail(object): @@ -93,7 +95,8 @@ class OutgoingMail(object): Sends Outgoing Mail, encrypting and signing if needed. """ - def __init__(self, from_address, keymanager, cert, key, host, port): + def __init__(self, from_address, keymanager, cert, key, host, port, + bouncer=None): """ Initialize the outgoing mail service. @@ -133,6 +136,7 @@ class OutgoingMail(object): self._cert = cert self._from_address = from_address self._keymanager = keymanager + self._bouncer = bouncer def send_message(self, raw, recipient): """ @@ -145,8 +149,8 @@ class OutgoingMail(object): :return: a deferred which delivers the message when fired """ d = self._maybe_encrypt_and_sign(raw, recipient) - d.addCallback(self._route_msg) - d.addErrback(self.sendError) + d.addCallback(self._route_msg, raw) + d.addErrback(self.sendError, raw) return d def sendSuccess(self, smtp_sender_result): @@ -163,21 +167,36 @@ class OutgoingMail(object): emit_async(catalog.SMTP_SEND_MESSAGE_SUCCESS, fromaddr, dest_addrstr) - def sendError(self, failure): + def sendError(self, failure, origmsg): """ Callback for an unsuccessfull send. - :param e: The result from the last errback. - :type e: anything + :param failure: The result from the last errback. + :type failure: anything + :param origmsg: the original, unencrypted, raw message, to be passed to + the bouncer. + :type origmsg: str """ - # XXX: need to get the address from the exception to send signal + # XXX: need to get the address from the original message to send signal # emit_async(catalog.SMTP_SEND_MESSAGE_ERROR, self._from_address, # self._user.dest.addrstr) + + # TODO when we implement outgoing queues/long-term-retries, we could + # examine the error *here* and delay the notification if it's just a + # temporal error. We might want to notify the permanent errors + # differently. + err = failure.value log.err(err) - raise err - def _route_msg(self, encrypt_and_sign_result): + if self._bouncer: + self._bouncer.bounce_message( + err.message, to=self._from_address, + orig=origmsg) + else: + raise err + + def _route_msg(self, encrypt_and_sign_result, raw): """ Sends the msg using the ESMTPSenderFactory. @@ -191,7 +210,8 @@ class OutgoingMail(object): # we construct a defer to pass to the ESMTPSenderFactory d = defer.Deferred() - d.addCallbacks(self.sendSuccess, self.sendError) + d.addCallback(self.sendSuccess) + d.addErrback(self.sendError, raw) # we don't pass an ssl context factory to the ESMTPSenderFactory # because ssl will be handled by reactor.connectSSL() below. factory = smtp.ESMTPSenderFactory( -- cgit v1.2.3