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