[SSSD] [PATCH] Handle Krb5 password expiration warning

Sumit Bose sbose at redhat.com
Fri Apr 30 16:10:23 UTC 2010


On Fri, Apr 30, 2010 at 10:18:39AM -0400, Stephen Gallagher wrote:
> On 04/29/2010 07:27 AM, Sumit Bose wrote:
> > Sorry, the first patch had dependencies to another patch, new version
> > attached.
> >
> > bye,
> > Sumit
> > On Tue, Apr 27, 2010 at 02:08:02PM +0200, Sumit Bose wrote:
> >> Hi,
> >>
> >> this two patches add the support to display a warning to the user that
> >> the Kerberos password is about to expire. The first patch just moves
> >> some utility functions to a separate file to avoid linking the
> >> krb5_child against libdbus.
> >>
> >> I the second patch a prompter function is introduced to catch the
> >> warning message which is generated by libkrb5. With the current API of
> >> MIT Kerberos we have to rely on this message, because the the underlying
> >> AS_REPLY data is not exposed by the library. As a consequece this message
> >> cannot be translated, but this is true for messages which are generated
> >> to indicate why a password change failed, too. The rest of the patch
> >> changes the way the response from the child is packed and unpacked to
> >> allow more than one response message.
> >>
> >> bye,
> >> Sumit
> >
> 
> 
> Patch 0001: Ack
> 
> Patch 0002: The krb5_child_done() function is in desperate need of 
> comments. At minimum, there needs to be a comment describing the format 
> of the received buffer. Same for the pack_response_packet().

comments added

> 
> This is wrong:
>          if (msg_len > pref_len &&
>              strncmp((const char *) &buf[p], CCACHE_ENV_NAME"=", 
> pref_len) == 0) {
> 
> pref_len needs to be another byte longer to account for the '='. Also, a 
> comment here would be fantastic.

pref_len is already a bye longer, because I used sizeof instead of
strlen. I added a comment about this, too.

New version attached.

bye,
Sumit
> 
> Otherwise this looks fine.
> 
> 
> -- 
> Stephen Gallagher
> RHCE 804006346421761
> 
> Delivering value year after year.
> Red Hat ranks #1 in value among software vendors.
> http://www.redhat.com/promo/vendor/
> _______________________________________________
> sssd-devel mailing list
> sssd-devel at lists.fedorahosted.org
> https://fedorahosted.org/mailman/listinfo/sssd-devel
-------------- next part --------------
From 7a1ecb87fe5b65bfbce19afcce2f8e1551f4b0a1 Mon Sep 17 00:00:00 2001
From: Sumit Bose <sbose at redhat.com>
Date: Fri, 23 Apr 2010 15:21:38 +0200
Subject: [PATCH 1/2] Split pam_data utilities into a separate file

---
 src/Makefile.am                  |    2 +
 src/providers/dp_auth_util.c     |   35 ----------------------
 src/providers/dp_pam_data_util.c |   60 ++++++++++++++++++++++++++++++++++++++
 3 files changed, 62 insertions(+), 35 deletions(-)
 create mode 100644 src/providers/dp_pam_data_util.c

diff --git a/src/Makefile.am b/src/Makefile.am
index 22ac358..5fb4062 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -221,6 +221,7 @@ SSSD_UTIL_OBJ = \
     db/sysdb_search.c \
     monitor/monitor_sbus.c \
     providers/dp_auth_util.c \
+    providers/dp_pam_data_util.c \
     providers/dp_sbus.c \
     sbus/sbus_client.c \
     sbus/sssd_dbus_common.c \
@@ -825,6 +826,7 @@ krb5_child_SOURCES = \
     providers/krb5/krb5_become_user.c \
     providers/krb5/krb5_child.c \
     providers/child_common.c \
+    providers/dp_pam_data_util.c \
     util/user_info_msg.c \
     util/sss_krb5.c
 krb5_child_CFLAGS = \
diff --git a/src/providers/dp_auth_util.c b/src/providers/dp_auth_util.c
index fb10ced..e09a692 100644
--- a/src/providers/dp_auth_util.c
+++ b/src/providers/dp_auth_util.c
@@ -21,41 +21,6 @@
 
 #include "data_provider.h"
 
-void pam_print_data(int l, struct pam_data *pd)
-{
-    DEBUG(l, ("command: %d\n", pd->cmd));
-    DEBUG(l, ("domain: %s\n", pd->domain));
-    DEBUG(l, ("user: %s\n", pd->user));
-    DEBUG(l, ("service: %s\n", pd->service));
-    DEBUG(l, ("tty: %s\n", pd->tty));
-    DEBUG(l, ("ruser: %s\n", pd->ruser));
-    DEBUG(l, ("rhost: %s\n", pd->rhost));
-    DEBUG(l, ("authtok type: %d\n", pd->authtok_type));
-    DEBUG(l, ("authtok size: %d\n", pd->authtok_size));
-    DEBUG(l, ("newauthtok type: %d\n", pd->newauthtok_type));
-    DEBUG(l, ("newauthtok size: %d\n", pd->newauthtok_size));
-    DEBUG(l, ("priv: %d\n", pd->priv));
-    DEBUG(l, ("cli_pid: %d\n", pd->cli_pid));
-}
-
-int pam_add_response(struct pam_data *pd, enum response_type type,
-                     int len, const uint8_t *data)
-{
-    struct response_data *new;
-
-    new = talloc(pd, struct response_data);
-    if (new == NULL) return ENOMEM;
-
-    new->type = type;
-    new->len = len;
-    new->data = talloc_memdup(pd, data, len);
-    if (new->data == NULL) return ENOMEM;
-    new->next = pd->resp_list;
-    pd->resp_list = new;
-
-    return EOK;
-}
-
 bool dp_pack_pam_request(DBusMessage *msg, struct pam_data *pd)
 {
     int ret;
diff --git a/src/providers/dp_pam_data_util.c b/src/providers/dp_pam_data_util.c
new file mode 100644
index 0000000..308bd7c
--- /dev/null
+++ b/src/providers/dp_pam_data_util.c
@@ -0,0 +1,60 @@
+/*
+    SSSD
+
+    Utilities to for tha pam_data structure
+
+    Authors:
+        Sumit Bose <sbose at redhat.com>
+
+    Copyright (C) 2009 Red Hat
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "providers/data_provider.h"
+
+void pam_print_data(int l, struct pam_data *pd)
+{
+    DEBUG(l, ("command: %d\n", pd->cmd));
+    DEBUG(l, ("domain: %s\n", pd->domain));
+    DEBUG(l, ("user: %s\n", pd->user));
+    DEBUG(l, ("service: %s\n", pd->service));
+    DEBUG(l, ("tty: %s\n", pd->tty));
+    DEBUG(l, ("ruser: %s\n", pd->ruser));
+    DEBUG(l, ("rhost: %s\n", pd->rhost));
+    DEBUG(l, ("authtok type: %d\n", pd->authtok_type));
+    DEBUG(l, ("authtok size: %d\n", pd->authtok_size));
+    DEBUG(l, ("newauthtok type: %d\n", pd->newauthtok_type));
+    DEBUG(l, ("newauthtok size: %d\n", pd->newauthtok_size));
+    DEBUG(l, ("priv: %d\n", pd->priv));
+    DEBUG(l, ("cli_pid: %d\n", pd->cli_pid));
+}
+
+int pam_add_response(struct pam_data *pd, enum response_type type,
+                     int len, const uint8_t *data)
+{
+    struct response_data *new;
+
+    new = talloc(pd, struct response_data);
+    if (new == NULL) return ENOMEM;
+
+    new->type = type;
+    new->len = len;
+    new->data = talloc_memdup(pd, data, len);
+    if (new->data == NULL) return ENOMEM;
+    new->next = pd->resp_list;
+    pd->resp_list = new;
+
+    return EOK;
+}
-- 
1.6.6.1

-------------- next part --------------
From 422d4c28318ae3896bbcc729a75d7652d86a44a5 Mon Sep 17 00:00:00 2001
From: Sumit Bose <sbose at redhat.com>
Date: Fri, 23 Apr 2010 13:37:22 +0200
Subject: [PATCH 2/2] Handle Krb5 password expiration warning

---
 src/providers/krb5/krb5_auth.c  |  190 +++++++++++++++------------------------
 src/providers/krb5/krb5_child.c |  177 +++++++++++++++++++++++++-----------
 src/sss_client/pam_sss.c        |   14 +++-
 src/sss_client/sss_cli.h        |    6 +-
 4 files changed, 214 insertions(+), 173 deletions(-)

diff --git a/src/providers/krb5/krb5_auth.c b/src/providers/krb5/krb5_auth.c
index 0c08fe1..9e9eb13 100644
--- a/src/providers/krb5/krb5_auth.c
+++ b/src/providers/krb5/krb5_auth.c
@@ -6,7 +6,7 @@
     Authors:
         Sumit Bose <sbose at redhat.com>
 
-    Copyright (C) 2009 Red Hat
+    Copyright (C) 2009-2010 Red Hat
 
     This program is free software; you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
@@ -45,74 +45,6 @@
 #define KRB5_CHILD SSSD_LIBEXEC_PATH"/krb5_child"
 #endif
 
-static errno_t add_krb5_env(struct dp_option *opts, const char *ccname,
-                            struct pam_data *pd)
-{
-    int ret;
-    const char *dummy;
-    char *env;
-    TALLOC_CTX *tmp_ctx = NULL;
-
-    tmp_ctx = talloc_new(NULL);
-    if (tmp_ctx == NULL) {
-        DEBUG(1, ("talloc_new failed.\n"));
-        return ENOMEM;
-    }
-
-    if (ccname != NULL) {
-        env = talloc_asprintf(tmp_ctx, "%s=%s",CCACHE_ENV_NAME, ccname);
-        if (env == NULL) {
-            DEBUG(1, ("talloc_asprintf failed.\n"));
-            ret = ENOMEM;
-            goto done;
-        }
-        ret = pam_add_response(pd, SSS_PAM_ENV_ITEM, strlen(env)+1,
-                               (uint8_t *) env);
-        if (ret != EOK) {
-            DEBUG(1, ("pam_add_response failed.\n"));
-            goto done;
-        }
-    }
-
-    dummy = dp_opt_get_cstring(opts, KRB5_REALM);
-    if (dummy != NULL) {
-        env = talloc_asprintf(tmp_ctx, "%s=%s", SSSD_KRB5_REALM, dummy);
-        if (env == NULL) {
-            DEBUG(1, ("talloc_asprintf failed.\n"));
-            ret = ENOMEM;
-            goto done;
-        }
-        ret = pam_add_response(pd, SSS_PAM_ENV_ITEM, strlen(env)+1,
-                               (uint8_t *) env);
-        if (ret != EOK) {
-            DEBUG(1, ("pam_add_response failed.\n"));
-            goto done;
-        }
-    }
-
-    dummy = dp_opt_get_cstring(opts, KRB5_KDC);
-    if (dummy != NULL) {
-        env = talloc_asprintf(tmp_ctx, "%s=%s", SSSD_KRB5_KDC, dummy);
-        if (env == NULL) {
-            DEBUG(1, ("talloc_asprintf failed.\n"));
-            ret = ENOMEM;
-            goto done;
-        }
-        ret = pam_add_response(pd, SSS_PAM_ENV_ITEM, strlen(env)+1,
-                               (uint8_t *) env);
-        if (ret != EOK) {
-            DEBUG(1, ("pam_add_response failed.\n"));
-            goto done;
-        }
-    }
-
-    ret = EOK;
-
-done:
-    talloc_free(tmp_ctx);
-    return ret;
-}
-
 static errno_t check_if_ccache_file_is_used(uid_t uid, const char *ccname,
                                             bool *result)
 {
@@ -1104,10 +1036,10 @@ static void krb5_child_done(struct tevent_req *subreq)
     uint8_t *buf = NULL;
     ssize_t len = -1;
     ssize_t pref_len;
-    int p;
-    int32_t *msg_status;
-    int32_t *msg_type;
-    int32_t *msg_len;
+    size_t p;
+    int32_t msg_status;
+    int32_t msg_type;
+    int32_t msg_len;
 
     ret = handle_child_recv(subreq, pd, &buf, &len);
     talloc_zfree(kr->timeout_handler);
@@ -1124,71 +1056,104 @@ static void krb5_child_done(struct tevent_req *subreq)
         return;
     }
 
-    if ((size_t) len < 3*sizeof(int32_t)) {
+    /* A buffer with the following structure is expected.
+     * int32_t status of the request (required)
+     * message (zero or more)
+     *
+     * A message consists of:
+     * int32_t type of the message
+     * int32_t length of the following data
+     * uint8_t[len] data
+     */
+
+    if ((size_t) len < sizeof(int32_t)) {
         DEBUG(1, ("message too short.\n"));
         ret = EINVAL;
         goto done;
     }
 
     p=0;
-    msg_status = ((int32_t *)(buf+p));
-    p += sizeof(int32_t);
-
-    msg_type = ((int32_t *)(buf+p));
-    p += sizeof(int32_t);
+    SAFEALIGN_COPY_INT32(&msg_status, buf+p, &p);
 
-    msg_len = ((int32_t *)(buf+p));
-    p += sizeof(int32_t);
+    while (p < len) {
+        SAFEALIGN_COPY_INT32(&msg_type, buf+p, &p);
+        SAFEALIGN_COPY_INT32(&msg_len, buf+p, &p);
 
-    DEBUG(4, ("child response [%d][%d][%d].\n", *msg_status, *msg_type,
-                                                *msg_len));
+        DEBUG(9, ("child response [%d][%d][%d].\n", msg_status, msg_type,
+                                                    msg_len));
 
-    if ((p + *msg_len) != len) {
-        DEBUG(1, ("message format error [%d] != [%d].\n", p+*msg_len, len));
-        goto done;
-    }
+        if ((p + msg_len) > len) {
+            DEBUG(1, ("message format error [%d] > [%d].\n", p+msg_len, len));
+            ret = EINVAL;
+            goto done;
+        }
 
-    if (*msg_status != PAM_SUCCESS && *msg_status != PAM_AUTHINFO_UNAVAIL) {
-        state->pam_status = *msg_status;
-        state->dp_err = DP_ERR_OK;
+        /* We need to save the name of the credential cache file. To find it
+         * we check if the data part of a message starts with
+         * CCACHE_ENV_NAME"=". pref_len also counts the trailing '=' because
+         * sizeof() counts the trailing '\0' of a string. */
+        pref_len = sizeof(CCACHE_ENV_NAME);
+        if (msg_len > pref_len &&
+            strncmp((const char *) &buf[p], CCACHE_ENV_NAME"=", pref_len) == 0) {
+            kr->ccname = talloc_strndup(kr, (char *) &buf[p+pref_len],
+                                        msg_len-pref_len);
+            if (kr->ccname == NULL) {
+                DEBUG(1, ("talloc_strndup failed.\n"));
+                ret = ENOMEM;
+                goto done;
+            }
+        }
 
-        ret = pam_add_response(pd, *msg_type, *msg_len, &buf[p]);
+        ret = pam_add_response(pd, msg_type, msg_len, &buf[p]);
         if (ret != EOK) {
             /* This is not a fatal error */
             DEBUG(1, ("pam_add_response failed.\n"));
         }
+        p += msg_len;
+
+        if ((p < len) && (p + 2*sizeof(int32_t) >= len)) {
+            DEBUG(1, ("The remainder of the message is too short.\n"));
+            ret = EINVAL;
+            goto done;
+        }
+    }
 
+    /* If the child request failed, but did not return an offline error code,
+     * return with the status */
+    if (msg_status != PAM_SUCCESS && msg_status != PAM_AUTHINFO_UNAVAIL &&
+        msg_status != PAM_AUTHTOK_LOCK_BUSY) {
+        state->pam_status = msg_status;
+        state->dp_err = DP_ERR_OK;
         ret = EOK;
         goto done;
     } else {
-        state->pam_status = *msg_status;
+        state->pam_status = msg_status;
     }
 
-    if (*msg_status == PAM_SUCCESS && pd->cmd == SSS_PAM_CHAUTHTOK_PRELIM) {
+    /* If the child request was successful and we run the first pass of the
+     * change password request just return success. */
+    if (msg_status == PAM_SUCCESS && pd->cmd == SSS_PAM_CHAUTHTOK_PRELIM) {
         state->pam_status = PAM_SUCCESS;
         state->dp_err = DP_ERR_OK;
         ret = EOK;
         goto done;
     }
 
-    pref_len = strlen(CCACHE_ENV_NAME)+1;
-    if (*msg_len > pref_len &&
-        strncmp((const char *) &buf[p], CCACHE_ENV_NAME"=", pref_len) == 0) {
-        kr->ccname = talloc_strndup(kr, (char *) &buf[p+pref_len],
-                                   *msg_len-pref_len);
-        if (kr->ccname == NULL) {
-            DEBUG(1, ("talloc_strndup failed.\n"));
-            ret = ENOMEM;
-            goto done;
-        }
-    } else {
-        DEBUG(1, ("Missing ccache name in child response [%.*s].\n", *msg_len,
-                                                                     &buf[p]));
+    /* The following cases are left now:
+     *  - offline (msg_status == PAM_AUTHINFO_UNAVAIL or
+     *             msg_status == PAM_AUTHTOK_LOCK_BUSY)
+     *  - successful authentication or password change
+     *
+     *  For all these cases we expect that one of the messages for the
+     *  received buffer contains the name of the credential cache file. */
+    if (kr->ccname == NULL) {
+        DEBUG(1, ("Missing ccache name in child response.\n"));
         ret = EINVAL;
         goto done;
     }
 
-    if (*msg_status == PAM_AUTHINFO_UNAVAIL) {
+    /* KDC is not reachable */
+    if (msg_status == PAM_AUTHINFO_UNAVAIL) {
         if (kr->srv != NULL) {
             fo_set_port_status(kr->srv, PORT_NOT_WORKING);
         }
@@ -1199,7 +1164,8 @@ static void krb5_child_done(struct tevent_req *subreq)
     }
 
     if (kr->kpasswd_srv != NULL) {
-        if (*msg_status == PAM_AUTHTOK_LOCK_BUSY) {
+        /* kpasswd is not reachable */
+        if (msg_status == PAM_AUTHTOK_LOCK_BUSY) {
             fo_set_port_status(kr->kpasswd_srv, PORT_NOT_WORKING);
         } else {
             fo_set_port_status(kr->kpasswd_srv, PORT_WORKING);
@@ -1242,14 +1208,6 @@ static void krb5_save_ccname_done(struct tevent_req *subreq)
     int ret;
     char *password = NULL;
 
-    if (pd->cmd == SSS_PAM_AUTHENTICATE || pd->cmd == SSS_PAM_CHAUTHTOK) {
-        ret = add_krb5_env(krb5_ctx->opts, kr->ccname, pd);
-        if (ret != EOK) {
-            DEBUG(1, ("add_krb5_env failed.\n"));
-            goto done;
-        }
-    }
-
     ret = sysdb_set_user_attr_recv(subreq);
     talloc_zfree(subreq);
     if (ret != EOK) {
diff --git a/src/providers/krb5/krb5_child.c b/src/providers/krb5/krb5_child.c
index 620e4d1..7249aeb 100644
--- a/src/providers/krb5/krb5_child.c
+++ b/src/providers/krb5/krb5_child.c
@@ -6,7 +6,7 @@
     Authors:
         Sumit Bose <sbose at redhat.com>
 
-    Copyright (C) 2009 Red Hat
+    Copyright (C) 2009-2010 Red Hat
 
     This program is free software; you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
@@ -103,6 +103,36 @@ static const char *__krb5_error_msg;
     sss_krb5_free_error_message(krb5_error_ctx, __krb5_error_msg); \
 } while(0);
 
+
+static krb5_error_code sss_krb5_prompter(krb5_context context, void *data,
+                                         const char *name, const char *banner,
+                                         int num_prompts, krb5_prompt prompts[])
+{
+    int ret;
+    struct krb5_req *kr = talloc_get_type(data, struct krb5_req);
+
+    if (num_prompts != 0) {
+        DEBUG(1, ("Cannot handle password prompts.\n"));
+        return KRB5_LIBOS_CANTREADPWD;
+    }
+
+    if (banner == NULL || *banner == '\0') {
+        DEBUG(5, ("Prompter called with empty banner, nothing to do.\n"));
+        return EOK;
+    }
+
+    DEBUG(9, ("Prompter called with [%s].\n", banner));
+
+    ret = pam_add_response(kr->pd, SSS_PAM_TEXT_MSG, strlen(banner)+1,
+                           (const uint8_t *) banner);
+    if (ret != EOK) {
+        DEBUG(1, ("pam_add_response failed.\n"));
+    }
+
+    return EOK;
+}
+
+
 static krb5_error_code create_empty_cred(struct krb5_req *kr, krb5_creds **_cred)
 {
     krb5_error_code kerr;
@@ -247,22 +277,49 @@ done:
     return kerr;
 }
 
-static errno_t pack_response_packet(struct response *resp, int status, int type,
-                                    size_t len, const uint8_t *data)
+static errno_t pack_response_packet(struct response *resp, int status,
+                                    struct pam_data *pd)
 {
+    size_t size = 0;
     size_t p = 0;
+    struct response_data *pdr;
+
+    /* A buffer with the following structure must be created:
+     * int32_t status of the request (required)
+     * message (zero or more)
+     *
+     * A message consists of:
+     * int32_t type of the message
+     * int32_t length of the following data
+     * uint8_t[len] data
+     */
 
-    resp->buf = talloc_array(resp, uint8_t,
-                             3*sizeof(int32_t) + len);
+    size = sizeof(int32_t);
+
+    pdr = pd->resp_list;
+    while (pdr != NULL) {
+        size += 2*sizeof(int32_t) + pdr->len;
+        pdr = pdr->next;
+    }
+
+
+    resp->buf = talloc_array(resp, uint8_t, size);
     if (!resp->buf) {
         DEBUG(1, ("Insufficient memory to create message.\n"));
         return ENOMEM;
     }
 
     SAFEALIGN_SET_INT32(&resp->buf[p], status, &p);
-    SAFEALIGN_SET_INT32(&resp->buf[p], type, &p);
-    SAFEALIGN_SET_INT32(&resp->buf[p], len, &p);
-    safealign_memcpy(&resp->buf[p], data, len, &p);
+
+    pdr = pd->resp_list;
+    while(pdr != NULL) {
+        SAFEALIGN_SET_INT32(&resp->buf[p], pdr->type, &p);
+        SAFEALIGN_SET_INT32(&resp->buf[p], pdr->len, &p);
+        safealign_memcpy(&resp->buf[p], pdr->data, pdr->len, &p);
+
+        pdr = pdr->next;
+    }
+
 
     resp->size = p;
 
@@ -271,15 +328,12 @@ static errno_t pack_response_packet(struct response *resp, int status, int type,
 
 static struct response *prepare_response_message(struct krb5_req *kr,
                                                  krb5_error_code kerr,
-                                                 char *user_error_message,
                                                  int pam_status)
 {
     char *msg = NULL;
     const char *krb5_msg = NULL;
     int ret;
     struct response *resp;
-    size_t user_resp_len;
-    uint8_t *user_resp;
 
     resp = talloc_zero(kr, struct response);
     if (resp == NULL) {
@@ -289,9 +343,8 @@ static struct response *prepare_response_message(struct krb5_req *kr,
 
     if (kerr == 0) {
         if(kr->pd->cmd == SSS_PAM_CHAUTHTOK_PRELIM) {
-            ret = pack_response_packet(resp, PAM_SUCCESS, SSS_PAM_SYSTEM_INFO,
-                                       strlen("success") + 1,
-                                       (const uint8_t *) "success");
+            pam_status = PAM_SUCCESS;
+            ret = EOK;
         } else {
             if (kr->ccname == NULL) {
                 DEBUG(1, ("Error obtaining ccname.\n"));
@@ -304,44 +357,28 @@ static struct response *prepare_response_message(struct krb5_req *kr,
                 return NULL;
             }
 
-            ret = pack_response_packet(resp, PAM_SUCCESS, SSS_PAM_ENV_ITEM,
-                                       strlen(msg) + 1, (uint8_t *) msg);
+            pam_status = PAM_SUCCESS;
+            ret = pam_add_response(kr->pd, SSS_PAM_ENV_ITEM, strlen(msg) + 1,
+                                   (uint8_t *) msg);
             talloc_zfree(msg);
         }
     } else {
-        if (user_error_message != NULL) {
-            ret = pack_user_info_chpass_error(kr, user_error_message,
-                                              &user_resp_len, &user_resp);
-            if (ret != EOK) {
-                DEBUG(1, ("pack_user_info_chpass_error failed.\n"));
-                talloc_zfree(user_error_message);
-            } else {
-                ret = pack_response_packet(resp, pam_status, SSS_PAM_USER_INFO,
-                                           user_resp_len, user_resp);
-                if (ret != EOK) {
-                    DEBUG(1, ("pack_response_packet failed.\n"));
-                    talloc_zfree(user_error_message);
-                }
-            }
-        }
-
-        if (user_error_message == NULL) {
-            krb5_msg = sss_krb5_get_error_message(krb5_error_ctx, kerr);
-            if (krb5_msg == NULL) {
-                DEBUG(1, ("sss_krb5_get_error_message failed.\n"));
-                return NULL;
-            }
-
-            ret = pack_response_packet(resp, pam_status, SSS_PAM_SYSTEM_INFO,
-                                       strlen(krb5_msg) + 1,
-                                       (const uint8_t *) krb5_msg);
-            sss_krb5_free_error_message(krb5_error_ctx, krb5_msg);
-        } else {
-
+        krb5_msg = sss_krb5_get_error_message(krb5_error_ctx, kerr);
+        if (krb5_msg == NULL) {
+            DEBUG(1, ("sss_krb5_get_error_message failed.\n"));
+            return NULL;
         }
 
+        ret = pam_add_response(kr->pd, SSS_PAM_SYSTEM_INFO,
+                               strlen(krb5_msg) + 1,
+                               (const uint8_t *) krb5_msg);
+        sss_krb5_free_error_message(krb5_error_ctx, krb5_msg);
+    }
+    if (ret != EOK) {
+        DEBUG(1, ("pam_add_response failed.\n"));
     }
 
+    ret = pack_response_packet(resp, pam_status, kr->pd);
     if (ret != EOK) {
         DEBUG(1, ("pack_response_packet failed.\n"));
         return NULL;
@@ -350,15 +387,14 @@ static struct response *prepare_response_message(struct krb5_req *kr,
     return resp;
 }
 
-static errno_t sendresponse(int fd, krb5_error_code kerr,
-                            char *user_error_message, int pam_status,
+static errno_t sendresponse(int fd, krb5_error_code kerr, int pam_status,
                             struct krb5_req *kr)
 {
     struct response *resp;
     size_t written;
     int ret;
 
-    resp = prepare_response_message(kr, kerr, user_error_message, pam_status);
+    resp = prepare_response_message(kr, kerr, pam_status);
     if (resp == NULL) {
         DEBUG(1, ("prepare_response_message failed.\n"));
         return ENOMEM;
@@ -481,8 +517,8 @@ static krb5_error_code get_and_save_tgt(struct krb5_req *kr,
     int ret;
 
     kerr = krb5_get_init_creds_password(kr->ctx, kr->creds, kr->princ,
-                                        password, NULL, NULL, 0, NULL,
-                                        kr->options);
+                                        password, sss_krb5_prompter, kr, 0,
+                                        NULL, kr->options);
     if (kerr != 0) {
         KRB5_DEBUG(1, kerr);
         return kerr;
@@ -533,6 +569,9 @@ static errno_t changepw_child(int fd, struct krb5_req *kr)
     krb5_data result_code_string;
     krb5_data result_string;
     char *user_error_message = NULL;
+    size_t user_resp_len;
+    uint8_t *user_resp;
+    krb5_prompter_fct prompter = sss_krb5_prompter;
 
     pass_str = talloc_strndup(kr, (const char *) kr->pd->authtok,
                               kr->pd->authtok_size);
@@ -542,8 +581,13 @@ static errno_t changepw_child(int fd, struct krb5_req *kr)
         goto sendresponse;
     }
 
+    if (kr->pd->cmd == SSS_PAM_CHAUTHTOK_PRELIM) {
+        /* We do not need a password expiration warning here. */
+        prompter = NULL;
+    }
+
     kerr = krb5_get_init_creds_password(kr->ctx, kr->creds, kr->princ,
-                                        pass_str, NULL, NULL, 0,
+                                        pass_str, prompter, kr, 0,
                                         kr->krb5_ctx->changepw_principle,
                                         kr->options);
     if (kerr != 0) {
@@ -612,6 +656,20 @@ static errno_t changepw_child(int fd, struct krb5_req *kr)
             }
         }
 
+        if (user_error_message != NULL) {
+            ret = pack_user_info_chpass_error(kr->pd, user_error_message,
+                                              &user_resp_len, &user_resp);
+            if (ret != EOK) {
+                DEBUG(1, ("pack_user_info_chpass_error failed.\n"));
+            } else {
+                ret = pam_add_response(kr->pd, SSS_PAM_USER_INFO, user_resp_len,
+                                       user_resp);
+                if (ret != EOK) {
+                    DEBUG(1, ("pack_response_packet failed.\n"));
+                }
+            }
+        }
+
         pam_status = PAM_AUTHTOK_ERR;
         goto sendresponse;
     }
@@ -631,7 +689,7 @@ static errno_t changepw_child(int fd, struct krb5_req *kr)
     }
 
 sendresponse:
-    ret = sendresponse(fd, kerr, user_error_message, pam_status, kr);
+    ret = sendresponse(fd, kerr, pam_status, kr);
     if (ret != EOK) {
         DEBUG(1, ("sendresponse failed.\n"));
     }
@@ -662,7 +720,7 @@ static errno_t tgt_req_child(int fd, struct krb5_req *kr)
        So we validate the password by trying to get a changepw ticket. */
     if (kerr == KRB5KDC_ERR_KEY_EXP) {
         kerr = krb5_get_init_creds_password(kr->ctx, kr->creds, kr->princ,
-                                            pass_str, NULL, NULL, 0,
+                                            pass_str, sss_krb5_prompter, kr, 0,
                                             kr->krb5_ctx->changepw_principle,
                                             kr->options);
         krb5_free_cred_contents(kr->ctx, kr->creds);
@@ -693,7 +751,7 @@ static errno_t tgt_req_child(int fd, struct krb5_req *kr)
     }
 
 sendresponse:
-    ret = sendresponse(fd, kerr, NULL, pam_status, kr);
+    ret = sendresponse(fd, kerr, pam_status, kr);
     if (ret != EOK) {
         DEBUG(1, ("sendresponse failed.\n"));
     }
@@ -712,7 +770,7 @@ static errno_t create_empty_ccache(int fd, struct krb5_req *kr)
         pam_status = PAM_SYSTEM_ERR;
     }
 
-    ret = sendresponse(fd, ret, NULL, pam_status, kr);
+    ret = sendresponse(fd, ret, pam_status, kr);
     if (ret != EOK) {
         DEBUG(1, ("sendresponse failed.\n"));
     }
@@ -878,6 +936,15 @@ static int krb5_setup(struct krb5_req *kr, uint32_t offline)
         goto failed;
     }
 
+    /* A prompter is used to catch messages about when a password will
+     * expired. The library shall not use the prompter to ask for a new password
+     * but shall return KRB5KDC_ERR_KEY_EXP. */
+    krb5_get_init_creds_opt_set_change_password_prompt(kr->options, 0);
+    if (kerr != 0) {
+        KRB5_DEBUG(1, kerr);
+        goto failed;
+    }
+
 /* TODO: set options, e.g.
  *  krb5_get_init_creds_opt_set_tkt_life
  *  krb5_get_init_creds_opt_set_renew_life
diff --git a/src/sss_client/pam_sss.c b/src/sss_client/pam_sss.c
index 8bd3c9e..d4e09da 100644
--- a/src/sss_client/pam_sss.c
+++ b/src/sss_client/pam_sss.c
@@ -888,7 +888,7 @@ static int eval_response(pam_handle_t *pamh, size_t buflen, uint8_t *buf,
         switch(type) {
             case SSS_PAM_SYSTEM_INFO:
                 if (buf[p + (len -1)] != '\0') {
-                    D(("user info does not end with \\0."));
+                    D(("system info does not end with \\0."));
                     break;
                 }
                 logger(pamh, LOG_INFO, "system info: [%s]", &buf[p]);
@@ -940,6 +940,18 @@ static int eval_response(pam_handle_t *pamh, size_t buflen, uint8_t *buf,
                     D(("eval_user_info_response failed"));
                 }
                 break;
+            case SSS_PAM_TEXT_MSG:
+                if (buf[p + (len -1)] != '\0') {
+                    D(("system info does not end with \\0."));
+                    break;
+                }
+
+                ret = do_pam_conversation(pamh, PAM_TEXT_INFO, (char *) &buf[p],
+                                          NULL, NULL);
+                if (ret != PAM_SUCCESS) {
+                    D(("do_pam_conversation failed."));
+                }
+                break;
             default:
                 D(("Unknown response type [%d]", type));
         }
diff --git a/src/sss_client/sss_cli.h b/src/sss_client/sss_cli.h
index f7e58fe..8712a6f 100644
--- a/src/sss_client/sss_cli.h
+++ b/src/sss_client/sss_cli.h
@@ -316,9 +316,13 @@ enum response_type {
                           * @param String, zero terminated, of the form
                           * name=value. See putenv(3) and pam_putenv(3) for
                           * details. */
-    SSS_PAM_USER_INFO    /**< A message which should be displayed to the user.
+    SSS_PAM_USER_INFO,   /**< A message which should be displayed to the user.
                           * @param User info message, see #user_info_type
                           * for details. */
+    SSS_PAM_TEXT_MSG,    /**< A plain text message which should be displayed to
+                          * the user.This should only be used in the case where
+                          * it is not possile to use SSS_PAM_USER_INFO.
+                          * @param A zero terminated string. */
 };
 
 /**
-- 
1.6.6.1



More information about the sssd-devel mailing list