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