[SSSD] [PATCH] Handle expired passwords like other PAM modules

Sumit Bose sbose at redhat.com
Tue Feb 23 11:58:36 UTC 2010


Hi,

this patch changes the way we handle expired passwords to meet the PAM
standards.

bye,
Sumit
-------------- next part --------------
From bb731b875dbb78980c7e2a9d0a97cac54fc5faa2 Mon Sep 17 00:00:00 2001
From: Sumit Bose <sbose at redhat.com>
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



More information about the sssd-devel mailing list