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