OpenSSH two factor authentication combined with Kerberos / public key

I'm trying to implement two-factor authentication for OpenSSH. The environment is Centos 7 (kernel: 3.10.0-229.1.2.el7.x86_64) with OpenSSH_6.6.1p1, OpenSSL 1.0.1e-fips 11 Feb 2013. We have Active Directory (LDAP) + Kerberos deployed. The specification is as follows:

  • A user with an existing, valid Kerberos ticket must be asked only for the second factor
  • A user without an existing, valid Kerberos ticket must be asked for both his password and a second factor
  • Local users (no LDAP acc.) should be able to authenticate with their local passwords
  • The second factor must not be offered before the first one
  • Besides Kerberos, public key authentication should be also accepted as first factor if available
  • The feature should be able to be limited to a set of users - others just get let in with their passwords

For performing the second factor's authentication process, there is 3rd party a PAM module available that knows nothing about Kerberos. So here is what I did:

Put these lines into /etc/ssh/sshd_config:

# To enable PAM - this will make sshd use PAM with configuration /etc/pam.d/sshd
UsePam yes
ChallengeResponseAuthentication yes

# To enable Kerberos and public key authentication - it will let sshd use existing Kerberos tickets
GSSAPIAuthentication yes

# Enable public key authentication
PubkeyAuthentication yes

# Password validation should be done via the KDC
PasswordAuthentication yes
KerberosAuthentication yes
KerberosOrLocalPasswd yes

# Kerberos / Public Key + PAM
AuthenticationMethods gssapi-with-mic,keyboard-interactive:pam publickey,keyboard-interactive:pam password,keyboard-interactive:pam
# (only supported for OpenSSH 6.2 or higher)

The auth section of the PAM configuration for sshd (/etc/pam.d/sshd)

auth       [success=ignore default=1] pam_localuser.so
auth       substack     password-auth
auth       [success=1 default=ignore] pam_localuser.so
auth       required     pam_2fa.so [...some arguments...]
auth       include      postlogin

The module pam_2fa.so is responsible for prompting for and validating the second factor.

Now for Kerberos, this does almost everything I wanted to achieve. However, for local accounts, it results in two subsequent password prompts. This is my main problem here. This is because in this case the path "password,keyboard-interactive:pam" is used,as expected. (I need this auth. path so someone with a Kerberos account but without a valid ticket can get a ticket by entering the password then the OTP.) If I remove the password-auth substack completely from the PAM config, then Kerberos accounts remain working and local accounts remain not working. To me, it seems like the KerberosOrLocalPasswd yes statement gets ignored, because UsePAM yes is also present. However, sshd really keeps using KDC for password validation, because otherwise it would not work for LDAP accounts either.

So again, to further clarify what I wish to implement here is the pseudocode that described the desired authentication logic:

if gssapi_auth_ok(principal) or pubkey_auth_ok(pubkey):
  return second_factor_auth(user, read_otp())
else:
  if is_local_account(user):
    if local_passwd_auth(user, read_password()):
      return second_factor_auth(user, read_otp())
    else:
      return AUTH_ERR
  else:
    if krb5_auth(principal, read_password()):
      return second_factor_auth(user, read_otp())
    return AUTH_ERR

So my scenario I think is not too complex or ambitious in any way, but I still could not find a clear way to implement it despite I spent days researching and experimenting. Could you please help me find a solution?

Thank you very much in advance!


Solution 1:

I don't know of any way to do what you want with PAM without writing a new pam module. PAM is relatively complex and the way that sshd interacts with PAM and kerberos has been in my experience such that there is always an edge case that just doesn't work.

What you want may be possible, I just don't know how to do it. If you only need to secure ssh what I would suggest is using

ForceCommand  /path/to/2fa_executable 

in your sshd_config. This would allow you to implement the logic you want, the drawback is that the 2fa_executable needs to be setuid to read any 2fa secrets.

There's an example and code on the Duo Security website. You could use the duo setuid wrapper and whatever 2fa code you are using.

Duo Unix Configuration

Solution 2:

  • A user with an existing, valid Kerberos ticket must be asked only for the second factor.

This can't be achieved through PAM.

Successful key-based authentication performed against sshd bypasses the auth stack of PAM. This includes GSSAPI, which is a requirement for ticket based Kerberos authentication. It has no choice but to do this as PAM was simply not designed with this type of authentication in mind.

Setting UsePAM yes does the following:

  • ChallengeResponseAuthentication and PasswordAuthentication will hook the auth stack of PAM to validate the password. Any form of authentication other than these two methods will not touch the auth stack.
  • On successful authentication, the account stack of PAM will be called to determine if the authenticated user is permitted access. (always)
  • The session stack of PAM will be called to handle session setup tasks.

Summary: There is absolutely no way to get a two-factor auth prompt located within the auth stack to fire for someone who has authenticated with a GSSAPI or ssh key. You can use account and session stacks in conjunction with those authentication methods, but that's as far as PAM will go.