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