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