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