From 34b30a1bd8005568de0e00e8c8f21c7979064280 Mon Sep 17 00:00:00 2001 From: Jarrod Johnson Date: Wed, 14 Nov 2018 14:52:03 -0500 Subject: [PATCH] Rework password authentication Have the cache operate against the sha256 of the passphrase rather than the passhprase directly (mitigate presence of password in memory). Pull PAM into the cache behavior to accelerate PAM based usage. Rearrange things so that both built in confluent and PAM passwords can be used. --- confluent_server/confluent/auth.py | 89 +++++++++++++++--------------- 1 file changed, 46 insertions(+), 43 deletions(-) diff --git a/confluent_server/confluent/auth.py b/confluent_server/confluent/auth.py index 7ced4cdd..c3b01fc8 100644 --- a/confluent_server/confluent/auth.py +++ b/confluent_server/confluent/auth.py @@ -158,20 +158,13 @@ def check_user_passphrase(name, passphrase, element=None, tenant=False): # but here there's no need for that eventlet.sleep(0.5) credobj = Credentials(user, passphrase) - try: - pammy = PAM.pam() - pammy.start(_pamservice, user, credobj.pam_conv) - pammy.authenticate() - pammy.acct_mgmt() - del pammy - return authorize(user, element, tenant, skipuserobj=False) - except NameError: - pass - except PAM.error: - if credobj.haspam: - return None + cfm = configmanager.ConfigManager(tenant, username=user) + ucfg = cfm.get_user(user) + if ucfg is None: + eventlet.sleep(0.05) + return None if (user, tenant) in _passcache: - if passphrase == _passcache[(user, tenant)]: + if hashlib.sha256(passphrase).digest() == _passcache[(user, tenant)]: return authorize(user, element, tenant) else: # In case of someone trying to guess, @@ -179,38 +172,48 @@ def check_user_passphrase(name, passphrase, element=None, tenant=False): # invalidate cache and force the slower check del _passcache[(user, tenant)] return None - cfm = configmanager.ConfigManager(tenant, username=user) - ucfg = cfm.get_user(user) - if ucfg is None or 'cryptpass' not in ucfg: - eventlet.sleep(0.05) # stall even on test for existence of a username - return None - _passchecking[(user, tenant)] = True - # TODO(jbjohnso): WORKERPOOL - # PBKDF2 is, by design, cpu intensive - # throw it at the worker pool when implemented - # maybe a distinct worker pool, wondering about starving out non-auth stuff - salt, crypt = ucfg['cryptpass'] - # execute inside tpool to get greenthreads to give it a special thread - # world - #TODO(jbjohnso): util function to generically offload a call - #such a beast could be passed into pyghmi as a way for pyghmi to - #magically get offload of the crypto functions without having - #to explicitly get into the eventlet tpool game - global authworkers - global authcleaner - if authworkers is None: - authworkers = multiprocessing.Pool(processes=1) - else: - authcleaner.cancel() - authcleaner = eventlet.spawn_after(30, _clean_authworkers) - crypted = eventlet.tpool.execute(_do_pbkdf, passphrase, salt) - del _passchecking[(user, tenant)] - eventlet.sleep(0.05) # either way, we want to stall so that client can't + if 'cryptpass' in ucfg: + _passchecking[(user, tenant)] = True + # TODO(jbjohnso): WORKERPOOL + # PBKDF2 is, by design, cpu intensive + # throw it at the worker pool when implemented + # maybe a distinct worker pool, wondering about starving out non-auth stuff + salt, crypt = ucfg['cryptpass'] + # execute inside tpool to get greenthreads to give it a special thread + # world + # TODO(jbjohnso): util function to generically offload a call + # such a beast could be passed into pyghmi as a way for pyghmi to + # magically get offload of the crypto functions without having + # to explicitly get into the eventlet tpool game + global authworkers + global authcleaner + if authworkers is None: + authworkers = multiprocessing.Pool(processes=1) + else: + authcleaner.cancel() + authcleaner = eventlet.spawn_after(30, _clean_authworkers) + crypted = eventlet.tpool.execute(_do_pbkdf, passphrase, salt) + del _passchecking[(user, tenant)] + eventlet.sleep( + 0.05) # either way, we want to stall so that client can't # determine failure because there is a delay, valid response will # delay as well - if crypt == crypted: - _passcache[(user, tenant)] = passphrase - return authorize(user, element, tenant) + if crypt == crypted: + _passcache[(user, tenant)] = hashlib.sha256(passphrase).digest() + return authorize(user, element, tenant) + try: + pammy = PAM.pam() + pammy.start(_pamservice, user, credobj.pam_conv) + pammy.authenticate() + pammy.acct_mgmt() + del pammy + _passcache[(user, tenant)] = hashlib.sha256(passphrase).digest() + return authorize(user, element, tenant, skipuserobj=False) + except NameError: + pass + except PAM.error: + pass + eventlet.sleep(0.05) # stall even on test for existence of a username return None