• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /asuswrt-rt-n18u-9.0.0.4.380.2695/release/src-rt/router/samba-3.0.25b/source/nsswitch/
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   Copyright (C) Guenther Deschner 2005
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
32#define WINBINDD_CACHE_VERSION 1
33#define WINBINDD_CACHE_VERSION_KEYSTR "WINBINDD_CACHE_VERSION"
34
35extern struct winbindd_methods reconnect_methods;
36extern BOOL opt_nocache;
37#ifdef HAVE_ADS
38extern struct winbindd_methods ads_methods;
39#endif
40
41/*
42 * JRA. KEEP THIS LIST UP TO DATE IF YOU ADD CACHE ENTRIES.
43 * Here are the list of entry types that are *not* stored
44 * as form struct cache_entry in the cache.
45 */
46
47static const char *non_centry_keys[] = {
48	"SEQNUM/",
49	"DR/",
50	"DE/",
51	"WINBINDD_OFFLINE",
52	WINBINDD_CACHE_VERSION_KEYSTR,
53	NULL
54};
55
56/************************************************************************
57 Is this key a non-centry type ?
58************************************************************************/
59
60static BOOL is_non_centry_key(TDB_DATA kbuf)
61{
62	int i;
63
64	if (kbuf.dptr == NULL || kbuf.dsize == 0) {
65		return False;
66	}
67	for (i = 0; non_centry_keys[i] != NULL; i++) {
68		size_t namelen = strlen(non_centry_keys[i]);
69		if (kbuf.dsize < namelen) {
70			continue;
71		}
72		if (strncmp(non_centry_keys[i], (const char *)kbuf.dptr, namelen) == 0) {
73			return True;
74		}
75	}
76	return False;
77}
78
79/* Global online/offline state - False when online. winbindd starts up online
80   and sets this to true if the first query fails and there's an entry in
81   the cache tdb telling us to stay offline. */
82
83static BOOL global_winbindd_offline_state;
84
85struct winbind_cache {
86	TDB_CONTEXT *tdb;
87};
88
89struct cache_entry {
90	NTSTATUS status;
91	uint32 sequence_number;
92	uint8 *data;
93	uint32 len, ofs;
94};
95
96#define WINBINDD_MAX_CACHE_SIZE (50*1024*1024)
97
98static struct winbind_cache *wcache;
99
100void winbindd_check_cache_size(time_t t)
101{
102	static time_t last_check_time;
103	struct stat st;
104
105	if (last_check_time == (time_t)0)
106		last_check_time = t;
107
108	if (t - last_check_time < 60 && t - last_check_time > 0)
109		return;
110
111	if (wcache == NULL || wcache->tdb == NULL) {
112		DEBUG(0, ("Unable to check size of tdb cache - cache not open !\n"));
113		return;
114	}
115
116	if (fstat(tdb_fd(wcache->tdb), &st) == -1) {
117		DEBUG(0, ("Unable to check size of tdb cache %s!\n", strerror(errno) ));
118		return;
119	}
120
121	if (st.st_size > WINBINDD_MAX_CACHE_SIZE) {
122		DEBUG(10,("flushing cache due to size (%lu) > (%lu)\n",
123			(unsigned long)st.st_size,
124			(unsigned long)WINBINDD_MAX_CACHE_SIZE));
125		wcache_flush_cache();
126	}
127}
128
129/* get the winbind_cache structure */
130static struct winbind_cache *get_cache(struct winbindd_domain *domain)
131{
132	struct winbind_cache *ret = wcache;
133#ifdef HAVE_ADS
134	struct winbindd_domain *our_domain = domain;
135#endif
136
137	/* We have to know what type of domain we are dealing with first. */
138
139	if ( !domain->initialized ) {
140		init_dc_connection( domain );
141	}
142
143	/*
144	   OK.  listen up becasue I'm only going to say this once.
145	   We have the following scenarios to consider
146	   (a) trusted AD domains on a Samba DC,
147	   (b) trusted AD domains and we are joined to a non-kerberos domain
148	   (c) trusted AD domains and we are joined to a kerberos (AD) domain
149
150	   For (a) we can always contact the trusted domain using krb5
151	   since we have the domain trust account password
152
153	   For (b) we can only use RPC since we have no way of
154	   getting a krb5 ticket in our own domain
155
156	   For (c) we can always use krb5 since we have a kerberos trust
157
158	   --jerry
159	 */
160
161	if (!domain->backend) {
162#ifdef HAVE_ADS
163		/* find our domain first so we can figure out if we
164		   are joined to a kerberized domain */
165
166		if ( !domain->primary )
167			our_domain = find_our_domain();
168
169		if ( (our_domain->active_directory || IS_DC) && domain->active_directory ) {
170			DEBUG(5,("get_cache: Setting ADS methods for domain %s\n", domain->name));
171			domain->backend = &ads_methods;
172		} else {
173#endif	/* HAVE_ADS */
174			DEBUG(5,("get_cache: Setting MS-RPC methods for domain %s\n", domain->name));
175			domain->backend = &reconnect_methods;
176#ifdef HAVE_ADS
177		}
178#endif	/* HAVE_ADS */
179	}
180
181	if (ret)
182		return ret;
183
184	ret = SMB_XMALLOC_P(struct winbind_cache);
185	ZERO_STRUCTP(ret);
186
187	wcache = ret;
188	wcache_flush_cache();
189
190	return ret;
191}
192
193/*
194  free a centry structure
195*/
196static void centry_free(struct cache_entry *centry)
197{
198	if (!centry)
199		return;
200	SAFE_FREE(centry->data);
201	free(centry);
202}
203
204/*
205  pull a uint32 from a cache entry
206*/
207static uint32 centry_uint32(struct cache_entry *centry)
208{
209	uint32 ret;
210	if (centry->len - centry->ofs < 4) {
211		DEBUG(0,("centry corruption? needed 4 bytes, have %d\n",
212			 centry->len - centry->ofs));
213		smb_panic("centry_uint32");
214	}
215	ret = IVAL(centry->data, centry->ofs);
216	centry->ofs += 4;
217	return ret;
218}
219
220/*
221  pull a uint16 from a cache entry
222*/
223static uint16 centry_uint16(struct cache_entry *centry)
224{
225	uint16 ret;
226	if (centry->len - centry->ofs < 2) {
227		DEBUG(0,("centry corruption? needed 2 bytes, have %d\n",
228			 centry->len - centry->ofs));
229		smb_panic("centry_uint16");
230	}
231	ret = CVAL(centry->data, centry->ofs);
232	centry->ofs += 2;
233	return ret;
234}
235
236/*
237  pull a uint8 from a cache entry
238*/
239static uint8 centry_uint8(struct cache_entry *centry)
240{
241	uint8 ret;
242	if (centry->len - centry->ofs < 1) {
243		DEBUG(0,("centry corruption? needed 1 bytes, have %d\n",
244			 centry->len - centry->ofs));
245		smb_panic("centry_uint32");
246	}
247	ret = CVAL(centry->data, centry->ofs);
248	centry->ofs += 1;
249	return ret;
250}
251
252/*
253  pull a NTTIME from a cache entry
254*/
255static NTTIME centry_nttime(struct cache_entry *centry)
256{
257	NTTIME ret;
258	if (centry->len - centry->ofs < 8) {
259		DEBUG(0,("centry corruption? needed 8 bytes, have %d\n",
260			 centry->len - centry->ofs));
261		smb_panic("centry_nttime");
262	}
263	ret = IVAL(centry->data, centry->ofs);
264	centry->ofs += 4;
265	ret += (uint64_t)IVAL(centry->data, centry->ofs) << 32;
266	centry->ofs += 4;
267	return ret;
268}
269
270/*
271  pull a time_t from a cache entry. time_t stored portably as a 64-bit time.
272*/
273static time_t centry_time(struct cache_entry *centry)
274{
275	return (time_t)centry_nttime(centry);
276}
277
278/* pull a string from a cache entry, using the supplied
279   talloc context
280*/
281static char *centry_string(struct cache_entry *centry, TALLOC_CTX *mem_ctx)
282{
283	uint32 len;
284	char *ret;
285
286	len = centry_uint8(centry);
287
288	if (len == 0xFF) {
289		/* a deliberate NULL string */
290		return NULL;
291	}
292
293	if (centry->len - centry->ofs < len) {
294		DEBUG(0,("centry corruption? needed %d bytes, have %d\n",
295			 len, centry->len - centry->ofs));
296		smb_panic("centry_string");
297	}
298
299	ret = TALLOC_ARRAY(mem_ctx, char, len+1);
300	if (!ret) {
301		smb_panic("centry_string out of memory\n");
302	}
303	memcpy(ret,centry->data + centry->ofs, len);
304	ret[len] = 0;
305	centry->ofs += len;
306	return ret;
307}
308
309/* pull a hash16 from a cache entry, using the supplied
310   talloc context
311*/
312static char *centry_hash16(struct cache_entry *centry, TALLOC_CTX *mem_ctx)
313{
314	uint32 len;
315	char *ret;
316
317	len = centry_uint8(centry);
318
319	if (len != 16) {
320		DEBUG(0,("centry corruption? hash len (%u) != 16\n",
321			len ));
322		return NULL;
323	}
324
325	if (centry->len - centry->ofs < 16) {
326		DEBUG(0,("centry corruption? needed 16 bytes, have %d\n",
327			 centry->len - centry->ofs));
328		return NULL;
329	}
330
331	ret = TALLOC_ARRAY(mem_ctx, char, 16);
332	if (!ret) {
333		smb_panic("centry_hash out of memory\n");
334	}
335	memcpy(ret,centry->data + centry->ofs, 16);
336	centry->ofs += 16;
337	return ret;
338}
339
340/* pull a sid from a cache entry, using the supplied
341   talloc context
342*/
343static BOOL centry_sid(struct cache_entry *centry, TALLOC_CTX *mem_ctx, DOM_SID *sid)
344{
345	char *sid_string;
346	sid_string = centry_string(centry, mem_ctx);
347	if ((sid_string == NULL) || (!string_to_sid(sid, sid_string))) {
348		return False;
349	}
350	return True;
351}
352
353/* the server is considered down if it can't give us a sequence number */
354static BOOL wcache_server_down(struct winbindd_domain *domain)
355{
356	BOOL ret;
357
358	if (!wcache->tdb)
359		return False;
360
361	ret = (domain->sequence_number == DOM_SEQUENCE_NONE);
362
363	if (ret)
364		DEBUG(10,("wcache_server_down: server for Domain %s down\n",
365			domain->name ));
366	return ret;
367}
368
369static NTSTATUS fetch_cache_seqnum( struct winbindd_domain *domain, time_t now )
370{
371	TDB_DATA data;
372	fstring key;
373	uint32 time_diff;
374
375	if (!wcache->tdb) {
376		DEBUG(10,("fetch_cache_seqnum: tdb == NULL\n"));
377		return NT_STATUS_UNSUCCESSFUL;
378	}
379
380	fstr_sprintf( key, "SEQNUM/%s", domain->name );
381
382	data = tdb_fetch_bystring( wcache->tdb, key );
383	if ( !data.dptr || data.dsize!=8 ) {
384		DEBUG(10,("fetch_cache_seqnum: invalid data size key [%s]\n", key ));
385		return NT_STATUS_UNSUCCESSFUL;
386	}
387
388	domain->sequence_number = IVAL(data.dptr, 0);
389	domain->last_seq_check  = IVAL(data.dptr, 4);
390
391	SAFE_FREE(data.dptr);
392
393	/* have we expired? */
394
395	time_diff = now - domain->last_seq_check;
396	if ( time_diff > lp_winbind_cache_time() ) {
397		DEBUG(10,("fetch_cache_seqnum: timeout [%s][%u @ %u]\n",
398			domain->name, domain->sequence_number,
399			(uint32)domain->last_seq_check));
400		return NT_STATUS_UNSUCCESSFUL;
401	}
402
403	DEBUG(10,("fetch_cache_seqnum: success [%s][%u @ %u]\n",
404		domain->name, domain->sequence_number,
405		(uint32)domain->last_seq_check));
406
407	return NT_STATUS_OK;
408}
409
410static NTSTATUS store_cache_seqnum( struct winbindd_domain *domain )
411{
412	TDB_DATA data;
413	fstring key_str;
414	char buf[8];
415
416	if (!wcache->tdb) {
417		DEBUG(10,("store_cache_seqnum: tdb == NULL\n"));
418		return NT_STATUS_UNSUCCESSFUL;
419	}
420
421	fstr_sprintf( key_str, "SEQNUM/%s", domain->name );
422
423	SIVAL(buf, 0, domain->sequence_number);
424	SIVAL(buf, 4, domain->last_seq_check);
425	data.dptr = buf;
426	data.dsize = 8;
427
428	if ( tdb_store_bystring( wcache->tdb, key_str, data, TDB_REPLACE) == -1 ) {
429		DEBUG(10,("store_cache_seqnum: tdb_store fail key [%s]\n", key_str ));
430		return NT_STATUS_UNSUCCESSFUL;
431	}
432
433	DEBUG(10,("store_cache_seqnum: success [%s][%u @ %u]\n",
434		domain->name, domain->sequence_number,
435		(uint32)domain->last_seq_check));
436
437	return NT_STATUS_OK;
438}
439
440/*
441  refresh the domain sequence number. If force is True
442  then always refresh it, no matter how recently we fetched it
443*/
444
445static void refresh_sequence_number(struct winbindd_domain *domain, BOOL force)
446{
447	NTSTATUS status;
448	unsigned time_diff;
449	time_t t = time(NULL);
450	unsigned cache_time = lp_winbind_cache_time();
451
452	get_cache( domain );
453
454#if 0	/* JERRY -- disable as the default cache time is now 5 minutes */
455	/* trying to reconnect is expensive, don't do it too often */
456	if (domain->sequence_number == DOM_SEQUENCE_NONE) {
457		cache_time *= 8;
458	}
459#endif
460
461	time_diff = t - domain->last_seq_check;
462
463	/* see if we have to refetch the domain sequence number */
464	if (!force && (time_diff < cache_time)) {
465		DEBUG(10, ("refresh_sequence_number: %s time ok\n", domain->name));
466		goto done;
467	}
468
469	/* try to get the sequence number from the tdb cache first */
470	/* this will update the timestamp as well */
471
472	status = fetch_cache_seqnum( domain, t );
473	if ( NT_STATUS_IS_OK(status) )
474		goto done;
475
476	/* important! make sure that we know if this is a native
477	   mode domain or not */
478
479	status = domain->backend->sequence_number(domain, &domain->sequence_number);
480
481	/* the above call could have set our domain->backend to NULL when
482	 * coming from offline to online mode, make sure to reinitialize the
483	 * backend - Guenther */
484	get_cache( domain );
485
486	if (!NT_STATUS_IS_OK(status)) {
487		DEBUG(10,("refresh_sequence_number: failed with %s\n", nt_errstr(status)));
488		domain->sequence_number = DOM_SEQUENCE_NONE;
489	}
490
491	domain->last_status = status;
492	domain->last_seq_check = time(NULL);
493
494	/* save the new sequence number ni the cache */
495	store_cache_seqnum( domain );
496
497done:
498	DEBUG(10, ("refresh_sequence_number: %s seq number is now %d\n",
499		   domain->name, domain->sequence_number));
500
501	return;
502}
503
504/*
505  decide if a cache entry has expired
506*/
507static BOOL centry_expired(struct winbindd_domain *domain, const char *keystr, struct cache_entry *centry)
508{
509	/* If we've been told to be offline - stay in that state... */
510	if (lp_winbind_offline_logon() && global_winbindd_offline_state) {
511		DEBUG(10,("centry_expired: Key %s for domain %s valid as winbindd is globally offline.\n",
512			keystr, domain->name ));
513		return False;
514	}
515
516	/* when the domain is offline return the cached entry.
517	 * This deals with transient offline states... */
518
519	if (!domain->online) {
520		DEBUG(10,("centry_expired: Key %s for domain %s valid as domain is offline.\n",
521			keystr, domain->name ));
522		return False;
523	}
524
525	/* if the server is OK and our cache entry came from when it was down then
526	   the entry is invalid */
527	if ((domain->sequence_number != DOM_SEQUENCE_NONE) &&
528	    (centry->sequence_number == DOM_SEQUENCE_NONE)) {
529		DEBUG(10,("centry_expired: Key %s for domain %s invalid sequence.\n",
530			keystr, domain->name ));
531		return True;
532	}
533
534	/* if the server is down or the cache entry is not older than the
535	   current sequence number then it is OK */
536	if (wcache_server_down(domain) ||
537	    centry->sequence_number == domain->sequence_number) {
538		DEBUG(10,("centry_expired: Key %s for domain %s is good.\n",
539			keystr, domain->name ));
540		return False;
541	}
542
543	DEBUG(10,("centry_expired: Key %s for domain %s expired\n",
544		keystr, domain->name ));
545
546	/* it's expired */
547	return True;
548}
549
550static struct cache_entry *wcache_fetch_raw(char *kstr)
551{
552	TDB_DATA data;
553	struct cache_entry *centry;
554	TDB_DATA key;
555
556	key.dptr = kstr;
557	key.dsize = strlen(kstr);
558	data = tdb_fetch(wcache->tdb, key);
559	if (!data.dptr) {
560		/* a cache miss */
561		return NULL;
562	}
563
564	centry = SMB_XMALLOC_P(struct cache_entry);
565	centry->data = (unsigned char *)data.dptr;
566	centry->len = data.dsize;
567	centry->ofs = 0;
568
569	if (centry->len < 8) {
570		/* huh? corrupt cache? */
571		DEBUG(10,("wcache_fetch_raw: Corrupt cache for key %s (len < 8) ?\n", kstr));
572		centry_free(centry);
573		return NULL;
574	}
575
576	centry->status = NT_STATUS(centry_uint32(centry));
577	centry->sequence_number = centry_uint32(centry);
578
579	return centry;
580}
581
582/*
583  fetch an entry from the cache, with a varargs key. auto-fetch the sequence
584  number and return status
585*/
586static struct cache_entry *wcache_fetch(struct winbind_cache *cache,
587					struct winbindd_domain *domain,
588					const char *format, ...) PRINTF_ATTRIBUTE(3,4);
589static struct cache_entry *wcache_fetch(struct winbind_cache *cache,
590					struct winbindd_domain *domain,
591					const char *format, ...)
592{
593	va_list ap;
594	char *kstr;
595	struct cache_entry *centry;
596
597	if (opt_nocache) {
598		return NULL;
599	}
600
601	refresh_sequence_number(domain, False);
602
603	va_start(ap, format);
604	smb_xvasprintf(&kstr, format, ap);
605	va_end(ap);
606
607	centry = wcache_fetch_raw(kstr);
608	if (centry == NULL) {
609		free(kstr);
610		return NULL;
611	}
612
613	if (centry_expired(domain, kstr, centry)) {
614
615		DEBUG(10,("wcache_fetch: entry %s expired for domain %s\n",
616			 kstr, domain->name ));
617
618		centry_free(centry);
619		free(kstr);
620		return NULL;
621	}
622
623	DEBUG(10,("wcache_fetch: returning entry %s for domain %s\n",
624		 kstr, domain->name ));
625
626	free(kstr);
627	return centry;
628}
629
630static void wcache_delete(const char *format, ...) PRINTF_ATTRIBUTE(1,2);
631static void wcache_delete(const char *format, ...)
632{
633	va_list ap;
634	char *kstr;
635	TDB_DATA key;
636
637	va_start(ap, format);
638	smb_xvasprintf(&kstr, format, ap);
639	va_end(ap);
640
641	key.dptr = kstr;
642	key.dsize = strlen(kstr);
643
644	tdb_delete(wcache->tdb, key);
645	free(kstr);
646}
647
648/*
649  make sure we have at least len bytes available in a centry
650*/
651static void centry_expand(struct cache_entry *centry, uint32 len)
652{
653	if (centry->len - centry->ofs >= len)
654		return;
655	centry->len *= 2;
656	centry->data = SMB_REALLOC_ARRAY(centry->data, unsigned char,
657					 centry->len);
658	if (!centry->data) {
659		DEBUG(0,("out of memory: needed %d bytes in centry_expand\n", centry->len));
660		smb_panic("out of memory in centry_expand");
661	}
662}
663
664/*
665  push a uint32 into a centry
666*/
667static void centry_put_uint32(struct cache_entry *centry, uint32 v)
668{
669	centry_expand(centry, 4);
670	SIVAL(centry->data, centry->ofs, v);
671	centry->ofs += 4;
672}
673
674/*
675  push a uint16 into a centry
676*/
677static void centry_put_uint16(struct cache_entry *centry, uint16 v)
678{
679	centry_expand(centry, 2);
680	SIVAL(centry->data, centry->ofs, v);
681	centry->ofs += 2;
682}
683
684/*
685  push a uint8 into a centry
686*/
687static void centry_put_uint8(struct cache_entry *centry, uint8 v)
688{
689	centry_expand(centry, 1);
690	SCVAL(centry->data, centry->ofs, v);
691	centry->ofs += 1;
692}
693
694/*
695   push a string into a centry
696 */
697static void centry_put_string(struct cache_entry *centry, const char *s)
698{
699	int len;
700
701	if (!s) {
702		/* null strings are marked as len 0xFFFF */
703		centry_put_uint8(centry, 0xFF);
704		return;
705	}
706
707	len = strlen(s);
708	/* can't handle more than 254 char strings. Truncating is probably best */
709	if (len > 254) {
710		DEBUG(10,("centry_put_string: truncating len (%d) to: 254\n", len));
711		len = 254;
712	}
713	centry_put_uint8(centry, len);
714	centry_expand(centry, len);
715	memcpy(centry->data + centry->ofs, s, len);
716	centry->ofs += len;
717}
718
719/*
720   push a 16 byte hash into a centry - treat as 16 byte string.
721 */
722static void centry_put_hash16(struct cache_entry *centry, const uint8 val[16])
723{
724	centry_put_uint8(centry, 16);
725	centry_expand(centry, 16);
726	memcpy(centry->data + centry->ofs, val, 16);
727	centry->ofs += 16;
728}
729
730static void centry_put_sid(struct cache_entry *centry, const DOM_SID *sid)
731{
732	fstring sid_string;
733	centry_put_string(centry, sid_to_string(sid_string, sid));
734}
735
736/*
737  push a NTTIME into a centry
738*/
739static void centry_put_nttime(struct cache_entry *centry, NTTIME nt)
740{
741	centry_expand(centry, 8);
742	SIVAL(centry->data, centry->ofs, nt & 0xFFFFFFFF);
743	centry->ofs += 4;
744	SIVAL(centry->data, centry->ofs, nt >> 32);
745	centry->ofs += 4;
746}
747
748/*
749  push a time_t into a centry - use a 64 bit size.
750  NTTIME here is being used as a convenient 64-bit size.
751*/
752static void centry_put_time(struct cache_entry *centry, time_t t)
753{
754	NTTIME nt = (NTTIME)t;
755	centry_put_nttime(centry, nt);
756}
757
758/*
759  start a centry for output. When finished, call centry_end()
760*/
761struct cache_entry *centry_start(struct winbindd_domain *domain, NTSTATUS status)
762{
763	struct cache_entry *centry;
764
765	if (!wcache->tdb)
766		return NULL;
767
768	centry = SMB_XMALLOC_P(struct cache_entry);
769
770	centry->len = 8192; /* reasonable default */
771	centry->data = SMB_XMALLOC_ARRAY(uint8, centry->len);
772	centry->ofs = 0;
773	centry->sequence_number = domain->sequence_number;
774	centry_put_uint32(centry, NT_STATUS_V(status));
775	centry_put_uint32(centry, centry->sequence_number);
776	return centry;
777}
778
779/*
780  finish a centry and write it to the tdb
781*/
782static void centry_end(struct cache_entry *centry, const char *format, ...) PRINTF_ATTRIBUTE(2,3);
783static void centry_end(struct cache_entry *centry, const char *format, ...)
784{
785	va_list ap;
786	char *kstr;
787	TDB_DATA key, data;
788
789	va_start(ap, format);
790	smb_xvasprintf(&kstr, format, ap);
791	va_end(ap);
792
793	key.dptr = kstr;
794	key.dsize = strlen(kstr);
795	data.dptr = (char *)centry->data;
796	data.dsize = centry->ofs;
797
798	tdb_store(wcache->tdb, key, data, TDB_REPLACE);
799	free(kstr);
800}
801
802static void wcache_save_name_to_sid(struct winbindd_domain *domain,
803				    NTSTATUS status, const char *domain_name,
804				    const char *name, const DOM_SID *sid,
805				    enum lsa_SidType type)
806{
807	struct cache_entry *centry;
808	fstring uname;
809
810	centry = centry_start(domain, status);
811	if (!centry)
812		return;
813	centry_put_uint32(centry, type);
814	centry_put_sid(centry, sid);
815	fstrcpy(uname, name);
816	strupper_m(uname);
817	centry_end(centry, "NS/%s/%s", domain_name, uname);
818	DEBUG(10,("wcache_save_name_to_sid: %s\\%s -> %s\n", domain_name, uname,
819		  sid_string_static(sid)));
820	centry_free(centry);
821}
822
823static void wcache_save_sid_to_name(struct winbindd_domain *domain, NTSTATUS status,
824				    const DOM_SID *sid, const char *domain_name, const char *name, enum lsa_SidType type)
825{
826	struct cache_entry *centry;
827	fstring sid_string;
828
829	if (is_null_sid(sid)) {
830		return;
831	}
832
833	centry = centry_start(domain, status);
834	if (!centry)
835		return;
836	if (NT_STATUS_IS_OK(status)) {
837		centry_put_uint32(centry, type);
838		centry_put_string(centry, domain_name);
839		centry_put_string(centry, name);
840	}
841	centry_end(centry, "SN/%s", sid_to_string(sid_string, sid));
842	DEBUG(10,("wcache_save_sid_to_name: %s -> %s\n", sid_string, name));
843	centry_free(centry);
844}
845
846
847static void wcache_save_user(struct winbindd_domain *domain, NTSTATUS status, WINBIND_USERINFO *info)
848{
849	struct cache_entry *centry;
850	fstring sid_string;
851
852	if (is_null_sid(&info->user_sid)) {
853		return;
854	}
855
856	centry = centry_start(domain, status);
857	if (!centry)
858		return;
859	centry_put_string(centry, info->acct_name);
860	centry_put_string(centry, info->full_name);
861	centry_put_string(centry, info->homedir);
862	centry_put_string(centry, info->shell);
863	centry_put_uint32(centry, info->primary_gid);
864	centry_put_sid(centry, &info->user_sid);
865	centry_put_sid(centry, &info->group_sid);
866	centry_end(centry, "U/%s", sid_to_string(sid_string, &info->user_sid));
867	DEBUG(10,("wcache_save_user: %s (acct_name %s)\n", sid_string, info->acct_name));
868	centry_free(centry);
869}
870
871static void wcache_save_lockout_policy(struct winbindd_domain *domain, NTSTATUS status, SAM_UNK_INFO_12 *lockout_policy)
872{
873	struct cache_entry *centry;
874
875	centry = centry_start(domain, status);
876	if (!centry)
877		return;
878
879	centry_put_nttime(centry, lockout_policy->duration);
880	centry_put_nttime(centry, lockout_policy->reset_count);
881	centry_put_uint16(centry, lockout_policy->bad_attempt_lockout);
882
883	centry_end(centry, "LOC_POL/%s", domain->name);
884
885	DEBUG(10,("wcache_save_lockout_policy: %s\n", domain->name));
886
887	centry_free(centry);
888}
889
890static void wcache_save_password_policy(struct winbindd_domain *domain, NTSTATUS status, SAM_UNK_INFO_1 *policy)
891{
892	struct cache_entry *centry;
893
894	centry = centry_start(domain, status);
895	if (!centry)
896		return;
897
898	centry_put_uint16(centry, policy->min_length_password);
899	centry_put_uint16(centry, policy->password_history);
900	centry_put_uint32(centry, policy->password_properties);
901	centry_put_nttime(centry, policy->expire);
902	centry_put_nttime(centry, policy->min_passwordage);
903
904	centry_end(centry, "PWD_POL/%s", domain->name);
905
906	DEBUG(10,("wcache_save_password_policy: %s\n", domain->name));
907
908	centry_free(centry);
909}
910
911NTSTATUS wcache_cached_creds_exist(struct winbindd_domain *domain, const DOM_SID *sid)
912{
913	struct winbind_cache *cache = get_cache(domain);
914	TDB_DATA data;
915	fstring key_str;
916	uint32 rid;
917
918	if (!cache->tdb) {
919		return NT_STATUS_INTERNAL_DB_ERROR;
920	}
921
922	if (is_null_sid(sid)) {
923		return NT_STATUS_INVALID_SID;
924	}
925
926	if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
927		return NT_STATUS_INVALID_SID;
928	}
929
930	fstr_sprintf(key_str, "CRED/%s", sid_string_static(sid));
931
932	data = tdb_fetch(cache->tdb, make_tdb_data(key_str, strlen(key_str)));
933	if (!data.dptr) {
934		return NT_STATUS_OBJECT_NAME_NOT_FOUND;
935	}
936
937	SAFE_FREE(data.dptr);
938	return NT_STATUS_OK;
939}
940
941/* Lookup creds for a SID - copes with old (unsalted) creds as well
942   as new salted ones. */
943
944NTSTATUS wcache_get_creds(struct winbindd_domain *domain,
945			  TALLOC_CTX *mem_ctx,
946			  const DOM_SID *sid,
947			  const uint8 **cached_nt_pass,
948			  const uint8 **cached_salt)
949{
950	struct winbind_cache *cache = get_cache(domain);
951	struct cache_entry *centry = NULL;
952	NTSTATUS status;
953	time_t t;
954	uint32 rid;
955
956	if (!cache->tdb) {
957		return NT_STATUS_INTERNAL_DB_ERROR;
958	}
959
960	if (is_null_sid(sid)) {
961		return NT_STATUS_INVALID_SID;
962	}
963
964	if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
965		return NT_STATUS_INVALID_SID;
966	}
967
968	/* Try and get a salted cred first. If we can't
969	   fall back to an unsalted cred. */
970
971	centry = wcache_fetch(cache, domain, "CRED/%s", sid_string_static(sid));
972	if (!centry) {
973		DEBUG(10,("wcache_get_creds: entry for [CRED/%s] not found\n",
974				sid_string_static(sid)));
975		return NT_STATUS_OBJECT_NAME_NOT_FOUND;
976	}
977
978	t = centry_time(centry);
979
980	/* In the salted case this isn't actually the nt_hash itself,
981	   but the MD5 of the salt + nt_hash. Let the caller
982	   sort this out. It can tell as we only return the cached_salt
983	   if we are returning a salted cred. */
984
985	*cached_nt_pass = (const uint8 *)centry_hash16(centry, mem_ctx);
986	if (*cached_nt_pass == NULL) {
987		const char *sidstr = sid_string_static(sid);
988
989		/* Bad (old) cred cache. Delete and pretend we
990		   don't have it. */
991		DEBUG(0,("wcache_get_creds: bad entry for [CRED/%s] - deleting\n",
992				sidstr));
993		wcache_delete("CRED/%s", sidstr);
994		centry_free(centry);
995		return NT_STATUS_OBJECT_NAME_NOT_FOUND;
996	}
997
998	/* We only have 17 bytes more data in the salted cred case. */
999	if (centry->len - centry->ofs == 17) {
1000		*cached_salt = (const uint8 *)centry_hash16(centry, mem_ctx);
1001	} else {
1002		*cached_salt = NULL;
1003	}
1004
1005#if DEBUG_PASSWORD
1006	dump_data(100, (const char *)*cached_nt_pass, NT_HASH_LEN);
1007	if (*cached_salt) {
1008		dump_data(100, (const char *)*cached_salt, NT_HASH_LEN);
1009	}
1010#endif
1011	status = centry->status;
1012
1013	DEBUG(10,("wcache_get_creds: [Cached] - cached creds for user %s status: %s\n",
1014		sid_string_static(sid), nt_errstr(status) ));
1015
1016	centry_free(centry);
1017	return status;
1018}
1019
1020/* Store creds for a SID - only writes out new salted ones. */
1021
1022NTSTATUS wcache_save_creds(struct winbindd_domain *domain,
1023			   TALLOC_CTX *mem_ctx,
1024			   const DOM_SID *sid,
1025			   const uint8 nt_pass[NT_HASH_LEN])
1026{
1027	struct cache_entry *centry;
1028	fstring sid_string;
1029	uint32 rid;
1030	uint8 cred_salt[NT_HASH_LEN];
1031	uint8 salted_hash[NT_HASH_LEN];
1032
1033	if (is_null_sid(sid)) {
1034		return NT_STATUS_INVALID_SID;
1035	}
1036
1037	if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
1038		return NT_STATUS_INVALID_SID;
1039	}
1040
1041	centry = centry_start(domain, NT_STATUS_OK);
1042	if (!centry) {
1043		return NT_STATUS_INTERNAL_DB_ERROR;
1044	}
1045
1046#if DEBUG_PASSWORD
1047	dump_data(100, (const char *)nt_pass, NT_HASH_LEN);
1048#endif
1049
1050	centry_put_time(centry, time(NULL));
1051
1052	/* Create a salt and then salt the hash. */
1053	generate_random_buffer(cred_salt, NT_HASH_LEN);
1054	E_md5hash(cred_salt, nt_pass, salted_hash);
1055
1056	centry_put_hash16(centry, salted_hash);
1057	centry_put_hash16(centry, cred_salt);
1058	centry_end(centry, "CRED/%s", sid_to_string(sid_string, sid));
1059
1060	DEBUG(10,("wcache_save_creds: %s\n", sid_string));
1061
1062	centry_free(centry);
1063
1064	return NT_STATUS_OK;
1065}
1066
1067
1068/* Query display info. This is the basic user list fn */
1069static NTSTATUS query_user_list(struct winbindd_domain *domain,
1070				TALLOC_CTX *mem_ctx,
1071				uint32 *num_entries,
1072				WINBIND_USERINFO **info)
1073{
1074	struct winbind_cache *cache = get_cache(domain);
1075	struct cache_entry *centry = NULL;
1076	NTSTATUS status;
1077	unsigned int i, retry;
1078
1079	if (!cache->tdb)
1080		goto do_query;
1081
1082	centry = wcache_fetch(cache, domain, "UL/%s", domain->name);
1083	if (!centry)
1084		goto do_query;
1085
1086	*num_entries = centry_uint32(centry);
1087
1088	if (*num_entries == 0)
1089		goto do_cached;
1090
1091	(*info) = TALLOC_ARRAY(mem_ctx, WINBIND_USERINFO, *num_entries);
1092	if (! (*info))
1093		smb_panic("query_user_list out of memory");
1094	for (i=0; i<(*num_entries); i++) {
1095		(*info)[i].acct_name = centry_string(centry, mem_ctx);
1096		(*info)[i].full_name = centry_string(centry, mem_ctx);
1097		(*info)[i].homedir = centry_string(centry, mem_ctx);
1098		(*info)[i].shell = centry_string(centry, mem_ctx);
1099		centry_sid(centry, mem_ctx, &(*info)[i].user_sid);
1100		centry_sid(centry, mem_ctx, &(*info)[i].group_sid);
1101	}
1102
1103do_cached:
1104	status = centry->status;
1105
1106	DEBUG(10,("query_user_list: [Cached] - cached list for domain %s status: %s\n",
1107		domain->name, nt_errstr(status) ));
1108
1109	centry_free(centry);
1110	return status;
1111
1112do_query:
1113	*num_entries = 0;
1114	*info = NULL;
1115
1116	/* Return status value returned by seq number check */
1117
1118	if (!NT_STATUS_IS_OK(domain->last_status))
1119		return domain->last_status;
1120
1121	/* Put the query_user_list() in a retry loop.  There appears to be
1122	 * some bug either with Windows 2000 or Samba's handling of large
1123	 * rpc replies.  This manifests itself as sudden disconnection
1124	 * at a random point in the enumeration of a large (60k) user list.
1125	 * The retry loop simply tries the operation again. )-:  It's not
1126	 * pretty but an acceptable workaround until we work out what the
1127	 * real problem is. */
1128
1129	retry = 0;
1130	do {
1131
1132		DEBUG(10,("query_user_list: [Cached] - doing backend query for list for domain %s\n",
1133			domain->name ));
1134
1135		status = domain->backend->query_user_list(domain, mem_ctx, num_entries, info);
1136		if (!NT_STATUS_IS_OK(status))
1137			DEBUG(3, ("query_user_list: returned 0x%08x, "
1138				  "retrying\n", NT_STATUS_V(status)));
1139			if (NT_STATUS_EQUAL(status, NT_STATUS_UNSUCCESSFUL)) {
1140				DEBUG(3, ("query_user_list: flushing "
1141					  "connection cache\n"));
1142				invalidate_cm_connection(&domain->conn);
1143			}
1144
1145	} while (NT_STATUS_V(status) == NT_STATUS_V(NT_STATUS_UNSUCCESSFUL) &&
1146		 (retry++ < 5));
1147
1148	/* and save it */
1149	refresh_sequence_number(domain, False);
1150	centry = centry_start(domain, status);
1151	if (!centry)
1152		goto skip_save;
1153	centry_put_uint32(centry, *num_entries);
1154	for (i=0; i<(*num_entries); i++) {
1155		centry_put_string(centry, (*info)[i].acct_name);
1156		centry_put_string(centry, (*info)[i].full_name);
1157		centry_put_string(centry, (*info)[i].homedir);
1158		centry_put_string(centry, (*info)[i].shell);
1159		centry_put_sid(centry, &(*info)[i].user_sid);
1160		centry_put_sid(centry, &(*info)[i].group_sid);
1161		if (domain->backend && domain->backend->consistent) {
1162			/* when the backend is consistent we can pre-prime some mappings */
1163			wcache_save_name_to_sid(domain, NT_STATUS_OK,
1164						domain->name,
1165						(*info)[i].acct_name,
1166						&(*info)[i].user_sid,
1167						SID_NAME_USER);
1168			wcache_save_sid_to_name(domain, NT_STATUS_OK,
1169						&(*info)[i].user_sid,
1170						domain->name,
1171						(*info)[i].acct_name,
1172						SID_NAME_USER);
1173			wcache_save_user(domain, NT_STATUS_OK, &(*info)[i]);
1174		}
1175	}
1176	centry_end(centry, "UL/%s", domain->name);
1177	centry_free(centry);
1178
1179skip_save:
1180	return status;
1181}
1182
1183/* list all domain groups */
1184static NTSTATUS enum_dom_groups(struct winbindd_domain *domain,
1185				TALLOC_CTX *mem_ctx,
1186				uint32 *num_entries,
1187				struct acct_info **info)
1188{
1189	struct winbind_cache *cache = get_cache(domain);
1190	struct cache_entry *centry = NULL;
1191	NTSTATUS status;
1192	unsigned int i;
1193
1194	if (!cache->tdb)
1195		goto do_query;
1196
1197	centry = wcache_fetch(cache, domain, "GL/%s/domain", domain->name);
1198	if (!centry)
1199		goto do_query;
1200
1201	*num_entries = centry_uint32(centry);
1202
1203	if (*num_entries == 0)
1204		goto do_cached;
1205
1206	(*info) = TALLOC_ARRAY(mem_ctx, struct acct_info, *num_entries);
1207	if (! (*info))
1208		smb_panic("enum_dom_groups out of memory");
1209	for (i=0; i<(*num_entries); i++) {
1210		fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx));
1211		fstrcpy((*info)[i].acct_desc, centry_string(centry, mem_ctx));
1212		(*info)[i].rid = centry_uint32(centry);
1213	}
1214
1215do_cached:
1216	status = centry->status;
1217
1218	DEBUG(10,("enum_dom_groups: [Cached] - cached list for domain %s status: %s\n",
1219		domain->name, nt_errstr(status) ));
1220
1221	centry_free(centry);
1222	return status;
1223
1224do_query:
1225	*num_entries = 0;
1226	*info = NULL;
1227
1228	/* Return status value returned by seq number check */
1229
1230	if (!NT_STATUS_IS_OK(domain->last_status))
1231		return domain->last_status;
1232
1233	DEBUG(10,("enum_dom_groups: [Cached] - doing backend query for list for domain %s\n",
1234		domain->name ));
1235
1236	status = domain->backend->enum_dom_groups(domain, mem_ctx, num_entries, info);
1237
1238	/* and save it */
1239	refresh_sequence_number(domain, False);
1240	centry = centry_start(domain, status);
1241	if (!centry)
1242		goto skip_save;
1243	centry_put_uint32(centry, *num_entries);
1244	for (i=0; i<(*num_entries); i++) {
1245		centry_put_string(centry, (*info)[i].acct_name);
1246		centry_put_string(centry, (*info)[i].acct_desc);
1247		centry_put_uint32(centry, (*info)[i].rid);
1248	}
1249	centry_end(centry, "GL/%s/domain", domain->name);
1250	centry_free(centry);
1251
1252skip_save:
1253	return status;
1254}
1255
1256/* list all domain groups */
1257static NTSTATUS enum_local_groups(struct winbindd_domain *domain,
1258				TALLOC_CTX *mem_ctx,
1259				uint32 *num_entries,
1260				struct acct_info **info)
1261{
1262	struct winbind_cache *cache = get_cache(domain);
1263	struct cache_entry *centry = NULL;
1264	NTSTATUS status;
1265	unsigned int i;
1266
1267	if (!cache->tdb)
1268		goto do_query;
1269
1270	centry = wcache_fetch(cache, domain, "GL/%s/local", domain->name);
1271	if (!centry)
1272		goto do_query;
1273
1274	*num_entries = centry_uint32(centry);
1275
1276	if (*num_entries == 0)
1277		goto do_cached;
1278
1279	(*info) = TALLOC_ARRAY(mem_ctx, struct acct_info, *num_entries);
1280	if (! (*info))
1281		smb_panic("enum_dom_groups out of memory");
1282	for (i=0; i<(*num_entries); i++) {
1283		fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx));
1284		fstrcpy((*info)[i].acct_desc, centry_string(centry, mem_ctx));
1285		(*info)[i].rid = centry_uint32(centry);
1286	}
1287
1288do_cached:
1289
1290	/* If we are returning cached data and the domain controller
1291	   is down then we don't know whether the data is up to date
1292	   or not.  Return NT_STATUS_MORE_PROCESSING_REQUIRED to
1293	   indicate this. */
1294
1295	if (wcache_server_down(domain)) {
1296		DEBUG(10, ("enum_local_groups: returning cached user list and server was down\n"));
1297		status = NT_STATUS_MORE_PROCESSING_REQUIRED;
1298	} else
1299		status = centry->status;
1300
1301	DEBUG(10,("enum_local_groups: [Cached] - cached list for domain %s status: %s\n",
1302		domain->name, nt_errstr(status) ));
1303
1304	centry_free(centry);
1305	return status;
1306
1307do_query:
1308	*num_entries = 0;
1309	*info = NULL;
1310
1311	/* Return status value returned by seq number check */
1312
1313	if (!NT_STATUS_IS_OK(domain->last_status))
1314		return domain->last_status;
1315
1316	DEBUG(10,("enum_local_groups: [Cached] - doing backend query for list for domain %s\n",
1317		domain->name ));
1318
1319	status = domain->backend->enum_local_groups(domain, mem_ctx, num_entries, info);
1320
1321	/* and save it */
1322	refresh_sequence_number(domain, False);
1323	centry = centry_start(domain, status);
1324	if (!centry)
1325		goto skip_save;
1326	centry_put_uint32(centry, *num_entries);
1327	for (i=0; i<(*num_entries); i++) {
1328		centry_put_string(centry, (*info)[i].acct_name);
1329		centry_put_string(centry, (*info)[i].acct_desc);
1330		centry_put_uint32(centry, (*info)[i].rid);
1331	}
1332	centry_end(centry, "GL/%s/local", domain->name);
1333	centry_free(centry);
1334
1335skip_save:
1336	return status;
1337}
1338
1339/* convert a single name to a sid in a domain */
1340static NTSTATUS name_to_sid(struct winbindd_domain *domain,
1341			    TALLOC_CTX *mem_ctx,
1342			    const char *domain_name,
1343			    const char *name,
1344			    DOM_SID *sid,
1345			    enum lsa_SidType *type)
1346{
1347	struct winbind_cache *cache = get_cache(domain);
1348	struct cache_entry *centry = NULL;
1349	NTSTATUS status;
1350	fstring uname;
1351
1352	if (!cache->tdb)
1353		goto do_query;
1354
1355	fstrcpy(uname, name);
1356	strupper_m(uname);
1357	centry = wcache_fetch(cache, domain, "NS/%s/%s", domain_name, uname);
1358	if (!centry)
1359		goto do_query;
1360	*type = (enum lsa_SidType)centry_uint32(centry);
1361	status = centry->status;
1362	if (NT_STATUS_IS_OK(status)) {
1363		centry_sid(centry, mem_ctx, sid);
1364	}
1365
1366	DEBUG(10,("name_to_sid: [Cached] - cached name for domain %s status: %s\n",
1367		domain->name, nt_errstr(status) ));
1368
1369	centry_free(centry);
1370	return status;
1371
1372do_query:
1373	ZERO_STRUCTP(sid);
1374
1375	/* If the seq number check indicated that there is a problem
1376	 * with this DC, then return that status... except for
1377	 * access_denied.  This is special because the dc may be in
1378	 * "restrict anonymous = 1" mode, in which case it will deny
1379	 * most unauthenticated operations, but *will* allow the LSA
1380	 * name-to-sid that we try as a fallback. */
1381
1382	if (!(NT_STATUS_IS_OK(domain->last_status)
1383	      || NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)))
1384		return domain->last_status;
1385
1386	DEBUG(10,("name_to_sid: [Cached] - doing backend query for name for domain %s\n",
1387		domain->name ));
1388
1389	status = domain->backend->name_to_sid(domain, mem_ctx, domain_name, name, sid, type);
1390
1391	/* and save it */
1392	refresh_sequence_number(domain, False);
1393
1394	if (domain->online && !is_null_sid(sid)) {
1395		wcache_save_name_to_sid(domain, status, domain_name, name, sid, *type);
1396	}
1397
1398	if (NT_STATUS_IS_OK(status)) {
1399		strupper_m(CONST_DISCARD(char *,domain_name));
1400		strlower_m(CONST_DISCARD(char *,name));
1401		wcache_save_sid_to_name(domain, status, sid, domain_name, name, *type);
1402	}
1403
1404	return status;
1405}
1406
1407/* convert a sid to a user or group name. The sid is guaranteed to be in the domain
1408   given */
1409static NTSTATUS sid_to_name(struct winbindd_domain *domain,
1410			    TALLOC_CTX *mem_ctx,
1411			    const DOM_SID *sid,
1412			    char **domain_name,
1413			    char **name,
1414			    enum lsa_SidType *type)
1415{
1416	struct winbind_cache *cache = get_cache(domain);
1417	struct cache_entry *centry = NULL;
1418	NTSTATUS status;
1419	fstring sid_string;
1420
1421	if (!cache->tdb)
1422		goto do_query;
1423
1424	centry = wcache_fetch(cache, domain, "SN/%s", sid_to_string(sid_string, sid));
1425	if (!centry)
1426		goto do_query;
1427	if (NT_STATUS_IS_OK(centry->status)) {
1428		*type = (enum lsa_SidType)centry_uint32(centry);
1429		*domain_name = centry_string(centry, mem_ctx);
1430		*name = centry_string(centry, mem_ctx);
1431	}
1432	status = centry->status;
1433
1434	DEBUG(10,("sid_to_name: [Cached] - cached name for domain %s status: %s\n",
1435		domain->name, nt_errstr(status) ));
1436
1437	centry_free(centry);
1438	return status;
1439
1440do_query:
1441	*name = NULL;
1442	*domain_name = NULL;
1443
1444	/* If the seq number check indicated that there is a problem
1445	 * with this DC, then return that status... except for
1446	 * access_denied.  This is special because the dc may be in
1447	 * "restrict anonymous = 1" mode, in which case it will deny
1448	 * most unauthenticated operations, but *will* allow the LSA
1449	 * sid-to-name that we try as a fallback. */
1450
1451	if (!(NT_STATUS_IS_OK(domain->last_status)
1452	      || NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)))
1453		return domain->last_status;
1454
1455	DEBUG(10,("sid_to_name: [Cached] - doing backend query for name for domain %s\n",
1456		domain->name ));
1457
1458	status = domain->backend->sid_to_name(domain, mem_ctx, sid, domain_name, name, type);
1459
1460	/* and save it */
1461	refresh_sequence_number(domain, False);
1462	wcache_save_sid_to_name(domain, status, sid, *domain_name, *name, *type);
1463
1464	/* We can't save the name to sid mapping here, as with sid history a
1465	 * later name2sid would give the wrong sid. */
1466
1467	return status;
1468}
1469
1470static NTSTATUS rids_to_names(struct winbindd_domain *domain,
1471			      TALLOC_CTX *mem_ctx,
1472			      const DOM_SID *domain_sid,
1473			      uint32 *rids,
1474			      size_t num_rids,
1475			      char **domain_name,
1476			      char ***names,
1477			      enum lsa_SidType **types)
1478{
1479	struct winbind_cache *cache = get_cache(domain);
1480	size_t i;
1481	NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
1482	BOOL have_mapped;
1483	BOOL have_unmapped;
1484
1485	*domain_name = NULL;
1486	*names = NULL;
1487	*types = NULL;
1488
1489	if (!cache->tdb) {
1490		goto do_query;
1491	}
1492
1493	if (num_rids == 0) {
1494		return NT_STATUS_OK;
1495	}
1496
1497	*names = TALLOC_ARRAY(mem_ctx, char *, num_rids);
1498	*types = TALLOC_ARRAY(mem_ctx, enum lsa_SidType, num_rids);
1499
1500	if ((*names == NULL) || (*types == NULL)) {
1501		result = NT_STATUS_NO_MEMORY;
1502		goto error;
1503	}
1504
1505	have_mapped = have_unmapped = False;
1506
1507	for (i=0; i<num_rids; i++) {
1508		DOM_SID sid;
1509		struct cache_entry *centry;
1510
1511		if (!sid_compose(&sid, domain_sid, rids[i])) {
1512			result = NT_STATUS_INTERNAL_ERROR;
1513			goto error;
1514		}
1515
1516		centry = wcache_fetch(cache, domain, "SN/%s",
1517				      sid_string_static(&sid));
1518		if (!centry) {
1519			goto do_query;
1520		}
1521
1522		(*types)[i] = SID_NAME_UNKNOWN;
1523		(*names)[i] = talloc_strdup(*names, "");
1524
1525		if (NT_STATUS_IS_OK(centry->status)) {
1526			char *dom;
1527			have_mapped = True;
1528			(*types)[i] = (enum lsa_SidType)centry_uint32(centry);
1529			dom = centry_string(centry, mem_ctx);
1530			if (*domain_name == NULL) {
1531				*domain_name = dom;
1532			} else {
1533				talloc_free(dom);
1534			}
1535			(*names)[i] = centry_string(centry, *names);
1536		} else {
1537			have_unmapped = True;
1538		}
1539
1540		centry_free(centry);
1541	}
1542
1543	if (!have_mapped) {
1544		return NT_STATUS_NONE_MAPPED;
1545	}
1546	if (!have_unmapped) {
1547		return NT_STATUS_OK;
1548	}
1549	return STATUS_SOME_UNMAPPED;
1550
1551 do_query:
1552
1553	TALLOC_FREE(*names);
1554	TALLOC_FREE(*types);
1555
1556	result = domain->backend->rids_to_names(domain, mem_ctx, domain_sid,
1557						rids, num_rids, domain_name,
1558						names, types);
1559
1560	if (!NT_STATUS_IS_OK(result) &&
1561	    !NT_STATUS_EQUAL(result, STATUS_SOME_UNMAPPED)) {
1562		return result;
1563	}
1564
1565	refresh_sequence_number(domain, False);
1566
1567	for (i=0; i<num_rids; i++) {
1568		DOM_SID sid;
1569		NTSTATUS status;
1570
1571		if (!sid_compose(&sid, domain_sid, rids[i])) {
1572			result = NT_STATUS_INTERNAL_ERROR;
1573			goto error;
1574		}
1575
1576		status = (*types)[i] == SID_NAME_UNKNOWN ?
1577			NT_STATUS_NONE_MAPPED : NT_STATUS_OK;
1578
1579		wcache_save_sid_to_name(domain, status, &sid, *domain_name,
1580					(*names)[i], (*types)[i]);
1581	}
1582
1583	return result;
1584
1585 error:
1586
1587	TALLOC_FREE(*names);
1588	TALLOC_FREE(*types);
1589	return result;
1590}
1591
1592/* Lookup user information from a rid */
1593static NTSTATUS query_user(struct winbindd_domain *domain,
1594			   TALLOC_CTX *mem_ctx,
1595			   const DOM_SID *user_sid,
1596			   WINBIND_USERINFO *info)
1597{
1598	struct winbind_cache *cache = get_cache(domain);
1599	struct cache_entry *centry = NULL;
1600	NTSTATUS status;
1601
1602	if (!cache->tdb)
1603		goto do_query;
1604
1605	centry = wcache_fetch(cache, domain, "U/%s", sid_string_static(user_sid));
1606
1607	/* If we have an access denied cache entry and a cached info3 in the
1608           samlogon cache then do a query.  This will force the rpc back end
1609           to return the info3 data. */
1610
1611	if (NT_STATUS_V(domain->last_status) == NT_STATUS_V(NT_STATUS_ACCESS_DENIED) &&
1612	    netsamlogon_cache_have(user_sid)) {
1613		DEBUG(10, ("query_user: cached access denied and have cached info3\n"));
1614		domain->last_status = NT_STATUS_OK;
1615		centry_free(centry);
1616		goto do_query;
1617	}
1618
1619	if (!centry)
1620		goto do_query;
1621
1622	info->acct_name = centry_string(centry, mem_ctx);
1623	info->full_name = centry_string(centry, mem_ctx);
1624	info->homedir = centry_string(centry, mem_ctx);
1625	info->shell = centry_string(centry, mem_ctx);
1626	info->primary_gid = centry_uint32(centry);
1627	centry_sid(centry, mem_ctx, &info->user_sid);
1628	centry_sid(centry, mem_ctx, &info->group_sid);
1629	status = centry->status;
1630
1631	DEBUG(10,("query_user: [Cached] - cached info for domain %s status: %s\n",
1632		domain->name, nt_errstr(status) ));
1633
1634	centry_free(centry);
1635	return status;
1636
1637do_query:
1638	ZERO_STRUCTP(info);
1639
1640	/* Return status value returned by seq number check */
1641
1642	if (!NT_STATUS_IS_OK(domain->last_status))
1643		return domain->last_status;
1644
1645	DEBUG(10,("query_user: [Cached] - doing backend query for info for domain %s\n",
1646		domain->name ));
1647
1648	status = domain->backend->query_user(domain, mem_ctx, user_sid, info);
1649
1650	/* and save it */
1651	refresh_sequence_number(domain, False);
1652	wcache_save_user(domain, status, info);
1653
1654	return status;
1655}
1656
1657
1658/* Lookup groups a user is a member of. */
1659static NTSTATUS lookup_usergroups(struct winbindd_domain *domain,
1660				  TALLOC_CTX *mem_ctx,
1661				  const DOM_SID *user_sid,
1662				  uint32 *num_groups, DOM_SID **user_gids)
1663{
1664	struct winbind_cache *cache = get_cache(domain);
1665	struct cache_entry *centry = NULL;
1666	NTSTATUS status;
1667	unsigned int i;
1668	fstring sid_string;
1669
1670	if (!cache->tdb)
1671		goto do_query;
1672
1673	centry = wcache_fetch(cache, domain, "UG/%s", sid_to_string(sid_string, user_sid));
1674
1675	/* If we have an access denied cache entry and a cached info3 in the
1676           samlogon cache then do a query.  This will force the rpc back end
1677           to return the info3 data. */
1678
1679	if (NT_STATUS_V(domain->last_status) == NT_STATUS_V(NT_STATUS_ACCESS_DENIED) &&
1680	    netsamlogon_cache_have(user_sid)) {
1681		DEBUG(10, ("lookup_usergroups: cached access denied and have cached info3\n"));
1682		domain->last_status = NT_STATUS_OK;
1683		centry_free(centry);
1684		goto do_query;
1685	}
1686
1687	if (!centry)
1688		goto do_query;
1689
1690	*num_groups = centry_uint32(centry);
1691
1692	if (*num_groups == 0)
1693		goto do_cached;
1694
1695	(*user_gids) = TALLOC_ARRAY(mem_ctx, DOM_SID, *num_groups);
1696	if (! (*user_gids))
1697		smb_panic("lookup_usergroups out of memory");
1698	for (i=0; i<(*num_groups); i++) {
1699		centry_sid(centry, mem_ctx, &(*user_gids)[i]);
1700	}
1701
1702do_cached:
1703	status = centry->status;
1704
1705	DEBUG(10,("lookup_usergroups: [Cached] - cached info for domain %s status: %s\n",
1706		domain->name, nt_errstr(status) ));
1707
1708	centry_free(centry);
1709	return status;
1710
1711do_query:
1712	(*num_groups) = 0;
1713	(*user_gids) = NULL;
1714
1715	/* Return status value returned by seq number check */
1716
1717	if (!NT_STATUS_IS_OK(domain->last_status))
1718		return domain->last_status;
1719
1720	DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info for domain %s\n",
1721		domain->name ));
1722
1723	status = domain->backend->lookup_usergroups(domain, mem_ctx, user_sid, num_groups, user_gids);
1724
1725	/* and save it */
1726	refresh_sequence_number(domain, False);
1727	centry = centry_start(domain, status);
1728	if (!centry)
1729		goto skip_save;
1730	centry_put_uint32(centry, *num_groups);
1731	for (i=0; i<(*num_groups); i++) {
1732		centry_put_sid(centry, &(*user_gids)[i]);
1733	}
1734	centry_end(centry, "UG/%s", sid_to_string(sid_string, user_sid));
1735	centry_free(centry);
1736
1737skip_save:
1738	return status;
1739}
1740
1741static NTSTATUS lookup_useraliases(struct winbindd_domain *domain,
1742				   TALLOC_CTX *mem_ctx,
1743				   uint32 num_sids, const DOM_SID *sids,
1744				   uint32 *num_aliases, uint32 **alias_rids)
1745{
1746	struct winbind_cache *cache = get_cache(domain);
1747	struct cache_entry *centry = NULL;
1748	NTSTATUS status;
1749	char *sidlist = talloc_strdup(mem_ctx, "");
1750	int i;
1751
1752	if (!cache->tdb)
1753		goto do_query;
1754
1755	if (num_sids == 0) {
1756		*num_aliases = 0;
1757		*alias_rids = NULL;
1758		return NT_STATUS_OK;
1759	}
1760
1761	/* We need to cache indexed by the whole list of SIDs, the aliases
1762	 * resulting might come from any of the SIDs. */
1763
1764	for (i=0; i<num_sids; i++) {
1765		sidlist = talloc_asprintf(mem_ctx, "%s/%s", sidlist,
1766					  sid_string_static(&sids[i]));
1767		if (sidlist == NULL)
1768			return NT_STATUS_NO_MEMORY;
1769	}
1770
1771	centry = wcache_fetch(cache, domain, "UA%s", sidlist);
1772
1773	if (!centry)
1774		goto do_query;
1775
1776	*num_aliases = centry_uint32(centry);
1777	*alias_rids = NULL;
1778
1779	if (*num_aliases) {
1780		(*alias_rids) = TALLOC_ARRAY(mem_ctx, uint32, *num_aliases);
1781
1782		if ((*alias_rids) == NULL) {
1783			centry_free(centry);
1784			return NT_STATUS_NO_MEMORY;
1785		}
1786	} else {
1787		(*alias_rids) = NULL;
1788	}
1789
1790	for (i=0; i<(*num_aliases); i++)
1791		(*alias_rids)[i] = centry_uint32(centry);
1792
1793	status = centry->status;
1794
1795	DEBUG(10,("lookup_useraliases: [Cached] - cached info for domain: %s "
1796		  "status %s\n", domain->name, nt_errstr(status)));
1797
1798	centry_free(centry);
1799	return status;
1800
1801 do_query:
1802	(*num_aliases) = 0;
1803	(*alias_rids) = NULL;
1804
1805	if (!NT_STATUS_IS_OK(domain->last_status))
1806		return domain->last_status;
1807
1808	DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info "
1809		  "for domain %s\n", domain->name ));
1810
1811	status = domain->backend->lookup_useraliases(domain, mem_ctx,
1812						     num_sids, sids,
1813						     num_aliases, alias_rids);
1814
1815	/* and save it */
1816	refresh_sequence_number(domain, False);
1817	centry = centry_start(domain, status);
1818	if (!centry)
1819		goto skip_save;
1820	centry_put_uint32(centry, *num_aliases);
1821	for (i=0; i<(*num_aliases); i++)
1822		centry_put_uint32(centry, (*alias_rids)[i]);
1823	centry_end(centry, "UA%s", sidlist);
1824	centry_free(centry);
1825
1826 skip_save:
1827	return status;
1828}
1829
1830
1831static NTSTATUS lookup_groupmem(struct winbindd_domain *domain,
1832				TALLOC_CTX *mem_ctx,
1833				const DOM_SID *group_sid, uint32 *num_names,
1834				DOM_SID **sid_mem, char ***names,
1835				uint32 **name_types)
1836{
1837	struct winbind_cache *cache = get_cache(domain);
1838	struct cache_entry *centry = NULL;
1839	NTSTATUS status;
1840	unsigned int i;
1841	fstring sid_string;
1842
1843	if (!cache->tdb)
1844		goto do_query;
1845
1846	centry = wcache_fetch(cache, domain, "GM/%s", sid_to_string(sid_string, group_sid));
1847	if (!centry)
1848		goto do_query;
1849
1850	*num_names = centry_uint32(centry);
1851
1852	if (*num_names == 0)
1853		goto do_cached;
1854
1855	(*sid_mem) = TALLOC_ARRAY(mem_ctx, DOM_SID, *num_names);
1856	(*names) = TALLOC_ARRAY(mem_ctx, char *, *num_names);
1857	(*name_types) = TALLOC_ARRAY(mem_ctx, uint32, *num_names);
1858
1859	if (! (*sid_mem) || ! (*names) || ! (*name_types)) {
1860		smb_panic("lookup_groupmem out of memory");
1861	}
1862
1863	for (i=0; i<(*num_names); i++) {
1864		centry_sid(centry, mem_ctx, &(*sid_mem)[i]);
1865		(*names)[i] = centry_string(centry, mem_ctx);
1866		(*name_types)[i] = centry_uint32(centry);
1867	}
1868
1869do_cached:
1870	status = centry->status;
1871
1872	DEBUG(10,("lookup_groupmem: [Cached] - cached info for domain %s status: %s\n",
1873		domain->name, nt_errstr(status)));
1874
1875	centry_free(centry);
1876	return status;
1877
1878do_query:
1879	(*num_names) = 0;
1880	(*sid_mem) = NULL;
1881	(*names) = NULL;
1882	(*name_types) = NULL;
1883
1884	/* Return status value returned by seq number check */
1885
1886	if (!NT_STATUS_IS_OK(domain->last_status))
1887		return domain->last_status;
1888
1889	DEBUG(10,("lookup_groupmem: [Cached] - doing backend query for info for domain %s\n",
1890		domain->name ));
1891
1892	status = domain->backend->lookup_groupmem(domain, mem_ctx, group_sid, num_names,
1893						  sid_mem, names, name_types);
1894
1895	/* and save it */
1896	refresh_sequence_number(domain, False);
1897	centry = centry_start(domain, status);
1898	if (!centry)
1899		goto skip_save;
1900	centry_put_uint32(centry, *num_names);
1901	for (i=0; i<(*num_names); i++) {
1902		centry_put_sid(centry, &(*sid_mem)[i]);
1903		centry_put_string(centry, (*names)[i]);
1904		centry_put_uint32(centry, (*name_types)[i]);
1905	}
1906	centry_end(centry, "GM/%s", sid_to_string(sid_string, group_sid));
1907	centry_free(centry);
1908
1909skip_save:
1910	return status;
1911}
1912
1913/* find the sequence number for a domain */
1914static NTSTATUS sequence_number(struct winbindd_domain *domain, uint32 *seq)
1915{
1916	refresh_sequence_number(domain, False);
1917
1918	*seq = domain->sequence_number;
1919
1920	return NT_STATUS_OK;
1921}
1922
1923/* enumerate trusted domains
1924 * (we need to have the list of trustdoms in the cache when we go offline) -
1925 * Guenther */
1926static NTSTATUS trusted_domains(struct winbindd_domain *domain,
1927				TALLOC_CTX *mem_ctx,
1928				uint32 *num_domains,
1929				char ***names,
1930				char ***alt_names,
1931				DOM_SID **dom_sids)
1932{
1933 	struct winbind_cache *cache = get_cache(domain);
1934 	struct cache_entry *centry = NULL;
1935 	NTSTATUS status;
1936	int i;
1937
1938	if (!cache->tdb)
1939		goto do_query;
1940
1941	centry = wcache_fetch(cache, domain, "TRUSTDOMS/%s", domain->name);
1942
1943	if (!centry) {
1944 		goto do_query;
1945	}
1946
1947	*num_domains = centry_uint32(centry);
1948
1949	if (*num_domains) {
1950		(*names) 	= TALLOC_ARRAY(mem_ctx, char *, *num_domains);
1951		(*alt_names) 	= TALLOC_ARRAY(mem_ctx, char *, *num_domains);
1952		(*dom_sids) 	= TALLOC_ARRAY(mem_ctx, DOM_SID, *num_domains);
1953
1954		if (! (*dom_sids) || ! (*names) || ! (*alt_names)) {
1955			smb_panic("trusted_domains out of memory");
1956 		}
1957	} else {
1958		(*names) = NULL;
1959		(*alt_names) = NULL;
1960		(*dom_sids) = NULL;
1961	}
1962
1963	for (i=0; i<(*num_domains); i++) {
1964		(*names)[i] = centry_string(centry, mem_ctx);
1965		(*alt_names)[i] = centry_string(centry, mem_ctx);
1966		centry_sid(centry, mem_ctx, &(*dom_sids)[i]);
1967	}
1968
1969 	status = centry->status;
1970
1971	DEBUG(10,("trusted_domains: [Cached] - cached info for domain %s (%d trusts) status: %s\n",
1972		domain->name, *num_domains, nt_errstr(status) ));
1973
1974 	centry_free(centry);
1975 	return status;
1976
1977do_query:
1978	(*num_domains) = 0;
1979	(*dom_sids) = NULL;
1980	(*names) = NULL;
1981	(*alt_names) = NULL;
1982
1983	/* Return status value returned by seq number check */
1984
1985 	if (!NT_STATUS_IS_OK(domain->last_status))
1986 		return domain->last_status;
1987
1988	DEBUG(10,("trusted_domains: [Cached] - doing backend query for info for domain %s\n",
1989		domain->name ));
1990
1991	status = domain->backend->trusted_domains(domain, mem_ctx, num_domains,
1992						names, alt_names, dom_sids);
1993
1994	/* no trusts gives NT_STATUS_NO_MORE_ENTRIES resetting to NT_STATUS_OK
1995	 * so that the generic centry handling still applies correctly -
1996	 * Guenther*/
1997
1998	if (!NT_STATUS_IS_ERR(status)) {
1999		status = NT_STATUS_OK;
2000	}
2001
2002	/* and save it */
2003	refresh_sequence_number(domain, False);
2004
2005 	centry = centry_start(domain, status);
2006	if (!centry)
2007		goto skip_save;
2008
2009	centry_put_uint32(centry, *num_domains);
2010
2011	for (i=0; i<(*num_domains); i++) {
2012		centry_put_string(centry, (*names)[i]);
2013		centry_put_string(centry, (*alt_names)[i]);
2014		centry_put_sid(centry, &(*dom_sids)[i]);
2015 	}
2016
2017	centry_end(centry, "TRUSTDOMS/%s", domain->name);
2018
2019 	centry_free(centry);
2020
2021skip_save:
2022 	return status;
2023}
2024
2025/* get lockout policy */
2026static NTSTATUS lockout_policy(struct winbindd_domain *domain,
2027 			       TALLOC_CTX *mem_ctx,
2028			       SAM_UNK_INFO_12 *policy){
2029 	struct winbind_cache *cache = get_cache(domain);
2030 	struct cache_entry *centry = NULL;
2031 	NTSTATUS status;
2032
2033	if (!cache->tdb)
2034		goto do_query;
2035
2036	centry = wcache_fetch(cache, domain, "LOC_POL/%s", domain->name);
2037
2038	if (!centry)
2039 		goto do_query;
2040
2041	policy->duration = centry_nttime(centry);
2042	policy->reset_count = centry_nttime(centry);
2043	policy->bad_attempt_lockout = centry_uint16(centry);
2044
2045 	status = centry->status;
2046
2047	DEBUG(10,("lockout_policy: [Cached] - cached info for domain %s status: %s\n",
2048		domain->name, nt_errstr(status) ));
2049
2050 	centry_free(centry);
2051 	return status;
2052
2053do_query:
2054	ZERO_STRUCTP(policy);
2055
2056	/* Return status value returned by seq number check */
2057
2058 	if (!NT_STATUS_IS_OK(domain->last_status))
2059 		return domain->last_status;
2060
2061	DEBUG(10,("lockout_policy: [Cached] - doing backend query for info for domain %s\n",
2062		domain->name ));
2063
2064	status = domain->backend->lockout_policy(domain, mem_ctx, policy);
2065
2066	/* and save it */
2067 	refresh_sequence_number(domain, False);
2068	wcache_save_lockout_policy(domain, status, policy);
2069
2070 	return status;
2071}
2072
2073/* get password policy */
2074static NTSTATUS password_policy(struct winbindd_domain *domain,
2075				TALLOC_CTX *mem_ctx,
2076				SAM_UNK_INFO_1 *policy)
2077{
2078	struct winbind_cache *cache = get_cache(domain);
2079	struct cache_entry *centry = NULL;
2080	NTSTATUS status;
2081
2082	if (!cache->tdb)
2083		goto do_query;
2084
2085	centry = wcache_fetch(cache, domain, "PWD_POL/%s", domain->name);
2086
2087	if (!centry)
2088		goto do_query;
2089
2090	policy->min_length_password = centry_uint16(centry);
2091	policy->password_history = centry_uint16(centry);
2092	policy->password_properties = centry_uint32(centry);
2093	policy->expire = centry_nttime(centry);
2094	policy->min_passwordage = centry_nttime(centry);
2095
2096	status = centry->status;
2097
2098	DEBUG(10,("lockout_policy: [Cached] - cached info for domain %s status: %s\n",
2099		domain->name, nt_errstr(status) ));
2100
2101	centry_free(centry);
2102	return status;
2103
2104do_query:
2105	ZERO_STRUCTP(policy);
2106
2107	/* Return status value returned by seq number check */
2108
2109	if (!NT_STATUS_IS_OK(domain->last_status))
2110		return domain->last_status;
2111
2112	DEBUG(10,("password_policy: [Cached] - doing backend query for info for domain %s\n",
2113		domain->name ));
2114
2115	status = domain->backend->password_policy(domain, mem_ctx, policy);
2116
2117	/* and save it */
2118	refresh_sequence_number(domain, False);
2119	wcache_save_password_policy(domain, status, policy);
2120
2121	return status;
2122}
2123
2124
2125/* Invalidate cached user and group lists coherently */
2126
2127static int traverse_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
2128		       void *state)
2129{
2130	if (strncmp(kbuf.dptr, "UL/", 3) == 0 ||
2131	    strncmp(kbuf.dptr, "GL/", 3) == 0)
2132		tdb_delete(the_tdb, kbuf);
2133
2134	return 0;
2135}
2136
2137/* Invalidate the getpwnam and getgroups entries for a winbindd domain */
2138
2139void wcache_invalidate_samlogon(struct winbindd_domain *domain,
2140				NET_USER_INFO_3 *info3)
2141{
2142	struct winbind_cache *cache;
2143
2144	/* dont clear cached U/SID and UG/SID entries when we want to logon
2145	 * offline - gd */
2146
2147	if (lp_winbind_offline_logon()) {
2148		return;
2149	}
2150
2151	if (!domain)
2152		return;
2153
2154	cache = get_cache(domain);
2155	netsamlogon_clear_cached_user(cache->tdb, info3);
2156}
2157
2158void wcache_invalidate_cache(void)
2159{
2160	struct winbindd_domain *domain;
2161
2162	for (domain = domain_list(); domain; domain = domain->next) {
2163		struct winbind_cache *cache = get_cache(domain);
2164
2165		DEBUG(10, ("wcache_invalidate_cache: invalidating cache "
2166			   "entries for %s\n", domain->name));
2167		if (cache)
2168			tdb_traverse(cache->tdb, traverse_fn, NULL);
2169	}
2170}
2171
2172static BOOL init_wcache(void)
2173{
2174	if (wcache == NULL) {
2175		wcache = SMB_XMALLOC_P(struct winbind_cache);
2176		ZERO_STRUCTP(wcache);
2177	}
2178
2179	if (wcache->tdb != NULL)
2180		return True;
2181
2182	/* when working offline we must not clear the cache on restart */
2183	wcache->tdb = tdb_open_log(lock_path("winbindd_cache.tdb"),
2184				WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
2185				lp_winbind_offline_logon() ? TDB_DEFAULT : (TDB_DEFAULT | TDB_CLEAR_IF_FIRST),
2186				O_RDWR|O_CREAT, 0600);
2187
2188	if (wcache->tdb == NULL) {
2189		DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
2190		return False;
2191	}
2192
2193	return True;
2194}
2195
2196/************************************************************************
2197 This is called by the parent to initialize the cache file.
2198 We don't need sophisticated locking here as we know we're the
2199 only opener.
2200************************************************************************/
2201
2202BOOL initialize_winbindd_cache(void)
2203{
2204	BOOL cache_bad = True;
2205	uint32 vers;
2206
2207	if (!init_wcache()) {
2208		DEBUG(0,("initialize_winbindd_cache: init_wcache failed.\n"));
2209		return False;
2210	}
2211
2212	/* Check version number. */
2213	if (tdb_fetch_uint32(wcache->tdb, WINBINDD_CACHE_VERSION_KEYSTR, &vers) &&
2214			vers == WINBINDD_CACHE_VERSION) {
2215		cache_bad = False;
2216	}
2217
2218	if (cache_bad) {
2219		DEBUG(0,("initialize_winbindd_cache: clearing cache "
2220			"and re-creating with version number %d\n",
2221			WINBINDD_CACHE_VERSION ));
2222
2223		tdb_close(wcache->tdb);
2224		wcache->tdb = NULL;
2225
2226		if (unlink(lock_path("winbindd_cache.tdb")) == -1) {
2227			DEBUG(0,("initialize_winbindd_cache: unlink %s failed %s ",
2228				lock_path("winbindd_cache.tdb"),
2229				strerror(errno) ));
2230			return False;
2231		}
2232		if (!init_wcache()) {
2233			DEBUG(0,("initialize_winbindd_cache: re-initialization "
2234					"init_wcache failed.\n"));
2235			return False;
2236		}
2237
2238		/* Write the version. */
2239		if (!tdb_store_uint32(wcache->tdb, WINBINDD_CACHE_VERSION_KEYSTR, WINBINDD_CACHE_VERSION)) {
2240			DEBUG(0,("initialize_winbindd_cache: version number store failed %s\n",
2241				tdb_errorstr(wcache->tdb) ));
2242			return False;
2243		}
2244	}
2245
2246	tdb_close(wcache->tdb);
2247	wcache->tdb = NULL;
2248	return True;
2249}
2250
2251void cache_store_response(pid_t pid, struct winbindd_response *response)
2252{
2253	fstring key_str;
2254
2255	if (!init_wcache())
2256		return;
2257
2258	DEBUG(10, ("Storing response for pid %d, len %d\n",
2259		   pid, response->length));
2260
2261	fstr_sprintf(key_str, "DR/%d", pid);
2262	if (tdb_store(wcache->tdb, string_tdb_data(key_str),
2263		      make_tdb_data((const char *)response, sizeof(*response)),
2264		      TDB_REPLACE) == -1)
2265		return;
2266
2267	if (response->length == sizeof(*response))
2268		return;
2269
2270	/* There's extra data */
2271
2272	DEBUG(10, ("Storing extra data: len=%d\n",
2273		   (int)(response->length - sizeof(*response))));
2274
2275	fstr_sprintf(key_str, "DE/%d", pid);
2276	if (tdb_store(wcache->tdb, string_tdb_data(key_str),
2277		      make_tdb_data((const char *)response->extra_data.data,
2278				    response->length - sizeof(*response)),
2279		      TDB_REPLACE) == 0)
2280		return;
2281
2282	/* We could not store the extra data, make sure the tdb does not
2283	 * contain a main record with wrong dangling extra data */
2284
2285	fstr_sprintf(key_str, "DR/%d", pid);
2286	tdb_delete(wcache->tdb, string_tdb_data(key_str));
2287
2288	return;
2289}
2290
2291BOOL cache_retrieve_response(pid_t pid, struct winbindd_response * response)
2292{
2293	TDB_DATA data;
2294	fstring key_str;
2295
2296	if (!init_wcache())
2297		return False;
2298
2299	DEBUG(10, ("Retrieving response for pid %d\n", pid));
2300
2301	fstr_sprintf(key_str, "DR/%d", pid);
2302	data = tdb_fetch(wcache->tdb, string_tdb_data(key_str));
2303
2304	if (data.dptr == NULL)
2305		return False;
2306
2307	if (data.dsize != sizeof(*response))
2308		return False;
2309
2310	memcpy(response, data.dptr, data.dsize);
2311	SAFE_FREE(data.dptr);
2312
2313	if (response->length == sizeof(*response)) {
2314		response->extra_data.data = NULL;
2315		return True;
2316	}
2317
2318	/* There's extra data */
2319
2320	DEBUG(10, ("Retrieving extra data length=%d\n",
2321		   (int)(response->length - sizeof(*response))));
2322
2323	fstr_sprintf(key_str, "DE/%d", pid);
2324	data = tdb_fetch(wcache->tdb, string_tdb_data(key_str));
2325
2326	if (data.dptr == NULL) {
2327		DEBUG(0, ("Did not find extra data\n"));
2328		return False;
2329	}
2330
2331	if (data.dsize != (response->length - sizeof(*response))) {
2332		DEBUG(0, ("Invalid extra data length: %d\n", (int)data.dsize));
2333		SAFE_FREE(data.dptr);
2334		return False;
2335	}
2336
2337	dump_data(11, data.dptr, data.dsize);
2338
2339	response->extra_data.data = data.dptr;
2340	return True;
2341}
2342
2343void cache_cleanup_response(pid_t pid)
2344{
2345	fstring key_str;
2346
2347	if (!init_wcache())
2348		return;
2349
2350	fstr_sprintf(key_str, "DR/%d", pid);
2351	tdb_delete(wcache->tdb, string_tdb_data(key_str));
2352
2353	fstr_sprintf(key_str, "DE/%d", pid);
2354	tdb_delete(wcache->tdb, string_tdb_data(key_str));
2355
2356	return;
2357}
2358
2359
2360BOOL lookup_cached_sid(TALLOC_CTX *mem_ctx, const DOM_SID *sid,
2361		       const char **domain_name, const char **name,
2362		       enum lsa_SidType *type)
2363{
2364	struct winbindd_domain *domain;
2365	struct winbind_cache *cache;
2366	struct cache_entry *centry = NULL;
2367	NTSTATUS status;
2368
2369	domain = find_lookup_domain_from_sid(sid);
2370	if (domain == NULL) {
2371		return False;
2372	}
2373
2374	cache = get_cache(domain);
2375
2376	if (cache->tdb == NULL) {
2377		return False;
2378	}
2379
2380	centry = wcache_fetch(cache, domain, "SN/%s", sid_string_static(sid));
2381	if (centry == NULL) {
2382		return False;
2383	}
2384
2385	if (NT_STATUS_IS_OK(centry->status)) {
2386		*type = (enum lsa_SidType)centry_uint32(centry);
2387		*domain_name = centry_string(centry, mem_ctx);
2388		*name = centry_string(centry, mem_ctx);
2389	}
2390
2391	status = centry->status;
2392	centry_free(centry);
2393	return NT_STATUS_IS_OK(status);
2394}
2395
2396BOOL lookup_cached_name(TALLOC_CTX *mem_ctx,
2397			const char *domain_name,
2398			const char *name,
2399			DOM_SID *sid,
2400			enum lsa_SidType *type)
2401{
2402	struct winbindd_domain *domain;
2403	struct winbind_cache *cache;
2404	struct cache_entry *centry = NULL;
2405	NTSTATUS status;
2406	fstring uname;
2407
2408	domain = find_lookup_domain_from_name(domain_name);
2409	if (domain == NULL) {
2410		return False;
2411	}
2412
2413	cache = get_cache(domain);
2414
2415	if (cache->tdb == NULL) {
2416		return False;
2417	}
2418
2419	fstrcpy(uname, name);
2420	strupper_m(uname);
2421
2422	centry = wcache_fetch(cache, domain, "NS/%s/%s", domain_name, uname);
2423	if (centry == NULL) {
2424		return False;
2425	}
2426
2427	if (NT_STATUS_IS_OK(centry->status)) {
2428		*type = (enum lsa_SidType)centry_uint32(centry);
2429		centry_sid(centry, mem_ctx, sid);
2430	}
2431
2432	status = centry->status;
2433	centry_free(centry);
2434
2435	return NT_STATUS_IS_OK(status);
2436}
2437
2438void cache_name2sid(struct winbindd_domain *domain,
2439		    const char *domain_name, const char *name,
2440		    enum lsa_SidType type, const DOM_SID *sid)
2441{
2442	refresh_sequence_number(domain, False);
2443	wcache_save_name_to_sid(domain, NT_STATUS_OK, domain_name, name,
2444				sid, type);
2445}
2446
2447/* delete all centries that don't have NT_STATUS_OK set */
2448/*
2449 * The original idea that this cache only contains centries has
2450 * been blurred - now other stuff gets put in here. Ensure we
2451 * ignore these things on cleanup.
2452 */
2453
2454static int traverse_fn_cleanup(TDB_CONTEXT *the_tdb, TDB_DATA kbuf,
2455			       TDB_DATA dbuf, void *state)
2456{
2457	struct cache_entry *centry;
2458
2459	if (is_non_centry_key(kbuf)) {
2460		return 0;
2461	}
2462
2463	centry = wcache_fetch_raw(kbuf.dptr);
2464	if (!centry) {
2465		return 0;
2466	}
2467
2468	if (!NT_STATUS_IS_OK(centry->status)) {
2469		DEBUG(10,("deleting centry %s\n", kbuf.dptr));
2470		tdb_delete(the_tdb, kbuf);
2471	}
2472
2473	centry_free(centry);
2474	return 0;
2475}
2476
2477/* flush the cache */
2478void wcache_flush_cache(void)
2479{
2480	if (!wcache)
2481		return;
2482	if (wcache->tdb) {
2483		tdb_close(wcache->tdb);
2484		wcache->tdb = NULL;
2485	}
2486	if (opt_nocache)
2487		return;
2488
2489	/* when working offline we must not clear the cache on restart */
2490	wcache->tdb = tdb_open_log(lock_path("winbindd_cache.tdb"),
2491				WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
2492				lp_winbind_offline_logon() ? TDB_DEFAULT : (TDB_DEFAULT | TDB_CLEAR_IF_FIRST),
2493				O_RDWR|O_CREAT, 0600);
2494
2495	if (!wcache->tdb) {
2496		DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
2497		return;
2498	}
2499
2500	tdb_traverse(wcache->tdb, traverse_fn_cleanup, NULL);
2501
2502	DEBUG(10,("wcache_flush_cache success\n"));
2503}
2504
2505/* Count cached creds */
2506
2507static int traverse_fn_cached_creds(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
2508			 	    void *state)
2509{
2510	int *cred_count = (int*)state;
2511
2512	if (strncmp(kbuf.dptr, "CRED/", 5) == 0) {
2513		(*cred_count)++;
2514	}
2515	return 0;
2516}
2517
2518NTSTATUS wcache_count_cached_creds(struct winbindd_domain *domain, int *count)
2519{
2520	struct winbind_cache *cache = get_cache(domain);
2521
2522	*count = 0;
2523
2524	if (!cache->tdb) {
2525		return NT_STATUS_INTERNAL_DB_ERROR;
2526	}
2527
2528	tdb_traverse(cache->tdb, traverse_fn_cached_creds, (void *)count);
2529
2530	return NT_STATUS_OK;
2531}
2532
2533struct cred_list {
2534	struct cred_list *prev, *next;
2535	TDB_DATA key;
2536	fstring name;
2537	time_t created;
2538};
2539static struct cred_list *wcache_cred_list;
2540
2541static int traverse_fn_get_credlist(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
2542				    void *state)
2543{
2544	struct cred_list *cred;
2545
2546	if (strncmp(kbuf.dptr, "CRED/", 5) == 0) {
2547
2548		cred = SMB_MALLOC_P(struct cred_list);
2549		if (cred == NULL) {
2550			DEBUG(0,("traverse_fn_remove_first_creds: failed to malloc new entry for list\n"));
2551			return -1;
2552		}
2553
2554		ZERO_STRUCTP(cred);
2555
2556		/* save a copy of the key */
2557
2558		fstrcpy(cred->name, kbuf.dptr);
2559		DLIST_ADD(wcache_cred_list, cred);
2560	}
2561
2562	return 0;
2563}
2564
2565NTSTATUS wcache_remove_oldest_cached_creds(struct winbindd_domain *domain, const DOM_SID *sid)
2566{
2567	struct winbind_cache *cache = get_cache(domain);
2568	NTSTATUS status;
2569	int ret;
2570	struct cred_list *cred, *oldest = NULL;
2571
2572	if (!cache->tdb) {
2573		return NT_STATUS_INTERNAL_DB_ERROR;
2574	}
2575
2576	/* we possibly already have an entry */
2577 	if (sid && NT_STATUS_IS_OK(wcache_cached_creds_exist(domain, sid))) {
2578
2579		fstring key_str;
2580
2581		DEBUG(11,("we already have an entry, deleting that\n"));
2582
2583		fstr_sprintf(key_str, "CRED/%s", sid_string_static(sid));
2584
2585		tdb_delete(cache->tdb, string_tdb_data(key_str));
2586
2587		return NT_STATUS_OK;
2588	}
2589
2590	ret = tdb_traverse(cache->tdb, traverse_fn_get_credlist, NULL);
2591	if (ret == 0) {
2592		return NT_STATUS_OK;
2593	} else if ((ret == -1) || (wcache_cred_list == NULL)) {
2594		return NT_STATUS_OBJECT_NAME_NOT_FOUND;
2595	}
2596
2597	ZERO_STRUCTP(oldest);
2598
2599	for (cred = wcache_cred_list; cred; cred = cred->next) {
2600
2601		TDB_DATA data;
2602		time_t t;
2603
2604		data = tdb_fetch(cache->tdb, make_tdb_data(cred->name, strlen(cred->name)));
2605		if (!data.dptr) {
2606			DEBUG(10,("wcache_remove_oldest_cached_creds: entry for [%s] not found\n",
2607				cred->name));
2608			status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
2609			goto done;
2610		}
2611
2612		t = IVAL(data.dptr, 0);
2613		SAFE_FREE(data.dptr);
2614
2615		if (!oldest) {
2616			oldest = SMB_MALLOC_P(struct cred_list);
2617			if (oldest == NULL) {
2618				status = NT_STATUS_NO_MEMORY;
2619				goto done;
2620			}
2621
2622			fstrcpy(oldest->name, cred->name);
2623			oldest->created = t;
2624			continue;
2625		}
2626
2627		if (t < oldest->created) {
2628			fstrcpy(oldest->name, cred->name);
2629			oldest->created = t;
2630		}
2631	}
2632
2633	if (tdb_delete(cache->tdb, string_tdb_data(oldest->name)) == 0) {
2634		status = NT_STATUS_OK;
2635	} else {
2636		status = NT_STATUS_UNSUCCESSFUL;
2637	}
2638done:
2639	SAFE_FREE(wcache_cred_list);
2640	SAFE_FREE(oldest);
2641
2642	return status;
2643}
2644
2645/* Change the global online/offline state. */
2646BOOL set_global_winbindd_state_offline(void)
2647{
2648	TDB_DATA data;
2649
2650	DEBUG(10,("set_global_winbindd_state_offline: offline requested.\n"));
2651
2652	/* Only go offline if someone has created
2653	   the key "WINBINDD_OFFLINE" in the cache tdb. */
2654
2655	if (wcache == NULL || wcache->tdb == NULL) {
2656		DEBUG(10,("set_global_winbindd_state_offline: wcache not open yet.\n"));
2657		return False;
2658	}
2659
2660	if (!lp_winbind_offline_logon()) {
2661		DEBUG(10,("set_global_winbindd_state_offline: rejecting.\n"));
2662		return False;
2663	}
2664
2665	if (global_winbindd_offline_state) {
2666		/* Already offline. */
2667		return True;
2668	}
2669
2670	data = tdb_fetch_bystring( wcache->tdb, "WINBINDD_OFFLINE" );
2671
2672	if (!data.dptr || data.dsize != 4) {
2673		DEBUG(10,("set_global_winbindd_state_offline: offline state not set.\n"));
2674		SAFE_FREE(data.dptr);
2675		return False;
2676	} else {
2677		DEBUG(10,("set_global_winbindd_state_offline: offline state set.\n"));
2678		global_winbindd_offline_state = True;
2679		SAFE_FREE(data.dptr);
2680		return True;
2681	}
2682}
2683
2684void set_global_winbindd_state_online(void)
2685{
2686	DEBUG(10,("set_global_winbindd_state_online: online requested.\n"));
2687
2688	if (!lp_winbind_offline_logon()) {
2689		DEBUG(10,("set_global_winbindd_state_online: rejecting.\n"));
2690		return;
2691	}
2692
2693	if (!global_winbindd_offline_state) {
2694		/* Already online. */
2695		return;
2696	}
2697	global_winbindd_offline_state = False;
2698
2699	if (!wcache->tdb) {
2700		return;
2701	}
2702
2703	/* Ensure there is no key "WINBINDD_OFFLINE" in the cache tdb. */
2704	tdb_delete_bystring(wcache->tdb, "WINBINDD_OFFLINE");
2705}
2706
2707BOOL get_global_winbindd_state_offline(void)
2708{
2709	return global_winbindd_offline_state;
2710}
2711
2712/* the cache backend methods are exposed via this structure */
2713struct winbindd_methods cache_methods = {
2714	True,
2715	query_user_list,
2716	enum_dom_groups,
2717	enum_local_groups,
2718	name_to_sid,
2719	sid_to_name,
2720	rids_to_names,
2721	query_user,
2722	lookup_usergroups,
2723	lookup_useraliases,
2724	lookup_groupmem,
2725	sequence_number,
2726	lockout_policy,
2727	password_policy,
2728	trusted_domains
2729};
2730