home *** CD-ROM | disk | FTP | other *** search
/ Maximum CD 2011 February / maximum-cd-2011-02.iso / DiscContents / digsby_setup85.exe / lib / pyxmpp / sasl / digest_md5.pyo (.txt) < prev    next >
Encoding:
Python Compiled Bytecode  |  2010-11-24  |  13.7 KB  |  453 lines

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