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