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