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