[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