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