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