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