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