2f3bac7587b12ea83f8a8b12458f6b472c7bf3f5
[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(talloc_tos(), "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 (is_domain_offline(domain)) {
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 (is_domain_offline(domain)) {
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
1838         if (domain->online &&
1839             (NT_STATUS_IS_OK(status) || NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED))) {
1840                 enum lsa_SidType save_type = *type;
1841
1842                 if (NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED)) {
1843                         save_type = SID_NAME_UNKNOWN;
1844                 }
1845
1846                 wcache_save_name_to_sid(domain, status, domain_name, name, sid,
1847                                         save_type);
1848
1849                 /* Only save the reverse mapping if this was not a UPN */
1850                 if (!strchr(name, '@')) {
1851                         if (!strupper_m(discard_const_p(char, domain_name))) {
1852                                 return NT_STATUS_INVALID_PARAMETER;
1853                         }
1854                         (void)strlower_m(discard_const_p(char, name));
1855                         wcache_save_sid_to_name(domain, status, sid,
1856                                                 domain_name, name, save_type);
1857                 }
1858         }
1859
1860         return status;
1861 }
1862
1863 struct wcache_sid_to_name_state {
1864         TALLOC_CTX *mem_ctx;
1865         char **domain_name;
1866         char **name;
1867         enum lsa_SidType *type;
1868         bool offline;
1869         bool found;
1870 };
1871
1872 static void wcache_sid_to_name_fn(const char *domain, const char *name,
1873                                   enum lsa_SidType type, time_t timeout,
1874                                   void *private_data)
1875 {
1876         struct wcache_sid_to_name_state *state = private_data;
1877
1878         *state->domain_name = talloc_strdup(state->mem_ctx, domain);
1879         if (*state->domain_name == NULL) {
1880                 return;
1881         }
1882         *state->name = talloc_strdup(state->mem_ctx, name);
1883         if (*state->name == NULL) {
1884                 return;
1885         }
1886         *state->type = type;
1887         state->found = (state->offline || (timeout < time(NULL)));
1888 }
1889
1890 static NTSTATUS wcache_sid_to_name(struct winbindd_domain *domain,
1891                                    const struct dom_sid *sid,
1892                                    TALLOC_CTX *mem_ctx,
1893                                    char **domain_name,
1894                                    char **name,
1895                                    enum lsa_SidType *type)
1896 {
1897         struct wcache_sid_to_name_state state = {
1898                 .mem_ctx = mem_ctx, .found = false,
1899                 .domain_name = domain_name, .name = name, .type = type,
1900                 .offline = is_domain_offline(domain)
1901         };
1902         bool ok;
1903
1904         ok = namemap_cache_find_sid(sid, wcache_sid_to_name_fn, &state);
1905         if (!ok) {
1906                 DBG_DEBUG("namemap_cache_find_name failed\n");
1907                 return NT_STATUS_NOT_FOUND;
1908         }
1909         if (!state.found) {
1910                 DBG_DEBUG("cache entry not found\n");
1911                 return NT_STATUS_NOT_FOUND;
1912         }
1913         if (*type == SID_NAME_UNKNOWN) {
1914                 return NT_STATUS_NONE_MAPPED;
1915         }
1916
1917         return NT_STATUS_OK;
1918 }
1919
1920 /* convert a sid to a user or group name. The sid is guaranteed to be in the domain
1921    given */
1922 NTSTATUS wb_cache_sid_to_name(struct winbindd_domain *domain,
1923                               TALLOC_CTX *mem_ctx,
1924                               const struct dom_sid *sid,
1925                               char **domain_name,
1926                               char **name,
1927                               enum lsa_SidType *type)
1928 {
1929         NTSTATUS status;
1930         bool old_status;
1931
1932         old_status = domain->online;
1933         status = wcache_sid_to_name(domain, sid, mem_ctx, domain_name, name,
1934                                     type);
1935         if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
1936                 return status;
1937         }
1938
1939         *name = NULL;
1940         *domain_name = NULL;
1941
1942         /* If the seq number check indicated that there is a problem
1943          * with this DC, then return that status... except for
1944          * access_denied.  This is special because the dc may be in
1945          * "restrict anonymous = 1" mode, in which case it will deny
1946          * most unauthenticated operations, but *will* allow the LSA
1947          * sid-to-name that we try as a fallback. */
1948
1949         if (!(NT_STATUS_IS_OK(domain->last_status)
1950               || NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)))
1951                 return domain->last_status;
1952
1953         DEBUG(10,("sid_to_name: [Cached] - doing backend query for name for domain %s\n",
1954                 domain->name ));
1955
1956         winbindd_domain_init_backend(domain);
1957
1958         status = domain->backend->sid_to_name(domain, mem_ctx, sid, domain_name, name, type);
1959
1960         if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1961                 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1962                 if (!domain->internal && old_status) {
1963                         set_domain_offline(domain);
1964                 }
1965                 if (!domain->internal &&
1966                         !domain->online &&
1967                         old_status) {
1968                         NTSTATUS cache_status;
1969                         cache_status = wcache_sid_to_name(domain, sid, mem_ctx,
1970                                                         domain_name, name, type);
1971                         return cache_status;
1972                 }
1973         }
1974         /* and save it */
1975         if (!NT_STATUS_IS_OK(status)) {
1976                 return status;
1977         }
1978         wcache_save_sid_to_name(domain, status, sid, *domain_name, *name, *type);
1979
1980         /* We can't save the name to sid mapping here, as with sid history a
1981          * later name2sid would give the wrong sid. */
1982
1983         return status;
1984 }
1985
1986 NTSTATUS wb_cache_rids_to_names(struct winbindd_domain *domain,
1987                                 TALLOC_CTX *mem_ctx,
1988                                 const struct dom_sid *domain_sid,
1989                                 uint32_t *rids,
1990                                 size_t num_rids,
1991                                 char **domain_name,
1992                                 char ***names,
1993                                 enum lsa_SidType **types)
1994 {
1995         struct winbind_cache *cache = get_cache(domain);
1996         size_t i;
1997         NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
1998         bool have_mapped;
1999         bool have_unmapped;
2000         bool old_status;
2001
2002         old_status = domain->online;
2003         *domain_name = NULL;
2004         *names = NULL;
2005         *types = NULL;
2006
2007         if (!cache->tdb) {
2008                 goto do_query;
2009         }
2010
2011         if (num_rids == 0) {
2012                 return NT_STATUS_OK;
2013         }
2014
2015         *names = talloc_array(mem_ctx, char *, num_rids);
2016         *types = talloc_array(mem_ctx, enum lsa_SidType, num_rids);
2017
2018         if ((*names == NULL) || (*types == NULL)) {
2019                 result = NT_STATUS_NO_MEMORY;
2020                 goto error;
2021         }
2022
2023         have_mapped = have_unmapped = false;
2024
2025         for (i=0; i<num_rids; i++) {
2026                 struct dom_sid sid;
2027                 NTSTATUS status;
2028                 enum lsa_SidType type;
2029                 char *dom, *name;
2030
2031                 if (!sid_compose(&sid, domain_sid, rids[i])) {
2032                         result = NT_STATUS_INTERNAL_ERROR;
2033                         goto error;
2034                 }
2035
2036                 status = wcache_sid_to_name(domain, &sid, *names, &dom,
2037                                             &name, &type);
2038
2039                 (*types)[i] = SID_NAME_UNKNOWN;
2040                 (*names)[i] = talloc_strdup(*names, "");
2041
2042                 if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2043                         /* not cached */
2044                         goto do_query;
2045                 }
2046
2047                 if (NT_STATUS_IS_OK(status)) {
2048                         have_mapped = true;
2049                         (*types)[i] = type;
2050
2051                         if (*domain_name == NULL) {
2052                                 *domain_name = dom;
2053                         } else {
2054                                 TALLOC_FREE(dom);
2055                         }
2056
2057                         (*names)[i] = name;
2058
2059                 } else if (NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED)) {
2060                         have_unmapped = true;
2061                 } else {
2062                         /* something's definitely wrong */
2063                         result = status;
2064                         goto error;
2065                 }
2066         }
2067
2068         if (!have_mapped) {
2069                 return NT_STATUS_NONE_MAPPED;
2070         }
2071         if (!have_unmapped) {
2072                 return NT_STATUS_OK;
2073         }
2074         return STATUS_SOME_UNMAPPED;
2075
2076  do_query:
2077
2078         TALLOC_FREE(*names);
2079         TALLOC_FREE(*types);
2080
2081         result = domain->backend->rids_to_names(domain, mem_ctx, domain_sid,
2082                                                 rids, num_rids, domain_name,
2083                                                 names, types);
2084
2085         if (NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
2086             NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2087                 if (!domain->internal && old_status) {
2088                         set_domain_offline(domain);
2089                 }
2090                 if (cache->tdb &&
2091                         !domain->internal &&
2092                         !domain->online &&
2093                         old_status) {
2094                         have_mapped = have_unmapped = false;
2095
2096                         *names = talloc_array(mem_ctx, char *, num_rids);
2097                         if (*names == NULL) {
2098                                 result = NT_STATUS_NO_MEMORY;
2099                                 goto error;
2100                         }
2101
2102                         *types = talloc_array(mem_ctx, enum lsa_SidType,
2103                                               num_rids);
2104                         if (*types == NULL) {
2105                                 result = NT_STATUS_NO_MEMORY;
2106                                 goto error;
2107                         }
2108
2109                         for (i=0; i<num_rids; i++) {
2110                                 struct dom_sid sid;
2111                                 NTSTATUS status;
2112                                 enum lsa_SidType type;
2113                                 char *dom, *name;
2114
2115                                 if (!sid_compose(&sid, domain_sid, rids[i])) {
2116                                         result = NT_STATUS_INTERNAL_ERROR;
2117                                         goto error;
2118                                 }
2119
2120                                 status = wcache_sid_to_name(domain, &sid,
2121                                                             *names, &dom,
2122                                                             &name, &type);
2123
2124                                 (*types)[i] = SID_NAME_UNKNOWN;
2125                                 (*names)[i] = talloc_strdup(*names, "");
2126
2127                                 if (NT_STATUS_IS_OK(status)) {
2128                                         have_mapped = true;
2129                                         (*types)[i] = type;
2130
2131                                         if (*domain_name == NULL) {
2132                                                 *domain_name = dom;
2133                                         } else {
2134                                                 TALLOC_FREE(dom);
2135                                         }
2136
2137                                         (*names)[i] = name;
2138
2139                                 } else if (NT_STATUS_EQUAL(
2140                                                    status,
2141                                                    NT_STATUS_NONE_MAPPED)) {
2142                                         have_unmapped = true;
2143                                 } else {
2144                                         /* something's definitely wrong */
2145                                         result = status;
2146                                         goto error;
2147                                 }
2148                         }
2149
2150                         if (!have_mapped) {
2151                                 return NT_STATUS_NONE_MAPPED;
2152                         }
2153                         if (!have_unmapped) {
2154                                 return NT_STATUS_OK;
2155                         }
2156                         return STATUS_SOME_UNMAPPED;
2157                 }
2158         }
2159         /*
2160           None of the queried rids has been found so save all negative entries
2161         */
2162         if (NT_STATUS_EQUAL(result, NT_STATUS_NONE_MAPPED)) {
2163                 for (i = 0; i < num_rids; i++) {
2164                         struct dom_sid sid;
2165                         const char *name = "";
2166                         const enum lsa_SidType type = SID_NAME_UNKNOWN;
2167                         NTSTATUS status = NT_STATUS_NONE_MAPPED;
2168
2169                         if (!sid_compose(&sid, domain_sid, rids[i])) {
2170                                 return NT_STATUS_INTERNAL_ERROR;
2171                         }
2172
2173                         wcache_save_sid_to_name(domain, status, &sid, *domain_name,
2174                                                 name, type);
2175                 }
2176
2177                 return result;
2178         }
2179
2180         /*
2181           Some or all of the queried rids have been found.
2182         */
2183         if (!NT_STATUS_IS_OK(result) &&
2184             !NT_STATUS_EQUAL(result, STATUS_SOME_UNMAPPED)) {
2185                 return result;
2186         }
2187
2188         refresh_sequence_number(domain);
2189
2190         for (i=0; i<num_rids; i++) {
2191                 struct dom_sid sid;
2192                 NTSTATUS status;
2193
2194                 if (!sid_compose(&sid, domain_sid, rids[i])) {
2195                         result = NT_STATUS_INTERNAL_ERROR;
2196                         goto error;
2197                 }
2198
2199                 status = (*types)[i] == SID_NAME_UNKNOWN ?
2200                         NT_STATUS_NONE_MAPPED : NT_STATUS_OK;
2201
2202                 wcache_save_sid_to_name(domain, status, &sid, *domain_name,
2203                                         (*names)[i], (*types)[i]);
2204         }
2205
2206         return result;
2207
2208  error:
2209         TALLOC_FREE(*names);
2210         TALLOC_FREE(*types);
2211         return result;
2212 }
2213
2214 static NTSTATUS wcache_query_user(struct winbindd_domain *domain,
2215                                   TALLOC_CTX *mem_ctx,
2216                                   const struct dom_sid *user_sid,
2217                                   struct wbint_userinfo *info)
2218 {
2219         struct winbind_cache *cache = get_cache(domain);
2220         struct cache_entry *centry = NULL;
2221         NTSTATUS status;
2222         char *sid_string;
2223
2224         if (cache->tdb == NULL) {
2225                 return NT_STATUS_NOT_FOUND;
2226         }
2227
2228         sid_string = sid_string_tos(user_sid);
2229         if (sid_string == NULL) {
2230                 return NT_STATUS_NO_MEMORY;
2231         }
2232
2233         centry = wcache_fetch(cache, domain, "U/%s", sid_string);
2234         TALLOC_FREE(sid_string);
2235         if (centry == NULL) {
2236                 return NT_STATUS_NOT_FOUND;
2237         }
2238
2239         /*
2240          * If we have an access denied cache entry and a cached info3
2241          * in the samlogon cache then do a query.  This will force the
2242          * rpc back end to return the info3 data.
2243          */
2244
2245         if (NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED) &&
2246             netsamlogon_cache_have(user_sid)) {
2247                 DEBUG(10, ("query_user: cached access denied and have cached "
2248                            "info3\n"));
2249                 domain->last_status = NT_STATUS_OK;
2250                 centry_free(centry);
2251                 return NT_STATUS_NOT_FOUND;
2252         }
2253
2254         /* if status is not ok then this is a negative hit
2255            and the rest of the data doesn't matter */
2256         status = centry->status;
2257         if (NT_STATUS_IS_OK(status)) {
2258                 info->domain_name = centry_string(centry, mem_ctx);
2259                 info->acct_name = centry_string(centry, mem_ctx);
2260                 info->full_name = centry_string(centry, mem_ctx);
2261                 info->homedir = centry_string(centry, mem_ctx);
2262                 info->shell = centry_string(centry, mem_ctx);
2263                 info->uid = centry_uint32(centry);
2264                 info->primary_gid = centry_uint32(centry);
2265                 info->primary_group_name = centry_string(centry, mem_ctx);
2266                 centry_sid(centry, &info->user_sid);
2267                 centry_sid(centry, &info->group_sid);
2268         }
2269
2270         DEBUG(10,("query_user: [Cached] - cached info for domain %s status: "
2271                   "%s\n", domain->name, nt_errstr(status) ));
2272
2273         centry_free(centry);
2274         return status;
2275 }
2276
2277
2278 /**
2279 * @brief Query a fullname from the username cache (for further gecos processing)
2280 *
2281 * @param domain         A pointer to the winbindd_domain struct.
2282 * @param mem_ctx        The talloc context.
2283 * @param user_sid       The user sid.
2284 * @param full_name      A pointer to the full_name string.
2285 *
2286 * @return NTSTATUS code
2287 */
2288 NTSTATUS wcache_query_user_fullname(struct winbindd_domain *domain,
2289                                     TALLOC_CTX *mem_ctx,
2290                                     const struct dom_sid *user_sid,
2291                                     const char **full_name)
2292 {
2293         NTSTATUS status;
2294         struct wbint_userinfo info;
2295
2296         status = wcache_query_user(domain, mem_ctx, user_sid, &info);
2297         if (!NT_STATUS_IS_OK(status)) {
2298                 return status;
2299         }
2300
2301         if (info.full_name != NULL) {
2302                 *full_name = talloc_strdup(mem_ctx, info.full_name);
2303                 if (*full_name == NULL) {
2304                         return NT_STATUS_NO_MEMORY;
2305                 }
2306         }
2307
2308         return NT_STATUS_OK;
2309 }
2310
2311 static NTSTATUS wcache_lookup_usergroups(struct winbindd_domain *domain,
2312                                          TALLOC_CTX *mem_ctx,
2313                                          const struct dom_sid *user_sid,
2314                                          uint32_t *pnum_sids,
2315                                          struct dom_sid **psids)
2316 {
2317         struct winbind_cache *cache = get_cache(domain);
2318         struct cache_entry *centry = NULL;
2319         NTSTATUS status;
2320         uint32_t i, num_sids;
2321         struct dom_sid *sids;
2322         fstring sid_string;
2323
2324         if (cache->tdb == NULL) {
2325                 return NT_STATUS_NOT_FOUND;
2326         }
2327
2328         centry = wcache_fetch(cache, domain, "UG/%s",
2329                               sid_to_fstring(sid_string, user_sid));
2330         if (centry == NULL) {
2331                 return NT_STATUS_NOT_FOUND;
2332         }
2333
2334         /* If we have an access denied cache entry and a cached info3 in the
2335            samlogon cache then do a query.  This will force the rpc back end
2336            to return the info3 data. */
2337
2338         if (NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)
2339             && netsamlogon_cache_have(user_sid)) {
2340                 DEBUG(10, ("lookup_usergroups: cached access denied and have "
2341                            "cached info3\n"));
2342                 domain->last_status = NT_STATUS_OK;
2343                 centry_free(centry);
2344                 return NT_STATUS_NOT_FOUND;
2345         }
2346
2347         num_sids = centry_uint32(centry);
2348         sids = talloc_array(mem_ctx, struct dom_sid, num_sids);
2349         if (sids == NULL) {
2350                 centry_free(centry);
2351                 return NT_STATUS_NO_MEMORY;
2352         }
2353
2354         for (i=0; i<num_sids; i++) {
2355                 centry_sid(centry, &sids[i]);
2356         }
2357
2358         status = centry->status;
2359
2360         DEBUG(10,("lookup_usergroups: [Cached] - cached info for domain %s "
2361                   "status: %s\n", domain->name, nt_errstr(status)));
2362
2363         centry_free(centry);
2364
2365         *pnum_sids = num_sids;
2366         *psids = sids;
2367         return status;
2368 }
2369
2370 /* Lookup groups a user is a member of. */
2371 NTSTATUS wb_cache_lookup_usergroups(struct winbindd_domain *domain,
2372                                     TALLOC_CTX *mem_ctx,
2373                                     const struct dom_sid *user_sid,
2374                                     uint32_t *num_groups,
2375                                     struct dom_sid **user_gids)
2376 {
2377         struct cache_entry *centry = NULL;
2378         NTSTATUS status;
2379         unsigned int i;
2380         fstring sid_string;
2381         bool old_status;
2382
2383         old_status = domain->online;
2384         status = wcache_lookup_usergroups(domain, mem_ctx, user_sid,
2385                                           num_groups, user_gids);
2386         if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2387                 return status;
2388         }
2389
2390         (*num_groups) = 0;
2391         (*user_gids) = NULL;
2392
2393         /* Return status value returned by seq number check */
2394
2395         if (!NT_STATUS_IS_OK(domain->last_status))
2396                 return domain->last_status;
2397
2398         DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info for domain %s\n",
2399                 domain->name ));
2400
2401         status = domain->backend->lookup_usergroups(domain, mem_ctx, user_sid, num_groups, user_gids);
2402
2403         if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2404                 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2405                 if (!domain->internal && old_status) {
2406                         set_domain_offline(domain);
2407                 }
2408                 if (!domain->internal &&
2409                         !domain->online &&
2410                         old_status) {
2411                         NTSTATUS cache_status;
2412                         cache_status = wcache_lookup_usergroups(domain, mem_ctx, user_sid,
2413                                                           num_groups, user_gids);
2414                         return cache_status;
2415                 }
2416         }
2417         if ( NT_STATUS_EQUAL(status, NT_STATUS_SYNCHRONIZATION_REQUIRED) )
2418                 goto skip_save;
2419
2420         /* and save it */
2421         refresh_sequence_number(domain);
2422         if (!NT_STATUS_IS_OK(status)) {
2423                 return status;
2424         }
2425         centry = centry_start(domain, status);
2426         if (!centry)
2427                 goto skip_save;
2428
2429         centry_put_uint32(centry, *num_groups);
2430         for (i=0; i<(*num_groups); i++) {
2431                 centry_put_sid(centry, &(*user_gids)[i]);
2432         }       
2433
2434         centry_end(centry, "UG/%s", sid_to_fstring(sid_string, user_sid));
2435         centry_free(centry);
2436
2437 skip_save:
2438         return status;
2439 }
2440
2441 static char *wcache_make_sidlist(TALLOC_CTX *mem_ctx, uint32_t num_sids,
2442                                  const struct dom_sid *sids)
2443 {
2444         uint32_t i;
2445         char *sidlist;
2446
2447         sidlist = talloc_strdup(mem_ctx, "");
2448         if (sidlist == NULL) {
2449                 return NULL;
2450         }
2451         for (i=0; i<num_sids; i++) {
2452                 fstring tmp;
2453                 sidlist = talloc_asprintf_append_buffer(
2454                         sidlist, "/%s", sid_to_fstring(tmp, &sids[i]));
2455                 if (sidlist == NULL) {
2456                         return NULL;
2457                 }
2458         }
2459         return sidlist;
2460 }
2461
2462 static NTSTATUS wcache_lookup_useraliases(struct winbindd_domain *domain,
2463                                           TALLOC_CTX *mem_ctx,
2464                                           uint32_t num_sids,
2465                                           const struct dom_sid *sids,
2466                                           uint32_t *pnum_aliases,
2467                                           uint32_t **paliases)
2468 {
2469         struct winbind_cache *cache = get_cache(domain);
2470         struct cache_entry *centry = NULL;
2471         uint32_t i, num_aliases;
2472         uint32_t *aliases;
2473         NTSTATUS status;
2474         char *sidlist;
2475
2476         if (cache->tdb == NULL) {
2477                 return NT_STATUS_NOT_FOUND;
2478         }
2479
2480         if (num_sids == 0) {
2481                 *pnum_aliases = 0;
2482                 *paliases = NULL;
2483                 return NT_STATUS_OK;
2484         }
2485
2486         /* We need to cache indexed by the whole list of SIDs, the aliases
2487          * resulting might come from any of the SIDs. */
2488
2489         sidlist = wcache_make_sidlist(talloc_tos(), num_sids, sids);
2490         if (sidlist == NULL) {
2491                 return NT_STATUS_NO_MEMORY;
2492         }
2493
2494         centry = wcache_fetch(cache, domain, "UA%s", sidlist);
2495         TALLOC_FREE(sidlist);
2496         if (centry == NULL) {
2497                 return NT_STATUS_NOT_FOUND;
2498         }
2499
2500         num_aliases = centry_uint32(centry);
2501         aliases = talloc_array(mem_ctx, uint32_t, num_aliases);
2502         if (aliases == NULL) {
2503                 centry_free(centry);
2504                 return NT_STATUS_NO_MEMORY;
2505         }
2506
2507         for (i=0; i<num_aliases; i++) {
2508                 aliases[i] = centry_uint32(centry);
2509         }
2510
2511         status = centry->status;
2512
2513         DEBUG(10,("lookup_useraliases: [Cached] - cached info for domain: %s "
2514                   "status %s\n", domain->name, nt_errstr(status)));
2515
2516         centry_free(centry);
2517
2518         *pnum_aliases = num_aliases;
2519         *paliases = aliases;
2520
2521         return status;
2522 }
2523
2524 NTSTATUS wb_cache_lookup_useraliases(struct winbindd_domain *domain,
2525                                      TALLOC_CTX *mem_ctx,
2526                                      uint32_t num_sids,
2527                                      const struct dom_sid *sids,
2528                                      uint32_t *num_aliases,
2529                                      uint32_t **alias_rids)
2530 {
2531         struct cache_entry *centry = NULL;
2532         NTSTATUS status;
2533         char *sidlist;
2534         uint32_t i;
2535         bool old_status;
2536
2537         old_status = domain->online;
2538         status = wcache_lookup_useraliases(domain, mem_ctx, num_sids, sids,
2539                                            num_aliases, alias_rids);
2540         if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2541                 return status;
2542         }
2543
2544         (*num_aliases) = 0;
2545         (*alias_rids) = NULL;
2546
2547         if (!NT_STATUS_IS_OK(domain->last_status))
2548                 return domain->last_status;
2549
2550         DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info "
2551                   "for domain %s\n", domain->name ));
2552
2553         sidlist = wcache_make_sidlist(talloc_tos(), num_sids, sids);
2554         if (sidlist == NULL) {
2555                 return NT_STATUS_NO_MEMORY;
2556         }
2557
2558         status = domain->backend->lookup_useraliases(domain, mem_ctx,
2559                                                      num_sids, sids,
2560                                                      num_aliases, alias_rids);
2561
2562         if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2563                 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2564                 if (!domain->internal && old_status) {
2565                         set_domain_offline(domain);
2566                 }
2567                 if (!domain->internal &&
2568                         !domain->online &&
2569                         old_status) {
2570                         NTSTATUS cache_status;
2571                         cache_status = wcache_lookup_useraliases(domain, mem_ctx, num_sids,
2572                                                                  sids, num_aliases, alias_rids);
2573                         return cache_status;
2574                 }
2575         }
2576         /* and save it */
2577         refresh_sequence_number(domain);
2578         if (!NT_STATUS_IS_OK(status)) {
2579                 return status;
2580         }
2581         centry = centry_start(domain, status);
2582         if (!centry)
2583                 goto skip_save;
2584         centry_put_uint32(centry, *num_aliases);
2585         for (i=0; i<(*num_aliases); i++)
2586                 centry_put_uint32(centry, (*alias_rids)[i]);
2587         centry_end(centry, "UA%s", sidlist);
2588         centry_free(centry);
2589
2590  skip_save:
2591         return status;
2592 }
2593
2594 static NTSTATUS wcache_lookup_groupmem(struct winbindd_domain *domain,
2595                                        TALLOC_CTX *mem_ctx,
2596                                        const struct dom_sid *group_sid,
2597                                        uint32_t *num_names,
2598                                        struct dom_sid **sid_mem, char ***names,
2599                                        uint32_t **name_types)
2600 {
2601         struct winbind_cache *cache = get_cache(domain);
2602         struct cache_entry *centry = NULL;
2603         NTSTATUS status;
2604         unsigned int i;
2605         char *sid_string;
2606
2607         if (cache->tdb == NULL) {
2608                 return NT_STATUS_NOT_FOUND;
2609         }
2610
2611         sid_string = sid_string_tos(group_sid);
2612         if (sid_string == NULL) {
2613                 return NT_STATUS_NO_MEMORY;
2614         }
2615
2616         centry = wcache_fetch(cache, domain, "GM/%s", sid_string);
2617         TALLOC_FREE(sid_string);
2618         if (centry == NULL) {
2619                 return NT_STATUS_NOT_FOUND;
2620         }
2621
2622         *sid_mem = NULL;
2623         *names = NULL;
2624         *name_types = NULL;
2625
2626         *num_names = centry_uint32(centry);
2627         if (*num_names == 0) {
2628                 centry_free(centry);
2629                 return NT_STATUS_OK;
2630         }
2631
2632         *sid_mem = talloc_array(mem_ctx, struct dom_sid, *num_names);
2633         *names = talloc_array(mem_ctx, char *, *num_names);
2634         *name_types = talloc_array(mem_ctx, uint32_t, *num_names);
2635
2636         if ((*sid_mem == NULL) || (*names == NULL) || (*name_types == NULL)) {
2637                 TALLOC_FREE(*sid_mem);
2638                 TALLOC_FREE(*names);
2639                 TALLOC_FREE(*name_types);
2640                 centry_free(centry);
2641                 return NT_STATUS_NO_MEMORY;
2642         }
2643
2644         for (i=0; i<(*num_names); i++) {
2645                 centry_sid(centry, &(*sid_mem)[i]);
2646                 (*names)[i] = centry_string(centry, mem_ctx);
2647                 (*name_types)[i] = centry_uint32(centry);
2648         }
2649
2650         status = centry->status;
2651
2652         DEBUG(10,("lookup_groupmem: [Cached] - cached info for domain %s "
2653                   "status: %s\n", domain->name, nt_errstr(status)));
2654
2655         centry_free(centry);
2656         return status;
2657 }
2658
2659 NTSTATUS wb_cache_lookup_groupmem(struct winbindd_domain *domain,
2660                                   TALLOC_CTX *mem_ctx,
2661                                   const struct dom_sid *group_sid,
2662                                   enum lsa_SidType type,
2663                                   uint32_t *num_names,
2664                                   struct dom_sid **sid_mem,
2665                                   char ***names,
2666                                   uint32_t **name_types)
2667 {
2668         struct cache_entry *centry = NULL;
2669         NTSTATUS status;
2670         unsigned int i;
2671         fstring sid_string;
2672         bool old_status;
2673
2674         old_status = domain->online;
2675         status = wcache_lookup_groupmem(domain, mem_ctx, group_sid, num_names,
2676                                         sid_mem, names, name_types);
2677         if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2678                 return status;
2679         }
2680
2681         (*num_names) = 0;
2682         (*sid_mem) = NULL;
2683         (*names) = NULL;
2684         (*name_types) = NULL;
2685
2686         /* Return status value returned by seq number check */
2687
2688         if (!NT_STATUS_IS_OK(domain->last_status))
2689                 return domain->last_status;
2690
2691         DEBUG(10,("lookup_groupmem: [Cached] - doing backend query for info for domain %s\n",
2692                 domain->name ));
2693
2694         status = domain->backend->lookup_groupmem(domain, mem_ctx, group_sid,
2695                                                   type, num_names,
2696                                                   sid_mem, names, name_types);
2697
2698         if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2699                 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2700                 if (!domain->internal && old_status) {
2701                         set_domain_offline(domain);
2702                 }
2703                 if (!domain->internal &&
2704                         !domain->online &&
2705                         old_status) {
2706                         NTSTATUS cache_status;
2707                         cache_status = wcache_lookup_groupmem(domain, mem_ctx, group_sid,
2708                                                               num_names, sid_mem, names,
2709                                                               name_types);
2710                         return cache_status;
2711                 }
2712         }
2713         /* and save it */
2714         refresh_sequence_number(domain);
2715         if (!NT_STATUS_IS_OK(status)) {
2716                 return status;
2717         }
2718         centry = centry_start(domain, status);
2719         if (!centry)
2720                 goto skip_save;
2721         centry_put_uint32(centry, *num_names);
2722         for (i=0; i<(*num_names); i++) {
2723                 centry_put_sid(centry, &(*sid_mem)[i]);
2724                 centry_put_string(centry, (*names)[i]);
2725                 centry_put_uint32(centry, (*name_types)[i]);
2726         }       
2727         centry_end(centry, "GM/%s", sid_to_fstring(sid_string, group_sid));
2728         centry_free(centry);
2729
2730 skip_save:
2731         return status;
2732 }
2733
2734 /* find the sequence number for a domain */
2735 NTSTATUS wb_cache_sequence_number(struct winbindd_domain *domain,
2736                                   uint32_t *seq)
2737 {
2738         refresh_sequence_number(domain);
2739
2740         *seq = domain->sequence_number;
2741
2742         return NT_STATUS_OK;
2743 }
2744
2745 /* enumerate trusted domains 
2746  * (we need to have the list of trustdoms in the cache when we go offline) -
2747  * Guenther */
2748 NTSTATUS wb_cache_trusted_domains(struct winbindd_domain *domain,
2749                                   TALLOC_CTX *mem_ctx,
2750                                   struct netr_DomainTrustList *trusts)
2751 {
2752         NTSTATUS status;
2753         struct winbind_cache *cache;
2754         struct winbindd_tdc_domain *dom_list = NULL;
2755         size_t num_domains = 0;
2756         bool retval = false;
2757         size_t i;
2758         bool old_status;
2759
2760         old_status = domain->online;
2761         trusts->count = 0;
2762         trusts->array = NULL;
2763
2764         cache = get_cache(domain);
2765         if (!cache || !cache->tdb) {
2766                 goto do_query;
2767         }
2768
2769         if (domain->online) {
2770                 goto do_query;
2771         }
2772
2773         retval = wcache_tdc_fetch_list(&dom_list, &num_domains);
2774         if (!retval || !num_domains || !dom_list) {
2775                 TALLOC_FREE(dom_list);
2776                 goto do_query;
2777         }
2778
2779 do_fetch_cache:
2780         trusts->array = talloc_zero_array(mem_ctx, struct netr_DomainTrust, num_domains);
2781         if (!trusts->array) {
2782                 TALLOC_FREE(dom_list);
2783                 return NT_STATUS_NO_MEMORY;
2784         }
2785
2786         for (i = 0; i < num_domains; i++) {
2787                 struct netr_DomainTrust *trust;
2788                 struct dom_sid *sid;
2789                 struct winbindd_domain *dom;
2790
2791                 dom = find_domain_from_name_noinit(dom_list[i].domain_name);
2792                 if (dom && dom->internal) {
2793                         continue;
2794                 }
2795
2796                 trust = &trusts->array[trusts->count];
2797                 trust->netbios_name = talloc_strdup(trusts->array, dom_list[i].domain_name);
2798                 trust->dns_name = talloc_strdup(trusts->array, dom_list[i].dns_name);
2799                 sid = talloc(trusts->array, struct dom_sid);
2800                 if (!trust->netbios_name || !trust->dns_name ||
2801                         !sid) {
2802                         TALLOC_FREE(dom_list);
2803                         TALLOC_FREE(trusts->array);
2804                         return NT_STATUS_NO_MEMORY;
2805                 }
2806
2807                 trust->trust_flags = dom_list[i].trust_flags;
2808                 trust->trust_attributes = dom_list[i].trust_attribs;
2809                 trust->trust_type = dom_list[i].trust_type;
2810                 sid_copy(sid, &dom_list[i].sid);
2811                 trust->sid = sid;
2812                 trusts->count++;
2813         }
2814
2815         TALLOC_FREE(dom_list);
2816         return NT_STATUS_OK;
2817
2818 do_query:
2819         /* Return status value returned by seq number check */
2820
2821         if (!NT_STATUS_IS_OK(domain->last_status))
2822                 return domain->last_status;
2823
2824         DEBUG(10,("trusted_domains: [Cached] - doing backend query for info for domain %s\n",
2825                 domain->name ));
2826
2827         status = domain->backend->trusted_domains(domain, mem_ctx, trusts);
2828
2829         if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2830                 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2831                 if (!domain->internal && old_status) {
2832                         set_domain_offline(domain);
2833                 }
2834                 if (!domain->internal &&
2835                         !domain->online &&
2836                         old_status) {
2837                         retval = wcache_tdc_fetch_list(&dom_list, &num_domains);
2838                         if (retval && num_domains && dom_list) {
2839                                 TALLOC_FREE(trusts->array);
2840                                 trusts->count = 0;
2841                                 goto do_fetch_cache;
2842                         }
2843                 }
2844         }
2845         /* no trusts gives NT_STATUS_NO_MORE_ENTRIES resetting to NT_STATUS_OK
2846          * so that the generic centry handling still applies correctly -
2847          * Guenther*/
2848
2849         if (!NT_STATUS_IS_ERR(status)) {
2850                 status = NT_STATUS_OK;
2851         }
2852         return status;
2853 }       
2854
2855 /* get lockout policy */
2856 NTSTATUS wb_cache_lockout_policy(struct winbindd_domain *domain,
2857                                  TALLOC_CTX *mem_ctx,
2858                                  struct samr_DomInfo12 *policy)
2859 {
2860         struct winbind_cache *cache = get_cache(domain);
2861         struct cache_entry *centry = NULL;
2862         NTSTATUS status;
2863         bool old_status;
2864
2865         old_status = domain->online;
2866         if (!cache->tdb)
2867                 goto do_query;
2868
2869         centry = wcache_fetch(cache, domain, "LOC_POL/%s", domain->name);
2870
2871         if (!centry)
2872                 goto do_query;
2873
2874 do_fetch_cache:
2875         policy->lockout_duration = centry_nttime(centry);
2876         policy->lockout_window = centry_nttime(centry);
2877         policy->lockout_threshold = centry_uint16(centry);
2878
2879         status = centry->status;
2880
2881         DEBUG(10,("lockout_policy: [Cached] - cached info for domain %s status: %s\n",
2882                 domain->name, nt_errstr(status) ));
2883
2884         centry_free(centry);
2885         return status;
2886
2887 do_query:
2888         ZERO_STRUCTP(policy);
2889
2890         /* Return status value returned by seq number check */
2891
2892         if (!NT_STATUS_IS_OK(domain->last_status))
2893                 return domain->last_status;
2894
2895         DEBUG(10,("lockout_policy: [Cached] - doing backend query for info for domain %s\n",
2896                 domain->name ));
2897
2898         status = domain->backend->lockout_policy(domain, mem_ctx, policy);
2899
2900         if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2901                 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2902                 if (!domain->internal && old_status) {
2903                         set_domain_offline(domain);
2904                 }
2905                 if (cache->tdb &&
2906                         !domain->internal &&
2907                         !domain->online &&
2908                         old_status) {
2909                         centry = wcache_fetch(cache, domain, "LOC_POL/%s", domain->name);
2910                         if (centry) {
2911                                 goto do_fetch_cache;
2912                         }
2913                 }
2914         }
2915         /* and save it */
2916         refresh_sequence_number(domain);
2917         if (!NT_STATUS_IS_OK(status)) {
2918                 return status;
2919         }
2920         wcache_save_lockout_policy(domain, status, policy);
2921
2922         return status;
2923 }
2924
2925 /* get password policy */
2926 NTSTATUS wb_cache_password_policy(struct winbindd_domain *domain,
2927                                   TALLOC_CTX *mem_ctx,
2928                                   struct samr_DomInfo1 *policy)
2929 {
2930         struct winbind_cache *cache = get_cache(domain);
2931         struct cache_entry *centry = NULL;
2932         NTSTATUS status;
2933         bool old_status;
2934
2935         old_status = domain->online;
2936         if (!cache->tdb)
2937                 goto do_query;
2938
2939         centry = wcache_fetch(cache, domain, "PWD_POL/%s", domain->name);
2940
2941         if (!centry)
2942                 goto do_query;
2943
2944 do_fetch_cache:
2945         policy->min_password_length = centry_uint16(centry);
2946         policy->password_history_length = centry_uint16(centry);
2947         policy->password_properties = centry_uint32(centry);
2948         policy->max_password_age = centry_nttime(centry);
2949         policy->min_password_age = centry_nttime(centry);
2950
2951         status = centry->status;
2952
2953         DEBUG(10,("lockout_policy: [Cached] - cached info for domain %s status: %s\n",
2954                 domain->name, nt_errstr(status) ));
2955
2956         centry_free(centry);
2957         return status;
2958
2959 do_query:
2960         ZERO_STRUCTP(policy);
2961
2962         /* Return status value returned by seq number check */
2963
2964         if (!NT_STATUS_IS_OK(domain->last_status))
2965                 return domain->last_status;
2966
2967         DEBUG(10,("password_policy: [Cached] - doing backend query for info for domain %s\n",
2968                 domain->name ));
2969
2970         status = domain->backend->password_policy(domain, mem_ctx, policy);
2971
2972         if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2973                 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2974                 if (!domain->internal && old_status) {
2975                         set_domain_offline(domain);
2976                 }
2977                 if (cache->tdb &&
2978                         !domain->internal &&
2979                         !domain->online &&
2980                         old_status) {
2981                         centry = wcache_fetch(cache, domain, "PWD_POL/%s", domain->name);
2982                         if (centry) {
2983                                 goto do_fetch_cache;
2984                         }
2985                 }
2986         }
2987         /* and save it */
2988         refresh_sequence_number(domain);
2989         if (!NT_STATUS_IS_OK(status)) {
2990                 return status;
2991         }
2992         wcache_save_password_policy(domain, status, policy);
2993
2994         return status;
2995 }
2996
2997
2998 /* Invalidate cached user and group lists coherently */
2999
3000 static int traverse_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf, 
3001                        void *state)
3002 {
3003         if (strncmp((const char *)kbuf.dptr, "UL/", 3) == 0 ||
3004             strncmp((const char *)kbuf.dptr, "GL/", 3) == 0)
3005                 tdb_delete(the_tdb, kbuf);
3006
3007         return 0;
3008 }
3009
3010 /* Invalidate the getpwnam and getgroups entries for a winbindd domain */
3011
3012 void wcache_invalidate_samlogon(struct winbindd_domain *domain, 
3013                                 const struct dom_sid *sid)
3014 {
3015         fstring key_str, sid_string;
3016         struct winbind_cache *cache;
3017
3018         /* don't clear cached U/SID and UG/SID entries when we want to logon
3019          * offline - gd */
3020
3021         if (lp_winbind_offline_logon()) {
3022                 return;
3023         }
3024
3025         if (!domain)
3026                 return;
3027
3028         cache = get_cache(domain);
3029
3030         if (!cache->tdb) {
3031                 return;
3032         }
3033
3034         /* Clear U/SID cache entry */
3035         fstr_sprintf(key_str, "U/%s", sid_to_fstring(sid_string, sid));
3036         DEBUG(10, ("wcache_invalidate_samlogon: clearing %s\n", key_str));
3037         tdb_delete(cache->tdb, string_tdb_data(key_str));
3038
3039         /* Clear UG/SID cache entry */
3040         fstr_sprintf(key_str, "UG/%s", sid_to_fstring(sid_string, sid));
3041         DEBUG(10, ("wcache_invalidate_samlogon: clearing %s\n", key_str));
3042         tdb_delete(cache->tdb, string_tdb_data(key_str));
3043
3044         /* Samba/winbindd never needs this. */
3045         netsamlogon_clear_cached_user(sid);
3046 }
3047
3048 bool wcache_invalidate_cache(void)
3049 {
3050         struct winbindd_domain *domain;
3051
3052         for (domain = domain_list(); domain; domain = domain->next) {
3053                 struct winbind_cache *cache = get_cache(domain);
3054
3055                 DEBUG(10, ("wcache_invalidate_cache: invalidating cache "
3056                            "entries for %s\n", domain->name));
3057                 if (cache) {
3058                         if (cache->tdb) {
3059                                 tdb_traverse(cache->tdb, traverse_fn, NULL);
3060                         } else {
3061                                 return false;
3062                         }
3063                 }
3064         }
3065         return true;
3066 }
3067
3068 bool wcache_invalidate_cache_noinit(void)
3069 {
3070         struct winbindd_domain *domain;
3071
3072         for (domain = domain_list(); domain; domain = domain->next) {
3073                 struct winbind_cache *cache;
3074
3075                 /* Skip uninitialized domains. */
3076                 if (!domain->initialized && !domain->internal) {
3077                         continue;
3078                 }
3079
3080                 cache = get_cache(domain);
3081
3082                 DEBUG(10, ("wcache_invalidate_cache: invalidating cache "
3083                            "entries for %s\n", domain->name));
3084                 if (cache) {
3085                         if (cache->tdb) {
3086                                 tdb_traverse(cache->tdb, traverse_fn, NULL);
3087                                 /*
3088                                  * Flushing cache has nothing to with domains.
3089                                  * return here if we successfully flushed once.
3090                                  * To avoid unnecessary traversing the cache.
3091                                  */
3092                                 return true;
3093                         } else {
3094                                 return false;
3095                         }
3096                 }
3097         }
3098         return true;
3099 }
3100
3101 static bool init_wcache(void)
3102 {
3103         char *db_path;
3104
3105         if (wcache == NULL) {
3106                 wcache = SMB_XMALLOC_P(struct winbind_cache);
3107                 ZERO_STRUCTP(wcache);
3108         }
3109
3110         if (wcache->tdb != NULL)
3111                 return true;
3112
3113         db_path = wcache_path();
3114         if (db_path == NULL) {
3115                 return false;
3116         }
3117
3118         /* when working offline we must not clear the cache on restart */
3119         wcache->tdb = tdb_open_log(db_path,
3120                                 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE, 
3121                                 TDB_INCOMPATIBLE_HASH |
3122                                         (lp_winbind_offline_logon() ? TDB_DEFAULT : (TDB_DEFAULT | TDB_CLEAR_IF_FIRST)),
3123                                 O_RDWR|O_CREAT, 0600);
3124         TALLOC_FREE(db_path);
3125         if (wcache->tdb == NULL) {
3126                 DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
3127                 return false;
3128         }
3129
3130         return true;
3131 }
3132
3133 /************************************************************************
3134  This is called by the parent to initialize the cache file.
3135  We don't need sophisticated locking here as we know we're the
3136  only opener.
3137 ************************************************************************/
3138
3139 bool initialize_winbindd_cache(void)
3140 {
3141         bool cache_bad = true;
3142         uint32_t vers;
3143
3144         if (!init_wcache()) {
3145                 DEBUG(0,("initialize_winbindd_cache: init_wcache failed.\n"));
3146                 return false;
3147         }
3148
3149         /* Check version number. */
3150         if (tdb_fetch_uint32(wcache->tdb, WINBINDD_CACHE_VERSION_KEYSTR, &vers) &&
3151                         vers == WINBINDD_CACHE_VERSION) {
3152                 cache_bad = false;
3153         }
3154
3155         if (cache_bad) {
3156                 char *db_path;
3157
3158                 DEBUG(0,("initialize_winbindd_cache: clearing cache "
3159                         "and re-creating with version number %d\n",
3160                         WINBINDD_CACHE_VERSION ));
3161
3162                 tdb_close(wcache->tdb);
3163                 wcache->tdb = NULL;
3164
3165                 db_path = wcache_path();
3166                 if (db_path == NULL) {
3167                         return false;
3168                 }
3169
3170                 if (unlink(db_path) == -1) {
3171                         DEBUG(0,("initialize_winbindd_cache: unlink %s failed %s ",
3172                                 db_path,
3173                                 strerror(errno) ));
3174                         TALLOC_FREE(db_path);
3175                         return false;
3176                 }
3177                 TALLOC_FREE(db_path);
3178                 if (!init_wcache()) {
3179                         DEBUG(0,("initialize_winbindd_cache: re-initialization "
3180                                         "init_wcache failed.\n"));
3181                         return false;
3182                 }
3183
3184                 /* Write the version. */
3185                 if (!tdb_store_uint32(wcache->tdb, WINBINDD_CACHE_VERSION_KEYSTR, WINBINDD_CACHE_VERSION)) {
3186                         DEBUG(0,("initialize_winbindd_cache: version number store failed %s\n",
3187                                 tdb_errorstr(wcache->tdb) ));
3188                         return false;
3189                 }
3190         }
3191
3192         tdb_close(wcache->tdb);
3193         wcache->tdb = NULL;
3194         return true;
3195 }
3196
3197 void close_winbindd_cache(void)
3198 {
3199         if (!wcache) {
3200                 return;
3201         }
3202         if (wcache->tdb) {
3203                 tdb_close(wcache->tdb);
3204                 wcache->tdb = NULL;
3205         }
3206 }
3207
3208 bool lookup_cached_sid(TALLOC_CTX *mem_ctx, const struct dom_sid *sid,
3209                        char **domain_name, char **name,
3210                        enum lsa_SidType *type)
3211 {
3212         struct winbindd_domain *domain;
3213         NTSTATUS status;
3214
3215         domain = find_lookup_domain_from_sid(sid);
3216         if (domain == NULL) {
3217                 return false;
3218         }
3219         status = wcache_sid_to_name(domain, sid, mem_ctx, domain_name, name,
3220                                     type);
3221         return NT_STATUS_IS_OK(status);
3222 }
3223
3224 bool lookup_cached_name(const char *namespace,
3225                         const char *domain_name,
3226                         const char *name,
3227                         struct dom_sid *sid,
3228                         enum lsa_SidType *type)
3229 {
3230         struct winbindd_domain *domain;
3231         NTSTATUS status;
3232         bool original_online_state;
3233
3234         domain = find_lookup_domain_from_name(namespace);
3235         if (domain == NULL) {
3236                 return false;
3237         }
3238
3239         /* If we are doing a cached logon, temporarily set the domain
3240            offline so the cache won't expire the entry */
3241
3242         original_online_state = domain->online;
3243         domain->online = false;
3244         status = wcache_name_to_sid(domain, domain_name, name, sid, type);
3245         domain->online = original_online_state;
3246
3247         return NT_STATUS_IS_OK(status);
3248 }
3249
3250 /*
3251  * Cache a name to sid without checking the sequence number.
3252  * Used when caching from a trusted PAC.
3253  */
3254
3255 void cache_name2sid_trusted(struct winbindd_domain *domain,
3256                         const char *domain_name,
3257                         const char *name,
3258                         enum lsa_SidType type,
3259                         const struct dom_sid *sid)
3260 {
3261         /*
3262          * Ensure we store the mapping with the
3263          * existing sequence number from the cache.
3264          */
3265         get_cache(domain);
3266         (void)fetch_cache_seqnum(domain, time(NULL));
3267         wcache_save_name_to_sid(domain,
3268                                 NT_STATUS_OK,
3269                                 domain_name,
3270                                 name,
3271                                 sid,
3272                                 type);
3273 }
3274
3275 void cache_name2sid(struct winbindd_domain *domain, 
3276                     const char *domain_name, const char *name,
3277                     enum lsa_SidType type, const struct dom_sid *sid)
3278 {
3279         refresh_sequence_number(domain);
3280         wcache_save_name_to_sid(domain, NT_STATUS_OK, domain_name, name,
3281                                 sid, type);
3282 }
3283
3284 /*
3285  * The original idea that this cache only contains centries has
3286  * been blurred - now other stuff gets put in here. Ensure we
3287  * ignore these things on cleanup.
3288  */
3289
3290 static int traverse_fn_cleanup(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, 
3291                                TDB_DATA dbuf, void *state)
3292 {
3293         struct cache_entry *centry;
3294
3295         if (is_non_centry_key(kbuf)) {
3296                 return 0;
3297         }
3298
3299         centry = wcache_fetch_raw((char *)kbuf.dptr);
3300         if (!centry) {
3301                 return 0;
3302         }
3303
3304         if (!NT_STATUS_IS_OK(centry->status)) {
3305                 DEBUG(10,("deleting centry %s\n", (const char *)kbuf.dptr));
3306                 tdb_delete(the_tdb, kbuf);
3307         }
3308
3309         centry_free(centry);
3310         return 0;
3311 }
3312
3313 /* flush the cache */
3314 static void wcache_flush_cache(void)
3315 {
3316         char *db_path;
3317
3318         if (!wcache)
3319                 return;
3320         if (wcache->tdb) {
3321                 tdb_close(wcache->tdb);
3322                 wcache->tdb = NULL;
3323         }
3324         if (!winbindd_use_cache()) {
3325                 return;
3326         }
3327
3328         db_path = wcache_path();
3329         if (db_path == NULL) {
3330                 return;
3331         }
3332
3333         /* when working offline we must not clear the cache on restart */
3334         wcache->tdb = tdb_open_log(db_path,
3335                                 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
3336                                 TDB_INCOMPATIBLE_HASH |
3337                                 (lp_winbind_offline_logon() ? TDB_DEFAULT : (TDB_DEFAULT | TDB_CLEAR_IF_FIRST)),
3338                                 O_RDWR|O_CREAT, 0600);
3339         TALLOC_FREE(db_path);
3340         if (!wcache->tdb) {
3341                 DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
3342                 return;
3343         }
3344
3345         tdb_traverse(wcache->tdb, traverse_fn_cleanup, NULL);
3346
3347         DEBUG(10,("wcache_flush_cache success\n"));
3348 }
3349
3350 /* Count cached creds */
3351
3352 static int traverse_fn_cached_creds(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf, 
3353                                     void *state)
3354 {
3355         int *cred_count = (int*)state;
3356  
3357         if (strncmp((const char *)kbuf.dptr, "CRED/", 5) == 0) {
3358                 (*cred_count)++;
3359         }
3360         return 0;
3361 }
3362
3363 NTSTATUS wcache_count_cached_creds(struct winbindd_domain *domain, int *count)
3364 {
3365         struct winbind_cache *cache = get_cache(domain);
3366
3367         *count = 0;
3368
3369         if (!cache->tdb) {
3370                 return NT_STATUS_INTERNAL_DB_ERROR;
3371         }
3372  
3373         tdb_traverse(cache->tdb, traverse_fn_cached_creds, (void *)count);
3374
3375         return NT_STATUS_OK;
3376 }
3377
3378 struct cred_list {
3379         struct cred_list *prev, *next;
3380         TDB_DATA key;
3381         fstring name;
3382         time_t created;
3383 };
3384 static struct cred_list *wcache_cred_list;
3385
3386 static int traverse_fn_get_credlist(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf, 
3387                                     void *state)
3388 {
3389         struct cred_list *cred;
3390
3391         if (strncmp((const char *)kbuf.dptr, "CRED/", 5) == 0) {
3392
3393                 cred = SMB_MALLOC_P(struct cred_list);
3394                 if (cred == NULL) {
3395                         DEBUG(0,("traverse_fn_remove_first_creds: failed to malloc new entry for list\n"));
3396                         return -1;
3397                 }
3398
3399                 ZERO_STRUCTP(cred);
3400
3401                 /* save a copy of the key */
3402
3403                 fstrcpy(cred->name, (const char *)kbuf.dptr);           
3404                 DLIST_ADD(wcache_cred_list, cred);
3405         }
3406
3407         return 0;
3408 }
3409
3410 NTSTATUS wcache_remove_oldest_cached_creds(struct winbindd_domain *domain, const struct dom_sid *sid)
3411 {
3412         struct winbind_cache *cache = get_cache(domain);
3413         NTSTATUS status;
3414         int ret;
3415         struct cred_list *cred, *next, *oldest = NULL;
3416
3417         if (!cache->tdb) {
3418                 return NT_STATUS_INTERNAL_DB_ERROR;
3419         }
3420
3421         /* we possibly already have an entry */
3422         if (sid && NT_STATUS_IS_OK(wcache_cached_creds_exist(domain, sid))) {
3423
3424                 fstring key_str, tmp;
3425
3426                 DEBUG(11,("we already have an entry, deleting that\n"));
3427
3428                 fstr_sprintf(key_str, "CRED/%s", sid_to_fstring(tmp, sid));
3429
3430                 tdb_delete(cache->tdb, string_tdb_data(key_str));
3431
3432                 return NT_STATUS_OK;
3433         }
3434
3435         ret = tdb_traverse(cache->tdb, traverse_fn_get_credlist, NULL);
3436         if (ret == 0) {
3437                 return NT_STATUS_OK;
3438         } else if ((ret < 0) || (wcache_cred_list == NULL)) {
3439                 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
3440         }
3441
3442         ZERO_STRUCTP(oldest);
3443
3444         for (cred = wcache_cred_list; cred; cred = cred->next) {
3445
3446                 TDB_DATA data;
3447                 time_t t;
3448
3449                 data = tdb_fetch(cache->tdb, string_tdb_data(cred->name));
3450                 if (!data.dptr) {
3451                         DEBUG(10,("wcache_remove_oldest_cached_creds: entry for [%s] not found\n", 
3452                                 cred->name));
3453                         status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
3454                         goto done;
3455                 }
3456
3457                 t = IVAL(data.dptr, 0);
3458                 SAFE_FREE(data.dptr);
3459
3460                 if (!oldest) {
3461                         oldest = SMB_MALLOC_P(struct cred_list);
3462                         if (oldest == NULL) {
3463                                 status = NT_STATUS_NO_MEMORY;
3464                                 goto done;
3465                         }
3466
3467                         fstrcpy(oldest->name, cred->name);
3468                         oldest->created = t;
3469                         continue;
3470                 }
3471
3472                 if (t < oldest->created) {
3473                         fstrcpy(oldest->name, cred->name);
3474                         oldest->created = t;
3475                 }
3476         }
3477
3478         if (tdb_delete(cache->tdb, string_tdb_data(oldest->name)) == 0) {
3479                 status = NT_STATUS_OK;
3480         } else {
3481                 status = NT_STATUS_UNSUCCESSFUL;
3482         }
3483 done:
3484         for (cred = wcache_cred_list; cred; cred = next) {
3485                 next = cred->next;
3486                 DLIST_REMOVE(wcache_cred_list, cred);
3487                 SAFE_FREE(cred);
3488         }
3489         SAFE_FREE(oldest);
3490
3491         return status;
3492 }
3493
3494 /* Change the global online/offline state. */
3495 bool set_global_winbindd_state_offline(void)
3496 {
3497         TDB_DATA data;
3498
3499         DEBUG(10,("set_global_winbindd_state_offline: offline requested.\n"));
3500
3501         /* Only go offline if someone has created
3502            the key "WINBINDD_OFFLINE" in the cache tdb. */
3503
3504         if (wcache == NULL || wcache->tdb == NULL) {
3505                 DEBUG(10,("set_global_winbindd_state_offline: wcache not open yet.\n"));
3506                 return false;
3507         }
3508
3509         if (!lp_winbind_offline_logon()) {
3510                 DEBUG(10,("set_global_winbindd_state_offline: rejecting.\n"));
3511                 return false;
3512         }
3513
3514         if (global_winbindd_offline_state) {
3515                 /* Already offline. */
3516                 return true;
3517         }
3518
3519         data = tdb_fetch_bystring( wcache->tdb, "WINBINDD_OFFLINE" );
3520
3521         if (!data.dptr || data.dsize != 4) {
3522                 DEBUG(10,("set_global_winbindd_state_offline: offline state not set.\n"));
3523                 SAFE_FREE(data.dptr);
3524                 return false;
3525         } else {
3526                 DEBUG(10,("set_global_winbindd_state_offline: offline state set.\n"));
3527                 global_winbindd_offline_state = true;
3528                 SAFE_FREE(data.dptr);
3529                 return true;
3530         }
3531 }
3532
3533 void set_global_winbindd_state_online(void)
3534 {
3535         DEBUG(10,("set_global_winbindd_state_online: online requested.\n"));
3536
3537         if (!lp_winbind_offline_logon()) {
3538                 DEBUG(10,("set_global_winbindd_state_online: rejecting.\n"));
3539                 return;
3540         }