>From a34fce168af4d3755faacf095dfffb1dbe5505f7 Mon Sep 17 00:00:00 2001 From: eindenbom Date: Wed, 7 Apr 2010 16:51:20 +0400 Subject: [PATCH 4/4] The LDAP ID backend connection logic has been refactored to: - Allow failover retries after connection breakdown during operation - Avoid opening 2 connections in parallel --- src/providers/ipa/ipa_access.c | 357 +++++++++----- src/providers/ldap/ldap_common.c | 711 +++++++++++++++++++++++----- src/providers/ldap/ldap_common.h | 58 ++- src/providers/ldap/ldap_id.c | 498 +++++++++----------- src/providers/ldap/ldap_id_enum.c | 402 ++++++++++------- src/providers/ldap/sdap_async.h | 1 + src/providers/ldap/sdap_async_connection.c | 4 + 7 files changed, 1340 insertions(+), 691 deletions(-) diff --git a/src/providers/ipa/ipa_access.c b/src/providers/ipa/ipa_access.c index 55a7133..a8f1a4e 100644 --- a/src/providers/ipa/ipa_access.c +++ b/src/providers/ipa/ipa_access.c @@ -311,10 +311,10 @@ static int hbac_get_user_info_recv(struct tevent_req *req, TALLOC_CTX *memctx, return EOK; } - struct hbac_get_host_info_state { struct tevent_context *ev; struct sdap_id_ctx *sdap_ctx; + struct sdap_id_op *sdap_op; struct sysdb_ctx *sysdb; struct sysdb_handle *handle; bool offline; @@ -329,7 +329,10 @@ struct hbac_get_host_info_state { struct hbac_host_info **hbac_host_info; }; +static bool hbac_get_host_info_connect_retry(struct tevent_req *req); static void hbac_get_host_info_connect_done(struct tevent_req *subreq); +static bool hbac_get_host_memberof_sdap_send(struct tevent_req *req); +static bool hbac_get_host_memberof_sysdb_send(struct tevent_req *req); static void hbac_get_host_memberof_done(struct tevent_req *subreq); static void hbac_get_host_info_sysdb_transaction_started(struct tevent_req *subreq); static void hbac_get_host_info_store_prepare(struct tevent_req *req); @@ -344,7 +347,6 @@ static struct tevent_req *hbac_get_host_info_send(TALLOC_CTX *memctx, const char **hostnames) { struct tevent_req *req = NULL; - struct tevent_req *subreq = NULL; struct hbac_get_host_info_state *state; int ret; int i; @@ -416,83 +418,91 @@ static struct tevent_req *hbac_get_host_info_send(TALLOC_CTX *memctx, state->host_attrs[6] = NULL; if (offline) { - subreq = sysdb_search_custom_send(state, state->ev, state->sysdb, NULL, - state->sdap_ctx->be->domain, - state->host_filter, HBAC_HOSTS_SUBDIR, - state->host_attrs); - if (subreq == NULL) { - DEBUG(1, ("sysdb_search_custom_send.\n")); + if (!hbac_get_host_memberof_sysdb_send(req)) { + tevent_req_post(req, ev); + } + } else { + state->sdap_op = sdap_id_op_create(state, sdap_ctx); + if (!state->sdap_op) { + DEBUG(1, ("sdap_id_op_create failed.\n")); ret = ENOMEM; goto fail; } - tevent_req_set_callback(subreq, hbac_get_host_memberof_done, req); - - return req; + if (!hbac_get_host_info_connect_retry(req)) { + tevent_req_post(req, ev); + } } - if (sdap_ctx->gsh == NULL || ! sdap_ctx->gsh->connected) { - if (sdap_ctx->gsh != NULL) { - talloc_zfree(sdap_ctx->gsh); - } + return req; - subreq = sdap_cli_connect_send(state, ev, sdap_ctx->opts, - sdap_ctx->be, sdap_ctx->service, NULL); - if (!subreq) { - DEBUG(1, ("sdap_cli_connect_send failed.\n")); - ret = ENOMEM; - goto fail; - } +fail: + tevent_req_error(req, ret); + tevent_req_post(req, ev); + return req; +} + +/* start an attempt to get host info from LDAP */ +static bool hbac_get_host_info_connect_retry(struct tevent_req *req) +{ + struct hbac_get_host_info_state *state = tevent_req_data(req, + struct hbac_get_host_info_state); + struct tevent_req *subreq; + + int ret = sdap_id_op_connect(state->sdap_op, state, &subreq); + if (ret) { + talloc_zfree(subreq); + + tevent_req_error(req, ret); + return false; + } + if (subreq) { tevent_req_set_callback(subreq, hbac_get_host_info_connect_done, req); - - return req; - } - - subreq = sdap_get_generic_send(state, state->ev, - state->sdap_ctx->opts, - state->sdap_ctx->gsh, - state->host_search_base, - LDAP_SCOPE_SUB, - state->host_filter, - state->host_attrs, - NULL, 0); - - if (subreq == NULL) { - DEBUG(1, ("sdap_get_generic_send failed.\n")); - ret = ENOMEM; - goto fail; + return true; } - tevent_req_set_callback(subreq, hbac_get_host_memberof_done, req); - - return req; - -fail: - tevent_req_error(req, ret); - tevent_req_post(req, ev); - return req; + return hbac_get_host_memberof_sdap_send(req); } +/* connect completion callback */ static void hbac_get_host_info_connect_done(struct tevent_req *subreq) { struct tevent_req *req = tevent_req_callback_data(subreq, struct tevent_req); struct hbac_get_host_info_state *state = tevent_req_data(req, - struct hbac_get_host_info_state); - int ret; + struct hbac_get_host_info_state); + int dp_error, ret; - ret = sdap_cli_connect_recv(subreq, state->sdap_ctx, &state->sdap_ctx->gsh, - NULL); + ret = sdap_id_op_connect_recv(subreq, &dp_error); talloc_zfree(subreq); - if (ret) { - tevent_req_error(req, ret); + + if (ret == EOK) { + hbac_get_host_memberof_sdap_send(req); return; } + if (dp_error == DP_ERR_OFFLINE) { + /* backend is offline, switching to offline */ + state->offline = true; + hbac_get_host_memberof_sysdb_send(req); + return; + } + + tevent_req_error(req, ret); + return; +} + +/* send LDAP memberof request */ +static bool hbac_get_host_memberof_sdap_send(struct tevent_req *req) +{ + struct hbac_get_host_info_state *state = tevent_req_data(req, + struct hbac_get_host_info_state); + struct tevent_req *subreq; + subreq = sdap_get_generic_send(state, state->ev, state->sdap_ctx->opts, - state->sdap_ctx->gsh, + sdap_id_op_handle(state->sdap_op), state->host_search_base, LDAP_SCOPE_SUB, state->host_filter, @@ -501,17 +511,33 @@ static void hbac_get_host_info_connect_done(struct tevent_req *subreq) if (subreq == NULL) { DEBUG(1, ("sdap_get_generic_send failed.\n")); - ret = ENOMEM; - goto fail; + tevent_req_error(req, ENOMEM); + return false; } tevent_req_set_callback(subreq, hbac_get_host_memberof_done, req); + return true; +} - return; +/* send sysdb memberof request */ +static bool hbac_get_host_memberof_sysdb_send(struct tevent_req *req) +{ + struct hbac_get_host_info_state *state = tevent_req_data(req, + struct hbac_get_host_info_state); + struct tevent_req *subreq; -fail: - tevent_req_error(req, ret); - return; + subreq = sysdb_search_custom_send(state, state->ev, state->sysdb, NULL, + state->sdap_ctx->be->domain, + state->host_filter, HBAC_HOSTS_SUBDIR, + state->host_attrs); + if (subreq == NULL) { + DEBUG(1, ("sysdb_search_custom_send.\n")); + tevent_req_error(req, ENOMEM); + return false; + } + + tevent_req_set_callback(subreq, hbac_get_host_memberof_done, req); + return true; } static void hbac_get_host_memberof_done(struct tevent_req *subreq) @@ -520,7 +546,7 @@ static void hbac_get_host_memberof_done(struct tevent_req *subreq) struct tevent_req); struct hbac_get_host_info_state *state = tevent_req_data(req, struct hbac_get_host_info_state); - int ret; + int ret, dp_error; int i; int v; struct ldb_message_element *el; @@ -530,11 +556,29 @@ static void hbac_get_host_memberof_done(struct tevent_req *subreq) if (state->offline) { ret = sysdb_search_custom_recv(subreq, state, &state->host_reply_count, &msgs); + talloc_zfree(subreq); } else { ret = sdap_get_generic_recv(subreq, state, &state->host_reply_count, &state->host_reply_list); + talloc_zfree(subreq); + + ret = sdap_id_op_done(state->sdap_op, ret, &dp_error); + if (ret != EOK) { + if (dp_error == DP_ERR_OK) { + /* retry */ + hbac_get_host_info_connect_retry(req); + return; + } + + if (dp_error == DP_ERR_OFFLINE) { + /* backend is offline, switch to sysdb */ + state->offline = true; + hbac_get_host_memberof_sysdb_send(req); + return; + } + } } - talloc_zfree(subreq); + if (ret != EOK) { tevent_req_error(req, ret); return; @@ -789,6 +833,7 @@ static void hbac_get_host_info_store_done(struct tevent_req *subreq) } static int hbac_get_host_info_recv(struct tevent_req *req, TALLOC_CTX *memctx, + bool *offline, struct hbac_host_info ***hhi) { struct hbac_get_host_info_state *state = tevent_req_data(req, @@ -796,14 +841,15 @@ static int hbac_get_host_info_recv(struct tevent_req *req, TALLOC_CTX *memctx, TEVENT_REQ_RETURN_ON_ERROR(req); + *offline = state->offline; *hhi = talloc_steal(memctx, state->hbac_host_info); return EOK; } - struct hbac_get_rules_state { struct tevent_context *ev; struct sdap_id_ctx *sdap_ctx; + struct sdap_id_op *sdap_op; struct sysdb_ctx *sysdb; struct sysdb_handle *handle; bool offline; @@ -820,7 +866,10 @@ struct hbac_get_rules_state { int current_item; }; +static bool hbac_get_rules_connect_retry(struct tevent_req *req); static void hbac_get_rules_connect_done(struct tevent_req *subreq); +static bool hbac_rule_get_sdap_send(struct tevent_req *req); +static bool hbac_rule_get_sysdb_send(struct tevent_req *req); static void hbac_rule_get_done(struct tevent_req *subreq); static void hbac_rule_sysdb_transaction_started(struct tevent_req *subreq); static void hbac_rule_sysdb_delete_done(struct tevent_req *subreq); @@ -837,7 +886,6 @@ static struct tevent_req *hbac_get_rules_send(TALLOC_CTX *memctx, const char **memberof) { struct tevent_req *req = NULL; - struct tevent_req *subreq = NULL; struct hbac_get_rules_state *state; int ret; int i; @@ -925,83 +973,92 @@ static struct tevent_req *hbac_get_rules_send(TALLOC_CTX *memctx, DEBUG(9, ("HBAC rule filter: [%s].\n", state->hbac_filter)); if (offline) { - subreq = sysdb_search_custom_send(state, state->ev, state->sysdb, NULL, - state->sdap_ctx->be->domain, - state->hbac_filter, HBAC_RULES_SUBDIR, - state->hbac_attrs); - if (subreq == NULL) { - DEBUG(1, ("sysdb_search_custom_send failed.\n")); + if (!hbac_rule_get_sysdb_send(req)) { + tevent_req_post(req, ev); + } + } else { + state->sdap_op = sdap_id_op_create(state, sdap_ctx); + if (!state->sdap_op) { + DEBUG(1, ("sdap_id_op_create failed.\n")); ret = ENOMEM; goto fail; } - tevent_req_set_callback(subreq, hbac_rule_get_done, req); - - return req; + if (!hbac_get_rules_connect_retry(req)) { + tevent_req_post(req, ev); + } } - if (sdap_ctx->gsh == NULL || ! sdap_ctx->gsh->connected) { - if (sdap_ctx->gsh != NULL) { - talloc_zfree(sdap_ctx->gsh); - } + return req; - subreq = sdap_cli_connect_send(state, ev, sdap_ctx->opts, - sdap_ctx->be, sdap_ctx->service, NULL); - if (!subreq) { - DEBUG(1, ("sdap_cli_connect_send failed.\n")); - ret = ENOMEM; - goto fail; - } +fail: + tevent_req_error(req, ret); + tevent_req_post(req, ev); + return req; +} + +/* Start connect attempt to LDAP for rules query */ +static bool hbac_get_rules_connect_retry(struct tevent_req *req) +{ + struct hbac_get_rules_state *state = tevent_req_data(req, + struct hbac_get_rules_state); + struct tevent_req *subreq; + int ret; + + ret = sdap_id_op_connect(state->sdap_op, state, &subreq); + if (ret != EOK) { + talloc_zfree(subreq); + + tevent_req_error(req, ret); + return false; + } + if (subreq) { tevent_req_set_callback(subreq, hbac_get_rules_connect_done, req); - - return req; - } - - subreq = sdap_get_generic_send(state, state->ev, - state->sdap_ctx->opts, - state->sdap_ctx->gsh, - state->hbac_search_base, - LDAP_SCOPE_SUB, - state->hbac_filter, - state->hbac_attrs, - NULL, 0); - - if (subreq == NULL) { - DEBUG(1, ("sdap_get_generic_send failed.\n")); - ret = ENOMEM; - goto fail; + return true; } - tevent_req_set_callback(subreq, hbac_rule_get_done, req); - - return req; - -fail: - tevent_req_error(req, ret); - tevent_req_post(req, ev); - return req; + return hbac_rule_get_sdap_send(req); } +/* LDAP connect callback for rules query */ static void hbac_get_rules_connect_done(struct tevent_req *subreq) { struct tevent_req *req = tevent_req_callback_data(subreq, struct tevent_req); struct hbac_get_rules_state *state = tevent_req_data(req, - struct hbac_get_rules_state); - int ret; + struct hbac_get_rules_state); + int dp_error, ret; - ret = sdap_cli_connect_recv(subreq, state->sdap_ctx, &state->sdap_ctx->gsh, - NULL); + ret = sdap_id_op_connect_recv(subreq, &dp_error); talloc_zfree(subreq); - if (ret) { - tevent_req_error(req, ret); + + if (ret == EOK) { + hbac_rule_get_sdap_send(req); return; } + if (dp_error == DP_ERR_OFFLINE) { + /* backend is offline, switching to offline */ + state->offline = true; + hbac_rule_get_sysdb_send(req); + return; + } + + tevent_req_error(req, ret); + return; +} + +/* send LDAP rules query */ +static bool hbac_rule_get_sdap_send(struct tevent_req *req) +{ + struct hbac_get_rules_state *state = tevent_req_data(req, + struct hbac_get_rules_state); + struct tevent_req *subreq; + subreq = sdap_get_generic_send(state, state->ev, state->sdap_ctx->opts, - state->sdap_ctx->gsh, + sdap_id_op_handle(state->sdap_op), state->hbac_search_base, LDAP_SCOPE_SUB, state->hbac_filter, @@ -1010,16 +1067,33 @@ static void hbac_get_rules_connect_done(struct tevent_req *subreq) if (subreq == NULL) { DEBUG(1, ("sdap_get_generic_send failed.\n")); - ret = ENOMEM; - goto fail; + tevent_req_error(req, ENOMEM); + return false; } tevent_req_set_callback(subreq, hbac_rule_get_done, req); - return; + return true; +} -fail: - tevent_req_error(req, ret); - return; +/* send sysdb rules query */ +static bool hbac_rule_get_sysdb_send(struct tevent_req *req) +{ + struct hbac_get_rules_state *state = tevent_req_data(req, + struct hbac_get_rules_state); + struct tevent_req *subreq; + + subreq = sysdb_search_custom_send(state, state->ev, state->sysdb, NULL, + state->sdap_ctx->be->domain, + state->hbac_filter, HBAC_RULES_SUBDIR, + state->hbac_attrs); + if (subreq == NULL) { + DEBUG(1, ("sysdb_search_custom_send failed.\n")); + tevent_req_error(req, ENOMEM); + return false; + } + + tevent_req_set_callback(subreq, hbac_rule_get_done, req); + return true; } static void hbac_rule_get_done(struct tevent_req *subreq) @@ -1028,7 +1102,7 @@ static void hbac_rule_get_done(struct tevent_req *subreq) struct tevent_req); struct hbac_get_rules_state *state = tevent_req_data(req, struct hbac_get_rules_state); - int ret; + int ret, dp_error; int i; struct ldb_message_element *el; struct ldb_message **msgs; @@ -1036,11 +1110,29 @@ static void hbac_rule_get_done(struct tevent_req *subreq) if (state->offline) { ret = sysdb_search_custom_recv(subreq, state, &state->hbac_reply_count, &msgs); + talloc_zfree(subreq); } else { ret = sdap_get_generic_recv(subreq, state, &state->hbac_reply_count, &state->hbac_reply_list); + talloc_zfree(subreq); + + ret = sdap_id_op_done(state->sdap_op, ret, &dp_error); + if (ret != EOK) { + if (dp_error == DP_ERR_OK) { + /* retry */ + hbac_get_rules_connect_retry(req); + return; + } + + if (dp_error == DP_ERR_OFFLINE) { + /* backend is offline, switching to sysdb */ + state->offline = true; + hbac_rule_get_sysdb_send(req); + return; + } + } } - talloc_zfree(subreq); + if (ret != EOK) { tevent_req_error(req, ret); return; @@ -1227,6 +1319,7 @@ static void hbac_rule_store_done(struct tevent_req *subreq) } static int hbac_get_rules_recv(struct tevent_req *req, TALLOC_CTX *memctx, + bool *offline, size_t *hbac_rule_count, struct sysdb_attrs ***hbac_rule_list) { @@ -1236,6 +1329,7 @@ static int hbac_get_rules_recv(struct tevent_req *req, TALLOC_CTX *memctx, TEVENT_REQ_RETURN_ON_ERROR(req); + *offline = state->offline; *hbac_rule_count = state->hbac_reply_count; *hbac_rule_list = talloc_steal(memctx, state->hbac_reply_list); for (i = 0; i < state->hbac_reply_count; i++) { @@ -1713,14 +1807,19 @@ static void hbac_get_host_info_done(struct tevent_req *req) int pam_status = PAM_SYSTEM_ERR; const char *ipa_hostname; struct hbac_host_info *local_hhi = NULL; + bool offline; int i; - ret = hbac_get_host_info_recv(req, hbac_ctx, &hbac_ctx->hbac_host_info); + ret = hbac_get_host_info_recv(req, hbac_ctx, &offline, &hbac_ctx->hbac_host_info); talloc_zfree(req); if (ret != EOK) { goto fail; } + if (offline) { + hbac_ctx->offline = true; + } + ipa_hostname = dp_opt_get_cstring(hbac_ctx->ipa_options, IPA_HOSTNAME); if (ipa_hostname == NULL) { DEBUG(1, ("Missing ipa_hostname, this should never happen.\n")); @@ -1769,16 +1868,22 @@ static void hbac_get_rules_done(struct tevent_req *req) struct hbac_ctx *hbac_ctx = tevent_req_callback_data(req, struct hbac_ctx); struct pam_data *pd = hbac_ctx->pd; struct be_req *be_req = hbac_ctx->be_req; + bool offline; int ret; int pam_status = PAM_SYSTEM_ERR; - ret = hbac_get_rules_recv(req, hbac_ctx, &hbac_ctx->hbac_rule_count, + ret = hbac_get_rules_recv(req, hbac_ctx, &offline, + &hbac_ctx->hbac_rule_count, &hbac_ctx->hbac_rule_list); talloc_zfree(req); if (ret != EOK) { goto fail; } + if (offline) { + hbac_ctx->offline = offline; + } + req = hbac_get_user_info_send(hbac_ctx, be_req->be_ctx->ev, be_req->be_ctx, pd->user); if (req == NULL) { diff --git a/src/providers/ldap/ldap_common.c b/src/providers/ldap/ldap_common.c index b5765c2..4b5bf65 100644 --- a/src/providers/ldap/ldap_common.c +++ b/src/providers/ldap/ldap_common.c @@ -24,8 +24,9 @@ #include "providers/ldap/ldap_common.h" #include "providers/fail_over.h" +#include "providers/ldap/sdap_async.h" -#include "util/sss_krb5.h" +#include /* a fd the child process would log into */ int ldap_child_debug_fd = -1; @@ -345,162 +346,646 @@ void sdap_handler_done(struct be_req *req, int dp_err, return req->fn(req, dp_err, error, errstr); } -bool sdap_connected(struct sdap_id_ctx *ctx) +/* LDAP ID backend established connection data */ +struct sdap_id_conn_data { + /* ID backend context */ + struct sdap_id_ctx* ctx; + /* double linked list pointers */ + struct sdap_id_conn_data *prev, *next; + /* sdap handle */ + struct sdap_handle *sh; + /* what rootDSE returns */ + struct sysdb_attrs *rootDSE; + /* connection request */ + struct tevent_req *connect_req; + /* timer for connection expiration */ + struct tevent_timer *expire_timer; + /* number of running connection notifies */ + int notify_lock; + /* list of operations using connect */ + struct sdap_id_op *ops; +}; + +/* LDAP ID backend hi-level operation handle with reconnect support */ +struct sdap_id_op { + /* ID backend context */ + struct sdap_id_ctx* ctx; + /* double linked list pointers */ + struct sdap_id_op *prev, *next; + /* current connection */ + struct sdap_id_conn_data *conn_data; + /* number of reconnects for this operation */ + int reconnect_retry_count; + /* connection request + * It is required as we need to know which requests to notify + * when shared connection request to sdap_handle completes. + * This member is cleared when sdap_id_op_connect_state + * associated with request is destroyed */ + struct tevent_req* connect_req; +}; + +/* state of connect request */ +struct sdap_id_op_connect_state { + struct sdap_id_op* op; + int dp_error; + int result; +}; + +/* Release sdap_id_conn_data and destroy it if no longer needed */ +static void sdap_id_release_conn_data(struct sdap_id_conn_data *conn_data) { - if (ctx->gsh) { - return ctx->gsh->connected; + struct sdap_id_ctx *ctx; + if (!conn_data || conn_data->ops || conn_data->notify_lock) { + /* connection is in use */ + return; + } + + ctx = conn_data->ctx; + if (conn_data == ctx->cached_connection) { + return; + } + + DEBUG(9, ("releasing unused connection\n")); + + DLIST_REMOVE(ctx->connections, conn_data); + talloc_zfree(conn_data); +} + +/* Destructor for struct sdap_id_conn_data */ +static int sdap_id_conn_data_destroy(struct sdap_id_conn_data* conn_data) +{ + struct sdap_id_op* op; + + /* we clean out list of ops to make sure that order of destruction does not matter */ + while ((op = conn_data->ops) != NULL) { + op->conn_data = NULL; + DLIST_REMOVE(conn_data->ops, op); + } + + return 0; +} + +/* Check whether connection will expire after timeout seconds */ +static bool sdap_is_connection_expired(struct sdap_id_conn_data *conn_data, int timeout) +{ + time_t expire_time; + if (!conn_data || !conn_data->sh || !conn_data->sh->connected) { + return true; + } + + expire_time = conn_data->sh->expire_time; + if (expire_time != 0 && expire_time < time( NULL ) + timeout ) { + return true; } return false; } -void sdap_mark_offline(struct sdap_id_ctx *ctx) +/* Check whether connection can be reused for next LDAP ID operation */ +static bool sdap_can_reuse_connection(struct sdap_id_conn_data *conn_data) { - if (ctx->gsh) { - /* make sure we mark the connection as gone when we go offline so that - * we do not try to reuse a bad connection by mistale later */ - talloc_zfree(ctx->gsh); + int timeout; + + if (!conn_data || !conn_data->sh || !conn_data->sh->connected) { + return false; + } + + timeout = dp_opt_get_int(conn_data->ctx->opts->basic, SDAP_OPT_TIMEOUT); + return !sdap_is_connection_expired(conn_data, timeout); +} + +static void sdap_id_conn_data_expire_handler(struct tevent_context *ev, + struct tevent_timer *te, + struct timeval current_time, + void *pvt); + +/* Set expiration timer for connection if needed */ +static int sdap_id_conn_data_set_expire_timer(struct sdap_id_conn_data *conn_data) +{ + int timeout; + struct timeval tv; + + memset(&tv, 0, sizeof(tv)); + + tv.tv_sec = conn_data->sh->expire_time; + if (tv.tv_sec <= 0) { + return EOK; + } + + timeout = dp_opt_get_int(conn_data->ctx->opts->basic, SDAP_OPT_TIMEOUT); + if (timeout > 0) { + tv.tv_sec -= timeout; + } + + if (tv.tv_sec <= time(NULL)) { + return EOK; + } + + talloc_zfree(conn_data->expire_timer); + + conn_data->expire_timer = tevent_add_timer(conn_data->ctx->be->ev, + conn_data, + tv, + sdap_id_conn_data_expire_handler, + conn_data); + if (!conn_data->expire_timer) { + return ENOMEM; } - be_mark_offline(ctx->be); + return EOK; } -bool sdap_check_gssapi_reconnect(struct sdap_id_ctx *ctx) +/* Handler for connection expiration timer */ +static void sdap_id_conn_data_expire_handler(struct tevent_context *ev, + struct tevent_timer *te, + struct timeval current_time, + void *pvt) +{ + struct sdap_id_conn_data *conn_data = talloc_get_type(pvt, + struct sdap_id_conn_data); + struct sdap_id_ctx* ctx = conn_data->ctx; + + DEBUG(3, ("connection is about to expire, releasing it\n")); + + if (ctx->cached_connection == conn_data) { + ctx->cached_connection = NULL; + + sdap_id_release_conn_data(conn_data); + } +} + +static int sdap_id_op_destroy(struct sdap_id_op*); + +/* Create an operation object */ +struct sdap_id_op* sdap_id_op_create(TALLOC_CTX *memctx, struct sdap_id_ctx *ctx) +{ + struct sdap_id_op* op = talloc_zero(memctx, struct sdap_id_op); + if (!op) { + return NULL; + } + + op->ctx = ctx; + + talloc_set_destructor(op, sdap_id_op_destroy); + return op; +} + +/* Attach/detach connection to sdap_id_op */ +static void sdap_id_op_hook_conn_data(struct sdap_id_op *op, struct sdap_id_conn_data *conn_data) +{ + struct sdap_id_conn_data *current = op->conn_data; + if (conn_data == current) { + return; + } + + if (current) { + DLIST_REMOVE(current->ops, op); + } + + op->conn_data = conn_data; + + if (conn_data) { + DLIST_ADD_END(conn_data->ops, op, struct sdap_id_op*); + } + + if (current) { + sdap_id_release_conn_data(current); + } +} + +/* Destructor for sdap_id_op */ +static int sdap_id_op_destroy(struct sdap_id_op* op) +{ + sdap_id_op_hook_conn_data(op, NULL); + return 0; +} + +/* Check whether retry with reconnect can be performed for the operation */ +static bool sdap_id_op_can_reconnect(struct sdap_id_op *op) +{ + /* we allow 2 retries for failover server configured: + * - one for connection broken during request execution + * - one for the following (probably failed) reconnect attempt */ + int max_retries = 2 * be_fo_get_server_count(op->ctx->be, op->ctx->service->name) - 1; + if (max_retries < 1) { + max_retries = 1; + } + + return op->reconnect_retry_count < max_retries; +} + +static int sdap_id_op_connect_state_destroy(struct sdap_id_op_connect_state* state); + +/* Create operation connection request structure */ +static int sdap_id_op_connect_req_create(struct sdap_id_op* op, TALLOC_CTX *memctx) +{ + struct tevent_req *subreq; + struct sdap_id_op_connect_state *state; + + if (op->connect_req) { + /* already created */ + return EOK; + } + + if (!memctx) { + return EINVAL; + } + + subreq = tevent_req_create(memctx, &state, struct sdap_id_op_connect_state); + if (!subreq) { + return ENOMEM; + } + + talloc_set_destructor(state, sdap_id_op_connect_state_destroy); + + state->op = op; + op->connect_req = subreq; + return EOK; +} + +/* Destructor for operation connection request */ +static int sdap_id_op_connect_state_destroy(struct sdap_id_op_connect_state* state) +{ + if (state->op != NULL) { + /* clear destroyed connection request */ + state->op->connect_req = NULL; + } + + return 0; +} + +static int sdap_id_op_connect_retry(struct sdap_id_op *op, TALLOC_CTX *memctx); +static void sdap_id_op_connect_done(struct tevent_req *subreq); +static void sdap_id_op_connect_req_done(struct sdap_id_op* op, int dp_error, int ret); + +/* Begin to connect to LDAP server */ +int sdap_id_op_connect(struct sdap_id_op *op, TALLOC_CTX *memctx, struct tevent_req **req_out) { int ret; - bool result = false; - const char *mech; - const char *realm; - char *ccname = NULL; - krb5_context context = NULL; - krb5_ccache ccache = NULL; - krb5_error_code krberr; - TALLOC_CTX *tmp_ctx = NULL; - krb5_creds mcred; - krb5_creds cred; - char *server_name = NULL; - char *client_princ_str = NULL; - char *full_princ = NULL; - krb5_principal client_principal = NULL; - krb5_principal server_principal = NULL; - char hostname[512]; - int l_errno; - - - mech = dp_opt_get_string(ctx->opts->basic, SDAP_SASL_MECH); - if (mech == NULL || strcasecmp(mech, "GSSAPI") != 0) { - return false; - } - realm = dp_opt_get_string(ctx->opts->basic, SDAP_KRB5_REALM); - if (realm == NULL) { - DEBUG(3, ("Kerberos realm not available.\n")); - return false; - } + *req_out = NULL; - tmp_ctx = talloc_new(NULL); - if (tmp_ctx == NULL) { - DEBUG(1, ("talloc_new failed.\n")); - return false; + if (op->connect_req) { + /* Connection already in progress, invalid operation */ + return EINVAL; } - ccname = talloc_asprintf(tmp_ctx, "FILE:%s/ccache_%s", DB_PATH, realm); - if (ccname == NULL) { - DEBUG(1, ("talloc_asprintf failed.\n")); - goto done; + if (op->conn_data) { + /* If the operation is already connected, + * reuse existing connection regardless of its status */ + DEBUG(9, ("reusing operation connection\n")); + return EOK; } - - krberr = krb5_init_context(&context); - if (krberr) { - DEBUG(1, ("Failed to init kerberos context\n")); - goto done; + + ret = sdap_id_op_connect_retry(op, memctx); + if (op->connect_req) { + *req_out = op->connect_req; } + + return ret; +} - krberr = krb5_cc_resolve(context, ccname, &ccache); - if (krberr != 0) { - DEBUG(1, ("krb5_cc_resolve failed.\n")); - goto done; - } +/* Begin a connection retry to LDAP server */ +static int sdap_id_op_connect_retry(struct sdap_id_op *op, TALLOC_CTX *memctx) +{ + int ret = EOK; + struct sdap_id_ctx *ctx = op->ctx; + struct sdap_id_conn_data *conn_data; + struct tevent_req *subreq = NULL; - server_name = talloc_asprintf(tmp_ctx, "krbtgt/%s@%s", realm, realm); - if (server_name == NULL) { - DEBUG(1, ("talloc_asprintf failed.\n")); - goto done; - } + /* Try to reuse context cached connection */ + conn_data = ctx->cached_connection; + if (conn_data) { + if (conn_data->connect_req) { + ret = sdap_id_op_connect_req_create(op, memctx); + if (ret) { + goto done; + } - krberr = krb5_parse_name(context, server_name, &server_principal); - if (krberr != 0) { - DEBUG(1, ("krb5_parse_name failed.\n")); - goto done; - } - - client_princ_str = dp_opt_get_string(ctx->opts->basic, SDAP_SASL_AUTHID); - if (client_princ_str) { - if (!strchr(client_princ_str, '@')) { - full_princ = talloc_asprintf(tmp_ctx, "%s@%s", client_princ_str, - realm); - } else { - full_princ = talloc_strdup(tmp_ctx, client_princ_str); + DEBUG(9, ("waiting for connection to complete\n")); + sdap_id_op_hook_conn_data(op, conn_data); + goto done; } - } else { - ret = gethostname(hostname, sizeof(hostname)-1); - if (ret == -1) { - l_errno = errno; - DEBUG(1, ("gethostname failed [%d][%s].\n", l_errno, - strerror(l_errno))); + + if (sdap_can_reuse_connection(conn_data)) { + DEBUG(9, ("reusing cached connection\n")); + sdap_id_op_hook_conn_data(op, conn_data); goto done; } - hostname[sizeof(hostname)-1] = '\0'; - full_princ = talloc_asprintf(tmp_ctx, "host/%s@%s", hostname, realm); + DEBUG(9, ("releasing expired cached connection\n")); + ctx->cached_connection = NULL; + sdap_id_release_conn_data(conn_data); } - if (!full_princ) { - DEBUG(1, ("Client principal not available.\n")); + + DEBUG(9, ("beginning to connect\n")); + + ret = sdap_id_op_connect_req_create(op, memctx); + if (ret) { goto done; } - DEBUG(7, ("Client principal name is: [%s]\n", full_princ)); - krberr = krb5_parse_name(context, full_princ, &client_principal); - if (krberr != 0) { - DEBUG(1, ("krb5_parse_name failed.\n")); + + conn_data = talloc_zero(ctx, struct sdap_id_conn_data); + if (!conn_data) { + ret = ENOMEM; goto done; } - memset(&mcred, 0, sizeof(mcred)); - memset(&cred, 0, sizeof(mcred)); - mcred.client = client_principal; - mcred.server = server_principal; + talloc_set_destructor(conn_data, sdap_id_conn_data_destroy); - krberr = krb5_cc_retrieve_cred(context, ccache, 0, &mcred, &cred); - if (krberr != 0) { - DEBUG(1, ("krb5_cc_retrieve_cred failed.\n")); + conn_data->ctx = ctx; + subreq = sdap_cli_connect_send(conn_data,ctx->be->ev, + ctx->opts, ctx->be, + ctx->service, &conn_data->rootDSE); + if (!subreq) { + ret = ENOMEM; goto done; } - DEBUG(7, ("TGT end time [%d].\n", cred.times.endtime)); + tevent_req_set_callback(subreq, sdap_id_op_connect_done, conn_data); + conn_data->connect_req = subreq; - if (cred.times.endtime <= time(NULL)) { - DEBUG(3, ("TGT is expired.\n")); - result = true; - } - krb5_free_cred_contents(context, &cred); + DLIST_ADD(ctx->connections, conn_data); + ctx->cached_connection = conn_data; + + sdap_id_op_hook_conn_data(op, conn_data); done: - if (client_principal != NULL) { - krb5_free_principal(context, client_principal); + if (ret && conn_data) { + sdap_id_release_conn_data(conn_data); } - if (server_principal != NULL) { - krb5_free_principal(context, server_principal); + + if (ret && subreq) { + talloc_zfree(subreq); } - if (ccache != NULL) { - if (result) { - krb5_cc_destroy(context, ccache); + + if (ret || !op->conn_data || !op->conn_data->connect_req) { + /* there is no connection in progress */ + sdap_id_op_connect_req_done(op, DP_ERR_FATAL, ret); + } + + return ret; +} + +/* Subrequest callback for connection completion */ +static void sdap_id_op_connect_done(struct tevent_req *subreq) +{ + struct sdap_id_conn_data *conn_data = tevent_req_callback_data(subreq, + struct sdap_id_conn_data); + struct sdap_id_ctx *ctx = conn_data->ctx; + bool can_retry = false; + bool is_offline = false; + int ret; + + ret = sdap_cli_connect_recv(subreq, conn_data, &can_retry, &conn_data->sh, &conn_data->rootDSE); + conn_data->connect_req = NULL; + talloc_zfree(subreq); + + conn_data->notify_lock++; + + if (ret == ENOTSUP) { + DEBUG(0, ("Authentication mechanism not Supported by server\n")); + } + + if (ret == EOK && (!conn_data->sh || !conn_data->sh->connected)) { + DEBUG(0, ("sdap_cli_connect_recv returned bogus connection\n")); + ret = EFAULT; + } + + if (ret != EOK && !can_retry) { + /* be is going offline as there is no more servers to try */ + DEBUG(1, ("Failed to connect, going offline (%d [%s])\n", + ret, strerror(ret))); + be_mark_offline(conn_data->ctx->be); + is_offline = true; + } + + if (ret == EOK) { + ret = sdap_id_conn_data_set_expire_timer(conn_data); + } + + if (can_retry) { + switch (ret) { + case EOK: + case ENOTSUP: + case EACCES: + case EIO: + case EFAULT: + case ETIMEDOUT: + break; + + default: + /* do not attempt to retry on errors like ENOMEM */ + can_retry = false; + break; + } + } + + int notify_count = 0; + + /* Notify about connection */ + for(;;) { + struct sdap_id_op* op; + + if (ret == EOK && !conn_data->sh->connected) { + DEBUG(9, ("connection was broken after %d notifies\n", notify_count)); + } + + DLIST_FOR_EACH(op, conn_data->ops) { + if (op->connect_req) { + break; + } + } + + if (!op) { + break; + } + + /* another operation to notify */ + notify_count++; + + if (ret != EOK || !conn_data->sh->connected) { + /* failed to connect or connection got broken during notify */ + bool retry = false; + + /* drop connection from cache now */ + if (ctx->cached_connection == conn_data) { + ctx->cached_connection = NULL; + } + + if (can_retry) { + /* determining whether retry is possible */ + if (be_is_offline(ctx->be)) { + /* be is offline, no retry possible */ + if (ret == EOK) { + DEBUG(9, ("skipping automatic retry on op #%d as be is offline\n", notify_count)); + ret = EIO; + } + + can_retry = false; + is_offline = true; + } else { + if (ret == EOK) { + DEBUG(9, ("attempting automatic retry on op #%d\n", notify_count)); + retry = true; + } else if (sdap_id_op_can_reconnect(op)) { + DEBUG(9, ("attempting failover retry on op #%d\n", notify_count)); + op->reconnect_retry_count++; + retry = true; + } + } + } + + if (retry) { + sdap_id_op_connect_retry(op, NULL); + continue; + } + } + + if (ret == EOK) { + DEBUG(9, ("notify connected to op #%d\n", notify_count)); + sdap_id_op_connect_req_done(op, DP_ERR_OK, ret); + } else if (is_offline) { + DEBUG(9, ("notify offline to op #%d\n", notify_count)); + sdap_id_op_connect_req_done(op, DP_ERR_OFFLINE, EAGAIN); } else { - krb5_cc_close(context, ccache); + DEBUG(9, ("notify error to op #%d: %d [%s]\n", notify_count, ret, strerror(ret))); + sdap_id_op_connect_req_done(op, DP_ERR_FATAL, ret); } } - if (context != NULL) krb5_free_context(context); - talloc_free(tmp_ctx); - return result; + + /* all operations notified */ + if (conn_data->notify_lock > 0) { + conn_data->notify_lock--; + } + + if (ret == EOK && conn_data->sh->connected && !be_is_offline(ctx->be)) { + DEBUG(9, ("caching successful connection after %d notifies\n", notify_count)); + ctx->cached_connection = conn_data; + } else { + if (ctx->cached_connection == conn_data) { + ctx->cached_connection = NULL; + } + + sdap_id_release_conn_data(conn_data); + } +} + +/* Mark operation connection request as complete */ +static void sdap_id_op_connect_req_done(struct sdap_id_op* op, int dp_error, int ret) +{ + struct tevent_req* req = op->connect_req; + struct sdap_id_op_connect_state *state; + + if (!req) { + return; + } + + op->connect_req = NULL; + + state = tevent_req_data(req, struct sdap_id_op_connect_state); + state->dp_error = dp_error; + state->result = ret; + + if (ret == EOK) { + tevent_req_done(req); + } else { + tevent_req_error(req, ret); + } +} + +/* Get the result of an asynchronous connect operation on sdap_id_op + * + * In dp_error data provider error code is returned: + * DP_ERR_OK - connection established + * DP_ERR_OFFLINE - backend is offline, operation result is set EAGAIN + * DP_ERR_FATAL - operation failed + */ +int sdap_id_op_connect_recv(struct tevent_req* req, int* dp_error) +{ + struct sdap_id_op_connect_state *state = tevent_req_data(req, + struct sdap_id_op_connect_state); + + *dp_error = state->dp_error; + return state->result; +} + +/* Report completion of LDAP operation and release associated connection. + * Returns operation result (possible updated) passed in ret parameter. + * + * In dp_error data provider error code is returned: + * DP_ERR_OK (operation result = EOK) - operation completed + * DP_ERR_OK (operation result != EOK) - operation can be retried + * DP_ERR_OFFLINE - backend is offline, operation result is set EAGAIN + * DP_ERR_FATAL - operation failed */ +int sdap_id_op_done(struct sdap_id_op *op, int retval, int *dp_err_out) +{ + bool communication_error; + switch (retval) { + case EIO: + /* this currently the only possible communication error after connection is established */ + communication_error = true; + break; + + default: + communication_error = false; + break; + } + + if (communication_error && op->conn_data != 0 && op->conn_data == op->ctx->cached_connection) { + /* do not reuse failed connection */ + op->ctx->cached_connection = NULL; + } + + int dp_err; + if (retval == EOK) { + dp_err = DP_ERR_OK; + } else if (be_is_offline(op->ctx->be)) { + /* if backend is already offline, just report offline, do not duplicate errors */ + dp_err = DP_ERR_OFFLINE; + retval = EAGAIN; + } else if (communication_error) { + /* communication error, can try to reconnect */ + + if (!sdap_id_op_can_reconnect(op)) { + dp_err = DP_ERR_FATAL; + } else { + dp_err = DP_ERR_OK; + retval = EAGAIN; + } + } else { + dp_err = DP_ERR_FATAL; + } + + if (dp_err == DP_ERR_OK && retval != EOK) { + /* reconnect retry */ + op->reconnect_retry_count++; + } else { + /* end of request */ + op->reconnect_retry_count = 0; + } + + sdap_id_op_hook_conn_data(op, NULL); + *dp_err_out = dp_err; + return retval; +} + +/* Get LDAP ID backend context associated with operation */ +struct sdap_id_ctx* sdap_id_op_ctx(struct sdap_id_op* op) +{ + return op->ctx; +} + +/* Get SDAP handle associated with operation by sdap_id_op_connect */ +struct sdap_handle* sdap_id_op_handle(struct sdap_id_op* op) +{ + return op->conn_data ? op->conn_data->sh : NULL; +} + +/* Begin to connect to LDAP server */ +const struct sysdb_attrs* sdap_id_op_rootDSE(struct sdap_id_op* op) +{ + return op->conn_data ? op->conn_data->rootDSE : NULL; } int sdap_id_setup_tasks(struct sdap_id_ctx *ctx) diff --git a/src/providers/ldap/ldap_common.h b/src/providers/ldap/ldap_common.h index ff1ffb7..49431a8 100644 --- a/src/providers/ldap/ldap_common.h +++ b/src/providers/ldap/ldap_common.h @@ -33,17 +33,17 @@ /* a fd the child process would log into */ extern int ldap_child_debug_fd; +struct sdap_id_conn_data; + struct sdap_id_ctx { struct be_ctx *be; struct sdap_options *opts; - struct fo_service *fo_service; struct sdap_service *service; - /* what rootDSE returns */ - struct sysdb_attrs *rootDSE; - - /* global sdap handler */ - struct sdap_handle *gsh; + /* list of all open connections */ + struct sdap_id_conn_data *connections; + /* cached (current) connection */ + struct sdap_id_conn_data *cached_connection; /* enumeration loop timer */ struct timeval last_enum; @@ -54,6 +54,8 @@ struct sdap_id_ctx { char *max_group_timestamp; }; +struct sdap_id_op; + struct sdap_auth_ctx { struct be_ctx *be; struct sdap_options *opts; @@ -61,6 +63,42 @@ struct sdap_auth_ctx { struct sdap_service *service; }; +/* common */ + +/* Create an operation object */ +struct sdap_id_op* sdap_id_op_create(TALLOC_CTX *memctx, struct sdap_id_ctx *ctx); + +/* Begin to connect to LDAP server. + * If there is no cached connection available begin + * an asynchronous connect and return an associated request */ +int sdap_id_op_connect(struct sdap_id_op*, TALLOC_CTX *memctx, struct tevent_req** req); + +/* Get the result of an asynchronous connect operation on sdap_id_op + * + * In dp_error data provider error code is returned: + * DP_ERR_OK - connection established + * DP_ERR_OFFLINE - backend is offline, operation result is set EAGAIN + * DP_ERR_FATAL - operation failed + */ +int sdap_id_op_connect_recv(struct tevent_req* req, int* dp_error); + +/* Report completion of LDAP operation and release associated connection. + * Returns operation result (possible updated) passed in ret parameter. + * + * In dp_error data provider error code is returned: + * DP_ERR_OK (operation result = EOK) - operation completed + * DP_ERR_OK (operation result != EOK) - operation can be retried + * DP_ERR_OFFLINE - backend is offline, operation result is set EAGAIN + * DP_ERR_FATAL - operation failed */ +int sdap_id_op_done(struct sdap_id_op*, int ret, int* dp_error); + +/* Get LDAP ID backend context associated with operation */ +struct sdap_id_ctx* sdap_id_op_ctx(struct sdap_id_op* op); +/* Get SDAP handle associated with operation by sdap_id_op_connect */ +struct sdap_handle* sdap_id_op_handle(struct sdap_id_op* op); +/* Get root DSE entry of connected LDAP server */ +const struct sysdb_attrs* sdap_id_op_rootDSE(struct sdap_id_op* op); + /* id */ void sdap_account_info_handler(struct be_req *breq); int sdap_id_setup_tasks(struct sdap_id_ctx *ctx); @@ -89,13 +127,9 @@ int ldap_get_options(TALLOC_CTX *memctx, int ldap_id_enumerate_set_timer(struct sdap_id_ctx *ctx, struct timeval tv); int ldap_id_cleanup_set_timer(struct sdap_id_ctx *ctx, struct timeval tv); -bool sdap_connected(struct sdap_id_ctx *ctx); -void sdap_mark_offline(struct sdap_id_ctx *ctx); -bool sdap_check_gssapi_reconnect(struct sdap_id_ctx *ctx); - struct tevent_req *users_get_send(TALLOC_CTX *memctx, struct tevent_context *ev, - struct sdap_id_ctx *ctx, + struct sdap_id_op *op, const char *name, int filter_type, int attrs_type); @@ -103,7 +137,7 @@ int users_get_recv(struct tevent_req *req); struct tevent_req *groups_get_send(TALLOC_CTX *memctx, struct tevent_context *ev, - struct sdap_id_ctx *ctx, + struct sdap_id_op *op, const char *name, int filter_type, int attrs_type); diff --git a/src/providers/ldap/ldap_id.c b/src/providers/ldap/ldap_id.c index 4bbc07a..3aac271 100644 --- a/src/providers/ldap/ldap_id.c +++ b/src/providers/ldap/ldap_id.c @@ -35,7 +35,7 @@ struct users_get_state { struct tevent_context *ev; - struct sdap_id_ctx *ctx; + struct sdap_id_op *op; struct sysdb_ctx *sysdb; struct sss_domain_info *domain; @@ -46,19 +46,19 @@ struct users_get_state { const char **attrs; }; -static void users_get_connect_done(struct tevent_req *subreq); static void users_get_done(struct tevent_req *subreq); static void users_get_delete(struct tevent_req *subreq); struct tevent_req *users_get_send(TALLOC_CTX *memctx, struct tevent_context *ev, - struct sdap_id_ctx *ctx, + struct sdap_id_op *op, const char *name, int filter_type, int attrs_type) { struct tevent_req *req, *subreq; struct users_get_state *state; + struct sdap_id_ctx *ctx = sdap_id_op_ctx(op); const char *attr_name; int ret; @@ -66,9 +66,9 @@ struct tevent_req *users_get_send(TALLOC_CTX *memctx, if (!req) return NULL; state->ev = ev; - state->ctx = ctx; + state->op = op; state->sysdb = ctx->be->sysdb; - state->domain = state->ctx->be->domain; + state->domain = ctx->be->domain; state->name = name; state->filter_type = filter_type; @@ -98,28 +98,9 @@ struct tevent_req *users_get_send(TALLOC_CTX *memctx, SDAP_OPTS_USER, &state->attrs); if (ret != EOK) goto fail; - if (!sdap_connected(ctx)) { - - if (ctx->gsh) talloc_zfree(ctx->gsh); - - /* FIXME: add option to decide if tls should be used - * or SASL/GSSAPI, etc ... */ - subreq = sdap_cli_connect_send(state, ev, ctx->opts, - ctx->be, ctx->service, - &ctx->rootDSE); - if (!subreq) { - ret = ENOMEM; - goto fail; - } - - tevent_req_set_callback(subreq, users_get_connect_done, req); - - return req; - } - subreq = sdap_get_users_send(state, state->ev, state->domain, state->sysdb, - state->ctx->opts, state->ctx->gsh, + sdap_id_op_ctx(op)->opts, sdap_id_op_handle(op), state->attrs, state->filter); if (!subreq) { ret = ENOMEM; @@ -135,36 +116,6 @@ fail: return req; } -static void users_get_connect_done(struct tevent_req *subreq) -{ - struct tevent_req *req = tevent_req_callback_data(subreq, - struct tevent_req); - struct users_get_state *state = tevent_req_data(req, - struct users_get_state); - int ret; - - ret = sdap_cli_connect_recv(subreq, state->ctx, - &state->ctx->gsh, &state->ctx->rootDSE); - talloc_zfree(subreq); - if (ret) { - if (ret == ENOTSUP) { - DEBUG(0, ("Authentication mechanism not Supported by server")); - } - tevent_req_error(req, ret); - return; - } - - subreq = sdap_get_users_send(state, state->ev, - state->domain, state->sysdb, - state->ctx->opts, state->ctx->gsh, - state->attrs, state->filter); - if (!subreq) { - tevent_req_error(req, ENOMEM); - return; - } - tevent_req_set_callback(subreq, users_get_done, req); -} - static void users_get_done(struct tevent_req *subreq) { struct tevent_req *req = tevent_req_callback_data(subreq, @@ -258,7 +209,7 @@ int users_get_recv(struct tevent_req *req) struct groups_get_state { struct tevent_context *ev; - struct sdap_id_ctx *ctx; + struct sdap_id_op *op; struct sysdb_ctx *sysdb; struct sss_domain_info *domain; @@ -269,17 +220,17 @@ struct groups_get_state { const char **attrs; }; -static void groups_get_connect_done(struct tevent_req *subreq); static void groups_get_done(struct tevent_req *subreq); static void groups_get_delete(struct tevent_req *subreq); struct tevent_req *groups_get_send(TALLOC_CTX *memctx, struct tevent_context *ev, - struct sdap_id_ctx *ctx, + struct sdap_id_op *op, const char *name, int filter_type, int attrs_type) { + struct sdap_id_ctx *ctx = sdap_id_op_ctx(op); struct tevent_req *req, *subreq; struct groups_get_state *state; const char *attr_name; @@ -289,9 +240,9 @@ struct tevent_req *groups_get_send(TALLOC_CTX *memctx, if (!req) return NULL; state->ev = ev; - state->ctx = ctx; + state->op = op; state->sysdb = ctx->be->sysdb; - state->domain = state->ctx->be->domain; + state->domain = ctx->be->domain; state->name = name; state->filter_type = filter_type; @@ -321,28 +272,9 @@ struct tevent_req *groups_get_send(TALLOC_CTX *memctx, SDAP_OPTS_GROUP, &state->attrs); if (ret != EOK) goto fail; - if (!sdap_connected(ctx)) { - - if (ctx->gsh) talloc_zfree(ctx->gsh); - - /* FIXME: add option to decide if tls should be used - * or SASL/GSSAPI, etc ... */ - subreq = sdap_cli_connect_send(state, ev, ctx->opts, - ctx->be, ctx->service, - &ctx->rootDSE); - if (!subreq) { - ret = ENOMEM; - goto fail; - } - - tevent_req_set_callback(subreq, groups_get_connect_done, req); - - return req; - } - subreq = sdap_get_groups_send(state, state->ev, state->domain, state->sysdb, - state->ctx->opts, state->ctx->gsh, + sdap_id_op_ctx(op)->opts, sdap_id_op_handle(op), state->attrs, state->filter); if (!subreq) { ret = ENOMEM; @@ -358,36 +290,6 @@ fail: return req; } -static void groups_get_connect_done(struct tevent_req *subreq) -{ - struct tevent_req *req = tevent_req_callback_data(subreq, - struct tevent_req); - struct groups_get_state *state = tevent_req_data(req, - struct groups_get_state); - int ret; - - ret = sdap_cli_connect_recv(subreq, state->ctx, - &state->ctx->gsh, &state->ctx->rootDSE); - talloc_zfree(subreq); - if (ret) { - if (ret == ENOTSUP) { - DEBUG(0, ("Authentication mechanism not Supported by server")); - } - tevent_req_error(req, ret); - return; - } - - subreq = sdap_get_groups_send(state, state->ev, - state->domain, state->sysdb, - state->ctx->opts, state->ctx->gsh, - state->attrs, state->filter); - if (!subreq) { - tevent_req_error(req, ENOMEM); - return; - } - tevent_req_set_callback(subreq, groups_get_done, req); -} - static void groups_get_done(struct tevent_req *subreq) { struct tevent_req *req = tevent_req_callback_data(subreq, @@ -481,19 +383,19 @@ int groups_get_recv(struct tevent_req *req) struct groups_by_user_state { struct tevent_context *ev; - struct sdap_id_ctx *ctx; + struct sdap_id_op *op; const char *name; const char **attrs; }; -static void groups_by_user_connect_done(struct tevent_req *subreq); static void groups_by_user_done(struct tevent_req *subreq); static struct tevent_req *groups_by_user_send(TALLOC_CTX *memctx, struct tevent_context *ev, - struct sdap_id_ctx *ctx, + struct sdap_id_op *op, const char *name) { + struct sdap_id_ctx *ctx = sdap_id_op_ctx(op); struct tevent_req *req, *subreq; struct groups_by_user_state *state; int ret; @@ -502,36 +404,17 @@ static struct tevent_req *groups_by_user_send(TALLOC_CTX *memctx, if (!req) return NULL; state->ev = ev; - state->ctx = ctx; + state->op = op; state->name = name; ret = build_attrs_from_map(state, ctx->opts->group_map, SDAP_OPTS_GROUP, &state->attrs); if (ret != EOK) goto fail; - if (!sdap_connected(ctx)) { - - if (ctx->gsh) talloc_zfree(ctx->gsh); - - /* FIXME: add option to decide if tls should be used - * or SASL/GSSAPI, etc ... */ - subreq = sdap_cli_connect_send(state, ev, ctx->opts, - ctx->be, ctx->service, - &ctx->rootDSE); - if (!subreq) { - ret = ENOMEM; - goto fail; - } - - tevent_req_set_callback(subreq, groups_by_user_connect_done, req); - - return req; - } - subreq = sdap_get_initgr_send(state, state->ev, - state->ctx->be->domain, - state->ctx->be->sysdb, - state->ctx->opts, state->ctx->gsh, + ctx->be->domain, + ctx->be->sysdb, + ctx->opts, sdap_id_op_handle(op), state->name, state->attrs); if (!subreq) { ret = ENOMEM; @@ -547,37 +430,6 @@ fail: return req; } -static void groups_by_user_connect_done(struct tevent_req *subreq) -{ - struct tevent_req *req = tevent_req_callback_data(subreq, - struct tevent_req); - struct groups_by_user_state *state = tevent_req_data(req, - struct groups_by_user_state); - int ret; - - ret = sdap_cli_connect_recv(subreq, state->ctx, - &state->ctx->gsh, &state->ctx->rootDSE); - talloc_zfree(subreq); - if (ret) { - if (ret == ENOTSUP) { - DEBUG(0, ("Authentication mechanism not Supported by server")); - } - tevent_req_error(req, ret); - return; - } - - subreq = sdap_get_initgr_send(state, state->ev, - state->ctx->be->domain, - state->ctx->be->sysdb, - state->ctx->opts, state->ctx->gsh, - state->name, state->attrs); - if (!subreq) { - tevent_req_error(req, ENOMEM); - return; - } - tevent_req_set_callback(subreq, groups_by_user_done, req); -} - static void groups_by_user_done(struct tevent_req *subreq) { struct tevent_req *req = tevent_req_callback_data(subreq, @@ -607,18 +459,32 @@ int groups_by_user_recv(struct tevent_req *req) /* FIXME: embed this function in sssd_be and only call out * specific functions from modules ? */ + +struct sdap_account_info_state { + struct be_req *breq; + struct sdap_id_op *op; +}; +static struct sdap_account_info_state *sdap_account_info_create_state(struct sdap_id_ctx *ctx, + struct be_req *breq); +static void sdap_account_info_handler_retry(struct sdap_account_info_state* state); +static void sdap_account_info_connect_done(struct tevent_req *subreq); + +static void sdap_account_info_request_send(struct sdap_account_info_state* state); +static void sdap_account_info_request_done(struct tevent_req *req, + int ret, const char* error); static void sdap_account_info_users_done(struct tevent_req *req); static void sdap_account_info_groups_done(struct tevent_req *req); static void sdap_account_info_initgr_done(struct tevent_req *req); +static void sdap_account_info_handler_done(struct sdap_account_info_state* state, + int dp_err, int ret, const char* error); + void sdap_account_info_handler(struct be_req *breq) { struct sdap_id_ctx *ctx; struct be_acct_req *ar; - struct tevent_req *req; - const char *err = "Unknown Error"; - int ret = EOK; + struct sdap_account_info_state* state = NULL; ctx = talloc_get_type(breq->be_ctx->bet_info[BET_ID].pvt_bet_data, struct sdap_id_ctx); @@ -636,160 +502,236 @@ void sdap_account_info_handler(struct be_req *breq) return sdap_handler_done(breq, DP_ERR_OK, EOK, "Success"); } - req = users_get_send(breq, breq->be_ctx->ev, ctx, + break; + + case BE_REQ_GROUP: /* group */ + + /* skip enumerations on demand */ + if (strcmp(ar->filter_value, "*") == 0) { + return sdap_handler_done(breq, DP_ERR_OK, EOK, "Success"); + } + + break; + + case BE_REQ_INITGROUPS: /* init groups for user */ + if (ar->filter_type != BE_FILTER_NAME) { + sdap_handler_done(breq, DP_ERR_FATAL, EINVAL, "Invalid filter type"); + return; + } + if (ar->attr_type != BE_ATTR_CORE) { + sdap_handler_done(breq, DP_ERR_FATAL, EINVAL, "Invalid attr type"); + return; + } + if (strchr(ar->filter_value, '*')) { + sdap_handler_done(breq, DP_ERR_FATAL, EINVAL, "Invalid filter value"); + return; + } + + break; + + default: /*fail*/ + sdap_handler_done(breq, DP_ERR_FATAL, EINVAL, "Invalid request type"); + return; + } + + state = sdap_account_info_create_state(ctx, breq); + if (!state) { + sdap_handler_done(breq, DP_ERR_FATAL, ENOMEM, "Out of memory"); + return; + } + + sdap_account_info_handler_retry(state); +} + +static struct sdap_account_info_state *sdap_account_info_create_state(struct sdap_id_ctx *ctx, struct be_req *breq) +{ + struct sdap_account_info_state* state = talloc_zero(breq, struct sdap_account_info_state); + if (!state) { + return NULL; + } + + state->breq = breq; + state->op = sdap_id_op_create(state, ctx); + if (!state->op) { + goto fail; + } + + return state; + +fail: + talloc_zfree(state); + return NULL; +} + +static void sdap_account_info_handler_retry(struct sdap_account_info_state* state) +{ + int ret; + struct tevent_req* subreq = NULL; + + ret = sdap_id_op_connect(state->op, state, &subreq); + if (ret != EOK) { + talloc_zfree(subreq); + + sdap_account_info_handler_done(state, DP_ERR_FATAL, ret, NULL); + return; + } + + if (subreq != NULL) { + tevent_req_set_callback(subreq, sdap_account_info_connect_done, state); + return; + } + + sdap_account_info_request_send(state); +} + +static void sdap_account_info_connect_done(struct tevent_req *subreq) +{ + struct sdap_account_info_state *state = tevent_req_callback_data(subreq, + struct sdap_account_info_state); + int dp_error, ret; + + ret = sdap_id_op_connect_recv(subreq, &dp_error); + talloc_zfree(subreq); + + if (ret != EOK) { + sdap_account_info_handler_done(state, dp_error, ret, NULL); + return; + } + + sdap_account_info_request_send(state); +} + +static void sdap_account_info_request_send(struct sdap_account_info_state* state) +{ + struct be_req *breq = state->breq; + struct be_acct_req *ar = talloc_get_type(breq->req_data, struct be_acct_req); + struct tevent_req *subreq; + int ret; + const char* err = NULL; + + switch (ar->entry_type & 0xFFF) { + case BE_REQ_USER: /* user */ + subreq = users_get_send(breq, breq->be_ctx->ev, state->op, ar->filter_value, ar->filter_type, ar->attr_type); - if (!req) { - return sdap_handler_done(breq, DP_ERR_FATAL, ENOMEM, "Out of memory"); + if (!subreq) { + ret = ENOMEM; + break; } - tevent_req_set_callback(req, sdap_account_info_users_done, breq); + tevent_req_set_callback(subreq, sdap_account_info_users_done, state); break; case BE_REQ_GROUP: /* group */ - - if (strcmp(ar->filter_value, "*") == 0) { - return sdap_handler_done(breq, DP_ERR_OK, EOK, "Success"); - } - - /* skip enumerations on demand */ - req = groups_get_send(breq, breq->be_ctx->ev, ctx, + subreq = groups_get_send(breq, breq->be_ctx->ev, state->op, ar->filter_value, ar->filter_type, ar->attr_type); - if (!req) { - return sdap_handler_done(breq, DP_ERR_FATAL, ENOMEM, "Out of memory"); + if (!subreq) { + ret = ENOMEM; + break; } - tevent_req_set_callback(req, sdap_account_info_groups_done, breq); + tevent_req_set_callback(subreq, sdap_account_info_groups_done, state); break; case BE_REQ_INITGROUPS: /* init groups for user */ - if (ar->filter_type != BE_FILTER_NAME) { - ret = EINVAL; - err = "Invalid filter type"; - break; - } - if (ar->attr_type != BE_ATTR_CORE) { - ret = EINVAL; - err = "Invalid attr type"; - break; - } - if (strchr(ar->filter_value, '*')) { - ret = EINVAL; - err = "Invalid filter value"; - break; - } - req = groups_by_user_send(breq, breq->be_ctx->ev, ctx, + subreq = groups_by_user_send(breq, breq->be_ctx->ev, state->op, ar->filter_value); - if (!req) ret = ENOMEM; - /* tevent_req_set_callback(req, groups_by_user_done, breq); */ + if (!subreq) { + ret = ENOMEM; + break; + } - tevent_req_set_callback(req, sdap_account_info_initgr_done, breq); + tevent_req_set_callback(subreq, sdap_account_info_initgr_done, state); break; default: /*fail*/ ret = EINVAL; err = "Invalid request type"; + break; } - if (ret != EOK) return sdap_handler_done(breq, DP_ERR_FATAL, ret, err); + if (ret != EOK) { + sdap_account_info_handler_done(state, DP_ERR_FATAL, ret, err); + } +} + +static void sdap_account_info_request_done(struct tevent_req *req, int ret, const char* default_error) +{ + struct sdap_account_info_state *state = tevent_req_callback_data(req, struct sdap_account_info_state); + int dp_error; + + talloc_zfree(req); + + ret = sdap_id_op_done(state->op, ret, &dp_error); + if (dp_error == DP_ERR_OK && ret != EOK) { + /* retry */ + sdap_account_info_handler_retry(state); + return; + } + + sdap_account_info_handler_done(state, dp_error, ret, NULL); } static void sdap_account_info_users_done(struct tevent_req *req) { - struct be_req *breq = tevent_req_callback_data(req, struct be_req); - struct sdap_id_ctx *ctx; - int dp_err = DP_ERR_OK; - const char *error = NULL; - int ret; - - ret = users_get_recv(req); - talloc_zfree(req); - - if (ret) { - dp_err = DP_ERR_FATAL; - error = "Enum Users Failed"; - - if (ret == ETIMEDOUT || ret == EFAULT || ret == EIO) { - dp_err = DP_ERR_OFFLINE; - ctx = talloc_get_type(breq->be_ctx->bet_info[BET_ID].pvt_bet_data, - struct sdap_id_ctx); - if (sdap_check_gssapi_reconnect(ctx)) { - talloc_zfree(ctx->gsh); - sdap_account_info_handler(breq); - return; - } - sdap_mark_offline(ctx); - } - } - - sdap_handler_done(breq, dp_err, ret, error); + int ret = users_get_recv(req); + sdap_account_info_request_done(req, ret, NULL); } static void sdap_account_info_groups_done(struct tevent_req *req) { - struct be_req *breq = tevent_req_callback_data(req, struct be_req); - struct sdap_id_ctx *ctx; - int dp_err = DP_ERR_OK; - const char *error = NULL; - int ret; - - ret = groups_get_recv(req); - talloc_zfree(req); - - if (ret) { - dp_err = DP_ERR_FATAL; - error = "Enum Groups Failed"; - - if (ret == ETIMEDOUT || ret == EFAULT || ret == EIO) { - dp_err = DP_ERR_OFFLINE; - ctx = talloc_get_type(breq->be_ctx->bet_info[BET_ID].pvt_bet_data, - struct sdap_id_ctx); - if (sdap_check_gssapi_reconnect(ctx)) { - talloc_zfree(ctx->gsh); - sdap_account_info_handler(breq); - return; - } - sdap_mark_offline(ctx); - } - } - - return sdap_handler_done(breq, dp_err, ret, error); + int ret = groups_get_recv(req); + sdap_account_info_request_done(req, ret, NULL); } static void sdap_account_info_initgr_done(struct tevent_req *req) { - struct be_req *breq = tevent_req_callback_data(req, struct be_req); - struct sdap_id_ctx *ctx; - int dp_err = DP_ERR_OK; - const char *error = NULL; - int ret; + int ret = groups_by_user_recv(req); + sdap_account_info_request_done(req, ret, NULL); +} - ret = groups_by_user_recv(req); - talloc_zfree(req); +static void sdap_account_info_handler_done(struct sdap_account_info_state* state, + int dp_error, int ret, const char* error) +{ + struct be_req* breq = state->breq; - if (ret) { - dp_err = DP_ERR_FATAL; - error = "Init Groups Failed"; + talloc_zfree(state); - if (ret == ETIMEDOUT || ret == EFAULT || ret == EIO) { - dp_err = DP_ERR_OFFLINE; - ctx = talloc_get_type(breq->be_ctx->bet_info[BET_ID].pvt_bet_data, - struct sdap_id_ctx); - if (sdap_check_gssapi_reconnect(ctx)) { - talloc_zfree(ctx->gsh); - sdap_account_info_handler(breq); - return; + if (error == NULL && dp_error != DP_ERR_OK) { + if (ret == ENOMEM) { + error = "Out of memory"; + } + else if (dp_error == DP_ERR_OFFLINE) { + error = "Offline"; + } else { + struct be_acct_req *ar = talloc_get_type(breq->req_data, struct be_acct_req); + + switch (ar->entry_type & 0xFFF) { + case BE_REQ_USER: /* user */ + error = "Enum Users Failed"; + break; + + case BE_REQ_GROUP: /* group */ + error = "Enum Groups Failed"; + break; + + case BE_REQ_INITGROUPS: /* init groups for user */ + error = "Init Groups Failed"; + break; + + default: /*fail*/ + error = "Unknown error"; + break; } - sdap_mark_offline(ctx); } } - return sdap_handler_done(breq, dp_err, ret, error); + sdap_handler_done(breq, dp_error, ret, error); } diff --git a/src/providers/ldap/ldap_id_enum.c b/src/providers/ldap/ldap_id_enum.c index bc06e8b..bc58feb 100644 --- a/src/providers/ldap/ldap_id_enum.c +++ b/src/providers/ldap/ldap_id_enum.c @@ -155,11 +155,13 @@ static struct tevent_req *enum_users_send(TALLOC_CTX *memctx, struct tevent_context *ev, struct sdap_id_ctx *ctx, bool purge); +static int enum_users_recv(struct tevent_req *req, bool *offline); static void ldap_id_enum_users_done(struct tevent_req *subreq); static struct tevent_req *enum_groups_send(TALLOC_CTX *memctx, struct tevent_context *ev, struct sdap_id_ctx *ctx, bool purge); +static int enum_groups_recv(struct tevent_req *req, bool *offline); static void ldap_id_enum_groups_done(struct tevent_req *subreq); static void ldap_id_enum_cleanup_done(struct tevent_req *subreq); @@ -201,45 +203,39 @@ static void ldap_id_enum_users_done(struct tevent_req *subreq) struct tevent_req); struct global_enum_state *state = tevent_req_data(req, struct global_enum_state); - enum tevent_req_state tstate; - uint64_t err = 0; + bool offline; + int ret; - if (tevent_req_is_error(subreq, &tstate, &err)) { - if (tstate != TEVENT_REQ_USER_ERROR) { - err = EIO; - } - if (err != ENOENT) { - goto fail; - } - } + ret = enum_users_recv(subreq, &offline); talloc_zfree(subreq); + if (ret == ENOENT) { + ret = EOK; + } + + if (ret != EOK) { + if (offline) { + DEBUG(9, ("Enumerate users canceled due to Offline, retrying later!\n")); + } else { + DEBUG(1, ("Failed to enumerate users (%d [%s]), retrying later!\n", + ret, strerror(ret))); + } + + goto done; + } + subreq = enum_groups_send(state, state->ev, state->ctx, state->purge); if (!subreq) { - goto fail; + DEBUG(1, ("enum_groups_send failed!\n")); + goto done; } + tevent_req_set_callback(subreq, ldap_id_enum_groups_done, req); - return; - -fail: - if (err) { - DEBUG(9, ("User enumeration failed with: (%d)[%s]\n", - (int)err, strerror(err))); - - if (sdap_check_gssapi_reconnect(state->ctx)) { - talloc_zfree(state->ctx->gsh); - subreq = enum_users_send(state, state->ev, state->ctx, state->purge); - if (subreq != NULL) { - tevent_req_set_callback(subreq, ldap_id_enum_users_done, req); - return; - } - } - sdap_mark_offline(state->ctx); - } - - DEBUG(1, ("Failed to enumerate users, retrying later!\n")); + +done: tevent_req_done(req); + return; } static void ldap_id_enum_groups_done(struct tevent_req *subreq) @@ -248,47 +244,43 @@ static void ldap_id_enum_groups_done(struct tevent_req *subreq) struct tevent_req); struct global_enum_state *state = tevent_req_data(req, struct global_enum_state); - enum tevent_req_state tstate; - uint64_t err = 0; + int ret; + bool offline; - if (tevent_req_is_error(subreq, &tstate, &err)) { - if (tstate != TEVENT_REQ_USER_ERROR) { - err = EIO; - } - if (err != ENOENT) { - goto fail; - } - } + ret = enum_groups_recv(subreq, &offline); talloc_zfree(subreq); - if (state->purge) { + if (ret == ENOENT) { + ret = EOK; + } - subreq = ldap_id_cleanup_send(state, state->ev, state->ctx); - if (!subreq) { - goto fail; + if (ret != EOK) { + if (offline) { + DEBUG(9, ("Enumerate groups canceled due to Offline, retrying later!\n")); + } else { + DEBUG(1, ("Failed to enumerate groups (%d [%s]), retrying later!\n", + ret, strerror(ret))); } - tevent_req_set_callback(subreq, ldap_id_enum_cleanup_done, req); - return; + goto done; } - tevent_req_done(req); - return; + if (!state->purge) { + goto done; + } -fail: - /* check if credentials are expired otherwise go offline on failures */ - if (sdap_check_gssapi_reconnect(state->ctx)) { - talloc_zfree(state->ctx->gsh); - subreq = enum_groups_send(state, state->ev, state->ctx, state->purge); - if (subreq != NULL) { - tevent_req_set_callback(subreq, ldap_id_enum_groups_done, req); - return; - } + subreq = ldap_id_cleanup_send(state, state->ev, state->ctx); + if (!subreq) { + DEBUG(1, ("ldap_id_cleanup_send failed!\n")); + goto done; } - sdap_mark_offline(state->ctx); - DEBUG(1, ("Failed to enumerate groups (%d [%s]), retrying later!\n", - (int)err, strerror(err))); + tevent_req_set_callback(subreq, ldap_id_enum_cleanup_done, req); + + return; + +done: tevent_req_done(req); + return; } static void ldap_id_enum_cleanup_done(struct tevent_req *subreq) @@ -304,13 +296,16 @@ static void ldap_id_enum_cleanup_done(struct tevent_req *subreq) struct enum_users_state { struct tevent_context *ev; - struct sdap_id_ctx *ctx; + struct sdap_id_op *op; + bool offline; char *filter; const char **attrs; }; +static bool enum_users_connect_retry(struct tevent_req *req); static void enum_users_connect_done(struct tevent_req *subreq); +static bool enum_users_send_request(struct tevent_req *req); static void enum_users_op_done(struct tevent_req *subreq); static struct tevent_req *enum_users_send(TALLOC_CTX *memctx, @@ -318,7 +313,7 @@ static struct tevent_req *enum_users_send(TALLOC_CTX *memctx, struct sdap_id_ctx *ctx, bool purge) { - struct tevent_req *req, *subreq; + struct tevent_req *req; struct enum_users_state *state; int ret; @@ -326,7 +321,14 @@ static struct tevent_req *enum_users_send(TALLOC_CTX *memctx, if (!req) return NULL; state->ev = ev; - state->ctx = ctx; + state->offline = false; + + state->op = sdap_id_op_create(state, ctx); + if (!state->op) { + DEBUG(2, ("Failed to create sdap_id_op\n")); + ret = ENOMEM; + goto fail; + } if (ctx->max_user_timestamp && !purge) { @@ -355,74 +357,82 @@ static struct tevent_req *enum_users_send(TALLOC_CTX *memctx, SDAP_OPTS_USER, &state->attrs); if (ret != EOK) goto fail; - if (!sdap_connected(ctx)) { + if (!enum_users_connect_retry(req)) { + tevent_req_post(req, ev); + } - if (ctx->gsh) talloc_zfree(ctx->gsh); + return req; - /* FIXME: add option to decide if tls should be used - * or SASL/GSSAPI, etc ... */ - subreq = sdap_cli_connect_send(state, ev, ctx->opts, - ctx->be, ctx->service, - &ctx->rootDSE); - if (!subreq) { - ret = ENOMEM; - goto fail; - } +fail: + tevent_req_error(req, ret); + tevent_req_post(req, ev); + return req; +} +/* start LDAP connect retry for users query */ +static bool enum_users_connect_retry(struct tevent_req *req) +{ + struct enum_users_state *state = tevent_req_data(req, + struct enum_users_state); + struct tevent_req *subreq; + int ret; + + ret = sdap_id_op_connect(state->op, state, &subreq); + if (ret != EOK) { + talloc_zfree(subreq); + + tevent_req_error(req, ret); + return false; + } + + if (subreq) { tevent_req_set_callback(subreq, enum_users_connect_done, req); - - return req; - } - - subreq = sdap_get_users_send(state, state->ev, - state->ctx->be->domain, - state->ctx->be->sysdb, - state->ctx->opts, - state->ctx->gsh, - state->attrs, state->filter); - if (!subreq) { - ret = ENOMEM; - goto fail; + return true; } - tevent_req_set_callback(subreq, enum_users_op_done, req); - - return req; -fail: - tevent_req_error(req, ret); - tevent_req_post(req, ev); - return req; + return enum_users_send_request(req); } -static void enum_users_connect_done(struct tevent_req *subreq) +static void enum_users_connect_done(struct tevent_req* subreq) { - struct tevent_req *req = tevent_req_callback_data(subreq, - struct tevent_req); + struct tevent_req *req = tevent_req_callback_data(subreq, struct tevent_req); struct enum_users_state *state = tevent_req_data(req, struct enum_users_state); - int ret; + int ret, dp_error; - ret = sdap_cli_connect_recv(subreq, state->ctx, - &state->ctx->gsh, &state->ctx->rootDSE); + ret = sdap_id_op_connect_recv(subreq, &dp_error); talloc_zfree(subreq); - if (ret) { - if (ret == ENOTSUP) { - DEBUG(0, ("Authentication mechanism not Supported by server")); + + if (ret != EOK) { + if (dp_error == DP_ERR_OFFLINE) { + state->offline = true; } + tevent_req_error(req, ret); return; } + enum_users_send_request(req); +} + +static bool enum_users_send_request(struct tevent_req *req) +{ + struct enum_users_state *state = tevent_req_data(req, + struct enum_users_state); + struct sdap_id_ctx *ctx = sdap_id_op_ctx(state->op); + struct tevent_req* subreq; + subreq = sdap_get_users_send(state, state->ev, - state->ctx->be->domain, - state->ctx->be->sysdb, - state->ctx->opts, state->ctx->gsh, + ctx->be->domain, + ctx->be->sysdb, + ctx->opts, sdap_id_op_handle(state->op), state->attrs, state->filter); if (!subreq) { tevent_req_error(req, ENOMEM); - return; + return false; } tevent_req_set_callback(subreq, enum_users_op_done, req); + return true; } static void enum_users_op_done(struct tevent_req *subreq) @@ -431,38 +441,66 @@ static void enum_users_op_done(struct tevent_req *subreq) struct tevent_req); struct enum_users_state *state = tevent_req_data(req, struct enum_users_state); + struct sdap_id_ctx *ctx = sdap_id_op_ctx(state->op); char *timestamp; - int ret; + int ret, dp_error; ret = sdap_get_users_recv(subreq, state, ×tamp); talloc_zfree(subreq); - if (ret) { + + ret = sdap_id_op_done(state->op, ret, &dp_error); + if (ret != EOK) { + if (dp_error == DP_ERR_OK) { + /* retry */ + enum_users_connect_retry(req); + return; + } + + if (dp_error == DP_ERR_OFFLINE) { + /* backend is offline */ + state->offline = true; + } + tevent_req_error(req, ret); return; } if (timestamp) { - talloc_zfree(state->ctx->max_user_timestamp); - state->ctx->max_user_timestamp = talloc_steal(state->ctx, timestamp); + talloc_zfree(ctx->max_user_timestamp); + ctx->max_user_timestamp = talloc_steal(ctx, timestamp); } DEBUG(4, ("Users higher timestamp: [%s]\n", - state->ctx->max_user_timestamp)); + ctx->max_user_timestamp)); tevent_req_done(req); } +/* Get the result of users enumeration */ +int enum_users_recv(struct tevent_req *req, bool *offline) +{ + struct enum_users_state *state = tevent_req_data(req, + struct enum_users_state); + *offline = state->offline; + TEVENT_REQ_RETURN_ON_ERROR(req); + + return EOK; +} + /* =Group-Enumeration===================================================== */ struct enum_groups_state { struct tevent_context *ev; - struct sdap_id_ctx *ctx; + struct sdap_id_op *op; + bool offline; char *filter; const char **attrs; }; +static bool enum_groups_connect_retry(struct tevent_req *req); static void enum_groups_connect_done(struct tevent_req *subreq); +static bool enum_groups_send_request(struct tevent_req *req); static void enum_groups_op_done(struct tevent_req *subreq); static struct tevent_req *enum_groups_send(TALLOC_CTX *memctx, @@ -470,7 +508,7 @@ static struct tevent_req *enum_groups_send(TALLOC_CTX *memctx, struct sdap_id_ctx *ctx, bool purge) { - struct tevent_req *req, *subreq; + struct tevent_req *req; struct enum_groups_state *state; const char *attr_name; int ret; @@ -479,7 +517,14 @@ static struct tevent_req *enum_groups_send(TALLOC_CTX *memctx, if (!req) return NULL; state->ev = ev; - state->ctx = ctx; + state->offline = false; + + state->op = sdap_id_op_create(state, ctx); + if (!state->op) { + DEBUG(2, ("Failed to create sdap_id_op\n")); + ret = ENOMEM; + goto fail; + } attr_name = ctx->opts->group_map[SDAP_AT_GROUP_NAME].name; @@ -510,73 +555,82 @@ static struct tevent_req *enum_groups_send(TALLOC_CTX *memctx, SDAP_OPTS_GROUP, &state->attrs); if (ret != EOK) goto fail; - if (!sdap_connected(ctx)) { + if (!enum_groups_connect_retry(req)) { + tevent_req_post(req, ev); + } - if (ctx->gsh) talloc_zfree(ctx->gsh); + return req; - /* FIXME: add option to decide if tls should be used - * or SASL/GSSAPI, etc ... */ - subreq = sdap_cli_connect_send(state, ev, ctx->opts, - ctx->be, ctx->service, - &ctx->rootDSE); - if (!subreq) { - ret = ENOMEM; - goto fail; - } +fail: + tevent_req_error(req, ret); + tevent_req_post(req, ev); + return req; +} + +/* start LDAP connect retry for groups query */ +static bool enum_groups_connect_retry(struct tevent_req *req) +{ + struct enum_groups_state *state = tevent_req_data(req, + struct enum_groups_state); + struct tevent_req *subreq; + int ret; + + ret = sdap_id_op_connect(state->op, state, &subreq); + if (ret != EOK) { + talloc_zfree(subreq); + + tevent_req_error(req, ret); + return false; + } + + if (subreq) { tevent_req_set_callback(subreq, enum_groups_connect_done, req); - - return req; - } - - subreq = sdap_get_groups_send(state, state->ev, - state->ctx->be->domain, - state->ctx->be->sysdb, - state->ctx->opts, state->ctx->gsh, - state->attrs, state->filter); - if (!subreq) { - ret = ENOMEM; - goto fail; + return true; } - tevent_req_set_callback(subreq, enum_groups_op_done, req); - - return req; -fail: - tevent_req_error(req, ret); - tevent_req_post(req, ev); - return req; + return enum_groups_send_request(req); } static void enum_groups_connect_done(struct tevent_req *subreq) { - struct tevent_req *req = tevent_req_callback_data(subreq, - struct tevent_req); + struct tevent_req *req = tevent_req_callback_data(subreq, struct tevent_req); struct enum_groups_state *state = tevent_req_data(req, - struct enum_groups_state); - int ret; + struct enum_groups_state); + int ret, dp_error; - ret = sdap_cli_connect_recv(subreq, state->ctx, - &state->ctx->gsh, &state->ctx->rootDSE); + ret = sdap_id_op_connect_recv(subreq, &dp_error); talloc_zfree(subreq); + if (ret) { - if (ret == ENOTSUP) { - DEBUG(0, ("Authentication mechanism not Supported by server")); + if (dp_error == DP_ERR_OFFLINE) { + state->offline = true; } + tevent_req_error(req, ret); return; } + enum_groups_send_request(req); +} + +static bool enum_groups_send_request(struct tevent_req *req) +{ + struct enum_groups_state *state = tevent_req_data(req, + struct enum_groups_state); + struct sdap_id_ctx *ctx = sdap_id_op_ctx(state->op); + struct tevent_req *subreq; subreq = sdap_get_groups_send(state, state->ev, - state->ctx->be->domain, - state->ctx->be->sysdb, - state->ctx->opts, state->ctx->gsh, + ctx->be->domain, + ctx->be->sysdb, + ctx->opts, sdap_id_op_handle(state->op), state->attrs, state->filter); if (!subreq) { tevent_req_error(req, ENOMEM); - return; + return false; } tevent_req_set_callback(subreq, enum_groups_op_done, req); + return true; } static void enum_groups_op_done(struct tevent_req *subreq) @@ -585,24 +639,48 @@ static void enum_groups_op_done(struct tevent_req *subreq) struct tevent_req); struct enum_groups_state *state = tevent_req_data(req, struct enum_groups_state); + struct sdap_id_ctx *ctx = sdap_id_op_ctx(state->op); char *timestamp; - int ret; + int ret, dp_error; ret = sdap_get_groups_recv(subreq, state, ×tamp); talloc_zfree(subreq); - if (ret) { + + ret = sdap_id_op_done(state->op, ret, &dp_error); + if (ret != EOK) { + if (dp_error == DP_ERR_OK) { + /* retry */ + enum_groups_connect_retry(req); + return; + } + + if (dp_error == DP_ERR_OFFLINE) { + /* backend is offline */ + state->offline = true; + } + tevent_req_error(req, ret); return; } if (timestamp) { - talloc_zfree(state->ctx->max_group_timestamp); - state->ctx->max_group_timestamp = talloc_steal(state->ctx, timestamp); + talloc_zfree(ctx->max_group_timestamp); + ctx->max_group_timestamp = talloc_steal(ctx, timestamp); } DEBUG(4, ("Groups higher timestamp: [%s]\n", - state->ctx->max_group_timestamp)); + ctx->max_group_timestamp)); tevent_req_done(req); } +/* Get the result of groups enumeration */ +int enum_groups_recv(struct tevent_req *req, bool *offline) +{ + struct enum_groups_state *state = tevent_req_data(req, + struct enum_groups_state); + *offline = state->offline; + TEVENT_REQ_RETURN_ON_ERROR(req); + + return EOK; +} diff --git a/src/providers/ldap/sdap_async.h b/src/providers/ldap/sdap_async.h index 020165e..12936c2 100644 --- a/src/providers/ldap/sdap_async.h +++ b/src/providers/ldap/sdap_async.h @@ -106,6 +106,7 @@ struct tevent_req *sdap_cli_connect_send(TALLOC_CTX *memctx, struct sysdb_attrs **rootdse); int sdap_cli_connect_recv(struct tevent_req *req, TALLOC_CTX *memctx, + bool *can_retry, struct sdap_handle **gsh, struct sysdb_attrs **rootdse); diff --git a/src/providers/ldap/sdap_async_connection.c b/src/providers/ldap/sdap_async_connection.c index adfc219..c8d9efd 100644 --- a/src/providers/ldap/sdap_async_connection.c +++ b/src/providers/ldap/sdap_async_connection.c @@ -1120,6 +1120,7 @@ static void sdap_cli_auth_done(struct tevent_req *subreq) int sdap_cli_connect_recv(struct tevent_req *req, TALLOC_CTX *memctx, + bool *can_retry, struct sdap_handle **gsh, struct sysdb_attrs **rootdse) { @@ -1128,10 +1129,13 @@ int sdap_cli_connect_recv(struct tevent_req *req, enum tevent_req_state tstate; uint64_t err; + *can_retry = true; if (tevent_req_is_error(req, &tstate, &err)) { /* mark the server as bad if connection failed */ if (state->srv) { fo_set_port_status(state->srv, PORT_NOT_WORKING); + } else { + *can_retry = false; } if (tstate == TEVENT_REQ_USER_ERROR) { -- 1.6.6.1