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