mirror of
https://github.com/xcat2/confluent.git
synced 2024-11-22 09:32:21 +00:00
Rework MFA handling
Avoid calling PAM in the parent process, as this seems to cause problems with some PAM configurations.
This commit is contained in:
parent
f830514d10
commit
9c43dbff47
@ -26,6 +26,7 @@ import Cryptodome.Protocol.KDF as KDF
|
||||
from fnmatch import fnmatch
|
||||
import hashlib
|
||||
import hmac
|
||||
import msgpack
|
||||
import multiprocessing
|
||||
import os
|
||||
import pwd
|
||||
@ -100,6 +101,10 @@ _deniedbyrole = {
|
||||
}
|
||||
|
||||
|
||||
class PromptsNeeded(Exception):
|
||||
def __init__(self, prompts):
|
||||
self.prompts = prompts
|
||||
|
||||
def _get_usertenant(name, tenant=False):
|
||||
"""_get_usertenant
|
||||
|
||||
@ -278,22 +283,34 @@ def check_user_passphrase(name, passphrase, operation=None, element=None, tenant
|
||||
# to let a non-0 user check anothers password.
|
||||
# We will fork and the child will assume elevated privilege to
|
||||
# get unix_chkpwd helper to enable checking /etc/shadow
|
||||
getprompt, sendprompt = os.pipe()
|
||||
getprompt, sendprompt = os.fdopen(getprompt, 'rb', 0), os.fdopen(sendprompt, 'wb', 0)
|
||||
pid = os.fork()
|
||||
if not pid:
|
||||
usergood = False
|
||||
try:
|
||||
getprompt.close()
|
||||
# we change to the uid we are trying to authenticate as, because
|
||||
# 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)
|
||||
pa = pam.pam()
|
||||
usergood = pa.authenticate(user, passphrase, service=_pamservice)
|
||||
if (not usergood and len(pa.prompts) > 1 and
|
||||
(not isinstance(passphrase, dict) or
|
||||
(set(passphrase) - pa.prompts))):
|
||||
sendprompt.write(msgpack.packb(list(pa.prompts)))
|
||||
sendprompt.close()
|
||||
os._exit(2)
|
||||
finally:
|
||||
os._exit(0 if usergood else 1)
|
||||
sendprompt.close()
|
||||
usergood = os.waitpid(pid, 0)[1]
|
||||
if (usergood >> 8) == 2:
|
||||
pam.authenticate(user, passphrase, service=_pamservice)
|
||||
prompts = getprompt.read()
|
||||
if (prompts):
|
||||
raise PromptsNeeded(msgpack.unpackb(prompts))
|
||||
usergood = usergood == 0
|
||||
getprompt.close()
|
||||
else:
|
||||
# We are running as root, we don't need to fork in order to authenticate the
|
||||
# user
|
||||
|
@ -104,17 +104,12 @@ 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
|
||||
|
||||
def __init__(self):
|
||||
pass
|
||||
self.prompts = set([])
|
||||
|
||||
def authenticate(self, username, password, service='login', encoding='utf-8', resetcreds=True):
|
||||
"""username and password authentication for the given service.
|
||||
@ -145,17 +140,17 @@ class pam():
|
||||
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)
|
||||
self.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:
|
||||
elif len(self.prompts) > 1:
|
||||
return 19 # PAM_CONV_ERR
|
||||
else:
|
||||
if len(prompts) > 1:
|
||||
if len(self.prompts) > 1:
|
||||
return 19 # PAM_CONV_ERR
|
||||
# Create an array of n_messages response objects
|
||||
addr = calloc(n_messages, sizeof(PamResponse))
|
||||
@ -197,10 +192,6 @@ class 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
|
||||
prompts = set([])
|
||||
|
||||
handle = PamHandle()
|
||||
conv = PamConv(my_conv, 0)
|
||||
retval = pam_start(service, username, byref(conv), byref(handle))
|
||||
@ -229,8 +220,6 @@ class pam():
|
||||
|
||||
if hasattr(libpam, 'pam_end'):
|
||||
pam_end(handle, retval)
|
||||
if (not isinstance(password, dict)) and len(prompts) > 1 and not auth_success:
|
||||
raise PromptsNeeded(prompts)
|
||||
return auth_success
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user