7ec8208f4b30d884538c695a03f52d2a94938347
[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, uname,
859                   sid_string_static(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;
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_string_static(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
995         if (!cache->tdb) {
996                 return NT_STATUS_INTERNAL_DB_ERROR;
997         }
998
999         if (is_null_sid(sid)) {
1000                 return NT_STATUS_INVALID_SID;
1001         }
1002
1003         if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
1004                 return NT_STATUS_INVALID_SID;
1005         }
1006
1007         /* Try and get a salted cred first. If we can't
1008            fall back to an unsalted cred. */
1009
1010         centry = wcache_fetch(cache, domain, "CRED/%s", sid_string_static(sid));
1011         if (!centry) {
1012                 DEBUG(10,("wcache_get_creds: entry for [CRED/%s] not found\n", 
1013                                 sid_string_static(sid)));
1014                 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1015         }
1016
1017         t = centry_time(centry);
1018
1019         /* In the salted case this isn't actually the nt_hash itself,
1020            but the MD5 of the salt + nt_hash. Let the caller
1021            sort this out. It can tell as we only return the cached_salt
1022            if we are returning a salted cred. */
1023
1024         *cached_nt_pass = (const uint8 *)centry_hash16(centry, mem_ctx);
1025         if (*cached_nt_pass == NULL) {
1026                 const char *sidstr = sid_string_static(sid);
1027
1028                 /* Bad (old) cred cache. Delete and pretend we
1029                    don't have it. */
1030                 DEBUG(0,("wcache_get_creds: bad entry for [CRED/%s] - deleting\n", 
1031                                 sidstr));
1032                 wcache_delete("CRED/%s", sidstr);
1033                 centry_free(centry);
1034                 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1035         }
1036
1037         /* We only have 17 bytes more data in the salted cred case. */
1038         if (centry->len - centry->ofs == 17) {
1039                 *cached_salt = (const uint8 *)centry_hash16(centry, mem_ctx);
1040         } else {
1041                 *cached_salt = NULL;
1042         }
1043
1044         dump_data_pw("cached_nt_pass", *cached_nt_pass, NT_HASH_LEN);
1045         if (*cached_salt) {
1046                 dump_data_pw("cached_salt", *cached_salt, NT_HASH_LEN);
1047         }
1048
1049         status = centry->status;
1050
1051         DEBUG(10,("wcache_get_creds: [Cached] - cached creds for user %s status: %s\n",
1052                 sid_string_static(sid), nt_errstr(status) ));
1053
1054         centry_free(centry);
1055         return status;
1056 }
1057
1058 /* Store creds for a SID - only writes out new salted ones. */
1059
1060 NTSTATUS wcache_save_creds(struct winbindd_domain *domain, 
1061                            TALLOC_CTX *mem_ctx, 
1062                            const DOM_SID *sid, 
1063                            const uint8 nt_pass[NT_HASH_LEN])
1064 {
1065         struct cache_entry *centry;
1066         fstring sid_string;
1067         uint32 rid;
1068         uint8 cred_salt[NT_HASH_LEN];
1069         uint8 salted_hash[NT_HASH_LEN];
1070
1071         if (is_null_sid(sid)) {
1072                 return NT_STATUS_INVALID_SID;
1073         }
1074
1075         if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
1076                 return NT_STATUS_INVALID_SID;
1077         }
1078
1079         centry = centry_start(domain, NT_STATUS_OK);
1080         if (!centry) {
1081                 return NT_STATUS_INTERNAL_DB_ERROR;
1082         }
1083
1084         dump_data_pw("nt_pass", nt_pass, NT_HASH_LEN);
1085
1086         centry_put_time(centry, time(NULL));
1087
1088         /* Create a salt and then salt the hash. */
1089         generate_random_buffer(cred_salt, NT_HASH_LEN);
1090         E_md5hash(cred_salt, nt_pass, salted_hash);
1091
1092         centry_put_hash16(centry, salted_hash);
1093         centry_put_hash16(centry, cred_salt);
1094         centry_end(centry, "CRED/%s", sid_to_string(sid_string, sid));
1095
1096         DEBUG(10,("wcache_save_creds: %s\n", sid_string));
1097
1098         centry_free(centry);
1099
1100         return NT_STATUS_OK;
1101 }
1102
1103
1104 /* Query display info. This is the basic user list fn */
1105 static NTSTATUS query_user_list(struct winbindd_domain *domain,
1106                                 TALLOC_CTX *mem_ctx,
1107                                 uint32 *num_entries, 
1108                                 WINBIND_USERINFO **info)
1109 {
1110         struct winbind_cache *cache = get_cache(domain);
1111         struct cache_entry *centry = NULL;
1112         NTSTATUS status;
1113         unsigned int i, retry;
1114
1115         if (!cache->tdb)
1116                 goto do_query;
1117
1118         centry = wcache_fetch(cache, domain, "UL/%s", domain->name);
1119         if (!centry)
1120                 goto do_query;
1121
1122         *num_entries = centry_uint32(centry);
1123         
1124         if (*num_entries == 0)
1125                 goto do_cached;
1126
1127         (*info) = TALLOC_ARRAY(mem_ctx, WINBIND_USERINFO, *num_entries);
1128         if (! (*info)) {
1129                 smb_panic_fn("query_user_list out of memory");
1130         }
1131         for (i=0; i<(*num_entries); i++) {
1132                 (*info)[i].acct_name = centry_string(centry, mem_ctx);
1133                 (*info)[i].full_name = centry_string(centry, mem_ctx);
1134                 (*info)[i].homedir = centry_string(centry, mem_ctx);
1135                 (*info)[i].shell = centry_string(centry, mem_ctx);
1136                 centry_sid(centry, mem_ctx, &(*info)[i].user_sid);
1137                 centry_sid(centry, mem_ctx, &(*info)[i].group_sid);
1138         }
1139
1140 do_cached:      
1141         status = centry->status;
1142
1143         DEBUG(10,("query_user_list: [Cached] - cached list for domain %s status: %s\n",
1144                 domain->name, nt_errstr(status) ));
1145
1146         centry_free(centry);
1147         return status;
1148
1149 do_query:
1150         *num_entries = 0;
1151         *info = NULL;
1152
1153         /* Return status value returned by seq number check */
1154
1155         if (!NT_STATUS_IS_OK(domain->last_status))
1156                 return domain->last_status;
1157
1158         /* Put the query_user_list() in a retry loop.  There appears to be
1159          * some bug either with Windows 2000 or Samba's handling of large
1160          * rpc replies.  This manifests itself as sudden disconnection
1161          * at a random point in the enumeration of a large (60k) user list.
1162          * The retry loop simply tries the operation again. )-:  It's not
1163          * pretty but an acceptable workaround until we work out what the
1164          * real problem is. */
1165
1166         retry = 0;
1167         do {
1168
1169                 DEBUG(10,("query_user_list: [Cached] - doing backend query for list for domain %s\n",
1170                         domain->name ));
1171
1172                 status = domain->backend->query_user_list(domain, mem_ctx, num_entries, info);
1173                 if (!NT_STATUS_IS_OK(status)) {
1174                         DEBUG(3, ("query_user_list: returned 0x%08x, "
1175                                   "retrying\n", NT_STATUS_V(status)));
1176                 }
1177                 if (NT_STATUS_EQUAL(status, NT_STATUS_UNSUCCESSFUL)) {
1178                         DEBUG(3, ("query_user_list: flushing "
1179                                   "connection cache\n"));
1180                         invalidate_cm_connection(&domain->conn);
1181                 }
1182
1183         } while (NT_STATUS_V(status) == NT_STATUS_V(NT_STATUS_UNSUCCESSFUL) && 
1184                  (retry++ < 5));
1185
1186         /* and save it */
1187         refresh_sequence_number(domain, False);
1188         centry = centry_start(domain, status);
1189         if (!centry)
1190                 goto skip_save;
1191         centry_put_uint32(centry, *num_entries);
1192         for (i=0; i<(*num_entries); i++) {
1193                 centry_put_string(centry, (*info)[i].acct_name);
1194                 centry_put_string(centry, (*info)[i].full_name);
1195                 centry_put_string(centry, (*info)[i].homedir);
1196                 centry_put_string(centry, (*info)[i].shell);
1197                 centry_put_sid(centry, &(*info)[i].user_sid);
1198                 centry_put_sid(centry, &(*info)[i].group_sid);
1199                 if (domain->backend && domain->backend->consistent) {
1200                         /* when the backend is consistent we can pre-prime some mappings */
1201                         wcache_save_name_to_sid(domain, NT_STATUS_OK, 
1202                                                 domain->name,
1203                                                 (*info)[i].acct_name, 
1204                                                 &(*info)[i].user_sid,
1205                                                 SID_NAME_USER);
1206                         wcache_save_sid_to_name(domain, NT_STATUS_OK, 
1207                                                 &(*info)[i].user_sid,
1208                                                 domain->name,
1209                                                 (*info)[i].acct_name, 
1210                                                 SID_NAME_USER);
1211                         wcache_save_user(domain, NT_STATUS_OK, &(*info)[i]);
1212                 }
1213         }       
1214         centry_end(centry, "UL/%s", domain->name);
1215         centry_free(centry);
1216
1217 skip_save:
1218         return status;
1219 }
1220
1221 /* list all domain groups */
1222 static NTSTATUS enum_dom_groups(struct winbindd_domain *domain,
1223                                 TALLOC_CTX *mem_ctx,
1224                                 uint32 *num_entries, 
1225                                 struct acct_info **info)
1226 {
1227         struct winbind_cache *cache = get_cache(domain);
1228         struct cache_entry *centry = NULL;
1229         NTSTATUS status;
1230         unsigned int i;
1231
1232         if (!cache->tdb)
1233                 goto do_query;
1234
1235         centry = wcache_fetch(cache, domain, "GL/%s/domain", domain->name);
1236         if (!centry)
1237                 goto do_query;
1238
1239         *num_entries = centry_uint32(centry);
1240         
1241         if (*num_entries == 0)
1242                 goto do_cached;
1243
1244         (*info) = TALLOC_ARRAY(mem_ctx, struct acct_info, *num_entries);
1245         if (! (*info)) {
1246                 smb_panic_fn("enum_dom_groups out of memory");
1247         }
1248         for (i=0; i<(*num_entries); i++) {
1249                 fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx));
1250                 fstrcpy((*info)[i].acct_desc, centry_string(centry, mem_ctx));
1251                 (*info)[i].rid = centry_uint32(centry);
1252         }
1253
1254 do_cached:      
1255         status = centry->status;
1256
1257         DEBUG(10,("enum_dom_groups: [Cached] - cached list for domain %s status: %s\n",
1258                 domain->name, nt_errstr(status) ));
1259
1260         centry_free(centry);
1261         return status;
1262
1263 do_query:
1264         *num_entries = 0;
1265         *info = NULL;
1266
1267         /* Return status value returned by seq number check */
1268
1269         if (!NT_STATUS_IS_OK(domain->last_status))
1270                 return domain->last_status;
1271
1272         DEBUG(10,("enum_dom_groups: [Cached] - doing backend query for list for domain %s\n",
1273                 domain->name ));
1274
1275         status = domain->backend->enum_dom_groups(domain, mem_ctx, num_entries, info);
1276
1277         /* and save it */
1278         refresh_sequence_number(domain, False);
1279         centry = centry_start(domain, status);
1280         if (!centry)
1281                 goto skip_save;
1282         centry_put_uint32(centry, *num_entries);
1283         for (i=0; i<(*num_entries); i++) {
1284                 centry_put_string(centry, (*info)[i].acct_name);
1285                 centry_put_string(centry, (*info)[i].acct_desc);
1286                 centry_put_uint32(centry, (*info)[i].rid);
1287         }       
1288         centry_end(centry, "GL/%s/domain", domain->name);
1289         centry_free(centry);
1290
1291 skip_save:
1292         return status;
1293 }
1294
1295 /* list all domain groups */
1296 static NTSTATUS enum_local_groups(struct winbindd_domain *domain,
1297                                 TALLOC_CTX *mem_ctx,
1298                                 uint32 *num_entries, 
1299                                 struct acct_info **info)
1300 {
1301         struct winbind_cache *cache = get_cache(domain);
1302         struct cache_entry *centry = NULL;
1303         NTSTATUS status;
1304         unsigned int i;
1305
1306         if (!cache->tdb)
1307                 goto do_query;
1308
1309         centry = wcache_fetch(cache, domain, "GL/%s/local", domain->name);
1310         if (!centry)
1311                 goto do_query;
1312
1313         *num_entries = centry_uint32(centry);
1314         
1315         if (*num_entries == 0)
1316                 goto do_cached;
1317
1318         (*info) = TALLOC_ARRAY(mem_ctx, struct acct_info, *num_entries);
1319         if (! (*info)) {
1320                 smb_panic_fn("enum_dom_groups out of memory");
1321         }
1322         for (i=0; i<(*num_entries); i++) {
1323                 fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx));
1324                 fstrcpy((*info)[i].acct_desc, centry_string(centry, mem_ctx));
1325                 (*info)[i].rid = centry_uint32(centry);
1326         }
1327
1328 do_cached:      
1329
1330         /* If we are returning cached data and the domain controller
1331            is down then we don't know whether the data is up to date
1332            or not.  Return NT_STATUS_MORE_PROCESSING_REQUIRED to
1333            indicate this. */
1334
1335         if (wcache_server_down(domain)) {
1336                 DEBUG(10, ("enum_local_groups: returning cached user list and server was down\n"));
1337                 status = NT_STATUS_MORE_PROCESSING_REQUIRED;
1338         } else
1339                 status = centry->status;
1340
1341         DEBUG(10,("enum_local_groups: [Cached] - cached list for domain %s status: %s\n",
1342                 domain->name, nt_errstr(status) ));
1343
1344         centry_free(centry);
1345         return status;
1346
1347 do_query:
1348         *num_entries = 0;
1349         *info = NULL;
1350
1351         /* Return status value returned by seq number check */
1352
1353         if (!NT_STATUS_IS_OK(domain->last_status))
1354                 return domain->last_status;
1355
1356         DEBUG(10,("enum_local_groups: [Cached] - doing backend query for list for domain %s\n",
1357                 domain->name ));
1358
1359         status = domain->backend->enum_local_groups(domain, mem_ctx, num_entries, info);
1360
1361         /* and save it */
1362         refresh_sequence_number(domain, False);
1363         centry = centry_start(domain, status);
1364         if (!centry)
1365                 goto skip_save;
1366         centry_put_uint32(centry, *num_entries);
1367         for (i=0; i<(*num_entries); i++) {
1368                 centry_put_string(centry, (*info)[i].acct_name);
1369                 centry_put_string(centry, (*info)[i].acct_desc);
1370                 centry_put_uint32(centry, (*info)[i].rid);
1371         }
1372         centry_end(centry, "GL/%s/local", domain->name);
1373         centry_free(centry);
1374
1375 skip_save:
1376         return status;
1377 }
1378
1379 /* convert a single name to a sid in a domain */
1380 static NTSTATUS name_to_sid(struct winbindd_domain *domain,
1381                             TALLOC_CTX *mem_ctx,
1382                             enum winbindd_cmd orig_cmd,
1383                             const char *domain_name,
1384                             const char *name,
1385                             DOM_SID *sid,
1386                             enum lsa_SidType *type)
1387 {
1388         struct winbind_cache *cache = get_cache(domain);
1389         struct cache_entry *centry = NULL;
1390         NTSTATUS status;
1391         fstring uname;
1392
1393         if (!cache->tdb)
1394                 goto do_query;
1395
1396         fstrcpy(uname, name);
1397         strupper_m(uname);
1398         centry = wcache_fetch(cache, domain, "NS/%s/%s", domain_name, uname);
1399         if (!centry)
1400                 goto do_query;
1401
1402         status = centry->status;
1403         if (NT_STATUS_IS_OK(status)) {
1404                 *type = (enum lsa_SidType)centry_uint32(centry);
1405                 centry_sid(centry, mem_ctx, sid);
1406         }
1407
1408         DEBUG(10,("name_to_sid: [Cached] - cached name for domain %s status: %s\n",
1409                 domain->name, nt_errstr(status) ));
1410
1411         centry_free(centry);
1412         return status;
1413
1414 do_query:
1415         ZERO_STRUCTP(sid);
1416
1417         /* If the seq number check indicated that there is a problem
1418          * with this DC, then return that status... except for
1419          * access_denied.  This is special because the dc may be in
1420          * "restrict anonymous = 1" mode, in which case it will deny
1421          * most unauthenticated operations, but *will* allow the LSA
1422          * name-to-sid that we try as a fallback. */
1423
1424         if (!(NT_STATUS_IS_OK(domain->last_status)
1425               || NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)))
1426                 return domain->last_status;
1427
1428         DEBUG(10,("name_to_sid: [Cached] - doing backend query for name for domain %s\n",
1429                 domain->name ));
1430
1431         status = domain->backend->name_to_sid(domain, mem_ctx, orig_cmd, 
1432                                               domain_name, name, sid, type);
1433
1434         /* and save it */
1435         refresh_sequence_number(domain, False);
1436
1437         if (domain->online &&
1438             (NT_STATUS_IS_OK(status) || NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED))) {
1439                 wcache_save_name_to_sid(domain, status, domain_name, name, sid, *type);
1440
1441                 /* Only save the reverse mapping if this was not a UPN */
1442                 if (!strchr(name, '@')) {
1443                         strupper_m(CONST_DISCARD(char *,domain_name));
1444                         strlower_m(CONST_DISCARD(char *,name));
1445                         wcache_save_sid_to_name(domain, status, sid, domain_name, name, *type);
1446                 }
1447         }
1448         
1449         return status;
1450 }
1451
1452 /* convert a sid to a user or group name. The sid is guaranteed to be in the domain
1453    given */
1454 static NTSTATUS sid_to_name(struct winbindd_domain *domain,
1455                             TALLOC_CTX *mem_ctx,
1456                             const DOM_SID *sid,
1457                             char **domain_name,
1458                             char **name,
1459                             enum lsa_SidType *type)
1460 {
1461         struct winbind_cache *cache = get_cache(domain);
1462         struct cache_entry *centry = NULL;
1463         NTSTATUS status;
1464         fstring sid_string;
1465
1466         if (!cache->tdb)
1467                 goto do_query;
1468
1469         centry = wcache_fetch(cache, domain, "SN/%s", sid_to_string(sid_string, sid));
1470         if (!centry)
1471                 goto do_query;
1472
1473         status = centry->status;
1474         if (NT_STATUS_IS_OK(status)) {
1475                 *type = (enum lsa_SidType)centry_uint32(centry);
1476                 *domain_name = centry_string(centry, mem_ctx);
1477                 *name = centry_string(centry, mem_ctx);
1478         }
1479
1480         DEBUG(10,("sid_to_name: [Cached] - cached name for domain %s status: %s\n",
1481                 domain->name, nt_errstr(status) ));
1482
1483         centry_free(centry);
1484         return status;
1485
1486 do_query:
1487         *name = NULL;
1488         *domain_name = NULL;
1489
1490         /* If the seq number check indicated that there is a problem
1491          * with this DC, then return that status... except for
1492          * access_denied.  This is special because the dc may be in
1493          * "restrict anonymous = 1" mode, in which case it will deny
1494          * most unauthenticated operations, but *will* allow the LSA
1495          * sid-to-name that we try as a fallback. */
1496
1497         if (!(NT_STATUS_IS_OK(domain->last_status)
1498               || NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)))
1499                 return domain->last_status;
1500
1501         DEBUG(10,("sid_to_name: [Cached] - doing backend query for name for domain %s\n",
1502                 domain->name ));
1503
1504         status = domain->backend->sid_to_name(domain, mem_ctx, sid, domain_name, name, type);
1505
1506         /* and save it */
1507         refresh_sequence_number(domain, False);
1508         wcache_save_sid_to_name(domain, status, sid, *domain_name, *name, *type);
1509
1510         /* We can't save the name to sid mapping here, as with sid history a
1511          * later name2sid would give the wrong sid. */
1512
1513         return status;
1514 }
1515
1516 static NTSTATUS rids_to_names(struct winbindd_domain *domain,
1517                               TALLOC_CTX *mem_ctx,
1518                               const DOM_SID *domain_sid,
1519                               uint32 *rids,
1520                               size_t num_rids,
1521                               char **domain_name,
1522                               char ***names,
1523                               enum lsa_SidType **types)
1524 {
1525         struct winbind_cache *cache = get_cache(domain);
1526         size_t i;
1527         NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
1528         bool have_mapped;
1529         bool have_unmapped;
1530
1531         *domain_name = NULL;
1532         *names = NULL;
1533         *types = NULL;
1534
1535         if (!cache->tdb) {
1536                 goto do_query;
1537         }
1538
1539         if (num_rids == 0) {
1540                 return NT_STATUS_OK;
1541         }
1542
1543         *names = TALLOC_ARRAY(mem_ctx, char *, num_rids);
1544         *types = TALLOC_ARRAY(mem_ctx, enum lsa_SidType, num_rids);
1545
1546         if ((*names == NULL) || (*types == NULL)) {
1547                 result = NT_STATUS_NO_MEMORY;
1548                 goto error;
1549         }
1550
1551         have_mapped = have_unmapped = False;
1552
1553         for (i=0; i<num_rids; i++) {
1554                 DOM_SID sid;
1555                 struct cache_entry *centry;
1556
1557                 if (!sid_compose(&sid, domain_sid, rids[i])) {
1558                         result = NT_STATUS_INTERNAL_ERROR;
1559                         goto error;
1560                 }
1561
1562                 centry = wcache_fetch(cache, domain, "SN/%s",
1563                                       sid_string_static(&sid));
1564                 if (!centry) {
1565                         goto do_query;
1566                 }
1567
1568                 (*types)[i] = SID_NAME_UNKNOWN;
1569                 (*names)[i] = talloc_strdup(*names, "");
1570
1571                 if (NT_STATUS_IS_OK(centry->status)) {
1572                         char *dom;
1573                         have_mapped = True;
1574                         (*types)[i] = (enum lsa_SidType)centry_uint32(centry);
1575
1576                         dom = centry_string(centry, mem_ctx);
1577                         if (*domain_name == NULL) {
1578                                 *domain_name = dom;
1579                         } else {
1580                                 talloc_free(dom);
1581                         }
1582
1583                         (*names)[i] = centry_string(centry, *names);
1584
1585                 } else if (NT_STATUS_EQUAL(centry->status, NT_STATUS_NONE_MAPPED)) {
1586                         have_unmapped = True;
1587
1588                 } else {
1589                         /* something's definitely wrong */
1590                         result = centry->status;
1591                         goto error;
1592                 }
1593
1594                 centry_free(centry);
1595         }
1596
1597         if (!have_mapped) {
1598                 return NT_STATUS_NONE_MAPPED;
1599         }
1600         if (!have_unmapped) {
1601                 return NT_STATUS_OK;
1602         }
1603         return STATUS_SOME_UNMAPPED;
1604
1605  do_query:
1606
1607         TALLOC_FREE(*names);
1608         TALLOC_FREE(*types);
1609
1610         result = domain->backend->rids_to_names(domain, mem_ctx, domain_sid,
1611                                                 rids, num_rids, domain_name,
1612                                                 names, types);
1613
1614         /*
1615           None of the queried rids has been found so save all negative entries
1616         */
1617         if (NT_STATUS_EQUAL(result, NT_STATUS_NONE_MAPPED)) {
1618                 for (i = 0; i < num_rids; i++) {
1619                         DOM_SID sid;
1620                         const char *name = "";
1621                         const enum lsa_SidType type = SID_NAME_UNKNOWN;
1622                         NTSTATUS status = NT_STATUS_NONE_MAPPED;
1623                         
1624                         if (!sid_compose(&sid, domain_sid, rids[i])) {
1625                                 return NT_STATUS_INTERNAL_ERROR;
1626                         }
1627
1628                         wcache_save_sid_to_name(domain, status, &sid, *domain_name,
1629                                                 name, type);
1630                 }
1631
1632                 return result;
1633         }
1634
1635         /*
1636           Some or all of the queried rids have been found.
1637         */
1638         if (!NT_STATUS_IS_OK(result) &&
1639             !NT_STATUS_EQUAL(result, STATUS_SOME_UNMAPPED)) {
1640                 return result;
1641         }
1642
1643         refresh_sequence_number(domain, False);
1644
1645         for (i=0; i<num_rids; i++) {
1646                 DOM_SID sid;
1647                 NTSTATUS status;
1648
1649                 if (!sid_compose(&sid, domain_sid, rids[i])) {
1650                         result = NT_STATUS_INTERNAL_ERROR;
1651                         goto error;
1652                 }
1653
1654                 status = (*types)[i] == SID_NAME_UNKNOWN ?
1655                         NT_STATUS_NONE_MAPPED : NT_STATUS_OK;
1656
1657                 wcache_save_sid_to_name(domain, status, &sid, *domain_name,
1658                                         (*names)[i], (*types)[i]);
1659         }
1660
1661         return result;
1662
1663  error:
1664         
1665         TALLOC_FREE(*names);
1666         TALLOC_FREE(*types);
1667         return result;
1668 }
1669
1670 /* Lookup user information from a rid */
1671 static NTSTATUS query_user(struct winbindd_domain *domain, 
1672                            TALLOC_CTX *mem_ctx, 
1673                            const DOM_SID *user_sid, 
1674                            WINBIND_USERINFO *info)
1675 {
1676         struct winbind_cache *cache = get_cache(domain);
1677         struct cache_entry *centry = NULL;
1678         NTSTATUS status;
1679
1680         if (!cache->tdb)
1681                 goto do_query;
1682
1683         centry = wcache_fetch(cache, domain, "U/%s", sid_string_static(user_sid));
1684         
1685         /* If we have an access denied cache entry and a cached info3 in the
1686            samlogon cache then do a query.  This will force the rpc back end
1687            to return the info3 data. */
1688
1689         if (NT_STATUS_V(domain->last_status) == NT_STATUS_V(NT_STATUS_ACCESS_DENIED) &&
1690             netsamlogon_cache_have(user_sid)) {
1691                 DEBUG(10, ("query_user: cached access denied and have cached info3\n"));
1692                 domain->last_status = NT_STATUS_OK;
1693                 centry_free(centry);
1694                 goto do_query;
1695         }
1696         
1697         if (!centry)
1698                 goto do_query;
1699         
1700         /* if status is not ok then this is a negative hit
1701            and the rest of the data doesn't matter */
1702         status = centry->status;
1703         if (NT_STATUS_IS_OK(status)) {
1704                 info->acct_name = centry_string(centry, mem_ctx);
1705                 info->full_name = centry_string(centry, mem_ctx);
1706                 info->homedir = centry_string(centry, mem_ctx);
1707                 info->shell = centry_string(centry, mem_ctx);
1708                 info->primary_gid = centry_uint32(centry);
1709                 centry_sid(centry, mem_ctx, &info->user_sid);
1710                 centry_sid(centry, mem_ctx, &info->group_sid);
1711         }
1712
1713         DEBUG(10,("query_user: [Cached] - cached info for domain %s status: %s\n",
1714                 domain->name, nt_errstr(status) ));
1715
1716         centry_free(centry);
1717         return status;
1718
1719 do_query:
1720         ZERO_STRUCTP(info);
1721
1722         /* Return status value returned by seq number check */
1723
1724         if (!NT_STATUS_IS_OK(domain->last_status))
1725                 return domain->last_status;
1726         
1727         DEBUG(10,("query_user: [Cached] - doing backend query for info for domain %s\n",
1728                 domain->name ));
1729
1730         status = domain->backend->query_user(domain, mem_ctx, user_sid, info);
1731
1732         /* and save it */
1733         refresh_sequence_number(domain, False);
1734         wcache_save_user(domain, status, info);
1735
1736         return status;
1737 }
1738
1739
1740 /* Lookup groups a user is a member of. */
1741 static NTSTATUS lookup_usergroups(struct winbindd_domain *domain,
1742                                   TALLOC_CTX *mem_ctx,
1743                                   const DOM_SID *user_sid, 
1744                                   uint32 *num_groups, DOM_SID **user_gids)
1745 {
1746         struct winbind_cache *cache = get_cache(domain);
1747         struct cache_entry *centry = NULL;
1748         NTSTATUS status;
1749         unsigned int i;
1750         fstring sid_string;
1751
1752         if (!cache->tdb)
1753                 goto do_query;
1754
1755         centry = wcache_fetch(cache, domain, "UG/%s", sid_to_string(sid_string, user_sid));
1756         
1757         /* If we have an access denied cache entry and a cached info3 in the
1758            samlogon cache then do a query.  This will force the rpc back end
1759            to return the info3 data. */
1760
1761         if (NT_STATUS_V(domain->last_status) == NT_STATUS_V(NT_STATUS_ACCESS_DENIED) &&
1762             netsamlogon_cache_have(user_sid)) {
1763                 DEBUG(10, ("lookup_usergroups: cached access denied and have cached info3\n"));
1764                 domain->last_status = NT_STATUS_OK;
1765                 centry_free(centry);
1766                 goto do_query;
1767         }
1768         
1769         if (!centry)
1770                 goto do_query;
1771
1772         *num_groups = centry_uint32(centry);
1773         
1774         if (*num_groups == 0)
1775                 goto do_cached;
1776
1777         (*user_gids) = TALLOC_ARRAY(mem_ctx, DOM_SID, *num_groups);
1778         if (! (*user_gids)) {
1779                 smb_panic_fn("lookup_usergroups out of memory");
1780         }
1781         for (i=0; i<(*num_groups); i++) {
1782                 centry_sid(centry, mem_ctx, &(*user_gids)[i]);
1783         }
1784
1785 do_cached:      
1786         status = centry->status;
1787
1788         DEBUG(10,("lookup_usergroups: [Cached] - cached info for domain %s status: %s\n",
1789                 domain->name, nt_errstr(status) ));
1790
1791         centry_free(centry);
1792         return status;
1793
1794 do_query:
1795         (*num_groups) = 0;
1796         (*user_gids) = NULL;
1797
1798         /* Return status value returned by seq number check */
1799
1800         if (!NT_STATUS_IS_OK(domain->last_status))
1801                 return domain->last_status;
1802
1803         DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info for domain %s\n",
1804                 domain->name ));
1805
1806         status = domain->backend->lookup_usergroups(domain, mem_ctx, user_sid, num_groups, user_gids);
1807
1808         if ( NT_STATUS_EQUAL(status, NT_STATUS_SYNCHRONIZATION_REQUIRED) )
1809                 goto skip_save;
1810         
1811         /* and save it */
1812         refresh_sequence_number(domain, False);
1813         centry = centry_start(domain, status);
1814         if (!centry)
1815                 goto skip_save;
1816
1817         centry_put_uint32(centry, *num_groups);
1818         for (i=0; i<(*num_groups); i++) {
1819                 centry_put_sid(centry, &(*user_gids)[i]);
1820         }       
1821
1822         centry_end(centry, "UG/%s", sid_to_string(sid_string, user_sid));
1823         centry_free(centry);
1824
1825 skip_save:
1826         return status;
1827 }
1828
1829 static NTSTATUS lookup_useraliases(struct winbindd_domain *domain,
1830                                    TALLOC_CTX *mem_ctx,
1831                                    uint32 num_sids, const DOM_SID *sids,
1832                                    uint32 *num_aliases, uint32 **alias_rids)
1833 {
1834         struct winbind_cache *cache = get_cache(domain);
1835         struct cache_entry *centry = NULL;
1836         NTSTATUS status;
1837         char *sidlist = talloc_strdup(mem_ctx, "");
1838         int i;
1839
1840         if (!cache->tdb)
1841                 goto do_query;
1842
1843         if (num_sids == 0) {
1844                 *num_aliases = 0;
1845                 *alias_rids = NULL;
1846                 return NT_STATUS_OK;
1847         }
1848
1849         /* We need to cache indexed by the whole list of SIDs, the aliases
1850          * resulting might come from any of the SIDs. */
1851
1852         for (i=0; i<num_sids; i++) {
1853                 sidlist = talloc_asprintf(mem_ctx, "%s/%s", sidlist,
1854                                           sid_string_static(&sids[i]));
1855                 if (sidlist == NULL)
1856                         return NT_STATUS_NO_MEMORY;
1857         }
1858
1859         centry = wcache_fetch(cache, domain, "UA%s", sidlist);
1860
1861         if (!centry)
1862                 goto do_query;
1863
1864         *num_aliases = centry_uint32(centry);
1865         *alias_rids = NULL;
1866
1867         if (*num_aliases) {
1868                 (*alias_rids) = TALLOC_ARRAY(mem_ctx, uint32, *num_aliases);
1869
1870                 if ((*alias_rids) == NULL) {
1871                         centry_free(centry);
1872                         return NT_STATUS_NO_MEMORY;
1873                 }
1874         } else {
1875                 (*alias_rids) = NULL;
1876         }
1877
1878         for (i=0; i<(*num_aliases); i++)
1879                 (*alias_rids)[i] = centry_uint32(centry);
1880
1881         status = centry->status;
1882
1883         DEBUG(10,("lookup_useraliases: [Cached] - cached info for domain: %s "
1884                   "status %s\n", domain->name, nt_errstr(status)));
1885
1886         centry_free(centry);
1887         return status;
1888
1889  do_query:
1890         (*num_aliases) = 0;
1891         (*alias_rids) = NULL;
1892
1893         if (!NT_STATUS_IS_OK(domain->last_status))
1894                 return domain->last_status;
1895
1896         DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info "
1897                   "for domain %s\n", domain->name ));
1898
1899         status = domain->backend->lookup_useraliases(domain, mem_ctx,
1900                                                      num_sids, sids,
1901                                                      num_aliases, alias_rids);
1902
1903         /* and save it */
1904         refresh_sequence_number(domain, False);
1905         centry = centry_start(domain, status);
1906         if (!centry)
1907                 goto skip_save;
1908         centry_put_uint32(centry, *num_aliases);
1909         for (i=0; i<(*num_aliases); i++)
1910                 centry_put_uint32(centry, (*alias_rids)[i]);
1911         centry_end(centry, "UA%s", sidlist);
1912         centry_free(centry);
1913
1914  skip_save:
1915         return status;
1916 }
1917
1918
1919 static NTSTATUS lookup_groupmem(struct winbindd_domain *domain,
1920                                 TALLOC_CTX *mem_ctx,
1921                                 const DOM_SID *group_sid, uint32 *num_names, 
1922                                 DOM_SID **sid_mem, char ***names, 
1923                                 uint32 **name_types)
1924 {
1925         struct winbind_cache *cache = get_cache(domain);
1926         struct cache_entry *centry = NULL;
1927         NTSTATUS status;
1928         unsigned int i;
1929         fstring sid_string;
1930
1931         if (!cache->tdb)
1932                 goto do_query;
1933
1934         centry = wcache_fetch(cache, domain, "GM/%s", sid_to_string(sid_string, group_sid));
1935         if (!centry)
1936                 goto do_query;
1937
1938         *num_names = centry_uint32(centry);
1939         
1940         if (*num_names == 0)
1941                 goto do_cached;
1942
1943         (*sid_mem) = TALLOC_ARRAY(mem_ctx, DOM_SID, *num_names);
1944         (*names) = TALLOC_ARRAY(mem_ctx, char *, *num_names);
1945         (*name_types) = TALLOC_ARRAY(mem_ctx, uint32, *num_names);
1946
1947         if (! (*sid_mem) || ! (*names) || ! (*name_types)) {
1948                 smb_panic_fn("lookup_groupmem out of memory");
1949         }
1950
1951         for (i=0; i<(*num_names); i++) {
1952                 centry_sid(centry, mem_ctx, &(*sid_mem)[i]);
1953                 (*names)[i] = centry_string(centry, mem_ctx);
1954                 (*name_types)[i] = centry_uint32(centry);
1955         }
1956
1957 do_cached:      
1958         status = centry->status;
1959
1960         DEBUG(10,("lookup_groupmem: [Cached] - cached info for domain %s status: %s\n",
1961                 domain->name, nt_errstr(status)));
1962
1963         centry_free(centry);
1964         return status;
1965
1966 do_query:
1967         (*num_names) = 0;
1968         (*sid_mem) = NULL;
1969         (*names) = NULL;
1970         (*name_types) = NULL;
1971         
1972         /* Return status value returned by seq number check */
1973
1974         if (!NT_STATUS_IS_OK(domain->last_status))
1975                 return domain->last_status;
1976
1977         DEBUG(10,("lookup_groupmem: [Cached] - doing backend query for info for domain %s\n",
1978                 domain->name ));
1979
1980         status = domain->backend->lookup_groupmem(domain, mem_ctx, group_sid, num_names, 
1981                                                   sid_mem, names, name_types);
1982
1983         /* and save it */
1984         refresh_sequence_number(domain, False);
1985         centry = centry_start(domain, status);
1986         if (!centry)
1987                 goto skip_save;
1988         centry_put_uint32(centry, *num_names);
1989         for (i=0; i<(*num_names); i++) {
1990                 centry_put_sid(centry, &(*sid_mem)[i]);
1991                 centry_put_string(centry, (*names)[i]);
1992                 centry_put_uint32(centry, (*name_types)[i]);
1993         }       
1994         centry_end(centry, "GM/%s", sid_to_string(sid_string, group_sid));
1995         centry_free(centry);
1996
1997 skip_save:
1998         return status;
1999 }
2000
2001 /* find the sequence number for a domain */
2002 static NTSTATUS sequence_number(struct winbindd_domain *domain, uint32 *seq)
2003 {
2004         refresh_sequence_number(domain, False);
2005
2006         *seq = domain->sequence_number;
2007
2008         return NT_STATUS_OK;
2009 }
2010
2011 /* enumerate trusted domains 
2012  * (we need to have the list of trustdoms in the cache when we go offline) -
2013  * Guenther */
2014 static NTSTATUS trusted_domains(struct winbindd_domain *domain,
2015                                 TALLOC_CTX *mem_ctx,
2016                                 uint32 *num_domains,
2017                                 char ***names,
2018                                 char ***alt_names,
2019                                 DOM_SID **dom_sids)
2020 {
2021         struct winbind_cache *cache = get_cache(domain);
2022         struct cache_entry *centry = NULL;
2023         NTSTATUS status;
2024         int i;
2025  
2026         if (!cache->tdb)
2027                 goto do_query;
2028  
2029         centry = wcache_fetch(cache, domain, "TRUSTDOMS/%s", domain->name);
2030         
2031         if (!centry) {
2032                 goto do_query;
2033         }
2034  
2035         *num_domains = centry_uint32(centry);
2036         
2037         if (*num_domains) {
2038                 (*names)        = TALLOC_ARRAY(mem_ctx, char *, *num_domains);
2039                 (*alt_names)    = TALLOC_ARRAY(mem_ctx, char *, *num_domains);
2040                 (*dom_sids)     = TALLOC_ARRAY(mem_ctx, DOM_SID, *num_domains);
2041  
2042                 if (! (*dom_sids) || ! (*names) || ! (*alt_names)) {
2043                         smb_panic_fn("trusted_domains out of memory");
2044                 }
2045         } else {
2046                 (*names) = NULL;
2047                 (*alt_names) = NULL;
2048                 (*dom_sids) = NULL;
2049         }
2050  
2051         for (i=0; i<(*num_domains); i++) {
2052                 (*names)[i] = centry_string(centry, mem_ctx);
2053                 (*alt_names)[i] = centry_string(centry, mem_ctx);
2054                 centry_sid(centry, mem_ctx, &(*dom_sids)[i]);
2055         }
2056
2057         status = centry->status;
2058  
2059         DEBUG(10,("trusted_domains: [Cached] - cached info for domain %s (%d trusts) status: %s\n",
2060                 domain->name, *num_domains, nt_errstr(status) ));
2061  
2062         centry_free(centry);
2063         return status;
2064  
2065 do_query:
2066         (*num_domains) = 0;
2067         (*dom_sids) = NULL;
2068         (*names) = NULL;
2069         (*alt_names) = NULL;
2070  
2071         /* Return status value returned by seq number check */
2072
2073         if (!NT_STATUS_IS_OK(domain->last_status))
2074                 return domain->last_status;
2075         
2076         DEBUG(10,("trusted_domains: [Cached] - doing backend query for info for domain %s\n",
2077                 domain->name ));
2078  
2079         status = domain->backend->trusted_domains(domain, mem_ctx, num_domains,
2080                                                 names, alt_names, dom_sids);
2081
2082         /* no trusts gives NT_STATUS_NO_MORE_ENTRIES resetting to NT_STATUS_OK
2083          * so that the generic centry handling still applies correctly -
2084          * Guenther*/
2085
2086         if (!NT_STATUS_IS_ERR(status)) {
2087                 status = NT_STATUS_OK;
2088         }
2089
2090
2091 #if 0    /* Disabled as we want the trust dom list to be managed by
2092             the main parent and always to make the query.  --jerry */
2093
2094         /* and save it */
2095         refresh_sequence_number(domain, False);
2096  
2097         centry = centry_start(domain, status);
2098         if (!centry)
2099                 goto skip_save;
2100
2101         centry_put_uint32(centry, *num_domains);
2102
2103         for (i=0; i<(*num_domains); i++) {
2104                 centry_put_string(centry, (*names)[i]);
2105                 centry_put_string(centry, (*alt_names)[i]);
2106                 centry_put_sid(centry, &(*dom_sids)[i]);
2107         }
2108         
2109         centry_end(centry, "TRUSTDOMS/%s", domain->name);
2110  
2111         centry_free(centry);
2112  
2113 skip_save:
2114 #endif
2115
2116         return status;
2117 }       
2118
2119 /* get lockout policy */
2120 static NTSTATUS lockout_policy(struct winbindd_domain *domain,
2121                                TALLOC_CTX *mem_ctx,
2122                                SAM_UNK_INFO_12 *policy){
2123         struct winbind_cache *cache = get_cache(domain);
2124         struct cache_entry *centry = NULL;
2125         NTSTATUS status;
2126  
2127         if (!cache->tdb)
2128                 goto do_query;
2129  
2130         centry = wcache_fetch(cache, domain, "LOC_POL/%s", domain->name);
2131         
2132         if (!centry)
2133                 goto do_query;
2134  
2135         policy->duration = centry_nttime(centry);
2136         policy->reset_count = centry_nttime(centry);
2137         policy->bad_attempt_lockout = centry_uint16(centry);
2138  
2139         status = centry->status;
2140  
2141         DEBUG(10,("lockout_policy: [Cached] - cached info for domain %s status: %s\n",
2142                 domain->name, nt_errstr(status) ));
2143  
2144         centry_free(centry);
2145         return status;
2146  
2147 do_query:
2148         ZERO_STRUCTP(policy);
2149  
2150         /* Return status value returned by seq number check */
2151
2152         if (!NT_STATUS_IS_OK(domain->last_status))
2153                 return domain->last_status;
2154         
2155         DEBUG(10,("lockout_policy: [Cached] - doing backend query for info for domain %s\n",
2156                 domain->name ));
2157  
2158         status = domain->backend->lockout_policy(domain, mem_ctx, policy); 
2159  
2160         /* and save it */
2161         refresh_sequence_number(domain, False);
2162         wcache_save_lockout_policy(domain, status, policy);
2163  
2164         return status;
2165 }
2166  
2167 /* get password policy */
2168 static NTSTATUS password_policy(struct winbindd_domain *domain,
2169                                 TALLOC_CTX *mem_ctx,
2170                                 SAM_UNK_INFO_1 *policy)
2171 {
2172         struct winbind_cache *cache = get_cache(domain);
2173         struct cache_entry *centry = NULL;
2174         NTSTATUS status;
2175
2176         if (!cache->tdb)
2177                 goto do_query;
2178  
2179         centry = wcache_fetch(cache, domain, "PWD_POL/%s", domain->name);
2180         
2181         if (!centry)
2182                 goto do_query;
2183
2184         policy->min_length_password = centry_uint16(centry);
2185         policy->password_history = centry_uint16(centry);
2186         policy->password_properties = centry_uint32(centry);
2187         policy->expire = centry_nttime(centry);
2188         policy->min_passwordage = centry_nttime(centry);
2189
2190         status = centry->status;
2191
2192         DEBUG(10,("lockout_policy: [Cached] - cached info for domain %s status: %s\n",
2193                 domain->name, nt_errstr(status) ));
2194
2195         centry_free(centry);
2196         return status;
2197
2198 do_query:
2199         ZERO_STRUCTP(policy);
2200
2201         /* Return status value returned by seq number check */
2202
2203         if (!NT_STATUS_IS_OK(domain->last_status))
2204                 return domain->last_status;
2205         
2206         DEBUG(10,("password_policy: [Cached] - doing backend query for info for domain %s\n",
2207                 domain->name ));
2208
2209         status = domain->backend->password_policy(domain, mem_ctx, policy); 
2210
2211         /* and save it */
2212         refresh_sequence_number(domain, False);
2213         wcache_save_password_policy(domain, status, policy);
2214
2215         return status;
2216 }
2217
2218
2219 /* Invalidate cached user and group lists coherently */
2220
2221 static int traverse_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf, 
2222                        void *state)
2223 {
2224         if (strncmp((const char *)kbuf.dptr, "UL/", 3) == 0 ||
2225             strncmp((const char *)kbuf.dptr, "GL/", 3) == 0)
2226                 tdb_delete(the_tdb, kbuf);
2227
2228         return 0;
2229 }
2230
2231 /* Invalidate the getpwnam and getgroups entries for a winbindd domain */
2232
2233 void wcache_invalidate_samlogon(struct winbindd_domain *domain, 
2234                                 NET_USER_INFO_3 *info3)
2235 {
2236         struct winbind_cache *cache;
2237
2238         /* dont clear cached U/SID and UG/SID entries when we want to logon
2239          * offline - gd */
2240
2241         if (lp_winbind_offline_logon()) {
2242                 return;
2243         }
2244
2245         if (!domain)
2246                 return;
2247
2248         cache = get_cache(domain);
2249         netsamlogon_clear_cached_user(cache->tdb, info3);
2250 }
2251
2252 void wcache_invalidate_cache(void)
2253 {
2254         struct winbindd_domain *domain;
2255
2256         for (domain = domain_list(); domain; domain = domain->next) {
2257                 struct winbind_cache *cache = get_cache(domain);
2258
2259                 DEBUG(10, ("wcache_invalidate_cache: invalidating cache "
2260                            "entries for %s\n", domain->name));
2261                 if (cache)
2262                         tdb_traverse(cache->tdb, traverse_fn, NULL);
2263         }
2264 }
2265
2266 bool init_wcache(void)
2267 {
2268         if (wcache == NULL) {
2269                 wcache = SMB_XMALLOC_P(struct winbind_cache);
2270                 ZERO_STRUCTP(wcache);
2271         }
2272
2273         if (wcache->tdb != NULL)
2274                 return True;
2275
2276         /* when working offline we must not clear the cache on restart */
2277         wcache->tdb = tdb_open_log(lock_path("winbindd_cache.tdb"),
2278                                 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE, 
2279                                 lp_winbind_offline_logon() ? TDB_DEFAULT : (TDB_DEFAULT | TDB_CLEAR_IF_FIRST), 
2280                                 O_RDWR|O_CREAT, 0600);
2281
2282         if (wcache->tdb == NULL) {
2283                 DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
2284                 return False;
2285         }
2286
2287         return True;
2288 }
2289
2290 /************************************************************************
2291  This is called by the parent to initialize the cache file.
2292  We don't need sophisticated locking here as we know we're the
2293  only opener.
2294 ************************************************************************/
2295
2296 bool initialize_winbindd_cache(void)
2297 {
2298         bool cache_bad = True;
2299         uint32 vers;
2300
2301         if (!init_wcache()) {
2302                 DEBUG(0,("initialize_winbindd_cache: init_wcache failed.\n"));
2303                 return False;
2304         }
2305
2306         /* Check version number. */
2307         if (tdb_fetch_uint32(wcache->tdb, WINBINDD_CACHE_VERSION_KEYSTR, &vers) &&
2308                         vers == WINBINDD_CACHE_VERSION) {
2309                 cache_bad = False;
2310         }
2311
2312         if (cache_bad) {
2313                 DEBUG(0,("initialize_winbindd_cache: clearing cache "
2314                         "and re-creating with version number %d\n",
2315                         WINBINDD_CACHE_VERSION ));
2316
2317                 tdb_close(wcache->tdb);
2318                 wcache->tdb = NULL;
2319
2320                 if (unlink(lock_path("winbindd_cache.tdb")) == -1) {
2321                         DEBUG(0,("initialize_winbindd_cache: unlink %s failed %s ",
2322                                 lock_path("winbindd_cache.tdb"),
2323                                 strerror(errno) ));
2324                         return False;
2325                 }
2326                 if (!init_wcache()) {
2327                         DEBUG(0,("initialize_winbindd_cache: re-initialization "
2328                                         "init_wcache failed.\n"));
2329                         return False;
2330                 }
2331
2332                 /* Write the version. */
2333                 if (!tdb_store_uint32(wcache->tdb, WINBINDD_CACHE_VERSION_KEYSTR, WINBINDD_CACHE_VERSION)) {
2334                         DEBUG(0,("initialize_winbindd_cache: version number store failed %s\n",
2335                                 tdb_errorstr(wcache->tdb) ));
2336                         return False;
2337                 }
2338         }
2339
2340         tdb_close(wcache->tdb);
2341         wcache->tdb = NULL;
2342         return True;
2343 }
2344
2345 void cache_store_response(pid_t pid, struct winbindd_response *response)
2346 {
2347         fstring key_str;
2348
2349         if (!init_wcache())
2350                 return;
2351
2352         DEBUG(10, ("Storing response for pid %d, len %d\n",
2353                    pid, response->length));
2354
2355         fstr_sprintf(key_str, "DR/%d", pid);
2356         if (tdb_store(wcache->tdb, string_tdb_data(key_str), 
2357                       make_tdb_data((uint8 *)response, sizeof(*response)),
2358                       TDB_REPLACE) == -1)
2359                 return;
2360
2361         if (response->length == sizeof(*response))
2362                 return;
2363
2364         /* There's extra data */
2365
2366         DEBUG(10, ("Storing extra data: len=%d\n",
2367                    (int)(response->length - sizeof(*response))));
2368
2369         fstr_sprintf(key_str, "DE/%d", pid);
2370         if (tdb_store(wcache->tdb, string_tdb_data(key_str),
2371                       make_tdb_data((uint8 *)response->extra_data.data,
2372                                     response->length - sizeof(*response)),
2373                       TDB_REPLACE) == 0)
2374                 return;
2375
2376         /* We could not store the extra data, make sure the tdb does not
2377          * contain a main record with wrong dangling extra data */
2378
2379         fstr_sprintf(key_str, "DR/%d", pid);
2380         tdb_delete(wcache->tdb, string_tdb_data(key_str));
2381
2382         return;
2383 }
2384
2385 bool cache_retrieve_response(pid_t pid, struct winbindd_response * response)
2386 {
2387         TDB_DATA data;
2388         fstring key_str;
2389
2390         if (!init_wcache())
2391                 return False;
2392
2393         DEBUG(10, ("Retrieving response for pid %d\n", pid));
2394
2395         fstr_sprintf(key_str, "DR/%d", pid);
2396         data = tdb_fetch(wcache->tdb, string_tdb_data(key_str));
2397
2398         if (data.dptr == NULL)
2399                 return False;
2400
2401         if (data.dsize != sizeof(*response))
2402                 return False;
2403
2404         memcpy(response, data.dptr, data.dsize);
2405         SAFE_FREE(data.dptr);
2406
2407         if (response->length == sizeof(*response)) {
2408                 response->extra_data.data = NULL;
2409                 return True;
2410         }
2411
2412         /* There's extra data */
2413
2414         DEBUG(10, ("Retrieving extra data length=%d\n",
2415                    (int)(response->length - sizeof(*response))));
2416
2417         fstr_sprintf(key_str, "DE/%d", pid);
2418         data = tdb_fetch(wcache->tdb, string_tdb_data(key_str));
2419
2420         if (data.dptr == NULL) {
2421                 DEBUG(0, ("Did not find extra data\n"));
2422                 return False;
2423         }
2424
2425         if (data.dsize != (response->length - sizeof(*response))) {
2426                 DEBUG(0, ("Invalid extra data length: %d\n", (int)data.dsize));
2427                 SAFE_FREE(data.dptr);
2428                 return False;
2429         }
2430
2431         dump_data(11, (uint8 *)data.dptr, data.dsize);
2432
2433         response->extra_data.data = data.dptr;
2434         return True;
2435 }
2436
2437 void cache_cleanup_response(pid_t pid)
2438 {
2439         fstring key_str;
2440
2441         if (!init_wcache())
2442                 return;
2443
2444         fstr_sprintf(key_str, "DR/%d", pid);
2445         tdb_delete(wcache->tdb, string_tdb_data(key_str));
2446
2447         fstr_sprintf(key_str, "DE/%d", pid);
2448         tdb_delete(wcache->tdb, string_tdb_data(key_str));
2449
2450         return;
2451 }
2452
2453
2454 bool lookup_cached_sid(TALLOC_CTX *mem_ctx, const DOM_SID *sid,
2455                        char **domain_name, char **name,
2456                        enum lsa_SidType *type)
2457 {
2458         struct winbindd_domain *domain;
2459         struct winbind_cache *cache;
2460         struct cache_entry *centry = NULL;
2461         NTSTATUS status;
2462
2463         domain = find_lookup_domain_from_sid(sid);
2464         if (domain == NULL) {
2465                 return False;
2466         }
2467
2468         cache = get_cache(domain);
2469
2470         if (cache->tdb == NULL) {
2471                 return False;
2472         }
2473
2474         centry = wcache_fetch(cache, domain, "SN/%s", sid_string_static(sid));
2475         if (centry == NULL) {
2476                 return False;
2477         }
2478
2479         if (NT_STATUS_IS_OK(centry->status)) {
2480                 *type = (enum lsa_SidType)centry_uint32(centry);
2481                 *domain_name = centry_string(centry, mem_ctx);
2482                 *name = centry_string(centry, mem_ctx);
2483         }
2484
2485         status = centry->status;
2486         centry_free(centry);
2487         return NT_STATUS_IS_OK(status);
2488 }
2489
2490 bool lookup_cached_name(TALLOC_CTX *mem_ctx,
2491                         const char *domain_name,
2492                         const char *name,
2493                         DOM_SID *sid,
2494                         enum lsa_SidType *type)
2495 {
2496         struct winbindd_domain *domain;
2497         struct winbind_cache *cache;
2498         struct cache_entry *centry = NULL;
2499         NTSTATUS status;
2500         fstring uname;
2501         bool original_online_state;     
2502
2503         domain = find_lookup_domain_from_name(domain_name);
2504         if (domain == NULL) {
2505                 return False;
2506         }
2507
2508         cache = get_cache(domain);
2509
2510         if (cache->tdb == NULL) {
2511                 return False;
2512         }
2513
2514         fstrcpy(uname, name);
2515         strupper_m(uname);
2516         
2517         /* If we are doing a cached logon, temporarily set the domain
2518            offline so the cache won't expire the entry */
2519         
2520         original_online_state = domain->online;
2521         domain->online = False;
2522         centry = wcache_fetch(cache, domain, "NS/%s/%s", domain_name, uname);
2523         domain->online = original_online_state;
2524         
2525         if (centry == NULL) {
2526                 return False;
2527         }
2528
2529         if (NT_STATUS_IS_OK(centry->status)) {
2530                 *type = (enum lsa_SidType)centry_uint32(centry);
2531                 centry_sid(centry, mem_ctx, sid);
2532         }
2533
2534         status = centry->status;
2535         centry_free(centry);
2536         
2537         return NT_STATUS_IS_OK(status);
2538 }
2539
2540 void cache_name2sid(struct winbindd_domain *domain, 
2541                     const char *domain_name, const char *name,
2542                     enum lsa_SidType type, const DOM_SID *sid)
2543 {
2544         refresh_sequence_number(domain, False);
2545         wcache_save_name_to_sid(domain, NT_STATUS_OK, domain_name, name,
2546                                 sid, type);
2547 }
2548
2549 /*
2550  * The original idea that this cache only contains centries has
2551  * been blurred - now other stuff gets put in here. Ensure we
2552  * ignore these things on cleanup.
2553  */
2554
2555 static int traverse_fn_cleanup(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, 
2556                                TDB_DATA dbuf, void *state)
2557 {
2558         struct cache_entry *centry;
2559
2560         if (is_non_centry_key(kbuf)) {
2561                 return 0;
2562         }
2563
2564         centry = wcache_fetch_raw((char *)kbuf.dptr);
2565         if (!centry) {
2566                 return 0;
2567         }
2568
2569         if (!NT_STATUS_IS_OK(centry->status)) {
2570                 DEBUG(10,("deleting centry %s\n", (const char *)kbuf.dptr));
2571                 tdb_delete(the_tdb, kbuf);
2572         }
2573
2574         centry_free(centry);
2575         return 0;
2576 }
2577
2578 /* flush the cache */
2579 void wcache_flush_cache(void)
2580 {
2581         if (!wcache)
2582                 return;
2583         if (wcache->tdb) {
2584                 tdb_close(wcache->tdb);
2585                 wcache->tdb = NULL;
2586         }
2587         if (opt_nocache)
2588                 return;
2589
2590         /* when working offline we must not clear the cache on restart */
2591         wcache->tdb = tdb_open_log(lock_path("winbindd_cache.tdb"),
2592                                 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE, 
2593                                 lp_winbind_offline_logon() ? TDB_DEFAULT : (TDB_DEFAULT | TDB_CLEAR_IF_FIRST), 
2594                                 O_RDWR|O_CREAT, 0600);
2595
2596         if (!wcache->tdb) {
2597                 DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
2598                 return;
2599         }
2600
2601         tdb_traverse(wcache->tdb, traverse_fn_cleanup, NULL);
2602
2603         DEBUG(10,("wcache_flush_cache success\n"));
2604 }
2605
2606 /* Count cached creds */
2607
2608 static int traverse_fn_cached_creds(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf, 
2609                                     void *state)
2610 {
2611         int *cred_count = (int*)state;
2612  
2613         if (strncmp((const char *)kbuf.dptr, "CRED/", 5) == 0) {
2614                 (*cred_count)++;
2615         }
2616         return 0;
2617 }
2618
2619 NTSTATUS wcache_count_cached_creds(struct winbindd_domain *domain, int *count)
2620 {
2621         struct winbind_cache *cache = get_cache(domain);
2622
2623         *count = 0;
2624
2625         if (!cache->tdb) {
2626                 return NT_STATUS_INTERNAL_DB_ERROR;
2627         }
2628  
2629         tdb_traverse(cache->tdb, traverse_fn_cached_creds, (void *)count);
2630
2631         return NT_STATUS_OK;
2632 }
2633
2634 struct cred_list {
2635         struct cred_list *prev, *next;
2636         TDB_DATA key;
2637         fstring name;
2638         time_t created;
2639 };
2640 static struct cred_list *wcache_cred_list;
2641
2642 static int traverse_fn_get_credlist(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf, 
2643                                     void *state)
2644 {
2645         struct cred_list *cred;
2646
2647         if (strncmp((const char *)kbuf.dptr, "CRED/", 5) == 0) {
2648
2649                 cred = SMB_MALLOC_P(struct cred_list);
2650                 if (cred == NULL) {
2651                         DEBUG(0,("traverse_fn_remove_first_creds: failed to malloc new entry for list\n"));
2652                         return -1;
2653                 }
2654
2655                 ZERO_STRUCTP(cred);
2656                 
2657                 /* save a copy of the key */
2658                 
2659                 fstrcpy(cred->name, (const char *)kbuf.dptr);           
2660                 DLIST_ADD(wcache_cred_list, cred);
2661         }
2662         
2663         return 0;
2664 }
2665
2666 NTSTATUS wcache_remove_oldest_cached_creds(struct winbindd_domain *domain, const DOM_SID *sid) 
2667 {
2668         struct winbind_cache *cache = get_cache(domain);
2669         NTSTATUS status;
2670         int ret;
2671         struct cred_list *cred, *oldest = NULL;
2672
2673         if (!cache->tdb) {
2674                 return NT_STATUS_INTERNAL_DB_ERROR;
2675         }
2676
2677         /* we possibly already have an entry */
2678         if (sid && NT_STATUS_IS_OK(wcache_cached_creds_exist(domain, sid))) {
2679         
2680                 fstring key_str;
2681
2682                 DEBUG(11,("we already have an entry, deleting that\n"));
2683
2684                 fstr_sprintf(key_str, "CRED/%s", sid_string_static(sid));
2685
2686                 tdb_delete(cache->tdb, string_tdb_data(key_str));
2687
2688                 return NT_STATUS_OK;
2689         }
2690
2691         ret = tdb_traverse(cache->tdb, traverse_fn_get_credlist, NULL);
2692         if (ret == 0) {
2693                 return NT_STATUS_OK;
2694         } else if ((ret == -1) || (wcache_cred_list == NULL)) {
2695                 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
2696         }
2697
2698         ZERO_STRUCTP(oldest);
2699
2700         for (cred = wcache_cred_list; cred; cred = cred->next) {
2701
2702                 TDB_DATA data;
2703                 time_t t;
2704
2705                 data = tdb_fetch(cache->tdb, string_tdb_data(cred->name));
2706                 if (!data.dptr) {
2707                         DEBUG(10,("wcache_remove_oldest_cached_creds: entry for [%s] not found\n", 
2708                                 cred->name));
2709                         status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
2710                         goto done;
2711                 }
2712         
2713                 t = IVAL(data.dptr, 0);
2714                 SAFE_FREE(data.dptr);
2715
2716                 if (!oldest) {
2717                         oldest = SMB_MALLOC_P(struct cred_list);
2718                         if (oldest == NULL) {
2719                                 status = NT_STATUS_NO_MEMORY;
2720                                 goto done;
2721                         }
2722
2723                         fstrcpy(oldest->name, cred->name);
2724                         oldest->created = t;
2725                         continue;
2726                 }
2727
2728                 if (t < oldest->created) {
2729                         fstrcpy(oldest->name, cred->name);
2730                         oldest->created = t;
2731                 }
2732         }
2733
2734         if (tdb_delete(cache->tdb, string_tdb_data(oldest->name)) == 0) {
2735                 status = NT_STATUS_OK;
2736         } else {
2737                 status = NT_STATUS_UNSUCCESSFUL;
2738         }
2739 done:
2740         SAFE_FREE(wcache_cred_list);
2741         SAFE_FREE(oldest);
2742         
2743         return status;
2744 }
2745
2746 /* Change the global online/offline state. */
2747 bool set_global_winbindd_state_offline(void)
2748 {
2749         TDB_DATA data;
2750
2751         DEBUG(10,("set_global_winbindd_state_offline: offline requested.\n"));
2752
2753         /* Only go offline if someone has created
2754            the key "WINBINDD_OFFLINE" in the cache tdb. */
2755
2756         if (wcache == NULL || wcache->tdb == NULL) {
2757                 DEBUG(10,("set_global_winbindd_state_offline: wcache not open yet.\n"));
2758                 return False;
2759         }
2760
2761         if (!lp_winbind_offline_logon()) {
2762                 DEBUG(10,("set_global_winbindd_state_offline: rejecting.\n"));
2763                 return False;
2764         }
2765
2766         if (global_winbindd_offline_state) {
2767                 /* Already offline. */
2768                 return True;
2769         }
2770
2771         data = tdb_fetch_bystring( wcache->tdb, "WINBINDD_OFFLINE" );
2772
2773         if (!data.dptr || data.dsize != 4) {
2774                 DEBUG(10,("set_global_winbindd_state_offline: offline state not set.\n"));
2775                 SAFE_FREE(data.dptr);
2776                 return False;
2777         } else {
2778                 DEBUG(10,("set_global_winbindd_state_offline: offline state set.\n"));
2779                 global_winbindd_offline_state = True;
2780                 SAFE_FREE(data.dptr);
2781                 return True;
2782         }
2783 }
2784
2785 void set_global_winbindd_state_online(void)
2786 {
2787         DEBUG(10,("set_global_winbindd_state_online: online requested.\n"));
2788
2789         if (!lp_winbind_offline_logon()) {
2790                 DEBUG(10,("set_global_winbindd_state_online: rejecting.\n"));
2791                 return;
2792         }
2793
2794         if (!global_winbindd_offline_state) {
2795                 /* Already online. */
2796                 return;
2797         }
2798         global_winbindd_offline_state = False;
2799
2800         if (!wcache->tdb) {
2801                 return;
2802         }
2803
2804         /* Ensure there is no key "WINBINDD_OFFLINE" in the cache tdb. */
2805         tdb_delete_bystring(wcache->tdb, "WINBINDD_OFFLINE");
2806 }
2807
2808 bool get_global_winbindd_state_offline(void)
2809 {
2810         return global_winbindd_offline_state;
2811 }
2812
2813 /***********************************************************************
2814  Validate functions for all possible cache tdb keys.
2815 ***********************************************************************/
2816
2817 static struct cache_entry *create_centry_validate(const char *kstr, TDB_DATA data, 
2818                                                   struct tdb_validation_status *state)
2819 {
2820         struct cache_entry *centry;
2821
2822         centry = SMB_XMALLOC_P(struct cache_entry);
2823         centry->data = (unsigned char *)memdup(data.dptr, data.dsize);
2824         if (!centry->data) {
2825                 SAFE_FREE(centry);
2826                 return NULL;
2827         }
2828         centry->len = data.dsize;
2829         centry->ofs = 0;
2830
2831         if (centry->len < 8) {
2832                 /* huh? corrupt cache? */
2833                 DEBUG(0,("create_centry_validate: Corrupt cache for key %s (len < 8) ?\n", kstr));
2834                 centry_free(centry);
2835                 state->bad_entry = True;
2836                 state->success = False;
2837                 return NULL;
2838         }
2839
2840         centry->status = NT_STATUS(centry_uint32(centry));
2841         centry->sequence_number = centry_uint32(centry);
2842         return centry;
2843 }
2844
2845 static int validate_seqnum(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
2846                            struct tdb_validation_status *state)
2847 {
2848         if (dbuf.dsize != 8) {
2849                 DEBUG(0,("validate_seqnum: Corrupt cache for key %s (len %u != 8) ?\n",
2850                                 keystr, (unsigned int)dbuf.dsize ));
2851                 state->bad_entry = True;
2852                 return 1;
2853         }
2854         return 0;
2855 }
2856
2857 static int validate_ns(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
2858                        struct tdb_validation_status *state)
2859 {
2860         struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
2861         if (!centry) {
2862                 return 1;
2863         }
2864
2865         (void)centry_uint32(centry);
2866         if (NT_STATUS_IS_OK(centry->status)) {
2867                 DOM_SID sid;
2868                 (void)centry_sid(centry, mem_ctx, &sid);
2869         }
2870
2871         centry_free(centry);
2872
2873         if (!(state->success)) {
2874                 return 1;
2875         }
2876         DEBUG(10,("validate_ns: %s ok\n", keystr));
2877         return 0;
2878 }
2879
2880 static int validate_sn(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
2881                        struct tdb_validation_status *state)
2882 {
2883         struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
2884         if (!centry) {
2885                 return 1;
2886         }
2887
2888         if (NT_STATUS_IS_OK(centry->status)) {
2889                 (void)centry_uint32(centry);
2890                 (void)centry_string(centry, mem_ctx);
2891                 (void)centry_string(centry, mem_ctx);
2892         }
2893
2894         centry_free(centry);
2895
2896         if (!(state->success)) {
2897                 return 1;
2898         }
2899         DEBUG(10,("validate_sn: %s ok\n", keystr));
2900         return 0;
2901 }
2902
2903 static int validate_u(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
2904                       struct tdb_validation_status *state)
2905 {
2906         struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
2907         DOM_SID sid;
2908
2909         if (!centry) {
2910                 return 1;
2911         }
2912
2913         (void)centry_string(centry, mem_ctx);
2914         (void)centry_string(centry, mem_ctx);
2915         (void)centry_string(centry, mem_ctx);
2916         (void)centry_string(centry, mem_ctx);
2917         (void)centry_uint32(centry);
2918         (void)centry_sid(centry, mem_ctx, &sid);
2919         (void)centry_sid(centry, mem_ctx, &sid);
2920
2921         centry_free(centry);
2922
2923         if (!(state->success)) {
2924                 return 1;
2925         }
2926         DEBUG(10,("validate_u: %s ok\n", keystr));
2927         return 0;
2928 }
2929
2930 static int validate_loc_pol(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
2931                             struct tdb_validation_status *state)
2932 {
2933         struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
2934
2935         if (!centry) {
2936                 return 1;
2937         }
2938
2939         (void)centry_nttime(centry);
2940         (void)centry_nttime(centry);
2941         (void)centry_uint16(centry);
2942
2943         centry_free(centry);
2944
2945         if (!(state->success)) {
2946                 return 1;
2947         }
2948         DEBUG(10,("validate_loc_pol: %s ok\n", keystr));
2949         return 0;
2950 }
2951
2952 static int validate_pwd_pol(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
2953                             struct tdb_validation_status *state)
2954 {
2955         struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
2956
2957         if (!centry) {
2958                 return 1;
2959         }
2960
2961         (void)centry_uint16(centry);
2962         (void)centry_uint16(centry);
2963         (void)centry_uint32(centry);
2964         (void)centry_nttime(centry);
2965         (void)centry_nttime(centry);
2966
2967         centry_free(centry);
2968
2969         if (!(state->success)) {
2970                 return 1;
2971         }
2972         DEBUG(10,("validate_pwd_pol: %s ok\n", keystr));
2973         return 0;
2974 }
2975
2976 static int validate_cred(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
2977                          struct tdb_validation_status *state)
2978 {
2979         struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
2980
2981         if (!centry) {
2982                 return 1;
2983         }
2984
2985         (void)centry_time(centry);
2986         (void)centry_hash16(centry, mem_ctx);
2987
2988         /* We only have 17 bytes more data in the salted cred case. */
2989         if (centry->len - centry->ofs == 17) {
2990                 (void)centry_hash16(centry, mem_ctx);
2991         }
2992
2993         centry_free(centry);
2994
2995         if (!(state->success)) {
2996                 return 1;
2997         }
2998         DEBUG(10,("validate_cred: %s ok\n", keystr));
2999         return 0;
3000 }
3001
3002 static int validate_ul(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3003                        struct tdb_validation_status *state)
3004 {
3005         struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3006         int32 num_entries, i;
3007
3008         if (!centry) {
3009                 return 1;
3010         }
3011
3012         num_entries = (int32)centry_uint32(centry);
3013
3014         for (i=0; i< num_entries; i++) {
3015                 DOM_SID sid;
3016                 (void)centry_string(centry, mem_ctx);
3017                 (void)centry_string(centry, mem_ctx);
3018                 (void)centry_string(centry, mem_ctx);
3019                 (void)centry_string(centry, mem_ctx);
3020                 (void)centry_sid(centry, mem_ctx, &sid);
3021                 (void)centry_sid(centry, mem_ctx, &sid);
3022         }
3023
3024         centry_free(centry);
3025
3026         if (!(state->success)) {
3027                 return 1;
3028         }
3029         DEBUG(10,("validate_ul: %s ok\n", keystr));
3030         return 0;
3031 }
3032
3033 static int validate_gl(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3034                        struct tdb_validation_status *state)
3035 {
3036         struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3037         int32 num_entries, i;
3038
3039         if (!centry) {
3040                 return 1;
3041         }
3042
3043         num_entries = centry_uint32(centry);
3044         
3045         for (i=0; i< num_entries; i++) {
3046                 (void)centry_string(centry, mem_ctx);
3047                 (void)centry_string(centry, mem_ctx);
3048                 (void)centry_uint32(centry);
3049         }
3050
3051         centry_free(centry);
3052
3053         if (!(state->success)) {
3054                 return 1;
3055         }
3056         DEBUG(10,("validate_gl: %s ok\n", keystr));
3057         return 0;
3058 }
3059
3060 static int validate_ug(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3061                        struct tdb_validation_status *state)
3062 {
3063         struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3064         int32 num_groups, i;
3065
3066         if (!centry) {
3067                 return 1;
3068         }
3069
3070         num_groups = centry_uint32(centry);
3071
3072         for (i=0; i< num_groups; i++) {
3073                 DOM_SID sid;
3074                 centry_sid(centry, mem_ctx, &sid);
3075         }
3076
3077         centry_free(centry);
3078
3079         if (!(state->success)) {
3080                 return 1;
3081         }
3082         DEBUG(10,("validate_ug: %s ok\n", keystr));
3083         return 0;
3084 }
3085
3086 static int validate_ua(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3087                        struct tdb_validation_status *state)
3088 {
3089         struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3090         int32 num_aliases, i;
3091
3092         if (!centry) {
3093                 return 1;
3094         }
3095
3096         num_aliases = centry_uint32(centry);
3097
3098         for (i=0; i < num_aliases; i++) {
3099                 (void)centry_uint32(centry);
3100         }
3101
3102         centry_free(centry);
3103
3104         if (!(state->success)) {
3105                 return 1;
3106         }
3107         DEBUG(10,("validate_ua: %s ok\n", keystr));
3108         return 0;
3109 }
3110
3111 static int validate_gm(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3112                        struct tdb_validation_status *state)
3113 {
3114         struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3115         int32 num_names, i;
3116
3117         if (!centry) {
3118                 return 1;
3119         }
3120
3121         num_names = centry_uint32(centry);
3122
3123         for (i=0; i< num_names; i++) {
3124                 DOM_SID sid;
3125                 centry_sid(centry, mem_ctx, &sid);
3126                 (void)centry_string(centry, mem_ctx);
3127                 (void)centry_uint32(centry);
3128         }
3129
3130         centry_free(centry);
3131
3132         if (!(state->success)) {
3133                 return 1;
3134         }
3135         DEBUG(10,("validate_gm: %s ok\n", keystr));
3136         return 0;
3137 }
3138
3139 static int validate_dr(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3140                        struct tdb_validation_status *state)
3141 {
3142         /* Can't say anything about this other than must be nonzero. */
3143         if (dbuf.dsize == 0) {
3144                 DEBUG(0,("validate_dr: Corrupt cache for key %s (len == 0) ?\n",
3145                                 keystr));
3146                 state->bad_entry = True;
3147                 state->success = False;
3148                 return 1;
3149         }
3150
3151         DEBUG(10,("validate_dr: %s ok\n", keystr));
3152         return 0;
3153 }
3154
3155 static int validate_de(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3156                        struct tdb_validation_status *state)
3157 {
3158         /* Can't say anything about this other than must be nonzero. */
3159         if (dbuf.dsize == 0) {
3160                 DEBUG(0,("validate_de: Corrupt cache for key %s (len == 0) ?\n",
3161                                 keystr));
3162                 state->bad_entry = True;
3163                 state->success = False;
3164                 return 1;
3165         }
3166
3167         DEBUG(10,("validate_de: %s ok\n", keystr));
3168         return 0;
3169 }
3170
3171 static int validate_trustdoms(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3172                               struct tdb_validation_status *state)
3173 {
3174         struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3175         int32 num_domains, i;
3176
3177         if (!centry) {
3178                 return 1;
3179         }
3180
3181         num_domains = centry_uint32(centry);
3182         
3183         for (i=0; i< num_domains; i++) {
3184                 DOM_SID sid;
3185                 (void)centry_string(centry, mem_ctx);
3186                 (void)centry_string(centry, mem_ctx);
3187                 (void)centry_sid(centry, mem_ctx, &sid);
3188         }
3189
3190         centry_free(centry);
3191
3192         if (!(state->success)) {
3193                 return 1;
3194         }
3195         DEBUG(10,("validate_trustdoms: %s ok\n", keystr));
3196         return 0;
3197 }
3198
3199 static int validate_trustdomcache(TALLOC_CTX *mem_ctx, const char *keystr, 
3200                                   TDB_DATA dbuf,
3201                                   struct tdb_validation_status *state)
3202 {
3203         if (dbuf.dsize == 0) {
3204                 DEBUG(0, ("validate_trustdomcache: Corrupt cache for "
3205                           "key %s (len ==0) ?\n", keystr));
3206                 state->bad_entry = True;
3207                 state->success = False;
3208                 return 1;
3209         }
3210
3211         DEBUG(10,    ("validate_trustdomcache: %s ok\n", keystr));
3212         DEBUGADD(10, ("  Don't trust me, I am a DUMMY!\n"));
3213         return 0;
3214 }
3215
3216 static int validate_offline(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3217                             struct tdb_validation_status *state)
3218 {
3219         if (dbuf.dsize != 4) {
3220                 DEBUG(0,("validate_offline: Corrupt cache for key %s (len %u != 4) ?\n",
3221                                 keystr, (unsigned int)dbuf.dsize ));
3222                 state->bad_entry = True;
3223                 state->success = False;
3224                 return 1;
3225         }
3226         DEBUG(10,("validate_offline: %s ok\n", keystr));
3227         return 0;
3228 }
3229
3230 static int validate_cache_version(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3231                                   struct tdb_validation_status *state)
3232 {
3233         if (dbuf.dsize != 4) {
3234                 DEBUG(0, ("validate_cache_version: Corrupt cache for "
3235                           "key %s (len %u != 4) ?\n", 
3236                           keystr, (unsigned int)dbuf.dsize));
3237                 state->bad_entry = True;
3238                 state->success = False;
3239                 return 1;
3240         }
3241
3242         DEBUG(10, ("validate_cache_version: %s ok\n", keystr));
3243         return 0;
3244 }
3245
3246 /***********************************************************************
3247  A list of all possible cache tdb keys with associated validation
3248  functions.
3249 ***********************************************************************/
3250
3251 struct key_val_struct {
3252         const char *keyname;
3253         int (*validate_data_fn)(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf, struct tdb_validation_status* state);
3254 } key_val[] = {
3255         {"SEQNUM/", validate_seqnum},
3256         {"NS/", validate_ns},
3257         {"SN/", validate_sn},
3258         {"U/", validate_u},
3259         {"LOC_POL/", validate_loc_pol},
3260         {"PWD_POL/", validate_pwd_pol},
3261         {"CRED/", validate_cred},
3262         {"UL/", validate_ul},
3263         {"GL/", validate_gl},
3264         {"UG/", validate_ug},
3265         {"UA", validate_ua},
3266         {"GM/", validate_gm},
3267         {"DR/", validate_dr},
3268         {"DE/", validate_de},
3269         {"TRUSTDOMS/", validate_trustdoms},
3270         {"TRUSTDOMCACHE/", validate_trustdomcache},
3271         {"WINBINDD_OFFLINE", validate_offline},
3272         {WINBINDD_CACHE_VERSION_KEYSTR, validate_cache_version},
3273         {NULL, NULL}
3274 };
3275
3276 /***********************************************************************
3277  Function to look at every entry in the tdb and validate it as far as
3278  possible.
3279 ***********************************************************************/
3280
3281 static int cache_traverse_validate_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf, void *state)
3282 {
3283         int i;
3284         struct tdb_validation_status *v_state = (struct tdb_validation_status *)state;
3285
3286         /* Paranoia check. */
3287         if (kbuf.dsize > 1024) {
3288                 DEBUG(0,("cache_traverse_validate_fn: key length too large (%u) > 1024\n\n",
3289                                 (unsigned int)kbuf.dsize ));
3290                 return 1;
3291         }
3292
3293         for (i = 0; key_val[i].keyname; i++) {
3294                 size_t namelen = strlen(key_val[i].keyname);
3295                 if (kbuf.dsize >= namelen && (
3296                                 strncmp(key_val[i].keyname, (const char *)kbuf.dptr, namelen)) == 0) {
3297                         TALLOC_CTX *mem_ctx;
3298                         char *keystr;
3299                         int ret;
3300
3301                         keystr = SMB_MALLOC_ARRAY(char, kbuf.dsize+1);
3302                         if (!keystr) {
3303                                 return 1;
3304                         }
3305                         memcpy(keystr, kbuf.dptr, kbuf.dsize);
3306                         keystr[kbuf.dsize] = '\0';
3307
3308                         mem_ctx = talloc_init("validate_ctx");
3309                         if (!mem_ctx) {
3310                                 SAFE_FREE(keystr);
3311                                 return 1;
3312                         }
3313
3314                         ret = key_val[i].validate_data_fn(mem_ctx, keystr, dbuf, 
3315                                                           v_state);
3316
3317                         SAFE_FREE(keystr);
3318                         talloc_destroy(mem_ctx);
3319                         return ret;
3320                 }
3321         }
3322
3323         DEBUG(0,("cache_traverse_validate_fn: unknown cache entry\nkey :\n"));
3324         dump_data(0, (uint8 *)kbuf.dptr, kbuf.dsize);
3325         DEBUG(0,("data :\n"));
3326         dump_data(0, (uint8 *)dbuf.dptr, dbuf.dsize);
3327         v_state->unknown_key = True;
3328         v_state->success = False;
3329         return 1; /* terminate. */
3330 }
3331
3332 static void validate_panic(const char *const why)
3333 {
3334         DEBUG(0,("validating cache: would panic %s\n", why ));
3335         DEBUGADD(0, ("exiting instead (cache validation mode)\n"));
3336         exit(47);
3337 }
3338
3339 /***********************************************************************
3340  Try and validate every entry in the winbindd cache. If we fail here,
3341  delete the cache tdb and return non-zero.
3342 ***********************************************************************/
3343
3344 int winbindd_validate_cache(void)
3345 {
3346         int ret = -1;
3347         const char *tdb_path = lock_path("winbindd_cache.tdb");
3348         TDB_CONTEXT *tdb = NULL;
3349
3350         DEBUG(10, ("winbindd_validate_cache: replacing panic function\n"));
3351         smb_panic_fn = validate_panic;
3352
3353
3354         tdb = tdb_open_log(tdb_path, 
3355                            WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
3356                            ( lp_winbind_offline_logon() 
3357                              ? TDB_DEFAULT 
3358                              : TDB_DEFAULT | TDB_CLEAR_IF_FIRST ),
3359                            O_RDWR|O_CREAT, 
3360                            0600);
3361         if (!tdb) {
3362                 DEBUG(0, ("winbindd_validate_cache: "
3363                           "error opening/initializing tdb\n"));
3364                 goto done;
3365         }
3366         tdb_close(tdb);
3367
3368         ret = tdb_validate_and_backup(tdb_path, cache_traverse_validate_fn);
3369
3370         if (ret != 0) {
3371                 DEBUG(10, ("winbindd_validate_cache: validation not successful.\n"));
3372                 DEBUGADD(10, ("removing tdb %s.\n", tdb_path));
3373                 unlink(tdb_path);
3374         }
3375
3376 done:
3377         DEBUG(10, ("winbindd_validate_cache: restoring panic function\n"));
3378         smb_panic_fn = smb_panic;
3379         return ret;
3380 }
3381
3382 /***********************************************************************
3383  Try and validate every entry in the winbindd cache.
3384 ***********************************************************************/
3385
3386 int winbindd_validate_cache_nobackup(void)
3387 {
3388         int ret = -1;
3389         const char *tdb_path = lock_path("winbindd_cache.tdb");
3390
3391         DEBUG(10, ("winbindd_validate_cache: replacing panic function\n"));
3392         smb_panic_fn = validate_panic;
3393
3394
3395         if (wcache == NULL || wcache->tdb == NULL) {
3396                 ret = tdb_validate_open(tdb_path, cache_traverse_validate_fn);
3397         } else {
3398                 ret = tdb_validate(wcache->tdb, cache_traverse_validate_fn);
3399         }
3400
3401         if (ret != 0) {
3402                 DEBUG(10, ("winbindd_validate_cache_nobackup: validation not "
3403                            "successful.\n"));
3404         }
3405
3406         DEBUG(10, ("winbindd_validate_cache_nobackup: restoring panic "
3407                    "function\n"));
3408         smb_panic_fn = smb_panic;
3409         return ret;
3410 }
3411
3412 /*********************************************************************
3413  ********************************************************************/
3414
3415 static bool add_wbdomain_to_tdc_array( struct winbindd_domain *new_dom,
3416                                        struct winbindd_tdc_domain **domains, 
3417                                        size_t *num_domains )
3418 {
3419         struct winbindd_tdc_domain *list = NULL;
3420         size_t idx;
3421         int i;
3422         bool set_only = False;  
3423         
3424         /* don't allow duplicates */
3425
3426         idx = *num_domains;
3427         list = *domains;
3428         
3429         for ( i=0; i< (*num_domains); i++ ) {
3430                 if ( strequal( new_dom->name, list[i].domain_name ) ) {
3431                         DEBUG(10,("add_wbdomain_to_tdc_array: Found existing record for %s\n",
3432                                   new_dom->name));
3433                         idx = i;
3434                         set_only = True;
3435                         
3436                         break;
3437                 }
3438         }
3439
3440         if ( !set_only ) {
3441                 if ( !*domains ) {
3442                         list = TALLOC_ARRAY( NULL, struct winbindd_tdc_domain, 1 );
3443                         idx = 0;
3444                 } else {
3445                         list = TALLOC_REALLOC_ARRAY( *domains, *domains, 
3446                                                      struct winbindd_tdc_domain,  
3447                                                      (*num_domains)+1);
3448                         idx = *num_domains;             
3449                 }
3450
3451                 ZERO_STRUCT( list[idx] );
3452         }
3453
3454         if ( !list )
3455                 return False;
3456
3457         list[idx].domain_name = talloc_strdup( list, new_dom->name );
3458         list[idx].dns_name = talloc_strdup( list, new_dom->alt_name );
3459
3460         if ( !is_null_sid( &new_dom->sid ) )
3461                 sid_copy( &list[idx].sid, &new_dom->sid );
3462
3463         if ( new_dom->domain_flags != 0x0 )
3464                 list[idx].trust_flags = new_dom->domain_flags;  
3465
3466         if ( new_dom->domain_type != 0x0 )
3467                 list[idx].trust_type = new_dom->domain_type;
3468
3469         if ( new_dom->domain_trust_attribs != 0x0 )
3470                 list[idx].trust_attribs = new_dom->domain_trust_attribs;
3471         
3472         if ( !set_only ) {
3473                 *domains = list;
3474                 *num_domains = idx + 1; 
3475         }
3476
3477         return True;    
3478 }
3479
3480 /*********************************************************************
3481  ********************************************************************/
3482
3483 static TDB_DATA make_tdc_key( const char *domain_name )
3484 {
3485         char *keystr = NULL;
3486         TDB_DATA key = { NULL, 0 };
3487         
3488         if ( !domain_name ) {
3489                 DEBUG(5,("make_tdc_key: Keyname workgroup is NULL!\n"));
3490                 return key;
3491         }
3492                
3493                 
3494         asprintf( &keystr, "TRUSTDOMCACHE/%s", domain_name );
3495         key = string_term_tdb_data(keystr);
3496         
3497         return key;     
3498 }
3499
3500 /*********************************************************************
3501  ********************************************************************/
3502
3503 static int pack_tdc_domains( struct winbindd_tdc_domain *domains, 
3504                              size_t num_domains,
3505                              unsigned char **buf )
3506 {
3507         unsigned char *buffer = NULL;
3508         int len = 0;
3509         int buflen = 0;
3510         int i = 0;
3511
3512         DEBUG(10,("pack_tdc_domains: Packing %d trusted domains\n",
3513                   (int)num_domains));
3514         
3515         buflen = 0;
3516         
3517  again: 
3518         len = 0;
3519         
3520         /* Store the number of array items first */
3521         len += tdb_pack( buffer+len, buflen-len, "d", 
3522                          num_domains );
3523
3524         /* now pack each domain trust record */
3525         for ( i=0; i<num_domains; i++ ) {
3526
3527                 if ( buflen > 0 ) {
3528                         DEBUG(10,("pack_tdc_domains: Packing domain %s (%s)\n",
3529                                   domains[i].domain_name,
3530                                   domains[i].dns_name ? domains[i].dns_name : "UNKNOWN" ));
3531                 }
3532                 
3533                 len += tdb_pack( buffer+len, buflen-len, "fffddd",
3534                                  domains[i].domain_name,
3535                                  domains[i].dns_name,
3536                                  sid_string_static(&domains[i].sid),
3537                                  domains[i].trust_flags,
3538                                  domains[i].trust_attribs,
3539                                  domains[i].trust_type );
3540         }
3541
3542         if ( buflen < len ) {
3543                 SAFE_FREE(buffer);
3544                 if ( (buffer = SMB_MALLOC_ARRAY(unsigned char, len)) == NULL ) {
3545                         DEBUG(0,("pack_tdc_domains: failed to alloc buffer!\n"));
3546                         buflen = -1;
3547                         goto done;
3548                 }
3549                 buflen = len;
3550                 goto again;
3551         }
3552
3553         *buf = buffer;  
3554         
3555  done:  
3556         return buflen;  
3557 }
3558
3559 /*********************************************************************
3560  ********************************************************************/
3561
3562 static size_t unpack_tdc_domains( unsigned char *buf, int buflen, 
3563                                   struct winbindd_tdc_domain **domains )
3564 {
3565         fstring domain_name, dns_name, sid_string;      
3566         uint32 type, attribs, flags;
3567         int num_domains;
3568         int len = 0;
3569         int i;
3570         struct winbindd_tdc_domain *list = NULL;
3571
3572         /* get the number of domains */
3573         len += tdb_unpack( buf+len, buflen-len, "d", &num_domains);
3574         if ( len == -1 ) {
3575                 DEBUG(5,("unpack_tdc_domains: Failed to unpack domain array\n"));               
3576                 return 0;
3577         }
3578
3579         list = TALLOC_ARRAY( NULL, struct winbindd_tdc_domain, num_domains );
3580         if ( !list ) {
3581                 DEBUG(0,("unpack_tdc_domains: Failed to talloc() domain list!\n"));
3582                 return 0;               
3583         }
3584         
3585         for ( i=0; i<num_domains; i++ ) {
3586                 len += tdb_unpack( buf+len, buflen-len, "fffddd",
3587                                    domain_name,
3588                                    dns_name,
3589                                    sid_string,
3590                                    &flags,
3591                                    &attribs,
3592                                    &type );
3593
3594                 if ( len == -1 ) {
3595                         DEBUG(5,("unpack_tdc_domains: Failed to unpack domain array\n"));
3596                         TALLOC_FREE( list );                    
3597                         return 0;
3598                 }
3599
3600                 DEBUG(11,("unpack_tdc_domains: Unpacking domain %s (%s) "
3601                           "SID %s, flags = 0x%x, attribs = 0x%x, type = 0x%x\n",
3602                           domain_name, dns_name, sid_string,
3603                           flags, attribs, type));
3604                 
3605                 list[i].domain_name = talloc_strdup( list, domain_name );
3606                 list[i].dns_name = talloc_strdup( list, dns_name );
3607                 if ( !string_to_sid( &(list[i].sid), sid_string ) ) {                   
3608                         DEBUG(10,("unpack_tdc_domains: no SID for domain %s\n",
3609                                   domain_name));
3610                 }
3611                 list[i].trust_flags = flags;
3612                 list[i].trust_attribs = attribs;
3613                 list[i].trust_type = type;
3614         }
3615
3616         *domains = list;
3617         
3618         return num_domains;
3619 }
3620
3621 /*********************************************************************
3622  ********************************************************************/
3623
3624 static bool wcache_tdc_store_list( struct winbindd_tdc_domain *domains, size_t num_domains )
3625 {
3626         TDB_DATA key = make_tdc_key( lp_workgroup() );   
3627         TDB_DATA data = { NULL, 0 };
3628         int ret;
3629         
3630         if ( !key.dptr )
3631                 return False;
3632         
3633         /* See if we were asked to delete the cache entry */
3634
3635         if ( !domains ) {
3636                 ret = tdb_delete( wcache->tdb, key );
3637                 goto done;
3638         }
3639         
3640         data.dsize = pack_tdc_domains( domains, num_domains, &data.dptr );
3641         
3642         if ( !data.dptr ) {
3643                 ret = -1;
3644                 goto done;
3645         }
3646                 
3647         ret = tdb_store( wcache->tdb, key, data, 0 );
3648
3649  done:
3650         SAFE_FREE( data.dptr );
3651         SAFE_FREE( key.dptr );
3652         
3653         return ( ret != -1 );   
3654 }
3655
3656 /*********************************************************************
3657  ********************************************************************/
3658
3659 bool wcache_tdc_fetch_list( struct winbindd_tdc_domain **domains, size_t *num_domains )
3660 {
3661         TDB_DATA key = make_tdc_key( lp_workgroup() );
3662         TDB_DATA data = { NULL, 0 };
3663
3664         *domains = NULL;        
3665         *num_domains = 0;       
3666
3667         if ( !key.dptr )
3668                 return False;
3669         
3670         data = tdb_fetch( wcache->tdb, key );
3671
3672         SAFE_FREE( key.dptr );
3673         
3674         if ( !data.dptr ) 
3675                 return False;
3676         
3677         *num_domains = unpack_tdc_domains( data.dptr, data.dsize, domains );
3678
3679         SAFE_FREE( data.dptr );
3680         
3681         if ( !*domains )
3682                 return False;
3683
3684         return True;    
3685 }
3686
3687 /*********************************************************************
3688  ********************************************************************/
3689
3690 bool wcache_tdc_add_domain( struct winbindd_domain *domain )
3691 {
3692         struct winbindd_tdc_domain *dom_list = NULL;
3693         size_t num_domains = 0;
3694         bool ret = False;       
3695
3696         DEBUG(10,("wcache_tdc_add_domain: Adding domain %s (%s), SID %s, "
3697                   "flags = 0x%x, attributes = 0x%x, type = 0x%x\n",
3698                   domain->name, domain->alt_name, 
3699                   sid_string_static(&domain->sid),
3700                   domain->domain_flags,
3701                   domain->domain_trust_attribs,
3702                   domain->domain_type));        
3703         
3704         if ( !init_wcache() ) {
3705                 return False;
3706         }
3707         
3708         /* fetch the list */
3709
3710         wcache_tdc_fetch_list( &dom_list, &num_domains );
3711         
3712         /* add the new domain */
3713
3714         if ( !add_wbdomain_to_tdc_array( domain, &dom_list, &num_domains ) ) {
3715                 goto done;              
3716         }       
3717
3718         /* pack the domain */
3719
3720         if ( !wcache_tdc_store_list( dom_list, num_domains ) ) {
3721                 goto done;              
3722         }
3723         
3724         /* Success */
3725
3726         ret = True;     
3727  done:
3728         TALLOC_FREE( dom_list );
3729         
3730         return ret;     
3731 }
3732
3733 /*********************************************************************
3734  ********************************************************************/
3735
3736 struct winbindd_tdc_domain * wcache_tdc_fetch_domain( TALLOC_CTX *ctx, const char *name )
3737 {
3738         struct winbindd_tdc_domain *dom_list = NULL;
3739         size_t num_domains = 0;
3740         int i;
3741         struct winbindd_tdc_domain *d = NULL;   
3742
3743         DEBUG(10,("wcache_tdc_fetch_domain: Searching for domain %s\n", name));
3744
3745         if ( !init_wcache() ) {
3746                 return False;
3747         }
3748         
3749         /* fetch the list */
3750
3751         wcache_tdc_fetch_list( &dom_list, &num_domains );
3752         
3753         for ( i=0; i<num_domains; i++ ) {
3754                 if ( strequal(name, dom_list[i].domain_name) ||
3755                      strequal(name, dom_list[i].dns_name) )
3756                 {
3757                         DEBUG(10,("wcache_tdc_fetch_domain: Found domain %s\n",
3758                                   name));
3759                         
3760                         d = TALLOC_P( ctx, struct winbindd_tdc_domain );
3761                         if ( !d )
3762                                 break;                  
3763                         
3764                         d->domain_name = talloc_strdup( d, dom_list[i].domain_name );
3765                         d->dns_name = talloc_strdup( d, dom_list[i].dns_name );
3766                         sid_copy( &d->sid, &dom_list[i].sid );
3767                         d->trust_flags   = dom_list[i].trust_flags;
3768                         d->trust_type    = dom_list[i].trust_type;
3769                         d->trust_attribs = dom_list[i].trust_attribs;
3770
3771                         break;
3772                 }
3773         }
3774
3775         TALLOC_FREE( dom_list );
3776         
3777         return d;       
3778 }
3779
3780
3781 /*********************************************************************
3782  ********************************************************************/
3783
3784 void wcache_tdc_clear( void )
3785 {
3786         if ( !init_wcache() )
3787                 return;
3788
3789         wcache_tdc_store_list( NULL, 0 );
3790         
3791         return; 
3792 }
3793
3794
3795 /*********************************************************************
3796  ********************************************************************/
3797
3798 static void wcache_save_user_pwinfo(struct winbindd_domain *domain, 
3799                                     NTSTATUS status,
3800                                     const DOM_SID *user_sid,
3801                                     const char *homedir,
3802                                     const char *shell,
3803                                     const char *gecos,
3804                                     uint32 gid)
3805 {
3806         struct cache_entry *centry;
3807
3808         if ( (centry = centry_start(domain, status)) == NULL )
3809                 return;
3810
3811         centry_put_string( centry, homedir );
3812         centry_put_string( centry, shell );
3813         centry_put_string( centry, gecos );
3814         centry_put_uint32( centry, gid );
3815         
3816         centry_end(centry, "NSS/PWINFO/%s", sid_string_static(user_sid) );
3817
3818         DEBUG(10,("wcache_save_user_pwinfo: %s\n", sid_string_static(user_sid) ));
3819
3820         centry_free(centry);
3821 }
3822
3823 NTSTATUS nss_get_info_cached( struct winbindd_domain *domain, 
3824                               const DOM_SID *user_sid,
3825                               TALLOC_CTX *ctx,
3826                               ADS_STRUCT *ads, LDAPMessage *msg,
3827                               char **homedir, char **shell, char **gecos,
3828                               gid_t *p_gid)
3829 {
3830         struct winbind_cache *cache = get_cache(domain);
3831         struct cache_entry *centry = NULL;
3832         NTSTATUS nt_status;
3833
3834         if (!cache->tdb)
3835                 goto do_query;
3836
3837         centry = wcache_fetch(cache, domain, "NSS/PWINFO/%s", sid_string_static(user_sid));     
3838         
3839         if (!centry)
3840                 goto do_query;
3841
3842         *homedir = centry_string( centry, ctx );
3843         *shell   = centry_string( centry, ctx );
3844         *gecos   = centry_string( centry, ctx );
3845         *p_gid   = centry_uint32( centry );     
3846
3847         centry_free(centry);
3848
3849         DEBUG(10,("nss_get_info_cached: [Cached] - user_sid %s\n",
3850                   sid_string_static(user_sid)));
3851
3852         return NT_STATUS_OK;
3853
3854 do_query:
3855         
3856         nt_status = nss_get_info( domain->name, user_sid, ctx, ads, msg, 
3857                                   homedir, shell, gecos, p_gid );
3858
3859         if ( NT_STATUS_IS_OK(nt_status) ) {
3860                 wcache_save_user_pwinfo( domain, nt_status, user_sid,
3861                                          *homedir, *shell, *gecos, *p_gid );
3862         }       
3863
3864         if ( NT_STATUS_EQUAL( nt_status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND ) ) {
3865                 DEBUG(5,("nss_get_info_cached: Setting domain %s offline\n",
3866                          domain->name ));
3867                 set_domain_offline( domain );
3868         }
3869
3870         return nt_status;       
3871 }
3872
3873
3874 /* the cache backend methods are exposed via this structure */
3875 struct winbindd_methods cache_methods = {
3876         True,
3877         query_user_list,
3878         enum_dom_groups,
3879         enum_local_groups,
3880         name_to_sid,
3881         sid_to_name,
3882         rids_to_names,
3883         query_user,
3884         lookup_usergroups,
3885         lookup_useraliases,
3886         lookup_groupmem,
3887         sequence_number,
3888         lockout_policy,
3889         password_policy,
3890         trusted_domains
3891 };