From 10a2f7a152eae3b63b2632cc953278cf0e7a876b Mon Sep 17 00:00:00 2001
From: Stephen Gallagher <sgallagh@redhat.com>
Date: Wed, 16 Nov 2011 14:52:40 -0500
Subject: [PATCH 1/2] RESPONDER: Refactor DP requests into tevent_req style

---
 src/responder/common/responder.h        |   38 ++-
 src/responder/common/responder_common.c |    8 +
 src/responder/common/responder_dp.c     |  829 +++++++++++++++++--------------
 src/responder/nss/nsssrv_cmd.c          |  163 +++++--
 src/responder/nss/nsssrv_private.h      |    1 +
 src/responder/pam/pamsrv_cmd.c          |   59 ++-
 6 files changed, 656 insertions(+), 442 deletions(-)

diff --git a/src/responder/common/responder.h b/src/responder/common/responder.h
index 1b39fdd5b6854e05acdf5b7be2c63a2c8381f3c3..3b5ab8a42136c17f0f71a202f8ae9367edfa6afb 100644
--- a/src/responder/common/responder.h
+++ b/src/responder/common/responder.h
@@ -89,6 +89,8 @@ struct resp_ctx {
 
     struct sss_names_ctx *names;
 
+    hash_table_t *dp_request_table;
+
     void *pvt_ctx;
 };
 
@@ -163,17 +165,41 @@ struct cli_protocol_version *register_cli_protocol_version(void);
 typedef void (*sss_dp_callback_t)(uint16_t err_maj, uint32_t err_min,
                                   const char *err_msg, void *ptr);
 
+struct dp_callback_ctx {
+    sss_dp_callback_t callback;
+    void *ptr;
+
+    void *mem_ctx;
+    struct cli_ctx *cctx;
+};
+
 void handle_requests_after_reconnect(void);
 
-int sss_dp_send_acct_req(struct resp_ctx *rctx, TALLOC_CTX *callback_memctx,
-                         sss_dp_callback_t callback, void *callback_ctx,
-                         int timeout, const char *domain,
-                         bool fast_reply, int type,
-                         const char *opt_name, uint32_t opt_id);
-
 int responder_logrotate(DBusMessage *message,
                         struct sbus_connection *conn);
 
+/* Send a request to the data provider
+ * Once this function is called, the communication
+ * with the data provider will always run to
+ * completion. Freeing the returned tevent_req will
+ * cancel the notification of completion, but not
+ * the data provider action.
+ */
+struct tevent_req *
+sss_dp_get_account_send(TALLOC_CTX *mem_ctx,
+                        struct resp_ctx *rctx,
+                        struct sss_domain_info *dom,
+                        bool fast_reply,
+                        int type,
+                        const char *opt_name,
+                        uint32_t opt_id);
+errno_t
+sss_dp_get_account_recv(TALLOC_CTX *mem_ctx,
+                        struct tevent_req *req,
+                        dbus_uint16_t *err_maj,
+                        dbus_uint32_t *err_min,
+                        char **err_msg);
+
 bool sss_utf8_check(const uint8_t *s, size_t n);
 
 #endif /* __SSS_RESPONDER_H__ */
diff --git a/src/responder/common/responder_common.c b/src/responder/common/responder_common.c
index f97ec06fd2d93d9568607c00bb3399792eec397a..24f9125b7ca32a996da808c77a20df0686874bc0 100644
--- a/src/responder/common/responder_common.c
+++ b/src/responder/common/responder_common.c
@@ -592,6 +592,14 @@ int sss_process_init(TALLOC_CTX *mem_ctx,
         return ret;
     }
 
+    /* Create DP request table */
+    ret = sss_hash_create(rctx, 30, &rctx->dp_request_table);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_FATAL_FAILURE,
+              ("Could not create hash table for the request queue\n"));
+        return ret;
+    }
+
     DEBUG(1, ("Responder Initialization complete\n"));
 
     *responder_ctx = rctx;
diff --git a/src/responder/common/responder_dp.c b/src/responder/common/responder_dp.c
index 2bf966cd0ae153f4d0a7075e5c73039a722ad9f9..93c7cae4dfa699265a3c77bd32ea755b49d45f75 100644
--- a/src/responder/common/responder_dp.c
+++ b/src/responder/common/responder_dp.c
@@ -32,14 +32,24 @@ hash_table_t *dp_requests = NULL;
 
 struct sss_dp_req;
 
+struct dp_get_account_state {
+    struct resp_ctx *rctx;
+    struct sss_domain_info *dom;
+    const char *opt_name;
+    uint32_t opt_id;
+    hash_key_t *key;
+
+    dbus_uint16_t err_maj;
+    dbus_uint32_t err_min;
+    char *err_msg;
+};
+
 struct sss_dp_callback {
     struct sss_dp_callback *prev;
     struct sss_dp_callback *next;
 
+    struct tevent_req *req;
     struct sss_dp_req *sdp_req;
-
-    sss_dp_callback_t callback;
-    void *callback_ctx;
 };
 
 struct sss_dp_req {
@@ -47,7 +57,7 @@ struct sss_dp_req {
     struct tevent_context *ev;
     DBusPendingCall *pending_reply;
 
-    char *key;
+    hash_key_t *key;
 
     struct tevent_timer *tev;
     struct sss_dp_callback *cb_list;
@@ -59,7 +69,8 @@ struct sss_dp_req {
 
 static int sss_dp_callback_destructor(void *ptr)
 {
-    struct sss_dp_callback *cb = talloc_get_type(ptr, struct sss_dp_callback);
+    struct sss_dp_callback *cb =
+            talloc_get_type(ptr, struct sss_dp_callback);
 
     DLIST_REMOVE(cb->sdp_req->cb_list, cb);
 
@@ -68,8 +79,9 @@ static int sss_dp_callback_destructor(void *ptr)
 
 static int sss_dp_req_destructor(void *ptr)
 {
+    struct sss_dp_callback *cb;
     struct sss_dp_req *sdp_req = talloc_get_type(ptr, struct sss_dp_req);
-    hash_key_t key;
+    struct dp_get_account_state *state;
 
     /* Cancel Dbus pending reply if still pending */
     if (sdp_req->pending_reply) {
@@ -77,15 +89,25 @@ static int sss_dp_req_destructor(void *ptr)
         sdp_req->pending_reply = NULL;
     }
 
-    /* Destroy the hash entry
-     * There are some situations when the entry has already
-     * been destroyed to avoid race condition, so don't check
-     * the result */
-    key.type = HASH_KEY_STRING;
-    key.str = sdp_req->key;
-    int hret = hash_delete(dp_requests, &key);
+    /* If there are callbacks that haven't been invoked, return
+     * an error now.
+     */
+    DLIST_FOR_EACH(cb, sdp_req->cb_list) {
+        state = tevent_req_data(cb->req, struct dp_get_account_state);
+        state->err_maj = DP_ERR_FATAL;
+        state->err_min = EIO;
+        tevent_req_error(cb->req, EIO);
+    }
+
+    /* Destroy the hash entry */
+    int hret = hash_delete(sdp_req->rctx->dp_request_table, sdp_req->key);
     if (hret != HASH_SUCCESS) {
-        DEBUG(SSSDBG_TRACE_INTERNAL, ("Could not clear entry from request queue\n"));
+        /* This should never happen */
+        DEBUG(SSSDBG_TRACE_INTERNAL,
+              ("BUG: Could not clear [%d:%d:%s] from request queue: [%s]\n",
+               sdp_req->key->type, sdp_req->key->ul, sdp_req->key->str,
+               hash_error_string(hret)));
+        return -1;
     }
 
     return 0;
@@ -116,180 +138,147 @@ void handle_requests_after_reconnect(void)
         talloc_free(sdp_req);
     }
 }
-
 static int sss_dp_get_reply(DBusPendingCall *pending,
                             dbus_uint16_t *err_maj,
                             dbus_uint32_t *err_min,
-                            char **err_msg);
-
-static void sss_dp_invoke_callback(struct tevent_context *ev,
-                                   struct tevent_timer *te,
-                                   struct timeval t, void *ptr)
+                            char **err_msg)
 {
-    struct sss_dp_req *sdp_req = talloc_get_type(ptr, struct sss_dp_req);
-    struct sss_dp_callback *cb;
+    DBusMessage *reply;
+    DBusError dbus_error;
+    dbus_bool_t ret;
+    int type;
+    int err = EOK;
 
-    cb = sdp_req->cb_list;
-    /* Remove the callback from the list, the caller may free it, within the
-     * callback. */
-    talloc_set_destructor((TALLOC_CTX *)cb, NULL);
-    DLIST_REMOVE(sdp_req->cb_list, cb);
+    dbus_error_init(&dbus_error);
 
-    cb->callback(sdp_req->err_maj,
-                 sdp_req->err_min,
-                 sdp_req->err_msg,
-                 cb->callback_ctx);
+    reply = dbus_pending_call_steal_reply(pending);
+    if (!reply) {
+        /* reply should never be null. This function shouldn't be called
+         * until reply is valid or timeout has occurred. If reply is NULL
+         * here, something is seriously wrong and we should bail out.
+         */
+        DEBUG(SSSDBG_CRIT_FAILURE,
+              ("Severe error. A reply callback was called but no reply "
+               "was received and no timeout occurred\n"));
 
-    /* If there are some more callbacks to be invoked,
-     * don't destroy the request */
-    if (sdp_req->cb_list != NULL) {
-        return;
+        /* FIXME: Destroy this connection ? */
+        err = EIO;
+        goto done;
     }
 
-    /* steal te on rctx because it will be freed as soon as the handler
-     * returns. Causing a double free if we don't, as te is allocated on
-     * sdp_req and we are just going to free it */
-    talloc_steal(sdp_req->rctx, te);
+    type = dbus_message_get_type(reply);
+    switch (type) {
+    case DBUS_MESSAGE_TYPE_METHOD_RETURN:
+        ret = dbus_message_get_args(reply, &dbus_error,
+                                    DBUS_TYPE_UINT16, err_maj,
+                                    DBUS_TYPE_UINT32, err_min,
+                                    DBUS_TYPE_STRING, err_msg,
+                                    DBUS_TYPE_INVALID);
+        if (!ret) {
+            DEBUG(1,("Failed to parse message\n"));
+            /* FIXME: Destroy this connection ? */
+            if (dbus_error_is_set(&dbus_error)) dbus_error_free(&dbus_error);
+            err = EIO;
+            goto done;
+        }
+        DEBUG(4, ("Got reply (%u, %u, %s) from Data Provider\n",
+                  (unsigned int)*err_maj, (unsigned int)*err_min, *err_msg));
 
-    talloc_zfree(sdp_req);
-}
+        break;
 
-static void sdp_req_timeout(struct tevent_context *ev,
-                            struct tevent_timer *te,
-                            struct timeval t, void *ptr)
-{
-    struct sss_dp_req *sdp_req = talloc_get_type(ptr, struct sss_dp_req);
-    struct sss_dp_callback *cb, *next;
-    struct timeval tv;
-    struct tevent_timer *tev;
-    hash_key_t key;
-
-    sdp_req->err_maj = DP_ERR_FATAL;
-    sdp_req->err_min = ETIMEDOUT;
-    sdp_req->err_msg = discard_const_p(char, "Timed out");
+    case DBUS_MESSAGE_TYPE_ERROR:
+        if (strcmp(dbus_message_get_error_name(reply),
+                   DBUS_ERROR_NO_REPLY) == 0) {
+            err = ETIME;
+            goto done;
+        }
+        DEBUG(0,("The Data Provider returned an error [%s]\n",
+                 dbus_message_get_error_name(reply)));
+        /* Falling through to default intentionally*/
+    default:
+        /*
+         * Timeout or other error occurred or something
+         * unexpected happened.
+         * It doesn't matter which, because either way we
+         * know that this connection isn't trustworthy.
+         * We'll destroy it now.
+         */
 
-    /* Destroy the hash entry */
-    key.type = HASH_KEY_STRING;
-    key.str = sdp_req->key;
-    int hret = hash_delete(dp_requests, &key);
-    if (hret != HASH_SUCCESS) {
-        /* This should never happen */
-        DEBUG(0, ("Could not clear entry from request queue\n"));
+        /* FIXME: Destroy this connection ? */
+        err = EIO;
     }
 
-    /* Queue up all callbacks */
-    cb = sdp_req->cb_list;
-    tv = tevent_timeval_current();
-    while (cb) {
-        next = cb->next;
-        tev = tevent_add_timer(sdp_req->ev, sdp_req, tv,
-                               sss_dp_invoke_callback, sdp_req);
-        if (!tev) {
-            return;
-        }
-        cb = next;
-    }
+done:
+    dbus_pending_call_unref(pending);
+    dbus_message_unref(reply);
+
+    return err;
 }
 
-static void sss_dp_send_acct_callback(DBusPendingCall *pending, void *ptr)
+static struct tevent_req *
+sss_dp_get_account_int_send(struct resp_ctx *rctx,
+                            hash_key_t *key,
+                            struct sss_domain_info *dom,
+                            uint32_t be_type,
+                            const char *filter);
+
+static void
+sss_dp_get_account_done(struct tevent_req *subreq);
+
+static errno_t
+sss_dp_get_account_int_recv(TALLOC_CTX *mem_ctx,
+                        struct tevent_req *req,
+                        dbus_uint16_t *err_maj,
+                        dbus_uint32_t *err_min,
+                        char **err_msg);
+
+
+/* Send a request to the data provider
+ * Once this function is called, the communication
+ * with the data provider will always run to
+ * completion. Freeing the returned tevent_req will
+ * cancel the notification of completion, but not
+ * the data provider action.
+ */
+struct tevent_req *
+sss_dp_get_account_send(TALLOC_CTX *mem_ctx,
+                        struct resp_ctx *rctx,
+                        struct sss_domain_info *dom,
+                        bool fast_reply,
+                        int type,
+                        const char *opt_name,
+                        uint32_t opt_id)
 {
-    int ret;
+    errno_t ret;
+    int hret;
+    struct tevent_req *req;
+    struct tevent_req *subreq;
+    struct dp_get_account_state *state;
     struct sss_dp_req *sdp_req;
-    struct sss_dp_callback *cb, *next;
-    struct timeval tv;
-    struct tevent_timer *te;
-    hash_key_t key;
-
-    sdp_req = talloc_get_type(ptr, struct sss_dp_req);
-
-    /* prevent trying to cancel a reply that we already received */
-    sdp_req->pending_reply = NULL;
-
-    ret = sss_dp_get_reply(pending,
-                           &sdp_req->err_maj,
-                           &sdp_req->err_min,
-                           &sdp_req->err_msg);
-    if (ret != EOK) {
-        if (ret == ETIME) {
-            sdp_req->err_maj = DP_ERR_TIMEOUT;
-            sdp_req->err_min = ret;
-            sdp_req->err_msg = talloc_strdup(sdp_req, "Request timed out");
-        }
-        else {
-            sdp_req->err_maj = DP_ERR_FATAL;
-            sdp_req->err_min = ret;
-            sdp_req->err_msg =
-                talloc_strdup(sdp_req,
-                              "Failed to get reply from Data Provider");
-        }
-    }
-
-    /* Check whether we need to issue any callbacks */
-    if (sdp_req->cb_list == NULL) {
-        /* No callbacks to invoke. Destroy the hash entry */
-        talloc_zfree(sdp_req);
-        return;
-    }
-
-    /* Destroy the hash entry */
-    key.type = HASH_KEY_STRING;
-    key.str = sdp_req->key;
-    int hret = hash_delete(dp_requests, &key);
-    if (hret != HASH_SUCCESS) {
-        /* This should never happen */
-        DEBUG(0, ("Could not clear entry from request queue\n"));
-    }
-
-    /* Queue up all callbacks */
-    cb = sdp_req->cb_list;
-    tv = tevent_timeval_current();
-    while (cb) {
-        next = cb->next;
-        te = tevent_add_timer(sdp_req->ev, sdp_req, tv,
-                              sss_dp_invoke_callback, sdp_req);
-        if (!te) {
-            /* Out of memory or other serious error */
-            return;
-        }
-        cb = next;
-    }
-}
-
-static int sss_dp_send_acct_req_create(struct resp_ctx *rctx,
-                                       TALLOC_CTX *callback_memctx,
-                                       const char *domain,
-                                       uint32_t be_type,
-                                       char *filter,
-                                       int timeout,
-                                       sss_dp_callback_t callback,
-                                       void *callback_ctx,
-                                       struct sss_dp_req **ndp);
-
-int sss_dp_send_acct_req(struct resp_ctx *rctx, TALLOC_CTX *callback_memctx,
-                         sss_dp_callback_t callback, void *callback_ctx,
-                         int timeout, const char *domain,
-                         bool fast_reply, int type,
-                         const char *opt_name, uint32_t opt_id)
-{
-    int ret, hret;
-    uint32_t be_type;
+    struct sss_dp_callback *cb;
     char *filter;
-    hash_key_t key;
+    uint32_t be_type;
     hash_value_t value;
-    TALLOC_CTX *tmp_ctx;
-    struct timeval tv;
-    struct sss_dp_req *sdp_req = NULL;
-    struct sss_dp_callback *cb;
+
+    req = tevent_req_create(mem_ctx, &state, struct dp_get_account_state);
+    if (!req) {
+        return NULL;
+    }
 
     /* either, or, not both */
     if (opt_name && opt_id) {
-        return EINVAL;
+        ret = EINVAL;
+        goto error;
     }
 
-    if (!domain) {
-        return EINVAL;
+    if (!dom) {
+        ret = EINVAL;
+        goto error;
     }
 
+    state->rctx = rctx;
+    state->dom = dom;
+
     switch (type) {
     case SSS_DP_USER:
         be_type = BE_REQ_USER;
@@ -304,160 +293,233 @@ int sss_dp_send_acct_req(struct resp_ctx *rctx, TALLOC_CTX *callback_memctx,
         be_type = BE_REQ_NETGROUP;
         break;
     default:
-        return EINVAL;
+        ret = EINVAL;
+        goto error;
     }
 
     if (fast_reply) {
         be_type |= BE_REQ_FAST;
     }
 
-    if (dp_requests == NULL) {
-        /* Create a hash table to handle queued update requests */
-        ret = hash_create(10, &dp_requests, NULL, NULL);
-        if (ret != HASH_SUCCESS) {
-            fprintf(stderr, "cannot create hash table (%s)\n", hash_error_string(ret));
-            return EIO;
-        }
-    }
+    /* Check whether there's already an identical request in progress */
 
-    tmp_ctx = talloc_new(NULL);
-    if (!tmp_ctx) {
-        return ENOMEM;
+    state->key = talloc(state, hash_key_t);
+    if (!state->key) {
+        ret = ENOMEM;
+        goto error;
     }
 
-    key.type = HASH_KEY_STRING;
-    key.str = NULL;
+    state->key->type = HASH_KEY_STRING;
 
     if (opt_name) {
-        filter = talloc_asprintf(tmp_ctx, "name=%s", opt_name);
-        key.str = talloc_asprintf(tmp_ctx, "%d%s@%s", type, opt_name, domain);
+        filter = talloc_asprintf(state, "name=%s", opt_name);
+        state->key->str = talloc_asprintf(state->key, "%d:%s@%s",
+                                          type, opt_name, dom->name);
     } else if (opt_id) {
-        filter = talloc_asprintf(tmp_ctx, "idnumber=%u", opt_id);
-        key.str = talloc_asprintf(tmp_ctx, "%d%d@%s", type, opt_id, domain);
+        filter = talloc_asprintf(state, "idnumber=%u", opt_id);
+        state->key->str = talloc_asprintf(state->key, "%d:%d@%s",
+                                          type, opt_id, dom->name);
     } else {
-        filter = talloc_strdup(tmp_ctx, ENUM_INDICATOR);
-        key.str = talloc_asprintf(tmp_ctx, "%d*@%s", type, domain);
+        filter = talloc_strdup(state, ENUM_INDICATOR);
+        state->key->str = talloc_asprintf(state->key, "%d:*@%s",
+                                          type, dom->name);
     }
-    if (!filter || !key.str) {
-        talloc_zfree(tmp_ctx);
-        return ENOMEM;
+    if (!filter || !state->key->str) {
+        ret = ENOMEM;
     }
 
-    /* Check whether there's already a request in progress */
-    hret = hash_lookup(dp_requests, &key, &value);
+    /* Check the hash for existing references to this request */
+    hret = hash_lookup(rctx->dp_request_table, state->key, &value);
     switch (hret) {
     case HASH_SUCCESS:
-        /* Request already in progress
-         * Add an additional callback if needed and return
-         */
-        DEBUG(2, ("Identical request in progress\n"));
-
-        if (callback) {
-            /* We have a new request asking for a callback */
-            sdp_req = talloc_get_type(value.ptr, struct sss_dp_req);
-            if (!sdp_req) {
-                DEBUG(0, ("Could not retrieve DP request context\n"));
-                ret = EIO;
-                goto done;
-            }
-
-            cb = talloc_zero(callback_memctx, struct sss_dp_callback);
-            if (!cb) {
-                ret = ENOMEM;
-                goto done;
-            }
-
-            cb->callback = callback;
-            cb->callback_ctx = callback_ctx;
-            cb->sdp_req = sdp_req;
-
-            DLIST_ADD_END(sdp_req->cb_list, cb, struct sss_dp_callback *);
-            talloc_set_destructor((TALLOC_CTX *)cb, sss_dp_callback_destructor);
-        }
-
-        ret = EOK;
+        /* Request already in progress */
+        DEBUG(SSSDBG_TRACE_FUNC,
+              ("Identical request in progress: [%s]\n", state->key->str));
         break;
 
     case HASH_ERROR_KEY_NOT_FOUND:
         /* No such request in progress
          * Create a new request
          */
-        ret = sss_dp_send_acct_req_create(rctx, callback_memctx, domain,
-                                          be_type, filter, timeout,
-                                          callback, callback_ctx,
-                                          &sdp_req);
-        if (ret != EOK) {
-            goto done;
-        }
 
         value.type = HASH_VALUE_PTR;
-        value.ptr = sdp_req;
-        hret = hash_enter(dp_requests, &key, &value);
+        subreq = sss_dp_get_account_int_send(rctx, state->key, dom,
+                                             be_type, filter);
+        if (!subreq) {
+            ret = ENOMEM;
+            goto error;
+        }
+        tevent_req_set_callback(subreq, sss_dp_get_account_done, NULL);
+
+        /* We should now be able to find the sdp_req in the hash table */
+        hret = hash_lookup(rctx->dp_request_table, state->key, &value);
         if (hret != HASH_SUCCESS) {
-            DEBUG(0, ("Could not store request query (%s)\n",
-                      hash_error_string(hret)));
-            talloc_zfree(sdp_req);
+            /* Something must have gone wrong with creating the request */
+            talloc_zfree(subreq);
             ret = EIO;
-            goto done;
+            goto error;
         }
 
-        sdp_req->key = talloc_strdup(sdp_req, key.str);
-
-        tv = tevent_timeval_current_ofs(timeout, 0);
-        sdp_req->tev = tevent_add_timer(sdp_req->ev, sdp_req, tv,
-                                        sdp_req_timeout, sdp_req);
-        if (!sdp_req->tev) {
-            DEBUG(0, ("Out of Memory!?\n"));
-            talloc_zfree(sdp_req);
-            ret = ENOMEM;
-            goto done;
-        }
-
-        talloc_set_destructor((TALLOC_CTX *)sdp_req, sss_dp_req_destructor);
-
-        ret = EOK;
         break;
 
     default:
-        DEBUG(0,("Could not query request list (%s)\n",
-                  hash_error_string(hret)));
-        talloc_zfree(sdp_req);
+        DEBUG(SSSDBG_CRIT_FAILURE,
+              ("Could not query request list (%s)\n",
+               hash_error_string(hret)));
         ret = EIO;
+        goto error;
     }
 
-done:
-    talloc_zfree(tmp_ctx);
-    return ret;
+    /* Register this request for results */
+    sdp_req = talloc_get_type(value.ptr, struct sss_dp_req);
+    if (!sdp_req) {
+        DEBUG(0, ("Could not retrieve DP request context\n"));
+        ret = EIO;
+        goto error;
+    }
+
+    cb = talloc_zero(state, struct sss_dp_callback);
+    if (!cb) {
+        ret = ENOMEM;
+        goto error;
+    }
+
+    cb->req = req;
+    cb->sdp_req = sdp_req;
+
+    /* Add it to the list of requests to call */
+    DLIST_ADD_END(sdp_req->cb_list, cb,
+                  struct sss_dp_callback *);
+    talloc_set_destructor((TALLOC_CTX *)cb,
+                          sss_dp_callback_destructor);
+
+    return req;
+
+error:
+    tevent_req_error(req, ret);
+    tevent_req_post(req, rctx->ev);
+    return req;
+}
+
+static void
+sss_dp_get_account_done(struct tevent_req *subreq)
+{
+    errno_t ret;
+    struct tevent_req *req = tevent_req_callback_data(subreq,
+                                                      struct tevent_req);
+    struct dp_get_account_state *state =
+            tevent_req_data(req, struct dp_get_account_state);
+
+    ret = sss_dp_get_account_int_recv(state, req,
+                                      &state->err_maj,
+                                      &state->err_min,
+                                      &state->err_msg);
+    if (ret != EOK) {
+        tevent_req_done(req);
+    } else {
+        tevent_req_error(req, ret);
+    }
+}
+
+errno_t
+sss_dp_get_account_recv(TALLOC_CTX *mem_ctx,
+                        struct tevent_req *req,
+                        dbus_uint16_t *err_maj,
+                        dbus_uint32_t *err_min,
+                        char **err_msg)
+{
+    struct dp_get_account_state *state =
+            tevent_req_data(req, struct dp_get_account_state);
+
+    enum tevent_req_state TRROEstate;
+    uint64_t TRROEerr;
+
+    *err_maj = state->err_maj;
+    *err_min = state->err_min;
+    *err_msg = talloc_steal(mem_ctx, state->err_msg);
+
+    if (tevent_req_is_error(req, &TRROEstate, &TRROEerr)) {
+        if (TRROEstate == TEVENT_REQ_USER_ERROR) {
+            *err_maj = DP_ERR_FATAL;
+            *err_min = TRROEerr;
+        } else {
+            return EIO;
+        }
+    }
+
+    return EOK;
 }
 
-static int sss_dp_send_acct_req_create(struct resp_ctx *rctx,
-                                       TALLOC_CTX *callback_memctx,
-                                       const char *domain,
-                                       uint32_t be_type,
-                                       char *filter,
-                                       int timeout,
-                                       sss_dp_callback_t callback,
-                                       void *callback_ctx,
-                                       struct sss_dp_req **ndp)
+struct dp_get_account_int_state {
+    struct resp_ctx *rctx;
+    struct sss_domain_info *dom;
+    uint32_t be_type;
+    const char *filter;
+
+    struct sss_dp_req *sdp_req;
+    DBusPendingCall *pending_reply;
+};
+
+static void sss_dp_get_account_int_done(DBusPendingCall *pending, void *ptr);
+
+static struct tevent_req *
+sss_dp_get_account_int_send(struct resp_ctx *rctx,
+                            hash_key_t *key,
+                            struct sss_domain_info *dom,
+                            uint32_t be_type,
+                            const char *filter)
 {
+    errno_t ret;
+    int hret;
+    struct tevent_req *req;
+    struct dp_get_account_int_state *state;
+    struct be_conn *be_conn;
     DBusMessage *msg;
-    DBusPendingCall *pending_reply;
     dbus_bool_t dbret;
-    struct sss_dp_callback *cb;
-    struct sss_dp_req *sdp_req;
+    bool msg_created = false;
+    hash_value_t value;
     uint32_t attrs = BE_ATTR_CORE;
-    struct be_conn *be_conn;
-    int ret;
+
+    /* Internal requests need to be allocated on the responder context
+     * so that they don't go away if a client disconnects. The worst-
+     * case scenario here is that the cache is updated without any
+     * client expecting a response.
+     */
+    req = tevent_req_create(rctx,
+                            &state,
+                            struct dp_get_account_int_state);
+    if (!req)  return NULL;
+
+    state->rctx = rctx;
+    state->dom = dom;
+    state->be_type = be_type;
+    state->filter = filter;
+
+    state->sdp_req = talloc_zero(state, struct sss_dp_req);
+    if (!state->sdp_req) {
+        ret = ENOMEM;
+        goto error;
+    }
+    state->sdp_req->rctx = rctx;
+    state->sdp_req->ev = rctx->ev;
+
+    /* Copy the key to use when calling the destructor
+     * It needs to be a copy because the original request
+     * might be freed if it no longer cares about the reply.
+     */
+    state->sdp_req->key = talloc_steal(state->sdp_req, key);
 
     /* double check dp_ctx has actually been initialized.
      * in some pathological cases it may happen that nss starts up before
      * dp connection code is actually able to establish a connection.
      */
-    ret = sss_dp_get_domain_conn(rctx, domain, &be_conn);
+    ret = sss_dp_get_domain_conn(rctx, dom->name, &be_conn);
     if (ret != EOK) {
-        DEBUG(1, ("The Data Provider connection for %s is not available!"
-                  " This maybe a bug, it shouldn't happen!\n", domain));
-        return EIO;
+        DEBUG(SSSDBG_CRIT_FAILURE,
+              ("BUG: The Data Provider connection for %s is not available!",
+               dom->name));
+        ret = EIO;
+        goto error;
     }
 
     /* create the message */
@@ -467,11 +529,14 @@ static int sss_dp_send_acct_req_create(struct resp_ctx *rctx,
                                        DP_METHOD_GETACCTINFO);
     if (msg == NULL) {
         DEBUG(0,("Out of memory?!\n"));
-        return ENOMEM;
+        ret = ENOMEM;
+        goto error;
     }
+    msg_created = true;
 
-    DEBUG(4, ("Sending request for [%s][%u][%d][%s]\n",
-              domain, be_type, attrs, filter));
+    DEBUG(SSSDBG_TRACE_FUNC,
+          ("Sending request for [%s][%u][%d][%s]\n",
+           dom->name, be_type, attrs, filter));
 
     dbret = dbus_message_append_args(msg,
                                      DBUS_TYPE_UINT32, &be_type,
@@ -480,125 +545,141 @@ static int sss_dp_send_acct_req_create(struct resp_ctx *rctx,
                                      DBUS_TYPE_INVALID);
     if (!dbret) {
         DEBUG(1,("Failed to build message\n"));
-        return EIO;
+        ret = EIO;
+        goto error;
     }
 
-    sdp_req = talloc_zero(rctx, struct sss_dp_req);
-    if (!sdp_req) {
-        dbus_message_unref(msg);
-        return ENOMEM;
-    }
-    sdp_req->rctx = rctx;
-
-    ret = sbus_conn_send(be_conn->conn, msg, timeout,
-                         sss_dp_send_acct_callback,
-                         sdp_req, &pending_reply);
+    ret = sbus_conn_send(be_conn->conn, msg,
+                         SSS_CLI_SOCKET_TIMEOUT / 2,
+                         sss_dp_get_account_int_done,
+                         req,
+                         &state->sdp_req->pending_reply);
     dbus_message_unref(msg);
+    msg_created = false;
     if (ret != EOK) {
         /*
          * Critical Failure
          * We can't communicate on this connection
-         * We'll drop it using the default destructor.
          */
-        DEBUG(0, ("D-BUS send failed.\n"));
-        return EIO;
+        DEBUG(SSSDBG_CRIT_FAILURE,
+              ("D-BUS send failed.\n"));
+        ret = EIO;
+        goto error;
     }
 
-    sdp_req->ev = rctx->ev;
-    sdp_req->pending_reply = pending_reply;
+    /* Add this sdp_req to the hash table */
+    value.type = HASH_VALUE_PTR;
+    value.ptr = state->sdp_req;
 
-    if (callback) {
-        cb = talloc_zero(callback_memctx, struct sss_dp_callback);
-        if (!cb) {
-            talloc_zfree(sdp_req);
-            return ENOMEM;
+    hret = hash_enter(rctx->dp_request_table, key, &value);
+    if (hret != HASH_SUCCESS) {
+        DEBUG(SSSDBG_CRIT_FAILURE,
+              ("Could not store request query (%s)\n",
+               hash_error_string(hret)));
+        ret = EIO;
+        goto error;
+    }
+    talloc_set_destructor((TALLOC_CTX *)state->sdp_req,
+                          sss_dp_req_destructor);
+
+    return req;
+
+error:
+    if (msg_created) {
+        dbus_message_unref(msg);
+    }
+
+    tevent_req_error(req, ret);
+    tevent_req_post(req, rctx->ev);
+    return req;
+}
+
+static void sss_dp_get_account_int_done(DBusPendingCall *pending, void *ptr)
+{
+    int ret;
+    struct tevent_req *req;
+    struct sss_dp_req *sdp_req;
+    struct sss_dp_callback *cb;
+    struct dp_get_account_int_state *state;
+    struct dp_get_account_state *cb_state;
+
+    req = talloc_get_type(ptr, struct tevent_req);
+    state = tevent_req_data(req, struct dp_get_account_int_state);
+    sdp_req = state->sdp_req;
+
+    /* prevent trying to cancel a reply that we already received */
+    sdp_req->pending_reply = NULL;
+
+    ret = sss_dp_get_reply(pending,
+                           &sdp_req->err_maj,
+                           &sdp_req->err_min,
+                           &sdp_req->err_msg);
+    if (ret != EOK) {
+        if (ret == ETIME) {
+            sdp_req->err_maj = DP_ERR_TIMEOUT;
+            sdp_req->err_min = ret;
+            sdp_req->err_msg = talloc_strdup(sdp_req, "Request timed out");
         }
-        cb->callback = callback;
-        cb->callback_ctx = callback_ctx;
-        cb->sdp_req = sdp_req;
+        else {
+            sdp_req->err_maj = DP_ERR_FATAL;
+            sdp_req->err_min = ret;
+            sdp_req->err_msg =
+                talloc_strdup(sdp_req,
+                              "Failed to get reply from Data Provider");
+        }
+    }
 
-        DLIST_ADD(sdp_req->cb_list, cb);
-        talloc_set_destructor((TALLOC_CTX *)cb, sss_dp_callback_destructor);
+    /* Check whether we need to issue any callbacks */
+    DLIST_FOR_EACH(cb, sdp_req->cb_list) {
+        cb_state = tevent_req_data(cb->req, struct dp_get_account_state);
+        cb_state->err_maj = sdp_req->err_maj;
+        cb_state->err_min = sdp_req->err_min;
+        cb_state->err_msg = talloc_strdup(cb_state, sdp_req->err_msg);
+        /* Don't bother checking for NULL. If it fails due to ENOMEM,
+         * we can't really handle it annyway.
+         */
+
+        if (ret == EOK) {
+            tevent_req_done(cb->req);
+        } else {
+            tevent_req_error(cb->req, ret);
+        }
+
+        /* Remove each callback from the list as it's replied to */
+        DLIST_REMOVE(sdp_req->cb_list, cb);
     }
 
-    *ndp = sdp_req;
+    /* We're done with this request. Free the sdp_req
+     * This will clean up the hash table entry as well
+     */
+    talloc_zfree(sdp_req);
+}
+
+static errno_t
+sss_dp_get_account_int_recv(TALLOC_CTX *mem_ctx,
+                        struct tevent_req *req,
+                        dbus_uint16_t *err_maj,
+                        dbus_uint32_t *err_min,
+                        char **err_msg)
+{
+    struct dp_get_account_int_state *state =
+            tevent_req_data(req, struct dp_get_account_int_state);
+
+    enum tevent_req_state TRROEstate;
+    uint64_t TRROEerr;
+
+    *err_maj = state->sdp_req->err_maj;
+    *err_min = state->sdp_req->err_min;
+    *err_msg = talloc_steal(mem_ctx, state->sdp_req->err_msg);
+
+    if (tevent_req_is_error(req, &TRROEstate, &TRROEerr)) {
+        if (TRROEstate == TEVENT_REQ_USER_ERROR) {
+            *err_maj = DP_ERR_FATAL;
+            *err_min = TRROEerr;
+        } else {
+            return EIO;
+        }
+    }
 
     return EOK;
 }
-
-static int sss_dp_get_reply(DBusPendingCall *pending,
-                            dbus_uint16_t *err_maj,
-                            dbus_uint32_t *err_min,
-                            char **err_msg)
-{
-    DBusMessage *reply;
-    DBusError dbus_error;
-    dbus_bool_t ret;
-    int type;
-    int err = EOK;
-
-    dbus_error_init(&dbus_error);
-
-    reply = dbus_pending_call_steal_reply(pending);
-    if (!reply) {
-        /* reply should never be null. This function shouldn't be called
-         * until reply is valid or timeout has occurred. If reply is NULL
-         * here, something is seriously wrong and we should bail out.
-         */
-        DEBUG(0, ("Severe error. A reply callback was called but no reply was received and no timeout occurred\n"));
-
-        /* FIXME: Destroy this connection ? */
-        err = EIO;
-        goto done;
-    }
-
-    type = dbus_message_get_type(reply);
-    switch (type) {
-    case DBUS_MESSAGE_TYPE_METHOD_RETURN:
-        ret = dbus_message_get_args(reply, &dbus_error,
-                                    DBUS_TYPE_UINT16, err_maj,
-                                    DBUS_TYPE_UINT32, err_min,
-                                    DBUS_TYPE_STRING, err_msg,
-                                    DBUS_TYPE_INVALID);
-        if (!ret) {
-            DEBUG(1,("Failed to parse message\n"));
-            /* FIXME: Destroy this connection ? */
-            if (dbus_error_is_set(&dbus_error)) dbus_error_free(&dbus_error);
-            err = EIO;
-            goto done;
-        }
-
-        DEBUG(4, ("Got reply (%u, %u, %s) from Data Provider\n",
-                  (unsigned int)*err_maj, (unsigned int)*err_min, *err_msg));
-
-        break;
-
-    case DBUS_MESSAGE_TYPE_ERROR:
-        if (strcmp(dbus_message_get_error_name(reply),
-                   DBUS_ERROR_NO_REPLY) == 0) {
-            err = ETIME;
-            goto done;
-        }
-        DEBUG(0,("The Data Provider returned an error [%s]\n",
-                 dbus_message_get_error_name(reply)));
-        /* Falling through to default intentionally*/
-    default:
-        /*
-         * Timeout or other error occurred or something
-         * unexpected happened.
-         * It doesn't matter which, because either way we
-         * know that this connection isn't trustworthy.
-         * We'll destroy it now.
-         */
-
-        /* FIXME: Destroy this connection ? */
-        err = EIO;
-    }
-
-done:
-    dbus_pending_call_unref(pending);
-    dbus_message_unref(reply);
-
-    return err;
-}
-
diff --git a/src/responder/nss/nsssrv_cmd.c b/src/responder/nss/nsssrv_cmd.c
index a37bd76641b04b004473464bf830b14f180f63a4..b14d4c3542d3bbbdbb01181e9716e60fc5e7983a 100644
--- a/src/responder/nss/nsssrv_cmd.c
+++ b/src/responder/nss/nsssrv_cmd.c
@@ -533,6 +533,7 @@ static int nss_cmd_getpw_send_reply(struct nss_dom_ctx *dctx, bool filter)
     return EOK;
 }
 
+static void nsssrv_dp_send_acct_req_done(struct tevent_req *req);
 
 /* FIXME: do not check res->count, but get in a msgs and check in parent */
 /* FIXME: do not sss_cmd_done, but return error and let parent do it */
@@ -546,7 +547,6 @@ errno_t check_cache(struct nss_dom_ctx *dctx,
                     void *pvt)
 {
     errno_t ret;
-    int timeout;
     time_t now;
     uint64_t lastUpdate;
     uint64_t cacheExpire = 0;
@@ -554,6 +554,8 @@ errno_t check_cache(struct nss_dom_ctx *dctx,
     struct nss_cmd_ctx *cmdctx = dctx->cmdctx;
     struct cli_ctx *cctx = cmdctx->cctx;
     bool off_band_update = false;
+    struct tevent_req *req = NULL;
+    struct dp_callback_ctx *cb_ctx = NULL;
 
     /* when searching for a user or netgroup, more than one reply is a
      * db error
@@ -620,26 +622,27 @@ errno_t check_cache(struct nss_dom_ctx *dctx,
     }
 
     if (off_band_update) {
-
-        timeout = SSS_CLI_SOCKET_TIMEOUT/2;
-
         /* No callback required
          * This was an out-of-band update. We'll return EOK
          * so the calling function can return the cached entry
          * immediately.
          */
-        ret = sss_dp_send_acct_req(cctx->rctx, NULL, NULL, NULL,
-                                   timeout, dctx->domain->name,
-                                   true, req_type,
-                                   opt_name, opt_id);
-        if (ret != EOK) {
-            DEBUG(3, ("Failed to dispatch request: %d(%s)\n",
-                      ret, strerror(ret)));
+        req = sss_dp_get_account_send(cctx, cctx->rctx, dctx->domain, true,
+                                      req_type, opt_name, opt_id);
+        if (!req) {
+            DEBUG(SSSDBG_CRIT_FAILURE,
+                  ("Out of memory sending out-of-band data provider "
+                   "request\n"));
+            /* This is non-fatal, so we'll continue here */
         } else {
-
-            DEBUG(3, ("Updating cache out-of-band\n"));
+            DEBUG(SSSDBG_TRACE_FUNC, ("Updating cache out-of-band\n"));
         }
 
+        /* We don't need to listen for a reply, so we will free the
+         * request here.
+         */
+        talloc_zfree(req);
+
     } else {
        /* This is a cache miss. Or the cache is expired.
         * We need to get the updated user information before returning it.
@@ -647,32 +650,66 @@ errno_t check_cache(struct nss_dom_ctx *dctx,
 
         /* dont loop forever :-) */
         dctx->check_provider = false;
-        timeout = SSS_CLI_SOCKET_TIMEOUT/2;
 
         /* keep around current data in case backend is offline */
         if (res->count) {
             dctx->res = talloc_steal(dctx, res);
         }
 
-        ret = sss_dp_send_acct_req(cctx->rctx, cmdctx,
-                                   callback, pvt, timeout,
-                                   dctx->domain->name,
-                                   true, req_type,
-                                   opt_name, opt_id);
-        if (ret != EOK) {
-            DEBUG(3, ("Failed to dispatch request: %d(%s)\n",
-                      ret, strerror(ret)));
-            ret = nss_cmd_send_error(cmdctx, ret);
-            if (ret != EOK) {
-                NSS_CMD_FATAL_ERROR_CODE(cctx, EIO);
-            }
-            sss_cmd_done(cctx, cmdctx);
+        req = sss_dp_get_account_send(dctx, cctx->rctx, dctx->domain, true,
+                                      req_type, opt_name, opt_id);
+        if (!req) {
+            DEBUG(SSSDBG_CRIT_FAILURE,
+                  ("Out of memory sending data provider request\n"));
+            ret = ENOMEM;
+            goto error;
         }
 
+        cb_ctx = talloc_zero(dctx, struct dp_callback_ctx);
+        if(!cb_ctx) {
+            talloc_zfree(req);
+            ret = ENOMEM;
+            goto error;
+        }
+        cb_ctx->callback = callback;
+        cb_ctx->ptr = dctx;
+        cb_ctx->cctx = dctx->cmdctx->cctx;
+        cb_ctx->mem_ctx = dctx;
+
+        tevent_req_set_callback(req, nsssrv_dp_send_acct_req_done, cb_ctx);
+
         return EAGAIN;
     }
 
     return EOK;
+
+error:
+    ret = nss_cmd_send_error(cmdctx, ENOMEM);
+    if (ret != EOK) {
+        NSS_CMD_FATAL_ERROR_CODE(cctx, ret);
+    }
+    sss_cmd_done(cctx, cmdctx);
+    return EOK;
+}
+
+static void nsssrv_dp_send_acct_req_done(struct tevent_req *req)
+{
+    struct dp_callback_ctx *cb_ctx =
+            tevent_req_callback_data(req, struct dp_callback_ctx);
+
+    errno_t ret;
+    dbus_uint16_t err_maj;
+    dbus_uint32_t err_min;
+    char *err_msg;
+
+    ret = sss_dp_get_account_recv(cb_ctx->mem_ctx, req,
+                                  &err_maj, &err_min,
+                                  &err_msg);
+    if (ret != EOK) {
+        NSS_CMD_FATAL_ERROR(cb_ctx->cctx);
+    }
+
+    cb_ctx->callback(err_maj, err_min, err_msg, cb_ctx->ptr);
 }
 
 static void nss_cmd_getpwnam_dp_callback(uint16_t err_maj, uint32_t err_min,
@@ -1274,6 +1311,7 @@ struct tevent_req *nss_cmd_setpwent_send(TALLOC_CTX *mem_ctx,
     step_ctx->nctx = state->nctx;
     step_ctx->getent_ctx = state->getent_ctx;
     step_ctx->rctx = client->rctx;
+    step_ctx->cctx = client;
     step_ctx->returned_to_mainloop = false;
 
     ret = nss_cmd_setpwent_step(step_ctx);
@@ -1315,7 +1353,8 @@ static errno_t nss_cmd_setpwent_step(struct setent_step_ctx *step_ctx)
     struct setent_req_list *req;
     struct timeval tv;
     struct tevent_timer *te;
-    int timeout;
+    struct tevent_req *dpreq;
+    struct dp_callback_ctx *cb_ctx;
 
     while (dom) {
         while (dom && dom->enumerate == 0) {
@@ -1347,18 +1386,29 @@ static errno_t nss_cmd_setpwent_step(struct setent_step_ctx *step_ctx)
             step_ctx->returned_to_mainloop = true;
             /* Only do this once per provider */
             dctx->check_provider = false;
-            timeout = SSS_CLI_SOCKET_TIMEOUT;
 
-            ret = sss_dp_send_acct_req(rctx, step_ctx,
-                                       nss_cmd_setpwent_dp_callback, step_ctx,
-                                       timeout, dom->name, true,
-                                       SSS_DP_USER, NULL, 0);
-            if (ret == EOK) {
-                return EAGAIN;
+            dpreq = sss_dp_get_account_send(step_ctx, rctx, dctx->domain, true,
+                                          SSS_DP_USER, NULL, 0);
+            if (!dpreq) {
+                DEBUG(SSSDBG_MINOR_FAILURE,
+                      ("Enum Cache refresh for domain [%s] failed."
+                       " Trying to return what we have in cache!\n",
+                       dom->name));
             } else {
-                DEBUG(2, ("Enum Cache refresh for domain [%s] failed."
-                          " Trying to return what we have in cache!\n",
-                          dom->name));
+                cb_ctx = talloc_zero(step_ctx, struct dp_callback_ctx);
+                if(!cb_ctx) {
+                    talloc_zfree(dpreq);
+                    return ENOMEM;
+                }
+
+                cb_ctx->callback = nss_cmd_setpwent_dp_callback;
+                cb_ctx->ptr = step_ctx;
+                cb_ctx->cctx = step_ctx->cctx;
+                cb_ctx->mem_ctx = step_ctx;
+
+                tevent_req_set_callback(dpreq, nsssrv_dp_send_acct_req_done, cb_ctx);
+
+                return EAGAIN;
             }
         }
 
@@ -2541,6 +2591,7 @@ struct tevent_req *nss_cmd_setgrent_send(TALLOC_CTX *mem_ctx,
     step_ctx->nctx = state->nctx;
     step_ctx->getent_ctx = state->getent_ctx;
     step_ctx->rctx = client->rctx;
+    step_ctx->cctx = client;
     step_ctx->returned_to_mainloop = false;
 
     ret = nss_cmd_setgrent_step(step_ctx);
@@ -2582,7 +2633,8 @@ static errno_t nss_cmd_setgrent_step(struct setent_step_ctx *step_ctx)
     struct setent_req_list *req;
     struct timeval tv;
     struct tevent_timer *te;
-    int timeout;
+    struct tevent_req *dpreq;
+    struct dp_callback_ctx *cb_ctx;
 
     while (dom) {
         while (dom && dom->enumerate == 0) {
@@ -2613,19 +2665,28 @@ static errno_t nss_cmd_setgrent_step(struct setent_step_ctx *step_ctx)
         if (dctx->check_provider) {
             step_ctx->returned_to_mainloop = true;
             /* Only do this once per provider */
-            dctx->check_provider = false;
-            timeout = SSS_CLI_SOCKET_TIMEOUT;
-
-            ret = sss_dp_send_acct_req(rctx, step_ctx,
-                                       nss_cmd_setgrent_dp_callback, step_ctx,
-                                       timeout, dom->name, true,
-                                       SSS_DP_GROUP, NULL, 0);
-            if (ret == EOK) {
-                return EAGAIN;
+            dpreq = sss_dp_get_account_send(step_ctx, rctx, dctx->domain, true,
+                                          SSS_DP_USER, NULL, 0);
+            if (!dpreq) {
+                DEBUG(SSSDBG_MINOR_FAILURE,
+                      ("Enum Cache refresh for domain [%s] failed."
+                       " Trying to return what we have in cache!\n",
+                       dom->name));
             } else {
-                DEBUG(2, ("Enum Cache refresh for domain [%s] failed."
-                          " Trying to return what we have in cache!\n",
-                          dom->name));
+                cb_ctx = talloc_zero(step_ctx, struct dp_callback_ctx);
+                if(!cb_ctx) {
+                    talloc_zfree(dpreq);
+                    return ENOMEM;
+                }
+
+                cb_ctx->callback = nss_cmd_setgrent_dp_callback;
+                cb_ctx->ptr = step_ctx;
+                cb_ctx->cctx = step_ctx->cctx;
+                cb_ctx->mem_ctx = step_ctx;
+
+                tevent_req_set_callback(dpreq, nsssrv_dp_send_acct_req_done, cb_ctx);
+
+                return EAGAIN;
             }
         }
 
diff --git a/src/responder/nss/nsssrv_private.h b/src/responder/nss/nsssrv_private.h
index 27b0e567e488008cccfd283101c32fe3fe483b03..2aa322c5ba673cfffc8734a65094e63c522a7083 100644
--- a/src/responder/nss/nsssrv_private.h
+++ b/src/responder/nss/nsssrv_private.h
@@ -85,6 +85,7 @@ struct setent_step_ctx {
     struct nss_dom_ctx *dctx;
     struct getent_ctx *getent_ctx;
     struct resp_ctx *rctx;
+    struct cli_ctx *cctx;
     bool check_next;
 
     bool returned_to_mainloop;
diff --git a/src/responder/pam/pamsrv_cmd.c b/src/responder/pam/pamsrv_cmd.c
index 18ba3fdf3fd1481846f7e30d56b5b227951fdc86..2a000f095d5f76663040961f4dcbb355d01e2234 100644
--- a/src/responder/pam/pamsrv_cmd.c
+++ b/src/responder/pam/pamsrv_cmd.c
@@ -807,6 +807,8 @@ done:
     return pam_check_user_done(preq, ret);
 }
 
+static void pam_dp_send_acct_req_done(struct tevent_req *req);
+
 static int pam_check_user_search(struct pam_auth_req *preq)
 {
     struct sss_domain_info *dom = preq->domain;
@@ -815,6 +817,8 @@ static int pam_check_user_search(struct pam_auth_req *preq)
     struct sysdb_ctx *sysdb;
     time_t cacheExpire;
     int ret;
+    struct tevent_req *dpreq;
+    struct dp_callback_ctx *cb_ctx;
 
     while (dom) {
        /* if it is a domainless search, skip domains that require fully
@@ -904,18 +908,28 @@ static int pam_check_user_search(struct pam_auth_req *preq)
         /* dont loop forever :-) */
         preq->check_provider = false;
 
-        ret = sss_dp_send_acct_req(preq->cctx->rctx, preq,
-                                   pam_check_user_dp_callback, preq,
-                                   SSS_CLI_SOCKET_TIMEOUT/2,
-                                   dom->name, false,
-                                   SSS_DP_INITGROUPS,
-                                   name, 0);
-        if (ret != EOK) {
-            DEBUG(3, ("Failed to dispatch request: %d(%s)\n",
-                      ret, strerror(ret)));
-            preq->pd->pam_status = PAM_SYSTEM_ERR;
-            return EIO;
+        dpreq = sss_dp_get_account_send(preq, preq->cctx->rctx,
+                                        dom, false, SSS_DP_INITGROUPS,
+                                        name, 0);
+        if (!dpreq) {
+            DEBUG(SSSDBG_CRIT_FAILURE,
+                  ("Out of memory sending data provider request\n"));
+            return ENOMEM;
         }
+
+        cb_ctx = talloc_zero(preq, struct dp_callback_ctx);
+        if(!cb_ctx) {
+            talloc_zfree(dpreq);
+            return ENOMEM;
+        }
+
+        cb_ctx->callback = pam_check_user_dp_callback;
+        cb_ctx->ptr = preq;
+        cb_ctx->cctx = preq->cctx;
+        cb_ctx->mem_ctx = preq;
+
+        tevent_req_set_callback(dpreq, pam_dp_send_acct_req_done, cb_ctx);
+
         /* tell caller we are in an async call */
         return EAGAIN;
     }
@@ -924,6 +938,29 @@ static int pam_check_user_search(struct pam_auth_req *preq)
     return ENOENT;
 }
 
+static void pam_dp_send_acct_req_done(struct tevent_req *req)
+{
+    struct dp_callback_ctx *cb_ctx =
+            tevent_req_callback_data(req, struct dp_callback_ctx);
+
+    errno_t ret;
+    dbus_uint16_t err_maj;
+    dbus_uint32_t err_min;
+    char *err_msg;
+
+    ret = sss_dp_get_account_recv(cb_ctx->mem_ctx, req,
+                                  &err_maj, &err_min,
+                                  &err_msg);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_CRIT_FAILURE,
+              ("Fatal error, killing connection!\n"));
+        talloc_free(cb_ctx->cctx);
+        return;
+    }
+
+    cb_ctx->callback(err_maj, err_min, err_msg, cb_ctx->ptr);
+}
+
 static int pam_check_user_done(struct pam_auth_req *preq, int ret)
 {
     switch (ret) {
-- 
1.7.7.3

