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

Jeremy Allison jra at samba.org
Tue Jan 6 04:06:47 GMT 2009


The branch, v3-3-test has been updated
       via  d6eb7f1af2f4097085a4a14e9e88327f039bdf29 (commit)
      from  ada3145ffe40dfbe89f968e844bfb139a58eab5d (commit)

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


- Log -----------------------------------------------------------------
commit d6eb7f1af2f4097085a4a14e9e88327f039bdf29
Author: Bo Yang <boyang at novell.com>
Date:   Mon Jan 5 20:05:53 2009 -0800

    Fix broken krb5 refresh chain
    
    Signed-off-by: Stefan Metzmacher <metze at samba.org>

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

Summary of changes:
 source/winbindd/winbindd_cm.c         |    2 +
 source/winbindd/winbindd_cred_cache.c |  252 ++++++++++++++++++++++++++++-----
 source/winbindd/winbindd_dual.c       |   21 +++
 3 files changed, 236 insertions(+), 39 deletions(-)


Changeset truncated at 500 lines:

diff --git a/source/winbindd/winbindd_cm.c b/source/winbindd/winbindd_cm.c
index 51ecc1f..f762498 100644
--- a/source/winbindd/winbindd_cm.c
+++ b/source/winbindd/winbindd_cm.c
@@ -426,6 +426,8 @@ static void set_domain_online(struct winbindd_domain *domain)
 	GetTimeOfDay(&now);
 	set_event_dispatch_time(winbind_event_context(),
 				"krb5_ticket_gain_handler", now);
+	set_event_dispatch_time(winbind_event_context(),
+				"krb5_ticket_refresh_handler", now);
 
 	/* Ok, we're out of any startup mode now... */
 	domain->startup = False;
diff --git a/source/winbindd/winbindd_cred_cache.c b/source/winbindd/winbindd_cred_cache.c
index 311b1d1..e9e9e4d 100644
--- a/source/winbindd/winbindd_cred_cache.c
+++ b/source/winbindd/winbindd_cred_cache.c
@@ -34,6 +34,10 @@
 #define MAX_CCACHES 100
 
 static struct WINBINDD_CCACHE_ENTRY *ccache_list;
+static void krb5_ticket_gain_handler(struct event_context *,
+				     struct timed_event *,
+				     const struct timeval *,
+				     void *);
 
 /* The Krb5 ticket refresh handler should be scheduled
    at one-half of the period from now till the tkt
@@ -85,6 +89,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 +102,84 @@ 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 */
+					entry->event = event_add_timed(winbind_event_context(),
+								       entry, 
+								       timeval_set(new_start, 0),
+								       "krb5_ticket_gain_handler",
+								       krb5_ticket_gain_handler,
+								       entry);
+					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 +191,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,20 +203,69 @@ 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 */
+			entry->event = event_add_timed(winbind_event_context(),
+							entry,
+							timeval_set(new_start, 0),
+							"krb5_ticket_gain_handler",
+							krb5_ticket_gain_handler,
+							entry);
+			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;
+		entry->event = event_add_timed(winbind_event_context(), entry,
+						timeval_set(expire_time, 0),
+						"krb5_ticket_gain_handler",
+						krb5_ticket_gain_handler,
+						entry);
+		return;
+	}
 
 	entry->event = event_add_timed(winbind_event_context(), entry,
 				       timeval_set(new_start, 0),
@@ -239,6 +334,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,8 +347,12 @@ 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
 
 	entry->event = event_add_timed(winbind_event_context(),
 				       entry,
@@ -331,6 +433,10 @@ NTSTATUS add_ccache_to_list(const char *princ_name,
 {
 	struct WINBINDD_CCACHE_ENTRY *entry = NULL;
 	struct timeval t;
+	NTSTATUS ntret;
+#ifdef HAVE_KRB5
+	int ret;
+#endif
 
 	if ((username == NULL && princ_name == NULL) ||
 	    ccname == NULL || uid < 0) {
@@ -343,6 +449,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) {
@@ -355,7 +483,53 @@ NTSTATUS add_ccache_to_list(const char *princ_name,
 			"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) {
+			if (postponed_request) {
+				t = timeval_current_ofs(MAX(30, lp_winbind_cache_time()), 0);
+				entry->event = event_add_timed(winbind_event_context(),
+							       entry,
+							       t,
+							       "krb5_ticket_gain_handler",
+							       krb5_ticket_gain_handler,
+							       entry);
+			} 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
+				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;
 	}
 
diff --git a/source/winbindd/winbindd_dual.c b/source/winbindd/winbindd_dual.c
index 6a8601d..2ce310c 100644
--- a/source/winbindd/winbindd_dual.c
+++ b/source/winbindd/winbindd_dual.c
@@ -985,6 +985,7 @@ static void child_msg_offline(struct messaging_context *msg,
 			      DATA_BLOB *data)
 {
 	struct winbindd_domain *domain;
+	struct winbindd_domain *primary_domain = NULL;
 	const char *domainname = (const char *)data->data;
 
 	if (data->data == NULL || data->length == 0) {
@@ -998,6 +999,8 @@ static void child_msg_offline(struct messaging_context *msg,
 		return;
 	}
 
+	primary_domain = find_our_domain();
+
 	/* Mark the requested domain offline. */
 
 	for (domain = domain_list(); domain; domain = domain->next) {
@@ -1007,6 +1010,11 @@ static void child_msg_offline(struct messaging_context *msg,
 		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);
+			}
 		}
 	}
 }
@@ -1020,6 +1028,7 @@ static void child_msg_online(struct messaging_context *msg,
 			     DATA_BLOB *data)
 {
 	struct winbindd_domain *domain;
+	struct winbindd_domain *primary_domain = NULL;
 	const char *domainname = (const char *)data->data;
 
 	if (data->data == NULL || data->length == 0) {
@@ -1033,6 +1042,8 @@ static void child_msg_online(struct messaging_context *msg,
 		return;
 	}
 
+	primary_domain = find_our_domain();
+
 	/* Set our global state as online. */
 	set_global_winbindd_state_online();
 
@@ -1047,6 +1058,16 @@ static void child_msg_online(struct messaging_context *msg,
 			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);
+			}
 		}
 	}
 }


-- 
Samba Shared Repository


More information about the samba-cvs mailing list