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