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