2
0
mirror of https://github.com/xcat2/confluent.git synced 2024-11-25 19:10:10 +00:00

Begin work to support complex PAM conversations

For example, TOTP setups need
more prompts, this will pass
the info to the client for the client to adjust.
This commit is contained in:
Jarrod Johnson 2021-06-23 16:31:42 -04:00
parent d86fc664e9
commit b8c9e9c535
3 changed files with 39 additions and 7 deletions

View File

@ -283,9 +283,14 @@ def check_user_passphrase(name, passphrase, operation=None, element=None, tenant
# pam_unix uses unix_chkpwd which reque
os.setuid(pwe.pw_uid)
usergood = pam.authenticate(user, passphrase, service=_pamservice)
except pam.PromptsNeeded:
os._exit(2)
finally:
os._exit(0 if usergood else 1)
usergood = os.waitpid(pid, 0)[1] == 0
usergood = os.waitpid(pid, 0)[1]
if (usergood >> 8) == 2:
pam.authenticate(user, passphrase, service=_pamservice)
usergood = usergood == 0
else:
# We are running as root, we don't need to fork in order to authenticate the
# user

View File

@ -307,7 +307,12 @@ def _authorize_request(env, operation):
return ('logout',)
name, passphrase = base64.b64decode(
env['HTTP_AUTHORIZATION'].replace('Basic ', '')).split(b':', 1)
authdata = auth.check_user_passphrase(name, passphrase, operation=operation, element=element)
try:
authdata = auth.check_user_passphrase(name, passphrase, operation=operation, element=element)
except Exception as e:
if hasattr(e, 'prompts'):
return {'code': 403, 'prompts': e.prompts}
raise
if authdata is False:
return {'code': 403}
elif not authdata:
@ -519,7 +524,14 @@ def resourcehandler_backend(env, start_response):
return
if authorized['code'] == 403:
start_response('403 Forbidden', badauth)
yield 'Forbidden'
response = {'result': 'Forbidden'}
if 'prompts' in authorized:
response['prompts'] = []
for prompt in authorized['prompts']:
if not isinstance(prompt, str):
prompt = prompt.decode('utf8')
response['prompts'].append(prompt)
yield json.dumps(response)
return
if authorized['code'] != 200:
raise Exception("Unrecognized code from auth engine")

View File

@ -103,6 +103,12 @@ pam_authenticate = libpam.pam_authenticate
pam_authenticate.restype = c_int
pam_authenticate.argtypes = [PamHandle, c_int]
class PromptsNeeded(Exception):
def __init__(self, prompts):
self.prompts = prompts
class pam():
code = 0
reason = None
@ -110,7 +116,7 @@ class pam():
def __init__(self):
pass
def authenticate(self, username, password, service='login', encoding='utf-8', resetcreds=True):
def authenticate(self, username, password, service='login', encoding='utf-8', resetcreds=True, answers=None):
"""username and password authentication for the given service.
Returns True for success, or False for failure.
@ -142,8 +148,15 @@ class pam():
p_response[0] = response
for i in range(n_messages):
if messages[i].contents.msg_style == PAM_PROMPT_ECHO_OFF:
dst = calloc(len(password)+1, sizeof(c_char))
memmove(dst, cpassword, len(password))
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)
else:
currpassword = password
currcpassword = cpassword
dst = calloc(len(currpassword)+1, sizeof(c_char))
memmove(dst, currcpassword, len(currpassword))
response[i].resp = dst
response[i].resp_retcode = 0
return 0
@ -169,6 +182,7 @@ class pam():
# 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()
conv = PamConv(my_conv, 0)
@ -198,7 +212,8 @@ class pam():
if hasattr(libpam, 'pam_end'):
pam_end(handle, retval)
if answers is None and len(prompts) > 1 and not auth_success:
raise PromptsNeeded(prompts)
return auth_success