Commit e5842c91 authored by Antoine Millet's avatar Antoine Millet
Browse files

Implementing hashed password storage (compatible with openldap).

parent 56caa838
Loading
Loading
Loading
Loading
+80 −3
Original line number Diff line number Diff line
@@ -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