Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- Hi,
- I am trying to setup OpenSSH for 2 factor authentication.
- Specifically, I want users to be able to log in with either:
- public key + liboath token
- or
- password + liboath token
- as well as public key only for some privileged internal hosts
- and public key or password for user/host pairs that recently logged in succesfully.
- I managed to do this, but it's very hacky. I think openssh needs to be modified to facilitate this use case better.
- My Solution:
- I set in sshd_config:
- PasswordAuthentication yes
- ChallengeResponseAuthentication yes
- PubkeyAuthentication yes
- UsePAM yes
- AuthenticationMethods publickey,keyboard-interactive password,keyboard-interactive
- Match Address [privileged address/range]
- AuthenticaionMethods publickey
- Now I can set up /etc/pam.d/sshd in such a way, it asks and checks the liboath token using pam_oath.so
- The Problem:
- Both PasswordAuthentication and ChallengeResponseAuthentication use the same PAM service in /etc/pam.d/sshd for authentication. The only difference is that PasswordAuthentication does not allow a challenge response prompt via pam_conv, and "dumb" provides a password, while ChallengeResponseAuthentication allows for arbitrary prompts.
- As such, I have to check for both password and oath token in the same pam service configuration
- Wrong solution 1:
- I could have either authentication method satisfy PAM,
- auth [default=ignore] pam_echo.so #ask for token
- auth sufficient pam_unix.so nullok_secure try_first_pass
- auth sufficient pam_oath.so use_first_pass
- but then one could enter the password twice, or the oath token twice even if asked for the other, and bypass the security check
- Wrong solution 2:
- I could ask pam to satisfy both and get rid of password authentication, the challenge response will then ask for both password AND oath token with separate prompts:
- auth [default=ignore] pam_echo.so #ask for password
- auth required pam_unix.so nullok_secure try_first_pass
- auth [default=ignore] pam_echo.so #ask for token
- auth sufficient pam_oath.so
- but that breaks the keybased authentication, as it would ask for all 3: key, password AND token
- Hacky Solution:
- I can make a custom script or pam module that somehow guesses which case is present and enforces the correct behavior, but pam is really not flexible enough to do this gracefully in the same service config:
- My choice was to use pam_script.so This third party module forks a process which runs a shell script with password/token, host, service name, ... in env variables. It has the advantage that it is already written, but the disadvantage that it is limited in its I/O. It can indicate success or failure through return code but is unable to access the pam_conv conversation and as such cannot actively do challenge response dialogs.
- auth [default=ignore] pam_echo.so #ask for token
- auth [success=ignore default=die] pam_script.so use_first_pass # runs an auth check script.
- auth sufficient pam_unix.so nullok_secure try_first_pass
- auth sufficient pam_oath.so use_first_pass
- The check script needs to distinguish two valid cases:
- Case 1:
- PAM was called via PasswordAuthentication.
- The supplied password is a login password
- return success. pam_unix will then validate the password, while pam_oath will fail
- Case 2:
- PAM was called via ChallengeResponseAuthentication
- The supplied password is a syntactically correct OATH token.
- return success. pam_oath will then validate the token, while pam_unix will fail
- Else:
- return failure and cause authentication to fail hard.
- Identifying the password is straightforward, as OATH tokens are too short to be accepted as passwords and can be identified with regular expression.
- The tricky part is identifieing if sshd called pam from Password or ChallengeResponse authentication.
- If I were to write my own pam module, I could try to initiate my own challenge response (which would hopefully fail for PasswordAuthentication) but this is a callback function I cannot call from a shell script in a forked process environment
- The Hack:
- Although ChallengeResponse authentication forks a subprocess for pam authentication, this process has a socket open pair open ( auth-pam.c line 798 ctxt->psock & csock ) for communication with the sshd mother process (used for challenge and return)
- When pam-script.so forks the calling PAM authentication process, it does not close the forked file descriptors.before starting the script. As such the open file descriptios of the calling sshd authentication process are exposed through /proc/self/fd/*
- For ChallengeResponse authentication the above socket can be found in /proc/self/fd, while with PasswordAuthentication this is absent.
- How do do this properly:
- Identifying the context in which PAM is called from SSHD from within PAM is - while possible - very very hacky, unclean, and possibly insecure, and might stop working with any update of openssh as it doesn't rely on any proper API
- The best way how sshd could enforce a different PAM context would be by using different service names in pam_start()
- currently this is hardcoded to argv[0] of the sshd process (usually "sshd"). There should be at least 1 config file parameter in sshd_config to specify PAMServiceName (which could then be chosen within different Match directived)
- PAMServiceName should obviously default to argv[0] for backwards compatibility
- but to solve the described usecase above, PAM needs to be able to distinguish between ChallengeResponse and Password (both using PAM)
- I therefore suggest two more parameters for sshd_config
- PasswordPAMServiceName
- ChallengeresponsePAMServiceName
- both would default to the value of PAMServiceName if unset.
- is this feasible?
Add Comment
Please, Sign In to add comment