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