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