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