esession bugfixes
This commit is contained in:
		
							parent
							
								
									9985b784d5
								
							
						
					
					
						commit
						0230c91e4c
					
				
					 4 changed files with 111 additions and 31 deletions
				
			
		| 
						 | 
				
			
			@ -971,6 +971,9 @@ class ChatControl(ChatControlBase):
 | 
			
		|||
 | 
			
		||||
		self.session = session
 | 
			
		||||
 | 
			
		||||
		# does this window have an existing, active esession?
 | 
			
		||||
		self.esessioned = False
 | 
			
		||||
 | 
			
		||||
		self.status_tooltip = gtk.Tooltips()
 | 
			
		||||
		self.update_ui()
 | 
			
		||||
		# restore previous conversation
 | 
			
		||||
| 
						 | 
				
			
			@ -1236,13 +1239,13 @@ class ChatControl(ChatControlBase):
 | 
			
		|||
 | 
			
		||||
		contact = self.contact
 | 
			
		||||
 | 
			
		||||
		encrypted = bool(self.session) and self.session.enable_encryption
 | 
			
		||||
 | 
			
		||||
		keyID = ''
 | 
			
		||||
		encrypted = False
 | 
			
		||||
		if self.xml.get_widget('gpg_togglebutton').get_active():
 | 
			
		||||
			keyID = contact.keyID
 | 
			
		||||
			encrypted = True
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
		chatstates_on = gajim.config.get('outgoing_chat_state_notifications') != \
 | 
			
		||||
			'disabled'
 | 
			
		||||
		composing_jep = contact.composing_jep
 | 
			
		||||
| 
						 | 
				
			
			@ -1352,18 +1355,46 @@ class ChatControl(ChatControlBase):
 | 
			
		|||
			kind = 'info'
 | 
			
		||||
			name = ''
 | 
			
		||||
		else:
 | 
			
		||||
			ec = gajim.encrypted_chats[self.account]
 | 
			
		||||
			if encrypted and jid not in ec:
 | 
			
		||||
				msg = _('Encryption enabled')
 | 
			
		||||
			# ESessions
 | 
			
		||||
			if self.session.enable_encryption:
 | 
			
		||||
				if not self.esessioned:
 | 
			
		||||
					msg = _('Encryption enabled')
 | 
			
		||||
					ChatControlBase.print_conversation_line(self, msg, 
 | 
			
		||||
						'status', '', tim)
 | 
			
		||||
 | 
			
		||||
					if self.session.loggable:
 | 
			
		||||
						msg = _('Session WILL be logged')
 | 
			
		||||
					else:
 | 
			
		||||
						msg = _('Session WILL NOT be logged')
 | 
			
		||||
 | 
			
		||||
					ChatControlBase.print_conversation_line(self, msg, 
 | 
			
		||||
						'status', '', tim)
 | 
			
		||||
 | 
			
		||||
					self.esessioned = True
 | 
			
		||||
				elif not encrypted:
 | 
			
		||||
					msg = _('The following message was NOT encrypted')
 | 
			
		||||
					ChatControlBase.print_conversation_line(self, msg, 
 | 
			
		||||
						'status', '', tim)
 | 
			
		||||
			elif self.esessioned:
 | 
			
		||||
				msg = _('Encryption disabled')
 | 
			
		||||
				ChatControlBase.print_conversation_line(self, msg, 
 | 
			
		||||
					'status', '', tim)
 | 
			
		||||
				ec.append(jid)
 | 
			
		||||
			elif not encrypted and jid in ec:
 | 
			
		||||
				msg = _('Encryption disabled')
 | 
			
		||||
				ChatControlBase.print_conversation_line(self, msg,
 | 
			
		||||
					'status', '', tim)
 | 
			
		||||
				ec.remove(jid)
 | 
			
		||||
			self.xml.get_widget('gpg_togglebutton').set_active(encrypted)
 | 
			
		||||
				self.esessioned = False
 | 
			
		||||
			else:
 | 
			
		||||
				# GPG encryption
 | 
			
		||||
				ec = gajim.encrypted_chats[self.account]
 | 
			
		||||
				if encrypted and jid not in ec:
 | 
			
		||||
					msg = _('Encryption enabled')
 | 
			
		||||
					ChatControlBase.print_conversation_line(self, msg, 
 | 
			
		||||
						'status', '', tim)
 | 
			
		||||
					ec.append(jid)
 | 
			
		||||
				elif not encrypted and jid in ec:
 | 
			
		||||
					msg = _('Encryption disabled')
 | 
			
		||||
					ChatControlBase.print_conversation_line(self, msg,
 | 
			
		||||
						'status', '', tim)
 | 
			
		||||
					ec.remove(jid)
 | 
			
		||||
				self.xml.get_widget('gpg_togglebutton').set_active(encrypted)
 | 
			
		||||
 | 
			
		||||
			if not frm:
 | 
			
		||||
				kind = 'incoming'
 | 
			
		||||
				name = contact.get_shown_name()
 | 
			
		||||
| 
						 | 
				
			
			@ -1949,9 +1980,15 @@ class ChatControl(ChatControlBase):
 | 
			
		|||
		if self.session and self.session.enable_encryption:
 | 
			
		||||
			self.session.terminate_e2e()
 | 
			
		||||
 | 
			
		||||
			msg = _('Encryption disabled')
 | 
			
		||||
			ChatControlBase.print_conversation_line(self, msg, 
 | 
			
		||||
				'status', '', None)
 | 
			
		||||
			self.esessioned = False
 | 
			
		||||
 | 
			
		||||
			jid = str(self.session.jid)
 | 
			
		||||
 | 
			
		||||
			gajim.connections[self.account].delete_session(jid, self.session.thread_id)
 | 
			
		||||
 | 
			
		||||
			self.session = gajim.connections[self.account].make_new_session(jid)
 | 
			
		||||
		else:
 | 
			
		||||
			if not self.session:
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1451,18 +1451,22 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco,
 | 
			
		|||
			self._InitE2ECB(con, msg, session)
 | 
			
		||||
		
 | 
			
		||||
		encrypted = False
 | 
			
		||||
		tim = msg.getTimestamp()
 | 
			
		||||
		tim = time.strptime(tim, '%Y%m%dT%H:%M:%S')
 | 
			
		||||
		tim = time.localtime(timegm(tim))
 | 
			
		||||
 | 
			
		||||
		e2eTag = msg.getTag('c', namespace = common.xmpp.NS_STANZA_CRYPTO)
 | 
			
		||||
		if e2eTag:
 | 
			
		||||
		e2e_tag = msg.getTag('c', namespace = common.xmpp.NS_STANZA_CRYPTO)
 | 
			
		||||
		if e2e_tag:
 | 
			
		||||
			encrypted = True
 | 
			
		||||
			msg = session.decrypt_stanza(msg)
 | 
			
		||||
 | 
			
		||||
			try:
 | 
			
		||||
				msg = session.decrypt_stanza(msg)
 | 
			
		||||
			except:
 | 
			
		||||
				self.dispatch('FAILED_DECRYPT', (frm, tim))
 | 
			
		||||
 | 
			
		||||
		msgtxt = msg.getBody()
 | 
			
		||||
		msghtml = msg.getXHTML()
 | 
			
		||||
		subject = msg.getSubject() # if not there, it's None
 | 
			
		||||
		tim = msg.getTimestamp()
 | 
			
		||||
		tim = time.strptime(tim, '%Y%m%dT%H:%M:%S')
 | 
			
		||||
		tim = time.localtime(timegm(tim))
 | 
			
		||||
		jid = helpers.get_jid_from_iq(msg)
 | 
			
		||||
		chatstate = None
 | 
			
		||||
		encTag = msg.getTag('x', namespace = common.xmpp.NS_ENCRYPTED)
 | 
			
		||||
| 
						 | 
				
			
			@ -1573,6 +1577,8 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco,
 | 
			
		|||
 | 
			
		||||
	def get_session(self, jid, thread_id, type):
 | 
			
		||||
		'''returns an existing session between this connection and 'jid', returns a new one if none exist.'''
 | 
			
		||||
		print repr(self.sessions)
 | 
			
		||||
 | 
			
		||||
		session = self.find_session(jid, thread_id, type)
 | 
			
		||||
 | 
			
		||||
		if session:
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -289,7 +289,7 @@ class EncryptedStanzaSession(StanzaSession):
 | 
			
		|||
		calculated_mac = self.hmac(self.km_o, macable + self.encode_mpi_with_padding(self.c_o))
 | 
			
		||||
 | 
			
		||||
		if not calculated_mac == received_mac:
 | 
			
		||||
			raise 'bad signature (%s != %s)' % (repr(received_mac), repr(calculated_mac))
 | 
			
		||||
			raise exceptions.DecryptionError, 'bad signature'
 | 
			
		||||
 | 
			
		||||
		m_final = base64.b64decode(c.getTagData('data'))
 | 
			
		||||
		m_compressed = self.decrypt(m_final)
 | 
			
		||||
| 
						 | 
				
			
			@ -298,7 +298,7 @@ class EncryptedStanzaSession(StanzaSession):
 | 
			
		|||
		try:
 | 
			
		||||
			parsed = xmpp.Node(node='<node>' + plaintext + '</node>')
 | 
			
		||||
		except:
 | 
			
		||||
			raise exceptions.DecryptionError
 | 
			
		||||
			raise exceptions.DecryptionError, 'decrypted <data/> not parseable as XML'
 | 
			
		||||
 | 
			
		||||
		for child in parsed.getChildren():
 | 
			
		||||
			stanza.addChild(node=child)
 | 
			
		||||
| 
						 | 
				
			
			@ -377,12 +377,12 @@ class EncryptedStanzaSession(StanzaSession):
 | 
			
		|||
			content += self.form_o + form_o2
 | 
			
		||||
 | 
			
		||||
		mac_o_calculated = self.hmac(self.ks_o, content)
 | 
			
		||||
		
 | 
			
		||||
	
 | 
			
		||||
		if self.negotiated['recv_pubkey']:
 | 
			
		||||
			hash = self.sha256(mac_o_calculated)
 | 
			
		||||
 | 
			
		||||
			if not eir_pubkey.verify(hash, signature):
 | 
			
		||||
				raise exceptions.NegotiationError, 'public key signature verification failed!'
 | 
			
		||||
				raise exceptions.NegotiationError, 'public key signature verification failed!' 
 | 
			
		||||
 | 
			
		||||
		elif mac_o_calculated != mac_o:
 | 
			
		||||
			raise exceptions.NegotiationError, 'calculated mac_%s differs from received mac_%s' % (i_o, i_o)
 | 
			
		||||
| 
						 | 
				
			
			@ -589,7 +589,7 @@ class EncryptedStanzaSession(StanzaSession):
 | 
			
		|||
		self.n_o = base64.b64decode(form['my_nonce'])
 | 
			
		||||
 | 
			
		||||
		dhhashes = form.getField('dhhashes').getValues()
 | 
			
		||||
		self.He = base64.b64decode(dhhashes[group_order].encode("utf8"))
 | 
			
		||||
		self.negotiated['He'] = base64.b64decode(dhhashes[group_order].encode("utf8"))
 | 
			
		||||
 | 
			
		||||
		bytes = int(self.n / 8)
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -741,7 +741,8 @@ class EncryptedStanzaSession(StanzaSession):
 | 
			
		|||
		e = self.decode_mpi(base64.b64decode(form['dhkeys']))
 | 
			
		||||
		p = dh.primes[self.modp]
 | 
			
		||||
 | 
			
		||||
		# XXX return <feature-not-implemented> if hash(e) != He
 | 
			
		||||
		if self.sha256(self.encode_mpi(e)) != self.negotiated['He']:
 | 
			
		||||
			raise exceptions.NegotiationError, 'SHA256(e) != He'
 | 
			
		||||
 | 
			
		||||
		k = self.get_shared_secret(e, self.y, p)
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -903,10 +904,17 @@ class EncryptedStanzaSession(StanzaSession):
 | 
			
		|||
 | 
			
		||||
	def fail_bad_negotiation(self, reason):
 | 
			
		||||
		'''they've tried to feed us a bogus value, send an error and cancel everything.'''
 | 
			
		||||
 | 
			
		||||
		err = xmpp.Error(xmpp.Message(), xmpp.ERR_FEATURE_NOT_IMPLEMENTED)
 | 
			
		||||
		err.T.error.T.text.setData(reason)
 | 
			
		||||
		self.send(err)
 | 
			
		||||
 | 
			
		||||
		self.status = None
 | 
			
		||||
		self.enable_encryption = False
 | 
			
		||||
 | 
			
		||||
		# this prevents the MAC check on decryption from succeeding,
 | 
			
		||||
		# preventing falsified messages from going through.
 | 
			
		||||
		self.km_o = ''
 | 
			
		||||
 | 
			
		||||
	def is_loggable(self):
 | 
			
		||||
		account = self.conn.name
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										43
									
								
								src/gajim.py
									
										
									
									
									
								
							
							
						
						
									
										43
									
								
								src/gajim.py
									
										
									
									
									
								
							| 
						 | 
				
			
			@ -1701,6 +1701,15 @@ class Interface:
 | 
			
		|||
		atom_entry, = data
 | 
			
		||||
		AtomWindow.newAtomEntry(atom_entry)
 | 
			
		||||
 | 
			
		||||
	def handle_event_failed_decrypt(self, account, data):
 | 
			
		||||
		jid, tim = data
 | 
			
		||||
 | 
			
		||||
		ctrl = self.msg_win_mgr.get_control(jid, account)
 | 
			
		||||
		if ctrl:
 | 
			
		||||
			ctrl.print_conversation_line('Unable to decrypt message from %s\nIt may have been tampered with.' % (jid), 'status', '', tim)
 | 
			
		||||
		else:
 | 
			
		||||
			print 'failed decrypt, unable to find a control to notify you in.'
 | 
			
		||||
 | 
			
		||||
	def handle_session_negotiation(self, account, data):
 | 
			
		||||
		jid, session, form = data
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -1713,12 +1722,25 @@ class Interface:
 | 
			
		|||
		# encrypted session states. these are described in stanza_session.py
 | 
			
		||||
 | 
			
		||||
		# bob responds
 | 
			
		||||
		if form.getType() == 'form' and u'e2e' in \
 | 
			
		||||
		map(lambda x: x[1], form.getField('security').getOptions()):
 | 
			
		||||
		if form.getType() == 'form' and 'security' in form.asDict():
 | 
			
		||||
			def continue_with_negotiation(*args):
 | 
			
		||||
				if len(args):
 | 
			
		||||
					self.dialog.destroy()
 | 
			
		||||
 | 
			
		||||
				# we don't support 3-message negotiation as the responder
 | 
			
		||||
				if 'dhkeys' in form.asDict():
 | 
			
		||||
					err = xmpp.Error(xmpp.Message(), xmpp.ERR_FEATURE_NOT_IMPLEMENTED)
 | 
			
		||||
 | 
			
		||||
					feature = xmpp.Node(xmpp.NS_FEATURE + ' feature')
 | 
			
		||||
					field = xmpp.Node('field')
 | 
			
		||||
					field['var'] = 'dhkeys'
 | 
			
		||||
					
 | 
			
		||||
					feature.addChild(node=field)
 | 
			
		||||
					err.addChild(node=feature)
 | 
			
		||||
 | 
			
		||||
					session.send(err)
 | 
			
		||||
					return
 | 
			
		||||
 | 
			
		||||
				negotiated, not_acceptable, ask_user = session.verify_options_bob(form)
 | 
			
		||||
 | 
			
		||||
				if ask_user:
 | 
			
		||||
| 
						 | 
				
			
			@ -1776,8 +1798,11 @@ class Interface:
 | 
			
		|||
					dialog.destroy()
 | 
			
		||||
 | 
			
		||||
					negotiated.update(ask_user)
 | 
			
		||||
				
 | 
			
		||||
					session.accept_e2e_alice(form, negotiated)
 | 
			
		||||
		
 | 
			
		||||
					try:
 | 
			
		||||
						session.accept_e2e_alice(form, negotiated)
 | 
			
		||||
					except exceptions.NegotiationError, details:
 | 
			
		||||
						session.fail_bad_negotiation(details)
 | 
			
		||||
 | 
			
		||||
				def reject_nondefault_options(widget):
 | 
			
		||||
					session.reject_negotiation()
 | 
			
		||||
| 
						 | 
				
			
			@ -1788,7 +1813,10 @@ class Interface:
 | 
			
		|||
						on_response_yes = accept_nondefault_options,
 | 
			
		||||
						on_response_no = reject_nondefault_options)
 | 
			
		||||
			else:
 | 
			
		||||
				session.accept_e2e_alice(form, negotiated)
 | 
			
		||||
				try:
 | 
			
		||||
					session.accept_e2e_alice(form, negotiated)
 | 
			
		||||
				except exceptions.NegotiationError, details: 
 | 
			
		||||
					session.fail_bad_negotiation(details)
 | 
			
		||||
 | 
			
		||||
			return
 | 
			
		||||
		elif session.status == 'responded-e2e' and form.getType() == 'result':
 | 
			
		||||
| 
						 | 
				
			
			@ -1802,7 +1830,7 @@ class Interface:
 | 
			
		|||
			return
 | 
			
		||||
		elif session.status == 'identified-alice' and form.getType() == 'result':
 | 
			
		||||
			session.check_identity = lambda: negotiation.show_sas_dialog(jid, session.sas)
 | 
			
		||||
 | 
			
		||||
			
 | 
			
		||||
			try:
 | 
			
		||||
				session.final_steps_alice(form)
 | 
			
		||||
			except exceptions.NegotiationError, details: 
 | 
			
		||||
| 
						 | 
				
			
			@ -1819,7 +1847,7 @@ class Interface:
 | 
			
		|||
				ctrl = gajim.interface.msg_win_mgr.get_control(str(jid), account)
 | 
			
		||||
 | 
			
		||||
				if ctrl:
 | 
			
		||||
					ctrl.session = gajim.connections[self.account].make_new_session(str(jid))
 | 
			
		||||
					ctrl.session = gajim.connections[account].make_new_session(str(jid))
 | 
			
		||||
 | 
			
		||||
				return
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -2266,6 +2294,7 @@ class Interface:
 | 
			
		|||
			'SIGNED_IN': self.handle_event_signed_in,
 | 
			
		||||
			'METACONTACTS': self.handle_event_metacontacts,
 | 
			
		||||
			'ATOM_ENTRY': self.handle_atom_entry,
 | 
			
		||||
			'FAILED_DECRYPT': self.handle_event_failed_decrypt,
 | 
			
		||||
			'PRIVACY_LISTS_RECEIVED': self.handle_event_privacy_lists_received,
 | 
			
		||||
			'PRIVACY_LIST_RECEIVED': self.handle_event_privacy_list_received,
 | 
			
		||||
			'PRIVACY_LISTS_ACTIVE_DEFAULT': \
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		
		Reference in a new issue