Beispiel #1
0
    def validateTo(self, user):
        """
        Validate the address of a recipient of the message, possibly
        rejecting it if the recipient key is not available.

        This method is called once for each recipient, i.e. for each SMTP
        protocol line beginning with "RCPT TO:", which includes all addresses
        in "To", "Cc" and "Bcc" MUA fields.

        The recipient's address is validated against the RFC 2822 definition.
        If self._encrypted_only is True and no key is found for a recipient,
        then that recipient is rejected.

        The method returns an encrypted message object that is able to send
        itself to the user's address.

        :param user: The user whose address we wish to validate.
        :type: twisted.mail.smtp.User

        @return: A callable which takes no arguments and returns an
                 encryptedMessage.
        @rtype: no-argument callable

        @raise SMTPBadRcpt: Raised if messages to the address are not to be
                            accepted.
        """
        # try to find recipient's public key
        address = validate_address(user.dest.addrstr)

        # verify if recipient key is available in keyring
        def found(_):
            logger.debug("Accepting mail for %s..." % user.dest.addrstr)
            emit_async(catalog.SMTP_RECIPIENT_ACCEPTED_ENCRYPTED,
                       self._userid, user.dest.addrstr)

        def not_found(failure):
            failure.trap(KeyNotFound)

            # if key was not found, check config to see if will send anyway
            if self._encrypted_only:
                emit_async(catalog.SMTP_RECIPIENT_REJECTED, self._userid,
                           user.dest.addrstr)
                raise smtp.SMTPBadRcpt(user.dest.addrstr)
            logger.warn(
                'Warning: will send an unencrypted message (because '
                '"encrypted_only" is set to False).')
            emit_async(
                catalog.SMTP_RECIPIENT_ACCEPTED_UNENCRYPTED,
                self._userid, user.dest.addrstr)

        def encrypt_func(_):
            return lambda: EncryptedMessage(user, self._outgoing_mail)

        d = self._km.get_key(address)
        d.addCallbacks(found, not_found)
        d.addCallback(encrypt_func)
        return d
Beispiel #2
0
    def _maybe_encrypt_and_sign(self, raw, recipient, fetch_remote=True):
        """
        Attempt to encrypt and sign the outgoing message.

        The behaviour of this method depends on:

            1. the original message's content-type, and
            2. the availability of the recipient's public key.

        If the original message's content-type is "multipart/encrypted", then
        the original message is not altered. For any other content-type, the
        method attempts to fetch the recipient's public key. If the
        recipient's public key is available, the message is encrypted and
        signed; otherwise it is only signed.

        Note that, if the C{encrypted_only} configuration is set to True and
        the recipient's public key is not available, then the recipient
        address would have been rejected in SMTPDelivery.validateTo().

        The following table summarizes the overall behaviour of the gateway:

        +---------------------------------------------------+----------------+
        | content-type        | rcpt pubkey | enforce encr. | action         |
        +---------------------+-------------+---------------+----------------+
        | multipart/encrypted | any         | any           | pass           |
        | other               | available   | any           | encrypt + sign |
        | other               | unavailable | yes           | reject         |
        | other               | unavailable | no            | sign           |
        +---------------------+-------------+---------------+----------------+

        :param raw: The raw message
        :type raw: str
        :param recipient: The recipient for the message
        :type: recipient: smtp.User

        :return: A Deferred that will be fired with a MIMEMultipart message
                 and the original recipient Message
        :rtype: Deferred
        """
        # pass if the original message's content-type is "multipart/encrypted"
        origmsg = Parser().parsestr(raw)

        if origmsg.get_content_type() == 'multipart/encrypted':
            return defer.succeed((origmsg, recipient))

        from_address = validate_address(self._from_address)
        username, domain = from_address.split('@')
        to_address = validate_address(recipient.dest.addrstr)

        def maybe_encrypt_and_sign(message):
            d = self._encrypt_and_sign(
                message, to_address, from_address,
                fetch_remote=fetch_remote)
            d.addCallbacks(signal_encrypt_sign,
                           if_key_not_found_send_unencrypted,
                           errbackArgs=(message,))
            return d

        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

        def if_key_not_found_send_unencrypted(failure, message):
            failure.trap(KeyNotFound, KeyAddressMismatch)

            self.log.info('Will send unencrypted message to %s.' % to_address)
            emit_async(catalog.SMTP_START_SIGN, self._from_address, to_address)
            d = self._sign(message, from_address)
            d.addCallback(signal_sign)
            return d

        def signal_sign(newmsg):
            emit_async(catalog.SMTP_END_SIGN, self._from_address)
            return newmsg, recipient

        self.log.info("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)
        return d