[SSSD] [PATCH] IPA password migration (sssd-1-2 only)

Sumit Bose sbose at redhat.com
Fri Apr 23 09:08:34 UTC 2010


On Thu, Apr 22, 2010 at 10:44:40AM -0400, Stephen Gallagher wrote:
> On 04/15/2010 07:10 AM, Sumit Bose wrote:
> > Hi,
> >
> > these two patches provides a new attempt for the IPA password migration
> > feature. The first one wraps the Kerberos authentication in the Kerberos
> > provider into a tevent request to make it easy accessible for IPA
> > authentication. The LDAP provider already has all needed features as
> > tevent requests.
> >
> > The second patch changes IPA auth to use only tevent request from the
> > LDAP and Kerberos provider. The ipaMigrationEnabled flag is read from
> > the server, too.
> >
> > Both patches only apply to sssd-1-2. Because of the sync sysdb patches
> > in master it is not easy to merge the first patch. I will rewrite the
> > first patch and provider the patches for master if we agree that the way
> > the two patches here handle password migration is the way we want to go.
> >
> > bye,
> > Sumit
> 
> 
> Patch 0001: Nack.
> There are several places in krb5_auth_send() that return 
> tevent_req_error() that should not (e.g. we're offline and trying to do 
> a chpass or we don't handle a particular PAM command). We should reserve 
> tevent_req_error for internal errors like ENOMEM or broken configuration.

done

> 
> In krb5_find_ccache_step:
>      if (kr->ccname == NULL ||
>          (be_is_offline(state->be_ctx) && !kr->active_ccache_present &&
>              !kr->valid_tgt_present) ||
>          (!be_is_offline(state->be_ctx) && !kr->active_ccache_present))
> 
> Should be:
>      if((kr->ccname == NULL || !kr->active_ccache_present || 
> (be_is_offline(be_req->be_ctx) && !kr->valid_tgt_present))
> 

The original version is the correct version. The second version was
suggested by me on irc during a discussion on how to make this condition
better understandable, but it misses the case that we always want to
keep a valid tgt if we are offline. I have added a comment which tries
to explain the condition.

A new version is attached.

bye,
Sumit

> Patch 0002 review forthcoming.
> 
> -- 
> Stephen Gallagher
> RHCE 804006346421761
> 
> Delivering value year after year.
> Red Hat ranks #1 in value among software vendors.
> http://www.redhat.com/promo/vendor/
> _______________________________________________
> sssd-devel mailing list
> sssd-devel at lists.fedorahosted.org
> https://fedorahosted.org/mailman/listinfo/sssd-devel
-------------- next part --------------
From e9f62aa1e69362f4c2b9f8a83118071b3db1126a Mon Sep 17 00:00:00 2001
From: Sumit Bose <sbose at redhat.com>
Date: Mon, 29 Mar 2010 15:27:44 +0200
Subject: [PATCH 1/2] Make Kerberos authentication a tevent_req

To allow other providers to include Kerberos authentication the main
part is put into a tevent request.
---
 src/providers/krb5/krb5_auth.c |  726 +++++++++++++++++++++++-----------------
 src/providers/krb5/krb5_auth.h |    7 +-
 2 files changed, 426 insertions(+), 307 deletions(-)

diff --git a/src/providers/krb5/krb5_auth.c b/src/providers/krb5/krb5_auth.c
index 880930a..9f3592d 100644
--- a/src/providers/krb5/krb5_auth.c
+++ b/src/providers/krb5/krb5_auth.c
@@ -385,13 +385,59 @@ static struct krb5_ctx *get_krb5_ctx(struct be_req *be_req)
 
 static void krb_reply(struct be_req *req, int dp_err, int result);
 
+static int krb5_cleanup(void *ptr)
+{
+    struct krb5child_req *kr = talloc_get_type(ptr, struct krb5child_req);
+
+    if (kr == NULL) return EOK;
+
+    child_cleanup(kr->read_from_child_fd, kr->write_to_child_fd);
+    memset(kr, 0, sizeof(struct krb5child_req));
+
+    return EOK;
+}
+
+static errno_t krb5_setup(TALLOC_CTX *mem_ctx, struct pam_data *pd,
+                          struct krb5_ctx *krb5_ctx,
+                          struct krb5child_req **krb5_req)
+{
+    struct krb5child_req *kr = NULL;
+
+    kr = talloc_zero(mem_ctx, struct krb5child_req);
+    if (kr == NULL) {
+        DEBUG(1, ("talloc failed.\n"));
+        return ENOMEM;
+    }
+    kr->read_from_child_fd = -1;
+    kr->write_to_child_fd = -1;
+    kr->is_offline = false;
+    kr->active_ccache_present = true;
+    talloc_set_destructor((TALLOC_CTX *) kr, krb5_cleanup);
+
+    kr->pd = pd;
+    kr->krb5_ctx = krb5_ctx;
+
+    *krb5_req = kr;
+
+    return EOK;
+
+}
+
+struct handle_child_state {
+    struct tevent_context *ev;
+    struct krb5child_req *kr;
+    uint8_t *buf;
+    ssize_t len;
+};
+
 static void krb5_child_timeout(struct tevent_context *ev,
                                struct tevent_timer *te,
                                struct timeval tv, void *pvt)
 {
-    struct krb5child_req *kr = talloc_get_type(pvt, struct krb5child_req);
-    struct be_req *be_req = kr->req;
-    struct pam_data *pd = kr->pd;
+    struct tevent_req *req = talloc_get_type(pvt, struct tevent_req);
+    struct handle_child_state *state = tevent_req_data(req,
+                                                     struct handle_child_state);
+    struct krb5child_req *kr = state->kr;
     int ret;
 
     if (kr->timeout_handler == NULL) {
@@ -405,25 +451,24 @@ static void krb5_child_timeout(struct tevent_context *ev,
         DEBUG(1, ("kill failed [%d][%s].\n", errno, strerror(errno)));
     }
 
-    talloc_zfree(kr);
-
-    pd->pam_status = PAM_AUTHINFO_UNAVAIL;
-    be_mark_offline(be_req->be_ctx);
-
-    krb_reply(be_req, DP_ERR_OFFLINE, pd->pam_status);
+    tevent_req_error(req, ETIMEDOUT);
 }
 
-static errno_t activate_child_timeout_handler(struct krb5child_req *kr)
+static errno_t activate_child_timeout_handler(struct tevent_req *req,
+                                              struct tevent_context *ev,
+                                              struct krb5child_req *kr)
 {
     struct timeval tv;
+    struct handle_child_state *state = tevent_req_data(req,
+                                                    struct handle_child_state);
 
     tv = tevent_timeval_current();
     tv = tevent_timeval_add(&tv,
                             dp_opt_get_int(kr->krb5_ctx->opts,
                                            KRB5_AUTH_TIMEOUT),
                             0);
-    kr->timeout_handler = tevent_add_timer(kr->req->be_ctx->ev, kr, tv,
-                                           krb5_child_timeout, kr);
+    kr->timeout_handler = tevent_add_timer(ev, state, tv,
+                                           krb5_child_timeout, req);
     if (kr->timeout_handler == NULL) {
         DEBUG(1, ("tevent_add_timer failed.\n"));
         return ENOMEM;
@@ -432,61 +477,8 @@ static errno_t activate_child_timeout_handler(struct krb5child_req *kr)
     return EOK;
 }
 
-static int krb5_cleanup(void *ptr)
-{
-    struct krb5child_req *kr = talloc_get_type(ptr, struct krb5child_req);
-
-    if (kr == NULL) return EOK;
-
-    child_cleanup(kr->read_from_child_fd, kr->write_to_child_fd);
-    memset(kr, 0, sizeof(struct krb5child_req));
-
-    return EOK;
-}
-
-static errno_t krb5_setup(struct be_req *req, struct krb5child_req **krb5_req)
-{
-    struct krb5child_req *kr = NULL;
-    struct krb5_ctx *krb5_ctx;
-    struct pam_data *pd;
-    errno_t err;
-
-    pd = talloc_get_type(req->req_data, struct pam_data);
-
-    krb5_ctx = get_krb5_ctx(req);
-    if (krb5_ctx == NULL) {
-        DEBUG(1, ("Kerberos context not available.\n"));
-        err = EINVAL;
-        goto failed;
-    }
-
-    kr = talloc_zero(req, struct krb5child_req);
-    if (kr == NULL) {
-        DEBUG(1, ("talloc failed.\n"));
-        err = ENOMEM;
-        goto failed;
-    }
-    kr->read_from_child_fd = -1;
-    kr->write_to_child_fd = -1;
-    kr->is_offline = false;
-    kr->active_ccache_present = true;
-    talloc_set_destructor((TALLOC_CTX *) kr, krb5_cleanup);
-
-    kr->pd = pd;
-    kr->req = req;
-    kr->krb5_ctx = krb5_ctx;
-
-    *krb5_req = kr;
-
-    return EOK;
-
-failed:
-    talloc_zfree(kr);
-
-    return err;
-}
-
-static errno_t fork_child(struct krb5child_req *kr)
+static errno_t fork_child(struct tevent_req *req, struct tevent_context *ev,
+                          struct krb5child_req *kr)
 {
     int pipefd_to_child[2];
     int pipefd_from_child[2];
@@ -541,7 +533,7 @@ static errno_t fork_child(struct krb5child_req *kr)
         fd_nonblocking(kr->read_from_child_fd);
         fd_nonblocking(kr->write_to_child_fd);
 
-        err = activate_child_timeout_handler(kr);
+        err = activate_child_timeout_handler(req, ev, kr);
         if (err != EOK) {
             DEBUG(1, ("activate_child_timeout_handler failed.\n"));
         }
@@ -555,13 +547,6 @@ static errno_t fork_child(struct krb5child_req *kr)
     return EOK;
 }
 
-struct handle_child_state {
-    struct tevent_context *ev;
-    struct krb5child_req *kr;
-    uint8_t *buf;
-    ssize_t len;
-};
-
 static void handle_child_step(struct tevent_req *subreq);
 static void handle_child_done(struct tevent_req *subreq);
 
@@ -590,7 +575,7 @@ static struct tevent_req *handle_child_send(TALLOC_CTX *mem_ctx,
         goto fail;
     }
 
-    ret = fork_child(kr);
+    ret = fork_child(req, ev, kr);
     if (ret != EOK) {
         DEBUG(1, ("fork_child failed.\n"));
         goto fail;
@@ -675,25 +660,66 @@ static int handle_child_recv(struct tevent_req *req,
     return EOK;
 }
 
-static void get_user_attr_done(void *pvt, int err, struct ldb_result *res);
+static void krb5_get_user_attr_done(struct tevent_req *req);
 static void krb5_resolve_kdc_done(struct tevent_req *req);
 static void krb5_resolve_kpasswd_done(struct tevent_req *req);
-static void krb5_find_ccache_step(struct krb5child_req *kr);
+static void krb5_find_ccache_step(struct tevent_req *req);
 static void krb5_save_ccname_done(struct tevent_req *req);
 static void krb5_child_done(struct tevent_req *req);
 static void krb5_pam_handler_cache_done(struct tevent_req *treq);
 
-void krb5_pam_handler(struct be_req *be_req)
-{
+struct krb5_auth_state {
+    struct tevent_context *ev;
+    struct be_ctx *be_ctx;
     struct pam_data *pd;
+    struct krb5_ctx *krb5_ctx;
+    struct krb5child_req *kr;
+
+    int pam_status;
+    int dp_err;
+};
+
+int krb5_auth_recv(struct tevent_req *req, int *pam_status, int *dp_err)
+{
+    struct krb5_auth_state *state = tevent_req_data(req, struct krb5_auth_state);
+
+    *pam_status = state->pam_status;
+    *dp_err = state->dp_err;
+
+    TEVENT_REQ_RETURN_ON_ERROR(req);
+
+    return EOK;
+}
+
+struct tevent_req *krb5_auth_send(TALLOC_CTX *mem_ctx,
+                                  struct tevent_context *ev,
+                                  struct be_ctx *be_ctx,
+                                  struct pam_data *pd,
+                                  struct krb5_ctx *krb5_ctx)
+{
     const char **attrs;
-    int pam_status = PAM_SYSTEM_ERR;
-    int dp_err = DP_ERR_FATAL;
     int ret;
+    struct krb5_auth_state *state;
+    struct tevent_req *req;
+    struct tevent_req *subreq;
 
-    pd = talloc_get_type(be_req->req_data, struct pam_data);
 
-    switch (pd->cmd) {
+    req = tevent_req_create(mem_ctx, &state, struct krb5_auth_state);
+    if (req == NULL) {
+        DEBUG(1, ("tevent_req_create failed.\n"));
+        return NULL;
+    }
+
+    state->ev = ev;
+    state->be_ctx = be_ctx;
+    state->pd = pd;
+    state->krb5_ctx = krb5_ctx;
+    state->kr = NULL;
+    state->pam_status = PAM_SYSTEM_ERR;
+    state->dp_err = DP_ERR_FATAL;
+
+
+    switch (state->pd->cmd) {
         case SSS_PAM_AUTHENTICATE:
         case SSS_PAM_CHAUTHTOK:
         case SSS_PAM_CHAUTHTOK_PRELIM:
@@ -702,27 +728,32 @@ void krb5_pam_handler(struct be_req *be_req)
         case SSS_PAM_SETCRED:
         case SSS_PAM_OPEN_SESSION:
         case SSS_PAM_CLOSE_SESSION:
-            pam_status = PAM_SUCCESS;
-            dp_err = DP_ERR_OK;
+            state->pam_status = PAM_SUCCESS;
+            state->dp_err = DP_ERR_OK;
+            ret = EOK;
             goto done;
             break;
         default:
-            DEBUG(4, ("krb5 does not handles pam task %d.\n", pd->cmd));
-            pam_status = PAM_MODULE_UNKNOWN;
-            dp_err = DP_ERR_OK;
+            DEBUG(4, ("krb5 does not handles pam task %d.\n", state->pd->cmd));
+            state->pam_status = PAM_MODULE_UNKNOWN;
+            state->dp_err = DP_ERR_OK;
+            ret = EOK;
             goto done;
     }
 
-    if (be_is_offline(be_req->be_ctx) &&
-        (pd->cmd == SSS_PAM_CHAUTHTOK || pd->cmd == SSS_PAM_CHAUTHTOK_PRELIM)) {
+    if (be_is_offline(be_ctx) &&
+        (state->pd->cmd == SSS_PAM_CHAUTHTOK ||
+         state->pd->cmd == SSS_PAM_CHAUTHTOK_PRELIM)) {
         DEBUG(9, ("Password changes are not possible while offline.\n"));
-        pam_status = PAM_AUTHINFO_UNAVAIL;
-        dp_err = DP_ERR_OFFLINE;
+        state->pam_status = PAM_AUTHINFO_UNAVAIL;
+        state->dp_err = DP_ERR_OFFLINE;
+        ret = EOK;
         goto done;
     }
 
-    attrs = talloc_array(be_req, const char *, 6);
+    attrs = talloc_array(state, const char *, 6);
     if (attrs == NULL) {
+        ret = ENOMEM;
         goto done;
     }
 
@@ -733,230 +764,229 @@ void krb5_pam_handler(struct be_req *be_req)
     attrs[4] = SYSDB_GIDNUM;
     attrs[5] = NULL;
 
-    ret = sysdb_get_user_attr(be_req, be_req->be_ctx->sysdb,
-                              be_req->be_ctx->domain, pd->user, attrs,
-                              get_user_attr_done, be_req);
+    ret = krb5_setup(state, pd, krb5_ctx, &state->kr);
+    if (ret != EOK) {
+        DEBUG(1, ("krb5_setup failed.\n"));
+        goto done;
+    }
 
-    if (ret) {
+    subreq = sysdb_search_user_by_name_send(state, state->ev, be_ctx->sysdb,
+                                            NULL, be_ctx->domain,
+                                            state->pd->user, attrs);
+    if (subreq == NULL) {
+        DEBUG(1, ("sysdb_search_user_by_name_send failed.\n"));
+        ret = ENOMEM;
         goto done;
     }
 
-    return;
+    tevent_req_set_callback(subreq, krb5_get_user_attr_done, req);
 
-done:
-    pd->pam_status = pam_status;
+    return req;
 
-    krb_reply(be_req, dp_err, pd->pam_status);
+done:
+    if (ret == EOK) {
+        tevent_req_done(req);
+    } else {
+        tevent_req_error(req, ret);
+    }
+    tevent_req_post(req, state->ev);
+    return req;
 }
 
-static void get_user_attr_done(void *pvt, int err, struct ldb_result *res)
+static void krb5_get_user_attr_done(struct tevent_req *subreq)
 {
-    struct be_req *be_req = talloc_get_type(pvt, struct be_req);
-    struct krb5_ctx *krb5_ctx;
-    struct krb5child_req *kr = NULL;
-    struct tevent_req *req;
+    struct tevent_req *req = tevent_req_callback_data(subreq, struct tevent_req);
+    struct krb5_auth_state *state = tevent_req_data(req, struct krb5_auth_state);
+    struct krb5_ctx *krb5_ctx = state->kr->krb5_ctx;
     krb5_error_code kerr;
     int ret;
-    struct pam_data *pd = talloc_get_type(be_req->req_data, struct pam_data);
-    int pam_status = PAM_SYSTEM_ERR;
-    int dp_err = DP_ERR_FATAL;
+    struct pam_data *pd = state->pd;
+    struct krb5child_req *kr =state->kr;
     const char *ccache_file = NULL;
     const char *realm;
+    struct ldb_message *msg;
 
-    ret = krb5_setup(be_req, &kr);
-    if (ret != EOK) {
-        DEBUG(1, ("krb5_setup failed.\n"));
-        goto failed;
-    }
-
-    krb5_ctx = kr->krb5_ctx;
-
-    if (err != LDB_SUCCESS) {
-        DEBUG(5, ("sysdb search for upn of user [%s] failed.\n", pd->user));
-        goto failed;
+    ret = sysdb_search_user_recv(subreq, state, &msg);
+    talloc_zfree(subreq);
+    if (ret) {
+        state->pam_status = PAM_SYSTEM_ERR;
+        state->dp_err = DP_ERR_OK;
+        tevent_req_error(req, ret);
+        return;
     }
 
     realm = dp_opt_get_cstring(krb5_ctx->opts, KRB5_REALM);
     if (realm == NULL) {
         DEBUG(1, ("Missing Kerberos realm.\n"));
+        ret = ENOENT;
         goto failed;
     }
 
-    switch (res->count) {
-    case 0:
-        DEBUG(5, ("No attributes for user [%s] found.\n", pd->user));
-        goto failed;
-        break;
-
-    case 1:
-        kr->upn = ldb_msg_find_attr_as_string(res->msgs[0], SYSDB_UPN, NULL);
+    kr->upn = ldb_msg_find_attr_as_string(msg, SYSDB_UPN, NULL);
+    if (kr->upn == NULL) {
+        /* NOTE: this is a hack, works only in some environments */
+        kr->upn = talloc_asprintf(kr, "%s@%s", pd->user, realm);
         if (kr->upn == NULL) {
-            /* NOTE: this is a hack, works only in some environments */
-            kr->upn = talloc_asprintf(be_req, "%s@%s", pd->user, realm);
-            if (kr->upn == NULL) {
-                DEBUG(1, ("failed to build simple upn.\n"));
-                goto failed;
-            }
-            DEBUG(9, ("Using simple UPN [%s].\n", kr->upn));
+            DEBUG(1, ("failed to build simple upn.\n"));
+            ret = ENOMEM;
+            goto failed;
         }
+        DEBUG(9, ("Using simple UPN [%s].\n", kr->upn));
+    }
 
-        kr->homedir = ldb_msg_find_attr_as_string(res->msgs[0], SYSDB_HOMEDIR,
-                                                  NULL);
-        if (kr->homedir == NULL) {
-            DEBUG(4, ("Home directory for user [%s] not known.\n", pd->user));
-        }
+    kr->homedir = ldb_msg_find_attr_as_string(msg, SYSDB_HOMEDIR, NULL);
+    if (kr->homedir == NULL) {
+        DEBUG(4, ("Home directory for user [%s] not known.\n", pd->user));
+    }
 
-        kr->uid = ldb_msg_find_attr_as_uint64(res->msgs[0], SYSDB_UIDNUM, 0);
-        if (kr->uid == 0) {
-            DEBUG(4, ("UID for user [%s] not known.\n", pd->user));
-            goto failed;
-        }
+    kr->uid = ldb_msg_find_attr_as_uint64(msg, SYSDB_UIDNUM, 0);
+    if (kr->uid == 0) {
+        DEBUG(4, ("UID for user [%s] not known.\n", pd->user));
+        ret = ENOENT;
+        goto failed;
+    }
 
-        kr->gid = ldb_msg_find_attr_as_uint64(res->msgs[0], SYSDB_GIDNUM, 0);
-        if (kr->gid == 0) {
-            DEBUG(4, ("GID for user [%s] not known.\n", pd->user));
+    kr->gid = ldb_msg_find_attr_as_uint64(msg, SYSDB_GIDNUM, 0);
+    if (kr->gid == 0) {
+        DEBUG(4, ("GID for user [%s] not known.\n", pd->user));
+        ret = ENOENT;
+        goto failed;
+    }
+
+    ccache_file = ldb_msg_find_attr_as_string(msg, SYSDB_CCACHE_FILE, NULL);
+    if (ccache_file != NULL) {
+        ret = check_if_ccache_file_is_used(kr->uid, ccache_file,
+                                           &kr->active_ccache_present);
+        if (ret != EOK) {
+            DEBUG(1, ("check_if_ccache_file_is_used failed.\n"));
             goto failed;
         }
 
-        ccache_file = ldb_msg_find_attr_as_string(res->msgs[0],
-                                                  SYSDB_CCACHE_FILE,
-                                                  NULL);
-        if (ccache_file != NULL) {
-            ret = check_if_ccache_file_is_used(kr->uid, ccache_file,
-                                               &kr->active_ccache_present);
-            if (ret != EOK) {
-                DEBUG(1, ("check_if_ccache_file_is_used failed.\n"));
-                goto failed;
-            }
-
-            kerr = check_for_valid_tgt(ccache_file, realm, kr->upn,
-                                       &kr->valid_tgt_present);
-            if (kerr != 0) {
-                DEBUG(1, ("check_for_valid_tgt failed.\n"));
-                goto failed;
-            }
-        } else {
-            kr->active_ccache_present = false;
-            kr->valid_tgt_present = false;
-            DEBUG(4, ("No ccache file for user [%s] found.\n", pd->user));
+        kerr = check_for_valid_tgt(ccache_file, realm, kr->upn,
+                                   &kr->valid_tgt_present);
+        if (kerr != 0) {
+            DEBUG(1, ("check_for_valid_tgt failed.\n"));
+            ret = kerr;
+            goto failed;
         }
-        DEBUG(9, ("Ccache_file is [%s] and is %s active and TGT is %s valid.\n",
-                  ccache_file ? ccache_file : "not set",
-                  kr->active_ccache_present ? "" : "not",
-                  kr->valid_tgt_present ? "" : "not"));
-        kr->ccname = ccache_file;
-        break;
-
-    default:
-        DEBUG(1, ("A user search by name (%s) returned > 1 results!\n",
-                  pd->user));
-        goto failed;
-        break;
+    } else {
+        kr->active_ccache_present = false;
+        kr->valid_tgt_present = false;
+        DEBUG(4, ("No ccache file for user [%s] found.\n", pd->user));
     }
+    DEBUG(9, ("Ccache_file is [%s] and is %s active and TGT is %s valid.\n",
+              ccache_file ? ccache_file : "not set",
+              kr->active_ccache_present ? "" : "not",
+              kr->valid_tgt_present ? "" : "not"));
+    kr->ccname = ccache_file;
 
     kr->srv = NULL;
     kr->kpasswd_srv = NULL;
-    req = be_resolve_server_send(kr, be_req->be_ctx->ev, be_req->be_ctx,
-                                 krb5_ctx->service->name);
-    if (req == NULL) {
+    subreq = be_resolve_server_send(state, state->ev, state->be_ctx,
+                                    krb5_ctx->service->name);
+    if (subreq == NULL) {
         DEBUG(1, ("be_resolve_server_send failed.\n"));
+        ret = ENOMEM;
         goto failed;
     }
 
-    tevent_req_set_callback(req, krb5_resolve_kdc_done, kr);
+    tevent_req_set_callback(subreq, krb5_resolve_kdc_done, req);
 
     return;
 
 failed:
-    talloc_free(kr);
-
-    pd->pam_status = pam_status;
-    krb_reply(be_req, dp_err, pd->pam_status);
+    tevent_req_error(req, ret);
 }
 
-static void krb5_resolve_kdc_done(struct tevent_req *req)
+static void krb5_resolve_kdc_done(struct tevent_req *subreq)
 {
-    struct krb5child_req *kr = tevent_req_callback_data(req,
-                                                        struct krb5child_req);
+    struct tevent_req *req = tevent_req_callback_data(subreq, struct tevent_req);
+    struct krb5_auth_state *state = tevent_req_data(req, struct krb5_auth_state);
+    struct krb5child_req *kr = state->kr;
     int ret;
-    struct pam_data *pd = kr->pd;
-    struct be_req *be_req = kr->req;
 
-    ret = be_resolve_server_recv(req, &kr->srv);
-    talloc_zfree(req);
+    ret = be_resolve_server_recv(subreq, &kr->srv);
+    talloc_zfree(subreq);
     if (ret) {
         /* all servers have been tried and none
          * was found good, setting offline,
          * but we still have to call the child to setup
          * the ccache file. */
-        be_mark_offline(be_req->be_ctx);
+        be_mark_offline(state->be_ctx);
         kr->is_offline = true;
     } else {
-        if (pd->cmd == SSS_PAM_CHAUTHTOK &&
+        if (state->pd->cmd == SSS_PAM_CHAUTHTOK &&
             kr->krb5_ctx->kpasswd_service != NULL) {
-            req = be_resolve_server_send(kr, be_req->be_ctx->ev, be_req->be_ctx,
-                                         kr->krb5_ctx->kpasswd_service->name);
-            if (req == NULL) {
+            subreq = be_resolve_server_send(state, state->ev, state->be_ctx,
+                                            kr->krb5_ctx->kpasswd_service->name);
+            if (subreq == NULL) {
                 DEBUG(1, ("be_resolve_server_send failed.\n"));
+                ret = ENOMEM;
                 goto failed;
             }
 
-            tevent_req_set_callback(req, krb5_resolve_kpasswd_done, kr);
+            tevent_req_set_callback(subreq, krb5_resolve_kpasswd_done, req);
 
             return;
         }
     }
 
-    krb5_find_ccache_step(kr);
+    krb5_find_ccache_step(req);
     return;
 
 failed:
-    talloc_free(kr);
-
-    pd->pam_status = PAM_SYSTEM_ERR;
-    krb_reply(be_req, DP_ERR_FATAL, pd->pam_status);
+    tevent_req_error(req, ret);
 }
 
-static void krb5_resolve_kpasswd_done(struct tevent_req *req)
+static void krb5_resolve_kpasswd_done(struct tevent_req *subreq)
 {
-    struct krb5child_req *kr = tevent_req_callback_data(req,
-                                                        struct krb5child_req);
+    struct tevent_req *req = tevent_req_callback_data(subreq, struct tevent_req);
+    struct krb5_auth_state *state = tevent_req_data(req, struct krb5_auth_state);
     int ret;
-    struct pam_data *pd = kr->pd;
-    struct be_req *be_req = kr->req;
 
-    ret = be_resolve_server_recv(req, &kr->kpasswd_srv);
-    talloc_zfree(req);
+    ret = be_resolve_server_recv(subreq, &state->kr->kpasswd_srv);
+    talloc_zfree(subreq);
     if (ret) {
         /* all kpasswd servers have been tried and none was found good, but the
          * kdc seems ok. Password changes are not possible but
          * authentication. We return an PAM error here, but do not mark the
          * backend offline. */
 
-        talloc_free(kr);
-        pd->pam_status = PAM_AUTHTOK_LOCK_BUSY;
-        krb_reply(be_req, DP_ERR_OK, pd->pam_status);
+        state->pam_status = PAM_AUTHTOK_LOCK_BUSY;
+        state->dp_err = DP_ERR_OK;
+        tevent_req_done(req);
+        return;
     }
 
-    krb5_find_ccache_step(kr);
+    krb5_find_ccache_step(req);
 }
 
-static void krb5_find_ccache_step(struct krb5child_req *kr)
+static void krb5_find_ccache_step(struct tevent_req *req)
 {
+    struct krb5_auth_state *state = tevent_req_data(req, struct krb5_auth_state);
     int ret;
-    int pam_status = PAM_SYSTEM_ERR;
-    int dp_err = DP_ERR_FATAL;
-    struct pam_data *pd = kr->pd;
-    struct be_req *be_req = kr->req;
+    struct krb5child_req *kr = state->kr;
+    struct pam_data *pd = state->pd;
     char *msg;
     size_t offset = 0;
     bool private_path = false;
-    struct tevent_req *req = NULL;
-
+    struct tevent_req *subreq = NULL;
+
+    /* The ccache file should be (re)created if one of the following conditions
+     * is true:
+     * - it doesn't exist (kr->ccname == NULL)
+     * - the backend is online and the current ccache file is not used, i.e
+     *   the related user is currently not logged in
+     *   (!be_is_offline(state->be_ctx) && !kr->active_ccache_present)
+     * - the backend is offline and the current cache file not used and
+     *   it does not contain a valid tgt
+     *   (be_is_offline(state->be_ctx) &&
+     *    !kr->active_ccache_present && !kr->valid_tgt_present)
+     */
     if (kr->ccname == NULL ||
-        (be_is_offline(be_req->be_ctx) && !kr->active_ccache_present &&
+        (be_is_offline(state->be_ctx) && !kr->active_ccache_present &&
             !kr->valid_tgt_present) ||
-        (!be_is_offline(be_req->be_ctx) && !kr->active_ccache_present)) {
+        (!be_is_offline(state->be_ctx) && !kr->active_ccache_present)) {
             DEBUG(9, ("Recreating  ccache file.\n"));
             if (kr->ccname != NULL) {
                 if (strncmp(kr->ccname, "FILE:", 5) == 0) {
@@ -965,12 +995,14 @@ static void krb5_find_ccache_step(struct krb5child_req *kr)
                 if (kr->ccname[offset] != '/') {
                     DEBUG(1, ("Ccache file name [%s] is not an absolute path.\n",
                               kr->ccname + offset));
+                    ret = EINVAL;
                     goto done;
                 }
                 ret = unlink(kr->ccname + offset);
                 if (ret == -1 && errno != ENOENT) {
-                    DEBUG(1, ("unlink [%s] failed [%d][%s].\n", kr->ccname,
-                             errno, strerror(errno)));
+                    ret = errno;
+                    DEBUG(1, ("unlink [%s] failed [%d][%s].\n", kr->ccname, ret,
+                                                                strerror(ret)));
                     goto done;
                 }
             }
@@ -980,6 +1012,7 @@ static void krb5_find_ccache_step(struct krb5child_req *kr)
                                                 true, &private_path);
             if (kr->ccname == NULL) {
                 DEBUG(1, ("expand_ccname_template failed.\n"));
+                ret = ENOMEM;
                 goto done;
             }
 
@@ -992,7 +1025,7 @@ static void krb5_find_ccache_step(struct krb5child_req *kr)
             }
     }
 
-    if (be_is_offline(be_req->be_ctx)) {
+    if (be_is_offline(state->be_ctx)) {
         DEBUG(9, ("Preparing for offline operation.\n"));
         kr->is_offline = true;
 
@@ -1001,6 +1034,7 @@ static void krb5_find_ccache_step(struct krb5child_req *kr)
             msg = talloc_asprintf(pd, "%s=%s", CCACHE_ENV_NAME, kr->ccname);
             if (msg == NULL) {
                 DEBUG(1, ("talloc_asprintf failed.\n"));
+                ret = ENOMEM;
                 goto done;
             }
 
@@ -1010,49 +1044,55 @@ static void krb5_find_ccache_step(struct krb5child_req *kr)
                 DEBUG(1, ("pam_add_response failed.\n"));
             }
 
-            pam_status = PAM_AUTHINFO_UNAVAIL;
-            dp_err = DP_ERR_OFFLINE;
+            state->pam_status = PAM_AUTHINFO_UNAVAIL;
+            state->dp_err = DP_ERR_OFFLINE;
+            ret = EOK;
             goto done;
         }
         memset(pd->authtok, 0, pd->authtok_size);
         pd->authtok_size = 0;
 
         if (kr->active_ccache_present) {
-            req = krb5_save_ccname_send(kr, be_req->be_ctx->ev,
-                                        be_req->be_ctx->sysdb,
-                                        be_req->be_ctx->domain, pd->user,
-                                        kr->ccname);
-            if (req == NULL) {
+            subreq = krb5_save_ccname_send(state, state->ev,
+                                           state->be_ctx->sysdb,
+                                           state->be_ctx->domain, pd->user,
+                                           kr->ccname);
+            if (subreq == NULL) {
                 DEBUG(1, ("krb5_save_ccname_send failed.\n"));
+                ret = ENOMEM;
                 goto done;
             }
 
-            tevent_req_set_callback(req, krb5_save_ccname_done, kr);
+            tevent_req_set_callback(subreq, krb5_save_ccname_done, req);
             return;
         }
     }
 
-    req = handle_child_send(kr, be_req->be_ctx->ev, kr);
-    if (req == NULL) {
+    subreq = handle_child_send(state, state->ev, kr);
+    if (subreq == NULL) {
         DEBUG(1, ("handle_child_send failed.\n"));
+        ret = ENOMEM;
         goto done;
     }
 
-    tevent_req_set_callback(req, krb5_child_done, kr);
+    tevent_req_set_callback(subreq, krb5_child_done, req);
     return;
 
 done:
-    talloc_free(kr);
-    pd->pam_status = pam_status;
-    krb_reply(be_req, dp_err, pd->pam_status);
+    if (ret == EOK) {
+        tevent_req_done(req);
+    } else {
+        tevent_req_error(req, ret);
+    }
 }
 
-static void krb5_child_done(struct tevent_req *req)
+static void krb5_child_done(struct tevent_req *subreq)
 {
-    struct krb5child_req *kr = tevent_req_callback_data(req,
-                                                        struct krb5child_req);
-    struct pam_data *pd = kr->pd;
-    struct be_req *be_req = kr->req;
+    struct tevent_req *req = tevent_req_callback_data(subreq, struct tevent_req);
+    struct krb5_auth_state *state = tevent_req_data(req, struct krb5_auth_state);
+
+    struct krb5child_req *kr = state->kr;
+    struct pam_data *pd = state->pd;
     int ret;
     uint8_t *buf = NULL;
     ssize_t len = -1;
@@ -1061,19 +1101,25 @@ static void krb5_child_done(struct tevent_req *req)
     int32_t *msg_status;
     int32_t *msg_type;
     int32_t *msg_len;
-    int pam_status = PAM_SYSTEM_ERR;
-    int dp_err = DP_ERR_FATAL;
 
-    ret = handle_child_recv(req, pd, &buf, &len);
+    ret = handle_child_recv(subreq, pd, &buf, &len);
     talloc_zfree(kr->timeout_handler);
-    talloc_zfree(req);
+    talloc_zfree(subreq);
     if (ret != EOK) {
         DEBUG(1, ("child failed (%d [%s])\n", ret, strerror(ret)));
-        goto done;
+        if (ret == ETIMEDOUT) {
+            state->pam_status = PAM_AUTHINFO_UNAVAIL;
+            state->dp_err = DP_ERR_OFFLINE;
+            tevent_req_done(req);
+        } else {
+            tevent_req_error(req, ret);
+        }
+        return;
     }
 
     if ((size_t) len < 3*sizeof(int32_t)) {
         DEBUG(1, ("message too short.\n"));
+        ret = EINVAL;
         goto done;
     }
 
@@ -1096,22 +1142,25 @@ static void krb5_child_done(struct tevent_req *req)
     }
 
     if (*msg_status != PAM_SUCCESS && *msg_status != PAM_AUTHINFO_UNAVAIL) {
-        pam_status = *msg_status;
-        dp_err = DP_ERR_OK;
+        state->pam_status = *msg_status;
+        state->dp_err = DP_ERR_OK;
 
         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"));
         }
 
+        ret = EOK;
         goto done;
     } else {
-        pd->pam_status = *msg_status;
+        state->pam_status = *msg_status;
     }
 
     if (*msg_status == PAM_SUCCESS && pd->cmd == SSS_PAM_CHAUTHTOK_PRELIM) {
-        pam_status = PAM_SUCCESS;
-        dp_err = DP_ERR_OK;
+        state->pam_status = PAM_SUCCESS;
+        state->dp_err = DP_ERR_OK;
+        ret = EOK;
         goto done;
     }
 
@@ -1122,11 +1171,13 @@ static void krb5_child_done(struct tevent_req *req)
                                    *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]));
+        ret = EINVAL;
         goto done;
     }
 
@@ -1134,7 +1185,7 @@ static void krb5_child_done(struct tevent_req *req)
         if (kr->srv != NULL) {
             fo_set_port_status(kr->srv, PORT_NOT_WORKING);
         }
-        be_mark_offline(be_req->be_ctx);
+        be_mark_offline(state->be_ctx);
         kr->is_offline = true;
     } else if (kr->srv != NULL) {
         fo_set_port_status(kr->srv, PORT_WORKING);
@@ -1149,37 +1200,38 @@ static void krb5_child_done(struct tevent_req *req)
     }
 
     struct sysdb_attrs *attrs;
-    attrs = sysdb_new_attrs(kr);
+    attrs = sysdb_new_attrs(state);
     ret = sysdb_attrs_add_string(attrs, SYSDB_CCACHE_FILE, kr->ccname);
     if (ret != EOK) {
         DEBUG(1, ("sysdb_attrs_add_string failed.\n"));
         goto done;
     }
 
-    req = krb5_save_ccname_send(kr, be_req->be_ctx->ev, be_req->be_ctx->sysdb,
-                                be_req->be_ctx->domain, pd->user, kr->ccname);
-    if (req == NULL) {
+    subreq = krb5_save_ccname_send(state, state->ev, state->be_ctx->sysdb,
+                                   state->be_ctx->domain, pd->user, kr->ccname);
+    if (subreq == NULL) {
         DEBUG(1, ("krb5_save_ccname_send failed.\n"));
+        ret = ENOMEM;
         goto done;
     }
 
-    tevent_req_set_callback(req, krb5_save_ccname_done, kr);
+    tevent_req_set_callback(subreq, krb5_save_ccname_done, req);
     return;
 done:
-    talloc_free(kr);
-    pd->pam_status = pam_status;
-    krb_reply(be_req, dp_err, pd->pam_status);
+    if (ret == EOK) {
+        tevent_req_done(req);
+    } else {
+        tevent_req_error(req, ret);
+    }
 }
 
-static void krb5_save_ccname_done(struct tevent_req *req)
+static void krb5_save_ccname_done(struct tevent_req *subreq)
 {
-    struct krb5child_req *kr = tevent_req_callback_data(req,
-                                                        struct krb5child_req);
-    struct pam_data *pd = kr->pd;
-    struct be_req *be_req = kr->req;
+    struct tevent_req *req = tevent_req_callback_data(subreq, struct tevent_req);
+    struct krb5_auth_state *state = tevent_req_data(req, struct krb5_auth_state);
+    struct krb5child_req *kr = state->kr;
+    struct pam_data *pd = state->pd;
     struct krb5_ctx *krb5_ctx = kr->krb5_ctx;
-    int pam_status = PAM_SYSTEM_ERR;
-    int dp_err = DP_ERR_FATAL;
     int ret;
     char *password = NULL;
 
@@ -1187,40 +1239,43 @@ static void krb5_save_ccname_done(struct tevent_req *req)
         ret = add_krb5_env(krb5_ctx->opts, kr->ccname, pd);
         if (ret != EOK) {
             DEBUG(1, ("add_krb5_env failed.\n"));
-            goto failed;
+            goto done;
         }
     }
 
-    ret = sysdb_set_user_attr_recv(req);
-    talloc_zfree(req);
+    ret = sysdb_set_user_attr_recv(subreq);
+    talloc_zfree(subreq);
     if (ret != EOK) {
         DEBUG(1, ("Saving ccache name failed.\n"));
-        goto failed;
+        tevent_req_error(req, ret);
+        return;
     }
 
     if (kr->is_offline) {
         DEBUG(4, ("Backend is marked offline, retry later!\n"));
-        pam_status = PAM_AUTHINFO_UNAVAIL;
-        dp_err = DP_ERR_OFFLINE;
-        goto failed;
+        state->pam_status = PAM_AUTHINFO_UNAVAIL;
+        state->dp_err = DP_ERR_OFFLINE;
+        ret = EOK;
+        goto done;
     }
 
-    if (be_req->be_ctx->domain->cache_credentials == TRUE) {
+    if (state->be_ctx->domain->cache_credentials == TRUE) {
 
         /* password caching failures are not fatal errors */
-        pd->pam_status = PAM_SUCCESS;
+        state->pam_status = PAM_SUCCESS;
+        state->dp_err = DP_ERR_OK;
 
         switch(pd->cmd) {
             case SSS_PAM_AUTHENTICATE:
             case SSS_PAM_CHAUTHTOK_PRELIM:
-                password = talloc_size(be_req, pd->authtok_size + 1);
+                password = talloc_size(state, pd->authtok_size + 1);
                 if (password != NULL) {
                     memcpy(password, pd->authtok, pd->authtok_size);
                     password[pd->authtok_size] = '\0';
                 }
                 break;
             case SSS_PAM_CHAUTHTOK:
-                password = talloc_size(be_req, pd->newauthtok_size + 1);
+                password = talloc_size(state, pd->newauthtok_size + 1);
                 if (password != NULL) {
                     memcpy(password, pd->newauthtok, pd->newauthtok_size);
                     password[pd->newauthtok_size] = '\0';
@@ -1232,49 +1287,55 @@ static void krb5_save_ccname_done(struct tevent_req *req)
 
         if (password == NULL) {
             DEBUG(0, ("password not available, offline auth may not work.\n"));
-            goto failed;
+            ret = EOK; /* password caching failures are not fatal errors */
+            goto done;
         }
 
         talloc_set_destructor((TALLOC_CTX *)password, password_destructor);
 
-        req = sysdb_cache_password_send(be_req, be_req->be_ctx->ev,
-                                        be_req->be_ctx->sysdb, NULL,
-                                        be_req->be_ctx->domain, pd->user,
-                                        password);
-        if (req == NULL) {
-            DEBUG(2, ("cache_password_send failed, offline auth may not work.\n"));
-            goto failed;
+        subreq = sysdb_cache_password_send(state, state->ev,
+                                           state->be_ctx->sysdb, NULL,
+                                           state->be_ctx->domain, pd->user,
+                                           password);
+        if (subreq == NULL) {
+            DEBUG(2, ("cache_password_send failed, "
+                      "offline auth may not work.\n"));
+            ret = EOK; /* password caching failures are not fatal errors */
+            goto done;
         }
-        tevent_req_set_callback(req, krb5_pam_handler_cache_done, be_req);
+        tevent_req_set_callback(subreq, krb5_pam_handler_cache_done, req);
         return;
     }
 
-    pam_status = PAM_SUCCESS;
-    dp_err = DP_ERR_OK;
-
-failed:
-    talloc_free(kr);
+    state->pam_status = PAM_SUCCESS;
+    state->dp_err = DP_ERR_OK;
+    ret = EOK;
 
-    pd->pam_status = pam_status;
-    krb_reply(be_req, dp_err, pd->pam_status);
+done:
+    if (ret == EOK) {
+        tevent_req_done(req);
+    } else {
+        tevent_req_error(req, ret);
+    }
 }
 
 static void krb5_pam_handler_cache_done(struct tevent_req *subreq)
 {
-    struct be_req *be_req = tevent_req_callback_data(subreq, struct be_req);
+    struct tevent_req *req = tevent_req_callback_data(subreq, struct tevent_req);
     int ret;
 
     /* password caching failures are not fatal errors */
     ret = sysdb_cache_password_recv(subreq);
     talloc_zfree(subreq);
 
-    /* so we just log it any return */
+    /* password caching failures are not fatal errors, 
+     * so we just log it and return */
     if (ret) {
         DEBUG(2, ("Failed to cache password (%d)[%s]!?\n",
                   ret, strerror(ret)));
     }
 
-    krb_reply(be_req, DP_ERR_OK, PAM_SUCCESS);
+    tevent_req_done(req);
 }
 
 static void krb_reply(struct be_req *req, int dp_err, int result)
@@ -1282,3 +1343,56 @@ static void krb_reply(struct be_req *req, int dp_err, int result)
     req->fn(req, dp_err, result, NULL);
 }
 
+void krb5_auth_done(struct tevent_req *req);
+
+void krb5_pam_handler(struct be_req *be_req)
+{
+    struct tevent_req *req;
+    struct pam_data *pd;
+    struct krb5_ctx *krb5_ctx;
+
+    pd = talloc_get_type(be_req->req_data, struct pam_data);
+
+    krb5_ctx = get_krb5_ctx(be_req);
+    if (krb5_ctx == NULL) {
+        DEBUG(1, ("Kerberos context not available.\n"));
+        goto failed;
+    }
+
+    req = krb5_auth_send(be_req, be_req->be_ctx->ev, be_req->be_ctx, pd,
+                         krb5_ctx);
+    if (req == NULL) {
+        DEBUG(1, ("krb5_auth_send failed.\n"));
+        goto failed;
+    }
+
+    tevent_req_set_callback(req, krb5_auth_done, be_req);
+
+    return;
+
+failed:
+    pd->pam_status = PAM_SYSTEM_ERR;
+    krb_reply(be_req, DP_ERR_FATAL, pd->pam_status);
+}
+
+void krb5_auth_done(struct tevent_req *req)
+{
+    int ret;
+    struct be_req *be_req = tevent_req_callback_data(req, struct be_req);
+    int pam_status;
+    int dp_err;
+    struct pam_data *pd;
+
+    pd = talloc_get_type(be_req->req_data, struct pam_data);
+
+    ret = krb5_auth_recv(req, &pam_status, &dp_err);
+    talloc_zfree(req);
+    if (ret) {
+        pd->pam_status = PAM_SYSTEM_ERR;
+        dp_err = DP_ERR_OK;
+    } else {
+        pd->pam_status = pam_status;
+    }
+
+    krb_reply(be_req, dp_err, pd->pam_status);
+}
diff --git a/src/providers/krb5/krb5_auth.h b/src/providers/krb5/krb5_auth.h
index 9f8c414..e614d5c 100644
--- a/src/providers/krb5/krb5_auth.h
+++ b/src/providers/krb5/krb5_auth.h
@@ -44,7 +44,6 @@ struct krb5child_req {
     int read_from_child_fd;
     int write_to_child_fd;
 
-    struct be_req *req;
     struct pam_data *pd;
     struct krb5_ctx *krb5_ctx;
 
@@ -99,4 +98,10 @@ struct krb5_ctx {
 
 void krb5_pam_handler(struct be_req *be_req);
 
+struct tevent_req *krb5_auth_send(TALLOC_CTX *mem_ctx,
+                                  struct tevent_context *ev,
+                                  struct be_ctx *be_ctx,
+                                  struct pam_data *pd,
+                                  struct krb5_ctx *krb5_ctx);
+int krb5_auth_recv(struct tevent_req *req, int *pam_status, int *dp_err);
 #endif /* __KRB5_AUTH_H__ */
-- 
1.6.6.1

-------------- next part --------------
From a415b4540270095671593d3f5ccc5413d1950635 Mon Sep 17 00:00:00 2001
From: Sumit Bose <sbose at redhat.com>
Date: Mon, 29 Mar 2010 13:41:41 +0200
Subject: [PATCH 2/2] New version of IPA auth and password migration

The current version modified some global structures to be able to use
Kerberos and LDAP authentication during the IPA password migration. This
new version only uses tevent requests.

Additionally the ipaMigrationEnabled attribute is read from the IPA
server to see if password migration is allowed or not.
---
 src/providers/ipa/ipa_auth.c     |  529 +++++++++++++++++++++++++-------------
 src/providers/ipa/ipa_common.h   |    8 +-
 src/providers/ipa/ipa_init.c     |   83 +++++--
 src/providers/krb5/krb5_common.c |    4 +-
 4 files changed, 425 insertions(+), 199 deletions(-)

diff --git a/src/providers/ipa/ipa_auth.c b/src/providers/ipa/ipa_auth.c
index 86b72e4..16bb407 100644
--- a/src/providers/ipa/ipa_auth.c
+++ b/src/providers/ipa/ipa_auth.c
@@ -31,283 +31,460 @@
 #include "providers/krb5/krb5_auth.h"
 #include "providers/ipa/ipa_common.h"
 
-struct ipa_auth_ctx {
-    struct sdap_auth_ctx *sdap_auth_ctx;
-    struct krb5_ctx *krb5_ctx;
-    struct be_req *be_req;
-    be_async_callback_t callback;
-    void *pvt;
-    bool password_migration;
-
-    int dp_err_type;
-    int errnum;
-    char *errstr;
-};
+#define IPA_CONFIG_MIRATION_ENABLED "ipaMigrationEnabled"
+#define IPA_CONFIG_SEARCH_BASE_TEMPLATE "cn=etc,%s"
+#define IPA_CONFIG_FILTER "(&(cn=ipaConfig)(objectClass=ipaGuiConfig))"
 
-static void ipa_auth_reply(struct ipa_auth_ctx *ipa_auth_ctx)
+static void ipa_auth_reply(struct be_req *be_req, int dp_err, int result)
 {
-    struct pam_data *pd;
-    struct be_req *be_req = ipa_auth_ctx->be_req;
-    be_req->fn = ipa_auth_ctx->callback;
-    be_req->pvt = ipa_auth_ctx->pvt;
-    be_req->be_ctx->bet_info[BET_AUTH].pvt_bet_data = ipa_auth_ctx->krb5_ctx;
-    pd = talloc_get_type(be_req->req_data, struct pam_data);
-    int dp_err_type = ipa_auth_ctx->dp_err_type;
-    char *errstr = ipa_auth_ctx->errstr;
-
-    talloc_zfree(ipa_auth_ctx);
-    DEBUG(9, ("sending [%d] [%d] [%s].\n", dp_err_type, pd->pam_status,
-                                           errstr));
-
-    be_req->fn(be_req, dp_err_type, pd->pam_status, errstr);
+    be_req->fn(be_req, dp_err, result, NULL);
 }
 
-struct ipa_auth_handler_state {
+struct get_password_migration_flag_state {
     struct tevent_context *ev;
-
-    int dp_err_type;
-    int errnum;
-    char *errstr;
+    struct sdap_auth_ctx *sdap_auth_ctx;
+    struct sdap_handle *sh;
+    enum sdap_result result;
+    struct fo_server *srv;
+    char *ipa_domain;
+    bool password_migration;
 };
 
-static void ipa_auth_handler_callback(struct be_req *be_req,
-                                   int dp_err_type,
-                                   int errnum,
-                                   const char *errstr);
+static void get_password_migration_flag_auth_done(struct tevent_req *subreq);
+static void get_password_migration_flag_done(struct tevent_req *subreq);
 
-static struct tevent_req *ipa_auth_handler_send(TALLOC_CTX *memctx,
+static struct tevent_req *get_password_migration_flag_send(TALLOC_CTX *memctx,
                                             struct tevent_context *ev,
-                                            struct be_req *be_req,
-                                            be_req_fn_t auth_handler)
+                                            struct sdap_auth_ctx *sdap_auth_ctx,
+                                            char *ipa_domain)
 {
-    struct ipa_auth_handler_state *state;
-    struct tevent_req *req;
+    int ret;
+    struct tevent_req *req, *subreq;
+    struct get_password_migration_flag_state *state;
+
+    if (sdap_auth_ctx == NULL || ipa_domain == NULL) {
+        DEBUG(1, ("Missing parameter.\n"));
+        return NULL;
+    }
 
-    req = tevent_req_create(memctx, &state, struct ipa_auth_handler_state);
+    req = tevent_req_create(memctx, &state,
+                            struct get_password_migration_flag_state);
     if (req == NULL) {
         DEBUG(1, ("tevent_req_create failed.\n"));
         return NULL;
     }
 
     state->ev = ev;
+    state->sdap_auth_ctx = sdap_auth_ctx;
+    state->sh = NULL;
+    state->result = SDAP_ERROR;
+    state->srv = NULL;
+    state->password_migration = false;
+    state->ipa_domain = ipa_domain;
+
+    /* We request to use StartTLS here, because if password migration is
+     * enabled we will use this connection for authentication, too. */
+    ret = dp_opt_set_bool(sdap_auth_ctx->opts->basic, SDAP_ID_TLS, true);
+    if (ret != EOK) {
+        DEBUG(1, ("Failed to set SDAP_ID_TLS to true.\n"));
+        goto fail;
+    }
 
-    be_req->fn = ipa_auth_handler_callback;
-    be_req->pvt = req;
-
-    auth_handler(be_req);
+    subreq = sdap_cli_connect_send(state, ev, sdap_auth_ctx->opts,
+                                   sdap_auth_ctx->be, sdap_auth_ctx->service,
+                                   NULL);
+    if (subreq == NULL) {
+        DEBUG(1, ("sdap_cli_connect_send failed.\n"));
+        goto fail;
+    }
+    tevent_req_set_callback(subreq, get_password_migration_flag_auth_done,
+                            req);
 
     return req;
+
+fail:
+    talloc_zfree(req);
+    return NULL;
 }
 
-static void ipa_auth_handler_callback(struct be_req *be_req,
-                                   int dp_err_type,
-                                   int errnum,
-                                   const char *errstr)
+static void get_password_migration_flag_auth_done(struct tevent_req *subreq)
 {
-    struct tevent_req *req = talloc_get_type(be_req->pvt, struct tevent_req);
-    struct ipa_auth_handler_state *state = tevent_req_data(req,
-                                                 struct ipa_auth_handler_state);
+    struct tevent_req *req = tevent_req_callback_data(subreq,
+                                                      struct tevent_req);
+    struct get_password_migration_flag_state *state = tevent_req_data(req,
+                                      struct get_password_migration_flag_state);
+    int ret;
+    char *ldap_basedn;
+    char *search_base;
+    const char **attrs;
+
+    ret = sdap_cli_connect_recv(subreq, state, &state->sh, NULL);
+    talloc_zfree(subreq);
+    if (ret) {
+        DEBUG(1, ("sdap_auth request failed.\n"));
+        tevent_req_error(req, ret);
+        return;
+    }
+
+    ret = domain_to_basedn(state, state->ipa_domain, &ldap_basedn);
+    if (ret != EOK) {
+        DEBUG(1, ("domain_to_basedn failed.\n"));
+        tevent_req_error(req, ret);
+        return;
+    }
 
-    DEBUG(9, ("received from handler [%d] [%d] [%s].\n", dp_err_type, errnum,
-                                                         errstr));
-    state->dp_err_type = dp_err_type;
-    state->errnum = errnum;
-    state->errstr = talloc_strdup(state, errstr);
+    search_base = talloc_asprintf(state, IPA_CONFIG_SEARCH_BASE_TEMPLATE,
+                                  ldap_basedn);
+    if (search_base == NULL) {
+        DEBUG(1, ("talloc_asprintf failed.\n"));
+        tevent_req_error(req, ENOMEM);
+        return;
+    }
 
-    tevent_req_post(req, state->ev);
-    tevent_req_done(req);
-    return;
+    attrs = talloc_array(state, const char*, 2);
+    if (attrs == NULL) {
+        DEBUG(1, ("talloc_array failed.\n"));
+        tevent_req_error(req, ENOMEM);
+        return;
+    }
+
+    attrs[0] = IPA_CONFIG_MIRATION_ENABLED;
+    attrs[1] = NULL;
+
+    subreq = sdap_get_generic_send(state, state->ev, state->sdap_auth_ctx->opts,
+                                   state->sh, search_base, LDAP_SCOPE_SUBTREE,
+                                   IPA_CONFIG_FILTER, attrs, NULL, 0);
+    if (!subreq) {
+        tevent_req_error(req, ENOMEM);
+        return;
+    }
+
+    tevent_req_set_callback(subreq, get_password_migration_flag_done, req);
 }
 
-static int ipa_auth_handler_recv(struct tevent_req *req, TALLOC_CTX *memctx,
-                              int *dp_err_type, int *errnum,
-                              char **errstr)
+static void get_password_migration_flag_done(struct tevent_req *subreq)
 {
-    struct ipa_auth_handler_state *state = tevent_req_data(req,
-                                                 struct ipa_auth_handler_state);
-    enum tevent_req_state tstate;
-    uint64_t err;
-
-    if (tevent_req_is_error(req, &tstate, &err)) {
-        if (err) return err;
-        return EIO;
+    struct tevent_req *req = tevent_req_callback_data(subreq,
+                                                      struct tevent_req);
+    struct get_password_migration_flag_state *state = tevent_req_data(req,
+                                      struct get_password_migration_flag_state);
+    int ret;
+    size_t reply_count;
+    struct sysdb_attrs **reply = NULL;
+    const char *value = NULL;
+
+    ret = sdap_get_generic_recv(subreq, state, &reply_count, &reply);
+    talloc_zfree(subreq);
+    if (ret) {
+        tevent_req_error(req, ret);
+        return;
+    }
+
+    if (reply_count != 1) {
+        DEBUG(1, ("Unexpected number of results, expected 1, got %d.\n",
+                  reply_count));
+        tevent_req_error(req, EINVAL);
+        return;
+    }
+
+    ret = sysdb_attrs_get_string(reply[0], IPA_CONFIG_MIRATION_ENABLED, &value);
+    if (strcasecmp(value, "true") == 0) {
+        state->password_migration = true;
     }
 
-    *dp_err_type = state->dp_err_type;
-    *errnum = state->errnum;
-    *errstr = talloc_steal(memctx, state->errstr);
+    tevent_req_done(req);
+}
+
+static int get_password_migration_flag_recv(struct tevent_req *req,
+                                            TALLOC_CTX *mem_ctx,
+                                            bool *password_migration,
+                                            struct sdap_handle **sh)
+{
+    struct get_password_migration_flag_state *state = tevent_req_data(req,
+                                      struct get_password_migration_flag_state);
+
+    TEVENT_REQ_RETURN_ON_ERROR(req);
+
+    *password_migration = state->password_migration;
+    if (sh != NULL) {
+        *sh = talloc_steal(mem_ctx, state->sh);
+    }
 
     return EOK;
 }
 
 
+struct ipa_auth_state {
+    struct be_req *be_req;
+    struct tevent_context *ev;
+    struct ipa_auth_ctx *ipa_auth_ctx;
+    struct pam_data *pd;
+    bool password_migration;
+    struct sdap_handle *sh;
+};
+
 static void ipa_auth_handler_done(struct tevent_req *req);
+static void ipa_get_migration_flag_done(struct tevent_req *req);
+static void ipa_auth_get_user_dn_done(struct tevent_req *req);
 static void ipa_auth_ldap_done(struct tevent_req *req);
 static void ipa_auth_handler_retry_done(struct tevent_req *req);
 
 void ipa_auth(struct be_req *be_req)
 {
     struct tevent_req *req;
-    struct ipa_auth_ctx *ipa_auth_ctx;
-    struct sdap_id_ctx *sdap_id_ctx;
+    struct ipa_auth_state *state;
 
-    ipa_auth_ctx = talloc_zero(be_req, struct ipa_auth_ctx);
-    if (ipa_auth_ctx == NULL) {
-        DEBUG(1, ("talloc failed.\n"));
-        be_req->fn(be_req, DP_ERR_FATAL, PAM_SYSTEM_ERR, NULL);
-    }
-
-    ipa_auth_ctx->callback = be_req->fn;
-    ipa_auth_ctx->pvt = be_req->pvt;
-
-    ipa_auth_ctx->be_req = be_req;
-
-    ipa_auth_ctx->sdap_auth_ctx = talloc_zero(ipa_auth_ctx,
-                                              struct sdap_auth_ctx);
-    if (ipa_auth_ctx->sdap_auth_ctx == NULL) {
-        DEBUG(1, ("talloc failed.\n"));
+    state = talloc_zero(be_req, struct ipa_auth_state);
+    if (state == NULL) {
+        DEBUG(1, ("talloc_zero failed.\n"));
         goto fail;
     }
 
-    sdap_id_ctx = talloc_get_type(
-                              be_req->be_ctx->bet_info[BET_ID].pvt_bet_data,
-                              struct sdap_id_ctx);
-    ipa_auth_ctx->sdap_auth_ctx->be = sdap_id_ctx->be;
-    ipa_auth_ctx->sdap_auth_ctx->opts = sdap_id_ctx->opts;
-
-    ipa_auth_ctx->krb5_ctx = talloc_get_type(
-                              be_req->be_ctx->bet_info[BET_AUTH].pvt_bet_data,
-                              struct krb5_ctx);
-
-/* TODO: test and activate when server side support is available */
-    ipa_auth_ctx->password_migration = false;
-
-    ipa_auth_ctx->dp_err_type = DP_ERR_FATAL;
-    ipa_auth_ctx->errnum = EIO;
-    ipa_auth_ctx->errstr = NULL;
+    state->password_migration = false;
+    state->sh = NULL;
+
+    state->be_req = be_req;
+    state->ev = be_req->be_ctx->ev;
+
+    state->pd = talloc_get_type(be_req->req_data, struct pam_data);
+
+    switch (state->pd->cmd) {
+        case SSS_PAM_AUTHENTICATE:
+            state->ipa_auth_ctx = talloc_get_type(
+                                be_req->be_ctx->bet_info[BET_AUTH].pvt_bet_data,
+                                struct ipa_auth_ctx);
+            break;
+        case SSS_PAM_CHAUTHTOK:
+        case SSS_PAM_CHAUTHTOK_PRELIM:
+            state->ipa_auth_ctx = talloc_get_type(
+                              be_req->be_ctx->bet_info[BET_CHPASS].pvt_bet_data,
+                              struct ipa_auth_ctx);
+            break;
+        default:
+            DEBUG(1, ("Unsupported PAM task.\n"));
+            goto fail;
+    }
 
-    req = ipa_auth_handler_send(ipa_auth_ctx, be_req->be_ctx->ev, be_req,
-                                krb5_pam_handler);
+    req = krb5_auth_send(state, state->ev, be_req->be_ctx, state->pd,
+                         state->ipa_auth_ctx->krb5_auth_ctx);
     if (req == NULL) {
-        DEBUG(1, ("ipa_auth_handler_send failed.\n"));
+        DEBUG(1, ("krb5_auth_send failed.\n"));
         goto fail;
     }
 
-    tevent_req_set_callback(req, ipa_auth_handler_done, ipa_auth_ctx);
+    tevent_req_set_callback(req, ipa_auth_handler_done, state);
     return;
 
 fail:
-    ipa_auth_reply(ipa_auth_ctx);
+    state->pd->pam_status = PAM_SYSTEM_ERR;
+    ipa_auth_reply(be_req, DP_ERR_FATAL, state->pd->pam_status);
 }
 
 static void ipa_auth_handler_done(struct tevent_req *req)
 {
-    struct ipa_auth_ctx *ipa_auth_ctx = tevent_req_callback_data(req,
-                                                           struct ipa_auth_ctx);
-    struct pam_data *pd;
-    struct be_req *be_req;
+    struct ipa_auth_state *state = tevent_req_callback_data(req,
+                                                         struct ipa_auth_state);
     int ret;
+    int pam_status = PAM_SYSTEM_ERR;
+    int dp_err;
 
-    be_req = ipa_auth_ctx->be_req;
-    pd = talloc_get_type(be_req->req_data, struct pam_data);
-
-    ret = ipa_auth_handler_recv(req, ipa_auth_ctx, &ipa_auth_ctx->dp_err_type,
-                                &ipa_auth_ctx->errnum, &ipa_auth_ctx->errstr);
+    ret = krb5_auth_recv(req, &pam_status, &dp_err);
     talloc_zfree(req);
-    if (ret != EOK) {
-        DEBUG(1, ("ipa_auth_handler request failed.\n"));
-        pd->pam_status = PAM_SYSTEM_ERR;
+    state->pd->pam_status = pam_status;
+    if (ret != EOK && pam_status != PAM_CRED_ERR) {
+        DEBUG(1, ("krb5_auth_recv request failed.\n"));
+        dp_err = DP_ERR_OK;
         goto done;
     }
-    if (ipa_auth_ctx->dp_err_type != DP_ERR_OK) {
-        pd->pam_status = ipa_auth_ctx->errnum;
+
+    if (dp_err != DP_ERR_OK) {
         goto done;
     }
 
-    if (ipa_auth_ctx->password_migration && pd->pam_status == PAM_CRED_ERR) {
-        DEBUG(1, ("Assuming Kerberos password is missing, "
-                  "starting password migration.\n"));
-        be_req->be_ctx->bet_info[BET_AUTH].pvt_bet_data =
-                                                    ipa_auth_ctx->sdap_auth_ctx;
-        req = ipa_auth_handler_send(ipa_auth_ctx, be_req->be_ctx->ev, be_req,
-                                    sdap_pam_auth_handler);
+    if (state->pd->cmd == SSS_PAM_AUTHENTICATE &&
+        state->pd->pam_status == PAM_CRED_ERR) {
+
+        req = get_password_migration_flag_send(state, state->ev,
+                                             state->ipa_auth_ctx->sdap_auth_ctx,
+                                             dp_opt_get_string(
+                                               state->ipa_auth_ctx->ipa_options,
+                                               IPA_DOMAIN));
         if (req == NULL) {
-            DEBUG(1, ("ipa_auth_ldap_send failed.\n"));
+            DEBUG(1, ("get_password_migration_flag failed.\n"));
             goto done;
         }
 
-        tevent_req_set_callback(req, ipa_auth_ldap_done, ipa_auth_ctx);
+        tevent_req_set_callback(req, ipa_get_migration_flag_done, state);
         return;
     }
 
 done:
-    ipa_auth_reply(ipa_auth_ctx);
+    ipa_auth_reply(state->be_req, dp_err, state->pd->pam_status);
 }
 
-static void ipa_auth_ldap_done(struct tevent_req *req)
+static void ipa_get_migration_flag_done(struct tevent_req *req)
 {
-    struct ipa_auth_ctx *ipa_auth_ctx = tevent_req_callback_data(req,
-                                                           struct ipa_auth_ctx);
-    struct pam_data *pd;
-    struct be_req *be_req;
+    struct ipa_auth_state *state = tevent_req_callback_data(req,
+                                                         struct ipa_auth_state);
     int ret;
+    int dp_err = DP_ERR_FATAL;
+    const char **attrs;
 
-    be_req = ipa_auth_ctx->be_req;
-    pd = talloc_get_type(be_req->req_data, struct pam_data);
-
-    ret = ipa_auth_handler_recv(req, ipa_auth_ctx, &ipa_auth_ctx->dp_err_type,
-                                &ipa_auth_ctx->errnum, &ipa_auth_ctx->errstr);
+    ret = get_password_migration_flag_recv(req, state,
+                                           &state->password_migration,
+                                           &state->sh);
     talloc_zfree(req);
     if (ret != EOK) {
-        DEBUG(1, ("ipa_auth_handler request failed.\n"));
-        pd->pam_status = PAM_SYSTEM_ERR;
-        goto done;
-    }
-    if (ipa_auth_ctx->dp_err_type != DP_ERR_OK) {
-        pd->pam_status = ipa_auth_ctx->errnum;
+        DEBUG(1, ("get_password_migration_flag request failed.\n"));
+        state->pd->pam_status = PAM_SYSTEM_ERR;
+        dp_err = DP_ERR_OK;
         goto done;
     }
 
-    if (pd->pam_status == PAM_SUCCESS) {
-        DEBUG(1, ("LDAP authentication succeded, "
-                  "trying Kerberos authentication again.\n"));
-        be_req->be_ctx->bet_info[BET_AUTH].pvt_bet_data = ipa_auth_ctx->krb5_ctx;
-        req = ipa_auth_handler_send(ipa_auth_ctx, be_req->be_ctx->ev, be_req,
-                                    krb5_pam_handler);
+    if (state->password_migration) {
+        state->pd->pam_status = PAM_SYSTEM_ERR;
+        DEBUG(1, ("Assuming Kerberos password is missing, "
+                  "starting password migration.\n"));
+
+        attrs = talloc_array(state, const char *, 2);
+        if (attrs == NULL) {
+            DEBUG(1, ("talloc_array failed.\n"));
+            state->pd->pam_status = PAM_SYSTEM_ERR;
+            dp_err = DP_ERR_OK;
+            goto done;
+        }
+        attrs[0] = SYSDB_ORIG_DN;
+        attrs[1] = NULL;
+
+        req = sysdb_search_user_by_name_send(state, state->ev,
+                                             state->be_req->be_ctx->sysdb, NULL,
+                                             state->be_req->be_ctx->domain,
+                                             state->pd->user, attrs);
         if (req == NULL) {
-            DEBUG(1, ("ipa_auth_ldap_send failed.\n"));
+            DEBUG(1, ("sysdb_search_user_by_name_send failed.\n"));
             goto done;
         }
 
-        tevent_req_set_callback(req, ipa_auth_handler_retry_done, ipa_auth_ctx);
+        tevent_req_set_callback(req, ipa_auth_get_user_dn_done, state);
         return;
+    } else {
+        DEBUG(5, ("Password migration is not enabled.\n"));
     }
 
+    dp_err = DP_ERR_OK;
+
 done:
-    ipa_auth_reply(ipa_auth_ctx);
+    ipa_auth_reply(state->be_req, dp_err, state->pd->pam_status);
 }
 
-static void ipa_auth_handler_retry_done(struct tevent_req *req)
+void ipa_auth_get_user_dn_done(struct tevent_req *req)
 {
-    struct ipa_auth_ctx *ipa_auth_ctx = tevent_req_callback_data(req,
-                                                           struct ipa_auth_ctx);
-    struct pam_data *pd;
-    struct be_req *be_req;
+    struct ipa_auth_state *state = tevent_req_callback_data(req,
+                                                         struct ipa_auth_state);
     int ret;
+    int dp_err = DP_ERR_FATAL;
+    struct dp_opt_blob password;
+    struct ldb_message *msg;
+    const char *dn;
+
+    ret = sysdb_search_user_recv(req, state, &msg);
+    talloc_zfree(req);
+    if (ret != EOK) {
+        DEBUG(1, ("sysdb_search_user request failed.\n"));
+        state->pd->pam_status = PAM_SYSTEM_ERR;
+        dp_err = DP_ERR_OK;
+        goto done;
+    }
+
+    dn = ldb_msg_find_attr_as_string(msg, SYSDB_ORIG_DN, NULL);
+    if (dn == NULL) {
+        DEBUG(1, ("Missing original DN for user [%s].\n", state->pd->user));
+        state->pd->pam_status = PAM_SYSTEM_ERR;
+        dp_err = DP_ERR_OK;
+        goto done;
+    }
+
+    password.data = state->pd->authtok;
+    password.length = state->pd->authtok_size;
+
+    req = sdap_auth_send(state, state->ev, state->sh, NULL, NULL, dn,
+                         "password", password);
+    if (req == NULL) {
+        DEBUG(1, ("sdap_auth_send failed.\n"));
+        goto done;
+    }
 
-    be_req = ipa_auth_ctx->be_req;
-    pd = talloc_get_type(be_req->req_data, struct pam_data);
+    tevent_req_set_callback(req, ipa_auth_ldap_done, state);
+    return;
+
+done:
+    ipa_auth_reply(state->be_req, dp_err, state->pd->pam_status);
+}
+
+
+static void ipa_auth_ldap_done(struct tevent_req *req)
+{
+    struct ipa_auth_state *state = tevent_req_callback_data(req,
+                                                         struct ipa_auth_state);
+    int ret;
+    int dp_err = DP_ERR_FATAL;
+    enum sdap_result result;
 
-    ret = ipa_auth_handler_recv(req, ipa_auth_ctx, &ipa_auth_ctx->dp_err_type,
-                                &ipa_auth_ctx->errnum, &ipa_auth_ctx->errstr);
+    ret = sdap_auth_recv(req, state, &result, NULL);
     talloc_zfree(req);
     if (ret != EOK) {
-        DEBUG(1, ("ipa_auth_handler request failed.\n"));
-        pd->pam_status = PAM_SYSTEM_ERR;
+        DEBUG(1, ("auth_send request failed.\n"));
+        state->pd->pam_status = PAM_SYSTEM_ERR;
+        dp_err = DP_ERR_OK;
+        goto done;
     }
-    if (ipa_auth_ctx->dp_err_type != DP_ERR_OK) {
-        pd->pam_status = ipa_auth_ctx->errnum;
+
+/* TODO: do we need to handle expired passwords? */
+    if (result != SDAP_AUTH_SUCCESS) {
+        DEBUG(1, ("LDAP authentication failed, "
+                  "Password migration not possible.\n"));
+        state->pd->pam_status = PAM_CRED_INSUFFICIENT;
+        dp_err = DP_ERR_OK;
+        goto done;
     }
 
-    ipa_auth_reply(ipa_auth_ctx);
+    DEBUG(1, ("LDAP authentication succeded, "
+              "trying Kerberos authentication again.\n"));
+
+    req = krb5_auth_send(state, state->ev,
+                         state->be_req->be_ctx, state->pd,
+                         state->ipa_auth_ctx->krb5_auth_ctx);
+    if (req == NULL) {
+        DEBUG(1, ("krb5_auth_send failed.\n"));
+        goto done;
+    }
+
+    tevent_req_set_callback(req, ipa_auth_handler_retry_done, state);
+    return;
+
+done:
+    ipa_auth_reply(state->be_req, dp_err, state->pd->pam_status);
+}
+
+static void ipa_auth_handler_retry_done(struct tevent_req *req)
+{
+    struct ipa_auth_state *state = tevent_req_callback_data(req,
+                                                         struct ipa_auth_state);
+    int ret;
+    int pam_status;
+    int dp_err;
+
+    ret = krb5_auth_recv(req, &pam_status, &dp_err);
+    talloc_zfree(req);
+    if (ret != EOK) {
+        DEBUG(1, ("krb5_auth_recv request failed.\n"));
+        state->pd->pam_status = PAM_SYSTEM_ERR;
+        dp_err = DP_ERR_OK;
+        goto done;
+    }
+
+    state->pd->pam_status = pam_status;
+
+done:
+    ipa_auth_reply(state->be_req, dp_err, state->pd->pam_status);
 }
diff --git a/src/providers/ipa/ipa_common.h b/src/providers/ipa/ipa_common.h
index 7762818..03a082e 100644
--- a/src/providers/ipa/ipa_common.h
+++ b/src/providers/ipa/ipa_common.h
@@ -50,6 +50,12 @@ enum ipa_basic_opt {
     IPA_OPTS_BASIC /* opts counter */
 };
 
+struct ipa_auth_ctx {
+    struct krb5_ctx *krb5_auth_ctx;
+    struct sdap_auth_ctx *sdap_auth_ctx;
+    struct dp_option *ipa_options;
+};
+
 struct ipa_options {
     struct dp_option *basic;
 
@@ -61,7 +67,7 @@ struct ipa_options {
 
     /* auth and chpass provider */
     struct dp_option *auth;
-    struct krb5_ctx *auth_ctx;
+    struct ipa_auth_ctx *auth_ctx;
 };
 
 int domain_to_basedn(TALLOC_CTX *memctx, const char *domain, char **basedn);
diff --git a/src/providers/ipa/ipa_init.c b/src/providers/ipa/ipa_init.c
index 1689ac2..6e0688f 100644
--- a/src/providers/ipa/ipa_init.c
+++ b/src/providers/ipa/ipa_init.c
@@ -48,7 +48,7 @@ struct bet_ops ipa_auth_ops = {
 };
 
 struct bet_ops ipa_chpass_ops = {
-    .handler = krb5_pam_handler,
+    .handler = ipa_auth,
     .finalize = NULL,
 };
 
@@ -162,7 +162,9 @@ int sssm_ipa_auth_init(struct be_ctx *bectx,
                        struct bet_ops **ops,
                        void **pvt_data)
 {
-    struct krb5_ctx *ctx;
+    struct ipa_auth_ctx *ipa_auth_ctx;
+    struct krb5_ctx *krb5_auth_ctx;
+    struct sdap_auth_ctx *sdap_auth_ctx;
     struct tevent_signal *sige;
     FILE *debug_filep;
     unsigned v;
@@ -182,33 +184,74 @@ int sssm_ipa_auth_init(struct be_ctx *bectx,
         return EOK;
     }
 
-    ctx = talloc_zero(bectx, struct krb5_ctx);
-    if (!ctx) {
+    ipa_auth_ctx = talloc_zero(ipa_options, struct ipa_auth_ctx);
+    if (!ipa_auth_ctx) {
         return ENOMEM;
     }
-    ctx->service = ipa_options->service->krb5_service;
-    ipa_options->auth_ctx = ctx;
+    ipa_options->auth_ctx = ipa_auth_ctx;
 
-    ret = ipa_get_auth_options(ipa_options, bectx->cdb,
-                               bectx->conf_path,
-                               &ctx->opts);
+    ret = dp_copy_options(ipa_auth_ctx, ipa_options->basic,
+                          IPA_OPTS_BASIC, &ipa_auth_ctx->ipa_options);
     if (ret != EOK) {
+        DEBUG(1, ("dp_copy_options failed.\n"));
+        goto done;
+    }
+
+    krb5_auth_ctx = talloc_zero(ipa_auth_ctx, struct krb5_ctx);
+    if (!krb5_auth_ctx) {
+        ret = ENOMEM;
         goto done;
     }
+    krb5_auth_ctx->service = ipa_options->service->krb5_service;
+    ipa_options->auth_ctx->krb5_auth_ctx = krb5_auth_ctx;
 
-    ret = check_and_export_options(ctx->opts, bectx->domain);
+    ret = ipa_get_auth_options(ipa_options, bectx->cdb, bectx->conf_path,
+                               &krb5_auth_ctx->opts);
     if (ret != EOK) {
-        DEBUG(1, ("check_and_export_opts failed.\n"));
         goto done;
     }
 
-    sige = tevent_add_signal(bectx->ev, ctx, SIGCHLD, SA_SIGINFO,
-                             child_sig_handler, NULL);
-    if (sige == NULL) {
-        DEBUG(1, ("tevent_add_signal failed.\n"));
+    sdap_auth_ctx = talloc_zero(ipa_auth_ctx, struct sdap_auth_ctx);
+    if (!sdap_auth_ctx) {
         ret = ENOMEM;
         goto done;
     }
+    sdap_auth_ctx->be =  bectx;
+    sdap_auth_ctx->service = ipa_options->service->sdap;
+    ipa_options->auth_ctx->sdap_auth_ctx = sdap_auth_ctx;
+
+    ret = ipa_get_id_options(ipa_options, bectx->cdb, bectx->conf_path,
+                             &sdap_auth_ctx->opts);
+    if (ret != EOK) {
+        goto done;
+    }
+
+    ret = setup_tls_config(sdap_auth_ctx->opts->basic);
+    if (ret != EOK) {
+        DEBUG(1, ("setup_tls_config failed [%d][%s].\n",
+                  ret, strerror(ret)));
+        goto done;
+    }
+
+    ret = check_and_export_options(krb5_auth_ctx->opts, bectx->domain);
+    if (ret != EOK) {
+        DEBUG(1, ("check_and_export_opts failed.\n"));
+        goto done;
+    }
+
+    if (ipa_options->id_ctx == NULL) {
+        DEBUG(9, ("Adding SIGCHLD handler for Kerberos child.\n"));
+        sige = tevent_add_signal(bectx->ev, krb5_auth_ctx, SIGCHLD, SA_SIGINFO,
+                                 child_sig_handler, NULL);
+        if (sige == NULL) {
+            DEBUG(1, ("tevent_add_signal failed.\n"));
+            ret = ENOMEM;
+            goto done;
+        }
+    } else {
+        DEBUG(9, ("IPA id provider already initialized, "
+                  "assuming that a SIGCHLD handler is already in place.\n"));
+    }
 
     if (debug_to_file != 0) {
         ret = open_debug_file_ex("krb5_child", &debug_filep);
@@ -218,19 +261,19 @@ int sssm_ipa_auth_init(struct be_ctx *bectx,
             goto done;
         }
 
-        ctx->child_debug_fd = fileno(debug_filep);
-        if (ctx->child_debug_fd == -1) {
+        krb5_auth_ctx->child_debug_fd = fileno(debug_filep);
+        if (krb5_auth_ctx->child_debug_fd == -1) {
             DEBUG(0, ("fileno failed [%d][%s]\n", errno, strerror(errno)));
             ret = errno;
             goto done;
         }
 
-        v = fcntl(ctx->child_debug_fd, F_GETFD, 0);
-        fcntl(ctx->child_debug_fd, F_SETFD, v & ~FD_CLOEXEC);
+        v = fcntl(krb5_auth_ctx->child_debug_fd, F_GETFD, 0);
+        fcntl(krb5_auth_ctx->child_debug_fd, F_SETFD, v & ~FD_CLOEXEC);
     }
 
     *ops = &ipa_auth_ops;
-    *pvt_data = ctx;
+    *pvt_data = ipa_auth_ctx;
     ret = EOK;
 
 done:
diff --git a/src/providers/krb5/krb5_common.c b/src/providers/krb5/krb5_common.c
index 2b3331e..52cbe16 100644
--- a/src/providers/krb5/krb5_common.c
+++ b/src/providers/krb5/krb5_common.c
@@ -69,13 +69,13 @@ errno_t check_and_export_options(struct dp_option *opts,
 
     dummy = dp_opt_get_cstring(opts, KRB5_KDC);
     if (dummy == NULL) {
-        DEBUG(1, ("No KDC explicitly configured, using defaults"));
+        DEBUG(1, ("No KDC explicitly configured, using defaults.\n"));
     }
 
     dummy = dp_opt_get_cstring(opts, KRB5_KPASSWD);
     if (dummy == NULL) {
         DEBUG(1, ("No kpasswd server explicitly configured, "
-                  "using the KDC or defaults"));
+                  "using the KDC or defaults.\n"));
     }
 
     dummy = dp_opt_get_cstring(opts, KRB5_CCNAME_TMPL);
-- 
1.6.6.1



More information about the sssd-devel mailing list