winbind: Use domain name from lsa query for sid_to_name cache entry
[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         struct dom_sid_buf sid_string;
858         centry_put_string(centry, dom_sid_str_buf(sid, &sid_string));
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         struct dom_sid_buf tmp;
1245         fstring key_str;
1246         uint32_t rid;
1247
1248         if (!cache->tdb) {
1249                 return NT_STATUS_INTERNAL_DB_ERROR;
1250         }
1251
1252         if (is_null_sid(sid)) {
1253                 return NT_STATUS_INVALID_SID;
1254         }
1255
1256         if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
1257                 return NT_STATUS_INVALID_SID;
1258         }
1259
1260         fstr_sprintf(key_str, "CRED/%s", dom_sid_str_buf(sid, &tmp));
1261
1262         ret = tdb_exists(cache->tdb, string_tdb_data(key_str));
1263         if (ret != 1) {
1264                 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1265         }
1266
1267         return NT_STATUS_OK;
1268 }
1269
1270 /* Lookup creds for a SID - copes with old (unsalted) creds as well
1271    as new salted ones. */
1272
1273 NTSTATUS wcache_get_creds(struct winbindd_domain *domain, 
1274                           TALLOC_CTX *mem_ctx, 
1275                           const struct dom_sid *sid,
1276                           const uint8_t **cached_nt_pass,
1277                           const uint8_t **cached_salt)
1278 {
1279         struct winbind_cache *cache = get_cache(domain);
1280         struct cache_entry *centry = NULL;
1281         NTSTATUS status;
1282         uint32_t rid;
1283         struct dom_sid_buf sidstr;
1284
1285         if (!cache->tdb) {
1286                 return NT_STATUS_INTERNAL_DB_ERROR;
1287         }
1288
1289         if (is_null_sid(sid)) {
1290                 return NT_STATUS_INVALID_SID;
1291         }
1292
1293         if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
1294                 return NT_STATUS_INVALID_SID;
1295         }
1296
1297         /* Try and get a salted cred first. If we can't
1298            fall back to an unsalted cred. */
1299
1300         centry = wcache_fetch(cache, domain, "CRED/%s",
1301                               dom_sid_str_buf(sid, &sidstr));
1302         if (!centry) {
1303                 DEBUG(10,("wcache_get_creds: entry for [CRED/%s] not found\n", 
1304                           dom_sid_str_buf(sid, &sidstr)));
1305                 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1306         }
1307
1308         /*
1309          * We don't use the time element at this moment,
1310          * but we have to consume it, so that we don't
1311          * neet to change the disk format of the cache.
1312          */
1313         (void)centry_time(centry);
1314
1315         /* In the salted case this isn't actually the nt_hash itself,
1316            but the MD5 of the salt + nt_hash. Let the caller
1317            sort this out. It can tell as we only return the cached_salt
1318            if we are returning a salted cred. */
1319
1320         *cached_nt_pass = (const uint8_t *)centry_hash16(centry, mem_ctx);
1321         if (*cached_nt_pass == NULL) {
1322
1323                 dom_sid_str_buf(sid, &sidstr);
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.buf));
1329                 wcache_delete("CRED/%s", sidstr.buf);
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                   dom_sid_str_buf(sid, &sidstr),
1350                   nt_errstr(status) ));
1351
1352         centry_free(centry);
1353         return status;
1354 }
1355
1356 /* Store creds for a SID - only writes out new salted ones. */
1357
1358 NTSTATUS wcache_save_creds(struct winbindd_domain *domain, 
1359                            const struct dom_sid *sid,
1360                            const uint8_t nt_pass[NT_HASH_LEN])
1361 {
1362         struct cache_entry *centry;
1363         struct dom_sid_buf sid_str;
1364         uint32_t rid;
1365         uint8_t cred_salt[NT_HASH_LEN];
1366         uint8_t salted_hash[NT_HASH_LEN];
1367
1368         if (is_null_sid(sid)) {
1369                 return NT_STATUS_INVALID_SID;
1370         }
1371
1372         if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
1373                 return NT_STATUS_INVALID_SID;
1374         }
1375
1376         centry = centry_start(domain, NT_STATUS_OK);
1377         if (!centry) {
1378                 return NT_STATUS_INTERNAL_DB_ERROR;
1379         }
1380
1381         dump_data_pw("nt_pass", nt_pass, NT_HASH_LEN);
1382
1383         centry_put_time(centry, time(NULL));
1384
1385         /* Create a salt and then salt the hash. */
1386         generate_random_buffer(cred_salt, NT_HASH_LEN);
1387         E_md5hash(cred_salt, nt_pass, salted_hash);
1388
1389         centry_put_hash16(centry, salted_hash);
1390         centry_put_hash16(centry, cred_salt);
1391         centry_end(centry, "CRED/%s", dom_sid_str_buf(sid, &sid_str));
1392
1393         DEBUG(10,("wcache_save_creds: %s\n", sid_str.buf));
1394
1395         centry_free(centry);
1396
1397         return NT_STATUS_OK;
1398 }
1399
1400
1401 /* Query display info. This is the basic user list fn */
1402 NTSTATUS wb_cache_query_user_list(struct winbindd_domain *domain,
1403                                   TALLOC_CTX *mem_ctx,
1404                                   uint32_t **prids)
1405 {
1406         struct winbind_cache *cache = get_cache(domain);
1407         struct cache_entry *centry = NULL;
1408         uint32_t num_rids = 0;
1409         uint32_t *rids = NULL;
1410         NTSTATUS status;
1411         unsigned int i, retry;
1412         bool old_status = domain->online;
1413
1414         *prids = NULL;
1415
1416         if (!cache->tdb)
1417                 goto do_query;
1418
1419         centry = wcache_fetch(cache, domain, "UL/%s", domain->name);
1420         if (!centry)
1421                 goto do_query;
1422
1423 do_fetch_cache:
1424         num_rids = centry_uint32(centry);
1425
1426         if (num_rids == 0) {
1427                 goto do_cached;
1428         }
1429
1430         rids = talloc_array(mem_ctx, uint32_t, num_rids);
1431         if (rids == NULL) {
1432                 centry_free(centry);
1433                 return NT_STATUS_NO_MEMORY;
1434         }
1435
1436         for (i=0; i<num_rids; i++) {
1437                 rids[i] = centry_uint32(centry);
1438         }
1439
1440 do_cached:      
1441         status = centry->status;
1442
1443         DEBUG(10,("query_user_list: [Cached] - cached list for domain %s status: %s\n",
1444                 domain->name, nt_errstr(status) ));
1445
1446         centry_free(centry);
1447         return status;
1448
1449 do_query:
1450
1451         /* Return status value returned by seq number check */
1452
1453         if (!NT_STATUS_IS_OK(domain->last_status))
1454                 return domain->last_status;
1455
1456         /* Put the query_user_list() in a retry loop.  There appears to be
1457          * some bug either with Windows 2000 or Samba's handling of large
1458          * rpc replies.  This manifests itself as sudden disconnection
1459          * at a random point in the enumeration of a large (60k) user list.
1460          * The retry loop simply tries the operation again. )-:  It's not
1461          * pretty but an acceptable workaround until we work out what the
1462          * real problem is. */
1463
1464         retry = 0;
1465         do {
1466
1467                 DEBUG(10,("query_user_list: [Cached] - doing backend query for list for domain %s\n",
1468                         domain->name ));
1469
1470                 rids = NULL;
1471                 status = domain->backend->query_user_list(domain, mem_ctx,
1472                                                           &rids);
1473                 num_rids = talloc_array_length(rids);
1474
1475                 if (!NT_STATUS_IS_OK(status)) {
1476                         DEBUG(3, ("query_user_list: returned 0x%08x, "
1477                                   "retrying\n", NT_STATUS_V(status)));
1478                 }
1479                 if (NT_STATUS_EQUAL(status, NT_STATUS_UNSUCCESSFUL)) {
1480                         DEBUG(3, ("query_user_list: flushing "
1481                                   "connection cache\n"));
1482                         invalidate_cm_connection(domain);
1483                 }
1484                 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1485                     NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1486                         if (!domain->internal && old_status) {
1487                                 set_domain_offline(domain);
1488                         }
1489                         /* store partial response. */
1490                         if (num_rids > 0) {
1491                                 /*
1492                                  * humm, what about the status used for cache?
1493                                  * Should it be NT_STATUS_OK?
1494                                  */
1495                                 break;
1496                         }
1497                         /*
1498                          * domain is offline now, and there is no user entries,
1499                          * try to fetch from cache again.
1500                          */
1501                         if (cache->tdb && !domain->online && !domain->internal && old_status) {
1502                                 centry = wcache_fetch(cache, domain, "UL/%s", domain->name);
1503                                 /* partial response... */
1504                                 if (!centry) {
1505                                         goto skip_save;
1506                                 } else {
1507                                         goto do_fetch_cache;
1508                                 }
1509                         } else {
1510                                 goto skip_save;
1511                         }
1512                 }
1513
1514         } while (NT_STATUS_V(status) == NT_STATUS_V(NT_STATUS_UNSUCCESSFUL) && 
1515                  (retry++ < 5));
1516
1517         /* and save it */
1518         refresh_sequence_number(domain);
1519         if (!NT_STATUS_IS_OK(status)) {
1520                 return status;
1521         }
1522         centry = centry_start(domain, status);
1523         if (!centry)
1524                 goto skip_save;
1525         centry_put_uint32(centry, num_rids);
1526         for (i=0; i<num_rids; i++) {
1527                 centry_put_uint32(centry, rids[i]);
1528         }       
1529         centry_end(centry, "UL/%s", domain->name);
1530         centry_free(centry);
1531
1532         *prids = rids;
1533
1534 skip_save:
1535         return status;
1536 }
1537
1538 /* list all domain groups */
1539 NTSTATUS wb_cache_enum_dom_groups(struct winbindd_domain *domain,
1540                                   TALLOC_CTX *mem_ctx,
1541                                   uint32_t *num_entries,
1542                                   struct wb_acct_info **info)
1543 {
1544         struct winbind_cache *cache = get_cache(domain);
1545         struct cache_entry *centry = NULL;
1546         NTSTATUS status;
1547         unsigned int i;
1548         bool old_status;
1549
1550         old_status = domain->online;
1551         if (!cache->tdb)
1552                 goto do_query;
1553
1554         centry = wcache_fetch(cache, domain, "GL/%s/domain", domain->name);
1555         if (!centry)
1556                 goto do_query;
1557
1558 do_fetch_cache:
1559         *num_entries = centry_uint32(centry);
1560
1561         if (*num_entries == 0)
1562                 goto do_cached;
1563
1564         (*info) = talloc_array(mem_ctx, struct wb_acct_info, *num_entries);
1565         if (! (*info)) {
1566                 smb_panic_fn("enum_dom_groups out of memory");
1567         }
1568         for (i=0; i<(*num_entries); i++) {
1569                 (*info)[i].acct_name = centry_string(centry, (*info));
1570                 (*info)[i].acct_desc = centry_string(centry, (*info));
1571                 (*info)[i].rid = centry_uint32(centry);
1572         }
1573
1574 do_cached:      
1575         status = centry->status;
1576
1577         DEBUG(10,("enum_dom_groups: [Cached] - cached list for domain %s status: %s\n",
1578                 domain->name, nt_errstr(status) ));
1579
1580         centry_free(centry);
1581         return status;
1582
1583 do_query:
1584         *num_entries = 0;
1585         *info = NULL;
1586
1587         /* Return status value returned by seq number check */
1588
1589         if (!NT_STATUS_IS_OK(domain->last_status))
1590                 return domain->last_status;
1591
1592         DEBUG(10,("enum_dom_groups: [Cached] - doing backend query for list for domain %s\n",
1593                 domain->name ));
1594
1595         status = domain->backend->enum_dom_groups(domain, mem_ctx, num_entries, info);
1596
1597         if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1598             NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1599                 if (!domain->internal && old_status) {
1600                         set_domain_offline(domain);
1601                 }
1602                 if (cache->tdb &&
1603                         !domain->online &&
1604                         !domain->internal &&
1605                         old_status) {
1606                         centry = wcache_fetch(cache, domain, "GL/%s/domain", domain->name);
1607                         if (centry) {
1608                                 goto do_fetch_cache;
1609                         }
1610                 }
1611         }
1612         /* and save it */
1613         refresh_sequence_number(domain);
1614         if (!NT_STATUS_IS_OK(status)) {
1615                 return status;
1616         }
1617         centry = centry_start(domain, status);
1618         if (!centry)
1619                 goto skip_save;
1620         centry_put_uint32(centry, *num_entries);
1621         for (i=0; i<(*num_entries); i++) {
1622                 centry_put_string(centry, (*info)[i].acct_name);
1623                 centry_put_string(centry, (*info)[i].acct_desc);
1624                 centry_put_uint32(centry, (*info)[i].rid);
1625         }       
1626         centry_end(centry, "GL/%s/domain", domain->name);
1627         centry_free(centry);
1628
1629 skip_save:
1630         return status;
1631 }
1632
1633 /* list all domain groups */
1634 NTSTATUS wb_cache_enum_local_groups(struct winbindd_domain *domain,
1635                                     TALLOC_CTX *mem_ctx,
1636                                     uint32_t *num_entries,
1637                                     struct wb_acct_info **info)
1638 {
1639         struct winbind_cache *cache = get_cache(domain);
1640         struct cache_entry *centry = NULL;
1641         NTSTATUS status;
1642         unsigned int i;
1643         bool old_status;
1644
1645         old_status = domain->online;
1646         if (!cache->tdb)
1647                 goto do_query;
1648
1649         centry = wcache_fetch(cache, domain, "GL/%s/local", domain->name);
1650         if (!centry)
1651                 goto do_query;
1652
1653 do_fetch_cache:
1654         *num_entries = centry_uint32(centry);
1655
1656         if (*num_entries == 0)
1657                 goto do_cached;
1658
1659         (*info) = talloc_array(mem_ctx, struct wb_acct_info, *num_entries);
1660         if (! (*info)) {
1661                 smb_panic_fn("enum_dom_groups out of memory");
1662         }
1663         for (i=0; i<(*num_entries); i++) {
1664                 (*info)[i].acct_name = centry_string(centry, (*info));
1665                 (*info)[i].acct_desc = centry_string(centry, (*info));
1666                 (*info)[i].rid = centry_uint32(centry);
1667         }
1668
1669 do_cached:      
1670
1671         /* If we are returning cached data and the domain controller
1672            is down then we don't know whether the data is up to date
1673            or not.  Return NT_STATUS_MORE_PROCESSING_REQUIRED to
1674            indicate this. */
1675
1676         if (wcache_server_down(domain)) {
1677                 DEBUG(10, ("enum_local_groups: returning cached user list and server was down\n"));
1678                 status = NT_STATUS_MORE_PROCESSING_REQUIRED;
1679         } else
1680                 status = centry->status;
1681
1682         DEBUG(10,("enum_local_groups: [Cached] - cached list for domain %s status: %s\n",
1683                 domain->name, nt_errstr(status) ));
1684
1685         centry_free(centry);
1686         return status;
1687
1688 do_query:
1689         *num_entries = 0;
1690         *info = NULL;
1691
1692         /* Return status value returned by seq number check */
1693
1694         if (!NT_STATUS_IS_OK(domain->last_status))
1695                 return domain->last_status;
1696
1697         DEBUG(10,("enum_local_groups: [Cached] - doing backend query for list for domain %s\n",
1698                 domain->name ));
1699
1700         status = domain->backend->enum_local_groups(domain, mem_ctx, num_entries, info);
1701
1702         if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1703                 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1704                 if (!domain->internal && old_status) {
1705                         set_domain_offline(domain);
1706                 }
1707                 if (cache->tdb &&
1708                         !domain->internal &&
1709                         !domain->online &&
1710                         old_status) {
1711                         centry = wcache_fetch(cache, domain, "GL/%s/local", domain->name);
1712                         if (centry) {
1713                                 goto do_fetch_cache;
1714                         }
1715                 }
1716         }
1717         /* and save it */
1718         refresh_sequence_number(domain);
1719         if (!NT_STATUS_IS_OK(status)) {
1720                 return status;
1721         }
1722         centry = centry_start(domain, status);
1723         if (!centry)
1724                 goto skip_save;
1725         centry_put_uint32(centry, *num_entries);
1726         for (i=0; i<(*num_entries); i++) {
1727                 centry_put_string(centry, (*info)[i].acct_name);
1728                 centry_put_string(centry, (*info)[i].acct_desc);
1729                 centry_put_uint32(centry, (*info)[i].rid);
1730         }
1731         centry_end(centry, "GL/%s/local", domain->name);
1732         centry_free(centry);
1733
1734 skip_save:
1735         return status;
1736 }
1737
1738 struct wcache_name_to_sid_state {
1739         struct dom_sid *sid;
1740         enum lsa_SidType *type;
1741         bool offline;
1742         bool found;
1743 };
1744
1745 static void wcache_name_to_sid_fn(const struct dom_sid *sid,
1746                                   enum lsa_SidType type,
1747                                   bool expired,
1748                                   void *private_data)
1749 {
1750         struct wcache_name_to_sid_state *state = private_data;
1751
1752         *state->sid = *sid;
1753         *state->type = type;
1754         state->found = (!expired || state->offline);
1755 }
1756
1757 static NTSTATUS wcache_name_to_sid(struct winbindd_domain *domain,
1758                                    const char *domain_name,
1759                                    const char *name,
1760                                    struct dom_sid *sid,
1761                                    enum lsa_SidType *type)
1762 {
1763         struct wcache_name_to_sid_state state = {
1764                 .sid = sid, .type = type, .found = false,
1765                 .offline = is_domain_offline(domain),
1766         };
1767         bool ok;
1768
1769         ok = namemap_cache_find_name(domain_name, name, wcache_name_to_sid_fn,
1770                                      &state);
1771         if (!ok) {
1772                 DBG_DEBUG("namemap_cache_find_name failed\n");
1773                 return NT_STATUS_NOT_FOUND;
1774         }
1775         if (!state.found) {
1776                 DBG_DEBUG("cache entry not found\n");
1777                 return NT_STATUS_NOT_FOUND;
1778         }
1779         if (*type == SID_NAME_UNKNOWN) {
1780                 return NT_STATUS_NONE_MAPPED;
1781         }
1782
1783         return NT_STATUS_OK;
1784 }
1785
1786 /* convert a single name to a sid in a domain */
1787 NTSTATUS wb_cache_name_to_sid(struct winbindd_domain *domain,
1788                               TALLOC_CTX *mem_ctx,
1789                               const char *domain_name,
1790                               const char *name,
1791                               uint32_t flags,
1792                               struct dom_sid *sid,
1793                               enum lsa_SidType *type)
1794 {
1795         NTSTATUS status;
1796         bool old_status;
1797         const char *dom_name;
1798
1799         old_status = domain->online;
1800
1801         status = wcache_name_to_sid(domain, domain_name, name, sid, type);
1802         if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
1803                 return status;
1804         }
1805
1806         ZERO_STRUCTP(sid);
1807
1808         /* If the seq number check indicated that there is a problem
1809          * with this DC, then return that status... except for
1810          * access_denied.  This is special because the dc may be in
1811          * "restrict anonymous = 1" mode, in which case it will deny
1812          * most unauthenticated operations, but *will* allow the LSA
1813          * name-to-sid that we try as a fallback. */
1814
1815         if (!(NT_STATUS_IS_OK(domain->last_status)
1816               || NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)))
1817                 return domain->last_status;
1818
1819         DEBUG(10,("name_to_sid: [Cached] - doing backend query for name for domain %s\n",
1820                 domain->name ));
1821
1822         winbindd_domain_init_backend(domain);
1823         status = domain->backend->name_to_sid(domain, mem_ctx, domain_name,
1824                                               name, flags, &dom_name, sid, type);
1825
1826         if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1827                 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1828                 if (!domain->internal && old_status) {
1829                         set_domain_offline(domain);
1830                 }
1831                 if (!domain->internal &&
1832                         !domain->online &&
1833                         old_status) {
1834                         NTSTATUS cache_status;
1835                         cache_status = wcache_name_to_sid(domain, domain_name, name, sid, type);
1836                         return cache_status;
1837                 }
1838         }
1839         /* and save it */
1840
1841         if (domain->online &&
1842             (NT_STATUS_IS_OK(status) || NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED))) {
1843                 enum lsa_SidType save_type = *type;
1844
1845                 if (NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED)) {
1846                         save_type = SID_NAME_UNKNOWN;
1847                 }
1848
1849                 wcache_save_name_to_sid(domain, status, domain_name, name, sid,
1850                                         save_type);
1851
1852                 /* Only save the reverse mapping if this was not a UPN */
1853                 if (!strchr(name, '@')) {
1854                         if (!strupper_m(discard_const_p(char, domain_name))) {
1855                                 return NT_STATUS_INVALID_PARAMETER;
1856                         }
1857                         (void)strlower_m(discard_const_p(char, name));
1858                         wcache_save_sid_to_name(domain, status, sid,
1859                                                 dom_name, name, save_type);
1860                 }
1861         }
1862
1863         return status;
1864 }
1865
1866 struct wcache_sid_to_name_state {
1867         TALLOC_CTX *mem_ctx;
1868         char **domain_name;
1869         char **name;
1870         enum lsa_SidType *type;
1871         bool offline;
1872         bool found;
1873 };
1874
1875 static void wcache_sid_to_name_fn(const char *domain,
1876                                   const char *name,
1877                                   enum lsa_SidType type,
1878                                   bool expired,
1879                                   void *private_data)
1880 {
1881         struct wcache_sid_to_name_state *state = private_data;
1882
1883         *state->domain_name = talloc_strdup(state->mem_ctx, domain);
1884         if (*state->domain_name == NULL) {
1885                 return;
1886         }
1887         *state->name = talloc_strdup(state->mem_ctx, name);
1888         if (*state->name == NULL) {
1889                 return;
1890         }
1891         *state->type = type;
1892         state->found = (!expired || state->offline);
1893 }
1894
1895 static NTSTATUS wcache_sid_to_name(struct winbindd_domain *domain,
1896                                    const struct dom_sid *sid,
1897                                    TALLOC_CTX *mem_ctx,
1898                                    char **domain_name,
1899                                    char **name,
1900                                    enum lsa_SidType *type)
1901 {
1902         struct wcache_sid_to_name_state state = {
1903                 .mem_ctx = mem_ctx, .found = false,
1904                 .domain_name = domain_name, .name = name, .type = type,
1905                 .offline = is_domain_offline(domain)
1906         };
1907         bool ok;
1908
1909         ok = namemap_cache_find_sid(sid, wcache_sid_to_name_fn, &state);
1910         if (!ok) {
1911                 DBG_DEBUG("namemap_cache_find_name failed\n");
1912                 return NT_STATUS_NOT_FOUND;
1913         }
1914         if (!state.found) {
1915                 DBG_DEBUG("cache entry not found\n");
1916                 return NT_STATUS_NOT_FOUND;
1917         }
1918         if (*type == SID_NAME_UNKNOWN) {
1919                 return NT_STATUS_NONE_MAPPED;
1920         }
1921
1922         return NT_STATUS_OK;
1923 }
1924
1925 /* convert a sid to a user or group name. The sid is guaranteed to be in the domain
1926    given */
1927 NTSTATUS wb_cache_sid_to_name(struct winbindd_domain *domain,
1928                               TALLOC_CTX *mem_ctx,
1929                               const struct dom_sid *sid,
1930                               char **domain_name,
1931                               char **name,
1932                               enum lsa_SidType *type)
1933 {
1934         NTSTATUS status;
1935         bool old_status;
1936
1937         old_status = domain->online;
1938         status = wcache_sid_to_name(domain, sid, mem_ctx, domain_name, name,
1939                                     type);
1940         if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
1941                 return status;
1942         }
1943
1944         *name = NULL;
1945         *domain_name = NULL;
1946
1947         /* If the seq number check indicated that there is a problem
1948          * with this DC, then return that status... except for
1949          * access_denied.  This is special because the dc may be in
1950          * "restrict anonymous = 1" mode, in which case it will deny
1951          * most unauthenticated operations, but *will* allow the LSA
1952          * sid-to-name that we try as a fallback. */
1953
1954         if (!(NT_STATUS_IS_OK(domain->last_status)
1955               || NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)))
1956                 return domain->last_status;
1957
1958         DEBUG(10,("sid_to_name: [Cached] - doing backend query for name for domain %s\n",
1959                 domain->name ));
1960
1961         winbindd_domain_init_backend(domain);
1962
1963         status = domain->backend->sid_to_name(domain, mem_ctx, sid, domain_name, name, type);
1964
1965         if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1966                 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1967                 if (!domain->internal && old_status) {
1968                         set_domain_offline(domain);
1969                 }
1970                 if (!domain->internal &&
1971                         !domain->online &&
1972                         old_status) {
1973                         NTSTATUS cache_status;
1974                         cache_status = wcache_sid_to_name(domain, sid, mem_ctx,
1975                                                         domain_name, name, type);
1976                         return cache_status;
1977                 }
1978         }
1979         /* and save it */
1980         if (!NT_STATUS_IS_OK(status)) {
1981                 return status;
1982         }
1983         wcache_save_sid_to_name(domain, status, sid, *domain_name, *name, *type);
1984
1985         /* We can't save the name to sid mapping here, as with sid history a
1986          * later name2sid would give the wrong sid. */
1987
1988         return status;
1989 }
1990
1991 NTSTATUS wb_cache_rids_to_names(struct winbindd_domain *domain,
1992                                 TALLOC_CTX *mem_ctx,
1993                                 const struct dom_sid *domain_sid,
1994                                 uint32_t *rids,
1995                                 size_t num_rids,
1996                                 char **domain_name,
1997                                 char ***names,
1998                                 enum lsa_SidType **types)
1999 {
2000         struct winbind_cache *cache = get_cache(domain);
2001         size_t i;
2002         NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
2003         bool have_mapped;
2004         bool have_unmapped;
2005         bool old_status;
2006
2007         old_status = domain->online;
2008         *domain_name = NULL;
2009         *names = NULL;
2010         *types = NULL;
2011
2012         if (!cache->tdb) {
2013                 goto do_query;
2014         }
2015
2016         if (num_rids == 0) {
2017                 return NT_STATUS_OK;
2018         }
2019
2020         *names = talloc_array(mem_ctx, char *, num_rids);
2021         *types = talloc_array(mem_ctx, enum lsa_SidType, num_rids);
2022
2023         if ((*names == NULL) || (*types == NULL)) {
2024                 result = NT_STATUS_NO_MEMORY;
2025                 goto error;
2026         }
2027
2028         have_mapped = have_unmapped = false;
2029
2030         for (i=0; i<num_rids; i++) {
2031                 struct dom_sid sid;
2032                 NTSTATUS status;
2033                 enum lsa_SidType type;
2034                 char *dom, *name;
2035
2036                 if (!sid_compose(&sid, domain_sid, rids[i])) {
2037                         result = NT_STATUS_INTERNAL_ERROR;
2038                         goto error;
2039                 }
2040
2041                 status = wcache_sid_to_name(domain, &sid, *names, &dom,
2042                                             &name, &type);
2043
2044                 (*types)[i] = SID_NAME_UNKNOWN;
2045                 (*names)[i] = talloc_strdup(*names, "");
2046
2047                 if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2048                         /* not cached */
2049                         goto do_query;
2050                 }
2051
2052                 if (NT_STATUS_IS_OK(status)) {
2053                         have_mapped = true;
2054                         (*types)[i] = type;
2055
2056                         if (*domain_name == NULL) {
2057                                 *domain_name = dom;
2058                         } else {
2059                                 TALLOC_FREE(dom);
2060                         }
2061
2062                         (*names)[i] = name;
2063
2064                 } else if (NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED)) {
2065                         have_unmapped = true;
2066                 } else {
2067                         /* something's definitely wrong */
2068                         result = status;
2069                         goto error;
2070                 }
2071         }
2072
2073         if (!have_mapped) {
2074                 return NT_STATUS_NONE_MAPPED;
2075         }
2076         if (!have_unmapped) {
2077                 return NT_STATUS_OK;
2078         }
2079         return STATUS_SOME_UNMAPPED;
2080
2081  do_query:
2082
2083         TALLOC_FREE(*names);
2084         TALLOC_FREE(*types);
2085
2086         result = domain->backend->rids_to_names(domain, mem_ctx, domain_sid,
2087                                                 rids, num_rids, domain_name,
2088                                                 names, types);
2089
2090         if (NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
2091             NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2092                 if (!domain->internal && old_status) {
2093                         set_domain_offline(domain);
2094                 }
2095                 if (cache->tdb &&
2096                         !domain->internal &&
2097                         !domain->online &&
2098                         old_status) {
2099                         have_mapped = have_unmapped = false;
2100
2101                         *names = talloc_array(mem_ctx, char *, num_rids);
2102                         if (*names == NULL) {
2103                                 result = NT_STATUS_NO_MEMORY;
2104                                 goto error;
2105                         }
2106
2107                         *types = talloc_array(mem_ctx, enum lsa_SidType,
2108                                               num_rids);
2109                         if (*types == NULL) {
2110                                 result = NT_STATUS_NO_MEMORY;
2111                                 goto error;
2112                         }
2113
2114                         for (i=0; i<num_rids; i++) {
2115                                 struct dom_sid sid;
2116                                 NTSTATUS status;
2117                                 enum lsa_SidType type;
2118                                 char *dom, *name;
2119
2120                                 if (!sid_compose(&sid, domain_sid, rids[i])) {
2121                                         result = NT_STATUS_INTERNAL_ERROR;
2122                                         goto error;
2123                                 }
2124
2125                                 status = wcache_sid_to_name(domain, &sid,
2126                                                             *names, &dom,
2127                                                             &name, &type);
2128
2129                                 (*types)[i] = SID_NAME_UNKNOWN;
2130                                 (*names)[i] = talloc_strdup(*names, "");
2131
2132                                 if (NT_STATUS_IS_OK(status)) {
2133                                         have_mapped = true;
2134                                         (*types)[i] = type;
2135
2136                                         if (*domain_name == NULL) {
2137                                                 *domain_name = dom;
2138                                         } else {
2139                                                 TALLOC_FREE(dom);
2140                                         }
2141
2142                                         (*names)[i] = name;
2143
2144                                 } else if (NT_STATUS_EQUAL(
2145                                                    status,
2146                                                    NT_STATUS_NONE_MAPPED)) {
2147                                         have_unmapped = true;
2148                                 } else {
2149                                         /* something's definitely wrong */
2150                                         result = status;
2151                                         goto error;
2152                                 }
2153                         }
2154
2155                         if (!have_mapped) {
2156                                 return NT_STATUS_NONE_MAPPED;
2157                         }
2158                         if (!have_unmapped) {
2159                                 return NT_STATUS_OK;
2160                         }
2161                         return STATUS_SOME_UNMAPPED;
2162                 }
2163         }
2164         /*
2165           None of the queried rids has been found so save all negative entries
2166         */
2167         if (NT_STATUS_EQUAL(result, NT_STATUS_NONE_MAPPED)) {
2168                 for (i = 0; i < num_rids; i++) {
2169                         struct dom_sid sid;
2170                         const char *name = "";
2171                         const enum lsa_SidType type = SID_NAME_UNKNOWN;
2172                         NTSTATUS status = NT_STATUS_NONE_MAPPED;
2173
2174                         if (!sid_compose(&sid, domain_sid, rids[i])) {
2175                                 return NT_STATUS_INTERNAL_ERROR;
2176                         }
2177
2178                         wcache_save_sid_to_name(domain, status, &sid, *domain_name,
2179                                                 name, type);
2180                 }
2181
2182                 return result;
2183         }
2184
2185         /*
2186           Some or all of the queried rids have been found.
2187         */
2188         if (!NT_STATUS_IS_OK(result) &&
2189             !NT_STATUS_EQUAL(result, STATUS_SOME_UNMAPPED)) {
2190                 return result;
2191         }
2192
2193         refresh_sequence_number(domain);
2194
2195         for (i=0; i<num_rids; i++) {
2196                 struct dom_sid sid;
2197                 NTSTATUS status;
2198
2199                 if (!sid_compose(&sid, domain_sid, rids[i])) {
2200                         result = NT_STATUS_INTERNAL_ERROR;
2201                         goto error;
2202                 }
2203
2204                 status = (*types)[i] == SID_NAME_UNKNOWN ?
2205                         NT_STATUS_NONE_MAPPED : NT_STATUS_OK;
2206
2207                 wcache_save_sid_to_name(domain, status, &sid, *domain_name,
2208                                         (*names)[i], (*types)[i]);
2209         }
2210
2211         return result;
2212
2213  error:
2214         TALLOC_FREE(*names);
2215         TALLOC_FREE(*types);
2216         return result;
2217 }
2218
2219 static NTSTATUS wcache_query_user(struct winbindd_domain *domain,
2220                                   TALLOC_CTX *mem_ctx,
2221                                   const struct dom_sid *user_sid,
2222                                   struct wbint_userinfo *info)
2223 {
2224         struct winbind_cache *cache = get_cache(domain);
2225         struct cache_entry *centry = NULL;
2226         NTSTATUS status;
2227         struct dom_sid_buf sid_string;
2228
2229         if (cache->tdb == NULL) {
2230                 return NT_STATUS_NOT_FOUND;
2231         }
2232
2233         centry = wcache_fetch(
2234                 cache, domain, "U/%s", dom_sid_str_buf(user_sid, &sid_string));
2235         if (centry == NULL) {
2236                 return NT_STATUS_NOT_FOUND;
2237         }
2238
2239         /*
2240          * If we have an access denied cache entry and a cached info3
2241          * in the samlogon cache then do a query.  This will force the
2242          * rpc back end to return the info3 data.
2243          */
2244
2245         if (NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED) &&
2246             netsamlogon_cache_have(user_sid)) {
2247                 DEBUG(10, ("query_user: cached access denied and have cached "
2248                            "info3\n"));
2249                 domain->last_status = NT_STATUS_OK;
2250                 centry_free(centry);
2251                 return NT_STATUS_NOT_FOUND;
2252         }
2253
2254         /* if status is not ok then this is a negative hit
2255            and the rest of the data doesn't matter */
2256         status = centry->status;
2257         if (NT_STATUS_IS_OK(status)) {
2258                 info->domain_name = centry_string(centry, mem_ctx);
2259                 info->acct_name = centry_string(centry, mem_ctx);
2260                 info->full_name = centry_string(centry, mem_ctx);
2261                 info->homedir = centry_string(centry, mem_ctx);
2262                 info->shell = centry_string(centry, mem_ctx);
2263                 info->uid = centry_uint32(centry);
2264                 info->primary_gid = centry_uint32(centry);
2265                 info->primary_group_name = centry_string(centry, mem_ctx);
2266                 centry_sid(centry, &info->user_sid);
2267                 centry_sid(centry, &info->group_sid);
2268         }
2269
2270         DEBUG(10,("query_user: [Cached] - cached info for domain %s status: "
2271                   "%s\n", domain->name, nt_errstr(status) ));
2272
2273         centry_free(centry);
2274         return status;
2275 }
2276
2277
2278 /**
2279 * @brief Query a fullname from the username cache (for further gecos processing)
2280 *
2281 * @param domain         A pointer to the winbindd_domain struct.
2282 * @param mem_ctx        The talloc context.
2283 * @param user_sid       The user sid.
2284 * @param full_name      A pointer to the full_name string.
2285 *
2286 * @return NTSTATUS code
2287 */
2288 NTSTATUS wcache_query_user_fullname(struct winbindd_domain *domain,
2289                                     TALLOC_CTX *mem_ctx,
2290                                     const struct dom_sid *user_sid,
2291                                     const char **full_name)
2292 {
2293         NTSTATUS status;
2294         struct wbint_userinfo info;
2295
2296         status = wcache_query_user(domain, mem_ctx, user_sid, &info);
2297         if (!NT_STATUS_IS_OK(status)) {
2298                 return status;
2299         }
2300
2301         if (info.full_name != NULL) {
2302                 *full_name = talloc_strdup(mem_ctx, info.full_name);
2303                 if (*full_name == NULL) {
2304                         return NT_STATUS_NO_MEMORY;
2305                 }
2306         }
2307
2308         return NT_STATUS_OK;
2309 }
2310
2311 static NTSTATUS wcache_lookup_usergroups(struct winbindd_domain *domain,
2312                                          TALLOC_CTX *mem_ctx,
2313                                          const struct dom_sid *user_sid,
2314                                          uint32_t *pnum_sids,
2315                                          struct dom_sid **psids)
2316 {
2317         struct winbind_cache *cache = get_cache(domain);
2318         struct cache_entry *centry = NULL;
2319         NTSTATUS status;
2320         uint32_t i, num_sids;
2321         struct dom_sid *sids;
2322         struct dom_sid_buf sid_string;
2323
2324         if (cache->tdb == NULL) {
2325                 return NT_STATUS_NOT_FOUND;
2326         }
2327
2328         centry = wcache_fetch(
2329                 cache,
2330                 domain,
2331                 "UG/%s",
2332                 dom_sid_str_buf(user_sid, &sid_string));
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         struct dom_sid_buf 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", dom_sid_str_buf(user_sid, &sid_string));
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                 struct dom_sid_buf tmp;
2456                 sidlist = talloc_asprintf_append_buffer(
2457                         sidlist,
2458                         "/%s",
2459                         dom_sid_str_buf(&sids[i], &tmp));
2460                 if (sidlist == NULL) {
2461                         return NULL;
2462                 }
2463         }
2464         return sidlist;
2465 }
2466
2467 static NTSTATUS wcache_lookup_useraliases(struct winbindd_domain *domain,
2468                                           TALLOC_CTX *mem_ctx,
2469                                           uint32_t num_sids,
2470                                           const struct dom_sid *sids,
2471                                           uint32_t *pnum_aliases,
2472                                           uint32_t **paliases)
2473 {
2474         struct winbind_cache *cache = get_cache(domain);
2475         struct cache_entry *centry = NULL;
2476         uint32_t i, num_aliases;
2477         uint32_t *aliases;
2478         NTSTATUS status;
2479         char *sidlist;
2480
2481         if (cache->tdb == NULL) {
2482                 return NT_STATUS_NOT_FOUND;
2483         }
2484
2485         if (num_sids == 0) {
2486                 *pnum_aliases = 0;
2487                 *paliases = NULL;
2488                 return NT_STATUS_OK;
2489         }
2490
2491         /* We need to cache indexed by the whole list of SIDs, the aliases
2492          * resulting might come from any of the SIDs. */
2493
2494         sidlist = wcache_make_sidlist(talloc_tos(), num_sids, sids);
2495         if (sidlist == NULL) {
2496                 return NT_STATUS_NO_MEMORY;
2497         }
2498
2499         centry = wcache_fetch(cache, domain, "UA%s", sidlist);
2500         TALLOC_FREE(sidlist);
2501         if (centry == NULL) {
2502                 return NT_STATUS_NOT_FOUND;
2503         }
2504
2505         num_aliases = centry_uint32(centry);
2506         aliases = talloc_array(mem_ctx, uint32_t, num_aliases);
2507         if (aliases == NULL) {
2508                 centry_free(centry);
2509                 return NT_STATUS_NO_MEMORY;
2510         }
2511
2512         for (i=0; i<num_aliases; i++) {
2513                 aliases[i] = centry_uint32(centry);
2514         }
2515
2516         status = centry->status;
2517
2518         DEBUG(10,("lookup_useraliases: [Cached] - cached info for domain: %s "
2519                   "status %s\n", domain->name, nt_errstr(status)));
2520
2521         centry_free(centry);
2522
2523         *pnum_aliases = num_aliases;
2524         *paliases = aliases;
2525
2526         return status;
2527 }
2528
2529 NTSTATUS wb_cache_lookup_useraliases(struct winbindd_domain *domain,
2530                                      TALLOC_CTX *mem_ctx,
2531                                      uint32_t num_sids,
2532                                      const struct dom_sid *sids,
2533                                      uint32_t *num_aliases,
2534                                      uint32_t **alias_rids)
2535 {
2536         struct cache_entry *centry = NULL;
2537         NTSTATUS status;
2538         char *sidlist;
2539         uint32_t i;
2540         bool old_status;
2541
2542         old_status = domain->online;
2543         status = wcache_lookup_useraliases(domain, mem_ctx, num_sids, sids,
2544                                            num_aliases, alias_rids);
2545         if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2546                 return status;
2547         }
2548
2549         (*num_aliases) = 0;
2550         (*alias_rids) = NULL;
2551
2552         if (!NT_STATUS_IS_OK(domain->last_status))
2553                 return domain->last_status;
2554
2555         DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info "
2556                   "for domain %s\n", domain->name ));
2557
2558         sidlist = wcache_make_sidlist(talloc_tos(), num_sids, sids);
2559         if (sidlist == NULL) {
2560                 return NT_STATUS_NO_MEMORY;
2561         }
2562
2563         status = domain->backend->lookup_useraliases(domain, mem_ctx,
2564                                                      num_sids, sids,
2565                                                      num_aliases, alias_rids);
2566
2567         if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2568                 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2569                 if (!domain->internal && old_status) {
2570                         set_domain_offline(domain);
2571                 }
2572                 if (!domain->internal &&
2573                         !domain->online &&
2574                         old_status) {
2575                         NTSTATUS cache_status;
2576                         cache_status = wcache_lookup_useraliases(domain, mem_ctx, num_sids,
2577                                                                  sids, num_aliases, alias_rids);
2578                         return cache_status;
2579                 }
2580         }
2581         /* and save it */
2582         refresh_sequence_number(domain);
2583         if (!NT_STATUS_IS_OK(status)) {
2584                 return status;
2585         }
2586         centry = centry_start(domain, status);
2587         if (!centry)
2588                 goto skip_save;
2589         centry_put_uint32(centry, *num_aliases);
2590         for (i=0; i<(*num_aliases); i++)
2591                 centry_put_uint32(centry, (*alias_rids)[i]);
2592         centry_end(centry, "UA%s", sidlist);
2593         centry_free(centry);
2594
2595  skip_save:
2596         return status;
2597 }
2598
2599 static NTSTATUS wcache_lookup_groupmem(struct winbindd_domain *domain,
2600                                        TALLOC_CTX *mem_ctx,
2601                                        const struct dom_sid *group_sid,
2602                                        uint32_t *num_names,
2603                                        struct dom_sid **sid_mem, char ***names,
2604                                        uint32_t **name_types)
2605 {
2606         struct winbind_cache *cache = get_cache(domain);
2607         struct cache_entry *centry = NULL;
2608         NTSTATUS status;
2609         unsigned int i;
2610         struct dom_sid_buf sid_string;
2611
2612         if (cache->tdb == NULL) {
2613                 return NT_STATUS_NOT_FOUND;
2614         }
2615
2616         centry = wcache_fetch(
2617                 cache,
2618                 domain,
2619                 "GM/%s",
2620                 dom_sid_str_buf(group_sid, &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         struct dom_sid_buf 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,
2731                    "GM/%s",
2732                    dom_sid_str_buf(group_sid, &sid_string));
2733         centry_free(centry);
2734
2735 skip_save:
2736         return status;
2737 }
2738
2739 /* find the sequence number for a domain */
2740 NTSTATUS wb_cache_sequence_number(struct winbindd_domain *domain,
2741                                   uint32_t *seq)
2742 {
2743         refresh_sequence_number(domain);
2744
2745         *seq = domain->sequence_number;
2746
2747         return NT_STATUS_OK;
2748 }
2749
2750 /* enumerate trusted domains 
2751  * (we need to have the list of trustdoms in the cache when we go offline) -
2752  * Guenther */
2753 NTSTATUS wb_cache_trusted_domains(struct winbindd_domain *domain,
2754                                   TALLOC_CTX *mem_ctx,
2755                                   struct netr_DomainTrustList *trusts)
2756 {
2757         NTSTATUS status;
2758         struct winbind_cache *cache;
2759         struct winbindd_tdc_domain *dom_list = NULL;
2760         size_t num_domains = 0;
2761         bool retval = false;
2762         size_t i;
2763         bool old_status;
2764
2765         old_status = domain->online;
2766         trusts->count = 0;
2767         trusts->array = NULL;
2768
2769         cache = get_cache(domain);
2770         if (!cache || !cache->tdb) {
2771                 goto do_query;
2772         }
2773
2774         if (domain->online) {
2775                 goto do_query;
2776         }
2777
2778         retval = wcache_tdc_fetch_list(&dom_list, &num_domains);
2779         if (!retval || !num_domains || !dom_list) {
2780                 TALLOC_FREE(dom_list);
2781                 goto do_query;
2782         }
2783
2784 do_fetch_cache:
2785         trusts->array = talloc_zero_array(mem_ctx, struct netr_DomainTrust, num_domains);
2786         if (!trusts->array) {
2787                 TALLOC_FREE(dom_list);
2788                 return NT_STATUS_NO_MEMORY;
2789         }
2790
2791         for (i = 0; i < num_domains; i++) {
2792                 struct netr_DomainTrust *trust;
2793                 struct dom_sid *sid;
2794                 struct winbindd_domain *dom;
2795
2796                 dom = find_domain_from_name_noinit(dom_list[i].domain_name);
2797                 if (dom && dom->internal) {
2798                         continue;
2799                 }
2800
2801                 trust = &trusts->array[trusts->count];
2802                 trust->netbios_name = talloc_strdup(trusts->array, dom_list[i].domain_name);
2803                 trust->dns_name = talloc_strdup(trusts->array, dom_list[i].dns_name);
2804                 sid = talloc(trusts->array, struct dom_sid);
2805                 if (!trust->netbios_name || !trust->dns_name ||
2806                         !sid) {
2807                         TALLOC_FREE(dom_list);
2808                         TALLOC_FREE(trusts->array);
2809                         return NT_STATUS_NO_MEMORY;
2810                 }
2811
2812                 trust->trust_flags = dom_list[i].trust_flags;
2813                 trust->trust_attributes = dom_list[i].trust_attribs;
2814                 trust->trust_type = dom_list[i].trust_type;
2815                 sid_copy(sid, &dom_list[i].sid);
2816                 trust->sid = sid;
2817                 trusts->count++;
2818         }
2819
2820         TALLOC_FREE(dom_list);
2821         return NT_STATUS_OK;
2822
2823 do_query:
2824         /* Return status value returned by seq number check */
2825
2826         if (!NT_STATUS_IS_OK(domain->last_status))
2827                 return domain->last_status;
2828
2829         DEBUG(10,("trusted_domains: [Cached] - doing backend query for info for domain %s\n",
2830                 domain->name ));
2831
2832         status = domain->backend->trusted_domains(domain, mem_ctx, trusts);
2833
2834         if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2835                 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2836                 if (!domain->internal && old_status) {
2837                         set_domain_offline(domain);
2838                 }
2839                 if (!domain->internal &&
2840                         !domain->online &&
2841                         old_status) {
2842                         retval = wcache_tdc_fetch_list(&dom_list, &num_domains);
2843                         if (retval && num_domains && dom_list) {
2844                                 TALLOC_FREE(trusts->array);
2845                                 trusts->count = 0;
2846                                 goto do_fetch_cache;
2847                         }
2848                 }
2849         }
2850         /* no trusts gives NT_STATUS_NO_MORE_ENTRIES resetting to NT_STATUS_OK
2851          * so that the generic centry handling still applies correctly -
2852          * Guenther*/
2853
2854         if (!NT_STATUS_IS_ERR(status)) {
2855                 status = NT_STATUS_OK;
2856         }
2857         return status;
2858 }       
2859
2860 /* get lockout policy */
2861 NTSTATUS wb_cache_lockout_policy(struct winbindd_domain *domain,
2862                                  TALLOC_CTX *mem_ctx,
2863                                  struct samr_DomInfo12 *policy)
2864 {
2865         struct winbind_cache *cache = get_cache(domain);
2866         struct cache_entry *centry = NULL;
2867         NTSTATUS status;
2868         bool old_status;
2869
2870         old_status = domain->online;
2871         if (!cache->tdb)
2872                 goto do_query;
2873
2874         centry = wcache_fetch(cache, domain, "LOC_POL/%s", domain->name);
2875
2876         if (!centry)
2877                 goto do_query;
2878
2879 do_fetch_cache:
2880         policy->lockout_duration = centry_nttime(centry);
2881         policy->lockout_window = centry_nttime(centry);
2882         policy->lockout_threshold = centry_uint16(centry);
2883
2884         status = centry->status;
2885
2886         DEBUG(10,("lockout_policy: [Cached] - cached info for domain %s status: %s\n",
2887                 domain->name, nt_errstr(status) ));
2888
2889         centry_free(centry);
2890         return status;
2891
2892 do_query:
2893         ZERO_STRUCTP(policy);
2894
2895         /* Return status value returned by seq number check */
2896
2897         if (!NT_STATUS_IS_OK(domain->last_status))
2898                 return domain->last_status;
2899
2900         DEBUG(10,("lockout_policy: [Cached] - doing backend query for info for domain %s\n",
2901                 domain->name ));
2902
2903         status = domain->backend->lockout_policy(domain, mem_ctx, policy);
2904
2905         if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2906                 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2907                 if (!domain->internal && old_status) {
2908                         set_domain_offline(domain);
2909                 }
2910                 if (cache->tdb &&
2911                         !domain->internal &&
2912                         !domain->online &&
2913                         old_status) {
2914                         centry = wcache_fetch(cache, domain, "LOC_POL/%s", domain->name);
2915                         if (centry) {
2916                                 goto do_fetch_cache;
2917                         }
2918                 }
2919         }
2920         /* and save it */
2921         refresh_sequence_number(domain);
2922         if (!NT_STATUS_IS_OK(status)) {
2923                 return status;
2924         }
2925         wcache_save_lockout_policy(domain, status, policy);
2926
2927         return status;
2928 }
2929
2930 /* get password policy */
2931 NTSTATUS wb_cache_password_policy(struct winbindd_domain *domain,
2932                                   TALLOC_CTX *mem_ctx,
2933                                   struct samr_DomInfo1 *policy)
2934 {
2935         struct winbind_cache *cache = get_cache(domain);
2936         struct cache_entry *centry = NULL;
2937         NTSTATUS status;
2938         bool old_status;
2939
2940         old_status = domain->online;
2941         if (!cache->tdb)
2942                 goto do_query;
2943
2944         centry = wcache_fetch(cache, domain, "PWD_POL/%s", domain->name);
2945
2946         if (!centry)
2947                 goto do_query;
2948
2949 do_fetch_cache:
2950         policy->min_password_length = centry_uint16(centry);
2951         policy->password_history_length = centry_uint16(centry);
2952         policy->password_properties = centry_uint32(centry);
2953         policy->max_password_age = centry_nttime(centry);
2954         policy->min_password_age = centry_nttime(centry);
2955
2956         status = centry->status;
2957
2958         DEBUG(10,("lockout_policy: [Cached] - cached info for domain %s status: %s\n",
2959                 domain->name, nt_errstr(status) ));
2960
2961         centry_free(centry);
2962         return status;
2963
2964 do_query:
2965         ZERO_STRUCTP(policy);
2966
2967         /* Return status value returned by seq number check */
2968
2969         if (!NT_STATUS_IS_OK(domain->last_status))
2970                 return domain->last_status;
2971
2972         DEBUG(10,("password_policy: [Cached] - doing backend query for info for domain %s\n",
2973                 domain->name ));
2974
2975         status = domain->backend->password_policy(domain, mem_ctx, policy);
2976
2977         if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2978                 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2979                 if (!domain->internal && old_status) {
2980                         set_domain_offline(domain);
2981                 }
2982                 if (cache->tdb &&
2983                         !domain->internal &&
2984                         !domain->online &&
2985                         old_status) {
2986                         centry = wcache_fetch(cache, domain, "PWD_POL/%s", domain->name);
2987                         if (centry) {
2988                                 goto do_fetch_cache;
2989                         }
2990                 }
2991         }
2992         /* and save it */
2993         refresh_sequence_number(domain);
2994         if (!NT_STATUS_IS_OK(status)) {
2995                 return status;
2996         }
2997         wcache_save_password_policy(domain, status, policy);
2998
2999         return status;
3000 }
3001
3002
3003 /* Invalidate cached user and group lists coherently */
3004
3005 static int traverse_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf, 
3006                        void *state)
3007 {
3008         if (strncmp((const char *)kbuf.dptr, "UL/", 3) == 0 ||
3009             strncmp((const char *)kbuf.dptr, "GL/", 3) == 0)
3010                 tdb_delete(the_tdb, kbuf);
3011
3012         return 0;
3013 }
3014
3015 /* Invalidate the getpwnam and getgroups entries for a winbindd domain */
3016
3017 void wcache_invalidate_samlogon(struct winbindd_domain *domain, 
3018                                 const struct dom_sid *sid)
3019 {
3020         fstring key_str;
3021         struct dom_sid_buf sid_string;
3022         struct winbind_cache *cache;
3023
3024         /* don't clear cached U/SID and UG/SID entries when we want to logon
3025          * offline - gd */
3026
3027         if (lp_winbind_offline_logon()) {
3028                 return;
3029         }
3030
3031         if (!domain)
3032                 return;
3033
3034         cache = get_cache(domain);
3035
3036         if (!cache->tdb) {
3037                 return;
3038         }
3039
3040         /* Clear U/SID cache entry */
3041         fstr_sprintf(key_str, "U/%s", dom_sid_str_buf(sid, &sid_string));
3042         DEBUG(10, ("wcache_invalidate_samlogon: clearing %s\n", key_str));
3043         tdb_delete(cache->tdb, string_tdb_data(key_str));
3044
3045         /* Clear UG/SID cache entry */
3046         fstr_sprintf(key_str, "UG/%s", dom_sid_str_buf(sid, &sid_string));
3047         DEBUG(10, ("wcache_invalidate_samlogon: clearing %s\n", key_str));
3048         tdb_delete(cache->tdb, string_tdb_data(key_str));
3049
3050         /* Samba/winbindd never needs this. */
3051         netsamlogon_clear_cached_user(sid);
3052 }
3053
3054 bool wcache_invalidate_cache(void)
3055 {
3056         struct winbindd_domain *domain;
3057
3058         for (domain = domain_list(); domain; domain = domain->next) {
3059                 struct winbind_cache *cache = get_cache(domain);
3060
3061                 DEBUG(10, ("wcache_invalidate_cache: invalidating cache "
3062                            "entries for %s\n", domain->name));
3063                 if (cache) {
3064                         if (cache->tdb) {
3065                                 tdb_traverse(cache->tdb, traverse_fn, NULL);
3066                         } else {
3067                                 return false;
3068                         }
3069                 }
3070         }
3071         return true;
3072 }
3073
3074 bool wcache_invalidate_cache_noinit(void)
3075 {