Commit e5842c91 authored by Antoine Millet's avatar Antoine Millet

Implementing hashed password storage (compatible with openldap).

parent 56caa838
......@@ -36,11 +36,14 @@ u'node'
>>>
'''
import hashlib
import base64
import random
import threading
import logging
import json
import os
import re
from functools import wraps
class CCConf(object):
......@@ -55,6 +58,8 @@ class CCConf(object):
'tags': [],
'perms': None}
RE_SALTPW = re.compile(r'{(?P<method>[A-Z]+)}(?P<password>.+)')
def __init__(self, path_directory):
self._path = path_directory
self._lock = threading.Lock()
......@@ -115,6 +120,61 @@ class CCConf(object):
return self._get_conf(login)
def _unsaltify(self, string, digest_size):
string = base64.decodestring(string)
password = string[0:digest_size]
salt = string[digest_size:]
return password, salt
def _get_randsalt(self, size=10):
salt = ''
for _ in xrange(size):
salt += chr(random.randint(1, 255))
return salt
def _auth_ssha(self, provided_passwd, configured_passwd=None):
if configured_passwd is not None:
salt = self._unsaltify(configured_passwd, 20)[1]
else:
salt = self._get_randsalt()
digest = hashlib.sha1(str(provided_passwd) + salt).digest()
return '{SSHA}%s' % base64.b64encode(digest + salt)
def _auth_sha(self, configured_passwd, provided_passwd):
digest = hashlib.sha1(str(provided_passwd)).digest()
return '{SHA}%s' % base64.b64encode(digest)
def _auth_smd5(self, configured_passwd, provided_passwd):
if configured_passwd is not None:
salt = self._unsaltify(configured_passwd, 16)[1]
else:
salt = self._get_randsalt()
digest = hashlib.sha1(str(provided_passwd) + salt).digest()
return '{SMD5}%s' % base64.b64encode(digest + salt)
def _auth_md5(self, configured_passwd, provided_passwd):
digest = hashlib.sha1(str(provided_passwd)).digest()
return '{MD5}%s' % base64.b64encode(digest)
def _auth_plain(self, configured_passwd, provided_passwd):
return provided_passwd
def _hash_password(self, password, method='ssha'):
'''
Hash a password using given method and return it.
:param password: the password to hash
:param method: the hashing method
:return: hashed password
'''
meth = '_auth_%s' % method.lower()
if hasattr(self, meth):
auth = getattr(self, meth)
return auth(password)
else:
raise CCConf.BadMethodError("Bad hashing method: '%s'" % method)
def authentify(self, login, password):
'''
Authentify the client providing its login and password. The function
......@@ -127,13 +187,26 @@ class CCConf(object):
'''
conf = self._get_conf(login)
if conf['password'] == password:
passwd_conf = conf['password']
is_valid = False
m = CCConf.RE_SALTPW.match(passwd_conf)
if m is None:
is_valid = self._auth_plain(passwd_conf, password)
else:
meth = '_auth_%s' % m.group('method').lower()
if hasattr(self, meth):
auth = getattr(self, meth)
is_valid = auth(password, m.group('password')) == passwd_conf
else:
logging.warning('Bad authentication method for %s: '
'%s' % (login, m.group('method')))
if is_valid:
return conf['role']
else:
return None
@_writer
def set_password(self, login, password):
def set_password(self, login, password, method='ssha'):
'''
Update the client's password in the configuration.
......@@ -143,6 +216,7 @@ class CCConf(object):
'''
conf = self._get_conf(login)
password = self._hash_password(password, method)
conf['password'] = password
self._set_conf(login, conf)
......@@ -240,3 +314,6 @@ class CCConf(object):
class AlreadyExistingAccount(Exception):
pass
class BadMethodError(Exception):
pass
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment