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