Store the key for a name to sid cache entry in upper case rather than
[ira/wip.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 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_CLEAR_IF_FIRST, O_RDWR|O_CREAT, 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         unsigned cache_time = lp_winbind_cache_time();
209
210         /* trying to reconnect is expensive, don't do it too often */
211         if (domain->sequence_number == DOM_SEQUENCE_NONE) {
212                 cache_time *= 8;
213         }
214
215         time_diff = time(NULL) - domain->last_seq_check;
216
217         /* see if we have to refetch the domain sequence number */
218         if (!force && (time_diff < cache_time)) {
219                 return;
220         }
221
222         status = wcache->backend->sequence_number(domain, &domain->sequence_number);
223
224         if (!NT_STATUS_IS_OK(status)) {
225                 domain->sequence_number = DOM_SEQUENCE_NONE;
226         }
227
228         domain->last_seq_check = time(NULL);
229 }
230
231 /*
232   decide if a cache entry has expired
233 */
234 static BOOL centry_expired(struct winbindd_domain *domain, struct cache_entry *centry)
235 {
236         /* if the server is OK and our cache entry came from when it was down then
237            the entry is invalid */
238         if (domain->sequence_number != DOM_SEQUENCE_NONE && 
239             centry->sequence_number == DOM_SEQUENCE_NONE) {
240                 return True;
241         }
242
243         /* if the server is down or the cache entry is not older than the
244            current sequence number then it is OK */
245         if (wcache_server_down(domain) || 
246             centry->sequence_number == domain->sequence_number) {
247                 return False;
248         }
249
250         /* it's expired */
251         return True;
252 }
253
254 /*
255   fetch an entry from the cache, with a varargs key. auto-fetch the sequence
256   number and return status
257 */
258 static struct cache_entry *wcache_fetch(struct winbind_cache *cache, 
259                                         struct winbindd_domain *domain,
260                                         const char *format, ...)
261 {
262         va_list ap;
263         char *kstr;
264         TDB_DATA data;
265         struct cache_entry *centry;
266         TDB_DATA key;
267
268         refresh_sequence_number(domain, False);
269
270         va_start(ap, format);
271         smb_xvasprintf(&kstr, format, ap);
272         va_end(ap);
273         
274         key.dptr = kstr;
275         key.dsize = strlen(kstr);
276         data = tdb_fetch(wcache->tdb, key);
277         free(kstr);
278         if (!data.dptr) {
279                 /* a cache miss */
280                 return NULL;
281         }
282
283         centry = smb_xmalloc(sizeof(*centry));
284         centry->data = data.dptr;
285         centry->len = data.dsize;
286         centry->ofs = 0;
287
288         if (centry->len < 8) {
289                 /* huh? corrupt cache? */
290                 centry_free(centry);
291                 return NULL;
292         }
293         
294         centry->status = NT_STATUS(centry_uint32(centry));
295         centry->sequence_number = centry_uint32(centry);
296
297         if (centry_expired(domain, centry)) {
298                 extern BOOL opt_dual_daemon;
299
300                 if (opt_dual_daemon) {
301                         extern BOOL backgroud_process;
302                         backgroud_process = True;
303                 } else {
304                         centry_free(centry);
305                         return NULL;
306                 }
307         }
308
309         return centry;
310 }
311
312 /*
313   make sure we have at least len bytes available in a centry 
314 */
315 static void centry_expand(struct cache_entry *centry, uint32 len)
316 {
317         uint8 *p;
318         if (centry->len - centry->ofs >= len) return;
319         centry->len *= 2;
320         p = realloc(centry->data, centry->len);
321         if (!p) {
322                 DEBUG(0,("out of memory: needed %d bytes in centry_expand\n", centry->len));
323                 smb_panic("out of memory in centry_expand");
324         }
325         centry->data = p;
326 }
327
328 /*
329   push a uint32 into a centry 
330 */
331 static void centry_put_uint32(struct cache_entry *centry, uint32 v)
332 {
333         centry_expand(centry, 4);
334         SIVAL(centry->data, centry->ofs, v);
335         centry->ofs += 4;
336 }
337
338 /*
339   push a uint8 into a centry 
340 */
341 static void centry_put_uint8(struct cache_entry *centry, uint8 v)
342 {
343         centry_expand(centry, 1);
344         SCVAL(centry->data, centry->ofs, v);
345         centry->ofs += 1;
346 }
347
348 /* 
349    push a string into a centry 
350  */
351 static void centry_put_string(struct cache_entry *centry, const char *s)
352 {
353         int len;
354
355         if (!s) {
356                 /* null strings are marked as len 0xFFFF */
357                 centry_put_uint8(centry, 0xFF);
358                 return;
359         }
360
361         len = strlen(s);
362         /* can't handle more than 254 char strings. Truncating is probably best */
363         if (len > 254) len = 254;
364         centry_put_uint8(centry, len);
365         centry_expand(centry, len);
366         memcpy(centry->data + centry->ofs, s, len);
367         centry->ofs += len;
368 }
369
370 /*
371   start a centry for output. When finished, call centry_end()
372 */
373 struct cache_entry *centry_start(struct winbindd_domain *domain, NTSTATUS status)
374 {
375         struct cache_entry *centry;
376
377         if (!wcache->tdb) return NULL;
378
379         centry = smb_xmalloc(sizeof(*centry));
380
381         centry->len = 8192; /* reasonable default */
382         centry->data = smb_xmalloc(centry->len);
383         centry->ofs = 0;
384         centry->sequence_number = domain->sequence_number;
385         centry_put_uint32(centry, NT_STATUS_V(status));
386         centry_put_uint32(centry, centry->sequence_number);
387         return centry;
388 }
389
390 /*
391   finish a centry and write it to the tdb
392 */
393 static void centry_end(struct cache_entry *centry, const char *format, ...)
394 {
395         va_list ap;
396         char *kstr;
397         TDB_DATA key, data;
398
399         va_start(ap, format);
400         smb_xvasprintf(&kstr, format, ap);
401         va_end(ap);
402
403         key.dptr = kstr;
404         key.dsize = strlen(kstr);
405         data.dptr = centry->data;
406         data.dsize = centry->ofs;
407
408         tdb_store(wcache->tdb, key, data, TDB_REPLACE);
409         free(kstr);
410 }
411
412 /* form a sid from the domain plus rid */
413 static DOM_SID *form_sid(struct winbindd_domain *domain, uint32 rid)
414 {
415         static DOM_SID sid;
416         sid_copy(&sid, &domain->sid);
417         sid_append_rid(&sid, rid);
418         return &sid;
419 }
420
421 static void wcache_save_name_to_sid(struct winbindd_domain *domain, NTSTATUS status, 
422                                     const char *name, DOM_SID *sid, enum SID_NAME_USE type)
423 {
424         struct cache_entry *centry;
425         uint32 len;
426         fstring uname;
427
428         centry = centry_start(domain, status);
429         if (!centry) return;
430         len = sid_size(sid);
431         centry_expand(centry, len);
432         centry_put_uint32(centry, type);
433         sid_linearize(centry->data + centry->ofs, len, sid);
434         centry->ofs += len;
435         fstrcpy(uname, name);
436         strupper(uname);
437         centry_end(centry, "NS/%s/%s", domain->name, uname);
438         centry_free(centry);
439 }
440
441 static void wcache_save_sid_to_name(struct winbindd_domain *domain, NTSTATUS status, 
442                                     DOM_SID *sid, const char *name, enum SID_NAME_USE type, uint32 rid)
443 {
444         struct cache_entry *centry;
445
446         centry = centry_start(domain, status);
447         if (!centry) return;
448         if (NT_STATUS_IS_OK(status)) {
449                 centry_put_uint32(centry, type);
450                 centry_put_string(centry, name);
451         }
452         centry_end(centry, "SN/%s/%d", domain->name, rid);
453         centry_free(centry);
454 }
455
456
457 static void wcache_save_user(struct winbindd_domain *domain, NTSTATUS status, WINBIND_USERINFO *info)
458 {
459         struct cache_entry *centry;
460
461         centry = centry_start(domain, status);
462         if (!centry) return;
463         centry_put_string(centry, info->acct_name);
464         centry_put_string(centry, info->full_name);
465         centry_put_uint32(centry, info->user_rid);
466         centry_put_uint32(centry, info->group_rid);
467         centry_end(centry, "U/%s/%x", domain->name, info->user_rid);
468         centry_free(centry);
469 }
470
471
472 /* Query display info. This is the basic user list fn */
473 static NTSTATUS query_user_list(struct winbindd_domain *domain,
474                                 TALLOC_CTX *mem_ctx,
475                                 uint32 *num_entries, 
476                                 WINBIND_USERINFO **info)
477 {
478         struct winbind_cache *cache = get_cache(domain);
479         struct cache_entry *centry = NULL;
480         NTSTATUS status;
481         int i;
482
483         if (!cache->tdb) goto do_query;
484
485         centry = wcache_fetch(cache, domain, "UL/%s", domain->name);
486         if (!centry) goto do_query;
487
488         *num_entries = centry_uint32(centry);
489         
490         if (*num_entries == 0) goto do_cached;
491
492         (*info) = talloc(mem_ctx, sizeof(**info) * (*num_entries));
493         if (! (*info)) smb_panic("query_user_list out of memory");
494         for (i=0; i<(*num_entries); i++) {
495                 (*info)[i].acct_name = centry_string(centry, mem_ctx);
496                 (*info)[i].full_name = centry_string(centry, mem_ctx);
497                 (*info)[i].user_rid = centry_uint32(centry);
498                 (*info)[i].group_rid = centry_uint32(centry);
499         }
500
501 do_cached:      
502         status = centry->status;
503         centry_free(centry);
504         return status;
505
506 do_query:
507         *num_entries = 0;
508         *info = NULL;
509
510         if (wcache_server_down(domain)) {
511                 return NT_STATUS_SERVER_DISABLED;
512         }
513
514         status = cache->backend->query_user_list(domain, mem_ctx, num_entries, info);
515
516         /* and save it */
517         refresh_sequence_number(domain, True);
518         centry = centry_start(domain, status);
519         if (!centry) goto skip_save;
520         centry_put_uint32(centry, *num_entries);
521         for (i=0; i<(*num_entries); i++) {
522                 centry_put_string(centry, (*info)[i].acct_name);
523                 centry_put_string(centry, (*info)[i].full_name);
524                 centry_put_uint32(centry, (*info)[i].user_rid);
525                 centry_put_uint32(centry, (*info)[i].group_rid);
526                 if (cache->backend->consistent) {
527                         /* when the backend is consistent we can pre-prime some mappings */
528                         wcache_save_name_to_sid(domain, NT_STATUS_OK, 
529                                                 (*info)[i].acct_name, 
530                                                 form_sid(domain, (*info)[i].user_rid),
531                                                 SID_NAME_USER);
532                         wcache_save_sid_to_name(domain, NT_STATUS_OK, 
533                                                 form_sid(domain, (*info)[i].user_rid),
534                                                 (*info)[i].acct_name, 
535                                                 SID_NAME_USER, (*info)[i].user_rid);
536                         wcache_save_user(domain, NT_STATUS_OK, &(*info)[i]);
537                 }
538         }       
539         centry_end(centry, "UL/%s", domain->name);
540         centry_free(centry);
541
542 skip_save:
543         return status;
544 }
545
546 /* list all domain groups */
547 static NTSTATUS enum_dom_groups(struct winbindd_domain *domain,
548                                 TALLOC_CTX *mem_ctx,
549                                 uint32 *num_entries, 
550                                 struct acct_info **info)
551 {
552         struct winbind_cache *cache = get_cache(domain);
553         struct cache_entry *centry = NULL;
554         NTSTATUS status;
555         int i;
556
557         if (!cache->tdb) goto do_query;
558
559         centry = wcache_fetch(cache, domain, "GL/%s", domain->name);
560         if (!centry) goto do_query;
561
562         *num_entries = centry_uint32(centry);
563         
564         if (*num_entries == 0) goto do_cached;
565
566         (*info) = talloc(mem_ctx, sizeof(**info) * (*num_entries));
567         if (! (*info)) smb_panic("enum_dom_groups out of memory");
568         for (i=0; i<(*num_entries); i++) {
569                 fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx));
570                 fstrcpy((*info)[i].acct_desc, centry_string(centry, mem_ctx));
571                 (*info)[i].rid = centry_uint32(centry);
572         }
573
574 do_cached:      
575         status = centry->status;
576         centry_free(centry);
577         return status;
578
579 do_query:
580         *num_entries = 0;
581         *info = NULL;
582
583         if (wcache_server_down(domain)) {
584                 return NT_STATUS_SERVER_DISABLED;
585         }
586
587         status = cache->backend->enum_dom_groups(domain, mem_ctx, num_entries, info);
588
589         /* and save it */
590         refresh_sequence_number(domain, True);
591         centry = centry_start(domain, status);
592         if (!centry) goto skip_save;
593         centry_put_uint32(centry, *num_entries);
594         for (i=0; i<(*num_entries); i++) {
595                 centry_put_string(centry, (*info)[i].acct_name);
596                 centry_put_string(centry, (*info)[i].acct_desc);
597                 centry_put_uint32(centry, (*info)[i].rid);
598         }       
599         centry_end(centry, "GL/%s", domain->name);
600         centry_free(centry);
601
602 skip_save:
603         return status;
604 }
605
606
607 /* convert a single name to a sid in a domain */
608 static NTSTATUS name_to_sid(struct winbindd_domain *domain,
609                             const char *name,
610                             DOM_SID *sid,
611                             enum SID_NAME_USE *type)
612 {
613         struct winbind_cache *cache = get_cache(domain);
614         struct cache_entry *centry = NULL;
615         NTSTATUS status;
616         fstring uname;
617
618         if (!cache->tdb) goto do_query;
619
620         fstrcpy(uname, name);
621         strupper(uname);
622         centry = wcache_fetch(cache, domain, "NS/%s/%s", domain->name, uname);
623         if (!centry) goto do_query;
624         *type = centry_uint32(centry);
625         sid_parse(centry->data + centry->ofs, centry->len - centry->ofs, sid);
626
627         status = centry->status;
628         centry_free(centry);
629         return status;
630
631 do_query:
632         ZERO_STRUCTP(sid);
633
634         if (wcache_server_down(domain)) {
635                 return NT_STATUS_SERVER_DISABLED;
636         }
637         status = cache->backend->name_to_sid(domain, name, sid, type);
638
639         /* and save it */
640         wcache_save_name_to_sid(domain, status, name, sid, *type);
641
642         /* We can't save the sid to name mapping as we don't know the
643            correct case of the name without looking it up */
644
645         return status;
646 }
647
648 /* convert a sid to a user or group name. The sid is guaranteed to be in the domain
649    given */
650 static NTSTATUS sid_to_name(struct winbindd_domain *domain,
651                             TALLOC_CTX *mem_ctx,
652                             DOM_SID *sid,
653                             char **name,
654                             enum SID_NAME_USE *type)
655 {
656         struct winbind_cache *cache = get_cache(domain);
657         struct cache_entry *centry = NULL;
658         NTSTATUS status;
659         uint32 rid = 0;
660
661         sid_peek_rid(sid, &rid);
662
663         if (!cache->tdb) goto do_query;
664
665         centry = wcache_fetch(cache, domain, "SN/%s/%d", domain->name, rid);
666         if (!centry) goto do_query;
667         if (NT_STATUS_IS_OK(centry->status)) {
668                 *type = centry_uint32(centry);
669                 *name = centry_string(centry, mem_ctx);
670         }
671         status = centry->status;
672         centry_free(centry);
673         return status;
674
675 do_query:
676         *name = NULL;
677
678         if (wcache_server_down(domain)) {
679                 return NT_STATUS_SERVER_DISABLED;
680         }
681         status = cache->backend->sid_to_name(domain, mem_ctx, sid, name, type);
682
683         /* and save it */
684         refresh_sequence_number(domain, True);
685         wcache_save_sid_to_name(domain, status, sid, *name, *type, rid);
686         wcache_save_name_to_sid(domain, status, *name, sid, *type);
687
688         return status;
689 }
690
691
692 /* Lookup user information from a rid */
693 static NTSTATUS query_user(struct winbindd_domain *domain, 
694                            TALLOC_CTX *mem_ctx, 
695                            uint32 user_rid, 
696                            WINBIND_USERINFO *info)
697 {
698         struct winbind_cache *cache = get_cache(domain);
699         struct cache_entry *centry = NULL;
700         NTSTATUS status;
701
702         if (!cache->tdb) goto do_query;
703
704         centry = wcache_fetch(cache, domain, "U/%s/%x", domain->name, user_rid);
705         if (!centry) goto do_query;
706
707         info->acct_name = centry_string(centry, mem_ctx);
708         info->full_name = centry_string(centry, mem_ctx);
709         info->user_rid = centry_uint32(centry);
710         info->group_rid = centry_uint32(centry);
711         status = centry->status;
712         centry_free(centry);
713         return status;
714
715 do_query:
716         ZERO_STRUCTP(info);
717
718         if (wcache_server_down(domain)) {
719                 return NT_STATUS_SERVER_DISABLED;
720         }
721         
722         status = cache->backend->query_user(domain, mem_ctx, user_rid, info);
723
724         /* and save it */
725         refresh_sequence_number(domain, True);
726         wcache_save_user(domain, status, info);
727
728         return status;
729 }
730
731
732 /* Lookup groups a user is a member of. */
733 static NTSTATUS lookup_usergroups(struct winbindd_domain *domain,
734                                   TALLOC_CTX *mem_ctx,
735                                   uint32 user_rid, 
736                                   uint32 *num_groups, uint32 **user_gids)
737 {
738         struct winbind_cache *cache = get_cache(domain);
739         struct cache_entry *centry = NULL;
740         NTSTATUS status;
741         int i;
742
743         if (!cache->tdb) goto do_query;
744
745         centry = wcache_fetch(cache, domain, "UG/%s/%x", domain->name, user_rid);
746         if (!centry) goto do_query;
747
748         *num_groups = centry_uint32(centry);
749         
750         if (*num_groups == 0) goto do_cached;
751
752         (*user_gids) = talloc(mem_ctx, sizeof(**user_gids) * (*num_groups));
753         if (! (*user_gids)) smb_panic("lookup_usergroups out of memory");
754         for (i=0; i<(*num_groups); i++) {
755                 (*user_gids)[i] = centry_uint32(centry);
756         }
757
758 do_cached:      
759         status = centry->status;
760         centry_free(centry);
761         return status;
762
763 do_query:
764         (*num_groups) = 0;
765         (*user_gids) = NULL;
766
767         if (wcache_server_down(domain)) {
768                 return NT_STATUS_SERVER_DISABLED;
769         }
770         status = cache->backend->lookup_usergroups(domain, mem_ctx, user_rid, num_groups, user_gids);
771
772         /* and save it */
773         refresh_sequence_number(domain, True);
774         centry = centry_start(domain, status);
775         if (!centry) goto skip_save;
776         centry_put_uint32(centry, *num_groups);
777         for (i=0; i<(*num_groups); i++) {
778                 centry_put_uint32(centry, (*user_gids)[i]);
779         }       
780         centry_end(centry, "UG/%s/%x", domain->name, user_rid);
781         centry_free(centry);
782
783 skip_save:
784         return status;
785 }
786
787
788 static NTSTATUS lookup_groupmem(struct winbindd_domain *domain,
789                                 TALLOC_CTX *mem_ctx,
790                                 uint32 group_rid, uint32 *num_names, 
791                                 uint32 **rid_mem, char ***names, 
792                                 uint32 **name_types)
793 {
794         struct winbind_cache *cache = get_cache(domain);
795         struct cache_entry *centry = NULL;
796         NTSTATUS status;
797         int i;
798
799         if (!cache->tdb) goto do_query;
800
801         centry = wcache_fetch(cache, domain, "GM/%s/%x", domain->name, group_rid);
802         if (!centry) goto do_query;
803
804         *num_names = centry_uint32(centry);
805         
806         if (*num_names == 0) goto do_cached;
807
808         (*rid_mem) = talloc(mem_ctx, sizeof(**rid_mem) * (*num_names));
809         (*names) = talloc(mem_ctx, sizeof(**names) * (*num_names));
810         (*name_types) = talloc(mem_ctx, sizeof(**name_types) * (*num_names));
811
812         if (! (*rid_mem) || ! (*names) || ! (*name_types)) {
813                 smb_panic("lookup_groupmem out of memory");
814         }
815
816         for (i=0; i<(*num_names); i++) {
817                 (*rid_mem)[i] = centry_uint32(centry);
818                 (*names)[i] = centry_string(centry, mem_ctx);
819                 (*name_types)[i] = centry_uint32(centry);
820         }
821
822 do_cached:      
823         status = centry->status;
824         centry_free(centry);
825         return status;
826
827 do_query:
828         (*num_names) = 0;
829         (*rid_mem) = NULL;
830         (*names) = NULL;
831         (*name_types) = NULL;
832         
833
834         if (wcache_server_down(domain)) {
835                 return NT_STATUS_SERVER_DISABLED;
836         }
837         status = cache->backend->lookup_groupmem(domain, mem_ctx, group_rid, num_names, 
838                                                  rid_mem, names, name_types);
839
840         /* and save it */
841         refresh_sequence_number(domain, True);
842         centry = centry_start(domain, status);
843         if (!centry) goto skip_save;
844         centry_put_uint32(centry, *num_names);
845         for (i=0; i<(*num_names); i++) {
846                 centry_put_uint32(centry, (*rid_mem)[i]);
847                 centry_put_string(centry, (*names)[i]);
848                 centry_put_uint32(centry, (*name_types)[i]);
849         }       
850         centry_end(centry, "GM/%s/%x", domain->name, group_rid);
851         centry_free(centry);
852
853 skip_save:
854         return status;
855 }
856
857 /* find the sequence number for a domain */
858 static NTSTATUS sequence_number(struct winbindd_domain *domain, uint32 *seq)
859 {
860         refresh_sequence_number(domain, False);
861
862         *seq = domain->sequence_number;
863
864         return NT_STATUS_OK;
865 }
866
867 /* enumerate trusted domains */
868 static NTSTATUS trusted_domains(struct winbindd_domain *domain,
869                                 TALLOC_CTX *mem_ctx,
870                                 uint32 *num_domains,
871                                 char ***names,
872                                 DOM_SID **dom_sids)
873 {
874         struct winbind_cache *cache = get_cache(domain);
875
876         /* we don't cache this call */
877         return cache->backend->trusted_domains(domain, mem_ctx, num_domains, 
878                                                names, dom_sids);
879 }
880
881 /* find the domain sid */
882 static NTSTATUS domain_sid(struct winbindd_domain *domain, DOM_SID *sid)
883 {
884         struct winbind_cache *cache = get_cache(domain);
885
886         /* we don't cache this call */
887         return cache->backend->domain_sid(domain, sid);
888 }
889
890 /* the ADS backend methods are exposed via this structure */
891 struct winbindd_methods cache_methods = {
892         True,
893         query_user_list,
894         enum_dom_groups,
895         name_to_sid,
896         sid_to_name,
897         query_user,
898         lookup_usergroups,
899         lookup_groupmem,
900         sequence_number,
901         trusted_domains,
902         domain_sid
903 };