r10656: BIG merge from trunk. Features not copied over
[vlendec/samba-autobuild/.git] / source3 / nsswitch / winbindd_cache.c
1 /* 
2    Unix SMB/CIFS implementation.
3
4    Winbind cache backend functions
5
6    Copyright (C) Andrew Tridgell 2001
7    Copyright (C) Gerald Carter   2003
8    Copyright (C) Volker Lendecke 2005
9    
10    
11    This program is free software; you can redistribute it and/or modify
12    it under the terms of the GNU General Public License as published by
13    the Free Software Foundation; either version 2 of the License, or
14    (at your option) any later version.
15    
16    This program is distributed in the hope that it will be useful,
17    but WITHOUT ANY WARRANTY; without even the implied warranty of
18    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19    GNU General Public License for more details.
20    
21    You should have received a copy of the GNU General Public License
22    along with this program; if not, write to the Free Software
23    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
24 */
25
26 #include "includes.h"
27 #include "winbindd.h"
28
29 #undef DBGC_CLASS
30 #define DBGC_CLASS DBGC_WINBIND
31
32 struct winbind_cache {
33         TDB_CONTEXT *tdb;
34 };
35
36 struct cache_entry {
37         NTSTATUS status;
38         uint32 sequence_number;
39         uint8 *data;
40         uint32 len, ofs;
41 };
42
43 #define WINBINDD_MAX_CACHE_SIZE (50*1024*1024)
44
45 static struct winbind_cache *wcache;
46
47 /* flush the cache */
48 void wcache_flush_cache(void)
49 {
50         extern BOOL opt_nocache;
51
52         if (!wcache)
53                 return;
54         if (wcache->tdb) {
55                 tdb_close(wcache->tdb);
56                 wcache->tdb = NULL;
57         }
58         if (opt_nocache)
59                 return;
60
61         wcache->tdb = tdb_open_log(lock_path("winbindd_cache.tdb"), 5000, 
62                                    TDB_CLEAR_IF_FIRST, O_RDWR|O_CREAT, 0600);
63
64         if (!wcache->tdb) {
65                 DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
66         }
67         DEBUG(10,("wcache_flush_cache success\n"));
68 }
69
70 void winbindd_check_cache_size(time_t t)
71 {
72         static time_t last_check_time;
73         struct stat st;
74
75         if (last_check_time == (time_t)0)
76                 last_check_time = t;
77
78         if (t - last_check_time < 60 && t - last_check_time > 0)
79                 return;
80
81         if (wcache == NULL || wcache->tdb == NULL) {
82                 DEBUG(0, ("Unable to check size of tdb cache - cache not open !\n"));
83                 return;
84         }
85
86         if (fstat(wcache->tdb->fd, &st) == -1) {
87                 DEBUG(0, ("Unable to check size of tdb cache %s!\n", strerror(errno) ));
88                 return;
89         }
90
91         if (st.st_size > WINBINDD_MAX_CACHE_SIZE) {
92                 DEBUG(10,("flushing cache due to size (%lu) > (%lu)\n",
93                         (unsigned long)st.st_size,
94                         (unsigned long)WINBINDD_MAX_CACHE_SIZE));
95                 wcache_flush_cache();
96         }
97 }
98
99 /* get the winbind_cache structure */
100 static struct winbind_cache *get_cache(struct winbindd_domain *domain)
101 {
102         struct winbind_cache *ret = wcache;
103
104         /* we have to know what type of domain we are dealing with first */
105
106         if ( !domain->initialized )
107                 set_dc_type_and_flags( domain );
108
109         if (!domain->backend) {
110                 extern struct winbindd_methods reconnect_methods;
111                 switch (lp_security()) {
112 #ifdef HAVE_ADS
113                 case SEC_ADS: {
114                         extern struct winbindd_methods ads_methods;
115                         /* always obey the lp_security parameter for our domain */
116                         if (domain->primary) {
117                                 domain->backend = &ads_methods;
118                                 break;
119                         }
120
121                         /* only use ADS for native modes at the momment.
122                            The problem is the correct detection of mixed 
123                            mode domains from NT4 BDC's    --jerry */
124                         
125                         if ( domain->native_mode ) {
126                                 DEBUG(5,("get_cache: Setting ADS methods for domain %s\n",
127                                         domain->name));
128                                 domain->backend = &ads_methods;
129                                 break;
130                         }
131
132                         /* fall through */
133                 }       
134 #endif
135                 default:
136                         DEBUG(5,("get_cache: Setting MS-RPC methods for domain %s\n",
137                                 domain->name));
138                         domain->backend = &reconnect_methods;
139                 }
140         }
141
142         if (ret)
143                 return ret;
144         
145         ret = SMB_XMALLOC_P(struct winbind_cache);
146         ZERO_STRUCTP(ret);
147
148         wcache = ret;
149         wcache_flush_cache();
150
151         return ret;
152 }
153
154 /*
155   free a centry structure
156 */
157 static void centry_free(struct cache_entry *centry)
158 {
159         if (!centry)
160                 return;
161         SAFE_FREE(centry->data);
162         free(centry);
163 }
164
165 /*
166   pull a uint32 from a cache entry 
167 */
168 static uint32 centry_uint32(struct cache_entry *centry)
169 {
170         uint32 ret;
171         if (centry->len - centry->ofs < 4) {
172                 DEBUG(0,("centry corruption? needed 4 bytes, have %d\n", 
173                          centry->len - centry->ofs));
174                 smb_panic("centry_uint32");
175         }
176         ret = IVAL(centry->data, centry->ofs);
177         centry->ofs += 4;
178         return ret;
179 }
180
181 /*
182   pull a uint8 from a cache entry 
183 */
184 static uint8 centry_uint8(struct cache_entry *centry)
185 {
186         uint8 ret;
187         if (centry->len - centry->ofs < 1) {
188                 DEBUG(0,("centry corruption? needed 1 bytes, have %d\n", 
189                          centry->len - centry->ofs));
190                 smb_panic("centry_uint32");
191         }
192         ret = CVAL(centry->data, centry->ofs);
193         centry->ofs += 1;
194         return ret;
195 }
196
197 /* pull a string from a cache entry, using the supplied
198    talloc context 
199 */
200 static char *centry_string(struct cache_entry *centry, TALLOC_CTX *mem_ctx)
201 {
202         uint32 len;
203         char *ret;
204
205         len = centry_uint8(centry);
206
207         if (len == 0xFF) {
208                 /* a deliberate NULL string */
209                 return NULL;
210         }
211
212         if (centry->len - centry->ofs < len) {
213                 DEBUG(0,("centry corruption? needed %d bytes, have %d\n", 
214                          len, centry->len - centry->ofs));
215                 smb_panic("centry_string");
216         }
217
218         if (mem_ctx != NULL)
219                 ret = TALLOC(mem_ctx, len+1);
220         else
221                 ret = SMB_MALLOC(len+1);
222         if (!ret) {
223                 smb_panic("centry_string out of memory\n");
224         }
225         memcpy(ret,centry->data + centry->ofs, len);
226         ret[len] = 0;
227         centry->ofs += len;
228         return ret;
229 }
230
231 /* pull a string from a cache entry, using the supplied
232    talloc context 
233 */
234 static BOOL centry_sid(struct cache_entry *centry, DOM_SID *sid)
235 {
236         char *sid_string;
237         sid_string = centry_string(centry, NULL);
238         if (!string_to_sid(sid, sid_string)) {
239                 return False;
240         }
241         SAFE_FREE(sid_string);
242         return True;
243 }
244
245 /* the server is considered down if it can't give us a sequence number */
246 static BOOL wcache_server_down(struct winbindd_domain *domain)
247 {
248         BOOL ret;
249
250         if (!wcache->tdb)
251                 return False;
252
253         ret = (domain->sequence_number == DOM_SEQUENCE_NONE);
254
255         if (ret)
256                 DEBUG(10,("wcache_server_down: server for Domain %s down\n", 
257                         domain->name ));
258         return ret;
259 }
260
261 static NTSTATUS fetch_cache_seqnum( struct winbindd_domain *domain, time_t now )
262 {
263         TDB_DATA data;
264         fstring key;
265         uint32 time_diff;
266         
267         if (!wcache->tdb) {
268                 DEBUG(10,("fetch_cache_seqnum: tdb == NULL\n"));
269                 return NT_STATUS_UNSUCCESSFUL;
270         }
271                 
272         fstr_sprintf( key, "SEQNUM/%s", domain->name );
273         
274         data = tdb_fetch_bystring( wcache->tdb, key );
275         if ( !data.dptr || data.dsize!=8 ) {
276                 DEBUG(10,("fetch_cache_seqnum: invalid data size key [%s]\n", key ));
277                 return NT_STATUS_UNSUCCESSFUL;
278         }
279         
280         domain->sequence_number = IVAL(data.dptr, 0);
281         domain->last_seq_check  = IVAL(data.dptr, 4);
282         
283         SAFE_FREE(data.dptr);
284
285         /* have we expired? */
286         
287         time_diff = now - domain->last_seq_check;
288         if ( time_diff > lp_winbind_cache_time() ) {
289                 DEBUG(10,("fetch_cache_seqnum: timeout [%s][%u @ %u]\n",
290                         domain->name, domain->sequence_number,
291                         (uint32)domain->last_seq_check));
292                 return NT_STATUS_UNSUCCESSFUL;
293         }
294
295         DEBUG(10,("fetch_cache_seqnum: success [%s][%u @ %u]\n", 
296                 domain->name, domain->sequence_number, 
297                 (uint32)domain->last_seq_check));
298
299         return NT_STATUS_OK;
300 }
301
302 static NTSTATUS store_cache_seqnum( struct winbindd_domain *domain )
303 {
304         TDB_DATA data, key;
305         fstring key_str;
306         char buf[8];
307         
308         if (!wcache->tdb) {
309                 DEBUG(10,("store_cache_seqnum: tdb == NULL\n"));
310                 return NT_STATUS_UNSUCCESSFUL;
311         }
312                 
313         fstr_sprintf( key_str, "SEQNUM/%s", domain->name );
314         key.dptr = key_str;
315         key.dsize = strlen(key_str)+1;
316         
317         SIVAL(buf, 0, domain->sequence_number);
318         SIVAL(buf, 4, domain->last_seq_check);
319         data.dptr = buf;
320         data.dsize = 8;
321         
322         if ( tdb_store( wcache->tdb, key, data, TDB_REPLACE) == -1 ) {
323                 DEBUG(10,("store_cache_seqnum: tdb_store fail key [%s]\n", key_str ));
324                 return NT_STATUS_UNSUCCESSFUL;
325         }
326
327         DEBUG(10,("store_cache_seqnum: success [%s][%u @ %u]\n", 
328                 domain->name, domain->sequence_number, 
329                 (uint32)domain->last_seq_check));
330         
331         return NT_STATUS_OK;
332 }
333
334 /*
335   refresh the domain sequence number. If force is True
336   then always refresh it, no matter how recently we fetched it
337 */
338
339 static void refresh_sequence_number(struct winbindd_domain *domain, BOOL force)
340 {
341         NTSTATUS status;
342         unsigned time_diff;
343         time_t t = time(NULL);
344         unsigned cache_time = lp_winbind_cache_time();
345
346         get_cache( domain );
347
348 #if 0   /* JERRY -- disable as the default cache time is now 5 minutes */
349         /* trying to reconnect is expensive, don't do it too often */
350         if (domain->sequence_number == DOM_SEQUENCE_NONE) {
351                 cache_time *= 8;
352         }
353 #endif
354
355         time_diff = t - domain->last_seq_check;
356
357         /* see if we have to refetch the domain sequence number */
358         if (!force && (time_diff < cache_time)) {
359                 DEBUG(10, ("refresh_sequence_number: %s time ok\n", domain->name));
360                 goto done;
361         }
362         
363         /* try to get the sequence number from the tdb cache first */
364         /* this will update the timestamp as well */
365         
366         status = fetch_cache_seqnum( domain, t );
367         if ( NT_STATUS_IS_OK(status) )
368                 goto done;      
369
370         /* important! make sure that we know if this is a native 
371            mode domain or not */
372
373         status = domain->backend->sequence_number(domain, &domain->sequence_number);
374
375         if (!NT_STATUS_IS_OK(status)) {
376                 domain->sequence_number = DOM_SEQUENCE_NONE;
377         }
378         
379         domain->last_status = status;
380         domain->last_seq_check = time(NULL);
381         
382         /* save the new sequence number ni the cache */
383         store_cache_seqnum( domain );
384
385 done:
386         DEBUG(10, ("refresh_sequence_number: %s seq number is now %d\n", 
387                    domain->name, domain->sequence_number));
388
389         return;
390 }
391
392 /*
393   decide if a cache entry has expired
394 */
395 static BOOL centry_expired(struct winbindd_domain *domain, const char *keystr, struct cache_entry *centry)
396 {
397         /* if the server is OK and our cache entry came from when it was down then
398            the entry is invalid */
399         if (domain->sequence_number != DOM_SEQUENCE_NONE && 
400             centry->sequence_number == DOM_SEQUENCE_NONE) {
401                 DEBUG(10,("centry_expired: Key %s for domain %s invalid sequence.\n",
402                         keystr, domain->name ));
403                 return True;
404         }
405
406         /* if the server is down or the cache entry is not older than the
407            current sequence number then it is OK */
408         if (wcache_server_down(domain) || 
409             centry->sequence_number == domain->sequence_number) {
410                 DEBUG(10,("centry_expired: Key %s for domain %s is good.\n",
411                         keystr, domain->name ));
412                 return False;
413         }
414
415         DEBUG(10,("centry_expired: Key %s for domain %s expired\n",
416                 keystr, domain->name ));
417
418         /* it's expired */
419         return True;
420 }
421
422 /*
423   fetch an entry from the cache, with a varargs key. auto-fetch the sequence
424   number and return status
425 */
426 static struct cache_entry *wcache_fetch(struct winbind_cache *cache, 
427                                         struct winbindd_domain *domain,
428                                         const char *format, ...) PRINTF_ATTRIBUTE(3,4);
429 static struct cache_entry *wcache_fetch(struct winbind_cache *cache, 
430                                         struct winbindd_domain *domain,
431                                         const char *format, ...)
432 {
433         va_list ap;
434         char *kstr;
435         TDB_DATA data;
436         struct cache_entry *centry;
437         TDB_DATA key;
438
439         refresh_sequence_number(domain, False);
440
441         va_start(ap, format);
442         smb_xvasprintf(&kstr, format, ap);
443         va_end(ap);
444         
445         key.dptr = kstr;
446         key.dsize = strlen(kstr);
447         data = tdb_fetch(wcache->tdb, key);
448         if (!data.dptr) {
449                 /* a cache miss */
450                 free(kstr);
451                 return NULL;
452         }
453
454         centry = SMB_XMALLOC_P(struct cache_entry);
455         centry->data = (unsigned char *)data.dptr;
456         centry->len = data.dsize;
457         centry->ofs = 0;
458
459         if (centry->len < 8) {
460                 /* huh? corrupt cache? */
461                 DEBUG(10,("wcache_fetch: Corrupt cache for key %s domain %s (len < 8) ?\n",
462                         kstr, domain->name ));
463                 centry_free(centry);
464                 free(kstr);
465                 return NULL;
466         }
467         
468         centry->status = NT_STATUS(centry_uint32(centry));
469         centry->sequence_number = centry_uint32(centry);
470
471         if (centry_expired(domain, kstr, centry)) {
472
473                 DEBUG(10,("wcache_fetch: entry %s expired for domain %s\n",
474                          kstr, domain->name ));
475
476                 centry_free(centry);
477                 free(kstr);
478                 return NULL;
479         }
480
481         DEBUG(10,("wcache_fetch: returning entry %s for domain %s\n",
482                  kstr, domain->name ));
483
484         free(kstr);
485         return centry;
486 }
487
488 /*
489   make sure we have at least len bytes available in a centry 
490 */
491 static void centry_expand(struct cache_entry *centry, uint32 len)
492 {
493         uint8 *p;
494         if (centry->len - centry->ofs >= len)
495                 return;
496         centry->len *= 2;
497         p = SMB_REALLOC(centry->data, centry->len);
498         if (!p) {
499                 DEBUG(0,("out of memory: needed %d bytes in centry_expand\n", centry->len));
500                 smb_panic("out of memory in centry_expand");
501         }
502         centry->data = p;
503 }
504
505 /*
506   push a uint32 into a centry 
507 */
508 static void centry_put_uint32(struct cache_entry *centry, uint32 v)
509 {
510         centry_expand(centry, 4);
511         SIVAL(centry->data, centry->ofs, v);
512         centry->ofs += 4;
513 }
514
515 /*
516   push a uint8 into a centry 
517 */
518 static void centry_put_uint8(struct cache_entry *centry, uint8 v)
519 {
520         centry_expand(centry, 1);
521         SCVAL(centry->data, centry->ofs, v);
522         centry->ofs += 1;
523 }
524
525 /* 
526    push a string into a centry 
527  */
528 static void centry_put_string(struct cache_entry *centry, const char *s)
529 {
530         int len;
531
532         if (!s) {
533                 /* null strings are marked as len 0xFFFF */
534                 centry_put_uint8(centry, 0xFF);
535                 return;
536         }
537
538         len = strlen(s);
539         /* can't handle more than 254 char strings. Truncating is probably best */
540         if (len > 254)
541                 len = 254;
542         centry_put_uint8(centry, len);
543         centry_expand(centry, len);
544         memcpy(centry->data + centry->ofs, s, len);
545         centry->ofs += len;
546 }
547
548 static void centry_put_sid(struct cache_entry *centry, const DOM_SID *sid) 
549 {
550         fstring sid_string;
551         centry_put_string(centry, sid_to_string(sid_string, sid));
552 }
553
554 /*
555   start a centry for output. When finished, call centry_end()
556 */
557 struct cache_entry *centry_start(struct winbindd_domain *domain, NTSTATUS status)
558 {
559         struct cache_entry *centry;
560
561         if (!wcache->tdb)
562                 return NULL;
563
564         centry = SMB_XMALLOC_P(struct cache_entry);
565
566         centry->len = 8192; /* reasonable default */
567         centry->data = SMB_XMALLOC_ARRAY(char, centry->len);
568         centry->ofs = 0;
569         centry->sequence_number = domain->sequence_number;
570         centry_put_uint32(centry, NT_STATUS_V(status));
571         centry_put_uint32(centry, centry->sequence_number);
572         return centry;
573 }
574
575 /*
576   finish a centry and write it to the tdb
577 */
578 static void centry_end(struct cache_entry *centry, const char *format, ...) PRINTF_ATTRIBUTE(2,3);
579 static void centry_end(struct cache_entry *centry, const char *format, ...)
580 {
581         va_list ap;
582         char *kstr;
583         TDB_DATA key, data;
584
585         va_start(ap, format);
586         smb_xvasprintf(&kstr, format, ap);
587         va_end(ap);
588
589         key.dptr = kstr;
590         key.dsize = strlen(kstr);
591         data.dptr = (char *)centry->data;
592         data.dsize = centry->ofs;
593
594         tdb_store(wcache->tdb, key, data, TDB_REPLACE);
595         free(kstr);
596 }
597
598 static void wcache_save_name_to_sid(struct winbindd_domain *domain, 
599                                     NTSTATUS status, const char *domain_name,
600                                     const char *name, const DOM_SID *sid, 
601                                     enum SID_NAME_USE type)
602 {
603         struct cache_entry *centry;
604         fstring uname;
605
606         centry = centry_start(domain, status);
607         if (!centry)
608                 return;
609         centry_put_uint32(centry, type);
610         centry_put_sid(centry, sid);
611         fstrcpy(uname, name);
612         strupper_m(uname);
613         centry_end(centry, "NS/%s/%s", domain_name, uname);
614         DEBUG(10,("wcache_save_name_to_sid: %s -> %s\n", uname,
615                   sid_string_static(sid)));
616         centry_free(centry);
617 }
618
619 static void wcache_save_sid_to_name(struct winbindd_domain *domain, NTSTATUS status, 
620                                     const DOM_SID *sid, const char *domain_name, const char *name, enum SID_NAME_USE type)
621 {
622         struct cache_entry *centry;
623         fstring sid_string;
624
625         centry = centry_start(domain, status);
626         if (!centry)
627                 return;
628         if (NT_STATUS_IS_OK(status)) {
629                 centry_put_uint32(centry, type);
630                 centry_put_string(centry, domain_name);
631                 centry_put_string(centry, name);
632         }
633         centry_end(centry, "SN/%s", sid_to_string(sid_string, sid));
634         DEBUG(10,("wcache_save_sid_to_name: %s -> %s\n", sid_string, name));
635         centry_free(centry);
636 }
637
638
639 static void wcache_save_user(struct winbindd_domain *domain, NTSTATUS status, WINBIND_USERINFO *info)
640 {
641         struct cache_entry *centry;
642         fstring sid_string;
643
644         centry = centry_start(domain, status);
645         if (!centry)
646                 return;
647         centry_put_string(centry, info->acct_name);
648         centry_put_string(centry, info->full_name);
649         centry_put_string(centry, info->homedir);
650         centry_put_string(centry, info->shell);
651         centry_put_sid(centry, &info->user_sid);
652         centry_put_sid(centry, &info->group_sid);
653         centry_end(centry, "U/%s", sid_to_string(sid_string, &info->user_sid));
654         DEBUG(10,("wcache_save_user: %s (acct_name %s)\n", sid_string, info->acct_name));
655         centry_free(centry);
656 }
657
658
659 /* Query display info. This is the basic user list fn */
660 static NTSTATUS query_user_list(struct winbindd_domain *domain,
661                                 TALLOC_CTX *mem_ctx,
662                                 uint32 *num_entries, 
663                                 WINBIND_USERINFO **info)
664 {
665         struct winbind_cache *cache = get_cache(domain);
666         struct cache_entry *centry = NULL;
667         NTSTATUS status;
668         unsigned int i, retry;
669
670         if (!cache->tdb)
671                 goto do_query;
672
673         centry = wcache_fetch(cache, domain, "UL/%s", domain->name);
674         if (!centry)
675                 goto do_query;
676
677         *num_entries = centry_uint32(centry);
678         
679         if (*num_entries == 0)
680                 goto do_cached;
681
682         (*info) = TALLOC_ARRAY(mem_ctx, WINBIND_USERINFO, *num_entries);
683         if (! (*info))
684                 smb_panic("query_user_list out of memory");
685         for (i=0; i<(*num_entries); i++) {
686                 (*info)[i].acct_name = centry_string(centry, mem_ctx);
687                 (*info)[i].full_name = centry_string(centry, mem_ctx);
688                 (*info)[i].homedir = centry_string(centry, mem_ctx);
689                 (*info)[i].shell = centry_string(centry, mem_ctx);
690                 centry_sid(centry, &(*info)[i].user_sid);
691                 centry_sid(centry, &(*info)[i].group_sid);
692         }
693
694 do_cached:      
695         status = centry->status;
696
697         DEBUG(10,("query_user_list: [Cached] - cached list for domain %s status %s\n",
698                 domain->name, get_friendly_nt_error_msg(status) ));
699
700         centry_free(centry);
701         return status;
702
703 do_query:
704         *num_entries = 0;
705         *info = NULL;
706
707         /* Return status value returned by seq number check */
708
709         if (!NT_STATUS_IS_OK(domain->last_status))
710                 return domain->last_status;
711
712         /* Put the query_user_list() in a retry loop.  There appears to be
713          * some bug either with Windows 2000 or Samba's handling of large
714          * rpc replies.  This manifests itself as sudden disconnection
715          * at a random point in the enumeration of a large (60k) user list.
716          * The retry loop simply tries the operation again. )-:  It's not
717          * pretty but an acceptable workaround until we work out what the
718          * real problem is. */
719
720         retry = 0;
721         do {
722
723                 DEBUG(10,("query_user_list: [Cached] - doing backend query for list for domain %s\n",
724                         domain->name ));
725
726                 status = domain->backend->query_user_list(domain, mem_ctx, num_entries, info);
727                 if (!NT_STATUS_IS_OK(status))
728                         DEBUG(3, ("query_user_list: returned 0x%08x, "
729                                   "retrying\n", NT_STATUS_V(status)));
730                         if (NT_STATUS_EQUAL(status, NT_STATUS_UNSUCCESSFUL)) {
731                                 DEBUG(3, ("query_user_list: flushing "
732                                           "connection cache\n"));
733                                 invalidate_cm_connection(&domain->conn);
734                         }
735
736         } while (NT_STATUS_V(status) == NT_STATUS_V(NT_STATUS_UNSUCCESSFUL) && 
737                  (retry++ < 5));
738
739         /* and save it */
740         refresh_sequence_number(domain, False);
741         centry = centry_start(domain, status);
742         if (!centry)
743                 goto skip_save;
744         centry_put_uint32(centry, *num_entries);
745         for (i=0; i<(*num_entries); i++) {
746                 centry_put_string(centry, (*info)[i].acct_name);
747                 centry_put_string(centry, (*info)[i].full_name);
748                 centry_put_string(centry, (*info)[i].homedir);
749                 centry_put_string(centry, (*info)[i].shell);
750                 centry_put_sid(centry, &(*info)[i].user_sid);
751                 centry_put_sid(centry, &(*info)[i].group_sid);
752                 if (domain->backend->consistent) {
753                         /* when the backend is consistent we can pre-prime some mappings */
754                         wcache_save_name_to_sid(domain, NT_STATUS_OK, 
755                                                 domain->name,
756                                                 (*info)[i].acct_name, 
757                                                 &(*info)[i].user_sid,
758                                                 SID_NAME_USER);
759                         wcache_save_sid_to_name(domain, NT_STATUS_OK, 
760                                                 &(*info)[i].user_sid,
761                                                 domain->name,
762                                                 (*info)[i].acct_name, 
763                                                 SID_NAME_USER);
764                         wcache_save_user(domain, NT_STATUS_OK, &(*info)[i]);
765                 }
766         }       
767         centry_end(centry, "UL/%s", domain->name);
768         centry_free(centry);
769
770 skip_save:
771         return status;
772 }
773
774 /* list all domain groups */
775 static NTSTATUS enum_dom_groups(struct winbindd_domain *domain,
776                                 TALLOC_CTX *mem_ctx,
777                                 uint32 *num_entries, 
778                                 struct acct_info **info)
779 {
780         struct winbind_cache *cache = get_cache(domain);
781         struct cache_entry *centry = NULL;
782         NTSTATUS status;
783         unsigned int i;
784
785         if (!cache->tdb)
786                 goto do_query;
787
788         centry = wcache_fetch(cache, domain, "GL/%s/domain", domain->name);
789         if (!centry)
790                 goto do_query;
791
792         *num_entries = centry_uint32(centry);
793         
794         if (*num_entries == 0)
795                 goto do_cached;
796
797         (*info) = TALLOC_ARRAY(mem_ctx, struct acct_info, *num_entries);
798         if (! (*info))
799                 smb_panic("enum_dom_groups out of memory");
800         for (i=0; i<(*num_entries); i++) {
801                 fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx));
802                 fstrcpy((*info)[i].acct_desc, centry_string(centry, mem_ctx));
803                 (*info)[i].rid = centry_uint32(centry);
804         }
805
806 do_cached:      
807         status = centry->status;
808
809         DEBUG(10,("enum_dom_groups: [Cached] - cached list for domain %s status %s\n",
810                 domain->name, get_friendly_nt_error_msg(status) ));
811
812         centry_free(centry);
813         return status;
814
815 do_query:
816         *num_entries = 0;
817         *info = NULL;
818
819         /* Return status value returned by seq number check */
820
821         if (!NT_STATUS_IS_OK(domain->last_status))
822                 return domain->last_status;
823
824         DEBUG(10,("enum_dom_groups: [Cached] - doing backend query for list for domain %s\n",
825                 domain->name ));
826
827         status = domain->backend->enum_dom_groups(domain, mem_ctx, num_entries, info);
828
829         /* and save it */
830         refresh_sequence_number(domain, False);
831         centry = centry_start(domain, status);
832         if (!centry)
833                 goto skip_save;
834         centry_put_uint32(centry, *num_entries);
835         for (i=0; i<(*num_entries); i++) {
836                 centry_put_string(centry, (*info)[i].acct_name);
837                 centry_put_string(centry, (*info)[i].acct_desc);
838                 centry_put_uint32(centry, (*info)[i].rid);
839         }       
840         centry_end(centry, "GL/%s/domain", domain->name);
841         centry_free(centry);
842
843 skip_save:
844         return status;
845 }
846
847 /* list all domain groups */
848 static NTSTATUS enum_local_groups(struct winbindd_domain *domain,
849                                 TALLOC_CTX *mem_ctx,
850                                 uint32 *num_entries, 
851                                 struct acct_info **info)
852 {
853         struct winbind_cache *cache = get_cache(domain);
854         struct cache_entry *centry = NULL;
855         NTSTATUS status;
856         unsigned int i;
857
858         if (!cache->tdb)
859                 goto do_query;
860
861         centry = wcache_fetch(cache, domain, "GL/%s/local", domain->name);
862         if (!centry)
863                 goto do_query;
864
865         *num_entries = centry_uint32(centry);
866         
867         if (*num_entries == 0)
868                 goto do_cached;
869
870         (*info) = TALLOC_ARRAY(mem_ctx, struct acct_info, *num_entries);
871         if (! (*info))
872                 smb_panic("enum_dom_groups out of memory");
873         for (i=0; i<(*num_entries); i++) {
874                 fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx));
875                 fstrcpy((*info)[i].acct_desc, centry_string(centry, mem_ctx));
876                 (*info)[i].rid = centry_uint32(centry);
877         }
878
879 do_cached:      
880
881         /* If we are returning cached data and the domain controller
882            is down then we don't know whether the data is up to date
883            or not.  Return NT_STATUS_MORE_PROCESSING_REQUIRED to
884            indicate this. */
885
886         if (wcache_server_down(domain)) {
887                 DEBUG(10, ("enum_local_groups: returning cached user list and server was down\n"));
888                 status = NT_STATUS_MORE_PROCESSING_REQUIRED;
889         } else
890                 status = centry->status;
891
892         DEBUG(10,("enum_local_groups: [Cached] - cached list for domain %s status %s\n",
893                 domain->name, get_friendly_nt_error_msg(status) ));
894
895         centry_free(centry);
896         return status;
897
898 do_query:
899         *num_entries = 0;
900         *info = NULL;
901
902         /* Return status value returned by seq number check */
903
904         if (!NT_STATUS_IS_OK(domain->last_status))
905                 return domain->last_status;
906
907         DEBUG(10,("enum_local_groups: [Cached] - doing backend query for list for domain %s\n",
908                 domain->name ));
909
910         status = domain->backend->enum_local_groups(domain, mem_ctx, num_entries, info);
911
912         /* and save it */
913         refresh_sequence_number(domain, False);
914         centry = centry_start(domain, status);
915         if (!centry)
916                 goto skip_save;
917         centry_put_uint32(centry, *num_entries);
918         for (i=0; i<(*num_entries); i++) {
919                 centry_put_string(centry, (*info)[i].acct_name);
920                 centry_put_string(centry, (*info)[i].acct_desc);
921                 centry_put_uint32(centry, (*info)[i].rid);
922         }
923         centry_end(centry, "GL/%s/local", domain->name);
924         centry_free(centry);
925
926 skip_save:
927         return status;
928 }
929
930 /* convert a single name to a sid in a domain */
931 static NTSTATUS name_to_sid(struct winbindd_domain *domain,
932                             TALLOC_CTX *mem_ctx,
933                             const char *domain_name,
934                             const char *name,
935                             DOM_SID *sid,
936                             enum SID_NAME_USE *type)
937 {
938         struct winbind_cache *cache = get_cache(domain);
939         struct cache_entry *centry = NULL;
940         NTSTATUS status;
941         fstring uname;
942
943         if (!cache->tdb)
944                 goto do_query;
945
946         fstrcpy(uname, name);
947         strupper_m(uname);
948         centry = wcache_fetch(cache, domain, "NS/%s/%s", domain_name, uname);
949         if (!centry)
950                 goto do_query;
951         *type = (enum SID_NAME_USE)centry_uint32(centry);
952         centry_sid(centry, sid);
953         status = centry->status;
954
955         DEBUG(10,("name_to_sid: [Cached] - cached name for domain %s status %s\n",
956                 domain->name, get_friendly_nt_error_msg(status) ));
957
958         centry_free(centry);
959         return status;
960
961 do_query:
962         ZERO_STRUCTP(sid);
963
964         /* If the seq number check indicated that there is a problem
965          * with this DC, then return that status... except for
966          * access_denied.  This is special because the dc may be in
967          * "restrict anonymous = 1" mode, in which case it will deny
968          * most unauthenticated operations, but *will* allow the LSA
969          * name-to-sid that we try as a fallback. */
970
971         if (!(NT_STATUS_IS_OK(domain->last_status)
972               || NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)))
973                 return domain->last_status;
974
975         DEBUG(10,("name_to_sid: [Cached] - doing backend query for name for domain %s\n",
976                 domain->name ));
977
978         status = domain->backend->name_to_sid(domain, mem_ctx, domain_name, name, sid, type);
979
980         /* and save it */
981         wcache_save_name_to_sid(domain, status, domain_name, name, sid, *type);
982
983         /* We can't save the sid to name mapping as we don't know the
984            correct case of the name without looking it up */
985
986         return status;
987 }
988
989 /* convert a sid to a user or group name. The sid is guaranteed to be in the domain
990    given */
991 static NTSTATUS sid_to_name(struct winbindd_domain *domain,
992                             TALLOC_CTX *mem_ctx,
993                             const DOM_SID *sid,
994                             char **domain_name,
995                             char **name,
996                             enum SID_NAME_USE *type)
997 {
998         struct winbind_cache *cache = get_cache(domain);
999         struct cache_entry *centry = NULL;
1000         NTSTATUS status;
1001         fstring sid_string;
1002
1003         if (!cache->tdb)
1004                 goto do_query;
1005
1006         centry = wcache_fetch(cache, domain, "SN/%s", sid_to_string(sid_string, sid));
1007         if (!centry)
1008                 goto do_query;
1009         if (NT_STATUS_IS_OK(centry->status)) {
1010                 *type = (enum SID_NAME_USE)centry_uint32(centry);
1011                 *domain_name = centry_string(centry, mem_ctx);
1012                 *name = centry_string(centry, mem_ctx);
1013         }
1014         status = centry->status;
1015
1016         DEBUG(10,("sid_to_name: [Cached] - cached name for domain %s status %s\n",
1017                 domain->name, get_friendly_nt_error_msg(status) ));
1018
1019         centry_free(centry);
1020         return status;
1021
1022 do_query:
1023         *name = NULL;
1024         *domain_name = NULL;
1025
1026         /* If the seq number check indicated that there is a problem
1027          * with this DC, then return that status... except for
1028          * access_denied.  This is special because the dc may be in
1029          * "restrict anonymous = 1" mode, in which case it will deny
1030          * most unauthenticated operations, but *will* allow the LSA
1031          * sid-to-name that we try as a fallback. */
1032
1033         if (!(NT_STATUS_IS_OK(domain->last_status)
1034               || NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)))
1035                 return domain->last_status;
1036
1037         DEBUG(10,("sid_to_name: [Cached] - doing backend query for name for domain %s\n",
1038                 domain->name ));
1039
1040         status = domain->backend->sid_to_name(domain, mem_ctx, sid, domain_name, name, type);
1041
1042         /* and save it */
1043         refresh_sequence_number(domain, False);
1044         wcache_save_sid_to_name(domain, status, sid, *domain_name, *name, *type);
1045
1046         /* We can't save the name to sid mapping here, as with sid history a
1047          * later name2sid would give the wrong sid. */
1048
1049         return status;
1050 }
1051
1052 /* Lookup user information from a rid */
1053 static NTSTATUS query_user(struct winbindd_domain *domain, 
1054                            TALLOC_CTX *mem_ctx, 
1055                            const DOM_SID *user_sid, 
1056                            WINBIND_USERINFO *info)
1057 {
1058         struct winbind_cache *cache = get_cache(domain);
1059         struct cache_entry *centry = NULL;
1060         NTSTATUS status;
1061
1062         if (!cache->tdb)
1063                 goto do_query;
1064
1065         centry = wcache_fetch(cache, domain, "U/%s", sid_string_static(user_sid));
1066         
1067         if (!centry)
1068                 goto do_query;
1069
1070         info->acct_name = centry_string(centry, mem_ctx);
1071         info->full_name = centry_string(centry, mem_ctx);
1072         info->homedir = centry_string(centry, mem_ctx);
1073         info->shell = centry_string(centry, mem_ctx);
1074         centry_sid(centry, &info->user_sid);
1075         centry_sid(centry, &info->group_sid);
1076         status = centry->status;
1077
1078         DEBUG(10,("query_user: [Cached] - cached info for domain %s status %s\n",
1079                 domain->name, get_friendly_nt_error_msg(status) ));
1080
1081         centry_free(centry);
1082         return status;
1083
1084 do_query:
1085         ZERO_STRUCTP(info);
1086
1087         /* Return status value returned by seq number check */
1088
1089         if (!NT_STATUS_IS_OK(domain->last_status))
1090                 return domain->last_status;
1091         
1092         DEBUG(10,("sid_to_name: [Cached] - doing backend query for info for domain %s\n",
1093                 domain->name ));
1094
1095         status = domain->backend->query_user(domain, mem_ctx, user_sid, info);
1096
1097         /* and save it */
1098         refresh_sequence_number(domain, False);
1099         wcache_save_user(domain, status, info);
1100
1101         return status;
1102 }
1103
1104
1105 /* Lookup groups a user is a member of. */
1106 static NTSTATUS lookup_usergroups(struct winbindd_domain *domain,
1107                                   TALLOC_CTX *mem_ctx,
1108                                   const DOM_SID *user_sid, 
1109                                   uint32 *num_groups, DOM_SID **user_gids)
1110 {
1111         struct winbind_cache *cache = get_cache(domain);
1112         struct cache_entry *centry = NULL;
1113         NTSTATUS status;
1114         unsigned int i;
1115         fstring sid_string;
1116
1117         if (!cache->tdb)
1118                 goto do_query;
1119
1120         centry = wcache_fetch(cache, domain, "UG/%s", sid_to_string(sid_string, user_sid));
1121         
1122         if (!centry)
1123                 goto do_query;
1124
1125         *num_groups = centry_uint32(centry);
1126         
1127         if (*num_groups == 0)
1128                 goto do_cached;
1129
1130         (*user_gids) = TALLOC_ARRAY(mem_ctx, DOM_SID, *num_groups);
1131         if (! (*user_gids))
1132                 smb_panic("lookup_usergroups out of memory");
1133         for (i=0; i<(*num_groups); i++) {
1134                 centry_sid(centry, &(*user_gids)[i]);
1135         }
1136
1137 do_cached:      
1138         status = centry->status;
1139
1140         DEBUG(10,("lookup_usergroups: [Cached] - cached info for domain %s status %s\n",
1141                 domain->name, get_friendly_nt_error_msg(status) ));
1142
1143         centry_free(centry);
1144         return status;
1145
1146 do_query:
1147         (*num_groups) = 0;
1148         (*user_gids) = NULL;
1149
1150         /* Return status value returned by seq number check */
1151
1152         if (!NT_STATUS_IS_OK(domain->last_status))
1153                 return domain->last_status;
1154
1155         DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info for domain %s\n",
1156                 domain->name ));
1157
1158         status = domain->backend->lookup_usergroups(domain, mem_ctx, user_sid, num_groups, user_gids);
1159
1160         /* and save it */
1161         refresh_sequence_number(domain, False);
1162         centry = centry_start(domain, status);
1163         if (!centry)
1164                 goto skip_save;
1165         centry_put_uint32(centry, *num_groups);
1166         for (i=0; i<(*num_groups); i++) {
1167                 centry_put_sid(centry, &(*user_gids)[i]);
1168         }       
1169         centry_end(centry, "UG/%s", sid_to_string(sid_string, user_sid));
1170         centry_free(centry);
1171
1172 skip_save:
1173         return status;
1174 }
1175
1176 static NTSTATUS lookup_useraliases(struct winbindd_domain *domain,
1177                                    TALLOC_CTX *mem_ctx,
1178                                    uint32 num_sids, const DOM_SID *sids,
1179                                    uint32 *num_aliases, uint32 **alias_rids)
1180 {
1181         struct winbind_cache *cache = get_cache(domain);
1182         struct cache_entry *centry = NULL;
1183         NTSTATUS status;
1184         char *sidlist = talloc_strdup(mem_ctx, "");
1185         int i;
1186
1187         if (!cache->tdb)
1188                 goto do_query;
1189
1190         if (num_sids == 0) {
1191                 *num_aliases = 0;
1192                 *alias_rids = NULL;
1193                 return NT_STATUS_OK;
1194         }
1195
1196         /* We need to cache indexed by the whole list of SIDs, the aliases
1197          * resulting might come from any of the SIDs. */
1198
1199         for (i=0; i<num_sids; i++) {
1200                 sidlist = talloc_asprintf(mem_ctx, "%s/%s", sidlist,
1201                                           sid_string_static(&sids[i]));
1202                 if (sidlist == NULL)
1203                         return NT_STATUS_NO_MEMORY;
1204         }
1205
1206         centry = wcache_fetch(cache, domain, "UA%s", sidlist);
1207
1208         if (!centry)
1209                 goto do_query;
1210
1211         *num_aliases = centry_uint32(centry);
1212         *alias_rids = NULL;
1213
1214         (*alias_rids) = TALLOC_ARRAY(mem_ctx, uint32, *num_aliases);
1215
1216         if ((*num_aliases != 0) && ((*alias_rids) == NULL))
1217                 return NT_STATUS_NO_MEMORY;
1218
1219         for (i=0; i<(*num_aliases); i++)
1220                 (*alias_rids)[i] = centry_uint32(centry);
1221
1222         status = centry->status;
1223
1224         DEBUG(10,("lookup_useraliases: [Cached] - cached info for domain %s "
1225                   "status %s\n", domain->name,
1226                   get_friendly_nt_error_msg(status)));
1227
1228         centry_free(centry);
1229         return status;
1230
1231  do_query:
1232         (*num_aliases) = 0;
1233         (*alias_rids) = NULL;
1234
1235         if (!NT_STATUS_IS_OK(domain->last_status))
1236                 return domain->last_status;
1237
1238         DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info "
1239                   "for domain %s\n", domain->name ));
1240
1241         status = domain->backend->lookup_useraliases(domain, mem_ctx,
1242                                                      num_sids, sids,
1243                                                      num_aliases, alias_rids);
1244
1245         /* and save it */
1246         refresh_sequence_number(domain, False);
1247         centry = centry_start(domain, status);
1248         if (!centry)
1249                 goto skip_save;
1250         centry_put_uint32(centry, *num_aliases);
1251         for (i=0; i<(*num_aliases); i++)
1252                 centry_put_uint32(centry, (*alias_rids)[i]);
1253         centry_end(centry, "UA%s", sidlist);
1254         centry_free(centry);
1255
1256  skip_save:
1257         return status;
1258 }
1259
1260
1261 static NTSTATUS lookup_groupmem(struct winbindd_domain *domain,
1262                                 TALLOC_CTX *mem_ctx,
1263                                 const DOM_SID *group_sid, uint32 *num_names, 
1264                                 DOM_SID **sid_mem, char ***names, 
1265                                 uint32 **name_types)
1266 {
1267         struct winbind_cache *cache = get_cache(domain);
1268         struct cache_entry *centry = NULL;
1269         NTSTATUS status;
1270         unsigned int i;
1271         fstring sid_string;
1272
1273         if (!cache->tdb)
1274                 goto do_query;
1275
1276         centry = wcache_fetch(cache, domain, "GM/%s", sid_to_string(sid_string, group_sid));
1277         if (!centry)
1278                 goto do_query;
1279
1280         *num_names = centry_uint32(centry);
1281         
1282         if (*num_names == 0)
1283                 goto do_cached;
1284
1285         (*sid_mem) = TALLOC_ARRAY(mem_ctx, DOM_SID, *num_names);
1286         (*names) = TALLOC_ARRAY(mem_ctx, char *, *num_names);
1287         (*name_types) = TALLOC_ARRAY(mem_ctx, uint32, *num_names);
1288
1289         if (! (*sid_mem) || ! (*names) || ! (*name_types)) {
1290                 smb_panic("lookup_groupmem out of memory");
1291         }
1292
1293         for (i=0; i<(*num_names); i++) {
1294                 centry_sid(centry, &(*sid_mem)[i]);
1295                 (*names)[i] = centry_string(centry, mem_ctx);
1296                 (*name_types)[i] = centry_uint32(centry);
1297         }
1298
1299 do_cached:      
1300         status = centry->status;
1301
1302         DEBUG(10,("lookup_groupmem: [Cached] - cached info for domain %s status %s\n",
1303                 domain->name, get_friendly_nt_error_msg(status) ));
1304
1305         centry_free(centry);
1306         return status;
1307
1308 do_query:
1309         (*num_names) = 0;
1310         (*sid_mem) = NULL;
1311         (*names) = NULL;
1312         (*name_types) = NULL;
1313         
1314         /* Return status value returned by seq number check */
1315
1316         if (!NT_STATUS_IS_OK(domain->last_status))
1317                 return domain->last_status;
1318
1319         DEBUG(10,("lookup_groupmem: [Cached] - doing backend query for info for domain %s\n",
1320                 domain->name ));
1321
1322         status = domain->backend->lookup_groupmem(domain, mem_ctx, group_sid, num_names, 
1323                                                   sid_mem, names, name_types);
1324
1325         /* and save it */
1326         refresh_sequence_number(domain, False);
1327         centry = centry_start(domain, status);
1328         if (!centry)
1329                 goto skip_save;
1330         centry_put_uint32(centry, *num_names);
1331         for (i=0; i<(*num_names); i++) {
1332                 centry_put_sid(centry, &(*sid_mem)[i]);
1333                 centry_put_string(centry, (*names)[i]);
1334                 centry_put_uint32(centry, (*name_types)[i]);
1335         }       
1336         centry_end(centry, "GM/%s", sid_to_string(sid_string, group_sid));
1337         centry_free(centry);
1338
1339 skip_save:
1340         return status;
1341 }
1342
1343 /* find the sequence number for a domain */
1344 static NTSTATUS sequence_number(struct winbindd_domain *domain, uint32 *seq)
1345 {
1346         refresh_sequence_number(domain, False);
1347
1348         *seq = domain->sequence_number;
1349
1350         return NT_STATUS_OK;
1351 }
1352
1353 /* enumerate trusted domains */
1354 static NTSTATUS trusted_domains(struct winbindd_domain *domain,
1355                                 TALLOC_CTX *mem_ctx,
1356                                 uint32 *num_domains,
1357                                 char ***names,
1358                                 char ***alt_names,
1359                                 DOM_SID **dom_sids)
1360 {
1361         get_cache(domain);
1362
1363         DEBUG(10,("trusted_domains: [Cached] - doing backend query for info for domain %s\n",
1364                 domain->name ));
1365
1366         /* we don't cache this call */
1367         return domain->backend->trusted_domains(domain, mem_ctx, num_domains, 
1368                                                names, alt_names, dom_sids);
1369 }
1370
1371 /* find the alternate names for the domain, if any */
1372 static NTSTATUS alternate_name(struct winbindd_domain *domain)
1373 {
1374         get_cache(domain);
1375
1376         DEBUG(10,("alternate_name: [Cached] - doing backend query for info for domain %s\n",
1377                 domain->name ));
1378
1379         /* we don't cache this call */
1380         return domain->backend->alternate_name(domain);
1381 }
1382
1383 /* Invalidate cached user and group lists coherently */
1384
1385 static int traverse_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf, 
1386                        void *state)
1387 {
1388         if (strncmp(kbuf.dptr, "UL/", 3) == 0 ||
1389             strncmp(kbuf.dptr, "GL/", 3) == 0)
1390                 tdb_delete(the_tdb, kbuf);
1391
1392         return 0;
1393 }
1394
1395 void wcache_invalidate_cache(void)
1396 {
1397         struct winbindd_domain *domain;
1398
1399         for (domain = domain_list(); domain; domain = domain->next) {
1400                 struct winbind_cache *cache = get_cache(domain);
1401
1402                 DEBUG(10, ("wcache_invalidate_cache: invalidating cache "
1403                            "entries for %s\n", domain->name));
1404                 if (cache)
1405                         tdb_traverse(cache->tdb, traverse_fn, NULL);
1406         }
1407 }
1408
1409 /* the ADS backend methods are exposed via this structure */
1410 struct winbindd_methods cache_methods = {
1411         True,
1412         query_user_list,
1413         enum_dom_groups,
1414         enum_local_groups,
1415         name_to_sid,
1416         sid_to_name,
1417         query_user,
1418         lookup_usergroups,
1419         lookup_useraliases,
1420         lookup_groupmem,
1421         sequence_number,
1422         trusted_domains,
1423         alternate_name
1424 };
1425
1426 static BOOL init_wcache(void)
1427 {
1428         if (wcache == NULL) {
1429                 wcache = SMB_XMALLOC_P(struct winbind_cache);
1430                 ZERO_STRUCTP(wcache);
1431         }
1432
1433         if (wcache->tdb != NULL)
1434                 return True;
1435
1436         wcache->tdb = tdb_open_log(lock_path("winbindd_cache.tdb"), 5000, 
1437                                    TDB_CLEAR_IF_FIRST, O_RDWR|O_CREAT, 0600);
1438
1439         if (wcache->tdb == NULL) {
1440                 DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
1441                 return False;
1442         }
1443
1444         return True;
1445 }
1446
1447 void cache_store_response(pid_t pid, struct winbindd_response *response)
1448 {
1449         fstring key_str;
1450
1451         if (!init_wcache())
1452                 return;
1453
1454         DEBUG(10, ("Storing response for pid %d, len %d\n",
1455                    pid, response->length));
1456
1457         fstr_sprintf(key_str, "DR/%d", pid);
1458         if (tdb_store(wcache->tdb, string_tdb_data(key_str), 
1459                       make_tdb_data((void *)response, sizeof(*response)),
1460                       TDB_REPLACE) == -1)
1461                 return;
1462
1463         if (response->length == sizeof(*response))
1464                 return;
1465
1466         /* There's extra data */
1467
1468         DEBUG(10, ("Storing extra data: len=%d\n",
1469                    (int)(response->length - sizeof(*response))));
1470
1471         fstr_sprintf(key_str, "DE/%d", pid);
1472         if (tdb_store(wcache->tdb, string_tdb_data(key_str),
1473                       make_tdb_data(response->extra_data,
1474                                     response->length - sizeof(*response)),
1475                       TDB_REPLACE) == 0)
1476                 return;
1477
1478         /* We could not store the extra data, make sure the tdb does not
1479          * contain a main record with wrong dangling extra data */
1480
1481         fstr_sprintf(key_str, "DR/%d", pid);
1482         tdb_delete(wcache->tdb, string_tdb_data(key_str));
1483
1484         return;
1485 }
1486
1487 BOOL cache_retrieve_response(pid_t pid, struct winbindd_response * response)
1488 {
1489         TDB_DATA data;
1490         fstring key_str;
1491
1492         if (!init_wcache())
1493                 return False;
1494
1495         DEBUG(10, ("Retrieving response for pid %d\n", pid));
1496
1497         fstr_sprintf(key_str, "DR/%d", pid);
1498         data = tdb_fetch(wcache->tdb, string_tdb_data(key_str));
1499
1500         if (data.dptr == NULL)
1501                 return False;
1502
1503         if (data.dsize != sizeof(*response))
1504                 return False;
1505
1506         memcpy(response, data.dptr, data.dsize);
1507         SAFE_FREE(data.dptr);
1508
1509         if (response->length == sizeof(*response)) {
1510                 response->extra_data = NULL;
1511                 return True;
1512         }
1513
1514         /* There's extra data */
1515
1516         DEBUG(10, ("Retrieving extra data length=%d\n",
1517                    (int)(response->length - sizeof(*response))));
1518
1519         fstr_sprintf(key_str, "DE/%d", pid);
1520         data = tdb_fetch(wcache->tdb, string_tdb_data(key_str));
1521
1522         if (data.dptr == NULL) {
1523                 DEBUG(0, ("Did not find extra data\n"));
1524                 return False;
1525         }
1526
1527         if (data.dsize != (response->length - sizeof(*response))) {
1528                 DEBUG(0, ("Invalid extra data length: %d\n", (int)data.dsize));
1529                 SAFE_FREE(data.dptr);
1530                 return False;
1531         }
1532
1533         dump_data(11, data.dptr, data.dsize);
1534
1535         response->extra_data = data.dptr;
1536         return True;
1537 }
1538
1539 BOOL lookup_cached_sid(TALLOC_CTX *mem_ctx, const DOM_SID *sid,
1540                        const char **domain_name, const char **name,
1541                        enum SID_NAME_USE *type)
1542 {
1543         struct winbindd_domain *domain;
1544         struct winbind_cache *cache;
1545         struct cache_entry *centry = NULL;
1546         NTSTATUS status;
1547
1548         domain = find_lookup_domain_from_sid(sid);
1549         if (domain == NULL) {
1550                 return False;
1551         }
1552
1553         cache = get_cache(domain);
1554
1555         if (cache->tdb == NULL) {
1556                 return False;
1557         }
1558
1559         centry = wcache_fetch(cache, domain, "SN/%s", sid_string_static(sid));
1560         if (centry == NULL) {
1561                 return False;
1562         }
1563
1564         if (NT_STATUS_IS_OK(centry->status)) {
1565                 *type = (enum SID_NAME_USE)centry_uint32(centry);
1566                 *domain_name = centry_string(centry, mem_ctx);
1567                 *name = centry_string(centry, mem_ctx);
1568         }
1569
1570         status = centry->status;
1571         centry_free(centry);
1572         return NT_STATUS_IS_OK(status);
1573 }
1574
1575 void cache_sid2name(struct winbindd_domain *domain, const DOM_SID *sid,
1576                     const char *domain_name, const char *name,
1577                     enum SID_NAME_USE type)
1578 {
1579         wcache_save_sid_to_name(domain, NT_STATUS_OK, sid, domain_name,
1580                                 name, type);
1581 }