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