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