URL: https://github.com/SSSD/sssd/pull/5300 Author: pbrezina Title: #5300: ad: use parallel cldap ping for site discovery Action: synchronized
To pull the PR as Git branch: git remote add ghsssd https://github.com/SSSD/sssd git fetch ghsssd pull/5300/head:pr5300 git checkout pr5300
From e147c7e87cb8d35e695c37c7c45474226d7e7418 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20B=C5=99ezina?= <pbrez...@redhat.com> Date: Thu, 30 Jul 2020 12:59:01 +0200 Subject: [PATCH 1/7] ldap: add support for cldap and udp connections --- src/util/sss_ldap.c | 28 ++++++++++++++++++++----- src/util/sss_sockets.c | 47 ++++++++++++++++++++++++++++-------------- src/util/sss_sockets.h | 1 + 3 files changed, 56 insertions(+), 20 deletions(-) diff --git a/src/util/sss_ldap.c b/src/util/sss_ldap.c index 9d1e95217a..976d6208ee 100644 --- a/src/util/sss_ldap.c +++ b/src/util/sss_ldap.c @@ -116,6 +116,7 @@ struct sss_ldap_init_state { LDAP *ldap; int sd; const char *uri; + bool use_udp; }; static int sss_ldap_init_state_destructor(void *data) @@ -159,11 +160,13 @@ struct tevent_req *sss_ldap_init_send(TALLOC_CTX *mem_ctx, state->ldap = NULL; state->sd = -1; state->uri = uri; + state->use_udp = strncmp(uri, "cldap", 5) == 0 ? true : false; #ifdef HAVE_LDAP_INIT_FD struct tevent_req *subreq; - subreq = sssd_async_socket_init_send(state, ev, addr, addr_len, timeout); + subreq = sssd_async_socket_init_send(state, ev, state->use_udp, addr, + addr_len, timeout); if (subreq == NULL) { ret = ENOMEM; DEBUG(SSSDBG_CRIT_FAILURE, "sssd_async_socket_init_send failed.\n"); @@ -244,14 +247,29 @@ static void sss_ldap_init_sys_connect_done(struct tevent_req *subreq) goto fail; } - ret = unset_fcntl_flags(state->sd, O_NONBLOCK); - if (ret != EOK) { - goto fail; + /* openldap < 2.5 does not correctly handle O_NONBLOCK during starttls for + * ldaps, so we need to remove the flag here. This is fine since I/O events + * are handled via tevent so we only read when there is data available. + * + * We need to keep O_NONBLOCK due to a bug in openldap to correctly perform + * a parallel CLDAP pings without timeout. See: + * https://bugs.openldap.org/show_bug.cgi?id=9328 + * + * @todo remove this when the bug is fixed and we can put a hard requirement + * on newer openldap. + */ + if (!state->use_udp) { + ret = unset_fcntl_flags(state->sd, O_NONBLOCK); + if (ret != EOK) { + goto fail; + } } /* Initialize LDAP handler */ - lret = ldap_init_fd(state->sd, LDAP_PROTO_TCP, state->uri, &state->ldap); + lret = ldap_init_fd(state->sd, + state->use_udp ? LDAP_PROTO_UDP : LDAP_PROTO_TCP, + state->uri, &state->ldap); if (lret != LDAP_SUCCESS) { DEBUG(SSSDBG_CRIT_FAILURE, "ldap_init_fd failed: %s. [%d][%s]\n", diff --git a/src/util/sss_sockets.c b/src/util/sss_sockets.c index 6f2b71bc83..733360f3ba 100644 --- a/src/util/sss_sockets.c +++ b/src/util/sss_sockets.c @@ -80,6 +80,18 @@ static errno_t set_fd_common_opts(int fd, int timeout) int ret; struct timeval tv; unsigned int milli; + int type; + socklen_t optlen = sizeof(int); + + /* Get protocol type. */ + ret = getsockopt(fd, SOL_SOCKET, SO_TYPE, &type, &optlen); + if (ret != 0) { + ret = errno; + DEBUG(SSSDBG_FUNC_DATA, "Unable to get socket type [%d]: %s.\n", + ret, strerror(ret)); + /* Assume TCP. */ + type = SOCK_STREAM; + } /* SO_KEEPALIVE and TCP_NODELAY are set by OpenLDAP client libraries but * failures are ignored.*/ @@ -91,12 +103,14 @@ static errno_t set_fd_common_opts(int fd, int timeout) strerror(ret)); } - ret = setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &dummy, sizeof(dummy)); - if (ret != 0) { - ret = errno; - DEBUG(SSSDBG_FUNC_DATA, - "setsockopt TCP_NODELAY failed.[%d][%s].\n", ret, - strerror(ret)); + if (type == SOCK_STREAM) { + ret = setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &dummy, sizeof(dummy)); + if (ret != 0) { + ret = errno; + DEBUG(SSSDBG_FUNC_DATA, + "setsockopt TCP_NODELAY failed.[%d][%s].\n", ret, + strerror(ret)); + } } if (timeout > 0) { @@ -119,14 +133,16 @@ static errno_t set_fd_common_opts(int fd, int timeout) strerror(ret)); } - milli = timeout * 1000; /* timeout in milliseconds */ - ret = setsockopt(fd, IPPROTO_TCP, TCP_USER_TIMEOUT, &milli, - sizeof(milli)); - if (ret != 0) { - ret = errno; - DEBUG(SSSDBG_FUNC_DATA, - "setsockopt TCP_USER_TIMEOUT failed.[%d][%s].\n", ret, - strerror(ret)); + if (type == SOCK_STREAM) { + milli = timeout * 1000; /* timeout in milliseconds */ + ret = setsockopt(fd, IPPROTO_TCP, TCP_USER_TIMEOUT, &milli, + sizeof(milli)); + if (ret != 0) { + ret = errno; + DEBUG(SSSDBG_FUNC_DATA, + "setsockopt TCP_USER_TIMEOUT failed.[%d][%s].\n", ret, + strerror(ret)); + } } } @@ -271,6 +287,7 @@ static void sssd_async_socket_init_done(struct tevent_req *subreq); struct tevent_req *sssd_async_socket_init_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev, + bool use_udp, struct sockaddr_storage *addr, socklen_t addr_len, int timeout) { @@ -289,7 +306,7 @@ struct tevent_req *sssd_async_socket_init_send(TALLOC_CTX *mem_ctx, talloc_set_destructor((TALLOC_CTX *)state, sssd_async_socket_state_destructor); - state->sd = socket(addr->ss_family, SOCK_STREAM, 0); + state->sd = socket(addr->ss_family, use_udp ? SOCK_DGRAM : SOCK_STREAM, 0); if (state->sd == -1) { ret = errno; DEBUG(SSSDBG_CRIT_FAILURE, diff --git a/src/util/sss_sockets.h b/src/util/sss_sockets.h index ccb05cb846..2758e6ed11 100644 --- a/src/util/sss_sockets.h +++ b/src/util/sss_sockets.h @@ -32,6 +32,7 @@ int sssd_async_connect_recv(struct tevent_req *req); struct tevent_req *sssd_async_socket_init_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev, + bool use_udp, struct sockaddr_storage *addr, socklen_t addr_len, int timeout); int sssd_async_socket_init_recv(struct tevent_req *req, int *sd); From 0f8274e35598d5ceb0c9351873528cbeb11025bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20B=C5=99ezina?= <pbrez...@redhat.com> Date: Thu, 30 Jul 2020 13:30:50 +0200 Subject: [PATCH 2/7] ad: use cldap for site and forrest discover (perform CLDAP ping) All Windows clients uses CLDAP (UDP) for LDAP ping. Even though AD also supports LDAP ping over TCP IPA does not therefore it is crusial for us to perform the ping over CLDAP protocol. Resolves: https://github.com/SSSD/sssd/issues/5215 --- src/providers/ad/ad_init.c | 6 +----- src/providers/ad/ad_srv.c | 9 +++------ src/providers/ad/ad_srv.h | 3 +-- src/providers/ad/ad_subdomains.c | 2 +- src/providers/ipa/ipa_subdomains_server.c | 2 +- 5 files changed, 7 insertions(+), 15 deletions(-) diff --git a/src/providers/ad/ad_init.c b/src/providers/ad/ad_init.c index c1cff20093..d88f375c03 100644 --- a/src/providers/ad/ad_init.c +++ b/src/providers/ad/ad_init.c @@ -189,14 +189,11 @@ static errno_t ad_init_srv_plugin(struct be_ctx *be_ctx, const char *ad_site_override; bool sites_enabled; errno_t ret; - bool ad_use_ldaps; hostname = dp_opt_get_string(ad_options->basic, AD_HOSTNAME); ad_domain = dp_opt_get_string(ad_options->basic, AD_DOMAIN); ad_site_override = dp_opt_get_string(ad_options->basic, AD_SITE); sites_enabled = dp_opt_get_bool(ad_options->basic, AD_ENABLE_DNS_SITES); - ad_use_ldaps = dp_opt_get_bool(ad_options->basic, AD_USE_LDAPS); - if (!sites_enabled) { ret = be_fo_set_dns_srv_lookup_plugin(be_ctx, hostname); @@ -212,8 +209,7 @@ static errno_t ad_init_srv_plugin(struct be_ctx *be_ctx, srv_ctx = ad_srv_plugin_ctx_init(be_ctx, be_ctx, be_ctx->be_res, default_host_dbs, ad_options->id, hostname, ad_domain, - ad_site_override, - ad_use_ldaps); + ad_site_override); if (srv_ctx == NULL) { DEBUG(SSSDBG_FATAL_FAILURE, "Out of memory?\n"); return ENOMEM; diff --git a/src/providers/ad/ad_srv.c b/src/providers/ad/ad_srv.c index ca15d3715a..55e8f63f7d 100644 --- a/src/providers/ad/ad_srv.c +++ b/src/providers/ad/ad_srv.c @@ -335,9 +335,9 @@ static errno_t ad_get_client_site_next_dc(struct tevent_req *req) state->be_res->resolv, state->be_res->family_order, state->host_db, - state->ad_use_ldaps ? "ldaps" : "ldap", + "cldap", state->dc.host, - state->ad_use_ldaps ? 636 : state->dc.port, + state->dc.port, false); if (subreq == NULL) { ret = ENOMEM; @@ -497,7 +497,6 @@ struct ad_srv_plugin_ctx { const char *ad_domain; const char *ad_site_override; const char *current_site; - bool ad_use_ldaps; }; struct ad_srv_plugin_ctx * @@ -508,8 +507,7 @@ ad_srv_plugin_ctx_init(TALLOC_CTX *mem_ctx, struct sdap_options *opts, const char *hostname, const char *ad_domain, - const char *ad_site_override, - bool ad_use_ldaps) + const char *ad_site_override) { struct ad_srv_plugin_ctx *ctx = NULL; errno_t ret; @@ -523,7 +521,6 @@ ad_srv_plugin_ctx_init(TALLOC_CTX *mem_ctx, ctx->be_res = be_res; ctx->host_dbs = host_dbs; ctx->opts = opts; - ctx->ad_use_ldaps = ad_use_ldaps; ctx->hostname = talloc_strdup(ctx, hostname); if (ctx->hostname == NULL) { diff --git a/src/providers/ad/ad_srv.h b/src/providers/ad/ad_srv.h index 8e410ec269..e553d594d7 100644 --- a/src/providers/ad/ad_srv.h +++ b/src/providers/ad/ad_srv.h @@ -31,8 +31,7 @@ ad_srv_plugin_ctx_init(TALLOC_CTX *mem_ctx, struct sdap_options *opts, const char *hostname, const char *ad_domain, - const char *ad_site_override, - bool ad_use_ldaps); + const char *ad_site_override); struct tevent_req *ad_srv_plugin_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev, diff --git a/src/providers/ad/ad_subdomains.c b/src/providers/ad/ad_subdomains.c index 7c6f51db77..4c457b7e54 100644 --- a/src/providers/ad/ad_subdomains.c +++ b/src/providers/ad/ad_subdomains.c @@ -411,7 +411,7 @@ ad_subdom_ad_ctx_new(struct be_ctx *be_ctx, ad_id_ctx->ad_options->id, hostname, ad_domain, - ad_site_override, ad_use_ldaps); + ad_site_override); if (srv_ctx == NULL) { DEBUG(SSSDBG_FATAL_FAILURE, "Out of memory?\n"); return ENOMEM; diff --git a/src/providers/ipa/ipa_subdomains_server.c b/src/providers/ipa/ipa_subdomains_server.c index 9aebf72a5a..fcdd053226 100644 --- a/src/providers/ipa/ipa_subdomains_server.c +++ b/src/providers/ipa/ipa_subdomains_server.c @@ -344,7 +344,7 @@ ipa_ad_ctx_new(struct be_ctx *be_ctx, ad_id_ctx->ad_options->id, id_ctx->server_mode->hostname, ad_domain, - ad_site_override, false); + ad_site_override); if (srv_ctx == NULL) { DEBUG(SSSDBG_FATAL_FAILURE, "Out of memory?\n"); return ENOMEM; From 123bb3e08a497d3b5e220f4b0fffd299a7fcbde8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20B=C5=99ezina?= <pbrez...@redhat.com> Date: Tue, 11 Aug 2020 13:27:42 +0200 Subject: [PATCH 3/7] ad: connect to the first available server for cldap ping Resolves: https://github.com/SSSD/sssd/issues/3743 --- Makefile.am | 5 +- src/providers/ad/ad_cldap_ping.c | 586 +++++++++++++++++++++++++++++++ src/providers/ad/ad_srv.c | 434 +---------------------- src/providers/ad/ad_srv.h | 14 + 4 files changed, 613 insertions(+), 426 deletions(-) create mode 100644 src/providers/ad/ad_cldap_ping.c diff --git a/Makefile.am b/Makefile.am index 538009197c..5fd44b169c 100644 --- a/Makefile.am +++ b/Makefile.am @@ -4469,7 +4469,9 @@ libsss_ipa_la_SOURCES = \ src/providers/ad/ad_pac.c \ src/providers/ad/ad_pac_common.c \ src/providers/ad/ad_srv.c \ - src/providers/ad/ad_domain_info.c + src/providers/ad/ad_domain_info.c \ + src/providers/ad/ad_cldap_ping.c \ + $(NULL) libsss_ipa_la_CFLAGS = \ $(AM_CFLAGS) \ $(OPENLDAP_CFLAGS) \ @@ -4537,6 +4539,7 @@ libsss_ad_la_SOURCES = \ src/providers/ad/ad_domain_info.c \ src/providers/ad/ad_refresh.c \ src/providers/ad/ad_resolver.c \ + src/providers/ad/ad_cldap_ping.c \ $(NULL) diff --git a/src/providers/ad/ad_cldap_ping.c b/src/providers/ad/ad_cldap_ping.c new file mode 100644 index 0000000000..5fc1a4d20f --- /dev/null +++ b/src/providers/ad/ad_cldap_ping.c @@ -0,0 +1,586 @@ +/* + Authors: + Pavel Březina <pbrez...@redhat.com> + + Copyright (C) 2020 Red Hat + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <string.h> +#include <talloc.h> +#include <tevent.h> +#include <ndr.h> +#include <ndr/ndr_nbt.h> + +#include "util/util.h" +#include "util/sss_ldap.h" +#include "resolv/async_resolv.h" +#include "providers/backend.h" +#include "providers/ad/ad_srv.h" +#include "providers/ad/ad_common.h" +#include "providers/fail_over.h" +#include "providers/fail_over_srv.h" +#include "providers/ldap/sdap.h" +#include "providers/ldap/sdap_async.h" +#include "db/sysdb.h" + +struct ad_cldap_ping_dc_state { + struct tevent_context *ev; + struct sdap_options *opts; + struct fo_server_info *dc; + struct sdap_handle *sh; + const char *ad_domain; + + char *site; + char *forest; +}; + +static void ad_cldap_ping_dc_connect_done(struct tevent_req *subreq); +static void ad_cldap_ping_dc_done(struct tevent_req *subreq); + +static struct tevent_req *ad_cldap_ping_dc_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct sdap_options *opts, + struct be_resolv_ctx *be_res, + enum host_database *host_db, + struct fo_server_info *dc, + const char *ad_domain) +{ + struct ad_cldap_ping_dc_state *state; + struct tevent_req *subreq; + struct tevent_req *req; + errno_t ret; + + req = tevent_req_create(mem_ctx, &state, + struct ad_cldap_ping_dc_state); + if (req == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "tevent_req_create() failed\n"); + return NULL; + } + + state->ev = ev; + state->opts = opts; + state->dc = dc; + state->ad_domain = ad_domain; + + subreq = sdap_connect_host_send(state, ev, opts, be_res->resolv, + be_res->family_order, host_db, "cldap", + dc->host, dc->port, false); + if (subreq == NULL) { + ret = ENOMEM; + goto done; + } + + tevent_req_set_callback(subreq, ad_cldap_ping_dc_connect_done, req); + + return req; + +done: + tevent_req_error(req, ret); + tevent_req_post(req, ev); + + return req; +} + +static void ad_cldap_ping_dc_connect_done(struct tevent_req *subreq) +{ + static const char *attrs[] = {AD_AT_NETLOGON, NULL}; + struct ad_cldap_ping_dc_state *state; + struct tevent_req *req; + char *ntver; + char *filter; + int timeout; + errno_t ret; + + req = tevent_req_callback_data(subreq, struct tevent_req); + state = tevent_req_data(req, struct ad_cldap_ping_dc_state); + + ret = sdap_connect_host_recv(state, subreq, &state->sh); + talloc_zfree(subreq); + if (ret != EOK) { + goto done; + } + + ntver = sss_ldap_encode_ndr_uint32(state, NETLOGON_NT_VERSION_5EX | + NETLOGON_NT_VERSION_WITH_CLOSEST_SITE); + if (ntver == NULL) { + ret = ENOMEM; + goto done; + } + + filter = talloc_asprintf(state, "(&(%s=%s)(%s=%s))", AD_AT_DNS_DOMAIN, + state->ad_domain, AD_AT_NT_VERSION, ntver); + if (filter == NULL) { + ret = ENOMEM; + goto done; + } + + timeout = dp_opt_get_int(state->opts->basic, SDAP_SEARCH_TIMEOUT); + subreq = sdap_get_generic_send(state, state->ev, state->opts, state->sh, "", + LDAP_SCOPE_BASE, filter, attrs, NULL, + 0, timeout, false); + if (subreq == NULL) { + ret = ENOMEM; + goto done; + } + + tevent_req_set_callback(subreq, ad_cldap_ping_dc_done, req); + + ret = EOK; + +done: + if (ret != EOK) { + tevent_req_error(req, ret); + } +} + +static void ad_cldap_ping_dc_done(struct tevent_req *subreq) +{ + struct ad_cldap_ping_dc_state *state; + struct tevent_req *req; + struct sysdb_attrs **reply; + size_t reply_count; + errno_t ret; + + req = tevent_req_callback_data(subreq, struct tevent_req); + state = tevent_req_data(req, struct ad_cldap_ping_dc_state); + + ret = sdap_get_generic_recv(subreq, state, &reply_count, &reply); + + talloc_zfree(subreq); + talloc_zfree(state->sh); + + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, "%s:%d: unable to get netlogon information\n", + state->dc->host, state->dc->port); + goto done; + } + + if (reply_count == 0) { + DEBUG(SSSDBG_OP_FAILURE, "%s:%d: no netlogon information available\n", + state->dc->host, state->dc->port); + ret = ENOENT; + goto done; + } + + ret = netlogon_get_domain_info(state, reply[0], true, NULL, &state->site, + &state->forest); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, + "%s:%d: unable to retrieve site name [%d]: %s\n", + state->dc->host, state->dc->port, ret, sss_strerror(ret)); + ret = ENOENT; + goto done; + } + + DEBUG(SSSDBG_TRACE_FUNC, "%s:%d: found site (%s) and forest (%s)\n", + state->dc->host, state->dc->port, state->site, state->forest); + + ret = EOK; + +done: + if (ret != EOK) { + tevent_req_error(req, ret); + return; + } + + tevent_req_done(req); +} + +static errno_t ad_cldap_ping_dc_recv(TALLOC_CTX *mem_ctx, + struct tevent_req *req, + const char **_site, + const char **_forest) +{ + struct ad_cldap_ping_dc_state *state = NULL; + state = tevent_req_data(req, struct ad_cldap_ping_dc_state); + + TEVENT_REQ_RETURN_ON_ERROR(req); + + *_site = talloc_steal(mem_ctx, state->site); + *_forest = talloc_steal(mem_ctx, state->forest); + + return EOK; +} + +struct ad_cldap_ping_parallel_state { + struct tevent_context *ev; + struct sdap_options *opts; + struct be_resolv_ctx *be_res; + enum host_database *host_db; + const char *ad_domain; + struct fo_server_info *dc_list; + size_t dc_count; + + TALLOC_CTX *reqs_ctx; + struct tevent_timer *te; + int active_requests; + size_t next_dc; + int batch; + + const char *site; + const char *forest; +}; + +static void ad_cldap_ping_parallel_batch(struct tevent_context *ev, + struct tevent_timer *te, + struct timeval tv, + void *data); +static void ad_cldap_ping_parallel_done(struct tevent_req *subreq); + +static struct tevent_req * +ad_cldap_ping_parallel_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct sdap_options *opts, + struct be_resolv_ctx *be_res, + enum host_database *host_db, + struct fo_server_info *dc_list, + size_t dc_count, + const char *ad_domain) +{ + struct ad_cldap_ping_parallel_state *state; + struct tevent_req *req; + struct timeval tv = {0, 0}; + errno_t ret; + + req = tevent_req_create(mem_ctx, &state, + struct ad_cldap_ping_parallel_state); + if (req == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "tevent_req_create() failed\n"); + return NULL; + } + + state->ev = ev; + state->opts = opts; + state->be_res = be_res; + state->host_db = host_db; + state->ad_domain = ad_domain; + state->dc_list = dc_list; + state->dc_count = dc_count; + + state->reqs_ctx = talloc_new(state); + if (state->reqs_ctx == NULL) { + ret = ENOMEM; + goto done; + } + + state->next_dc = 0; + state->batch = 1; + ad_cldap_ping_parallel_batch(ev, NULL, tv, req); + + return req; + +done: + tevent_req_error(req, ret); + tevent_req_post(req, ev); + + return req; +} + +static void ad_cldap_ping_parallel_batch(struct tevent_context *ev, + struct tevent_timer *te, + struct timeval tv, + void *data) +{ + struct ad_cldap_ping_parallel_state *state; + struct tevent_req *req; + struct tevent_req *subreq; + uint32_t delay; + size_t limit; + size_t i; + + req = talloc_get_type(data, struct tevent_req); + state = tevent_req_data(req, struct ad_cldap_ping_parallel_state); + + state->te = NULL; + + /* Issue three batches in total to avoid pinging too many domain controllers + * if not necessary. The first batch (5 pings) is issued immediately and we + * will wait 400ms for it to finish. If we don't get a reply in time we + * issue next batch (5 pings) and wait 200ms. If we still have no reply, + * we contact remaining domain controllers. + * + * This follows algorithm described at section 5.4.5.3 of MS-DISO: + * https://winprotocoldoc.blob.core.windows.net/productionwindowsarchives/WinArchive/%5bMS-DISO%5d.pdf + */ + switch (state->batch) { + case 1: + case 2: + limit = MIN(state->dc_count, 5 + state->next_dc); + delay = 400000 / state->batch; + break; + default: + limit = state->dc_count; + delay = 0; + } + + for (i = state->next_dc; i < limit; i++) { + DEBUG(SSSDBG_TRACE_ALL, "Batch %d: %s:%d\n", state->batch, + state->dc_list[i].host, state->dc_list[i].port); + } + + for (; state->next_dc < limit; state->next_dc++) { + subreq = ad_cldap_ping_dc_send(state->reqs_ctx, ev, state->opts, + state->be_res, state->host_db, + &state->dc_list[state->next_dc], + state->ad_domain); + if (subreq == NULL) { + DEBUG(SSSDBG_OP_FAILURE, "Unable to create new ping request\n"); + goto fail; + } + + state->active_requests++; + tevent_req_set_callback(subreq, ad_cldap_ping_parallel_done, req); + } + + state->batch++; + if (delay > 0) { + tv = tevent_timeval_current_ofs(0, delay); + state->te = tevent_add_timer(ev, state->reqs_ctx, tv, + ad_cldap_ping_parallel_batch, req); + if (state->te == NULL) { + DEBUG(SSSDBG_OP_FAILURE, "Unable to schedule next batch!\n"); + goto fail; + } + } + + return; + +fail: + if (state->active_requests == 0) { + tevent_req_error(req, ENOMEM); + if (state->batch == 1) { + tevent_req_post(req, ev); + } + } +} + +static void ad_cldap_ping_parallel_done(struct tevent_req *subreq) +{ + struct ad_cldap_ping_parallel_state *state; + struct timeval tv = {0, 0}; + struct tevent_req *req; + errno_t ret; + + req = tevent_req_callback_data(subreq, struct tevent_req); + state = tevent_req_data(req, struct ad_cldap_ping_parallel_state); + + ret = ad_cldap_ping_dc_recv(state, subreq, &state->site, &state->forest); + talloc_zfree(subreq); + state->active_requests--; + + if (ret == EOK) { + /* We have the answer. Terminate other attempts and finish. */ + talloc_zfree(state->reqs_ctx); + tevent_req_done(req); + } else if (state->active_requests == 0) { + /* There are still servers to try, don't wait for the timer. */ + if (state->next_dc < state->dc_count) { + talloc_zfree(state->te); + ad_cldap_ping_parallel_batch(state->ev, NULL, tv, req); + return; + } + /* There is no available server. */ + tevent_req_error(req, ENOENT); + } + + /* Wait for another request to finish. */ +} + +static errno_t ad_cldap_ping_parallel_recv(TALLOC_CTX *mem_ctx, + struct tevent_req *req, + const char **_site, + const char **_forest) +{ + struct ad_cldap_ping_parallel_state *state = NULL; + state = tevent_req_data(req, struct ad_cldap_ping_parallel_state); + + TEVENT_REQ_RETURN_ON_ERROR(req); + + *_site = talloc_steal(mem_ctx, state->site); + *_forest = talloc_steal(mem_ctx, state->forest); + + return EOK; +} + +struct ad_cldap_ping_state { + struct tevent_context *ev; + struct sdap_options *opts; + struct be_resolv_ctx *be_res; + enum host_database *host_db; + const char *ad_domain; + + struct fo_server_info *dc_list; + size_t dc_count; + const char *site; + const char *forest; +}; + +static void ad_cldap_ping_discovery_done(struct tevent_req *subreq); +static void ad_cldap_ping_done(struct tevent_req *subreq); + +struct tevent_req *ad_cldap_ping_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct sdap_options *opts, + struct be_resolv_ctx *be_res, + enum host_database *host_db, + const char *ad_domain, + const char *discovery_domain, + const char *current_site) +{ + struct ad_cldap_ping_state *state; + struct tevent_req *subreq; + struct tevent_req *req; + const char **domains; + errno_t ret; + + req = tevent_req_create(mem_ctx, &state, struct ad_cldap_ping_state); + if (req == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "tevent_req_create() failed\n"); + return NULL; + } + + state->ev = ev; + state->opts = opts; + state->be_res = be_res; + state->host_db = host_db; + state->ad_domain = ad_domain; + + domains = talloc_zero_array(state, const char *, 3); + if (domains == NULL) { + ret = ENOMEM; + goto done; + } + + if (current_site == NULL) { + domains[0] = discovery_domain; + domains[1] = NULL; + } else { + domains[0] = ad_site_dns_discovery_domain(state, current_site, + discovery_domain); + domains[1] = discovery_domain; + + if (domains[0] == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "Out of memory!"); + ret = ENOMEM; + goto done; + } + } + + /* Even though we use CLDAP (UDP) to perform the ping we need to discover + * domain controllers in TCP namespace as they are not automatically + * available under UDP. */ + subreq = fo_discover_srv_send(state, ev, be_res->resolv, "ldap", + FO_PROTO_TCP, domains); + if (subreq == NULL) { + ret = ENOMEM; + goto done; + } + + tevent_req_set_callback(subreq, ad_cldap_ping_discovery_done, req); + + return req; + +done: + tevent_req_error(req, ret); + tevent_req_post(req, ev); + + return req; +} + +static void ad_cldap_ping_discovery_done(struct tevent_req *subreq) +{ + struct ad_cldap_ping_state *state; + struct tevent_req *req; + char *domain; + errno_t ret; + + req = tevent_req_callback_data(subreq, struct tevent_req); + state = tevent_req_data(req, struct ad_cldap_ping_state); + + ret = fo_discover_srv_recv(state, subreq, &domain, NULL, &state->dc_list, + &state->dc_count); + talloc_zfree(subreq); + if (ret != EOK) { + goto done; + } + + DEBUG(SSSDBG_TRACE_FUNC, "Found %zu domain controllers in domain %s\n", + state->dc_count, domain); + + subreq = ad_cldap_ping_parallel_send(state, state->ev, state->opts, + state->be_res, state->host_db, + state->dc_list, state->dc_count, + state->ad_domain); + if (subreq == NULL) { + ret = ENOMEM; + goto done; + } + + tevent_req_set_callback(subreq, ad_cldap_ping_done, req); + +done: + if (ret != EOK) { + tevent_req_error(req, ret); + return; + } +} + +static void ad_cldap_ping_done(struct tevent_req *subreq) +{ + struct ad_cldap_ping_state *state; + struct tevent_req *req; + errno_t ret; + + req = tevent_req_callback_data(subreq, struct tevent_req); + state = tevent_req_data(req, struct ad_cldap_ping_state); + + ret = ad_cldap_ping_parallel_recv(state, subreq, &state->site, + &state->forest); + talloc_zfree(subreq); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, + "Unable to get site and forest information [%d]: %s\n", + ret, sss_strerror(ret)); + goto done; + } + + DEBUG(SSSDBG_TRACE_FUNC, "Found site: %s\n", state->site); + DEBUG(SSSDBG_TRACE_FUNC, "Found forest: %s\n", state->forest); + +done: + if (ret != EOK) { + tevent_req_error(req, ret); + return; + } + + tevent_req_done(req); +} + +errno_t ad_cldap_ping_recv(TALLOC_CTX *mem_ctx, + struct tevent_req *req, + const char **_site, + const char **_forest) +{ + struct ad_cldap_ping_state *state = NULL; + state = tevent_req_data(req, struct ad_cldap_ping_state); + + TEVENT_REQ_RETURN_ON_ERROR(req); + + *_site = talloc_steal(mem_ctx, state->site); + *_forest = talloc_steal(mem_ctx, state->forest); + + return EOK; +} \ No newline at end of file diff --git a/src/providers/ad/ad_srv.c b/src/providers/ad/ad_srv.c index 55e8f63f7d..d12f0971c2 100644 --- a/src/providers/ad/ad_srv.c +++ b/src/providers/ad/ad_srv.c @@ -116,378 +116,6 @@ static errno_t ad_sort_servers_by_dns(TALLOC_CTX *mem_ctx, return EOK; } -struct ad_get_dc_servers_state { - struct fo_server_info *servers; - size_t num_servers; -}; - -static void ad_get_dc_servers_done(struct tevent_req *subreq); - -static struct tevent_req *ad_get_dc_servers_send(TALLOC_CTX *mem_ctx, - struct tevent_context *ev, - struct resolv_ctx *resolv_ctx, - const char *discovery_domain, - const char *site) -{ - struct ad_get_dc_servers_state *state = NULL; - struct tevent_req *req = NULL; - struct tevent_req *subreq = NULL; - const char **domains = NULL; - errno_t ret; - - req = tevent_req_create(mem_ctx, &state, - struct ad_get_dc_servers_state); - if (req == NULL) { - DEBUG(SSSDBG_CRIT_FAILURE, "tevent_req_create() failed\n"); - return NULL; - } - - domains = talloc_zero_array(state, const char *, 3); - if (domains == NULL) { - ret = ENOMEM; - goto immediately; - } - - if (site == NULL) { - DEBUG(SSSDBG_TRACE_FUNC, "Looking up domain controllers in domain " - "%s\n", discovery_domain); - - domains[0] = talloc_strdup(domains, discovery_domain); - if (domains[0] == NULL) { - ret = ENOMEM; - goto immediately; - } - } else { - DEBUG(SSSDBG_TRACE_FUNC, "Looking up domain controllers in domain " - "%s and site %s\n", discovery_domain, site); - - domains[0] = ad_site_dns_discovery_domain(domains, - site, discovery_domain); - if (domains[0] == NULL) { - ret = ENOMEM; - goto immediately; - } - - domains[1] = talloc_strdup(domains, discovery_domain); - if (domains[1] == NULL) { - ret = ENOMEM; - goto immediately; - } - } - - subreq = fo_discover_srv_send(state, ev, resolv_ctx, - "ldap", FO_PROTO_TCP, domains); - if (subreq == NULL) { - ret = ENOMEM; - goto immediately; - } - - tevent_req_set_callback(subreq, ad_get_dc_servers_done, req); - - return req; - -immediately: - tevent_req_error(req, ret); - tevent_req_post(req, ev); - - return req; -} - -static void ad_get_dc_servers_done(struct tevent_req *subreq) -{ - struct ad_get_dc_servers_state *state = NULL; - struct tevent_req *req = NULL; - char *domain = NULL; - errno_t ret; - - req = tevent_req_callback_data(subreq, struct tevent_req); - state = tevent_req_data(req, struct ad_get_dc_servers_state); - - ret = fo_discover_srv_recv(state, subreq, &domain, NULL, - &state->servers, &state->num_servers); - talloc_zfree(subreq); - if (ret != EOK) { - goto done; - } - - DEBUG(SSSDBG_TRACE_FUNC, "Found %zu domain controllers in domain %s\n", - state->num_servers, domain); - -done: - if (ret != EOK) { - tevent_req_error(req, ret); - return; - } - - tevent_req_done(req); -} - -static int ad_get_dc_servers_recv(TALLOC_CTX *mem_ctx, - struct tevent_req *req, - struct fo_server_info **_dcs, - size_t *_num_dcs) -{ - struct ad_get_dc_servers_state *state = NULL; - state = tevent_req_data(req, struct ad_get_dc_servers_state); - - TEVENT_REQ_RETURN_ON_ERROR(req); - - *_dcs = talloc_steal(mem_ctx, state->servers); - *_num_dcs = state->num_servers; - - return EOK; -} - -struct ad_get_client_site_state { - struct tevent_context *ev; - struct be_resolv_ctx *be_res; - enum host_database *host_db; - struct sdap_options *opts; - const char *ad_domain; - bool ad_use_ldaps; - struct fo_server_info *dcs; - size_t num_dcs; - size_t dc_index; - struct fo_server_info dc; - - struct sdap_handle *sh; - char *site; - char *forest; -}; - -static errno_t ad_get_client_site_next_dc(struct tevent_req *req); -static void ad_get_client_site_connect_done(struct tevent_req *subreq); -static void ad_get_client_site_done(struct tevent_req *subreq); - -struct tevent_req *ad_get_client_site_send(TALLOC_CTX *mem_ctx, - struct tevent_context *ev, - struct be_resolv_ctx *be_res, - enum host_database *host_db, - struct sdap_options *opts, - const char *ad_domain, - bool ad_use_ldaps, - struct fo_server_info *dcs, - size_t num_dcs) -{ - struct ad_get_client_site_state *state = NULL; - struct tevent_req *req = NULL; - errno_t ret; - - req = tevent_req_create(mem_ctx, &state, - struct ad_get_client_site_state); - if (req == NULL) { - DEBUG(SSSDBG_CRIT_FAILURE, "tevent_req_create() failed\n"); - return NULL; - } - - if (be_res == NULL || host_db == NULL || opts == NULL) { - ret = EINVAL; - goto immediately; - } - - state->ev = ev; - state->be_res = be_res; - state->host_db = host_db; - state->opts = opts; - state->ad_domain = ad_domain; - state->ad_use_ldaps = ad_use_ldaps; - state->dcs = dcs; - state->num_dcs = num_dcs; - - state->dc_index = 0; - ret = ad_get_client_site_next_dc(req); - if (ret == EOK) { - ret = ENOENT; - goto immediately; - } else if (ret != EAGAIN) { - goto immediately; - } - - return req; - -immediately: - if (ret == EOK) { - tevent_req_done(req); - } else { - tevent_req_error(req, ret); - } - tevent_req_post(req, ev); - - return req; -} - -static errno_t ad_get_client_site_next_dc(struct tevent_req *req) -{ - struct ad_get_client_site_state *state = NULL; - struct tevent_req *subreq = NULL; - errno_t ret; - - state = tevent_req_data(req, struct ad_get_client_site_state); - - if (state->dc_index >= state->num_dcs) { - ret = EOK; - goto done; - } - - state->dc = state->dcs[state->dc_index]; - - subreq = sdap_connect_host_send(state, state->ev, state->opts, - state->be_res->resolv, - state->be_res->family_order, - state->host_db, - "cldap", - state->dc.host, - state->dc.port, - false); - if (subreq == NULL) { - ret = ENOMEM; - goto done; - } - - tevent_req_set_callback(subreq, ad_get_client_site_connect_done, req); - - state->dc_index++; - ret = EAGAIN; - -done: - return ret; -} - -static void ad_get_client_site_connect_done(struct tevent_req *subreq) -{ - struct ad_get_client_site_state *state = NULL; - struct tevent_req *req = NULL; - static const char *attrs[] = {AD_AT_NETLOGON, NULL}; - char *filter = NULL; - char *ntver = NULL; - errno_t ret; - - req = tevent_req_callback_data(subreq, struct tevent_req); - state = tevent_req_data(req, struct ad_get_client_site_state); - - ret = sdap_connect_host_recv(state, subreq, &state->sh); - talloc_zfree(subreq); - if (ret != EOK) { - DEBUG(SSSDBG_MINOR_FAILURE, "Unable to connect to domain controller " - "[%s:%d]\n", state->dc.host, state->dc.port); - - ret = ad_get_client_site_next_dc(req); - if (ret == EOK) { - ret = ENOENT; - } - - goto done; - } - - ntver = sss_ldap_encode_ndr_uint32(state, NETLOGON_NT_VERSION_5EX | - NETLOGON_NT_VERSION_WITH_CLOSEST_SITE); - if (ntver == NULL) { - ret = ENOMEM; - goto done; - } - - filter = talloc_asprintf(state, "(&(%s=%s)(%s=%s))", - AD_AT_DNS_DOMAIN, state->ad_domain, - AD_AT_NT_VERSION, ntver); - if (filter == NULL) { - ret = ENOMEM; - goto done; - } - - subreq = sdap_get_generic_send(state, state->ev, state->opts, state->sh, - "", LDAP_SCOPE_BASE, filter, - attrs, NULL, 0, - dp_opt_get_int(state->opts->basic, - SDAP_SEARCH_TIMEOUT), - false); - if (subreq == NULL) { - ret = ENOMEM; - goto done; - } - - tevent_req_set_callback(subreq, ad_get_client_site_done, req); - - ret = EAGAIN; - -done: - if (ret == EOK) { - tevent_req_done(req); - } else if (ret != EAGAIN) { - tevent_req_error(req, ret); - } - - return; -} - -static void ad_get_client_site_done(struct tevent_req *subreq) -{ - struct ad_get_client_site_state *state = NULL; - struct tevent_req *req = NULL; - struct sysdb_attrs **reply = NULL; - size_t reply_count; - errno_t ret; - - req = tevent_req_callback_data(subreq, struct tevent_req); - state = tevent_req_data(req, struct ad_get_client_site_state); - - ret = sdap_get_generic_recv(subreq, state, &reply_count, &reply); - talloc_zfree(subreq); - - /* we're done with this LDAP, close connection */ - talloc_zfree(state->sh); - if (ret != EOK) { - DEBUG(SSSDBG_OP_FAILURE, "Unable to get netlogon information\n"); - - ret = ad_get_client_site_next_dc(req); - if (ret == EOK) { - ret = ENOENT; - } - goto done; - } - - if (reply_count == 0) { - DEBUG(SSSDBG_OP_FAILURE, "No netlogon information retrieved\n"); - ret = ENOENT; - goto done; - } - - ret = netlogon_get_domain_info(state, reply[0], true, NULL, &state->site, - &state->forest); - if (ret != EOK) { - DEBUG(SSSDBG_OP_FAILURE, "Unable to retrieve site name [%d]: %s\n", - ret, strerror(ret)); - ret = ENOENT; - goto done; - } - - DEBUG(SSSDBG_TRACE_FUNC, "Found site: %s\n", state->site); - DEBUG(SSSDBG_TRACE_FUNC, "Found forest: %s\n", state->forest); - -done: - if (ret != EOK) { - tevent_req_error(req, ret); - return; - } - - tevent_req_done(req); -} - -int ad_get_client_site_recv(TALLOC_CTX *mem_ctx, - struct tevent_req *req, - const char **_site, - const char **_forest) -{ - struct ad_get_client_site_state *state = NULL; - state = tevent_req_data(req, struct ad_get_client_site_state); - - TEVENT_REQ_RETURN_ON_ERROR(req); - - *_site = talloc_steal(mem_ctx, state->site); - *_forest = talloc_steal(mem_ctx, state->forest); - - return EOK; -} - struct ad_srv_plugin_ctx { struct be_ctx *be_ctx; struct be_resolv_ctx *be_res; @@ -610,8 +238,7 @@ struct ad_srv_plugin_state { size_t num_backup_servers; }; -static void ad_srv_plugin_dcs_done(struct tevent_req *subreq); -static void ad_srv_plugin_site_done(struct tevent_req *subreq); +static void ad_srv_plugin_ping_done(struct tevent_req *subreq); static void ad_srv_plugin_servers_done(struct tevent_req *subreq); /* 1. Do a DNS lookup to find any DC in domain @@ -677,15 +304,16 @@ struct tevent_req *ad_srv_plugin_send(TALLOC_CTX *mem_ctx, DEBUG(SSSDBG_TRACE_FUNC, "About to find domain controllers\n"); - subreq = ad_get_dc_servers_send(state, ev, ctx->be_res->resolv, - state->discovery_domain, - state->ctx->current_site); + subreq = ad_cldap_ping_send(state, ev, ctx->opts, ctx->be_res, + ctx->host_dbs, ctx->ad_domain, + state->discovery_domain, + state->ctx->current_site); if (subreq == NULL) { ret = ENOMEM; goto immediately; } - tevent_req_set_callback(subreq, ad_srv_plugin_dcs_done, req); + tevent_req_set_callback(subreq, ad_srv_plugin_ping_done, req); return req; @@ -696,52 +324,7 @@ struct tevent_req *ad_srv_plugin_send(TALLOC_CTX *mem_ctx, return req; } -static void ad_srv_plugin_dcs_done(struct tevent_req *subreq) -{ - struct ad_srv_plugin_state *state = NULL; - struct tevent_req *req = NULL; - struct fo_server_info *dcs = NULL; - size_t num_dcs = 0; - errno_t ret; - - req = tevent_req_callback_data(subreq, struct tevent_req); - state = tevent_req_data(req, struct ad_srv_plugin_state); - - ret = ad_get_dc_servers_recv(state, subreq, &dcs, &num_dcs); - talloc_zfree(subreq); - if (ret != EOK) { - goto done; - } - - DEBUG(SSSDBG_TRACE_FUNC, "About to locate suitable site\n"); - - subreq = ad_get_client_site_send(state, state->ev, - state->ctx->be_res, - state->ctx->host_dbs, - state->ctx->opts, - state->discovery_domain, - state->ctx->ad_use_ldaps, - dcs, num_dcs); - if (subreq == NULL) { - ret = ENOMEM; - goto done; - } - - tevent_req_set_callback(subreq, ad_srv_plugin_site_done, req); - - ret = EAGAIN; - -done: - if (ret == EOK) { - tevent_req_done(req); - } else if (ret != EAGAIN) { - tevent_req_error(req, ret); - } - - return; -} - -static void ad_srv_plugin_site_done(struct tevent_req *subreq) +static void ad_srv_plugin_ping_done(struct tevent_req *subreq) { struct ad_srv_plugin_state *state = NULL; struct tevent_req *req = NULL; @@ -752,8 +335,9 @@ static void ad_srv_plugin_site_done(struct tevent_req *subreq) req = tevent_req_callback_data(subreq, struct tevent_req); state = tevent_req_data(req, struct ad_srv_plugin_state); - ret = ad_get_client_site_recv(state, subreq, &state->site, &state->forest); + ret = ad_cldap_ping_recv(state, subreq, &state->site, &state->forest); talloc_zfree(subreq); + /* Ignore AD site found by dns discovery if specific site is set in * configuration file. */ if (state->ctx->ad_site_override != NULL) { diff --git a/src/providers/ad/ad_srv.h b/src/providers/ad/ad_srv.h index e553d594d7..c03ac873fc 100644 --- a/src/providers/ad/ad_srv.h +++ b/src/providers/ad/ad_srv.h @@ -53,4 +53,18 @@ char *ad_site_dns_discovery_domain(TALLOC_CTX *mem_ctx, const char *site, const char *domain); +struct tevent_req *ad_cldap_ping_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct sdap_options *opts, + struct be_resolv_ctx *be_res, + enum host_database *host_db, + const char *ad_domain, + const char *discovery_domain, + const char *current_site); + +errno_t ad_cldap_ping_recv(TALLOC_CTX *mem_ctx, + struct tevent_req *req, + const char **_site, + const char **_forest); + #endif /* __AD_SRV_H__ */ From d3542930a4f19b823701fa276ada8979bc520086 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20B=C5=99ezina?= <pbrez...@redhat.com> Date: Tue, 25 Aug 2020 12:11:19 +0200 Subject: [PATCH 4/7] ad: if all in-site dc are unreachable try off-site controllers Previous implementation would not fallback to the off-site domain controllers. This would cause problems if the site actually changed. --- src/providers/ad/ad_cldap_ping.c | 227 ++++++++++++++++++++++++------- 1 file changed, 181 insertions(+), 46 deletions(-) diff --git a/src/providers/ad/ad_cldap_ping.c b/src/providers/ad/ad_cldap_ping.c index 5fc1a4d20f..7ecdcdbef0 100644 --- a/src/providers/ad/ad_cldap_ping.c +++ b/src/providers/ad/ad_cldap_ping.c @@ -415,7 +415,7 @@ static errno_t ad_cldap_ping_parallel_recv(TALLOC_CTX *mem_ctx, return EOK; } -struct ad_cldap_ping_state { +struct ad_cldap_ping_domain_state { struct tevent_context *ev; struct sdap_options *opts; struct be_resolv_ctx *be_res; @@ -428,25 +428,25 @@ struct ad_cldap_ping_state { const char *forest; }; -static void ad_cldap_ping_discovery_done(struct tevent_req *subreq); -static void ad_cldap_ping_done(struct tevent_req *subreq); +static void ad_cldap_ping_domain_discovery_done(struct tevent_req *subreq); +static void ad_cldap_ping_domain_done(struct tevent_req *subreq); -struct tevent_req *ad_cldap_ping_send(TALLOC_CTX *mem_ctx, - struct tevent_context *ev, - struct sdap_options *opts, - struct be_resolv_ctx *be_res, - enum host_database *host_db, - const char *ad_domain, - const char *discovery_domain, - const char *current_site) +static struct tevent_req * +ad_cldap_ping_domain_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct sdap_options *opts, + struct be_resolv_ctx *be_res, + enum host_database *host_db, + const char *ad_domain, + const char *discovery_domain) { - struct ad_cldap_ping_state *state; + struct ad_cldap_ping_domain_state *state; struct tevent_req *subreq; struct tevent_req *req; const char **domains; errno_t ret; - req = tevent_req_create(mem_ctx, &state, struct ad_cldap_ping_state); + req = tevent_req_create(mem_ctx, &state, struct ad_cldap_ping_domain_state); if (req == NULL) { DEBUG(SSSDBG_CRIT_FAILURE, "tevent_req_create() failed\n"); return NULL; @@ -458,25 +458,18 @@ struct tevent_req *ad_cldap_ping_send(TALLOC_CTX *mem_ctx, state->host_db = host_db; state->ad_domain = ad_domain; - domains = talloc_zero_array(state, const char *, 3); + domains = talloc_zero_array(state, const char *, 2); if (domains == NULL) { ret = ENOMEM; goto done; } - if (current_site == NULL) { - domains[0] = discovery_domain; - domains[1] = NULL; - } else { - domains[0] = ad_site_dns_discovery_domain(state, current_site, - discovery_domain); - domains[1] = discovery_domain; - - if (domains[0] == NULL) { - DEBUG(SSSDBG_CRIT_FAILURE, "Out of memory!"); - ret = ENOMEM; - goto done; - } + domains[0] = discovery_domain; + domains[1] = NULL; + if (domains[0] == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "Out of memory!"); + ret = ENOMEM; + goto done; } /* Even though we use CLDAP (UDP) to perform the ping we need to discover @@ -489,7 +482,7 @@ struct tevent_req *ad_cldap_ping_send(TALLOC_CTX *mem_ctx, goto done; } - tevent_req_set_callback(subreq, ad_cldap_ping_discovery_done, req); + tevent_req_set_callback(subreq, ad_cldap_ping_domain_discovery_done, req); return req; @@ -500,15 +493,15 @@ struct tevent_req *ad_cldap_ping_send(TALLOC_CTX *mem_ctx, return req; } -static void ad_cldap_ping_discovery_done(struct tevent_req *subreq) +static void ad_cldap_ping_domain_discovery_done(struct tevent_req *subreq) { - struct ad_cldap_ping_state *state; + struct ad_cldap_ping_domain_state *state; struct tevent_req *req; char *domain; errno_t ret; req = tevent_req_callback_data(subreq, struct tevent_req); - state = tevent_req_data(req, struct ad_cldap_ping_state); + state = tevent_req_data(req, struct ad_cldap_ping_domain_state); ret = fo_discover_srv_recv(state, subreq, &domain, NULL, &state->dc_list, &state->dc_count); @@ -529,7 +522,7 @@ static void ad_cldap_ping_discovery_done(struct tevent_req *subreq) goto done; } - tevent_req_set_callback(subreq, ad_cldap_ping_done, req); + tevent_req_set_callback(subreq, ad_cldap_ping_domain_done, req); done: if (ret != EOK) { @@ -538,41 +531,183 @@ static void ad_cldap_ping_discovery_done(struct tevent_req *subreq) } } -static void ad_cldap_ping_done(struct tevent_req *subreq) +static void ad_cldap_ping_domain_done(struct tevent_req *subreq) { - struct ad_cldap_ping_state *state; + struct ad_cldap_ping_domain_state *state; struct tevent_req *req; errno_t ret; req = tevent_req_callback_data(subreq, struct tevent_req); - state = tevent_req_data(req, struct ad_cldap_ping_state); + state = tevent_req_data(req, struct ad_cldap_ping_domain_state); ret = ad_cldap_ping_parallel_recv(state, subreq, &state->site, &state->forest); talloc_zfree(subreq); if (ret != EOK) { - DEBUG(SSSDBG_OP_FAILURE, - "Unable to get site and forest information [%d]: %s\n", - ret, sss_strerror(ret)); + tevent_req_error(req, ret); + return; + } + + tevent_req_done(req); +} + +static errno_t ad_cldap_ping_domain_recv(TALLOC_CTX *mem_ctx, + struct tevent_req *req, + const char **_site, + const char **_forest) +{ + struct ad_cldap_ping_domain_state *state = NULL; + state = tevent_req_data(req, struct ad_cldap_ping_domain_state); + + TEVENT_REQ_RETURN_ON_ERROR(req); + + *_site = talloc_steal(mem_ctx, state->site); + *_forest = talloc_steal(mem_ctx, state->forest); + + return EOK; +} + +struct ad_cldap_ping_state { + struct tevent_context *ev; + struct sdap_options *opts; + struct be_resolv_ctx *be_res; + enum host_database *host_db; + const char *ad_domain; + const char *discovery_domain; + bool all_tried; + + const char *site; + const char *forest; +}; + +static errno_t ad_cldap_ping_step(struct tevent_req *req, + const char *domain); +static void ad_cldap_ping_done(struct tevent_req *subreq); + +struct tevent_req *ad_cldap_ping_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct sdap_options *opts, + struct be_resolv_ctx *be_res, + enum host_database *host_db, + const char *ad_domain, + const char *discovery_domain, + const char *current_site) +{ + struct ad_cldap_ping_state *state; + struct tevent_req *req; + const char *domain; + errno_t ret; + + req = tevent_req_create(mem_ctx, &state, struct ad_cldap_ping_state); + if (req == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "tevent_req_create() failed\n"); + return NULL; + } + + state->ev = ev; + state->opts = opts; + state->be_res = be_res; + state->host_db = host_db; + state->ad_domain = ad_domain; + state->discovery_domain = discovery_domain; + + /* If possible, lookup the information in the current site first. */ + if (current_site != NULL) { + state->all_tried = false; + domain = ad_site_dns_discovery_domain(state, current_site, + discovery_domain); + if (domain == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "Out of memory!"); + ret = ENOMEM; + goto done; + } + } else { + state->all_tried = true; + domain = discovery_domain; + } + + ret = ad_cldap_ping_step(req, domain); + if (ret != EOK) { goto done; } - DEBUG(SSSDBG_TRACE_FUNC, "Found site: %s\n", state->site); - DEBUG(SSSDBG_TRACE_FUNC, "Found forest: %s\n", state->forest); + return req; done: - if (ret != EOK) { - tevent_req_error(req, ret); + tevent_req_error(req, ret); + tevent_req_post(req, ev); + + return req; +} + +static errno_t ad_cldap_ping_step(struct tevent_req *req, + const char *domain) +{ + struct ad_cldap_ping_state *state; + struct tevent_req *subreq; + struct timeval tv; + int timeout; + + state = tevent_req_data(req, struct ad_cldap_ping_state); + + subreq = ad_cldap_ping_domain_send(state, state->ev, state->opts, + state->be_res, state->host_db, + state->ad_domain, domain); + if (subreq == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "Out of memory!"); + return ENOMEM; + } + + tevent_req_set_callback(subreq, ad_cldap_ping_done, req); + + timeout = dp_opt_get_int(state->be_res->opts, + DP_RES_OPT_RESOLVER_OP_TIMEOUT); + if (timeout > 0) { + tv = tevent_timeval_current_ofs(timeout, 0); + tevent_req_set_endtime(subreq, state->ev, tv); + } + + return EOK; +} + +static void ad_cldap_ping_done(struct tevent_req *subreq) +{ + struct ad_cldap_ping_state *state; + struct tevent_req *req; + errno_t ret; + + req = tevent_req_callback_data(subreq, struct tevent_req); + state = tevent_req_data(req, struct ad_cldap_ping_state); + + ret = ad_cldap_ping_domain_recv(state, subreq, &state->site, + &state->forest); + talloc_zfree(subreq); + if (ret == EOK) { + DEBUG(SSSDBG_TRACE_FUNC, "Found site: %s\n", state->site); + DEBUG(SSSDBG_TRACE_FUNC, "Found forest: %s\n", state->forest); + tevent_req_done(req); return; } - tevent_req_done(req); + if (!state->all_tried) { + state->all_tried = true; + ret = ad_cldap_ping_step(req, state->discovery_domain); + if (ret == EOK) { + return; + } + } + + DEBUG(SSSDBG_OP_FAILURE, + "Unable to get site and forest information [%d]: %s\n", + ret, sss_strerror(ret)); + + tevent_req_error(req, ret); } errno_t ad_cldap_ping_recv(TALLOC_CTX *mem_ctx, - struct tevent_req *req, - const char **_site, - const char **_forest) + struct tevent_req *req, + const char **_site, + const char **_forest) { struct ad_cldap_ping_state *state = NULL; state = tevent_req_data(req, struct ad_cldap_ping_state); From ac74b7ba371cd97ca9f7acd46ea4c3c51c38f94b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20B=C5=99ezina?= <pbrez...@redhat.com> Date: Tue, 25 Aug 2020 12:33:59 +0200 Subject: [PATCH 5/7] man: fix typo in failover description --- src/man/include/failover.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/man/include/failover.xml b/src/man/include/failover.xml index c9d2d3799c..758270d211 100644 --- a/src/man/include/failover.xml +++ b/src/man/include/failover.xml @@ -122,7 +122,7 @@ <para> For LDAP-based providers, the resolve operation is performed as part of an LDAP connection operation. Therefore, also the - <quote>ldap_opt_timeout></quote> timeout should be set to + <quote>ldap_opt_timeout</quote> timeout should be set to a larger value than <quote>dns_resolver_timeout</quote> which in turn should be set to a larger value than <quote>dns_resolver_op_timeout</quote> which should be larger From 0d0564a87a9d20020e7ca0ffc7a183facd6e23d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20B=C5=99ezina?= <pbrez...@redhat.com> Date: Tue, 25 Aug 2020 13:43:32 +0200 Subject: [PATCH 6/7] ad: renew site information only when SSSD was previously offline Site and forest information is stable not dynamic. To avoid spamming network with cldap pings all the time we will renew netlogon information only when SSSD starts and when we are recovering from an offline state to detect possible change (e.g. user moves to another location with laptop). --- src/providers/ad/ad_cldap_ping.c | 45 ++++++++++++++++---------- src/providers/ad/ad_srv.c | 54 +++++++++++++++++++++----------- src/providers/ad/ad_srv.h | 22 ++++++++----- 3 files changed, 80 insertions(+), 41 deletions(-) diff --git a/src/providers/ad/ad_cldap_ping.c b/src/providers/ad/ad_cldap_ping.c index 7ecdcdbef0..dc25f6670e 100644 --- a/src/providers/ad/ad_cldap_ping.c +++ b/src/providers/ad/ad_cldap_ping.c @@ -586,12 +586,8 @@ static void ad_cldap_ping_done(struct tevent_req *subreq); struct tevent_req *ad_cldap_ping_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev, - struct sdap_options *opts, - struct be_resolv_ctx *be_res, - enum host_database *host_db, - const char *ad_domain, - const char *discovery_domain, - const char *current_site) + struct ad_srv_plugin_ctx *srv_ctx, + const char *discovery_domain) { struct ad_cldap_ping_state *state; struct tevent_req *req; @@ -604,17 +600,30 @@ struct tevent_req *ad_cldap_ping_send(TALLOC_CTX *mem_ctx, return NULL; } + if (!srv_ctx->renew_site) { + state->site = srv_ctx->current_site; + state->forest = srv_ctx->current_forest; + DEBUG(SSSDBG_TRACE_FUNC, + "CLDAP ping is not necessary, using site '%s' and forest '%s'\n", + state->site != NULL ? state->site : "unknown", + state->forest != NULL ? state->forest : "unknown"); + ret = EOK; + goto done; + } + + DEBUG(SSSDBG_TRACE_FUNC, "Sending CLDAP ping\n"); + state->ev = ev; - state->opts = opts; - state->be_res = be_res; - state->host_db = host_db; - state->ad_domain = ad_domain; + state->opts = srv_ctx->opts; + state->be_res = srv_ctx->be_res; + state->host_db = srv_ctx->host_dbs; + state->ad_domain = srv_ctx->ad_domain; state->discovery_domain = discovery_domain; /* If possible, lookup the information in the current site first. */ - if (current_site != NULL) { + if (srv_ctx->current_site != NULL) { state->all_tried = false; - domain = ad_site_dns_discovery_domain(state, current_site, + domain = ad_site_dns_discovery_domain(state, srv_ctx->current_site, discovery_domain); if (domain == NULL) { DEBUG(SSSDBG_CRIT_FAILURE, "Out of memory!"); @@ -634,7 +643,11 @@ struct tevent_req *ad_cldap_ping_send(TALLOC_CTX *mem_ctx, return req; done: - tevent_req_error(req, ret); + if (ret != EOK) { + tevent_req_error(req, ret); + } else { + tevent_req_done(req); + } tevent_req_post(req, ev); return req; @@ -705,9 +718,9 @@ static void ad_cldap_ping_done(struct tevent_req *subreq) } errno_t ad_cldap_ping_recv(TALLOC_CTX *mem_ctx, - struct tevent_req *req, - const char **_site, - const char **_forest) + struct tevent_req *req, + const char **_site, + const char **_forest) { struct ad_cldap_ping_state *state = NULL; state = tevent_req_data(req, struct ad_cldap_ping_state); diff --git a/src/providers/ad/ad_srv.c b/src/providers/ad/ad_srv.c index d12f0971c2..e58c19aac0 100644 --- a/src/providers/ad/ad_srv.c +++ b/src/providers/ad/ad_srv.c @@ -116,16 +116,13 @@ static errno_t ad_sort_servers_by_dns(TALLOC_CTX *mem_ctx, return EOK; } -struct ad_srv_plugin_ctx { - struct be_ctx *be_ctx; - struct be_resolv_ctx *be_res; - enum host_database *host_dbs; - struct sdap_options *opts; - const char *hostname; - const char *ad_domain; - const char *ad_site_override; - const char *current_site; -}; +static void ad_srv_mark_renew_site(void *pvt) +{ + struct ad_srv_plugin_ctx *ctx; + + ctx = talloc_get_type(pvt, struct ad_srv_plugin_ctx); + ctx->renew_site = true; +} struct ad_srv_plugin_ctx * ad_srv_plugin_ctx_init(TALLOC_CTX *mem_ctx, @@ -149,6 +146,7 @@ ad_srv_plugin_ctx_init(TALLOC_CTX *mem_ctx, ctx->be_res = be_res; ctx->host_dbs = host_dbs; ctx->opts = opts; + ctx->renew_site = true; ctx->hostname = talloc_strdup(ctx, hostname); if (ctx->hostname == NULL) { @@ -181,6 +179,12 @@ ad_srv_plugin_ctx_init(TALLOC_CTX *mem_ctx, } } + ret = be_add_offline_cb(ctx, be_ctx, ad_srv_mark_renew_site, ctx, NULL); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "be_add_offline_cb failed.\n"); + goto fail; + } + return ctx; fail: @@ -190,11 +194,26 @@ ad_srv_plugin_ctx_init(TALLOC_CTX *mem_ctx, static errno_t ad_srv_plugin_ctx_switch_site(struct ad_srv_plugin_ctx *ctx, - const char *new_site) + const char *new_site, + const char *new_forest) { const char *site; + const char *forest; errno_t ret; + /* Switch forest. */ + if (new_forest != NULL + && (ctx->current_forest == NULL + || strcmp(ctx->current_forest, new_forest) != 0)) { + forest = talloc_strdup(ctx, new_forest); + if (forest == NULL) { + return ENOMEM; + } + + talloc_zfree(ctx->current_forest); + ctx->current_forest = forest; + } + if (new_site == NULL) { return EOK; } @@ -302,12 +321,7 @@ struct tevent_req *ad_srv_plugin_send(TALLOC_CTX *mem_ctx, goto immediately; } - DEBUG(SSSDBG_TRACE_FUNC, "About to find domain controllers\n"); - - subreq = ad_cldap_ping_send(state, ev, ctx->opts, ctx->be_res, - ctx->host_dbs, ctx->ad_domain, - state->discovery_domain, - state->ctx->current_site); + subreq = ad_cldap_ping_send(state, ev, state->ctx, state->discovery_domain); if (subreq == NULL) { ret = ENOMEM; goto immediately; @@ -363,13 +377,17 @@ static void ad_srv_plugin_ping_done(struct tevent_req *subreq) /* Remember current site so it can be used during next lookup so * we can contact directory controllers within a known reachable * site first. */ - ret = ad_srv_plugin_ctx_switch_site(state->ctx, state->site); + ret = ad_srv_plugin_ctx_switch_site(state->ctx, state->site, + state->forest); if (ret != EOK) { DEBUG(SSSDBG_CRIT_FAILURE, "Unable to set site [%d]: %s\n", ret, sss_strerror(ret)); goto done; } + /* Do not renew the site again unless we go offline. */ + state->ctx->renew_site = false; + if (strcmp(state->service, "gc") == 0) { if (state->forest != NULL) { if (state->site != NULL) { diff --git a/src/providers/ad/ad_srv.h b/src/providers/ad/ad_srv.h index c03ac873fc..3c6a779ead 100644 --- a/src/providers/ad/ad_srv.h +++ b/src/providers/ad/ad_srv.h @@ -21,7 +21,19 @@ #ifndef __AD_SRV_H__ #define __AD_SRV_H__ -struct ad_srv_plugin_ctx; +struct ad_srv_plugin_ctx { + struct be_ctx *be_ctx; + struct be_resolv_ctx *be_res; + enum host_database *host_dbs; + struct sdap_options *opts; + const char *hostname; + const char *ad_domain; + const char *ad_site_override; + const char *current_site; + const char *current_forest; + + bool renew_site; +}; struct ad_srv_plugin_ctx * ad_srv_plugin_ctx_init(TALLOC_CTX *mem_ctx, @@ -55,12 +67,8 @@ char *ad_site_dns_discovery_domain(TALLOC_CTX *mem_ctx, struct tevent_req *ad_cldap_ping_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev, - struct sdap_options *opts, - struct be_resolv_ctx *be_res, - enum host_database *host_db, - const char *ad_domain, - const char *discovery_domain, - const char *current_site); + struct ad_srv_plugin_ctx *srv_ctx, + const char *discovery_domain); errno_t ad_cldap_ping_recv(TALLOC_CTX *mem_ctx, struct tevent_req *req, From 7eb509b1ff7d3d56316dc0dfb00236708cdef3af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20B=C5=99ezina?= <pbrez...@redhat.com> Date: Wed, 30 Sep 2020 13:45:43 +0200 Subject: [PATCH 7/7] tevent: correctly handle req timeout error --- src/util/util.h | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/util/util.h b/src/util/util.h index d538e0674d..d7475ee86a 100644 --- a/src/util/util.h +++ b/src/util/util.h @@ -138,13 +138,17 @@ extern int dbus_activated; \ if (tevent_req_is_error(req, &TRROEstate, &TRROEuint64)) { \ TRROEerr = (errno_t)TRROEuint64; \ - if (TRROEstate == TEVENT_REQ_USER_ERROR) { \ - if (TRROEerr == 0) { \ + switch (TRROEstate) { \ + case TEVENT_REQ_USER_ERROR: \ + if (TRROEerr == 0) { \ + return ERR_INTERNAL; \ + } \ + return TRROEerr; \ + case TEVENT_REQ_TIMED_OUT: \ + return ETIMEDOUT; \ + default: \ return ERR_INTERNAL; \ - } \ - return TRROEerr; \ } \ - return ERR_INTERNAL; \ } \ } while (0)
_______________________________________________ sssd-devel mailing list -- sssd-devel@lists.fedorahosted.org To unsubscribe send an email to sssd-devel-le...@lists.fedorahosted.org Fedora Code of Conduct: https://docs.fedoraproject.org/en-US/project/code-of-conduct/ List Guidelines: https://fedoraproject.org/wiki/Mailing_list_guidelines List Archives: https://lists.fedorahosted.org/archives/list/sssd-devel@lists.fedorahosted.org