From bb731b875dbb78980c7e2a9d0a97cac54fc5faa2 Mon Sep 17 00:00:00 2001 From: Sumit Bose Date: Mon, 22 Feb 2010 09:10:32 +0100 Subject: [PATCH] Handle expired passwords like other PAM modules So far we handled expired password during authentication. Other PAM modules typically detect expired password during account management and return PAM_NEW_AUTHTOK_REQD if the password is expired and should be changed. The PAM library then calls the change password routines. To meet these standards pam_sss is change accordingly. As a result it is now possible to update an expired password via ssh if sssd is running with PasswordAuthentication=yes. One drawback due to limitations of PAM is that the user now has to type his current password again before setting a new one. --- src/providers/krb5/krb5_child.c | 2 +- src/providers/ldap/ldap_auth.c | 2 +- src/sss_client/pam_sss.c | 49 ++++++++++++++++++++++++-------------- src/sss_client/sss_cli.h | 21 ++++++++++++++++- 4 files changed, 53 insertions(+), 21 deletions(-) diff --git a/src/providers/krb5/krb5_child.c b/src/providers/krb5/krb5_child.c index d1cc53f..08df598 100644 --- a/src/providers/krb5/krb5_child.c +++ b/src/providers/krb5/krb5_child.c @@ -688,7 +688,7 @@ static errno_t tgt_req_child(int fd, struct krb5_req *kr) pam_status = PAM_AUTHINFO_UNAVAIL; break; case KRB5KDC_ERR_KEY_EXP: - pam_status = PAM_AUTHTOK_EXPIRED; + pam_status = PAM_NEW_AUTHTOK_REQD; break; case KRB5KDC_ERR_PREAUTH_FAILED: pam_status = PAM_CRED_ERR; diff --git a/src/providers/ldap/ldap_auth.c b/src/providers/ldap/ldap_auth.c index cfe8adb..ba1136b 100644 --- a/src/providers/ldap/ldap_auth.c +++ b/src/providers/ldap/ldap_auth.c @@ -983,7 +983,7 @@ static void sdap_pam_auth_done(struct tevent_req *req) state->pd->pam_status = PAM_ACCT_EXPIRED; break; case SDAP_AUTH_PW_EXPIRED: - state->pd->pam_status = PAM_AUTHTOK_EXPIRED; + state->pd->pam_status = PAM_NEW_AUTHTOK_REQD; break; default: state->pd->pam_status = PAM_SYSTEM_ERR; diff --git a/src/sss_client/pam_sss.c b/src/sss_client/pam_sss.c index a13563d..010ca4e 100644 --- a/src/sss_client/pam_sss.c +++ b/src/sss_client/pam_sss.c @@ -46,6 +46,8 @@ #define FLAGS_FORWARD_PASS (1 << 1) #define FLAGS_USE_AUTHTOK (1 << 2) +#define PWEXP_FLAG "pam_sss:password_expired_flag" + struct pam_items { const char* pam_service; const char* pam_user; @@ -101,6 +103,11 @@ static void logger(pam_handle_t *pamh, int level, const char *fmt, ...) { va_end(ap); } +static void free_exp_data(pam_handle_t *pamh, void *ptr, int err) +{ + free(ptr); + ptr = NULL; +} static size_t add_authtok_item(enum pam_item_type type, enum sss_authtok_type authtok_type, @@ -847,13 +854,13 @@ done: return pam_status; } -static int prompt_password(pam_handle_t *pamh, struct pam_items *pi) +static int prompt_password(pam_handle_t *pamh, struct pam_items *pi, + const char *prompt) { int ret; char *answer = NULL; - ret = do_pam_conversation(pamh, PAM_PROMPT_ECHO_OFF, _("Password: "), - NULL, &answer); + ret = do_pam_conversation(pamh, PAM_PROMPT_ECHO_OFF, prompt, NULL, &answer); if (ret != PAM_SUCCESS) { D(("do_pam_conversation failed.")); return ret; @@ -943,7 +950,7 @@ static int get_authtok_for_authentication(pam_handle_t *pamh, } pi->pam_authtok_size = strlen(pi->pam_authtok); } else { - ret = prompt_password(pamh, pi); + ret = prompt_password(pamh, pi, _("Password: ")); if (ret != PAM_SUCCESS) { D(("failed to get password from user")); return ret; @@ -973,7 +980,7 @@ static int get_authtok_for_password_change(pam_handle_t *pamh, * pam_sss work e.g. with pam_cracklib */ if (pam_flags & PAM_PRELIM_CHECK) { if (getuid() != 0 && !(flags & FLAGS_USE_FIRST_PASS)) { - ret = prompt_password(pamh, pi); + ret = prompt_password(pamh, pi, _("Current Password: ")); if (ret != PAM_SUCCESS) { D(("failed to get password from user")); return ret; @@ -1040,6 +1047,7 @@ static int pam_sss(enum sss_cli_command task, pam_handle_t *pamh, int ret; struct pam_items pi; uint32_t flags = 0; + int *exp_data; bindtextdomain(PACKAGE, LOCALEDIR); @@ -1053,7 +1061,6 @@ static int pam_sss(enum sss_cli_command task, pam_handle_t *pamh, return ret; } - switch(task) { case SSS_PAM_AUTHENTICATE: ret = get_authtok_for_authentication(pamh, &pi, flags); @@ -1086,24 +1093,30 @@ static int pam_sss(enum sss_cli_command task, pam_handle_t *pamh, ret = send_and_receive(pamh, &pi, task); - if (ret == PAM_AUTHTOK_EXPIRED && task == SSS_PAM_AUTHENTICATE) { +/* We allow sssd to send the return code PAM_NEW_AUTHTOK_REQD during + * authentication, see sss_cli.h for details */ + if (ret == PAM_NEW_AUTHTOK_REQD && task == SSS_PAM_AUTHENTICATE) { D(("Authtoken expired, trying to change it")); - ret = do_pam_conversation(pamh, PAM_ERROR_MSG, - _("Password has expired."), NULL, NULL); - if (ret != PAM_SUCCESS) { - D(("do_pam_conversation failed.")); - return PAM_SYSTEM_ERR; - } - pi.pamstack_oldauthtok = pi.pam_authtok; - ret = get_authtok_for_password_change(pamh, &pi, flags, pam_flags); + exp_data = malloc(sizeof(int)); + if (exp_data == NULL) { + D(("malloc failed.")); + return PAM_BUF_ERR; + } + *exp_data = 1; + ret = pam_set_data(pamh, PWEXP_FLAG, exp_data, free_exp_data); if (ret != PAM_SUCCESS) { - D(("failed to get tokens for password change: %s", - pam_strerror(pamh, ret))); + D(("pam_set_data failed.")); return ret; } - ret = send_and_receive(pamh, &pi, SSS_PAM_CHAUTHTOK); + return PAM_SUCCESS; + } + + if (ret == PAM_SUCCESS && task == SSS_PAM_ACCT_MGMT && + pam_get_data(pamh, PWEXP_FLAG, (const void **) &exp_data) == + PAM_SUCCESS) { + return PAM_NEW_AUTHTOK_REQD; } overwrite_and_free_authtoks(&pi); diff --git a/src/sss_client/sss_cli.h b/src/sss_client/sss_cli.h index 522e079..1953db2 100644 --- a/src/sss_client/sss_cli.h +++ b/src/sss_client/sss_cli.h @@ -144,7 +144,26 @@ enum sss_cli_command { /* PAM related calls */ SSS_PAM_AUTHENTICATE = 0x00F1, /**< see pam_sm_authenticate(3) for - * details */ + * details. + * + * Additionally we allow sssd to send + * the return code PAM_NEW_AUTHTOK_REQD + * during authentication if the + * authentication was successful but + * the authentication token is expired. + * To meet the standards of libpam we + * return PAM_SUCCESS for + * authentication and set a flag so + * that the account management module + * can return PAM_NEW_AUTHTOK_REQD if + * sssd return success for account + * management. We do this to reduce the + * communication with external servers, + * because there are cases, e.g. + * Kerberos authentication, where the + * information that the password is + * expired is already available during + * authentication. */ SSS_PAM_SETCRED = 0x00F2, /**< see pam_sm_setcred(3) for * details */ SSS_PAM_ACCT_MGMT = 0x00F3, /**< see pam_sm_acct_mgmt(3) for -- 1.6.6