home *** CD-ROM | disk | FTP | other *** search
/ Maximum CD 2009 June / maximum-cd-2009-06.iso / DiscContents / digsby_setup.exe / lib / pyxmpp / sasl / digest_md5.pyo (.txt) < prev    next >
Encoding:
Python Compiled Bytecode  |  2009-02-26  |  13.9 KB  |  508 lines

  1. # Source Generated with Decompyle++
  2. # File: in.pyo (Python 2.5)
  3.  
  4. __revision__ = '$Id: digest_md5.py 683 2008-12-05 18:25:45Z jajcus $'
  5. __docformat__ = 'restructuredtext en'
  6. from binascii import b2a_hex
  7. import re
  8. import logging
  9.  
  10. try:
  11.     import hashlib
  12.     md5_factory = hashlib.md5
  13. except:
  14.     import md5
  15.     md5_factory = md5.new
  16.  
  17. from pyxmpp.sasl.core import ClientAuthenticator, ServerAuthenticator
  18. from pyxmpp.sasl.core import Failure, Response, Challenge, Success, Failure
  19. from pyxmpp.utils import to_utf8, from_utf8
  20. quote_re = re.compile('(?<!\\\\)\\\\(.)')
  21.  
  22. def _unquote(s):
  23.     if not s.startswith('"') or not s.endswith('"'):
  24.         return s
  25.     
  26.     return quote_re.sub('\\1', s[1:-1])
  27.  
  28.  
  29. def _quote(s):
  30.     s = s.replace('\\', '\\\\')
  31.     s = s.replace('"', '\\"')
  32.     return '%s' % (s,)
  33.  
  34.  
  35. def _h_value(s):
  36.     return md5_factory(s).digest()
  37.  
  38.  
  39. def _kd_value(k, s):
  40.     return _h_value('%s:%s' % (k, s))
  41.  
  42.  
  43. def _make_urp_hash(username, realm, password):
  44.     if realm is None:
  45.         realm = ''
  46.     
  47.     if type(password) is unicode:
  48.         password = password.encode('utf-8')
  49.     
  50.     return _h_value('%s:%s:%s' % (username, realm, password))
  51.  
  52.  
  53. def _compute_response(urp_hash, nonce, cnonce, nonce_count, authzid, digest_uri):
  54.     if authzid:
  55.         a1 = '%s:%s:%s:%s' % (urp_hash, nonce, cnonce, authzid)
  56.     else:
  57.         a1 = '%s:%s:%s' % (urp_hash, nonce, cnonce)
  58.     a2 = 'AUTHENTICATE:' + digest_uri
  59.     return b2a_hex(_kd_value(b2a_hex(_h_value(a1)), '%s:%s:%s:%s:%s' % (nonce, nonce_count, cnonce, 'auth', b2a_hex(_h_value(a2)))))
  60.  
  61.  
  62. def _compute_response_auth(urp_hash, nonce, cnonce, nonce_count, authzid, digest_uri):
  63.     if authzid:
  64.         a1 = '%s:%s:%s:%s' % (urp_hash, nonce, cnonce, authzid)
  65.     else:
  66.         a1 = '%s:%s:%s' % (urp_hash, nonce, cnonce)
  67.     a2 = ':' + digest_uri
  68.     return b2a_hex(_kd_value(b2a_hex(_h_value(a1)), '%s:%s:%s:%s:%s' % (nonce, nonce_count, cnonce, 'auth', b2a_hex(_h_value(a2)))))
  69.  
  70. _param_re = re.compile('^(?P<var>[^=]+)\\=(?P<val>(\\"(([^"\\\\]+)|(\\\\\\")|(\\\\\\\\))+\\")|([^",]+))(\\s*\\,\\s*(?P<rest>.*))?$')
  71.  
  72. class DigestMD5ClientAuthenticator(ClientAuthenticator):
  73.     
  74.     def __init__(self, password_manager):
  75.         ClientAuthenticator.__init__(self, password_manager)
  76.         self.username = None
  77.         self.rspauth_checked = None
  78.         self.response_auth = None
  79.         self.authzid = None
  80.         self.pformat = None
  81.         self.realm = None
  82.         self.password = None
  83.         self.nonce_count = None
  84.         self._DigestMD5ClientAuthenticator__logger = logging.getLogger('pyxmpp.sasl.DigestMD5ClientAuthenticator')
  85.  
  86.     
  87.     def start(self, username, authzid):
  88.         self.username = from_utf8(username)
  89.         if authzid:
  90.             self.authzid = from_utf8(authzid)
  91.         else:
  92.             self.authzid = ''
  93.         self.password = None
  94.         self.pformat = None
  95.         self.nonce_count = 0
  96.         self.response_auth = None
  97.         self.rspauth_checked = 0
  98.         self.realm = None
  99.         return Response()
  100.  
  101.     
  102.     def challenge(self, challenge):
  103.         if not challenge:
  104.             self._DigestMD5ClientAuthenticator__logger.debug('Empty challenge')
  105.             return Failure('bad-challenge')
  106.         
  107.         challenge = challenge.split('\x00')[0]
  108.         if self.response_auth:
  109.             return self._final_challenge(challenge)
  110.         
  111.         realms = []
  112.         nonce = None
  113.         charset = 'iso-8859-1'
  114.         while challenge:
  115.             m = _param_re.match(challenge)
  116.             if not m:
  117.                 self._DigestMD5ClientAuthenticator__logger.debug('Challenge syntax error: %r' % (challenge,))
  118.                 return Failure('bad-challenge')
  119.             
  120.             challenge = m.group('rest')
  121.             var = m.group('var')
  122.             val = m.group('val')
  123.             self._DigestMD5ClientAuthenticator__logger.debug('%r: %r' % (var, val))
  124.             if var == 'realm':
  125.                 realms.append(_unquote(val))
  126.                 continue
  127.             if var == 'nonce':
  128.                 if nonce:
  129.                     self._DigestMD5ClientAuthenticator__logger.debug('Duplicate nonce')
  130.                     return Failure('bad-challenge')
  131.                 
  132.                 nonce = _unquote(val)
  133.                 continue
  134.             if var == 'qop':
  135.                 qopl = _unquote(val).split(',')
  136.                 if 'auth' not in qopl:
  137.                     self._DigestMD5ClientAuthenticator__logger.debug('auth not supported')
  138.                     return Failure('not-implemented')
  139.                 
  140.             'auth' not in qopl
  141.             if var == 'charset':
  142.                 val = _unquote(val)
  143.                 if val != 'utf-8':
  144.                     self._DigestMD5ClientAuthenticator__logger.debug('charset given and not utf-8')
  145.                     return Failure('bad-challenge')
  146.                 
  147.                 charset = 'utf-8'
  148.                 continue
  149.             if var == 'algorithm':
  150.                 val = _unquote(val)
  151.                 if val != 'md5-sess':
  152.                     self._DigestMD5ClientAuthenticator__logger.debug('algorithm given and not md5-sess')
  153.                     return Failure('bad-challenge')
  154.                 
  155.             val != 'md5-sess'
  156.         if not nonce:
  157.             self._DigestMD5ClientAuthenticator__logger.debug('nonce not given')
  158.             return Failure('bad-challenge')
  159.         
  160.         self._get_password()
  161.         return self._make_response(charset, realms, nonce)
  162.  
  163.     
  164.     def _get_password(self):
  165.         if self.password is None:
  166.             (self.password, self.pformat) = self.password_manager.get_password(self.username, [
  167.                 'plain',
  168.                 'md5:user:realm:pass'])
  169.         
  170.         if not (self.password) or self.pformat not in ('plain', 'md5:user:realm:pass'):
  171.             self._DigestMD5ClientAuthenticator__logger.debug("Couldn't get plain password. Password: %r Format: %r" % (self.password, self.pformat))
  172.             return Failure('password-unavailable')
  173.         
  174.  
  175.     
  176.     def _make_response(self, charset, realms, nonce):
  177.         params = []
  178.         realm = self._get_realm(realms, charset)
  179.         if isinstance(realm, Failure):
  180.             return realm
  181.         elif realm:
  182.             realm = _quote(realm)
  183.             params.append('realm="%s"' % (realm,))
  184.         
  185.         
  186.         try:
  187.             username = self.username.encode(charset)
  188.         except UnicodeError:
  189.             self._DigestMD5ClientAuthenticator__logger.debug("Couldn't encode username to %r" % (charset,))
  190.             return Failure('incompatible-charset')
  191.  
  192.         username = _quote(username)
  193.         params.append('username="%s"' % (username,))
  194.         cnonce = self.password_manager.generate_nonce()
  195.         cnonce = _quote(cnonce)
  196.         params.append('cnonce="%s"' % (cnonce,))
  197.         params.append('nonce="%s"' % (_quote(nonce),))
  198.         self.nonce_count += 1
  199.         nonce_count = '%08x' % (self.nonce_count,)
  200.         params.append('nc=%s' % (nonce_count,))
  201.         params.append('qop=auth')
  202.         serv_type = self.password_manager.get_serv_type().encode('us-ascii')
  203.         host = self.password_manager.get_serv_host().encode('us-ascii')
  204.         serv_name = self.password_manager.get_serv_name().encode('us-ascii')
  205.         if serv_name and serv_name != host:
  206.             digest_uri = '%s/%s/%s' % (serv_type, host, serv_name)
  207.         else:
  208.             digest_uri = '%s/%s' % (serv_type, host)
  209.         digest_uri = _quote(digest_uri)
  210.         params.append('digest-uri="%s"' % (digest_uri,))
  211.         if self.authzid:
  212.             
  213.             try:
  214.                 authzid = self.authzid.encode(charset)
  215.             except UnicodeError:
  216.                 self._DigestMD5ClientAuthenticator__logger.debug("Couldn't encode authzid to %r" % (charset,))
  217.                 return Failure('incompatible-charset')
  218.  
  219.             authzid = _quote(authzid)
  220.         else:
  221.             authzid = ''
  222.         if self.pformat == 'md5:user:realm:pass':
  223.             urp_hash = self.password
  224.         else:
  225.             urp_hash = _make_urp_hash(username, realm, self.password)
  226.         response = _compute_response(urp_hash, nonce, cnonce, nonce_count, authzid, digest_uri)
  227.         self.response_auth = _compute_response_auth(urp_hash, nonce, cnonce, nonce_count, authzid, digest_uri)
  228.         params.append('response=%s' % (response,))
  229.         if authzid:
  230.             params.append('authzid="%s"' % (authzid,))
  231.         
  232.         return Response(','.join(params))
  233.  
  234.     
  235.     def _get_realm(self, realms, charset):
  236.         if realm:
  237.             if type(realm) is unicode:
  238.                 
  239.                 try:
  240.                     realm = realm.encode(charset)
  241.                 except UnicodeError:
  242.                     None if realms else []
  243.                     None if realms else []
  244.                     self._DigestMD5ClientAuthenticator__logger.debug("Couldn't encode realm to %r" % (charset,))
  245.                     return Failure('incompatible-charset')
  246.                 except:
  247.                     None if realms else []<EXCEPTION MATCH>UnicodeError
  248.                 
  249.  
  250.             None if realms else []
  251.             if charset != 'utf-8':
  252.                 
  253.                 try:
  254.                     realm = unicode(realm, 'utf-8').encode(charset)
  255.                 except UnicodeError:
  256.                     None if realms else []
  257.                     None if realms else []
  258.                     self._DigestMD5ClientAuthenticator__logger.debug("Couldn't encode realm from utf-8 to %r" % (charset,))
  259.                     return Failure('incompatible-charset')
  260.                 except:
  261.                     None if realms else []<EXCEPTION MATCH>UnicodeError
  262.                 
  263.  
  264.             None if realms else []
  265.             self.realm = realm
  266.         
  267.         return realm
  268.  
  269.     
  270.     def _final_challenge(self, challenge):
  271.         if self.rspauth_checked:
  272.             return Failure('extra-challenge')
  273.         
  274.         challenge = challenge.split('\x00')[0]
  275.         rspauth = None
  276.         while challenge:
  277.             m = _param_re.match(challenge)
  278.             if not m:
  279.                 self._DigestMD5ClientAuthenticator__logger.debug('Challenge syntax error: %r' % (challenge,))
  280.                 return Failure('bad-challenge')
  281.             
  282.             challenge = m.group('rest')
  283.             var = m.group('var')
  284.             val = m.group('val')
  285.             self._DigestMD5ClientAuthenticator__logger.debug('%r: %r' % (var, val))
  286.             if var == 'rspauth':
  287.                 rspauth = val
  288.                 continue
  289.         if not rspauth:
  290.             self._DigestMD5ClientAuthenticator__logger.debug('Final challenge without rspauth')
  291.             return Failure('bad-success')
  292.         
  293.         if rspauth == self.response_auth:
  294.             self.rspauth_checked = 1
  295.             return Response('')
  296.         else:
  297.             self._DigestMD5ClientAuthenticator__logger.debug('Wrong rspauth value - peer is cheating?')
  298.             self._DigestMD5ClientAuthenticator__logger.debug('my rspauth: %r' % (self.response_auth,))
  299.             return Failure('bad-success')
  300.  
  301.     
  302.     def finish(self, data):
  303.         if not self.response_auth:
  304.             self._DigestMD5ClientAuthenticator__logger.debug('Got success too early')
  305.             return Failure('bad-success')
  306.         
  307.         if self.rspauth_checked:
  308.             return Success(self.username, self.realm, self.authzid)
  309.         else:
  310.             r = self._final_challenge(data)
  311.             if isinstance(r, Failure):
  312.                 return r
  313.             
  314.             if self.rspauth_checked:
  315.                 return Success(self.username, self.realm, self.authzid)
  316.             else:
  317.                 self._DigestMD5ClientAuthenticator__logger.debug('Something went wrong when processing additional data with success?')
  318.                 return Failure('bad-success')
  319.  
  320.  
  321.  
  322. class DigestMD5ServerAuthenticator(ServerAuthenticator):
  323.     
  324.     def __init__(self, password_manager):
  325.         ServerAuthenticator.__init__(self, password_manager)
  326.         self.nonce = None
  327.         self.username = None
  328.         self.realm = None
  329.         self.authzid = None
  330.         self.done = None
  331.         self.last_nonce_count = None
  332.         self._DigestMD5ServerAuthenticator__logger = logging.getLogger('pyxmpp.sasl.DigestMD5ServerAuthenticator')
  333.  
  334.     
  335.     def start(self, response):
  336.         _unused = response
  337.         self.last_nonce_count = 0
  338.         params = []
  339.         realms = self.password_manager.get_realms()
  340.         if realms:
  341.             self.realm = _quote(realms[0])
  342.             for r in realms:
  343.                 r = _quote(r)
  344.                 params.append('realm="%s"' % (r,))
  345.             
  346.         else:
  347.             self.realm = None
  348.         nonce = _quote(self.password_manager.generate_nonce())
  349.         self.nonce = nonce
  350.         params.append('nonce="%s"' % (nonce,))
  351.         params.append('qop="auth"')
  352.         params.append('charset=utf-8')
  353.         params.append('algorithm=md5-sess')
  354.         self.authzid = None
  355.         self.done = 0
  356.         return Challenge(','.join(params))
  357.  
  358.     
  359.     def response(self, response):
  360.         if self.done:
  361.             return Success(self.username, self.realm, self.authzid)
  362.         
  363.         if not response:
  364.             return Failure('not-authorized')
  365.         
  366.         return self._parse_response(response)
  367.  
  368.     
  369.     def _parse_response(self, response):
  370.         response = response.split('\x00')[0]
  371.         if self.realm:
  372.             realm = to_utf8(self.realm)
  373.             realm = _quote(realm)
  374.         else:
  375.             realm = None
  376.         username = None
  377.         cnonce = None
  378.         digest_uri = None
  379.         response_val = None
  380.         authzid = None
  381.         nonce_count = None
  382.         while response:
  383.             m = _param_re.match(response)
  384.             if not m:
  385.                 self._DigestMD5ServerAuthenticator__logger.debug('Response syntax error: %r' % (response,))
  386.                 return Failure('not-authorized')
  387.             
  388.             response = m.group('rest')
  389.             var = m.group('var')
  390.             val = m.group('val')
  391.             self._DigestMD5ServerAuthenticator__logger.debug('%r: %r' % (var, val))
  392.             if var == 'realm':
  393.                 realm = val[1:-1]
  394.                 continue
  395.             if var == 'cnonce':
  396.                 if cnonce:
  397.                     self._DigestMD5ServerAuthenticator__logger.debug('Duplicate cnonce')
  398.                     return Failure('not-authorized')
  399.                 
  400.                 cnonce = val[1:-1]
  401.                 continue
  402.             if var == 'qop':
  403.                 if val != 'auth':
  404.                     self._DigestMD5ServerAuthenticator__logger.debug("qop other then 'auth'")
  405.                     return Failure('not-authorized')
  406.                 
  407.             val != 'auth'
  408.             if var == 'digest-uri':
  409.                 digest_uri = val[1:-1]
  410.                 continue
  411.             if var == 'authzid':
  412.                 authzid = val[1:-1]
  413.                 continue
  414.             if var == 'username':
  415.                 username = val[1:-1]
  416.                 continue
  417.             if var == 'response':
  418.                 response_val = val
  419.                 continue
  420.             if var == 'nc':
  421.                 nonce_count = val
  422.                 self.last_nonce_count += 1
  423.                 if int(nonce_count) != self.last_nonce_count:
  424.                     self._DigestMD5ServerAuthenticator__logger.debug('bad nonce: %r != %r' % (nonce_count, self.last_nonce_count))
  425.                     return Failure('not-authorized')
  426.                 
  427.             int(nonce_count) != self.last_nonce_count
  428.             continue
  429.             self
  430.         return self._check_params(username, realm, cnonce, digest_uri, response_val, authzid, nonce_count)
  431.  
  432.     
  433.     def _check_params(self, username, realm, cnonce, digest_uri, response_val, authzid, nonce_count):
  434.         if not cnonce:
  435.             self._DigestMD5ServerAuthenticator__logger.debug("Required 'cnonce' parameter not given")
  436.             return Failure('not-authorized')
  437.         
  438.         if not response_val:
  439.             self._DigestMD5ServerAuthenticator__logger.debug("Required 'response' parameter not given")
  440.             return Failure('not-authorized')
  441.         
  442.         if not username:
  443.             self._DigestMD5ServerAuthenticator__logger.debug("Required 'username' parameter not given")
  444.             return Failure('not-authorized')
  445.         
  446.         if not digest_uri:
  447.             self._DigestMD5ServerAuthenticator__logger.debug("Required 'digest_uri' parameter not given")
  448.             return Failure('not-authorized')
  449.         
  450.         if not nonce_count:
  451.             self._DigestMD5ServerAuthenticator__logger.debug("Required 'nc' parameter not given")
  452.             return Failure('not-authorized')
  453.         
  454.         return self._make_final_challenge(username, realm, cnonce, digest_uri, response_val, authzid, nonce_count)
  455.  
  456.     
  457.     def _make_final_challenge(self, username, realm, cnonce, digest_uri, response_val, authzid, nonce_count):
  458.         username_uq = from_utf8(username.replace('\\', ''))
  459.         if authzid:
  460.             authzid_uq = from_utf8(authzid.replace('\\', ''))
  461.         else:
  462.             authzid_uq = None
  463.         if realm:
  464.             realm_uq = from_utf8(realm.replace('\\', ''))
  465.         else:
  466.             realm_uq = None
  467.         digest_uri_uq = digest_uri.replace('\\', '')
  468.         self.username = username_uq
  469.         self.realm = realm_uq
  470.         (password, pformat) = self.password_manager.get_password(username_uq, realm_uq, ('plain', 'md5:user:realm:pass'))
  471.         if pformat == 'md5:user:realm:pass':
  472.             urp_hash = password
  473.         elif pformat == 'plain':
  474.             urp_hash = _make_urp_hash(username, realm, password)
  475.         else:
  476.             self._DigestMD5ServerAuthenticator__logger.debug("Couldn't get password.")
  477.             return Failure('not-authorized')
  478.         valid_response = _compute_response(urp_hash, self.nonce, cnonce, nonce_count, authzid, digest_uri)
  479.         if response_val != valid_response:
  480.             self._DigestMD5ServerAuthenticator__logger.debug('Response mismatch: %r != %r' % (response_val, valid_response))
  481.             return Failure('not-authorized')
  482.         
  483.         s = digest_uri_uq.split('/')
  484.         if len(s) == 3:
  485.             (serv_type, host, serv_name) = s
  486.         elif len(s) == 2:
  487.             (serv_type, host) = s
  488.             serv_name = None
  489.         else:
  490.             self._DigestMD5ServerAuthenticator__logger.debug('Bad digest_uri: %r' % (digest_uri_uq,))
  491.             return Failure('not-authorized')
  492.         info = { }
  493.         info['mechanism'] = 'DIGEST-MD5'
  494.         info['username'] = username_uq
  495.         info['serv-type'] = serv_type
  496.         info['host'] = host
  497.         info['serv-name'] = serv_name
  498.         if self.password_manager.check_authzid(authzid_uq, info):
  499.             rspauth = _compute_response_auth(urp_hash, self.nonce, cnonce, nonce_count, authzid, digest_uri)
  500.             self.authzid = authzid
  501.             self.done = 1
  502.             return Challenge('rspauth=' + rspauth)
  503.         else:
  504.             self._DigestMD5ServerAuthenticator__logger.debug('Authzid check failed')
  505.             return Failure('invalid_authzid')
  506.  
  507.  
  508.