Add in rety loop for query_user_list (from APP_HEAD). Deals with a bug
[kai/samba.git] / source3 / nsswitch / winbindd_cache.c
1 /* 
2    Unix SMB/CIFS implementation.
3
4    Winbind cache backend functions
5
6    Copyright (C) Andrew Tridgell 2001
7    
8    This program is free software; you can redistribute it and/or modify
9    it under the terms of the GNU General Public License as published by
10    the Free Software Foundation; either version 2 of the License, or
11    (at your option) any later version.
12    
13    This program is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16    GNU General Public License for more details.
17    
18    You should have received a copy of the GNU General Public License
19    along with this program; if not, write to the Free Software
20    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 */
22
23 #include "winbindd.h"
24
25 #undef DBGC_CLASS
26 #define DBGC_CLASS DBGC_WINBIND
27
28 struct winbind_cache {
29         TDB_CONTEXT *tdb;
30 };
31
32 struct cache_entry {
33         NTSTATUS status;
34         uint32 sequence_number;
35         uint8 *data;
36         uint32 len, ofs;
37 };
38
39 #define WINBINDD_MAX_CACHE_SIZE (50*1024*1024)
40
41 static struct winbind_cache *wcache;
42
43 /* flush the cache */
44 void wcache_flush_cache(void)
45 {
46         extern BOOL opt_nocache;
47
48         if (!wcache)
49                 return;
50         if (wcache->tdb) {
51                 tdb_close(wcache->tdb);
52                 wcache->tdb = NULL;
53         }
54         if (opt_nocache)
55                 return;
56
57         wcache->tdb = tdb_open_log(lock_path("winbindd_cache.tdb"), 5000, 
58                                    TDB_CLEAR_IF_FIRST, O_RDWR|O_CREAT, 0600);
59
60         if (!wcache->tdb) {
61                 DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
62         }
63         DEBUG(10,("wcache_flush_cache success\n"));
64 }
65
66 void winbindd_check_cache_size(time_t t)
67 {
68         static time_t last_check_time;
69         struct stat st;
70
71         if (last_check_time == (time_t)0)
72                 last_check_time = t;
73
74         if (t - last_check_time < 60 && t - last_check_time > 0)
75                 return;
76
77         if (wcache == NULL || wcache->tdb == NULL) {
78                 DEBUG(0, ("Unable to check size of tdb cache - cache not open !\n"));
79                 return;
80         }
81
82         if (fstat(wcache->tdb->fd, &st) == -1) {
83                 DEBUG(0, ("Unable to check size of tdb cache %s!\n", strerror(errno) ));
84                 return;
85         }
86
87         if (st.st_size > WINBINDD_MAX_CACHE_SIZE) {
88                 DEBUG(10,("flushing cache due to size (%lu) > (%lu)\n",
89                         (unsigned long)st.st_size,
90                         (unsigned long)WINBINDD_MAX_CACHE_SIZE));
91                 wcache_flush_cache();
92         }
93 }
94
95 /* get the winbind_cache structure */
96 static struct winbind_cache *get_cache(struct winbindd_domain *domain)
97 {
98         struct winbind_cache *ret = wcache;
99
100         if (!domain->backend) {
101                 extern struct winbindd_methods msrpc_methods;
102                 switch (lp_security()) {
103 #ifdef HAVE_ADS
104                 case SEC_ADS: {
105                         extern struct winbindd_methods ads_methods;
106                         domain->backend = &ads_methods;
107                         break;
108                 }
109 #endif
110                 default:
111                         domain->backend = &msrpc_methods;
112                 }
113         }
114
115         if (ret)
116                 return ret;
117         
118         ret = smb_xmalloc(sizeof(*ret));
119         ZERO_STRUCTP(ret);
120
121         wcache = ret;
122         wcache_flush_cache();
123
124         return ret;
125 }
126
127 /*
128   free a centry structure
129 */
130 static void centry_free(struct cache_entry *centry)
131 {
132         if (!centry)
133                 return;
134         SAFE_FREE(centry->data);
135         free(centry);
136 }
137
138 /*
139   pull a uint32 from a cache entry 
140 */
141 static uint32 centry_uint32(struct cache_entry *centry)
142 {
143         uint32 ret;
144         if (centry->len - centry->ofs < 4) {
145                 DEBUG(0,("centry corruption? needed 4 bytes, have %d\n", 
146                          centry->len - centry->ofs));
147                 smb_panic("centry_uint32");
148         }
149         ret = IVAL(centry->data, centry->ofs);
150         centry->ofs += 4;
151         return ret;
152 }
153
154 /*
155   pull a uint8 from a cache entry 
156 */
157 static uint8 centry_uint8(struct cache_entry *centry)
158 {
159         uint8 ret;
160         if (centry->len - centry->ofs < 1) {
161                 DEBUG(0,("centry corruption? needed 1 bytes, have %d\n", 
162                          centry->len - centry->ofs));
163                 smb_panic("centry_uint32");
164         }
165         ret = CVAL(centry->data, centry->ofs);
166         centry->ofs += 1;
167         return ret;
168 }
169
170 /* pull a string from a cache entry, using the supplied
171    talloc context 
172 */
173 static char *centry_string(struct cache_entry *centry, TALLOC_CTX *mem_ctx)
174 {
175         uint32 len;
176         char *ret;
177
178         len = centry_uint8(centry);
179
180         if (len == 0xFF) {
181                 /* a deliberate NULL string */
182                 return NULL;
183         }
184
185         if (centry->len - centry->ofs < len) {
186                 DEBUG(0,("centry corruption? needed %d bytes, have %d\n", 
187                          len, centry->len - centry->ofs));
188                 smb_panic("centry_string");
189         }
190
191         ret = talloc(mem_ctx, len+1);
192         if (!ret) {
193                 smb_panic("centry_string out of memory\n");
194         }
195         memcpy(ret,centry->data + centry->ofs, len);
196         ret[len] = 0;
197         centry->ofs += len;
198         return ret;
199 }
200
201 /* pull a string from a cache entry, using the supplied
202    talloc context 
203 */
204 static DOM_SID *centry_sid(struct cache_entry *centry, TALLOC_CTX *mem_ctx)
205 {
206         DOM_SID *sid;
207         char *sid_string;
208
209         sid = talloc(mem_ctx, sizeof(*sid));
210         if (!sid)
211                 return NULL;
212         
213         sid_string = centry_string(centry, mem_ctx);
214         if (!string_to_sid(sid, sid_string)) {
215                 return NULL;
216         }
217         return sid;
218 }
219
220 /* the server is considered down if it can't give us a sequence number */
221 static BOOL wcache_server_down(struct winbindd_domain *domain)
222 {
223         BOOL ret;
224
225         if (!wcache->tdb)
226                 return False;
227
228         ret = (domain->sequence_number == DOM_SEQUENCE_NONE);
229
230         if (ret)
231                 DEBUG(10,("wcache_server_down: server for Domain %s down\n", 
232                         domain->name ));
233         return ret;
234 }
235
236 static NTSTATUS fetch_cache_seqnum( struct winbindd_domain *domain, time_t now )
237 {
238         TDB_DATA data;
239         fstring key;
240         uint32 time_diff;
241         
242         if (!wcache->tdb) {
243                 DEBUG(10,("fetch_cache_seqnum: tdb == NULL\n"));
244                 return NT_STATUS_UNSUCCESSFUL;
245         }
246                 
247         snprintf( key, sizeof(key), "SEQNUM/%s", domain->name );
248         
249         data = tdb_fetch_by_string( wcache->tdb, key );
250         if ( !data.dptr || data.dsize!=8 ) {
251                 DEBUG(10,("fetch_cache_seqnum: invalid data size key [%s]\n", key ));
252                 return NT_STATUS_UNSUCCESSFUL;
253         }
254         
255         domain->sequence_number = IVAL(data.dptr, 0);
256         domain->last_seq_check  = IVAL(data.dptr, 4);
257         
258         /* have we expired? */
259         
260         time_diff = now - domain->last_seq_check;
261         if ( time_diff > lp_winbind_cache_time() ) {
262                 DEBUG(10,("fetch_cache_seqnum: timeout [%s][%u @ %u]\n",
263                         domain->name, domain->sequence_number,
264                         (uint32)domain->last_seq_check));
265                 return NT_STATUS_UNSUCCESSFUL;
266         }
267
268         DEBUG(10,("fetch_cache_seqnum: success [%s][%u @ %u]\n", 
269                 domain->name, domain->sequence_number, 
270                 (uint32)domain->last_seq_check));
271
272         return NT_STATUS_OK;
273 }
274
275 static NTSTATUS store_cache_seqnum( struct winbindd_domain *domain )
276 {
277         TDB_DATA data, key;
278         fstring key_str;
279         char buf[8];
280         
281         if (!wcache->tdb) {
282                 DEBUG(10,("store_cache_seqnum: tdb == NULL\n"));
283                 return NT_STATUS_UNSUCCESSFUL;
284         }
285                 
286         snprintf( key_str, sizeof(key_str), "SEQNUM/%s", domain->name );
287         key.dptr = key_str;
288         key.dsize = strlen(key_str)+1;
289         
290         SIVAL(buf, 0, domain->sequence_number);
291         SIVAL(buf, 4, domain->last_seq_check);
292         data.dptr = buf;
293         data.dsize = 8;
294         
295         if ( tdb_store( wcache->tdb, key, data, TDB_REPLACE) == -1 ) {
296                 DEBUG(10,("store_cache_seqnum: tdb_store fail key [%s]\n", key_str ));
297                 return NT_STATUS_UNSUCCESSFUL;
298         }
299
300         DEBUG(10,("store_cache_seqnum: success [%s][%u @ %u]\n", 
301                 domain->name, domain->sequence_number, 
302                 (uint32)domain->last_seq_check));
303         
304         return NT_STATUS_OK;
305 }
306
307 /*
308   refresh the domain sequence number. If force is True
309   then always refresh it, no matter how recently we fetched it
310 */
311
312 static void refresh_sequence_number(struct winbindd_domain *domain, BOOL force)
313 {
314         NTSTATUS status;
315         unsigned time_diff;
316         time_t t = time(NULL);
317         unsigned cache_time = lp_winbind_cache_time();
318
319         /* trying to reconnect is expensive, don't do it too often */
320         if (domain->sequence_number == DOM_SEQUENCE_NONE) {
321                 cache_time *= 8;
322         }
323
324         time_diff = t - domain->last_seq_check;
325
326         /* see if we have to refetch the domain sequence number */
327         if (!force && (time_diff < cache_time)) {
328                 DEBUG(10, ("refresh_sequence_number: %s time ok\n", domain->name));
329                 return;
330         }
331         
332         /* try to get the sequence number from the tdb cache first */
333         /* this will update the timestamp as well */
334         
335         status = fetch_cache_seqnum( domain, t );
336         if ( NT_STATUS_IS_OK(status) )
337                 goto done;      
338
339         status = domain->backend->sequence_number(domain, &domain->sequence_number);
340
341         if (!NT_STATUS_IS_OK(status)) {
342                 domain->sequence_number = DOM_SEQUENCE_NONE;
343         }
344         
345         domain->last_seq_check = time(NULL);
346         
347         /* save the new sequence number ni the cache */
348         store_cache_seqnum( domain );
349
350 done:
351         DEBUG(10, ("refresh_sequence_number: %s seq number is now %d\n", 
352                    domain->name, domain->sequence_number));
353
354         return;
355 }
356
357 /*
358   decide if a cache entry has expired
359 */
360 static BOOL centry_expired(struct winbindd_domain *domain, const char *keystr, struct cache_entry *centry)
361 {
362         /* if the server is OK and our cache entry came from when it was down then
363            the entry is invalid */
364         if (domain->sequence_number != DOM_SEQUENCE_NONE && 
365             centry->sequence_number == DOM_SEQUENCE_NONE) {
366                 DEBUG(10,("centry_expired: Key %s for domain %s invalid sequence.\n",
367                         keystr, domain->name ));
368                 return True;
369         }
370
371         /* if the server is down or the cache entry is not older than the
372            current sequence number then it is OK */
373         if (wcache_server_down(domain) || 
374             centry->sequence_number == domain->sequence_number) {
375                 DEBUG(10,("centry_expired: Key %s for domain %s is good.\n",
376                         keystr, domain->name ));
377                 return False;
378         }
379
380         DEBUG(10,("centry_expired: Key %s for domain %s expired\n",
381                 keystr, domain->name ));
382
383         /* it's expired */
384         return True;
385 }
386
387 /*
388   fetch an entry from the cache, with a varargs key. auto-fetch the sequence
389   number and return status
390 */
391 static struct cache_entry *wcache_fetch(struct winbind_cache *cache, 
392                                         struct winbindd_domain *domain,
393                                         const char *format, ...) PRINTF_ATTRIBUTE(3,4);
394 static struct cache_entry *wcache_fetch(struct winbind_cache *cache, 
395                                         struct winbindd_domain *domain,
396                                         const char *format, ...)
397 {
398         va_list ap;
399         char *kstr;
400         TDB_DATA data;
401         struct cache_entry *centry;
402         TDB_DATA key;
403
404         refresh_sequence_number(domain, False);
405
406         va_start(ap, format);
407         smb_xvasprintf(&kstr, format, ap);
408         va_end(ap);
409         
410         key.dptr = kstr;
411         key.dsize = strlen(kstr);
412         data = tdb_fetch(wcache->tdb, key);
413         if (!data.dptr) {
414                 /* a cache miss */
415                 free(kstr);
416                 return NULL;
417         }
418
419         centry = smb_xmalloc(sizeof(*centry));
420         centry->data = data.dptr;
421         centry->len = data.dsize;
422         centry->ofs = 0;
423
424         if (centry->len < 8) {
425                 /* huh? corrupt cache? */
426                 DEBUG(10,("wcache_fetch: Corrupt cache for key %s domain %s (len < 8) ?\n",
427                         kstr, domain->name ));
428                 centry_free(centry);
429                 free(kstr);
430                 return NULL;
431         }
432         
433         centry->status = NT_STATUS(centry_uint32(centry));
434         centry->sequence_number = centry_uint32(centry);
435
436         if (centry_expired(domain, kstr, centry)) {
437                 extern BOOL opt_dual_daemon;
438
439                 DEBUG(10,("wcache_fetch: entry %s expired for domain %s\n",
440                          kstr, domain->name ));
441
442                 if (opt_dual_daemon) {
443                         extern BOOL background_process;
444                         background_process = True;
445                         DEBUG(10,("wcache_fetch: background processing expired entry %s for domain %s\n",
446                                  kstr, domain->name ));
447                 } else {
448                         centry_free(centry);
449                         free(kstr);
450                         return NULL;
451                 }
452         }
453
454         DEBUG(10,("wcache_fetch: returning entry %s for domain %s\n",
455                  kstr, domain->name ));
456
457         free(kstr);
458         return centry;
459 }
460
461 /*
462   make sure we have at least len bytes available in a centry 
463 */
464 static void centry_expand(struct cache_entry *centry, uint32 len)
465 {
466         uint8 *p;
467         if (centry->len - centry->ofs >= len)
468                 return;
469         centry->len *= 2;
470         p = realloc(centry->data, centry->len);
471         if (!p) {
472                 DEBUG(0,("out of memory: needed %d bytes in centry_expand\n", centry->len));
473                 smb_panic("out of memory in centry_expand");
474         }
475         centry->data = p;
476 }
477
478 /*
479   push a uint32 into a centry 
480 */
481 static void centry_put_uint32(struct cache_entry *centry, uint32 v)
482 {
483         centry_expand(centry, 4);
484         SIVAL(centry->data, centry->ofs, v);
485         centry->ofs += 4;
486 }
487
488 /*
489   push a uint8 into a centry 
490 */
491 static void centry_put_uint8(struct cache_entry *centry, uint8 v)
492 {
493         centry_expand(centry, 1);
494         SCVAL(centry->data, centry->ofs, v);
495         centry->ofs += 1;
496 }
497
498 /* 
499    push a string into a centry 
500  */
501 static void centry_put_string(struct cache_entry *centry, const char *s)
502 {
503         int len;
504
505         if (!s) {
506                 /* null strings are marked as len 0xFFFF */
507                 centry_put_uint8(centry, 0xFF);
508                 return;
509         }
510
511         len = strlen(s);
512         /* can't handle more than 254 char strings. Truncating is probably best */
513         if (len > 254)
514                 len = 254;
515         centry_put_uint8(centry, len);
516         centry_expand(centry, len);
517         memcpy(centry->data + centry->ofs, s, len);
518         centry->ofs += len;
519 }
520
521 static void centry_put_sid(struct cache_entry *centry, const DOM_SID *sid) 
522 {
523         fstring sid_string;
524         centry_put_string(centry, sid_to_string(sid_string, sid));
525 }
526
527 /*
528   start a centry for output. When finished, call centry_end()
529 */
530 struct cache_entry *centry_start(struct winbindd_domain *domain, NTSTATUS status)
531 {
532         struct cache_entry *centry;
533
534         if (!wcache->tdb)
535                 return NULL;
536
537         centry = smb_xmalloc(sizeof(*centry));
538
539         centry->len = 8192; /* reasonable default */
540         centry->data = smb_xmalloc(centry->len);
541         centry->ofs = 0;
542         centry->sequence_number = domain->sequence_number;
543         centry_put_uint32(centry, NT_STATUS_V(status));
544         centry_put_uint32(centry, centry->sequence_number);
545         return centry;
546 }
547
548 /*
549   finish a centry and write it to the tdb
550 */
551 static void centry_end(struct cache_entry *centry, const char *format, ...) PRINTF_ATTRIBUTE(2,3);
552 static void centry_end(struct cache_entry *centry, const char *format, ...)
553 {
554         va_list ap;
555         char *kstr;
556         TDB_DATA key, data;
557
558         va_start(ap, format);
559         smb_xvasprintf(&kstr, format, ap);
560         va_end(ap);
561
562         key.dptr = kstr;
563         key.dsize = strlen(kstr);
564         data.dptr = centry->data;
565         data.dsize = centry->ofs;
566
567         tdb_store(wcache->tdb, key, data, TDB_REPLACE);
568         free(kstr);
569 }
570
571 static void wcache_save_name_to_sid(struct winbindd_domain *domain, 
572                                     NTSTATUS status, 
573                                     const char *name, DOM_SID *sid, 
574                                     enum SID_NAME_USE type)
575 {
576         struct cache_entry *centry;
577         fstring uname;
578         fstring sid_string;
579
580         centry = centry_start(domain, status);
581         if (!centry)
582                 return;
583         centry_put_sid(centry, sid);
584         fstrcpy(uname, name);
585         strupper(uname);
586         centry_end(centry, "NS/%s", sid_to_string(sid_string, sid));
587         DEBUG(10,("wcache_save_name_to_sid: %s -> %s\n", uname, sid_string));
588         centry_free(centry);
589 }
590
591 static void wcache_save_sid_to_name(struct winbindd_domain *domain, NTSTATUS status, 
592                                     DOM_SID *sid, const char *name, enum SID_NAME_USE type)
593 {
594         struct cache_entry *centry;
595         fstring sid_string;
596
597         centry = centry_start(domain, status);
598         if (!centry)
599                 return;
600         if (NT_STATUS_IS_OK(status)) {
601                 centry_put_uint32(centry, type);
602                 centry_put_string(centry, name);
603         }
604         centry_end(centry, "SN/%s", sid_to_string(sid_string, sid));
605         DEBUG(10,("wcache_save_sid_to_name: %s -> %s\n", sid_string, name));
606         centry_free(centry);
607 }
608
609
610 static void wcache_save_user(struct winbindd_domain *domain, NTSTATUS status, WINBIND_USERINFO *info)
611 {
612         struct cache_entry *centry;
613         fstring sid_string;
614
615         centry = centry_start(domain, status);
616         if (!centry)
617                 return;
618         centry_put_string(centry, info->acct_name);
619         centry_put_string(centry, info->full_name);
620         centry_put_sid(centry, info->user_sid);
621         centry_put_sid(centry, info->group_sid);
622         centry_end(centry, "U/%s", sid_to_string(sid_string, info->user_sid));
623         DEBUG(10,("wcache_save_user: %s (acct_name %s)\n", sid_string, info->acct_name));
624         centry_free(centry);
625 }
626
627
628 /* Query display info. This is the basic user list fn */
629 static NTSTATUS query_user_list(struct winbindd_domain *domain,
630                                 TALLOC_CTX *mem_ctx,
631                                 uint32 *num_entries, 
632                                 WINBIND_USERINFO **info)
633 {
634         struct winbind_cache *cache = get_cache(domain);
635         struct cache_entry *centry = NULL;
636         NTSTATUS status;
637         unsigned int i, retry;
638
639         if (!cache->tdb)
640                 goto do_query;
641
642         centry = wcache_fetch(cache, domain, "UL/%s", domain->name);
643         if (!centry)
644                 goto do_query;
645
646         *num_entries = centry_uint32(centry);
647         
648         if (*num_entries == 0)
649                 goto do_cached;
650
651         (*info) = talloc(mem_ctx, sizeof(**info) * (*num_entries));
652         if (! (*info))
653                 smb_panic("query_user_list out of memory");
654         for (i=0; i<(*num_entries); i++) {
655                 (*info)[i].acct_name = centry_string(centry, mem_ctx);
656                 (*info)[i].full_name = centry_string(centry, mem_ctx);
657                 (*info)[i].user_sid = centry_sid(centry, mem_ctx);
658                 (*info)[i].group_sid = centry_sid(centry, mem_ctx);
659         }
660
661 do_cached:      
662         status = centry->status;
663
664         DEBUG(10,("query_user_list: [Cached] - cached list for domain %s status %s\n",
665                 domain->name, get_friendly_nt_error_msg(status) ));
666
667         centry_free(centry);
668         return status;
669
670 do_query:
671         *num_entries = 0;
672         *info = NULL;
673
674         if (wcache_server_down(domain)) {
675                 return NT_STATUS_SERVER_DISABLED;
676         }
677
678         /* Put the query_user_list() in a retry loop.  There appears to be
679          * some bug either with Windows 2000 or Samba's handling of large
680          * rpc replies.  This manifests itself as sudden disconnection
681          * at a random point in the enumeration of a large (60k) user list.
682          * The retry loop simply tries the operation again. )-:  It's not
683          * pretty but an acceptable workaround until we work out what the
684          * real problem is. */
685
686         retry = 0;
687         do {
688
689                 DEBUG(10,("query_user_list: [Cached] - doing backend query for list for domain %s\n",
690                         domain->name ));
691
692                 status = domain->backend->query_user_list(domain, mem_ctx, num_entries, info);
693                 if (!NT_STATUS_IS_OK(status))
694                         DEBUG(3, ("query_user_list: returned 0x%08x, retrying\n", NT_STATUS_V(status)));
695                         if (NT_STATUS_V(status) == NT_STATUS_V(NT_STATUS_UNSUCCESSFUL)) {
696                                 DEBUG(3, ("query_user_list: flushing connection cache\n"));
697                                 winbindd_cm_flush();
698                         }
699
700         } while (NT_STATUS_V(status) == NT_STATUS_V(NT_STATUS_UNSUCCESSFUL) && (retry++ < 5));
701
702         /* and save it */
703         refresh_sequence_number(domain, False);
704         centry = centry_start(domain, status);
705         if (!centry)
706                 goto skip_save;
707         centry_put_uint32(centry, *num_entries);
708         for (i=0; i<(*num_entries); i++) {
709                 centry_put_string(centry, (*info)[i].acct_name);
710                 centry_put_string(centry, (*info)[i].full_name);
711                 centry_put_sid(centry, (*info)[i].user_sid);
712                 centry_put_sid(centry, (*info)[i].group_sid);
713                 if (domain->backend->consistent) {
714                         /* when the backend is consistent we can pre-prime some mappings */
715                         wcache_save_name_to_sid(domain, NT_STATUS_OK, 
716                                                 (*info)[i].acct_name, 
717                                                 (*info)[i].user_sid,
718                                                 SID_NAME_USER);
719                         wcache_save_sid_to_name(domain, NT_STATUS_OK, 
720                                                 (*info)[i].user_sid,
721                                                 (*info)[i].acct_name, 
722                                                 SID_NAME_USER);
723                         wcache_save_user(domain, NT_STATUS_OK, &(*info)[i]);
724                 }
725         }       
726         centry_end(centry, "UL/%s", domain->name);
727         centry_free(centry);
728
729 skip_save:
730         return status;
731 }
732
733 /* list all domain groups */
734 static NTSTATUS enum_dom_groups(struct winbindd_domain *domain,
735                                 TALLOC_CTX *mem_ctx,
736                                 uint32 *num_entries, 
737                                 struct acct_info **info)
738 {
739         struct winbind_cache *cache = get_cache(domain);
740         struct cache_entry *centry = NULL;
741         NTSTATUS status;
742         unsigned int i;
743
744         if (!cache->tdb)
745                 goto do_query;
746
747         centry = wcache_fetch(cache, domain, "GL/%s/domain", domain->name);
748         if (!centry)
749                 goto do_query;
750
751         *num_entries = centry_uint32(centry);
752         
753         if (*num_entries == 0)
754                 goto do_cached;
755
756         (*info) = talloc(mem_ctx, sizeof(**info) * (*num_entries));
757         if (! (*info))
758                 smb_panic("enum_dom_groups out of memory");
759         for (i=0; i<(*num_entries); i++) {
760                 fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx));
761                 fstrcpy((*info)[i].acct_desc, centry_string(centry, mem_ctx));
762                 (*info)[i].rid = centry_uint32(centry);
763         }
764
765 do_cached:      
766         status = centry->status;
767
768         DEBUG(10,("enum_dom_groups: [Cached] - cached list for domain %s status %s\n",
769                 domain->name, get_friendly_nt_error_msg(status) ));
770
771         centry_free(centry);
772         return status;
773
774 do_query:
775         *num_entries = 0;
776         *info = NULL;
777
778         if (wcache_server_down(domain)) {
779                 return NT_STATUS_SERVER_DISABLED;
780         }
781
782         DEBUG(10,("enum_dom_groups: [Cached] - doing backend query for list for domain %s\n",
783                 domain->name ));
784
785         status = domain->backend->enum_dom_groups(domain, mem_ctx, num_entries, info);
786
787         /* and save it */
788         refresh_sequence_number(domain, False);
789         centry = centry_start(domain, status);
790         if (!centry)
791                 goto skip_save;
792         centry_put_uint32(centry, *num_entries);
793         for (i=0; i<(*num_entries); i++) {
794                 centry_put_string(centry, (*info)[i].acct_name);
795                 centry_put_string(centry, (*info)[i].acct_desc);
796                 centry_put_uint32(centry, (*info)[i].rid);
797         }       
798         centry_end(centry, "GL/%s/domain", domain->name);
799         centry_free(centry);
800
801 skip_save:
802         return status;
803 }
804
805 /* list all domain groups */
806 static NTSTATUS enum_local_groups(struct winbindd_domain *domain,
807                                 TALLOC_CTX *mem_ctx,
808                                 uint32 *num_entries, 
809                                 struct acct_info **info)
810 {
811         struct winbind_cache *cache = get_cache(domain);
812         struct cache_entry *centry = NULL;
813         NTSTATUS status;
814         unsigned int i;
815
816         if (!cache->tdb)
817                 goto do_query;
818
819         centry = wcache_fetch(cache, domain, "GL/%s/local", domain->name);
820         if (!centry)
821                 goto do_query;
822
823         *num_entries = centry_uint32(centry);
824         
825         if (*num_entries == 0)
826                 goto do_cached;
827
828         (*info) = talloc(mem_ctx, sizeof(**info) * (*num_entries));
829         if (! (*info))
830                 smb_panic("enum_dom_groups out of memory");
831         for (i=0; i<(*num_entries); i++) {
832                 fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx));
833                 fstrcpy((*info)[i].acct_desc, centry_string(centry, mem_ctx));
834                 (*info)[i].rid = centry_uint32(centry);
835         }
836
837 do_cached:      
838
839         /* If we are returning cached data and the domain controller
840            is down then we don't know whether the data is up to date
841            or not.  Return NT_STATUS_MORE_PROCESSING_REQUIRED to
842            indicate this. */
843
844         if (wcache_server_down(domain)) {
845                 DEBUG(10, ("enum_local_groups: returning cached user list and server was down\n"));
846                 status = NT_STATUS_MORE_PROCESSING_REQUIRED;
847         } else
848                 status = centry->status;
849
850         DEBUG(10,("enum_local_groups: [Cached] - cached list for domain %s status %s\n",
851                 domain->name, get_friendly_nt_error_msg(status) ));
852
853         centry_free(centry);
854         return status;
855
856 do_query:
857         *num_entries = 0;
858         *info = NULL;
859
860         if (wcache_server_down(domain)) {
861                 return NT_STATUS_SERVER_DISABLED;
862         }
863
864         DEBUG(10,("enum_local_groups: [Cached] - doing backend query for list for domain %s\n",
865                 domain->name ));
866
867         status = domain->backend->enum_local_groups(domain, mem_ctx, num_entries, info);
868
869         /* and save it */
870         refresh_sequence_number(domain, False);
871         centry = centry_start(domain, status);
872         if (!centry)
873                 goto skip_save;
874         centry_put_uint32(centry, *num_entries);
875         for (i=0; i<(*num_entries); i++) {
876                 centry_put_string(centry, (*info)[i].acct_name);
877                 centry_put_string(centry, (*info)[i].acct_desc);
878                 centry_put_uint32(centry, (*info)[i].rid);
879         }
880         centry_end(centry, "GL/%s/local", domain->name);
881         centry_free(centry);
882
883 skip_save:
884         return status;
885 }
886
887 /* convert a single name to a sid in a domain */
888 static NTSTATUS name_to_sid(struct winbindd_domain *domain,
889                             TALLOC_CTX *mem_ctx,
890                             const char *name,
891                             DOM_SID *sid,
892                             enum SID_NAME_USE *type)
893 {
894         struct winbind_cache *cache = get_cache(domain);
895         struct cache_entry *centry = NULL;
896         NTSTATUS status;
897         fstring uname;
898         DOM_SID *sid2;
899
900         if (!cache->tdb)
901                 goto do_query;
902
903         fstrcpy(uname, name);
904         strupper(uname);
905         centry = wcache_fetch(cache, domain, "NS/%s/%s", domain->name, uname);
906         if (!centry)
907                 goto do_query;
908         *type = centry_uint32(centry);
909         sid2 = centry_sid(centry, mem_ctx);
910         if (!sid2) {
911                 ZERO_STRUCTP(sid);
912         } else {
913                 sid_copy(sid, sid2);
914         }
915
916         status = centry->status;
917
918         DEBUG(10,("name_to_sid: [Cached] - cached name for domain %s status %s\n",
919                 domain->name, get_friendly_nt_error_msg(status) ));
920
921         centry_free(centry);
922         return status;
923
924 do_query:
925         ZERO_STRUCTP(sid);
926
927         if (wcache_server_down(domain)) {
928                 return NT_STATUS_SERVER_DISABLED;
929         }
930
931         DEBUG(10,("name_to_sid: [Cached] - doing backend query for name for domain %s\n",
932                 domain->name ));
933
934         status = domain->backend->name_to_sid(domain, mem_ctx, name, sid, type);
935
936         /* and save it */
937         wcache_save_name_to_sid(domain, status, name, sid, *type);
938
939         /* We can't save the sid to name mapping as we don't know the
940            correct case of the name without looking it up */
941
942         return status;
943 }
944
945 /* convert a sid to a user or group name. The sid is guaranteed to be in the domain
946    given */
947 static NTSTATUS sid_to_name(struct winbindd_domain *domain,
948                             TALLOC_CTX *mem_ctx,
949                             DOM_SID *sid,
950                             char **name,
951                             enum SID_NAME_USE *type)
952 {
953         struct winbind_cache *cache = get_cache(domain);
954         struct cache_entry *centry = NULL;
955         NTSTATUS status;
956         fstring sid_string;
957
958         if (!cache->tdb)
959                 goto do_query;
960
961         centry = wcache_fetch(cache, domain, "SN/%s", sid_to_string(sid_string, sid));
962         if (!centry)
963                 goto do_query;
964         if (NT_STATUS_IS_OK(centry->status)) {
965                 *type = centry_uint32(centry);
966                 *name = centry_string(centry, mem_ctx);
967         }
968         status = centry->status;
969
970         DEBUG(10,("sid_to_name: [Cached] - cached name for domain %s status %s\n",
971                 domain->name, get_friendly_nt_error_msg(status) ));
972
973         centry_free(centry);
974         return status;
975
976 do_query:
977         *name = NULL;
978
979         if (wcache_server_down(domain)) {
980                 return NT_STATUS_SERVER_DISABLED;
981         }
982
983         DEBUG(10,("sid_to_name: [Cached] - doing backend query for name for domain %s\n",
984                 domain->name ));
985
986         status = domain->backend->sid_to_name(domain, mem_ctx, sid, name, type);
987
988         /* and save it */
989         refresh_sequence_number(domain, False);
990         wcache_save_sid_to_name(domain, status, sid, *name, *type);
991         wcache_save_name_to_sid(domain, status, *name, sid, *type);
992
993         return status;
994 }
995
996
997 /* Lookup user information from a rid */
998 static NTSTATUS query_user(struct winbindd_domain *domain, 
999                            TALLOC_CTX *mem_ctx, 
1000                            DOM_SID *user_sid, 
1001                            WINBIND_USERINFO *info)
1002 {
1003         struct winbind_cache *cache = get_cache(domain);
1004         struct cache_entry *centry = NULL;
1005         NTSTATUS status;
1006         fstring sid_string;
1007
1008         if (!cache->tdb)
1009                 goto do_query;
1010
1011         centry = wcache_fetch(cache, domain, "U/%s", sid_to_string(sid_string, user_sid));
1012         if (!centry)
1013                 goto do_query;
1014
1015         info->acct_name = centry_string(centry, mem_ctx);
1016         info->full_name = centry_string(centry, mem_ctx);
1017         info->user_sid = centry_sid(centry, mem_ctx);
1018         info->group_sid = centry_sid(centry, mem_ctx);
1019         status = centry->status;
1020
1021         DEBUG(10,("query_user: [Cached] - cached info for domain %s status %s\n",
1022                 domain->name, get_friendly_nt_error_msg(status) ));
1023
1024         centry_free(centry);
1025         return status;
1026
1027 do_query:
1028         ZERO_STRUCTP(info);
1029
1030         if (wcache_server_down(domain)) {
1031                 return NT_STATUS_SERVER_DISABLED;
1032         }
1033         
1034         DEBUG(10,("sid_to_name: [Cached] - doing backend query for info for domain %s\n",
1035                 domain->name ));
1036
1037         status = domain->backend->query_user(domain, mem_ctx, user_sid, info);
1038
1039         /* and save it */
1040         refresh_sequence_number(domain, False);
1041         wcache_save_user(domain, status, info);
1042
1043         return status;
1044 }
1045
1046
1047 /* Lookup groups a user is a member of. */
1048 static NTSTATUS lookup_usergroups(struct winbindd_domain *domain,
1049                                   TALLOC_CTX *mem_ctx,
1050                                   DOM_SID *user_sid, 
1051                                   uint32 *num_groups, DOM_SID ***user_gids)
1052 {
1053         struct winbind_cache *cache = get_cache(domain);
1054         struct cache_entry *centry = NULL;
1055         NTSTATUS status;
1056         unsigned int i;
1057         fstring sid_string;
1058
1059         if (!cache->tdb)
1060                 goto do_query;
1061
1062         centry = wcache_fetch(cache, domain, "UG/%s", sid_to_string(sid_string, user_sid));
1063         if (!centry)
1064                 goto do_query;
1065
1066         *num_groups = centry_uint32(centry);
1067         
1068         if (*num_groups == 0)
1069                 goto do_cached;
1070
1071         (*user_gids) = talloc(mem_ctx, sizeof(**user_gids) * (*num_groups));
1072         if (! (*user_gids))
1073                 smb_panic("lookup_usergroups out of memory");
1074         for (i=0; i<(*num_groups); i++) {
1075                 (*user_gids)[i] = centry_sid(centry, mem_ctx);
1076         }
1077
1078 do_cached:      
1079         status = centry->status;
1080
1081         DEBUG(10,("lookup_usergroups: [Cached] - cached info for domain %s status %s\n",
1082                 domain->name, get_friendly_nt_error_msg(status) ));
1083
1084         centry_free(centry);
1085         return status;
1086
1087 do_query:
1088         (*num_groups) = 0;
1089         (*user_gids) = NULL;
1090
1091         if (wcache_server_down(domain)) {
1092                 return NT_STATUS_SERVER_DISABLED;
1093         }
1094
1095         DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info for domain %s\n",
1096                 domain->name ));
1097
1098         status = domain->backend->lookup_usergroups(domain, mem_ctx, user_sid, num_groups, user_gids);
1099
1100         /* and save it */
1101         refresh_sequence_number(domain, False);
1102         centry = centry_start(domain, status);
1103         if (!centry)
1104                 goto skip_save;
1105         centry_put_uint32(centry, *num_groups);
1106         for (i=0; i<(*num_groups); i++) {
1107                 centry_put_sid(centry, (*user_gids)[i]);
1108         }       
1109         centry_end(centry, "UG/%s", sid_to_string(sid_string, user_sid));
1110         centry_free(centry);
1111
1112 skip_save:
1113         return status;
1114 }
1115
1116
1117 static NTSTATUS lookup_groupmem(struct winbindd_domain *domain,
1118                                 TALLOC_CTX *mem_ctx,
1119                                 DOM_SID *group_sid, uint32 *num_names, 
1120                                 DOM_SID ***sid_mem, char ***names, 
1121                                 uint32 **name_types)
1122 {
1123         struct winbind_cache *cache = get_cache(domain);
1124         struct cache_entry *centry = NULL;
1125         NTSTATUS status;
1126         unsigned int i;
1127         fstring sid_string;
1128
1129         if (!cache->tdb)
1130                 goto do_query;
1131
1132         centry = wcache_fetch(cache, domain, "GM/%s", sid_to_string(sid_string, group_sid));
1133         if (!centry)
1134                 goto do_query;
1135
1136         *num_names = centry_uint32(centry);
1137         
1138         if (*num_names == 0)
1139                 goto do_cached;
1140
1141         (*sid_mem) = talloc(mem_ctx, sizeof(**sid_mem) * (*num_names));
1142         (*names) = talloc(mem_ctx, sizeof(**names) * (*num_names));
1143         (*name_types) = talloc(mem_ctx, sizeof(**name_types) * (*num_names));
1144
1145         if (! (*sid_mem) || ! (*names) || ! (*name_types)) {
1146                 smb_panic("lookup_groupmem out of memory");
1147         }
1148
1149         for (i=0; i<(*num_names); i++) {
1150                 (*sid_mem)[i] = centry_sid(centry, mem_ctx);
1151                 (*names)[i] = centry_string(centry, mem_ctx);
1152                 (*name_types)[i] = centry_uint32(centry);
1153         }
1154
1155 do_cached:      
1156         status = centry->status;
1157
1158         DEBUG(10,("lookup_groupmem: [Cached] - cached info for domain %s status %s\n",
1159                 domain->name, get_friendly_nt_error_msg(status) ));
1160
1161         centry_free(centry);
1162         return status;
1163
1164 do_query:
1165         (*num_names) = 0;
1166         (*sid_mem) = NULL;
1167         (*names) = NULL;
1168         (*name_types) = NULL;
1169         
1170
1171         if (wcache_server_down(domain)) {
1172                 return NT_STATUS_SERVER_DISABLED;
1173         }
1174
1175         DEBUG(10,("lookup_groupmem: [Cached] - doing backend query for info for domain %s\n",
1176                 domain->name ));
1177
1178         status = domain->backend->lookup_groupmem(domain, mem_ctx, group_sid, num_names, 
1179                                                   sid_mem, names, name_types);
1180
1181         /* and save it */
1182         refresh_sequence_number(domain, False);
1183         centry = centry_start(domain, status);
1184         if (!centry)
1185                 goto skip_save;
1186         centry_put_uint32(centry, *num_names);
1187         for (i=0; i<(*num_names); i++) {
1188                 centry_put_sid(centry, (*sid_mem)[i]);
1189                 centry_put_string(centry, (*names)[i]);
1190                 centry_put_uint32(centry, (*name_types)[i]);
1191         }       
1192         centry_end(centry, "GM/%s", sid_to_string(sid_string, group_sid));
1193         centry_free(centry);
1194
1195 skip_save:
1196         return status;
1197 }
1198
1199 /* find the sequence number for a domain */
1200 static NTSTATUS sequence_number(struct winbindd_domain *domain, uint32 *seq)
1201 {
1202         refresh_sequence_number(domain, False);
1203
1204         *seq = domain->sequence_number;
1205
1206         return NT_STATUS_OK;
1207 }
1208
1209 /* enumerate trusted domains */
1210 static NTSTATUS trusted_domains(struct winbindd_domain *domain,
1211                                 TALLOC_CTX *mem_ctx,
1212                                 uint32 *num_domains,
1213                                 char ***names,
1214                                 char ***alt_names,
1215                                 DOM_SID **dom_sids)
1216 {
1217         get_cache(domain);
1218
1219         DEBUG(10,("trusted_domains: [Cached] - doing backend query for info for domain %s\n",
1220                 domain->name ));
1221
1222         /* we don't cache this call */
1223         return domain->backend->trusted_domains(domain, mem_ctx, num_domains, 
1224                                                names, alt_names, dom_sids);
1225 }
1226
1227 /* find the domain sid */
1228 static NTSTATUS domain_sid(struct winbindd_domain *domain, DOM_SID *sid)
1229 {
1230         get_cache(domain);
1231
1232         DEBUG(10,("domain_sid: [Cached] - doing backend query for info for domain %s\n",
1233                 domain->name ));
1234
1235         /* we don't cache this call */
1236         return domain->backend->domain_sid(domain, sid);
1237 }
1238
1239 /* find the alternate names for the domain, if any */
1240 static NTSTATUS alternate_name(struct winbindd_domain *domain)
1241 {
1242         get_cache(domain);
1243
1244         DEBUG(10,("alternate_name: [Cached] - doing backend query for info for domain %s\n",
1245                 domain->name ));
1246
1247         /* we don't cache this call */
1248         return domain->backend->alternate_name(domain);
1249 }
1250
1251 /* the ADS backend methods are exposed via this structure */
1252 struct winbindd_methods cache_methods = {
1253         True,
1254         query_user_list,
1255         enum_dom_groups,
1256         enum_local_groups,
1257         name_to_sid,
1258         sid_to_name,
1259         query_user,
1260         lookup_usergroups,
1261         lookup_groupmem,
1262         sequence_number,
1263         trusted_domains,
1264         domain_sid,
1265         alternate_name
1266 };