Martin Geisler committed 2454556

Added salt to password hashes

Comments (0)

Files changed (1)

 password field encodes what type of password we are using. The following
 would be a typical authentication file::
-    user1:$plain$thisismypassword
-    user2:$md5$31435008693ce6976f45dedc5532e2c1
-    user3:$sha1$4b2c5a6d33c70caa171639d1e5a76a81f83c3cfb
-    user4:$sha224$8607c28d53f2c41d101c3fdbe87210adbd0584c21fcd5ddd7237f039
-    user5:$sha256$1da9133ab9dbd11d2937ec8d312e1e2569857059e73cc72df92e670928983ab5
-    user6:$sha384$f2e4be47d9642bdd1d208f0738a55feca0c7bc42a445f1a0233c28260141ad6bb432810dafc7ffb5891f96ad65a3206f
-    user7:$sha512$e9670bc9fb8d3a578736c31390205b1d9fef50adf941b3b349076d27e483e42b00c38998cac4370a362ffe5966d45012b95f8eb279b2cecfe1760cdf25d5ecca
+    user1:plain::thisismypassword
+    user2:md5:782181cd91165905:31435008693ce6976f45dedc5532e2c1
+    user3:sha1:501af982d28a19be:4b2c5a6d33c70caa171639d1e5a76a81f83c3cfb
+    user4:sha224:cc00bf8ba388b6b8:8607c28d53f2c41d101c3fdbe87210adbd0584c21fcd5ddd7237f039
+    user5:sha256:501af982d28a19be:1da9133ab9dbd11d2937ec8d312e1e2569857059e73cc72df92e670928983ab5
+    user6:sha384:4eb4a2459ad47fe9:f2e4be47d9642bdd1d208f0738a55feca0c7bc42a445f1a0233c28260141ad6bb432810dafc7ffb5891f96ad65a3206f
+    user7:sha512:501af982d28a19be:e9670bc9fb8d3a578736c31390205b1d9fef50adf941b3b349076d27e483e42b00c38998cac4370a362ffe5966d45012b95f8eb279b2cecfe1760cdf25d5ecca
+The passwords are "salted" before being stored, which means that the
+same password will result a different line every time you store it.
+You can therefore not see if two users have the same password.
 This extension also doubles as a management interface to this file that can be
 used through Mercurial::
+import binascii
 import getpass
 import base64
-import re
-import os.path
+import os
 from mercurial.i18n import _
 from mercurial.hgweb import common
     do_auth(hgweb, req, op, user, passwd)
 def _decode_db_password(dbpasswd):
-    regex = re.compile(r'^\$([^\$]+)\$(.*)$')
-    m = regex.match(dbpasswd)
-    if m:
-        return,
-    return None, None
+    try:
+        hash, salt, storepw = dbpasswd.split(':', 2)
+        return hash, salt, storepw
+    except ValueError:
+        return None, None, None
 def do_auth(hgweb, req, op, user, passwd):
     fname = hgweb.config('textauth', 'file')
         db = [line.rstrip('\r\n').split(':', 1) for line in f]
         for dbuser, dbpasswd in db:
             if user == dbuser:
-                algo, rdbpasswd = _decode_db_password(dbpasswd)
+                algo, salt, rdbpasswd = _decode_db_password(dbpasswd)
                 if algo is None:
                     raise common.ErrorResponse(common.HTTP_SERVER_ERROR,
                             'invalid authentication file on server')
                 algos = _gethashes()
-                if algos[algo](passwd) == rdbpasswd:
+                if algos[algo](salt + passwd) == rdbpasswd:
                     req.env['REMOTE_USER'] = user
         ui.warn(_('passwords do not match\n'))
         return 1
-    storepw = hashes[hash](passwd)
+    salt = binascii.hexlify(os.urandom(8))
+    storepw = hashes[hash](salt + passwd)
     fh = open(authfile, 'a')
-        fh.write('%s:$%s$%s\n' % (username, hash, storepw))
+        fh.write('%s:%s:%s:%s\n' % (username, hash, salt, storepw))
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.