winbindd: handle trusted domains without sid.
[kai/samba.git] / source3 / winbindd / winbindd_cache.c
1 /* 
2    Unix SMB/CIFS implementation.
3
4    Winbind cache backend functions
5
6    Copyright (C) Andrew Tridgell 2001
7    Copyright (C) Gerald Carter   2003-2007
8    Copyright (C) Volker Lendecke 2005
9    Copyright (C) Guenther Deschner 2005
10    Copyright (C) Michael Adam    2007
11
12    This program is free software; you can redistribute it and/or modify
13    it under the terms of the GNU General Public License as published by
14    the Free Software Foundation; either version 3 of the License, or
15    (at your option) any later version.
16    
17    This program is distributed in the hope that it will be useful,
18    but WITHOUT ANY WARRANTY; without even the implied warranty of
19    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20    GNU General Public License for more details.
21    
22    You should have received a copy of the GNU General Public License
23    along with this program.  If not, see <http://www.gnu.org/licenses/>.
24 */
25
26 #include "includes.h"
27 #include "winbindd.h"
28
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 builtin_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 = &builtin_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 in 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                 if (!centry_sid(centry, mem_ctx, &(*dom_sids)[i])) {
2076                         sid_copy(&(*dom_sids)[i], &global_sid_NULL);
2077                 }
2078         }
2079
2080         status = centry->status;
2081  
2082         DEBUG(10,("trusted_domains: [Cached] - cached info for domain %s (%d trusts) status: %s\n",
2083                 domain->name, *num_domains, nt_errstr(status) ));
2084  
2085         centry_free(centry);
2086         return status;
2087  
2088 do_query:
2089         (*num_domains) = 0;
2090         (*dom_sids) = NULL;
2091         (*names) = NULL;
2092         (*alt_names) = NULL;
2093  
2094         /* Return status value returned by seq number check */
2095
2096         if (!NT_STATUS_IS_OK(domain->last_status))
2097                 return domain->last_status;
2098         
2099         DEBUG(10,("trusted_domains: [Cached] - doing backend query for info for domain %s\n",
2100                 domain->name ));
2101  
2102         status = domain->backend->trusted_domains(domain, mem_ctx, num_domains,
2103                                                 names, alt_names, dom_sids);
2104
2105         /* no trusts gives NT_STATUS_NO_MORE_ENTRIES resetting to NT_STATUS_OK
2106          * so that the generic centry handling still applies correctly -
2107          * Guenther*/
2108
2109         if (!NT_STATUS_IS_ERR(status)) {
2110                 status = NT_STATUS_OK;
2111         }
2112
2113
2114 #if 0    /* Disabled as we want the trust dom list to be managed by
2115             the main parent and always to make the query.  --jerry */
2116
2117         /* and save it */
2118         refresh_sequence_number(domain, false);
2119  
2120         centry = centry_start(domain, status);
2121         if (!centry)
2122                 goto skip_save;
2123
2124         centry_put_uint32(centry, *num_domains);
2125
2126         for (i=0; i<(*num_domains); i++) {
2127                 centry_put_string(centry, (*names)[i]);
2128                 centry_put_string(centry, (*alt_names)[i]);
2129                 centry_put_sid(centry, &(*dom_sids)[i]);
2130         }
2131         
2132         centry_end(centry, "TRUSTDOMS/%s", domain->name);
2133  
2134         centry_free(centry);
2135  
2136 skip_save:
2137 #endif
2138
2139         return status;
2140 }       
2141
2142 /* get lockout policy */
2143 static NTSTATUS lockout_policy(struct winbindd_domain *domain,
2144                                TALLOC_CTX *mem_ctx,
2145                                struct samr_DomInfo12 *policy)
2146 {
2147         struct winbind_cache *cache = get_cache(domain);
2148         struct cache_entry *centry = NULL;
2149         NTSTATUS status;
2150
2151         if (!cache->tdb)
2152                 goto do_query;
2153
2154         centry = wcache_fetch(cache, domain, "LOC_POL/%s", domain->name);
2155
2156         if (!centry)
2157                 goto do_query;
2158
2159         policy->lockout_duration = centry_nttime(centry);
2160         policy->lockout_window = centry_nttime(centry);
2161         policy->lockout_threshold = centry_uint16(centry);
2162
2163         status = centry->status;
2164
2165         DEBUG(10,("lockout_policy: [Cached] - cached info for domain %s status: %s\n",
2166                 domain->name, nt_errstr(status) ));
2167
2168         centry_free(centry);
2169         return status;
2170
2171 do_query:
2172         ZERO_STRUCTP(policy);
2173
2174         /* Return status value returned by seq number check */
2175
2176         if (!NT_STATUS_IS_OK(domain->last_status))
2177                 return domain->last_status;
2178
2179         DEBUG(10,("lockout_policy: [Cached] - doing backend query for info for domain %s\n",
2180                 domain->name ));
2181
2182         status = domain->backend->lockout_policy(domain, mem_ctx, policy);
2183
2184         /* and save it */
2185         refresh_sequence_number(domain, false);
2186         wcache_save_lockout_policy(domain, status, policy);
2187
2188         return status;
2189 }
2190
2191 /* get password policy */
2192 static NTSTATUS password_policy(struct winbindd_domain *domain,
2193                                 TALLOC_CTX *mem_ctx,
2194                                 struct samr_DomInfo1 *policy)
2195 {
2196         struct winbind_cache *cache = get_cache(domain);
2197         struct cache_entry *centry = NULL;
2198         NTSTATUS status;
2199
2200         if (!cache->tdb)
2201                 goto do_query;
2202
2203         centry = wcache_fetch(cache, domain, "PWD_POL/%s", domain->name);
2204
2205         if (!centry)
2206                 goto do_query;
2207
2208         policy->min_password_length = centry_uint16(centry);
2209         policy->password_history_length = centry_uint16(centry);
2210         policy->password_properties = centry_uint32(centry);
2211         policy->max_password_age = centry_nttime(centry);
2212         policy->min_password_age = centry_nttime(centry);
2213
2214         status = centry->status;
2215
2216         DEBUG(10,("lockout_policy: [Cached] - cached info for domain %s status: %s\n",
2217                 domain->name, nt_errstr(status) ));
2218
2219         centry_free(centry);
2220         return status;
2221
2222 do_query:
2223         ZERO_STRUCTP(policy);
2224
2225         /* Return status value returned by seq number check */
2226
2227         if (!NT_STATUS_IS_OK(domain->last_status))
2228                 return domain->last_status;
2229
2230         DEBUG(10,("password_policy: [Cached] - doing backend query for info for domain %s\n",
2231                 domain->name ));
2232
2233         status = domain->backend->password_policy(domain, mem_ctx, policy);
2234
2235         /* and save it */
2236         refresh_sequence_number(domain, false);
2237         if (NT_STATUS_IS_OK(status)) {
2238                 wcache_save_password_policy(domain, status, policy);
2239         }
2240
2241         return status;
2242 }
2243
2244
2245 /* Invalidate cached user and group lists coherently */
2246
2247 static int traverse_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf, 
2248                        void *state)
2249 {
2250         if (strncmp((const char *)kbuf.dptr, "UL/", 3) == 0 ||
2251             strncmp((const char *)kbuf.dptr, "GL/", 3) == 0)
2252                 tdb_delete(the_tdb, kbuf);
2253
2254         return 0;
2255 }
2256
2257 /* Invalidate the getpwnam and getgroups entries for a winbindd domain */
2258
2259 void wcache_invalidate_samlogon(struct winbindd_domain *domain, 
2260                                 struct netr_SamInfo3 *info3)
2261 {
2262         struct winbind_cache *cache;
2263
2264         /* dont clear cached U/SID and UG/SID entries when we want to logon
2265          * offline - gd */
2266
2267         if (lp_winbind_offline_logon()) {
2268                 return;
2269         }
2270
2271         if (!domain)
2272                 return;
2273
2274         cache = get_cache(domain);
2275         netsamlogon_clear_cached_user(cache->tdb, info3);
2276 }
2277
2278 bool wcache_invalidate_cache(void)
2279 {
2280         struct winbindd_domain *domain;
2281
2282         for (domain = domain_list(); domain; domain = domain->next) {
2283                 struct winbind_cache *cache = get_cache(domain);
2284
2285                 DEBUG(10, ("wcache_invalidate_cache: invalidating cache "
2286                            "entries for %s\n", domain->name));
2287                 if (cache) {
2288                         if (cache->tdb) {
2289                                 tdb_traverse(cache->tdb, traverse_fn, NULL);
2290                         } else {
2291                                 return false;
2292                         }
2293                 }
2294         }
2295         return true;
2296 }
2297
2298 bool init_wcache(void)
2299 {
2300         if (wcache == NULL) {
2301                 wcache = SMB_XMALLOC_P(struct winbind_cache);
2302                 ZERO_STRUCTP(wcache);
2303         }
2304
2305         if (wcache->tdb != NULL)
2306                 return true;
2307
2308         /* when working offline we must not clear the cache on restart */
2309         wcache->tdb = tdb_open_log(lock_path("winbindd_cache.tdb"),
2310                                 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE, 
2311                                 lp_winbind_offline_logon() ? TDB_DEFAULT : (TDB_DEFAULT | TDB_CLEAR_IF_FIRST), 
2312                                 O_RDWR|O_CREAT, 0600);
2313
2314         if (wcache->tdb == NULL) {
2315                 DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
2316                 return false;
2317         }
2318
2319         return true;
2320 }
2321
2322 /************************************************************************
2323  This is called by the parent to initialize the cache file.
2324  We don't need sophisticated locking here as we know we're the
2325  only opener.
2326 ************************************************************************/
2327
2328 bool initialize_winbindd_cache(void)
2329 {
2330         bool cache_bad = true;
2331         uint32 vers;
2332
2333         if (!init_wcache()) {
2334                 DEBUG(0,("initialize_winbindd_cache: init_wcache failed.\n"));
2335                 return false;
2336         }
2337
2338         /* Check version number. */
2339         if (tdb_fetch_uint32(wcache->tdb, WINBINDD_CACHE_VERSION_KEYSTR, &vers) &&
2340                         vers == WINBINDD_CACHE_VERSION) {
2341                 cache_bad = false;
2342         }
2343
2344         if (cache_bad) {
2345                 DEBUG(0,("initialize_winbindd_cache: clearing cache "
2346                         "and re-creating with version number %d\n",
2347                         WINBINDD_CACHE_VERSION ));
2348
2349                 tdb_close(wcache->tdb);
2350                 wcache->tdb = NULL;
2351
2352                 if (unlink(lock_path("winbindd_cache.tdb")) == -1) {
2353                         DEBUG(0,("initialize_winbindd_cache: unlink %s failed %s ",
2354                                 lock_path("winbindd_cache.tdb"),
2355                                 strerror(errno) ));
2356                         return false;
2357                 }
2358                 if (!init_wcache()) {
2359                         DEBUG(0,("initialize_winbindd_cache: re-initialization "
2360                                         "init_wcache failed.\n"));
2361                         return false;
2362                 }
2363
2364                 /* Write the version. */
2365                 if (!tdb_store_uint32(wcache->tdb, WINBINDD_CACHE_VERSION_KEYSTR, WINBINDD_CACHE_VERSION)) {
2366                         DEBUG(0,("initialize_winbindd_cache: version number store failed %s\n",
2367                                 tdb_errorstr(wcache->tdb) ));
2368                         return false;
2369                 }
2370         }
2371
2372         tdb_close(wcache->tdb);
2373         wcache->tdb = NULL;
2374         return true;
2375 }
2376
2377 void close_winbindd_cache(void)
2378 {
2379         if (!wcache) {
2380                 return;
2381         }
2382         if (wcache->tdb) {
2383                 tdb_close(wcache->tdb);
2384                 wcache->tdb = NULL;
2385         }
2386 }
2387
2388 void cache_store_response(pid_t pid, struct winbindd_response *response)
2389 {
2390         fstring key_str;
2391
2392         if (!init_wcache())
2393                 return;
2394
2395         DEBUG(10, ("Storing response for pid %d, len %d\n",
2396                    pid, response->length));
2397
2398         fstr_sprintf(key_str, "DR/%d", pid);
2399         if (tdb_store(wcache->tdb, string_tdb_data(key_str), 
2400                       make_tdb_data((uint8 *)response, sizeof(*response)),
2401                       TDB_REPLACE) == -1)
2402                 return;
2403
2404         if (response->length == sizeof(*response))
2405                 return;
2406
2407         /* There's extra data */
2408
2409         DEBUG(10, ("Storing extra data: len=%d\n",
2410                    (int)(response->length - sizeof(*response))));
2411
2412         fstr_sprintf(key_str, "DE/%d", pid);
2413         if (tdb_store(wcache->tdb, string_tdb_data(key_str),
2414                       make_tdb_data((uint8 *)response->extra_data.data,
2415                                     response->length - sizeof(*response)),
2416                       TDB_REPLACE) == 0)
2417                 return;
2418
2419         /* We could not store the extra data, make sure the tdb does not
2420          * contain a main record with wrong dangling extra data */
2421
2422         fstr_sprintf(key_str, "DR/%d", pid);
2423         tdb_delete(wcache->tdb, string_tdb_data(key_str));
2424
2425         return;
2426 }
2427
2428 bool cache_retrieve_response(pid_t pid, struct winbindd_response * response)
2429 {
2430         TDB_DATA data;
2431         fstring key_str;
2432
2433         if (!init_wcache())
2434                 return false;
2435
2436         DEBUG(10, ("Retrieving response for pid %d\n", pid));
2437
2438         fstr_sprintf(key_str, "DR/%d", pid);
2439         data = tdb_fetch(wcache->tdb, string_tdb_data(key_str));
2440
2441         if (data.dptr == NULL)
2442                 return false;
2443
2444         if (data.dsize != sizeof(*response))
2445                 return false;
2446
2447         memcpy(response, data.dptr, data.dsize);
2448         SAFE_FREE(data.dptr);
2449
2450         if (response->length == sizeof(*response)) {
2451                 response->extra_data.data = NULL;
2452                 return true;
2453         }
2454
2455         /* There's extra data */
2456
2457         DEBUG(10, ("Retrieving extra data length=%d\n",
2458                    (int)(response->length - sizeof(*response))));
2459
2460         fstr_sprintf(key_str, "DE/%d", pid);
2461         data = tdb_fetch(wcache->tdb, string_tdb_data(key_str));
2462
2463         if (data.dptr == NULL) {
2464                 DEBUG(0, ("Did not find extra data\n"));
2465                 return false;
2466         }
2467
2468         if (data.dsize != (response->length - sizeof(*response))) {
2469                 DEBUG(0, ("Invalid extra data length: %d\n", (int)data.dsize));
2470                 SAFE_FREE(data.dptr);
2471                 return false;
2472         }
2473
2474         dump_data(11, (uint8 *)data.dptr, data.dsize);
2475
2476         response->extra_data.data = data.dptr;
2477         return true;
2478 }
2479
2480 void cache_cleanup_response(pid_t pid)
2481 {
2482         fstring key_str;
2483
2484         if (!init_wcache())
2485                 return;
2486
2487         fstr_sprintf(key_str, "DR/%d", pid);
2488         tdb_delete(wcache->tdb, string_tdb_data(key_str));
2489
2490         fstr_sprintf(key_str, "DE/%d", pid);
2491         tdb_delete(wcache->tdb, string_tdb_data(key_str));
2492
2493         return;
2494 }
2495
2496
2497 bool lookup_cached_sid(TALLOC_CTX *mem_ctx, const DOM_SID *sid,
2498                        char **domain_name, char **name,
2499                        enum lsa_SidType *type)
2500 {
2501         struct winbindd_domain *domain;
2502         struct winbind_cache *cache;
2503         struct cache_entry *centry = NULL;
2504         NTSTATUS status;
2505         fstring tmp;
2506
2507         domain = find_lookup_domain_from_sid(sid);
2508         if (domain == NULL) {
2509                 return false;
2510         }
2511
2512         cache = get_cache(domain);
2513
2514         if (cache->tdb == NULL) {
2515                 return false;
2516         }
2517
2518         centry = wcache_fetch(cache, domain, "SN/%s",
2519                               sid_to_fstring(tmp, sid));
2520         if (centry == NULL) {
2521                 return false;
2522         }
2523
2524         if (NT_STATUS_IS_OK(centry->status)) {
2525                 *type = (enum lsa_SidType)centry_uint32(centry);
2526                 *domain_name = centry_string(centry, mem_ctx);
2527                 *name = centry_string(centry, mem_ctx);
2528         }
2529
2530         status = centry->status;
2531         centry_free(centry);
2532         return NT_STATUS_IS_OK(status);
2533 }
2534
2535 bool lookup_cached_name(TALLOC_CTX *mem_ctx,
2536                         const char *domain_name,
2537                         const char *name,
2538                         DOM_SID *sid,
2539                         enum lsa_SidType *type)
2540 {
2541         struct winbindd_domain *domain;
2542         struct winbind_cache *cache;
2543         struct cache_entry *centry = NULL;
2544         NTSTATUS status;
2545         fstring uname;
2546         bool original_online_state;     
2547
2548         domain = find_lookup_domain_from_name(domain_name);
2549         if (domain == NULL) {
2550                 return false;
2551         }
2552
2553         cache = get_cache(domain);
2554
2555         if (cache->tdb == NULL) {
2556                 return false;
2557         }
2558
2559         fstrcpy(uname, name);
2560         strupper_m(uname);
2561         
2562         /* If we are doing a cached logon, temporarily set the domain
2563            offline so the cache won't expire the entry */
2564         
2565         original_online_state = domain->online;
2566         domain->online = false;
2567         centry = wcache_fetch(cache, domain, "NS/%s/%s", domain_name, uname);
2568         domain->online = original_online_state;
2569         
2570         if (centry == NULL) {
2571                 return false;
2572         }
2573
2574         if (NT_STATUS_IS_OK(centry->status)) {
2575                 *type = (enum lsa_SidType)centry_uint32(centry);
2576                 centry_sid(centry, mem_ctx, sid);
2577         }
2578
2579         status = centry->status;
2580         centry_free(centry);
2581         
2582         return NT_STATUS_IS_OK(status);
2583 }
2584
2585 void cache_name2sid(struct winbindd_domain *domain, 
2586                     const char *domain_name, const char *name,
2587                     enum lsa_SidType type, const DOM_SID *sid)
2588 {
2589         refresh_sequence_number(domain, false);
2590         wcache_save_name_to_sid(domain, NT_STATUS_OK, domain_name, name,
2591                                 sid, type);
2592 }
2593
2594 /*
2595  * The original idea that this cache only contains centries has
2596  * been blurred - now other stuff gets put in here. Ensure we
2597  * ignore these things on cleanup.
2598  */
2599
2600 static int traverse_fn_cleanup(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, 
2601                                TDB_DATA dbuf, void *state)
2602 {
2603         struct cache_entry *centry;
2604
2605         if (is_non_centry_key(kbuf)) {
2606                 return 0;
2607         }
2608
2609         centry = wcache_fetch_raw((char *)kbuf.dptr);
2610         if (!centry) {
2611                 return 0;
2612         }
2613
2614         if (!NT_STATUS_IS_OK(centry->status)) {
2615                 DEBUG(10,("deleting centry %s\n", (const char *)kbuf.dptr));
2616                 tdb_delete(the_tdb, kbuf);
2617         }
2618
2619         centry_free(centry);
2620         return 0;
2621 }
2622
2623 /* flush the cache */
2624 void wcache_flush_cache(void)
2625 {
2626         if (!wcache)
2627                 return;
2628         if (wcache->tdb) {
2629                 tdb_close(wcache->tdb);
2630                 wcache->tdb = NULL;
2631         }
2632         if (opt_nocache)
2633                 return;
2634
2635         /* when working offline we must not clear the cache on restart */
2636         wcache->tdb = tdb_open_log(lock_path("winbindd_cache.tdb"),
2637                                 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE, 
2638                                 lp_winbind_offline_logon() ? TDB_DEFAULT : (TDB_DEFAULT | TDB_CLEAR_IF_FIRST), 
2639                                 O_RDWR|O_CREAT, 0600);
2640
2641         if (!wcache->tdb) {
2642                 DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
2643                 return;
2644         }
2645
2646         tdb_traverse(wcache->tdb, traverse_fn_cleanup, NULL);
2647
2648         DEBUG(10,("wcache_flush_cache success\n"));
2649 }
2650
2651 /* Count cached creds */
2652
2653 static int traverse_fn_cached_creds(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf, 
2654                                     void *state)
2655 {
2656         int *cred_count = (int*)state;
2657  
2658         if (strncmp((const char *)kbuf.dptr, "CRED/", 5) == 0) {
2659                 (*cred_count)++;
2660         }
2661         return 0;
2662 }
2663
2664 NTSTATUS wcache_count_cached_creds(struct winbindd_domain *domain, int *count)
2665 {
2666         struct winbind_cache *cache = get_cache(domain);
2667
2668         *count = 0;
2669
2670         if (!cache->tdb) {
2671                 return NT_STATUS_INTERNAL_DB_ERROR;
2672         }
2673  
2674         tdb_traverse(cache->tdb, traverse_fn_cached_creds, (void *)count);
2675
2676         return NT_STATUS_OK;
2677 }
2678
2679 struct cred_list {
2680         struct cred_list *prev, *next;
2681         TDB_DATA key;
2682         fstring name;
2683         time_t created;
2684 };
2685 static struct cred_list *wcache_cred_list;
2686
2687 static int traverse_fn_get_credlist(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf, 
2688                                     void *state)
2689 {
2690         struct cred_list *cred;
2691
2692         if (strncmp((const char *)kbuf.dptr, "CRED/", 5) == 0) {
2693
2694                 cred = SMB_MALLOC_P(struct cred_list);
2695                 if (cred == NULL) {
2696                         DEBUG(0,("traverse_fn_remove_first_creds: failed to malloc new entry for list\n"));
2697                         return -1;
2698                 }
2699
2700                 ZERO_STRUCTP(cred);
2701                 
2702                 /* save a copy of the key */
2703                 
2704                 fstrcpy(cred->name, (const char *)kbuf.dptr);           
2705                 DLIST_ADD(wcache_cred_list, cred);
2706         }
2707         
2708         return 0;
2709 }
2710
2711 NTSTATUS wcache_remove_oldest_cached_creds(struct winbindd_domain *domain, const DOM_SID *sid) 
2712 {
2713         struct winbind_cache *cache = get_cache(domain);
2714         NTSTATUS status;
2715         int ret;
2716         struct cred_list *cred, *oldest = NULL;
2717
2718         if (!cache->tdb) {
2719                 return NT_STATUS_INTERNAL_DB_ERROR;
2720         }
2721
2722         /* we possibly already have an entry */
2723         if (sid && NT_STATUS_IS_OK(wcache_cached_creds_exist(domain, sid))) {
2724         
2725                 fstring key_str, tmp;
2726
2727                 DEBUG(11,("we already have an entry, deleting that\n"));
2728
2729                 fstr_sprintf(key_str, "CRED/%s", sid_to_fstring(tmp, sid));
2730
2731                 tdb_delete(cache->tdb, string_tdb_data(key_str));
2732
2733                 return NT_STATUS_OK;
2734         }
2735
2736         ret = tdb_traverse(cache->tdb, traverse_fn_get_credlist, NULL);
2737         if (ret == 0) {
2738                 return NT_STATUS_OK;
2739         } else if ((ret == -1) || (wcache_cred_list == NULL)) {
2740                 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
2741         }
2742
2743         ZERO_STRUCTP(oldest);
2744
2745         for (cred = wcache_cred_list; cred; cred = cred->next) {
2746
2747                 TDB_DATA data;
2748                 time_t t;
2749
2750                 data = tdb_fetch(cache->tdb, string_tdb_data(cred->name));
2751                 if (!data.dptr) {
2752                         DEBUG(10,("wcache_remove_oldest_cached_creds: entry for [%s] not found\n", 
2753                                 cred->name));
2754                         status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
2755                         goto done;
2756                 }
2757         
2758                 t = IVAL(data.dptr, 0);
2759                 SAFE_FREE(data.dptr);
2760
2761                 if (!oldest) {
2762                         oldest = SMB_MALLOC_P(struct cred_list);
2763                         if (oldest == NULL) {
2764                                 status = NT_STATUS_NO_MEMORY;
2765                                 goto done;
2766                         }
2767
2768                         fstrcpy(oldest->name, cred->name);
2769                         oldest->created = t;
2770                         continue;
2771                 }
2772
2773                 if (t < oldest->created) {
2774                         fstrcpy(oldest->name, cred->name);
2775                         oldest->created = t;
2776                 }
2777         }
2778
2779         if (tdb_delete(cache->tdb, string_tdb_data(oldest->name)) == 0) {
2780                 status = NT_STATUS_OK;
2781         } else {
2782                 status = NT_STATUS_UNSUCCESSFUL;
2783         }
2784 done:
2785         SAFE_FREE(wcache_cred_list);
2786         SAFE_FREE(oldest);
2787         
2788         return status;
2789 }
2790
2791 /* Change the global online/offline state. */
2792 bool set_global_winbindd_state_offline(void)
2793 {
2794         TDB_DATA data;
2795
2796         DEBUG(10,("set_global_winbindd_state_offline: offline requested.\n"));
2797
2798         /* Only go offline if someone has created
2799            the key "WINBINDD_OFFLINE" in the cache tdb. */
2800
2801         if (wcache == NULL || wcache->tdb == NULL) {
2802                 DEBUG(10,("set_global_winbindd_state_offline: wcache not open yet.\n"));
2803                 return false;
2804         }
2805
2806         if (!lp_winbind_offline_logon()) {
2807                 DEBUG(10,("set_global_winbindd_state_offline: rejecting.\n"));
2808                 return false;
2809         }
2810
2811         if (global_winbindd_offline_state) {
2812                 /* Already offline. */
2813                 return true;
2814         }
2815
2816         data = tdb_fetch_bystring( wcache->tdb, "WINBINDD_OFFLINE" );
2817
2818         if (!data.dptr || data.dsize != 4) {
2819                 DEBUG(10,("set_global_winbindd_state_offline: offline state not set.\n"));
2820                 SAFE_FREE(data.dptr);
2821                 return false;
2822         } else {
2823                 DEBUG(10,("set_global_winbindd_state_offline: offline state set.\n"));
2824                 global_winbindd_offline_state = true;
2825                 SAFE_FREE(data.dptr);
2826                 return true;
2827         }
2828 }
2829
2830 void set_global_winbindd_state_online(void)
2831 {
2832         DEBUG(10,("set_global_winbindd_state_online: online requested.\n"));
2833
2834         if (!lp_winbind_offline_logon()) {
2835                 DEBUG(10,("set_global_winbindd_state_online: rejecting.\n"));
2836                 return;
2837         }
2838
2839         if (!global_winbindd_offline_state) {
2840                 /* Already online. */
2841                 return;
2842         }
2843         global_winbindd_offline_state = false;
2844
2845         if (!wcache->tdb) {
2846                 return;
2847         }
2848
2849         /* Ensure there is no key "WINBINDD_OFFLINE" in the cache tdb. */
2850         tdb_delete_bystring(wcache->tdb, "WINBINDD_OFFLINE");
2851 }
2852
2853 bool get_global_winbindd_state_offline(void)
2854 {
2855         return global_winbindd_offline_state;
2856 }
2857
2858 /***********************************************************************
2859  Validate functions for all possible cache tdb keys.
2860 ***********************************************************************/
2861
2862 static struct cache_entry *create_centry_validate(const char *kstr, TDB_DATA data, 
2863                                                   struct tdb_validation_status *state)
2864 {
2865         struct cache_entry *centry;
2866
2867         centry = SMB_XMALLOC_P(struct cache_entry);
2868         centry->data = (unsigned char *)memdup(data.dptr, data.dsize);
2869         if (!centry->data) {
2870                 SAFE_FREE(centry);
2871                 return NULL;
2872         }
2873         centry->len = data.dsize;
2874         centry->ofs = 0;
2875
2876         if (centry->len < 8) {
2877                 /* huh? corrupt cache? */
2878                 DEBUG(0,("create_centry_validate: Corrupt cache for key %s (len < 8) ?\n", kstr));
2879                 centry_free(centry);
2880                 state->bad_entry = true;
2881                 state->success = false;
2882                 return NULL;
2883         }
2884
2885         centry->status = NT_STATUS(centry_uint32(centry));
2886         centry->sequence_number = centry_uint32(centry);
2887         return centry;
2888 }
2889
2890 static int validate_seqnum(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
2891                            struct tdb_validation_status *state)
2892 {
2893         if (dbuf.dsize != 8) {
2894                 DEBUG(0,("validate_seqnum: Corrupt cache for key %s (len %u != 8) ?\n",
2895                                 keystr, (unsigned int)dbuf.dsize ));
2896                 state->bad_entry = true;
2897                 return 1;
2898         }
2899         return 0;
2900 }
2901
2902 static int validate_ns(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
2903                        struct tdb_validation_status *state)
2904 {
2905         struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
2906         if (!centry) {
2907                 return 1;
2908         }
2909
2910         (void)centry_uint32(centry);
2911         if (NT_STATUS_IS_OK(centry->status)) {
2912                 DOM_SID sid;
2913                 (void)centry_sid(centry, mem_ctx, &sid);
2914         }
2915
2916         centry_free(centry);
2917
2918         if (!(state->success)) {
2919                 return 1;
2920         }
2921         DEBUG(10,("validate_ns: %s ok\n", keystr));
2922         return 0;
2923 }
2924
2925 static int validate_sn(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
2926                        struct tdb_validation_status *state)
2927 {
2928         struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
2929         if (!centry) {
2930                 return 1;
2931         }
2932
2933         if (NT_STATUS_IS_OK(centry->status)) {
2934                 (void)centry_uint32(centry);
2935                 (void)centry_string(centry, mem_ctx);
2936                 (void)centry_string(centry, mem_ctx);
2937         }
2938
2939         centry_free(centry);
2940
2941         if (!(state->success)) {
2942                 return 1;
2943         }
2944         DEBUG(10,("validate_sn: %s ok\n", keystr));
2945         return 0;
2946 }
2947
2948 static int validate_u(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
2949                       struct tdb_validation_status *state)
2950 {
2951         struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
2952         DOM_SID sid;
2953
2954         if (!centry) {
2955                 return 1;
2956         }
2957
2958         (void)centry_string(centry, mem_ctx);
2959         (void)centry_string(centry, mem_ctx);
2960         (void)centry_string(centry, mem_ctx);
2961         (void)centry_string(centry, mem_ctx);
2962         (void)centry_uint32(centry);
2963         (void)centry_sid(centry, mem_ctx, &sid);
2964         (void)centry_sid(centry, mem_ctx, &sid);
2965
2966         centry_free(centry);
2967
2968         if (!(state->success)) {
2969                 return 1;
2970         }
2971         DEBUG(10,("validate_u: %s ok\n", keystr));
2972         return 0;
2973 }
2974
2975 static int validate_loc_pol(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
2976                             struct tdb_validation_status *state)
2977 {
2978         struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
2979
2980         if (!centry) {
2981                 return 1;
2982         }
2983
2984         (void)centry_nttime(centry);
2985         (void)centry_nttime(centry);
2986         (void)centry_uint16(centry);
2987
2988         centry_free(centry);
2989
2990         if (!(state->success)) {
2991                 return 1;
2992         }
2993         DEBUG(10,("validate_loc_pol: %s ok\n", keystr));
2994         return 0;
2995 }
2996
2997 static int validate_pwd_pol(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
2998                             struct tdb_validation_status *state)
2999 {
3000         struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3001
3002         if (!centry) {
3003                 return 1;
3004         }
3005
3006         (void)centry_uint16(centry);
3007         (void)centry_uint16(centry);
3008         (void)centry_uint32(centry);
3009         (void)centry_nttime(centry);
3010         (void)centry_nttime(centry);
3011
3012         centry_free(centry);
3013
3014         if (!(state->success)) {
3015                 return 1;
3016         }
3017         DEBUG(10,("validate_pwd_pol: %s ok\n", keystr));
3018         return 0;
3019 }
3020
3021 static int validate_cred(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3022                          struct tdb_validation_status *state)
3023 {
3024         struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3025
3026         if (!centry) {
3027                 return 1;
3028         }
3029
3030         (void)centry_time(centry);
3031         (void)centry_hash16(centry, mem_ctx);
3032
3033         /* We only have 17 bytes more data in the salted cred case. */
3034         if (centry->len - centry->ofs == 17) {
3035                 (void)centry_hash16(centry, mem_ctx);
3036         }
3037
3038         centry_free(centry);
3039
3040         if (!(state->success)) {
3041                 return 1;
3042         }
3043         DEBUG(10,("validate_cred: %s ok\n", keystr));
3044         return 0;
3045 }
3046
3047 static int validate_ul(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3048                        struct tdb_validation_status *state)
3049 {
3050         struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3051         int32 num_entries, i;
3052
3053         if (!centry) {
3054                 return 1;
3055         }
3056
3057         num_entries = (int32)centry_uint32(centry);
3058
3059         for (i=0; i< num_entries; i++) {
3060                 DOM_SID sid;
3061                 (void)centry_string(centry, mem_ctx);
3062                 (void)centry_string(centry, mem_ctx);
3063                 (void)centry_string(centry, mem_ctx);
3064                 (void)centry_string(centry, mem_ctx);
3065                 (void)centry_sid(centry, mem_ctx, &sid);
3066                 (void)centry_sid(centry, mem_ctx, &sid);
3067         }
3068
3069         centry_free(centry);
3070
3071         if (!(state->success)) {
3072                 return 1;
3073         }
3074         DEBUG(10,("validate_ul: %s ok\n", keystr));
3075         return 0;
3076 }
3077
3078 static int validate_gl(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3079                        struct tdb_validation_status *state)
3080 {
3081         struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3082         int32 num_entries, i;
3083
3084         if (!centry) {
3085                 return 1;
3086         }
3087
3088         num_entries = centry_uint32(centry);
3089         
3090         for (i=0; i< num_entries; i++) {
3091                 (void)centry_string(centry, mem_ctx);
3092                 (void)centry_string(centry, mem_ctx);
3093                 (void)centry_uint32(centry);
3094         }
3095
3096         centry_free(centry);
3097
3098         if (!(state->success)) {
3099                 return 1;
3100         }
3101         DEBUG(10,("validate_gl: %s ok\n", keystr));
3102         return 0;
3103 }
3104
3105 static int validate_ug(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3106                        struct tdb_validation_status *state)
3107 {
3108         struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3109         int32 num_groups, i;
3110
3111         if (!centry) {
3112                 return 1;
3113         }
3114
3115         num_groups = centry_uint32(centry);
3116
3117         for (i=0; i< num_groups; i++) {
3118                 DOM_SID sid;
3119                 centry_sid(centry, mem_ctx, &sid);
3120         }
3121
3122         centry_free(centry);
3123
3124         if (!(state->success)) {
3125                 return 1;
3126         }
3127         DEBUG(10,("validate_ug: %s ok\n", keystr));
3128         return 0;
3129 }
3130
3131 static int validate_ua(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3132                        struct tdb_validation_status *state)
3133 {
3134         struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3135         int32 num_aliases, i;
3136
3137         if (!centry) {
3138                 return 1;
3139         }
3140
3141         num_aliases = centry_uint32(centry);
3142
3143         for (i=0; i < num_aliases; i++) {
3144                 (void)centry_uint32(centry);
3145         }
3146
3147         centry_free(centry);
3148
3149         if (!(state->success)) {
3150                 return 1;
3151         }
3152         DEBUG(10,("validate_ua: %s ok\n", keystr));
3153         return 0;
3154 }
3155
3156 static int validate_gm(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3157                        struct tdb_validation_status *state)
3158 {
3159         struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3160         int32 num_names, i;
3161
3162         if (!centry) {
3163                 return 1;
3164         }
3165
3166         num_names = centry_uint32(centry);
3167
3168         for (i=0; i< num_names; i++) {
3169                 DOM_SID sid;
3170                 centry_sid(centry, mem_ctx, &sid);
3171                 (void)centry_string(centry, mem_ctx);
3172                 (void)centry_uint32(centry);
3173         }
3174
3175         centry_free(centry);
3176
3177         if (!(state->success)) {
3178                 return 1;
3179         }
3180         DEBUG(10,("validate_gm: %s ok\n", keystr));
3181         return 0;
3182 }
3183
3184 static int validate_dr(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3185                        struct tdb_validation_status *state)
3186 {
3187         /* Can't say anything about this other than must be nonzero. */
3188         if (dbuf.dsize == 0) {
3189                 DEBUG(0,("validate_dr: Corrupt cache for key %s (len == 0) ?\n",
3190                                 keystr));
3191                 state->bad_entry = true;
3192                 state->success = false;
3193                 return 1;
3194         }
3195
3196         DEBUG(10,("validate_dr: %s ok\n", keystr));
3197         return 0;
3198 }
3199
3200 static int validate_de(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3201                        struct tdb_validation_status *state)
3202 {
3203         /* Can't say anything about this other than must be nonzero. */
3204         if (dbuf.dsize == 0) {
3205                 DEBUG(0,("validate_de: Corrupt cache for key %s (len == 0) ?\n",
3206                                 keystr));
3207                 state->bad_entry = true;
3208                 state->success = false;
3209                 return 1;
3210         }
3211
3212         DEBUG(10,("validate_de: %s ok\n", keystr));
3213         return 0;
3214 }
3215
3216 static int validate_pwinfo(TALLOC_CTX *mem_ctx, const char *keystr,
3217                            TDB_DATA dbuf, struct tdb_validation_status *state)
3218 {
3219         struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3220
3221         if (!centry) {
3222                 return 1;
3223         }
3224
3225         (void)centry_string(centry, mem_ctx);
3226         (void)centry_string(centry, mem_ctx);
3227         (void)centry_string(centry, mem_ctx);
3228         (void)centry_uint32(centry);
3229
3230         centry_free(centry);
3231
3232         if (!(state->success)) {
3233                 return 1;
3234         }
3235         DEBUG(10,("validate_pwinfo: %s ok\n", keystr));
3236         return 0;
3237 }
3238
3239 static int validate_trustdoms(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3240                               struct tdb_validation_status *state)
3241 {
3242         struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3243         int32 num_domains, i;
3244
3245         if (!centry) {
3246                 return 1;
3247         }
3248
3249         num_domains = centry_uint32(centry);
3250         
3251         for (i=0; i< num_domains; i++) {
3252                 DOM_SID sid;
3253                 (void)centry_string(centry, mem_ctx);
3254                 (void)centry_string(centry, mem_ctx);
3255                 (void)centry_sid(centry, mem_ctx, &sid);
3256         }
3257
3258         centry_free(centry);
3259
3260         if (!(state->success)) {
3261                 return 1;
3262         }
3263         DEBUG(10,("validate_trustdoms: %s ok\n", keystr));
3264         return 0;
3265 }
3266
3267 static int validate_trustdomcache(TALLOC_CTX *mem_ctx, const char *keystr, 
3268                                   TDB_DATA dbuf,
3269                                   struct tdb_validation_status *state)
3270 {
3271         if (dbuf.dsize == 0) {
3272                 DEBUG(0, ("validate_trustdomcache: Corrupt cache for "
3273                           "key %s (len ==0) ?\n", keystr));
3274                 state->bad_entry = true;
3275                 state->success = false;
3276                 return 1;
3277         }
3278
3279         DEBUG(10,    ("validate_trustdomcache: %s ok\n", keystr));
3280         DEBUGADD(10, ("  Don't trust me, I am a DUMMY!\n"));
3281         return 0;
3282 }
3283
3284 static int validate_offline(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3285                             struct tdb_validation_status *state)
3286 {
3287         if (dbuf.dsize != 4) {
3288                 DEBUG(0,("validate_offline: Corrupt cache for key %s (len %u != 4) ?\n",
3289                                 keystr, (unsigned int)dbuf.dsize ));
3290                 state->bad_entry = true;
3291                 state->success = false;
3292                 return 1;
3293         }
3294         DEBUG(10,("validate_offline: %s ok\n", keystr));
3295         return 0;
3296 }
3297
3298 static int validate_cache_version(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3299                                   struct tdb_validation_status *state)
3300 {
3301         if (dbuf.dsize != 4) {
3302                 DEBUG(0, ("validate_cache_version: Corrupt cache for "
3303                           "key %s (len %u != 4) ?\n", 
3304                           keystr, (unsigned int)dbuf.dsize));
3305                 state->bad_entry = true;
3306                 state->success = false;
3307                 return 1;
3308         }
3309
3310         DEBUG(10, ("validate_cache_version: %s ok\n", keystr));
3311         return 0;
3312 }
3313
3314 /***********************************************************************
3315  A list of all possible cache tdb keys with associated validation
3316  functions.
3317 ***********************************************************************/
3318
3319 struct key_val_struct {
3320         const char *keyname;
3321         int (*validate_data_fn)(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf, struct tdb_validation_status* state);
3322 } key_val[] = {
3323         {"SEQNUM/", validate_seqnum},
3324         {"NS/", validate_ns},
3325         {"SN/", validate_sn},
3326         {"U/", validate_u},
3327         {"LOC_POL/", validate_loc_pol},
3328         {"PWD_POL/", validate_pwd_pol},
3329         {"CRED/", validate_cred},
3330         {"UL/", validate_ul},
3331         {"GL/", validate_gl},
3332         {"UG/", validate_ug},
3333         {"UA", validate_ua},
3334         {"GM/", validate_gm},
3335         {"DR/", validate_dr},
3336         {"DE/", validate_de},
3337         {"NSS/PWINFO/", validate_pwinfo},
3338         {"TRUSTDOMS/", validate_trustdoms},
3339         {"TRUSTDOMCACHE/", validate_trustdomcache},
3340         {"WINBINDD_OFFLINE", validate_offline},
3341         {WINBINDD_CACHE_VERSION_KEYSTR, validate_cache_version},
3342         {NULL, NULL}
3343 };
3344
3345 /***********************************************************************
3346  Function to look at every entry in the tdb and validate it as far as
3347  possible.
3348 ***********************************************************************/
3349
3350 static int cache_traverse_validate_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf, void *state)
3351 {
3352         int i;
3353         unsigned int max_key_len = 1024;
3354         struct tdb_validation_status *v_state = (struct tdb_validation_status *)state;
3355
3356         /* Paranoia check. */
3357         if (strncmp("UA/", (const char *)kbuf.dptr, 3) == 0) {
3358                 max_key_len = 1024 * 1024;
3359         }
3360         if (kbuf.dsize > max_key_len) {
3361                 DEBUG(0, ("cache_traverse_validate_fn: key length too large: "
3362                           "(%u) > (%u)\n\n",
3363                           (unsigned int)kbuf.dsize, (unsigned int)max_key_len));
3364                 return 1;
3365         }
3366
3367         for (i = 0; key_val[i].keyname; i++) {
3368                 size_t namelen = strlen(key_val[i].keyname);
3369                 if (kbuf.dsize >= namelen && (
3370                                 strncmp(key_val[i].keyname, (const char *)kbuf.dptr, namelen)) == 0) {
3371                         TALLOC_CTX *mem_ctx;
3372                         char *keystr;
3373                         int ret;
3374
3375                         keystr = SMB_MALLOC_ARRAY(char, kbuf.dsize+1);
3376                         if (!keystr) {
3377                                 return 1;
3378                         }
3379                         memcpy(keystr, kbuf.dptr, kbuf.dsize);
3380                         keystr[kbuf.dsize] = '\0';
3381
3382                         mem_ctx = talloc_init("validate_ctx");
3383                         if (!mem_ctx) {
3384                                 SAFE_FREE(keystr);
3385                                 return 1;
3386                         }
3387
3388                         ret = key_val[i].validate_data_fn(mem_ctx, keystr, dbuf, 
3389                                                           v_state);
3390
3391                         SAFE_FREE(keystr);
3392                         talloc_destroy(mem_ctx);
3393                         return ret;
3394                 }
3395         }
3396
3397         DEBUG(0,("cache_traverse_validate_fn: unknown cache entry\nkey :\n"));
3398         dump_data(0, (uint8 *)kbuf.dptr, kbuf.dsize);
3399         DEBUG(0,("data :\n"));
3400         dump_data(0, (uint8 *)dbuf.dptr, dbuf.dsize);
3401         v_state->unknown_key = true;
3402         v_state->success = false;
3403         return 1; /* terminate. */
3404 }
3405
3406 static void validate_panic(const char *const why)
3407 {
3408         DEBUG(0,("validating cache: would panic %s\n", why ));
3409         DEBUGADD(0, ("exiting instead (cache validation mode)\n"));
3410         exit(47);
3411 }
3412
3413 /***********************************************************************
3414  Try and validate every entry in the winbindd cache. If we fail here,
3415  delete the cache tdb and return non-zero.
3416 ***********************************************************************/
3417
3418 int winbindd_validate_cache(void)
3419 {
3420         int ret = -1;
3421         const char *tdb_path = lock_path("winbindd_cache.tdb");
3422         TDB_CONTEXT *tdb = NULL;
3423
3424         DEBUG(10, ("winbindd_validate_cache: replacing panic function\n"));
3425         smb_panic_fn = validate_panic;
3426
3427
3428         tdb = tdb_open_log(tdb_path, 
3429                            WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
3430                            ( lp_winbind_offline_logon() 
3431                              ? TDB_DEFAULT 
3432                              : TDB_DEFAULT | TDB_CLEAR_IF_FIRST ),
3433                            O_RDWR|O_CREAT, 
3434                            0600);
3435         if (!tdb) {
3436                 DEBUG(0, ("winbindd_validate_cache: "
3437                           "error opening/initializing tdb\n"));
3438                 goto done;
3439         }
3440         tdb_close(tdb);
3441
3442         ret = tdb_validate_and_backup(tdb_path, cache_traverse_validate_fn);
3443
3444         if (ret != 0) {
3445                 DEBUG(10, ("winbindd_validate_cache: validation not successful.\n"));
3446                 DEBUGADD(10, ("removing tdb %s.\n", tdb_path));
3447                 unlink(tdb_path);
3448         }
3449
3450 done:
3451         DEBUG(10, ("winbindd_validate_cache: restoring panic function\n"));
3452         smb_panic_fn = smb_panic;
3453         return ret;
3454 }
3455
3456 /***********************************************************************
3457  Try and validate every entry in the winbindd cache.
3458 ***********************************************************************/
3459
3460 int winbindd_validate_cache_nobackup(void)
3461 {
3462         int ret = -1;
3463         const char *tdb_path = lock_path("winbindd_cache.tdb");
3464
3465         DEBUG(10, ("winbindd_validate_cache: replacing panic function\n"));
3466         smb_panic_fn = validate_panic;
3467
3468
3469         if (wcache == NULL || wcache->tdb == NULL) {
3470                 ret = tdb_validate_open(tdb_path, cache_traverse_validate_fn);
3471         } else {
3472                 ret = tdb_validate(wcache->tdb, cache_traverse_validate_fn);
3473         }
3474
3475         if (ret != 0) {
3476                 DEBUG(10, ("winbindd_validate_cache_nobackup: validation not "
3477                            "successful.\n"));
3478         }
3479
3480         DEBUG(10, ("winbindd_validate_cache_nobackup: restoring panic "
3481                    "function\n"));
3482         smb_panic_fn = smb_panic;
3483         return ret;
3484 }
3485
3486 bool winbindd_cache_validate_and_initialize(void)
3487 {
3488         close_winbindd_cache();
3489
3490         if (lp_winbind_offline_logon()) {
3491                 if (winbindd_validate_cache() < 0) {
3492                         DEBUG(0, ("winbindd cache tdb corrupt and no backup "
3493                                   "could be restored.\n"));
3494                 }
3495         }
3496
3497         return initialize_winbindd_cache();
3498 }
3499
3500 /*********************************************************************
3501  ********************************************************************/
3502
3503 static bool add_wbdomain_to_tdc_array( struct winbindd_domain *new_dom,
3504                                        struct winbindd_tdc_domain **domains, 
3505                                        size_t *num_domains )
3506 {
3507         struct winbindd_tdc_domain *list = NULL;
3508         size_t idx;
3509         int i;
3510         bool set_only = false;
3511         
3512         /* don't allow duplicates */
3513
3514         idx = *num_domains;
3515         list = *domains;
3516         
3517         for ( i=0; i< (*num_domains); i++ ) {
3518                 if ( strequal( new_dom->name, list[i].domain_name ) ) {
3519                         DEBUG(10,("add_wbdomain_to_tdc_array: Found existing record for %s\n",
3520                                   new_dom->name));
3521                         idx = i;
3522                         set_only = true;
3523                         
3524                         break;
3525                 }
3526         }
3527
3528         if ( !set_only ) {
3529                 if ( !*domains ) {
3530                         list = TALLOC_ARRAY( NULL, struct winbindd_tdc_domain, 1 );
3531                         idx = 0;
3532                 } else {
3533                         list = TALLOC_REALLOC_ARRAY( *domains, *domains, 
3534                                                      struct winbindd_tdc_domain,  
3535                                                      (*num_domains)+1);
3536                         idx = *num_domains;             
3537                 }
3538
3539                 ZERO_STRUCT( list[idx] );
3540         }
3541
3542         if ( !list )
3543                 return false;
3544
3545         list[idx].domain_name = talloc_strdup( list, new_dom->name );
3546         list[idx].dns_name = talloc_strdup( list, new_dom->alt_name );
3547
3548         if ( !is_null_sid( &new_dom->sid ) ) {
3549                 sid_copy( &list[idx].sid, &new_dom->sid );
3550         } else {
3551                 sid_copy(&list[idx].sid, &global_sid_NULL);
3552         }
3553
3554         if ( new_dom->domain_flags != 0x0 )
3555                 list[idx].trust_flags = new_dom->domain_flags;  
3556
3557         if ( new_dom->domain_type != 0x0 )
3558                 list[idx].trust_type = new_dom->domain_type;
3559
3560         if ( new_dom->domain_trust_attribs != 0x0 )
3561                 list[idx].trust_attribs = new_dom->domain_trust_attribs;
3562         
3563         if ( !set_only ) {
3564                 *domains = list;
3565                 *num_domains = idx + 1; 
3566         }
3567
3568         return true;
3569 }
3570
3571 /*********************************************************************
3572  ********************************************************************/
3573
3574 static TDB_DATA make_tdc_key( const char *domain_name )
3575 {
3576         char *keystr = NULL;
3577         TDB_DATA key = { NULL, 0 };
3578         
3579         if ( !domain_name ) {
3580                 DEBUG(5,("make_tdc_key: Keyname workgroup is NULL!\n"));
3581                 return key;
3582         }
3583                
3584                 
3585         asprintf( &keystr, "TRUSTDOMCACHE/%s", domain_name );
3586         key = string_term_tdb_data(keystr);
3587         
3588         return key;     
3589 }
3590
3591 /*********************************************************************
3592  ********************************************************************/
3593
3594 static int pack_tdc_domains( struct winbindd_tdc_domain *domains, 
3595                              size_t num_domains,
3596                              unsigned char **buf )
3597 {
3598         unsigned char *buffer = NULL;
3599         int len = 0;
3600         int buflen = 0;
3601         int i = 0;
3602
3603         DEBUG(10,("pack_tdc_domains: Packing %d trusted domains\n",
3604                   (int)num_domains));
3605         
3606         buflen = 0;
3607         
3608  again: 
3609         len = 0;
3610         
3611         /* Store the number of array items first */
3612         len += tdb_pack( buffer+len, buflen-len, "d", 
3613                          num_domains );
3614
3615         /* now pack each domain trust record */
3616         for ( i=0; i<num_domains; i++ ) {
3617
3618                 fstring tmp;
3619
3620                 if ( buflen > 0 ) {
3621                         DEBUG(10,("pack_tdc_domains: Packing domain %s (%s)\n",
3622                                   domains[i].domain_name,
3623                                   domains[i].dns_name ? domains[i].dns_name : "UNKNOWN" ));
3624                 }
3625                 
3626                 len += tdb_pack( buffer+len, buflen-len, "fffddd",
3627                                  domains[i].domain_name,
3628                                  domains[i].dns_name,
3629                                  sid_to_fstring(tmp, &domains[i].sid),
3630                                  domains[i].trust_flags,
3631                                  domains[i].trust_attribs,
3632                                  domains[i].trust_type );
3633         }
3634
3635         if ( buflen < len ) {
3636                 SAFE_FREE(buffer);
3637                 if ( (buffer = SMB_MALLOC_ARRAY(unsigned char, len)) == NULL ) {
3638                         DEBUG(0,("pack_tdc_domains: failed to alloc buffer!\n"));
3639                         buflen = -1;
3640                         goto done;
3641                 }
3642                 buflen = len;
3643                 goto again;
3644         }
3645
3646         *buf = buffer;  
3647         
3648  done:  
3649         return buflen;  
3650 }
3651
3652 /*********************************************************************
3653  ********************************************************************/
3654
3655 static size_t unpack_tdc_domains( unsigned char *buf, int buflen, 
3656                                   struct winbindd_tdc_domain **domains )
3657 {
3658         fstring domain_name, dns_name, sid_string;      
3659         uint32 type, attribs, flags;
3660         int num_domains;
3661         int len = 0;
3662         int i;
3663         struct winbindd_tdc_domain *list = NULL;
3664
3665         /* get the number of domains */
3666         len += tdb_unpack( buf+len, buflen-len, "d", &num_domains);
3667         if ( len == -1 ) {
3668                 DEBUG(5,("unpack_tdc_domains: Failed to unpack domain array\n"));               
3669                 return 0;
3670         }
3671
3672         list = TALLOC_ARRAY( NULL, struct winbindd_tdc_domain, num_domains );
3673         if ( !list ) {
3674                 DEBUG(0,("unpack_tdc_domains: Failed to talloc() domain list!\n"));
3675                 return 0;               
3676         }
3677         
3678         for ( i=0; i<num_domains; i++ ) {
3679                 len += tdb_unpack( buf+len, buflen-len, "fffddd",
3680                                    domain_name,
3681                                    dns_name,
3682                                    sid_string,
3683                                    &flags,
3684                                    &attribs,
3685                                    &type );
3686
3687                 if ( len == -1 ) {
3688                         DEBUG(5,("unpack_tdc_domains: Failed to unpack domain array\n"));
3689                         TALLOC_FREE( list );                    
3690                         return 0;
3691                 }
3692
3693                 DEBUG(11,("unpack_tdc_domains: Unpacking domain %s (%s) "
3694                           "SID %s, flags = 0x%x, attribs = 0x%x, type = 0x%x\n",
3695                           domain_name, dns_name, sid_string,
3696                           flags, attribs, type));
3697                 
3698                 list[i].domain_name = talloc_strdup( list, domain_name );
3699                 list[i].dns_name = talloc_strdup( list, dns_name );
3700                 if ( !string_to_sid( &(list[i].sid), sid_string ) ) {                   
3701                         DEBUG(10,("unpack_tdc_domains: no SID for domain %s\n",
3702                                   domain_name));
3703                 }
3704                 list[i].trust_flags = flags;
3705                 list[i].trust_attribs = attribs;
3706                 list[i].trust_type = type;
3707         }
3708
3709         *domains = list;
3710         
3711         return num_domains;
3712 }
3713
3714 /*********************************************************************
3715  ********************************************************************/
3716
3717 static bool wcache_tdc_store_list( struct winbindd_tdc_domain *domains, size_t num_domains )
3718 {
3719         TDB_DATA key = make_tdc_key( lp_workgroup() );   
3720         TDB_DATA data = { NULL, 0 };
3721         int ret;
3722         
3723         if ( !key.dptr )
3724                 return false;
3725         
3726         /* See if we were asked to delete the cache entry */
3727
3728         if ( !domains ) {
3729                 ret = tdb_delete( wcache->tdb, key );
3730                 goto done;
3731         }
3732         
3733         data.dsize = pack_tdc_domains( domains, num_domains, &data.dptr );
3734         
3735         if ( !data.dptr ) {
3736                 ret = -1;
3737                 goto done;
3738         }
3739                 
3740         ret = tdb_store( wcache->tdb, key, data, 0 );
3741
3742  done:
3743         SAFE_FREE( data.dptr );
3744         SAFE_FREE( key.dptr );
3745         
3746         return ( ret != -1 );   
3747 }
3748
3749 /*********************************************************************
3750  ********************************************************************/
3751
3752 bool wcache_tdc_fetch_list( struct winbindd_tdc_domain **domains, size_t *num_domains )
3753 {
3754         TDB_DATA key = make_tdc_key( lp_workgroup() );
3755         TDB_DATA data = { NULL, 0 };
3756
3757         *domains = NULL;        
3758         *num_domains = 0;       
3759
3760         if ( !key.dptr )
3761                 return false;
3762         
3763         data = tdb_fetch( wcache->tdb, key );
3764
3765         SAFE_FREE( key.dptr );
3766         
3767         if ( !data.dptr ) 
3768                 return false;
3769         
3770         *num_domains = unpack_tdc_domains( data.dptr, data.dsize, domains );
3771
3772         SAFE_FREE( data.dptr );
3773         
3774         if ( !*domains )
3775                 return false;
3776
3777         return true;
3778 }
3779
3780 /*********************************************************************
3781  ********************************************************************/
3782
3783 bool wcache_tdc_add_domain( struct winbindd_domain *domain )
3784 {
3785         struct winbindd_tdc_domain *dom_list = NULL;
3786         size_t num_domains = 0;
3787         bool ret = false;
3788
3789         DEBUG(10,("wcache_tdc_add_domain: Adding domain %s (%s), SID %s, "
3790                   "flags = 0x%x, attributes = 0x%x, type = 0x%x\n",
3791                   domain->name, domain->alt_name, 
3792                   sid_string_dbg(&domain->sid),
3793                   domain->domain_flags,
3794                   domain->domain_trust_attribs,
3795                   domain->domain_type));        
3796         
3797         if ( !init_wcache() ) {
3798                 return false;
3799         }
3800         
3801         /* fetch the list */
3802
3803         wcache_tdc_fetch_list( &dom_list, &num_domains );
3804         
3805         /* add the new domain */
3806
3807         if ( !add_wbdomain_to_tdc_array( domain, &dom_list, &num_domains ) ) {
3808                 goto done;              
3809         }       
3810
3811         /* pack the domain */
3812
3813         if ( !wcache_tdc_store_list( dom_list, num_domains ) ) {
3814                 goto done;              
3815         }
3816         
3817         /* Success */
3818
3819         ret = true;
3820  done:
3821         TALLOC_FREE( dom_list );
3822         
3823         return ret;     
3824 }
3825
3826 /*********************************************************************
3827  ********************************************************************/
3828
3829 struct winbindd_tdc_domain * wcache_tdc_fetch_domain( TALLOC_CTX *ctx, const char *name )
3830 {
3831         struct winbindd_tdc_domain *dom_list = NULL;
3832         size_t num_domains = 0;
3833         int i;
3834         struct winbindd_tdc_domain *d = NULL;   
3835
3836         DEBUG(10,("wcache_tdc_fetch_domain: Searching for domain %s\n", name));
3837
3838         if ( !init_wcache() ) {
3839                 return false;
3840         }
3841         
3842         /* fetch the list */
3843
3844         wcache_tdc_fetch_list( &dom_list, &num_domains );
3845         
3846         for ( i=0; i<num_domains; i++ ) {
3847                 if ( strequal(name, dom_list[i].domain_name) ||
3848                      strequal(name, dom_list[i].dns_name) )
3849                 {
3850                         DEBUG(10,("wcache_tdc_fetch_domain: Found domain %s\n",
3851                                   name));
3852                         
3853                         d = TALLOC_P( ctx, struct winbindd_tdc_domain );
3854                         if ( !d )
3855                                 break;                  
3856                         
3857                         d->domain_name = talloc_strdup( d, dom_list[i].domain_name );
3858                         d->dns_name = talloc_strdup( d, dom_list[i].dns_name );
3859                         sid_copy( &d->sid, &dom_list[i].sid );
3860                         d->trust_flags   = dom_list[i].trust_flags;
3861                         d->trust_type    = dom_list[i].trust_type;
3862                         d->trust_attribs = dom_list[i].trust_attribs;
3863
3864                         break;
3865                 }
3866         }
3867
3868         TALLOC_FREE( dom_list );
3869         
3870         return d;       
3871 }
3872
3873
3874 /*********************************************************************
3875  ********************************************************************/
3876
3877 void wcache_tdc_clear( void )
3878 {
3879         if ( !init_wcache() )
3880                 return;
3881
3882         wcache_tdc_store_list( NULL, 0 );
3883         
3884         return; 
3885 }
3886
3887
3888 /*********************************************************************
3889  ********************************************************************/
3890
3891 static void wcache_save_user_pwinfo(struct winbindd_domain *domain, 
3892                                     NTSTATUS status,
3893                                     const DOM_SID *user_sid,
3894                                     const char *homedir,
3895                                     const char *shell,
3896                                     const char *gecos,
3897                                     uint32 gid)
3898 {
3899         struct cache_entry *centry;
3900         fstring tmp;
3901
3902         if ( (centry = centry_start(domain, status)) == NULL )
3903                 return;
3904
3905         centry_put_string( centry, homedir );
3906         centry_put_string( centry, shell );
3907         centry_put_string( centry, gecos );
3908         centry_put_uint32( centry, gid );
3909         
3910         centry_end(centry, "NSS/PWINFO/%s", sid_to_fstring(tmp, user_sid) );
3911
3912         DEBUG(10,("wcache_save_user_pwinfo: %s\n", sid_string_dbg(user_sid) ));
3913
3914         centry_free(centry);
3915 }
3916
3917 NTSTATUS nss_get_info_cached( struct winbindd_domain *domain, 
3918                               const DOM_SID *user_sid,
3919                               TALLOC_CTX *ctx,
3920                               ADS_STRUCT *ads, LDAPMessage *msg,
3921                               char **homedir, char **shell, char **gecos,
3922                               gid_t *p_gid)
3923 {
3924         struct winbind_cache *cache = get_cache(domain);
3925         struct cache_entry *centry = NULL;
3926         NTSTATUS nt_status;
3927         fstring tmp;
3928
3929         if (!cache->tdb)
3930                 goto do_query;
3931
3932         centry = wcache_fetch(cache, domain, "NSS/PWINFO/%s",
3933                               sid_to_fstring(tmp, user_sid));
3934         
3935         if (!centry)
3936                 goto do_query;
3937
3938         *homedir = centry_string( centry, ctx );
3939         *shell   = centry_string( centry, ctx );
3940         *gecos   = centry_string( centry, ctx );
3941         *p_gid   = centry_uint32( centry );     
3942
3943         centry_free(centry);
3944
3945         DEBUG(10,("nss_get_info_cached: [Cached] - user_sid %s\n",
3946                   sid_string_dbg(user_sid)));
3947
3948         return NT_STATUS_OK;
3949
3950 do_query:
3951         
3952         nt_status = nss_get_info( domain->name, user_sid, ctx, ads, msg, 
3953                                   homedir, shell, gecos, p_gid );
3954
3955         if ( NT_STATUS_IS_OK(nt_status) ) {
3956                 wcache_save_user_pwinfo( domain, nt_status, user_sid,
3957                                          *homedir, *shell, *gecos, *p_gid );
3958         }       
3959
3960         if ( NT_STATUS_EQUAL( nt_status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND ) ) {
3961                 DEBUG(5,("nss_get_info_cached: Setting domain %s offline\n",
3962                          domain->name ));
3963                 set_domain_offline( domain );
3964         }
3965
3966         return nt_status;       
3967 }
3968
3969
3970 /* the cache backend methods are exposed via this structure */
3971 struct winbindd_methods cache_methods = {
3972         true,
3973         query_user_list,
3974         enum_dom_groups,
3975         enum_local_groups,
3976         name_to_sid,
3977         sid_to_name,
3978         rids_to_names,
3979         query_user,
3980         lookup_usergroups,
3981         lookup_useraliases,
3982         lookup_groupmem,
3983         sequence_number,
3984         lockout_policy,
3985         password_policy,
3986         trusted_domains
3987 };