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