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