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