Fix bug #9098 - winbind does not refresh kerberos tickets.
[samba.git] / source3 / winbindd / winbindd_cred_cache.c
index c869544048755b53bc700957b37e5e26e318eafb..ba4a7b27da078c29e0ee09c8f97a6f5d7beb5b1c 100644 (file)
@@ -23,6 +23,9 @@
 
 #include "includes.h"
 #include "winbindd.h"
+#include "../libcli/auth/libcli_auth.h"
+#include "smb_krb5.h"
+
 #undef DBGC_CLASS
 #define DBGC_CLASS DBGC_WINBIND
 
 static struct WINBINDD_CCACHE_ENTRY *ccache_list;
 static void krb5_ticket_gain_handler(struct event_context *,
                                     struct timed_event *,
-                                    const struct timeval *,
+                                    struct timeval,
                                     void *);
+static void add_krb5_ticket_gain_handler_event(struct WINBINDD_CCACHE_ENTRY *,
+                                    struct timeval);
 
 /* The Krb5 ticket refresh handler should be scheduled
    at one-half of the period from now till the tkt
@@ -95,7 +100,7 @@ void ccache_remove_all_after_fork(void)
 
 static void krb5_ticket_refresh_handler(struct event_context *event_ctx,
                                        struct timed_event *te,
-                                       const struct timeval *now,
+                                       struct timeval now,
                                        void *private_data)
 {
        struct WINBINDD_CCACHE_ENTRY *entry =
@@ -148,7 +153,8 @@ rekinit:
 
                                /* Don't break the ticket refresh chain: retry 
                                 * refreshing ticket sometime later when KDC is 
-                                * unreachable -- BoYang 
+                                * unreachable -- BoYang. More error code handling
+                                * here? 
                                 * */
 
                                if ((ret == KRB5_KDC_UNREACH)
@@ -159,13 +165,8 @@ rekinit:
                                        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);
+                                       add_krb5_ticket_gain_handler_event(entry,
+                                                       timeval_set(new_start, 0));
                                        return;
                                }
                                TALLOC_FREE(entry->event);
@@ -227,7 +228,8 @@ rekinit:
                /* avoid breaking the renewal chain: retry in
                 * lp_winbind_cache_time() seconds when the KDC was not
                 * available right now. 
-                * the return code can be KRB5_REALM_CANT_RESOLVE*/
+                * the return code can be KRB5_REALM_CANT_RESOLVE. 
+                * More error code handling here? */
 
                if ((ret == KRB5_KDC_UNREACH) 
                    || (ret == KRB5_REALM_CANT_RESOLVE)) {
@@ -239,12 +241,8 @@ rekinit:
 #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);
+                       add_krb5_ticket_gain_handler_event(entry,
+                                               timeval_set(new_start, 0));
                        return;
                }
 
@@ -273,11 +271,8 @@ done:
             && (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);
+               add_krb5_ticket_gain_handler_event(entry,
+                                       timeval_set(expire_time, 0));
                return;
        }
 
@@ -286,7 +281,6 @@ done:
        }
        entry->event = event_add_timed(winbind_event_context(), entry,
                                       timeval_set(new_start, 0),
-                                      "krb5_ticket_refresh_handler",
                                       krb5_ticket_refresh_handler,
                                       entry);
 
@@ -299,7 +293,7 @@ done:
 
 static void krb5_ticket_gain_handler(struct event_context *event_ctx,
                                     struct timed_event *te,
-                                    const struct timeval *now,
+                                    struct timeval now,
                                     void *private_data)
 {
        struct WINBINDD_CCACHE_ENTRY *entry =
@@ -371,14 +365,7 @@ static void krb5_ticket_gain_handler(struct event_context *event_ctx,
        t = timeval_current_ofs(MAX(30, lp_winbind_cache_time()), 0);
 #endif
 
-       entry->refresh_time = 0;
-       entry->event = event_add_timed(winbind_event_context(),
-                                      entry,
-                                      t,
-                                      "krb5_ticket_gain_handler",
-                                      krb5_ticket_gain_handler,
-                                      entry);
-
+       add_krb5_ticket_gain_handler_event(entry, t);
        return;
 
   got_ticket:
@@ -395,7 +382,6 @@ static void krb5_ticket_gain_handler(struct event_context *event_ctx,
        entry->event = event_add_timed(winbind_event_context(),
                                       entry,
                                       t,
-                                      "krb5_ticket_refresh_handler",
                                       krb5_ticket_refresh_handler,
                                       entry);
 
@@ -403,6 +389,22 @@ static void krb5_ticket_gain_handler(struct event_context *event_ctx,
 #endif
 }
 
+/**************************************************************
+ The gain initial ticket case is recognised 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,
+                                      entry);
+}
+
 void ccache_regain_all_now(void)
 {
        struct WINBINDD_CCACHE_ENTRY *cur;
@@ -419,14 +421,12 @@ void ccache_regain_all_now(void)
                        new_event = event_add_timed(winbind_event_context(),
                                                    cur,
                                                    t,
-                                                   "krb5_ticket_gain_handler",
                                                    krb5_ticket_gain_handler,
                                                    cur);
                } else {
                        new_event = event_add_timed(winbind_event_context(),
                                                    cur,
                                                    t,
-                                                   "krb5_ticket_refresh_handler",
                                                    krb5_ticket_refresh_handler,
                                                    cur);
                }
@@ -484,6 +484,7 @@ NTSTATUS add_ccache_to_list(const char *princ_name,
                            const char *ccname,
                            const char *service,
                            const char *username,
+                           const char *pass,
                            const char *realm,
                            uid_t uid,
                            time_t create_time,
@@ -553,12 +554,7 @@ NTSTATUS add_ccache_to_list(const char *princ_name,
                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);
+                               add_krb5_ticket_gain_handler_event(entry, t);
                        } else {
                                /* Renew at 1/2 the ticket expiration time */
 #if defined(DEBUG_KRB5_TKT_RENEWAL)
@@ -566,10 +562,12 @@ NTSTATUS add_ccache_to_list(const char *princ_name,
 #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);
                        }
@@ -589,7 +587,20 @@ NTSTATUS add_ccache_to_list(const char *princ_name,
 
                        DEBUG(10,("add_ccache_to_list: added krb5_ticket handler\n"));
                }
-                
+
+               /*
+                * If we're set up to renew our krb5 tickets, we must
+                * cache the credentials in memory for the ticket
+                * renew function (or increase the reference count
+                * if we're logging in more than once). Fix inspired
+                * by patch from Ian Gordon <ian.gordon@strath.ac.uk>
+                * for bugid #9098.
+                */
+
+               ntret = winbindd_add_memory_creds(username, uid, pass);
+               DEBUG(10, ("winbindd_add_memory_creds returned: %s\n",
+                       nt_errstr(ntret)));
+
                return NT_STATUS_OK;
        }
 
@@ -640,13 +651,7 @@ NTSTATUS add_ccache_to_list(const char *princ_name,
 
        if (postponed_request) {
                t = timeval_current_ofs(MAX(30, lp_winbind_cache_time()), 0);
-               entry->refresh_time = 0;
-               entry->event = event_add_timed(winbind_event_context(),
-                                              entry,
-                                              t,
-                                              "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 */
 #if defined(DEBUG_KRB5_TKT_RENEWAL)
@@ -660,7 +665,6 @@ NTSTATUS add_ccache_to_list(const char *princ_name,
                entry->event = event_add_timed(winbind_event_context(),
                                               entry,
                                               t,
-                                              "krb5_ticket_refresh_handler",
                                               krb5_ticket_refresh_handler,
                                               entry);
        }
@@ -679,6 +683,20 @@ NTSTATUS add_ccache_to_list(const char *princ_name,
                "added ccache [%s] for user [%s] to the list\n",
                ccname, username));
 
+       if (entry->event) {
+               /*
+                * If we're set up to renew our krb5 tickets, we must
+                * cache the credentials in memory for the ticket
+                * renew function. Fix inspired by patch from
+                * Ian Gordon <ian.gordon@strath.ac.uk> for
+                * bugid #9098.
+                */
+
+               ntret = winbindd_add_memory_creds(username, uid, pass);
+               DEBUG(10, ("winbindd_add_memory_creds returned: %s\n",
+                       nt_errstr(ntret)));
+       }
+
        return NT_STATUS_OK;
 
  no_mem: