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