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