s3:winbind: Add a generic cache for NDR based parent-child requests
[sfrench/samba-autobuild/.git] / source3 / winbindd / winbindd_cache.c
index bec2a714c80a8c18c318025fcb8ea927e4f344d3..e8f928867b53e5d089c971a2368fb43f0d9c1bc2 100644 (file)
@@ -392,30 +392,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;
@@ -898,7 +923,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;
@@ -1334,7 +1360,7 @@ 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;
@@ -1353,7 +1379,7 @@ static NTSTATUS query_user_list(struct winbindd_domain *domain,
        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");
        }
@@ -1605,28 +1631,31 @@ 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,
+NTSTATUS wcache_name_to_sid(struct winbindd_domain *domain,
                            const char *domain_name,
                            const char *name,
-                           uint32_t flags,
-                           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)) {
@@ -1634,13 +1663,29 @@ static NTSTATUS name_to_sid(struct winbindd_domain *domain,
                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,
+                           DOM_SID *sid,
+                           enum lsa_SidType *type)
+{
+       NTSTATUS status;
+
+       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
@@ -1678,42 +1723,65 @@ 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 DOM_SID *sid,
+                           char **domain_name,
+                           char **name,
+                           enum lsa_SidType *type)
+{
+       NTSTATUS status;
+
+       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;
 
@@ -1897,38 +1965,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 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) &&
+       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;
@@ -1942,13 +2018,26 @@ static NTSTATUS query_user(struct winbindd_domain *domain,
                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 DOM_SID *user_sid,
+                          struct wbint_userinfo *info)
+{
+       NTSTATUS status;
+
+       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 */
@@ -1968,63 +2057,81 @@ do_query:
        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) {
+               return NT_STATUS_NO_MEMORY;
        }
-       for (i=0; i<(*num_groups); i++) {
-               centry_sid(centry, &(*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 DOM_SID *user_sid,
+                                 uint32 *num_groups, DOM_SID **user_gids)
+{
+       struct cache_entry *centry = NULL;
+       NTSTATUS status;
+       unsigned int i;
+       fstring sid_string;
+
+       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;
 
@@ -2059,58 +2166,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;
 
@@ -2118,9 +2241,29 @@ 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 DOM_SID *sids,
+                                  uint32 *num_aliases, uint32 **alias_rids)
+{
+       struct cache_entry *centry = NULL;
+       NTSTATUS status;
+       char *sidlist;
+       int i;
+
+       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;
 
@@ -2130,6 +2273,11 @@ 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);
@@ -2149,38 +2297,54 @@ 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;
+       }
+
+       *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);
 
-       if (! (*sid_mem) || ! (*names) || ! (*name_types)) {
-               smb_panic_fn("lookup_groupmem out of memory");
+       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++) {
@@ -2189,16 +2353,32 @@ static NTSTATUS lookup_groupmem(struct winbindd_domain *domain,
                (*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 DOM_SID *group_sid, uint32 *num_names,
+                               DOM_SID **sid_mem, char ***names,
+                               uint32 **name_types)
+{
+       struct cache_entry *centry = NULL;
+       NTSTATUS status;
+       unsigned int i;
+       fstring sid_string;
+
+       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;
@@ -2625,36 +2805,14 @@ bool lookup_cached_sid(TALLOC_CTX *mem_ctx, const DOM_SID *sid,
                       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);
 }
 
@@ -2665,46 +2823,22 @@ bool lookup_cached_name(TALLOC_CTX *mem_ctx,
                        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, sid);
-       }
-
-       status = centry->status;
-       centry_free(centry);
-
        return NT_STATUS_IS_OK(status);
 }
 
@@ -4164,3 +4298,115 @@ 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;
+}
+
+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->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 < 4) {
+               goto fail;
+       }
+
+       if (IS_DOMAIN_ONLINE(domain)) {
+               uint32_t entry_seqnum, dom_seqnum, last_check;
+
+               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;
+               }
+       }
+
+       resp->data = (uint8_t *)talloc_memdup(mem_ctx, data.dptr + 4,
+                                             data.dsize - 4);
+       if (resp->data == NULL) {
+               DEBUG(10, ("talloc failed\n"));
+               goto fail;
+       }
+       resp->length = data.dsize - 4;
+
+       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;
+
+       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;
+       }
+
+       data.dsize = resp->length + 4;
+       data.dptr = talloc_array(key.dptr, uint8_t, data.dsize);
+       if (data.dptr == NULL) {
+               goto done;
+       }
+
+       SIVAL(data.dptr, 0, dom_seqnum);
+       memcpy(data.dptr+4, resp->data, resp->length);
+
+       tdb_store(wcache->tdb, key, data, 0);
+
+done:
+       TALLOC_FREE(key.dptr);
+       return;
+}