s3:winbind add timeouts to winbind cache
authorChristian Ambach <christian.ambach@de.ibm.com>
Thu, 4 Nov 2010 16:10:25 +0000 (17:10 +0100)
committerVolker Lendecke <vl@samba.org>
Mon, 8 Nov 2010 12:39:51 +0000 (13:39 +0100)
This adds a timeout value to cache entries and the NDR records
in the winbind cache.

The previous approach of just comparing the sequence number has some issues,
e.g. when retrying a wbinfo -n operation for a user in a not yet trusted
domain was always failing even after the trusted domain was added.

The new approach compares sequence number and timeout value to
determine if a cache entry is still valid or not.

I increased the cache version number so an old cache will be wiped
automatically after upgrade.

source3/winbindd/winbindd_cache.c

index 9ee8f6ab2a6add3a76c8483cc6958ba1e8fe3f8e..54cbc616bd2fbfd85775cb9dadb61f9f2ccd54f4 100644 (file)
@@ -35,7 +35,7 @@
 #undef DBGC_CLASS
 #define DBGC_CLASS DBGC_WINBIND
 
-#define WINBINDD_CACHE_VERSION 1
+#define WINBINDD_CACHE_VERSION 2
 #define WINBINDD_CACHE_VERSION_KEYSTR "WINBINDD_CACHE_VERSION"
 
 extern struct winbindd_methods reconnect_methods;
@@ -96,6 +96,7 @@ struct winbind_cache {
 struct cache_entry {
        NTSTATUS status;
        uint32 sequence_number;
+       uint64 timeout;
        uint8 *data;
        uint32 len, ofs;
 };
@@ -204,6 +205,21 @@ static bool centry_check_bytes(struct cache_entry *centry, size_t nbytes)
        return true;
 }
 
+/*
+  pull a uint64 from a cache entry
+*/
+static uint64 centry_uint64(struct cache_entry *centry)
+{
+       uint64 ret;
+
+       if (!centry_check_bytes(centry, 8)) {
+               smb_panic_fn("centry_uint64");
+       }
+       ret = BVAL(centry->data, centry->ofs);
+       centry->ofs += 8;
+       return ret;
+}
+
 /*
   pull a uint32 from a cache entry 
 */
@@ -596,9 +612,10 @@ static bool centry_expired(struct winbindd_domain *domain, const char *keystr, s
        }
 
        /* if the server is down or the cache entry is not older than the
-          current sequence number then it is OK */
-       if (wcache_server_down(domain) || 
-           centry->sequence_number == domain->sequence_number) {
+          current sequence number or it did not timeout then it is OK */
+       if (wcache_server_down(domain)
+           || (centry->sequence_number == domain->sequence_number
+               && centry->timeout > time(NULL))) {
                DEBUG(10,("centry_expired: Key %s for domain %s is good.\n",
                        keystr, domain->name ));
                return false;
@@ -629,15 +646,17 @@ static struct cache_entry *wcache_fetch_raw(char *kstr)
        centry->len = data.dsize;
        centry->ofs = 0;
 
-       if (centry->len < 8) {
+       if (centry->len < 16) {
                /* huh? corrupt cache? */
-               DEBUG(10,("wcache_fetch_raw: Corrupt cache for key %s (len < 8) ?\n", kstr));
+               DEBUG(10,("wcache_fetch_raw: Corrupt cache for key %s "
+                         "(len < 16)?\n", kstr));
                centry_free(centry);
                return NULL;
        }
 
        centry->status = centry_ntstatus(centry);
        centry->sequence_number = centry_uint32(centry);
+       centry->timeout = centry_uint64(centry);
 
        return centry;
 }
@@ -745,6 +764,16 @@ static void centry_expand(struct cache_entry *centry, uint32 len)
        }
 }
 
+/*
+  push a uint64 into a centry
+*/
+static void centry_put_uint64(struct cache_entry *centry, uint64 v)
+{
+       centry_expand(centry, 8);
+       SBVAL(centry->data, centry->ofs, v);
+       centry->ofs += 8;
+}
+
 /*
   push a uint32 into a centry 
 */
@@ -866,8 +895,10 @@ struct cache_entry *centry_start(struct winbindd_domain *domain, NTSTATUS status
        centry->data = SMB_XMALLOC_ARRAY(uint8, centry->len);
        centry->ofs = 0;
        centry->sequence_number = domain->sequence_number;
+       centry->timeout = lp_winbind_cache_time() + time(NULL);
        centry_put_ntstatus(centry, status);
        centry_put_uint32(centry, centry->sequence_number);
+       centry_put_uint64(centry, centry->timeout);
        return centry;
 }
 
@@ -3484,9 +3515,10 @@ static struct cache_entry *create_centry_validate(const char *kstr, TDB_DATA dat
        centry->len = data.dsize;
        centry->ofs = 0;
 
-       if (centry->len < 8) {
+       if (centry->len < 16) {
                /* huh? corrupt cache? */
-               DEBUG(0,("create_centry_validate: Corrupt cache for key %s (len < 8) ?\n", kstr));
+               DEBUG(0,("create_centry_validate: Corrupt cache for key %s "
+                        "(len < 16) ?\n", kstr));
                centry_free(centry);
                state->bad_entry = true;
                state->success = false;
@@ -3495,6 +3527,7 @@ static struct cache_entry *create_centry_validate(const char *kstr, TDB_DATA dat
 
        centry->status = NT_STATUS(centry_uint32(centry));
        centry->sequence_number = centry_uint32(centry);
+       centry->timeout = centry_uint64(centry);
        return centry;
 }
 
@@ -4699,12 +4732,13 @@ bool wcache_fetch_ndr(TALLOC_CTX *mem_ctx, struct winbindd_domain *domain,
        if (data.dptr == NULL) {
                return false;
        }
-       if (data.dsize < 4) {
+       if (data.dsize < 12) {
                goto fail;
        }
 
        if (!is_domain_offline(domain)) {
                uint32_t entry_seqnum, dom_seqnum, last_check;
+               uint64_t entry_timeout;
 
                if (!wcache_fetch_seqnum(domain->name, &dom_seqnum,
                                         &last_check)) {
@@ -4716,15 +4750,20 @@ bool wcache_fetch_ndr(TALLOC_CTX *mem_ctx, struct winbindd_domain *domain,
                                   (int)entry_seqnum));
                        goto fail;
                }
+               entry_timeout = BVAL(data.dptr, 4);
+               if (entry_timeout > time(NULL)) {
+                       DEBUG(10, ("Entry has timed out\n"));
+                       goto fail;
+               }
        }
 
-       resp->data = (uint8_t *)talloc_memdup(mem_ctx, data.dptr + 4,
-                                             data.dsize - 4);
+       resp->data = (uint8_t *)talloc_memdup(mem_ctx, data.dptr + 12,
+                                             data.dsize - 12);
        if (resp->data == NULL) {
                DEBUG(10, ("talloc failed\n"));
                goto fail;
        }
-       resp->length = data.dsize - 4;
+       resp->length = data.dsize - 12;
 
        ret = true;
 fail:
@@ -4737,6 +4776,7 @@ void wcache_store_ndr(struct winbindd_domain *domain, uint32_t opnum,
 {
        TDB_DATA key, data;
        uint32_t dom_seqnum, last_check;
+       uint64_t timeout;
 
        if (!wcache_opnum_cacheable(opnum) ||
            is_my_own_sam_domain(domain) ||
@@ -4758,14 +4798,17 @@ void wcache_store_ndr(struct winbindd_domain *domain, uint32_t opnum,
                return;
        }
 
-       data.dsize = resp->length + 4;
+       timeout = time(NULL) + lp_winbind_cache_time();
+
+       data.dsize = resp->length + 12;
        data.dptr = talloc_array(key.dptr, uint8_t, data.dsize);
        if (data.dptr == NULL) {
                goto done;
        }
 
        SIVAL(data.dptr, 0, dom_seqnum);
-       memcpy(data.dptr+4, resp->data, resp->length);
+       SBVAL(data.dptr, 4, timeout);
+       memcpy(data.dptr + 12, resp->data, resp->length);
 
        tdb_store(wcache->tdb, key, data, 0);