Hi, with the three attached patches pam_sss can handle expired kerberos passwords: - 0001: kerberos provider returns PAM_AUTHTOK_EXPIRED if KDC returns KRB5KDC_ERR_KEY_EXP - 0002: some refactoring of pam_sss - 0003: query the user for a new password if sssd returns PAM_AUTHTOK_EXPIRED
All this happens during the pam authentication phase and not as often seen during the pam account management phase. For this reason I used PAM_AUTHTOK_EXPIRED instead of PAM_NEW_AUTHTOK_REQD, which is used by pam_sm_acct_mgmt(). I have two questions about the user experience: - currently PAM_AUTHTOK_EXPIRED is returned if the password is expired regardless of the supplied password is correct or not. Would it be better to return a different error if the password is wrong? - currently the pam_sss only asks the new password, because the current/old password is already known. Typically pam modules are asking for the current password for a second time (because the password is not know anymore) and the for the new one. I think this behaviour if often irritation people. Which version shall we use? bye, Sumit
>From bc5f929826a656562a9fa2b8f8d82da97604ec9c Mon Sep 17 00:00:00 2001 From: Sumit Bose <sb...@redhat.com> Date: Fri, 2 Oct 2009 13:50:20 +0200 Subject: [PATCH 1/3] handle expired password during authentication --- server/providers/krb5/krb5_child.c | 12 ++++++++++-- 1 files changed, 10 insertions(+), 2 deletions(-) diff --git a/server/providers/krb5/krb5_child.c b/server/providers/krb5/krb5_child.c index 6f69840..ef38d48 100644 --- a/server/providers/krb5/krb5_child.c +++ b/server/providers/krb5/krb5_child.c @@ -359,14 +359,22 @@ static errno_t tgt_req_child(int fd, struct krb5_req *kr) } kerr = get_and_save_tgt(kr, pass_str); + memset(pass_str, 0, kr->pd->authtok_size); talloc_zfree(pass_str); memset(kr->pd->authtok, 0, kr->pd->authtok_size); if (kerr != 0) { KRB5_DEBUG(1, kerr); - if (kerr == KRB5_KDC_UNREACH) { - pam_status = PAM_AUTHINFO_UNAVAIL; + switch (kerr) { + case KRB5_KDC_UNREACH: + pam_status = PAM_AUTHINFO_UNAVAIL; + break; + case KRB5KDC_ERR_KEY_EXP: + pam_status = PAM_AUTHTOK_EXPIRED; + break; + default: + pam_status = PAM_SYSTEM_ERR; } } -- 1.6.2.5
>From b0b64abda4b6e106bc2c5d664510814ab49fa072 Mon Sep 17 00:00:00 2001 From: Sumit Bose <sb...@redhat.com> Date: Fri, 2 Oct 2009 16:03:02 +0200 Subject: [PATCH 2/3] move password handling into subroutines --- sss_client/pam_sss.c | 188 +++++++++++++++++++++++++++++++------------------- 1 files changed, 117 insertions(+), 71 deletions(-) diff --git a/sss_client/pam_sss.c b/sss_client/pam_sss.c index 9a1d441..eec25ab 100644 --- a/sss_client/pam_sss.c +++ b/sss_client/pam_sss.c @@ -651,6 +651,111 @@ static void eval_argv(pam_handle_t *pamh, int argc, const char **argv, return; } +static int get_authtok_for_authentication(pam_handle_t *pamh, + struct pam_items *pi, + uint32_t flags) +{ + int ret; + + if (flags & FLAGS_USE_FIRST_PASS) { + pi->pam_authtok_type = SSS_AUTHTOK_TYPE_PASSWORD; + pi->pam_authtok = strdup(pi->pamstack_authtok); + if (pi->pam_authtok == NULL) { + D(("option use_first_pass set, but no password found")); + return PAM_BUF_ERR; + } + pi->pam_authtok_size = strlen(pi->pam_authtok); + } else { + ret = prompt_password(pamh, pi); + if (ret != PAM_SUCCESS) { + D(("failed to get password from user")); + return ret; + } + + if (flags & FLAGS_FORWARD_PASS) { + ret = pam_set_item(pamh, PAM_AUTHTOK, pi->pam_authtok); + if (ret != PAM_SUCCESS) { + D(("Failed to set PAM_AUTHTOK [%s], " + "authtok may not be available for other modules", + pam_strerror(pamh,ret))); + } + } + } + + return PAM_SUCCESS; +} + +static int get_authtok_for_password_change(pam_handle_t *pamh, + struct pam_items *pi, + uint32_t flags, + int pam_flags) +{ + int ret; + + /* we query for the old password during PAM_PRELIM_CHECK to make + * 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); + if (ret != PAM_SUCCESS) { + D(("failed to get password from user")); + return ret; + } + + ret = pam_set_item(pamh, PAM_OLDAUTHTOK, pi->pam_authtok); + if (ret != PAM_SUCCESS) { + D(("Failed to set PAM_OLDAUTHTOK [%s], " + "oldauthtok may not be available", + pam_strerror(pamh,ret))); + return ret; + } + } + + return PAM_SUCCESS; + } + + if (getuid() != 0) { + pi->pam_authtok_type = SSS_AUTHTOK_TYPE_PASSWORD; + pi->pam_authtok = strdup(pi->pamstack_oldauthtok); + if (pi->pam_authtok == NULL) { + D(("no password found for chauthtok")); + return PAM_BUF_ERR; + } + pi->pam_authtok_size = strlen(pi->pam_authtok); + } else { + pi->pam_authtok_type = SSS_AUTHTOK_TYPE_EMPTY; + pi->pam_authtok = NULL; + pi->pam_authtok_size = 0; + } + + if (flags & FLAGS_USE_AUTHTOK) { + pi->pam_newauthtok_type = SSS_AUTHTOK_TYPE_PASSWORD; + pi->pam_newauthtok = strdup(pi->pamstack_authtok); + if (pi->pam_newauthtok == NULL) { + D(("option use_authtok set, but no new password found")); + return PAM_BUF_ERR; + } + pi->pam_newauthtok_size = strlen(pi->pam_newauthtok); + } else { + ret = prompt_new_password(pamh, pi); + if (ret != PAM_SUCCESS) { + D(("failed to get new password from user")); + return ret; + } + + if (flags & FLAGS_FORWARD_PASS) { + ret = pam_set_item(pamh, PAM_AUTHTOK, pi->pam_newauthtok); + if (ret != PAM_SUCCESS) { + D(("Failed to set PAM_AUTHTOK [%s], " + "oldauthtok may not be available", + pam_strerror(pamh,ret))); + } + } + } + + return PAM_SUCCESS; +} + static int pam_sss(enum sss_cli_command task, pam_handle_t *pamh, int pam_flags, int argc, const char **argv) { @@ -673,82 +778,23 @@ static int pam_sss(enum sss_cli_command task, pam_handle_t *pamh, switch(task) { case SSS_PAM_AUTHENTICATE: - if (flags & FLAGS_USE_FIRST_PASS) { - pi.pam_authtok_type = SSS_AUTHTOK_TYPE_PASSWORD; - pi.pam_authtok = strdup(pi.pamstack_authtok); - if (pi.pam_authtok == NULL) { - D(("option use_first_pass set, but no password found")); - return PAM_BUF_ERR; - } - pi.pam_authtok_size = strlen(pi.pam_authtok); - } else { - prompt_password(pamh, &pi); - - if (flags & FLAGS_FORWARD_PASS) { - ret = pam_set_item(pamh, PAM_AUTHTOK, pi.pam_authtok); - if (ret != PAM_SUCCESS) { - D(("Failed to set PAM_AUTHTOK [%s], " - "authtok may not be available for other modules", - pam_strerror(pamh,ret))); - } - } + ret = get_authtok_for_authentication(pamh, &pi, flags); + if (ret != PAM_SUCCESS) { + D(("failed to get authentication token: %s", + pam_strerror(pamh, ret))); + return ret; } - break; case SSS_PAM_CHAUTHTOK: - /* we query for the old password during PAM_PRELIM_CHECK to make - * pam_sss work e.g. with pam_cracklib */ - if (pam_flags & PAM_PRELIM_CHECK) { - if (getuid() != 0 && !(flags & FLAGS_USE_FIRST_PASS)) { - prompt_password(pamh, &pi); - - ret = pam_set_item(pamh, PAM_OLDAUTHTOK, pi.pam_authtok); - if (ret != PAM_SUCCESS) { - D(("Failed to set PAM_OLDAUTHTOK [%s], " - "oldauthtok may not be available", - pam_strerror(pamh,ret))); - return ret; - } - } - - return PAM_SUCCESS; - } - - if (getuid() != 0) { - pi.pam_authtok_type = SSS_AUTHTOK_TYPE_PASSWORD; - pi.pam_authtok = strdup(pi.pamstack_oldauthtok); - if (pi.pam_authtok == NULL) { - D(("no password found for chauthtok")); - return PAM_BUF_ERR; - } - pi.pam_authtok_size = strlen(pi.pam_authtok); - } else { - pi.pam_authtok_type = SSS_AUTHTOK_TYPE_EMPTY; - pi.pam_authtok = NULL; - pi.pam_authtok_size = 0; + ret = get_authtok_for_password_change(pamh, &pi, flags, pam_flags); + if (ret != PAM_SUCCESS) { + D(("failed to get tokens for password change: %s", + pam_strerror(pamh, ret))); + return ret; } - - if (flags & FLAGS_USE_AUTHTOK) { - pi.pam_newauthtok_type = SSS_AUTHTOK_TYPE_PASSWORD; - pi.pam_newauthtok = strdup(pi.pamstack_authtok); - if (pi.pam_newauthtok == NULL) { - D(("option use_authtok set, but no new password found")); - return PAM_BUF_ERR; - } - pi.pam_newauthtok_size = strlen(pi.pam_newauthtok); - } else { - prompt_new_password(pamh, &pi); - - if (flags & FLAGS_FORWARD_PASS) { - ret = pam_set_item(pamh, PAM_AUTHTOK, pi.pam_newauthtok); - if (ret != PAM_SUCCESS) { - D(("Failed to set PAM_AUTHTOK [%s], " - "oldauthtok may not be available", - pam_strerror(pamh,ret))); - } - } + if (pam_flags & PAM_PRELIM_CHECK) { + return ret; } - break; case SSS_PAM_ACCT_MGMT: case SSS_PAM_SETCRED: -- 1.6.2.5
>From 0b32e6ef92e857ffeaa97162a86a4f983faec61b Mon Sep 17 00:00:00 2001 From: Sumit Bose <sb...@redhat.com> Date: Mon, 5 Oct 2009 10:23:38 +0200 Subject: [PATCH 3/3] ask for new password if password is expired --- sss_client/pam_sss.c | 40 +++++++++++++++++++++++++++++++++------- 1 files changed, 33 insertions(+), 7 deletions(-) diff --git a/sss_client/pam_sss.c b/sss_client/pam_sss.c index eec25ab..7073872 100644 --- a/sss_client/pam_sss.c +++ b/sss_client/pam_sss.c @@ -158,6 +158,21 @@ static size_t add_string_item(enum pam_item_type type, const char *str, return rp; } +static void overwrite_and_free_authtoks(struct pam_items *pi) +{ + if (pi->pam_authtok != NULL) { + _pam_overwrite_n((void *)pi->pam_authtok, pi->pam_authtok_size); + free((void *)pi->pam_authtok); + pi->pam_authtok = NULL; + } + + if (pi->pam_newauthtok != NULL) { + _pam_overwrite_n((void *)pi->pam_newauthtok, pi->pam_newauthtok_size); + free((void *)pi->pam_newauthtok); + pi->pam_newauthtok = NULL; + } +} + static int pack_message_v3(struct pam_items *pi, size_t *size, uint8_t **buffer) { int len; @@ -210,16 +225,10 @@ static int pack_message_v3(struct pam_items *pi, size_t *size, rp += add_authtok_item(PAM_ITEM_AUTHTOK, pi->pam_authtok_type, pi->pam_authtok, pi->pam_authtok_size, &buf[rp]); - _pam_overwrite_n((void *)pi->pam_authtok, pi->pam_authtok_size); - free((void *)pi->pam_authtok); - pi->pam_authtok = NULL; rp += add_authtok_item(PAM_ITEM_NEWAUTHTOK, pi->pam_newauthtok_type, pi->pam_newauthtok, pi->pam_newauthtok_size, &buf[rp]); - _pam_overwrite_n((void *)pi->pam_newauthtok, pi->pam_newauthtok_size); - free((void *)pi->pam_newauthtok); - pi->pam_newauthtok = NULL; ((uint32_t *)(&buf[rp]))[0] = END_OF_PAM_REQUEST; rp += sizeof(uint32_t); @@ -806,7 +815,24 @@ static int pam_sss(enum sss_cli_command task, pam_handle_t *pamh, return PAM_SYSTEM_ERR; } - return send_and_receive(pamh, &pi, task); + ret = send_and_receive(pamh, &pi, task); + + if (ret == PAM_AUTHTOK_EXPIRED && task == SSS_PAM_AUTHENTICATE) { + D(("Authtoken expired, trying to change it")); + pi.pamstack_oldauthtok = pi.pam_authtok; + ret = get_authtok_for_password_change(pamh, &pi, flags, pam_flags); + if (ret != PAM_SUCCESS) { + D(("failed to get tokens for password change: %s", + pam_strerror(pamh, ret))); + return ret; + } + + ret = send_and_receive(pamh, &pi, SSS_PAM_CHAUTHTOK); + } + + overwrite_and_free_authtoks(&pi); + + return ret; } PAM_EXTERN int pam_sm_authenticate(pam_handle_t *pamh, int flags, int argc, -- 1.6.2.5
_______________________________________________ sssd-devel mailing list sssd-devel@lists.fedorahosted.org https://fedorahosted.org/mailman/listinfo/sssd-devel