1
0
mirror of https://github.com/samba-team/samba.git synced 2025-03-09 08:58:35 +03:00

s3:winbind: Get rid of the winbind dc-connect child

The new code uses PING_DC to tell the child to try to go online.

Pair-Programmed-With: Andreas Schneider <asn@samba.org>
Pair-Programmed-With: Stefan Metzmacher <metze@samba.org>

Signed-off-by: Andreas Schneider <asn@samba.org>
Signed-off-by: Isaac Boukris <iboukris@samba.org>
Signed-off-by: Stefan Metzmacher <metze@samba.org>
This commit is contained in:
Isaac Boukris 2020-09-17 11:12:53 +02:00 committed by Stefan Metzmacher
parent 9f63240503
commit 17c86a2c5a
8 changed files with 137 additions and 405 deletions

View File

@ -122,8 +122,7 @@ interface messaging
MSG_WINBIND_ONLINE = 0x0403,
MSG_WINBIND_OFFLINE = 0x0404,
MSG_WINBIND_ONLINESTATUS = 0x0405,
MSG_WINBIND_TRY_TO_GO_ONLINE = 0x0406,
MSG_WINBIND_FAILED_TO_GO_ONLINE = 0x0407,
MSG_WINBIND_VALIDATE_CACHE = 0x0408,
MSG_WINBIND_DUMP_DOMAIN_LIST = 0x0409,
MSG_WINBIND_IP_DROPPED = 0x040A,

View File

@ -1383,6 +1383,8 @@ static void winbindd_register_handlers(struct messaging_context *msg_ctx,
{
bool scan_trusts = true;
NTSTATUS status;
struct tevent_timer *te = NULL;
/* Setup signal handlers */
if (!winbindd_setup_sig_term_handler(true))
@ -1486,6 +1488,16 @@ static void winbindd_register_handlers(struct messaging_context *msg_ctx,
}
}
te = tevent_add_timer(global_event_context(),
NULL,
timeval_zero(),
winbindd_ping_offline_domains,
NULL);
if (te == NULL) {
DBG_ERR("Failed to schedule winbindd_ping_offline_domains()\n");
exit(1);
}
status = wb_irpc_register();
if (!NT_STATUS_IS_OK(status)) {

View File

@ -158,8 +158,8 @@ struct winbindd_domain {
void *private_data;
/* A working DC */
pid_t dc_probe_pid; /* Child we're using to detect the DC. */
char *dcname;
const char *ping_dcname;
struct sockaddr_storage dcaddr;
/* Sequence number stuff */
@ -179,10 +179,7 @@ struct winbindd_domain {
struct tevent_queue *queue;
struct dcerpc_binding_handle *binding_handle;
/* Callback we use to try put us back online. */
uint32_t check_online_timeout;
struct tevent_timer *check_online_event;
struct tevent_req *check_online_event;
/* Linked list info */

View File

@ -87,6 +87,7 @@
#include "lib/gencache.h"
#include "lib/util/string_wrappers.h"
#include "lib/global_contexts.h"
#include "librpc/gen_ndr/ndr_winbind_c.h"
#undef DBGC_CLASS
#define DBGC_CLASS DBGC_WINBIND
@ -106,256 +107,6 @@ static bool get_dcs(TALLOC_CTX *mem_ctx, struct winbindd_domain *domain,
struct dc_name_ip **dcs, int *num_dcs,
uint32_t request_flags);
/****************************************************************
Child failed to find DC's. Reschedule check.
****************************************************************/
static void msg_failed_to_go_online(struct messaging_context *msg,
void *private_data,
uint32_t msg_type,
struct server_id server_id,
DATA_BLOB *data)
{
struct winbindd_domain *domain;
const char *domainname = (const char *)data->data;
if (data->data == NULL || data->length == 0) {
return;
}
DEBUG(5,("msg_fail_to_go_online: received for domain %s.\n", domainname));
for (domain = domain_list(); domain; domain = domain->next) {
if (domain->internal) {
continue;
}
if (strequal(domain->name, domainname)) {
if (domain->online) {
/* We're already online, ignore. */
DEBUG(5,("msg_fail_to_go_online: domain %s "
"already online.\n", domainname));
continue;
}
/* Reschedule the online check. */
set_domain_offline(domain);
break;
}
}
}
/****************************************************************
Actually cause a reconnect from a message.
****************************************************************/
static void msg_try_to_go_online(struct messaging_context *msg,
void *private_data,
uint32_t msg_type,
struct server_id server_id,
DATA_BLOB *data)
{
struct winbindd_domain *domain;
const char *domainname = (const char *)data->data;
if (data->data == NULL || data->length == 0) {
return;
}
DEBUG(5,("msg_try_to_go_online: received for domain %s.\n", domainname));
for (domain = domain_list(); domain; domain = domain->next) {
if (domain->internal) {
continue;
}
if (strequal(domain->name, domainname)) {
if (domain->online) {
/* We're already online, ignore. */
DEBUG(5,("msg_try_to_go_online: domain %s "
"already online.\n", domainname));
continue;
}
/* This call takes care of setting the online
flag to true if we connected, or re-adding
the offline handler if false. Bypasses online
check so always does network calls. */
init_dc_connection_network(domain, true);
break;
}
}
}
/****************************************************************
Fork a child to try and contact a DC. Do this as contacting a
DC requires blocking lookups and we don't want to block our
parent.
****************************************************************/
static bool fork_child_dc_connect(struct winbindd_domain *domain)
{
struct dc_name_ip *dcs = NULL;
int num_dcs = 0;
TALLOC_CTX *mem_ctx = NULL;
pid_t parent_pid = getpid();
char *lfile = NULL;
NTSTATUS status;
bool ok;
if (domain->dc_probe_pid != (pid_t)-1) {
/*
* We might already have a DC probe
* child working, check.
*/
if (process_exists_by_pid(domain->dc_probe_pid)) {
DEBUG(10,("fork_child_dc_connect: pid %u already "
"checking for DC's.\n",
(unsigned int)domain->dc_probe_pid));
return true;
}
domain->dc_probe_pid = (pid_t)-1;
}
domain->dc_probe_pid = fork();
if (domain->dc_probe_pid == (pid_t)-1) {
DEBUG(0, ("fork_child_dc_connect: Could not fork: %s\n", strerror(errno)));
return False;
}
if (domain->dc_probe_pid != (pid_t)0) {
/* Parent */
messaging_register(global_messaging_context(), NULL,
MSG_WINBIND_TRY_TO_GO_ONLINE,
msg_try_to_go_online);
messaging_register(global_messaging_context(), NULL,
MSG_WINBIND_FAILED_TO_GO_ONLINE,
msg_failed_to_go_online);
return True;
}
/* Child. */
/* Leave messages blocked - we will never process one. */
if (!override_logfile) {
if (asprintf(&lfile, "%s/log.winbindd-dc-connect", get_dyn_LOGFILEBASE()) == -1) {
DBG_ERR("fork_child_dc_connect: "
"out of memory in asprintf().\n");
_exit(1);
}
}
status = winbindd_reinit_after_fork(NULL, lfile);
if (!NT_STATUS_IS_OK(status)) {
DEBUG(1, ("winbindd_reinit_after_fork failed: %s\n",
nt_errstr(status)));
messaging_send_buf(global_messaging_context(),
pid_to_procid(parent_pid),
MSG_WINBIND_FAILED_TO_GO_ONLINE,
(const uint8_t *)domain->name,
strlen(domain->name)+1);
_exit(1);
}
SAFE_FREE(lfile);
setproctitle("dc-connect child");
mem_ctx = talloc_init("fork_child_dc_connect");
if (!mem_ctx) {
DEBUG(0,("talloc_init failed.\n"));
messaging_send_buf(global_messaging_context(),
pid_to_procid(parent_pid),
MSG_WINBIND_FAILED_TO_GO_ONLINE,
(const uint8_t *)domain->name,
strlen(domain->name)+1);
_exit(1);
}
ok = get_dcs(mem_ctx, domain, &dcs, &num_dcs, 0);
TALLOC_FREE(mem_ctx);
if (!ok || (num_dcs == 0)) {
/* Still offline ? Can't find DC's. */
messaging_send_buf(global_messaging_context(),
pid_to_procid(parent_pid),
MSG_WINBIND_FAILED_TO_GO_ONLINE,
(const uint8_t *)domain->name,
strlen(domain->name)+1);
_exit(0);
}
/* We got a DC. Send a message to our parent to get it to
try and do the same. */
messaging_send_buf(global_messaging_context(),
pid_to_procid(parent_pid),
MSG_WINBIND_TRY_TO_GO_ONLINE,
(const uint8_t *)domain->name,
strlen(domain->name)+1);
_exit(0);
}
/****************************************************************
Handler triggered if we're offline to try and detect a DC.
****************************************************************/
static void check_domain_online_handler(struct tevent_context *ctx,
struct tevent_timer *te,
struct timeval now,
void *private_data)
{
struct winbindd_domain *domain =
(struct winbindd_domain *)private_data;
DEBUG(10,("check_domain_online_handler: called for domain "
"%s (online = %s)\n", domain->name,
domain->online ? "True" : "False" ));
TALLOC_FREE(domain->check_online_event);
/* Are we still in "startup" mode ? */
if (domain->startup && (time_mono(NULL) > domain->startup_time + 30)) {
/* No longer in "startup" mode. */
DEBUG(10,("check_domain_online_handler: domain %s no longer in 'startup' mode.\n",
domain->name ));
domain->startup = False;
}
/* We've been told to stay offline, so stay
that way. */
if (get_global_winbindd_state_offline()) {
DEBUG(10,("check_domain_online_handler: domain %s remaining globally offline\n",
domain->name ));
return;
}
/* Fork a child to test if it can contact a DC.
If it can then send ourselves a message to
cause a reconnect. */
fork_child_dc_connect(domain);
}
/****************************************************************
If we're still offline setup the timeout check.
****************************************************************/
static void calc_new_online_timeout_check(struct winbindd_domain *domain)
{
int wbr = lp_winbind_reconnect_delay();
if (domain->startup) {
domain->check_online_timeout = 10;
} else if (domain->check_online_timeout < wbr) {
domain->check_online_timeout = wbr;
}
}
void winbind_msg_domain_offline(struct messaging_context *msg_ctx,
void *private_data,
uint32_t msg_type,
@ -367,13 +118,15 @@ void winbind_msg_domain_offline(struct messaging_context *msg_ctx,
domain = find_domain_from_name_noinit(domain_name);
if (domain == NULL) {
DBG_DEBUG("Domain %s not found!\n", domain_name);
return;
}
domain->online = false;
DBG_DEBUG("Domain %s was %s, change to offline now.\n",
domain_name,
domain->online ? "online" : "offline");
DEBUG(10, ("Domain %s is marked as offline now.\n",
domain_name));
domain->online = false;
}
void winbind_msg_domain_online(struct messaging_context *msg_ctx,
@ -390,10 +143,13 @@ void winbind_msg_domain_online(struct messaging_context *msg_ctx,
return;
}
domain->online = true;
SMB_ASSERT(wb_child_domain() == NULL);
DEBUG(10, ("Domain %s is marked as online now.\n",
domain_name));
DBG_DEBUG("Domain %s was %s, marking as online now!\n",
domain_name,
domain->online ? "online" : "offline");
domain->online = true;
}
/****************************************************************
@ -408,8 +164,6 @@ void set_domain_offline(struct winbindd_domain *domain)
DEBUG(10,("set_domain_offline: called for domain %s\n",
domain->name ));
TALLOC_FREE(domain->check_online_event);
if (domain->internal) {
DEBUG(3,("set_domain_offline: domain %s is internal - logic error.\n",
domain->name ));
@ -423,35 +177,6 @@ void set_domain_offline(struct winbindd_domain *domain)
domain->initialized = True;
/* We only add the timeout handler that checks and
allows us to go back online when we've not
been told to remain offline. */
if (get_global_winbindd_state_offline()) {
DEBUG(10,("set_domain_offline: domain %s remaining globally offline\n",
domain->name ));
return;
}
/* If we're in startup mode, check again in 10 seconds, not in
lp_winbind_reconnect_delay() seconds (which is 30 seconds by default). */
calc_new_online_timeout_check(domain);
domain->check_online_event = tevent_add_timer(global_event_context(),
NULL,
timeval_current_ofs(domain->check_online_timeout,0),
check_domain_online_handler,
domain);
/* The above *has* to succeed for winbindd to work. */
if (!domain->check_online_event) {
smb_panic("set_domain_offline: failed to add online handler");
}
DEBUG(10,("set_domain_offline: added event handler for domain %s\n",
domain->name ));
/* Send a message to the parent that the domain is offline. */
if (parent_pid > 1 && !domain->internal) {
messaging_send_buf(global_messaging_context(),
@ -463,7 +188,6 @@ void set_domain_offline(struct winbindd_domain *domain)
/* Send an offline message to the idmap child when our
primary domain goes offline */
if ( domain->primary ) {
pid_t idmap_pid = idmap_child_pid();
@ -526,16 +250,6 @@ static void set_domain_online(struct winbindd_domain *domain)
}
}
/* Ensure we have no online timeout checks. */
domain->check_online_timeout = 0;
TALLOC_FREE(domain->check_online_event);
/* Ensure we ignore any pending child messages. */
messaging_deregister(global_messaging_context(),
MSG_WINBIND_TRY_TO_GO_ONLINE, NULL);
messaging_deregister(global_messaging_context(),
MSG_WINBIND_FAILED_TO_GO_ONLINE, NULL);
domain->online = True;
/* Send a message to the parent that the domain is online. */
@ -571,7 +285,9 @@ static void set_domain_online(struct winbindd_domain *domain)
void set_domain_online_request(struct winbindd_domain *domain)
{
struct timeval tev;
NTSTATUS status;
SMB_ASSERT(wb_child_domain() || idmap_child());
DEBUG(10,("set_domain_online_request: called for domain %s\n",
domain->name ));
@ -588,42 +304,17 @@ void set_domain_online_request(struct winbindd_domain *domain)
return;
}
/* We've been told it's safe to go online and
try and connect to a DC. But I don't believe it
because network manager seems to lie.
Wait at least 5 seconds. Heuristics suck... */
GetTimeOfDay(&tev);
/* Go into "startup" mode again. */
domain->startup_time = time_mono(NULL);
domain->startup = True;
tev.tv_sec += 5;
if (!domain->check_online_event) {
/* If we've come from being globally offline we
don't have a check online event handler set.
We need to add one now we're trying to go
back online. */
DEBUG(10,("set_domain_online_request: domain %s was globally offline.\n",
domain->name ));
}
TALLOC_FREE(domain->check_online_event);
domain->check_online_event = tevent_add_timer(global_event_context(),
NULL,
tev,
check_domain_online_handler,
domain);
/* The above *has* to succeed for winbindd to work. */
if (!domain->check_online_event) {
smb_panic("set_domain_online_request: failed to add online handler");
}
/*
* This call takes care of setting the online flag to true if we
* connected, or tell the parent to ping us back if false. Bypasses
* online check so always does network calls.
*/
status = init_dc_connection_network(domain, true);
DBG_DEBUG("init_dc_connection_network(), returned %s, called for "
"domain %s (online = %s)\n",
nt_errstr(status),
domain->name,
domain->online ? "true" : "false");
}
/****************************************************************
@ -2242,10 +1933,7 @@ NTSTATUS init_dc_connection(struct winbindd_domain *domain, bool need_rw_dc)
return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
}
if (domain->initialized && !domain->online) {
/* We check for online status elsewhere. */
return NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND;
}
SMB_ASSERT(wb_child_domain() || idmap_child());
return init_dc_connection_network(domain, need_rw_dc);
}

View File

@ -1026,7 +1026,7 @@ void winbind_msg_offline(struct messaging_context *msg_ctx,
continue;
}
DEBUG(5,("winbind_msg_offline: marking %s offline.\n", domain->name));
set_domain_offline(domain);
domain->online = false;
}
forall_domain_children(winbind_msg_on_offline_fn, &state);
@ -1044,7 +1044,6 @@ void winbind_msg_online(struct messaging_context *msg_ctx,
.msg_ctx = msg_ctx,
.msg_type = MSG_WINBIND_ONLINE,
};
struct winbindd_domain *domain;
DEBUG(10,("winbind_msg_online: got online message.\n"));
@ -1059,32 +1058,7 @@ void winbind_msg_online(struct messaging_context *msg_ctx,
smb_nscd_flush_user_cache();
smb_nscd_flush_group_cache();
/* Set all our domains as online. */
for (domain = domain_list(); domain; domain = domain->next) {
if (domain->internal) {
continue;
}
DEBUG(5,("winbind_msg_online: requesting %s to go online.\n", domain->name));
winbindd_flush_negative_conn_cache(domain);
set_domain_online_request(domain);
/* Send an online message to the idmap child when our
primary domain comes back online */
if ( domain->primary ) {
pid_t idmap_pid = idmap_child_pid();
if (idmap_pid != 0) {
messaging_send_buf(msg_ctx,
pid_to_procid(idmap_pid),
MSG_WINBIND_ONLINE,
(const uint8_t *)domain->name,
strlen(domain->name)+1);
}
}
}
/* Tell all our child domains to go online online. */
forall_domain_children(winbind_msg_on_offline_fn, &state);
}
@ -1832,8 +1806,7 @@ static bool fork_domain_child(struct winbindd_child *child)
}
/*
* We are in idmap child, make sure that we set the
* check_online_event to bring primary domain online.
* We are in idmap child, bring primary domain online.
*/
if (is_idmap_child(child)) {
set_domain_online_request(primary_domain);

View File

@ -153,7 +153,6 @@ void ndr_print_winbindd_domain(struct ndr_print *ndr,
for (i=0; i<lp_winbind_max_domain_connections(); i++) {
ndr_print_winbindd_child(ndr, "children", &r->children[i]);
}
ndr_print_uint32(ndr, "check_online_timeout", r->check_online_timeout);
ndr_print_ptr(ndr, "check_online_event", r->check_online_event);
ndr->depth--;
}

View File

@ -479,6 +479,10 @@ bool add_trusted_domain_from_auth(uint16_t validation_level,
bool domain_is_forest_root(const struct winbindd_domain *domain);
void rescan_trusted_domains(struct tevent_context *ev, struct tevent_timer *te,
struct timeval now, void *private_data);
void winbindd_ping_offline_domains(struct tevent_context *ev,
struct tevent_timer *te,
struct timeval now,
void *private_data);
enum winbindd_result winbindd_dual_init_connection(struct winbindd_domain *domain,
struct winbindd_cli_state *state);
bool init_domain_list(void);

View File

@ -36,6 +36,7 @@
#include "lib/util/smb_strtox.h"
#include "lib/util/string_wrappers.h"
#include "lib/global_contexts.h"
#include "librpc/gen_ndr/ndr_winbind_c.h"
#undef DBGC_CLASS
#define DBGC_CLASS DBGC_WINBIND
@ -251,8 +252,6 @@ static NTSTATUS add_trusted_domain(const char *domain_name,
domain->last_seq_check = 0;
domain->initialized = false;
domain->online = is_internal_domain(sid);
domain->check_online_timeout = 0;
domain->dc_probe_pid = (pid_t)-1;
domain->domain_flags = trust_flags;
domain->domain_type = trust_type;
domain->domain_trust_attribs = trust_attribs;
@ -798,6 +797,93 @@ void rescan_trusted_domains(struct tevent_context *ev, struct tevent_timer *te,
return;
}
static void wbd_ping_dc_done(struct tevent_req *subreq);
void winbindd_ping_offline_domains(struct tevent_context *ev,
struct tevent_timer *te,
struct timeval now,
void *private_data)
{
struct winbindd_domain *domain = NULL;
TALLOC_FREE(te);
for (domain = domain_list(); domain != NULL; domain = domain->next) {
DBG_DEBUG("Domain %s is %s\n",
domain->name,
domain->online ? "online" : "offline");
if (get_global_winbindd_state_offline()) {
DBG_DEBUG("We are globally offline, do nothing.\n");
break;
}
if (domain->online ||
domain->check_online_event != NULL ||
domain->secure_channel_type == SEC_CHAN_NULL) {
continue;
}
winbindd_flush_negative_conn_cache(domain);
domain->check_online_event =
dcerpc_wbint_PingDc_send(domain,
ev,
dom_child_handle(domain),
&domain->ping_dcname);
if (domain->check_online_event == NULL) {
DBG_WARNING("Failed to schedule ping, no-memory\n");
continue;
}
tevent_req_set_callback(domain->check_online_event,
wbd_ping_dc_done, domain);
}
te = tevent_add_timer(ev,
NULL,
timeval_current_ofs(lp_winbind_reconnect_delay(),
0),
winbindd_ping_offline_domains,
NULL);
if (te == NULL) {
DBG_ERR("Failed to schedule winbindd_ping_offline_domains()\n");
}
return;
}
static void wbd_ping_dc_done(struct tevent_req *subreq)
{
struct winbindd_domain *domain =
tevent_req_callback_data(subreq,
struct winbindd_domain);
NTSTATUS status, result;
SMB_ASSERT(subreq == domain->check_online_event);
domain->check_online_event = NULL;
status = dcerpc_wbint_PingDc_recv(subreq, domain, &result);
TALLOC_FREE(subreq);
if (any_nt_status_not_ok(status, result, &status)) {
DBG_WARNING("dcerpc_wbint_PingDc_recv failed for domain: "
"%s - %s\n",
domain->name,
nt_errstr(status));
return;
}
DBG_DEBUG("dcerpc_wbint_PingDc_recv() succeeded, "
"domain: %s, dc-name: %s\n",
domain->name,
domain->ping_dcname);
talloc_free(discard_const(domain->ping_dcname));
domain->ping_dcname = NULL;
return;
}
enum winbindd_result winbindd_dual_init_connection(struct winbindd_domain *domain,
struct winbindd_cli_state *state)
{
@ -941,14 +1027,6 @@ static bool add_trusted_domains_dc(void)
nt_errstr(status));
return false;
}
/* Even in the parent winbindd we'll need to
talk to the DC, so try and see if we can
contact it. Theoretically this isn't necessary
as the init_dc_connection() in init_child_recv()
will do this, but we can start detecting the DC
early here. */
set_domain_online_request(domain);
}
return true;
@ -1014,16 +1092,6 @@ static bool add_trusted_domains_dc(void)
}
domain->domain_type = domains[i]->trust_type;
domain->domain_trust_attribs = domains[i]->trust_attributes;
if (sec_chan_type != SEC_CHAN_NULL) {
/* Even in the parent winbindd we'll need to
talk to the DC, so try and see if we can
contact it. Theoretically this isn't necessary
as the init_dc_connection() in init_child_recv()
will do this, but we can start detecting the DC
early here. */
set_domain_online_request(domain);
}
}
for (i = 0; i < num_domains; i++) {
@ -1308,14 +1376,6 @@ bool init_domain_list(void)
"domain to winbindd's internal list\n");
return false;
}
/* Even in the parent winbindd we'll need to
talk to the DC, so try and see if we can
contact it. Theoretically this isn't necessary
as the init_dc_connection() in init_child_recv()
will do this, but we can start detecting the DC
early here. */
set_domain_online_request(domain);
}
status = imessaging_register(winbind_imessaging_context(), NULL,