summaryrefslogtreecommitdiff
path: root/src/leap/mail
diff options
context:
space:
mode:
Diffstat (limited to 'src/leap/mail')
-rw-r--r--src/leap/mail/imap/tests/test_imap.py4
-rw-r--r--src/leap/mail/smtp/__init__.py33
-rw-r--r--src/leap/mail/smtp/smtprelay.py260
-rw-r--r--src/leap/mail/smtp/tests/__init__.py4
-rw-r--r--src/leap/mail/smtp/tests/test_smtprelay.py52
5 files changed, 159 insertions, 194 deletions
diff --git a/src/leap/mail/imap/tests/test_imap.py b/src/leap/mail/imap/tests/test_imap.py
index 3411795..ad11315 100644
--- a/src/leap/mail/imap/tests/test_imap.py
+++ b/src/leap/mail/imap/tests/test_imap.py
@@ -91,7 +91,7 @@ def initialize_soledad(email, gnupg_home, tempdir):
"""
uuid = "foobar-uuid"
- passphrase = "verysecretpassphrase"
+ passphrase = u"verysecretpassphrase"
secret_path = os.path.join(tempdir, "secret.gpg")
local_db_path = os.path.join(tempdir, "soledad.u1db")
server_url = "http://provider"
@@ -101,6 +101,8 @@ def initialize_soledad(email, gnupg_home, tempdir):
get_doc = Mock(return_value=None)
put_doc = Mock()
+ lock = Mock(return_value=('atoken', 300))
+ unlock = Mock(return_value=True)
def __call__(self):
return self
diff --git a/src/leap/mail/smtp/__init__.py b/src/leap/mail/smtp/__init__.py
index be568b8..753ef34 100644
--- a/src/leap/mail/smtp/__init__.py
+++ b/src/leap/mail/smtp/__init__.py
@@ -46,7 +46,7 @@ def setup_smtp_relay(port, userid, keymanager, smtp_host, smtp_port,
: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.
+ :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
@@ -58,36 +58,17 @@ def setup_smtp_relay(port, userid, keymanager, smtp_host, smtp_port,
:returns: tuple of SMTPFactory, twisted.internet.tcp.Port
"""
- # The configuration for the SMTP relay is a dict with the following
- # format:
- #
- # {
- # 'host': '<host>',
- # 'port': <int>,
- # 'cert': '<cert path>',
- # 'key': '<key path>',
- # 'encrypted_only': <True/False>
- # }
- config = {
- 'host': smtp_host,
- 'port': smtp_port,
- 'cert': smtp_cert,
- 'key': smtp_key,
- 'encrypted_only': encrypted_only
- }
-
# configure the use of this service with twistd
- factory = SMTPFactory(userid, keymanager, config)
+ factory = SMTPFactory(userid, keymanager, smtp_host, smtp_port, smtp_cert,
+ smtp_key, encrypted_only)
try:
- tport = reactor.listenTCP(port, factory,
- interface="localhost")
- signal(proto.SMTP_SERVICE_STARTED, str(smtp_port))
+ tport = reactor.listenTCP(port, factory, interface="localhost")
+ signal(proto.SMTP_SERVICE_STARTED, str(port))
return factory, tport
except CannotListenError:
logger.error("STMP Service failed to start: "
- "cannot listen in port %s" % (
- smtp_port,))
- signal(proto.SMTP_SERVICE_FAILED_TO_START, str(smtp_port))
+ "cannot listen in port %s" % port)
+ signal(proto.SMTP_SERVICE_FAILED_TO_START, str(port))
except Exception as exc:
logger.error("Unhandled error while launching smtp relay service")
logger.exception(exc)
diff --git a/src/leap/mail/smtp/smtprelay.py b/src/leap/mail/smtp/smtprelay.py
index 14de849..474fc3b 100644
--- a/src/leap/mail/smtp/smtprelay.py
+++ b/src/leap/mail/smtp/smtprelay.py
@@ -68,67 +68,15 @@ generator.Generator = RFC3156CompliantGenerator
#
-# Exceptions
-#
-
-class MalformedConfig(Exception):
- """
- Raised when the configuration dictionary passed as parameter is malformed.
- """
- pass
-
-
-#
# Helper utilities
#
-HOST_KEY = 'host'
-PORT_KEY = 'port'
-CERT_KEY = 'cert'
-KEY_KEY = 'key'
-ENCRYPTED_ONLY_KEY = 'encrypted_only'
-
-
-def assert_config_structure(config):
- """
- Assert that C{config} is a dict with the following structure:
-
- {
- HOST_KEY: '<str>',
- PORT_KEY: <int>,
- CERT_KEY: '<str>',
- KEY_KEY: '<str>',
- ENCRYPTED_ONLY_KEY: <bool>,
- }
-
- @param config: The dictionary to check.
- @type config: dict
- """
- # assert smtp config structure is valid
- leap_assert_type(config, dict)
- leap_assert(HOST_KEY in config)
- leap_assert_type(config[HOST_KEY], str)
- leap_assert(PORT_KEY in config)
- leap_assert_type(config[PORT_KEY], int)
- leap_assert(CERT_KEY in config)
- leap_assert_type(config[CERT_KEY], (str, unicode))
- leap_assert(KEY_KEY in config)
- leap_assert_type(config[KEY_KEY], (str, unicode))
- leap_assert(ENCRYPTED_ONLY_KEY in config)
- leap_assert_type(config[ENCRYPTED_ONLY_KEY], bool)
- # assert received params are not empty
- leap_assert(config[HOST_KEY] != '')
- leap_assert(config[PORT_KEY] is not 0)
- leap_assert(config[CERT_KEY] != '')
- leap_assert(config[KEY_KEY] != '')
-
-
def validate_address(address):
"""
Validate C{address} as defined in RFC 2822.
- @param address: The address to be validated.
- @type address: str
+ :param address: The address to be validated.
+ :type address: str
@return: A valid address.
@rtype: str
@@ -153,45 +101,60 @@ class SMTPFactory(ServerFactory):
Factory for an SMTP server with encrypted relaying capabilities.
"""
- def __init__(self, userid, keymanager, config):
+ def __init__(self, userid, keymanager, host, port, cert, key,
+ encrypted_only):
"""
Initialize the SMTP factory.
- @param keymanager: A KeyManager for retrieving recipient's keys.
- @type keymanager: leap.common.keymanager.KeyManager
- @param config: A dictionary with smtp configuration. Should have
- the following structure:
- {
- HOST_KEY: '<str>',
- PORT_KEY: <int>,
- CERT_KEY: '<str>',
- KEY_KEY: '<str>',
- ENCRYPTED_ONLY_KEY: <bool>,
- }
- @type config: dict
- @param userid: The user currently logged in
- @type userid: unicode
+ :param userid: The user currently logged in
+ :type userid: unicode
+ :param keymanager: A KeyManager for retrieving recipient's keys.
+ :type keymanager: leap.common.keymanager.KeyManager
+ :param host: The hostname of the remote SMTP server.
+ :type host: str
+ :param port: The port of the remote SMTP server.
+ :type port: int
+ :param cert: The client certificate for authentication.
+ :type cert: str
+ :param key: The client key for authentication.
+ :type key: str
+ :param encrypted_only: Whether the SMTP relay should send unencrypted mail
+ or not.
+ :type encrypted_only: bool
"""
# assert params
leap_assert_type(keymanager, KeyManager)
- assert_config_structure(config)
+ leap_assert_type(host, str)
+ leap_assert(host != '')
+ leap_assert_type(port, int)
+ leap_assert(port is not 0)
+ leap_assert_type(cert, str)
+ leap_assert(cert != '')
+ leap_assert_type(key, str)
+ leap_assert(key != '')
+ leap_assert_type(encrypted_only, bool)
# and store them
self._userid = userid
self._km = keymanager
- self._config = config
+ self._host = host
+ self._port = port
+ self._cert = cert
+ self._key = key
+ self._encrypted_only = encrypted_only
def buildProtocol(self, addr):
"""
Return a protocol suitable for the job.
- @param addr: An address, e.g. a TCP (host, port).
- @type addr: twisted.internet.interfaces.IAddress
+ :param addr: An address, e.g. a TCP (host, port).
+ :type addr: twisted.internet.interfaces.IAddress
@return: The protocol.
@rtype: SMTPDelivery
"""
- smtpProtocol = smtp.SMTP(SMTPDelivery(self._userid, self._km,
- self._config))
+ smtpProtocol = smtp.SMTP(SMTPDelivery(
+ self._userid, self._km, self._host, self._port, self._cert,
+ self._key, self._encrypted_only))
smtpProtocol.factory = self
return smtpProtocol
@@ -207,49 +170,53 @@ class SMTPDelivery(object):
implements(smtp.IMessageDelivery)
- def __init__(self, userid, keymanager, config):
+ def __init__(self, userid, keymanager, host, port, cert, key,
+ encrypted_only):
"""
Initialize the SMTP delivery object.
- @param keymanager: A KeyManager for retrieving recipient's keys.
- @type keymanager: leap.common.keymanager.KeyManager
- @param config: A dictionary with smtp configuration. Should have
- the following structure:
- {
- HOST_KEY: '<str>',
- PORT_KEY: <int>,
- CERT_KEY: '<str>',
- KEY_KEY: '<str>',
- ENCRYPTED_ONLY_KEY: <bool>,
- }
- @type config: dict
- @param userid: The user currently logged in
- @type userid: unicode
+ :param userid: The user currently logged in
+ :type userid: unicode
+ :param keymanager: A KeyManager for retrieving recipient's keys.
+ :type keymanager: leap.common.keymanager.KeyManager
+ :param host: The hostname of the remote SMTP server.
+ :type host: str
+ :param port: The port of the remote SMTP server.
+ :type port: int
+ :param cert: The client certificate for authentication.
+ :type cert: str
+ :param key: The client key for authentication.
+ :type key: str
+ :param encrypted_only: Whether the SMTP relay should send unencrypted mail
+ or not.
+ :type encrypted_only: bool
"""
- # assert params
- leap_assert_type(keymanager, KeyManager)
- assert_config_structure(config)
- # and store them
self._userid = userid
self._km = keymanager
- self._config = config
+ self._userid = userid
+ self._km = keymanager
+ self._host = host
+ self._port = port
+ self._cert = cert
+ self._key = key
+ self._encrypted_only = encrypted_only
self._origin = None
def receivedHeader(self, helo, origin, recipients):
"""
Generate the 'Received:' header for a message.
- @param helo: The argument to the HELO command and the client's IP
+ :param helo: The argument to the HELO command and the client's IP
address.
- @type helo: (str, str)
- @param origin: The address the message is from.
- @type origin: twisted.mail.smtp.Address
- @param recipients: A list of the addresses for which this message is
+ :type helo: (str, str)
+ :param origin: The address the message is from.
+ :type origin: twisted.mail.smtp.Address
+ :param recipients: A list of the addresses for which this message is
bound.
- @type: list of twisted.mail.smtp.User
+ :type: list of twisted.mail.smtp.User
@return: The full "Received" header string.
- @type: str
+ :type: str
"""
myHostname, clientIP = helo
headerValue = "by %s from %s with ESMTP ; %s" % (
@@ -269,8 +236,8 @@ class SMTPDelivery(object):
In the end, it returns an encrypted message object that is able to
send itself to the C{user}'s address.
- @param user: The user whose address we wish to validate.
- @type: twisted.mail.smtp.User
+ :param user: The user whose address we wish to validate.
+ :type: twisted.mail.smtp.User
@return: A Deferred which becomes, or a callable which takes no
arguments and returns an object implementing IMessage. This will
@@ -290,7 +257,7 @@ class SMTPDelivery(object):
signal(proto.SMTP_RECIPIENT_ACCEPTED_ENCRYPTED, user.dest.addrstr)
except KeyNotFound:
# if key was not found, check config to see if will send anyway.
- if self._config[ENCRYPTED_ONLY_KEY]:
+ if self._encrypted_only:
signal(proto.SMTP_RECIPIENT_REJECTED, user.dest.addrstr)
raise smtp.SMTPBadRcpt(user.dest.addrstr)
log.msg("Warning: will send an unencrypted message (because "
@@ -298,17 +265,18 @@ class SMTPDelivery(object):
signal(
proto.SMTP_RECIPIENT_ACCEPTED_UNENCRYPTED, user.dest.addrstr)
return lambda: EncryptedMessage(
- self._origin, user, self._km, self._config)
+ self._origin, user, self._km, self._host, self._port, self._cert,
+ self._key)
def validateFrom(self, helo, origin):
"""
Validate the address from which the message originates.
- @param helo: The argument to the HELO command and the client's IP
+ :param helo: The argument to the HELO command and the client's IP
address.
- @type: (str, str)
- @param origin: The address the message is from.
- @type origin: twisted.mail.smtp.Address
+ :type: (str, str)
+ :param origin: The address the message is from.
+ :type origin: twisted.mail.smtp.Address
@return: origin or a Deferred whose callback will be passed origin.
@rtype: Deferred or Address
@@ -360,36 +328,36 @@ class EncryptedMessage(object):
"""
implements(smtp.IMessage)
- def __init__(self, fromAddress, user, keymanager, config):
+ def __init__(self, fromAddress, user, keymanager, host, port, cert, key):
"""
Initialize the encrypted message.
- @param fromAddress: The address of the sender.
- @type fromAddress: twisted.mail.smtp.Address
- @param user: The recipient of this message.
- @type user: twisted.mail.smtp.User
- @param keymanager: A KeyManager for retrieving recipient's keys.
- @type keymanager: leap.common.keymanager.KeyManager
- @param config: A dictionary with smtp configuration. Should have
- the following structure:
- {
- HOST_KEY: '<str>',
- PORT_KEY: <int>,
- CERT_KEY: '<str>',
- KEY_KEY: '<str>',
- ENCRYPTED_ONLY_KEY: <bool>,
- }
- @type config: dict
+ :param fromAddress: The address of the sender.
+ :type fromAddress: twisted.mail.smtp.Address
+ :param user: The recipient of this message.
+ :type user: twisted.mail.smtp.User
+ :param keymanager: A KeyManager for retrieving recipient's keys.
+ :type keymanager: leap.common.keymanager.KeyManager
+ :param host: The hostname of the remote SMTP server.
+ :type host: str
+ :param port: The port of the remote SMTP server.
+ :type port: int
+ :param cert: The client certificate for authentication.
+ :type cert: str
+ :param key: The client key for authentication.
+ :type key: str
"""
# assert params
leap_assert_type(user, smtp.User)
leap_assert_type(keymanager, KeyManager)
- assert_config_structure(config)
# and store them
self._fromAddress = fromAddress
self._user = user
self._km = keymanager
- self._config = config
+ self._host = host
+ self._port = port
+ self._cert = cert
+ self._key = key
# initialize list for message's lines
self.lines = []
@@ -401,8 +369,8 @@ class EncryptedMessage(object):
"""
Handle another line.
- @param line: The received line.
- @type line: str
+ :param line: The received line.
+ :type line: str
"""
self.lines.append(line)
@@ -441,8 +409,8 @@ class EncryptedMessage(object):
"""
Callback for a successful send.
- @param r: The result from the last previous callback in the chain.
- @type r: anything
+ :param r: The result from the last previous callback in the chain.
+ :type r: anything
"""
log.msg(r)
signal(proto.SMTP_SEND_MESSAGE_SUCCESS, self._user.dest.addrstr)
@@ -451,8 +419,8 @@ class EncryptedMessage(object):
"""
Callback for an unsuccessfull send.
- @param e: The result from the last errback.
- @type e: anything
+ :param e: The result from the last errback.
+ :type e: anything
"""
log.msg(e)
log.err()
@@ -471,8 +439,7 @@ class EncryptedMessage(object):
"""
msg = self._msg.as_string(False)
- log.msg("Connecting to SMTP server %s:%s" % (self._config[HOST_KEY],
- self._config[PORT_KEY]))
+ log.msg("Connecting to SMTP server %s:%s" % (self._host, self._port))
d = defer.Deferred()
# we don't pass an ssl context factory to the ESMTPSenderFactory
@@ -488,11 +455,8 @@ class EncryptedMessage(object):
requireTransportSecurity=True)
signal(proto.SMTP_SEND_MESSAGE_START, self._user.dest.addrstr)
reactor.connectSSL(
- self._config[HOST_KEY],
- self._config[PORT_KEY],
- factory,
- contextFactory=SSLContextFactory(self._config[CERT_KEY],
- self._config[KEY_KEY]))
+ self._host, self._port, factory,
+ contextFactory=SSLContextFactory(self._cert, self._key))
d.addCallback(self.sendSuccess)
d.addErrback(self.sendError)
return d
@@ -506,10 +470,10 @@ class EncryptedMessage(object):
Create an RFC 3156 compliang PGP encrypted and signed message using
C{pubkey} to encrypt and C{signkey} to sign.
- @param pubkey: The public key used to encrypt the message.
- @type pubkey: leap.common.keymanager.openpgp.OpenPGPKey
- @param signkey: The private key used to sign the message.
- @type signkey: leap.common.keymanager.openpgp.OpenPGPKey
+ :param pubkey: The public key used to encrypt the message.
+ :type pubkey: leap.common.keymanager.openpgp.OpenPGPKey
+ :param signkey: The private key used to sign the message.
+ :type signkey: leap.common.keymanager.openpgp.OpenPGPKey
"""
# create new multipart/encrypted message with 'pgp-encrypted' protocol
newmsg = MultipartEncrypted('application/pgp-encrypted')
@@ -534,8 +498,8 @@ class EncryptedMessage(object):
"""
Create an RFC 3156 compliant PGP signed MIME message using C{signkey}.
- @param signkey: The private key used to sign the message.
- @type signkey: leap.common.keymanager.openpgp.OpenPGPKey
+ :param signkey: The private key used to sign the message.
+ :type signkey: leap.common.keymanager.openpgp.OpenPGPKey
"""
# create new multipart/signed message
newmsg = MultipartSigned('application/pgp-signature', 'pgp-sha512')
diff --git a/src/leap/mail/smtp/tests/__init__.py b/src/leap/mail/smtp/tests/__init__.py
index 9b54de3..ee6de9b 100644
--- a/src/leap/mail/smtp/tests/__init__.py
+++ b/src/leap/mail/smtp/tests/__init__.py
@@ -59,7 +59,7 @@ class TestCaseWithKeyManager(BaseLeapTest):
# setup our own stuff
address = 'leap@leap.se' # user's address in the form user@provider
uuid = 'leap@leap.se'
- passphrase = '123'
+ passphrase = u'123'
secrets_path = os.path.join(self.tempdir, 'secret.gpg')
local_db_path = os.path.join(self.tempdir, 'soledad.u1db')
server_url = 'http://provider/'
@@ -88,6 +88,8 @@ class TestCaseWithKeyManager(BaseLeapTest):
get_doc = Mock(return_value=None)
put_doc = Mock(side_effect=_put_doc_side_effect)
+ lock = Mock(return_value=('atoken', 300))
+ unlock = Mock(return_value=True)
def __call__(self):
return self
diff --git a/src/leap/mail/smtp/tests/test_smtprelay.py b/src/leap/mail/smtp/tests/test_smtprelay.py
index 7fefe77..25c780e 100644
--- a/src/leap/mail/smtp/tests/test_smtprelay.py
+++ b/src/leap/mail/smtp/tests/test_smtprelay.py
@@ -102,8 +102,10 @@ class TestSmtpRelay(TestCaseWithKeyManager):
'250 Sender address accepted',
'250 Recipient address accepted',
'354 Continue']
- proto = SMTPFactory(
- self._km, self._config).buildProtocol(('127.0.0.1', 0))
+ proto = SMTPFactory(u'anotheruser@leap.se',
+ self._km, self._config['host'], self._config['port'],
+ self._config['cert'], self._config['key'],
+ self._config['encrypted_only']).buildProtocol(('127.0.0.1', 0))
transport = proto_helpers.StringTransport()
proto.makeConnection(transport)
for i, line in enumerate(self.EMAIL_DATA):
@@ -117,11 +119,15 @@ class TestSmtpRelay(TestCaseWithKeyManager):
"""
Test if message gets encrypted to destination email.
"""
- proto = SMTPFactory(
- self._km, self._config).buildProtocol(('127.0.0.1', 0))
+ proto = SMTPFactory(u'anotheruser@leap.se',
+ self._km, self._config['host'], self._config['port'],
+ self._config['cert'], self._config['key'],
+ self._config['encrypted_only']).buildProtocol(('127.0.0.1', 0))
fromAddr = Address(ADDRESS_2)
dest = User(ADDRESS, 'relay.leap.se', proto, ADDRESS)
- m = EncryptedMessage(fromAddr, dest, self._km, self._config)
+ m = EncryptedMessage(
+ fromAddr, dest, self._km, self._config['host'],
+ self._config['port'], self._config['cert'], self._config['key'])
for line in self.EMAIL_DATA[4:12]:
m.lineReceived(line)
m.eomReceived()
@@ -149,11 +155,15 @@ class TestSmtpRelay(TestCaseWithKeyManager):
Test if message gets encrypted to destination email and signed with
sender key.
"""
- proto = SMTPFactory(
- self._km, self._config).buildProtocol(('127.0.0.1', 0))
+ proto = SMTPFactory(u'anotheruser@leap.se',
+ self._km, self._config['host'], self._config['port'],
+ self._config['cert'], self._config['key'],
+ self._config['encrypted_only']).buildProtocol(('127.0.0.1', 0))
user = User(ADDRESS, 'relay.leap.se', proto, ADDRESS)
fromAddr = Address(ADDRESS_2)
- m = EncryptedMessage(fromAddr, user, self._km, self._config)
+ m = EncryptedMessage(
+ fromAddr, user, self._km, self._config['host'],
+ self._config['port'], self._config['cert'], self._config['key'])
for line in self.EMAIL_DATA[4:12]:
m.lineReceived(line)
# trigger encryption and signing
@@ -185,11 +195,15 @@ class TestSmtpRelay(TestCaseWithKeyManager):
"""
# mock the key fetching
self._km.fetch_keys_from_server = Mock(return_value=[])
- proto = SMTPFactory(
- self._km, self._config).buildProtocol(('127.0.0.1', 0))
+ proto = SMTPFactory(u'anotheruser@leap.se',
+ self._km, self._config['host'], self._config['port'],
+ self._config['cert'], self._config['key'],
+ self._config['encrypted_only']).buildProtocol(('127.0.0.1', 0))
user = User('ihavenopubkey@nonleap.se', 'relay.leap.se', proto, ADDRESS)
fromAddr = Address(ADDRESS_2)
- m = EncryptedMessage(fromAddr, user, self._km, self._config)
+ m = EncryptedMessage(
+ fromAddr, user, self._km, self._config['host'],
+ self._config['port'], self._config['cert'], self._config['key'])
for line in self.EMAIL_DATA[4:12]:
m.lineReceived(line)
# trigger signing
@@ -237,8 +251,10 @@ class TestSmtpRelay(TestCaseWithKeyManager):
# mock the key fetching
self._km.fetch_keys_from_server = Mock(return_value=[])
# prepare the SMTP factory
- proto = SMTPFactory(
- self._km, self._config).buildProtocol(('127.0.0.1', 0))
+ proto = SMTPFactory(u'anotheruser@leap.se',
+ self._km, self._config['host'], self._config['port'],
+ self._config['cert'], self._config['key'],
+ self._config['encrypted_only']).buildProtocol(('127.0.0.1', 0))
transport = proto_helpers.StringTransport()
proto.makeConnection(transport)
proto.lineReceived(self.EMAIL_DATA[0] + '\r\n')
@@ -263,11 +279,11 @@ class TestSmtpRelay(TestCaseWithKeyManager):
pgp.delete_key(pubkey)
# mock the key fetching
self._km.fetch_keys_from_server = Mock(return_value=[])
- # change the configuration
- self._config['encrypted_only'] = False
- # prepare the SMTP factory
- proto = SMTPFactory(
- self._km, self._config).buildProtocol(('127.0.0.1', 0))
+ # prepare the SMTP factory with encrypted only equal to false
+ proto = SMTPFactory(u'anotheruser@leap.se',
+ self._km, self._config['host'], self._config['port'],
+ self._config['cert'], self._config['key'],
+ False).buildProtocol(('127.0.0.1', 0))
transport = proto_helpers.StringTransport()
proto.makeConnection(transport)
proto.lineReceived(self.EMAIL_DATA[0] + '\r\n')