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