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