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