check DIGEST-MD% and SCRAM-SHA-1 latest answer from server before accepting authentication. Fixes #6940

This commit is contained in:
Yann Leboulanger 2011-08-21 23:14:11 +02:00
parent 1482d1e10b
commit fa3ebd9874
1 changed files with 33 additions and 18 deletions

View File

@ -271,13 +271,14 @@ class SASL(PlugIn):
""" """
if challenge.getNamespace() != NS_SASL: if challenge.getNamespace() != NS_SASL:
return return
def scram_base64(s):
return ''.join(s.encode('base64').split('\n'))
incoming_data = challenge.getData()
data=base64.decodestring(incoming_data)
### Handle Auth result ### Handle Auth result
if challenge.getName() == 'failure': def on_auth_fail(reason):
self.startsasl = SASL_FAILURE
try:
reason = challenge.getChildren()[0]
except Exception:
reason = challenge
log.info('Failed SASL authentification: %s' % reason) log.info('Failed SASL authentification: %s' % reason)
if len(self.mecs) > 0: if len(self.mecs) > 0:
# There are other mechanisms to test # There are other mechanisms to test
@ -286,9 +287,21 @@ class SASL(PlugIn):
if self.on_sasl: if self.on_sasl:
self.on_sasl() self.on_sasl()
raise NodeProcessed raise NodeProcessed
if challenge.getName() == 'failure':
self.startsasl = SASL_FAILURE
try:
reason = challenge.getChildren()[0]
except Exception:
reason = challenge
on_auth_fail(reason)
elif challenge.getName() == 'success': elif challenge.getName() == 'success':
# TODO: Need to validate any data-with-success. if self.mechanism == 'SCRAM-SHA-1':
# TODO: Important for DIGEST-MD5 and SCRAM. # check data-with-success
data = scram_parse(data)
if data['v'] != scram_base64(self.scram_ServerSignature):
on_auth_fail('ServerSignature is wrong')
self.startsasl = SASL_SUCCESS self.startsasl = SASL_SUCCESS
log.info('Successfully authenticated with remote server.') log.info('Successfully authenticated with remote server.')
handlers = self._owner.Dispatcher.dumpHandlers() handlers = self._owner.Dispatcher.dumpHandlers()
@ -309,8 +322,6 @@ class SASL(PlugIn):
raise NodeProcessed raise NodeProcessed
### Perform auth step ### Perform auth step
incoming_data = challenge.getData()
data=base64.decodestring(incoming_data)
log.info('Got challenge:' + data) log.info('Got challenge:' + data)
if self.mechanism == 'GSSAPI': if self.mechanism == 'GSSAPI':
@ -353,12 +364,9 @@ class SASL(PlugIn):
ui = XOR(ui, ui_1) ui = XOR(ui, ui_1)
return ui return ui
def H(s): def scram_H(s):
return hashfn(s).digest() return hashfn(s).digest()
def scram_base64(s):
return ''.join(s.encode('base64').split('\n'))
if self.scram_step == 0: if self.scram_step == 0:
self.scram_step = 1 self.scram_step = 1
self.scram_soup += ',' + data + ',' self.scram_soup += ',' + data + ','
@ -373,7 +381,7 @@ class SASL(PlugIn):
SaltedPassword = Hi(self.password, salt, iter) SaltedPassword = Hi(self.password, salt, iter)
# TODO: Could cache this, along with salt+iter. # TODO: Could cache this, along with salt+iter.
ClientKey = HMAC(SaltedPassword, 'Client Key') ClientKey = HMAC(SaltedPassword, 'Client Key')
StoredKey = H(ClientKey) StoredKey = scram_H(ClientKey)
ClientSignature = HMAC(StoredKey, self.scram_soup) ClientSignature = HMAC(StoredKey, self.scram_soup)
ClientProof = XOR(ClientKey, ClientSignature) ClientProof = XOR(ClientKey, ClientSignature)
r += ',p=' + scram_base64(ClientProof) r += ',p=' + scram_base64(ClientProof)
@ -417,6 +425,9 @@ class SASL(PlugIn):
# Password is now required # Password is now required
self._owner._caller.get_password(self.set_password, self.mechanism) self._owner._caller.get_password(self.set_password, self.mechanism)
elif 'rspauth' in chal: elif 'rspauth' in chal:
# Check rspauth value
if chal['rspauth'] != self.digest_rspauth:
on_auth_fail('rspauth is wrong'):
self._owner.send(str(Node('response', attrs={'xmlns':NS_SASL}))) self._owner.send(str(Node('response', attrs={'xmlns':NS_SASL})))
else: else:
self.startsasl = SASL_FAILURE self.startsasl = SASL_FAILURE
@ -449,10 +460,14 @@ class SASL(PlugIn):
hash_realm = self._convert_to_iso88591(self.resp['realm']) hash_realm = self._convert_to_iso88591(self.resp['realm'])
hash_password = self._convert_to_iso88591(self.password) hash_password = self._convert_to_iso88591(self.password)
A1 = C([H(C([hash_username, hash_realm, hash_password])), A1 = C([H(C([hash_username, hash_realm, hash_password])),
self.resp['nonce'], self.resp['cnonce']]) self.resp['nonce'], self.resp['cnonce']])
A2 = C(['AUTHENTICATE', self.resp['digest-uri']]) A2 = C(['AUTHENTICATE', self.resp['digest-uri']])
response= HH(C([HH(A1), self.resp['nonce'], self.resp['nc'], response = HH(C([HH(A1), self.resp['nonce'], self.resp['nc'],
self.resp['cnonce'], self.resp['qop'], HH(A2)])) self.resp['cnonce'], self.resp['qop'], HH(A2)]))
A2 = C(['', self.resp['digest-uri']])
self.digest_rspauth = HH(C([HH(A1), self.resp['nonce'],
self.resp['nc'], self.resp['cnonce'], self.resp['qop'],
HH(A2)]))
self.resp['response'] = response self.resp['response'] = response
sasl_data = u'' sasl_data = u''
for key in ('charset', 'username', 'realm', 'nonce', 'nc', 'cnonce', for key in ('charset', 'username', 'realm', 'nonce', 'nc', 'cnonce',