s3-talloc Change TALLOC_ARRAY() to talloc_array()
[samba.git] / source3 / winbindd / winbindd_cache.c
index 80ec688a7557796f50204a04ae3e3fa4a00bc330..a1f5ac69b5c48b1a55273092e1244be580f36af3 100644 (file)
 */
 
 #include "includes.h"
+#include "system/filesys.h"
 #include "winbindd.h"
 #include "tdb_validate.h"
 #include "../libcli/auth/libcli_auth.h"
+#include "../librpc/gen_ndr/ndr_wbint.h"
+#include "ads.h"
+#include "nss_info.h"
+#include "../libcli/security/security.h"
+#include "passdb/machine_sid.h"
+#include "util_tdb.h"
 
 #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;
@@ -39,6 +46,7 @@ extern struct winbindd_methods reconnect_methods;
 extern struct winbindd_methods ads_methods;
 #endif
 extern struct winbindd_methods builtin_passdb_methods;
+extern struct winbindd_methods sam_passdb_methods;
 
 /*
  * JRA. KEEP THIS LIST UP TO DATE IF YOU ADD CACHE ENTRIES.
@@ -91,6 +99,7 @@ struct winbind_cache {
 struct cache_entry {
        NTSTATUS status;
        uint32 sequence_number;
+       uint64_t timeout;
        uint8 *data;
        uint32 len, ofs;
 };
@@ -101,35 +110,6 @@ void (*smb_panic_fn)(const char *const why) = smb_panic;
 
 static struct winbind_cache *wcache;
 
-void winbindd_check_cache_size(time_t t)
-{
-       static time_t last_check_time;
-       struct stat st;
-
-       if (last_check_time == (time_t)0)
-               last_check_time = t;
-
-       if (t - last_check_time < 60 && t - last_check_time > 0)
-               return;
-
-       if (wcache == NULL || wcache->tdb == NULL) {
-               DEBUG(0, ("Unable to check size of tdb cache - cache not open !\n"));
-               return;
-       }
-
-       if (fstat(tdb_fd(wcache->tdb), &st) == -1) {
-               DEBUG(0, ("Unable to check size of tdb cache %s!\n", strerror(errno) ));
-               return;
-       }
-
-       if (st.st_size > WINBINDD_MAX_CACHE_SIZE) {
-               DEBUG(10,("flushing cache due to size (%lu) > (%lu)\n",
-                       (unsigned long)st.st_size,
-                       (unsigned long)WINBINDD_MAX_CACHE_SIZE));
-               wcache_flush_cache();
-       }
-}
-
 /* get the winbind_cache structure */
 static struct winbind_cache *get_cache(struct winbindd_domain *domain)
 {
@@ -141,6 +121,13 @@ static struct winbind_cache *get_cache(struct winbindd_domain *domain)
                domain->backend = &builtin_passdb_methods;
                domain->initialized = True;
        }
+
+       if (strequal(domain->name, get_global_sam_name()) &&
+           sid_check_is_domain(&domain->sid)) {
+               domain->backend = &sam_passdb_methods;
+               domain->initialized = True;
+       }
+
        if ( !domain->initialized ) {
                init_dc_connection( domain );
        }
@@ -221,6 +208,21 @@ static bool centry_check_bytes(struct cache_entry *centry, size_t nbytes)
        return true;
 }
 
+/*
+  pull a uint64_t from a cache entry
+*/
+static uint64_t centry_uint64_t(struct cache_entry *centry)
+{
+       uint64_t ret;
+
+       if (!centry_check_bytes(centry, 8)) {
+               smb_panic_fn("centry_uint64_t");
+       }
+       ret = BVAL(centry->data, centry->ofs);
+       centry->ofs += 8;
+       return ret;
+}
+
 /*
   pull a uint32 from a cache entry 
 */
@@ -245,7 +247,7 @@ static uint16 centry_uint16(struct cache_entry *centry)
        if (!centry_check_bytes(centry, 2)) {
                smb_panic_fn("centry_uint16");
        }
-       ret = CVAL(centry->data, centry->ofs);
+       ret = SVAL(centry->data, centry->ofs);
        centry->ofs += 2;
        return ret;
 }
@@ -275,7 +277,7 @@ static NTTIME centry_nttime(struct cache_entry *centry)
        }
        ret = IVAL(centry->data, centry->ofs);
        centry->ofs += 4;
-       ret += (uint64_t)IVAL(centry->data, centry->ofs) << 32;
+       ret += (uint64)IVAL(centry->data, centry->ofs) << 32;
        centry->ofs += 4;
        return ret;
 }
@@ -307,7 +309,7 @@ static char *centry_string(struct cache_entry *centry, TALLOC_CTX *mem_ctx)
                smb_panic_fn("centry_string");
        }
 
-       ret = TALLOC_ARRAY(mem_ctx, char, len+1);
+       ret = talloc_array(mem_ctx, char, len+1);
        if (!ret) {
                smb_panic_fn("centry_string out of memory\n");
        }
@@ -337,7 +339,7 @@ static char *centry_hash16(struct cache_entry *centry, TALLOC_CTX *mem_ctx)
                return NULL;
        }
 
-       ret = TALLOC_ARRAY(mem_ctx, char, 16);
+       ret = talloc_array(mem_ctx, char, 16);
        if (!ret) {
                smb_panic_fn("centry_hash out of memory\n");
        }
@@ -349,14 +351,18 @@ static char *centry_hash16(struct cache_entry *centry, TALLOC_CTX *mem_ctx)
 /* pull a sid from a cache entry, using the supplied
    talloc context 
 */
-static bool centry_sid(struct cache_entry *centry, TALLOC_CTX *mem_ctx, DOM_SID *sid)
+static bool centry_sid(struct cache_entry *centry, struct dom_sid *sid)
 {
        char *sid_string;
-       sid_string = centry_string(centry, mem_ctx);
-       if ((sid_string == NULL) || (!string_to_sid(sid, sid_string))) {
+       bool ret;
+
+       sid_string = centry_string(centry, talloc_tos());
+       if (sid_string == NULL) {
                return false;
        }
-       return true;
+       ret = string_to_sid(sid, sid_string);
+       TALLOC_FREE(sid_string);
+       return ret;
 }
 
 
@@ -388,30 +394,55 @@ static bool wcache_server_down(struct winbindd_domain *domain)
        return ret;
 }
 
-static NTSTATUS fetch_cache_seqnum( struct winbindd_domain *domain, time_t now )
+static bool wcache_fetch_seqnum(const char *domain_name, uint32_t *seqnum,
+                               uint32_t *last_seq_check)
 {
+       char *key;
        TDB_DATA data;
-       fstring key;
-       uint32 time_diff;
 
-       if (!wcache->tdb) {
-               DEBUG(10,("fetch_cache_seqnum: tdb == NULL\n"));
-               return NT_STATUS_UNSUCCESSFUL;
+       if (wcache->tdb == NULL) {
+               DEBUG(10,("wcache_fetch_seqnum: tdb == NULL\n"));
+               return false;
        }
 
-       fstr_sprintf( key, "SEQNUM/%s", domain->name );
-
-       data = tdb_fetch_bystring( wcache->tdb, key );
-       if ( !data.dptr || data.dsize!=8 ) {
-               DEBUG(10,("fetch_cache_seqnum: invalid data size key [%s]\n", key ));
-               return NT_STATUS_UNSUCCESSFUL;
+       key = talloc_asprintf(talloc_tos(), "SEQNUM/%s", domain_name);
+       if (key == NULL) {
+               DEBUG(10, ("talloc failed\n"));
+               return false;
        }
 
-       domain->sequence_number = IVAL(data.dptr, 0);
-       domain->last_seq_check  = IVAL(data.dptr, 4);
+       data = tdb_fetch_bystring(wcache->tdb, key);
+       TALLOC_FREE(key);
 
+       if (data.dptr == NULL) {
+               DEBUG(10, ("wcache_fetch_seqnum: %s not found\n",
+                          domain_name));
+               return false;
+       }
+       if (data.dsize != 8) {
+               DEBUG(10, ("wcache_fetch_seqnum: invalid data size %d\n",
+                          (int)data.dsize));
+               SAFE_FREE(data.dptr);
+               return false;
+       }
+
+       *seqnum = IVAL(data.dptr, 0);
+       *last_seq_check = IVAL(data.dptr, 4);
        SAFE_FREE(data.dptr);
 
+       return true;
+}
+
+static NTSTATUS fetch_cache_seqnum( struct winbindd_domain *domain, time_t now )
+{
+       uint32 last_check, time_diff;
+
+       if (!wcache_fetch_seqnum(domain->name, &domain->sequence_number,
+                                &last_check)) {
+               return NT_STATUS_UNSUCCESSFUL;
+       }
+       domain->last_seq_check = last_check;
+
        /* have we expired? */
 
        time_diff = now - domain->last_seq_check;
@@ -429,34 +460,47 @@ static NTSTATUS fetch_cache_seqnum( struct winbindd_domain *domain, time_t now )
        return NT_STATUS_OK;
 }
 
-static NTSTATUS store_cache_seqnum( struct winbindd_domain *domain )
+bool wcache_store_seqnum(const char *domain_name, uint32_t seqnum,
+                        time_t last_seq_check)
 {
-       TDB_DATA data;
-       fstring key_str;
-       uint8 buf[8];
+       char *key_str;
+       uint8_t buf[8];
+       int ret;
 
-       if (!wcache->tdb) {
-               DEBUG(10,("store_cache_seqnum: tdb == NULL\n"));
-               return NT_STATUS_UNSUCCESSFUL;
+       if (wcache->tdb == NULL) {
+               DEBUG(10, ("wcache_store_seqnum: wcache->tdb == NULL\n"));
+               return false;
        }
 
-       fstr_sprintf( key_str, "SEQNUM/%s", domain->name );
+       key_str = talloc_asprintf(talloc_tos(), "SEQNUM/%s", domain_name);
+       if (key_str == NULL) {
+               DEBUG(10, ("talloc_asprintf failed\n"));
+               return false;
+       }
 
-       SIVAL(buf, 0, domain->sequence_number);
-       SIVAL(buf, 4, domain->last_seq_check);
-       data.dptr = buf;
-       data.dsize = 8;
+       SIVAL(buf, 0, seqnum);
+       SIVAL(buf, 4, last_seq_check);
 
-       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;
+       ret = tdb_store_bystring(wcache->tdb, key_str,
+                                make_tdb_data(buf, sizeof(buf)), TDB_REPLACE);
+       TALLOC_FREE(key_str);
+       if (ret == -1) {
+               DEBUG(10, ("tdb_store_bystring failed: %s\n",
+                          tdb_errorstr(wcache->tdb)));
+               TALLOC_FREE(key_str);
+               return false;
        }
 
-       DEBUG(10,("store_cache_seqnum: success [%s][%u @ %u]\n", 
-               domain->name, domain->sequence_number, 
-               (uint32)domain->last_seq_check));
+       DEBUG(10, ("wcache_store_seqnum: success [%s][%u @ %u]\n",
+                  domain_name, seqnum, (unsigned)last_seq_check));
 
-       return NT_STATUS_OK;
+       return true;
+}
+
+static bool store_cache_seqnum( struct winbindd_domain *domain )
+{
+       return wcache_store_seqnum(domain->name, domain->sequence_number,
+                                  domain->last_seq_check);
 }
 
 /*
@@ -471,7 +515,7 @@ static void refresh_sequence_number(struct winbindd_domain *domain, bool force)
        time_t t = time(NULL);
        unsigned cache_time = lp_winbind_cache_time();
 
-       if ( IS_DOMAIN_OFFLINE(domain) ) {
+       if (is_domain_offline(domain)) {
                return;
        }
 
@@ -571,9 +615,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;
@@ -604,19 +649,41 @@ 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_t(centry);
 
        return centry;
 }
 
+static bool is_my_own_sam_domain(struct winbindd_domain *domain)
+{
+       if (strequal(domain->name, get_global_sam_name()) &&
+           sid_check_is_domain(&domain->sid)) {
+               return true;
+       }
+
+       return false;
+}
+
+static bool is_builtin_domain(struct winbindd_domain *domain)
+{
+       if (strequal(domain->name, "BUILTIN") &&
+           sid_check_is_builtin(&domain->sid)) {
+               return true;
+       }
+
+       return false;
+}
+
 /*
   fetch an entry from the cache, with a varargs key. auto-fetch the sequence
   number and return status
@@ -632,7 +699,9 @@ static struct cache_entry *wcache_fetch(struct winbind_cache *cache,
        char *kstr;
        struct cache_entry *centry;
 
-       if (!winbindd_use_cache()) {
+       if (!winbindd_use_cache() ||
+           is_my_own_sam_domain(domain) ||
+           is_builtin_domain(domain)) {
                return NULL;
        }
 
@@ -698,6 +767,16 @@ static void centry_expand(struct cache_entry *centry, uint32 len)
        }
 }
 
+/*
+  push a uint64_t into a centry
+*/
+static void centry_put_uint64_t(struct cache_entry *centry, uint64_t v)
+{
+       centry_expand(centry, 8);
+       SBVAL(centry->data, centry->ofs, v);
+       centry->ofs += 8;
+}
+
 /*
   push a uint32 into a centry 
 */
@@ -714,7 +793,7 @@ static void centry_put_uint32(struct cache_entry *centry, uint32 v)
 static void centry_put_uint16(struct cache_entry *centry, uint16 v)
 {
        centry_expand(centry, 2);
-       SIVAL(centry->data, centry->ofs, v);
+       SSVAL(centry->data, centry->ofs, v);
        centry->ofs += 2;
 }
 
@@ -764,7 +843,7 @@ static void centry_put_hash16(struct cache_entry *centry, const uint8 val[16])
        centry->ofs += 16;
 }
 
-static void centry_put_sid(struct cache_entry *centry, const DOM_SID *sid) 
+static void centry_put_sid(struct cache_entry *centry, const struct dom_sid *sid)
 {
        fstring sid_string;
        centry_put_string(centry, sid_to_fstring(sid_string, sid));
@@ -819,8 +898,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_t(centry, centry->timeout);
        return centry;
 }
 
@@ -852,7 +933,7 @@ static void centry_end(struct cache_entry *centry, const char *format, ...)
 
 static void wcache_save_name_to_sid(struct winbindd_domain *domain, 
                                    NTSTATUS status, const char *domain_name,
-                                   const char *name, const DOM_SID *sid, 
+                                   const char *name, const struct dom_sid *sid,
                                    enum lsa_SidType type)
 {
        struct cache_entry *centry;
@@ -872,7 +953,7 @@ static void wcache_save_name_to_sid(struct winbindd_domain *domain,
 }
 
 static void wcache_save_sid_to_name(struct winbindd_domain *domain, NTSTATUS status, 
-                                   const DOM_SID *sid, const char *domain_name, const char *name, enum lsa_SidType type)
+                                   const struct dom_sid *sid, const char *domain_name, const char *name, enum lsa_SidType type)
 {
        struct cache_entry *centry;
        fstring sid_string;
@@ -894,7 +975,8 @@ static void wcache_save_sid_to_name(struct winbindd_domain *domain, NTSTATUS sta
 }
 
 
-static void wcache_save_user(struct winbindd_domain *domain, NTSTATUS status, WINBIND_USERINFO *info)
+static void wcache_save_user(struct winbindd_domain *domain, NTSTATUS status,
+                            struct wbint_userinfo *info)
 {
        struct cache_entry *centry;
        fstring sid_string;
@@ -1168,7 +1250,7 @@ do_query:
        return status;
 }
 
-NTSTATUS wcache_cached_creds_exist(struct winbindd_domain *domain, const DOM_SID *sid)
+NTSTATUS wcache_cached_creds_exist(struct winbindd_domain *domain, const struct dom_sid *sid)
 {
        struct winbind_cache *cache = get_cache(domain);
        TDB_DATA data;
@@ -1203,7 +1285,7 @@ NTSTATUS wcache_cached_creds_exist(struct winbindd_domain *domain, const DOM_SID
 
 NTSTATUS wcache_get_creds(struct winbindd_domain *domain, 
                          TALLOC_CTX *mem_ctx, 
-                         const DOM_SID *sid,
+                         const struct dom_sid *sid,
                          const uint8 **cached_nt_pass,
                          const uint8 **cached_salt)
 {
@@ -1214,6 +1296,10 @@ NTSTATUS wcache_get_creds(struct winbindd_domain *domain,
        uint32 rid;
        fstring tmp;
 
+       if (!winbindd_use_cache()) {
+               return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+       }
+
        if (!cache->tdb) {
                return NT_STATUS_INTERNAL_DB_ERROR;
        }
@@ -1283,8 +1369,7 @@ NTSTATUS wcache_get_creds(struct winbindd_domain *domain,
 /* Store creds for a SID - only writes out new salted ones. */
 
 NTSTATUS wcache_save_creds(struct winbindd_domain *domain, 
-                          TALLOC_CTX *mem_ctx, 
-                          const DOM_SID *sid, 
+                          const struct dom_sid *sid,
                           const uint8 nt_pass[NT_HASH_LEN])
 {
        struct cache_entry *centry;
@@ -1330,12 +1415,13 @@ NTSTATUS wcache_save_creds(struct winbindd_domain *domain,
 static NTSTATUS query_user_list(struct winbindd_domain *domain,
                                TALLOC_CTX *mem_ctx,
                                uint32 *num_entries, 
-                               WINBIND_USERINFO **info)
+                               struct wbint_userinfo **info)
 {
        struct winbind_cache *cache = get_cache(domain);
        struct cache_entry *centry = NULL;
        NTSTATUS status;
        unsigned int i, retry;
+       bool old_status = domain->online;
 
        if (!cache->tdb)
                goto do_query;
@@ -1344,12 +1430,13 @@ static NTSTATUS query_user_list(struct winbindd_domain *domain,
        if (!centry)
                goto do_query;
 
+do_fetch_cache:
        *num_entries = centry_uint32(centry);
 
        if (*num_entries == 0)
                goto do_cached;
 
-       (*info) = TALLOC_ARRAY(mem_ctx, WINBIND_USERINFO, *num_entries);
+       (*info) = talloc_array(mem_ctx, struct wbint_userinfo, *num_entries);
        if (! (*info)) {
                smb_panic_fn("query_user_list out of memory");
        }
@@ -1358,8 +1445,8 @@ static NTSTATUS query_user_list(struct winbindd_domain *domain,
                (*info)[i].full_name = centry_string(centry, mem_ctx);
                (*info)[i].homedir = centry_string(centry, mem_ctx);
                (*info)[i].shell = centry_string(centry, mem_ctx);
-               centry_sid(centry, mem_ctx, &(*info)[i].user_sid);
-               centry_sid(centry, mem_ctx, &(*info)[i].group_sid);
+               centry_sid(centry, &(*info)[i].user_sid);
+               centry_sid(centry, &(*info)[i].group_sid);
        }
 
 do_cached:     
@@ -1404,12 +1491,44 @@ do_query:
                                  "connection cache\n"));
                        invalidate_cm_connection(&domain->conn);
                }
+               if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
+                   NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
+                       if (!domain->internal && old_status) {
+                               set_domain_offline(domain);
+                       }
+                       /* store partial response. */
+                       if (*num_entries > 0) {
+                               /*
+                                * humm, what about the status used for cache?
+                                * Should it be NT_STATUS_OK?
+                                */
+                               break;
+                       }
+                       /*
+                        * domain is offline now, and there is no user entries,
+                        * try to fetch from cache again.
+                        */
+                       if (cache->tdb && !domain->online && !domain->internal && old_status) {
+                               centry = wcache_fetch(cache, domain, "UL/%s", domain->name);
+                               /* partial response... */
+                               if (!centry) {
+                                       goto skip_save;
+                               } else {
+                                       goto do_fetch_cache;
+                               }
+                       } else {
+                               goto skip_save;
+                       }
+               }
 
        } while (NT_STATUS_V(status) == NT_STATUS_V(NT_STATUS_UNSUCCESSFUL) && 
                 (retry++ < 5));
 
        /* and save it */
        refresh_sequence_number(domain, false);
+       if (!NT_STATUS_IS_OK(status)) {
+               return status;
+       }
        centry = centry_start(domain, status);
        if (!centry)
                goto skip_save;
@@ -1447,13 +1566,15 @@ skip_save:
 static NTSTATUS enum_dom_groups(struct winbindd_domain *domain,
                                TALLOC_CTX *mem_ctx,
                                uint32 *num_entries, 
-                               struct acct_info **info)
+                               struct wb_acct_info **info)
 {
        struct winbind_cache *cache = get_cache(domain);
        struct cache_entry *centry = NULL;
        NTSTATUS status;
        unsigned int i;
+       bool old_status;
 
+       old_status = domain->online;
        if (!cache->tdb)
                goto do_query;
 
@@ -1461,12 +1582,13 @@ static NTSTATUS enum_dom_groups(struct winbindd_domain *domain,
        if (!centry)
                goto do_query;
 
+do_fetch_cache:
        *num_entries = centry_uint32(centry);
 
        if (*num_entries == 0)
                goto do_cached;
 
-       (*info) = TALLOC_ARRAY(mem_ctx, struct acct_info, *num_entries);
+       (*info) = talloc_array(mem_ctx, struct wb_acct_info, *num_entries);
        if (! (*info)) {
                smb_panic_fn("enum_dom_groups out of memory");
        }
@@ -1499,8 +1621,26 @@ do_query:
 
        status = domain->backend->enum_dom_groups(domain, mem_ctx, num_entries, info);
 
+       if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
+           NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
+               if (!domain->internal && old_status) {
+                       set_domain_offline(domain);
+               }
+               if (cache->tdb &&
+                       !domain->online &&
+                       !domain->internal &&
+                       old_status) {
+                       centry = wcache_fetch(cache, domain, "GL/%s/domain", domain->name);
+                       if (centry) {
+                               goto do_fetch_cache;
+                       }
+               }
+       }
        /* and save it */
        refresh_sequence_number(domain, false);
+       if (!NT_STATUS_IS_OK(status)) {
+               return status;
+       }
        centry = centry_start(domain, status);
        if (!centry)
                goto skip_save;
@@ -1521,13 +1661,15 @@ skip_save:
 static NTSTATUS enum_local_groups(struct winbindd_domain *domain,
                                TALLOC_CTX *mem_ctx,
                                uint32 *num_entries, 
-                               struct acct_info **info)
+                               struct wb_acct_info **info)
 {
        struct winbind_cache *cache = get_cache(domain);
        struct cache_entry *centry = NULL;
        NTSTATUS status;
        unsigned int i;
+       bool old_status;
 
+       old_status = domain->online;
        if (!cache->tdb)
                goto do_query;
 
@@ -1535,12 +1677,13 @@ static NTSTATUS enum_local_groups(struct winbindd_domain *domain,
        if (!centry)
                goto do_query;
 
+do_fetch_cache:
        *num_entries = centry_uint32(centry);
 
        if (*num_entries == 0)
                goto do_cached;
 
-       (*info) = TALLOC_ARRAY(mem_ctx, struct acct_info, *num_entries);
+       (*info) = talloc_array(mem_ctx, struct wb_acct_info, *num_entries);
        if (! (*info)) {
                smb_panic_fn("enum_dom_groups out of memory");
        }
@@ -1583,8 +1726,26 @@ do_query:
 
        status = domain->backend->enum_local_groups(domain, mem_ctx, num_entries, info);
 
+       if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
+               NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
+               if (!domain->internal && old_status) {
+                       set_domain_offline(domain);
+               }
+               if (cache->tdb &&
+                       !domain->internal &&
+                       !domain->online &&
+                       old_status) {
+                       centry = wcache_fetch(cache, domain, "GL/%s/local", domain->name);
+                       if (centry) {
+                               goto do_fetch_cache;
+                       }
+               }
+       }
        /* and save it */
        refresh_sequence_number(domain, false);
+       if (!NT_STATUS_IS_OK(status)) {
+               return status;
+       }
        centry = centry_start(domain, status);
        if (!centry)
                goto skip_save;
@@ -1601,42 +1762,64 @@ skip_save:
        return status;
 }
 
-/* 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,
+NTSTATUS wcache_name_to_sid(struct winbindd_domain *domain,
                            const char *domain_name,
                            const char *name,
-                           DOM_SID *sid,
+                           struct dom_sid *sid,
                            enum lsa_SidType *type)
 {
        struct winbind_cache *cache = get_cache(domain);
-       struct cache_entry *centry = NULL;
+       struct cache_entry *centry;
        NTSTATUS status;
-       fstring uname;
+       char *uname;
 
-       if (!cache->tdb)
-               goto do_query;
+       if (cache->tdb == NULL) {
+               return NT_STATUS_NOT_FOUND;
+       }
+
+       uname = talloc_strdup_upper(talloc_tos(), name);
+       if (uname == NULL) {
+               return NT_STATUS_NO_MEMORY;
+       }
 
-       fstrcpy(uname, name);
-       strupper_m(uname);
        centry = wcache_fetch(cache, domain, "NS/%s/%s", domain_name, uname);
-       if (!centry)
-               goto do_query;
+       TALLOC_FREE(uname);
+       if (centry == NULL) {
+               return NT_STATUS_NOT_FOUND;
+       }
 
        status = centry->status;
        if (NT_STATUS_IS_OK(status)) {
                *type = (enum lsa_SidType)centry_uint32(centry);
-               centry_sid(centry, mem_ctx, sid);
+               centry_sid(centry, sid);
        }
 
-       DEBUG(10,("name_to_sid: [Cached] - cached name for domain %s status: %s\n",
-               domain->name, nt_errstr(status) ));
+       DEBUG(10,("name_to_sid: [Cached] - cached name for domain %s status: "
+                 "%s\n", domain->name, nt_errstr(status) ));
 
        centry_free(centry);
        return status;
+}
+
+/* convert a single name to a sid in a domain */
+static NTSTATUS name_to_sid(struct winbindd_domain *domain,
+                           TALLOC_CTX *mem_ctx,
+                           const char *domain_name,
+                           const char *name,
+                           uint32_t flags,
+                           struct dom_sid *sid,
+                           enum lsa_SidType *type)
+{
+       NTSTATUS status;
+       bool old_status;
+
+       old_status = domain->online;
+
+       status = wcache_name_to_sid(domain, domain_name, name, sid, type);
+       if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
+               return status;
+       }
 
-do_query:
        ZERO_STRUCTP(sid);
 
        /* If the seq number check indicated that there is a problem
@@ -1653,9 +1836,22 @@ do_query:
        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, orig_cmd, 
-                                             domain_name, name, sid, type);
+       status = domain->backend->name_to_sid(domain, mem_ctx, domain_name,
+                                             name, flags, sid, type);
 
+       if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
+               NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
+               if (!domain->internal && old_status) {
+                       set_domain_offline(domain);
+               }
+               if (!domain->internal &&
+                       !domain->online &&
+                       old_status) {
+                       NTSTATUS cache_status;
+                       cache_status = wcache_name_to_sid(domain, domain_name, name, sid, type);
+                       return cache_status;
+               }
+       }
        /* and save it */
        refresh_sequence_number(domain, false);
 
@@ -1665,8 +1861,8 @@ do_query:
 
                /* Only save the reverse mapping if this was not a UPN */
                if (!strchr(name, '@')) {
-                       strupper_m(CONST_DISCARD(char *,domain_name));
-                       strlower_m(CONST_DISCARD(char *,name));
+                       strupper_m(discard_const_p(char, domain_name));
+                       strlower_m(discard_const_p(char, name));
                        wcache_save_sid_to_name(domain, status, sid, domain_name, name, *type);
                }
        }
@@ -1674,42 +1870,67 @@ do_query:
        return status;
 }
 
-/* convert a sid to a user or group name. The sid is guaranteed to be in the domain
-   given */
-static NTSTATUS sid_to_name(struct winbindd_domain *domain,
+NTSTATUS wcache_sid_to_name(struct winbindd_domain *domain,
+                           const struct dom_sid *sid,
                            TALLOC_CTX *mem_ctx,
-                           const DOM_SID *sid,
                            char **domain_name,
                            char **name,
                            enum lsa_SidType *type)
 {
        struct winbind_cache *cache = get_cache(domain);
-       struct cache_entry *centry = NULL;
+       struct cache_entry *centry;
+       char *sid_string;
        NTSTATUS status;
-       fstring sid_string;
 
-       if (!cache->tdb)
-               goto do_query;
+       if (cache->tdb == NULL) {
+               return NT_STATUS_NOT_FOUND;
+       }
 
-       centry = wcache_fetch(cache, domain, "SN/%s",
-                             sid_to_fstring(sid_string, sid));
-       if (!centry)
-               goto do_query;
+       sid_string = sid_string_tos(sid);
+       if (sid_string == NULL) {
+               return NT_STATUS_NO_MEMORY;
+       }
 
-       status = centry->status;
-       if (NT_STATUS_IS_OK(status)) {
+       centry = wcache_fetch(cache, domain, "SN/%s", sid_string);
+       TALLOC_FREE(sid_string);
+       if (centry == NULL) {
+               return NT_STATUS_NOT_FOUND;
+       }
+
+       if (NT_STATUS_IS_OK(centry->status)) {
                *type = (enum lsa_SidType)centry_uint32(centry);
                *domain_name = centry_string(centry, mem_ctx);
                *name = centry_string(centry, mem_ctx);
        }
 
-       DEBUG(10,("sid_to_name: [Cached] - cached name for domain %s status: %s\n",
-               domain->name, nt_errstr(status) ));
-
+       status = centry->status;
        centry_free(centry);
+
+       DEBUG(10,("sid_to_name: [Cached] - cached name for domain %s status: "
+                 "%s\n", domain->name, nt_errstr(status) ));
+
        return status;
+}
+
+/* convert a sid to a user or group name. The sid is guaranteed to be in the domain
+   given */
+static NTSTATUS sid_to_name(struct winbindd_domain *domain,
+                           TALLOC_CTX *mem_ctx,
+                           const struct dom_sid *sid,
+                           char **domain_name,
+                           char **name,
+                           enum lsa_SidType *type)
+{
+       NTSTATUS status;
+       bool old_status;
+
+       old_status = domain->online;
+       status = wcache_sid_to_name(domain, sid, mem_ctx, domain_name, name,
+                                   type);
+       if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
+               return status;
+       }
 
-do_query:
        *name = NULL;
        *domain_name = NULL;
 
@@ -1729,8 +1950,25 @@ do_query:
 
        status = domain->backend->sid_to_name(domain, mem_ctx, sid, domain_name, name, type);
 
+       if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
+               NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
+               if (!domain->internal && old_status) {
+                       set_domain_offline(domain);
+               }
+               if (!domain->internal &&
+                       !domain->online &&
+                       old_status) {
+                       NTSTATUS cache_status;
+                       cache_status = wcache_sid_to_name(domain, sid, mem_ctx,
+                                                       domain_name, name, type);
+                       return cache_status;
+               }
+       }
        /* and save it */
        refresh_sequence_number(domain, false);
+       if (!NT_STATUS_IS_OK(status)) {
+               return status;
+       }
        wcache_save_sid_to_name(domain, status, sid, *domain_name, *name, *type);
 
        /* We can't save the name to sid mapping here, as with sid history a
@@ -1741,7 +1979,7 @@ do_query:
 
 static NTSTATUS rids_to_names(struct winbindd_domain *domain,
                              TALLOC_CTX *mem_ctx,
-                             const DOM_SID *domain_sid,
+                             const struct dom_sid *domain_sid,
                              uint32 *rids,
                              size_t num_rids,
                              char **domain_name,
@@ -1753,7 +1991,9 @@ static NTSTATUS rids_to_names(struct winbindd_domain *domain,
        NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
        bool have_mapped;
        bool have_unmapped;
+       bool old_status;
 
+       old_status = domain->online;
        *domain_name = NULL;
        *names = NULL;
        *types = NULL;
@@ -1766,8 +2006,8 @@ static NTSTATUS rids_to_names(struct winbindd_domain *domain,
                return NT_STATUS_OK;
        }
 
-       *names = TALLOC_ARRAY(mem_ctx, char *, num_rids);
-       *types = TALLOC_ARRAY(mem_ctx, enum lsa_SidType, num_rids);
+       *names = talloc_array(mem_ctx, char *, num_rids);
+       *types = talloc_array(mem_ctx, enum lsa_SidType, num_rids);
 
        if ((*names == NULL) || (*types == NULL)) {
                result = NT_STATUS_NO_MEMORY;
@@ -1777,7 +2017,7 @@ static NTSTATUS rids_to_names(struct winbindd_domain *domain,
        have_mapped = have_unmapped = false;
 
        for (i=0; i<num_rids; i++) {
-               DOM_SID sid;
+               struct dom_sid sid;
                struct cache_entry *centry;
                fstring tmp;
 
@@ -1809,7 +2049,8 @@ static NTSTATUS rids_to_names(struct winbindd_domain *domain,
 
                        (*names)[i] = centry_string(centry, *names);
 
-               } else if (NT_STATUS_EQUAL(centry->status, NT_STATUS_NONE_MAPPED)) {
+               } else if (NT_STATUS_EQUAL(centry->status, NT_STATUS_NONE_MAPPED)
+                          || NT_STATUS_EQUAL(centry->status, STATUS_SOME_UNMAPPED)) {
                        have_unmapped = true;
 
                } else {
@@ -1838,12 +2079,79 @@ static NTSTATUS rids_to_names(struct winbindd_domain *domain,
                                                rids, num_rids, domain_name,
                                                names, types);
 
+       if (NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
+               NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
+               if (!domain->internal && old_status) {
+                       set_domain_offline(domain);
+               }
+               if (cache->tdb &&
+                       !domain->internal &&
+                       !domain->online &&
+                       old_status) {
+                       have_mapped = have_unmapped = false;
+
+                       for (i=0; i<num_rids; i++) {
+                               struct dom_sid sid;
+                               struct cache_entry *centry;
+                               fstring tmp;
+
+                               if (!sid_compose(&sid, domain_sid, rids[i])) {
+                                       result = NT_STATUS_INTERNAL_ERROR;
+                                       goto error;
+                               }
+
+                               centry = wcache_fetch(cache, domain, "SN/%s",
+                                                     sid_to_fstring(tmp, &sid));
+                               if (!centry) {
+                                       (*types)[i] = SID_NAME_UNKNOWN;
+                                       (*names)[i] = talloc_strdup(*names, "");
+                                       continue;
+                               }
+
+                               (*types)[i] = SID_NAME_UNKNOWN;
+                               (*names)[i] = talloc_strdup(*names, "");
+
+                               if (NT_STATUS_IS_OK(centry->status)) {
+                                       char *dom;
+                                       have_mapped = true;
+                                       (*types)[i] = (enum lsa_SidType)centry_uint32(centry);
+
+                                       dom = centry_string(centry, mem_ctx);
+                                       if (*domain_name == NULL) {
+                                               *domain_name = dom;
+                                       } else {
+                                               talloc_free(dom);
+                                       }
+
+                                       (*names)[i] = centry_string(centry, *names);
+
+                               } else if (NT_STATUS_EQUAL(centry->status, NT_STATUS_NONE_MAPPED)) {
+                                       have_unmapped = true;
+
+                               } else {
+                                       /* something's definitely wrong */
+                                       result = centry->status;
+                                       goto error;
+                               }
+
+                               centry_free(centry);
+                       }
+
+                       if (!have_mapped) {
+                               return NT_STATUS_NONE_MAPPED;
+                       }
+                       if (!have_unmapped) {
+                               return NT_STATUS_OK;
+                       }
+                       return STATUS_SOME_UNMAPPED;
+               }
+       }
        /*
          None of the queried rids has been found so save all negative entries
        */
        if (NT_STATUS_EQUAL(result, NT_STATUS_NONE_MAPPED)) {
                for (i = 0; i < num_rids; i++) {
-                       DOM_SID sid;
+                       struct dom_sid sid;
                        const char *name = "";
                        const enum lsa_SidType type = SID_NAME_UNKNOWN;
                        NTSTATUS status = NT_STATUS_NONE_MAPPED;
@@ -1870,7 +2178,7 @@ static NTSTATUS rids_to_names(struct winbindd_domain *domain,
        refresh_sequence_number(domain, false);
 
        for (i=0; i<num_rids; i++) {
-               DOM_SID sid;
+               struct dom_sid sid;
                NTSTATUS status;
 
                if (!sid_compose(&sid, domain_sid, rids[i])) {
@@ -1893,38 +2201,46 @@ static NTSTATUS rids_to_names(struct winbindd_domain *domain,
        return result;
 }
 
-/* Lookup user information from a rid */
-static NTSTATUS query_user(struct winbindd_domain *domain, 
-                          TALLOC_CTX *mem_ctx, 
-                          const DOM_SID *user_sid, 
-                          WINBIND_USERINFO *info)
+NTSTATUS wcache_query_user(struct winbindd_domain *domain,
+                          TALLOC_CTX *mem_ctx,
+                          const struct dom_sid *user_sid,
+                          struct wbint_userinfo *info)
 {
        struct winbind_cache *cache = get_cache(domain);
        struct cache_entry *centry = NULL;
        NTSTATUS status;
-       fstring tmp;
+       char *sid_string;
 
-       if (!cache->tdb)
-               goto do_query;
+       if (cache->tdb == NULL) {
+               return NT_STATUS_NOT_FOUND;
+       }
 
-       centry = wcache_fetch(cache, domain, "U/%s",
-                             sid_to_fstring(tmp, user_sid));
+       sid_string = sid_string_tos(user_sid);
+       if (sid_string == NULL) {
+               return NT_STATUS_NO_MEMORY;
+       }
 
-       /* If we have an access denied cache entry and a cached info3 in the
-           samlogon cache then do a query.  This will force the rpc back end
-           to return the info3 data. */
+       centry = wcache_fetch(cache, domain, "U/%s", sid_string);
+       TALLOC_FREE(sid_string);
+       if (centry == NULL) {
+               return NT_STATUS_NOT_FOUND;
+       }
 
-       if (NT_STATUS_V(domain->last_status) == NT_STATUS_V(NT_STATUS_ACCESS_DENIED) &&
+       /*
+        * If we have an access denied cache entry and a cached info3
+        * in the samlogon cache then do a query.  This will force the
+        * rpc back end to return the info3 data.
+        */
+
+       if (NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED) &&
            netsamlogon_cache_have(user_sid)) {
-               DEBUG(10, ("query_user: cached access denied and have cached info3\n"));
+               DEBUG(10, ("query_user: cached access denied and have cached "
+                          "info3\n"));
                domain->last_status = NT_STATUS_OK;
                centry_free(centry);
-               goto do_query;
+               return NT_STATUS_NOT_FOUND;
        }
 
-       if (!centry)
-               goto do_query;
-
        /* if status is not ok then this is a negative hit
           and the rest of the data doesn't matter */
        status = centry->status;
@@ -1934,17 +2250,32 @@ static NTSTATUS query_user(struct winbindd_domain *domain,
                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);
+               centry_sid(centry, &info->user_sid);
+               centry_sid(centry, &info->group_sid);
        }
 
-       DEBUG(10,("query_user: [Cached] - cached info for domain %s status: %s\n",
-               domain->name, nt_errstr(status) ));
+       DEBUG(10,("query_user: [Cached] - cached info for domain %s status: "
+                 "%s\n", domain->name, nt_errstr(status) ));
 
        centry_free(centry);
        return status;
+}
+
+/* Lookup user information from a rid */
+static NTSTATUS query_user(struct winbindd_domain *domain,
+                          TALLOC_CTX *mem_ctx,
+                          const struct dom_sid *user_sid,
+                          struct wbint_userinfo *info)
+{
+       NTSTATUS status;
+       bool old_status;
+
+       old_status = domain->online;
+       status = wcache_query_user(domain, mem_ctx, user_sid, info);
+       if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
+               return status;
+       }
 
-do_query:
        ZERO_STRUCTP(info);
 
        /* Return status value returned by seq number check */
@@ -1957,70 +2288,107 @@ do_query:
 
        status = domain->backend->query_user(domain, mem_ctx, user_sid, info);
 
+       if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
+               NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
+               if (!domain->internal && old_status) {
+                       set_domain_offline(domain);
+               }
+               if (!domain->internal &&
+                       !domain->online &&
+                       old_status) {
+                       NTSTATUS cache_status;
+                       cache_status = wcache_query_user(domain, mem_ctx, user_sid, info);
+                       return cache_status;
+               }
+       }
        /* and save it */
        refresh_sequence_number(domain, false);
+       if (!NT_STATUS_IS_OK(status)) {
+               return status;
+       }
        wcache_save_user(domain, status, info);
 
        return status;
 }
 
-
-/* Lookup groups a user is a member of. */
-static NTSTATUS lookup_usergroups(struct winbindd_domain *domain,
+NTSTATUS wcache_lookup_usergroups(struct winbindd_domain *domain,
                                  TALLOC_CTX *mem_ctx,
-                                 const DOM_SID *user_sid, 
-                                 uint32 *num_groups, DOM_SID **user_gids)
+                                 const struct dom_sid *user_sid,
+                                 uint32_t *pnum_sids,
+                                 struct dom_sid **psids)
 {
        struct winbind_cache *cache = get_cache(domain);
        struct cache_entry *centry = NULL;
        NTSTATUS status;
-       unsigned int i;
+       uint32_t i, num_sids;
+       struct dom_sid *sids;
        fstring sid_string;
 
-       if (!cache->tdb)
-               goto do_query;
+       if (cache->tdb == NULL) {
+               return NT_STATUS_NOT_FOUND;
+       }
 
        centry = wcache_fetch(cache, domain, "UG/%s",
                              sid_to_fstring(sid_string, user_sid));
+       if (centry == NULL) {
+               return NT_STATUS_NOT_FOUND;
+       }
 
        /* If we have an access denied cache entry and a cached info3 in the
            samlogon cache then do a query.  This will force the rpc back end
            to return the info3 data. */
 
-       if (NT_STATUS_V(domain->last_status) == NT_STATUS_V(NT_STATUS_ACCESS_DENIED) &&
-           netsamlogon_cache_have(user_sid)) {
-               DEBUG(10, ("lookup_usergroups: cached access denied and have cached info3\n"));
+       if (NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)
+           && netsamlogon_cache_have(user_sid)) {
+               DEBUG(10, ("lookup_usergroups: cached access denied and have "
+                          "cached info3\n"));
                domain->last_status = NT_STATUS_OK;
                centry_free(centry);
-               goto do_query;
+               return NT_STATUS_NOT_FOUND;
        }
 
-       if (!centry)
-               goto do_query;
-
-       *num_groups = centry_uint32(centry);
-
-       if (*num_groups == 0)
-               goto do_cached;
-
-       (*user_gids) = TALLOC_ARRAY(mem_ctx, DOM_SID, *num_groups);
-       if (! (*user_gids)) {
-               smb_panic_fn("lookup_usergroups out of memory");
+       num_sids = centry_uint32(centry);
+       sids = talloc_array(mem_ctx, struct dom_sid, num_sids);
+       if (sids == NULL) {
+               centry_free(centry);
+               return NT_STATUS_NO_MEMORY;
        }
-       for (i=0; i<(*num_groups); i++) {
-               centry_sid(centry, mem_ctx, &(*user_gids)[i]);
+
+       for (i=0; i<num_sids; i++) {
+               centry_sid(centry, &sids[i]);
        }
 
-do_cached:     
        status = centry->status;
 
-       DEBUG(10,("lookup_usergroups: [Cached] - cached info for domain %s status: %s\n",
-               domain->name, nt_errstr(status) ));
+       DEBUG(10,("lookup_usergroups: [Cached] - cached info for domain %s "
+                 "status: %s\n", domain->name, nt_errstr(status)));
 
        centry_free(centry);
+
+       *pnum_sids = num_sids;
+       *psids = sids;
        return status;
+}
+
+/* Lookup groups a user is a member of. */
+static NTSTATUS lookup_usergroups(struct winbindd_domain *domain,
+                                 TALLOC_CTX *mem_ctx,
+                                 const struct dom_sid *user_sid,
+                                 uint32 *num_groups, struct dom_sid **user_gids)
+{
+       struct cache_entry *centry = NULL;
+       NTSTATUS status;
+       unsigned int i;
+       fstring sid_string;
+       bool old_status;
+
+       old_status = domain->online;
+       status = wcache_lookup_usergroups(domain, mem_ctx, user_sid,
+                                         num_groups, user_gids);
+       if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
+               return status;
+       }
 
-do_query:
        (*num_groups) = 0;
        (*user_gids) = NULL;
 
@@ -2034,11 +2402,28 @@ do_query:
 
        status = domain->backend->lookup_usergroups(domain, mem_ctx, user_sid, num_groups, user_gids);
 
+       if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
+               NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
+               if (!domain->internal && old_status) {
+                       set_domain_offline(domain);
+               }
+               if (!domain->internal &&
+                       !domain->online &&
+                       old_status) {
+                       NTSTATUS cache_status;
+                       cache_status = wcache_lookup_usergroups(domain, mem_ctx, user_sid,
+                                                         num_groups, user_gids);
+                       return cache_status;
+               }
+       }
        if ( NT_STATUS_EQUAL(status, NT_STATUS_SYNCHRONIZATION_REQUIRED) )
                goto skip_save;
 
        /* and save it */
        refresh_sequence_number(domain, false);
+       if (!NT_STATUS_IS_OK(status)) {
+               return status;
+       }
        centry = centry_start(domain, status);
        if (!centry)
                goto skip_save;
@@ -2055,58 +2440,74 @@ skip_save:
        return status;
 }
 
-static NTSTATUS lookup_useraliases(struct winbindd_domain *domain,
-                                  TALLOC_CTX *mem_ctx,
-                                  uint32 num_sids, const DOM_SID *sids,
-                                  uint32 *num_aliases, uint32 **alias_rids)
+static char *wcache_make_sidlist(TALLOC_CTX *mem_ctx, uint32_t num_sids,
+                                const struct dom_sid *sids)
+{
+       uint32_t i;
+       char *sidlist;
+
+       sidlist = talloc_strdup(mem_ctx, "");
+       if (sidlist == NULL) {
+               return NULL;
+       }
+       for (i=0; i<num_sids; i++) {
+               fstring tmp;
+               sidlist = talloc_asprintf_append_buffer(
+                       sidlist, "/%s", sid_to_fstring(tmp, &sids[i]));
+               if (sidlist == NULL) {
+                       return NULL;
+               }
+       }
+       return sidlist;
+}
+
+NTSTATUS wcache_lookup_useraliases(struct winbindd_domain *domain,
+                                  TALLOC_CTX *mem_ctx, uint32_t num_sids,
+                                  const struct dom_sid *sids,
+                                  uint32_t *pnum_aliases, uint32_t **paliases)
 {
        struct winbind_cache *cache = get_cache(domain);
        struct cache_entry *centry = NULL;
+       uint32_t num_aliases;
+       uint32_t *aliases;
        NTSTATUS status;
-       char *sidlist = talloc_strdup(mem_ctx, "");
+       char *sidlist;
        int i;
 
-       if (!cache->tdb)
-               goto do_query;
+       if (cache->tdb == NULL) {
+               return NT_STATUS_NOT_FOUND;
+       }
 
        if (num_sids == 0) {
-               *num_aliases = 0;
-               *alias_rids = NULL;
+               *pnum_aliases = 0;
+               *paliases = NULL;
                return NT_STATUS_OK;
        }
 
        /* We need to cache indexed by the whole list of SIDs, the aliases
         * resulting might come from any of the SIDs. */
 
-       for (i=0; i<num_sids; i++) {
-               fstring tmp;
-               sidlist = talloc_asprintf(mem_ctx, "%s/%s", sidlist,
-                                         sid_to_fstring(tmp, &sids[i]));
-               if (sidlist == NULL)
-                       return NT_STATUS_NO_MEMORY;
+       sidlist = wcache_make_sidlist(talloc_tos(), num_sids, sids);
+       if (sidlist == NULL) {
+               return NT_STATUS_NO_MEMORY;
        }
 
        centry = wcache_fetch(cache, domain, "UA%s", sidlist);
+       TALLOC_FREE(sidlist);
+       if (centry == NULL) {
+               return NT_STATUS_NOT_FOUND;
+       }
 
-       if (!centry)
-               goto do_query;
-
-       *num_aliases = centry_uint32(centry);
-       *alias_rids = NULL;
-
-       if (*num_aliases) {
-               (*alias_rids) = TALLOC_ARRAY(mem_ctx, uint32, *num_aliases);
-
-               if ((*alias_rids) == NULL) {
-                       centry_free(centry);
-                       return NT_STATUS_NO_MEMORY;
-               }
-       } else {
-               (*alias_rids) = NULL;
+       num_aliases = centry_uint32(centry);
+       aliases = talloc_array(mem_ctx, uint32_t, num_aliases);
+       if (aliases == NULL) {
+               centry_free(centry);
+               return NT_STATUS_NO_MEMORY;
        }
 
-       for (i=0; i<(*num_aliases); i++)
-               (*alias_rids)[i] = centry_uint32(centry);
+       for (i=0; i<num_aliases; i++) {
+               aliases[i] = centry_uint32(centry);
+       }
 
        status = centry->status;
 
@@ -2114,9 +2515,31 @@ static NTSTATUS lookup_useraliases(struct winbindd_domain *domain,
                  "status %s\n", domain->name, nt_errstr(status)));
 
        centry_free(centry);
+
+       *pnum_aliases = num_aliases;
+       *paliases = aliases;
+
        return status;
+}
+
+static NTSTATUS lookup_useraliases(struct winbindd_domain *domain,
+                                  TALLOC_CTX *mem_ctx,
+                                  uint32 num_sids, const struct dom_sid *sids,
+                                  uint32 *num_aliases, uint32 **alias_rids)
+{
+       struct cache_entry *centry = NULL;
+       NTSTATUS status;
+       char *sidlist;
+       int i;
+       bool old_status;
+
+       old_status = domain->online;
+       status = wcache_lookup_useraliases(domain, mem_ctx, num_sids, sids,
+                                          num_aliases, alias_rids);
+       if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
+               return status;
+       }
 
- do_query:
        (*num_aliases) = 0;
        (*alias_rids) = NULL;
 
@@ -2126,12 +2549,34 @@ static NTSTATUS lookup_useraliases(struct winbindd_domain *domain,
        DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info "
                  "for domain %s\n", domain->name ));
 
+       sidlist = wcache_make_sidlist(talloc_tos(), num_sids, sids);
+       if (sidlist == NULL) {
+               return NT_STATUS_NO_MEMORY;
+       }
+
        status = domain->backend->lookup_useraliases(domain, mem_ctx,
                                                     num_sids, sids,
                                                     num_aliases, alias_rids);
 
+       if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
+               NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
+               if (!domain->internal && old_status) {
+                       set_domain_offline(domain);
+               }
+               if (!domain->internal &&
+                       !domain->online &&
+                       old_status) {
+                       NTSTATUS cache_status;
+                       cache_status = wcache_lookup_useraliases(domain, mem_ctx, num_sids,
+                                                                sids, num_aliases, alias_rids);
+                       return cache_status;
+               }
+       }
        /* and save it */
        refresh_sequence_number(domain, false);
+       if (!NT_STATUS_IS_OK(status)) {
+               return status;
+       }
        centry = centry_start(domain, status);
        if (!centry)
                goto skip_save;
@@ -2145,56 +2590,92 @@ static NTSTATUS lookup_useraliases(struct winbindd_domain *domain,
        return status;
 }
 
-
-static NTSTATUS lookup_groupmem(struct winbindd_domain *domain,
+NTSTATUS wcache_lookup_groupmem(struct winbindd_domain *domain,
                                TALLOC_CTX *mem_ctx,
-                               const DOM_SID *group_sid, uint32 *num_names, 
-                               DOM_SID **sid_mem, char ***names, 
-                               uint32 **name_types)
+                               const struct dom_sid *group_sid,
+                               uint32_t *num_names,
+                               struct dom_sid **sid_mem, char ***names,
+                               uint32_t **name_types)
 {
        struct winbind_cache *cache = get_cache(domain);
        struct cache_entry *centry = NULL;
        NTSTATUS status;
        unsigned int i;
-       fstring sid_string;
+       char *sid_string;
 
-       if (!cache->tdb)
-               goto do_query;
+       if (cache->tdb == NULL) {
+               return NT_STATUS_NOT_FOUND;
+       }
 
-       centry = wcache_fetch(cache, domain, "GM/%s",
-                             sid_to_fstring(sid_string, group_sid));
-       if (!centry)
-               goto do_query;
+       sid_string = sid_string_tos(group_sid);
+       if (sid_string == NULL) {
+               return NT_STATUS_NO_MEMORY;
+       }
 
-       *num_names = centry_uint32(centry);
+       centry = wcache_fetch(cache, domain, "GM/%s", sid_string);
+       TALLOC_FREE(sid_string);
+       if (centry == NULL) {
+               return NT_STATUS_NOT_FOUND;
+       }
 
-       if (*num_names == 0)
-               goto do_cached;
+       *sid_mem = NULL;
+       *names = NULL;
+       *name_types = NULL;
 
-       (*sid_mem) = TALLOC_ARRAY(mem_ctx, DOM_SID, *num_names);
-       (*names) = TALLOC_ARRAY(mem_ctx, char *, *num_names);
-       (*name_types) = TALLOC_ARRAY(mem_ctx, uint32, *num_names);
+       *num_names = centry_uint32(centry);
+       if (*num_names == 0) {
+               centry_free(centry);
+               return NT_STATUS_OK;
+       }
 
-       if (! (*sid_mem) || ! (*names) || ! (*name_types)) {
-               smb_panic_fn("lookup_groupmem out of memory");
+       *sid_mem = talloc_array(mem_ctx, struct dom_sid, *num_names);
+       *names = talloc_array(mem_ctx, char *, *num_names);
+       *name_types = talloc_array(mem_ctx, uint32, *num_names);
+
+       if ((*sid_mem == NULL) || (*names == NULL) || (*name_types == NULL)) {
+               TALLOC_FREE(*sid_mem);
+               TALLOC_FREE(*names);
+               TALLOC_FREE(*name_types);
+               centry_free(centry);
+               return NT_STATUS_NO_MEMORY;
        }
 
        for (i=0; i<(*num_names); i++) {
-               centry_sid(centry, mem_ctx, &(*sid_mem)[i]);
+               centry_sid(centry, &(*sid_mem)[i]);
                (*names)[i] = centry_string(centry, mem_ctx);
                (*name_types)[i] = centry_uint32(centry);
        }
 
-do_cached:     
        status = centry->status;
 
-       DEBUG(10,("lookup_groupmem: [Cached] - cached info for domain %s status: %s\n",
-               domain->name, nt_errstr(status)));
+       DEBUG(10,("lookup_groupmem: [Cached] - cached info for domain %s "
+                 "status: %s\n", domain->name, nt_errstr(status)));
 
        centry_free(centry);
        return status;
+}
+
+static NTSTATUS lookup_groupmem(struct winbindd_domain *domain,
+                               TALLOC_CTX *mem_ctx,
+                               const struct dom_sid *group_sid,
+                               enum lsa_SidType type,
+                               uint32 *num_names,
+                               struct dom_sid **sid_mem, char ***names,
+                               uint32 **name_types)
+{
+       struct cache_entry *centry = NULL;
+       NTSTATUS status;
+       unsigned int i;
+       fstring sid_string;
+       bool old_status;
+
+       old_status = domain->online;
+       status = wcache_lookup_groupmem(domain, mem_ctx, group_sid, num_names,
+                                       sid_mem, names, name_types);
+       if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
+               return status;
+       }
 
-do_query:
        (*num_names) = 0;
        (*sid_mem) = NULL;
        (*names) = NULL;
@@ -2208,11 +2689,30 @@ do_query:
        DEBUG(10,("lookup_groupmem: [Cached] - doing backend query for info for domain %s\n",
                domain->name ));
 
-       status = domain->backend->lookup_groupmem(domain, mem_ctx, group_sid, num_names, 
+       status = domain->backend->lookup_groupmem(domain, mem_ctx, group_sid,
+                                                 type, num_names,
                                                  sid_mem, names, name_types);
 
+       if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
+               NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
+               if (!domain->internal && old_status) {
+                       set_domain_offline(domain);
+               }
+               if (!domain->internal &&
+                       !domain->online &&
+                       old_status) {
+                       NTSTATUS cache_status;
+                       cache_status = wcache_lookup_groupmem(domain, mem_ctx, group_sid,
+                                                             num_names, sid_mem, names,
+                                                             name_types);
+                       return cache_status;
+               }
+       }
        /* and save it */
        refresh_sequence_number(domain, false);
+       if (!NT_STATUS_IS_OK(status)) {
+               return status;
+       }
        centry = centry_start(domain, status);
        if (!centry)
                goto skip_save;
@@ -2244,63 +2744,75 @@ static NTSTATUS sequence_number(struct winbindd_domain *domain, uint32 *seq)
  * Guenther */
 static NTSTATUS trusted_domains(struct winbindd_domain *domain,
                                TALLOC_CTX *mem_ctx,
-                               uint32 *num_domains,
-                               char ***names,
-                               char ***alt_names,
-                               DOM_SID **dom_sids)
+                               struct netr_DomainTrustList *trusts)
 {
-       struct winbind_cache *cache = get_cache(domain);
-       struct cache_entry *centry = NULL;
        NTSTATUS status;
+       struct winbind_cache *cache;
+       struct winbindd_tdc_domain *dom_list = NULL;
+       size_t num_domains = 0;
+       bool retval = false;
        int i;
+       bool old_status;
 
-       if (!cache->tdb)
-               goto do_query;
+       old_status = domain->online;
+       trusts->count = 0;
+       trusts->array = NULL;
 
-       centry = wcache_fetch(cache, domain, "TRUSTDOMS/%s", domain->name);
+       cache = get_cache(domain);
+       if (!cache || !cache->tdb) {
+               goto do_query;
+       }
 
-       if (!centry) {
-               goto do_query;
+       if (domain->online) {
+               goto do_query;
        }
 
-       *num_domains = centry_uint32(centry);
-
-       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);
+       retval = wcache_tdc_fetch_list(&dom_list, &num_domains);
+       if (!retval || !num_domains || !dom_list) {
+               TALLOC_FREE(dom_list);
+               goto do_query;
+       }
 
-               if (! (*dom_sids) || ! (*names) || ! (*alt_names)) {
-                       smb_panic_fn("trusted_domains out of memory");
-               }
-       } else {
-               (*names) = NULL;
-               (*alt_names) = NULL;
-               (*dom_sids) = NULL;
+do_fetch_cache:
+       trusts->array = TALLOC_ZERO_ARRAY(mem_ctx, struct netr_DomainTrust, num_domains);
+       if (!trusts->array) {
+               TALLOC_FREE(dom_list);
+               return NT_STATUS_NO_MEMORY;
        }
 
-       for (i=0; i<(*num_domains); i++) {
-               (*names)[i] = centry_string(centry, mem_ctx);
-               (*alt_names)[i] = centry_string(centry, mem_ctx);
-               if (!centry_sid(centry, mem_ctx, &(*dom_sids)[i])) {
-                       sid_copy(&(*dom_sids)[i], &global_sid_NULL);
+       for (i = 0; i < num_domains; i++) {
+               struct netr_DomainTrust *trust;
+               struct dom_sid *sid;
+               struct winbindd_domain *dom;
+
+               dom = find_domain_from_name_noinit(dom_list[i].domain_name);
+               if (dom && dom->internal) {
+                       continue;
                }
-       }
 
-       status = centry->status;
+               trust = &trusts->array[trusts->count];
+               trust->netbios_name = talloc_strdup(trusts->array, dom_list[i].domain_name);
+               trust->dns_name = talloc_strdup(trusts->array, dom_list[i].dns_name);
+               sid = talloc(trusts->array, struct dom_sid);
+               if (!trust->netbios_name || !trust->dns_name ||
+                       !sid) {
+                       TALLOC_FREE(dom_list);
+                       TALLOC_FREE(trusts->array);
+                       return NT_STATUS_NO_MEMORY;
+               }
 
-       DEBUG(10,("trusted_domains: [Cached] - cached info for domain %s (%d trusts) status: %s\n",
-               domain->name, *num_domains, nt_errstr(status) ));
+               trust->trust_flags = dom_list[i].trust_flags;
+               trust->trust_attributes = dom_list[i].trust_attribs;
+               trust->trust_type = dom_list[i].trust_type;
+               sid_copy(sid, &dom_list[i].sid);
+               trust->sid = sid;
+               trusts->count++;
+       }
 
-       centry_free(centry);
-       return status;
+       TALLOC_FREE(dom_list);
+       return NT_STATUS_OK;
 
 do_query:
-       (*num_domains) = 0;
-       (*dom_sids) = NULL;
-       (*names) = NULL;
-       (*alt_names) = NULL;
-
        /* Return status value returned by seq number check */
 
        if (!NT_STATUS_IS_OK(domain->last_status))
@@ -2309,9 +2821,24 @@ do_query:
        DEBUG(10,("trusted_domains: [Cached] - doing backend query for info for domain %s\n",
                domain->name ));
 
-       status = domain->backend->trusted_domains(domain, mem_ctx, num_domains,
-                                               names, alt_names, dom_sids);
+       status = domain->backend->trusted_domains(domain, mem_ctx, trusts);
 
+       if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
+               NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
+               if (!domain->internal && old_status) {
+                       set_domain_offline(domain);
+               }
+               if (!domain->internal &&
+                       !domain->online &&
+                       old_status) {
+                       retval = wcache_tdc_fetch_list(&dom_list, &num_domains);
+                       if (retval && num_domains && dom_list) {
+                               TALLOC_FREE(trusts->array);
+                               trusts->count = 0;
+                               goto do_fetch_cache;
+                       }
+               }
+       }
        /* no trusts gives NT_STATUS_NO_MORE_ENTRIES resetting to NT_STATUS_OK
         * so that the generic centry handling still applies correctly -
         * Guenther*/
@@ -2319,33 +2846,6 @@ do_query:
        if (!NT_STATUS_IS_ERR(status)) {
                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);
-
-       centry = centry_start(domain, status);
-       if (!centry)
-               goto skip_save;
-
-       centry_put_uint32(centry, *num_domains);
-
-       for (i=0; i<(*num_domains); i++) {
-               centry_put_string(centry, (*names)[i]);
-               centry_put_string(centry, (*alt_names)[i]);
-               centry_put_sid(centry, &(*dom_sids)[i]);
-       }
-
-       centry_end(centry, "TRUSTDOMS/%s", domain->name);
-
-       centry_free(centry);
-
-skip_save:
-#endif
-
        return status;
 }      
 
@@ -2357,7 +2857,9 @@ static NTSTATUS lockout_policy(struct winbindd_domain *domain,
        struct winbind_cache *cache = get_cache(domain);
        struct cache_entry *centry = NULL;
        NTSTATUS status;
+       bool old_status;
 
+       old_status = domain->online;
        if (!cache->tdb)
                goto do_query;
 
@@ -2366,6 +2868,7 @@ static NTSTATUS lockout_policy(struct winbindd_domain *domain,
        if (!centry)
                goto do_query;
 
+do_fetch_cache:
        policy->lockout_duration = centry_nttime(centry);
        policy->lockout_window = centry_nttime(centry);
        policy->lockout_threshold = centry_uint16(centry);
@@ -2391,8 +2894,26 @@ do_query:
 
        status = domain->backend->lockout_policy(domain, mem_ctx, policy);
 
+       if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
+               NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
+               if (!domain->internal && old_status) {
+                       set_domain_offline(domain);
+               }
+               if (cache->tdb &&
+                       !domain->internal &&
+                       !domain->online &&
+                       old_status) {
+                       centry = wcache_fetch(cache, domain, "LOC_POL/%s", domain->name);
+                       if (centry) {
+                               goto do_fetch_cache;
+                       }
+               }
+       }
        /* and save it */
        refresh_sequence_number(domain, false);
+       if (!NT_STATUS_IS_OK(status)) {
+               return status;
+       }
        wcache_save_lockout_policy(domain, status, policy);
 
        return status;
@@ -2406,7 +2927,9 @@ static NTSTATUS password_policy(struct winbindd_domain *domain,
        struct winbind_cache *cache = get_cache(domain);
        struct cache_entry *centry = NULL;
        NTSTATUS status;
+       bool old_status;
 
+       old_status = domain->online;
        if (!cache->tdb)
                goto do_query;
 
@@ -2415,6 +2938,7 @@ static NTSTATUS password_policy(struct winbindd_domain *domain,
        if (!centry)
                goto do_query;
 
+do_fetch_cache:
        policy->min_password_length = centry_uint16(centry);
        policy->password_history_length = centry_uint16(centry);
        policy->password_properties = centry_uint32(centry);
@@ -2442,11 +2966,27 @@ do_query:
 
        status = domain->backend->password_policy(domain, mem_ctx, policy);
 
+       if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
+               NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
+               if (!domain->internal && old_status) {
+                       set_domain_offline(domain);
+               }
+               if (cache->tdb &&
+                       !domain->internal &&
+                       !domain->online &&
+                       old_status) {
+                       centry = wcache_fetch(cache, domain, "PWD_POL/%s", domain->name);
+                       if (centry) {
+                               goto do_fetch_cache;
+                       }
+               }
+       }
        /* and save it */
        refresh_sequence_number(domain, false);
-       if (NT_STATUS_IS_OK(status)) {
-               wcache_save_password_policy(domain, status, policy);
+       if (!NT_STATUS_IS_OK(status)) {
+               return status;
        }
+       wcache_save_password_policy(domain, status, policy);
 
        return status;
 }
@@ -2467,9 +3007,8 @@ static int traverse_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
 /* Invalidate the getpwnam and getgroups entries for a winbindd domain */
 
 void wcache_invalidate_samlogon(struct winbindd_domain *domain, 
-                               struct netr_SamInfo3 *info3)
+                               const struct dom_sid *sid)
 {
-        DOM_SID sid;
         fstring key_str, sid_string;
        struct winbind_cache *cache;
 
@@ -2489,21 +3028,18 @@ void wcache_invalidate_samlogon(struct winbindd_domain *domain,
                 return;
         }
 
-       sid_copy(&sid, info3->base.domain_sid);
-       sid_append_rid(&sid, info3->base.rid);
-
        /* Clear U/SID cache entry */
-       fstr_sprintf(key_str, "U/%s", sid_to_fstring(sid_string, &sid));
+       fstr_sprintf(key_str, "U/%s", sid_to_fstring(sid_string, sid));
        DEBUG(10, ("wcache_invalidate_samlogon: clearing %s\n", key_str));
        tdb_delete(cache->tdb, string_tdb_data(key_str));
 
        /* Clear UG/SID cache entry */
-       fstr_sprintf(key_str, "UG/%s", sid_to_fstring(sid_string, &sid));
+       fstr_sprintf(key_str, "UG/%s", sid_to_fstring(sid_string, sid));
        DEBUG(10, ("wcache_invalidate_samlogon: clearing %s\n", key_str));
        tdb_delete(cache->tdb, string_tdb_data(key_str));
 
        /* Samba/winbindd never needs this. */
-       netsamlogon_clear_cached_user(info3);
+       netsamlogon_clear_cached_user(sid);
 }
 
 bool wcache_invalidate_cache(void)
@@ -2526,6 +3062,39 @@ bool wcache_invalidate_cache(void)
        return true;
 }
 
+bool wcache_invalidate_cache_noinit(void)
+{
+       struct winbindd_domain *domain;
+
+       for (domain = domain_list(); domain; domain = domain->next) {
+               struct winbind_cache *cache;
+
+               /* Skip uninitialized domains. */
+               if (!domain->initialized && !domain->internal) {
+                       continue;
+               }
+
+               cache = get_cache(domain);
+
+               DEBUG(10, ("wcache_invalidate_cache: invalidating cache "
+                          "entries for %s\n", domain->name));
+               if (cache) {
+                       if (cache->tdb) {
+                               tdb_traverse(cache->tdb, traverse_fn, NULL);
+                               /*
+                                * Flushing cache has nothing to with domains.
+                                * return here if we successfully flushed once.
+                                * To avoid unnecessary traversing the cache.
+                                */
+                               return true;
+                       } else {
+                               return false;
+                       }
+               }
+       }
+       return true;
+}
+
 bool init_wcache(void)
 {
        if (wcache == NULL) {
@@ -2539,7 +3108,8 @@ bool init_wcache(void)
        /* when working offline we must not clear the cache on restart */
        wcache->tdb = tdb_open_log(cache_path("winbindd_cache.tdb"),
                                WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE, 
-                               lp_winbind_offline_logon() ? TDB_DEFAULT : (TDB_DEFAULT | TDB_CLEAR_IF_FIRST), 
+                               TDB_INCOMPATIBLE_HASH |
+                                       (lp_winbind_offline_logon() ? TDB_DEFAULT : (TDB_DEFAULT | TDB_CLEAR_IF_FIRST)),
                                O_RDWR|O_CREAT, 0600);
 
        if (wcache->tdb == NULL) {
@@ -2616,97 +3186,50 @@ void close_winbindd_cache(void)
        }
 }
 
-bool lookup_cached_sid(TALLOC_CTX *mem_ctx, const DOM_SID *sid,
+bool lookup_cached_sid(TALLOC_CTX *mem_ctx, const struct dom_sid *sid,
                       char **domain_name, char **name,
                       enum lsa_SidType *type)
 {
        struct winbindd_domain *domain;
-       struct winbind_cache *cache;
-       struct cache_entry *centry = NULL;
        NTSTATUS status;
-       fstring tmp;
 
        domain = find_lookup_domain_from_sid(sid);
        if (domain == NULL) {
                return false;
        }
-
-       cache = get_cache(domain);
-
-       if (cache->tdb == NULL) {
-               return false;
-       }
-
-       centry = wcache_fetch(cache, domain, "SN/%s",
-                             sid_to_fstring(tmp, sid));
-       if (centry == NULL) {
-               return false;
-       }
-
-       if (NT_STATUS_IS_OK(centry->status)) {
-               *type = (enum lsa_SidType)centry_uint32(centry);
-               *domain_name = centry_string(centry, mem_ctx);
-               *name = centry_string(centry, mem_ctx);
-       }
-
-       status = centry->status;
-       centry_free(centry);
+       status = wcache_sid_to_name(domain, sid, mem_ctx, domain_name, name,
+                                   type);
        return NT_STATUS_IS_OK(status);
 }
 
-bool lookup_cached_name(TALLOC_CTX *mem_ctx,
-                       const char *domain_name,
+bool lookup_cached_name(const char *domain_name,
                        const char *name,
-                       DOM_SID *sid,
+                       struct dom_sid *sid,
                        enum lsa_SidType *type)
 {
        struct winbindd_domain *domain;
-       struct winbind_cache *cache;
-       struct cache_entry *centry = NULL;
        NTSTATUS status;
-       fstring uname;
-       bool original_online_state;     
+       bool original_online_state;
 
        domain = find_lookup_domain_from_name(domain_name);
        if (domain == NULL) {
                return false;
        }
 
-       cache = get_cache(domain);
-
-       if (cache->tdb == NULL) {
-               return false;
-       }
-
-       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);
+       status = wcache_name_to_sid(domain, domain_name, name, sid, type);
        domain->online = original_online_state;
 
-       if (centry == NULL) {
-               return false;
-       }
-
-       if (NT_STATUS_IS_OK(centry->status)) {
-               *type = (enum lsa_SidType)centry_uint32(centry);
-               centry_sid(centry, mem_ctx, sid);
-       }
-
-       status = centry->status;
-       centry_free(centry);
-
        return NT_STATUS_IS_OK(status);
 }
 
 void cache_name2sid(struct winbindd_domain *domain, 
                    const char *domain_name, const char *name,
-                   enum lsa_SidType type, const DOM_SID *sid)
+                   enum lsa_SidType type, const struct dom_sid *sid)
 {
        refresh_sequence_number(domain, false);
        wcache_save_name_to_sid(domain, NT_STATUS_OK, domain_name, name,
@@ -2758,7 +3281,8 @@ void wcache_flush_cache(void)
        /* when working offline we must not clear the cache on restart */
        wcache->tdb = tdb_open_log(cache_path("winbindd_cache.tdb"),
                                WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE, 
-                               lp_winbind_offline_logon() ? TDB_DEFAULT : (TDB_DEFAULT | TDB_CLEAR_IF_FIRST), 
+                               TDB_INCOMPATIBLE_HASH |
+                               (lp_winbind_offline_logon() ? TDB_DEFAULT : (TDB_DEFAULT | TDB_CLEAR_IF_FIRST)),
                                O_RDWR|O_CREAT, 0600);
 
        if (!wcache->tdb) {
@@ -2831,7 +3355,7 @@ static int traverse_fn_get_credlist(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DAT
        return 0;
 }
 
-NTSTATUS wcache_remove_oldest_cached_creds(struct winbindd_domain *domain, const DOM_SID *sid) 
+NTSTATUS wcache_remove_oldest_cached_creds(struct winbindd_domain *domain, const struct dom_sid *sid)
 {
        struct winbind_cache *cache = get_cache(domain);
        NTSTATUS status;
@@ -2996,9 +3520,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;
@@ -3007,6 +3532,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_t(centry);
        return centry;
 }
 
@@ -3032,8 +3558,8 @@ static int validate_ns(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
 
        (void)centry_uint32(centry);
        if (NT_STATUS_IS_OK(centry->status)) {
-               DOM_SID sid;
-               (void)centry_sid(centry, mem_ctx, &sid);
+               struct dom_sid sid;
+               (void)centry_sid(centry, &sid);
        }
 
        centry_free(centry);
@@ -3072,7 +3598,7 @@ 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;
+       struct dom_sid sid;
 
        if (!centry) {
                return 1;
@@ -3083,8 +3609,8 @@ static int validate_u(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
        (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);
+       (void)centry_sid(centry, &sid);
+       (void)centry_sid(centry, &sid);
 
        centry_free(centry);
 
@@ -3180,13 +3706,13 @@ static int validate_ul(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
        num_entries = (int32)centry_uint32(centry);
 
        for (i=0; i< num_entries; i++) {
-               DOM_SID sid;
+               struct 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);
+               (void)centry_sid(centry, &sid);
+               (void)centry_sid(centry, &sid);
        }
 
        centry_free(centry);
@@ -3238,8 +3764,8 @@ static int validate_ug(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
        num_groups = centry_uint32(centry);
 
        for (i=0; i< num_groups; i++) {
-               DOM_SID sid;
-               centry_sid(centry, mem_ctx, &sid);
+               struct dom_sid sid;
+               centry_sid(centry, &sid);
        }
 
        centry_free(centry);
@@ -3289,8 +3815,8 @@ static int validate_gm(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
        num_names = centry_uint32(centry);
 
        for (i=0; i< num_names; i++) {
-               DOM_SID sid;
-               centry_sid(centry, mem_ctx, &sid);
+               struct dom_sid sid;
+               centry_sid(centry, &sid);
                (void)centry_string(centry, mem_ctx);
                (void)centry_uint32(centry);
        }
@@ -3401,34 +3927,6 @@ static int validate_nss_na(TALLOC_CTX *mem_ctx, const char *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)
@@ -3460,6 +3958,16 @@ static int validate_offline(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA db
        return 0;
 }
 
+static int validate_ndr(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
+                       struct tdb_validation_status *state)
+{
+       /*
+        * Ignore validation for now. The proper way to do this is with a
+        * checksum. Just pure parsing does not really catch much.
+        */
+       return 0;
+}
+
 static int validate_cache_version(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
                                  struct tdb_validation_status *state)
 {
@@ -3500,11 +4008,11 @@ struct key_val_struct {
        {"DR/", validate_dr},
        {"DE/", validate_de},
        {"NSS/PWINFO/", validate_pwinfo},
-       {"TRUSTDOMS/", validate_trustdoms},
        {"TRUSTDOMCACHE/", validate_trustdomcache},
        {"NSS/NA/", validate_nss_na},
        {"NSS/AN/", validate_nss_an},
        {"WINBINDD_OFFLINE", validate_offline},
+       {"NDR/", validate_ndr},
        {WINBINDD_CACHE_VERSION_KEYSTR, validate_cache_version},
        {NULL, NULL}
 };
@@ -3594,6 +4102,7 @@ int winbindd_validate_cache(void)
 
        tdb = tdb_open_log(tdb_path, 
                           WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
+                          TDB_INCOMPATIBLE_HASH |
                           ( lp_winbind_offline_logon() 
                             ? TDB_DEFAULT 
                             : TDB_DEFAULT | TDB_CLEAR_IF_FIRST ),
@@ -3694,10 +4203,10 @@ static bool add_wbdomain_to_tdc_array( struct winbindd_domain *new_dom,
 
        if ( !set_only ) {
                if ( !*domains ) {
-                       list = TALLOC_ARRAY( NULL, struct winbindd_tdc_domain, 1 );
+                       list = talloc_array( NULL, struct winbindd_tdc_domain, 1 );
                        idx = 0;
                } else {
-                       list = TALLOC_REALLOC_ARRAY( *domains, *domains, 
+                       list = talloc_realloc( *domains, *domains, 
                                                     struct winbindd_tdc_domain,  
                                                     (*num_domains)+1);
                        idx = *num_domains;             
@@ -3837,7 +4346,7 @@ static size_t unpack_tdc_domains( unsigned char *buf, int buflen,
                return 0;
        }
 
-       list = TALLOC_ARRAY( NULL, struct winbindd_tdc_domain, num_domains );
+       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;               
@@ -4038,6 +4547,58 @@ struct winbindd_tdc_domain * wcache_tdc_fetch_domain( TALLOC_CTX *ctx, const cha
        return d;       
 }
 
+/*********************************************************************
+ ********************************************************************/
+
+struct winbindd_tdc_domain*
+       wcache_tdc_fetch_domainbysid(TALLOC_CTX *ctx,
+                                    const struct dom_sid *sid)
+{
+       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_domainbysid: Searching for domain %s\n",
+                 sid_string_dbg(sid)));
+
+       if (!init_wcache()) {
+               return false;
+       }
+
+       /* fetch the list */
+
+       wcache_tdc_fetch_list(&dom_list, &num_domains);
+
+       for (i = 0; i<num_domains; i++) {
+               if (sid_equal(sid, &(dom_list[i].sid))) {
+                       DEBUG(10, ("wcache_tdc_fetch_domainbysid: "
+                                  "Found domain %s for SID %s\n",
+                                  dom_list[i].domain_name,
+                                  sid_string_dbg(sid)));
+
+                       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;
+}
+
 
 /*********************************************************************
  ********************************************************************/
@@ -4058,7 +4619,7 @@ void wcache_tdc_clear( void )
 
 static void wcache_save_user_pwinfo(struct winbindd_domain *domain, 
                                    NTSTATUS status,
-                                   const DOM_SID *user_sid,
+                                   const struct dom_sid *user_sid,
                                    const char *homedir,
                                    const char *shell,
                                    const char *gecos,
@@ -4082,12 +4643,13 @@ static void wcache_save_user_pwinfo(struct winbindd_domain *domain,
        centry_free(centry);
 }
 
+#ifdef HAVE_ADS
+
 NTSTATUS nss_get_info_cached( struct winbindd_domain *domain, 
-                             const DOM_SID *user_sid,
+                             const struct dom_sid *user_sid,
                              TALLOC_CTX *ctx,
-                             ADS_STRUCT *ads, LDAPMessage *msg,
-                             char **homedir, char **shell, char **gecos,
-                             gid_t *p_gid)
+                             const char **homedir, const char **shell,
+                             const char **gecos, gid_t *p_gid)
 {
        struct winbind_cache *cache = get_cache(domain);
        struct cache_entry *centry = NULL;
@@ -4117,7 +4679,7 @@ NTSTATUS nss_get_info_cached( struct winbindd_domain *domain,
 
 do_query:
 
-       nt_status = nss_get_info( domain->name, user_sid, ctx, ads, msg, 
+       nt_status = nss_get_info( domain->name, user_sid, ctx,
                                  homedir, shell, gecos, p_gid );
 
        DEBUG(10, ("nss_get_info returned %s\n", nt_errstr(nt_status)));
@@ -4141,6 +4703,7 @@ do_query:
        return nt_status;       
 }
 
+#endif
 
 /* the cache backend methods are exposed via this structure */
 struct winbindd_methods cache_methods = {
@@ -4160,3 +4723,152 @@ struct winbindd_methods cache_methods = {
        password_policy,
        trusted_domains
 };
+
+static bool wcache_ndr_key(TALLOC_CTX *mem_ctx, char *domain_name,
+                          uint32_t opnum, const DATA_BLOB *req,
+                          TDB_DATA *pkey)
+{
+       char *key;
+       size_t keylen;
+
+       key = talloc_asprintf(mem_ctx, "NDR/%s/%d/", domain_name, (int)opnum);
+       if (key == NULL) {
+               return false;
+       }
+       keylen = talloc_get_size(key) - 1;
+
+       key = talloc_realloc(mem_ctx, key, char, keylen + req->length);
+       if (key == NULL) {
+               return false;
+       }
+       memcpy(key + keylen, req->data, req->length);
+
+       pkey->dptr = (uint8_t *)key;
+       pkey->dsize = talloc_get_size(key);
+       return true;
+}
+
+static bool wcache_opnum_cacheable(uint32_t opnum)
+{
+       switch (opnum) {
+       case NDR_WBINT_PING:
+       case NDR_WBINT_QUERYSEQUENCENUMBER:
+       case NDR_WBINT_ALLOCATEUID:
+       case NDR_WBINT_ALLOCATEGID:
+       case NDR_WBINT_CHECKMACHINEACCOUNT:
+       case NDR_WBINT_CHANGEMACHINEACCOUNT:
+       case NDR_WBINT_PINGDC:
+               return false;
+       }
+       return true;
+}
+
+bool wcache_fetch_ndr(TALLOC_CTX *mem_ctx, struct winbindd_domain *domain,
+                     uint32_t opnum, const DATA_BLOB *req, DATA_BLOB *resp)
+{
+       TDB_DATA key, data;
+       bool ret = false;
+
+       if (!wcache_opnum_cacheable(opnum) ||
+           is_my_own_sam_domain(domain) ||
+           is_builtin_domain(domain)) {
+               return false;
+       }
+
+       if (wcache->tdb == NULL) {
+               return false;
+       }
+
+       if (!wcache_ndr_key(talloc_tos(), domain->name, opnum, req, &key)) {
+               return false;
+       }
+       data = tdb_fetch(wcache->tdb, key);
+       TALLOC_FREE(key.dptr);
+
+       if (data.dptr == NULL) {
+               return false;
+       }
+       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)) {
+                       goto fail;
+               }
+               entry_seqnum = IVAL(data.dptr, 0);
+               if (entry_seqnum != dom_seqnum) {
+                       DEBUG(10, ("Entry has wrong sequence number: %d\n",
+                                  (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 + 12,
+                                             data.dsize - 12);
+       if (resp->data == NULL) {
+               DEBUG(10, ("talloc failed\n"));
+               goto fail;
+       }
+       resp->length = data.dsize - 12;
+
+       ret = true;
+fail:
+       SAFE_FREE(data.dptr);
+       return ret;
+}
+
+void wcache_store_ndr(struct winbindd_domain *domain, uint32_t opnum,
+                     const DATA_BLOB *req, const DATA_BLOB *resp)
+{
+       TDB_DATA key, data;
+       uint32_t dom_seqnum, last_check;
+       uint64_t timeout;
+
+       if (!wcache_opnum_cacheable(opnum) ||
+           is_my_own_sam_domain(domain) ||
+           is_builtin_domain(domain)) {
+               return;
+       }
+
+       if (wcache->tdb == NULL) {
+               return;
+       }
+
+       if (!wcache_fetch_seqnum(domain->name, &dom_seqnum, &last_check)) {
+               DEBUG(10, ("could not fetch seqnum for domain %s\n",
+                          domain->name));
+               return;
+       }
+
+       if (!wcache_ndr_key(talloc_tos(), domain->name, opnum, req, &key)) {
+               return;
+       }
+
+       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);
+       SBVAL(data.dptr, 4, timeout);
+       memcpy(data.dptr + 12, resp->data, resp->length);
+
+       tdb_store(wcache->tdb, key, data, 0);
+
+done:
+       TALLOC_FREE(key.dptr);
+       return;
+}