s3:winbind add timeouts to winbind cache
[mat/samba.git] / source3 / winbindd / winbindd_cache.c
1 /* 
2    Unix SMB/CIFS implementation.
3
4    Winbind cache backend functions
5
6    Copyright (C) Andrew Tridgell 2001
7    Copyright (C) Gerald Carter   2003-2007
8    Copyright (C) Volker Lendecke 2005
9    Copyright (C) Guenther Deschner 2005
10    Copyright (C) Michael Adam    2007
11
12    This program is free software; you can redistribute it and/or modify
13    it under the terms of the GNU General Public License as published by
14    the Free Software Foundation; either version 3 of the License, or
15    (at your option) any later version.
16
17    This program is distributed in the hope that it will be useful,
18    but WITHOUT ANY WARRANTY; without even the implied warranty of
19    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20    GNU General Public License for more details.
21
22    You should have received a copy of the GNU General Public License
23    along with this program.  If not, see <http://www.gnu.org/licenses/>.
24 */
25
26 #include "includes.h"
27 #include "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 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             dom_sid_equal(&domain->sid, get_global_sam_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 from a cache entry
210 */
211 static uint64 centry_uint64(struct cache_entry *centry)
212 {
213         uint64 ret;
214
215         if (!centry_check_bytes(centry, 8)) {
216                 smb_panic_fn("centry_uint64");
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_t)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(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              dom_sid_equal(&domain->sid, get_global_sam_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             dom_sid_equal(&domain->sid, &global_sid_Builtin)) {
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 into a centry
769 */
770 static void centry_put_uint64(struct cache_entry *centry, uint64 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(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                         have_unmapped = true;
2047
2048                 } else {
2049                         /* something's definitely wrong */
2050                         result = centry->status;
2051                         goto error;
2052                 }
2053
2054                 centry_free(centry);
2055         }
2056
2057         if (!have_mapped) {
2058                 return NT_STATUS_NONE_MAPPED;
2059         }
2060         if (!have_unmapped) {
2061                 return NT_STATUS_OK;
2062         }
2063         return STATUS_SOME_UNMAPPED;
2064
2065  do_query:
2066
2067         TALLOC_FREE(*names);
2068         TALLOC_FREE(*types);
2069
2070         result = domain->backend->rids_to_names(domain, mem_ctx, domain_sid,
2071                                                 rids, num_rids, domain_name,
2072                                                 names, types);
2073
2074         if (NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
2075                 NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2076                 if (!domain->internal && old_status) {
2077                         set_domain_offline(domain);
2078                 }
2079                 if (cache->tdb &&
2080                         !domain->internal &&
2081                         !domain->online &&
2082                         old_status) {
2083                         have_mapped = have_unmapped = false;
2084
2085                         for (i=0; i<num_rids; i++) {
2086                                 struct dom_sid sid;
2087                                 struct cache_entry *centry;
2088                                 fstring tmp;
2089
2090                                 if (!sid_compose(&sid, domain_sid, rids[i])) {
2091                                         result = NT_STATUS_INTERNAL_ERROR;
2092                                         goto error;
2093                                 }
2094
2095                                 centry = wcache_fetch(cache, domain, "SN/%s",
2096                                                       sid_to_fstring(tmp, &sid));
2097                                 if (!centry) {
2098                                         (*types)[i] = SID_NAME_UNKNOWN;
2099                                         (*names)[i] = talloc_strdup(*names, "");
2100                                         continue;
2101                                 }
2102
2103                                 (*types)[i] = SID_NAME_UNKNOWN;
2104                                 (*names)[i] = talloc_strdup(*names, "");
2105
2106                                 if (NT_STATUS_IS_OK(centry->status)) {
2107                                         char *dom;
2108                                         have_mapped = true;
2109                                         (*types)[i] = (enum lsa_SidType)centry_uint32(centry);
2110
2111                                         dom = centry_string(centry, mem_ctx);
2112                                         if (*domain_name == NULL) {
2113                                                 *domain_name = dom;
2114                                         } else {
2115                                                 talloc_free(dom);
2116                                         }
2117
2118                                         (*names)[i] = centry_string(centry, *names);
2119
2120                                 } else if (NT_STATUS_EQUAL(centry->status, NT_STATUS_NONE_MAPPED)) {
2121                                         have_unmapped = true;
2122
2123                                 } else {
2124                                         /* something's definitely wrong */
2125                                         result = centry->status;
2126                                         goto error;
2127                                 }
2128
2129                                 centry_free(centry);
2130                         }
2131
2132                         if (!have_mapped) {
2133                                 return NT_STATUS_NONE_MAPPED;
2134                         }
2135                         if (!have_unmapped) {
2136                                 return NT_STATUS_OK;
2137                         }
2138                         return STATUS_SOME_UNMAPPED;
2139                 }
2140         }
2141         /*
2142           None of the queried rids has been found so save all negative entries
2143         */
2144         if (NT_STATUS_EQUAL(result, NT_STATUS_NONE_MAPPED)) {
2145                 for (i = 0; i < num_rids; i++) {
2146                         struct dom_sid sid;
2147                         const char *name = "";
2148                         const enum lsa_SidType type = SID_NAME_UNKNOWN;
2149                         NTSTATUS status = NT_STATUS_NONE_MAPPED;
2150
2151                         if (!sid_compose(&sid, domain_sid, rids[i])) {
2152                                 return NT_STATUS_INTERNAL_ERROR;
2153                         }
2154
2155                         wcache_save_sid_to_name(domain, status, &sid, *domain_name,
2156                                                 name, type);
2157                 }
2158
2159                 return result;
2160         }
2161
2162         /*
2163           Some or all of the queried rids have been found.
2164         */
2165         if (!NT_STATUS_IS_OK(result) &&
2166             !NT_STATUS_EQUAL(result, STATUS_SOME_UNMAPPED)) {
2167                 return result;
2168         }
2169
2170         refresh_sequence_number(domain, false);
2171
2172         for (i=0; i<num_rids; i++) {
2173                 struct dom_sid sid;
2174                 NTSTATUS status;
2175
2176                 if (!sid_compose(&sid, domain_sid, rids[i])) {
2177                         result = NT_STATUS_INTERNAL_ERROR;
2178                         goto error;
2179                 }
2180
2181                 status = (*types)[i] == SID_NAME_UNKNOWN ?
2182                         NT_STATUS_NONE_MAPPED : NT_STATUS_OK;
2183
2184                 wcache_save_sid_to_name(domain, status, &sid, *domain_name,
2185                                         (*names)[i], (*types)[i]);
2186         }
2187
2188         return result;
2189
2190  error:
2191         TALLOC_FREE(*names);
2192         TALLOC_FREE(*types);
2193         return result;
2194 }
2195
2196 NTSTATUS wcache_query_user(struct winbindd_domain *domain,
2197                            TALLOC_CTX *mem_ctx,
2198                            const struct dom_sid *user_sid,
2199                            struct wbint_userinfo *info)
2200 {
2201         struct winbind_cache *cache = get_cache(domain);
2202         struct cache_entry *centry = NULL;
2203         NTSTATUS status;
2204         char *sid_string;
2205
2206         if (cache->tdb == NULL) {
2207                 return NT_STATUS_NOT_FOUND;
2208         }
2209
2210         sid_string = sid_string_tos(user_sid);
2211         if (sid_string == NULL) {
2212                 return NT_STATUS_NO_MEMORY;
2213         }
2214
2215         centry = wcache_fetch(cache, domain, "U/%s", sid_string);
2216         TALLOC_FREE(sid_string);
2217         if (centry == NULL) {
2218                 return NT_STATUS_NOT_FOUND;
2219         }
2220
2221         /*
2222          * If we have an access denied cache entry and a cached info3
2223          * in the samlogon cache then do a query.  This will force the
2224          * rpc back end to return the info3 data.
2225          */
2226
2227         if (NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED) &&
2228             netsamlogon_cache_have(user_sid)) {
2229                 DEBUG(10, ("query_user: cached access denied and have cached "
2230                            "info3\n"));
2231                 domain->last_status = NT_STATUS_OK;
2232                 centry_free(centry);
2233                 return NT_STATUS_NOT_FOUND;
2234         }
2235
2236         /* if status is not ok then this is a negative hit
2237            and the rest of the data doesn't matter */
2238         status = centry->status;
2239         if (NT_STATUS_IS_OK(status)) {
2240                 info->acct_name = centry_string(centry, mem_ctx);
2241                 info->full_name = centry_string(centry, mem_ctx);
2242                 info->homedir = centry_string(centry, mem_ctx);
2243                 info->shell = centry_string(centry, mem_ctx);
2244                 info->primary_gid = centry_uint32(centry);
2245                 centry_sid(centry, &info->user_sid);
2246                 centry_sid(centry, &info->group_sid);
2247         }
2248
2249         DEBUG(10,("query_user: [Cached] - cached info for domain %s status: "
2250                   "%s\n", domain->name, nt_errstr(status) ));
2251
2252         centry_free(centry);
2253         return status;
2254 }
2255
2256 /* Lookup user information from a rid */
2257 static NTSTATUS query_user(struct winbindd_domain *domain,
2258                            TALLOC_CTX *mem_ctx,
2259                            const struct dom_sid *user_sid,
2260                            struct wbint_userinfo *info)
2261 {
2262         NTSTATUS status;
2263         bool old_status;
2264
2265         old_status = domain->online;
2266         status = wcache_query_user(domain, mem_ctx, user_sid, info);
2267         if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2268                 return status;
2269         }
2270
2271         ZERO_STRUCTP(info);
2272
2273         /* Return status value returned by seq number check */
2274
2275         if (!NT_STATUS_IS_OK(domain->last_status))
2276                 return domain->last_status;
2277
2278         DEBUG(10,("query_user: [Cached] - doing backend query for info for domain %s\n",
2279                 domain->name ));
2280
2281         status = domain->backend->query_user(domain, mem_ctx, user_sid, info);
2282
2283         if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2284                 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2285                 if (!domain->internal && old_status) {
2286                         set_domain_offline(domain);
2287                 }
2288                 if (!domain->internal &&
2289                         !domain->online &&
2290                         old_status) {
2291                         NTSTATUS cache_status;
2292                         cache_status = wcache_query_user(domain, mem_ctx, user_sid, info);
2293                         return cache_status;
2294                 }
2295         }
2296         /* and save it */
2297         refresh_sequence_number(domain, false);
2298         if (!NT_STATUS_IS_OK(status)) {
2299                 return status;
2300         }
2301         wcache_save_user(domain, status, info);
2302
2303         return status;
2304 }
2305
2306 NTSTATUS wcache_lookup_usergroups(struct winbindd_domain *domain,
2307                                   TALLOC_CTX *mem_ctx,
2308                                   const struct dom_sid *user_sid,
2309                                   uint32_t *pnum_sids,
2310                                   struct dom_sid **psids)
2311 {
2312         struct winbind_cache *cache = get_cache(domain);
2313         struct cache_entry *centry = NULL;
2314         NTSTATUS status;
2315         uint32_t i, num_sids;
2316         struct dom_sid *sids;
2317         fstring sid_string;
2318
2319         if (cache->tdb == NULL) {
2320                 return NT_STATUS_NOT_FOUND;
2321         }
2322
2323         centry = wcache_fetch(cache, domain, "UG/%s",
2324                               sid_to_fstring(sid_string, user_sid));
2325         if (centry == NULL) {
2326                 return NT_STATUS_NOT_FOUND;
2327         }
2328
2329         /* If we have an access denied cache entry and a cached info3 in the
2330            samlogon cache then do a query.  This will force the rpc back end
2331            to return the info3 data. */
2332
2333         if (NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)
2334             && netsamlogon_cache_have(user_sid)) {
2335                 DEBUG(10, ("lookup_usergroups: cached access denied and have "
2336                            "cached info3\n"));
2337                 domain->last_status = NT_STATUS_OK;
2338                 centry_free(centry);
2339                 return NT_STATUS_NOT_FOUND;
2340         }
2341
2342         num_sids = centry_uint32(centry);
2343         sids = talloc_array(mem_ctx, struct dom_sid, num_sids);
2344         if (sids == NULL) {
2345                 centry_free(centry);
2346                 return NT_STATUS_NO_MEMORY;
2347         }
2348
2349         for (i=0; i<num_sids; i++) {
2350                 centry_sid(centry, &sids[i]);
2351         }
2352
2353         status = centry->status;
2354
2355         DEBUG(10,("lookup_usergroups: [Cached] - cached info for domain %s "
2356                   "status: %s\n", domain->name, nt_errstr(status)));
2357
2358         centry_free(centry);
2359
2360         *pnum_sids = num_sids;
2361         *psids = sids;
2362         return status;
2363 }
2364
2365 /* Lookup groups a user is a member of. */
2366 static NTSTATUS lookup_usergroups(struct winbindd_domain *domain,
2367                                   TALLOC_CTX *mem_ctx,
2368                                   const struct dom_sid *user_sid,
2369                                   uint32 *num_groups, struct dom_sid **user_gids)
2370 {
2371         struct cache_entry *centry = NULL;
2372         NTSTATUS status;
2373         unsigned int i;
2374         fstring sid_string;
2375         bool old_status;
2376
2377         old_status = domain->online;
2378         status = wcache_lookup_usergroups(domain, mem_ctx, user_sid,
2379                                           num_groups, user_gids);
2380         if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2381                 return status;
2382         }
2383
2384         (*num_groups) = 0;
2385         (*user_gids) = NULL;
2386
2387         /* Return status value returned by seq number check */
2388
2389         if (!NT_STATUS_IS_OK(domain->last_status))
2390                 return domain->last_status;
2391
2392         DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info for domain %s\n",
2393                 domain->name ));
2394
2395         status = domain->backend->lookup_usergroups(domain, mem_ctx, user_sid, num_groups, user_gids);
2396
2397         if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2398                 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2399                 if (!domain->internal && old_status) {
2400                         set_domain_offline(domain);
2401                 }
2402                 if (!domain->internal &&
2403                         !domain->online &&
2404                         old_status) {
2405                         NTSTATUS cache_status;
2406                         cache_status = wcache_lookup_usergroups(domain, mem_ctx, user_sid,
2407                                                           num_groups, user_gids);
2408                         return cache_status;
2409                 }
2410         }
2411         if ( NT_STATUS_EQUAL(status, NT_STATUS_SYNCHRONIZATION_REQUIRED) )
2412                 goto skip_save;
2413
2414         /* and save it */
2415         refresh_sequence_number(domain, false);
2416         if (!NT_STATUS_IS_OK(status)) {
2417                 return status;
2418         }
2419         centry = centry_start(domain, status);
2420         if (!centry)
2421                 goto skip_save;
2422
2423         centry_put_uint32(centry, *num_groups);
2424         for (i=0; i<(*num_groups); i++) {
2425                 centry_put_sid(centry, &(*user_gids)[i]);
2426         }       
2427
2428         centry_end(centry, "UG/%s", sid_to_fstring(sid_string, user_sid));
2429         centry_free(centry);
2430
2431 skip_save:
2432         return status;
2433 }
2434
2435 static char *wcache_make_sidlist(TALLOC_CTX *mem_ctx, uint32_t num_sids,
2436                                  const struct dom_sid *sids)
2437 {
2438         uint32_t i;
2439         char *sidlist;
2440
2441         sidlist = talloc_strdup(mem_ctx, "");
2442         if (sidlist == NULL) {
2443                 return NULL;
2444         }
2445         for (i=0; i<num_sids; i++) {
2446                 fstring tmp;
2447                 sidlist = talloc_asprintf_append_buffer(
2448                         sidlist, "/%s", sid_to_fstring(tmp, &sids[i]));
2449                 if (sidlist == NULL) {
2450                         return NULL;
2451                 }
2452         }
2453         return sidlist;
2454 }
2455
2456 NTSTATUS wcache_lookup_useraliases(struct winbindd_domain *domain,
2457                                    TALLOC_CTX *mem_ctx, uint32_t num_sids,
2458                                    const struct dom_sid *sids,
2459                                    uint32_t *pnum_aliases, uint32_t **paliases)
2460 {
2461         struct winbind_cache *cache = get_cache(domain);
2462         struct cache_entry *centry = NULL;
2463         uint32_t num_aliases;
2464         uint32_t *aliases;
2465         NTSTATUS status;
2466         char *sidlist;
2467         int i;
2468
2469         if (cache->tdb == NULL) {
2470                 return NT_STATUS_NOT_FOUND;
2471         }
2472
2473         if (num_sids == 0) {
2474                 *pnum_aliases = 0;
2475                 *paliases = NULL;
2476                 return NT_STATUS_OK;
2477         }
2478
2479         /* We need to cache indexed by the whole list of SIDs, the aliases
2480          * resulting might come from any of the SIDs. */
2481
2482         sidlist = wcache_make_sidlist(talloc_tos(), num_sids, sids);
2483         if (sidlist == NULL) {
2484                 return NT_STATUS_NO_MEMORY;
2485         }
2486
2487         centry = wcache_fetch(cache, domain, "UA%s", sidlist);
2488         TALLOC_FREE(sidlist);
2489         if (centry == NULL) {
2490                 return NT_STATUS_NOT_FOUND;
2491         }
2492
2493         num_aliases = centry_uint32(centry);
2494         aliases = talloc_array(mem_ctx, uint32_t, num_aliases);
2495         if (aliases == NULL) {
2496                 centry_free(centry);
2497                 return NT_STATUS_NO_MEMORY;
2498         }
2499
2500         for (i=0; i<num_aliases; i++) {
2501                 aliases[i] = centry_uint32(centry);
2502         }
2503
2504         status = centry->status;
2505
2506         DEBUG(10,("lookup_useraliases: [Cached] - cached info for domain: %s "
2507                   "status %s\n", domain->name, nt_errstr(status)));
2508
2509         centry_free(centry);
2510
2511         *pnum_aliases = num_aliases;
2512         *paliases = aliases;
2513
2514         return status;
2515 }
2516
2517 static NTSTATUS lookup_useraliases(struct winbindd_domain *domain,
2518                                    TALLOC_CTX *mem_ctx,
2519                                    uint32 num_sids, const struct dom_sid *sids,
2520                                    uint32 *num_aliases, uint32 **alias_rids)
2521 {
2522         struct cache_entry *centry = NULL;
2523         NTSTATUS status;
2524         char *sidlist;
2525         int i;
2526         bool old_status;
2527
2528         old_status = domain->online;
2529         status = wcache_lookup_useraliases(domain, mem_ctx, num_sids, sids,
2530                                            num_aliases, alias_rids);
2531         if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2532                 return status;
2533         }
2534
2535         (*num_aliases) = 0;
2536         (*alias_rids) = NULL;
2537
2538         if (!NT_STATUS_IS_OK(domain->last_status))
2539                 return domain->last_status;
2540
2541         DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info "
2542                   "for domain %s\n", domain->name ));
2543
2544         sidlist = wcache_make_sidlist(talloc_tos(), num_sids, sids);
2545         if (sidlist == NULL) {
2546                 return NT_STATUS_NO_MEMORY;
2547         }
2548
2549         status = domain->backend->lookup_useraliases(domain, mem_ctx,
2550                                                      num_sids, sids,
2551                                                      num_aliases, alias_rids);
2552
2553         if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2554                 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2555                 if (!domain->internal && old_status) {
2556                         set_domain_offline(domain);
2557                 }
2558                 if (!domain->internal &&
2559                         !domain->online &&
2560                         old_status) {
2561                         NTSTATUS cache_status;
2562                         cache_status = wcache_lookup_useraliases(domain, mem_ctx, num_sids,
2563                                                                  sids, num_aliases, alias_rids);
2564                         return cache_status;
2565                 }
2566         }
2567         /* and save it */
2568         refresh_sequence_number(domain, false);
2569         if (!NT_STATUS_IS_OK(status)) {
2570                 return status;
2571         }
2572         centry = centry_start(domain, status);
2573         if (!centry)
2574                 goto skip_save;
2575         centry_put_uint32(centry, *num_aliases);
2576         for (i=0; i<(*num_aliases); i++)
2577                 centry_put_uint32(centry, (*alias_rids)[i]);
2578         centry_end(centry, "UA%s", sidlist);
2579         centry_free(centry);
2580
2581  skip_save:
2582         return status;
2583 }
2584
2585 NTSTATUS wcache_lookup_groupmem(struct winbindd_domain *domain,
2586                                 TALLOC_CTX *mem_ctx,
2587                                 const struct dom_sid *group_sid,
2588                                 uint32_t *num_names,
2589                                 struct dom_sid **sid_mem, char ***names,
2590                                 uint32_t **name_types)
2591 {
2592         struct winbind_cache *cache = get_cache(domain);
2593         struct cache_entry *centry = NULL;
2594         NTSTATUS status;
2595         unsigned int i;
2596         char *sid_string;
2597
2598         if (cache->tdb == NULL) {
2599                 return NT_STATUS_NOT_FOUND;
2600         }
2601
2602         sid_string = sid_string_tos(group_sid);
2603         if (sid_string == NULL) {
2604                 return NT_STATUS_NO_MEMORY;
2605         }
2606
2607         centry = wcache_fetch(cache, domain, "GM/%s", sid_string);
2608         TALLOC_FREE(sid_string);
2609         if (centry == NULL) {
2610                 return NT_STATUS_NOT_FOUND;
2611         }
2612
2613         *sid_mem = NULL;
2614         *names = NULL;
2615         *name_types = NULL;
2616
2617         *num_names = centry_uint32(centry);
2618         if (*num_names == 0) {
2619                 centry_free(centry);
2620                 return NT_STATUS_OK;
2621         }
2622
2623         *sid_mem = talloc_array(mem_ctx, struct dom_sid, *num_names);
2624         *names = talloc_array(mem_ctx, char *, *num_names);
2625         *name_types = talloc_array(mem_ctx, uint32, *num_names);
2626
2627         if ((*sid_mem == NULL) || (*names == NULL) || (*name_types == NULL)) {
2628                 TALLOC_FREE(*sid_mem);
2629                 TALLOC_FREE(*names);
2630                 TALLOC_FREE(*name_types);
2631                 centry_free(centry);
2632                 return NT_STATUS_NO_MEMORY;
2633         }
2634
2635         for (i=0; i<(*num_names); i++) {
2636                 centry_sid(centry, &(*sid_mem)[i]);
2637                 (*names)[i] = centry_string(centry, mem_ctx);
2638                 (*name_types)[i] = centry_uint32(centry);
2639         }
2640
2641         status = centry->status;
2642
2643         DEBUG(10,("lookup_groupmem: [Cached] - cached info for domain %s "
2644                   "status: %s\n", domain->name, nt_errstr(status)));
2645
2646         centry_free(centry);
2647         return status;
2648 }
2649
2650 static NTSTATUS lookup_groupmem(struct winbindd_domain *domain,
2651                                 TALLOC_CTX *mem_ctx,
2652                                 const struct dom_sid *group_sid,
2653                                 enum lsa_SidType type,
2654                                 uint32 *num_names,
2655                                 struct dom_sid **sid_mem, char ***names,
2656                                 uint32 **name_types)
2657 {
2658         struct cache_entry *centry = NULL;
2659         NTSTATUS status;
2660         unsigned int i;
2661         fstring sid_string;
2662         bool old_status;
2663
2664         old_status = domain->online;
2665         status = wcache_lookup_groupmem(domain, mem_ctx, group_sid, num_names,
2666                                         sid_mem, names, name_types);
2667         if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2668                 return status;
2669         }
2670
2671         (*num_names) = 0;
2672         (*sid_mem) = NULL;
2673         (*names) = NULL;
2674         (*name_types) = NULL;
2675
2676         /* Return status value returned by seq number check */
2677
2678         if (!NT_STATUS_IS_OK(domain->last_status))
2679                 return domain->last_status;
2680
2681         DEBUG(10,("lookup_groupmem: [Cached] - doing backend query for info for domain %s\n",
2682                 domain->name ));
2683
2684         status = domain->backend->lookup_groupmem(domain, mem_ctx, group_sid,
2685                                                   type, num_names,
2686                                                   sid_mem, names, name_types);
2687
2688         if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2689                 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2690                 if (!domain->internal && old_status) {
2691                         set_domain_offline(domain);
2692                 }
2693                 if (!domain->internal &&
2694                         !domain->online &&
2695                         old_status) {
2696                         NTSTATUS cache_status;
2697                         cache_status = wcache_lookup_groupmem(domain, mem_ctx, group_sid,
2698                                                               num_names, sid_mem, names,
2699                                                               name_types);
2700                         return cache_status;
2701                 }
2702         }
2703         /* and save it */
2704         refresh_sequence_number(domain, false);
2705         if (!NT_STATUS_IS_OK(status)) {
2706                 return status;
2707         }
2708         centry = centry_start(domain, status);
2709         if (!centry)
2710                 goto skip_save;
2711         centry_put_uint32(centry, *num_names);
2712         for (i=0; i<(*num_names); i++) {
2713                 centry_put_sid(centry, &(*sid_mem)[i]);
2714                 centry_put_string(centry, (*names)[i]);
2715                 centry_put_uint32(centry, (*name_types)[i]);
2716         }       
2717         centry_end(centry, "GM/%s", sid_to_fstring(sid_string, group_sid));
2718         centry_free(centry);
2719
2720 skip_save:
2721         return status;
2722 }
2723
2724 /* find the sequence number for a domain */
2725 static NTSTATUS sequence_number(struct winbindd_domain *domain, uint32 *seq)
2726 {
2727         refresh_sequence_number(domain, false);
2728
2729         *seq = domain->sequence_number;
2730
2731         return NT_STATUS_OK;
2732 }
2733
2734 /* enumerate trusted domains 
2735  * (we need to have the list of trustdoms in the cache when we go offline) -
2736  * Guenther */
2737 static NTSTATUS trusted_domains(struct winbindd_domain *domain,
2738                                 TALLOC_CTX *mem_ctx,
2739                                 struct netr_DomainTrustList *trusts)
2740 {
2741         NTSTATUS status;
2742         struct winbind_cache *cache;
2743         struct winbindd_tdc_domain *dom_list = NULL;
2744         size_t num_domains = 0;
2745         bool retval = false;
2746         int i;
2747         bool old_status;
2748
2749         old_status = domain->online;
2750         trusts->count = 0;
2751         trusts->array = NULL;
2752
2753         cache = get_cache(domain);
2754         if (!cache || !cache->tdb) {
2755                 goto do_query;
2756         }
2757
2758         if (domain->online) {
2759                 goto do_query;
2760         }
2761
2762         retval = wcache_tdc_fetch_list(&dom_list, &num_domains);
2763         if (!retval || !num_domains || !dom_list) {
2764                 TALLOC_FREE(dom_list);
2765                 goto do_query;
2766         }
2767
2768 do_fetch_cache:
2769         trusts->array = TALLOC_ZERO_ARRAY(mem_ctx, struct netr_DomainTrust, num_domains);
2770         if (!trusts->array) {
2771                 TALLOC_FREE(dom_list);
2772                 return NT_STATUS_NO_MEMORY;
2773         }
2774
2775         for (i = 0; i < num_domains; i++) {
2776                 struct netr_DomainTrust *trust;
2777                 struct dom_sid *sid;
2778                 struct winbindd_domain *dom;
2779
2780                 dom = find_domain_from_name_noinit(dom_list[i].domain_name);
2781                 if (dom && dom->internal) {
2782                         continue;
2783                 }
2784
2785                 trust = &trusts->array[trusts->count];
2786                 trust->netbios_name = talloc_strdup(trusts->array, dom_list[i].domain_name);
2787                 trust->dns_name = talloc_strdup(trusts->array, dom_list[i].dns_name);
2788                 sid = talloc(trusts->array, struct dom_sid);
2789                 if (!trust->netbios_name || !trust->dns_name ||
2790                         !sid) {
2791                         TALLOC_FREE(dom_list);
2792                         TALLOC_FREE(trusts->array);
2793                         return NT_STATUS_NO_MEMORY;
2794                 }
2795
2796                 trust->trust_flags = dom_list[i].trust_flags;
2797                 trust->trust_attributes = dom_list[i].trust_attribs;
2798                 trust->trust_type = dom_list[i].trust_type;
2799                 sid_copy(sid, &dom_list[i].sid);
2800                 trust->sid = sid;
2801                 trusts->count++;
2802         }
2803
2804         TALLOC_FREE(dom_list);
2805         return NT_STATUS_OK;
2806
2807 do_query:
2808         /* Return status value returned by seq number check */
2809
2810         if (!NT_STATUS_IS_OK(domain->last_status))
2811                 return domain->last_status;
2812
2813         DEBUG(10,("trusted_domains: [Cached] - doing backend query for info for domain %s\n",
2814                 domain->name ));
2815
2816         status = domain->backend->trusted_domains(domain, mem_ctx, trusts);
2817
2818         if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2819                 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2820                 if (!domain->internal && old_status) {
2821                         set_domain_offline(domain);
2822                 }
2823                 if (!domain->internal &&
2824                         !domain->online &&
2825                         old_status) {
2826                         retval = wcache_tdc_fetch_list(&dom_list, &num_domains);
2827                         if (retval && num_domains && dom_list) {
2828                                 TALLOC_FREE(trusts->array);
2829                                 trusts->count = 0;
2830                                 goto do_fetch_cache;
2831                         }
2832                 }
2833         }
2834         /* no trusts gives NT_STATUS_NO_MORE_ENTRIES resetting to NT_STATUS_OK
2835          * so that the generic centry handling still applies correctly -
2836          * Guenther*/
2837
2838         if (!NT_STATUS_IS_ERR(status)) {
2839                 status = NT_STATUS_OK;
2840         }
2841         return status;
2842 }       
2843
2844 /* get lockout policy */
2845 static NTSTATUS lockout_policy(struct winbindd_domain *domain,
2846                                TALLOC_CTX *mem_ctx,
2847                                struct samr_DomInfo12 *policy)
2848 {
2849         struct winbind_cache *cache = get_cache(domain);
2850         struct cache_entry *centry = NULL;
2851         NTSTATUS status;
2852         bool old_status;
2853
2854         old_status = domain->online;
2855         if (!cache->tdb)
2856                 goto do_query;
2857
2858         centry = wcache_fetch(cache, domain, "LOC_POL/%s", domain->name);
2859
2860         if (!centry)
2861                 goto do_query;
2862
2863 do_fetch_cache:
2864         policy->lockout_duration = centry_nttime(centry);
2865         policy->lockout_window = centry_nttime(centry);
2866         policy->lockout_threshold = centry_uint16(centry);
2867
2868         status = centry->status;
2869
2870         DEBUG(10,("lockout_policy: [Cached] - cached info for domain %s status: %s\n",
2871                 domain->name, nt_errstr(status) ));
2872
2873         centry_free(centry);
2874         return status;
2875
2876 do_query:
2877         ZERO_STRUCTP(policy);
2878
2879         /* Return status value returned by seq number check */
2880
2881         if (!NT_STATUS_IS_OK(domain->last_status))
2882                 return domain->last_status;
2883
2884         DEBUG(10,("lockout_policy: [Cached] - doing backend query for info for domain %s\n",
2885                 domain->name ));
2886
2887         status = domain->backend->lockout_policy(domain, mem_ctx, policy);
2888
2889         if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2890                 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2891                 if (!domain->internal && old_status) {
2892                         set_domain_offline(domain);
2893                 }
2894                 if (cache->tdb &&
2895                         !domain->internal &&
2896                         !domain->online &&
2897                         old_status) {
2898                         centry = wcache_fetch(cache, domain, "LOC_POL/%s", domain->name);
2899                         if (centry) {
2900                                 goto do_fetch_cache;
2901                         }
2902                 }
2903         }
2904         /* and save it */
2905         refresh_sequence_number(domain, false);
2906         if (!NT_STATUS_IS_OK(status)) {
2907                 return status;
2908         }
2909         wcache_save_lockout_policy(domain, status, policy);
2910
2911         return status;
2912 }
2913
2914 /* get password policy */
2915 static NTSTATUS password_policy(struct winbindd_domain *domain,
2916                                 TALLOC_CTX *mem_ctx,
2917                                 struct samr_DomInfo1 *policy)
2918 {
2919         struct winbind_cache *cache = get_cache(domain);
2920         struct cache_entry *centry = NULL;
2921         NTSTATUS status;
2922         bool old_status;
2923
2924         old_status = domain->online;
2925         if (!cache->tdb)
2926                 goto do_query;
2927
2928         centry = wcache_fetch(cache, domain, "PWD_POL/%s", domain->name);
2929
2930         if (!centry)
2931                 goto do_query;
2932
2933 do_fetch_cache:
2934         policy->min_password_length = centry_uint16(centry);
2935         policy->password_history_length = centry_uint16(centry);
2936         policy->password_properties = centry_uint32(centry);
2937         policy->max_password_age = centry_nttime(centry);
2938         policy->min_password_age = centry_nttime(centry);
2939
2940         status = centry->status;
2941
2942         DEBUG(10,("lockout_policy: [Cached] - cached info for domain %s status: %s\n",
2943                 domain->name, nt_errstr(status) ));
2944
2945         centry_free(centry);
2946         return status;
2947
2948 do_query:
2949         ZERO_STRUCTP(policy);
2950
2951         /* Return status value returned by seq number check */
2952
2953         if (!NT_STATUS_IS_OK(domain->last_status))
2954                 return domain->last_status;
2955
2956         DEBUG(10,("password_policy: [Cached] - doing backend query for info for domain %s\n",
2957                 domain->name ));
2958
2959         status = domain->backend->password_policy(domain, mem_ctx, policy);
2960
2961         if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2962                 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2963                 if (!domain->internal && old_status) {
2964                         set_domain_offline(domain);
2965                 }
2966                 if (cache->tdb &&
2967                         !domain->internal &&
2968                         !domain->online &&
2969                         old_status) {
2970                         centry = wcache_fetch(cache, domain, "PWD_POL/%s", domain->name);
2971                         if (centry) {
2972                                 goto do_fetch_cache;
2973                         }
2974                 }
2975         }
2976         /* and save it */
2977         refresh_sequence_number(domain, false);
2978         if (!NT_STATUS_IS_OK(status)) {
2979                 return status;
2980         }
2981         wcache_save_password_policy(domain, status, policy);
2982
2983         return status;
2984 }
2985
2986
2987 /* Invalidate cached user and group lists coherently */
2988
2989 static int traverse_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf, 
2990                        void *state)
2991 {
2992         if (strncmp((const char *)kbuf.dptr, "UL/", 3) == 0 ||
2993             strncmp((const char *)kbuf.dptr, "GL/", 3) == 0)
2994                 tdb_delete(the_tdb, kbuf);
2995
2996         return 0;
2997 }
2998
2999 /* Invalidate the getpwnam and getgroups entries for a winbindd domain */
3000
3001 void wcache_invalidate_samlogon(struct winbindd_domain *domain, 
3002                                 struct netr_SamInfo3 *info3)
3003 {
3004         struct dom_sid sid;
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         sid_compose(&sid, info3->base.domain_sid, info3->base.rid);
3025
3026         /* Clear U/SID cache entry */
3027         fstr_sprintf(key_str, "U/%s", sid_to_fstring(sid_string, &sid));
3028         DEBUG(10, ("wcache_invalidate_samlogon: clearing %s\n", key_str));
3029         tdb_delete(cache->tdb, string_tdb_data(key_str));
3030
3031         /* Clear UG/SID cache entry */
3032         fstr_sprintf(key_str, "UG/%s", sid_to_fstring(sid_string, &sid));
3033         DEBUG(10, ("wcache_invalidate_samlogon: clearing %s\n", key_str));
3034         tdb_delete(cache->tdb, string_tdb_data(key_str));
3035
3036         /* Samba/winbindd never needs this. */
3037         netsamlogon_clear_cached_user(info3);
3038 }
3039
3040 bool wcache_invalidate_cache(void)
3041 {
3042         struct winbindd_domain *domain;
3043
3044         for (domain = domain_list(); domain; domain = domain->next) {
3045                 struct winbind_cache *cache = get_cache(domain);
3046
3047                 DEBUG(10, ("wcache_invalidate_cache: invalidating cache "
3048                            "entries for %s\n", domain->name));
3049                 if (cache) {
3050                         if (cache->tdb) {
3051                                 tdb_traverse(cache->tdb, traverse_fn, NULL);
3052                         } else {
3053                                 return false;
3054                         }
3055                 }
3056         }
3057         return true;
3058 }
3059
3060 bool wcache_invalidate_cache_noinit(void)
3061 {
3062         struct winbindd_domain *domain;
3063
3064         for (domain = domain_list(); domain; domain = domain->next) {
3065                 struct winbind_cache *cache;
3066
3067                 /* Skip uninitialized domains. */
3068                 if (!domain->initialized && !domain->internal) {
3069                         continue;
3070                 }
3071
3072                 cache = get_cache(domain);
3073
3074                 DEBUG(10, ("wcache_invalidate_cache: invalidating cache "
3075                            "entries for %s\n", domain->name));
3076                 if (cache) {
3077                         if (cache->tdb) {
3078                                 tdb_traverse(cache->tdb, traverse_fn, NULL);
3079                                 /*
3080                                  * Flushing cache has nothing to with domains.
3081                                  * return here if we successfully flushed once.
3082                                  * To avoid unnecessary traversing the cache.
3083                                  */
3084                                 return true;
3085                         } else {
3086                                 return false;
3087                         }
3088                 }
3089         }
3090         return true;
3091 }
3092
3093 bool init_wcache(void)
3094 {
3095         if (wcache == NULL) {
3096                 wcache = SMB_XMALLOC_P(struct winbind_cache);
3097                 ZERO_STRUCTP(wcache);
3098         }
3099
3100         if (wcache->tdb != NULL)
3101                 return true;
3102
3103         /* when working offline we must not clear the cache on restart */
3104         wcache->tdb = tdb_open_log(cache_path("winbindd_cache.tdb"),
3105                                 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE, 
3106                                 TDB_INCOMPATIBLE_HASH |
3107                                         (lp_winbind_offline_logon() ? TDB_DEFAULT : (TDB_DEFAULT | TDB_CLEAR_IF_FIRST)),
3108                                 O_RDWR|O_CREAT, 0600);
3109
3110         if (wcache->tdb == NULL) {
3111                 DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
3112                 return false;
3113         }
3114
3115         return true;
3116 }
3117
3118 /************************************************************************
3119  This is called by the parent to initialize the cache file.
3120  We don't need sophisticated locking here as we know we're the
3121  only opener.
3122 ************************************************************************/
3123
3124 bool initialize_winbindd_cache(void)
3125 {
3126         bool cache_bad = true;
3127         uint32 vers;
3128
3129         if (!init_wcache()) {
3130                 DEBUG(0,("initialize_winbindd_cache: init_wcache failed.\n"));
3131                 return false;
3132         }
3133
3134         /* Check version number. */
3135         if (tdb_fetch_uint32(wcache->tdb, WINBINDD_CACHE_VERSION_KEYSTR, &vers) &&
3136                         vers == WINBINDD_CACHE_VERSION) {
3137                 cache_bad = false;
3138         }
3139
3140         if (cache_bad) {
3141                 DEBUG(0,("initialize_winbindd_cache: clearing cache "
3142                         "and re-creating with version number %d\n",
3143                         WINBINDD_CACHE_VERSION ));
3144
3145                 tdb_close(wcache->tdb);
3146                 wcache->tdb = NULL;
3147
3148                 if (unlink(cache_path("winbindd_cache.tdb")) == -1) {
3149                         DEBUG(0,("initialize_winbindd_cache: unlink %s failed %s ",
3150                                 cache_path("winbindd_cache.tdb"),
3151                                 strerror(errno) ));
3152                         return false;
3153                 }
3154                 if (!init_wcache()) {
3155                         DEBUG(0,("initialize_winbindd_cache: re-initialization "
3156                                         "init_wcache failed.\n"));
3157                         return false;
3158                 }
3159
3160                 /* Write the version. */
3161                 if (!tdb_store_uint32(wcache->tdb, WINBINDD_CACHE_VERSION_KEYSTR, WINBINDD_CACHE_VERSION)) {
3162                         DEBUG(0,("initialize_winbindd_cache: version number store failed %s\n",
3163                                 tdb_errorstr(wcache->tdb) ));
3164                         return false;
3165                 }
3166         }
3167
3168         tdb_close(wcache->tdb);
3169         wcache->tdb = NULL;
3170         return true;
3171 }
3172
3173 void close_winbindd_cache(void)
3174 {
3175         if (!wcache) {
3176                 return;
3177         }
3178         if (wcache->tdb) {
3179                 tdb_close(wcache->tdb);
3180                 wcache->tdb = NULL;
3181         }
3182 }
3183
3184 bool lookup_cached_sid(TALLOC_CTX *mem_ctx, const struct dom_sid *sid,
3185                        char **domain_name, char **name,
3186                        enum lsa_SidType *type)
3187 {
3188         struct winbindd_domain *domain;
3189         NTSTATUS status;
3190
3191         domain = find_lookup_domain_from_sid(sid);
3192         if (domain == NULL) {
3193                 return false;
3194         }
3195         status = wcache_sid_to_name(domain, sid, mem_ctx, domain_name, name,
3196                                     type);
3197         return NT_STATUS_IS_OK(status);
3198 }
3199
3200 bool lookup_cached_name(const char *domain_name,
3201                         const char *name,
3202                         struct dom_sid *sid,
3203                         enum lsa_SidType *type)
3204 {
3205         struct winbindd_domain *domain;
3206         NTSTATUS status;
3207         bool original_online_state;
3208
3209         domain = find_lookup_domain_from_name(domain_name);
3210         if (domain == NULL) {
3211                 return false;
3212         }
3213
3214         /* If we are doing a cached logon, temporarily set the domain
3215            offline so the cache won't expire the entry */
3216
3217         original_online_state = domain->online;
3218         domain->online = false;
3219         status = wcache_name_to_sid(domain, domain_name, name, sid, type);
3220         domain->online = original_online_state;
3221
3222         return NT_STATUS_IS_OK(status);
3223 }
3224
3225 void cache_name2sid(struct winbindd_domain *domain, 
3226                     const char *domain_name, const char *name,
3227                     enum lsa_SidType type, const struct dom_sid *sid)
3228 {
3229         refresh_sequence_number(domain, false);
3230         wcache_save_name_to_sid(domain, NT_STATUS_OK, domain_name, name,
3231                                 sid, type);
3232 }
3233
3234 /*
3235  * The original idea that this cache only contains centries has
3236  * been blurred - now other stuff gets put in here. Ensure we
3237  * ignore these things on cleanup.
3238  */
3239
3240 static int traverse_fn_cleanup(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, 
3241                                TDB_DATA dbuf, void *state)
3242 {
3243         struct cache_entry *centry;
3244
3245         if (is_non_centry_key(kbuf)) {
3246                 return 0;
3247         }
3248
3249         centry = wcache_fetch_raw((char *)kbuf.dptr);
3250         if (!centry) {
3251                 return 0;
3252         }
3253
3254         if (!NT_STATUS_IS_OK(centry->status)) {
3255                 DEBUG(10,("deleting centry %s\n", (const char *)kbuf.dptr));
3256                 tdb_delete(the_tdb, kbuf);
3257         }
3258
3259         centry_free(centry);
3260         return 0;
3261 }
3262
3263 /* flush the cache */
3264 void wcache_flush_cache(void)
3265 {
3266         if (!wcache)
3267                 return;
3268         if (wcache->tdb) {
3269                 tdb_close(wcache->tdb);
3270                 wcache->tdb = NULL;
3271         }
3272         if (!winbindd_use_cache()) {
3273                 return;
3274         }
3275
3276         /* when working offline we must not clear the cache on restart */
3277         wcache->tdb = tdb_open_log(cache_path("winbindd_cache.tdb"),
3278                                 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE, 
3279                                 TDB_INCOMPATIBLE_HASH |
3280                                 (lp_winbind_offline_logon() ? TDB_DEFAULT : (TDB_DEFAULT | TDB_CLEAR_IF_FIRST)),
3281                                 O_RDWR|O_CREAT, 0600);
3282
3283         if (!wcache->tdb) {
3284                 DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
3285                 return;
3286         }
3287
3288         tdb_traverse(wcache->tdb, traverse_fn_cleanup, NULL);
3289
3290         DEBUG(10,("wcache_flush_cache success\n"));
3291 }
3292
3293 /* Count cached creds */
3294
3295 static int traverse_fn_cached_creds(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf, 
3296                                     void *state)
3297 {
3298         int *cred_count = (int*)state;
3299  
3300         if (strncmp((const char *)kbuf.dptr, "CRED/", 5) == 0) {
3301                 (*cred_count)++;
3302         }
3303         return 0;
3304 }
3305
3306 NTSTATUS wcache_count_cached_creds(struct winbindd_domain *domain, int *count)
3307 {
3308         struct winbind_cache *cache = get_cache(domain);
3309
3310         *count = 0;
3311
3312         if (!cache->tdb) {
3313                 return NT_STATUS_INTERNAL_DB_ERROR;
3314         }
3315  
3316         tdb_traverse(cache->tdb, traverse_fn_cached_creds, (void *)count);
3317
3318         return NT_STATUS_OK;
3319 }
3320
3321 struct cred_list {
3322         struct cred_list *prev, *next;
3323         TDB_DATA key;
3324         fstring name;
3325         time_t created;
3326 };
3327 static struct cred_list *wcache_cred_list;
3328
3329 static int traverse_fn_get_credlist(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf, 
3330                                     void *state)
3331 {
3332         struct cred_list *cred;
3333
3334         if (strncmp((const char *)kbuf.dptr, "CRED/", 5) == 0) {
3335
3336                 cred = SMB_MALLOC_P(struct cred_list);
3337                 if (cred == NULL) {
3338                         DEBUG(0,("traverse_fn_remove_first_creds: failed to malloc new entry for list\n"));
3339                         return -1;
3340                 }
3341
3342                 ZERO_STRUCTP(cred);
3343
3344                 /* save a copy of the key */
3345
3346                 fstrcpy(cred->name, (const char *)kbuf.dptr);           
3347                 DLIST_ADD(wcache_cred_list, cred);
3348         }
3349
3350         return 0;
3351 }
3352
3353 NTSTATUS wcache_remove_oldest_cached_creds(struct winbindd_domain *domain, const struct dom_sid *sid)
3354 {
3355         struct winbind_cache *cache = get_cache(domain);
3356         NTSTATUS status;
3357         int ret;
3358         struct cred_list *cred, *oldest = NULL;
3359
3360         if (!cache->tdb) {
3361                 return NT_STATUS_INTERNAL_DB_ERROR;
3362         }
3363
3364         /* we possibly already have an entry */
3365         if (sid && NT_STATUS_IS_OK(wcache_cached_creds_exist(domain, sid))) {
3366
3367                 fstring key_str, tmp;
3368
3369                 DEBUG(11,("we already have an entry, deleting that\n"));
3370
3371                 fstr_sprintf(key_str, "CRED/%s", sid_to_fstring(tmp, sid));
3372
3373                 tdb_delete(cache->tdb, string_tdb_data(key_str));
3374
3375                 return NT_STATUS_OK;
3376         }
3377
3378         ret = tdb_traverse(cache->tdb, traverse_fn_get_credlist, NULL);
3379         if (ret == 0) {
3380                 return NT_STATUS_OK;
3381         } else if ((ret == -1) || (wcache_cred_list == NULL)) {
3382                 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
3383         }
3384
3385         ZERO_STRUCTP(oldest);
3386
3387         for (cred = wcache_cred_list; cred; cred = cred->next) {
3388
3389                 TDB_DATA data;
3390                 time_t t;
3391
3392                 data = tdb_fetch(cache->tdb, string_tdb_data(cred->name));
3393                 if (!data.dptr) {
3394                         DEBUG(10,("wcache_remove_oldest_cached_creds: entry for [%s] not found\n", 
3395                                 cred->name));
3396                         status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
3397                         goto done;
3398                 }
3399
3400                 t = IVAL(data.dptr, 0);
3401                 SAFE_FREE(data.dptr);
3402
3403                 if (!oldest) {
3404                         oldest = SMB_MALLOC_P(struct cred_list);
3405                         if (oldest == NULL) {
3406                                 status = NT_STATUS_NO_MEMORY;
3407                                 goto done;
3408                         }
3409
3410                         fstrcpy(oldest->name, cred->name);
3411                         oldest->created = t;
3412                         continue;
3413                 }
3414
3415                 if (t < oldest->created) {
3416                         fstrcpy(oldest->name, cred->name);
3417                         oldest->created = t;
3418                 }
3419         }
3420
3421         if (tdb_delete(cache->tdb, string_tdb_data(oldest->name)) == 0) {
3422                 status = NT_STATUS_OK;
3423         } else {
3424                 status = NT_STATUS_UNSUCCESSFUL;
3425         }
3426 done:
3427         SAFE_FREE(wcache_cred_list);
3428         SAFE_FREE(oldest);
3429
3430         return status;
3431 }
3432
3433 /* Change the global online/offline state. */
3434 bool set_global_winbindd_state_offline(void)
3435 {
3436         TDB_DATA data;
3437
3438         DEBUG(10,("set_global_winbindd_state_offline: offline requested.\n"));
3439
3440         /* Only go offline if someone has created
3441            the key "WINBINDD_OFFLINE" in the cache tdb. */
3442
3443         if (wcache == NULL || wcache->tdb == NULL) {
3444                 DEBUG(10,("set_global_winbindd_state_offline: wcache not open yet.\n"));
3445                 return false;
3446         }
3447
3448         if (!lp_winbind_offline_logon()) {
3449                 DEBUG(10,("set_global_winbindd_state_offline: rejecting.\n"));
3450                 return false;
3451         }
3452
3453         if (global_winbindd_offline_state) {
3454                 /* Already offline. */
3455                 return true;
3456         }
3457
3458         data = tdb_fetch_bystring( wcache->tdb, "WINBINDD_OFFLINE" );
3459
3460         if (!data.dptr || data.dsize != 4) {
3461                 DEBUG(10,("set_global_winbindd_state_offline: offline state not set.\n"));
3462                 SAFE_FREE(data.dptr);
3463                 return false;
3464         } else {
3465                 DEBUG(10,("set_global_winbindd_state_offline: offline state set.\n"));
3466                 global_winbindd_offline_state = true;
3467                 SAFE_FREE(data.dptr);
3468                 return true;
3469         }
3470 }
3471
3472 void set_global_winbindd_state_online(void)
3473 {
3474         DEBUG(10,("set_global_winbindd_state_online: online requested.\n"));
3475
3476         if (!lp_winbind_offline_logon()) {
3477                 DEBUG(10,("set_global_winbindd_state_online: rejecting.\n"));
3478                 return;
3479         }
3480
3481         if (!global_winbindd_offline_state) {
3482                 /* Already online. */
3483                 return;
3484         }
3485         global_winbindd_offline_state = false;
3486
3487         if (!wcache->tdb) {
3488                 return;
3489         }
3490
3491         /* Ensure there is no key "WINBINDD_OFFLINE" in the cache tdb. */
3492         tdb_delete_bystring(wcache->tdb, "WINBINDD_OFFLINE");
3493 }
3494
3495 bool get_global_winbindd_state_offline(void)
3496 {
3497         return global_winbindd_offline_state;
3498 }
3499
3500 /***********************************************************************
3501  Validate functions for all possible cache tdb keys.
3502 ***********************************************************************/
3503
3504 static struct cache_entry *create_centry_validate(const char *kstr, TDB_DATA data, 
3505                                                   struct tdb_validation_status *state)
3506 {
3507         struct cache_entry *centry;
3508
3509         centry = SMB_XMALLOC_P(struct cache_entry);
3510         centry->data = (unsigned char *)memdup(data.dptr, data.dsize);
3511         if (!centry->data) {
3512                 SAFE_FREE(centry);
3513                 return NULL;
3514         }
3515         centry->len = data.dsize;
3516         centry->ofs = 0;
3517
3518         if (centry->len < 16) {
3519                 /* huh? corrupt cache? */
3520                 DEBUG(0,("create_centry_validate: Corrupt cache for key %s "
3521                          "(len < 16) ?\n", kstr));
3522                 centry_free(centry);
3523                 state->bad_entry = true;
3524                 state->success = false;
3525                 return NULL;
3526         }
3527
3528         centry->status = NT_STATUS(centry_uint32(centry));
3529         centry->sequence_number = centry_uint32(centry);
3530         centry->timeout = centry_uint64(centry);
3531         return centry;
3532 }
3533
3534 static int validate_seqnum(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3535                            struct tdb_validation_status *state)
3536 {
3537         if (dbuf.dsize != 8) {
3538                 DEBUG(0,("validate_seqnum: Corrupt cache for key %s (len %u != 8) ?\n",
3539                                 keystr, (unsigned int)dbuf.dsize ));
3540                 state->bad_entry = true;
3541                 return 1;
3542         }
3543         return 0;
3544 }
3545
3546 static int validate_ns(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3547                        struct tdb_validation_status *state)
3548 {
3549         struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3550         if (!centry) {
3551                 return 1;
3552         }
3553
3554         (void)centry_uint32(centry);
3555         if (NT_STATUS_IS_OK(centry->status)) {
3556                 struct dom_sid sid;
3557                 (void)centry_sid(centry, &sid);
3558         }
3559
3560         centry_free(centry);
3561
3562         if (!(state->success)) {
3563                 return 1;
3564         }
3565         DEBUG(10,("validate_ns: %s ok\n", keystr));
3566         return 0;
3567 }
3568
3569 static int validate_sn(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3570                        struct tdb_validation_status *state)
3571 {
3572         struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3573         if (!centry) {
3574                 return 1;
3575         }
3576
3577         if (NT_STATUS_IS_OK(centry->status)) {
3578                 (void)centry_uint32(centry);
3579                 (void)centry_string(centry, mem_ctx);
3580                 (void)centry_string(centry, mem_ctx);
3581         }
3582
3583         centry_free(centry);
3584
3585         if (!(state->success)) {
3586                 return 1;
3587         }
3588         DEBUG(10,("validate_sn: %s ok\n", keystr));
3589         return 0;
3590 }
3591
3592 static int validate_u(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3593                       struct tdb_validation_status *state)
3594 {
3595         struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3596         struct dom_sid sid;
3597
3598         if (!centry) {
3599                 return 1;
3600         }
3601
3602         (void)centry_string(centry, mem_ctx);
3603         (void)centry_string(centry, mem_ctx);
3604         (void)centry_string(centry, mem_ctx);
3605         (void)centry_string(centry, mem_ctx);
3606         (void)centry_uint32(centry);
3607         (void)centry_sid(centry, &sid);
3608         (void)centry_sid(centry, &sid);
3609
3610         centry_free(centry);
3611
3612         if (!(state->success)) {
3613                 return 1;
3614         }
3615         DEBUG(10,("validate_u: %s ok\n", keystr));
3616         return 0;
3617 }
3618
3619 static int validate_loc_pol(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3620                             struct tdb_validation_status *state)
3621 {
3622         struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3623
3624         if (!centry) {
3625                 return 1;
3626         }
3627
3628         (void)centry_nttime(centry);
3629         (void)centry_nttime(centry);
3630         (void)centry_uint16(centry);
3631
3632         centry_free(centry);
3633
3634         if (!(state->success)) {
3635                 return 1;
3636         }
3637         DEBUG(10,("validate_loc_pol: %s ok\n", keystr));
3638         return 0;
3639 }
3640
3641 static int validate_pwd_pol(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3642                             struct tdb_validation_status *state)
3643 {
3644         struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3645
3646         if (!centry) {
3647                 return 1;
3648         }
3649
3650         (void)centry_uint16(centry);
3651         (void)centry_uint16(centry);
3652         (void)centry_uint32(centry);
3653         (void)centry_nttime(centry);
3654         (void)centry_nttime(centry);
3655
3656         centry_free(centry);
3657
3658         if (!(state->success)) {
3659                 return 1;
3660         }
3661         DEBUG(10,("validate_pwd_pol: %s ok\n", keystr));
3662         return 0;
3663 }
3664
3665 static int validate_cred(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3666                          struct tdb_validation_status *state)
3667 {
3668         struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3669
3670         if (!centry) {
3671                 return 1;
3672         }
3673
3674         (void)centry_time(centry);
3675         (void)centry_hash16(centry, mem_ctx);
3676
3677         /* We only have 17 bytes more data in the salted cred case. */
3678         if (centry->len - centry->ofs == 17) {
3679                 (void)centry_hash16(centry, mem_ctx);
3680         }
3681
3682         centry_free(centry);
3683
3684         if (!(state->success)) {
3685                 return 1;
3686         }
3687         DEBUG(10,("validate_cred: %s ok\n", keystr));
3688         return 0;
3689 }
3690
3691 static int validate_ul(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3692                        struct tdb_validation_status *state)
3693 {
3694         struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3695         int32 num_entries, i;
3696
3697         if (!centry) {
3698                 return 1;
3699         }
3700
3701         num_entries = (int32)centry_uint32(centry);
3702
3703         for (i=0; i< num_entries; i++) {
3704                 struct dom_sid sid;
3705                 (void)centry_string(centry, mem_ctx);
3706                 (void)centry_string(centry, mem_ctx);
3707                 (void)centry_string(centry, mem_ctx);
3708                 (void)centry_string(centry, mem_ctx);
3709                 (void)centry_sid(centry, &sid);
3710                 (void)centry_sid(centry, &sid);
3711         }
3712
3713         centry_free(centry);
3714
3715         if (!(state->success)) {
3716                 return 1;
3717         }
3718         DEBUG(10,("validate_ul: %s ok\n", keystr));
3719         return 0;
3720 }
3721
3722 static int validate_gl(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3723                        struct tdb_validation_status *state)
3724 {
3725         struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3726         int32 num_entries, i;
3727
3728         if (!centry) {
3729                 return 1;
3730         }
3731
3732         num_entries = centry_uint32(centry);
3733
3734         for (i=0; i< num_entries; i++) {
3735                 (void)centry_string(centry, mem_ctx);
3736                 (void)centry_string(centry, mem_ctx);
3737                 (void)centry_uint32(centry);
3738         }
3739
3740         centry_free(centry);
3741
3742         if (!(state->success)) {
3743                 return 1;
3744         }
3745         DEBUG(10,("validate_gl: %s ok\n", keystr));
3746         return 0;
3747 }
3748
3749 static int validate_ug(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3750                        struct tdb_validation_status *state)
3751 {
3752         struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3753         int32 num_groups, i;
3754
3755         if (!centry) {
3756                 return 1;
3757         }
3758
3759         num_groups = centry_uint32(centry);
3760
3761         for (i=0; i< num_groups; i++) {
3762                 struct dom_sid sid;
3763                 centry_sid(centry, &sid);
3764         }
3765
3766         centry_free(centry);
3767
3768         if (!(state->success)) {
3769                 return 1;
3770         }
3771         DEBUG(10,("validate_ug: %s ok\n", keystr));
3772         return 0;
3773 }
3774
3775 static int validate_ua(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3776                        struct tdb_validation_status *state)
3777 {
3778         struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3779         int32 num_aliases, i;
3780
3781         if (!centry) {
3782                 return 1;
3783         }
3784
3785         num_aliases = centry_uint32(centry);
3786
3787         for (i=0; i < num_aliases; i++) {
3788                 (void)centry_uint32(centry);
3789         }
3790
3791         centry_free(centry);
3792
3793         if (!(state->success)) {
3794                 return 1;
3795         }
3796         DEBUG(10,("validate_ua: %s ok\n", keystr));
3797         return 0;
3798 }
3799
3800 static int validate_gm(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3801                        struct tdb_validation_status *state)
3802 {
3803         struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3804         int32 num_names, i;
3805
3806         if (!centry) {
3807                 return 1;
3808         }
3809
3810         num_names = centry_uint32(centry);
3811
3812         for (i=0; i< num_names; i++) {
3813                 struct dom_sid sid;
3814                 centry_sid(centry, &sid);
3815                 (void)centry_string(centry, mem_ctx);
3816                 (void)centry_uint32(centry);
3817         }
3818
3819         centry_free(centry);
3820
3821         if (!(state->success)) {
3822                 return 1;
3823         }
3824         DEBUG(10,("validate_gm: %s ok\n", keystr));
3825         return 0;
3826 }
3827
3828 static int validate_dr(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3829                        struct tdb_validation_status *state)
3830 {
3831         /* Can't say anything about this other than must be nonzero. */
3832         if (dbuf.dsize == 0) {
3833                 DEBUG(0,("validate_dr: Corrupt cache for key %s (len == 0) ?\n",
3834                                 keystr));
3835                 state->bad_entry = true;
3836                 state->success = false;
3837                 return 1;
3838         }
3839
3840         DEBUG(10,("validate_dr: %s ok\n", keystr));
3841         return 0;
3842 }
3843
3844 static int validate_de(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3845                        struct tdb_validation_status *state)
3846 {
3847         /* Can't say anything about this other than must be nonzero. */
3848         if (dbuf.dsize == 0) {
3849                 DEBUG(0,("validate_de: Corrupt cache for key %s (len == 0) ?\n",
3850                                 keystr));
3851                 state->bad_entry = true;
3852                 state->success = false;
3853                 return 1;
3854         }
3855
3856         DEBUG(10,("validate_de: %s ok\n", keystr));
3857         return 0;
3858 }
3859
3860 static int validate_pwinfo(TALLOC_CTX *mem_ctx, const char *keystr,
3861                            TDB_DATA dbuf, struct tdb_validation_status *state)
3862 {
3863         struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3864
3865         if (!centry) {
3866                 return 1;
3867         }
3868
3869         (void)centry_string(centry, mem_ctx);
3870         (void)centry_string(centry, mem_ctx);
3871         (void)centry_string(centry, mem_ctx);
3872         (void)centry_uint32(centry);
3873
3874         centry_free(centry);
3875
3876         if (!(state->success)) {
3877                 return 1;
3878         }
3879         DEBUG(10,("validate_pwinfo: %s ok\n", keystr));
3880         return 0;
3881 }
3882
3883 static int validate_nss_an(TALLOC_CTX *mem_ctx, const char *keystr,
3884                            TDB_DATA dbuf,
3885                            struct tdb_validation_status *state)
3886 {
3887         struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3888
3889         if (!centry) {
3890                 return 1;
3891         }
3892
3893         (void)centry_string( centry, mem_ctx );
3894
3895         centry_free(centry);
3896
3897         if (!(state->success)) {
3898                 return 1;
3899         }
3900         DEBUG(10,("validate_pwinfo: %s ok\n", keystr));
3901         return 0;
3902 }
3903
3904 static int validate_nss_na(TALLOC_CTX *mem_ctx, const char *keystr,
3905                            TDB_DATA dbuf,
3906                            struct tdb_validation_status *state)
3907 {
3908         struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3909
3910         if (!centry) {
3911                 return 1;
3912         }
3913
3914         (void)centry_string( centry, mem_ctx );
3915
3916         centry_free(centry);
3917
3918         if (!(state->success)) {
3919                 return 1;
3920         }
3921         DEBUG(10,("validate_pwinfo: %s ok\n", keystr));
3922         return 0;
3923 }
3924
3925 static int validate_trustdomcache(TALLOC_CTX *mem_ctx, const char *keystr, 
3926                                   TDB_DATA dbuf,
3927                                   struct tdb_validation_status *state)
3928 {
3929         if (dbuf.dsize == 0) {
3930                 DEBUG(0, ("validate_trustdomcache: Corrupt cache for "
3931                           "key %s (len ==0) ?\n", keystr));
3932                 state->bad_entry = true;
3933                 state->success = false;
3934                 return 1;
3935         }
3936
3937         DEBUG(10,    ("validate_trustdomcache: %s ok\n", keystr));
3938         DEBUGADD(10, ("  Don't trust me, I am a DUMMY!\n"));
3939         return 0;
3940 }
3941
3942 static int validate_offline(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3943                             struct tdb_validation_status *state)
3944 {
3945         if (dbuf.dsize != 4) {
3946                 DEBUG(0,("validate_offline: Corrupt cache for key %s (len %u != 4) ?\n",
3947                                 keystr, (unsigned int)dbuf.dsize ));
3948                 state->bad_entry = true;
3949                 state->success = false;
3950                 return 1;
3951         }
3952         DEBUG(10,("validate_offline: %s ok\n", keystr));
3953         return 0;
3954 }
3955
3956 static int validate_ndr(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3957                         struct tdb_validation_status *state)
3958 {
3959         /*
3960          * Ignore validation for now. The proper way to do this is with a
3961          * checksum. Just pure parsing does not really catch much.
3962          */
3963         return 0;
3964 }
3965
3966 static int validate_cache_version(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3967                                   struct tdb_validation_status *state)
3968 {
3969         if (dbuf.dsize != 4) {
3970                 DEBUG(0, ("validate_cache_version: Corrupt cache for "
3971                           "key %s (len %u != 4) ?\n", 
3972                           keystr, (unsigned int)dbuf.dsize));
3973                 state->bad_entry = true;
3974                 state->success = false;
3975                 return 1;
3976         }
3977
3978         DEBUG(10, ("validate_cache_version: %s ok\n", keystr));
3979         return 0;
3980 }
3981
3982 /***********************************************************************
3983  A list of all possible cache tdb keys with associated validation
3984  functions.
3985 ***********************************************************************/
3986
3987 struct key_val_struct {
3988         const char *keyname;
3989         int (*validate_data_fn)(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf, struct tdb_validation_status* state);
3990 } key_val[] = {
3991         {"SEQNUM/", validate_seqnum},
3992         {"NS/", validate_ns},
3993         {"SN/", validate_sn},
3994         {"U/", validate_u},
3995         {"LOC_POL/", validate_loc_pol},
3996         {"PWD_POL/", validate_pwd_pol},
3997         {"CRED/", validate_cred},
3998         {"UL/", validate_ul},
3999         {"GL/", validate_gl},
4000         {"UG/", validate_ug},
4001         {"UA", validate_ua},
4002         {"GM/", validate_gm},
4003         {"DR/", validate_dr},
4004         {"DE/", validate_de},
4005         {"NSS/PWINFO/", validate_pwinfo},
4006         {"TRUSTDOMCACHE/", validate_trustdomcache},
4007         {"NSS/NA/", validate_nss_na},
4008         {"NSS/AN/", validate_nss_an},
4009         {"WINBINDD_OFFLINE", validate_offline},
4010         {"NDR/", validate_ndr},
4011         {WINBINDD_CACHE_VERSION_KEYSTR, validate_cache_version},
4012         {NULL, NULL}
4013 };
4014
4015 /***********************************************************************
4016  Function to look at every entry in the tdb and validate it as far as
4017  possible.
4018 ***********************************************************************/
4019
4020 static int cache_traverse_validate_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf, void *state)
4021 {
4022         int i;
4023         unsigned int max_key_len = 1024;
4024         struct tdb_validation_status *v_state = (struct tdb_validation_status *)state;
4025
4026         /* Paranoia check. */
4027         if (strncmp("UA/", (const char *)kbuf.dptr, 3) == 0) {
4028                 max_key_len = 1024 * 1024;
4029         }
4030         if (kbuf.dsize > max_key_len) {
4031                 DEBUG(0, ("cache_traverse_validate_fn: key length too large: "
4032                           "(%u) > (%u)\n\n",
4033                           (unsigned int)kbuf.dsize, (unsigned int)max_key_len));
4034                 return 1;
4035         }
4036
4037         for (i = 0; key_val[i].keyname; i++) {
4038                 size_t namelen = strlen(key_val[i].keyname);
4039                 if (kbuf.dsize >= namelen && (
4040                                 strncmp(key_val[i].keyname, (const char *)kbuf.dptr, namelen)) == 0) {
4041                         TALLOC_CTX *mem_ctx;
4042                         char *keystr;
4043                         int ret;
4044
4045                         keystr = SMB_MALLOC_ARRAY(char, kbuf.dsize+1);
4046                         if (!keystr) {
4047                                 return 1;
4048                         }
4049                         memcpy(keystr, kbuf.dptr, kbuf.dsize);
4050                         keystr[kbuf.dsize] = '\0';
4051
4052                         mem_ctx = talloc_init("validate_ctx");
4053                         if (!mem_ctx) {
4054                                 SAFE_FREE(keystr);
4055                                 return 1;
4056                         }
4057
4058                         ret = key_val[i].validate_data_fn(mem_ctx, keystr, dbuf, 
4059                                                           v_state);
4060
4061                         SAFE_FREE(keystr);
4062                         talloc_destroy(mem_ctx);
4063                         return ret;
4064                 }
4065         }
4066
4067         DEBUG(0,("cache_traverse_validate_fn: unknown cache entry\nkey :\n"));
4068         dump_data(0, (uint8 *)kbuf.dptr, kbuf.dsize);
4069         DEBUG(0,("data :\n"));
4070         dump_data(0, (uint8 *)dbuf.dptr, dbuf.dsize);
4071         v_state->unknown_key = true;
4072         v_state->success = false;
4073         return 1; /* terminate. */
4074 }
4075
4076 static void validate_panic(const char *const why)
4077 {
4078         DEBUG(0,("validating cache: would panic %s\n", why ));
4079         DEBUGADD(0, ("exiting instead (cache validation mode)\n"));
4080         exit(47);
4081 }
4082
4083 /***********************************************************************
4084  Try and validate every entry in the winbindd cache. If we fail here,
4085  delete the cache tdb and return non-zero.
4086 ***********************************************************************/
4087
4088 int winbindd_validate_cache(void)
4089 {
4090         int ret = -1;
4091         const char *tdb_path = cache_path("winbindd_cache.tdb");
4092         TDB_CONTEXT *tdb = NULL;
4093
4094         DEBUG(10, ("winbindd_validate_cache: replacing panic function\n"));
4095         smb_panic_fn = validate_panic;
4096
4097
4098         tdb = tdb_open_log(tdb_path, 
4099                            WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
4100                            TDB_INCOMPATIBLE_HASH |
4101                            ( lp_winbind_offline_logon() 
4102                              ? TDB_DEFAULT 
4103                              : TDB_DEFAULT | TDB_CLEAR_IF_FIRST ),
4104                            O_RDWR|O_CREAT, 
4105                            0600);
4106         if (!tdb) {
4107                 DEBUG(0, ("winbindd_validate_cache: "
4108                           "error opening/initializing tdb\n"));
4109                 goto done;
4110         }
4111         tdb_close(tdb);
4112
4113         ret = tdb_validate_and_backup(tdb_path, cache_traverse_validate_fn);
4114
4115         if (ret != 0) {
4116                 DEBUG(10, ("winbindd_validate_cache: validation not successful.\n"));
4117                 DEBUGADD(10, ("removing tdb %s.\n", tdb_path));
4118                 unlink(tdb_path);
4119         }
4120
4121 done:
4122         DEBUG(10, ("winbindd_validate_cache: restoring panic function\n"));
4123         smb_panic_fn = smb_panic;
4124         return ret;
4125 }
4126
4127 /***********************************************************************
4128  Try and validate every entry in the winbindd cache.
4129 ***********************************************************************/
4130
4131 int winbindd_validate_cache_nobackup(void)
4132 {
4133         int ret = -1;
4134         const char *tdb_path = cache_path("winbindd_cache.tdb");
4135
4136         DEBUG(10, ("winbindd_validate_cache: replacing panic function\n"));
4137         smb_panic_fn = validate_panic;
4138
4139
4140         if (wcache == NULL || wcache->tdb == NULL) {
4141                 ret = tdb_validate_open(tdb_path, cache_traverse_validate_fn);
4142         } else {
4143                 ret = tdb_validate(wcache->tdb, cache_traverse_validate_fn);
4144         }
4145
4146         if (ret != 0) {
4147                 DEBUG(10, ("winbindd_validate_cache_nobackup: validation not "
4148                            "successful.\n"));
4149         }
4150
4151         DEBUG(10, ("winbindd_validate_cache_nobackup: restoring panic "
4152                    "function\n"));
4153         smb_panic_fn = smb_panic;
4154         return ret;
4155 }
4156
4157 bool winbindd_cache_validate_and_initialize(void)
4158 {
4159         close_winbindd_cache();
4160
4161         if (lp_winbind_offline_logon()) {
4162                 if (winbindd_validate_cache() < 0) {
4163                         DEBUG(0, ("winbindd cache tdb corrupt and no backup "
4164                                   "could be restored.\n"));
4165                 }
4166         }
4167
4168         return initialize_winbindd_cache();
4169 }
4170
4171 /*********************************************************************
4172  ********************************************************************/
4173
4174 static bool add_wbdomain_to_tdc_array( struct winbindd_domain *new_dom,
4175                                        struct winbindd_tdc_domain **domains, 
4176                                        size_t *num_domains )
4177 {
4178         struct winbindd_tdc_domain *list = NULL;
4179         size_t idx;
4180         int i;
4181         bool set_only = false;
4182
4183         /* don't allow duplicates */
4184
4185         idx = *num_domains;
4186         list = *domains;
4187
4188         for ( i=0; i< (*num_domains); i++ ) {
4189                 if ( strequal( new_dom->name, list[i].domain_name ) ) {
4190                         DEBUG(10,("add_wbdomain_to_tdc_array: Found existing record for %s\n",
4191                                   new_dom->name));
4192                         idx = i;
4193                         set_only = true;
4194
4195                         break;
4196                 }
4197         }
4198
4199         if ( !set_only ) {
4200                 if ( !*domains ) {
4201                         list = TALLOC_ARRAY( NULL, struct winbindd_tdc_domain, 1 );
4202                         idx = 0;
4203                 } else {
4204                         list = TALLOC_REALLOC_ARRAY( *domains, *domains, 
4205                                                      struct winbindd_tdc_domain,  
4206                                                      (*num_domains)+1);
4207                         idx = *num_domains;             
4208                 }
4209
4210                 ZERO_STRUCT( list[idx] );
4211         }
4212
4213         if ( !list )
4214                 return false;
4215
4216         list[idx].domain_name = talloc_strdup( list, new_dom->name );
4217         list[idx].dns_name = talloc_strdup( list, new_dom->alt_name );
4218
4219         if ( !is_null_sid( &new_dom->sid ) ) {
4220                 sid_copy( &list[idx].sid, &new_dom->sid );
4221         } else {
4222                 sid_copy(&list[idx].sid, &global_sid_NULL);
4223         }
4224
4225         if ( new_dom->domain_flags != 0x0 )
4226                 list[idx].trust_flags = new_dom->domain_flags;  
4227
4228         if ( new_dom->domain_type != 0x0 )
4229                 list[idx].trust_type = new_dom->domain_type;
4230
4231         if ( new_dom->domain_trust_attribs != 0x0 )
4232                 list[idx].trust_attribs = new_dom->domain_trust_attribs;
4233
4234         if ( !set_only ) {
4235                 *domains = list;
4236                 *num_domains = idx + 1; 
4237         }
4238
4239         return true;
4240 }
4241
4242 /*********************************************************************
4243  ********************************************************************/
4244
4245 static TDB_DATA make_tdc_key( const char *domain_name )
4246 {
4247         char *keystr = NULL;
4248         TDB_DATA key = { NULL, 0 };
4249
4250         if ( !domain_name ) {
4251                 DEBUG(5,("make_tdc_key: Keyname workgroup is NULL!\n"));
4252                 return key;
4253         }
4254
4255         if (asprintf( &keystr, "TRUSTDOMCACHE/%s", domain_name ) == -1) {
4256                 return key;
4257         }
4258         key = string_term_tdb_data(keystr);
4259
4260         return key;     
4261 }
4262
4263 /*********************************************************************
4264  ********************************************************************/
4265
4266 static int pack_tdc_domains( struct winbindd_tdc_domain *domains, 
4267                              size_t num_domains,
4268                              unsigned char **buf )
4269 {
4270         unsigned char *buffer = NULL;
4271         int len = 0;
4272         int buflen = 0;
4273         int i = 0;
4274
4275         DEBUG(10,("pack_tdc_domains: Packing %d trusted domains\n",
4276                   (int)num_domains));
4277
4278         buflen = 0;
4279
4280  again: 
4281         len = 0;
4282
4283         /* Store the number of array items first */
4284         len += tdb_pack( buffer+len, buflen-len, "d", 
4285                          num_domains );
4286
4287         /* now pack each domain trust record */
4288         for ( i=0; i<num_domains; i++ ) {
4289
4290                 fstring tmp;
4291
4292                 if ( buflen > 0 ) {
4293                         DEBUG(10,("pack_tdc_domains: Packing domain %s (%s)\n",
4294                                   domains[i].domain_name,
4295                                   domains[i].dns_name ? domains[i].dns_name : "UNKNOWN" ));
4296                 }
4297
4298                 len += tdb_pack( buffer+len, buflen-len, "fffddd",
4299                                  domains[i].domain_name,
4300                                  domains[i].dns_name,
4301                                  sid_to_fstring(tmp, &domains[i].sid),
4302                                  domains[i].trust_flags,
4303                                  domains[i].trust_attribs,
4304                                  domains[i].trust_type );
4305         }
4306
4307         if ( buflen < len ) {
4308                 SAFE_FREE(buffer);
4309                 if ( (buffer = SMB_MALLOC_ARRAY(unsigned char, len)) == NULL ) {
4310                         DEBUG(0,("pack_tdc_domains: failed to alloc buffer!\n"));
4311                         buflen = -1;
4312                         goto done;
4313                 }
4314                 buflen = len;
4315                 goto again;
4316         }
4317
4318         *buf = buffer;  
4319
4320  done:  
4321         return buflen;  
4322 }
4323
4324 /*********************************************************************
4325  ********************************************************************/
4326
4327 static size_t unpack_tdc_domains( unsigned char *buf, int buflen, 
4328                                   struct winbindd_tdc_domain **domains )
4329 {
4330         fstring domain_name, dns_name, sid_string;      
4331         uint32 type, attribs, flags;
4332         int num_domains;
4333         int len = 0;
4334         int i;
4335         struct winbindd_tdc_domain *list = NULL;
4336
4337         /* get the number of domains */
4338         len += tdb_unpack( buf+len, buflen-len, "d", &num_domains);
4339         if ( len == -1 ) {
4340                 DEBUG(5,("unpack_tdc_domains: Failed to unpack domain array\n"));               
4341                 return 0;
4342         }
4343
4344         list = TALLOC_ARRAY( NULL, struct winbindd_tdc_domain, num_domains );
4345         if ( !list ) {
4346                 DEBUG(0,("unpack_tdc_domains: Failed to talloc() domain list!\n"));
4347                 return 0;               
4348         }
4349
4350         for ( i=0; i<num_domains; i++ ) {
4351                 len += tdb_unpack( buf+len, buflen-len, "fffddd",
4352                                    domain_name,
4353                                    dns_name,
4354                                    sid_string,
4355                                    &flags,
4356                                    &attribs,
4357                                    &type );
4358
4359                 if ( len == -1 ) {
4360                         DEBUG(5,("unpack_tdc_domains: Failed to unpack domain array\n"));
4361                         TALLOC_FREE( list );                    
4362                         return 0;
4363                 }
4364
4365                 DEBUG(11,("unpack_tdc_domains: Unpacking domain %s (%s) "
4366                           "SID %s, flags = 0x%x, attribs = 0x%x, type = 0x%x\n",
4367                           domain_name, dns_name, sid_string,
4368                           flags, attribs, type));
4369
4370                 list[i].domain_name = talloc_strdup( list, domain_name );
4371                 list[i].dns_name = talloc_strdup( list, dns_name );
4372                 if ( !string_to_sid( &(list[i].sid), sid_string ) ) {                   
4373                         DEBUG(10,("unpack_tdc_domains: no SID for domain %s\n",
4374                                   domain_name));
4375                 }
4376                 list[i].trust_flags = flags;
4377                 list[i].trust_attribs = attribs;
4378                 list[i].trust_type = type;
4379         }
4380
4381         *domains = list;
4382
4383         return num_domains;
4384 }
4385
4386 /*********************************************************************
4387  ********************************************************************/
4388
4389 static bool wcache_tdc_store_list( struct winbindd_tdc_domain *domains, size_t num_domains )
4390 {
4391         TDB_DATA key = make_tdc_key( lp_workgroup() );   
4392         TDB_DATA data = { NULL, 0 };
4393         int ret;
4394
4395         if ( !key.dptr )
4396                 return false;
4397
4398         /* See if we were asked to delete the cache entry */
4399
4400         if ( !domains ) {
4401                 ret = tdb_delete( wcache->tdb, key );
4402                 goto done;
4403         }
4404
4405         data.dsize = pack_tdc_domains( domains, num_domains, &data.dptr );
4406
4407         if ( !data.dptr ) {
4408                 ret = -1;
4409                 goto done;
4410         }
4411
4412         ret = tdb_store( wcache->tdb, key, data, 0 );
4413
4414  done:
4415         SAFE_FREE( data.dptr );
4416         SAFE_FREE( key.dptr );
4417
4418         return ( ret != -1 );   
4419 }
4420
4421 /*********************************************************************
4422  ********************************************************************/
4423
4424 bool wcache_tdc_fetch_list( struct winbindd_tdc_domain **domains, size_t *num_domains )
4425 {
4426         TDB_DATA key = make_tdc_key( lp_workgroup() );
4427         TDB_DATA data = { NULL, 0 };
4428
4429         *domains = NULL;        
4430         *num_domains = 0;       
4431
4432         if ( !key.dptr )
4433                 return false;
4434
4435         data = tdb_fetch( wcache->tdb, key );
4436
4437         SAFE_FREE( key.dptr );
4438
4439         if ( !data.dptr ) 
4440                 return false;
4441
4442         *num_domains = unpack_tdc_domains( data.dptr, data.dsize, domains );
4443
4444         SAFE_FREE( data.dptr );
4445
4446         if ( !*domains )
4447                 return false;
4448
4449         return true;
4450 }
4451
4452 /*********************************************************************
4453  ********************************************************************/
4454
4455 bool wcache_tdc_add_domain( struct winbindd_domain *domain )
4456 {
4457         struct winbindd_tdc_domain *dom_list = NULL;
4458         size_t num_domains = 0;
4459         bool ret = false;
4460
4461         DEBUG(10,("wcache_tdc_add_domain: Adding domain %s (%s), SID %s, "
4462                   "flags = 0x%x, attributes = 0x%x, type = 0x%x\n",
4463                   domain->name, domain->alt_name, 
4464                   sid_string_dbg(&domain->sid),
4465                   domain->domain_flags,
4466                   domain->domain_trust_attribs,
4467                   domain->domain_type));        
4468
4469         if ( !init_wcache() ) {
4470                 return false;
4471         }
4472
4473         /* fetch the list */
4474
4475         wcache_tdc_fetch_list( &dom_list, &num_domains );
4476
4477         /* add the new domain */
4478
4479         if ( !add_wbdomain_to_tdc_array( domain, &dom_list, &num_domains ) ) {
4480                 goto done;              
4481         }       
4482
4483         /* pack the domain */
4484
4485         if ( !wcache_tdc_store_list( dom_list, num_domains ) ) {
4486                 goto done;              
4487         }
4488
4489         /* Success */
4490
4491         ret = true;
4492  done:
4493         TALLOC_FREE( dom_list );
4494
4495         return ret;     
4496 }
4497
4498 /*********************************************************************
4499  ********************************************************************/
4500
4501 struct winbindd_tdc_domain * wcache_tdc_fetch_domain( TALLOC_CTX *ctx, const char *name )
4502 {
4503         struct winbindd_tdc_domain *dom_list = NULL;
4504         size_t num_domains = 0;
4505         int i;
4506         struct winbindd_tdc_domain *d = NULL;   
4507
4508         DEBUG(10,("wcache_tdc_fetch_domain: Searching for domain %s\n", name));
4509
4510         if ( !init_wcache() ) {
4511                 return false;
4512         }
4513
4514         /* fetch the list */
4515
4516         wcache_tdc_fetch_list( &dom_list, &num_domains );
4517
4518         for ( i=0; i<num_domains; i++ ) {
4519                 if ( strequal(name, dom_list[i].domain_name) ||
4520                      strequal(name, dom_list[i].dns_name) )
4521                 {
4522                         DEBUG(10,("wcache_tdc_fetch_domain: Found domain %s\n",
4523                                   name));
4524
4525                         d = TALLOC_P( ctx, struct winbindd_tdc_domain );
4526                         if ( !d )
4527                                 break;                  
4528
4529                         d->domain_name = talloc_strdup( d, dom_list[i].domain_name );
4530                         d->dns_name = talloc_strdup( d, dom_list[i].dns_name );
4531                         sid_copy( &d->sid, &dom_list[i].sid );
4532                         d->trust_flags   = dom_list[i].trust_flags;
4533                         d->trust_type    = dom_list[i].trust_type;
4534                         d->trust_attribs = dom_list[i].trust_attribs;
4535
4536                         break;
4537                 }
4538         }
4539
4540         TALLOC_FREE( dom_list );
4541
4542         return d;       
4543 }
4544
4545
4546 /*********************************************************************
4547  ********************************************************************/
4548
4549 void wcache_tdc_clear( void )
4550 {
4551         if ( !init_wcache() )
4552                 return;
4553
4554         wcache_tdc_store_list( NULL, 0 );
4555
4556         return; 
4557 }
4558
4559
4560 /*********************************************************************
4561  ********************************************************************/
4562
4563 static void wcache_save_user_pwinfo(struct winbindd_domain *domain, 
4564                                     NTSTATUS status,
4565                                     const struct dom_sid *user_sid,
4566                                     const char *homedir,
4567                                     const char *shell,
4568                                     const char *gecos,
4569                                     uint32 gid)
4570 {
4571         struct cache_entry *centry;
4572         fstring tmp;
4573
4574         if ( (centry = centry_start(domain, status)) == NULL )
4575                 return;
4576
4577         centry_put_string( centry, homedir );
4578         centry_put_string( centry, shell );
4579         centry_put_string( centry, gecos );
4580         centry_put_uint32( centry, gid );
4581
4582         centry_end(centry, "NSS/PWINFO/%s", sid_to_fstring(tmp, user_sid) );
4583
4584         DEBUG(10,("wcache_save_user_pwinfo: %s\n", sid_string_dbg(user_sid) ));
4585
4586         centry_free(centry);
4587 }
4588
4589 #ifdef HAVE_ADS
4590
4591 NTSTATUS nss_get_info_cached( struct winbindd_domain *domain, 
4592                               const struct dom_sid *user_sid,
4593                               TALLOC_CTX *ctx,
4594                               ADS_STRUCT *ads, LDAPMessage *msg,
4595                               const char **homedir, const char **shell,
4596                               const char **gecos, gid_t *p_gid)
4597 {
4598         struct winbind_cache *cache = get_cache(domain);
4599         struct cache_entry *centry = NULL;
4600         NTSTATUS nt_status;
4601         fstring tmp;
4602
4603         if (!cache->tdb)
4604                 goto do_query;
4605
4606         centry = wcache_fetch(cache, domain, "NSS/PWINFO/%s",
4607                               sid_to_fstring(tmp, user_sid));
4608
4609         if (!centry)
4610                 goto do_query;
4611
4612         *homedir = centry_string( centry, ctx );
4613         *shell   = centry_string( centry, ctx );
4614         *gecos   = centry_string( centry, ctx );
4615         *p_gid   = centry_uint32( centry );     
4616
4617         centry_free(centry);
4618
4619         DEBUG(10,("nss_get_info_cached: [Cached] - user_sid %s\n",
4620                   sid_string_dbg(user_sid)));
4621
4622         return NT_STATUS_OK;
4623
4624 do_query:
4625
4626         nt_status = nss_get_info( domain->name, user_sid, ctx, ads, msg, 
4627                                   homedir, shell, gecos, p_gid );
4628
4629         DEBUG(10, ("nss_get_info returned %s\n", nt_errstr(nt_status)));
4630
4631         if ( NT_STATUS_IS_OK(nt_status) ) {
4632                 DEBUG(10, ("result:\n\thomedir = '%s'\n", *homedir));
4633                 DEBUGADD(10, ("\tshell = '%s'\n", *shell));
4634                 DEBUGADD(10, ("\tgecos = '%s'\n", *gecos));
4635                 DEBUGADD(10, ("\tgid = '%u'\n", (unsigned int)*p_gid));
4636
4637                 wcache_save_user_pwinfo( domain, nt_status, user_sid,
4638                                          *homedir, *shell, *gecos, *p_gid );
4639         }       
4640
4641         if ( NT_STATUS_EQUAL( nt_status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND ) ) {
4642                 DEBUG(5,("nss_get_info_cached: Setting domain %s offline\n",
4643                          domain->name ));
4644                 set_domain_offline( domain );
4645         }
4646
4647         return nt_status;       
4648 }
4649
4650 #endif
4651
4652 /* the cache backend methods are exposed via this structure */
4653 struct winbindd_methods cache_methods = {
4654         true,
4655         query_user_list,
4656         enum_dom_groups,
4657         enum_local_groups,
4658         name_to_sid,
4659         sid_to_name,
4660         rids_to_names,
4661         query_user,
4662         lookup_usergroups,
4663         lookup_useraliases,
4664         lookup_groupmem,
4665         sequence_number,
4666         lockout_policy,
4667         password_policy,
4668         trusted_domains
4669 };
4670
4671 static bool wcache_ndr_key(TALLOC_CTX *mem_ctx, char *domain_name,
4672                            uint32_t opnum, const DATA_BLOB *req,
4673                            TDB_DATA *pkey)
4674 {
4675         char *key;
4676         size_t keylen;
4677
4678         key = talloc_asprintf(mem_ctx, "NDR/%s/%d/", domain_name, (int)opnum);
4679         if (key == NULL) {
4680                 return false;
4681         }
4682         keylen = talloc_get_size(key) - 1;
4683
4684         key = talloc_realloc(mem_ctx, key, char, keylen + req->length);
4685         if (key == NULL) {
4686                 return false;
4687         }
4688         memcpy(key + keylen, req->data, req->length);
4689
4690         pkey->dptr = (uint8_t *)key;
4691         pkey->dsize = talloc_get_size(key);
4692         return true;
4693 }
4694
4695 static bool wcache_opnum_cacheable(uint32_t opnum)
4696 {
4697         switch (opnum) {
4698         case NDR_WBINT_PING:
4699         case NDR_WBINT_QUERYSEQUENCENUMBER:
4700         case NDR_WBINT_ALLOCATEUID:
4701         case NDR_WBINT_ALLOCATEGID:
4702         case NDR_WBINT_CHECKMACHINEACCOUNT:
4703         case NDR_WBINT_CHANGEMACHINEACCOUNT:
4704         case NDR_WBINT_PINGDC:
4705                 return false;
4706         }
4707         return true;
4708 }
4709
4710 bool wcache_fetch_ndr(TALLOC_CTX *mem_ctx, struct winbindd_domain *domain,
4711                       uint32_t opnum, const DATA_BLOB *req, DATA_BLOB *resp)
4712 {
4713         TDB_DATA key, data;
4714         bool ret = false;
4715
4716         if (!wcache_opnum_cacheable(opnum) ||
4717             is_my_own_sam_domain(domain) ||
4718             is_builtin_domain(domain)) {
4719                 return false;
4720         }
4721
4722         if (wcache->tdb == NULL) {
4723                 return false;
4724         }
4725
4726         if (!wcache_ndr_key(talloc_tos(), domain->name, opnum, req, &key)) {
4727                 return false;
4728         }
4729         data = tdb_fetch(wcache->tdb, key);
4730         TALLOC_FREE(key.dptr);
4731
4732         if (data.dptr == NULL) {
4733                 return false;
4734         }
4735         if (data.dsize < 12) {
4736                 goto fail;
4737         }
4738
4739         if (!is_domain_offline(domain)) {
4740                 uint32_t entry_seqnum, dom_seqnum, last_check;
4741                 uint64_t entry_timeout;
4742
4743                 if (!wcache_fetch_seqnum(domain->name, &dom_seqnum,
4744                                          &last_check)) {
4745                         goto fail;
4746                 }
4747                 entry_seqnum = IVAL(data.dptr, 0);
4748                 if (entry_seqnum != dom_seqnum) {
4749                         DEBUG(10, ("Entry has wrong sequence number: %d\n",
4750                                    (int)entry_seqnum));
4751                         goto fail;
4752                 }
4753                 entry_timeout = BVAL(data.dptr, 4);
4754                 if (entry_timeout > time(NULL)) {
4755                         DEBUG(10, ("Entry has timed out\n"));
4756                         goto fail;
4757                 }
4758         }
4759
4760         resp->data = (uint8_t *)talloc_memdup(mem_ctx, data.dptr + 12,
4761                                               data.dsize - 12);
4762         if (resp->data == NULL) {
4763                 DEBUG(10, ("talloc failed\n"));
4764                 goto fail;
4765         }
4766         resp->length = data.dsize - 12;
4767
4768         ret = true;
4769 fail:
4770         SAFE_FREE(data.dptr);
4771         return ret;
4772 }
4773
4774 void wcache_store_ndr(struct winbindd_domain *domain, uint32_t opnum,
4775                       const DATA_BLOB *req, const DATA_BLOB *resp)
4776 {
4777         TDB_DATA key, data;
4778         uint32_t dom_seqnum, last_check;
4779         uint64_t timeout;
4780
4781         if (!wcache_opnum_cacheable(opnum) ||
4782             is_my_own_sam_domain(domain) ||
4783             is_builtin_domain(domain)) {
4784                 return;
4785         }
4786
4787         if (wcache->tdb == NULL) {
4788                 return;
4789         }
4790
4791         if (!wcache_fetch_seqnum(domain->name, &dom_seqnum, &last_check)) {
4792                 DEBUG(10, ("could not fetch seqnum for domain %s\n",
4793                            domain->name));
4794                 return;
4795         }
4796
4797         if (!wcache_ndr_key(talloc_tos(), domain->name, opnum, req, &key)) {
4798                 return;
4799         }
4800
4801         timeout = time(NULL) + lp_winbind_cache_time();
4802
4803         data.dsize = resp->length + 12;
4804         data.dptr = talloc_array(key.dptr, uint8_t, data.dsize);
4805         if (data.dptr == NULL) {
4806                 goto done;
4807         }
4808
4809         SIVAL(data.dptr, 0, dom_seqnum);
4810         SBVAL(data.dptr, 4, timeout);
4811         memcpy(data.dptr + 12, resp->data, resp->length);
4812
4813         tdb_store(wcache->tdb, key, data, 0);
4814
4815 done:
4816         TALLOC_FREE(key.dptr);
4817         return;
4818 }