Mercurial > libervia-backend
diff sat/core/xmpp.py @ 2687:e9cd473a2f46
core (xmpp): server certificate validation:
XMPP server certificate is now checked, and connection is refused (by default) if it's not valid.
Certificate check can be disabled in the new parameter "Configuration/check_certificate".
If certificate checking is disabled, a warning note is sent on every new connection.
Twisted and Wokkel are temporarly monkey patched in sat.core.tls_patches module, until modifications are merged upstream.
author | Goffi <goffi@goffi.org> |
---|---|
date | Sat, 10 Nov 2018 10:16:35 +0100 |
parents | 661f66d41215 |
children | 943e78e18882 |
line wrap: on
line diff
--- a/sat/core/xmpp.py Fri Nov 09 16:17:45 2018 +0100 +++ b/sat/core/xmpp.py Sat Nov 10 10:16:35 2018 +0100 @@ -35,6 +35,7 @@ log = getLogger(__name__) from sat.core import exceptions from sat.memory import encryption +from sat.tools import xml_tools from zope.interface import implements import time import calendar @@ -44,6 +45,7 @@ class SatXMPPEntity(object): """Common code for Client and Component""" + _reason = None # reason of disconnection def __init__(self, host_app, profile, max_retries): @@ -214,6 +216,11 @@ return super(SatXMPPEntity, self)._authd(xmlstream) + if self._reason is not None: + # if we have had trouble to connect we can reset + # the exception as the connection is now working. + del self._reason + # the following Deferred is used to know when we are connected # so we need to be set it to None when connection is lost self._connected = defer.Deferred() @@ -267,6 +274,11 @@ ## connection ## + def _disconnected(self, reason): + # we have to save the reason of disconnection, otherwise it would be lost + self._reason = reason + super(SatXMPPEntity, self)._disconnected(reason) + def connectionLost(self, connector, reason): try: self.keep_alife.stop() @@ -288,9 +300,21 @@ if not self.conn_deferred.called: # FIXME: real error is not gotten here (e.g. if jid is not know by Prosody, # we should have the real error) - self.conn_deferred.errback( - error.StreamError(u"Server unexpectedly closed the connection") - ) + if self._reason is None: + err = error.StreamError(u"Server unexpectedly closed the connection") + else: + err = self._reason + try: + if err.value.args[0][0][2] == "certificate verify failed": + err = exceptions.InvalidCertificate( + _(u"Your server certificate is not valid " + u"(its identity can't be checked).\n\n" + u"This should never happen and may indicate that " + u"somebody is trying to spy on you.\n" + u"Please contact your server administrator.")) + except (IndexError, TypeError): + pass + self.conn_deferred.errback(err) @defer.inlineCallbacks def _cleanConnection(self, __): @@ -544,16 +568,8 @@ trigger_suffix = "" is_component = False - def __init__( - self, - host_app, - profile, - user_jid, - password, - host=None, - port=C.XMPP_C2S_PORT, - max_retries=C.XMPP_MAX_RETRIES, - ): + def __init__(self, host_app, profile, user_jid, password, host=None, + port=C.XMPP_C2S_PORT, max_retries=C.XMPP_MAX_RETRIES): # XXX: DNS SRV records are checked when the host is not specified. # If no SRV record is found, the host is directly extracted from the JID. self.started = time.time() @@ -594,11 +610,24 @@ .format(host_ori=user_jid.host, host=host, port=port) ) + self.check_certificate = host_app.memory.getParamA( + "check_certificate", "Connection", profile_key=profile) + wokkel_client.XMPPClient.__init__( - self, user_jid, password, host or None, port or C.XMPP_C2S_PORT + self, user_jid, password, host or None, port or C.XMPP_C2S_PORT, + check_certificate = self.check_certificate ) SatXMPPEntity.__init__(self, host_app, profile, max_retries) + if not self.check_certificate: + msg = (_(u"Certificate validation is deactivated, this is unsecure and " + u"somebody may be spying on you. If you have no good reason to disable " + u"certificate validation, please activate \"Check certificate\" in your " + u"settings in \"Connection\" tab.")) + xml_tools.quickNote(host_app, self, msg, _(u"Security notice"), + level = C.XMLUI_DATA_LVL_WARNING) + + def _getPluginsList(self): for p in self.host_app.plugins.itervalues(): if C.PLUG_MODE_CLIENT in p._info[u"modes"]: