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

Reply via email to