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