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