s3: Do not free a string where we should not
[amitay/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(upper_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 (!cache->tdb) {
1306                 return NT_STATUS_INTERNAL_DB_ERROR;
1307         }
1308
1309         if (is_null_sid(sid)) {
1310                 return NT_STATUS_INVALID_SID;
1311         }
1312
1313         if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
1314                 return NT_STATUS_INVALID_SID;
1315         }
1316
1317         /* Try and get a salted cred first. If we can't
1318            fall back to an unsalted cred. */
1319
1320         centry = wcache_fetch(cache, domain, "CRED/%s",
1321                               sid_to_fstring(tmp, sid));
1322         if (!centry) {
1323                 DEBUG(10,("wcache_get_creds: entry for [CRED/%s] not found\n", 
1324                           sid_string_dbg(sid)));
1325                 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1326         }
1327
1328         /*
1329          * We don't use the time element at this moment,
1330          * but we have to consume it, so that we don't
1331          * neet to change the disk format of the cache.
1332          */
1333         (void)centry_time(centry);
1334
1335         /* In the salted case this isn't actually the nt_hash itself,
1336            but the MD5 of the salt + nt_hash. Let the caller
1337            sort this out. It can tell as we only return the cached_salt
1338            if we are returning a salted cred. */
1339
1340         *cached_nt_pass = (const uint8 *)centry_hash16(centry, mem_ctx);
1341         if (*cached_nt_pass == NULL) {
1342                 fstring sidstr;
1343
1344                 sid_to_fstring(sidstr, sid);
1345
1346                 /* Bad (old) cred cache. Delete and pretend we
1347                    don't have it. */
1348                 DEBUG(0,("wcache_get_creds: bad entry for [CRED/%s] - deleting\n", 
1349                                 sidstr));
1350                 wcache_delete("CRED/%s", sidstr);
1351                 centry_free(centry);
1352                 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1353         }
1354
1355         /* We only have 17 bytes more data in the salted cred case. */
1356         if (centry->len - centry->ofs == 17) {
1357                 *cached_salt = (const uint8 *)centry_hash16(centry, mem_ctx);
1358         } else {
1359                 *cached_salt = NULL;
1360         }
1361
1362         dump_data_pw("cached_nt_pass", *cached_nt_pass, NT_HASH_LEN);
1363         if (*cached_salt) {
1364                 dump_data_pw("cached_salt", *cached_salt, NT_HASH_LEN);
1365         }
1366
1367         status = centry->status;
1368
1369         DEBUG(10,("wcache_get_creds: [Cached] - cached creds for user %s status: %s\n",
1370                   sid_string_dbg(sid), nt_errstr(status) ));
1371
1372         centry_free(centry);
1373         return status;
1374 }
1375
1376 /* Store creds for a SID - only writes out new salted ones. */
1377
1378 NTSTATUS wcache_save_creds(struct winbindd_domain *domain, 
1379                            const struct dom_sid *sid,
1380                            const uint8 nt_pass[NT_HASH_LEN])
1381 {
1382         struct cache_entry *centry;
1383         fstring sid_string;
1384         uint32 rid;
1385         uint8 cred_salt[NT_HASH_LEN];
1386         uint8 salted_hash[NT_HASH_LEN];
1387
1388         if (is_null_sid(sid)) {
1389                 return NT_STATUS_INVALID_SID;
1390         }
1391
1392         if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
1393                 return NT_STATUS_INVALID_SID;
1394         }
1395
1396         centry = centry_start(domain, NT_STATUS_OK);
1397         if (!centry) {
1398                 return NT_STATUS_INTERNAL_DB_ERROR;
1399         }
1400
1401         dump_data_pw("nt_pass", nt_pass, NT_HASH_LEN);
1402
1403         centry_put_time(centry, time(NULL));
1404
1405         /* Create a salt and then salt the hash. */
1406         generate_random_buffer(cred_salt, NT_HASH_LEN);
1407         E_md5hash(cred_salt, nt_pass, salted_hash);
1408
1409         centry_put_hash16(centry, salted_hash);
1410         centry_put_hash16(centry, cred_salt);
1411         centry_end(centry, "CRED/%s", sid_to_fstring(sid_string, sid));
1412
1413         DEBUG(10,("wcache_save_creds: %s\n", sid_string));
1414
1415         centry_free(centry);
1416
1417         return NT_STATUS_OK;
1418 }
1419
1420
1421 /* Query display info. This is the basic user list fn */
1422 static NTSTATUS query_user_list(struct winbindd_domain *domain,
1423                                 TALLOC_CTX *mem_ctx,
1424                                 uint32 *num_entries, 
1425                                 struct wbint_userinfo **info)
1426 {
1427         struct winbind_cache *cache = get_cache(domain);
1428         struct cache_entry *centry = NULL;
1429         NTSTATUS status;
1430         unsigned int i, retry;
1431         bool old_status = domain->online;
1432
1433         if (!cache->tdb)
1434                 goto do_query;
1435
1436         centry = wcache_fetch(cache, domain, "UL/%s", domain->name);
1437         if (!centry)
1438                 goto do_query;
1439
1440 do_fetch_cache:
1441         *num_entries = centry_uint32(centry);
1442
1443         if (*num_entries == 0)
1444                 goto do_cached;
1445
1446         (*info) = talloc_array(mem_ctx, struct wbint_userinfo, *num_entries);
1447         if (! (*info)) {
1448                 smb_panic_fn("query_user_list out of memory");
1449         }
1450         for (i=0; i<(*num_entries); i++) {
1451                 (*info)[i].acct_name = centry_string(centry, mem_ctx);
1452                 (*info)[i].full_name = centry_string(centry, mem_ctx);
1453                 (*info)[i].homedir = centry_string(centry, mem_ctx);
1454                 (*info)[i].shell = centry_string(centry, mem_ctx);
1455                 centry_sid(centry, &(*info)[i].user_sid);
1456                 centry_sid(centry, &(*info)[i].group_sid);
1457         }
1458
1459 do_cached:      
1460         status = centry->status;
1461
1462         DEBUG(10,("query_user_list: [Cached] - cached list for domain %s status: %s\n",
1463                 domain->name, nt_errstr(status) ));
1464
1465         centry_free(centry);
1466         return status;
1467
1468 do_query:
1469         *num_entries = 0;
1470         *info = NULL;
1471
1472         /* Return status value returned by seq number check */
1473
1474         if (!NT_STATUS_IS_OK(domain->last_status))
1475                 return domain->last_status;
1476
1477         /* Put the query_user_list() in a retry loop.  There appears to be
1478          * some bug either with Windows 2000 or Samba's handling of large
1479          * rpc replies.  This manifests itself as sudden disconnection
1480          * at a random point in the enumeration of a large (60k) user list.
1481          * The retry loop simply tries the operation again. )-:  It's not
1482          * pretty but an acceptable workaround until we work out what the
1483          * real problem is. */
1484
1485         retry = 0;
1486         do {
1487
1488                 DEBUG(10,("query_user_list: [Cached] - doing backend query for list for domain %s\n",
1489                         domain->name ));
1490
1491                 status = domain->backend->query_user_list(domain, mem_ctx, num_entries, info);
1492                 if (!NT_STATUS_IS_OK(status)) {
1493                         DEBUG(3, ("query_user_list: returned 0x%08x, "
1494                                   "retrying\n", NT_STATUS_V(status)));
1495                 }
1496                 if (NT_STATUS_EQUAL(status, NT_STATUS_UNSUCCESSFUL)) {
1497                         DEBUG(3, ("query_user_list: flushing "
1498                                   "connection cache\n"));
1499                         invalidate_cm_connection(&domain->conn);
1500                 }
1501                 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1502                     NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1503                         if (!domain->internal && old_status) {
1504                                 set_domain_offline(domain);
1505                         }
1506                         /* store partial response. */
1507                         if (*num_entries > 0) {
1508                                 /*
1509                                  * humm, what about the status used for cache?
1510                                  * Should it be NT_STATUS_OK?
1511                                  */
1512                                 break;
1513                         }
1514                         /*
1515                          * domain is offline now, and there is no user entries,
1516                          * try to fetch from cache again.
1517                          */
1518                         if (cache->tdb && !domain->online && !domain->internal && old_status) {
1519                                 centry = wcache_fetch(cache, domain, "UL/%s", domain->name);
1520                                 /* partial response... */
1521                                 if (!centry) {
1522                                         goto skip_save;
1523                                 } else {
1524                                         goto do_fetch_cache;
1525                                 }
1526                         } else {
1527                                 goto skip_save;
1528                         }
1529                 }
1530
1531         } while (NT_STATUS_V(status) == NT_STATUS_V(NT_STATUS_UNSUCCESSFUL) && 
1532                  (retry++ < 5));
1533
1534         /* and save it */
1535         refresh_sequence_number(domain, false);
1536         if (!NT_STATUS_IS_OK(status)) {
1537                 return status;
1538         }
1539         centry = centry_start(domain, status);
1540         if (!centry)
1541                 goto skip_save;
1542         centry_put_uint32(centry, *num_entries);
1543         for (i=0; i<(*num_entries); i++) {
1544                 centry_put_string(centry, (*info)[i].acct_name);
1545                 centry_put_string(centry, (*info)[i].full_name);
1546                 centry_put_string(centry, (*info)[i].homedir);
1547                 centry_put_string(centry, (*info)[i].shell);
1548                 centry_put_sid(centry, &(*info)[i].user_sid);
1549                 centry_put_sid(centry, &(*info)[i].group_sid);
1550                 if (domain->backend && domain->backend->consistent) {
1551                         /* when the backend is consistent we can pre-prime some mappings */
1552                         wcache_save_name_to_sid(domain, NT_STATUS_OK, 
1553                                                 domain->name,
1554                                                 (*info)[i].acct_name, 
1555                                                 &(*info)[i].user_sid,
1556                                                 SID_NAME_USER);
1557                         wcache_save_sid_to_name(domain, NT_STATUS_OK, 
1558                                                 &(*info)[i].user_sid,
1559                                                 domain->name,
1560                                                 (*info)[i].acct_name, 
1561                                                 SID_NAME_USER);
1562                         wcache_save_user(domain, NT_STATUS_OK, &(*info)[i]);
1563                 }
1564         }       
1565         centry_end(centry, "UL/%s", domain->name);
1566         centry_free(centry);
1567
1568 skip_save:
1569         return status;
1570 }
1571
1572 /* list all domain groups */
1573 static NTSTATUS enum_dom_groups(struct winbindd_domain *domain,
1574                                 TALLOC_CTX *mem_ctx,
1575                                 uint32 *num_entries, 
1576                                 struct wb_acct_info **info)
1577 {
1578         struct winbind_cache *cache = get_cache(domain);
1579         struct cache_entry *centry = NULL;
1580         NTSTATUS status;
1581         unsigned int i;
1582         bool old_status;
1583
1584         old_status = domain->online;
1585         if (!cache->tdb)
1586                 goto do_query;
1587
1588         centry = wcache_fetch(cache, domain, "GL/%s/domain", domain->name);
1589         if (!centry)
1590                 goto do_query;
1591
1592 do_fetch_cache:
1593         *num_entries = centry_uint32(centry);
1594
1595         if (*num_entries == 0)
1596                 goto do_cached;
1597
1598         (*info) = talloc_array(mem_ctx, struct wb_acct_info, *num_entries);
1599         if (! (*info)) {
1600                 smb_panic_fn("enum_dom_groups out of memory");
1601         }
1602         for (i=0; i<(*num_entries); i++) {
1603                 fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx));
1604                 fstrcpy((*info)[i].acct_desc, centry_string(centry, mem_ctx));
1605                 (*info)[i].rid = centry_uint32(centry);
1606         }
1607
1608 do_cached:      
1609         status = centry->status;
1610
1611         DEBUG(10,("enum_dom_groups: [Cached] - cached list for domain %s status: %s\n",
1612                 domain->name, nt_errstr(status) ));
1613
1614         centry_free(centry);
1615         return status;
1616
1617 do_query:
1618         *num_entries = 0;
1619         *info = NULL;
1620
1621         /* Return status value returned by seq number check */
1622
1623         if (!NT_STATUS_IS_OK(domain->last_status))
1624                 return domain->last_status;
1625
1626         DEBUG(10,("enum_dom_groups: [Cached] - doing backend query for list for domain %s\n",
1627                 domain->name ));
1628
1629         status = domain->backend->enum_dom_groups(domain, mem_ctx, num_entries, info);
1630
1631         if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1632             NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1633                 if (!domain->internal && old_status) {
1634                         set_domain_offline(domain);
1635                 }
1636                 if (cache->tdb &&
1637                         !domain->online &&
1638                         !domain->internal &&
1639                         old_status) {
1640                         centry = wcache_fetch(cache, domain, "GL/%s/domain", domain->name);
1641                         if (centry) {
1642                                 goto do_fetch_cache;
1643                         }
1644                 }
1645         }
1646         /* and save it */
1647         refresh_sequence_number(domain, false);
1648         if (!NT_STATUS_IS_OK(status)) {
1649                 return status;
1650         }
1651         centry = centry_start(domain, status);
1652         if (!centry)
1653                 goto skip_save;
1654         centry_put_uint32(centry, *num_entries);
1655         for (i=0; i<(*num_entries); i++) {
1656                 centry_put_string(centry, (*info)[i].acct_name);
1657                 centry_put_string(centry, (*info)[i].acct_desc);
1658                 centry_put_uint32(centry, (*info)[i].rid);
1659         }       
1660         centry_end(centry, "GL/%s/domain", domain->name);
1661         centry_free(centry);
1662
1663 skip_save:
1664         return status;
1665 }
1666
1667 /* list all domain groups */
1668 static NTSTATUS enum_local_groups(struct winbindd_domain *domain,
1669                                 TALLOC_CTX *mem_ctx,
1670                                 uint32 *num_entries, 
1671                                 struct wb_acct_info **info)
1672 {
1673         struct winbind_cache *cache = get_cache(domain);
1674         struct cache_entry *centry = NULL;
1675         NTSTATUS status;
1676         unsigned int i;
1677         bool old_status;
1678
1679         old_status = domain->online;
1680         if (!cache->tdb)
1681                 goto do_query;
1682
1683         centry = wcache_fetch(cache, domain, "GL/%s/local", domain->name);
1684         if (!centry)
1685                 goto do_query;
1686
1687 do_fetch_cache:
1688         *num_entries = centry_uint32(centry);
1689
1690         if (*num_entries == 0)
1691                 goto do_cached;
1692
1693         (*info) = talloc_array(mem_ctx, struct wb_acct_info, *num_entries);
1694         if (! (*info)) {
1695                 smb_panic_fn("enum_dom_groups out of memory");
1696         }
1697         for (i=0; i<(*num_entries); i++) {
1698                 fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx));
1699                 fstrcpy((*info)[i].acct_desc, centry_string(centry, mem_ctx));
1700                 (*info)[i].rid = centry_uint32(centry);
1701         }
1702
1703 do_cached:      
1704
1705         /* If we are returning cached data and the domain controller
1706            is down then we don't know whether the data is up to date
1707            or not.  Return NT_STATUS_MORE_PROCESSING_REQUIRED to
1708            indicate this. */
1709
1710         if (wcache_server_down(domain)) {
1711                 DEBUG(10, ("enum_local_groups: returning cached user list and server was down\n"));
1712                 status = NT_STATUS_MORE_PROCESSING_REQUIRED;
1713         } else
1714                 status = centry->status;
1715
1716         DEBUG(10,("enum_local_groups: [Cached] - cached list for domain %s status: %s\n",
1717                 domain->name, nt_errstr(status) ));
1718
1719         centry_free(centry);
1720         return status;
1721
1722 do_query:
1723         *num_entries = 0;
1724         *info = NULL;
1725
1726         /* Return status value returned by seq number check */
1727
1728         if (!NT_STATUS_IS_OK(domain->last_status))
1729                 return domain->last_status;
1730
1731         DEBUG(10,("enum_local_groups: [Cached] - doing backend query for list for domain %s\n",
1732                 domain->name ));
1733
1734         status = domain->backend->enum_local_groups(domain, mem_ctx, num_entries, info);
1735
1736         if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1737                 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1738                 if (!domain->internal && old_status) {
1739                         set_domain_offline(domain);
1740                 }
1741                 if (cache->tdb &&
1742                         !domain->internal &&
1743                         !domain->online &&
1744                         old_status) {
1745                         centry = wcache_fetch(cache, domain, "GL/%s/local", domain->name);
1746                         if (centry) {
1747                                 goto do_fetch_cache;
1748                         }
1749                 }
1750         }
1751         /* and save it */
1752         refresh_sequence_number(domain, false);
1753         if (!NT_STATUS_IS_OK(status)) {
1754                 return status;
1755         }
1756         centry = centry_start(domain, status);
1757         if (!centry)
1758                 goto skip_save;
1759         centry_put_uint32(centry, *num_entries);
1760         for (i=0; i<(*num_entries); i++) {
1761                 centry_put_string(centry, (*info)[i].acct_name);
1762                 centry_put_string(centry, (*info)[i].acct_desc);
1763                 centry_put_uint32(centry, (*info)[i].rid);
1764         }
1765         centry_end(centry, "GL/%s/local", domain->name);
1766         centry_free(centry);
1767
1768 skip_save:
1769         return status;
1770 }
1771
1772 NTSTATUS wcache_name_to_sid(struct winbindd_domain *domain,
1773                             const char *domain_name,
1774                             const char *name,
1775                             struct dom_sid *sid,
1776                             enum lsa_SidType *type)
1777 {
1778         struct winbind_cache *cache = get_cache(domain);
1779         struct cache_entry *centry;
1780         NTSTATUS status;
1781         char *uname;
1782
1783         if (cache->tdb == NULL) {
1784                 return NT_STATUS_NOT_FOUND;
1785         }
1786
1787         uname = talloc_strdup_upper(talloc_tos(), name);
1788         if (uname == NULL) {
1789                 return NT_STATUS_NO_MEMORY;
1790         }
1791
1792         centry = wcache_fetch(cache, domain, "NS/%s/%s", domain_name, uname);
1793         TALLOC_FREE(uname);
1794         if (centry == NULL) {
1795                 return NT_STATUS_NOT_FOUND;
1796         }
1797
1798         status = centry->status;
1799         if (NT_STATUS_IS_OK(status)) {
1800                 *type = (enum lsa_SidType)centry_uint32(centry);
1801                 centry_sid(centry, sid);
1802         }
1803
1804         DEBUG(10,("name_to_sid: [Cached] - cached name for domain %s status: "
1805                   "%s\n", domain->name, nt_errstr(status) ));
1806
1807         centry_free(centry);
1808         return status;
1809 }
1810
1811 /* convert a single name to a sid in a domain */
1812 static NTSTATUS name_to_sid(struct winbindd_domain *domain,
1813                             TALLOC_CTX *mem_ctx,
1814                             const char *domain_name,
1815                             const char *name,
1816                             uint32_t flags,
1817                             struct dom_sid *sid,
1818                             enum lsa_SidType *type)
1819 {
1820         NTSTATUS status;
1821         bool old_status;
1822
1823         old_status = domain->online;
1824
1825         status = wcache_name_to_sid(domain, domain_name, name, sid, type);
1826         if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
1827                 return status;
1828         }
1829
1830         ZERO_STRUCTP(sid);
1831
1832         /* If the seq number check indicated that there is a problem
1833          * with this DC, then return that status... except for
1834          * access_denied.  This is special because the dc may be in
1835          * "restrict anonymous = 1" mode, in which case it will deny
1836          * most unauthenticated operations, but *will* allow the LSA
1837          * name-to-sid that we try as a fallback. */
1838
1839         if (!(NT_STATUS_IS_OK(domain->last_status)
1840               || NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)))
1841                 return domain->last_status;
1842
1843         DEBUG(10,("name_to_sid: [Cached] - doing backend query for name for domain %s\n",
1844                 domain->name ));
1845
1846         status = domain->backend->name_to_sid(domain, mem_ctx, domain_name,
1847                                               name, flags, sid, type);
1848
1849         if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1850                 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1851                 if (!domain->internal && old_status) {
1852                         set_domain_offline(domain);
1853                 }
1854                 if (!domain->internal &&
1855                         !domain->online &&
1856                         old_status) {
1857                         NTSTATUS cache_status;
1858                         cache_status = wcache_name_to_sid(domain, domain_name, name, sid, type);
1859                         return cache_status;
1860                 }
1861         }
1862         /* and save it */
1863         refresh_sequence_number(domain, false);
1864
1865         if (domain->online &&
1866             (NT_STATUS_IS_OK(status) || NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED))) {
1867                 wcache_save_name_to_sid(domain, status, domain_name, name, sid, *type);
1868
1869                 /* Only save the reverse mapping if this was not a UPN */
1870                 if (!strchr(name, '@')) {
1871                         if (!strupper_m(discard_const_p(char, domain_name))) {
1872                                 return NT_STATUS_INVALID_PARAMETER;
1873                         }
1874                         (void)strlower_m(discard_const_p(char, name));
1875                         wcache_save_sid_to_name(domain, status, sid, domain_name, name, *type);
1876                 }
1877         }
1878
1879         return status;
1880 }
1881
1882 NTSTATUS wcache_sid_to_name(struct winbindd_domain *domain,
1883                             const struct dom_sid *sid,
1884                             TALLOC_CTX *mem_ctx,
1885                             char **domain_name,
1886                             char **name,
1887                             enum lsa_SidType *type)
1888 {
1889         struct winbind_cache *cache = get_cache(domain);
1890         struct cache_entry *centry;
1891         char *sid_string;
1892         NTSTATUS status;
1893
1894         if (cache->tdb == NULL) {
1895                 return NT_STATUS_NOT_FOUND;
1896         }
1897
1898         sid_string = sid_string_tos(sid);
1899         if (sid_string == NULL) {
1900                 return NT_STATUS_NO_MEMORY;
1901         }
1902
1903         centry = wcache_fetch(cache, domain, "SN/%s", sid_string);
1904         TALLOC_FREE(sid_string);
1905         if (centry == NULL) {
1906                 return NT_STATUS_NOT_FOUND;
1907         }
1908
1909         if (NT_STATUS_IS_OK(centry->status)) {
1910                 *type = (enum lsa_SidType)centry_uint32(centry);
1911                 *domain_name = centry_string(centry, mem_ctx);
1912                 *name = centry_string(centry, mem_ctx);
1913         }
1914
1915         status = centry->status;
1916         centry_free(centry);
1917
1918         DEBUG(10,("sid_to_name: [Cached] - cached name for domain %s status: "
1919                   "%s\n", domain->name, nt_errstr(status) ));
1920
1921         return status;
1922 }
1923
1924 /* convert a sid to a user or group name. The sid is guaranteed to be in the domain
1925    given */
1926 static NTSTATUS sid_to_name(struct winbindd_domain *domain,
1927                             TALLOC_CTX *mem_ctx,
1928                             const struct dom_sid *sid,
1929                             char **domain_name,
1930                             char **name,
1931                             enum lsa_SidType *type)
1932 {
1933         NTSTATUS status;
1934         bool old_status;
1935
1936         old_status = domain->online;
1937         status = wcache_sid_to_name(domain, sid, mem_ctx, domain_name, name,
1938                                     type);
1939         if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
1940                 return status;
1941         }
1942
1943         *name = NULL;
1944         *domain_name = NULL;
1945
1946         /* If the seq number check indicated that there is a problem
1947          * with this DC, then return that status... except for
1948          * access_denied.  This is special because the dc may be in
1949          * "restrict anonymous = 1" mode, in which case it will deny
1950          * most unauthenticated operations, but *will* allow the LSA
1951          * sid-to-name that we try as a fallback. */
1952
1953         if (!(NT_STATUS_IS_OK(domain->last_status)
1954               || NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)))
1955                 return domain->last_status;
1956
1957         DEBUG(10,("sid_to_name: [Cached] - doing backend query for name for domain %s\n",
1958                 domain->name ));
1959
1960         status = domain->backend->sid_to_name(domain, mem_ctx, sid, domain_name, name, type);
1961
1962         if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1963                 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1964                 if (!domain->internal && old_status) {
1965                         set_domain_offline(domain);
1966                 }
1967                 if (!domain->internal &&
1968                         !domain->online &&
1969                         old_status) {
1970                         NTSTATUS cache_status;
1971                         cache_status = wcache_sid_to_name(domain, sid, mem_ctx,
1972                                                         domain_name, name, type);
1973                         return cache_status;
1974                 }
1975         }
1976         /* and save it */
1977         refresh_sequence_number(domain, false);
1978         if (!NT_STATUS_IS_OK(status)) {
1979                 return status;
1980         }
1981         wcache_save_sid_to_name(domain, status, sid, *domain_name, *name, *type);
1982
1983         /* We can't save the name to sid mapping here, as with sid history a
1984          * later name2sid would give the wrong sid. */
1985
1986         return status;
1987 }
1988
1989 static NTSTATUS rids_to_names(struct winbindd_domain *domain,
1990                               TALLOC_CTX *mem_ctx,
1991                               const struct dom_sid *domain_sid,
1992                               uint32 *rids,
1993                               size_t num_rids,
1994                               char **domain_name,
1995                               char ***names,
1996                               enum lsa_SidType **types)
1997 {
1998         struct winbind_cache *cache = get_cache(domain);
1999         size_t i;
2000         NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
2001         bool have_mapped;
2002         bool have_unmapped;
2003         bool old_status;
2004
2005         old_status = domain->online;
2006         *domain_name = NULL;
2007         *names = NULL;
2008         *types = NULL;
2009
2010         if (!cache->tdb) {
2011                 goto do_query;
2012         }
2013
2014         if (num_rids == 0) {
2015                 return NT_STATUS_OK;
2016         }
2017
2018         *names = talloc_array(mem_ctx, char *, num_rids);
2019         *types = talloc_array(mem_ctx, enum lsa_SidType, num_rids);
2020
2021         if ((*names == NULL) || (*types == NULL)) {
2022                 result = NT_STATUS_NO_MEMORY;
2023                 goto error;
2024         }
2025
2026         have_mapped = have_unmapped = false;
2027
2028         for (i=0; i<num_rids; i++) {
2029                 struct dom_sid sid;
2030                 struct cache_entry *centry;
2031                 fstring tmp;
2032
2033                 if (!sid_compose(&sid, domain_sid, rids[i])) {
2034                         result = NT_STATUS_INTERNAL_ERROR;
2035                         goto error;
2036                 }
2037
2038                 centry = wcache_fetch(cache, domain, "SN/%s",
2039                                       sid_to_fstring(tmp, &sid));
2040                 if (!centry) {
2041                         goto do_query;
2042                 }
2043
2044                 (*types)[i] = SID_NAME_UNKNOWN;
2045                 (*names)[i] = talloc_strdup(*names, "");
2046
2047                 if (NT_STATUS_IS_OK(centry->status)) {
2048                         char *dom;
2049                         have_mapped = true;
2050                         (*types)[i] = (enum lsa_SidType)centry_uint32(centry);
2051
2052                         dom = centry_string(centry, mem_ctx);
2053                         if (*domain_name == NULL) {
2054                                 *domain_name = dom;
2055                         } else {
2056                                 talloc_free(dom);
2057                         }
2058
2059                         (*names)[i] = centry_string(centry, *names);
2060
2061                 } else if (NT_STATUS_EQUAL(centry->status, NT_STATUS_NONE_MAPPED)
2062                            || NT_STATUS_EQUAL(centry->status, STATUS_SOME_UNMAPPED)) {
2063                         have_unmapped = true;
2064
2065                 } else {
2066                         /* something's definitely wrong */
2067                         result = centry->status;
2068                         goto error;
2069                 }
2070
2071                 centry_free(centry);
2072         }
2073
2074         if (!have_mapped) {
2075                 return NT_STATUS_NONE_MAPPED;
2076         }
2077         if (!have_unmapped) {
2078                 return NT_STATUS_OK;
2079         }
2080         return STATUS_SOME_UNMAPPED;
2081
2082  do_query:
2083
2084         TALLOC_FREE(*names);
2085         TALLOC_FREE(*types);
2086
2087         result = domain->backend->rids_to_names(domain, mem_ctx, domain_sid,
2088                                                 rids, num_rids, domain_name,
2089                                                 names, types);
2090
2091         if (NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
2092                 NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2093                 if (!domain->internal && old_status) {
2094                         set_domain_offline(domain);
2095                 }
2096                 if (cache->tdb &&
2097                         !domain->internal &&
2098                         !domain->online &&
2099                         old_status) {
2100                         have_mapped = have_unmapped = false;
2101
2102                         for (i=0; i<num_rids; i++) {
2103                                 struct dom_sid sid;
2104                                 struct cache_entry *centry;
2105                                 fstring tmp;
2106
2107                                 if (!sid_compose(&sid, domain_sid, rids[i])) {
2108                                         result = NT_STATUS_INTERNAL_ERROR;
2109                                         goto error;
2110                                 }
2111
2112                                 centry = wcache_fetch(cache, domain, "SN/%s",
2113                                                       sid_to_fstring(tmp, &sid));
2114                                 if (!centry) {
2115                                         (*types)[i] = SID_NAME_UNKNOWN;
2116                                         (*names)[i] = talloc_strdup(*names, "");
2117                                         continue;
2118                                 }
2119
2120                                 (*types)[i] = SID_NAME_UNKNOWN;
2121                                 (*names)[i] = talloc_strdup(*names, "");
2122
2123                                 if (NT_STATUS_IS_OK(centry->status)) {
2124                                         char *dom;
2125                                         have_mapped = true;
2126                                         (*types)[i] = (enum lsa_SidType)centry_uint32(centry);
2127
2128                                         dom = centry_string(centry, mem_ctx);
2129                                         if (*domain_name == NULL) {
2130                                                 *domain_name = dom;
2131                                         } else {
2132                                                 talloc_free(dom);
2133                                         }
2134
2135                                         (*names)[i] = centry_string(centry, *names);
2136
2137                                 } else if (NT_STATUS_EQUAL(centry->status, NT_STATUS_NONE_MAPPED)) {
2138                                         have_unmapped = true;
2139
2140                                 } else {
2141                                         /* something's definitely wrong */
2142                                         result = centry->status;
2143                                         goto error;
2144                                 }
2145
2146                                 centry_free(centry);
2147                         }
2148
2149                         if (!have_mapped) {
2150                                 return NT_STATUS_NONE_MAPPED;
2151                         }
2152                         if (!have_unmapped) {
2153                                 return NT_STATUS_OK;
2154                         }
2155                         return STATUS_SOME_UNMAPPED;
2156                 }
2157         }
2158         /*
2159           None of the queried rids has been found so save all negative entries
2160         */
2161         if (NT_STATUS_EQUAL(result, NT_STATUS_NONE_MAPPED)) {
2162                 for (i = 0; i < num_rids; i++) {
2163                         struct dom_sid sid;
2164                         const char *name = "";
2165                         const enum lsa_SidType type = SID_NAME_UNKNOWN;
2166                         NTSTATUS status = NT_STATUS_NONE_MAPPED;
2167
2168                         if (!sid_compose(&sid, domain_sid, rids[i])) {
2169                                 return NT_STATUS_INTERNAL_ERROR;
2170                         }
2171
2172                         wcache_save_sid_to_name(domain, status, &sid, *domain_name,
2173                                                 name, type);
2174                 }
2175
2176                 return result;
2177         }
2178
2179         /*
2180           Some or all of the queried rids have been found.
2181         */
2182         if (!NT_STATUS_IS_OK(result) &&
2183             !NT_STATUS_EQUAL(result, STATUS_SOME_UNMAPPED)) {
2184                 return result;
2185         }
2186
2187         refresh_sequence_number(domain, false);
2188
2189         for (i=0; i<num_rids; i++) {
2190                 struct dom_sid sid;
2191                 NTSTATUS status;
2192
2193                 if (!sid_compose(&sid, domain_sid, rids[i])) {
2194                         result = NT_STATUS_INTERNAL_ERROR;
2195                         goto error;
2196                 }
2197
2198                 status = (*types)[i] == SID_NAME_UNKNOWN ?
2199                         NT_STATUS_NONE_MAPPED : NT_STATUS_OK;
2200
2201                 wcache_save_sid_to_name(domain, status, &sid, *domain_name,
2202                                         (*names)[i], (*types)[i]);
2203         }
2204
2205         return result;
2206
2207  error:
2208         TALLOC_FREE(*names);
2209         TALLOC_FREE(*types);
2210         return result;
2211 }
2212
2213 NTSTATUS wcache_query_user(struct winbindd_domain *domain,
2214                            TALLOC_CTX *mem_ctx,
2215                            const struct dom_sid *user_sid,
2216                            struct wbint_userinfo *info)
2217 {
2218         struct winbind_cache *cache = get_cache(domain);
2219         struct cache_entry *centry = NULL;
2220         NTSTATUS status;
2221         char *sid_string;
2222
2223         if (cache->tdb == NULL) {
2224                 return NT_STATUS_NOT_FOUND;
2225         }
2226
2227         sid_string = sid_string_tos(user_sid);
2228         if (sid_string == NULL) {
2229                 return NT_STATUS_NO_MEMORY;
2230         }
2231
2232         centry = wcache_fetch(cache, domain, "U/%s", sid_string);
2233         TALLOC_FREE(sid_string);
2234         if (centry == NULL) {
2235                 return NT_STATUS_NOT_FOUND;
2236         }
2237
2238         /*
2239          * If we have an access denied cache entry and a cached info3
2240          * in the samlogon cache then do a query.  This will force the
2241          * rpc back end to return the info3 data.
2242          */
2243
2244         if (NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED) &&
2245             netsamlogon_cache_have(user_sid)) {
2246                 DEBUG(10, ("query_user: cached access denied and have cached "
2247                            "info3\n"));
2248                 domain->last_status = NT_STATUS_OK;
2249                 centry_free(centry);
2250                 return NT_STATUS_NOT_FOUND;
2251         }
2252
2253         /* if status is not ok then this is a negative hit
2254            and the rest of the data doesn't matter */
2255         status = centry->status;
2256         if (NT_STATUS_IS_OK(status)) {
2257                 info->acct_name = centry_string(centry, mem_ctx);
2258                 info->full_name = centry_string(centry, mem_ctx);
2259                 info->homedir = centry_string(centry, mem_ctx);
2260                 info->shell = centry_string(centry, mem_ctx);
2261                 info->primary_gid = centry_uint32(centry);
2262                 centry_sid(centry, &info->user_sid);
2263                 centry_sid(centry, &info->group_sid);
2264         }
2265
2266         DEBUG(10,("query_user: [Cached] - cached info for domain %s status: "
2267                   "%s\n", domain->name, nt_errstr(status) ));
2268
2269         centry_free(centry);
2270         return status;
2271 }
2272
2273 /* Lookup user information from a rid */
2274 static NTSTATUS query_user(struct winbindd_domain *domain,
2275                            TALLOC_CTX *mem_ctx,
2276                            const struct dom_sid *user_sid,
2277                            struct wbint_userinfo *info)
2278 {
2279         NTSTATUS status;
2280         bool old_status;
2281
2282         old_status = domain->online;
2283         status = wcache_query_user(domain, mem_ctx, user_sid, info);
2284         if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2285                 return status;
2286         }
2287
2288         ZERO_STRUCTP(info);
2289
2290         /* Return status value returned by seq number check */
2291
2292         if (!NT_STATUS_IS_OK(domain->last_status))
2293                 return domain->last_status;
2294
2295         DEBUG(10,("query_user: [Cached] - doing backend query for info for domain %s\n",
2296                 domain->name ));
2297
2298         status = domain->backend->query_user(domain, mem_ctx, user_sid, info);
2299
2300         if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2301                 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2302                 if (!domain->internal && old_status) {
2303                         set_domain_offline(domain);
2304                 }
2305                 if (!domain->internal &&
2306                         !domain->online &&
2307                         old_status) {
2308                         NTSTATUS cache_status;
2309                         cache_status = wcache_query_user(domain, mem_ctx, user_sid, info);
2310                         return cache_status;
2311                 }
2312         }
2313         /* and save it */
2314         refresh_sequence_number(domain, false);
2315         if (!NT_STATUS_IS_OK(status)) {
2316                 return status;
2317         }
2318         wcache_save_user(domain, status, info);
2319
2320         return status;
2321 }
2322
2323 NTSTATUS wcache_lookup_usergroups(struct winbindd_domain *domain,
2324                                   TALLOC_CTX *mem_ctx,
2325                                   const struct dom_sid *user_sid,
2326                                   uint32_t *pnum_sids,
2327                                   struct dom_sid **psids)
2328 {
2329         struct winbind_cache *cache = get_cache(domain);
2330         struct cache_entry *centry = NULL;
2331         NTSTATUS status;
2332         uint32_t i, num_sids;
2333         struct dom_sid *sids;
2334         fstring sid_string;
2335
2336         if (cache->tdb == NULL) {
2337                 return NT_STATUS_NOT_FOUND;
2338         }
2339
2340         centry = wcache_fetch(cache, domain, "UG/%s",
2341                               sid_to_fstring(sid_string, user_sid));
2342         if (centry == NULL) {
2343                 return NT_STATUS_NOT_FOUND;
2344         }
2345
2346         /* If we have an access denied cache entry and a cached info3 in the
2347            samlogon cache then do a query.  This will force the rpc back end
2348            to return the info3 data. */
2349
2350         if (NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)
2351             && netsamlogon_cache_have(user_sid)) {
2352                 DEBUG(10, ("lookup_usergroups: cached access denied and have "
2353                            "cached info3\n"));
2354                 domain->last_status = NT_STATUS_OK;
2355                 centry_free(centry);
2356                 return NT_STATUS_NOT_FOUND;
2357         }
2358
2359         num_sids = centry_uint32(centry);
2360         sids = talloc_array(mem_ctx, struct dom_sid, num_sids);
2361         if (sids == NULL) {
2362                 centry_free(centry);
2363                 return NT_STATUS_NO_MEMORY;
2364         }
2365
2366         for (i=0; i<num_sids; i++) {
2367                 centry_sid(centry, &sids[i]);
2368         }
2369
2370         status = centry->status;
2371
2372         DEBUG(10,("lookup_usergroups: [Cached] - cached info for domain %s "
2373                   "status: %s\n", domain->name, nt_errstr(status)));
2374
2375         centry_free(centry);
2376
2377         *pnum_sids = num_sids;
2378         *psids = sids;
2379         return status;
2380 }
2381
2382 /* Lookup groups a user is a member of. */
2383 static NTSTATUS lookup_usergroups(struct winbindd_domain *domain,
2384                                   TALLOC_CTX *mem_ctx,
2385                                   const struct dom_sid *user_sid,
2386                                   uint32 *num_groups, struct dom_sid **user_gids)
2387 {
2388         struct cache_entry *centry = NULL;
2389         NTSTATUS status;
2390         unsigned int i;
2391         fstring sid_string;
2392         bool old_status;
2393
2394         old_status = domain->online;
2395         status = wcache_lookup_usergroups(domain, mem_ctx, user_sid,
2396                                           num_groups, user_gids);
2397         if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2398                 return status;
2399         }
2400
2401         (*num_groups) = 0;
2402         (*user_gids) = NULL;
2403
2404         /* Return status value returned by seq number check */
2405
2406         if (!NT_STATUS_IS_OK(domain->last_status))
2407                 return domain->last_status;
2408
2409         DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info for domain %s\n",
2410                 domain->name ));
2411
2412         status = domain->backend->lookup_usergroups(domain, mem_ctx, user_sid, num_groups, user_gids);
2413
2414         if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2415                 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2416                 if (!domain->internal && old_status) {
2417                         set_domain_offline(domain);
2418                 }
2419                 if (!domain->internal &&
2420                         !domain->online &&
2421                         old_status) {
2422                         NTSTATUS cache_status;
2423                         cache_status = wcache_lookup_usergroups(domain, mem_ctx, user_sid,
2424                                                           num_groups, user_gids);
2425                         return cache_status;
2426                 }
2427         }
2428         if ( NT_STATUS_EQUAL(status, NT_STATUS_SYNCHRONIZATION_REQUIRED) )
2429                 goto skip_save;
2430
2431         /* and save it */
2432         refresh_sequence_number(domain, false);
2433         if (!NT_STATUS_IS_OK(status)) {
2434                 return status;
2435         }
2436         centry = centry_start(domain, status);
2437         if (!centry)
2438                 goto skip_save;
2439
2440         centry_put_uint32(centry, *num_groups);
2441         for (i=0; i<(*num_groups); i++) {
2442                 centry_put_sid(centry, &(*user_gids)[i]);
2443         }       
2444
2445         centry_end(centry, "UG/%s", sid_to_fstring(sid_string, user_sid));
2446         centry_free(centry);
2447
2448 skip_save:
2449         return status;
2450 }
2451
2452 static char *wcache_make_sidlist(TALLOC_CTX *mem_ctx, uint32_t num_sids,
2453                                  const struct dom_sid *sids)
2454 {
2455         uint32_t i;
2456         char *sidlist;
2457
2458         sidlist = talloc_strdup(mem_ctx, "");
2459         if (sidlist == NULL) {
2460                 return NULL;
2461         }
2462         for (i=0; i<num_sids; i++) {
2463                 fstring tmp;
2464                 sidlist = talloc_asprintf_append_buffer(
2465                         sidlist, "/%s", sid_to_fstring(tmp, &sids[i]));
2466                 if (sidlist == NULL) {
2467                         return NULL;
2468                 }
2469         }
2470         return sidlist;
2471 }
2472
2473 NTSTATUS wcache_lookup_useraliases(struct winbindd_domain *domain,
2474                                    TALLOC_CTX *mem_ctx, uint32_t num_sids,
2475                                    const struct dom_sid *sids,
2476                                    uint32_t *pnum_aliases, uint32_t **paliases)
2477 {
2478         struct winbind_cache *cache = get_cache(domain);
2479         struct cache_entry *centry = NULL;
2480         uint32_t num_aliases;
2481         uint32_t *aliases;
2482         NTSTATUS status;
2483         char *sidlist;
2484         int i;
2485
2486         if (cache->tdb == NULL) {
2487                 return NT_STATUS_NOT_FOUND;
2488         }
2489
2490         if (num_sids == 0) {
2491                 *pnum_aliases = 0;
2492                 *paliases = NULL;
2493                 return NT_STATUS_OK;
2494         }
2495
2496         /* We need to cache indexed by the whole list of SIDs, the aliases
2497          * resulting might come from any of the SIDs. */
2498
2499         sidlist = wcache_make_sidlist(talloc_tos(), num_sids, sids);
2500         if (sidlist == NULL) {
2501                 return NT_STATUS_NO_MEMORY;
2502         }
2503
2504         centry = wcache_fetch(cache, domain, "UA%s", sidlist);
2505         TALLOC_FREE(sidlist);
2506         if (centry == NULL) {
2507                 return NT_STATUS_NOT_FOUND;
2508         }
2509
2510         num_aliases = centry_uint32(centry);
2511         aliases = talloc_array(mem_ctx, uint32_t, num_aliases);
2512         if (aliases == NULL) {
2513                 centry_free(centry);
2514                 return NT_STATUS_NO_MEMORY;
2515         }
2516
2517         for (i=0; i<num_aliases; i++) {
2518                 aliases[i] = centry_uint32(centry);
2519         }
2520
2521         status = centry->status;
2522
2523         DEBUG(10,("lookup_useraliases: [Cached] - cached info for domain: %s "
2524                   "status %s\n", domain->name, nt_errstr(status)));
2525
2526         centry_free(centry);
2527
2528         *pnum_aliases = num_aliases;
2529         *paliases = aliases;
2530
2531         return status;
2532 }
2533
2534 static NTSTATUS lookup_useraliases(struct winbindd_domain *domain,
2535                                    TALLOC_CTX *mem_ctx,
2536                                    uint32 num_sids, const struct dom_sid *sids,
2537                                    uint32 *num_aliases, uint32 **alias_rids)
2538 {
2539         struct cache_entry *centry = NULL;
2540         NTSTATUS status;
2541         char *sidlist;
2542         int i;
2543         bool old_status;
2544
2545         old_status = domain->online;
2546         status = wcache_lookup_useraliases(domain, mem_ctx, num_sids, sids,
2547                                            num_aliases, alias_rids);
2548         if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2549                 return status;
2550         }
2551
2552         (*num_aliases) = 0;
2553         (*alias_rids) = NULL;
2554
2555         if (!NT_STATUS_IS_OK(domain->last_status))
2556                 return domain->last_status;
2557
2558         DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info "
2559                   "for domain %s\n", domain->name ));
2560
2561         sidlist = wcache_make_sidlist(talloc_tos(), num_sids, sids);
2562         if (sidlist == NULL) {
2563                 return NT_STATUS_NO_MEMORY;
2564         }
2565
2566         status = domain->backend->lookup_useraliases(domain, mem_ctx,
2567                                                      num_sids, sids,
2568                                                      num_aliases, alias_rids);
2569
2570         if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2571                 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2572                 if (!domain->internal && old_status) {
2573                         set_domain_offline(domain);
2574                 }
2575                 if (!domain->internal &&
2576                         !domain->online &&
2577                         old_status) {
2578                         NTSTATUS cache_status;
2579                         cache_status = wcache_lookup_useraliases(domain, mem_ctx, num_sids,
2580                                                                  sids, num_aliases, alias_rids);
2581                         return cache_status;
2582                 }
2583         }
2584         /* and save it */
2585         refresh_sequence_number(domain, false);
2586         if (!NT_STATUS_IS_OK(status)) {
2587                 return status;
2588         }
2589         centry = centry_start(domain, status);
2590         if (!centry)
2591                 goto skip_save;
2592         centry_put_uint32(centry, *num_aliases);
2593         for (i=0; i<(*num_aliases); i++)
2594                 centry_put_uint32(centry, (*alias_rids)[i]);
2595         centry_end(centry, "UA%s", sidlist);
2596         centry_free(centry);
2597
2598  skip_save:
2599         return status;
2600 }
2601
2602 NTSTATUS wcache_lookup_groupmem(struct winbindd_domain *domain,
2603                                 TALLOC_CTX *mem_ctx,
2604                                 const struct dom_sid *group_sid,
2605                                 uint32_t *num_names,
2606                                 struct dom_sid **sid_mem, char ***names,
2607                                 uint32_t **name_types)
2608 {
2609         struct winbind_cache *cache = get_cache(domain);
2610         struct cache_entry *centry = NULL;
2611         NTSTATUS status;
2612         unsigned int i;
2613         char *sid_string;
2614
2615         if (cache->tdb == NULL) {
2616                 return NT_STATUS_NOT_FOUND;
2617         }
2618
2619         sid_string = sid_string_tos(group_sid);
2620         if (sid_string == NULL) {
2621                 return NT_STATUS_NO_MEMORY;
2622         }
2623
2624         centry = wcache_fetch(cache, domain, "GM/%s", sid_string);
2625         TALLOC_FREE(sid_string);
2626         if (centry == NULL) {
2627                 return NT_STATUS_NOT_FOUND;
2628         }
2629
2630         *sid_mem = NULL;
2631         *names = NULL;
2632         *name_types = NULL;
2633
2634         *num_names = centry_uint32(centry);
2635         if (*num_names == 0) {
2636                 centry_free(centry);
2637                 return NT_STATUS_OK;
2638         }
2639
2640         *sid_mem = talloc_array(mem_ctx, struct dom_sid, *num_names);
2641         *names = talloc_array(mem_ctx, char *, *num_names);
2642         *name_types = talloc_array(mem_ctx, uint32, *num_names);
2643
2644         if ((*sid_mem == NULL) || (*names == NULL) || (*name_types == NULL)) {
2645                 TALLOC_FREE(*sid_mem);
2646                 TALLOC_FREE(*names);
2647                 TALLOC_FREE(*name_types);
2648                 centry_free(centry);
2649                 return NT_STATUS_NO_MEMORY;
2650         }
2651
2652         for (i=0; i<(*num_names); i++) {
2653                 centry_sid(centry, &(*sid_mem)[i]);
2654                 (*names)[i] = centry_string(centry, mem_ctx);
2655                 (*name_types)[i] = centry_uint32(centry);
2656         }
2657
2658         status = centry->status;
2659
2660         DEBUG(10,("lookup_groupmem: [Cached] - cached info for domain %s "
2661                   "status: %s\n", domain->name, nt_errstr(status)));
2662
2663         centry_free(centry);
2664         return status;
2665 }
2666
2667 static NTSTATUS lookup_groupmem(struct winbindd_domain *domain,
2668                                 TALLOC_CTX *mem_ctx,
2669                                 const struct dom_sid *group_sid,
2670                                 enum lsa_SidType type,
2671                                 uint32 *num_names,
2672                                 struct dom_sid **sid_mem, char ***names,
2673                                 uint32 **name_types)
2674 {
2675         struct cache_entry *centry = NULL;
2676         NTSTATUS status;
2677         unsigned int i;
2678         fstring sid_string;
2679         bool old_status;
2680
2681         old_status = domain->online;
2682         status = wcache_lookup_groupmem(domain, mem_ctx, group_sid, num_names,
2683                                         sid_mem, names, name_types);
2684         if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2685                 return status;
2686         }
2687
2688         (*num_names) = 0;
2689         (*sid_mem) = NULL;
2690         (*names) = NULL;
2691         (*name_types) = NULL;
2692
2693         /* Return status value returned by seq number check */
2694
2695         if (!NT_STATUS_IS_OK(domain->last_status))
2696                 return domain->last_status;
2697
2698         DEBUG(10,("lookup_groupmem: [Cached] - doing backend query for info for domain %s\n",
2699                 domain->name ));
2700
2701         status = domain->backend->lookup_groupmem(domain, mem_ctx, group_sid,
2702                                                   type, num_names,
2703                                                   sid_mem, names, name_types);
2704
2705         if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2706                 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2707                 if (!domain->internal && old_status) {
2708                         set_domain_offline(domain);
2709                 }
2710                 if (!domain->internal &&
2711                         !domain->online &&
2712                         old_status) {
2713                         NTSTATUS cache_status;
2714                         cache_status = wcache_lookup_groupmem(domain, mem_ctx, group_sid,
2715                                                               num_names, sid_mem, names,
2716                                                               name_types);
2717                         return cache_status;
2718                 }
2719         }
2720         /* and save it */
2721         refresh_sequence_number(domain, false);
2722         if (!NT_STATUS_IS_OK(status)) {
2723                 return status;
2724         }
2725         centry = centry_start(domain, status);
2726         if (!centry)
2727                 goto skip_save;
2728         centry_put_uint32(centry, *num_names);
2729         for (i=0; i<(*num_names); i++) {
2730                 centry_put_sid(centry, &(*sid_mem)[i]);
2731                 centry_put_string(centry, (*names)[i]);
2732                 centry_put_uint32(centry, (*name_types)[i]);
2733         }       
2734         centry_end(centry, "GM/%s", sid_to_fstring(sid_string, group_sid));
2735         centry_free(centry);
2736
2737 skip_save:
2738         return status;
2739 }
2740
2741 /* find the sequence number for a domain */
2742 static NTSTATUS sequence_number(struct winbindd_domain *domain, uint32 *seq)
2743 {
2744         refresh_sequence_number(domain, false);
2745
2746         *seq = domain->sequence_number;
2747
2748         return NT_STATUS_OK;
2749 }
2750
2751 /* enumerate trusted domains 
2752  * (we need to have the list of trustdoms in the cache when we go offline) -
2753  * Guenther */
2754 static NTSTATUS trusted_domains(struct winbindd_domain *domain,
2755                                 TALLOC_CTX *mem_ctx,
2756                                 struct netr_DomainTrustList *trusts)
2757 {
2758         NTSTATUS status;
2759         struct winbind_cache *cache;
2760         struct winbindd_tdc_domain *dom_list = NULL;
2761         size_t num_domains = 0;
2762         bool retval = false;
2763         int i;
2764         bool old_status;
2765
2766         old_status = domain->online;
2767         trusts->count = 0;
2768         trusts->array = NULL;
2769
2770         cache = get_cache(domain);
2771         if (!cache || !cache->tdb) {
2772                 goto do_query;
2773         }
2774
2775         if (domain->online) {
2776                 goto do_query;
2777         }
2778
2779         retval = wcache_tdc_fetch_list(&dom_list, &num_domains);
2780         if (!retval || !num_domains || !dom_list) {
2781                 TALLOC_FREE(dom_list);
2782                 goto do_query;
2783         }
2784
2785 do_fetch_cache:
2786         trusts->array = talloc_zero_array(mem_ctx, struct netr_DomainTrust, num_domains);
2787         if (!trusts->array) {
2788                 TALLOC_FREE(dom_list);
2789                 return NT_STATUS_NO_MEMORY;
2790         }
2791
2792         for (i = 0; i < num_domains; i++) {
2793                 struct netr_DomainTrust *trust;
2794                 struct dom_sid *sid;
2795                 struct winbindd_domain *dom;
2796
2797                 dom = find_domain_from_name_noinit(dom_list[i].domain_name);
2798                 if (dom && dom->internal) {
2799                         continue;
2800                 }
2801
2802                 trust = &trusts->array[trusts->count];
2803                 trust->netbios_name = talloc_strdup(trusts->array, dom_list[i].domain_name);
2804                 trust->dns_name = talloc_strdup(trusts->array, dom_list[i].dns_name);
2805                 sid = talloc(trusts->array, struct dom_sid);
2806                 if (!trust->netbios_name || !trust->dns_name ||
2807                         !sid) {
2808                         TALLOC_FREE(dom_list);
2809                         TALLOC_FREE(trusts->array);
2810                         return NT_STATUS_NO_MEMORY;
2811                 }
2812
2813                 trust->trust_flags = dom_list[i].trust_flags;
2814                 trust->trust_attributes = dom_list[i].trust_attribs;
2815                 trust->trust_type = dom_list[i].trust_type;
2816                 sid_copy(sid, &dom_list[i].sid);
2817                 trust->sid = sid;
2818                 trusts->count++;
2819         }
2820
2821         TALLOC_FREE(dom_list);
2822         return NT_STATUS_OK;
2823
2824 do_query:
2825         /* Return status value returned by seq number check */
2826
2827         if (!NT_STATUS_IS_OK(domain->last_status))
2828                 return domain->last_status;
2829
2830         DEBUG(10,("trusted_domains: [Cached] - doing backend query for info for domain %s\n",
2831                 domain->name ));
2832
2833         status = domain->backend->trusted_domains(domain, mem_ctx, trusts);
2834
2835         if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2836                 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2837                 if (!domain->internal && old_status) {
2838                         set_domain_offline(domain);
2839                 }
2840                 if (!domain->internal &&
2841                         !domain->online &&
2842                         old_status) {
2843                         retval = wcache_tdc_fetch_list(&dom_list, &num_domains);
2844                         if (retval && num_domains && dom_list) {
2845                                 TALLOC_FREE(trusts->array);
2846                                 trusts->count = 0;
2847                                 goto do_fetch_cache;
2848                         }
2849                 }
2850         }
2851         /* no trusts gives NT_STATUS_NO_MORE_ENTRIES resetting to NT_STATUS_OK
2852          * so that the generic centry handling still applies correctly -
2853          * Guenther*/
2854
2855         if (!NT_STATUS_IS_ERR(status)) {
2856                 status = NT_STATUS_OK;
2857         }
2858         return status;
2859 }       
2860
2861 /* get lockout policy */
2862 static NTSTATUS lockout_policy(struct winbindd_domain *domain,
2863                                TALLOC_CTX *mem_ctx,
2864                                struct samr_DomInfo12 *policy)
2865 {
2866         struct winbind_cache *cache = get_cache(domain);
2867         struct cache_entry *centry = NULL;
2868         NTSTATUS status;
2869         bool old_status;
2870
2871         old_status = domain->online;
2872         if (!cache->tdb)
2873                 goto do_query;
2874
2875         centry = wcache_fetch(cache, domain, "LOC_POL/%s", domain->name);
2876
2877         if (!centry)
2878                 goto do_query;
2879
2880 do_fetch_cache:
2881         policy->lockout_duration = centry_nttime(centry);
2882         policy->lockout_window = centry_nttime(centry);
2883         policy->lockout_threshold = centry_uint16(centry);
2884
2885         status = centry->status;
2886
2887         DEBUG(10,("lockout_policy: [Cached] - cached info for domain %s status: %s\n",
2888                 domain->name, nt_errstr(status) ));
2889
2890         centry_free(centry);
2891         return status;
2892
2893 do_query:
2894         ZERO_STRUCTP(policy);
2895
2896         /* Return status value returned by seq number check */
2897
2898         if (!NT_STATUS_IS_OK(domain->last_status))
2899                 return domain->last_status;
2900
2901         DEBUG(10,("lockout_policy: [Cached] - doing backend query for info for domain %s\n",
2902                 domain->name ));
2903
2904         status = domain->backend->lockout_policy(domain, mem_ctx, policy);
2905
2906         if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2907                 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2908                 if (!domain->internal && old_status) {
2909                         set_domain_offline(domain);
2910                 }
2911                 if (cache->tdb &&
2912                         !domain->internal &&
2913                         !domain->online &&
2914                         old_status) {
2915                         centry = wcache_fetch(cache, domain, "LOC_POL/%s", domain->name);
2916                         if (centry) {
2917                                 goto do_fetch_cache;
2918                         }
2919                 }
2920         }
2921         /* and save it */
2922         refresh_sequence_number(domain, false);
2923         if (!NT_STATUS_IS_OK(status)) {
2924                 return status;
2925         }
2926         wcache_save_lockout_policy(domain, status, policy);
2927
2928         return status;
2929 }
2930
2931 /* get password policy */
2932 static NTSTATUS password_policy(struct winbindd_domain *domain,
2933                                 TALLOC_CTX *mem_ctx,
2934                                 struct samr_DomInfo1 *policy)
2935 {
2936         struct winbind_cache *cache = get_cache(domain);
2937         struct cache_entry *centry = NULL;
2938         NTSTATUS status;
2939         bool old_status;
2940
2941         old_status = domain->online;
2942         if (!cache->tdb)
2943                 goto do_query;
2944
2945         centry = wcache_fetch(cache, domain, "PWD_POL/%s", domain->name);
2946
2947         if (!centry)
2948                 goto do_query;
2949
2950 do_fetch_cache:
2951         policy->min_password_length = centry_uint16(centry);
2952         policy->password_history_length = centry_uint16(centry);
2953         policy->password_properties = centry_uint32(centry);
2954         policy->max_password_age = centry_nttime(centry);
2955         policy->min_password_age = centry_nttime(centry);
2956
2957         status = centry->status;
2958
2959         DEBUG(10,("lockout_policy: [Cached] - cached info for domain %s status: %s\n",
2960                 domain->name, nt_errstr(status) ));
2961
2962         centry_free(centry);
2963         return status;
2964
2965 do_query:
2966         ZERO_STRUCTP(policy);
2967
2968         /* Return status value returned by seq number check */
2969
2970         if (!NT_STATUS_IS_OK(domain->last_status))
2971                 return domain->last_status;
2972
2973         DEBUG(10,("password_policy: [Cached] - doing backend query for info for domain %s\n",
2974                 domain->name ));
2975
2976         status = domain->backend->password_policy(domain, mem_ctx, policy);
2977
2978         if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2979                 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2980                 if (!domain->internal && old_status) {
2981                         set_domain_offline(domain);
2982                 }
2983                 if (cache->tdb &&
2984                         !domain->internal &&
2985                         !domain->online &&
2986                         old_status) {
2987                         centry = wcache_fetch(cache, domain, "PWD_POL/%s", domain->name);
2988                         if (centry) {
2989                                 goto do_fetch_cache;
2990                         }
2991                 }
2992         }
2993         /* and save it */
2994         refresh_sequence_number(domain, false);
2995         if (!NT_STATUS_IS_OK(status)) {
2996                 return status;
2997         }
2998         wcache_save_password_policy(domain, status, policy);
2999
3000         return status;
3001 }
3002
3003
3004 /* Invalidate cached user and group lists coherently */
3005
3006 static int traverse_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf, 
3007                        void *state)
3008 {
3009         if (strncmp((const char *)kbuf.dptr, "UL/", 3) == 0 ||
3010             strncmp((const char *)kbuf.dptr, "GL/", 3) == 0)
3011                 tdb_delete(the_tdb, kbuf);
3012
3013         return 0;
3014 }
3015
3016 /* Invalidate the getpwnam and getgroups entries for a winbindd domain */
3017
3018 void wcache_invalidate_samlogon(struct winbindd_domain *domain, 
3019                                 const struct dom_sid *sid)
3020 {
3021         fstring key_str, sid_string;
3022         struct winbind_cache *cache;
3023
3024         /* dont clear cached U/SID and UG/SID entries when we want to logon
3025          * offline - gd */
3026
3027         if (lp_winbind_offline_logon()) {
3028                 return;
3029         }
3030
3031         if (!domain)
3032                 return;
3033
3034         cache = get_cache(domain);
3035
3036         if (!cache->tdb) {
3037                 return;
3038         }
3039
3040         /* Clear U/SID cache entry */
3041         fstr_sprintf(key_str, "U/%s", sid_to_fstring(sid_string, sid));
3042         DEBUG(10, ("wcache_invalidate_samlogon: clearing %s\n", key_str));
3043         tdb_delete(cache->tdb, string_tdb_data(key_str));
3044
3045         /* Clear UG/SID cache entry */
3046         fstr_sprintf(key_str, "UG/%s", sid_to_fstring(sid_string, sid));
3047         DEBUG(10, ("wcache_invalidate_samlogon: clearing %s\n", key_str));
3048         tdb_delete(cache->tdb, string_tdb_data(key_str));
3049
3050         /* Samba/winbindd never needs this. */
3051         netsamlogon_clear_cached_user(sid);
3052 }
3053
3054 bool wcache_invalidate_cache(void)
3055 {
3056         struct winbindd_domain *domain;
3057
3058         for (domain = domain_list(); domain; domain = domain->next) {
3059                 struct winbind_cache *cache = get_cache(domain);
3060
3061                 DEBUG(10, ("wcache_invalidate_cache: invalidating cache "
3062                            "entries for %s\n", domain->name));
3063                 if (cache) {
3064                         if (cache->tdb) {
3065                                 tdb_traverse(cache->tdb, traverse_fn, NULL);
3066                         } else {
3067                                 return false;
3068                         }
3069                 }
3070         }
3071         return true;
3072 }
3073
3074 bool wcache_invalidate_cache_noinit(void)
3075 {
3076         struct winbindd_domain *domain;
3077
3078         for (domain = domain_list(); domain; domain = domain->next) {
3079                 struct winbind_cache *cache;
3080
3081                 /* Skip uninitialized domains. */
3082                 if (!domain->initialized && !domain->internal) {
3083                         continue;
3084                 }
3085
3086                 cache = get_cache(domain);
3087
3088                 DEBUG(10, ("wcache_invalidate_cache: invalidating cache "
3089                            "entries for %s\n", domain->name));
3090                 if (cache) {
3091                         if (cache->tdb) {
3092                                 tdb_traverse(cache->tdb, traverse_fn, NULL);
3093                                 /*
3094                                  * Flushing cache has nothing to with domains.
3095                                  * return here if we successfully flushed once.
3096                                  * To avoid unnecessary traversing the cache.
3097                                  */
3098                                 return true;
3099                         } else {
3100                                 return false;
3101                         }
3102                 }
3103         }
3104         return true;
3105 }
3106
3107 bool init_wcache(void)
3108 {
3109         if (wcache == NULL) {
3110                 wcache = SMB_XMALLOC_P(struct winbind_cache);
3111                 ZERO_STRUCTP(wcache);
3112         }
3113
3114         if (wcache->tdb != NULL)
3115                 return true;
3116
3117         /* when working offline we must not clear the cache on restart */
3118         wcache->tdb = tdb_open_log(state_path("winbindd_cache.tdb"),
3119                                 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE, 
3120                                 TDB_INCOMPATIBLE_HASH |
3121                                         (lp_winbind_offline_logon() ? TDB_DEFAULT : (TDB_DEFAULT | TDB_CLEAR_IF_FIRST)),
3122                                 O_RDWR|O_CREAT, 0600);
3123
3124         if (wcache->tdb == NULL) {
3125                 DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
3126                 return false;
3127         }
3128
3129         return true;
3130 }
3131
3132 /************************************************************************
3133  This is called by the parent to initialize the cache file.
3134  We don't need sophisticated locking here as we know we're the
3135  only opener.
3136 ************************************************************************/
3137
3138 bool initialize_winbindd_cache(void)
3139 {
3140         bool cache_bad = true;
3141         uint32 vers;
3142
3143         if (!init_wcache()) {
3144                 DEBUG(0,("initialize_winbindd_cache: init_wcache failed.\n"));
3145                 return false;
3146         }
3147
3148         /* Check version number. */
3149         if (tdb_fetch_uint32(wcache->tdb, WINBINDD_CACHE_VERSION_KEYSTR, &vers) &&
3150                         vers == WINBINDD_CACHE_VERSION) {
3151                 cache_bad = false;
3152         }
3153
3154         if (cache_bad) {
3155                 DEBUG(0,("initialize_winbindd_cache: clearing cache "
3156                         "and re-creating with version number %d\n",
3157                         WINBINDD_CACHE_VERSION ));
3158
3159                 tdb_close(wcache->tdb);
3160                 wcache->tdb = NULL;
3161
3162                 if (unlink(state_path("winbindd_cache.tdb")) == -1) {
3163                         DEBUG(0,("initialize_winbindd_cache: unlink %s failed %s ",
3164                                 state_path("winbindd_cache.tdb"),
3165                                 strerror(errno) ));
3166                         return false;
3167                 }
3168                 if (!init_wcache()) {
3169                         DEBUG(0,("initialize_winbindd_cache: re-initialization "
3170                                         "init_wcache failed.\n"));
3171                         return false;
3172                 }
3173
3174                 /* Write the version. */
3175                 if (!tdb_store_uint32(wcache->tdb, WINBINDD_CACHE_VERSION_KEYSTR, WINBINDD_CACHE_VERSION)) {
3176                         DEBUG(0,("initialize_winbindd_cache: version number store failed %s\n",
3177                                 tdb_errorstr_compat(wcache->tdb) ));
3178                         return false;
3179                 }
3180         }
3181
3182         tdb_close(wcache->tdb);
3183         wcache->tdb = NULL;
3184         return true;
3185 }
3186
3187 void close_winbindd_cache(void)
3188 {
3189         if (!wcache) {
3190                 return;
3191         }
3192         if (wcache->tdb) {
3193                 tdb_close(wcache->tdb);
3194                 wcache->tdb = NULL;
3195         }
3196 }
3197
3198 bool lookup_cached_sid(TALLOC_CTX *mem_ctx, const struct dom_sid *sid,
3199                        char **domain_name, char **name,
3200                        enum lsa_SidType *type)
3201 {
3202         struct winbindd_domain *domain;
3203         NTSTATUS status;
3204
3205         domain = find_lookup_domain_from_sid(sid);
3206         if (domain == NULL) {
3207                 return false;
3208         }
3209         status = wcache_sid_to_name(domain, sid, mem_ctx, domain_name, name,
3210                                     type);
3211         return NT_STATUS_IS_OK(status);
3212 }
3213
3214 bool lookup_cached_name(const char *domain_name,
3215                         const char *name,
3216                         struct dom_sid *sid,
3217                         enum lsa_SidType *type)
3218 {
3219         struct winbindd_domain *domain;
3220         NTSTATUS status;
3221         bool original_online_state;
3222
3223         domain = find_lookup_domain_from_name(domain_name);
3224         if (domain == NULL) {
3225                 return false;
3226         }
3227
3228         /* If we are doing a cached logon, temporarily set the domain
3229            offline so the cache won't expire the entry */
3230
3231         original_online_state = domain->online;
3232         domain->online = false;
3233         status = wcache_name_to_sid(domain, domain_name, name, sid, type);
3234         domain->online = original_online_state;
3235
3236         return NT_STATUS_IS_OK(status);
3237 }
3238
3239 void cache_name2sid(struct winbindd_domain *domain, 
3240                     const char *domain_name, const char *name,
3241                     enum lsa_SidType type, const struct dom_sid *sid)
3242 {
3243         refresh_sequence_number(domain, false);
3244         wcache_save_name_to_sid(domain, NT_STATUS_OK, domain_name, name,
3245                                 sid, type);
3246 }
3247
3248 /*
3249  * The original idea that this cache only contains centries has
3250  * been blurred - now other stuff gets put in here. Ensure we
3251  * ignore these things on cleanup.
3252  */
3253
3254 static int traverse_fn_cleanup(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, 
3255                                TDB_DATA dbuf, void *state)
3256 {
3257         struct cache_entry *centry;
3258
3259         if (is_non_centry_key(kbuf)) {
3260                 return 0;
3261         }
3262
3263         centry = wcache_fetch_raw((char *)kbuf.dptr);
3264         if (!centry) {
3265                 return 0;
3266         }
3267
3268         if (!NT_STATUS_IS_OK(centry->status)) {
3269                 DEBUG(10,("deleting centry %s\n", (const char *)kbuf.dptr));
3270                 tdb_delete(the_tdb, kbuf);
3271         }
3272
3273         centry_free(centry);
3274         return 0;
3275 }
3276
3277 /* flush the cache */
3278 void wcache_flush_cache(void)
3279 {
3280         if (!wcache)
3281                 return;
3282         if (wcache->tdb) {
3283                 tdb_close(wcache->tdb);
3284                 wcache->tdb = NULL;
3285         }
3286         if (!winbindd_use_cache()) {
3287                 return;
3288         }
3289
3290         /* when working offline we must not clear the cache on restart */
3291         wcache->tdb = tdb_open_log(state_path("winbindd_cache.tdb"),
3292                                 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE, 
3293                                 TDB_INCOMPATIBLE_HASH |
3294                                 (lp_winbind_offline_logon() ? TDB_DEFAULT : (TDB_DEFAULT | TDB_CLEAR_IF_FIRST)),
3295                                 O_RDWR|O_CREAT, 0600);
3296
3297         if (!wcache->tdb) {
3298                 DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
3299                 return;
3300         }
3301
3302         tdb_traverse(wcache->tdb, traverse_fn_cleanup, NULL);
3303
3304         DEBUG(10,("wcache_flush_cache success\n"));
3305 }
3306
3307 /* Count cached creds */
3308
3309 static int traverse_fn_cached_creds(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf, 
3310                                     void *state)
3311 {
3312         int *cred_count = (int*)state;
3313  
3314         if (strncmp((const char *)kbuf.dptr, "CRED/", 5) == 0) {
3315                 (*cred_count)++;
3316         }
3317         return 0;
3318 }
3319
3320 NTSTATUS wcache_count_cached_creds(struct winbindd_domain *domain, int *count)
3321 {
3322         struct winbind_cache *cache = get_cache(domain);
3323
3324         *count = 0;
3325
3326         if (!cache->tdb) {
3327                 return NT_STATUS_INTERNAL_DB_ERROR;
3328         }
3329  
3330         tdb_traverse(cache->tdb, traverse_fn_cached_creds, (void *)count);
3331
3332         return NT_STATUS_OK;
3333 }
3334
3335 struct cred_list {
3336         struct cred_list *prev, *next;
3337         TDB_DATA key;
3338         fstring name;
3339         time_t created;
3340 };
3341 static struct cred_list *wcache_cred_list;
3342
3343 static int traverse_fn_get_credlist(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf, 
3344                                     void *state)
3345 {
3346         struct cred_list *cred;
3347
3348         if (strncmp((const char *)kbuf.dptr, "CRED/", 5) == 0) {
3349
3350                 cred = SMB_MALLOC_P(struct cred_list);
3351                 if (cred == NULL) {
3352                         DEBUG(0,("traverse_fn_remove_first_creds: failed to malloc new entry for list\n"));
3353                         return -1;
3354                 }
3355
3356                 ZERO_STRUCTP(cred);
3357
3358                 /* save a copy of the key */
3359
3360                 fstrcpy(cred->name, (const char *)kbuf.dptr);           
3361                 DLIST_ADD(wcache_cred_list, cred);
3362         }
3363
3364         return 0;
3365 }
3366
3367 NTSTATUS wcache_remove_oldest_cached_creds(struct winbindd_domain *domain, const struct dom_sid *sid)
3368 {
3369         struct winbind_cache *cache = get_cache(domain);
3370         NTSTATUS status;
3371         int ret;
3372         struct cred_list *cred, *oldest = NULL;
3373
3374         if (!cache->tdb) {
3375                 return NT_STATUS_INTERNAL_DB_ERROR;
3376         }
3377
3378         /* we possibly already have an entry */
3379         if (sid && NT_STATUS_IS_OK(wcache_cached_creds_exist(domain, sid))) {
3380
3381                 fstring key_str, tmp;
3382
3383                 DEBUG(11,("we already have an entry, deleting that\n"));
3384
3385                 fstr_sprintf(key_str, "CRED/%s", sid_to_fstring(tmp, sid));
3386
3387                 tdb_delete(cache->tdb, string_tdb_data(key_str));
3388
3389                 return NT_STATUS_OK;
3390         }
3391
3392         ret = tdb_traverse(cache->tdb, traverse_fn_get_credlist, NULL);
3393         if (ret == 0) {
3394                 return NT_STATUS_OK;
3395         } else if ((ret < 0) || (wcache_cred_list == NULL)) {
3396                 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
3397         }
3398
3399         ZERO_STRUCTP(oldest);
3400
3401         for (cred = wcache_cred_list; cred; cred = cred->next) {
3402
3403                 TDB_DATA data;
3404                 time_t t;
3405
3406                 data = tdb_fetch_compat(cache->tdb, string_tdb_data(cred->name));
3407                 if (!data.dptr) {
3408                         DEBUG(10,("wcache_remove_oldest_cached_creds: entry for [%s] not found\n", 
3409                                 cred->name));
3410                         status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
3411                         goto done;
3412                 }
3413
3414                 t = IVAL(data.dptr, 0);
3415                 SAFE_FREE(data.dptr);
3416
3417                 if (!oldest) {
3418                         oldest = SMB_MALLOC_P(struct cred_list);
3419                         if (oldest == NULL) {
3420                                 status = NT_STATUS_NO_MEMORY;
3421                                 goto done;
3422                         }
3423
3424                         fstrcpy(oldest->name, cred->name);
3425                         oldest->created = t;
3426                         continue;
3427                 }
3428
3429                 if (t < oldest->created) {
3430                         fstrcpy(oldest->name, cred->name);
3431                         oldest->created = t;
3432                 }
3433         }
3434
3435         if (tdb_delete(cache->tdb, string_tdb_data(oldest->name)) == 0) {
3436                 status = NT_STATUS_OK;
3437         } else {
3438                 status = NT_STATUS_UNSUCCESSFUL;
3439         }
3440 done:
3441         SAFE_FREE(wcache_cred_list);
3442         SAFE_FREE(oldest);
3443
3444         return status;
3445 }
3446
3447 /* Change the global online/offline state. */
3448 bool set_global_winbindd_state_offline(void)
3449 {
3450         TDB_DATA data;
3451
3452         DEBUG(10,("set_global_winbindd_state_offline: offline requested.\n"));
3453
3454         /* Only go offline if someone has created
3455            the key "WINBINDD_OFFLINE" in the cache tdb. */
3456
3457         if (wcache == NULL || wcache->tdb == NULL) {
3458                 DEBUG(10,("set_global_winbindd_state_offline: wcache not open yet.\n"));
3459                 return false;
3460         }
3461
3462         if (!lp_winbind_offline_logon()) {
3463                 DEBUG(10,("set_global_winbindd_state_offline: rejecting.\n"));
3464                 return false;
3465         }
3466
3467         if (global_winbindd_offline_state) {
3468                 /* Already offline. */
3469                 return true;
3470         }
3471
3472         data = tdb_fetch_bystring( wcache->tdb, "WINBINDD_OFFLINE" );
3473
3474         if (!data.dptr || data.dsize != 4) {
3475                 DEBUG(10,("set_global_winbindd_state_offline: offline state not set.\n"));
3476                 SAFE_FREE(data.dptr);
3477                 return false;
3478         } else {
3479                 DEBUG(10,("set_global_winbindd_state_offline: offline state set.\n"));
3480                 global_winbindd_offline_state = true;
3481                 SAFE_FREE(data.dptr);
3482                 return true;
3483         }
3484 }
3485
3486 void set_global_winbindd_state_online(void)
3487 {
3488         DEBUG(10,("set_global_winbindd_state_online: online requested.\n"));
3489
3490         if (!lp_winbind_offline_logon()) {
3491                 DEBUG(10,("set_global_winbindd_state_online: rejecting.\n"));
3492                 return;
3493         }
3494
3495         if (!global_winbindd_offline_state) {
3496                 /* Already online. */
3497                 return;
3498         }
3499         global_winbindd_offline_state = false;
3500
3501         if (!wcache->tdb) {
3502                 return;
3503         }
3504
3505         /* Ensure there is no key "WINBINDD_OFFLINE" in the cache tdb. */
3506         tdb_delete_bystring(wcache->tdb, "WINBINDD_OFFLINE");
3507 }
3508
3509 bool get_global_winbindd_state_offline(void)
3510 {
3511         return global_winbindd_offline_state;
3512 }
3513
3514 /***********************************************************************
3515  Validate functions for all possible cache tdb keys.
3516 ***********************************************************************/
3517
3518 static struct cache_entry *create_centry_validate(const char *kstr, TDB_DATA data, 
3519                                                   struct tdb_validation_status *state)
3520 {
3521         struct cache_entry *centry;
3522
3523         centry = SMB_XMALLOC_P(struct cache_entry);
3524         centry->data = (unsigned char *)memdup(data.dptr, data.dsize);
3525         if (!centry->data) {
3526                 SAFE_FREE(centry);
3527                 return NULL;
3528         }
3529         centry->len = data.dsize;
3530         centry->ofs = 0;
3531
3532         if (centry->len < 16) {
3533                 /* huh? corrupt cache? */
3534                 DEBUG(0,("create_centry_validate: Corrupt cache for key %s "
3535                          "(len < 16) ?\n", kstr));
3536                 centry_free(centry);
3537                 state->bad_entry = true;
3538                 state->success = false;
3539                 return NULL;
3540         }
3541
3542         centry->status = NT_STATUS(centry_uint32(centry));
3543         centry->sequence_number = centry_uint32(centry);
3544         centry->timeout = centry_uint64_t(centry);
3545         return centry;
3546 }
3547
3548 static int validate_seqnum(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3549                            struct tdb_validation_status *state)
3550 {
3551         if (dbuf.dsize != 8) {
3552                 DEBUG(0,("validate_seqnum: Corrupt cache for key %s (len %u != 8) ?\n",
3553                                 keystr, (unsigned int)dbuf.dsize ));
3554                 state->bad_entry = true;
3555                 return 1;
3556         }
3557         return 0;
3558 }
3559
3560 static int validate_ns(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3561                        struct tdb_validation_status *state)
3562 {
3563         struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3564         if (!centry) {
3565                 return 1;
3566         }
3567
3568         (void)centry_uint32(centry);
3569         if (NT_STATUS_IS_OK(centry->status)) {
3570                 struct dom_sid sid;
3571                 (void)centry_sid(centry, &sid);
3572         }
3573
3574         centry_free(centry);
3575
3576         if (!(state->success)) {
3577                 return 1;
3578         }
3579         DEBUG(10,("validate_ns: %s ok\n", keystr));
3580         return 0;
3581 }
3582
3583 static int validate_sn(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3584                        struct tdb_validation_status *state)
3585 {
3586         struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3587         if (!centry) {
3588                 return 1;
3589         }
3590
3591         if (NT_STATUS_IS_OK(centry->status)) {
3592                 (void)centry_uint32(centry);
3593                 (void)centry_string(centry, mem_ctx);
3594                 (void)centry_string(centry, mem_ctx);
3595         }
3596
3597         centry_free(centry);
3598
3599         if (!(state->success)) {
3600                 return 1;
3601         }
3602         DEBUG(10,("validate_sn: %s ok\n", keystr));
3603         return 0;
3604 }
3605
3606 static int validate_u(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3607                       struct tdb_validation_status *state)
3608 {
3609         struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3610         struct dom_sid sid;
3611
3612         if (!centry) {
3613                 return 1;
3614         }
3615
3616         (void)centry_string(centry, mem_ctx);
3617         (void)centry_string(centry, mem_ctx);
3618         (void)centry_string(centry, mem_ctx);
3619         (void)centry_string(centry, mem_ctx);
3620         (void)centry_uint32(centry);
3621         (void)centry_sid(centry, &sid);
3622         (void)centry_sid(centry, &sid);
3623
3624         centry_free(centry);
3625
3626         if (!(state->success)) {
3627                 return 1;
3628         }
3629         DEBUG(10,("validate_u: %s ok\n", keystr));
3630         return 0;
3631 }
3632
3633 static int validate_loc_pol(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3634                             struct tdb_validation_status *state)
3635 {
3636         struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3637
3638         if (!centry) {
3639                 return 1;
3640         }
3641
3642         (void)centry_nttime(centry);
3643         (void)centry_nttime(centry);
3644         (void)centry_uint16(centry);
3645
3646         centry_free(centry);
3647
3648         if (!(state->success)) {
3649                 return 1;
3650         }
3651         DEBUG(10,("validate_loc_pol: %s ok\n", keystr));
3652         return 0;
3653 }
3654
3655 static int validate_pwd_pol(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3656                             struct tdb_validation_status *state)
3657 {
3658         struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3659
3660         if (!centry) {
3661                 return 1;
3662         }
3663
3664         (void)centry_uint16(centry);
3665         (void)centry_uint16(centry);
3666         (void)centry_uint32(centry);
3667         (void)centry_nttime(centry);
3668         (void)centry_nttime(centry);
3669
3670         centry_free(centry);
3671
3672         if (!(state->success)) {
3673                 return 1;
3674         }
3675         DEBUG(10,("validate_pwd_pol: %s ok\n", keystr));
3676         return 0;
3677 }
3678
3679 static int validate_cred(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3680                          struct tdb_validation_status *state)
3681 {
3682         struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3683
3684         if (!centry) {
3685                 return 1;
3686         }
3687
3688         (void)centry_time(centry);
3689         (void)centry_hash16(centry, mem_ctx);
3690
3691         /* We only have 17 bytes more data in the salted cred case. */
3692         if (centry->len - centry->ofs == 17) {
3693                 (void)centry_hash16(centry, mem_ctx);
3694         }
3695
3696         centry_free(centry);
3697
3698         if (!(state->success)) {
3699                 return 1;
3700         }
3701         DEBUG(10,("validate_cred: %s ok\n", keystr));
3702         return 0;
3703 }
3704
3705 static int validate_ul(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3706                        struct tdb_validation_status *state)
3707 {
3708         struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3709         int32 num_entries, i;
3710
3711         if (!centry) {
3712                 return 1;
3713         }
3714
3715         num_entries = (int32)centry_uint32(centry);
3716
3717         for (i=0; i< num_entries; i++) {
3718                 struct dom_sid sid;
3719                 (void)centry_string(centry, mem_ctx);
3720                 (void)centry_string(centry, mem_ctx);
3721                 (void)centry_string(centry, mem_ctx);
3722                 (void)centry_string(centry, mem_ctx);
3723                 (void)centry_sid(centry, &sid);
3724                 (void)centry_sid(centry, &sid);
3725         }
3726
3727         centry_free(centry);
3728
3729         if (!(state->success)) {
3730                 return 1;
3731         }
3732         DEBUG(10,("validate_ul: %s ok\n", keystr));
3733         return 0;
3734 }
3735
3736 static int validate_gl(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3737                        struct tdb_validation_status *state)
3738 {
3739         struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3740         int32 num_entries, i;
3741
3742         if (!centry) {
3743                 return 1;
3744         }
3745
3746         num_entries = centry_uint32(centry);
3747
3748         for (i=0; i< num_entries; i++) {
3749                 (void)centry_string(centry, mem_ctx);
3750                 (void)centry_string(centry, mem_ctx);
3751                 (void)centry_uint32(centry);
3752         }
3753
3754         centry_free(centry);
3755
3756         if (!(state->success)) {
3757                 return 1;
3758         }
3759         DEBUG(10,("validate_gl: %s ok\n", keystr));
3760         return 0;
3761 }
3762
3763 static int validate_ug(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3764                        struct tdb_validation_status *state)
3765 {
3766         struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3767         int32 num_groups, i;
3768
3769         if (!centry) {
3770                 return 1;
3771         }
3772
3773         num_groups = centry_uint32(centry);
3774
3775         for (i=0; i< num_groups; i++) {
3776                 struct dom_sid sid;
3777                 centry_sid(centry, &sid);
3778         }
3779
3780         centry_free(centry);
3781
3782         if (!(state->success)) {
3783                 return 1;
3784         }
3785         DEBUG(10,("validate_ug: %s ok\n", keystr));
3786         return 0;
3787 }
3788
3789 static int validate_ua(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3790                        struct tdb_validation_status *state)
3791 {
3792         struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3793         int32 num_aliases, i;
3794
3795         if (!centry) {
3796                 return 1;
3797         }
3798
3799         num_aliases = centry_uint32(centry);
3800
3801         for (i=0; i < num_aliases; i++) {
3802                 (void)centry_uint32(centry);
3803         }
3804
3805         centry_free(centry);
3806
3807         if (!(state->success)) {
3808                 return 1;
3809         }
3810         DEBUG(10,("validate_ua: %s ok\n", keystr));
3811         return 0;
3812 }
3813
3814 static int validate_gm(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3815                        struct tdb_validation_status *state)
3816 {
3817         struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3818         int32 num_names, i;
3819
3820         if (!centry) {
3821                 return 1;
3822         }
3823
3824         num_names = centry_uint32(centry);
3825
3826         for (i=0; i< num_names; i++) {
3827                 struct dom_sid sid;
3828                 centry_sid(centry, &sid);
3829                 (void)centry_string(centry, mem_ctx);
3830                 (void)centry_uint32(centry);
3831         }
3832
3833         centry_free(centry);
3834
3835         if (!(state->success)) {
3836                 return 1;
3837         }
3838         DEBUG(10,("validate_gm: %s ok\n", keystr));
3839         return 0;
3840 }
3841
3842 static int validate_dr(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3843                        struct tdb_validation_status *state)
3844 {
3845         /* Can't say anything about this other than must be nonzero. */
3846         if (dbuf.dsize == 0) {
3847                 DEBUG(0,("validate_dr: Corrupt cache for key %s (len == 0) ?\n",
3848                                 keystr));
3849                 state->bad_entry = true;
3850                 state->success = false;
3851                 return 1;
3852         }
3853
3854         DEBUG(10,("validate_dr: %s ok\n", keystr));
3855         return 0;
3856 }
3857
3858 static int validate_de(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3859                        struct tdb_validation_status *state)
3860 {
3861         /* Can't say anything about this other than must be nonzero. */
3862         if (dbuf.dsize == 0) {
3863                 DEBUG(0,("validate_de: Corrupt cache for key %s (len == 0) ?\n",
3864                                 keystr));
3865                 state->bad_entry = true;
3866                 state->success = false;
3867                 return 1;
3868         }
3869
3870         DEBUG(10,("validate_de: %s ok\n", keystr));
3871         return 0;
3872 }
3873
3874 static int validate_pwinfo(TALLOC_CTX *mem_ctx, const char *keystr,
3875                            TDB_DATA dbuf, struct tdb_validation_status *state)
3876 {
3877         struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3878
3879         if (!centry) {
3880                 return 1;
3881         }
3882
3883         (void)centry_string(centry, mem_ctx);
3884         (void)centry_string(centry, mem_ctx);
3885         (void)centry_string(centry, mem_ctx);
3886         (void)centry_uint32(centry);
3887
3888         centry_free(centry);
3889
3890         if (!(state->success)) {
3891                 return 1;
3892         }
3893         DEBUG(10,("validate_pwinfo: %s ok\n", keystr));
3894         return 0;
3895 }
3896
3897 static int validate_nss_an(TALLOC_CTX *mem_ctx, const char *keystr,
3898                            TDB_DATA dbuf,
3899                            struct tdb_validation_status *state)
3900 {
3901         struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3902
3903         if (!centry) {
3904                 return 1;
3905         }
3906
3907         (void)centry_string( centry, mem_ctx );
3908
3909         centry_free(centry);
3910
3911         if (!(state->success)) {
3912                 return 1;
3913         }
3914         DEBUG(10,("validate_pwinfo: %s ok\n", keystr));
3915         return 0;
3916 }
3917
3918 static int validate_nss_na(TALLOC_CTX *mem_ctx, const char *keystr,
3919                            TDB_DATA dbuf,
3920                            struct tdb_validation_status *state)
3921 {
3922         struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3923
3924         if (!centry) {
3925                 return 1;
3926         }
3927
3928         (void)centry_string( centry, mem_ctx );
3929
3930         centry_free(centry);
3931
3932         if (!(state->success)) {
3933                 return 1;
3934         }
3935         DEBUG(10,("validate_pwinfo: %s ok\n", keystr));
3936         return 0;
3937 }
3938
3939 static int validate_trustdomcache(TALLOC_CTX *mem_ctx, const char *keystr, 
3940                                   TDB_DATA dbuf,
3941                                   struct tdb_validation_status *state)
3942 {
3943         if (dbuf.dsize == 0) {
3944                 DEBUG(0, ("validate_trustdomcache: Corrupt cache for "
3945                           "key %s (len ==0) ?\n", keystr));
3946                 state->bad_entry = true;
3947                 state->success = false;
3948                 return 1;
3949         }
3950
3951         DEBUG(10,    ("validate_trustdomcache: %s ok\n", keystr));
3952         DEBUGADD(10, ("  Don't trust me, I am a DUMMY!\n"));
3953         return 0;
3954 }
3955
3956 static int validate_offline(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3957                             struct tdb_validation_status *state)
3958 {
3959         if (dbuf.dsize != 4) {
3960                 DEBUG(0,("validate_offline: Corrupt cache for key %s (len %u != 4) ?\n",
3961                                 keystr, (unsigned int)dbuf.dsize ));
3962                 state->bad_entry = true;
3963                 state->success = false;
3964                 return 1;
3965         }
3966         DEBUG(10,("validate_offline: %s ok\n", keystr));
3967         return 0;
3968 }
3969
3970 static int validate_ndr(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3971                         struct tdb_validation_status *state)
3972 {
3973         /*
3974          * Ignore validation for now. The proper way to do this is with a
3975          * checksum. Just pure parsing does not really catch much.
3976          */
3977         return 0;
3978 }
3979
3980 static int validate_cache_version(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3981                                   struct tdb_validation_status *state)
3982 {
3983         if (dbuf.dsize != 4) {
3984                 DEBUG(0, ("validate_cache_version: Corrupt cache for "
3985                           "key %s (len %u != 4) ?\n", 
3986                           keystr, (unsigned int)dbuf.dsize));
3987                 state->bad_entry = true;
3988                 state->success = false;
3989                 return 1;
3990         }
3991
3992         DEBUG(10, ("validate_cache_version: %s ok\n", keystr));
3993         return 0;
3994 }
3995
3996 /***********************************************************************
3997  A list of all possible cache tdb keys with associated validation
3998  functions.
3999 ***********************************************************************/
4000
4001 struct key_val_struct {
4002         const char *keyname;
4003         int (*validate_data_fn)(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf, struct tdb_validation_status* state);
4004 } key_val[] = {
4005         {"SEQNUM/", validate_seqnum},
4006         {"NS/", validate_ns},
4007         {"SN/", validate_sn},
4008         {"U/", validate_u},
4009         {"LOC_POL/", validate_loc_pol},
4010         {"PWD_POL/", validate_pwd_pol},
4011         {"CRED/", validate_cred},
4012         {"UL/", validate_ul},
4013         {"GL/", validate_gl},
4014         {"UG/", validate_ug},
4015         {"UA", validate_ua},
4016         {"GM/", validate_gm},
4017         {"DR/", validate_dr},
4018         {"DE/", validate_de},
4019         {"NSS/PWINFO/", validate_pwinfo},
4020         {"TRUSTDOMCACHE/", validate_trustdomcache},
4021         {"NSS/NA/", validate_nss_na},
4022         {"NSS/AN/", validate_nss_an},
4023         {"WINBINDD_OFFLINE", validate_offline},
4024         {"NDR/", validate_ndr},
4025         {WINBINDD_CACHE_VERSION_KEYSTR, validate_cache_version},
4026         {NULL, NULL}
4027 };
4028
4029 /***********************************************************************
4030  Function to look at every entry in the tdb and validate it as far as
4031  possible.
4032 ***********************************************************************/
4033
4034 static int cache_traverse_validate_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf, void *state)
4035 {
4036         int i;
4037         unsigned int max_key_len = 1024;
4038         struct tdb_validation_status *v_state = (struct tdb_validation_status *)state;
4039
4040         /* Paranoia check. */
4041         if (strncmp("UA/", (const char *)kbuf.dptr, 3) == 0) {
4042                 max_key_len = 1024 * 1024;
4043         }
4044         if (kbuf.dsize > max_key_len) {
4045                 DEBUG(0, ("cache_traverse_validate_fn: key length too large: "
4046                           "(%u) > (%u)\n\n",
4047                           (unsigned int)kbuf.dsize, (unsigned int)max_key_len));
4048                 return 1;
4049         }
4050
4051         for (i = 0; key_val[i].keyname; i++) {
4052                 size_t namelen = strlen(key_val[i].keyname);
4053                 if (kbuf.dsize >= namelen && (
4054                                 strncmp(key_val[i].keyname, (const char *)kbuf.dptr, namelen)) == 0) {
4055                         TALLOC_CTX *mem_ctx;
4056                         char *keystr;
4057                         int ret;
4058
4059                         keystr = SMB_MALLOC_ARRAY(char, kbuf.dsize+1);
4060                         if (!keystr) {
4061                                 return 1;
4062                         }
4063                         memcpy(keystr, kbuf.dptr, kbuf.dsize);
4064                         keystr[kbuf.dsize] = '\0';
4065
4066                         mem_ctx = talloc_init("validate_ctx");
4067                         if (!mem_ctx) {
4068                                 SAFE_FREE(keystr);
4069                                 return 1;
4070                         }
4071
4072                         ret = key_val[i].validate_data_fn(mem_ctx, keystr, dbuf, 
4073                                                           v_state);
4074
4075                         SAFE_FREE(keystr);
4076                         talloc_destroy(mem_ctx);
4077                         return ret;
4078                 }
4079         }
4080
4081         DEBUG(0,("cache_traverse_validate_fn: unknown cache entry\nkey :\n"));
4082         dump_data(0, (uint8 *)kbuf.dptr, kbuf.dsize);
4083         DEBUG(0,("data :\n"));
4084         dump_data(0, (uint8 *)dbuf.dptr, dbuf.dsize);
4085         v_state->unknown_key = true;
4086         v_state->success = false;
4087         return 1; /* terminate. */
4088 }
4089
4090 static void validate_panic(const char *const why)
4091 {
4092         DEBUG(0,("validating cache: would panic %s\n", why ));
4093         DEBUGADD(0, ("exiting instead (cache validation mode)\n"));
4094         exit(47);
4095 }
4096
4097 static int wbcache_update_centry_fn(TDB_CONTEXT *tdb,
4098                                     TDB_DATA key,
4099                                     TDB_DATA data,
4100                                     void *state)
4101 {
4102         uint64_t ctimeout;
4103         TDB_DATA blob;
4104
4105         if (is_non_centry_key(key)) {
4106                 return 0;
4107         }
4108
4109         if (data.dptr == NULL || data.dsize == 0) {
4110                 if (tdb_delete(tdb, key) < 0) {
4111                         DEBUG(0, ("tdb_delete for [%s] failed!\n",
4112                                   key.dptr));
4113                         return 1;
4114                 }
4115         }
4116
4117         /* add timeout to blob (uint64_t) */
4118         blob.dsize = data.dsize + 8;
4119
4120         blob.dptr = SMB_XMALLOC_ARRAY(uint8_t, blob.dsize);
4121         if (blob.dptr == NULL) {
4122                 return 1;
4123         }
4124         memset(blob.dptr, 0, blob.dsize);
4125
4126         /* copy status and seqnum */
4127         memcpy(blob.dptr, data.dptr, 8);
4128
4129         /* add timeout */
4130         ctimeout = lp_winbind_cache_time() + time(NULL);
4131         SBVAL(blob.dptr, 8, ctimeout);
4132
4133         /* copy the rest */
4134         memcpy(blob.dptr + 16, data.dptr + 8, data.dsize - 8);
4135
4136         if (tdb_store(tdb, key, blob, TDB_REPLACE) < 0) {
4137                 DEBUG(0, ("tdb_store to update [%s] failed!\n",
4138                           key.dptr));
4139                 SAFE_FREE(blob.dptr);
4140                 return 1;
4141         }
4142
4143         SAFE_FREE(blob.dptr);
4144         return 0;
4145 }
4146
4147 static bool wbcache_upgrade_v1_to_v2(TDB_CONTEXT *tdb)
4148 {
4149         int rc;
4150
4151         DEBUG(1, ("Upgrade to version 2 of the winbindd_cache.tdb\n"));
4152
4153         rc = tdb_traverse(tdb, wbcache_update_centry_fn, NULL);
4154         if (rc < 0) {
4155                 return false;
4156         }
4157
4158         return true;
4159 }
4160
4161 /***********************************************************************
4162  Try and validate every entry in the winbindd cache. If we fail here,
4163  delete the cache tdb and return non-zero.
4164 ***********************************************************************/
4165
4166 int winbindd_validate_cache(void)
4167 {
4168         int ret = -1;
4169         const char *tdb_path = state_path("winbindd_cache.tdb");
4170         TDB_CONTEXT *tdb = NULL;
4171         uint32_t vers_id;
4172         bool ok;
4173
4174         DEBUG(10, ("winbindd_validate_cache: replacing panic function\n"));
4175         smb_panic_fn = validate_panic;
4176
4177         tdb = tdb_open_log(tdb_path, 
4178                            WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
4179                            TDB_INCOMPATIBLE_HASH |
4180                            ( lp_winbind_offline_logon() 
4181                              ? TDB_DEFAULT 
4182                              : TDB_DEFAULT | TDB_CLEAR_IF_FIRST ),
4183                            O_RDWR|O_CREAT, 
4184                            0600);
4185         if (!tdb) {
4186                 DEBUG(0, ("winbindd_validate_cache: "
4187                           "error opening/initializing tdb\n"));
4188                 goto done;
4189         }
4190
4191         /* Version check and upgrade code. */
4192         if (!tdb_fetch_uint32(tdb, WINBINDD_CACHE_VERSION_KEYSTR, &vers_id)) {
4193                 DEBUG(10, ("Fresh database\n"));
4194                 tdb_store_uint32(tdb, WINBINDD_CACHE_VERSION_KEYSTR, WINBINDD_CACHE_VERSION);
4195                 vers_id = WINBINDD_CACHE_VERSION;
4196         }
4197
4198         if (vers_id != WINBINDD_CACHE_VERSION) {
4199                 if (vers_id == WINBINDD_CACHE_VER1) {
4200                         ok = wbcache_upgrade_v1_to_v2(tdb);
4201                         if (!ok) {
4202                                 DEBUG(10, ("winbindd_validate_cache: upgrade to version 2 failed.\n"));
4203                                 unlink(tdb_path);
4204                                 goto done;
4205                         }
4206
4207                         tdb_store_uint32(tdb,
4208                                          WINBINDD_CACHE_VERSION_KEYSTR,
4209                                          WINBINDD_CACHE_VERSION);
4210                         vers_id = WINBINDD_CACHE_VER2;
4211                 }
4212         }
4213
4214         tdb_close(tdb);
4215
4216         ret = tdb_validate_and_backup(tdb_path, cache_traverse_validate_fn);
4217
4218         if (ret != 0) {
4219                 DEBUG(10, ("winbindd_validate_cache: validation not successful.\n"));
4220                 DEBUGADD(10, ("removing tdb %s.\n", tdb_path));
4221                 unlink(tdb_path);
4222         }
4223
4224 done:
4225         DEBUG(10, ("winbindd_validate_cache: restoring panic function\n"));
4226         smb_panic_fn = smb_panic;
4227         return ret;
4228 }
4229
4230 /***********************************************************************
4231  Try and validate every entry in the winbindd cache.
4232 ***********************************************************************/
4233
4234 int winbindd_validate_cache_nobackup(void)
4235 {
4236         int ret = -1;
4237         const char *tdb_path = state_path("winbindd_cache.tdb");
4238
4239         DEBUG(10, ("winbindd_validate_cache: replacing panic function\n"));
4240         smb_panic_fn = validate_panic;
4241
4242
4243         if (wcache == NULL || wcache->tdb == NULL) {
4244                 ret = tdb_validate_open(tdb_path, cache_traverse_validate_fn);
4245         } else {
4246                 ret = tdb_validate(wcache->tdb, cache_traverse_validate_fn);
4247         }
4248
4249         if (ret != 0) {
4250                 DEBUG(10, ("winbindd_validate_cache_nobackup: validation not "
4251                            "successful.\n"));
4252         }
4253
4254         DEBUG(10, ("winbindd_validate_cache_nobackup: restoring panic "
4255                    "function\n"));
4256         smb_panic_fn = smb_panic;
4257         return ret;
4258 }
4259
4260 bool winbindd_cache_validate_and_initialize(void)
4261 {
4262         close_winbindd_cache();
4263
4264         if (lp_winbind_offline_logon()) {
4265                 if (winbindd_validate_cache() < 0) {
4266                         DEBUG(0, ("winbindd cache tdb corrupt and no backup "
4267                                   "could be restored.\n"));
4268                 }
4269         }
4270
4271         return initialize_winbindd_cache();
4272 }
4273
4274 /*********************************************************************
4275  ********************************************************************/
4276
4277 static bool add_wbdomain_to_tdc_array( struct winbindd_domain *new_dom,
4278                                        struct winbindd_tdc_domain **domains, 
4279                                        size_t *num_domains )
4280 {
4281         struct winbindd_tdc_domain *list = NULL;
4282         size_t idx;
4283         int i;
4284         bool set_only = false;
4285
4286         /* don't allow duplicates */
4287
4288         idx = *num_domains;
4289         list = *domains;
4290
4291         for ( i=0; i< (*num_domains); i++ ) {
4292                 if ( strequal( new_dom->name, list[i].domain_name ) ) {
4293                         DEBUG(10,("add_wbdomain_to_tdc_array: Found existing record for %s\n",
4294                                   new_dom->name));
4295                         idx = i;
4296                         set_only = true;
4297
4298                         break;
4299                 }
4300         }
4301
4302         if ( !set_only ) {
4303                 if ( !*domains ) {
4304                         list = talloc_array( NULL, struct winbindd_tdc_domain, 1 );
4305                         idx = 0;
4306                 } else {
4307                         list = talloc_realloc( *domains, *domains, 
4308                                                      struct winbindd_tdc_domain,  
4309                                                      (*num_domains)+1);
4310                         idx = *num_domains;             
4311                 }
4312
4313                 ZERO_STRUCT( list[idx] );
4314         }
4315
4316         if ( !list )
4317                 return false;
4318
4319         list[idx].domain_name = talloc_strdup( list, new_dom->name );
4320         list[idx].dns_name = talloc_strdup( list, new_dom->alt_name );
4321
4322         if ( !is_null_sid( &new_dom->sid ) ) {
4323                 sid_copy( &list[idx].sid, &new_dom->sid );
4324         } else {
4325                 sid_copy(&list[idx].sid, &global_sid_NULL);
4326         }
4327
4328         if ( new_dom->domain_flags != 0x0 )
4329                 list[idx].trust_flags = new_dom->domain_flags;  
4330
4331         if ( new_dom->domain_type != 0x0 )
4332                 list[idx].trust_type = new_dom->domain_type;
4333
4334         if ( new_dom->domain_trust_attribs != 0x0 )
4335                 list[idx].trust_attribs = new_dom->domain_trust_attribs;
4336
4337         if ( !set_only ) {
4338                 *domains = list;
4339                 *num_domains = idx + 1; 
4340         }
4341
4342         return true;
4343 }
4344
4345 /*********************************************************************
4346  ********************************************************************/
4347
4348 static TDB_DATA make_tdc_key( const char *domain_name )
4349 {
4350         char *keystr = NULL;
4351         TDB_DATA key = { NULL, 0 };
4352
4353         if ( !domain_name ) {
4354                 DEBUG(5,("make_tdc_key: Keyname workgroup is NULL!\n"));
4355                 return key;
4356         }
4357
4358         if (asprintf( &keystr, "TRUSTDOMCACHE/%s", domain_name ) == -1) {
4359                 return key;
4360         }
4361         key = string_term_tdb_data(keystr);
4362
4363         return key;     
4364 }
4365
4366 /*********************************************************************
4367  ********************************************************************/
4368
4369 static int pack_tdc_domains( struct winbindd_tdc_domain *domains, 
4370                              size_t num_domains,
4371                              unsigned char **buf )
4372 {
4373         unsigned char *buffer = NULL;
4374         int len = 0;
4375         int buflen = 0;
4376         int i = 0;
4377
4378         DEBUG(10,("pack_tdc_domains: Packing %d trusted domains\n",
4379                   (int)num_domains));
4380
4381         buflen = 0;
4382
4383  again: 
4384         len = 0;
4385
4386         /* Store the number of array items first */
4387         len += tdb_pack( buffer+len, buflen-len, "d", 
4388                          num_domains );
4389
4390         /* now pack each domain trust record */
4391         for ( i=0; i<num_domains; i++ ) {
4392
4393                 fstring tmp;
4394
4395                 if ( buflen > 0 ) {
4396                         DEBUG(10,("pack_tdc_domains: Packing domain %s (%s)\n",
4397                                   domains[i].domain_name,
4398                                   domains[i].dns_name ? domains[i].dns_name : "UNKNOWN" ));
4399                 }
4400
4401                 len += tdb_pack( buffer+len, buflen-len, "fffddd",
4402                                  domains[i].domain_name,
4403                                  domains[i].dns_name,
4404                                  sid_to_fstring(tmp, &domains[i].sid),
4405                                  domains[i].trust_flags,
4406                                  domains[i].trust_attribs,
4407                                  domains[i].trust_type );
4408         }
4409
4410         if ( buflen < len ) {
4411                 SAFE_FREE(buffer);
4412                 if ( (buffer = SMB_MALLOC_ARRAY(unsigned char, len)) == NULL ) {
4413                         DEBUG(0,("pack_tdc_domains: failed to alloc buffer!\n"));
4414                         buflen = -1;
4415                         goto done;
4416                 }
4417                 buflen = len;
4418                 goto again;
4419         }
4420
4421         *buf = buffer;  
4422
4423  done:  
4424         return buflen;  
4425 }
4426
4427 /*********************************************************************
4428  ********************************************************************/
4429
4430 static size_t unpack_tdc_domains( unsigned char *buf, int buflen, 
4431                                   struct winbindd_tdc_domain **domains )
4432 {
4433         fstring domain_name, dns_name, sid_string;      
4434         uint32 type, attribs, flags;
4435         int num_domains;
4436         int len = 0;
4437         int i;
4438         struct winbindd_tdc_domain *list = NULL;
4439
4440         /* get the number of domains */
4441         len += tdb_unpack( buf+len, buflen-len, "d", &num_domains);
4442         if ( len == -1 ) {
4443                 DEBUG(5,("unpack_tdc_domains: Failed to unpack domain array\n"));               
4444                 return 0;
4445         }
4446
4447         list = talloc_array( NULL, struct winbindd_tdc_domain, num_domains );
4448         if ( !list ) {
4449                 DEBUG(0,("unpack_tdc_domains: Failed to talloc() domain list!\n"));
4450                 return 0;               
4451         }
4452
4453         for ( i=0; i<num_domains; i++ ) {
4454                 len += tdb_unpack( buf+len, buflen-len, "fffddd",
4455                                    domain_name,
4456                                    dns_name,
4457                                    sid_string,
4458                                    &flags,
4459                                    &attribs,
4460                                    &type );
4461
4462                 if ( len == -1 ) {
4463                         DEBUG(5,("unpack_tdc_domains: Failed to unpack domain array\n"));
4464                         TALLOC_FREE( list );                    
4465                         return 0;
4466                 }
4467
4468                 DEBUG(11,("unpack_tdc_domains: Unpacking domain %s (%s) "
4469                           "SID %s, flags = 0x%x, attribs = 0x%x, type = 0x%x\n",
4470                           domain_name, dns_name, sid_string,
4471                           flags, attribs, type));
4472
4473                 list[i].domain_name = talloc_strdup( list, domain_name );
4474                 list[i].dns_name = talloc_strdup( list, dns_name );
4475                 if ( !string_to_sid( &(list[i].sid), sid_string ) ) {                   
4476                         DEBUG(10,("unpack_tdc_domains: no SID for domain %s\n",
4477                                   domain_name));
4478                 }
4479                 list[i].trust_flags = flags;
4480                 list[i].trust_attribs = attribs;
4481                 list[i].trust_type = type;
4482         }
4483
4484         *domains = list;
4485
4486         return num_domains;
4487 }
4488
4489 /*********************************************************************
4490  ********************************************************************/
4491
4492 static bool wcache_tdc_store_list( struct winbindd_tdc_domain *domains, size_t num_domains )
4493 {
4494         TDB_DATA key = make_tdc_key( lp_workgroup() );   
4495         TDB_DATA data = { NULL, 0 };
4496         int ret;
4497
4498         if ( !key.dptr )
4499                 return false;
4500
4501         /* See if we were asked to delete the cache entry */
4502
4503         if ( !domains ) {
4504                 ret = tdb_delete( wcache->tdb, key );
4505                 goto done;
4506         }
4507
4508         data.dsize = pack_tdc_domains( domains, num_domains, &data.dptr );
4509
4510         if ( !data.dptr ) {
4511                 ret = -1;
4512                 goto done;
4513         }
4514
4515         ret = tdb_store( wcache->tdb, key, data, 0 );
4516
4517  done:
4518         SAFE_FREE( data.dptr );
4519         SAFE_FREE( key.dptr );
4520
4521         return ( ret == 0 );
4522 }
4523
4524 /*********************************************************************
4525  ********************************************************************/
4526
4527 bool wcache_tdc_fetch_list( struct winbindd_tdc_domain **domains, size_t *num_domains )
4528 {
4529         TDB_DATA key = make_tdc_key( lp_workgroup() );
4530         TDB_DATA data = { NULL, 0 };
4531
4532         *domains = NULL;        
4533         *num_domains = 0;       
4534
4535         if ( !key.dptr )
4536                 return false;
4537
4538         data = tdb_fetch_compat( wcache->tdb, key );
4539
4540         SAFE_FREE( key.dptr );
4541
4542         if ( !data.dptr ) 
4543                 return false;
4544
4545         *num_domains = unpack_tdc_domains( data.dptr, data.dsize, domains );
4546
4547         SAFE_FREE( data.dptr );
4548
4549         if ( !*domains )
4550                 return false;
4551
4552         return true;
4553 }
4554
4555 /*********************************************************************
4556  ********************************************************************/
4557
4558 bool wcache_tdc_add_domain( struct winbindd_domain *domain )
4559 {
4560         struct winbindd_tdc_domain *dom_list = NULL;
4561         size_t num_domains = 0;
4562         bool ret = false;
4563
4564         DEBUG(10,("wcache_tdc_add_domain: Adding domain %s (%s), SID %s, "
4565                   "flags = 0x%x, attributes = 0x%x, type = 0x%x\n",
4566                   domain->name, domain->alt_name, 
4567                   sid_string_dbg(&domain->sid),
4568                   domain->domain_flags,
4569                   domain->domain_trust_attribs,
4570                   domain->domain_type));        
4571
4572         if ( !init_wcache() ) {
4573                 return false;
4574         }
4575
4576         /* fetch the list */
4577
4578         wcache_tdc_fetch_list( &dom_list, &num_domains );
4579
4580         /* add the new domain */
4581
4582         if ( !add_wbdomain_to_tdc_array( domain, &dom_list, &num_domains ) ) {
4583                 goto done;              
4584         }       
4585
4586         /* pack the domain */
4587
4588         if ( !wcache_tdc_store_list( dom_list, num_domains ) ) {
4589                 goto done;              
4590         }
4591
4592         /* Success */
4593
4594         ret = true;
4595  done:
4596         TALLOC_FREE( dom_list );
4597
4598         return ret;     
4599 }
4600
4601 /*********************************************************************
4602  ********************************************************************/
4603
4604 struct winbindd_tdc_domain * wcache_tdc_fetch_domain( TALLOC_CTX *ctx, const char *name )
4605 {
4606         struct winbindd_tdc_domain *dom_list = NULL;
4607         size_t num_domains = 0;
4608         int i;
4609         struct winbindd_tdc_domain *d = NULL;   
4610
4611         DEBUG(10,("wcache_tdc_fetch_domain: Searching for domain %s\n", name));
4612
4613         if ( !init_wcache() ) {
4614                 return NULL;
4615         }
4616
4617         /* fetch the list */
4618
4619         wcache_tdc_fetch_list( &dom_list, &num_domains );
4620
4621         for ( i=0; i<num_domains; i++ ) {
4622                 if ( strequal(name, dom_list[i].domain_name) ||
4623                      strequal(name, dom_list[i].dns_name) )
4624                 {
4625                         DEBUG(10,("wcache_tdc_fetch_domain: Found domain %s\n",
4626                                   name));
4627
4628                         d = talloc( ctx, struct winbindd_tdc_domain );
4629                         if ( !d )
4630                                 break;                  
4631
4632                         d->domain_name = talloc_strdup( d, dom_list[i].domain_name );
4633                         d->dns_name = talloc_strdup( d, dom_list[i].dns_name );
4634                         sid_copy( &d->sid, &dom_list[i].sid );
4635                         d->trust_flags   = dom_list[i].trust_flags;
4636                         d->trust_type    = dom_list[i].trust_type;
4637                         d->trust_attribs = dom_list[i].trust_attribs;
4638
4639                         break;
4640                 }
4641         }
4642
4643         TALLOC_FREE( dom_list );
4644
4645         return d;       
4646 }
4647
4648 /*********************************************************************
4649  ********************************************************************/
4650
4651 struct winbindd_tdc_domain*
4652         wcache_tdc_fetch_domainbysid(TALLOC_CTX *ctx,
4653                                      const struct dom_sid *sid)
4654 {
4655         struct winbindd_tdc_domain *dom_list = NULL;
4656         size_t num_domains = 0;
4657         int i;
4658         struct winbindd_tdc_domain *d = NULL;
4659
4660         DEBUG(10,("wcache_tdc_fetch_domainbysid: Searching for domain %s\n",
4661                   sid_string_dbg(sid)));
4662
4663         if (!init_wcache()) {
4664                 return NULL;
4665         }
4666
4667         /* fetch the list */
4668
4669         wcache_tdc_fetch_list(&dom_list, &num_domains);
4670
4671         for (i = 0; i<num_domains; i++) {
4672                 if (dom_sid_equal(sid, &(dom_list[i].sid))) {
4673                         DEBUG(10, ("wcache_tdc_fetch_domainbysid: "
4674                                    "Found domain %s for SID %s\n",
4675                                    dom_list[i].domain_name,
4676                                    sid_string_dbg(sid)));
4677
4678                         d = talloc(ctx, struct winbindd_tdc_domain);
4679                         if (!d)
4680                                 break;
4681
4682                         d->domain_name = talloc_strdup(d,
4683                                                        dom_list[i].domain_name);
4684
4685                         d->dns_name = talloc_strdup(d, dom_list[i].dns_name);
4686                         sid_copy(&d->sid, &dom_list[i].sid);
4687                         d->trust_flags = dom_list[i].trust_flags;
4688                         d->trust_type = dom_list[i].trust_type;
4689                         d->trust_attribs = dom_list[i].trust_attribs;
4690
4691                         break;
4692                 }
4693         }
4694
4695         TALLOC_FREE(dom_list);
4696
4697         return d;
4698 }
4699
4700
4701 /*********************************************************************
4702  ********************************************************************/
4703
4704 void wcache_tdc_clear( void )
4705 {
4706         if ( !init_wcache() )
4707                 return;
4708
4709         wcache_tdc_store_list( NULL, 0 );
4710
4711         return; 
4712 }
4713
4714
4715 /*********************************************************************
4716  ********************************************************************/
4717
4718 static void wcache_save_user_pwinfo(struct winbindd_domain *domain, 
4719                                     NTSTATUS status,
4720                                     const struct dom_sid *user_sid,
4721                                     const char *homedir,
4722                                     const char *shell,
4723                                     const char *gecos,
4724                                     uint32 gid)
4725 {
4726         struct cache_entry *centry;
4727         fstring tmp;
4728
4729         if ( (centry = centry_start(domain, status)) == NULL )
4730                 return;
4731
4732         centry_put_string( centry, homedir );
4733         centry_put_string( centry, shell );
4734         centry_put_string( centry, gecos );
4735         centry_put_uint32( centry, gid );
4736
4737         centry_end(centry, "NSS/PWINFO/%s", sid_to_fstring(tmp, user_sid) );
4738
4739         DEBUG(10,("wcache_save_user_pwinfo: %s\n", sid_string_dbg(user_sid) ));
4740
4741         centry_free(centry);
4742 }
4743
4744 #ifdef HAVE_ADS
4745
4746 NTSTATUS nss_get_info_cached( struct winbindd_domain *domain, 
4747                               const struct dom_sid *user_sid,
4748                               TALLOC_CTX *ctx,
4749                               const char **homedir, const char **shell,
4750                               const char **gecos, gid_t *p_gid)
4751 {
4752         struct winbind_cache *cache = get_cache(domain);
4753         struct cache_entry *centry = NULL;
4754         NTSTATUS nt_status;
4755         fstring tmp;
4756
4757         if (!cache->tdb)
4758                 goto do_query;
4759
4760         centry = wcache_fetch(cache, domain, "NSS/PWINFO/%s",
4761                               sid_to_fstring(tmp, user_sid));
4762
4763         if (!centry)
4764                 goto do_query;
4765
4766         *homedir = centry_string( centry, ctx );
4767         *shell   = centry_string( centry, ctx );
4768         *gecos   = centry_string( centry, ctx );
4769         *p_gid   = centry_uint32( centry );     
4770
4771         centry_free(centry);
4772
4773         DEBUG(10,("nss_get_info_cached: [Cached] - user_sid %s\n",
4774                   sid_string_dbg(user_sid)));
4775
4776         return NT_STATUS_OK;
4777
4778 do_query:
4779
4780         nt_status = nss_get_info( domain->name, user_sid, ctx,
4781                                   homedir, shell, gecos, p_gid );
4782
4783         DEBUG(10, ("nss_get_info returned %s\n", nt_errstr(nt_status)));
4784
4785         if ( NT_STATUS_IS_OK(nt_status) ) {
4786                 DEBUG(10, ("result:\n\thomedir = '%s'\n", *homedir));
4787                 DEBUGADD(10, ("\tshell = '%s'\n", *shell));
4788                 DEBUGADD(10, ("\tgecos = '%s'\n", *gecos));
4789                 DEBUGADD(10, ("\tgid = '%u'\n", (unsigned int)*p_gid));
4790
4791                 wcache_save_user_pwinfo( domain, nt_status, user_sid,
4792                                          *homedir, *shell, *gecos, *p_gid );
4793         }       
4794
4795         if ( NT_STATUS_EQUAL( nt_status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND ) ) {
4796                 DEBUG(5,("nss_get_info_cached: Setting domain %s offline\n",
4797                          domain->name ));
4798                 set_domain_offline( domain );
4799         }
4800
4801         return nt_status;       
4802 }
4803
4804 #endif
4805
4806 /* the cache backend methods are exposed via this structure */
4807 struct winbindd_methods cache_methods = {
4808         true,
4809         query_user_list,
4810         enum_dom_groups,
4811         enum_local_groups,
4812         name_to_sid,
4813         sid_to_name,
4814         rids_to_names,
4815         query_user,
4816         lookup_usergroups,
4817         lookup_useraliases,
4818         lookup_groupmem,
4819         sequence_number,
4820         lockout_policy,
4821         password_policy,
4822         trusted_domains
4823 };
4824
4825 static bool wcache_ndr_key(TALLOC_CTX *mem_ctx, char *domain_name,
4826                            uint32_t opnum, const DATA_BLOB *req,
4827                            TDB_DATA *pkey)
4828 {
4829         char *key;
4830         size_t keylen;
4831
4832         key = talloc_asprintf(mem_ctx, "NDR/%s/%d/", domain_name, (int)opnum);
4833         if (key == NULL) {
4834                 return false;
4835         }
4836         keylen = talloc_get_size(key) - 1;
4837
4838         key = talloc_realloc(mem_ctx, key, char, keylen + req->length);
4839         if (key == NULL) {
4840                 return false;
4841         }
4842         memcpy(key + keylen, req->data, req->length);
4843
4844         pkey->dptr = (uint8_t *)key;
4845         pkey->dsize = talloc_get_size(key);
4846         return true;
4847 }
4848
4849 static bool wcache_opnum_cacheable(uint32_t opnum)
4850 {
4851         switch (opnum) {
4852         case NDR_WBINT_PING:
4853         case NDR_WBINT_QUERYSEQUENCENUMBER:
4854         case NDR_WBINT_ALLOCATEUID:
4855         case NDR_WBINT_ALLOCATEGID:
4856         case NDR_WBINT_CHECKMACHINEACCOUNT:
4857         case NDR_WBINT_CHANGEMACHINEACCOUNT:
4858         case NDR_WBINT_PINGDC:
4859                 return false;
4860         }
4861         return true;
4862 }
4863
4864 bool wcache_fetch_ndr(TALLOC_CTX *mem_ctx, struct winbindd_domain *domain,
4865                       uint32_t opnum, const DATA_BLOB *req, DATA_BLOB *resp)
4866 {
4867         TDB_DATA key, data;
4868         bool ret = false;
4869
4870         if (!wcache_opnum_cacheable(opnum) ||
4871             is_my_own_sam_domain(domain) ||
4872             is_builtin_domain(domain)) {
4873                 return false;
4874         }
4875
4876         if (wcache->tdb == NULL) {
4877                 return false;
4878         }
4879
4880         if (!wcache_ndr_key(talloc_tos(), domain->name, opnum, req, &key)) {
4881                 return false;
4882         }
4883         data = tdb_fetch_compat(wcache->tdb, key);
4884         TALLOC_FREE(key.dptr);
4885
4886         if (data.dptr == NULL) {
4887                 return false;
4888         }
4889         if (data.dsize < 12) {
4890                 goto fail;
4891         }
4892
4893         if (!is_domain_offline(domain)) {
4894                 uint32_t entry_seqnum, dom_seqnum, last_check;
4895                 uint64_t entry_timeout;
4896
4897                 if (!wcache_fetch_seqnum(domain->name, &dom_seqnum,
4898                                          &last_check)) {
4899                         goto fail;
4900                 }
4901                 entry_seqnum = IVAL(data.dptr, 0);
4902                 if (entry_seqnum != dom_seqnum) {
4903                         DEBUG(10, ("Entry has wrong sequence number: %d\n",
4904                                    (int)entry_seqnum));
4905                         goto fail;
4906                 }
4907                 entry_timeout = BVAL(data.dptr, 4);
4908                 if (time(NULL) > entry_timeout) {
4909                         DEBUG(10, ("Entry has timed out\n"));
4910                         goto fail;
4911                 }
4912         }
4913
4914         resp->data = (uint8_t *)talloc_memdup(mem_ctx, data.dptr + 12,
4915                                               data.dsize - 12);
4916         if (resp->data == NULL) {
4917                 DEBUG(10, ("talloc failed\n"));
4918                 goto fail;
4919         }
4920         resp->length = data.dsize - 12;
4921
4922         ret = true;
4923 fail:
4924         SAFE_FREE(data.dptr);
4925         return ret;
4926 }
4927
4928 void wcache_store_ndr(struct winbindd_domain *domain, uint32_t opnum,
4929                       const DATA_BLOB *req, const DATA_BLOB *resp)
4930 {
4931         TDB_DATA key, data;
4932         uint32_t dom_seqnum, last_check;
4933         uint64_t timeout;
4934
4935         if (!wcache_opnum_cacheable(opnum) ||
4936             is_my_own_sam_domain(domain) ||
4937             is_builtin_domain(domain)) {
4938                 return;
4939         }
4940
4941         if (wcache->tdb == NULL) {
4942                 return;
4943         }
4944
4945         if (!wcache_fetch_seqnum(domain->name, &dom_seqnum, &last_check)) {
4946                 DEBUG(10, ("could not fetch seqnum for domain %s\n",
4947                            domain->name));
4948                 return;
4949         }
4950
4951         if (!wcache_ndr_key(talloc_tos(), domain->name, opnum, req, &key)) {
4952                 return;
4953         }
4954
4955         timeout = time(NULL) + lp_winbind_cache_time();
4956
4957         data.dsize = resp->length + 12;
4958         data.dptr = talloc_array(key.dptr, uint8_t, data.dsize);
4959         if (data.dptr == NULL) {
4960                 goto done;
4961         }
4962
4963         SIVAL(data.dptr, 0, dom_seqnum);
4964         SBVAL(data.dptr, 4, timeout);
4965         memcpy(data.dptr + 12, resp->data, resp->length);
4966
4967         tdb_store(wcache->tdb, key, data, 0);
4968
4969 done:
4970         TALLOC_FREE(key.dptr);
4971         return;
4972 }