2
0
mirror of https://github.com/xcat2/confluent.git synced 2024-11-21 17:11:58 +00:00

Implement support for additional pam prompts

For example, if PAM has OTP, then support it.
This commit is contained in:
Jarrod Johnson 2021-06-25 17:26:32 -04:00
parent f2eba22b9b
commit f830514d10
4 changed files with 43 additions and 17 deletions

View File

@ -221,11 +221,14 @@ def check_user_passphrase(name, passphrase, operation=None, element=None, tenant
if ucfg is None:
eventlet.sleep(0.05)
return None
bpassphrase = None
if isinstance(passphrase, dict) and len(passphrase) == 1:
passphrase = list(passphrase.values())[0]
if isinstance(passphrase, bytes):
bpassphrase = passphrase
else:
elif not isinstance(passphrase, dict):
bpassphrase = passphrase.encode('utf8')
if (user, tenant) in _passcache:
if (user, tenant) in _passcache and bpassphrase:
if hashlib.sha256(bpassphrase).digest() == _passcache[(user, tenant)]:
return authorize(user, element, tenant, operation=operation)
else:
@ -233,7 +236,7 @@ def check_user_passphrase(name, passphrase, operation=None, element=None, tenant
# while someone is legitimately logged in
# invalidate cache and force the slower check
del _passcache[(user, tenant)]
if 'cryptpass' in ucfg:
if 'cryptpass' in ucfg and bpassphrase:
_passchecking[(user, tenant)] = True
# TODO(jbjohnso): WORKERPOOL
# PBKDF2 is, by design, cpu intensive
@ -296,7 +299,8 @@ def check_user_passphrase(name, passphrase, operation=None, element=None, tenant
# user
usergood = pam.authenticate(user, passphrase, service=_pamservice)
if usergood:
_passcache[(user, tenant)] = hashlib.sha256(bpassphrase).digest()
if bpassphrase:
_passcache[(user, tenant)] = hashlib.sha256(bpassphrase).digest()
return authorize(user, element, tenant, operation, skipuserobj=False)
eventlet.sleep(0.05) # stall even on test for existence of a username
return None

View File

@ -305,8 +305,13 @@ def _authorize_request(env, operation):
# of a CSRF
return {'code': 401}
return ('logout',)
name, passphrase = base64.b64decode(
env['HTTP_AUTHORIZATION'].replace('Basic ', '')).split(b':', 1)
if env['HTTP_AUTHORIZATION'].startswith('MultiBasic '):
name, passphrase = base64.b64decode(
env['HTTP_AUTHORIZATION'].replace('MultiBasic ', '')).split(b':', 1)
passphrase = json.loads(passphrase)
else:
name, passphrase = base64.b64decode(
env['HTTP_AUTHORIZATION'].replace('Basic ', '')).split(b':', 1)
try:
authdata = auth.check_user_passphrase(name, passphrase, operation=operation, element=element)
except Exception as e:

View File

@ -116,7 +116,7 @@ class pam():
def __init__(self):
pass
def authenticate(self, username, password, service='login', encoding='utf-8', resetcreds=True, answers=None):
def authenticate(self, username, password, service='login', encoding='utf-8', resetcreds=True):
"""username and password authentication for the given service.
Returns True for success, or False for failure.
@ -142,19 +142,38 @@ class pam():
def my_conv(n_messages, messages, p_response, app_data):
"""Simple conversation function that responds to any
prompt where the echo is off with the supplied password"""
for i in range(n_messages):
if messages[i].contents.msg_style == PAM_PROMPT_ECHO_OFF:
currprompt = messages[i].contents.msg.decode('utf8').strip()
prompts.add(currprompt)
for i in range(n_messages):
if messages[i].contents.msg_style == PAM_PROMPT_ECHO_OFF:
currprompt = messages[i].contents.msg.decode('utf8').strip()
if isinstance(password, dict):
if currprompt in password:
continue
elif len(prompts) > 1:
return 19 # PAM_CONV_ERR
else:
if len(prompts) > 1:
return 19 # PAM_CONV_ERR
# Create an array of n_messages response objects
addr = calloc(n_messages, sizeof(PamResponse))
response = cast(addr, POINTER(PamResponse))
p_response[0] = response
for i in range(n_messages):
if messages[i].contents.msg_style == PAM_PROMPT_ECHO_OFF:
prompts.add(messages[i].contents.msg)
if answers and messages[i].contents.msg in answers:
currpassword = answers[messages[i].contents.msg]
currcpassword = c_char_p(currpassword)
currprompt = messages[i].contents.msg.decode('utf8').strip()
if isinstance(password, dict):
if currprompt in password:
currpassword = password[currprompt]
currcpassword = c_char_p(currpassword.encode('utf8'))
elif len(password) == 1:
currpassword = list(password.values())[0]
currcpassword = c_char_p(currpassword.encode('utf8'))
else:
currpassword = password
currcpassword = cpassword
currcpassword = c_char_p(password.encode('utf8'))
dst = calloc(len(currpassword)+1, sizeof(c_char))
memmove(dst, currcpassword, len(currpassword))
response[i].resp = dst
@ -164,7 +183,6 @@ class pam():
# python3 ctypes prefers bytes
if sys.version_info >= (3,):
if isinstance(username, str): username = username.encode(encoding)
if isinstance(password, str): password = password.encode(encoding)
if isinstance(service, str): service = service.encode(encoding)
else:
if isinstance(username, unicode):
@ -174,14 +192,13 @@ class pam():
if isinstance(service, unicode):
service = service.encode(encoding)
if b'\x00' in username or b'\x00' in password or b'\x00' in service:
if b'\x00' in username or b'\x00' in service:
self.code = 4 # PAM_SYSTEM_ERR in Linux-PAM
self.reason = 'strings may not contain NUL'
return False
# do this up front so we can safely throw an exception if there's
# anything wrong with it
cpassword = c_char_p(password)
prompts = set([])
handle = PamHandle()
@ -212,7 +229,7 @@ class pam():
if hasattr(libpam, 'pam_end'):
pam_end(handle, retval)
if answers is None and len(prompts) > 1 and not auth_success:
if (not isinstance(password, dict)) and len(prompts) > 1 and not auth_success:
raise PromptsNeeded(prompts)
return auth_success

View File

@ -1,4 +1,4 @@
dracut_install /usr/bin/mktemp
dracut_install mktemp
dracut_install /usr/lib64/libtss2-tcti-device.so.*
dracut_install tpm2_create tpm2_pcrread tpm2_createpolicy tpm2_createprimary
dracut_install tpm2_load tpm2_unseal tpm2_getcap tpm2_evictcontrol