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