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