r22855: fix the build
[sfrench/samba-autobuild/.git] / source / nsswitch / winbindd_cache.c
index 86e57cb07c21dda972d2f2b68b1c3781287cabdd..28a0a0942288d53403ad567d31c7228d4d9b25eb 100644 (file)
@@ -4,9 +4,10 @@
    Winbind cache backend functions
 
    Copyright (C) Andrew Tridgell 2001
-   Copyright (C) Gerald Carter   2003
+   Copyright (C) Gerald Carter   2003-2007
    Copyright (C) Volker Lendecke 2005
    Copyright (C) Guenther Deschner 2005
+   Copyright (C) Michael Adam    2007
    
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
@@ -223,7 +224,6 @@ static uint32 centry_uint32(struct cache_entry *centry)
 
        if (!centry_check_bytes(centry, 4)) {
                smb_panic_fn("centry_uint32");
-               return (uint32)-1;
        }
        ret = IVAL(centry->data, centry->ofs);
        centry->ofs += 4;
@@ -238,7 +238,6 @@ static uint16 centry_uint16(struct cache_entry *centry)
        uint16 ret;
        if (!centry_check_bytes(centry, 2)) {
                smb_panic_fn("centry_uint16");
-               return (uint16)-1;
        }
        ret = CVAL(centry->data, centry->ofs);
        centry->ofs += 2;
@@ -253,7 +252,6 @@ static uint8 centry_uint8(struct cache_entry *centry)
        uint8 ret;
        if (!centry_check_bytes(centry, 1)) {
                smb_panic_fn("centry_uint8");
-               return (uint8)-1;
        }
        ret = CVAL(centry->data, centry->ofs);
        centry->ofs += 1;
@@ -268,7 +266,6 @@ static NTTIME centry_nttime(struct cache_entry *centry)
        NTTIME ret;
        if (!centry_check_bytes(centry, 8)) {
                smb_panic_fn("centry_nttime");
-               return (NTTIME)-1;
        }
        ret = IVAL(centry->data, centry->ofs);
        centry->ofs += 4;
@@ -302,13 +299,11 @@ static char *centry_string(struct cache_entry *centry, TALLOC_CTX *mem_ctx)
 
        if (!centry_check_bytes(centry, (size_t)len)) {
                smb_panic_fn("centry_string");
-               return NULL;
        }
 
        ret = TALLOC_ARRAY(mem_ctx, char, len+1);
        if (!ret) {
                smb_panic_fn("centry_string out of memory\n");
-               return NULL;
        }
        memcpy(ret,centry->data + centry->ofs, len);
        ret[len] = 0;
@@ -339,7 +334,6 @@ static char *centry_hash16(struct cache_entry *centry, TALLOC_CTX *mem_ctx)
        ret = TALLOC_ARRAY(mem_ctx, char, 16);
        if (!ret) {
                smb_panic_fn("centry_hash out of memory\n");
-               return NULL;
        }
        memcpy(ret,centry->data + centry->ofs, 16);
        centry->ofs += 16;
@@ -458,6 +452,10 @@ 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) ) {
+               return;
+       }
+       
        get_cache( domain );
 
 #if 0  /* JERRY -- disable as the default cache time is now 5 minutes */
@@ -483,9 +481,17 @@ static void refresh_sequence_number(struct winbindd_domain *domain, BOOL force)
                goto done;      
 
        /* important! make sure that we know if this is a native 
-          mode domain or not */
+          mode domain or not.  And that we can contact it. */
+
+       if ( winbindd_can_contact_domain( domain ) ) {          
+               status = domain->backend->sequence_number(domain, 
+                                                         &domain->sequence_number);
+       } else {
+               /* just use the current time */
+               status = NT_STATUS_OK;
+               domain->sequence_number = time(NULL);
+       }
 
-       status = domain->backend->sequence_number(domain, &domain->sequence_number);
 
        /* the above call could have set our domain->backend to NULL when
         * coming from offline to online mode, make sure to reinitialize the
@@ -821,8 +827,8 @@ static void wcache_save_name_to_sid(struct winbindd_domain *domain,
        fstrcpy(uname, name);
        strupper_m(uname);
        centry_end(centry, "NS/%s/%s", domain_name, uname);
-       DEBUG(10,("wcache_save_name_to_sid: %s\\%s -> %s\n", domain_name, uname,
-                 sid_string_static(sid)));
+       DEBUG(10,("wcache_save_name_to_sid: %s\\%s -> %s (%s)\n", domain_name, uname,
+                 sid_string_static(sid), nt_errstr(status)));
        centry_free(centry);
 }
 
@@ -845,7 +851,8 @@ static void wcache_save_sid_to_name(struct winbindd_domain *domain, NTSTATUS sta
                centry_put_string(centry, name);
        }
        centry_end(centry, "SN/%s", sid_to_string(sid_string, sid));
-       DEBUG(10,("wcache_save_sid_to_name: %s -> %s\n", sid_string, name));
+       DEBUG(10,("wcache_save_sid_to_name: %s -> %s (%s)\n", sid_string, 
+                 name, nt_errstr(status)));
        centry_free(centry);
 }
 
@@ -894,7 +901,7 @@ static void wcache_save_lockout_policy(struct winbindd_domain *domain, NTSTATUS
 }
 
 static void wcache_save_password_policy(struct winbindd_domain *domain, NTSTATUS status, SAM_UNK_INFO_1 *policy)
- {
+{
        struct cache_entry *centry;
 
        centry = centry_start(domain, status);
@@ -1097,8 +1104,6 @@ static NTSTATUS query_user_list(struct winbindd_domain *domain,
        (*info) = TALLOC_ARRAY(mem_ctx, WINBIND_USERINFO, *num_entries);
        if (! (*info)) {
                smb_panic_fn("query_user_list out of memory");
-               centry_free(centry);
-               return NT_STATUS_NO_MEMORY;
        }
        for (i=0; i<(*num_entries); i++) {
                (*info)[i].acct_name = centry_string(centry, mem_ctx);
@@ -1215,8 +1220,6 @@ static NTSTATUS enum_dom_groups(struct winbindd_domain *domain,
        (*info) = TALLOC_ARRAY(mem_ctx, struct acct_info, *num_entries);
        if (! (*info)) {
                smb_panic_fn("enum_dom_groups out of memory");
-               centry_free(centry);
-               return NT_STATUS_NO_MEMORY;
        }
        for (i=0; i<(*num_entries); i++) {
                fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx));
@@ -1291,8 +1294,6 @@ static NTSTATUS enum_local_groups(struct winbindd_domain *domain,
        (*info) = TALLOC_ARRAY(mem_ctx, struct acct_info, *num_entries);
        if (! (*info)) {
                smb_panic_fn("enum_dom_groups out of memory");
-               centry_free(centry);
-               return NT_STATUS_NO_MEMORY;
        }
        for (i=0; i<(*num_entries); i++) {
                fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx));
@@ -1710,8 +1711,6 @@ static NTSTATUS lookup_usergroups(struct winbindd_domain *domain,
        (*user_gids) = TALLOC_ARRAY(mem_ctx, DOM_SID, *num_groups);
        if (! (*user_gids)) {
                smb_panic_fn("lookup_usergroups out of memory");
-               centry_free(centry);
-               return NT_STATUS_NO_MEMORY;
        }
        for (i=0; i<(*num_groups); i++) {
                centry_sid(centry, mem_ctx, &(*user_gids)[i]);
@@ -1740,6 +1739,9 @@ do_query:
 
        status = domain->backend->lookup_usergroups(domain, mem_ctx, user_sid, num_groups, user_gids);
 
+       if ( NT_STATUS_EQUAL(status, NT_STATUS_SYNCHRONIZATION_REQUIRED) )
+               goto skip_save;
+       
        /* and save it */
        refresh_sequence_number(domain, False);
        centry = centry_start(domain, status);
@@ -1876,8 +1878,6 @@ static NTSTATUS lookup_groupmem(struct winbindd_domain *domain,
 
        if (! (*sid_mem) || ! (*names) || ! (*name_types)) {
                smb_panic_fn("lookup_groupmem out of memory");
-               centry_free(centry);
-               return NT_STATUS_NO_MEMORY;
        }
 
        for (i=0; i<(*num_names); i++) {
@@ -1973,8 +1973,6 @@ static NTSTATUS trusted_domains(struct winbindd_domain *domain,
  
                if (! (*dom_sids) || ! (*names) || ! (*alt_names)) {
                        smb_panic_fn("trusted_domains out of memory");
-                       centry_free(centry);
-                       return NT_STATUS_NO_MEMORY;
                }
        } else {
                (*names) = NULL;
@@ -2021,6 +2019,10 @@ do_query:
                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);
  
@@ -2041,6 +2043,8 @@ do_query:
        centry_free(centry);
  
 skip_save:
+#endif
+
        return status;
 }      
 
@@ -2191,7 +2195,7 @@ void wcache_invalidate_cache(void)
        }
 }
 
-static BOOL init_wcache(void)
+BOOL init_wcache(void)
 {
        if (wcache == NULL) {
                wcache = SMB_XMALLOC_P(struct winbind_cache);
@@ -2296,7 +2300,7 @@ void cache_store_response(pid_t pid, struct winbindd_response *response)
 
        fstr_sprintf(key_str, "DE/%d", pid);
        if (tdb_store(wcache->tdb, string_tdb_data(key_str),
-                     make_tdb_data(response->extra_data.data,
+                     make_tdb_data((uint8 *)response->extra_data.data,
                                    response->length - sizeof(*response)),
                      TDB_REPLACE) == 0)
                return;
@@ -2426,6 +2430,7 @@ BOOL lookup_cached_name(TALLOC_CTX *mem_ctx,
        struct cache_entry *centry = NULL;
        NTSTATUS status;
        fstring uname;
+       BOOL original_online_state;     
 
        domain = find_lookup_domain_from_name(domain_name);
        if (domain == NULL) {
@@ -2441,7 +2446,14 @@ BOOL lookup_cached_name(TALLOC_CTX *mem_ctx,
        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);
+       domain->online = original_online_state;
+       
        if (centry == NULL) {
                return False;
        }
@@ -2734,9 +2746,16 @@ BOOL get_global_winbindd_state_offline(void)
  Validate functions for all possible cache tdb keys.
 ***********************************************************************/
 
-static BOOL bad_cache_entry;
+struct validation_status {
+       BOOL tdb_error;
+       BOOL bad_freelist;
+       BOOL bad_entry;
+       BOOL unknown_key;
+       BOOL success;
+};
 
-static struct cache_entry *create_centry_validate(const char *kstr, TDB_DATA data)
+static struct cache_entry *create_centry_validate(const char *kstr, TDB_DATA data, 
+                                                 struct validation_status *state)
 {
        struct cache_entry *centry;
 
@@ -2751,9 +2770,10 @@ static struct cache_entry *create_centry_validate(const char *kstr, TDB_DATA dat
 
        if (centry->len < 8) {
                /* huh? corrupt cache? */
-               DEBUG(0,("validate_create_centry: Corrupt cache for key %s (len < 8) ?\n", kstr));
+               DEBUG(0,("create_centry_validate: Corrupt cache for key %s (len < 8) ?\n", kstr));
                centry_free(centry);
-               bad_cache_entry = True;
+               state->bad_entry = True;
+               state->success = False;
                return NULL;
        }
 
@@ -2762,20 +2782,22 @@ static struct cache_entry *create_centry_validate(const char *kstr, TDB_DATA dat
        return centry;
 }
 
-static int validate_seqnum(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf)
+static int validate_seqnum(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
+                          struct validation_status *state)
 {
        if (dbuf.dsize != 8) {
                DEBUG(0,("validate_seqnum: Corrupt cache for key %s (len %u != 8) ?\n",
                                keystr, (unsigned int)dbuf.dsize ));
-               bad_cache_entry = True;
+               state->bad_entry = True;
                return 1;
        }
        return 0;
 }
 
-static int validate_ns(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf)
+static int validate_ns(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
+                      struct validation_status *state)
 {
-       struct cache_entry *centry = create_centry_validate(keystr, dbuf);
+       struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
        if (!centry) {
                return 1;
        }
@@ -2788,16 +2810,17 @@ static int validate_ns(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf)
 
        centry_free(centry);
 
-       if (bad_cache_entry) {
+       if (!(state->success)) {
                return 1;
        }
        DEBUG(10,("validate_ns: %s ok\n", keystr));
        return 0;
 }
 
-static int validate_sn(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf)
+static int validate_sn(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
+                      struct validation_status *state)
 {
-       struct cache_entry *centry = create_centry_validate(keystr, dbuf);
+       struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
        if (!centry) {
                return 1;
        }
@@ -2810,16 +2833,17 @@ static int validate_sn(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf)
 
        centry_free(centry);
 
-       if (bad_cache_entry) {
+       if (!(state->success)) {
                return 1;
        }
        DEBUG(10,("validate_sn: %s ok\n", keystr));
        return 0;
 }
 
-static int validate_u(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf)
+static int validate_u(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
+                     struct validation_status *state)
 {
-       struct cache_entry *centry = create_centry_validate(keystr, dbuf);
+       struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
        DOM_SID sid;
 
        if (!centry) {
@@ -2836,16 +2860,17 @@ static int validate_u(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf)
 
        centry_free(centry);
 
-       if (bad_cache_entry) {
+       if (!(state->success)) {
                return 1;
        }
        DEBUG(10,("validate_u: %s ok\n", keystr));
        return 0;
 }
 
-static int validate_loc_pol(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf)
+static int validate_loc_pol(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
+                           struct validation_status *state)
 {
-       struct cache_entry *centry = create_centry_validate(keystr, dbuf);
+       struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
 
        if (!centry) {
                return 1;
@@ -2857,16 +2882,17 @@ static int validate_loc_pol(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA db
 
        centry_free(centry);
 
-       if (bad_cache_entry) {
+       if (!(state->success)) {
                return 1;
        }
        DEBUG(10,("validate_loc_pol: %s ok\n", keystr));
        return 0;
 }
 
-static int validate_pwd_pol(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf)
+static int validate_pwd_pol(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
+                           struct validation_status *state)
 {
-       struct cache_entry *centry = create_centry_validate(keystr, dbuf);
+       struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
 
        if (!centry) {
                return 1;
@@ -2880,16 +2906,17 @@ static int validate_pwd_pol(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA db
 
        centry_free(centry);
 
-       if (bad_cache_entry) {
+       if (!(state->success)) {
                return 1;
        }
        DEBUG(10,("validate_pwd_pol: %s ok\n", keystr));
        return 0;
 }
 
-static int validate_cred(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf)
+static int validate_cred(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
+                        struct validation_status *state)
 {
-       struct cache_entry *centry = create_centry_validate(keystr, dbuf);
+       struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
 
        if (!centry) {
                return 1;
@@ -2905,16 +2932,17 @@ static int validate_cred(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf)
 
        centry_free(centry);
 
-       if (bad_cache_entry) {
+       if (!(state->success)) {
                return 1;
        }
        DEBUG(10,("validate_cred: %s ok\n", keystr));
        return 0;
 }
 
-static int validate_ul(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf)
+static int validate_ul(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
+                      struct validation_status *state)
 {
-       struct cache_entry *centry = create_centry_validate(keystr, dbuf);
+       struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
        int32 num_entries, i;
 
        if (!centry) {
@@ -2935,16 +2963,17 @@ static int validate_ul(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf)
 
        centry_free(centry);
 
-       if (bad_cache_entry) {
+       if (!(state->success)) {
                return 1;
        }
        DEBUG(10,("validate_ul: %s ok\n", keystr));
        return 0;
 }
 
-static int validate_gl(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf)
+static int validate_gl(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
+                      struct validation_status *state)
 {
-       struct cache_entry *centry = create_centry_validate(keystr, dbuf);
+       struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
        int32 num_entries, i;
 
        if (!centry) {
@@ -2961,16 +2990,17 @@ static int validate_gl(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf)
 
        centry_free(centry);
 
-       if (bad_cache_entry) {
+       if (!(state->success)) {
                return 1;
        }
        DEBUG(10,("validate_gl: %s ok\n", keystr));
        return 0;
 }
 
-static int validate_ug(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf)
+static int validate_ug(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
+                      struct validation_status *state)
 {
-       struct cache_entry *centry = create_centry_validate(keystr, dbuf);
+       struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
        int32 num_groups, i;
 
        if (!centry) {
@@ -2986,16 +3016,17 @@ static int validate_ug(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf)
 
        centry_free(centry);
 
-       if (bad_cache_entry) {
+       if (!(state->success)) {
                return 1;
        }
        DEBUG(10,("validate_ug: %s ok\n", keystr));
        return 0;
 }
 
-static int validate_ua(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf)
+static int validate_ua(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
+                      struct validation_status *state)
 {
-       struct cache_entry *centry = create_centry_validate(keystr, dbuf);
+       struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
        int32 num_aliases, i;
 
        if (!centry) {
@@ -3010,16 +3041,17 @@ static int validate_ua(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf)
 
        centry_free(centry);
 
-       if (bad_cache_entry) {
+       if (!(state->success)) {
                return 1;
        }
        DEBUG(10,("validate_ua: %s ok\n", keystr));
        return 0;
 }
 
-static int validate_gm(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf)
+static int validate_gm(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
+                      struct validation_status *state)
 {
-       struct cache_entry *centry = create_centry_validate(keystr, dbuf);
+       struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
        int32 num_names, i;
 
        if (!centry) {
@@ -3037,20 +3069,22 @@ static int validate_gm(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf)
 
        centry_free(centry);
 
-       if (bad_cache_entry) {
+       if (!(state->success)) {
                return 1;
        }
        DEBUG(10,("validate_gm: %s ok\n", keystr));
        return 0;
 }
 
-static int validate_dr(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf)
+static int validate_dr(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
+                      struct validation_status *state)
 {
        /* Can't say anything about this other than must be nonzero. */
        if (dbuf.dsize == 0) {
                DEBUG(0,("validate_dr: Corrupt cache for key %s (len == 0) ?\n",
                                keystr));
-               bad_cache_entry = True;
+               state->bad_entry = True;
+               state->success = False;
                return 1;
        }
 
@@ -3058,13 +3092,15 @@ static int validate_dr(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf)
        return 0;
 }
 
-static int validate_de(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf)
+static int validate_de(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
+                      struct validation_status *state)
 {
        /* Can't say anything about this other than must be nonzero. */
        if (dbuf.dsize == 0) {
                DEBUG(0,("validate_de: Corrupt cache for key %s (len == 0) ?\n",
                                keystr));
-               bad_cache_entry = True;
+               state->bad_entry = True;
+               state->success = False;
                return 1;
        }
 
@@ -3072,9 +3108,10 @@ static int validate_de(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf)
        return 0;
 }
 
-static int validate_trustdoms(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf)
+static int validate_trustdoms(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
+                             struct validation_status *state)
 {
-       struct cache_entry *centry = create_centry_validate(keystr, dbuf);
+       struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
        int32 num_domains, i;
 
        if (!centry) {
@@ -3092,25 +3129,60 @@ static int validate_trustdoms(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA
 
        centry_free(centry);
 
-       if (bad_cache_entry) {
+       if (!(state->success)) {
                return 1;
        }
        DEBUG(10,("validate_trustdoms: %s ok\n", keystr));
        return 0;
 }
 
-static int validate_offline(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf)
+static int validate_trustdomcache(TALLOC_CTX *mem_ctx, const char *keystr, 
+                                 TDB_DATA dbuf,
+                                 struct validation_status *state)
+{
+       if (dbuf.dsize == 0) {
+               DEBUG(0, ("validate_trustdomcache: Corrupt cache for "
+                         "key %s (len ==0) ?\n", keystr));
+               state->bad_entry = True;
+               state->success = False;
+               return 1;
+       }
+
+       DEBUG(10,    ("validate_trustdomcache: %s ok\n", keystr));
+       DEBUGADD(10, ("  Don't trust me, I am a DUMMY!\n"));
+       return 0;
+}
+
+static int validate_offline(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
+                           struct validation_status *state)
 {
        if (dbuf.dsize != 4) {
                DEBUG(0,("validate_offline: Corrupt cache for key %s (len %u != 4) ?\n",
                                keystr, (unsigned int)dbuf.dsize ));
-               bad_cache_entry = True;
+               state->bad_entry = True;
+               state->success = False;
                return 1;
        }
        DEBUG(10,("validate_offline: %s ok\n", keystr));
        return 0;
 }
 
+static int validate_cache_version(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
+                                 struct validation_status *state)
+{
+       if (dbuf.dsize != 4) {
+               DEBUG(0, ("validate_cache_version: Corrupt cache for "
+                         "key %s (len %u != 4) ?\n", 
+                         keystr, (unsigned int)dbuf.dsize));
+               state->bad_entry = True;
+               state->success = False;
+               return 1;
+       }
+
+       DEBUG(10, ("validate_cache_version: %s ok\n", keystr));
+       return 0;
+}
+
 /***********************************************************************
  A list of all possible cache tdb keys with associated validation
  functions.
@@ -3118,7 +3190,7 @@ static int validate_offline(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA db
 
 struct key_val_struct {
        const char *keyname;
-       int (*validate_data_fn)(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf);
+       int (*validate_data_fn)(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf, struct validation_status* state);
 } key_val[] = {
        {"SEQNUM/", validate_seqnum},
        {"NS/", validate_ns},
@@ -3135,7 +3207,9 @@ struct key_val_struct {
        {"DR/", validate_dr},
        {"DE/", validate_de},
        {"TRUSTDOMS/", validate_trustdoms},
+       {"TRUSTDOMCACHE/", validate_trustdomcache},
        {"WINBINDD_OFFLINE", validate_offline},
+       {WINBINDD_CACHE_VERSION_KEYSTR, validate_cache_version},
        {NULL, NULL}
 };
 
@@ -3147,6 +3221,7 @@ struct key_val_struct {
 static int cache_traverse_validate_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf, void *state)
 {
        int i;
+       struct validation_status *v_state = (struct validation_status *)state;
 
        /* Paranoia check. */
        if (kbuf.dsize > 1024) {
@@ -3163,7 +3238,7 @@ static int cache_traverse_validate_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_D
                        char *keystr;
                        int ret;
 
-                       keystr = SMB_MALLOC(kbuf.dsize+1);
+                       keystr = SMB_MALLOC_ARRAY(char, kbuf.dsize+1);
                        if (!keystr) {
                                return 1;
                        }
@@ -3176,7 +3251,8 @@ static int cache_traverse_validate_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_D
                                return 1;
                        }
 
-                       ret = key_val[i].validate_data_fn(mem_ctx, keystr, dbuf);
+                       ret = key_val[i].validate_data_fn(mem_ctx, keystr, dbuf, 
+                                                         v_state);
 
                        SAFE_FREE(keystr);
                        talloc_destroy(mem_ctx);
@@ -3188,40 +3264,17 @@ static int cache_traverse_validate_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_D
        dump_data(0, (uint8 *)kbuf.dptr, kbuf.dsize);
        DEBUG(0,("data :\n"));
        dump_data(0, (uint8 *)dbuf.dptr, dbuf.dsize);
+       v_state->unknown_key = True;
+       v_state->success = False;
        return 1; /* terminate. */
 }
 
 static void validate_panic(const char *const why)
 {
-       DEBUG(0,("validating cache: would panic %s\n", why ));
-       bad_cache_entry = True;
-}
-
-/* Handle any signals generated when validating a possibly
-   bad cache tdb. */
-
-static jmp_buf jmpbuf;
-
-#ifdef SIGSEGV
-static void sig_segv(int sig)
-{
-       longjmp(jmpbuf, SIGSEGV);
+        DEBUG(0,("validating cache: would panic %s\n", why ));
+       DEBUGADD(0, ("exiting instead (cache validation mode)\n"));
+       exit(47);
 }
-#endif
-
-#ifdef SIGBUS
-static void sig_bus(int sig)
-{
-       longjmp(jmpbuf, SIGBUS);
-}
-#endif
-
-#ifdef SIGABRT
-static void sig_abrt(int sig)
-{
-       longjmp(jmpbuf, SIGABRT);
-}
-#endif
 
 /***********************************************************************
  Try and validate every entry in the winbindd cache. If we fail here,
@@ -3229,105 +3282,669 @@ static void sig_abrt(int sig)
  function) will restart us as we don't know if we crashed or not.
 ***********************************************************************/
 
-int winbindd_validate_cache(void)
+/* 
+ * internal validation function, executed by the child.  
+ */
+static int winbindd_validate_cache_child(const char *cache_path, int pfd)
 {
-       BOOL ret = -1;
-       int fd = -1;
+       int ret = -1;
+       int tfd = -1;
        int num_entries = 0;
        TDB_CONTEXT *tdb = NULL;
-       const char *cache_path = lock_path("winbindd_cache.tdb");
-
-#ifdef SIGSEGV
-       void (*old_segv_handler)(int) = CatchSignal(SIGSEGV,SIGNAL_CAST sig_segv);
-#endif
-#ifdef SIGBUS
-       void (*old_bus_handler)(int) = CatchSignal(SIGBUS,SIGNAL_CAST sig_bus);
-#endif
-#ifdef SIGABRT
-       void (*old_abrt_handler)(int) = CatchSignal(SIGABRT,SIGNAL_CAST sig_abrt);
-#endif
-
-       switch((ret = setjmp(jmpbuf))) {
-               case 0:
-                       ret = -1;
-                       break;
-               case SIGSEGV:
-               case SIGBUS:
-               case SIGABRT:
-               default:
-                       goto out;
-       }
-
-       /* Doh ! Volker is very smart :-). Use TDB_NOMMAP to prevent
-        * any wild pointer references when reading a corrupt tdb file. */
+       struct validation_status v_status;
+       
+       v_status.tdb_error = False;
+       v_status.bad_freelist = False;
+       v_status.bad_entry = False;
+       v_status.unknown_key = False;
+       v_status.success = True;
 
        tdb = tdb_open_log(cache_path,
                        WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
-                       lp_winbind_offline_logon() ? TDB_NOMMAP : (TDB_NOMMAP | TDB_CLEAR_IF_FIRST),
+                       lp_winbind_offline_logon() 
+                               ?  TDB_DEFAULT 
+                               : (TDB_DEFAULT | TDB_CLEAR_IF_FIRST),
                        O_RDWR|O_CREAT, 0600);
        if (!tdb) {
+               v_status.tdb_error = True;
+               v_status.success = False;
                goto out;
        }
 
-       fd = tdb_fd(tdb);
+       tfd = tdb_fd(tdb);
 
        /* Check the cache freelist is good. */
        if (tdb_validate_freelist(tdb, &num_entries) == -1) {
-               DEBUG(0,("winbindd_validate_cache: bad freelist in cache %s\n",
+               DEBUG(0,("winbindd_validate_cache_child: bad freelist in cache %s\n",
                        cache_path));
+               v_status.bad_freelist = True;
+               v_status.success = False;
                goto out;
        }
 
-       DEBUG(10,("winbindd_validate_cache: cache %s freelist has %d entries\n",
+       DEBUG(10,("winbindd_validate_cache_child: cache %s freelist has %d entries\n",
                cache_path, num_entries));
 
-       smb_panic_fn = validate_panic;
-
        /* Now traverse the cache to validate it. */
-       num_entries = tdb_traverse(tdb, cache_traverse_validate_fn, NULL);
-       if (num_entries == -1 || bad_cache_entry) {
-               DEBUG(0,("winbindd_validate_cache: cache %s traverse failed\n",
+       num_entries = tdb_traverse(tdb, cache_traverse_validate_fn, (void *)&v_status);
+       if (num_entries == -1 || !(v_status.success)) {
+               DEBUG(0,("winbindd_validate_cache_child: cache %s traverse failed\n",
                        cache_path));
+               if (!(v_status.success)) {
+                       if (v_status.bad_entry) {
+                               DEBUGADD(0, (" -> bad entry found\n"));
+                       }
+                       if (v_status.unknown_key) {
+                               DEBUGADD(0, (" -> unknown key encountered\n"));
+                       }
+               }
                goto out;
        }
 
-       DEBUG(10,("winbindd_validate_cache: cache %s is good "
+       DEBUG(10,("winbindd_validate_cache_child: cache %s is good "
                "with %d entries\n", cache_path, num_entries));
        ret = 0; /* Cache is good. */
 
-  out:
-
-       bad_cache_entry = False;
-       smb_panic_fn = smb_panic;
-
-       /* Ensure if we segv on exit we use the original
-          handlers to avoid a loop. */
-
-#ifdef SIGSEGV
-       CatchSignal(SIGSEGV,SIGNAL_CAST old_segv_handler);
-#endif
-#ifdef SIGBUS
-       CatchSignal(SIGBUS,SIGNAL_CAST old_bus_handler);
-#endif
-#ifdef SIGABRT
-       CatchSignal(SIGABRT,SIGNAL_CAST old_abrt_handler);
-#endif
-
+out:
        if (tdb) {
                if (ret == 0) {
                        tdb_close(tdb);
-               } else if (fd != -1) {
-                       close(fd);
+               } 
+               else if (tfd != -1) {
+                       close(tfd);
                }
        }
 
-       if (ret) {
+       DEBUG(10, ("winbindd_validate_cache_child: writing status to pipe\n"));
+       write (pfd, (const char *)&v_status, sizeof(v_status));
+       close(pfd);
+
+       return ret;
+}
+
+int winbindd_validate_cache(void)
+{
+       pid_t child_pid = -1;
+       int child_status = 0;
+       int wait_pid = 0;
+       int ret = -1;
+       int pipe_fds[2];
+       struct validation_status v_status;
+       int bytes_read = 0;
+       const char *cache_path = lock_path("winbindd_cache.tdb");
+       
+       DEBUG(10, ("winbindd_validate_cache: replacing panic function\n"));
+       smb_panic_fn = validate_panic;
+
+       /* fork and let the child do the validation. 
+        * benefit: no need to twist signal handlers and panic functions.
+        * just let the child panic. we catch the signal. 
+        * communicate the extended status struct over a pipe. */
+
+       if (pipe(pipe_fds) != 0) {
+               DEBUG(0, ("winbindd_validate_cache: unable to create pipe, "
+                         "error %s", strerror(errno)));
+               smb_panic("winbind_validate_cache: unable to create pipe.");
+       }
+
+       DEBUG(10, ("winbindd_validate_cache: forking to let child do validation.\n"));
+       child_pid = sys_fork();
+       if (child_pid == 0) {
+               DEBUG(10, ("winbindd_validate_cache (validation child): created\n"));
+               close(pipe_fds[0]); /* close reading fd */
+               DEBUG(10, ("winbindd_validate_cache (validation child): "
+                          "calling winbindd_validate_cache_child\n"));
+               exit(winbindd_validate_cache_child(cache_path, pipe_fds[1]));
+       }
+       else if (child_pid < 0) {
+               smb_panic("winbindd_validate_cache: fork for validation failed.");
+       }
+
+       /* parent */
+
+       DEBUG(10, ("winbindd_validate_cache: fork succeeded, child PID = %d\n", 
+                  child_pid));
+       close(pipe_fds[1]); /* close writing fd */
+
+       v_status.success = True;
+       v_status.bad_entry = False;
+       v_status.unknown_key = False;
+
+       DEBUG(10, ("winbindd_validate_cache: reading from pipe.\n"));
+       bytes_read = read(pipe_fds[0], (void *)&v_status, sizeof(v_status));
+       close(pipe_fds[0]);
+
+       if (bytes_read != sizeof(v_status)) {
+               DEBUG(10, ("winbindd_validate_cache: read %d bytes from pipe "
+                          "but expected %d", bytes_read, sizeof(v_status)));
+               DEBUGADD(10, (" -> assuming child crashed\n"));
+               v_status.success = False;
+       }
+       else {
+               DEBUG(10,    ("winbindd_validate_cache: read status from child\n"));
+               DEBUGADD(10, (" *  tdb error: %s\n", v_status.tdb_error ? "yes" : "no"));
+               DEBUGADD(10, (" *  bad freelist: %s\n", v_status.bad_freelist ? "yes" : "no"));
+               DEBUGADD(10, (" *  bad entry: %s\n", v_status.bad_entry ? "yes" : "no"));
+               DEBUGADD(10, (" *  unknown key: %s\n", v_status.unknown_key ? "yes" : "no"));
+               DEBUGADD(10, (" => overall success: %s\n", v_status.success ? "yes" : "no"));
+       }
+
+       if (!v_status.success) {
+               DEBUG(10, ("winbindd_validate_cache: validation not successful.\n"));
+               DEBUGADD(10, ("removing tdb %s.\n", cache_path));
                unlink(cache_path);
        }
 
+       DEBUG(10, ("winbindd_validate_cache: waiting for child to finish...\n"));
+       while  ((wait_pid = sys_waitpid(child_pid, &child_status, 0)) < 0) {
+               if (errno == EINTR) {
+                       DEBUG(10, ("winbindd_validate_cache: got signal during "
+                                  "waitpid, retrying\n"));
+                       errno = 0;
+                       continue;
+               }
+               DEBUG(0, ("winbindd_validate_cache: waitpid failed with "
+                          "errno %s\n", strerror(errno)));
+               smb_panic("winbindd_validate_cache: waitpid failed.");
+       }
+       if (wait_pid != child_pid) {
+               DEBUG(0, ("winbindd_validate_cache: waitpid returned pid %d, "
+                         "but %d was expexted\n", wait_pid, child_pid));
+               smb_panic("winbindd_validate_cache: waitpid returned "
+                            "unexpected PID.");
+       }
+
+               
+       DEBUG(10, ("winbindd_validate_cache: validating child returned.\n"));
+       if (WIFEXITED(child_status)) {
+               DEBUG(10, ("winbindd_validate_cache: child exited, code %d.\n",
+                          WEXITSTATUS(child_status)));
+               ret = WEXITSTATUS(child_status);
+       }
+       if (WIFSIGNALED(child_status)) {
+               DEBUG(10, ("winbindd_validate_cache: child terminated "
+                          "by signal %d\n", WTERMSIG(child_status)));
+#ifdef WCOREDUMP
+               if (WCOREDUMP(child_status)) {
+                       DEBUGADD(10, ("core dumped\n"));
+               }
+#endif
+               ret = WTERMSIG(child_status);
+       }
+       if (WIFSTOPPED(child_status)) {
+               DEBUG(10, ("winbindd_validate_cache: child was stopped "
+                          "by signal %d\n",
+                          WSTOPSIG(child_status)));
+               ret = WSTOPSIG(child_status);
+       }
+
+       DEBUG(10, ("winbindd_validate_cache: restoring panic function\n"));
+       smb_panic_fn = smb_panic;
        return ret;
 }
 
+/*********************************************************************
+ ********************************************************************/
+
+static BOOL add_wbdomain_to_tdc_array( struct winbindd_domain *new_dom,
+                                      struct winbindd_tdc_domain **domains, 
+                                      size_t *num_domains )
+{
+       struct winbindd_tdc_domain *list = NULL;
+       size_t idx;
+       int i;
+       BOOL set_only = False;  
+       
+       /* don't allow duplicates */
+
+       idx = *num_domains;
+       list = *domains;
+       
+       for ( i=0; i< (*num_domains); i++ ) {
+               if ( strequal( new_dom->name, list[i].domain_name ) ) {
+                       DEBUG(10,("add_wbdomain_to_tdc_array: Found existing record for %s\n",
+                                 new_dom->name));
+                       idx = i;
+                       set_only = True;
+                       
+                       break;
+               }
+       }
+
+       if ( !set_only ) {
+               if ( !*domains ) {
+                       list = TALLOC_ARRAY( NULL, struct winbindd_tdc_domain, 1 );
+                       idx = 0;
+               } else {
+                       list = TALLOC_REALLOC_ARRAY( *domains, *domains, 
+                                                    struct winbindd_tdc_domain,  
+                                                    (*num_domains)+1);
+                       idx = *num_domains;             
+               }
+
+               ZERO_STRUCT( list[idx] );
+       }
+
+       if ( !list )
+               return False;
+
+       list[idx].domain_name = talloc_strdup( list, new_dom->name );
+       list[idx].dns_name = talloc_strdup( list, new_dom->alt_name );
+
+       if ( !is_null_sid( &new_dom->sid ) )
+               sid_copy( &list[idx].sid, &new_dom->sid );
+
+       if ( new_dom->domain_flags != 0x0 )
+               list[idx].trust_flags = new_dom->domain_flags;  
+
+       if ( new_dom->domain_type != 0x0 )
+               list[idx].trust_type = new_dom->domain_type;
+
+       if ( new_dom->domain_trust_attribs != 0x0 )
+               list[idx].trust_attribs = new_dom->domain_trust_attribs;
+       
+       if ( !set_only ) {
+               *domains = list;
+               *num_domains = idx + 1; 
+       }
+
+       return True;    
+}
+
+/*********************************************************************
+ ********************************************************************/
+
+static TDB_DATA make_tdc_key( const char *domain_name )
+{
+       char *keystr = NULL;
+       TDB_DATA key = { NULL, 0 };
+       
+       if ( !domain_name ) {
+               DEBUG(5,("make_tdc_key: Keyname workgroup is NULL!\n"));
+               return key;
+       }
+              
+               
+       asprintf( &keystr, "TRUSTDOMCACHE/%s", domain_name );
+       key.dptr = (unsigned char*)keystr;
+       key.dsize = strlen_m(keystr) + 1;
+       
+       return key;     
+}
+
+/*********************************************************************
+ ********************************************************************/
+
+static int pack_tdc_domains( struct winbindd_tdc_domain *domains, 
+                            size_t num_domains,
+                            unsigned char **buf )
+{
+        unsigned char *buffer = NULL;
+       int len = 0;
+       int buflen = 0;
+       int i = 0;
+
+       DEBUG(10,("pack_tdc_domains: Packing %d trusted domains\n",
+                 (int)num_domains));
+       
+       buflen = 0;
+       
+ again: 
+       len = 0;
+       
+       /* Store the number of array items first */
+       len += tdb_pack( buffer+len, buflen-len, "d", 
+                        num_domains );
+
+       /* now pack each domain trust record */
+       for ( i=0; i<num_domains; i++ ) {
+
+               if ( buflen > 0 ) {
+                       DEBUG(10,("pack_tdc_domains: Packing domain %s (%s)\n",
+                                 domains[i].domain_name,
+                                 domains[i].dns_name ? domains[i].dns_name : "UNKNOWN" ));
+               }
+               
+               len += tdb_pack( buffer+len, buflen-len, "fffddd",
+                                domains[i].domain_name,
+                                domains[i].dns_name,
+                                sid_string_static(&domains[i].sid),
+                                domains[i].trust_flags,
+                                domains[i].trust_attribs,
+                                domains[i].trust_type );
+       }
+
+       if ( buflen < len ) {
+               if ( (buffer = SMB_MALLOC_ARRAY(unsigned char, len)) == NULL ) {
+                       DEBUG(0,("pack_tdc_domains: failed to alloc buffer!\n"));
+                       buflen = -1;
+                       goto done;
+               }
+               buflen = len;
+               goto again;
+       }
+
+       *buf = buffer;  
+       
+ done: 
+       return buflen;  
+}
+
+/*********************************************************************
+ ********************************************************************/
+
+static size_t unpack_tdc_domains( unsigned char *buf, int buflen, 
+                                 struct winbindd_tdc_domain **domains )
+{
+       fstring domain_name, dns_name, sid_string;      
+       uint32 type, attribs, flags;
+       int num_domains;
+       int len = 0;
+       int i;
+       struct winbindd_tdc_domain *list = NULL;
+
+       /* get the number of domains */
+       len += tdb_unpack( buf+len, buflen-len, "d", &num_domains);
+       if ( len == -1 ) {
+               DEBUG(5,("unpack_tdc_domains: Failed to unpack domain array\n"));               
+               return 0;
+       }
+
+       list = TALLOC_ARRAY( NULL, struct winbindd_tdc_domain, num_domains );
+       if ( !list ) {
+               DEBUG(0,("unpack_tdc_domains: Failed to talloc() domain list!\n"));
+               return 0;               
+       }
+       
+       for ( i=0; i<num_domains; i++ ) {
+               len += tdb_unpack( buf+len, buflen-len, "fffddd",
+                                  domain_name,
+                                  dns_name,
+                                  sid_string,
+                                  &flags,
+                                  &attribs,
+                                  &type );
+
+               if ( len == -1 ) {
+                       DEBUG(5,("unpack_tdc_domains: Failed to unpack domain array\n"));
+                       TALLOC_FREE( list );                    
+                       return 0;
+               }
+
+               DEBUG(11,("unpack_tdc_domains: Unpacking domain %s (%s) "
+                         "SID %s, flags = 0x%x, attribs = 0x%x, type = 0x%x\n",
+                         domain_name, dns_name, sid_string,
+                         flags, attribs, type));
+               
+               list[i].domain_name = talloc_strdup( list, domain_name );
+               list[i].dns_name = talloc_strdup( list, dns_name );
+               if ( !string_to_sid( &(list[i].sid), sid_string ) ) {                   
+                       DEBUG(10,("unpack_tdc_domains: no SID for domain %s\n",
+                                 domain_name));
+               }
+               list[i].trust_flags = flags;
+               list[i].trust_attribs = attribs;
+               list[i].trust_type = type;
+       }
+
+       *domains = list;
+       
+       return num_domains;
+}
+
+/*********************************************************************
+ ********************************************************************/
+
+static BOOL wcache_tdc_store_list( struct winbindd_tdc_domain *domains, size_t num_domains )
+{
+       TDB_DATA key = make_tdc_key( lp_workgroup() );   
+       TDB_DATA data = { NULL, 0 };
+       int ret;
+       
+       if ( !key.dptr )
+               return False;
+       
+       /* See if we were asked to delete the cache entry */
+
+       if ( !domains ) {
+               ret = tdb_delete( wcache->tdb, key );
+               goto done;
+       }
+       
+       data.dsize = pack_tdc_domains( domains, num_domains, &data.dptr );
+       
+       if ( !data.dptr ) {
+               ret = -1;
+               goto done;
+       }
+               
+       ret = tdb_store( wcache->tdb, key, data, 0 );
+
+ done:
+       SAFE_FREE( data.dptr );
+       SAFE_FREE( key.dptr );
+       
+       return ( ret != -1 );   
+}
+
+/*********************************************************************
+ ********************************************************************/
+
+BOOL wcache_tdc_fetch_list( struct winbindd_tdc_domain **domains, size_t *num_domains )
+{
+       TDB_DATA key = make_tdc_key( lp_workgroup() );
+       TDB_DATA data = { NULL, 0 };
+
+       *domains = NULL;        
+       *num_domains = 0;       
+
+       if ( !key.dptr )
+               return False;
+       
+       data = tdb_fetch( wcache->tdb, key );
+
+       SAFE_FREE( key.dptr );
+       
+       if ( !data.dptr ) 
+               return False;
+       
+       *num_domains = unpack_tdc_domains( data.dptr, data.dsize, domains );
+
+       SAFE_FREE( data.dptr );
+       
+       if ( !*domains )
+               return False;
+
+       return True;    
+}
+
+/*********************************************************************
+ ********************************************************************/
+
+BOOL wcache_tdc_add_domain( struct winbindd_domain *domain )
+{
+       struct winbindd_tdc_domain *dom_list = NULL;
+       size_t num_domains = 0;
+       BOOL ret = False;       
+
+       DEBUG(10,("wcache_tdc_add_domain: Adding domain %s (%s), SID %s, "
+                 "flags = 0x%x, attributes = 0x%x, type = 0x%x\n",
+                 domain->name, domain->alt_name, 
+                 sid_string_static(&domain->sid),
+                 domain->domain_flags,
+                 domain->domain_trust_attribs,
+                 domain->domain_type));        
+       
+       if ( !init_wcache() ) {
+               return False;
+       }
+       
+       /* fetch the list */
+
+       wcache_tdc_fetch_list( &dom_list, &num_domains );
+       
+       /* add the new domain */
+
+       if ( !add_wbdomain_to_tdc_array( domain, &dom_list, &num_domains ) ) {
+               goto done;              
+       }       
+
+       /* pack the domain */
+
+       if ( !wcache_tdc_store_list( dom_list, num_domains ) ) {
+               goto done;              
+       }
+       
+       /* Success */
+
+       ret = True;     
+ done:
+       TALLOC_FREE( dom_list );
+       
+       return ret;     
+}
+
+/*********************************************************************
+ ********************************************************************/
+
+struct winbindd_tdc_domain * wcache_tdc_fetch_domain( TALLOC_CTX *ctx, const char *name )
+{
+       struct winbindd_tdc_domain *dom_list = NULL;
+       size_t num_domains = 0;
+       int i;
+       struct winbindd_tdc_domain *d = NULL;   
+
+       DEBUG(10,("wcache_tdc_fetch_domain: Searching for domain %s\n", name));
+
+       if ( !init_wcache() ) {
+               return False;
+       }
+       
+       /* fetch the list */
+
+       wcache_tdc_fetch_list( &dom_list, &num_domains );
+       
+       for ( i=0; i<num_domains; i++ ) {
+               if ( strequal(name, dom_list[i].domain_name) ||
+                    strequal(name, dom_list[i].dns_name) )
+               {
+                       DEBUG(10,("wcache_tdc_fetch_domain: Found domain %s\n",
+                                 name));
+                       
+                       d = TALLOC_P( ctx, struct winbindd_tdc_domain );
+                       if ( !d )
+                               break;                  
+                       
+                       d->domain_name = talloc_strdup( d, dom_list[i].domain_name );
+                       d->dns_name = talloc_strdup( d, dom_list[i].dns_name );
+                       sid_copy( &d->sid, &dom_list[i].sid );
+                       d->trust_flags   = dom_list[i].trust_flags;
+                       d->trust_type    = dom_list[i].trust_type;
+                       d->trust_attribs = dom_list[i].trust_attribs;
+
+                       break;
+               }
+       }
+
+        TALLOC_FREE( dom_list );
+       
+       return d;       
+}
+
+
+/*********************************************************************
+ ********************************************************************/
+
+void wcache_tdc_clear( void )
+{
+       if ( !init_wcache() )
+               return;
+
+       wcache_tdc_store_list( NULL, 0 );
+       
+       return; 
+}
+
+
+/*********************************************************************
+ ********************************************************************/
+
+static void wcache_save_user_pwinfo(struct winbindd_domain *domain, 
+                                   NTSTATUS status,
+                                   const DOM_SID *user_sid,
+                                   const char *homedir,
+                                   const char *shell,
+                                   const char *gecos,
+                                   uint32 gid)
+{
+       struct cache_entry *centry;
+
+       if ( (centry = centry_start(domain, status)) == NULL )
+               return;
+
+       centry_put_string( centry, homedir );
+       centry_put_string( centry, shell );
+       centry_put_string( centry, gecos );
+       centry_put_uint32( centry, gid );
+       
+       centry_end(centry, "NSS/PWINFO/%s", sid_string_static(user_sid) );
+
+       DEBUG(10,("wcache_save_user_pwinfo: %s\n", sid_string_static(user_sid) ));
+
+       centry_free(centry);
+}
+
+NTSTATUS nss_get_info_cached( struct winbindd_domain *domain, 
+                             const DOM_SID *user_sid,
+                             TALLOC_CTX *ctx,
+                             ADS_STRUCT *ads, LDAPMessage *msg,
+                             char **homedir, char **shell, char **gecos,
+                             gid_t *p_gid)
+{
+       struct winbind_cache *cache = get_cache(domain);
+       struct cache_entry *centry = NULL;
+       NTSTATUS nt_status;
+
+       if (!cache->tdb)
+               goto do_query;
+
+       centry = wcache_fetch(cache, domain, "NSS/PWINFO/%s", sid_string_static(user_sid));     
+       
+       if (!centry)
+               goto do_query;
+
+       *homedir = centry_string( centry, ctx );
+       *shell   = centry_string( centry, ctx );
+       *gecos   = centry_string( centry, ctx );
+       *p_gid   = centry_uint32( centry );     
+
+       centry_free(centry);
+
+       DEBUG(10,("nss_get_info_cached: [Cached] - user_sid %s\n",
+                 sid_string_static(user_sid)));
+
+       return NT_STATUS_OK;
+
+do_query:
+       
+       nt_status = nss_get_info( domain->name, user_sid, ctx, ads, msg, 
+                                 homedir, shell, gecos, p_gid );
+
+       if ( NT_STATUS_IS_OK(nt_status) ) {
+               wcache_save_user_pwinfo( domain, nt_status, user_sid,
+                                        *homedir, *shell, *gecos, *p_gid );
+       }       
+
+       if ( NT_STATUS_EQUAL( nt_status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND ) ) {
+               DEBUG(5,("nss_get_info_cached: Setting domain %s offline\n",
+                        domain->name ));
+               set_domain_offline( domain );
+       }
+
+       return nt_status;       
+}
+
+
 /* the cache backend methods are exposed via this structure */
 struct winbindd_methods cache_methods = {
        True,