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