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