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 |