[SCM] Samba Shared Repository - branch v3-0-test updated - release-3-0-32-101-g194425f

Jeremy Allison jra at samba.org
Sat Jan 10 22:33:51 GMT 2009


The branch, v3-0-test has been updated
       via  194425f8074e2cfd5893499099614666f8d8ecd9 (commit)
      from  a4438df44621ae37c13e5c5064cc3dc5e1371457 (commit)

http://gitweb.samba.org/?p=samba.git;a=shortlog;h=v3-0-test


- Log -----------------------------------------------------------------
commit 194425f8074e2cfd5893499099614666f8d8ecd9
Author: Bo Yang <boyang at novell.com>
Date:   Sat Jan 10 14:32:43 2009 -0800

    Backport of the clean event context after fork and
    krb5 refresh chain fixes.

-----------------------------------------------------------------------

Summary of changes:
 source/lib/events.c                   |   47 ++---
 source/lib/util.c                     |   13 ++
 source/nsswitch/winbindd_cm.c         |   67 +++----
 source/nsswitch/winbindd_cred_cache.c |  346 +++++++++++++++++++++++++++------
 source/nsswitch/winbindd_dual.c       |  123 +++++++++----
 source/smbd/server.c                  |   20 +-
 6 files changed, 452 insertions(+), 164 deletions(-)


Changeset truncated at 500 lines:

diff --git a/source/lib/events.c b/source/lib/events.c
index a00db77..43ca8df 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 3c9233d..f46aaaf 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 8d303b3..1fa40ca 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 e47858b..a0f7237 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;
+	}


-- 
Samba Shared Repository


More information about the samba-cvs mailing list