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