i guess i'm the only one this ever annyoed...
[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    Copyright (C) Gerald Carter   2003
8    
9    
10    This program is free software; you can redistribute it and/or modify
11    it under the terms of the GNU General Public License as published by
12    the Free Software Foundation; either version 2 of the License, or
13    (at your option) any later version.
14    
15    This program is distributed in the hope that it will be useful,
16    but WITHOUT ANY WARRANTY; without even the implied warranty of
17    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18    GNU General Public License for more details.
19    
20    You should have received a copy of the GNU General Public License
21    along with this program; if not, write to the Free Software
22    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23 */
24
25 #include "winbindd.h"
26
27 #undef DBGC_CLASS
28 #define DBGC_CLASS DBGC_WINBIND
29
30 struct winbind_cache {
31         TDB_CONTEXT *tdb;
32 };
33
34 struct cache_entry {
35         NTSTATUS status;
36         uint32 sequence_number;
37         uint8 *data;
38         uint32 len, ofs;
39 };
40
41 #define WINBINDD_MAX_CACHE_SIZE (50*1024*1024)
42
43 static struct winbind_cache *wcache;
44
45 /* flush the cache */
46 void wcache_flush_cache(void)
47 {
48         extern BOOL opt_nocache;
49
50         if (!wcache)
51                 return;
52         if (wcache->tdb) {
53                 tdb_close(wcache->tdb);
54                 wcache->tdb = NULL;
55         }
56         if (opt_nocache)
57                 return;
58
59         wcache->tdb = tdb_open_log(lock_path("winbindd_cache.tdb"), 5000, 
60                                    TDB_CLEAR_IF_FIRST, O_RDWR|O_CREAT, 0600);
61
62         if (!wcache->tdb) {
63                 DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
64         }
65         DEBUG(10,("wcache_flush_cache success\n"));
66 }
67
68 void winbindd_check_cache_size(time_t t)
69 {
70         static time_t last_check_time;
71         struct stat st;
72
73         if (last_check_time == (time_t)0)
74                 last_check_time = t;
75
76         if (t - last_check_time < 60 && t - last_check_time > 0)
77                 return;
78
79         if (wcache == NULL || wcache->tdb == NULL) {
80                 DEBUG(0, ("Unable to check size of tdb cache - cache not open !\n"));
81                 return;
82         }
83
84         if (fstat(wcache->tdb->fd, &st) == -1) {
85                 DEBUG(0, ("Unable to check size of tdb cache %s!\n", strerror(errno) ));
86                 return;
87         }
88
89         if (st.st_size > WINBINDD_MAX_CACHE_SIZE) {
90                 DEBUG(10,("flushing cache due to size (%lu) > (%lu)\n",
91                         (unsigned long)st.st_size,
92                         (unsigned long)WINBINDD_MAX_CACHE_SIZE));
93                 wcache_flush_cache();
94         }
95 }
96
97 /* get the winbind_cache structure */
98 static struct winbind_cache *get_cache(struct winbindd_domain *domain)
99 {
100         struct winbind_cache *ret = wcache;
101
102         if (!domain->backend) {
103                 extern struct winbindd_methods msrpc_methods;
104                 switch (lp_security()) {
105 #ifdef HAVE_ADS
106                 case SEC_ADS: {
107                         extern struct winbindd_methods ads_methods;
108                         /* always obey the lp_security parameter for our domain */
109                         if ( strequal(lp_realm(), domain->alt_name) ) {
110                                 domain->backend = &ads_methods;
111                                 break;
112                         }
113
114                         if ( domain->native_mode ) {
115                                 domain->backend = &ads_methods;
116                                 break;
117                         }
118
119                         /* fall through */
120                 }       
121 #endif
122                 default:
123                         domain->backend = &msrpc_methods;
124                 }
125         }
126
127         if (ret)
128                 return ret;
129         
130         ret = smb_xmalloc(sizeof(*ret));
131         ZERO_STRUCTP(ret);
132
133         wcache = ret;
134         wcache_flush_cache();
135
136         return ret;
137 }
138
139 /*
140   free a centry structure
141 */
142 static void centry_free(struct cache_entry *centry)
143 {
144         if (!centry)
145                 return;
146         SAFE_FREE(centry->data);
147         free(centry);
148 }
149
150 /*
151   pull a uint32 from a cache entry 
152 */
153 static uint32 centry_uint32(struct cache_entry *centry)
154 {
155         uint32 ret;
156         if (centry->len - centry->ofs < 4) {
157                 DEBUG(0,("centry corruption? needed 4 bytes, have %d\n", 
158                          centry->len - centry->ofs));
159                 smb_panic("centry_uint32");
160         }
161         ret = IVAL(centry->data, centry->ofs);
162         centry->ofs += 4;
163         return ret;
164 }
165
166 /*
167   pull a uint8 from a cache entry 
168 */
169 static uint8 centry_uint8(struct cache_entry *centry)
170 {
171         uint8 ret;
172         if (centry->len - centry->ofs < 1) {
173                 DEBUG(0,("centry corruption? needed 1 bytes, have %d\n", 
174                          centry->len - centry->ofs));
175                 smb_panic("centry_uint32");
176         }
177         ret = CVAL(centry->data, centry->ofs);
178         centry->ofs += 1;
179         return ret;
180 }
181
182 /* pull a string from a cache entry, using the supplied
183    talloc context 
184 */
185 static char *centry_string(struct cache_entry *centry, TALLOC_CTX *mem_ctx)
186 {
187         uint32 len;
188         char *ret;
189
190         len = centry_uint8(centry);
191
192         if (len == 0xFF) {
193                 /* a deliberate NULL string */
194                 return NULL;
195         }
196
197         if (centry->len - centry->ofs < len) {
198                 DEBUG(0,("centry corruption? needed %d bytes, have %d\n", 
199                          len, centry->len - centry->ofs));
200                 smb_panic("centry_string");
201         }
202
203         ret = talloc(mem_ctx, len+1);
204         if (!ret) {
205                 smb_panic("centry_string out of memory\n");
206         }
207         memcpy(ret,centry->data + centry->ofs, len);
208         ret[len] = 0;
209         centry->ofs += len;
210         return ret;
211 }
212
213 /* pull a string from a cache entry, using the supplied
214    talloc context 
215 */
216 static DOM_SID *centry_sid(struct cache_entry *centry, TALLOC_CTX *mem_ctx)
217 {
218         DOM_SID *sid;
219         char *sid_string;
220
221         sid = talloc(mem_ctx, sizeof(*sid));
222         if (!sid)
223                 return NULL;
224         
225         sid_string = centry_string(centry, mem_ctx);
226         if (!string_to_sid(sid, sid_string)) {
227                 return NULL;
228         }
229         return sid;
230 }
231
232 /* the server is considered down if it can't give us a sequence number */
233 static BOOL wcache_server_down(struct winbindd_domain *domain)
234 {
235         BOOL ret;
236
237         if (!wcache->tdb)
238                 return False;
239
240         ret = (domain->sequence_number == DOM_SEQUENCE_NONE);
241
242         if (ret)
243                 DEBUG(10,("wcache_server_down: server for Domain %s down\n", 
244                         domain->name ));
245         return ret;
246 }
247
248 static NTSTATUS fetch_cache_seqnum( struct winbindd_domain *domain, time_t now )
249 {
250         TDB_DATA data;
251         fstring key;
252         uint32 time_diff;
253         
254         if (!wcache->tdb) {
255                 DEBUG(10,("fetch_cache_seqnum: tdb == NULL\n"));
256                 return NT_STATUS_UNSUCCESSFUL;
257         }
258                 
259         snprintf( key, sizeof(key), "SEQNUM/%s", domain->name );
260         
261         data = tdb_fetch_bystring( wcache->tdb, key );
262         if ( !data.dptr || data.dsize!=8 ) {
263                 DEBUG(10,("fetch_cache_seqnum: invalid data size key [%s]\n", key ));
264                 return NT_STATUS_UNSUCCESSFUL;
265         }
266         
267         domain->sequence_number = IVAL(data.dptr, 0);
268         domain->last_seq_check  = IVAL(data.dptr, 4);
269         
270         /* have we expired? */
271         
272         time_diff = now - domain->last_seq_check;
273         if ( time_diff > lp_winbind_cache_time() ) {
274                 DEBUG(10,("fetch_cache_seqnum: timeout [%s][%u @ %u]\n",
275                         domain->name, domain->sequence_number,
276                         (uint32)domain->last_seq_check));
277                 return NT_STATUS_UNSUCCESSFUL;
278         }
279
280         DEBUG(10,("fetch_cache_seqnum: success [%s][%u @ %u]\n", 
281                 domain->name, domain->sequence_number, 
282                 (uint32)domain->last_seq_check));
283
284         return NT_STATUS_OK;
285 }
286
287 static NTSTATUS store_cache_seqnum( struct winbindd_domain *domain )
288 {
289         TDB_DATA data, key;
290         fstring key_str;
291         char buf[8];
292         
293         if (!wcache->tdb) {
294                 DEBUG(10,("store_cache_seqnum: tdb == NULL\n"));
295                 return NT_STATUS_UNSUCCESSFUL;
296         }
297                 
298         snprintf( key_str, sizeof(key_str), "SEQNUM/%s", domain->name );
299         key.dptr = key_str;
300         key.dsize = strlen(key_str)+1;
301         
302         SIVAL(buf, 0, domain->sequence_number);
303         SIVAL(buf, 4, domain->last_seq_check);
304         data.dptr = buf;
305         data.dsize = 8;
306         
307         if ( tdb_store( wcache->tdb, key, data, TDB_REPLACE) == -1 ) {
308                 DEBUG(10,("store_cache_seqnum: tdb_store fail key [%s]\n", key_str ));
309                 return NT_STATUS_UNSUCCESSFUL;
310         }
311
312         DEBUG(10,("store_cache_seqnum: success [%s][%u @ %u]\n", 
313                 domain->name, domain->sequence_number, 
314                 (uint32)domain->last_seq_check));
315         
316         return NT_STATUS_OK;
317 }
318
319 /*
320   refresh the domain sequence number. If force is True
321   then always refresh it, no matter how recently we fetched it
322 */
323
324 static void refresh_sequence_number(struct winbindd_domain *domain, BOOL force)
325 {
326         NTSTATUS status;
327         unsigned time_diff;
328         time_t t = time(NULL);
329         unsigned cache_time = lp_winbind_cache_time();
330
331         /* trying to reconnect is expensive, don't do it too often */
332         if (domain->sequence_number == DOM_SEQUENCE_NONE) {
333                 cache_time *= 8;
334         }
335
336         time_diff = t - domain->last_seq_check;
337
338         /* see if we have to refetch the domain sequence number */
339         if (!force && (time_diff < cache_time)) {
340                 DEBUG(10, ("refresh_sequence_number: %s time ok\n", domain->name));
341                 goto done;
342         }
343         
344         /* try to get the sequence number from the tdb cache first */
345         /* this will update the timestamp as well */
346         
347         status = fetch_cache_seqnum( domain, t );
348         if ( NT_STATUS_IS_OK(status) )
349                 goto done;      
350
351         status = domain->backend->sequence_number(domain, &domain->sequence_number);
352
353         if (!NT_STATUS_IS_OK(status)) {
354                 domain->sequence_number = DOM_SEQUENCE_NONE;
355         }
356         
357         domain->last_status = status;
358         domain->last_seq_check = time(NULL);
359         
360         /* save the new sequence number ni the cache */
361         store_cache_seqnum( domain );
362
363 done:
364         DEBUG(10, ("refresh_sequence_number: %s seq number is now %d\n", 
365                    domain->name, domain->sequence_number));
366
367         return;
368 }
369
370 /*
371   decide if a cache entry has expired
372 */
373 static BOOL centry_expired(struct winbindd_domain *domain, const char *keystr, struct cache_entry *centry)
374 {
375         /* if the server is OK and our cache entry came from when it was down then
376            the entry is invalid */
377         if (domain->sequence_number != DOM_SEQUENCE_NONE && 
378             centry->sequence_number == DOM_SEQUENCE_NONE) {
379                 DEBUG(10,("centry_expired: Key %s for domain %s invalid sequence.\n",
380                         keystr, domain->name ));
381                 return True;
382         }
383
384         /* if the server is down or the cache entry is not older than the
385            current sequence number then it is OK */
386         if (wcache_server_down(domain) || 
387             centry->sequence_number == domain->sequence_number) {
388                 DEBUG(10,("centry_expired: Key %s for domain %s is good.\n",
389                         keystr, domain->name ));
390                 return False;
391         }
392
393         DEBUG(10,("centry_expired: Key %s for domain %s expired\n",
394                 keystr, domain->name ));
395
396         /* it's expired */
397         return True;
398 }
399
400 /*
401   fetch an entry from the cache, with a varargs key. auto-fetch the sequence
402   number and return status
403 */
404 static struct cache_entry *wcache_fetch(struct winbind_cache *cache, 
405                                         struct winbindd_domain *domain,
406                                         const char *format, ...) PRINTF_ATTRIBUTE(3,4);
407 static struct cache_entry *wcache_fetch(struct winbind_cache *cache, 
408                                         struct winbindd_domain *domain,
409                                         const char *format, ...)
410 {
411         va_list ap;
412         char *kstr;
413         TDB_DATA data;
414         struct cache_entry *centry;
415         TDB_DATA key;
416
417         refresh_sequence_number(domain, False);
418
419         va_start(ap, format);
420         smb_xvasprintf(&kstr, format, ap);
421         va_end(ap);
422         
423         key.dptr = kstr;
424         key.dsize = strlen(kstr);
425         data = tdb_fetch(wcache->tdb, key);
426         if (!data.dptr) {
427                 /* a cache miss */
428                 free(kstr);
429                 return NULL;
430         }
431
432         centry = smb_xmalloc(sizeof(*centry));
433         centry->data = data.dptr;
434         centry->len = data.dsize;
435         centry->ofs = 0;
436
437         if (centry->len < 8) {
438                 /* huh? corrupt cache? */
439                 DEBUG(10,("wcache_fetch: Corrupt cache for key %s domain %s (len < 8) ?\n",
440                         kstr, domain->name ));
441                 centry_free(centry);
442                 free(kstr);
443                 return NULL;
444         }
445         
446         centry->status = NT_STATUS(centry_uint32(centry));
447         centry->sequence_number = centry_uint32(centry);
448
449         if (centry_expired(domain, kstr, centry)) {
450                 extern BOOL opt_dual_daemon;
451
452                 DEBUG(10,("wcache_fetch: entry %s expired for domain %s\n",
453                          kstr, domain->name ));
454
455                 if (opt_dual_daemon) {
456                         extern BOOL background_process;
457                         background_process = True;
458                         DEBUG(10,("wcache_fetch: background processing expired entry %s for domain %s\n",
459                                  kstr, domain->name ));
460                 } else {
461                         centry_free(centry);
462                         free(kstr);
463                         return NULL;
464                 }
465         }
466
467         DEBUG(10,("wcache_fetch: returning entry %s for domain %s\n",
468                  kstr, domain->name ));
469
470         free(kstr);
471         return centry;
472 }
473
474 /*
475   make sure we have at least len bytes available in a centry 
476 */
477 static void centry_expand(struct cache_entry *centry, uint32 len)
478 {
479         uint8 *p;
480         if (centry->len - centry->ofs >= len)
481                 return;
482         centry->len *= 2;
483         p = realloc(centry->data, centry->len);
484         if (!p) {
485                 DEBUG(0,("out of memory: needed %d bytes in centry_expand\n", centry->len));
486                 smb_panic("out of memory in centry_expand");
487         }
488         centry->data = p;
489 }
490
491 /*
492   push a uint32 into a centry 
493 */
494 static void centry_put_uint32(struct cache_entry *centry, uint32 v)
495 {
496         centry_expand(centry, 4);
497         SIVAL(centry->data, centry->ofs, v);
498         centry->ofs += 4;
499 }
500
501 /*
502   push a uint8 into a centry 
503 */
504 static void centry_put_uint8(struct cache_entry *centry, uint8 v)
505 {
506         centry_expand(centry, 1);
507         SCVAL(centry->data, centry->ofs, v);
508         centry->ofs += 1;
509 }
510
511 /* 
512    push a string into a centry 
513  */
514 static void centry_put_string(struct cache_entry *centry, const char *s)
515 {
516         int len;
517
518         if (!s) {
519                 /* null strings are marked as len 0xFFFF */
520                 centry_put_uint8(centry, 0xFF);
521                 return;
522         }
523
524         len = strlen(s);
525         /* can't handle more than 254 char strings. Truncating is probably best */
526         if (len > 254)
527                 len = 254;
528         centry_put_uint8(centry, len);
529         centry_expand(centry, len);
530         memcpy(centry->data + centry->ofs, s, len);
531         centry->ofs += len;
532 }
533
534 static void centry_put_sid(struct cache_entry *centry, const DOM_SID *sid) 
535 {
536         fstring sid_string;
537         centry_put_string(centry, sid_to_string(sid_string, sid));
538 }
539
540 /*
541   start a centry for output. When finished, call centry_end()
542 */
543 struct cache_entry *centry_start(struct winbindd_domain *domain, NTSTATUS status)
544 {
545         struct cache_entry *centry;
546
547         if (!wcache->tdb)
548                 return NULL;
549
550         centry = smb_xmalloc(sizeof(*centry));
551
552         centry->len = 8192; /* reasonable default */
553         centry->data = smb_xmalloc(centry->len);
554         centry->ofs = 0;
555         centry->sequence_number = domain->sequence_number;
556         centry_put_uint32(centry, NT_STATUS_V(status));
557         centry_put_uint32(centry, centry->sequence_number);
558         return centry;
559 }
560
561 /*
562   finish a centry and write it to the tdb
563 */
564 static void centry_end(struct cache_entry *centry, const char *format, ...) PRINTF_ATTRIBUTE(2,3);
565 static void centry_end(struct cache_entry *centry, const char *format, ...)
566 {
567         va_list ap;
568         char *kstr;
569         TDB_DATA key, data;
570
571         va_start(ap, format);
572         smb_xvasprintf(&kstr, format, ap);
573         va_end(ap);
574
575         key.dptr = kstr;
576         key.dsize = strlen(kstr);
577         data.dptr = centry->data;
578         data.dsize = centry->ofs;
579
580         tdb_store(wcache->tdb, key, data, TDB_REPLACE);
581         free(kstr);
582 }
583
584 static void wcache_save_name_to_sid(struct winbindd_domain *domain, 
585                                     NTSTATUS status, 
586                                     const char *name, DOM_SID *sid, 
587                                     enum SID_NAME_USE type)
588 {
589         struct cache_entry *centry;
590         fstring uname;
591         fstring sid_string;
592
593         centry = centry_start(domain, status);
594         if (!centry)
595                 return;
596         centry_put_sid(centry, sid);
597         fstrcpy(uname, name);
598         strupper_m(uname);
599         centry_end(centry, "NS/%s", sid_to_string(sid_string, sid));
600         DEBUG(10,("wcache_save_name_to_sid: %s -> %s\n", uname, sid_string));
601         centry_free(centry);
602 }
603
604 static void wcache_save_sid_to_name(struct winbindd_domain *domain, NTSTATUS status, 
605                                     DOM_SID *sid, const char *name, enum SID_NAME_USE type)
606 {
607         struct cache_entry *centry;
608         fstring sid_string;
609
610         centry = centry_start(domain, status);
611         if (!centry)
612                 return;
613         if (NT_STATUS_IS_OK(status)) {
614                 centry_put_uint32(centry, type);
615                 centry_put_string(centry, name);
616         }
617         centry_end(centry, "SN/%s", sid_to_string(sid_string, sid));
618         DEBUG(10,("wcache_save_sid_to_name: %s -> %s\n", sid_string, name));
619         centry_free(centry);
620 }
621
622
623 static void wcache_save_user(struct winbindd_domain *domain, NTSTATUS status, WINBIND_USERINFO *info)
624 {
625         struct cache_entry *centry;
626         fstring sid_string;
627
628         centry = centry_start(domain, status);
629         if (!centry)
630                 return;
631         centry_put_string(centry, info->acct_name);
632         centry_put_string(centry, info->full_name);
633         centry_put_sid(centry, info->user_sid);
634         centry_put_sid(centry, info->group_sid);
635         centry_end(centry, "U/%s", sid_to_string(sid_string, info->user_sid));
636         DEBUG(10,("wcache_save_user: %s (acct_name %s)\n", sid_string, info->acct_name));
637         centry_free(centry);
638 }
639
640
641 /* Query display info. This is the basic user list fn */
642 static NTSTATUS query_user_list(struct winbindd_domain *domain,
643                                 TALLOC_CTX *mem_ctx,
644                                 uint32 *num_entries, 
645                                 WINBIND_USERINFO **info)
646 {
647         struct winbind_cache *cache = get_cache(domain);
648         struct cache_entry *centry = NULL;
649         NTSTATUS status;
650         unsigned int i, retry;
651
652         if (!cache->tdb)
653                 goto do_query;
654
655         centry = wcache_fetch(cache, domain, "UL/%s", domain->name);
656         if (!centry)
657                 goto do_query;
658
659         *num_entries = centry_uint32(centry);
660         
661         if (*num_entries == 0)
662                 goto do_cached;
663
664         (*info) = talloc(mem_ctx, sizeof(**info) * (*num_entries));
665         if (! (*info))
666                 smb_panic("query_user_list out of memory");
667         for (i=0; i<(*num_entries); i++) {
668                 (*info)[i].acct_name = centry_string(centry, mem_ctx);
669                 (*info)[i].full_name = centry_string(centry, mem_ctx);
670                 (*info)[i].user_sid = centry_sid(centry, mem_ctx);
671                 (*info)[i].group_sid = centry_sid(centry, mem_ctx);
672         }
673
674 do_cached:      
675         status = centry->status;
676
677         DEBUG(10,("query_user_list: [Cached] - cached list for domain %s status %s\n",
678                 domain->name, get_friendly_nt_error_msg(status) ));
679
680         centry_free(centry);
681         return status;
682
683 do_query:
684         *num_entries = 0;
685         *info = NULL;
686
687         /* Return status value returned by seq number check */
688
689         if (!NT_STATUS_IS_OK(domain->last_status))
690                 return domain->last_status;
691
692         /* Put the query_user_list() in a retry loop.  There appears to be
693          * some bug either with Windows 2000 or Samba's handling of large
694          * rpc replies.  This manifests itself as sudden disconnection
695          * at a random point in the enumeration of a large (60k) user list.
696          * The retry loop simply tries the operation again. )-:  It's not
697          * pretty but an acceptable workaround until we work out what the
698          * real problem is. */
699
700         retry = 0;
701         do {
702
703                 DEBUG(10,("query_user_list: [Cached] - doing backend query for list for domain %s\n",
704                         domain->name ));
705
706                 status = domain->backend->query_user_list(domain, mem_ctx, num_entries, info);
707                 if (!NT_STATUS_IS_OK(status))
708                         DEBUG(3, ("query_user_list: returned 0x%08x, retrying\n", NT_STATUS_V(status)));
709                         if (NT_STATUS_V(status) == NT_STATUS_V(NT_STATUS_UNSUCCESSFUL)) {
710                                 DEBUG(3, ("query_user_list: flushing connection cache\n"));
711                                 winbindd_cm_flush();
712                         }
713
714         } while (NT_STATUS_V(status) == NT_STATUS_V(NT_STATUS_UNSUCCESSFUL) && 
715                  (retry++ < 5));
716
717         /* and save it */
718         refresh_sequence_number(domain, False);
719         centry = centry_start(domain, status);
720         if (!centry)
721                 goto skip_save;
722         centry_put_uint32(centry, *num_entries);
723         for (i=0; i<(*num_entries); i++) {
724                 centry_put_string(centry, (*info)[i].acct_name);
725                 centry_put_string(centry, (*info)[i].full_name);
726                 centry_put_sid(centry, (*info)[i].user_sid);
727                 centry_put_sid(centry, (*info)[i].group_sid);
728                 if (domain->backend->consistent) {
729                         /* when the backend is consistent we can pre-prime some mappings */
730                         wcache_save_name_to_sid(domain, NT_STATUS_OK, 
731                                                 (*info)[i].acct_name, 
732                                                 (*info)[i].user_sid,
733                                                 SID_NAME_USER);
734                         wcache_save_sid_to_name(domain, NT_STATUS_OK, 
735                                                 (*info)[i].user_sid,
736                                                 (*info)[i].acct_name, 
737                                                 SID_NAME_USER);
738                         wcache_save_user(domain, NT_STATUS_OK, &(*info)[i]);
739                 }
740         }       
741         centry_end(centry, "UL/%s", domain->name);
742         centry_free(centry);
743
744 skip_save:
745         return status;
746 }
747
748 /* list all domain groups */
749 static NTSTATUS enum_dom_groups(struct winbindd_domain *domain,
750                                 TALLOC_CTX *mem_ctx,
751                                 uint32 *num_entries, 
752                                 struct acct_info **info)
753 {
754         struct winbind_cache *cache = get_cache(domain);
755         struct cache_entry *centry = NULL;
756         NTSTATUS status;
757         unsigned int i;
758
759         if (!cache->tdb)
760                 goto do_query;
761
762         centry = wcache_fetch(cache, domain, "GL/%s/domain", domain->name);
763         if (!centry)
764                 goto do_query;
765
766         *num_entries = centry_uint32(centry);
767         
768         if (*num_entries == 0)
769                 goto do_cached;
770
771         (*info) = talloc(mem_ctx, sizeof(**info) * (*num_entries));
772         if (! (*info))
773                 smb_panic("enum_dom_groups out of memory");
774         for (i=0; i<(*num_entries); i++) {
775                 fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx));
776                 fstrcpy((*info)[i].acct_desc, centry_string(centry, mem_ctx));
777                 (*info)[i].rid = centry_uint32(centry);
778         }
779
780 do_cached:      
781         status = centry->status;
782
783         DEBUG(10,("enum_dom_groups: [Cached] - cached list for domain %s status %s\n",
784                 domain->name, get_friendly_nt_error_msg(status) ));
785
786         centry_free(centry);
787         return status;
788
789 do_query:
790         *num_entries = 0;
791         *info = NULL;
792
793         /* Return status value returned by seq number check */
794
795         if (!NT_STATUS_IS_OK(domain->last_status))
796                 return domain->last_status;
797
798         DEBUG(10,("enum_dom_groups: [Cached] - doing backend query for list for domain %s\n",
799                 domain->name ));
800
801         status = domain->backend->enum_dom_groups(domain, mem_ctx, num_entries, info);
802
803         /* and save it */
804         refresh_sequence_number(domain, False);
805         centry = centry_start(domain, status);
806         if (!centry)
807                 goto skip_save;
808         centry_put_uint32(centry, *num_entries);
809         for (i=0; i<(*num_entries); i++) {
810                 centry_put_string(centry, (*info)[i].acct_name);
811                 centry_put_string(centry, (*info)[i].acct_desc);
812                 centry_put_uint32(centry, (*info)[i].rid);
813         }       
814         centry_end(centry, "GL/%s/domain", domain->name);
815         centry_free(centry);
816
817 skip_save:
818         return status;
819 }
820
821 /* list all domain groups */
822 static NTSTATUS enum_local_groups(struct winbindd_domain *domain,
823                                 TALLOC_CTX *mem_ctx,
824                                 uint32 *num_entries, 
825                                 struct acct_info **info)
826 {
827         struct winbind_cache *cache = get_cache(domain);
828         struct cache_entry *centry = NULL;
829         NTSTATUS status;
830         unsigned int i;
831
832         if (!cache->tdb)
833                 goto do_query;
834
835         centry = wcache_fetch(cache, domain, "GL/%s/local", domain->name);
836         if (!centry)
837                 goto do_query;
838
839         *num_entries = centry_uint32(centry);
840         
841         if (*num_entries == 0)
842                 goto do_cached;
843
844         (*info) = talloc(mem_ctx, sizeof(**info) * (*num_entries));
845         if (! (*info))
846                 smb_panic("enum_dom_groups out of memory");
847         for (i=0; i<(*num_entries); i++) {
848                 fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx));
849                 fstrcpy((*info)[i].acct_desc, centry_string(centry, mem_ctx));
850                 (*info)[i].rid = centry_uint32(centry);
851         }
852
853 do_cached:      
854
855         /* If we are returning cached data and the domain controller
856            is down then we don't know whether the data is up to date
857            or not.  Return NT_STATUS_MORE_PROCESSING_REQUIRED to
858            indicate this. */
859
860         if (wcache_server_down(domain)) {
861                 DEBUG(10, ("enum_local_groups: returning cached user list and server was down\n"));
862                 status = NT_STATUS_MORE_PROCESSING_REQUIRED;
863         } else
864                 status = centry->status;
865
866         DEBUG(10,("enum_local_groups: [Cached] - cached list for domain %s status %s\n",
867                 domain->name, get_friendly_nt_error_msg(status) ));
868
869         centry_free(centry);
870         return status;
871
872 do_query:
873         *num_entries = 0;
874         *info = NULL;
875
876         /* Return status value returned by seq number check */
877
878         if (!NT_STATUS_IS_OK(domain->last_status))
879                 return domain->last_status;
880
881         DEBUG(10,("enum_local_groups: [Cached] - doing backend query for list for domain %s\n",
882                 domain->name ));
883
884         status = domain->backend->enum_local_groups(domain, mem_ctx, num_entries, info);
885
886         /* and save it */
887         refresh_sequence_number(domain, False);
888         centry = centry_start(domain, status);
889         if (!centry)
890                 goto skip_save;
891         centry_put_uint32(centry, *num_entries);
892         for (i=0; i<(*num_entries); i++) {
893                 centry_put_string(centry, (*info)[i].acct_name);
894                 centry_put_string(centry, (*info)[i].acct_desc);
895                 centry_put_uint32(centry, (*info)[i].rid);
896         }
897         centry_end(centry, "GL/%s/local", domain->name);
898         centry_free(centry);
899
900 skip_save:
901         return status;
902 }
903
904 /* convert a single name to a sid in a domain */
905 static NTSTATUS name_to_sid(struct winbindd_domain *domain,
906                             TALLOC_CTX *mem_ctx,
907                             const char *name,
908                             DOM_SID *sid,
909                             enum SID_NAME_USE *type)
910 {
911         struct winbind_cache *cache = get_cache(domain);
912         struct cache_entry *centry = NULL;
913         NTSTATUS status;
914         fstring uname;
915         DOM_SID *sid2;
916
917         if (!cache->tdb)
918                 goto do_query;
919
920         fstrcpy(uname, name);
921         strupper_m(uname);
922         centry = wcache_fetch(cache, domain, "NS/%s/%s", domain->name, uname);
923         if (!centry)
924                 goto do_query;
925         *type = centry_uint32(centry);
926         sid2 = centry_sid(centry, mem_ctx);
927         if (!sid2) {
928                 ZERO_STRUCTP(sid);
929         } else {
930                 sid_copy(sid, sid2);
931         }
932
933         status = centry->status;
934
935         DEBUG(10,("name_to_sid: [Cached] - cached name for domain %s status %s\n",
936                 domain->name, get_friendly_nt_error_msg(status) ));
937
938         centry_free(centry);
939         return status;
940
941 do_query:
942         ZERO_STRUCTP(sid);
943
944         /* If the seq number check indicated that there is a problem
945          * with this DC, then return that status... except for
946          * access_denied.  This is special because the dc may be in
947          * "restrict anonymous = 1" mode, in which case it will deny
948          * most unauthenticated operations, but *will* allow the LSA
949          * name-to-sid that we try as a fallback. */
950
951         if (!(NT_STATUS_IS_OK(domain->last_status)
952               || NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)))
953                 return domain->last_status;
954
955         DEBUG(10,("name_to_sid: [Cached] - doing backend query for name for domain %s\n",
956                 domain->name ));
957
958         status = domain->backend->name_to_sid(domain, mem_ctx, name, sid, type);
959
960         /* and save it */
961         wcache_save_name_to_sid(domain, status, name, sid, *type);
962
963         /* We can't save the sid to name mapping as we don't know the
964            correct case of the name without looking it up */
965
966         return status;
967 }
968
969 /* convert a sid to a user or group name. The sid is guaranteed to be in the domain
970    given */
971 static NTSTATUS sid_to_name(struct winbindd_domain *domain,
972                             TALLOC_CTX *mem_ctx,
973                             DOM_SID *sid,
974                             char **name,
975                             enum SID_NAME_USE *type)
976 {
977         struct winbind_cache *cache = get_cache(domain);
978         struct cache_entry *centry = NULL;
979         NTSTATUS status;
980         fstring sid_string;
981
982         if (!cache->tdb)
983                 goto do_query;
984
985         centry = wcache_fetch(cache, domain, "SN/%s", sid_to_string(sid_string, sid));
986         if (!centry)
987                 goto do_query;
988         if (NT_STATUS_IS_OK(centry->status)) {
989                 *type = centry_uint32(centry);
990                 *name = centry_string(centry, mem_ctx);
991         }
992         status = centry->status;
993
994         DEBUG(10,("sid_to_name: [Cached] - cached name for domain %s status %s\n",
995                 domain->name, get_friendly_nt_error_msg(status) ));
996
997         centry_free(centry);
998         return status;
999
1000 do_query:
1001         *name = NULL;
1002
1003         /* If the seq number check indicated that there is a problem
1004          * with this DC, then return that status... except for
1005          * access_denied.  This is special because the dc may be in
1006          * "restrict anonymous = 1" mode, in which case it will deny
1007          * most unauthenticated operations, but *will* allow the LSA
1008          * sid-to-name that we try as a fallback. */
1009
1010         if (!(NT_STATUS_IS_OK(domain->last_status)
1011               || NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)))
1012                 return domain->last_status;
1013
1014         DEBUG(10,("sid_to_name: [Cached] - doing backend query for name for domain %s\n",
1015                 domain->name ));
1016
1017         status = domain->backend->sid_to_name(domain, mem_ctx, sid, name, type);
1018
1019         /* and save it */
1020         refresh_sequence_number(domain, False);
1021         wcache_save_sid_to_name(domain, status, sid, *name, *type);
1022         wcache_save_name_to_sid(domain, status, *name, sid, *type);
1023
1024         return status;
1025 }
1026
1027
1028 /* Lookup user information from a rid */
1029 static NTSTATUS query_user(struct winbindd_domain *domain, 
1030                            TALLOC_CTX *mem_ctx, 
1031                            DOM_SID *user_sid, 
1032                            WINBIND_USERINFO *info)
1033 {
1034         struct winbind_cache *cache = get_cache(domain);
1035         struct cache_entry *centry = NULL;
1036         NTSTATUS status;
1037
1038         if (!cache->tdb)
1039                 goto do_query;
1040
1041         centry = wcache_fetch(cache, domain, "U/%s", sid_string_static(user_sid));
1042         
1043         /* If we have an access denied cache entry and a cached info3 in the
1044            samlogon cache then do a query.  This will force the rpc back end
1045            to return the info3 data. */
1046
1047         if (NT_STATUS_V(domain->last_status) == NT_STATUS_V(NT_STATUS_ACCESS_DENIED) &&
1048             netsamlogon_cache_have(user_sid)) {
1049                 DEBUG(10, ("query_user: cached access denied and have cached info3\n"));
1050                 domain->last_status = NT_STATUS_OK;
1051                 centry_free(centry);
1052                 goto do_query;
1053         }
1054         
1055         if (!centry)
1056                 goto do_query;
1057
1058         info->acct_name = centry_string(centry, mem_ctx);
1059         info->full_name = centry_string(centry, mem_ctx);
1060         info->user_sid = centry_sid(centry, mem_ctx);
1061         info->group_sid = centry_sid(centry, mem_ctx);
1062         status = centry->status;
1063
1064         DEBUG(10,("query_user: [Cached] - cached info for domain %s status %s\n",
1065                 domain->name, get_friendly_nt_error_msg(status) ));
1066
1067         centry_free(centry);
1068         return status;
1069
1070 do_query:
1071         ZERO_STRUCTP(info);
1072
1073         /* Return status value returned by seq number check */
1074
1075         if (!NT_STATUS_IS_OK(domain->last_status))
1076                 return domain->last_status;
1077         
1078         DEBUG(10,("sid_to_name: [Cached] - doing backend query for info for domain %s\n",
1079                 domain->name ));
1080
1081         status = domain->backend->query_user(domain, mem_ctx, user_sid, info);
1082
1083         /* and save it */
1084         refresh_sequence_number(domain, False);
1085         wcache_save_user(domain, status, info);
1086
1087         return status;
1088 }
1089
1090
1091 /* Lookup groups a user is a member of. */
1092 static NTSTATUS lookup_usergroups(struct winbindd_domain *domain,
1093                                   TALLOC_CTX *mem_ctx,
1094                                   DOM_SID *user_sid, 
1095                                   uint32 *num_groups, DOM_SID ***user_gids)
1096 {
1097         struct winbind_cache *cache = get_cache(domain);
1098         struct cache_entry *centry = NULL;
1099         NTSTATUS status;
1100         unsigned int i;
1101         fstring sid_string;
1102
1103         if (!cache->tdb)
1104                 goto do_query;
1105
1106         centry = wcache_fetch(cache, domain, "UG/%s", sid_to_string(sid_string, user_sid));
1107         
1108         /* If we have an access denied cache entry and a cached info3 in the
1109            samlogon cache then do a query.  This will force the rpc back end
1110            to return the info3 data. */
1111
1112         if (NT_STATUS_V(domain->last_status) == NT_STATUS_V(NT_STATUS_ACCESS_DENIED) &&
1113             netsamlogon_cache_have(user_sid)) {
1114                 DEBUG(10, ("query_user: cached access denied and have cached info3\n"));
1115                 domain->last_status = NT_STATUS_OK;
1116                 centry_free(centry);
1117                 goto do_query;
1118         }
1119         
1120         if (!centry)
1121                 goto do_query;
1122
1123         *num_groups = centry_uint32(centry);
1124         
1125         if (*num_groups == 0)
1126                 goto do_cached;
1127
1128         (*user_gids) = talloc(mem_ctx, sizeof(**user_gids) * (*num_groups));
1129         if (! (*user_gids))
1130                 smb_panic("lookup_usergroups out of memory");
1131         for (i=0; i<(*num_groups); i++) {
1132                 (*user_gids)[i] = centry_sid(centry, mem_ctx);
1133         }
1134
1135 do_cached:      
1136         status = centry->status;
1137
1138         DEBUG(10,("lookup_usergroups: [Cached] - cached info for domain %s status %s\n",
1139                 domain->name, get_friendly_nt_error_msg(status) ));
1140
1141         centry_free(centry);
1142         return status;
1143
1144 do_query:
1145         (*num_groups) = 0;
1146         (*user_gids) = NULL;
1147
1148         /* Return status value returned by seq number check */
1149
1150         if (!NT_STATUS_IS_OK(domain->last_status))
1151                 return domain->last_status;
1152
1153         DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info for domain %s\n",
1154                 domain->name ));
1155
1156         status = domain->backend->lookup_usergroups(domain, mem_ctx, user_sid, num_groups, user_gids);
1157
1158         /* and save it */
1159         refresh_sequence_number(domain, False);
1160         centry = centry_start(domain, status);
1161         if (!centry)
1162                 goto skip_save;
1163         centry_put_uint32(centry, *num_groups);
1164         for (i=0; i<(*num_groups); i++) {
1165                 centry_put_sid(centry, (*user_gids)[i]);
1166         }       
1167         centry_end(centry, "UG/%s", sid_to_string(sid_string, user_sid));
1168         centry_free(centry);
1169
1170 skip_save:
1171         return status;
1172 }
1173
1174
1175 static NTSTATUS lookup_groupmem(struct winbindd_domain *domain,
1176                                 TALLOC_CTX *mem_ctx,
1177                                 DOM_SID *group_sid, uint32 *num_names, 
1178                                 DOM_SID ***sid_mem, char ***names, 
1179                                 uint32 **name_types)
1180 {
1181         struct winbind_cache *cache = get_cache(domain);
1182         struct cache_entry *centry = NULL;
1183         NTSTATUS status;
1184         unsigned int i;
1185         fstring sid_string;
1186
1187         if (!cache->tdb)
1188                 goto do_query;
1189
1190         centry = wcache_fetch(cache, domain, "GM/%s", sid_to_string(sid_string, group_sid));
1191         if (!centry)
1192                 goto do_query;
1193
1194         *num_names = centry_uint32(centry);
1195         
1196         if (*num_names == 0)
1197                 goto do_cached;
1198
1199         (*sid_mem) = talloc(mem_ctx, sizeof(**sid_mem) * (*num_names));
1200         (*names) = talloc(mem_ctx, sizeof(**names) * (*num_names));
1201         (*name_types) = talloc(mem_ctx, sizeof(**name_types) * (*num_names));
1202
1203         if (! (*sid_mem) || ! (*names) || ! (*name_types)) {
1204                 smb_panic("lookup_groupmem out of memory");
1205         }
1206
1207         for (i=0; i<(*num_names); i++) {
1208                 (*sid_mem)[i] = centry_sid(centry, mem_ctx);
1209                 (*names)[i] = centry_string(centry, mem_ctx);
1210                 (*name_types)[i] = centry_uint32(centry);
1211         }
1212
1213 do_cached:      
1214         status = centry->status;
1215
1216         DEBUG(10,("lookup_groupmem: [Cached] - cached info for domain %s status %s\n",
1217                 domain->name, get_friendly_nt_error_msg(status) ));
1218
1219         centry_free(centry);
1220         return status;
1221
1222 do_query:
1223         (*num_names) = 0;
1224         (*sid_mem) = NULL;
1225         (*names) = NULL;
1226         (*name_types) = NULL;
1227         
1228         /* Return status value returned by seq number check */
1229
1230         if (!NT_STATUS_IS_OK(domain->last_status))
1231                 return domain->last_status;
1232
1233         DEBUG(10,("lookup_groupmem: [Cached] - doing backend query for info for domain %s\n",
1234                 domain->name ));
1235
1236         status = domain->backend->lookup_groupmem(domain, mem_ctx, group_sid, num_names, 
1237                                                   sid_mem, names, name_types);
1238
1239         /* and save it */
1240         refresh_sequence_number(domain, False);
1241         centry = centry_start(domain, status);
1242         if (!centry)
1243                 goto skip_save;
1244         centry_put_uint32(centry, *num_names);
1245         for (i=0; i<(*num_names); i++) {
1246                 centry_put_sid(centry, (*sid_mem)[i]);
1247                 centry_put_string(centry, (*names)[i]);
1248                 centry_put_uint32(centry, (*name_types)[i]);
1249         }       
1250         centry_end(centry, "GM/%s", sid_to_string(sid_string, group_sid));
1251         centry_free(centry);
1252
1253 skip_save:
1254         return status;
1255 }
1256
1257 /* find the sequence number for a domain */
1258 static NTSTATUS sequence_number(struct winbindd_domain *domain, uint32 *seq)
1259 {
1260         refresh_sequence_number(domain, False);
1261
1262         *seq = domain->sequence_number;
1263
1264         return NT_STATUS_OK;
1265 }
1266
1267 /* enumerate trusted domains */
1268 static NTSTATUS trusted_domains(struct winbindd_domain *domain,
1269                                 TALLOC_CTX *mem_ctx,
1270                                 uint32 *num_domains,
1271                                 char ***names,
1272                                 char ***alt_names,
1273                                 DOM_SID **dom_sids)
1274 {
1275         get_cache(domain);
1276
1277         DEBUG(10,("trusted_domains: [Cached] - doing backend query for info for domain %s\n",
1278                 domain->name ));
1279
1280         /* we don't cache this call */
1281         return domain->backend->trusted_domains(domain, mem_ctx, num_domains, 
1282                                                names, alt_names, dom_sids);
1283 }
1284
1285 /* find the domain sid */
1286 static NTSTATUS domain_sid(struct winbindd_domain *domain, DOM_SID *sid)
1287 {
1288         get_cache(domain);
1289
1290         DEBUG(10,("domain_sid: [Cached] - doing backend query for info for domain %s\n",
1291                 domain->name ));
1292
1293         /* we don't cache this call */
1294         return domain->backend->domain_sid(domain, sid);
1295 }
1296
1297 /* find the alternate names for the domain, if any */
1298 static NTSTATUS alternate_name(struct winbindd_domain *domain)
1299 {
1300         get_cache(domain);
1301
1302         DEBUG(10,("alternate_name: [Cached] - doing backend query for info for domain %s\n",
1303                 domain->name ));
1304
1305         /* we don't cache this call */
1306         return domain->backend->alternate_name(domain);
1307 }
1308
1309 /* Invalidate cached user and group lists coherently */
1310
1311 static int traverse_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf, 
1312                        void *state)
1313 {
1314         if (strncmp(kbuf.dptr, "UL/", 3) == 0 ||
1315             strncmp(kbuf.dptr, "GL/", 3) == 0)
1316                 tdb_delete(the_tdb, kbuf);
1317
1318         return 0;
1319 }
1320
1321 /* Invalidate the getpwnam and getgroups entries for a winbindd domain */
1322
1323 void wcache_invalidate_samlogon(struct winbindd_domain *domain, 
1324                                 NET_USER_INFO_3 *info3)
1325 {
1326         struct winbind_cache *cache;
1327         
1328         if (!domain)
1329                 return;
1330
1331         cache = get_cache(domain);
1332         netsamlogon_clear_cached_user(cache->tdb, info3);
1333 }
1334
1335 void wcache_invalidate_cache(void)
1336 {
1337         struct winbindd_domain *domain;
1338
1339         for (domain = domain_list(); domain; domain = domain->next) {
1340                 struct winbind_cache *cache = get_cache(domain);
1341
1342                 DEBUG(10, ("wcache_invalidate_cache: invalidating cache "
1343                            "entries for %s\n", domain->name));
1344                 if (cache)
1345                         tdb_traverse(cache->tdb, traverse_fn, NULL);
1346         }
1347 }
1348
1349 /* the ADS backend methods are exposed via this structure */
1350 struct winbindd_methods cache_methods = {
1351         True,
1352         query_user_list,
1353         enum_dom_groups,
1354         enum_local_groups,
1355         name_to_sid,
1356         sid_to_name,
1357         query_user,
1358         lookup_usergroups,
1359         lookup_groupmem,
1360         sequence_number,
1361         trusted_domains,
1362         domain_sid,
1363         alternate_name
1364 };