VirtualMailManager/password.py
branchv0.6.x
changeset 287 1e77dd639fa3
parent 284 ec1966828246
child 289 142f188f7552
equal deleted inserted replaced
286:e2046d47688b 287:1e77dd639fa3
     4 
     4 
     5 """
     5 """
     6     VirtualMailManager.password
     6     VirtualMailManager.password
     7 
     7 
     8     VirtualMailManager's password module to generate password hashes from
     8     VirtualMailManager's password module to generate password hashes from
     9     passwords or random passwords. There are two functions:
     9     passwords or random passwords. This module provides following
       
    10     functions:
    10 
    11 
    11         hashed_password = pwhash(password[, scheme][, user])
    12         hashed_password = pwhash(password[, scheme][, user])
    12         random_password = randompw()
    13         random_password = randompw()
       
    14         scheme, encoding = verify_scheme(scheme)
    13 """
    15 """
    14 
    16 
    15 from crypt import crypt
    17 from crypt import crypt
    16 from random import SystemRandom
    18 from random import SystemRandom
    17 from subprocess import Popen, PIPE
    19 from subprocess import Popen, PIPE
   341     'SSHA256': (_ssha256_hash, 0x10200a04),
   343     'SSHA256': (_ssha256_hash, 0x10200a04),
   342     'SSHA512': (_ssha512_hash, 0x20000b03),
   344     'SSHA512': (_ssha512_hash, 0x20000b03),
   343 }
   345 }
   344 
   346 
   345 
   347 
       
   348 def verify_scheme(scheme):
       
   349     """Checks if the password scheme *scheme* is known and supported by the
       
   350     configured `misc.dovecot_version`.
       
   351 
       
   352     The *scheme* maybe a password scheme's name (e.g.: 'PLAIN') or a scheme
       
   353     name with a encoding suffix (e.g. 'PLAIN.BASE64').  If the scheme is
       
   354     known and supported by the used Dovecot version,
       
   355     a tuple ``(scheme, encoding)`` will be returned.
       
   356     The `encoding` in the tuple may be `None`.
       
   357 
       
   358     Raises a `VMMError` if the password scheme:
       
   359       * is unknown
       
   360       * depends on a newer Dovecot version
       
   361       * has a unknown encoding suffix
       
   362     """
       
   363     assert isinstance(scheme, basestring), 'Not a str/unicode: %r' % scheme
       
   364     scheme_encoding = scheme.upper().split('.')
       
   365     scheme = scheme_encoding[0]
       
   366     if not scheme in _scheme_info:
       
   367         raise VMMError(_(u"Unsupported password scheme: '%s'") % scheme,
       
   368                        VMM_ERROR)
       
   369     if cfg_dget('misc.dovecot_version') < _scheme_info[scheme][1]:
       
   370         raise VMMError(_(u"The password scheme '%(scheme)s' requires Dovecot \
       
   371 >= v%(version)s") %
       
   372                        {'scheme': scheme,
       
   373                         'version': version_str(_scheme_info[scheme][1])},
       
   374                        VMM_ERROR)
       
   375     if len(scheme_encoding) > 1:
       
   376         if cfg_dget('misc.dovecot_version') < 0x10100a01:
       
   377             raise VMMError(_(u'Encoding suffixes for password schemes require \
       
   378 Dovecot >= v1.1.alpha1'),
       
   379                            VMM_ERROR)
       
   380         if scheme_encoding[1] not in ('B64', 'BASE64', 'HEX'):
       
   381             raise VMMError(_(u"Unsupported password encoding: '%s'") %
       
   382                            scheme_encoding[1], VMM_ERROR)
       
   383         encoding = scheme_encoding[1]
       
   384     else:
       
   385         encoding = None
       
   386     return scheme, encoding
       
   387 
       
   388 
   346 def pwhash(password, scheme=None, user=None):
   389 def pwhash(password, scheme=None, user=None):
   347     """Generates a password hash from the plain text *password* string.
   390     """Generates a password hash from the plain text *password* string.
   348 
   391 
   349     If no *scheme* is given the password scheme from the configuration will
   392     If no *scheme* is given the password scheme from the configuration will
   350     be used for the hash generation.  When 'DIGEST-MD5' is used as scheme,
   393     be used for the hash generation.  When 'DIGEST-MD5' is used as scheme,
   357     password = password.strip()
   400     password = password.strip()
   358     if not password:
   401     if not password:
   359         raise ValueError("Couldn't accept empty password.")
   402         raise ValueError("Couldn't accept empty password.")
   360     if scheme is None:
   403     if scheme is None:
   361         scheme = cfg_dget('misc.password_scheme')
   404         scheme = cfg_dget('misc.password_scheme')
   362     scheme_encoding = scheme.split('.')
   405     scheme, encoding = verify_scheme(scheme)
   363     scheme = scheme_encoding[0].upper()
       
   364     if not scheme in _scheme_info:
       
   365         raise VMMError(_(u"Unsupported password scheme: '%s'") % scheme,
       
   366                        VMM_ERROR)
       
   367     if cfg_dget('misc.dovecot_version') < _scheme_info[scheme][1]:
       
   368         raise VMMError(_(u"The scheme '%s' requires Dovecot >= v%s") %
       
   369                        (scheme, version_str(_scheme_info[scheme][1])),
       
   370                        VMM_ERROR)
       
   371     if len(scheme_encoding) > 1:
       
   372         if cfg_dget('misc.dovecot_version') < 0x10100a01:
       
   373             raise VMMError(_(u'Encoding suffixes for password schemes require \
       
   374 Dovecot >= v1.1.alpha1'),
       
   375                            VMM_ERROR)
       
   376         if scheme_encoding[1].upper() not in ('B64', 'BASE64', 'HEX'):
       
   377             raise ValueError('Unsupported encoding: %r' % scheme_encoding[1])
       
   378         encoding = scheme_encoding[1].upper()
       
   379     else:
       
   380         encoding = None
       
   381     if scheme == 'DIGEST-MD5':
   406     if scheme == 'DIGEST-MD5':
   382         assert isinstance(user, EmailAddress)
   407         assert isinstance(user, EmailAddress)
   383         return _md5_hash(password, scheme, encoding, user)
   408         return _md5_hash(password, scheme, encoding, user)
   384     return _scheme_info[scheme][0](password, scheme, encoding)
   409     return _scheme_info[scheme][0](password, scheme, encoding)
   385 
   410