[SSSD] [PATCH] Handle Krb5 password expiration warning

Sumit Bose sbose at redhat.com
Tue Apr 27 12:08:02 UTC 2010


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
-------------- next part --------------
From cbf80a5f3aa12917c6c05dc264916564caf40068 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     |   91 -----------------------------
 src/providers/dp_pam_data_util.c |  116 ++++++++++++++++++++++++++++++++++++++
 3 files changed, 118 insertions(+), 91 deletions(-)
 create mode 100644 src/providers/dp_pam_data_util.c

diff --git a/src/Makefile.am b/src/Makefile.am
index ead53f9..637a4f3 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 \
@@ -824,6 +825,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 d0ce190..e09a692 100644
--- a/src/providers/dp_auth_util.c
+++ b/src/providers/dp_auth_util.c
@@ -21,97 +21,6 @@
 
 #include "data_provider.h"
 
-#define PD_STR_COPY(el) do { \
-    if (old_pd->el != NULL) { \
-        pd->el = talloc_strdup(pd, old_pd->el); \
-        if (pd->el == NULL) { \
-            DEBUG(1, ("talloc_strdup failed.\n")); \
-            goto failed; \
-        } \
-    } \
-} while(0);
-
-#define PD_MEM_COPY(el, size) do { \
-    if (old_pd->el != NULL) { \
-        pd->el = talloc_memdup(pd, old_pd->el, (size)); \
-        if (pd->el == NULL) { \
-            DEBUG(1, ("talloc_memdup failed.\n")); \
-            goto failed; \
-        } \
-    } \
-} while(0);
-
-errno_t copy_pam_data(TALLOC_CTX *mem_ctx, struct pam_data *old_pd,
-                      struct pam_data **new_pd)
-{
-    struct pam_data *pd = NULL;
-
-    pd = talloc_zero(mem_ctx, struct pam_data);
-    if (pd == NULL) {
-        DEBUG(1, ("talloc_zero failed.\n"));
-        return ENOMEM;
-    }
-
-    pd->cmd  = old_pd->cmd;
-    pd->authtok_type = old_pd->authtok_type;
-    pd->authtok_size = old_pd->authtok_size;
-    pd->newauthtok_type = old_pd->newauthtok_type;
-    pd->newauthtok_size = old_pd->newauthtok_size;
-
-    PD_STR_COPY(domain);
-    PD_STR_COPY(user);
-    PD_STR_COPY(service);
-    PD_STR_COPY(tty);
-    PD_STR_COPY(ruser);
-    PD_STR_COPY(rhost);
-    PD_MEM_COPY(authtok, old_pd->authtok_size);
-    PD_MEM_COPY(newauthtok, old_pd->newauthtok_size);
-    pd->cli_pid = old_pd->cli_pid;
-
-    *new_pd = pd;
-
-    return EOK;
-
-failed:
-    talloc_free(pd);
-    return ENOMEM;
-}
-
-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..f551a48
--- /dev/null
+++ b/src/providers/dp_pam_data_util.c
@@ -0,0 +1,116 @@
+/*
+    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"
+
+#define PD_STR_COPY(el) do { \
+    if (old_pd->el != NULL) { \
+        pd->el = talloc_strdup(pd, old_pd->el); \
+        if (pd->el == NULL) { \
+            DEBUG(1, ("talloc_strdup failed.\n")); \
+            goto failed; \
+        } \
+    } \
+} while(0);
+
+#define PD_MEM_COPY(el, size) do { \
+    if (old_pd->el != NULL) { \
+        pd->el = talloc_memdup(pd, old_pd->el, (size)); \
+        if (pd->el == NULL) { \
+            DEBUG(1, ("talloc_memdup failed.\n")); \
+            goto failed; \
+        } \
+    } \
+} while(0);
+
+errno_t copy_pam_data(TALLOC_CTX *mem_ctx, struct pam_data *old_pd,
+                      struct pam_data **new_pd)
+{
+    struct pam_data *pd = NULL;
+
+    pd = talloc_zero(mem_ctx, struct pam_data);
+    if (pd == NULL) {
+        DEBUG(1, ("talloc_zero failed.\n"));
+        return ENOMEM;
+    }
+
+    pd->cmd  = old_pd->cmd;
+    pd->authtok_type = old_pd->authtok_type;
+    pd->authtok_size = old_pd->authtok_size;
+    pd->newauthtok_type = old_pd->newauthtok_type;
+    pd->newauthtok_size = old_pd->newauthtok_size;
+
+    PD_STR_COPY(domain);
+    PD_STR_COPY(user);
+    PD_STR_COPY(service);
+    PD_STR_COPY(tty);
+    PD_STR_COPY(ruser);
+    PD_STR_COPY(rhost);
+    PD_MEM_COPY(authtok, old_pd->authtok_size);
+    PD_MEM_COPY(newauthtok, old_pd->newauthtok_size);
+    pd->cli_pid = old_pd->cli_pid;
+
+    *new_pd = pd;
+
+    return EOK;
+
+failed:
+    talloc_free(pd);
+    return ENOMEM;
+}
+
+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 37faca0409579e20f0744730d1be8f1a105b523d 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  |  163 +++++++++++---------------------------
 src/providers/krb5/krb5_child.c |  167 ++++++++++++++++++++++++++-------------
 src/sss_client/pam_sss.c        |   14 +++-
 src/sss_client/sss_cli.h        |    6 +-
 4 files changed, 176 insertions(+), 174 deletions(-)

diff --git a/src/providers/krb5/krb5_auth.c b/src/providers/krb5/krb5_auth.c
index 3b36b93..cca0132 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)
 {
@@ -1116,11 +1048,11 @@ static void krb5_child_done(struct tevent_req *subreq)
     int ret;
     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;
+    ssize_t pref_len = sizeof(CCACHE_ENV_NAME);
+    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);
@@ -1137,71 +1069,76 @@ static void krb5_child_done(struct tevent_req *subreq)
         return;
     }
 
-    if ((size_t) len < 3*sizeof(int32_t)) {
+    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);
+    SAFEALIGN_COPY_INT32(&msg_status, buf+p, &p);
 
-    msg_type = ((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);
 
-    msg_len = ((int32_t *)(buf+p));
-    p += sizeof(int32_t);
+        DEBUG(9, ("child response [%d][%d][%d].\n", msg_status, msg_type,
+                                                    msg_len));
 
-    DEBUG(4, ("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;
+        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 (msg_status != PAM_SUCCESS && msg_status != PAM_AUTHINFO_UNAVAIL) {
+        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 (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]));
+    if (kr->ccname == NULL) {
+        DEBUG(1, ("Missing ccache name in child response.\n"));
         ret = EINVAL;
         goto done;
     }
 
-    if (*msg_status == PAM_AUTHINFO_UNAVAIL) {
+    if (msg_status == PAM_AUTHINFO_UNAVAIL) {
         if (kr->srv != NULL) {
             fo_set_port_status(kr->srv, PORT_NOT_WORKING);
         }
@@ -1212,7 +1149,7 @@ static void krb5_child_done(struct tevent_req *subreq)
     }
 
     if (kr->kpasswd_srv != NULL) {
-        if (*msg_status == PAM_AUTHTOK_LOCK_BUSY) {
+        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);
@@ -1255,14 +1192,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..ac5fd75 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,39 @@ 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;
+
+    size = sizeof(int32_t);
 
-    resp->buf = talloc_array(resp, uint8_t,
-                             3*sizeof(int32_t) + len);
+    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 +318,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 +333,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 +347,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 +377,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 +507,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 +559,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 +571,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 +646,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 +679,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 +710,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 +741,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 +760,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 +926,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 31a3de8..8d6953b 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