Backport of the clean event context after fork and
authorBo Yang <boyang@novell.com>
Sat, 10 Jan 2009 22:32:43 +0000 (14:32 -0800)
committerJeremy Allison <jra@samba.org>
Sat, 10 Jan 2009 22:32:43 +0000 (14:32 -0800)
krb5 refresh chain fixes.

source/lib/events.c
source/lib/util.c
source/nsswitch/winbindd_cm.c
source/nsswitch/winbindd_cred_cache.c
source/nsswitch/winbindd_dual.c
source/smbd/server.c

index a00db77b6bea3cd1fbaece208e5f9b3a3cd871d0..43ca8df6209f337b55615439063f86bfe57fa4a6 100644 (file)
@@ -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;
 }
index 3c9233d9b34d66856a0d1f495a227a78d6b6b845..f46aaafc219a835d87b5932e909ad6b818936015 100644 (file)
@@ -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;
+}
index 8d303b3d4acbff73d7113b10210003207a182de1..1fa40caf6ddc276a639ddc5bd636a7799d1f0308 100644 (file)
@@ -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");
+       }
 }
 
 /****************************************************************
index e47858b628c2fe4d8dd08776442ed27f5d65c5b7..a0f72379d1717b319ddf4b0f63cc25fd909ef251 100644 (file)
@@ -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);
index 14a602a4f789ff9393637a642a6fef0bbeefdeee..f976eba79507b0ace9fa60cd178e09f8c963bf0a 100644 (file)
@@ -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()) {
index c90d5e727f7c585bfa6c0827de027bcbdddde170..2ed6792c4a956376539173531543ef0f854843c8 100644 (file)
@@ -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;