VMM/password: Added support Blowfish/SHA-256/SHA-512 crypt(). v0.6.x
authorPascal Volk <neverseen@users.sourceforge.net>
Mon, 03 May 2010 08:25:26 +0000
branchv0.6.x
changeset 284 ec1966828246
parent 283 ea6d052de24a
child 285 d30a94f5aef5
VMM/password: Added support Blowfish/SHA-256/SHA-512 crypt(). Also updated Config and man section 5.
TODO
VirtualMailManager/Config.py
VirtualMailManager/password.py
man/de/man5/vmm.cfg.5.rst
man/man5/vmm.cfg.5.rst
man/substitute_links_5.rst
--- a/TODO	Fri Apr 30 08:02:03 2010 +0000
+++ b/TODO	Mon May 03 08:25:26 2010 +0000
@@ -4,6 +4,10 @@
 
    ds - domainservices: smtp pop imap sieve???
 
+
+subcommand for displaying support crypt algorithms.
+
+
 - Aliases
     - avoid looping aliases
 
@@ -15,7 +19,9 @@
         + alias domains
 
 Database:
-   public.users.passwd: increase to "character varying(150)"
-   	why? `doveadm pw -s SSHA512.hex -p 1`
+   public.users.passwd: increase to "character varying(250)"
+   	why? len(VirtualMailManager.password.pwhash('1', 'crypt.hex')) -> 249
+	     if VirtualMailManager.password.CRYPT_SHA512 is True and
+	     misc.crypt_sha512_rounds > 0
    public.users.digestmd5: add "character varying(48)"
 	Outlook will love it. (`doveadm pw -s DIGEST-MD5.hex -p 1 -u 0`)
--- a/VirtualMailManager/Config.py	Fri Apr 30 08:02:03 2010 +0000
+++ b/VirtualMailManager/Config.py	Mon May 03 08:25:26 2010 +0000
@@ -343,6 +343,9 @@
             },
             'misc': {
                 'base_directory': LCO(str, '/srv/mail', self.get, is_dir),
+                'crypt_blowfish_rounds': LCO(int, 0, self.getint),
+                'crypt_sha256_rounds': LCO(int, 0, self.getint),
+                'crypt_sha512_rounds': LCO(int, 0, self.getint),
                 'dovecot_version': LCO(str, '1.2.11', self.hexversion,
                                        check_version_format),
                 'password_scheme': LCO(str, 'CRAM-MD5', self.get,
--- a/VirtualMailManager/password.py	Fri Apr 30 08:02:03 2010 +0000
+++ b/VirtualMailManager/password.py	Mon May 03 08:25:26 2010 +0000
@@ -13,7 +13,7 @@
 """
 
 from crypt import crypt
-from random import choice, shuffle
+from random import SystemRandom
 from subprocess import Popen, PIPE
 
 try:
@@ -35,7 +35,28 @@
 
 _ = lambda msg: msg
 cfg_dget = lambda option: None
-_get_salt = lambda s_len: ''.join(choice(SALTCHARS) for x in xrange(s_len))
+_sys_rand = SystemRandom()
+_get_salt = lambda salt_len: ''.join(_sys_rand.sample(SALTCHARS, salt_len))
+
+
+def _test_crypt_algorithms():
+    """Check for Blowfish/SHA-256/SHA-512 support in crypt.crypt()."""
+    blowfish_ = sha256_ = sha512_ = False
+    _blowfish = '$2a$04$0123456789abcdefABCDE.N.drYX5yIAL1LkTaaZotW3yI0hQhZru'
+    _sha256 = '$5$rounds=1000$0123456789abcdef$K/DksR0DT01hGc8g/kt9McEgrbFMKi\
+9qrb1jehe7hn4'
+    _sha512 = '$6$rounds=1000$0123456789abcdef$ZIAd5WqfyLkpvsVCVUU1GrvqaZTqvh\
+JoouxdSqJO71l9Ld3tVrfOatEjarhghvEYADkq//LpDnTeO90tcbtHR1'
+
+    if crypt('08/15!test~4711', '$2a$04$0123456789abcdefABCDEF$') == _blowfish:
+        blowfish_ = True
+    if crypt('08/15!test~4711', '$5$rounds=1000$0123456789abcdef$') == _sha256:
+        sha256_ = True
+    if crypt('08/15!test~4711', '$6$rounds=1000$0123456789abcdef$') == _sha512:
+        sha512_ = True
+    return blowfish_, sha256_, sha512_
+
+CRYPT_BLOWFISH, CRYPT_SHA256, CRYPT_SHA512 = _test_crypt_algorithms()
 
 
 def _dovecotpw(password, scheme, encoding):
@@ -110,10 +131,44 @@
     return get_unicode('{%s}%s' % (scheme, password))
 
 
+def _get_crypt_blowfish_salt():
+    """Generates a salt for Blowfish crypt."""
+    rounds = cfg_dget('misc.crypt_blowfish_rounds')
+    if rounds < 4:
+        rounds = 4
+    elif rounds > 31:
+        rounds = 31
+    return '$2a$%02d$%s$' % (rounds, _get_salt(22))
+
+
+def _get_crypt_shaxxx_salt(crypt_id):
+    """Generates a salt for crypt using the SHA-256 or SHA-512 encryption
+    method.
+    *crypt_id* must be either `5` (SHA-256) or `6` (SHA1-512).
+    """
+    assert crypt_id in (5, 6), 'invalid crypt id: %r' % crypt_id
+    if crypt_id is 6:
+        rounds = cfg_dget('misc.crypt_sha512_rounds')
+    else:
+        rounds = cfg_dget('misc.crypt_sha256_rounds')
+    if rounds < 1000:
+        rounds = 1000
+    elif rounds > 999999999:
+        rounds = 999999999
+    return '$%d$rounds=%d$%s$' % (crypt_id, rounds, _get_salt(16))
+
+
 def _crypt_hash(password, scheme, encoding):
     """Generates (encoded) CRYPT/MD5/MD5-CRYPT hashes."""
     if scheme == 'CRYPT':
-        salt = _get_salt(2)
+        if CRYPT_BLOWFISH and cfg_dget('misc.crypt_blowfish_rounds'):
+            salt = _get_crypt_blowfish_salt()
+        elif CRYPT_SHA512 and cfg_dget('misc.crypt_sha512_rounds'):
+            salt = _get_crypt_shaxxx_salt(6)
+        elif CRYPT_SHA256 and cfg_dget('misc.crypt_sha256_rounds'):
+            salt = _get_crypt_shaxxx_salt(5)
+        else:
+            salt = _get_salt(2)
     else:
         salt = '$1$%s$' % _get_salt(8)
     encrypted = crypt(password, salt)
@@ -121,7 +176,7 @@
         if encoding == 'HEX':
             encrypted = encrypted.encode('hex')
         else:
-            encrypted = encrypted.encode('base64').rstrip()
+            encrypted = encrypted.encode('base64').replace('\n', '')
     return _format_digest(encrypted, scheme, encoding)
 
 
@@ -335,11 +390,9 @@
     The length of the password can be configured in the ``vmm.cfg``
     (account.password_length).
     """
-    pw_chars = list(PASSWDCHARS)
-    shuffle(pw_chars)
     pw_len = cfg_dget('account.password_length')
     if pw_len < 8:
         pw_len = 8
-    return ''.join(choice(pw_chars) for x in xrange(pw_len))
+    return ''.join(_sys_rand.sample(PASSWDCHARS, pw_len))
 
-del _, cfg_dget
+del _, cfg_dget, _test_crypt_algorithms
--- a/man/de/man5/vmm.cfg.5.rst	Fri Apr 30 08:02:03 2010 +0000
+++ b/man/de/man5/vmm.cfg.5.rst	Mon May 03 08:25:26 2010 +0000
@@ -355,6 +355,48 @@
   Alle Domain-Verzeichnisse werden innerhalb dieses Basis-Verzeichnisses
   angelegt.
 
+.. _misc.crypt_blowfish_rounds:
+
+``crypt_blowfish_rounds (Vorgabe: 0)`` : *Int*
+  Anzahl der Verschlüsselungsdurchgänge für Blowfish-Crypt.
+
+  Diese Einstellung beeinflusst das Verhalten des 'CRYPT'-Passwortschematas.
+  Standardmäßig verwendet crypt den DES-Algorithmus für die
+  Kennwortverschlüsselung. |vmm(1)|_ prüft, ob Blowfish-Crypt verfügbar
+  ist. Wenn der Blowfishalgorithmus unterstützt wird und der Wert dieser
+  Einstellung größer als 0 ist, wird Blowfish anstelle von DES als
+  Algorithmus verwendet.
+
+  Der Wert muss im Bereich von **4** - **31** liegen.
+
+.. _misc.crypt_sha256_rounds:
+
+``crypt_sha256_rounds (Vorgabe: 0)`` : *Int*
+  Anzahl der Verschlüsselungdurchgänge für crypt unter Verwendung der SHA-256
+  Verschlüsselungsmethode.
+
+  Diese Einstellung beeinflusst das Verhalten des 'CRYPT'-Passwortschematas.
+  Standardmäßig verwendet crypt den DES-Algorithmus für die
+  Kennwortverschlüsselung. |vmm(1)|_ prüft, ob crypt die SHA-256 und/oder
+  SHA-512 Algorithmen unterstützt. Wenn die Algorithmen unterstützt werden
+  und der Wert dieser Einstellung größer als 0 ist, wird SHA-256 anstelle
+  von DES als Algorithmus verwendet.
+
+  Wenn der Wert von |misc.crypt_sha512_rounds|_ größer als 0 ist, wird der
+  SHA-512 Algorithmus anstelle von SHA-256 verwendet.
+
+  Der Wert muss im Bereich von **1000** - **999999999** liegen.
+
+.. _misc.crypt_sha512_rounds:
+
+``crypt_sha512_rounds (Vorgabe: 0)`` : *Int*
+  Anzahl der Verschlüsselungsdurchgänge für crypt unter Verwendung
+  der SHA-512 Verschlüsselungsmethode.
+
+  Siehe |misc.crypt_sha256_rounds|_ für Details.
+
+  Der Wert muss im Bereich von **1000** - **999999999** liegen.
+
 .. _misc.password_scheme:
 
 ``password_scheme (Vorgabe: CRAM-MD5)`` : *String*
@@ -381,7 +423,8 @@
 
   [misc]
   base_directory = /srv/mail
-  password_scheme = PLAIN
+  crypt_sha512_rounds = 10000
+  password_scheme = CRYPT
   transport = dovecot:
   dovecot_version = 2.0.beta4
 
--- a/man/man5/vmm.cfg.5.rst	Fri Apr 30 08:02:03 2010 +0000
+++ b/man/man5/vmm.cfg.5.rst	Mon May 03 08:25:26 2010 +0000
@@ -339,6 +339,44 @@
 ``base_directory (default: /srv/mail)`` : *String*
   All domain directories will be created inside this directory.
 
+.. _misc.crypt_blowfish_rounds:
+
+``crypt_blowfish_rounds (default: 0)`` : *Int*
+  Number of encryption rounds for Blowfish crypt.
+
+  This setting affects the behavior of the 'CRYPT' password scheme. By
+  default crypt will use the DES algorithm for password encryption.
+  |vmm(1)|_ checks if Blowfish crypt is available. When the Blowfish
+  algorithm is supported and the value of this setting is greater than 0
+  Blowfish will be used for crypt, instead of DES.
+
+  The value must be in range **4** - **31**.
+
+.. _misc.crypt_sha256_rounds:
+
+``crypt_sha256_rounds (default: 0)`` : *Int*
+  Number of encryption rounds for crypt using the SHA-256 encryption method.
+
+  This setting affects the behavior of the 'CRYPT' password scheme. By
+  default crypt will use the DES algorithm for password encryption.
+  |vmm(1)|_ checks if crypt supports the SHA-256 and/or SHA-512 algorithms.
+  When the algorithms are supported and the value of this setting is greater
+  than 0, SHA-256 will be used instead of DES.
+
+  When the value of |misc.crypt_sha512_rounds|_ is greater than 0, the
+  SHA-512 algorithm will be used instead of SHA-256.
+
+  The value must be in range **1000** - **999999999**.
+
+.. _misc.crypt_sha512_rounds:
+
+``crypt_sha512_rounds (default: 0)`` : *Int*
+  Number of encryption rounds for crypt using the SHA-512 encryption method.
+
+  See |misc.crypt_sha256_rounds|_ for details.
+
+  The value must be in range **1000** - **999999999**.
+
 .. _misc.password_scheme:
 
 ``password_scheme (default: CRAM-MD5)`` : *String*
@@ -365,7 +403,8 @@
 
   [misc]
   base_directory = /srv/mail
-  password_scheme = PLAIN
+  crypt_sha512_rounds = 10000
+  password_scheme = CRYPT
   transport = dovecot:
   dovecot_version = 2.0.beta4
 
--- a/man/substitute_links_5.rst	Fri Apr 30 08:02:03 2010 +0000
+++ b/man/substitute_links_5.rst	Mon May 03 08:25:26 2010 +0000
@@ -2,6 +2,8 @@
 
 .. |account.password_length| replace:: **password_length**
 .. |mailbox.format| replace:: **format**
+.. |misc.crypt_sha256_rounds| replace:: **crypt_sha256_rounds**
+.. |misc.crypt_sha512_rounds| replace:: **crypt_sha512_rounds**
 .. |misc.password_scheme| replace:: **misc.password_scheme**
 
 .. |vmm configure| replace:: **vmm configure**