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