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