r22845: Modified and extended the winbindd cache validation code:
[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                 return (uint32)-1;
228         }
229         ret = IVAL(centry->data, centry->ofs);
230         centry->ofs += 4;
231         return ret;
232 }
233
234 /*
235   pull a uint16 from a cache entry 
236 */
237 static uint16 centry_uint16(struct cache_entry *centry)
238 {
239         uint16 ret;
240         if (!centry_check_bytes(centry, 2)) {
241                 smb_panic_fn("centry_uint16");
242                 return (uint16)-1;
243         }
244         ret = CVAL(centry->data, centry->ofs);
245         centry->ofs += 2;
246         return ret;
247 }
248
249 /*
250   pull a uint8 from a cache entry 
251 */
252 static uint8 centry_uint8(struct cache_entry *centry)
253 {
254         uint8 ret;
255         if (!centry_check_bytes(centry, 1)) {
256                 smb_panic_fn("centry_uint8");
257                 return (uint8)-1;
258         }
259         ret = CVAL(centry->data, centry->ofs);
260         centry->ofs += 1;
261         return ret;
262 }
263
264 /*
265   pull a NTTIME from a cache entry 
266 */
267 static NTTIME centry_nttime(struct cache_entry *centry)
268 {
269         NTTIME ret;
270         if (!centry_check_bytes(centry, 8)) {
271                 smb_panic_fn("centry_nttime");
272                 return (NTTIME)-1;
273         }
274         ret = IVAL(centry->data, centry->ofs);
275         centry->ofs += 4;
276         ret += (uint64_t)IVAL(centry->data, centry->ofs) << 32;
277         centry->ofs += 4;
278         return ret;
279 }
280
281 /*
282   pull a time_t from a cache entry. time_t stored portably as a 64-bit time.
283 */
284 static time_t centry_time(struct cache_entry *centry)
285 {
286         return (time_t)centry_nttime(centry);
287 }
288
289 /* pull a string from a cache entry, using the supplied
290    talloc context 
291 */
292 static char *centry_string(struct cache_entry *centry, TALLOC_CTX *mem_ctx)
293 {
294         uint32 len;
295         char *ret;
296
297         len = centry_uint8(centry);
298
299         if (len == 0xFF) {
300                 /* a deliberate NULL string */
301                 return NULL;
302         }
303
304         if (!centry_check_bytes(centry, (size_t)len)) {
305                 smb_panic_fn("centry_string");
306                 return NULL;
307         }
308
309         ret = TALLOC_ARRAY(mem_ctx, char, len+1);
310         if (!ret) {
311                 smb_panic_fn("centry_string out of memory\n");
312                 return NULL;
313         }
314         memcpy(ret,centry->data + centry->ofs, len);
315         ret[len] = 0;
316         centry->ofs += len;
317         return ret;
318 }
319
320 /* pull a hash16 from a cache entry, using the supplied
321    talloc context 
322 */
323 static char *centry_hash16(struct cache_entry *centry, TALLOC_CTX *mem_ctx)
324 {
325         uint32 len;
326         char *ret;
327
328         len = centry_uint8(centry);
329
330         if (len != 16) {
331                 DEBUG(0,("centry corruption? hash len (%u) != 16\n", 
332                         len ));
333                 return NULL;
334         }
335
336         if (!centry_check_bytes(centry, 16)) {
337                 return NULL;
338         }
339
340         ret = TALLOC_ARRAY(mem_ctx, char, 16);
341         if (!ret) {
342                 smb_panic_fn("centry_hash out of memory\n");
343                 return NULL;
344         }
345         memcpy(ret,centry->data + centry->ofs, 16);
346         centry->ofs += 16;
347         return ret;
348 }
349
350 /* pull a sid from a cache entry, using the supplied
351    talloc context 
352 */
353 static BOOL centry_sid(struct cache_entry *centry, TALLOC_CTX *mem_ctx, DOM_SID *sid)
354 {
355         char *sid_string;
356         sid_string = centry_string(centry, mem_ctx);
357         if ((sid_string == NULL) || (!string_to_sid(sid, sid_string))) {
358                 return False;
359         }
360         return True;
361 }
362
363 /* the server is considered down if it can't give us a sequence number */
364 static BOOL wcache_server_down(struct winbindd_domain *domain)
365 {
366         BOOL ret;
367
368         if (!wcache->tdb)
369                 return False;
370
371         ret = (domain->sequence_number == DOM_SEQUENCE_NONE);
372
373         if (ret)
374                 DEBUG(10,("wcache_server_down: server for Domain %s down\n", 
375                         domain->name ));
376         return ret;
377 }
378
379 static NTSTATUS fetch_cache_seqnum( struct winbindd_domain *domain, time_t now )
380 {
381         TDB_DATA data;
382         fstring key;
383         uint32 time_diff;
384         
385         if (!wcache->tdb) {
386                 DEBUG(10,("fetch_cache_seqnum: tdb == NULL\n"));
387                 return NT_STATUS_UNSUCCESSFUL;
388         }
389                 
390         fstr_sprintf( key, "SEQNUM/%s", domain->name );
391         
392         data = tdb_fetch_bystring( wcache->tdb, key );
393         if ( !data.dptr || data.dsize!=8 ) {
394                 DEBUG(10,("fetch_cache_seqnum: invalid data size key [%s]\n", key ));
395                 return NT_STATUS_UNSUCCESSFUL;
396         }
397         
398         domain->sequence_number = IVAL(data.dptr, 0);
399         domain->last_seq_check  = IVAL(data.dptr, 4);
400         
401         SAFE_FREE(data.dptr);
402
403         /* have we expired? */
404         
405         time_diff = now - domain->last_seq_check;
406         if ( time_diff > lp_winbind_cache_time() ) {
407                 DEBUG(10,("fetch_cache_seqnum: timeout [%s][%u @ %u]\n",
408                         domain->name, domain->sequence_number,
409                         (uint32)domain->last_seq_check));
410                 return NT_STATUS_UNSUCCESSFUL;
411         }
412
413         DEBUG(10,("fetch_cache_seqnum: success [%s][%u @ %u]\n", 
414                 domain->name, domain->sequence_number, 
415                 (uint32)domain->last_seq_check));
416
417         return NT_STATUS_OK;
418 }
419
420 static NTSTATUS store_cache_seqnum( struct winbindd_domain *domain )
421 {
422         TDB_DATA data;
423         fstring key_str;
424         uint8 buf[8];
425         
426         if (!wcache->tdb) {
427                 DEBUG(10,("store_cache_seqnum: tdb == NULL\n"));
428                 return NT_STATUS_UNSUCCESSFUL;
429         }
430                 
431         fstr_sprintf( key_str, "SEQNUM/%s", domain->name );
432         
433         SIVAL(buf, 0, domain->sequence_number);
434         SIVAL(buf, 4, domain->last_seq_check);
435         data.dptr = buf;
436         data.dsize = 8;
437         
438         if ( tdb_store_bystring( wcache->tdb, key_str, data, TDB_REPLACE) == -1 ) {
439                 DEBUG(10,("store_cache_seqnum: tdb_store fail key [%s]\n", key_str ));
440                 return NT_STATUS_UNSUCCESSFUL;
441         }
442
443         DEBUG(10,("store_cache_seqnum: success [%s][%u @ %u]\n", 
444                 domain->name, domain->sequence_number, 
445                 (uint32)domain->last_seq_check));
446         
447         return NT_STATUS_OK;
448 }
449
450 /*
451   refresh the domain sequence number. If force is True
452   then always refresh it, no matter how recently we fetched it
453 */
454
455 static void refresh_sequence_number(struct winbindd_domain *domain, BOOL force)
456 {
457         NTSTATUS status;
458         unsigned time_diff;
459         time_t t = time(NULL);
460         unsigned cache_time = lp_winbind_cache_time();
461
462         if ( IS_DOMAIN_OFFLINE(domain) ) {
463                 return;
464         }
465         
466         get_cache( domain );
467
468 #if 0   /* JERRY -- disable as the default cache time is now 5 minutes */
469         /* trying to reconnect is expensive, don't do it too often */
470         if (domain->sequence_number == DOM_SEQUENCE_NONE) {
471                 cache_time *= 8;
472         }
473 #endif
474
475         time_diff = t - domain->last_seq_check;
476
477         /* see if we have to refetch the domain sequence number */
478         if (!force && (time_diff < cache_time)) {
479                 DEBUG(10, ("refresh_sequence_number: %s time ok\n", domain->name));
480                 goto done;
481         }
482         
483         /* try to get the sequence number from the tdb cache first */
484         /* this will update the timestamp as well */
485         
486         status = fetch_cache_seqnum( domain, t );
487         if ( NT_STATUS_IS_OK(status) )
488                 goto done;      
489
490         /* important! make sure that we know if this is a native 
491            mode domain or not.  And that we can contact it. */
492
493         if ( winbindd_can_contact_domain( domain ) ) {          
494                 status = domain->backend->sequence_number(domain, 
495                                                           &domain->sequence_number);
496         } else {
497                 /* just use the current time */
498                 status = NT_STATUS_OK;
499                 domain->sequence_number = time(NULL);
500         }
501
502
503         /* the above call could have set our domain->backend to NULL when
504          * coming from offline to online mode, make sure to reinitialize the
505          * backend - Guenther */
506         get_cache( domain );
507
508         if (!NT_STATUS_IS_OK(status)) {
509                 DEBUG(10,("refresh_sequence_number: failed with %s\n", nt_errstr(status)));
510                 domain->sequence_number = DOM_SEQUENCE_NONE;
511         }
512         
513         domain->last_status = status;
514         domain->last_seq_check = time(NULL);
515         
516         /* save the new sequence number ni the cache */
517         store_cache_seqnum( domain );
518
519 done:
520         DEBUG(10, ("refresh_sequence_number: %s seq number is now %d\n", 
521                    domain->name, domain->sequence_number));
522
523         return;
524 }
525
526 /*
527   decide if a cache entry has expired
528 */
529 static BOOL centry_expired(struct winbindd_domain *domain, const char *keystr, struct cache_entry *centry)
530 {
531         /* If we've been told to be offline - stay in that state... */
532         if (lp_winbind_offline_logon() && global_winbindd_offline_state) {
533                 DEBUG(10,("centry_expired: Key %s for domain %s valid as winbindd is globally offline.\n",
534                         keystr, domain->name ));
535                 return False;
536         }
537
538         /* when the domain is offline return the cached entry.
539          * This deals with transient offline states... */
540
541         if (!domain->online) {
542                 DEBUG(10,("centry_expired: Key %s for domain %s valid as domain is offline.\n",
543                         keystr, domain->name ));
544                 return False;
545         }
546
547         /* if the server is OK and our cache entry came from when it was down then
548            the entry is invalid */
549         if ((domain->sequence_number != DOM_SEQUENCE_NONE) &&  
550             (centry->sequence_number == DOM_SEQUENCE_NONE)) {
551                 DEBUG(10,("centry_expired: Key %s for domain %s invalid sequence.\n",
552                         keystr, domain->name ));
553                 return True;
554         }
555
556         /* if the server is down or the cache entry is not older than the
557            current sequence number then it is OK */
558         if (wcache_server_down(domain) || 
559             centry->sequence_number == domain->sequence_number) {
560                 DEBUG(10,("centry_expired: Key %s for domain %s is good.\n",
561                         keystr, domain->name ));
562                 return False;
563         }
564
565         DEBUG(10,("centry_expired: Key %s for domain %s expired\n",
566                 keystr, domain->name ));
567
568         /* it's expired */
569         return True;
570 }
571
572 static struct cache_entry *wcache_fetch_raw(char *kstr)
573 {
574         TDB_DATA data;
575         struct cache_entry *centry;
576         TDB_DATA key;
577
578         key = string_tdb_data(kstr);
579         data = tdb_fetch(wcache->tdb, key);
580         if (!data.dptr) {
581                 /* a cache miss */
582                 return NULL;
583         }
584
585         centry = SMB_XMALLOC_P(struct cache_entry);
586         centry->data = (unsigned char *)data.dptr;
587         centry->len = data.dsize;
588         centry->ofs = 0;
589
590         if (centry->len < 8) {
591                 /* huh? corrupt cache? */
592                 DEBUG(10,("wcache_fetch_raw: Corrupt cache for key %s (len < 8) ?\n", kstr));
593                 centry_free(centry);
594                 return NULL;
595         }
596         
597         centry->status = NT_STATUS(centry_uint32(centry));
598         centry->sequence_number = centry_uint32(centry);
599
600         return centry;
601 }
602
603 /*
604   fetch an entry from the cache, with a varargs key. auto-fetch the sequence
605   number and return status
606 */
607 static struct cache_entry *wcache_fetch(struct winbind_cache *cache, 
608                                         struct winbindd_domain *domain,
609                                         const char *format, ...) PRINTF_ATTRIBUTE(3,4);
610 static struct cache_entry *wcache_fetch(struct winbind_cache *cache, 
611                                         struct winbindd_domain *domain,
612                                         const char *format, ...)
613 {
614         va_list ap;
615         char *kstr;
616         struct cache_entry *centry;
617
618         if (opt_nocache) {
619                 return NULL;
620         }
621
622         refresh_sequence_number(domain, False);
623
624         va_start(ap, format);
625         smb_xvasprintf(&kstr, format, ap);
626         va_end(ap);
627
628         centry = wcache_fetch_raw(kstr);
629         if (centry == NULL) {
630                 free(kstr);
631                 return NULL;
632         }
633
634         if (centry_expired(domain, kstr, centry)) {
635
636                 DEBUG(10,("wcache_fetch: entry %s expired for domain %s\n",
637                          kstr, domain->name ));
638
639                 centry_free(centry);
640                 free(kstr);
641                 return NULL;
642         }
643
644         DEBUG(10,("wcache_fetch: returning entry %s for domain %s\n",
645                  kstr, domain->name ));
646
647         free(kstr);
648         return centry;
649 }
650
651 static void wcache_delete(const char *format, ...) PRINTF_ATTRIBUTE(1,2);
652 static void wcache_delete(const char *format, ...)
653 {
654         va_list ap;
655         char *kstr;
656         TDB_DATA key;
657
658         va_start(ap, format);
659         smb_xvasprintf(&kstr, format, ap);
660         va_end(ap);
661
662         key = string_tdb_data(kstr);
663
664         tdb_delete(wcache->tdb, key);
665         free(kstr);
666 }
667
668 /*
669   make sure we have at least len bytes available in a centry 
670 */
671 static void centry_expand(struct cache_entry *centry, uint32 len)
672 {
673         if (centry->len - centry->ofs >= len)
674                 return;
675         centry->len *= 2;
676         centry->data = SMB_REALLOC_ARRAY(centry->data, unsigned char,
677                                          centry->len);
678         if (!centry->data) {
679                 DEBUG(0,("out of memory: needed %d bytes in centry_expand\n", centry->len));
680                 smb_panic_fn("out of memory in centry_expand");
681         }
682 }
683
684 /*
685   push a uint32 into a centry 
686 */
687 static void centry_put_uint32(struct cache_entry *centry, uint32 v)
688 {
689         centry_expand(centry, 4);
690         SIVAL(centry->data, centry->ofs, v);
691         centry->ofs += 4;
692 }
693
694 /*
695   push a uint16 into a centry 
696 */
697 static void centry_put_uint16(struct cache_entry *centry, uint16 v)
698 {
699         centry_expand(centry, 2);
700         SIVAL(centry->data, centry->ofs, v);
701         centry->ofs += 2;
702 }
703
704 /*
705   push a uint8 into a centry 
706 */
707 static void centry_put_uint8(struct cache_entry *centry, uint8 v)
708 {
709         centry_expand(centry, 1);
710         SCVAL(centry->data, centry->ofs, v);
711         centry->ofs += 1;
712 }
713
714 /* 
715    push a string into a centry 
716  */
717 static void centry_put_string(struct cache_entry *centry, const char *s)
718 {
719         int len;
720
721         if (!s) {
722                 /* null strings are marked as len 0xFFFF */
723                 centry_put_uint8(centry, 0xFF);
724                 return;
725         }
726
727         len = strlen(s);
728         /* can't handle more than 254 char strings. Truncating is probably best */
729         if (len > 254) {
730                 DEBUG(10,("centry_put_string: truncating len (%d) to: 254\n", len));
731                 len = 254;
732         }
733         centry_put_uint8(centry, len);
734         centry_expand(centry, len);
735         memcpy(centry->data + centry->ofs, s, len);
736         centry->ofs += len;
737 }
738
739 /* 
740    push a 16 byte hash into a centry - treat as 16 byte string.
741  */
742 static void centry_put_hash16(struct cache_entry *centry, const uint8 val[16])
743 {
744         centry_put_uint8(centry, 16);
745         centry_expand(centry, 16);
746         memcpy(centry->data + centry->ofs, val, 16);
747         centry->ofs += 16;
748 }
749
750 static void centry_put_sid(struct cache_entry *centry, const DOM_SID *sid) 
751 {
752         fstring sid_string;
753         centry_put_string(centry, sid_to_string(sid_string, sid));
754 }
755
756 /*
757   push a NTTIME into a centry 
758 */
759 static void centry_put_nttime(struct cache_entry *centry, NTTIME nt)
760 {
761         centry_expand(centry, 8);
762         SIVAL(centry->data, centry->ofs, nt & 0xFFFFFFFF);
763         centry->ofs += 4;
764         SIVAL(centry->data, centry->ofs, nt >> 32);
765         centry->ofs += 4;
766 }
767
768 /*
769   push a time_t into a centry - use a 64 bit size.
770   NTTIME here is being used as a convenient 64-bit size.
771 */
772 static void centry_put_time(struct cache_entry *centry, time_t t)
773 {
774         NTTIME nt = (NTTIME)t;
775         centry_put_nttime(centry, nt);
776 }
777
778 /*
779   start a centry for output. When finished, call centry_end()
780 */
781 struct cache_entry *centry_start(struct winbindd_domain *domain, NTSTATUS status)
782 {
783         struct cache_entry *centry;
784
785         if (!wcache->tdb)
786                 return NULL;
787
788         centry = SMB_XMALLOC_P(struct cache_entry);
789
790         centry->len = 8192; /* reasonable default */
791         centry->data = SMB_XMALLOC_ARRAY(uint8, centry->len);
792         centry->ofs = 0;
793         centry->sequence_number = domain->sequence_number;
794         centry_put_uint32(centry, NT_STATUS_V(status));
795         centry_put_uint32(centry, centry->sequence_number);
796         return centry;
797 }
798
799 /*
800   finish a centry and write it to the tdb
801 */
802 static void centry_end(struct cache_entry *centry, const char *format, ...) PRINTF_ATTRIBUTE(2,3);
803 static void centry_end(struct cache_entry *centry, const char *format, ...)
804 {
805         va_list ap;
806         char *kstr;
807         TDB_DATA key, data;
808
809         va_start(ap, format);
810         smb_xvasprintf(&kstr, format, ap);
811         va_end(ap);
812
813         key = string_tdb_data(kstr);
814         data.dptr = centry->data;
815         data.dsize = centry->ofs;
816
817         tdb_store(wcache->tdb, key, data, TDB_REPLACE);
818         free(kstr);
819 }
820
821 static void wcache_save_name_to_sid(struct winbindd_domain *domain, 
822                                     NTSTATUS status, const char *domain_name,
823                                     const char *name, const DOM_SID *sid, 
824                                     enum lsa_SidType type)
825 {
826         struct cache_entry *centry;
827         fstring uname;
828
829         centry = centry_start(domain, status);
830         if (!centry)
831                 return;
832         centry_put_uint32(centry, type);
833         centry_put_sid(centry, sid);
834         fstrcpy(uname, name);
835         strupper_m(uname);
836         centry_end(centry, "NS/%s/%s", domain_name, uname);
837         DEBUG(10,("wcache_save_name_to_sid: %s\\%s -> %s (%s)\n", domain_name, uname,
838                   sid_string_static(sid), nt_errstr(status)));
839         centry_free(centry);
840 }
841
842 static void wcache_save_sid_to_name(struct winbindd_domain *domain, NTSTATUS status, 
843                                     const DOM_SID *sid, const char *domain_name, const char *name, enum lsa_SidType type)
844 {
845         struct cache_entry *centry;
846         fstring sid_string;
847
848         if (is_null_sid(sid)) {
849                 return;
850         }
851
852         centry = centry_start(domain, status);
853         if (!centry)
854                 return;
855         if (NT_STATUS_IS_OK(status)) {
856                 centry_put_uint32(centry, type);
857                 centry_put_string(centry, domain_name);
858                 centry_put_string(centry, name);
859         }
860         centry_end(centry, "SN/%s", sid_to_string(sid_string, sid));
861         DEBUG(10,("wcache_save_sid_to_name: %s -> %s (%s)\n", sid_string, 
862                   name, nt_errstr(status)));
863         centry_free(centry);
864 }
865
866
867 static void wcache_save_user(struct winbindd_domain *domain, NTSTATUS status, WINBIND_USERINFO *info)
868 {
869         struct cache_entry *centry;
870         fstring sid_string;
871
872         if (is_null_sid(&info->user_sid)) {
873                 return;
874         }
875
876         centry = centry_start(domain, status);
877         if (!centry)
878                 return;
879         centry_put_string(centry, info->acct_name);
880         centry_put_string(centry, info->full_name);
881         centry_put_string(centry, info->homedir);
882         centry_put_string(centry, info->shell);
883         centry_put_uint32(centry, info->primary_gid);
884         centry_put_sid(centry, &info->user_sid);
885         centry_put_sid(centry, &info->group_sid);
886         centry_end(centry, "U/%s", sid_to_string(sid_string, &info->user_sid));
887         DEBUG(10,("wcache_save_user: %s (acct_name %s)\n", sid_string, info->acct_name));
888         centry_free(centry);
889 }
890
891 static void wcache_save_lockout_policy(struct winbindd_domain *domain, NTSTATUS status, SAM_UNK_INFO_12 *lockout_policy)
892 {
893         struct cache_entry *centry;
894
895         centry = centry_start(domain, status);
896         if (!centry)
897                 return;
898
899         centry_put_nttime(centry, lockout_policy->duration);
900         centry_put_nttime(centry, lockout_policy->reset_count);
901         centry_put_uint16(centry, lockout_policy->bad_attempt_lockout);
902
903         centry_end(centry, "LOC_POL/%s", domain->name);
904         
905         DEBUG(10,("wcache_save_lockout_policy: %s\n", domain->name));
906
907         centry_free(centry);
908 }
909
910 static void wcache_save_password_policy(struct winbindd_domain *domain, NTSTATUS status, SAM_UNK_INFO_1 *policy)
911  {
912         struct cache_entry *centry;
913
914         centry = centry_start(domain, status);
915         if (!centry)
916                 return;
917
918         centry_put_uint16(centry, policy->min_length_password);
919         centry_put_uint16(centry, policy->password_history);
920         centry_put_uint32(centry, policy->password_properties);
921         centry_put_nttime(centry, policy->expire);
922         centry_put_nttime(centry, policy->min_passwordage);
923
924         centry_end(centry, "PWD_POL/%s", domain->name);
925         
926         DEBUG(10,("wcache_save_password_policy: %s\n", domain->name));
927
928         centry_free(centry);
929 }
930
931 NTSTATUS wcache_cached_creds_exist(struct winbindd_domain *domain, const DOM_SID *sid)
932 {
933         struct winbind_cache *cache = get_cache(domain);
934         TDB_DATA data;
935         fstring key_str;
936         uint32 rid;
937
938         if (!cache->tdb) {
939                 return NT_STATUS_INTERNAL_DB_ERROR;
940         }
941
942         if (is_null_sid(sid)) {
943                 return NT_STATUS_INVALID_SID;
944         }
945
946         if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
947                 return NT_STATUS_INVALID_SID;
948         }
949
950         fstr_sprintf(key_str, "CRED/%s", sid_string_static(sid));
951
952         data = tdb_fetch(cache->tdb, string_tdb_data(key_str));
953         if (!data.dptr) {
954                 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
955         }
956
957         SAFE_FREE(data.dptr);
958         return NT_STATUS_OK;
959 }
960
961 /* Lookup creds for a SID - copes with old (unsalted) creds as well
962    as new salted ones. */
963
964 NTSTATUS wcache_get_creds(struct winbindd_domain *domain, 
965                           TALLOC_CTX *mem_ctx, 
966                           const DOM_SID *sid,
967                           const uint8 **cached_nt_pass,
968                           const uint8 **cached_salt)
969 {
970         struct winbind_cache *cache = get_cache(domain);
971         struct cache_entry *centry = NULL;
972         NTSTATUS status;
973         time_t t;
974         uint32 rid;
975
976         if (!cache->tdb) {
977                 return NT_STATUS_INTERNAL_DB_ERROR;
978         }
979
980         if (is_null_sid(sid)) {
981                 return NT_STATUS_INVALID_SID;
982         }
983
984         if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
985                 return NT_STATUS_INVALID_SID;
986         }
987
988         /* Try and get a salted cred first. If we can't
989            fall back to an unsalted cred. */
990
991         centry = wcache_fetch(cache, domain, "CRED/%s", sid_string_static(sid));
992         if (!centry) {
993                 DEBUG(10,("wcache_get_creds: entry for [CRED/%s] not found\n", 
994                                 sid_string_static(sid)));
995                 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
996         }
997
998         t = centry_time(centry);
999
1000         /* In the salted case this isn't actually the nt_hash itself,
1001            but the MD5 of the salt + nt_hash. Let the caller
1002            sort this out. It can tell as we only return the cached_salt
1003            if we are returning a salted cred. */
1004
1005         *cached_nt_pass = (const uint8 *)centry_hash16(centry, mem_ctx);
1006         if (*cached_nt_pass == NULL) {
1007                 const char *sidstr = sid_string_static(sid);
1008
1009                 /* Bad (old) cred cache. Delete and pretend we
1010                    don't have it. */
1011                 DEBUG(0,("wcache_get_creds: bad entry for [CRED/%s] - deleting\n", 
1012                                 sidstr));
1013                 wcache_delete("CRED/%s", sidstr);
1014                 centry_free(centry);
1015                 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1016         }
1017
1018         /* We only have 17 bytes more data in the salted cred case. */
1019         if (centry->len - centry->ofs == 17) {
1020                 *cached_salt = (const uint8 *)centry_hash16(centry, mem_ctx);
1021         } else {
1022                 *cached_salt = NULL;
1023         }
1024
1025 #if DEBUG_PASSWORD
1026         dump_data(100, *cached_nt_pass, NT_HASH_LEN);
1027         if (*cached_salt) {
1028                 dump_data(100, *cached_salt, NT_HASH_LEN);
1029         }
1030 #endif
1031         status = centry->status;
1032
1033         DEBUG(10,("wcache_get_creds: [Cached] - cached creds for user %s status: %s\n",
1034                 sid_string_static(sid), nt_errstr(status) ));
1035
1036         centry_free(centry);
1037         return status;
1038 }
1039
1040 /* Store creds for a SID - only writes out new salted ones. */
1041
1042 NTSTATUS wcache_save_creds(struct winbindd_domain *domain, 
1043                            TALLOC_CTX *mem_ctx, 
1044                            const DOM_SID *sid, 
1045                            const uint8 nt_pass[NT_HASH_LEN])
1046 {
1047         struct cache_entry *centry;
1048         fstring sid_string;
1049         uint32 rid;
1050         uint8 cred_salt[NT_HASH_LEN];
1051         uint8 salted_hash[NT_HASH_LEN];
1052
1053         if (is_null_sid(sid)) {
1054                 return NT_STATUS_INVALID_SID;
1055         }
1056
1057         if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
1058                 return NT_STATUS_INVALID_SID;
1059         }
1060
1061         centry = centry_start(domain, NT_STATUS_OK);
1062         if (!centry) {
1063                 return NT_STATUS_INTERNAL_DB_ERROR;
1064         }
1065
1066 #if DEBUG_PASSWORD
1067         dump_data(100, nt_pass, NT_HASH_LEN);
1068 #endif
1069
1070         centry_put_time(centry, time(NULL));
1071
1072         /* Create a salt and then salt the hash. */
1073         generate_random_buffer(cred_salt, NT_HASH_LEN);
1074         E_md5hash(cred_salt, nt_pass, salted_hash);
1075
1076         centry_put_hash16(centry, salted_hash);
1077         centry_put_hash16(centry, cred_salt);
1078         centry_end(centry, "CRED/%s", sid_to_string(sid_string, sid));
1079
1080         DEBUG(10,("wcache_save_creds: %s\n", sid_string));
1081
1082         centry_free(centry);
1083
1084         return NT_STATUS_OK;
1085 }
1086
1087
1088 /* Query display info. This is the basic user list fn */
1089 static NTSTATUS query_user_list(struct winbindd_domain *domain,
1090                                 TALLOC_CTX *mem_ctx,
1091                                 uint32 *num_entries, 
1092                                 WINBIND_USERINFO **info)
1093 {
1094         struct winbind_cache *cache = get_cache(domain);
1095         struct cache_entry *centry = NULL;
1096         NTSTATUS status;
1097         unsigned int i, retry;
1098
1099         if (!cache->tdb)
1100                 goto do_query;
1101
1102         centry = wcache_fetch(cache, domain, "UL/%s", domain->name);
1103         if (!centry)
1104                 goto do_query;
1105
1106         *num_entries = centry_uint32(centry);
1107         
1108         if (*num_entries == 0)
1109                 goto do_cached;
1110
1111         (*info) = TALLOC_ARRAY(mem_ctx, WINBIND_USERINFO, *num_entries);
1112         if (! (*info)) {
1113                 smb_panic_fn("query_user_list out of memory");
1114                 centry_free(centry);
1115                 return NT_STATUS_NO_MEMORY;
1116         }
1117         for (i=0; i<(*num_entries); i++) {
1118                 (*info)[i].acct_name = centry_string(centry, mem_ctx);
1119                 (*info)[i].full_name = centry_string(centry, mem_ctx);
1120                 (*info)[i].homedir = centry_string(centry, mem_ctx);
1121                 (*info)[i].shell = centry_string(centry, mem_ctx);
1122                 centry_sid(centry, mem_ctx, &(*info)[i].user_sid);
1123                 centry_sid(centry, mem_ctx, &(*info)[i].group_sid);
1124         }
1125
1126 do_cached:      
1127         status = centry->status;
1128
1129         DEBUG(10,("query_user_list: [Cached] - cached list for domain %s status: %s\n",
1130                 domain->name, nt_errstr(status) ));
1131
1132         centry_free(centry);
1133         return status;
1134
1135 do_query:
1136         *num_entries = 0;
1137         *info = NULL;
1138
1139         /* Return status value returned by seq number check */
1140
1141         if (!NT_STATUS_IS_OK(domain->last_status))
1142                 return domain->last_status;
1143
1144         /* Put the query_user_list() in a retry loop.  There appears to be
1145          * some bug either with Windows 2000 or Samba's handling of large
1146          * rpc replies.  This manifests itself as sudden disconnection
1147          * at a random point in the enumeration of a large (60k) user list.
1148          * The retry loop simply tries the operation again. )-:  It's not
1149          * pretty but an acceptable workaround until we work out what the
1150          * real problem is. */
1151
1152         retry = 0;
1153         do {
1154
1155                 DEBUG(10,("query_user_list: [Cached] - doing backend query for list for domain %s\n",
1156                         domain->name ));
1157
1158                 status = domain->backend->query_user_list(domain, mem_ctx, num_entries, info);
1159                 if (!NT_STATUS_IS_OK(status))
1160                         DEBUG(3, ("query_user_list: returned 0x%08x, "
1161                                   "retrying\n", NT_STATUS_V(status)));
1162                         if (NT_STATUS_EQUAL(status, NT_STATUS_UNSUCCESSFUL)) {
1163                                 DEBUG(3, ("query_user_list: flushing "
1164                                           "connection cache\n"));
1165                                 invalidate_cm_connection(&domain->conn);
1166                         }
1167
1168         } while (NT_STATUS_V(status) == NT_STATUS_V(NT_STATUS_UNSUCCESSFUL) && 
1169                  (retry++ < 5));
1170
1171         /* and save it */
1172         refresh_sequence_number(domain, False);
1173         centry = centry_start(domain, status);
1174         if (!centry)
1175                 goto skip_save;
1176         centry_put_uint32(centry, *num_entries);
1177         for (i=0; i<(*num_entries); i++) {
1178                 centry_put_string(centry, (*info)[i].acct_name);
1179                 centry_put_string(centry, (*info)[i].full_name);
1180                 centry_put_string(centry, (*info)[i].homedir);
1181                 centry_put_string(centry, (*info)[i].shell);
1182                 centry_put_sid(centry, &(*info)[i].user_sid);
1183                 centry_put_sid(centry, &(*info)[i].group_sid);
1184                 if (domain->backend && domain->backend->consistent) {
1185                         /* when the backend is consistent we can pre-prime some mappings */
1186                         wcache_save_name_to_sid(domain, NT_STATUS_OK, 
1187                                                 domain->name,
1188                                                 (*info)[i].acct_name, 
1189                                                 &(*info)[i].user_sid,
1190                                                 SID_NAME_USER);
1191                         wcache_save_sid_to_name(domain, NT_STATUS_OK, 
1192                                                 &(*info)[i].user_sid,
1193                                                 domain->name,
1194                                                 (*info)[i].acct_name, 
1195                                                 SID_NAME_USER);
1196                         wcache_save_user(domain, NT_STATUS_OK, &(*info)[i]);
1197                 }
1198         }       
1199         centry_end(centry, "UL/%s", domain->name);
1200         centry_free(centry);
1201
1202 skip_save:
1203         return status;
1204 }
1205
1206 /* list all domain groups */
1207 static NTSTATUS enum_dom_groups(struct winbindd_domain *domain,
1208                                 TALLOC_CTX *mem_ctx,
1209                                 uint32 *num_entries, 
1210                                 struct acct_info **info)
1211 {
1212         struct winbind_cache *cache = get_cache(domain);
1213         struct cache_entry *centry = NULL;
1214         NTSTATUS status;
1215         unsigned int i;
1216
1217         if (!cache->tdb)
1218                 goto do_query;
1219
1220         centry = wcache_fetch(cache, domain, "GL/%s/domain", domain->name);
1221         if (!centry)
1222                 goto do_query;
1223
1224         *num_entries = centry_uint32(centry);
1225         
1226         if (*num_entries == 0)
1227                 goto do_cached;
1228
1229         (*info) = TALLOC_ARRAY(mem_ctx, struct acct_info, *num_entries);
1230         if (! (*info)) {
1231                 smb_panic_fn("enum_dom_groups out of memory");
1232                 centry_free(centry);
1233                 return NT_STATUS_NO_MEMORY;
1234         }
1235         for (i=0; i<(*num_entries); i++) {
1236                 fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx));
1237                 fstrcpy((*info)[i].acct_desc, centry_string(centry, mem_ctx));
1238                 (*info)[i].rid = centry_uint32(centry);
1239         }
1240
1241 do_cached:      
1242         status = centry->status;
1243
1244         DEBUG(10,("enum_dom_groups: [Cached] - cached list for domain %s status: %s\n",
1245                 domain->name, nt_errstr(status) ));
1246
1247         centry_free(centry);
1248         return status;
1249
1250 do_query:
1251         *num_entries = 0;
1252         *info = NULL;
1253
1254         /* Return status value returned by seq number check */
1255
1256         if (!NT_STATUS_IS_OK(domain->last_status))
1257                 return domain->last_status;
1258
1259         DEBUG(10,("enum_dom_groups: [Cached] - doing backend query for list for domain %s\n",
1260                 domain->name ));
1261
1262         status = domain->backend->enum_dom_groups(domain, mem_ctx, num_entries, info);
1263
1264         /* and save it */
1265         refresh_sequence_number(domain, False);
1266         centry = centry_start(domain, status);
1267         if (!centry)
1268                 goto skip_save;
1269         centry_put_uint32(centry, *num_entries);
1270         for (i=0; i<(*num_entries); i++) {
1271                 centry_put_string(centry, (*info)[i].acct_name);
1272                 centry_put_string(centry, (*info)[i].acct_desc);
1273                 centry_put_uint32(centry, (*info)[i].rid);
1274         }       
1275         centry_end(centry, "GL/%s/domain", domain->name);
1276         centry_free(centry);
1277
1278 skip_save:
1279         return status;
1280 }
1281
1282 /* list all domain groups */
1283 static NTSTATUS enum_local_groups(struct winbindd_domain *domain,
1284                                 TALLOC_CTX *mem_ctx,
1285                                 uint32 *num_entries, 
1286                                 struct acct_info **info)
1287 {
1288         struct winbind_cache *cache = get_cache(domain);
1289         struct cache_entry *centry = NULL;
1290         NTSTATUS status;
1291         unsigned int i;
1292
1293         if (!cache->tdb)
1294                 goto do_query;
1295
1296         centry = wcache_fetch(cache, domain, "GL/%s/local", domain->name);
1297         if (!centry)
1298                 goto do_query;
1299
1300         *num_entries = centry_uint32(centry);
1301         
1302         if (*num_entries == 0)
1303                 goto do_cached;
1304
1305         (*info) = TALLOC_ARRAY(mem_ctx, struct acct_info, *num_entries);
1306         if (! (*info)) {
1307                 smb_panic_fn("enum_dom_groups out of memory");
1308                 centry_free(centry);
1309                 return NT_STATUS_NO_MEMORY;
1310         }
1311         for (i=0; i<(*num_entries); i++) {
1312                 fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx));
1313                 fstrcpy((*info)[i].acct_desc, centry_string(centry, mem_ctx));
1314                 (*info)[i].rid = centry_uint32(centry);
1315         }
1316
1317 do_cached:      
1318
1319         /* If we are returning cached data and the domain controller
1320            is down then we don't know whether the data is up to date
1321            or not.  Return NT_STATUS_MORE_PROCESSING_REQUIRED to
1322            indicate this. */
1323
1324         if (wcache_server_down(domain)) {
1325                 DEBUG(10, ("enum_local_groups: returning cached user list and server was down\n"));
1326                 status = NT_STATUS_MORE_PROCESSING_REQUIRED;
1327         } else
1328                 status = centry->status;
1329
1330         DEBUG(10,("enum_local_groups: [Cached] - cached list for domain %s status: %s\n",
1331                 domain->name, nt_errstr(status) ));
1332
1333         centry_free(centry);
1334         return status;
1335
1336 do_query:
1337         *num_entries = 0;
1338         *info = NULL;
1339
1340         /* Return status value returned by seq number check */
1341
1342         if (!NT_STATUS_IS_OK(domain->last_status))
1343                 return domain->last_status;
1344
1345         DEBUG(10,("enum_local_groups: [Cached] - doing backend query for list for domain %s\n",
1346                 domain->name ));
1347
1348         status = domain->backend->enum_local_groups(domain, mem_ctx, num_entries, info);
1349
1350         /* and save it */
1351         refresh_sequence_number(domain, False);
1352         centry = centry_start(domain, status);
1353         if (!centry)
1354                 goto skip_save;
1355         centry_put_uint32(centry, *num_entries);
1356         for (i=0; i<(*num_entries); i++) {
1357                 centry_put_string(centry, (*info)[i].acct_name);
1358                 centry_put_string(centry, (*info)[i].acct_desc);
1359                 centry_put_uint32(centry, (*info)[i].rid);
1360         }
1361         centry_end(centry, "GL/%s/local", domain->name);
1362         centry_free(centry);
1363
1364 skip_save:
1365         return status;
1366 }
1367
1368 /* convert a single name to a sid in a domain */
1369 static NTSTATUS name_to_sid(struct winbindd_domain *domain,
1370                             TALLOC_CTX *mem_ctx,
1371                             const char *domain_name,
1372                             const char *name,
1373                             DOM_SID *sid,
1374                             enum lsa_SidType *type)
1375 {
1376         struct winbind_cache *cache = get_cache(domain);
1377         struct cache_entry *centry = NULL;
1378         NTSTATUS status;
1379         fstring uname;
1380
1381         if (!cache->tdb)
1382                 goto do_query;
1383
1384         fstrcpy(uname, name);
1385         strupper_m(uname);
1386         centry = wcache_fetch(cache, domain, "NS/%s/%s", domain_name, uname);
1387         if (!centry)
1388                 goto do_query;
1389         *type = (enum lsa_SidType)centry_uint32(centry);
1390         status = centry->status;
1391         if (NT_STATUS_IS_OK(status)) {
1392                 centry_sid(centry, mem_ctx, sid);
1393         }
1394
1395         DEBUG(10,("name_to_sid: [Cached] - cached name for domain %s status: %s\n",
1396                 domain->name, nt_errstr(status) ));
1397
1398         centry_free(centry);
1399         return status;
1400
1401 do_query:
1402         ZERO_STRUCTP(sid);
1403
1404         /* If the seq number check indicated that there is a problem
1405          * with this DC, then return that status... except for
1406          * access_denied.  This is special because the dc may be in
1407          * "restrict anonymous = 1" mode, in which case it will deny
1408          * most unauthenticated operations, but *will* allow the LSA
1409          * name-to-sid that we try as a fallback. */
1410
1411         if (!(NT_STATUS_IS_OK(domain->last_status)
1412               || NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)))
1413                 return domain->last_status;
1414
1415         DEBUG(10,("name_to_sid: [Cached] - doing backend query for name for domain %s\n",
1416                 domain->name ));
1417
1418         status = domain->backend->name_to_sid(domain, mem_ctx, domain_name, name, sid, type);
1419
1420         /* and save it */
1421         refresh_sequence_number(domain, False);
1422
1423         if (domain->online && !is_null_sid(sid)) {
1424                 wcache_save_name_to_sid(domain, status, domain_name, name, sid, *type);
1425         }
1426
1427         if (NT_STATUS_IS_OK(status)) {
1428                 strupper_m(CONST_DISCARD(char *,domain_name));
1429                 strlower_m(CONST_DISCARD(char *,name));
1430                 wcache_save_sid_to_name(domain, status, sid, domain_name, name, *type);
1431         }
1432
1433         return status;
1434 }
1435
1436 /* convert a sid to a user or group name. The sid is guaranteed to be in the domain
1437    given */
1438 static NTSTATUS sid_to_name(struct winbindd_domain *domain,
1439                             TALLOC_CTX *mem_ctx,
1440                             const DOM_SID *sid,
1441                             char **domain_name,
1442                             char **name,
1443                             enum lsa_SidType *type)
1444 {
1445         struct winbind_cache *cache = get_cache(domain);
1446         struct cache_entry *centry = NULL;
1447         NTSTATUS status;
1448         fstring sid_string;
1449
1450         if (!cache->tdb)
1451                 goto do_query;
1452
1453         centry = wcache_fetch(cache, domain, "SN/%s", sid_to_string(sid_string, sid));
1454         if (!centry)
1455                 goto do_query;
1456         if (NT_STATUS_IS_OK(centry->status)) {
1457                 *type = (enum lsa_SidType)centry_uint32(centry);
1458                 *domain_name = centry_string(centry, mem_ctx);
1459                 *name = centry_string(centry, mem_ctx);
1460         }
1461         status = centry->status;
1462
1463         DEBUG(10,("sid_to_name: [Cached] - cached name for domain %s status: %s\n",
1464                 domain->name, nt_errstr(status) ));
1465
1466         centry_free(centry);
1467         return status;
1468
1469 do_query:
1470         *name = NULL;
1471         *domain_name = NULL;
1472
1473         /* If the seq number check indicated that there is a problem
1474          * with this DC, then return that status... except for
1475          * access_denied.  This is special because the dc may be in
1476          * "restrict anonymous = 1" mode, in which case it will deny
1477          * most unauthenticated operations, but *will* allow the LSA
1478          * sid-to-name that we try as a fallback. */
1479
1480         if (!(NT_STATUS_IS_OK(domain->last_status)
1481               || NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)))
1482                 return domain->last_status;
1483
1484         DEBUG(10,("sid_to_name: [Cached] - doing backend query for name for domain %s\n",
1485                 domain->name ));
1486
1487         status = domain->backend->sid_to_name(domain, mem_ctx, sid, domain_name, name, type);
1488
1489         /* and save it */
1490         refresh_sequence_number(domain, False);
1491         wcache_save_sid_to_name(domain, status, sid, *domain_name, *name, *type);
1492
1493         /* We can't save the name to sid mapping here, as with sid history a
1494          * later name2sid would give the wrong sid. */
1495
1496         return status;
1497 }
1498
1499 static NTSTATUS rids_to_names(struct winbindd_domain *domain,
1500                               TALLOC_CTX *mem_ctx,
1501                               const DOM_SID *domain_sid,
1502                               uint32 *rids,
1503                               size_t num_rids,
1504                               char **domain_name,
1505                               char ***names,
1506                               enum lsa_SidType **types)
1507 {
1508         struct winbind_cache *cache = get_cache(domain);
1509         size_t i;
1510         NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
1511         BOOL have_mapped;
1512         BOOL have_unmapped;
1513
1514         *domain_name = NULL;
1515         *names = NULL;
1516         *types = NULL;
1517
1518         if (!cache->tdb) {
1519                 goto do_query;
1520         }
1521
1522         if (num_rids == 0) {
1523                 return NT_STATUS_OK;
1524         }
1525
1526         *names = TALLOC_ARRAY(mem_ctx, char *, num_rids);
1527         *types = TALLOC_ARRAY(mem_ctx, enum lsa_SidType, num_rids);
1528
1529         if ((*names == NULL) || (*types == NULL)) {
1530                 result = NT_STATUS_NO_MEMORY;
1531                 goto error;
1532         }
1533
1534         have_mapped = have_unmapped = False;
1535
1536         for (i=0; i<num_rids; i++) {
1537                 DOM_SID sid;
1538                 struct cache_entry *centry;
1539
1540                 if (!sid_compose(&sid, domain_sid, rids[i])) {
1541                         result = NT_STATUS_INTERNAL_ERROR;
1542                         goto error;
1543                 }
1544
1545                 centry = wcache_fetch(cache, domain, "SN/%s",
1546                                       sid_string_static(&sid));
1547                 if (!centry) {
1548                         goto do_query;
1549                 }
1550
1551                 (*types)[i] = SID_NAME_UNKNOWN;
1552                 (*names)[i] = talloc_strdup(*names, "");
1553
1554                 if (NT_STATUS_IS_OK(centry->status)) {
1555                         char *dom;
1556                         have_mapped = True;
1557                         (*types)[i] = (enum lsa_SidType)centry_uint32(centry);
1558                         dom = centry_string(centry, mem_ctx);
1559                         if (*domain_name == NULL) {
1560                                 *domain_name = dom;
1561                         } else {
1562                                 talloc_free(dom);
1563                         }
1564                         (*names)[i] = centry_string(centry, *names);
1565                 } else {
1566                         have_unmapped = True;
1567                 }
1568
1569                 centry_free(centry);
1570         }
1571
1572         if (!have_mapped) {
1573                 return NT_STATUS_NONE_MAPPED;
1574         }
1575         if (!have_unmapped) {
1576                 return NT_STATUS_OK;
1577         }
1578         return STATUS_SOME_UNMAPPED;
1579
1580  do_query:
1581
1582         TALLOC_FREE(*names);
1583         TALLOC_FREE(*types);
1584
1585         result = domain->backend->rids_to_names(domain, mem_ctx, domain_sid,
1586                                                 rids, num_rids, domain_name,
1587                                                 names, types);
1588
1589         if (!NT_STATUS_IS_OK(result) &&
1590             !NT_STATUS_EQUAL(result, STATUS_SOME_UNMAPPED)) {
1591                 return result;
1592         }
1593
1594         refresh_sequence_number(domain, False);
1595
1596         for (i=0; i<num_rids; i++) {
1597                 DOM_SID sid;
1598                 NTSTATUS status;
1599
1600                 if (!sid_compose(&sid, domain_sid, rids[i])) {
1601                         result = NT_STATUS_INTERNAL_ERROR;
1602                         goto error;
1603                 }
1604
1605                 status = (*types)[i] == SID_NAME_UNKNOWN ?
1606                         NT_STATUS_NONE_MAPPED : NT_STATUS_OK;
1607
1608                 wcache_save_sid_to_name(domain, status, &sid, *domain_name,
1609                                         (*names)[i], (*types)[i]);
1610         }
1611
1612         return result;
1613
1614  error:
1615         
1616         TALLOC_FREE(*names);
1617         TALLOC_FREE(*types);
1618         return result;
1619 }
1620
1621 /* Lookup user information from a rid */
1622 static NTSTATUS query_user(struct winbindd_domain *domain, 
1623                            TALLOC_CTX *mem_ctx, 
1624                            const DOM_SID *user_sid, 
1625                            WINBIND_USERINFO *info)
1626 {
1627         struct winbind_cache *cache = get_cache(domain);
1628         struct cache_entry *centry = NULL;
1629         NTSTATUS status;
1630
1631         if (!cache->tdb)
1632                 goto do_query;
1633
1634         centry = wcache_fetch(cache, domain, "U/%s", sid_string_static(user_sid));
1635         
1636         /* If we have an access denied cache entry and a cached info3 in the
1637            samlogon cache then do a query.  This will force the rpc back end
1638            to return the info3 data. */
1639
1640         if (NT_STATUS_V(domain->last_status) == NT_STATUS_V(NT_STATUS_ACCESS_DENIED) &&
1641             netsamlogon_cache_have(user_sid)) {
1642                 DEBUG(10, ("query_user: cached access denied and have cached info3\n"));
1643                 domain->last_status = NT_STATUS_OK;
1644                 centry_free(centry);
1645                 goto do_query;
1646         }
1647         
1648         if (!centry)
1649                 goto do_query;
1650
1651         info->acct_name = centry_string(centry, mem_ctx);
1652         info->full_name = centry_string(centry, mem_ctx);
1653         info->homedir = centry_string(centry, mem_ctx);
1654         info->shell = centry_string(centry, mem_ctx);
1655         info->primary_gid = centry_uint32(centry);
1656         centry_sid(centry, mem_ctx, &info->user_sid);
1657         centry_sid(centry, mem_ctx, &info->group_sid);
1658         status = centry->status;
1659
1660         DEBUG(10,("query_user: [Cached] - cached info for domain %s status: %s\n",
1661                 domain->name, nt_errstr(status) ));
1662
1663         centry_free(centry);
1664         return status;
1665
1666 do_query:
1667         ZERO_STRUCTP(info);
1668
1669         /* Return status value returned by seq number check */
1670
1671         if (!NT_STATUS_IS_OK(domain->last_status))
1672                 return domain->last_status;
1673         
1674         DEBUG(10,("query_user: [Cached] - doing backend query for info for domain %s\n",
1675                 domain->name ));
1676
1677         status = domain->backend->query_user(domain, mem_ctx, user_sid, info);
1678
1679         /* and save it */
1680         refresh_sequence_number(domain, False);
1681         wcache_save_user(domain, status, info);
1682
1683         return status;
1684 }
1685
1686
1687 /* Lookup groups a user is a member of. */
1688 static NTSTATUS lookup_usergroups(struct winbindd_domain *domain,
1689                                   TALLOC_CTX *mem_ctx,
1690                                   const DOM_SID *user_sid, 
1691                                   uint32 *num_groups, DOM_SID **user_gids)
1692 {
1693         struct winbind_cache *cache = get_cache(domain);
1694         struct cache_entry *centry = NULL;
1695         NTSTATUS status;
1696         unsigned int i;
1697         fstring sid_string;
1698
1699         if (!cache->tdb)
1700                 goto do_query;
1701
1702         centry = wcache_fetch(cache, domain, "UG/%s", sid_to_string(sid_string, user_sid));
1703         
1704         /* If we have an access denied cache entry and a cached info3 in the
1705            samlogon cache then do a query.  This will force the rpc back end
1706            to return the info3 data. */
1707
1708         if (NT_STATUS_V(domain->last_status) == NT_STATUS_V(NT_STATUS_ACCESS_DENIED) &&
1709             netsamlogon_cache_have(user_sid)) {
1710                 DEBUG(10, ("lookup_usergroups: cached access denied and have cached info3\n"));
1711                 domain->last_status = NT_STATUS_OK;
1712                 centry_free(centry);
1713                 goto do_query;
1714         }
1715         
1716         if (!centry)
1717                 goto do_query;
1718
1719         *num_groups = centry_uint32(centry);
1720         
1721         if (*num_groups == 0)
1722                 goto do_cached;
1723
1724         (*user_gids) = TALLOC_ARRAY(mem_ctx, DOM_SID, *num_groups);
1725         if (! (*user_gids)) {
1726                 smb_panic_fn("lookup_usergroups out of memory");
1727                 centry_free(centry);
1728                 return NT_STATUS_NO_MEMORY;
1729         }
1730         for (i=0; i<(*num_groups); i++) {
1731                 centry_sid(centry, mem_ctx, &(*user_gids)[i]);
1732         }
1733
1734 do_cached:      
1735         status = centry->status;
1736
1737         DEBUG(10,("lookup_usergroups: [Cached] - cached info for domain %s status: %s\n",
1738                 domain->name, nt_errstr(status) ));
1739
1740         centry_free(centry);
1741         return status;
1742
1743 do_query:
1744         (*num_groups) = 0;
1745         (*user_gids) = NULL;
1746
1747         /* Return status value returned by seq number check */
1748
1749         if (!NT_STATUS_IS_OK(domain->last_status))
1750                 return domain->last_status;
1751
1752         DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info for domain %s\n",
1753                 domain->name ));
1754
1755         status = domain->backend->lookup_usergroups(domain, mem_ctx, user_sid, num_groups, user_gids);
1756
1757         if ( NT_STATUS_EQUAL(status, NT_STATUS_SYNCHRONIZATION_REQUIRED) )
1758                 goto skip_save;
1759         
1760         /* and save it */
1761         refresh_sequence_number(domain, False);
1762         centry = centry_start(domain, status);
1763         if (!centry)
1764                 goto skip_save;
1765         centry_put_uint32(centry, *num_groups);
1766         for (i=0; i<(*num_groups); i++) {
1767                 centry_put_sid(centry, &(*user_gids)[i]);
1768         }       
1769         centry_end(centry, "UG/%s", sid_to_string(sid_string, user_sid));
1770         centry_free(centry);
1771
1772 skip_save:
1773         return status;
1774 }
1775
1776 static NTSTATUS lookup_useraliases(struct winbindd_domain *domain,
1777                                    TALLOC_CTX *mem_ctx,
1778                                    uint32 num_sids, const DOM_SID *sids,
1779                                    uint32 *num_aliases, uint32 **alias_rids)
1780 {
1781         struct winbind_cache *cache = get_cache(domain);
1782         struct cache_entry *centry = NULL;
1783         NTSTATUS status;
1784         char *sidlist = talloc_strdup(mem_ctx, "");
1785         int i;
1786
1787         if (!cache->tdb)
1788                 goto do_query;
1789
1790         if (num_sids == 0) {
1791                 *num_aliases = 0;
1792                 *alias_rids = NULL;
1793                 return NT_STATUS_OK;
1794         }
1795
1796         /* We need to cache indexed by the whole list of SIDs, the aliases
1797          * resulting might come from any of the SIDs. */
1798
1799         for (i=0; i<num_sids; i++) {
1800                 sidlist = talloc_asprintf(mem_ctx, "%s/%s", sidlist,
1801                                           sid_string_static(&sids[i]));
1802                 if (sidlist == NULL)
1803                         return NT_STATUS_NO_MEMORY;
1804         }
1805
1806         centry = wcache_fetch(cache, domain, "UA%s", sidlist);
1807
1808         if (!centry)
1809                 goto do_query;
1810
1811         *num_aliases = centry_uint32(centry);
1812         *alias_rids = NULL;
1813
1814         if (*num_aliases) {
1815                 (*alias_rids) = TALLOC_ARRAY(mem_ctx, uint32, *num_aliases);
1816
1817                 if ((*alias_rids) == NULL) {
1818                         centry_free(centry);
1819                         return NT_STATUS_NO_MEMORY;
1820                 }
1821         } else {
1822                 (*alias_rids) = NULL;
1823         }
1824
1825         for (i=0; i<(*num_aliases); i++)
1826                 (*alias_rids)[i] = centry_uint32(centry);
1827
1828         status = centry->status;
1829
1830         DEBUG(10,("lookup_useraliases: [Cached] - cached info for domain: %s "
1831                   "status %s\n", domain->name, nt_errstr(status)));
1832
1833         centry_free(centry);
1834         return status;
1835
1836  do_query:
1837         (*num_aliases) = 0;
1838         (*alias_rids) = NULL;
1839
1840         if (!NT_STATUS_IS_OK(domain->last_status))
1841                 return domain->last_status;
1842
1843         DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info "
1844                   "for domain %s\n", domain->name ));
1845
1846         status = domain->backend->lookup_useraliases(domain, mem_ctx,
1847                                                      num_sids, sids,
1848                                                      num_aliases, alias_rids);
1849
1850         /* and save it */
1851         refresh_sequence_number(domain, False);
1852         centry = centry_start(domain, status);
1853         if (!centry)
1854                 goto skip_save;
1855         centry_put_uint32(centry, *num_aliases);
1856         for (i=0; i<(*num_aliases); i++)
1857                 centry_put_uint32(centry, (*alias_rids)[i]);
1858         centry_end(centry, "UA%s", sidlist);
1859         centry_free(centry);
1860
1861  skip_save:
1862         return status;
1863 }
1864
1865
1866 static NTSTATUS lookup_groupmem(struct winbindd_domain *domain,
1867                                 TALLOC_CTX *mem_ctx,
1868                                 const DOM_SID *group_sid, uint32 *num_names, 
1869                                 DOM_SID **sid_mem, char ***names, 
1870                                 uint32 **name_types)
1871 {
1872         struct winbind_cache *cache = get_cache(domain);
1873         struct cache_entry *centry = NULL;
1874         NTSTATUS status;
1875         unsigned int i;
1876         fstring sid_string;
1877
1878         if (!cache->tdb)
1879                 goto do_query;
1880
1881         centry = wcache_fetch(cache, domain, "GM/%s", sid_to_string(sid_string, group_sid));
1882         if (!centry)
1883                 goto do_query;
1884
1885         *num_names = centry_uint32(centry);
1886         
1887         if (*num_names == 0)
1888                 goto do_cached;
1889
1890         (*sid_mem) = TALLOC_ARRAY(mem_ctx, DOM_SID, *num_names);
1891         (*names) = TALLOC_ARRAY(mem_ctx, char *, *num_names);
1892         (*name_types) = TALLOC_ARRAY(mem_ctx, uint32, *num_names);
1893
1894         if (! (*sid_mem) || ! (*names) || ! (*name_types)) {
1895                 smb_panic_fn("lookup_groupmem out of memory");
1896                 centry_free(centry);
1897                 return NT_STATUS_NO_MEMORY;
1898         }
1899
1900         for (i=0; i<(*num_names); i++) {
1901                 centry_sid(centry, mem_ctx, &(*sid_mem)[i]);
1902                 (*names)[i] = centry_string(centry, mem_ctx);
1903                 (*name_types)[i] = centry_uint32(centry);
1904         }
1905
1906 do_cached:      
1907         status = centry->status;
1908
1909         DEBUG(10,("lookup_groupmem: [Cached] - cached info for domain %s status: %s\n",
1910                 domain->name, nt_errstr(status)));
1911
1912         centry_free(centry);
1913         return status;
1914
1915 do_query:
1916         (*num_names) = 0;
1917         (*sid_mem) = NULL;
1918         (*names) = NULL;
1919         (*name_types) = NULL;
1920         
1921         /* Return status value returned by seq number check */
1922
1923         if (!NT_STATUS_IS_OK(domain->last_status))
1924                 return domain->last_status;
1925
1926         DEBUG(10,("lookup_groupmem: [Cached] - doing backend query for info for domain %s\n",
1927                 domain->name ));
1928
1929         status = domain->backend->lookup_groupmem(domain, mem_ctx, group_sid, num_names, 
1930                                                   sid_mem, names, name_types);
1931
1932         /* and save it */
1933         refresh_sequence_number(domain, False);
1934         centry = centry_start(domain, status);
1935         if (!centry)
1936                 goto skip_save;
1937         centry_put_uint32(centry, *num_names);
1938         for (i=0; i<(*num_names); i++) {
1939                 centry_put_sid(centry, &(*sid_mem)[i]);
1940                 centry_put_string(centry, (*names)[i]);
1941                 centry_put_uint32(centry, (*name_types)[i]);
1942         }       
1943         centry_end(centry, "GM/%s", sid_to_string(sid_string, group_sid));
1944         centry_free(centry);
1945
1946 skip_save:
1947         return status;
1948 }
1949
1950 /* find the sequence number for a domain */
1951 static NTSTATUS sequence_number(struct winbindd_domain *domain, uint32 *seq)
1952 {
1953         refresh_sequence_number(domain, False);
1954
1955         *seq = domain->sequence_number;
1956
1957         return NT_STATUS_OK;
1958 }
1959
1960 /* enumerate trusted domains 
1961  * (we need to have the list of trustdoms in the cache when we go offline) -
1962  * Guenther */
1963 static NTSTATUS trusted_domains(struct winbindd_domain *domain,
1964                                 TALLOC_CTX *mem_ctx,
1965                                 uint32 *num_domains,
1966                                 char ***names,
1967                                 char ***alt_names,
1968                                 DOM_SID **dom_sids)
1969 {
1970         struct winbind_cache *cache = get_cache(domain);
1971         struct cache_entry *centry = NULL;
1972         NTSTATUS status;
1973         int i;
1974  
1975         if (!cache->tdb)
1976                 goto do_query;
1977  
1978         centry = wcache_fetch(cache, domain, "TRUSTDOMS/%s", domain->name);
1979         
1980         if (!centry) {
1981                 goto do_query;
1982         }
1983  
1984         *num_domains = centry_uint32(centry);
1985         
1986         if (*num_domains) {
1987                 (*names)        = TALLOC_ARRAY(mem_ctx, char *, *num_domains);
1988                 (*alt_names)    = TALLOC_ARRAY(mem_ctx, char *, *num_domains);
1989                 (*dom_sids)     = TALLOC_ARRAY(mem_ctx, DOM_SID, *num_domains);
1990  
1991                 if (! (*dom_sids) || ! (*names) || ! (*alt_names)) {
1992                         smb_panic_fn("trusted_domains out of memory");
1993                         centry_free(centry);
1994                         return NT_STATUS_NO_MEMORY;
1995                 }
1996         } else {
1997                 (*names) = NULL;
1998                 (*alt_names) = NULL;
1999                 (*dom_sids) = NULL;
2000         }
2001  
2002         for (i=0; i<(*num_domains); i++) {
2003                 (*names)[i] = centry_string(centry, mem_ctx);
2004                 (*alt_names)[i] = centry_string(centry, mem_ctx);
2005                 centry_sid(centry, mem_ctx, &(*dom_sids)[i]);
2006         }
2007
2008         status = centry->status;
2009  
2010         DEBUG(10,("trusted_domains: [Cached] - cached info for domain %s (%d trusts) status: %s\n",
2011                 domain->name, *num_domains, nt_errstr(status) ));
2012  
2013         centry_free(centry);
2014         return status;
2015  
2016 do_query:
2017         (*num_domains) = 0;
2018         (*dom_sids) = NULL;
2019         (*names) = NULL;
2020         (*alt_names) = NULL;
2021  
2022         /* Return status value returned by seq number check */
2023
2024         if (!NT_STATUS_IS_OK(domain->last_status))
2025                 return domain->last_status;
2026         
2027         DEBUG(10,("trusted_domains: [Cached] - doing backend query for info for domain %s\n",
2028                 domain->name ));
2029  
2030         status = domain->backend->trusted_domains(domain, mem_ctx, num_domains,
2031                                                 names, alt_names, dom_sids);
2032
2033         /* no trusts gives NT_STATUS_NO_MORE_ENTRIES resetting to NT_STATUS_OK
2034          * so that the generic centry handling still applies correctly -
2035          * Guenther*/
2036
2037         if (!NT_STATUS_IS_ERR(status)) {
2038                 status = NT_STATUS_OK;
2039         }
2040
2041
2042 #if 0    /* Disabled as we want the trust dom list to be managed by
2043             the main parent and always to make the query.  --jerry */
2044
2045         /* and save it */
2046         refresh_sequence_number(domain, False);
2047  
2048         centry = centry_start(domain, status);
2049         if (!centry)
2050                 goto skip_save;
2051
2052         centry_put_uint32(centry, *num_domains);
2053
2054         for (i=0; i<(*num_domains); i++) {
2055                 centry_put_string(centry, (*names)[i]);
2056                 centry_put_string(centry, (*alt_names)[i]);
2057                 centry_put_sid(centry, &(*dom_sids)[i]);
2058         }
2059         
2060         centry_end(centry, "TRUSTDOMS/%s", domain->name);
2061  
2062         centry_free(centry);
2063  
2064 skip_save:
2065 #endif
2066
2067         return status;
2068 }       
2069
2070 /* get lockout policy */
2071 static NTSTATUS lockout_policy(struct winbindd_domain *domain,
2072                                TALLOC_CTX *mem_ctx,
2073                                SAM_UNK_INFO_12 *policy){
2074         struct winbind_cache *cache = get_cache(domain);
2075         struct cache_entry *centry = NULL;
2076         NTSTATUS status;
2077  
2078         if (!cache->tdb)
2079                 goto do_query;
2080  
2081         centry = wcache_fetch(cache, domain, "LOC_POL/%s", domain->name);
2082         
2083         if (!centry)
2084                 goto do_query;
2085  
2086         policy->duration = centry_nttime(centry);
2087         policy->reset_count = centry_nttime(centry);
2088         policy->bad_attempt_lockout = centry_uint16(centry);
2089  
2090         status = centry->status;
2091  
2092         DEBUG(10,("lockout_policy: [Cached] - cached info for domain %s status: %s\n",
2093                 domain->name, nt_errstr(status) ));
2094  
2095         centry_free(centry);
2096         return status;
2097  
2098 do_query:
2099         ZERO_STRUCTP(policy);
2100  
2101         /* Return status value returned by seq number check */
2102
2103         if (!NT_STATUS_IS_OK(domain->last_status))
2104                 return domain->last_status;
2105         
2106         DEBUG(10,("lockout_policy: [Cached] - doing backend query for info for domain %s\n",
2107                 domain->name ));
2108  
2109         status = domain->backend->lockout_policy(domain, mem_ctx, policy); 
2110  
2111         /* and save it */
2112         refresh_sequence_number(domain, False);
2113         wcache_save_lockout_policy(domain, status, policy);
2114  
2115         return status;
2116 }
2117  
2118 /* get password policy */
2119 static NTSTATUS password_policy(struct winbindd_domain *domain,
2120                                 TALLOC_CTX *mem_ctx,
2121                                 SAM_UNK_INFO_1 *policy)
2122 {
2123         struct winbind_cache *cache = get_cache(domain);
2124         struct cache_entry *centry = NULL;
2125         NTSTATUS status;
2126
2127         if (!cache->tdb)
2128                 goto do_query;
2129  
2130         centry = wcache_fetch(cache, domain, "PWD_POL/%s", domain->name);
2131         
2132         if (!centry)
2133                 goto do_query;
2134
2135         policy->min_length_password = centry_uint16(centry);
2136         policy->password_history = centry_uint16(centry);
2137         policy->password_properties = centry_uint32(centry);
2138         policy->expire = centry_nttime(centry);
2139         policy->min_passwordage = centry_nttime(centry);
2140
2141         status = centry->status;
2142
2143         DEBUG(10,("lockout_policy: [Cached] - cached info for domain %s status: %s\n",
2144                 domain->name, nt_errstr(status) ));
2145
2146         centry_free(centry);
2147         return status;
2148
2149 do_query:
2150         ZERO_STRUCTP(policy);
2151
2152         /* Return status value returned by seq number check */
2153
2154         if (!NT_STATUS_IS_OK(domain->last_status))
2155                 return domain->last_status;
2156         
2157         DEBUG(10,("password_policy: [Cached] - doing backend query for info for domain %s\n",
2158                 domain->name ));
2159
2160         status = domain->backend->password_policy(domain, mem_ctx, policy); 
2161
2162         /* and save it */
2163         refresh_sequence_number(domain, False);
2164         wcache_save_password_policy(domain, status, policy);
2165
2166         return status;
2167 }
2168
2169
2170 /* Invalidate cached user and group lists coherently */
2171
2172 static int traverse_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf, 
2173                        void *state)
2174 {
2175         if (strncmp((const char *)kbuf.dptr, "UL/", 3) == 0 ||
2176             strncmp((const char *)kbuf.dptr, "GL/", 3) == 0)
2177                 tdb_delete(the_tdb, kbuf);
2178
2179         return 0;
2180 }
2181
2182 /* Invalidate the getpwnam and getgroups entries for a winbindd domain */
2183
2184 void wcache_invalidate_samlogon(struct winbindd_domain *domain, 
2185                                 NET_USER_INFO_3 *info3)
2186 {
2187         struct winbind_cache *cache;
2188
2189         /* dont clear cached U/SID and UG/SID entries when we want to logon
2190          * offline - gd */
2191
2192         if (lp_winbind_offline_logon()) {
2193                 return;
2194         }
2195
2196         if (!domain)
2197                 return;
2198
2199         cache = get_cache(domain);
2200         netsamlogon_clear_cached_user(cache->tdb, info3);
2201 }
2202
2203 void wcache_invalidate_cache(void)
2204 {
2205         struct winbindd_domain *domain;
2206
2207         for (domain = domain_list(); domain; domain = domain->next) {
2208                 struct winbind_cache *cache = get_cache(domain);
2209
2210                 DEBUG(10, ("wcache_invalidate_cache: invalidating cache "
2211                            "entries for %s\n", domain->name));
2212                 if (cache)
2213                         tdb_traverse(cache->tdb, traverse_fn, NULL);
2214         }
2215 }
2216
2217 BOOL init_wcache(void)
2218 {
2219         if (wcache == NULL) {
2220                 wcache = SMB_XMALLOC_P(struct winbind_cache);
2221                 ZERO_STRUCTP(wcache);
2222         }
2223
2224         if (wcache->tdb != NULL)
2225                 return True;
2226
2227         /* when working offline we must not clear the cache on restart */
2228         wcache->tdb = tdb_open_log(lock_path("winbindd_cache.tdb"),
2229                                 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE, 
2230                                 lp_winbind_offline_logon() ? TDB_DEFAULT : (TDB_DEFAULT | TDB_CLEAR_IF_FIRST), 
2231                                 O_RDWR|O_CREAT, 0600);
2232
2233         if (wcache->tdb == NULL) {
2234                 DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
2235                 return False;
2236         }
2237
2238         return True;
2239 }
2240
2241 /************************************************************************
2242  This is called by the parent to initialize the cache file.
2243  We don't need sophisticated locking here as we know we're the
2244  only opener.
2245 ************************************************************************/
2246
2247 BOOL initialize_winbindd_cache(void)
2248 {
2249         BOOL cache_bad = True;
2250         uint32 vers;
2251
2252         if (!init_wcache()) {
2253                 DEBUG(0,("initialize_winbindd_cache: init_wcache failed.\n"));
2254                 return False;
2255         }
2256
2257         /* Check version number. */
2258         if (tdb_fetch_uint32(wcache->tdb, WINBINDD_CACHE_VERSION_KEYSTR, &vers) &&
2259                         vers == WINBINDD_CACHE_VERSION) {
2260                 cache_bad = False;
2261         }
2262
2263         if (cache_bad) {
2264                 DEBUG(0,("initialize_winbindd_cache: clearing cache "
2265                         "and re-creating with version number %d\n",
2266                         WINBINDD_CACHE_VERSION ));
2267
2268                 tdb_close(wcache->tdb);
2269                 wcache->tdb = NULL;
2270
2271                 if (unlink(lock_path("winbindd_cache.tdb")) == -1) {
2272                         DEBUG(0,("initialize_winbindd_cache: unlink %s failed %s ",
2273                                 lock_path("winbindd_cache.tdb"),
2274                                 strerror(errno) ));
2275                         return False;
2276                 }
2277                 if (!init_wcache()) {
2278                         DEBUG(0,("initialize_winbindd_cache: re-initialization "
2279                                         "init_wcache failed.\n"));
2280                         return False;
2281                 }
2282
2283                 /* Write the version. */
2284                 if (!tdb_store_uint32(wcache->tdb, WINBINDD_CACHE_VERSION_KEYSTR, WINBINDD_CACHE_VERSION)) {
2285                         DEBUG(0,("initialize_winbindd_cache: version number store failed %s\n",
2286                                 tdb_errorstr(wcache->tdb) ));
2287                         return False;
2288                 }
2289         }
2290
2291         tdb_close(wcache->tdb);
2292         wcache->tdb = NULL;
2293         return True;
2294 }
2295
2296 void cache_store_response(pid_t pid, struct winbindd_response *response)
2297 {
2298         fstring key_str;
2299
2300         if (!init_wcache())
2301                 return;
2302
2303         DEBUG(10, ("Storing response for pid %d, len %d\n",
2304                    pid, response->length));
2305
2306         fstr_sprintf(key_str, "DR/%d", pid);
2307         if (tdb_store(wcache->tdb, string_tdb_data(key_str), 
2308                       make_tdb_data((uint8 *)response, sizeof(*response)),
2309                       TDB_REPLACE) == -1)
2310                 return;
2311
2312         if (response->length == sizeof(*response))
2313                 return;
2314
2315         /* There's extra data */
2316
2317         DEBUG(10, ("Storing extra data: len=%d\n",
2318                    (int)(response->length - sizeof(*response))));
2319
2320         fstr_sprintf(key_str, "DE/%d", pid);
2321         if (tdb_store(wcache->tdb, string_tdb_data(key_str),
2322                       make_tdb_data((uint8 *)response->extra_data.data,
2323                                     response->length - sizeof(*response)),
2324                       TDB_REPLACE) == 0)
2325                 return;
2326
2327         /* We could not store the extra data, make sure the tdb does not
2328          * contain a main record with wrong dangling extra data */
2329
2330         fstr_sprintf(key_str, "DR/%d", pid);
2331         tdb_delete(wcache->tdb, string_tdb_data(key_str));
2332
2333         return;
2334 }
2335
2336 BOOL cache_retrieve_response(pid_t pid, struct winbindd_response * response)
2337 {
2338         TDB_DATA data;
2339         fstring key_str;
2340
2341         if (!init_wcache())
2342                 return False;
2343
2344         DEBUG(10, ("Retrieving response for pid %d\n", pid));
2345
2346         fstr_sprintf(key_str, "DR/%d", pid);
2347         data = tdb_fetch(wcache->tdb, string_tdb_data(key_str));
2348
2349         if (data.dptr == NULL)
2350                 return False;
2351
2352         if (data.dsize != sizeof(*response))
2353                 return False;
2354
2355         memcpy(response, data.dptr, data.dsize);
2356         SAFE_FREE(data.dptr);
2357
2358         if (response->length == sizeof(*response)) {
2359                 response->extra_data.data = NULL;
2360                 return True;
2361         }
2362
2363         /* There's extra data */
2364
2365         DEBUG(10, ("Retrieving extra data length=%d\n",
2366                    (int)(response->length - sizeof(*response))));
2367
2368         fstr_sprintf(key_str, "DE/%d", pid);
2369         data = tdb_fetch(wcache->tdb, string_tdb_data(key_str));
2370
2371         if (data.dptr == NULL) {
2372                 DEBUG(0, ("Did not find extra data\n"));
2373                 return False;
2374         }
2375
2376         if (data.dsize != (response->length - sizeof(*response))) {
2377                 DEBUG(0, ("Invalid extra data length: %d\n", (int)data.dsize));
2378                 SAFE_FREE(data.dptr);
2379                 return False;
2380         }
2381
2382         dump_data(11, (uint8 *)data.dptr, data.dsize);
2383
2384         response->extra_data.data = data.dptr;
2385         return True;
2386 }
2387
2388 void cache_cleanup_response(pid_t pid)
2389 {
2390         fstring key_str;
2391
2392         if (!init_wcache())
2393                 return;
2394
2395         fstr_sprintf(key_str, "DR/%d", pid);
2396         tdb_delete(wcache->tdb, string_tdb_data(key_str));
2397
2398         fstr_sprintf(key_str, "DE/%d", pid);
2399         tdb_delete(wcache->tdb, string_tdb_data(key_str));
2400
2401         return;
2402 }
2403
2404
2405 BOOL lookup_cached_sid(TALLOC_CTX *mem_ctx, const DOM_SID *sid,
2406                        char **domain_name, char **name,
2407                        enum lsa_SidType *type)
2408 {
2409         struct winbindd_domain *domain;
2410         struct winbind_cache *cache;
2411         struct cache_entry *centry = NULL;
2412         NTSTATUS status;
2413
2414         domain = find_lookup_domain_from_sid(sid);
2415         if (domain == NULL) {
2416                 return False;
2417         }
2418
2419         cache = get_cache(domain);
2420
2421         if (cache->tdb == NULL) {
2422                 return False;
2423         }
2424
2425         centry = wcache_fetch(cache, domain, "SN/%s", sid_string_static(sid));
2426         if (centry == NULL) {
2427                 return False;
2428         }
2429
2430         if (NT_STATUS_IS_OK(centry->status)) {
2431                 *type = (enum lsa_SidType)centry_uint32(centry);
2432                 *domain_name = centry_string(centry, mem_ctx);
2433                 *name = centry_string(centry, mem_ctx);
2434         }
2435
2436         status = centry->status;
2437         centry_free(centry);
2438         return NT_STATUS_IS_OK(status);
2439 }
2440
2441 BOOL lookup_cached_name(TALLOC_CTX *mem_ctx,
2442                         const char *domain_name,
2443                         const char *name,
2444                         DOM_SID *sid,
2445                         enum lsa_SidType *type)
2446 {
2447         struct winbindd_domain *domain;
2448         struct winbind_cache *cache;
2449         struct cache_entry *centry = NULL;
2450         NTSTATUS status;
2451         fstring uname;
2452         BOOL original_online_state;     
2453
2454         domain = find_lookup_domain_from_name(domain_name);
2455         if (domain == NULL) {
2456                 return False;
2457         }
2458
2459         cache = get_cache(domain);
2460
2461         if (cache->tdb == NULL) {
2462                 return False;
2463         }
2464
2465         fstrcpy(uname, name);
2466         strupper_m(uname);
2467         
2468         /* If we are doing a cached logon, temporarily set the domain
2469            offline so the cache won't expire the entry */
2470         
2471         original_online_state = domain->online;
2472         domain->online = False;
2473         centry = wcache_fetch(cache, domain, "NS/%s/%s", domain_name, uname);
2474         domain->online = original_online_state;
2475         
2476         if (centry == NULL) {
2477                 return False;
2478         }
2479
2480         if (NT_STATUS_IS_OK(centry->status)) {
2481                 *type = (enum lsa_SidType)centry_uint32(centry);
2482                 centry_sid(centry, mem_ctx, sid);
2483         }
2484
2485         status = centry->status;
2486         centry_free(centry);
2487         
2488         return NT_STATUS_IS_OK(status);
2489 }
2490
2491 void cache_name2sid(struct winbindd_domain *domain, 
2492                     const char *domain_name, const char *name,
2493                     enum lsa_SidType type, const DOM_SID *sid)
2494 {
2495         refresh_sequence_number(domain, False);
2496         wcache_save_name_to_sid(domain, NT_STATUS_OK, domain_name, name,
2497                                 sid, type);
2498 }
2499
2500 /*
2501  * The original idea that this cache only contains centries has
2502  * been blurred - now other stuff gets put in here. Ensure we
2503  * ignore these things on cleanup.
2504  */
2505
2506 static int traverse_fn_cleanup(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, 
2507                                TDB_DATA dbuf, void *state)
2508 {
2509         struct cache_entry *centry;
2510
2511         if (is_non_centry_key(kbuf)) {
2512                 return 0;
2513         }
2514
2515         centry = wcache_fetch_raw((char *)kbuf.dptr);
2516         if (!centry) {
2517                 return 0;
2518         }
2519
2520         if (!NT_STATUS_IS_OK(centry->status)) {
2521                 DEBUG(10,("deleting centry %s\n", (const char *)kbuf.dptr));
2522                 tdb_delete(the_tdb, kbuf);
2523         }
2524
2525         centry_free(centry);
2526         return 0;
2527 }
2528
2529 /* flush the cache */
2530 void wcache_flush_cache(void)
2531 {
2532         if (!wcache)
2533                 return;
2534         if (wcache->tdb) {
2535                 tdb_close(wcache->tdb);
2536                 wcache->tdb = NULL;
2537         }
2538         if (opt_nocache)
2539                 return;
2540
2541         /* when working offline we must not clear the cache on restart */
2542         wcache->tdb = tdb_open_log(lock_path("winbindd_cache.tdb"),
2543                                 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE, 
2544                                 lp_winbind_offline_logon() ? TDB_DEFAULT : (TDB_DEFAULT | TDB_CLEAR_IF_FIRST), 
2545                                 O_RDWR|O_CREAT, 0600);
2546
2547         if (!wcache->tdb) {
2548                 DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
2549                 return;
2550         }
2551
2552         tdb_traverse(wcache->tdb, traverse_fn_cleanup, NULL);
2553
2554         DEBUG(10,("wcache_flush_cache success\n"));
2555 }
2556
2557 /* Count cached creds */
2558
2559 static int traverse_fn_cached_creds(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf, 
2560                                     void *state)
2561 {
2562         int *cred_count = (int*)state;
2563  
2564         if (strncmp((const char *)kbuf.dptr, "CRED/", 5) == 0) {
2565                 (*cred_count)++;
2566         }
2567         return 0;
2568 }
2569
2570 NTSTATUS wcache_count_cached_creds(struct winbindd_domain *domain, int *count)
2571 {
2572         struct winbind_cache *cache = get_cache(domain);
2573
2574         *count = 0;
2575
2576         if (!cache->tdb) {
2577                 return NT_STATUS_INTERNAL_DB_ERROR;
2578         }
2579  
2580         tdb_traverse(cache->tdb, traverse_fn_cached_creds, (void *)count);
2581
2582         return NT_STATUS_OK;
2583 }
2584
2585 struct cred_list {
2586         struct cred_list *prev, *next;
2587         TDB_DATA key;
2588         fstring name;
2589         time_t created;
2590 };
2591 static struct cred_list *wcache_cred_list;
2592
2593 static int traverse_fn_get_credlist(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf, 
2594                                     void *state)
2595 {
2596         struct cred_list *cred;
2597
2598         if (strncmp((const char *)kbuf.dptr, "CRED/", 5) == 0) {
2599
2600                 cred = SMB_MALLOC_P(struct cred_list);
2601                 if (cred == NULL) {
2602                         DEBUG(0,("traverse_fn_remove_first_creds: failed to malloc new entry for list\n"));
2603                         return -1;
2604                 }
2605
2606                 ZERO_STRUCTP(cred);
2607                 
2608                 /* save a copy of the key */
2609                 
2610                 fstrcpy(cred->name, (const char *)kbuf.dptr);           
2611                 DLIST_ADD(wcache_cred_list, cred);
2612         }
2613         
2614         return 0;
2615 }
2616
2617 NTSTATUS wcache_remove_oldest_cached_creds(struct winbindd_domain *domain, const DOM_SID *sid) 
2618 {
2619         struct winbind_cache *cache = get_cache(domain);
2620         NTSTATUS status;
2621         int ret;
2622         struct cred_list *cred, *oldest = NULL;
2623
2624         if (!cache->tdb) {
2625                 return NT_STATUS_INTERNAL_DB_ERROR;
2626         }
2627
2628         /* we possibly already have an entry */
2629         if (sid && NT_STATUS_IS_OK(wcache_cached_creds_exist(domain, sid))) {
2630         
2631                 fstring key_str;
2632
2633                 DEBUG(11,("we already have an entry, deleting that\n"));
2634
2635                 fstr_sprintf(key_str, "CRED/%s", sid_string_static(sid));
2636
2637                 tdb_delete(cache->tdb, string_tdb_data(key_str));
2638
2639                 return NT_STATUS_OK;
2640         }
2641
2642         ret = tdb_traverse(cache->tdb, traverse_fn_get_credlist, NULL);
2643         if (ret == 0) {
2644                 return NT_STATUS_OK;
2645         } else if ((ret == -1) || (wcache_cred_list == NULL)) {
2646                 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
2647         }
2648
2649         ZERO_STRUCTP(oldest);
2650
2651         for (cred = wcache_cred_list; cred; cred = cred->next) {
2652
2653                 TDB_DATA data;
2654                 time_t t;
2655
2656                 data = tdb_fetch(cache->tdb, string_tdb_data(cred->name));
2657                 if (!data.dptr) {
2658                         DEBUG(10,("wcache_remove_oldest_cached_creds: entry for [%s] not found\n", 
2659                                 cred->name));
2660                         status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
2661                         goto done;
2662                 }
2663         
2664                 t = IVAL(data.dptr, 0);
2665                 SAFE_FREE(data.dptr);
2666
2667                 if (!oldest) {
2668                         oldest = SMB_MALLOC_P(struct cred_list);
2669                         if (oldest == NULL) {
2670                                 status = NT_STATUS_NO_MEMORY;
2671                                 goto done;
2672                         }
2673
2674                         fstrcpy(oldest->name, cred->name);
2675                         oldest->created = t;
2676                         continue;
2677                 }
2678
2679                 if (t < oldest->created) {
2680                         fstrcpy(oldest->name, cred->name);
2681                         oldest->created = t;
2682                 }
2683         }
2684
2685         if (tdb_delete(cache->tdb, string_tdb_data(oldest->name)) == 0) {
2686                 status = NT_STATUS_OK;
2687         } else {
2688                 status = NT_STATUS_UNSUCCESSFUL;
2689         }
2690 done:
2691         SAFE_FREE(wcache_cred_list);
2692         SAFE_FREE(oldest);
2693         
2694         return status;
2695 }
2696
2697 /* Change the global online/offline state. */
2698 BOOL set_global_winbindd_state_offline(void)
2699 {
2700         TDB_DATA data;
2701
2702         DEBUG(10,("set_global_winbindd_state_offline: offline requested.\n"));
2703
2704         /* Only go offline if someone has created
2705            the key "WINBINDD_OFFLINE" in the cache tdb. */
2706
2707         if (wcache == NULL || wcache->tdb == NULL) {
2708                 DEBUG(10,("set_global_winbindd_state_offline: wcache not open yet.\n"));
2709                 return False;
2710         }
2711
2712         if (!lp_winbind_offline_logon()) {
2713                 DEBUG(10,("set_global_winbindd_state_offline: rejecting.\n"));
2714                 return False;
2715         }
2716
2717         if (global_winbindd_offline_state) {
2718                 /* Already offline. */
2719                 return True;
2720         }
2721
2722         data = tdb_fetch_bystring( wcache->tdb, "WINBINDD_OFFLINE" );
2723
2724         if (!data.dptr || data.dsize != 4) {
2725                 DEBUG(10,("set_global_winbindd_state_offline: offline state not set.\n"));
2726                 SAFE_FREE(data.dptr);
2727                 return False;
2728         } else {
2729                 DEBUG(10,("set_global_winbindd_state_offline: offline state set.\n"));
2730                 global_winbindd_offline_state = True;
2731                 SAFE_FREE(data.dptr);
2732                 return True;
2733         }
2734 }
2735
2736 void set_global_winbindd_state_online(void)
2737 {
2738         DEBUG(10,("set_global_winbindd_state_online: online requested.\n"));
2739
2740         if (!lp_winbind_offline_logon()) {
2741                 DEBUG(10,("set_global_winbindd_state_online: rejecting.\n"));
2742                 return;
2743         }
2744
2745         if (!global_winbindd_offline_state) {
2746                 /* Already online. */
2747                 return;
2748         }
2749         global_winbindd_offline_state = False;
2750
2751         if (!wcache->tdb) {
2752                 return;
2753         }
2754
2755         /* Ensure there is no key "WINBINDD_OFFLINE" in the cache tdb. */
2756         tdb_delete_bystring(wcache->tdb, "WINBINDD_OFFLINE");
2757 }
2758
2759 BOOL get_global_winbindd_state_offline(void)
2760 {
2761         return global_winbindd_offline_state;
2762 }
2763
2764 /***********************************************************************
2765  Validate functions for all possible cache tdb keys.
2766 ***********************************************************************/
2767
2768 struct validation_status {
2769         BOOL tdb_error;
2770         BOOL bad_freelist;
2771         BOOL bad_entry;
2772         BOOL unknown_key;
2773         BOOL success;
2774 };
2775
2776 static struct cache_entry *create_centry_validate(const char *kstr, TDB_DATA data, 
2777                                                   struct validation_status *state)
2778 {
2779         struct cache_entry *centry;
2780
2781         centry = SMB_XMALLOC_P(struct cache_entry);
2782         centry->data = (unsigned char *)memdup(data.dptr, data.dsize);
2783         if (!centry->data) {
2784                 SAFE_FREE(centry);
2785                 return NULL;
2786         }
2787         centry->len = data.dsize;
2788         centry->ofs = 0;
2789
2790         if (centry->len < 8) {
2791                 /* huh? corrupt cache? */
2792                 DEBUG(0,("create_centry_validate: Corrupt cache for key %s (len < 8) ?\n", kstr));
2793                 centry_free(centry);
2794                 state->bad_entry = True;
2795                 state->success = False;
2796                 return NULL;
2797         }
2798
2799         centry->status = NT_STATUS(centry_uint32(centry));
2800         centry->sequence_number = centry_uint32(centry);
2801         return centry;
2802 }
2803
2804 static int validate_seqnum(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
2805                            struct validation_status *state)
2806 {
2807         if (dbuf.dsize != 8) {
2808                 DEBUG(0,("validate_seqnum: Corrupt cache for key %s (len %u != 8) ?\n",
2809                                 keystr, (unsigned int)dbuf.dsize ));
2810                 state->bad_entry = True;
2811                 return 1;
2812         }
2813         return 0;
2814 }
2815
2816 static int validate_ns(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
2817                        struct validation_status *state)
2818 {
2819         struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
2820         if (!centry) {
2821                 return 1;
2822         }
2823
2824         (void)centry_uint32(centry);
2825         if (NT_STATUS_IS_OK(centry->status)) {
2826                 DOM_SID sid;
2827                 (void)centry_sid(centry, mem_ctx, &sid);
2828         }
2829
2830         centry_free(centry);
2831
2832         if (!(state->success)) {
2833                 return 1;
2834         }
2835         DEBUG(10,("validate_ns: %s ok\n", keystr));
2836         return 0;
2837 }
2838
2839 static int validate_sn(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
2840                        struct validation_status *state)
2841 {
2842         struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
2843         if (!centry) {
2844                 return 1;
2845         }
2846
2847         if (NT_STATUS_IS_OK(centry->status)) {
2848                 (void)centry_uint32(centry);
2849                 (void)centry_string(centry, mem_ctx);
2850                 (void)centry_string(centry, mem_ctx);
2851         }
2852
2853         centry_free(centry);
2854
2855         if (!(state->success)) {
2856                 return 1;
2857         }
2858         DEBUG(10,("validate_sn: %s ok\n", keystr));
2859         return 0;
2860 }
2861
2862 static int validate_u(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
2863                       struct validation_status *state)
2864 {
2865         struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
2866         DOM_SID sid;
2867
2868         if (!centry) {
2869                 return 1;
2870         }
2871
2872         (void)centry_string(centry, mem_ctx);
2873         (void)centry_string(centry, mem_ctx);
2874         (void)centry_string(centry, mem_ctx);
2875         (void)centry_string(centry, mem_ctx);
2876         (void)centry_uint32(centry);
2877         (void)centry_sid(centry, mem_ctx, &sid);
2878         (void)centry_sid(centry, mem_ctx, &sid);
2879
2880         centry_free(centry);
2881
2882         if (!(state->success)) {
2883                 return 1;
2884         }
2885         DEBUG(10,("validate_u: %s ok\n", keystr));
2886         return 0;
2887 }
2888
2889 static int validate_loc_pol(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
2890                             struct validation_status *state)
2891 {
2892         struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
2893
2894         if (!centry) {
2895                 return 1;
2896         }
2897
2898         (void)centry_nttime(centry);
2899         (void)centry_nttime(centry);
2900         (void)centry_uint16(centry);
2901
2902         centry_free(centry);
2903
2904         if (!(state->success)) {
2905                 return 1;
2906         }
2907         DEBUG(10,("validate_loc_pol: %s ok\n", keystr));
2908         return 0;
2909 }
2910
2911 static int validate_pwd_pol(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
2912                             struct validation_status *state)
2913 {
2914         struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
2915
2916         if (!centry) {
2917                 return 1;
2918         }
2919
2920         (void)centry_uint16(centry);
2921         (void)centry_uint16(centry);
2922         (void)centry_uint32(centry);
2923         (void)centry_nttime(centry);
2924         (void)centry_nttime(centry);
2925
2926         centry_free(centry);
2927
2928         if (!(state->success)) {
2929                 return 1;
2930         }
2931         DEBUG(10,("validate_pwd_pol: %s ok\n", keystr));
2932         return 0;
2933 }
2934
2935 static int validate_cred(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
2936                          struct validation_status *state)
2937 {
2938         struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
2939
2940         if (!centry) {
2941                 return 1;
2942         }
2943
2944         (void)centry_time(centry);
2945         (void)centry_hash16(centry, mem_ctx);
2946
2947         /* We only have 17 bytes more data in the salted cred case. */
2948         if (centry->len - centry->ofs == 17) {
2949                 (void)centry_hash16(centry, mem_ctx);
2950         }
2951
2952         centry_free(centry);
2953
2954         if (!(state->success)) {
2955                 return 1;
2956         }
2957         DEBUG(10,("validate_cred: %s ok\n", keystr));
2958         return 0;
2959 }
2960
2961 static int validate_ul(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
2962                        struct validation_status *state)
2963 {
2964         struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
2965         int32 num_entries, i;
2966
2967         if (!centry) {
2968                 return 1;
2969         }
2970
2971         num_entries = (int32)centry_uint32(centry);
2972
2973         for (i=0; i< num_entries; i++) {
2974                 DOM_SID sid;
2975                 (void)centry_string(centry, mem_ctx);
2976                 (void)centry_string(centry, mem_ctx);
2977                 (void)centry_string(centry, mem_ctx);
2978                 (void)centry_string(centry, mem_ctx);
2979                 (void)centry_sid(centry, mem_ctx, &sid);
2980                 (void)centry_sid(centry, mem_ctx, &sid);
2981         }
2982
2983         centry_free(centry);
2984
2985         if (!(state->success)) {
2986                 return 1;
2987         }
2988         DEBUG(10,("validate_ul: %s ok\n", keystr));
2989         return 0;
2990 }
2991
2992 static int validate_gl(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
2993                        struct validation_status *state)
2994 {
2995         struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
2996         int32 num_entries, i;
2997
2998         if (!centry) {
2999                 return 1;
3000         }
3001
3002         num_entries = centry_uint32(centry);
3003         
3004         for (i=0; i< num_entries; i++) {
3005                 (void)centry_string(centry, mem_ctx);
3006                 (void)centry_string(centry, mem_ctx);
3007                 (void)centry_uint32(centry);
3008         }
3009
3010         centry_free(centry);
3011
3012         if (!(state->success)) {
3013                 return 1;
3014         }
3015         DEBUG(10,("validate_gl: %s ok\n", keystr));
3016         return 0;
3017 }
3018
3019 static int validate_ug(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3020                        struct validation_status *state)
3021 {
3022         struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3023         int32 num_groups, i;
3024
3025         if (!centry) {
3026                 return 1;
3027         }
3028
3029         num_groups = centry_uint32(centry);
3030
3031         for (i=0; i< num_groups; i++) {
3032                 DOM_SID sid;
3033                 centry_sid(centry, mem_ctx, &sid);
3034         }
3035
3036         centry_free(centry);
3037
3038         if (!(state->success)) {
3039                 return 1;
3040         }
3041         DEBUG(10,("validate_ug: %s ok\n", keystr));
3042         return 0;
3043 }
3044
3045 static int validate_ua(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3046                        struct validation_status *state)
3047 {
3048         struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3049         int32 num_aliases, i;
3050
3051         if (!centry) {
3052                 return 1;
3053         }
3054
3055         num_aliases = centry_uint32(centry);
3056
3057         for (i=0; i < num_aliases; i++) {
3058                 (void)centry_uint32(centry);
3059         }
3060
3061         centry_free(centry);
3062
3063         if (!(state->success)) {
3064                 return 1;
3065         }
3066         DEBUG(10,("validate_ua: %s ok\n", keystr));
3067         return 0;
3068 }
3069
3070 static int validate_gm(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3071                        struct validation_status *state)
3072 {
3073         struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3074         int32 num_names, i;
3075
3076         if (!centry) {
3077                 return 1;
3078         }
3079
3080         num_names = centry_uint32(centry);
3081
3082         for (i=0; i< num_names; i++) {
3083                 DOM_SID sid;
3084                 centry_sid(centry, mem_ctx, &sid);
3085                 (void)centry_string(centry, mem_ctx);
3086                 (void)centry_uint32(centry);
3087         }
3088
3089         centry_free(centry);
3090
3091         if (!(state->success)) {
3092                 return 1;
3093         }
3094         DEBUG(10,("validate_gm: %s ok\n", keystr));
3095         return 0;
3096 }
3097
3098 static int validate_dr(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3099                        struct validation_status *state)
3100 {
3101         /* Can't say anything about this other than must be nonzero. */
3102         if (dbuf.dsize == 0) {
3103                 DEBUG(0,("validate_dr: Corrupt cache for key %s (len == 0) ?\n",
3104                                 keystr));
3105                 state->bad_entry = True;
3106                 state->success = False;
3107                 return 1;
3108         }
3109
3110         DEBUG(10,("validate_dr: %s ok\n", keystr));
3111         return 0;
3112 }
3113
3114 static int validate_de(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3115                        struct validation_status *state)
3116 {
3117         /* Can't say anything about this other than must be nonzero. */
3118         if (dbuf.dsize == 0) {
3119                 DEBUG(0,("validate_de: Corrupt cache for key %s (len == 0) ?\n",
3120                                 keystr));
3121                 state->bad_entry = True;
3122                 state->success = False;
3123                 return 1;
3124         }
3125
3126         DEBUG(10,("validate_de: %s ok\n", keystr));
3127         return 0;
3128 }
3129
3130 static int validate_trustdoms(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3131                               struct validation_status *state)
3132 {
3133         struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3134         int32 num_domains, i;
3135
3136         if (!centry) {
3137                 return 1;
3138         }
3139
3140         num_domains = centry_uint32(centry);
3141         
3142         for (i=0; i< num_domains; i++) {
3143                 DOM_SID sid;
3144                 (void)centry_string(centry, mem_ctx);
3145                 (void)centry_string(centry, mem_ctx);
3146                 (void)centry_sid(centry, mem_ctx, &sid);
3147         }
3148
3149         centry_free(centry);
3150
3151         if (!(state->success)) {
3152                 return 1;
3153         }
3154         DEBUG(10,("validate_trustdoms: %s ok\n", keystr));
3155         return 0;
3156 }
3157
3158 static int validate_trustdomcache(TALLOC_CTX *mem_ctx, const char *keystr, 
3159                                   TDB_DATA dbuf,
3160                                   struct validation_status *state)
3161 {
3162         if (dbuf.dsize == 0) {
3163                 DEBUG(0, ("validate_trustdomcache: Corrupt cache for "
3164                           "key %s (len ==0) ?\n", keystr));
3165                 state->bad_entry = True;
3166                 state->success = False;
3167                 return 1;
3168         }
3169
3170         DEBUG(10,    ("validate_trustdomcache: %s ok\n", keystr));
3171         DEBUGADD(10, ("  Don't trust me, I am a DUMMY!\n"));
3172         return 0;
3173 }
3174
3175 static int validate_offline(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3176                             struct validation_status *state)
3177 {
3178         if (dbuf.dsize != 4) {
3179                 DEBUG(0,("validate_offline: Corrupt cache for key %s (len %u != 4) ?\n",
3180                                 keystr, (unsigned int)dbuf.dsize ));
3181                 state->bad_entry = True;
3182                 state->success = False;
3183                 return 1;
3184         }
3185         DEBUG(10,("validate_offline: %s ok\n", keystr));
3186         return 0;
3187 }
3188
3189 static int validate_cache_version(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3190                                   struct validation_status *state)
3191 {
3192         if (dbuf.dsize != 4) {
3193                 DEBUG(0, ("validate_cache_version: Corrupt cache for "
3194                           "key %s (len %u != 4) ?\n", 
3195                           keystr, (unsigned int)dbuf.dsize));
3196                 state->bad_entry = True;
3197                 state->success = False;
3198                 return 1;
3199         }
3200
3201         DEBUG(10, ("validate_cache_version: %s ok\n", keystr));
3202         return 0;
3203 }
3204
3205 /***********************************************************************
3206  A list of all possible cache tdb keys with associated validation
3207  functions.
3208 ***********************************************************************/
3209
3210 struct key_val_struct {
3211         const char *keyname;
3212         int (*validate_data_fn)(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf, struct validation_status* state);
3213 } key_val[] = {
3214         {"SEQNUM/", validate_seqnum},
3215         {"NS/", validate_ns},
3216         {"SN/", validate_sn},
3217         {"U/", validate_u},
3218         {"LOC_POL/", validate_loc_pol},
3219         {"PWD_POL/", validate_pwd_pol},
3220         {"CRED/", validate_cred},
3221         {"UL/", validate_ul},
3222         {"GL/", validate_gl},
3223         {"UG/", validate_ug},
3224         {"UA", validate_ua},
3225         {"GM/", validate_gm},
3226         {"DR/", validate_dr},
3227         {"DE/", validate_de},
3228         {"TRUSTDOMS/", validate_trustdoms},
3229         {"TRUSTDOMCACHE/", validate_trustdomcache},
3230         {"WINBINDD_OFFLINE", validate_offline},
3231         {WINBINDD_CACHE_VERSION_KEYSTR, validate_cache_version},
3232         {NULL, NULL}
3233 };
3234
3235 /***********************************************************************
3236  Function to look at every entry in the tdb and validate it as far as
3237  possible.
3238 ***********************************************************************/
3239
3240 static int cache_traverse_validate_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf, void *state)
3241 {
3242         int i;
3243         struct validation_status *v_state = (struct validation_status *)state;
3244
3245         /* Paranoia check. */
3246         if (kbuf.dsize > 1024) {
3247                 DEBUG(0,("cache_traverse_validate_fn: key length too large (%u) > 1024\n\n",
3248                                 (unsigned int)kbuf.dsize ));
3249                 return 1;
3250         }
3251
3252         for (i = 0; key_val[i].keyname; i++) {
3253                 size_t namelen = strlen(key_val[i].keyname);
3254                 if (kbuf.dsize >= namelen && (
3255                                 strncmp(key_val[i].keyname, (const char *)kbuf.dptr, namelen)) == 0) {
3256                         TALLOC_CTX *mem_ctx;
3257                         char *keystr;
3258                         int ret;
3259
3260                         keystr = SMB_MALLOC_ARRAY(char, kbuf.dsize+1);
3261                         if (!keystr) {
3262                                 return 1;
3263                         }
3264                         memcpy(keystr, kbuf.dptr, kbuf.dsize);
3265                         keystr[kbuf.dsize] = '\0';
3266
3267                         mem_ctx = talloc_init("validate_ctx");
3268                         if (!mem_ctx) {
3269                                 SAFE_FREE(keystr);
3270                                 return 1;
3271                         }
3272
3273                         ret = key_val[i].validate_data_fn(mem_ctx, keystr, dbuf, 
3274                                                           v_state);
3275
3276                         SAFE_FREE(keystr);
3277                         talloc_destroy(mem_ctx);
3278                         return ret;
3279                 }
3280         }
3281
3282         DEBUG(0,("cache_traverse_validate_fn: unknown cache entry\nkey :\n"));
3283         dump_data(0, (uint8 *)kbuf.dptr, kbuf.dsize);
3284         DEBUG(0,("data :\n"));
3285         dump_data(0, (uint8 *)dbuf.dptr, dbuf.dsize);
3286         v_state->unknown_key = True;
3287         v_state->success = False;
3288         return 1; /* terminate. */
3289 }
3290
3291 static void validate_panic(const char *const why)
3292 {
3293         DEBUG(0,("validating cache: would panic %s\n", why ));
3294         DEBUGADD(0, ("exiting instead (cache validation mode)\n"));
3295         exit(47);
3296 }
3297
3298 /***********************************************************************
3299  Try and validate every entry in the winbindd cache. If we fail here,
3300  delete the cache tdb and return non-zero - the caller (main winbindd
3301  function) will restart us as we don't know if we crashed or not.
3302 ***********************************************************************/
3303
3304 /* 
3305  * internal validation function, executed by the child.  
3306  */
3307 static int winbindd_validate_cache_child(const char *cache_path, int pfd)
3308 {
3309         int ret = -1;
3310         int tfd = -1;
3311         int num_entries = 0;
3312         TDB_CONTEXT *tdb = NULL;
3313         struct validation_status v_status;
3314         
3315         v_status.tdb_error = False;
3316         v_status.bad_freelist = False;
3317         v_status.bad_entry = False;
3318         v_status.unknown_key = False;
3319         v_status.success = True;
3320
3321         tdb = tdb_open_log(cache_path,
3322                         WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
3323                         lp_winbind_offline_logon() 
3324                                 ?  TDB_DEFAULT 
3325                                 : (TDB_DEFAULT | TDB_CLEAR_IF_FIRST),
3326                         O_RDWR|O_CREAT, 0600);
3327         if (!tdb) {
3328                 v_status.tdb_error = True;
3329                 v_status.success = False;
3330                 goto out;
3331         }
3332
3333         tfd = tdb_fd(tdb);
3334
3335         /* Check the cache freelist is good. */
3336         if (tdb_validate_freelist(tdb, &num_entries) == -1) {
3337                 DEBUG(0,("winbindd_validate_cache_child: bad freelist in cache %s\n",
3338                         cache_path));
3339                 v_status.bad_freelist = True;
3340                 v_status.success = False;
3341                 goto out;
3342         }
3343
3344         DEBUG(10,("winbindd_validate_cache_child: cache %s freelist has %d entries\n",
3345                 cache_path, num_entries));
3346
3347         /* Now traverse the cache to validate it. */
3348         num_entries = tdb_traverse(tdb, cache_traverse_validate_fn, (void *)&v_status);
3349         if (num_entries == -1 || !(v_status.success)) {
3350                 DEBUG(0,("winbindd_validate_cache_child: cache %s traverse failed\n",
3351                         cache_path));
3352                 if (!(v_status.success)) {
3353                         if (v_status.bad_entry) {
3354                                 DEBUGADD(0, (" -> bad entry found\n"));
3355                         }
3356                         if (v_status.unknown_key) {
3357                                 DEBUGADD(0, (" -> unknown key encountered\n"));
3358                         }
3359                 }
3360                 goto out;
3361         }
3362
3363         DEBUG(10,("winbindd_validate_cache_child: cache %s is good "
3364                 "with %d entries\n", cache_path, num_entries));
3365         ret = 0; /* Cache is good. */
3366
3367 out:
3368         if (tdb) {
3369                 if (ret == 0) {
3370                         tdb_close(tdb);
3371                 } 
3372                 else if (tfd != -1) {
3373                         close(tfd);
3374                 }
3375         }
3376
3377         DEBUG(10, ("winbindd_validate_cache_child: writing status to pipe\n"));
3378         write (pfd, (const char *)&v_status, sizeof(v_status));
3379         close(pfd);
3380
3381         return ret;
3382 }
3383
3384 int winbindd_validate_cache(void)
3385 {
3386         pid_t child_pid = -1;
3387         int child_status = 0;
3388         int wait_pid = 0;
3389         int ret = -1;
3390         int pipe_fds[2];
3391         struct validation_status v_status;
3392         int bytes_read = 0;
3393         const char *cache_path = lock_path("winbindd_cache.tdb");
3394         
3395         DEBUG(10, ("winbindd_validate_cache: replacing panic function\n"));
3396         smb_panic_fn = validate_panic;
3397
3398         /* fork and let the child do the validation. 
3399          * benefit: no need to twist signal handlers and panic functions.
3400          * just let the child panic. we catch the signal. 
3401          * communicate the extended status struct over a pipe. */
3402
3403         if (pipe(pipe_fds) != 0) {
3404                 DEBUG(0, ("winbindd_validate_cache: unable to create pipe, "
3405                           "error %s", strerror(errno)));
3406                 smb_panic("winbind_validate_cache: unable to create pipe.");
3407         }
3408
3409         DEBUG(10, ("winbindd_validate_cache: forking to let child do validation.\n"));
3410         child_pid = sys_fork();
3411         if (child_pid == 0) {
3412                 DEBUG(10, ("winbindd_validate_cache (validation child): created\n"));
3413                 close(pipe_fds[0]); /* close reading fd */
3414                 DEBUG(10, ("winbindd_validate_cache (validation child): "
3415                            "calling winbindd_validate_cache_child\n"));
3416                 exit(winbindd_validate_cache_child(cache_path, pipe_fds[1]));
3417         }
3418         else if (child_pid < 0) {
3419                 smb_panic("winbindd_validate_cache: fork for validation failed.");
3420         }
3421
3422         /* parent */
3423
3424         DEBUG(10, ("winbindd_validate_cache: fork succeeded, child PID = %d\n", 
3425                    child_pid));
3426         close(pipe_fds[1]); /* close writing fd */
3427
3428         v_status.success = True;
3429         v_status.bad_entry = False;
3430         v_status.unknown_key = False;
3431
3432         DEBUG(10, ("winbindd_validate_cache: reading from pipe.\n"));
3433         bytes_read = read(pipe_fds[0], (void *)&v_status, sizeof(v_status));
3434         close(pipe_fds[0]);
3435
3436         if (bytes_read != sizeof(v_status)) {
3437                 DEBUG(10, ("winbindd_validate_cache: read %d bytes from pipe "
3438                            "but expected %d", bytes_read, sizeof(v_status)));
3439                 DEBUGADD(10, (" -> assuming child crashed\n"));
3440                 v_status.success = False;
3441         }
3442         else {
3443                 DEBUG(10,    ("winbindd_validate_cache: read status from child\n"));
3444                 DEBUGADD(10, (" *  tdb error: %s\n", v_status.tdb_error ? "yes" : "no"));
3445                 DEBUGADD(10, (" *  bad freelist: %s\n", v_status.bad_freelist ? "yes" : "no"));
3446                 DEBUGADD(10, (" *  bad entry: %s\n", v_status.bad_entry ? "yes" : "no"));
3447                 DEBUGADD(10, (" *  unknown key: %s\n", v_status.unknown_key ? "yes" : "no"));
3448                 DEBUGADD(10, (" => overall success: %s\n", v_status.success ? "yes" : "no"));
3449         }
3450
3451         if (!v_status.success) {
3452                 DEBUG(10, ("winbindd_validate_cache: validation not successful.\n"));
3453                 DEBUGADD(10, ("removing tdb %s.\n", cache_path));
3454                 unlink(cache_path);
3455         }
3456
3457         DEBUG(10, ("winbindd_validate_cache: waiting for child to finish...\n"));
3458         while  ((wait_pid = sys_waitpid(child_pid, &child_status, 0)) < 0) {
3459                 if (errno == EINTR) {
3460                         DEBUG(10, ("winbindd_validate_cache: got signal during "
3461                                    "waitpid, retrying\n"));
3462                         errno = 0;
3463                         continue;
3464                 }
3465                 DEBUG(0, ("winbindd_validate_cache: waitpid failed with "
3466                           "errno %s\n", strerror(errno)));
3467                 smb_panic("winbindd_validate_cache: waitpid failed.");
3468         }
3469         if (wait_pid != child_pid) {
3470                 DEBUG(0, ("winbindd_validate_cache: waitpid returned pid %d, "
3471                           "but %d was expexted\n", wait_pid, child_pid));
3472                 smb_panic("winbindd_validate_cache: waitpid returned "
3473                              "unexpected PID.");
3474         }
3475
3476                 
3477         DEBUG(10, ("winbindd_validate_cache: validating child returned.\n"));
3478         if (WIFEXITED(child_status)) {
3479                 DEBUG(10, ("winbindd_validate_cache: child exited, code %d.\n",
3480                            WEXITSTATUS(child_status)));
3481                 ret = WEXITSTATUS(child_status);
3482         }
3483         if (WIFSIGNALED(child_status)) {
3484                 DEBUG(10, ("winbindd_validate_cache: child terminated "
3485                            "by signal %d%s\n",
3486                            WTERMSIG(child_status),
3487 #if defined(WCOREDUMP)
3488                            WCOREDUMP(child_status)?" (core dumped)":""
3489 #else
3490                            ""
3491 #endif
3492                            ));
3493                 ret = WTERMSIG(child_status);
3494         }
3495         if (WIFSTOPPED(child_status)) {
3496                 DEBUG(10, ("winbindd_validate_cache: child was stopped "
3497                            "by signal %d\n",
3498                            WSTOPSIG(child_status)));
3499                 ret = WSTOPSIG(child_status);
3500         }
3501
3502         DEBUG(10, ("winbindd_validate_cache: restoring panic function\n"));
3503         smb_panic_fn = smb_panic;
3504         return ret;
3505 }
3506
3507 /*********************************************************************
3508  ********************************************************************/
3509
3510 static BOOL add_wbdomain_to_tdc_array( struct winbindd_domain *new_dom,
3511                                        struct winbindd_tdc_domain **domains, 
3512                                        size_t *num_domains )
3513 {
3514         struct winbindd_tdc_domain *list = NULL;
3515         size_t idx;
3516         int i;
3517         BOOL set_only = False;  
3518         
3519         /* don't allow duplicates */
3520
3521         idx = *num_domains;
3522         list = *domains;
3523         
3524         for ( i=0; i< (*num_domains); i++ ) {
3525                 if ( strequal( new_dom->name, list[i].domain_name ) ) {
3526                         DEBUG(10,("add_wbdomain_to_tdc_array: Found existing record for %s\n",
3527                                   new_dom->name));
3528                         idx = i;
3529                         set_only = True;
3530                         
3531                         break;
3532                 }
3533         }
3534
3535         if ( !set_only ) {
3536                 if ( !*domains ) {
3537                         list = TALLOC_ARRAY( NULL, struct winbindd_tdc_domain, 1 );
3538                         idx = 0;
3539                 } else {
3540                         list = TALLOC_REALLOC_ARRAY( *domains, *domains, 
3541                                                      struct winbindd_tdc_domain,  
3542                                                      (*num_domains)+1);
3543                         idx = *num_domains;             
3544                 }
3545
3546                 ZERO_STRUCT( list[idx] );
3547         }
3548
3549         if ( !list )
3550                 return False;
3551
3552         list[idx].domain_name = talloc_strdup( list, new_dom->name );
3553         list[idx].dns_name = talloc_strdup( list, new_dom->alt_name );
3554
3555         if ( !is_null_sid( &new_dom->sid ) )
3556                 sid_copy( &list[idx].sid, &new_dom->sid );
3557
3558         if ( new_dom->domain_flags != 0x0 )
3559                 list[idx].trust_flags = new_dom->domain_flags;  
3560
3561         if ( new_dom->domain_type != 0x0 )
3562                 list[idx].trust_type = new_dom->domain_type;
3563
3564         if ( new_dom->domain_trust_attribs != 0x0 )
3565                 list[idx].trust_attribs = new_dom->domain_trust_attribs;
3566         
3567         if ( !set_only ) {
3568                 *domains = list;
3569                 *num_domains = idx + 1; 
3570         }
3571
3572         return True;    
3573 }
3574
3575 /*********************************************************************
3576  ********************************************************************/
3577
3578 static TDB_DATA make_tdc_key( const char *domain_name )
3579 {
3580         char *keystr = NULL;
3581         TDB_DATA key = { NULL, 0 };
3582         
3583         if ( !domain_name ) {
3584                 DEBUG(5,("make_tdc_key: Keyname workgroup is NULL!\n"));
3585                 return key;
3586         }
3587                
3588                 
3589         asprintf( &keystr, "TRUSTDOMCACHE/%s", domain_name );
3590         key.dptr = (unsigned char*)keystr;
3591         key.dsize = strlen_m(keystr) + 1;
3592         
3593         return key;     
3594 }
3595
3596 /*********************************************************************
3597  ********************************************************************/
3598
3599 static int pack_tdc_domains( struct winbindd_tdc_domain *domains, 
3600                              size_t num_domains,
3601                              unsigned char **buf )
3602 {
3603         unsigned char *buffer = NULL;
3604         int len = 0;
3605         int buflen = 0;
3606         int i = 0;
3607
3608         DEBUG(10,("pack_tdc_domains: Packing %d trusted domains\n",
3609                   (int)num_domains));
3610         
3611         buflen = 0;
3612         
3613  again: 
3614         len = 0;
3615         
3616         /* Store the number of array items first */
3617         len += tdb_pack( buffer+len, buflen-len, "d", 
3618                          num_domains );
3619
3620         /* now pack each domain trust record */
3621         for ( i=0; i<num_domains; i++ ) {
3622
3623                 if ( buflen > 0 ) {
3624                         DEBUG(10,("pack_tdc_domains: Packing domain %s (%s)\n",
3625                                   domains[i].domain_name,
3626                                   domains[i].dns_name ? domains[i].dns_name : "UNKNOWN" ));
3627                 }
3628                 
3629                 len += tdb_pack( buffer+len, buflen-len, "fffddd",
3630                                  domains[i].domain_name,
3631                                  domains[i].dns_name,
3632                                  sid_string_static(&domains[i].sid),
3633                                  domains[i].trust_flags,
3634                                  domains[i].trust_attribs,
3635                                  domains[i].trust_type );
3636         }
3637
3638         if ( buflen < len ) {
3639                 if ( (buffer = SMB_MALLOC_ARRAY(unsigned char, len)) == NULL ) {
3640                         DEBUG(0,("pack_tdc_domains: failed to alloc buffer!\n"));
3641                         buflen = -1;
3642                         goto done;
3643                 }
3644                 buflen = len;
3645                 goto again;
3646         }
3647
3648         *buf = buffer;  
3649         
3650  done:  
3651         return buflen;  
3652 }
3653
3654 /*********************************************************************
3655  ********************************************************************/
3656
3657 static size_t unpack_tdc_domains( unsigned char *buf, int buflen, 
3658                                   struct winbindd_tdc_domain **domains )
3659 {
3660         fstring domain_name, dns_name, sid_string;      
3661         uint32 type, attribs, flags;
3662         int num_domains;
3663         int len = 0;
3664         int i;
3665         struct winbindd_tdc_domain *list = NULL;
3666
3667         /* get the number of domains */
3668         len += tdb_unpack( buf+len, buflen-len, "d", &num_domains);
3669         if ( len == -1 ) {
3670                 DEBUG(5,("unpack_tdc_domains: Failed to unpack domain array\n"));               
3671                 return 0;
3672         }
3673
3674         list = TALLOC_ARRAY( NULL, struct winbindd_tdc_domain, num_domains );
3675         if ( !list ) {
3676                 DEBUG(0,("unpack_tdc_domains: Failed to talloc() domain list!\n"));
3677                 return 0;               
3678         }
3679         
3680         for ( i=0; i<num_domains; i++ ) {
3681                 len += tdb_unpack( buf+len, buflen-len, "fffddd",
3682                                    domain_name,
3683                                    dns_name,
3684                                    sid_string,
3685                                    &flags,
3686                                    &attribs,
3687                                    &type );
3688
3689                 if ( len == -1 ) {
3690                         DEBUG(5,("unpack_tdc_domains: Failed to unpack domain array\n"));
3691                         TALLOC_FREE( list );                    
3692                         return 0;
3693                 }
3694
3695                 DEBUG(11,("unpack_tdc_domains: Unpacking domain %s (%s) "
3696                           "SID %s, flags = 0x%x, attribs = 0x%x, type = 0x%x\n",
3697                           domain_name, dns_name, sid_string,
3698                           flags, attribs, type));
3699                 
3700                 list[i].domain_name = talloc_strdup( list, domain_name );
3701                 list[i].dns_name = talloc_strdup( list, dns_name );
3702                 if ( !string_to_sid( &(list[i].sid), sid_string ) ) {                   
3703                         DEBUG(10,("unpack_tdc_domains: no SID for domain %s\n",
3704                                   domain_name));
3705                 }
3706                 list[i].trust_flags = flags;
3707                 list[i].trust_attribs = attribs;
3708                 list[i].trust_type = type;
3709         }
3710
3711         *domains = list;
3712         
3713         return num_domains;
3714 }
3715
3716 /*********************************************************************
3717  ********************************************************************/
3718
3719 static BOOL wcache_tdc_store_list( struct winbindd_tdc_domain *domains, size_t num_domains )
3720 {
3721         TDB_DATA key = make_tdc_key( lp_workgroup() );   
3722         TDB_DATA data = { NULL, 0 };
3723         int ret;
3724         
3725         if ( !key.dptr )
3726                 return False;
3727         
3728         /* See if we were asked to delete the cache entry */
3729
3730         if ( !domains ) {
3731                 ret = tdb_delete( wcache->tdb, key );
3732                 goto done;
3733         }
3734         
3735         data.dsize = pack_tdc_domains( domains, num_domains, &data.dptr );
3736         
3737         if ( !data.dptr ) {
3738                 ret = -1;
3739                 goto done;
3740         }
3741                 
3742         ret = tdb_store( wcache->tdb, key, data, 0 );
3743
3744  done:
3745         SAFE_FREE( data.dptr );
3746         SAFE_FREE( key.dptr );
3747         
3748         return ( ret != -1 );   
3749 }
3750
3751 /*********************************************************************
3752  ********************************************************************/
3753
3754 BOOL wcache_tdc_fetch_list( struct winbindd_tdc_domain **domains, size_t *num_domains )
3755 {
3756         TDB_DATA key = make_tdc_key( lp_workgroup() );
3757         TDB_DATA data = { NULL, 0 };
3758
3759         *domains = NULL;        
3760         *num_domains = 0;       
3761
3762         if ( !key.dptr )
3763                 return False;
3764         
3765         data = tdb_fetch( wcache->tdb, key );
3766
3767         SAFE_FREE( key.dptr );
3768         
3769         if ( !data.dptr ) 
3770                 return False;
3771         
3772         *num_domains = unpack_tdc_domains( data.dptr, data.dsize, domains );
3773
3774         SAFE_FREE( data.dptr );
3775         
3776         if ( !*domains )
3777                 return False;
3778
3779         return True;    
3780 }
3781
3782 /*********************************************************************
3783  ********************************************************************/
3784
3785 BOOL wcache_tdc_add_domain( struct winbindd_domain *domain )
3786 {
3787         struct winbindd_tdc_domain *dom_list = NULL;
3788         size_t num_domains = 0;
3789         BOOL ret = False;       
3790
3791         DEBUG(10,("wcache_tdc_add_domain: Adding domain %s (%s), SID %s, "
3792                   "flags = 0x%x, attributes = 0x%x, type = 0x%x\n",
3793                   domain->name, domain->alt_name, 
3794                   sid_string_static(&domain->sid),
3795                   domain->domain_flags,
3796                   domain->domain_trust_attribs,
3797                   domain->domain_type));        
3798         
3799         if ( !init_wcache() ) {
3800                 return False;
3801         }
3802         
3803         /* fetch the list */
3804
3805         wcache_tdc_fetch_list( &dom_list, &num_domains );
3806         
3807         /* add the new domain */
3808
3809         if ( !add_wbdomain_to_tdc_array( domain, &dom_list, &num_domains ) ) {
3810                 goto done;              
3811         }       
3812
3813         /* pack the domain */
3814
3815         if ( !wcache_tdc_store_list( dom_list, num_domains ) ) {
3816                 goto done;              
3817         }
3818         
3819         /* Success */
3820
3821         ret = True;     
3822  done:
3823         TALLOC_FREE( dom_list );
3824         
3825         return ret;     
3826 }
3827
3828 /*********************************************************************
3829  ********************************************************************/
3830
3831 struct winbindd_tdc_domain * wcache_tdc_fetch_domain( TALLOC_CTX *ctx, const char *name )
3832 {
3833         struct winbindd_tdc_domain *dom_list = NULL;
3834         size_t num_domains = 0;
3835         int i;
3836         struct winbindd_tdc_domain *d = NULL;   
3837
3838         DEBUG(10,("wcache_tdc_fetch_domain: Searching for domain %s\n", name));
3839
3840         if ( !init_wcache() ) {
3841                 return False;
3842         }
3843         
3844         /* fetch the list */
3845
3846         wcache_tdc_fetch_list( &dom_list, &num_domains );
3847         
3848         for ( i=0; i<num_domains; i++ ) {
3849                 if ( strequal(name, dom_list[i].domain_name) ||
3850                      strequal(name, dom_list[i].dns_name) )
3851                 {
3852                         DEBUG(10,("wcache_tdc_fetch_domain: Found domain %s\n",
3853                                   name));
3854                         
3855                         d = TALLOC_P( ctx, struct winbindd_tdc_domain );
3856                         if ( !d )
3857                                 break;                  
3858                         
3859                         d->domain_name = talloc_strdup( d, dom_list[i].domain_name );
3860                         d->dns_name = talloc_strdup( d, dom_list[i].dns_name );
3861                         sid_copy( &d->sid, &dom_list[i].sid );
3862                         d->trust_flags   = dom_list[i].trust_flags;
3863                         d->trust_type    = dom_list[i].trust_type;
3864                         d->trust_attribs = dom_list[i].trust_attribs;
3865
3866                         break;
3867                 }
3868         }
3869
3870         TALLOC_FREE( dom_list );
3871         
3872         return d;       
3873 }
3874
3875
3876 /*********************************************************************
3877  ********************************************************************/
3878
3879 void wcache_tdc_clear( void )
3880 {
3881         if ( !init_wcache() )
3882                 return;
3883
3884         wcache_tdc_store_list( NULL, 0 );
3885         
3886         return; 
3887 }
3888
3889
3890 /*********************************************************************
3891  ********************************************************************/
3892
3893 static void wcache_save_user_pwinfo(struct winbindd_domain *domain, 
3894                                     NTSTATUS status,
3895                                     const DOM_SID *user_sid,
3896                                     const char *homedir,
3897                                     const char *shell,
3898                                     const char *gecos,
3899                                     uint32 gid)
3900 {
3901         struct cache_entry *centry;
3902
3903         if ( (centry = centry_start(domain, status)) == NULL )
3904                 return;
3905
3906         centry_put_string( centry, homedir );
3907         centry_put_string( centry, shell );
3908         centry_put_string( centry, gecos );
3909         centry_put_uint32( centry, gid );
3910         
3911         centry_end(centry, "NSS/PWINFO/%s", sid_string_static(user_sid) );
3912
3913         DEBUG(10,("wcache_save_user_pwinfo: %s\n", sid_string_static(user_sid) ));
3914
3915         centry_free(centry);
3916 }
3917
3918 NTSTATUS nss_get_info_cached( struct winbindd_domain *domain, 
3919                               const DOM_SID *user_sid,
3920                               TALLOC_CTX *ctx,
3921                               ADS_STRUCT *ads, LDAPMessage *msg,
3922                               char **homedir, char **shell, char **gecos,
3923                               gid_t *p_gid)
3924 {
3925         struct winbind_cache *cache = get_cache(domain);
3926         struct cache_entry *centry = NULL;
3927         NTSTATUS nt_status;
3928
3929         if (!cache->tdb)
3930                 goto do_query;
3931
3932         centry = wcache_fetch(cache, domain, "NSS/PWINFO/%s", sid_string_static(user_sid));     
3933         
3934         if (!centry)
3935                 goto do_query;
3936
3937         *homedir = centry_string( centry, ctx );
3938         *shell   = centry_string( centry, ctx );
3939         *gecos   = centry_string( centry, ctx );
3940         *p_gid   = centry_uint32( centry );     
3941
3942         centry_free(centry);
3943
3944         DEBUG(10,("nss_get_info_cached: [Cached] - user_sid %s\n",
3945                   sid_string_static(user_sid)));
3946
3947         return NT_STATUS_OK;
3948
3949 do_query:
3950         
3951         nt_status = nss_get_info( domain->name, user_sid, ctx, ads, msg, 
3952                                   homedir, shell, gecos, p_gid );
3953
3954         if ( NT_STATUS_IS_OK(nt_status) ) {
3955                 wcache_save_user_pwinfo( domain, nt_status, user_sid,
3956                                          *homedir, *shell, *gecos, *p_gid );
3957         }       
3958
3959         if ( NT_STATUS_EQUAL( nt_status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND ) ) {
3960                 DEBUG(5,("nss_get_info_cached: Setting domain %s offline\n",
3961                          domain->name ));
3962                 set_domain_offline( domain );
3963         }
3964
3965         return nt_status;       
3966 }
3967
3968
3969 /* the cache backend methods are exposed via this structure */
3970 struct winbindd_methods cache_methods = {
3971         True,
3972         query_user_list,
3973         enum_dom_groups,
3974         enum_local_groups,
3975         name_to_sid,
3976         sid_to_name,
3977         rids_to_names,
3978         query_user,
3979         lookup_usergroups,
3980         lookup_useraliases,
3981         lookup_groupmem,
3982         sequence_number,
3983         lockout_policy,
3984         password_policy,
3985         trusted_domains
3986 };