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