da8983dda25b57692c068638b44b4d2990ad79bb
[tprouty/samba.git] / source / nsswitch / winbindd_cache.c
1 /* 
2    Unix SMB/CIFS implementation.
3
4    Winbind cache backend functions
5
6    Copyright (C) Andrew Tridgell 2001
7    Copyright (C) Gerald Carter   2003
8    Copyright (C) Volker Lendecke 2005
9    Copyright (C) Guenther Deschner 2005
10    
11    This program is free software; you can redistribute it and/or modify
12    it under the terms of the GNU General Public License as published by
13    the Free Software Foundation; either version 2 of the License, or
14    (at your option) any later version.
15    
16    This program is distributed in the hope that it will be useful,
17    but WITHOUT ANY WARRANTY; without even the implied warranty of
18    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19    GNU General Public License for more details.
20    
21    You should have received a copy of the GNU General Public License
22    along with this program; if not, write to the Free Software
23    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
24 */
25
26 #include "includes.h"
27 #include "winbindd.h"
28
29 #undef DBGC_CLASS
30 #define DBGC_CLASS DBGC_WINBIND
31
32 #define WINBINDD_CACHE_VERSION 1
33 #define WINBINDD_CACHE_VERSION_KEYSTR "WINBINDD_CACHE_VERSION"
34
35 extern struct winbindd_methods reconnect_methods;
36 extern BOOL opt_nocache;
37 #ifdef HAVE_ADS
38 extern struct winbindd_methods ads_methods;
39 #endif
40
41 /*
42  * JRA. KEEP THIS LIST UP TO DATE IF YOU ADD CACHE ENTRIES.
43  * Here are the list of entry types that are *not* stored
44  * as form struct cache_entry in the cache.
45  */
46
47 static const char *non_centry_keys[] = {
48         "SEQNUM/",
49         "DR/",
50         "DE/",
51         "WINBINDD_OFFLINE",
52         WINBINDD_CACHE_VERSION_KEYSTR,
53         NULL
54 };
55
56 /************************************************************************
57  Is this key a non-centry type ?
58 ************************************************************************/
59
60 static BOOL is_non_centry_key(TDB_DATA kbuf)
61 {
62         int i;
63
64         if (kbuf.dptr == NULL || kbuf.dsize == 0) {
65                 return False;
66         }
67         for (i = 0; non_centry_keys[i] != NULL; i++) {
68                 size_t namelen = strlen(non_centry_keys[i]);
69                 if (kbuf.dsize < namelen) {
70                         continue;
71                 }
72                 if (strncmp(non_centry_keys[i], (const char *)kbuf.dptr, namelen) == 0) {
73                         return True;
74                 }
75         }
76         return False;
77 }
78
79 /* Global online/offline state - False when online. winbindd starts up online
80    and sets this to true if the first query fails and there's an entry in
81    the cache tdb telling us to stay offline. */
82
83 static BOOL global_winbindd_offline_state;
84
85 struct winbind_cache {
86         TDB_CONTEXT *tdb;
87 };
88
89 struct cache_entry {
90         NTSTATUS status;
91         uint32 sequence_number;
92         uint8 *data;
93         uint32 len, ofs;
94 };
95
96 void (*smb_panic_fn)(const char *const why) = smb_panic;
97
98 #define WINBINDD_MAX_CACHE_SIZE (50*1024*1024)
99
100 static struct winbind_cache *wcache;
101
102 void winbindd_check_cache_size(time_t t)
103 {
104         static time_t last_check_time;
105         struct stat st;
106
107         if (last_check_time == (time_t)0)
108                 last_check_time = t;
109
110         if (t - last_check_time < 60 && t - last_check_time > 0)
111                 return;
112
113         if (wcache == NULL || wcache->tdb == NULL) {
114                 DEBUG(0, ("Unable to check size of tdb cache - cache not open !\n"));
115                 return;
116         }
117
118         if (fstat(tdb_fd(wcache->tdb), &st) == -1) {
119                 DEBUG(0, ("Unable to check size of tdb cache %s!\n", strerror(errno) ));
120                 return;
121         }
122
123         if (st.st_size > WINBINDD_MAX_CACHE_SIZE) {
124                 DEBUG(10,("flushing cache due to size (%lu) > (%lu)\n",
125                         (unsigned long)st.st_size,
126                         (unsigned long)WINBINDD_MAX_CACHE_SIZE));
127                 wcache_flush_cache();
128         }
129 }
130
131 /* get the winbind_cache structure */
132 static struct winbind_cache *get_cache(struct winbindd_domain *domain)
133 {
134         struct winbind_cache *ret = wcache;
135 #ifdef HAVE_ADS
136         struct winbindd_domain *our_domain = domain;
137 #endif
138
139         /* We have to know what type of domain we are dealing with first. */
140
141         if ( !domain->initialized ) {
142                 init_dc_connection( domain );
143         }
144
145         /* 
146            OK.  listen up becasue I'm only going to say this once.
147            We have the following scenarios to consider
148            (a) trusted AD domains on a Samba DC,
149            (b) trusted AD domains and we are joined to a non-kerberos domain
150            (c) trusted AD domains and we are joined to a kerberos (AD) domain
151
152            For (a) we can always contact the trusted domain using krb5 
153            since we have the domain trust account password
154
155            For (b) we can only use RPC since we have no way of 
156            getting a krb5 ticket in our own domain
157
158            For (c) we can always use krb5 since we have a kerberos trust
159
160            --jerry
161          */
162
163         if (!domain->backend) {
164 #ifdef HAVE_ADS
165                 /* find our domain first so we can figure out if we 
166                    are joined to a kerberized domain */
167
168                 if ( !domain->primary )
169                         our_domain = find_our_domain();
170
171                 if ( (our_domain->active_directory || IS_DC) && domain->active_directory ) {
172                         DEBUG(5,("get_cache: Setting ADS methods for domain %s\n", domain->name));
173                         domain->backend = &ads_methods;
174                 } else {
175 #endif  /* HAVE_ADS */
176                         DEBUG(5,("get_cache: Setting MS-RPC methods for domain %s\n", domain->name));
177                         domain->backend = &reconnect_methods;
178 #ifdef HAVE_ADS
179                 }
180 #endif  /* HAVE_ADS */
181         }
182
183         if (ret)
184                 return ret;
185         
186         ret = SMB_XMALLOC_P(struct winbind_cache);
187         ZERO_STRUCTP(ret);
188
189         wcache = ret;
190         wcache_flush_cache();
191
192         return ret;
193 }
194
195 /*
196   free a centry structure
197 */
198 static void centry_free(struct cache_entry *centry)
199 {
200         if (!centry)
201                 return;
202         SAFE_FREE(centry->data);
203         free(centry);
204 }
205
206 static BOOL centry_check_bytes(struct cache_entry *centry, size_t nbytes)
207 {
208         if (centry->len - centry->ofs < nbytes) {
209                 DEBUG(0,("centry corruption? needed %u bytes, have %d\n", 
210                          (unsigned int)nbytes,
211                          centry->len - centry->ofs));
212                 return False;
213         }
214         return True;
215 }
216
217 /*
218   pull a uint32 from a cache entry 
219 */
220 static uint32 centry_uint32(struct cache_entry *centry)
221 {
222         uint32 ret;
223
224         if (centry_check_bytes(centry, 4)) {
225                 smb_panic_fn("centry_uint32");
226                 return (uint32)-1;
227         }
228         ret = IVAL(centry->data, centry->ofs);
229         centry->ofs += 4;
230         return ret;
231 }
232
233 /*
234   pull a uint16 from a cache entry 
235 */
236 static uint16 centry_uint16(struct cache_entry *centry)
237 {
238         uint16 ret;
239         if (centry_check_bytes(centry, 2)) {
240                 smb_panic_fn("centry_uint16");
241                 return (uint16)-1;
242         }
243         ret = CVAL(centry->data, centry->ofs);
244         centry->ofs += 2;
245         return ret;
246 }
247
248 /*
249   pull a uint8 from a cache entry 
250 */
251 static uint8 centry_uint8(struct cache_entry *centry)
252 {
253         uint8 ret;
254         if (centry_check_bytes(centry, 1)) {
255                 smb_panic_fn("centry_uint8");
256                 return (uint8)-1;
257         }
258         ret = CVAL(centry->data, centry->ofs);
259         centry->ofs += 1;
260         return ret;
261 }
262
263 /*
264   pull a NTTIME from a cache entry 
265 */
266 static NTTIME centry_nttime(struct cache_entry *centry)
267 {
268         NTTIME ret;
269         if (centry_check_bytes(centry, 8)) {
270                 smb_panic_fn("centry_nttime");
271                 return (NTTIME)-1;
272         }
273         ret = IVAL(centry->data, centry->ofs);
274         centry->ofs += 4;
275         ret += (uint64_t)IVAL(centry->data, centry->ofs) << 32;
276         centry->ofs += 4;
277         return ret;
278 }
279
280 /*
281   pull a time_t from a cache entry. time_t stored portably as a 64-bit time.
282 */
283 static time_t centry_time(struct cache_entry *centry)
284 {
285         return (time_t)centry_nttime(centry);
286 }
287
288 /* pull a string from a cache entry, using the supplied
289    talloc context 
290 */
291 static char *centry_string(struct cache_entry *centry, TALLOC_CTX *mem_ctx)
292 {
293         uint32 len;
294         char *ret;
295
296         len = centry_uint8(centry);
297
298         if (len == 0xFF) {
299                 /* a deliberate NULL string */
300                 return NULL;
301         }
302
303         if (centry_check_bytes(centry, (size_t)len)) {
304                 smb_panic_fn("centry_string");
305                 return NULL;
306         }
307
308         ret = TALLOC_ARRAY(mem_ctx, char, len+1);
309         if (!ret) {
310                 smb_panic_fn("centry_string out of memory\n");
311                 return NULL;
312         }
313         memcpy(ret,centry->data + centry->ofs, len);
314         ret[len] = 0;
315         centry->ofs += len;
316         return ret;
317 }
318
319 /* pull a hash16 from a cache entry, using the supplied
320    talloc context 
321 */
322 static char *centry_hash16(struct cache_entry *centry, TALLOC_CTX *mem_ctx)
323 {
324         uint32 len;
325         char *ret;
326
327         len = centry_uint8(centry);
328
329         if (len != 16) {
330                 DEBUG(0,("centry corruption? hash len (%u) != 16\n", 
331                         len ));
332                 return NULL;
333         }
334
335         if (centry_check_bytes(centry, 16)) {
336                 return NULL;
337         }
338
339         ret = TALLOC_ARRAY(mem_ctx, char, 16);
340         if (!ret) {
341                 smb_panic_fn("centry_hash out of memory\n");
342                 return NULL;
343         }
344         memcpy(ret,centry->data + centry->ofs, 16);
345         centry->ofs += 16;
346         return ret;
347 }
348
349 /* pull a sid from a cache entry, using the supplied
350    talloc context 
351 */
352 static BOOL centry_sid(struct cache_entry *centry, TALLOC_CTX *mem_ctx, DOM_SID *sid)
353 {
354         char *sid_string;
355         sid_string = centry_string(centry, mem_ctx);
356         if ((sid_string == NULL) || (!string_to_sid(sid, sid_string))) {
357                 return False;
358         }
359         return True;
360 }
361
362 /* the server is considered down if it can't give us a sequence number */
363 static BOOL wcache_server_down(struct winbindd_domain *domain)
364 {
365         BOOL ret;
366
367         if (!wcache->tdb)
368                 return False;
369
370         ret = (domain->sequence_number == DOM_SEQUENCE_NONE);
371
372         if (ret)
373                 DEBUG(10,("wcache_server_down: server for Domain %s down\n", 
374                         domain->name ));
375         return ret;
376 }
377
378 static NTSTATUS fetch_cache_seqnum( struct winbindd_domain *domain, time_t now )
379 {
380         TDB_DATA data;
381         fstring key;
382         uint32 time_diff;
383         
384         if (!wcache->tdb) {
385                 DEBUG(10,("fetch_cache_seqnum: tdb == NULL\n"));
386                 return NT_STATUS_UNSUCCESSFUL;
387         }
388                 
389         fstr_sprintf( key, "SEQNUM/%s", domain->name );
390         
391         data = tdb_fetch_bystring( wcache->tdb, key );
392         if ( !data.dptr || data.dsize!=8 ) {
393                 DEBUG(10,("fetch_cache_seqnum: invalid data size key [%s]\n", key ));
394                 return NT_STATUS_UNSUCCESSFUL;
395         }
396         
397         domain->sequence_number = IVAL(data.dptr, 0);
398         domain->last_seq_check  = IVAL(data.dptr, 4);
399         
400         SAFE_FREE(data.dptr);
401
402         /* have we expired? */
403         
404         time_diff = now - domain->last_seq_check;
405         if ( time_diff > lp_winbind_cache_time() ) {
406                 DEBUG(10,("fetch_cache_seqnum: timeout [%s][%u @ %u]\n",
407                         domain->name, domain->sequence_number,
408                         (uint32)domain->last_seq_check));
409                 return NT_STATUS_UNSUCCESSFUL;
410         }
411
412         DEBUG(10,("fetch_cache_seqnum: success [%s][%u @ %u]\n", 
413                 domain->name, domain->sequence_number, 
414                 (uint32)domain->last_seq_check));
415
416         return NT_STATUS_OK;
417 }
418
419 static NTSTATUS store_cache_seqnum( struct winbindd_domain *domain )
420 {
421         TDB_DATA data;
422         fstring key_str;
423         uint8 buf[8];
424         
425         if (!wcache->tdb) {
426                 DEBUG(10,("store_cache_seqnum: tdb == NULL\n"));
427                 return NT_STATUS_UNSUCCESSFUL;
428         }
429                 
430         fstr_sprintf( key_str, "SEQNUM/%s", domain->name );
431         
432         SIVAL(buf, 0, domain->sequence_number);
433         SIVAL(buf, 4, domain->last_seq_check);
434         data.dptr = buf;
435         data.dsize = 8;
436         
437         if ( tdb_store_bystring( wcache->tdb, key_str, data, TDB_REPLACE) == -1 ) {
438                 DEBUG(10,("store_cache_seqnum: tdb_store fail key [%s]\n", key_str ));
439                 return NT_STATUS_UNSUCCESSFUL;
440         }
441
442         DEBUG(10,("store_cache_seqnum: success [%s][%u @ %u]\n", 
443                 domain->name, domain->sequence_number, 
444                 (uint32)domain->last_seq_check));
445         
446         return NT_STATUS_OK;
447 }
448
449 /*
450   refresh the domain sequence number. If force is True
451   then always refresh it, no matter how recently we fetched it
452 */
453
454 static void refresh_sequence_number(struct winbindd_domain *domain, BOOL force)
455 {
456         NTSTATUS status;
457         unsigned time_diff;
458         time_t t = time(NULL);
459         unsigned cache_time = lp_winbind_cache_time();
460
461         get_cache( domain );
462
463 #if 0   /* JERRY -- disable as the default cache time is now 5 minutes */
464         /* trying to reconnect is expensive, don't do it too often */
465         if (domain->sequence_number == DOM_SEQUENCE_NONE) {
466                 cache_time *= 8;
467         }
468 #endif
469
470         time_diff = t - domain->last_seq_check;
471
472         /* see if we have to refetch the domain sequence number */
473         if (!force && (time_diff < cache_time)) {
474                 DEBUG(10, ("refresh_sequence_number: %s time ok\n", domain->name));
475                 goto done;
476         }
477         
478         /* try to get the sequence number from the tdb cache first */
479         /* this will update the timestamp as well */
480         
481         status = fetch_cache_seqnum( domain, t );
482         if ( NT_STATUS_IS_OK(status) )
483                 goto done;      
484
485         /* important! make sure that we know if this is a native 
486            mode domain or not */
487
488         status = domain->backend->sequence_number(domain, &domain->sequence_number);
489
490         /* the above call could have set our domain->backend to NULL when
491          * coming from offline to online mode, make sure to reinitialize the
492          * backend - Guenther */
493         get_cache( domain );
494
495         if (!NT_STATUS_IS_OK(status)) {
496                 DEBUG(10,("refresh_sequence_number: failed with %s\n", nt_errstr(status)));
497                 domain->sequence_number = DOM_SEQUENCE_NONE;
498         }
499         
500         domain->last_status = status;
501         domain->last_seq_check = time(NULL);
502         
503         /* save the new sequence number ni the cache */
504         store_cache_seqnum( domain );
505
506 done:
507         DEBUG(10, ("refresh_sequence_number: %s seq number is now %d\n", 
508                    domain->name, domain->sequence_number));
509
510         return;
511 }
512
513 /*
514   decide if a cache entry has expired
515 */
516 static BOOL centry_expired(struct winbindd_domain *domain, const char *keystr, struct cache_entry *centry)
517 {
518         /* If we've been told to be offline - stay in that state... */
519         if (lp_winbind_offline_logon() && global_winbindd_offline_state) {
520                 DEBUG(10,("centry_expired: Key %s for domain %s valid as winbindd is globally offline.\n",
521                         keystr, domain->name ));
522                 return False;
523         }
524
525         /* when the domain is offline return the cached entry.
526          * This deals with transient offline states... */
527
528         if (!domain->online) {
529                 DEBUG(10,("centry_expired: Key %s for domain %s valid as domain is offline.\n",
530                         keystr, domain->name ));
531                 return False;
532         }
533
534         /* if the server is OK and our cache entry came from when it was down then
535            the entry is invalid */
536         if ((domain->sequence_number != DOM_SEQUENCE_NONE) &&  
537             (centry->sequence_number == DOM_SEQUENCE_NONE)) {
538                 DEBUG(10,("centry_expired: Key %s for domain %s invalid sequence.\n",
539                         keystr, domain->name ));
540                 return True;
541         }
542
543         /* if the server is down or the cache entry is not older than the
544            current sequence number then it is OK */
545         if (wcache_server_down(domain) || 
546             centry->sequence_number == domain->sequence_number) {
547                 DEBUG(10,("centry_expired: Key %s for domain %s is good.\n",
548                         keystr, domain->name ));
549                 return False;
550         }
551
552         DEBUG(10,("centry_expired: Key %s for domain %s expired\n",
553                 keystr, domain->name ));
554
555         /* it's expired */
556         return True;
557 }
558
559 static struct cache_entry *wcache_fetch_raw(char *kstr)
560 {
561         TDB_DATA data;
562         struct cache_entry *centry;
563         TDB_DATA key;
564
565         key = string_tdb_data(kstr);
566         data = tdb_fetch(wcache->tdb, key);
567         if (!data.dptr) {
568                 /* a cache miss */
569                 return NULL;
570         }
571
572         centry = SMB_XMALLOC_P(struct cache_entry);
573         centry->data = (unsigned char *)data.dptr;
574         centry->len = data.dsize;
575         centry->ofs = 0;
576
577         if (centry->len < 8) {
578                 /* huh? corrupt cache? */
579                 DEBUG(10,("wcache_fetch_raw: Corrupt cache for key %s (len < 8) ?\n", kstr));
580                 centry_free(centry);
581                 return NULL;
582         }
583         
584         centry->status = NT_STATUS(centry_uint32(centry));
585         centry->sequence_number = centry_uint32(centry);
586
587         return centry;
588 }
589
590 /*
591   fetch an entry from the cache, with a varargs key. auto-fetch the sequence
592   number and return status
593 */
594 static struct cache_entry *wcache_fetch(struct winbind_cache *cache, 
595                                         struct winbindd_domain *domain,
596                                         const char *format, ...) PRINTF_ATTRIBUTE(3,4);
597 static struct cache_entry *wcache_fetch(struct winbind_cache *cache, 
598                                         struct winbindd_domain *domain,
599                                         const char *format, ...)
600 {
601         va_list ap;
602         char *kstr;
603         struct cache_entry *centry;
604
605         if (opt_nocache) {
606                 return NULL;
607         }
608
609         refresh_sequence_number(domain, False);
610
611         va_start(ap, format);
612         smb_xvasprintf(&kstr, format, ap);
613         va_end(ap);
614
615         centry = wcache_fetch_raw(kstr);
616         if (centry == NULL) {
617                 free(kstr);
618                 return NULL;
619         }
620
621         if (centry_expired(domain, kstr, centry)) {
622
623                 DEBUG(10,("wcache_fetch: entry %s expired for domain %s\n",
624                          kstr, domain->name ));
625
626                 centry_free(centry);
627                 free(kstr);
628                 return NULL;
629         }
630
631         DEBUG(10,("wcache_fetch: returning entry %s for domain %s\n",
632                  kstr, domain->name ));
633
634         free(kstr);
635         return centry;
636 }
637
638 static void wcache_delete(const char *format, ...) PRINTF_ATTRIBUTE(1,2);
639 static void wcache_delete(const char *format, ...)
640 {
641         va_list ap;
642         char *kstr;
643         TDB_DATA key;
644
645         va_start(ap, format);
646         smb_xvasprintf(&kstr, format, ap);
647         va_end(ap);
648
649         key = string_tdb_data(kstr);
650
651         tdb_delete(wcache->tdb, key);
652         free(kstr);
653 }
654
655 /*
656   make sure we have at least len bytes available in a centry 
657 */
658 static void centry_expand(struct cache_entry *centry, uint32 len)
659 {
660         if (centry->len - centry->ofs >= len)
661                 return;
662         centry->len *= 2;
663         centry->data = SMB_REALLOC_ARRAY(centry->data, unsigned char,
664                                          centry->len);
665         if (!centry->data) {
666                 DEBUG(0,("out of memory: needed %d bytes in centry_expand\n", centry->len));
667                 smb_panic_fn("out of memory in centry_expand");
668         }
669 }
670
671 /*
672   push a uint32 into a centry 
673 */
674 static void centry_put_uint32(struct cache_entry *centry, uint32 v)
675 {
676         centry_expand(centry, 4);
677         SIVAL(centry->data, centry->ofs, v);
678         centry->ofs += 4;
679 }
680
681 /*
682   push a uint16 into a centry 
683 */
684 static void centry_put_uint16(struct cache_entry *centry, uint16 v)
685 {
686         centry_expand(centry, 2);
687         SIVAL(centry->data, centry->ofs, v);
688         centry->ofs += 2;
689 }
690
691 /*
692   push a uint8 into a centry 
693 */
694 static void centry_put_uint8(struct cache_entry *centry, uint8 v)
695 {
696         centry_expand(centry, 1);
697         SCVAL(centry->data, centry->ofs, v);
698         centry->ofs += 1;
699 }
700
701 /* 
702    push a string into a centry 
703  */
704 static void centry_put_string(struct cache_entry *centry, const char *s)
705 {
706         int len;
707
708         if (!s) {
709                 /* null strings are marked as len 0xFFFF */
710                 centry_put_uint8(centry, 0xFF);
711                 return;
712         }
713
714         len = strlen(s);
715         /* can't handle more than 254 char strings. Truncating is probably best */
716         if (len > 254) {
717                 DEBUG(10,("centry_put_string: truncating len (%d) to: 254\n", len));
718                 len = 254;
719         }
720         centry_put_uint8(centry, len);
721         centry_expand(centry, len);
722         memcpy(centry->data + centry->ofs, s, len);
723         centry->ofs += len;
724 }
725
726 /* 
727    push a 16 byte hash into a centry - treat as 16 byte string.
728  */
729 static void centry_put_hash16(struct cache_entry *centry, const uint8 val[16])
730 {
731         centry_put_uint8(centry, 16);
732         centry_expand(centry, 16);
733         memcpy(centry->data + centry->ofs, val, 16);
734         centry->ofs += 16;
735 }
736
737 static void centry_put_sid(struct cache_entry *centry, const DOM_SID *sid) 
738 {
739         fstring sid_string;
740         centry_put_string(centry, sid_to_string(sid_string, sid));
741 }
742
743 /*
744   push a NTTIME into a centry 
745 */
746 static void centry_put_nttime(struct cache_entry *centry, NTTIME nt)
747 {
748         centry_expand(centry, 8);
749         SIVAL(centry->data, centry->ofs, nt & 0xFFFFFFFF);
750         centry->ofs += 4;
751         SIVAL(centry->data, centry->ofs, nt >> 32);
752         centry->ofs += 4;
753 }
754
755 /*
756   push a time_t into a centry - use a 64 bit size.
757   NTTIME here is being used as a convenient 64-bit size.
758 */
759 static void centry_put_time(struct cache_entry *centry, time_t t)
760 {
761         NTTIME nt = (NTTIME)t;
762         centry_put_nttime(centry, nt);
763 }
764
765 /*
766   start a centry for output. When finished, call centry_end()
767 */
768 struct cache_entry *centry_start(struct winbindd_domain *domain, NTSTATUS status)
769 {
770         struct cache_entry *centry;
771
772         if (!wcache->tdb)
773                 return NULL;
774
775         centry = SMB_XMALLOC_P(struct cache_entry);
776
777         centry->len = 8192; /* reasonable default */
778         centry->data = SMB_XMALLOC_ARRAY(uint8, centry->len);
779         centry->ofs = 0;
780         centry->sequence_number = domain->sequence_number;
781         centry_put_uint32(centry, NT_STATUS_V(status));
782         centry_put_uint32(centry, centry->sequence_number);
783         return centry;
784 }
785
786 /*
787   finish a centry and write it to the tdb
788 */
789 static void centry_end(struct cache_entry *centry, const char *format, ...) PRINTF_ATTRIBUTE(2,3);
790 static void centry_end(struct cache_entry *centry, const char *format, ...)
791 {
792         va_list ap;
793         char *kstr;
794         TDB_DATA key, data;
795
796         va_start(ap, format);
797         smb_xvasprintf(&kstr, format, ap);
798         va_end(ap);
799
800         key = string_tdb_data(kstr);
801         data.dptr = centry->data;
802         data.dsize = centry->ofs;
803
804         tdb_store(wcache->tdb, key, data, TDB_REPLACE);
805         free(kstr);
806 }
807
808 static void wcache_save_name_to_sid(struct winbindd_domain *domain, 
809                                     NTSTATUS status, const char *domain_name,
810                                     const char *name, const DOM_SID *sid, 
811                                     enum lsa_SidType type)
812 {
813         struct cache_entry *centry;
814         fstring uname;
815
816         centry = centry_start(domain, status);
817         if (!centry)
818                 return;
819         centry_put_uint32(centry, type);
820         centry_put_sid(centry, sid);
821         fstrcpy(uname, name);
822         strupper_m(uname);
823         centry_end(centry, "NS/%s/%s", domain_name, uname);
824         DEBUG(10,("wcache_save_name_to_sid: %s\\%s -> %s\n", domain_name, uname,
825                   sid_string_static(sid)));
826         centry_free(centry);
827 }
828
829 static void wcache_save_sid_to_name(struct winbindd_domain *domain, NTSTATUS status, 
830                                     const DOM_SID *sid, const char *domain_name, const char *name, enum lsa_SidType type)
831 {
832         struct cache_entry *centry;
833         fstring sid_string;
834
835         if (is_null_sid(sid)) {
836                 return;
837         }
838
839         centry = centry_start(domain, status);
840         if (!centry)
841                 return;
842         if (NT_STATUS_IS_OK(status)) {
843                 centry_put_uint32(centry, type);
844                 centry_put_string(centry, domain_name);
845                 centry_put_string(centry, name);
846         }
847         centry_end(centry, "SN/%s", sid_to_string(sid_string, sid));
848         DEBUG(10,("wcache_save_sid_to_name: %s -> %s\n", sid_string, name));
849         centry_free(centry);
850 }
851
852
853 static void wcache_save_user(struct winbindd_domain *domain, NTSTATUS status, WINBIND_USERINFO *info)
854 {
855         struct cache_entry *centry;
856         fstring sid_string;
857
858         if (is_null_sid(&info->user_sid)) {
859                 return;
860         }
861
862         centry = centry_start(domain, status);
863         if (!centry)
864                 return;
865         centry_put_string(centry, info->acct_name);
866         centry_put_string(centry, info->full_name);
867         centry_put_string(centry, info->homedir);
868         centry_put_string(centry, info->shell);
869         centry_put_uint32(centry, info->primary_gid);
870         centry_put_sid(centry, &info->user_sid);
871         centry_put_sid(centry, &info->group_sid);
872         centry_end(centry, "U/%s", sid_to_string(sid_string, &info->user_sid));
873         DEBUG(10,("wcache_save_user: %s (acct_name %s)\n", sid_string, info->acct_name));
874         centry_free(centry);
875 }
876
877 static void wcache_save_lockout_policy(struct winbindd_domain *domain, NTSTATUS status, SAM_UNK_INFO_12 *lockout_policy)
878 {
879         struct cache_entry *centry;
880
881         centry = centry_start(domain, status);
882         if (!centry)
883                 return;
884
885         centry_put_nttime(centry, lockout_policy->duration);
886         centry_put_nttime(centry, lockout_policy->reset_count);
887         centry_put_uint16(centry, lockout_policy->bad_attempt_lockout);
888
889         centry_end(centry, "LOC_POL/%s", domain->name);
890         
891         DEBUG(10,("wcache_save_lockout_policy: %s\n", domain->name));
892
893         centry_free(centry);
894 }
895
896 static void wcache_save_password_policy(struct winbindd_domain *domain, NTSTATUS status, SAM_UNK_INFO_1 *policy)
897  {
898         struct cache_entry *centry;
899
900         centry = centry_start(domain, status);
901         if (!centry)
902                 return;
903
904         centry_put_uint16(centry, policy->min_length_password);
905         centry_put_uint16(centry, policy->password_history);
906         centry_put_uint32(centry, policy->password_properties);
907         centry_put_nttime(centry, policy->expire);
908         centry_put_nttime(centry, policy->min_passwordage);
909
910         centry_end(centry, "PWD_POL/%s", domain->name);
911         
912         DEBUG(10,("wcache_save_password_policy: %s\n", domain->name));
913
914         centry_free(centry);
915 }
916
917 NTSTATUS wcache_cached_creds_exist(struct winbindd_domain *domain, const DOM_SID *sid)
918 {
919         struct winbind_cache *cache = get_cache(domain);
920         TDB_DATA data;
921         fstring key_str;
922         uint32 rid;
923
924         if (!cache->tdb) {
925                 return NT_STATUS_INTERNAL_DB_ERROR;
926         }
927
928         if (is_null_sid(sid)) {
929                 return NT_STATUS_INVALID_SID;
930         }
931
932         if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
933                 return NT_STATUS_INVALID_SID;
934         }
935
936         fstr_sprintf(key_str, "CRED/%s", sid_string_static(sid));
937
938         data = tdb_fetch(cache->tdb, string_tdb_data(key_str));
939         if (!data.dptr) {
940                 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
941         }
942
943         SAFE_FREE(data.dptr);
944         return NT_STATUS_OK;
945 }
946
947 /* Lookup creds for a SID - copes with old (unsalted) creds as well
948    as new salted ones. */
949
950 NTSTATUS wcache_get_creds(struct winbindd_domain *domain, 
951                           TALLOC_CTX *mem_ctx, 
952                           const DOM_SID *sid,
953                           const uint8 **cached_nt_pass,
954                           const uint8 **cached_salt)
955 {
956         struct winbind_cache *cache = get_cache(domain);
957         struct cache_entry *centry = NULL;
958         NTSTATUS status;
959         time_t t;
960         uint32 rid;
961
962         if (!cache->tdb) {
963                 return NT_STATUS_INTERNAL_DB_ERROR;
964         }
965
966         if (is_null_sid(sid)) {
967                 return NT_STATUS_INVALID_SID;
968         }
969
970         if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
971                 return NT_STATUS_INVALID_SID;
972         }
973
974         /* Try and get a salted cred first. If we can't
975            fall back to an unsalted cred. */
976
977         centry = wcache_fetch(cache, domain, "CRED/%s", sid_string_static(sid));
978         if (!centry) {
979                 DEBUG(10,("wcache_get_creds: entry for [CRED/%s] not found\n", 
980                                 sid_string_static(sid)));
981                 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
982         }
983
984         t = centry_time(centry);
985
986         /* In the salted case this isn't actually the nt_hash itself,
987            but the MD5 of the salt + nt_hash. Let the caller
988            sort this out. It can tell as we only return the cached_salt
989            if we are returning a salted cred. */
990
991         *cached_nt_pass = (const uint8 *)centry_hash16(centry, mem_ctx);
992         if (*cached_nt_pass == NULL) {
993                 const char *sidstr = sid_string_static(sid);
994
995                 /* Bad (old) cred cache. Delete and pretend we
996                    don't have it. */
997                 DEBUG(0,("wcache_get_creds: bad entry for [CRED/%s] - deleting\n", 
998                                 sidstr));
999                 wcache_delete("CRED/%s", sidstr);
1000                 centry_free(centry);
1001                 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1002         }
1003
1004         /* We only have 17 bytes more data in the salted cred case. */
1005         if (centry->len - centry->ofs == 17) {
1006                 *cached_salt = (const uint8 *)centry_hash16(centry, mem_ctx);
1007         } else {
1008                 *cached_salt = NULL;
1009         }
1010
1011 #if DEBUG_PASSWORD
1012         dump_data(100, *cached_nt_pass, NT_HASH_LEN);
1013         if (*cached_salt) {
1014                 dump_data(100, *cached_salt, NT_HASH_LEN);
1015         }
1016 #endif
1017         status = centry->status;
1018
1019         DEBUG(10,("wcache_get_creds: [Cached] - cached creds for user %s status: %s\n",
1020                 sid_string_static(sid), nt_errstr(status) ));
1021
1022         centry_free(centry);
1023         return status;
1024 }
1025
1026 /* Store creds for a SID - only writes out new salted ones. */
1027
1028 NTSTATUS wcache_save_creds(struct winbindd_domain *domain, 
1029                            TALLOC_CTX *mem_ctx, 
1030                            const DOM_SID *sid, 
1031                            const uint8 nt_pass[NT_HASH_LEN])
1032 {
1033         struct cache_entry *centry;
1034         fstring sid_string;
1035         uint32 rid;
1036         uint8 cred_salt[NT_HASH_LEN];
1037         uint8 salted_hash[NT_HASH_LEN];
1038
1039         if (is_null_sid(sid)) {
1040                 return NT_STATUS_INVALID_SID;
1041         }
1042
1043         if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
1044                 return NT_STATUS_INVALID_SID;
1045         }
1046
1047         centry = centry_start(domain, NT_STATUS_OK);
1048         if (!centry) {
1049                 return NT_STATUS_INTERNAL_DB_ERROR;
1050         }
1051
1052 #if DEBUG_PASSWORD
1053         dump_data(100, nt_pass, NT_HASH_LEN);
1054 #endif
1055
1056         centry_put_time(centry, time(NULL));
1057
1058         /* Create a salt and then salt the hash. */
1059         generate_random_buffer(cred_salt, NT_HASH_LEN);
1060         E_md5hash(cred_salt, nt_pass, salted_hash);
1061
1062         centry_put_hash16(centry, salted_hash);
1063         centry_put_hash16(centry, cred_salt);
1064         centry_end(centry, "CRED/%s", sid_to_string(sid_string, sid));
1065
1066         DEBUG(10,("wcache_save_creds: %s\n", sid_string));
1067
1068         centry_free(centry);
1069
1070         return NT_STATUS_OK;
1071 }
1072
1073
1074 /* Query display info. This is the basic user list fn */
1075 static NTSTATUS query_user_list(struct winbindd_domain *domain,
1076                                 TALLOC_CTX *mem_ctx,
1077                                 uint32 *num_entries, 
1078                                 WINBIND_USERINFO **info)
1079 {
1080         struct winbind_cache *cache = get_cache(domain);
1081         struct cache_entry *centry = NULL;
1082         NTSTATUS status;
1083         unsigned int i, retry;
1084
1085         if (!cache->tdb)
1086                 goto do_query;
1087
1088         centry = wcache_fetch(cache, domain, "UL/%s", domain->name);
1089         if (!centry)
1090                 goto do_query;
1091
1092         *num_entries = centry_uint32(centry);
1093         
1094         if (*num_entries == 0)
1095                 goto do_cached;
1096
1097         (*info) = TALLOC_ARRAY(mem_ctx, WINBIND_USERINFO, *num_entries);
1098         if (! (*info)) {
1099                 smb_panic_fn("query_user_list out of memory");
1100                 centry_free(centry);
1101                 return NT_STATUS_NO_MEMORY;
1102         }
1103         for (i=0; i<(*num_entries); i++) {
1104                 (*info)[i].acct_name = centry_string(centry, mem_ctx);
1105                 (*info)[i].full_name = centry_string(centry, mem_ctx);
1106                 (*info)[i].homedir = centry_string(centry, mem_ctx);
1107                 (*info)[i].shell = centry_string(centry, mem_ctx);
1108                 centry_sid(centry, mem_ctx, &(*info)[i].user_sid);
1109                 centry_sid(centry, mem_ctx, &(*info)[i].group_sid);
1110         }
1111
1112 do_cached:      
1113         status = centry->status;
1114
1115         DEBUG(10,("query_user_list: [Cached] - cached list for domain %s status: %s\n",
1116                 domain->name, nt_errstr(status) ));
1117
1118         centry_free(centry);
1119         return status;
1120
1121 do_query:
1122         *num_entries = 0;
1123         *info = NULL;
1124
1125         /* Return status value returned by seq number check */
1126
1127         if (!NT_STATUS_IS_OK(domain->last_status))
1128                 return domain->last_status;
1129
1130         /* Put the query_user_list() in a retry loop.  There appears to be
1131          * some bug either with Windows 2000 or Samba's handling of large
1132          * rpc replies.  This manifests itself as sudden disconnection
1133          * at a random point in the enumeration of a large (60k) user list.
1134          * The retry loop simply tries the operation again. )-:  It's not
1135          * pretty but an acceptable workaround until we work out what the
1136          * real problem is. */
1137
1138         retry = 0;
1139         do {
1140
1141                 DEBUG(10,("query_user_list: [Cached] - doing backend query for list for domain %s\n",
1142                         domain->name ));
1143
1144                 status = domain->backend->query_user_list(domain, mem_ctx, num_entries, info);
1145                 if (!NT_STATUS_IS_OK(status))
1146                         DEBUG(3, ("query_user_list: returned 0x%08x, "
1147                                   "retrying\n", NT_STATUS_V(status)));
1148                         if (NT_STATUS_EQUAL(status, NT_STATUS_UNSUCCESSFUL)) {
1149                                 DEBUG(3, ("query_user_list: flushing "
1150                                           "connection cache\n"));
1151                                 invalidate_cm_connection(&domain->conn);
1152                         }
1153
1154         } while (NT_STATUS_V(status) == NT_STATUS_V(NT_STATUS_UNSUCCESSFUL) && 
1155                  (retry++ < 5));
1156
1157         /* and save it */
1158         refresh_sequence_number(domain, False);
1159         centry = centry_start(domain, status);
1160         if (!centry)
1161                 goto skip_save;
1162         centry_put_uint32(centry, *num_entries);
1163         for (i=0; i<(*num_entries); i++) {
1164                 centry_put_string(centry, (*info)[i].acct_name);
1165                 centry_put_string(centry, (*info)[i].full_name);
1166                 centry_put_string(centry, (*info)[i].homedir);
1167                 centry_put_string(centry, (*info)[i].shell);
1168                 centry_put_sid(centry, &(*info)[i].user_sid);
1169                 centry_put_sid(centry, &(*info)[i].group_sid);
1170                 if (domain->backend && domain->backend->consistent) {
1171                         /* when the backend is consistent we can pre-prime some mappings */
1172                         wcache_save_name_to_sid(domain, NT_STATUS_OK, 
1173                                                 domain->name,
1174                                                 (*info)[i].acct_name, 
1175                                                 &(*info)[i].user_sid,
1176                                                 SID_NAME_USER);
1177                         wcache_save_sid_to_name(domain, NT_STATUS_OK, 
1178                                                 &(*info)[i].user_sid,
1179                                                 domain->name,
1180                                                 (*info)[i].acct_name, 
1181                                                 SID_NAME_USER);
1182                         wcache_save_user(domain, NT_STATUS_OK, &(*info)[i]);
1183                 }
1184         }       
1185         centry_end(centry, "UL/%s", domain->name);
1186         centry_free(centry);
1187
1188 skip_save:
1189         return status;
1190 }
1191
1192 /* list all domain groups */
1193 static NTSTATUS enum_dom_groups(struct winbindd_domain *domain,
1194                                 TALLOC_CTX *mem_ctx,
1195                                 uint32 *num_entries, 
1196                                 struct acct_info **info)
1197 {
1198         struct winbind_cache *cache = get_cache(domain);
1199         struct cache_entry *centry = NULL;
1200         NTSTATUS status;
1201         unsigned int i;
1202
1203         if (!cache->tdb)
1204                 goto do_query;
1205
1206         centry = wcache_fetch(cache, domain, "GL/%s/domain", domain->name);
1207         if (!centry)
1208                 goto do_query;
1209
1210         *num_entries = centry_uint32(centry);
1211         
1212         if (*num_entries == 0)
1213                 goto do_cached;
1214
1215         (*info) = TALLOC_ARRAY(mem_ctx, struct acct_info, *num_entries);
1216         if (! (*info)) {
1217                 smb_panic_fn("enum_dom_groups out of memory");
1218                 centry_free(centry);
1219                 return NT_STATUS_NO_MEMORY;
1220         }
1221         for (i=0; i<(*num_entries); i++) {
1222                 fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx));
1223                 fstrcpy((*info)[i].acct_desc, centry_string(centry, mem_ctx));
1224                 (*info)[i].rid = centry_uint32(centry);
1225         }
1226
1227 do_cached:      
1228         status = centry->status;
1229
1230         DEBUG(10,("enum_dom_groups: [Cached] - cached list for domain %s status: %s\n",
1231                 domain->name, nt_errstr(status) ));
1232
1233         centry_free(centry);
1234         return status;
1235
1236 do_query:
1237         *num_entries = 0;
1238         *info = NULL;
1239
1240         /* Return status value returned by seq number check */
1241
1242         if (!NT_STATUS_IS_OK(domain->last_status))
1243                 return domain->last_status;
1244
1245         DEBUG(10,("enum_dom_groups: [Cached] - doing backend query for list for domain %s\n",
1246                 domain->name ));
1247
1248         status = domain->backend->enum_dom_groups(domain, mem_ctx, num_entries, info);
1249
1250         /* and save it */
1251         refresh_sequence_number(domain, False);
1252         centry = centry_start(domain, status);
1253         if (!centry)
1254                 goto skip_save;
1255         centry_put_uint32(centry, *num_entries);
1256         for (i=0; i<(*num_entries); i++) {
1257                 centry_put_string(centry, (*info)[i].acct_name);
1258                 centry_put_string(centry, (*info)[i].acct_desc);
1259                 centry_put_uint32(centry, (*info)[i].rid);
1260         }       
1261         centry_end(centry, "GL/%s/domain", domain->name);
1262         centry_free(centry);
1263
1264 skip_save:
1265         return status;
1266 }
1267
1268 /* list all domain groups */
1269 static NTSTATUS enum_local_groups(struct winbindd_domain *domain,
1270                                 TALLOC_CTX *mem_ctx,
1271                                 uint32 *num_entries, 
1272                                 struct acct_info **info)
1273 {
1274         struct winbind_cache *cache = get_cache(domain);
1275         struct cache_entry *centry = NULL;
1276         NTSTATUS status;
1277         unsigned int i;
1278
1279         if (!cache->tdb)
1280                 goto do_query;
1281
1282         centry = wcache_fetch(cache, domain, "GL/%s/local", domain->name);
1283         if (!centry)
1284                 goto do_query;
1285
1286         *num_entries = centry_uint32(centry);
1287         
1288         if (*num_entries == 0)
1289                 goto do_cached;
1290
1291         (*info) = TALLOC_ARRAY(mem_ctx, struct acct_info, *num_entries);
1292         if (! (*info)) {
1293                 smb_panic_fn("enum_dom_groups out of memory");
1294                 centry_free(centry);
1295                 return NT_STATUS_NO_MEMORY;
1296         }
1297         for (i=0; i<(*num_entries); i++) {
1298                 fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx));
1299                 fstrcpy((*info)[i].acct_desc, centry_string(centry, mem_ctx));
1300                 (*info)[i].rid = centry_uint32(centry);
1301         }
1302
1303 do_cached:      
1304
1305         /* If we are returning cached data and the domain controller
1306            is down then we don't know whether the data is up to date
1307            or not.  Return NT_STATUS_MORE_PROCESSING_REQUIRED to
1308            indicate this. */
1309
1310         if (wcache_server_down(domain)) {
1311                 DEBUG(10, ("enum_local_groups: returning cached user list and server was down\n"));
1312                 status = NT_STATUS_MORE_PROCESSING_REQUIRED;
1313         } else
1314                 status = centry->status;
1315
1316         DEBUG(10,("enum_local_groups: [Cached] - cached list for domain %s status: %s\n",
1317                 domain->name, nt_errstr(status) ));
1318
1319         centry_free(centry);
1320         return status;
1321
1322 do_query:
1323         *num_entries = 0;
1324         *info = NULL;
1325
1326         /* Return status value returned by seq number check */
1327
1328         if (!NT_STATUS_IS_OK(domain->last_status))
1329                 return domain->last_status;
1330
1331         DEBUG(10,("enum_local_groups: [Cached] - doing backend query for list for domain %s\n",
1332                 domain->name ));
1333
1334         status = domain->backend->enum_local_groups(domain, mem_ctx, num_entries, info);
1335
1336         /* and save it */
1337         refresh_sequence_number(domain, False);
1338         centry = centry_start(domain, status);
1339         if (!centry)
1340                 goto skip_save;
1341         centry_put_uint32(centry, *num_entries);
1342         for (i=0; i<(*num_entries); i++) {
1343                 centry_put_string(centry, (*info)[i].acct_name);
1344                 centry_put_string(centry, (*info)[i].acct_desc);
1345                 centry_put_uint32(centry, (*info)[i].rid);
1346         }
1347         centry_end(centry, "GL/%s/local", domain->name);
1348         centry_free(centry);
1349
1350 skip_save:
1351         return status;
1352 }
1353
1354 /* convert a single name to a sid in a domain */
1355 static NTSTATUS name_to_sid(struct winbindd_domain *domain,
1356                             TALLOC_CTX *mem_ctx,
1357                             const char *domain_name,
1358                             const char *name,
1359                             DOM_SID *sid,
1360                             enum lsa_SidType *type)
1361 {
1362         struct winbind_cache *cache = get_cache(domain);
1363         struct cache_entry *centry = NULL;
1364         NTSTATUS status;
1365         fstring uname;
1366
1367         if (!cache->tdb)
1368                 goto do_query;
1369
1370         fstrcpy(uname, name);
1371         strupper_m(uname);
1372         centry = wcache_fetch(cache, domain, "NS/%s/%s", domain_name, uname);
1373         if (!centry)
1374                 goto do_query;
1375         *type = (enum lsa_SidType)centry_uint32(centry);
1376         status = centry->status;
1377         if (NT_STATUS_IS_OK(status)) {
1378                 centry_sid(centry, mem_ctx, sid);
1379         }
1380
1381         DEBUG(10,("name_to_sid: [Cached] - cached name for domain %s status: %s\n",
1382                 domain->name, nt_errstr(status) ));
1383
1384         centry_free(centry);
1385         return status;
1386
1387 do_query:
1388         ZERO_STRUCTP(sid);
1389
1390         /* If the seq number check indicated that there is a problem
1391          * with this DC, then return that status... except for
1392          * access_denied.  This is special because the dc may be in
1393          * "restrict anonymous = 1" mode, in which case it will deny
1394          * most unauthenticated operations, but *will* allow the LSA
1395          * name-to-sid that we try as a fallback. */
1396
1397         if (!(NT_STATUS_IS_OK(domain->last_status)
1398               || NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)))
1399                 return domain->last_status;
1400
1401         DEBUG(10,("name_to_sid: [Cached] - doing backend query for name for domain %s\n",
1402                 domain->name ));
1403
1404         status = domain->backend->name_to_sid(domain, mem_ctx, domain_name, name, sid, type);
1405
1406         /* and save it */
1407         refresh_sequence_number(domain, False);
1408
1409         if (domain->online && !is_null_sid(sid)) {
1410                 wcache_save_name_to_sid(domain, status, domain_name, name, sid, *type);
1411         }
1412
1413         if (NT_STATUS_IS_OK(status)) {
1414                 strupper_m(CONST_DISCARD(char *,domain_name));
1415                 strlower_m(CONST_DISCARD(char *,name));
1416                 wcache_save_sid_to_name(domain, status, sid, domain_name, name, *type);
1417         }
1418
1419         return status;
1420 }
1421
1422 /* convert a sid to a user or group name. The sid is guaranteed to be in the domain
1423    given */
1424 static NTSTATUS sid_to_name(struct winbindd_domain *domain,
1425                             TALLOC_CTX *mem_ctx,
1426                             const DOM_SID *sid,
1427                             char **domain_name,
1428                             char **name,
1429                             enum lsa_SidType *type)
1430 {
1431         struct winbind_cache *cache = get_cache(domain);
1432         struct cache_entry *centry = NULL;
1433         NTSTATUS status;
1434         fstring sid_string;
1435
1436         if (!cache->tdb)
1437                 goto do_query;
1438
1439         centry = wcache_fetch(cache, domain, "SN/%s", sid_to_string(sid_string, sid));
1440         if (!centry)
1441                 goto do_query;
1442         if (NT_STATUS_IS_OK(centry->status)) {
1443                 *type = (enum lsa_SidType)centry_uint32(centry);
1444                 *domain_name = centry_string(centry, mem_ctx);
1445                 *name = centry_string(centry, mem_ctx);
1446         }
1447         status = centry->status;
1448
1449         DEBUG(10,("sid_to_name: [Cached] - cached name for domain %s status: %s\n",
1450                 domain->name, nt_errstr(status) ));
1451
1452         centry_free(centry);
1453         return status;
1454
1455 do_query:
1456         *name = NULL;
1457         *domain_name = NULL;
1458
1459         /* If the seq number check indicated that there is a problem
1460          * with this DC, then return that status... except for
1461          * access_denied.  This is special because the dc may be in
1462          * "restrict anonymous = 1" mode, in which case it will deny
1463          * most unauthenticated operations, but *will* allow the LSA
1464          * sid-to-name that we try as a fallback. */
1465
1466         if (!(NT_STATUS_IS_OK(domain->last_status)
1467               || NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)))
1468                 return domain->last_status;
1469
1470         DEBUG(10,("sid_to_name: [Cached] - doing backend query for name for domain %s\n",
1471                 domain->name ));
1472
1473         status = domain->backend->sid_to_name(domain, mem_ctx, sid, domain_name, name, type);
1474
1475         /* and save it */
1476         refresh_sequence_number(domain, False);
1477         wcache_save_sid_to_name(domain, status, sid, *domain_name, *name, *type);
1478
1479         /* We can't save the name to sid mapping here, as with sid history a
1480          * later name2sid would give the wrong sid. */
1481
1482         return status;
1483 }
1484
1485 static NTSTATUS rids_to_names(struct winbindd_domain *domain,
1486                               TALLOC_CTX *mem_ctx,
1487                               const DOM_SID *domain_sid,
1488                               uint32 *rids,
1489                               size_t num_rids,
1490                               char **domain_name,
1491                               char ***names,
1492                               enum lsa_SidType **types)
1493 {
1494         struct winbind_cache *cache = get_cache(domain);
1495         size_t i;
1496         NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
1497         BOOL have_mapped;
1498         BOOL have_unmapped;
1499
1500         *domain_name = NULL;
1501         *names = NULL;
1502         *types = NULL;
1503
1504         if (!cache->tdb) {
1505                 goto do_query;
1506         }
1507
1508         if (num_rids == 0) {
1509                 return NT_STATUS_OK;
1510         }
1511
1512         *names = TALLOC_ARRAY(mem_ctx, char *, num_rids);
1513         *types = TALLOC_ARRAY(mem_ctx, enum lsa_SidType, num_rids);
1514
1515         if ((*names == NULL) || (*types == NULL)) {
1516                 result = NT_STATUS_NO_MEMORY;
1517                 goto error;
1518         }
1519
1520         have_mapped = have_unmapped = False;
1521
1522         for (i=0; i<num_rids; i++) {
1523                 DOM_SID sid;
1524                 struct cache_entry *centry;
1525
1526                 if (!sid_compose(&sid, domain_sid, rids[i])) {
1527                         result = NT_STATUS_INTERNAL_ERROR;
1528                         goto error;
1529                 }
1530
1531                 centry = wcache_fetch(cache, domain, "SN/%s",
1532                                       sid_string_static(&sid));
1533                 if (!centry) {
1534                         goto do_query;
1535                 }
1536
1537                 (*types)[i] = SID_NAME_UNKNOWN;
1538                 (*names)[i] = talloc_strdup(*names, "");
1539
1540                 if (NT_STATUS_IS_OK(centry->status)) {
1541                         char *dom;
1542                         have_mapped = True;
1543                         (*types)[i] = (enum lsa_SidType)centry_uint32(centry);
1544                         dom = centry_string(centry, mem_ctx);
1545                         if (*domain_name == NULL) {
1546                                 *domain_name = dom;
1547                         } else {
1548                                 talloc_free(dom);
1549                         }
1550                         (*names)[i] = centry_string(centry, *names);
1551                 } else {
1552                         have_unmapped = True;
1553                 }
1554
1555                 centry_free(centry);
1556         }
1557
1558         if (!have_mapped) {
1559                 return NT_STATUS_NONE_MAPPED;
1560         }
1561         if (!have_unmapped) {
1562                 return NT_STATUS_OK;
1563         }
1564         return STATUS_SOME_UNMAPPED;
1565
1566  do_query:
1567
1568         TALLOC_FREE(*names);
1569         TALLOC_FREE(*types);
1570
1571         result = domain->backend->rids_to_names(domain, mem_ctx, domain_sid,
1572                                                 rids, num_rids, domain_name,
1573                                                 names, types);
1574
1575         if (!NT_STATUS_IS_OK(result) &&
1576             !NT_STATUS_EQUAL(result, STATUS_SOME_UNMAPPED)) {
1577                 return result;
1578         }
1579
1580         refresh_sequence_number(domain, False);
1581
1582         for (i=0; i<num_rids; i++) {
1583                 DOM_SID sid;
1584                 NTSTATUS status;
1585
1586                 if (!sid_compose(&sid, domain_sid, rids[i])) {
1587                         result = NT_STATUS_INTERNAL_ERROR;
1588                         goto error;
1589                 }
1590
1591                 status = (*types)[i] == SID_NAME_UNKNOWN ?
1592                         NT_STATUS_NONE_MAPPED : NT_STATUS_OK;
1593
1594                 wcache_save_sid_to_name(domain, status, &sid, *domain_name,
1595                                         (*names)[i], (*types)[i]);
1596         }
1597
1598         return result;
1599
1600  error:
1601         
1602         TALLOC_FREE(*names);
1603         TALLOC_FREE(*types);
1604         return result;
1605 }
1606
1607 /* Lookup user information from a rid */
1608 static NTSTATUS query_user(struct winbindd_domain *domain, 
1609                            TALLOC_CTX *mem_ctx, 
1610                            const DOM_SID *user_sid, 
1611                            WINBIND_USERINFO *info)
1612 {
1613         struct winbind_cache *cache = get_cache(domain);
1614         struct cache_entry *centry = NULL;
1615         NTSTATUS status;
1616
1617         if (!cache->tdb)
1618                 goto do_query;
1619
1620         centry = wcache_fetch(cache, domain, "U/%s", sid_string_static(user_sid));
1621         
1622         /* If we have an access denied cache entry and a cached info3 in the
1623            samlogon cache then do a query.  This will force the rpc back end
1624            to return the info3 data. */
1625
1626         if (NT_STATUS_V(domain->last_status) == NT_STATUS_V(NT_STATUS_ACCESS_DENIED) &&
1627             netsamlogon_cache_have(user_sid)) {
1628                 DEBUG(10, ("query_user: cached access denied and have cached info3\n"));
1629                 domain->last_status = NT_STATUS_OK;
1630                 centry_free(centry);
1631                 goto do_query;
1632         }
1633         
1634         if (!centry)
1635                 goto do_query;
1636
1637         info->acct_name = centry_string(centry, mem_ctx);
1638         info->full_name = centry_string(centry, mem_ctx);
1639         info->homedir = centry_string(centry, mem_ctx);
1640         info->shell = centry_string(centry, mem_ctx);
1641         info->primary_gid = centry_uint32(centry);
1642         centry_sid(centry, mem_ctx, &info->user_sid);
1643         centry_sid(centry, mem_ctx, &info->group_sid);
1644         status = centry->status;
1645
1646         DEBUG(10,("query_user: [Cached] - cached info for domain %s status: %s\n",
1647                 domain->name, nt_errstr(status) ));
1648
1649         centry_free(centry);
1650         return status;
1651
1652 do_query:
1653         ZERO_STRUCTP(info);
1654
1655         /* Return status value returned by seq number check */
1656
1657         if (!NT_STATUS_IS_OK(domain->last_status))
1658                 return domain->last_status;
1659         
1660         DEBUG(10,("query_user: [Cached] - doing backend query for info for domain %s\n",
1661                 domain->name ));
1662
1663         status = domain->backend->query_user(domain, mem_ctx, user_sid, info);
1664
1665         /* and save it */
1666         refresh_sequence_number(domain, False);
1667         wcache_save_user(domain, status, info);
1668
1669         return status;
1670 }
1671
1672
1673 /* Lookup groups a user is a member of. */
1674 static NTSTATUS lookup_usergroups(struct winbindd_domain *domain,
1675                                   TALLOC_CTX *mem_ctx,
1676                                   const DOM_SID *user_sid, 
1677                                   uint32 *num_groups, DOM_SID **user_gids)
1678 {
1679         struct winbind_cache *cache = get_cache(domain);
1680         struct cache_entry *centry = NULL;
1681         NTSTATUS status;
1682         unsigned int i;
1683         fstring sid_string;
1684
1685         if (!cache->tdb)
1686                 goto do_query;
1687
1688         centry = wcache_fetch(cache, domain, "UG/%s", sid_to_string(sid_string, user_sid));
1689         
1690         /* If we have an access denied cache entry and a cached info3 in the
1691            samlogon cache then do a query.  This will force the rpc back end
1692            to return the info3 data. */
1693
1694         if (NT_STATUS_V(domain->last_status) == NT_STATUS_V(NT_STATUS_ACCESS_DENIED) &&
1695             netsamlogon_cache_have(user_sid)) {
1696                 DEBUG(10, ("lookup_usergroups: cached access denied and have cached info3\n"));
1697                 domain->last_status = NT_STATUS_OK;
1698                 centry_free(centry);
1699                 goto do_query;
1700         }
1701         
1702         if (!centry)
1703                 goto do_query;
1704
1705         *num_groups = centry_uint32(centry);
1706         
1707         if (*num_groups == 0)
1708                 goto do_cached;
1709
1710         (*user_gids) = TALLOC_ARRAY(mem_ctx, DOM_SID, *num_groups);
1711         if (! (*user_gids)) {
1712                 smb_panic_fn("lookup_usergroups out of memory");
1713                 centry_free(centry);
1714                 return NT_STATUS_NO_MEMORY;
1715         }
1716         for (i=0; i<(*num_groups); i++) {
1717                 centry_sid(centry, mem_ctx, &(*user_gids)[i]);
1718         }
1719
1720 do_cached:      
1721         status = centry->status;
1722
1723         DEBUG(10,("lookup_usergroups: [Cached] - cached info for domain %s status: %s\n",
1724                 domain->name, nt_errstr(status) ));
1725
1726         centry_free(centry);
1727         return status;
1728
1729 do_query:
1730         (*num_groups) = 0;
1731         (*user_gids) = NULL;
1732
1733         /* Return status value returned by seq number check */
1734
1735         if (!NT_STATUS_IS_OK(domain->last_status))
1736                 return domain->last_status;
1737
1738         DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info for domain %s\n",
1739                 domain->name ));
1740
1741         status = domain->backend->lookup_usergroups(domain, mem_ctx, user_sid, num_groups, user_gids);
1742
1743         /* and save it */
1744         refresh_sequence_number(domain, False);
1745         centry = centry_start(domain, status);
1746         if (!centry)
1747                 goto skip_save;
1748         centry_put_uint32(centry, *num_groups);
1749         for (i=0; i<(*num_groups); i++) {
1750                 centry_put_sid(centry, &(*user_gids)[i]);
1751         }       
1752         centry_end(centry, "UG/%s", sid_to_string(sid_string, user_sid));
1753         centry_free(centry);
1754
1755 skip_save:
1756         return status;
1757 }
1758
1759 static NTSTATUS lookup_useraliases(struct winbindd_domain *domain,
1760                                    TALLOC_CTX *mem_ctx,
1761                                    uint32 num_sids, const DOM_SID *sids,
1762                                    uint32 *num_aliases, uint32 **alias_rids)
1763 {
1764         struct winbind_cache *cache = get_cache(domain);
1765         struct cache_entry *centry = NULL;
1766         NTSTATUS status;
1767         char *sidlist = talloc_strdup(mem_ctx, "");
1768         int i;
1769
1770         if (!cache->tdb)
1771                 goto do_query;
1772
1773         if (num_sids == 0) {
1774                 *num_aliases = 0;
1775                 *alias_rids = NULL;
1776                 return NT_STATUS_OK;
1777         }
1778
1779         /* We need to cache indexed by the whole list of SIDs, the aliases
1780          * resulting might come from any of the SIDs. */
1781
1782         for (i=0; i<num_sids; i++) {
1783                 sidlist = talloc_asprintf(mem_ctx, "%s/%s", sidlist,
1784                                           sid_string_static(&sids[i]));
1785                 if (sidlist == NULL)
1786                         return NT_STATUS_NO_MEMORY;
1787         }
1788
1789         centry = wcache_fetch(cache, domain, "UA%s", sidlist);
1790
1791         if (!centry)
1792                 goto do_query;
1793
1794         *num_aliases = centry_uint32(centry);
1795         *alias_rids = NULL;
1796
1797         (*alias_rids) = TALLOC_ARRAY(mem_ctx, uint32, *num_aliases);
1798
1799         if ((*num_aliases != 0) && ((*alias_rids) == NULL)) {
1800                 centry_free(centry);
1801                 return NT_STATUS_NO_MEMORY;
1802         }
1803
1804         for (i=0; i<(*num_aliases); i++)
1805                 (*alias_rids)[i] = centry_uint32(centry);
1806
1807         status = centry->status;
1808
1809         DEBUG(10,("lookup_useraliases: [Cached] - cached info for domain: %s "
1810                   "status %s\n", domain->name, nt_errstr(status)));
1811
1812         centry_free(centry);
1813         return status;
1814
1815  do_query:
1816         (*num_aliases) = 0;
1817         (*alias_rids) = NULL;
1818
1819         if (!NT_STATUS_IS_OK(domain->last_status))
1820                 return domain->last_status;
1821
1822         DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info "
1823                   "for domain %s\n", domain->name ));
1824
1825         status = domain->backend->lookup_useraliases(domain, mem_ctx,
1826                                                      num_sids, sids,
1827                                                      num_aliases, alias_rids);
1828
1829         /* and save it */
1830         refresh_sequence_number(domain, False);
1831         centry = centry_start(domain, status);
1832         if (!centry)
1833                 goto skip_save;
1834         centry_put_uint32(centry, *num_aliases);
1835         for (i=0; i<(*num_aliases); i++)
1836                 centry_put_uint32(centry, (*alias_rids)[i]);
1837         centry_end(centry, "UA%s", sidlist);
1838         centry_free(centry);
1839
1840  skip_save:
1841         return status;
1842 }
1843
1844
1845 static NTSTATUS lookup_groupmem(struct winbindd_domain *domain,
1846                                 TALLOC_CTX *mem_ctx,
1847                                 const DOM_SID *group_sid, uint32 *num_names, 
1848                                 DOM_SID **sid_mem, char ***names, 
1849                                 uint32 **name_types)
1850 {
1851         struct winbind_cache *cache = get_cache(domain);
1852         struct cache_entry *centry = NULL;
1853         NTSTATUS status;
1854         unsigned int i;
1855         fstring sid_string;
1856
1857         if (!cache->tdb)
1858                 goto do_query;
1859
1860         centry = wcache_fetch(cache, domain, "GM/%s", sid_to_string(sid_string, group_sid));
1861         if (!centry)
1862                 goto do_query;
1863
1864         *num_names = centry_uint32(centry);
1865         
1866         if (*num_names == 0)
1867                 goto do_cached;
1868
1869         (*sid_mem) = TALLOC_ARRAY(mem_ctx, DOM_SID, *num_names);
1870         (*names) = TALLOC_ARRAY(mem_ctx, char *, *num_names);
1871         (*name_types) = TALLOC_ARRAY(mem_ctx, uint32, *num_names);
1872
1873         if (! (*sid_mem) || ! (*names) || ! (*name_types)) {
1874                 smb_panic_fn("lookup_groupmem out of memory");
1875                 centry_free(centry);
1876                 return NT_STATUS_NO_MEMORY;
1877         }
1878
1879         for (i=0; i<(*num_names); i++) {
1880                 centry_sid(centry, mem_ctx, &(*sid_mem)[i]);
1881                 (*names)[i] = centry_string(centry, mem_ctx);
1882                 (*name_types)[i] = centry_uint32(centry);
1883         }
1884
1885 do_cached:      
1886         status = centry->status;
1887
1888         DEBUG(10,("lookup_groupmem: [Cached] - cached info for domain %s status: %s\n",
1889                 domain->name, nt_errstr(status)));
1890
1891         centry_free(centry);
1892         return status;
1893
1894 do_query:
1895         (*num_names) = 0;
1896         (*sid_mem) = NULL;
1897         (*names) = NULL;
1898         (*name_types) = NULL;
1899         
1900         /* Return status value returned by seq number check */
1901
1902         if (!NT_STATUS_IS_OK(domain->last_status))
1903                 return domain->last_status;
1904
1905         DEBUG(10,("lookup_groupmem: [Cached] - doing backend query for info for domain %s\n",
1906                 domain->name ));
1907
1908         status = domain->backend->lookup_groupmem(domain, mem_ctx, group_sid, num_names, 
1909                                                   sid_mem, names, name_types);
1910
1911         /* and save it */
1912         refresh_sequence_number(domain, False);
1913         centry = centry_start(domain, status);
1914         if (!centry)
1915                 goto skip_save;
1916         centry_put_uint32(centry, *num_names);
1917         for (i=0; i<(*num_names); i++) {
1918                 centry_put_sid(centry, &(*sid_mem)[i]);
1919                 centry_put_string(centry, (*names)[i]);
1920                 centry_put_uint32(centry, (*name_types)[i]);
1921         }       
1922         centry_end(centry, "GM/%s", sid_to_string(sid_string, group_sid));
1923         centry_free(centry);
1924
1925 skip_save:
1926         return status;
1927 }
1928
1929 /* find the sequence number for a domain */
1930 static NTSTATUS sequence_number(struct winbindd_domain *domain, uint32 *seq)
1931 {
1932         refresh_sequence_number(domain, False);
1933
1934         *seq = domain->sequence_number;
1935
1936         return NT_STATUS_OK;
1937 }
1938
1939 /* enumerate trusted domains 
1940  * (we need to have the list of trustdoms in the cache when we go offline) -
1941  * Guenther */
1942 static NTSTATUS trusted_domains(struct winbindd_domain *domain,
1943                                 TALLOC_CTX *mem_ctx,
1944                                 uint32 *num_domains,
1945                                 char ***names,
1946                                 char ***alt_names,
1947                                 DOM_SID **dom_sids)
1948 {
1949         struct winbind_cache *cache = get_cache(domain);
1950         struct cache_entry *centry = NULL;
1951         NTSTATUS status;
1952         int i;
1953  
1954         if (!cache->tdb)
1955                 goto do_query;
1956  
1957         centry = wcache_fetch(cache, domain, "TRUSTDOMS/%s", domain->name);
1958         
1959         if (!centry) {
1960                 goto do_query;
1961         }
1962  
1963         *num_domains = centry_uint32(centry);
1964         
1965         (*names)        = TALLOC_ARRAY(mem_ctx, char *, *num_domains);
1966         (*alt_names)    = TALLOC_ARRAY(mem_ctx, char *, *num_domains);
1967         (*dom_sids)     = TALLOC_ARRAY(mem_ctx, DOM_SID, *num_domains);
1968  
1969         if (! (*dom_sids) || ! (*names) || ! (*alt_names)) {
1970                 smb_panic_fn("trusted_domains out of memory");
1971                 centry_free(centry);
1972                 return NT_STATUS_NO_MEMORY;
1973         }
1974  
1975         for (i=0; i<(*num_domains); i++) {
1976                 (*names)[i] = centry_string(centry, mem_ctx);
1977                 (*alt_names)[i] = centry_string(centry, mem_ctx);
1978                 centry_sid(centry, mem_ctx, &(*dom_sids)[i]);
1979         }
1980
1981         status = centry->status;
1982  
1983         DEBUG(10,("trusted_domains: [Cached] - cached info for domain %s (%d trusts) status: %s\n",
1984                 domain->name, *num_domains, nt_errstr(status) ));
1985  
1986         centry_free(centry);
1987         return status;
1988  
1989 do_query:
1990         (*num_domains) = 0;
1991         (*dom_sids) = NULL;
1992         (*names) = NULL;
1993         (*alt_names) = NULL;
1994  
1995         /* Return status value returned by seq number check */
1996
1997         if (!NT_STATUS_IS_OK(domain->last_status))
1998                 return domain->last_status;
1999         
2000         DEBUG(10,("trusted_domains: [Cached] - doing backend query for info for domain %s\n",
2001                 domain->name ));
2002  
2003         status = domain->backend->trusted_domains(domain, mem_ctx, num_domains,
2004                                                 names, alt_names, dom_sids);
2005
2006         /* no trusts gives NT_STATUS_NO_MORE_ENTRIES resetting to NT_STATUS_OK
2007          * so that the generic centry handling still applies correctly -
2008          * Guenther*/
2009
2010         if (!NT_STATUS_IS_ERR(status)) {
2011                 status = NT_STATUS_OK;
2012         }
2013
2014         /* and save it */
2015         refresh_sequence_number(domain, False);
2016  
2017         centry = centry_start(domain, status);
2018         if (!centry)
2019                 goto skip_save;
2020
2021         centry_put_uint32(centry, *num_domains);
2022
2023         for (i=0; i<(*num_domains); i++) {
2024                 centry_put_string(centry, (*names)[i]);
2025                 centry_put_string(centry, (*alt_names)[i]);
2026                 centry_put_sid(centry, &(*dom_sids)[i]);
2027         }
2028         
2029         centry_end(centry, "TRUSTDOMS/%s", domain->name);
2030  
2031         centry_free(centry);
2032  
2033 skip_save:
2034         return status;
2035 }       
2036
2037 /* get lockout policy */
2038 static NTSTATUS lockout_policy(struct winbindd_domain *domain,
2039                                TALLOC_CTX *mem_ctx,
2040                                SAM_UNK_INFO_12 *policy){
2041         struct winbind_cache *cache = get_cache(domain);
2042         struct cache_entry *centry = NULL;
2043         NTSTATUS status;
2044  
2045         if (!cache->tdb)
2046                 goto do_query;
2047  
2048         centry = wcache_fetch(cache, domain, "LOC_POL/%s", domain->name);
2049         
2050         if (!centry)
2051                 goto do_query;
2052  
2053         policy->duration = centry_nttime(centry);
2054         policy->reset_count = centry_nttime(centry);
2055         policy->bad_attempt_lockout = centry_uint16(centry);
2056  
2057         status = centry->status;
2058  
2059         DEBUG(10,("lockout_policy: [Cached] - cached info for domain %s status: %s\n",
2060                 domain->name, nt_errstr(status) ));
2061  
2062         centry_free(centry);
2063         return status;
2064  
2065 do_query:
2066         ZERO_STRUCTP(policy);
2067  
2068         /* Return status value returned by seq number check */
2069
2070         if (!NT_STATUS_IS_OK(domain->last_status))
2071                 return domain->last_status;
2072         
2073         DEBUG(10,("lockout_policy: [Cached] - doing backend query for info for domain %s\n",
2074                 domain->name ));
2075  
2076         status = domain->backend->lockout_policy(domain, mem_ctx, policy); 
2077  
2078         /* and save it */
2079         refresh_sequence_number(domain, False);
2080         wcache_save_lockout_policy(domain, status, policy);
2081  
2082         return status;
2083 }
2084  
2085 /* get password policy */
2086 static NTSTATUS password_policy(struct winbindd_domain *domain,
2087                                 TALLOC_CTX *mem_ctx,
2088                                 SAM_UNK_INFO_1 *policy)
2089 {
2090         struct winbind_cache *cache = get_cache(domain);
2091         struct cache_entry *centry = NULL;
2092         NTSTATUS status;
2093
2094         if (!cache->tdb)
2095                 goto do_query;
2096  
2097         centry = wcache_fetch(cache, domain, "PWD_POL/%s", domain->name);
2098         
2099         if (!centry)
2100                 goto do_query;
2101
2102         policy->min_length_password = centry_uint16(centry);
2103         policy->password_history = centry_uint16(centry);
2104         policy->password_properties = centry_uint32(centry);
2105         policy->expire = centry_nttime(centry);
2106         policy->min_passwordage = centry_nttime(centry);
2107
2108         status = centry->status;
2109
2110         DEBUG(10,("lockout_policy: [Cached] - cached info for domain %s status: %s\n",
2111                 domain->name, nt_errstr(status) ));
2112
2113         centry_free(centry);
2114         return status;
2115
2116 do_query:
2117         ZERO_STRUCTP(policy);
2118
2119         /* Return status value returned by seq number check */
2120
2121         if (!NT_STATUS_IS_OK(domain->last_status))
2122                 return domain->last_status;
2123         
2124         DEBUG(10,("password_policy: [Cached] - doing backend query for info for domain %s\n",
2125                 domain->name ));
2126
2127         status = domain->backend->password_policy(domain, mem_ctx, policy); 
2128
2129         /* and save it */
2130         refresh_sequence_number(domain, False);
2131         wcache_save_password_policy(domain, status, policy);
2132
2133         return status;
2134 }
2135
2136
2137 /* Invalidate cached user and group lists coherently */
2138
2139 static int traverse_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf, 
2140                        void *state)
2141 {
2142         if (strncmp((const char *)kbuf.dptr, "UL/", 3) == 0 ||
2143             strncmp((const char *)kbuf.dptr, "GL/", 3) == 0)
2144                 tdb_delete(the_tdb, kbuf);
2145
2146         return 0;
2147 }
2148
2149 /* Invalidate the getpwnam and getgroups entries for a winbindd domain */
2150
2151 void wcache_invalidate_samlogon(struct winbindd_domain *domain, 
2152                                 NET_USER_INFO_3 *info3)
2153 {
2154         struct winbind_cache *cache;
2155         
2156         if (!domain)
2157                 return;
2158
2159         cache = get_cache(domain);
2160         netsamlogon_clear_cached_user(cache->tdb, info3);
2161 }
2162
2163 void wcache_invalidate_cache(void)
2164 {
2165         struct winbindd_domain *domain;
2166
2167         for (domain = domain_list(); domain; domain = domain->next) {
2168                 struct winbind_cache *cache = get_cache(domain);
2169
2170                 DEBUG(10, ("wcache_invalidate_cache: invalidating cache "
2171                            "entries for %s\n", domain->name));
2172                 if (cache)
2173                         tdb_traverse(cache->tdb, traverse_fn, NULL);
2174         }
2175 }
2176
2177 static BOOL init_wcache(void)
2178 {
2179         if (wcache == NULL) {
2180                 wcache = SMB_XMALLOC_P(struct winbind_cache);
2181                 ZERO_STRUCTP(wcache);
2182         }
2183
2184         if (wcache->tdb != NULL)
2185                 return True;
2186
2187         /* when working offline we must not clear the cache on restart */
2188         wcache->tdb = tdb_open_log(lock_path("winbindd_cache.tdb"),
2189                                 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE, 
2190                                 lp_winbind_offline_logon() ? TDB_DEFAULT : (TDB_DEFAULT | TDB_CLEAR_IF_FIRST), 
2191                                 O_RDWR|O_CREAT, 0600);
2192
2193         if (wcache->tdb == NULL) {
2194                 DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
2195                 return False;
2196         }
2197
2198         return True;
2199 }
2200
2201 /************************************************************************
2202  This is called by the parent to initialize the cache file.
2203  We don't need sophisticated locking here as we know we're the
2204  only opener.
2205 ************************************************************************/
2206
2207 BOOL initialize_winbindd_cache(void)
2208 {
2209         BOOL cache_bad = True;
2210         uint32 vers;
2211
2212         if (!init_wcache()) {
2213                 DEBUG(0,("initialize_winbindd_cache: init_wcache failed.\n"));
2214                 return False;
2215         }
2216
2217         /* Check version number. */
2218         if (tdb_fetch_uint32(wcache->tdb, WINBINDD_CACHE_VERSION_KEYSTR, &vers) &&
2219                         vers == WINBINDD_CACHE_VERSION) {
2220                 cache_bad = False;
2221         }
2222
2223         if (cache_bad) {
2224                 DEBUG(0,("initialize_winbindd_cache: clearing cache "
2225                         "and re-creating with version number %d\n",
2226                         WINBINDD_CACHE_VERSION ));
2227
2228                 tdb_close(wcache->tdb);
2229                 wcache->tdb = NULL;
2230
2231                 if (unlink(lock_path("winbindd_cache.tdb")) == -1) {
2232                         DEBUG(0,("initialize_winbindd_cache: unlink %s failed %s ",
2233                                 lock_path("winbindd_cache.tdb"),
2234                                 strerror(errno) ));
2235                         return False;
2236                 }
2237                 if (!init_wcache()) {
2238                         DEBUG(0,("initialize_winbindd_cache: re-initialization "
2239                                         "init_wcache failed.\n"));
2240                         return False;
2241                 }
2242
2243                 /* Write the version. */
2244                 if (!tdb_store_uint32(wcache->tdb, WINBINDD_CACHE_VERSION_KEYSTR, WINBINDD_CACHE_VERSION)) {
2245                         DEBUG(0,("initialize_winbindd_cache: version number store failed %s\n",
2246                                 tdb_errorstr(wcache->tdb) ));
2247                         return False;
2248                 }
2249         }
2250
2251         tdb_close(wcache->tdb);
2252         wcache->tdb = NULL;
2253         return True;
2254 }
2255
2256 void cache_store_response(pid_t pid, struct winbindd_response *response)
2257 {
2258         fstring key_str;
2259
2260         if (!init_wcache())
2261                 return;
2262
2263         DEBUG(10, ("Storing response for pid %d, len %d\n",
2264                    pid, response->length));
2265
2266         fstr_sprintf(key_str, "DR/%d", pid);
2267         if (tdb_store(wcache->tdb, string_tdb_data(key_str), 
2268                       make_tdb_data((uint8 *)response, sizeof(*response)),
2269                       TDB_REPLACE) == -1)
2270                 return;
2271
2272         if (response->length == sizeof(*response))
2273                 return;
2274
2275         /* There's extra data */
2276
2277         DEBUG(10, ("Storing extra data: len=%d\n",
2278                    (int)(response->length - sizeof(*response))));
2279
2280         fstr_sprintf(key_str, "DE/%d", pid);
2281         if (tdb_store(wcache->tdb, string_tdb_data(key_str),
2282                       make_tdb_data(response->extra_data.data,
2283                                     response->length - sizeof(*response)),
2284                       TDB_REPLACE) == 0)
2285                 return;
2286
2287         /* We could not store the extra data, make sure the tdb does not
2288          * contain a main record with wrong dangling extra data */
2289
2290         fstr_sprintf(key_str, "DR/%d", pid);
2291         tdb_delete(wcache->tdb, string_tdb_data(key_str));
2292
2293         return;
2294 }
2295
2296 BOOL cache_retrieve_response(pid_t pid, struct winbindd_response * response)
2297 {
2298         TDB_DATA data;
2299         fstring key_str;
2300
2301         if (!init_wcache())
2302                 return False;
2303
2304         DEBUG(10, ("Retrieving response for pid %d\n", pid));
2305
2306         fstr_sprintf(key_str, "DR/%d", pid);
2307         data = tdb_fetch(wcache->tdb, string_tdb_data(key_str));
2308
2309         if (data.dptr == NULL)
2310                 return False;
2311
2312         if (data.dsize != sizeof(*response))
2313                 return False;
2314
2315         memcpy(response, data.dptr, data.dsize);
2316         SAFE_FREE(data.dptr);
2317
2318         if (response->length == sizeof(*response)) {
2319                 response->extra_data.data = NULL;
2320                 return True;
2321         }
2322
2323         /* There's extra data */
2324
2325         DEBUG(10, ("Retrieving extra data length=%d\n",
2326                    (int)(response->length - sizeof(*response))));
2327
2328         fstr_sprintf(key_str, "DE/%d", pid);
2329         data = tdb_fetch(wcache->tdb, string_tdb_data(key_str));
2330
2331         if (data.dptr == NULL) {
2332                 DEBUG(0, ("Did not find extra data\n"));
2333                 return False;
2334         }
2335
2336         if (data.dsize != (response->length - sizeof(*response))) {
2337                 DEBUG(0, ("Invalid extra data length: %d\n", (int)data.dsize));
2338                 SAFE_FREE(data.dptr);
2339                 return False;
2340         }
2341
2342         dump_data(11, (uint8 *)data.dptr, data.dsize);
2343
2344         response->extra_data.data = data.dptr;
2345         return True;
2346 }
2347
2348 void cache_cleanup_response(pid_t pid)
2349 {
2350         fstring key_str;
2351
2352         if (!init_wcache())
2353                 return;
2354
2355         fstr_sprintf(key_str, "DR/%d", pid);
2356         tdb_delete(wcache->tdb, string_tdb_data(key_str));
2357
2358         fstr_sprintf(key_str, "DE/%d", pid);
2359         tdb_delete(wcache->tdb, string_tdb_data(key_str));
2360
2361         return;
2362 }
2363
2364
2365 BOOL lookup_cached_sid(TALLOC_CTX *mem_ctx, const DOM_SID *sid,
2366                        char **domain_name, char **name,
2367                        enum lsa_SidType *type)
2368 {
2369         struct winbindd_domain *domain;
2370         struct winbind_cache *cache;
2371         struct cache_entry *centry = NULL;
2372         NTSTATUS status;
2373
2374         domain = find_lookup_domain_from_sid(sid);
2375         if (domain == NULL) {
2376                 return False;
2377         }
2378
2379         cache = get_cache(domain);
2380
2381         if (cache->tdb == NULL) {
2382                 return False;
2383         }
2384
2385         centry = wcache_fetch(cache, domain, "SN/%s", sid_string_static(sid));
2386         if (centry == NULL) {
2387                 return False;
2388         }
2389
2390         if (NT_STATUS_IS_OK(centry->status)) {
2391                 *type = (enum lsa_SidType)centry_uint32(centry);
2392                 *domain_name = centry_string(centry, mem_ctx);
2393                 *name = centry_string(centry, mem_ctx);
2394         }
2395
2396         status = centry->status;
2397         centry_free(centry);
2398         return NT_STATUS_IS_OK(status);
2399 }
2400
2401 BOOL lookup_cached_name(TALLOC_CTX *mem_ctx,
2402                         const char *domain_name,
2403                         const char *name,
2404                         DOM_SID *sid,
2405                         enum lsa_SidType *type)
2406 {
2407         struct winbindd_domain *domain;
2408         struct winbind_cache *cache;
2409         struct cache_entry *centry = NULL;
2410         NTSTATUS status;
2411         fstring uname;
2412
2413         domain = find_lookup_domain_from_name(domain_name);
2414         if (domain == NULL) {
2415                 return False;
2416         }
2417
2418         cache = get_cache(domain);
2419
2420         if (cache->tdb == NULL) {
2421                 return False;
2422         }
2423
2424         fstrcpy(uname, name);
2425         strupper_m(uname);
2426         
2427         centry = wcache_fetch(cache, domain, "NS/%s/%s", domain_name, uname);
2428         if (centry == NULL) {
2429                 return False;
2430         }
2431
2432         if (NT_STATUS_IS_OK(centry->status)) {
2433                 *type = (enum lsa_SidType)centry_uint32(centry);
2434                 centry_sid(centry, mem_ctx, sid);
2435         }
2436
2437         status = centry->status;
2438         centry_free(centry);
2439         
2440         return NT_STATUS_IS_OK(status);
2441 }
2442
2443 void cache_name2sid(struct winbindd_domain *domain, 
2444                     const char *domain_name, const char *name,
2445                     enum lsa_SidType type, const DOM_SID *sid)
2446 {
2447         refresh_sequence_number(domain, False);
2448         wcache_save_name_to_sid(domain, NT_STATUS_OK, domain_name, name,
2449                                 sid, type);
2450 }
2451
2452 /*
2453  * The original idea that this cache only contains centries has
2454  * been blurred - now other stuff gets put in here. Ensure we
2455  * ignore these things on cleanup.
2456  */
2457
2458 static int traverse_fn_cleanup(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, 
2459                                TDB_DATA dbuf, void *state)
2460 {
2461         struct cache_entry *centry;
2462
2463         if (is_non_centry_key(kbuf)) {
2464                 return 0;
2465         }
2466
2467         centry = wcache_fetch_raw((char *)kbuf.dptr);
2468         if (!centry) {
2469                 return 0;
2470         }
2471
2472         if (!NT_STATUS_IS_OK(centry->status)) {
2473                 DEBUG(10,("deleting centry %s\n", (const char *)kbuf.dptr));
2474                 tdb_delete(the_tdb, kbuf);
2475         }
2476
2477         centry_free(centry);
2478         return 0;
2479 }
2480
2481 /* flush the cache */
2482 void wcache_flush_cache(void)
2483 {
2484         if (!wcache)
2485                 return;
2486         if (wcache->tdb) {
2487                 tdb_close(wcache->tdb);
2488                 wcache->tdb = NULL;
2489         }
2490         if (opt_nocache)
2491                 return;
2492
2493         /* when working offline we must not clear the cache on restart */
2494         wcache->tdb = tdb_open_log(lock_path("winbindd_cache.tdb"),
2495                                 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE, 
2496                                 lp_winbind_offline_logon() ? TDB_DEFAULT : (TDB_DEFAULT | TDB_CLEAR_IF_FIRST), 
2497                                 O_RDWR|O_CREAT, 0600);
2498
2499         if (!wcache->tdb) {
2500                 DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
2501                 return;
2502         }
2503
2504         tdb_traverse(wcache->tdb, traverse_fn_cleanup, NULL);
2505
2506         DEBUG(10,("wcache_flush_cache success\n"));
2507 }
2508
2509 /* Count cached creds */
2510
2511 static int traverse_fn_cached_creds(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf, 
2512                                     void *state)
2513 {
2514         int *cred_count = (int*)state;
2515  
2516         if (strncmp((const char *)kbuf.dptr, "CRED/", 5) == 0) {
2517                 (*cred_count)++;
2518         }
2519         return 0;
2520 }
2521
2522 NTSTATUS wcache_count_cached_creds(struct winbindd_domain *domain, int *count)
2523 {
2524         struct winbind_cache *cache = get_cache(domain);
2525
2526         *count = 0;
2527
2528         if (!cache->tdb) {
2529                 return NT_STATUS_INTERNAL_DB_ERROR;
2530         }
2531  
2532         tdb_traverse(cache->tdb, traverse_fn_cached_creds, (void *)count);
2533
2534         return NT_STATUS_OK;
2535 }
2536
2537 struct cred_list {
2538         struct cred_list *prev, *next;
2539         TDB_DATA key;
2540         fstring name;
2541         time_t created;
2542 };
2543 static struct cred_list *wcache_cred_list;
2544
2545 static int traverse_fn_get_credlist(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf, 
2546                                     void *state)
2547 {
2548         struct cred_list *cred;
2549
2550         if (strncmp((const char *)kbuf.dptr, "CRED/", 5) == 0) {
2551
2552                 cred = SMB_MALLOC_P(struct cred_list);
2553                 if (cred == NULL) {
2554                         DEBUG(0,("traverse_fn_remove_first_creds: failed to malloc new entry for list\n"));
2555                         return -1;
2556                 }
2557
2558                 ZERO_STRUCTP(cred);
2559                 
2560                 /* save a copy of the key */
2561                 
2562                 fstrcpy(cred->name, (const char *)kbuf.dptr);           
2563                 DLIST_ADD(wcache_cred_list, cred);
2564         }
2565         
2566         return 0;
2567 }
2568
2569 NTSTATUS wcache_remove_oldest_cached_creds(struct winbindd_domain *domain, const DOM_SID *sid) 
2570 {
2571         struct winbind_cache *cache = get_cache(domain);
2572         NTSTATUS status;
2573         int ret;
2574         struct cred_list *cred, *oldest = NULL;
2575
2576         if (!cache->tdb) {
2577                 return NT_STATUS_INTERNAL_DB_ERROR;
2578         }
2579
2580         /* we possibly already have an entry */
2581         if (sid && NT_STATUS_IS_OK(wcache_cached_creds_exist(domain, sid))) {
2582         
2583                 fstring key_str;
2584
2585                 DEBUG(11,("we already have an entry, deleting that\n"));
2586
2587                 fstr_sprintf(key_str, "CRED/%s", sid_string_static(sid));
2588
2589                 tdb_delete(cache->tdb, string_tdb_data(key_str));
2590
2591                 return NT_STATUS_OK;
2592         }
2593
2594         ret = tdb_traverse(cache->tdb, traverse_fn_get_credlist, NULL);
2595         if (ret == 0) {
2596                 return NT_STATUS_OK;
2597         } else if ((ret == -1) || (wcache_cred_list == NULL)) {
2598                 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
2599         }
2600
2601         ZERO_STRUCTP(oldest);
2602
2603         for (cred = wcache_cred_list; cred; cred = cred->next) {
2604
2605                 TDB_DATA data;
2606                 time_t t;
2607
2608                 data = tdb_fetch(cache->tdb, string_tdb_data(cred->name));
2609                 if (!data.dptr) {
2610                         DEBUG(10,("wcache_remove_oldest_cached_creds: entry for [%s] not found\n", 
2611                                 cred->name));
2612                         status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
2613                         goto done;
2614                 }
2615         
2616                 t = IVAL(data.dptr, 0);
2617                 SAFE_FREE(data.dptr);
2618
2619                 if (!oldest) {
2620                         oldest = SMB_MALLOC_P(struct cred_list);
2621                         if (oldest == NULL) {
2622                                 status = NT_STATUS_NO_MEMORY;
2623                                 goto done;
2624                         }
2625
2626                         fstrcpy(oldest->name, cred->name);
2627                         oldest->created = t;
2628                         continue;
2629                 }
2630
2631                 if (t < oldest->created) {
2632                         fstrcpy(oldest->name, cred->name);
2633                         oldest->created = t;
2634                 }
2635         }
2636
2637         if (tdb_delete(cache->tdb, string_tdb_data(oldest->name)) == 0) {
2638                 status = NT_STATUS_OK;
2639         } else {
2640                 status = NT_STATUS_UNSUCCESSFUL;
2641         }
2642 done:
2643         SAFE_FREE(wcache_cred_list);
2644         SAFE_FREE(oldest);
2645         
2646         return status;
2647 }
2648
2649 /* Change the global online/offline state. */
2650 BOOL set_global_winbindd_state_offline(void)
2651 {
2652         TDB_DATA data;
2653
2654         DEBUG(10,("set_global_winbindd_state_offline: offline requested.\n"));
2655
2656         /* Only go offline if someone has created
2657            the key "WINBINDD_OFFLINE" in the cache tdb. */
2658
2659         if (wcache == NULL || wcache->tdb == NULL) {
2660                 DEBUG(10,("set_global_winbindd_state_offline: wcache not open yet.\n"));
2661                 return False;
2662         }
2663
2664         if (!lp_winbind_offline_logon()) {
2665                 DEBUG(10,("set_global_winbindd_state_offline: rejecting.\n"));
2666                 return False;
2667         }
2668
2669         if (global_winbindd_offline_state) {
2670                 /* Already offline. */
2671                 return True;
2672         }
2673
2674         data = tdb_fetch_bystring( wcache->tdb, "WINBINDD_OFFLINE" );
2675
2676         if (!data.dptr || data.dsize != 4) {
2677                 DEBUG(10,("set_global_winbindd_state_offline: offline state not set.\n"));
2678                 SAFE_FREE(data.dptr);
2679                 return False;
2680         } else {
2681                 DEBUG(10,("set_global_winbindd_state_offline: offline state set.\n"));
2682                 global_winbindd_offline_state = True;
2683                 SAFE_FREE(data.dptr);
2684                 return True;
2685         }
2686 }
2687
2688 void set_global_winbindd_state_online(void)
2689 {
2690         DEBUG(10,("set_global_winbindd_state_online: online requested.\n"));
2691
2692         if (!lp_winbind_offline_logon()) {
2693                 DEBUG(10,("set_global_winbindd_state_online: rejecting.\n"));
2694                 return;
2695         }
2696
2697         if (!global_winbindd_offline_state) {
2698                 /* Already online. */
2699                 return;
2700         }
2701         global_winbindd_offline_state = False;
2702
2703         if (!wcache->tdb) {
2704                 return;
2705         }
2706
2707         /* Ensure there is no key "WINBINDD_OFFLINE" in the cache tdb. */
2708         tdb_delete_bystring(wcache->tdb, "WINBINDD_OFFLINE");
2709 }
2710
2711 BOOL get_global_winbindd_state_offline(void)
2712 {
2713         return global_winbindd_offline_state;
2714 }
2715
2716 /***********************************************************************
2717  Validate functions for all possible cache tdb keys.
2718 ***********************************************************************/
2719
2720 static BOOL bad_cache_entry;
2721
2722 static struct cache_entry *create_centry_validate(const char *kstr, TDB_DATA data)
2723 {
2724         struct cache_entry *centry;
2725
2726         centry = SMB_XMALLOC_P(struct cache_entry);
2727         centry->data = (unsigned char *)memdup(data.dptr, data.dsize);
2728         if (!centry->data) {
2729                 SAFE_FREE(centry);
2730                 return NULL;
2731         }
2732         centry->len = data.dsize;
2733         centry->ofs = 0;
2734
2735         if (centry->len < 8) {
2736                 /* huh? corrupt cache? */
2737                 DEBUG(0,("validate_create_centry: Corrupt cache for key %s (len < 8) ?\n", kstr));
2738                 centry_free(centry);
2739                 bad_cache_entry = True;
2740                 return NULL;
2741         }
2742
2743         centry->status = NT_STATUS(centry_uint32(centry));
2744         centry->sequence_number = centry_uint32(centry);
2745         return centry;
2746 }
2747
2748 static int validate_seqnum(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf)
2749 {
2750         if (dbuf.dsize != 8) {
2751                 DEBUG(0,("validate_seqnum: Corrupt cache for key %s (len %u != 8) ?\n",
2752                                 keystr, (unsigned int)dbuf.dsize ));
2753                 bad_cache_entry = True;
2754                 return 1;
2755         }
2756         return 0;
2757 }
2758
2759 static int validate_ns(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf)
2760 {
2761         struct cache_entry *centry = create_centry_validate(keystr, dbuf);
2762         if (!centry) {
2763                 return 1;
2764         }
2765
2766         (void)centry_uint32(centry);
2767         if (NT_STATUS_IS_OK(centry->status)) {
2768                 DOM_SID sid;
2769                 (void)centry_sid(centry, mem_ctx, &sid);
2770         }
2771
2772         centry_free(centry);
2773
2774         if (bad_cache_entry) {
2775                 return 1;
2776         }
2777         DEBUG(10,("validate_ns: %s ok\n", keystr));
2778         return 0;
2779 }
2780
2781 static int validate_sn(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf)
2782 {
2783         struct cache_entry *centry = create_centry_validate(keystr, dbuf);
2784         if (!centry) {
2785                 return 1;
2786         }
2787
2788         if (NT_STATUS_IS_OK(centry->status)) {
2789                 (void)centry_uint32(centry);
2790                 (void)centry_string(centry, mem_ctx);
2791                 (void)centry_string(centry, mem_ctx);
2792         }
2793
2794         centry_free(centry);
2795
2796         if (bad_cache_entry) {
2797                 return 1;
2798         }
2799         DEBUG(10,("validate_sn: %s ok\n", keystr));
2800         return 0;
2801 }
2802
2803 static int validate_u(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf)
2804 {
2805         struct cache_entry *centry = create_centry_validate(keystr, dbuf);
2806         DOM_SID sid;
2807
2808         if (!centry) {
2809                 return 1;
2810         }
2811
2812         (void)centry_string(centry, mem_ctx);
2813         (void)centry_string(centry, mem_ctx);
2814         (void)centry_string(centry, mem_ctx);
2815         (void)centry_string(centry, mem_ctx);
2816         (void)centry_uint32(centry);
2817         (void)centry_sid(centry, mem_ctx, &sid);
2818         (void)centry_sid(centry, mem_ctx, &sid);
2819
2820         centry_free(centry);
2821
2822         if (bad_cache_entry) {
2823                 return 1;
2824         }
2825         DEBUG(10,("validate_u: %s ok\n", keystr));
2826         return 0;
2827 }
2828
2829 static int validate_loc_pol(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf)
2830 {
2831         struct cache_entry *centry = create_centry_validate(keystr, dbuf);
2832
2833         if (!centry) {
2834                 return 1;
2835         }
2836
2837         (void)centry_nttime(centry);
2838         (void)centry_nttime(centry);
2839         (void)centry_uint16(centry);
2840
2841         centry_free(centry);
2842
2843         if (bad_cache_entry) {
2844                 return 1;
2845         }
2846         DEBUG(10,("validate_loc_pol: %s ok\n", keystr));
2847         return 0;
2848 }
2849
2850 static int validate_pwd_pol(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf)
2851 {
2852         struct cache_entry *centry = create_centry_validate(keystr, dbuf);
2853
2854         if (!centry) {
2855                 return 1;
2856         }
2857
2858         (void)centry_uint16(centry);
2859         (void)centry_uint16(centry);
2860         (void)centry_uint32(centry);
2861         (void)centry_nttime(centry);
2862         (void)centry_nttime(centry);
2863
2864         centry_free(centry);
2865
2866         if (bad_cache_entry) {
2867                 return 1;
2868         }
2869         DEBUG(10,("validate_pwd_pol: %s ok\n", keystr));
2870         return 0;
2871 }
2872
2873 static int validate_cred(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf)
2874 {
2875         struct cache_entry *centry = create_centry_validate(keystr, dbuf);
2876
2877         if (!centry) {
2878                 return 1;
2879         }
2880
2881         (void)centry_time(centry);
2882         (void)centry_hash16(centry, mem_ctx);
2883
2884         /* We only have 17 bytes more data in the salted cred case. */
2885         if (centry->len - centry->ofs == 17) {
2886                 (void)centry_hash16(centry, mem_ctx);
2887         }
2888
2889         centry_free(centry);
2890
2891         if (bad_cache_entry) {
2892                 return 1;
2893         }
2894         DEBUG(10,("validate_cred: %s ok\n", keystr));
2895         return 0;
2896 }
2897
2898 static int validate_ul(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf)
2899 {
2900         struct cache_entry *centry = create_centry_validate(keystr, dbuf);
2901         int32 num_entries, i;
2902
2903         if (!centry) {
2904                 return 1;
2905         }
2906
2907         num_entries = (int32)centry_uint32(centry);
2908
2909         for (i=0; i< num_entries; i++) {
2910                 DOM_SID sid;
2911                 (void)centry_string(centry, mem_ctx);
2912                 (void)centry_string(centry, mem_ctx);
2913                 (void)centry_string(centry, mem_ctx);
2914                 (void)centry_string(centry, mem_ctx);
2915                 (void)centry_sid(centry, mem_ctx, &sid);
2916                 (void)centry_sid(centry, mem_ctx, &sid);
2917         }
2918
2919         centry_free(centry);
2920
2921         if (bad_cache_entry) {
2922                 return 1;
2923         }
2924         DEBUG(10,("validate_ul: %s ok\n", keystr));
2925         return 0;
2926 }
2927
2928 static int validate_gl(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf)
2929 {
2930         struct cache_entry *centry = create_centry_validate(keystr, dbuf);
2931         int32 num_entries, i;
2932
2933         if (!centry) {
2934                 return 1;
2935         }
2936
2937         num_entries = centry_uint32(centry);
2938         
2939         for (i=0; i< num_entries; i++) {
2940                 (void)centry_string(centry, mem_ctx);
2941                 (void)centry_string(centry, mem_ctx);
2942                 (void)centry_uint32(centry);
2943         }
2944
2945         centry_free(centry);
2946
2947         if (bad_cache_entry) {
2948                 return 1;
2949         }
2950         DEBUG(10,("validate_gl: %s ok\n", keystr));
2951         return 0;
2952 }
2953
2954 static int validate_ug(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf)
2955 {
2956         struct cache_entry *centry = create_centry_validate(keystr, dbuf);
2957         int32 num_groups, i;
2958
2959         if (!centry) {
2960                 return 1;
2961         }
2962
2963         num_groups = centry_uint32(centry);
2964
2965         for (i=0; i< num_groups; i++) {
2966                 DOM_SID sid;
2967                 centry_sid(centry, mem_ctx, &sid);
2968         }
2969
2970         centry_free(centry);
2971
2972         if (bad_cache_entry) {
2973                 return 1;
2974         }
2975         DEBUG(10,("validate_ug: %s ok\n", keystr));
2976         return 0;
2977 }
2978
2979 static int validate_ua(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf)
2980 {
2981         struct cache_entry *centry = create_centry_validate(keystr, dbuf);
2982         int32 num_aliases, i;
2983
2984         if (!centry) {
2985                 return 1;
2986         }
2987
2988         num_aliases = centry_uint32(centry);
2989
2990         for (i=0; i < num_aliases; i++) {
2991                 (void)centry_uint32(centry);
2992         }
2993
2994         centry_free(centry);
2995
2996         if (bad_cache_entry) {
2997                 return 1;
2998         }
2999         DEBUG(10,("validate_ua: %s ok\n", keystr));
3000         return 0;
3001 }
3002
3003 static int validate_gm(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf)
3004 {
3005         struct cache_entry *centry = create_centry_validate(keystr, dbuf);
3006         int32 num_names, i;
3007
3008         if (!centry) {
3009                 return 1;
3010         }
3011
3012         num_names = centry_uint32(centry);
3013
3014         for (i=0; i< num_names; i++) {
3015                 DOM_SID sid;
3016                 centry_sid(centry, mem_ctx, &sid);
3017                 (void)centry_string(centry, mem_ctx);
3018                 (void)centry_uint32(centry);
3019         }
3020
3021         centry_free(centry);
3022
3023         if (bad_cache_entry) {
3024                 return 1;
3025         }
3026         DEBUG(10,("validate_gm: %s ok\n", keystr));
3027         return 0;
3028 }
3029
3030 static int validate_dr(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf)
3031 {
3032         /* Can't say anything about this other than must be nonzero. */
3033         if (dbuf.dsize == 0) {
3034                 DEBUG(0,("validate_dr: Corrupt cache for key %s (len == 0) ?\n",
3035                                 keystr));
3036                 bad_cache_entry = True;
3037                 return 1;
3038         }
3039
3040         DEBUG(10,("validate_dr: %s ok\n", keystr));
3041         return 0;
3042 }
3043
3044 static int validate_de(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf)
3045 {
3046         /* Can't say anything about this other than must be nonzero. */
3047         if (dbuf.dsize == 0) {
3048                 DEBUG(0,("validate_de: Corrupt cache for key %s (len == 0) ?\n",
3049                                 keystr));
3050                 bad_cache_entry = True;
3051                 return 1;
3052         }
3053
3054         DEBUG(10,("validate_de: %s ok\n", keystr));
3055         return 0;
3056 }
3057
3058 static int validate_trustdoms(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf)
3059 {
3060         struct cache_entry *centry = create_centry_validate(keystr, dbuf);
3061         int32 num_domains, i;
3062
3063         if (!centry) {
3064                 return 1;
3065         }
3066
3067         num_domains = centry_uint32(centry);
3068         
3069         for (i=0; i< num_domains; i++) {
3070                 DOM_SID sid;
3071                 (void)centry_string(centry, mem_ctx);
3072                 (void)centry_string(centry, mem_ctx);
3073                 (void)centry_sid(centry, mem_ctx, &sid);
3074         }
3075
3076         centry_free(centry);
3077
3078         if (bad_cache_entry) {
3079                 return 1;
3080         }
3081         DEBUG(10,("validate_trustdoms: %s ok\n", keystr));
3082         return 0;
3083 }
3084
3085 static int validate_offline(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf)
3086 {
3087         if (dbuf.dsize != 4) {
3088                 DEBUG(0,("validate_offline: Corrupt cache for key %s (len %u != 4) ?\n",
3089                                 keystr, (unsigned int)dbuf.dsize ));
3090                 bad_cache_entry = True;
3091                 return 1;
3092         }
3093         DEBUG(10,("validate_offline: %s ok\n", keystr));
3094         return 0;
3095 }
3096
3097 /***********************************************************************
3098  A list of all possible cache tdb keys with associated validation
3099  functions.
3100 ***********************************************************************/
3101
3102 struct key_val_struct {
3103         const char *keyname;
3104         int (*validate_data_fn)(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf);
3105 } key_val[] = {
3106         {"SEQNUM/", validate_seqnum},
3107         {"NS/", validate_ns},
3108         {"SN/", validate_sn},
3109         {"U/", validate_u},
3110         {"LOC_POL/", validate_loc_pol},
3111         {"PWD_POL/", validate_pwd_pol},
3112         {"CRED/", validate_cred},
3113         {"UL/", validate_ul},
3114         {"GL/", validate_gl},
3115         {"UG/", validate_ug},
3116         {"UA", validate_ua},
3117         {"GM/", validate_gm},
3118         {"DR/", validate_dr},
3119         {"DE/", validate_de},
3120         {"TRUSTDOMS/", validate_trustdoms},
3121         {"WINBINDD_OFFLINE", validate_offline},
3122         {NULL, NULL}
3123 };
3124
3125 /***********************************************************************
3126  Function to look at every entry in the tdb and validate it as far as
3127  possible.
3128 ***********************************************************************/
3129
3130 static int cache_traverse_validate_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf, void *state)
3131 {
3132         int i;
3133
3134         /* Paranoia check. */
3135         if (kbuf.dsize > 1024) {
3136                 DEBUG(0,("cache_traverse_validate_fn: key length too large (%u) > 1024\n\n",
3137                                 (unsigned int)kbuf.dsize ));
3138                 return 1;
3139         }
3140
3141         for (i = 0; key_val[i].keyname; i++) {
3142                 size_t namelen = strlen(key_val[i].keyname);
3143                 if (kbuf.dsize >= namelen && (
3144                                 strncmp(key_val[i].keyname, (const char *)kbuf.dptr, namelen)) == 0) {
3145                         TALLOC_CTX *mem_ctx;
3146                         char *keystr;
3147                         int ret;
3148
3149                         keystr = SMB_MALLOC(kbuf.dsize+1);
3150                         if (!keystr) {
3151                                 return 1;
3152                         }
3153                         memcpy(keystr, kbuf.dptr, kbuf.dsize);
3154                         keystr[kbuf.dsize] = '\0';
3155
3156                         mem_ctx = talloc_init("validate_ctx");
3157                         if (!mem_ctx) {
3158                                 SAFE_FREE(keystr);
3159                                 return 1;
3160                         }
3161
3162                         ret = key_val[i].validate_data_fn(mem_ctx, keystr, dbuf);
3163
3164                         SAFE_FREE(keystr);
3165                         talloc_destroy(mem_ctx);
3166                         return ret;
3167                 }
3168         }
3169
3170         DEBUG(0,("cache_traverse_validate_fn: unknown cache entry\nkey :\n"));
3171         dump_data(0, (uint8 *)kbuf.dptr, kbuf.dsize);
3172         DEBUG(0,("data :\n"));
3173         dump_data(0, (uint8 *)dbuf.dptr, dbuf.dsize);
3174         return 1; /* terminate. */
3175 }
3176
3177 static void validate_panic(const char *const why)
3178 {
3179         DEBUG(0,("validating cache: would panic %s\n", why ));
3180         bad_cache_entry = True;
3181 }
3182
3183 /* Handle any signals generated when validating a possibly
3184    bad cache tdb. */
3185
3186 static jmp_buf jmpbuf;
3187
3188 #ifdef SIGSEGV
3189 static void sig_segv(int sig)
3190 {
3191         longjmp(jmpbuf, SIGSEGV);
3192 }
3193 #endif
3194
3195 #ifdef SIGBUS
3196 static void sig_bus(int sig)
3197 {
3198         longjmp(jmpbuf, SIGBUS);
3199 }
3200 #endif
3201
3202 #ifdef SIGABRT
3203 static void sig_abrt(int sig)
3204 {
3205         longjmp(jmpbuf, SIGABRT);
3206 }
3207 #endif
3208
3209 /***********************************************************************
3210  Try and validate every entry in the winbindd cache. If we fail here,
3211  delete the cache tdb and return non-zero - the caller (main winbindd
3212  function) will restart us as we don't know if we crashed or not.
3213 ***********************************************************************/
3214
3215 int winbindd_validate_cache(void)
3216 {
3217         BOOL ret = -1;
3218         int fd = -1;
3219         int num_entries = 0;
3220         TDB_CONTEXT *tdb = NULL;
3221         const char *cache_path = lock_path("winbindd_cache.tdb");
3222
3223 #ifdef SIGSEGV
3224         void (*old_segv_handler)(int) = CatchSignal(SIGSEGV,SIGNAL_CAST sig_segv);
3225 #endif
3226 #ifdef SIGBUS
3227         void (*old_bus_handler)(int) = CatchSignal(SIGBUS,SIGNAL_CAST sig_bus);
3228 #endif
3229 #ifdef SIGABRT
3230         void (*old_abrt_handler)(int) = CatchSignal(SIGABRT,SIGNAL_CAST sig_abrt);
3231 #endif
3232
3233         switch((ret = setjmp(jmpbuf))) {
3234                 case 0:
3235                         ret = -1;
3236                         break;
3237                 case SIGSEGV:
3238                 case SIGBUS:
3239                 case SIGABRT:
3240                 default:
3241                         goto out;
3242         }
3243
3244         /* Doh ! Volker is very smart :-). Use TDB_NOMMAP to prevent
3245          * any wild pointer references when reading a corrupt tdb file. */
3246
3247         tdb = tdb_open_log(cache_path,
3248                         WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
3249                         lp_winbind_offline_logon() ? TDB_NOMMAP : (TDB_NOMMAP | TDB_CLEAR_IF_FIRST),
3250                         O_RDWR|O_CREAT, 0600);
3251         if (!tdb) {
3252                 goto out;
3253         }
3254
3255         fd = tdb_fd(tdb);
3256
3257         /* Check the cache freelist is good. */
3258         if (tdb_validate_freelist(tdb, &num_entries) == -1) {
3259                 DEBUG(0,("winbindd_validate_cache: bad freelist in cache %s\n",
3260                         cache_path));
3261                 goto out;
3262         }
3263
3264         DEBUG(10,("winbindd_validate_cache: cache %s freelist has %d entries\n",
3265                 cache_path, num_entries));
3266
3267         smb_panic_fn = validate_panic;
3268
3269         /* Now traverse the cache to validate it. */
3270         num_entries = tdb_traverse(tdb, cache_traverse_validate_fn, NULL);
3271         if (num_entries == -1 || bad_cache_entry) {
3272                 DEBUG(0,("winbindd_validate_cache: cache %s traverse failed\n",
3273                         cache_path));
3274                 goto out;
3275         }
3276
3277         DEBUG(10,("winbindd_validate_cache: cache %s is good "
3278                 "with %d entries\n", cache_path, num_entries));
3279         ret = 0; /* Cache is good. */
3280
3281   out:
3282
3283         bad_cache_entry = False;
3284         smb_panic_fn = smb_panic;
3285
3286         /* Ensure if we segv on exit we use the original
3287            handlers to avoid a loop. */
3288
3289 #ifdef SIGSEGV
3290         CatchSignal(SIGSEGV,SIGNAL_CAST old_segv_handler);
3291 #endif
3292 #ifdef SIGBUS
3293         CatchSignal(SIGBUS,SIGNAL_CAST old_bus_handler);
3294 #endif
3295 #ifdef SIGABRT
3296         CatchSignal(SIGABRT,SIGNAL_CAST old_abrt_handler);
3297 #endif
3298
3299         if (tdb) {
3300                 if (ret == 0) {
3301                         tdb_close(tdb);
3302                 } else if (fd != -1) {
3303                         close(fd);
3304                 }
3305         }
3306
3307         if (ret) {
3308                 unlink(cache_path);
3309         }
3310
3311         return ret;
3312 }
3313
3314 /* the cache backend methods are exposed via this structure */
3315 struct winbindd_methods cache_methods = {
3316         True,
3317         query_user_list,
3318         enum_dom_groups,
3319         enum_local_groups,
3320         name_to_sid,
3321         sid_to_name,
3322         rids_to_names,
3323         query_user,
3324         lookup_usergroups,
3325         lookup_useraliases,
3326         lookup_groupmem,
3327         sequence_number,
3328         lockout_policy,
3329         password_policy,
3330         trusted_domains
3331 };