1/*
2   Unix SMB/CIFS implementation.
3
4   Winbind cache backend functions
5
6   Copyright (C) Andrew Tridgell 2001
7   Copyright (C) Gerald Carter   2003
8   Copyright (C) Volker Lendecke 2005
9
10
11   This program is free software; you can redistribute it and/or modify
12   it under the terms of the GNU General Public License as published by
13   the Free Software Foundation; either version 2 of the License, or
14   (at your option) any later version.
15
16   This program is distributed in the hope that it will be useful,
17   but WITHOUT ANY WARRANTY; without even the implied warranty of
18   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19   GNU General Public License for more details.
20
21   You should have received a copy of the GNU General Public License
22   along with this program; if not, write to the Free Software
23   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
24*/
25
26#include "includes.h"
27#include "winbindd.h"
28
29#undef DBGC_CLASS
30#define DBGC_CLASS DBGC_WINBIND
31
32struct winbind_cache {
33	TDB_CONTEXT *tdb;
34};
35
36struct cache_entry {
37	NTSTATUS status;
38	uint32 sequence_number;
39	uint8 *data;
40	uint32 len, ofs;
41};
42
43#define WINBINDD_MAX_CACHE_SIZE (50*1024*1024)
44
45static struct winbind_cache *wcache;
46
47/* flush the cache */
48void wcache_flush_cache(void)
49{
50	extern BOOL opt_nocache;
51
52	if (!wcache)
53		return;
54	if (wcache->tdb) {
55		tdb_close(wcache->tdb);
56		wcache->tdb = NULL;
57	}
58	if (opt_nocache)
59		return;
60
61	wcache->tdb = tdb_open_log(lock_path("winbindd_cache.tdb"), 5000,
62				   TDB_CLEAR_IF_FIRST, O_RDWR|O_CREAT, 0600);
63
64	if (!wcache->tdb) {
65		DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
66	}
67	DEBUG(10,("wcache_flush_cache success\n"));
68}
69
70void winbindd_check_cache_size(time_t t)
71{
72	static time_t last_check_time;
73	struct stat st;
74
75	if (last_check_time == (time_t)0)
76		last_check_time = t;
77
78	if (t - last_check_time < 60 && t - last_check_time > 0)
79		return;
80
81	if (wcache == NULL || wcache->tdb == NULL) {
82		DEBUG(0, ("Unable to check size of tdb cache - cache not open !\n"));
83		return;
84	}
85
86	if (fstat(wcache->tdb->fd, &st) == -1) {
87		DEBUG(0, ("Unable to check size of tdb cache %s!\n", strerror(errno) ));
88		return;
89	}
90
91	if (st.st_size > WINBINDD_MAX_CACHE_SIZE) {
92		DEBUG(10,("flushing cache due to size (%lu) > (%lu)\n",
93			(unsigned long)st.st_size,
94			(unsigned long)WINBINDD_MAX_CACHE_SIZE));
95		wcache_flush_cache();
96	}
97}
98
99/* get the winbind_cache structure */
100static struct winbind_cache *get_cache(struct winbindd_domain *domain)
101{
102	struct winbind_cache *ret = wcache;
103
104	if (!domain->backend) {
105		extern struct winbindd_methods msrpc_methods;
106		switch (lp_security()) {
107#ifdef HAVE_ADS
108		case SEC_ADS: {
109			extern struct winbindd_methods ads_methods;
110			/* always obey the lp_security parameter for our domain */
111			if (domain->primary) {
112				domain->backend = &ads_methods;
113				break;
114			}
115
116			/* only use ADS for native modes at the momment.
117			   The problem is the correct detection of mixed
118			   mode domains from NT4 BDC's    --jerry */
119
120			if ( domain->native_mode ) {
121				DEBUG(5,("get_cache: Setting ADS methods for domain %s\n",
122					domain->name));
123				domain->backend = &ads_methods;
124				break;
125			}
126
127			/* fall through */
128		}
129#endif
130		default:
131			DEBUG(5,("get_cache: Setting MS-RPC methods for domain %s\n",
132				domain->name));
133			domain->backend = &msrpc_methods;
134		}
135	}
136
137	if (ret)
138		return ret;
139
140	ret = SMB_XMALLOC_P(struct winbind_cache);
141	ZERO_STRUCTP(ret);
142
143	wcache = ret;
144	wcache_flush_cache();
145
146	return ret;
147}
148
149/*
150  free a centry structure
151*/
152static void centry_free(struct cache_entry *centry)
153{
154	if (!centry)
155		return;
156	SAFE_FREE(centry->data);
157	free(centry);
158}
159
160/*
161  pull a uint32 from a cache entry
162*/
163static uint32 centry_uint32(struct cache_entry *centry)
164{
165	uint32 ret;
166	if (centry->len - centry->ofs < 4) {
167		DEBUG(0,("centry corruption? needed 4 bytes, have %d\n",
168			 centry->len - centry->ofs));
169		smb_panic("centry_uint32");
170	}
171	ret = IVAL(centry->data, centry->ofs);
172	centry->ofs += 4;
173	return ret;
174}
175
176/*
177  pull a uint8 from a cache entry
178*/
179static uint8 centry_uint8(struct cache_entry *centry)
180{
181	uint8 ret;
182	if (centry->len - centry->ofs < 1) {
183		DEBUG(0,("centry corruption? needed 1 bytes, have %d\n",
184			 centry->len - centry->ofs));
185		smb_panic("centry_uint32");
186	}
187	ret = CVAL(centry->data, centry->ofs);
188	centry->ofs += 1;
189	return ret;
190}
191
192/* pull a string from a cache entry, using the supplied
193   talloc context
194*/
195static char *centry_string(struct cache_entry *centry, TALLOC_CTX *mem_ctx)
196{
197	uint32 len;
198	char *ret;
199
200	len = centry_uint8(centry);
201
202	if (len == 0xFF) {
203		/* a deliberate NULL string */
204		return NULL;
205	}
206
207	if (centry->len - centry->ofs < len) {
208		DEBUG(0,("centry corruption? needed %d bytes, have %d\n",
209			 len, centry->len - centry->ofs));
210		smb_panic("centry_string");
211	}
212
213	ret = TALLOC(mem_ctx, len+1);
214	if (!ret) {
215		smb_panic("centry_string out of memory\n");
216	}
217	memcpy(ret,centry->data + centry->ofs, len);
218	ret[len] = 0;
219	centry->ofs += len;
220	return ret;
221}
222
223/* pull a string from a cache entry, using the supplied
224   talloc context
225*/
226static DOM_SID *centry_sid(struct cache_entry *centry, TALLOC_CTX *mem_ctx)
227{
228	DOM_SID *sid;
229	char *sid_string;
230
231	sid = TALLOC_P(mem_ctx, DOM_SID);
232	if (!sid)
233		return NULL;
234
235	sid_string = centry_string(centry, mem_ctx);
236	if (!string_to_sid(sid, sid_string)) {
237		return NULL;
238	}
239	return sid;
240}
241
242/* the server is considered down if it can't give us a sequence number */
243static BOOL wcache_server_down(struct winbindd_domain *domain)
244{
245	BOOL ret;
246
247	if (!wcache->tdb)
248		return False;
249
250	ret = (domain->sequence_number == DOM_SEQUENCE_NONE);
251
252	if (ret)
253		DEBUG(10,("wcache_server_down: server for Domain %s down\n",
254			domain->name ));
255	return ret;
256}
257
258static NTSTATUS fetch_cache_seqnum( struct winbindd_domain *domain, time_t now )
259{
260	TDB_DATA data;
261	fstring key;
262	uint32 time_diff;
263
264	if (!wcache->tdb) {
265		DEBUG(10,("fetch_cache_seqnum: tdb == NULL\n"));
266		return NT_STATUS_UNSUCCESSFUL;
267	}
268
269	fstr_sprintf( key, "SEQNUM/%s", domain->name );
270
271	data = tdb_fetch_bystring( wcache->tdb, key );
272	if ( !data.dptr || data.dsize!=8 ) {
273		DEBUG(10,("fetch_cache_seqnum: invalid data size key [%s]\n", key ));
274		return NT_STATUS_UNSUCCESSFUL;
275	}
276
277	domain->sequence_number = IVAL(data.dptr, 0);
278	domain->last_seq_check  = IVAL(data.dptr, 4);
279
280	SAFE_FREE(data.dptr);
281
282	/* have we expired? */
283
284	time_diff = now - domain->last_seq_check;
285	if ( time_diff > lp_winbind_cache_time() ) {
286		DEBUG(10,("fetch_cache_seqnum: timeout [%s][%u @ %u]\n",
287			domain->name, domain->sequence_number,
288			(uint32)domain->last_seq_check));
289		return NT_STATUS_UNSUCCESSFUL;
290	}
291
292	DEBUG(10,("fetch_cache_seqnum: success [%s][%u @ %u]\n",
293		domain->name, domain->sequence_number,
294		(uint32)domain->last_seq_check));
295
296	return NT_STATUS_OK;
297}
298
299static NTSTATUS store_cache_seqnum( struct winbindd_domain *domain )
300{
301	TDB_DATA data, key;
302	fstring key_str;
303	char buf[8];
304
305	if (!wcache->tdb) {
306		DEBUG(10,("store_cache_seqnum: tdb == NULL\n"));
307		return NT_STATUS_UNSUCCESSFUL;
308	}
309
310	fstr_sprintf( key_str, "SEQNUM/%s", domain->name );
311	key.dptr = key_str;
312	key.dsize = strlen(key_str)+1;
313
314	SIVAL(buf, 0, domain->sequence_number);
315	SIVAL(buf, 4, domain->last_seq_check);
316	data.dptr = buf;
317	data.dsize = 8;
318
319	if ( tdb_store( wcache->tdb, key, data, TDB_REPLACE) == -1 ) {
320		DEBUG(10,("store_cache_seqnum: tdb_store fail key [%s]\n", key_str ));
321		return NT_STATUS_UNSUCCESSFUL;
322	}
323
324	DEBUG(10,("store_cache_seqnum: success [%s][%u @ %u]\n",
325		domain->name, domain->sequence_number,
326		(uint32)domain->last_seq_check));
327
328	return NT_STATUS_OK;
329}
330
331/*
332  refresh the domain sequence number. If force is True
333  then always refresh it, no matter how recently we fetched it
334*/
335
336static void refresh_sequence_number(struct winbindd_domain *domain, BOOL force)
337{
338	NTSTATUS status;
339	unsigned time_diff;
340	time_t t = time(NULL);
341	unsigned cache_time = lp_winbind_cache_time();
342
343	get_cache( domain );
344
345#if 0	/* JERRY -- disable as the default cache time is now 5 minutes */
346	/* trying to reconnect is expensive, don't do it too often */
347	if (domain->sequence_number == DOM_SEQUENCE_NONE) {
348		cache_time *= 8;
349	}
350#endif
351
352	time_diff = t - domain->last_seq_check;
353
354	/* see if we have to refetch the domain sequence number */
355	if (!force && (time_diff < cache_time)) {
356		DEBUG(10, ("refresh_sequence_number: %s time ok\n", domain->name));
357		goto done;
358	}
359
360	/* try to get the sequence number from the tdb cache first */
361	/* this will update the timestamp as well */
362
363	status = fetch_cache_seqnum( domain, t );
364	if ( NT_STATUS_IS_OK(status) )
365		goto done;
366
367	/* important! make sure that we know if this is a native
368	   mode domain or not */
369
370	if ( !domain->initialized )
371		set_dc_type_and_flags( domain );
372
373	status = domain->backend->sequence_number(domain, &domain->sequence_number);
374
375	if (!NT_STATUS_IS_OK(status)) {
376		domain->sequence_number = DOM_SEQUENCE_NONE;
377	}
378
379	domain->last_status = status;
380	domain->last_seq_check = time(NULL);
381
382	/* save the new sequence number ni the cache */
383	store_cache_seqnum( domain );
384
385done:
386	DEBUG(10, ("refresh_sequence_number: %s seq number is now %d\n",
387		   domain->name, domain->sequence_number));
388
389	return;
390}
391
392/*
393  decide if a cache entry has expired
394*/
395static BOOL centry_expired(struct winbindd_domain *domain, const char *keystr, struct cache_entry *centry)
396{
397	/* if the server is OK and our cache entry came from when it was down then
398	   the entry is invalid */
399	if (domain->sequence_number != DOM_SEQUENCE_NONE &&
400	    centry->sequence_number == DOM_SEQUENCE_NONE) {
401		DEBUG(10,("centry_expired: Key %s for domain %s invalid sequence.\n",
402			keystr, domain->name ));
403		return True;
404	}
405
406	/* if the server is down or the cache entry is not older than the
407	   current sequence number then it is OK */
408	if (wcache_server_down(domain) ||
409	    centry->sequence_number == domain->sequence_number) {
410		DEBUG(10,("centry_expired: Key %s for domain %s is good.\n",
411			keystr, domain->name ));
412		return False;
413	}
414
415	DEBUG(10,("centry_expired: Key %s for domain %s expired\n",
416		keystr, domain->name ));
417
418	/* it's expired */
419	return True;
420}
421
422/*
423  fetch an entry from the cache, with a varargs key. auto-fetch the sequence
424  number and return status
425*/
426static struct cache_entry *wcache_fetch(struct winbind_cache *cache,
427					struct winbindd_domain *domain,
428					const char *format, ...) PRINTF_ATTRIBUTE(3,4);
429static struct cache_entry *wcache_fetch(struct winbind_cache *cache,
430					struct winbindd_domain *domain,
431					const char *format, ...)
432{
433	va_list ap;
434	char *kstr;
435	TDB_DATA data;
436	struct cache_entry *centry;
437	TDB_DATA key;
438
439	refresh_sequence_number(domain, False);
440
441	va_start(ap, format);
442	smb_xvasprintf(&kstr, format, ap);
443	va_end(ap);
444
445	key.dptr = kstr;
446	key.dsize = strlen(kstr);
447	data = tdb_fetch(wcache->tdb, key);
448	if (!data.dptr) {
449		/* a cache miss */
450		free(kstr);
451		return NULL;
452	}
453
454	centry = SMB_XMALLOC_P(struct cache_entry);
455	centry->data = (unsigned char *)data.dptr;
456	centry->len = data.dsize;
457	centry->ofs = 0;
458
459	if (centry->len < 8) {
460		/* huh? corrupt cache? */
461		DEBUG(10,("wcache_fetch: Corrupt cache for key %s domain %s (len < 8) ?\n",
462			kstr, domain->name ));
463		centry_free(centry);
464		free(kstr);
465		return NULL;
466	}
467
468	centry->status = NT_STATUS(centry_uint32(centry));
469	centry->sequence_number = centry_uint32(centry);
470
471	if (centry_expired(domain, kstr, centry)) {
472		extern BOOL opt_dual_daemon;
473
474		DEBUG(10,("wcache_fetch: entry %s expired for domain %s\n",
475			 kstr, domain->name ));
476
477		if (opt_dual_daemon) {
478			extern BOOL background_process;
479			background_process = True;
480			DEBUG(10,("wcache_fetch: background processing expired entry %s for domain %s\n",
481				 kstr, domain->name ));
482		} else {
483			centry_free(centry);
484			free(kstr);
485			return NULL;
486		}
487	}
488
489	DEBUG(10,("wcache_fetch: returning entry %s for domain %s\n",
490		 kstr, domain->name ));
491
492	free(kstr);
493	return centry;
494}
495
496/*
497  make sure we have at least len bytes available in a centry
498*/
499static void centry_expand(struct cache_entry *centry, uint32 len)
500{
501	uint8 *p;
502	if (centry->len - centry->ofs >= len)
503		return;
504	centry->len *= 2;
505	p = SMB_REALLOC(centry->data, centry->len);
506	if (!p) {
507		DEBUG(0,("out of memory: needed %d bytes in centry_expand\n", centry->len));
508		smb_panic("out of memory in centry_expand");
509	}
510	centry->data = p;
511}
512
513/*
514  push a uint32 into a centry
515*/
516static void centry_put_uint32(struct cache_entry *centry, uint32 v)
517{
518	centry_expand(centry, 4);
519	SIVAL(centry->data, centry->ofs, v);
520	centry->ofs += 4;
521}
522
523/*
524  push a uint8 into a centry
525*/
526static void centry_put_uint8(struct cache_entry *centry, uint8 v)
527{
528	centry_expand(centry, 1);
529	SCVAL(centry->data, centry->ofs, v);
530	centry->ofs += 1;
531}
532
533/*
534   push a string into a centry
535 */
536static void centry_put_string(struct cache_entry *centry, const char *s)
537{
538	int len;
539
540	if (!s) {
541		/* null strings are marked as len 0xFFFF */
542		centry_put_uint8(centry, 0xFF);
543		return;
544	}
545
546	len = strlen(s);
547	/* can't handle more than 254 char strings. Truncating is probably best */
548	if (len > 254)
549		len = 254;
550	centry_put_uint8(centry, len);
551	centry_expand(centry, len);
552	memcpy(centry->data + centry->ofs, s, len);
553	centry->ofs += len;
554}
555
556static void centry_put_sid(struct cache_entry *centry, const DOM_SID *sid)
557{
558	fstring sid_string;
559	centry_put_string(centry, sid_to_string(sid_string, sid));
560}
561
562/*
563  start a centry for output. When finished, call centry_end()
564*/
565struct cache_entry *centry_start(struct winbindd_domain *domain, NTSTATUS status)
566{
567	struct cache_entry *centry;
568
569	if (!wcache->tdb)
570		return NULL;
571
572	centry = SMB_XMALLOC_P(struct cache_entry);
573
574	centry->len = 8192; /* reasonable default */
575	centry->data = SMB_XMALLOC_ARRAY(char, centry->len);
576	centry->ofs = 0;
577	centry->sequence_number = domain->sequence_number;
578	centry_put_uint32(centry, NT_STATUS_V(status));
579	centry_put_uint32(centry, centry->sequence_number);
580	return centry;
581}
582
583/*
584  finish a centry and write it to the tdb
585*/
586static void centry_end(struct cache_entry *centry, const char *format, ...) PRINTF_ATTRIBUTE(2,3);
587static void centry_end(struct cache_entry *centry, const char *format, ...)
588{
589	va_list ap;
590	char *kstr;
591	TDB_DATA key, data;
592
593	va_start(ap, format);
594	smb_xvasprintf(&kstr, format, ap);
595	va_end(ap);
596
597	key.dptr = kstr;
598	key.dsize = strlen(kstr);
599	data.dptr = (char *)centry->data;
600	data.dsize = centry->ofs;
601
602	tdb_store(wcache->tdb, key, data, TDB_REPLACE);
603	free(kstr);
604}
605
606static void wcache_save_name_to_sid(struct winbindd_domain *domain,
607				    NTSTATUS status, const char *domain_name,
608				    const char *name, const DOM_SID *sid,
609				    enum SID_NAME_USE type)
610{
611	struct cache_entry *centry;
612	fstring uname;
613
614	centry = centry_start(domain, status);
615	if (!centry)
616		return;
617	centry_put_uint32(centry, type);
618	centry_put_sid(centry, sid);
619	fstrcpy(uname, name);
620	strupper_m(uname);
621	centry_end(centry, "NS/%s/%s", domain_name, uname);
622	DEBUG(10,("wcache_save_name_to_sid: %s -> %s\n", uname,
623		  sid_string_static(sid)));
624	centry_free(centry);
625}
626
627static void wcache_save_sid_to_name(struct winbindd_domain *domain, NTSTATUS status,
628				    const DOM_SID *sid, const char *domain_name, const char *name, enum SID_NAME_USE type)
629{
630	struct cache_entry *centry;
631	fstring sid_string;
632
633	centry = centry_start(domain, status);
634	if (!centry)
635		return;
636	if (NT_STATUS_IS_OK(status)) {
637		centry_put_uint32(centry, type);
638		centry_put_string(centry, domain_name);
639		centry_put_string(centry, name);
640	}
641	centry_end(centry, "SN/%s", sid_to_string(sid_string, sid));
642	DEBUG(10,("wcache_save_sid_to_name: %s -> %s\n", sid_string, name));
643	centry_free(centry);
644}
645
646
647static void wcache_save_user(struct winbindd_domain *domain, NTSTATUS status, WINBIND_USERINFO *info)
648{
649	struct cache_entry *centry;
650	fstring sid_string;
651
652	centry = centry_start(domain, status);
653	if (!centry)
654		return;
655	centry_put_string(centry, info->acct_name);
656	centry_put_string(centry, info->full_name);
657	centry_put_sid(centry, info->user_sid);
658	centry_put_sid(centry, info->group_sid);
659	centry_end(centry, "U/%s", sid_to_string(sid_string, info->user_sid));
660	DEBUG(10,("wcache_save_user: %s (acct_name %s)\n", sid_string, info->acct_name));
661	centry_free(centry);
662}
663
664
665/* Query display info. This is the basic user list fn */
666static NTSTATUS query_user_list(struct winbindd_domain *domain,
667				TALLOC_CTX *mem_ctx,
668				uint32 *num_entries,
669				WINBIND_USERINFO **info)
670{
671	struct winbind_cache *cache = get_cache(domain);
672	struct cache_entry *centry = NULL;
673	NTSTATUS status;
674	unsigned int i, retry;
675
676	if (!cache->tdb)
677		goto do_query;
678
679	centry = wcache_fetch(cache, domain, "UL/%s", domain->name);
680	if (!centry)
681		goto do_query;
682
683	*num_entries = centry_uint32(centry);
684
685	if (*num_entries == 0)
686		goto do_cached;
687
688	(*info) = TALLOC_ARRAY(mem_ctx, WINBIND_USERINFO, *num_entries);
689	if (! (*info))
690		smb_panic("query_user_list out of memory");
691	for (i=0; i<(*num_entries); i++) {
692		(*info)[i].acct_name = centry_string(centry, mem_ctx);
693		(*info)[i].full_name = centry_string(centry, mem_ctx);
694		(*info)[i].user_sid = centry_sid(centry, mem_ctx);
695		(*info)[i].group_sid = centry_sid(centry, mem_ctx);
696	}
697
698do_cached:
699	status = centry->status;
700
701	DEBUG(10,("query_user_list: [Cached] - cached list for domain %s status %s\n",
702		domain->name, get_friendly_nt_error_msg(status) ));
703
704	centry_free(centry);
705	return status;
706
707do_query:
708	*num_entries = 0;
709	*info = NULL;
710
711	/* Return status value returned by seq number check */
712
713	if (!NT_STATUS_IS_OK(domain->last_status))
714		return domain->last_status;
715
716	/* Put the query_user_list() in a retry loop.  There appears to be
717	 * some bug either with Windows 2000 or Samba's handling of large
718	 * rpc replies.  This manifests itself as sudden disconnection
719	 * at a random point in the enumeration of a large (60k) user list.
720	 * The retry loop simply tries the operation again. )-:  It's not
721	 * pretty but an acceptable workaround until we work out what the
722	 * real problem is. */
723
724	retry = 0;
725	do {
726
727		DEBUG(10,("query_user_list: [Cached] - doing backend query for list for domain %s\n",
728			domain->name ));
729
730		status = domain->backend->query_user_list(domain, mem_ctx, num_entries, info);
731		if (!NT_STATUS_IS_OK(status))
732			DEBUG(3, ("query_user_list: returned 0x%08x, retrying\n", NT_STATUS_V(status)));
733			if (NT_STATUS_V(status) == NT_STATUS_V(NT_STATUS_UNSUCCESSFUL)) {
734				DEBUG(3, ("query_user_list: flushing connection cache\n"));
735				winbindd_cm_flush();
736			}
737
738	} while (NT_STATUS_V(status) == NT_STATUS_V(NT_STATUS_UNSUCCESSFUL) &&
739		 (retry++ < 5));
740
741	/* and save it */
742	refresh_sequence_number(domain, False);
743	centry = centry_start(domain, status);
744	if (!centry)
745		goto skip_save;
746	centry_put_uint32(centry, *num_entries);
747	for (i=0; i<(*num_entries); i++) {
748		centry_put_string(centry, (*info)[i].acct_name);
749		centry_put_string(centry, (*info)[i].full_name);
750		centry_put_sid(centry, (*info)[i].user_sid);
751		centry_put_sid(centry, (*info)[i].group_sid);
752		if (domain->backend->consistent) {
753			/* when the backend is consistent we can pre-prime some mappings */
754			wcache_save_name_to_sid(domain, NT_STATUS_OK,
755						domain->name,
756						(*info)[i].acct_name,
757						(*info)[i].user_sid,
758						SID_NAME_USER);
759			wcache_save_sid_to_name(domain, NT_STATUS_OK,
760						(*info)[i].user_sid,
761						domain->name,
762						(*info)[i].acct_name,
763						SID_NAME_USER);
764			wcache_save_user(domain, NT_STATUS_OK, &(*info)[i]);
765		}
766	}
767	centry_end(centry, "UL/%s", domain->name);
768	centry_free(centry);
769
770skip_save:
771	return status;
772}
773
774/* list all domain groups */
775static NTSTATUS enum_dom_groups(struct winbindd_domain *domain,
776				TALLOC_CTX *mem_ctx,
777				uint32 *num_entries,
778				struct acct_info **info)
779{
780	struct winbind_cache *cache = get_cache(domain);
781	struct cache_entry *centry = NULL;
782	NTSTATUS status;
783	unsigned int i;
784
785	if (!cache->tdb)
786		goto do_query;
787
788	centry = wcache_fetch(cache, domain, "GL/%s/domain", domain->name);
789	if (!centry)
790		goto do_query;
791
792	*num_entries = centry_uint32(centry);
793
794	if (*num_entries == 0)
795		goto do_cached;
796
797	(*info) = TALLOC_ARRAY(mem_ctx, struct acct_info, *num_entries);
798	if (! (*info))
799		smb_panic("enum_dom_groups out of memory");
800	for (i=0; i<(*num_entries); i++) {
801		fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx));
802		fstrcpy((*info)[i].acct_desc, centry_string(centry, mem_ctx));
803		(*info)[i].rid = centry_uint32(centry);
804	}
805
806do_cached:
807	status = centry->status;
808
809	DEBUG(10,("enum_dom_groups: [Cached] - cached list for domain %s status %s\n",
810		domain->name, get_friendly_nt_error_msg(status) ));
811
812	centry_free(centry);
813	return status;
814
815do_query:
816	*num_entries = 0;
817	*info = NULL;
818
819	/* Return status value returned by seq number check */
820
821	if (!NT_STATUS_IS_OK(domain->last_status))
822		return domain->last_status;
823
824	DEBUG(10,("enum_dom_groups: [Cached] - doing backend query for list for domain %s\n",
825		domain->name ));
826
827	status = domain->backend->enum_dom_groups(domain, mem_ctx, num_entries, info);
828
829	/* and save it */
830	refresh_sequence_number(domain, False);
831	centry = centry_start(domain, status);
832	if (!centry)
833		goto skip_save;
834	centry_put_uint32(centry, *num_entries);
835	for (i=0; i<(*num_entries); i++) {
836		centry_put_string(centry, (*info)[i].acct_name);
837		centry_put_string(centry, (*info)[i].acct_desc);
838		centry_put_uint32(centry, (*info)[i].rid);
839	}
840	centry_end(centry, "GL/%s/domain", domain->name);
841	centry_free(centry);
842
843skip_save:
844	return status;
845}
846
847/* list all domain groups */
848static NTSTATUS enum_local_groups(struct winbindd_domain *domain,
849				TALLOC_CTX *mem_ctx,
850				uint32 *num_entries,
851				struct acct_info **info)
852{
853	struct winbind_cache *cache = get_cache(domain);
854	struct cache_entry *centry = NULL;
855	NTSTATUS status;
856	unsigned int i;
857
858	if (!cache->tdb)
859		goto do_query;
860
861	centry = wcache_fetch(cache, domain, "GL/%s/local", domain->name);
862	if (!centry)
863		goto do_query;
864
865	*num_entries = centry_uint32(centry);
866
867	if (*num_entries == 0)
868		goto do_cached;
869
870	(*info) = TALLOC_ARRAY(mem_ctx, struct acct_info, *num_entries);
871	if (! (*info))
872		smb_panic("enum_dom_groups out of memory");
873	for (i=0; i<(*num_entries); i++) {
874		fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx));
875		fstrcpy((*info)[i].acct_desc, centry_string(centry, mem_ctx));
876		(*info)[i].rid = centry_uint32(centry);
877	}
878
879do_cached:
880
881	/* If we are returning cached data and the domain controller
882	   is down then we don't know whether the data is up to date
883	   or not.  Return NT_STATUS_MORE_PROCESSING_REQUIRED to
884	   indicate this. */
885
886	if (wcache_server_down(domain)) {
887		DEBUG(10, ("enum_local_groups: returning cached user list and server was down\n"));
888		status = NT_STATUS_MORE_PROCESSING_REQUIRED;
889	} else
890		status = centry->status;
891
892	DEBUG(10,("enum_local_groups: [Cached] - cached list for domain %s status %s\n",
893		domain->name, get_friendly_nt_error_msg(status) ));
894
895	centry_free(centry);
896	return status;
897
898do_query:
899	*num_entries = 0;
900	*info = NULL;
901
902	/* Return status value returned by seq number check */
903
904	if (!NT_STATUS_IS_OK(domain->last_status))
905		return domain->last_status;
906
907	DEBUG(10,("enum_local_groups: [Cached] - doing backend query for list for domain %s\n",
908		domain->name ));
909
910	status = domain->backend->enum_local_groups(domain, mem_ctx, num_entries, info);
911
912	/* and save it */
913	refresh_sequence_number(domain, False);
914	centry = centry_start(domain, status);
915	if (!centry)
916		goto skip_save;
917	centry_put_uint32(centry, *num_entries);
918	for (i=0; i<(*num_entries); i++) {
919		centry_put_string(centry, (*info)[i].acct_name);
920		centry_put_string(centry, (*info)[i].acct_desc);
921		centry_put_uint32(centry, (*info)[i].rid);
922	}
923	centry_end(centry, "GL/%s/local", domain->name);
924	centry_free(centry);
925
926skip_save:
927	return status;
928}
929
930/* convert a single name to a sid in a domain */
931static NTSTATUS name_to_sid(struct winbindd_domain *domain,
932			    TALLOC_CTX *mem_ctx,
933			    const char *domain_name,
934			    const char *name,
935			    DOM_SID *sid,
936			    enum SID_NAME_USE *type)
937{
938	struct winbind_cache *cache = get_cache(domain);
939	struct cache_entry *centry = NULL;
940	NTSTATUS status;
941	fstring uname;
942	DOM_SID *sid2;
943
944	if (!cache->tdb)
945		goto do_query;
946
947	fstrcpy(uname, name);
948	strupper_m(uname);
949	centry = wcache_fetch(cache, domain, "NS/%s/%s", domain_name, uname);
950	if (!centry)
951		goto do_query;
952	*type = (enum SID_NAME_USE)centry_uint32(centry);
953	sid2 = centry_sid(centry, mem_ctx);
954	if (!sid2) {
955		ZERO_STRUCTP(sid);
956	} else {
957		sid_copy(sid, sid2);
958	}
959
960	status = centry->status;
961
962	DEBUG(10,("name_to_sid: [Cached] - cached name for domain %s status %s\n",
963		domain->name, get_friendly_nt_error_msg(status) ));
964
965	centry_free(centry);
966	return status;
967
968do_query:
969	ZERO_STRUCTP(sid);
970
971	/* If the seq number check indicated that there is a problem
972	 * with this DC, then return that status... except for
973	 * access_denied.  This is special because the dc may be in
974	 * "restrict anonymous = 1" mode, in which case it will deny
975	 * most unauthenticated operations, but *will* allow the LSA
976	 * name-to-sid that we try as a fallback. */
977
978	if (!(NT_STATUS_IS_OK(domain->last_status)
979	      || NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)))
980		return domain->last_status;
981
982	DEBUG(10,("name_to_sid: [Cached] - doing backend query for name for domain %s\n",
983		domain->name ));
984
985	status = domain->backend->name_to_sid(domain, mem_ctx, domain_name, name, sid, type);
986
987	/* and save it */
988	wcache_save_name_to_sid(domain, status, domain_name, name, sid, *type);
989
990	/* We can't save the sid to name mapping as we don't know the
991	   correct case of the name without looking it up */
992
993	return status;
994}
995
996/* convert a sid to a user or group name. The sid is guaranteed to be in the domain
997   given */
998static NTSTATUS sid_to_name(struct winbindd_domain *domain,
999			    TALLOC_CTX *mem_ctx,
1000			    const DOM_SID *sid,
1001			    char **domain_name,
1002			    char **name,
1003			    enum SID_NAME_USE *type)
1004{
1005	struct winbind_cache *cache = get_cache(domain);
1006	struct cache_entry *centry = NULL;
1007	NTSTATUS status;
1008	fstring sid_string;
1009
1010	if (!cache->tdb)
1011		goto do_query;
1012
1013	centry = wcache_fetch(cache, domain, "SN/%s", sid_to_string(sid_string, sid));
1014	if (!centry)
1015		goto do_query;
1016	if (NT_STATUS_IS_OK(centry->status)) {
1017		*type = (enum SID_NAME_USE)centry_uint32(centry);
1018		*domain_name = centry_string(centry, mem_ctx);
1019		*name = centry_string(centry, mem_ctx);
1020	}
1021	status = centry->status;
1022
1023	DEBUG(10,("sid_to_name: [Cached] - cached name for domain %s status %s\n",
1024		domain->name, get_friendly_nt_error_msg(status) ));
1025
1026	centry_free(centry);
1027	return status;
1028
1029do_query:
1030	*name = NULL;
1031	*domain_name = NULL;
1032
1033	/* If the seq number check indicated that there is a problem
1034	 * with this DC, then return that status... except for
1035	 * access_denied.  This is special because the dc may be in
1036	 * "restrict anonymous = 1" mode, in which case it will deny
1037	 * most unauthenticated operations, but *will* allow the LSA
1038	 * sid-to-name that we try as a fallback. */
1039
1040	if (!(NT_STATUS_IS_OK(domain->last_status)
1041	      || NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)))
1042		return domain->last_status;
1043
1044	DEBUG(10,("sid_to_name: [Cached] - doing backend query for name for domain %s\n",
1045		domain->name ));
1046
1047	status = domain->backend->sid_to_name(domain, mem_ctx, sid, domain_name, name, type);
1048
1049	/* and save it */
1050	refresh_sequence_number(domain, False);
1051	wcache_save_sid_to_name(domain, status, sid, *domain_name, *name, *type);
1052
1053	/* We can't save the name to sid mapping here, as with sid history a
1054	 * later name2sid would give the wrong sid. */
1055
1056	return status;
1057}
1058
1059
1060/* Lookup user information from a rid */
1061static NTSTATUS query_user(struct winbindd_domain *domain,
1062			   TALLOC_CTX *mem_ctx,
1063			   const DOM_SID *user_sid,
1064			   WINBIND_USERINFO *info)
1065{
1066	struct winbind_cache *cache = get_cache(domain);
1067	struct cache_entry *centry = NULL;
1068	NTSTATUS status;
1069
1070	if (!cache->tdb)
1071		goto do_query;
1072
1073	centry = wcache_fetch(cache, domain, "U/%s", sid_string_static(user_sid));
1074
1075	/* If we have an access denied cache entry and a cached info3 in the
1076           samlogon cache then do a query.  This will force the rpc back end
1077           to return the info3 data. */
1078
1079	if (NT_STATUS_V(domain->last_status) == NT_STATUS_V(NT_STATUS_ACCESS_DENIED) &&
1080	    netsamlogon_cache_have(user_sid)) {
1081		DEBUG(10, ("query_user: cached access denied and have cached info3\n"));
1082		domain->last_status = NT_STATUS_OK;
1083		centry_free(centry);
1084		goto do_query;
1085	}
1086
1087	if (!centry)
1088		goto do_query;
1089
1090	info->acct_name = centry_string(centry, mem_ctx);
1091	info->full_name = centry_string(centry, mem_ctx);
1092	info->user_sid = centry_sid(centry, mem_ctx);
1093	info->group_sid = centry_sid(centry, mem_ctx);
1094	status = centry->status;
1095
1096	DEBUG(10,("query_user: [Cached] - cached info for domain %s status %s\n",
1097		domain->name, get_friendly_nt_error_msg(status) ));
1098
1099	centry_free(centry);
1100	return status;
1101
1102do_query:
1103	ZERO_STRUCTP(info);
1104
1105	/* Return status value returned by seq number check */
1106
1107	if (!NT_STATUS_IS_OK(domain->last_status))
1108		return domain->last_status;
1109
1110	DEBUG(10,("sid_to_name: [Cached] - doing backend query for info for domain %s\n",
1111		domain->name ));
1112
1113	status = domain->backend->query_user(domain, mem_ctx, user_sid, info);
1114
1115	/* and save it */
1116	refresh_sequence_number(domain, False);
1117	wcache_save_user(domain, status, info);
1118
1119	return status;
1120}
1121
1122
1123/* Lookup groups a user is a member of. */
1124static NTSTATUS lookup_usergroups(struct winbindd_domain *domain,
1125				  TALLOC_CTX *mem_ctx,
1126				  const DOM_SID *user_sid,
1127				  uint32 *num_groups, DOM_SID ***user_gids)
1128{
1129	struct winbind_cache *cache = get_cache(domain);
1130	struct cache_entry *centry = NULL;
1131	NTSTATUS status;
1132	unsigned int i;
1133	fstring sid_string;
1134
1135	if (!cache->tdb)
1136		goto do_query;
1137
1138	centry = wcache_fetch(cache, domain, "UG/%s", sid_to_string(sid_string, user_sid));
1139
1140	/* If we have an access denied cache entry and a cached info3 in the
1141           samlogon cache then do a query.  This will force the rpc back end
1142           to return the info3 data. */
1143
1144	if (NT_STATUS_V(domain->last_status) == NT_STATUS_V(NT_STATUS_ACCESS_DENIED) &&
1145	    netsamlogon_cache_have(user_sid)) {
1146		DEBUG(10, ("query_user: cached access denied and have cached info3\n"));
1147		domain->last_status = NT_STATUS_OK;
1148		centry_free(centry);
1149		goto do_query;
1150	}
1151
1152	if (!centry)
1153		goto do_query;
1154
1155	*num_groups = centry_uint32(centry);
1156
1157	if (*num_groups == 0)
1158		goto do_cached;
1159
1160	(*user_gids) = TALLOC_ARRAY(mem_ctx, DOM_SID *, *num_groups);
1161	if (! (*user_gids))
1162		smb_panic("lookup_usergroups out of memory");
1163	for (i=0; i<(*num_groups); i++) {
1164		(*user_gids)[i] = centry_sid(centry, mem_ctx);
1165	}
1166
1167do_cached:
1168	status = centry->status;
1169
1170	DEBUG(10,("lookup_usergroups: [Cached] - cached info for domain %s status %s\n",
1171		domain->name, get_friendly_nt_error_msg(status) ));
1172
1173	centry_free(centry);
1174	return status;
1175
1176do_query:
1177	(*num_groups) = 0;
1178	(*user_gids) = NULL;
1179
1180	/* Return status value returned by seq number check */
1181
1182	if (!NT_STATUS_IS_OK(domain->last_status))
1183		return domain->last_status;
1184
1185	DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info for domain %s\n",
1186		domain->name ));
1187
1188	status = domain->backend->lookup_usergroups(domain, mem_ctx, user_sid, num_groups, user_gids);
1189
1190	/* and save it */
1191	refresh_sequence_number(domain, False);
1192	centry = centry_start(domain, status);
1193	if (!centry)
1194		goto skip_save;
1195	centry_put_uint32(centry, *num_groups);
1196	for (i=0; i<(*num_groups); i++) {
1197		centry_put_sid(centry, (*user_gids)[i]);
1198	}
1199	centry_end(centry, "UG/%s", sid_to_string(sid_string, user_sid));
1200	centry_free(centry);
1201
1202skip_save:
1203	return status;
1204}
1205
1206static NTSTATUS lookup_useraliases(struct winbindd_domain *domain,
1207				   TALLOC_CTX *mem_ctx,
1208				   uint32 num_sids, DOM_SID **sids,
1209				   uint32 *num_aliases, uint32 **alias_rids)
1210{
1211	struct winbind_cache *cache = get_cache(domain);
1212	struct cache_entry *centry = NULL;
1213	NTSTATUS status;
1214	char *sidlist = talloc_strdup(mem_ctx, "");
1215	int i;
1216
1217	if (!cache->tdb)
1218		goto do_query;
1219
1220	if (num_sids == 0) {
1221		*num_aliases = 0;
1222		*alias_rids = NULL;
1223		return NT_STATUS_OK;
1224	}
1225
1226	/* We need to cache indexed by the whole list of SIDs, the aliases
1227	 * resulting might come from any of the SIDs. */
1228
1229	for (i=0; i<num_sids; i++) {
1230		sidlist = talloc_asprintf(mem_ctx, "%s/%s", sidlist,
1231					  sid_string_static(sids[i]));
1232		if (sidlist == NULL)
1233			return NT_STATUS_NO_MEMORY;
1234	}
1235
1236	centry = wcache_fetch(cache, domain, "UA%s", sidlist);
1237
1238	if (!centry)
1239		goto do_query;
1240
1241	*num_aliases = centry_uint32(centry);
1242	*alias_rids = NULL;
1243
1244	(*alias_rids) = TALLOC_ARRAY(mem_ctx, uint32, *num_aliases);
1245
1246	if ((*num_aliases != 0) && ((*alias_rids) == NULL))
1247		return NT_STATUS_NO_MEMORY;
1248
1249	for (i=0; i<(*num_aliases); i++)
1250		(*alias_rids)[i] = centry_uint32(centry);
1251
1252	status = centry->status;
1253
1254	DEBUG(10,("lookup_useraliases: [Cached] - cached info for domain %s "
1255		  "status %s\n", domain->name,
1256		  get_friendly_nt_error_msg(status)));
1257
1258	centry_free(centry);
1259	return status;
1260
1261 do_query:
1262	(*num_aliases) = 0;
1263	(*alias_rids) = NULL;
1264
1265	if (!NT_STATUS_IS_OK(domain->last_status))
1266		return domain->last_status;
1267
1268	DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info "
1269		  "for domain %s\n", domain->name ));
1270
1271	status = domain->backend->lookup_useraliases(domain, mem_ctx,
1272						     num_sids, sids,
1273						     num_aliases, alias_rids);
1274
1275	/* and save it */
1276	refresh_sequence_number(domain, False);
1277	centry = centry_start(domain, status);
1278	if (!centry)
1279		goto skip_save;
1280	centry_put_uint32(centry, *num_aliases);
1281	for (i=0; i<(*num_aliases); i++)
1282		centry_put_uint32(centry, (*alias_rids)[i]);
1283	centry_end(centry, "UA%s", sidlist);
1284	centry_free(centry);
1285
1286 skip_save:
1287	return status;
1288}
1289
1290
1291static NTSTATUS lookup_groupmem(struct winbindd_domain *domain,
1292				TALLOC_CTX *mem_ctx,
1293				const DOM_SID *group_sid, uint32 *num_names,
1294				DOM_SID ***sid_mem, char ***names,
1295				uint32 **name_types)
1296{
1297	struct winbind_cache *cache = get_cache(domain);
1298	struct cache_entry *centry = NULL;
1299	NTSTATUS status;
1300	unsigned int i;
1301	fstring sid_string;
1302
1303	if (!cache->tdb)
1304		goto do_query;
1305
1306	centry = wcache_fetch(cache, domain, "GM/%s", sid_to_string(sid_string, group_sid));
1307	if (!centry)
1308		goto do_query;
1309
1310	*num_names = centry_uint32(centry);
1311
1312	if (*num_names == 0)
1313		goto do_cached;
1314
1315	(*sid_mem) = TALLOC_ARRAY(mem_ctx, DOM_SID *, *num_names);
1316	(*names) = TALLOC_ARRAY(mem_ctx, char *, *num_names);
1317	(*name_types) = TALLOC_ARRAY(mem_ctx, uint32, *num_names);
1318
1319	if (! (*sid_mem) || ! (*names) || ! (*name_types)) {
1320		smb_panic("lookup_groupmem out of memory");
1321	}
1322
1323	for (i=0; i<(*num_names); i++) {
1324		(*sid_mem)[i] = centry_sid(centry, mem_ctx);
1325		(*names)[i] = centry_string(centry, mem_ctx);
1326		(*name_types)[i] = centry_uint32(centry);
1327	}
1328
1329do_cached:
1330	status = centry->status;
1331
1332	DEBUG(10,("lookup_groupmem: [Cached] - cached info for domain %s status %s\n",
1333		domain->name, get_friendly_nt_error_msg(status) ));
1334
1335	centry_free(centry);
1336	return status;
1337
1338do_query:
1339	(*num_names) = 0;
1340	(*sid_mem) = NULL;
1341	(*names) = NULL;
1342	(*name_types) = NULL;
1343
1344	/* Return status value returned by seq number check */
1345
1346	if (!NT_STATUS_IS_OK(domain->last_status))
1347		return domain->last_status;
1348
1349	DEBUG(10,("lookup_groupmem: [Cached] - doing backend query for info for domain %s\n",
1350		domain->name ));
1351
1352	status = domain->backend->lookup_groupmem(domain, mem_ctx, group_sid, num_names,
1353						  sid_mem, names, name_types);
1354
1355	/* and save it */
1356	refresh_sequence_number(domain, False);
1357	centry = centry_start(domain, status);
1358	if (!centry)
1359		goto skip_save;
1360	centry_put_uint32(centry, *num_names);
1361	for (i=0; i<(*num_names); i++) {
1362		centry_put_sid(centry, (*sid_mem)[i]);
1363		centry_put_string(centry, (*names)[i]);
1364		centry_put_uint32(centry, (*name_types)[i]);
1365	}
1366	centry_end(centry, "GM/%s", sid_to_string(sid_string, group_sid));
1367	centry_free(centry);
1368
1369skip_save:
1370	return status;
1371}
1372
1373/* find the sequence number for a domain */
1374static NTSTATUS sequence_number(struct winbindd_domain *domain, uint32 *seq)
1375{
1376	refresh_sequence_number(domain, False);
1377
1378	*seq = domain->sequence_number;
1379
1380	return NT_STATUS_OK;
1381}
1382
1383/* enumerate trusted domains */
1384static NTSTATUS trusted_domains(struct winbindd_domain *domain,
1385				TALLOC_CTX *mem_ctx,
1386				uint32 *num_domains,
1387				char ***names,
1388				char ***alt_names,
1389				DOM_SID **dom_sids)
1390{
1391	get_cache(domain);
1392
1393	DEBUG(10,("trusted_domains: [Cached] - doing backend query for info for domain %s\n",
1394		domain->name ));
1395
1396	/* we don't cache this call */
1397	return domain->backend->trusted_domains(domain, mem_ctx, num_domains,
1398					       names, alt_names, dom_sids);
1399}
1400
1401/* find the domain sid */
1402static NTSTATUS domain_sid(struct winbindd_domain *domain, DOM_SID *sid)
1403{
1404	get_cache(domain);
1405
1406	DEBUG(10,("domain_sid: [Cached] - doing backend query for info for domain %s\n",
1407		domain->name ));
1408
1409	/* we don't cache this call */
1410	return domain->backend->domain_sid(domain, sid);
1411}
1412
1413/* find the alternate names for the domain, if any */
1414static NTSTATUS alternate_name(struct winbindd_domain *domain)
1415{
1416	get_cache(domain);
1417
1418	DEBUG(10,("alternate_name: [Cached] - doing backend query for info for domain %s\n",
1419		domain->name ));
1420
1421	/* we don't cache this call */
1422	return domain->backend->alternate_name(domain);
1423}
1424
1425/* Invalidate cached user and group lists coherently */
1426
1427static int traverse_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
1428		       void *state)
1429{
1430	if (strncmp(kbuf.dptr, "UL/", 3) == 0 ||
1431	    strncmp(kbuf.dptr, "GL/", 3) == 0)
1432		tdb_delete(the_tdb, kbuf);
1433
1434	return 0;
1435}
1436
1437/* Invalidate the getpwnam and getgroups entries for a winbindd domain */
1438
1439void wcache_invalidate_samlogon(struct winbindd_domain *domain,
1440				NET_USER_INFO_3 *info3)
1441{
1442	struct winbind_cache *cache;
1443
1444	if (!domain)
1445		return;
1446
1447	cache = get_cache(domain);
1448	netsamlogon_clear_cached_user(cache->tdb, info3);
1449}
1450
1451void wcache_invalidate_cache(void)
1452{
1453	struct winbindd_domain *domain;
1454
1455	for (domain = domain_list(); domain; domain = domain->next) {
1456		struct winbind_cache *cache = get_cache(domain);
1457
1458		DEBUG(10, ("wcache_invalidate_cache: invalidating cache "
1459			   "entries for %s\n", domain->name));
1460		if (cache)
1461			tdb_traverse(cache->tdb, traverse_fn, NULL);
1462	}
1463}
1464
1465/* the ADS backend methods are exposed via this structure */
1466struct winbindd_methods cache_methods = {
1467	True,
1468	query_user_list,
1469	enum_dom_groups,
1470	enum_local_groups,
1471	name_to_sid,
1472	sid_to_name,
1473	query_user,
1474	lookup_usergroups,
1475	lookup_useraliases,
1476	lookup_groupmem,
1477	sequence_number,
1478	trusted_domains,
1479	domain_sid,
1480	alternate_name
1481};
1482