This commit was manufactured by cvs2svn to create branch 'SAMBA_3_0'.(This used to...
[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 #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 background_process;
305                         background_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", 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", domain->name);
603         centry_free(centry);
604
605 skip_save:
606         return status;
607 }
608
609 /* list all domain groups */
610 static NTSTATUS enum_local_groups(struct winbindd_domain *domain,
611                                 TALLOC_CTX *mem_ctx,
612                                 uint32 *num_entries, 
613                                 struct acct_info **info)
614 {
615         struct winbind_cache *cache = get_cache(domain);
616         struct cache_entry *centry = NULL;
617         NTSTATUS status;
618         int i;
619
620         if (!cache->tdb) goto do_query;
621
622         centry = wcache_fetch(cache, domain, "GL/%s/local", domain->name);
623         if (!centry) goto do_query;
624
625         *num_entries = centry_uint32(centry);
626         
627         if (*num_entries == 0) goto do_cached;
628
629         (*info) = talloc(mem_ctx, sizeof(**info) * (*num_entries));
630         if (! (*info)) smb_panic("enum_dom_groups out of memory");
631         for (i=0; i<(*num_entries); i++) {
632                 fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx));
633                 fstrcpy((*info)[i].acct_desc, centry_string(centry, mem_ctx));
634                 (*info)[i].rid = centry_uint32(centry);
635         }
636
637 do_cached:      
638
639         /* If we are returning cached data and the domain controller
640            is down then we don't know whether the data is up to date
641            or not.  Return NT_STATUS_MORE_PROCESSING_REQUIRED to
642            indicate this. */
643
644         if (wcache_server_down(domain)) {
645                 DEBUG(10, ("query_user_list: returning cached user list and server was down\n"));
646                 status = NT_STATUS_MORE_PROCESSING_REQUIRED;
647         } else
648                 status = centry->status;
649
650         centry_free(centry);
651         return status;
652
653 do_query:
654         *num_entries = 0;
655         *info = NULL;
656
657         if (wcache_server_down(domain)) {
658                 return NT_STATUS_SERVER_DISABLED;
659         }
660
661         status = cache->backend->enum_local_groups(domain, mem_ctx, num_entries, info);
662
663         /* and save it */
664         refresh_sequence_number(domain, True);
665         centry = centry_start(domain, status);
666         if (!centry) goto skip_save;
667         centry_put_uint32(centry, *num_entries);
668         for (i=0; i<(*num_entries); i++) {
669                 centry_put_string(centry, (*info)[i].acct_name);
670                 centry_put_string(centry, (*info)[i].acct_desc);
671                 centry_put_uint32(centry, (*info)[i].rid);
672         }       
673         centry_end(centry, "GL/%s/local", domain->name);
674         centry_free(centry);
675
676 skip_save:
677         return status;
678 }
679
680 /* convert a single name to a sid in a domain */
681 static NTSTATUS name_to_sid(struct winbindd_domain *domain,
682                             const char *name,
683                             DOM_SID *sid,
684                             enum SID_NAME_USE *type)
685 {
686         struct winbind_cache *cache = get_cache(domain);
687         struct cache_entry *centry = NULL;
688         NTSTATUS status;
689         fstring uname;
690
691         if (!cache->tdb) goto do_query;
692
693         fstrcpy(uname, name);
694         strupper(uname);
695         centry = wcache_fetch(cache, domain, "NS/%s/%s", domain->name, uname);
696         if (!centry) goto do_query;
697         *type = centry_uint32(centry);
698         sid_parse(centry->data + centry->ofs, centry->len - centry->ofs, sid);
699
700         status = centry->status;
701         centry_free(centry);
702         return status;
703
704 do_query:
705         ZERO_STRUCTP(sid);
706
707         if (wcache_server_down(domain)) {
708                 return NT_STATUS_SERVER_DISABLED;
709         }
710         status = cache->backend->name_to_sid(domain, name, sid, type);
711
712         /* and save it */
713         wcache_save_name_to_sid(domain, status, name, sid, *type);
714
715         /* We can't save the sid to name mapping as we don't know the
716            correct case of the name without looking it up */
717
718         return status;
719 }
720
721 /* convert a sid to a user or group name. The sid is guaranteed to be in the domain
722    given */
723 static NTSTATUS sid_to_name(struct winbindd_domain *domain,
724                             TALLOC_CTX *mem_ctx,
725                             DOM_SID *sid,
726                             char **name,
727                             enum SID_NAME_USE *type)
728 {
729         struct winbind_cache *cache = get_cache(domain);
730         struct cache_entry *centry = NULL;
731         NTSTATUS status;
732         uint32 rid = 0;
733
734         if (!sid_peek_check_rid(&domain->sid, sid, &rid))
735                 return NT_STATUS_INVALID_PARAMETER;
736
737         if (!cache->tdb) goto do_query;
738
739         centry = wcache_fetch(cache, domain, "SN/%s/%d", domain->name, rid);
740         if (!centry) goto do_query;
741         if (NT_STATUS_IS_OK(centry->status)) {
742                 *type = centry_uint32(centry);
743                 *name = centry_string(centry, mem_ctx);
744         }
745         status = centry->status;
746         centry_free(centry);
747         return status;
748
749 do_query:
750         *name = NULL;
751
752         if (wcache_server_down(domain)) {
753                 return NT_STATUS_SERVER_DISABLED;
754         }
755         status = cache->backend->sid_to_name(domain, mem_ctx, sid, name, type);
756
757         /* and save it */
758         refresh_sequence_number(domain, True);
759         wcache_save_sid_to_name(domain, status, sid, *name, *type, rid);
760         wcache_save_name_to_sid(domain, status, *name, sid, *type);
761
762         return status;
763 }
764
765
766 /* Lookup user information from a rid */
767 static NTSTATUS query_user(struct winbindd_domain *domain, 
768                            TALLOC_CTX *mem_ctx, 
769                            uint32 user_rid, 
770                            WINBIND_USERINFO *info)
771 {
772         struct winbind_cache *cache = get_cache(domain);
773         struct cache_entry *centry = NULL;
774         NTSTATUS status;
775
776         if (!cache->tdb) goto do_query;
777
778         centry = wcache_fetch(cache, domain, "U/%s/%d", domain->name, user_rid);
779         if (!centry) goto do_query;
780
781         info->acct_name = centry_string(centry, mem_ctx);
782         info->full_name = centry_string(centry, mem_ctx);
783         info->user_rid = centry_uint32(centry);
784         info->group_rid = centry_uint32(centry);
785         status = centry->status;
786         centry_free(centry);
787         return status;
788
789 do_query:
790         ZERO_STRUCTP(info);
791
792         if (wcache_server_down(domain)) {
793                 return NT_STATUS_SERVER_DISABLED;
794         }
795         
796         status = cache->backend->query_user(domain, mem_ctx, user_rid, info);
797
798         /* and save it */
799         refresh_sequence_number(domain, True);
800         wcache_save_user(domain, status, info);
801
802         return status;
803 }
804
805
806 /* Lookup groups a user is a member of. */
807 static NTSTATUS lookup_usergroups(struct winbindd_domain *domain,
808                                   TALLOC_CTX *mem_ctx,
809                                   uint32 user_rid, 
810                                   uint32 *num_groups, uint32 **user_gids)
811 {
812         struct winbind_cache *cache = get_cache(domain);
813         struct cache_entry *centry = NULL;
814         NTSTATUS status;
815         int i;
816
817         if (!cache->tdb) goto do_query;
818
819         centry = wcache_fetch(cache, domain, "UG/%s/%d", domain->name, user_rid);
820         if (!centry) goto do_query;
821
822         *num_groups = centry_uint32(centry);
823         
824         if (*num_groups == 0) goto do_cached;
825
826         (*user_gids) = talloc(mem_ctx, sizeof(**user_gids) * (*num_groups));
827         if (! (*user_gids)) smb_panic("lookup_usergroups out of memory");
828         for (i=0; i<(*num_groups); i++) {
829                 (*user_gids)[i] = centry_uint32(centry);
830         }
831
832 do_cached:      
833         status = centry->status;
834         centry_free(centry);
835         return status;
836
837 do_query:
838         (*num_groups) = 0;
839         (*user_gids) = NULL;
840
841         if (wcache_server_down(domain)) {
842                 return NT_STATUS_SERVER_DISABLED;
843         }
844         status = cache->backend->lookup_usergroups(domain, mem_ctx, user_rid, num_groups, user_gids);
845
846         /* and save it */
847         refresh_sequence_number(domain, True);
848         centry = centry_start(domain, status);
849         if (!centry) goto skip_save;
850         centry_put_uint32(centry, *num_groups);
851         for (i=0; i<(*num_groups); i++) {
852                 centry_put_uint32(centry, (*user_gids)[i]);
853         }       
854         centry_end(centry, "UG/%s/%d", domain->name, user_rid);
855         centry_free(centry);
856
857 skip_save:
858         return status;
859 }
860
861
862 static NTSTATUS lookup_groupmem(struct winbindd_domain *domain,
863                                 TALLOC_CTX *mem_ctx,
864                                 uint32 group_rid, uint32 *num_names, 
865                                 uint32 **rid_mem, char ***names, 
866                                 uint32 **name_types)
867 {
868         struct winbind_cache *cache = get_cache(domain);
869         struct cache_entry *centry = NULL;
870         NTSTATUS status;
871         int i;
872
873         if (!cache->tdb) goto do_query;
874
875         centry = wcache_fetch(cache, domain, "GM/%s/%d", domain->name, group_rid);
876         if (!centry) goto do_query;
877
878         *num_names = centry_uint32(centry);
879         
880         if (*num_names == 0) goto do_cached;
881
882         (*rid_mem) = talloc(mem_ctx, sizeof(**rid_mem) * (*num_names));
883         (*names) = talloc(mem_ctx, sizeof(**names) * (*num_names));
884         (*name_types) = talloc(mem_ctx, sizeof(**name_types) * (*num_names));
885
886         if (! (*rid_mem) || ! (*names) || ! (*name_types)) {
887                 smb_panic("lookup_groupmem out of memory");
888         }
889
890         for (i=0; i<(*num_names); i++) {
891                 (*rid_mem)[i] = centry_uint32(centry);
892                 (*names)[i] = centry_string(centry, mem_ctx);
893                 (*name_types)[i] = centry_uint32(centry);
894         }
895
896 do_cached:      
897         status = centry->status;
898         centry_free(centry);
899         return status;
900
901 do_query:
902         (*num_names) = 0;
903         (*rid_mem) = NULL;
904         (*names) = NULL;
905         (*name_types) = NULL;
906         
907
908         if (wcache_server_down(domain)) {
909                 return NT_STATUS_SERVER_DISABLED;
910         }
911         status = cache->backend->lookup_groupmem(domain, mem_ctx, group_rid, num_names, 
912                                                  rid_mem, names, name_types);
913
914         /* and save it */
915         refresh_sequence_number(domain, True);
916         centry = centry_start(domain, status);
917         if (!centry) goto skip_save;
918         centry_put_uint32(centry, *num_names);
919         for (i=0; i<(*num_names); i++) {
920                 centry_put_uint32(centry, (*rid_mem)[i]);
921                 centry_put_string(centry, (*names)[i]);
922                 centry_put_uint32(centry, (*name_types)[i]);
923         }       
924         centry_end(centry, "GM/%s/%d", domain->name, group_rid);
925         centry_free(centry);
926
927 skip_save:
928         return status;
929 }
930
931 /* find the sequence number for a domain */
932 static NTSTATUS sequence_number(struct winbindd_domain *domain, uint32 *seq)
933 {
934         refresh_sequence_number(domain, False);
935
936         *seq = domain->sequence_number;
937
938         return NT_STATUS_OK;
939 }
940
941 /* enumerate trusted domains */
942 static NTSTATUS trusted_domains(struct winbindd_domain *domain,
943                                 TALLOC_CTX *mem_ctx,
944                                 uint32 *num_domains,
945                                 char ***names,
946                                 char ***alt_names,
947                                 DOM_SID **dom_sids)
948 {
949         struct winbind_cache *cache = get_cache(domain);
950
951         /* we don't cache this call */
952         return cache->backend->trusted_domains(domain, mem_ctx, num_domains, 
953                                                names, alt_names, dom_sids);
954 }
955
956 /* find the domain sid */
957 static NTSTATUS domain_sid(struct winbindd_domain *domain, DOM_SID *sid)
958 {
959         struct winbind_cache *cache = get_cache(domain);
960
961         /* we don't cache this call */
962         return cache->backend->domain_sid(domain, sid);
963 }
964
965 /* find the alternate names for the domain, if any */
966 static NTSTATUS alternate_name(struct winbindd_domain *domain)
967 {
968         struct winbind_cache *cache = get_cache(domain);
969
970         /* we don't cache this call */
971         return cache->backend->alternate_name(domain);
972 }
973
974 /* the ADS backend methods are exposed via this structure */
975 struct winbindd_methods cache_methods = {
976         True,
977         query_user_list,
978         enum_dom_groups,
979         enum_local_groups,
980         name_to_sid,
981         sid_to_name,
982         query_user,
983         lookup_usergroups,
984         lookup_groupmem,
985         sequence_number,
986         trusted_domains,
987         domain_sid,
988         alternate_name
989 };