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