d1c34100ca29a7a5ff8a4b9baa59e6487f599d98
[sfrench/samba-autobuild/.git] / source / 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 2 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_parm_bool(-1, "winbind", "rpc only", False)) {
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 struct validation_status {
2753         BOOL tdb_error;
2754         BOOL bad_freelist;
2755         BOOL bad_entry;
2756         BOOL unknown_key;
2757         BOOL success;
2758 };
2759
2760 static struct cache_entry *create_centry_validate(const char *kstr, TDB_DATA data, 
2761                                                   struct validation_status *state)
2762 {
2763         struct cache_entry *centry;
2764
2765         centry = SMB_XMALLOC_P(struct cache_entry);
2766         centry->data = (unsigned char *)memdup(data.dptr, data.dsize);
2767         if (!centry->data) {
2768                 SAFE_FREE(centry);
2769                 return NULL;
2770         }
2771         centry->len = data.dsize;
2772         centry->ofs = 0;
2773
2774         if (centry->len < 8) {
2775                 /* huh? corrupt cache? */
2776                 DEBUG(0,("create_centry_validate: Corrupt cache for key %s (len < 8) ?\n", kstr));
2777                 centry_free(centry);
2778                 state->bad_entry = True;
2779                 state->success = False;
2780                 return NULL;
2781         }
2782
2783         centry->status = NT_STATUS(centry_uint32(centry));
2784         centry->sequence_number = centry_uint32(centry);
2785         return centry;
2786 }
2787
2788 static int validate_seqnum(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
2789                            struct validation_status *state)
2790 {
2791         if (dbuf.dsize != 8) {
2792                 DEBUG(0,("validate_seqnum: Corrupt cache for key %s (len %u != 8) ?\n",
2793                                 keystr, (unsigned int)dbuf.dsize ));
2794                 state->bad_entry = True;
2795                 return 1;
2796         }
2797         return 0;
2798 }
2799
2800 static int validate_ns(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
2801                        struct validation_status *state)
2802 {
2803         struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
2804         if (!centry) {
2805                 return 1;
2806         }
2807
2808         (void)centry_uint32(centry);
2809         if (NT_STATUS_IS_OK(centry->status)) {
2810                 DOM_SID sid;
2811                 (void)centry_sid(centry, mem_ctx, &sid);
2812         }
2813
2814         centry_free(centry);
2815
2816         if (!(state->success)) {
2817                 return 1;
2818         }
2819         DEBUG(10,("validate_ns: %s ok\n", keystr));
2820         return 0;
2821 }
2822
2823 static int validate_sn(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
2824                        struct validation_status *state)
2825 {
2826         struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
2827         if (!centry) {
2828                 return 1;
2829         }
2830
2831         if (NT_STATUS_IS_OK(centry->status)) {
2832                 (void)centry_uint32(centry);
2833                 (void)centry_string(centry, mem_ctx);
2834                 (void)centry_string(centry, mem_ctx);
2835         }
2836
2837         centry_free(centry);
2838
2839         if (!(state->success)) {
2840                 return 1;
2841         }
2842         DEBUG(10,("validate_sn: %s ok\n", keystr));
2843         return 0;
2844 }
2845
2846 static int validate_u(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
2847                       struct validation_status *state)
2848 {
2849         struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
2850         DOM_SID sid;
2851
2852         if (!centry) {
2853                 return 1;
2854         }
2855
2856         (void)centry_string(centry, mem_ctx);
2857         (void)centry_string(centry, mem_ctx);
2858         (void)centry_string(centry, mem_ctx);
2859         (void)centry_string(centry, mem_ctx);
2860         (void)centry_uint32(centry);
2861         (void)centry_sid(centry, mem_ctx, &sid);
2862         (void)centry_sid(centry, mem_ctx, &sid);
2863
2864         centry_free(centry);
2865
2866         if (!(state->success)) {
2867                 return 1;
2868         }
2869         DEBUG(10,("validate_u: %s ok\n", keystr));
2870         return 0;
2871 }
2872
2873 static int validate_loc_pol(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
2874                             struct validation_status *state)
2875 {
2876         struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
2877
2878         if (!centry) {
2879                 return 1;
2880         }
2881
2882         (void)centry_nttime(centry);
2883         (void)centry_nttime(centry);
2884         (void)centry_uint16(centry);
2885
2886         centry_free(centry);
2887
2888         if (!(state->success)) {
2889                 return 1;
2890         }
2891         DEBUG(10,("validate_loc_pol: %s ok\n", keystr));
2892         return 0;
2893 }
2894
2895 static int validate_pwd_pol(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
2896                             struct validation_status *state)
2897 {
2898         struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
2899
2900         if (!centry) {
2901                 return 1;
2902         }
2903
2904         (void)centry_uint16(centry);
2905         (void)centry_uint16(centry);
2906         (void)centry_uint32(centry);
2907         (void)centry_nttime(centry);
2908         (void)centry_nttime(centry);
2909
2910         centry_free(centry);
2911
2912         if (!(state->success)) {
2913                 return 1;
2914         }
2915         DEBUG(10,("validate_pwd_pol: %s ok\n", keystr));
2916         return 0;
2917 }
2918
2919 static int validate_cred(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
2920                          struct validation_status *state)
2921 {
2922         struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
2923
2924         if (!centry) {
2925                 return 1;
2926         }
2927
2928         (void)centry_time(centry);
2929         (void)centry_hash16(centry, mem_ctx);
2930
2931         /* We only have 17 bytes more data in the salted cred case. */
2932         if (centry->len - centry->ofs == 17) {
2933                 (void)centry_hash16(centry, mem_ctx);
2934         }
2935
2936         centry_free(centry);
2937
2938         if (!(state->success)) {
2939                 return 1;
2940         }
2941         DEBUG(10,("validate_cred: %s ok\n", keystr));
2942         return 0;
2943 }
2944
2945 static int validate_ul(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
2946                        struct validation_status *state)
2947 {
2948         struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
2949         int32 num_entries, i;
2950
2951         if (!centry) {
2952                 return 1;
2953         }
2954
2955         num_entries = (int32)centry_uint32(centry);
2956
2957         for (i=0; i< num_entries; i++) {
2958                 DOM_SID sid;
2959                 (void)centry_string(centry, mem_ctx);
2960                 (void)centry_string(centry, mem_ctx);
2961                 (void)centry_string(centry, mem_ctx);
2962                 (void)centry_string(centry, mem_ctx);
2963                 (void)centry_sid(centry, mem_ctx, &sid);
2964                 (void)centry_sid(centry, mem_ctx, &sid);
2965         }
2966
2967         centry_free(centry);
2968
2969         if (!(state->success)) {
2970                 return 1;
2971         }
2972         DEBUG(10,("validate_ul: %s ok\n", keystr));
2973         return 0;
2974 }
2975
2976 static int validate_gl(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
2977                        struct validation_status *state)
2978 {
2979         struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
2980         int32 num_entries, i;
2981
2982         if (!centry) {
2983                 return 1;
2984         }
2985
2986         num_entries = centry_uint32(centry);
2987         
2988         for (i=0; i< num_entries; i++) {
2989                 (void)centry_string(centry, mem_ctx);
2990                 (void)centry_string(centry, mem_ctx);
2991                 (void)centry_uint32(centry);
2992         }
2993
2994         centry_free(centry);
2995
2996         if (!(state->success)) {
2997                 return 1;
2998         }
2999         DEBUG(10,("validate_gl: %s ok\n", keystr));
3000         return 0;
3001 }
3002
3003 static int validate_ug(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3004                        struct validation_status *state)
3005 {
3006         struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3007         int32 num_groups, i;
3008
3009         if (!centry) {
3010                 return 1;
3011         }
3012
3013         num_groups = centry_uint32(centry);
3014
3015         for (i=0; i< num_groups; i++) {
3016                 DOM_SID sid;
3017                 centry_sid(centry, mem_ctx, &sid);
3018         }
3019
3020         centry_free(centry);
3021
3022         if (!(state->success)) {
3023                 return 1;
3024         }
3025         DEBUG(10,("validate_ug: %s ok\n", keystr));
3026         return 0;
3027 }
3028
3029 static int validate_ua(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3030                        struct validation_status *state)
3031 {
3032         struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3033         int32 num_aliases, i;
3034
3035         if (!centry) {
3036                 return 1;
3037         }
3038
3039         num_aliases = centry_uint32(centry);
3040
3041         for (i=0; i < num_aliases; i++) {
3042                 (void)centry_uint32(centry);
3043         }
3044
3045         centry_free(centry);
3046
3047         if (!(state->success)) {
3048                 return 1;
3049         }
3050         DEBUG(10,("validate_ua: %s ok\n", keystr));
3051         return 0;
3052 }
3053
3054 static int validate_gm(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3055                        struct validation_status *state)
3056 {
3057         struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3058         int32 num_names, i;
3059
3060         if (!centry) {
3061                 return 1;
3062         }
3063
3064         num_names = centry_uint32(centry);
3065
3066         for (i=0; i< num_names; i++) {
3067                 DOM_SID sid;
3068                 centry_sid(centry, mem_ctx, &sid);
3069                 (void)centry_string(centry, mem_ctx);
3070                 (void)centry_uint32(centry);
3071         }
3072
3073         centry_free(centry);
3074
3075         if (!(state->success)) {
3076                 return 1;
3077         }
3078         DEBUG(10,("validate_gm: %s ok\n", keystr));
3079         return 0;
3080 }
3081
3082 static int validate_dr(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3083                        struct validation_status *state)
3084 {
3085         /* Can't say anything about this other than must be nonzero. */
3086         if (dbuf.dsize == 0) {
3087                 DEBUG(0,("validate_dr: Corrupt cache for key %s (len == 0) ?\n",
3088                                 keystr));
3089                 state->bad_entry = True;
3090                 state->success = False;
3091                 return 1;
3092         }
3093
3094         DEBUG(10,("validate_dr: %s ok\n", keystr));
3095         return 0;
3096 }
3097
3098 static int validate_de(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3099                        struct validation_status *state)
3100 {
3101         /* Can't say anything about this other than must be nonzero. */
3102         if (dbuf.dsize == 0) {
3103                 DEBUG(0,("validate_de: Corrupt cache for key %s (len == 0) ?\n",
3104                                 keystr));
3105                 state->bad_entry = True;
3106                 state->success = False;
3107                 return 1;
3108         }
3109
3110         DEBUG(10,("validate_de: %s ok\n", keystr));
3111         return 0;
3112 }
3113
3114 static int validate_trustdoms(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3115                               struct validation_status *state)
3116 {
3117         struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3118         int32 num_domains, i;
3119
3120         if (!centry) {
3121                 return 1;
3122         }
3123
3124         num_domains = centry_uint32(centry);
3125         
3126         for (i=0; i< num_domains; i++) {
3127                 DOM_SID sid;
3128                 (void)centry_string(centry, mem_ctx);
3129                 (void)centry_string(centry, mem_ctx);
3130                 (void)centry_sid(centry, mem_ctx, &sid);
3131         }
3132
3133         centry_free(centry);
3134
3135         if (!(state->success)) {
3136                 return 1;
3137         }
3138         DEBUG(10,("validate_trustdoms: %s ok\n", keystr));
3139         return 0;
3140 }
3141
3142 static int validate_trustdomcache(TALLOC_CTX *mem_ctx, const char *keystr, 
3143                                   TDB_DATA dbuf,
3144                                   struct validation_status *state)
3145 {
3146         if (dbuf.dsize == 0) {
3147                 DEBUG(0, ("validate_trustdomcache: Corrupt cache for "
3148                           "key %s (len ==0) ?\n", keystr));
3149                 state->bad_entry = True;
3150                 state->success = False;
3151                 return 1;
3152         }
3153
3154         DEBUG(10,    ("validate_trustdomcache: %s ok\n", keystr));
3155         DEBUGADD(10, ("  Don't trust me, I am a DUMMY!\n"));
3156         return 0;
3157 }
3158
3159 static int validate_offline(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3160                             struct validation_status *state)
3161 {
3162         if (dbuf.dsize != 4) {
3163                 DEBUG(0,("validate_offline: Corrupt cache for key %s (len %u != 4) ?\n",
3164                                 keystr, (unsigned int)dbuf.dsize ));
3165                 state->bad_entry = True;
3166                 state->success = False;
3167                 return 1;
3168         }
3169         DEBUG(10,("validate_offline: %s ok\n", keystr));
3170         return 0;
3171 }
3172
3173 static int validate_cache_version(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3174                                   struct validation_status *state)
3175 {
3176         if (dbuf.dsize != 4) {
3177                 DEBUG(0, ("validate_cache_version: Corrupt cache for "
3178                           "key %s (len %u != 4) ?\n", 
3179                           keystr, (unsigned int)dbuf.dsize));
3180                 state->bad_entry = True;
3181                 state->success = False;
3182                 return 1;
3183         }
3184
3185         DEBUG(10, ("validate_cache_version: %s ok\n", keystr));
3186         return 0;
3187 }
3188
3189 /***********************************************************************
3190  A list of all possible cache tdb keys with associated validation
3191  functions.
3192 ***********************************************************************/
3193
3194 struct key_val_struct {
3195         const char *keyname;
3196         int (*validate_data_fn)(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf, struct validation_status* state);
3197 } key_val[] = {
3198         {"SEQNUM/", validate_seqnum},
3199         {"NS/", validate_ns},
3200         {"SN/", validate_sn},
3201         {"U/", validate_u},
3202         {"LOC_POL/", validate_loc_pol},
3203         {"PWD_POL/", validate_pwd_pol},
3204         {"CRED/", validate_cred},
3205         {"UL/", validate_ul},
3206         {"GL/", validate_gl},
3207         {"UG/", validate_ug},
3208         {"UA", validate_ua},
3209         {"GM/", validate_gm},
3210         {"DR/", validate_dr},
3211         {"DE/", validate_de},
3212         {"TRUSTDOMS/", validate_trustdoms},
3213         {"TRUSTDOMCACHE/", validate_trustdomcache},
3214         {"WINBINDD_OFFLINE", validate_offline},
3215         {WINBINDD_CACHE_VERSION_KEYSTR, validate_cache_version},
3216         {NULL, NULL}
3217 };
3218
3219 /***********************************************************************
3220  Function to look at every entry in the tdb and validate it as far as
3221  possible.
3222 ***********************************************************************/
3223
3224 static int cache_traverse_validate_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf, void *state)
3225 {
3226         int i;
3227         struct validation_status *v_state = (struct validation_status *)state;
3228
3229         /* Paranoia check. */
3230         if (kbuf.dsize > 1024) {
3231                 DEBUG(0,("cache_traverse_validate_fn: key length too large (%u) > 1024\n\n",
3232                                 (unsigned int)kbuf.dsize ));
3233                 return 1;
3234         }
3235
3236         for (i = 0; key_val[i].keyname; i++) {
3237                 size_t namelen = strlen(key_val[i].keyname);
3238                 if (kbuf.dsize >= namelen && (
3239                                 strncmp(key_val[i].keyname, (const char *)kbuf.dptr, namelen)) == 0) {
3240                         TALLOC_CTX *mem_ctx;
3241                         char *keystr;
3242                         int ret;
3243
3244                         keystr = SMB_MALLOC_ARRAY(char, kbuf.dsize+1);
3245                         if (!keystr) {
3246                                 return 1;
3247                         }
3248                         memcpy(keystr, kbuf.dptr, kbuf.dsize);
3249                         keystr[kbuf.dsize] = '\0';
3250
3251                         mem_ctx = talloc_init("validate_ctx");
3252                         if (!mem_ctx) {
3253                                 SAFE_FREE(keystr);
3254                                 return 1;
3255                         }
3256
3257                         ret = key_val[i].validate_data_fn(mem_ctx, keystr, dbuf, 
3258                                                           v_state);
3259
3260                         SAFE_FREE(keystr);
3261                         talloc_destroy(mem_ctx);
3262                         return ret;
3263                 }
3264         }
3265
3266         DEBUG(0,("cache_traverse_validate_fn: unknown cache entry\nkey :\n"));
3267         dump_data(0, (uint8 *)kbuf.dptr, kbuf.dsize);
3268         DEBUG(0,("data :\n"));
3269         dump_data(0, (uint8 *)dbuf.dptr, dbuf.dsize);
3270         v_state->unknown_key = True;
3271         v_state->success = False;
3272         return 1; /* terminate. */
3273 }
3274
3275 static void validate_panic(const char *const why)
3276 {
3277         DEBUG(0,("validating cache: would panic %s\n", why ));
3278         DEBUGADD(0, ("exiting instead (cache validation mode)\n"));
3279         exit(47);
3280 }
3281
3282 /***********************************************************************
3283  Try and validate every entry in the winbindd cache. If we fail here,
3284  delete the cache tdb and return non-zero - the caller (main winbindd
3285  function) will restart us as we don't know if we crashed or not.
3286 ***********************************************************************/
3287
3288 /* 
3289  * internal validation function, executed by the child.  
3290  */
3291 static int winbindd_validate_cache_child(const char *cache_path, int pfd)
3292 {
3293         int ret = -1;
3294         int tfd = -1;
3295         int num_entries = 0;
3296         TDB_CONTEXT *tdb = NULL;
3297         struct validation_status v_status;
3298         
3299         v_status.tdb_error = False;
3300         v_status.bad_freelist = False;
3301         v_status.bad_entry = False;
3302         v_status.unknown_key = False;
3303         v_status.success = True;
3304
3305         tdb = tdb_open_log(cache_path,
3306                         WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
3307                         lp_winbind_offline_logon() 
3308                                 ?  TDB_DEFAULT 
3309                                 : (TDB_DEFAULT | TDB_CLEAR_IF_FIRST),
3310                         O_RDWR|O_CREAT, 0600);
3311         if (!tdb) {
3312                 v_status.tdb_error = True;
3313                 v_status.success = False;
3314                 goto out;
3315         }
3316
3317         tfd = tdb_fd(tdb);
3318
3319         /* Check the cache freelist is good. */
3320         if (tdb_validate_freelist(tdb, &num_entries) == -1) {
3321                 DEBUG(0,("winbindd_validate_cache_child: bad freelist in cache %s\n",
3322                         cache_path));
3323                 v_status.bad_freelist = True;
3324                 v_status.success = False;
3325                 goto out;
3326         }
3327
3328         DEBUG(10,("winbindd_validate_cache_child: cache %s freelist has %d entries\n",
3329                 cache_path, num_entries));
3330
3331         /* Now traverse the cache to validate it. */
3332         num_entries = tdb_traverse(tdb, cache_traverse_validate_fn, (void *)&v_status);
3333         if (num_entries == -1 || !(v_status.success)) {
3334                 DEBUG(0,("winbindd_validate_cache_child: cache %s traverse failed\n",
3335                         cache_path));
3336                 if (!(v_status.success)) {
3337                         if (v_status.bad_entry) {
3338                                 DEBUGADD(0, (" -> bad entry found\n"));
3339                         }
3340                         if (v_status.unknown_key) {
3341                                 DEBUGADD(0, (" -> unknown key encountered\n"));
3342                         }
3343                 }
3344                 goto out;
3345         }
3346
3347         DEBUG(10,("winbindd_validate_cache_child: cache %s is good "
3348                 "with %d entries\n", cache_path, num_entries));
3349         ret = 0; /* Cache is good. */
3350
3351 out:
3352         if (tdb) {
3353                 if (ret == 0) {
3354                         tdb_close(tdb);
3355                 } 
3356                 else if (tfd != -1) {
3357                         close(tfd);
3358                 }
3359         }
3360
3361         DEBUG(10, ("winbindd_validate_cache_child: writing status to pipe\n"));
3362         write (pfd, (const char *)&v_status, sizeof(v_status));
3363         close(pfd);
3364
3365         return ret;
3366 }
3367
3368 int winbindd_validate_cache(void)
3369 {
3370         pid_t child_pid = -1;
3371         int child_status = 0;
3372         int wait_pid = 0;
3373         int ret = -1;
3374         int pipe_fds[2];
3375         struct validation_status v_status;
3376         int bytes_read = 0;
3377         const char *cache_path = lock_path("winbindd_cache.tdb");
3378         
3379         DEBUG(10, ("winbindd_validate_cache: replacing panic function\n"));
3380         smb_panic_fn = validate_panic;
3381
3382         /* fork and let the child do the validation. 
3383          * benefit: no need to twist signal handlers and panic functions.
3384          * just let the child panic. we catch the signal. 
3385          * communicate the extended status struct over a pipe. */
3386
3387         if (pipe(pipe_fds) != 0) {
3388                 DEBUG(0, ("winbindd_validate_cache: unable to create pipe, "
3389                           "error %s", strerror(errno)));
3390                 smb_panic("winbind_validate_cache: unable to create pipe.");
3391         }
3392
3393         DEBUG(10, ("winbindd_validate_cache: forking to let child do validation.\n"));
3394         child_pid = sys_fork();
3395         if (child_pid == 0) {
3396                 DEBUG(10, ("winbindd_validate_cache (validation child): created\n"));
3397                 close(pipe_fds[0]); /* close reading fd */
3398                 DEBUG(10, ("winbindd_validate_cache (validation child): "
3399                            "calling winbindd_validate_cache_child\n"));
3400                 exit(winbindd_validate_cache_child(cache_path, pipe_fds[1]));
3401         }
3402         else if (child_pid < 0) {
3403                 smb_panic("winbindd_validate_cache: fork for validation failed.");
3404         }
3405
3406         /* parent */
3407
3408         DEBUG(10, ("winbindd_validate_cache: fork succeeded, child PID = %d\n", 
3409                    child_pid));
3410         close(pipe_fds[1]); /* close writing fd */
3411
3412         v_status.success = True;
3413         v_status.bad_entry = False;
3414         v_status.unknown_key = False;
3415
3416         DEBUG(10, ("winbindd_validate_cache: reading from pipe.\n"));
3417         bytes_read = read(pipe_fds[0], (void *)&v_status, sizeof(v_status));
3418         close(pipe_fds[0]);
3419
3420         if (bytes_read != sizeof(v_status)) {
3421                 DEBUG(10, ("winbindd_validate_cache: read %d bytes from pipe "
3422                            "but expected %d", bytes_read, (int)sizeof(v_status)));
3423                 DEBUGADD(10, (" -> assuming child crashed\n"));
3424                 v_status.success = False;
3425         }
3426         else {
3427                 DEBUG(10,    ("winbindd_validate_cache: read status from child\n"));
3428                 DEBUGADD(10, (" *  tdb error: %s\n", v_status.tdb_error ? "yes" : "no"));
3429                 DEBUGADD(10, (" *  bad freelist: %s\n", v_status.bad_freelist ? "yes" : "no"));
3430                 DEBUGADD(10, (" *  bad entry: %s\n", v_status.bad_entry ? "yes" : "no"));
3431                 DEBUGADD(10, (" *  unknown key: %s\n", v_status.unknown_key ? "yes" : "no"));
3432                 DEBUGADD(10, (" => overall success: %s\n", v_status.success ? "yes" : "no"));
3433         }
3434
3435         if (!v_status.success) {
3436                 DEBUG(10, ("winbindd_validate_cache: validation not successful.\n"));
3437                 DEBUGADD(10, ("removing tdb %s.\n", cache_path));
3438                 unlink(cache_path);
3439         }
3440
3441         DEBUG(10, ("winbindd_validate_cache: waiting for child to finish...\n"));
3442         while  ((wait_pid = sys_waitpid(child_pid, &child_status, 0)) < 0) {
3443                 if (errno == EINTR) {
3444                         DEBUG(10, ("winbindd_validate_cache: got signal during "
3445                                    "waitpid, retrying\n"));
3446                         errno = 0;
3447                         continue;
3448                 }
3449                 DEBUG(0, ("winbindd_validate_cache: waitpid failed with "
3450                           "errno %s\n", strerror(errno)));
3451                 smb_panic("winbindd_validate_cache: waitpid failed.");
3452         }
3453         if (wait_pid != child_pid) {
3454                 DEBUG(0, ("winbindd_validate_cache: waitpid returned pid %d, "
3455                           "but %d was expexted\n", wait_pid, child_pid));
3456                 smb_panic("winbindd_validate_cache: waitpid returned "
3457                              "unexpected PID.");
3458         }
3459
3460                 
3461         DEBUG(10, ("winbindd_validate_cache: validating child returned.\n"));
3462         if (WIFEXITED(child_status)) {
3463                 DEBUG(10, ("winbindd_validate_cache: child exited, code %d.\n",
3464                            WEXITSTATUS(child_status)));
3465                 ret = WEXITSTATUS(child_status);
3466         }
3467         if (WIFSIGNALED(child_status)) {
3468                 DEBUG(10, ("winbindd_validate_cache: child terminated "
3469                            "by signal %d\n", WTERMSIG(child_status)));
3470 #ifdef WCOREDUMP
3471                 if (WCOREDUMP(child_status)) {
3472                         DEBUGADD(10, ("core dumped\n"));
3473                 }
3474 #endif
3475                 ret = WTERMSIG(child_status);
3476         }
3477         if (WIFSTOPPED(child_status)) {
3478                 DEBUG(10, ("winbindd_validate_cache: child was stopped "
3479                            "by signal %d\n",
3480                            WSTOPSIG(child_status)));
3481                 ret = WSTOPSIG(child_status);
3482         }
3483
3484         DEBUG(10, ("winbindd_validate_cache: restoring panic function\n"));
3485         smb_panic_fn = smb_panic;
3486         return ret;
3487 }
3488
3489 /*********************************************************************
3490  ********************************************************************/
3491
3492 static BOOL add_wbdomain_to_tdc_array( struct winbindd_domain *new_dom,
3493                                        struct winbindd_tdc_domain **domains, 
3494                                        size_t *num_domains )
3495 {
3496         struct winbindd_tdc_domain *list = NULL;
3497         size_t idx;
3498         int i;
3499         BOOL set_only = False;  
3500         
3501         /* don't allow duplicates */
3502
3503         idx = *num_domains;
3504         list = *domains;
3505         
3506         for ( i=0; i< (*num_domains); i++ ) {
3507                 if ( strequal( new_dom->name, list[i].domain_name ) ) {
3508                         DEBUG(10,("add_wbdomain_to_tdc_array: Found existing record for %s\n",
3509                                   new_dom->name));
3510                         idx = i;
3511                         set_only = True;
3512                         
3513                         break;
3514                 }
3515         }
3516
3517         if ( !set_only ) {
3518                 if ( !*domains ) {
3519                         list = TALLOC_ARRAY( NULL, struct winbindd_tdc_domain, 1 );
3520                         idx = 0;
3521                 } else {
3522                         list = TALLOC_REALLOC_ARRAY( *domains, *domains, 
3523                                                      struct winbindd_tdc_domain,  
3524                                                      (*num_domains)+1);
3525                         idx = *num_domains;             
3526                 }
3527
3528                 ZERO_STRUCT( list[idx] );
3529         }
3530
3531         if ( !list )
3532                 return False;
3533
3534         list[idx].domain_name = talloc_strdup( list, new_dom->name );
3535         list[idx].dns_name = talloc_strdup( list, new_dom->alt_name );
3536
3537         if ( !is_null_sid( &new_dom->sid ) )
3538                 sid_copy( &list[idx].sid, &new_dom->sid );
3539
3540         if ( new_dom->domain_flags != 0x0 )
3541                 list[idx].trust_flags = new_dom->domain_flags;  
3542
3543         if ( new_dom->domain_type != 0x0 )
3544                 list[idx].trust_type = new_dom->domain_type;
3545
3546         if ( new_dom->domain_trust_attribs != 0x0 )
3547                 list[idx].trust_attribs = new_dom->domain_trust_attribs;
3548         
3549         if ( !set_only ) {
3550                 *domains = list;
3551                 *num_domains = idx + 1; 
3552         }
3553
3554         return True;    
3555 }
3556
3557 /*********************************************************************
3558  ********************************************************************/
3559
3560 static TDB_DATA make_tdc_key( const char *domain_name )
3561 {
3562         char *keystr = NULL;
3563         TDB_DATA key = { NULL, 0 };
3564         
3565         if ( !domain_name ) {
3566                 DEBUG(5,("make_tdc_key: Keyname workgroup is NULL!\n"));
3567                 return key;
3568         }
3569                
3570                 
3571         asprintf( &keystr, "TRUSTDOMCACHE/%s", domain_name );
3572         key = string_term_tdb_data(keystr);
3573         
3574         return key;     
3575 }
3576
3577 /*********************************************************************
3578  ********************************************************************/
3579
3580 static int pack_tdc_domains( struct winbindd_tdc_domain *domains, 
3581                              size_t num_domains,
3582                              unsigned char **buf )
3583 {
3584         unsigned char *buffer = NULL;
3585         int len = 0;
3586         int buflen = 0;
3587         int i = 0;
3588
3589         DEBUG(10,("pack_tdc_domains: Packing %d trusted domains\n",
3590                   (int)num_domains));
3591         
3592         buflen = 0;
3593         
3594  again: 
3595         len = 0;
3596         
3597         /* Store the number of array items first */
3598         len += tdb_pack( buffer+len, buflen-len, "d", 
3599                          num_domains );
3600
3601         /* now pack each domain trust record */
3602         for ( i=0; i<num_domains; i++ ) {
3603
3604                 if ( buflen > 0 ) {
3605                         DEBUG(10,("pack_tdc_domains: Packing domain %s (%s)\n",
3606                                   domains[i].domain_name,
3607                                   domains[i].dns_name ? domains[i].dns_name : "UNKNOWN" ));
3608                 }
3609                 
3610                 len += tdb_pack( buffer+len, buflen-len, "fffddd",
3611                                  domains[i].domain_name,
3612                                  domains[i].dns_name,
3613                                  sid_string_static(&domains[i].sid),
3614                                  domains[i].trust_flags,
3615                                  domains[i].trust_attribs,
3616                                  domains[i].trust_type );
3617         }
3618
3619         if ( buflen < len ) {
3620                 SAFE_FREE(buffer);
3621                 if ( (buffer = SMB_MALLOC_ARRAY(unsigned char, len)) == NULL ) {
3622                         DEBUG(0,("pack_tdc_domains: failed to alloc buffer!\n"));
3623                         buflen = -1;
3624                         goto done;
3625                 }
3626                 buflen = len;
3627                 goto again;
3628         }
3629
3630         *buf = buffer;  
3631         
3632  done:  
3633         return buflen;  
3634 }
3635
3636 /*********************************************************************
3637  ********************************************************************/
3638
3639 static size_t unpack_tdc_domains( unsigned char *buf, int buflen, 
3640                                   struct winbindd_tdc_domain **domains )
3641 {
3642         fstring domain_name, dns_name, sid_string;      
3643         uint32 type, attribs, flags;
3644         int num_domains;
3645         int len = 0;
3646         int i;
3647         struct winbindd_tdc_domain *list = NULL;
3648
3649         /* get the number of domains */
3650         len += tdb_unpack( buf+len, buflen-len, "d", &num_domains);
3651         if ( len == -1 ) {
3652                 DEBUG(5,("unpack_tdc_domains: Failed to unpack domain array\n"));               
3653                 return 0;
3654         }
3655
3656         list = TALLOC_ARRAY( NULL, struct winbindd_tdc_domain, num_domains );
3657         if ( !list ) {
3658                 DEBUG(0,("unpack_tdc_domains: Failed to talloc() domain list!\n"));
3659                 return 0;               
3660         }
3661         
3662         for ( i=0; i<num_domains; i++ ) {
3663                 len += tdb_unpack( buf+len, buflen-len, "fffddd",
3664                                    domain_name,
3665                                    dns_name,
3666                                    sid_string,
3667                                    &flags,
3668                                    &attribs,
3669                                    &type );
3670
3671                 if ( len == -1 ) {
3672                         DEBUG(5,("unpack_tdc_domains: Failed to unpack domain array\n"));
3673                         TALLOC_FREE( list );                    
3674                         return 0;
3675                 }
3676
3677                 DEBUG(11,("unpack_tdc_domains: Unpacking domain %s (%s) "
3678                           "SID %s, flags = 0x%x, attribs = 0x%x, type = 0x%x\n",
3679                           domain_name, dns_name, sid_string,
3680                           flags, attribs, type));
3681                 
3682                 list[i].domain_name = talloc_strdup( list, domain_name );
3683                 list[i].dns_name = talloc_strdup( list, dns_name );
3684                 if ( !string_to_sid( &(list[i].sid), sid_string ) ) {                   
3685                         DEBUG(10,("unpack_tdc_domains: no SID for domain %s\n",
3686                                   domain_name));
3687                 }
3688                 list[i].trust_flags = flags;
3689                 list[i].trust_attribs = attribs;
3690                 list[i].trust_type = type;
3691         }
3692
3693         *domains = list;
3694         
3695         return num_domains;
3696 }
3697
3698 /*********************************************************************
3699  ********************************************************************/
3700
3701 static BOOL wcache_tdc_store_list( struct winbindd_tdc_domain *domains, size_t num_domains )
3702 {
3703         TDB_DATA key = make_tdc_key( lp_workgroup() );   
3704         TDB_DATA data = { NULL, 0 };
3705         int ret;
3706         
3707         if ( !key.dptr )
3708                 return False;
3709         
3710         /* See if we were asked to delete the cache entry */
3711
3712         if ( !domains ) {
3713                 ret = tdb_delete( wcache->tdb, key );
3714                 goto done;
3715         }
3716         
3717         data.dsize = pack_tdc_domains( domains, num_domains, &data.dptr );
3718         
3719         if ( !data.dptr ) {
3720                 ret = -1;
3721                 goto done;
3722         }
3723                 
3724         ret = tdb_store( wcache->tdb, key, data, 0 );
3725
3726  done:
3727         SAFE_FREE( data.dptr );
3728         SAFE_FREE( key.dptr );
3729         
3730         return ( ret != -1 );   
3731 }
3732
3733 /*********************************************************************
3734  ********************************************************************/
3735
3736 BOOL wcache_tdc_fetch_list( struct winbindd_tdc_domain **domains, size_t *num_domains )
3737 {
3738         TDB_DATA key = make_tdc_key( lp_workgroup() );
3739         TDB_DATA data = { NULL, 0 };
3740
3741         *domains = NULL;        
3742         *num_domains = 0;       
3743
3744         if ( !key.dptr )
3745                 return False;
3746         
3747         data = tdb_fetch( wcache->tdb, key );
3748
3749         SAFE_FREE( key.dptr );
3750         
3751         if ( !data.dptr ) 
3752                 return False;
3753         
3754         *num_domains = unpack_tdc_domains( data.dptr, data.dsize, domains );
3755
3756         SAFE_FREE( data.dptr );
3757         
3758         if ( !*domains )
3759                 return False;
3760
3761         return True;    
3762 }
3763
3764 /*********************************************************************
3765  ********************************************************************/
3766
3767 BOOL wcache_tdc_add_domain( struct winbindd_domain *domain )
3768 {
3769         struct winbindd_tdc_domain *dom_list = NULL;
3770         size_t num_domains = 0;
3771         BOOL ret = False;       
3772
3773         DEBUG(10,("wcache_tdc_add_domain: Adding domain %s (%s), SID %s, "
3774                   "flags = 0x%x, attributes = 0x%x, type = 0x%x\n",
3775                   domain->name, domain->alt_name, 
3776                   sid_string_static(&domain->sid),
3777                   domain->domain_flags,
3778                   domain->domain_trust_attribs,
3779                   domain->domain_type));        
3780         
3781         if ( !init_wcache() ) {
3782                 return False;
3783         }
3784         
3785         /* fetch the list */
3786
3787         wcache_tdc_fetch_list( &dom_list, &num_domains );
3788         
3789         /* add the new domain */
3790
3791         if ( !add_wbdomain_to_tdc_array( domain, &dom_list, &num_domains ) ) {
3792                 goto done;              
3793         }       
3794
3795         /* pack the domain */
3796
3797         if ( !wcache_tdc_store_list( dom_list, num_domains ) ) {
3798                 goto done;              
3799         }
3800         
3801         /* Success */
3802
3803         ret = True;     
3804  done:
3805         TALLOC_FREE( dom_list );
3806         
3807         return ret;     
3808 }
3809
3810 /*********************************************************************
3811  ********************************************************************/
3812
3813 struct winbindd_tdc_domain * wcache_tdc_fetch_domain( TALLOC_CTX *ctx, const char *name )
3814 {
3815         struct winbindd_tdc_domain *dom_list = NULL;
3816         size_t num_domains = 0;
3817         int i;
3818         struct winbindd_tdc_domain *d = NULL;   
3819
3820         DEBUG(10,("wcache_tdc_fetch_domain: Searching for domain %s\n", name));
3821
3822         if ( !init_wcache() ) {
3823                 return False;
3824         }
3825         
3826         /* fetch the list */
3827
3828         wcache_tdc_fetch_list( &dom_list, &num_domains );
3829         
3830         for ( i=0; i<num_domains; i++ ) {
3831                 if ( strequal(name, dom_list[i].domain_name) ||
3832                      strequal(name, dom_list[i].dns_name) )
3833                 {
3834                         DEBUG(10,("wcache_tdc_fetch_domain: Found domain %s\n",
3835                                   name));
3836                         
3837                         d = TALLOC_P( ctx, struct winbindd_tdc_domain );
3838                         if ( !d )
3839                                 break;                  
3840                         
3841                         d->domain_name = talloc_strdup( d, dom_list[i].domain_name );
3842                         d->dns_name = talloc_strdup( d, dom_list[i].dns_name );
3843                         sid_copy( &d->sid, &dom_list[i].sid );
3844                         d->trust_flags   = dom_list[i].trust_flags;
3845                         d->trust_type    = dom_list[i].trust_type;
3846                         d->trust_attribs = dom_list[i].trust_attribs;
3847
3848                         break;
3849                 }
3850         }
3851
3852         TALLOC_FREE( dom_list );
3853         
3854         return d;       
3855 }
3856
3857
3858 /*********************************************************************
3859  ********************************************************************/
3860
3861 void wcache_tdc_clear( void )
3862 {
3863         if ( !init_wcache() )
3864                 return;
3865
3866         wcache_tdc_store_list( NULL, 0 );
3867         
3868         return; 
3869 }
3870
3871
3872 /*********************************************************************
3873  ********************************************************************/
3874
3875 static void wcache_save_user_pwinfo(struct winbindd_domain *domain, 
3876                                     NTSTATUS status,
3877                                     const DOM_SID *user_sid,
3878                                     const char *homedir,
3879                                     const char *shell,
3880                                     const char *gecos,
3881                                     uint32 gid)
3882 {
3883         struct cache_entry *centry;
3884
3885         if ( (centry = centry_start(domain, status)) == NULL )
3886                 return;
3887
3888         centry_put_string( centry, homedir );
3889         centry_put_string( centry, shell );
3890         centry_put_string( centry, gecos );
3891         centry_put_uint32( centry, gid );
3892         
3893         centry_end(centry, "NSS/PWINFO/%s", sid_string_static(user_sid) );
3894
3895         DEBUG(10,("wcache_save_user_pwinfo: %s\n", sid_string_static(user_sid) ));
3896
3897         centry_free(centry);
3898 }
3899
3900 NTSTATUS nss_get_info_cached( struct winbindd_domain *domain, 
3901                               const DOM_SID *user_sid,
3902                               TALLOC_CTX *ctx,
3903                               ADS_STRUCT *ads, LDAPMessage *msg,
3904                               char **homedir, char **shell, char **gecos,
3905                               gid_t *p_gid)
3906 {
3907         struct winbind_cache *cache = get_cache(domain);
3908         struct cache_entry *centry = NULL;
3909         NTSTATUS nt_status;
3910
3911         if (!cache->tdb)
3912                 goto do_query;
3913
3914         centry = wcache_fetch(cache, domain, "NSS/PWINFO/%s", sid_string_static(user_sid));     
3915         
3916         if (!centry)
3917                 goto do_query;
3918
3919         *homedir = centry_string( centry, ctx );
3920         *shell   = centry_string( centry, ctx );
3921         *gecos   = centry_string( centry, ctx );
3922         *p_gid   = centry_uint32( centry );     
3923
3924         centry_free(centry);
3925
3926         DEBUG(10,("nss_get_info_cached: [Cached] - user_sid %s\n",
3927                   sid_string_static(user_sid)));
3928
3929         return NT_STATUS_OK;
3930
3931 do_query:
3932         
3933         nt_status = nss_get_info( domain->name, user_sid, ctx, ads, msg, 
3934                                   homedir, shell, gecos, p_gid );
3935
3936         if ( NT_STATUS_IS_OK(nt_status) ) {
3937                 wcache_save_user_pwinfo( domain, nt_status, user_sid,
3938                                          *homedir, *shell, *gecos, *p_gid );
3939         }       
3940
3941         if ( NT_STATUS_EQUAL( nt_status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND ) ) {
3942                 DEBUG(5,("nss_get_info_cached: Setting domain %s offline\n",
3943                          domain->name ));
3944                 set_domain_offline( domain );
3945         }
3946
3947         return nt_status;       
3948 }
3949
3950
3951 /* the cache backend methods are exposed via this structure */
3952 struct winbindd_methods cache_methods = {
3953         True,
3954         query_user_list,
3955         enum_dom_groups,
3956         enum_local_groups,
3957         name_to_sid,
3958         sid_to_name,
3959         rids_to_names,
3960         query_user,
3961         lookup_usergroups,
3962         lookup_useraliases,
3963         lookup_groupmem,
3964         sequence_number,
3965         lockout_policy,
3966         password_policy,
3967         trusted_domains
3968 };