comparison 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
comparison
equal deleted inserted replaced
2686:ce1e15d59496 2687:e9cd473a2f46
33 from sat.core.log import getLogger 33 from sat.core.log import getLogger
34 34
35 log = getLogger(__name__) 35 log = getLogger(__name__)
36 from sat.core import exceptions 36 from sat.core import exceptions
37 from sat.memory import encryption 37 from sat.memory import encryption
38 from sat.tools import xml_tools
38 from zope.interface import implements 39 from zope.interface import implements
39 import time 40 import time
40 import calendar 41 import calendar
41 import uuid 42 import uuid
42 import sys 43 import sys
43 44
44 45
45 class SatXMPPEntity(object): 46 class SatXMPPEntity(object):
46 """Common code for Client and Component""" 47 """Common code for Client and Component"""
48 _reason = None # reason of disconnection
47 49
48 def __init__(self, host_app, profile, max_retries): 50 def __init__(self, host_app, profile, max_retries):
49 51
50 self.factory.clientConnectionLost = self.connectionLost 52 self.factory.clientConnectionLost = self.connectionLost
51 self.factory.maxRetries = max_retries 53 self.factory.maxRetries = max_retries
212 def _authd(self, xmlstream): 214 def _authd(self, xmlstream):
213 if not self.host_app.trigger.point("XML Initialized", xmlstream, self.profile): 215 if not self.host_app.trigger.point("XML Initialized", xmlstream, self.profile):
214 return 216 return
215 super(SatXMPPEntity, self)._authd(xmlstream) 217 super(SatXMPPEntity, self)._authd(xmlstream)
216 218
219 if self._reason is not None:
220 # if we have had trouble to connect we can reset
221 # the exception as the connection is now working.
222 del self._reason
223
217 # the following Deferred is used to know when we are connected 224 # the following Deferred is used to know when we are connected
218 # so we need to be set it to None when connection is lost 225 # so we need to be set it to None when connection is lost
219 self._connected = defer.Deferred() 226 self._connected = defer.Deferred()
220 self._connected.addCallback(self._cleanConnection) 227 self._connected.addCallback(self._cleanConnection)
221 self._connected.addCallback(self._disconnectionCb) 228 self._connected.addCallback(self._disconnectionCb)
264 except: 271 except:
265 # we already chained an errback, no need to raise an exception 272 # we already chained an errback, no need to raise an exception
266 pass 273 pass
267 274
268 ## connection ## 275 ## connection ##
276
277 def _disconnected(self, reason):
278 # we have to save the reason of disconnection, otherwise it would be lost
279 self._reason = reason
280 super(SatXMPPEntity, self)._disconnected(reason)
269 281
270 def connectionLost(self, connector, reason): 282 def connectionLost(self, connector, reason):
271 try: 283 try:
272 self.keep_alife.stop() 284 self.keep_alife.stop()
273 except AttributeError: 285 except AttributeError:
286 ) 298 )
287 ) 299 )
288 if not self.conn_deferred.called: 300 if not self.conn_deferred.called:
289 # FIXME: real error is not gotten here (e.g. if jid is not know by Prosody, 301 # FIXME: real error is not gotten here (e.g. if jid is not know by Prosody,
290 # we should have the real error) 302 # we should have the real error)
291 self.conn_deferred.errback( 303 if self._reason is None:
292 error.StreamError(u"Server unexpectedly closed the connection") 304 err = error.StreamError(u"Server unexpectedly closed the connection")
293 ) 305 else:
306 err = self._reason
307 try:
308 if err.value.args[0][0][2] == "certificate verify failed":
309 err = exceptions.InvalidCertificate(
310 _(u"Your server certificate is not valid "
311 u"(its identity can't be checked).\n\n"
312 u"This should never happen and may indicate that "
313 u"somebody is trying to spy on you.\n"
314 u"Please contact your server administrator."))
315 except (IndexError, TypeError):
316 pass
317 self.conn_deferred.errback(err)
294 318
295 @defer.inlineCallbacks 319 @defer.inlineCallbacks
296 def _cleanConnection(self, __): 320 def _cleanConnection(self, __):
297 """method called on disconnection 321 """method called on disconnection
298 322
542 class SatXMPPClient(SatXMPPEntity, wokkel_client.XMPPClient): 566 class SatXMPPClient(SatXMPPEntity, wokkel_client.XMPPClient):
543 implements(iwokkel.IDisco) 567 implements(iwokkel.IDisco)
544 trigger_suffix = "" 568 trigger_suffix = ""
545 is_component = False 569 is_component = False
546 570
547 def __init__( 571 def __init__(self, host_app, profile, user_jid, password, host=None,
548 self, 572 port=C.XMPP_C2S_PORT, max_retries=C.XMPP_MAX_RETRIES):
549 host_app,
550 profile,
551 user_jid,
552 password,
553 host=None,
554 port=C.XMPP_C2S_PORT,
555 max_retries=C.XMPP_MAX_RETRIES,
556 ):
557 # XXX: DNS SRV records are checked when the host is not specified. 573 # XXX: DNS SRV records are checked when the host is not specified.
558 # If no SRV record is found, the host is directly extracted from the JID. 574 # If no SRV record is found, the host is directly extracted from the JID.
559 self.started = time.time() 575 self.started = time.time()
560 576
561 # Currently, we use "client/pc/Salut à Toi", but as 577 # Currently, we use "client/pc/Salut à Toi", but as
592 log.info( 608 log.info(
593 u"using {host}:{port} for host {host_ori} as requested in config" 609 u"using {host}:{port} for host {host_ori} as requested in config"
594 .format(host_ori=user_jid.host, host=host, port=port) 610 .format(host_ori=user_jid.host, host=host, port=port)
595 ) 611 )
596 612
613 self.check_certificate = host_app.memory.getParamA(
614 "check_certificate", "Connection", profile_key=profile)
615
597 wokkel_client.XMPPClient.__init__( 616 wokkel_client.XMPPClient.__init__(
598 self, user_jid, password, host or None, port or C.XMPP_C2S_PORT 617 self, user_jid, password, host or None, port or C.XMPP_C2S_PORT,
618 check_certificate = self.check_certificate
599 ) 619 )
600 SatXMPPEntity.__init__(self, host_app, profile, max_retries) 620 SatXMPPEntity.__init__(self, host_app, profile, max_retries)
621
622 if not self.check_certificate:
623 msg = (_(u"Certificate validation is deactivated, this is unsecure and "
624 u"somebody may be spying on you. If you have no good reason to disable "
625 u"certificate validation, please activate \"Check certificate\" in your "
626 u"settings in \"Connection\" tab."))
627 xml_tools.quickNote(host_app, self, msg, _(u"Security notice"),
628 level = C.XMLUI_DATA_LVL_WARNING)
629
601 630
602 def _getPluginsList(self): 631 def _getPluginsList(self):
603 for p in self.host_app.plugins.itervalues(): 632 for p in self.host_app.plugins.itervalues():
604 if C.PLUG_MODE_CLIENT in p._info[u"modes"]: 633 if C.PLUG_MODE_CLIENT in p._info[u"modes"]:
605 yield p 634 yield p