diff options
| -rw-r--r-- | src/leap/mail/outgoing/service.py | 9 | ||||
| -rw-r--r-- | src/leap/mail/outgoing/tests/test_outgoing.py | 24 | ||||
| -rw-r--r-- | src/leap/mail/smtp/gateway.py | 43 | ||||
| -rw-r--r-- | src/leap/mail/smtp/tests/test_gateway.py | 91 | 
4 files changed, 112 insertions, 55 deletions
| 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) diff --git a/src/leap/mail/smtp/gateway.py b/src/leap/mail/smtp/gateway.py index 85b1560..7ff6b14 100644 --- a/src/leap/mail/smtp/gateway.py +++ b/src/leap/mail/smtp/gateway.py @@ -65,7 +65,8 @@ class LocalSMTPRealm(object):      _encoding = 'utf-8' -    def __init__(self, keymanager_sessions, sendmail_opts): +    def __init__(self, keymanager_sessions, sendmail_opts, +                 encrypted_only=False):          """          :param keymanager_sessions: a dict-like object, containing instances                                   of a Keymanager objects, indexed by @@ -73,6 +74,7 @@ class LocalSMTPRealm(object):          """          self._keymanager_sessions = keymanager_sessions          self._sendmail_opts = sendmail_opts +        self.encrypted_only = encrypted_only      def requestAvatar(self, avatarId, mind, *interfaces):          if isinstance(avatarId, str): @@ -86,7 +88,8 @@ class LocalSMTPRealm(object):                  userid = avatarId                  opts = self.getSendingOpts(userid)                  outgoing = outgoingFactory(userid, keymanager, opts) -                avatar = SMTPDelivery(userid, keymanager, False, outgoing) +                avatar = SMTPDelivery(userid, keymanager, self.encrypted_only, +                                      outgoing)                  return (smtp.IMessageDelivery, avatar,                          getattr(avatar, 'logout', lambda: None)) @@ -123,22 +126,41 @@ class SMTPTokenChecker(LocalSoledadTokenChecker):      # we could also verify the certificate here. -# TODO -- implement Queue using twisted.mail.mail.MailService -class LocalSMTPServer(smtp.ESMTP): +class LEAPInitMixin(object): +    """ +    A Mixin that takes care of initialization of all the data needed to access +    LEAP sessions. +    """      def __init__(self, soledad_sessions, keymanager_sessions, sendmail_opts, -                 *args, **kw): - -        smtp.ESMTP.__init__(self, *args, **kw) - -        realm = LocalSMTPRealm(keymanager_sessions, sendmail_opts) +                 encrypted_only=False): +        realm = LocalSMTPRealm(keymanager_sessions, sendmail_opts, +                               encrypted_only)          portal = Portal(realm) +          checker = SMTPTokenChecker(soledad_sessions)          self.checker = checker          self.portal = portal          portal.registerChecker(checker) +class LocalSMTPServer(smtp.ESMTP, LEAPInitMixin): +    """ +    The Production ESMTP Server: Authentication Needed. +    Authenticates against SMTP Token stored in Local Soledad instance. +    The Realm will produce a Delivery Object that handles encryption/signing. +    """ + +    # TODO: implement Queue using twisted.mail.mail.MailService + +    def __init__(self, soledads, keyms, sendmailopts, *args, **kw): +        encrypted_only = kw.pop('encrypted_only', False) + +        LEAPInitMixin.__init__(self, soledads, keyms, sendmailopts, +                               encrypted_only) +        smtp.ESMTP.__init__(self, *args, **kw) + +  class SMTPFactory(protocol.ServerFactory):      """      Factory for an SMTP server with encrypted gatewaying capabilities. @@ -147,6 +169,7 @@ class SMTPFactory(protocol.ServerFactory):      protocol = LocalSMTPServer      domain = LOCAL_FQDN      timeout = 600 +    encrypted_only = False      def __init__(self, soledad_sessions, keymanager_sessions, sendmail_opts):          self._soledad_sessions = soledad_sessions @@ -156,7 +179,7 @@ class SMTPFactory(protocol.ServerFactory):      def buildProtocol(self, addr):          p = self.protocol(              self._soledad_sessions, self._keymanager_sessions, -            self._sendmail_opts) +            self._sendmail_opts, encrypted_only=self.encrypted_only)          p.factory = self          p.host = LOCAL_FQDN          p.challengers = {"LOGIN": LOGINCredentials, "PLAIN": PLAINCredentials} diff --git a/src/leap/mail/smtp/tests/test_gateway.py b/src/leap/mail/smtp/tests/test_gateway.py index 0b9a364..df83cf0 100644 --- a/src/leap/mail/smtp/tests/test_gateway.py +++ b/src/leap/mail/smtp/tests/test_gateway.py @@ -15,7 +15,6 @@  # You should have received a copy of the GNU General Public License  # along with this program. If not, see <http://www.gnu.org/licenses/>. -  """  SMTP gateway tests.  """ @@ -23,19 +22,18 @@ SMTP gateway tests.  import re  from datetime import datetime +from twisted.mail import smtp  from twisted.internet import reactor  from twisted.internet.defer import inlineCallbacks, fail, succeed, Deferred  from twisted.test import proto_helpers  from mock import Mock -from leap.mail.smtp.gateway import ( -    SMTPFactory -) -from leap.mail.tests import ( -    TestCaseWithKeyManager, -    ADDRESS, -    ADDRESS_2, -) +from leap.mail.smtp.gateway import SMTPFactory, LOCAL_FQDN +from leap.mail.smtp.gateway import SMTPDelivery + +from leap.mail.outgoing.service import outgoingFactory +from leap.mail.tests import TestCaseWithKeyManager +from leap.mail.tests import ADDRESS, ADDRESS_2  from leap.keymanager import openpgp, errors @@ -46,6 +44,52 @@ HOSTNAME_REGEX = "(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*" + \      "([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\-]*[A-Za-z0-9])"  IP_OR_HOST_REGEX = '(' + IP_REGEX + '|' + HOSTNAME_REGEX + ')' +TEST_USER = u'anotheruser@leap.se' + + +def getSMTPFactory(soledad_s, keymanager_s, sendmail_opts, +                   encrypted_only=False): +    factory = UnauthenticatedSMTPFactory +    factory.encrypted_only = encrypted_only +    proto = factory( +        soledad_s, keymanager_s, sendmail_opts).buildProtocol(('127.0.0.1', 0)) +    return proto + + +class UnauthenticatedSMTPServer(smtp.SMTP): + +    encrypted_only = False + +    def __init__(self, soledads, keyms, opts, encrypted_only=False): +        smtp.SMTP.__init__(self) + +        userid = TEST_USER +        keym = keyms[userid] + +        class Opts: +            cert = '/tmp/cert' +            key = '/tmp/cert' +            hostname = 'remote' +            port = 666 + +        outgoing = outgoingFactory( +            userid, keym, Opts, check_cert=False) +        avatar = SMTPDelivery(userid, keym, encrypted_only, outgoing) +        self.delivery = avatar + +    def validateFrom(self, helo, origin): +        return origin + + +class UnauthenticatedSMTPFactory(SMTPFactory): +    """ +    A Factory that produces a SMTP server that does not authenticate user. +    Only for tests! +    """ +    protocol = UnauthenticatedSMTPServer +    domain = LOCAL_FQDN +    encrypted_only = False +  class TestSmtpGateway(TestCaseWithKeyManager): @@ -85,14 +129,8 @@ class TestSmtpGateway(TestCaseWithKeyManager):                          '250 Recipient address accepted',                          '354 Continue'] -        # XXX this bit can be refactored away in a helper -        # method... -        proto = SMTPFactory( -            u'anotheruser@leap.se', -            self._km, -            self._config['encrypted_only'], -            outgoing_mail=Mock()).buildProtocol(('127.0.0.1', 0)) -        # snip... +        user = TEST_USER +        proto = getSMTPFactory({user: None}, {user: self._km}, {user: None})          transport = proto_helpers.StringTransport()          proto.makeConnection(transport)          reply = "" @@ -116,12 +154,10 @@ class TestSmtpGateway(TestCaseWithKeyManager):          # mock the key fetching          self._km._fetch_keys_from_server = Mock(              return_value=fail(errors.KeyNotFound())) -        # prepare the SMTP factory -        proto = SMTPFactory( -            u'anotheruser@leap.se', -            self._km, -            self._config['encrypted_only'], -            outgoing_mail=Mock()).buildProtocol(('127.0.0.1', 0)) +        user = TEST_USER +        proto = getSMTPFactory( +            {user: None}, {user: self._km}, {user: None}, +            encrypted_only=True)          transport = proto_helpers.StringTransport()          proto.makeConnection(transport)          yield self.getReply(self.EMAIL_DATA[0] + '\r\n', proto, transport) @@ -132,7 +168,7 @@ class TestSmtpGateway(TestCaseWithKeyManager):          self.assertEqual(              '550 Cannot receive for specified address\r\n',              reply, -            'Address should have been rejecetd with appropriate message.') +            'Address should have been rejected with appropriate message.')          proto.setTimeout(None)      @inlineCallbacks @@ -149,11 +185,8 @@ class TestSmtpGateway(TestCaseWithKeyManager):          # mock the key fetching          self._km._fetch_keys_from_server = Mock(              return_value=fail(errors.KeyNotFound())) -        # prepare the SMTP factory with encrypted only equal to false -        proto = SMTPFactory( -            u'anotheruser@leap.se', -            self._km, -            False, outgoing_mail=Mock()).buildProtocol(('127.0.0.1', 0)) +        user = TEST_USER +        proto = getSMTPFactory({user: None}, {user: self._km}, {user: None})          transport = proto_helpers.StringTransport()          proto.makeConnection(transport)          yield self.getReply(self.EMAIL_DATA[0] + '\r\n', proto, transport) | 
