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