[SCM] Samba Shared Repository - branch v3-2-test updated - release-3-2-0pre2-3347-g80a3759

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


The branch, v3-2-test has been updated
       via  80a37595ef5c289fcc02a8902e18eb9ab289b28c (commit)
      from  9811454d8ae3f09d9a206f14ed533fa6382a914c (commit)

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


- Log -----------------------------------------------------------------
commit 80a37595ef5c289fcc02a8902e18eb9ab289b28c
Author: Bo Yang <boyang at novell.com>
Date:   Sat Jan 10 14:33:36 2009 -0800

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

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

Summary of changes:
 source/lib/events.c                   |   32 ----
 source/winbindd/winbindd.c            |    5 +
 source/winbindd/winbindd_cm.c         |   80 ++++----
 source/winbindd/winbindd_cred_cache.c |  326 +++++++++++++++++++++++++++-----
 source/winbindd/winbindd_dual.c       |  165 +++++++++++-------
 5 files changed, 423 insertions(+), 185 deletions(-)


Changeset truncated at 500 lines:

diff --git a/source/lib/events.c b/source/lib/events.c
index 2dae56e..27dd052 100644
--- a/source/lib/events.c
+++ b/source/lib/events.c
@@ -386,38 +386,6 @@ struct event_context *event_context_init(TALLOC_CTX *mem_ctx)
 	return result;
 }
 
-int set_event_dispatch_time(struct event_context *event_ctx,
-			    const char *event_name, struct timeval when)
-{
-	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;
-		}
-	}
-	return 0;
-}
-
-/* Returns 1 if event was found and cancelled, 0 otherwise. */
-
-int cancel_named_event(struct event_context *event_ctx,
-		       const char *event_name)
-{
-	struct timed_event *te;
-
-	for (te = event_ctx->timed_events; te; te = te->next) {
-		if (strcmp(event_name, te->event_name) == 0) {
-			TALLOC_FREE(te);
-			return 1;
-		}
-	}
-	return 0;
-}
-
 void dump_event_list(struct event_context *event_ctx)
 {
 	struct timed_event *te;
diff --git a/source/winbindd/winbindd.c b/source/winbindd/winbindd.c
index 442f072..683e554 100644
--- a/source/winbindd/winbindd.c
+++ b/source/winbindd/winbindd.c
@@ -1222,6 +1222,11 @@ int main(int argc, char **argv, char **envp)
 
 	TimeInit();
 
+	/* Don't use winbindd_reinit_after_fork here as
+	 * we're just starting up and haven't created any
+	 * winbindd-specific resources we must free yet. JRA.
+	 */
+
 	if (!reinit_after_fork(winbind_messaging_context(),
 			       winbind_event_context(), false)) {
 		DEBUG(0,("reinit_after_fork() failed\n"));
diff --git a/source/winbindd/winbindd_cm.c b/source/winbindd/winbindd_cm.c
index 343ad39..3996e48 100644
--- a/source/winbindd/winbindd_cm.c
+++ b/source/winbindd/winbindd_cm.c
@@ -173,6 +173,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();
+	char *lfile = NULL;
 
 	/* Stop zombies */
 	CatchChild();
@@ -199,22 +200,24 @@ static bool fork_child_dc_connect(struct winbindd_domain *domain)
 
 	/* Leave messages blocked - we will never process one. */
 
-	if (!reinit_after_fork(winbind_messaging_context(),
-			       winbind_event_context(), true)) {
+	if (!override_logfile) {
+		if (asprintf(&lfile, "%s/log.winbindd-dc-connect", get_dyn_LOGFILEBASE()) == -1) {
+			DEBUG(0, ("fork_child_dc_connect: out of memory!\n"));
+			return false;
+		}
+	}
+
+	if (!winbindd_reinit_after_fork(lfile)) {
 		DEBUG(0,("reinit_after_fork() failed\n"));
+		messaging_send_buf(winbind_messaging_context(),
+				   pid_to_procid(parent_pid),
+				   MSG_WINBIND_FAILED_TO_GO_ONLINE,
+				   (uint8 *)domain->name,
+				   strlen(domain->name) + 1);
 		_exit(0);
 	}
 
-	close_conns_after_fork();
-
-	if (!override_logfile) {
-		char *logfile;
-		if (asprintf(&logfile, "%s/log.winbindd-dc-connect", get_dyn_LOGFILEBASE()) > 0) {
-			lp_set_logfile(logfile);
-			SAFE_FREE(logfile);
-			reopen_logs();
-		}
-	}
+	SAFE_FREE(lfile);
 
 	mem_ctx = talloc_init("fork_child_dc_connect");
 	if (!mem_ctx) {
@@ -378,9 +381,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 ));
@@ -400,10 +404,8 @@ static void set_domain_online(struct winbindd_domain *domain)
 	winbindd_set_locator_kdc_envs(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;
 
@@ -475,37 +477,35 @@ void set_domain_online_request(struct winbindd_domain *domain)
 	   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
 		   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 ));
-
-		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");
-		}
 	}
-
-	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);
+	
+	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);
+	
+	/* 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");
+	}
 }
 
 /****************************************************************
diff --git a/source/winbindd/winbindd_cred_cache.c b/source/winbindd/winbindd_cred_cache.c
index 311b1d1..05294a9 100644
--- a/source/winbindd/winbindd_cred_cache.c
+++ b/source/winbindd/winbindd_cred_cache.c
@@ -34,6 +34,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
@@ -71,6 +73,72 @@ 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 tikcet case is recongnized 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.
 ****************************************************************/
@@ -85,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 +166,80 @@ 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,
-						  NULL);
-		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,
+							  NULL);
+			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 
+				 * */
+
+				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
+					/* 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;
+			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);
+			/* 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;
+			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);
@@ -146,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
 
@@ -157,21 +263,65 @@ 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. */
+		 * available right now. 
+		 * the return code can be KRB5_REALM_CANT_RESOLVE*/
 
-		if (ret == KRB5_KDC_UNREACH) {
+		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());
-			goto done;
+#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",
@@ -239,6 +389,9 @@ static void krb5_ticket_gain_handler(struct event_context *event_ctx,
 		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;
 	}
 
@@ -249,15 +402,14 @@ static void krb5_ticket_gain_handler(struct event_context *event_ctx,
 	goto got_ticket;
 
   retry_later:
-
+ 
+#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
 


-- 
Samba Shared Repository


More information about the samba-cvs mailing list