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