refactored and corrected identity testing, prompt user when a session is initiated by an unsubscribed jid
This commit is contained in:
parent
d32e8352d5
commit
8af883e852
|
@ -45,8 +45,16 @@ class SessionBusNotPresent(Exception):
|
|||
def __str__(self):
|
||||
return _('Session bus is not available.\nTry reading http://trac.gajim.org/wiki/GajimDBus')
|
||||
|
||||
class NegotiationError(Exception):
|
||||
'''A session negotiation failed'''
|
||||
pass
|
||||
|
||||
class DecryptionError(Exception):
|
||||
'''A message couldn't be decrypted into usable XML'''
|
||||
pass
|
||||
|
||||
class GajimGeneralException(Exception):
|
||||
'''This exception ir our general exception'''
|
||||
'''This exception is our general exception'''
|
||||
def __init__(self, text=''):
|
||||
Exception.__init__(self)
|
||||
self.text = text
|
||||
|
|
|
@ -2,6 +2,7 @@ import gajim
|
|||
|
||||
from common import xmpp
|
||||
from common import helpers
|
||||
from common import exceptions
|
||||
|
||||
import random
|
||||
import string
|
||||
|
@ -47,13 +48,14 @@ class StanzaSession(object):
|
|||
return "".join([random.choice(string.letters) for x in xrange(0,32)])
|
||||
|
||||
def send(self, msg):
|
||||
if self.thread_id:
|
||||
if self.thread_id and isinstance(msg, xmpp.Message):
|
||||
msg.setThread(self.thread_id)
|
||||
|
||||
msg.setAttr('to', self.jid)
|
||||
self.conn.send_stanza(msg)
|
||||
|
||||
self.last_send = time.time()
|
||||
|
||||
if isinstance(msg, xmpp.Message):
|
||||
self.last_send = time.time()
|
||||
|
||||
def reject_negotiation(self, body = None):
|
||||
msg = xmpp.Message()
|
||||
|
@ -291,7 +293,7 @@ class EncryptedStanzaSession(StanzaSession):
|
|||
try:
|
||||
parsed = xmpp.Node(node='<node>' + plaintext + '</node>')
|
||||
except:
|
||||
raise DecryptionError
|
||||
raise exceptions.DecryptionError
|
||||
|
||||
for child in parsed.getChildren():
|
||||
stanza.addChild(node=child)
|
||||
|
@ -446,7 +448,7 @@ class EncryptedStanzaSession(StanzaSession):
|
|||
self.n_o = base64.b64decode(form['my_nonce'])
|
||||
|
||||
dhhashes = form.getField('dhhashes').getValues()
|
||||
self.He = dhhashes[group_order].encode("utf8")
|
||||
self.He = base64.b64decode(dhhashes[group_order].encode("utf8"))
|
||||
|
||||
bytes = int(self.n / 8)
|
||||
|
||||
|
@ -586,13 +588,8 @@ class EncryptedStanzaSession(StanzaSession):
|
|||
e = self.decode_mpi(base64.b64decode(form['dhkeys']))
|
||||
p = dh.primes[self.modp]
|
||||
|
||||
if (not self.sha256(self.encode_mpi(e)) == self.He): or \
|
||||
(not e > 1 and e < (p - 1)):
|
||||
err = xmpp.Error(response, xmpp.ERR_FEATURE_NOT_IMPLEMENTED)
|
||||
err.T.error.T.text.setData("invalid DH value 'e'")
|
||||
self.send(err)
|
||||
self.status = None
|
||||
return
|
||||
if (self.sha256(self.encode_mpi(e)) != self.He) or (not 1 < e < (p - 1)):
|
||||
raise exceptions.NegotiationError, "invalid DH value 'e'"
|
||||
|
||||
k = self.sha256(self.encode_mpi(self.powmod(e, self.y, p)))
|
||||
|
||||
|
@ -605,11 +602,7 @@ class EncryptedStanzaSession(StanzaSession):
|
|||
m_a_calculated = self.hmac(self.km_o, self.encode_mpi(self.c_o) + id_a)
|
||||
|
||||
if m_a_calculated != m_a:
|
||||
err = xmpp.Error(response, xmpp.ERR_FEATURE_NOT_IMPLEMENTED)
|
||||
err.T.error.T.text.setData('calculated m_a differs from received m_a')
|
||||
self.send(err)
|
||||
self.status = None
|
||||
return
|
||||
raise exceptions.NegotiationError, 'calculated m_a differs from received m_a'
|
||||
|
||||
mac_a = self.decrypt(id_a)
|
||||
|
||||
|
@ -619,11 +612,7 @@ class EncryptedStanzaSession(StanzaSession):
|
|||
mac_a_calculated = self.hmac(self.ks_o, self.n_s + self.n_o + self.encode_mpi(e) + self.form_a + form_a2)
|
||||
|
||||
if mac_a_calculated != mac_a:
|
||||
err = xmpp.Error(response, xmpp.ERR_FEATURE_NOT_IMPLEMENTED)
|
||||
err.T.error.T.text.setData('calculated mac_a differs from received mac_a')
|
||||
self.send(err)
|
||||
self.status = None
|
||||
return
|
||||
raise exceptions.NegotiationError, 'calculated mac_a differs from received mac_a'
|
||||
|
||||
# 4.5.4 generating bob's final session keys
|
||||
self.srs = ''
|
||||
|
@ -653,7 +642,8 @@ class EncryptedStanzaSession(StanzaSession):
|
|||
form_b2 = ''.join(map(lambda el: xmpp.c14n.c14n(el), x.getChildren()))
|
||||
|
||||
old_c_s = self.c_s
|
||||
mac_b = self.hmac(self.n_o + self.n_s + self.encode_mpi(self.d) + self.form_b + form_b2, self.ks_s)
|
||||
|
||||
mac_b = self.hmac(self.ks_s, self.n_o + self.n_s + self.encode_mpi(self.d) + self.form_b + form_b2)
|
||||
id_b = self.encrypt(mac_b)
|
||||
|
||||
m_b = self.hmac(self.km_s, self.encode_mpi(old_c_s) + id_b)
|
||||
|
@ -703,27 +693,20 @@ class EncryptedStanzaSession(StanzaSession):
|
|||
m_b = base64.b64decode(form['mac'])
|
||||
id_b = base64.b64decode(form['identity'])
|
||||
|
||||
m_b_calculated = self.hmac(self.encode_mpi(self.c_o) + id_b, self.km_o)
|
||||
m_b_calculated = self.hmac(self.km_o, self.encode_mpi(self.c_o) + id_b)
|
||||
|
||||
if m_b_calculated != m_b:
|
||||
err = xmpp.Error(response, xmpp.ERR_FEATURE_NOT_IMPLEMENTED)
|
||||
err.T.error.T.text.setData('calculated m_b differs from received m_b')
|
||||
self.send(err)
|
||||
self.status = None
|
||||
return
|
||||
raise exceptions.NegotiationError, 'calculated m_b differs from received m_b'
|
||||
|
||||
mac_b = self.decrypt(id_b)
|
||||
|
||||
form_b2 = ''.join(map(lambda el: xmpp.c14n.c14n(el), form.getChildren()))
|
||||
macable_children = filter(lambda x: x.getVar() not in ('mac', 'identity'), form.getChildren())
|
||||
form_b2 = ''.join(map(lambda el: xmpp.c14n.c14n(el), macable_children))
|
||||
|
||||
mac_b_calculated = self.hmac(self.n_s + self.n_o + self.encode_mpi(self.d) + self.form_b + form_b2, self.ks_o)
|
||||
mac_b_calculated = self.hmac(self.ks_o, self.n_s + self.n_o + self.encode_mpi(self.d) + self.form_b + form_b2)
|
||||
|
||||
if mac_b_calculated != mac_b:
|
||||
err = xmpp.Error(response, xmpp.ERR_FEATURE_NOT_IMPLEMENTED)
|
||||
err.T.error.T.text.setData('calculated mac_b differs from received mac_b')
|
||||
self.send(err)
|
||||
self.status = None
|
||||
return
|
||||
raise exceptions.NegotiationError, 'calculated mac_b differs from received mac_b'
|
||||
|
||||
# Note: If Alice discovers an error then she SHOULD ignore any encrypted content she received in the stanza.
|
||||
|
||||
|
@ -782,6 +765,13 @@ class EncryptedStanzaSession(StanzaSession):
|
|||
|
||||
self.enable_encryption = False
|
||||
|
||||
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
|
||||
|
||||
def is_loggable(self):
|
||||
name = self.conn.name
|
||||
no_log_for = gajim.config.get_per('accounts', name, 'no_log_for')
|
||||
|
|
86
src/gajim.py
86
src/gajim.py
|
@ -1669,38 +1669,50 @@ class Interface:
|
|||
# bob responds
|
||||
if form.getType() == 'form' and u'e2e' in \
|
||||
map(lambda x: x[1], form.getField('security').getOptions()):
|
||||
contact = gajim.contacts.get_contact(account, jid.getStripped(), jid.getResource())
|
||||
def continue_with_negotiation(*args):
|
||||
if len(args):
|
||||
self.dialog.destroy()
|
||||
|
||||
if gajim.SHOW_LIST[gajim.connections[account].connected] == 'invisible' or \
|
||||
contact.sub not in ('from', 'both'):
|
||||
negotiated, not_acceptable, ask_user = session.verify_options_bob(form)
|
||||
|
||||
if ask_user:
|
||||
def accept_nondefault_options(widget):
|
||||
self.dialog.destroy()
|
||||
negotiated.update(ask_user)
|
||||
session.respond_e2e_bob(form, negotiated, not_acceptable)
|
||||
|
||||
def reject_nondefault_options(widget):
|
||||
self.dialog.destroy()
|
||||
for key in ask_user.keys():
|
||||
not_acceptable.append(key)
|
||||
session.respond_e2e_bob(form, negotiated, not_acceptable)
|
||||
|
||||
self.dialog = dialogs.YesNoDialog(_('Confirm these session options'),
|
||||
_('''The remote client wants to negotiate an session with these features:
|
||||
|
||||
%s
|
||||
|
||||
Are these options acceptable?''') % (negotiation.describe_features(ask_user)),
|
||||
on_response_yes = accept_nondefault_options,
|
||||
on_response_no = reject_nondefault_options)
|
||||
else:
|
||||
session.respond_e2e_bob(form, negotiated, not_acceptable)
|
||||
|
||||
def ignore_negotiation(widget):
|
||||
self.dialog.destroy()
|
||||
return
|
||||
|
||||
negotiated, not_acceptable, ask_user = session.verify_options_bob(form)
|
||||
|
||||
if ask_user:
|
||||
def accept_nondefault_options(widget):
|
||||
negotiated.update(ask_user)
|
||||
session.respond_e2e_bob(form, negotiated, not_acceptable)
|
||||
|
||||
dialog.destroy()
|
||||
|
||||
def reject_nondefault_options(widget):
|
||||
for key in ask_user.keys():
|
||||
not_acceptable.append(key)
|
||||
session.respond_e2e_bob(form, negotiated, not_acceptable)
|
||||
|
||||
dialog.destroy()
|
||||
|
||||
dialog = dialogs.YesNoDialog(_('Confirm these session options'),
|
||||
_('''The remote client wants to negotiate an session with these features:
|
||||
|
||||
%s
|
||||
|
||||
Are these options acceptable?''') % (negotiation.describe_features(ask_user)),
|
||||
on_response_yes = accept_nondefault_options,
|
||||
on_response_no = reject_nondefault_options)
|
||||
|
||||
contact = gajim.contacts.get_contact_with_highest_priority(account, str(jid))
|
||||
|
||||
if gajim.SHOW_LIST[gajim.connections[account].connected] == 'invisible' or not contact or\
|
||||
contact.sub not in ('from', 'both'):
|
||||
self.dialog = dialogs.YesNoDialog(_('Start session?'),
|
||||
_('''%s would like to start a session with you. Should I respond?''') % jid,
|
||||
on_response_yes = continue_with_negotiation,
|
||||
on_response_no = ignore_negotiation,
|
||||
)
|
||||
else:
|
||||
session.respond_e2e_bob(form, negotiated, not_acceptable)
|
||||
continue_with_negotiation()
|
||||
|
||||
return
|
||||
|
||||
|
@ -1727,16 +1739,24 @@ Are these options acceptable?''') % (negotiation.describe_features(ask_user)),
|
|||
on_response_no = reject_nondefault_options)
|
||||
else:
|
||||
session.accept_e2e_alice(form, negotiated)
|
||||
|
||||
negotiation.show_sas_dialog(jid, session.sas)
|
||||
|
||||
return
|
||||
elif session.status == 'responded-e2e' and form.getType() == 'result':
|
||||
session.accept_e2e_bob(form)
|
||||
negotiation.show_sas_dialog(jid, session.sas)
|
||||
try:
|
||||
session.accept_e2e_bob(form)
|
||||
except exceptions.NegotiationError, details:
|
||||
session.fail_bad_negotiation(details)
|
||||
else:
|
||||
negotiation.show_sas_dialog(jid, session.sas)
|
||||
|
||||
return
|
||||
elif session.status == 'identified-alice' and form.getType() == 'result':
|
||||
session.final_steps_alice(form)
|
||||
try:
|
||||
session.final_steps_alice(form)
|
||||
except exceptions.NegotiationError, details:
|
||||
session.fail_bad_negotiation(details)
|
||||
|
||||
return
|
||||
|
||||
if form.getField('terminate'):
|
||||
|
|
Loading…
Reference in New Issue