/*
- Unix SMB/Netbios implementation.
- Version 2.0
+ Unix SMB/CIFS implementation.
- Winbind daemon - caching related functions
+ Winbind cache backend functions
- Copyright (C) Tim Potter 2000
+ 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
#include "winbindd.h"
-#define CACHE_TYPE_USER "USR"
-#define CACHE_TYPE_GROUP "GRP"
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_WINBIND
-/* Initialise caching system */
-
-static TDB_CONTEXT *cache_tdb;
+struct winbind_cache {
+ struct winbindd_methods *backend;
+ TDB_CONTEXT *tdb;
+};
-struct cache_rec {
- uint32 seq_num;
- time_t mod_time;
+struct cache_entry {
+ NTSTATUS status;
+ uint32 sequence_number;
+ uint8 *data;
+ uint32 len, ofs;
};
-void winbindd_cache_init(void)
+#define WINBINDD_MAX_CACHE_SIZE (50*1024*1024)
+
+static struct winbind_cache *wcache;
+
+/* flush the cache */
+void wcache_flush_cache(void)
{
- /* Open tdb cache */
- unlink(lock_path("winbindd_cache.tdb"));
- if (!(cache_tdb = tdb_open(lock_path("winbindd_cache.tdb"), 0,
- TDB_NOLOCK,
- O_RDWR | O_CREAT, 0600))) {
- DEBUG(0, ("Unable to open tdb cache - user and group caching "
- "disabled\n"));
+ 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_CLEAR_IF_FIRST, O_RDWR|O_CREAT, 0600);
+
+ if (!wcache->tdb) {
+ DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
}
}
-/* get the domain sequence number, possibly re-fetching */
-static uint32 cached_sequence_number(char *domain_name)
+void winbindd_check_cache_size(time_t t)
{
- fstring keystr;
- TDB_DATA dbuf;
- struct cache_rec rec;
- time_t t = time(NULL);
+ static time_t last_check_time;
+ struct stat st;
+
+ if (last_check_time == (time_t)0)
+ last_check_time = t;
- slprintf(keystr, sizeof(keystr), "CACHESEQ/%s", domain_name);
- dbuf = tdb_fetch_by_string(cache_tdb, keystr);
- if (!dbuf.dptr || dbuf.dsize != sizeof(rec)) {
- goto refetch;
+ 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;
}
- memcpy(&rec, dbuf.dptr, sizeof(rec));
- free(dbuf.dptr);
- if (t < (rec.mod_time + lp_winbind_cache_time())) {
- DEBUG(4,("cached sequence number for %s is %u\n",
- domain_name, (unsigned)rec.seq_num));
- return rec.seq_num;
+ if (fstat(wcache->tdb->fd, &st) == -1) {
+ DEBUG(0, ("Unable to check size of tdb cache %s!\n", strerror(errno) ));
+ return;
}
- refetch:
- rec.seq_num = domain_sequence_number(domain_name);
- rec.mod_time = t;
- tdb_store_by_string(cache_tdb, keystr, &rec, sizeof(rec));
+ 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 rec.seq_num;
+ return ret;
}
-/* Check whether a seq_num for a cached item has expired */
-static BOOL cache_domain_expired(char *domain_name, uint32 seq_num)
+/*
+ free a centry structure
+*/
+static void centry_free(struct cache_entry *centry)
{
- if (cached_sequence_number(domain_name) != seq_num) {
- DEBUG(4,("seq %u for %s has expired\n", (unsigned)seq_num, domain_name));
- return True;
+ 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");
}
- return False;
+ ret = IVAL(centry->data, centry->ofs);
+ centry->ofs += 4;
+ return ret;
}
-static void set_cache_sequence_number(char *domain_name, char *cache_type, char *subkey)
+/*
+ pull a uint8 from a cache entry
+*/
+static uint8 centry_uint8(struct cache_entry *centry)
{
- fstring keystr;
- slprintf(keystr,sizeof(keystr),"CACHESEQ %s/%s/%s",
- domain_name, cache_type, subkey?subkey:"");
- tdb_store_int(cache_tdb, keystr, cached_sequence_number(domain_name));
+ 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;
}
-static uint32 get_cache_sequence_number(char *domain_name, char *cache_type, char *subkey)
+/* pull a string from a cache entry, using the supplied
+ talloc context
+*/
+static char *centry_string(struct cache_entry *centry, TALLOC_CTX *mem_ctx)
{
- fstring keystr;
- uint32 seq_num;
- slprintf(keystr,sizeof(keystr),"CACHESEQ %s/%s/%s",
- domain_name, cache_type, subkey?subkey:"");
- seq_num = (uint32)tdb_fetch_int(cache_tdb, keystr);
- DEBUG(4,("%s is %u\n", keystr, (unsigned)seq_num));
- return seq_num;
+ 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;
}
-/* Fill the user or group cache with supplied data */
-static void fill_cache(char *domain_name, char *cache_type,
- struct acct_info *sam_entries,
- int num_sam_entries)
+/* the server is considered down if it can't give us a sequence number */
+static BOOL wcache_server_down(struct winbindd_domain *domain)
{
- fstring keystr;
+ if (!wcache->tdb) return False;
+ return (domain->sequence_number == DOM_SEQUENCE_NONE);
+}
- if (lp_winbind_cache_time() == 0) return;
- /* Error check */
- if (!sam_entries || (num_sam_entries == 0)) return;
+/*
+ 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;
+ unsigned cache_time = lp_winbind_cache_time();
- DEBUG(4, ("filling %s cache for domain %s with %d entries\n",
- cache_type, domain_name, num_sam_entries));
+ /* trying to reconnect is expensive, don't do it too often */
+ if (domain->sequence_number == DOM_SEQUENCE_NONE) {
+ cache_time *= 8;
+ }
- /* Store data as a mega-huge chunk in the tdb */
- slprintf(keystr, sizeof(keystr), "%s CACHE DATA/%s", cache_type,
- domain_name);
+ time_diff = time(NULL) - domain->last_seq_check;
- tdb_store_by_string(cache_tdb, keystr,
- sam_entries, sizeof(struct acct_info) * num_sam_entries);
+ /* see if we have to refetch the domain sequence number */
+ if (!force && (time_diff < cache_time)) {
+ return;
+ }
- /* Stamp cache with current seq number */
- set_cache_sequence_number(domain_name, cache_type, NULL);
+ 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);
}
-/* Fill the user cache with supplied data */
+/*
+ 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;
+}
-void winbindd_fill_user_cache(char *domain_name,
- struct acct_info *sam_entries,
- int num_sam_entries)
+/*
+ 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, ...)
{
- fill_cache(domain_name, CACHE_TYPE_USER, sam_entries, num_sam_entries);
+ 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)) {
+ extern BOOL opt_dual_daemon;
+
+ if (opt_dual_daemon) {
+ extern BOOL backgroud_process;
+ backgroud_process = True;
+ } else {
+ centry_free(centry);
+ return NULL;
+ }
+ }
+
+ return centry;
}
-/* Fill the group cache with supplied data */
+/*
+ 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;
+}
-void winbindd_fill_group_cache(char *domain_name,
- struct acct_info *sam_entries,
- int num_sam_entries)
+/*
+ push a uint32 into a centry
+*/
+static void centry_put_uint32(struct cache_entry *centry, uint32 v)
{
- fill_cache(domain_name, CACHE_TYPE_GROUP, sam_entries, num_sam_entries);
+ centry_expand(centry, 4);
+ SIVAL(centry->data, centry->ofs, v);
+ centry->ofs += 4;
}
-static void fill_cache_entry(char *domain, char *cache_type, char *name, void *buf, int len)
+/*
+ push a uint8 into a centry
+*/
+static void centry_put_uint8(struct cache_entry *centry, uint8 v)
{
- fstring keystr;
+ centry_expand(centry, 1);
+ SCVAL(centry->data, centry->ofs, v);
+ centry->ofs += 1;
+}
- /* Create key for store */
- slprintf(keystr, sizeof(keystr), "%s/%s/%s", cache_type, domain, name);
+/*
+ push a string into a centry
+ */
+static void centry_put_string(struct cache_entry *centry, const char *s)
+{
+ int len;
- DEBUG(4, ("filling cache entry %s\n", keystr));
+ if (!s) {
+ /* null strings are marked as len 0xFFFF */
+ centry_put_uint8(centry, 0xFF);
+ return;
+ }
- /* Store it */
- tdb_store_by_string(cache_tdb, keystr, buf, len);
+ 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;
}
-/* Fill a user info cache entry */
-void winbindd_fill_user_cache_entry(char *domain, char *user_name,
- struct winbindd_pw *pw)
+/*
+ start a centry for output. When finished, call centry_end()
+*/
+struct cache_entry *centry_start(struct winbindd_domain *domain, NTSTATUS status)
{
- if (lp_winbind_cache_time() == 0) return;
+ struct cache_entry *centry;
+
+ if (!wcache->tdb) return NULL;
+
+ centry = smb_xmalloc(sizeof(*centry));
- fill_cache_entry(domain, CACHE_TYPE_USER, user_name, pw, sizeof(struct winbindd_pw));
- set_cache_sequence_number(domain, CACHE_TYPE_USER, user_name);
+ 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;
}
-/* Fill a user uid cache entry */
-void winbindd_fill_uid_cache_entry(char *domain, uid_t uid,
- struct winbindd_pw *pw)
+/*
+ finish a centry and write it to the tdb
+*/
+static void centry_end(struct cache_entry *centry, const char *format, ...)
{
- fstring uidstr;
+ va_list ap;
+ char *kstr;
+ TDB_DATA key, data;
+
+ va_start(ap, format);
+ smb_xvasprintf(&kstr, format, ap);
+ va_end(ap);
- if (lp_winbind_cache_time() == 0) return;
+ key.dptr = kstr;
+ key.dsize = strlen(kstr);
+ data.dptr = centry->data;
+ data.dsize = centry->ofs;
- slprintf(uidstr, sizeof(uidstr), "#%u", (unsigned)uid);
- fill_cache_entry(domain, CACHE_TYPE_USER, uidstr, pw, sizeof(struct winbindd_pw));
- set_cache_sequence_number(domain, CACHE_TYPE_USER, uidstr);
+ tdb_store(wcache->tdb, key, data, TDB_REPLACE);
+ free(kstr);
}
-/* Fill a group info cache entry */
-void winbindd_fill_group_cache_entry(char *domain, char *group_name,
- struct winbindd_gr *gr, void *extra_data,
- int extra_data_len)
+/* form a sid from the domain plus rid */
+static DOM_SID *form_sid(struct winbindd_domain *domain, uint32 rid)
{
- fstring keystr;
+ static DOM_SID sid;
+ sid_copy(&sid, &domain->sid);
+ sid_append_rid(&sid, rid);
+ return &sid;
+}
- if (lp_winbind_cache_time() == 0) return;
+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;
+ fstring uname;
+
+ 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;
+ fstrcpy(uname, name);
+ strupper(uname);
+ centry_end(centry, "NS/%s/%s", domain->name, uname);
+ centry_free(centry);
+}
- /* Fill group data */
- fill_cache_entry(domain, CACHE_TYPE_GROUP, group_name, gr, sizeof(struct winbindd_gr));
+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;
- /* Fill extra data */
- slprintf(keystr, sizeof(keystr), "%s/%s/%s DATA", CACHE_TYPE_GROUP, domain, group_name);
- tdb_store_by_string(cache_tdb, keystr, extra_data, extra_data_len);
+ 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);
+}
- set_cache_sequence_number(domain, CACHE_TYPE_GROUP, group_name);
+
+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/%d", domain->name, info->user_rid);
+ centry_free(centry);
}
-/* Fill a group info cache entry */
-void winbindd_fill_gid_cache_entry(char *domain, gid_t gid,
- struct winbindd_gr *gr, void *extra_data,
- int extra_data_len)
+
+/* 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)
{
- fstring keystr;
- fstring gidstr;
+ struct winbind_cache *cache = get_cache(domain);
+ struct cache_entry *centry = NULL;
+ NTSTATUS status;
+ int i;
- slprintf(gidstr, sizeof(gidstr), "#%u", (unsigned)gid);
+ if (!cache->tdb) goto do_query;
- if (lp_winbind_cache_time() == 0) return;
+ centry = wcache_fetch(cache, domain, "UL/%s", domain->name);
+ if (!centry) goto do_query;
- /* Fill group data */
- fill_cache_entry(domain, CACHE_TYPE_GROUP, gidstr, gr, sizeof(struct winbindd_gr));
+ *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);
+ }
- /* Fill extra data */
- slprintf(keystr, sizeof(keystr), "%s/%s/%s DATA", CACHE_TYPE_GROUP, domain, gidstr);
- tdb_store_by_string(cache_tdb, keystr, extra_data, extra_data_len);
+do_cached:
+ status = centry->status;
+ centry_free(centry);
+ return status;
- set_cache_sequence_number(domain, CACHE_TYPE_GROUP, gidstr);
+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;
}
-/* Fetch some cached user or group data */
-static BOOL fetch_cache(char *domain_name, char *cache_type,
- struct acct_info **sam_entries, int *num_sam_entries)
+/* list all domain groups */
+static NTSTATUS enum_dom_groups(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ uint32 *num_entries,
+ struct acct_info **info)
{
- TDB_DATA data;
- fstring keystr;
+ struct winbind_cache *cache = get_cache(domain);
+ struct cache_entry *centry = NULL;
+ NTSTATUS status;
+ int i;
- if (lp_winbind_cache_time() == 0) return False;
+ if (!cache->tdb) goto do_query;
- /* Parameter check */
- if (!sam_entries || !num_sam_entries) {
- return False;
+ 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);
}
- /* Check cache data is current */
- if (cache_domain_expired(domain_name,
- get_cache_sequence_number(domain_name, cache_type, NULL))) {
- return False;
+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;
}
-
- /* Create key */
- slprintf(keystr, sizeof(keystr), "%s CACHE DATA/%s", cache_type,
- domain_name);
-
- /* Fetch cache information */
- data = tdb_fetch_by_string(cache_tdb, keystr);
-
- if (!data.dptr) return False;
- /* Copy across cached data. We can save a memcpy() by directly
- assigning the data.dptr to the sam_entries pointer. It will
- be freed by the end{pw,gr}ent() function. */
-
- *sam_entries = (struct acct_info *)data.dptr;
- *num_sam_entries = data.dsize / sizeof(struct acct_info);
-
- DEBUG(4, ("fetched %d cached %s entries for domain %s\n",
- *num_sam_entries, cache_type, domain_name));
-
- return True;
+ 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;
}
-/* Return cached entries for a domain. Return false if there are no cached
- entries, or the cached information has expired for the domain. */
-BOOL winbindd_fetch_user_cache(char *domain_name,
- struct acct_info **sam_entries,
- int *num_entries)
+/* 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)
{
- return fetch_cache(domain_name, CACHE_TYPE_USER, sam_entries,
- num_entries);
-}
+ struct winbind_cache *cache = get_cache(domain);
+ struct cache_entry *centry = NULL;
+ NTSTATUS status;
+ fstring uname;
-/* Return cached entries for a domain. Return false if there are no cached
- entries, or the cached information has expired for the domain. */
+ if (!cache->tdb) goto do_query;
-BOOL winbindd_fetch_group_cache(char *domain_name,
- struct acct_info **sam_entries,
- int *num_entries)
-{
- return fetch_cache(domain_name, CACHE_TYPE_GROUP, sam_entries,
- num_entries);
+ fstrcpy(uname, name);
+ strupper(uname);
+ centry = wcache_fetch(cache, domain, "NS/%s/%s", domain->name, uname);
+ 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);
+
+ /* We can't save the sid to name mapping as we don't know the
+ correct case of the name without looking it up */
+
+ return status;
}
-static BOOL fetch_cache_entry(char *domain, char *cache_type, char *name, void *buf, int len)
+/* 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)
{
- TDB_DATA data;
- fstring keystr;
-
- /* Create key for lookup */
- slprintf(keystr, sizeof(keystr), "%s/%s/%s", cache_type, domain, name);
-
- /* Look up cache entry */
- data = tdb_fetch_by_string(cache_tdb, keystr);
- if (!data.dptr) return False;
-
- DEBUG(4, ("returning cached entry for %s\\%s\n", domain, name));
-
- /* Copy found entry into buffer */
- memcpy((char *)buf, data.dptr, len < data.dsize ? len : data.dsize);
- free(data.dptr);
- return True;
+ struct winbind_cache *cache = get_cache(domain);
+ struct cache_entry *centry = NULL;
+ NTSTATUS status;
+ uint32 rid = 0;
+
+ if (!sid_peek_check_rid(&domain->sid, sid, &rid))
+ return NT_STATUS_INVALID_PARAMETER;
+
+ 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);
+ wcache_save_name_to_sid(domain, status, *name, sid, *type);
+
+ return status;
}
-/* Fetch an individual user cache entry */
-BOOL winbindd_fetch_user_cache_entry(char *domain_name, char *user,
- struct winbindd_pw *pw)
+
+/* Lookup user information from a rid */
+static NTSTATUS query_user(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ uint32 user_rid,
+ WINBIND_USERINFO *info)
{
- uint32 seq_num;
+ 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/%d", 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;
- if (lp_winbind_cache_time() == 0) return False;
+do_query:
+ ZERO_STRUCTP(info);
- seq_num = get_cache_sequence_number(domain_name, CACHE_TYPE_USER, user);
- if (cache_domain_expired(domain_name, seq_num)) return False;
+ 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 fetch_cache_entry(domain_name, CACHE_TYPE_USER, user, pw, sizeof(struct winbindd_pw));
+ return status;
}
-/* Fetch an individual uid cache entry */
-BOOL winbindd_fetch_uid_cache_entry(char *domain_name, uid_t uid,
- struct winbindd_pw *pw)
+
+/* 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)
{
- fstring uidstr;
- uint32 seq_num;
+ 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/%d", 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);
+ }
- if (lp_winbind_cache_time() == 0) return False;
+do_cached:
+ status = centry->status;
+ centry_free(centry);
+ return status;
- slprintf(uidstr, sizeof(uidstr), "#%u", (unsigned)uid);
- seq_num = get_cache_sequence_number(domain_name, CACHE_TYPE_USER, uidstr);
- if (cache_domain_expired(domain_name, seq_num)) return False;
+do_query:
+ (*num_groups) = 0;
+ (*user_gids) = NULL;
- return fetch_cache_entry(domain_name, CACHE_TYPE_USER, uidstr, pw, sizeof(struct winbindd_pw));
+ 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/%d", domain->name, user_rid);
+ centry_free(centry);
+
+skip_save:
+ return status;
}
-/* Fetch an individual group cache entry. This function differs from the
- user cache code as we need to store the group membership data. */
-BOOL winbindd_fetch_group_cache_entry(char *domain_name, char *group,
- struct winbindd_gr *gr,
- void **extra_data, int *extra_data_len)
+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)
{
- TDB_DATA data;
- fstring keystr;
- uint32 seq_num;
+ struct winbind_cache *cache = get_cache(domain);
+ struct cache_entry *centry = NULL;
+ NTSTATUS status;
+ int i;
- if (lp_winbind_cache_time() == 0) return False;
+ if (!cache->tdb) goto do_query;
- seq_num = get_cache_sequence_number(domain_name, CACHE_TYPE_GROUP, group);
- if (cache_domain_expired(domain_name, seq_num)) return False;
+ centry = wcache_fetch(cache, domain, "GM/%s/%d", domain->name, group_rid);
+ if (!centry) goto do_query;
- /* Fetch group data */
- if (!fetch_cache_entry(domain_name, CACHE_TYPE_GROUP, group, gr, sizeof(struct winbindd_gr))) return False;
+ *num_names = centry_uint32(centry);
- /* Fetch extra data */
- slprintf(keystr, sizeof(keystr), "%s/%s/%s DATA", CACHE_TYPE_GROUP, domain_name, group);
- data = tdb_fetch_by_string(cache_tdb, keystr);
+ if (*num_names == 0) goto do_cached;
- if (!data.dptr) return False;
+ (*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));
- /* Extra data freed when data has been sent */
- if (extra_data) *extra_data = data.dptr;
- if (extra_data_len) *extra_data_len = data.dsize;
-
- return True;
-}
+ 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;
-/* Fetch an individual gid cache entry. This function differs from the
- user cache code as we need to store the group membership data. */
+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/%d", domain->name, group_rid);
+ centry_free(centry);
+
+skip_save:
+ return status;
+}
-BOOL winbindd_fetch_gid_cache_entry(char *domain_name, gid_t gid,
- struct winbindd_gr *gr,
- void **extra_data, int *extra_data_len)
+/* find the sequence number for a domain */
+static NTSTATUS sequence_number(struct winbindd_domain *domain, uint32 *seq)
{
- TDB_DATA data;
- fstring keystr;
- fstring gidstr;
- uint32 seq_num;
+ refresh_sequence_number(domain, False);
- slprintf(gidstr, sizeof(gidstr), "#%u", (unsigned)gid);
-
- if (lp_winbind_cache_time() == 0) return False;
+ *seq = domain->sequence_number;
- seq_num = get_cache_sequence_number(domain_name, CACHE_TYPE_GROUP, gidstr);
- if (cache_domain_expired(domain_name, seq_num)) return False;
+ return NT_STATUS_OK;
+}
- /* Fetch group data */
- if (!fetch_cache_entry(domain_name, CACHE_TYPE_GROUP,
- gidstr, gr, sizeof(struct winbindd_gr))) return False;
+/* enumerate trusted domains */
+static NTSTATUS trusted_domains(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ uint32 *num_domains,
+ char ***names,
+ char ***alt_names,
+ DOM_SID **dom_sids)
+{
+ struct winbind_cache *cache = get_cache(domain);
- /* Fetch extra data */
- slprintf(keystr, sizeof(keystr), "%s/%s/%s DATA", CACHE_TYPE_GROUP, domain_name, gidstr);
- data = tdb_fetch_by_string(cache_tdb, keystr);
- if (!data.dptr) return False;
+ /* we don't cache this call */
+ return cache->backend->trusted_domains(domain, mem_ctx, num_domains,
+ names, alt_names, dom_sids);
+}
- /* Extra data freed when data has been sent */
- if (extra_data) *extra_data = data.dptr;
- if (extra_data_len) *extra_data_len = data.dsize;
+/* find the domain sid */
+static NTSTATUS domain_sid(struct winbindd_domain *domain, DOM_SID *sid)
+{
+ struct winbind_cache *cache = get_cache(domain);
- return True;
+ /* we don't cache this call */
+ return cache->backend->domain_sid(domain, sid);
}
-/* Flush cache data - easiest to just reopen the tdb */
-void winbindd_flush_cache(void)
+/* find the alternate names for the domain, if any */
+static NTSTATUS alternate_name(struct winbindd_domain *domain)
{
- tdb_close(cache_tdb);
- winbindd_cache_init();
+ struct winbind_cache *cache = get_cache(domain);
+
+ /* we don't cache this call */
+ return cache->backend->alternate_name(domain);
}
+
+/* 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,
+ alternate_name
+};