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

Source Code for Module pyxmpp.sasl.digest_md5

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