From 1c693f562c7f219b3cc339a190f86f713d8c77d9 Mon Sep 17 00:00:00 2001 From: Stephen Gelman Date: Tue, 8 Jan 2019 04:51:13 +0000 Subject: [PATCH] Add always_prompt configuration option to skip initial check for YubiKey As raised in #174, ldap_bind_as_user cannot be used if this module is set to get YubiKey+OTP because the initial ldap lookup fails (since the password is not set yet). `always_prompt` will stil the initial lookup, meaning that the user will be given the chance to enter their password. --- README | 7 ++++++ pam_yubico.8.txt | 3 +++ pam_yubico.c | 58 ++++++++++++++++++++++++++---------------------- 3 files changed, 42 insertions(+), 26 deletions(-) diff --git a/README b/README index 35b57da..c8e1b51 100644 --- a/README +++ b/README @@ -171,6 +171,13 @@ stacked modules password and will never prompt the user - if no password is available or the password is not appropriate, the user will be denied access. +always_prompt:: +If set, don't attempt to do a lookup to determine if the user has a +YubiKey configured but instead prompt for one no matter what. This +is useful in the case where ldap_bind_as_user is enabled but this +module is being used to read the user's password (in a YubiKey+OTP +auth scenario). + nullok:: If set, don't fail when there are no tokens declared for the user in the authorization mapping files or in LDAP. diff --git a/pam_yubico.8.txt b/pam_yubico.8.txt index de98d2e..9bc9abc 100644 --- a/pam_yubico.8.txt +++ b/pam_yubico.8.txt @@ -41,6 +41,9 @@ Before prompting the user for their password, the module first tries the previou *use_first_pass*:: Forces the module to use a previous stacked modules password and will never prompt the user - if no password is available or the password is not appropriate, the user will be denied access. +*always_prompt*:: +If set, don't attempt to do a lookup to determine if the user has a YubiKey configured but instead prompt for one no matter what. This is useful in the case where ldap_bind_as_user is enabled but this module is being used to read the user's password (in a YubiKey+OTP auth scenario). + *nullok*:: Don’t fail when there are no tokens declared for the user in the authorization mapping files or in LDAP. This can be used to make YubiKey authentication optional unless the user has associated tokens. diff --git a/pam_yubico.c b/pam_yubico.c index 1dbad63..ac8b621 100644 --- a/pam_yubico.c +++ b/pam_yubico.c @@ -111,6 +111,7 @@ struct cfg int verbose_otp; int try_first_pass; int use_first_pass; + int always_prompt; int nullok; int ldap_starttls; int ldap_bind_as_user; @@ -805,6 +806,8 @@ parse_cfg (int flags, int argc, const char **argv, struct cfg *cfg) cfg->try_first_pass = 1; if (strcmp (argv[i], "use_first_pass") == 0) cfg->use_first_pass = 1; + if (strcmp (argv[i], "always_prompt") == 0) + cfg->always_prompt = 1; if (strcmp (argv[i], "nullok") == 0) cfg->nullok = 1; if (strcmp (argv[i], "ldap_starttls") == 0) @@ -904,6 +907,7 @@ parse_cfg (int flags, int argc, const char **argv, struct cfg *cfg) DBG ("verbose_otp=%d", cfg->verbose_otp); DBG ("try_first_pass=%d", cfg->try_first_pass); DBG ("use_first_pass=%d", cfg->use_first_pass); + DBG ("always_prompt=%d", cfg->always_prompt); DBG ("nullok=%d", cfg->nullok); DBG ("ldap_starttls=%d", cfg->ldap_starttls); DBG ("ldap_bind_as_user=%d", cfg->ldap_bind_as_user); @@ -1080,34 +1084,36 @@ pam_sm_authenticate (pam_handle_t * pamh, /* check if the user has at least one associated token id */ /* we set otp_id to NULL so that no matches will ever be found * but AUTH_NO_TOKENS will be returned if there are no tokens for the user */ - if (cfg->ldapserver != NULL || cfg->ldap_uri != NULL) - valid_token = authorize_user_token_ldap (cfg, user, NULL, pamh); - else - valid_token = authorize_user_token (cfg, user, NULL, pamh); + if (!cfg->always_prompt) { + if (cfg->ldapserver != NULL || cfg->ldap_uri != NULL) + valid_token = authorize_user_token_ldap (cfg, user, NULL, pamh); + else + valid_token = authorize_user_token (cfg, user, NULL, pamh); - switch(valid_token) - { - case AUTH_ERROR: - DBG ("Internal error while looking for user tokens"); - retval = PAM_AUTHINFO_UNAVAIL; - goto done; - case AUTH_NOT_FOUND: - /* User has associated tokens, so continue */ - DBG ("Tokens found for user"); - break; - case AUTH_NO_TOKENS: - DBG ("No tokens found for user"); - if (cfg->nullok) { - retval = PAM_IGNORE; - } else { - retval = PAM_USER_UNKNOWN; + switch(valid_token) + { + case AUTH_ERROR: + DBG ("Internal error while looking for user tokens"); + retval = PAM_AUTHINFO_UNAVAIL; + goto done; + case AUTH_NOT_FOUND: + /* User has associated tokens, so continue */ + DBG ("Tokens found for user"); + break; + case AUTH_NO_TOKENS: + DBG ("No tokens found for user"); + if (cfg->nullok) { + retval = PAM_IGNORE; + } else { + retval = PAM_USER_UNKNOWN; + } + goto done; + default: + DBG ("Unhandled value while looking for user tokens"); + retval = PAM_AUTHINFO_UNAVAIL; + goto done; } - goto done; - default: - DBG ("Unhandled value while looking for user tokens"); - retval = PAM_AUTHINFO_UNAVAIL; - goto done; - } + } if (password == NULL) {