Initial import
[samba] / 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         if (NT_STATUS_IS_OK(status)) {
995                 strupper_m(CONST_DISCARD(char *,domain_name));
996                 strlower_m(CONST_DISCARD(char *,name));
997                 wcache_save_sid_to_name(domain, status, sid, domain_name, name, *type);
998         }
999
1000         return status;
1001 }
1002
1003 /* convert a sid to a user or group name. The sid is guaranteed to be in the domain
1004    given */
1005 static NTSTATUS sid_to_name(struct winbindd_domain *domain,
1006                             TALLOC_CTX *mem_ctx,
1007                             const DOM_SID *sid,
1008                             char **domain_name,
1009                             char **name,
1010                             enum SID_NAME_USE *type)
1011 {
1012         struct winbind_cache *cache = get_cache(domain);
1013         struct cache_entry *centry = NULL;
1014         NTSTATUS status;
1015         fstring sid_string;
1016
1017         if (!cache->tdb)
1018                 goto do_query;
1019
1020         centry = wcache_fetch(cache, domain, "SN/%s", sid_to_string(sid_string, sid));
1021         if (!centry)
1022                 goto do_query;
1023         if (NT_STATUS_IS_OK(centry->status)) {
1024                 *type = (enum SID_NAME_USE)centry_uint32(centry);
1025                 *domain_name = centry_string(centry, mem_ctx);
1026                 *name = centry_string(centry, mem_ctx);
1027         }
1028         status = centry->status;
1029
1030         DEBUG(10,("sid_to_name: [Cached] - cached name for domain %s status %s\n",
1031                 domain->name, get_friendly_nt_error_msg(status) ));
1032
1033         centry_free(centry);
1034         return status;
1035
1036 do_query:
1037         *name = NULL;
1038         *domain_name = NULL;
1039
1040         /* If the seq number check indicated that there is a problem
1041          * with this DC, then return that status... except for
1042          * access_denied.  This is special because the dc may be in
1043          * "restrict anonymous = 1" mode, in which case it will deny
1044          * most unauthenticated operations, but *will* allow the LSA
1045          * sid-to-name that we try as a fallback. */
1046
1047         if (!(NT_STATUS_IS_OK(domain->last_status)
1048               || NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)))
1049                 return domain->last_status;
1050
1051         DEBUG(10,("sid_to_name: [Cached] - doing backend query for name for domain %s\n",
1052                 domain->name ));
1053
1054         status = domain->backend->sid_to_name(domain, mem_ctx, sid, domain_name, name, type);
1055
1056         /* and save it */
1057         refresh_sequence_number(domain, False);
1058         wcache_save_sid_to_name(domain, status, sid, *domain_name, *name, *type);
1059
1060         /* We can't save the name to sid mapping here, as with sid history a
1061          * later name2sid would give the wrong sid. */
1062
1063         return status;
1064 }
1065
1066 /* Lookup user information from a rid */
1067 static NTSTATUS query_user(struct winbindd_domain *domain, 
1068                            TALLOC_CTX *mem_ctx, 
1069                            const DOM_SID *user_sid, 
1070                            WINBIND_USERINFO *info)
1071 {
1072         struct winbind_cache *cache = get_cache(domain);
1073         struct cache_entry *centry = NULL;
1074         NTSTATUS status;
1075
1076         if (!cache->tdb)
1077                 goto do_query;
1078
1079         centry = wcache_fetch(cache, domain, "U/%s", sid_string_static(user_sid));
1080         
1081         /* If we have an access denied cache entry and a cached info3 in the
1082            samlogon cache then do a query.  This will force the rpc back end
1083            to return the info3 data. */
1084
1085         if (NT_STATUS_V(domain->last_status) == NT_STATUS_V(NT_STATUS_ACCESS_DENIED) &&
1086             netsamlogon_cache_have(user_sid)) {
1087                 DEBUG(10, ("query_user: cached access denied and have cached info3\n"));
1088                 domain->last_status = NT_STATUS_OK;
1089                 centry_free(centry);
1090                 goto do_query;
1091         }
1092         
1093         if (!centry)
1094                 goto do_query;
1095
1096         info->acct_name = centry_string(centry, mem_ctx);
1097         info->full_name = centry_string(centry, mem_ctx);
1098         info->homedir = centry_string(centry, mem_ctx);
1099         info->shell = centry_string(centry, mem_ctx);
1100         centry_sid(centry, &info->user_sid);
1101         centry_sid(centry, &info->group_sid);
1102         status = centry->status;
1103
1104         DEBUG(10,("query_user: [Cached] - cached info for domain %s status %s\n",
1105                 domain->name, get_friendly_nt_error_msg(status) ));
1106
1107         centry_free(centry);
1108         return status;
1109
1110 do_query:
1111         ZERO_STRUCTP(info);
1112
1113         /* Return status value returned by seq number check */
1114
1115         if (!NT_STATUS_IS_OK(domain->last_status))
1116                 return domain->last_status;
1117         
1118         DEBUG(10,("sid_to_name: [Cached] - doing backend query for info for domain %s\n",
1119                 domain->name ));
1120
1121         status = domain->backend->query_user(domain, mem_ctx, user_sid, info);
1122
1123         /* and save it */
1124         refresh_sequence_number(domain, False);
1125         wcache_save_user(domain, status, info);
1126
1127         return status;
1128 }
1129
1130
1131 /* Lookup groups a user is a member of. */
1132 static NTSTATUS lookup_usergroups(struct winbindd_domain *domain,
1133                                   TALLOC_CTX *mem_ctx,
1134                                   const DOM_SID *user_sid, 
1135                                   uint32 *num_groups, DOM_SID **user_gids)
1136 {
1137         struct winbind_cache *cache = get_cache(domain);
1138         struct cache_entry *centry = NULL;
1139         NTSTATUS status;
1140         unsigned int i;
1141         fstring sid_string;
1142
1143         if (!cache->tdb)
1144                 goto do_query;
1145
1146         centry = wcache_fetch(cache, domain, "UG/%s", sid_to_string(sid_string, user_sid));
1147         
1148         /* If we have an access denied cache entry and a cached info3 in the
1149            samlogon cache then do a query.  This will force the rpc back end
1150            to return the info3 data. */
1151
1152         if (NT_STATUS_V(domain->last_status) == NT_STATUS_V(NT_STATUS_ACCESS_DENIED) &&
1153             netsamlogon_cache_have(user_sid)) {
1154                 DEBUG(10, ("lookup_usergroups: cached access denied and have cached info3\n"));
1155                 domain->last_status = NT_STATUS_OK;
1156                 centry_free(centry);
1157                 goto do_query;
1158         }
1159         
1160         if (!centry)
1161                 goto do_query;
1162
1163         *num_groups = centry_uint32(centry);
1164         
1165         if (*num_groups == 0)
1166                 goto do_cached;
1167
1168         (*user_gids) = TALLOC_ARRAY(mem_ctx, DOM_SID, *num_groups);
1169         if (! (*user_gids))
1170                 smb_panic("lookup_usergroups out of memory");
1171         for (i=0; i<(*num_groups); i++) {
1172                 centry_sid(centry, &(*user_gids)[i]);
1173         }
1174
1175 do_cached:      
1176         status = centry->status;
1177
1178         DEBUG(10,("lookup_usergroups: [Cached] - cached info for domain %s status %s\n",
1179                 domain->name, get_friendly_nt_error_msg(status) ));
1180
1181         centry_free(centry);
1182         return status;
1183
1184 do_query:
1185         (*num_groups) = 0;
1186         (*user_gids) = NULL;
1187
1188         /* Return status value returned by seq number check */
1189
1190         if (!NT_STATUS_IS_OK(domain->last_status))
1191                 return domain->last_status;
1192
1193         DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info for domain %s\n",
1194                 domain->name ));
1195
1196         status = domain->backend->lookup_usergroups(domain, mem_ctx, user_sid, num_groups, user_gids);
1197
1198         /* and save it */
1199         refresh_sequence_number(domain, False);
1200         centry = centry_start(domain, status);
1201         if (!centry)
1202                 goto skip_save;
1203         centry_put_uint32(centry, *num_groups);
1204         for (i=0; i<(*num_groups); i++) {
1205                 centry_put_sid(centry, &(*user_gids)[i]);
1206         }       
1207         centry_end(centry, "UG/%s", sid_to_string(sid_string, user_sid));
1208         centry_free(centry);
1209
1210 skip_save:
1211         return status;
1212 }
1213
1214 static NTSTATUS lookup_useraliases(struct winbindd_domain *domain,
1215                                    TALLOC_CTX *mem_ctx,
1216                                    uint32 num_sids, const DOM_SID *sids,
1217                                    uint32 *num_aliases, uint32 **alias_rids)
1218 {
1219         struct winbind_cache *cache = get_cache(domain);
1220         struct cache_entry *centry = NULL;
1221         NTSTATUS status;
1222         char *sidlist = talloc_strdup(mem_ctx, "");
1223         int i;
1224
1225         if (!cache->tdb)
1226                 goto do_query;
1227
1228         if (num_sids == 0) {
1229                 *num_aliases = 0;
1230                 *alias_rids = NULL;
1231                 return NT_STATUS_OK;
1232         }
1233
1234         /* We need to cache indexed by the whole list of SIDs, the aliases
1235          * resulting might come from any of the SIDs. */
1236
1237         for (i=0; i<num_sids; i++) {
1238                 sidlist = talloc_asprintf(mem_ctx, "%s/%s", sidlist,
1239                                           sid_string_static(&sids[i]));
1240                 if (sidlist == NULL)
1241                         return NT_STATUS_NO_MEMORY;
1242         }
1243
1244         centry = wcache_fetch(cache, domain, "UA%s", sidlist);
1245
1246         if (!centry)
1247                 goto do_query;
1248
1249         *num_aliases = centry_uint32(centry);
1250         *alias_rids = NULL;
1251
1252         (*alias_rids) = TALLOC_ARRAY(mem_ctx, uint32, *num_aliases);
1253
1254         if ((*num_aliases != 0) && ((*alias_rids) == NULL))
1255                 return NT_STATUS_NO_MEMORY;
1256
1257         for (i=0; i<(*num_aliases); i++)
1258                 (*alias_rids)[i] = centry_uint32(centry);
1259
1260         status = centry->status;
1261
1262         DEBUG(10,("lookup_useraliases: [Cached] - cached info for domain %s "
1263                   "status %s\n", domain->name,
1264                   get_friendly_nt_error_msg(status)));
1265
1266         centry_free(centry);
1267         return status;
1268
1269  do_query:
1270         (*num_aliases) = 0;
1271         (*alias_rids) = NULL;
1272
1273         if (!NT_STATUS_IS_OK(domain->last_status))
1274                 return domain->last_status;
1275
1276         DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info "
1277                   "for domain %s\n", domain->name ));
1278
1279         status = domain->backend->lookup_useraliases(domain, mem_ctx,
1280                                                      num_sids, sids,
1281                                                      num_aliases, alias_rids);
1282
1283         /* and save it */
1284         refresh_sequence_number(domain, False);
1285         centry = centry_start(domain, status);
1286         if (!centry)
1287                 goto skip_save;
1288         centry_put_uint32(centry, *num_aliases);
1289         for (i=0; i<(*num_aliases); i++)
1290                 centry_put_uint32(centry, (*alias_rids)[i]);
1291         centry_end(centry, "UA%s", sidlist);
1292         centry_free(centry);
1293
1294  skip_save:
1295         return status;
1296 }
1297
1298
1299 static NTSTATUS lookup_groupmem(struct winbindd_domain *domain,
1300                                 TALLOC_CTX *mem_ctx,
1301                                 const DOM_SID *group_sid, uint32 *num_names, 
1302                                 DOM_SID **sid_mem, char ***names, 
1303                                 uint32 **name_types)
1304 {
1305         struct winbind_cache *cache = get_cache(domain);
1306         struct cache_entry *centry = NULL;
1307         NTSTATUS status;
1308         unsigned int i;
1309         fstring sid_string;
1310
1311         if (!cache->tdb)
1312                 goto do_query;
1313
1314         centry = wcache_fetch(cache, domain, "GM/%s", sid_to_string(sid_string, group_sid));
1315         if (!centry)
1316                 goto do_query;
1317
1318         *num_names = centry_uint32(centry);
1319         
1320         if (*num_names == 0)
1321                 goto do_cached;
1322
1323         (*sid_mem) = TALLOC_ARRAY(mem_ctx, DOM_SID, *num_names);
1324         (*names) = TALLOC_ARRAY(mem_ctx, char *, *num_names);
1325         (*name_types) = TALLOC_ARRAY(mem_ctx, uint32, *num_names);
1326
1327         if (! (*sid_mem) || ! (*names) || ! (*name_types)) {
1328                 smb_panic("lookup_groupmem out of memory");
1329         }
1330
1331         for (i=0; i<(*num_names); i++) {
1332                 centry_sid(centry, &(*sid_mem)[i]);
1333                 (*names)[i] = centry_string(centry, mem_ctx);
1334                 (*name_types)[i] = centry_uint32(centry);
1335         }
1336
1337 do_cached:      
1338         status = centry->status;
1339
1340         DEBUG(10,("lookup_groupmem: [Cached] - cached info for domain %s status %s\n",
1341                 domain->name, get_friendly_nt_error_msg(status) ));
1342
1343         centry_free(centry);
1344         return status;
1345
1346 do_query:
1347         (*num_names) = 0;
1348         (*sid_mem) = NULL;
1349         (*names) = NULL;
1350         (*name_types) = NULL;
1351         
1352         /* Return status value returned by seq number check */
1353
1354         if (!NT_STATUS_IS_OK(domain->last_status))
1355                 return domain->last_status;
1356
1357         DEBUG(10,("lookup_groupmem: [Cached] - doing backend query for info for domain %s\n",
1358                 domain->name ));
1359
1360         status = domain->backend->lookup_groupmem(domain, mem_ctx, group_sid, num_names, 
1361                                                   sid_mem, names, name_types);
1362
1363         /* and save it */
1364         refresh_sequence_number(domain, False);
1365         centry = centry_start(domain, status);
1366         if (!centry)
1367                 goto skip_save;
1368         centry_put_uint32(centry, *num_names);
1369         for (i=0; i<(*num_names); i++) {
1370                 centry_put_sid(centry, &(*sid_mem)[i]);
1371                 centry_put_string(centry, (*names)[i]);
1372                 centry_put_uint32(centry, (*name_types)[i]);
1373         }       
1374         centry_end(centry, "GM/%s", sid_to_string(sid_string, group_sid));
1375         centry_free(centry);
1376
1377 skip_save:
1378         return status;
1379 }
1380
1381 /* find the sequence number for a domain */
1382 static NTSTATUS sequence_number(struct winbindd_domain *domain, uint32 *seq)
1383 {
1384         refresh_sequence_number(domain, False);
1385
1386         *seq = domain->sequence_number;
1387
1388         return NT_STATUS_OK;
1389 }
1390
1391 /* enumerate trusted domains */
1392 static NTSTATUS trusted_domains(struct winbindd_domain *domain,
1393                                 TALLOC_CTX *mem_ctx,
1394                                 uint32 *num_domains,
1395                                 char ***names,
1396                                 char ***alt_names,
1397                                 DOM_SID **dom_sids)
1398 {
1399         get_cache(domain);
1400
1401         DEBUG(10,("trusted_domains: [Cached] - doing backend query for info for domain %s\n",
1402                 domain->name ));
1403
1404         /* we don't cache this call */
1405         return domain->backend->trusted_domains(domain, mem_ctx, num_domains, 
1406                                                names, alt_names, dom_sids);
1407 }
1408
1409 /* Invalidate cached user and group lists coherently */
1410
1411 static int traverse_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf, 
1412                        void *state)
1413 {
1414         if (strncmp(kbuf.dptr, "UL/", 3) == 0 ||
1415             strncmp(kbuf.dptr, "GL/", 3) == 0)
1416                 tdb_delete(the_tdb, kbuf);
1417
1418         return 0;
1419 }
1420
1421 /* Invalidate the getpwnam and getgroups entries for a winbindd domain */
1422
1423 void wcache_invalidate_samlogon(struct winbindd_domain *domain, 
1424                                 NET_USER_INFO_3 *info3)
1425 {
1426         struct winbind_cache *cache;
1427         
1428         if (!domain)
1429                 return;
1430
1431         cache = get_cache(domain);
1432         netsamlogon_clear_cached_user(cache->tdb, info3);
1433 }
1434
1435 void wcache_invalidate_cache(void)
1436 {
1437         struct winbindd_domain *domain;
1438
1439         for (domain = domain_list(); domain; domain = domain->next) {
1440                 struct winbind_cache *cache = get_cache(domain);
1441
1442                 DEBUG(10, ("wcache_invalidate_cache: invalidating cache "
1443                            "entries for %s\n", domain->name));
1444                 if (cache)
1445                         tdb_traverse(cache->tdb, traverse_fn, NULL);
1446         }
1447 }
1448
1449 /* the ADS backend methods are exposed via this structure */
1450 struct winbindd_methods cache_methods = {
1451         True,
1452         query_user_list,
1453         enum_dom_groups,
1454         enum_local_groups,
1455         name_to_sid,
1456         sid_to_name,
1457         query_user,
1458         lookup_usergroups,
1459         lookup_useraliases,
1460         lookup_groupmem,
1461         sequence_number,
1462         trusted_domains,
1463 };
1464
1465 static BOOL init_wcache(void)
1466 {
1467         if (wcache == NULL) {
1468                 wcache = SMB_XMALLOC_P(struct winbind_cache);
1469                 ZERO_STRUCTP(wcache);
1470         }
1471
1472         if (wcache->tdb != NULL)
1473                 return True;
1474
1475         wcache->tdb = tdb_open_log(lock_path("winbindd_cache.tdb"), 5000, 
1476                                    TDB_CLEAR_IF_FIRST, O_RDWR|O_CREAT, 0600);
1477
1478         if (wcache->tdb == NULL) {
1479                 DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
1480                 return False;
1481         }
1482
1483         return True;
1484 }
1485
1486 void cache_store_response(pid_t pid, struct winbindd_response *response)
1487 {
1488         fstring key_str;
1489
1490         if (!init_wcache())
1491                 return;
1492
1493         DEBUG(10, ("Storing response for pid %d, len %d\n",
1494                    pid, response->length));
1495
1496         fstr_sprintf(key_str, "DR/%d", pid);
1497         if (tdb_store(wcache->tdb, string_tdb_data(key_str), 
1498                       make_tdb_data((void *)response, sizeof(*response)),
1499                       TDB_REPLACE) == -1)
1500                 return;
1501
1502         if (response->length == sizeof(*response))
1503                 return;
1504
1505         /* There's extra data */
1506
1507         DEBUG(10, ("Storing extra data: len=%d\n",
1508                    (int)(response->length - sizeof(*response))));
1509
1510         fstr_sprintf(key_str, "DE/%d", pid);
1511         if (tdb_store(wcache->tdb, string_tdb_data(key_str),
1512                       make_tdb_data(response->extra_data,
1513                                     response->length - sizeof(*response)),
1514                       TDB_REPLACE) == 0)
1515                 return;
1516
1517         /* We could not store the extra data, make sure the tdb does not
1518          * contain a main record with wrong dangling extra data */
1519
1520         fstr_sprintf(key_str, "DR/%d", pid);
1521         tdb_delete(wcache->tdb, string_tdb_data(key_str));
1522
1523         return;
1524 }
1525
1526 BOOL cache_retrieve_response(pid_t pid, struct winbindd_response * response)
1527 {
1528         TDB_DATA data;
1529         fstring key_str;
1530
1531         if (!init_wcache())
1532                 return False;
1533
1534         DEBUG(10, ("Retrieving response for pid %d\n", pid));
1535
1536         fstr_sprintf(key_str, "DR/%d", pid);
1537         data = tdb_fetch(wcache->tdb, string_tdb_data(key_str));
1538
1539         if (data.dptr == NULL)
1540                 return False;
1541
1542         if (data.dsize != sizeof(*response))
1543                 return False;
1544
1545         memcpy(response, data.dptr, data.dsize);
1546         SAFE_FREE(data.dptr);
1547
1548         if (response->length == sizeof(*response)) {
1549                 response->extra_data = NULL;
1550                 return True;
1551         }
1552
1553         /* There's extra data */
1554
1555         DEBUG(10, ("Retrieving extra data length=%d\n",
1556                    (int)(response->length - sizeof(*response))));
1557
1558         fstr_sprintf(key_str, "DE/%d", pid);
1559         data = tdb_fetch(wcache->tdb, string_tdb_data(key_str));
1560
1561         if (data.dptr == NULL) {
1562                 DEBUG(0, ("Did not find extra data\n"));
1563                 return False;
1564         }
1565
1566         if (data.dsize != (response->length - sizeof(*response))) {
1567                 DEBUG(0, ("Invalid extra data length: %d\n", (int)data.dsize));
1568                 SAFE_FREE(data.dptr);
1569                 return False;
1570         }
1571
1572         dump_data(11, data.dptr, data.dsize);
1573
1574         response->extra_data = data.dptr;
1575         return True;
1576 }
1577
1578 BOOL lookup_cached_sid(TALLOC_CTX *mem_ctx, const DOM_SID *sid,
1579                        const char **domain_name, const char **name,
1580                        enum SID_NAME_USE *type)
1581 {
1582         struct winbindd_domain *domain;
1583         struct winbind_cache *cache;
1584         struct cache_entry *centry = NULL;
1585         NTSTATUS status;
1586
1587         domain = find_lookup_domain_from_sid(sid);
1588         if (domain == NULL) {
1589                 return False;
1590         }
1591
1592         cache = get_cache(domain);
1593
1594         if (cache->tdb == NULL) {
1595                 return False;
1596         }
1597
1598         centry = wcache_fetch(cache, domain, "SN/%s", sid_string_static(sid));
1599         if (centry == NULL) {
1600                 return False;
1601         }
1602
1603         if (NT_STATUS_IS_OK(centry->status)) {
1604                 *type = (enum SID_NAME_USE)centry_uint32(centry);
1605                 *domain_name = centry_string(centry, mem_ctx);
1606                 *name = centry_string(centry, mem_ctx);
1607         }
1608
1609         status = centry->status;
1610         centry_free(centry);
1611         return NT_STATUS_IS_OK(status);
1612 }
1613
1614 void cache_sid2name(struct winbindd_domain *domain, const DOM_SID *sid,
1615                     const char *domain_name, const char *name,
1616                     enum SID_NAME_USE type)
1617 {
1618         wcache_save_sid_to_name(domain, NT_STATUS_OK, sid, domain_name,
1619                                 name, type);
1620 }