summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--mail/changes/feature_cert_auth1
-rw-r--r--mail/src/leap/mail/smtp/__init__.py20
-rw-r--r--mail/src/leap/mail/smtp/smtprelay.py54
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 0000000..23cdf90
--- /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 78eb4f8..3b4d9d6 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 1738840..e5a5614 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],