• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /asuswrt-rt-n18u-9.0.0.4.380.2695/release/src-rt/router/samba-3.5.8/source3/winbindd/
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-2007
8   Copyright (C) Volker Lendecke 2005
9   Copyright (C) Guenther Deschner 2005
10   Copyright (C) Michael Adam    2007
11
12   This program is free software; you can redistribute it and/or modify
13   it under the terms of the GNU General Public License as published by
14   the Free Software Foundation; either version 3 of the License, or
15   (at your option) any later version.
16
17   This program is distributed in the hope that it will be useful,
18   but WITHOUT ANY WARRANTY; without even the implied warranty of
19   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20   GNU General Public License for more details.
21
22   You should have received a copy of the GNU General Public License
23   along with this program.  If not, see <http://www.gnu.org/licenses/>.
24*/
25
26#include "includes.h"
27#include "winbindd.h"
28#include "tdb_validate.h"
29#include "../libcli/auth/libcli_auth.h"
30#include "../librpc/gen_ndr/ndr_wbint.h"
31
32#undef DBGC_CLASS
33#define DBGC_CLASS DBGC_WINBIND
34
35#define WINBINDD_CACHE_VERSION 1
36#define WINBINDD_CACHE_VERSION_KEYSTR "WINBINDD_CACHE_VERSION"
37
38extern struct winbindd_methods reconnect_methods;
39#ifdef HAVE_ADS
40extern struct winbindd_methods ads_methods;
41#endif
42extern struct winbindd_methods builtin_passdb_methods;
43
44/*
45 * JRA. KEEP THIS LIST UP TO DATE IF YOU ADD CACHE ENTRIES.
46 * Here are the list of entry types that are *not* stored
47 * as form struct cache_entry in the cache.
48 */
49
50static const char *non_centry_keys[] = {
51	"SEQNUM/",
52	"DR/",
53	"DE/",
54	"WINBINDD_OFFLINE",
55	WINBINDD_CACHE_VERSION_KEYSTR,
56	NULL
57};
58
59/************************************************************************
60 Is this key a non-centry type ?
61************************************************************************/
62
63static bool is_non_centry_key(TDB_DATA kbuf)
64{
65	int i;
66
67	if (kbuf.dptr == NULL || kbuf.dsize == 0) {
68		return false;
69	}
70	for (i = 0; non_centry_keys[i] != NULL; i++) {
71		size_t namelen = strlen(non_centry_keys[i]);
72		if (kbuf.dsize < namelen) {
73			continue;
74		}
75		if (strncmp(non_centry_keys[i], (const char *)kbuf.dptr, namelen) == 0) {
76			return true;
77		}
78	}
79	return false;
80}
81
82/* Global online/offline state - False when online. winbindd starts up online
83   and sets this to true if the first query fails and there's an entry in
84   the cache tdb telling us to stay offline. */
85
86static bool global_winbindd_offline_state;
87
88struct winbind_cache {
89	TDB_CONTEXT *tdb;
90};
91
92struct cache_entry {
93	NTSTATUS status;
94	uint32 sequence_number;
95	uint8 *data;
96	uint32 len, ofs;
97};
98
99void (*smb_panic_fn)(const char *const why) = smb_panic;
100
101#define WINBINDD_MAX_CACHE_SIZE (50*1024*1024)
102
103static struct winbind_cache *wcache;
104
105void winbindd_check_cache_size(time_t t)
106{
107	static time_t last_check_time;
108	struct stat st;
109
110	if (last_check_time == (time_t)0)
111		last_check_time = t;
112
113	if (t - last_check_time < 60 && t - last_check_time > 0)
114		return;
115
116	if (wcache == NULL || wcache->tdb == NULL) {
117		DEBUG(0, ("Unable to check size of tdb cache - cache not open !\n"));
118		return;
119	}
120
121	if (fstat(tdb_fd(wcache->tdb), &st) == -1) {
122		DEBUG(0, ("Unable to check size of tdb cache %s!\n", strerror(errno) ));
123		return;
124	}
125
126	if (st.st_size > WINBINDD_MAX_CACHE_SIZE) {
127		DEBUG(10,("flushing cache due to size (%lu) > (%lu)\n",
128			(unsigned long)st.st_size,
129			(unsigned long)WINBINDD_MAX_CACHE_SIZE));
130		wcache_flush_cache();
131	}
132}
133
134/* get the winbind_cache structure */
135static struct winbind_cache *get_cache(struct winbindd_domain *domain)
136{
137	struct winbind_cache *ret = wcache;
138
139	/* We have to know what type of domain we are dealing with first. */
140
141	if (domain->internal) {
142		domain->backend = &builtin_passdb_methods;
143		domain->initialized = True;
144	}
145	if ( !domain->initialized ) {
146		init_dc_connection( domain );
147	}
148
149	/*
150	   OK.  listen up becasue I'm only going to say this once.
151	   We have the following scenarios to consider
152	   (a) trusted AD domains on a Samba DC,
153	   (b) trusted AD domains and we are joined to a non-kerberos domain
154	   (c) trusted AD domains and we are joined to a kerberos (AD) domain
155
156	   For (a) we can always contact the trusted domain using krb5
157	   since we have the domain trust account password
158
159	   For (b) we can only use RPC since we have no way of
160	   getting a krb5 ticket in our own domain
161
162	   For (c) we can always use krb5 since we have a kerberos trust
163
164	   --jerry
165	 */
166
167	if (!domain->backend) {
168#ifdef HAVE_ADS
169		struct winbindd_domain *our_domain = domain;
170
171		/* find our domain first so we can figure out if we
172		   are joined to a kerberized domain */
173
174		if ( !domain->primary )
175			our_domain = find_our_domain();
176
177		if ((our_domain->active_directory || IS_DC)
178		    && domain->active_directory
179		    && !lp_winbind_rpc_only()) {
180			DEBUG(5,("get_cache: Setting ADS methods for domain %s\n", domain->name));
181			domain->backend = &ads_methods;
182		} else {
183#endif	/* HAVE_ADS */
184			DEBUG(5,("get_cache: Setting MS-RPC methods for domain %s\n", domain->name));
185			domain->backend = &reconnect_methods;
186#ifdef HAVE_ADS
187		}
188#endif	/* HAVE_ADS */
189	}
190
191	if (ret)
192		return ret;
193
194	ret = SMB_XMALLOC_P(struct winbind_cache);
195	ZERO_STRUCTP(ret);
196
197	wcache = ret;
198	wcache_flush_cache();
199
200	return ret;
201}
202
203/*
204  free a centry structure
205*/
206static void centry_free(struct cache_entry *centry)
207{
208	if (!centry)
209		return;
210	SAFE_FREE(centry->data);
211	free(centry);
212}
213
214static bool centry_check_bytes(struct cache_entry *centry, size_t nbytes)
215{
216	if (centry->len - centry->ofs < nbytes) {
217		DEBUG(0,("centry corruption? needed %u bytes, have %d\n",
218			 (unsigned int)nbytes,
219			 centry->len - centry->ofs));
220		return false;
221	}
222	return true;
223}
224
225/*
226  pull a uint32 from a cache entry
227*/
228static uint32 centry_uint32(struct cache_entry *centry)
229{
230	uint32 ret;
231
232	if (!centry_check_bytes(centry, 4)) {
233		smb_panic_fn("centry_uint32");
234	}
235	ret = IVAL(centry->data, centry->ofs);
236	centry->ofs += 4;
237	return ret;
238}
239
240/*
241  pull a uint16 from a cache entry
242*/
243static uint16 centry_uint16(struct cache_entry *centry)
244{
245	uint16 ret;
246	if (!centry_check_bytes(centry, 2)) {
247		smb_panic_fn("centry_uint16");
248	}
249	ret = CVAL(centry->data, centry->ofs);
250	centry->ofs += 2;
251	return ret;
252}
253
254/*
255  pull a uint8 from a cache entry
256*/
257static uint8 centry_uint8(struct cache_entry *centry)
258{
259	uint8 ret;
260	if (!centry_check_bytes(centry, 1)) {
261		smb_panic_fn("centry_uint8");
262	}
263	ret = CVAL(centry->data, centry->ofs);
264	centry->ofs += 1;
265	return ret;
266}
267
268/*
269  pull a NTTIME from a cache entry
270*/
271static NTTIME centry_nttime(struct cache_entry *centry)
272{
273	NTTIME ret;
274	if (!centry_check_bytes(centry, 8)) {
275		smb_panic_fn("centry_nttime");
276	}
277	ret = IVAL(centry->data, centry->ofs);
278	centry->ofs += 4;
279	ret += (uint64_t)IVAL(centry->data, centry->ofs) << 32;
280	centry->ofs += 4;
281	return ret;
282}
283
284/*
285  pull a time_t from a cache entry. time_t stored portably as a 64-bit time.
286*/
287static time_t centry_time(struct cache_entry *centry)
288{
289	return (time_t)centry_nttime(centry);
290}
291
292/* pull a string from a cache entry, using the supplied
293   talloc context
294*/
295static char *centry_string(struct cache_entry *centry, TALLOC_CTX *mem_ctx)
296{
297	uint32 len;
298	char *ret;
299
300	len = centry_uint8(centry);
301
302	if (len == 0xFF) {
303		/* a deliberate NULL string */
304		return NULL;
305	}
306
307	if (!centry_check_bytes(centry, (size_t)len)) {
308		smb_panic_fn("centry_string");
309	}
310
311	ret = TALLOC_ARRAY(mem_ctx, char, len+1);
312	if (!ret) {
313		smb_panic_fn("centry_string out of memory\n");
314	}
315	memcpy(ret,centry->data + centry->ofs, len);
316	ret[len] = 0;
317	centry->ofs += len;
318	return ret;
319}
320
321/* pull a hash16 from a cache entry, using the supplied
322   talloc context
323*/
324static char *centry_hash16(struct cache_entry *centry, TALLOC_CTX *mem_ctx)
325{
326	uint32 len;
327	char *ret;
328
329	len = centry_uint8(centry);
330
331	if (len != 16) {
332		DEBUG(0,("centry corruption? hash len (%u) != 16\n",
333			len ));
334		return NULL;
335	}
336
337	if (!centry_check_bytes(centry, 16)) {
338		return NULL;
339	}
340
341	ret = TALLOC_ARRAY(mem_ctx, char, 16);
342	if (!ret) {
343		smb_panic_fn("centry_hash out of memory\n");
344	}
345	memcpy(ret,centry->data + centry->ofs, 16);
346	centry->ofs += 16;
347	return ret;
348}
349
350/* pull a sid from a cache entry, using the supplied
351   talloc context
352*/
353static bool centry_sid(struct cache_entry *centry, struct dom_sid *sid)
354{
355	char *sid_string;
356	bool ret;
357
358	sid_string = centry_string(centry, talloc_tos());
359	if (sid_string == NULL) {
360		return false;
361	}
362	ret = string_to_sid(sid, sid_string);
363	TALLOC_FREE(sid_string);
364	return ret;
365}
366
367
368/*
369  pull a NTSTATUS from a cache entry
370*/
371static NTSTATUS centry_ntstatus(struct cache_entry *centry)
372{
373	NTSTATUS status;
374
375	status = NT_STATUS(centry_uint32(centry));
376	return status;
377}
378
379
380/* the server is considered down if it can't give us a sequence number */
381static bool wcache_server_down(struct winbindd_domain *domain)
382{
383	bool ret;
384
385	if (!wcache->tdb)
386		return false;
387
388	ret = (domain->sequence_number == DOM_SEQUENCE_NONE);
389
390	if (ret)
391		DEBUG(10,("wcache_server_down: server for Domain %s down\n",
392			domain->name ));
393	return ret;
394}
395
396static bool wcache_fetch_seqnum(const char *domain_name, uint32_t *seqnum,
397				uint32_t *last_seq_check)
398{
399	char *key;
400	TDB_DATA data;
401
402	if (wcache->tdb == NULL) {
403		DEBUG(10,("wcache_fetch_seqnum: tdb == NULL\n"));
404		return false;
405	}
406
407	key = talloc_asprintf(talloc_tos(), "SEQNUM/%s", domain_name);
408	if (key == NULL) {
409		DEBUG(10, ("talloc failed\n"));
410		return false;
411	}
412
413	data = tdb_fetch_bystring(wcache->tdb, key);
414	TALLOC_FREE(key);
415
416	if (data.dptr == NULL) {
417		DEBUG(10, ("wcache_fetch_seqnum: %s not found\n",
418			   domain_name));
419		return false;
420	}
421	if (data.dsize != 8) {
422		DEBUG(10, ("wcache_fetch_seqnum: invalid data size %d\n",
423			   (int)data.dsize));
424		SAFE_FREE(data.dptr);
425		return false;
426	}
427
428	*seqnum = IVAL(data.dptr, 0);
429	*last_seq_check = IVAL(data.dptr, 4);
430	SAFE_FREE(data.dptr);
431
432	return true;
433}
434
435static NTSTATUS fetch_cache_seqnum( struct winbindd_domain *domain, time_t now )
436{
437	uint32 last_check, time_diff;
438
439	if (!wcache_fetch_seqnum(domain->name, &domain->sequence_number,
440				 &last_check)) {
441		return NT_STATUS_UNSUCCESSFUL;
442	}
443	domain->last_seq_check = last_check;
444
445	/* have we expired? */
446
447	time_diff = now - domain->last_seq_check;
448	if ( time_diff > lp_winbind_cache_time() ) {
449		DEBUG(10,("fetch_cache_seqnum: timeout [%s][%u @ %u]\n",
450			domain->name, domain->sequence_number,
451			(uint32)domain->last_seq_check));
452		return NT_STATUS_UNSUCCESSFUL;
453	}
454
455	DEBUG(10,("fetch_cache_seqnum: success [%s][%u @ %u]\n",
456		domain->name, domain->sequence_number,
457		(uint32)domain->last_seq_check));
458
459	return NT_STATUS_OK;
460}
461
462bool wcache_store_seqnum(const char *domain_name, uint32_t seqnum,
463			 time_t last_seq_check)
464{
465	char *key_str;
466	uint8_t buf[8];
467	int ret;
468
469	if (wcache->tdb == NULL) {
470		DEBUG(10, ("wcache_store_seqnum: wcache->tdb == NULL\n"));
471		return false;
472	}
473
474	key_str = talloc_asprintf(talloc_tos(), "SEQNUM/%s", domain_name);
475	if (key_str == NULL) {
476		DEBUG(10, ("talloc_asprintf failed\n"));
477		return false;
478	}
479
480	SIVAL(buf, 0, seqnum);
481	SIVAL(buf, 4, last_seq_check);
482
483	ret = tdb_store_bystring(wcache->tdb, key_str,
484				 make_tdb_data(buf, sizeof(buf)), TDB_REPLACE);
485	TALLOC_FREE(key_str);
486	if (ret == -1) {
487		DEBUG(10, ("tdb_store_bystring failed: %s\n",
488			   tdb_errorstr(wcache->tdb)));
489		TALLOC_FREE(key_str);
490		return false;
491	}
492
493	DEBUG(10, ("wcache_store_seqnum: success [%s][%u @ %u]\n",
494		   domain_name, seqnum, (unsigned)last_seq_check));
495
496	return true;
497}
498
499static bool store_cache_seqnum( struct winbindd_domain *domain )
500{
501	return wcache_store_seqnum(domain->name, domain->sequence_number,
502				   domain->last_seq_check);
503}
504
505/*
506  refresh the domain sequence number. If force is true
507  then always refresh it, no matter how recently we fetched it
508*/
509
510static void refresh_sequence_number(struct winbindd_domain *domain, bool force)
511{
512	NTSTATUS status;
513	unsigned time_diff;
514	time_t t = time(NULL);
515	unsigned cache_time = lp_winbind_cache_time();
516
517	if (is_domain_offline(domain)) {
518		return;
519	}
520
521	get_cache( domain );
522
523#if 0	/* JERRY -- disable as the default cache time is now 5 minutes */
524	/* trying to reconnect is expensive, don't do it too often */
525	if (domain->sequence_number == DOM_SEQUENCE_NONE) {
526		cache_time *= 8;
527	}
528#endif
529
530	time_diff = t - domain->last_seq_check;
531
532	/* see if we have to refetch the domain sequence number */
533	if (!force && (time_diff < cache_time) &&
534			(domain->sequence_number != DOM_SEQUENCE_NONE) &&
535			NT_STATUS_IS_OK(domain->last_status)) {
536		DEBUG(10, ("refresh_sequence_number: %s time ok\n", domain->name));
537		goto done;
538	}
539
540	/* try to get the sequence number from the tdb cache first */
541	/* this will update the timestamp as well */
542
543	status = fetch_cache_seqnum( domain, t );
544	if (NT_STATUS_IS_OK(status) &&
545			(domain->sequence_number != DOM_SEQUENCE_NONE) &&
546			NT_STATUS_IS_OK(domain->last_status)) {
547		goto done;
548	}
549
550	/* important! make sure that we know if this is a native
551	   mode domain or not.  And that we can contact it. */
552
553	if ( winbindd_can_contact_domain( domain ) ) {
554		status = domain->backend->sequence_number(domain,
555							  &domain->sequence_number);
556	} else {
557		/* just use the current time */
558		status = NT_STATUS_OK;
559		domain->sequence_number = time(NULL);
560	}
561
562
563	/* the above call could have set our domain->backend to NULL when
564	 * coming from offline to online mode, make sure to reinitialize the
565	 * backend - Guenther */
566	get_cache( domain );
567
568	if (!NT_STATUS_IS_OK(status)) {
569		DEBUG(10,("refresh_sequence_number: failed with %s\n", nt_errstr(status)));
570		domain->sequence_number = DOM_SEQUENCE_NONE;
571	}
572
573	domain->last_status = status;
574	domain->last_seq_check = time(NULL);
575
576	/* save the new sequence number in the cache */
577	store_cache_seqnum( domain );
578
579done:
580	DEBUG(10, ("refresh_sequence_number: %s seq number is now %d\n",
581		   domain->name, domain->sequence_number));
582
583	return;
584}
585
586/*
587  decide if a cache entry has expired
588*/
589static bool centry_expired(struct winbindd_domain *domain, const char *keystr, struct cache_entry *centry)
590{
591	/* If we've been told to be offline - stay in that state... */
592	if (lp_winbind_offline_logon() && global_winbindd_offline_state) {
593		DEBUG(10,("centry_expired: Key %s for domain %s valid as winbindd is globally offline.\n",
594			keystr, domain->name ));
595		return false;
596	}
597
598	/* when the domain is offline return the cached entry.
599	 * This deals with transient offline states... */
600
601	if (!domain->online) {
602		DEBUG(10,("centry_expired: Key %s for domain %s valid as domain is offline.\n",
603			keystr, domain->name ));
604		return false;
605	}
606
607	/* if the server is OK and our cache entry came from when it was down then
608	   the entry is invalid */
609	if ((domain->sequence_number != DOM_SEQUENCE_NONE) &&
610	    (centry->sequence_number == DOM_SEQUENCE_NONE)) {
611		DEBUG(10,("centry_expired: Key %s for domain %s invalid sequence.\n",
612			keystr, domain->name ));
613		return true;
614	}
615
616	/* if the server is down or the cache entry is not older than the
617	   current sequence number then it is OK */
618	if (wcache_server_down(domain) ||
619	    centry->sequence_number == domain->sequence_number) {
620		DEBUG(10,("centry_expired: Key %s for domain %s is good.\n",
621			keystr, domain->name ));
622		return false;
623	}
624
625	DEBUG(10,("centry_expired: Key %s for domain %s expired\n",
626		keystr, domain->name ));
627
628	/* it's expired */
629	return true;
630}
631
632static struct cache_entry *wcache_fetch_raw(char *kstr)
633{
634	TDB_DATA data;
635	struct cache_entry *centry;
636	TDB_DATA key;
637
638	key = string_tdb_data(kstr);
639	data = tdb_fetch(wcache->tdb, key);
640	if (!data.dptr) {
641		/* a cache miss */
642		return NULL;
643	}
644
645	centry = SMB_XMALLOC_P(struct cache_entry);
646	centry->data = (unsigned char *)data.dptr;
647	centry->len = data.dsize;
648	centry->ofs = 0;
649
650	if (centry->len < 8) {
651		/* huh? corrupt cache? */
652		DEBUG(10,("wcache_fetch_raw: Corrupt cache for key %s (len < 8) ?\n", kstr));
653		centry_free(centry);
654		return NULL;
655	}
656
657	centry->status = centry_ntstatus(centry);
658	centry->sequence_number = centry_uint32(centry);
659
660	return centry;
661}
662
663/*
664  fetch an entry from the cache, with a varargs key. auto-fetch the sequence
665  number and return status
666*/
667static struct cache_entry *wcache_fetch(struct winbind_cache *cache,
668					struct winbindd_domain *domain,
669					const char *format, ...) PRINTF_ATTRIBUTE(3,4);
670static struct cache_entry *wcache_fetch(struct winbind_cache *cache,
671					struct winbindd_domain *domain,
672					const char *format, ...)
673{
674	va_list ap;
675	char *kstr;
676	struct cache_entry *centry;
677
678	if (!winbindd_use_cache()) {
679		return NULL;
680	}
681
682	refresh_sequence_number(domain, false);
683
684	va_start(ap, format);
685	smb_xvasprintf(&kstr, format, ap);
686	va_end(ap);
687
688	centry = wcache_fetch_raw(kstr);
689	if (centry == NULL) {
690		free(kstr);
691		return NULL;
692	}
693
694	if (centry_expired(domain, kstr, centry)) {
695
696		DEBUG(10,("wcache_fetch: entry %s expired for domain %s\n",
697			 kstr, domain->name ));
698
699		centry_free(centry);
700		free(kstr);
701		return NULL;
702	}
703
704	DEBUG(10,("wcache_fetch: returning entry %s for domain %s\n",
705		 kstr, domain->name ));
706
707	free(kstr);
708	return centry;
709}
710
711static void wcache_delete(const char *format, ...) PRINTF_ATTRIBUTE(1,2);
712static void wcache_delete(const char *format, ...)
713{
714	va_list ap;
715	char *kstr;
716	TDB_DATA key;
717
718	va_start(ap, format);
719	smb_xvasprintf(&kstr, format, ap);
720	va_end(ap);
721
722	key = string_tdb_data(kstr);
723
724	tdb_delete(wcache->tdb, key);
725	free(kstr);
726}
727
728/*
729  make sure we have at least len bytes available in a centry
730*/
731static void centry_expand(struct cache_entry *centry, uint32 len)
732{
733	if (centry->len - centry->ofs >= len)
734		return;
735	centry->len *= 2;
736	centry->data = SMB_REALLOC_ARRAY(centry->data, unsigned char,
737					 centry->len);
738	if (!centry->data) {
739		DEBUG(0,("out of memory: needed %d bytes in centry_expand\n", centry->len));
740		smb_panic_fn("out of memory in centry_expand");
741	}
742}
743
744/*
745  push a uint32 into a centry
746*/
747static void centry_put_uint32(struct cache_entry *centry, uint32 v)
748{
749	centry_expand(centry, 4);
750	SIVAL(centry->data, centry->ofs, v);
751	centry->ofs += 4;
752}
753
754/*
755  push a uint16 into a centry
756*/
757static void centry_put_uint16(struct cache_entry *centry, uint16 v)
758{
759	centry_expand(centry, 2);
760	SIVAL(centry->data, centry->ofs, v);
761	centry->ofs += 2;
762}
763
764/*
765  push a uint8 into a centry
766*/
767static void centry_put_uint8(struct cache_entry *centry, uint8 v)
768{
769	centry_expand(centry, 1);
770	SCVAL(centry->data, centry->ofs, v);
771	centry->ofs += 1;
772}
773
774/*
775   push a string into a centry
776 */
777static void centry_put_string(struct cache_entry *centry, const char *s)
778{
779	int len;
780
781	if (!s) {
782		/* null strings are marked as len 0xFFFF */
783		centry_put_uint8(centry, 0xFF);
784		return;
785	}
786
787	len = strlen(s);
788	/* can't handle more than 254 char strings. Truncating is probably best */
789	if (len > 254) {
790		DEBUG(10,("centry_put_string: truncating len (%d) to: 254\n", len));
791		len = 254;
792	}
793	centry_put_uint8(centry, len);
794	centry_expand(centry, len);
795	memcpy(centry->data + centry->ofs, s, len);
796	centry->ofs += len;
797}
798
799/*
800   push a 16 byte hash into a centry - treat as 16 byte string.
801 */
802static void centry_put_hash16(struct cache_entry *centry, const uint8 val[16])
803{
804	centry_put_uint8(centry, 16);
805	centry_expand(centry, 16);
806	memcpy(centry->data + centry->ofs, val, 16);
807	centry->ofs += 16;
808}
809
810static void centry_put_sid(struct cache_entry *centry, const DOM_SID *sid)
811{
812	fstring sid_string;
813	centry_put_string(centry, sid_to_fstring(sid_string, sid));
814}
815
816
817/*
818  put NTSTATUS into a centry
819*/
820static void centry_put_ntstatus(struct cache_entry *centry, NTSTATUS status)
821{
822	uint32 status_value = NT_STATUS_V(status);
823	centry_put_uint32(centry, status_value);
824}
825
826
827/*
828  push a NTTIME into a centry
829*/
830static void centry_put_nttime(struct cache_entry *centry, NTTIME nt)
831{
832	centry_expand(centry, 8);
833	SIVAL(centry->data, centry->ofs, nt & 0xFFFFFFFF);
834	centry->ofs += 4;
835	SIVAL(centry->data, centry->ofs, nt >> 32);
836	centry->ofs += 4;
837}
838
839/*
840  push a time_t into a centry - use a 64 bit size.
841  NTTIME here is being used as a convenient 64-bit size.
842*/
843static void centry_put_time(struct cache_entry *centry, time_t t)
844{
845	NTTIME nt = (NTTIME)t;
846	centry_put_nttime(centry, nt);
847}
848
849/*
850  start a centry for output. When finished, call centry_end()
851*/
852struct cache_entry *centry_start(struct winbindd_domain *domain, NTSTATUS status)
853{
854	struct cache_entry *centry;
855
856	if (!wcache->tdb)
857		return NULL;
858
859	centry = SMB_XMALLOC_P(struct cache_entry);
860
861	centry->len = 8192; /* reasonable default */
862	centry->data = SMB_XMALLOC_ARRAY(uint8, centry->len);
863	centry->ofs = 0;
864	centry->sequence_number = domain->sequence_number;
865	centry_put_ntstatus(centry, status);
866	centry_put_uint32(centry, centry->sequence_number);
867	return centry;
868}
869
870/*
871  finish a centry and write it to the tdb
872*/
873static void centry_end(struct cache_entry *centry, const char *format, ...) PRINTF_ATTRIBUTE(2,3);
874static void centry_end(struct cache_entry *centry, const char *format, ...)
875{
876	va_list ap;
877	char *kstr;
878	TDB_DATA key, data;
879
880	if (!winbindd_use_cache()) {
881		return;
882	}
883
884	va_start(ap, format);
885	smb_xvasprintf(&kstr, format, ap);
886	va_end(ap);
887
888	key = string_tdb_data(kstr);
889	data.dptr = centry->data;
890	data.dsize = centry->ofs;
891
892	tdb_store(wcache->tdb, key, data, TDB_REPLACE);
893	free(kstr);
894}
895
896static void wcache_save_name_to_sid(struct winbindd_domain *domain,
897				    NTSTATUS status, const char *domain_name,
898				    const char *name, const DOM_SID *sid,
899				    enum lsa_SidType type)
900{
901	struct cache_entry *centry;
902	fstring uname;
903
904	centry = centry_start(domain, status);
905	if (!centry)
906		return;
907	centry_put_uint32(centry, type);
908	centry_put_sid(centry, sid);
909	fstrcpy(uname, name);
910	strupper_m(uname);
911	centry_end(centry, "NS/%s/%s", domain_name, uname);
912	DEBUG(10,("wcache_save_name_to_sid: %s\\%s -> %s (%s)\n", domain_name,
913		  uname, sid_string_dbg(sid), nt_errstr(status)));
914	centry_free(centry);
915}
916
917static void wcache_save_sid_to_name(struct winbindd_domain *domain, NTSTATUS status,
918				    const DOM_SID *sid, const char *domain_name, const char *name, enum lsa_SidType type)
919{
920	struct cache_entry *centry;
921	fstring sid_string;
922
923	centry = centry_start(domain, status);
924	if (!centry)
925		return;
926
927	if (NT_STATUS_IS_OK(status)) {
928		centry_put_uint32(centry, type);
929		centry_put_string(centry, domain_name);
930		centry_put_string(centry, name);
931	}
932
933	centry_end(centry, "SN/%s", sid_to_fstring(sid_string, sid));
934	DEBUG(10,("wcache_save_sid_to_name: %s -> %s (%s)\n", sid_string,
935		  name, nt_errstr(status)));
936	centry_free(centry);
937}
938
939
940static void wcache_save_user(struct winbindd_domain *domain, NTSTATUS status,
941			     struct wbint_userinfo *info)
942{
943	struct cache_entry *centry;
944	fstring sid_string;
945
946	if (is_null_sid(&info->user_sid)) {
947		return;
948	}
949
950	centry = centry_start(domain, status);
951	if (!centry)
952		return;
953	centry_put_string(centry, info->acct_name);
954	centry_put_string(centry, info->full_name);
955	centry_put_string(centry, info->homedir);
956	centry_put_string(centry, info->shell);
957	centry_put_uint32(centry, info->primary_gid);
958	centry_put_sid(centry, &info->user_sid);
959	centry_put_sid(centry, &info->group_sid);
960	centry_end(centry, "U/%s", sid_to_fstring(sid_string,
961						  &info->user_sid));
962	DEBUG(10,("wcache_save_user: %s (acct_name %s)\n", sid_string, info->acct_name));
963	centry_free(centry);
964}
965
966static void wcache_save_lockout_policy(struct winbindd_domain *domain,
967				       NTSTATUS status,
968				       struct samr_DomInfo12 *lockout_policy)
969{
970	struct cache_entry *centry;
971
972	centry = centry_start(domain, status);
973	if (!centry)
974		return;
975
976	centry_put_nttime(centry, lockout_policy->lockout_duration);
977	centry_put_nttime(centry, lockout_policy->lockout_window);
978	centry_put_uint16(centry, lockout_policy->lockout_threshold);
979
980	centry_end(centry, "LOC_POL/%s", domain->name);
981
982	DEBUG(10,("wcache_save_lockout_policy: %s\n", domain->name));
983
984	centry_free(centry);
985}
986
987
988
989static void wcache_save_password_policy(struct winbindd_domain *domain,
990					NTSTATUS status,
991					struct samr_DomInfo1 *policy)
992{
993	struct cache_entry *centry;
994
995	centry = centry_start(domain, status);
996	if (!centry)
997		return;
998
999	centry_put_uint16(centry, policy->min_password_length);
1000	centry_put_uint16(centry, policy->password_history_length);
1001	centry_put_uint32(centry, policy->password_properties);
1002	centry_put_nttime(centry, policy->max_password_age);
1003	centry_put_nttime(centry, policy->min_password_age);
1004
1005	centry_end(centry, "PWD_POL/%s", domain->name);
1006
1007	DEBUG(10,("wcache_save_password_policy: %s\n", domain->name));
1008
1009	centry_free(centry);
1010}
1011
1012/***************************************************************************
1013 ***************************************************************************/
1014
1015static void wcache_save_username_alias(struct winbindd_domain *domain,
1016				       NTSTATUS status,
1017				       const char *name, const char *alias)
1018{
1019	struct cache_entry *centry;
1020	fstring uname;
1021
1022	if ( (centry = centry_start(domain, status)) == NULL )
1023		return;
1024
1025	centry_put_string( centry, alias );
1026
1027	fstrcpy(uname, name);
1028	strupper_m(uname);
1029	centry_end(centry, "NSS/NA/%s", uname);
1030
1031	DEBUG(10,("wcache_save_username_alias: %s -> %s\n", name, alias ));
1032
1033	centry_free(centry);
1034}
1035
1036static void wcache_save_alias_username(struct winbindd_domain *domain,
1037				       NTSTATUS status,
1038				       const char *alias, const char *name)
1039{
1040	struct cache_entry *centry;
1041	fstring uname;
1042
1043	if ( (centry = centry_start(domain, status)) == NULL )
1044		return;
1045
1046	centry_put_string( centry, name );
1047
1048	fstrcpy(uname, alias);
1049	strupper_m(uname);
1050	centry_end(centry, "NSS/AN/%s", uname);
1051
1052	DEBUG(10,("wcache_save_alias_username: %s -> %s\n", alias, name ));
1053
1054	centry_free(centry);
1055}
1056
1057/***************************************************************************
1058 ***************************************************************************/
1059
1060NTSTATUS resolve_username_to_alias( TALLOC_CTX *mem_ctx,
1061				    struct winbindd_domain *domain,
1062				    const char *name, char **alias )
1063{
1064	struct winbind_cache *cache = get_cache(domain);
1065	struct cache_entry *centry = NULL;
1066	NTSTATUS status;
1067	char *upper_name;
1068
1069	if ( domain->internal )
1070		return NT_STATUS_NOT_SUPPORTED;
1071
1072	if (!cache->tdb)
1073		goto do_query;
1074
1075	if ( (upper_name = SMB_STRDUP(name)) == NULL )
1076		return NT_STATUS_NO_MEMORY;
1077	strupper_m(upper_name);
1078
1079	centry = wcache_fetch(cache, domain, "NSS/NA/%s", upper_name);
1080
1081	SAFE_FREE( upper_name );
1082
1083	if (!centry)
1084		goto do_query;
1085
1086	status = centry->status;
1087
1088	if (!NT_STATUS_IS_OK(status)) {
1089		centry_free(centry);
1090		return status;
1091	}
1092
1093	*alias = centry_string( centry, mem_ctx );
1094
1095	centry_free(centry);
1096
1097	DEBUG(10,("resolve_username_to_alias: [Cached] - mapped %s to %s\n",
1098		  name, *alias ? *alias : "(none)"));
1099
1100	return (*alias) ? NT_STATUS_OK : NT_STATUS_OBJECT_NAME_NOT_FOUND;
1101
1102do_query:
1103
1104	/* If its not in cache and we are offline, then fail */
1105
1106	if ( get_global_winbindd_state_offline() || !domain->online ) {
1107		DEBUG(8,("resolve_username_to_alias: rejecting query "
1108			 "in offline mode\n"));
1109		return NT_STATUS_NOT_FOUND;
1110	}
1111
1112	status = nss_map_to_alias( mem_ctx, domain->name, name, alias );
1113
1114	if ( NT_STATUS_IS_OK( status ) ) {
1115		wcache_save_username_alias(domain, status, name, *alias);
1116	}
1117
1118	if ( NT_STATUS_EQUAL( status, NT_STATUS_NONE_MAPPED ) ) {
1119		wcache_save_username_alias(domain, status, name, "(NULL)");
1120	}
1121
1122	DEBUG(5,("resolve_username_to_alias: backend query returned %s\n",
1123		 nt_errstr(status)));
1124
1125	if ( NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ) {
1126		set_domain_offline( domain );
1127	}
1128
1129	return status;
1130}
1131
1132/***************************************************************************
1133 ***************************************************************************/
1134
1135NTSTATUS resolve_alias_to_username( TALLOC_CTX *mem_ctx,
1136				    struct winbindd_domain *domain,
1137				    const char *alias, char **name )
1138{
1139	struct winbind_cache *cache = get_cache(domain);
1140	struct cache_entry *centry = NULL;
1141	NTSTATUS status;
1142	char *upper_name;
1143
1144	if ( domain->internal )
1145		return  NT_STATUS_NOT_SUPPORTED;
1146
1147	if (!cache->tdb)
1148		goto do_query;
1149
1150	if ( (upper_name = SMB_STRDUP(alias)) == NULL )
1151		return NT_STATUS_NO_MEMORY;
1152	strupper_m(upper_name);
1153
1154	centry = wcache_fetch(cache, domain, "NSS/AN/%s", upper_name);
1155
1156	SAFE_FREE( upper_name );
1157
1158	if (!centry)
1159		goto do_query;
1160
1161	status = centry->status;
1162
1163	if (!NT_STATUS_IS_OK(status)) {
1164		centry_free(centry);
1165		return status;
1166	}
1167
1168	*name = centry_string( centry, mem_ctx );
1169
1170	centry_free(centry);
1171
1172	DEBUG(10,("resolve_alias_to_username: [Cached] - mapped %s to %s\n",
1173		  alias, *name ? *name : "(none)"));
1174
1175	return (*name) ? NT_STATUS_OK : NT_STATUS_OBJECT_NAME_NOT_FOUND;
1176
1177do_query:
1178
1179	/* If its not in cache and we are offline, then fail */
1180
1181	if ( get_global_winbindd_state_offline() || !domain->online ) {
1182		DEBUG(8,("resolve_alias_to_username: rejecting query "
1183			 "in offline mode\n"));
1184		return NT_STATUS_NOT_FOUND;
1185	}
1186
1187	/* an alias cannot contain a domain prefix or '@' */
1188
1189	if (strchr(alias, '\\') || strchr(alias, '@')) {
1190		DEBUG(10,("resolve_alias_to_username: skipping fully "
1191			  "qualified name %s\n", alias));
1192		return NT_STATUS_OBJECT_NAME_INVALID;
1193	}
1194
1195	status = nss_map_from_alias( mem_ctx, domain->name, alias, name );
1196
1197	if ( NT_STATUS_IS_OK( status ) ) {
1198		wcache_save_alias_username( domain, status, alias, *name );
1199	}
1200
1201	if (NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED)) {
1202		wcache_save_alias_username(domain, status, alias, "(NULL)");
1203	}
1204
1205	DEBUG(5,("resolve_alias_to_username: backend query returned %s\n",
1206		 nt_errstr(status)));
1207
1208	if ( NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ) {
1209		set_domain_offline( domain );
1210	}
1211
1212	return status;
1213}
1214
1215NTSTATUS wcache_cached_creds_exist(struct winbindd_domain *domain, const DOM_SID *sid)
1216{
1217	struct winbind_cache *cache = get_cache(domain);
1218	TDB_DATA data;
1219	fstring key_str, tmp;
1220	uint32 rid;
1221
1222	if (!cache->tdb) {
1223		return NT_STATUS_INTERNAL_DB_ERROR;
1224	}
1225
1226	if (is_null_sid(sid)) {
1227		return NT_STATUS_INVALID_SID;
1228	}
1229
1230	if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
1231		return NT_STATUS_INVALID_SID;
1232	}
1233
1234	fstr_sprintf(key_str, "CRED/%s", sid_to_fstring(tmp, sid));
1235
1236	data = tdb_fetch(cache->tdb, string_tdb_data(key_str));
1237	if (!data.dptr) {
1238		return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1239	}
1240
1241	SAFE_FREE(data.dptr);
1242	return NT_STATUS_OK;
1243}
1244
1245/* Lookup creds for a SID - copes with old (unsalted) creds as well
1246   as new salted ones. */
1247
1248NTSTATUS wcache_get_creds(struct winbindd_domain *domain,
1249			  TALLOC_CTX *mem_ctx,
1250			  const DOM_SID *sid,
1251			  const uint8 **cached_nt_pass,
1252			  const uint8 **cached_salt)
1253{
1254	struct winbind_cache *cache = get_cache(domain);
1255	struct cache_entry *centry = NULL;
1256	NTSTATUS status;
1257	time_t t;
1258	uint32 rid;
1259	fstring tmp;
1260
1261	if (!cache->tdb) {
1262		return NT_STATUS_INTERNAL_DB_ERROR;
1263	}
1264
1265	if (is_null_sid(sid)) {
1266		return NT_STATUS_INVALID_SID;
1267	}
1268
1269	if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
1270		return NT_STATUS_INVALID_SID;
1271	}
1272
1273	/* Try and get a salted cred first. If we can't
1274	   fall back to an unsalted cred. */
1275
1276	centry = wcache_fetch(cache, domain, "CRED/%s",
1277			      sid_to_fstring(tmp, sid));
1278	if (!centry) {
1279		DEBUG(10,("wcache_get_creds: entry for [CRED/%s] not found\n",
1280			  sid_string_dbg(sid)));
1281		return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1282	}
1283
1284	t = centry_time(centry);
1285
1286	/* In the salted case this isn't actually the nt_hash itself,
1287	   but the MD5 of the salt + nt_hash. Let the caller
1288	   sort this out. It can tell as we only return the cached_salt
1289	   if we are returning a salted cred. */
1290
1291	*cached_nt_pass = (const uint8 *)centry_hash16(centry, mem_ctx);
1292	if (*cached_nt_pass == NULL) {
1293		fstring sidstr;
1294
1295		sid_to_fstring(sidstr, sid);
1296
1297		/* Bad (old) cred cache. Delete and pretend we
1298		   don't have it. */
1299		DEBUG(0,("wcache_get_creds: bad entry for [CRED/%s] - deleting\n",
1300				sidstr));
1301		wcache_delete("CRED/%s", sidstr);
1302		centry_free(centry);
1303		return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1304	}
1305
1306	/* We only have 17 bytes more data in the salted cred case. */
1307	if (centry->len - centry->ofs == 17) {
1308		*cached_salt = (const uint8 *)centry_hash16(centry, mem_ctx);
1309	} else {
1310		*cached_salt = NULL;
1311	}
1312
1313	dump_data_pw("cached_nt_pass", *cached_nt_pass, NT_HASH_LEN);
1314	if (*cached_salt) {
1315		dump_data_pw("cached_salt", *cached_salt, NT_HASH_LEN);
1316	}
1317
1318	status = centry->status;
1319
1320	DEBUG(10,("wcache_get_creds: [Cached] - cached creds for user %s status: %s\n",
1321		  sid_string_dbg(sid), nt_errstr(status) ));
1322
1323	centry_free(centry);
1324	return status;
1325}
1326
1327/* Store creds for a SID - only writes out new salted ones. */
1328
1329NTSTATUS wcache_save_creds(struct winbindd_domain *domain,
1330			   TALLOC_CTX *mem_ctx,
1331			   const DOM_SID *sid,
1332			   const uint8 nt_pass[NT_HASH_LEN])
1333{
1334	struct cache_entry *centry;
1335	fstring sid_string;
1336	uint32 rid;
1337	uint8 cred_salt[NT_HASH_LEN];
1338	uint8 salted_hash[NT_HASH_LEN];
1339
1340	if (is_null_sid(sid)) {
1341		return NT_STATUS_INVALID_SID;
1342	}
1343
1344	if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
1345		return NT_STATUS_INVALID_SID;
1346	}
1347
1348	centry = centry_start(domain, NT_STATUS_OK);
1349	if (!centry) {
1350		return NT_STATUS_INTERNAL_DB_ERROR;
1351	}
1352
1353	dump_data_pw("nt_pass", nt_pass, NT_HASH_LEN);
1354
1355	centry_put_time(centry, time(NULL));
1356
1357	/* Create a salt and then salt the hash. */
1358	generate_random_buffer(cred_salt, NT_HASH_LEN);
1359	E_md5hash(cred_salt, nt_pass, salted_hash);
1360
1361	centry_put_hash16(centry, salted_hash);
1362	centry_put_hash16(centry, cred_salt);
1363	centry_end(centry, "CRED/%s", sid_to_fstring(sid_string, sid));
1364
1365	DEBUG(10,("wcache_save_creds: %s\n", sid_string));
1366
1367	centry_free(centry);
1368
1369	return NT_STATUS_OK;
1370}
1371
1372
1373/* Query display info. This is the basic user list fn */
1374static NTSTATUS query_user_list(struct winbindd_domain *domain,
1375				TALLOC_CTX *mem_ctx,
1376				uint32 *num_entries,
1377				struct wbint_userinfo **info)
1378{
1379	struct winbind_cache *cache = get_cache(domain);
1380	struct cache_entry *centry = NULL;
1381	NTSTATUS status;
1382	unsigned int i, retry;
1383	bool old_status = domain->online;
1384
1385	if (!cache->tdb)
1386		goto do_query;
1387
1388	centry = wcache_fetch(cache, domain, "UL/%s", domain->name);
1389	if (!centry)
1390		goto do_query;
1391
1392do_fetch_cache:
1393	*num_entries = centry_uint32(centry);
1394
1395	if (*num_entries == 0)
1396		goto do_cached;
1397
1398	(*info) = TALLOC_ARRAY(mem_ctx, struct wbint_userinfo, *num_entries);
1399	if (! (*info)) {
1400		smb_panic_fn("query_user_list out of memory");
1401	}
1402	for (i=0; i<(*num_entries); i++) {
1403		(*info)[i].acct_name = centry_string(centry, mem_ctx);
1404		(*info)[i].full_name = centry_string(centry, mem_ctx);
1405		(*info)[i].homedir = centry_string(centry, mem_ctx);
1406		(*info)[i].shell = centry_string(centry, mem_ctx);
1407		centry_sid(centry, &(*info)[i].user_sid);
1408		centry_sid(centry, &(*info)[i].group_sid);
1409	}
1410
1411do_cached:
1412	status = centry->status;
1413
1414	DEBUG(10,("query_user_list: [Cached] - cached list for domain %s status: %s\n",
1415		domain->name, nt_errstr(status) ));
1416
1417	centry_free(centry);
1418	return status;
1419
1420do_query:
1421	*num_entries = 0;
1422	*info = NULL;
1423
1424	/* Return status value returned by seq number check */
1425
1426	if (!NT_STATUS_IS_OK(domain->last_status))
1427		return domain->last_status;
1428
1429	/* Put the query_user_list() in a retry loop.  There appears to be
1430	 * some bug either with Windows 2000 or Samba's handling of large
1431	 * rpc replies.  This manifests itself as sudden disconnection
1432	 * at a random point in the enumeration of a large (60k) user list.
1433	 * The retry loop simply tries the operation again. )-:  It's not
1434	 * pretty but an acceptable workaround until we work out what the
1435	 * real problem is. */
1436
1437	retry = 0;
1438	do {
1439
1440		DEBUG(10,("query_user_list: [Cached] - doing backend query for list for domain %s\n",
1441			domain->name ));
1442
1443		status = domain->backend->query_user_list(domain, mem_ctx, num_entries, info);
1444		if (!NT_STATUS_IS_OK(status)) {
1445			DEBUG(3, ("query_user_list: returned 0x%08x, "
1446				  "retrying\n", NT_STATUS_V(status)));
1447		}
1448		if (NT_STATUS_EQUAL(status, NT_STATUS_UNSUCCESSFUL)) {
1449			DEBUG(3, ("query_user_list: flushing "
1450				  "connection cache\n"));
1451			invalidate_cm_connection(&domain->conn);
1452		}
1453		if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1454		    NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1455			if (!domain->internal && old_status) {
1456				set_domain_offline(domain);
1457			}
1458			/* store partial response. */
1459			if (*num_entries > 0) {
1460				/*
1461				 * humm, what about the status used for cache?
1462				 * Should it be NT_STATUS_OK?
1463				 */
1464				break;
1465			}
1466			/*
1467			 * domain is offline now, and there is no user entries,
1468			 * try to fetch from cache again.
1469			 */
1470			if (cache->tdb && !domain->online && !domain->internal && old_status) {
1471				centry = wcache_fetch(cache, domain, "UL/%s", domain->name);
1472				/* partial response... */
1473				if (!centry) {
1474					goto skip_save;
1475				} else {
1476					goto do_fetch_cache;
1477				}
1478			} else {
1479				goto skip_save;
1480			}
1481		}
1482
1483	} while (NT_STATUS_V(status) == NT_STATUS_V(NT_STATUS_UNSUCCESSFUL) &&
1484		 (retry++ < 5));
1485
1486	/* and save it */
1487	refresh_sequence_number(domain, false);
1488	if (!NT_STATUS_IS_OK(status)) {
1489		return status;
1490	}
1491	centry = centry_start(domain, status);
1492	if (!centry)
1493		goto skip_save;
1494	centry_put_uint32(centry, *num_entries);
1495	for (i=0; i<(*num_entries); i++) {
1496		centry_put_string(centry, (*info)[i].acct_name);
1497		centry_put_string(centry, (*info)[i].full_name);
1498		centry_put_string(centry, (*info)[i].homedir);
1499		centry_put_string(centry, (*info)[i].shell);
1500		centry_put_sid(centry, &(*info)[i].user_sid);
1501		centry_put_sid(centry, &(*info)[i].group_sid);
1502		if (domain->backend && domain->backend->consistent) {
1503			/* when the backend is consistent we can pre-prime some mappings */
1504			wcache_save_name_to_sid(domain, NT_STATUS_OK,
1505						domain->name,
1506						(*info)[i].acct_name,
1507						&(*info)[i].user_sid,
1508						SID_NAME_USER);
1509			wcache_save_sid_to_name(domain, NT_STATUS_OK,
1510						&(*info)[i].user_sid,
1511						domain->name,
1512						(*info)[i].acct_name,
1513						SID_NAME_USER);
1514			wcache_save_user(domain, NT_STATUS_OK, &(*info)[i]);
1515		}
1516	}
1517	centry_end(centry, "UL/%s", domain->name);
1518	centry_free(centry);
1519
1520skip_save:
1521	return status;
1522}
1523
1524/* list all domain groups */
1525static NTSTATUS enum_dom_groups(struct winbindd_domain *domain,
1526				TALLOC_CTX *mem_ctx,
1527				uint32 *num_entries,
1528				struct acct_info **info)
1529{
1530	struct winbind_cache *cache = get_cache(domain);
1531	struct cache_entry *centry = NULL;
1532	NTSTATUS status;
1533	unsigned int i;
1534	bool old_status;
1535
1536	old_status = domain->online;
1537	if (!cache->tdb)
1538		goto do_query;
1539
1540	centry = wcache_fetch(cache, domain, "GL/%s/domain", domain->name);
1541	if (!centry)
1542		goto do_query;
1543
1544do_fetch_cache:
1545	*num_entries = centry_uint32(centry);
1546
1547	if (*num_entries == 0)
1548		goto do_cached;
1549
1550	(*info) = TALLOC_ARRAY(mem_ctx, struct acct_info, *num_entries);
1551	if (! (*info)) {
1552		smb_panic_fn("enum_dom_groups out of memory");
1553	}
1554	for (i=0; i<(*num_entries); i++) {
1555		fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx));
1556		fstrcpy((*info)[i].acct_desc, centry_string(centry, mem_ctx));
1557		(*info)[i].rid = centry_uint32(centry);
1558	}
1559
1560do_cached:
1561	status = centry->status;
1562
1563	DEBUG(10,("enum_dom_groups: [Cached] - cached list for domain %s status: %s\n",
1564		domain->name, nt_errstr(status) ));
1565
1566	centry_free(centry);
1567	return status;
1568
1569do_query:
1570	*num_entries = 0;
1571	*info = NULL;
1572
1573	/* Return status value returned by seq number check */
1574
1575	if (!NT_STATUS_IS_OK(domain->last_status))
1576		return domain->last_status;
1577
1578	DEBUG(10,("enum_dom_groups: [Cached] - doing backend query for list for domain %s\n",
1579		domain->name ));
1580
1581	status = domain->backend->enum_dom_groups(domain, mem_ctx, num_entries, info);
1582
1583	if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1584	    NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1585		if (!domain->internal && old_status) {
1586			set_domain_offline(domain);
1587		}
1588		if (cache->tdb &&
1589			!domain->online &&
1590			!domain->internal &&
1591			old_status) {
1592			centry = wcache_fetch(cache, domain, "GL/%s/domain", domain->name);
1593			if (centry) {
1594				goto do_fetch_cache;
1595			}
1596		}
1597	}
1598	/* and save it */
1599	refresh_sequence_number(domain, false);
1600	if (!NT_STATUS_IS_OK(status)) {
1601		return status;
1602	}
1603	centry = centry_start(domain, status);
1604	if (!centry)
1605		goto skip_save;
1606	centry_put_uint32(centry, *num_entries);
1607	for (i=0; i<(*num_entries); i++) {
1608		centry_put_string(centry, (*info)[i].acct_name);
1609		centry_put_string(centry, (*info)[i].acct_desc);
1610		centry_put_uint32(centry, (*info)[i].rid);
1611	}
1612	centry_end(centry, "GL/%s/domain", domain->name);
1613	centry_free(centry);
1614
1615skip_save:
1616	return status;
1617}
1618
1619/* list all domain groups */
1620static NTSTATUS enum_local_groups(struct winbindd_domain *domain,
1621				TALLOC_CTX *mem_ctx,
1622				uint32 *num_entries,
1623				struct acct_info **info)
1624{
1625	struct winbind_cache *cache = get_cache(domain);
1626	struct cache_entry *centry = NULL;
1627	NTSTATUS status;
1628	unsigned int i;
1629	bool old_status;
1630
1631	old_status = domain->online;
1632	if (!cache->tdb)
1633		goto do_query;
1634
1635	centry = wcache_fetch(cache, domain, "GL/%s/local", domain->name);
1636	if (!centry)
1637		goto do_query;
1638
1639do_fetch_cache:
1640	*num_entries = centry_uint32(centry);
1641
1642	if (*num_entries == 0)
1643		goto do_cached;
1644
1645	(*info) = TALLOC_ARRAY(mem_ctx, struct acct_info, *num_entries);
1646	if (! (*info)) {
1647		smb_panic_fn("enum_dom_groups out of memory");
1648	}
1649	for (i=0; i<(*num_entries); i++) {
1650		fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx));
1651		fstrcpy((*info)[i].acct_desc, centry_string(centry, mem_ctx));
1652		(*info)[i].rid = centry_uint32(centry);
1653	}
1654
1655do_cached:
1656
1657	/* If we are returning cached data and the domain controller
1658	   is down then we don't know whether the data is up to date
1659	   or not.  Return NT_STATUS_MORE_PROCESSING_REQUIRED to
1660	   indicate this. */
1661
1662	if (wcache_server_down(domain)) {
1663		DEBUG(10, ("enum_local_groups: returning cached user list and server was down\n"));
1664		status = NT_STATUS_MORE_PROCESSING_REQUIRED;
1665	} else
1666		status = centry->status;
1667
1668	DEBUG(10,("enum_local_groups: [Cached] - cached list for domain %s status: %s\n",
1669		domain->name, nt_errstr(status) ));
1670
1671	centry_free(centry);
1672	return status;
1673
1674do_query:
1675	*num_entries = 0;
1676	*info = NULL;
1677
1678	/* Return status value returned by seq number check */
1679
1680	if (!NT_STATUS_IS_OK(domain->last_status))
1681		return domain->last_status;
1682
1683	DEBUG(10,("enum_local_groups: [Cached] - doing backend query for list for domain %s\n",
1684		domain->name ));
1685
1686	status = domain->backend->enum_local_groups(domain, mem_ctx, num_entries, info);
1687
1688	if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1689		NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1690		if (!domain->internal && old_status) {
1691			set_domain_offline(domain);
1692		}
1693		if (cache->tdb &&
1694			!domain->internal &&
1695			!domain->online &&
1696			old_status) {
1697			centry = wcache_fetch(cache, domain, "GL/%s/local", domain->name);
1698			if (centry) {
1699				goto do_fetch_cache;
1700			}
1701		}
1702	}
1703	/* and save it */
1704	refresh_sequence_number(domain, false);
1705	if (!NT_STATUS_IS_OK(status)) {
1706		return status;
1707	}
1708	centry = centry_start(domain, status);
1709	if (!centry)
1710		goto skip_save;
1711	centry_put_uint32(centry, *num_entries);
1712	for (i=0; i<(*num_entries); i++) {
1713		centry_put_string(centry, (*info)[i].acct_name);
1714		centry_put_string(centry, (*info)[i].acct_desc);
1715		centry_put_uint32(centry, (*info)[i].rid);
1716	}
1717	centry_end(centry, "GL/%s/local", domain->name);
1718	centry_free(centry);
1719
1720skip_save:
1721	return status;
1722}
1723
1724NTSTATUS wcache_name_to_sid(struct winbindd_domain *domain,
1725			    const char *domain_name,
1726			    const char *name,
1727			    struct dom_sid *sid,
1728			    enum lsa_SidType *type)
1729{
1730	struct winbind_cache *cache = get_cache(domain);
1731	struct cache_entry *centry;
1732	NTSTATUS status;
1733	char *uname;
1734
1735	if (cache->tdb == NULL) {
1736		return NT_STATUS_NOT_FOUND;
1737	}
1738
1739	uname = talloc_strdup_upper(talloc_tos(), name);
1740	if (uname == NULL) {
1741		return NT_STATUS_NO_MEMORY;
1742	}
1743
1744	centry = wcache_fetch(cache, domain, "NS/%s/%s", domain_name, uname);
1745	TALLOC_FREE(uname);
1746	if (centry == NULL) {
1747		return NT_STATUS_NOT_FOUND;
1748	}
1749
1750	status = centry->status;
1751	if (NT_STATUS_IS_OK(status)) {
1752		*type = (enum lsa_SidType)centry_uint32(centry);
1753		centry_sid(centry, sid);
1754	}
1755
1756	DEBUG(10,("name_to_sid: [Cached] - cached name for domain %s status: "
1757		  "%s\n", domain->name, nt_errstr(status) ));
1758
1759	centry_free(centry);
1760	return status;
1761}
1762
1763/* convert a single name to a sid in a domain */
1764static NTSTATUS name_to_sid(struct winbindd_domain *domain,
1765			    TALLOC_CTX *mem_ctx,
1766			    const char *domain_name,
1767			    const char *name,
1768			    uint32_t flags,
1769			    DOM_SID *sid,
1770			    enum lsa_SidType *type)
1771{
1772	NTSTATUS status;
1773	bool old_status;
1774
1775	old_status = domain->online;
1776
1777	status = wcache_name_to_sid(domain, domain_name, name, sid, type);
1778	if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
1779		return status;
1780	}
1781
1782	ZERO_STRUCTP(sid);
1783
1784	/* If the seq number check indicated that there is a problem
1785	 * with this DC, then return that status... except for
1786	 * access_denied.  This is special because the dc may be in
1787	 * "restrict anonymous = 1" mode, in which case it will deny
1788	 * most unauthenticated operations, but *will* allow the LSA
1789	 * name-to-sid that we try as a fallback. */
1790
1791	if (!(NT_STATUS_IS_OK(domain->last_status)
1792	      || NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)))
1793		return domain->last_status;
1794
1795	DEBUG(10,("name_to_sid: [Cached] - doing backend query for name for domain %s\n",
1796		domain->name ));
1797
1798	status = domain->backend->name_to_sid(domain, mem_ctx, domain_name,
1799					      name, flags, sid, type);
1800
1801	if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1802		NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1803		if (!domain->internal && old_status) {
1804			set_domain_offline(domain);
1805		}
1806		if (!domain->internal &&
1807			!domain->online &&
1808			old_status) {
1809			NTSTATUS cache_status;
1810			cache_status = wcache_name_to_sid(domain, domain_name, name, sid, type);
1811			return cache_status;
1812		}
1813	}
1814	/* and save it */
1815	refresh_sequence_number(domain, false);
1816
1817	if (domain->online &&
1818	    (NT_STATUS_IS_OK(status) || NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED))) {
1819		wcache_save_name_to_sid(domain, status, domain_name, name, sid, *type);
1820
1821		/* Only save the reverse mapping if this was not a UPN */
1822		if (!strchr(name, '@')) {
1823			strupper_m(CONST_DISCARD(char *,domain_name));
1824			strlower_m(CONST_DISCARD(char *,name));
1825			wcache_save_sid_to_name(domain, status, sid, domain_name, name, *type);
1826		}
1827	}
1828
1829	return status;
1830}
1831
1832NTSTATUS wcache_sid_to_name(struct winbindd_domain *domain,
1833			    const struct dom_sid *sid,
1834			    TALLOC_CTX *mem_ctx,
1835			    char **domain_name,
1836			    char **name,
1837			    enum lsa_SidType *type)
1838{
1839	struct winbind_cache *cache = get_cache(domain);
1840	struct cache_entry *centry;
1841	char *sid_string;
1842	NTSTATUS status;
1843
1844	if (cache->tdb == NULL) {
1845		return NT_STATUS_NOT_FOUND;
1846	}
1847
1848	sid_string = sid_string_tos(sid);
1849	if (sid_string == NULL) {
1850		return NT_STATUS_NO_MEMORY;
1851	}
1852
1853	centry = wcache_fetch(cache, domain, "SN/%s", sid_string);
1854	TALLOC_FREE(sid_string);
1855	if (centry == NULL) {
1856		return NT_STATUS_NOT_FOUND;
1857	}
1858
1859	if (NT_STATUS_IS_OK(centry->status)) {
1860		*type = (enum lsa_SidType)centry_uint32(centry);
1861		*domain_name = centry_string(centry, mem_ctx);
1862		*name = centry_string(centry, mem_ctx);
1863	}
1864
1865	status = centry->status;
1866	centry_free(centry);
1867
1868	DEBUG(10,("sid_to_name: [Cached] - cached name for domain %s status: "
1869		  "%s\n", domain->name, nt_errstr(status) ));
1870
1871	return status;
1872}
1873
1874/* convert a sid to a user or group name. The sid is guaranteed to be in the domain
1875   given */
1876static NTSTATUS sid_to_name(struct winbindd_domain *domain,
1877			    TALLOC_CTX *mem_ctx,
1878			    const DOM_SID *sid,
1879			    char **domain_name,
1880			    char **name,
1881			    enum lsa_SidType *type)
1882{
1883	NTSTATUS status;
1884	bool old_status;
1885
1886	old_status = domain->online;
1887	status = wcache_sid_to_name(domain, sid, mem_ctx, domain_name, name,
1888				    type);
1889	if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
1890		return status;
1891	}
1892
1893	*name = NULL;
1894	*domain_name = NULL;
1895
1896	/* If the seq number check indicated that there is a problem
1897	 * with this DC, then return that status... except for
1898	 * access_denied.  This is special because the dc may be in
1899	 * "restrict anonymous = 1" mode, in which case it will deny
1900	 * most unauthenticated operations, but *will* allow the LSA
1901	 * sid-to-name that we try as a fallback. */
1902
1903	if (!(NT_STATUS_IS_OK(domain->last_status)
1904	      || NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)))
1905		return domain->last_status;
1906
1907	DEBUG(10,("sid_to_name: [Cached] - doing backend query for name for domain %s\n",
1908		domain->name ));
1909
1910	status = domain->backend->sid_to_name(domain, mem_ctx, sid, domain_name, name, type);
1911
1912	if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1913		NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1914		if (!domain->internal && old_status) {
1915			set_domain_offline(domain);
1916		}
1917		if (!domain->internal &&
1918			!domain->online &&
1919			old_status) {
1920			NTSTATUS cache_status;
1921			cache_status = wcache_sid_to_name(domain, sid, mem_ctx,
1922							domain_name, name, type);
1923			return cache_status;
1924		}
1925	}
1926	/* and save it */
1927	refresh_sequence_number(domain, false);
1928	if (!NT_STATUS_IS_OK(status)) {
1929		return status;
1930	}
1931	wcache_save_sid_to_name(domain, status, sid, *domain_name, *name, *type);
1932
1933	/* We can't save the name to sid mapping here, as with sid history a
1934	 * later name2sid would give the wrong sid. */
1935
1936	return status;
1937}
1938
1939static NTSTATUS rids_to_names(struct winbindd_domain *domain,
1940			      TALLOC_CTX *mem_ctx,
1941			      const DOM_SID *domain_sid,
1942			      uint32 *rids,
1943			      size_t num_rids,
1944			      char **domain_name,
1945			      char ***names,
1946			      enum lsa_SidType **types)
1947{
1948	struct winbind_cache *cache = get_cache(domain);
1949	size_t i;
1950	NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
1951	bool have_mapped;
1952	bool have_unmapped;
1953	bool old_status;
1954
1955	old_status = domain->online;
1956	*domain_name = NULL;
1957	*names = NULL;
1958	*types = NULL;
1959
1960	if (!cache->tdb) {
1961		goto do_query;
1962	}
1963
1964	if (num_rids == 0) {
1965		return NT_STATUS_OK;
1966	}
1967
1968	*names = TALLOC_ARRAY(mem_ctx, char *, num_rids);
1969	*types = TALLOC_ARRAY(mem_ctx, enum lsa_SidType, num_rids);
1970
1971	if ((*names == NULL) || (*types == NULL)) {
1972		result = NT_STATUS_NO_MEMORY;
1973		goto error;
1974	}
1975
1976	have_mapped = have_unmapped = false;
1977
1978	for (i=0; i<num_rids; i++) {
1979		DOM_SID sid;
1980		struct cache_entry *centry;
1981		fstring tmp;
1982
1983		if (!sid_compose(&sid, domain_sid, rids[i])) {
1984			result = NT_STATUS_INTERNAL_ERROR;
1985			goto error;
1986		}
1987
1988		centry = wcache_fetch(cache, domain, "SN/%s",
1989				      sid_to_fstring(tmp, &sid));
1990		if (!centry) {
1991			goto do_query;
1992		}
1993
1994		(*types)[i] = SID_NAME_UNKNOWN;
1995		(*names)[i] = talloc_strdup(*names, "");
1996
1997		if (NT_STATUS_IS_OK(centry->status)) {
1998			char *dom;
1999			have_mapped = true;
2000			(*types)[i] = (enum lsa_SidType)centry_uint32(centry);
2001
2002			dom = centry_string(centry, mem_ctx);
2003			if (*domain_name == NULL) {
2004				*domain_name = dom;
2005			} else {
2006				talloc_free(dom);
2007			}
2008
2009			(*names)[i] = centry_string(centry, *names);
2010
2011		} else if (NT_STATUS_EQUAL(centry->status, NT_STATUS_NONE_MAPPED)) {
2012			have_unmapped = true;
2013
2014		} else {
2015			/* something's definitely wrong */
2016			result = centry->status;
2017			goto error;
2018		}
2019
2020		centry_free(centry);
2021	}
2022
2023	if (!have_mapped) {
2024		return NT_STATUS_NONE_MAPPED;
2025	}
2026	if (!have_unmapped) {
2027		return NT_STATUS_OK;
2028	}
2029	return STATUS_SOME_UNMAPPED;
2030
2031 do_query:
2032
2033	TALLOC_FREE(*names);
2034	TALLOC_FREE(*types);
2035
2036	result = domain->backend->rids_to_names(domain, mem_ctx, domain_sid,
2037						rids, num_rids, domain_name,
2038						names, types);
2039
2040	if (NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
2041		NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2042		if (!domain->internal && old_status) {
2043			set_domain_offline(domain);
2044		}
2045		if (cache->tdb &&
2046			!domain->internal &&
2047			!domain->online &&
2048			old_status) {
2049			have_mapped = have_unmapped = false;
2050
2051			for (i=0; i<num_rids; i++) {
2052				DOM_SID sid;
2053				struct cache_entry *centry;
2054				fstring tmp;
2055
2056				if (!sid_compose(&sid, domain_sid, rids[i])) {
2057					result = NT_STATUS_INTERNAL_ERROR;
2058					goto error;
2059				}
2060
2061				centry = wcache_fetch(cache, domain, "SN/%s",
2062						      sid_to_fstring(tmp, &sid));
2063				if (!centry) {
2064					(*types)[i] = SID_NAME_UNKNOWN;
2065					(*names)[i] = talloc_strdup(*names, "");
2066					continue;
2067				}
2068
2069				(*types)[i] = SID_NAME_UNKNOWN;
2070				(*names)[i] = talloc_strdup(*names, "");
2071
2072				if (NT_STATUS_IS_OK(centry->status)) {
2073					char *dom;
2074					have_mapped = true;
2075					(*types)[i] = (enum lsa_SidType)centry_uint32(centry);
2076
2077					dom = centry_string(centry, mem_ctx);
2078					if (*domain_name == NULL) {
2079						*domain_name = dom;
2080					} else {
2081						talloc_free(dom);
2082					}
2083
2084					(*names)[i] = centry_string(centry, *names);
2085
2086				} else if (NT_STATUS_EQUAL(centry->status, NT_STATUS_NONE_MAPPED)) {
2087					have_unmapped = true;
2088
2089				} else {
2090					/* something's definitely wrong */
2091					result = centry->status;
2092					goto error;
2093				}
2094
2095				centry_free(centry);
2096			}
2097
2098			if (!have_mapped) {
2099				return NT_STATUS_NONE_MAPPED;
2100			}
2101			if (!have_unmapped) {
2102				return NT_STATUS_OK;
2103			}
2104			return STATUS_SOME_UNMAPPED;
2105		}
2106	}
2107	/*
2108	  None of the queried rids has been found so save all negative entries
2109	*/
2110	if (NT_STATUS_EQUAL(result, NT_STATUS_NONE_MAPPED)) {
2111		for (i = 0; i < num_rids; i++) {
2112			DOM_SID sid;
2113			const char *name = "";
2114			const enum lsa_SidType type = SID_NAME_UNKNOWN;
2115			NTSTATUS status = NT_STATUS_NONE_MAPPED;
2116
2117			if (!sid_compose(&sid, domain_sid, rids[i])) {
2118				return NT_STATUS_INTERNAL_ERROR;
2119			}
2120
2121			wcache_save_sid_to_name(domain, status, &sid, *domain_name,
2122						name, type);
2123		}
2124
2125		return result;
2126	}
2127
2128	/*
2129	  Some or all of the queried rids have been found.
2130	*/
2131	if (!NT_STATUS_IS_OK(result) &&
2132	    !NT_STATUS_EQUAL(result, STATUS_SOME_UNMAPPED)) {
2133		return result;
2134	}
2135
2136	refresh_sequence_number(domain, false);
2137
2138	for (i=0; i<num_rids; i++) {
2139		DOM_SID sid;
2140		NTSTATUS status;
2141
2142		if (!sid_compose(&sid, domain_sid, rids[i])) {
2143			result = NT_STATUS_INTERNAL_ERROR;
2144			goto error;
2145		}
2146
2147		status = (*types)[i] == SID_NAME_UNKNOWN ?
2148			NT_STATUS_NONE_MAPPED : NT_STATUS_OK;
2149
2150		wcache_save_sid_to_name(domain, status, &sid, *domain_name,
2151					(*names)[i], (*types)[i]);
2152	}
2153
2154	return result;
2155
2156 error:
2157	TALLOC_FREE(*names);
2158	TALLOC_FREE(*types);
2159	return result;
2160}
2161
2162NTSTATUS wcache_query_user(struct winbindd_domain *domain,
2163			   TALLOC_CTX *mem_ctx,
2164			   const struct dom_sid *user_sid,
2165			   struct wbint_userinfo *info)
2166{
2167	struct winbind_cache *cache = get_cache(domain);
2168	struct cache_entry *centry = NULL;
2169	NTSTATUS status;
2170	char *sid_string;
2171
2172	if (cache->tdb == NULL) {
2173		return NT_STATUS_NOT_FOUND;
2174	}
2175
2176	sid_string = sid_string_tos(user_sid);
2177	if (sid_string == NULL) {
2178		return NT_STATUS_NO_MEMORY;
2179	}
2180
2181	centry = wcache_fetch(cache, domain, "U/%s", sid_string);
2182	TALLOC_FREE(sid_string);
2183	if (centry == NULL) {
2184		return NT_STATUS_NOT_FOUND;
2185	}
2186
2187	/*
2188	 * If we have an access denied cache entry and a cached info3
2189	 * in the samlogon cache then do a query.  This will force the
2190	 * rpc back end to return the info3 data.
2191	 */
2192
2193	if (NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED) &&
2194	    netsamlogon_cache_have(user_sid)) {
2195		DEBUG(10, ("query_user: cached access denied and have cached "
2196			   "info3\n"));
2197		domain->last_status = NT_STATUS_OK;
2198		centry_free(centry);
2199		return NT_STATUS_NOT_FOUND;
2200	}
2201
2202	/* if status is not ok then this is a negative hit
2203	   and the rest of the data doesn't matter */
2204	status = centry->status;
2205	if (NT_STATUS_IS_OK(status)) {
2206		info->acct_name = centry_string(centry, mem_ctx);
2207		info->full_name = centry_string(centry, mem_ctx);
2208		info->homedir = centry_string(centry, mem_ctx);
2209		info->shell = centry_string(centry, mem_ctx);
2210		info->primary_gid = centry_uint32(centry);
2211		centry_sid(centry, &info->user_sid);
2212		centry_sid(centry, &info->group_sid);
2213	}
2214
2215	DEBUG(10,("query_user: [Cached] - cached info for domain %s status: "
2216		  "%s\n", domain->name, nt_errstr(status) ));
2217
2218	centry_free(centry);
2219	return status;
2220}
2221
2222/* Lookup user information from a rid */
2223static NTSTATUS query_user(struct winbindd_domain *domain,
2224			   TALLOC_CTX *mem_ctx,
2225			   const DOM_SID *user_sid,
2226			   struct wbint_userinfo *info)
2227{
2228	NTSTATUS status;
2229	bool old_status;
2230
2231	old_status = domain->online;
2232	status = wcache_query_user(domain, mem_ctx, user_sid, info);
2233	if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2234		return status;
2235	}
2236
2237	ZERO_STRUCTP(info);
2238
2239	/* Return status value returned by seq number check */
2240
2241	if (!NT_STATUS_IS_OK(domain->last_status))
2242		return domain->last_status;
2243
2244	DEBUG(10,("query_user: [Cached] - doing backend query for info for domain %s\n",
2245		domain->name ));
2246
2247	status = domain->backend->query_user(domain, mem_ctx, user_sid, info);
2248
2249	if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2250		NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2251		if (!domain->internal && old_status) {
2252			set_domain_offline(domain);
2253		}
2254		if (!domain->internal &&
2255			!domain->online &&
2256			old_status) {
2257			NTSTATUS cache_status;
2258			cache_status = wcache_query_user(domain, mem_ctx, user_sid, info);
2259			return cache_status;
2260		}
2261	}
2262	/* and save it */
2263	refresh_sequence_number(domain, false);
2264	if (!NT_STATUS_IS_OK(status)) {
2265		return status;
2266	}
2267	wcache_save_user(domain, status, info);
2268
2269	return status;
2270}
2271
2272NTSTATUS wcache_lookup_usergroups(struct winbindd_domain *domain,
2273				  TALLOC_CTX *mem_ctx,
2274				  const struct dom_sid *user_sid,
2275				  uint32_t *pnum_sids,
2276				  struct dom_sid **psids)
2277{
2278	struct winbind_cache *cache = get_cache(domain);
2279	struct cache_entry *centry = NULL;
2280	NTSTATUS status;
2281	uint32_t i, num_sids;
2282	struct dom_sid *sids;
2283	fstring sid_string;
2284
2285	if (cache->tdb == NULL) {
2286		return NT_STATUS_NOT_FOUND;
2287	}
2288
2289	centry = wcache_fetch(cache, domain, "UG/%s",
2290			      sid_to_fstring(sid_string, user_sid));
2291	if (centry == NULL) {
2292		return NT_STATUS_NOT_FOUND;
2293	}
2294
2295	/* If we have an access denied cache entry and a cached info3 in the
2296           samlogon cache then do a query.  This will force the rpc back end
2297           to return the info3 data. */
2298
2299	if (NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)
2300	    && netsamlogon_cache_have(user_sid)) {
2301		DEBUG(10, ("lookup_usergroups: cached access denied and have "
2302			   "cached info3\n"));
2303		domain->last_status = NT_STATUS_OK;
2304		centry_free(centry);
2305		return NT_STATUS_NOT_FOUND;
2306	}
2307
2308	num_sids = centry_uint32(centry);
2309	sids = talloc_array(mem_ctx, struct dom_sid, num_sids);
2310	if (sids == NULL) {
2311		centry_free(centry);
2312		return NT_STATUS_NO_MEMORY;
2313	}
2314
2315	for (i=0; i<num_sids; i++) {
2316		centry_sid(centry, &sids[i]);
2317	}
2318
2319	status = centry->status;
2320
2321	DEBUG(10,("lookup_usergroups: [Cached] - cached info for domain %s "
2322		  "status: %s\n", domain->name, nt_errstr(status)));
2323
2324	centry_free(centry);
2325
2326	*pnum_sids = num_sids;
2327	*psids = sids;
2328	return status;
2329}
2330
2331/* Lookup groups a user is a member of. */
2332static NTSTATUS lookup_usergroups(struct winbindd_domain *domain,
2333				  TALLOC_CTX *mem_ctx,
2334				  const DOM_SID *user_sid,
2335				  uint32 *num_groups, DOM_SID **user_gids)
2336{
2337	struct cache_entry *centry = NULL;
2338	NTSTATUS status;
2339	unsigned int i;
2340	fstring sid_string;
2341	bool old_status;
2342
2343	old_status = domain->online;
2344	status = wcache_lookup_usergroups(domain, mem_ctx, user_sid,
2345					  num_groups, user_gids);
2346	if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2347		return status;
2348	}
2349
2350	(*num_groups) = 0;
2351	(*user_gids) = NULL;
2352
2353	/* Return status value returned by seq number check */
2354
2355	if (!NT_STATUS_IS_OK(domain->last_status))
2356		return domain->last_status;
2357
2358	DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info for domain %s\n",
2359		domain->name ));
2360
2361	status = domain->backend->lookup_usergroups(domain, mem_ctx, user_sid, num_groups, user_gids);
2362
2363	if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2364		NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2365		if (!domain->internal && old_status) {
2366			set_domain_offline(domain);
2367		}
2368		if (!domain->internal &&
2369			!domain->online &&
2370			old_status) {
2371			NTSTATUS cache_status;
2372			cache_status = wcache_lookup_usergroups(domain, mem_ctx, user_sid,
2373							  num_groups, user_gids);
2374			return cache_status;
2375		}
2376	}
2377	if ( NT_STATUS_EQUAL(status, NT_STATUS_SYNCHRONIZATION_REQUIRED) )
2378		goto skip_save;
2379
2380	/* and save it */
2381	refresh_sequence_number(domain, false);
2382	if (!NT_STATUS_IS_OK(status)) {
2383		return status;
2384	}
2385	centry = centry_start(domain, status);
2386	if (!centry)
2387		goto skip_save;
2388
2389	centry_put_uint32(centry, *num_groups);
2390	for (i=0; i<(*num_groups); i++) {
2391		centry_put_sid(centry, &(*user_gids)[i]);
2392	}
2393
2394	centry_end(centry, "UG/%s", sid_to_fstring(sid_string, user_sid));
2395	centry_free(centry);
2396
2397skip_save:
2398	return status;
2399}
2400
2401static char *wcache_make_sidlist(TALLOC_CTX *mem_ctx, uint32_t num_sids,
2402				 const struct dom_sid *sids)
2403{
2404	uint32_t i;
2405	char *sidlist;
2406
2407	sidlist = talloc_strdup(mem_ctx, "");
2408	if (sidlist == NULL) {
2409		return NULL;
2410	}
2411	for (i=0; i<num_sids; i++) {
2412		fstring tmp;
2413		sidlist = talloc_asprintf_append_buffer(
2414			sidlist, "/%s", sid_to_fstring(tmp, &sids[i]));
2415		if (sidlist == NULL) {
2416			return NULL;
2417		}
2418	}
2419	return sidlist;
2420}
2421
2422NTSTATUS wcache_lookup_useraliases(struct winbindd_domain *domain,
2423				   TALLOC_CTX *mem_ctx, uint32_t num_sids,
2424				   const struct dom_sid *sids,
2425				   uint32_t *pnum_aliases, uint32_t **paliases)
2426{
2427	struct winbind_cache *cache = get_cache(domain);
2428	struct cache_entry *centry = NULL;
2429	uint32_t num_aliases;
2430	uint32_t *aliases;
2431	NTSTATUS status;
2432	char *sidlist;
2433	int i;
2434
2435	if (cache->tdb == NULL) {
2436		return NT_STATUS_NOT_FOUND;
2437	}
2438
2439	if (num_sids == 0) {
2440		*pnum_aliases = 0;
2441		*paliases = NULL;
2442		return NT_STATUS_OK;
2443	}
2444
2445	/* We need to cache indexed by the whole list of SIDs, the aliases
2446	 * resulting might come from any of the SIDs. */
2447
2448	sidlist = wcache_make_sidlist(talloc_tos(), num_sids, sids);
2449	if (sidlist == NULL) {
2450		return NT_STATUS_NO_MEMORY;
2451	}
2452
2453	centry = wcache_fetch(cache, domain, "UA%s", sidlist);
2454	TALLOC_FREE(sidlist);
2455	if (centry == NULL) {
2456		return NT_STATUS_NOT_FOUND;
2457	}
2458
2459	num_aliases = centry_uint32(centry);
2460	aliases = talloc_array(mem_ctx, uint32_t, num_aliases);
2461	if (aliases == NULL) {
2462		centry_free(centry);
2463		return NT_STATUS_NO_MEMORY;
2464	}
2465
2466	for (i=0; i<num_aliases; i++) {
2467		aliases[i] = centry_uint32(centry);
2468	}
2469
2470	status = centry->status;
2471
2472	DEBUG(10,("lookup_useraliases: [Cached] - cached info for domain: %s "
2473		  "status %s\n", domain->name, nt_errstr(status)));
2474
2475	centry_free(centry);
2476
2477	*pnum_aliases = num_aliases;
2478	*paliases = aliases;
2479
2480	return status;
2481}
2482
2483static NTSTATUS lookup_useraliases(struct winbindd_domain *domain,
2484				   TALLOC_CTX *mem_ctx,
2485				   uint32 num_sids, const DOM_SID *sids,
2486				   uint32 *num_aliases, uint32 **alias_rids)
2487{
2488	struct cache_entry *centry = NULL;
2489	NTSTATUS status;
2490	char *sidlist;
2491	int i;
2492	bool old_status;
2493
2494	old_status = domain->online;
2495	status = wcache_lookup_useraliases(domain, mem_ctx, num_sids, sids,
2496					   num_aliases, alias_rids);
2497	if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2498		return status;
2499	}
2500
2501	(*num_aliases) = 0;
2502	(*alias_rids) = NULL;
2503
2504	if (!NT_STATUS_IS_OK(domain->last_status))
2505		return domain->last_status;
2506
2507	DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info "
2508		  "for domain %s\n", domain->name ));
2509
2510	sidlist = wcache_make_sidlist(talloc_tos(), num_sids, sids);
2511	if (sidlist == NULL) {
2512		return NT_STATUS_NO_MEMORY;
2513	}
2514
2515	status = domain->backend->lookup_useraliases(domain, mem_ctx,
2516						     num_sids, sids,
2517						     num_aliases, alias_rids);
2518
2519	if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2520		NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2521		if (!domain->internal && old_status) {
2522			set_domain_offline(domain);
2523		}
2524		if (!domain->internal &&
2525			!domain->online &&
2526			old_status) {
2527			NTSTATUS cache_status;
2528			cache_status = wcache_lookup_useraliases(domain, mem_ctx, num_sids,
2529								 sids, num_aliases, alias_rids);
2530			return cache_status;
2531		}
2532	}
2533	/* and save it */
2534	refresh_sequence_number(domain, false);
2535	if (!NT_STATUS_IS_OK(status)) {
2536		return status;
2537	}
2538	centry = centry_start(domain, status);
2539	if (!centry)
2540		goto skip_save;
2541	centry_put_uint32(centry, *num_aliases);
2542	for (i=0; i<(*num_aliases); i++)
2543		centry_put_uint32(centry, (*alias_rids)[i]);
2544	centry_end(centry, "UA%s", sidlist);
2545	centry_free(centry);
2546
2547 skip_save:
2548	return status;
2549}
2550
2551NTSTATUS wcache_lookup_groupmem(struct winbindd_domain *domain,
2552				TALLOC_CTX *mem_ctx,
2553				const struct dom_sid *group_sid,
2554				uint32_t *num_names,
2555				struct dom_sid **sid_mem, char ***names,
2556				uint32_t **name_types)
2557{
2558	struct winbind_cache *cache = get_cache(domain);
2559	struct cache_entry *centry = NULL;
2560	NTSTATUS status;
2561	unsigned int i;
2562	char *sid_string;
2563
2564	if (cache->tdb == NULL) {
2565		return NT_STATUS_NOT_FOUND;
2566	}
2567
2568	sid_string = sid_string_tos(group_sid);
2569	if (sid_string == NULL) {
2570		return NT_STATUS_NO_MEMORY;
2571	}
2572
2573	centry = wcache_fetch(cache, domain, "GM/%s", sid_string);
2574	TALLOC_FREE(sid_string);
2575	if (centry == NULL) {
2576		return NT_STATUS_NOT_FOUND;
2577	}
2578
2579	*sid_mem = NULL;
2580	*names = NULL;
2581	*name_types = NULL;
2582
2583	*num_names = centry_uint32(centry);
2584	if (*num_names == 0) {
2585		centry_free(centry);
2586		return NT_STATUS_OK;
2587	}
2588
2589	*sid_mem = talloc_array(mem_ctx, DOM_SID, *num_names);
2590	*names = talloc_array(mem_ctx, char *, *num_names);
2591	*name_types = talloc_array(mem_ctx, uint32, *num_names);
2592
2593	if ((*sid_mem == NULL) || (*names == NULL) || (*name_types == NULL)) {
2594		TALLOC_FREE(*sid_mem);
2595		TALLOC_FREE(*names);
2596		TALLOC_FREE(*name_types);
2597		centry_free(centry);
2598		return NT_STATUS_NO_MEMORY;
2599	}
2600
2601	for (i=0; i<(*num_names); i++) {
2602		centry_sid(centry, &(*sid_mem)[i]);
2603		(*names)[i] = centry_string(centry, mem_ctx);
2604		(*name_types)[i] = centry_uint32(centry);
2605	}
2606
2607	status = centry->status;
2608
2609	DEBUG(10,("lookup_groupmem: [Cached] - cached info for domain %s "
2610		  "status: %s\n", domain->name, nt_errstr(status)));
2611
2612	centry_free(centry);
2613	return status;
2614}
2615
2616static NTSTATUS lookup_groupmem(struct winbindd_domain *domain,
2617				TALLOC_CTX *mem_ctx,
2618				const DOM_SID *group_sid,
2619				enum lsa_SidType type,
2620				uint32 *num_names,
2621				DOM_SID **sid_mem, char ***names,
2622				uint32 **name_types)
2623{
2624	struct cache_entry *centry = NULL;
2625	NTSTATUS status;
2626	unsigned int i;
2627	fstring sid_string;
2628	bool old_status;
2629
2630	old_status = domain->online;
2631	status = wcache_lookup_groupmem(domain, mem_ctx, group_sid, num_names,
2632					sid_mem, names, name_types);
2633	if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2634		return status;
2635	}
2636
2637	(*num_names) = 0;
2638	(*sid_mem) = NULL;
2639	(*names) = NULL;
2640	(*name_types) = NULL;
2641
2642	/* Return status value returned by seq number check */
2643
2644	if (!NT_STATUS_IS_OK(domain->last_status))
2645		return domain->last_status;
2646
2647	DEBUG(10,("lookup_groupmem: [Cached] - doing backend query for info for domain %s\n",
2648		domain->name ));
2649
2650	status = domain->backend->lookup_groupmem(domain, mem_ctx, group_sid,
2651						  type, num_names,
2652						  sid_mem, names, name_types);
2653
2654	if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2655		NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2656		if (!domain->internal && old_status) {
2657			set_domain_offline(domain);
2658		}
2659		if (!domain->internal &&
2660			!domain->online &&
2661			old_status) {
2662			NTSTATUS cache_status;
2663			cache_status = wcache_lookup_groupmem(domain, mem_ctx, group_sid,
2664							      num_names, sid_mem, names,
2665							      name_types);
2666			return cache_status;
2667		}
2668	}
2669	/* and save it */
2670	refresh_sequence_number(domain, false);
2671	if (!NT_STATUS_IS_OK(status)) {
2672		return status;
2673	}
2674	centry = centry_start(domain, status);
2675	if (!centry)
2676		goto skip_save;
2677	centry_put_uint32(centry, *num_names);
2678	for (i=0; i<(*num_names); i++) {
2679		centry_put_sid(centry, &(*sid_mem)[i]);
2680		centry_put_string(centry, (*names)[i]);
2681		centry_put_uint32(centry, (*name_types)[i]);
2682	}
2683	centry_end(centry, "GM/%s", sid_to_fstring(sid_string, group_sid));
2684	centry_free(centry);
2685
2686skip_save:
2687	return status;
2688}
2689
2690/* find the sequence number for a domain */
2691static NTSTATUS sequence_number(struct winbindd_domain *domain, uint32 *seq)
2692{
2693	refresh_sequence_number(domain, false);
2694
2695	*seq = domain->sequence_number;
2696
2697	return NT_STATUS_OK;
2698}
2699
2700/* enumerate trusted domains
2701 * (we need to have the list of trustdoms in the cache when we go offline) -
2702 * Guenther */
2703static NTSTATUS trusted_domains(struct winbindd_domain *domain,
2704				TALLOC_CTX *mem_ctx,
2705				struct netr_DomainTrustList *trusts)
2706{
2707 	NTSTATUS status;
2708	struct winbind_cache *cache;
2709	struct winbindd_tdc_domain *dom_list = NULL;
2710	size_t num_domains = 0;
2711	bool retval = false;
2712	int i;
2713	bool old_status;
2714
2715	old_status = domain->online;
2716	trusts->count = 0;
2717	trusts->array = NULL;
2718
2719	cache = get_cache(domain);
2720	if (!cache || !cache->tdb) {
2721		goto do_query;
2722	}
2723
2724	if (domain->online) {
2725		goto do_query;
2726	}
2727
2728	retval = wcache_tdc_fetch_list(&dom_list, &num_domains);
2729	if (!retval || !num_domains || !dom_list) {
2730		TALLOC_FREE(dom_list);
2731		goto do_query;
2732	}
2733
2734do_fetch_cache:
2735	trusts->array = TALLOC_ZERO_ARRAY(mem_ctx, struct netr_DomainTrust, num_domains);
2736	if (!trusts->array) {
2737		TALLOC_FREE(dom_list);
2738		return NT_STATUS_NO_MEMORY;
2739	}
2740
2741	for (i = 0; i < num_domains; i++) {
2742		struct netr_DomainTrust *trust;
2743		struct dom_sid *sid;
2744		struct winbindd_domain *dom;
2745
2746		dom = find_domain_from_name_noinit(dom_list[i].domain_name);
2747		if (dom && dom->internal) {
2748			continue;
2749		}
2750
2751		trust = &trusts->array[trusts->count];
2752		trust->netbios_name = talloc_strdup(trusts->array, dom_list[i].domain_name);
2753		trust->dns_name = talloc_strdup(trusts->array, dom_list[i].dns_name);
2754		sid = talloc(trusts->array, struct dom_sid);
2755		if (!trust->netbios_name || !trust->dns_name ||
2756			!sid) {
2757			TALLOC_FREE(dom_list);
2758			TALLOC_FREE(trusts->array);
2759			return NT_STATUS_NO_MEMORY;
2760		}
2761
2762		trust->trust_flags = dom_list[i].trust_flags;
2763		trust->trust_attributes = dom_list[i].trust_attribs;
2764		trust->trust_type = dom_list[i].trust_type;
2765		sid_copy(sid, &dom_list[i].sid);
2766		trust->sid = sid;
2767		trusts->count++;
2768	}
2769
2770	TALLOC_FREE(dom_list);
2771	return NT_STATUS_OK;
2772
2773do_query:
2774	/* Return status value returned by seq number check */
2775
2776 	if (!NT_STATUS_IS_OK(domain->last_status))
2777 		return domain->last_status;
2778
2779	DEBUG(10,("trusted_domains: [Cached] - doing backend query for info for domain %s\n",
2780		domain->name ));
2781
2782	status = domain->backend->trusted_domains(domain, mem_ctx, trusts);
2783
2784	if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2785		NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2786		if (!domain->internal && old_status) {
2787			set_domain_offline(domain);
2788		}
2789		if (!domain->internal &&
2790			!domain->online &&
2791			old_status) {
2792			retval = wcache_tdc_fetch_list(&dom_list, &num_domains);
2793			if (retval && num_domains && dom_list) {
2794				TALLOC_FREE(trusts->array);
2795				trusts->count = 0;
2796				goto do_fetch_cache;
2797			}
2798		}
2799	}
2800	/* no trusts gives NT_STATUS_NO_MORE_ENTRIES resetting to NT_STATUS_OK
2801	 * so that the generic centry handling still applies correctly -
2802	 * Guenther*/
2803
2804	if (!NT_STATUS_IS_ERR(status)) {
2805		status = NT_STATUS_OK;
2806	}
2807 	return status;
2808}
2809
2810/* get lockout policy */
2811static NTSTATUS lockout_policy(struct winbindd_domain *domain,
2812 			       TALLOC_CTX *mem_ctx,
2813			       struct samr_DomInfo12 *policy)
2814{
2815 	struct winbind_cache *cache = get_cache(domain);
2816 	struct cache_entry *centry = NULL;
2817 	NTSTATUS status;
2818	bool old_status;
2819
2820	old_status = domain->online;
2821	if (!cache->tdb)
2822		goto do_query;
2823
2824	centry = wcache_fetch(cache, domain, "LOC_POL/%s", domain->name);
2825
2826	if (!centry)
2827 		goto do_query;
2828
2829do_fetch_cache:
2830	policy->lockout_duration = centry_nttime(centry);
2831	policy->lockout_window = centry_nttime(centry);
2832	policy->lockout_threshold = centry_uint16(centry);
2833
2834 	status = centry->status;
2835
2836	DEBUG(10,("lockout_policy: [Cached] - cached info for domain %s status: %s\n",
2837		domain->name, nt_errstr(status) ));
2838
2839 	centry_free(centry);
2840 	return status;
2841
2842do_query:
2843	ZERO_STRUCTP(policy);
2844
2845	/* Return status value returned by seq number check */
2846
2847 	if (!NT_STATUS_IS_OK(domain->last_status))
2848 		return domain->last_status;
2849
2850	DEBUG(10,("lockout_policy: [Cached] - doing backend query for info for domain %s\n",
2851		domain->name ));
2852
2853	status = domain->backend->lockout_policy(domain, mem_ctx, policy);
2854
2855	if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2856		NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2857		if (!domain->internal && old_status) {
2858			set_domain_offline(domain);
2859		}
2860		if (cache->tdb &&
2861			!domain->internal &&
2862			!domain->online &&
2863			old_status) {
2864			centry = wcache_fetch(cache, domain, "LOC_POL/%s", domain->name);
2865			if (centry) {
2866				goto do_fetch_cache;
2867			}
2868		}
2869	}
2870	/* and save it */
2871 	refresh_sequence_number(domain, false);
2872	if (!NT_STATUS_IS_OK(status)) {
2873		return status;
2874	}
2875	wcache_save_lockout_policy(domain, status, policy);
2876
2877 	return status;
2878}
2879
2880/* get password policy */
2881static NTSTATUS password_policy(struct winbindd_domain *domain,
2882				TALLOC_CTX *mem_ctx,
2883				struct samr_DomInfo1 *policy)
2884{
2885	struct winbind_cache *cache = get_cache(domain);
2886	struct cache_entry *centry = NULL;
2887	NTSTATUS status;
2888	bool old_status;
2889
2890	old_status = domain->online;
2891	if (!cache->tdb)
2892		goto do_query;
2893
2894	centry = wcache_fetch(cache, domain, "PWD_POL/%s", domain->name);
2895
2896	if (!centry)
2897		goto do_query;
2898
2899do_fetch_cache:
2900	policy->min_password_length = centry_uint16(centry);
2901	policy->password_history_length = centry_uint16(centry);
2902	policy->password_properties = centry_uint32(centry);
2903	policy->max_password_age = centry_nttime(centry);
2904	policy->min_password_age = centry_nttime(centry);
2905
2906	status = centry->status;
2907
2908	DEBUG(10,("lockout_policy: [Cached] - cached info for domain %s status: %s\n",
2909		domain->name, nt_errstr(status) ));
2910
2911	centry_free(centry);
2912	return status;
2913
2914do_query:
2915	ZERO_STRUCTP(policy);
2916
2917	/* Return status value returned by seq number check */
2918
2919	if (!NT_STATUS_IS_OK(domain->last_status))
2920		return domain->last_status;
2921
2922	DEBUG(10,("password_policy: [Cached] - doing backend query for info for domain %s\n",
2923		domain->name ));
2924
2925	status = domain->backend->password_policy(domain, mem_ctx, policy);
2926
2927	if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2928		NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2929		if (!domain->internal && old_status) {
2930			set_domain_offline(domain);
2931		}
2932		if (cache->tdb &&
2933			!domain->internal &&
2934			!domain->online &&
2935			old_status) {
2936			centry = wcache_fetch(cache, domain, "PWD_POL/%s", domain->name);
2937			if (centry) {
2938				goto do_fetch_cache;
2939			}
2940		}
2941	}
2942	/* and save it */
2943	refresh_sequence_number(domain, false);
2944	if (!NT_STATUS_IS_OK(status)) {
2945		return status;
2946	}
2947	wcache_save_password_policy(domain, status, policy);
2948
2949	return status;
2950}
2951
2952
2953/* Invalidate cached user and group lists coherently */
2954
2955static int traverse_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
2956		       void *state)
2957{
2958	if (strncmp((const char *)kbuf.dptr, "UL/", 3) == 0 ||
2959	    strncmp((const char *)kbuf.dptr, "GL/", 3) == 0)
2960		tdb_delete(the_tdb, kbuf);
2961
2962	return 0;
2963}
2964
2965/* Invalidate the getpwnam and getgroups entries for a winbindd domain */
2966
2967void wcache_invalidate_samlogon(struct winbindd_domain *domain,
2968				struct netr_SamInfo3 *info3)
2969{
2970        DOM_SID sid;
2971        fstring key_str, sid_string;
2972	struct winbind_cache *cache;
2973
2974	/* dont clear cached U/SID and UG/SID entries when we want to logon
2975	 * offline - gd */
2976
2977	if (lp_winbind_offline_logon()) {
2978		return;
2979	}
2980
2981	if (!domain)
2982		return;
2983
2984	cache = get_cache(domain);
2985
2986        if (!cache->tdb) {
2987                return;
2988        }
2989
2990	sid_copy(&sid, info3->base.domain_sid);
2991	sid_append_rid(&sid, info3->base.rid);
2992
2993	/* Clear U/SID cache entry */
2994	fstr_sprintf(key_str, "U/%s", sid_to_fstring(sid_string, &sid));
2995	DEBUG(10, ("wcache_invalidate_samlogon: clearing %s\n", key_str));
2996	tdb_delete(cache->tdb, string_tdb_data(key_str));
2997
2998	/* Clear UG/SID cache entry */
2999	fstr_sprintf(key_str, "UG/%s", sid_to_fstring(sid_string, &sid));
3000	DEBUG(10, ("wcache_invalidate_samlogon: clearing %s\n", key_str));
3001	tdb_delete(cache->tdb, string_tdb_data(key_str));
3002
3003	/* Samba/winbindd never needs this. */
3004	netsamlogon_clear_cached_user(info3);
3005}
3006
3007bool wcache_invalidate_cache(void)
3008{
3009	struct winbindd_domain *domain;
3010
3011	for (domain = domain_list(); domain; domain = domain->next) {
3012		struct winbind_cache *cache = get_cache(domain);
3013
3014		DEBUG(10, ("wcache_invalidate_cache: invalidating cache "
3015			   "entries for %s\n", domain->name));
3016		if (cache) {
3017			if (cache->tdb) {
3018				tdb_traverse(cache->tdb, traverse_fn, NULL);
3019			} else {
3020				return false;
3021			}
3022		}
3023	}
3024	return true;
3025}
3026
3027bool init_wcache(void)
3028{
3029	if (wcache == NULL) {
3030		wcache = SMB_XMALLOC_P(struct winbind_cache);
3031		ZERO_STRUCTP(wcache);
3032	}
3033
3034	if (wcache->tdb != NULL)
3035		return true;
3036
3037	/* when working offline we must not clear the cache on restart */
3038	wcache->tdb = tdb_open_log(cache_path("winbindd_cache.tdb"),
3039				WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
3040				lp_winbind_offline_logon() ? TDB_DEFAULT : (TDB_DEFAULT | TDB_CLEAR_IF_FIRST),
3041				O_RDWR|O_CREAT, 0600);
3042
3043	if (wcache->tdb == NULL) {
3044		DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
3045		return false;
3046	}
3047
3048	return true;
3049}
3050
3051/************************************************************************
3052 This is called by the parent to initialize the cache file.
3053 We don't need sophisticated locking here as we know we're the
3054 only opener.
3055************************************************************************/
3056
3057bool initialize_winbindd_cache(void)
3058{
3059	bool cache_bad = true;
3060	uint32 vers;
3061
3062	if (!init_wcache()) {
3063		DEBUG(0,("initialize_winbindd_cache: init_wcache failed.\n"));
3064		return false;
3065	}
3066
3067	/* Check version number. */
3068	if (tdb_fetch_uint32(wcache->tdb, WINBINDD_CACHE_VERSION_KEYSTR, &vers) &&
3069			vers == WINBINDD_CACHE_VERSION) {
3070		cache_bad = false;
3071	}
3072
3073	if (cache_bad) {
3074		DEBUG(0,("initialize_winbindd_cache: clearing cache "
3075			"and re-creating with version number %d\n",
3076			WINBINDD_CACHE_VERSION ));
3077
3078		tdb_close(wcache->tdb);
3079		wcache->tdb = NULL;
3080
3081		if (unlink(cache_path("winbindd_cache.tdb")) == -1) {
3082			DEBUG(0,("initialize_winbindd_cache: unlink %s failed %s ",
3083				cache_path("winbindd_cache.tdb"),
3084				strerror(errno) ));
3085			return false;
3086		}
3087		if (!init_wcache()) {
3088			DEBUG(0,("initialize_winbindd_cache: re-initialization "
3089					"init_wcache failed.\n"));
3090			return false;
3091		}
3092
3093		/* Write the version. */
3094		if (!tdb_store_uint32(wcache->tdb, WINBINDD_CACHE_VERSION_KEYSTR, WINBINDD_CACHE_VERSION)) {
3095			DEBUG(0,("initialize_winbindd_cache: version number store failed %s\n",
3096				tdb_errorstr(wcache->tdb) ));
3097			return false;
3098		}
3099	}
3100
3101	tdb_close(wcache->tdb);
3102	wcache->tdb = NULL;
3103	return true;
3104}
3105
3106void close_winbindd_cache(void)
3107{
3108	if (!wcache) {
3109		return;
3110	}
3111	if (wcache->tdb) {
3112		tdb_close(wcache->tdb);
3113		wcache->tdb = NULL;
3114	}
3115}
3116
3117bool lookup_cached_sid(TALLOC_CTX *mem_ctx, const DOM_SID *sid,
3118		       char **domain_name, char **name,
3119		       enum lsa_SidType *type)
3120{
3121	struct winbindd_domain *domain;
3122	NTSTATUS status;
3123
3124	domain = find_lookup_domain_from_sid(sid);
3125	if (domain == NULL) {
3126		return false;
3127	}
3128	status = wcache_sid_to_name(domain, sid, mem_ctx, domain_name, name,
3129				    type);
3130	return NT_STATUS_IS_OK(status);
3131}
3132
3133bool lookup_cached_name(TALLOC_CTX *mem_ctx,
3134			const char *domain_name,
3135			const char *name,
3136			DOM_SID *sid,
3137			enum lsa_SidType *type)
3138{
3139	struct winbindd_domain *domain;
3140	NTSTATUS status;
3141	bool original_online_state;
3142
3143	domain = find_lookup_domain_from_name(domain_name);
3144	if (domain == NULL) {
3145		return false;
3146	}
3147
3148	/* If we are doing a cached logon, temporarily set the domain
3149	   offline so the cache won't expire the entry */
3150
3151	original_online_state = domain->online;
3152	domain->online = false;
3153	status = wcache_name_to_sid(domain, domain_name, name, sid, type);
3154	domain->online = original_online_state;
3155
3156	return NT_STATUS_IS_OK(status);
3157}
3158
3159void cache_name2sid(struct winbindd_domain *domain,
3160		    const char *domain_name, const char *name,
3161		    enum lsa_SidType type, const DOM_SID *sid)
3162{
3163	refresh_sequence_number(domain, false);
3164	wcache_save_name_to_sid(domain, NT_STATUS_OK, domain_name, name,
3165				sid, type);
3166}
3167
3168/*
3169 * The original idea that this cache only contains centries has
3170 * been blurred - now other stuff gets put in here. Ensure we
3171 * ignore these things on cleanup.
3172 */
3173
3174static int traverse_fn_cleanup(TDB_CONTEXT *the_tdb, TDB_DATA kbuf,
3175			       TDB_DATA dbuf, void *state)
3176{
3177	struct cache_entry *centry;
3178
3179	if (is_non_centry_key(kbuf)) {
3180		return 0;
3181	}
3182
3183	centry = wcache_fetch_raw((char *)kbuf.dptr);
3184	if (!centry) {
3185		return 0;
3186	}
3187
3188	if (!NT_STATUS_IS_OK(centry->status)) {
3189		DEBUG(10,("deleting centry %s\n", (const char *)kbuf.dptr));
3190		tdb_delete(the_tdb, kbuf);
3191	}
3192
3193	centry_free(centry);
3194	return 0;
3195}
3196
3197/* flush the cache */
3198void wcache_flush_cache(void)
3199{
3200	if (!wcache)
3201		return;
3202	if (wcache->tdb) {
3203		tdb_close(wcache->tdb);
3204		wcache->tdb = NULL;
3205	}
3206	if (!winbindd_use_cache()) {
3207		return;
3208	}
3209
3210	/* when working offline we must not clear the cache on restart */
3211	wcache->tdb = tdb_open_log(cache_path("winbindd_cache.tdb"),
3212				WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
3213				lp_winbind_offline_logon() ? TDB_DEFAULT : (TDB_DEFAULT | TDB_CLEAR_IF_FIRST),
3214				O_RDWR|O_CREAT, 0600);
3215
3216	if (!wcache->tdb) {
3217		DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
3218		return;
3219	}
3220
3221	tdb_traverse(wcache->tdb, traverse_fn_cleanup, NULL);
3222
3223	DEBUG(10,("wcache_flush_cache success\n"));
3224}
3225
3226/* Count cached creds */
3227
3228static int traverse_fn_cached_creds(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
3229			 	    void *state)
3230{
3231	int *cred_count = (int*)state;
3232
3233	if (strncmp((const char *)kbuf.dptr, "CRED/", 5) == 0) {
3234		(*cred_count)++;
3235	}
3236	return 0;
3237}
3238
3239NTSTATUS wcache_count_cached_creds(struct winbindd_domain *domain, int *count)
3240{
3241	struct winbind_cache *cache = get_cache(domain);
3242
3243	*count = 0;
3244
3245	if (!cache->tdb) {
3246		return NT_STATUS_INTERNAL_DB_ERROR;
3247	}
3248
3249	tdb_traverse(cache->tdb, traverse_fn_cached_creds, (void *)count);
3250
3251	return NT_STATUS_OK;
3252}
3253
3254struct cred_list {
3255	struct cred_list *prev, *next;
3256	TDB_DATA key;
3257	fstring name;
3258	time_t created;
3259};
3260static struct cred_list *wcache_cred_list;
3261
3262static int traverse_fn_get_credlist(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
3263				    void *state)
3264{
3265	struct cred_list *cred;
3266
3267	if (strncmp((const char *)kbuf.dptr, "CRED/", 5) == 0) {
3268
3269		cred = SMB_MALLOC_P(struct cred_list);
3270		if (cred == NULL) {
3271			DEBUG(0,("traverse_fn_remove_first_creds: failed to malloc new entry for list\n"));
3272			return -1;
3273		}
3274
3275		ZERO_STRUCTP(cred);
3276
3277		/* save a copy of the key */
3278
3279		fstrcpy(cred->name, (const char *)kbuf.dptr);
3280		DLIST_ADD(wcache_cred_list, cred);
3281	}
3282
3283	return 0;
3284}
3285
3286NTSTATUS wcache_remove_oldest_cached_creds(struct winbindd_domain *domain, const DOM_SID *sid)
3287{
3288	struct winbind_cache *cache = get_cache(domain);
3289	NTSTATUS status;
3290	int ret;
3291	struct cred_list *cred, *oldest = NULL;
3292
3293	if (!cache->tdb) {
3294		return NT_STATUS_INTERNAL_DB_ERROR;
3295	}
3296
3297	/* we possibly already have an entry */
3298 	if (sid && NT_STATUS_IS_OK(wcache_cached_creds_exist(domain, sid))) {
3299
3300		fstring key_str, tmp;
3301
3302		DEBUG(11,("we already have an entry, deleting that\n"));
3303
3304		fstr_sprintf(key_str, "CRED/%s", sid_to_fstring(tmp, sid));
3305
3306		tdb_delete(cache->tdb, string_tdb_data(key_str));
3307
3308		return NT_STATUS_OK;
3309	}
3310
3311	ret = tdb_traverse(cache->tdb, traverse_fn_get_credlist, NULL);
3312	if (ret == 0) {
3313		return NT_STATUS_OK;
3314	} else if ((ret == -1) || (wcache_cred_list == NULL)) {
3315		return NT_STATUS_OBJECT_NAME_NOT_FOUND;
3316	}
3317
3318	ZERO_STRUCTP(oldest);
3319
3320	for (cred = wcache_cred_list; cred; cred = cred->next) {
3321
3322		TDB_DATA data;
3323		time_t t;
3324
3325		data = tdb_fetch(cache->tdb, string_tdb_data(cred->name));
3326		if (!data.dptr) {
3327			DEBUG(10,("wcache_remove_oldest_cached_creds: entry for [%s] not found\n",
3328				cred->name));
3329			status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
3330			goto done;
3331		}
3332
3333		t = IVAL(data.dptr, 0);
3334		SAFE_FREE(data.dptr);
3335
3336		if (!oldest) {
3337			oldest = SMB_MALLOC_P(struct cred_list);
3338			if (oldest == NULL) {
3339				status = NT_STATUS_NO_MEMORY;
3340				goto done;
3341			}
3342
3343			fstrcpy(oldest->name, cred->name);
3344			oldest->created = t;
3345			continue;
3346		}
3347
3348		if (t < oldest->created) {
3349			fstrcpy(oldest->name, cred->name);
3350			oldest->created = t;
3351		}
3352	}
3353
3354	if (tdb_delete(cache->tdb, string_tdb_data(oldest->name)) == 0) {
3355		status = NT_STATUS_OK;
3356	} else {
3357		status = NT_STATUS_UNSUCCESSFUL;
3358	}
3359done:
3360	SAFE_FREE(wcache_cred_list);
3361	SAFE_FREE(oldest);
3362
3363	return status;
3364}
3365
3366/* Change the global online/offline state. */
3367bool set_global_winbindd_state_offline(void)
3368{
3369	TDB_DATA data;
3370
3371	DEBUG(10,("set_global_winbindd_state_offline: offline requested.\n"));
3372
3373	/* Only go offline if someone has created
3374	   the key "WINBINDD_OFFLINE" in the cache tdb. */
3375
3376	if (wcache == NULL || wcache->tdb == NULL) {
3377		DEBUG(10,("set_global_winbindd_state_offline: wcache not open yet.\n"));
3378		return false;
3379	}
3380
3381	if (!lp_winbind_offline_logon()) {
3382		DEBUG(10,("set_global_winbindd_state_offline: rejecting.\n"));
3383		return false;
3384	}
3385
3386	if (global_winbindd_offline_state) {
3387		/* Already offline. */
3388		return true;
3389	}
3390
3391	data = tdb_fetch_bystring( wcache->tdb, "WINBINDD_OFFLINE" );
3392
3393	if (!data.dptr || data.dsize != 4) {
3394		DEBUG(10,("set_global_winbindd_state_offline: offline state not set.\n"));
3395		SAFE_FREE(data.dptr);
3396		return false;
3397	} else {
3398		DEBUG(10,("set_global_winbindd_state_offline: offline state set.\n"));
3399		global_winbindd_offline_state = true;
3400		SAFE_FREE(data.dptr);
3401		return true;
3402	}
3403}
3404
3405void set_global_winbindd_state_online(void)
3406{
3407	DEBUG(10,("set_global_winbindd_state_online: online requested.\n"));
3408
3409	if (!lp_winbind_offline_logon()) {
3410		DEBUG(10,("set_global_winbindd_state_online: rejecting.\n"));
3411		return;
3412	}
3413
3414	if (!global_winbindd_offline_state) {
3415		/* Already online. */
3416		return;
3417	}
3418	global_winbindd_offline_state = false;
3419
3420	if (!wcache->tdb) {
3421		return;
3422	}
3423
3424	/* Ensure there is no key "WINBINDD_OFFLINE" in the cache tdb. */
3425	tdb_delete_bystring(wcache->tdb, "WINBINDD_OFFLINE");
3426}
3427
3428bool get_global_winbindd_state_offline(void)
3429{
3430	return global_winbindd_offline_state;
3431}
3432
3433/***********************************************************************
3434 Validate functions for all possible cache tdb keys.
3435***********************************************************************/
3436
3437static struct cache_entry *create_centry_validate(const char *kstr, TDB_DATA data,
3438						  struct tdb_validation_status *state)
3439{
3440	struct cache_entry *centry;
3441
3442	centry = SMB_XMALLOC_P(struct cache_entry);
3443	centry->data = (unsigned char *)memdup(data.dptr, data.dsize);
3444	if (!centry->data) {
3445		SAFE_FREE(centry);
3446		return NULL;
3447	}
3448	centry->len = data.dsize;
3449	centry->ofs = 0;
3450
3451	if (centry->len < 8) {
3452		/* huh? corrupt cache? */
3453		DEBUG(0,("create_centry_validate: Corrupt cache for key %s (len < 8) ?\n", kstr));
3454		centry_free(centry);
3455		state->bad_entry = true;
3456		state->success = false;
3457		return NULL;
3458	}
3459
3460	centry->status = NT_STATUS(centry_uint32(centry));
3461	centry->sequence_number = centry_uint32(centry);
3462	return centry;
3463}
3464
3465static int validate_seqnum(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3466			   struct tdb_validation_status *state)
3467{
3468	if (dbuf.dsize != 8) {
3469		DEBUG(0,("validate_seqnum: Corrupt cache for key %s (len %u != 8) ?\n",
3470				keystr, (unsigned int)dbuf.dsize ));
3471		state->bad_entry = true;
3472		return 1;
3473	}
3474	return 0;
3475}
3476
3477static int validate_ns(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3478		       struct tdb_validation_status *state)
3479{
3480	struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3481	if (!centry) {
3482		return 1;
3483	}
3484
3485	(void)centry_uint32(centry);
3486	if (NT_STATUS_IS_OK(centry->status)) {
3487		DOM_SID sid;
3488		(void)centry_sid(centry, &sid);
3489	}
3490
3491	centry_free(centry);
3492
3493	if (!(state->success)) {
3494		return 1;
3495	}
3496	DEBUG(10,("validate_ns: %s ok\n", keystr));
3497	return 0;
3498}
3499
3500static int validate_sn(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3501		       struct tdb_validation_status *state)
3502{
3503	struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3504	if (!centry) {
3505		return 1;
3506	}
3507
3508	if (NT_STATUS_IS_OK(centry->status)) {
3509		(void)centry_uint32(centry);
3510		(void)centry_string(centry, mem_ctx);
3511		(void)centry_string(centry, mem_ctx);
3512	}
3513
3514	centry_free(centry);
3515
3516	if (!(state->success)) {
3517		return 1;
3518	}
3519	DEBUG(10,("validate_sn: %s ok\n", keystr));
3520	return 0;
3521}
3522
3523static int validate_u(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3524		      struct tdb_validation_status *state)
3525{
3526	struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3527	DOM_SID sid;
3528
3529	if (!centry) {
3530		return 1;
3531	}
3532
3533	(void)centry_string(centry, mem_ctx);
3534	(void)centry_string(centry, mem_ctx);
3535	(void)centry_string(centry, mem_ctx);
3536	(void)centry_string(centry, mem_ctx);
3537	(void)centry_uint32(centry);
3538	(void)centry_sid(centry, &sid);
3539	(void)centry_sid(centry, &sid);
3540
3541	centry_free(centry);
3542
3543	if (!(state->success)) {
3544		return 1;
3545	}
3546	DEBUG(10,("validate_u: %s ok\n", keystr));
3547	return 0;
3548}
3549
3550static int validate_loc_pol(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3551			    struct tdb_validation_status *state)
3552{
3553	struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3554
3555	if (!centry) {
3556		return 1;
3557	}
3558
3559	(void)centry_nttime(centry);
3560	(void)centry_nttime(centry);
3561	(void)centry_uint16(centry);
3562
3563	centry_free(centry);
3564
3565	if (!(state->success)) {
3566		return 1;
3567	}
3568	DEBUG(10,("validate_loc_pol: %s ok\n", keystr));
3569	return 0;
3570}
3571
3572static int validate_pwd_pol(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3573			    struct tdb_validation_status *state)
3574{
3575	struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3576
3577	if (!centry) {
3578		return 1;
3579	}
3580
3581	(void)centry_uint16(centry);
3582	(void)centry_uint16(centry);
3583	(void)centry_uint32(centry);
3584	(void)centry_nttime(centry);
3585	(void)centry_nttime(centry);
3586
3587	centry_free(centry);
3588
3589	if (!(state->success)) {
3590		return 1;
3591	}
3592	DEBUG(10,("validate_pwd_pol: %s ok\n", keystr));
3593	return 0;
3594}
3595
3596static int validate_cred(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3597			 struct tdb_validation_status *state)
3598{
3599	struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3600
3601	if (!centry) {
3602		return 1;
3603	}
3604
3605	(void)centry_time(centry);
3606	(void)centry_hash16(centry, mem_ctx);
3607
3608	/* We only have 17 bytes more data in the salted cred case. */
3609	if (centry->len - centry->ofs == 17) {
3610		(void)centry_hash16(centry, mem_ctx);
3611	}
3612
3613	centry_free(centry);
3614
3615	if (!(state->success)) {
3616		return 1;
3617	}
3618	DEBUG(10,("validate_cred: %s ok\n", keystr));
3619	return 0;
3620}
3621
3622static int validate_ul(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3623		       struct tdb_validation_status *state)
3624{
3625	struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3626	int32 num_entries, i;
3627
3628	if (!centry) {
3629		return 1;
3630	}
3631
3632	num_entries = (int32)centry_uint32(centry);
3633
3634	for (i=0; i< num_entries; i++) {
3635		DOM_SID sid;
3636		(void)centry_string(centry, mem_ctx);
3637		(void)centry_string(centry, mem_ctx);
3638		(void)centry_string(centry, mem_ctx);
3639		(void)centry_string(centry, mem_ctx);
3640		(void)centry_sid(centry, &sid);
3641		(void)centry_sid(centry, &sid);
3642	}
3643
3644	centry_free(centry);
3645
3646	if (!(state->success)) {
3647		return 1;
3648	}
3649	DEBUG(10,("validate_ul: %s ok\n", keystr));
3650	return 0;
3651}
3652
3653static int validate_gl(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3654		       struct tdb_validation_status *state)
3655{
3656	struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3657	int32 num_entries, i;
3658
3659	if (!centry) {
3660		return 1;
3661	}
3662
3663	num_entries = centry_uint32(centry);
3664
3665	for (i=0; i< num_entries; i++) {
3666		(void)centry_string(centry, mem_ctx);
3667		(void)centry_string(centry, mem_ctx);
3668		(void)centry_uint32(centry);
3669	}
3670
3671	centry_free(centry);
3672
3673	if (!(state->success)) {
3674		return 1;
3675	}
3676	DEBUG(10,("validate_gl: %s ok\n", keystr));
3677	return 0;
3678}
3679
3680static int validate_ug(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3681		       struct tdb_validation_status *state)
3682{
3683	struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3684	int32 num_groups, i;
3685
3686	if (!centry) {
3687		return 1;
3688	}
3689
3690	num_groups = centry_uint32(centry);
3691
3692	for (i=0; i< num_groups; i++) {
3693		DOM_SID sid;
3694		centry_sid(centry, &sid);
3695	}
3696
3697	centry_free(centry);
3698
3699	if (!(state->success)) {
3700		return 1;
3701	}
3702	DEBUG(10,("validate_ug: %s ok\n", keystr));
3703	return 0;
3704}
3705
3706static int validate_ua(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3707		       struct tdb_validation_status *state)
3708{
3709	struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3710	int32 num_aliases, i;
3711
3712	if (!centry) {
3713		return 1;
3714	}
3715
3716	num_aliases = centry_uint32(centry);
3717
3718	for (i=0; i < num_aliases; i++) {
3719		(void)centry_uint32(centry);
3720	}
3721
3722	centry_free(centry);
3723
3724	if (!(state->success)) {
3725		return 1;
3726	}
3727	DEBUG(10,("validate_ua: %s ok\n", keystr));
3728	return 0;
3729}
3730
3731static int validate_gm(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3732		       struct tdb_validation_status *state)
3733{
3734	struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3735	int32 num_names, i;
3736
3737	if (!centry) {
3738		return 1;
3739	}
3740
3741	num_names = centry_uint32(centry);
3742
3743	for (i=0; i< num_names; i++) {
3744		DOM_SID sid;
3745		centry_sid(centry, &sid);
3746		(void)centry_string(centry, mem_ctx);
3747		(void)centry_uint32(centry);
3748	}
3749
3750	centry_free(centry);
3751
3752	if (!(state->success)) {
3753		return 1;
3754	}
3755	DEBUG(10,("validate_gm: %s ok\n", keystr));
3756	return 0;
3757}
3758
3759static int validate_dr(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3760		       struct tdb_validation_status *state)
3761{
3762	/* Can't say anything about this other than must be nonzero. */
3763	if (dbuf.dsize == 0) {
3764		DEBUG(0,("validate_dr: Corrupt cache for key %s (len == 0) ?\n",
3765				keystr));
3766		state->bad_entry = true;
3767		state->success = false;
3768		return 1;
3769	}
3770
3771	DEBUG(10,("validate_dr: %s ok\n", keystr));
3772	return 0;
3773}
3774
3775static int validate_de(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3776		       struct tdb_validation_status *state)
3777{
3778	/* Can't say anything about this other than must be nonzero. */
3779	if (dbuf.dsize == 0) {
3780		DEBUG(0,("validate_de: Corrupt cache for key %s (len == 0) ?\n",
3781				keystr));
3782		state->bad_entry = true;
3783		state->success = false;
3784		return 1;
3785	}
3786
3787	DEBUG(10,("validate_de: %s ok\n", keystr));
3788	return 0;
3789}
3790
3791static int validate_pwinfo(TALLOC_CTX *mem_ctx, const char *keystr,
3792			   TDB_DATA dbuf, struct tdb_validation_status *state)
3793{
3794	struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3795
3796	if (!centry) {
3797		return 1;
3798	}
3799
3800	(void)centry_string(centry, mem_ctx);
3801	(void)centry_string(centry, mem_ctx);
3802	(void)centry_string(centry, mem_ctx);
3803	(void)centry_uint32(centry);
3804
3805	centry_free(centry);
3806
3807	if (!(state->success)) {
3808		return 1;
3809	}
3810	DEBUG(10,("validate_pwinfo: %s ok\n", keystr));
3811	return 0;
3812}
3813
3814static int validate_nss_an(TALLOC_CTX *mem_ctx, const char *keystr,
3815			   TDB_DATA dbuf,
3816			   struct tdb_validation_status *state)
3817{
3818	struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3819
3820	if (!centry) {
3821		return 1;
3822	}
3823
3824	(void)centry_string( centry, mem_ctx );
3825
3826	centry_free(centry);
3827
3828	if (!(state->success)) {
3829		return 1;
3830	}
3831	DEBUG(10,("validate_pwinfo: %s ok\n", keystr));
3832	return 0;
3833}
3834
3835static int validate_nss_na(TALLOC_CTX *mem_ctx, const char *keystr,
3836			   TDB_DATA dbuf,
3837			   struct tdb_validation_status *state)
3838{
3839	struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3840
3841	if (!centry) {
3842		return 1;
3843	}
3844
3845	(void)centry_string( centry, mem_ctx );
3846
3847	centry_free(centry);
3848
3849	if (!(state->success)) {
3850		return 1;
3851	}
3852	DEBUG(10,("validate_pwinfo: %s ok\n", keystr));
3853	return 0;
3854}
3855
3856static int validate_trustdomcache(TALLOC_CTX *mem_ctx, const char *keystr,
3857				  TDB_DATA dbuf,
3858				  struct tdb_validation_status *state)
3859{
3860	if (dbuf.dsize == 0) {
3861		DEBUG(0, ("validate_trustdomcache: Corrupt cache for "
3862			  "key %s (len ==0) ?\n", keystr));
3863		state->bad_entry = true;
3864		state->success = false;
3865		return 1;
3866	}
3867
3868	DEBUG(10,    ("validate_trustdomcache: %s ok\n", keystr));
3869	DEBUGADD(10, ("  Don't trust me, I am a DUMMY!\n"));
3870	return 0;
3871}
3872
3873static int validate_offline(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3874			    struct tdb_validation_status *state)
3875{
3876	if (dbuf.dsize != 4) {
3877		DEBUG(0,("validate_offline: Corrupt cache for key %s (len %u != 4) ?\n",
3878				keystr, (unsigned int)dbuf.dsize ));
3879		state->bad_entry = true;
3880		state->success = false;
3881		return 1;
3882	}
3883	DEBUG(10,("validate_offline: %s ok\n", keystr));
3884	return 0;
3885}
3886
3887static int validate_ndr(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3888			struct tdb_validation_status *state)
3889{
3890	/*
3891	 * Ignore validation for now. The proper way to do this is with a
3892	 * checksum. Just pure parsing does not really catch much.
3893	 */
3894	return 0;
3895}
3896
3897static int validate_cache_version(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3898				  struct tdb_validation_status *state)
3899{
3900	if (dbuf.dsize != 4) {
3901		DEBUG(0, ("validate_cache_version: Corrupt cache for "
3902			  "key %s (len %u != 4) ?\n",
3903			  keystr, (unsigned int)dbuf.dsize));
3904		state->bad_entry = true;
3905		state->success = false;
3906		return 1;
3907	}
3908
3909	DEBUG(10, ("validate_cache_version: %s ok\n", keystr));
3910	return 0;
3911}
3912
3913/***********************************************************************
3914 A list of all possible cache tdb keys with associated validation
3915 functions.
3916***********************************************************************/
3917
3918struct key_val_struct {
3919	const char *keyname;
3920	int (*validate_data_fn)(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf, struct tdb_validation_status* state);
3921} key_val[] = {
3922	{"SEQNUM/", validate_seqnum},
3923	{"NS/", validate_ns},
3924	{"SN/", validate_sn},
3925	{"U/", validate_u},
3926	{"LOC_POL/", validate_loc_pol},
3927	{"PWD_POL/", validate_pwd_pol},
3928	{"CRED/", validate_cred},
3929	{"UL/", validate_ul},
3930	{"GL/", validate_gl},
3931	{"UG/", validate_ug},
3932	{"UA", validate_ua},
3933	{"GM/", validate_gm},
3934	{"DR/", validate_dr},
3935	{"DE/", validate_de},
3936	{"NSS/PWINFO/", validate_pwinfo},
3937	{"TRUSTDOMCACHE/", validate_trustdomcache},
3938	{"NSS/NA/", validate_nss_na},
3939	{"NSS/AN/", validate_nss_an},
3940	{"WINBINDD_OFFLINE", validate_offline},
3941	{"NDR/", validate_ndr},
3942	{WINBINDD_CACHE_VERSION_KEYSTR, validate_cache_version},
3943	{NULL, NULL}
3944};
3945
3946/***********************************************************************
3947 Function to look at every entry in the tdb and validate it as far as
3948 possible.
3949***********************************************************************/
3950
3951static int cache_traverse_validate_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf, void *state)
3952{
3953	int i;
3954	unsigned int max_key_len = 1024;
3955	struct tdb_validation_status *v_state = (struct tdb_validation_status *)state;
3956
3957	/* Paranoia check. */
3958	if (strncmp("UA/", (const char *)kbuf.dptr, 3) == 0) {
3959		max_key_len = 1024 * 1024;
3960	}
3961	if (kbuf.dsize > max_key_len) {
3962		DEBUG(0, ("cache_traverse_validate_fn: key length too large: "
3963			  "(%u) > (%u)\n\n",
3964			  (unsigned int)kbuf.dsize, (unsigned int)max_key_len));
3965		return 1;
3966	}
3967
3968	for (i = 0; key_val[i].keyname; i++) {
3969		size_t namelen = strlen(key_val[i].keyname);
3970		if (kbuf.dsize >= namelen && (
3971				strncmp(key_val[i].keyname, (const char *)kbuf.dptr, namelen)) == 0) {
3972			TALLOC_CTX *mem_ctx;
3973			char *keystr;
3974			int ret;
3975
3976			keystr = SMB_MALLOC_ARRAY(char, kbuf.dsize+1);
3977			if (!keystr) {
3978				return 1;
3979			}
3980			memcpy(keystr, kbuf.dptr, kbuf.dsize);
3981			keystr[kbuf.dsize] = '\0';
3982
3983			mem_ctx = talloc_init("validate_ctx");
3984			if (!mem_ctx) {
3985				SAFE_FREE(keystr);
3986				return 1;
3987			}
3988
3989			ret = key_val[i].validate_data_fn(mem_ctx, keystr, dbuf,
3990							  v_state);
3991
3992			SAFE_FREE(keystr);
3993			talloc_destroy(mem_ctx);
3994			return ret;
3995		}
3996	}
3997
3998	DEBUG(0,("cache_traverse_validate_fn: unknown cache entry\nkey :\n"));
3999	dump_data(0, (uint8 *)kbuf.dptr, kbuf.dsize);
4000	DEBUG(0,("data :\n"));
4001	dump_data(0, (uint8 *)dbuf.dptr, dbuf.dsize);
4002	v_state->unknown_key = true;
4003	v_state->success = false;
4004	return 1; /* terminate. */
4005}
4006
4007static void validate_panic(const char *const why)
4008{
4009        DEBUG(0,("validating cache: would panic %s\n", why ));
4010	DEBUGADD(0, ("exiting instead (cache validation mode)\n"));
4011	exit(47);
4012}
4013
4014/***********************************************************************
4015 Try and validate every entry in the winbindd cache. If we fail here,
4016 delete the cache tdb and return non-zero.
4017***********************************************************************/
4018
4019int winbindd_validate_cache(void)
4020{
4021	int ret = -1;
4022	const char *tdb_path = cache_path("winbindd_cache.tdb");
4023	TDB_CONTEXT *tdb = NULL;
4024
4025	DEBUG(10, ("winbindd_validate_cache: replacing panic function\n"));
4026	smb_panic_fn = validate_panic;
4027
4028
4029	tdb = tdb_open_log(tdb_path,
4030			   WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
4031			   ( lp_winbind_offline_logon()
4032			     ? TDB_DEFAULT
4033			     : TDB_DEFAULT | TDB_CLEAR_IF_FIRST ),
4034			   O_RDWR|O_CREAT,
4035			   0600);
4036	if (!tdb) {
4037		DEBUG(0, ("winbindd_validate_cache: "
4038			  "error opening/initializing tdb\n"));
4039		goto done;
4040	}
4041	tdb_close(tdb);
4042
4043	ret = tdb_validate_and_backup(tdb_path, cache_traverse_validate_fn);
4044
4045	if (ret != 0) {
4046		DEBUG(10, ("winbindd_validate_cache: validation not successful.\n"));
4047		DEBUGADD(10, ("removing tdb %s.\n", tdb_path));
4048		unlink(tdb_path);
4049	}
4050
4051done:
4052	DEBUG(10, ("winbindd_validate_cache: restoring panic function\n"));
4053	smb_panic_fn = smb_panic;
4054	return ret;
4055}
4056
4057/***********************************************************************
4058 Try and validate every entry in the winbindd cache.
4059***********************************************************************/
4060
4061int winbindd_validate_cache_nobackup(void)
4062{
4063	int ret = -1;
4064	const char *tdb_path = cache_path("winbindd_cache.tdb");
4065
4066	DEBUG(10, ("winbindd_validate_cache: replacing panic function\n"));
4067	smb_panic_fn = validate_panic;
4068
4069
4070	if (wcache == NULL || wcache->tdb == NULL) {
4071		ret = tdb_validate_open(tdb_path, cache_traverse_validate_fn);
4072	} else {
4073		ret = tdb_validate(wcache->tdb, cache_traverse_validate_fn);
4074	}
4075
4076	if (ret != 0) {
4077		DEBUG(10, ("winbindd_validate_cache_nobackup: validation not "
4078			   "successful.\n"));
4079	}
4080
4081	DEBUG(10, ("winbindd_validate_cache_nobackup: restoring panic "
4082		   "function\n"));
4083	smb_panic_fn = smb_panic;
4084	return ret;
4085}
4086
4087bool winbindd_cache_validate_and_initialize(void)
4088{
4089	close_winbindd_cache();
4090
4091	if (lp_winbind_offline_logon()) {
4092		if (winbindd_validate_cache() < 0) {
4093			DEBUG(0, ("winbindd cache tdb corrupt and no backup "
4094				  "could be restored.\n"));
4095		}
4096	}
4097
4098	return initialize_winbindd_cache();
4099}
4100
4101/*********************************************************************
4102 ********************************************************************/
4103
4104static bool add_wbdomain_to_tdc_array( struct winbindd_domain *new_dom,
4105				       struct winbindd_tdc_domain **domains,
4106				       size_t *num_domains )
4107{
4108	struct winbindd_tdc_domain *list = NULL;
4109	size_t idx;
4110	int i;
4111	bool set_only = false;
4112
4113	/* don't allow duplicates */
4114
4115	idx = *num_domains;
4116	list = *domains;
4117
4118	for ( i=0; i< (*num_domains); i++ ) {
4119		if ( strequal( new_dom->name, list[i].domain_name ) ) {
4120			DEBUG(10,("add_wbdomain_to_tdc_array: Found existing record for %s\n",
4121				  new_dom->name));
4122			idx = i;
4123			set_only = true;
4124
4125			break;
4126		}
4127	}
4128
4129	if ( !set_only ) {
4130		if ( !*domains ) {
4131			list = TALLOC_ARRAY( NULL, struct winbindd_tdc_domain, 1 );
4132			idx = 0;
4133		} else {
4134			list = TALLOC_REALLOC_ARRAY( *domains, *domains,
4135						     struct winbindd_tdc_domain,
4136						     (*num_domains)+1);
4137			idx = *num_domains;
4138		}
4139
4140		ZERO_STRUCT( list[idx] );
4141	}
4142
4143	if ( !list )
4144		return false;
4145
4146	list[idx].domain_name = talloc_strdup( list, new_dom->name );
4147	list[idx].dns_name = talloc_strdup( list, new_dom->alt_name );
4148
4149	if ( !is_null_sid( &new_dom->sid ) ) {
4150		sid_copy( &list[idx].sid, &new_dom->sid );
4151	} else {
4152		sid_copy(&list[idx].sid, &global_sid_NULL);
4153	}
4154
4155	if ( new_dom->domain_flags != 0x0 )
4156		list[idx].trust_flags = new_dom->domain_flags;
4157
4158	if ( new_dom->domain_type != 0x0 )
4159		list[idx].trust_type = new_dom->domain_type;
4160
4161	if ( new_dom->domain_trust_attribs != 0x0 )
4162		list[idx].trust_attribs = new_dom->domain_trust_attribs;
4163
4164	if ( !set_only ) {
4165		*domains = list;
4166		*num_domains = idx + 1;
4167	}
4168
4169	return true;
4170}
4171
4172/*********************************************************************
4173 ********************************************************************/
4174
4175static TDB_DATA make_tdc_key( const char *domain_name )
4176{
4177	char *keystr = NULL;
4178	TDB_DATA key = { NULL, 0 };
4179
4180	if ( !domain_name ) {
4181		DEBUG(5,("make_tdc_key: Keyname workgroup is NULL!\n"));
4182		return key;
4183	}
4184
4185	if (asprintf( &keystr, "TRUSTDOMCACHE/%s", domain_name ) == -1) {
4186		return key;
4187	}
4188	key = string_term_tdb_data(keystr);
4189
4190	return key;
4191}
4192
4193/*********************************************************************
4194 ********************************************************************/
4195
4196static int pack_tdc_domains( struct winbindd_tdc_domain *domains,
4197			     size_t num_domains,
4198			     unsigned char **buf )
4199{
4200        unsigned char *buffer = NULL;
4201	int len = 0;
4202	int buflen = 0;
4203	int i = 0;
4204
4205	DEBUG(10,("pack_tdc_domains: Packing %d trusted domains\n",
4206		  (int)num_domains));
4207
4208	buflen = 0;
4209
4210 again:
4211	len = 0;
4212
4213	/* Store the number of array items first */
4214	len += tdb_pack( buffer+len, buflen-len, "d",
4215			 num_domains );
4216
4217	/* now pack each domain trust record */
4218	for ( i=0; i<num_domains; i++ ) {
4219
4220		fstring tmp;
4221
4222		if ( buflen > 0 ) {
4223			DEBUG(10,("pack_tdc_domains: Packing domain %s (%s)\n",
4224				  domains[i].domain_name,
4225				  domains[i].dns_name ? domains[i].dns_name : "UNKNOWN" ));
4226		}
4227
4228		len += tdb_pack( buffer+len, buflen-len, "fffddd",
4229				 domains[i].domain_name,
4230				 domains[i].dns_name,
4231				 sid_to_fstring(tmp, &domains[i].sid),
4232				 domains[i].trust_flags,
4233				 domains[i].trust_attribs,
4234				 domains[i].trust_type );
4235	}
4236
4237	if ( buflen < len ) {
4238		SAFE_FREE(buffer);
4239		if ( (buffer = SMB_MALLOC_ARRAY(unsigned char, len)) == NULL ) {
4240			DEBUG(0,("pack_tdc_domains: failed to alloc buffer!\n"));
4241			buflen = -1;
4242			goto done;
4243		}
4244		buflen = len;
4245		goto again;
4246	}
4247
4248	*buf = buffer;
4249
4250 done:
4251	return buflen;
4252}
4253
4254/*********************************************************************
4255 ********************************************************************/
4256
4257static size_t unpack_tdc_domains( unsigned char *buf, int buflen,
4258				  struct winbindd_tdc_domain **domains )
4259{
4260	fstring domain_name, dns_name, sid_string;
4261	uint32 type, attribs, flags;
4262	int num_domains;
4263	int len = 0;
4264	int i;
4265	struct winbindd_tdc_domain *list = NULL;
4266
4267	/* get the number of domains */
4268	len += tdb_unpack( buf+len, buflen-len, "d", &num_domains);
4269	if ( len == -1 ) {
4270		DEBUG(5,("unpack_tdc_domains: Failed to unpack domain array\n"));
4271		return 0;
4272	}
4273
4274	list = TALLOC_ARRAY( NULL, struct winbindd_tdc_domain, num_domains );
4275	if ( !list ) {
4276		DEBUG(0,("unpack_tdc_domains: Failed to talloc() domain list!\n"));
4277		return 0;
4278	}
4279
4280	for ( i=0; i<num_domains; i++ ) {
4281		len += tdb_unpack( buf+len, buflen-len, "fffddd",
4282				   domain_name,
4283				   dns_name,
4284				   sid_string,
4285				   &flags,
4286				   &attribs,
4287				   &type );
4288
4289		if ( len == -1 ) {
4290			DEBUG(5,("unpack_tdc_domains: Failed to unpack domain array\n"));
4291			TALLOC_FREE( list );
4292			return 0;
4293		}
4294
4295		DEBUG(11,("unpack_tdc_domains: Unpacking domain %s (%s) "
4296			  "SID %s, flags = 0x%x, attribs = 0x%x, type = 0x%x\n",
4297			  domain_name, dns_name, sid_string,
4298			  flags, attribs, type));
4299
4300		list[i].domain_name = talloc_strdup( list, domain_name );
4301		list[i].dns_name = talloc_strdup( list, dns_name );
4302		if ( !string_to_sid( &(list[i].sid), sid_string ) ) {
4303			DEBUG(10,("unpack_tdc_domains: no SID for domain %s\n",
4304				  domain_name));
4305		}
4306		list[i].trust_flags = flags;
4307		list[i].trust_attribs = attribs;
4308		list[i].trust_type = type;
4309	}
4310
4311	*domains = list;
4312
4313	return num_domains;
4314}
4315
4316/*********************************************************************
4317 ********************************************************************/
4318
4319static bool wcache_tdc_store_list( struct winbindd_tdc_domain *domains, size_t num_domains )
4320{
4321	TDB_DATA key = make_tdc_key( lp_workgroup() );
4322	TDB_DATA data = { NULL, 0 };
4323	int ret;
4324
4325	if ( !key.dptr )
4326		return false;
4327
4328	/* See if we were asked to delete the cache entry */
4329
4330	if ( !domains ) {
4331		ret = tdb_delete( wcache->tdb, key );
4332		goto done;
4333	}
4334
4335	data.dsize = pack_tdc_domains( domains, num_domains, &data.dptr );
4336
4337	if ( !data.dptr ) {
4338		ret = -1;
4339		goto done;
4340	}
4341
4342	ret = tdb_store( wcache->tdb, key, data, 0 );
4343
4344 done:
4345	SAFE_FREE( data.dptr );
4346	SAFE_FREE( key.dptr );
4347
4348	return ( ret != -1 );
4349}
4350
4351/*********************************************************************
4352 ********************************************************************/
4353
4354bool wcache_tdc_fetch_list( struct winbindd_tdc_domain **domains, size_t *num_domains )
4355{
4356	TDB_DATA key = make_tdc_key( lp_workgroup() );
4357	TDB_DATA data = { NULL, 0 };
4358
4359	*domains = NULL;
4360	*num_domains = 0;
4361
4362	if ( !key.dptr )
4363		return false;
4364
4365	data = tdb_fetch( wcache->tdb, key );
4366
4367	SAFE_FREE( key.dptr );
4368
4369	if ( !data.dptr )
4370		return false;
4371
4372	*num_domains = unpack_tdc_domains( data.dptr, data.dsize, domains );
4373
4374	SAFE_FREE( data.dptr );
4375
4376	if ( !*domains )
4377		return false;
4378
4379	return true;
4380}
4381
4382/*********************************************************************
4383 ********************************************************************/
4384
4385bool wcache_tdc_add_domain( struct winbindd_domain *domain )
4386{
4387	struct winbindd_tdc_domain *dom_list = NULL;
4388	size_t num_domains = 0;
4389	bool ret = false;
4390
4391	DEBUG(10,("wcache_tdc_add_domain: Adding domain %s (%s), SID %s, "
4392		  "flags = 0x%x, attributes = 0x%x, type = 0x%x\n",
4393		  domain->name, domain->alt_name,
4394		  sid_string_dbg(&domain->sid),
4395		  domain->domain_flags,
4396		  domain->domain_trust_attribs,
4397		  domain->domain_type));
4398
4399	if ( !init_wcache() ) {
4400		return false;
4401	}
4402
4403	/* fetch the list */
4404
4405	wcache_tdc_fetch_list( &dom_list, &num_domains );
4406
4407	/* add the new domain */
4408
4409	if ( !add_wbdomain_to_tdc_array( domain, &dom_list, &num_domains ) ) {
4410		goto done;
4411	}
4412
4413	/* pack the domain */
4414
4415	if ( !wcache_tdc_store_list( dom_list, num_domains ) ) {
4416		goto done;
4417	}
4418
4419	/* Success */
4420
4421	ret = true;
4422 done:
4423	TALLOC_FREE( dom_list );
4424
4425	return ret;
4426}
4427
4428/*********************************************************************
4429 ********************************************************************/
4430
4431struct winbindd_tdc_domain * wcache_tdc_fetch_domain( TALLOC_CTX *ctx, const char *name )
4432{
4433	struct winbindd_tdc_domain *dom_list = NULL;
4434	size_t num_domains = 0;
4435	int i;
4436	struct winbindd_tdc_domain *d = NULL;
4437
4438	DEBUG(10,("wcache_tdc_fetch_domain: Searching for domain %s\n", name));
4439
4440	if ( !init_wcache() ) {
4441		return false;
4442	}
4443
4444	/* fetch the list */
4445
4446	wcache_tdc_fetch_list( &dom_list, &num_domains );
4447
4448	for ( i=0; i<num_domains; i++ ) {
4449		if ( strequal(name, dom_list[i].domain_name) ||
4450		     strequal(name, dom_list[i].dns_name) )
4451		{
4452			DEBUG(10,("wcache_tdc_fetch_domain: Found domain %s\n",
4453				  name));
4454
4455			d = TALLOC_P( ctx, struct winbindd_tdc_domain );
4456			if ( !d )
4457				break;
4458
4459			d->domain_name = talloc_strdup( d, dom_list[i].domain_name );
4460			d->dns_name = talloc_strdup( d, dom_list[i].dns_name );
4461			sid_copy( &d->sid, &dom_list[i].sid );
4462			d->trust_flags   = dom_list[i].trust_flags;
4463			d->trust_type    = dom_list[i].trust_type;
4464			d->trust_attribs = dom_list[i].trust_attribs;
4465
4466			break;
4467		}
4468	}
4469
4470        TALLOC_FREE( dom_list );
4471
4472	return d;
4473}
4474
4475
4476/*********************************************************************
4477 ********************************************************************/
4478
4479void wcache_tdc_clear( void )
4480{
4481	if ( !init_wcache() )
4482		return;
4483
4484	wcache_tdc_store_list( NULL, 0 );
4485
4486	return;
4487}
4488
4489
4490/*********************************************************************
4491 ********************************************************************/
4492
4493static void wcache_save_user_pwinfo(struct winbindd_domain *domain,
4494				    NTSTATUS status,
4495				    const DOM_SID *user_sid,
4496				    const char *homedir,
4497				    const char *shell,
4498				    const char *gecos,
4499				    uint32 gid)
4500{
4501	struct cache_entry *centry;
4502	fstring tmp;
4503
4504	if ( (centry = centry_start(domain, status)) == NULL )
4505		return;
4506
4507	centry_put_string( centry, homedir );
4508	centry_put_string( centry, shell );
4509	centry_put_string( centry, gecos );
4510	centry_put_uint32( centry, gid );
4511
4512	centry_end(centry, "NSS/PWINFO/%s", sid_to_fstring(tmp, user_sid) );
4513
4514	DEBUG(10,("wcache_save_user_pwinfo: %s\n", sid_string_dbg(user_sid) ));
4515
4516	centry_free(centry);
4517}
4518
4519NTSTATUS nss_get_info_cached( struct winbindd_domain *domain,
4520			      const DOM_SID *user_sid,
4521			      TALLOC_CTX *ctx,
4522			      ADS_STRUCT *ads, LDAPMessage *msg,
4523			      const char **homedir, const char **shell,
4524			      const char **gecos, gid_t *p_gid)
4525{
4526	struct winbind_cache *cache = get_cache(domain);
4527	struct cache_entry *centry = NULL;
4528	NTSTATUS nt_status;
4529	fstring tmp;
4530
4531	if (!cache->tdb)
4532		goto do_query;
4533
4534	centry = wcache_fetch(cache, domain, "NSS/PWINFO/%s",
4535			      sid_to_fstring(tmp, user_sid));
4536
4537	if (!centry)
4538		goto do_query;
4539
4540	*homedir = centry_string( centry, ctx );
4541	*shell   = centry_string( centry, ctx );
4542	*gecos   = centry_string( centry, ctx );
4543	*p_gid   = centry_uint32( centry );
4544
4545	centry_free(centry);
4546
4547	DEBUG(10,("nss_get_info_cached: [Cached] - user_sid %s\n",
4548		  sid_string_dbg(user_sid)));
4549
4550	return NT_STATUS_OK;
4551
4552do_query:
4553
4554	nt_status = nss_get_info( domain->name, user_sid, ctx, ads, msg,
4555				  homedir, shell, gecos, p_gid );
4556
4557	DEBUG(10, ("nss_get_info returned %s\n", nt_errstr(nt_status)));
4558
4559	if ( NT_STATUS_IS_OK(nt_status) ) {
4560		DEBUG(10, ("result:\n\thomedir = '%s'\n", *homedir));
4561                DEBUGADD(10, ("\tshell = '%s'\n", *shell));
4562                DEBUGADD(10, ("\tgecos = '%s'\n", *gecos));
4563                DEBUGADD(10, ("\tgid = '%u'\n", (unsigned int)*p_gid));
4564
4565		wcache_save_user_pwinfo( domain, nt_status, user_sid,
4566					 *homedir, *shell, *gecos, *p_gid );
4567	}
4568
4569	if ( NT_STATUS_EQUAL( nt_status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND ) ) {
4570		DEBUG(5,("nss_get_info_cached: Setting domain %s offline\n",
4571			 domain->name ));
4572		set_domain_offline( domain );
4573	}
4574
4575	return nt_status;
4576}
4577
4578
4579/* the cache backend methods are exposed via this structure */
4580struct winbindd_methods cache_methods = {
4581	true,
4582	query_user_list,
4583	enum_dom_groups,
4584	enum_local_groups,
4585	name_to_sid,
4586	sid_to_name,
4587	rids_to_names,
4588	query_user,
4589	lookup_usergroups,
4590	lookup_useraliases,
4591	lookup_groupmem,
4592	sequence_number,
4593	lockout_policy,
4594	password_policy,
4595	trusted_domains
4596};
4597
4598static bool wcache_ndr_key(TALLOC_CTX *mem_ctx, char *domain_name,
4599			   uint32_t opnum, const DATA_BLOB *req,
4600			   TDB_DATA *pkey)
4601{
4602	char *key;
4603	size_t keylen;
4604
4605	key = talloc_asprintf(mem_ctx, "NDR/%s/%d/", domain_name, (int)opnum);
4606	if (key == NULL) {
4607		return false;
4608	}
4609	keylen = talloc_get_size(key) - 1;
4610
4611	key = talloc_realloc(mem_ctx, key, char, keylen + req->length);
4612	if (key == NULL) {
4613		return false;
4614	}
4615	memcpy(key + keylen, req->data, req->length);
4616
4617	pkey->dptr = (uint8_t *)key;
4618	pkey->dsize = talloc_get_size(key);
4619	return true;
4620}
4621
4622static bool wcache_opnum_cacheable(uint32_t opnum)
4623{
4624	switch (opnum) {
4625	case NDR_WBINT_PING:
4626	case NDR_WBINT_QUERYSEQUENCENUMBER:
4627	case NDR_WBINT_ALLOCATEUID:
4628	case NDR_WBINT_ALLOCATEGID:
4629	case NDR_WBINT_CHECKMACHINEACCOUNT:
4630	case NDR_WBINT_CHANGEMACHINEACCOUNT:
4631	case NDR_WBINT_PINGDC:
4632		return false;
4633	}
4634	return true;
4635}
4636
4637bool wcache_fetch_ndr(TALLOC_CTX *mem_ctx, struct winbindd_domain *domain,
4638		      uint32_t opnum, const DATA_BLOB *req, DATA_BLOB *resp)
4639{
4640	TDB_DATA key, data;
4641	bool ret = false;
4642
4643	if (!wcache_opnum_cacheable(opnum)) {
4644		return false;
4645	}
4646
4647	if (wcache->tdb == NULL) {
4648		return false;
4649	}
4650
4651	if (!wcache_ndr_key(talloc_tos(), domain->name, opnum, req, &key)) {
4652		return false;
4653	}
4654	data = tdb_fetch(wcache->tdb, key);
4655	TALLOC_FREE(key.dptr);
4656
4657	if (data.dptr == NULL) {
4658		return false;
4659	}
4660	if (data.dsize < 4) {
4661		goto fail;
4662	}
4663
4664	if (!is_domain_offline(domain)) {
4665		uint32_t entry_seqnum, dom_seqnum, last_check;
4666
4667		if (!wcache_fetch_seqnum(domain->name, &dom_seqnum,
4668					 &last_check)) {
4669			goto fail;
4670		}
4671		entry_seqnum = IVAL(data.dptr, 0);
4672		if (entry_seqnum != dom_seqnum) {
4673			DEBUG(10, ("Entry has wrong sequence number: %d\n",
4674				   (int)entry_seqnum));
4675			goto fail;
4676		}
4677	}
4678
4679	resp->data = (uint8_t *)talloc_memdup(mem_ctx, data.dptr + 4,
4680					      data.dsize - 4);
4681	if (resp->data == NULL) {
4682		DEBUG(10, ("talloc failed\n"));
4683		goto fail;
4684	}
4685	resp->length = data.dsize - 4;
4686
4687	ret = true;
4688fail:
4689	SAFE_FREE(data.dptr);
4690	return ret;
4691}
4692
4693void wcache_store_ndr(struct winbindd_domain *domain, uint32_t opnum,
4694		      const DATA_BLOB *req, const DATA_BLOB *resp)
4695{
4696	TDB_DATA key, data;
4697	uint32_t dom_seqnum, last_check;
4698
4699	if (!wcache_opnum_cacheable(opnum)) {
4700		return;
4701	}
4702
4703	if (wcache->tdb == NULL) {
4704		return;
4705	}
4706
4707	if (!wcache_fetch_seqnum(domain->name, &dom_seqnum, &last_check)) {
4708		DEBUG(10, ("could not fetch seqnum for domain %s\n",
4709			   domain->name));
4710		return;
4711	}
4712
4713	if (!wcache_ndr_key(talloc_tos(), domain->name, opnum, req, &key)) {
4714		return;
4715	}
4716
4717	data.dsize = resp->length + 4;
4718	data.dptr = talloc_array(key.dptr, uint8_t, data.dsize);
4719	if (data.dptr == NULL) {
4720		goto done;
4721	}
4722
4723	SIVAL(data.dptr, 0, dom_seqnum);
4724	memcpy(data.dptr+4, resp->data, resp->length);
4725
4726	tdb_store(wcache->tdb, key, data, 0);
4727
4728done:
4729	TALLOC_FREE(key.dptr);
4730	return;
4731}
4732