From: Bo Yang Date: Sat, 10 Jan 2009 22:32:43 +0000 (-0800) Subject: Backport of the clean event context after fork and X-Git-Url: http://git.samba.org/?p=samba.git;a=commitdiff_plain;h=194425f8074e2cfd5893499099614666f8d8ecd9 Backport of the clean event context after fork and krb5 refresh chain fixes. --- diff --git a/source/lib/events.c b/source/lib/events.c index a00db77b6be..43ca8df6209 100644 --- a/source/lib/events.c +++ b/source/lib/events.c @@ -297,39 +297,34 @@ struct timeval *get_timed_events_timeout(struct event_context *event_ctx, return to_ret; } -struct event_context *event_context_init(TALLOC_CTX *mem_ctx) -{ - return TALLOC_ZERO_P(NULL, struct event_context); -} - -int set_event_dispatch_time(struct event_context *event_ctx, - const char *event_name, struct timeval when) +static int event_context_destructor(struct event_context *ev) { - struct timed_event *te; - - for (te = event_ctx->timed_events; te; te = te->next) { - if (strcmp(event_name, te->event_name) == 0) { - DLIST_REMOVE(event_ctx->timed_events, te); - te->when = when; - add_event_by_time(te); - return 1; - } + while (ev->fd_events != NULL) { + ev->fd_events->event_ctx = NULL; + DLIST_REMOVE(ev->fd_events, ev->fd_events); + } + while (ev->timed_events != NULL) { + ev->timed_events->event_ctx = NULL; + DLIST_REMOVE(ev->timed_events, ev->timed_events); } return 0; } -/* Returns 1 if event was found and cancelled, 0 otherwise. */ +void event_context_reinit(struct event_context *ev) +{ + event_context_destructor(ev); + return; +} -int cancel_named_event(struct event_context *event_ctx, - const char *event_name) +struct event_context *event_context_init(TALLOC_CTX *mem_ctx) { - struct timed_event *te; + struct event_context *result; - for (te = event_ctx->timed_events; te; te = te->next) { - if (strcmp(event_name, te->event_name) == 0) { - TALLOC_FREE(te); - return 1; - } + result = TALLOC_ZERO_P(mem_ctx, struct event_context); + if (result == NULL) { + return NULL; } - return 0; + + talloc_set_destructor(result, event_context_destructor); + return result; } diff --git a/source/lib/util.c b/source/lib/util.c index 3c9233d9b34..f46aaafc219 100644 --- a/source/lib/util.c +++ b/source/lib/util.c @@ -3309,3 +3309,16 @@ void *talloc_zeronull(const void *context, size_t size, const char *name) } return talloc_named_const(context, size, name); } + +bool reinit_after_fork(struct messaging_context *msg_ctx, + struct event_context *ev_ctx, + bool parent_longlived) +{ + set_need_random_reseed(); + if (tdb_reopen_all(parent_longlived ? 1 : 0) == -1) { + DEBUG(0, ("tdb_reopen_all failed.\n")); + return false; + } + event_context_reinit(ev_ctx); + return true; +} diff --git a/source/nsswitch/winbindd_cm.c b/source/nsswitch/winbindd_cm.c index 8d303b3d4ac..1fa40caf6dd 100644 --- a/source/nsswitch/winbindd_cm.c +++ b/source/nsswitch/winbindd_cm.c @@ -161,6 +161,8 @@ static void msg_try_to_go_online(int msg_type, struct process_id src, parent. ****************************************************************/ +bool winbindd_reinit_after_fork(const char *logfile); + static BOOL fork_child_dc_connect(struct winbindd_domain *domain) { struct dc_name_ip *dcs = NULL; @@ -168,6 +170,7 @@ static BOOL fork_child_dc_connect(struct winbindd_domain *domain) TALLOC_CTX *mem_ctx = NULL; pid_t child_pid; pid_t parent_pid = sys_getpid(); + pstring logfile; /* Stop zombies */ CatchChild(); @@ -197,18 +200,15 @@ static BOOL fork_child_dc_connect(struct winbindd_domain *domain) /* Leave messages blocked - we will never process one. */ /* tdb needs special fork handling */ - if (tdb_reopen_all(1) == -1) { - DEBUG(0,("tdb_reopen_all failed.\n")); - _exit(0); - } - - close_conns_after_fork(); - if (!override_logfile) { - pstring logfile; pstr_sprintf(logfile, "%s/log.winbindd-dc-connect", dyn_LOGFILEBASE); - lp_set_logfile(logfile); - reopen_logs(); + } + if (!winbindd_reinit_after_fork(logfile)) { + DEBUG(0,("winbindd_reinit_after_fork failed.\n")); + message_send_pid(pid_to_procid(parent_pid), MSG_WINBIND_FAILED_TO_GO_ONLINE, + domain->name, + strlen(domain->name)+1, False); + _exit(0); } mem_ctx = talloc_init("fork_child_dc_connect"); @@ -351,10 +351,10 @@ void set_domain_offline(struct winbindd_domain *domain) Set domain online - if allowed. ****************************************************************/ +void ccache_regain_all_now(void); + static void set_domain_online(struct winbindd_domain *domain) { - struct timeval now; - DEBUG(10,("set_domain_online: called for domain %s\n", domain->name )); @@ -371,9 +371,7 @@ static void set_domain_online(struct winbindd_domain *domain) } /* If we are waiting to get a krb5 ticket, trigger immediately. */ - GetTimeOfDay(&now); - set_event_dispatch_time(winbind_event_context(), - "krb5_ticket_gain_handler", now); + ccache_regain_all_now(); /* Ok, we're out of any startup mode now... */ domain->startup = False; @@ -426,6 +424,13 @@ void set_domain_online_request(struct winbindd_domain *domain) 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 = tev.tv_sec; + domain->startup = True; + + tev.tv_sec += 5; if (!domain->check_online_event) { /* If we've come from being globally offline we @@ -436,28 +441,20 @@ void set_domain_online_request(struct winbindd_domain *domain) DEBUG(10,("set_domain_online_request: domain %s was globally offline.\n", domain->name )); - domain->check_online_event = event_add_timed(winbind_event_context(), - NULL, - timeval_current_ofs(5, 0), - "check_domain_online_handler", - 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.\n"); - } } + TALLOC_FREE(domain->check_online_event); + + domain->check_online_event = event_add_timed(winbind_event_context(), + NULL, + tev, + "check_domain_online_handler", + check_domain_online_handler, + domain); - GetTimeOfDay(&tev); - - /* Go into "startup" mode again. */ - domain->startup_time = tev.tv_sec; - domain->startup = True; - - tev.tv_sec += 5; - - set_event_dispatch_time(winbind_event_context(), "check_domain_online_handler", tev); + /* 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.\n"); + } } /**************************************************************** diff --git a/source/nsswitch/winbindd_cred_cache.c b/source/nsswitch/winbindd_cred_cache.c index e47858b628c..a0f72379d17 100644 --- a/source/nsswitch/winbindd_cred_cache.c +++ b/source/nsswitch/winbindd_cred_cache.c @@ -35,6 +35,8 @@ #define MAX_CCACHES 100 static struct WINBINDD_CCACHE_ENTRY *ccache_list; +static void add_krb5_ticket_gain_handler_event(struct WINBINDD_CCACHE_ENTRY *entry, + struct timeval t); /* The Krb5 ticket refresh handler should be scheduled at one-half of the period from now till the tkt @@ -72,6 +74,71 @@ static int ccache_entry_count(void) return i; } +void ccache_remove_all_after_fork(void) +{ + struct WINBINDD_CCACHE_ENTRY *cur; + cur = ccache_list; + while (cur) { + DLIST_REMOVE(ccache_list, cur); + TALLOC_FREE(cur->event); + TALLOC_FREE(cur); + cur = ccache_list; + } +} + +static void krb5_ticket_gain_handler(struct event_context *event_ctx, + struct timed_event *te, + const struct timeval *now, + void *private_data); +static void krb5_ticket_refresh_handler(struct event_context *event_ctx, + struct timed_event *te, + const struct timeval *now, + void *private_data); + +void ccache_regain_all_now(void) +{ + struct WINBINDD_CCACHE_ENTRY *cur; + struct timeval t = timeval_current(); + + cur = ccache_list; + while (cur) { + TALLOC_FREE(cur->event); + if (cur->refresh_time) { + cur->event = event_add_timed(winbind_event_context(), + cur, t, + "krb5_ticket_refresh_handler", + krb5_ticket_refresh_handler, + cur); + } else { + cur->event = event_add_timed(winbind_event_context(), + cur, t, + "krb5_ticket_gain_handler", + krb5_ticket_gain_handler, + cur); + } + if (!cur->event) { + DEBUG(0, ("ccache_regain_all_now: out of memory!!\n")); + } + } + return; +} + +/**************************************************************** + The gain initial ticket is recognized as entry->refresh_time is + always zero. +****************************************************************/ + +static void add_krb5_ticket_gain_handler_event(struct WINBINDD_CCACHE_ENTRY *entry, + struct timeval t) +{ + entry->refresh_time = 0; + entry->event = event_add_timed(winbind_event_context(), entry, + t, + "krb5_ticket_gain_handler", + krb5_ticket_gain_handler, + entry); +} + /**************************************************************** Do the work of refreshing the ticket. ****************************************************************/ @@ -86,6 +153,7 @@ static void krb5_ticket_refresh_handler(struct event_context *event_ctx, #ifdef HAVE_KRB5 int ret; time_t new_start; + time_t expire_time = 0; struct WINBINDD_MEMORY_CREDS *cred_ptr = entry->cred_ptr; #endif @@ -97,44 +165,82 @@ static void krb5_ticket_refresh_handler(struct event_context *event_ctx, #ifdef HAVE_KRB5 /* Kinit again if we have the user password and we can't renew the old - * tgt anymore */ - - if ((entry->renew_until < time(NULL)) && cred_ptr && cred_ptr->pass) { - - set_effective_uid(entry->uid); - - ret = kerberos_kinit_password_ext(entry->principal_name, - cred_ptr->pass, - 0, /* hm, can we do time correction here ? */ - &entry->refresh_time, - &entry->renew_until, - entry->ccname, - False, /* no PAC required anymore */ - True, - WINBINDD_PAM_AUTH_KRB5_RENEW_TIME); - gain_root_privilege(); - - if (ret) { - DEBUG(3,("krb5_ticket_refresh_handler: could not re-kinit: %s\n", - error_message(ret))); - TALLOC_FREE(entry->event); - return; - } - - DEBUG(10,("krb5_ticket_refresh_handler: successful re-kinit " - "for: %s in ccache: %s\n", - entry->principal_name, entry->ccname)); - + * tgt anymore + * NB + * This happens when machine are put to sleep for a very long time. */ + + if (entry->renew_until < time(NULL)) { +rekinit: + if (cred_ptr && cred_ptr->pass) { + + set_effective_uid(entry->uid); + + ret = kerberos_kinit_password_ext(entry->principal_name, + cred_ptr->pass, + 0, /* hm, can we do time correction here ? */ + &entry->refresh_time, + &entry->renew_until, + entry->ccname, + False, /* no PAC required anymore */ + True, + WINBINDD_PAM_AUTH_KRB5_RENEW_TIME); + gain_root_privilege(); + + if (ret) { + DEBUG(3,("krb5_ticket_refresh_handler: " + "could not re-kinit: %s\n", + error_message(ret))); + /* destroy the ticket because we cannot rekinit + * it, ignore error here */ + ads_kdestroy(entry->ccname); + + /* Don't break the ticket refresh chain: retry + * refreshing ticket sometime later when KDC is + * unreachable -- BoYang. + * More error handling here? KRB5_CC_IO, + * KRB5KRB_AP_ERR_SKEW. + * */ + + if ((ret == KRB5_KDC_UNREACH) + || (ret == KRB5_REALM_CANT_RESOLVE)) { #if defined(DEBUG_KRB5_TKT_RENEWAL) - new_start = time(NULL) + 30; + new_start = time(NULL) + 30; #else - /* The tkt should be refreshed at one-half the period - from now to the expiration time */ - new_start = KRB5_EVENT_REFRESH_TIME(entry->refresh_time); + new_start = time(NULL) + + MAX(30, lp_winbind_cache_time()); #endif - - goto done; - } + /* try to regain ticket here */ + add_krb5_ticket_gain_handler_event(entry, + timeval_set(new_start, 0)); + return; + } + TALLOC_FREE(entry->event); + return; + } + + DEBUG(10,("krb5_ticket_refresh_handler: successful re-kinit " + "for: %s in ccache: %s\n", + entry->principal_name, entry->ccname)); + +#if defined(DEBUG_KRB5_TKT_RENEWAL) + new_start = time(NULL) + 30; +#else + /* The tkt should be refreshed at one-half the period + from now to the expiration time */ + expire_time = entry->refresh_time; + new_start = KRB5_EVENT_REFRESH_TIME(entry->refresh_time); +#endif + goto done; + } else { + /* can this happen? + * No cached credentials + * destroy ticket and refresh chain + * */ + ads_kdestroy(entry->ccname); + TALLOC_FREE(entry->event); + return; + } + } set_effective_uid(entry->uid); @@ -145,6 +251,7 @@ static void krb5_ticket_refresh_handler(struct event_context *event_ctx, #if defined(DEBUG_KRB5_TKT_RENEWAL) new_start = time(NULL) + 30; #else + expire_time = new_start; new_start = KRB5_EVENT_REFRESH_TIME(new_start); #endif @@ -155,19 +262,64 @@ static void krb5_ticket_refresh_handler(struct event_context *event_ctx, error_message(ret))); /* maybe we are beyond the renewing window */ + /* evil rises here, we refresh ticket failed, + * but the ticket might be expired. Therefore, + * When we refresh ticket failed, destory the + * ticket */ + + ads_kdestroy(entry->ccname); + /* avoid breaking the renewal chain: retry in lp_winbind_cache_time() - * seconds when the KDC was not available right now. */ - - if (ret == KRB5_KDC_UNREACH) { - new_start = time(NULL) + MAX(30, lp_winbind_cache_time()); - goto done; - } + * seconds when the KDC was not available right now. + * the return code can be KRB5_REALM_CANT_RESOLVE + * More error handling here? KRB5_CC_IO, KRB5KRB_AP_ERR_SKEW. */ + + if ((ret == KRB5_KDC_UNREACH) + || (ret == KRB5_REALM_CANT_RESOLVE)) { +#if defined(DEBUG_KRB5_TKT_RENEWAL) + new_start = time(NULL) + 30; +#else + new_start = time(NULL) + + MAX(30, lp_winbind_cache_time()); +#endif + /* ticket is destroyed here, we have to regain it + * if it is possible */ + add_krb5_ticket_gain_handler_event(entry, timeval_set(new_start, 0)); + return; + } + /* This is evil, if the ticket was already expired. + * renew ticket function returns KRB5KRB_AP_ERR_TKT_EXPIRED. + * But there is still a chance that we can rekinit it. + * + * This happens when user login in online mode, and then network + * down or something cause winbind goes offline for a very long time, + * and then goes online again. ticket expired, renew failed. + * This happens when machine are put to sleep for a long time, + * but shorter than entry->renew_util. + * NB + * Looks like the KDC is reachable, we want to rekinit as soon as + * possible instead of waiting some time later. */ + if ((ret == KRB5KRB_AP_ERR_TKT_EXPIRED) + || (ret == KRB5_FCC_NOFILE)) goto rekinit; + return; } done: - + /* in cases that ticket will be unrenewable soon, we don't try to renew ticket + * but try to regain ticket if it is possible */ + if (entry->renew_until && expire_time + && (entry->renew_until <= expire_time)) { + /* try to regain ticket 10 seconds beforre expiration */ + expire_time -= 10; + add_krb5_ticket_gain_handler_event(entry, timeval_set(expire_time, 0)); + return; + } + + if (!entry->refresh_time) { + entry->refresh_time = new_start; + } entry->event = event_add_timed(winbind_event_context(), entry, timeval_set(new_start, 0), "krb5_ticket_refresh_handler", @@ -230,6 +382,9 @@ static void krb5_ticket_gain_handler(struct event_context *event_ctx, if (ret) { DEBUG(3,("krb5_ticket_gain_handler: could not kinit: %s\n", error_message(ret))); + /* evil. If we cannot do it, destroy any the __maybe__ + * __existing__ ticket */ + ads_kdestroy(entry->ccname); goto retry_later; } @@ -240,12 +395,12 @@ static void krb5_ticket_gain_handler(struct event_context *event_ctx, } retry_later: - - entry->event = event_add_timed(winbind_event_context(), entry, - timeval_current_ofs(MAX(30, lp_winbind_cache_time()), 0), - "krb5_ticket_gain_handler", - krb5_ticket_gain_handler, - entry); +#if defined(DEBUG_KRB5_TKT_REGAIN) + t = timeval_set(time(NULL) + 30, 0); +#else + t = timeval_current_ofs(MAX(30, lp_winbind_cache_time()), 0); +#endif + add_krb5_ticket_gain_handler_event(entry, t); return; @@ -256,7 +411,10 @@ static void krb5_ticket_gain_handler(struct event_context *event_ctx, #else t = timeval_set(KRB5_EVENT_REFRESH_TIME(entry->refresh_time), 0); #endif - + + if (!entry->refresh_time) { + entry->refresh_time = t.tv_sec; + } entry->event = event_add_timed(winbind_event_context(), entry, t, "krb5_ticket_refresh_handler", @@ -314,6 +472,10 @@ NTSTATUS add_ccache_to_list(const char *princ_name, BOOL postponed_request) { struct WINBINDD_CCACHE_ENTRY *entry = NULL; + NTSTATUS ntret; +#ifdef HAVE_KRB5 + int ret; +#endif if ((username == NULL && princ_name == NULL) || ccname == NULL || uid < 0) { return NT_STATUS_INVALID_PARAMETER; @@ -324,6 +486,28 @@ NTSTATUS add_ccache_to_list(const char *princ_name, return NT_STATUS_NO_MORE_ENTRIES; } + /* If it is cached login, destroy krb5 ticket + * to avoid surprise. */ +#ifdef HAVE_KRB5 + if (postponed_request) { + /* ignore KRB5_FCC_NOFILE error here */ + ret = ads_kdestroy(ccname); + if (ret == KRB5_FCC_NOFILE) { + ret = 0; + } + if (ret) { + DEBUG(0, ("add_ccache_to_list: failed to destroy " + "user krb5 ccache %s with %s\n", ccname, + error_message(ret))); + return krb5_to_nt_status(ret); + } else { + DEBUG(10, ("add_ccache_to_list: successfully destroyed " + "krb5 ccache %s for user %s\n", ccname, + username)); + } + } +#endif + /* Reference count old entries */ entry = get_ccache_by_username(username); if (entry) { @@ -335,7 +519,52 @@ NTSTATUS add_ccache_to_list(const char *princ_name, DEBUG(10,("add_ccache_to_list: ref count on entry %s is now %d\n", username, entry->ref_count)); /* FIXME: in this case we still might want to have a krb5 cred - * event handler created - gd*/ + * event handler created - gd + * Add ticket refresh handler here */ + + if (!lp_winbind_refresh_tickets() || renew_until <= 0) { + return NT_STATUS_OK; + } + + if (!entry->event) { + struct timeval t; + if (postponed_request) { + t = timeval_current_ofs(MAX(30, lp_winbind_cache_time()), 0); + add_krb5_ticket_gain_handler_event(entry, t); + } else { + /* Renew at 1/2 the ticket expiration time */ +#if defined(DEBUG_KRB5_TKT_RENEWAL) + t = timeval_set(time(NULL)+30, 0); +#else + t = timeval_set(KRB5_EVENT_REFRESH_TIME(ticket_end), 0); +#endif + if (!entry->refresh_time) { + entry->refresh_time = t.tv_sec; + } + entry->event = event_add_timed(winbind_event_context(), + entry, + t, + "krb5_ticket_refresh_handler", + krb5_ticket_refresh_handler, + entry); + } + + if (!entry->event) { + ntret = remove_ccache(username); + if (!NT_STATUS_IS_OK(ntret)) { + DEBUG(0, ("add_ccache_to_list: Failed to remove krb5 " + "ccache %s for user %s\n", entry->ccname, + entry->username)); + DEBUG(0, ("add_ccache_to_list: error is %s\n", + nt_errstr(ntret))); + return ntret; + } + return NT_STATUS_NO_MEMORY; + } + + DEBUG(10,("add_ccache_to_list: added krb5_ticket handler\n")); + } + return NT_STATUS_OK; } @@ -381,20 +610,21 @@ NTSTATUS add_ccache_to_list(const char *princ_name, entry->ref_count = 1; if (lp_winbind_refresh_tickets() && renew_until > 0) { + struct timeval t; if (postponed_request) { - entry->event = event_add_timed(winbind_event_context(), entry, - timeval_current_ofs(MAX(30, lp_winbind_cache_time()), 0), - "krb5_ticket_gain_handler", - krb5_ticket_gain_handler, - entry); + add_krb5_ticket_gain_handler_event(entry, t); } else { /* Renew at 1/2 the ticket expiration time */ - entry->event = event_add_timed(winbind_event_context(), entry, #if defined(DEBUG_KRB5_TKT_RENEWAL) - timeval_set(time(NULL)+30, 0), + t = timeval_set(time(NULL)+30, 0); #else - timeval_set(KRB5_EVENT_REFRESH_TIME(ticket_end), 0), + t = timeval_set(KRB5_EVENT_REFRESH_TIME(ticket_end), 0); #endif + if (!entry->refresh_time) { + entry->refresh_time = t.tv_sec; + } + entry->event = event_add_timed(winbind_event_context(), entry, + t, "krb5_ticket_refresh_handler", krb5_ticket_refresh_handler, entry); diff --git a/source/nsswitch/winbindd_dual.c b/source/nsswitch/winbindd_dual.c index 14a602a4f78..f976eba7950 100644 --- a/source/nsswitch/winbindd_dual.c +++ b/source/nsswitch/winbindd_dual.c @@ -779,6 +779,7 @@ static void child_msg_offline(int msg_type, struct process_id src, void *buf, size_t len, void *private_data) { struct winbindd_domain *domain; + struct winbindd_domain *primary_domain = NULL; const char *domainname = (const char *)buf; if (buf == NULL || len == 0) { @@ -798,6 +799,8 @@ static void child_msg_offline(int msg_type, struct process_id src, return; } + primary_domain = find_our_domain(); + /* Mark the requested domain offline. */ for (domain = domain_list(); domain; domain = domain->next) { @@ -807,6 +810,11 @@ static void child_msg_offline(int msg_type, struct process_id src, if (strequal(domain->name, domainname)) { DEBUG(5,("child_msg_offline: marking %s offline.\n", domain->name)); set_domain_offline(domain); + /* we are in the trusted domain, set the primary domain + * offline too */ + if (domain != primary_domain) { + set_domain_offline(primary_domain); + } } } } @@ -817,6 +825,7 @@ static void child_msg_online(int msg_type, struct process_id src, void *buf, size_t len, void *private_data) { struct winbindd_domain *domain; + struct winbindd_domain *primary_domain = NULL; const char *domainname = (const char *)buf; if (buf == NULL || len == 0) { @@ -830,6 +839,8 @@ static void child_msg_online(int msg_type, struct process_id src, return; } + primary_domain = find_our_domain(); + /* Set our global state as online. */ set_global_winbindd_state_online(); @@ -844,6 +855,16 @@ static void child_msg_online(int msg_type, struct process_id src, DEBUG(5,("child_msg_online: requesting %s to go online.\n", domain->name)); winbindd_flush_negative_conn_cache(domain); set_domain_online_request(domain); + + /* we can be in trusted domain, which will contact primary domain + * we have to bring primary domain online in trusted domain process + * see, winbindd_dual_pam_auth() --> winbindd_dual_pam_auth_samlogon() + * --> contact_domain = find_our_domain() + * */ + if (domain != primary_domain) { + winbindd_flush_negative_conn_cache(primary_domain); + set_domain_online_request(primary_domain); + } } } } @@ -907,11 +928,58 @@ static void child_msg_onlinestatus(int msg_type, struct process_id src, talloc_destroy(mem_ctx); } +bool reinit_after_fork(struct messaging_context *msg_ctx, + struct event_context *ev_ctx, + bool parent_longlived); +void ccache_remove_all_after_fork(void); + +bool winbindd_reinit_after_fork(const char *logfile) +{ + struct winbindd_domain *dom; + struct winbindd_child *cl; + + if (!reinit_after_fork(NULL, + winbind_event_context(), true)) { + DEBUG(0, ("reinit_after_fork failed.\n")); + return false; + } + + close_conns_after_fork(); + + if (!override_logfile && logfile) { + lp_set_logfile(logfile); + reopen_logs(); + } + + /* Don't handle the same messages as our parent. */ + message_deregister(MSG_SMB_CONF_UPDATED); + message_deregister(MSG_SHUTDOWN); + message_deregister(MSG_WINBIND_OFFLINE); + message_deregister(MSG_WINBIND_ONLINE); + message_deregister(MSG_WINBIND_ONLINESTATUS); + + ccache_remove_all_after_fork(); + + for (dom = domain_list(); dom; dom = dom->next) { + TALLOC_FREE(dom->check_online_event); + } + + for (cl = children; cl; cl = cl->next) { + struct winbindd_async_request *request; + + for (request = cl->requests; request; request = request->next) { + TALLOC_FREE(request->reply_timeout_event); + } + TALLOC_FREE(cl->lockout_policy_event); + } + + return true; +} + static BOOL fork_domain_child(struct winbindd_child *child) { int fdpair[2]; struct winbindd_cli_state state; - struct winbindd_domain *domain; struct winbindd_domain *primary_domain = NULL; if (socketpair(AF_UNIX, SOCK_STREAM, 0, fdpair) != 0) { @@ -957,25 +1025,11 @@ static BOOL fork_domain_child(struct winbindd_child *child) close(fdpair[1]); /* tdb needs special fork handling */ - if (tdb_reopen_all(1) == -1) { - DEBUG(0,("tdb_reopen_all failed.\n")); + if (!winbindd_reinit_after_fork(child->logfilename)) { + DEBUG(0, ("winbindd_reinit_after_fork failed.\n")); _exit(0); } - close_conns_after_fork(); - - if (!override_logfile) { - lp_set_logfile(child->logfilename); - reopen_logs(); - } - - /* Don't handle the same messages as our parent. */ - message_deregister(MSG_SMB_CONF_UPDATED); - message_deregister(MSG_SHUTDOWN); - message_deregister(MSG_WINBIND_OFFLINE); - message_deregister(MSG_WINBIND_ONLINE); - message_deregister(MSG_WINBIND_ONLINESTATUS); - /* The child is ok with online/offline messages now. */ message_unblock(); @@ -985,29 +1039,30 @@ static BOOL fork_domain_child(struct winbindd_child *child) message_register(MSG_WINBIND_ONLINESTATUS, child_msg_onlinestatus, NULL); + primary_domain = find_our_domain(); + + if (primary_domain == NULL) { + smb_panic("no primary domain found"); + } + + /* It doesn't matter if we allow cache login, + * try to bring domain online after fork. */ if ( child->domain ) { child->domain->startup = True; child->domain->startup_time = time(NULL); - } - - /* Ensure we have no pending check_online events other - than one for this domain or the primary domain. */ - - for (domain = domain_list(); domain; domain = domain->next) { - if (domain->primary) { - primary_domain = domain; - } - if ((domain != child->domain) && !domain->primary) { - TALLOC_FREE(domain->check_online_event); + /* we can be in primary domain or in trusted domain + * If we are in trusted domain, set the primary domain + * in start-up mode */ + if (!(child->domain->internal)) { + set_domain_online_request(child->domain); + if (!(child->domain->primary)) { + primary_domain->startup = True; + primary_domain->startup_time = time(NULL); + set_domain_online_request(primary_domain); + } } } - /* Ensure we're not handling an event inherited from - our parent. */ - - cancel_named_event(winbind_event_context(), - "krb5_ticket_refresh_handler"); - /* We might be in the idmap child...*/ if (child->domain && !(child->domain->internal) && lp_winbind_offline_logon()) { diff --git a/source/smbd/server.c b/source/smbd/server.c index c90d5e727f7..2ed6792c4a9 100644 --- a/source/smbd/server.c +++ b/source/smbd/server.c @@ -300,6 +300,10 @@ static BOOL allowable_number_of_smbd_processes(void) Open the socket communication. ****************************************************************************/ +bool reinit_after_fork(struct messaging_context *msg_ctx, + struct event_context *ev_ctx, + bool parent_longlived); + static BOOL open_sockets_smbd(BOOL is_daemon, BOOL interactive, const char *smb_ports) { int num_interfaces = iface_count(); @@ -560,17 +564,11 @@ static BOOL open_sockets_smbd(BOOL is_daemon, BOOL interactive, const char *smb_ set_remote_machine_name(get_peer_addr(smbd_server_fd()), False); - /* Reset the state of the random - * number generation system, so - * children do not get the same random - * numbers as each other */ - - set_need_random_reseed(); - /* tdb needs special fork handling - remove - * CLEAR_IF_FIRST flags */ - if (tdb_reopen_all(1) == -1) { - DEBUG(0,("tdb_reopen_all failed.\n")); - smb_panic("tdb_reopen_all failed."); + if (!reinit_after_fork(smbd_messaging_context(), + smbd_event_context(), + true)) { + DEBUG(0, ("reinit_after_fork failed.\n")); + smb_panic("reinit_after_fork failed.\n"); } return True;