From 31842f5d017f01922cec306f7bfa2fa0fe748ca2 Mon Sep 17 00:00:00 2001 From: Andrew Tridgell Date: Sun, 9 Dec 2001 23:59:42 +0000 Subject: completely new winbindd cache infrastructure this one looks like just another winbind backend, and has the following properties: - does -ve and +ve cacheing of all queries - can be disabled with -n switch to winbindd - stores all records packed, so even huge domains are not a problem for a complete cache - handles the server being down - uses sequence numbers for all entries This fixes a lot of problems with winbindd. Serving from cache is now *very* fast. (This used to be commit fddb4f4c04473a60a97212c0c8e143d6a4d68380) --- source3/nsswitch/winbindd_cache.c | 997 +++++++++++++++++++++----------------- 1 file changed, 548 insertions(+), 449 deletions(-) (limited to 'source3/nsswitch/winbindd_cache.c') diff --git a/source3/nsswitch/winbindd_cache.c b/source3/nsswitch/winbindd_cache.c index 32ef3526fa..da918df4f3 100644 --- a/source3/nsswitch/winbindd_cache.c +++ b/source3/nsswitch/winbindd_cache.c @@ -1,10 +1,9 @@ /* Unix SMB/Netbios implementation. - Version 2.0 - 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 @@ -23,587 +22,687 @@ #include "winbindd.h" -#define CACHE_TYPE_USER "USR" -#define CACHE_TYPE_GROUP "GRP" -#define CACHE_TYPE_NAME "NAM" /* Stores mapping from SID to name. */ -#define CACHE_TYPE_SID "SID" /* Stores mapping from name to SID. */ - -/* Initialise caching system */ - -static TDB_CONTEXT *cache_tdb; - -struct cache_rec { - uint32 seq_num; - time_t mod_time; +struct winbind_cache { + struct winbindd_methods *backend; + TDB_CONTEXT *tdb; }; -void winbindd_cache_init(void) -{ - /* Open tdb cache */ - - if (!(cache_tdb = tdb_open_log(lock_path("winbindd_cache.tdb"), 0, - TDB_NOLOCK, O_RDWR | O_CREAT | O_TRUNC, - 0600))) - DEBUG(0, ("Unable to open tdb cache - user and group caching disabled\n")); -} +struct cache_entry { + NTSTATUS status; + uint32 sequence_number; + uint8 *data; + uint32 len, ofs; +}; -/* get the domain sequence number, possibly re-fetching */ +static struct winbind_cache *wcache; -static uint32 cached_sequence_number(struct winbindd_domain *domain) +void wcache_flush_cache(void) { - fstring keystr; - TDB_DATA dbuf; - struct cache_rec rec; - time_t t = time(NULL); - - snprintf(keystr, sizeof(keystr), "CACHESEQ/%s", domain->name); - dbuf = tdb_fetch_by_string(cache_tdb, keystr); - - if (!dbuf.dptr || dbuf.dsize != sizeof(rec)) - goto refetch; + extern BOOL opt_nocache; - memcpy(&rec, dbuf.dptr, sizeof(rec)); - SAFE_FREE(dbuf.dptr); - - if (t < (rec.mod_time + lp_winbind_cache_time())) { - DEBUG(3,("cached sequence number for %s is %u\n", - domain->name, (unsigned)rec.seq_num)); - return rec.seq_num; + if (!wcache) return; + if (wcache->tdb) { + tdb_close(wcache->tdb); + wcache->tdb = NULL; } + if (opt_nocache) return; - refetch: - rec.seq_num = domain->methods->sequence_number(domain); - rec.mod_time = t; - - tdb_store_by_string(cache_tdb, keystr, &rec, sizeof(rec)); + wcache->tdb = tdb_open_log(lock_path("winbindd_cache.tdb"), 0, + TDB_NOLOCK, O_RDWR | O_CREAT | O_TRUNC, 0600); - return rec.seq_num; + if (!wcache->tdb) { + DEBUG(0,("Failed to open winbindd_cache.tdb!\n")); + } } -/* Check whether a seq_num for a cached item has expired */ -static BOOL cache_domain_expired(struct winbindd_domain *domain, - uint32 seq_num) +/* get the winbind_cache structure */ +static struct winbind_cache *get_cache(struct winbindd_domain *domain) { - uint32 cache_seq = cached_sequence_number(domain); - if (cache_seq != seq_num) { - DEBUG(3,("seq %u for %s has expired (not == %u)\n", (unsigned)seq_num, - domain->name, (unsigned)cache_seq )); - return True; + 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 False; + return ret; } -static void set_cache_sequence_number(struct winbindd_domain *domain, - const char *cache_type, const char *subkey) +/* + free a centry structure +*/ +static void centry_free(struct cache_entry *centry) { - fstring keystr; + if (!centry) return; + SAFE_FREE(centry->data); + free(centry); +} - snprintf(keystr, sizeof(keystr),"CACHESEQ %s/%s/%s", - domain->name, cache_type, subkey?subkey:""); - tdb_store_int(cache_tdb, keystr, cached_sequence_number(domain)); +/* + 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; } -static uint32 get_cache_sequence_number(struct winbindd_domain *domain, - const char *cache_type, const 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; - - snprintf(keystr, sizeof(keystr), "CACHESEQ %s/%s/%s", - domain->name, cache_type, subkey ? subkey : ""); - - seq_num = (uint32)tdb_fetch_int(cache_tdb, keystr); - - DEBUG(3,("%s is %u\n", keystr, (unsigned)seq_num)); + uint32 len; + char *ret; + + len = centry_uint32(centry); + 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"); + } - return seq_num; + 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 store_cache(struct winbindd_domain *domain, const char *cache_type, - void *sam_entries, int buflen) +/* 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 */ +/* + 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; - if (!sam_entries || buflen == 0) + /* see if we have to refetch the domain sequence number */ + if (!force && (time(NULL) - domain->last_seq_check < lp_winbind_cache_time())) { return; + } - /* Store data as a mega-huge chunk in the tdb */ - - snprintf(keystr, sizeof(keystr), "%s CACHE DATA/%s", cache_type, - domain->name); + status = wcache->backend->sequence_number(domain, &domain->sequence_number); - tdb_store_by_string(cache_tdb, keystr, sam_entries, buflen); + if (!NT_STATUS_IS_OK(status)) { + domain->sequence_number = DOM_SEQUENCE_NONE; + } - /* Stamp cache with current seq number */ + domain->last_seq_check = time(NULL); - set_cache_sequence_number(domain, cache_type, NULL); + DEBUG(0,("seq number now %d\n", domain->sequence_number)); } -/* Fill the user cache with supplied data */ - -void winbindd_store_user_cache(struct winbindd_domain *domain, - struct getpwent_user *sam_entries, - int num_sam_entries) +/* + decide if a cache entry has expired +*/ +static BOOL centry_expired(struct winbindd_domain *domain, struct cache_entry *centry) { - DEBUG(3, ("storing user cache %s/%d entries\n", domain->name, - num_sam_entries)); - - store_cache(domain, CACHE_TYPE_USER, sam_entries, - num_sam_entries * sizeof(struct getpwent_user)); -} - -/* Fill the group cache with supplied data */ + /* 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; + } -void winbindd_store_group_cache(struct winbindd_domain *domain, - struct acct_info *sam_entries, - int num_sam_entries) -{ - DEBUG(0, ("storing group cache %s/%d entries\n", domain->name, - num_sam_entries)); + /* 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; + } - store_cache(domain, CACHE_TYPE_GROUP, sam_entries, - num_sam_entries * sizeof(struct acct_info)); + /* it's expired */ + return True; } -static void store_cache_entry(struct winbindd_domain *domain, const char *cache_type, - const char *name, void *buf, int len) +/* + 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, ...) { - fstring keystr; - - /* Create key for store */ - - snprintf(keystr, sizeof(keystr), "%s/%s/%s", cache_type, - domain->name, name); + va_list ap; + char *kstr; + TDB_DATA data; + struct cache_entry *centry; - /* Store it */ + refresh_sequence_number(domain, False); - tdb_store_by_string(cache_tdb, keystr, buf, len); -} + va_start(ap, format); + smb_xvasprintf(&kstr, format, ap); + va_end(ap); + + data = tdb_fetch_by_string(wcache->tdb, kstr); + free(kstr); + if (!data.dptr) { + /* a cache miss */ + return NULL; + } -/* Fill a name cache entry */ + centry = smb_xmalloc(sizeof(*centry)); + centry->data = data.dptr; + centry->len = data.dsize; + centry->ofs = 0; -void winbindd_store_name_cache_entry(struct winbindd_domain *domain, - char *sid, struct winbindd_name *name) -{ - if (lp_winbind_cache_time() == 0) - return; + 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); - store_cache_entry(domain, CACHE_TYPE_NAME, sid, name, - sizeof(struct winbindd_name)); + if (centry_expired(domain, centry)) { + centry_free(centry); + return NULL; + } - set_cache_sequence_number(domain, CACHE_TYPE_NAME, sid); + return centry; } -/* Fill a SID cache entry */ - -void winbindd_store_sid_cache_entry(struct winbindd_domain *domain, - const char *name, struct winbindd_sid *sid) +/* + make sure we have at least len bytes available in a centry +*/ +static void centry_expand(struct cache_entry *centry, uint32 len) { - if (lp_winbind_cache_time() == 0) - return; - - store_cache_entry(domain, CACHE_TYPE_SID, name, sid, - sizeof(struct winbindd_sid)); - - set_cache_sequence_number(domain, CACHE_TYPE_SID, name); + 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; } -/* Fill a user info cache entry */ - -void winbindd_store_user_cache_entry(struct winbindd_domain *domain, - char *user_name, struct winbindd_pw *pw) +/* + push a uint32 into a centry +*/ +static void centry_put_uint32(struct cache_entry *centry, uint32 v) { - if (lp_winbind_cache_time() == 0) - return; - - store_cache_entry(domain, CACHE_TYPE_USER, user_name, pw, - sizeof(struct winbindd_pw)); - - set_cache_sequence_number(domain, CACHE_TYPE_USER, user_name); + centry_expand(centry, 4); + SIVAL(centry->data, centry->ofs, v); + centry->ofs += 4; } -/* Fill a user uid cache entry */ - -void winbindd_store_uid_cache_entry(struct winbindd_domain *domain, uid_t uid, - struct winbindd_pw *pw) +/* + push a string into a centry + */ +static void centry_put_string(struct cache_entry *centry, const char *s) { - fstring uidstr; - - if (lp_winbind_cache_time() == 0) - return; - - snprintf(uidstr, sizeof(uidstr), "#%u", (unsigned)uid); - - DEBUG(3, ("storing uid cache entry %s/%s\n", domain->name, uidstr)); - - store_cache_entry(domain, CACHE_TYPE_USER, uidstr, pw, - sizeof(struct winbindd_pw)); - - set_cache_sequence_number(domain, CACHE_TYPE_USER, uidstr); + int len = strlen(s); + centry_put_uint32(centry, len); + centry_expand(centry, len); + memcpy(centry->data + centry->ofs, s, len); + centry->ofs += len; } -/* Fill a group info cache entry */ - -void winbindd_store_group_cache_entry(struct winbindd_domain *domain, - char *group_name, struct winbindd_gr *gr, - void *extra_data, int extra_data_len) +/* + start a centry for output. When finished, call centry_end() +*/ +struct cache_entry *centry_start(struct winbindd_domain *domain, NTSTATUS status) { - fstring keystr; - - if (lp_winbind_cache_time() == 0) - return; - - DEBUG(3, ("storing group cache entry %s/%s\n", domain->name, - group_name)); - - /* Fill group data */ + struct cache_entry *centry; - store_cache_entry(domain, CACHE_TYPE_GROUP, group_name, gr, - sizeof(struct winbindd_gr)); + if (!wcache->tdb) return NULL; - /* Fill extra data */ + centry = smb_xmalloc(sizeof(*centry)); - snprintf(keystr, sizeof(keystr), "%s/%s/%s DATA", CACHE_TYPE_GROUP, - domain->name, group_name); + refresh_sequence_number(domain, True); - tdb_store_by_string(cache_tdb, keystr, extra_data, extra_data_len); - - set_cache_sequence_number(domain, CACHE_TYPE_GROUP, group_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 group info cache entry */ - -void winbindd_store_gid_cache_entry(struct winbindd_domain *domain, gid_t gid, - struct winbindd_gr *gr, void *extra_data, - int extra_data_len) +/* + finish a centry and write it to the tdb +*/ +static void centry_end(struct cache_entry *centry, const char *format, ...) { - fstring keystr; - fstring gidstr; - - snprintf(gidstr, sizeof(gidstr), "#%u", (unsigned)gid); - - if (lp_winbind_cache_time() == 0) - return; + va_list ap; + char *kstr; - DEBUG(3, ("storing gid cache entry %s/%s\n", domain->name, gidstr)); + va_start(ap, format); + smb_xvasprintf(&kstr, format, ap); + va_end(ap); - /* Fill group data */ - - store_cache_entry(domain, CACHE_TYPE_GROUP, gidstr, gr, - sizeof(struct winbindd_gr)); - - /* Fill extra data */ - - snprintf(keystr, sizeof(keystr), "%s/%s/%s DATA", CACHE_TYPE_GROUP, - domain->name, gidstr); - - tdb_store_by_string(cache_tdb, keystr, extra_data, extra_data_len); - - set_cache_sequence_number(domain, CACHE_TYPE_GROUP, gidstr); + tdb_store_by_string(wcache->tdb, kstr, centry->data, centry->ofs); + free(kstr); } -/* Fetch some cached user or group data */ - -static BOOL fetch_cache(struct winbindd_domain *domain, char *cache_type, - void **sam_entries, int *buflen) +/* Query display info. This is the basic user list fn */ +static NTSTATUS query_user_list(struct winbindd_domain *domain, + TALLOC_CTX *mem_ctx, + uint32 *start_ndx, uint32 *num_entries, + WINBIND_USERINFO **info) { - TDB_DATA data; - fstring keystr; - - if (lp_winbind_cache_time() == 0) - return False; + struct winbind_cache *cache = get_cache(domain); + struct cache_entry *centry = NULL; + NTSTATUS status; + int i; - /* Parameter check */ + if (!cache->tdb) goto do_query; - if (!sam_entries || !buflen) - return False; - - /* Check cache data is current */ + centry = wcache_fetch(cache, domain, "USERLIST/%s/%d", domain->name, *start_ndx); + if (!centry) goto do_query; - if (cache_domain_expired(domain, get_cache_sequence_number(domain, cache_type, NULL))) - return False; + *num_entries = centry_uint32(centry); - /* Create key */ + 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); + } - snprintf(keystr, sizeof(keystr), "%s CACHE DATA/%s", cache_type, domain->name); - - /* Fetch cache information */ +do_cached: + status = centry->status; + centry_free(centry); + return status; - data = tdb_fetch_by_string(cache_tdb, keystr); - - if (!data.dptr) - return False; +do_query: + if (wcache_server_down(domain)) { + *num_entries = 0; + return NT_STATUS_SERVER_DISABLED; + } - /* 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; - *buflen = data.dsize; - - return True; + status = cache->backend->query_user_list(domain, mem_ctx, start_ndx, num_entries, info); + + /* and save it */ + 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); + } + centry_end(centry, "USERLIST/%s/%d", domain->name, *start_ndx); + 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(struct winbindd_domain *domain, - struct getpwent_user **sam_entries, - int *num_entries) +/* list all domain groups */ +static NTSTATUS enum_dom_groups(struct winbindd_domain *domain, + TALLOC_CTX *mem_ctx, + uint32 *start_ndx, uint32 *num_entries, + struct acct_info **info) { - BOOL result; - int buflen; - - result = fetch_cache(domain, CACHE_TYPE_USER, - (void **)sam_entries, &buflen); - - *num_entries = buflen / sizeof(struct getpwent_user); - - DEBUG(3, ("fetched %d cache entries for %s\n", *num_entries, - domain->name)); + struct winbind_cache *cache = get_cache(domain); + struct cache_entry *centry = NULL; + NTSTATUS status; + int i; - return result; -} - -/* 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(struct winbindd_domain *domain, - struct acct_info **sam_entries, - int *num_entries) -{ - BOOL result; - int buflen; + centry = wcache_fetch(cache, domain, "GROUPLIST/%s/%d", domain->name, *start_ndx); + if (!centry) goto do_query; - result = fetch_cache(domain, CACHE_TYPE_GROUP, - (void **)sam_entries, &buflen); + *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); + } - *num_entries = buflen / sizeof(struct acct_info); +do_cached: + status = centry->status; + centry_free(centry); + return status; - DEBUG(3, ("fetched %d cache entries for %s\n", *num_entries, - domain->name)); +do_query: + if (wcache_server_down(domain)) { + *num_entries = 0; + return NT_STATUS_SERVER_DISABLED; + } - return result; + status = cache->backend->enum_dom_groups(domain, mem_ctx, start_ndx, num_entries, info); + + /* and save it */ + 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, "GROUPLIST/%s/%d", domain->name, *start_ndx); + centry_free(centry); + +skip_save: + return status; } -static BOOL fetch_cache_entry(struct winbindd_domain *domain, - const char *cache_type, - const char *name, void *buf, int len) -{ - TDB_DATA data; - fstring keystr; - - /* Create key for lookup */ - - snprintf(keystr, sizeof(keystr), "%s/%s/%s", cache_type, domain->name, name); - - /* Look up cache entry */ - - data = tdb_fetch_by_string(cache_tdb, keystr); - - if (!data.dptr) - return False; - - /* Copy found entry into buffer */ - memcpy((char *)buf, data.dptr, len < data.dsize ? len : data.dsize); - SAFE_FREE(data.dptr); - - return True; -} - -/* Fetch an individual SID cache entry */ -BOOL winbindd_fetch_sid_cache_entry(struct winbindd_domain *domain, - const char *name, struct winbindd_sid *sid) +/* 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) { - uint32 seq_num; + struct winbind_cache *cache = get_cache(domain); + struct cache_entry *centry = NULL; + NTSTATUS status; + int len; - if (lp_winbind_cache_time() == 0) - return False; + if (!cache->tdb) goto do_query; - seq_num = get_cache_sequence_number(domain, CACHE_TYPE_SID, name); + centry = wcache_fetch(cache, domain, "NAMETOSID/%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); - if (cache_domain_expired(domain, seq_num)) - return False; + status = centry->status; + centry_free(centry); + return status; - return fetch_cache_entry(domain, CACHE_TYPE_SID, name, sid, - sizeof(struct winbindd_sid)); +do_query: + if (wcache_server_down(domain)) { + return NT_STATUS_SERVER_DISABLED; + } + status = cache->backend->name_to_sid(domain, name, sid, type); + + /* and save it */ + centry = centry_start(domain, status); + if (!centry) goto skip_save; + 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, "NAMETOSID/%s/%s", domain->name, name); + centry_free(centry); + +skip_save: + return status; } -/* Fetch an individual name cache entry */ - -BOOL winbindd_fetch_name_cache_entry(struct winbindd_domain *domain, - char *sid, struct winbindd_name *name) +/* convert a sid to a user or group name */ +static NTSTATUS sid_to_name(struct winbindd_domain *domain, + TALLOC_CTX *mem_ctx, + DOM_SID *sid, + char **name, + enum SID_NAME_USE *type) { - uint32 seq_num; + struct winbind_cache *cache = get_cache(domain); + struct cache_entry *centry = NULL; + NTSTATUS status; + char *sidstr = NULL; - if (lp_winbind_cache_time() == 0) - return False; + if (!cache->tdb) goto do_query; - seq_num = get_cache_sequence_number(domain, CACHE_TYPE_NAME, sid); + sidstr = ads_sid_binstring(sid); - if (cache_domain_expired(domain, seq_num)) - return False; + centry = wcache_fetch(cache, domain, "SIDTONAME/%s/%s", domain->name, sidstr); + 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); + SAFE_FREE(sidstr); + return status; + +do_query: + 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 */ + centry = centry_start(domain, status); + if (!centry) goto skip_save; + if (NT_STATUS_IS_OK(status)) { + centry_put_uint32(centry, *type); + centry_put_string(centry, *name); + } + centry_end(centry, "SIDTONAME/%s/%s", domain->name, sidstr); + centry_free(centry); - return fetch_cache_entry(domain, CACHE_TYPE_NAME, sid, name, - sizeof(struct winbindd_name)); +skip_save: + SAFE_FREE(sidstr); + return status; } -/* Fetch an individual user cache entry */ -BOOL winbindd_fetch_user_cache_entry(struct winbindd_domain *domain, - 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; - - if (lp_winbind_cache_time() == 0) - return False; - - seq_num = get_cache_sequence_number(domain, CACHE_TYPE_USER, user); - - if (cache_domain_expired(domain, seq_num)) - return False; - - return fetch_cache_entry(domain, CACHE_TYPE_USER, user, pw, - sizeof(struct winbindd_pw)); + 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, "USER/%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: + if (wcache_server_down(domain)) { + return NT_STATUS_SERVER_DISABLED; + } + status = cache->backend->query_user(domain, mem_ctx, user_rid, info); + + /* and save it */ + centry = centry_start(domain, status); + if (!centry) goto skip_save; + 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, "USER/%s/%x", domain->name, user_rid); + centry_free(centry); + +skip_save: + return status; } -/* Fetch an individual uid cache entry */ -BOOL winbindd_fetch_uid_cache_entry(struct winbindd_domain *domain, 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 (lp_winbind_cache_time() == 0) - return False; + if (!cache->tdb) goto do_query; - snprintf(uidstr, sizeof(uidstr), "#%u", (unsigned)uid); + centry = wcache_fetch(cache, domain, "USERGROUPS/%s/%x", domain->name, user_rid); + if (!centry) goto do_query; - seq_num = get_cache_sequence_number(domain, CACHE_TYPE_USER, uidstr); + *num_groups = centry_uint32(centry); + + if (*num_groups == 0) goto do_cached; - if (cache_domain_expired(domain, seq_num)) - return False; + (*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); + } - return fetch_cache_entry(domain, CACHE_TYPE_USER, uidstr, pw, - sizeof(struct winbindd_pw)); +do_cached: + status = centry->status; + centry_free(centry); + return status; + +do_query: + if (wcache_server_down(domain)) { + (*num_groups) = 0; + return NT_STATUS_SERVER_DISABLED; + } + status = cache->backend->lookup_usergroups(domain, mem_ctx, user_rid, num_groups, user_gids); + + /* and save it */ + 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, "USERGROUPS/%s/%x", 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(struct winbindd_domain *domain, - 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; - - if (lp_winbind_cache_time() == 0) - return False; - - seq_num = get_cache_sequence_number(domain, CACHE_TYPE_GROUP, group); + struct winbind_cache *cache = get_cache(domain); + struct cache_entry *centry = NULL; + NTSTATUS status; + int i; - if (cache_domain_expired(domain, seq_num)) - return False; + if (!cache->tdb) goto do_query; - /* Fetch group data */ + centry = wcache_fetch(cache, domain, "GROUPMEM/%s/%x", domain->name, group_rid); + if (!centry) goto do_query; - if (!fetch_cache_entry(domain, CACHE_TYPE_GROUP, group, gr, sizeof(struct winbindd_gr))) - return False; + *num_names = centry_uint32(centry); - /* Fetch extra data */ - - snprintf(keystr, sizeof(keystr), "%s/%s/%s DATA", CACHE_TYPE_GROUP, - domain->name, group); + if (*num_names == 0) goto do_cached; - data = tdb_fetch_by_string(cache_tdb, keystr); + (*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 (!data.dptr) - return False; + if (! (*rid_mem) || ! (*names) || ! (*name_types)) { + smb_panic("lookup_groupmem out of memory"); + } - /* Extra data freed when data has been sent */ + 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); + } - if (extra_data) - *extra_data = data.dptr; +do_cached: + status = centry->status; + centry_free(centry); + return status; - if (extra_data_len) - *extra_data_len = data.dsize; - - return True; +do_query: + if (wcache_server_down(domain)) { + (*num_names) = 0; + 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 */ + 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, "GROUPMEM/%s/%x", domain->name, group_rid); + centry_free(centry); + +skip_save: + 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. */ - -BOOL winbindd_fetch_gid_cache_entry(struct winbindd_domain *domain, 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); - snprintf(gidstr, sizeof(gidstr), "#%u", (unsigned)gid); - - if (lp_winbind_cache_time() == 0) - return False; - - seq_num = get_cache_sequence_number(domain, CACHE_TYPE_GROUP, gidstr); - - if (cache_domain_expired(domain, seq_num)) - return False; - - /* Fetch group data */ + *seq = domain->sequence_number; - if (!fetch_cache_entry(domain, CACHE_TYPE_GROUP, - gidstr, gr, sizeof(struct winbindd_gr))) - return False; - - /* Fetch extra data */ - - snprintf(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; - - /* 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; + return NT_STATUS_OK; } -/* Flush cache data - easiest to just reopen the tdb */ - -void winbindd_flush_cache(void) -{ - tdb_close(cache_tdb); - winbindd_cache_init(); -} +/* the ADS backend methods are exposed via this structure */ +struct winbindd_methods cache_methods = { + query_user_list, + enum_dom_groups, + name_to_sid, + sid_to_name, + query_user, + lookup_usergroups, + lookup_groupmem, + sequence_number +}; -/* Print cache status information */ -void winbindd_cache_status(void) -{ -} -- cgit