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