[SSSD] [PATCH] Handle Krb5 password expiration warning

Sumit Bose sbose at redhat.com
Thu Apr 29 11:27:57 UTC 2010


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

> 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
> 

> 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
> 

> _______________________________________________
> sssd-devel mailing list
> sssd-devel at lists.fedorahosted.org
> https://fedorahosted.org/mailman/listinfo/sssd-devel

-------------- next part --------------
From a19ac991d1dd2622058fddcf8923083d6a341aa1 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 92244f4..a991a35 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 \
@@ -822,6 +823,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 b2f3017a6d69496e023f06500544380c454f42d4 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 0c08fe1..6bf50a7 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)
 {
@@ -1103,11 +1035,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);
@@ -1124,71 +1056,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);
         }
@@ -1199,7 +1136,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);
@@ -1242,14 +1179,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 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