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