diff options
-rw-r--r-- | mail/changes/feature_cert_auth | 1 | ||||
-rw-r--r-- | mail/src/leap/mail/smtp/__init__.py | 20 | ||||
-rw-r--r-- | mail/src/leap/mail/smtp/smtprelay.py | 54 |
3 files changed, 45 insertions, 30 deletions
diff --git a/mail/changes/feature_cert_auth b/mail/changes/feature_cert_auth new file mode 100644 index 00000000..23cdf909 --- /dev/null +++ b/mail/changes/feature_cert_auth @@ -0,0 +1 @@ + o Add client certificate authentication. Closes #3376.
\ No newline at end of file diff --git a/mail/src/leap/mail/smtp/__init__.py b/mail/src/leap/mail/smtp/__init__.py index 78eb4f88..3b4d9d66 100644 --- a/mail/src/leap/mail/smtp/__init__.py +++ b/mail/src/leap/mail/smtp/__init__.py @@ -25,8 +25,8 @@ from twisted.internet import reactor from leap.mail.smtp.smtprelay import SMTPFactory -def setup_smtp_relay(port, keymanager, smtp_host, smtp_port, smtp_username, - smtp_password, encrypted_only): +def setup_smtp_relay(port, keymanager, smtp_host, smtp_port, + smtp_cert, smtp_key, encrypted_only): """ Setup SMTP relay to run with Twisted. @@ -42,10 +42,10 @@ def setup_smtp_relay(port, keymanager, smtp_host, smtp_port, smtp_username, @type smtp_host: str @param smtp_port: The port of the remote SMTP server. @type smtp_port: int - @param smtp_username: The username used to connect to remote SMTP server. - @type smtp_username: str - @param smtp_password: The password used to connect to remote SMTP server. - @type smtp_password: str + @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 relay should send unencrypted mail or not. @type encrypted_only: bool @@ -56,15 +56,15 @@ def setup_smtp_relay(port, keymanager, smtp_host, smtp_port, smtp_username, # { # 'host': '<host>', # 'port': <int>, - # 'username': '<username>', - # 'password': '<password>', + # 'cert': '<cert path>', + # 'key': '<key path>', # 'encrypted_only': <True/False> # } config = { 'host': smtp_host, 'port': smtp_port, - 'username': smtp_username, - 'password': smtp_password, + 'cert': smtp_cert, + 'key': smtp_key, 'encrypted_only': encrypted_only } diff --git a/mail/src/leap/mail/smtp/smtprelay.py b/mail/src/leap/mail/smtp/smtprelay.py index 17388409..e5a56146 100644 --- a/mail/src/leap/mail/smtp/smtprelay.py +++ b/mail/src/leap/mail/smtp/smtprelay.py @@ -19,16 +19,12 @@ LEAP SMTP encrypted relay. """ -import re -import os -import tempfile - - from zope.interface import implements from StringIO import StringIO +from OpenSSL import SSL from twisted.mail import smtp from twisted.internet.protocol import ServerFactory -from twisted.internet import reactor +from twisted.internet import reactor, ssl from twisted.internet import defer from twisted.python import log from email.Header import Header @@ -63,8 +59,8 @@ class MalformedConfig(Exception): HOST_KEY = 'host' PORT_KEY = 'port' -USERNAME_KEY = 'username' -PASSWORD_KEY = 'password' +CERT_KEY = 'cert' +KEY_KEY = 'key' ENCRYPTED_ONLY_KEY = 'encrypted_only' @@ -89,17 +85,17 @@ def assert_config_structure(config): leap_assert_type(config[HOST_KEY], str) leap_assert(PORT_KEY in config) leap_assert_type(config[PORT_KEY], int) - leap_assert(USERNAME_KEY in config) - leap_assert_type(config[USERNAME_KEY], str) - leap_assert(PASSWORD_KEY in config) - leap_assert_type(config[PASSWORD_KEY], str) + leap_assert(CERT_KEY in config) + leap_assert_type(config[CERT_KEY], str) + leap_assert(KEY_KEY in config) + leap_assert_type(config[KEY_KEY], str) 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[USERNAME_KEY] != '') - leap_assert(config[PASSWORD_KEY] != '') + leap_assert(config[CERT_KEY] != '') + leap_assert(config[KEY_KEY] != '') def validate_address(address): @@ -143,8 +139,8 @@ class SMTPFactory(ServerFactory): { HOST_KEY: '<str>', PORT_KEY: <int>, - USERNAME_KEY: '<str>', - PASSWORD_KEY: '<str>', + CERT_KEY: '<str>', + KEY_KEY: '<str>', ENCRYPTED_ONLY_KEY: <bool>, } @type config: dict @@ -294,6 +290,18 @@ class SMTPDelivery(object): # EncryptedMessage # +class CtxFactory(ssl.ClientContextFactory): + def __init__(self, cert, key): + self.cert = cert + self.key = key + + def getContext(self): + self.method = SSL.TLSv1_METHOD #SSLv23_METHOD + ctx = ssl.ClientContextFactory.getContext(self) + ctx.use_certificate_file(self.cert) + ctx.use_privatekey_file(self.key) + return ctx + class EncryptedMessage(object): """ Receive plaintext from client, encrypt it and send message to a @@ -405,17 +413,23 @@ class EncryptedMessage(object): @rtype: twisted.internet.defer.Deferred """ msg = self._message.as_string(False) + + log.msg("Connecting to SMTP server %s:%s" % (self._config[HOST_KEY], + self._config[PORT_KEY])) + d = defer.Deferred() factory = smtp.ESMTPSenderFactory( - self._config[USERNAME_KEY], - self._config[PASSWORD_KEY], + "", + "", self._fromAddress.addrstr, self._user.dest.addrstr, StringIO(msg), d, - requireAuthentication=False, # for now do unauth, see issue #2474 + contextFactory=CtxFactory(self._config[CERT_KEY], + self._config[KEY_KEY]), + requireAuthentication=False, ) - # TODO: Change this to connectSSL when cert auth is in place in the platform + reactor.connectTCP( self._config[HOST_KEY], self._config[PORT_KEY], |