diff options
Diffstat (limited to 'source/nsswitch/winbindd_cache.c')
-rw-r--r-- | source/nsswitch/winbindd_cache.c | 1142 |
1 files changed, 679 insertions, 463 deletions
diff --git a/source/nsswitch/winbindd_cache.c b/source/nsswitch/winbindd_cache.c index 8ad5bc2e7d8..3a76ba97fae 100644 --- a/source/nsswitch/winbindd_cache.c +++ b/source/nsswitch/winbindd_cache.c @@ -1,10 +1,9 @@ /* - 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 @@ -23,642 +22,859 @@ #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 */ +struct cache_entry { + NTSTATUS status; + uint32 sequence_number; + uint8 *data; + uint32 len, ofs; +}; - 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")); -} +#define WINBINDD_MAX_CACHE_SIZE (50*1024*1024) -/* find the sequence number for a domain */ +static struct winbind_cache *wcache; -static uint32 domain_sequence_number(struct winbindd_domain *domain) +/* flush the cache */ +void wcache_flush_cache(void) { - TALLOC_CTX *mem_ctx; - CLI_POLICY_HND *hnd; - SAM_UNK_CTR ctr; - uint16 switch_value = 2; - NTSTATUS result; - uint32 seqnum = DOM_SEQUENCE_NONE; - POLICY_HND dom_pol; - BOOL got_dom_pol = False; - uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED; + extern BOOL opt_nocache; - if (!(mem_ctx = talloc_init())) - return DOM_SEQUENCE_NONE; - - /* Get sam handle */ - - if (!(hnd = cm_get_sam_handle(domain->name))) - goto done; - - /* Get domain handle */ + if (!wcache) return; + if (wcache->tdb) { + tdb_close(wcache->tdb); + wcache->tdb = NULL; + } + if (opt_nocache) return; - result = cli_samr_open_domain(hnd->cli, mem_ctx, &hnd->pol, - des_access, &domain->sid, &dom_pol); + wcache->tdb = tdb_open_log(lock_path("winbindd_cache.tdb"), 5000, + TDB_DEFAULT, O_RDWR | O_CREAT | O_TRUNC, 0600); - if (!NT_STATUS_IS_OK(result)) - goto done; + if (!wcache->tdb) { + DEBUG(0,("Failed to open winbindd_cache.tdb!\n")); + } +} - got_dom_pol = True; +void winbindd_check_cache_size(time_t t) +{ + static time_t last_check_time; + struct stat st; - /* Query domain info */ + if (last_check_time == (time_t)0) + last_check_time = t; - result = cli_samr_query_dom_info(hnd->cli, mem_ctx, &dom_pol, - switch_value, &ctr); + if (t - last_check_time < 60 && t - last_check_time > 0) + return; - if (NT_STATUS_IS_OK(result)) { - seqnum = ctr.info.inf2.seq_num; - DEBUG(10,("domain_sequence_number: for domain %s is %u\n", domain->name, (unsigned)seqnum )); - } else { - DEBUG(10,("domain_sequence_number: failed to get sequence number (%u) for domain %s\n", - (unsigned)seqnum, domain->name )); + if (wcache == NULL || wcache->tdb == NULL) { + DEBUG(0, ("Unable to check size of tdb cache - cache not open !\n")); + return; } - done: - - if (got_dom_pol) - cli_samr_close(hnd->cli, mem_ctx, &dom_pol); - - talloc_destroy(mem_ctx); + if (fstat(wcache->tdb->fd, &st) == -1) { + DEBUG(0, ("Unable to check size of tdb cache %s!\n", strerror(errno) )); + return; + } - return seqnum; + 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 domain sequence number, possibly re-fetching */ - -static uint32 cached_sequence_number(struct winbindd_domain *domain) +/* get the winbind_cache structure */ +static struct winbind_cache *get_cache(struct winbindd_domain *domain) { - 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); + extern struct winbindd_methods msrpc_methods; + struct winbind_cache *ret = wcache; - if (!dbuf.dptr || dbuf.dsize != sizeof(rec)) - goto refetch; - - 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 (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; } - refetch: - rec.seq_num = domain_sequence_number(domain); - rec.mod_time = t; + wcache = ret; + wcache_flush_cache(); - tdb_store_by_string(cache_tdb, keystr, &rec, sizeof(rec)); + return ret; +} - return rec.seq_num; +/* + free a centry structure +*/ +static void centry_free(struct cache_entry *centry) +{ + if (!centry) return; + SAFE_FREE(centry->data); + free(centry); } -/* Check whether a seq_num for a cached item has expired */ -static BOOL cache_domain_expired(struct winbindd_domain *domain, - uint32 seq_num) + +/* + pull a uint32 from a cache entry +*/ +static uint32 centry_uint32(struct cache_entry *centry) { - 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; + 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(struct winbindd_domain *domain, - char *cache_type, char *subkey) +/* + pull a uint8 from a cache entry +*/ +static uint8 centry_uint8(struct cache_entry *centry) { - fstring keystr; - - snprintf(keystr, sizeof(keystr),"CACHESEQ %s/%s/%s", - domain->name, cache_type, subkey?subkey:""); - - tdb_store_int(cache_tdb, keystr, cached_sequence_number(domain)); + 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(struct winbindd_domain *domain, - 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; + uint32 len; + char *ret; - snprintf(keystr, sizeof(keystr), "CACHESEQ %s/%s/%s", - domain->name, cache_type, subkey ? subkey : ""); + len = centry_uint8(centry); - seq_num = (uint32)tdb_fetch_int(cache_tdb, keystr); + if (len == 0xFF) { + /* a deliberate NULL string */ + return NULL; + } - DEBUG(3,("%s is %u\n", keystr, (unsigned)seq_num)); + 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, 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 (lp_winbind_cache_time() == 0) - return; + if (!wcache->tdb) return False; + return (domain->sequence_number == DOM_SEQUENCE_NONE); +} - /* Error check */ - if (!sam_entries || buflen == 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; - /* Store data as a mega-huge chunk in the tdb */ + time_diff = time(NULL) - domain->last_seq_check; - snprintf(keystr, sizeof(keystr), "%s CACHE DATA/%s", cache_type, - domain->name); + /* see if we have to refetch the domain sequence number */ + if (!force && (time_diff < lp_winbind_cache_time())) { + return; + } - tdb_store_by_string(cache_tdb, keystr, sam_entries, buflen); + status = wcache->backend->sequence_number(domain, &domain->sequence_number); - /* Stamp cache with current seq number */ + if (!NT_STATUS_IS_OK(status)) { + domain->sequence_number = DOM_SEQUENCE_NONE; + } - set_cache_sequence_number(domain, cache_type, NULL); + domain->last_seq_check = time(NULL); } -/* 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)); + /* 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; + } - store_cache(domain, CACHE_TYPE_USER, sam_entries, - num_sam_entries * sizeof(struct getpwent_user)); -} + /* 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; + } -/* Fill the group cache with supplied data */ + /* it's expired */ + return True; +} -void winbindd_store_group_cache(struct winbindd_domain *domain, - 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, ...) { - DEBUG(0, ("storing group cache %s/%d entries\n", domain->name, - num_sam_entries)); + va_list ap; + char *kstr; + TDB_DATA data; + struct cache_entry *centry; + TDB_DATA key; - store_cache(domain, CACHE_TYPE_GROUP, sam_entries, - num_sam_entries * sizeof(struct acct_info)); -} + refresh_sequence_number(domain, False); -static void store_cache_entry(struct winbindd_domain *domain, char *cache_type, - char *name, void *buf, int len) -{ - fstring keystr; + 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; + } - /* Create key for store */ + centry = smb_xmalloc(sizeof(*centry)); + centry->data = data.dptr; + centry->len = data.dsize; + centry->ofs = 0; - snprintf(keystr, sizeof(keystr), "%s/%s/%s", cache_type, - domain->name, name); + 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 it */ + if (centry_expired(domain, centry)) { + centry_free(centry); + return NULL; + } - tdb_store_by_string(cache_tdb, keystr, buf, len); + return centry; } -/* Fill a name cache entry */ - -void winbindd_store_name_cache_entry(struct winbindd_domain *domain, - char *sid, struct winbindd_name *name) +/* + 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_NAME, sid, name, - sizeof(struct winbindd_name)); - - set_cache_sequence_number(domain, CACHE_TYPE_NAME, sid); + 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 SID cache entry */ - -void winbindd_store_sid_cache_entry(struct winbindd_domain *domain, - char *name, struct winbindd_sid *sid) +/* + 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_SID, name, sid, - sizeof(struct winbindd_sid)); - - set_cache_sequence_number(domain, CACHE_TYPE_SID, name); + centry_expand(centry, 4); + SIVAL(centry->data, centry->ofs, v); + centry->ofs += 4; } -/* 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 uint8 into a centry +*/ +static void centry_put_uint8(struct cache_entry *centry, uint8 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, 1); + SCVAL(centry->data, centry->ofs, v); + centry->ofs += 1; } -/* 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; + int len; - if (lp_winbind_cache_time() == 0) + if (!s) { + /* null strings are marked as len 0xFFFF */ + centry_put_uint8(centry, 0xFF); 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); + 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 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); - - 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; + TDB_DATA key, data; - 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 */ + key.dptr = kstr; + key.dsize = strlen(kstr); + data.dptr = centry->data; + data.dsize = centry->ofs; - store_cache_entry(domain, CACHE_TYPE_GROUP, gidstr, gr, - sizeof(struct winbindd_gr)); + tdb_store(wcache->tdb, key, data, TDB_REPLACE); + free(kstr); +} - /* Fill extra data */ +/* 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; +} - snprintf(keystr, sizeof(keystr), "%s/%s/%s DATA", CACHE_TYPE_GROUP, - domain->name, gidstr); +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); +} - tdb_store_by_string(cache_tdb, keystr, extra_data, extra_data_len); +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; - set_cache_sequence_number(domain, CACHE_TYPE_GROUP, gidstr); + 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); } -/* Fetch some cached user or group data */ -static BOOL fetch_cache(struct winbindd_domain *domain, char *cache_type, - void **sam_entries, int *buflen) +static void wcache_save_user(struct winbindd_domain *domain, NTSTATUS status, WINBIND_USERINFO *info) { - TDB_DATA data; - fstring keystr; + 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); +} - if (lp_winbind_cache_time() == 0) - return False; - /* Parameter check */ +/* 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 (!sam_entries || !buflen) - return False; + if (!cache->tdb) goto do_query; - /* Check cache data is current */ + centry = wcache_fetch(cache, domain, "UL/%s", domain->name); + 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: + *num_entries = 0; + *info = NULL; - /* 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; -} + if (wcache_server_down(domain)) { + return NT_STATUS_SERVER_DISABLED; + } -/* Return cached entries for a domain. Return false if there are no cached - entries, or the cached information has expired for the domain. */ + 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; +} -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 *num_entries, + struct acct_info **info) { - BOOL result; - int buflen; + struct winbind_cache *cache = get_cache(domain); + struct cache_entry *centry = NULL; + NTSTATUS status; + int i; - result = fetch_cache(domain, CACHE_TYPE_USER, - (void **)sam_entries, &buflen); + if (!cache->tdb) goto do_query; - *num_entries = buflen / sizeof(struct getpwent_user); + centry = wcache_fetch(cache, domain, "GL/%s", domain->name); + if (!centry) goto do_query; - DEBUG(3, ("fetched %d cache entries for %s\n", *num_entries, - domain->name)); + *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); + } - return result; -} +do_cached: + status = centry->status; + centry_free(centry); + 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. */ +do_query: + *num_entries = 0; + *info = NULL; -BOOL winbindd_fetch_group_cache(struct winbindd_domain *domain, - struct acct_info **sam_entries, - int *num_entries) -{ - BOOL result; - int buflen; + if (wcache_server_down(domain)) { + return NT_STATUS_SERVER_DISABLED; + } - result = fetch_cache(domain, CACHE_TYPE_GROUP, - (void **)sam_entries, &buflen); + 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; +} - *num_entries = buflen / sizeof(struct acct_info); - DEBUG(3, ("fetched %d cache entries for %s\n", *num_entries, - domain->name)); +/* 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; - return result; -} + if (!cache->tdb) goto do_query; -static BOOL fetch_cache_entry(struct winbindd_domain *domain, - char *cache_type, char *name, void *buf, int len) -{ - TDB_DATA data; - fstring keystr; - - /* Create key for lookup */ + 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); - snprintf(keystr, sizeof(keystr), "%s/%s/%s", cache_type, domain->name, name); - - /* Look up cache entry */ + status = centry->status; + centry_free(centry); + return status; - data = tdb_fetch_by_string(cache_tdb, keystr); +do_query: + ZERO_STRUCTP(sid); - if (!data.dptr) - return False; - - /* Copy found entry into buffer */ + if (wcache_server_down(domain)) { + return NT_STATUS_SERVER_DISABLED; + } + status = cache->backend->name_to_sid(domain, name, sid, type); - memcpy((char *)buf, data.dptr, len < data.dsize ? len : data.dsize); - SAFE_FREE(data.dptr); + /* and save it */ + wcache_save_name_to_sid(domain, status, name, sid, *type); - return True; + return status; } -/* Fetch an individual SID cache entry */ - -BOOL winbindd_fetch_sid_cache_entry(struct winbindd_domain *domain, - char *name, struct winbindd_sid *sid) +/* 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) { - uint32 seq_num; - - if (lp_winbind_cache_time() == 0) - return False; - - seq_num = get_cache_sequence_number(domain, CACHE_TYPE_SID, name); + struct winbind_cache *cache = get_cache(domain); + struct cache_entry *centry = NULL; + NTSTATUS status; + uint32 rid = 0; - if (cache_domain_expired(domain, seq_num)) - return False; - - return fetch_cache_entry(domain, CACHE_TYPE_SID, name, sid, - sizeof(struct winbindd_sid)); -} + sid_peek_rid(sid, &rid); -/* Fetch an individual name cache entry */ + if (!cache->tdb) goto do_query; -BOOL winbindd_fetch_name_cache_entry(struct winbindd_domain *domain, - char *sid, struct winbindd_name *name) -{ - uint32 seq_num; + 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; - if (lp_winbind_cache_time() == 0) - return False; +do_query: + *name = NULL; - seq_num = get_cache_sequence_number(domain, CACHE_TYPE_NAME, sid); + if (wcache_server_down(domain)) { + return NT_STATUS_SERVER_DISABLED; + } + status = cache->backend->sid_to_name(domain, mem_ctx, sid, name, type); - if (cache_domain_expired(domain, seq_num)) - return False; + /* and save it */ + refresh_sequence_number(domain, True); + wcache_save_sid_to_name(domain, status, sid, *name, *type, rid); - return fetch_cache_entry(domain, CACHE_TYPE_NAME, sid, name, - sizeof(struct winbindd_name)); + 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; + struct winbind_cache *cache = get_cache(domain); + struct cache_entry *centry = NULL; + NTSTATUS status; - if (lp_winbind_cache_time() == 0) - return False; + if (!cache->tdb) goto do_query; - seq_num = get_cache_sequence_number(domain, CACHE_TYPE_USER, user); + centry = wcache_fetch(cache, domain, "U/%s/%x", domain->name, user_rid); + if (!centry) goto do_query; - if (cache_domain_expired(domain, seq_num)) - return False; + 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; - return fetch_cache_entry(domain, CACHE_TYPE_USER, user, pw, - sizeof(struct winbindd_pw)); -} - -/* Fetch an individual uid cache entry */ +do_query: + ZERO_STRUCTP(info); -BOOL winbindd_fetch_uid_cache_entry(struct winbindd_domain *domain, uid_t uid, - struct winbindd_pw *pw) -{ - fstring uidstr; - uint32 seq_num; - - if (lp_winbind_cache_time() == 0) - return False; - - snprintf(uidstr, sizeof(uidstr), "#%u", (unsigned)uid); + if (wcache_server_down(domain)) { + return NT_STATUS_SERVER_DISABLED; + } + + status = cache->backend->query_user(domain, mem_ctx, user_rid, info); - seq_num = get_cache_sequence_number(domain, CACHE_TYPE_USER, uidstr); + /* and save it */ + refresh_sequence_number(domain, True); + wcache_save_user(domain, status, info); - if (cache_domain_expired(domain, seq_num)) - return False; - - return fetch_cache_entry(domain, CACHE_TYPE_USER, uidstr, pw, - sizeof(struct winbindd_pw)); + 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) +/* 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) { - 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, "UG/%s/%x", domain->name, user_rid); + if (!centry) goto do_query; - if (!fetch_cache_entry(domain, CACHE_TYPE_GROUP, group, gr, sizeof(struct winbindd_gr))) - return False; + *num_groups = centry_uint32(centry); - /* Fetch extra data */ - - snprintf(keystr, sizeof(keystr), "%s/%s/%s DATA", CACHE_TYPE_GROUP, - domain->name, group); - - data = tdb_fetch_by_string(cache_tdb, keystr); + if (*num_groups == 0) goto do_cached; - if (!data.dptr) - 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); + } - /* Extra data freed when data has been sent */ +do_cached: + status = centry->status; + centry_free(centry); + return status; - if (extra_data) - *extra_data = data.dptr; +do_query: + (*num_groups) = 0; + (*user_gids) = NULL; - if (extra_data_len) - *extra_data_len = data.dsize; - - return True; + 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; } -/* 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) +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; - fstring gidstr; - uint32 seq_num; + struct winbind_cache *cache = get_cache(domain); + struct cache_entry *centry = NULL; + NTSTATUS status; + int i; - snprintf(gidstr, sizeof(gidstr), "#%u", (unsigned)gid); - - if (lp_winbind_cache_time() == 0) - return False; + if (!cache->tdb) goto do_query; - seq_num = get_cache_sequence_number(domain, CACHE_TYPE_GROUP, gidstr); + centry = wcache_fetch(cache, domain, "GM/%s/%x", domain->name, group_rid); + if (!centry) goto do_query; - if (cache_domain_expired(domain, seq_num)) - return False; - - /* Fetch group data */ + *num_names = centry_uint32(centry); + + if (*num_names == 0) goto do_cached; - if (!fetch_cache_entry(domain, CACHE_TYPE_GROUP, - gidstr, gr, sizeof(struct winbindd_gr))) - 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)); - /* Fetch extra data */ + if (! (*rid_mem) || ! (*names) || ! (*name_types)) { + smb_panic("lookup_groupmem out of memory"); + } - snprintf(keystr, sizeof(keystr), "%s/%s/%s DATA", CACHE_TYPE_GROUP, - domain->name, gidstr); + 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); + } - data = tdb_fetch_by_string(cache_tdb, keystr); +do_cached: + status = centry->status; + centry_free(centry); + return status; - if (!data.dptr) - return False; +do_query: + (*num_names) = 0; + (*rid_mem) = NULL; + (*names) = NULL; + (*name_types) = NULL; + - /* Extra data freed when data has been sent */ + 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; +} - if (extra_data) - *extra_data = data.dptr; +/* find the sequence number for a domain */ +static NTSTATUS sequence_number(struct winbindd_domain *domain, uint32 *seq) +{ + refresh_sequence_number(domain, False); - if (extra_data_len) - *extra_data_len = data.dsize; + *seq = domain->sequence_number; - return True; + return NT_STATUS_OK; } -/* Flush cache data - easiest to just reopen the tdb */ - -void winbindd_flush_cache(void) +/* enumerate trusted domains */ +static NTSTATUS trusted_domains(struct winbindd_domain *domain, + TALLOC_CTX *mem_ctx, + uint32 *num_domains, + char ***names, + DOM_SID **dom_sids) { - tdb_close(cache_tdb); - winbindd_cache_init(); -} + struct winbind_cache *cache = get_cache(domain); -/* Print cache status information */ + /* we don't cache this call */ + return cache->backend->trusted_domains(domain, mem_ctx, num_domains, + names, dom_sids); +} -void winbindd_cache_status(void) +/* 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 +}; |