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