r16790: Fix memleak.
[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 %u bytes, have %u\n", 
233                          (unsigned int)sizeof(time_t), (unsigned int)(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 ((sid_string == NULL) || (!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 -> %s\n", domain_name, 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         SAFE_FREE(data.dptr);
831         return NT_STATUS_OK;
832 }
833
834 /* Lookup creds for a SID */
835 NTSTATUS wcache_get_creds(struct winbindd_domain *domain, 
836                           TALLOC_CTX *mem_ctx, 
837                           const DOM_SID *sid,
838                           const uint8 **cached_nt_pass)
839 {
840         struct winbind_cache *cache = get_cache(domain);
841         struct cache_entry *centry = NULL;
842         NTSTATUS status;
843         time_t t;
844         uint32 rid;
845
846         if (!cache->tdb) {
847                 return NT_STATUS_INTERNAL_DB_ERROR;
848         }
849
850         if (is_null_sid(sid)) {
851                 return NT_STATUS_INVALID_SID;
852         }
853
854         if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
855                 return NT_STATUS_INVALID_SID;
856         }
857
858         centry = wcache_fetch(cache, domain, "CRED/%s", sid_string_static(sid));
859         
860         if (!centry) {
861                 DEBUG(10,("wcache_get_creds: entry for [CRED/%s] not found\n", 
862                         sid_string_static(sid)));
863                 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
864         }
865
866         t = centry_time(centry);
867         *cached_nt_pass = (const uint8 *)centry_string(centry, mem_ctx);
868
869 #if DEBUG_PASSWORD
870         dump_data(100, (const char *)cached_nt_pass, NT_HASH_LEN);
871 #endif
872         status = centry->status;
873
874         DEBUG(10,("wcache_get_creds: [Cached] - cached creds for user %s status: %s\n",
875                 sid_string_static(sid), nt_errstr(status) ));
876
877         centry_free(centry);
878         return status;
879 }
880
881 NTSTATUS wcache_save_creds(struct winbindd_domain *domain, 
882                            TALLOC_CTX *mem_ctx, 
883                            const DOM_SID *sid, 
884                            const uint8 nt_pass[NT_HASH_LEN])
885 {
886         struct cache_entry *centry;
887         fstring sid_string;
888         uint32 rid;
889
890         if (is_null_sid(sid)) {
891                 return NT_STATUS_INVALID_SID;
892         }
893
894         if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
895                 return NT_STATUS_INVALID_SID;
896         }
897
898         centry = centry_start(domain, NT_STATUS_OK);
899         if (!centry) {
900                 return NT_STATUS_INTERNAL_DB_ERROR;
901         }
902
903 #if DEBUG_PASSWORD
904         dump_data(100, (const char *)nt_pass, NT_HASH_LEN);
905 #endif
906
907         centry_put_time(centry, time(NULL));
908         centry_put_string(centry, (const char *)nt_pass);
909         centry_end(centry, "CRED/%s", sid_to_string(sid_string, sid));
910
911         DEBUG(10,("wcache_save_creds: %s\n", sid_string));
912
913         centry_free(centry);
914
915         return NT_STATUS_OK;
916 }
917
918
919 /* Query display info. This is the basic user list fn */
920 static NTSTATUS query_user_list(struct winbindd_domain *domain,
921                                 TALLOC_CTX *mem_ctx,
922                                 uint32 *num_entries, 
923                                 WINBIND_USERINFO **info)
924 {
925         struct winbind_cache *cache = get_cache(domain);
926         struct cache_entry *centry = NULL;
927         NTSTATUS status;
928         unsigned int i, retry;
929
930         if (!cache->tdb)
931                 goto do_query;
932
933         centry = wcache_fetch(cache, domain, "UL/%s", domain->name);
934         if (!centry)
935                 goto do_query;
936
937         *num_entries = centry_uint32(centry);
938         
939         if (*num_entries == 0)
940                 goto do_cached;
941
942         (*info) = TALLOC_ARRAY(mem_ctx, WINBIND_USERINFO, *num_entries);
943         if (! (*info))
944                 smb_panic("query_user_list out of memory");
945         for (i=0; i<(*num_entries); i++) {
946                 (*info)[i].acct_name = centry_string(centry, mem_ctx);
947                 (*info)[i].full_name = centry_string(centry, mem_ctx);
948                 (*info)[i].homedir = centry_string(centry, mem_ctx);
949                 (*info)[i].shell = centry_string(centry, mem_ctx);
950                 centry_sid(centry, mem_ctx, &(*info)[i].user_sid);
951                 centry_sid(centry, mem_ctx, &(*info)[i].group_sid);
952         }
953
954 do_cached:      
955         status = centry->status;
956
957         DEBUG(10,("query_user_list: [Cached] - cached list for domain %s status: %s\n",
958                 domain->name, nt_errstr(status) ));
959
960         centry_free(centry);
961         return status;
962
963 do_query:
964         *num_entries = 0;
965         *info = NULL;
966
967         /* Return status value returned by seq number check */
968
969         if (!NT_STATUS_IS_OK(domain->last_status))
970                 return domain->last_status;
971
972         /* Put the query_user_list() in a retry loop.  There appears to be
973          * some bug either with Windows 2000 or Samba's handling of large
974          * rpc replies.  This manifests itself as sudden disconnection
975          * at a random point in the enumeration of a large (60k) user list.
976          * The retry loop simply tries the operation again. )-:  It's not
977          * pretty but an acceptable workaround until we work out what the
978          * real problem is. */
979
980         retry = 0;
981         do {
982
983                 DEBUG(10,("query_user_list: [Cached] - doing backend query for list for domain %s\n",
984                         domain->name ));
985
986                 status = domain->backend->query_user_list(domain, mem_ctx, num_entries, info);
987                 if (!NT_STATUS_IS_OK(status))
988                         DEBUG(3, ("query_user_list: returned 0x%08x, "
989                                   "retrying\n", NT_STATUS_V(status)));
990                         if (NT_STATUS_EQUAL(status, NT_STATUS_UNSUCCESSFUL)) {
991                                 DEBUG(3, ("query_user_list: flushing "
992                                           "connection cache\n"));
993                                 invalidate_cm_connection(&domain->conn);
994                         }
995
996         } while (NT_STATUS_V(status) == NT_STATUS_V(NT_STATUS_UNSUCCESSFUL) && 
997                  (retry++ < 5));
998
999         /* and save it */
1000         refresh_sequence_number(domain, False);
1001         centry = centry_start(domain, status);
1002         if (!centry)
1003                 goto skip_save;
1004         centry_put_uint32(centry, *num_entries);
1005         for (i=0; i<(*num_entries); i++) {
1006                 centry_put_string(centry, (*info)[i].acct_name);
1007                 centry_put_string(centry, (*info)[i].full_name);
1008                 centry_put_string(centry, (*info)[i].homedir);
1009                 centry_put_string(centry, (*info)[i].shell);
1010                 centry_put_sid(centry, &(*info)[i].user_sid);
1011                 centry_put_sid(centry, &(*info)[i].group_sid);
1012                 if (domain->backend->consistent) {
1013                         /* when the backend is consistent we can pre-prime some mappings */
1014                         wcache_save_name_to_sid(domain, NT_STATUS_OK, 
1015                                                 domain->name,
1016                                                 (*info)[i].acct_name, 
1017                                                 &(*info)[i].user_sid,
1018                                                 SID_NAME_USER);
1019                         wcache_save_sid_to_name(domain, NT_STATUS_OK, 
1020                                                 &(*info)[i].user_sid,
1021                                                 domain->name,
1022                                                 (*info)[i].acct_name, 
1023                                                 SID_NAME_USER);
1024                         wcache_save_user(domain, NT_STATUS_OK, &(*info)[i]);
1025                 }
1026         }       
1027         centry_end(centry, "UL/%s", domain->name);
1028         centry_free(centry);
1029
1030 skip_save:
1031         return status;
1032 }
1033
1034 /* list all domain groups */
1035 static NTSTATUS enum_dom_groups(struct winbindd_domain *domain,
1036                                 TALLOC_CTX *mem_ctx,
1037                                 uint32 *num_entries, 
1038                                 struct acct_info **info)
1039 {
1040         struct winbind_cache *cache = get_cache(domain);
1041         struct cache_entry *centry = NULL;
1042         NTSTATUS status;
1043         unsigned int i;
1044
1045         if (!cache->tdb)
1046                 goto do_query;
1047
1048         centry = wcache_fetch(cache, domain, "GL/%s/domain", domain->name);
1049         if (!centry)
1050                 goto do_query;
1051
1052         *num_entries = centry_uint32(centry);
1053         
1054         if (*num_entries == 0)
1055                 goto do_cached;
1056
1057         (*info) = TALLOC_ARRAY(mem_ctx, struct acct_info, *num_entries);
1058         if (! (*info))
1059                 smb_panic("enum_dom_groups out of memory");
1060         for (i=0; i<(*num_entries); i++) {
1061                 fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx));
1062                 fstrcpy((*info)[i].acct_desc, centry_string(centry, mem_ctx));
1063                 (*info)[i].rid = centry_uint32(centry);
1064         }
1065
1066 do_cached:      
1067         status = centry->status;
1068
1069         DEBUG(10,("enum_dom_groups: [Cached] - cached list for domain %s status: %s\n",
1070                 domain->name, nt_errstr(status) ));
1071
1072         centry_free(centry);
1073         return status;
1074
1075 do_query:
1076         *num_entries = 0;
1077         *info = NULL;
1078
1079         /* Return status value returned by seq number check */
1080
1081         if (!NT_STATUS_IS_OK(domain->last_status))
1082                 return domain->last_status;
1083
1084         DEBUG(10,("enum_dom_groups: [Cached] - doing backend query for list for domain %s\n",
1085                 domain->name ));
1086
1087         status = domain->backend->enum_dom_groups(domain, mem_ctx, num_entries, info);
1088
1089         /* and save it */
1090         refresh_sequence_number(domain, False);
1091         centry = centry_start(domain, status);
1092         if (!centry)
1093                 goto skip_save;
1094         centry_put_uint32(centry, *num_entries);
1095         for (i=0; i<(*num_entries); i++) {
1096                 centry_put_string(centry, (*info)[i].acct_name);
1097                 centry_put_string(centry, (*info)[i].acct_desc);
1098                 centry_put_uint32(centry, (*info)[i].rid);
1099         }       
1100         centry_end(centry, "GL/%s/domain", domain->name);
1101         centry_free(centry);
1102
1103 skip_save:
1104         return status;
1105 }
1106
1107 /* list all domain groups */
1108 static NTSTATUS enum_local_groups(struct winbindd_domain *domain,
1109                                 TALLOC_CTX *mem_ctx,
1110                                 uint32 *num_entries, 
1111                                 struct acct_info **info)
1112 {
1113         struct winbind_cache *cache = get_cache(domain);
1114         struct cache_entry *centry = NULL;
1115         NTSTATUS status;
1116         unsigned int i;
1117
1118         if (!cache->tdb)
1119                 goto do_query;
1120
1121         centry = wcache_fetch(cache, domain, "GL/%s/local", domain->name);
1122         if (!centry)
1123                 goto do_query;
1124
1125         *num_entries = centry_uint32(centry);
1126         
1127         if (*num_entries == 0)
1128                 goto do_cached;
1129
1130         (*info) = TALLOC_ARRAY(mem_ctx, struct acct_info, *num_entries);
1131         if (! (*info))
1132                 smb_panic("enum_dom_groups out of memory");
1133         for (i=0; i<(*num_entries); i++) {
1134                 fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx));
1135                 fstrcpy((*info)[i].acct_desc, centry_string(centry, mem_ctx));
1136                 (*info)[i].rid = centry_uint32(centry);
1137         }
1138
1139 do_cached:      
1140
1141         /* If we are returning cached data and the domain controller
1142            is down then we don't know whether the data is up to date
1143            or not.  Return NT_STATUS_MORE_PROCESSING_REQUIRED to
1144            indicate this. */
1145
1146         if (wcache_server_down(domain)) {
1147                 DEBUG(10, ("enum_local_groups: returning cached user list and server was down\n"));
1148                 status = NT_STATUS_MORE_PROCESSING_REQUIRED;
1149         } else
1150                 status = centry->status;
1151
1152         DEBUG(10,("enum_local_groups: [Cached] - cached list for domain %s status: %s\n",
1153                 domain->name, nt_errstr(status) ));
1154
1155         centry_free(centry);
1156         return status;
1157
1158 do_query:
1159         *num_entries = 0;
1160         *info = NULL;
1161
1162         /* Return status value returned by seq number check */
1163
1164         if (!NT_STATUS_IS_OK(domain->last_status))
1165                 return domain->last_status;
1166
1167         DEBUG(10,("enum_local_groups: [Cached] - doing backend query for list for domain %s\n",
1168                 domain->name ));
1169
1170         status = domain->backend->enum_local_groups(domain, mem_ctx, num_entries, info);
1171
1172         /* and save it */
1173         refresh_sequence_number(domain, False);
1174         centry = centry_start(domain, status);
1175         if (!centry)
1176                 goto skip_save;
1177         centry_put_uint32(centry, *num_entries);
1178         for (i=0; i<(*num_entries); i++) {
1179                 centry_put_string(centry, (*info)[i].acct_name);
1180                 centry_put_string(centry, (*info)[i].acct_desc);
1181                 centry_put_uint32(centry, (*info)[i].rid);
1182         }
1183         centry_end(centry, "GL/%s/local", domain->name);
1184         centry_free(centry);
1185
1186 skip_save:
1187         return status;
1188 }
1189
1190 /* convert a single name to a sid in a domain */
1191 static NTSTATUS name_to_sid(struct winbindd_domain *domain,
1192                             TALLOC_CTX *mem_ctx,
1193                             const char *domain_name,
1194                             const char *name,
1195                             DOM_SID *sid,
1196                             enum SID_NAME_USE *type)
1197 {
1198         struct winbind_cache *cache = get_cache(domain);
1199         struct cache_entry *centry = NULL;
1200         NTSTATUS status;
1201         fstring uname;
1202
1203         if (!cache->tdb)
1204                 goto do_query;
1205
1206         fstrcpy(uname, name);
1207         strupper_m(uname);
1208         centry = wcache_fetch(cache, domain, "NS/%s/%s", domain_name, uname);
1209         if (!centry)
1210                 goto do_query;
1211         *type = (enum SID_NAME_USE)centry_uint32(centry);
1212         status = centry->status;
1213         if (NT_STATUS_IS_OK(status)) {
1214                 centry_sid(centry, mem_ctx, sid);
1215         }
1216
1217         DEBUG(10,("name_to_sid: [Cached] - cached name for domain %s status: %s\n",
1218                 domain->name, nt_errstr(status) ));
1219
1220         centry_free(centry);
1221         return status;
1222
1223 do_query:
1224         ZERO_STRUCTP(sid);
1225
1226         /* If the seq number check indicated that there is a problem
1227          * with this DC, then return that status... except for
1228          * access_denied.  This is special because the dc may be in
1229          * "restrict anonymous = 1" mode, in which case it will deny
1230          * most unauthenticated operations, but *will* allow the LSA
1231          * name-to-sid that we try as a fallback. */
1232
1233         if (!(NT_STATUS_IS_OK(domain->last_status)
1234               || NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)))
1235                 return domain->last_status;
1236
1237         DEBUG(10,("name_to_sid: [Cached] - doing backend query for name for domain %s\n",
1238                 domain->name ));
1239
1240         status = domain->backend->name_to_sid(domain, mem_ctx, domain_name, name, sid, type);
1241
1242         /* and save it */
1243         if (domain->online || !is_null_sid(sid)) {
1244                 wcache_save_name_to_sid(domain, status, domain_name, name, sid, *type);
1245         }
1246
1247         if (NT_STATUS_IS_OK(status)) {
1248                 strupper_m(CONST_DISCARD(char *,domain_name));
1249                 strlower_m(CONST_DISCARD(char *,name));
1250                 wcache_save_sid_to_name(domain, status, sid, domain_name, name, *type);
1251         }
1252
1253         return status;
1254 }
1255
1256 /* convert a sid to a user or group name. The sid is guaranteed to be in the domain
1257    given */
1258 static NTSTATUS sid_to_name(struct winbindd_domain *domain,
1259                             TALLOC_CTX *mem_ctx,
1260                             const DOM_SID *sid,
1261                             char **domain_name,
1262                             char **name,
1263                             enum SID_NAME_USE *type)
1264 {
1265         struct winbind_cache *cache = get_cache(domain);
1266         struct cache_entry *centry = NULL;
1267         NTSTATUS status;
1268         fstring sid_string;
1269
1270         if (!cache->tdb)
1271                 goto do_query;
1272
1273         centry = wcache_fetch(cache, domain, "SN/%s", sid_to_string(sid_string, sid));
1274         if (!centry)
1275                 goto do_query;
1276         if (NT_STATUS_IS_OK(centry->status)) {
1277                 *type = (enum SID_NAME_USE)centry_uint32(centry);
1278                 *domain_name = centry_string(centry, mem_ctx);
1279                 *name = centry_string(centry, mem_ctx);
1280         }
1281         status = centry->status;
1282
1283         DEBUG(10,("sid_to_name: [Cached] - cached name for domain %s status: %s\n",
1284                 domain->name, nt_errstr(status) ));
1285
1286         centry_free(centry);
1287         return status;
1288
1289 do_query:
1290         *name = NULL;
1291         *domain_name = NULL;
1292
1293         /* If the seq number check indicated that there is a problem
1294          * with this DC, then return that status... except for
1295          * access_denied.  This is special because the dc may be in
1296          * "restrict anonymous = 1" mode, in which case it will deny
1297          * most unauthenticated operations, but *will* allow the LSA
1298          * sid-to-name that we try as a fallback. */
1299
1300         if (!(NT_STATUS_IS_OK(domain->last_status)
1301               || NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)))
1302                 return domain->last_status;
1303
1304         DEBUG(10,("sid_to_name: [Cached] - doing backend query for name for domain %s\n",
1305                 domain->name ));
1306
1307         status = domain->backend->sid_to_name(domain, mem_ctx, sid, domain_name, name, type);
1308
1309         /* and save it */
1310         refresh_sequence_number(domain, False);
1311         wcache_save_sid_to_name(domain, status, sid, *domain_name, *name, *type);
1312
1313         /* We can't save the name to sid mapping here, as with sid history a
1314          * later name2sid would give the wrong sid. */
1315
1316         return status;
1317 }
1318
1319 /* Lookup user information from a rid */
1320 static NTSTATUS query_user(struct winbindd_domain *domain, 
1321                            TALLOC_CTX *mem_ctx, 
1322                            const DOM_SID *user_sid, 
1323                            WINBIND_USERINFO *info)
1324 {
1325         struct winbind_cache *cache = get_cache(domain);
1326         struct cache_entry *centry = NULL;
1327         NTSTATUS status;
1328
1329         if (!cache->tdb)
1330                 goto do_query;
1331
1332         centry = wcache_fetch(cache, domain, "U/%s", sid_string_static(user_sid));
1333         
1334         /* If we have an access denied cache entry and a cached info3 in the
1335            samlogon cache then do a query.  This will force the rpc back end
1336            to return the info3 data. */
1337
1338         if (NT_STATUS_V(domain->last_status) == NT_STATUS_V(NT_STATUS_ACCESS_DENIED) &&
1339             netsamlogon_cache_have(user_sid)) {
1340                 DEBUG(10, ("query_user: cached access denied and have cached info3\n"));
1341                 domain->last_status = NT_STATUS_OK;
1342                 centry_free(centry);
1343                 goto do_query;
1344         }
1345         
1346         if (!centry)
1347                 goto do_query;
1348
1349         info->acct_name = centry_string(centry, mem_ctx);
1350         info->full_name = centry_string(centry, mem_ctx);
1351         info->homedir = centry_string(centry, mem_ctx);
1352         info->shell = centry_string(centry, mem_ctx);
1353         centry_sid(centry, mem_ctx, &info->user_sid);
1354         centry_sid(centry, mem_ctx, &info->group_sid);
1355         status = centry->status;
1356
1357         DEBUG(10,("query_user: [Cached] - cached info for domain %s status: %s\n",
1358                 domain->name, nt_errstr(status) ));
1359
1360         centry_free(centry);
1361         return status;
1362
1363 do_query:
1364         ZERO_STRUCTP(info);
1365
1366         /* Return status value returned by seq number check */
1367
1368         if (!NT_STATUS_IS_OK(domain->last_status))
1369                 return domain->last_status;
1370         
1371         DEBUG(10,("sid_to_name: [Cached] - doing backend query for info for domain %s\n",
1372                 domain->name ));
1373
1374         status = domain->backend->query_user(domain, mem_ctx, user_sid, info);
1375
1376         /* and save it */
1377         refresh_sequence_number(domain, False);
1378         wcache_save_user(domain, status, info);
1379
1380         return status;
1381 }
1382
1383
1384 /* Lookup groups a user is a member of. */
1385 static NTSTATUS lookup_usergroups(struct winbindd_domain *domain,
1386                                   TALLOC_CTX *mem_ctx,
1387                                   const DOM_SID *user_sid, 
1388                                   uint32 *num_groups, DOM_SID **user_gids)
1389 {
1390         struct winbind_cache *cache = get_cache(domain);
1391         struct cache_entry *centry = NULL;
1392         NTSTATUS status;
1393         unsigned int i;
1394         fstring sid_string;
1395
1396         if (!cache->tdb)
1397                 goto do_query;
1398
1399         centry = wcache_fetch(cache, domain, "UG/%s", sid_to_string(sid_string, user_sid));
1400         
1401         /* If we have an access denied cache entry and a cached info3 in the
1402            samlogon cache then do a query.  This will force the rpc back end
1403            to return the info3 data. */
1404
1405         if (NT_STATUS_V(domain->last_status) == NT_STATUS_V(NT_STATUS_ACCESS_DENIED) &&
1406             netsamlogon_cache_have(user_sid)) {
1407                 DEBUG(10, ("lookup_usergroups: cached access denied and have cached info3\n"));
1408                 domain->last_status = NT_STATUS_OK;
1409                 centry_free(centry);
1410                 goto do_query;
1411         }
1412         
1413         if (!centry)
1414                 goto do_query;
1415
1416         *num_groups = centry_uint32(centry);
1417         
1418         if (*num_groups == 0)
1419                 goto do_cached;
1420
1421         (*user_gids) = TALLOC_ARRAY(mem_ctx, DOM_SID, *num_groups);
1422         if (! (*user_gids))
1423                 smb_panic("lookup_usergroups out of memory");
1424         for (i=0; i<(*num_groups); i++) {
1425                 centry_sid(centry, mem_ctx, &(*user_gids)[i]);
1426         }
1427
1428 do_cached:      
1429         status = centry->status;
1430
1431         DEBUG(10,("lookup_usergroups: [Cached] - cached info for domain %s status: %s\n",
1432                 domain->name, nt_errstr(status) ));
1433
1434         centry_free(centry);
1435         return status;
1436
1437 do_query:
1438         (*num_groups) = 0;
1439         (*user_gids) = NULL;
1440
1441         /* Return status value returned by seq number check */
1442
1443         if (!NT_STATUS_IS_OK(domain->last_status))
1444                 return domain->last_status;
1445
1446         DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info for domain %s\n",
1447                 domain->name ));
1448
1449         status = domain->backend->lookup_usergroups(domain, mem_ctx, user_sid, num_groups, user_gids);
1450
1451         /* and save it */
1452         refresh_sequence_number(domain, False);
1453         centry = centry_start(domain, status);
1454         if (!centry)
1455                 goto skip_save;
1456         centry_put_uint32(centry, *num_groups);
1457         for (i=0; i<(*num_groups); i++) {
1458                 centry_put_sid(centry, &(*user_gids)[i]);
1459         }       
1460         centry_end(centry, "UG/%s", sid_to_string(sid_string, user_sid));
1461         centry_free(centry);
1462
1463 skip_save:
1464         return status;
1465 }
1466
1467 static NTSTATUS lookup_useraliases(struct winbindd_domain *domain,
1468                                    TALLOC_CTX *mem_ctx,
1469                                    uint32 num_sids, const DOM_SID *sids,
1470                                    uint32 *num_aliases, uint32 **alias_rids)
1471 {
1472         struct winbind_cache *cache = get_cache(domain);
1473         struct cache_entry *centry = NULL;
1474         NTSTATUS status;
1475         char *sidlist = talloc_strdup(mem_ctx, "");
1476         int i;
1477
1478         if (!cache->tdb)
1479                 goto do_query;
1480
1481         if (num_sids == 0) {
1482                 *num_aliases = 0;
1483                 *alias_rids = NULL;
1484                 return NT_STATUS_OK;
1485         }
1486
1487         /* We need to cache indexed by the whole list of SIDs, the aliases
1488          * resulting might come from any of the SIDs. */
1489
1490         for (i=0; i<num_sids; i++) {
1491                 sidlist = talloc_asprintf(mem_ctx, "%s/%s", sidlist,
1492                                           sid_string_static(&sids[i]));
1493                 if (sidlist == NULL)
1494                         return NT_STATUS_NO_MEMORY;
1495         }
1496
1497         centry = wcache_fetch(cache, domain, "UA%s", sidlist);
1498
1499         if (!centry)
1500                 goto do_query;
1501
1502         *num_aliases = centry_uint32(centry);
1503         *alias_rids = NULL;
1504
1505         (*alias_rids) = TALLOC_ARRAY(mem_ctx, uint32, *num_aliases);
1506
1507         if ((*num_aliases != 0) && ((*alias_rids) == NULL)) {
1508                 centry_free(centry);
1509                 return NT_STATUS_NO_MEMORY;
1510         }
1511
1512         for (i=0; i<(*num_aliases); i++)
1513                 (*alias_rids)[i] = centry_uint32(centry);
1514
1515         status = centry->status;
1516
1517         DEBUG(10,("lookup_useraliases: [Cached] - cached info for domain: %s "
1518                   "status %s\n", domain->name, nt_errstr(status)));
1519
1520         centry_free(centry);
1521         return status;
1522
1523  do_query:
1524         (*num_aliases) = 0;
1525         (*alias_rids) = NULL;
1526
1527         if (!NT_STATUS_IS_OK(domain->last_status))
1528                 return domain->last_status;
1529
1530         DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info "
1531                   "for domain %s\n", domain->name ));
1532
1533         status = domain->backend->lookup_useraliases(domain, mem_ctx,
1534                                                      num_sids, sids,
1535                                                      num_aliases, alias_rids);
1536
1537         /* and save it */
1538         refresh_sequence_number(domain, False);
1539         centry = centry_start(domain, status);
1540         if (!centry)
1541                 goto skip_save;
1542         centry_put_uint32(centry, *num_aliases);
1543         for (i=0; i<(*num_aliases); i++)
1544                 centry_put_uint32(centry, (*alias_rids)[i]);
1545         centry_end(centry, "UA%s", sidlist);
1546         centry_free(centry);
1547
1548  skip_save:
1549         return status;
1550 }
1551
1552
1553 static NTSTATUS lookup_groupmem(struct winbindd_domain *domain,
1554                                 TALLOC_CTX *mem_ctx,
1555                                 const DOM_SID *group_sid, uint32 *num_names, 
1556                                 DOM_SID **sid_mem, char ***names, 
1557                                 uint32 **name_types)
1558 {
1559         struct winbind_cache *cache = get_cache(domain);
1560         struct cache_entry *centry = NULL;
1561         NTSTATUS status;
1562         unsigned int i;
1563         fstring sid_string;
1564
1565         if (!cache->tdb)
1566                 goto do_query;
1567
1568         centry = wcache_fetch(cache, domain, "GM/%s", sid_to_string(sid_string, group_sid));
1569         if (!centry)
1570                 goto do_query;
1571
1572         *num_names = centry_uint32(centry);
1573         
1574         if (*num_names == 0)
1575                 goto do_cached;
1576
1577         (*sid_mem) = TALLOC_ARRAY(mem_ctx, DOM_SID, *num_names);
1578         (*names) = TALLOC_ARRAY(mem_ctx, char *, *num_names);
1579         (*name_types) = TALLOC_ARRAY(mem_ctx, uint32, *num_names);
1580
1581         if (! (*sid_mem) || ! (*names) || ! (*name_types)) {
1582                 smb_panic("lookup_groupmem out of memory");
1583         }
1584
1585         for (i=0; i<(*num_names); i++) {
1586                 centry_sid(centry, mem_ctx, &(*sid_mem)[i]);
1587                 (*names)[i] = centry_string(centry, mem_ctx);
1588                 (*name_types)[i] = centry_uint32(centry);
1589         }
1590
1591 do_cached:      
1592         status = centry->status;
1593
1594         DEBUG(10,("lookup_groupmem: [Cached] - cached info for domain %s status: %s\n",
1595                 domain->name, nt_errstr(status)));
1596
1597         centry_free(centry);
1598         return status;
1599
1600 do_query:
1601         (*num_names) = 0;
1602         (*sid_mem) = NULL;
1603         (*names) = NULL;
1604         (*name_types) = NULL;
1605         
1606         /* Return status value returned by seq number check */
1607
1608         if (!NT_STATUS_IS_OK(domain->last_status))
1609                 return domain->last_status;
1610
1611         DEBUG(10,("lookup_groupmem: [Cached] - doing backend query for info for domain %s\n",
1612                 domain->name ));
1613
1614         status = domain->backend->lookup_groupmem(domain, mem_ctx, group_sid, num_names, 
1615                                                   sid_mem, names, name_types);
1616
1617         /* and save it */
1618         refresh_sequence_number(domain, False);
1619         centry = centry_start(domain, status);
1620         if (!centry)
1621                 goto skip_save;
1622         centry_put_uint32(centry, *num_names);
1623         for (i=0; i<(*num_names); i++) {
1624                 centry_put_sid(centry, &(*sid_mem)[i]);
1625                 centry_put_string(centry, (*names)[i]);
1626                 centry_put_uint32(centry, (*name_types)[i]);
1627         }       
1628         centry_end(centry, "GM/%s", sid_to_string(sid_string, group_sid));
1629         centry_free(centry);
1630
1631 skip_save:
1632         return status;
1633 }
1634
1635 /* find the sequence number for a domain */
1636 static NTSTATUS sequence_number(struct winbindd_domain *domain, uint32 *seq)
1637 {
1638         refresh_sequence_number(domain, False);
1639
1640         *seq = domain->sequence_number;
1641
1642         return NT_STATUS_OK;
1643 }
1644
1645 /* enumerate trusted domains 
1646  * (we need to have the list of trustdoms in the cache when we go offline) -
1647  * Guenther */
1648 static NTSTATUS trusted_domains(struct winbindd_domain *domain,
1649                                 TALLOC_CTX *mem_ctx,
1650                                 uint32 *num_domains,
1651                                 char ***names,
1652                                 char ***alt_names,
1653                                 DOM_SID **dom_sids)
1654 {
1655         struct winbind_cache *cache = get_cache(domain);
1656         struct cache_entry *centry = NULL;
1657         NTSTATUS status;
1658         int i;
1659  
1660         if (!cache->tdb)
1661                 goto do_query;
1662  
1663         centry = wcache_fetch(cache, domain, "TRUSTDOMS/%s", domain->name);
1664         
1665         if (!centry) {
1666                 goto do_query;
1667         }
1668  
1669         *num_domains = centry_uint32(centry);
1670         
1671         (*names)        = TALLOC_ARRAY(mem_ctx, char *, *num_domains);
1672         (*alt_names)    = TALLOC_ARRAY(mem_ctx, char *, *num_domains);
1673         (*dom_sids)     = TALLOC_ARRAY(mem_ctx, DOM_SID, *num_domains);
1674  
1675         if (! (*dom_sids) || ! (*names) || ! (*alt_names)) {
1676                 smb_panic("trusted_domains out of memory");
1677         }
1678  
1679         for (i=0; i<(*num_domains); i++) {
1680                 (*names)[i] = centry_string(centry, mem_ctx);
1681                 (*alt_names)[i] = centry_string(centry, mem_ctx);
1682                 centry_sid(centry, mem_ctx, &(*dom_sids)[i]);
1683         }
1684
1685         status = centry->status;
1686  
1687         DEBUG(10,("trusted_domains: [Cached] - cached info for domain %s (%d trusts) status: %s\n",
1688                 domain->name, *num_domains, nt_errstr(status) ));
1689  
1690         centry_free(centry);
1691         return status;
1692  
1693 do_query:
1694         (*num_domains) = 0;
1695         (*dom_sids) = NULL;
1696         (*names) = NULL;
1697         (*alt_names) = NULL;
1698  
1699         /* Return status value returned by seq number check */
1700
1701         if (!NT_STATUS_IS_OK(domain->last_status))
1702                 return domain->last_status;
1703         
1704         DEBUG(10,("trusted_domains: [Cached] - doing backend query for info for domain %s\n",
1705                 domain->name ));
1706  
1707         status = domain->backend->trusted_domains(domain, mem_ctx, num_domains,
1708                                                 names, alt_names, dom_sids);
1709
1710         /* no trusts gives NT_STATUS_NO_MORE_ENTRIES resetting to NT_STATUS_OK
1711          * so that the generic centry handling still applies correctly -
1712          * Guenther*/
1713
1714         if (!NT_STATUS_IS_ERR(status)) {
1715                 status = NT_STATUS_OK;
1716         }
1717
1718         /* and save it */
1719         refresh_sequence_number(domain, False);
1720  
1721         centry = centry_start(domain, status);
1722         if (!centry)
1723                 goto skip_save;
1724
1725         centry_put_uint32(centry, *num_domains);
1726
1727         for (i=0; i<(*num_domains); i++) {
1728                 centry_put_string(centry, (*names)[i]);
1729                 centry_put_string(centry, (*alt_names)[i]);
1730                 centry_put_sid(centry, &(*dom_sids)[i]);
1731         }
1732         
1733         centry_end(centry, "TRUSTDOMS/%s", domain->name);
1734  
1735         centry_free(centry);
1736  
1737 skip_save:
1738         return status;
1739 }       
1740
1741 /* get lockout policy */
1742 static NTSTATUS lockout_policy(struct winbindd_domain *domain,
1743                                TALLOC_CTX *mem_ctx,
1744                                SAM_UNK_INFO_12 *policy){
1745         struct winbind_cache *cache = get_cache(domain);
1746         struct cache_entry *centry = NULL;
1747         NTSTATUS status;
1748  
1749         if (!cache->tdb)
1750                 goto do_query;
1751  
1752         centry = wcache_fetch(cache, domain, "LOC_POL/%s", domain->name);
1753         
1754         if (!centry)
1755                 goto do_query;
1756  
1757         policy->duration = centry_nttime(centry);
1758         policy->reset_count = centry_nttime(centry);
1759         policy->bad_attempt_lockout = centry_uint16(centry);
1760  
1761         status = centry->status;
1762  
1763         DEBUG(10,("lockout_policy: [Cached] - cached info for domain %s status: %s\n",
1764                 domain->name, nt_errstr(status) ));
1765  
1766         centry_free(centry);
1767         return status;
1768  
1769 do_query:
1770         ZERO_STRUCTP(policy);
1771  
1772         /* Return status value returned by seq number check */
1773
1774         if (!NT_STATUS_IS_OK(domain->last_status))
1775                 return domain->last_status;
1776         
1777         DEBUG(10,("lockout_policy: [Cached] - doing backend query for info for domain %s\n",
1778                 domain->name ));
1779  
1780         status = domain->backend->lockout_policy(domain, mem_ctx, policy); 
1781  
1782         /* and save it */
1783         refresh_sequence_number(domain, False);
1784         wcache_save_lockout_policy(domain, status, policy);
1785  
1786         return status;
1787 }
1788  
1789 /* get password policy */
1790 static NTSTATUS password_policy(struct winbindd_domain *domain,
1791                                 TALLOC_CTX *mem_ctx,
1792                                 SAM_UNK_INFO_1 *policy)
1793 {
1794         struct winbind_cache *cache = get_cache(domain);
1795         struct cache_entry *centry = NULL;
1796         NTSTATUS status;
1797
1798         if (!cache->tdb)
1799                 goto do_query;
1800  
1801         centry = wcache_fetch(cache, domain, "PWD_POL/%s", domain->name);
1802         
1803         if (!centry)
1804                 goto do_query;
1805
1806         policy->min_length_password = centry_uint16(centry);
1807         policy->password_history = centry_uint16(centry);
1808         policy->password_properties = centry_uint32(centry);
1809         policy->expire = centry_nttime(centry);
1810         policy->min_passwordage = centry_nttime(centry);
1811
1812         status = centry->status;
1813
1814         DEBUG(10,("lockout_policy: [Cached] - cached info for domain %s status: %s\n",
1815                 domain->name, nt_errstr(status) ));
1816
1817         centry_free(centry);
1818         return status;
1819
1820 do_query:
1821         ZERO_STRUCTP(policy);
1822
1823         /* Return status value returned by seq number check */
1824
1825         if (!NT_STATUS_IS_OK(domain->last_status))
1826                 return domain->last_status;
1827         
1828         DEBUG(10,("password_policy: [Cached] - doing backend query for info for domain %s\n",
1829                 domain->name ));
1830
1831         status = domain->backend->password_policy(domain, mem_ctx, policy); 
1832
1833         /* and save it */
1834         refresh_sequence_number(domain, False);
1835         wcache_save_password_policy(domain, status, policy);
1836
1837         return status;
1838 }
1839
1840
1841 /* Invalidate cached user and group lists coherently */
1842
1843 static int traverse_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf, 
1844                        void *state)
1845 {
1846         if (strncmp(kbuf.dptr, "UL/", 3) == 0 ||
1847             strncmp(kbuf.dptr, "GL/", 3) == 0)
1848                 tdb_delete(the_tdb, kbuf);
1849
1850         return 0;
1851 }
1852
1853 /* Invalidate the getpwnam and getgroups entries for a winbindd domain */
1854
1855 void wcache_invalidate_samlogon(struct winbindd_domain *domain, 
1856                                 NET_USER_INFO_3 *info3)
1857 {
1858         struct winbind_cache *cache;
1859         
1860         if (!domain)
1861                 return;
1862
1863         cache = get_cache(domain);
1864         netsamlogon_clear_cached_user(cache->tdb, info3);
1865 }
1866
1867 void wcache_invalidate_cache(void)
1868 {
1869         struct winbindd_domain *domain;
1870
1871         for (domain = domain_list(); domain; domain = domain->next) {
1872                 struct winbind_cache *cache = get_cache(domain);
1873
1874                 DEBUG(10, ("wcache_invalidate_cache: invalidating cache "
1875                            "entries for %s\n", domain->name));
1876                 if (cache)
1877                         tdb_traverse(cache->tdb, traverse_fn, NULL);
1878         }
1879 }
1880
1881 static BOOL init_wcache(void)
1882 {
1883         if (wcache == NULL) {
1884                 wcache = SMB_XMALLOC_P(struct winbind_cache);
1885                 ZERO_STRUCTP(wcache);
1886         }
1887
1888         if (wcache->tdb != NULL)
1889                 return True;
1890
1891         /* when working offline we must not clear the cache on restart */
1892         wcache->tdb = tdb_open_log(lock_path("winbindd_cache.tdb"),
1893                                 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE, 
1894                                 TDB_DEFAULT /*TDB_CLEAR_IF_FIRST*/, O_RDWR|O_CREAT, 0600);
1895
1896         if (wcache->tdb == NULL) {
1897                 DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
1898                 return False;
1899         }
1900
1901         return True;
1902 }
1903
1904 void cache_store_response(pid_t pid, struct winbindd_response *response)
1905 {
1906         fstring key_str;
1907
1908         if (!init_wcache())
1909                 return;
1910
1911         DEBUG(10, ("Storing response for pid %d, len %d\n",
1912                    pid, response->length));
1913
1914         fstr_sprintf(key_str, "DR/%d", pid);
1915         if (tdb_store(wcache->tdb, string_tdb_data(key_str), 
1916                       make_tdb_data((void *)response, sizeof(*response)),
1917                       TDB_REPLACE) == -1)
1918                 return;
1919
1920         if (response->length == sizeof(*response))
1921                 return;
1922
1923         /* There's extra data */
1924
1925         DEBUG(10, ("Storing extra data: len=%d\n",
1926                    (int)(response->length - sizeof(*response))));
1927
1928         fstr_sprintf(key_str, "DE/%d", pid);
1929         if (tdb_store(wcache->tdb, string_tdb_data(key_str),
1930                       make_tdb_data(response->extra_data.data,
1931                                     response->length - sizeof(*response)),
1932                       TDB_REPLACE) == 0)
1933                 return;
1934
1935         /* We could not store the extra data, make sure the tdb does not
1936          * contain a main record with wrong dangling extra data */
1937
1938         fstr_sprintf(key_str, "DR/%d", pid);
1939         tdb_delete(wcache->tdb, string_tdb_data(key_str));
1940
1941         return;
1942 }
1943
1944 BOOL cache_retrieve_response(pid_t pid, struct winbindd_response * response)
1945 {
1946         TDB_DATA data;
1947         fstring key_str;
1948
1949         if (!init_wcache())
1950                 return False;
1951
1952         DEBUG(10, ("Retrieving response for pid %d\n", pid));
1953
1954         fstr_sprintf(key_str, "DR/%d", pid);
1955         data = tdb_fetch(wcache->tdb, string_tdb_data(key_str));
1956
1957         if (data.dptr == NULL)
1958                 return False;
1959
1960         if (data.dsize != sizeof(*response))
1961                 return False;
1962
1963         memcpy(response, data.dptr, data.dsize);
1964         SAFE_FREE(data.dptr);
1965
1966         if (response->length == sizeof(*response)) {
1967                 response->extra_data.data = NULL;
1968                 return True;
1969         }
1970
1971         /* There's extra data */
1972
1973         DEBUG(10, ("Retrieving extra data length=%d\n",
1974                    (int)(response->length - sizeof(*response))));
1975
1976         fstr_sprintf(key_str, "DE/%d", pid);
1977         data = tdb_fetch(wcache->tdb, string_tdb_data(key_str));
1978
1979         if (data.dptr == NULL) {
1980                 DEBUG(0, ("Did not find extra data\n"));
1981                 return False;
1982         }
1983
1984         if (data.dsize != (response->length - sizeof(*response))) {
1985                 DEBUG(0, ("Invalid extra data length: %d\n", (int)data.dsize));
1986                 SAFE_FREE(data.dptr);
1987                 return False;
1988         }
1989
1990         dump_data(11, data.dptr, data.dsize);
1991
1992         response->extra_data.data = data.dptr;
1993         return True;
1994 }
1995
1996 void cache_cleanup_response(pid_t pid)
1997 {
1998         fstring key_str;
1999
2000         if (!init_wcache())
2001                 return;
2002
2003         fstr_sprintf(key_str, "DR/%d", pid);
2004         tdb_delete(wcache->tdb, string_tdb_data(key_str));
2005
2006         fstr_sprintf(key_str, "DE/%d", pid);
2007         tdb_delete(wcache->tdb, string_tdb_data(key_str));
2008
2009         return;
2010 }
2011
2012
2013 BOOL lookup_cached_sid(TALLOC_CTX *mem_ctx, const DOM_SID *sid,
2014                        const char **domain_name, const char **name,
2015                        enum SID_NAME_USE *type)
2016 {
2017         struct winbindd_domain *domain;
2018         struct winbind_cache *cache;
2019         struct cache_entry *centry = NULL;
2020         NTSTATUS status;
2021
2022         domain = find_lookup_domain_from_sid(sid);
2023         if (domain == NULL) {
2024                 return False;
2025         }
2026
2027         cache = get_cache(domain);
2028
2029         if (cache->tdb == NULL) {
2030                 return False;
2031         }
2032
2033         centry = wcache_fetch(cache, domain, "SN/%s", sid_string_static(sid));
2034         if (centry == NULL) {
2035                 return False;
2036         }
2037
2038         if (NT_STATUS_IS_OK(centry->status)) {
2039                 *type = (enum SID_NAME_USE)centry_uint32(centry);
2040                 *domain_name = centry_string(centry, mem_ctx);
2041                 *name = centry_string(centry, mem_ctx);
2042         }
2043
2044         status = centry->status;
2045         centry_free(centry);
2046         return NT_STATUS_IS_OK(status);
2047 }
2048
2049 BOOL lookup_cached_name(TALLOC_CTX *mem_ctx,
2050                         const char *domain_name,
2051                         const char *name,
2052                         DOM_SID *sid,
2053                         enum SID_NAME_USE *type)
2054 {
2055         struct winbindd_domain *domain;
2056         struct winbind_cache *cache;
2057         struct cache_entry *centry = NULL;
2058         NTSTATUS status;
2059         fstring uname;
2060
2061         domain = find_lookup_domain_from_name(domain_name);
2062         if (domain == NULL) {
2063                 return False;
2064         }
2065
2066         cache = get_cache(domain);
2067
2068         if (cache->tdb == NULL) {
2069                 return False;
2070         }
2071
2072         fstrcpy(uname, name);
2073         strupper_m(uname);
2074         
2075         centry = wcache_fetch(cache, domain, "NS/%s/%s", domain_name, uname);
2076         if (centry == NULL) {
2077                 return False;
2078         }
2079
2080         if (NT_STATUS_IS_OK(centry->status)) {
2081                 *type = (enum SID_NAME_USE)centry_uint32(centry);
2082                 centry_sid(centry, mem_ctx, sid);
2083         }
2084
2085         status = centry->status;
2086         centry_free(centry);
2087         
2088         return NT_STATUS_IS_OK(status);
2089 }
2090
2091 void cache_name2sid(struct winbindd_domain *domain, 
2092                     const char *domain_name, const char *name,
2093                     enum SID_NAME_USE type, const DOM_SID *sid)
2094 {
2095         wcache_save_name_to_sid(domain, NT_STATUS_OK, domain_name, name,
2096                                 sid, type);
2097 }
2098
2099 /* delete all centries that don't have NT_STATUS_OK set */
2100 static int traverse_fn_cleanup(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, 
2101                                TDB_DATA dbuf, void *state)
2102 {
2103         struct cache_entry *centry;
2104
2105         centry = wcache_fetch_raw(kbuf.dptr);
2106         if (!centry) {
2107                 return 0;
2108         }
2109
2110         if (!NT_STATUS_IS_OK(centry->status)) {
2111                 DEBUG(10,("deleting centry %s\n", kbuf.dptr));
2112                 tdb_delete(the_tdb, kbuf);
2113         }
2114
2115         centry_free(centry);
2116         return 0;
2117 }
2118
2119 /* flush the cache */
2120 void wcache_flush_cache(void)
2121 {
2122         extern BOOL opt_nocache;
2123
2124         if (!wcache)
2125                 return;
2126         if (wcache->tdb) {
2127                 tdb_close(wcache->tdb);
2128                 wcache->tdb = NULL;
2129         }
2130         if (opt_nocache)
2131                 return;
2132
2133         /* when working offline we must not clear the cache on restart */
2134         wcache->tdb = tdb_open_log(lock_path("winbindd_cache.tdb"),
2135                                 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE, 
2136                                 TDB_DEFAULT /* TDB_CLEAR_IF_FIRST */, O_RDWR|O_CREAT, 0600);
2137
2138         if (!wcache->tdb) {
2139                 DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
2140                 return;
2141         }
2142
2143         tdb_traverse(wcache->tdb, traverse_fn_cleanup, NULL);
2144
2145         DEBUG(10,("wcache_flush_cache success\n"));
2146 }
2147
2148 /* Count cached creds */
2149
2150 static int traverse_fn_cached_creds(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf, 
2151                                     void *state)
2152 {
2153         int *cred_count = (int*)state;
2154  
2155         if (strncmp(kbuf.dptr, "CRED/", 5) == 0) {
2156                 (*cred_count)++;
2157         }
2158         return 0;
2159 }
2160
2161 NTSTATUS wcache_count_cached_creds(struct winbindd_domain *domain, int *count)
2162 {
2163         struct winbind_cache *cache = get_cache(domain);
2164
2165         *count = 0;
2166
2167         if (!cache->tdb) {
2168                 return NT_STATUS_INTERNAL_DB_ERROR;
2169         }
2170  
2171         tdb_traverse(cache->tdb, traverse_fn_cached_creds, (void *)count);
2172
2173         return NT_STATUS_OK;
2174 }
2175
2176 struct cred_list {
2177         struct cred_list *prev, *next;
2178         TDB_DATA key;
2179         fstring name;
2180         time_t created;
2181 };
2182 static struct cred_list *wcache_cred_list;
2183
2184 static int traverse_fn_get_credlist(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf, 
2185                                     void *state)
2186 {
2187         struct cred_list *cred;
2188
2189         if (strncmp(kbuf.dptr, "CRED/", 5) == 0) {
2190
2191                 cred = SMB_MALLOC_P(struct cred_list);
2192                 if (cred == NULL) {
2193                         DEBUG(0,("traverse_fn_remove_first_creds: failed to malloc new entry for list\n"));
2194                         return -1;
2195                 }
2196
2197                 ZERO_STRUCTP(cred);
2198                 
2199                 /* save a copy of the key */
2200                 
2201                 fstrcpy(cred->name, kbuf.dptr);         
2202                 DLIST_ADD(wcache_cred_list, cred);
2203         }
2204         
2205         return 0;
2206 }
2207
2208 NTSTATUS wcache_remove_oldest_cached_creds(struct winbindd_domain *domain, const DOM_SID *sid) 
2209 {
2210         struct winbind_cache *cache = get_cache(domain);
2211         NTSTATUS status;
2212         int ret;
2213         struct cred_list *cred, *oldest = NULL;
2214
2215         if (!cache->tdb) {
2216                 return NT_STATUS_INTERNAL_DB_ERROR;
2217         }
2218
2219         /* we possibly already have an entry */
2220         if (sid && NT_STATUS_IS_OK(wcache_cached_creds_exist(domain, sid))) {
2221         
2222                 fstring key_str;
2223
2224                 DEBUG(11,("we already have an entry, deleting that\n"));
2225
2226                 fstr_sprintf(key_str, "CRED/%s", sid_string_static(sid));
2227
2228                 tdb_delete(cache->tdb, string_tdb_data(key_str));
2229
2230                 return NT_STATUS_OK;
2231         }
2232
2233         ret = tdb_traverse(cache->tdb, traverse_fn_get_credlist, NULL);
2234         if (ret == 0) {
2235                 return NT_STATUS_OK;
2236         } else if ((ret == -1) || (wcache_cred_list == NULL)) {
2237                 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
2238         }
2239
2240         ZERO_STRUCTP(oldest);
2241
2242         for (cred = wcache_cred_list; cred; cred = cred->next) {
2243
2244                 TDB_DATA data;
2245                 time_t t;
2246
2247                 data = tdb_fetch(cache->tdb, make_tdb_data(cred->name, strlen(cred->name)));
2248                 if (!data.dptr) {
2249                         DEBUG(10,("wcache_remove_oldest_cached_creds: entry for [%s] not found\n", 
2250                                 cred->name));
2251                         status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
2252                         goto done;
2253                 }
2254         
2255                 t = IVAL(data.dptr, 0);
2256                 SAFE_FREE(data.dptr);
2257
2258                 if (!oldest) {
2259                         oldest = SMB_MALLOC_P(struct cred_list);
2260                         if (oldest == NULL) {
2261                                 status = NT_STATUS_NO_MEMORY;
2262                                 goto done;
2263                         }
2264
2265                         fstrcpy(oldest->name, cred->name);
2266                         oldest->created = t;
2267                         continue;
2268                 }
2269
2270                 if (t < oldest->created) {
2271                         fstrcpy(oldest->name, cred->name);
2272                         oldest->created = t;
2273                 }
2274         }
2275
2276         if (tdb_delete(cache->tdb, string_tdb_data(oldest->name)) == 0) {
2277                 status = NT_STATUS_OK;
2278         } else {
2279                 status = NT_STATUS_UNSUCCESSFUL;
2280         }
2281 done:
2282         SAFE_FREE(wcache_cred_list);
2283         SAFE_FREE(oldest);
2284         
2285         return status;
2286 }
2287
2288 /* Change the global online/offline state. */
2289 BOOL set_global_winbindd_state_offline(void)
2290 {
2291         TDB_DATA data;
2292         int err;
2293
2294         DEBUG(10,("set_global_winbindd_state_offline: offline requested.\n"));
2295
2296         /* Only go offline if someone has created
2297            the key "WINBINDD_OFFLINE" in the cache tdb. */
2298
2299         if (wcache == NULL || wcache->tdb == NULL) {
2300                 DEBUG(10,("set_global_winbindd_state_offline: wcache not open yet.\n"));
2301                 return False;
2302         }
2303
2304         if (!lp_winbind_offline_logon()) {
2305                 DEBUG(10,("set_global_winbindd_state_offline: rejecting.\n"));
2306                 return False;
2307         }
2308
2309         if (global_winbindd_offline_state) {
2310                 /* Already offline. */
2311                 return True;
2312         }
2313
2314         wcache->tdb->ecode = 0;
2315
2316         data = tdb_fetch_bystring( wcache->tdb, "WINBINDD_OFFLINE" );
2317
2318         /* As this is a key with no data we don't need to free, we
2319            check for existence by looking at tdb_err. */
2320
2321         err = tdb_error(wcache->tdb);
2322
2323         if (err == TDB_ERR_NOEXIST) {
2324                 DEBUG(10,("set_global_winbindd_state_offline: offline state not set.\n"));
2325                 return False;
2326         } else {
2327                 DEBUG(10,("set_global_winbindd_state_offline: offline state set.\n"));
2328                 global_winbindd_offline_state = True;
2329                 return True;
2330         }
2331 }
2332
2333 void set_global_winbindd_state_online(void)
2334 {
2335         DEBUG(10,("set_global_winbindd_state_online: online requested.\n"));
2336
2337         if (!lp_winbind_offline_logon()) {
2338                 DEBUG(10,("set_global_winbindd_state_online: rejecting.\n"));
2339                 return;
2340         }
2341
2342         if (!global_winbindd_offline_state) {
2343                 /* Already online. */
2344                 return;
2345         }
2346         global_winbindd_offline_state = False;
2347
2348         if (!wcache->tdb) {
2349                 return;
2350         }
2351
2352         /* Ensure there is no key "WINBINDD_OFFLINE" in the cache tdb. */
2353         tdb_delete_bystring(wcache->tdb, "WINBINDD_OFFLINE");
2354 }
2355
2356 BOOL get_global_winbindd_state_online(void)
2357 {
2358         return global_winbindd_offline_state;
2359 }
2360
2361 /* the cache backend methods are exposed via this structure */
2362 struct winbindd_methods cache_methods = {
2363         True,
2364         query_user_list,
2365         enum_dom_groups,
2366         enum_local_groups,
2367         name_to_sid,
2368         sid_to_name,
2369         query_user,
2370         lookup_usergroups,
2371         lookup_useraliases,
2372         lookup_groupmem,
2373         sequence_number,
2374         lockout_policy,
2375         password_policy,
2376         trusted_domains
2377 };