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