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