r16221: No need for friednly error messages at log level 10.
[sfrench/samba-autobuild/.git] / source / nsswitch / winbindd_cache.c
1 /* 
2    Unix SMB/CIFS implementation.
3
4    Winbind cache backend functions
5
6    Copyright (C) Andrew Tridgell 2001
7    Copyright (C) Gerald Carter   2003
8    Copyright (C) Volker Lendecke 2005
9    Copyright (C) Guenther Deschner 2005
10    
11    This program is free software; you can redistribute it and/or modify
12    it under the terms of the GNU General Public License as published by
13    the Free Software Foundation; either version 2 of the License, or
14    (at your option) any later version.
15    
16    This program is distributed in the hope that it will be useful,
17    but WITHOUT ANY WARRANTY; without even the implied warranty of
18    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19    GNU General Public License for more details.
20    
21    You should have received a copy of the GNU General Public License
22    along with this program; if not, write to the Free Software
23    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
24 */
25
26 #include "includes.h"
27 #include "winbindd.h"
28
29 #undef DBGC_CLASS
30 #define DBGC_CLASS DBGC_WINBIND
31
32 /* Global online/offline state - False when online. winbindd starts up online
33    and sets this to true if the first query fails and there's an entry in
34    the cache tdb telling us to stay offline. */
35
36 static BOOL global_winbindd_offline_state;
37
38 struct winbind_cache {
39         TDB_CONTEXT *tdb;
40 };
41
42 struct cache_entry {
43         NTSTATUS status;
44         uint32 sequence_number;
45         uint8 *data;
46         uint32 len, ofs;
47 };
48
49 #define WINBINDD_MAX_CACHE_SIZE (50*1024*1024)
50
51 static struct winbind_cache *wcache;
52
53 void winbindd_check_cache_size(time_t t)
54 {
55         static time_t last_check_time;
56         struct stat st;
57
58         if (last_check_time == (time_t)0)
59                 last_check_time = t;
60
61         if (t - last_check_time < 60 && t - last_check_time > 0)
62                 return;
63
64         if (wcache == NULL || wcache->tdb == NULL) {
65                 DEBUG(0, ("Unable to check size of tdb cache - cache not open !\n"));
66                 return;
67         }
68
69         if (fstat(wcache->tdb->fd, &st) == -1) {
70                 DEBUG(0, ("Unable to check size of tdb cache %s!\n", strerror(errno) ));
71                 return;
72         }
73
74         if (st.st_size > WINBINDD_MAX_CACHE_SIZE) {
75                 DEBUG(10,("flushing cache due to size (%lu) > (%lu)\n",
76                         (unsigned long)st.st_size,
77                         (unsigned long)WINBINDD_MAX_CACHE_SIZE));
78                 wcache_flush_cache();
79         }
80 }
81
82 /* get the winbind_cache structure */
83 static struct winbind_cache *get_cache(struct winbindd_domain *domain)
84 {
85         struct winbind_cache *ret = wcache;
86 #ifdef HAVE_ADS
87         struct winbindd_domain *our_domain = domain;
88 #endif
89
90         /* we have to know what type of domain we are dealing with first */
91
92         if ( !domain->initialized )
93                 set_dc_type_and_flags( domain );
94
95         /* 
96            OK.  listen up becasue I'm only going to say this once.
97            We have the following scenarios to consider
98            (a) trusted AD domains on a Samba DC,
99            (b) trusted AD domains and we are joined to a non-kerberos domain
100            (c) trusted AD domains and we are joined to a kerberos (AD) domain
101
102            For (a) we can always contact the trusted domain using krb5 
103            since we have the domain trust account password
104
105            For (b) we can only use RPC since we have no way of 
106            getting a krb5 ticket in our own domain
107
108            For (c) we can always use krb5 since we have a kerberos trust
109
110            --jerry
111          */
112
113         if (!domain->backend) {
114                 extern struct winbindd_methods reconnect_methods;
115 #ifdef HAVE_ADS
116                 extern struct winbindd_methods ads_methods;
117
118                 /* find our domain first so we can figure out if we 
119                    are joined to a kerberized domain */
120
121                 if ( !domain->primary )
122                         our_domain = find_our_domain();
123
124                 if ( (our_domain->active_directory || IS_DC) && domain->active_directory ) {
125                         DEBUG(5,("get_cache: Setting ADS methods for domain %s\n", domain->name));
126                         domain->backend = &ads_methods;
127                 } else {
128 #endif  /* HAVE_ADS */
129                         DEBUG(5,("get_cache: Setting MS-RPC methods for domain %s\n", domain->name));
130                         domain->backend = &reconnect_methods;
131 #ifdef HAVE_ADS
132                 }
133 #endif  /* HAVE_ADS */
134         }
135
136         if (ret)
137                 return ret;
138         
139         ret = SMB_XMALLOC_P(struct winbind_cache);
140         ZERO_STRUCTP(ret);
141
142         wcache = ret;
143         wcache_flush_cache();
144
145         return ret;
146 }
147
148 /*
149   free a centry structure
150 */
151 static void centry_free(struct cache_entry *centry)
152 {
153         if (!centry)
154                 return;
155         SAFE_FREE(centry->data);
156         free(centry);
157 }
158
159 /*
160   pull a uint32 from a cache entry 
161 */
162 static uint32 centry_uint32(struct cache_entry *centry)
163 {
164         uint32 ret;
165         if (centry->len - centry->ofs < 4) {
166                 DEBUG(0,("centry corruption? needed 4 bytes, have %d\n", 
167                          centry->len - centry->ofs));
168                 smb_panic("centry_uint32");
169         }
170         ret = IVAL(centry->data, centry->ofs);
171         centry->ofs += 4;
172         return ret;
173 }
174
175 /*
176   pull a uint16 from a cache entry 
177 */
178 static uint16 centry_uint16(struct cache_entry *centry)
179 {
180         uint16 ret;
181         if (centry->len - centry->ofs < 2) {
182                 DEBUG(0,("centry corruption? needed 2 bytes, have %d\n", 
183                          centry->len - centry->ofs));
184                 smb_panic("centry_uint16");
185         }
186         ret = CVAL(centry->data, centry->ofs);
187         centry->ofs += 2;
188         return ret;
189 }
190
191 /*
192   pull a uint8 from a cache entry 
193 */
194 static uint8 centry_uint8(struct cache_entry *centry)
195 {
196         uint8 ret;
197         if (centry->len - centry->ofs < 1) {
198                 DEBUG(0,("centry corruption? needed 1 bytes, have %d\n", 
199                          centry->len - centry->ofs));
200                 smb_panic("centry_uint32");
201         }
202         ret = CVAL(centry->data, centry->ofs);
203         centry->ofs += 1;
204         return ret;
205 }
206
207 /*
208   pull a NTTIME from a cache entry 
209 */
210 static NTTIME centry_nttime(struct cache_entry *centry)
211 {
212         NTTIME ret;
213         if (centry->len - centry->ofs < 8) {
214                 DEBUG(0,("centry corruption? needed 8 bytes, have %d\n", 
215                          centry->len - centry->ofs));
216                 smb_panic("centry_nttime");
217         }
218         ret.low = IVAL(centry->data, centry->ofs);
219         centry->ofs += 4;
220         ret.high = IVAL(centry->data, centry->ofs);
221         centry->ofs += 4;
222         return ret;
223 }
224
225 /*
226   pull a time_t from a cache entry 
227 */
228 static time_t centry_time(struct cache_entry *centry)
229 {
230         time_t ret;
231         if (centry->len - centry->ofs < sizeof(time_t)) {
232                 DEBUG(0,("centry corruption? needed %d bytes, have %d\n", 
233                          sizeof(time_t), centry->len - centry->ofs));
234                 smb_panic("centry_time");
235         }
236         ret = IVAL(centry->data, centry->ofs); /* FIXME: correct ? */
237         centry->ofs += sizeof(time_t);
238         return ret;
239 }
240
241 /* pull a string from a cache entry, using the supplied
242    talloc context 
243 */
244 static char *centry_string(struct cache_entry *centry, TALLOC_CTX *mem_ctx)
245 {
246         uint32 len;
247         char *ret;
248
249         len = centry_uint8(centry);
250
251         if (len == 0xFF) {
252                 /* a deliberate NULL string */
253                 return NULL;
254         }
255
256         if (centry->len - centry->ofs < len) {
257                 DEBUG(0,("centry corruption? needed %d bytes, have %d\n", 
258                          len, centry->len - centry->ofs));
259                 smb_panic("centry_string");
260         }
261
262         ret = TALLOC(mem_ctx, len+1);
263         if (!ret) {
264                 smb_panic("centry_string out of memory\n");
265         }
266         memcpy(ret,centry->data + centry->ofs, len);
267         ret[len] = 0;
268         centry->ofs += len;
269         return ret;
270 }
271
272 /* pull a string from a cache entry, using the supplied
273    talloc context 
274 */
275 static BOOL centry_sid(struct cache_entry *centry, TALLOC_CTX *mem_ctx, DOM_SID *sid)
276 {
277         char *sid_string;
278         sid_string = centry_string(centry, mem_ctx);
279         if (!string_to_sid(sid, sid_string)) {
280                 return False;
281         }
282         return True;
283 }
284
285 /* the server is considered down if it can't give us a sequence number */
286 static BOOL wcache_server_down(struct winbindd_domain *domain)
287 {
288         BOOL ret;
289
290         if (!wcache->tdb)
291                 return False;
292
293         ret = (domain->sequence_number == DOM_SEQUENCE_NONE);
294
295         if (ret)
296                 DEBUG(10,("wcache_server_down: server for Domain %s down\n", 
297                         domain->name ));
298         return ret;
299 }
300
301 static NTSTATUS fetch_cache_seqnum( struct winbindd_domain *domain, time_t now )
302 {
303         TDB_DATA data;
304         fstring key;
305         uint32 time_diff;
306         
307         if (!wcache->tdb) {
308                 DEBUG(10,("fetch_cache_seqnum: tdb == NULL\n"));
309                 return NT_STATUS_UNSUCCESSFUL;
310         }
311                 
312         fstr_sprintf( key, "SEQNUM/%s", domain->name );
313         
314         data = tdb_fetch_bystring( wcache->tdb, key );
315         if ( !data.dptr || data.dsize!=8 ) {
316                 DEBUG(10,("fetch_cache_seqnum: invalid data size key [%s]\n", key ));
317                 return NT_STATUS_UNSUCCESSFUL;
318         }
319         
320         domain->sequence_number = IVAL(data.dptr, 0);
321         domain->last_seq_check  = IVAL(data.dptr, 4);
322         
323         SAFE_FREE(data.dptr);
324
325         /* have we expired? */
326         
327         time_diff = now - domain->last_seq_check;
328         if ( time_diff > lp_winbind_cache_time() ) {
329                 DEBUG(10,("fetch_cache_seqnum: timeout [%s][%u @ %u]\n",
330                         domain->name, domain->sequence_number,
331                         (uint32)domain->last_seq_check));
332                 return NT_STATUS_UNSUCCESSFUL;
333         }
334
335         DEBUG(10,("fetch_cache_seqnum: success [%s][%u @ %u]\n", 
336                 domain->name, domain->sequence_number, 
337                 (uint32)domain->last_seq_check));
338
339         return NT_STATUS_OK;
340 }
341
342 static NTSTATUS store_cache_seqnum( struct winbindd_domain *domain )
343 {
344         TDB_DATA data, key;
345         fstring key_str;
346         char buf[8];
347         
348         if (!wcache->tdb) {
349                 DEBUG(10,("store_cache_seqnum: tdb == NULL\n"));
350                 return NT_STATUS_UNSUCCESSFUL;
351         }
352                 
353         fstr_sprintf( key_str, "SEQNUM/%s", domain->name );
354         key.dptr = key_str;
355         key.dsize = strlen(key_str)+1;
356         
357         SIVAL(buf, 0, domain->sequence_number);
358         SIVAL(buf, 4, domain->last_seq_check);
359         data.dptr = buf;
360         data.dsize = 8;
361         
362         if ( tdb_store( wcache->tdb, key, data, TDB_REPLACE) == -1 ) {
363                 DEBUG(10,("store_cache_seqnum: tdb_store fail key [%s]\n", key_str ));
364                 return NT_STATUS_UNSUCCESSFUL;
365         }
366
367         DEBUG(10,("store_cache_seqnum: success [%s][%u @ %u]\n", 
368                 domain->name, domain->sequence_number, 
369                 (uint32)domain->last_seq_check));
370         
371         return NT_STATUS_OK;
372 }
373
374 /*
375   refresh the domain sequence number. If force is True
376   then always refresh it, no matter how recently we fetched it
377 */
378
379 static void refresh_sequence_number(struct winbindd_domain *domain, BOOL force)
380 {
381         NTSTATUS status;
382         unsigned time_diff;
383         time_t t = time(NULL);
384         unsigned cache_time = lp_winbind_cache_time();
385
386         get_cache( domain );
387
388 #if 0   /* JERRY -- disable as the default cache time is now 5 minutes */
389         /* trying to reconnect is expensive, don't do it too often */
390         if (domain->sequence_number == DOM_SEQUENCE_NONE) {
391                 cache_time *= 8;
392         }
393 #endif
394
395         time_diff = t - domain->last_seq_check;
396
397         /* see if we have to refetch the domain sequence number */
398         if (!force && (time_diff < cache_time)) {
399                 DEBUG(10, ("refresh_sequence_number: %s time ok\n", domain->name));
400                 goto done;
401         }
402         
403         /* try to get the sequence number from the tdb cache first */
404         /* this will update the timestamp as well */
405         
406         status = fetch_cache_seqnum( domain, t );
407         if ( NT_STATUS_IS_OK(status) )
408                 goto done;      
409
410         /* important! make sure that we know if this is a native 
411            mode domain or not */
412
413         status = domain->backend->sequence_number(domain, &domain->sequence_number);
414
415         if (!NT_STATUS_IS_OK(status)) {
416                 domain->sequence_number = DOM_SEQUENCE_NONE;
417         }
418         
419         domain->last_status = status;
420         domain->last_seq_check = time(NULL);
421         
422         /* save the new sequence number ni the cache */
423         store_cache_seqnum( domain );
424
425 done:
426         DEBUG(10, ("refresh_sequence_number: %s seq number is now %d\n", 
427                    domain->name, domain->sequence_number));
428
429         return;
430 }
431
432 /*
433   decide if a cache entry has expired
434 */
435 static BOOL centry_expired(struct winbindd_domain *domain, const char *keystr, struct cache_entry *centry)
436 {
437         /* If we've been told to be offline - stay in that state... */
438         if (lp_winbind_offline_logon() && global_winbindd_offline_state) {
439                 DEBUG(10,("centry_expired: Key %s for domain %s valid as winbindd is globally offline.\n",
440                         keystr, domain->name ));
441                 return False;
442         }
443
444         /* when the domain is offline and we havent checked in the last 30
445          * seconds if it has become online again, return the cached entry.
446          * This deals with transient offline states... */
447
448         if (!domain->online && 
449             !NT_STATUS_IS_OK(check_negative_conn_cache(domain->name, domain->dcname))) {
450                 DEBUG(10,("centry_expired: Key %s for domain %s valid as domain is offline.\n",
451                         keystr, domain->name ));
452                 return False;
453         }
454
455         /* if the server is OK and our cache entry came from when it was down then
456            the entry is invalid */
457         if ((domain->sequence_number != DOM_SEQUENCE_NONE) &&  
458             (centry->sequence_number == DOM_SEQUENCE_NONE)) {
459                 DEBUG(10,("centry_expired: Key %s for domain %s invalid sequence.\n",
460                         keystr, domain->name ));
461                 return True;
462         }
463
464         /* if the server is down or the cache entry is not older than the
465            current sequence number then it is OK */
466         if (wcache_server_down(domain) || 
467             centry->sequence_number == domain->sequence_number) {
468                 DEBUG(10,("centry_expired: Key %s for domain %s is good.\n",
469                         keystr, domain->name ));
470                 return False;
471         }
472
473         DEBUG(10,("centry_expired: Key %s for domain %s expired\n",
474                 keystr, domain->name ));
475
476         /* it's expired */
477         return True;
478 }
479
480 static struct cache_entry *wcache_fetch_raw(char *kstr)
481 {
482         TDB_DATA data;
483         struct cache_entry *centry;
484         TDB_DATA key;
485
486         key.dptr = kstr;
487         key.dsize = strlen(kstr);
488         data = tdb_fetch(wcache->tdb, key);
489         if (!data.dptr) {
490                 /* a cache miss */
491                 return NULL;
492         }
493
494         centry = SMB_XMALLOC_P(struct cache_entry);
495         centry->data = (unsigned char *)data.dptr;
496         centry->len = data.dsize;
497         centry->ofs = 0;
498
499         if (centry->len < 8) {
500                 /* huh? corrupt cache? */
501                 DEBUG(10,("wcache_fetch_raw: Corrupt cache for key %s (len < 8) ?\n", kstr));
502                 centry_free(centry);
503                 return NULL;
504         }
505         
506         centry->status = NT_STATUS(centry_uint32(centry));
507         centry->sequence_number = centry_uint32(centry);
508
509         return centry;
510 }
511
512 /*
513   fetch an entry from the cache, with a varargs key. auto-fetch the sequence
514   number and return status
515 */
516 static struct cache_entry *wcache_fetch(struct winbind_cache *cache, 
517                                         struct winbindd_domain *domain,
518                                         const char *format, ...) PRINTF_ATTRIBUTE(3,4);
519 static struct cache_entry *wcache_fetch(struct winbind_cache *cache, 
520                                         struct winbindd_domain *domain,
521                                         const char *format, ...)
522 {
523         va_list ap;
524         char *kstr;
525         struct cache_entry *centry;
526
527         extern BOOL opt_nocache;
528
529         if (opt_nocache) {
530                 return NULL;
531         }
532
533         refresh_sequence_number(domain, False);
534
535         va_start(ap, format);
536         smb_xvasprintf(&kstr, format, ap);
537         va_end(ap);
538
539         centry = wcache_fetch_raw(kstr);
540         if (centry == NULL) {
541                 free(kstr);
542                 return NULL;
543         }
544
545         if (centry_expired(domain, kstr, centry)) {
546
547                 DEBUG(10,("wcache_fetch: entry %s expired for domain %s\n",
548                          kstr, domain->name ));
549
550                 centry_free(centry);
551                 free(kstr);
552                 return NULL;
553         }
554
555         DEBUG(10,("wcache_fetch: returning entry %s for domain %s\n",
556                  kstr, domain->name ));
557
558         free(kstr);
559         return centry;
560 }
561
562 /*
563   make sure we have at least len bytes available in a centry 
564 */
565 static void centry_expand(struct cache_entry *centry, uint32 len)
566 {
567         if (centry->len - centry->ofs >= len)
568                 return;
569         centry->len *= 2;
570         centry->data = SMB_REALLOC(centry->data, centry->len);
571         if (!centry->data) {
572                 DEBUG(0,("out of memory: needed %d bytes in centry_expand\n", centry->len));
573                 smb_panic("out of memory in centry_expand");
574         }
575 }
576
577 /*
578   push a uint32 into a centry 
579 */
580 static void centry_put_uint32(struct cache_entry *centry, uint32 v)
581 {
582         centry_expand(centry, 4);
583         SIVAL(centry->data, centry->ofs, v);
584         centry->ofs += 4;
585 }
586
587 /*
588   push a uint16 into a centry 
589 */
590 static void centry_put_uint16(struct cache_entry *centry, uint16 v)
591 {
592         centry_expand(centry, 2);
593         SIVAL(centry->data, centry->ofs, v);
594         centry->ofs += 2;
595 }
596
597 /*
598   push a uint8 into a centry 
599 */
600 static void centry_put_uint8(struct cache_entry *centry, uint8 v)
601 {
602         centry_expand(centry, 1);
603         SCVAL(centry->data, centry->ofs, v);
604         centry->ofs += 1;
605 }
606
607 /* 
608    push a string into a centry 
609  */
610 static void centry_put_string(struct cache_entry *centry, const char *s)
611 {
612         int len;
613
614         if (!s) {
615                 /* null strings are marked as len 0xFFFF */
616                 centry_put_uint8(centry, 0xFF);
617                 return;
618         }
619
620         len = strlen(s);
621         /* can't handle more than 254 char strings. Truncating is probably best */
622         if (len > 254) {
623                 DEBUG(10,("centry_put_string: truncating len (%d) to: 254\n", len));
624                 len = 254;
625         }
626         centry_put_uint8(centry, len);
627         centry_expand(centry, len);
628         memcpy(centry->data + centry->ofs, s, len);
629         centry->ofs += len;
630 }
631
632 static void centry_put_sid(struct cache_entry *centry, const DOM_SID *sid) 
633 {
634         fstring sid_string;
635         centry_put_string(centry, sid_to_string(sid_string, sid));
636 }
637
638 /*
639   push a NTTIME into a centry 
640 */
641 static void centry_put_nttime(struct cache_entry *centry, NTTIME nt)
642 {
643         centry_expand(centry, 8);
644         SIVAL(centry->data, centry->ofs, nt.low);
645         centry->ofs += 4;
646         SIVAL(centry->data, centry->ofs, nt.high);
647         centry->ofs += 4;
648 }
649
650 /*
651   push a time_t into a centry 
652 */
653 static void centry_put_time(struct cache_entry *centry, time_t t)
654 {
655         centry_expand(centry, sizeof(time_t));
656         SIVAL(centry->data, centry->ofs, t); /* FIXME: is this correct ?? */
657         centry->ofs += sizeof(time_t);
658 }
659
660 /*
661   start a centry for output. When finished, call centry_end()
662 */
663 struct cache_entry *centry_start(struct winbindd_domain *domain, NTSTATUS status)
664 {
665         struct cache_entry *centry;
666
667         if (!wcache->tdb)
668                 return NULL;
669
670         centry = SMB_XMALLOC_P(struct cache_entry);
671
672         centry->len = 8192; /* reasonable default */
673         centry->data = SMB_XMALLOC_ARRAY(uint8, centry->len);
674         centry->ofs = 0;
675         centry->sequence_number = domain->sequence_number;
676         centry_put_uint32(centry, NT_STATUS_V(status));
677         centry_put_uint32(centry, centry->sequence_number);
678         return centry;
679 }
680
681 /*
682   finish a centry and write it to the tdb
683 */
684 static void centry_end(struct cache_entry *centry, const char *format, ...) PRINTF_ATTRIBUTE(2,3);
685 static void centry_end(struct cache_entry *centry, const char *format, ...)
686 {
687         va_list ap;
688         char *kstr;
689         TDB_DATA key, data;
690
691         va_start(ap, format);
692         smb_xvasprintf(&kstr, format, ap);
693         va_end(ap);
694
695         key.dptr = kstr;
696         key.dsize = strlen(kstr);
697         data.dptr = (char *)centry->data;
698         data.dsize = centry->ofs;
699
700         tdb_store(wcache->tdb, key, data, TDB_REPLACE);
701         free(kstr);
702 }
703
704 static void wcache_save_name_to_sid(struct winbindd_domain *domain, 
705                                     NTSTATUS status, const char *domain_name,
706                                     const char *name, const DOM_SID *sid, 
707                                     enum SID_NAME_USE type)
708 {
709         struct cache_entry *centry;
710         fstring uname;
711
712         centry = centry_start(domain, status);
713         if (!centry)
714                 return;
715         centry_put_uint32(centry, type);
716         centry_put_sid(centry, sid);
717         fstrcpy(uname, name);
718         strupper_m(uname);
719         centry_end(centry, "NS/%s/%s", domain_name, uname);
720         DEBUG(10,("wcache_save_name_to_sid: %s -> %s\n", uname,
721                   sid_string_static(sid)));
722         centry_free(centry);
723 }
724
725 static void wcache_save_sid_to_name(struct winbindd_domain *domain, NTSTATUS status, 
726                                     const DOM_SID *sid, const char *domain_name, const char *name, enum SID_NAME_USE type)
727 {
728         struct cache_entry *centry;
729         fstring sid_string;
730
731         centry = centry_start(domain, status);
732         if (!centry)
733                 return;
734         if (NT_STATUS_IS_OK(status)) {
735                 centry_put_uint32(centry, type);
736                 centry_put_string(centry, domain_name);
737                 centry_put_string(centry, name);
738         }
739         centry_end(centry, "SN/%s", sid_to_string(sid_string, sid));
740         DEBUG(10,("wcache_save_sid_to_name: %s -> %s\n", sid_string, name));
741         centry_free(centry);
742 }
743
744
745 static void wcache_save_user(struct winbindd_domain *domain, NTSTATUS status, WINBIND_USERINFO *info)
746 {
747         struct cache_entry *centry;
748         fstring sid_string;
749
750         centry = centry_start(domain, status);
751         if (!centry)
752                 return;
753         centry_put_string(centry, info->acct_name);
754         centry_put_string(centry, info->full_name);
755         centry_put_string(centry, info->homedir);
756         centry_put_string(centry, info->shell);
757         centry_put_sid(centry, &info->user_sid);
758         centry_put_sid(centry, &info->group_sid);
759         centry_end(centry, "U/%s", sid_to_string(sid_string, &info->user_sid));
760         DEBUG(10,("wcache_save_user: %s (acct_name %s)\n", sid_string, info->acct_name));
761         centry_free(centry);
762 }
763
764 static void wcache_save_lockout_policy(struct winbindd_domain *domain, NTSTATUS status, SAM_UNK_INFO_12 *lockout_policy)
765 {
766         struct cache_entry *centry;
767
768         centry = centry_start(domain, status);
769         if (!centry)
770                 return;
771
772         centry_put_nttime(centry, lockout_policy->duration);
773         centry_put_nttime(centry, lockout_policy->reset_count);
774         centry_put_uint16(centry, lockout_policy->bad_attempt_lockout);
775
776         centry_end(centry, "LOC_POL/%s", domain->name);
777         
778         DEBUG(10,("wcache_save_lockout_policy: %s\n", domain->name));
779
780         centry_free(centry);
781 }
782
783 static void wcache_save_password_policy(struct winbindd_domain *domain, NTSTATUS status, SAM_UNK_INFO_1 *policy)
784 {
785         struct cache_entry *centry;
786
787         centry = centry_start(domain, status);
788         if (!centry)
789                 return;
790
791         centry_put_uint16(centry, policy->min_length_password);
792         centry_put_uint16(centry, policy->password_history);
793         centry_put_uint32(centry, policy->password_properties);
794         centry_put_nttime(centry, policy->expire);
795         centry_put_nttime(centry, policy->min_passwordage);
796
797         centry_end(centry, "PWD_POL/%s", domain->name);
798         
799         DEBUG(10,("wcache_save_password_policy: %s\n", domain->name));
800
801         centry_free(centry);
802 }
803
804 NTSTATUS wcache_cached_creds_exist(struct winbindd_domain *domain, const DOM_SID *sid)
805 {
806         struct winbind_cache *cache = get_cache(domain);
807         TDB_DATA data;
808         fstring key_str;
809         uint32 rid;
810
811         if (!cache->tdb) {
812                 return NT_STATUS_INTERNAL_DB_ERROR;
813         }
814
815         if (is_null_sid(sid)) {
816                 return NT_STATUS_INVALID_SID;
817         }
818
819         if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
820                 return NT_STATUS_INVALID_SID;
821         }
822
823         fstr_sprintf(key_str, "CRED/%s", sid_string_static(sid));
824
825         data = tdb_fetch(cache->tdb, make_tdb_data(key_str, strlen(key_str)));
826         if (!data.dptr) {
827                 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
828         }
829
830         return NT_STATUS_OK;
831 }
832
833 /* Lookup creds for a SID */
834 NTSTATUS wcache_get_creds(struct winbindd_domain *domain, 
835                           TALLOC_CTX *mem_ctx, 
836                           const DOM_SID *sid,
837                           const uint8 **cached_nt_pass)
838 {
839         struct winbind_cache *cache = get_cache(domain);
840         struct cache_entry *centry = NULL;
841         NTSTATUS status;
842         time_t t;
843         uint32 rid;
844
845         if (!cache->tdb) {
846                 return NT_STATUS_INTERNAL_DB_ERROR;
847         }
848
849         if (is_null_sid(sid)) {
850                 return NT_STATUS_INVALID_SID;
851         }
852
853         if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
854                 return NT_STATUS_INVALID_SID;
855         }
856
857         centry = wcache_fetch(cache, domain, "CRED/%s", sid_string_static(sid));
858         
859         if (!centry) {
860                 DEBUG(10,("wcache_get_creds: entry for [CRED/%s] not found\n", 
861                         sid_string_static(sid)));
862                 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
863         }
864
865         t = centry_time(centry);
866         *cached_nt_pass = (const uint8 *)centry_string(centry, mem_ctx);
867
868 #if DEBUG_PASSWORD
869         dump_data(100, (const char *)cached_nt_pass, NT_HASH_LEN);
870 #endif
871         status = centry->status;
872
873         DEBUG(10,("wcache_get_creds: [Cached] - cached creds for user %s status: %s\n",
874                 sid_string_static(sid), nt_errstr(status) ));
875
876         centry_free(centry);
877         return status;
878 }
879
880 NTSTATUS wcache_save_creds(struct winbindd_domain *domain, 
881                            TALLOC_CTX *mem_ctx, 
882                            const DOM_SID *sid, 
883                            const uint8 nt_pass[NT_HASH_LEN])
884 {
885         struct cache_entry *centry;
886         fstring sid_string;
887         uint32 rid;
888
889         if (is_null_sid(sid)) {
890                 return NT_STATUS_INVALID_SID;
891         }
892
893         if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
894                 return NT_STATUS_INVALID_SID;
895         }
896
897         centry = centry_start(domain, NT_STATUS_OK);
898         if (!centry) {
899                 return NT_STATUS_INTERNAL_DB_ERROR;
900         }
901
902 #if DEBUG_PASSWORD
903         dump_data(100, (const char *)nt_pass, NT_HASH_LEN);
904 #endif
905
906         centry_put_time(centry, time(NULL));
907         centry_put_string(centry, (const char *)nt_pass);
908         centry_end(centry, "CRED/%s", sid_to_string(sid_string, sid));
909
910         DEBUG(10,("wcache_save_creds: %s\n", sid_string));
911
912         centry_free(centry);
913
914         return NT_STATUS_OK;
915 }
916
917
918 /* Query display info. This is the basic user list fn */
919 static NTSTATUS query_user_list(struct winbindd_domain *domain,
920                                 TALLOC_CTX *mem_ctx,
921                                 uint32 *num_entries, 
922                                 WINBIND_USERINFO **info)
923 {
924         struct winbind_cache *cache = get_cache(domain);
925         struct cache_entry *centry = NULL;
926         NTSTATUS status;
927         unsigned int i, retry;
928
929         if (!cache->tdb)
930                 goto do_query;
931
932         centry = wcache_fetch(cache, domain, "UL/%s", domain->name);
933         if (!centry)
934                 goto do_query;
935
936         *num_entries = centry_uint32(centry);
937         
938         if (*num_entries == 0)
939                 goto do_cached;
940
941         (*info) = TALLOC_ARRAY(mem_ctx, WINBIND_USERINFO, *num_entries);
942         if (! (*info))
943                 smb_panic("query_user_list out of memory");
944         for (i=0; i<(*num_entries); i++) {
945                 (*info)[i].acct_name = centry_string(centry, mem_ctx);
946                 (*info)[i].full_name = centry_string(centry, mem_ctx);
947                 (*info)[i].homedir = centry_string(centry, mem_ctx);
948                 (*info)[i].shell = centry_string(centry, mem_ctx);
949                 centry_sid(centry, mem_ctx, &(*info)[i].user_sid);
950                 centry_sid(centry, mem_ctx, &(*info)[i].group_sid);
951         }
952
953 do_cached:      
954         status = centry->status;
955
956         DEBUG(10,("query_user_list: [Cached] - cached list for domain %s status: %s\n",
957                 domain->name, nt_errstr(status) ));
958
959         centry_free(centry);
960         return status;
961
962 do_query:
963         *num_entries = 0;
964         *info = NULL;
965
966         /* Return status value returned by seq number check */
967
968         if (!NT_STATUS_IS_OK(domain->last_status))
969                 return domain->last_status;
970
971         /* Put the query_user_list() in a retry loop.  There appears to be
972          * some bug either with Windows 2000 or Samba's handling of large
973          * rpc replies.  This manifests itself as sudden disconnection
974          * at a random point in the enumeration of a large (60k) user list.
975          * The retry loop simply tries the operation again. )-:  It's not
976          * pretty but an acceptable workaround until we work out what the
977          * real problem is. */
978
979         retry = 0;
980         do {
981
982                 DEBUG(10,("query_user_list: [Cached] - doing backend query for list for domain %s\n",
983                         domain->name ));
984
985                 status = domain->backend->query_user_list(domain, mem_ctx, num_entries, info);
986                 if (!NT_STATUS_IS_OK(status))
987                         DEBUG(3, ("query_user_list: returned 0x%08x, "
988                                   "retrying\n", NT_STATUS_V(status)));
989                         if (NT_STATUS_EQUAL(status, NT_STATUS_UNSUCCESSFUL)) {
990                                 DEBUG(3, ("query_user_list: flushing "
991                                           "connection cache\n"));
992                                 invalidate_cm_connection(&domain->conn);
993                         }
994
995         } while (NT_STATUS_V(status) == NT_STATUS_V(NT_STATUS_UNSUCCESSFUL) && 
996                  (retry++ < 5));
997
998         /* and save it */
999         refresh_sequence_number(domain, False);
1000         centry = centry_start(domain, status);
1001         if (!centry)
1002                 goto skip_save;
1003         centry_put_uint32(centry, *num_entries);
1004         for (i=0; i<(*num_entries); i++) {
1005                 centry_put_string(centry, (*info)[i].acct_name);
1006                 centry_put_string(centry, (*info)[i].full_name);
1007                 centry_put_string(centry, (*info)[i].homedir);
1008                 centry_put_string(centry, (*info)[i].shell);
1009                 centry_put_sid(centry, &(*info)[i].user_sid);
1010                 centry_put_sid(centry, &(*info)[i].group_sid);
1011                 if (domain->backend->consistent) {
1012                         /* when the backend is consistent we can pre-prime some mappings */
1013                         wcache_save_name_to_sid(domain, NT_STATUS_OK, 
1014                                                 domain->name,
1015                                                 (*info)[i].acct_name, 
1016                                                 &(*info)[i].user_sid,
1017                                                 SID_NAME_USER);
1018                         wcache_save_sid_to_name(domain, NT_STATUS_OK, 
1019                                                 &(*info)[i].user_sid,
1020                                                 domain->name,
1021                                                 (*info)[i].acct_name, 
1022                                                 SID_NAME_USER);
1023                         wcache_save_user(domain, NT_STATUS_OK, &(*info)[i]);
1024                 }
1025         }       
1026         centry_end(centry, "UL/%s", domain->name);
1027         centry_free(centry);
1028
1029 skip_save:
1030         return status;
1031 }
1032
1033 /* list all domain groups */
1034 static NTSTATUS enum_dom_groups(struct winbindd_domain *domain,
1035                                 TALLOC_CTX *mem_ctx,
1036                                 uint32 *num_entries, 
1037                                 struct acct_info **info)
1038 {
1039         struct winbind_cache *cache = get_cache(domain);
1040         struct cache_entry *centry = NULL;
1041         NTSTATUS status;
1042         unsigned int i;
1043
1044         if (!cache->tdb)
1045                 goto do_query;
1046
1047         centry = wcache_fetch(cache, domain, "GL/%s/domain", domain->name);
1048         if (!centry)
1049                 goto do_query;
1050
1051         *num_entries = centry_uint32(centry);
1052         
1053         if (*num_entries == 0)
1054                 goto do_cached;
1055
1056         (*info) = TALLOC_ARRAY(mem_ctx, struct acct_info, *num_entries);
1057         if (! (*info))
1058                 smb_panic("enum_dom_groups out of memory");
1059         for (i=0; i<(*num_entries); i++) {
1060                 fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx));
1061                 fstrcpy((*info)[i].acct_desc, centry_string(centry, mem_ctx));
1062                 (*info)[i].rid = centry_uint32(centry);
1063         }
1064
1065 do_cached:      
1066         status = centry->status;
1067
1068         DEBUG(10,("enum_dom_groups: [Cached] - cached list for domain %s status: %s\n",
1069                 domain->name, nt_errstr(status) ));
1070
1071         centry_free(centry);
1072         return status;
1073
1074 do_query:
1075         *num_entries = 0;
1076         *info = NULL;
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,("enum_dom_groups: [Cached] - doing backend query for list for domain %s\n",
1084                 domain->name ));
1085
1086         status = domain->backend->enum_dom_groups(domain, mem_ctx, num_entries, info);
1087
1088         /* and save it */
1089         refresh_sequence_number(domain, False);
1090         centry = centry_start(domain, status);
1091         if (!centry)
1092                 goto skip_save;
1093         centry_put_uint32(centry, *num_entries);
1094         for (i=0; i<(*num_entries); i++) {
1095                 centry_put_string(centry, (*info)[i].acct_name);
1096                 centry_put_string(centry, (*info)[i].acct_desc);
1097                 centry_put_uint32(centry, (*info)[i].rid);
1098         }       
1099         centry_end(centry, "GL/%s/domain", domain->name);
1100         centry_free(centry);
1101
1102 skip_save:
1103         return status;
1104 }
1105
1106 /* list all domain groups */
1107 static NTSTATUS enum_local_groups(struct winbindd_domain *domain,
1108                                 TALLOC_CTX *mem_ctx,
1109                                 uint32 *num_entries, 
1110                                 struct acct_info **info)
1111 {
1112         struct winbind_cache *cache = get_cache(domain);
1113         struct cache_entry *centry = NULL;
1114         NTSTATUS status;
1115         unsigned int i;
1116
1117         if (!cache->tdb)
1118                 goto do_query;
1119
1120         centry = wcache_fetch(cache, domain, "GL/%s/local", domain->name);
1121         if (!centry)
1122                 goto do_query;
1123
1124         *num_entries = centry_uint32(centry);
1125         
1126         if (*num_entries == 0)
1127                 goto do_cached;
1128
1129         (*info) = TALLOC_ARRAY(mem_ctx, struct acct_info, *num_entries);
1130         if (! (*info))
1131                 smb_panic("enum_dom_groups out of memory");
1132         for (i=0; i<(*num_entries); i++) {
1133                 fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx));
1134                 fstrcpy((*info)[i].acct_desc, centry_string(centry, mem_ctx));
1135                 (*info)[i].rid = centry_uint32(centry);
1136         }
1137
1138 do_cached:      
1139
1140         /* If we are returning cached data and the domain controller
1141            is down then we don't know whether the data is up to date
1142            or not.  Return NT_STATUS_MORE_PROCESSING_REQUIRED to
1143            indicate this. */
1144
1145         if (wcache_server_down(domain)) {
1146                 DEBUG(10, ("enum_local_groups: returning cached user list and server was down\n"));
1147                 status = NT_STATUS_MORE_PROCESSING_REQUIRED;
1148         } else
1149                 status = centry->status;
1150
1151         DEBUG(10,("enum_local_groups: [Cached] - cached list for domain %s status: %s\n",
1152                 domain->name, nt_errstr(status) ));
1153
1154         centry_free(centry);
1155         return status;
1156
1157 do_query:
1158         *num_entries = 0;
1159         *info = NULL;
1160
1161         /* Return status value returned by seq number check */
1162
1163         if (!NT_STATUS_IS_OK(domain->last_status))
1164                 return domain->last_status;
1165
1166         DEBUG(10,("enum_local_groups: [Cached] - doing backend query for list for domain %s\n",
1167                 domain->name ));
1168
1169         status = domain->backend->enum_local_groups(domain, mem_ctx, num_entries, info);
1170
1171         /* and save it */
1172         refresh_sequence_number(domain, False);
1173         centry = centry_start(domain, status);
1174         if (!centry)
1175                 goto skip_save;
1176         centry_put_uint32(centry, *num_entries);
1177         for (i=0; i<(*num_entries); i++) {
1178                 centry_put_string(centry, (*info)[i].acct_name);
1179                 centry_put_string(centry, (*info)[i].acct_desc);
1180                 centry_put_uint32(centry, (*info)[i].rid);
1181         }
1182         centry_end(centry, "GL/%s/local", domain->name);
1183         centry_free(centry);
1184
1185 skip_save:
1186         return status;
1187 }
1188
1189 /* convert a single name to a sid in a domain */
1190 static NTSTATUS name_to_sid(struct winbindd_domain *domain,
1191                             TALLOC_CTX *mem_ctx,
1192                             const char *domain_name,
1193                             const char *name,
1194                             DOM_SID *sid,
1195                             enum SID_NAME_USE *type)
1196 {
1197         struct winbind_cache *cache = get_cache(domain);
1198         struct cache_entry *centry = NULL;
1199         NTSTATUS status;
1200         fstring uname;
1201
1202         if (!cache->tdb)
1203                 goto do_query;
1204
1205         fstrcpy(uname, name);
1206         strupper_m(uname);
1207         centry = wcache_fetch(cache, domain, "NS/%s/%s", domain_name, uname);
1208         if (!centry)
1209                 goto do_query;
1210         *type = (enum SID_NAME_USE)centry_uint32(centry);
1211         status = centry->status;
1212         if (NT_STATUS_IS_OK(status)) {
1213                 centry_sid(centry, mem_ctx, sid);
1214         }
1215
1216         DEBUG(10,("name_to_sid: [Cached] - cached name for domain %s status: %s\n",
1217                 domain->name, nt_errstr(status) ));
1218
1219         centry_free(centry);
1220         return status;
1221
1222 do_query:
1223         ZERO_STRUCTP(sid);
1224
1225         /* If the seq number check indicated that there is a problem
1226          * with this DC, then return that status... except for
1227          * access_denied.  This is special because the dc may be in
1228          * "restrict anonymous = 1" mode, in which case it will deny
1229          * most unauthenticated operations, but *will* allow the LSA
1230          * name-to-sid that we try as a fallback. */
1231
1232         if (!(NT_STATUS_IS_OK(domain->last_status)
1233               || NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)))
1234                 return domain->last_status;
1235
1236         DEBUG(10,("name_to_sid: [Cached] - doing backend query for name for domain %s\n",
1237                 domain->name ));
1238
1239         status = domain->backend->name_to_sid(domain, mem_ctx, domain_name, name, sid, type);
1240
1241         /* and save it */
1242         if (domain->online || !is_null_sid(sid)) {
1243                 wcache_save_name_to_sid(domain, status, domain_name, name, sid, *type);
1244         }
1245
1246         if (NT_STATUS_IS_OK(status)) {
1247                 strupper_m(CONST_DISCARD(char *,domain_name));
1248                 strlower_m(CONST_DISCARD(char *,name));
1249                 wcache_save_sid_to_name(domain, status, sid, domain_name, name, *type);
1250         }
1251
1252         return status;
1253 }
1254
1255 /* convert a sid to a user or group name. The sid is guaranteed to be in the domain
1256    given */
1257 static NTSTATUS sid_to_name(struct winbindd_domain *domain,
1258                             TALLOC_CTX *mem_ctx,
1259                             const DOM_SID *sid,
1260                             char **domain_name,
1261                             char **name,
1262                             enum SID_NAME_USE *type)
1263 {
1264         struct winbind_cache *cache = get_cache(domain);
1265         struct cache_entry *centry = NULL;
1266         NTSTATUS status;
1267         fstring sid_string;
1268
1269         if (!cache->tdb)
1270                 goto do_query;
1271
1272         centry = wcache_fetch(cache, domain, "SN/%s", sid_to_string(sid_string, sid));
1273         if (!centry)
1274                 goto do_query;
1275         if (NT_STATUS_IS_OK(centry->status)) {
1276                 *type = (enum SID_NAME_USE)centry_uint32(centry);
1277                 *domain_name = centry_string(centry, mem_ctx);
1278                 *name = centry_string(centry, mem_ctx);
1279         }
1280         status = centry->status;
1281
1282         DEBUG(10,("sid_to_name: [Cached] - cached name for domain %s status: %s\n",
1283                 domain->name, nt_errstr(status) ));
1284
1285         centry_free(centry);
1286         return status;
1287
1288 do_query:
1289         *name = NULL;
1290         *domain_name = NULL;
1291
1292         /* If the seq number check indicated that there is a problem
1293          * with this DC, then return that status... except for
1294          * access_denied.  This is special because the dc may be in
1295          * "restrict anonymous = 1" mode, in which case it will deny
1296          * most unauthenticated operations, but *will* allow the LSA
1297          * sid-to-name that we try as a fallback. */
1298
1299         if (!(NT_STATUS_IS_OK(domain->last_status)
1300               || NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)))
1301                 return domain->last_status;
1302
1303         DEBUG(10,("sid_to_name: [Cached] - doing backend query for name for domain %s\n",
1304                 domain->name ));
1305
1306         status = domain->backend->sid_to_name(domain, mem_ctx, sid, domain_name, name, type);
1307
1308         /* and save it */
1309         refresh_sequence_number(domain, False);
1310         wcache_save_sid_to_name(domain, status, sid, *domain_name, *name, *type);
1311
1312         /* We can't save the name to sid mapping here, as with sid history a
1313          * later name2sid would give the wrong sid. */
1314
1315         return status;
1316 }
1317
1318 /* Lookup user information from a rid */
1319 static NTSTATUS query_user(struct winbindd_domain *domain, 
1320                            TALLOC_CTX *mem_ctx, 
1321                            const DOM_SID *user_sid, 
1322                            WINBIND_USERINFO *info)
1323 {
1324         struct winbind_cache *cache = get_cache(domain);
1325         struct cache_entry *centry = NULL;
1326         NTSTATUS status;
1327
1328         if (!cache->tdb)
1329                 goto do_query;
1330
1331         centry = wcache_fetch(cache, domain, "U/%s", sid_string_static(user_sid));
1332         
1333         /* If we have an access denied cache entry and a cached info3 in the
1334            samlogon cache then do a query.  This will force the rpc back end
1335            to return the info3 data. */
1336
1337         if (NT_STATUS_V(domain->last_status) == NT_STATUS_V(NT_STATUS_ACCESS_DENIED) &&
1338             netsamlogon_cache_have(user_sid)) {
1339                 DEBUG(10, ("query_user: cached access denied and have cached info3\n"));
1340                 domain->last_status = NT_STATUS_OK;
1341                 centry_free(centry);
1342                 goto do_query;
1343         }
1344         
1345         if (!centry)
1346                 goto do_query;
1347
1348         info->acct_name = centry_string(centry, mem_ctx);
1349         info->full_name = centry_string(centry, mem_ctx);
1350         info->homedir = centry_string(centry, mem_ctx);
1351         info->shell = centry_string(centry, mem_ctx);
1352         centry_sid(centry, mem_ctx, &info->user_sid);
1353         centry_sid(centry, mem_ctx, &info->group_sid);
1354         status = centry->status;
1355
1356         DEBUG(10,("query_user: [Cached] - cached info for domain %s status: %s\n",
1357                 domain->name, nt_errstr(status) ));
1358
1359         centry_free(centry);
1360         return status;
1361
1362 do_query:
1363         ZERO_STRUCTP(info);
1364
1365         /* Return status value returned by seq number check */
1366
1367         if (!NT_STATUS_IS_OK(domain->last_status))
1368                 return domain->last_status;
1369         
1370         DEBUG(10,("sid_to_name: [Cached] - doing backend query for info for domain %s\n",
1371                 domain->name ));
1372
1373         status = domain->backend->query_user(domain, mem_ctx, user_sid, info);
1374
1375         /* and save it */
1376         refresh_sequence_number(domain, False);
1377         wcache_save_user(domain, status, info);
1378
1379         return status;
1380 }
1381
1382
1383 /* Lookup groups a user is a member of. */
1384 static NTSTATUS lookup_usergroups(struct winbindd_domain *domain,
1385                                   TALLOC_CTX *mem_ctx,
1386                                   const DOM_SID *user_sid, 
1387                                   uint32 *num_groups, DOM_SID **user_gids)
1388 {
1389         struct winbind_cache *cache = get_cache(domain);
1390         struct cache_entry *centry = NULL;
1391         NTSTATUS status;
1392         unsigned int i;
1393         fstring sid_string;
1394
1395         if (!cache->tdb)
1396                 goto do_query;
1397
1398         centry = wcache_fetch(cache, domain, "UG/%s", sid_to_string(sid_string, user_sid));
1399         
1400         /* If we have an access denied cache entry and a cached info3 in the
1401            samlogon cache then do a query.  This will force the rpc back end
1402            to return the info3 data. */
1403
1404         if (NT_STATUS_V(domain->last_status) == NT_STATUS_V(NT_STATUS_ACCESS_DENIED) &&
1405             netsamlogon_cache_have(user_sid)) {
1406                 DEBUG(10, ("lookup_usergroups: cached access denied and have cached info3\n"));
1407                 domain->last_status = NT_STATUS_OK;
1408                 centry_free(centry);
1409                 goto do_query;
1410         }
1411         
1412         if (!centry)
1413                 goto do_query;
1414
1415         *num_groups = centry_uint32(centry);
1416         
1417         if (*num_groups == 0)
1418                 goto do_cached;
1419
1420         (*user_gids) = TALLOC_ARRAY(mem_ctx, DOM_SID, *num_groups);
1421         if (! (*user_gids))
1422                 smb_panic("lookup_usergroups out of memory");
1423         for (i=0; i<(*num_groups); i++) {
1424                 centry_sid(centry, mem_ctx, &(*user_gids)[i]);
1425         }
1426
1427 do_cached:      
1428         status = centry->status;
1429
1430         DEBUG(10,("lookup_usergroups: [Cached] - cached info for domain %s status: %s\n",
1431                 domain->name, nt_errstr(status) ));
1432
1433         centry_free(centry);
1434         return status;
1435
1436 do_query:
1437         (*num_groups) = 0;
1438         (*user_gids) = NULL;
1439
1440         /* Return status value returned by seq number check */
1441
1442         if (!NT_STATUS_IS_OK(domain->last_status))
1443                 return domain->last_status;
1444
1445         DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info for domain %s\n",
1446                 domain->name ));
1447
1448         status = domain->backend->lookup_usergroups(domain, mem_ctx, user_sid, num_groups, user_gids);
1449
1450         /* and save it */
1451         refresh_sequence_number(domain, False);
1452         centry = centry_start(domain, status);
1453         if (!centry)
1454                 goto skip_save;
1455         centry_put_uint32(centry, *num_groups);
1456         for (i=0; i<(*num_groups); i++) {
1457                 centry_put_sid(centry, &(*user_gids)[i]);
1458         }       
1459         centry_end(centry, "UG/%s", sid_to_string(sid_string, user_sid));
1460         centry_free(centry);
1461
1462 skip_save:
1463         return status;
1464 }
1465
1466 static NTSTATUS lookup_useraliases(struct winbindd_domain *domain,
1467                                    TALLOC_CTX *mem_ctx,
1468                                    uint32 num_sids, const DOM_SID *sids,
1469                                    uint32 *num_aliases, uint32 **alias_rids)
1470 {
1471         struct winbind_cache *cache = get_cache(domain);
1472         struct cache_entry *centry = NULL;
1473         NTSTATUS status;
1474         char *sidlist = talloc_strdup(mem_ctx, "");
1475         int i;
1476
1477         if (!cache->tdb)
1478                 goto do_query;
1479
1480         if (num_sids == 0) {
1481                 *num_aliases = 0;
1482                 *alias_rids = NULL;
1483                 return NT_STATUS_OK;
1484         }
1485
1486         /* We need to cache indexed by the whole list of SIDs, the aliases
1487          * resulting might come from any of the SIDs. */
1488
1489         for (i=0; i<num_sids; i++) {
1490                 sidlist = talloc_asprintf(mem_ctx, "%s/%s", sidlist,
1491                                           sid_string_static(&sids[i]));
1492                 if (sidlist == NULL)
1493                         return NT_STATUS_NO_MEMORY;
1494         }
1495
1496         centry = wcache_fetch(cache, domain, "UA%s", sidlist);
1497
1498         if (!centry)
1499                 goto do_query;
1500
1501         *num_aliases = centry_uint32(centry);
1502         *alias_rids = NULL;
1503
1504         (*alias_rids) = TALLOC_ARRAY(mem_ctx, uint32, *num_aliases);
1505
1506         if ((*num_aliases != 0) && ((*alias_rids) == NULL)) {
1507                 centry_free(centry);
1508                 return NT_STATUS_NO_MEMORY;
1509         }
1510
1511         for (i=0; i<(*num_aliases); i++)
1512                 (*alias_rids)[i] = centry_uint32(centry);
1513
1514         status = centry->status;
1515
1516         DEBUG(10,("lookup_useraliases: [Cached] - cached info for domain: %s "
1517                   "status %s\n", domain->name, nt_errstr(status)));
1518
1519         centry_free(centry);
1520         return status;
1521
1522  do_query:
1523         (*num_aliases) = 0;
1524         (*alias_rids) = NULL;
1525
1526         if (!NT_STATUS_IS_OK(domain->last_status))
1527                 return domain->last_status;
1528
1529         DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info "
1530                   "for domain %s\n", domain->name ));
1531
1532         status = domain->backend->lookup_useraliases(domain, mem_ctx,
1533                                                      num_sids, sids,
1534                                                      num_aliases, alias_rids);
1535
1536         /* and save it */
1537         refresh_sequence_number(domain, False);
1538         centry = centry_start(domain, status);
1539         if (!centry)
1540                 goto skip_save;
1541         centry_put_uint32(centry, *num_aliases);
1542         for (i=0; i<(*num_aliases); i++)
1543                 centry_put_uint32(centry, (*alias_rids)[i]);
1544         centry_end(centry, "UA%s", sidlist);
1545         centry_free(centry);
1546
1547  skip_save:
1548         return status;
1549 }
1550
1551
1552 static NTSTATUS lookup_groupmem(struct winbindd_domain *domain,
1553                                 TALLOC_CTX *mem_ctx,
1554                                 const DOM_SID *group_sid, uint32 *num_names, 
1555                                 DOM_SID **sid_mem, char ***names, 
1556                                 uint32 **name_types)
1557 {
1558         struct winbind_cache *cache = get_cache(domain);
1559         struct cache_entry *centry = NULL;
1560         NTSTATUS status;
1561         unsigned int i;
1562         fstring sid_string;
1563
1564         if (!cache->tdb)
1565                 goto do_query;
1566
1567         centry = wcache_fetch(cache, domain, "GM/%s", sid_to_string(sid_string, group_sid));
1568         if (!centry)
1569                 goto do_query;
1570
1571         *num_names = centry_uint32(centry);
1572         
1573         if (*num_names == 0)
1574                 goto do_cached;
1575
1576         (*sid_mem) = TALLOC_ARRAY(mem_ctx, DOM_SID, *num_names);
1577         (*names) = TALLOC_ARRAY(mem_ctx, char *, *num_names);
1578         (*name_types) = TALLOC_ARRAY(mem_ctx, uint32, *num_names);
1579
1580         if (! (*sid_mem) || ! (*names) || ! (*name_types)) {
1581                 smb_panic("lookup_groupmem out of memory");
1582         }
1583
1584         for (i=0; i<(*num_names); i++) {
1585                 centry_sid(centry, mem_ctx, &(*sid_mem)[i]);
1586                 (*names)[i] = centry_string(centry, mem_ctx);
1587                 (*name_types)[i] = centry_uint32(centry);
1588         }
1589
1590 do_cached:      
1591         status = centry->status;
1592
1593         DEBUG(10,("lookup_groupmem: [Cached] - cached info for domain %s status: %s\n",
1594                 domain->name, nt_errstr(status)));
1595
1596         centry_free(centry);
1597         return status;
1598
1599 do_query:
1600         (*num_names) = 0;
1601         (*sid_mem) = NULL;
1602         (*names) = NULL;
1603         (*name_types) = NULL;
1604         
1605         /* Return status value returned by seq number check */
1606
1607         if (!NT_STATUS_IS_OK(domain->last_status))
1608                 return domain->last_status;
1609
1610         DEBUG(10,("lookup_groupmem: [Cached] - doing backend query for info for domain %s\n",
1611                 domain->name ));
1612
1613         status = domain->backend->lookup_groupmem(domain, mem_ctx, group_sid, num_names, 
1614                                                   sid_mem, names, name_types);
1615
1616         /* and save it */
1617         refresh_sequence_number(domain, False);
1618         centry = centry_start(domain, status);
1619         if (!centry)
1620                 goto skip_save;
1621         centry_put_uint32(centry, *num_names);
1622         for (i=0; i<(*num_names); i++) {
1623                 centry_put_sid(centry, &(*sid_mem)[i]);
1624                 centry_put_string(centry, (*names)[i]);
1625                 centry_put_uint32(centry, (*name_types)[i]);
1626         }       
1627         centry_end(centry, "GM/%s", sid_to_string(sid_string, group_sid));
1628         centry_free(centry);
1629
1630 skip_save:
1631         return status;
1632 }
1633
1634 /* find the sequence number for a domain */
1635 static NTSTATUS sequence_number(struct winbindd_domain *domain, uint32 *seq)
1636 {
1637         refresh_sequence_number(domain, False);
1638
1639         *seq = domain->sequence_number;
1640
1641         return NT_STATUS_OK;
1642 }
1643
1644 /* enumerate trusted domains 
1645  * (we need to have the list of trustdoms in the cache when we go offline) -
1646  * Guenther */
1647 static NTSTATUS trusted_domains(struct winbindd_domain *domain,
1648                                 TALLOC_CTX *mem_ctx,
1649                                 uint32 *num_domains,
1650                                 char ***names,
1651                                 char ***alt_names,
1652                                 DOM_SID **dom_sids)
1653 {
1654         struct winbind_cache *cache = get_cache(domain);
1655         struct cache_entry *centry = NULL;
1656         NTSTATUS status;
1657         int i;
1658  
1659         if (!cache->tdb)
1660                 goto do_query;
1661  
1662         centry = wcache_fetch(cache, domain, "TRUSTDOMS/%s", domain->name);
1663         
1664         if (!centry) {
1665                 goto do_query;
1666         }
1667  
1668         *num_domains = centry_uint32(centry);
1669         
1670         (*names)        = TALLOC_ARRAY(mem_ctx, char *, *num_domains);
1671         (*alt_names)    = TALLOC_ARRAY(mem_ctx, char *, *num_domains);
1672         (*dom_sids)     = TALLOC_ARRAY(mem_ctx, DOM_SID, *num_domains);
1673  
1674         if (! (*dom_sids) || ! (*names) || ! (*alt_names)) {
1675                 smb_panic("trusted_domains out of memory");
1676         }
1677  
1678         for (i=0; i<(*num_domains); i++) {
1679                 (*names)[i] = centry_string(centry, mem_ctx);
1680                 (*alt_names)[i] = centry_string(centry, mem_ctx);
1681                 centry_sid(centry, mem_ctx, &(*dom_sids)[i]);
1682         }
1683
1684         status = centry->status;
1685  
1686         DEBUG(10,("trusted_domains: [Cached] - cached info for domain %s (%d trusts) status: %s\n",
1687                 domain->name, *num_domains, nt_errstr(status) ));
1688  
1689         centry_free(centry);
1690         return status;
1691  
1692 do_query:
1693         (*num_domains) = 0;
1694         (*dom_sids) = NULL;
1695         (*names) = NULL;
1696         (*alt_names) = NULL;
1697  
1698         /* Return status value returned by seq number check */
1699
1700         if (!NT_STATUS_IS_OK(domain->last_status))
1701                 return domain->last_status;
1702         
1703         DEBUG(10,("trusted_domains: [Cached] - doing backend query for info for domain %s\n",
1704                 domain->name ));
1705  
1706         status = domain->backend->trusted_domains(domain, mem_ctx, num_domains,
1707                                                 names, alt_names, dom_sids);
1708
1709         /* no trusts gives NT_STATUS_NO_MORE_ENTRIES resetting to NT_STATUS_OK
1710          * so that the generic centry handling still applies correctly -
1711          * Guenther*/
1712
1713         if (!NT_STATUS_IS_ERR(status)) {
1714                 status = NT_STATUS_OK;
1715         }
1716
1717         /* and save it */
1718         refresh_sequence_number(domain, False);
1719  
1720         centry = centry_start(domain, status);
1721         if (!centry)
1722                 goto skip_save;
1723
1724         centry_put_uint32(centry, *num_domains);
1725
1726         for (i=0; i<(*num_domains); i++) {
1727                 centry_put_string(centry, (*names)[i]);
1728                 centry_put_string(centry, (*alt_names)[i]);
1729                 centry_put_sid(centry, &(*dom_sids)[i]);
1730         }
1731         
1732         centry_end(centry, "TRUSTDOMS/%s", domain->name);
1733  
1734         centry_free(centry);
1735  
1736 skip_save:
1737         return status;
1738 }       
1739
1740 /* get lockout policy */
1741 static NTSTATUS lockout_policy(struct winbindd_domain *domain,
1742                                TALLOC_CTX *mem_ctx,
1743                                SAM_UNK_INFO_12 *policy){
1744         struct winbind_cache *cache = get_cache(domain);
1745         struct cache_entry *centry = NULL;
1746         NTSTATUS status;
1747  
1748         if (!cache->tdb)
1749                 goto do_query;
1750  
1751         centry = wcache_fetch(cache, domain, "LOC_POL/%s", domain->name);
1752         
1753         if (!centry)
1754                 goto do_query;
1755  
1756         policy->duration = centry_nttime(centry);
1757         policy->reset_count = centry_nttime(centry);
1758         policy->bad_attempt_lockout = centry_uint16(centry);
1759  
1760         status = centry->status;
1761  
1762         DEBUG(10,("lockout_policy: [Cached] - cached info for domain %s status: %s\n",
1763                 domain->name, nt_errstr(status) ));
1764  
1765         centry_free(centry);
1766         return status;
1767  
1768 do_query:
1769         ZERO_STRUCTP(policy);
1770  
1771         /* Return status value returned by seq number check */
1772
1773         if (!NT_STATUS_IS_OK(domain->last_status))
1774                 return domain->last_status;
1775         
1776         DEBUG(10,("lockout_policy: [Cached] - doing backend query for info for domain %s\n",
1777                 domain->name ));
1778  
1779         status = domain->backend->lockout_policy(domain, mem_ctx, policy); 
1780  
1781         /* and save it */
1782         refresh_sequence_number(domain, False);
1783         wcache_save_lockout_policy(domain, status, policy);
1784  
1785         return status;
1786 }
1787  
1788 /* get password policy */
1789 static NTSTATUS password_policy(struct winbindd_domain *domain,
1790                                 TALLOC_CTX *mem_ctx,
1791                                 SAM_UNK_INFO_1 *policy)
1792 {
1793         struct winbind_cache *cache = get_cache(domain);
1794         struct cache_entry *centry = NULL;
1795         NTSTATUS status;
1796
1797         if (!cache->tdb)
1798                 goto do_query;
1799  
1800         centry = wcache_fetch(cache, domain, "PWD_POL/%s", domain->name);
1801         
1802         if (!centry)
1803                 goto do_query;
1804
1805         policy->min_length_password = centry_uint16(centry);
1806         policy->password_history = centry_uint16(centry);
1807         policy->password_properties = centry_uint32(centry);
1808         policy->expire = centry_nttime(centry);
1809         policy->min_passwordage = centry_nttime(centry);
1810
1811         status = centry->status;
1812
1813         DEBUG(10,("lockout_policy: [Cached] - cached info for domain %s status: %s\n",
1814                 domain->name, nt_errstr(status) ));
1815
1816         centry_free(centry);
1817         return status;
1818
1819 do_query:
1820         ZERO_STRUCTP(policy);
1821
1822         /* Return status value returned by seq number check */
1823
1824         if (!NT_STATUS_IS_OK(domain->last_status))
1825                 return domain->last_status;
1826         
1827         DEBUG(10,("password_policy: [Cached] - doing backend query for info for domain %s\n",
1828                 domain->name ));
1829
1830         status = domain->backend->password_policy(domain, mem_ctx, policy); 
1831
1832         /* and save it */
1833         refresh_sequence_number(domain, False);
1834         wcache_save_password_policy(domain, status, policy);
1835
1836         return status;
1837 }
1838
1839
1840 /* Invalidate cached user and group lists coherently */
1841
1842 static int traverse_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf, 
1843                        void *state)
1844 {
1845         if (strncmp(kbuf.dptr, "UL/", 3) == 0 ||
1846             strncmp(kbuf.dptr, "GL/", 3) == 0)
1847                 tdb_delete(the_tdb, kbuf);
1848
1849         return 0;
1850 }
1851
1852 /* Invalidate the getpwnam and getgroups entries for a winbindd domain */
1853
1854 void wcache_invalidate_samlogon(struct winbindd_domain *domain, 
1855                                 NET_USER_INFO_3 *info3)
1856 {
1857         struct winbind_cache *cache;
1858         
1859         if (!domain)
1860                 return;
1861
1862         cache = get_cache(domain);
1863         netsamlogon_clear_cached_user(cache->tdb, info3);
1864 }
1865
1866 void wcache_invalidate_cache(void)
1867 {
1868         struct winbindd_domain *domain;
1869
1870         for (domain = domain_list(); domain; domain = domain->next) {
1871                 struct winbind_cache *cache = get_cache(domain);
1872
1873                 DEBUG(10, ("wcache_invalidate_cache: invalidating cache "
1874                            "entries for %s\n", domain->name));
1875                 if (cache)
1876                         tdb_traverse(cache->tdb, traverse_fn, NULL);
1877         }
1878 }
1879
1880 static BOOL init_wcache(void)
1881 {
1882         if (wcache == NULL) {
1883                 wcache = SMB_XMALLOC_P(struct winbind_cache);
1884                 ZERO_STRUCTP(wcache);
1885         }
1886
1887         if (wcache->tdb != NULL)
1888                 return True;
1889
1890         /* when working offline we must not clear the cache on restart */
1891         wcache->tdb = tdb_open_log(lock_path("winbindd_cache.tdb"),
1892                                 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE, 
1893                                 TDB_DEFAULT /*TDB_CLEAR_IF_FIRST*/, O_RDWR|O_CREAT, 0600);
1894
1895         if (wcache->tdb == NULL) {
1896                 DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
1897                 return False;
1898         }
1899
1900         return True;
1901 }
1902
1903 void cache_store_response(pid_t pid, struct winbindd_response *response)
1904 {
1905         fstring key_str;
1906
1907         if (!init_wcache())
1908                 return;
1909
1910         DEBUG(10, ("Storing response for pid %d, len %d\n",
1911                    pid, response->length));
1912
1913         fstr_sprintf(key_str, "DR/%d", pid);
1914         if (tdb_store(wcache->tdb, string_tdb_data(key_str), 
1915                       make_tdb_data((void *)response, sizeof(*response)),
1916                       TDB_REPLACE) == -1)
1917                 return;
1918
1919         if (response->length == sizeof(*response))
1920                 return;
1921
1922         /* There's extra data */
1923
1924         DEBUG(10, ("Storing extra data: len=%d\n",
1925                    (int)(response->length - sizeof(*response))));
1926
1927         fstr_sprintf(key_str, "DE/%d", pid);
1928         if (tdb_store(wcache->tdb, string_tdb_data(key_str),
1929                       make_tdb_data(response->extra_data.data,
1930                                     response->length - sizeof(*response)),
1931                       TDB_REPLACE) == 0)
1932                 return;
1933
1934         /* We could not store the extra data, make sure the tdb does not
1935          * contain a main record with wrong dangling extra data */
1936
1937         fstr_sprintf(key_str, "DR/%d", pid);
1938         tdb_delete(wcache->tdb, string_tdb_data(key_str));
1939
1940         return;
1941 }
1942
1943 BOOL cache_retrieve_response(pid_t pid, struct winbindd_response * response)
1944 {
1945         TDB_DATA data;
1946         fstring key_str;
1947
1948         if (!init_wcache())
1949                 return False;
1950
1951         DEBUG(10, ("Retrieving response for pid %d\n", pid));
1952
1953         fstr_sprintf(key_str, "DR/%d", pid);
1954         data = tdb_fetch(wcache->tdb, string_tdb_data(key_str));
1955
1956         if (data.dptr == NULL)
1957                 return False;
1958
1959         if (data.dsize != sizeof(*response))
1960                 return False;
1961
1962         memcpy(response, data.dptr, data.dsize);
1963         SAFE_FREE(data.dptr);
1964
1965         if (response->length == sizeof(*response)) {
1966                 response->extra_data.data = NULL;
1967                 return True;
1968         }
1969
1970         /* There's extra data */
1971
1972         DEBUG(10, ("Retrieving extra data length=%d\n",
1973                    (int)(response->length - sizeof(*response))));
1974
1975         fstr_sprintf(key_str, "DE/%d", pid);
1976         data = tdb_fetch(wcache->tdb, string_tdb_data(key_str));
1977
1978         if (data.dptr == NULL) {
1979                 DEBUG(0, ("Did not find extra data\n"));
1980                 return False;
1981         }
1982
1983         if (data.dsize != (response->length - sizeof(*response))) {
1984                 DEBUG(0, ("Invalid extra data length: %d\n", (int)data.dsize));
1985                 SAFE_FREE(data.dptr);
1986                 return False;
1987         }
1988
1989         dump_data(11, data.dptr, data.dsize);
1990
1991         response->extra_data.data = data.dptr;
1992         return True;
1993 }
1994
1995 void cache_cleanup_response(pid_t pid)
1996 {
1997         fstring key_str;
1998
1999         if (!init_wcache())
2000                 return;
2001
2002         fstr_sprintf(key_str, "DR/%d", pid);
2003         tdb_delete(wcache->tdb, string_tdb_data(key_str));
2004
2005         fstr_sprintf(key_str, "DE/%d", pid);
2006         tdb_delete(wcache->tdb, string_tdb_data(key_str));
2007
2008         return;
2009 }
2010
2011
2012 BOOL lookup_cached_sid(TALLOC_CTX *mem_ctx, const DOM_SID *sid,
2013                        const char **domain_name, const char **name,
2014                        enum SID_NAME_USE *type)
2015 {
2016         struct winbindd_domain *domain;
2017         struct winbind_cache *cache;
2018         struct cache_entry *centry = NULL;
2019         NTSTATUS status;
2020
2021         domain = find_lookup_domain_from_sid(sid);
2022         if (domain == NULL) {
2023                 return False;
2024         }
2025
2026         cache = get_cache(domain);
2027
2028         if (cache->tdb == NULL) {
2029                 return False;
2030         }
2031
2032         centry = wcache_fetch(cache, domain, "SN/%s", sid_string_static(sid));
2033         if (centry == NULL) {
2034                 return False;
2035         }
2036
2037         if (NT_STATUS_IS_OK(centry->status)) {
2038                 *type = (enum SID_NAME_USE)centry_uint32(centry);
2039                 *domain_name = centry_string(centry, mem_ctx);
2040                 *name = centry_string(centry, mem_ctx);
2041         }
2042
2043         status = centry->status;
2044         centry_free(centry);
2045         return NT_STATUS_IS_OK(status);
2046 }
2047
2048 BOOL lookup_cached_name(TALLOC_CTX *mem_ctx,
2049                         const char *domain_name,
2050                         const char *name,
2051                         DOM_SID *sid,
2052                         enum SID_NAME_USE *type)
2053 {
2054         struct winbindd_domain *domain;
2055         struct winbind_cache *cache;
2056         struct cache_entry *centry = NULL;
2057         NTSTATUS status;
2058         fstring uname;
2059
2060         domain = find_lookup_domain_from_name(domain_name);
2061         if (domain == NULL) {
2062                 return False;
2063         }
2064
2065         cache = get_cache(domain);
2066
2067         if (cache->tdb == NULL) {
2068                 return False;
2069         }
2070
2071         fstrcpy(uname, name);
2072         strupper_m(uname);
2073         
2074         centry = wcache_fetch(cache, domain, "NS/%s/%s", domain_name, uname);
2075         if (centry == NULL) {
2076                 return False;
2077         }
2078
2079         if (NT_STATUS_IS_OK(centry->status)) {
2080                 *type = (enum SID_NAME_USE)centry_uint32(centry);
2081                 centry_sid(centry, mem_ctx, sid);
2082         }
2083
2084         status = centry->status;
2085         centry_free(centry);
2086         
2087         return NT_STATUS_IS_OK(status);
2088 }
2089
2090 void cache_name2sid(struct winbindd_domain *domain, 
2091                     const char *domain_name, const char *name,
2092                     enum SID_NAME_USE type, const DOM_SID *sid)
2093 {
2094         wcache_save_name_to_sid(domain, NT_STATUS_OK, domain_name, name,
2095                                 sid, type);
2096 }
2097
2098 /* delete all centries that don't have NT_STATUS_OK set */
2099 static int traverse_fn_cleanup(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, 
2100                                TDB_DATA dbuf, void *state)
2101 {
2102         struct cache_entry *centry;
2103
2104         centry = wcache_fetch_raw(kbuf.dptr);
2105         if (!centry) {
2106                 return 0;
2107         }
2108
2109         if (!NT_STATUS_IS_OK(centry->status)) {
2110                 DEBUG(10,("deleting centry %s\n", kbuf.dptr));
2111                 tdb_delete(the_tdb, kbuf);
2112         }
2113
2114         centry_free(centry);
2115         return 0;
2116 }
2117
2118 /* flush the cache */
2119 void wcache_flush_cache(void)
2120 {
2121         extern BOOL opt_nocache;
2122
2123         if (!wcache)
2124                 return;
2125         if (wcache->tdb) {
2126                 tdb_close(wcache->tdb);
2127                 wcache->tdb = NULL;
2128         }
2129         if (opt_nocache)
2130                 return;
2131
2132         /* when working offline we must not clear the cache on restart */
2133         wcache->tdb = tdb_open_log(lock_path("winbindd_cache.tdb"),
2134                                 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE, 
2135                                 TDB_DEFAULT /* TDB_CLEAR_IF_FIRST */, O_RDWR|O_CREAT, 0600);
2136
2137         if (!wcache->tdb) {
2138                 DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
2139         }
2140
2141         tdb_traverse(wcache->tdb, traverse_fn_cleanup, NULL);
2142
2143         DEBUG(10,("wcache_flush_cache success\n"));
2144 }
2145
2146 /* Count cached creds */
2147
2148 static int traverse_fn_cached_creds(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf, 
2149                                     void *state)
2150 {
2151         int *cred_count = (int*)state;
2152  
2153         if (strncmp(kbuf.dptr, "CRED/", 5) == 0) {
2154                 (*cred_count)++;
2155         }
2156         return 0;
2157 }
2158
2159 NTSTATUS wcache_count_cached_creds(struct winbindd_domain *domain, int *count)
2160 {
2161         struct winbind_cache *cache = get_cache(domain);
2162
2163         *count = 0;
2164
2165         if (!cache->tdb) {
2166                 return NT_STATUS_INTERNAL_DB_ERROR;
2167         }
2168  
2169         tdb_traverse(cache->tdb, traverse_fn_cached_creds, (void *)count);
2170
2171         return NT_STATUS_OK;
2172 }
2173
2174 struct cred_list {
2175         struct cred_list *prev, *next;
2176         TDB_DATA key;
2177         fstring name;
2178         time_t created;
2179 };
2180 static struct cred_list *wcache_cred_list;
2181
2182 static int traverse_fn_get_credlist(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf, 
2183                                     void *state)
2184 {
2185         struct cred_list *cred;
2186
2187         if (strncmp(kbuf.dptr, "CRED/", 5) == 0) {
2188
2189                 cred = SMB_MALLOC_P(struct cred_list);
2190                 if (cred == NULL) {
2191                         DEBUG(0,("traverse_fn_remove_first_creds: failed to malloc new entry for list\n"));
2192                         return -1;
2193                 }
2194
2195                 ZERO_STRUCTP(cred);
2196                 
2197                 /* save a copy of the key */
2198                 
2199                 fstrcpy(cred->name, kbuf.dptr);         
2200                 DLIST_ADD(wcache_cred_list, cred);
2201         }
2202         
2203         return 0;
2204 }
2205
2206 NTSTATUS wcache_remove_oldest_cached_creds(struct winbindd_domain *domain, const DOM_SID *sid) 
2207 {
2208         struct winbind_cache *cache = get_cache(domain);
2209         NTSTATUS status;
2210         int ret;
2211         struct cred_list *cred, *oldest = NULL;
2212
2213         if (!cache->tdb) {
2214                 return NT_STATUS_INTERNAL_DB_ERROR;
2215         }
2216
2217         /* we possibly already have an entry */
2218         if (sid && NT_STATUS_IS_OK(wcache_cached_creds_exist(domain, sid))) {
2219         
2220                 fstring key_str;
2221
2222                 DEBUG(11,("we already have an entry, deleting that\n"));
2223
2224                 fstr_sprintf(key_str, "CRED/%s", sid_string_static(sid));
2225
2226                 tdb_delete(cache->tdb, string_tdb_data(key_str));
2227
2228                 return NT_STATUS_OK;
2229         }
2230
2231         ret = tdb_traverse(cache->tdb, traverse_fn_get_credlist, NULL);
2232         if (ret == 0) {
2233                 return NT_STATUS_OK;
2234         } else if ((ret == -1) || (wcache_cred_list == NULL)) {
2235                 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
2236         }
2237
2238         ZERO_STRUCTP(oldest);
2239
2240         for (cred = wcache_cred_list; cred; cred = cred->next) {
2241
2242                 TDB_DATA data;
2243                 time_t t;
2244
2245                 data = tdb_fetch(cache->tdb, make_tdb_data(cred->name, strlen(cred->name)));
2246                 if (!data.dptr) {
2247                         DEBUG(10,("wcache_remove_oldest_cached_creds: entry for [%s] not found\n", 
2248                                 cred->name));
2249                         status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
2250                         goto done;
2251                 }
2252         
2253                 t = IVAL(data.dptr, 0);
2254                 SAFE_FREE(data.dptr);
2255
2256                 if (!oldest) {
2257                         oldest = SMB_MALLOC_P(struct cred_list);
2258                         if (oldest == NULL) {
2259                                 status = NT_STATUS_NO_MEMORY;
2260                                 goto done;
2261                         }
2262
2263                         fstrcpy(oldest->name, cred->name);
2264                         oldest->created = t;
2265                         continue;
2266                 }
2267
2268                 if (t < oldest->created) {
2269                         fstrcpy(oldest->name, cred->name);
2270                         oldest->created = t;
2271                 }
2272         }
2273
2274         if (tdb_delete(cache->tdb, string_tdb_data(oldest->name)) == 0) {
2275                 status = NT_STATUS_OK;
2276         } else {
2277                 status = NT_STATUS_UNSUCCESSFUL;
2278         }
2279 done:
2280         SAFE_FREE(wcache_cred_list);
2281         SAFE_FREE(oldest);
2282         
2283         return status;
2284 }
2285
2286 /* Change the global online/offline state. */
2287 BOOL set_global_winbindd_state_offline(void)
2288 {
2289         TDB_DATA data;
2290         int err;
2291
2292         DEBUG(10,("set_global_winbindd_state_offline: offline requested.\n"));
2293
2294         /* Only go offline if someone has created
2295            the key "WINBINDD_OFFLINE" in the cache tdb. */
2296
2297         if (wcache == NULL || wcache->tdb == NULL) {
2298                 DEBUG(10,("set_global_winbindd_state_offline: wcache not open yet.\n"));
2299                 return False;
2300         }
2301
2302         if (!lp_winbind_offline_logon()) {
2303                 DEBUG(10,("set_global_winbindd_state_offline: rejecting.\n"));
2304                 return False;
2305         }
2306
2307         if (global_winbindd_offline_state) {
2308                 /* Already offline. */
2309                 return True;
2310         }
2311
2312         wcache->tdb->ecode = 0;
2313
2314         data = tdb_fetch_bystring( wcache->tdb, "WINBINDD_OFFLINE" );
2315
2316         /* As this is a key with no data we don't need to free, we
2317            check for existence by looking at tdb_err. */
2318
2319         err = tdb_error(wcache->tdb);
2320
2321         if (err == TDB_ERR_NOEXIST) {
2322                 DEBUG(10,("set_global_winbindd_state_offline: offline state not set.\n"));
2323                 return False;
2324         } else {
2325                 DEBUG(10,("set_global_winbindd_state_offline: offline state set.\n"));
2326                 global_winbindd_offline_state = True;
2327                 return True;
2328         }
2329 }
2330
2331 void set_global_winbindd_state_online(void)
2332 {
2333         DEBUG(10,("set_global_winbindd_state_online: online requested.\n"));
2334
2335         if (!lp_winbind_offline_logon()) {
2336                 DEBUG(10,("set_global_winbindd_state_online: rejecting.\n"));
2337                 return;
2338         }
2339
2340         if (!global_winbindd_offline_state) {
2341                 /* Already online. */
2342                 return;
2343         }
2344         global_winbindd_offline_state = False;
2345
2346         if (!wcache->tdb) {
2347                 return;
2348         }
2349
2350         /* Ensure there is no key "WINBINDD_OFFLINE" in the cache tdb. */
2351         tdb_delete_bystring(wcache->tdb, "WINBINDD_OFFLINE");
2352 }
2353
2354 BOOL get_global_winbindd_state_online(void)
2355 {
2356         return global_winbindd_offline_state;
2357 }
2358
2359 /* the cache backend methods are exposed via this structure */
2360 struct winbindd_methods cache_methods = {
2361         True,
2362         query_user_list,
2363         enum_dom_groups,
2364         enum_local_groups,
2365         name_to_sid,
2366         sid_to_name,
2367         query_user,
2368         lookup_usergroups,
2369         lookup_useraliases,
2370         lookup_groupmem,
2371         sequence_number,
2372         lockout_policy,
2373         password_policy,
2374         trusted_domains
2375 };