summaryrefslogtreecommitdiff
path: root/source3/nsswitch/winbindd_cache.c
diff options
context:
space:
mode:
Diffstat (limited to 'source3/nsswitch/winbindd_cache.c')
-rw-r--r--source3/nsswitch/winbindd_cache.c882
1 files changed, 882 insertions, 0 deletions
diff --git a/source3/nsswitch/winbindd_cache.c b/source3/nsswitch/winbindd_cache.c
new file mode 100644
index 0000000000..9bd95fdd86
--- /dev/null
+++ b/source3/nsswitch/winbindd_cache.c
@@ -0,0 +1,882 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Winbind cache backend functions
+
+ Copyright (C) Andrew Tridgell 2001
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "winbindd.h"
+
+struct winbind_cache {
+ struct winbindd_methods *backend;
+ TDB_CONTEXT *tdb;
+};
+
+struct cache_entry {
+ NTSTATUS status;
+ uint32 sequence_number;
+ uint8 *data;
+ uint32 len, ofs;
+};
+
+#define WINBINDD_MAX_CACHE_SIZE (50*1024*1024)
+
+static struct winbind_cache *wcache;
+
+/* flush the cache */
+void wcache_flush_cache(void)
+{
+ extern BOOL opt_nocache;
+
+ if (!wcache) return;
+ if (wcache->tdb) {
+ tdb_close(wcache->tdb);
+ wcache->tdb = NULL;
+ }
+ if (opt_nocache) return;
+
+ wcache->tdb = tdb_open_log(lock_path("winbindd_cache.tdb"), 5000,
+ TDB_DEFAULT, O_RDWR | O_CREAT | O_TRUNC, 0600);
+
+ if (!wcache->tdb) {
+ DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
+ }
+}
+
+void winbindd_check_cache_size(time_t t)
+{
+ static time_t last_check_time;
+ struct stat st;
+
+ if (last_check_time == (time_t)0)
+ last_check_time = t;
+
+ if (t - last_check_time < 60 && t - last_check_time > 0)
+ return;
+
+ if (wcache == NULL || wcache->tdb == NULL) {
+ DEBUG(0, ("Unable to check size of tdb cache - cache not open !\n"));
+ return;
+ }
+
+ if (fstat(wcache->tdb->fd, &st) == -1) {
+ DEBUG(0, ("Unable to check size of tdb cache %s!\n", strerror(errno) ));
+ return;
+ }
+
+ if (st.st_size > WINBINDD_MAX_CACHE_SIZE) {
+ DEBUG(10,("flushing cache due to size (%lu) > (%lu)\n",
+ (unsigned long)st.st_size,
+ (unsigned long)WINBINDD_MAX_CACHE_SIZE));
+ wcache_flush_cache();
+ }
+}
+
+/* get the winbind_cache structure */
+static struct winbind_cache *get_cache(struct winbindd_domain *domain)
+{
+ extern struct winbindd_methods msrpc_methods;
+ struct winbind_cache *ret = wcache;
+
+ if (ret) return ret;
+
+ ret = smb_xmalloc(sizeof(*ret));
+ ZERO_STRUCTP(ret);
+ switch (lp_security()) {
+#ifdef HAVE_ADS
+ case SEC_ADS: {
+ extern struct winbindd_methods ads_methods;
+ ret->backend = &ads_methods;
+ break;
+ }
+#endif
+ default:
+ ret->backend = &msrpc_methods;
+ }
+
+ wcache = ret;
+ wcache_flush_cache();
+
+ return ret;
+}
+
+/*
+ free a centry structure
+*/
+static void centry_free(struct cache_entry *centry)
+{
+ if (!centry) return;
+ SAFE_FREE(centry->data);
+ free(centry);
+}
+
+
+/*
+ pull a uint32 from a cache entry
+*/
+static uint32 centry_uint32(struct cache_entry *centry)
+{
+ uint32 ret;
+ if (centry->len - centry->ofs < 4) {
+ DEBUG(0,("centry corruption? needed 4 bytes, have %d\n",
+ centry->len - centry->ofs));
+ smb_panic("centry_uint32");
+ }
+ ret = IVAL(centry->data, centry->ofs);
+ centry->ofs += 4;
+ return ret;
+}
+
+/*
+ pull a uint8 from a cache entry
+*/
+static uint8 centry_uint8(struct cache_entry *centry)
+{
+ uint8 ret;
+ if (centry->len - centry->ofs < 1) {
+ DEBUG(0,("centry corruption? needed 1 bytes, have %d\n",
+ centry->len - centry->ofs));
+ smb_panic("centry_uint32");
+ }
+ ret = CVAL(centry->data, centry->ofs);
+ centry->ofs += 1;
+ return ret;
+}
+
+/* pull a string from a cache entry, using the supplied
+ talloc context
+*/
+static char *centry_string(struct cache_entry *centry, TALLOC_CTX *mem_ctx)
+{
+ uint32 len;
+ char *ret;
+
+ len = centry_uint8(centry);
+
+ if (len == 0xFF) {
+ /* a deliberate NULL string */
+ return NULL;
+ }
+
+ if (centry->len - centry->ofs < len) {
+ DEBUG(0,("centry corruption? needed %d bytes, have %d\n",
+ len, centry->len - centry->ofs));
+ smb_panic("centry_string");
+ }
+
+ ret = talloc(mem_ctx, len+1);
+ if (!ret) {
+ smb_panic("centry_string out of memory\n");
+ }
+ memcpy(ret,centry->data + centry->ofs, len);
+ ret[len] = 0;
+ centry->ofs += len;
+ return ret;
+}
+
+/* the server is considered down if it can't give us a sequence number */
+static BOOL wcache_server_down(struct winbindd_domain *domain)
+{
+ if (!wcache->tdb) return False;
+ return (domain->sequence_number == DOM_SEQUENCE_NONE);
+}
+
+
+/*
+ refresh the domain sequence number. If force is True
+ then always refresh it, no matter how recently we fetched it
+*/
+static void refresh_sequence_number(struct winbindd_domain *domain, BOOL force)
+{
+ NTSTATUS status;
+ unsigned time_diff;
+
+ time_diff = time(NULL) - domain->last_seq_check;
+
+ /* see if we have to refetch the domain sequence number */
+ if (!force && (time_diff < lp_winbind_cache_time())) {
+ return;
+ }
+
+ status = wcache->backend->sequence_number(domain, &domain->sequence_number);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ domain->sequence_number = DOM_SEQUENCE_NONE;
+ }
+
+ domain->last_seq_check = time(NULL);
+}
+
+/*
+ decide if a cache entry has expired
+*/
+static BOOL centry_expired(struct winbindd_domain *domain, struct cache_entry *centry)
+{
+ /* if the server is OK and our cache entry came from when it was down then
+ the entry is invalid */
+ if (domain->sequence_number != DOM_SEQUENCE_NONE &&
+ centry->sequence_number == DOM_SEQUENCE_NONE) {
+ return True;
+ }
+
+ /* if the server is down or the cache entry is not older than the
+ current sequence number then it is OK */
+ if (wcache_server_down(domain) ||
+ centry->sequence_number == domain->sequence_number) {
+ return False;
+ }
+
+ /* it's expired */
+ return True;
+}
+
+/*
+ fetch an entry from the cache, with a varargs key. auto-fetch the sequence
+ number and return status
+*/
+static struct cache_entry *wcache_fetch(struct winbind_cache *cache,
+ struct winbindd_domain *domain,
+ const char *format, ...)
+{
+ va_list ap;
+ char *kstr;
+ TDB_DATA data;
+ struct cache_entry *centry;
+ TDB_DATA key;
+
+ refresh_sequence_number(domain, False);
+
+ va_start(ap, format);
+ smb_xvasprintf(&kstr, format, ap);
+ va_end(ap);
+
+ key.dptr = kstr;
+ key.dsize = strlen(kstr);
+ data = tdb_fetch(wcache->tdb, key);
+ free(kstr);
+ if (!data.dptr) {
+ /* a cache miss */
+ return NULL;
+ }
+
+ centry = smb_xmalloc(sizeof(*centry));
+ centry->data = data.dptr;
+ centry->len = data.dsize;
+ centry->ofs = 0;
+
+ if (centry->len < 8) {
+ /* huh? corrupt cache? */
+ centry_free(centry);
+ return NULL;
+ }
+
+ centry->status = NT_STATUS(centry_uint32(centry));
+ centry->sequence_number = centry_uint32(centry);
+
+ if (centry_expired(domain, centry)) {
+ centry_free(centry);
+ return NULL;
+ }
+
+ return centry;
+}
+
+/*
+ make sure we have at least len bytes available in a centry
+*/
+static void centry_expand(struct cache_entry *centry, uint32 len)
+{
+ uint8 *p;
+ if (centry->len - centry->ofs >= len) return;
+ centry->len *= 2;
+ p = realloc(centry->data, centry->len);
+ if (!p) {
+ DEBUG(0,("out of memory: needed %d bytes in centry_expand\n", centry->len));
+ smb_panic("out of memory in centry_expand");
+ }
+ centry->data = p;
+}
+
+/*
+ push a uint32 into a centry
+*/
+static void centry_put_uint32(struct cache_entry *centry, uint32 v)
+{
+ centry_expand(centry, 4);
+ SIVAL(centry->data, centry->ofs, v);
+ centry->ofs += 4;
+}
+
+/*
+ push a uint8 into a centry
+*/
+static void centry_put_uint8(struct cache_entry *centry, uint8 v)
+{
+ centry_expand(centry, 1);
+ SCVAL(centry->data, centry->ofs, v);
+ centry->ofs += 1;
+}
+
+/*
+ push a string into a centry
+ */
+static void centry_put_string(struct cache_entry *centry, const char *s)
+{
+ int len;
+
+ if (!s) {
+ /* null strings are marked as len 0xFFFF */
+ centry_put_uint8(centry, 0xFF);
+ return;
+ }
+
+ len = strlen(s);
+ /* can't handle more than 254 char strings. Truncating is probably best */
+ if (len > 254) len = 254;
+ centry_put_uint8(centry, len);
+ centry_expand(centry, len);
+ memcpy(centry->data + centry->ofs, s, len);
+ centry->ofs += len;
+}
+
+/*
+ start a centry for output. When finished, call centry_end()
+*/
+struct cache_entry *centry_start(struct winbindd_domain *domain, NTSTATUS status)
+{
+ struct cache_entry *centry;
+
+ if (!wcache->tdb) return NULL;
+
+ centry = smb_xmalloc(sizeof(*centry));
+
+ centry->len = 8192; /* reasonable default */
+ centry->data = smb_xmalloc(centry->len);
+ centry->ofs = 0;
+ centry->sequence_number = domain->sequence_number;
+ centry_put_uint32(centry, NT_STATUS_V(status));
+ centry_put_uint32(centry, centry->sequence_number);
+ return centry;
+}
+
+/*
+ finish a centry and write it to the tdb
+*/
+static void centry_end(struct cache_entry *centry, const char *format, ...)
+{
+ va_list ap;
+ char *kstr;
+ TDB_DATA key, data;
+
+ va_start(ap, format);
+ smb_xvasprintf(&kstr, format, ap);
+ va_end(ap);
+
+ key.dptr = kstr;
+ key.dsize = strlen(kstr);
+ data.dptr = centry->data;
+ data.dsize = centry->ofs;
+
+ tdb_store(wcache->tdb, key, data, TDB_REPLACE);
+ free(kstr);
+}
+
+/* form a sid from the domain plus rid */
+static DOM_SID *form_sid(struct winbindd_domain *domain, uint32 rid)
+{
+ static DOM_SID sid;
+ sid_copy(&sid, &domain->sid);
+ sid_append_rid(&sid, rid);
+ return &sid;
+}
+
+static void wcache_save_name_to_sid(struct winbindd_domain *domain, NTSTATUS status,
+ const char *name, DOM_SID *sid, enum SID_NAME_USE type)
+{
+ struct cache_entry *centry;
+ uint32 len;
+
+ centry = centry_start(domain, status);
+ if (!centry) return;
+ len = sid_size(sid);
+ centry_expand(centry, len);
+ centry_put_uint32(centry, type);
+ sid_linearize(centry->data + centry->ofs, len, sid);
+ centry->ofs += len;
+ centry_end(centry, "NS/%s/%s", domain->name, name);
+ centry_free(centry);
+}
+
+static void wcache_save_sid_to_name(struct winbindd_domain *domain, NTSTATUS status,
+ DOM_SID *sid, const char *name, enum SID_NAME_USE type, uint32 rid)
+{
+ struct cache_entry *centry;
+
+ centry = centry_start(domain, status);
+ if (!centry) return;
+ if (NT_STATUS_IS_OK(status)) {
+ centry_put_uint32(centry, type);
+ centry_put_string(centry, name);
+ }
+ centry_end(centry, "SN/%s/%d", domain->name, rid);
+ centry_free(centry);
+}
+
+
+static void wcache_save_user(struct winbindd_domain *domain, NTSTATUS status, WINBIND_USERINFO *info)
+{
+ struct cache_entry *centry;
+
+ centry = centry_start(domain, status);
+ if (!centry) return;
+ centry_put_string(centry, info->acct_name);
+ centry_put_string(centry, info->full_name);
+ centry_put_uint32(centry, info->user_rid);
+ centry_put_uint32(centry, info->group_rid);
+ centry_end(centry, "U/%s/%x", domain->name, info->user_rid);
+ centry_free(centry);
+}
+
+
+/* Query display info. This is the basic user list fn */
+static NTSTATUS query_user_list(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ uint32 *num_entries,
+ WINBIND_USERINFO **info)
+{
+ struct winbind_cache *cache = get_cache(domain);
+ struct cache_entry *centry = NULL;
+ NTSTATUS status;
+ int i;
+
+ if (!cache->tdb) goto do_query;
+
+ centry = wcache_fetch(cache, domain, "UL/%s", domain->name);
+ if (!centry) goto do_query;
+
+ *num_entries = centry_uint32(centry);
+
+ if (*num_entries == 0) goto do_cached;
+
+ (*info) = talloc(mem_ctx, sizeof(**info) * (*num_entries));
+ if (! (*info)) smb_panic("query_user_list out of memory");
+ for (i=0; i<(*num_entries); i++) {
+ (*info)[i].acct_name = centry_string(centry, mem_ctx);
+ (*info)[i].full_name = centry_string(centry, mem_ctx);
+ (*info)[i].user_rid = centry_uint32(centry);
+ (*info)[i].group_rid = centry_uint32(centry);
+ }
+
+do_cached:
+ status = centry->status;
+ centry_free(centry);
+ return status;
+
+do_query:
+ *num_entries = 0;
+ *info = NULL;
+
+ if (wcache_server_down(domain)) {
+ return NT_STATUS_SERVER_DISABLED;
+ }
+
+ status = cache->backend->query_user_list(domain, mem_ctx, num_entries, info);
+
+ /* and save it */
+ refresh_sequence_number(domain, True);
+ centry = centry_start(domain, status);
+ if (!centry) goto skip_save;
+ centry_put_uint32(centry, *num_entries);
+ for (i=0; i<(*num_entries); i++) {
+ centry_put_string(centry, (*info)[i].acct_name);
+ centry_put_string(centry, (*info)[i].full_name);
+ centry_put_uint32(centry, (*info)[i].user_rid);
+ centry_put_uint32(centry, (*info)[i].group_rid);
+ if (cache->backend->consistent) {
+ /* when the backend is consistent we can pre-prime some mappings */
+ wcache_save_name_to_sid(domain, NT_STATUS_OK,
+ (*info)[i].acct_name,
+ form_sid(domain, (*info)[i].user_rid),
+ SID_NAME_USER);
+ wcache_save_sid_to_name(domain, NT_STATUS_OK,
+ form_sid(domain, (*info)[i].user_rid),
+ (*info)[i].acct_name,
+ SID_NAME_USER, (*info)[i].user_rid);
+ wcache_save_user(domain, NT_STATUS_OK, &(*info)[i]);
+ }
+ }
+ centry_end(centry, "UL/%s", domain->name);
+ centry_free(centry);
+
+skip_save:
+ return status;
+}
+
+/* list all domain groups */
+static NTSTATUS enum_dom_groups(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ uint32 *num_entries,
+ struct acct_info **info)
+{
+ struct winbind_cache *cache = get_cache(domain);
+ struct cache_entry *centry = NULL;
+ NTSTATUS status;
+ int i;
+
+ if (!cache->tdb) goto do_query;
+
+ centry = wcache_fetch(cache, domain, "GL/%s", domain->name);
+ if (!centry) goto do_query;
+
+ *num_entries = centry_uint32(centry);
+
+ if (*num_entries == 0) goto do_cached;
+
+ (*info) = talloc(mem_ctx, sizeof(**info) * (*num_entries));
+ if (! (*info)) smb_panic("enum_dom_groups out of memory");
+ for (i=0; i<(*num_entries); i++) {
+ fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx));
+ fstrcpy((*info)[i].acct_desc, centry_string(centry, mem_ctx));
+ (*info)[i].rid = centry_uint32(centry);
+ }
+
+do_cached:
+ status = centry->status;
+ centry_free(centry);
+ return status;
+
+do_query:
+ *num_entries = 0;
+ *info = NULL;
+
+ if (wcache_server_down(domain)) {
+ return NT_STATUS_SERVER_DISABLED;
+ }
+
+ status = cache->backend->enum_dom_groups(domain, mem_ctx, num_entries, info);
+
+ /* and save it */
+ refresh_sequence_number(domain, True);
+ centry = centry_start(domain, status);
+ if (!centry) goto skip_save;
+ centry_put_uint32(centry, *num_entries);
+ for (i=0; i<(*num_entries); i++) {
+ centry_put_string(centry, (*info)[i].acct_name);
+ centry_put_string(centry, (*info)[i].acct_desc);
+ centry_put_uint32(centry, (*info)[i].rid);
+ }
+ centry_end(centry, "GL/%s", domain->name);
+ centry_free(centry);
+
+skip_save:
+ return status;
+}
+
+
+/* convert a single name to a sid in a domain */
+static NTSTATUS name_to_sid(struct winbindd_domain *domain,
+ const char *name,
+ DOM_SID *sid,
+ enum SID_NAME_USE *type)
+{
+ struct winbind_cache *cache = get_cache(domain);
+ struct cache_entry *centry = NULL;
+ NTSTATUS status;
+
+ if (!cache->tdb) goto do_query;
+
+ centry = wcache_fetch(cache, domain, "NS/%s/%s", domain->name, name);
+ if (!centry) goto do_query;
+ *type = centry_uint32(centry);
+ sid_parse(centry->data + centry->ofs, centry->len - centry->ofs, sid);
+
+ status = centry->status;
+ centry_free(centry);
+ return status;
+
+do_query:
+ ZERO_STRUCTP(sid);
+
+ if (wcache_server_down(domain)) {
+ return NT_STATUS_SERVER_DISABLED;
+ }
+ status = cache->backend->name_to_sid(domain, name, sid, type);
+
+ /* and save it */
+ wcache_save_name_to_sid(domain, status, name, sid, *type);
+
+ 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,
+ DOM_SID *sid,
+ char **name,
+ enum SID_NAME_USE *type)
+{
+ struct winbind_cache *cache = get_cache(domain);
+ struct cache_entry *centry = NULL;
+ NTSTATUS status;
+ uint32 rid = 0;
+
+ sid_peek_rid(sid, &rid);
+
+ if (!cache->tdb) goto do_query;
+
+ centry = wcache_fetch(cache, domain, "SN/%s/%d", domain->name, rid);
+ if (!centry) goto do_query;
+ if (NT_STATUS_IS_OK(centry->status)) {
+ *type = centry_uint32(centry);
+ *name = centry_string(centry, mem_ctx);
+ }
+ status = centry->status;
+ centry_free(centry);
+ return status;
+
+do_query:
+ *name = NULL;
+
+ if (wcache_server_down(domain)) {
+ return NT_STATUS_SERVER_DISABLED;
+ }
+ status = cache->backend->sid_to_name(domain, mem_ctx, sid, name, type);
+
+ /* and save it */
+ refresh_sequence_number(domain, True);
+ wcache_save_sid_to_name(domain, status, sid, *name, *type, rid);
+
+ return status;
+}
+
+
+/* Lookup user information from a rid */
+static NTSTATUS query_user(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ uint32 user_rid,
+ WINBIND_USERINFO *info)
+{
+ struct winbind_cache *cache = get_cache(domain);
+ struct cache_entry *centry = NULL;
+ NTSTATUS status;
+
+ if (!cache->tdb) goto do_query;
+
+ centry = wcache_fetch(cache, domain, "U/%s/%x", domain->name, user_rid);
+ if (!centry) goto do_query;
+
+ info->acct_name = centry_string(centry, mem_ctx);
+ info->full_name = centry_string(centry, mem_ctx);
+ info->user_rid = centry_uint32(centry);
+ info->group_rid = centry_uint32(centry);
+ status = centry->status;
+ centry_free(centry);
+ return status;
+
+do_query:
+ ZERO_STRUCTP(info);
+
+ if (wcache_server_down(domain)) {
+ return NT_STATUS_SERVER_DISABLED;
+ }
+
+ status = cache->backend->query_user(domain, mem_ctx, user_rid, info);
+
+ /* and save it */
+ refresh_sequence_number(domain, True);
+ wcache_save_user(domain, status, info);
+
+ return status;
+}
+
+
+/* Lookup groups a user is a member of. */
+static NTSTATUS lookup_usergroups(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ uint32 user_rid,
+ uint32 *num_groups, uint32 **user_gids)
+{
+ struct winbind_cache *cache = get_cache(domain);
+ struct cache_entry *centry = NULL;
+ NTSTATUS status;
+ int i;
+
+ if (!cache->tdb) goto do_query;
+
+ centry = wcache_fetch(cache, domain, "UG/%s/%x", domain->name, user_rid);
+ if (!centry) goto do_query;
+
+ *num_groups = centry_uint32(centry);
+
+ if (*num_groups == 0) goto do_cached;
+
+ (*user_gids) = talloc(mem_ctx, sizeof(**user_gids) * (*num_groups));
+ if (! (*user_gids)) smb_panic("lookup_usergroups out of memory");
+ for (i=0; i<(*num_groups); i++) {
+ (*user_gids)[i] = centry_uint32(centry);
+ }
+
+do_cached:
+ status = centry->status;
+ centry_free(centry);
+ return status;
+
+do_query:
+ (*num_groups) = 0;
+ (*user_gids) = NULL;
+
+ if (wcache_server_down(domain)) {
+ return NT_STATUS_SERVER_DISABLED;
+ }
+ status = cache->backend->lookup_usergroups(domain, mem_ctx, user_rid, num_groups, user_gids);
+
+ /* and save it */
+ refresh_sequence_number(domain, True);
+ centry = centry_start(domain, status);
+ if (!centry) goto skip_save;
+ centry_put_uint32(centry, *num_groups);
+ for (i=0; i<(*num_groups); i++) {
+ centry_put_uint32(centry, (*user_gids)[i]);
+ }
+ centry_end(centry, "UG/%s/%x", domain->name, user_rid);
+ centry_free(centry);
+
+skip_save:
+ return status;
+}
+
+
+static NTSTATUS lookup_groupmem(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ uint32 group_rid, uint32 *num_names,
+ uint32 **rid_mem, char ***names,
+ uint32 **name_types)
+{
+ struct winbind_cache *cache = get_cache(domain);
+ struct cache_entry *centry = NULL;
+ NTSTATUS status;
+ int i;
+
+ if (!cache->tdb) goto do_query;
+
+ centry = wcache_fetch(cache, domain, "GM/%s/%x", domain->name, group_rid);
+ if (!centry) goto do_query;
+
+ *num_names = centry_uint32(centry);
+
+ if (*num_names == 0) goto do_cached;
+
+ (*rid_mem) = talloc(mem_ctx, sizeof(**rid_mem) * (*num_names));
+ (*names) = talloc(mem_ctx, sizeof(**names) * (*num_names));
+ (*name_types) = talloc(mem_ctx, sizeof(**name_types) * (*num_names));
+
+ if (! (*rid_mem) || ! (*names) || ! (*name_types)) {
+ smb_panic("lookup_groupmem out of memory");
+ }
+
+ for (i=0; i<(*num_names); i++) {
+ (*rid_mem)[i] = centry_uint32(centry);
+ (*names)[i] = centry_string(centry, mem_ctx);
+ (*name_types)[i] = centry_uint32(centry);
+ }
+
+do_cached:
+ status = centry->status;
+ centry_free(centry);
+ return status;
+
+do_query:
+ (*num_names) = 0;
+ (*rid_mem) = NULL;
+ (*names) = NULL;
+ (*name_types) = NULL;
+
+
+ if (wcache_server_down(domain)) {
+ return NT_STATUS_SERVER_DISABLED;
+ }
+ status = cache->backend->lookup_groupmem(domain, mem_ctx, group_rid, num_names,
+ rid_mem, names, name_types);
+
+ /* and save it */
+ refresh_sequence_number(domain, True);
+ centry = centry_start(domain, status);
+ if (!centry) goto skip_save;
+ centry_put_uint32(centry, *num_names);
+ for (i=0; i<(*num_names); i++) {
+ centry_put_uint32(centry, (*rid_mem)[i]);
+ centry_put_string(centry, (*names)[i]);
+ centry_put_uint32(centry, (*name_types)[i]);
+ }
+ centry_end(centry, "GM/%s/%x", domain->name, group_rid);
+ centry_free(centry);
+
+skip_save:
+ return status;
+}
+
+/* find the sequence number for a domain */
+static NTSTATUS sequence_number(struct winbindd_domain *domain, uint32 *seq)
+{
+ refresh_sequence_number(domain, False);
+
+ *seq = domain->sequence_number;
+
+ return NT_STATUS_OK;
+}
+
+/* enumerate trusted domains */
+static NTSTATUS trusted_domains(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ uint32 *num_domains,
+ char ***names,
+ DOM_SID **dom_sids)
+{
+ struct winbind_cache *cache = get_cache(domain);
+
+ /* we don't cache this call */
+ return cache->backend->trusted_domains(domain, mem_ctx, num_domains,
+ names, dom_sids);
+}
+
+/* find the domain sid */
+static NTSTATUS domain_sid(struct winbindd_domain *domain, DOM_SID *sid)
+{
+ struct winbind_cache *cache = get_cache(domain);
+
+ /* we don't cache this call */
+ return cache->backend->domain_sid(domain, sid);
+}
+
+/* the ADS backend methods are exposed via this structure */
+struct winbindd_methods cache_methods = {
+ True,
+ query_user_list,
+ enum_dom_groups,
+ name_to_sid,
+ sid_to_name,
+ query_user,
+ lookup_usergroups,
+ lookup_groupmem,
+ sequence_number,
+ trusted_domains,
+ domain_sid
+};
+
+