r24722: Squashed commit of the following:
[tprouty/samba.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         dump_data_pw("cached_nt_pass", *cached_nt_pass, NT_HASH_LEN);
1019         if (*cached_salt) {
1020                 dump_data_pw("cached_salt", *cached_salt, NT_HASH_LEN);
1021         }
1022
1023         status = centry->status;
1024
1025         DEBUG(10,("wcache_get_creds: [Cached] - cached creds for user %s status: %s\n",
1026                 sid_string_static(sid), nt_errstr(status) ));
1027
1028         centry_free(centry);
1029         return status;
1030 }
1031
1032 /* Store creds for a SID - only writes out new salted ones. */
1033
1034 NTSTATUS wcache_save_creds(struct winbindd_domain *domain, 
1035                            TALLOC_CTX *mem_ctx, 
1036                            const DOM_SID *sid, 
1037                            const uint8 nt_pass[NT_HASH_LEN])
1038 {
1039         struct cache_entry *centry;
1040         fstring sid_string;
1041         uint32 rid;
1042         uint8 cred_salt[NT_HASH_LEN];
1043         uint8 salted_hash[NT_HASH_LEN];
1044
1045         if (is_null_sid(sid)) {
1046                 return NT_STATUS_INVALID_SID;
1047         }
1048
1049         if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
1050                 return NT_STATUS_INVALID_SID;
1051         }
1052
1053         centry = centry_start(domain, NT_STATUS_OK);
1054         if (!centry) {
1055                 return NT_STATUS_INTERNAL_DB_ERROR;
1056         }
1057
1058         dump_data_pw("nt_pass", nt_pass, NT_HASH_LEN);
1059
1060         centry_put_time(centry, time(NULL));
1061
1062         /* Create a salt and then salt the hash. */
1063         generate_random_buffer(cred_salt, NT_HASH_LEN);
1064         E_md5hash(cred_salt, nt_pass, salted_hash);
1065
1066         centry_put_hash16(centry, salted_hash);
1067         centry_put_hash16(centry, cred_salt);
1068         centry_end(centry, "CRED/%s", sid_to_string(sid_string, sid));
1069
1070         DEBUG(10,("wcache_save_creds: %s\n", sid_string));
1071
1072         centry_free(centry);
1073
1074         return NT_STATUS_OK;
1075 }
1076
1077
1078 /* Query display info. This is the basic user list fn */
1079 static NTSTATUS query_user_list(struct winbindd_domain *domain,
1080                                 TALLOC_CTX *mem_ctx,
1081                                 uint32 *num_entries, 
1082                                 WINBIND_USERINFO **info)
1083 {
1084         struct winbind_cache *cache = get_cache(domain);
1085         struct cache_entry *centry = NULL;
1086         NTSTATUS status;
1087         unsigned int i, retry;
1088
1089         if (!cache->tdb)
1090                 goto do_query;
1091
1092         centry = wcache_fetch(cache, domain, "UL/%s", domain->name);
1093         if (!centry)
1094                 goto do_query;
1095
1096         *num_entries = centry_uint32(centry);
1097         
1098         if (*num_entries == 0)
1099                 goto do_cached;
1100
1101         (*info) = TALLOC_ARRAY(mem_ctx, WINBIND_USERINFO, *num_entries);
1102         if (! (*info)) {
1103                 smb_panic_fn("query_user_list out of memory");
1104         }
1105         for (i=0; i<(*num_entries); i++) {
1106                 (*info)[i].acct_name = centry_string(centry, mem_ctx);
1107                 (*info)[i].full_name = centry_string(centry, mem_ctx);
1108                 (*info)[i].homedir = centry_string(centry, mem_ctx);
1109                 (*info)[i].shell = centry_string(centry, mem_ctx);
1110                 centry_sid(centry, mem_ctx, &(*info)[i].user_sid);
1111                 centry_sid(centry, mem_ctx, &(*info)[i].group_sid);
1112         }
1113
1114 do_cached:      
1115         status = centry->status;
1116
1117         DEBUG(10,("query_user_list: [Cached] - cached list for domain %s status: %s\n",
1118                 domain->name, nt_errstr(status) ));
1119
1120         centry_free(centry);
1121         return status;
1122
1123 do_query:
1124         *num_entries = 0;
1125         *info = NULL;
1126
1127         /* Return status value returned by seq number check */
1128
1129         if (!NT_STATUS_IS_OK(domain->last_status))
1130                 return domain->last_status;
1131
1132         /* Put the query_user_list() in a retry loop.  There appears to be
1133          * some bug either with Windows 2000 or Samba's handling of large
1134          * rpc replies.  This manifests itself as sudden disconnection
1135          * at a random point in the enumeration of a large (60k) user list.
1136          * The retry loop simply tries the operation again. )-:  It's not
1137          * pretty but an acceptable workaround until we work out what the
1138          * real problem is. */
1139
1140         retry = 0;
1141         do {
1142
1143                 DEBUG(10,("query_user_list: [Cached] - doing backend query for list for domain %s\n",
1144                         domain->name ));
1145
1146                 status = domain->backend->query_user_list(domain, mem_ctx, num_entries, info);
1147                 if (!NT_STATUS_IS_OK(status)) {
1148                         DEBUG(3, ("query_user_list: returned 0x%08x, "
1149                                   "retrying\n", NT_STATUS_V(status)));
1150                 }
1151                 if (NT_STATUS_EQUAL(status, NT_STATUS_UNSUCCESSFUL)) {
1152                         DEBUG(3, ("query_user_list: flushing "
1153                                   "connection cache\n"));
1154                         invalidate_cm_connection(&domain->conn);
1155                 }
1156
1157         } while (NT_STATUS_V(status) == NT_STATUS_V(NT_STATUS_UNSUCCESSFUL) && 
1158                  (retry++ < 5));
1159
1160         /* and save it */
1161         refresh_sequence_number(domain, False);
1162         centry = centry_start(domain, status);
1163         if (!centry)
1164                 goto skip_save;
1165         centry_put_uint32(centry, *num_entries);
1166         for (i=0; i<(*num_entries); i++) {
1167                 centry_put_string(centry, (*info)[i].acct_name);
1168                 centry_put_string(centry, (*info)[i].full_name);
1169                 centry_put_string(centry, (*info)[i].homedir);
1170                 centry_put_string(centry, (*info)[i].shell);
1171                 centry_put_sid(centry, &(*info)[i].user_sid);
1172                 centry_put_sid(centry, &(*info)[i].group_sid);
1173                 if (domain->backend && domain->backend->consistent) {
1174                         /* when the backend is consistent we can pre-prime some mappings */
1175                         wcache_save_name_to_sid(domain, NT_STATUS_OK, 
1176                                                 domain->name,
1177                                                 (*info)[i].acct_name, 
1178                                                 &(*info)[i].user_sid,
1179                                                 SID_NAME_USER);
1180                         wcache_save_sid_to_name(domain, NT_STATUS_OK, 
1181                                                 &(*info)[i].user_sid,
1182                                                 domain->name,
1183                                                 (*info)[i].acct_name, 
1184                                                 SID_NAME_USER);
1185                         wcache_save_user(domain, NT_STATUS_OK, &(*info)[i]);
1186                 }
1187         }       
1188         centry_end(centry, "UL/%s", domain->name);
1189         centry_free(centry);
1190
1191 skip_save:
1192         return status;
1193 }
1194
1195 /* list all domain groups */
1196 static NTSTATUS enum_dom_groups(struct winbindd_domain *domain,
1197                                 TALLOC_CTX *mem_ctx,
1198                                 uint32 *num_entries, 
1199                                 struct acct_info **info)
1200 {
1201         struct winbind_cache *cache = get_cache(domain);
1202         struct cache_entry *centry = NULL;
1203         NTSTATUS status;
1204         unsigned int i;
1205
1206         if (!cache->tdb)
1207                 goto do_query;
1208
1209         centry = wcache_fetch(cache, domain, "GL/%s/domain", domain->name);
1210         if (!centry)
1211                 goto do_query;
1212
1213         *num_entries = centry_uint32(centry);
1214         
1215         if (*num_entries == 0)
1216                 goto do_cached;
1217
1218         (*info) = TALLOC_ARRAY(mem_ctx, struct acct_info, *num_entries);
1219         if (! (*info)) {
1220                 smb_panic_fn("enum_dom_groups out of memory");
1221         }
1222         for (i=0; i<(*num_entries); i++) {
1223                 fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx));
1224                 fstrcpy((*info)[i].acct_desc, centry_string(centry, mem_ctx));
1225                 (*info)[i].rid = centry_uint32(centry);
1226         }
1227
1228 do_cached:      
1229         status = centry->status;
1230
1231         DEBUG(10,("enum_dom_groups: [Cached] - cached list for domain %s status: %s\n",
1232                 domain->name, nt_errstr(status) ));
1233
1234         centry_free(centry);
1235         return status;
1236
1237 do_query:
1238         *num_entries = 0;
1239         *info = NULL;
1240
1241         /* Return status value returned by seq number check */
1242
1243         if (!NT_STATUS_IS_OK(domain->last_status))
1244                 return domain->last_status;
1245
1246         DEBUG(10,("enum_dom_groups: [Cached] - doing backend query for list for domain %s\n",
1247                 domain->name ));
1248
1249         status = domain->backend->enum_dom_groups(domain, mem_ctx, num_entries, info);
1250
1251         /* and save it */
1252         refresh_sequence_number(domain, False);
1253         centry = centry_start(domain, status);
1254         if (!centry)
1255                 goto skip_save;
1256         centry_put_uint32(centry, *num_entries);
1257         for (i=0; i<(*num_entries); i++) {
1258                 centry_put_string(centry, (*info)[i].acct_name);
1259                 centry_put_string(centry, (*info)[i].acct_desc);
1260                 centry_put_uint32(centry, (*info)[i].rid);
1261         }       
1262         centry_end(centry, "GL/%s/domain", domain->name);
1263         centry_free(centry);
1264
1265 skip_save:
1266         return status;
1267 }
1268
1269 /* list all domain groups */
1270 static NTSTATUS enum_local_groups(struct winbindd_domain *domain,
1271                                 TALLOC_CTX *mem_ctx,
1272                                 uint32 *num_entries, 
1273                                 struct acct_info **info)
1274 {
1275         struct winbind_cache *cache = get_cache(domain);
1276         struct cache_entry *centry = NULL;
1277         NTSTATUS status;
1278         unsigned int i;
1279
1280         if (!cache->tdb)
1281                 goto do_query;
1282
1283         centry = wcache_fetch(cache, domain, "GL/%s/local", domain->name);
1284         if (!centry)
1285                 goto do_query;
1286
1287         *num_entries = centry_uint32(centry);
1288         
1289         if (*num_entries == 0)
1290                 goto do_cached;
1291
1292         (*info) = TALLOC_ARRAY(mem_ctx, struct acct_info, *num_entries);
1293         if (! (*info)) {
1294                 smb_panic_fn("enum_dom_groups out of memory");
1295         }
1296         for (i=0; i<(*num_entries); i++) {
1297                 fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx));
1298                 fstrcpy((*info)[i].acct_desc, centry_string(centry, mem_ctx));
1299                 (*info)[i].rid = centry_uint32(centry);
1300         }
1301
1302 do_cached:      
1303
1304         /* If we are returning cached data and the domain controller
1305            is down then we don't know whether the data is up to date
1306            or not.  Return NT_STATUS_MORE_PROCESSING_REQUIRED to
1307            indicate this. */
1308
1309         if (wcache_server_down(domain)) {
1310                 DEBUG(10, ("enum_local_groups: returning cached user list and server was down\n"));
1311                 status = NT_STATUS_MORE_PROCESSING_REQUIRED;
1312         } else
1313                 status = centry->status;
1314
1315         DEBUG(10,("enum_local_groups: [Cached] - cached list for domain %s status: %s\n",
1316                 domain->name, nt_errstr(status) ));
1317
1318         centry_free(centry);
1319         return status;
1320
1321 do_query:
1322         *num_entries = 0;
1323         *info = NULL;
1324
1325         /* Return status value returned by seq number check */
1326
1327         if (!NT_STATUS_IS_OK(domain->last_status))
1328                 return domain->last_status;
1329
1330         DEBUG(10,("enum_local_groups: [Cached] - doing backend query for list for domain %s\n",
1331                 domain->name ));
1332
1333         status = domain->backend->enum_local_groups(domain, mem_ctx, num_entries, info);
1334
1335         /* and save it */
1336         refresh_sequence_number(domain, False);
1337         centry = centry_start(domain, status);
1338         if (!centry)
1339                 goto skip_save;
1340         centry_put_uint32(centry, *num_entries);
1341         for (i=0; i<(*num_entries); i++) {
1342                 centry_put_string(centry, (*info)[i].acct_name);
1343                 centry_put_string(centry, (*info)[i].acct_desc);
1344                 centry_put_uint32(centry, (*info)[i].rid);
1345         }
1346         centry_end(centry, "GL/%s/local", domain->name);
1347         centry_free(centry);
1348
1349 skip_save:
1350         return status;
1351 }
1352
1353 /* convert a single name to a sid in a domain */
1354 static NTSTATUS name_to_sid(struct winbindd_domain *domain,
1355                             TALLOC_CTX *mem_ctx,
1356                             enum winbindd_cmd orig_cmd,
1357                             const char *domain_name,
1358                             const char *name,
1359                             DOM_SID *sid,
1360                             enum lsa_SidType *type)
1361 {
1362         struct winbind_cache *cache = get_cache(domain);
1363         struct cache_entry *centry = NULL;
1364         NTSTATUS status;
1365         fstring uname;
1366
1367         if (!cache->tdb)
1368                 goto do_query;
1369
1370         fstrcpy(uname, name);
1371         strupper_m(uname);
1372         centry = wcache_fetch(cache, domain, "NS/%s/%s", domain_name, uname);
1373         if (!centry)
1374                 goto do_query;
1375         *type = (enum lsa_SidType)centry_uint32(centry);
1376         status = centry->status;
1377         if (NT_STATUS_IS_OK(status)) {
1378                 centry_sid(centry, mem_ctx, sid);
1379         }
1380
1381         DEBUG(10,("name_to_sid: [Cached] - cached name for domain %s status: %s\n",
1382                 domain->name, nt_errstr(status) ));
1383
1384         centry_free(centry);
1385         return status;
1386
1387 do_query:
1388         ZERO_STRUCTP(sid);
1389
1390         /* If the seq number check indicated that there is a problem
1391          * with this DC, then return that status... except for
1392          * access_denied.  This is special because the dc may be in
1393          * "restrict anonymous = 1" mode, in which case it will deny
1394          * most unauthenticated operations, but *will* allow the LSA
1395          * name-to-sid that we try as a fallback. */
1396
1397         if (!(NT_STATUS_IS_OK(domain->last_status)
1398               || NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)))
1399                 return domain->last_status;
1400
1401         DEBUG(10,("name_to_sid: [Cached] - doing backend query for name for domain %s\n",
1402                 domain->name ));
1403
1404         status = domain->backend->name_to_sid(domain, mem_ctx, orig_cmd, 
1405                                               domain_name, name, sid, type);
1406
1407         /* and save it */
1408         refresh_sequence_number(domain, False);
1409
1410         if (domain->online && !is_null_sid(sid)) {
1411                 wcache_save_name_to_sid(domain, status, domain_name, name, sid, *type);
1412         }
1413
1414         /* Only save the reverse mapping if this was not a UPN */
1415         if (NT_STATUS_IS_OK(status) && !strchr(name, '@')) {
1416                 strupper_m(CONST_DISCARD(char *,domain_name));
1417                 strlower_m(CONST_DISCARD(char *,name));
1418                 wcache_save_sid_to_name(domain, status, sid, domain_name, name, *type);
1419         }
1420
1421         return status;
1422 }
1423
1424 /* convert a sid to a user or group name. The sid is guaranteed to be in the domain
1425    given */
1426 static NTSTATUS sid_to_name(struct winbindd_domain *domain,
1427                             TALLOC_CTX *mem_ctx,
1428                             const DOM_SID *sid,
1429                             char **domain_name,
1430                             char **name,
1431                             enum lsa_SidType *type)
1432 {
1433         struct winbind_cache *cache = get_cache(domain);
1434         struct cache_entry *centry = NULL;
1435         NTSTATUS status;
1436         fstring sid_string;
1437
1438         if (!cache->tdb)
1439                 goto do_query;
1440
1441         centry = wcache_fetch(cache, domain, "SN/%s", sid_to_string(sid_string, sid));
1442         if (!centry)
1443                 goto do_query;
1444         if (NT_STATUS_IS_OK(centry->status)) {
1445                 *type = (enum lsa_SidType)centry_uint32(centry);
1446                 *domain_name = centry_string(centry, mem_ctx);
1447                 *name = centry_string(centry, mem_ctx);
1448         }
1449         status = centry->status;
1450
1451         DEBUG(10,("sid_to_name: [Cached] - cached name for domain %s status: %s\n",
1452                 domain->name, nt_errstr(status) ));
1453
1454         centry_free(centry);
1455         return status;
1456
1457 do_query:
1458         *name = NULL;
1459         *domain_name = NULL;
1460
1461         /* If the seq number check indicated that there is a problem
1462          * with this DC, then return that status... except for
1463          * access_denied.  This is special because the dc may be in
1464          * "restrict anonymous = 1" mode, in which case it will deny
1465          * most unauthenticated operations, but *will* allow the LSA
1466          * sid-to-name that we try as a fallback. */
1467
1468         if (!(NT_STATUS_IS_OK(domain->last_status)
1469               || NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)))
1470                 return domain->last_status;
1471
1472         DEBUG(10,("sid_to_name: [Cached] - doing backend query for name for domain %s\n",
1473                 domain->name ));
1474
1475         status = domain->backend->sid_to_name(domain, mem_ctx, sid, domain_name, name, type);
1476
1477         /* and save it */
1478         refresh_sequence_number(domain, False);
1479         wcache_save_sid_to_name(domain, status, sid, *domain_name, *name, *type);
1480
1481         /* We can't save the name to sid mapping here, as with sid history a
1482          * later name2sid would give the wrong sid. */
1483
1484         return status;
1485 }
1486
1487 static NTSTATUS rids_to_names(struct winbindd_domain *domain,
1488                               TALLOC_CTX *mem_ctx,
1489                               const DOM_SID *domain_sid,
1490                               uint32 *rids,
1491                               size_t num_rids,
1492                               char **domain_name,
1493                               char ***names,
1494                               enum lsa_SidType **types)
1495 {
1496         struct winbind_cache *cache = get_cache(domain);
1497         size_t i;
1498         NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
1499         BOOL have_mapped;
1500         BOOL have_unmapped;
1501
1502         *domain_name = NULL;
1503         *names = NULL;
1504         *types = NULL;
1505
1506         if (!cache->tdb) {
1507                 goto do_query;
1508         }
1509
1510         if (num_rids == 0) {
1511                 return NT_STATUS_OK;
1512         }
1513
1514         *names = TALLOC_ARRAY(mem_ctx, char *, num_rids);
1515         *types = TALLOC_ARRAY(mem_ctx, enum lsa_SidType, num_rids);
1516
1517         if ((*names == NULL) || (*types == NULL)) {
1518                 result = NT_STATUS_NO_MEMORY;
1519                 goto error;
1520         }
1521
1522         have_mapped = have_unmapped = False;
1523
1524         for (i=0; i<num_rids; i++) {
1525                 DOM_SID sid;
1526                 struct cache_entry *centry;
1527
1528                 if (!sid_compose(&sid, domain_sid, rids[i])) {
1529                         result = NT_STATUS_INTERNAL_ERROR;
1530                         goto error;
1531                 }
1532
1533                 centry = wcache_fetch(cache, domain, "SN/%s",
1534                                       sid_string_static(&sid));
1535                 if (!centry) {
1536                         goto do_query;
1537                 }
1538
1539                 (*types)[i] = SID_NAME_UNKNOWN;
1540                 (*names)[i] = talloc_strdup(*names, "");
1541
1542                 if (NT_STATUS_IS_OK(centry->status)) {
1543                         char *dom;
1544                         have_mapped = True;
1545                         (*types)[i] = (enum lsa_SidType)centry_uint32(centry);
1546                         dom = centry_string(centry, mem_ctx);
1547                         if (*domain_name == NULL) {
1548                                 *domain_name = dom;
1549                         } else {
1550                                 talloc_free(dom);
1551                         }
1552                         (*names)[i] = centry_string(centry, *names);
1553                 } else {
1554                         have_unmapped = True;
1555                 }
1556
1557                 centry_free(centry);
1558         }
1559
1560         if (!have_mapped) {
1561                 return NT_STATUS_NONE_MAPPED;
1562         }
1563         if (!have_unmapped) {
1564                 return NT_STATUS_OK;
1565         }
1566         return STATUS_SOME_UNMAPPED;
1567
1568  do_query:
1569
1570         TALLOC_FREE(*names);
1571         TALLOC_FREE(*types);
1572
1573         result = domain->backend->rids_to_names(domain, mem_ctx, domain_sid,
1574                                                 rids, num_rids, domain_name,
1575                                                 names, types);
1576
1577         if (!NT_STATUS_IS_OK(result) &&
1578             !NT_STATUS_EQUAL(result, STATUS_SOME_UNMAPPED)) {
1579                 return result;
1580         }
1581
1582         refresh_sequence_number(domain, False);
1583
1584         for (i=0; i<num_rids; i++) {
1585                 DOM_SID sid;
1586                 NTSTATUS status;
1587
1588                 if (!sid_compose(&sid, domain_sid, rids[i])) {
1589                         result = NT_STATUS_INTERNAL_ERROR;
1590                         goto error;
1591                 }
1592
1593                 status = (*types)[i] == SID_NAME_UNKNOWN ?
1594                         NT_STATUS_NONE_MAPPED : NT_STATUS_OK;
1595
1596                 wcache_save_sid_to_name(domain, status, &sid, *domain_name,
1597                                         (*names)[i], (*types)[i]);
1598         }
1599
1600         return result;
1601
1602  error:
1603         
1604         TALLOC_FREE(*names);
1605         TALLOC_FREE(*types);
1606         return result;
1607 }
1608
1609 /* Lookup user information from a rid */
1610 static NTSTATUS query_user(struct winbindd_domain *domain, 
1611                            TALLOC_CTX *mem_ctx, 
1612                            const DOM_SID *user_sid, 
1613                            WINBIND_USERINFO *info)
1614 {
1615         struct winbind_cache *cache = get_cache(domain);
1616         struct cache_entry *centry = NULL;
1617         NTSTATUS status;
1618
1619         if (!cache->tdb)
1620                 goto do_query;
1621
1622         centry = wcache_fetch(cache, domain, "U/%s", sid_string_static(user_sid));
1623         
1624         /* If we have an access denied cache entry and a cached info3 in the
1625            samlogon cache then do a query.  This will force the rpc back end
1626            to return the info3 data. */
1627
1628         if (NT_STATUS_V(domain->last_status) == NT_STATUS_V(NT_STATUS_ACCESS_DENIED) &&
1629             netsamlogon_cache_have(user_sid)) {
1630                 DEBUG(10, ("query_user: cached access denied and have cached info3\n"));
1631                 domain->last_status = NT_STATUS_OK;
1632                 centry_free(centry);
1633                 goto do_query;
1634         }
1635         
1636         if (!centry)
1637                 goto do_query;
1638
1639         info->acct_name = centry_string(centry, mem_ctx);
1640         info->full_name = centry_string(centry, mem_ctx);
1641         info->homedir = centry_string(centry, mem_ctx);
1642         info->shell = centry_string(centry, mem_ctx);
1643         info->primary_gid = centry_uint32(centry);
1644         centry_sid(centry, mem_ctx, &info->user_sid);
1645         centry_sid(centry, mem_ctx, &info->group_sid);
1646         status = centry->status;
1647
1648         DEBUG(10,("query_user: [Cached] - cached info for domain %s status: %s\n",
1649                 domain->name, nt_errstr(status) ));
1650
1651         centry_free(centry);
1652         return status;
1653
1654 do_query:
1655         ZERO_STRUCTP(info);
1656
1657         /* Return status value returned by seq number check */
1658
1659         if (!NT_STATUS_IS_OK(domain->last_status))
1660                 return domain->last_status;
1661         
1662         DEBUG(10,("query_user: [Cached] - doing backend query for info for domain %s\n",
1663                 domain->name ));
1664
1665         status = domain->backend->query_user(domain, mem_ctx, user_sid, info);
1666
1667         /* and save it */
1668         refresh_sequence_number(domain, False);
1669         wcache_save_user(domain, status, info);
1670
1671         return status;
1672 }
1673
1674
1675 /* Lookup groups a user is a member of. */
1676 static NTSTATUS lookup_usergroups(struct winbindd_domain *domain,
1677                                   TALLOC_CTX *mem_ctx,
1678                                   const DOM_SID *user_sid, 
1679                                   uint32 *num_groups, DOM_SID **user_gids)
1680 {
1681         struct winbind_cache *cache = get_cache(domain);
1682         struct cache_entry *centry = NULL;
1683         NTSTATUS status;
1684         unsigned int i;
1685         fstring sid_string;
1686
1687         if (!cache->tdb)
1688                 goto do_query;
1689
1690         centry = wcache_fetch(cache, domain, "UG/%s", sid_to_string(sid_string, user_sid));
1691         
1692         /* If we have an access denied cache entry and a cached info3 in the
1693            samlogon cache then do a query.  This will force the rpc back end
1694            to return the info3 data. */
1695
1696         if (NT_STATUS_V(domain->last_status) == NT_STATUS_V(NT_STATUS_ACCESS_DENIED) &&
1697             netsamlogon_cache_have(user_sid)) {
1698                 DEBUG(10, ("lookup_usergroups: cached access denied and have cached info3\n"));
1699                 domain->last_status = NT_STATUS_OK;
1700                 centry_free(centry);
1701                 goto do_query;
1702         }
1703         
1704         if (!centry)
1705                 goto do_query;
1706
1707         *num_groups = centry_uint32(centry);
1708         
1709         if (*num_groups == 0)
1710                 goto do_cached;
1711
1712         (*user_gids) = TALLOC_ARRAY(mem_ctx, DOM_SID, *num_groups);
1713         if (! (*user_gids)) {
1714                 smb_panic_fn("lookup_usergroups out of memory");
1715         }
1716         for (i=0; i<(*num_groups); i++) {
1717                 centry_sid(centry, mem_ctx, &(*user_gids)[i]);
1718         }
1719
1720 do_cached:      
1721         status = centry->status;
1722
1723         DEBUG(10,("lookup_usergroups: [Cached] - cached info for domain %s status: %s\n",
1724                 domain->name, nt_errstr(status) ));
1725
1726         centry_free(centry);
1727         return status;
1728
1729 do_query:
1730         (*num_groups) = 0;
1731         (*user_gids) = NULL;
1732
1733         /* Return status value returned by seq number check */
1734
1735         if (!NT_STATUS_IS_OK(domain->last_status))
1736                 return domain->last_status;
1737
1738         DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info for domain %s\n",
1739                 domain->name ));
1740
1741         status = domain->backend->lookup_usergroups(domain, mem_ctx, user_sid, num_groups, user_gids);
1742
1743         if ( NT_STATUS_EQUAL(status, NT_STATUS_SYNCHRONIZATION_REQUIRED) )
1744                 goto skip_save;
1745         
1746         /* and save it */
1747         refresh_sequence_number(domain, False);
1748         centry = centry_start(domain, status);
1749         if (!centry)
1750                 goto skip_save;
1751         centry_put_uint32(centry, *num_groups);
1752         for (i=0; i<(*num_groups); i++) {
1753                 centry_put_sid(centry, &(*user_gids)[i]);
1754         }       
1755         centry_end(centry, "UG/%s", sid_to_string(sid_string, user_sid));
1756         centry_free(centry);
1757
1758 skip_save:
1759         return status;
1760 }
1761
1762 static NTSTATUS lookup_useraliases(struct winbindd_domain *domain,
1763                                    TALLOC_CTX *mem_ctx,
1764                                    uint32 num_sids, const DOM_SID *sids,
1765                                    uint32 *num_aliases, uint32 **alias_rids)
1766 {
1767         struct winbind_cache *cache = get_cache(domain);
1768         struct cache_entry *centry = NULL;
1769         NTSTATUS status;
1770         char *sidlist = talloc_strdup(mem_ctx, "");
1771         int i;
1772
1773         if (!cache->tdb)
1774                 goto do_query;
1775
1776         if (num_sids == 0) {
1777                 *num_aliases = 0;
1778                 *alias_rids = NULL;
1779                 return NT_STATUS_OK;
1780         }
1781
1782         /* We need to cache indexed by the whole list of SIDs, the aliases
1783          * resulting might come from any of the SIDs. */
1784
1785         for (i=0; i<num_sids; i++) {
1786                 sidlist = talloc_asprintf(mem_ctx, "%s/%s", sidlist,
1787                                           sid_string_static(&sids[i]));
1788                 if (sidlist == NULL)
1789                         return NT_STATUS_NO_MEMORY;
1790         }
1791
1792         centry = wcache_fetch(cache, domain, "UA%s", sidlist);
1793
1794         if (!centry)
1795                 goto do_query;
1796
1797         *num_aliases = centry_uint32(centry);
1798         *alias_rids = NULL;
1799
1800         if (*num_aliases) {
1801                 (*alias_rids) = TALLOC_ARRAY(mem_ctx, uint32, *num_aliases);
1802
1803                 if ((*alias_rids) == NULL) {
1804                         centry_free(centry);
1805                         return NT_STATUS_NO_MEMORY;
1806                 }
1807         } else {
1808                 (*alias_rids) = NULL;
1809         }
1810
1811         for (i=0; i<(*num_aliases); i++)
1812                 (*alias_rids)[i] = centry_uint32(centry);
1813
1814         status = centry->status;
1815
1816         DEBUG(10,("lookup_useraliases: [Cached] - cached info for domain: %s "
1817                   "status %s\n", domain->name, nt_errstr(status)));
1818
1819         centry_free(centry);
1820         return status;
1821
1822  do_query:
1823         (*num_aliases) = 0;
1824         (*alias_rids) = NULL;
1825
1826         if (!NT_STATUS_IS_OK(domain->last_status))
1827                 return domain->last_status;
1828
1829         DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info "
1830                   "for domain %s\n", domain->name ));
1831
1832         status = domain->backend->lookup_useraliases(domain, mem_ctx,
1833                                                      num_sids, sids,
1834                                                      num_aliases, alias_rids);
1835
1836         /* and save it */
1837         refresh_sequence_number(domain, False);
1838         centry = centry_start(domain, status);
1839         if (!centry)
1840                 goto skip_save;
1841         centry_put_uint32(centry, *num_aliases);
1842         for (i=0; i<(*num_aliases); i++)
1843                 centry_put_uint32(centry, (*alias_rids)[i]);
1844         centry_end(centry, "UA%s", sidlist);
1845         centry_free(centry);
1846
1847  skip_save:
1848         return status;
1849 }
1850
1851
1852 static NTSTATUS lookup_groupmem(struct winbindd_domain *domain,
1853                                 TALLOC_CTX *mem_ctx,
1854                                 const DOM_SID *group_sid, uint32 *num_names, 
1855                                 DOM_SID **sid_mem, char ***names, 
1856                                 uint32 **name_types)
1857 {
1858         struct winbind_cache *cache = get_cache(domain);
1859         struct cache_entry *centry = NULL;
1860         NTSTATUS status;
1861         unsigned int i;
1862         fstring sid_string;
1863
1864         if (!cache->tdb)
1865                 goto do_query;
1866
1867         centry = wcache_fetch(cache, domain, "GM/%s", sid_to_string(sid_string, group_sid));
1868         if (!centry)
1869                 goto do_query;
1870
1871         *num_names = centry_uint32(centry);
1872         
1873         if (*num_names == 0)
1874                 goto do_cached;
1875
1876         (*sid_mem) = TALLOC_ARRAY(mem_ctx, DOM_SID, *num_names);
1877         (*names) = TALLOC_ARRAY(mem_ctx, char *, *num_names);
1878         (*name_types) = TALLOC_ARRAY(mem_ctx, uint32, *num_names);
1879
1880         if (! (*sid_mem) || ! (*names) || ! (*name_types)) {
1881                 smb_panic_fn("lookup_groupmem out of memory");
1882         }
1883
1884         for (i=0; i<(*num_names); i++) {
1885                 centry_sid(centry, mem_ctx, &(*sid_mem)[i]);
1886                 (*names)[i] = centry_string(centry, mem_ctx);
1887                 (*name_types)[i] = centry_uint32(centry);
1888         }
1889
1890 do_cached:      
1891         status = centry->status;
1892
1893         DEBUG(10,("lookup_groupmem: [Cached] - cached info for domain %s status: %s\n",
1894                 domain->name, nt_errstr(status)));
1895
1896         centry_free(centry);
1897         return status;
1898
1899 do_query:
1900         (*num_names) = 0;
1901         (*sid_mem) = NULL;
1902         (*names) = NULL;
1903         (*name_types) = NULL;
1904         
1905         /* Return status value returned by seq number check */
1906
1907         if (!NT_STATUS_IS_OK(domain->last_status))
1908                 return domain->last_status;
1909
1910         DEBUG(10,("lookup_groupmem: [Cached] - doing backend query for info for domain %s\n",
1911                 domain->name ));
1912
1913         status = domain->backend->lookup_groupmem(domain, mem_ctx, group_sid, num_names, 
1914                                                   sid_mem, names, name_types);
1915
1916         /* and save it */
1917         refresh_sequence_number(domain, False);
1918         centry = centry_start(domain, status);
1919         if (!centry)
1920                 goto skip_save;
1921         centry_put_uint32(centry, *num_names);
1922         for (i=0; i<(*num_names); i++) {
1923                 centry_put_sid(centry, &(*sid_mem)[i]);
1924                 centry_put_string(centry, (*names)[i]);
1925                 centry_put_uint32(centry, (*name_types)[i]);
1926         }       
1927         centry_end(centry, "GM/%s", sid_to_string(sid_string, group_sid));
1928         centry_free(centry);
1929
1930 skip_save:
1931         return status;
1932 }
1933
1934 /* find the sequence number for a domain */
1935 static NTSTATUS sequence_number(struct winbindd_domain *domain, uint32 *seq)
1936 {
1937         refresh_sequence_number(domain, False);
1938
1939         *seq = domain->sequence_number;
1940
1941         return NT_STATUS_OK;
1942 }
1943
1944 /* enumerate trusted domains 
1945  * (we need to have the list of trustdoms in the cache when we go offline) -
1946  * Guenther */
1947 static NTSTATUS trusted_domains(struct winbindd_domain *domain,
1948                                 TALLOC_CTX *mem_ctx,
1949                                 uint32 *num_domains,
1950                                 char ***names,
1951                                 char ***alt_names,
1952                                 DOM_SID **dom_sids)
1953 {
1954         struct winbind_cache *cache = get_cache(domain);
1955         struct cache_entry *centry = NULL;
1956         NTSTATUS status;
1957         int i;
1958  
1959         if (!cache->tdb)
1960                 goto do_query;
1961  
1962         centry = wcache_fetch(cache, domain, "TRUSTDOMS/%s", domain->name);
1963         
1964         if (!centry) {
1965                 goto do_query;
1966         }
1967  
1968         *num_domains = centry_uint32(centry);
1969         
1970         if (*num_domains) {
1971                 (*names)        = TALLOC_ARRAY(mem_ctx, char *, *num_domains);
1972                 (*alt_names)    = TALLOC_ARRAY(mem_ctx, char *, *num_domains);
1973                 (*dom_sids)     = TALLOC_ARRAY(mem_ctx, DOM_SID, *num_domains);
1974  
1975                 if (! (*dom_sids) || ! (*names) || ! (*alt_names)) {
1976                         smb_panic_fn("trusted_domains out of memory");
1977                 }
1978         } else {
1979                 (*names) = NULL;
1980                 (*alt_names) = NULL;
1981                 (*dom_sids) = NULL;
1982         }
1983  
1984         for (i=0; i<(*num_domains); i++) {
1985                 (*names)[i] = centry_string(centry, mem_ctx);
1986                 (*alt_names)[i] = centry_string(centry, mem_ctx);
1987                 centry_sid(centry, mem_ctx, &(*dom_sids)[i]);
1988         }
1989
1990         status = centry->status;
1991  
1992         DEBUG(10,("trusted_domains: [Cached] - cached info for domain %s (%d trusts) status: %s\n",
1993                 domain->name, *num_domains, nt_errstr(status) ));
1994  
1995         centry_free(centry);
1996         return status;
1997  
1998 do_query:
1999         (*num_domains) = 0;
2000         (*dom_sids) = NULL;
2001         (*names) = NULL;
2002         (*alt_names) = NULL;
2003  
2004         /* Return status value returned by seq number check */
2005
2006         if (!NT_STATUS_IS_OK(domain->last_status))
2007                 return domain->last_status;
2008         
2009         DEBUG(10,("trusted_domains: [Cached] - doing backend query for info for domain %s\n",
2010                 domain->name ));
2011  
2012         status = domain->backend->trusted_domains(domain, mem_ctx, num_domains,
2013                                                 names, alt_names, dom_sids);
2014
2015         /* no trusts gives NT_STATUS_NO_MORE_ENTRIES resetting to NT_STATUS_OK
2016          * so that the generic centry handling still applies correctly -
2017          * Guenther*/
2018
2019         if (!NT_STATUS_IS_ERR(status)) {
2020                 status = NT_STATUS_OK;
2021         }
2022
2023
2024 #if 0    /* Disabled as we want the trust dom list to be managed by
2025             the main parent and always to make the query.  --jerry */
2026
2027         /* and save it */
2028         refresh_sequence_number(domain, False);
2029  
2030         centry = centry_start(domain, status);
2031         if (!centry)
2032                 goto skip_save;
2033
2034         centry_put_uint32(centry, *num_domains);
2035
2036         for (i=0; i<(*num_domains); i++) {
2037                 centry_put_string(centry, (*names)[i]);
2038                 centry_put_string(centry, (*alt_names)[i]);
2039                 centry_put_sid(centry, &(*dom_sids)[i]);
2040         }
2041         
2042         centry_end(centry, "TRUSTDOMS/%s", domain->name);
2043  
2044         centry_free(centry);
2045  
2046 skip_save:
2047 #endif
2048
2049         return status;
2050 }       
2051
2052 /* get lockout policy */
2053 static NTSTATUS lockout_policy(struct winbindd_domain *domain,
2054                                TALLOC_CTX *mem_ctx,
2055                                SAM_UNK_INFO_12 *policy){
2056         struct winbind_cache *cache = get_cache(domain);
2057         struct cache_entry *centry = NULL;
2058         NTSTATUS status;
2059  
2060         if (!cache->tdb)
2061                 goto do_query;
2062  
2063         centry = wcache_fetch(cache, domain, "LOC_POL/%s", domain->name);
2064         
2065         if (!centry)
2066                 goto do_query;
2067  
2068         policy->duration = centry_nttime(centry);
2069         policy->reset_count = centry_nttime(centry);
2070         policy->bad_attempt_lockout = centry_uint16(centry);
2071  
2072         status = centry->status;
2073  
2074         DEBUG(10,("lockout_policy: [Cached] - cached info for domain %s status: %s\n",
2075                 domain->name, nt_errstr(status) ));
2076  
2077         centry_free(centry);
2078         return status;
2079  
2080 do_query:
2081         ZERO_STRUCTP(policy);
2082  
2083         /* Return status value returned by seq number check */
2084
2085         if (!NT_STATUS_IS_OK(domain->last_status))
2086                 return domain->last_status;
2087         
2088         DEBUG(10,("lockout_policy: [Cached] - doing backend query for info for domain %s\n",
2089                 domain->name ));
2090  
2091         status = domain->backend->lockout_policy(domain, mem_ctx, policy); 
2092  
2093         /* and save it */
2094         refresh_sequence_number(domain, False);
2095         wcache_save_lockout_policy(domain, status, policy);
2096  
2097         return status;
2098 }
2099  
2100 /* get password policy */
2101 static NTSTATUS password_policy(struct winbindd_domain *domain,
2102                                 TALLOC_CTX *mem_ctx,
2103                                 SAM_UNK_INFO_1 *policy)
2104 {
2105         struct winbind_cache *cache = get_cache(domain);
2106         struct cache_entry *centry = NULL;
2107         NTSTATUS status;
2108
2109         if (!cache->tdb)
2110                 goto do_query;
2111  
2112         centry = wcache_fetch(cache, domain, "PWD_POL/%s", domain->name);
2113         
2114         if (!centry)
2115                 goto do_query;
2116
2117         policy->min_length_password = centry_uint16(centry);
2118         policy->password_history = centry_uint16(centry);
2119         policy->password_properties = centry_uint32(centry);
2120         policy->expire = centry_nttime(centry);
2121         policy->min_passwordage = centry_nttime(centry);
2122
2123         status = centry->status;
2124
2125         DEBUG(10,("lockout_policy: [Cached] - cached info for domain %s status: %s\n",
2126                 domain->name, nt_errstr(status) ));
2127
2128         centry_free(centry);
2129         return status;
2130
2131 do_query:
2132         ZERO_STRUCTP(policy);
2133
2134         /* Return status value returned by seq number check */
2135
2136         if (!NT_STATUS_IS_OK(domain->last_status))
2137                 return domain->last_status;
2138         
2139         DEBUG(10,("password_policy: [Cached] - doing backend query for info for domain %s\n",
2140                 domain->name ));
2141
2142         status = domain->backend->password_policy(domain, mem_ctx, policy); 
2143
2144         /* and save it */
2145         refresh_sequence_number(domain, False);
2146         wcache_save_password_policy(domain, status, policy);
2147
2148         return status;
2149 }
2150
2151
2152 /* Invalidate cached user and group lists coherently */
2153
2154 static int traverse_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf, 
2155                        void *state)
2156 {
2157         if (strncmp((const char *)kbuf.dptr, "UL/", 3) == 0 ||
2158             strncmp((const char *)kbuf.dptr, "GL/", 3) == 0)
2159                 tdb_delete(the_tdb, kbuf);
2160
2161         return 0;
2162 }
2163
2164 /* Invalidate the getpwnam and getgroups entries for a winbindd domain */
2165
2166 void wcache_invalidate_samlogon(struct winbindd_domain *domain, 
2167                                 NET_USER_INFO_3 *info3)
2168 {
2169         struct winbind_cache *cache;
2170
2171         /* dont clear cached U/SID and UG/SID entries when we want to logon
2172          * offline - gd */
2173
2174         if (lp_winbind_offline_logon()) {
2175                 return;
2176         }
2177
2178         if (!domain)
2179                 return;
2180
2181         cache = get_cache(domain);
2182         netsamlogon_clear_cached_user(cache->tdb, info3);
2183 }
2184
2185 void wcache_invalidate_cache(void)
2186 {
2187         struct winbindd_domain *domain;
2188
2189         for (domain = domain_list(); domain; domain = domain->next) {
2190                 struct winbind_cache *cache = get_cache(domain);
2191
2192                 DEBUG(10, ("wcache_invalidate_cache: invalidating cache "
2193                            "entries for %s\n", domain->name));
2194                 if (cache)
2195                         tdb_traverse(cache->tdb, traverse_fn, NULL);
2196         }
2197 }
2198
2199 BOOL init_wcache(void)
2200 {
2201         if (wcache == NULL) {
2202                 wcache = SMB_XMALLOC_P(struct winbind_cache);
2203                 ZERO_STRUCTP(wcache);
2204         }
2205
2206         if (wcache->tdb != NULL)
2207                 return True;
2208
2209         /* when working offline we must not clear the cache on restart */
2210         wcache->tdb = tdb_open_log(lock_path("winbindd_cache.tdb"),
2211                                 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE, 
2212                                 lp_winbind_offline_logon() ? TDB_DEFAULT : (TDB_DEFAULT | TDB_CLEAR_IF_FIRST), 
2213                                 O_RDWR|O_CREAT, 0600);
2214
2215         if (wcache->tdb == NULL) {
2216                 DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
2217                 return False;
2218         }
2219
2220         return True;
2221 }
2222
2223 /************************************************************************
2224  This is called by the parent to initialize the cache file.
2225  We don't need sophisticated locking here as we know we're the
2226  only opener.
2227 ************************************************************************/
2228
2229 BOOL initialize_winbindd_cache(void)
2230 {
2231         BOOL cache_bad = True;
2232         uint32 vers;
2233
2234         if (!init_wcache()) {
2235                 DEBUG(0,("initialize_winbindd_cache: init_wcache failed.\n"));
2236                 return False;
2237         }
2238
2239         /* Check version number. */
2240         if (tdb_fetch_uint32(wcache->tdb, WINBINDD_CACHE_VERSION_KEYSTR, &vers) &&
2241                         vers == WINBINDD_CACHE_VERSION) {
2242                 cache_bad = False;
2243         }
2244
2245         if (cache_bad) {
2246                 DEBUG(0,("initialize_winbindd_cache: clearing cache "
2247                         "and re-creating with version number %d\n",
2248                         WINBINDD_CACHE_VERSION ));
2249
2250                 tdb_close(wcache->tdb);
2251                 wcache->tdb = NULL;
2252
2253                 if (unlink(lock_path("winbindd_cache.tdb")) == -1) {
2254                         DEBUG(0,("initialize_winbindd_cache: unlink %s failed %s ",
2255                                 lock_path("winbindd_cache.tdb"),
2256                                 strerror(errno) ));
2257                         return False;
2258                 }
2259                 if (!init_wcache()) {
2260                         DEBUG(0,("initialize_winbindd_cache: re-initialization "
2261                                         "init_wcache failed.\n"));
2262                         return False;
2263                 }
2264
2265                 /* Write the version. */
2266                 if (!tdb_store_uint32(wcache->tdb, WINBINDD_CACHE_VERSION_KEYSTR, WINBINDD_CACHE_VERSION)) {
2267                         DEBUG(0,("initialize_winbindd_cache: version number store failed %s\n",
2268                                 tdb_errorstr(wcache->tdb) ));
2269                         return False;
2270                 }
2271         }
2272
2273         tdb_close(wcache->tdb);
2274         wcache->tdb = NULL;
2275         return True;
2276 }
2277
2278 void cache_store_response(pid_t pid, struct winbindd_response *response)
2279 {
2280         fstring key_str;
2281
2282         if (!init_wcache())
2283                 return;
2284
2285         DEBUG(10, ("Storing response for pid %d, len %d\n",
2286                    pid, response->length));
2287
2288         fstr_sprintf(key_str, "DR/%d", pid);
2289         if (tdb_store(wcache->tdb, string_tdb_data(key_str), 
2290                       make_tdb_data((uint8 *)response, sizeof(*response)),
2291                       TDB_REPLACE) == -1)
2292                 return;
2293
2294         if (response->length == sizeof(*response))
2295                 return;
2296
2297         /* There's extra data */
2298
2299         DEBUG(10, ("Storing extra data: len=%d\n",
2300                    (int)(response->length - sizeof(*response))));
2301
2302         fstr_sprintf(key_str, "DE/%d", pid);
2303         if (tdb_store(wcache->tdb, string_tdb_data(key_str),
2304                       make_tdb_data((uint8 *)response->extra_data.data,
2305                                     response->length - sizeof(*response)),
2306                       TDB_REPLACE) == 0)
2307                 return;
2308
2309         /* We could not store the extra data, make sure the tdb does not
2310          * contain a main record with wrong dangling extra data */
2311
2312         fstr_sprintf(key_str, "DR/%d", pid);
2313         tdb_delete(wcache->tdb, string_tdb_data(key_str));
2314
2315         return;
2316 }
2317
2318 BOOL cache_retrieve_response(pid_t pid, struct winbindd_response * response)
2319 {
2320         TDB_DATA data;
2321         fstring key_str;
2322
2323         if (!init_wcache())
2324                 return False;
2325
2326         DEBUG(10, ("Retrieving response for pid %d\n", pid));
2327
2328         fstr_sprintf(key_str, "DR/%d", pid);
2329         data = tdb_fetch(wcache->tdb, string_tdb_data(key_str));
2330
2331         if (data.dptr == NULL)
2332                 return False;
2333
2334         if (data.dsize != sizeof(*response))
2335                 return False;
2336
2337         memcpy(response, data.dptr, data.dsize);
2338         SAFE_FREE(data.dptr);
2339
2340         if (response->length == sizeof(*response)) {
2341                 response->extra_data.data = NULL;
2342                 return True;
2343         }
2344
2345         /* There's extra data */
2346
2347         DEBUG(10, ("Retrieving extra data length=%d\n",
2348                    (int)(response->length - sizeof(*response))));
2349
2350         fstr_sprintf(key_str, "DE/%d", pid);
2351         data = tdb_fetch(wcache->tdb, string_tdb_data(key_str));
2352
2353         if (data.dptr == NULL) {
2354                 DEBUG(0, ("Did not find extra data\n"));
2355                 return False;
2356         }
2357
2358         if (data.dsize != (response->length - sizeof(*response))) {
2359                 DEBUG(0, ("Invalid extra data length: %d\n", (int)data.dsize));
2360                 SAFE_FREE(data.dptr);
2361                 return False;
2362         }
2363
2364         dump_data(11, (uint8 *)data.dptr, data.dsize);
2365
2366         response->extra_data.data = data.dptr;
2367         return True;
2368 }
2369
2370 void cache_cleanup_response(pid_t pid)
2371 {
2372         fstring key_str;
2373
2374         if (!init_wcache())
2375                 return;
2376
2377         fstr_sprintf(key_str, "DR/%d", pid);
2378         tdb_delete(wcache->tdb, string_tdb_data(key_str));
2379
2380         fstr_sprintf(key_str, "DE/%d", pid);
2381         tdb_delete(wcache->tdb, string_tdb_data(key_str));
2382
2383         return;
2384 }
2385
2386
2387 BOOL lookup_cached_sid(TALLOC_CTX *mem_ctx, const DOM_SID *sid,
2388                        char **domain_name, char **name,
2389                        enum lsa_SidType *type)
2390 {
2391         struct winbindd_domain *domain;
2392         struct winbind_cache *cache;
2393         struct cache_entry *centry = NULL;
2394         NTSTATUS status;
2395
2396         domain = find_lookup_domain_from_sid(sid);
2397         if (domain == NULL) {
2398                 return False;
2399         }
2400
2401         cache = get_cache(domain);
2402
2403         if (cache->tdb == NULL) {
2404                 return False;
2405         }
2406
2407         centry = wcache_fetch(cache, domain, "SN/%s", sid_string_static(sid));
2408         if (centry == NULL) {
2409                 return False;
2410         }
2411
2412         if (NT_STATUS_IS_OK(centry->status)) {
2413                 *type = (enum lsa_SidType)centry_uint32(centry);
2414                 *domain_name = centry_string(centry, mem_ctx);
2415                 *name = centry_string(centry, mem_ctx);
2416         }
2417
2418         status = centry->status;
2419         centry_free(centry);
2420         return NT_STATUS_IS_OK(status);
2421 }
2422
2423 BOOL lookup_cached_name(TALLOC_CTX *mem_ctx,
2424                         const char *domain_name,
2425                         const char *name,
2426                         DOM_SID *sid,
2427                         enum lsa_SidType *type)
2428 {
2429         struct winbindd_domain *domain;
2430         struct winbind_cache *cache;
2431         struct cache_entry *centry = NULL;
2432         NTSTATUS status;
2433         fstring uname;
2434         BOOL original_online_state;     
2435
2436         domain = find_lookup_domain_from_name(domain_name);
2437         if (domain == NULL) {
2438                 return False;
2439         }
2440
2441         cache = get_cache(domain);
2442
2443         if (cache->tdb == NULL) {
2444                 return False;
2445         }
2446
2447         fstrcpy(uname, name);
2448         strupper_m(uname);
2449         
2450         /* If we are doing a cached logon, temporarily set the domain
2451            offline so the cache won't expire the entry */
2452         
2453         original_online_state = domain->online;
2454         domain->online = False;
2455         centry = wcache_fetch(cache, domain, "NS/%s/%s", domain_name, uname);
2456         domain->online = original_online_state;
2457         
2458         if (centry == NULL) {
2459                 return False;
2460         }
2461
2462         if (NT_STATUS_IS_OK(centry->status)) {
2463                 *type = (enum lsa_SidType)centry_uint32(centry);
2464                 centry_sid(centry, mem_ctx, sid);
2465         }
2466
2467         status = centry->status;
2468         centry_free(centry);
2469         
2470         return NT_STATUS_IS_OK(status);
2471 }
2472
2473 void cache_name2sid(struct winbindd_domain *domain, 
2474                     const char *domain_name, const char *name,
2475                     enum lsa_SidType type, const DOM_SID *sid)
2476 {
2477         refresh_sequence_number(domain, False);
2478         wcache_save_name_to_sid(domain, NT_STATUS_OK, domain_name, name,
2479                                 sid, type);
2480 }
2481
2482 /*
2483  * The original idea that this cache only contains centries has
2484  * been blurred - now other stuff gets put in here. Ensure we
2485  * ignore these things on cleanup.
2486  */
2487
2488 static int traverse_fn_cleanup(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, 
2489                                TDB_DATA dbuf, void *state)
2490 {
2491         struct cache_entry *centry;
2492
2493         if (is_non_centry_key(kbuf)) {
2494                 return 0;
2495         }
2496
2497         centry = wcache_fetch_raw((char *)kbuf.dptr);
2498         if (!centry) {
2499                 return 0;
2500         }
2501
2502         if (!NT_STATUS_IS_OK(centry->status)) {
2503                 DEBUG(10,("deleting centry %s\n", (const char *)kbuf.dptr));
2504                 tdb_delete(the_tdb, kbuf);
2505         }
2506
2507         centry_free(centry);
2508         return 0;
2509 }
2510
2511 /* flush the cache */
2512 void wcache_flush_cache(void)
2513 {
2514         if (!wcache)
2515                 return;
2516         if (wcache->tdb) {
2517                 tdb_close(wcache->tdb);
2518                 wcache->tdb = NULL;
2519         }
2520         if (opt_nocache)
2521                 return;
2522
2523         /* when working offline we must not clear the cache on restart */
2524         wcache->tdb = tdb_open_log(lock_path("winbindd_cache.tdb"),
2525                                 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE, 
2526                                 lp_winbind_offline_logon() ? TDB_DEFAULT : (TDB_DEFAULT | TDB_CLEAR_IF_FIRST), 
2527                                 O_RDWR|O_CREAT, 0600);
2528
2529         if (!wcache->tdb) {
2530                 DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
2531                 return;
2532         }
2533
2534         tdb_traverse(wcache->tdb, traverse_fn_cleanup, NULL);
2535
2536         DEBUG(10,("wcache_flush_cache success\n"));
2537 }
2538
2539 /* Count cached creds */
2540
2541 static int traverse_fn_cached_creds(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf, 
2542                                     void *state)
2543 {
2544         int *cred_count = (int*)state;
2545  
2546         if (strncmp((const char *)kbuf.dptr, "CRED/", 5) == 0) {
2547                 (*cred_count)++;
2548         }
2549         return 0;
2550 }
2551
2552 NTSTATUS wcache_count_cached_creds(struct winbindd_domain *domain, int *count)
2553 {
2554         struct winbind_cache *cache = get_cache(domain);
2555
2556         *count = 0;
2557
2558         if (!cache->tdb) {
2559                 return NT_STATUS_INTERNAL_DB_ERROR;
2560         }
2561  
2562         tdb_traverse(cache->tdb, traverse_fn_cached_creds, (void *)count);
2563
2564         return NT_STATUS_OK;
2565 }
2566
2567 struct cred_list {
2568         struct cred_list *prev, *next;
2569         TDB_DATA key;
2570         fstring name;
2571         time_t created;
2572 };
2573 static struct cred_list *wcache_cred_list;
2574
2575 static int traverse_fn_get_credlist(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf, 
2576                                     void *state)
2577 {
2578         struct cred_list *cred;
2579
2580         if (strncmp((const char *)kbuf.dptr, "CRED/", 5) == 0) {
2581
2582                 cred = SMB_MALLOC_P(struct cred_list);
2583                 if (cred == NULL) {
2584                         DEBUG(0,("traverse_fn_remove_first_creds: failed to malloc new entry for list\n"));
2585                         return -1;
2586                 }
2587
2588                 ZERO_STRUCTP(cred);
2589                 
2590                 /* save a copy of the key */
2591                 
2592                 fstrcpy(cred->name, (const char *)kbuf.dptr);           
2593                 DLIST_ADD(wcache_cred_list, cred);
2594         }
2595         
2596         return 0;
2597 }
2598
2599 NTSTATUS wcache_remove_oldest_cached_creds(struct winbindd_domain *domain, const DOM_SID *sid) 
2600 {
2601         struct winbind_cache *cache = get_cache(domain);
2602         NTSTATUS status;
2603         int ret;
2604         struct cred_list *cred, *oldest = NULL;
2605
2606         if (!cache->tdb) {
2607                 return NT_STATUS_INTERNAL_DB_ERROR;
2608         }
2609
2610         /* we possibly already have an entry */
2611         if (sid && NT_STATUS_IS_OK(wcache_cached_creds_exist(domain, sid))) {
2612         
2613                 fstring key_str;
2614
2615                 DEBUG(11,("we already have an entry, deleting that\n"));
2616
2617                 fstr_sprintf(key_str, "CRED/%s", sid_string_static(sid));
2618
2619                 tdb_delete(cache->tdb, string_tdb_data(key_str));
2620
2621                 return NT_STATUS_OK;
2622         }
2623
2624         ret = tdb_traverse(cache->tdb, traverse_fn_get_credlist, NULL);
2625         if (ret == 0) {
2626                 return NT_STATUS_OK;
2627         } else if ((ret == -1) || (wcache_cred_list == NULL)) {
2628                 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
2629         }
2630
2631         ZERO_STRUCTP(oldest);
2632
2633         for (cred = wcache_cred_list; cred; cred = cred->next) {
2634
2635                 TDB_DATA data;
2636                 time_t t;
2637
2638                 data = tdb_fetch(cache->tdb, string_tdb_data(cred->name));
2639                 if (!data.dptr) {
2640                         DEBUG(10,("wcache_remove_oldest_cached_creds: entry for [%s] not found\n", 
2641                                 cred->name));
2642                         status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
2643                         goto done;
2644                 }
2645         
2646                 t = IVAL(data.dptr, 0);
2647                 SAFE_FREE(data.dptr);
2648
2649                 if (!oldest) {
2650                         oldest = SMB_MALLOC_P(struct cred_list);
2651                         if (oldest == NULL) {
2652                                 status = NT_STATUS_NO_MEMORY;
2653                                 goto done;
2654                         }
2655
2656                         fstrcpy(oldest->name, cred->name);
2657                         oldest->created = t;
2658                         continue;
2659                 }
2660
2661                 if (t < oldest->created) {
2662                         fstrcpy(oldest->name, cred->name);
2663                         oldest->created = t;
2664                 }
2665         }
2666
2667         if (tdb_delete(cache->tdb, string_tdb_data(oldest->name)) == 0) {
2668                 status = NT_STATUS_OK;
2669         } else {
2670                 status = NT_STATUS_UNSUCCESSFUL;
2671         }
2672 done:
2673         SAFE_FREE(wcache_cred_list);
2674         SAFE_FREE(oldest);
2675         
2676         return status;
2677 }
2678
2679 /* Change the global online/offline state. */
2680 BOOL set_global_winbindd_state_offline(void)
2681 {
2682         TDB_DATA data;
2683
2684         DEBUG(10,("set_global_winbindd_state_offline: offline requested.\n"));
2685
2686         /* Only go offline if someone has created
2687            the key "WINBINDD_OFFLINE" in the cache tdb. */
2688
2689         if (wcache == NULL || wcache->tdb == NULL) {
2690                 DEBUG(10,("set_global_winbindd_state_offline: wcache not open yet.\n"));
2691                 return False;
2692         }
2693
2694         if (!lp_winbind_offline_logon()) {
2695                 DEBUG(10,("set_global_winbindd_state_offline: rejecting.\n"));
2696                 return False;
2697         }
2698
2699         if (global_winbindd_offline_state) {
2700                 /* Already offline. */
2701                 return True;
2702         }
2703
2704         data = tdb_fetch_bystring( wcache->tdb, "WINBINDD_OFFLINE" );
2705
2706         if (!data.dptr || data.dsize != 4) {
2707                 DEBUG(10,("set_global_winbindd_state_offline: offline state not set.\n"));
2708                 SAFE_FREE(data.dptr);
2709                 return False;
2710         } else {
2711                 DEBUG(10,("set_global_winbindd_state_offline: offline state set.\n"));
2712                 global_winbindd_offline_state = True;
2713                 SAFE_FREE(data.dptr);
2714                 return True;
2715         }
2716 }
2717
2718 void set_global_winbindd_state_online(void)
2719 {
2720         DEBUG(10,("set_global_winbindd_state_online: online requested.\n"));
2721
2722         if (!lp_winbind_offline_logon()) {
2723                 DEBUG(10,("set_global_winbindd_state_online: rejecting.\n"));
2724                 return;
2725         }
2726
2727         if (!global_winbindd_offline_state) {
2728                 /* Already online. */
2729                 return;
2730         }
2731         global_winbindd_offline_state = False;
2732
2733         if (!wcache->tdb) {
2734                 return;
2735         }
2736
2737         /* Ensure there is no key "WINBINDD_OFFLINE" in the cache tdb. */
2738         tdb_delete_bystring(wcache->tdb, "WINBINDD_OFFLINE");
2739 }
2740
2741 BOOL get_global_winbindd_state_offline(void)
2742 {
2743         return global_winbindd_offline_state;
2744 }
2745
2746 /***********************************************************************
2747  Validate functions for all possible cache tdb keys.
2748 ***********************************************************************/
2749
2750 static struct cache_entry *create_centry_validate(const char *kstr, TDB_DATA data, 
2751                                                   struct tdb_validation_status *state)
2752 {
2753         struct cache_entry *centry;
2754
2755         centry = SMB_XMALLOC_P(struct cache_entry);
2756         centry->data = (unsigned char *)memdup(data.dptr, data.dsize);
2757         if (!centry->data) {
2758                 SAFE_FREE(centry);
2759                 return NULL;
2760         }
2761         centry->len = data.dsize;
2762         centry->ofs = 0;
2763
2764         if (centry->len < 8) {
2765                 /* huh? corrupt cache? */
2766                 DEBUG(0,("create_centry_validate: Corrupt cache for key %s (len < 8) ?\n", kstr));
2767                 centry_free(centry);
2768                 state->bad_entry = True;
2769                 state->success = False;
2770                 return NULL;
2771         }
2772
2773         centry->status = NT_STATUS(centry_uint32(centry));
2774         centry->sequence_number = centry_uint32(centry);
2775         return centry;
2776 }
2777
2778 static int validate_seqnum(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
2779                            struct tdb_validation_status *state)
2780 {
2781         if (dbuf.dsize != 8) {
2782                 DEBUG(0,("validate_seqnum: Corrupt cache for key %s (len %u != 8) ?\n",
2783                                 keystr, (unsigned int)dbuf.dsize ));
2784                 state->bad_entry = True;
2785                 return 1;
2786         }
2787         return 0;
2788 }
2789
2790 static int validate_ns(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
2791                        struct tdb_validation_status *state)
2792 {
2793         struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
2794         if (!centry) {
2795                 return 1;
2796         }
2797
2798         (void)centry_uint32(centry);
2799         if (NT_STATUS_IS_OK(centry->status)) {
2800                 DOM_SID sid;
2801                 (void)centry_sid(centry, mem_ctx, &sid);
2802         }
2803
2804         centry_free(centry);
2805
2806         if (!(state->success)) {
2807                 return 1;
2808         }
2809         DEBUG(10,("validate_ns: %s ok\n", keystr));
2810         return 0;
2811 }
2812
2813 static int validate_sn(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
2814                        struct tdb_validation_status *state)
2815 {
2816         struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
2817         if (!centry) {
2818                 return 1;
2819         }
2820
2821         if (NT_STATUS_IS_OK(centry->status)) {
2822                 (void)centry_uint32(centry);
2823                 (void)centry_string(centry, mem_ctx);
2824                 (void)centry_string(centry, mem_ctx);
2825         }
2826
2827         centry_free(centry);
2828
2829         if (!(state->success)) {
2830                 return 1;
2831         }
2832         DEBUG(10,("validate_sn: %s ok\n", keystr));
2833         return 0;
2834 }
2835
2836 static int validate_u(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
2837                       struct tdb_validation_status *state)
2838 {
2839         struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
2840         DOM_SID sid;
2841
2842         if (!centry) {
2843                 return 1;
2844         }
2845
2846         (void)centry_string(centry, mem_ctx);
2847         (void)centry_string(centry, mem_ctx);
2848         (void)centry_string(centry, mem_ctx);
2849         (void)centry_string(centry, mem_ctx);
2850         (void)centry_uint32(centry);
2851         (void)centry_sid(centry, mem_ctx, &sid);
2852         (void)centry_sid(centry, mem_ctx, &sid);
2853
2854         centry_free(centry);
2855
2856         if (!(state->success)) {
2857                 return 1;
2858         }
2859         DEBUG(10,("validate_u: %s ok\n", keystr));
2860         return 0;
2861 }
2862
2863 static int validate_loc_pol(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
2864                             struct tdb_validation_status *state)
2865 {
2866         struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
2867
2868         if (!centry) {
2869                 return 1;
2870         }
2871
2872         (void)centry_nttime(centry);
2873         (void)centry_nttime(centry);
2874         (void)centry_uint16(centry);
2875
2876         centry_free(centry);
2877
2878         if (!(state->success)) {
2879                 return 1;
2880         }
2881         DEBUG(10,("validate_loc_pol: %s ok\n", keystr));
2882         return 0;
2883 }
2884
2885 static int validate_pwd_pol(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
2886                             struct tdb_validation_status *state)
2887 {
2888         struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
2889
2890         if (!centry) {
2891                 return 1;
2892         }
2893
2894         (void)centry_uint16(centry);
2895         (void)centry_uint16(centry);
2896         (void)centry_uint32(centry);
2897         (void)centry_nttime(centry);
2898         (void)centry_nttime(centry);
2899
2900         centry_free(centry);
2901
2902         if (!(state->success)) {
2903                 return 1;
2904         }
2905         DEBUG(10,("validate_pwd_pol: %s ok\n", keystr));
2906         return 0;
2907 }
2908
2909 static int validate_cred(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
2910                          struct tdb_validation_status *state)
2911 {
2912         struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
2913
2914         if (!centry) {
2915                 return 1;
2916         }
2917
2918         (void)centry_time(centry);
2919         (void)centry_hash16(centry, mem_ctx);
2920
2921         /* We only have 17 bytes more data in the salted cred case. */
2922         if (centry->len - centry->ofs == 17) {
2923                 (void)centry_hash16(centry, mem_ctx);
2924         }
2925
2926         centry_free(centry);
2927
2928         if (!(state->success)) {
2929                 return 1;
2930         }
2931         DEBUG(10,("validate_cred: %s ok\n", keystr));
2932         return 0;
2933 }
2934
2935 static int validate_ul(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
2936                        struct tdb_validation_status *state)
2937 {
2938         struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
2939         int32 num_entries, i;
2940
2941         if (!centry) {
2942                 return 1;
2943         }
2944
2945         num_entries = (int32)centry_uint32(centry);
2946
2947         for (i=0; i< num_entries; i++) {
2948                 DOM_SID sid;
2949                 (void)centry_string(centry, mem_ctx);
2950                 (void)centry_string(centry, mem_ctx);
2951                 (void)centry_string(centry, mem_ctx);
2952                 (void)centry_string(centry, mem_ctx);
2953                 (void)centry_sid(centry, mem_ctx, &sid);
2954                 (void)centry_sid(centry, mem_ctx, &sid);
2955         }
2956
2957         centry_free(centry);
2958
2959         if (!(state->success)) {
2960                 return 1;
2961         }
2962         DEBUG(10,("validate_ul: %s ok\n", keystr));
2963         return 0;
2964 }
2965
2966 static int validate_gl(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
2967                        struct tdb_validation_status *state)
2968 {
2969         struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
2970         int32 num_entries, i;
2971
2972         if (!centry) {
2973                 return 1;
2974         }
2975
2976         num_entries = centry_uint32(centry);
2977         
2978         for (i=0; i< num_entries; i++) {
2979                 (void)centry_string(centry, mem_ctx);
2980                 (void)centry_string(centry, mem_ctx);
2981                 (void)centry_uint32(centry);
2982         }
2983
2984         centry_free(centry);
2985
2986         if (!(state->success)) {
2987                 return 1;
2988         }
2989         DEBUG(10,("validate_gl: %s ok\n", keystr));
2990         return 0;
2991 }
2992
2993 static int validate_ug(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
2994                        struct tdb_validation_status *state)
2995 {
2996         struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
2997         int32 num_groups, i;
2998
2999         if (!centry) {
3000                 return 1;
3001         }
3002
3003         num_groups = centry_uint32(centry);
3004
3005         for (i=0; i< num_groups; i++) {
3006                 DOM_SID sid;
3007                 centry_sid(centry, mem_ctx, &sid);
3008         }
3009
3010         centry_free(centry);
3011
3012         if (!(state->success)) {
3013                 return 1;
3014         }
3015         DEBUG(10,("validate_ug: %s ok\n", keystr));
3016         return 0;
3017 }
3018
3019 static int validate_ua(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3020                        struct tdb_validation_status *state)
3021 {
3022         struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3023         int32 num_aliases, i;
3024
3025         if (!centry) {
3026                 return 1;
3027         }
3028
3029         num_aliases = centry_uint32(centry);
3030
3031         for (i=0; i < num_aliases; i++) {
3032                 (void)centry_uint32(centry);
3033         }
3034
3035         centry_free(centry);
3036
3037         if (!(state->success)) {
3038                 return 1;
3039         }
3040         DEBUG(10,("validate_ua: %s ok\n", keystr));
3041         return 0;
3042 }
3043
3044 static int validate_gm(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3045                        struct tdb_validation_status *state)
3046 {
3047         struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3048         int32 num_names, i;
3049
3050         if (!centry) {
3051                 return 1;
3052         }
3053
3054         num_names = centry_uint32(centry);
3055
3056         for (i=0; i< num_names; i++) {
3057                 DOM_SID sid;
3058                 centry_sid(centry, mem_ctx, &sid);
3059                 (void)centry_string(centry, mem_ctx);
3060                 (void)centry_uint32(centry);
3061         }
3062
3063         centry_free(centry);
3064
3065         if (!(state->success)) {
3066                 return 1;
3067         }
3068         DEBUG(10,("validate_gm: %s ok\n", keystr));
3069         return 0;
3070 }
3071
3072 static int validate_dr(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3073                        struct tdb_validation_status *state)
3074 {
3075         /* Can't say anything about this other than must be nonzero. */
3076         if (dbuf.dsize == 0) {
3077                 DEBUG(0,("validate_dr: Corrupt cache for key %s (len == 0) ?\n",
3078                                 keystr));
3079                 state->bad_entry = True;
3080                 state->success = False;
3081                 return 1;
3082         }
3083
3084         DEBUG(10,("validate_dr: %s ok\n", keystr));
3085         return 0;
3086 }
3087
3088 static int validate_de(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3089                        struct tdb_validation_status *state)
3090 {
3091         /* Can't say anything about this other than must be nonzero. */
3092         if (dbuf.dsize == 0) {
3093                 DEBUG(0,("validate_de: Corrupt cache for key %s (len == 0) ?\n",
3094                                 keystr));
3095                 state->bad_entry = True;
3096                 state->success = False;
3097                 return 1;
3098         }
3099
3100         DEBUG(10,("validate_de: %s ok\n", keystr));
3101         return 0;
3102 }
3103
3104 static int validate_trustdoms(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3105                               struct tdb_validation_status *state)
3106 {
3107         struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3108         int32 num_domains, i;
3109
3110         if (!centry) {
3111                 return 1;
3112         }
3113
3114         num_domains = centry_uint32(centry);
3115         
3116         for (i=0; i< num_domains; i++) {
3117                 DOM_SID sid;
3118                 (void)centry_string(centry, mem_ctx);
3119                 (void)centry_string(centry, mem_ctx);
3120                 (void)centry_sid(centry, mem_ctx, &sid);
3121         }
3122
3123         centry_free(centry);
3124
3125         if (!(state->success)) {
3126                 return 1;
3127         }
3128         DEBUG(10,("validate_trustdoms: %s ok\n", keystr));
3129         return 0;
3130 }
3131
3132 static int validate_trustdomcache(TALLOC_CTX *mem_ctx, const char *keystr, 
3133                                   TDB_DATA dbuf,
3134                                   struct tdb_validation_status *state)
3135 {
3136         if (dbuf.dsize == 0) {
3137                 DEBUG(0, ("validate_trustdomcache: Corrupt cache for "
3138                           "key %s (len ==0) ?\n", keystr));
3139                 state->bad_entry = True;
3140                 state->success = False;
3141                 return 1;
3142         }
3143
3144         DEBUG(10,    ("validate_trustdomcache: %s ok\n", keystr));
3145         DEBUGADD(10, ("  Don't trust me, I am a DUMMY!\n"));
3146         return 0;
3147 }
3148
3149 static int validate_offline(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3150                             struct tdb_validation_status *state)
3151 {
3152         if (dbuf.dsize != 4) {
3153                 DEBUG(0,("validate_offline: Corrupt cache for key %s (len %u != 4) ?\n",
3154                                 keystr, (unsigned int)dbuf.dsize ));
3155                 state->bad_entry = True;
3156                 state->success = False;
3157                 return 1;
3158         }
3159         DEBUG(10,("validate_offline: %s ok\n", keystr));
3160         return 0;
3161 }
3162
3163 static int validate_cache_version(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3164                                   struct tdb_validation_status *state)
3165 {
3166         if (dbuf.dsize != 4) {
3167                 DEBUG(0, ("validate_cache_version: Corrupt cache for "
3168                           "key %s (len %u != 4) ?\n", 
3169                           keystr, (unsigned int)dbuf.dsize));
3170                 state->bad_entry = True;
3171                 state->success = False;
3172                 return 1;
3173         }
3174
3175         DEBUG(10, ("validate_cache_version: %s ok\n", keystr));
3176         return 0;
3177 }
3178
3179 /***********************************************************************
3180  A list of all possible cache tdb keys with associated validation
3181  functions.
3182 ***********************************************************************/
3183
3184 struct key_val_struct {
3185         const char *keyname;
3186         int (*validate_data_fn)(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf, struct tdb_validation_status* state);
3187 } key_val[] = {
3188         {"SEQNUM/", validate_seqnum},
3189         {"NS/", validate_ns},
3190         {"SN/", validate_sn},
3191         {"U/", validate_u},
3192         {"LOC_POL/", validate_loc_pol},
3193         {"PWD_POL/", validate_pwd_pol},
3194         {"CRED/", validate_cred},
3195         {"UL/", validate_ul},
3196         {"GL/", validate_gl},
3197         {"UG/", validate_ug},
3198         {"UA", validate_ua},
3199         {"GM/", validate_gm},
3200         {"DR/", validate_dr},
3201         {"DE/", validate_de},
3202         {"TRUSTDOMS/", validate_trustdoms},
3203         {"TRUSTDOMCACHE/", validate_trustdomcache},
3204         {"WINBINDD_OFFLINE", validate_offline},
3205         {WINBINDD_CACHE_VERSION_KEYSTR, validate_cache_version},
3206         {NULL, NULL}
3207 };
3208
3209 /***********************************************************************
3210  Function to look at every entry in the tdb and validate it as far as
3211  possible.
3212 ***********************************************************************/
3213
3214 static int cache_traverse_validate_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf, void *state)
3215 {
3216         int i;
3217         struct tdb_validation_status *v_state = (struct tdb_validation_status *)state;
3218
3219         /* Paranoia check. */
3220         if (kbuf.dsize > 1024) {
3221                 DEBUG(0,("cache_traverse_validate_fn: key length too large (%u) > 1024\n\n",
3222                                 (unsigned int)kbuf.dsize ));
3223                 return 1;
3224         }
3225
3226         for (i = 0; key_val[i].keyname; i++) {
3227                 size_t namelen = strlen(key_val[i].keyname);
3228                 if (kbuf.dsize >= namelen && (
3229                                 strncmp(key_val[i].keyname, (const char *)kbuf.dptr, namelen)) == 0) {
3230                         TALLOC_CTX *mem_ctx;
3231                         char *keystr;
3232                         int ret;
3233
3234                         keystr = SMB_MALLOC_ARRAY(char, kbuf.dsize+1);
3235                         if (!keystr) {
3236                                 return 1;
3237                         }
3238                         memcpy(keystr, kbuf.dptr, kbuf.dsize);
3239                         keystr[kbuf.dsize] = '\0';
3240
3241                         mem_ctx = talloc_init("validate_ctx");
3242                         if (!mem_ctx) {
3243                                 SAFE_FREE(keystr);
3244                                 return 1;
3245                         }
3246
3247                         ret = key_val[i].validate_data_fn(mem_ctx, keystr, dbuf, 
3248                                                           v_state);
3249
3250                         SAFE_FREE(keystr);
3251                         talloc_destroy(mem_ctx);
3252                         return ret;
3253                 }
3254         }
3255
3256         DEBUG(0,("cache_traverse_validate_fn: unknown cache entry\nkey :\n"));
3257         dump_data(0, (uint8 *)kbuf.dptr, kbuf.dsize);
3258         DEBUG(0,("data :\n"));
3259         dump_data(0, (uint8 *)dbuf.dptr, dbuf.dsize);
3260         v_state->unknown_key = True;
3261         v_state->success = False;
3262         return 1; /* terminate. */
3263 }
3264
3265 static void validate_panic(const char *const why)
3266 {
3267         DEBUG(0,("validating cache: would panic %s\n", why ));
3268         DEBUGADD(0, ("exiting instead (cache validation mode)\n"));
3269         exit(47);
3270 }
3271
3272 /***********************************************************************
3273  Try and validate every entry in the winbindd cache. If we fail here,
3274  delete the cache tdb and return non-zero - the caller (main winbindd
3275  function) will restart us as we don't know if we crashed or not.
3276 ***********************************************************************/
3277
3278 int winbindd_validate_cache(void)
3279 {
3280         int ret = -1;
3281         const char *tdb_path = lock_path("winbindd_cache.tdb");
3282         TDB_CONTEXT *tdb = NULL;
3283
3284         DEBUG(10, ("winbindd_validate_cache: replacing panic function\n"));
3285         smb_panic_fn = validate_panic;
3286
3287
3288         tdb = tdb_open_log(tdb_path, 
3289                            WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
3290                            ( lp_winbind_offline_logon() 
3291                              ? TDB_DEFAULT 
3292                              : TDB_DEFAULT | TDB_CLEAR_IF_FIRST ),
3293                            O_RDWR|O_CREAT, 
3294                            0600);
3295         if (!tdb) {
3296                 DEBUG(0, ("winbindd_validate_cache: "
3297                           "error opening/initializing tdb\n"));
3298                 goto done;
3299         }
3300         tdb_close(tdb);
3301
3302         ret = tdb_validate_and_backup(lock_path("winbindd_cache.tdb"),
3303                                       cache_traverse_validate_fn);
3304
3305         if (ret != 0) {
3306                 DEBUG(10, ("winbindd_validate_cache: validation not successful.\n"));
3307                 DEBUGADD(10, ("removing tdb %s.\n", tdb_path));
3308                 unlink(tdb_path);
3309         }
3310
3311 done:
3312         DEBUG(10, ("winbindd_validate_cache: restoring panic function\n"));
3313         smb_panic_fn = smb_panic;
3314         return ret;
3315 }
3316
3317 /*********************************************************************
3318  ********************************************************************/
3319
3320 static BOOL add_wbdomain_to_tdc_array( struct winbindd_domain *new_dom,
3321                                        struct winbindd_tdc_domain **domains, 
3322                                        size_t *num_domains )
3323 {
3324         struct winbindd_tdc_domain *list = NULL;
3325         size_t idx;
3326         int i;
3327         BOOL set_only = False;  
3328         
3329         /* don't allow duplicates */
3330
3331         idx = *num_domains;
3332         list = *domains;
3333         
3334         for ( i=0; i< (*num_domains); i++ ) {
3335                 if ( strequal( new_dom->name, list[i].domain_name ) ) {
3336                         DEBUG(10,("add_wbdomain_to_tdc_array: Found existing record for %s\n",
3337                                   new_dom->name));
3338                         idx = i;
3339                         set_only = True;
3340                         
3341                         break;
3342                 }
3343         }
3344
3345         if ( !set_only ) {
3346                 if ( !*domains ) {
3347                         list = TALLOC_ARRAY( NULL, struct winbindd_tdc_domain, 1 );
3348                         idx = 0;
3349                 } else {
3350                         list = TALLOC_REALLOC_ARRAY( *domains, *domains, 
3351                                                      struct winbindd_tdc_domain,  
3352                                                      (*num_domains)+1);
3353                         idx = *num_domains;             
3354                 }
3355
3356                 ZERO_STRUCT( list[idx] );
3357         }
3358
3359         if ( !list )
3360                 return False;
3361
3362         list[idx].domain_name = talloc_strdup( list, new_dom->name );
3363         list[idx].dns_name = talloc_strdup( list, new_dom->alt_name );
3364
3365         if ( !is_null_sid( &new_dom->sid ) )
3366                 sid_copy( &list[idx].sid, &new_dom->sid );
3367
3368         if ( new_dom->domain_flags != 0x0 )
3369                 list[idx].trust_flags = new_dom->domain_flags;  
3370
3371         if ( new_dom->domain_type != 0x0 )
3372                 list[idx].trust_type = new_dom->domain_type;
3373
3374         if ( new_dom->domain_trust_attribs != 0x0 )
3375                 list[idx].trust_attribs = new_dom->domain_trust_attribs;
3376         
3377         if ( !set_only ) {
3378                 *domains = list;
3379                 *num_domains = idx + 1; 
3380         }
3381
3382         return True;    
3383 }
3384
3385 /*********************************************************************
3386  ********************************************************************/
3387
3388 static TDB_DATA make_tdc_key( const char *domain_name )
3389 {
3390         char *keystr = NULL;
3391         TDB_DATA key = { NULL, 0 };
3392         
3393         if ( !domain_name ) {
3394                 DEBUG(5,("make_tdc_key: Keyname workgroup is NULL!\n"));
3395                 return key;
3396         }
3397                
3398                 
3399         asprintf( &keystr, "TRUSTDOMCACHE/%s", domain_name );
3400         key = string_term_tdb_data(keystr);
3401         
3402         return key;     
3403 }
3404
3405 /*********************************************************************
3406  ********************************************************************/
3407
3408 static int pack_tdc_domains( struct winbindd_tdc_domain *domains, 
3409                              size_t num_domains,
3410                              unsigned char **buf )
3411 {
3412         unsigned char *buffer = NULL;
3413         int len = 0;
3414         int buflen = 0;
3415         int i = 0;
3416
3417         DEBUG(10,("pack_tdc_domains: Packing %d trusted domains\n",
3418                   (int)num_domains));
3419         
3420         buflen = 0;
3421         
3422  again: 
3423         len = 0;
3424         
3425         /* Store the number of array items first */
3426         len += tdb_pack( buffer+len, buflen-len, "d", 
3427                          num_domains );
3428
3429         /* now pack each domain trust record */
3430         for ( i=0; i<num_domains; i++ ) {
3431
3432                 if ( buflen > 0 ) {
3433                         DEBUG(10,("pack_tdc_domains: Packing domain %s (%s)\n",
3434                                   domains[i].domain_name,
3435                                   domains[i].dns_name ? domains[i].dns_name : "UNKNOWN" ));
3436                 }
3437                 
3438                 len += tdb_pack( buffer+len, buflen-len, "fffddd",
3439                                  domains[i].domain_name,
3440                                  domains[i].dns_name,
3441                                  sid_string_static(&domains[i].sid),
3442                                  domains[i].trust_flags,
3443                                  domains[i].trust_attribs,
3444                                  domains[i].trust_type );
3445         }
3446
3447         if ( buflen < len ) {
3448                 SAFE_FREE(buffer);
3449                 if ( (buffer = SMB_MALLOC_ARRAY(unsigned char, len)) == NULL ) {
3450                         DEBUG(0,("pack_tdc_domains: failed to alloc buffer!\n"));
3451                         buflen = -1;
3452                         goto done;
3453                 }
3454                 buflen = len;
3455                 goto again;
3456         }
3457
3458         *buf = buffer;  
3459         
3460  done:  
3461         return buflen;  
3462 }
3463
3464 /*********************************************************************
3465  ********************************************************************/
3466
3467 static size_t unpack_tdc_domains( unsigned char *buf, int buflen, 
3468                                   struct winbindd_tdc_domain **domains )
3469 {
3470         fstring domain_name, dns_name, sid_string;      
3471         uint32 type, attribs, flags;
3472         int num_domains;
3473         int len = 0;
3474         int i;
3475         struct winbindd_tdc_domain *list = NULL;
3476
3477         /* get the number of domains */
3478         len += tdb_unpack( buf+len, buflen-len, "d", &num_domains);
3479         if ( len == -1 ) {
3480                 DEBUG(5,("unpack_tdc_domains: Failed to unpack domain array\n"));               
3481                 return 0;
3482         }
3483
3484         list = TALLOC_ARRAY( NULL, struct winbindd_tdc_domain, num_domains );
3485         if ( !list ) {
3486                 DEBUG(0,("unpack_tdc_domains: Failed to talloc() domain list!\n"));
3487                 return 0;               
3488         }
3489         
3490         for ( i=0; i<num_domains; i++ ) {
3491                 len += tdb_unpack( buf+len, buflen-len, "fffddd",
3492                                    domain_name,
3493                                    dns_name,
3494                                    sid_string,
3495                                    &flags,
3496                                    &attribs,
3497                                    &type );
3498
3499                 if ( len == -1 ) {
3500                         DEBUG(5,("unpack_tdc_domains: Failed to unpack domain array\n"));
3501                         TALLOC_FREE( list );                    
3502                         return 0;
3503                 }
3504
3505                 DEBUG(11,("unpack_tdc_domains: Unpacking domain %s (%s) "
3506                           "SID %s, flags = 0x%x, attribs = 0x%x, type = 0x%x\n",
3507                           domain_name, dns_name, sid_string,
3508                           flags, attribs, type));
3509                 
3510                 list[i].domain_name = talloc_strdup( list, domain_name );
3511                 list[i].dns_name = talloc_strdup( list, dns_name );
3512                 if ( !string_to_sid( &(list[i].sid), sid_string ) ) {                   
3513                         DEBUG(10,("unpack_tdc_domains: no SID for domain %s\n",
3514                                   domain_name));
3515                 }
3516                 list[i].trust_flags = flags;
3517                 list[i].trust_attribs = attribs;
3518                 list[i].trust_type = type;
3519         }
3520
3521         *domains = list;
3522         
3523         return num_domains;
3524 }
3525
3526 /*********************************************************************
3527  ********************************************************************/
3528
3529 static BOOL wcache_tdc_store_list( struct winbindd_tdc_domain *domains, size_t num_domains )
3530 {
3531         TDB_DATA key = make_tdc_key( lp_workgroup() );   
3532         TDB_DATA data = { NULL, 0 };
3533         int ret;
3534         
3535         if ( !key.dptr )
3536                 return False;
3537         
3538         /* See if we were asked to delete the cache entry */
3539
3540         if ( !domains ) {
3541                 ret = tdb_delete( wcache->tdb, key );
3542                 goto done;
3543         }
3544         
3545         data.dsize = pack_tdc_domains( domains, num_domains, &data.dptr );
3546         
3547         if ( !data.dptr ) {
3548                 ret = -1;
3549                 goto done;
3550         }
3551                 
3552         ret = tdb_store( wcache->tdb, key, data, 0 );
3553
3554  done:
3555         SAFE_FREE( data.dptr );
3556         SAFE_FREE( key.dptr );
3557         
3558         return ( ret != -1 );   
3559 }
3560
3561 /*********************************************************************
3562  ********************************************************************/
3563
3564 BOOL wcache_tdc_fetch_list( struct winbindd_tdc_domain **domains, size_t *num_domains )
3565 {
3566         TDB_DATA key = make_tdc_key( lp_workgroup() );
3567         TDB_DATA data = { NULL, 0 };
3568
3569         *domains = NULL;        
3570         *num_domains = 0;       
3571
3572         if ( !key.dptr )
3573                 return False;
3574         
3575         data = tdb_fetch( wcache->tdb, key );
3576
3577         SAFE_FREE( key.dptr );
3578         
3579         if ( !data.dptr ) 
3580                 return False;
3581         
3582         *num_domains = unpack_tdc_domains( data.dptr, data.dsize, domains );
3583
3584         SAFE_FREE( data.dptr );
3585         
3586         if ( !*domains )
3587                 return False;
3588
3589         return True;    
3590 }
3591
3592 /*********************************************************************
3593  ********************************************************************/
3594
3595 BOOL wcache_tdc_add_domain( struct winbindd_domain *domain )
3596 {
3597         struct winbindd_tdc_domain *dom_list = NULL;
3598         size_t num_domains = 0;
3599         BOOL ret = False;       
3600
3601         DEBUG(10,("wcache_tdc_add_domain: Adding domain %s (%s), SID %s, "
3602                   "flags = 0x%x, attributes = 0x%x, type = 0x%x\n",
3603                   domain->name, domain->alt_name, 
3604                   sid_string_static(&domain->sid),
3605                   domain->domain_flags,
3606                   domain->domain_trust_attribs,
3607                   domain->domain_type));        
3608         
3609         if ( !init_wcache() ) {
3610                 return False;
3611         }
3612         
3613         /* fetch the list */
3614
3615         wcache_tdc_fetch_list( &dom_list, &num_domains );
3616         
3617         /* add the new domain */
3618
3619         if ( !add_wbdomain_to_tdc_array( domain, &dom_list, &num_domains ) ) {
3620                 goto done;              
3621         }       
3622
3623         /* pack the domain */
3624
3625         if ( !wcache_tdc_store_list( dom_list, num_domains ) ) {
3626                 goto done;              
3627         }
3628         
3629         /* Success */
3630
3631         ret = True;     
3632  done:
3633         TALLOC_FREE( dom_list );
3634         
3635         return ret;     
3636 }
3637
3638 /*********************************************************************
3639  ********************************************************************/
3640
3641 struct winbindd_tdc_domain * wcache_tdc_fetch_domain( TALLOC_CTX *ctx, const char *name )
3642 {
3643         struct winbindd_tdc_domain *dom_list = NULL;
3644         size_t num_domains = 0;
3645         int i;
3646         struct winbindd_tdc_domain *d = NULL;   
3647
3648         DEBUG(10,("wcache_tdc_fetch_domain: Searching for domain %s\n", name));
3649
3650         if ( !init_wcache() ) {
3651                 return False;
3652         }
3653         
3654         /* fetch the list */
3655
3656         wcache_tdc_fetch_list( &dom_list, &num_domains );
3657         
3658         for ( i=0; i<num_domains; i++ ) {
3659                 if ( strequal(name, dom_list[i].domain_name) ||
3660                      strequal(name, dom_list[i].dns_name) )
3661                 {
3662                         DEBUG(10,("wcache_tdc_fetch_domain: Found domain %s\n",
3663                                   name));
3664                         
3665                         d = TALLOC_P( ctx, struct winbindd_tdc_domain );
3666                         if ( !d )
3667                                 break;                  
3668                         
3669                         d->domain_name = talloc_strdup( d, dom_list[i].domain_name );
3670                         d->dns_name = talloc_strdup( d, dom_list[i].dns_name );
3671                         sid_copy( &d->sid, &dom_list[i].sid );
3672                         d->trust_flags   = dom_list[i].trust_flags;
3673                         d->trust_type    = dom_list[i].trust_type;
3674                         d->trust_attribs = dom_list[i].trust_attribs;
3675
3676                         break;
3677                 }
3678         }
3679
3680         TALLOC_FREE( dom_list );
3681         
3682         return d;       
3683 }
3684
3685
3686 /*********************************************************************
3687  ********************************************************************/
3688
3689 void wcache_tdc_clear( void )
3690 {
3691         if ( !init_wcache() )
3692                 return;
3693
3694         wcache_tdc_store_list( NULL, 0 );
3695         
3696         return; 
3697 }
3698
3699
3700 /*********************************************************************
3701  ********************************************************************/
3702
3703 static void wcache_save_user_pwinfo(struct winbindd_domain *domain, 
3704                                     NTSTATUS status,
3705                                     const DOM_SID *user_sid,
3706                                     const char *homedir,
3707                                     const char *shell,
3708                                     const char *gecos,
3709                                     uint32 gid)
3710 {
3711         struct cache_entry *centry;
3712
3713         if ( (centry = centry_start(domain, status)) == NULL )
3714                 return;
3715
3716         centry_put_string( centry, homedir );
3717         centry_put_string( centry, shell );
3718         centry_put_string( centry, gecos );
3719         centry_put_uint32( centry, gid );
3720         
3721         centry_end(centry, "NSS/PWINFO/%s", sid_string_static(user_sid) );
3722
3723         DEBUG(10,("wcache_save_user_pwinfo: %s\n", sid_string_static(user_sid) ));
3724
3725         centry_free(centry);
3726 }
3727
3728 NTSTATUS nss_get_info_cached( struct winbindd_domain *domain, 
3729                               const DOM_SID *user_sid,
3730                               TALLOC_CTX *ctx,
3731                               ADS_STRUCT *ads, LDAPMessage *msg,
3732                               char **homedir, char **shell, char **gecos,
3733                               gid_t *p_gid)
3734 {
3735         struct winbind_cache *cache = get_cache(domain);
3736         struct cache_entry *centry = NULL;
3737         NTSTATUS nt_status;
3738
3739         if (!cache->tdb)
3740                 goto do_query;
3741
3742         centry = wcache_fetch(cache, domain, "NSS/PWINFO/%s", sid_string_static(user_sid));     
3743         
3744         if (!centry)
3745                 goto do_query;
3746
3747         *homedir = centry_string( centry, ctx );
3748         *shell   = centry_string( centry, ctx );
3749         *gecos   = centry_string( centry, ctx );
3750         *p_gid   = centry_uint32( centry );     
3751
3752         centry_free(centry);
3753
3754         DEBUG(10,("nss_get_info_cached: [Cached] - user_sid %s\n",
3755                   sid_string_static(user_sid)));
3756
3757         return NT_STATUS_OK;
3758
3759 do_query:
3760         
3761         nt_status = nss_get_info( domain->name, user_sid, ctx, ads, msg, 
3762                                   homedir, shell, gecos, p_gid );
3763
3764         if ( NT_STATUS_IS_OK(nt_status) ) {
3765                 wcache_save_user_pwinfo( domain, nt_status, user_sid,
3766                                          *homedir, *shell, *gecos, *p_gid );
3767         }       
3768
3769         if ( NT_STATUS_EQUAL( nt_status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND ) ) {
3770                 DEBUG(5,("nss_get_info_cached: Setting domain %s offline\n",
3771                          domain->name ));
3772                 set_domain_offline( domain );
3773         }
3774
3775         return nt_status;       
3776 }
3777
3778
3779 /* the cache backend methods are exposed via this structure */
3780 struct winbindd_methods cache_methods = {
3781         True,
3782         query_user_list,
3783         enum_dom_groups,
3784         enum_local_groups,
3785         name_to_sid,
3786         sid_to_name,
3787         rids_to_names,
3788         query_user,
3789         lookup_usergroups,
3790         lookup_useraliases,
3791         lookup_groupmem,
3792         sequence_number,
3793         lockout_policy,
3794         password_policy,
3795         trusted_domains
3796 };