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