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