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