request connection password only when neede. No need to request it for GSSAPI or ANONYMOUS login. see #2465

This commit is contained in:
Yann Leboulanger 2009-02-13 20:24:23 +00:00
parent 95d7e157da
commit 849108b11f
4 changed files with 85 additions and 69 deletions

View File

@ -167,6 +167,7 @@ class Connection(ConnectionHandlers):
# request vcard or os info... to a real JID but act as if it comes from # request vcard or os info... to a real JID but act as if it comes from
# the fake jid # the fake jid
self.groupchat_jids = {} # {ID : groupchat_jid} self.groupchat_jids = {} # {ID : groupchat_jid}
self.pasword_callback = None
self.on_connect_success = None self.on_connect_success = None
self.on_connect_failure = None self.on_connect_failure = None
@ -1815,6 +1816,19 @@ class Connection(ConnectionHandlers):
q.setTagData('password',password) q.setTagData('password',password)
self.connection.send(iq) self.connection.send(iq)
def get_password(self, callback):
if self.password:
callback(self.password)
return
self.pasword_callback = callback
self.dispatch('PASSWORD_REQUIRED', None)
def set_password(self, password):
self.password = password
if self.pasword_callback:
self.pasword_callback(password)
self.pasword_callback = None
def unregister_account(self, on_remove_success): def unregister_account(self, on_remove_success):
# no need to write this as a class method and keep the value of # no need to write this as a class method and keep the value of
# on_remove_success as a class property as pass it as an argument # on_remove_success as a class property as pass it as an argument

View File

@ -212,13 +212,8 @@ class SASL(PlugIn):
self.mechanism = 'DIGEST-MD5' self.mechanism = 'DIGEST-MD5'
elif 'PLAIN' in self.mecs: elif 'PLAIN' in self.mecs:
self.mecs.remove('PLAIN') self.mecs.remove('PLAIN')
sasl_data = u'%s\x00%s\x00%s' % (self.username + '@' + \
self._owner.Server, self.username, self.password)
sasl_data = sasl_data.encode('utf-8').encode('base64').replace(
'\n','')
node = Node('auth', attrs={'xmlns': NS_SASL, 'mechanism': 'PLAIN'},
payload=[sasl_data])
self.mechanism = 'PLAIN' self.mechanism = 'PLAIN'
self._owner._caller.get_password(self.set_password)
else: else:
self.startsasl = SASL_FAILURE self.startsasl = SASL_FAILURE
log.error('I can only use DIGEST-MD5, GSSAPI and PLAIN mecanisms.') log.error('I can only use DIGEST-MD5, GSSAPI and PLAIN mecanisms.')
@ -297,36 +292,21 @@ class SASL(PlugIn):
if 'qop' in chal and ((isinstance(chal['qop'], str) and \ if 'qop' in chal and ((isinstance(chal['qop'], str) and \
chal['qop'] =='auth') or (isinstance(chal['qop'], list) and 'auth' in \ chal['qop'] =='auth') or (isinstance(chal['qop'], list) and 'auth' in \
chal['qop'])): chal['qop'])):
resp = {} self.resp = {}
resp['username'] = self.username self.resp['username'] = self.username
if self.realm: if self.realm:
resp['realm'] = self.realm self.resp['realm'] = self.realm
else: else:
resp['realm'] = self._owner.Server self.resp['realm'] = self._owner.Server
resp['nonce'] = chal['nonce'] self.resp['nonce'] = chal['nonce']
resp['cnonce'] = ''.join("%x" % randint(0, 2**28) for randint in self.resp['cnonce'] = ''.join("%x" % randint(0, 2**28) for randint in
itertools.repeat(random.randint, 7)) itertools.repeat(random.randint, 7))
resp['nc'] = ('00000001') self.resp['nc'] = ('00000001')
resp['qop'] = 'auth' self.resp['qop'] = 'auth'
resp['digest-uri'] = 'xmpp/' + self._owner.Server self.resp['digest-uri'] = 'xmpp/' + self._owner.Server
A1=C([H(C([resp['username'], resp['realm'], self.password])), self.resp['charset'] = 'utf-8'
resp['nonce'], resp['cnonce']]) # Password is now required
A2=C(['AUTHENTICATE',resp['digest-uri']]) self._owner._caller.get_password(self.set_password)
response= HH(C([HH(A1), resp['nonce'], resp['nc'], resp['cnonce'],
resp['qop'], HH(A2)]))
resp['response'] = response
resp['charset'] = 'utf-8'
sasl_data = u''
for key in ('charset', 'username', 'realm', 'nonce', 'nc', 'cnonce',
'digest-uri', 'response', 'qop'):
if key in ('nc','qop','response','charset'):
sasl_data += u"%s=%s," % (key, resp[key])
else:
sasl_data += u'%s="%s",' % (key, resp[key])
sasl_data = sasl_data[:-1].encode('utf-8').encode('base64').replace(
'\r','').replace('\n','')
node = Node('response', attrs={'xmlns':NS_SASL}, payload=[sasl_data])
self._owner.send(str(node))
elif 'rspauth' in chal: elif 'rspauth' in chal:
self._owner.send(str(Node('response', attrs={'xmlns':NS_SASL}))) self._owner.send(str(Node('response', attrs={'xmlns':NS_SASL})))
else: else:
@ -335,6 +315,34 @@ class SASL(PlugIn):
if self.on_sasl: if self.on_sasl:
self.on_sasl() self.on_sasl()
raise NodeProcessed raise NodeProcessed
def set_password(self, password):
self.password = password
if self.mechanism == 'DIGEST-MD5':
A1 = C([H(C([self.resp['username'], self.resp['realm'],
self.password])), self.resp['nonce'], self.resp['cnonce']])
A2 = C(['AUTHENTICATE', self.resp['digest-uri']])
response= HH(C([HH(A1), self.resp['nonce'], self.resp['nc'],
self.resp['cnonce'], self.resp['qop'], HH(A2)]))
self.resp['response'] = response
sasl_data = u''
for key in ('charset', 'username', 'realm', 'nonce', 'nc', 'cnonce',
'digest-uri', 'response', 'qop'):
if key in ('nc','qop','response','charset'):
sasl_data += u"%s=%s," % (key, self.resp[key])
else:
sasl_data += u'%s="%s",' % (key, self.resp[key])
sasl_data = sasl_data[:-1].encode('utf-8').encode('base64').replace(
'\r', '').replace('\n', '')
node = Node('response', attrs={'xmlns':NS_SASL}, payload=[sasl_data])
elif self.mechanism == 'PLAIN':
sasl_data = u'%s\x00%s\x00%s' % (self.username + '@' + \
self._owner.Server, self.username, self.password)
sasl_data = sasl_data.encode('utf-8').encode('base64').replace(
'\n', '')
node = Node('auth', attrs={'xmlns': NS_SASL, 'mechanism': 'PLAIN'},
payload=[sasl_data])
self._owner.send(str(node))
class NonBlockingNonSASL(PlugIn): class NonBlockingNonSASL(PlugIn):

View File

@ -265,6 +265,7 @@ from common import socks5
from common import helpers from common import helpers
from common import optparser from common import optparser
from common import dataforms from common import dataforms
from common import passwords
if verbose: gajim.verbose = True if verbose: gajim.verbose = True
del verbose del verbose
@ -1522,6 +1523,28 @@ class Interface:
self.gpg_passphrase[keyid] = request self.gpg_passphrase[keyid] = request
request.add_callback(account, callback) request.add_callback(account, callback)
def handle_event_password_required(self, account, array):
#('PASSWORD_REQUIRED', account, None)
text = _('Enter your password for account %s') % account
if passwords.USER_HAS_GNOMEKEYRING and \
not passwords.USER_USES_GNOMEKEYRING:
text += '\n' + _('Gnome Keyring is installed but not \
correctly started (environment variable probably not \
correctly set)')
def on_ok(passphrase, save):
if save:
gajim.config.set_per('accounts', account, 'savepass', True)
passwords.save_password(account, passphrase)
gajim.connections[account].set_password(passphrase)
def on_cancel():
self.roster.set_state(account, 'offline')
self.roster.update_status_combobox()
dialogs.PassphraseDialog(_('Password Required'), text, _('Save password'),
ok_handler=on_ok, cancel_handler=on_cancel)
def handle_event_roster_info(self, account, array): def handle_event_roster_info(self, account, array):
#('ROSTER_INFO', account, (jid, name, sub, ask, groups)) #('ROSTER_INFO', account, (jid, name, sub, ask, groups))
jid = array[0] jid = array[0]
@ -2262,6 +2285,7 @@ class Interface:
self.handle_event_unique_room_id_unsupported, self.handle_event_unique_room_id_unsupported,
'UNIQUE_ROOM_ID_SUPPORTED': self.handle_event_unique_room_id_supported, 'UNIQUE_ROOM_ID_SUPPORTED': self.handle_event_unique_room_id_supported,
'GPG_PASSWORD_REQUIRED': self.handle_event_gpg_password_required, 'GPG_PASSWORD_REQUIRED': self.handle_event_gpg_password_required,
'PASSWORD_REQUIRED': self.handle_event_password_required,
'SSL_ERROR': self.handle_event_ssl_error, 'SSL_ERROR': self.handle_event_ssl_error,
'FINGERPRINT_ERROR': self.handle_event_fingerprint_error, 'FINGERPRINT_ERROR': self.handle_event_fingerprint_error,
'PLAIN_CONNECTION': self.handle_event_plain_connection, 'PLAIN_CONNECTION': self.handle_event_plain_connection,

View File

@ -54,7 +54,6 @@ import features_window
from common import gajim from common import gajim
from common import helpers from common import helpers
from common import passwords
from common.exceptions import GajimGeneralException from common.exceptions import GajimGeneralException
from common import i18n from common import i18n
from common import pep from common import pep
@ -1909,13 +1908,16 @@ class RosterWindow:
dialogs.InformationDialog(_('Authorization has been removed'), dialogs.InformationDialog(_('Authorization has been removed'),
_('Now "%s" will always see you as offline.') %jid) _('Now "%s" will always see you as offline.') %jid)
def set_connecting_state(self, account): def set_state(self, account, state):
child_iterA = self._get_account_iter(account, self.model) child_iterA = self._get_account_iter(account, self.model)
if child_iterA: if child_iterA:
self.model[child_iterA][0] = \ self.model[child_iterA][0] = \
gajim.interface.jabber_state_images['16']['connecting'] gajim.interface.jabber_state_images['16'][state]
if gajim.interface.systray_enabled: if gajim.interface.systray_enabled:
gajim.interface.systray.change_status('connecting') gajim.interface.systray.change_status(state)
def set_connecting_state(self, account):
self.set_state(account, 'connecting')
def send_status(self, account, status, txt, auto=False, to=None): def send_status(self, account, status, txt, auto=False, to=None):
child_iterA = self._get_account_iter(account, self.model) child_iterA = self._get_account_iter(account, self.model)
@ -1927,38 +1929,6 @@ class RosterWindow:
if gajim.connections[account].connected < 2: if gajim.connections[account].connected < 2:
self.set_connecting_state(account) self.set_connecting_state(account)
if not gajim.connections[account].password:
text = _('Enter your password for account %s') % account
if passwords.USER_HAS_GNOMEKEYRING and \
not passwords.USER_USES_GNOMEKEYRING:
text += '\n' + _('Gnome Keyring is installed but not \
correctly started (environment variable probably not \
correctly set)')
def on_ok(passphrase, save):
gajim.connections[account].password = passphrase
if save:
gajim.config.set_per('accounts', account, 'savepass', True)
passwords.save_password(account, passphrase)
keyid = gajim.config.get_per('accounts', account, 'keyid')
if keyid and not gajim.connections[account].gpg:
dialogs.WarningDialog(_('GPG is not usable'),
_('You will be connected to %s without OpenPGP.') % \
account)
self.send_status_continue(account, status, txt, auto, to)
def on_cancel():
if child_iterA:
self.model[child_iterA][0] = \
gajim.interface.jabber_state_images['16']['offline']
if gajim.interface.systray_enabled:
gajim.interface.systray.change_status('offline')
self.update_status_combobox()
dialogs.PassphraseDialog(_('Password Required'), text,
_('Save password'), ok_handler=on_ok,
cancel_handler=on_cancel)
return
keyid = gajim.config.get_per('accounts', account, 'keyid') keyid = gajim.config.get_per('accounts', account, 'keyid')
if keyid and not gajim.connections[account].gpg: if keyid and not gajim.connections[account].gpg:
dialogs.WarningDialog(_('GPG is not usable'), dialogs.WarningDialog(_('GPG is not usable'),