Example #1
0
 def init_outgoing_and_proto(_):
     self.outgoing_mail = OutgoingMail(self.fromAddr, self._km,
                                       self._config['cert'],
                                       self._config['key'],
                                       self._config['host'],
                                       self._config['port'])
     self.proto = SMTPFactory(u'*****@*****.**', 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_2)
Example #2
0
    def test_message_sign(self):
        """
        Test if message is signed with sender key.
        """
        # mock the key fetching
        self._km._fetch_keys_from_server = Mock(
            return_value=fail(errors.KeyNotFound()))
        recipient = User('*****@*****.**',
                         'gateway.leap.se', self.proto, ADDRESS)
        self.outgoing_mail = OutgoingMail(
            self.fromAddr, self._km, self._config['cert'], self._config['key'],
            self._config['host'], self._config['port'])

        def check_signed(res):
            message, _ = res
            self.assertTrue('Content-Type' in message)
            self.assertEqual('multipart/signed', message.get_content_type())
            self.assertEqual('application/pgp-signature',
                             message.get_param('protocol'))
            self.assertEqual('pgp-sha512', message.get_param('micalg'))
            # assert content of message
            body = (message.get_payload(0)
                           .get_payload(0)
                           .get_payload(decode=True))
            self.assertEqual(self.expected_body,
                             body)
            # assert content of signature
            self.assertTrue(
                message.get_payload(1).get_payload().startswith(
                    '-----BEGIN PGP SIGNATURE-----\n'),
                'Message does not start with signature header.')
            self.assertTrue(
                message.get_payload(1).get_payload().endswith(
                    '-----END PGP SIGNATURE-----\n'),
                'Message does not end with signature footer.')
            return message

        def verify(message):
            # replace EOL before verifying (according to rfc3156)
            fp = StringIO()
            g = RFC3156CompliantGenerator(
                fp, mangle_from_=False, maxheaderlen=76)
            g.flatten(message.get_payload(0))
            signed_text = re.sub('\r?\n', '\r\n',
                                 fp.getvalue())

            def assert_verify(key):
                self.assertTrue(ADDRESS_2 in key.address,
                                'Signature could not be verified.')

            d = self._km.verify(
                signed_text, ADDRESS_2, openpgp.OpenPGPKey,
                detached_sig=message.get_payload(1).get_payload())
            d.addCallback(assert_verify)
            return d

        d = self.outgoing_mail._maybe_encrypt_and_sign(self.raw, recipient)
        d.addCallback(check_signed)
        d.addCallback(verify)
        return d
 def _create_outgoing_mail(self):
     return OutgoingMail(str(self._smtp_config.account_email),
                         self._keymanager,
                         self._smtp_config.cert_path,
                         self._smtp_config.cert_path,
                         str(self._smtp_config.remote_smtp_host),
                         int(self._smtp_config.remote_smtp_port))
Example #4
0
 def init_outgoing_and_proto(_):
     self.outgoing_mail = OutgoingMail(
         self.fromAddr, self._km, self._config['cert'],
         self._config['key'], self._config['host'],
         self._config['port'])
     self.proto = SMTPFactory(
         u'*****@*****.**',
         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_2)
Example #5
0
def setup_smtp_gateway(port, userid, keymanager, smtp_host, smtp_port,
                       smtp_cert, smtp_key, encrypted_only):
    """
    Setup SMTP gateway to run with Twisted.

    This function sets up the SMTP gateway configuration and the Twisted
    reactor.

    :param port: The port in which to run the server.
    :type port: int
    :param userid: The user currently logged in
    :type userid: str
    :param keymanager: A Key Manager from where to get recipients' public
                       keys.
    :type keymanager: leap.common.keymanager.KeyManager
    :param smtp_host: The hostname of the remote SMTP server.
    :type smtp_host: str
    :param smtp_port: The port of the remote SMTP server.
    :type smtp_port: int
    :param smtp_cert: The client certificate for authentication.
    :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.
    :type encrypted_only: bool

    :returns: tuple of SMTPFactory, twisted.internet.tcp.Port
    """
    # configure the use of this service with twistd
    outgoing_mail = OutgoingMail(userid, keymanager, smtp_cert, smtp_key,
                                 smtp_host, smtp_port)
    factory = SMTPFactory(userid, keymanager, encrypted_only, outgoing_mail)
    try:
        tport = reactor.listenTCP(port, factory, interface="localhost")
        emit(catalog.SMTP_SERVICE_STARTED, str(port))
        return factory, tport
    except CannotListenError:
        logger.error("STMP Service failed to start: "
                     "cannot listen in port %s" % port)
        emit(catalog.SMTP_SERVICE_FAILED_TO_START, str(port))
    except Exception as exc:
        logger.error("Unhandled error while launching smtp gateway service")
        logger.exception(exc)
Example #6
0
class TestOutgoingMail(TestCaseWithKeyManager):
    EMAIL_DATA = [
        'HELO gateway.leap.se',
        'MAIL FROM: <%s>' % ADDRESS_2,
        'RCPT TO: <%s>' % ADDRESS, 'DATA',
        'From: User <%s>' % ADDRESS_2,
        'To: Leap <%s>' % ADDRESS, 'Date: ' + datetime.now().strftime('%c'),
        'Subject: test message', '', 'This is a secret message.', 'Yours,',
        'A.', '', '.', 'QUIT'
    ]

    def setUp(self):
        self.lines = [line for line in self.EMAIL_DATA[4:12]]
        self.lines.append('')  # add a trailing newline
        self.raw = '\r\n'.join(self.lines)
        self.expected_body = '\r\n'.join(self.EMAIL_DATA[9:12]) + "\r\n"
        self.fromAddr = ADDRESS_2

        def init_outgoing_and_proto(_):
            self.outgoing_mail = OutgoingMail(self.fromAddr, self._km,
                                              self._config['cert'],
                                              self._config['key'],
                                              self._config['host'],
                                              self._config['port'])
            self.proto = SMTPFactory(u'*****@*****.**', 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_2)

        d = TestCaseWithKeyManager.setUp(self)
        d.addCallback(init_outgoing_and_proto)
        return d

    def test_message_encrypt(self):
        """
        Test if message gets encrypted to destination email.
        """
        def check_decryption(res):
            decrypted, _ = res
            self.assertEqual('\n' + self.expected_body, decrypted,
                             'Decrypted text differs from plaintext.')

        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))
        d.addCallback(check_decryption)
        return d

    def test_message_encrypt_sign(self):
        """
        Test if message gets encrypted to destination email and signed with
        sender key.
        '"""
        def check_decryption_and_verify(res):
            decrypted, signkey = res
            self.assertEqual('\n' + self.expected_body, decrypted,
                             'Decrypted text differs from plaintext.')
            self.assertTrue(ADDRESS_2 in signkey.address,
                            "Verification failed")

        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,
                                                       verify=ADDRESS_2))
        d.addCallback(check_decryption_and_verify)
        return d

    def test_message_sign(self):
        """
        Test if message is signed with sender key.
        """
        # mock the key fetching
        self._km._fetch_keys_from_server = Mock(
            return_value=fail(errors.KeyNotFound()))
        recipient = User('*****@*****.**', 'gateway.leap.se',
                         self.proto, ADDRESS)
        self.outgoing_mail = OutgoingMail(self.fromAddr, self._km,
                                          self._config['cert'],
                                          self._config['key'],
                                          self._config['host'],
                                          self._config['port'])

        def check_signed(res):
            message, _ = res
            self.assertTrue('Content-Type' in message)
            self.assertEqual('multipart/signed', message.get_content_type())
            self.assertEqual('application/pgp-signature',
                             message.get_param('protocol'))
            self.assertEqual('pgp-sha512', message.get_param('micalg'))
            # assert content of message
            body = (message.get_payload(0).get_payload(0).get_payload(
                decode=True))
            self.assertEqual(self.expected_body, body)
            # assert content of signature
            self.assertTrue(
                message.get_payload(1).get_payload().startswith(
                    '-----BEGIN PGP SIGNATURE-----\n'),
                'Message does not start with signature header.')
            self.assertTrue(
                message.get_payload(1).get_payload().endswith(
                    '-----END PGP SIGNATURE-----\n'),
                'Message does not end with signature footer.')
            return message

        def verify(message):
            # replace EOL before verifying (according to rfc3156)
            fp = StringIO()
            g = RFC3156CompliantGenerator(fp,
                                          mangle_from_=False,
                                          maxheaderlen=76)
            g.flatten(message.get_payload(0))
            signed_text = re.sub('\r?\n', '\r\n', fp.getvalue())

            def assert_verify(key):
                self.assertTrue(ADDRESS_2 in key.address,
                                'Signature could not be verified.')

            d = self._km.verify(
                signed_text,
                ADDRESS_2,
                openpgp.OpenPGPKey,
                detached_sig=message.get_payload(1).get_payload())
            d.addCallback(assert_verify)
            return d

        d = self.outgoing_mail._maybe_encrypt_and_sign(self.raw, recipient)
        d.addCallback(check_signed)
        d.addCallback(verify)
        return d

    def test_attach_key(self):
        d = self.outgoing_mail._maybe_encrypt_and_sign(self.raw, self.dest)
        d.addCallback(self._assert_encrypted)
        d.addCallback(self._check_headers, self.lines[:4])
        d.addCallback(lambda message: self._km.decrypt(
            message.get_payload(1).get_payload(), ADDRESS, openpgp.OpenPGPKey))
        d.addCallback(lambda (decrypted, _): self._check_key_attachment(Parser(
        ).parsestr(decrypted)))
        return d

    def test_attach_key_not_known(self):
        address = "*****@*****.**"
        lines = self.lines
        lines[1] = "To: <%s>" % (address, )
        raw = '\r\n'.join(lines)
        dest = User(address, 'gateway.leap.se', self.proto, ADDRESS_2)

        d = self.outgoing_mail._maybe_encrypt_and_sign(raw, dest)
        d.addCallback(lambda
                      (message, _): self._check_headers(message, lines[:4]))
        d.addCallback(self._check_key_attachment)
        return d

    def _check_headers(self, message, headers):
        msgstr = message.as_string(unixfrom=False)
        for header in headers:
            self.assertTrue(header in msgstr,
                            "Missing header: %s" % (header, ))
        return message

    def _check_key_attachment(self, message):
        for payload in message.get_payload():
            if payload.is_multipart():
                return self._check_key_attachment(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(decode=True),
                                "Key attachment don't match")
                return
        self.fail("No public key attachment found")

    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)
        self.assertEqual('multipart/encrypted', message.get_content_type())
        self.assertEqual('application/pgp-encrypted',
                         message.get_param('protocol'))
        self.assertEqual(2, len(message.get_payload()))
        self.assertEqual('application/pgp-encrypted',
                         message.get_payload(0).get_content_type())
        self.assertEqual('application/octet-stream',
                         message.get_payload(1).get_content_type())
        return message
Example #7
0
    def test_message_sign(self):
        """
        Test if message is signed with sender key.
        """
        # mock the key fetching
        self._km._fetch_keys_from_server = Mock(
            return_value=fail(errors.KeyNotFound()))
        recipient = User('*****@*****.**', 'gateway.leap.se',
                         self.proto, ADDRESS)
        self.outgoing_mail = OutgoingMail(self.fromAddr, self._km,
                                          self._config['cert'],
                                          self._config['key'],
                                          self._config['host'],
                                          self._config['port'])

        def check_signed(res):
            message, _ = res
            self.assertTrue('Content-Type' in message)
            self.assertEqual('multipart/signed', message.get_content_type())
            self.assertEqual('application/pgp-signature',
                             message.get_param('protocol'))
            self.assertEqual('pgp-sha512', message.get_param('micalg'))
            # assert content of message
            body = (message.get_payload(0).get_payload(0).get_payload(
                decode=True))
            self.assertEqual(self.expected_body, body)
            # assert content of signature
            self.assertTrue(
                message.get_payload(1).get_payload().startswith(
                    '-----BEGIN PGP SIGNATURE-----\n'),
                'Message does not start with signature header.')
            self.assertTrue(
                message.get_payload(1).get_payload().endswith(
                    '-----END PGP SIGNATURE-----\n'),
                'Message does not end with signature footer.')
            return message

        def verify(message):
            # replace EOL before verifying (according to rfc3156)
            fp = StringIO()
            g = RFC3156CompliantGenerator(fp,
                                          mangle_from_=False,
                                          maxheaderlen=76)
            g.flatten(message.get_payload(0))
            signed_text = re.sub('\r?\n', '\r\n', fp.getvalue())

            def assert_verify(key):
                self.assertTrue(ADDRESS_2 in key.address,
                                'Signature could not be verified.')

            d = self._km.verify(
                signed_text,
                ADDRESS_2,
                openpgp.OpenPGPKey,
                detached_sig=message.get_payload(1).get_payload())
            d.addCallback(assert_verify)
            return d

        d = self.outgoing_mail._maybe_encrypt_and_sign(self.raw, recipient)
        d.addCallback(check_signed)
        d.addCallback(verify)
        return d
Example #8
0
class TestOutgoingMail(TestCaseWithKeyManager):
    EMAIL_DATA = ['HELO gateway.leap.se',
                  'MAIL FROM: <%s>' % ADDRESS_2,
                  'RCPT TO: <%s>' % ADDRESS,
                  'DATA',
                  'From: User <%s>' % ADDRESS_2,
                  'To: Leap <%s>' % ADDRESS,
                  'Date: ' + datetime.now().strftime('%c'),
                  'Subject: test message',
                  '',
                  'This is a secret message.',
                  'Yours,',
                  'A.',
                  '',
                  '.',
                  'QUIT']

    def setUp(self):
        self.lines = [line for line in self.EMAIL_DATA[4:12]]
        self.lines.append('')  # add a trailing newline
        self.raw = '\r\n'.join(self.lines)
        self.expected_body = '\r\n'.join(self.EMAIL_DATA[9:12]) + "\r\n"
        self.fromAddr = ADDRESS_2

        def init_outgoing_and_proto(_):
            self.outgoing_mail = OutgoingMail(
                self.fromAddr, self._km, self._config['cert'],
                self._config['key'], self._config['host'],
                self._config['port'])
            self.proto = SMTPFactory(
                u'*****@*****.**',
                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_2)

        d = TestCaseWithKeyManager.setUp(self)
        d.addCallback(init_outgoing_and_proto)
        return d

    def test_message_encrypt(self):
        """
        Test if message gets encrypted to destination email.
        """
        def check_decryption(res):
            decrypted, _ = res
            self.assertEqual(
                '\n' + self.expected_body,
                decrypted,
                'Decrypted text differs from plaintext.')

        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))
        d.addCallback(check_decryption)
        return d

    def test_message_encrypt_sign(self):
        """
        Test if message gets encrypted to destination email and signed with
        sender key.
        '"""
        def check_decryption_and_verify(res):
            decrypted, signkey = res
            self.assertEqual(
                '\n' + self.expected_body,
                decrypted,
                'Decrypted text differs from plaintext.')
            self.assertTrue(ADDRESS_2 in signkey.address,
                            "Verification failed")

        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,
            verify=ADDRESS_2))
        d.addCallback(check_decryption_and_verify)
        return d

    def test_message_sign(self):
        """
        Test if message is signed with sender key.
        """
        # mock the key fetching
        self._km._fetch_keys_from_server = Mock(
            return_value=fail(errors.KeyNotFound()))
        recipient = User('*****@*****.**',
                         'gateway.leap.se', self.proto, ADDRESS)
        self.outgoing_mail = OutgoingMail(
            self.fromAddr, self._km, self._config['cert'], self._config['key'],
            self._config['host'], self._config['port'])

        def check_signed(res):
            message, _ = res
            self.assertTrue('Content-Type' in message)
            self.assertEqual('multipart/signed', message.get_content_type())
            self.assertEqual('application/pgp-signature',
                             message.get_param('protocol'))
            self.assertEqual('pgp-sha512', message.get_param('micalg'))
            # assert content of message
            body = (message.get_payload(0)
                           .get_payload(0)
                           .get_payload(decode=True))
            self.assertEqual(self.expected_body,
                             body)
            # assert content of signature
            self.assertTrue(
                message.get_payload(1).get_payload().startswith(
                    '-----BEGIN PGP SIGNATURE-----\n'),
                'Message does not start with signature header.')
            self.assertTrue(
                message.get_payload(1).get_payload().endswith(
                    '-----END PGP SIGNATURE-----\n'),
                'Message does not end with signature footer.')
            return message

        def verify(message):
            # replace EOL before verifying (according to rfc3156)
            fp = StringIO()
            g = RFC3156CompliantGenerator(
                fp, mangle_from_=False, maxheaderlen=76)
            g.flatten(message.get_payload(0))
            signed_text = re.sub('\r?\n', '\r\n',
                                 fp.getvalue())

            def assert_verify(key):
                self.assertTrue(ADDRESS_2 in key.address,
                                'Signature could not be verified.')

            d = self._km.verify(
                signed_text, ADDRESS_2, openpgp.OpenPGPKey,
                detached_sig=message.get_payload(1).get_payload())
            d.addCallback(assert_verify)
            return d

        d = self.outgoing_mail._maybe_encrypt_and_sign(self.raw, recipient)
        d.addCallback(check_signed)
        d.addCallback(verify)
        return d

    def test_attach_key(self):
        d = self.outgoing_mail._maybe_encrypt_and_sign(self.raw, self.dest)
        d.addCallback(self._assert_encrypted)
        d.addCallback(self._check_headers, self.lines[:4])
        d.addCallback(lambda message: self._km.decrypt(
            message.get_payload(1).get_payload(), ADDRESS, openpgp.OpenPGPKey))
        d.addCallback(lambda (decrypted, _):
                      self._check_key_attachment(Parser().parsestr(decrypted)))
        return d

    def test_attach_key_not_known(self):
        address = "*****@*****.**"
        lines = self.lines
        lines[1] = "To: <%s>" % (address,)
        raw = '\r\n'.join(lines)
        dest = User(address, 'gateway.leap.se', self.proto, ADDRESS_2)

        d = self.outgoing_mail._maybe_encrypt_and_sign(raw, dest)
        d.addCallback(lambda (message, _):
                      self._check_headers(message, lines[:4]))
        d.addCallback(self._check_key_attachment)
        return d

    def _check_headers(self, message, headers):
        msgstr = message.as_string(unixfrom=False)
        for header in headers:
            self.assertTrue(header in msgstr,
                            "Missing header: %s" % (header,))
        return message

    def _check_key_attachment(self, message):
        for payload in message.get_payload():
            if payload.is_multipart():
                return self._check_key_attachment(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(decode=True),
                                "Key attachment don't match")
                return
        self.fail("No public key attachment found")

    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)
        self.assertEqual('multipart/encrypted', message.get_content_type())
        self.assertEqual('application/pgp-encrypted',
                         message.get_param('protocol'))
        self.assertEqual(2, len(message.get_payload()))
        self.assertEqual('application/pgp-encrypted',
                         message.get_payload(0).get_content_type())
        self.assertEqual('application/octet-stream',
                         message.get_payload(1).get_content_type())
        return message