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