From e5842c91ae2f1cc0e075f7b7f6613230f532bfec Mon Sep 17 00:00:00 2001 From: Antoine Millet Date: Fri, 24 Dec 2010 15:36:06 +0100 Subject: [PATCH] Implementing hashed password storage (compatible with openldap). --- ccserver/conf.py | 83 ++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 80 insertions(+), 3 deletions(-) diff --git a/ccserver/conf.py b/ccserver/conf.py index 88eaf9c..1192275 100644 --- a/ccserver/conf.py +++ b/ccserver/conf.py @@ -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[A-Z]+)}(?P.+)') + 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 -- GitLab