winbindd: Use a remote RPC server when we are an RODC when needed
[samba.git] / source3 / winbindd / winbindd_cache.c
index 44c6f2b80bd772fc6c80a83ea52d775050997f79..bfd78daef537e45a1551536677f3b958059de72b 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 "../librpc/gen_ndr/ndr_winbind.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 2
+#define WINBINDD_CACHE_VER1 1 /* initial db version */
+#define WINBINDD_CACHE_VER2 2 /* second version with timeouts for NDR entries */
+
+#define WINBINDD_CACHE_VERSION WINBINDD_CACHE_VER2
 #define WINBINDD_CACHE_VERSION_KEYSTR "WINBINDD_CACHE_VERSION"
 
 extern struct winbindd_methods reconnect_methods;
@@ -53,8 +59,6 @@ extern struct winbindd_methods sam_passdb_methods;
 
 static const char *non_centry_keys[] = {
        "SEQNUM/",
-       "DR/",
-       "DE/",
        "WINBINDD_OFFLINE",
        WINBINDD_CACHE_VERSION_KEYSTR,
        NULL
@@ -116,17 +120,20 @@ static struct winbind_cache *get_cache(struct winbindd_domain *domain)
 
        if (domain->internal) {
                domain->backend = &builtin_passdb_methods;
-               domain->initialized = True;
+       }
+
+       if (dom_sid_equal(&domain->sid, &global_sid_Builtin)) {
+               domain->initialized = true;
        }
 
        if (strequal(domain->name, get_global_sam_name()) &&
-           sid_check_is_domain(&domain->sid)) {
+           sid_check_is_our_sam(&domain->sid)) {
                domain->backend = &sam_passdb_methods;
-               domain->initialized = True;
        }
 
        if ( !domain->initialized ) {
-               init_dc_connection( domain );
+               /* We do not need a connection to an RW DC for cache operation */
+               init_dc_connection(domain, false);
        }
 
        /* 
@@ -244,7 +251,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;
 }
@@ -306,7 +313,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");
        }
@@ -336,7 +343,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");
        }
@@ -481,9 +488,9 @@ bool wcache_store_seqnum(const char *domain_name, uint32_t seqnum,
        ret = tdb_store_bystring(wcache->tdb, key_str,
                                 make_tdb_data(buf, sizeof(buf)), TDB_REPLACE);
        TALLOC_FREE(key_str);
-       if (ret == -1) {
+       if (ret != 0) {
                DEBUG(10, ("tdb_store_bystring failed: %s\n",
-                          tdb_errorstr(wcache->tdb)));
+                          tdb_errorstr_compat(wcache->tdb)));
                TALLOC_FREE(key_str);
                return false;
        }
@@ -635,7 +642,7 @@ static struct cache_entry *wcache_fetch_raw(char *kstr)
        TDB_DATA key;
 
        key = string_tdb_data(kstr);
-       data = tdb_fetch(wcache->tdb, key);
+       data = tdb_fetch_compat(wcache->tdb, key);
        if (!data.dptr) {
                /* a cache miss */
                return NULL;
@@ -664,7 +671,7 @@ static struct cache_entry *wcache_fetch_raw(char *kstr)
 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)) {
+           sid_check_is_our_sam(&domain->sid)) {
                return true;
        }
 
@@ -790,7 +797,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;
 }
 
@@ -882,7 +889,8 @@ static void centry_put_time(struct cache_entry *centry, time_t t)
 /*
   start a centry for output. When finished, call centry_end()
 */
-struct cache_entry *centry_start(struct winbindd_domain *domain, NTSTATUS status)
+static struct cache_entry *centry_start(struct winbindd_domain *domain,
+                                       NTSTATUS status)
 {
        struct cache_entry *centry;
 
@@ -939,10 +947,19 @@ static void wcache_save_name_to_sid(struct winbindd_domain *domain,
        centry = centry_start(domain, status);
        if (!centry)
                return;
+
+       if ((domain_name == NULL) || (domain_name[0] == '\0')) {
+               struct winbindd_domain *mydomain =
+                       find_domain_from_sid_noinit(sid);
+               if (mydomain != NULL) {
+                       domain_name = mydomain->name;
+               }
+       }
+
        centry_put_uint32(centry, type);
        centry_put_sid(centry, sid);
        fstrcpy(uname, name);
-       strupper_m(uname);
+       (void)strupper_m(uname);
        centry_end(centry, "NS/%s/%s", domain_name, uname);
        DEBUG(10,("wcache_save_name_to_sid: %s\\%s -> %s (%s)\n", domain_name,
                  uname, sid_string_dbg(sid), nt_errstr(status)));
@@ -959,6 +976,14 @@ static void wcache_save_sid_to_name(struct winbindd_domain *domain, NTSTATUS sta
        if (!centry)
                return;
 
+       if ((domain_name == NULL) || (domain_name[0] == '\0')) {
+               struct winbindd_domain *mydomain =
+                       find_domain_from_sid_noinit(sid);
+               if (mydomain != NULL) {
+                       domain_name = mydomain->name;
+               }
+       }
+
        if (NT_STATUS_IS_OK(status)) {
                centry_put_uint32(centry, type);
                centry_put_string(centry, domain_name);
@@ -966,8 +991,8 @@ static void wcache_save_sid_to_name(struct winbindd_domain *domain, NTSTATUS sta
        }
 
        centry_end(centry, "SN/%s", sid_to_fstring(sid_string, sid));
-       DEBUG(10,("wcache_save_sid_to_name: %s -> %s (%s)\n", sid_string, 
-                 name, nt_errstr(status)));
+       DEBUG(10,("wcache_save_sid_to_name: %s -> %s\\%s (%s)\n", sid_string,
+                 domain_name, name, nt_errstr(status)));
        centry_free(centry);
 }
 
@@ -1060,7 +1085,7 @@ static void wcache_save_username_alias(struct winbindd_domain *domain,
        centry_put_string( centry, alias );
 
        fstrcpy(uname, name);
-       strupper_m(uname);
+       (void)strupper_m(uname);
        centry_end(centry, "NSS/NA/%s", uname);
 
        DEBUG(10,("wcache_save_username_alias: %s -> %s\n", name, alias ));
@@ -1081,7 +1106,7 @@ static void wcache_save_alias_username(struct winbindd_domain *domain,
        centry_put_string( centry, name );
 
        fstrcpy(uname, alias);
-       strupper_m(uname);
+       (void)strupper_m(uname);
        centry_end(centry, "NSS/AN/%s", uname);
 
        DEBUG(10,("wcache_save_alias_username: %s -> %s\n", alias, name ));
@@ -1107,13 +1132,18 @@ NTSTATUS resolve_username_to_alias( TALLOC_CTX *mem_ctx,
        if (!cache->tdb)
                goto do_query;
 
-       if ( (upper_name = SMB_STRDUP(name)) == NULL )
+       upper_name = talloc_strdup(mem_ctx, name);
+       if (upper_name == NULL) {
                return NT_STATUS_NO_MEMORY;
-       strupper_m(upper_name);
+       }
+       if (!strupper_m(upper_name)) {
+               talloc_free(upper_name);
+               return NT_STATUS_INVALID_PARAMETER;
+       }
 
        centry = wcache_fetch(cache, domain, "NSS/NA/%s", upper_name);
 
-       SAFE_FREE( upper_name );
+       talloc_free(upper_name);
 
        if (!centry)
                goto do_query;
@@ -1182,13 +1212,18 @@ NTSTATUS resolve_alias_to_username( TALLOC_CTX *mem_ctx,
        if (!cache->tdb)
                goto do_query;
 
-       if ( (upper_name = SMB_STRDUP(alias)) == NULL )
+       upper_name = talloc_strdup(mem_ctx, alias);
+       if (upper_name == NULL) {
                return NT_STATUS_NO_MEMORY;
-       strupper_m(upper_name);
+       }
+       if (!strupper_m(upper_name)) {
+               talloc_free(upper_name);
+               return NT_STATUS_INVALID_PARAMETER;
+       }
 
        centry = wcache_fetch(cache, domain, "NSS/AN/%s", upper_name);
 
-       SAFE_FREE( upper_name );
+       talloc_free(upper_name);
 
        if (!centry)
                goto do_query;
@@ -1268,7 +1303,7 @@ NTSTATUS wcache_cached_creds_exist(struct winbindd_domain *domain, const struct
 
        fstr_sprintf(key_str, "CRED/%s", sid_to_fstring(tmp, sid));
 
-       data = tdb_fetch(cache->tdb, string_tdb_data(key_str));
+       data = tdb_fetch_compat(cache->tdb, string_tdb_data(key_str));
        if (!data.dptr) {
                return NT_STATUS_OBJECT_NAME_NOT_FOUND;
        }
@@ -1289,7 +1324,6 @@ NTSTATUS wcache_get_creds(struct winbindd_domain *domain,
        struct winbind_cache *cache = get_cache(domain);
        struct cache_entry *centry = NULL;
        NTSTATUS status;
-       time_t t;
        uint32 rid;
        fstring tmp;
 
@@ -1316,7 +1350,12 @@ NTSTATUS wcache_get_creds(struct winbindd_domain *domain,
                return NT_STATUS_OBJECT_NAME_NOT_FOUND;
        }
 
-       t = centry_time(centry);
+       /*
+        * We don't use the time element at this moment,
+        * but we have to consume it, so that we don't
+        * neet to change the disk format of the cache.
+        */
+       (void)centry_time(centry);
 
        /* In the salted case this isn't actually the nt_hash itself,
           but the MD5 of the salt + nt_hash. Let the caller
@@ -1429,7 +1468,7 @@ do_fetch_cache:
        if (*num_entries == 0)
                goto do_cached;
 
-       (*info) = TALLOC_ARRAY(mem_ctx, struct wbint_userinfo, *num_entries);
+       (*info) = talloc_array(mem_ctx, struct wbint_userinfo, *num_entries);
        if (! (*info)) {
                smb_panic_fn("query_user_list out of memory");
        }
@@ -1559,7 +1598,7 @@ 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;
@@ -1581,7 +1620,7 @@ do_fetch_cache:
        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");
        }
@@ -1654,7 +1693,7 @@ 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;
@@ -1676,7 +1715,7 @@ do_fetch_cache:
        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");
        }
@@ -1775,6 +1814,10 @@ NTSTATUS wcache_name_to_sid(struct winbindd_domain *domain,
                return NT_STATUS_NO_MEMORY;
        }
 
+       if ((domain_name == NULL) || (domain_name[0] == '\0')) {
+               domain_name = domain->name;
+       }
+
        centry = wcache_fetch(cache, domain, "NS/%s/%s", domain_name, uname);
        TALLOC_FREE(uname);
        if (centry == NULL) {
@@ -1854,8 +1897,10 @@ static NTSTATUS name_to_sid(struct winbindd_domain *domain,
 
                /* 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));
+                       if (!strupper_m(discard_const_p(char, domain_name))) {
+                               return NT_STATUS_INVALID_PARAMETER;
+                       }
+                       (void)strlower_m(discard_const_p(char, name));
                        wcache_save_sid_to_name(domain, status, sid, domain_name, name, *type);
                }
        }
@@ -1999,8 +2044,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;
@@ -2042,12 +2087,14 @@ 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 {
                        /* something's definitely wrong */
                        result = centry->status;
+                       centry_free(centry);
                        goto error;
                }
 
@@ -2082,6 +2129,19 @@ static NTSTATUS rids_to_names(struct winbindd_domain *domain,
                        old_status) {
                        have_mapped = have_unmapped = false;
 
+                       *names = talloc_array(mem_ctx, char *, num_rids);
+                       if (*names == NULL) {
+                               result = NT_STATUS_NO_MEMORY;
+                               goto error;
+                       }
+
+                       *types = talloc_array(mem_ctx, enum lsa_SidType,
+                                             num_rids);
+                       if (*types == NULL) {
+                               result = NT_STATUS_NO_MEMORY;
+                               goto error;
+                       }
+
                        for (i=0; i<num_rids; i++) {
                                struct dom_sid sid;
                                struct cache_entry *centry;
@@ -2123,6 +2183,7 @@ static NTSTATUS rids_to_names(struct winbindd_domain *domain,
                                } else {
                                        /* something's definitely wrong */
                                        result = centry->status;
+                                       centry_free(centry);
                                        goto error;
                                }
 
@@ -2766,7 +2827,7 @@ static NTSTATUS trusted_domains(struct winbindd_domain *domain,
        }
 
 do_fetch_cache:
-       trusts->array = TALLOC_ZERO_ARRAY(mem_ctx, struct netr_DomainTrust, num_domains);
+       trusts->array = talloc_zero_array(mem_ctx, struct netr_DomainTrust, num_domains);
        if (!trusts->array) {
                TALLOC_FREE(dom_list);
                return NT_STATUS_NO_MEMORY;
@@ -2999,9 +3060,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)
 {
-        struct dom_sid sid;
         fstring key_str, sid_string;
        struct winbind_cache *cache;
 
@@ -3021,20 +3081,18 @@ void wcache_invalidate_samlogon(struct winbindd_domain *domain,
                 return;
         }
 
-       sid_compose(&sid, info3->base.domain_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)
@@ -3101,7 +3159,7 @@ bool init_wcache(void)
                return true;
 
        /* when working offline we must not clear the cache on restart */
-       wcache->tdb = tdb_open_log(cache_path("winbindd_cache.tdb"),
+       wcache->tdb = tdb_open_log(state_path("winbindd_cache.tdb"),
                                WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE, 
                                TDB_INCOMPATIBLE_HASH |
                                        (lp_winbind_offline_logon() ? TDB_DEFAULT : (TDB_DEFAULT | TDB_CLEAR_IF_FIRST)),
@@ -3145,9 +3203,9 @@ bool initialize_winbindd_cache(void)
                tdb_close(wcache->tdb);
                wcache->tdb = NULL;
 
-               if (unlink(cache_path("winbindd_cache.tdb")) == -1) {
+               if (unlink(state_path("winbindd_cache.tdb")) == -1) {
                        DEBUG(0,("initialize_winbindd_cache: unlink %s failed %s ",
-                               cache_path("winbindd_cache.tdb"),
+                               state_path("winbindd_cache.tdb"),
                                strerror(errno) ));
                        return false;
                }
@@ -3160,7 +3218,7 @@ bool initialize_winbindd_cache(void)
                /* Write the version. */
                if (!tdb_store_uint32(wcache->tdb, WINBINDD_CACHE_VERSION_KEYSTR, WINBINDD_CACHE_VERSION)) {
                        DEBUG(0,("initialize_winbindd_cache: version number store failed %s\n",
-                               tdb_errorstr(wcache->tdb) ));
+                               tdb_errorstr_compat(wcache->tdb) ));
                        return false;
                }
        }
@@ -3274,7 +3332,7 @@ 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"),
+       wcache->tdb = tdb_open_log(state_path("winbindd_cache.tdb"),
                                WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE, 
                                TDB_INCOMPATIBLE_HASH |
                                (lp_winbind_offline_logon() ? TDB_DEFAULT : (TDB_DEFAULT | TDB_CLEAR_IF_FIRST)),
@@ -3378,7 +3436,7 @@ NTSTATUS wcache_remove_oldest_cached_creds(struct winbindd_domain *domain, const
        ret = tdb_traverse(cache->tdb, traverse_fn_get_credlist, NULL);
        if (ret == 0) {
                return NT_STATUS_OK;
-       } else if ((ret == -1) || (wcache_cred_list == NULL)) {
+       } else if ((ret < 0) || (wcache_cred_list == NULL)) {
                return NT_STATUS_OBJECT_NAME_NOT_FOUND;
        }
 
@@ -3389,7 +3447,7 @@ NTSTATUS wcache_remove_oldest_cached_creds(struct winbindd_domain *domain, const
                TDB_DATA data;
                time_t t;
 
-               data = tdb_fetch(cache->tdb, string_tdb_data(cred->name));
+               data = tdb_fetch_compat(cache->tdb, string_tdb_data(cred->name));
                if (!data.dptr) {
                        DEBUG(10,("wcache_remove_oldest_cached_creds: entry for [%s] not found\n", 
                                cred->name));
@@ -3507,7 +3565,7 @@ static struct cache_entry *create_centry_validate(const char *kstr, TDB_DATA dat
        struct cache_entry *centry;
 
        centry = SMB_XMALLOC_P(struct cache_entry);
-       centry->data = (unsigned char *)memdup(data.dptr, data.dsize);
+       centry->data = (unsigned char *)smb_memdup(data.dptr, data.dsize);
        if (!centry->data) {
                SAFE_FREE(centry);
                return NULL;
@@ -4024,7 +4082,8 @@ static int cache_traverse_validate_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_D
        struct tdb_validation_status *v_state = (struct tdb_validation_status *)state;
 
        /* Paranoia check. */
-       if (strncmp("UA/", (const char *)kbuf.dptr, 3) == 0) {
+       if (strncmp("UA/", (const char *)kbuf.dptr, 3) == 0 ||
+           strncmp("NDR/", (const char *)kbuf.dptr, 4) == 0) {
                max_key_len = 1024 * 1024;
        }
        if (kbuf.dsize > max_key_len) {
@@ -4080,6 +4139,70 @@ static void validate_panic(const char *const why)
        exit(47);
 }
 
+static int wbcache_update_centry_fn(TDB_CONTEXT *tdb,
+                                   TDB_DATA key,
+                                   TDB_DATA data,
+                                   void *state)
+{
+       uint64_t ctimeout;
+       TDB_DATA blob;
+
+       if (is_non_centry_key(key)) {
+               return 0;
+       }
+
+       if (data.dptr == NULL || data.dsize == 0) {
+               if (tdb_delete(tdb, key) < 0) {
+                       DEBUG(0, ("tdb_delete for [%s] failed!\n",
+                                 key.dptr));
+                       return 1;
+               }
+       }
+
+       /* add timeout to blob (uint64_t) */
+       blob.dsize = data.dsize + 8;
+
+       blob.dptr = SMB_XMALLOC_ARRAY(uint8_t, blob.dsize);
+       if (blob.dptr == NULL) {
+               return 1;
+       }
+       memset(blob.dptr, 0, blob.dsize);
+
+       /* copy status and seqnum */
+       memcpy(blob.dptr, data.dptr, 8);
+
+       /* add timeout */
+       ctimeout = lp_winbind_cache_time() + time(NULL);
+       SBVAL(blob.dptr, 8, ctimeout);
+
+       /* copy the rest */
+       memcpy(blob.dptr + 16, data.dptr + 8, data.dsize - 8);
+
+       if (tdb_store(tdb, key, blob, TDB_REPLACE) < 0) {
+               DEBUG(0, ("tdb_store to update [%s] failed!\n",
+                         key.dptr));
+               SAFE_FREE(blob.dptr);
+               return 1;
+       }
+
+       SAFE_FREE(blob.dptr);
+       return 0;
+}
+
+static bool wbcache_upgrade_v1_to_v2(TDB_CONTEXT *tdb)
+{
+       int rc;
+
+       DEBUG(1, ("Upgrade to version 2 of the winbindd_cache.tdb\n"));
+
+       rc = tdb_traverse(tdb, wbcache_update_centry_fn, NULL);
+       if (rc < 0) {
+               return false;
+       }
+
+       return true;
+}
+
 /***********************************************************************
  Try and validate every entry in the winbindd cache. If we fail here,
  delete the cache tdb and return non-zero.
@@ -4088,13 +4211,14 @@ static void validate_panic(const char *const why)
 int winbindd_validate_cache(void)
 {
        int ret = -1;
-       const char *tdb_path = cache_path("winbindd_cache.tdb");
+       const char *tdb_path = state_path("winbindd_cache.tdb");
        TDB_CONTEXT *tdb = NULL;
+       uint32_t vers_id;
+       bool ok;
 
        DEBUG(10, ("winbindd_validate_cache: replacing panic function\n"));
        smb_panic_fn = validate_panic;
 
-
        tdb = tdb_open_log(tdb_path, 
                           WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
                           TDB_INCOMPATIBLE_HASH |
@@ -4108,6 +4232,30 @@ int winbindd_validate_cache(void)
                          "error opening/initializing tdb\n"));
                goto done;
        }
+
+       /* Version check and upgrade code. */
+       if (!tdb_fetch_uint32(tdb, WINBINDD_CACHE_VERSION_KEYSTR, &vers_id)) {
+               DEBUG(10, ("Fresh database\n"));
+               tdb_store_uint32(tdb, WINBINDD_CACHE_VERSION_KEYSTR, WINBINDD_CACHE_VERSION);
+               vers_id = WINBINDD_CACHE_VERSION;
+       }
+
+       if (vers_id != WINBINDD_CACHE_VERSION) {
+               if (vers_id == WINBINDD_CACHE_VER1) {
+                       ok = wbcache_upgrade_v1_to_v2(tdb);
+                       if (!ok) {
+                               DEBUG(10, ("winbindd_validate_cache: upgrade to version 2 failed.\n"));
+                               unlink(tdb_path);
+                               goto done;
+                       }
+
+                       tdb_store_uint32(tdb,
+                                        WINBINDD_CACHE_VERSION_KEYSTR,
+                                        WINBINDD_CACHE_VERSION);
+                       vers_id = WINBINDD_CACHE_VER2;
+               }
+       }
+
        tdb_close(tdb);
 
        ret = tdb_validate_and_backup(tdb_path, cache_traverse_validate_fn);
@@ -4131,7 +4279,7 @@ done:
 int winbindd_validate_cache_nobackup(void)
 {
        int ret = -1;
-       const char *tdb_path = cache_path("winbindd_cache.tdb");
+       const char *tdb_path = state_path("winbindd_cache.tdb");
 
        DEBUG(10, ("winbindd_validate_cache: replacing panic function\n"));
        smb_panic_fn = validate_panic;
@@ -4198,10 +4346,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;             
@@ -4213,8 +4361,16 @@ static bool add_wbdomain_to_tdc_array( struct winbindd_domain *new_dom,
        if ( !list )
                return false;
 
-       list[idx].domain_name = talloc_strdup( list, new_dom->name );
-       list[idx].dns_name = talloc_strdup( list, new_dom->alt_name );
+       list[idx].domain_name = talloc_strdup(list, new_dom->name);
+       if (list[idx].domain_name == NULL) {
+               return false;
+       }
+       if (new_dom->alt_name != NULL) {
+               list[idx].dns_name = talloc_strdup(list, new_dom->alt_name);
+               if (list[idx].dns_name == NULL) {
+                       return false;
+               }
+       }
 
        if ( !is_null_sid( &new_dom->sid ) ) {
                sid_copy( &list[idx].sid, &new_dom->sid );
@@ -4297,7 +4453,7 @@ static int pack_tdc_domains( struct winbindd_tdc_domain *domains,
 
                len += tdb_pack( buffer+len, buflen-len, "fffddd",
                                 domains[i].domain_name,
-                                domains[i].dns_name,
+                                domains[i].dns_name ? domains[i].dns_name : "",
                                 sid_to_fstring(tmp, &domains[i].sid),
                                 domains[i].trust_flags,
                                 domains[i].trust_attribs,
@@ -4341,14 +4497,16 @@ 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;               
        }
 
        for ( i=0; i<num_domains; i++ ) {
-               len += tdb_unpack( buf+len, buflen-len, "fffddd",
+               int this_len;
+
+               this_len = tdb_unpack( buf+len, buflen-len, "fffddd",
                                   domain_name,
                                   dns_name,
                                   sid_string,
@@ -4356,11 +4514,12 @@ static size_t unpack_tdc_domains( unsigned char *buf, int buflen,
                                   &attribs,
                                   &type );
 
-               if ( len == -1 ) {
+               if ( this_len == -1 ) {
                        DEBUG(5,("unpack_tdc_domains: Failed to unpack domain array\n"));
                        TALLOC_FREE( list );                    
                        return 0;
                }
+               len += this_len;
 
                DEBUG(11,("unpack_tdc_domains: Unpacking domain %s (%s) "
                          "SID %s, flags = 0x%x, attribs = 0x%x, type = 0x%x\n",
@@ -4368,7 +4527,10 @@ static size_t unpack_tdc_domains( unsigned char *buf, int buflen,
                          flags, attribs, type));
 
                list[i].domain_name = talloc_strdup( list, domain_name );
-               list[i].dns_name = talloc_strdup( list, dns_name );
+               list[i].dns_name = NULL;
+               if (dns_name[0] != '\0') {
+                       list[i].dns_name = talloc_strdup(list, dns_name);
+               }
                if ( !string_to_sid( &(list[i].sid), sid_string ) ) {                   
                        DEBUG(10,("unpack_tdc_domains: no SID for domain %s\n",
                                  domain_name));
@@ -4415,7 +4577,7 @@ static bool wcache_tdc_store_list( struct winbindd_tdc_domain *domains, size_t n
        SAFE_FREE( data.dptr );
        SAFE_FREE( key.dptr );
 
-       return ( ret != -1 );   
+       return ( ret == 0 );
 }
 
 /*********************************************************************
@@ -4432,7 +4594,7 @@ bool wcache_tdc_fetch_list( struct winbindd_tdc_domain **domains, size_t *num_do
        if ( !key.dptr )
                return false;
 
-       data = tdb_fetch( wcache->tdb, key );
+       data = tdb_fetch_compat( wcache->tdb, key );
 
        SAFE_FREE( key.dptr );
 
@@ -4495,6 +4657,38 @@ bool wcache_tdc_add_domain( struct winbindd_domain *domain )
        return ret;     
 }
 
+static struct winbindd_tdc_domain *wcache_tdc_dup_domain(
+       TALLOC_CTX *mem_ctx, const struct winbindd_tdc_domain *src)
+{
+       struct winbindd_tdc_domain *dst;
+
+       dst = talloc(mem_ctx, struct winbindd_tdc_domain);
+       if (dst == NULL) {
+               goto fail;
+       }
+       dst->domain_name = talloc_strdup(dst, src->domain_name);
+       if (dst->domain_name == NULL) {
+               goto fail;
+       }
+
+       dst->dns_name = NULL;
+       if (src->dns_name != NULL) {
+               dst->dns_name = talloc_strdup(dst, src->dns_name);
+               if (dst->dns_name == NULL) {
+                       goto fail;
+               }
+       }
+
+       sid_copy(&dst->sid, &src->sid);
+       dst->trust_flags = src->trust_flags;
+       dst->trust_type = src->trust_type;
+       dst->trust_attribs = src->trust_attribs;
+       return dst;
+fail:
+       TALLOC_FREE(dst);
+       return NULL;
+}
+
 /*********************************************************************
  ********************************************************************/
 
@@ -4508,7 +4702,7 @@ struct winbindd_tdc_domain * wcache_tdc_fetch_domain( TALLOC_CTX *ctx, const cha
        DEBUG(10,("wcache_tdc_fetch_domain: Searching for domain %s\n", name));
 
        if ( !init_wcache() ) {
-               return false;
+               return NULL;
        }
 
        /* fetch the list */
@@ -4522,17 +4716,7 @@ struct winbindd_tdc_domain * wcache_tdc_fetch_domain( TALLOC_CTX *ctx, const cha
                        DEBUG(10,("wcache_tdc_fetch_domain: Found domain %s\n",
                                  name));
 
-                       d = TALLOC_P( ctx, struct winbindd_tdc_domain );
-                       if ( !d )
-                               break;                  
-
-                       d->domain_name = talloc_strdup( d, dom_list[i].domain_name );
-                       d->dns_name = talloc_strdup( d, dom_list[i].dns_name );
-                       sid_copy( &d->sid, &dom_list[i].sid );
-                       d->trust_flags   = dom_list[i].trust_flags;
-                       d->trust_type    = dom_list[i].trust_type;
-                       d->trust_attribs = dom_list[i].trust_attribs;
-
+                       d = wcache_tdc_dup_domain(ctx, &dom_list[i]);
                        break;
                }
        }
@@ -4558,7 +4742,7 @@ struct winbindd_tdc_domain*
                  sid_string_dbg(sid)));
 
        if (!init_wcache()) {
-               return false;
+               return NULL;
        }
 
        /* fetch the list */
@@ -4566,25 +4750,13 @@ struct winbindd_tdc_domain*
        wcache_tdc_fetch_list(&dom_list, &num_domains);
 
        for (i = 0; i<num_domains; i++) {
-               if (sid_equal(sid, &(dom_list[i].sid))) {
+               if (dom_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;
-
+                       d = wcache_tdc_dup_domain(ctx, &dom_list[i]);
                        break;
                }
        }
@@ -4643,7 +4815,6 @@ static void wcache_save_user_pwinfo(struct winbindd_domain *domain,
 NTSTATUS nss_get_info_cached( struct winbindd_domain *domain, 
                              const struct dom_sid *user_sid,
                              TALLOC_CTX *ctx,
-                             ADS_STRUCT *ads, LDAPMessage *msg,
                              const char **homedir, const char **shell,
                              const char **gecos, gid_t *p_gid)
 {
@@ -4675,7 +4846,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)));
@@ -4720,7 +4891,7 @@ struct winbindd_methods cache_methods = {
        trusted_domains
 };
 
-static bool wcache_ndr_key(TALLOC_CTX *mem_ctx, char *domain_name,
+static bool wcache_ndr_key(TALLOC_CTX *mem_ctx, const char *domain_name,
                           uint32_t opnum, const DATA_BLOB *req,
                           TDB_DATA *pkey)
 {
@@ -4778,7 +4949,7 @@ bool wcache_fetch_ndr(TALLOC_CTX *mem_ctx, struct winbindd_domain *domain,
        if (!wcache_ndr_key(talloc_tos(), domain->name, opnum, req, &key)) {
                return false;
        }
-       data = tdb_fetch(wcache->tdb, key);
+       data = tdb_fetch_compat(wcache->tdb, key);
        TALLOC_FREE(key.dptr);
 
        if (data.dptr == NULL) {
@@ -4803,7 +4974,7 @@ bool wcache_fetch_ndr(TALLOC_CTX *mem_ctx, struct winbindd_domain *domain,
                        goto fail;
                }
                entry_timeout = BVAL(data.dptr, 4);
-               if (entry_timeout > time(NULL)) {
+               if (time(NULL) > entry_timeout) {
                        DEBUG(10, ("Entry has timed out\n"));
                        goto fail;
                }