Mercurial > libervia-backend
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 |