Package pyxmpp :: Package sasl :: Module digest_md5
[hide private]

Source Code for Module pyxmpp.sasl.digest_md5

  1  # 
  2  # (C) Copyright 2003-2010 Jacek Konieczny <jajcus@jajcus.net> 
  3  # 
  4  # This program is free software; you can redistribute it and/or modify 
  5  # it under the terms of the GNU Lesser General Public License Version 
  6  # 2.1 as published by the Free Software Foundation. 
  7  # 
  8  # This program is distributed in the hope that it will be useful, 
  9  # but WITHOUT ANY WARRANTY; without even the implied warranty of 
 10  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
 11  # GNU Lesser General Public License for more details. 
 12  # 
 13  # You should have received a copy of the GNU Lesser General Public 
 14  # License along with this program; if not, write to the Free Software 
 15  # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 
 16  # 
 17  """DIGEST-MD5 authentication mechanism for PyXMPP SASL implementation. 
 18   
 19  Normative reference: 
 20    - `RFC 2831 <http://www.ietf.org/rfc/rfc2831.txt>`__ 
 21  """ 
 22   
 23  __docformat__="restructuredtext en" 
 24   
 25  from binascii import b2a_hex 
 26  import re 
 27  import logging 
 28   
 29  import hashlib 
 30   
 31  from pyxmpp.sasl.core import ClientAuthenticator,ServerAuthenticator 
 32  from pyxmpp.sasl.core import Failure,Response,Challenge,Success,Failure 
 33   
 34  from pyxmpp.utils import to_utf8,from_utf8 
 35   
 36  quote_re=re.compile(r"(?<!\\)\\(.)") 
 37   
38 -def _unquote(s):
39 """Unquote quoted value from DIGEST-MD5 challenge or response. 40 41 If `s` doesn't start or doesn't end with '"' then return it unchanged, 42 remove the quotes and escape backslashes otherwise. 43 44 :Parameters: 45 - `s`: a quoted string. 46 :Types: 47 - `s`: `str` 48 49 :return: the unquoted string. 50 :returntype: `str`""" 51 if not s.startswith('"') or not s.endswith('"'): 52 return s 53 return quote_re.sub(r"\1",s[1:-1])
54
55 -def _quote(s):
56 """Prepare a string for quoting for DIGEST-MD5 challenge or response. 57 58 Don't add the quotes, only escape '"' and "\\" with backslashes. 59 60 :Parameters: 61 - `s`: a raw string. 62 :Types: 63 - `s`: `str` 64 65 :return: `s` with '"' and "\\" escaped using "\\". 66 :returntype: `str`""" 67 s=s.replace('\\','\\\\') 68 s=s.replace('"','\\"') 69 return '%s' % (s,)
70
71 -def _h_value(s):
72 """H function of the DIGEST-MD5 algorithm (MD5 sum). 73 74 :Parameters: 75 - `s`: a string. 76 :Types: 77 - `s`: `str` 78 79 :return: MD5 sum of the string. 80 :returntype: `str`""" 81 return hashlib.md5(s).digest()
82
83 -def _kd_value(k,s):
84 """KD function of the DIGEST-MD5 algorithm. 85 86 :Parameters: 87 - `k`: a string. 88 - `s`: a string. 89 :Types: 90 - `k`: `str` 91 - `s`: `str` 92 93 :return: MD5 sum of the strings joined with ':'. 94 :returntype: `str`""" 95 return _h_value("%s:%s" % (k,s))
96
97 -def _make_urp_hash(username,realm,passwd):
98 """Compute MD5 sum of username:realm:password. 99 100 :Parameters: 101 - `username`: a username. 102 - `realm`: a realm. 103 - `passwd`: a password. 104 :Types: 105 - `username`: `str` 106 - `realm`: `str` 107 - `passwd`: `str` 108 109 :return: the MD5 sum of the parameters joined with ':'. 110 :returntype: `str`""" 111 if realm is None: 112 realm="" 113 if type(passwd) is unicode: 114 passwd=passwd.encode("utf-8") 115 return _h_value("%s:%s:%s" % (username,realm,passwd))
116
117 -def _compute_response(urp_hash,nonce,cnonce,nonce_count,authzid,digest_uri):
118 """Compute DIGEST-MD5 response value. 119 120 :Parameters: 121 - `urp_hash`: MD5 sum of username:realm:password. 122 - `nonce`: nonce value from a server challenge. 123 - `cnonce`: cnonce value from the client response. 124 - `nonce_count`: nonce count value. 125 - `authzid`: authorization id. 126 - `digest_uri`: digest-uri value. 127 :Types: 128 - `urp_hash`: `str` 129 - `nonce`: `str` 130 - `nonce_count`: `int` 131 - `authzid`: `str` 132 - `digest_uri`: `str` 133 134 :return: the computed response value. 135 :returntype: `str`""" 136 if authzid: 137 a1="%s:%s:%s:%s" % (urp_hash,nonce,cnonce,authzid) 138 else: 139 a1="%s:%s:%s" % (urp_hash,nonce,cnonce) 140 a2="AUTHENTICATE:"+digest_uri 141 return b2a_hex(_kd_value( b2a_hex(_h_value(a1)),"%s:%s:%s:%s:%s" % ( 142 nonce,nonce_count, 143 cnonce,"auth",b2a_hex(_h_value(a2)) ) ))
144
145 -def _compute_response_auth(urp_hash,nonce,cnonce,nonce_count,authzid,digest_uri):
146 """Compute DIGEST-MD5 rspauth value. 147 148 :Parameters: 149 - `urp_hash`: MD5 sum of username:realm:password. 150 - `nonce`: nonce value from a server challenge. 151 - `cnonce`: cnonce value from the client response. 152 - `nonce_count`: nonce count value. 153 - `authzid`: authorization id. 154 - `digest_uri`: digest-uri value. 155 :Types: 156 - `urp_hash`: `str` 157 - `nonce`: `str` 158 - `nonce_count`: `int` 159 - `authzid`: `str` 160 - `digest_uri`: `str` 161 162 :return: the computed rspauth value. 163 :returntype: `str`""" 164 if authzid: 165 a1="%s:%s:%s:%s" % (urp_hash,nonce,cnonce,authzid) 166 else: 167 a1="%s:%s:%s" % (urp_hash,nonce,cnonce) 168 a2=":"+digest_uri 169 return b2a_hex(_kd_value( b2a_hex(_h_value(a1)),"%s:%s:%s:%s:%s" % ( 170 nonce,nonce_count, 171 cnonce,"auth",b2a_hex(_h_value(a2)) ) ))
172 173 _param_re=re.compile(r'^(?P<var>[^=]+)\=(?P<val>(\"(([^"\\]+)|(\\\")' 174 r'|(\\\\))+\")|([^",]+))(\s*\,\s*(?P<rest>.*))?$') 175
176 -class DigestMD5ClientAuthenticator(ClientAuthenticator):
177 """Provides PLAIN SASL authentication for a client. 178 179 :Ivariables: 180 - `password`: current authentication password 181 - `pformat`: current authentication password format 182 - `realm`: current authentication realm 183 """ 184
185 - def __init__(self,password_manager):
186 """Initialize a `DigestMD5ClientAuthenticator` object. 187 188 :Parameters: 189 - `password_manager`: name of the password manager object providing 190 authentication credentials. 191 :Types: 192 - `password_manager`: `PasswordManager`""" 193 ClientAuthenticator.__init__(self,password_manager) 194 self.username=None 195 self.rspauth_checked=None 196 self.response_auth=None 197 self.authzid=None 198 self.pformat=None 199 self.realm=None 200 self.password=None 201 self.nonce_count=None 202 self.__logger=logging.getLogger("pyxmpp.sasl.DigestMD5ClientAuthenticator")
203
204 - def start(self,username,authzid):
205 """Start the authentication process initializing client state. 206 207 :Parameters: 208 - `username`: username (authentication id). 209 - `authzid`: authorization id. 210 :Types: 211 - `username`: `unicode` 212 - `authzid`: `unicode` 213 214 :return: the (empty) initial response 215 :returntype: `sasl.Response` or `sasl.Failure`""" 216 self.username=from_utf8(username) 217 if authzid: 218 self.authzid=from_utf8(authzid) 219 else: 220 self.authzid="" 221 self.password=None 222 self.pformat=None 223 self.nonce_count=0 224 self.response_auth=None 225 self.rspauth_checked=0 226 self.realm=None 227 return Response()
228
229 - def challenge(self,challenge):
230 """Process a challenge and return the response. 231 232 :Parameters: 233 - `challenge`: the challenge from server. 234 :Types: 235 - `challenge`: `str` 236 237 :return: the response or a failure indicator. 238 :returntype: `sasl.Response` or `sasl.Failure`""" 239 if not challenge: 240 self.__logger.debug("Empty challenge") 241 return Failure("bad-challenge") 242 challenge=challenge.split('\x00')[0] # workaround for some buggy implementations 243 if self.response_auth: 244 return self._final_challenge(challenge) 245 realms=[] 246 nonce=None 247 charset="iso-8859-1" 248 while challenge: 249 m=_param_re.match(challenge) 250 if not m: 251 self.__logger.debug("Challenge syntax error: %r" % (challenge,)) 252 return Failure("bad-challenge") 253 challenge=m.group("rest") 254 var=m.group("var") 255 val=m.group("val") 256 self.__logger.debug("%r: %r" % (var,val)) 257 if var=="realm": 258 realms.append(_unquote(val)) 259 elif var=="nonce": 260 if nonce: 261 self.__logger.debug("Duplicate nonce") 262 return Failure("bad-challenge") 263 nonce=_unquote(val) 264 elif var=="qop": 265 qopl=_unquote(val).split(",") 266 if "auth" not in qopl: 267 self.__logger.debug("auth not supported") 268 return Failure("not-implemented") 269 elif var=="charset": 270 if val!="utf-8": 271 self.__logger.debug("charset given and not utf-8") 272 return Failure("bad-challenge") 273 charset="utf-8" 274 elif var=="algorithm": 275 if val!="md5-sess": 276 self.__logger.debug("algorithm given and not md5-sess") 277 return Failure("bad-challenge") 278 if not nonce: 279 self.__logger.debug("nonce not given") 280 return Failure("bad-challenge") 281 self._get_password() 282 return self._make_response(charset,realms,nonce)
283
284 - def _get_password(self):
285 """Retrieve user's password from the password manager. 286 287 Set `self.password` to the password and `self.pformat` 288 to its format name ('plain' or 'md5:user:realm:pass').""" 289 if self.password is None: 290 self.password,self.pformat=self.password_manager.get_password( 291 self.username,["plain","md5:user:realm:pass"]) 292 if not self.password or self.pformat not in ("plain","md5:user:realm:pass"): 293 self.__logger.debug("Couldn't get plain password. Password: %r Format: %r" 294 % (self.password,self.pformat)) 295 return Failure("password-unavailable")
296
297 - def _make_response(self,charset,realms,nonce):
298 """Make a response for the first challenge from the server. 299 300 :Parameters: 301 - `charset`: charset name from the challenge. 302 - `realms`: realms list from the challenge. 303 - `nonce`: nonce value from the challenge. 304 :Types: 305 - `charset`: `str` 306 - `realms`: `str` 307 - `nonce`: `str` 308 309 :return: the response or a failure indicator. 310 :returntype: `sasl.Response` or `sasl.Failure`""" 311 params=[] 312 realm=self._get_realm(realms,charset) 313 if isinstance(realm,Failure): 314 return realm 315 elif realm: 316 realm=_quote(realm) 317 params.append('realm="%s"' % (realm,)) 318 319 try: 320 username=self.username.encode(charset) 321 except UnicodeError: 322 self.__logger.debug("Couldn't encode username to %r" % (charset,)) 323 return Failure("incompatible-charset") 324 325 username=_quote(username) 326 params.append('username="%s"' % (username,)) 327 328 cnonce=self.password_manager.generate_nonce() 329 cnonce=_quote(cnonce) 330 params.append('cnonce="%s"' % (cnonce,)) 331 332 params.append('nonce="%s"' % (_quote(nonce),)) 333 334 self.nonce_count+=1 335 nonce_count="%08x" % (self.nonce_count,) 336 params.append('nc=%s' % (nonce_count,)) 337 338 params.append('qop=auth') 339 340 serv_type=self.password_manager.get_serv_type().encode("us-ascii") 341 host=self.password_manager.get_serv_host().encode("idna") 342 serv_name=self.password_manager.get_serv_name().encode("utf-8") 343 344 if serv_name and serv_name != host: 345 digest_uri="%s/%s/%s" % (serv_type,host,serv_name) 346 else: 347 digest_uri="%s/%s" % (serv_type,host) 348 349 digest_uri=_quote(digest_uri) 350 params.append('digest-uri="%s"' % (digest_uri,)) 351 352 if self.authzid: 353 try: 354 authzid=self.authzid.encode(charset) 355 except UnicodeError: 356 self.__logger.debug("Couldn't encode authzid to %r" % (charset,)) 357 return Failure("incompatible-charset") 358 authzid=_quote(authzid) 359 else: 360 authzid="" 361 362 if self.pformat=="md5:user:realm:pass": 363 urp_hash=self.password 364 else: 365 urp_hash=_make_urp_hash(username,realm,self.password) 366 367 response=_compute_response(urp_hash,nonce,cnonce,nonce_count, 368 authzid,digest_uri) 369 self.response_auth=_compute_response_auth(urp_hash,nonce,cnonce, 370 nonce_count,authzid,digest_uri) 371 params.append('response=%s' % (response,)) 372 if authzid: 373 params.append('authzid="%s"' % (authzid,)) 374 return Response(",".join(params))
375
376 - def _get_realm(self,realms,charset):
377 """Choose a realm from the list specified by the server. 378 379 :Parameters: 380 - `realms`: the realm list. 381 - `charset`: encoding of realms on the list. 382 :Types: 383 - `realms`: `list` of `str` 384 - `charset`: `str` 385 386 :return: the realm chosen or a failure indicator. 387 :returntype: `str` or `Failure`""" 388 if realms: 389 realms=[unicode(r,charset) for r in realms] 390 realm=self.password_manager.choose_realm(realms) 391 else: 392 realm=self.password_manager.choose_realm([]) 393 if realm: 394 if type(realm) is unicode: 395 try: 396 realm=realm.encode(charset) 397 except UnicodeError: 398 self.__logger.debug("Couldn't encode realm to %r" % (charset,)) 399 return Failure("incompatible-charset") 400 elif charset!="utf-8": 401 try: 402 realm=unicode(realm,"utf-8").encode(charset) 403 except UnicodeError: 404 self.__logger.debug("Couldn't encode realm from utf-8 to %r" 405 % (charset,)) 406 return Failure("incompatible-charset") 407 self.realm=realm 408 return realm
409
410 - def _final_challenge(self,challenge):
411 """Process the second challenge from the server and return the response. 412 413 :Parameters: 414 - `challenge`: the challenge from server. 415 :Types: 416 - `challenge`: `str` 417 418 :return: the response or a failure indicator. 419 :returntype: `sasl.Response` or `sasl.Failure`""" 420 if self.rspauth_checked: 421 return Failure("extra-challenge") 422 challenge=challenge.split('\x00')[0] 423 rspauth=None 424 while challenge: 425 m=_param_re.match(challenge) 426 if not m: 427 self.__logger.debug("Challenge syntax error: %r" % (challenge,)) 428 return Failure("bad-challenge") 429 challenge=m.group("rest") 430 var=m.group("var") 431 val=m.group("val") 432 self.__logger.debug("%r: %r" % (var,val)) 433 if var=="rspauth": 434 rspauth=val 435 if not rspauth: 436 self.__logger.debug("Final challenge without rspauth") 437 return Failure("bad-success") 438 if rspauth==self.response_auth: 439 self.rspauth_checked=1 440 return Response("") 441 else: 442 self.__logger.debug("Wrong rspauth value - peer is cheating?") 443 self.__logger.debug("my rspauth: %r" % (self.response_auth,)) 444 return Failure("bad-success")
445
446 - def finish(self,data):
447 """Process success indicator from the server. 448 449 Process any addiitional data passed with the success. 450 Fail if the server was not authenticated. 451 452 :Parameters: 453 - `data`: an optional additional data with success. 454 :Types: 455 - `data`: `str` 456 457 :return: success or failure indicator. 458 :returntype: `sasl.Success` or `sasl.Failure`""" 459 if not self.response_auth: 460 self.__logger.debug("Got success too early") 461 return Failure("bad-success") 462 if self.rspauth_checked: 463 return Success(self.username,self.realm,self.authzid) 464 else: 465 r = self._final_challenge(data) 466 if isinstance(r, Failure): 467 return r 468 if self.rspauth_checked: 469 return Success(self.username,self.realm,self.authzid) 470 else: 471 self.__logger.debug("Something went wrong when processing additional data with success?") 472 return Failure("bad-success")
473
474 -class DigestMD5ServerAuthenticator(ServerAuthenticator):
475 """Provides DIGEST-MD5 SASL authentication for a server.""" 476
477 - def __init__(self,password_manager):
478 """Initialize a `DigestMD5ServerAuthenticator` object. 479 480 :Parameters: 481 - `password_manager`: name of the password manager object providing 482 authentication credential verification. 483 :Types: 484 - `password_manager`: `PasswordManager`""" 485 ServerAuthenticator.__init__(self,password_manager) 486 self.nonce=None 487 self.username=None 488 self.realm=None 489 self.authzid=None 490 self.done=None 491 self.last_nonce_count=None 492 self.__logger=logging.getLogger("pyxmpp.sasl.DigestMD5ServerAuthenticator")
493
494 - def start(self,response):
495 """Start the authentication process. 496 497 :Parameters: 498 - `response`: the initial response from the client (empty for 499 DIGEST-MD5). 500 :Types: 501 - `response`: `str` 502 503 :return: a challenge, a success indicator or a failure indicator. 504 :returntype: `sasl.Challenge`, `sasl.Success` or `sasl.Failure`""" 505 _unused = response 506 self.last_nonce_count=0 507 params=[] 508 realms=self.password_manager.get_realms() 509 if realms: 510 self.realm=_quote(realms[0]) 511 for r in realms: 512 r=_quote(r) 513 params.append('realm="%s"' % (r,)) 514 else: 515 self.realm=None 516 nonce=_quote(self.password_manager.generate_nonce()) 517 self.nonce=nonce 518 params.append('nonce="%s"' % (nonce,)) 519 params.append('qop="auth"') 520 params.append('charset=utf-8') 521 params.append('algorithm=md5-sess') 522 self.authzid=None 523 self.done=0 524 return Challenge(",".join(params))
525
526 - def response(self,response):
527 """Process a client reponse. 528 529 :Parameters: 530 - `response`: the response from the client. 531 :Types: 532 - `response`: `str` 533 534 :return: a challenge, a success indicator or a failure indicator. 535 :returntype: `sasl.Challenge`, `sasl.Success` or `sasl.Failure`""" 536 if self.done: 537 return Success(self.username,self.realm,self.authzid) 538 if not response: 539 return Failure("not-authorized") 540 return self._parse_response(response)
541
542 - def _parse_response(self,response):
543 """Parse a client reponse and pass to further processing. 544 545 :Parameters: 546 - `response`: the response from the client. 547 :Types: 548 - `response`: `str` 549 550 :return: a challenge, a success indicator or a failure indicator. 551 :returntype: `sasl.Challenge`, `sasl.Success` or `sasl.Failure`""" 552 response=response.split('\x00')[0] # workaround for some SASL implementations 553 if self.realm: 554 realm=to_utf8(self.realm) 555 realm=_quote(realm) 556 else: 557 realm=None 558 username=None 559 cnonce=None 560 digest_uri=None 561 response_val=None 562 authzid=None 563 nonce_count=None 564 while response: 565 m=_param_re.match(response) 566 if not m: 567 self.__logger.debug("Response syntax error: %r" % (response,)) 568 return Failure("not-authorized") 569 response=m.group("rest") 570 var=m.group("var") 571 val=m.group("val") 572 self.__logger.debug("%r: %r" % (var,val)) 573 if var=="realm": 574 realm=val[1:-1] 575 elif var=="cnonce": 576 if cnonce: 577 self.__logger.debug("Duplicate cnonce") 578 return Failure("not-authorized") 579 cnonce=val[1:-1] 580 elif var=="qop": 581 if val!='auth': 582 self.__logger.debug("qop other then 'auth'") 583 return Failure("not-authorized") 584 elif var=="digest-uri": 585 digest_uri=val[1:-1] 586 elif var=="authzid": 587 authzid=val[1:-1] 588 elif var=="username": 589 username=val[1:-1] 590 elif var=="response": 591 response_val=val 592 elif var=="nc": 593 nonce_count=val 594 self.last_nonce_count+=1 595 if int(nonce_count)!=self.last_nonce_count: 596 self.__logger.debug("bad nonce: %r != %r" 597 % (nonce_count,self.last_nonce_count)) 598 return Failure("not-authorized") 599 return self._check_params(username,realm,cnonce,digest_uri, 600 response_val,authzid,nonce_count)
601
602 - def _check_params(self,username,realm,cnonce,digest_uri, 603 response_val,authzid,nonce_count):
604 """Check parameters of a client reponse and pass them to further 605 processing. 606 607 :Parameters: 608 - `username`: user name. 609 - `realm`: realm. 610 - `cnonce`: cnonce value. 611 - `digest_uri`: digest-uri value. 612 - `response_val`: response value computed by the client. 613 - `authzid`: authorization id. 614 - `nonce_count`: nonce count value. 615 :Types: 616 - `username`: `str` 617 - `realm`: `str` 618 - `cnonce`: `str` 619 - `digest_uri`: `str` 620 - `response_val`: `str` 621 - `authzid`: `str` 622 - `nonce_count`: `int` 623 624 :return: a challenge, a success indicator or a failure indicator. 625 :returntype: `sasl.Challenge`, `sasl.Success` or `sasl.Failure`""" 626 if not cnonce: 627 self.__logger.debug("Required 'cnonce' parameter not given") 628 return Failure("not-authorized") 629 if not response_val: 630 self.__logger.debug("Required 'response' parameter not given") 631 return Failure("not-authorized") 632 if not username: 633 self.__logger.debug("Required 'username' parameter not given") 634 return Failure("not-authorized") 635 if not digest_uri: 636 self.__logger.debug("Required 'digest_uri' parameter not given") 637 return Failure("not-authorized") 638 if not nonce_count: 639 self.__logger.debug("Required 'nc' parameter not given") 640 return Failure("not-authorized") 641 return self._make_final_challenge(username,realm,cnonce,digest_uri, 642 response_val,authzid,nonce_count)
643
644 - def _make_final_challenge(self,username,realm,cnonce,digest_uri, 645 response_val,authzid,nonce_count):
646 """Send the second challenge in reply to the client response. 647 648 :Parameters: 649 - `username`: user name. 650 - `realm`: realm. 651 - `cnonce`: cnonce value. 652 - `digest_uri`: digest-uri value. 653 - `response_val`: response value computed by the client. 654 - `authzid`: authorization id. 655 - `nonce_count`: nonce count value. 656 :Types: 657 - `username`: `str` 658 - `realm`: `str` 659 - `cnonce`: `str` 660 - `digest_uri`: `str` 661 - `response_val`: `str` 662 - `authzid`: `str` 663 - `nonce_count`: `int` 664 665 :return: a challenge, a success indicator or a failure indicator. 666 :returntype: `sasl.Challenge`, `sasl.Success` or `sasl.Failure`""" 667 username_uq=from_utf8(username.replace('\\','')) 668 if authzid: 669 authzid_uq=from_utf8(authzid.replace('\\','')) 670 else: 671 authzid_uq=None 672 if realm: 673 realm_uq=from_utf8(realm.replace('\\','')) 674 else: 675 realm_uq=None 676 digest_uri_uq=digest_uri.replace('\\','') 677 self.username=username_uq 678 self.realm=realm_uq 679 password,pformat=self.password_manager.get_password( 680 username_uq,realm_uq,("plain","md5:user:realm:pass")) 681 if pformat=="md5:user:realm:pass": 682 urp_hash=password 683 elif pformat=="plain": 684 urp_hash=_make_urp_hash(username,realm,password) 685 else: 686 self.__logger.debug("Couldn't get password.") 687 return Failure("not-authorized") 688 valid_response=_compute_response(urp_hash,self.nonce,cnonce, 689 nonce_count,authzid,digest_uri) 690 if response_val!=valid_response: 691 self.__logger.debug("Response mismatch: %r != %r" % (response_val,valid_response)) 692 return Failure("not-authorized") 693 s=digest_uri_uq.split("/") 694 if len(s)==3: 695 serv_type,host,serv_name=s 696 elif len(s)==2: 697 serv_type,host=s 698 serv_name=None 699 else: 700 self.__logger.debug("Bad digest_uri: %r" % (digest_uri_uq,)) 701 return Failure("not-authorized") 702 info={} 703 info["mechanism"]="DIGEST-MD5" 704 info["username"]=username_uq 705 info["serv-type"]=serv_type 706 info["host"]=host 707 info["serv-name"]=serv_name 708 if self.password_manager.check_authzid(authzid_uq,info): 709 rspauth=_compute_response_auth(urp_hash,self.nonce, 710 cnonce,nonce_count,authzid,digest_uri) 711 self.authzid=authzid 712 self.done=1 713 return Challenge("rspauth="+rspauth) 714 else: 715 self.__logger.debug("Authzid check failed") 716 return Failure("invalid_authzid")
717 718 # vi: sts=4 et sw=4 719