Fix wbinfo -a trusted\\user%password on a Samba DC with trusts
[ira/wip.git] / source / 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
29 #undef DBGC_CLASS
30 #define DBGC_CLASS DBGC_WINBIND
31
32 #define WINBINDD_CACHE_VERSION 1
33 #define WINBINDD_CACHE_VERSION_KEYSTR "WINBINDD_CACHE_VERSION"
34
35 extern struct winbindd_methods reconnect_methods;
36 extern bool opt_nocache;
37 #ifdef HAVE_ADS
38 extern struct winbindd_methods ads_methods;
39 #endif
40 extern struct winbindd_methods passdb_methods;
41
42 /*
43  * JRA. KEEP THIS LIST UP TO DATE IF YOU ADD CACHE ENTRIES.
44  * Here are the list of entry types that are *not* stored
45  * as form struct cache_entry in the cache.
46  */
47
48 static const char *non_centry_keys[] = {
49         "SEQNUM/",
50         "DR/",
51         "DE/",
52         "WINBINDD_OFFLINE",
53         WINBINDD_CACHE_VERSION_KEYSTR,
54         NULL
55 };
56
57 /************************************************************************
58  Is this key a non-centry type ?
59 ************************************************************************/
60
61 static bool is_non_centry_key(TDB_DATA kbuf)
62 {
63         int i;
64
65         if (kbuf.dptr == NULL || kbuf.dsize == 0) {
66                 return false;
67         }
68         for (i = 0; non_centry_keys[i] != NULL; i++) {
69                 size_t namelen = strlen(non_centry_keys[i]);
70                 if (kbuf.dsize < namelen) {
71                         continue;
72                 }
73                 if (strncmp(non_centry_keys[i], (const char *)kbuf.dptr, namelen) == 0) {
74                         return true;
75                 }
76         }
77         return false;
78 }
79
80 /* Global online/offline state - False when online. winbindd starts up online
81    and sets this to true if the first query fails and there's an entry in
82    the cache tdb telling us to stay offline. */
83
84 static bool global_winbindd_offline_state;
85
86 struct winbind_cache {
87         TDB_CONTEXT *tdb;
88 };
89
90 struct cache_entry {
91         NTSTATUS status;
92         uint32 sequence_number;
93         uint8 *data;
94         uint32 len, ofs;
95 };
96
97 void (*smb_panic_fn)(const char *const why) = smb_panic;
98
99 #define WINBINDD_MAX_CACHE_SIZE (50*1024*1024)
100
101 static struct winbind_cache *wcache;
102
103 void winbindd_check_cache_size(time_t t)
104 {
105         static time_t last_check_time;
106         struct stat st;
107
108         if (last_check_time == (time_t)0)
109                 last_check_time = t;
110
111         if (t - last_check_time < 60 && t - last_check_time > 0)
112                 return;
113
114         if (wcache == NULL || wcache->tdb == NULL) {
115                 DEBUG(0, ("Unable to check size of tdb cache - cache not open !\n"));
116                 return;
117         }
118
119         if (fstat(tdb_fd(wcache->tdb), &st) == -1) {
120                 DEBUG(0, ("Unable to check size of tdb cache %s!\n", strerror(errno) ));
121                 return;
122         }
123
124         if (st.st_size > WINBINDD_MAX_CACHE_SIZE) {
125                 DEBUG(10,("flushing cache due to size (%lu) > (%lu)\n",
126                         (unsigned long)st.st_size,
127                         (unsigned long)WINBINDD_MAX_CACHE_SIZE));
128                 wcache_flush_cache();
129         }
130 }
131
132 /* get the winbind_cache structure */
133 static struct winbind_cache *get_cache(struct winbindd_domain *domain)
134 {
135         struct winbind_cache *ret = wcache;
136
137         /* We have to know what type of domain we are dealing with first. */
138
139         if (domain->internal) {
140                 domain->backend = &passdb_methods;
141                 domain->initialized = True;
142         }
143         if ( !domain->initialized ) {
144                 init_dc_connection( domain );
145         }
146
147         /* 
148            OK.  listen up becasue I'm only going to say this once.
149            We have the following scenarios to consider
150            (a) trusted AD domains on a Samba DC,
151            (b) trusted AD domains and we are joined to a non-kerberos domain
152            (c) trusted AD domains and we are joined to a kerberos (AD) domain
153
154            For (a) we can always contact the trusted domain using krb5 
155            since we have the domain trust account password
156
157            For (b) we can only use RPC since we have no way of 
158            getting a krb5 ticket in our own domain
159
160            For (c) we can always use krb5 since we have a kerberos trust
161
162            --jerry
163          */
164
165         if (!domain->backend) {
166 #ifdef HAVE_ADS
167                 struct winbindd_domain *our_domain = domain;
168
169                 /* find our domain first so we can figure out if we 
170                    are joined to a kerberized domain */
171
172                 if ( !domain->primary )
173                         our_domain = find_our_domain();
174
175                 if ((our_domain->active_directory || IS_DC)
176                     && domain->active_directory
177                     && !lp_winbind_rpc_only()) {
178                         DEBUG(5,("get_cache: Setting ADS methods for domain %s\n", domain->name));
179                         domain->backend = &ads_methods;
180                 } else {
181 #endif  /* HAVE_ADS */
182                         DEBUG(5,("get_cache: Setting MS-RPC methods for domain %s\n", domain->name));
183                         domain->backend = &reconnect_methods;
184 #ifdef HAVE_ADS
185                 }
186 #endif  /* HAVE_ADS */
187         }
188
189         if (ret)
190                 return ret;
191         
192         ret = SMB_XMALLOC_P(struct winbind_cache);
193         ZERO_STRUCTP(ret);
194
195         wcache = ret;
196         wcache_flush_cache();
197
198         return ret;
199 }
200
201 /*
202   free a centry structure
203 */
204 static void centry_free(struct cache_entry *centry)
205 {
206         if (!centry)
207                 return;
208         SAFE_FREE(centry->data);
209         free(centry);
210 }
211
212 static bool centry_check_bytes(struct cache_entry *centry, size_t nbytes)
213 {
214         if (centry->len - centry->ofs < nbytes) {
215                 DEBUG(0,("centry corruption? needed %u bytes, have %d\n", 
216                          (unsigned int)nbytes,
217                          centry->len - centry->ofs));
218                 return false;
219         }
220         return true;
221 }
222
223 /*
224   pull a uint32 from a cache entry 
225 */
226 static uint32 centry_uint32(struct cache_entry *centry)
227 {
228         uint32 ret;
229
230         if (!centry_check_bytes(centry, 4)) {
231                 smb_panic_fn("centry_uint32");
232         }
233         ret = IVAL(centry->data, centry->ofs);
234         centry->ofs += 4;
235         return ret;
236 }
237
238 /*
239   pull a uint16 from a cache entry 
240 */
241 static uint16 centry_uint16(struct cache_entry *centry)
242 {
243         uint16 ret;
244         if (!centry_check_bytes(centry, 2)) {
245                 smb_panic_fn("centry_uint16");
246         }
247         ret = CVAL(centry->data, centry->ofs);
248         centry->ofs += 2;
249         return ret;
250 }
251
252 /*
253   pull a uint8 from a cache entry 
254 */
255 static uint8 centry_uint8(struct cache_entry *centry)
256 {
257         uint8 ret;
258         if (!centry_check_bytes(centry, 1)) {
259                 smb_panic_fn("centry_uint8");
260         }
261         ret = CVAL(centry->data, centry->ofs);
262         centry->ofs += 1;
263         return ret;
264 }
265
266 /*
267   pull a NTTIME from a cache entry 
268 */
269 static NTTIME centry_nttime(struct cache_entry *centry)
270 {
271         NTTIME ret;
272         if (!centry_check_bytes(centry, 8)) {
273                 smb_panic_fn("centry_nttime");
274         }
275         ret = IVAL(centry->data, centry->ofs);
276         centry->ofs += 4;
277         ret += (uint64_t)IVAL(centry->data, centry->ofs) << 32;
278         centry->ofs += 4;
279         return ret;
280 }
281
282 /*
283   pull a time_t from a cache entry. time_t stored portably as a 64-bit time.
284 */
285 static time_t centry_time(struct cache_entry *centry)
286 {
287         return (time_t)centry_nttime(centry);
288 }
289
290 /* pull a string from a cache entry, using the supplied
291    talloc context 
292 */
293 static char *centry_string(struct cache_entry *centry, TALLOC_CTX *mem_ctx)
294 {
295         uint32 len;
296         char *ret;
297
298         len = centry_uint8(centry);
299
300         if (len == 0xFF) {
301                 /* a deliberate NULL string */
302                 return NULL;
303         }
304
305         if (!centry_check_bytes(centry, (size_t)len)) {
306                 smb_panic_fn("centry_string");
307         }
308
309         ret = TALLOC_ARRAY(mem_ctx, char, len+1);
310         if (!ret) {
311                 smb_panic_fn("centry_string out of memory\n");
312         }
313         memcpy(ret,centry->data + centry->ofs, len);
314         ret[len] = 0;
315         centry->ofs += len;
316         return ret;
317 }
318
319 /* pull a hash16 from a cache entry, using the supplied
320    talloc context 
321 */
322 static char *centry_hash16(struct cache_entry *centry, TALLOC_CTX *mem_ctx)
323 {
324         uint32 len;
325         char *ret;
326
327         len = centry_uint8(centry);
328
329         if (len != 16) {
330                 DEBUG(0,("centry corruption? hash len (%u) != 16\n", 
331                         len ));
332                 return NULL;
333         }
334
335         if (!centry_check_bytes(centry, 16)) {
336                 return NULL;
337         }
338
339         ret = TALLOC_ARRAY(mem_ctx, char, 16);
340         if (!ret) {
341                 smb_panic_fn("centry_hash out of memory\n");
342         }
343         memcpy(ret,centry->data + centry->ofs, 16);
344         centry->ofs += 16;
345         return ret;
346 }
347
348 /* pull a sid from a cache entry, using the supplied
349    talloc context 
350 */
351 static bool centry_sid(struct cache_entry *centry, TALLOC_CTX *mem_ctx, DOM_SID *sid)
352 {
353         char *sid_string;
354         sid_string = centry_string(centry, mem_ctx);
355         if ((sid_string == NULL) || (!string_to_sid(sid, sid_string))) {
356                 return false;
357         }
358         return true;
359 }
360
361
362 /*
363   pull a NTSTATUS from a cache entry
364 */
365 static NTSTATUS centry_ntstatus(struct cache_entry *centry)
366 {
367         NTSTATUS status;
368
369         status = NT_STATUS(centry_uint32(centry));
370         return status;
371 }
372
373
374 /* the server is considered down if it can't give us a sequence number */
375 static bool wcache_server_down(struct winbindd_domain *domain)
376 {
377         bool ret;
378
379         if (!wcache->tdb)
380                 return false;
381
382         ret = (domain->sequence_number == DOM_SEQUENCE_NONE);
383
384         if (ret)
385                 DEBUG(10,("wcache_server_down: server for Domain %s down\n", 
386                         domain->name ));
387         return ret;
388 }
389
390 static NTSTATUS fetch_cache_seqnum( struct winbindd_domain *domain, time_t now )
391 {
392         TDB_DATA data;
393         fstring key;
394         uint32 time_diff;
395         
396         if (!wcache->tdb) {
397                 DEBUG(10,("fetch_cache_seqnum: tdb == NULL\n"));
398                 return NT_STATUS_UNSUCCESSFUL;
399         }
400                 
401         fstr_sprintf( key, "SEQNUM/%s", domain->name );
402         
403         data = tdb_fetch_bystring( wcache->tdb, key );
404         if ( !data.dptr || data.dsize!=8 ) {
405                 DEBUG(10,("fetch_cache_seqnum: invalid data size key [%s]\n", key ));
406                 return NT_STATUS_UNSUCCESSFUL;
407         }
408         
409         domain->sequence_number = IVAL(data.dptr, 0);
410         domain->last_seq_check  = IVAL(data.dptr, 4);
411         
412         SAFE_FREE(data.dptr);
413
414         /* have we expired? */
415         
416         time_diff = now - domain->last_seq_check;
417         if ( time_diff > lp_winbind_cache_time() ) {
418                 DEBUG(10,("fetch_cache_seqnum: timeout [%s][%u @ %u]\n",
419                         domain->name, domain->sequence_number,
420                         (uint32)domain->last_seq_check));
421                 return NT_STATUS_UNSUCCESSFUL;
422         }
423
424         DEBUG(10,("fetch_cache_seqnum: success [%s][%u @ %u]\n", 
425                 domain->name, domain->sequence_number, 
426                 (uint32)domain->last_seq_check));
427
428         return NT_STATUS_OK;
429 }
430
431 static NTSTATUS store_cache_seqnum( struct winbindd_domain *domain )
432 {
433         TDB_DATA data;
434         fstring key_str;
435         uint8 buf[8];
436         
437         if (!wcache->tdb) {
438                 DEBUG(10,("store_cache_seqnum: tdb == NULL\n"));
439                 return NT_STATUS_UNSUCCESSFUL;
440         }
441                 
442         fstr_sprintf( key_str, "SEQNUM/%s", domain->name );
443         
444         SIVAL(buf, 0, domain->sequence_number);
445         SIVAL(buf, 4, domain->last_seq_check);
446         data.dptr = buf;
447         data.dsize = 8;
448         
449         if ( tdb_store_bystring( wcache->tdb, key_str, data, TDB_REPLACE) == -1 ) {
450                 DEBUG(10,("store_cache_seqnum: tdb_store fail key [%s]\n", key_str ));
451                 return NT_STATUS_UNSUCCESSFUL;
452         }
453
454         DEBUG(10,("store_cache_seqnum: success [%s][%u @ %u]\n", 
455                 domain->name, domain->sequence_number, 
456                 (uint32)domain->last_seq_check));
457         
458         return NT_STATUS_OK;
459 }
460
461 /*
462   refresh the domain sequence number. If force is true
463   then always refresh it, no matter how recently we fetched it
464 */
465
466 static void refresh_sequence_number(struct winbindd_domain *domain, bool force)
467 {
468         NTSTATUS status;
469         unsigned time_diff;
470         time_t t = time(NULL);
471         unsigned cache_time = lp_winbind_cache_time();
472
473         if ( IS_DOMAIN_OFFLINE(domain) ) {
474                 return;
475         }
476         
477         get_cache( domain );
478
479 #if 0   /* JERRY -- disable as the default cache time is now 5 minutes */
480         /* trying to reconnect is expensive, don't do it too often */
481         if (domain->sequence_number == DOM_SEQUENCE_NONE) {
482                 cache_time *= 8;
483         }
484 #endif
485
486         time_diff = t - domain->last_seq_check;
487
488         /* see if we have to refetch the domain sequence number */
489         if (!force && (time_diff < cache_time)) {
490                 DEBUG(10, ("refresh_sequence_number: %s time ok\n", domain->name));
491                 goto done;
492         }
493         
494         /* try to get the sequence number from the tdb cache first */
495         /* this will update the timestamp as well */
496         
497         status = fetch_cache_seqnum( domain, t );
498         if ( NT_STATUS_IS_OK(status) )
499                 goto done;      
500
501         /* important! make sure that we know if this is a native 
502            mode domain or not.  And that we can contact it. */
503
504         if ( winbindd_can_contact_domain( domain ) ) {          
505                 status = domain->backend->sequence_number(domain, 
506                                                           &domain->sequence_number);
507         } else {
508                 /* just use the current time */
509                 status = NT_STATUS_OK;
510                 domain->sequence_number = time(NULL);
511         }
512
513
514         /* the above call could have set our domain->backend to NULL when
515          * coming from offline to online mode, make sure to reinitialize the
516          * backend - Guenther */
517         get_cache( domain );
518
519         if (!NT_STATUS_IS_OK(status)) {
520                 DEBUG(10,("refresh_sequence_number: failed with %s\n", nt_errstr(status)));
521                 domain->sequence_number = DOM_SEQUENCE_NONE;
522         }
523         
524         domain->last_status = status;
525         domain->last_seq_check = time(NULL);
526         
527         /* save the new sequence number ni the cache */
528         store_cache_seqnum( domain );
529
530 done:
531         DEBUG(10, ("refresh_sequence_number: %s seq number is now %d\n", 
532                    domain->name, domain->sequence_number));
533
534         return;
535 }
536
537 /*
538   decide if a cache entry has expired
539 */
540 static bool centry_expired(struct winbindd_domain *domain, const char *keystr, struct cache_entry *centry)
541 {
542         /* If we've been told to be offline - stay in that state... */
543         if (lp_winbind_offline_logon() && global_winbindd_offline_state) {
544                 DEBUG(10,("centry_expired: Key %s for domain %s valid as winbindd is globally offline.\n",
545                         keystr, domain->name ));
546                 return false;
547         }
548
549         /* when the domain is offline return the cached entry.
550          * This deals with transient offline states... */
551
552         if (!domain->online) {
553                 DEBUG(10,("centry_expired: Key %s for domain %s valid as domain is offline.\n",
554                         keystr, domain->name ));
555                 return false;
556         }
557
558         /* if the server is OK and our cache entry came from when it was down then
559            the entry is invalid */
560         if ((domain->sequence_number != DOM_SEQUENCE_NONE) &&  
561             (centry->sequence_number == DOM_SEQUENCE_NONE)) {
562                 DEBUG(10,("centry_expired: Key %s for domain %s invalid sequence.\n",
563                         keystr, domain->name ));
564                 return true;
565         }
566
567         /* if the server is down or the cache entry is not older than the
568            current sequence number then it is OK */
569         if (wcache_server_down(domain) || 
570             centry->sequence_number == domain->sequence_number) {
571                 DEBUG(10,("centry_expired: Key %s for domain %s is good.\n",
572                         keystr, domain->name ));
573                 return false;
574         }
575
576         DEBUG(10,("centry_expired: Key %s for domain %s expired\n",
577                 keystr, domain->name ));
578
579         /* it's expired */
580         return true;
581 }
582
583 static struct cache_entry *wcache_fetch_raw(char *kstr)
584 {
585         TDB_DATA data;
586         struct cache_entry *centry;
587         TDB_DATA key;
588
589         key = string_tdb_data(kstr);
590         data = tdb_fetch(wcache->tdb, key);
591         if (!data.dptr) {
592                 /* a cache miss */
593                 return NULL;
594         }
595
596         centry = SMB_XMALLOC_P(struct cache_entry);
597         centry->data = (unsigned char *)data.dptr;
598         centry->len = data.dsize;
599         centry->ofs = 0;
600
601         if (centry->len < 8) {
602                 /* huh? corrupt cache? */
603                 DEBUG(10,("wcache_fetch_raw: Corrupt cache for key %s (len < 8) ?\n", kstr));
604                 centry_free(centry);
605                 return NULL;
606         }
607         
608         centry->status = centry_ntstatus(centry);
609         centry->sequence_number = centry_uint32(centry);
610
611         return centry;
612 }
613
614 /*
615   fetch an entry from the cache, with a varargs key. auto-fetch the sequence
616   number and return status
617 */
618 static struct cache_entry *wcache_fetch(struct winbind_cache *cache, 
619                                         struct winbindd_domain *domain,
620                                         const char *format, ...) PRINTF_ATTRIBUTE(3,4);
621 static struct cache_entry *wcache_fetch(struct winbind_cache *cache, 
622                                         struct winbindd_domain *domain,
623                                         const char *format, ...)
624 {
625         va_list ap;
626         char *kstr;
627         struct cache_entry *centry;
628
629         if (opt_nocache) {
630                 return NULL;
631         }
632
633         refresh_sequence_number(domain, false);
634
635         va_start(ap, format);
636         smb_xvasprintf(&kstr, format, ap);
637         va_end(ap);
638
639         centry = wcache_fetch_raw(kstr);
640         if (centry == NULL) {
641                 free(kstr);
642                 return NULL;
643         }
644
645         if (centry_expired(domain, kstr, centry)) {
646
647                 DEBUG(10,("wcache_fetch: entry %s expired for domain %s\n",
648                          kstr, domain->name ));
649
650                 centry_free(centry);
651                 free(kstr);
652                 return NULL;
653         }
654
655         DEBUG(10,("wcache_fetch: returning entry %s for domain %s\n",
656                  kstr, domain->name ));
657
658         free(kstr);
659         return centry;
660 }
661
662 static void wcache_delete(const char *format, ...) PRINTF_ATTRIBUTE(1,2);
663 static void wcache_delete(const char *format, ...)
664 {
665         va_list ap;
666         char *kstr;
667         TDB_DATA key;
668
669         va_start(ap, format);
670         smb_xvasprintf(&kstr, format, ap);
671         va_end(ap);
672
673         key = string_tdb_data(kstr);
674
675         tdb_delete(wcache->tdb, key);
676         free(kstr);
677 }
678
679 /*
680   make sure we have at least len bytes available in a centry 
681 */
682 static void centry_expand(struct cache_entry *centry, uint32 len)
683 {
684         if (centry->len - centry->ofs >= len)
685                 return;
686         centry->len *= 2;
687         centry->data = SMB_REALLOC_ARRAY(centry->data, unsigned char,
688                                          centry->len);
689         if (!centry->data) {
690                 DEBUG(0,("out of memory: needed %d bytes in centry_expand\n", centry->len));
691                 smb_panic_fn("out of memory in centry_expand");
692         }
693 }
694
695 /*
696   push a uint32 into a centry 
697 */
698 static void centry_put_uint32(struct cache_entry *centry, uint32 v)
699 {
700         centry_expand(centry, 4);
701         SIVAL(centry->data, centry->ofs, v);
702         centry->ofs += 4;
703 }
704
705 /*
706   push a uint16 into a centry 
707 */
708 static void centry_put_uint16(struct cache_entry *centry, uint16 v)
709 {
710         centry_expand(centry, 2);
711         SIVAL(centry->data, centry->ofs, v);
712         centry->ofs += 2;
713 }
714
715 /*
716   push a uint8 into a centry 
717 */
718 static void centry_put_uint8(struct cache_entry *centry, uint8 v)
719 {
720         centry_expand(centry, 1);
721         SCVAL(centry->data, centry->ofs, v);
722         centry->ofs += 1;
723 }
724
725 /* 
726    push a string into a centry 
727  */
728 static void centry_put_string(struct cache_entry *centry, const char *s)
729 {
730         int len;
731
732         if (!s) {
733                 /* null strings are marked as len 0xFFFF */
734                 centry_put_uint8(centry, 0xFF);
735                 return;
736         }
737
738         len = strlen(s);
739         /* can't handle more than 254 char strings. Truncating is probably best */
740         if (len > 254) {
741                 DEBUG(10,("centry_put_string: truncating len (%d) to: 254\n", len));
742                 len = 254;
743         }
744         centry_put_uint8(centry, len);
745         centry_expand(centry, len);
746         memcpy(centry->data + centry->ofs, s, len);
747         centry->ofs += len;
748 }
749
750 /* 
751    push a 16 byte hash into a centry - treat as 16 byte string.
752  */
753 static void centry_put_hash16(struct cache_entry *centry, const uint8 val[16])
754 {
755         centry_put_uint8(centry, 16);
756         centry_expand(centry, 16);
757         memcpy(centry->data + centry->ofs, val, 16);
758         centry->ofs += 16;
759 }
760
761 static void centry_put_sid(struct cache_entry *centry, const DOM_SID *sid) 
762 {
763         fstring sid_string;
764         centry_put_string(centry, sid_to_fstring(sid_string, sid));
765 }
766
767
768 /*
769   put NTSTATUS into a centry
770 */
771 static void centry_put_ntstatus(struct cache_entry *centry, NTSTATUS status)
772 {
773         uint32 status_value = NT_STATUS_V(status);
774         centry_put_uint32(centry, status_value);
775 }
776
777
778 /*
779   push a NTTIME into a centry 
780 */
781 static void centry_put_nttime(struct cache_entry *centry, NTTIME nt)
782 {
783         centry_expand(centry, 8);
784         SIVAL(centry->data, centry->ofs, nt & 0xFFFFFFFF);
785         centry->ofs += 4;
786         SIVAL(centry->data, centry->ofs, nt >> 32);
787         centry->ofs += 4;
788 }
789
790 /*
791   push a time_t into a centry - use a 64 bit size.
792   NTTIME here is being used as a convenient 64-bit size.
793 */
794 static void centry_put_time(struct cache_entry *centry, time_t t)
795 {
796         NTTIME nt = (NTTIME)t;
797         centry_put_nttime(centry, nt);
798 }
799
800 /*
801   start a centry for output. When finished, call centry_end()
802 */
803 struct cache_entry *centry_start(struct winbindd_domain *domain, NTSTATUS status)
804 {
805         struct cache_entry *centry;
806
807         if (!wcache->tdb)
808                 return NULL;
809
810         centry = SMB_XMALLOC_P(struct cache_entry);
811
812         centry->len = 8192; /* reasonable default */
813         centry->data = SMB_XMALLOC_ARRAY(uint8, centry->len);
814         centry->ofs = 0;
815         centry->sequence_number = domain->sequence_number;
816         centry_put_ntstatus(centry, status);
817         centry_put_uint32(centry, centry->sequence_number);
818         return centry;
819 }
820
821 /*
822   finish a centry and write it to the tdb
823 */
824 static void centry_end(struct cache_entry *centry, const char *format, ...) PRINTF_ATTRIBUTE(2,3);
825 static void centry_end(struct cache_entry *centry, const char *format, ...)
826 {
827         va_list ap;
828         char *kstr;
829         TDB_DATA key, data;
830
831         if (opt_nocache) {
832                 return;
833         }
834
835         va_start(ap, format);
836         smb_xvasprintf(&kstr, format, ap);
837         va_end(ap);
838
839         key = string_tdb_data(kstr);
840         data.dptr = centry->data;
841         data.dsize = centry->ofs;
842
843         tdb_store(wcache->tdb, key, data, TDB_REPLACE);
844         free(kstr);
845 }
846
847 static void wcache_save_name_to_sid(struct winbindd_domain *domain, 
848                                     NTSTATUS status, const char *domain_name,
849                                     const char *name, const DOM_SID *sid, 
850                                     enum lsa_SidType type)
851 {
852         struct cache_entry *centry;
853         fstring uname;
854
855         centry = centry_start(domain, status);
856         if (!centry)
857                 return;
858         centry_put_uint32(centry, type);
859         centry_put_sid(centry, sid);
860         fstrcpy(uname, name);
861         strupper_m(uname);
862         centry_end(centry, "NS/%s/%s", domain_name, uname);
863         DEBUG(10,("wcache_save_name_to_sid: %s\\%s -> %s (%s)\n", domain_name,
864                   uname, sid_string_dbg(sid), nt_errstr(status)));
865         centry_free(centry);
866 }
867
868 static void wcache_save_sid_to_name(struct winbindd_domain *domain, NTSTATUS status, 
869                                     const DOM_SID *sid, const char *domain_name, const char *name, enum lsa_SidType type)
870 {
871         struct cache_entry *centry;
872         fstring sid_string;
873
874         centry = centry_start(domain, status);
875         if (!centry)
876                 return;
877
878         if (NT_STATUS_IS_OK(status)) {
879                 centry_put_uint32(centry, type);
880                 centry_put_string(centry, domain_name);
881                 centry_put_string(centry, name);
882         }
883
884         centry_end(centry, "SN/%s", sid_to_fstring(sid_string, sid));
885         DEBUG(10,("wcache_save_sid_to_name: %s -> %s (%s)\n", sid_string, 
886                   name, nt_errstr(status)));
887         centry_free(centry);
888 }
889
890
891 static void wcache_save_user(struct winbindd_domain *domain, NTSTATUS status, WINBIND_USERINFO *info)
892 {
893         struct cache_entry *centry;
894         fstring sid_string;
895
896         if (is_null_sid(&info->user_sid)) {
897                 return;
898         }
899
900         centry = centry_start(domain, status);
901         if (!centry)
902                 return;
903         centry_put_string(centry, info->acct_name);
904         centry_put_string(centry, info->full_name);
905         centry_put_string(centry, info->homedir);
906         centry_put_string(centry, info->shell);
907         centry_put_uint32(centry, info->primary_gid);
908         centry_put_sid(centry, &info->user_sid);
909         centry_put_sid(centry, &info->group_sid);
910         centry_end(centry, "U/%s", sid_to_fstring(sid_string,
911                                                   &info->user_sid));
912         DEBUG(10,("wcache_save_user: %s (acct_name %s)\n", sid_string, info->acct_name));
913         centry_free(centry);
914 }
915
916 static void wcache_save_lockout_policy(struct winbindd_domain *domain,
917                                        NTSTATUS status,
918                                        struct samr_DomInfo12 *lockout_policy)
919 {
920         struct cache_entry *centry;
921
922         centry = centry_start(domain, status);
923         if (!centry)
924                 return;
925
926         centry_put_nttime(centry, lockout_policy->lockout_duration);
927         centry_put_nttime(centry, lockout_policy->lockout_window);
928         centry_put_uint16(centry, lockout_policy->lockout_threshold);
929
930         centry_end(centry, "LOC_POL/%s", domain->name);
931
932         DEBUG(10,("wcache_save_lockout_policy: %s\n", domain->name));
933
934         centry_free(centry);
935 }
936
937 static void wcache_save_password_policy(struct winbindd_domain *domain,
938                                         NTSTATUS status,
939                                         struct samr_DomInfo1 *policy)
940 {
941         struct cache_entry *centry;
942
943         centry = centry_start(domain, status);
944         if (!centry)
945                 return;
946
947         centry_put_uint16(centry, policy->min_password_length);
948         centry_put_uint16(centry, policy->password_history_length);
949         centry_put_uint32(centry, policy->password_properties);
950         centry_put_nttime(centry, policy->max_password_age);
951         centry_put_nttime(centry, policy->min_password_age);
952
953         centry_end(centry, "PWD_POL/%s", domain->name);
954
955         DEBUG(10,("wcache_save_password_policy: %s\n", domain->name));
956
957         centry_free(centry);
958 }
959
960 NTSTATUS wcache_cached_creds_exist(struct winbindd_domain *domain, const DOM_SID *sid)
961 {
962         struct winbind_cache *cache = get_cache(domain);
963         TDB_DATA data;
964         fstring key_str, tmp;
965         uint32 rid;
966
967         if (!cache->tdb) {
968                 return NT_STATUS_INTERNAL_DB_ERROR;
969         }
970
971         if (is_null_sid(sid)) {
972                 return NT_STATUS_INVALID_SID;
973         }
974
975         if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
976                 return NT_STATUS_INVALID_SID;
977         }
978
979         fstr_sprintf(key_str, "CRED/%s", sid_to_fstring(tmp, sid));
980
981         data = tdb_fetch(cache->tdb, string_tdb_data(key_str));
982         if (!data.dptr) {
983                 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
984         }
985
986         SAFE_FREE(data.dptr);
987         return NT_STATUS_OK;
988 }
989
990 /* Lookup creds for a SID - copes with old (unsalted) creds as well
991    as new salted ones. */
992
993 NTSTATUS wcache_get_creds(struct winbindd_domain *domain, 
994                           TALLOC_CTX *mem_ctx, 
995                           const DOM_SID *sid,
996                           const uint8 **cached_nt_pass,
997                           const uint8 **cached_salt)
998 {
999         struct winbind_cache *cache = get_cache(domain);
1000         struct cache_entry *centry = NULL;
1001         NTSTATUS status;
1002         time_t t;
1003         uint32 rid;
1004         fstring tmp;
1005
1006         if (!cache->tdb) {
1007                 return NT_STATUS_INTERNAL_DB_ERROR;
1008         }
1009
1010         if (is_null_sid(sid)) {
1011                 return NT_STATUS_INVALID_SID;
1012         }
1013
1014         if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
1015                 return NT_STATUS_INVALID_SID;
1016         }
1017
1018         /* Try and get a salted cred first. If we can't
1019            fall back to an unsalted cred. */
1020
1021         centry = wcache_fetch(cache, domain, "CRED/%s",
1022                               sid_to_fstring(tmp, sid));
1023         if (!centry) {
1024                 DEBUG(10,("wcache_get_creds: entry for [CRED/%s] not found\n", 
1025                           sid_string_dbg(sid)));
1026                 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1027         }
1028
1029         t = centry_time(centry);
1030
1031         /* In the salted case this isn't actually the nt_hash itself,
1032            but the MD5 of the salt + nt_hash. Let the caller
1033            sort this out. It can tell as we only return the cached_salt
1034            if we are returning a salted cred. */
1035
1036         *cached_nt_pass = (const uint8 *)centry_hash16(centry, mem_ctx);
1037         if (*cached_nt_pass == NULL) {
1038                 fstring sidstr;
1039
1040                 sid_to_fstring(sidstr, sid);
1041
1042                 /* Bad (old) cred cache. Delete and pretend we
1043                    don't have it. */
1044                 DEBUG(0,("wcache_get_creds: bad entry for [CRED/%s] - deleting\n", 
1045                                 sidstr));
1046                 wcache_delete("CRED/%s", sidstr);
1047                 centry_free(centry);
1048                 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1049         }
1050
1051         /* We only have 17 bytes more data in the salted cred case. */
1052         if (centry->len - centry->ofs == 17) {
1053                 *cached_salt = (const uint8 *)centry_hash16(centry, mem_ctx);
1054         } else {
1055                 *cached_salt = NULL;
1056         }
1057
1058         dump_data_pw("cached_nt_pass", *cached_nt_pass, NT_HASH_LEN);
1059         if (*cached_salt) {
1060                 dump_data_pw("cached_salt", *cached_salt, NT_HASH_LEN);
1061         }
1062
1063         status = centry->status;
1064
1065         DEBUG(10,("wcache_get_creds: [Cached] - cached creds for user %s status: %s\n",
1066                   sid_string_dbg(sid), nt_errstr(status) ));
1067
1068         centry_free(centry);
1069         return status;
1070 }
1071
1072 /* Store creds for a SID - only writes out new salted ones. */
1073
1074 NTSTATUS wcache_save_creds(struct winbindd_domain *domain, 
1075                            TALLOC_CTX *mem_ctx, 
1076                            const DOM_SID *sid, 
1077                            const uint8 nt_pass[NT_HASH_LEN])
1078 {
1079         struct cache_entry *centry;
1080         fstring sid_string;
1081         uint32 rid;
1082         uint8 cred_salt[NT_HASH_LEN];
1083         uint8 salted_hash[NT_HASH_LEN];
1084
1085         if (is_null_sid(sid)) {
1086                 return NT_STATUS_INVALID_SID;
1087         }
1088
1089         if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
1090                 return NT_STATUS_INVALID_SID;
1091         }
1092
1093         centry = centry_start(domain, NT_STATUS_OK);
1094         if (!centry) {
1095                 return NT_STATUS_INTERNAL_DB_ERROR;
1096         }
1097
1098         dump_data_pw("nt_pass", nt_pass, NT_HASH_LEN);
1099
1100         centry_put_time(centry, time(NULL));
1101
1102         /* Create a salt and then salt the hash. */
1103         generate_random_buffer(cred_salt, NT_HASH_LEN);
1104         E_md5hash(cred_salt, nt_pass, salted_hash);
1105
1106         centry_put_hash16(centry, salted_hash);
1107         centry_put_hash16(centry, cred_salt);
1108         centry_end(centry, "CRED/%s", sid_to_fstring(sid_string, sid));
1109
1110         DEBUG(10,("wcache_save_creds: %s\n", sid_string));
1111
1112         centry_free(centry);
1113
1114         return NT_STATUS_OK;
1115 }
1116
1117
1118 /* Query display info. This is the basic user list fn */
1119 static NTSTATUS query_user_list(struct winbindd_domain *domain,
1120                                 TALLOC_CTX *mem_ctx,
1121                                 uint32 *num_entries, 
1122                                 WINBIND_USERINFO **info)
1123 {
1124         struct winbind_cache *cache = get_cache(domain);
1125         struct cache_entry *centry = NULL;
1126         NTSTATUS status;
1127         unsigned int i, retry;
1128
1129         if (!cache->tdb)
1130                 goto do_query;
1131
1132         centry = wcache_fetch(cache, domain, "UL/%s", domain->name);
1133         if (!centry)
1134                 goto do_query;
1135
1136         *num_entries = centry_uint32(centry);
1137         
1138         if (*num_entries == 0)
1139                 goto do_cached;
1140
1141         (*info) = TALLOC_ARRAY(mem_ctx, WINBIND_USERINFO, *num_entries);
1142         if (! (*info)) {
1143                 smb_panic_fn("query_user_list out of memory");
1144         }
1145         for (i=0; i<(*num_entries); i++) {
1146                 (*info)[i].acct_name = centry_string(centry, mem_ctx);
1147                 (*info)[i].full_name = centry_string(centry, mem_ctx);
1148                 (*info)[i].homedir = centry_string(centry, mem_ctx);
1149                 (*info)[i].shell = centry_string(centry, mem_ctx);
1150                 centry_sid(centry, mem_ctx, &(*info)[i].user_sid);
1151                 centry_sid(centry, mem_ctx, &(*info)[i].group_sid);
1152         }
1153
1154 do_cached:      
1155         status = centry->status;
1156
1157         DEBUG(10,("query_user_list: [Cached] - cached list for domain %s status: %s\n",
1158                 domain->name, nt_errstr(status) ));
1159
1160         centry_free(centry);
1161         return status;
1162
1163 do_query:
1164         *num_entries = 0;
1165         *info = NULL;
1166
1167         /* Return status value returned by seq number check */
1168
1169         if (!NT_STATUS_IS_OK(domain->last_status))
1170                 return domain->last_status;
1171
1172         /* Put the query_user_list() in a retry loop.  There appears to be
1173          * some bug either with Windows 2000 or Samba's handling of large
1174          * rpc replies.  This manifests itself as sudden disconnection
1175          * at a random point in the enumeration of a large (60k) user list.
1176          * The retry loop simply tries the operation again. )-:  It's not
1177          * pretty but an acceptable workaround until we work out what the
1178          * real problem is. */
1179
1180         retry = 0;
1181         do {
1182
1183                 DEBUG(10,("query_user_list: [Cached] - doing backend query for list for domain %s\n",
1184                         domain->name ));
1185
1186                 status = domain->backend->query_user_list(domain, mem_ctx, num_entries, info);
1187                 if (!NT_STATUS_IS_OK(status)) {
1188                         DEBUG(3, ("query_user_list: returned 0x%08x, "
1189                                   "retrying\n", NT_STATUS_V(status)));
1190                 }
1191                 if (NT_STATUS_EQUAL(status, NT_STATUS_UNSUCCESSFUL)) {
1192                         DEBUG(3, ("query_user_list: flushing "
1193                                   "connection cache\n"));
1194                         invalidate_cm_connection(&domain->conn);
1195                 }
1196
1197         } while (NT_STATUS_V(status) == NT_STATUS_V(NT_STATUS_UNSUCCESSFUL) && 
1198                  (retry++ < 5));
1199
1200         /* and save it */
1201         refresh_sequence_number(domain, false);
1202         centry = centry_start(domain, status);
1203         if (!centry)
1204                 goto skip_save;
1205         centry_put_uint32(centry, *num_entries);
1206         for (i=0; i<(*num_entries); i++) {
1207                 centry_put_string(centry, (*info)[i].acct_name);
1208                 centry_put_string(centry, (*info)[i].full_name);
1209                 centry_put_string(centry, (*info)[i].homedir);
1210                 centry_put_string(centry, (*info)[i].shell);
1211                 centry_put_sid(centry, &(*info)[i].user_sid);
1212                 centry_put_sid(centry, &(*info)[i].group_sid);
1213                 if (domain->backend && domain->backend->consistent) {
1214                         /* when the backend is consistent we can pre-prime some mappings */
1215                         wcache_save_name_to_sid(domain, NT_STATUS_OK, 
1216                                                 domain->name,
1217                                                 (*info)[i].acct_name, 
1218                                                 &(*info)[i].user_sid,
1219                                                 SID_NAME_USER);
1220                         wcache_save_sid_to_name(domain, NT_STATUS_OK, 
1221                                                 &(*info)[i].user_sid,
1222                                                 domain->name,
1223                                                 (*info)[i].acct_name, 
1224                                                 SID_NAME_USER);
1225                         wcache_save_user(domain, NT_STATUS_OK, &(*info)[i]);
1226                 }
1227         }       
1228         centry_end(centry, "UL/%s", domain->name);
1229         centry_free(centry);
1230
1231 skip_save:
1232         return status;
1233 }
1234
1235 /* list all domain groups */
1236 static NTSTATUS enum_dom_groups(struct winbindd_domain *domain,
1237                                 TALLOC_CTX *mem_ctx,
1238                                 uint32 *num_entries, 
1239                                 struct acct_info **info)
1240 {
1241         struct winbind_cache *cache = get_cache(domain);
1242         struct cache_entry *centry = NULL;
1243         NTSTATUS status;
1244         unsigned int i;
1245
1246         if (!cache->tdb)
1247                 goto do_query;
1248
1249         centry = wcache_fetch(cache, domain, "GL/%s/domain", domain->name);
1250         if (!centry)
1251                 goto do_query;
1252
1253         *num_entries = centry_uint32(centry);
1254         
1255         if (*num_entries == 0)
1256                 goto do_cached;
1257
1258         (*info) = TALLOC_ARRAY(mem_ctx, struct acct_info, *num_entries);
1259         if (! (*info)) {
1260                 smb_panic_fn("enum_dom_groups out of memory");
1261         }
1262         for (i=0; i<(*num_entries); i++) {
1263                 fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx));
1264                 fstrcpy((*info)[i].acct_desc, centry_string(centry, mem_ctx));
1265                 (*info)[i].rid = centry_uint32(centry);
1266         }
1267
1268 do_cached:      
1269         status = centry->status;
1270
1271         DEBUG(10,("enum_dom_groups: [Cached] - cached list for domain %s status: %s\n",
1272                 domain->name, nt_errstr(status) ));
1273
1274         centry_free(centry);
1275         return status;
1276
1277 do_query:
1278         *num_entries = 0;
1279         *info = NULL;
1280
1281         /* Return status value returned by seq number check */
1282
1283         if (!NT_STATUS_IS_OK(domain->last_status))
1284                 return domain->last_status;
1285
1286         DEBUG(10,("enum_dom_groups: [Cached] - doing backend query for list for domain %s\n",
1287                 domain->name ));
1288
1289         status = domain->backend->enum_dom_groups(domain, mem_ctx, num_entries, info);
1290
1291         /* and save it */
1292         refresh_sequence_number(domain, false);
1293         centry = centry_start(domain, status);
1294         if (!centry)
1295                 goto skip_save;
1296         centry_put_uint32(centry, *num_entries);
1297         for (i=0; i<(*num_entries); i++) {
1298                 centry_put_string(centry, (*info)[i].acct_name);
1299                 centry_put_string(centry, (*info)[i].acct_desc);
1300                 centry_put_uint32(centry, (*info)[i].rid);
1301         }       
1302         centry_end(centry, "GL/%s/domain", domain->name);
1303         centry_free(centry);
1304
1305 skip_save:
1306         return status;
1307 }
1308
1309 /* list all domain groups */
1310 static NTSTATUS enum_local_groups(struct winbindd_domain *domain,
1311                                 TALLOC_CTX *mem_ctx,
1312                                 uint32 *num_entries, 
1313                                 struct acct_info **info)
1314 {
1315         struct winbind_cache *cache = get_cache(domain);
1316         struct cache_entry *centry = NULL;
1317         NTSTATUS status;
1318         unsigned int i;
1319
1320         if (!cache->tdb)
1321                 goto do_query;
1322
1323         centry = wcache_fetch(cache, domain, "GL/%s/local", domain->name);
1324         if (!centry)
1325                 goto do_query;
1326
1327         *num_entries = centry_uint32(centry);
1328         
1329         if (*num_entries == 0)
1330                 goto do_cached;
1331
1332         (*info) = TALLOC_ARRAY(mem_ctx, struct acct_info, *num_entries);
1333         if (! (*info)) {
1334                 smb_panic_fn("enum_dom_groups out of memory");
1335         }
1336         for (i=0; i<(*num_entries); i++) {
1337                 fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx));
1338                 fstrcpy((*info)[i].acct_desc, centry_string(centry, mem_ctx));
1339                 (*info)[i].rid = centry_uint32(centry);
1340         }
1341
1342 do_cached:      
1343
1344         /* If we are returning cached data and the domain controller
1345            is down then we don't know whether the data is up to date
1346            or not.  Return NT_STATUS_MORE_PROCESSING_REQUIRED to
1347            indicate this. */
1348
1349         if (wcache_server_down(domain)) {
1350                 DEBUG(10, ("enum_local_groups: returning cached user list and server was down\n"));
1351                 status = NT_STATUS_MORE_PROCESSING_REQUIRED;
1352         } else
1353                 status = centry->status;
1354
1355         DEBUG(10,("enum_local_groups: [Cached] - cached list for domain %s status: %s\n",
1356                 domain->name, nt_errstr(status) ));
1357
1358         centry_free(centry);
1359         return status;
1360
1361 do_query:
1362         *num_entries = 0;
1363         *info = NULL;
1364
1365         /* Return status value returned by seq number check */
1366
1367         if (!NT_STATUS_IS_OK(domain->last_status))
1368                 return domain->last_status;
1369
1370         DEBUG(10,("enum_local_groups: [Cached] - doing backend query for list for domain %s\n",
1371                 domain->name ));
1372
1373         status = domain->backend->enum_local_groups(domain, mem_ctx, num_entries, info);
1374
1375         /* and save it */
1376         refresh_sequence_number(domain, false);
1377         centry = centry_start(domain, status);
1378         if (!centry)
1379                 goto skip_save;
1380         centry_put_uint32(centry, *num_entries);
1381         for (i=0; i<(*num_entries); i++) {
1382                 centry_put_string(centry, (*info)[i].acct_name);
1383                 centry_put_string(centry, (*info)[i].acct_desc);
1384                 centry_put_uint32(centry, (*info)[i].rid);
1385         }
1386         centry_end(centry, "GL/%s/local", domain->name);
1387         centry_free(centry);
1388
1389 skip_save:
1390         return status;
1391 }
1392
1393 /* convert a single name to a sid in a domain */
1394 static NTSTATUS name_to_sid(struct winbindd_domain *domain,
1395                             TALLOC_CTX *mem_ctx,
1396                             enum winbindd_cmd orig_cmd,
1397                             const char *domain_name,
1398                             const char *name,
1399                             DOM_SID *sid,
1400                             enum lsa_SidType *type)
1401 {
1402         struct winbind_cache *cache = get_cache(domain);
1403         struct cache_entry *centry = NULL;
1404         NTSTATUS status;
1405         fstring uname;
1406
1407         if (!cache->tdb)
1408                 goto do_query;
1409
1410         fstrcpy(uname, name);
1411         strupper_m(uname);
1412         centry = wcache_fetch(cache, domain, "NS/%s/%s", domain_name, uname);
1413         if (!centry)
1414                 goto do_query;
1415
1416         status = centry->status;
1417         if (NT_STATUS_IS_OK(status)) {
1418                 *type = (enum lsa_SidType)centry_uint32(centry);
1419                 centry_sid(centry, mem_ctx, sid);
1420         }
1421
1422         DEBUG(10,("name_to_sid: [Cached] - cached name for domain %s status: %s\n",
1423                 domain->name, nt_errstr(status) ));
1424
1425         centry_free(centry);
1426         return status;
1427
1428 do_query:
1429         ZERO_STRUCTP(sid);
1430
1431         /* If the seq number check indicated that there is a problem
1432          * with this DC, then return that status... except for
1433          * access_denied.  This is special because the dc may be in
1434          * "restrict anonymous = 1" mode, in which case it will deny
1435          * most unauthenticated operations, but *will* allow the LSA
1436          * name-to-sid that we try as a fallback. */
1437
1438         if (!(NT_STATUS_IS_OK(domain->last_status)
1439               || NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)))
1440                 return domain->last_status;
1441
1442         DEBUG(10,("name_to_sid: [Cached] - doing backend query for name for domain %s\n",
1443                 domain->name ));
1444
1445         status = domain->backend->name_to_sid(domain, mem_ctx, orig_cmd, 
1446                                               domain_name, name, sid, type);
1447
1448         /* and save it */
1449         refresh_sequence_number(domain, false);
1450
1451         if (domain->online &&
1452             (NT_STATUS_IS_OK(status) || NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED))) {
1453                 wcache_save_name_to_sid(domain, status, domain_name, name, sid, *type);
1454
1455                 /* Only save the reverse mapping if this was not a UPN */
1456                 if (!strchr(name, '@')) {
1457                         strupper_m(CONST_DISCARD(char *,domain_name));
1458                         strlower_m(CONST_DISCARD(char *,name));
1459                         wcache_save_sid_to_name(domain, status, sid, domain_name, name, *type);
1460                 }
1461         }
1462         
1463         return status;
1464 }
1465
1466 /* convert a sid to a user or group name. The sid is guaranteed to be in the domain
1467    given */
1468 static NTSTATUS sid_to_name(struct winbindd_domain *domain,
1469                             TALLOC_CTX *mem_ctx,
1470                             const DOM_SID *sid,
1471                             char **domain_name,
1472                             char **name,
1473                             enum lsa_SidType *type)
1474 {
1475         struct winbind_cache *cache = get_cache(domain);
1476         struct cache_entry *centry = NULL;
1477         NTSTATUS status;
1478         fstring sid_string;
1479
1480         if (!cache->tdb)
1481                 goto do_query;
1482
1483         centry = wcache_fetch(cache, domain, "SN/%s",
1484                               sid_to_fstring(sid_string, sid));
1485         if (!centry)
1486                 goto do_query;
1487
1488         status = centry->status;
1489         if (NT_STATUS_IS_OK(status)) {
1490                 *type = (enum lsa_SidType)centry_uint32(centry);
1491                 *domain_name = centry_string(centry, mem_ctx);
1492                 *name = centry_string(centry, mem_ctx);
1493         }
1494
1495         DEBUG(10,("sid_to_name: [Cached] - cached name for domain %s status: %s\n",
1496                 domain->name, nt_errstr(status) ));
1497
1498         centry_free(centry);
1499         return status;
1500
1501 do_query:
1502         *name = NULL;
1503         *domain_name = NULL;
1504
1505         /* If the seq number check indicated that there is a problem
1506          * with this DC, then return that status... except for
1507          * access_denied.  This is special because the dc may be in
1508          * "restrict anonymous = 1" mode, in which case it will deny
1509          * most unauthenticated operations, but *will* allow the LSA
1510          * sid-to-name that we try as a fallback. */
1511
1512         if (!(NT_STATUS_IS_OK(domain->last_status)
1513               || NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)))
1514                 return domain->last_status;
1515
1516         DEBUG(10,("sid_to_name: [Cached] - doing backend query for name for domain %s\n",
1517                 domain->name ));
1518
1519         status = domain->backend->sid_to_name(domain, mem_ctx, sid, domain_name, name, type);
1520
1521         /* and save it */
1522         refresh_sequence_number(domain, false);
1523         wcache_save_sid_to_name(domain, status, sid, *domain_name, *name, *type);
1524
1525         /* We can't save the name to sid mapping here, as with sid history a
1526          * later name2sid would give the wrong sid. */
1527
1528         return status;
1529 }
1530
1531 static NTSTATUS rids_to_names(struct winbindd_domain *domain,
1532                               TALLOC_CTX *mem_ctx,
1533                               const DOM_SID *domain_sid,
1534                               uint32 *rids,
1535                               size_t num_rids,
1536                               char **domain_name,
1537                               char ***names,
1538                               enum lsa_SidType **types)
1539 {
1540         struct winbind_cache *cache = get_cache(domain);
1541         size_t i;
1542         NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
1543         bool have_mapped;
1544         bool have_unmapped;
1545
1546         *domain_name = NULL;
1547         *names = NULL;
1548         *types = NULL;
1549
1550         if (!cache->tdb) {
1551                 goto do_query;
1552         }
1553
1554         if (num_rids == 0) {
1555                 return NT_STATUS_OK;
1556         }
1557
1558         *names = TALLOC_ARRAY(mem_ctx, char *, num_rids);
1559         *types = TALLOC_ARRAY(mem_ctx, enum lsa_SidType, num_rids);
1560
1561         if ((*names == NULL) || (*types == NULL)) {
1562                 result = NT_STATUS_NO_MEMORY;
1563                 goto error;
1564         }
1565
1566         have_mapped = have_unmapped = false;
1567
1568         for (i=0; i<num_rids; i++) {
1569                 DOM_SID sid;
1570                 struct cache_entry *centry;
1571                 fstring tmp;
1572
1573                 if (!sid_compose(&sid, domain_sid, rids[i])) {
1574                         result = NT_STATUS_INTERNAL_ERROR;
1575                         goto error;
1576                 }
1577
1578                 centry = wcache_fetch(cache, domain, "SN/%s",
1579                                       sid_to_fstring(tmp, &sid));
1580                 if (!centry) {
1581                         goto do_query;
1582                 }
1583
1584                 (*types)[i] = SID_NAME_UNKNOWN;
1585                 (*names)[i] = talloc_strdup(*names, "");
1586
1587                 if (NT_STATUS_IS_OK(centry->status)) {
1588                         char *dom;
1589                         have_mapped = true;
1590                         (*types)[i] = (enum lsa_SidType)centry_uint32(centry);
1591
1592                         dom = centry_string(centry, mem_ctx);
1593                         if (*domain_name == NULL) {
1594                                 *domain_name = dom;
1595                         } else {
1596                                 talloc_free(dom);
1597                         }
1598
1599                         (*names)[i] = centry_string(centry, *names);
1600
1601                 } else if (NT_STATUS_EQUAL(centry->status, NT_STATUS_NONE_MAPPED)) {
1602                         have_unmapped = true;
1603
1604                 } else {
1605                         /* something's definitely wrong */
1606                         result = centry->status;
1607                         goto error;
1608                 }
1609
1610                 centry_free(centry);
1611         }
1612
1613         if (!have_mapped) {
1614                 return NT_STATUS_NONE_MAPPED;
1615         }
1616         if (!have_unmapped) {
1617                 return NT_STATUS_OK;
1618         }
1619         return STATUS_SOME_UNMAPPED;
1620
1621  do_query:
1622
1623         TALLOC_FREE(*names);
1624         TALLOC_FREE(*types);
1625
1626         result = domain->backend->rids_to_names(domain, mem_ctx, domain_sid,
1627                                                 rids, num_rids, domain_name,
1628                                                 names, types);
1629
1630         /*
1631           None of the queried rids has been found so save all negative entries
1632         */
1633         if (NT_STATUS_EQUAL(result, NT_STATUS_NONE_MAPPED)) {
1634                 for (i = 0; i < num_rids; i++) {
1635                         DOM_SID sid;
1636                         const char *name = "";
1637                         const enum lsa_SidType type = SID_NAME_UNKNOWN;
1638                         NTSTATUS status = NT_STATUS_NONE_MAPPED;
1639                         
1640                         if (!sid_compose(&sid, domain_sid, rids[i])) {
1641                                 return NT_STATUS_INTERNAL_ERROR;
1642                         }
1643
1644                         wcache_save_sid_to_name(domain, status, &sid, *domain_name,
1645                                                 name, type);
1646                 }
1647
1648                 return result;
1649         }
1650
1651         /*
1652           Some or all of the queried rids have been found.
1653         */
1654         if (!NT_STATUS_IS_OK(result) &&
1655             !NT_STATUS_EQUAL(result, STATUS_SOME_UNMAPPED)) {
1656                 return result;
1657         }
1658
1659         refresh_sequence_number(domain, false);
1660
1661         for (i=0; i<num_rids; i++) {
1662                 DOM_SID sid;
1663                 NTSTATUS status;
1664
1665                 if (!sid_compose(&sid, domain_sid, rids[i])) {
1666                         result = NT_STATUS_INTERNAL_ERROR;
1667                         goto error;
1668                 }
1669
1670                 status = (*types)[i] == SID_NAME_UNKNOWN ?
1671                         NT_STATUS_NONE_MAPPED : NT_STATUS_OK;
1672
1673                 wcache_save_sid_to_name(domain, status, &sid, *domain_name,
1674                                         (*names)[i], (*types)[i]);
1675         }
1676
1677         return result;
1678
1679  error:
1680         
1681         TALLOC_FREE(*names);
1682         TALLOC_FREE(*types);
1683         return result;
1684 }
1685
1686 /* Lookup user information from a rid */
1687 static NTSTATUS query_user(struct winbindd_domain *domain, 
1688                            TALLOC_CTX *mem_ctx, 
1689                            const DOM_SID *user_sid, 
1690                            WINBIND_USERINFO *info)
1691 {
1692         struct winbind_cache *cache = get_cache(domain);
1693         struct cache_entry *centry = NULL;
1694         NTSTATUS status;
1695         fstring tmp;
1696
1697         if (!cache->tdb)
1698                 goto do_query;
1699
1700         centry = wcache_fetch(cache, domain, "U/%s",
1701                               sid_to_fstring(tmp, user_sid));
1702         
1703         /* If we have an access denied cache entry and a cached info3 in the
1704            samlogon cache then do a query.  This will force the rpc back end
1705            to return the info3 data. */
1706
1707         if (NT_STATUS_V(domain->last_status) == NT_STATUS_V(NT_STATUS_ACCESS_DENIED) &&
1708             netsamlogon_cache_have(user_sid)) {
1709                 DEBUG(10, ("query_user: cached access denied and have cached info3\n"));
1710                 domain->last_status = NT_STATUS_OK;
1711                 centry_free(centry);
1712                 goto do_query;
1713         }
1714         
1715         if (!centry)
1716                 goto do_query;
1717         
1718         /* if status is not ok then this is a negative hit
1719            and the rest of the data doesn't matter */
1720         status = centry->status;
1721         if (NT_STATUS_IS_OK(status)) {
1722                 info->acct_name = centry_string(centry, mem_ctx);
1723                 info->full_name = centry_string(centry, mem_ctx);
1724                 info->homedir = centry_string(centry, mem_ctx);
1725                 info->shell = centry_string(centry, mem_ctx);
1726                 info->primary_gid = centry_uint32(centry);
1727                 centry_sid(centry, mem_ctx, &info->user_sid);
1728                 centry_sid(centry, mem_ctx, &info->group_sid);
1729         }
1730
1731         DEBUG(10,("query_user: [Cached] - cached info for domain %s status: %s\n",
1732                 domain->name, nt_errstr(status) ));
1733
1734         centry_free(centry);
1735         return status;
1736
1737 do_query:
1738         ZERO_STRUCTP(info);
1739
1740         /* Return status value returned by seq number check */
1741
1742         if (!NT_STATUS_IS_OK(domain->last_status))
1743                 return domain->last_status;
1744         
1745         DEBUG(10,("query_user: [Cached] - doing backend query for info for domain %s\n",
1746                 domain->name ));
1747
1748         status = domain->backend->query_user(domain, mem_ctx, user_sid, info);
1749
1750         /* and save it */
1751         refresh_sequence_number(domain, false);
1752         wcache_save_user(domain, status, info);
1753
1754         return status;
1755 }
1756
1757
1758 /* Lookup groups a user is a member of. */
1759 static NTSTATUS lookup_usergroups(struct winbindd_domain *domain,
1760                                   TALLOC_CTX *mem_ctx,
1761                                   const DOM_SID *user_sid, 
1762                                   uint32 *num_groups, DOM_SID **user_gids)
1763 {
1764         struct winbind_cache *cache = get_cache(domain);
1765         struct cache_entry *centry = NULL;
1766         NTSTATUS status;
1767         unsigned int i;
1768         fstring sid_string;
1769
1770         if (!cache->tdb)
1771                 goto do_query;
1772
1773         centry = wcache_fetch(cache, domain, "UG/%s",
1774                               sid_to_fstring(sid_string, user_sid));
1775         
1776         /* If we have an access denied cache entry and a cached info3 in the
1777            samlogon cache then do a query.  This will force the rpc back end
1778            to return the info3 data. */
1779
1780         if (NT_STATUS_V(domain->last_status) == NT_STATUS_V(NT_STATUS_ACCESS_DENIED) &&
1781             netsamlogon_cache_have(user_sid)) {
1782                 DEBUG(10, ("lookup_usergroups: cached access denied and have cached info3\n"));
1783                 domain->last_status = NT_STATUS_OK;
1784                 centry_free(centry);
1785                 goto do_query;
1786         }
1787         
1788         if (!centry)
1789                 goto do_query;
1790
1791         *num_groups = centry_uint32(centry);
1792         
1793         if (*num_groups == 0)
1794                 goto do_cached;
1795
1796         (*user_gids) = TALLOC_ARRAY(mem_ctx, DOM_SID, *num_groups);
1797         if (! (*user_gids)) {
1798                 smb_panic_fn("lookup_usergroups out of memory");
1799         }
1800         for (i=0; i<(*num_groups); i++) {
1801                 centry_sid(centry, mem_ctx, &(*user_gids)[i]);
1802         }
1803
1804 do_cached:      
1805         status = centry->status;
1806
1807         DEBUG(10,("lookup_usergroups: [Cached] - cached info for domain %s status: %s\n",
1808                 domain->name, nt_errstr(status) ));
1809
1810         centry_free(centry);
1811         return status;
1812
1813 do_query:
1814         (*num_groups) = 0;
1815         (*user_gids) = NULL;
1816
1817         /* Return status value returned by seq number check */
1818
1819         if (!NT_STATUS_IS_OK(domain->last_status))
1820                 return domain->last_status;
1821
1822         DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info for domain %s\n",
1823                 domain->name ));
1824
1825         status = domain->backend->lookup_usergroups(domain, mem_ctx, user_sid, num_groups, user_gids);
1826
1827         if ( NT_STATUS_EQUAL(status, NT_STATUS_SYNCHRONIZATION_REQUIRED) )
1828                 goto skip_save;
1829         
1830         /* and save it */
1831         refresh_sequence_number(domain, false);
1832         centry = centry_start(domain, status);
1833         if (!centry)
1834                 goto skip_save;
1835
1836         centry_put_uint32(centry, *num_groups);
1837         for (i=0; i<(*num_groups); i++) {
1838                 centry_put_sid(centry, &(*user_gids)[i]);
1839         }       
1840
1841         centry_end(centry, "UG/%s", sid_to_fstring(sid_string, user_sid));
1842         centry_free(centry);
1843
1844 skip_save:
1845         return status;
1846 }
1847
1848 static NTSTATUS lookup_useraliases(struct winbindd_domain *domain,
1849                                    TALLOC_CTX *mem_ctx,
1850                                    uint32 num_sids, const DOM_SID *sids,
1851                                    uint32 *num_aliases, uint32 **alias_rids)
1852 {
1853         struct winbind_cache *cache = get_cache(domain);
1854         struct cache_entry *centry = NULL;
1855         NTSTATUS status;
1856         char *sidlist = talloc_strdup(mem_ctx, "");
1857         int i;
1858
1859         if (!cache->tdb)
1860                 goto do_query;
1861
1862         if (num_sids == 0) {
1863                 *num_aliases = 0;
1864                 *alias_rids = NULL;
1865                 return NT_STATUS_OK;
1866         }
1867
1868         /* We need to cache indexed by the whole list of SIDs, the aliases
1869          * resulting might come from any of the SIDs. */
1870
1871         for (i=0; i<num_sids; i++) {
1872                 fstring tmp;
1873                 sidlist = talloc_asprintf(mem_ctx, "%s/%s", sidlist,
1874                                           sid_to_fstring(tmp, &sids[i]));
1875                 if (sidlist == NULL)
1876                         return NT_STATUS_NO_MEMORY;
1877         }
1878
1879         centry = wcache_fetch(cache, domain, "UA%s", sidlist);
1880
1881         if (!centry)
1882                 goto do_query;
1883
1884         *num_aliases = centry_uint32(centry);
1885         *alias_rids = NULL;
1886
1887         if (*num_aliases) {
1888                 (*alias_rids) = TALLOC_ARRAY(mem_ctx, uint32, *num_aliases);
1889
1890                 if ((*alias_rids) == NULL) {
1891                         centry_free(centry);
1892                         return NT_STATUS_NO_MEMORY;
1893                 }
1894         } else {
1895                 (*alias_rids) = NULL;
1896         }
1897
1898         for (i=0; i<(*num_aliases); i++)
1899                 (*alias_rids)[i] = centry_uint32(centry);
1900
1901         status = centry->status;
1902
1903         DEBUG(10,("lookup_useraliases: [Cached] - cached info for domain: %s "
1904                   "status %s\n", domain->name, nt_errstr(status)));
1905
1906         centry_free(centry);
1907         return status;
1908
1909  do_query:
1910         (*num_aliases) = 0;
1911         (*alias_rids) = NULL;
1912
1913         if (!NT_STATUS_IS_OK(domain->last_status))
1914                 return domain->last_status;
1915
1916         DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info "
1917                   "for domain %s\n", domain->name ));
1918
1919         status = domain->backend->lookup_useraliases(domain, mem_ctx,
1920                                                      num_sids, sids,
1921                                                      num_aliases, alias_rids);
1922
1923         /* and save it */
1924         refresh_sequence_number(domain, false);
1925         centry = centry_start(domain, status);
1926         if (!centry)
1927                 goto skip_save;
1928         centry_put_uint32(centry, *num_aliases);
1929         for (i=0; i<(*num_aliases); i++)
1930                 centry_put_uint32(centry, (*alias_rids)[i]);
1931         centry_end(centry, "UA%s", sidlist);
1932         centry_free(centry);
1933
1934  skip_save:
1935         return status;
1936 }
1937
1938
1939 static NTSTATUS lookup_groupmem(struct winbindd_domain *domain,
1940                                 TALLOC_CTX *mem_ctx,
1941                                 const DOM_SID *group_sid, uint32 *num_names, 
1942                                 DOM_SID **sid_mem, char ***names, 
1943                                 uint32 **name_types)
1944 {
1945         struct winbind_cache *cache = get_cache(domain);
1946         struct cache_entry *centry = NULL;
1947         NTSTATUS status;
1948         unsigned int i;
1949         fstring sid_string;
1950
1951         if (!cache->tdb)
1952                 goto do_query;
1953
1954         centry = wcache_fetch(cache, domain, "GM/%s",
1955                               sid_to_fstring(sid_string, group_sid));
1956         if (!centry)
1957                 goto do_query;
1958
1959         *num_names = centry_uint32(centry);
1960         
1961         if (*num_names == 0)
1962                 goto do_cached;
1963
1964         (*sid_mem) = TALLOC_ARRAY(mem_ctx, DOM_SID, *num_names);
1965         (*names) = TALLOC_ARRAY(mem_ctx, char *, *num_names);
1966         (*name_types) = TALLOC_ARRAY(mem_ctx, uint32, *num_names);
1967
1968         if (! (*sid_mem) || ! (*names) || ! (*name_types)) {
1969                 smb_panic_fn("lookup_groupmem out of memory");
1970         }
1971
1972         for (i=0; i<(*num_names); i++) {
1973                 centry_sid(centry, mem_ctx, &(*sid_mem)[i]);
1974                 (*names)[i] = centry_string(centry, mem_ctx);
1975                 (*name_types)[i] = centry_uint32(centry);
1976         }
1977
1978 do_cached:      
1979         status = centry->status;
1980
1981         DEBUG(10,("lookup_groupmem: [Cached] - cached info for domain %s status: %s\n",
1982                 domain->name, nt_errstr(status)));
1983
1984         centry_free(centry);
1985         return status;
1986
1987 do_query:
1988         (*num_names) = 0;
1989         (*sid_mem) = NULL;
1990         (*names) = NULL;
1991         (*name_types) = NULL;
1992         
1993         /* Return status value returned by seq number check */
1994
1995         if (!NT_STATUS_IS_OK(domain->last_status))
1996                 return domain->last_status;
1997
1998         DEBUG(10,("lookup_groupmem: [Cached] - doing backend query for info for domain %s\n",
1999                 domain->name ));
2000
2001         status = domain->backend->lookup_groupmem(domain, mem_ctx, group_sid, num_names, 
2002                                                   sid_mem, names, name_types);
2003
2004         /* and save it */
2005         refresh_sequence_number(domain, false);
2006         centry = centry_start(domain, status);
2007         if (!centry)
2008                 goto skip_save;
2009         centry_put_uint32(centry, *num_names);
2010         for (i=0; i<(*num_names); i++) {
2011                 centry_put_sid(centry, &(*sid_mem)[i]);
2012                 centry_put_string(centry, (*names)[i]);
2013                 centry_put_uint32(centry, (*name_types)[i]);
2014         }       
2015         centry_end(centry, "GM/%s", sid_to_fstring(sid_string, group_sid));
2016         centry_free(centry);
2017
2018 skip_save:
2019         return status;
2020 }
2021
2022 /* find the sequence number for a domain */
2023 static NTSTATUS sequence_number(struct winbindd_domain *domain, uint32 *seq)
2024 {
2025         refresh_sequence_number(domain, false);
2026
2027         *seq = domain->sequence_number;
2028
2029         return NT_STATUS_OK;
2030 }
2031
2032 /* enumerate trusted domains 
2033  * (we need to have the list of trustdoms in the cache when we go offline) -
2034  * Guenther */
2035 static NTSTATUS trusted_domains(struct winbindd_domain *domain,
2036                                 TALLOC_CTX *mem_ctx,
2037                                 uint32 *num_domains,
2038                                 char ***names,
2039                                 char ***alt_names,
2040                                 DOM_SID **dom_sids)
2041 {
2042         struct winbind_cache *cache = get_cache(domain);
2043         struct cache_entry *centry = NULL;
2044         NTSTATUS status;
2045         int i;
2046  
2047         if (!cache->tdb)
2048                 goto do_query;
2049  
2050         centry = wcache_fetch(cache, domain, "TRUSTDOMS/%s", domain->name);
2051         
2052         if (!centry) {
2053                 goto do_query;
2054         }
2055  
2056         *num_domains = centry_uint32(centry);
2057         
2058         if (*num_domains) {
2059                 (*names)        = TALLOC_ARRAY(mem_ctx, char *, *num_domains);
2060                 (*alt_names)    = TALLOC_ARRAY(mem_ctx, char *, *num_domains);
2061                 (*dom_sids)     = TALLOC_ARRAY(mem_ctx, DOM_SID, *num_domains);
2062  
2063                 if (! (*dom_sids) || ! (*names) || ! (*alt_names)) {
2064                         smb_panic_fn("trusted_domains out of memory");
2065                 }
2066         } else {
2067                 (*names) = NULL;
2068                 (*alt_names) = NULL;
2069                 (*dom_sids) = NULL;
2070         }
2071  
2072         for (i=0; i<(*num_domains); i++) {
2073                 (*names)[i] = centry_string(centry, mem_ctx);
2074                 (*alt_names)[i] = centry_string(centry, mem_ctx);
2075                 centry_sid(centry, mem_ctx, &(*dom_sids)[i]);
2076         }
2077
2078         status = centry->status;
2079  
2080         DEBUG(10,("trusted_domains: [Cached] - cached info for domain %s (%d trusts) status: %s\n",
2081                 domain->name, *num_domains, nt_errstr(status) ));
2082  
2083         centry_free(centry);
2084         return status;
2085  
2086 do_query:
2087         (*num_domains) = 0;
2088         (*dom_sids) = NULL;
2089         (*names) = NULL;
2090         (*alt_names) = NULL;
2091  
2092         /* Return status value returned by seq number check */
2093
2094         if (!NT_STATUS_IS_OK(domain->last_status))
2095                 return domain->last_status;
2096         
2097         DEBUG(10,("trusted_domains: [Cached] - doing backend query for info for domain %s\n",
2098                 domain->name ));
2099  
2100         status = domain->backend->trusted_domains(domain, mem_ctx, num_domains,
2101                                                 names, alt_names, dom_sids);
2102
2103         /* no trusts gives NT_STATUS_NO_MORE_ENTRIES resetting to NT_STATUS_OK
2104          * so that the generic centry handling still applies correctly -
2105          * Guenther*/
2106
2107         if (!NT_STATUS_IS_ERR(status)) {
2108                 status = NT_STATUS_OK;
2109         }
2110
2111
2112 #if 0    /* Disabled as we want the trust dom list to be managed by
2113             the main parent and always to make the query.  --jerry */
2114
2115         /* and save it */
2116         refresh_sequence_number(domain, false);
2117  
2118         centry = centry_start(domain, status);
2119         if (!centry)
2120                 goto skip_save;
2121
2122         centry_put_uint32(centry, *num_domains);
2123
2124         for (i=0; i<(*num_domains); i++) {
2125                 centry_put_string(centry, (*names)[i]);
2126                 centry_put_string(centry, (*alt_names)[i]);
2127                 centry_put_sid(centry, &(*dom_sids)[i]);
2128         }
2129         
2130         centry_end(centry, "TRUSTDOMS/%s", domain->name);
2131  
2132         centry_free(centry);
2133  
2134 skip_save:
2135 #endif
2136
2137         return status;
2138 }       
2139
2140 /* get lockout policy */
2141 static NTSTATUS lockout_policy(struct winbindd_domain *domain,
2142                                TALLOC_CTX *mem_ctx,
2143                                struct samr_DomInfo12 *policy)
2144 {
2145         struct winbind_cache *cache = get_cache(domain);
2146         struct cache_entry *centry = NULL;
2147         NTSTATUS status;
2148
2149         if (!cache->tdb)
2150                 goto do_query;
2151
2152         centry = wcache_fetch(cache, domain, "LOC_POL/%s", domain->name);
2153
2154         if (!centry)
2155                 goto do_query;
2156
2157         policy->lockout_duration = centry_nttime(centry);
2158         policy->lockout_window = centry_nttime(centry);
2159         policy->lockout_threshold = centry_uint16(centry);
2160
2161         status = centry->status;
2162
2163         DEBUG(10,("lockout_policy: [Cached] - cached info for domain %s status: %s\n",
2164                 domain->name, nt_errstr(status) ));
2165
2166         centry_free(centry);
2167         return status;
2168
2169 do_query:
2170         ZERO_STRUCTP(policy);
2171
2172         /* Return status value returned by seq number check */
2173
2174         if (!NT_STATUS_IS_OK(domain->last_status))
2175                 return domain->last_status;
2176
2177         DEBUG(10,("lockout_policy: [Cached] - doing backend query for info for domain %s\n",
2178                 domain->name ));
2179
2180         status = domain->backend->lockout_policy(domain, mem_ctx, policy);
2181
2182         /* and save it */
2183         refresh_sequence_number(domain, false);
2184         wcache_save_lockout_policy(domain, status, policy);
2185
2186         return status;
2187 }
2188
2189 /* get password policy */
2190 static NTSTATUS password_policy(struct winbindd_domain *domain,
2191                                 TALLOC_CTX *mem_ctx,
2192                                 struct samr_DomInfo1 *policy)
2193 {
2194         struct winbind_cache *cache = get_cache(domain);
2195         struct cache_entry *centry = NULL;
2196         NTSTATUS status;
2197
2198         if (!cache->tdb)
2199                 goto do_query;
2200
2201         centry = wcache_fetch(cache, domain, "PWD_POL/%s", domain->name);
2202
2203         if (!centry)
2204                 goto do_query;
2205
2206         policy->min_password_length = centry_uint16(centry);
2207         policy->password_history_length = centry_uint16(centry);
2208         policy->password_properties = centry_uint32(centry);
2209         policy->max_password_age = centry_nttime(centry);
2210         policy->min_password_age = centry_nttime(centry);
2211
2212         status = centry->status;
2213
2214         DEBUG(10,("lockout_policy: [Cached] - cached info for domain %s status: %s\n",
2215                 domain->name, nt_errstr(status) ));
2216
2217         centry_free(centry);
2218         return status;
2219
2220 do_query:
2221         ZERO_STRUCTP(policy);
2222
2223         /* Return status value returned by seq number check */
2224
2225         if (!NT_STATUS_IS_OK(domain->last_status))
2226                 return domain->last_status;
2227
2228         DEBUG(10,("password_policy: [Cached] - doing backend query for info for domain %s\n",
2229                 domain->name ));
2230
2231         status = domain->backend->password_policy(domain, mem_ctx, policy);
2232
2233         /* and save it */
2234         refresh_sequence_number(domain, false);
2235         wcache_save_password_policy(domain, status, policy);
2236
2237         return status;
2238 }
2239
2240
2241 /* Invalidate cached user and group lists coherently */
2242
2243 static int traverse_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf, 
2244                        void *state)
2245 {
2246         if (strncmp((const char *)kbuf.dptr, "UL/", 3) == 0 ||
2247             strncmp((const char *)kbuf.dptr, "GL/", 3) == 0)
2248                 tdb_delete(the_tdb, kbuf);
2249
2250         return 0;
2251 }
2252
2253 /* Invalidate the getpwnam and getgroups entries for a winbindd domain */
2254
2255 void wcache_invalidate_samlogon(struct winbindd_domain *domain, 
2256                                 struct netr_SamInfo3 *info3)
2257 {
2258         struct winbind_cache *cache;
2259
2260         /* dont clear cached U/SID and UG/SID entries when we want to logon
2261          * offline - gd */
2262
2263         if (lp_winbind_offline_logon()) {
2264                 return;
2265         }
2266
2267         if (!domain)
2268                 return;
2269
2270         cache = get_cache(domain);
2271         netsamlogon_clear_cached_user(cache->tdb, info3);
2272 }
2273
2274 bool wcache_invalidate_cache(void)
2275 {
2276         struct winbindd_domain *domain;
2277
2278         for (domain = domain_list(); domain; domain = domain->next) {
2279                 struct winbind_cache *cache = get_cache(domain);
2280
2281                 DEBUG(10, ("wcache_invalidate_cache: invalidating cache "
2282                            "entries for %s\n", domain->name));
2283                 if (cache) {
2284                         if (cache->tdb) {
2285                                 tdb_traverse(cache->tdb, traverse_fn, NULL);
2286                         } else {
2287                                 return false;
2288                         }
2289                 }
2290         }
2291         return true;
2292 }
2293
2294 bool init_wcache(void)
2295 {
2296         if (wcache == NULL) {
2297                 wcache = SMB_XMALLOC_P(struct winbind_cache);
2298                 ZERO_STRUCTP(wcache);
2299         }
2300
2301         if (wcache->tdb != NULL)
2302                 return true;
2303
2304         /* when working offline we must not clear the cache on restart */
2305         wcache->tdb = tdb_open_log(lock_path("winbindd_cache.tdb"),
2306                                 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE, 
2307                                 lp_winbind_offline_logon() ? TDB_DEFAULT : (TDB_DEFAULT | TDB_CLEAR_IF_FIRST), 
2308                                 O_RDWR|O_CREAT, 0600);
2309
2310         if (wcache->tdb == NULL) {
2311                 DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
2312                 return false;
2313         }
2314
2315         return true;
2316 }
2317
2318 /************************************************************************
2319  This is called by the parent to initialize the cache file.
2320  We don't need sophisticated locking here as we know we're the
2321  only opener.
2322 ************************************************************************/
2323
2324 bool initialize_winbindd_cache(void)
2325 {
2326         bool cache_bad = true;
2327         uint32 vers;
2328
2329         if (!init_wcache()) {
2330                 DEBUG(0,("initialize_winbindd_cache: init_wcache failed.\n"));
2331                 return false;
2332         }
2333
2334         /* Check version number. */
2335         if (tdb_fetch_uint32(wcache->tdb, WINBINDD_CACHE_VERSION_KEYSTR, &vers) &&
2336                         vers == WINBINDD_CACHE_VERSION) {
2337                 cache_bad = false;
2338         }
2339
2340         if (cache_bad) {
2341                 DEBUG(0,("initialize_winbindd_cache: clearing cache "
2342                         "and re-creating with version number %d\n",
2343                         WINBINDD_CACHE_VERSION ));
2344
2345                 tdb_close(wcache->tdb);
2346                 wcache->tdb = NULL;
2347
2348                 if (unlink(lock_path("winbindd_cache.tdb")) == -1) {
2349                         DEBUG(0,("initialize_winbindd_cache: unlink %s failed %s ",
2350                                 lock_path("winbindd_cache.tdb"),
2351                                 strerror(errno) ));
2352                         return false;
2353                 }
2354                 if (!init_wcache()) {
2355                         DEBUG(0,("initialize_winbindd_cache: re-initialization "
2356                                         "init_wcache failed.\n"));
2357                         return false;
2358                 }
2359
2360                 /* Write the version. */
2361                 if (!tdb_store_uint32(wcache->tdb, WINBINDD_CACHE_VERSION_KEYSTR, WINBINDD_CACHE_VERSION)) {
2362                         DEBUG(0,("initialize_winbindd_cache: version number store failed %s\n",
2363                                 tdb_errorstr(wcache->tdb) ));
2364                         return false;
2365                 }
2366         }
2367
2368         tdb_close(wcache->tdb);
2369         wcache->tdb = NULL;
2370         return true;
2371 }
2372
2373 void close_winbindd_cache(void)
2374 {
2375         if (!wcache) {
2376                 return;
2377         }
2378         if (wcache->tdb) {
2379                 tdb_close(wcache->tdb);
2380                 wcache->tdb = NULL;
2381         }
2382 }
2383
2384 void cache_store_response(pid_t pid, struct winbindd_response *response)
2385 {
2386         fstring key_str;
2387
2388         if (!init_wcache())
2389                 return;
2390
2391         DEBUG(10, ("Storing response for pid %d, len %d\n",
2392                    pid, response->length));
2393
2394         fstr_sprintf(key_str, "DR/%d", pid);
2395         if (tdb_store(wcache->tdb, string_tdb_data(key_str), 
2396                       make_tdb_data((uint8 *)response, sizeof(*response)),
2397                       TDB_REPLACE) == -1)
2398                 return;
2399
2400         if (response->length == sizeof(*response))
2401                 return;
2402
2403         /* There's extra data */
2404
2405         DEBUG(10, ("Storing extra data: len=%d\n",
2406                    (int)(response->length - sizeof(*response))));
2407
2408         fstr_sprintf(key_str, "DE/%d", pid);
2409         if (tdb_store(wcache->tdb, string_tdb_data(key_str),
2410                       make_tdb_data((uint8 *)response->extra_data.data,
2411                                     response->length - sizeof(*response)),
2412                       TDB_REPLACE) == 0)
2413                 return;
2414
2415         /* We could not store the extra data, make sure the tdb does not
2416          * contain a main record with wrong dangling extra data */
2417
2418         fstr_sprintf(key_str, "DR/%d", pid);
2419         tdb_delete(wcache->tdb, string_tdb_data(key_str));
2420
2421         return;
2422 }
2423
2424 bool cache_retrieve_response(pid_t pid, struct winbindd_response * response)
2425 {
2426         TDB_DATA data;
2427         fstring key_str;
2428
2429         if (!init_wcache())
2430                 return false;
2431
2432         DEBUG(10, ("Retrieving response for pid %d\n", pid));
2433
2434         fstr_sprintf(key_str, "DR/%d", pid);
2435         data = tdb_fetch(wcache->tdb, string_tdb_data(key_str));
2436
2437         if (data.dptr == NULL)
2438                 return false;
2439
2440         if (data.dsize != sizeof(*response))
2441                 return false;
2442
2443         memcpy(response, data.dptr, data.dsize);
2444         SAFE_FREE(data.dptr);
2445
2446         if (response->length == sizeof(*response)) {
2447                 response->extra_data.data = NULL;
2448                 return true;
2449         }
2450
2451         /* There's extra data */
2452
2453         DEBUG(10, ("Retrieving extra data length=%d\n",
2454                    (int)(response->length - sizeof(*response))));
2455
2456         fstr_sprintf(key_str, "DE/%d", pid);
2457         data = tdb_fetch(wcache->tdb, string_tdb_data(key_str));
2458
2459         if (data.dptr == NULL) {
2460                 DEBUG(0, ("Did not find extra data\n"));
2461                 return false;
2462         }
2463
2464         if (data.dsize != (response->length - sizeof(*response))) {
2465                 DEBUG(0, ("Invalid extra data length: %d\n", (int)data.dsize));
2466                 SAFE_FREE(data.dptr);
2467                 return false;
2468         }
2469
2470         dump_data(11, (uint8 *)data.dptr, data.dsize);
2471
2472         response->extra_data.data = data.dptr;
2473         return true;
2474 }
2475
2476 void cache_cleanup_response(pid_t pid)
2477 {
2478         fstring key_str;
2479
2480         if (!init_wcache())
2481                 return;
2482
2483         fstr_sprintf(key_str, "DR/%d", pid);
2484         tdb_delete(wcache->tdb, string_tdb_data(key_str));
2485
2486         fstr_sprintf(key_str, "DE/%d", pid);
2487         tdb_delete(wcache->tdb, string_tdb_data(key_str));
2488
2489         return;
2490 }
2491
2492
2493 bool lookup_cached_sid(TALLOC_CTX *mem_ctx, const DOM_SID *sid,
2494                        char **domain_name, char **name,
2495                        enum lsa_SidType *type)
2496 {
2497         struct winbindd_domain *domain;
2498         struct winbind_cache *cache;
2499         struct cache_entry *centry = NULL;
2500         NTSTATUS status;
2501         fstring tmp;
2502
2503         domain = find_lookup_domain_from_sid(sid);
2504         if (domain == NULL) {
2505                 return false;
2506         }
2507
2508         cache = get_cache(domain);
2509
2510         if (cache->tdb == NULL) {
2511                 return false;
2512         }
2513
2514         centry = wcache_fetch(cache, domain, "SN/%s",
2515                               sid_to_fstring(tmp, sid));
2516         if (centry == NULL) {
2517                 return false;
2518         }
2519
2520         if (NT_STATUS_IS_OK(centry->status)) {
2521                 *type = (enum lsa_SidType)centry_uint32(centry);
2522                 *domain_name = centry_string(centry, mem_ctx);
2523                 *name = centry_string(centry, mem_ctx);
2524         }
2525
2526         status = centry->status;
2527         centry_free(centry);
2528         return NT_STATUS_IS_OK(status);
2529 }
2530
2531 bool lookup_cached_name(TALLOC_CTX *mem_ctx,
2532                         const char *domain_name,
2533                         const char *name,
2534                         DOM_SID *sid,
2535                         enum lsa_SidType *type)
2536 {
2537         struct winbindd_domain *domain;
2538         struct winbind_cache *cache;
2539         struct cache_entry *centry = NULL;
2540         NTSTATUS status;
2541         fstring uname;
2542         bool original_online_state;     
2543
2544         domain = find_lookup_domain_from_name(domain_name);
2545         if (domain == NULL) {
2546                 return false;
2547         }
2548
2549         cache = get_cache(domain);
2550
2551         if (cache->tdb == NULL) {
2552                 return false;
2553         }
2554
2555         fstrcpy(uname, name);
2556         strupper_m(uname);
2557         
2558         /* If we are doing a cached logon, temporarily set the domain
2559            offline so the cache won't expire the entry */
2560         
2561         original_online_state = domain->online;
2562         domain->online = false;
2563         centry = wcache_fetch(cache, domain, "NS/%s/%s", domain_name, uname);
2564         domain->online = original_online_state;
2565         
2566         if (centry == NULL) {
2567                 return false;
2568         }
2569
2570         if (NT_STATUS_IS_OK(centry->status)) {
2571                 *type = (enum lsa_SidType)centry_uint32(centry);
2572                 centry_sid(centry, mem_ctx, sid);
2573         }
2574
2575         status = centry->status;
2576         centry_free(centry);
2577         
2578         return NT_STATUS_IS_OK(status);
2579 }
2580
2581 void cache_name2sid(struct winbindd_domain *domain, 
2582                     const char *domain_name, const char *name,
2583                     enum lsa_SidType type, const DOM_SID *sid)
2584 {
2585         refresh_sequence_number(domain, false);
2586         wcache_save_name_to_sid(domain, NT_STATUS_OK, domain_name, name,
2587                                 sid, type);
2588 }
2589
2590 /*
2591  * The original idea that this cache only contains centries has
2592  * been blurred - now other stuff gets put in here. Ensure we
2593  * ignore these things on cleanup.
2594  */
2595
2596 static int traverse_fn_cleanup(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, 
2597                                TDB_DATA dbuf, void *state)
2598 {
2599         struct cache_entry *centry;
2600
2601         if (is_non_centry_key(kbuf)) {
2602                 return 0;
2603         }
2604
2605         centry = wcache_fetch_raw((char *)kbuf.dptr);
2606         if (!centry) {
2607                 return 0;
2608         }
2609
2610         if (!NT_STATUS_IS_OK(centry->status)) {
2611                 DEBUG(10,("deleting centry %s\n", (const char *)kbuf.dptr));
2612                 tdb_delete(the_tdb, kbuf);
2613         }
2614
2615         centry_free(centry);
2616         return 0;
2617 }
2618
2619 /* flush the cache */
2620 void wcache_flush_cache(void)
2621 {
2622         if (!wcache)
2623                 return;
2624         if (wcache->tdb) {
2625                 tdb_close(wcache->tdb);
2626                 wcache->tdb = NULL;
2627         }
2628         if (opt_nocache)
2629                 return;
2630
2631         /* when working offline we must not clear the cache on restart */
2632         wcache->tdb = tdb_open_log(lock_path("winbindd_cache.tdb"),
2633                                 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE, 
2634                                 lp_winbind_offline_logon() ? TDB_DEFAULT : (TDB_DEFAULT | TDB_CLEAR_IF_FIRST), 
2635                                 O_RDWR|O_CREAT, 0600);
2636
2637         if (!wcache->tdb) {
2638                 DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
2639                 return;
2640         }
2641
2642         tdb_traverse(wcache->tdb, traverse_fn_cleanup, NULL);
2643
2644         DEBUG(10,("wcache_flush_cache success\n"));
2645 }
2646
2647 /* Count cached creds */
2648
2649 static int traverse_fn_cached_creds(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf, 
2650                                     void *state)
2651 {
2652         int *cred_count = (int*)state;
2653  
2654         if (strncmp((const char *)kbuf.dptr, "CRED/", 5) == 0) {
2655                 (*cred_count)++;
2656         }
2657         return 0;
2658 }
2659
2660 NTSTATUS wcache_count_cached_creds(struct winbindd_domain *domain, int *count)
2661 {
2662         struct winbind_cache *cache = get_cache(domain);
2663
2664         *count = 0;
2665
2666         if (!cache->tdb) {
2667                 return NT_STATUS_INTERNAL_DB_ERROR;
2668         }
2669  
2670         tdb_traverse(cache->tdb, traverse_fn_cached_creds, (void *)count);
2671
2672         return NT_STATUS_OK;
2673 }
2674
2675 struct cred_list {
2676         struct cred_list *prev, *next;
2677         TDB_DATA key;
2678         fstring name;
2679         time_t created;
2680 };
2681 static struct cred_list *wcache_cred_list;
2682
2683 static int traverse_fn_get_credlist(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf, 
2684                                     void *state)
2685 {
2686         struct cred_list *cred;
2687
2688         if (strncmp((const char *)kbuf.dptr, "CRED/", 5) == 0) {
2689
2690                 cred = SMB_MALLOC_P(struct cred_list);
2691                 if (cred == NULL) {
2692                         DEBUG(0,("traverse_fn_remove_first_creds: failed to malloc new entry for list\n"));
2693                         return -1;
2694                 }
2695
2696                 ZERO_STRUCTP(cred);
2697                 
2698                 /* save a copy of the key */
2699                 
2700                 fstrcpy(cred->name, (const char *)kbuf.dptr);           
2701                 DLIST_ADD(wcache_cred_list, cred);
2702         }
2703         
2704         return 0;
2705 }
2706
2707 NTSTATUS wcache_remove_oldest_cached_creds(struct winbindd_domain *domain, const DOM_SID *sid) 
2708 {
2709         struct winbind_cache *cache = get_cache(domain);
2710         NTSTATUS status;
2711         int ret;
2712         struct cred_list *cred, *oldest = NULL;
2713
2714         if (!cache->tdb) {
2715                 return NT_STATUS_INTERNAL_DB_ERROR;
2716         }
2717
2718         /* we possibly already have an entry */
2719         if (sid && NT_STATUS_IS_OK(wcache_cached_creds_exist(domain, sid))) {
2720         
2721                 fstring key_str, tmp;
2722
2723                 DEBUG(11,("we already have an entry, deleting that\n"));
2724
2725                 fstr_sprintf(key_str, "CRED/%s", sid_to_fstring(tmp, sid));
2726
2727                 tdb_delete(cache->tdb, string_tdb_data(key_str));
2728
2729                 return NT_STATUS_OK;
2730         }
2731
2732         ret = tdb_traverse(cache->tdb, traverse_fn_get_credlist, NULL);
2733         if (ret == 0) {
2734                 return NT_STATUS_OK;
2735         } else if ((ret == -1) || (wcache_cred_list == NULL)) {
2736                 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
2737         }
2738
2739         ZERO_STRUCTP(oldest);
2740
2741         for (cred = wcache_cred_list; cred; cred = cred->next) {
2742
2743                 TDB_DATA data;
2744                 time_t t;
2745
2746                 data = tdb_fetch(cache->tdb, string_tdb_data(cred->name));
2747                 if (!data.dptr) {
2748                         DEBUG(10,("wcache_remove_oldest_cached_creds: entry for [%s] not found\n", 
2749                                 cred->name));
2750                         status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
2751                         goto done;
2752                 }
2753         
2754                 t = IVAL(data.dptr, 0);
2755                 SAFE_FREE(data.dptr);
2756
2757                 if (!oldest) {
2758                         oldest = SMB_MALLOC_P(struct cred_list);
2759                         if (oldest == NULL) {
2760                                 status = NT_STATUS_NO_MEMORY;
2761                                 goto done;
2762                         }
2763
2764                         fstrcpy(oldest->name, cred->name);
2765                         oldest->created = t;
2766                         continue;
2767                 }
2768
2769                 if (t < oldest->created) {
2770                         fstrcpy(oldest->name, cred->name);
2771                         oldest->created = t;
2772                 }
2773         }
2774
2775         if (tdb_delete(cache->tdb, string_tdb_data(oldest->name)) == 0) {
2776                 status = NT_STATUS_OK;
2777         } else {
2778                 status = NT_STATUS_UNSUCCESSFUL;
2779         }
2780 done:
2781         SAFE_FREE(wcache_cred_list);
2782         SAFE_FREE(oldest);
2783         
2784         return status;
2785 }
2786
2787 /* Change the global online/offline state. */
2788 bool set_global_winbindd_state_offline(void)
2789 {
2790         TDB_DATA data;
2791
2792         DEBUG(10,("set_global_winbindd_state_offline: offline requested.\n"));
2793
2794         /* Only go offline if someone has created
2795            the key "WINBINDD_OFFLINE" in the cache tdb. */
2796
2797         if (wcache == NULL || wcache->tdb == NULL) {
2798                 DEBUG(10,("set_global_winbindd_state_offline: wcache not open yet.\n"));
2799                 return false;
2800         }
2801
2802         if (!lp_winbind_offline_logon()) {
2803                 DEBUG(10,("set_global_winbindd_state_offline: rejecting.\n"));
2804                 return false;
2805         }
2806
2807         if (global_winbindd_offline_state) {
2808                 /* Already offline. */
2809                 return true;
2810         }
2811
2812         data = tdb_fetch_bystring( wcache->tdb, "WINBINDD_OFFLINE" );
2813
2814         if (!data.dptr || data.dsize != 4) {
2815                 DEBUG(10,("set_global_winbindd_state_offline: offline state not set.\n"));
2816                 SAFE_FREE(data.dptr);
2817                 return false;
2818         } else {
2819                 DEBUG(10,("set_global_winbindd_state_offline: offline state set.\n"));
2820                 global_winbindd_offline_state = true;
2821                 SAFE_FREE(data.dptr);
2822                 return true;
2823         }
2824 }
2825
2826 void set_global_winbindd_state_online(void)
2827 {
2828         DEBUG(10,("set_global_winbindd_state_online: online requested.\n"));
2829
2830         if (!lp_winbind_offline_logon()) {
2831                 DEBUG(10,("set_global_winbindd_state_online: rejecting.\n"));
2832                 return;
2833         }
2834
2835         if (!global_winbindd_offline_state) {
2836                 /* Already online. */
2837                 return;
2838         }
2839         global_winbindd_offline_state = false;
2840
2841         if (!wcache->tdb) {
2842                 return;
2843         }
2844
2845         /* Ensure there is no key "WINBINDD_OFFLINE" in the cache tdb. */
2846         tdb_delete_bystring(wcache->tdb, "WINBINDD_OFFLINE");
2847 }
2848
2849 bool get_global_winbindd_state_offline(void)
2850 {
2851         return global_winbindd_offline_state;
2852 }
2853
2854 /***********************************************************************
2855  Validate functions for all possible cache tdb keys.
2856 ***********************************************************************/
2857
2858 static struct cache_entry *create_centry_validate(const char *kstr, TDB_DATA data, 
2859                                                   struct tdb_validation_status *state)
2860 {
2861         struct cache_entry *centry;
2862
2863         centry = SMB_XMALLOC_P(struct cache_entry);
2864         centry->data = (unsigned char *)memdup(data.dptr, data.dsize);
2865         if (!centry->data) {
2866                 SAFE_FREE(centry);
2867                 return NULL;
2868         }
2869         centry->len = data.dsize;
2870         centry->ofs = 0;
2871
2872         if (centry->len < 8) {
2873                 /* huh? corrupt cache? */
2874                 DEBUG(0,("create_centry_validate: Corrupt cache for key %s (len < 8) ?\n", kstr));
2875                 centry_free(centry);
2876                 state->bad_entry = true;
2877                 state->success = false;
2878                 return NULL;
2879         }
2880
2881         centry->status = NT_STATUS(centry_uint32(centry));
2882         centry->sequence_number = centry_uint32(centry);
2883         return centry;
2884 }
2885
2886 static int validate_seqnum(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
2887                            struct tdb_validation_status *state)
2888 {
2889         if (dbuf.dsize != 8) {
2890                 DEBUG(0,("validate_seqnum: Corrupt cache for key %s (len %u != 8) ?\n",
2891                                 keystr, (unsigned int)dbuf.dsize ));
2892                 state->bad_entry = true;
2893                 return 1;
2894         }
2895         return 0;
2896 }
2897
2898 static int validate_ns(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
2899                        struct tdb_validation_status *state)
2900 {
2901         struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
2902         if (!centry) {
2903                 return 1;
2904         }
2905
2906         (void)centry_uint32(centry);
2907         if (NT_STATUS_IS_OK(centry->status)) {
2908                 DOM_SID sid;
2909                 (void)centry_sid(centry, mem_ctx, &sid);
2910         }
2911
2912         centry_free(centry);
2913
2914         if (!(state->success)) {
2915                 return 1;
2916         }
2917         DEBUG(10,("validate_ns: %s ok\n", keystr));
2918         return 0;
2919 }
2920
2921 static int validate_sn(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
2922                        struct tdb_validation_status *state)
2923 {
2924         struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
2925         if (!centry) {
2926                 return 1;
2927         }
2928
2929         if (NT_STATUS_IS_OK(centry->status)) {
2930                 (void)centry_uint32(centry);
2931                 (void)centry_string(centry, mem_ctx);
2932                 (void)centry_string(centry, mem_ctx);
2933         }
2934
2935         centry_free(centry);
2936
2937         if (!(state->success)) {
2938                 return 1;
2939         }
2940         DEBUG(10,("validate_sn: %s ok\n", keystr));
2941         return 0;
2942 }
2943
2944 static int validate_u(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
2945                       struct tdb_validation_status *state)
2946 {
2947         struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
2948         DOM_SID sid;
2949
2950         if (!centry) {
2951                 return 1;
2952         }
2953
2954         (void)centry_string(centry, mem_ctx);
2955         (void)centry_string(centry, mem_ctx);
2956         (void)centry_string(centry, mem_ctx);
2957         (void)centry_string(centry, mem_ctx);
2958         (void)centry_uint32(centry);
2959         (void)centry_sid(centry, mem_ctx, &sid);
2960         (void)centry_sid(centry, mem_ctx, &sid);
2961
2962         centry_free(centry);
2963
2964         if (!(state->success)) {
2965                 return 1;
2966         }
2967         DEBUG(10,("validate_u: %s ok\n", keystr));
2968         return 0;
2969 }
2970
2971 static int validate_loc_pol(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
2972                             struct tdb_validation_status *state)
2973 {
2974         struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
2975
2976         if (!centry) {
2977                 return 1;
2978         }
2979
2980         (void)centry_nttime(centry);
2981         (void)centry_nttime(centry);
2982         (void)centry_uint16(centry);
2983
2984         centry_free(centry);
2985
2986         if (!(state->success)) {
2987                 return 1;
2988         }
2989         DEBUG(10,("validate_loc_pol: %s ok\n", keystr));
2990         return 0;
2991 }
2992
2993 static int validate_pwd_pol(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
2994                             struct tdb_validation_status *state)
2995 {
2996         struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
2997
2998         if (!centry) {
2999                 return 1;
3000         }
3001
3002         (void)centry_uint16(centry);
3003         (void)centry_uint16(centry);
3004         (void)centry_uint32(centry);
3005         (void)centry_nttime(centry);
3006         (void)centry_nttime(centry);
3007
3008         centry_free(centry);
3009
3010         if (!(state->success)) {
3011                 return 1;
3012         }
3013         DEBUG(10,("validate_pwd_pol: %s ok\n", keystr));
3014         return 0;
3015 }
3016
3017 static int validate_cred(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3018                          struct tdb_validation_status *state)
3019 {
3020         struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3021
3022         if (!centry) {
3023                 return 1;
3024         }
3025
3026         (void)centry_time(centry);
3027         (void)centry_hash16(centry, mem_ctx);
3028
3029         /* We only have 17 bytes more data in the salted cred case. */
3030         if (centry->len - centry->ofs == 17) {
3031                 (void)centry_hash16(centry, mem_ctx);
3032         }
3033
3034         centry_free(centry);
3035
3036         if (!(state->success)) {
3037                 return 1;
3038         }
3039         DEBUG(10,("validate_cred: %s ok\n", keystr));
3040         return 0;
3041 }
3042
3043 static int validate_ul(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3044                        struct tdb_validation_status *state)
3045 {
3046         struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3047         int32 num_entries, i;
3048
3049         if (!centry) {
3050                 return 1;
3051         }
3052
3053         num_entries = (int32)centry_uint32(centry);
3054
3055         for (i=0; i< num_entries; i++) {
3056                 DOM_SID sid;
3057                 (void)centry_string(centry, mem_ctx);
3058                 (void)centry_string(centry, mem_ctx);
3059                 (void)centry_string(centry, mem_ctx);
3060                 (void)centry_string(centry, mem_ctx);
3061                 (void)centry_sid(centry, mem_ctx, &sid);
3062                 (void)centry_sid(centry, mem_ctx, &sid);
3063         }
3064
3065         centry_free(centry);
3066
3067         if (!(state->success)) {
3068                 return 1;
3069         }
3070         DEBUG(10,("validate_ul: %s ok\n", keystr));
3071         return 0;
3072 }
3073
3074 static int validate_gl(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3075                        struct tdb_validation_status *state)
3076 {
3077         struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3078         int32 num_entries, i;
3079
3080         if (!centry) {
3081                 return 1;
3082         }
3083
3084         num_entries = centry_uint32(centry);
3085         
3086         for (i=0; i< num_entries; i++) {
3087                 (void)centry_string(centry, mem_ctx);
3088                 (void)centry_string(centry, mem_ctx);
3089                 (void)centry_uint32(centry);
3090         }
3091
3092         centry_free(centry);
3093
3094         if (!(state->success)) {
3095                 return 1;
3096         }
3097         DEBUG(10,("validate_gl: %s ok\n", keystr));
3098         return 0;
3099 }
3100
3101 static int validate_ug(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3102                        struct tdb_validation_status *state)
3103 {
3104         struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3105         int32 num_groups, i;
3106
3107         if (!centry) {
3108                 return 1;
3109         }
3110
3111         num_groups = centry_uint32(centry);
3112
3113         for (i=0; i< num_groups; i++) {
3114                 DOM_SID sid;
3115                 centry_sid(centry, mem_ctx, &sid);
3116         }
3117
3118         centry_free(centry);
3119
3120         if (!(state->success)) {
3121                 return 1;
3122         }
3123         DEBUG(10,("validate_ug: %s ok\n", keystr));
3124         return 0;
3125 }
3126
3127 static int validate_ua(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3128                        struct tdb_validation_status *state)
3129 {
3130         struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3131         int32 num_aliases, i;
3132
3133         if (!centry) {
3134                 return 1;
3135         }
3136
3137         num_aliases = centry_uint32(centry);
3138
3139         for (i=0; i < num_aliases; i++) {
3140                 (void)centry_uint32(centry);
3141         }
3142
3143         centry_free(centry);
3144
3145         if (!(state->success)) {
3146                 return 1;
3147         }
3148         DEBUG(10,("validate_ua: %s ok\n", keystr));
3149         return 0;
3150 }
3151
3152 static int validate_gm(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3153                        struct tdb_validation_status *state)
3154 {
3155         struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3156         int32 num_names, i;
3157
3158         if (!centry) {
3159                 return 1;
3160         }
3161
3162         num_names = centry_uint32(centry);
3163
3164         for (i=0; i< num_names; i++) {
3165                 DOM_SID sid;
3166                 centry_sid(centry, mem_ctx, &sid);
3167                 (void)centry_string(centry, mem_ctx);
3168                 (void)centry_uint32(centry);
3169         }
3170
3171         centry_free(centry);
3172
3173         if (!(state->success)) {
3174                 return 1;
3175         }
3176         DEBUG(10,("validate_gm: %s ok\n", keystr));
3177         return 0;
3178 }
3179
3180 static int validate_dr(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3181                        struct tdb_validation_status *state)
3182 {
3183         /* Can't say anything about this other than must be nonzero. */
3184         if (dbuf.dsize == 0) {
3185                 DEBUG(0,("validate_dr: Corrupt cache for key %s (len == 0) ?\n",
3186                                 keystr));
3187                 state->bad_entry = true;
3188                 state->success = false;
3189                 return 1;
3190         }
3191
3192         DEBUG(10,("validate_dr: %s ok\n", keystr));
3193         return 0;
3194 }
3195
3196 static int validate_de(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3197                        struct tdb_validation_status *state)
3198 {
3199         /* Can't say anything about this other than must be nonzero. */
3200         if (dbuf.dsize == 0) {
3201                 DEBUG(0,("validate_de: Corrupt cache for key %s (len == 0) ?\n",
3202                                 keystr));
3203                 state->bad_entry = true;
3204                 state->success = false;
3205                 return 1;
3206         }
3207
3208         DEBUG(10,("validate_de: %s ok\n", keystr));
3209         return 0;
3210 }
3211
3212 static int validate_trustdoms(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3213                               struct tdb_validation_status *state)
3214 {
3215         struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3216         int32 num_domains, i;
3217
3218         if (!centry) {
3219                 return 1;
3220         }
3221
3222         num_domains = centry_uint32(centry);
3223         
3224         for (i=0; i< num_domains; i++) {
3225                 DOM_SID sid;
3226                 (void)centry_string(centry, mem_ctx);
3227                 (void)centry_string(centry, mem_ctx);
3228                 (void)centry_sid(centry, mem_ctx, &sid);
3229         }
3230
3231         centry_free(centry);
3232
3233         if (!(state->success)) {
3234                 return 1;
3235         }
3236         DEBUG(10,("validate_trustdoms: %s ok\n", keystr));
3237         return 0;
3238 }
3239
3240 static int validate_trustdomcache(TALLOC_CTX *mem_ctx, const char *keystr, 
3241                                   TDB_DATA dbuf,
3242                                   struct tdb_validation_status *state)
3243 {
3244         if (dbuf.dsize == 0) {
3245                 DEBUG(0, ("validate_trustdomcache: Corrupt cache for "
3246                           "key %s (len ==0) ?\n", keystr));
3247                 state->bad_entry = true;
3248                 state->success = false;
3249                 return 1;
3250         }
3251
3252         DEBUG(10,    ("validate_trustdomcache: %s ok\n", keystr));
3253         DEBUGADD(10, ("  Don't trust me, I am a DUMMY!\n"));
3254         return 0;
3255 }
3256
3257 static int validate_offline(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3258                             struct tdb_validation_status *state)
3259 {
3260         if (dbuf.dsize != 4) {
3261                 DEBUG(0,("validate_offline: Corrupt cache for key %s (len %u != 4) ?\n",
3262                                 keystr, (unsigned int)dbuf.dsize ));
3263                 state->bad_entry = true;
3264                 state->success = false;
3265                 return 1;
3266         }
3267         DEBUG(10,("validate_offline: %s ok\n", keystr));
3268         return 0;
3269 }
3270
3271 static int validate_cache_version(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3272                                   struct tdb_validation_status *state)
3273 {
3274         if (dbuf.dsize != 4) {
3275                 DEBUG(0, ("validate_cache_version: Corrupt cache for "
3276                           "key %s (len %u != 4) ?\n", 
3277                           keystr, (unsigned int)dbuf.dsize));
3278                 state->bad_entry = true;
3279                 state->success = false;
3280                 return 1;
3281         }
3282
3283         DEBUG(10, ("validate_cache_version: %s ok\n", keystr));
3284         return 0;
3285 }
3286
3287 /***********************************************************************
3288  A list of all possible cache tdb keys with associated validation
3289  functions.
3290 ***********************************************************************/
3291
3292 struct key_val_struct {
3293         const char *keyname;
3294         int (*validate_data_fn)(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf, struct tdb_validation_status* state);
3295 } key_val[] = {
3296         {"SEQNUM/", validate_seqnum},
3297         {"NS/", validate_ns},
3298         {"SN/", validate_sn},
3299         {"U/", validate_u},
3300         {"LOC_POL/", validate_loc_pol},
3301         {"PWD_POL/", validate_pwd_pol},
3302         {"CRED/", validate_cred},
3303         {"UL/", validate_ul},
3304         {"GL/", validate_gl},
3305         {"UG/", validate_ug},
3306         {"UA", validate_ua},
3307         {"GM/", validate_gm},
3308         {"DR/", validate_dr},
3309         {"DE/", validate_de},
3310         {"TRUSTDOMS/", validate_trustdoms},
3311         {"TRUSTDOMCACHE/", validate_trustdomcache},
3312         {"WINBINDD_OFFLINE", validate_offline},
3313         {WINBINDD_CACHE_VERSION_KEYSTR, validate_cache_version},
3314         {NULL, NULL}
3315 };
3316
3317 /***********************************************************************
3318  Function to look at every entry in the tdb and validate it as far as
3319  possible.
3320 ***********************************************************************/
3321
3322 static int cache_traverse_validate_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf, void *state)
3323 {
3324         int i;
3325         struct tdb_validation_status *v_state = (struct tdb_validation_status *)state;
3326
3327         /* Paranoia check. */
3328         if (kbuf.dsize > 1024) {
3329                 DEBUG(0,("cache_traverse_validate_fn: key length too large (%u) > 1024\n\n",
3330                                 (unsigned int)kbuf.dsize ));
3331                 return 1;
3332         }
3333
3334         for (i = 0; key_val[i].keyname; i++) {
3335                 size_t namelen = strlen(key_val[i].keyname);
3336                 if (kbuf.dsize >= namelen && (
3337                                 strncmp(key_val[i].keyname, (const char *)kbuf.dptr, namelen)) == 0) {
3338                         TALLOC_CTX *mem_ctx;