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