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