comparison sat/plugins/plugin_sec_otr.py @ 2653:7213caa5c5d0

plugin OTR: integrated in new encryption handler + fixed use of bare jid where full jid was expected
author Goffi <goffi@goffi.org>
date Sat, 11 Aug 2018 18:24:55 +0200
parents 189e38fb11ff
children 9190874a8ac5
comparison
equal deleted inserted replaced
2652:baccc27d5c5c 2653:7213caa5c5d0
121 old_state = self.state 121 old_state = self.state
122 super(Context, self).setState(state) 122 super(Context, self).setState(state)
123 log.debug(u"setState: %s (old_state=%s)" % (state, old_state)) 123 log.debug(u"setState: %s (old_state=%s)" % (state, old_state))
124 124
125 if state == potr.context.STATE_PLAINTEXT: 125 if state == potr.context.STATE_PLAINTEXT:
126 client.encryption.stop(self.peer, NS_OTR)
126 feedback = _(u"/!\\ conversation with %(other_jid)s is now UNENCRYPTED") % { 127 feedback = _(u"/!\\ conversation with %(other_jid)s is now UNENCRYPTED") % {
127 "other_jid": self.peer.full() 128 "other_jid": self.peer.full()
128 } 129 }
129 self.host.bridge.otrState( 130 self.host.bridge.otrState(
130 OTR_STATE_UNENCRYPTED, self.peer.full(), client.profile 131 OTR_STATE_UNENCRYPTED, self.peer.full(), client.profile
131 ) 132 )
132 elif state == potr.context.STATE_ENCRYPTED: 133 elif state == potr.context.STATE_ENCRYPTED:
134 client.encryption.start(self.peer, NS_OTR)
133 try: 135 try:
134 trusted = self.getCurrentTrust() 136 trusted = self.getCurrentTrust()
135 except TypeError: 137 except TypeError:
136 trusted = False 138 trusted = False
137 trusted_str = _(u"trusted") if trusted else _(u"untrusted") 139 trusted_str = _(u"trusted") if trusted else _(u"untrusted")
151 ) 153 )
152 self.host.bridge.otrState( 154 self.host.bridge.otrState(
153 OTR_STATE_ENCRYPTED, self.peer.full(), client.profile 155 OTR_STATE_ENCRYPTED, self.peer.full(), client.profile
154 ) 156 )
155 elif state == potr.context.STATE_FINISHED: 157 elif state == potr.context.STATE_FINISHED:
158 client.encryption.stop(self.peer, NS_OTR)
156 feedback = D_(u"OTR conversation with {other_jid} is FINISHED").format( 159 feedback = D_(u"OTR conversation with {other_jid} is FINISHED").format(
157 other_jid=self.peer.full() 160 other_jid=self.peer.full()
158 ) 161 )
159 self.host.bridge.otrState( 162 self.host.bridge.otrState(
160 OTR_STATE_UNENCRYPTED, self.peer.full(), client.profile 163 OTR_STATE_UNENCRYPTED, self.peer.full(), client.profile
301 self._dropPrivKey, 304 self._dropPrivKey,
302 security_limit=0, 305 security_limit=0,
303 type_=C.MENU_SINGLE, 306 type_=C.MENU_SINGLE,
304 ) 307 )
305 host.trigger.add("presenceReceived", self._presenceReceivedTrigger) 308 host.trigger.add("presenceReceived", self._presenceReceivedTrigger)
309 self.host.registerEncryptionPlugin(self, u"OTR", NS_OTR)
306 310
307 def _skipOTR(self, profile): 311 def _skipOTR(self, profile):
308 """Tell the backend to not handle OTR for this profile. 312 """Tell the backend to not handle OTR for this profile.
309 313
310 @param profile (str): %(doc_profile)s 314 @param profile (str): %(doc_profile)s
359 def startRefresh(self, client, to_jid): 363 def startRefresh(self, client, to_jid):
360 """Start or refresh an OTR session 364 """Start or refresh an OTR session
361 365
362 @param to_jid(jid.JID): jid to start encrypted session with 366 @param to_jid(jid.JID): jid to start encrypted session with
363 """ 367 """
368 encrypted_session = client.encryption.getSession(to_jid.userhostJID())
369 if encrypted_session and encrypted_session[u'plugin'].namespace != NS_OTR:
370 raise exceptions.ConflictError(_(
371 u"Can't start an OTR session, there is already an encrypted session "
372 u"with {name}").format(name=encrypted_session[u'plugin'].name))
364 if not to_jid.resource: 373 if not to_jid.resource:
365 to_jid.resource = self.host.memory.getMainResource( 374 to_jid.resource = self.host.memory.getMainResource(
366 client, to_jid 375 client, to_jid
367 ) # FIXME: temporary and unsecure, must be changed when frontends 376 ) # FIXME: temporary and unsecure, must be changed when frontends
368 # are refactored 377 # are refactored
577 u"WARNING: received unencrypted data in a supposedly encrypted " 586 u"WARNING: received unencrypted data in a supposedly encrypted "
578 u"context" 587 u"context"
579 ), 588 ),
580 ) 589 )
581 client.feedback(from_jid, feedback) 590 client.feedback(from_jid, feedback)
591 except potr.context.NotEncryptedError:
592 msg = D_(u"WARNING: received OTR encrypted data in an unencrypted context")
593 log.warning(msg)
594 feedback = msg
595 client.feedback(from_jid, msg)
596 raise failure.Failure(exceptions.CancelError(msg))
582 except StopIteration: 597 except StopIteration:
583 return data 598 return data
584 else: 599 else:
585 encrypted = True 600 encrypted = True
586 601
601 # TODO: add skip history as an option, but by default we don't skip it 616 # TODO: add skip history as an option, but by default we don't skip it
602 # data[u'history'] = C.HISTORY_SKIP # we send the decrypted message to 617 # data[u'history'] = C.HISTORY_SKIP # we send the decrypted message to
603 # frontends, but we don't want it in 618 # frontends, but we don't want it in
604 # history 619 # history
605 else: 620 else:
606 log.warning(
607 u"An encrypted message was expected, but got {}".format(
608 data["message"]
609 )
610 )
611 raise failure.Failure( 621 raise failure.Failure(
612 exceptions.CancelError("Cancelled by OTR") 622 exceptions.CancelError("Cancelled by OTR")
613 ) # no message at all (no history, no signal) 623 ) # no message at all (no history, no signal)
614 return data 624 return data
615 625
644 else: 654 else:
645 post_treat.addCallback(self._receivedTreatment, client) 655 post_treat.addCallback(self._receivedTreatment, client)
646 return True 656 return True
647 657
648 def _sendMessageDataTrigger(self, client, mess_data): 658 def _sendMessageDataTrigger(self, client, mess_data):
649 if not "OTR" in mess_data: 659 encryption = mess_data.get(C.MESS_KEY_ENCRYPTION)
660 if encryption is None or encryption['plugin'].namespace != NS_OTR:
650 return 661 return
651 otrctx = mess_data["OTR"] 662 to_jid = mess_data['to']
663 if not to_jid.resource:
664 to_jid.resource = self.host.memory.getMainResource(
665 client, to_jid
666 ) # FIXME: temporary and unsecure, must be changed when frontends
667 otrctx = client._otr_context_manager.getContextForUser(to_jid)
652 message_elt = mess_data["xml"] 668 message_elt = mess_data["xml"]
653 to_jid = mess_data["to"]
654 if otrctx.state == potr.context.STATE_ENCRYPTED: 669 if otrctx.state == potr.context.STATE_ENCRYPTED:
655 log.debug(u"encrypting message") 670 log.debug(u"encrypting message")
656 body = None 671 body = None
657 for child in list(message_elt.children): 672 for child in list(message_elt.children):
658 if child.name == "body": 673 if child.name == "body":
666 message_elt.children.remove(child) 681 message_elt.children.remove(child)
667 if body is None: 682 if body is None:
668 log.warning(u"No message found") 683 log.warning(u"No message found")
669 else: 684 else:
670 self._p_carbons.setPrivate(message_elt) 685 self._p_carbons.setPrivate(message_elt)
686 self._p_hints.addHintElements(message_elt, [
687 self._p_hints.HINT_NO_COPY,
688 self._p_hints.HINT_NO_PERMANENT_STORE])
671 otrctx.sendMessage(0, unicode(body).encode("utf-8"), appdata=mess_data) 689 otrctx.sendMessage(0, unicode(body).encode("utf-8"), appdata=mess_data)
672 else: 690 else:
673 feedback = D_( 691 feedback = D_(
674 u"Your message was not sent because your correspondent closed the " 692 u"Your message was not sent because your correspondent closed the "
675 u"encrypted conversation on his/her side. " 693 u"encrypted conversation on his/her side. "
687 if client.profile in self.skipped_profiles: 705 if client.profile in self.skipped_profiles:
688 #  FIXME: should not be done on a per-profile basis 706 #  FIXME: should not be done on a per-profile basis
689 return True 707 return True
690 708
691 to_jid = copy.copy(mess_data["to"]) 709 to_jid = copy.copy(mess_data["to"])
710 if client.encryption.getSession(to_jid.userhostJID()):
711 # there is already an encrypted session with this entity
712 return True
713
692 if not to_jid.resource: 714 if not to_jid.resource:
693 to_jid.resource = self.host.memory.getMainResource( 715 to_jid.resource = self.host.memory.getMainResource(
694 client, to_jid 716 client, to_jid
695 ) # FIXME: full jid may not be known 717 ) # FIXME: full jid may not be known
696 718
697 otrctx = client._otr_context_manager.getContextForUser(to_jid) 719 otrctx = client._otr_context_manager.getContextForUser(to_jid)
698 720
699 if otrctx.state != potr.context.STATE_PLAINTEXT: 721 if otrctx.state != potr.context.STATE_PLAINTEXT:
700 self._p_hints.addHint(mess_data, self._p_hints.HINT_NO_COPY) 722 client.encryption.start(to_jid, NS_OTR)
701 self._p_hints.addHint(mess_data, self._p_hints.HINT_NO_PERMANENT_STORE) 723 client.encryption.setEncryptionFlag(mess_data)
702 mess_data["OTR"] = (otrctx) #  this indicate that encryption is needed in
703 # sendMessageData trigger
704 if not mess_data["to"].resource: 724 if not mess_data["to"].resource:
705 # if not resource was given, we force it here 725 # if not resource was given, we force it here
706 mess_data["to"] = to_jid 726 mess_data["to"] = to_jid
707 return True 727 return True
708 728