refactored and corrected identity testing, prompt user when a session is initiated by an unsubscribed jid

This commit is contained in:
Brendan Taylor 2007-07-12 06:25:05 +00:00
parent d32e8352d5
commit 8af883e852
3 changed files with 88 additions and 70 deletions

View File

@ -45,8 +45,16 @@ class SessionBusNotPresent(Exception):
def __str__(self): def __str__(self):
return _('Session bus is not available.\nTry reading http://trac.gajim.org/wiki/GajimDBus') 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): class GajimGeneralException(Exception):
'''This exception ir our general exception''' '''This exception is our general exception'''
def __init__(self, text=''): def __init__(self, text=''):
Exception.__init__(self) Exception.__init__(self)
self.text = text self.text = text

View File

@ -2,6 +2,7 @@ import gajim
from common import xmpp from common import xmpp
from common import helpers from common import helpers
from common import exceptions
import random import random
import string import string
@ -47,12 +48,13 @@ class StanzaSession(object):
return "".join([random.choice(string.letters) for x in xrange(0,32)]) return "".join([random.choice(string.letters) for x in xrange(0,32)])
def send(self, msg): def send(self, msg):
if self.thread_id: if self.thread_id and isinstance(msg, xmpp.Message):
msg.setThread(self.thread_id) msg.setThread(self.thread_id)
msg.setAttr('to', self.jid) msg.setAttr('to', self.jid)
self.conn.send_stanza(msg) self.conn.send_stanza(msg)
if isinstance(msg, xmpp.Message):
self.last_send = time.time() self.last_send = time.time()
def reject_negotiation(self, body = None): def reject_negotiation(self, body = None):
@ -291,7 +293,7 @@ class EncryptedStanzaSession(StanzaSession):
try: try:
parsed = xmpp.Node(node='<node>' + plaintext + '</node>') parsed = xmpp.Node(node='<node>' + plaintext + '</node>')
except: except:
raise DecryptionError raise exceptions.DecryptionError
for child in parsed.getChildren(): for child in parsed.getChildren():
stanza.addChild(node=child) stanza.addChild(node=child)
@ -446,7 +448,7 @@ class EncryptedStanzaSession(StanzaSession):
self.n_o = base64.b64decode(form['my_nonce']) self.n_o = base64.b64decode(form['my_nonce'])
dhhashes = form.getField('dhhashes').getValues() 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) bytes = int(self.n / 8)
@ -586,13 +588,8 @@ class EncryptedStanzaSession(StanzaSession):
e = self.decode_mpi(base64.b64decode(form['dhkeys'])) e = self.decode_mpi(base64.b64decode(form['dhkeys']))
p = dh.primes[self.modp] p = dh.primes[self.modp]
if (not self.sha256(self.encode_mpi(e)) == self.He): or \ if (self.sha256(self.encode_mpi(e)) != self.He) or (not 1 < e < (p - 1)):
(not e > 1 and e < (p - 1)): raise exceptions.NegotiationError, "invalid DH value 'e'"
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
k = self.sha256(self.encode_mpi(self.powmod(e, self.y, p))) 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) m_a_calculated = self.hmac(self.km_o, self.encode_mpi(self.c_o) + id_a)
if m_a_calculated != m_a: if m_a_calculated != m_a:
err = xmpp.Error(response, xmpp.ERR_FEATURE_NOT_IMPLEMENTED) raise exceptions.NegotiationError, 'calculated m_a differs from received m_a'
err.T.error.T.text.setData('calculated m_a differs from received m_a')
self.send(err)
self.status = None
return
mac_a = self.decrypt(id_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) 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: if mac_a_calculated != mac_a:
err = xmpp.Error(response, xmpp.ERR_FEATURE_NOT_IMPLEMENTED) raise exceptions.NegotiationError, 'calculated mac_a differs from received mac_a'
err.T.error.T.text.setData('calculated mac_a differs from received mac_a')
self.send(err)
self.status = None
return
# 4.5.4 generating bob's final session keys # 4.5.4 generating bob's final session keys
self.srs = '' self.srs = ''
@ -653,7 +642,8 @@ class EncryptedStanzaSession(StanzaSession):
form_b2 = ''.join(map(lambda el: xmpp.c14n.c14n(el), x.getChildren())) form_b2 = ''.join(map(lambda el: xmpp.c14n.c14n(el), x.getChildren()))
old_c_s = self.c_s 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) id_b = self.encrypt(mac_b)
m_b = self.hmac(self.km_s, self.encode_mpi(old_c_s) + id_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']) m_b = base64.b64decode(form['mac'])
id_b = base64.b64decode(form['identity']) 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: if m_b_calculated != m_b:
err = xmpp.Error(response, xmpp.ERR_FEATURE_NOT_IMPLEMENTED) raise exceptions.NegotiationError, 'calculated m_b differs from received m_b'
err.T.error.T.text.setData('calculated m_b differs from received m_b')
self.send(err)
self.status = None
return
mac_b = self.decrypt(id_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: if mac_b_calculated != mac_b:
err = xmpp.Error(response, xmpp.ERR_FEATURE_NOT_IMPLEMENTED) raise exceptions.NegotiationError, 'calculated mac_b differs from received mac_b'
err.T.error.T.text.setData('calculated mac_b differs from received mac_b')
self.send(err)
self.status = None
return
# Note: If Alice discovers an error then she SHOULD ignore any encrypted content she received in the stanza. # 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 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): def is_loggable(self):
name = self.conn.name name = self.conn.name
no_log_for = gajim.config.get_per('accounts', name, 'no_log_for') no_log_for = gajim.config.get_per('accounts', name, 'no_log_for')

View File

@ -1669,29 +1669,25 @@ class Interface:
# bob responds # bob responds
if form.getType() == 'form' and u'e2e' in \ if form.getType() == 'form' and u'e2e' in \
map(lambda x: x[1], form.getField('security').getOptions()): 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):
if gajim.SHOW_LIST[gajim.connections[account].connected] == 'invisible' or \ self.dialog.destroy()
contact.sub not in ('from', 'both'):
return
negotiated, not_acceptable, ask_user = session.verify_options_bob(form) negotiated, not_acceptable, ask_user = session.verify_options_bob(form)
if ask_user: if ask_user:
def accept_nondefault_options(widget): def accept_nondefault_options(widget):
self.dialog.destroy()
negotiated.update(ask_user) negotiated.update(ask_user)
session.respond_e2e_bob(form, negotiated, not_acceptable) session.respond_e2e_bob(form, negotiated, not_acceptable)
dialog.destroy()
def reject_nondefault_options(widget): def reject_nondefault_options(widget):
self.dialog.destroy()
for key in ask_user.keys(): for key in ask_user.keys():
not_acceptable.append(key) not_acceptable.append(key)
session.respond_e2e_bob(form, negotiated, not_acceptable) session.respond_e2e_bob(form, negotiated, not_acceptable)
dialog.destroy() self.dialog = dialogs.YesNoDialog(_('Confirm these session options'),
dialog = dialogs.YesNoDialog(_('Confirm these session options'),
_('''The remote client wants to negotiate an session with these features: _('''The remote client wants to negotiate an session with these features:
%s %s
@ -1702,6 +1698,22 @@ Are these options acceptable?''') % (negotiation.describe_features(ask_user)),
else: else:
session.respond_e2e_bob(form, negotiated, not_acceptable) session.respond_e2e_bob(form, negotiated, not_acceptable)
def ignore_negotiation(widget):
self.dialog.destroy()
return
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:
continue_with_negotiation()
return return
# alice accepts # alice accepts
@ -1727,16 +1739,24 @@ Are these options acceptable?''') % (negotiation.describe_features(ask_user)),
on_response_no = reject_nondefault_options) on_response_no = reject_nondefault_options)
else: else:
session.accept_e2e_alice(form, negotiated) session.accept_e2e_alice(form, negotiated)
negotiation.show_sas_dialog(jid, session.sas) negotiation.show_sas_dialog(jid, session.sas)
return return
elif session.status == 'responded-e2e' and form.getType() == 'result': elif session.status == 'responded-e2e' and form.getType() == 'result':
try:
session.accept_e2e_bob(form) session.accept_e2e_bob(form)
except exceptions.NegotiationError, details:
session.fail_bad_negotiation(details)
else:
negotiation.show_sas_dialog(jid, session.sas) negotiation.show_sas_dialog(jid, session.sas)
return return
elif session.status == 'identified-alice' and form.getType() == 'result': elif session.status == 'identified-alice' and form.getType() == 'result':
try:
session.final_steps_alice(form) session.final_steps_alice(form)
except exceptions.NegotiationError, details:
session.fail_bad_negotiation(details)
return return
if form.getField('terminate'): if form.getField('terminate'):