r23779: Change from v2 or later to v3 or later.
[gd/samba/.git] / source3 / nsswitch / winbindd_cache.c
index e9b0d2e8f9a68c7a9b17796f68c29ed394d83339..02c34220bf52cc5e4a0331530b1b1727a2337aa6 100644 (file)
@@ -4,13 +4,14 @@
    Winbind cache backend functions
 
    Copyright (C) Andrew Tridgell 2001
    Winbind cache backend functions
 
    Copyright (C) Andrew Tridgell 2001
-   Copyright (C) Gerald Carter   2003
+   Copyright (C) Gerald Carter   2003-2007
    Copyright (C) Volker Lendecke 2005
    Copyright (C) Guenther Deschner 2005
    Copyright (C) Volker Lendecke 2005
    Copyright (C) Guenther Deschner 2005
+   Copyright (C) Michael Adam    2007
    
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
-   the Free Software Foundation; either version 2 of the License, or
+   the Free Software Foundation; either version 3 of the License, or
    (at your option) any later version.
    
    This program is distributed in the hope that it will be useful,
    (at your option) any later version.
    
    This program is distributed in the hope that it will be useful,
 #undef DBGC_CLASS
 #define DBGC_CLASS DBGC_WINBIND
 
 #undef DBGC_CLASS
 #define DBGC_CLASS DBGC_WINBIND
 
+#define WINBINDD_CACHE_VERSION 1
+#define WINBINDD_CACHE_VERSION_KEYSTR "WINBINDD_CACHE_VERSION"
+
+extern struct winbindd_methods reconnect_methods;
+extern BOOL opt_nocache;
+#ifdef HAVE_ADS
+extern struct winbindd_methods ads_methods;
+#endif
+
+/*
+ * JRA. KEEP THIS LIST UP TO DATE IF YOU ADD CACHE ENTRIES.
+ * Here are the list of entry types that are *not* stored
+ * as form struct cache_entry in the cache.
+ */
+
+static const char *non_centry_keys[] = {
+       "SEQNUM/",
+       "DR/",
+       "DE/",
+       "WINBINDD_OFFLINE",
+       WINBINDD_CACHE_VERSION_KEYSTR,
+       NULL
+};
+
+/************************************************************************
+ Is this key a non-centry type ?
+************************************************************************/
+
+static BOOL is_non_centry_key(TDB_DATA kbuf)
+{
+       int i;
+
+       if (kbuf.dptr == NULL || kbuf.dsize == 0) {
+               return False;
+       }
+       for (i = 0; non_centry_keys[i] != NULL; i++) {
+               size_t namelen = strlen(non_centry_keys[i]);
+               if (kbuf.dsize < namelen) {
+                       continue;
+               }
+               if (strncmp(non_centry_keys[i], (const char *)kbuf.dptr, namelen) == 0) {
+                       return True;
+               }
+       }
+       return False;
+}
+
 /* Global online/offline state - False when online. winbindd starts up online
    and sets this to true if the first query fails and there's an entry in
    the cache tdb telling us to stay offline. */
 /* Global online/offline state - False when online. winbindd starts up online
    and sets this to true if the first query fails and there's an entry in
    the cache tdb telling us to stay offline. */
@@ -46,6 +94,8 @@ struct cache_entry {
        uint32 len, ofs;
 };
 
        uint32 len, ofs;
 };
 
+void (*smb_panic_fn)(const char *const why) = smb_panic;
+
 #define WINBINDD_MAX_CACHE_SIZE (50*1024*1024)
 
 static struct winbind_cache *wcache;
 #define WINBINDD_MAX_CACHE_SIZE (50*1024*1024)
 
 static struct winbind_cache *wcache;
@@ -83,14 +133,12 @@ void winbindd_check_cache_size(time_t t)
 static struct winbind_cache *get_cache(struct winbindd_domain *domain)
 {
        struct winbind_cache *ret = wcache;
 static struct winbind_cache *get_cache(struct winbindd_domain *domain)
 {
        struct winbind_cache *ret = wcache;
-#ifdef HAVE_ADS
-       struct winbindd_domain *our_domain = domain;
-#endif
 
 
-       /* we have to know what type of domain we are dealing with first */
+       /* We have to know what type of domain we are dealing with first. */
 
 
-       if ( !domain->initialized )
+       if ( !domain->initialized ) {
                init_dc_connection( domain );
                init_dc_connection( domain );
+       }
 
        /* 
           OK.  listen up becasue I'm only going to say this once.
 
        /* 
           OK.  listen up becasue I'm only going to say this once.
@@ -111,9 +159,8 @@ static struct winbind_cache *get_cache(struct winbindd_domain *domain)
         */
 
        if (!domain->backend) {
         */
 
        if (!domain->backend) {
-               extern struct winbindd_methods reconnect_methods;
 #ifdef HAVE_ADS
 #ifdef HAVE_ADS
-               extern struct winbindd_methods ads_methods;
+               struct winbindd_domain *our_domain = domain;
 
                /* find our domain first so we can figure out if we 
                   are joined to a kerberized domain */
 
                /* find our domain first so we can figure out if we 
                   are joined to a kerberized domain */
@@ -121,7 +168,9 @@ static struct winbind_cache *get_cache(struct winbindd_domain *domain)
                if ( !domain->primary )
                        our_domain = find_our_domain();
 
                if ( !domain->primary )
                        our_domain = find_our_domain();
 
-               if ( (our_domain->active_directory || IS_DC) && domain->active_directory ) {
+               if ((our_domain->active_directory || IS_DC)
+                   && domain->active_directory
+                   && !lp_winbind_rpc_only()) {
                        DEBUG(5,("get_cache: Setting ADS methods for domain %s\n", domain->name));
                        domain->backend = &ads_methods;
                } else {
                        DEBUG(5,("get_cache: Setting ADS methods for domain %s\n", domain->name));
                        domain->backend = &ads_methods;
                } else {
@@ -156,16 +205,26 @@ static void centry_free(struct cache_entry *centry)
        free(centry);
 }
 
        free(centry);
 }
 
+static BOOL centry_check_bytes(struct cache_entry *centry, size_t nbytes)
+{
+       if (centry->len - centry->ofs < nbytes) {
+               DEBUG(0,("centry corruption? needed %u bytes, have %d\n", 
+                        (unsigned int)nbytes,
+                        centry->len - centry->ofs));
+               return False;
+       }
+       return True;
+}
+
 /*
   pull a uint32 from a cache entry 
 */
 static uint32 centry_uint32(struct cache_entry *centry)
 {
        uint32 ret;
 /*
   pull a uint32 from a cache entry 
 */
 static uint32 centry_uint32(struct cache_entry *centry)
 {
        uint32 ret;
-       if (centry->len - centry->ofs < 4) {
-               DEBUG(0,("centry corruption? needed 4 bytes, have %d\n", 
-                        centry->len - centry->ofs));
-               smb_panic("centry_uint32");
+
+       if (!centry_check_bytes(centry, 4)) {
+               smb_panic_fn("centry_uint32");
        }
        ret = IVAL(centry->data, centry->ofs);
        centry->ofs += 4;
        }
        ret = IVAL(centry->data, centry->ofs);
        centry->ofs += 4;
@@ -178,10 +237,8 @@ static uint32 centry_uint32(struct cache_entry *centry)
 static uint16 centry_uint16(struct cache_entry *centry)
 {
        uint16 ret;
 static uint16 centry_uint16(struct cache_entry *centry)
 {
        uint16 ret;
-       if (centry->len - centry->ofs < 2) {
-               DEBUG(0,("centry corruption? needed 2 bytes, have %d\n", 
-                        centry->len - centry->ofs));
-               smb_panic("centry_uint16");
+       if (!centry_check_bytes(centry, 2)) {
+               smb_panic_fn("centry_uint16");
        }
        ret = CVAL(centry->data, centry->ofs);
        centry->ofs += 2;
        }
        ret = CVAL(centry->data, centry->ofs);
        centry->ofs += 2;
@@ -194,10 +251,8 @@ static uint16 centry_uint16(struct cache_entry *centry)
 static uint8 centry_uint8(struct cache_entry *centry)
 {
        uint8 ret;
 static uint8 centry_uint8(struct cache_entry *centry)
 {
        uint8 ret;
-       if (centry->len - centry->ofs < 1) {
-               DEBUG(0,("centry corruption? needed 1 bytes, have %d\n", 
-                        centry->len - centry->ofs));
-               smb_panic("centry_uint32");
+       if (!centry_check_bytes(centry, 1)) {
+               smb_panic_fn("centry_uint8");
        }
        ret = CVAL(centry->data, centry->ofs);
        centry->ofs += 1;
        }
        ret = CVAL(centry->data, centry->ofs);
        centry->ofs += 1;
@@ -210,10 +265,8 @@ static uint8 centry_uint8(struct cache_entry *centry)
 static NTTIME centry_nttime(struct cache_entry *centry)
 {
        NTTIME ret;
 static NTTIME centry_nttime(struct cache_entry *centry)
 {
        NTTIME ret;
-       if (centry->len - centry->ofs < 8) {
-               DEBUG(0,("centry corruption? needed 8 bytes, have %d\n", 
-                        centry->len - centry->ofs));
-               smb_panic("centry_nttime");
+       if (!centry_check_bytes(centry, 8)) {
+               smb_panic_fn("centry_nttime");
        }
        ret = IVAL(centry->data, centry->ofs);
        centry->ofs += 4;
        }
        ret = IVAL(centry->data, centry->ofs);
        centry->ofs += 4;
@@ -223,19 +276,11 @@ static NTTIME centry_nttime(struct cache_entry *centry)
 }
 
 /*
 }
 
 /*
-  pull a time_t from a cache entry 
+  pull a time_t from a cache entry. time_t stored portably as a 64-bit time.
 */
 static time_t centry_time(struct cache_entry *centry)
 {
 */
 static time_t centry_time(struct cache_entry *centry)
 {
-       time_t ret;
-       if (centry->len - centry->ofs < sizeof(time_t)) {
-               DEBUG(0,("centry corruption? needed %u bytes, have %u\n", 
-                        (unsigned int)sizeof(time_t), (unsigned int)(centry->len - centry->ofs)));
-               smb_panic("centry_time");
-       }
-       ret = IVAL(centry->data, centry->ofs); /* FIXME: correct ? */
-       centry->ofs += sizeof(time_t);
-       return ret;
+       return (time_t)centry_nttime(centry);
 }
 
 /* pull a string from a cache entry, using the supplied
 }
 
 /* pull a string from a cache entry, using the supplied
@@ -253,15 +298,13 @@ static char *centry_string(struct cache_entry *centry, TALLOC_CTX *mem_ctx)
                return NULL;
        }
 
                return NULL;
        }
 
-       if (centry->len - centry->ofs < len) {
-               DEBUG(0,("centry corruption? needed %d bytes, have %d\n", 
-                        len, centry->len - centry->ofs));
-               smb_panic("centry_string");
+       if (!centry_check_bytes(centry, (size_t)len)) {
+               smb_panic_fn("centry_string");
        }
 
        ret = TALLOC_ARRAY(mem_ctx, char, len+1);
        if (!ret) {
        }
 
        ret = TALLOC_ARRAY(mem_ctx, char, len+1);
        if (!ret) {
-               smb_panic("centry_string out of memory\n");
+               smb_panic_fn("centry_string out of memory\n");
        }
        memcpy(ret,centry->data + centry->ofs, len);
        ret[len] = 0;
        }
        memcpy(ret,centry->data + centry->ofs, len);
        ret[len] = 0;
@@ -285,15 +328,13 @@ static char *centry_hash16(struct cache_entry *centry, TALLOC_CTX *mem_ctx)
                return NULL;
        }
 
                return NULL;
        }
 
-       if (centry->len - centry->ofs < 16) {
-               DEBUG(0,("centry corruption? needed 16 bytes, have %d\n", 
-                        centry->len - centry->ofs));
+       if (!centry_check_bytes(centry, 16)) {
                return NULL;
        }
 
        ret = TALLOC_ARRAY(mem_ctx, char, 16);
        if (!ret) {
                return NULL;
        }
 
        ret = TALLOC_ARRAY(mem_ctx, char, 16);
        if (!ret) {
-               smb_panic("centry_hash out of memory\n");
+               smb_panic_fn("centry_hash out of memory\n");
        }
        memcpy(ret,centry->data + centry->ofs, 16);
        centry->ofs += 16;
        }
        memcpy(ret,centry->data + centry->ofs, 16);
        centry->ofs += 16;
@@ -372,9 +413,9 @@ static NTSTATUS fetch_cache_seqnum( struct winbindd_domain *domain, time_t now )
 
 static NTSTATUS store_cache_seqnum( struct winbindd_domain *domain )
 {
 
 static NTSTATUS store_cache_seqnum( struct winbindd_domain *domain )
 {
-       TDB_DATA data, key;
+       TDB_DATA data;
        fstring key_str;
        fstring key_str;
-       char buf[8];
+       uint8 buf[8];
        
        if (!wcache->tdb) {
                DEBUG(10,("store_cache_seqnum: tdb == NULL\n"));
        
        if (!wcache->tdb) {
                DEBUG(10,("store_cache_seqnum: tdb == NULL\n"));
@@ -382,15 +423,13 @@ static NTSTATUS store_cache_seqnum( struct winbindd_domain *domain )
        }
                
        fstr_sprintf( key_str, "SEQNUM/%s", domain->name );
        }
                
        fstr_sprintf( key_str, "SEQNUM/%s", domain->name );
-       key.dptr = key_str;
-       key.dsize = strlen(key_str)+1;
        
        SIVAL(buf, 0, domain->sequence_number);
        SIVAL(buf, 4, domain->last_seq_check);
        data.dptr = buf;
        data.dsize = 8;
        
        
        SIVAL(buf, 0, domain->sequence_number);
        SIVAL(buf, 4, domain->last_seq_check);
        data.dptr = buf;
        data.dsize = 8;
        
-       if ( tdb_store( wcache->tdb, key, data, TDB_REPLACE) == -1 ) {
+       if ( tdb_store_bystring( wcache->tdb, key_str, data, TDB_REPLACE) == -1 ) {
                DEBUG(10,("store_cache_seqnum: tdb_store fail key [%s]\n", key_str ));
                return NT_STATUS_UNSUCCESSFUL;
        }
                DEBUG(10,("store_cache_seqnum: tdb_store fail key [%s]\n", key_str ));
                return NT_STATUS_UNSUCCESSFUL;
        }
@@ -414,6 +453,10 @@ static void refresh_sequence_number(struct winbindd_domain *domain, BOOL force)
        time_t t = time(NULL);
        unsigned cache_time = lp_winbind_cache_time();
 
        time_t t = time(NULL);
        unsigned cache_time = lp_winbind_cache_time();
 
+       if ( IS_DOMAIN_OFFLINE(domain) ) {
+               return;
+       }
+       
        get_cache( domain );
 
 #if 0  /* JERRY -- disable as the default cache time is now 5 minutes */
        get_cache( domain );
 
 #if 0  /* JERRY -- disable as the default cache time is now 5 minutes */
@@ -439,9 +482,17 @@ static void refresh_sequence_number(struct winbindd_domain *domain, BOOL force)
                goto done;      
 
        /* important! make sure that we know if this is a native 
                goto done;      
 
        /* important! make sure that we know if this is a native 
-          mode domain or not */
+          mode domain or not.  And that we can contact it. */
+
+       if ( winbindd_can_contact_domain( domain ) ) {          
+               status = domain->backend->sequence_number(domain, 
+                                                         &domain->sequence_number);
+       } else {
+               /* just use the current time */
+               status = NT_STATUS_OK;
+               domain->sequence_number = time(NULL);
+       }
 
 
-       status = domain->backend->sequence_number(domain, &domain->sequence_number);
 
        /* the above call could have set our domain->backend to NULL when
         * coming from offline to online mode, make sure to reinitialize the
 
        /* the above call could have set our domain->backend to NULL when
         * coming from offline to online mode, make sure to reinitialize the
@@ -518,8 +569,7 @@ static struct cache_entry *wcache_fetch_raw(char *kstr)
        struct cache_entry *centry;
        TDB_DATA key;
 
        struct cache_entry *centry;
        TDB_DATA key;
 
-       key.dptr = kstr;
-       key.dsize = strlen(kstr);
+       key = string_tdb_data(kstr);
        data = tdb_fetch(wcache->tdb, key);
        if (!data.dptr) {
                /* a cache miss */
        data = tdb_fetch(wcache->tdb, key);
        if (!data.dptr) {
                /* a cache miss */
@@ -559,8 +609,6 @@ static struct cache_entry *wcache_fetch(struct winbind_cache *cache,
        char *kstr;
        struct cache_entry *centry;
 
        char *kstr;
        struct cache_entry *centry;
 
-       extern BOOL opt_nocache;
-
        if (opt_nocache) {
                return NULL;
        }
        if (opt_nocache) {
                return NULL;
        }
@@ -605,8 +653,7 @@ static void wcache_delete(const char *format, ...)
        smb_xvasprintf(&kstr, format, ap);
        va_end(ap);
 
        smb_xvasprintf(&kstr, format, ap);
        va_end(ap);
 
-       key.dptr = kstr;
-       key.dsize = strlen(kstr);
+       key = string_tdb_data(kstr);
 
        tdb_delete(wcache->tdb, key);
        free(kstr);
 
        tdb_delete(wcache->tdb, key);
        free(kstr);
@@ -624,7 +671,7 @@ static void centry_expand(struct cache_entry *centry, uint32 len)
                                         centry->len);
        if (!centry->data) {
                DEBUG(0,("out of memory: needed %d bytes in centry_expand\n", centry->len));
                                         centry->len);
        if (!centry->data) {
                DEBUG(0,("out of memory: needed %d bytes in centry_expand\n", centry->len));
-               smb_panic("out of memory in centry_expand");
+               smb_panic_fn("out of memory in centry_expand");
        }
 }
 
        }
 }
 
@@ -713,13 +760,13 @@ static void centry_put_nttime(struct cache_entry *centry, NTTIME nt)
 }
 
 /*
 }
 
 /*
-  push a time_t into a centry 
+  push a time_t into a centry - use a 64 bit size.
+  NTTIME here is being used as a convenient 64-bit size.
 */
 static void centry_put_time(struct cache_entry *centry, time_t t)
 {
 */
 static void centry_put_time(struct cache_entry *centry, time_t t)
 {
-       centry_expand(centry, sizeof(time_t));
-       SIVAL(centry->data, centry->ofs, t); /* FIXME: is this correct ?? */
-       centry->ofs += sizeof(time_t);
+       NTTIME nt = (NTTIME)t;
+       centry_put_nttime(centry, nt);
 }
 
 /*
 }
 
 /*
@@ -757,9 +804,8 @@ static void centry_end(struct cache_entry *centry, const char *format, ...)
        smb_xvasprintf(&kstr, format, ap);
        va_end(ap);
 
        smb_xvasprintf(&kstr, format, ap);
        va_end(ap);
 
-       key.dptr = kstr;
-       key.dsize = strlen(kstr);
-       data.dptr = (char *)centry->data;
+       key = string_tdb_data(kstr);
+       data.dptr = centry->data;
        data.dsize = centry->ofs;
 
        tdb_store(wcache->tdb, key, data, TDB_REPLACE);
        data.dsize = centry->ofs;
 
        tdb_store(wcache->tdb, key, data, TDB_REPLACE);
@@ -782,8 +828,8 @@ static void wcache_save_name_to_sid(struct winbindd_domain *domain,
        fstrcpy(uname, name);
        strupper_m(uname);
        centry_end(centry, "NS/%s/%s", domain_name, uname);
        fstrcpy(uname, name);
        strupper_m(uname);
        centry_end(centry, "NS/%s/%s", domain_name, uname);
-       DEBUG(10,("wcache_save_name_to_sid: %s\\%s -> %s\n", domain_name, uname,
-                 sid_string_static(sid)));
+       DEBUG(10,("wcache_save_name_to_sid: %s\\%s -> %s (%s)\n", domain_name, uname,
+                 sid_string_static(sid), nt_errstr(status)));
        centry_free(centry);
 }
 
        centry_free(centry);
 }
 
@@ -806,7 +852,8 @@ static void wcache_save_sid_to_name(struct winbindd_domain *domain, NTSTATUS sta
                centry_put_string(centry, name);
        }
        centry_end(centry, "SN/%s", sid_to_string(sid_string, sid));
                centry_put_string(centry, name);
        }
        centry_end(centry, "SN/%s", sid_to_string(sid_string, sid));
-       DEBUG(10,("wcache_save_sid_to_name: %s -> %s\n", sid_string, name));
+       DEBUG(10,("wcache_save_sid_to_name: %s -> %s (%s)\n", sid_string, 
+                 name, nt_errstr(status)));
        centry_free(centry);
 }
 
        centry_free(centry);
 }
 
@@ -827,6 +874,7 @@ static void wcache_save_user(struct winbindd_domain *domain, NTSTATUS status, WI
        centry_put_string(centry, info->full_name);
        centry_put_string(centry, info->homedir);
        centry_put_string(centry, info->shell);
        centry_put_string(centry, info->full_name);
        centry_put_string(centry, info->homedir);
        centry_put_string(centry, info->shell);
+       centry_put_uint32(centry, info->primary_gid);
        centry_put_sid(centry, &info->user_sid);
        centry_put_sid(centry, &info->group_sid);
        centry_end(centry, "U/%s", sid_to_string(sid_string, &info->user_sid));
        centry_put_sid(centry, &info->user_sid);
        centry_put_sid(centry, &info->group_sid);
        centry_end(centry, "U/%s", sid_to_string(sid_string, &info->user_sid));
@@ -895,7 +943,7 @@ NTSTATUS wcache_cached_creds_exist(struct winbindd_domain *domain, const DOM_SID
 
        fstr_sprintf(key_str, "CRED/%s", sid_string_static(sid));
 
 
        fstr_sprintf(key_str, "CRED/%s", sid_string_static(sid));
 
-       data = tdb_fetch(cache->tdb, make_tdb_data(key_str, strlen(key_str)));
+       data = tdb_fetch(cache->tdb, string_tdb_data(key_str));
        if (!data.dptr) {
                return NT_STATUS_OBJECT_NAME_NOT_FOUND;
        }
        if (!data.dptr) {
                return NT_STATUS_OBJECT_NAME_NOT_FOUND;
        }
@@ -969,9 +1017,9 @@ NTSTATUS wcache_get_creds(struct winbindd_domain *domain,
        }
 
 #if DEBUG_PASSWORD
        }
 
 #if DEBUG_PASSWORD
-       dump_data(100, (const char *)*cached_nt_pass, NT_HASH_LEN);
+       dump_data(100, *cached_nt_pass, NT_HASH_LEN);
        if (*cached_salt) {
        if (*cached_salt) {
-               dump_data(100, (const char *)*cached_salt, NT_HASH_LEN);
+               dump_data(100, *cached_salt, NT_HASH_LEN);
        }
 #endif
        status = centry->status;
        }
 #endif
        status = centry->status;
@@ -1010,7 +1058,7 @@ NTSTATUS wcache_save_creds(struct winbindd_domain *domain,
        }
 
 #if DEBUG_PASSWORD
        }
 
 #if DEBUG_PASSWORD
-       dump_data(100, (const char *)nt_pass, NT_HASH_LEN);
+       dump_data(100, nt_pass, NT_HASH_LEN);
 #endif
 
        centry_put_time(centry, time(NULL));
 #endif
 
        centry_put_time(centry, time(NULL));
@@ -1055,8 +1103,9 @@ static NTSTATUS query_user_list(struct winbindd_domain *domain,
                goto do_cached;
 
        (*info) = TALLOC_ARRAY(mem_ctx, WINBIND_USERINFO, *num_entries);
                goto do_cached;
 
        (*info) = TALLOC_ARRAY(mem_ctx, WINBIND_USERINFO, *num_entries);
-       if (! (*info))
-               smb_panic("query_user_list out of memory");
+       if (! (*info)) {
+               smb_panic_fn("query_user_list out of memory");
+       }
        for (i=0; i<(*num_entries); i++) {
                (*info)[i].acct_name = centry_string(centry, mem_ctx);
                (*info)[i].full_name = centry_string(centry, mem_ctx);
        for (i=0; i<(*num_entries); i++) {
                (*info)[i].acct_name = centry_string(centry, mem_ctx);
                (*info)[i].full_name = centry_string(centry, mem_ctx);
@@ -1170,8 +1219,9 @@ static NTSTATUS enum_dom_groups(struct winbindd_domain *domain,
                goto do_cached;
 
        (*info) = TALLOC_ARRAY(mem_ctx, struct acct_info, *num_entries);
                goto do_cached;
 
        (*info) = TALLOC_ARRAY(mem_ctx, struct acct_info, *num_entries);
-       if (! (*info))
-               smb_panic("enum_dom_groups out of memory");
+       if (! (*info)) {
+               smb_panic_fn("enum_dom_groups out of memory");
+       }
        for (i=0; i<(*num_entries); i++) {
                fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx));
                fstrcpy((*info)[i].acct_desc, centry_string(centry, mem_ctx));
        for (i=0; i<(*num_entries); i++) {
                fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx));
                fstrcpy((*info)[i].acct_desc, centry_string(centry, mem_ctx));
@@ -1243,8 +1293,9 @@ static NTSTATUS enum_local_groups(struct winbindd_domain *domain,
                goto do_cached;
 
        (*info) = TALLOC_ARRAY(mem_ctx, struct acct_info, *num_entries);
                goto do_cached;
 
        (*info) = TALLOC_ARRAY(mem_ctx, struct acct_info, *num_entries);
-       if (! (*info))
-               smb_panic("enum_dom_groups out of memory");
+       if (! (*info)) {
+               smb_panic_fn("enum_dom_groups out of memory");
+       }
        for (i=0; i<(*num_entries); i++) {
                fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx));
                fstrcpy((*info)[i].acct_desc, centry_string(centry, mem_ctx));
        for (i=0; i<(*num_entries); i++) {
                fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx));
                fstrcpy((*info)[i].acct_desc, centry_string(centry, mem_ctx));
@@ -1305,6 +1356,7 @@ skip_save:
 /* convert a single name to a sid in a domain */
 static NTSTATUS name_to_sid(struct winbindd_domain *domain,
                            TALLOC_CTX *mem_ctx,
 /* convert a single name to a sid in a domain */
 static NTSTATUS name_to_sid(struct winbindd_domain *domain,
                            TALLOC_CTX *mem_ctx,
+                           enum winbindd_cmd orig_cmd,
                            const char *domain_name,
                            const char *name,
                            DOM_SID *sid,
                            const char *domain_name,
                            const char *name,
                            DOM_SID *sid,
@@ -1352,7 +1404,8 @@ do_query:
        DEBUG(10,("name_to_sid: [Cached] - doing backend query for name for domain %s\n",
                domain->name ));
 
        DEBUG(10,("name_to_sid: [Cached] - doing backend query for name for domain %s\n",
                domain->name ));
 
-       status = domain->backend->name_to_sid(domain, mem_ctx, domain_name, name, sid, type);
+       status = domain->backend->name_to_sid(domain, mem_ctx, orig_cmd, 
+                                             domain_name, name, sid, type);
 
        /* and save it */
        refresh_sequence_number(domain, False);
 
        /* and save it */
        refresh_sequence_number(domain, False);
@@ -1589,6 +1642,7 @@ static NTSTATUS query_user(struct winbindd_domain *domain,
        info->full_name = centry_string(centry, mem_ctx);
        info->homedir = centry_string(centry, mem_ctx);
        info->shell = centry_string(centry, mem_ctx);
        info->full_name = centry_string(centry, mem_ctx);
        info->homedir = centry_string(centry, mem_ctx);
        info->shell = centry_string(centry, mem_ctx);
+       info->primary_gid = centry_uint32(centry);
        centry_sid(centry, mem_ctx, &info->user_sid);
        centry_sid(centry, mem_ctx, &info->group_sid);
        status = centry->status;
        centry_sid(centry, mem_ctx, &info->user_sid);
        centry_sid(centry, mem_ctx, &info->group_sid);
        status = centry->status;
@@ -1607,7 +1661,7 @@ do_query:
        if (!NT_STATUS_IS_OK(domain->last_status))
                return domain->last_status;
        
        if (!NT_STATUS_IS_OK(domain->last_status))
                return domain->last_status;
        
-       DEBUG(10,("sid_to_name: [Cached] - doing backend query for info for domain %s\n",
+       DEBUG(10,("query_user: [Cached] - doing backend query for info for domain %s\n",
                domain->name ));
 
        status = domain->backend->query_user(domain, mem_ctx, user_sid, info);
                domain->name ));
 
        status = domain->backend->query_user(domain, mem_ctx, user_sid, info);
@@ -1658,8 +1712,9 @@ static NTSTATUS lookup_usergroups(struct winbindd_domain *domain,
                goto do_cached;
 
        (*user_gids) = TALLOC_ARRAY(mem_ctx, DOM_SID, *num_groups);
                goto do_cached;
 
        (*user_gids) = TALLOC_ARRAY(mem_ctx, DOM_SID, *num_groups);
-       if (! (*user_gids))
-               smb_panic("lookup_usergroups out of memory");
+       if (! (*user_gids)) {
+               smb_panic_fn("lookup_usergroups out of memory");
+       }
        for (i=0; i<(*num_groups); i++) {
                centry_sid(centry, mem_ctx, &(*user_gids)[i]);
        }
        for (i=0; i<(*num_groups); i++) {
                centry_sid(centry, mem_ctx, &(*user_gids)[i]);
        }
@@ -1687,6 +1742,9 @@ do_query:
 
        status = domain->backend->lookup_usergroups(domain, mem_ctx, user_sid, num_groups, user_gids);
 
 
        status = domain->backend->lookup_usergroups(domain, mem_ctx, user_sid, num_groups, user_gids);
 
+       if ( NT_STATUS_EQUAL(status, NT_STATUS_SYNCHRONIZATION_REQUIRED) )
+               goto skip_save;
+       
        /* and save it */
        refresh_sequence_number(domain, False);
        centry = centry_start(domain, status);
        /* and save it */
        refresh_sequence_number(domain, False);
        centry = centry_start(domain, status);
@@ -1741,11 +1799,15 @@ static NTSTATUS lookup_useraliases(struct winbindd_domain *domain,
        *num_aliases = centry_uint32(centry);
        *alias_rids = NULL;
 
        *num_aliases = centry_uint32(centry);
        *alias_rids = NULL;
 
-       (*alias_rids) = TALLOC_ARRAY(mem_ctx, uint32, *num_aliases);
+       if (*num_aliases) {
+               (*alias_rids) = TALLOC_ARRAY(mem_ctx, uint32, *num_aliases);
 
 
-       if ((*num_aliases != 0) && ((*alias_rids) == NULL)) {
-               centry_free(centry);
-               return NT_STATUS_NO_MEMORY;
+               if ((*alias_rids) == NULL) {
+                       centry_free(centry);
+                       return NT_STATUS_NO_MEMORY;
+               }
+       } else {
+               (*alias_rids) = NULL;
        }
 
        for (i=0; i<(*num_aliases); i++)
        }
 
        for (i=0; i<(*num_aliases); i++)
@@ -1818,7 +1880,7 @@ static NTSTATUS lookup_groupmem(struct winbindd_domain *domain,
        (*name_types) = TALLOC_ARRAY(mem_ctx, uint32, *num_names);
 
        if (! (*sid_mem) || ! (*names) || ! (*name_types)) {
        (*name_types) = TALLOC_ARRAY(mem_ctx, uint32, *num_names);
 
        if (! (*sid_mem) || ! (*names) || ! (*name_types)) {
-               smb_panic("lookup_groupmem out of memory");
+               smb_panic_fn("lookup_groupmem out of memory");
        }
 
        for (i=0; i<(*num_names); i++) {
        }
 
        for (i=0; i<(*num_names); i++) {
@@ -1907,13 +1969,19 @@ static NTSTATUS trusted_domains(struct winbindd_domain *domain,
  
        *num_domains = centry_uint32(centry);
        
  
        *num_domains = centry_uint32(centry);
        
-       (*names)        = TALLOC_ARRAY(mem_ctx, char *, *num_domains);
-       (*alt_names)    = TALLOC_ARRAY(mem_ctx, char *, *num_domains);
-       (*dom_sids)     = TALLOC_ARRAY(mem_ctx, DOM_SID, *num_domains);
+       if (*num_domains) {
+               (*names)        = TALLOC_ARRAY(mem_ctx, char *, *num_domains);
+               (*alt_names)    = TALLOC_ARRAY(mem_ctx, char *, *num_domains);
+               (*dom_sids)     = TALLOC_ARRAY(mem_ctx, DOM_SID, *num_domains);
  
  
-       if (! (*dom_sids) || ! (*names) || ! (*alt_names)) {
-               smb_panic("trusted_domains out of memory");
-       }
+               if (! (*dom_sids) || ! (*names) || ! (*alt_names)) {
+                       smb_panic_fn("trusted_domains out of memory");
+               }
+       } else {
+               (*names) = NULL;
+               (*alt_names) = NULL;
+               (*dom_sids) = NULL;
+       }
  
        for (i=0; i<(*num_domains); i++) {
                (*names)[i] = centry_string(centry, mem_ctx);
  
        for (i=0; i<(*num_domains); i++) {
                (*names)[i] = centry_string(centry, mem_ctx);
@@ -1954,6 +2022,10 @@ do_query:
                status = NT_STATUS_OK;
        }
 
                status = NT_STATUS_OK;
        }
 
+
+#if 0    /* Disabled as we want the trust dom list to be managed by
+           the main parent and always to make the query.  --jerry */
+
        /* and save it */
        refresh_sequence_number(domain, False);
  
        /* and save it */
        refresh_sequence_number(domain, False);
  
@@ -1974,6 +2046,8 @@ do_query:
        centry_free(centry);
  
 skip_save:
        centry_free(centry);
  
 skip_save:
+#endif
+
        return status;
 }      
 
        return status;
 }      
 
@@ -2082,8 +2156,8 @@ do_query:
 static int traverse_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf, 
                       void *state)
 {
 static int traverse_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf, 
                       void *state)
 {
-       if (strncmp(kbuf.dptr, "UL/", 3) == 0 ||
-           strncmp(kbuf.dptr, "GL/", 3) == 0)
+       if (strncmp((const char *)kbuf.dptr, "UL/", 3) == 0 ||
+           strncmp((const char *)kbuf.dptr, "GL/", 3) == 0)
                tdb_delete(the_tdb, kbuf);
 
        return 0;
                tdb_delete(the_tdb, kbuf);
 
        return 0;
@@ -2095,7 +2169,14 @@ void wcache_invalidate_samlogon(struct winbindd_domain *domain,
                                NET_USER_INFO_3 *info3)
 {
        struct winbind_cache *cache;
                                NET_USER_INFO_3 *info3)
 {
        struct winbind_cache *cache;
-       
+
+       /* dont clear cached U/SID and UG/SID entries when we want to logon
+        * offline - gd */
+
+       if (lp_winbind_offline_logon()) {
+               return;
+       }
+
        if (!domain)
                return;
 
        if (!domain)
                return;
 
@@ -2117,7 +2198,7 @@ void wcache_invalidate_cache(void)
        }
 }
 
        }
 }
 
-static BOOL init_wcache(void)
+BOOL init_wcache(void)
 {
        if (wcache == NULL) {
                wcache = SMB_XMALLOC_P(struct winbind_cache);
 {
        if (wcache == NULL) {
                wcache = SMB_XMALLOC_P(struct winbind_cache);
@@ -2141,6 +2222,61 @@ static BOOL init_wcache(void)
        return True;
 }
 
        return True;
 }
 
+/************************************************************************
+ This is called by the parent to initialize the cache file.
+ We don't need sophisticated locking here as we know we're the
+ only opener.
+************************************************************************/
+
+BOOL initialize_winbindd_cache(void)
+{
+       BOOL cache_bad = True;
+       uint32 vers;
+
+       if (!init_wcache()) {
+               DEBUG(0,("initialize_winbindd_cache: init_wcache failed.\n"));
+               return False;
+       }
+
+       /* Check version number. */
+       if (tdb_fetch_uint32(wcache->tdb, WINBINDD_CACHE_VERSION_KEYSTR, &vers) &&
+                       vers == WINBINDD_CACHE_VERSION) {
+               cache_bad = False;
+       }
+
+       if (cache_bad) {
+               DEBUG(0,("initialize_winbindd_cache: clearing cache "
+                       "and re-creating with version number %d\n",
+                       WINBINDD_CACHE_VERSION ));
+
+               tdb_close(wcache->tdb);
+               wcache->tdb = NULL;
+
+               if (unlink(lock_path("winbindd_cache.tdb")) == -1) {
+                       DEBUG(0,("initialize_winbindd_cache: unlink %s failed %s ",
+                               lock_path("winbindd_cache.tdb"),
+                               strerror(errno) ));
+                       return False;
+               }
+               if (!init_wcache()) {
+                       DEBUG(0,("initialize_winbindd_cache: re-initialization "
+                                       "init_wcache failed.\n"));
+                       return False;
+               }
+
+               /* Write the version. */
+               if (!tdb_store_uint32(wcache->tdb, WINBINDD_CACHE_VERSION_KEYSTR, WINBINDD_CACHE_VERSION)) {
+                       DEBUG(0,("initialize_winbindd_cache: version number store failed %s\n",
+                               tdb_errorstr(wcache->tdb) ));
+                       return False;
+               }
+       }
+
+       tdb_close(wcache->tdb);
+       wcache->tdb = NULL;
+       return True;
+}
+
 void cache_store_response(pid_t pid, struct winbindd_response *response)
 {
        fstring key_str;
 void cache_store_response(pid_t pid, struct winbindd_response *response)
 {
        fstring key_str;
@@ -2153,7 +2289,7 @@ void cache_store_response(pid_t pid, struct winbindd_response *response)
 
        fstr_sprintf(key_str, "DR/%d", pid);
        if (tdb_store(wcache->tdb, string_tdb_data(key_str), 
 
        fstr_sprintf(key_str, "DR/%d", pid);
        if (tdb_store(wcache->tdb, string_tdb_data(key_str), 
-                     make_tdb_data((const char *)response, sizeof(*response)),
+                     make_tdb_data((uint8 *)response, sizeof(*response)),
                      TDB_REPLACE) == -1)
                return;
 
                      TDB_REPLACE) == -1)
                return;
 
@@ -2167,7 +2303,7 @@ void cache_store_response(pid_t pid, struct winbindd_response *response)
 
        fstr_sprintf(key_str, "DE/%d", pid);
        if (tdb_store(wcache->tdb, string_tdb_data(key_str),
 
        fstr_sprintf(key_str, "DE/%d", pid);
        if (tdb_store(wcache->tdb, string_tdb_data(key_str),
-                     make_tdb_data((const char *)response->extra_data.data,
+                     make_tdb_data((uint8 *)response->extra_data.data,
                                    response->length - sizeof(*response)),
                      TDB_REPLACE) == 0)
                return;
                                    response->length - sizeof(*response)),
                      TDB_REPLACE) == 0)
                return;
@@ -2227,7 +2363,7 @@ BOOL cache_retrieve_response(pid_t pid, struct winbindd_response * response)
                return False;
        }
 
                return False;
        }
 
-       dump_data(11, data.dptr, data.dsize);
+       dump_data(11, (uint8 *)data.dptr, data.dsize);
 
        response->extra_data.data = data.dptr;
        return True;
 
        response->extra_data.data = data.dptr;
        return True;
@@ -2251,7 +2387,7 @@ void cache_cleanup_response(pid_t pid)
 
 
 BOOL lookup_cached_sid(TALLOC_CTX *mem_ctx, const DOM_SID *sid,
 
 
 BOOL lookup_cached_sid(TALLOC_CTX *mem_ctx, const DOM_SID *sid,
-                      const char **domain_name, const char **name,
+                      char **domain_name, char **name,
                       enum lsa_SidType *type)
 {
        struct winbindd_domain *domain;
                       enum lsa_SidType *type)
 {
        struct winbindd_domain *domain;
@@ -2297,6 +2433,7 @@ BOOL lookup_cached_name(TALLOC_CTX *mem_ctx,
        struct cache_entry *centry = NULL;
        NTSTATUS status;
        fstring uname;
        struct cache_entry *centry = NULL;
        NTSTATUS status;
        fstring uname;
+       BOOL original_online_state;     
 
        domain = find_lookup_domain_from_name(domain_name);
        if (domain == NULL) {
 
        domain = find_lookup_domain_from_name(domain_name);
        if (domain == NULL) {
@@ -2312,7 +2449,14 @@ BOOL lookup_cached_name(TALLOC_CTX *mem_ctx,
        fstrcpy(uname, name);
        strupper_m(uname);
        
        fstrcpy(uname, name);
        strupper_m(uname);
        
+       /* If we are doing a cached logon, temporarily set the domain
+          offline so the cache won't expire the entry */
+       
+       original_online_state = domain->online;
+       domain->online = False;
        centry = wcache_fetch(cache, domain, "NS/%s/%s", domain_name, uname);
        centry = wcache_fetch(cache, domain, "NS/%s/%s", domain_name, uname);
+       domain->online = original_online_state;
+       
        if (centry == NULL) {
                return False;
        }
        if (centry == NULL) {
                return False;
        }
@@ -2337,19 +2481,28 @@ void cache_name2sid(struct winbindd_domain *domain,
                                sid, type);
 }
 
                                sid, type);
 }
 
-/* delete all centries that don't have NT_STATUS_OK set */
+/*
+ * The original idea that this cache only contains centries has
+ * been blurred - now other stuff gets put in here. Ensure we
+ * ignore these things on cleanup.
+ */
+
 static int traverse_fn_cleanup(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, 
                               TDB_DATA dbuf, void *state)
 {
        struct cache_entry *centry;
 
 static int traverse_fn_cleanup(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, 
                               TDB_DATA dbuf, void *state)
 {
        struct cache_entry *centry;
 
-       centry = wcache_fetch_raw(kbuf.dptr);
+       if (is_non_centry_key(kbuf)) {
+               return 0;
+       }
+
+       centry = wcache_fetch_raw((char *)kbuf.dptr);
        if (!centry) {
                return 0;
        }
 
        if (!NT_STATUS_IS_OK(centry->status)) {
        if (!centry) {
                return 0;
        }
 
        if (!NT_STATUS_IS_OK(centry->status)) {
-               DEBUG(10,("deleting centry %s\n", kbuf.dptr));
+               DEBUG(10,("deleting centry %s\n", (const char *)kbuf.dptr));
                tdb_delete(the_tdb, kbuf);
        }
 
                tdb_delete(the_tdb, kbuf);
        }
 
@@ -2360,8 +2513,6 @@ static int traverse_fn_cleanup(TDB_CONTEXT *the_tdb, TDB_DATA kbuf,
 /* flush the cache */
 void wcache_flush_cache(void)
 {
 /* flush the cache */
 void wcache_flush_cache(void)
 {
-       extern BOOL opt_nocache;
-
        if (!wcache)
                return;
        if (wcache->tdb) {
        if (!wcache)
                return;
        if (wcache->tdb) {
@@ -2394,7 +2545,7 @@ static int traverse_fn_cached_creds(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DAT
 {
        int *cred_count = (int*)state;
  
 {
        int *cred_count = (int*)state;
  
-       if (strncmp(kbuf.dptr, "CRED/", 5) == 0) {
+       if (strncmp((const char *)kbuf.dptr, "CRED/", 5) == 0) {
                (*cred_count)++;
        }
        return 0;
                (*cred_count)++;
        }
        return 0;
@@ -2428,7 +2579,7 @@ static int traverse_fn_get_credlist(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DAT
 {
        struct cred_list *cred;
 
 {
        struct cred_list *cred;
 
-       if (strncmp(kbuf.dptr, "CRED/", 5) == 0) {
+       if (strncmp((const char *)kbuf.dptr, "CRED/", 5) == 0) {
 
                cred = SMB_MALLOC_P(struct cred_list);
                if (cred == NULL) {
 
                cred = SMB_MALLOC_P(struct cred_list);
                if (cred == NULL) {
@@ -2440,7 +2591,7 @@ static int traverse_fn_get_credlist(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DAT
                
                /* save a copy of the key */
                
                
                /* save a copy of the key */
                
-               fstrcpy(cred->name, kbuf.dptr);         
+               fstrcpy(cred->name, (const char *)kbuf.dptr);           
                DLIST_ADD(wcache_cred_list, cred);
        }
        
                DLIST_ADD(wcache_cred_list, cred);
        }
        
@@ -2486,7 +2637,7 @@ NTSTATUS wcache_remove_oldest_cached_creds(struct winbindd_domain *domain, const
                TDB_DATA data;
                time_t t;
 
                TDB_DATA data;
                time_t t;
 
-               data = tdb_fetch(cache->tdb, make_tdb_data(cred->name, strlen(cred->name)));
+               data = tdb_fetch(cache->tdb, string_tdb_data(cred->name));
                if (!data.dptr) {
                        DEBUG(10,("wcache_remove_oldest_cached_creds: entry for [%s] not found\n", 
                                cred->name));
                if (!data.dptr) {
                        DEBUG(10,("wcache_remove_oldest_cached_creds: entry for [%s] not found\n", 
                                cred->name));
@@ -2598,68 +2749,432 @@ BOOL get_global_winbindd_state_offline(void)
  Validate functions for all possible cache tdb keys.
 ***********************************************************************/
 
  Validate functions for all possible cache tdb keys.
 ***********************************************************************/
 
-static int validate_seqnum(TDB_DATA kbuf, TDB_DATA dbuf)
+static struct cache_entry *create_centry_validate(const char *kstr, TDB_DATA data, 
+                                                 struct tdb_validation_status *state)
+{
+       struct cache_entry *centry;
+
+       centry = SMB_XMALLOC_P(struct cache_entry);
+       centry->data = (unsigned char *)memdup(data.dptr, data.dsize);
+       if (!centry->data) {
+               SAFE_FREE(centry);
+               return NULL;
+       }
+       centry->len = data.dsize;
+       centry->ofs = 0;
+
+       if (centry->len < 8) {
+               /* huh? corrupt cache? */
+               DEBUG(0,("create_centry_validate: Corrupt cache for key %s (len < 8) ?\n", kstr));
+               centry_free(centry);
+               state->bad_entry = True;
+               state->success = False;
+               return NULL;
+       }
+
+       centry->status = NT_STATUS(centry_uint32(centry));
+       centry->sequence_number = centry_uint32(centry);
+       return centry;
+}
+
+static int validate_seqnum(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
+                          struct tdb_validation_status *state)
 {
 {
+       if (dbuf.dsize != 8) {
+               DEBUG(0,("validate_seqnum: Corrupt cache for key %s (len %u != 8) ?\n",
+                               keystr, (unsigned int)dbuf.dsize ));
+               state->bad_entry = True;
+               return 1;
+       }
        return 0;
 }
 
        return 0;
 }
 
-static int validate_ns(TDB_DATA kbuf, TDB_DATA dbuf)
+static int validate_ns(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
+                      struct tdb_validation_status *state)
 {
 {
+       struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
+       if (!centry) {
+               return 1;
+       }
+
+       (void)centry_uint32(centry);
+       if (NT_STATUS_IS_OK(centry->status)) {
+               DOM_SID sid;
+               (void)centry_sid(centry, mem_ctx, &sid);
+       }
+
+       centry_free(centry);
+
+       if (!(state->success)) {
+               return 1;
+       }
+       DEBUG(10,("validate_ns: %s ok\n", keystr));
        return 0;
 }
 
        return 0;
 }
 
-static int validate_sn(TDB_DATA kbuf, TDB_DATA dbuf)
+static int validate_sn(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
+                      struct tdb_validation_status *state)
 {
 {
+       struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
+       if (!centry) {
+               return 1;
+       }
+
+       if (NT_STATUS_IS_OK(centry->status)) {
+               (void)centry_uint32(centry);
+               (void)centry_string(centry, mem_ctx);
+               (void)centry_string(centry, mem_ctx);
+       }
+
+       centry_free(centry);
+
+       if (!(state->success)) {
+               return 1;
+       }
+       DEBUG(10,("validate_sn: %s ok\n", keystr));
        return 0;
 }
 
        return 0;
 }
 
-static int validate_u(TDB_DATA kbuf, TDB_DATA dbuf)
+static int validate_u(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
+                     struct tdb_validation_status *state)
 {
 {
+       struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
+       DOM_SID sid;
+
+       if (!centry) {
+               return 1;
+       }
+
+       (void)centry_string(centry, mem_ctx);
+       (void)centry_string(centry, mem_ctx);
+       (void)centry_string(centry, mem_ctx);
+       (void)centry_string(centry, mem_ctx);
+       (void)centry_uint32(centry);
+       (void)centry_sid(centry, mem_ctx, &sid);
+       (void)centry_sid(centry, mem_ctx, &sid);
+
+       centry_free(centry);
+
+       if (!(state->success)) {
+               return 1;
+       }
+       DEBUG(10,("validate_u: %s ok\n", keystr));
        return 0;
 }
 
        return 0;
 }
 
-static int validate_loc_pol(TDB_DATA kbuf, TDB_DATA dbuf)
+static int validate_loc_pol(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
+                           struct tdb_validation_status *state)
 {
 {
+       struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
+
+       if (!centry) {
+               return 1;
+       }
+
+       (void)centry_nttime(centry);
+       (void)centry_nttime(centry);
+       (void)centry_uint16(centry);
+
+       centry_free(centry);
+
+       if (!(state->success)) {
+               return 1;
+       }
+       DEBUG(10,("validate_loc_pol: %s ok\n", keystr));
        return 0;
 }
 
        return 0;
 }
 
-static int validate_pwd_pol(TDB_DATA kbuf, TDB_DATA dbuf)
+static int validate_pwd_pol(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
+                           struct tdb_validation_status *state)
 {
 {
+       struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
+
+       if (!centry) {
+               return 1;
+       }
+
+       (void)centry_uint16(centry);
+       (void)centry_uint16(centry);
+       (void)centry_uint32(centry);
+       (void)centry_nttime(centry);
+       (void)centry_nttime(centry);
+
+       centry_free(centry);
+
+       if (!(state->success)) {
+               return 1;
+       }
+       DEBUG(10,("validate_pwd_pol: %s ok\n", keystr));
        return 0;
 }
 
        return 0;
 }
 
-static int validate_cred(TDB_DATA kbuf, TDB_DATA dbuf)
+static int validate_cred(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
+                        struct tdb_validation_status *state)
 {
 {
+       struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
+
+       if (!centry) {
+               return 1;
+       }
+
+       (void)centry_time(centry);
+       (void)centry_hash16(centry, mem_ctx);
+
+       /* We only have 17 bytes more data in the salted cred case. */
+       if (centry->len - centry->ofs == 17) {
+               (void)centry_hash16(centry, mem_ctx);
+       }
+
+       centry_free(centry);
+
+       if (!(state->success)) {
+               return 1;
+       }
+       DEBUG(10,("validate_cred: %s ok\n", keystr));
        return 0;
 }
 
        return 0;
 }
 
-static int validate_ul(TDB_DATA kbuf, TDB_DATA dbuf)
+static int validate_ul(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
+                      struct tdb_validation_status *state)
 {
 {
+       struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
+       int32 num_entries, i;
+
+       if (!centry) {
+               return 1;
+       }
+
+       num_entries = (int32)centry_uint32(centry);
+
+       for (i=0; i< num_entries; i++) {
+               DOM_SID sid;
+               (void)centry_string(centry, mem_ctx);
+               (void)centry_string(centry, mem_ctx);
+               (void)centry_string(centry, mem_ctx);
+               (void)centry_string(centry, mem_ctx);
+               (void)centry_sid(centry, mem_ctx, &sid);
+               (void)centry_sid(centry, mem_ctx, &sid);
+       }
+
+       centry_free(centry);
+
+       if (!(state->success)) {
+               return 1;
+       }
+       DEBUG(10,("validate_ul: %s ok\n", keystr));
        return 0;
 }
 
        return 0;
 }
 
-static int validate_gl(TDB_DATA kbuf, TDB_DATA dbuf)
+static int validate_gl(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
+                      struct tdb_validation_status *state)
 {
 {
+       struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
+       int32 num_entries, i;
+
+       if (!centry) {
+               return 1;
+       }
+
+       num_entries = centry_uint32(centry);
+       
+       for (i=0; i< num_entries; i++) {
+               (void)centry_string(centry, mem_ctx);
+               (void)centry_string(centry, mem_ctx);
+               (void)centry_uint32(centry);
+       }
+
+       centry_free(centry);
+
+       if (!(state->success)) {
+               return 1;
+       }
+       DEBUG(10,("validate_gl: %s ok\n", keystr));
        return 0;
 }
 
        return 0;
 }
 
-static int validate_ug(TDB_DATA kbuf, TDB_DATA dbuf)
+static int validate_ug(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
+                      struct tdb_validation_status *state)
 {
 {
+       struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
+       int32 num_groups, i;
+
+       if (!centry) {
+               return 1;
+       }
+
+       num_groups = centry_uint32(centry);
+
+       for (i=0; i< num_groups; i++) {
+               DOM_SID sid;
+               centry_sid(centry, mem_ctx, &sid);
+       }
+
+       centry_free(centry);
+
+       if (!(state->success)) {
+               return 1;
+       }
+       DEBUG(10,("validate_ug: %s ok\n", keystr));
        return 0;
 }
 
        return 0;
 }
 
-static int validate_ua(TDB_DATA kbuf, TDB_DATA dbuf)
+static int validate_ua(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
+                      struct tdb_validation_status *state)
 {
 {
+       struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
+       int32 num_aliases, i;
+
+       if (!centry) {
+               return 1;
+       }
+
+       num_aliases = centry_uint32(centry);
+
+       for (i=0; i < num_aliases; i++) {
+               (void)centry_uint32(centry);
+       }
+
+       centry_free(centry);
+
+       if (!(state->success)) {
+               return 1;
+       }
+       DEBUG(10,("validate_ua: %s ok\n", keystr));
        return 0;
 }
 
        return 0;
 }
 
-static int validate_gm(TDB_DATA kbuf, TDB_DATA dbuf)
+static int validate_gm(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
+                      struct tdb_validation_status *state)
 {
 {
+       struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
+       int32 num_names, i;
+
+       if (!centry) {
+               return 1;
+       }
+
+       num_names = centry_uint32(centry);
+
+       for (i=0; i< num_names; i++) {
+               DOM_SID sid;
+               centry_sid(centry, mem_ctx, &sid);
+               (void)centry_string(centry, mem_ctx);
+               (void)centry_uint32(centry);
+       }
+
+       centry_free(centry);
+
+       if (!(state->success)) {
+               return 1;
+       }
+       DEBUG(10,("validate_gm: %s ok\n", keystr));
        return 0;
 }
 
        return 0;
 }
 
-static int validate_trustdoms(TDB_DATA kbuf, TDB_DATA dbuf)
+static int validate_dr(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
+                      struct tdb_validation_status *state)
 {
 {
+       /* Can't say anything about this other than must be nonzero. */
+       if (dbuf.dsize == 0) {
+               DEBUG(0,("validate_dr: Corrupt cache for key %s (len == 0) ?\n",
+                               keystr));
+               state->bad_entry = True;
+               state->success = False;
+               return 1;
+       }
+
+       DEBUG(10,("validate_dr: %s ok\n", keystr));
+       return 0;
+}
+
+static int validate_de(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
+                      struct tdb_validation_status *state)
+{
+       /* Can't say anything about this other than must be nonzero. */
+       if (dbuf.dsize == 0) {
+               DEBUG(0,("validate_de: Corrupt cache for key %s (len == 0) ?\n",
+                               keystr));
+               state->bad_entry = True;
+               state->success = False;
+               return 1;
+       }
+
+       DEBUG(10,("validate_de: %s ok\n", keystr));
+       return 0;
+}
+
+static int validate_trustdoms(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
+                             struct tdb_validation_status *state)
+{
+       struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
+       int32 num_domains, i;
+
+       if (!centry) {
+               return 1;
+       }
+
+       num_domains = centry_uint32(centry);
+       
+       for (i=0; i< num_domains; i++) {
+               DOM_SID sid;
+               (void)centry_string(centry, mem_ctx);
+               (void)centry_string(centry, mem_ctx);
+               (void)centry_sid(centry, mem_ctx, &sid);
+       }
+
+       centry_free(centry);
+
+       if (!(state->success)) {
+               return 1;
+       }
+       DEBUG(10,("validate_trustdoms: %s ok\n", keystr));
+       return 0;
+}
+
+static int validate_trustdomcache(TALLOC_CTX *mem_ctx, const char *keystr, 
+                                 TDB_DATA dbuf,
+                                 struct tdb_validation_status *state)
+{
+       if (dbuf.dsize == 0) {
+               DEBUG(0, ("validate_trustdomcache: Corrupt cache for "
+                         "key %s (len ==0) ?\n", keystr));
+               state->bad_entry = True;
+               state->success = False;
+               return 1;
+       }
+
+       DEBUG(10,    ("validate_trustdomcache: %s ok\n", keystr));
+       DEBUGADD(10, ("  Don't trust me, I am a DUMMY!\n"));
+       return 0;
+}
+
+static int validate_offline(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
+                           struct tdb_validation_status *state)
+{
+       if (dbuf.dsize != 4) {
+               DEBUG(0,("validate_offline: Corrupt cache for key %s (len %u != 4) ?\n",
+                               keystr, (unsigned int)dbuf.dsize ));
+               state->bad_entry = True;
+               state->success = False;
+               return 1;
+       }
+       DEBUG(10,("validate_offline: %s ok\n", keystr));
+       return 0;
+}
+
+static int validate_cache_version(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
+                                 struct tdb_validation_status *state)
+{
+       if (dbuf.dsize != 4) {
+               DEBUG(0, ("validate_cache_version: Corrupt cache for "
+                         "key %s (len %u != 4) ?\n", 
+                         keystr, (unsigned int)dbuf.dsize));
+               state->bad_entry = True;
+               state->success = False;
+               return 1;
+       }
+
+       DEBUG(10, ("validate_cache_version: %s ok\n", keystr));
        return 0;
 }
 
        return 0;
 }
 
@@ -2670,7 +3185,7 @@ static int validate_trustdoms(TDB_DATA kbuf, TDB_DATA dbuf)
 
 struct key_val_struct {
        const char *keyname;
 
 struct key_val_struct {
        const char *keyname;
-       int (*validate_data_fn)(TDB_DATA kbuf, TDB_DATA dbuf);
+       int (*validate_data_fn)(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf, struct tdb_validation_status* state);
 } key_val[] = {
        {"SEQNUM/", validate_seqnum},
        {"NS/", validate_ns},
 } key_val[] = {
        {"SEQNUM/", validate_seqnum},
        {"NS/", validate_ns},
@@ -2684,7 +3199,12 @@ struct key_val_struct {
        {"UG/", validate_ug},
        {"UA", validate_ua},
        {"GM/", validate_gm},
        {"UG/", validate_ug},
        {"UA", validate_ua},
        {"GM/", validate_gm},
+       {"DR/", validate_dr},
+       {"DE/", validate_de},
        {"TRUSTDOMS/", validate_trustdoms},
        {"TRUSTDOMS/", validate_trustdoms},
+       {"TRUSTDOMCACHE/", validate_trustdomcache},
+       {"WINBINDD_OFFLINE", validate_offline},
+       {WINBINDD_CACHE_VERSION_KEYSTR, validate_cache_version},
        {NULL, NULL}
 };
 
        {NULL, NULL}
 };
 
@@ -2696,51 +3216,60 @@ struct key_val_struct {
 static int cache_traverse_validate_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf, void *state)
 {
        int i;
 static int cache_traverse_validate_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf, void *state)
 {
        int i;
+       struct tdb_validation_status *v_state = (struct tdb_validation_status *)state;
 
 
-       /* Ensure key is valid. */
-       if (kbuf.dsize < 3) {
-               return 1; /* terminate. */
-       }
-       /* Ensure key is a string. */
-       if (kbuf.dptr[kbuf.dsize] != '\0') {
-               return 1; /* terminate. */
+       /* Paranoia check. */
+       if (kbuf.dsize > 1024) {
+               DEBUG(0,("cache_traverse_validate_fn: key length too large (%u) > 1024\n\n",
+                               (unsigned int)kbuf.dsize ));
+               return 1;
        }
 
        for (i = 0; key_val[i].keyname; i++) {
        }
 
        for (i = 0; key_val[i].keyname; i++) {
-               if (strncmp(key_val[i].keyname, kbuf.dptr, strlen(key_val[i].keyname)) == 0) {
-                       if (key_val[i].validate_data_fn(kbuf, dbuf)) {
-                               return 1; /* terminate. */
+               size_t namelen = strlen(key_val[i].keyname);
+               if (kbuf.dsize >= namelen && (
+                               strncmp(key_val[i].keyname, (const char *)kbuf.dptr, namelen)) == 0) {
+                       TALLOC_CTX *mem_ctx;
+                       char *keystr;
+                       int ret;
+
+                       keystr = SMB_MALLOC_ARRAY(char, kbuf.dsize+1);
+                       if (!keystr) {
+                               return 1;
                        }
                        }
-               }
-       }
-       return 0;
-}
+                       memcpy(keystr, kbuf.dptr, kbuf.dsize);
+                       keystr[kbuf.dsize] = '\0';
 
 
-/* Handle any signals generated when validating a possibly
-   bad cache tdb. */
+                       mem_ctx = talloc_init("validate_ctx");
+                       if (!mem_ctx) {
+                               SAFE_FREE(keystr);
+                               return 1;
+                       }
 
 
-static jmp_buf jmpbuf;
+                       ret = key_val[i].validate_data_fn(mem_ctx, keystr, dbuf, 
+                                                         v_state);
 
 
-#ifdef SIGSEGV
-static void sig_segv(int sig)
-{
-       longjmp(jmpbuf, SIGSEGV);
-}
-#endif
+                       SAFE_FREE(keystr);
+                       talloc_destroy(mem_ctx);
+                       return ret;
+               }
+       }
 
 
-#ifdef SIGBUS
-static void sig_bus(int sig)
-{
-       longjmp(jmpbuf, SIGBUS);
+       DEBUG(0,("cache_traverse_validate_fn: unknown cache entry\nkey :\n"));
+       dump_data(0, (uint8 *)kbuf.dptr, kbuf.dsize);
+       DEBUG(0,("data :\n"));
+       dump_data(0, (uint8 *)dbuf.dptr, dbuf.dsize);
+       v_state->unknown_key = True;
+       v_state->success = False;
+       return 1; /* terminate. */
 }
 }
-#endif
 
 
-#ifdef SIGABRT
-static void sig_abrt(int sig)
+static void validate_panic(const char *const why)
 {
 {
-       longjmp(jmpbuf, SIGABRT);
+        DEBUG(0,("validating cache: would panic %s\n", why ));
+       DEBUGADD(0, ("exiting instead (cache validation mode)\n"));
+       exit(47);
 }
 }
-#endif
 
 /***********************************************************************
  Try and validate every entry in the winbindd cache. If we fail here,
 
 /***********************************************************************
  Try and validate every entry in the winbindd cache. If we fail here,
@@ -2750,80 +3279,505 @@ static void sig_abrt(int sig)
 
 int winbindd_validate_cache(void)
 {
 
 int winbindd_validate_cache(void)
 {
-       BOOL ret = -1;
-       int fd = -1;
+       int ret = -1;
+       const char *tdb_path = lock_path("winbindd_cache.tdb");
        TDB_CONTEXT *tdb = NULL;
        TDB_CONTEXT *tdb = NULL;
-       const char *cache_path = lock_path("winbindd_cache.tdb");
 
 
-#ifdef SIGSEGV
-       void (*old_segv_handler)(int) = CatchSignal(SIGSEGV,SIGNAL_CAST sig_segv);
-#endif
-#ifdef SIGBUS
-       void (*old_bus_handler)(int) = CatchSignal(SIGBUS,SIGNAL_CAST sig_bus);
-#endif
-#ifdef SIGABRT
-       void (*old_abrt_handler)(int) = CatchSignal(SIGABRT,SIGNAL_CAST sig_abrt);
-#endif
+       DEBUG(10, ("winbindd_validate_cache: replacing panic function\n"));
+       smb_panic_fn = validate_panic;
 
 
-       switch((ret = setjmp(jmpbuf))) {
-               case 0:
-                       ret = -1;
+
+       tdb = tdb_open_log(tdb_path, 
+                          WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
+                          ( lp_winbind_offline_logon() 
+                            ? TDB_DEFAULT 
+                            : TDB_DEFAULT | TDB_CLEAR_IF_FIRST ),
+                          O_RDWR|O_CREAT, 
+                          0600);
+       if (!tdb) {
+               DEBUG(0, ("winbindd_validate_cache: "
+                         "error opening/initializing tdb\n"));
+               goto done;
+       }
+       tdb_close(tdb);
+
+       ret = tdb_validate(lock_path("winbindd_cache.tdb"),
+                          cache_traverse_validate_fn);
+
+       if (ret != 0) {
+               DEBUG(10, ("winbindd_validate_cache: validation not successful.\n"));
+               DEBUGADD(10, ("removing tdb %s.\n", tdb_path));
+               unlink(tdb_path);
+       }
+
+done:
+       DEBUG(10, ("winbindd_validate_cache: restoring panic function\n"));
+       smb_panic_fn = smb_panic;
+       return ret;
+}
+
+/*********************************************************************
+ ********************************************************************/
+
+static BOOL add_wbdomain_to_tdc_array( struct winbindd_domain *new_dom,
+                                      struct winbindd_tdc_domain **domains, 
+                                      size_t *num_domains )
+{
+       struct winbindd_tdc_domain *list = NULL;
+       size_t idx;
+       int i;
+       BOOL set_only = False;  
+       
+       /* don't allow duplicates */
+
+       idx = *num_domains;
+       list = *domains;
+       
+       for ( i=0; i< (*num_domains); i++ ) {
+               if ( strequal( new_dom->name, list[i].domain_name ) ) {
+                       DEBUG(10,("add_wbdomain_to_tdc_array: Found existing record for %s\n",
+                                 new_dom->name));
+                       idx = i;
+                       set_only = True;
+                       
                        break;
                        break;
-               case SIGSEGV:
-               case SIGBUS:
-               case SIGABRT:
-               default:
-                       goto out;
+               }
        }
 
        }
 
-       tdb = tdb_open_log(cache_path,
-                       WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
-                       lp_winbind_offline_logon() ? TDB_DEFAULT : (TDB_DEFAULT | TDB_CLEAR_IF_FIRST),
-                       O_RDWR|O_CREAT, 0600);
-       if (!tdb) {
-               goto out;
+       if ( !set_only ) {
+               if ( !*domains ) {
+                       list = TALLOC_ARRAY( NULL, struct winbindd_tdc_domain, 1 );
+                       idx = 0;
+               } else {
+                       list = TALLOC_REALLOC_ARRAY( *domains, *domains, 
+                                                    struct winbindd_tdc_domain,  
+                                                    (*num_domains)+1);
+                       idx = *num_domains;             
+               }
+
+               ZERO_STRUCT( list[idx] );
        }
 
        }
 
-       fd = tdb_fd(tdb);
+       if ( !list )
+               return False;
+
+       list[idx].domain_name = talloc_strdup( list, new_dom->name );
+       list[idx].dns_name = talloc_strdup( list, new_dom->alt_name );
+
+       if ( !is_null_sid( &new_dom->sid ) )
+               sid_copy( &list[idx].sid, &new_dom->sid );
+
+       if ( new_dom->domain_flags != 0x0 )
+               list[idx].trust_flags = new_dom->domain_flags;  
+
+       if ( new_dom->domain_type != 0x0 )
+               list[idx].trust_type = new_dom->domain_type;
 
 
-       /* Now traverse the cache to validate it. */
-       if (tdb_traverse(tdb, cache_traverse_validate_fn, NULL)) {
-               goto out;
+       if ( new_dom->domain_trust_attribs != 0x0 )
+               list[idx].trust_attribs = new_dom->domain_trust_attribs;
+       
+       if ( !set_only ) {
+               *domains = list;
+               *num_domains = idx + 1; 
        }
 
        }
 
-       DEBUG(10,("winbindd_validate_cache: cache %s is good\n", cache_path));
-       ret = 0; /* Cache is good. */
+       return True;    
+}
 
 
-  out:
+/*********************************************************************
+ ********************************************************************/
 
 
-       /* Ensure if we segv on exit we use the original
-          handlers to avoid a loop. */
+static TDB_DATA make_tdc_key( const char *domain_name )
+{
+       char *keystr = NULL;
+       TDB_DATA key = { NULL, 0 };
+       
+       if ( !domain_name ) {
+               DEBUG(5,("make_tdc_key: Keyname workgroup is NULL!\n"));
+               return key;
+       }
+              
+               
+       asprintf( &keystr, "TRUSTDOMCACHE/%s", domain_name );
+       key = string_term_tdb_data(keystr);
+       
+       return key;     
+}
 
 
-#ifdef SIGSEGV
-       CatchSignal(SIGSEGV,SIGNAL_CAST old_segv_handler);
-#endif
-#ifdef SIGBUS
-       CatchSignal(SIGBUS,SIGNAL_CAST old_bus_handler);
-#endif
-#ifdef SIGABRT
-       CatchSignal(SIGABRT,SIGNAL_CAST old_abrt_handler);
-#endif
+/*********************************************************************
+ ********************************************************************/
 
 
-       if (tdb) {
-               if (ret == 0) {
-                       tdb_close(tdb);
-               } else if (fd != -1) {
-                       close(fd);
+static int pack_tdc_domains( struct winbindd_tdc_domain *domains, 
+                            size_t num_domains,
+                            unsigned char **buf )
+{
+        unsigned char *buffer = NULL;
+       int len = 0;
+       int buflen = 0;
+       int i = 0;
+
+       DEBUG(10,("pack_tdc_domains: Packing %d trusted domains\n",
+                 (int)num_domains));
+       
+       buflen = 0;
+       
+ again: 
+       len = 0;
+       
+       /* Store the number of array items first */
+       len += tdb_pack( buffer+len, buflen-len, "d", 
+                        num_domains );
+
+       /* now pack each domain trust record */
+       for ( i=0; i<num_domains; i++ ) {
+
+               if ( buflen > 0 ) {
+                       DEBUG(10,("pack_tdc_domains: Packing domain %s (%s)\n",
+                                 domains[i].domain_name,
+                                 domains[i].dns_name ? domains[i].dns_name : "UNKNOWN" ));
                }
                }
+               
+               len += tdb_pack( buffer+len, buflen-len, "fffddd",
+                                domains[i].domain_name,
+                                domains[i].dns_name,
+                                sid_string_static(&domains[i].sid),
+                                domains[i].trust_flags,
+                                domains[i].trust_attribs,
+                                domains[i].trust_type );
+       }
+
+       if ( buflen < len ) {
+               SAFE_FREE(buffer);
+               if ( (buffer = SMB_MALLOC_ARRAY(unsigned char, len)) == NULL ) {
+                       DEBUG(0,("pack_tdc_domains: failed to alloc buffer!\n"));
+                       buflen = -1;
+                       goto done;
+               }
+               buflen = len;
+               goto again;
        }
 
        }
 
-       if (ret) {
-               unlink(cache_path);
+       *buf = buffer;  
+       
+ done: 
+       return buflen;  
+}
+
+/*********************************************************************
+ ********************************************************************/
+
+static size_t unpack_tdc_domains( unsigned char *buf, int buflen, 
+                                 struct winbindd_tdc_domain **domains )
+{
+       fstring domain_name, dns_name, sid_string;      
+       uint32 type, attribs, flags;
+       int num_domains;
+       int len = 0;
+       int i;
+       struct winbindd_tdc_domain *list = NULL;
+
+       /* get the number of domains */
+       len += tdb_unpack( buf+len, buflen-len, "d", &num_domains);
+       if ( len == -1 ) {
+               DEBUG(5,("unpack_tdc_domains: Failed to unpack domain array\n"));               
+               return 0;
        }
 
        }
 
-       return ret;
+       list = TALLOC_ARRAY( NULL, struct winbindd_tdc_domain, num_domains );
+       if ( !list ) {
+               DEBUG(0,("unpack_tdc_domains: Failed to talloc() domain list!\n"));
+               return 0;               
+       }
+       
+       for ( i=0; i<num_domains; i++ ) {
+               len += tdb_unpack( buf+len, buflen-len, "fffddd",
+                                  domain_name,
+                                  dns_name,
+                                  sid_string,
+                                  &flags,
+                                  &attribs,
+                                  &type );
+
+               if ( len == -1 ) {
+                       DEBUG(5,("unpack_tdc_domains: Failed to unpack domain array\n"));
+                       TALLOC_FREE( list );                    
+                       return 0;
+               }
+
+               DEBUG(11,("unpack_tdc_domains: Unpacking domain %s (%s) "
+                         "SID %s, flags = 0x%x, attribs = 0x%x, type = 0x%x\n",
+                         domain_name, dns_name, sid_string,
+                         flags, attribs, type));
+               
+               list[i].domain_name = talloc_strdup( list, domain_name );
+               list[i].dns_name = talloc_strdup( list, dns_name );
+               if ( !string_to_sid( &(list[i].sid), sid_string ) ) {                   
+                       DEBUG(10,("unpack_tdc_domains: no SID for domain %s\n",
+                                 domain_name));
+               }
+               list[i].trust_flags = flags;
+               list[i].trust_attribs = attribs;
+               list[i].trust_type = type;
+       }
+
+       *domains = list;
+       
+       return num_domains;
 }
 
 }
 
+/*********************************************************************
+ ********************************************************************/
+
+static BOOL wcache_tdc_store_list( struct winbindd_tdc_domain *domains, size_t num_domains )
+{
+       TDB_DATA key = make_tdc_key( lp_workgroup() );   
+       TDB_DATA data = { NULL, 0 };
+       int ret;
+       
+       if ( !key.dptr )
+               return False;
+       
+       /* See if we were asked to delete the cache entry */
+
+       if ( !domains ) {
+               ret = tdb_delete( wcache->tdb, key );
+               goto done;
+       }
+       
+       data.dsize = pack_tdc_domains( domains, num_domains, &data.dptr );
+       
+       if ( !data.dptr ) {
+               ret = -1;
+               goto done;
+       }
+               
+       ret = tdb_store( wcache->tdb, key, data, 0 );
+
+ done:
+       SAFE_FREE( data.dptr );
+       SAFE_FREE( key.dptr );
+       
+       return ( ret != -1 );   
+}
+
+/*********************************************************************
+ ********************************************************************/
+
+BOOL wcache_tdc_fetch_list( struct winbindd_tdc_domain **domains, size_t *num_domains )
+{
+       TDB_DATA key = make_tdc_key( lp_workgroup() );
+       TDB_DATA data = { NULL, 0 };
+
+       *domains = NULL;        
+       *num_domains = 0;       
+
+       if ( !key.dptr )
+               return False;
+       
+       data = tdb_fetch( wcache->tdb, key );
+
+       SAFE_FREE( key.dptr );
+       
+       if ( !data.dptr ) 
+               return False;
+       
+       *num_domains = unpack_tdc_domains( data.dptr, data.dsize, domains );
+
+       SAFE_FREE( data.dptr );
+       
+       if ( !*domains )
+               return False;
+
+       return True;    
+}
+
+/*********************************************************************
+ ********************************************************************/
+
+BOOL wcache_tdc_add_domain( struct winbindd_domain *domain )
+{
+       struct winbindd_tdc_domain *dom_list = NULL;
+       size_t num_domains = 0;
+       BOOL ret = False;       
+
+       DEBUG(10,("wcache_tdc_add_domain: Adding domain %s (%s), SID %s, "
+                 "flags = 0x%x, attributes = 0x%x, type = 0x%x\n",
+                 domain->name, domain->alt_name, 
+                 sid_string_static(&domain->sid),
+                 domain->domain_flags,
+                 domain->domain_trust_attribs,
+                 domain->domain_type));        
+       
+       if ( !init_wcache() ) {
+               return False;
+       }
+       
+       /* fetch the list */
+
+       wcache_tdc_fetch_list( &dom_list, &num_domains );
+       
+       /* add the new domain */
+
+       if ( !add_wbdomain_to_tdc_array( domain, &dom_list, &num_domains ) ) {
+               goto done;              
+       }       
+
+       /* pack the domain */
+
+       if ( !wcache_tdc_store_list( dom_list, num_domains ) ) {
+               goto done;              
+       }
+       
+       /* Success */
+
+       ret = True;     
+ done:
+       TALLOC_FREE( dom_list );
+       
+       return ret;     
+}
+
+/*********************************************************************
+ ********************************************************************/
+
+struct winbindd_tdc_domain * wcache_tdc_fetch_domain( TALLOC_CTX *ctx, const char *name )
+{
+       struct winbindd_tdc_domain *dom_list = NULL;
+       size_t num_domains = 0;
+       int i;
+       struct winbindd_tdc_domain *d = NULL;   
+
+       DEBUG(10,("wcache_tdc_fetch_domain: Searching for domain %s\n", name));
+
+       if ( !init_wcache() ) {
+               return False;
+       }
+       
+       /* fetch the list */
+
+       wcache_tdc_fetch_list( &dom_list, &num_domains );
+       
+       for ( i=0; i<num_domains; i++ ) {
+               if ( strequal(name, dom_list[i].domain_name) ||
+                    strequal(name, dom_list[i].dns_name) )
+               {
+                       DEBUG(10,("wcache_tdc_fetch_domain: Found domain %s\n",
+                                 name));
+                       
+                       d = TALLOC_P( ctx, struct winbindd_tdc_domain );
+                       if ( !d )
+                               break;                  
+                       
+                       d->domain_name = talloc_strdup( d, dom_list[i].domain_name );
+                       d->dns_name = talloc_strdup( d, dom_list[i].dns_name );
+                       sid_copy( &d->sid, &dom_list[i].sid );
+                       d->trust_flags   = dom_list[i].trust_flags;
+                       d->trust_type    = dom_list[i].trust_type;
+                       d->trust_attribs = dom_list[i].trust_attribs;
+
+                       break;
+               }
+       }
+
+        TALLOC_FREE( dom_list );
+       
+       return d;       
+}
+
+
+/*********************************************************************
+ ********************************************************************/
+
+void wcache_tdc_clear( void )
+{
+       if ( !init_wcache() )
+               return;
+
+       wcache_tdc_store_list( NULL, 0 );
+       
+       return; 
+}
+
+
+/*********************************************************************
+ ********************************************************************/
+
+static void wcache_save_user_pwinfo(struct winbindd_domain *domain, 
+                                   NTSTATUS status,
+                                   const DOM_SID *user_sid,
+                                   const char *homedir,
+                                   const char *shell,
+                                   const char *gecos,
+                                   uint32 gid)
+{
+       struct cache_entry *centry;
+
+       if ( (centry = centry_start(domain, status)) == NULL )
+               return;
+
+       centry_put_string( centry, homedir );
+       centry_put_string( centry, shell );
+       centry_put_string( centry, gecos );
+       centry_put_uint32( centry, gid );
+       
+       centry_end(centry, "NSS/PWINFO/%s", sid_string_static(user_sid) );
+
+       DEBUG(10,("wcache_save_user_pwinfo: %s\n", sid_string_static(user_sid) ));
+
+       centry_free(centry);
+}
+
+NTSTATUS nss_get_info_cached( struct winbindd_domain *domain, 
+                             const DOM_SID *user_sid,
+                             TALLOC_CTX *ctx,
+                             ADS_STRUCT *ads, LDAPMessage *msg,
+                             char **homedir, char **shell, char **gecos,
+                             gid_t *p_gid)
+{
+       struct winbind_cache *cache = get_cache(domain);
+       struct cache_entry *centry = NULL;
+       NTSTATUS nt_status;
+
+       if (!cache->tdb)
+               goto do_query;
+
+       centry = wcache_fetch(cache, domain, "NSS/PWINFO/%s", sid_string_static(user_sid));     
+       
+       if (!centry)
+               goto do_query;
+
+       *homedir = centry_string( centry, ctx );
+       *shell   = centry_string( centry, ctx );
+       *gecos   = centry_string( centry, ctx );
+       *p_gid   = centry_uint32( centry );     
+
+       centry_free(centry);
+
+       DEBUG(10,("nss_get_info_cached: [Cached] - user_sid %s\n",
+                 sid_string_static(user_sid)));
+
+       return NT_STATUS_OK;
+
+do_query:
+       
+       nt_status = nss_get_info( domain->name, user_sid, ctx, ads, msg, 
+                                 homedir, shell, gecos, p_gid );
+
+       if ( NT_STATUS_IS_OK(nt_status) ) {
+               wcache_save_user_pwinfo( domain, nt_status, user_sid,
+                                        *homedir, *shell, *gecos, *p_gid );
+       }       
+
+       if ( NT_STATUS_EQUAL( nt_status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND ) ) {
+               DEBUG(5,("nss_get_info_cached: Setting domain %s offline\n",
+                        domain->name ));
+               set_domain_offline( domain );
+       }
+
+       return nt_status;       
+}
+
+
 /* the cache backend methods are exposed via this structure */
 struct winbindd_methods cache_methods = {
        True,
 /* the cache backend methods are exposed via this structure */
 struct winbindd_methods cache_methods = {
        True,