Beispiel #1
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.opts.cert, self.opts.key,
            self.opts.hostname, self.opts.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,
                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
Beispiel #2
0
    def test_send_error_raises_exception_if_there_is_no_bouncer(self):
        bouncer = None
        outgoing_mail = OutgoingMail(self.from_address, self.keymanager,
                                     bouncer)

        failure = Failure(exc_value=Exception('smtp error'))
        origmsg = 'message'
        with self.assertRaises(Exception):
            outgoing_mail.sendError(failure, origmsg)
Beispiel #3
0
    def test_send_error_bounces_if_bouncer_is_provided(self):
        bouncer = MagicMock()
        outgoing_mail = OutgoingMail(self.from_address, self.keymanager,
                                     bouncer)

        failure = Failure(exc_value=Exception())
        origmsg = 'message'
        outgoing_mail.sendError(failure, origmsg)

        bouncer.bounce_message.assert_called()
Beispiel #4
0
        def init_outgoing_and_proto(_):
            self.outgoing_mail = OutgoingMail(
                self.fromAddr, self.km, opts.cert,
                opts.key, opts.hostname, opts.port)

            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)
Beispiel #5
0
    def _create_outgoing_service(self, bouncer, userid, keymanager, soledad):
        outgoing = OutgoingMail(userid, keymanager, bouncer)

        username, provider = userid.split('@')
        key = _get_smtp_client_cert_path(self._basedir, provider, username)
        prov = _get_provider_for_service('smtp', self._basedir, provider)
        hostname = prov.hostname
        port = prov.port
        if not os.path.isfile(key):
            raise errors.ConfigurationError(
                'No valid SMTP certificate could be found for %s!' % userid)
        smtp_sender = SMTPSender(userid, key, hostname, port)
        outgoing.add_sender(smtp_sender)

        self._outgoing_sessions[userid] = outgoing
Beispiel #6
0
 def test_missing_key_rejects_address(self):
     """
     Test if server rejects to send unencrypted when 'encrypted_only' is
     True.
     """
     # remove key from key manager
     pubkey = yield self.km.get_key(ADDRESS)
     pgp = openpgp.OpenPGPScheme(self._soledad,
                                 gpgbinary=self.gpg_binary_path)
     yield pgp.delete_key(pubkey)
     # mock the key fetching
     self.km._nicknym.fetch_key_with_address = Mock(
         return_value=fail(errors.KeyNotFound()))
     user = TEST_USER
     proto = getSMTPFactory({user: OutgoingMail(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)
     yield self.getReply(self.EMAIL_DATA[1] + '\r\n', proto, transport)
     reply = yield self.getReply(self.EMAIL_DATA[2] + '\r\n', proto,
                                 transport)
     # ensure the address was rejected
     self.assertEqual(
         '550 Cannot receive for specified address\r\n', reply,
         'Address should have been rejected with appropriate message.')
     proto.setTimeout(None)
Beispiel #7
0
 def test_missing_key_accepts_address(self):
     """
     Test if server accepts to send unencrypted when 'encrypted_only' is
     False.
     """
     # remove key from key manager
     pubkey = yield self.km.get_key(ADDRESS)
     pgp = openpgp.OpenPGPScheme(self._soledad,
                                 gpgbinary=self.gpg_binary_path)
     yield pgp.delete_key(pubkey)
     # mock the key fetching
     self.km._fetch_keys_from_server_and_store_local = Mock(
         return_value=fail(errors.KeyNotFound()))
     user = TEST_USER
     proto = getSMTPFactory({user: OutgoingMail(user, self.km)},
                            {user: None})
     transport = proto_helpers.StringTransport()
     proto.makeConnection(transport)
     yield self.getReply(self.EMAIL_DATA[0] + '\r\n', proto, transport)
     yield self.getReply(self.EMAIL_DATA[1] + '\r\n', proto, transport)
     reply = yield self.getReply(self.EMAIL_DATA[2] + '\r\n', proto,
                                 transport)
     # ensure the address was accepted
     self.assertEqual(
         '250 Recipient address accepted\r\n', reply,
         'Address should have been accepted with appropriate message.')
     proto.setTimeout(None)
Beispiel #8
0
    def test_gateway_accepts_valid_email(self):
        """
        Test if SMTP server responds correctly for valid interaction.
        """

        SMTP_ANSWERS = [
            '220 ' + IP_OR_HOST_REGEX + ' NO UCE NO UBE NO RELAY PROBES',
            '250 ' + IP_OR_HOST_REGEX + ' Hello ' + IP_OR_HOST_REGEX +
            ', nice to meet you', '250 Sender address accepted',
            '250 Recipient address accepted', '354 Continue'
        ]

        user = TEST_USER
        proto = getSMTPFactory({user: OutgoingMail(user, self.km)},
                               {user: None})
        transport = proto_helpers.StringTransport()
        proto.makeConnection(transport)
        reply = ""
        for i, line in enumerate(self.EMAIL_DATA):
            reply += yield self.getReply(line + '\r\n', proto, transport)
        self.assertMatch(reply, '\r\n'.join(SMTP_ANSWERS),
                         'Did not get expected answer from gateway.')
        proto.setTimeout(None)
Beispiel #9
0
class TestOutgoingMail(KeyManagerWithSoledadTestCase):
    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

        class opts:
            cert = u'/tmp/cert'
            key = u'/tmp/cert'
            hostname = 'remote'
            port = 666
        self.opts = opts

        def init_outgoing_and_proto(_):
            self.outgoing_mail = OutgoingMail(
                self.fromAddr, self.km, opts.cert,
                opts.key, opts.hostname, opts.port)

            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 = KeyManagerWithSoledadTestCase.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))
        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, 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.opts.cert, self.opts.key,
            self.opts.hostname, self.opts.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,
                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))
        d.addCallback(lambda (decrypted, _):
                      self._check_key_attachment(Parser().parsestr(decrypted)))
        return d

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

        d = self.outgoing_mail._maybe_encrypt_and_sign(
            raw, dest, fetch_remote=False)
        d.addCallback(lambda (message, _):
                      self._check_headers(message, lines[:4]))
        d.addCallback(self._check_key_attachment)
        d.addErrback(log.err)
        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)

        d = self.km.get_key(address, 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
 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))