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