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