VirtualMailManager/password.py
branchv0.7.x
changeset 643 df1e3b67882a
parent 638 0de0b9e75c9f
child 651 6937cb38db71
equal deleted inserted replaced
642:4cd9d0a9f42f 643:df1e3b67882a
    49 
    49 
    50 _ = lambda msg: msg
    50 _ = lambda msg: msg
    51 cfg_dget = lambda option: None
    51 cfg_dget = lambda option: None
    52 _sys_rand = SystemRandom()
    52 _sys_rand = SystemRandom()
    53 _choice = _sys_rand.choice
    53 _choice = _sys_rand.choice
    54 _get_salt = lambda s_len: ''.join(_choice(SALTCHARS) for x in xrange(s_len))
    54 _get_salt = lambda s_len: ''.join(_choice(SALTCHARS) for x in range(s_len))
    55 
    55 
    56 
    56 
    57 def _dovecotpw(password, scheme, encoding):
    57 def _dovecotpw(password, scheme, encoding):
    58     """Communicates with dovecotpw (Dovecot 2.0: `doveadm pw`) and returns
    58     """Communicates with dovecotpw (Dovecot 2.0: `doveadm pw`) and returns
    59     the hashed password: {scheme[.encoding]}hash
    59     the hashed password: {scheme[.encoding]}hash
    79     """Returns an new MD4-hash object if supported by the hashlib or
    79     """Returns an new MD4-hash object if supported by the hashlib or
    80     provided by PyCrypto - otherwise `None`.
    80     provided by PyCrypto - otherwise `None`.
    81     """
    81     """
    82     try:
    82     try:
    83         return hashlib.new('md4')
    83         return hashlib.new('md4')
    84     except ValueError, err:
    84     except ValueError as err:
    85         if str(err) == 'unsupported hash type':
    85         if str(err) == 'unsupported hash type':
    86             try:
    86             try:
    87                 from Crypto.Hash import MD4
    87                 from Crypto.Hash import MD4
    88                 return MD4.new()
    88                 return MD4.new()
    89             except ImportError:
    89             except ImportError:
   328     the used Dovecot version and features of the libc).
   328     the used Dovecot version and features of the libc).
   329     `encodings` is a tuple with all usable encoding suffixes. The tuple may
   329     `encodings` is a tuple with all usable encoding suffixes. The tuple may
   330     be empty.
   330     be empty.
   331     """
   331     """
   332     dcv = cfg_dget('misc.dovecot_version')
   332     dcv = cfg_dget('misc.dovecot_version')
   333     schemes = (k for (k, v) in _scheme_info.iteritems() if v[1] <= dcv)
   333     schemes = (k for (k, v) in _scheme_info.items() if v[1] <= dcv)
   334     encodings = ('.B64', '.BASE64', '.HEX') if dcv >= 0x10100a01 else ()
   334     encodings = ('.B64', '.BASE64', '.HEX') if dcv >= 0x10100a01 else ()
   335     return schemes, encodings
   335     return schemes, encodings
   336 
   336 
   337 
   337 
   338 def verify_scheme(scheme):
   338 def verify_scheme(scheme):
   348     Raises a `VMMError` if the password scheme:
   348     Raises a `VMMError` if the password scheme:
   349       * is unknown
   349       * is unknown
   350       * depends on a newer Dovecot version
   350       * depends on a newer Dovecot version
   351       * has a unknown encoding suffix
   351       * has a unknown encoding suffix
   352     """
   352     """
   353     assert isinstance(scheme, basestring), 'Not a str/unicode: %r' % scheme
   353     assert isinstance(scheme, str), 'Not a str/unicode: %r' % scheme
   354     scheme_encoding = scheme.upper().split('.')
   354     scheme_encoding = scheme.upper().split('.')
   355     scheme = scheme_encoding[0]
   355     scheme = scheme_encoding[0]
   356     if scheme not in _scheme_info:
   356     if scheme not in _scheme_info:
   357         raise VMMError(_(u"Unsupported password scheme: '%s'") % scheme,
   357         raise VMMError(_("Unsupported password scheme: '%s'") % scheme,
   358                        VMM_ERROR)
   358                        VMM_ERROR)
   359     if cfg_dget('misc.dovecot_version') < _scheme_info[scheme][1]:
   359     if cfg_dget('misc.dovecot_version') < _scheme_info[scheme][1]:
   360         raise VMMError(_(u"The password scheme '%(scheme)s' requires Dovecot "
   360         raise VMMError(_("The password scheme '%(scheme)s' requires Dovecot "
   361                          u">= v%(version)s.") % {'scheme': scheme,
   361                          ">= v%(version)s.") % {'scheme': scheme,
   362                        'version': version_str(_scheme_info[scheme][1])},
   362                        'version': version_str(_scheme_info[scheme][1])},
   363                        VMM_ERROR)
   363                        VMM_ERROR)
   364     if len(scheme_encoding) > 1:
   364     if len(scheme_encoding) > 1:
   365         if cfg_dget('misc.dovecot_version') < 0x10100a01:
   365         if cfg_dget('misc.dovecot_version') < 0x10100a01:
   366             raise VMMError(_(u'Encoding suffixes for password schemes require '
   366             raise VMMError(_('Encoding suffixes for password schemes require '
   367                              u'Dovecot >= v1.1.alpha1.'), VMM_ERROR)
   367                              'Dovecot >= v1.1.alpha1.'), VMM_ERROR)
   368         if scheme_encoding[1] not in ('B64', 'BASE64', 'HEX'):
   368         if scheme_encoding[1] not in ('B64', 'BASE64', 'HEX'):
   369             raise VMMError(_(u"Unsupported password encoding: '%s'") %
   369             raise VMMError(_("Unsupported password encoding: '%s'") %
   370                            scheme_encoding[1], VMM_ERROR)
   370                            scheme_encoding[1], VMM_ERROR)
   371         encoding = scheme_encoding[1]
   371         encoding = scheme_encoding[1]
   372     else:
   372     else:
   373         encoding = None
   373         encoding = None
   374     return scheme, encoding
   374     return scheme, encoding
   379 
   379 
   380     If no *scheme* is given the password scheme from the configuration will
   380     If no *scheme* is given the password scheme from the configuration will
   381     be used for the hash generation.  When 'DIGEST-MD5' is used as scheme,
   381     be used for the hash generation.  When 'DIGEST-MD5' is used as scheme,
   382     also an EmailAddress instance must be given as *user* argument.
   382     also an EmailAddress instance must be given as *user* argument.
   383     """
   383     """
   384     if not isinstance(password, basestring):
   384     if not isinstance(password, str):
   385         raise TypeError('Password is not a string: %r' % password)
   385         raise TypeError('Password is not a string: %r' % password)
   386     if isinstance(password, unicode):
   386     if isinstance(password, str):
   387         password = password.encode(ENCODING)
   387         password = password.encode(ENCODING)
   388     password = password.strip()
   388     password = password.strip()
   389     if not password:
   389     if not password:
   390         raise ValueError("Could not accept empty password.")
   390         raise ValueError("Could not accept empty password.")
   391     if scheme is None:
   391     if scheme is None: