[SSSD] [PATCH] Add handling of expired passwords
Sumit Bose
sbose at redhat.com
Mon Oct 5 08:45:20 UTC 2009
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
-------------- next part --------------
>From bc5f929826a656562a9fa2b8f8d82da97604ec9c Mon Sep 17 00:00:00 2001
From: Sumit Bose <sbose at 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
-------------- next part --------------
>From b0b64abda4b6e106bc2c5d664510814ab49fa072 Mon Sep 17 00:00:00 2001
From: Sumit Bose <sbose at 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
-------------- next part --------------
>From 0b32e6ef92e857ffeaa97162a86a4f983faec61b Mon Sep 17 00:00:00 2001
From: Sumit Bose <sbose at 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
More information about the sssd-devel
mailing list