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