Skip to content

Commit

Permalink
First draft of Drupal 7 password compatibility.
Browse files Browse the repository at this point in the history
  • Loading branch information
halstead committed Apr 27, 2012
1 parent db4b929 commit e3667be
Show file tree
Hide file tree
Showing 2 changed files with 114 additions and 11 deletions.
35 changes: 24 additions & 11 deletions drupalGitSSHDaemon.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
from config import config
from service import Service
from service.protocols import AuthProtocol
from drupalpass import DrupalHash

class DrupalMeta(object):
def __init__(self):
Expand Down Expand Up @@ -390,17 +391,29 @@ def __init__(self, meta):
self.meta = meta

def requestAvatarId(self, credentials):
self.meta.password = hashlib.md5(credentials.password).hexdigest()
service = Service(AuthProtocol('drupalorg-vcs-auth-check-user-pass'))
service.request_bool({"username":credentials.username},
{"password":self.meta.password})
def auth_callback(result):
if result:
return credentials.username
else:
return Failure(UnauthorizedLogin(credentials.username))
service.addCallback(auth_callback)
return service.deferred
def fetchHash(credentials):
service = Service(AuthProtocol('drupalorg-vcs-auth-fetch-user-hash'))
service.request_json({"username":credentials.username})
def auth_callback(result):
if result:
self.meta.password = DrupalHash(result, credentials.password).get_hash()
return checkAuth(credentials)
service.addCallback(auth_callback)
return service.deferred

def checkAuth(credentials):
service = Service(AuthProtocol('drupalorg-vcs-auth-check-user-pass'))
service.request_bool({"username":credentials.username},
{"password":self.meta.password})
def auth_callback(result):
if result:
print "SUCCESS--"*88

This comment has been minimized.

Copy link
@chizu

chizu Apr 27, 2012

Probably don't want to output SUCCESS-- 88 times in deployed code. The rest of this looks good, the deferred chain makes sense.

This comment has been minimized.

Copy link
@halstead

halstead Apr 27, 2012

Author Owner

Absolutely right. Oops and thank you.

return credentials.username
else:
return Failure(UnauthorizedLogin(credentials.username))
service.addCallback(auth_callback)
return service.deferred
return fetchHash(credentials)

class GitServer(SSHFactory):
authmeta = DrupalMeta()
Expand Down
90 changes: 90 additions & 0 deletions drupalpass/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
import hashlib

# Calculate a non-truncated Drupal 7 compatible password hash.
# The consumer of these hashes must truncate correctly.

class DrupalHash:

def __init__(self,stored_hash, password):
self.itoa64 = './0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'
self.last_hash = self.rehash(stored_hash, password)

def get_hash(self):
return self.last_hash

def password_get_count_log2(self, setting):
return self.itoa64.index(setting[3])

def password_crypt(self, algo, password, setting):
setting = setting[0:12]
if setting[0] != '$' or setting[2] != '$':
return False

count_log2 = self.password_get_count_log2(setting)
salt = setting[4:12]
if len(salt) < 8:
return False
count = 1 << count_log2

if algo == 'md5':
hash_func = hashlib.md5
elif algo == 'sha512':
hash_func = hashlib.sha512
else:
return False
hash_str = hash_func(salt + password).digest()
for c in range(count):
hash_str = hash_func(hash_str + password).digest()
output = setting + self.custom64(hash_str)
return output

def custom64(self, string, count = 0):
if count == 0:
count = len(string)
output = ''
i = 0
itoa64 = self.itoa64
while 1:
value = ord(string[i])
i += 1
output += itoa64[value & 0x3f]
if i < count:
value |= ord(string[i]) << 8
output += itoa64[(value >> 6) & 0x3f]
if i >= count:
break
i += 1
if i < count:
value |= ord(string[i]) << 16
output += itoa64[(value >> 12) & 0x3f]
if i >= count:
break
i += 1
output += itoa64[(value >> 18) & 0x3f]
if i >= count:
break
return output

def rehash(self, stored_hash, password):
# Drupal 6 compatibility
if len(stored_hash) == 32 and stored_hash.find('$') == -1:
return hashlib.md5(password).hexdigest()
# Drupal 7
if stored_hash[0:2] == 'U$':
stored_hash = stored_hash[1:]
password = hashlib.md5(password).hexdigest()
hash_type = stored_hash[0:3]
if hash_type == '$S$':
hash_str = self.password_crypt('sha512', password, stored_hash)
elif hash_type == '$H$' or hash_type == '$P$':
hash_str = self.password_crypt('md5', password, stored_hash)
else:
hash_str = False
return hash_str

if __name__ == "__main__":
ha = '$S$D5z1Wm4bevjS5EQ3OdB.lI0NFTnCyIuD6VFHs5fkdjFHo0lvsdmv'
pw = 'admin'
new_ha = DrupalHash(ha, pw)
print "Original hash: %s" % ha
print "Password hash: %s" % new_ha.get_hash()

0 comments on commit e3667be

Please sign in to comment.