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