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(_): log.msg("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) log.msg("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, OpenPGPKey) d.addCallbacks(found, not_found) d.addCallback(encrypt_func) return d
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(_): log.msg("Accepting mail for %s..." % user.dest.addrstr) emit(catalog.SMTP_RECIPIENT_ACCEPTED_ENCRYPTED, 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(catalog.SMTP_RECIPIENT_REJECTED, user.dest.addrstr) raise smtp.SMTPBadRcpt(user.dest.addrstr) log.msg("Warning: will send an unencrypted message (because " "encrypted_only' is set to False).") emit(catalog.SMTP_RECIPIENT_ACCEPTED_UNENCRYPTED, user.dest.addrstr) def encrypt_func(_): return lambda: EncryptedMessage(user, self._outgoing_mail) d = self._km.get_key(address, OpenPGPKey) d.addCallbacks(found, not_found) d.addCallback(encrypt_func) return d
def _maybe_encrypt_and_sign(self, raw, recipient): """ 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.success((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) 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, "%s,%s" % (self._from_address, to_address)) return newmsg, recipient def if_key_not_found_send_unencrypted(failure, message): failure.trap(KeyNotFound, KeyAddressMismatch) log.msg('Will send unencrypted message to %s.' % to_address) emit_async(catalog.SMTP_START_SIGN, self._from_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 log.msg("Will encrypt the message with %s and sign with %s." % (to_address, from_address)) emit_async(catalog.SMTP_START_ENCRYPT_AND_SIGN, "%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
def _maybe_encrypt_and_sign(self, raw, recipient): """ 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.success((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) d.addCallbacks(signal_encrypt_sign, if_key_not_found_send_unencrypted, errbackArgs=(message, )) return d def signal_encrypt_sign(newmsg): emit(catalog.SMTP_END_ENCRYPT_AND_SIGN, "%s,%s" % (self._from_address, to_address)) return newmsg, recipient def if_key_not_found_send_unencrypted(failure, message): failure.trap(KeyNotFound, KeyAddressMismatch) log.msg('Will send unencrypted message to %s.' % to_address) emit(catalog.SMTP_START_SIGN, self._from_address) d = self._sign(message, from_address) d.addCallback(signal_sign) return d def signal_sign(newmsg): emit(catalog.SMTP_END_SIGN, self._from_address) return newmsg, recipient log.msg("Will encrypt the message with %s and sign with %s." % (to_address, from_address)) emit(catalog.SMTP_START_ENCRYPT_AND_SIGN, "%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