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