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