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