Fix up conversion code from old winbindd versions (some testing needed).
[kai/samba.git] / source / nsswitch / winbindd_cache.c
1 /* 
2    Unix SMB/CIFS implementation.
3
4    Winbind cache backend functions
5
6    Copyright (C) Andrew Tridgell 2001
7    
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 struct winbind_cache {
26         struct winbindd_methods *backend;
27         TDB_CONTEXT *tdb;
28 };
29
30 struct cache_entry {
31         NTSTATUS status;
32         uint32 sequence_number;
33         uint8 *data;
34         uint32 len, ofs;
35 };
36
37 #define WINBINDD_MAX_CACHE_SIZE (50*1024*1024)
38
39 static struct winbind_cache *wcache;
40
41 /* flush the cache */
42 void wcache_flush_cache(void)
43 {
44         extern BOOL opt_nocache;
45
46         if (!wcache) return;
47         if (wcache->tdb) {
48                 tdb_close(wcache->tdb);
49                 wcache->tdb = NULL;
50         }
51         if (opt_nocache) return;
52
53         wcache->tdb = tdb_open_log(lock_path("winbindd_cache.tdb"), 5000, 
54                                    TDB_DEFAULT, O_RDWR | O_CREAT | O_TRUNC, 0600);
55
56         if (!wcache->tdb) {
57                 DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
58         }
59 }
60
61 void winbindd_check_cache_size(time_t t)
62 {
63         static time_t last_check_time;
64         struct stat st;
65
66         if (last_check_time == (time_t)0)
67                 last_check_time = t;
68
69         if (t - last_check_time < 60 && t - last_check_time > 0)
70                 return;
71
72         if (wcache == NULL || wcache->tdb == NULL) {
73                 DEBUG(0, ("Unable to check size of tdb cache - cache not open !\n"));
74                 return;
75         }
76
77         if (fstat(wcache->tdb->fd, &st) == -1) {
78                 DEBUG(0, ("Unable to check size of tdb cache %s!\n", strerror(errno) ));
79                 return;
80         }
81
82         if (st.st_size > WINBINDD_MAX_CACHE_SIZE) {
83                 DEBUG(10,("flushing cache due to size (%lu) > (%lu)\n",
84                         (unsigned long)st.st_size,
85                         (unsigned long)WINBINDD_MAX_CACHE_SIZE));
86                 wcache_flush_cache();
87         }
88 }
89
90 /* get the winbind_cache structure */
91 static struct winbind_cache *get_cache(struct winbindd_domain *domain)
92 {
93         extern struct winbindd_methods msrpc_methods;
94         struct winbind_cache *ret = wcache;
95
96         if (ret) return ret;
97         
98         ret = smb_xmalloc(sizeof(*ret));
99         ZERO_STRUCTP(ret);
100         switch (lp_security()) {
101 #ifdef HAVE_ADS
102         case SEC_ADS: {
103                 extern struct winbindd_methods ads_methods;
104                 ret->backend = &ads_methods;
105                 break;
106         }
107 #endif
108         default:
109                 ret->backend = &msrpc_methods;
110         }
111
112         wcache = ret;
113         wcache_flush_cache();
114
115         return ret;
116 }
117
118 /*
119   free a centry structure
120 */
121 static void centry_free(struct cache_entry *centry)
122 {
123         if (!centry) return;
124         SAFE_FREE(centry->data);
125         free(centry);
126 }
127
128
129 /*
130   pull a uint32 from a cache entry 
131 */
132 static uint32 centry_uint32(struct cache_entry *centry)
133 {
134         uint32 ret;
135         if (centry->len - centry->ofs < 4) {
136                 DEBUG(0,("centry corruption? needed 4 bytes, have %d\n", 
137                          centry->len - centry->ofs));
138                 smb_panic("centry_uint32");
139         }
140         ret = IVAL(centry->data, centry->ofs);
141         centry->ofs += 4;
142         return ret;
143 }
144
145 /*
146   pull a uint8 from a cache entry 
147 */
148 static uint8 centry_uint8(struct cache_entry *centry)
149 {
150         uint8 ret;
151         if (centry->len - centry->ofs < 1) {
152                 DEBUG(0,("centry corruption? needed 1 bytes, have %d\n", 
153                          centry->len - centry->ofs));
154                 smb_panic("centry_uint32");
155         }
156         ret = CVAL(centry->data, centry->ofs);
157         centry->ofs += 1;
158         return ret;
159 }
160
161 /* pull a string from a cache entry, using the supplied
162    talloc context 
163 */
164 static char *centry_string(struct cache_entry *centry, TALLOC_CTX *mem_ctx)
165 {
166         uint32 len;
167         char *ret;
168
169         len = centry_uint8(centry);
170
171         if (len == 0xFF) {
172                 /* a deliberate NULL string */
173                 return NULL;
174         }
175
176         if (centry->len - centry->ofs < len) {
177                 DEBUG(0,("centry corruption? needed %d bytes, have %d\n", 
178                          len, centry->len - centry->ofs));
179                 smb_panic("centry_string");
180         }
181
182         ret = talloc(mem_ctx, len+1);
183         if (!ret) {
184                 smb_panic("centry_string out of memory\n");
185         }
186         memcpy(ret,centry->data + centry->ofs, len);
187         ret[len] = 0;
188         centry->ofs += len;
189         return ret;
190 }
191
192 /* the server is considered down if it can't give us a sequence number */
193 static BOOL wcache_server_down(struct winbindd_domain *domain)
194 {
195         if (!wcache->tdb) return False;
196         return (domain->sequence_number == DOM_SEQUENCE_NONE);
197 }
198
199
200 /*
201   refresh the domain sequence number. If force is True
202   then always refresh it, no matter how recently we fetched it
203 */
204 static void refresh_sequence_number(struct winbindd_domain *domain, BOOL force)
205 {
206         NTSTATUS status;
207         unsigned time_diff;
208
209         time_diff = time(NULL) - domain->last_seq_check;
210
211         /* see if we have to refetch the domain sequence number */
212         if (!force && (time_diff < lp_winbind_cache_time())) {
213                 return;
214         }
215
216         status = wcache->backend->sequence_number(domain, &domain->sequence_number);
217
218         if (!NT_STATUS_IS_OK(status)) {
219                 domain->sequence_number = DOM_SEQUENCE_NONE;
220         }
221
222         domain->last_seq_check = time(NULL);
223 }
224
225 /*
226   decide if a cache entry has expired
227 */
228 static BOOL centry_expired(struct winbindd_domain *domain, struct cache_entry *centry)
229 {
230         /* if the server is OK and our cache entry came from when it was down then
231            the entry is invalid */
232         if (domain->sequence_number != DOM_SEQUENCE_NONE && 
233             centry->sequence_number == DOM_SEQUENCE_NONE) {
234                 return True;
235         }
236
237         /* if the server is down or the cache entry is not older than the
238            current sequence number then it is OK */
239         if (wcache_server_down(domain) || 
240             centry->sequence_number == domain->sequence_number) {
241                 return False;
242         }
243
244         /* it's expired */
245         return True;
246 }
247
248 /*
249   fetch an entry from the cache, with a varargs key. auto-fetch the sequence
250   number and return status
251 */
252 static struct cache_entry *wcache_fetch(struct winbind_cache *cache, 
253                                         struct winbindd_domain *domain,
254                                         const char *format, ...)
255 {
256         va_list ap;
257         char *kstr;
258         TDB_DATA data;
259         struct cache_entry *centry;
260         TDB_DATA key;
261
262         refresh_sequence_number(domain, False);
263
264         va_start(ap, format);
265         smb_xvasprintf(&kstr, format, ap);
266         va_end(ap);
267         
268         key.dptr = kstr;
269         key.dsize = strlen(kstr);
270         data = tdb_fetch(wcache->tdb, key);
271         free(kstr);
272         if (!data.dptr) {
273                 /* a cache miss */
274                 return NULL;
275         }
276
277         centry = smb_xmalloc(sizeof(*centry));
278         centry->data = data.dptr;
279         centry->len = data.dsize;
280         centry->ofs = 0;
281
282         if (centry->len < 8) {
283                 /* huh? corrupt cache? */
284                 centry_free(centry);
285                 return NULL;
286         }
287         
288         centry->status = NT_STATUS(centry_uint32(centry));
289         centry->sequence_number = centry_uint32(centry);
290
291         if (centry_expired(domain, centry)) {
292                 centry_free(centry);
293                 return NULL;
294         }
295
296         return centry;
297 }
298
299 /*
300   make sure we have at least len bytes available in a centry 
301 */
302 static void centry_expand(struct cache_entry *centry, uint32 len)
303 {
304         uint8 *p;
305         if (centry->len - centry->ofs >= len) return;
306         centry->len *= 2;
307         p = realloc(centry->data, centry->len);
308         if (!p) {
309                 DEBUG(0,("out of memory: needed %d bytes in centry_expand\n", centry->len));
310                 smb_panic("out of memory in centry_expand");
311         }
312         centry->data = p;
313 }
314
315 /*
316   push a uint32 into a centry 
317 */
318 static void centry_put_uint32(struct cache_entry *centry, uint32 v)
319 {
320         centry_expand(centry, 4);
321         SIVAL(centry->data, centry->ofs, v);
322         centry->ofs += 4;
323 }
324
325 /*
326   push a uint8 into a centry 
327 */
328 static void centry_put_uint8(struct cache_entry *centry, uint8 v)
329 {
330         centry_expand(centry, 1);
331         SCVAL(centry->data, centry->ofs, v);
332         centry->ofs += 1;
333 }
334
335 /* 
336    push a string into a centry 
337  */
338 static void centry_put_string(struct cache_entry *centry, const char *s)
339 {
340         int len;
341
342         if (!s) {
343                 /* null strings are marked as len 0xFFFF */
344                 centry_put_uint8(centry, 0xFF);
345                 return;
346         }
347
348         len = strlen(s);
349         /* can't handle more than 254 char strings. Truncating is probably best */
350         if (len > 254) len = 254;
351         centry_put_uint8(centry, len);
352         centry_expand(centry, len);
353         memcpy(centry->data + centry->ofs, s, len);
354         centry->ofs += len;
355 }
356
357 /*
358   start a centry for output. When finished, call centry_end()
359 */
360 struct cache_entry *centry_start(struct winbindd_domain *domain, NTSTATUS status)
361 {
362         struct cache_entry *centry;
363
364         if (!wcache->tdb) return NULL;
365
366         centry = smb_xmalloc(sizeof(*centry));
367
368         centry->len = 8192; /* reasonable default */
369         centry->data = smb_xmalloc(centry->len);
370         centry->ofs = 0;
371         centry->sequence_number = domain->sequence_number;
372         centry_put_uint32(centry, NT_STATUS_V(status));
373         centry_put_uint32(centry, centry->sequence_number);
374         return centry;
375 }
376
377 /*
378   finish a centry and write it to the tdb
379 */
380 static void centry_end(struct cache_entry *centry, const char *format, ...)
381 {
382         va_list ap;
383         char *kstr;
384         TDB_DATA key, data;
385
386         va_start(ap, format);
387         smb_xvasprintf(&kstr, format, ap);
388         va_end(ap);
389
390         key.dptr = kstr;
391         key.dsize = strlen(kstr);
392         data.dptr = centry->data;
393         data.dsize = centry->ofs;
394
395         tdb_store(wcache->tdb, key, data, TDB_REPLACE);
396         free(kstr);
397 }
398
399 /* form a sid from the domain plus rid */
400 static DOM_SID *form_sid(struct winbindd_domain *domain, uint32 rid)
401 {
402         static DOM_SID sid;
403         sid_copy(&sid, &domain->sid);
404         sid_append_rid(&sid, rid);
405         return &sid;
406 }
407
408 static void wcache_save_name_to_sid(struct winbindd_domain *domain, NTSTATUS status, 
409                                     const char *name, DOM_SID *sid, enum SID_NAME_USE type)
410 {
411         struct cache_entry *centry;
412         uint32 len;
413
414         centry = centry_start(domain, status);
415         if (!centry) return;
416         len = sid_size(sid);
417         centry_expand(centry, len);
418         centry_put_uint32(centry, type);
419         sid_linearize(centry->data + centry->ofs, len, sid);
420         centry->ofs += len;
421         centry_end(centry, "NS/%s/%s", domain->name, name);
422         centry_free(centry);
423 }
424
425 static void wcache_save_sid_to_name(struct winbindd_domain *domain, NTSTATUS status, 
426                                     DOM_SID *sid, const char *name, enum SID_NAME_USE type, uint32 rid)
427 {
428         struct cache_entry *centry;
429
430         centry = centry_start(domain, status);
431         if (!centry) return;
432         if (NT_STATUS_IS_OK(status)) {
433                 centry_put_uint32(centry, type);
434                 centry_put_string(centry, name);
435         }
436         centry_end(centry, "SN/%s/%d", domain->name, rid);
437         centry_free(centry);
438 }
439
440
441 static void wcache_save_user(struct winbindd_domain *domain, NTSTATUS status, WINBIND_USERINFO *info)
442 {
443         struct cache_entry *centry;
444
445         centry = centry_start(domain, status);
446         if (!centry) return;
447         centry_put_string(centry, info->acct_name);
448         centry_put_string(centry, info->full_name);
449         centry_put_uint32(centry, info->user_rid);
450         centry_put_uint32(centry, info->group_rid);
451         centry_end(centry, "U/%s/%x", domain->name, info->user_rid);
452         centry_free(centry);
453 }
454
455
456 /* Query display info. This is the basic user list fn */
457 static NTSTATUS query_user_list(struct winbindd_domain *domain,
458                                 TALLOC_CTX *mem_ctx,
459                                 uint32 *num_entries, 
460                                 WINBIND_USERINFO **info)
461 {
462         struct winbind_cache *cache = get_cache(domain);
463         struct cache_entry *centry = NULL;
464         NTSTATUS status;
465         int i;
466
467         if (!cache->tdb) goto do_query;
468
469         centry = wcache_fetch(cache, domain, "UL/%s", domain->name);
470         if (!centry) goto do_query;
471
472         *num_entries = centry_uint32(centry);
473         
474         if (*num_entries == 0) goto do_cached;
475
476         (*info) = talloc(mem_ctx, sizeof(**info) * (*num_entries));
477         if (! (*info)) smb_panic("query_user_list out of memory");
478         for (i=0; i<(*num_entries); i++) {
479                 (*info)[i].acct_name = centry_string(centry, mem_ctx);
480                 (*info)[i].full_name = centry_string(centry, mem_ctx);
481                 (*info)[i].user_rid = centry_uint32(centry);
482                 (*info)[i].group_rid = centry_uint32(centry);
483         }
484
485 do_cached:      
486         status = centry->status;
487         centry_free(centry);
488         return status;
489
490 do_query:
491         *num_entries = 0;
492         *info = NULL;
493
494         if (wcache_server_down(domain)) {
495                 return NT_STATUS_SERVER_DISABLED;
496         }
497
498         status = cache->backend->query_user_list(domain, mem_ctx, num_entries, info);
499
500         /* and save it */
501         refresh_sequence_number(domain, True);
502         centry = centry_start(domain, status);
503         if (!centry) goto skip_save;
504         centry_put_uint32(centry, *num_entries);
505         for (i=0; i<(*num_entries); i++) {
506                 centry_put_string(centry, (*info)[i].acct_name);
507                 centry_put_string(centry, (*info)[i].full_name);
508                 centry_put_uint32(centry, (*info)[i].user_rid);
509                 centry_put_uint32(centry, (*info)[i].group_rid);
510                 if (cache->backend->consistent) {
511                         /* when the backend is consistent we can pre-prime some mappings */
512                         wcache_save_name_to_sid(domain, NT_STATUS_OK, 
513                                                 (*info)[i].acct_name, 
514                                                 form_sid(domain, (*info)[i].user_rid),
515                                                 SID_NAME_USER);
516                         wcache_save_sid_to_name(domain, NT_STATUS_OK, 
517                                                 form_sid(domain, (*info)[i].user_rid),
518                                                 (*info)[i].acct_name, 
519                                                 SID_NAME_USER, (*info)[i].user_rid);
520                         wcache_save_user(domain, NT_STATUS_OK, &(*info)[i]);
521                 }
522         }       
523         centry_end(centry, "UL/%s", domain->name);
524         centry_free(centry);
525
526 skip_save:
527         return status;
528 }
529
530 /* list all domain groups */
531 static NTSTATUS enum_dom_groups(struct winbindd_domain *domain,
532                                 TALLOC_CTX *mem_ctx,
533                                 uint32 *num_entries, 
534                                 struct acct_info **info)
535 {
536         struct winbind_cache *cache = get_cache(domain);
537         struct cache_entry *centry = NULL;
538         NTSTATUS status;
539         int i;
540
541         if (!cache->tdb) goto do_query;
542
543         centry = wcache_fetch(cache, domain, "GL/%s", domain->name);
544         if (!centry) goto do_query;
545
546         *num_entries = centry_uint32(centry);
547         
548         if (*num_entries == 0) goto do_cached;
549
550         (*info) = talloc(mem_ctx, sizeof(**info) * (*num_entries));
551         if (! (*info)) smb_panic("enum_dom_groups out of memory");
552         for (i=0; i<(*num_entries); i++) {
553                 fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx));
554                 fstrcpy((*info)[i].acct_desc, centry_string(centry, mem_ctx));
555                 (*info)[i].rid = centry_uint32(centry);
556         }
557
558 do_cached:      
559         status = centry->status;
560         centry_free(centry);
561         return status;
562
563 do_query:
564         *num_entries = 0;
565         *info = NULL;
566
567         if (wcache_server_down(domain)) {
568                 return NT_STATUS_SERVER_DISABLED;
569         }
570
571         status = cache->backend->enum_dom_groups(domain, mem_ctx, num_entries, info);
572
573         /* and save it */
574         refresh_sequence_number(domain, True);
575         centry = centry_start(domain, status);
576         if (!centry) goto skip_save;
577         centry_put_uint32(centry, *num_entries);
578         for (i=0; i<(*num_entries); i++) {
579                 centry_put_string(centry, (*info)[i].acct_name);
580                 centry_put_string(centry, (*info)[i].acct_desc);
581                 centry_put_uint32(centry, (*info)[i].rid);
582         }       
583         centry_end(centry, "GL/%s", domain->name);
584         centry_free(centry);
585
586 skip_save:
587         return status;
588 }
589
590
591 /* convert a single name to a sid in a domain */
592 static NTSTATUS name_to_sid(struct winbindd_domain *domain,
593                             const char *name,
594                             DOM_SID *sid,
595                             enum SID_NAME_USE *type)
596 {
597         struct winbind_cache *cache = get_cache(domain);
598         struct cache_entry *centry = NULL;
599         NTSTATUS status;
600
601         if (!cache->tdb) goto do_query;
602
603         centry = wcache_fetch(cache, domain, "NS/%s/%s", domain->name, name);
604         if (!centry) goto do_query;
605         *type = centry_uint32(centry);
606         sid_parse(centry->data + centry->ofs, centry->len - centry->ofs, sid);
607
608         status = centry->status;
609         centry_free(centry);
610         return status;
611
612 do_query:
613         ZERO_STRUCTP(sid);
614
615         if (wcache_server_down(domain)) {
616                 return NT_STATUS_SERVER_DISABLED;
617         }
618         status = cache->backend->name_to_sid(domain, name, sid, type);
619
620         /* and save it */
621         wcache_save_name_to_sid(domain, status, name, sid, *type);
622
623         return status;
624 }
625
626 /* convert a sid to a user or group name. The sid is guaranteed to be in the domain
627    given */
628 static NTSTATUS sid_to_name(struct winbindd_domain *domain,
629                             TALLOC_CTX *mem_ctx,
630                             DOM_SID *sid,
631                             char **name,
632                             enum SID_NAME_USE *type)
633 {
634         struct winbind_cache *cache = get_cache(domain);
635         struct cache_entry *centry = NULL;
636         NTSTATUS status;
637         uint32 rid = 0;
638
639         sid_peek_rid(sid, &rid);
640
641         if (!cache->tdb) goto do_query;
642
643         centry = wcache_fetch(cache, domain, "SN/%s/%d", domain->name, rid);
644         if (!centry) goto do_query;
645         if (NT_STATUS_IS_OK(centry->status)) {
646                 *type = centry_uint32(centry);
647                 *name = centry_string(centry, mem_ctx);
648         }
649         status = centry->status;
650         centry_free(centry);
651         return status;
652
653 do_query:
654         *name = NULL;
655
656         if (wcache_server_down(domain)) {
657                 return NT_STATUS_SERVER_DISABLED;
658         }
659         status = cache->backend->sid_to_name(domain, mem_ctx, sid, name, type);
660
661         /* and save it */
662         refresh_sequence_number(domain, True);
663         wcache_save_sid_to_name(domain, status, sid, *name, *type, rid);
664
665         return status;
666 }
667
668
669 /* Lookup user information from a rid */
670 static NTSTATUS query_user(struct winbindd_domain *domain, 
671                            TALLOC_CTX *mem_ctx, 
672                            uint32 user_rid, 
673                            WINBIND_USERINFO *info)
674 {
675         struct winbind_cache *cache = get_cache(domain);
676         struct cache_entry *centry = NULL;
677         NTSTATUS status;
678
679         if (!cache->tdb) goto do_query;
680
681         centry = wcache_fetch(cache, domain, "U/%s/%x", domain->name, user_rid);
682         if (!centry) goto do_query;
683
684         info->acct_name = centry_string(centry, mem_ctx);
685         info->full_name = centry_string(centry, mem_ctx);
686         info->user_rid = centry_uint32(centry);
687         info->group_rid = centry_uint32(centry);
688         status = centry->status;
689         centry_free(centry);
690         return status;
691
692 do_query:
693         ZERO_STRUCTP(info);
694
695         if (wcache_server_down(domain)) {
696                 return NT_STATUS_SERVER_DISABLED;
697         }
698         
699         status = cache->backend->query_user(domain, mem_ctx, user_rid, info);
700
701         /* and save it */
702         refresh_sequence_number(domain, True);
703         wcache_save_user(domain, status, info);
704
705         return status;
706 }
707
708
709 /* Lookup groups a user is a member of. */
710 static NTSTATUS lookup_usergroups(struct winbindd_domain *domain,
711                                   TALLOC_CTX *mem_ctx,
712                                   uint32 user_rid, 
713                                   uint32 *num_groups, uint32 **user_gids)
714 {
715         struct winbind_cache *cache = get_cache(domain);
716         struct cache_entry *centry = NULL;
717         NTSTATUS status;
718         int i;
719
720         if (!cache->tdb) goto do_query;
721
722         centry = wcache_fetch(cache, domain, "UG/%s/%x", domain->name, user_rid);
723         if (!centry) goto do_query;
724
725         *num_groups = centry_uint32(centry);
726         
727         if (*num_groups == 0) goto do_cached;
728
729         (*user_gids) = talloc(mem_ctx, sizeof(**user_gids) * (*num_groups));
730         if (! (*user_gids)) smb_panic("lookup_usergroups out of memory");
731         for (i=0; i<(*num_groups); i++) {
732                 (*user_gids)[i] = centry_uint32(centry);
733         }
734
735 do_cached:      
736         status = centry->status;
737         centry_free(centry);
738         return status;
739
740 do_query:
741         (*num_groups) = 0;
742         (*user_gids) = NULL;
743
744         if (wcache_server_down(domain)) {
745                 return NT_STATUS_SERVER_DISABLED;
746         }
747         status = cache->backend->lookup_usergroups(domain, mem_ctx, user_rid, num_groups, user_gids);
748
749         /* and save it */
750         refresh_sequence_number(domain, True);
751         centry = centry_start(domain, status);
752         if (!centry) goto skip_save;
753         centry_put_uint32(centry, *num_groups);
754         for (i=0; i<(*num_groups); i++) {
755                 centry_put_uint32(centry, (*user_gids)[i]);
756         }       
757         centry_end(centry, "UG/%s/%x", domain->name, user_rid);
758         centry_free(centry);
759
760 skip_save:
761         return status;
762 }
763
764
765 static NTSTATUS lookup_groupmem(struct winbindd_domain *domain,
766                                 TALLOC_CTX *mem_ctx,
767                                 uint32 group_rid, uint32 *num_names, 
768                                 uint32 **rid_mem, char ***names, 
769                                 uint32 **name_types)
770 {
771         struct winbind_cache *cache = get_cache(domain);
772         struct cache_entry *centry = NULL;
773         NTSTATUS status;
774         int i;
775
776         if (!cache->tdb) goto do_query;
777
778         centry = wcache_fetch(cache, domain, "GM/%s/%x", domain->name, group_rid);
779         if (!centry) goto do_query;
780
781         *num_names = centry_uint32(centry);
782         
783         if (*num_names == 0) goto do_cached;
784
785         (*rid_mem) = talloc(mem_ctx, sizeof(**rid_mem) * (*num_names));
786         (*names) = talloc(mem_ctx, sizeof(**names) * (*num_names));
787         (*name_types) = talloc(mem_ctx, sizeof(**name_types) * (*num_names));
788
789         if (! (*rid_mem) || ! (*names) || ! (*name_types)) {
790                 smb_panic("lookup_groupmem out of memory");
791         }
792
793         for (i=0; i<(*num_names); i++) {
794                 (*rid_mem)[i] = centry_uint32(centry);
795                 (*names)[i] = centry_string(centry, mem_ctx);
796                 (*name_types)[i] = centry_uint32(centry);
797         }
798
799 do_cached:      
800         status = centry->status;
801         centry_free(centry);
802         return status;
803
804 do_query:
805         (*num_names) = 0;
806         (*rid_mem) = NULL;
807         (*names) = NULL;
808         (*name_types) = NULL;
809         
810
811         if (wcache_server_down(domain)) {
812                 return NT_STATUS_SERVER_DISABLED;
813         }
814         status = cache->backend->lookup_groupmem(domain, mem_ctx, group_rid, num_names, 
815                                                  rid_mem, names, name_types);
816
817         /* and save it */
818         refresh_sequence_number(domain, True);
819         centry = centry_start(domain, status);
820         if (!centry) goto skip_save;
821         centry_put_uint32(centry, *num_names);
822         for (i=0; i<(*num_names); i++) {
823                 centry_put_uint32(centry, (*rid_mem)[i]);
824                 centry_put_string(centry, (*names)[i]);
825                 centry_put_uint32(centry, (*name_types)[i]);
826         }       
827         centry_end(centry, "GM/%s/%x", domain->name, group_rid);
828         centry_free(centry);
829
830 skip_save:
831         return status;
832 }
833
834 /* find the sequence number for a domain */
835 static NTSTATUS sequence_number(struct winbindd_domain *domain, uint32 *seq)
836 {
837         refresh_sequence_number(domain, False);
838
839         *seq = domain->sequence_number;
840
841         return NT_STATUS_OK;
842 }
843
844 /* enumerate trusted domains */
845 static NTSTATUS trusted_domains(struct winbindd_domain *domain,
846                                 TALLOC_CTX *mem_ctx,
847                                 uint32 *num_domains,
848                                 char ***names,
849                                 DOM_SID **dom_sids)
850 {
851         struct winbind_cache *cache = get_cache(domain);
852
853         /* we don't cache this call */
854         return cache->backend->trusted_domains(domain, mem_ctx, num_domains, 
855                                                names, dom_sids);
856 }
857
858 /* find the domain sid */
859 static NTSTATUS domain_sid(struct winbindd_domain *domain, DOM_SID *sid)
860 {
861         struct winbind_cache *cache = get_cache(domain);
862
863         /* we don't cache this call */
864         return cache->backend->domain_sid(domain, sid);
865 }
866
867 /* the ADS backend methods are exposed via this structure */
868 struct winbindd_methods cache_methods = {
869         True,
870         query_user_list,
871         enum_dom_groups,
872         name_to_sid,
873         sid_to_name,
874         query_user,
875         lookup_usergroups,
876         lookup_groupmem,
877         sequence_number,
878         trusted_domains,
879         domain_sid
880 };
881
882