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