9ffb3289df08020ce3e08024373ecb9c0f80b629
[nivanova/samba-autobuild/.git] / source3 / winbindd / winbindd_cache.c
1 /* 
2    Unix SMB/CIFS implementation.
3
4    Winbind cache backend functions
5
6    Copyright (C) Andrew Tridgell 2001
7    Copyright (C) Gerald Carter   2003-2007
8    Copyright (C) Volker Lendecke 2005
9    Copyright (C) Guenther Deschner 2005
10    Copyright (C) Michael Adam    2007
11
12    This program is free software; you can redistribute it and/or modify
13    it under the terms of the GNU General Public License as published by
14    the Free Software Foundation; either version 3 of the License, or
15    (at your option) any later version.
16
17    This program is distributed in the hope that it will be useful,
18    but WITHOUT ANY WARRANTY; without even the implied warranty of
19    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20    GNU General Public License for more details.
21
22    You should have received a copy of the GNU General Public License
23    along with this program.  If not, see <http://www.gnu.org/licenses/>.
24 */
25
26 #include "includes.h"
27 #include "system/filesys.h"
28 #include "winbindd.h"
29 #include "tdb_validate.h"
30 #include "../libcli/auth/libcli_auth.h"
31 #include "../librpc/gen_ndr/ndr_winbind.h"
32 #include "ads.h"
33 #include "nss_info.h"
34 #include "../libcli/security/security.h"
35 #include "passdb/machine_sid.h"
36 #include "util_tdb.h"
37 #include "libsmb/samlogon_cache.h"
38 #include "lib/namemap_cache.h"
39
40 #undef DBGC_CLASS
41 #define DBGC_CLASS DBGC_WINBIND
42
43 #define WINBINDD_CACHE_VER1 1 /* initial db version */
44 #define WINBINDD_CACHE_VER2 2 /* second version with timeouts for NDR entries */
45
46 #define WINBINDD_CACHE_VERSION WINBINDD_CACHE_VER2
47 #define WINBINDD_CACHE_VERSION_KEYSTR "WINBINDD_CACHE_VERSION"
48
49 extern struct winbindd_methods reconnect_methods;
50 #ifdef HAVE_ADS
51 extern struct winbindd_methods reconnect_ads_methods;
52 #endif
53 extern struct winbindd_methods builtin_passdb_methods;
54 extern struct winbindd_methods sam_passdb_methods;
55
56 static void wcache_flush_cache(void);
57
58 /*
59  * JRA. KEEP THIS LIST UP TO DATE IF YOU ADD CACHE ENTRIES.
60  * Here are the list of entry types that are *not* stored
61  * as form struct cache_entry in the cache.
62  */
63
64 static const char *non_centry_keys[] = {
65         "SEQNUM/",
66         "WINBINDD_OFFLINE",
67         WINBINDD_CACHE_VERSION_KEYSTR,
68         NULL
69 };
70
71 /************************************************************************
72  Is this key a non-centry type ?
73 ************************************************************************/
74
75 static bool is_non_centry_key(TDB_DATA kbuf)
76 {
77         int i;
78
79         if (kbuf.dptr == NULL || kbuf.dsize == 0) {
80                 return false;
81         }
82         for (i = 0; non_centry_keys[i] != NULL; i++) {
83                 size_t namelen = strlen(non_centry_keys[i]);
84                 if (kbuf.dsize < namelen) {
85                         continue;
86                 }
87                 if (strncmp(non_centry_keys[i], (const char *)kbuf.dptr, namelen) == 0) {
88                         return true;
89                 }
90         }
91         return false;
92 }
93
94 /* Global online/offline state - False when online. winbindd starts up online
95    and sets this to true if the first query fails and there's an entry in
96    the cache tdb telling us to stay offline. */
97
98 static bool global_winbindd_offline_state;
99
100 struct winbind_cache {
101         TDB_CONTEXT *tdb;
102 };
103
104 struct cache_entry {
105         NTSTATUS status;
106         uint32_t sequence_number;
107         uint64_t timeout;
108         uint8_t *data;
109         uint32_t len, ofs;
110 };
111
112 void (*smb_panic_fn)(const char *const why) = smb_panic;
113
114 static struct winbind_cache *wcache;
115
116 static char *wcache_path(void)
117 {
118         /*
119          * Data needs to be kept persistent in state directory for
120          * running with "winbindd offline logon".
121          */
122         return state_path("winbindd_cache.tdb");
123 }
124
125 static void winbindd_domain_init_backend(struct winbindd_domain *domain)
126 {
127         if (domain->backend != NULL) {
128                 return;
129         }
130
131         if (domain->internal) {
132                 domain->backend = &builtin_passdb_methods;
133         }
134
135         if (dom_sid_equal(&domain->sid, &global_sid_Builtin)) {
136                 domain->initialized = true;
137         }
138
139         if (strequal(domain->name, get_global_sam_name()) &&
140             sid_check_is_our_sam(&domain->sid))
141         {
142                 domain->backend = &sam_passdb_methods;
143         }
144
145         if (!domain->initialized) {
146                 /* We do not need a connection to an RW DC for cache operation */
147                 init_dc_connection(domain, false);
148         }
149
150 #ifdef HAVE_ADS
151         if (domain->backend == NULL) {
152                 struct winbindd_domain *our_domain = domain;
153
154                 /* find our domain first so we can figure out if we
155                    are joined to a kerberized domain */
156
157                 if (!domain->primary) {
158                         our_domain = find_our_domain();
159                 }
160
161                 if ((our_domain->active_directory || IS_DC)
162                     && domain->active_directory
163                     && !lp_winbind_rpc_only())
164                 {
165                         DBG_INFO("Setting ADS methods for domain %s\n",
166                                  domain->name);
167                         domain->backend = &reconnect_ads_methods;
168                 }
169         }
170 #endif  /* HAVE_ADS */
171
172         if (domain->backend == NULL) {
173                 DBG_INFO("Setting MS-RPC methods for domain %s\n", domain->name);
174                 domain->backend = &reconnect_methods;
175         }
176 }
177
178 /* get the winbind_cache structure */
179 static struct winbind_cache *get_cache(struct winbindd_domain *domain)
180 {
181         struct winbind_cache *ret = wcache;
182
183         winbindd_domain_init_backend(domain);
184
185         if (ret != NULL) {
186                 return ret;
187         }
188
189         ret = SMB_XMALLOC_P(struct winbind_cache);
190         ZERO_STRUCTP(ret);
191
192         wcache = ret;
193         wcache_flush_cache();
194
195         return ret;
196 }
197
198 /*
199   free a centry structure
200 */
201 static void centry_free(struct cache_entry *centry)
202 {
203         if (!centry)
204                 return;
205         SAFE_FREE(centry->data);
206         free(centry);
207 }
208
209 static bool centry_check_bytes(struct cache_entry *centry, size_t nbytes)
210 {
211         if (centry->len - centry->ofs < nbytes) {
212                 DEBUG(0,("centry corruption? needed %u bytes, have %d\n", 
213                          (unsigned int)nbytes,
214                          centry->len - centry->ofs));
215                 return false;
216         }
217         return true;
218 }
219
220 /*
221   pull a uint64_t from a cache entry
222 */
223 static uint64_t centry_uint64_t(struct cache_entry *centry)
224 {
225         uint64_t ret;
226
227         if (!centry_check_bytes(centry, 8)) {
228                 smb_panic_fn("centry_uint64_t");
229         }
230         ret = BVAL(centry->data, centry->ofs);
231         centry->ofs += 8;
232         return ret;
233 }
234
235 /*
236   pull a uint32_t from a cache entry
237 */
238 static uint32_t centry_uint32(struct cache_entry *centry)
239 {
240         uint32_t 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_t from a cache entry
252 */
253 static uint16_t centry_uint16(struct cache_entry *centry)
254 {
255         uint16_t ret;
256         if (!centry_check_bytes(centry, 2)) {
257                 smb_panic_fn("centry_uint16");
258         }
259         ret = SVAL(centry->data, centry->ofs);
260         centry->ofs += 2;
261         return ret;
262 }
263
264 /*
265   pull a uint8_t from a cache entry
266 */
267 static uint8_t centry_uint8(struct cache_entry *centry)
268 {
269         uint8_t 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_t 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_t 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 struct wcache_seqnum_state {
407         uint32_t *seqnum;
408         uint32_t *last_seq_check;
409 };
410
411 static int wcache_seqnum_parser(TDB_DATA key, TDB_DATA data,
412                                 void *private_data)
413 {
414         struct wcache_seqnum_state *state = private_data;
415
416         if (data.dsize != 8) {
417                 DEBUG(10, ("wcache_fetch_seqnum: invalid data size %d\n",
418                            (int)data.dsize));
419                 return -1;
420         }
421
422         *state->seqnum = IVAL(data.dptr, 0);
423         *state->last_seq_check = IVAL(data.dptr, 4);
424         return 0;
425 }
426
427 static bool wcache_fetch_seqnum(const char *domain_name, uint32_t *seqnum,
428                                 uint32_t *last_seq_check)
429 {
430         struct wcache_seqnum_state state = {
431                 .seqnum = seqnum, .last_seq_check = last_seq_check
432         };
433         size_t len = strlen(domain_name);
434         char keystr[len+8];
435         TDB_DATA key = { .dptr = (uint8_t *)keystr, .dsize = sizeof(keystr) };
436         int ret;
437
438         if (wcache->tdb == NULL) {
439                 DEBUG(10,("wcache_fetch_seqnum: tdb == NULL\n"));
440                 return false;
441         }
442
443         snprintf(keystr, sizeof(keystr),  "SEQNUM/%s", domain_name);
444
445         ret = tdb_parse_record(wcache->tdb, key, wcache_seqnum_parser,
446                                &state);
447         return (ret == 0);
448 }
449
450 static NTSTATUS fetch_cache_seqnum( struct winbindd_domain *domain, time_t now )
451 {
452         uint32_t last_check, time_diff;
453
454         if (!wcache_fetch_seqnum(domain->name, &domain->sequence_number,
455                                  &last_check)) {
456                 return NT_STATUS_UNSUCCESSFUL;
457         }
458         domain->last_seq_check = last_check;
459
460         /* have we expired? */
461
462         time_diff = now - domain->last_seq_check;
463         if ((int)time_diff > lp_winbind_cache_time()) {
464                 DEBUG(10,("fetch_cache_seqnum: timeout [%s][%u @ %u]\n",
465                         domain->name, domain->sequence_number,
466                         (uint32_t)domain->last_seq_check));
467                 return NT_STATUS_UNSUCCESSFUL;
468         }
469
470         DEBUG(10,("fetch_cache_seqnum: success [%s][%u @ %u]\n", 
471                 domain->name, domain->sequence_number, 
472                 (uint32_t)domain->last_seq_check));
473
474         return NT_STATUS_OK;
475 }
476
477 bool wcache_store_seqnum(const char *domain_name, uint32_t seqnum,
478                          time_t last_seq_check)
479 {
480         size_t len = strlen(domain_name);
481         char keystr[len+8];
482         TDB_DATA key = { .dptr = (uint8_t *)keystr, .dsize = sizeof(keystr) };
483         uint8_t buf[8];
484         int ret;
485
486         if (wcache->tdb == NULL) {
487                 DEBUG(10, ("wcache_store_seqnum: wcache->tdb == NULL\n"));
488                 return false;
489         }
490
491         snprintf(keystr, sizeof(keystr),  "SEQNUM/%s", domain_name);
492
493         SIVAL(buf, 0, seqnum);
494         SIVAL(buf, 4, last_seq_check);
495
496         ret = tdb_store(wcache->tdb, key, make_tdb_data(buf, sizeof(buf)),
497                         TDB_REPLACE);
498         if (ret != 0) {
499                 DEBUG(10, ("tdb_store_bystring failed: %s\n",
500                            tdb_errorstr(wcache->tdb)));
501                 return false;
502         }
503
504         DEBUG(10, ("wcache_store_seqnum: success [%s][%u @ %u]\n",
505                    domain_name, seqnum, (unsigned)last_seq_check));
506
507         return true;
508 }
509
510 static bool store_cache_seqnum( struct winbindd_domain *domain )
511 {
512         return wcache_store_seqnum(domain->name, domain->sequence_number,
513                                    domain->last_seq_check);
514 }
515
516 /*
517   refresh the domain sequence number on timeout.
518 */
519
520 static void refresh_sequence_number(struct winbindd_domain *domain)
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 ((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 or it did not timeout then it is OK */
628         if (wcache_server_down(domain)
629             || ((centry->sequence_number == domain->sequence_number)
630                 && ((time_t)centry->timeout > time(NULL)))) {
631                 DEBUG(10,("centry_expired: Key %s for domain %s is good.\n",
632                         keystr, domain->name ));
633                 return false;
634         }
635
636         DEBUG(10,("centry_expired: Key %s for domain %s expired\n",
637                 keystr, domain->name ));
638
639         /* it's expired */
640         return true;
641 }
642
643 static struct cache_entry *wcache_fetch_raw(char *kstr)
644 {
645         TDB_DATA data;
646         struct cache_entry *centry;
647         TDB_DATA key;
648
649         key = string_tdb_data(kstr);
650         data = tdb_fetch(wcache->tdb, key);
651         if (!data.dptr) {
652                 /* a cache miss */
653                 return NULL;
654         }
655
656         centry = SMB_XMALLOC_P(struct cache_entry);
657         centry->data = (unsigned char *)data.dptr;
658         centry->len = data.dsize;
659         centry->ofs = 0;
660
661         if (centry->len < 16) {
662                 /* huh? corrupt cache? */
663                 DEBUG(10,("wcache_fetch_raw: Corrupt cache for key %s "
664                           "(len < 16)?\n", kstr));
665                 centry_free(centry);
666                 return NULL;
667         }
668
669         centry->status = centry_ntstatus(centry);
670         centry->sequence_number = centry_uint32(centry);
671         centry->timeout = centry_uint64_t(centry);
672
673         return centry;
674 }
675
676 static bool is_my_own_sam_domain(struct winbindd_domain *domain)
677 {
678         if (strequal(domain->name, get_global_sam_name()) &&
679             sid_check_is_our_sam(&domain->sid)) {
680                 return true;
681         }
682
683         return false;
684 }
685
686 static bool is_builtin_domain(struct winbindd_domain *domain)
687 {
688         if (strequal(domain->name, "BUILTIN") &&
689             sid_check_is_builtin(&domain->sid)) {
690                 return true;
691         }
692
693         return false;
694 }
695
696 /*
697   fetch an entry from the cache, with a varargs key. auto-fetch the sequence
698   number and return status
699 */
700 static struct cache_entry *wcache_fetch(struct winbind_cache *cache, 
701                                         struct winbindd_domain *domain,
702                                         const char *format, ...) PRINTF_ATTRIBUTE(3,4);
703 static struct cache_entry *wcache_fetch(struct winbind_cache *cache, 
704                                         struct winbindd_domain *domain,
705                                         const char *format, ...)
706 {
707         va_list ap;
708         char *kstr;
709         struct cache_entry *centry;
710
711         if (!winbindd_use_cache() ||
712             is_my_own_sam_domain(domain) ||
713             is_builtin_domain(domain)) {
714                 return NULL;
715         }
716
717         refresh_sequence_number(domain);
718
719         va_start(ap, format);
720         smb_xvasprintf(&kstr, format, ap);
721         va_end(ap);
722
723         centry = wcache_fetch_raw(kstr);
724         if (centry == NULL) {
725                 free(kstr);
726                 return NULL;
727         }
728
729         if (centry_expired(domain, kstr, centry)) {
730
731                 DEBUG(10,("wcache_fetch: entry %s expired for domain %s\n",
732                          kstr, domain->name ));
733
734                 centry_free(centry);
735                 free(kstr);
736                 return NULL;
737         }
738
739         DEBUG(10,("wcache_fetch: returning entry %s for domain %s\n",
740                  kstr, domain->name ));
741
742         free(kstr);
743         return centry;
744 }
745
746 static void wcache_delete(const char *format, ...) PRINTF_ATTRIBUTE(1,2);
747 static void wcache_delete(const char *format, ...)
748 {
749         va_list ap;
750         char *kstr;
751         TDB_DATA key;
752
753         va_start(ap, format);
754         smb_xvasprintf(&kstr, format, ap);
755         va_end(ap);
756
757         key = string_tdb_data(kstr);
758
759         tdb_delete(wcache->tdb, key);
760         free(kstr);
761 }
762
763 /*
764   make sure we have at least len bytes available in a centry 
765 */
766 static void centry_expand(struct cache_entry *centry, uint32_t len)
767 {
768         if (centry->len - centry->ofs >= len)
769                 return;
770         centry->len *= 2;
771         centry->data = SMB_REALLOC_ARRAY(centry->data, unsigned char,
772                                          centry->len);
773         if (!centry->data) {
774                 DEBUG(0,("out of memory: needed %d bytes in centry_expand\n", centry->len));
775                 smb_panic_fn("out of memory in centry_expand");
776         }
777 }
778
779 /*
780   push a uint64_t into a centry
781 */
782 static void centry_put_uint64_t(struct cache_entry *centry, uint64_t v)
783 {
784         centry_expand(centry, 8);
785         SBVAL(centry->data, centry->ofs, v);
786         centry->ofs += 8;
787 }
788
789 /*
790   push a uint32_t into a centry
791 */
792 static void centry_put_uint32(struct cache_entry *centry, uint32_t v)
793 {
794         centry_expand(centry, 4);
795         SIVAL(centry->data, centry->ofs, v);
796         centry->ofs += 4;
797 }
798
799 /*
800   push a uint16_t into a centry
801 */
802 static void centry_put_uint16(struct cache_entry *centry, uint16_t v)
803 {
804         centry_expand(centry, 2);
805         SSVAL(centry->data, centry->ofs, v);
806         centry->ofs += 2;
807 }
808
809 /*
810   push a uint8_t into a centry
811 */
812 static void centry_put_uint8(struct cache_entry *centry, uint8_t v)
813 {
814         centry_expand(centry, 1);
815         SCVAL(centry->data, centry->ofs, v);
816         centry->ofs += 1;
817 }
818
819 /* 
820    push a string into a centry 
821  */
822 static void centry_put_string(struct cache_entry *centry, const char *s)
823 {
824         int len;
825
826         if (!s) {
827                 /* null strings are marked as len 0xFFFF */
828                 centry_put_uint8(centry, 0xFF);
829                 return;
830         }
831
832         len = strlen(s);
833         /* can't handle more than 254 char strings. Truncating is probably best */
834         if (len > 254) {
835                 DEBUG(10,("centry_put_string: truncating len (%d) to: 254\n", len));
836                 len = 254;
837         }
838         centry_put_uint8(centry, len);
839         centry_expand(centry, len);
840         memcpy(centry->data + centry->ofs, s, len);
841         centry->ofs += len;
842 }
843
844 /* 
845    push a 16 byte hash into a centry - treat as 16 byte string.
846  */
847 static void centry_put_hash16(struct cache_entry *centry, const uint8_t val[16])
848 {
849         centry_put_uint8(centry, 16);
850         centry_expand(centry, 16);
851         memcpy(centry->data + centry->ofs, val, 16);
852         centry->ofs += 16;
853 }
854
855 static void centry_put_sid(struct cache_entry *centry, const struct dom_sid *sid)
856 {
857         fstring sid_string;
858         centry_put_string(centry, sid_to_fstring(sid_string, sid));
859 }
860
861
862 /*
863   put NTSTATUS into a centry
864 */
865 static void centry_put_ntstatus(struct cache_entry *centry, NTSTATUS status)
866 {
867         uint32_t status_value = NT_STATUS_V(status);
868         centry_put_uint32(centry, status_value);
869 }
870
871
872 /*
873   push a NTTIME into a centry 
874 */
875 static void centry_put_nttime(struct cache_entry *centry, NTTIME nt)
876 {
877         centry_expand(centry, 8);
878         SIVAL(centry->data, centry->ofs, nt & 0xFFFFFFFF);
879         centry->ofs += 4;
880         SIVAL(centry->data, centry->ofs, nt >> 32);
881         centry->ofs += 4;
882 }
883
884 /*
885   push a time_t into a centry - use a 64 bit size.
886   NTTIME here is being used as a convenient 64-bit size.
887 */
888 static void centry_put_time(struct cache_entry *centry, time_t t)
889 {
890         NTTIME nt = (NTTIME)t;
891         centry_put_nttime(centry, nt);
892 }
893
894 /*
895   start a centry for output. When finished, call centry_end()
896 */
897 static struct cache_entry *centry_start(struct winbindd_domain *domain,
898                                         NTSTATUS status)
899 {
900         struct cache_entry *centry;
901
902         if (!wcache->tdb)
903                 return NULL;
904
905         centry = SMB_XMALLOC_P(struct cache_entry);
906
907         centry->len = 8192; /* reasonable default */
908         centry->data = SMB_XMALLOC_ARRAY(uint8_t, centry->len);
909         centry->ofs = 0;
910         centry->sequence_number = domain->sequence_number;
911         centry->timeout = lp_winbind_cache_time() + time(NULL);
912         centry_put_ntstatus(centry, status);
913         centry_put_uint32(centry, centry->sequence_number);
914         centry_put_uint64_t(centry, centry->timeout);
915         return centry;
916 }
917
918 /*
919   finish a centry and write it to the tdb
920 */
921 static void centry_end(struct cache_entry *centry, const char *format, ...) PRINTF_ATTRIBUTE(2,3);
922 static void centry_end(struct cache_entry *centry, const char *format, ...)
923 {
924         va_list ap;
925         char *kstr;
926         TDB_DATA key, data;
927
928         if (!winbindd_use_cache()) {
929                 return;
930         }
931
932         va_start(ap, format);
933         smb_xvasprintf(&kstr, format, ap);
934         va_end(ap);
935
936         key = string_tdb_data(kstr);
937         data.dptr = centry->data;
938         data.dsize = centry->ofs;
939
940         tdb_store(wcache->tdb, key, data, TDB_REPLACE);
941         free(kstr);
942 }
943
944 static void wcache_save_name_to_sid(struct winbindd_domain *domain, 
945                                     NTSTATUS status, const char *domain_name,
946                                     const char *name, const struct dom_sid *sid,
947                                     enum lsa_SidType type)
948 {
949         bool ok;
950
951         ok = namemap_cache_set_name2sid(domain_name, name, sid, type,
952                                         time(NULL) + lp_winbind_cache_time());
953         if (!ok) {
954                 DBG_DEBUG("namemap_cache_set_name2sid failed\n");
955         }
956
957         /*
958          * Don't store the reverse mapping. The name came from user
959          * input, and we might not have the correct capitalization,
960          * which is important for nsswitch.
961          */
962 }
963
964 static void wcache_save_sid_to_name(struct winbindd_domain *domain, NTSTATUS status, 
965                                     const struct dom_sid *sid, const char *domain_name, const char *name, enum lsa_SidType type)
966 {
967         bool ok;
968
969         ok = namemap_cache_set_sid2name(sid, domain_name, name, type,
970                                         time(NULL) + lp_winbind_cache_time());
971         if (!ok) {
972                 DBG_DEBUG("namemap_cache_set_sid2name failed\n");
973         }
974
975         if (type != SID_NAME_UNKNOWN) {
976                 ok = namemap_cache_set_name2sid(
977                         domain_name, name, sid, type,
978                         time(NULL) + lp_winbind_cache_time());
979                 if (!ok) {
980                         DBG_DEBUG("namemap_cache_set_name2sid failed\n");
981                 }
982         }
983 }
984
985 static void wcache_save_lockout_policy(struct winbindd_domain *domain,
986                                        NTSTATUS status,
987                                        struct samr_DomInfo12 *lockout_policy)
988 {
989         struct cache_entry *centry;
990
991         centry = centry_start(domain, status);
992         if (!centry)
993                 return;
994
995         centry_put_nttime(centry, lockout_policy->lockout_duration);
996         centry_put_nttime(centry, lockout_policy->lockout_window);
997         centry_put_uint16(centry, lockout_policy->lockout_threshold);
998
999         centry_end(centry, "LOC_POL/%s", domain->name);
1000
1001         DEBUG(10,("wcache_save_lockout_policy: %s\n", domain->name));
1002
1003         centry_free(centry);
1004 }
1005
1006
1007
1008 static void wcache_save_password_policy(struct winbindd_domain *domain,
1009                                         NTSTATUS status,
1010                                         struct samr_DomInfo1 *policy)
1011 {
1012         struct cache_entry *centry;
1013
1014         centry = centry_start(domain, status);
1015         if (!centry)
1016                 return;
1017
1018         centry_put_uint16(centry, policy->min_password_length);
1019         centry_put_uint16(centry, policy->password_history_length);
1020         centry_put_uint32(centry, policy->password_properties);
1021         centry_put_nttime(centry, policy->max_password_age);
1022         centry_put_nttime(centry, policy->min_password_age);
1023
1024         centry_end(centry, "PWD_POL/%s", domain->name);
1025
1026         DEBUG(10,("wcache_save_password_policy: %s\n", domain->name));
1027
1028         centry_free(centry);
1029 }
1030
1031 /***************************************************************************
1032  ***************************************************************************/
1033
1034 static void wcache_save_username_alias(struct winbindd_domain *domain,
1035                                        NTSTATUS status,
1036                                        const char *name, const char *alias)
1037 {
1038         struct cache_entry *centry;
1039         fstring uname;
1040
1041         if ( (centry = centry_start(domain, status)) == NULL )
1042                 return;
1043
1044         centry_put_string( centry, alias );
1045
1046         fstrcpy(uname, name);
1047         (void)strupper_m(uname);
1048         centry_end(centry, "NSS/NA/%s", uname);
1049
1050         DEBUG(10,("wcache_save_username_alias: %s -> %s\n", name, alias ));
1051
1052         centry_free(centry);
1053 }
1054
1055 static void wcache_save_alias_username(struct winbindd_domain *domain,
1056                                        NTSTATUS status,
1057                                        const char *alias, const char *name)
1058 {
1059         struct cache_entry *centry;
1060         fstring uname;
1061
1062         if ( (centry = centry_start(domain, status)) == NULL )
1063                 return;
1064
1065         centry_put_string( centry, name );
1066
1067         fstrcpy(uname, alias);
1068         (void)strupper_m(uname);
1069         centry_end(centry, "NSS/AN/%s", uname);
1070
1071         DEBUG(10,("wcache_save_alias_username: %s -> %s\n", alias, name ));
1072
1073         centry_free(centry);
1074 }
1075
1076 /***************************************************************************
1077  ***************************************************************************/
1078
1079 NTSTATUS resolve_username_to_alias( TALLOC_CTX *mem_ctx,
1080                                     struct winbindd_domain *domain,
1081                                     const char *name, char **alias )
1082 {
1083         struct winbind_cache *cache = get_cache(domain);
1084         struct cache_entry *centry = NULL;
1085         NTSTATUS status;
1086         char *upper_name;
1087
1088         if ( domain->internal )
1089                 return NT_STATUS_NOT_SUPPORTED;
1090
1091         if (!cache->tdb)
1092                 goto do_query;
1093
1094         upper_name = talloc_strdup_upper(mem_ctx, name);
1095         if (upper_name == NULL) {
1096                 return NT_STATUS_NO_MEMORY;
1097         }
1098
1099         centry = wcache_fetch(cache, domain, "NSS/NA/%s", upper_name);
1100
1101         talloc_free(upper_name);
1102
1103         if (!centry)
1104                 goto do_query;
1105
1106         status = centry->status;
1107
1108         if (!NT_STATUS_IS_OK(status)) {
1109                 centry_free(centry);
1110                 return status;
1111         }
1112
1113         *alias = centry_string( centry, mem_ctx );
1114
1115         centry_free(centry);
1116
1117         DEBUG(10,("resolve_username_to_alias: [Cached] - mapped %s to %s\n",
1118                   name, *alias ? *alias : "(none)"));
1119
1120         return (*alias) ? NT_STATUS_OK : NT_STATUS_OBJECT_NAME_NOT_FOUND;
1121
1122 do_query:
1123
1124         /* If its not in cache and we are offline, then fail */
1125
1126         if ( get_global_winbindd_state_offline() || !domain->online ) {
1127                 DEBUG(8,("resolve_username_to_alias: rejecting query "
1128                          "in offline mode\n"));
1129                 return NT_STATUS_NOT_FOUND;
1130         }
1131
1132         status = nss_map_to_alias( mem_ctx, domain->name, name, alias );
1133
1134         if ( NT_STATUS_IS_OK( status ) ) {
1135                 wcache_save_username_alias(domain, status, name, *alias);
1136         }
1137
1138         if ( NT_STATUS_EQUAL( status, NT_STATUS_NONE_MAPPED ) ) {
1139                 wcache_save_username_alias(domain, status, name, "(NULL)");
1140         }
1141
1142         DEBUG(5,("resolve_username_to_alias: backend query returned %s\n",
1143                  nt_errstr(status)));
1144
1145         if ( NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ) {
1146                 set_domain_offline( domain );
1147         }
1148
1149         return status;
1150 }
1151
1152 /***************************************************************************
1153  ***************************************************************************/
1154
1155 NTSTATUS resolve_alias_to_username( TALLOC_CTX *mem_ctx,
1156                                     struct winbindd_domain *domain,
1157                                     const char *alias, char **name )
1158 {
1159         struct winbind_cache *cache = get_cache(domain);
1160         struct cache_entry *centry = NULL;
1161         NTSTATUS status;
1162         char *upper_name;
1163
1164         if ( domain->internal )
1165                 return  NT_STATUS_NOT_SUPPORTED;
1166
1167         if (!cache->tdb)
1168                 goto do_query;
1169
1170         upper_name = talloc_strdup(mem_ctx, alias);
1171         if (upper_name == NULL) {
1172                 return NT_STATUS_NO_MEMORY;
1173         }
1174         if (!strupper_m(upper_name)) {
1175                 talloc_free(upper_name);
1176                 return NT_STATUS_INVALID_PARAMETER;
1177         }
1178
1179         centry = wcache_fetch(cache, domain, "NSS/AN/%s", upper_name);
1180
1181         talloc_free(upper_name);
1182
1183         if (!centry)
1184                 goto do_query;
1185
1186         status = centry->status;
1187
1188         if (!NT_STATUS_IS_OK(status)) {
1189                 centry_free(centry);
1190                 return status;
1191         }
1192
1193         *name = centry_string( centry, mem_ctx );
1194
1195         centry_free(centry);
1196
1197         DEBUG(10,("resolve_alias_to_username: [Cached] - mapped %s to %s\n",
1198                   alias, *name ? *name : "(none)"));
1199
1200         return (*name) ? NT_STATUS_OK : NT_STATUS_OBJECT_NAME_NOT_FOUND;
1201
1202 do_query:
1203
1204         /* If its not in cache and we are offline, then fail */
1205
1206         if ( get_global_winbindd_state_offline() || !domain->online ) {
1207                 DEBUG(8,("resolve_alias_to_username: rejecting query "
1208                          "in offline mode\n"));
1209                 return NT_STATUS_NOT_FOUND;
1210         }
1211
1212         /* an alias cannot contain a domain prefix or '@' */
1213
1214         if (strchr(alias, '\\') || strchr(alias, '@')) {
1215                 DEBUG(10,("resolve_alias_to_username: skipping fully "
1216                           "qualified name %s\n", alias));
1217                 return NT_STATUS_OBJECT_NAME_INVALID;
1218         }
1219
1220         status = nss_map_from_alias( mem_ctx, domain->name, alias, name );
1221
1222         if ( NT_STATUS_IS_OK( status ) ) {
1223                 wcache_save_alias_username( domain, status, alias, *name );
1224         }
1225
1226         if (NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED)) {
1227                 wcache_save_alias_username(domain, status, alias, "(NULL)");
1228         }
1229
1230         DEBUG(5,("resolve_alias_to_username: backend query returned %s\n",
1231                  nt_errstr(status)));
1232
1233         if ( NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ) {
1234                 set_domain_offline( domain );
1235         }
1236
1237         return status;
1238 }
1239
1240 NTSTATUS wcache_cached_creds_exist(struct winbindd_domain *domain, const struct dom_sid *sid)
1241 {
1242         struct winbind_cache *cache = get_cache(domain);
1243         int ret;
1244         fstring key_str, tmp;
1245         uint32_t rid;
1246
1247         if (!cache->tdb) {
1248                 return NT_STATUS_INTERNAL_DB_ERROR;
1249         }
1250
1251         if (is_null_sid(sid)) {
1252                 return NT_STATUS_INVALID_SID;
1253         }
1254
1255         if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
1256                 return NT_STATUS_INVALID_SID;
1257         }
1258
1259         fstr_sprintf(key_str, "CRED/%s", sid_to_fstring(tmp, sid));
1260
1261         ret = tdb_exists(cache->tdb, string_tdb_data(key_str));
1262         if (ret != 1) {
1263                 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1264         }
1265
1266         return NT_STATUS_OK;
1267 }
1268
1269 /* Lookup creds for a SID - copes with old (unsalted) creds as well
1270    as new salted ones. */
1271
1272 NTSTATUS wcache_get_creds(struct winbindd_domain *domain, 
1273                           TALLOC_CTX *mem_ctx, 
1274                           const struct dom_sid *sid,
1275                           const uint8_t **cached_nt_pass,
1276                           const uint8_t **cached_salt)
1277 {
1278         struct winbind_cache *cache = get_cache(domain);
1279         struct cache_entry *centry = NULL;
1280         NTSTATUS status;
1281         uint32_t rid;
1282         fstring tmp;
1283
1284         if (!cache->tdb) {
1285                 return NT_STATUS_INTERNAL_DB_ERROR;
1286         }
1287
1288         if (is_null_sid(sid)) {
1289                 return NT_STATUS_INVALID_SID;
1290         }
1291
1292         if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
1293                 return NT_STATUS_INVALID_SID;
1294         }
1295
1296         /* Try and get a salted cred first. If we can't
1297            fall back to an unsalted cred. */
1298
1299         centry = wcache_fetch(cache, domain, "CRED/%s",
1300                               sid_to_fstring(tmp, sid));
1301         if (!centry) {
1302                 DEBUG(10,("wcache_get_creds: entry for [CRED/%s] not found\n", 
1303                           sid_string_dbg(sid)));
1304                 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1305         }
1306
1307         /*
1308          * We don't use the time element at this moment,
1309          * but we have to consume it, so that we don't
1310          * neet to change the disk format of the cache.
1311          */
1312         (void)centry_time(centry);
1313
1314         /* In the salted case this isn't actually the nt_hash itself,
1315            but the MD5 of the salt + nt_hash. Let the caller
1316            sort this out. It can tell as we only return the cached_salt
1317            if we are returning a salted cred. */
1318
1319         *cached_nt_pass = (const uint8_t *)centry_hash16(centry, mem_ctx);
1320         if (*cached_nt_pass == NULL) {
1321                 fstring sidstr;
1322
1323                 sid_to_fstring(sidstr, sid);
1324
1325                 /* Bad (old) cred cache. Delete and pretend we
1326                    don't have it. */
1327                 DEBUG(0,("wcache_get_creds: bad entry for [CRED/%s] - deleting\n", 
1328                                 sidstr));
1329                 wcache_delete("CRED/%s", sidstr);
1330                 centry_free(centry);
1331                 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1332         }
1333
1334         /* We only have 17 bytes more data in the salted cred case. */
1335         if (centry->len - centry->ofs == 17) {
1336                 *cached_salt = (const uint8_t *)centry_hash16(centry, mem_ctx);
1337         } else {
1338                 *cached_salt = NULL;
1339         }
1340
1341         dump_data_pw("cached_nt_pass", *cached_nt_pass, NT_HASH_LEN);
1342         if (*cached_salt) {
1343                 dump_data_pw("cached_salt", *cached_salt, NT_HASH_LEN);
1344         }
1345
1346         status = centry->status;
1347
1348         DEBUG(10,("wcache_get_creds: [Cached] - cached creds for user %s status: %s\n",
1349                   sid_string_dbg(sid), nt_errstr(status) ));
1350
1351         centry_free(centry);
1352         return status;
1353 }
1354
1355 /* Store creds for a SID - only writes out new salted ones. */
1356
1357 NTSTATUS wcache_save_creds(struct winbindd_domain *domain, 
1358                            const struct dom_sid *sid,
1359                            const uint8_t nt_pass[NT_HASH_LEN])
1360 {
1361         struct cache_entry *centry;
1362         fstring sid_string;
1363         uint32_t rid;
1364         uint8_t cred_salt[NT_HASH_LEN];
1365         uint8_t salted_hash[NT_HASH_LEN];
1366
1367         if (is_null_sid(sid)) {
1368                 return NT_STATUS_INVALID_SID;
1369         }
1370
1371         if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
1372                 return NT_STATUS_INVALID_SID;
1373         }
1374
1375         centry = centry_start(domain, NT_STATUS_OK);
1376         if (!centry) {
1377                 return NT_STATUS_INTERNAL_DB_ERROR;
1378         }
1379
1380         dump_data_pw("nt_pass", nt_pass, NT_HASH_LEN);
1381
1382         centry_put_time(centry, time(NULL));
1383
1384         /* Create a salt and then salt the hash. */
1385         generate_random_buffer(cred_salt, NT_HASH_LEN);
1386         E_md5hash(cred_salt, nt_pass, salted_hash);
1387
1388         centry_put_hash16(centry, salted_hash);
1389         centry_put_hash16(centry, cred_salt);
1390         centry_end(centry, "CRED/%s", sid_to_fstring(sid_string, sid));
1391
1392         DEBUG(10,("wcache_save_creds: %s\n", sid_string));
1393
1394         centry_free(centry);
1395
1396         return NT_STATUS_OK;
1397 }
1398
1399
1400 /* Query display info. This is the basic user list fn */
1401 NTSTATUS wb_cache_query_user_list(struct winbindd_domain *domain,
1402                                   TALLOC_CTX *mem_ctx,
1403                                   uint32_t **prids)
1404 {
1405         struct winbind_cache *cache = get_cache(domain);
1406         struct cache_entry *centry = NULL;
1407         uint32_t num_rids = 0;
1408         uint32_t *rids = NULL;
1409         NTSTATUS status;
1410         unsigned int i, retry;
1411         bool old_status = domain->online;
1412
1413         *prids = NULL;
1414
1415         if (!cache->tdb)
1416                 goto do_query;
1417
1418         centry = wcache_fetch(cache, domain, "UL/%s", domain->name);
1419         if (!centry)
1420                 goto do_query;
1421
1422 do_fetch_cache:
1423         num_rids = centry_uint32(centry);
1424
1425         if (num_rids == 0) {
1426                 goto do_cached;
1427         }
1428
1429         rids = talloc_array(mem_ctx, uint32_t, num_rids);
1430         if (rids == NULL) {
1431                 centry_free(centry);
1432                 return NT_STATUS_NO_MEMORY;
1433         }
1434
1435         for (i=0; i<num_rids; i++) {
1436                 rids[i] = centry_uint32(centry);
1437         }
1438
1439 do_cached:      
1440         status = centry->status;
1441
1442         DEBUG(10,("query_user_list: [Cached] - cached list for domain %s status: %s\n",
1443                 domain->name, nt_errstr(status) ));
1444
1445         centry_free(centry);
1446         return status;
1447
1448 do_query:
1449
1450         /* Return status value returned by seq number check */
1451
1452         if (!NT_STATUS_IS_OK(domain->last_status))
1453                 return domain->last_status;
1454
1455         /* Put the query_user_list() in a retry loop.  There appears to be
1456          * some bug either with Windows 2000 or Samba's handling of large
1457          * rpc replies.  This manifests itself as sudden disconnection
1458          * at a random point in the enumeration of a large (60k) user list.
1459          * The retry loop simply tries the operation again. )-:  It's not
1460          * pretty but an acceptable workaround until we work out what the
1461          * real problem is. */
1462
1463         retry = 0;
1464         do {
1465
1466                 DEBUG(10,("query_user_list: [Cached] - doing backend query for list for domain %s\n",
1467                         domain->name ));
1468
1469                 rids = NULL;
1470                 status = domain->backend->query_user_list(domain, mem_ctx,
1471                                                           &rids);
1472                 num_rids = talloc_array_length(rids);
1473
1474                 if (!NT_STATUS_IS_OK(status)) {
1475                         DEBUG(3, ("query_user_list: returned 0x%08x, "
1476                                   "retrying\n", NT_STATUS_V(status)));
1477                 }
1478                 if (NT_STATUS_EQUAL(status, NT_STATUS_UNSUCCESSFUL)) {
1479                         DEBUG(3, ("query_user_list: flushing "
1480                                   "connection cache\n"));
1481                         invalidate_cm_connection(domain);
1482                 }
1483                 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1484                     NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1485                         if (!domain->internal && old_status) {
1486                                 set_domain_offline(domain);
1487                         }
1488                         /* store partial response. */
1489                         if (num_rids > 0) {
1490                                 /*
1491                                  * humm, what about the status used for cache?
1492                                  * Should it be NT_STATUS_OK?
1493                                  */
1494                                 break;
1495                         }
1496                         /*
1497                          * domain is offline now, and there is no user entries,
1498                          * try to fetch from cache again.
1499                          */
1500                         if (cache->tdb && !domain->online && !domain->internal && old_status) {
1501                                 centry = wcache_fetch(cache, domain, "UL/%s", domain->name);
1502                                 /* partial response... */
1503                                 if (!centry) {
1504                                         goto skip_save;
1505                                 } else {
1506                                         goto do_fetch_cache;
1507                                 }
1508                         } else {
1509                                 goto skip_save;
1510                         }
1511                 }
1512
1513         } while (NT_STATUS_V(status) == NT_STATUS_V(NT_STATUS_UNSUCCESSFUL) && 
1514                  (retry++ < 5));
1515
1516         /* and save it */
1517         refresh_sequence_number(domain);
1518         if (!NT_STATUS_IS_OK(status)) {
1519                 return status;
1520         }
1521         centry = centry_start(domain, status);
1522         if (!centry)
1523                 goto skip_save;
1524         centry_put_uint32(centry, num_rids);
1525         for (i=0; i<num_rids; i++) {
1526                 centry_put_uint32(centry, rids[i]);
1527         }       
1528         centry_end(centry, "UL/%s", domain->name);
1529         centry_free(centry);
1530
1531         *prids = rids;
1532
1533 skip_save:
1534         return status;
1535 }
1536
1537 /* list all domain groups */
1538 NTSTATUS wb_cache_enum_dom_groups(struct winbindd_domain *domain,
1539                                   TALLOC_CTX *mem_ctx,
1540                                   uint32_t *num_entries,
1541                                   struct wb_acct_info **info)
1542 {
1543         struct winbind_cache *cache = get_cache(domain);
1544         struct cache_entry *centry = NULL;
1545         NTSTATUS status;
1546         unsigned int i;
1547         bool old_status;
1548
1549         old_status = domain->online;
1550         if (!cache->tdb)
1551                 goto do_query;
1552
1553         centry = wcache_fetch(cache, domain, "GL/%s/domain", domain->name);
1554         if (!centry)
1555                 goto do_query;
1556
1557 do_fetch_cache:
1558         *num_entries = centry_uint32(centry);
1559
1560         if (*num_entries == 0)
1561                 goto do_cached;
1562
1563         (*info) = talloc_array(mem_ctx, struct wb_acct_info, *num_entries);
1564         if (! (*info)) {
1565                 smb_panic_fn("enum_dom_groups out of memory");
1566         }
1567         for (i=0; i<(*num_entries); i++) {
1568                 fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx));
1569                 fstrcpy((*info)[i].acct_desc, centry_string(centry, mem_ctx));
1570                 (*info)[i].rid = centry_uint32(centry);
1571         }
1572
1573 do_cached:      
1574         status = centry->status;
1575
1576         DEBUG(10,("enum_dom_groups: [Cached] - cached list for domain %s status: %s\n",
1577                 domain->name, nt_errstr(status) ));
1578
1579         centry_free(centry);
1580         return status;
1581
1582 do_query:
1583         *num_entries = 0;
1584         *info = NULL;
1585
1586         /* Return status value returned by seq number check */
1587
1588         if (!NT_STATUS_IS_OK(domain->last_status))
1589                 return domain->last_status;
1590
1591         DEBUG(10,("enum_dom_groups: [Cached] - doing backend query for list for domain %s\n",
1592                 domain->name ));
1593
1594         status = domain->backend->enum_dom_groups(domain, mem_ctx, num_entries, info);
1595
1596         if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1597             NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1598                 if (!domain->internal && old_status) {
1599                         set_domain_offline(domain);
1600                 }
1601                 if (cache->tdb &&
1602                         !domain->online &&
1603                         !domain->internal &&
1604                         old_status) {
1605                         centry = wcache_fetch(cache, domain, "GL/%s/domain", domain->name);
1606                         if (centry) {
1607                                 goto do_fetch_cache;
1608                         }
1609                 }
1610         }
1611         /* and save it */
1612         refresh_sequence_number(domain);
1613         if (!NT_STATUS_IS_OK(status)) {
1614                 return status;
1615         }
1616         centry = centry_start(domain, status);
1617         if (!centry)
1618                 goto skip_save;
1619         centry_put_uint32(centry, *num_entries);
1620         for (i=0; i<(*num_entries); i++) {
1621                 centry_put_string(centry, (*info)[i].acct_name);
1622                 centry_put_string(centry, (*info)[i].acct_desc);
1623                 centry_put_uint32(centry, (*info)[i].rid);
1624         }       
1625         centry_end(centry, "GL/%s/domain", domain->name);
1626         centry_free(centry);
1627
1628 skip_save:
1629         return status;
1630 }
1631
1632 /* list all domain groups */
1633 NTSTATUS wb_cache_enum_local_groups(struct winbindd_domain *domain,
1634                                     TALLOC_CTX *mem_ctx,
1635                                     uint32_t *num_entries,
1636                                     struct wb_acct_info **info)
1637 {
1638         struct winbind_cache *cache = get_cache(domain);
1639         struct cache_entry *centry = NULL;
1640         NTSTATUS status;
1641         unsigned int i;
1642         bool old_status;
1643
1644         old_status = domain->online;
1645         if (!cache->tdb)
1646                 goto do_query;
1647
1648         centry = wcache_fetch(cache, domain, "GL/%s/local", domain->name);
1649         if (!centry)
1650                 goto do_query;
1651
1652 do_fetch_cache:
1653         *num_entries = centry_uint32(centry);
1654
1655         if (*num_entries == 0)
1656                 goto do_cached;
1657
1658         (*info) = talloc_array(mem_ctx, struct wb_acct_info, *num_entries);
1659         if (! (*info)) {
1660                 smb_panic_fn("enum_dom_groups out of memory");
1661         }
1662         for (i=0; i<(*num_entries); i++) {
1663                 fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx));
1664                 fstrcpy((*info)[i].acct_desc, centry_string(centry, mem_ctx));
1665                 (*info)[i].rid = centry_uint32(centry);
1666         }
1667
1668 do_cached:      
1669
1670         /* If we are returning cached data and the domain controller
1671            is down then we don't know whether the data is up to date
1672            or not.  Return NT_STATUS_MORE_PROCESSING_REQUIRED to
1673            indicate this. */
1674
1675         if (wcache_server_down(domain)) {
1676                 DEBUG(10, ("enum_local_groups: returning cached user list and server was down\n"));
1677                 status = NT_STATUS_MORE_PROCESSING_REQUIRED;
1678         } else
1679                 status = centry->status;
1680
1681         DEBUG(10,("enum_local_groups: [Cached] - cached list for domain %s status: %s\n",
1682                 domain->name, nt_errstr(status) ));
1683
1684         centry_free(centry);
1685         return status;
1686
1687 do_query:
1688         *num_entries = 0;
1689         *info = NULL;
1690
1691         /* Return status value returned by seq number check */
1692
1693         if (!NT_STATUS_IS_OK(domain->last_status))
1694                 return domain->last_status;
1695
1696         DEBUG(10,("enum_local_groups: [Cached] - doing backend query for list for domain %s\n",
1697                 domain->name ));
1698
1699         status = domain->backend->enum_local_groups(domain, mem_ctx, num_entries, info);
1700
1701         if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1702                 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1703                 if (!domain->internal && old_status) {
1704                         set_domain_offline(domain);
1705                 }
1706                 if (cache->tdb &&
1707                         !domain->internal &&
1708                         !domain->online &&
1709                         old_status) {
1710                         centry = wcache_fetch(cache, domain, "GL/%s/local", domain->name);
1711                         if (centry) {
1712                                 goto do_fetch_cache;
1713                         }
1714                 }
1715         }
1716         /* and save it */
1717         refresh_sequence_number(domain);
1718         if (!NT_STATUS_IS_OK(status)) {
1719                 return status;
1720         }
1721         centry = centry_start(domain, status);
1722         if (!centry)
1723                 goto skip_save;
1724         centry_put_uint32(centry, *num_entries);
1725         for (i=0; i<(*num_entries); i++) {
1726                 centry_put_string(centry, (*info)[i].acct_name);
1727                 centry_put_string(centry, (*info)[i].acct_desc);
1728                 centry_put_uint32(centry, (*info)[i].rid);
1729         }
1730         centry_end(centry, "GL/%s/local", domain->name);
1731         centry_free(centry);
1732
1733 skip_save:
1734         return status;
1735 }
1736
1737 struct wcache_name_to_sid_state {
1738         struct dom_sid *sid;
1739         enum lsa_SidType *type;
1740         bool offline;
1741         bool found;
1742 };
1743
1744 static void wcache_name_to_sid_fn(const struct dom_sid *sid,
1745                                   enum lsa_SidType type, time_t timeout,
1746                                   void *private_data)
1747 {
1748         struct wcache_name_to_sid_state *state = private_data;
1749
1750         *state->sid = *sid;
1751         *state->type = type;
1752         state->found = (state->offline || (timeout < time(NULL)));
1753 }
1754
1755 static NTSTATUS wcache_name_to_sid(struct winbindd_domain *domain,
1756                                    const char *domain_name,
1757                                    const char *name,
1758                                    struct dom_sid *sid,
1759                                    enum lsa_SidType *type)
1760 {
1761         struct wcache_name_to_sid_state state = {
1762                 .sid = sid, .type = type, .found = false,
1763                 .offline = is_domain_offline(domain),
1764         };
1765         bool ok;
1766
1767         ok = namemap_cache_find_name(domain_name, name, wcache_name_to_sid_fn,
1768                                      &state);
1769         if (!ok) {
1770                 DBG_DEBUG("namemap_cache_find_name failed\n");
1771                 return NT_STATUS_NOT_FOUND;
1772         }
1773         if (!state.found) {
1774                 DBG_DEBUG("cache entry not found\n");
1775                 return NT_STATUS_NOT_FOUND;
1776         }
1777         if (*type == SID_NAME_UNKNOWN) {
1778                 return NT_STATUS_NONE_MAPPED;
1779         }
1780
1781         return NT_STATUS_OK;
1782 }
1783
1784 /* convert a single name to a sid in a domain */
1785 NTSTATUS wb_cache_name_to_sid(struct winbindd_domain *domain,
1786                               TALLOC_CTX *mem_ctx,
1787                               const char *domain_name,
1788                               const char *name,
1789                               uint32_t flags,
1790                               struct dom_sid *sid,
1791                               enum lsa_SidType *type)
1792 {
1793         NTSTATUS status;
1794         bool old_status;
1795
1796         old_status = domain->online;
1797
1798         status = wcache_name_to_sid(domain, domain_name, name, sid, type);
1799         if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
1800                 return status;
1801         }
1802
1803         ZERO_STRUCTP(sid);
1804
1805         /* If the seq number check indicated that there is a problem
1806          * with this DC, then return that status... except for
1807          * access_denied.  This is special because the dc may be in
1808          * "restrict anonymous = 1" mode, in which case it will deny
1809          * most unauthenticated operations, but *will* allow the LSA
1810          * name-to-sid that we try as a fallback. */
1811
1812         if (!(NT_STATUS_IS_OK(domain->last_status)
1813               || NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)))
1814                 return domain->last_status;
1815
1816         DEBUG(10,("name_to_sid: [Cached] - doing backend query for name for domain %s\n",
1817                 domain->name ));
1818
1819         winbindd_domain_init_backend(domain);
1820         status = domain->backend->name_to_sid(domain, mem_ctx, domain_name,
1821                                               name, flags, sid, type);
1822
1823         if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1824                 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1825                 if (!domain->internal && old_status) {
1826                         set_domain_offline(domain);
1827                 }
1828                 if (!domain->internal &&
1829                         !domain->online &&
1830                         old_status) {
1831                         NTSTATUS cache_status;
1832                         cache_status = wcache_name_to_sid(domain, domain_name, name, sid, type);
1833                         return cache_status;
1834                 }
1835         }
1836         /* and save it */
1837         refresh_sequence_number(domain);
1838
1839         if (domain->online &&
1840             (NT_STATUS_IS_OK(status) || NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED))) {
1841                 enum lsa_SidType save_type = *type;
1842
1843                 if (NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED)) {
1844                         save_type = SID_NAME_UNKNOWN;
1845                 }
1846
1847                 wcache_save_name_to_sid(domain, status, domain_name, name, sid,
1848                                         save_type);
1849
1850                 /* Only save the reverse mapping if this was not a UPN */
1851                 if (!strchr(name, '@')) {
1852                         if (!strupper_m(discard_const_p(char, domain_name))) {
1853                                 return NT_STATUS_INVALID_PARAMETER;
1854                         }
1855                         (void)strlower_m(discard_const_p(char, name));
1856                         wcache_save_sid_to_name(domain, status, sid,
1857                                                 domain_name, name, save_type);
1858                 }
1859         }
1860
1861         return status;
1862 }
1863
1864 struct wcache_sid_to_name_state {
1865         TALLOC_CTX *mem_ctx;
1866         char **domain_name;
1867         char **name;
1868         enum lsa_SidType *type;
1869         bool offline;
1870         bool found;
1871 };
1872
1873 static void wcache_sid_to_name_fn(const char *domain, const char *name,
1874                                   enum lsa_SidType type, time_t timeout,
1875                                   void *private_data)
1876 {
1877         struct wcache_sid_to_name_state *state = private_data;
1878
1879         *state->domain_name = talloc_strdup(state->mem_ctx, domain);
1880         if (*state->domain_name == NULL) {
1881                 return;
1882         }
1883         *state->name = talloc_strdup(state->mem_ctx, name);
1884         if (*state->name == NULL) {
1885                 return;
1886         }
1887         *state->type = type;
1888         state->found = (state->offline || (timeout < time(NULL)));
1889 }
1890
1891 static NTSTATUS wcache_sid_to_name(struct winbindd_domain *domain,
1892                                    const struct dom_sid *sid,
1893                                    TALLOC_CTX *mem_ctx,
1894                                    char **domain_name,
1895                                    char **name,
1896                                    enum lsa_SidType *type)
1897 {
1898         struct wcache_sid_to_name_state state = {
1899                 .mem_ctx = mem_ctx, .found = false,
1900                 .domain_name = domain_name, .name = name, .type = type,
1901                 .offline = is_domain_offline(domain)
1902         };
1903         bool ok;
1904
1905         ok = namemap_cache_find_sid(sid, wcache_sid_to_name_fn, &state);
1906         if (!ok) {
1907                 DBG_DEBUG("namemap_cache_find_name failed\n");
1908                 return NT_STATUS_NOT_FOUND;
1909         }
1910         if (!state.found) {
1911                 DBG_DEBUG("cache entry not found\n");
1912                 return NT_STATUS_NOT_FOUND;
1913         }
1914         if (*type == SID_NAME_UNKNOWN) {
1915                 return NT_STATUS_NONE_MAPPED;
1916         }
1917
1918         return NT_STATUS_OK;
1919 }
1920
1921 /* convert a sid to a user or group name. The sid is guaranteed to be in the domain
1922    given */
1923 NTSTATUS wb_cache_sid_to_name(struct winbindd_domain *domain,
1924                               TALLOC_CTX *mem_ctx,
1925                               const struct dom_sid *sid,
1926                               char **domain_name,
1927                               char **name,
1928                               enum lsa_SidType *type)
1929 {
1930         NTSTATUS status;
1931         bool old_status;
1932
1933         old_status = domain->online;
1934         status = wcache_sid_to_name(domain, sid, mem_ctx, domain_name, name,
1935                                     type);
1936         if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
1937                 return status;
1938         }
1939
1940         *name = NULL;
1941         *domain_name = NULL;
1942
1943         /* If the seq number check indicated that there is a problem
1944          * with this DC, then return that status... except for
1945          * access_denied.  This is special because the dc may be in
1946          * "restrict anonymous = 1" mode, in which case it will deny
1947          * most unauthenticated operations, but *will* allow the LSA
1948          * sid-to-name that we try as a fallback. */
1949
1950         if (!(NT_STATUS_IS_OK(domain->last_status)
1951               || NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)))
1952                 return domain->last_status;
1953
1954         DEBUG(10,("sid_to_name: [Cached] - doing backend query for name for domain %s\n",
1955                 domain->name ));
1956
1957         winbindd_domain_init_backend(domain);
1958
1959         status = domain->backend->sid_to_name(domain, mem_ctx, sid, domain_name, name, type);
1960
1961         if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1962                 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1963                 if (!domain->internal && old_status) {
1964                         set_domain_offline(domain);
1965                 }
1966                 if (!domain->internal &&
1967                         !domain->online &&
1968                         old_status) {
1969                         NTSTATUS cache_status;
1970                         cache_status = wcache_sid_to_name(domain, sid, mem_ctx,
1971                                                         domain_name, name, type);
1972                         return cache_status;
1973                 }
1974         }
1975         /* and save it */
1976         refresh_sequence_number(domain);
1977         if (!NT_STATUS_IS_OK(status)) {
1978                 return status;
1979         }
1980         wcache_save_sid_to_name(domain, status, sid, *domain_name, *name, *type);
1981
1982         /* We can't save the name to sid mapping here, as with sid history a
1983          * later name2sid would give the wrong sid. */
1984
1985         return status;
1986 }
1987
1988 NTSTATUS wb_cache_rids_to_names(struct winbindd_domain *domain,
1989                                 TALLOC_CTX *mem_ctx,
1990                                 const struct dom_sid *domain_sid,
1991                                 uint32_t *rids,
1992                                 size_t num_rids,
1993                                 char **domain_name,
1994                                 char ***names,
1995                                 enum lsa_SidType **types)
1996 {
1997         struct winbind_cache *cache = get_cache(domain);
1998         size_t i;
1999         NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
2000         bool have_mapped;
2001         bool have_unmapped;
2002         bool old_status;
2003
2004         old_status = domain->online;
2005         *domain_name = NULL;
2006         *names = NULL;
2007         *types = NULL;
2008
2009         if (!cache->tdb) {
2010                 goto do_query;
2011         }
2012
2013         if (num_rids == 0) {
2014                 return NT_STATUS_OK;
2015         }
2016
2017         *names = talloc_array(mem_ctx, char *, num_rids);
2018         *types = talloc_array(mem_ctx, enum lsa_SidType, num_rids);
2019
2020         if ((*names == NULL) || (*types == NULL)) {
2021                 result = NT_STATUS_NO_MEMORY;
2022                 goto error;
2023         }
2024
2025         have_mapped = have_unmapped = false;
2026
2027         for (i=0; i<num_rids; i++) {
2028                 struct dom_sid sid;
2029                 NTSTATUS status;
2030                 enum lsa_SidType type;
2031                 char *dom, *name;
2032
2033                 if (!sid_compose(&sid, domain_sid, rids[i])) {
2034                         result = NT_STATUS_INTERNAL_ERROR;
2035                         goto error;
2036                 }
2037
2038                 status = wcache_sid_to_name(domain, &sid, *names, &dom,
2039                                             &name, &type);
2040
2041                 (*types)[i] = SID_NAME_UNKNOWN;
2042                 (*names)[i] = talloc_strdup(*names, "");
2043
2044                 if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2045                         /* not cached */
2046                         goto do_query;
2047                 }
2048
2049                 if (NT_STATUS_IS_OK(status)) {
2050                         have_mapped = true;
2051                         (*types)[i] = type;
2052
2053                         if (*domain_name == NULL) {
2054                                 *domain_name = dom;
2055                         } else {
2056                                 TALLOC_FREE(dom);
2057                         }
2058
2059                         (*names)[i] = name;
2060
2061                 } else if (NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED)) {
2062                         have_unmapped = true;
2063                 } else {
2064                         /* something's definitely wrong */
2065                         result = status;
2066                         goto error;
2067                 }
2068         }
2069
2070         if (!have_mapped) {
2071                 return NT_STATUS_NONE_MAPPED;
2072         }
2073         if (!have_unmapped) {
2074                 return NT_STATUS_OK;
2075         }
2076         return STATUS_SOME_UNMAPPED;
2077
2078  do_query:
2079
2080         TALLOC_FREE(*names);
2081         TALLOC_FREE(*types);
2082
2083         result = domain->backend->rids_to_names(domain, mem_ctx, domain_sid,
2084                                                 rids, num_rids, domain_name,
2085                                                 names, types);
2086
2087         if (NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
2088             NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2089                 if (!domain->internal && old_status) {
2090                         set_domain_offline(domain);
2091                 }
2092                 if (cache->tdb &&
2093                         !domain->internal &&
2094                         !domain->online &&
2095                         old_status) {
2096                         have_mapped = have_unmapped = false;
2097
2098                         *names = talloc_array(mem_ctx, char *, num_rids);
2099                         if (*names == NULL) {
2100                                 result = NT_STATUS_NO_MEMORY;
2101                                 goto error;
2102                         }
2103
2104                         *types = talloc_array(mem_ctx, enum lsa_SidType,
2105                                               num_rids);
2106                         if (*types == NULL) {
2107                                 result = NT_STATUS_NO_MEMORY;
2108                                 goto error;
2109                         }
2110
2111                         for (i=0; i<num_rids; i++) {
2112                                 struct dom_sid sid;
2113                                 NTSTATUS status;
2114                                 enum lsa_SidType type;
2115                                 char *dom, *name;
2116
2117                                 if (!sid_compose(&sid, domain_sid, rids[i])) {
2118                                         result = NT_STATUS_INTERNAL_ERROR;
2119                                         goto error;
2120                                 }
2121
2122                                 status = wcache_sid_to_name(domain, &sid,
2123                                                             *names, &dom,
2124                                                             &name, &type);
2125
2126                                 (*types)[i] = SID_NAME_UNKNOWN;
2127                                 (*names)[i] = talloc_strdup(*names, "");
2128
2129                                 if (NT_STATUS_IS_OK(status)) {
2130                                         have_mapped = true;
2131                                         (*types)[i] = type;
2132
2133                                         if (*domain_name == NULL) {
2134                                                 *domain_name = dom;
2135                                         } else {
2136                                                 TALLOC_FREE(dom);
2137                                         }
2138
2139                                         (*names)[i] = name;
2140
2141                                 } else if (NT_STATUS_EQUAL(
2142                                                    status,
2143                                                    NT_STATUS_NONE_MAPPED)) {
2144                                         have_unmapped = true;
2145                                 } else {
2146                                         /* something's definitely wrong */
2147                                         result = status;
2148                                         goto error;
2149                                 }
2150                         }
2151
2152                         if (!have_mapped) {
2153                                 return NT_STATUS_NONE_MAPPED;
2154                         }
2155                         if (!have_unmapped) {
2156                                 return NT_STATUS_OK;
2157                         }
2158                         return STATUS_SOME_UNMAPPED;
2159                 }
2160         }
2161         /*
2162           None of the queried rids has been found so save all negative entries
2163         */
2164         if (NT_STATUS_EQUAL(result, NT_STATUS_NONE_MAPPED)) {
2165                 for (i = 0; i < num_rids; i++) {
2166                         struct dom_sid sid;
2167                         const char *name = "";
2168                         const enum lsa_SidType type = SID_NAME_UNKNOWN;
2169                         NTSTATUS status = NT_STATUS_NONE_MAPPED;
2170
2171                         if (!sid_compose(&sid, domain_sid, rids[i])) {
2172                                 return NT_STATUS_INTERNAL_ERROR;
2173                         }
2174
2175                         wcache_save_sid_to_name(domain, status, &sid, *domain_name,
2176                                                 name, type);
2177                 }
2178
2179                 return result;
2180         }
2181
2182         /*
2183           Some or all of the queried rids have been found.
2184         */
2185         if (!NT_STATUS_IS_OK(result) &&
2186             !NT_STATUS_EQUAL(result, STATUS_SOME_UNMAPPED)) {
2187                 return result;
2188         }
2189
2190         refresh_sequence_number(domain);
2191
2192         for (i=0; i<num_rids; i++) {
2193                 struct dom_sid sid;
2194                 NTSTATUS status;
2195
2196                 if (!sid_compose(&sid, domain_sid, rids[i])) {
2197                         result = NT_STATUS_INTERNAL_ERROR;
2198                         goto error;
2199                 }
2200
2201                 status = (*types)[i] == SID_NAME_UNKNOWN ?
2202                         NT_STATUS_NONE_MAPPED : NT_STATUS_OK;
2203
2204                 wcache_save_sid_to_name(domain, status, &sid, *domain_name,
2205                                         (*names)[i], (*types)[i]);
2206         }
2207
2208         return result;
2209
2210  error:
2211         TALLOC_FREE(*names);
2212         TALLOC_FREE(*types);
2213         return result;
2214 }
2215
2216 static NTSTATUS wcache_query_user(struct winbindd_domain *domain,
2217                                   TALLOC_CTX *mem_ctx,
2218                                   const struct dom_sid *user_sid,
2219                                   struct wbint_userinfo *info)
2220 {
2221         struct winbind_cache *cache = get_cache(domain);
2222         struct cache_entry *centry = NULL;
2223         NTSTATUS status;
2224         char *sid_string;
2225
2226         if (cache->tdb == NULL) {
2227                 return NT_STATUS_NOT_FOUND;
2228         }
2229
2230         sid_string = sid_string_tos(user_sid);
2231         if (sid_string == NULL) {
2232                 return NT_STATUS_NO_MEMORY;
2233         }
2234
2235         centry = wcache_fetch(cache, domain, "U/%s", sid_string);
2236         TALLOC_FREE(sid_string);
2237         if (centry == NULL) {
2238                 return NT_STATUS_NOT_FOUND;
2239         }
2240
2241         /*
2242          * If we have an access denied cache entry and a cached info3
2243          * in the samlogon cache then do a query.  This will force the
2244          * rpc back end to return the info3 data.
2245          */
2246
2247         if (NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED) &&
2248             netsamlogon_cache_have(user_sid)) {
2249                 DEBUG(10, ("query_user: cached access denied and have cached "
2250                            "info3\n"));
2251                 domain->last_status = NT_STATUS_OK;
2252                 centry_free(centry);
2253                 return NT_STATUS_NOT_FOUND;
2254         }
2255
2256         /* if status is not ok then this is a negative hit
2257            and the rest of the data doesn't matter */
2258         status = centry->status;
2259         if (NT_STATUS_IS_OK(status)) {
2260                 info->domain_name = centry_string(centry, mem_ctx);
2261                 info->acct_name = centry_string(centry, mem_ctx);
2262                 info->full_name = centry_string(centry, mem_ctx);
2263                 info->homedir = centry_string(centry, mem_ctx);
2264                 info->shell = centry_string(centry, mem_ctx);
2265                 info->uid = centry_uint32(centry);
2266                 info->primary_gid = centry_uint32(centry);
2267                 info->primary_group_name = centry_string(centry, mem_ctx);
2268                 centry_sid(centry, &info->user_sid);
2269                 centry_sid(centry, &info->group_sid);
2270         }
2271
2272         DEBUG(10,("query_user: [Cached] - cached info for domain %s status: "
2273                   "%s\n", domain->name, nt_errstr(status) ));
2274
2275         centry_free(centry);
2276         return status;
2277 }
2278
2279
2280 /**
2281 * @brief Query a fullname from the username cache (for further gecos processing)
2282 *
2283 * @param domain         A pointer to the winbindd_domain struct.
2284 * @param mem_ctx        The talloc context.
2285 * @param user_sid       The user sid.
2286 * @param full_name      A pointer to the full_name string.
2287 *
2288 * @return NTSTATUS code
2289 */
2290 NTSTATUS wcache_query_user_fullname(struct winbindd_domain *domain,
2291                                     TALLOC_CTX *mem_ctx,
2292                                     const struct dom_sid *user_sid,
2293                                     const char **full_name)
2294 {
2295         NTSTATUS status;
2296         struct wbint_userinfo info;
2297
2298         status = wcache_query_user(domain, mem_ctx, user_sid, &info);
2299         if (!NT_STATUS_IS_OK(status)) {
2300                 return status;
2301         }
2302
2303         if (info.full_name != NULL) {
2304                 *full_name = talloc_strdup(mem_ctx, info.full_name);
2305                 if (*full_name == NULL) {
2306                         return NT_STATUS_NO_MEMORY;
2307                 }
2308         }
2309
2310         return NT_STATUS_OK;
2311 }
2312
2313 static NTSTATUS wcache_lookup_usergroups(struct winbindd_domain *domain,
2314                                          TALLOC_CTX *mem_ctx,
2315                                          const struct dom_sid *user_sid,
2316                                          uint32_t *pnum_sids,
2317                                          struct dom_sid **psids)
2318 {
2319         struct winbind_cache *cache = get_cache(domain);
2320         struct cache_entry *centry = NULL;
2321         NTSTATUS status;
2322         uint32_t i, num_sids;
2323         struct dom_sid *sids;
2324         fstring sid_string;
2325
2326         if (cache->tdb == NULL) {
2327                 return NT_STATUS_NOT_FOUND;
2328         }
2329
2330         centry = wcache_fetch(cache, domain, "UG/%s",
2331                               sid_to_fstring(sid_string, user_sid));
2332         if (centry == NULL) {
2333                 return NT_STATUS_NOT_FOUND;
2334         }
2335
2336         /* If we have an access denied cache entry and a cached info3 in the
2337            samlogon cache then do a query.  This will force the rpc back end
2338            to return the info3 data. */
2339
2340         if (NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)
2341             && netsamlogon_cache_have(user_sid)) {
2342                 DEBUG(10, ("lookup_usergroups: cached access denied and have "
2343                            "cached info3\n"));
2344                 domain->last_status = NT_STATUS_OK;
2345                 centry_free(centry);
2346                 return NT_STATUS_NOT_FOUND;
2347         }
2348
2349         num_sids = centry_uint32(centry);
2350         sids = talloc_array(mem_ctx, struct dom_sid, num_sids);
2351         if (sids == NULL) {
2352                 centry_free(centry);
2353                 return NT_STATUS_NO_MEMORY;
2354         }
2355
2356         for (i=0; i<num_sids; i++) {
2357                 centry_sid(centry, &sids[i]);
2358         }
2359
2360         status = centry->status;
2361
2362         DEBUG(10,("lookup_usergroups: [Cached] - cached info for domain %s "
2363                   "status: %s\n", domain->name, nt_errstr(status)));
2364
2365         centry_free(centry);
2366
2367         *pnum_sids = num_sids;
2368         *psids = sids;
2369         return status;
2370 }
2371
2372 /* Lookup groups a user is a member of. */
2373 NTSTATUS wb_cache_lookup_usergroups(struct winbindd_domain *domain,
2374                                     TALLOC_CTX *mem_ctx,
2375                                     const struct dom_sid *user_sid,
2376                                     uint32_t *num_groups,
2377                                     struct dom_sid **user_gids)
2378 {
2379         struct cache_entry *centry = NULL;
2380         NTSTATUS status;
2381         unsigned int i;
2382         fstring sid_string;
2383         bool old_status;
2384
2385         old_status = domain->online;
2386         status = wcache_lookup_usergroups(domain, mem_ctx, user_sid,
2387                                           num_groups, user_gids);
2388         if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2389                 return status;
2390         }
2391
2392         (*num_groups) = 0;
2393         (*user_gids) = NULL;
2394
2395         /* Return status value returned by seq number check */
2396
2397         if (!NT_STATUS_IS_OK(domain->last_status))
2398                 return domain->last_status;
2399
2400         DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info for domain %s\n",
2401                 domain->name ));
2402
2403         status = domain->backend->lookup_usergroups(domain, mem_ctx, user_sid, num_groups, user_gids);
2404
2405         if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2406                 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2407                 if (!domain->internal && old_status) {
2408                         set_domain_offline(domain);
2409                 }
2410                 if (!domain->internal &&
2411                         !domain->online &&
2412                         old_status) {
2413                         NTSTATUS cache_status;
2414                         cache_status = wcache_lookup_usergroups(domain, mem_ctx, user_sid,
2415                                                           num_groups, user_gids);
2416                         return cache_status;
2417                 }
2418         }
2419         if ( NT_STATUS_EQUAL(status, NT_STATUS_SYNCHRONIZATION_REQUIRED) )
2420                 goto skip_save;
2421
2422         /* and save it */
2423         refresh_sequence_number(domain);
2424         if (!NT_STATUS_IS_OK(status)) {
2425                 return status;
2426         }
2427         centry = centry_start(domain, status);
2428         if (!centry)
2429                 goto skip_save;
2430
2431         centry_put_uint32(centry, *num_groups);
2432         for (i=0; i<(*num_groups); i++) {
2433                 centry_put_sid(centry, &(*user_gids)[i]);
2434         }       
2435
2436         centry_end(centry, "UG/%s", sid_to_fstring(sid_string, user_sid));
2437         centry_free(centry);
2438
2439 skip_save:
2440         return status;
2441 }
2442
2443 static char *wcache_make_sidlist(TALLOC_CTX *mem_ctx, uint32_t num_sids,
2444                                  const struct dom_sid *sids)
2445 {
2446         uint32_t i;
2447         char *sidlist;
2448
2449         sidlist = talloc_strdup(mem_ctx, "");
2450         if (sidlist == NULL) {
2451                 return NULL;
2452         }
2453         for (i=0; i<num_sids; i++) {
2454                 fstring tmp;
2455                 sidlist = talloc_asprintf_append_buffer(
2456                         sidlist, "/%s", sid_to_fstring(tmp, &sids[i]));
2457                 if (sidlist == NULL) {
2458                         return NULL;
2459                 }
2460         }
2461         return sidlist;
2462 }
2463
2464 static NTSTATUS wcache_lookup_useraliases(struct winbindd_domain *domain,
2465                                           TALLOC_CTX *mem_ctx,
2466                                           uint32_t num_sids,
2467                                           const struct dom_sid *sids,
2468                                           uint32_t *pnum_aliases,
2469                                           uint32_t **paliases)
2470 {
2471         struct winbind_cache *cache = get_cache(domain);
2472         struct cache_entry *centry = NULL;
2473         uint32_t i, num_aliases;
2474         uint32_t *aliases;
2475         NTSTATUS status;
2476         char *sidlist;
2477
2478         if (cache->tdb == NULL) {
2479                 return NT_STATUS_NOT_FOUND;
2480         }
2481
2482         if (num_sids == 0) {
2483                 *pnum_aliases = 0;
2484                 *paliases = NULL;
2485                 return NT_STATUS_OK;
2486         }
2487
2488         /* We need to cache indexed by the whole list of SIDs, the aliases
2489          * resulting might come from any of the SIDs. */
2490
2491         sidlist = wcache_make_sidlist(talloc_tos(), num_sids, sids);
2492         if (sidlist == NULL) {
2493                 return NT_STATUS_NO_MEMORY;
2494         }
2495
2496         centry = wcache_fetch(cache, domain, "UA%s", sidlist);
2497         TALLOC_FREE(sidlist);
2498         if (centry == NULL) {
2499                 return NT_STATUS_NOT_FOUND;
2500         }
2501
2502         num_aliases = centry_uint32(centry);
2503         aliases = talloc_array(mem_ctx, uint32_t, num_aliases);
2504         if (aliases == NULL) {
2505                 centry_free(centry);
2506                 return NT_STATUS_NO_MEMORY;
2507         }
2508
2509         for (i=0; i<num_aliases; i++) {
2510                 aliases[i] = centry_uint32(centry);
2511         }
2512
2513         status = centry->status;
2514
2515         DEBUG(10,("lookup_useraliases: [Cached] - cached info for domain: %s "
2516                   "status %s\n", domain->name, nt_errstr(status)));
2517
2518         centry_free(centry);
2519
2520         *pnum_aliases = num_aliases;
2521         *paliases = aliases;
2522
2523         return status;
2524 }
2525
2526 NTSTATUS wb_cache_lookup_useraliases(struct winbindd_domain *domain,
2527                                      TALLOC_CTX *mem_ctx,
2528                                      uint32_t num_sids,
2529                                      const struct dom_sid *sids,
2530                                      uint32_t *num_aliases,
2531                                      uint32_t **alias_rids)
2532 {
2533         struct cache_entry *centry = NULL;
2534         NTSTATUS status;
2535         char *sidlist;
2536         uint32_t i;
2537         bool old_status;
2538
2539         old_status = domain->online;
2540         status = wcache_lookup_useraliases(domain, mem_ctx, num_sids, sids,
2541                                            num_aliases, alias_rids);
2542         if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2543                 return status;
2544         }
2545
2546         (*num_aliases) = 0;
2547         (*alias_rids) = NULL;
2548
2549         if (!NT_STATUS_IS_OK(domain->last_status))
2550                 return domain->last_status;
2551
2552         DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info "
2553                   "for domain %s\n", domain->name ));
2554
2555         sidlist = wcache_make_sidlist(talloc_tos(), num_sids, sids);
2556         if (sidlist == NULL) {
2557                 return NT_STATUS_NO_MEMORY;
2558         }
2559
2560         status = domain->backend->lookup_useraliases(domain, mem_ctx,
2561                                                      num_sids, sids,
2562                                                      num_aliases, alias_rids);
2563
2564         if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2565                 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2566                 if (!domain->internal && old_status) {
2567                         set_domain_offline(domain);
2568                 }
2569                 if (!domain->internal &&
2570                         !domain->online &&
2571                         old_status) {
2572                         NTSTATUS cache_status;
2573                         cache_status = wcache_lookup_useraliases(domain, mem_ctx, num_sids,
2574                                                                  sids, num_aliases, alias_rids);
2575                         return cache_status;
2576                 }
2577         }
2578         /* and save it */
2579         refresh_sequence_number(domain);
2580         if (!NT_STATUS_IS_OK(status)) {
2581                 return status;
2582         }
2583         centry = centry_start(domain, status);
2584         if (!centry)
2585                 goto skip_save;
2586         centry_put_uint32(centry, *num_aliases);
2587         for (i=0; i<(*num_aliases); i++)
2588                 centry_put_uint32(centry, (*alias_rids)[i]);
2589         centry_end(centry, "UA%s", sidlist);
2590         centry_free(centry);
2591
2592  skip_save:
2593         return status;
2594 }
2595
2596 static NTSTATUS wcache_lookup_groupmem(struct winbindd_domain *domain,
2597                                        TALLOC_CTX *mem_ctx,
2598                                        const struct dom_sid *group_sid,
2599                                        uint32_t *num_names,
2600                                        struct dom_sid **sid_mem, char ***names,
2601                                        uint32_t **name_types)
2602 {
2603         struct winbind_cache *cache = get_cache(domain);
2604         struct cache_entry *centry = NULL;
2605         NTSTATUS status;
2606         unsigned int i;
2607         char *sid_string;
2608
2609         if (cache->tdb == NULL) {
2610                 return NT_STATUS_NOT_FOUND;
2611         }
2612
2613         sid_string = sid_string_tos(group_sid);
2614         if (sid_string == NULL) {
2615                 return NT_STATUS_NO_MEMORY;
2616         }
2617
2618         centry = wcache_fetch(cache, domain, "GM/%s", sid_string);
2619         TALLOC_FREE(sid_string);
2620         if (centry == NULL) {
2621                 return NT_STATUS_NOT_FOUND;
2622         }
2623
2624         *sid_mem = NULL;
2625         *names = NULL;
2626         *name_types = NULL;
2627
2628         *num_names = centry_uint32(centry);
2629         if (*num_names == 0) {
2630                 centry_free(centry);
2631                 return NT_STATUS_OK;
2632         }
2633
2634         *sid_mem = talloc_array(mem_ctx, struct dom_sid, *num_names);
2635         *names = talloc_array(mem_ctx, char *, *num_names);
2636         *name_types = talloc_array(mem_ctx, uint32_t, *num_names);
2637
2638         if ((*sid_mem == NULL) || (*names == NULL) || (*name_types == NULL)) {
2639                 TALLOC_FREE(*sid_mem);
2640                 TALLOC_FREE(*names);
2641                 TALLOC_FREE(*name_types);
2642                 centry_free(centry);
2643                 return NT_STATUS_NO_MEMORY;
2644         }
2645
2646         for (i=0; i<(*num_names); i++) {
2647                 centry_sid(centry, &(*sid_mem)[i]);
2648                 (*names)[i] = centry_string(centry, mem_ctx);
2649                 (*name_types)[i] = centry_uint32(centry);
2650         }
2651
2652         status = centry->status;
2653
2654         DEBUG(10,("lookup_groupmem: [Cached] - cached info for domain %s "
2655                   "status: %s\n", domain->name, nt_errstr(status)));
2656
2657         centry_free(centry);
2658         return status;
2659 }
2660
2661 NTSTATUS wb_cache_lookup_groupmem(struct winbindd_domain *domain,
2662                                   TALLOC_CTX *mem_ctx,
2663                                   const struct dom_sid *group_sid,
2664                                   enum lsa_SidType type,
2665                                   uint32_t *num_names,
2666                                   struct dom_sid **sid_mem,
2667                                   char ***names,
2668                                   uint32_t **name_types)
2669 {
2670         struct cache_entry *centry = NULL;
2671         NTSTATUS status;
2672         unsigned int i;
2673         fstring sid_string;
2674         bool old_status;
2675
2676         old_status = domain->online;
2677         status = wcache_lookup_groupmem(domain, mem_ctx, group_sid, num_names,
2678                                         sid_mem, names, name_types);
2679         if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2680                 return status;
2681         }
2682
2683         (*num_names) = 0;
2684         (*sid_mem) = NULL;
2685         (*names) = NULL;
2686         (*name_types) = NULL;
2687
2688         /* Return status value returned by seq number check */
2689
2690         if (!NT_STATUS_IS_OK(domain->last_status))
2691                 return domain->last_status;
2692
2693         DEBUG(10,("lookup_groupmem: [Cached] - doing backend query for info for domain %s\n",
2694                 domain->name ));
2695
2696         status = domain->backend->lookup_groupmem(domain, mem_ctx, group_sid,
2697                                                   type, num_names,
2698                                                   sid_mem, names, name_types);
2699
2700         if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2701                 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2702                 if (!domain->internal && old_status) {
2703                         set_domain_offline(domain);
2704                 }
2705                 if (!domain->internal &&
2706                         !domain->online &&
2707                         old_status) {
2708                         NTSTATUS cache_status;
2709                         cache_status = wcache_lookup_groupmem(domain, mem_ctx, group_sid,
2710                                                               num_names, sid_mem, names,
2711                                                               name_types);
2712                         return cache_status;
2713                 }
2714         }
2715         /* and save it */
2716         refresh_sequence_number(domain);
2717         if (!NT_STATUS_IS_OK(status)) {
2718                 return status;
2719         }
2720         centry = centry_start(domain, status);
2721         if (!centry)
2722                 goto skip_save;
2723         centry_put_uint32(centry, *num_names);
2724         for (i=0; i<(*num_names); i++) {
2725                 centry_put_sid(centry, &(*sid_mem)[i]);
2726                 centry_put_string(centry, (*names)[i]);
2727                 centry_put_uint32(centry, (*name_types)[i]);
2728         }       
2729         centry_end(centry, "GM/%s", sid_to_fstring(sid_string, group_sid));
2730         centry_free(centry);
2731
2732 skip_save:
2733         return status;
2734 }
2735
2736 /* find the sequence number for a domain */
2737 NTSTATUS wb_cache_sequence_number(struct winbindd_domain *domain,
2738                                   uint32_t *seq)
2739 {
2740         refresh_sequence_number(domain);
2741
2742         *seq = domain->sequence_number;
2743
2744         return NT_STATUS_OK;
2745 }
2746
2747 /* enumerate trusted domains 
2748  * (we need to have the list of trustdoms in the cache when we go offline) -
2749  * Guenther */
2750 NTSTATUS wb_cache_trusted_domains(struct winbindd_domain *domain,
2751                                   TALLOC_CTX *mem_ctx,
2752                                   struct netr_DomainTrustList *trusts)
2753 {
2754         NTSTATUS status;
2755         struct winbind_cache *cache;
2756         struct winbindd_tdc_domain *dom_list = NULL;
2757         size_t num_domains = 0;
2758         bool retval = false;
2759         size_t i;
2760         bool old_status;
2761
2762         old_status = domain->online;
2763         trusts->count = 0;
2764         trusts->array = NULL;
2765
2766         cache = get_cache(domain);
2767         if (!cache || !cache->tdb) {
2768                 goto do_query;
2769         }
2770
2771         if (domain->online) {
2772                 goto do_query;
2773         }
2774
2775         retval = wcache_tdc_fetch_list(&dom_list, &num_domains);
2776         if (!retval || !num_domains || !dom_list) {
2777                 TALLOC_FREE(dom_list);
2778                 goto do_query;
2779         }
2780
2781 do_fetch_cache:
2782         trusts->array = talloc_zero_array(mem_ctx, struct netr_DomainTrust, num_domains);
2783         if (!trusts->array) {
2784                 TALLOC_FREE(dom_list);
2785                 return NT_STATUS_NO_MEMORY;
2786         }
2787
2788         for (i = 0; i < num_domains; i++) {
2789                 struct netr_DomainTrust *trust;
2790                 struct dom_sid *sid;
2791                 struct winbindd_domain *dom;
2792
2793                 dom = find_domain_from_name_noinit(dom_list[i].domain_name);
2794                 if (dom && dom->internal) {
2795                         continue;
2796                 }
2797
2798                 trust = &trusts->array[trusts->count];
2799                 trust->netbios_name = talloc_strdup(trusts->array, dom_list[i].domain_name);
2800                 trust->dns_name = talloc_strdup(trusts->array, dom_list[i].dns_name);
2801                 sid = talloc(trusts->array, struct dom_sid);
2802                 if (!trust->netbios_name || !trust->dns_name ||
2803                         !sid) {
2804                         TALLOC_FREE(dom_list);
2805                         TALLOC_FREE(trusts->array);
2806                         return NT_STATUS_NO_MEMORY;
2807                 }
2808
2809                 trust->trust_flags = dom_list[i].trust_flags;
2810                 trust->trust_attributes = dom_list[i].trust_attribs;
2811                 trust->trust_type = dom_list[i].trust_type;
2812                 sid_copy(sid, &dom_list[i].sid);
2813                 trust->sid = sid;
2814                 trusts->count++;
2815         }
2816
2817         TALLOC_FREE(dom_list);
2818         return NT_STATUS_OK;
2819
2820 do_query:
2821         /* Return status value returned by seq number check */
2822
2823         if (!NT_STATUS_IS_OK(domain->last_status))
2824                 return domain->last_status;
2825
2826         DEBUG(10,("trusted_domains: [Cached] - doing backend query for info for domain %s\n",
2827                 domain->name ));
2828
2829         status = domain->backend->trusted_domains(domain, mem_ctx, trusts);
2830
2831         if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2832                 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2833                 if (!domain->internal && old_status) {
2834                         set_domain_offline(domain);
2835                 }
2836                 if (!domain->internal &&
2837                         !domain->online &&
2838                         old_status) {
2839                         retval = wcache_tdc_fetch_list(&dom_list, &num_domains);
2840                         if (retval && num_domains && dom_list) {
2841                                 TALLOC_FREE(trusts->array);
2842                                 trusts->count = 0;
2843                                 goto do_fetch_cache;
2844                         }
2845                 }
2846         }
2847         /* no trusts gives NT_STATUS_NO_MORE_ENTRIES resetting to NT_STATUS_OK
2848          * so that the generic centry handling still applies correctly -
2849          * Guenther*/
2850
2851         if (!NT_STATUS_IS_ERR(status)) {
2852                 status = NT_STATUS_OK;
2853         }
2854         return status;
2855 }       
2856
2857 /* get lockout policy */
2858 NTSTATUS wb_cache_lockout_policy(struct winbindd_domain *domain,
2859                                  TALLOC_CTX *mem_ctx,
2860                                  struct samr_DomInfo12 *policy)
2861 {
2862         struct winbind_cache *cache = get_cache(domain);
2863         struct cache_entry *centry = NULL;
2864         NTSTATUS status;
2865         bool old_status;
2866
2867         old_status = domain->online;
2868         if (!cache->tdb)
2869                 goto do_query;
2870
2871         centry = wcache_fetch(cache, domain, "LOC_POL/%s", domain->name);
2872
2873         if (!centry)
2874                 goto do_query;
2875
2876 do_fetch_cache:
2877         policy->lockout_duration = centry_nttime(centry);
2878         policy->lockout_window = centry_nttime(centry);
2879         policy->lockout_threshold = centry_uint16(centry);
2880
2881         status = centry->status;
2882
2883         DEBUG(10,("lockout_policy: [Cached] - cached info for domain %s status: %s\n",
2884                 domain->name, nt_errstr(status) ));
2885
2886         centry_free(centry);
2887         return status;
2888
2889 do_query:
2890         ZERO_STRUCTP(policy);
2891
2892         /* Return status value returned by seq number check */
2893
2894         if (!NT_STATUS_IS_OK(domain->last_status))
2895                 return domain->last_status;
2896
2897         DEBUG(10,("lockout_policy: [Cached] - doing backend query for info for domain %s\n",
2898                 domain->name ));
2899
2900         status = domain->backend->lockout_policy(domain, mem_ctx, policy);
2901
2902         if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2903                 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2904                 if (!domain->internal && old_status) {
2905                         set_domain_offline(domain);
2906                 }
2907                 if (cache->tdb &&
2908                         !domain->internal &&
2909                         !domain->online &&
2910                         old_status) {
2911                         centry = wcache_fetch(cache, domain, "LOC_POL/%s", domain->name);
2912                         if (centry) {
2913                                 goto do_fetch_cache;
2914                         }
2915                 }
2916         }
2917         /* and save it */
2918         refresh_sequence_number(domain);
2919         if (!NT_STATUS_IS_OK(status)) {
2920                 return status;
2921         }
2922         wcache_save_lockout_policy(domain, status, policy);
2923
2924         return status;
2925 }
2926
2927 /* get password policy */
2928 NTSTATUS wb_cache_password_policy(struct winbindd_domain *domain,
2929                                   TALLOC_CTX *mem_ctx,
2930                                   struct samr_DomInfo1 *policy)
2931 {
2932         struct winbind_cache *cache = get_cache(domain);
2933         struct cache_entry *centry = NULL;
2934         NTSTATUS status;
2935         bool old_status;
2936
2937         old_status = domain->online;
2938         if (!cache->tdb)
2939                 goto do_query;
2940
2941         centry = wcache_fetch(cache, domain, "PWD_POL/%s", domain->name);
2942
2943         if (!centry)
2944                 goto do_query;
2945
2946 do_fetch_cache:
2947         policy->min_password_length = centry_uint16(centry);
2948         policy->password_history_length = centry_uint16(centry);
2949         policy->password_properties = centry_uint32(centry);
2950         policy->max_password_age = centry_nttime(centry);
2951         policy->min_password_age = centry_nttime(centry);
2952
2953         status = centry->status;
2954
2955         DEBUG(10,("lockout_policy: [Cached] - cached info for domain %s status: %s\n",
2956                 domain->name, nt_errstr(status) ));
2957
2958         centry_free(centry);
2959         return status;
2960
2961 do_query:
2962         ZERO_STRUCTP(policy);
2963
2964         /* Return status value returned by seq number check */
2965
2966         if (!NT_STATUS_IS_OK(domain->last_status))
2967                 return domain->last_status;
2968
2969         DEBUG(10,("password_policy: [Cached] - doing backend query for info for domain %s\n",
2970                 domain->name ));
2971
2972         status = domain->backend->password_policy(domain, mem_ctx, policy);
2973
2974         if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2975                 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2976                 if (!domain->internal && old_status) {
2977                         set_domain_offline(domain);
2978                 }
2979                 if (cache->tdb &&
2980                         !domain->internal &&
2981                         !domain->online &&
2982                         old_status) {
2983                         centry = wcache_fetch(cache, domain, "PWD_POL/%s", domain->name);
2984                         if (centry) {
2985                                 goto do_fetch_cache;
2986                         }
2987                 }
2988         }
2989         /* and save it */
2990         refresh_sequence_number(domain);
2991         if (!NT_STATUS_IS_OK(status)) {
2992                 return status;
2993         }
2994         wcache_save_password_policy(domain, status, policy);
2995
2996         return status;
2997 }
2998
2999
3000 /* Invalidate cached user and group lists coherently */
3001
3002 static int traverse_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf, 
3003                        void *state)
3004 {
3005         if (strncmp((const char *)kbuf.dptr, "UL/", 3) == 0 ||
3006             strncmp((const char *)kbuf.dptr, "GL/", 3) == 0)
3007                 tdb_delete(the_tdb, kbuf);
3008
3009         return 0;
3010 }
3011
3012 /* Invalidate the getpwnam and getgroups entries for a winbindd domain */
3013
3014 void wcache_invalidate_samlogon(struct winbindd_domain *domain, 
3015                                 const struct dom_sid *sid)
3016 {
3017         fstring key_str, sid_string;
3018         struct winbind_cache *cache;
3019
3020         /* don't clear cached U/SID and UG/SID entries when we want to logon
3021          * offline - gd */
3022
3023         if (lp_winbind_offline_logon()) {
3024                 return;
3025         }
3026
3027         if (!domain)
3028                 return;
3029
3030         cache = get_cache(domain);
3031
3032         if (!cache->tdb) {
3033                 return;
3034         }
3035
3036         /* Clear U/SID cache entry */
3037         fstr_sprintf(key_str, "U/%s", sid_to_fstring(sid_string, sid));
3038         DEBUG(10, ("wcache_invalidate_samlogon: clearing %s\n", key_str));
3039         tdb_delete(cache->tdb, string_tdb_data(key_str));
3040
3041         /* Clear UG/SID cache entry */
3042         fstr_sprintf(key_str, "UG/%s", sid_to_fstring(sid_string, sid));
3043         DEBUG(10, ("wcache_invalidate_samlogon: clearing %s\n", key_str));
3044         tdb_delete(cache->tdb, string_tdb_data(key_str));
3045
3046         /* Samba/winbindd never needs this. */
3047         netsamlogon_clear_cached_user(sid);
3048 }
3049
3050 bool wcache_invalidate_cache(void)
3051 {
3052         struct winbindd_domain *domain;
3053
3054         for (domain = domain_list(); domain; domain = domain->next) {
3055                 struct winbind_cache *cache = get_cache(domain);
3056
3057                 DEBUG(10, ("wcache_invalidate_cache: invalidating cache "
3058                            "entries for %s\n", domain->name));
3059                 if (cache) {
3060                         if (cache->tdb) {
3061                                 tdb_traverse(cache->tdb, traverse_fn, NULL);
3062                         } else {
3063                                 return false;
3064                         }
3065                 }
3066         }
3067         return true;
3068 }
3069
3070 bool wcache_invalidate_cache_noinit(void)
3071 {
3072         struct winbindd_domain *domain;
3073
3074         for (domain = domain_list(); domain; domain = domain->next) {
3075                 struct winbind_cache *cache;
3076
3077                 /* Skip uninitialized domains. */
3078                 if (!domain->initialized && !domain->internal) {
3079                         continue;
3080                 }
3081
3082                 cache = get_cache(domain);
3083
3084                 DEBUG(10, ("wcache_invalidate_cache: invalidating cache "
3085                            "entries for %s\n", domain->name));
3086                 if (cache) {
3087                         if (cache->tdb) {
3088                                 tdb_traverse(cache->tdb, traverse_fn, NULL);
3089                                 /*
3090                                  * Flushing cache has nothing to with domains.
3091                                  * return here if we successfully flushed once.
3092                                  * To avoid unnecessary traversing the cache.
3093                                  */
3094                                 return true;
3095                         } else {
3096                                 return false;
3097                         }
3098                 }
3099         }
3100         return true;
3101 }
3102
3103 static bool init_wcache(void)
3104 {
3105         char *db_path;
3106
3107         if (wcache == NULL) {
3108                 wcache = SMB_XMALLOC_P(struct winbind_cache);
3109                 ZERO_STRUCTP(wcache);
3110         }
3111
3112         if (wcache->tdb != NULL)
3113                 return true;
3114
3115         db_path = wcache_path();
3116         if (db_path == NULL) {
3117                 return false;
3118         }
3119
3120         /* when working offline we must not clear the cache on restart */
3121         wcache->tdb = tdb_open_log(db_path,
3122                                 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE, 
3123                                 TDB_INCOMPATIBLE_HASH |
3124                                         (lp_winbind_offline_logon() ? TDB_DEFAULT : (TDB_DEFAULT | TDB_CLEAR_IF_FIRST)),
3125                                 O_RDWR|O_CREAT, 0600);
3126         TALLOC_FREE(db_path);
3127         if (wcache->tdb == NULL) {
3128                 DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
3129                 return false;
3130         }
3131
3132         return true;
3133 }
3134
3135 /************************************************************************
3136  This is called by the parent to initialize the cache file.
3137  We don't need sophisticated locking here as we know we're the
3138  only opener.
3139 ************************************************************************/
3140
3141 bool initialize_winbindd_cache(void)
3142 {
3143         bool cache_bad = true;
3144         uint32_t vers;
3145
3146         if (!init_wcache()) {
3147                 DEBUG(0,("initialize_winbindd_cache: init_wcache failed.\n"));
3148                 return false;
3149         }
3150
3151         /* Check version number. */
3152         if (tdb_fetch_uint32(wcache->tdb, WINBINDD_CACHE_VERSION_KEYSTR, &vers) &&
3153                         vers == WINBINDD_CACHE_VERSION) {
3154                 cache_bad = false;
3155         }
3156
3157         if (cache_bad) {
3158                 char *db_path;
3159
3160                 DEBUG(0,("initialize_winbindd_cache: clearing cache "
3161                         "and re-creating with version number %d\n",
3162                         WINBINDD_CACHE_VERSION ));
3163
3164                 tdb_close(wcache->tdb);
3165                 wcache->tdb = NULL;
3166
3167                 db_path = wcache_path();
3168                 if (db_path == NULL) {
3169                         return false;
3170                 }
3171
3172                 if (unlink(db_path) == -1) {
3173                         DEBUG(0,("initialize_winbindd_cache: unlink %s failed %s ",
3174                                 db_path,
3175                                 strerror(errno) ));
3176                         TALLOC_FREE(db_path);
3177                         return false;
3178                 }
3179                 TALLOC_FREE(db_path);
3180                 if (!init_wcache()) {
3181                         DEBUG(0,("initialize_winbindd_cache: re-initialization "
3182                                         "init_wcache failed.\n"));
3183                         return false;
3184                 }
3185
3186                 /* Write the version. */
3187                 if (!tdb_store_uint32(wcache->tdb, WINBINDD_CACHE_VERSION_KEYSTR, WINBINDD_CACHE_VERSION)) {
3188                         DEBUG(0,("initialize_winbindd_cache: version number store failed %s\n",
3189                                 tdb_errorstr(wcache->tdb) ));
3190                         return false;
3191                 }
3192         }
3193
3194         tdb_close(wcache->tdb);
3195         wcache->tdb = NULL;
3196         return true;
3197 }
3198
3199 void close_winbindd_cache(void)
3200 {
3201         if (!wcache) {
3202                 return;
3203         }
3204         if (wcache->tdb) {
3205                 tdb_close(wcache->tdb);
3206                 wcache->tdb = NULL;
3207         }
3208 }
3209
3210 bool lookup_cached_sid(TALLOC_CTX *mem_ctx, const struct dom_sid *sid,
3211                        char **domain_name, char **name,
3212                        enum lsa_SidType *type)
3213 {
3214         struct winbindd_domain *domain;
3215         NTSTATUS status;
3216
3217         domain = find_lookup_domain_from_sid(sid);
3218         if (domain == NULL) {
3219                 return false;
3220         }
3221         status = wcache_sid_to_name(domain, sid, mem_ctx, domain_name, name,
3222                                     type);
3223         return NT_STATUS_IS_OK(status);
3224 }
3225
3226 bool lookup_cached_name(const char *domain_name,
3227                         const char *name,
3228                         struct dom_sid *sid,
3229                         enum lsa_SidType *type)
3230 {
3231         struct winbindd_domain *domain;
3232         NTSTATUS status;
3233         bool original_online_state;
3234
3235         domain = find_lookup_domain_from_name(domain_name);
3236         if (domain == NULL) {
3237                 return false;
3238         }
3239
3240         /* If we are doing a cached logon, temporarily set the domain
3241            offline so the cache won't expire the entry */
3242
3243         original_online_state = domain->online;
3244         domain->online = false;
3245         status = wcache_name_to_sid(domain, domain_name, name, sid, type);
3246         domain->online = original_online_state;
3247
3248         return NT_STATUS_IS_OK(status);
3249 }
3250
3251 /*
3252  * Cache a name to sid without checking the sequence number.
3253  * Used when caching from a trusted PAC.
3254  */
3255
3256 void cache_name2sid_trusted(struct winbindd_domain *domain,
3257                         const char *domain_name,
3258                         const char *name,
3259                         enum lsa_SidType type,
3260                         const struct dom_sid *sid)
3261 {
3262         /*
3263          * Ensure we store the mapping with the
3264          * existing sequence number from the cache.
3265          */
3266         get_cache(domain);
3267         (void)fetch_cache_seqnum(domain, time(NULL));
3268         wcache_save_name_to_sid(domain,
3269                                 NT_STATUS_OK,
3270                                 domain_name,
3271                                 name,
3272                                 sid,
3273                                 type);
3274 }
3275
3276 void cache_name2sid(struct winbindd_domain *domain, 
3277                     const char *domain_name, const char *name,
3278                     enum lsa_SidType type, const struct dom_sid *sid)
3279 {
3280         refresh_sequence_number(domain);
3281         wcache_save_name_to_sid(domain, NT_STATUS_OK, domain_name, name,
3282                                 sid, type);
3283 }
3284
3285 /*
3286  * The original idea that this cache only contains centries has
3287  * been blurred - now other stuff gets put in here. Ensure we
3288  * ignore these things on cleanup.
3289  */
3290
3291 static int traverse_fn_cleanup(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, 
3292                                TDB_DATA dbuf, void *state)
3293 {
3294         struct cache_entry *centry;
3295
3296         if (is_non_centry_key(kbuf)) {
3297                 return 0;
3298         }
3299
3300         centry = wcache_fetch_raw((char *)kbuf.dptr);
3301         if (!centry) {
3302                 return 0;
3303         }
3304
3305         if (!NT_STATUS_IS_OK(centry->status)) {
3306                 DEBUG(10,("deleting centry %s\n", (const char *)kbuf.dptr));
3307                 tdb_delete(the_tdb, kbuf);
3308         }
3309
3310         centry_free(centry);
3311         return 0;
3312 }
3313
3314 /* flush the cache */
3315 static void wcache_flush_cache(void)
3316 {
3317         char *db_path;
3318
3319         if (!wcache)
3320                 return;
3321         if (wcache->tdb) {
3322                 tdb_close(wcache->tdb);
3323                 wcache->tdb = NULL;
3324         }
3325         if (!winbindd_use_cache()) {
3326                 return;
3327         }
3328
3329         db_path = wcache_path();
3330         if (db_path == NULL) {
3331                 return;
3332         }
3333
3334         /* when working offline we must not clear the cache on restart */
3335         wcache->tdb = tdb_open_log(db_path,
3336                                 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
3337                                 TDB_INCOMPATIBLE_HASH |
3338                                 (lp_winbind_offline_logon() ? TDB_DEFAULT : (TDB_DEFAULT | TDB_CLEAR_IF_FIRST)),
3339                                 O_RDWR|O_CREAT, 0600);
3340         TALLOC_FREE(db_path);
3341         if (!wcache->tdb) {
3342                 DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
3343                 return;
3344         }
3345
3346         tdb_traverse(wcache->tdb, traverse_fn_cleanup, NULL);
3347
3348         DEBUG(10,("wcache_flush_cache success\n"));
3349 }
3350
3351 /* Count cached creds */
3352
3353 static int traverse_fn_cached_creds(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf, 
3354                                     void *state)
3355 {
3356         int *cred_count = (int*)state;
3357  
3358         if (strncmp((const char *)kbuf.dptr, "CRED/", 5) == 0) {
3359                 (*cred_count)++;
3360         }
3361         return 0;
3362 }
3363
3364 NTSTATUS wcache_count_cached_creds(struct winbindd_domain *domain, int *count)
3365 {
3366         struct winbind_cache *cache = get_cache(domain);
3367
3368         *count = 0;
3369
3370         if (!cache->tdb) {
3371                 return NT_STATUS_INTERNAL_DB_ERROR;
3372         }
3373  
3374         tdb_traverse(cache->tdb, traverse_fn_cached_creds, (void *)count);
3375
3376         return NT_STATUS_OK;
3377 }
3378
3379 struct cred_list {
3380         struct cred_list *prev, *next;
3381         TDB_DATA key;
3382         fstring name;
3383         time_t created;
3384 };
3385 static struct cred_list *wcache_cred_list;
3386
3387 static int traverse_fn_get_credlist(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf, 
3388                                     void *state)
3389 {
3390         struct cred_list *cred;
3391
3392         if (strncmp((const char *)kbuf.dptr, "CRED/", 5) == 0) {
3393
3394                 cred = SMB_MALLOC_P(struct cred_list);
3395                 if (cred == NULL) {
3396                         DEBUG(0,("traverse_fn_remove_first_creds: failed to malloc new entry for list\n"));
3397                         return -1;
3398                 }
3399
3400                 ZERO_STRUCTP(cred);
3401
3402                 /* save a copy of the key */
3403
3404                 fstrcpy(cred->name, (const char *)kbuf.dptr);           
3405                 DLIST_ADD(wcache_cred_list, cred);
3406         }
3407
3408         return 0;
3409 }
3410
3411 NTSTATUS wcache_remove_oldest_cached_creds(struct winbindd_domain *domain, const struct dom_sid *sid)
3412 {
3413         struct winbind_cache *cache = get_cache(domain);
3414         NTSTATUS status;
3415         int ret;
3416         struct cred_list *cred, *next, *oldest = NULL;
3417
3418         if (!cache->tdb) {
3419                 return NT_STATUS_INTERNAL_DB_ERROR;
3420         }
3421
3422         /* we possibly already have an entry */
3423         if (sid && NT_STATUS_IS_OK(wcache_cached_creds_exist(domain, sid))) {
3424
3425                 fstring key_str, tmp;
3426
3427                 DEBUG(11,("we already have an entry, deleting that\n"));
3428
3429                 fstr_sprintf(key_str, "CRED/%s", sid_to_fstring(tmp, sid));
3430
3431                 tdb_delete(cache->tdb, string_tdb_data(key_str));
3432
3433                 return NT_STATUS_OK;
3434         }
3435
3436         ret = tdb_traverse(cache->tdb, traverse_fn_get_credlist, NULL);
3437         if (ret == 0) {
3438                 return NT_STATUS_OK;
3439         } else if ((ret < 0) || (wcache_cred_list == NULL)) {
3440                 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
3441         }
3442
3443         ZERO_STRUCTP(oldest);
3444
3445         for (cred = wcache_cred_list; cred; cred = cred->next) {
3446
3447                 TDB_DATA data;
3448                 time_t t;
3449
3450                 data = tdb_fetch(cache->tdb, string_tdb_data(cred->name));
3451                 if (!data.dptr) {
3452                         DEBUG(10,("wcache_remove_oldest_cached_creds: entry for [%s] not found\n", 
3453                                 cred->name));
3454                         status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
3455                         goto done;
3456                 }
3457
3458                 t = IVAL(data.dptr, 0);
3459                 SAFE_FREE(data.dptr);
3460
3461                 if (!oldest) {
3462                         oldest = SMB_MALLOC_P(struct cred_list);
3463                         if (oldest == NULL) {
3464                                 status = NT_STATUS_NO_MEMORY;
3465                                 goto done;
3466                         }
3467
3468                         fstrcpy(oldest->name, cred->name);
3469                         oldest->created = t;
3470                         continue;
3471                 }
3472
3473                 if (t < oldest->created) {
3474                         fstrcpy(oldest->name, cred->name);
3475                         oldest->created = t;
3476                 }
3477         }
3478
3479         if (tdb_delete(cache->tdb, string_tdb_data(oldest->name)) == 0) {
3480                 status = NT_STATUS_OK;
3481         } else {
3482                 status = NT_STATUS_UNSUCCESSFUL;
3483         }
3484 done:
3485         for (cred = wcache_cred_list; cred; cred = next) {
3486                 next = cred->next;
3487                 DLIST_REMOVE(wcache_cred_list, cred);
3488                 SAFE_FREE(cred);
3489         }
3490         SAFE_FREE(oldest);
3491
3492         return status;
3493 }
3494
3495 /* Change the global online/offline state. */
3496 bool set_global_winbindd_state_offline(void)
3497 {
3498         TDB_DATA data;
3499
3500         DEBUG(10,("set_global_winbindd_state_offline: offline requested.\n"));
3501
3502         /* Only go offline if someone has created
3503            the key "WINBINDD_OFFLINE" in the cache tdb. */
3504
3505         if (wcache == NULL || wcache->tdb == NULL) {
3506                 DEBUG(10,("set_global_winbindd_state_offline: wcache not open yet.\n"));
3507                 return false;
3508         }
3509
3510         if (!lp_winbind_offline_logon()) {
3511                 DEBUG(10,("set_global_winbindd_state_offline: rejecting.\n"));
3512                 return false;
3513         }
3514
3515         if (global_winbindd_offline_state) {
3516                 /* Already offline. */
3517                 return true;
3518         }
3519
3520         data = tdb_fetch_bystring( wcache->tdb, "WINBINDD_OFFLINE" );
3521
3522         if (!data.dptr || data.dsize != 4) {
3523                 DEBUG(10,("set_global_winbindd_state_offline: offline state not set.\n"));
3524                 SAFE_FREE(data.dptr);
3525                 return false;
3526         } else {
3527                 DEBUG(10,("set_global_winbindd_state_offline: offline state set.\n"));
3528                 global_winbindd_offline_state = true;
3529                 SAFE_FREE(data.dptr);
3530                 return true;
3531         }
3532 }
3533
3534 void set_global_winbindd_state_online(void)
3535 {
3536         DEBUG(10,("set_global_winbindd_state_online: online requested.\n"));
3537
3538         if (!lp_winbind_offline_logon()) {
3539                 DEBUG(10,("set_global_winbindd_state_online: rejecting.\n"));
3540                 return;