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