librpc/idl: Merge wbint.idl with winbind.idl so we can forward IRPC requests to inter...
[gd/samba-autobuild/.git] / source3 / winbindd / winbindd_cache.c
1 /* 
2    Unix SMB/CIFS implementation.
3
4    Winbind cache backend functions
5
6    Copyright (C) Andrew Tridgell 2001
7    Copyright (C) Gerald Carter   2003-2007
8    Copyright (C) Volker Lendecke 2005
9    Copyright (C) Guenther Deschner 2005
10    Copyright (C) Michael Adam    2007
11
12    This program is free software; you can redistribute it and/or modify
13    it under the terms of the GNU General Public License as published by
14    the Free Software Foundation; either version 3 of the License, or
15    (at your option) any later version.
16
17    This program is distributed in the hope that it will be useful,
18    but WITHOUT ANY WARRANTY; without even the implied warranty of
19    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20    GNU General Public License for more details.
21
22    You should have received a copy of the GNU General Public License
23    along with this program.  If not, see <http://www.gnu.org/licenses/>.
24 */
25
26 #include "includes.h"
27 #include "system/filesys.h"
28 #include "winbindd.h"
29 #include "tdb_validate.h"
30 #include "../libcli/auth/libcli_auth.h"
31 #include "../librpc/gen_ndr/ndr_winbind.h"
32 #include "ads.h"
33 #include "nss_info.h"
34 #include "../libcli/security/security.h"
35 #include "passdb/machine_sid.h"
36 #include "util_tdb.h"
37
38 #undef DBGC_CLASS
39 #define DBGC_CLASS DBGC_WINBIND
40
41 #define WINBINDD_CACHE_VER1 1 /* initial db version */
42 #define WINBINDD_CACHE_VER2 2 /* second version with timeouts for NDR entries */
43
44 #define WINBINDD_CACHE_VERSION WINBINDD_CACHE_VER2
45 #define WINBINDD_CACHE_VERSION_KEYSTR "WINBINDD_CACHE_VERSION"
46
47 extern struct winbindd_methods reconnect_methods;
48 #ifdef HAVE_ADS
49 extern struct winbindd_methods ads_methods;
50 #endif
51 extern struct winbindd_methods builtin_passdb_methods;
52 extern struct winbindd_methods sam_passdb_methods;
53
54 /*
55  * JRA. KEEP THIS LIST UP TO DATE IF YOU ADD CACHE ENTRIES.
56  * Here are the list of entry types that are *not* stored
57  * as form struct cache_entry in the cache.
58  */
59
60 static const char *non_centry_keys[] = {
61         "SEQNUM/",
62         "WINBINDD_OFFLINE",
63         WINBINDD_CACHE_VERSION_KEYSTR,
64         NULL
65 };
66
67 /************************************************************************
68  Is this key a non-centry type ?
69 ************************************************************************/
70
71 static bool is_non_centry_key(TDB_DATA kbuf)
72 {
73         int i;
74
75         if (kbuf.dptr == NULL || kbuf.dsize == 0) {
76                 return false;
77         }
78         for (i = 0; non_centry_keys[i] != NULL; i++) {
79                 size_t namelen = strlen(non_centry_keys[i]);
80                 if (kbuf.dsize < namelen) {
81                         continue;
82                 }
83                 if (strncmp(non_centry_keys[i], (const char *)kbuf.dptr, namelen) == 0) {
84                         return true;
85                 }
86         }
87         return false;
88 }
89
90 /* Global online/offline state - False when online. winbindd starts up online
91    and sets this to true if the first query fails and there's an entry in
92    the cache tdb telling us to stay offline. */
93
94 static bool global_winbindd_offline_state;
95
96 struct winbind_cache {
97         TDB_CONTEXT *tdb;
98 };
99
100 struct cache_entry {
101         NTSTATUS status;
102         uint32 sequence_number;
103         uint64_t timeout;
104         uint8 *data;
105         uint32 len, ofs;
106 };
107
108 void (*smb_panic_fn)(const char *const why) = smb_panic;
109
110 #define WINBINDD_MAX_CACHE_SIZE (50*1024*1024)
111
112 static struct winbind_cache *wcache;
113
114 /* get the winbind_cache structure */
115 static struct winbind_cache *get_cache(struct winbindd_domain *domain)
116 {
117         struct winbind_cache *ret = wcache;
118
119         /* We have to know what type of domain we are dealing with first. */
120
121         if (domain->internal) {
122                 domain->backend = &builtin_passdb_methods;
123         }
124
125         if (dom_sid_equal(&domain->sid, &global_sid_Builtin)) {
126                 domain->initialized = true;
127         }
128
129         if (strequal(domain->name, get_global_sam_name()) &&
130             sid_check_is_our_sam(&domain->sid)) {
131                 domain->backend = &sam_passdb_methods;
132         }
133
134         if ( !domain->initialized ) {
135                 init_dc_connection( domain );
136         }
137
138         /* 
139            OK.  listen up becasue I'm only going to say this once.
140            We have the following scenarios to consider
141            (a) trusted AD domains on a Samba DC,
142            (b) trusted AD domains and we are joined to a non-kerberos domain
143            (c) trusted AD domains and we are joined to a kerberos (AD) domain
144
145            For (a) we can always contact the trusted domain using krb5 
146            since we have the domain trust account password
147
148            For (b) we can only use RPC since we have no way of 
149            getting a krb5 ticket in our own domain
150
151            For (c) we can always use krb5 since we have a kerberos trust
152
153            --jerry
154          */
155
156         if (!domain->backend) {
157 #ifdef HAVE_ADS
158                 struct winbindd_domain *our_domain = domain;
159
160                 /* find our domain first so we can figure out if we 
161                    are joined to a kerberized domain */
162
163                 if ( !domain->primary )
164                         our_domain = find_our_domain();
165
166                 if ((our_domain->active_directory || IS_DC)
167                     && domain->active_directory
168                     && !lp_winbind_rpc_only()) {
169                         DEBUG(5,("get_cache: Setting ADS methods for domain %s\n", domain->name));
170                         domain->backend = &ads_methods;
171                 } else {
172 #endif  /* HAVE_ADS */
173                         DEBUG(5,("get_cache: Setting MS-RPC methods for domain %s\n", domain->name));
174                         domain->backend = &reconnect_methods;
175 #ifdef HAVE_ADS
176                 }
177 #endif  /* HAVE_ADS */
178         }
179
180         if (ret)
181                 return ret;
182
183         ret = SMB_XMALLOC_P(struct winbind_cache);
184         ZERO_STRUCTP(ret);
185
186         wcache = ret;
187         wcache_flush_cache();
188
189         return ret;
190 }
191
192 /*
193   free a centry structure
194 */
195 static void centry_free(struct cache_entry *centry)
196 {
197         if (!centry)
198                 return;
199         SAFE_FREE(centry->data);
200         free(centry);
201 }
202
203 static bool centry_check_bytes(struct cache_entry *centry, size_t nbytes)
204 {
205         if (centry->len - centry->ofs < nbytes) {
206                 DEBUG(0,("centry corruption? needed %u bytes, have %d\n", 
207                          (unsigned int)nbytes,
208                          centry->len - centry->ofs));
209                 return false;
210         }
211         return true;
212 }
213
214 /*
215   pull a uint64_t from a cache entry
216 */
217 static uint64_t centry_uint64_t(struct cache_entry *centry)
218 {
219         uint64_t ret;
220
221         if (!centry_check_bytes(centry, 8)) {
222                 smb_panic_fn("centry_uint64_t");
223         }
224         ret = BVAL(centry->data, centry->ofs);
225         centry->ofs += 8;
226         return ret;
227 }
228
229 /*
230   pull a uint32 from a cache entry 
231 */
232 static uint32 centry_uint32(struct cache_entry *centry)
233 {
234         uint32 ret;
235
236         if (!centry_check_bytes(centry, 4)) {
237                 smb_panic_fn("centry_uint32");
238         }
239         ret = IVAL(centry->data, centry->ofs);
240         centry->ofs += 4;
241         return ret;
242 }
243
244 /*
245   pull a uint16 from a cache entry 
246 */
247 static uint16 centry_uint16(struct cache_entry *centry)
248 {
249         uint16 ret;
250         if (!centry_check_bytes(centry, 2)) {
251                 smb_panic_fn("centry_uint16");
252         }
253         ret = SVAL(centry->data, centry->ofs);
254         centry->ofs += 2;
255         return ret;
256 }
257
258 /*
259   pull a uint8 from a cache entry 
260 */
261 static uint8 centry_uint8(struct cache_entry *centry)
262 {
263         uint8 ret;
264         if (!centry_check_bytes(centry, 1)) {
265                 smb_panic_fn("centry_uint8");
266         }
267         ret = CVAL(centry->data, centry->ofs);
268         centry->ofs += 1;
269         return ret;
270 }
271
272 /*
273   pull a NTTIME from a cache entry 
274 */
275 static NTTIME centry_nttime(struct cache_entry *centry)
276 {
277         NTTIME ret;
278         if (!centry_check_bytes(centry, 8)) {
279                 smb_panic_fn("centry_nttime");
280         }
281         ret = IVAL(centry->data, centry->ofs);
282         centry->ofs += 4;
283         ret += (uint64)IVAL(centry->data, centry->ofs) << 32;
284         centry->ofs += 4;
285         return ret;
286 }
287
288 /*
289   pull a time_t from a cache entry. time_t stored portably as a 64-bit time.
290 */
291 static time_t centry_time(struct cache_entry *centry)
292 {
293         return (time_t)centry_nttime(centry);
294 }
295
296 /* pull a string from a cache entry, using the supplied
297    talloc context 
298 */
299 static char *centry_string(struct cache_entry *centry, TALLOC_CTX *mem_ctx)
300 {
301         uint32 len;
302         char *ret;
303
304         len = centry_uint8(centry);
305
306         if (len == 0xFF) {
307                 /* a deliberate NULL string */
308                 return NULL;
309         }
310
311         if (!centry_check_bytes(centry, (size_t)len)) {
312                 smb_panic_fn("centry_string");
313         }
314
315         ret = talloc_array(mem_ctx, char, len+1);
316         if (!ret) {
317                 smb_panic_fn("centry_string out of memory\n");
318         }
319         memcpy(ret,centry->data + centry->ofs, len);
320         ret[len] = 0;
321         centry->ofs += len;
322         return ret;
323 }
324
325 /* pull a hash16 from a cache entry, using the supplied
326    talloc context 
327 */
328 static char *centry_hash16(struct cache_entry *centry, TALLOC_CTX *mem_ctx)
329 {
330         uint32 len;
331         char *ret;
332
333         len = centry_uint8(centry);
334
335         if (len != 16) {
336                 DEBUG(0,("centry corruption? hash len (%u) != 16\n", 
337                         len ));
338                 return NULL;
339         }
340
341         if (!centry_check_bytes(centry, 16)) {
342                 return NULL;
343         }
344
345         ret = talloc_array(mem_ctx, char, 16);
346         if (!ret) {
347                 smb_panic_fn("centry_hash out of memory\n");
348         }
349         memcpy(ret,centry->data + centry->ofs, 16);
350         centry->ofs += 16;
351         return ret;
352 }
353
354 /* pull a sid from a cache entry, using the supplied
355    talloc context 
356 */
357 static bool centry_sid(struct cache_entry *centry, struct dom_sid *sid)
358 {
359         char *sid_string;
360         bool ret;
361
362         sid_string = centry_string(centry, talloc_tos());
363         if (sid_string == NULL) {
364                 return false;
365         }
366         ret = string_to_sid(sid, sid_string);
367         TALLOC_FREE(sid_string);
368         return ret;
369 }
370
371
372 /*
373   pull a NTSTATUS from a cache entry
374 */
375 static NTSTATUS centry_ntstatus(struct cache_entry *centry)
376 {
377         NTSTATUS status;
378
379         status = NT_STATUS(centry_uint32(centry));
380         return status;
381 }
382
383
384 /* the server is considered down if it can't give us a sequence number */
385 static bool wcache_server_down(struct winbindd_domain *domain)
386 {
387         bool ret;
388
389         if (!wcache->tdb)
390                 return false;
391
392         ret = (domain->sequence_number == DOM_SEQUENCE_NONE);
393
394         if (ret)
395                 DEBUG(10,("wcache_server_down: server for Domain %s down\n", 
396                         domain->name ));
397         return ret;
398 }
399
400 static bool wcache_fetch_seqnum(const char *domain_name, uint32_t *seqnum,
401                                 uint32_t *last_seq_check)
402 {
403         char *key;
404         TDB_DATA data;
405
406         if (wcache->tdb == NULL) {
407                 DEBUG(10,("wcache_fetch_seqnum: tdb == NULL\n"));
408                 return false;
409         }
410
411         key = talloc_asprintf(talloc_tos(), "SEQNUM/%s", domain_name);
412         if (key == NULL) {
413                 DEBUG(10, ("talloc failed\n"));
414                 return false;
415         }
416
417         data = tdb_fetch_bystring(wcache->tdb, key);
418         TALLOC_FREE(key);
419
420         if (data.dptr == NULL) {
421                 DEBUG(10, ("wcache_fetch_seqnum: %s not found\n",
422                            domain_name));
423                 return false;
424         }
425         if (data.dsize != 8) {
426                 DEBUG(10, ("wcache_fetch_seqnum: invalid data size %d\n",
427                            (int)data.dsize));
428                 SAFE_FREE(data.dptr);
429                 return false;
430         }
431
432         *seqnum = IVAL(data.dptr, 0);
433         *last_seq_check = IVAL(data.dptr, 4);
434         SAFE_FREE(data.dptr);
435
436         return true;
437 }
438
439 static NTSTATUS fetch_cache_seqnum( struct winbindd_domain *domain, time_t now )
440 {
441         uint32 last_check, time_diff;
442
443         if (!wcache_fetch_seqnum(domain->name, &domain->sequence_number,
444                                  &last_check)) {
445                 return NT_STATUS_UNSUCCESSFUL;
446         }
447         domain->last_seq_check = last_check;
448
449         /* have we expired? */
450
451         time_diff = now - domain->last_seq_check;
452         if ( time_diff > lp_winbind_cache_time() ) {
453                 DEBUG(10,("fetch_cache_seqnum: timeout [%s][%u @ %u]\n",
454                         domain->name, domain->sequence_number,
455                         (uint32)domain->last_seq_check));
456                 return NT_STATUS_UNSUCCESSFUL;
457         }
458
459         DEBUG(10,("fetch_cache_seqnum: success [%s][%u @ %u]\n", 
460                 domain->name, domain->sequence_number, 
461                 (uint32)domain->last_seq_check));
462
463         return NT_STATUS_OK;
464 }
465
466 bool wcache_store_seqnum(const char *domain_name, uint32_t seqnum,
467                          time_t last_seq_check)
468 {
469         char *key_str;
470         uint8_t buf[8];
471         int ret;
472
473         if (wcache->tdb == NULL) {
474                 DEBUG(10, ("wcache_store_seqnum: wcache->tdb == NULL\n"));
475                 return false;
476         }
477
478         key_str = talloc_asprintf(talloc_tos(), "SEQNUM/%s", domain_name);
479         if (key_str == NULL) {
480                 DEBUG(10, ("talloc_asprintf failed\n"));
481                 return false;
482         }
483
484         SIVAL(buf, 0, seqnum);
485         SIVAL(buf, 4, last_seq_check);
486
487         ret = tdb_store_bystring(wcache->tdb, key_str,
488                                  make_tdb_data(buf, sizeof(buf)), TDB_REPLACE);
489         TALLOC_FREE(key_str);
490         if (ret != 0) {
491                 DEBUG(10, ("tdb_store_bystring failed: %s\n",
492                            tdb_errorstr_compat(wcache->tdb)));
493                 TALLOC_FREE(key_str);
494                 return false;
495         }
496
497         DEBUG(10, ("wcache_store_seqnum: success [%s][%u @ %u]\n",
498                    domain_name, seqnum, (unsigned)last_seq_check));
499
500         return true;
501 }
502
503 static bool store_cache_seqnum( struct winbindd_domain *domain )
504 {
505         return wcache_store_seqnum(domain->name, domain->sequence_number,
506                                    domain->last_seq_check);
507 }
508
509 /*
510   refresh the domain sequence number. If force is true
511   then always refresh it, no matter how recently we fetched it
512 */
513
514 static void refresh_sequence_number(struct winbindd_domain *domain, bool force)
515 {
516         NTSTATUS status;
517         unsigned time_diff;
518         time_t t = time(NULL);
519         unsigned cache_time = lp_winbind_cache_time();
520
521         if (is_domain_offline(domain)) {
522                 return;
523         }
524
525         get_cache( domain );
526
527 #if 0   /* JERRY -- disable as the default cache time is now 5 minutes */
528         /* trying to reconnect is expensive, don't do it too often */
529         if (domain->sequence_number == DOM_SEQUENCE_NONE) {
530                 cache_time *= 8;
531         }
532 #endif
533
534         time_diff = t - domain->last_seq_check;
535
536         /* see if we have to refetch the domain sequence number */
537         if (!force && (time_diff < cache_time) &&
538                         (domain->sequence_number != DOM_SEQUENCE_NONE) &&
539                         NT_STATUS_IS_OK(domain->last_status)) {
540                 DEBUG(10, ("refresh_sequence_number: %s time ok\n", domain->name));
541                 goto done;
542         }
543
544         /* try to get the sequence number from the tdb cache first */
545         /* this will update the timestamp as well */
546
547         status = fetch_cache_seqnum( domain, t );
548         if (NT_STATUS_IS_OK(status) &&
549                         (domain->sequence_number != DOM_SEQUENCE_NONE) &&
550                         NT_STATUS_IS_OK(domain->last_status)) {
551                 goto done;
552         }
553
554         /* important! make sure that we know if this is a native 
555            mode domain or not.  And that we can contact it. */
556
557         if ( winbindd_can_contact_domain( domain ) ) {          
558                 status = domain->backend->sequence_number(domain, 
559                                                           &domain->sequence_number);
560         } else {
561                 /* just use the current time */
562                 status = NT_STATUS_OK;
563                 domain->sequence_number = time(NULL);
564         }
565
566
567         /* the above call could have set our domain->backend to NULL when
568          * coming from offline to online mode, make sure to reinitialize the
569          * backend - Guenther */
570         get_cache( domain );
571
572         if (!NT_STATUS_IS_OK(status)) {
573                 DEBUG(10,("refresh_sequence_number: failed with %s\n", nt_errstr(status)));
574                 domain->sequence_number = DOM_SEQUENCE_NONE;
575         }
576
577         domain->last_status = status;
578         domain->last_seq_check = time(NULL);
579
580         /* save the new sequence number in the cache */
581         store_cache_seqnum( domain );
582
583 done:
584         DEBUG(10, ("refresh_sequence_number: %s seq number is now %d\n", 
585                    domain->name, domain->sequence_number));
586
587         return;
588 }
589
590 /*
591   decide if a cache entry has expired
592 */
593 static bool centry_expired(struct winbindd_domain *domain, const char *keystr, struct cache_entry *centry)
594 {
595         /* If we've been told to be offline - stay in that state... */
596         if (lp_winbind_offline_logon() && global_winbindd_offline_state) {
597                 DEBUG(10,("centry_expired: Key %s for domain %s valid as winbindd is globally offline.\n",
598                         keystr, domain->name ));
599                 return false;
600         }
601
602         /* when the domain is offline return the cached entry.
603          * This deals with transient offline states... */
604
605         if (!domain->online) {
606                 DEBUG(10,("centry_expired: Key %s for domain %s valid as domain is offline.\n",
607                         keystr, domain->name ));
608                 return false;
609         }
610
611         /* if the server is OK and our cache entry came from when it was down then
612            the entry is invalid */
613         if ((domain->sequence_number != DOM_SEQUENCE_NONE) &&  
614             (centry->sequence_number == DOM_SEQUENCE_NONE)) {
615                 DEBUG(10,("centry_expired: Key %s for domain %s invalid sequence.\n",
616                         keystr, domain->name ));
617                 return true;
618         }
619
620         /* if the server is down or the cache entry is not older than the
621            current sequence number or it did not timeout then it is OK */
622         if (wcache_server_down(domain)
623             || ((centry->sequence_number == domain->sequence_number)
624                 && (centry->timeout > time(NULL)))) {
625                 DEBUG(10,("centry_expired: Key %s for domain %s is good.\n",
626                         keystr, domain->name ));
627                 return false;
628         }
629
630         DEBUG(10,("centry_expired: Key %s for domain %s expired\n",
631                 keystr, domain->name ));
632
633         /* it's expired */
634         return true;
635 }
636
637 static struct cache_entry *wcache_fetch_raw(char *kstr)
638 {
639         TDB_DATA data;
640         struct cache_entry *centry;
641         TDB_DATA key;
642
643         key = string_tdb_data(kstr);
644         data = tdb_fetch_compat(wcache->tdb, key);
645         if (!data.dptr) {
646                 /* a cache miss */
647                 return NULL;
648         }
649
650         centry = SMB_XMALLOC_P(struct cache_entry);
651         centry->data = (unsigned char *)data.dptr;
652         centry->len = data.dsize;
653         centry->ofs = 0;
654
655         if (centry->len < 16) {
656                 /* huh? corrupt cache? */
657                 DEBUG(10,("wcache_fetch_raw: Corrupt cache for key %s "
658                           "(len < 16)?\n", kstr));
659                 centry_free(centry);
660                 return NULL;
661         }
662
663         centry->status = centry_ntstatus(centry);
664         centry->sequence_number = centry_uint32(centry);
665         centry->timeout = centry_uint64_t(centry);
666
667         return centry;
668 }
669
670 static bool is_my_own_sam_domain(struct winbindd_domain *domain)
671 {
672         if (strequal(domain->name, get_global_sam_name()) &&
673             sid_check_is_our_sam(&domain->sid)) {
674                 return true;
675         }
676
677         return false;
678 }
679
680 static bool is_builtin_domain(struct winbindd_domain *domain)
681 {
682         if (strequal(domain->name, "BUILTIN") &&
683             sid_check_is_builtin(&domain->sid)) {
684                 return true;
685         }
686
687         return false;
688 }
689
690 /*
691   fetch an entry from the cache, with a varargs key. auto-fetch the sequence
692   number and return status
693 */
694 static struct cache_entry *wcache_fetch(struct winbind_cache *cache, 
695                                         struct winbindd_domain *domain,
696                                         const char *format, ...) PRINTF_ATTRIBUTE(3,4);
697 static struct cache_entry *wcache_fetch(struct winbind_cache *cache, 
698                                         struct winbindd_domain *domain,
699                                         const char *format, ...)
700 {
701         va_list ap;
702         char *kstr;
703         struct cache_entry *centry;
704
705         if (!winbindd_use_cache() ||
706             is_my_own_sam_domain(domain) ||
707             is_builtin_domain(domain)) {
708                 return NULL;
709         }
710
711         refresh_sequence_number(domain, false);
712
713         va_start(ap, format);
714         smb_xvasprintf(&kstr, format, ap);
715         va_end(ap);
716
717         centry = wcache_fetch_raw(kstr);
718         if (centry == NULL) {
719                 free(kstr);
720                 return NULL;
721         }
722
723         if (centry_expired(domain, kstr, centry)) {
724
725                 DEBUG(10,("wcache_fetch: entry %s expired for domain %s\n",
726                          kstr, domain->name ));
727
728                 centry_free(centry);
729                 free(kstr);
730                 return NULL;
731         }
732
733         DEBUG(10,("wcache_fetch: returning entry %s for domain %s\n",
734                  kstr, domain->name ));
735
736         free(kstr);
737         return centry;
738 }
739
740 static void wcache_delete(const char *format, ...) PRINTF_ATTRIBUTE(1,2);
741 static void wcache_delete(const char *format, ...)
742 {
743         va_list ap;
744         char *kstr;
745         TDB_DATA key;
746
747         va_start(ap, format);
748         smb_xvasprintf(&kstr, format, ap);
749         va_end(ap);
750
751         key = string_tdb_data(kstr);
752
753         tdb_delete(wcache->tdb, key);
754         free(kstr);
755 }
756
757 /*
758   make sure we have at least len bytes available in a centry 
759 */
760 static void centry_expand(struct cache_entry *centry, uint32 len)
761 {
762         if (centry->len - centry->ofs >= len)
763                 return;
764         centry->len *= 2;
765         centry->data = SMB_REALLOC_ARRAY(centry->data, unsigned char,
766                                          centry->len);
767         if (!centry->data) {
768                 DEBUG(0,("out of memory: needed %d bytes in centry_expand\n", centry->len));
769                 smb_panic_fn("out of memory in centry_expand");
770         }
771 }
772
773 /*
774   push a uint64_t into a centry
775 */
776 static void centry_put_uint64_t(struct cache_entry *centry, uint64_t v)
777 {
778         centry_expand(centry, 8);
779         SBVAL(centry->data, centry->ofs, v);
780         centry->ofs += 8;
781 }
782
783 /*
784   push a uint32 into a centry 
785 */
786 static void centry_put_uint32(struct cache_entry *centry, uint32 v)
787 {
788         centry_expand(centry, 4);
789         SIVAL(centry->data, centry->ofs, v);
790         centry->ofs += 4;
791 }
792
793 /*
794   push a uint16 into a centry 
795 */
796 static void centry_put_uint16(struct cache_entry *centry, uint16 v)
797 {
798         centry_expand(centry, 2);
799         SSVAL(centry->data, centry->ofs, v);
800         centry->ofs += 2;
801 }
802
803 /*
804   push a uint8 into a centry 
805 */
806 static void centry_put_uint8(struct cache_entry *centry, uint8 v)
807 {
808         centry_expand(centry, 1);
809         SCVAL(centry->data, centry->ofs, v);
810         centry->ofs += 1;
811 }
812
813 /* 
814    push a string into a centry 
815  */
816 static void centry_put_string(struct cache_entry *centry, const char *s)
817 {
818         int len;
819
820         if (!s) {
821                 /* null strings are marked as len 0xFFFF */
822                 centry_put_uint8(centry, 0xFF);
823                 return;
824         }
825
826         len = strlen(s);
827         /* can't handle more than 254 char strings. Truncating is probably best */
828         if (len > 254) {
829                 DEBUG(10,("centry_put_string: truncating len (%d) to: 254\n", len));
830                 len = 254;
831         }
832         centry_put_uint8(centry, len);
833         centry_expand(centry, len);
834         memcpy(centry->data + centry->ofs, s, len);
835         centry->ofs += len;
836 }
837
838 /* 
839    push a 16 byte hash into a centry - treat as 16 byte string.
840  */
841 static void centry_put_hash16(struct cache_entry *centry, const uint8 val[16])
842 {
843         centry_put_uint8(centry, 16);
844         centry_expand(centry, 16);
845         memcpy(centry->data + centry->ofs, val, 16);
846         centry->ofs += 16;
847 }
848
849 static void centry_put_sid(struct cache_entry *centry, const struct dom_sid *sid)
850 {
851         fstring sid_string;
852         centry_put_string(centry, sid_to_fstring(sid_string, sid));
853 }
854
855
856 /*
857   put NTSTATUS into a centry
858 */
859 static void centry_put_ntstatus(struct cache_entry *centry, NTSTATUS status)
860 {
861         uint32 status_value = NT_STATUS_V(status);
862         centry_put_uint32(centry, status_value);
863 }
864
865
866 /*
867   push a NTTIME into a centry 
868 */
869 static void centry_put_nttime(struct cache_entry *centry, NTTIME nt)
870 {
871         centry_expand(centry, 8);
872         SIVAL(centry->data, centry->ofs, nt & 0xFFFFFFFF);
873         centry->ofs += 4;
874         SIVAL(centry->data, centry->ofs, nt >> 32);
875         centry->ofs += 4;
876 }
877
878 /*
879   push a time_t into a centry - use a 64 bit size.
880   NTTIME here is being used as a convenient 64-bit size.
881 */
882 static void centry_put_time(struct cache_entry *centry, time_t t)
883 {
884         NTTIME nt = (NTTIME)t;
885         centry_put_nttime(centry, nt);
886 }
887
888 /*
889   start a centry for output. When finished, call centry_end()
890 */
891 static struct cache_entry *centry_start(struct winbindd_domain *domain,
892                                         NTSTATUS status)
893 {
894         struct cache_entry *centry;
895
896         if (!wcache->tdb)
897                 return NULL;
898
899         centry = SMB_XMALLOC_P(struct cache_entry);
900
901         centry->len = 8192; /* reasonable default */
902         centry->data = SMB_XMALLOC_ARRAY(uint8, centry->len);
903         centry->ofs = 0;
904         centry->sequence_number = domain->sequence_number;
905         centry->timeout = lp_winbind_cache_time() + time(NULL);
906         centry_put_ntstatus(centry, status);
907         centry_put_uint32(centry, centry->sequence_number);
908         centry_put_uint64_t(centry, centry->timeout);
909         return centry;
910 }
911
912 /*
913   finish a centry and write it to the tdb
914 */
915 static void centry_end(struct cache_entry *centry, const char *format, ...) PRINTF_ATTRIBUTE(2,3);
916 static void centry_end(struct cache_entry *centry, const char *format, ...)
917 {
918         va_list ap;
919         char *kstr;
920         TDB_DATA key, data;
921
922         if (!winbindd_use_cache()) {
923                 return;
924         }
925
926         va_start(ap, format);
927         smb_xvasprintf(&kstr, format, ap);
928         va_end(ap);
929
930         key = string_tdb_data(kstr);
931         data.dptr = centry->data;
932         data.dsize = centry->ofs;
933
934         tdb_store(wcache->tdb, key, data, TDB_REPLACE);
935         free(kstr);
936 }
937
938 static void wcache_save_name_to_sid(struct winbindd_domain *domain, 
939                                     NTSTATUS status, const char *domain_name,
940                                     const char *name, const struct dom_sid *sid,
941                                     enum lsa_SidType type)
942 {
943         struct cache_entry *centry;
944         fstring uname;
945
946         centry = centry_start(domain, status);
947         if (!centry)
948                 return;
949
950         if ((domain_name == NULL) || (domain_name[0] == '\0')) {
951                 struct winbindd_domain *mydomain =
952                         find_domain_from_sid_noinit(sid);
953                 if (mydomain != NULL) {
954                         domain_name = mydomain->name;
955                 }
956         }
957
958         centry_put_uint32(centry, type);
959         centry_put_sid(centry, sid);
960         fstrcpy(uname, name);
961         (void)strupper_m(uname);
962         centry_end(centry, "NS/%s/%s", domain_name, uname);
963         DEBUG(10,("wcache_save_name_to_sid: %s\\%s -> %s (%s)\n", domain_name,
964                   uname, sid_string_dbg(sid), nt_errstr(status)));
965         centry_free(centry);
966 }
967
968 static void wcache_save_sid_to_name(struct winbindd_domain *domain, NTSTATUS status, 
969                                     const struct dom_sid *sid, const char *domain_name, const char *name, enum lsa_SidType type)
970 {
971         struct cache_entry *centry;
972         fstring sid_string;
973
974         centry = centry_start(domain, status);
975         if (!centry)
976                 return;
977
978         if ((domain_name == NULL) || (domain_name[0] == '\0')) {
979                 struct winbindd_domain *mydomain =
980                         find_domain_from_sid_noinit(sid);
981                 if (mydomain != NULL) {
982                         domain_name = mydomain->name;
983                 }
984         }
985
986         if (NT_STATUS_IS_OK(status)) {
987                 centry_put_uint32(centry, type);
988                 centry_put_string(centry, domain_name);
989                 centry_put_string(centry, name);
990         }
991
992         centry_end(centry, "SN/%s", sid_to_fstring(sid_string, sid));
993         DEBUG(10,("wcache_save_sid_to_name: %s -> %s\\%s (%s)\n", sid_string,
994                   domain_name, name, nt_errstr(status)));
995         centry_free(centry);
996 }
997
998
999 static void wcache_save_user(struct winbindd_domain *domain, NTSTATUS status,
1000                              struct wbint_userinfo *info)
1001 {
1002         struct cache_entry *centry;
1003         fstring sid_string;
1004
1005         if (is_null_sid(&info->user_sid)) {
1006                 return;
1007         }
1008
1009         centry = centry_start(domain, status);
1010         if (!centry)
1011                 return;
1012         centry_put_string(centry, info->acct_name);
1013         centry_put_string(centry, info->full_name);
1014         centry_put_string(centry, info->homedir);
1015         centry_put_string(centry, info->shell);
1016         centry_put_uint32(centry, info->primary_gid);
1017         centry_put_sid(centry, &info->user_sid);
1018         centry_put_sid(centry, &info->group_sid);
1019         centry_end(centry, "U/%s", sid_to_fstring(sid_string,
1020                                                   &info->user_sid));
1021         DEBUG(10,("wcache_save_user: %s (acct_name %s)\n", sid_string, info->acct_name));
1022         centry_free(centry);
1023 }
1024
1025 static void wcache_save_lockout_policy(struct winbindd_domain *domain,
1026                                        NTSTATUS status,
1027                                        struct samr_DomInfo12 *lockout_policy)
1028 {
1029         struct cache_entry *centry;
1030
1031         centry = centry_start(domain, status);
1032         if (!centry)
1033                 return;
1034
1035         centry_put_nttime(centry, lockout_policy->lockout_duration);
1036         centry_put_nttime(centry, lockout_policy->lockout_window);
1037         centry_put_uint16(centry, lockout_policy->lockout_threshold);
1038
1039         centry_end(centry, "LOC_POL/%s", domain->name);
1040
1041         DEBUG(10,("wcache_save_lockout_policy: %s\n", domain->name));
1042
1043         centry_free(centry);
1044 }
1045
1046
1047
1048 static void wcache_save_password_policy(struct winbindd_domain *domain,
1049                                         NTSTATUS status,
1050                                         struct samr_DomInfo1 *policy)
1051 {
1052         struct cache_entry *centry;
1053
1054         centry = centry_start(domain, status);
1055         if (!centry)
1056                 return;
1057
1058         centry_put_uint16(centry, policy->min_password_length);
1059         centry_put_uint16(centry, policy->password_history_length);
1060         centry_put_uint32(centry, policy->password_properties);
1061         centry_put_nttime(centry, policy->max_password_age);
1062         centry_put_nttime(centry, policy->min_password_age);
1063
1064         centry_end(centry, "PWD_POL/%s", domain->name);
1065
1066         DEBUG(10,("wcache_save_password_policy: %s\n", domain->name));
1067
1068         centry_free(centry);
1069 }
1070
1071 /***************************************************************************
1072  ***************************************************************************/
1073
1074 static void wcache_save_username_alias(struct winbindd_domain *domain,
1075                                        NTSTATUS status,
1076                                        const char *name, const char *alias)
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, alias );
1085
1086         fstrcpy(uname, name);
1087         (void)strupper_m(uname);
1088         centry_end(centry, "NSS/NA/%s", uname);
1089
1090         DEBUG(10,("wcache_save_username_alias: %s -> %s\n", name, alias ));
1091
1092         centry_free(centry);
1093 }
1094
1095 static void wcache_save_alias_username(struct winbindd_domain *domain,
1096                                        NTSTATUS status,
1097                                        const char *alias, const char *name)
1098 {
1099         struct cache_entry *centry;
1100         fstring uname;
1101
1102         if ( (centry = centry_start(domain, status)) == NULL )
1103                 return;
1104
1105         centry_put_string( centry, name );
1106
1107         fstrcpy(uname, alias);
1108         (void)strupper_m(uname);
1109         centry_end(centry, "NSS/AN/%s", uname);
1110
1111         DEBUG(10,("wcache_save_alias_username: %s -> %s\n", alias, name ));
1112
1113         centry_free(centry);
1114 }
1115
1116 /***************************************************************************
1117  ***************************************************************************/
1118
1119 NTSTATUS resolve_username_to_alias( TALLOC_CTX *mem_ctx,
1120                                     struct winbindd_domain *domain,
1121                                     const char *name, char **alias )
1122 {
1123         struct winbind_cache *cache = get_cache(domain);
1124         struct cache_entry *centry = NULL;
1125         NTSTATUS status;
1126         char *upper_name;
1127
1128         if ( domain->internal )
1129                 return NT_STATUS_NOT_SUPPORTED;
1130
1131         if (!cache->tdb)
1132                 goto do_query;
1133
1134         upper_name = talloc_strdup(mem_ctx, name);
1135         if (upper_name == NULL) {
1136                 return NT_STATUS_NO_MEMORY;
1137         }
1138         if (!strupper_m(upper_name)) {
1139                 talloc_free(upper_name);
1140                 return NT_STATUS_INVALID_PARAMETER;
1141         }
1142
1143         centry = wcache_fetch(cache, domain, "NSS/NA/%s", upper_name);
1144
1145         talloc_free(upper_name);
1146
1147         if (!centry)
1148                 goto do_query;
1149
1150         status = centry->status;
1151
1152         if (!NT_STATUS_IS_OK(status)) {
1153                 centry_free(centry);
1154                 return status;
1155         }
1156
1157         *alias = centry_string( centry, mem_ctx );
1158
1159         centry_free(centry);
1160
1161         DEBUG(10,("resolve_username_to_alias: [Cached] - mapped %s to %s\n",
1162                   name, *alias ? *alias : "(none)"));
1163
1164         return (*alias) ? NT_STATUS_OK : NT_STATUS_OBJECT_NAME_NOT_FOUND;
1165
1166 do_query:
1167
1168         /* If its not in cache and we are offline, then fail */
1169
1170         if ( get_global_winbindd_state_offline() || !domain->online ) {
1171                 DEBUG(8,("resolve_username_to_alias: rejecting query "
1172                          "in offline mode\n"));
1173                 return NT_STATUS_NOT_FOUND;
1174         }
1175
1176         status = nss_map_to_alias( mem_ctx, domain->name, name, alias );
1177
1178         if ( NT_STATUS_IS_OK( status ) ) {
1179                 wcache_save_username_alias(domain, status, name, *alias);
1180         }
1181
1182         if ( NT_STATUS_EQUAL( status, NT_STATUS_NONE_MAPPED ) ) {
1183                 wcache_save_username_alias(domain, status, name, "(NULL)");
1184         }
1185
1186         DEBUG(5,("resolve_username_to_alias: backend query returned %s\n",
1187                  nt_errstr(status)));
1188
1189         if ( NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ) {
1190                 set_domain_offline( domain );
1191         }
1192
1193         return status;
1194 }
1195
1196 /***************************************************************************
1197  ***************************************************************************/
1198
1199 NTSTATUS resolve_alias_to_username( TALLOC_CTX *mem_ctx,
1200                                     struct winbindd_domain *domain,
1201                                     const char *alias, char **name )
1202 {
1203         struct winbind_cache *cache = get_cache(domain);
1204         struct cache_entry *centry = NULL;
1205         NTSTATUS status;
1206         char *upper_name;
1207
1208         if ( domain->internal )
1209                 return  NT_STATUS_NOT_SUPPORTED;
1210
1211         if (!cache->tdb)
1212                 goto do_query;
1213
1214         upper_name = talloc_strdup(mem_ctx, alias);
1215         if (upper_name == NULL) {
1216                 return NT_STATUS_NO_MEMORY;
1217         }
1218         if (!strupper_m(upper_name)) {
1219                 talloc_free(upper_name);
1220                 return NT_STATUS_INVALID_PARAMETER;
1221         }
1222
1223         centry = wcache_fetch(cache, domain, "NSS/AN/%s", upper_name);
1224
1225         talloc_free(upper_name);
1226
1227         if (!centry)
1228                 goto do_query;
1229
1230         status = centry->status;
1231
1232         if (!NT_STATUS_IS_OK(status)) {
1233                 centry_free(centry);
1234                 return status;
1235         }
1236
1237         *name = centry_string( centry, mem_ctx );
1238
1239         centry_free(centry);
1240
1241         DEBUG(10,("resolve_alias_to_username: [Cached] - mapped %s to %s\n",
1242                   alias, *name ? *name : "(none)"));
1243
1244         return (*name) ? NT_STATUS_OK : NT_STATUS_OBJECT_NAME_NOT_FOUND;
1245
1246 do_query:
1247
1248         /* If its not in cache and we are offline, then fail */
1249
1250         if ( get_global_winbindd_state_offline() || !domain->online ) {
1251                 DEBUG(8,("resolve_alias_to_username: rejecting query "
1252                          "in offline mode\n"));
1253                 return NT_STATUS_NOT_FOUND;
1254         }
1255
1256         /* an alias cannot contain a domain prefix or '@' */
1257
1258         if (strchr(alias, '\\') || strchr(alias, '@')) {
1259                 DEBUG(10,("resolve_alias_to_username: skipping fully "
1260                           "qualified name %s\n", alias));
1261                 return NT_STATUS_OBJECT_NAME_INVALID;
1262         }
1263
1264         status = nss_map_from_alias( mem_ctx, domain->name, alias, name );
1265
1266         if ( NT_STATUS_IS_OK( status ) ) {
1267                 wcache_save_alias_username( domain, status, alias, *name );
1268         }
1269
1270         if (NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED)) {
1271                 wcache_save_alias_username(domain, status, alias, "(NULL)");
1272         }
1273
1274         DEBUG(5,("resolve_alias_to_username: backend query returned %s\n",
1275                  nt_errstr(status)));
1276
1277         if ( NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ) {
1278                 set_domain_offline( domain );
1279         }
1280
1281         return status;
1282 }
1283
1284 NTSTATUS wcache_cached_creds_exist(struct winbindd_domain *domain, const struct dom_sid *sid)
1285 {
1286         struct winbind_cache *cache = get_cache(domain);
1287         TDB_DATA data;
1288         fstring key_str, tmp;
1289         uint32 rid;
1290
1291         if (!cache->tdb) {
1292                 return NT_STATUS_INTERNAL_DB_ERROR;
1293         }
1294
1295         if (is_null_sid(sid)) {
1296                 return NT_STATUS_INVALID_SID;
1297         }
1298
1299         if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
1300                 return NT_STATUS_INVALID_SID;
1301         }
1302
1303         fstr_sprintf(key_str, "CRED/%s", sid_to_fstring(tmp, sid));
1304
1305         data = tdb_fetch_compat(cache->tdb, string_tdb_data(key_str));
1306         if (!data.dptr) {
1307                 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1308         }
1309
1310         SAFE_FREE(data.dptr);
1311         return NT_STATUS_OK;
1312 }
1313
1314 /* Lookup creds for a SID - copes with old (unsalted) creds as well
1315    as new salted ones. */
1316
1317 NTSTATUS wcache_get_creds(struct winbindd_domain *domain, 
1318                           TALLOC_CTX *mem_ctx, 
1319                           const struct dom_sid *sid,
1320                           const uint8 **cached_nt_pass,
1321                           const uint8 **cached_salt)
1322 {
1323         struct winbind_cache *cache = get_cache(domain);
1324         struct cache_entry *centry = NULL;
1325         NTSTATUS status;
1326         uint32 rid;
1327         fstring tmp;
1328
1329         if (!cache->tdb) {
1330                 return NT_STATUS_INTERNAL_DB_ERROR;
1331         }
1332
1333         if (is_null_sid(sid)) {
1334                 return NT_STATUS_INVALID_SID;
1335         }
1336
1337         if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
1338                 return NT_STATUS_INVALID_SID;
1339         }
1340
1341         /* Try and get a salted cred first. If we can't
1342            fall back to an unsalted cred. */
1343
1344         centry = wcache_fetch(cache, domain, "CRED/%s",
1345                               sid_to_fstring(tmp, sid));
1346         if (!centry) {
1347                 DEBUG(10,("wcache_get_creds: entry for [CRED/%s] not found\n", 
1348                           sid_string_dbg(sid)));
1349                 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1350         }
1351
1352         /*
1353          * We don't use the time element at this moment,
1354          * but we have to consume it, so that we don't
1355          * neet to change the disk format of the cache.
1356          */
1357         (void)centry_time(centry);
1358
1359         /* In the salted case this isn't actually the nt_hash itself,
1360            but the MD5 of the salt + nt_hash. Let the caller
1361            sort this out. It can tell as we only return the cached_salt
1362            if we are returning a salted cred. */
1363
1364         *cached_nt_pass = (const uint8 *)centry_hash16(centry, mem_ctx);
1365         if (*cached_nt_pass == NULL) {
1366                 fstring sidstr;
1367
1368                 sid_to_fstring(sidstr, sid);
1369
1370                 /* Bad (old) cred cache. Delete and pretend we
1371                    don't have it. */
1372                 DEBUG(0,("wcache_get_creds: bad entry for [CRED/%s] - deleting\n", 
1373                                 sidstr));
1374                 wcache_delete("CRED/%s", sidstr);
1375                 centry_free(centry);
1376                 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1377         }
1378
1379         /* We only have 17 bytes more data in the salted cred case. */
1380         if (centry->len - centry->ofs == 17) {
1381                 *cached_salt = (const uint8 *)centry_hash16(centry, mem_ctx);
1382         } else {
1383                 *cached_salt = NULL;
1384         }
1385
1386         dump_data_pw("cached_nt_pass", *cached_nt_pass, NT_HASH_LEN);
1387         if (*cached_salt) {
1388                 dump_data_pw("cached_salt", *cached_salt, NT_HASH_LEN);
1389         }
1390
1391         status = centry->status;
1392
1393         DEBUG(10,("wcache_get_creds: [Cached] - cached creds for user %s status: %s\n",
1394                   sid_string_dbg(sid), nt_errstr(status) ));
1395
1396         centry_free(centry);
1397         return status;
1398 }
1399
1400 /* Store creds for a SID - only writes out new salted ones. */
1401
1402 NTSTATUS wcache_save_creds(struct winbindd_domain *domain, 
1403                            const struct dom_sid *sid,
1404                            const uint8 nt_pass[NT_HASH_LEN])
1405 {
1406         struct cache_entry *centry;
1407         fstring sid_string;
1408         uint32 rid;
1409         uint8 cred_salt[NT_HASH_LEN];
1410         uint8 salted_hash[NT_HASH_LEN];
1411
1412         if (is_null_sid(sid)) {
1413                 return NT_STATUS_INVALID_SID;
1414         }
1415
1416         if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
1417                 return NT_STATUS_INVALID_SID;
1418         }
1419
1420         centry = centry_start(domain, NT_STATUS_OK);
1421         if (!centry) {
1422                 return NT_STATUS_INTERNAL_DB_ERROR;
1423         }
1424
1425         dump_data_pw("nt_pass", nt_pass, NT_HASH_LEN);
1426
1427         centry_put_time(centry, time(NULL));
1428
1429         /* Create a salt and then salt the hash. */
1430         generate_random_buffer(cred_salt, NT_HASH_LEN);
1431         E_md5hash(cred_salt, nt_pass, salted_hash);
1432
1433         centry_put_hash16(centry, salted_hash);
1434         centry_put_hash16(centry, cred_salt);
1435         centry_end(centry, "CRED/%s", sid_to_fstring(sid_string, sid));
1436
1437         DEBUG(10,("wcache_save_creds: %s\n", sid_string));
1438
1439         centry_free(centry);
1440
1441         return NT_STATUS_OK;
1442 }
1443
1444
1445 /* Query display info. This is the basic user list fn */
1446 static NTSTATUS query_user_list(struct winbindd_domain *domain,
1447                                 TALLOC_CTX *mem_ctx,
1448                                 uint32 *num_entries, 
1449                                 struct wbint_userinfo **info)
1450 {
1451         struct winbind_cache *cache = get_cache(domain);
1452         struct cache_entry *centry = NULL;
1453         NTSTATUS status;
1454         unsigned int i, retry;
1455         bool old_status = domain->online;
1456
1457         if (!cache->tdb)
1458                 goto do_query;
1459
1460         centry = wcache_fetch(cache, domain, "UL/%s", domain->name);
1461         if (!centry)
1462                 goto do_query;
1463
1464 do_fetch_cache:
1465         *num_entries = centry_uint32(centry);
1466
1467         if (*num_entries == 0)
1468                 goto do_cached;
1469
1470         (*info) = talloc_array(mem_ctx, struct wbint_userinfo, *num_entries);
1471         if (! (*info)) {
1472                 smb_panic_fn("query_user_list out of memory");
1473         }
1474         for (i=0; i<(*num_entries); i++) {
1475                 (*info)[i].acct_name = centry_string(centry, mem_ctx);
1476                 (*info)[i].full_name = centry_string(centry, mem_ctx);
1477                 (*info)[i].homedir = centry_string(centry, mem_ctx);
1478                 (*info)[i].shell = centry_string(centry, mem_ctx);
1479                 centry_sid(centry, &(*info)[i].user_sid);
1480                 centry_sid(centry, &(*info)[i].group_sid);
1481         }
1482
1483 do_cached:      
1484         status = centry->status;
1485
1486         DEBUG(10,("query_user_list: [Cached] - cached list for domain %s status: %s\n",
1487                 domain->name, nt_errstr(status) ));
1488
1489         centry_free(centry);
1490         return status;
1491
1492 do_query:
1493         *num_entries = 0;
1494         *info = NULL;
1495
1496         /* Return status value returned by seq number check */
1497
1498         if (!NT_STATUS_IS_OK(domain->last_status))
1499                 return domain->last_status;
1500
1501         /* Put the query_user_list() in a retry loop.  There appears to be
1502          * some bug either with Windows 2000 or Samba's handling of large
1503          * rpc replies.  This manifests itself as sudden disconnection
1504          * at a random point in the enumeration of a large (60k) user list.
1505          * The retry loop simply tries the operation again. )-:  It's not
1506          * pretty but an acceptable workaround until we work out what the
1507          * real problem is. */
1508
1509         retry = 0;
1510         do {
1511
1512                 DEBUG(10,("query_user_list: [Cached] - doing backend query for list for domain %s\n",
1513                         domain->name ));
1514
1515                 status = domain->backend->query_user_list(domain, mem_ctx, num_entries, info);
1516                 if (!NT_STATUS_IS_OK(status)) {
1517                         DEBUG(3, ("query_user_list: returned 0x%08x, "
1518                                   "retrying\n", NT_STATUS_V(status)));
1519                 }
1520                 if (NT_STATUS_EQUAL(status, NT_STATUS_UNSUCCESSFUL)) {
1521                         DEBUG(3, ("query_user_list: flushing "
1522                                   "connection cache\n"));
1523                         invalidate_cm_connection(&domain->conn);
1524                 }
1525                 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1526                     NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1527                         if (!domain->internal && old_status) {
1528                                 set_domain_offline(domain);
1529                         }
1530                         /* store partial response. */
1531                         if (*num_entries > 0) {
1532                                 /*
1533                                  * humm, what about the status used for cache?
1534                                  * Should it be NT_STATUS_OK?
1535                                  */
1536                                 break;
1537                         }
1538                         /*
1539                          * domain is offline now, and there is no user entries,
1540                          * try to fetch from cache again.
1541                          */
1542                         if (cache->tdb && !domain->online && !domain->internal && old_status) {
1543                                 centry = wcache_fetch(cache, domain, "UL/%s", domain->name);
1544                                 /* partial response... */
1545                                 if (!centry) {
1546                                         goto skip_save;
1547                                 } else {
1548                                         goto do_fetch_cache;
1549                                 }
1550                         } else {
1551                                 goto skip_save;
1552                         }
1553                 }
1554
1555         } while (NT_STATUS_V(status) == NT_STATUS_V(NT_STATUS_UNSUCCESSFUL) && 
1556                  (retry++ < 5));
1557
1558         /* and save it */
1559         refresh_sequence_number(domain, false);
1560         if (!NT_STATUS_IS_OK(status)) {
1561                 return status;
1562         }
1563         centry = centry_start(domain, status);
1564         if (!centry)
1565                 goto skip_save;
1566         centry_put_uint32(centry, *num_entries);
1567         for (i=0; i<(*num_entries); i++) {
1568                 centry_put_string(centry, (*info)[i].acct_name);
1569                 centry_put_string(centry, (*info)[i].full_name);
1570                 centry_put_string(centry, (*info)[i].homedir);
1571                 centry_put_string(centry, (*info)[i].shell);
1572                 centry_put_sid(centry, &(*info)[i].user_sid);
1573                 centry_put_sid(centry, &(*info)[i].group_sid);
1574                 if (domain->backend && domain->backend->consistent) {
1575                         /* when the backend is consistent we can pre-prime some mappings */
1576                         wcache_save_name_to_sid(domain, NT_STATUS_OK, 
1577                                                 domain->name,
1578                                                 (*info)[i].acct_name, 
1579                                                 &(*info)[i].user_sid,
1580                                                 SID_NAME_USER);
1581                         wcache_save_sid_to_name(domain, NT_STATUS_OK, 
1582                                                 &(*info)[i].user_sid,
1583                                                 domain->name,
1584                                                 (*info)[i].acct_name, 
1585                                                 SID_NAME_USER);
1586                         wcache_save_user(domain, NT_STATUS_OK, &(*info)[i]);
1587                 }
1588         }       
1589         centry_end(centry, "UL/%s", domain->name);
1590         centry_free(centry);
1591
1592 skip_save:
1593         return status;
1594 }
1595
1596 /* list all domain groups */
1597 static NTSTATUS enum_dom_groups(struct winbindd_domain *domain,
1598                                 TALLOC_CTX *mem_ctx,
1599                                 uint32 *num_entries, 
1600                                 struct wb_acct_info **info)
1601 {
1602         struct winbind_cache *cache = get_cache(domain);
1603         struct cache_entry *centry = NULL;
1604         NTSTATUS status;
1605         unsigned int i;
1606         bool old_status;
1607
1608         old_status = domain->online;
1609         if (!cache->tdb)
1610                 goto do_query;
1611
1612         centry = wcache_fetch(cache, domain, "GL/%s/domain", domain->name);
1613         if (!centry)
1614                 goto do_query;
1615
1616 do_fetch_cache:
1617         *num_entries = centry_uint32(centry);
1618
1619         if (*num_entries == 0)
1620                 goto do_cached;
1621
1622         (*info) = talloc_array(mem_ctx, struct wb_acct_info, *num_entries);
1623         if (! (*info)) {
1624                 smb_panic_fn("enum_dom_groups out of memory");
1625         }
1626         for (i=0; i<(*num_entries); i++) {
1627                 fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx));
1628                 fstrcpy((*info)[i].acct_desc, centry_string(centry, mem_ctx));
1629                 (*info)[i].rid = centry_uint32(centry);
1630         }
1631
1632 do_cached:      
1633         status = centry->status;
1634
1635         DEBUG(10,("enum_dom_groups: [Cached] - cached list for domain %s status: %s\n",
1636                 domain->name, nt_errstr(status) ));
1637
1638         centry_free(centry);
1639         return status;
1640
1641 do_query:
1642         *num_entries = 0;
1643         *info = NULL;
1644
1645         /* Return status value returned by seq number check */
1646
1647         if (!NT_STATUS_IS_OK(domain->last_status))
1648                 return domain->last_status;
1649
1650         DEBUG(10,("enum_dom_groups: [Cached] - doing backend query for list for domain %s\n",
1651                 domain->name ));
1652
1653         status = domain->backend->enum_dom_groups(domain, mem_ctx, num_entries, info);
1654
1655         if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1656             NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1657                 if (!domain->internal && old_status) {
1658                         set_domain_offline(domain);
1659                 }
1660                 if (cache->tdb &&
1661                         !domain->online &&
1662                         !domain->internal &&
1663                         old_status) {
1664                         centry = wcache_fetch(cache, domain, "GL/%s/domain", domain->name);
1665                         if (centry) {
1666                                 goto do_fetch_cache;
1667                         }
1668                 }
1669         }
1670         /* and save it */
1671         refresh_sequence_number(domain, false);
1672         if (!NT_STATUS_IS_OK(status)) {
1673                 return status;
1674         }
1675         centry = centry_start(domain, status);
1676         if (!centry)
1677                 goto skip_save;
1678         centry_put_uint32(centry, *num_entries);
1679         for (i=0; i<(*num_entries); i++) {
1680                 centry_put_string(centry, (*info)[i].acct_name);
1681                 centry_put_string(centry, (*info)[i].acct_desc);
1682                 centry_put_uint32(centry, (*info)[i].rid);
1683         }       
1684         centry_end(centry, "GL/%s/domain", domain->name);
1685         centry_free(centry);
1686
1687 skip_save:
1688         return status;
1689 }
1690
1691 /* list all domain groups */
1692 static NTSTATUS enum_local_groups(struct winbindd_domain *domain,
1693                                 TALLOC_CTX *mem_ctx,
1694                                 uint32 *num_entries, 
1695                                 struct wb_acct_info **info)
1696 {
1697         struct winbind_cache *cache = get_cache(domain);
1698         struct cache_entry *centry = NULL;
1699         NTSTATUS status;
1700         unsigned int i;
1701         bool old_status;
1702
1703         old_status = domain->online;
1704         if (!cache->tdb)
1705                 goto do_query;
1706
1707         centry = wcache_fetch(cache, domain, "GL/%s/local", domain->name);
1708         if (!centry)
1709                 goto do_query;
1710
1711 do_fetch_cache:
1712         *num_entries = centry_uint32(centry);
1713
1714         if (*num_entries == 0)
1715                 goto do_cached;
1716
1717         (*info) = talloc_array(mem_ctx, struct wb_acct_info, *num_entries);
1718         if (! (*info)) {
1719                 smb_panic_fn("enum_dom_groups out of memory");
1720         }
1721         for (i=0; i<(*num_entries); i++) {
1722                 fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx));
1723                 fstrcpy((*info)[i].acct_desc, centry_string(centry, mem_ctx));
1724                 (*info)[i].rid = centry_uint32(centry);
1725         }
1726
1727 do_cached:      
1728
1729         /* If we are returning cached data and the domain controller
1730            is down then we don't know whether the data is up to date
1731            or not.  Return NT_STATUS_MORE_PROCESSING_REQUIRED to
1732            indicate this. */
1733
1734         if (wcache_server_down(domain)) {
1735                 DEBUG(10, ("enum_local_groups: returning cached user list and server was down\n"));
1736                 status = NT_STATUS_MORE_PROCESSING_REQUIRED;
1737         } else
1738                 status = centry->status;
1739
1740         DEBUG(10,("enum_local_groups: [Cached] - cached list for domain %s status: %s\n",
1741                 domain->name, nt_errstr(status) ));
1742
1743         centry_free(centry);
1744         return status;
1745
1746 do_query:
1747         *num_entries = 0;
1748         *info = NULL;
1749
1750         /* Return status value returned by seq number check */
1751
1752         if (!NT_STATUS_IS_OK(domain->last_status))
1753                 return domain->last_status;
1754
1755         DEBUG(10,("enum_local_groups: [Cached] - doing backend query for list for domain %s\n",
1756                 domain->name ));
1757
1758         status = domain->backend->enum_local_groups(domain, mem_ctx, num_entries, info);
1759
1760         if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1761                 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1762                 if (!domain->internal && old_status) {
1763                         set_domain_offline(domain);
1764                 }
1765                 if (cache->tdb &&
1766                         !domain->internal &&
1767                         !domain->online &&
1768                         old_status) {
1769                         centry = wcache_fetch(cache, domain, "GL/%s/local", domain->name);
1770                         if (centry) {
1771                                 goto do_fetch_cache;
1772                         }
1773                 }
1774         }
1775         /* and save it */
1776         refresh_sequence_number(domain, false);
1777         if (!NT_STATUS_IS_OK(status)) {
1778                 return status;
1779         }
1780         centry = centry_start(domain, status);
1781         if (!centry)
1782                 goto skip_save;
1783         centry_put_uint32(centry, *num_entries);
1784         for (i=0; i<(*num_entries); i++) {
1785                 centry_put_string(centry, (*info)[i].acct_name);
1786                 centry_put_string(centry, (*info)[i].acct_desc);
1787                 centry_put_uint32(centry, (*info)[i].rid);
1788         }
1789         centry_end(centry, "GL/%s/local", domain->name);
1790         centry_free(centry);
1791
1792 skip_save:
1793         return status;
1794 }
1795
1796 NTSTATUS wcache_name_to_sid(struct winbindd_domain *domain,
1797                             const char *domain_name,
1798                             const char *name,
1799                             struct dom_sid *sid,
1800                             enum lsa_SidType *type)
1801 {
1802         struct winbind_cache *cache = get_cache(domain);
1803         struct cache_entry *centry;
1804         NTSTATUS status;
1805         char *uname;
1806
1807         if (cache->tdb == NULL) {
1808                 return NT_STATUS_NOT_FOUND;
1809         }
1810
1811         uname = talloc_strdup_upper(talloc_tos(), name);
1812         if (uname == NULL) {
1813                 return NT_STATUS_NO_MEMORY;
1814         }
1815
1816         if ((domain_name == NULL) || (domain_name[0] == '\0')) {
1817                 domain_name = domain->name;
1818         }
1819
1820         centry = wcache_fetch(cache, domain, "NS/%s/%s", domain_name, uname);
1821         TALLOC_FREE(uname);
1822         if (centry == NULL) {
1823                 return NT_STATUS_NOT_FOUND;
1824         }
1825
1826         status = centry->status;
1827         if (NT_STATUS_IS_OK(status)) {
1828                 *type = (enum lsa_SidType)centry_uint32(centry);
1829                 centry_sid(centry, sid);
1830         }
1831
1832         DEBUG(10,("name_to_sid: [Cached] - cached name for domain %s status: "
1833                   "%s\n", domain->name, nt_errstr(status) ));
1834
1835         centry_free(centry);
1836         return status;
1837 }
1838
1839 /* convert a single name to a sid in a domain */
1840 static NTSTATUS name_to_sid(struct winbindd_domain *domain,
1841                             TALLOC_CTX *mem_ctx,
1842                             const char *domain_name,
1843                             const char *name,
1844                             uint32_t flags,
1845                             struct dom_sid *sid,
1846                             enum lsa_SidType *type)
1847 {
1848         NTSTATUS status;
1849         bool old_status;
1850
1851         old_status = domain->online;
1852
1853         status = wcache_name_to_sid(domain, domain_name, name, sid, type);
1854         if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
1855                 return status;
1856         }
1857
1858         ZERO_STRUCTP(sid);
1859
1860         /* If the seq number check indicated that there is a problem
1861          * with this DC, then return that status... except for
1862          * access_denied.  This is special because the dc may be in
1863          * "restrict anonymous = 1" mode, in which case it will deny
1864          * most unauthenticated operations, but *will* allow the LSA
1865          * name-to-sid that we try as a fallback. */
1866
1867         if (!(NT_STATUS_IS_OK(domain->last_status)
1868               || NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)))
1869                 return domain->last_status;
1870
1871         DEBUG(10,("name_to_sid: [Cached] - doing backend query for name for domain %s\n",
1872                 domain->name ));
1873
1874         status = domain->backend->name_to_sid(domain, mem_ctx, domain_name,
1875                                               name, flags, sid, type);
1876
1877         if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1878                 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1879                 if (!domain->internal && old_status) {
1880                         set_domain_offline(domain);
1881                 }
1882                 if (!domain->internal &&
1883                         !domain->online &&
1884                         old_status) {
1885                         NTSTATUS cache_status;
1886                         cache_status = wcache_name_to_sid(domain, domain_name, name, sid, type);
1887                         return cache_status;
1888                 }
1889         }
1890         /* and save it */
1891         refresh_sequence_number(domain, false);
1892
1893         if (domain->online &&
1894             (NT_STATUS_IS_OK(status) || NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED))) {
1895                 wcache_save_name_to_sid(domain, status, domain_name, name, sid, *type);
1896
1897                 /* Only save the reverse mapping if this was not a UPN */
1898                 if (!strchr(name, '@')) {
1899                         if (!strupper_m(discard_const_p(char, domain_name))) {
1900                                 return NT_STATUS_INVALID_PARAMETER;
1901                         }
1902                         (void)strlower_m(discard_const_p(char, name));
1903                         wcache_save_sid_to_name(domain, status, sid, domain_name, name, *type);
1904                 }
1905         }
1906
1907         return status;
1908 }
1909
1910 NTSTATUS wcache_sid_to_name(struct winbindd_domain *domain,
1911                             const struct dom_sid *sid,
1912                             TALLOC_CTX *mem_ctx,
1913                             char **domain_name,
1914                             char **name,
1915                             enum lsa_SidType *type)
1916 {
1917         struct winbind_cache *cache = get_cache(domain);
1918         struct cache_entry *centry;
1919         char *sid_string;
1920         NTSTATUS status;
1921
1922         if (cache->tdb == NULL) {
1923                 return NT_STATUS_NOT_FOUND;
1924         }
1925
1926         sid_string = sid_string_tos(sid);
1927         if (sid_string == NULL) {
1928                 return NT_STATUS_NO_MEMORY;
1929         }
1930
1931         centry = wcache_fetch(cache, domain, "SN/%s", sid_string);
1932         TALLOC_FREE(sid_string);
1933         if (centry == NULL) {
1934                 return NT_STATUS_NOT_FOUND;
1935         }
1936
1937         if (NT_STATUS_IS_OK(centry->status)) {
1938                 *type = (enum lsa_SidType)centry_uint32(centry);
1939                 *domain_name = centry_string(centry, mem_ctx);
1940                 *name = centry_string(centry, mem_ctx);
1941         }
1942
1943         status = centry->status;
1944         centry_free(centry);
1945
1946         DEBUG(10,("sid_to_name: [Cached] - cached name for domain %s status: "
1947                   "%s\n", domain->name, nt_errstr(status) ));
1948
1949         return status;
1950 }
1951
1952 /* convert a sid to a user or group name. The sid is guaranteed to be in the domain
1953    given */
1954 static NTSTATUS sid_to_name(struct winbindd_domain *domain,
1955                             TALLOC_CTX *mem_ctx,
1956                             const struct dom_sid *sid,
1957                             char **domain_name,
1958                             char **name,
1959                             enum lsa_SidType *type)
1960 {
1961         NTSTATUS status;
1962         bool old_status;
1963
1964         old_status = domain->online;
1965         status = wcache_sid_to_name(domain, sid, mem_ctx, domain_name, name,
1966                                     type);
1967         if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
1968                 return status;
1969         }
1970
1971         *name = NULL;
1972         *domain_name = NULL;
1973
1974         /* If the seq number check indicated that there is a problem
1975          * with this DC, then return that status... except for
1976          * access_denied.  This is special because the dc may be in
1977          * "restrict anonymous = 1" mode, in which case it will deny
1978          * most unauthenticated operations, but *will* allow the LSA
1979          * sid-to-name that we try as a fallback. */
1980
1981         if (!(NT_STATUS_IS_OK(domain->last_status)
1982               || NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)))
1983                 return domain->last_status;
1984
1985         DEBUG(10,("sid_to_name: [Cached] - doing backend query for name for domain %s\n",
1986                 domain->name ));
1987
1988         status = domain->backend->sid_to_name(domain, mem_ctx, sid, domain_name, name, type);
1989
1990         if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1991                 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1992                 if (!domain->internal && old_status) {
1993                         set_domain_offline(domain);
1994                 }
1995                 if (!domain->internal &&
1996                         !domain->online &&
1997                         old_status) {
1998                         NTSTATUS cache_status;
1999                         cache_status = wcache_sid_to_name(domain, sid, mem_ctx,
2000                                                         domain_name, name, type);
2001                         return cache_status;
2002                 }
2003         }
2004         /* and save it */
2005         refresh_sequence_number(domain, false);
2006         if (!NT_STATUS_IS_OK(status)) {
2007                 return status;
2008         }
2009         wcache_save_sid_to_name(domain, status, sid, *domain_name, *name, *type);
2010
2011         /* We can't save the name to sid mapping here, as with sid history a
2012          * later name2sid would give the wrong sid. */
2013
2014         return status;
2015 }
2016
2017 static NTSTATUS rids_to_names(struct winbindd_domain *domain,
2018                               TALLOC_CTX *mem_ctx,
2019                               const struct dom_sid *domain_sid,
2020                               uint32 *rids,
2021                               size_t num_rids,
2022                               char **domain_name,
2023                               char ***names,
2024                               enum lsa_SidType **types)
2025 {
2026         struct winbind_cache *cache = get_cache(domain);
2027         size_t i;
2028         NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
2029         bool have_mapped;
2030         bool have_unmapped;
2031         bool old_status;
2032
2033         old_status = domain->online;
2034         *domain_name = NULL;
2035         *names = NULL;
2036         *types = NULL;
2037
2038         if (!cache->tdb) {
2039                 goto do_query;
2040         }
2041
2042         if (num_rids == 0) {
2043                 return NT_STATUS_OK;
2044         }
2045
2046         *names = talloc_array(mem_ctx, char *, num_rids);
2047         *types = talloc_array(mem_ctx, enum lsa_SidType, num_rids);
2048
2049         if ((*names == NULL) || (*types == NULL)) {
2050                 result = NT_STATUS_NO_MEMORY;
2051                 goto error;
2052         }
2053
2054         have_mapped = have_unmapped = false;
2055
2056         for (i=0; i<num_rids; i++) {
2057                 struct dom_sid sid;
2058                 struct cache_entry *centry;
2059                 fstring tmp;
2060
2061                 if (!sid_compose(&sid, domain_sid, rids[i])) {
2062                         result = NT_STATUS_INTERNAL_ERROR;
2063                         goto error;
2064                 }
2065
2066                 centry = wcache_fetch(cache, domain, "SN/%s",
2067                                       sid_to_fstring(tmp, &sid));
2068                 if (!centry) {
2069                         goto do_query;
2070                 }
2071
2072                 (*types)[i] = SID_NAME_UNKNOWN;
2073                 (*names)[i] = talloc_strdup(*names, "");
2074
2075                 if (NT_STATUS_IS_OK(centry->status)) {
2076                         char *dom;
2077                         have_mapped = true;
2078                         (*types)[i] = (enum lsa_SidType)centry_uint32(centry);
2079
2080                         dom = centry_string(centry, mem_ctx);
2081                         if (*domain_name == NULL) {
2082                                 *domain_name = dom;
2083                         } else {
2084                                 talloc_free(dom);
2085                         }
2086
2087                         (*names)[i] = centry_string(centry, *names);
2088
2089                 } else if (NT_STATUS_EQUAL(centry->status, NT_STATUS_NONE_MAPPED)
2090                            || NT_STATUS_EQUAL(centry->status, STATUS_SOME_UNMAPPED)) {
2091                         have_unmapped = true;
2092
2093                 } else {
2094                         /* something's definitely wrong */
2095                         result = centry->status;
2096                         centry_free(centry);
2097                         goto error;
2098                 }
2099
2100                 centry_free(centry);
2101         }
2102
2103         if (!have_mapped) {
2104                 return NT_STATUS_NONE_MAPPED;
2105         }
2106         if (!have_unmapped) {
2107                 return NT_STATUS_OK;
2108         }
2109         return STATUS_SOME_UNMAPPED;
2110
2111  do_query:
2112
2113         TALLOC_FREE(*names);
2114         TALLOC_FREE(*types);
2115
2116         result = domain->backend->rids_to_names(domain, mem_ctx, domain_sid,
2117                                                 rids, num_rids, domain_name,
2118                                                 names, types);
2119
2120         if (NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
2121                 NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2122                 if (!domain->internal && old_status) {
2123                         set_domain_offline(domain);
2124                 }
2125                 if (cache->tdb &&
2126                         !domain->internal &&
2127                         !domain->online &&
2128                         old_status) {
2129                         have_mapped = have_unmapped = false;
2130
2131                         *names = talloc_array(mem_ctx, char *, num_rids);
2132                         if (*names != NULL) {
2133                                 result = NT_STATUS_NO_MEMORY;
2134                                 goto error;
2135                         }
2136
2137                         *types = talloc_array(mem_ctx, enum lsa_SidType,
2138                                               num_rids);
2139                         if (*types != NULL) {
2140                                 result = NT_STATUS_NO_MEMORY;
2141                                 goto error;
2142                         }
2143
2144                         for (i=0; i<num_rids; i++) {
2145                                 struct dom_sid sid;
2146                                 struct cache_entry *centry;
2147                                 fstring tmp;
2148
2149                                 if (!sid_compose(&sid, domain_sid, rids[i])) {
2150                                         result = NT_STATUS_INTERNAL_ERROR;
2151                                         goto error;
2152                                 }
2153
2154                                 centry = wcache_fetch(cache, domain, "SN/%s",
2155                                                       sid_to_fstring(tmp, &sid));
2156                                 if (!centry) {
2157                                         (*types)[i] = SID_NAME_UNKNOWN;
2158                                         (*names)[i] = talloc_strdup(*names, "");
2159                                         continue;
2160                                 }
2161
2162                                 (*types)[i] = SID_NAME_UNKNOWN;
2163                                 (*names)[i] = talloc_strdup(*names, "");
2164
2165                                 if (NT_STATUS_IS_OK(centry->status)) {
2166                                         char *dom;
2167                                         have_mapped = true;
2168                                         (*types)[i] = (enum lsa_SidType)centry_uint32(centry);
2169
2170                                         dom = centry_string(centry, mem_ctx);
2171                                         if (*domain_name == NULL) {
2172                                                 *domain_name = dom;
2173                                         } else {
2174                                                 talloc_free(dom);
2175                                         }
2176
2177                                         (*names)[i] = centry_string(centry, *names);
2178
2179                                 } else if (NT_STATUS_EQUAL(centry->status, NT_STATUS_NONE_MAPPED)) {
2180                                         have_unmapped = true;
2181
2182                                 } else {
2183                                         /* something's definitely wrong */
2184                                         result = centry->status;
2185                                         centry_free(centry);
2186                                         goto error;
2187                                 }
2188
2189                                 centry_free(centry);
2190                         }
2191
2192                         if (!have_mapped) {
2193                                 return NT_STATUS_NONE_MAPPED;
2194                         }
2195                         if (!have_unmapped) {
2196                                 return NT_STATUS_OK;
2197                         }
2198                         return STATUS_SOME_UNMAPPED;
2199                 }
2200         }
2201         /*
2202           None of the queried rids has been found so save all negative entries
2203         */
2204         if (NT_STATUS_EQUAL(result, NT_STATUS_NONE_MAPPED)) {
2205                 for (i = 0; i < num_rids; i++) {
2206                         struct dom_sid sid;
2207                         const char *name = "";
2208                         const enum lsa_SidType type = SID_NAME_UNKNOWN;
2209                         NTSTATUS status = NT_STATUS_NONE_MAPPED;
2210
2211                         if (!sid_compose(&sid, domain_sid, rids[i])) {
2212                                 return NT_STATUS_INTERNAL_ERROR;
2213                         }
2214
2215                         wcache_save_sid_to_name(domain, status, &sid, *domain_name,
2216                                                 name, type);
2217                 }
2218
2219                 return result;
2220         }
2221
2222         /*
2223           Some or all of the queried rids have been found.
2224         */
2225         if (!NT_STATUS_IS_OK(result) &&
2226             !NT_STATUS_EQUAL(result, STATUS_SOME_UNMAPPED)) {
2227                 return result;
2228         }
2229
2230         refresh_sequence_number(domain, false);
2231
2232         for (i=0; i<num_rids; i++) {
2233                 struct dom_sid sid;
2234                 NTSTATUS status;
2235
2236                 if (!sid_compose(&sid, domain_sid, rids[i])) {
2237                         result = NT_STATUS_INTERNAL_ERROR;
2238                         goto error;
2239                 }
2240
2241                 status = (*types)[i] == SID_NAME_UNKNOWN ?
2242                         NT_STATUS_NONE_MAPPED : NT_STATUS_OK;
2243
2244                 wcache_save_sid_to_name(domain, status, &sid, *domain_name,
2245                                         (*names)[i], (*types)[i]);
2246         }
2247
2248         return result;
2249
2250  error:
2251         TALLOC_FREE(*names);
2252         TALLOC_FREE(*types);
2253         return result;
2254 }
2255
2256 NTSTATUS wcache_query_user(struct winbindd_domain *domain,
2257                            TALLOC_CTX *mem_ctx,
2258                            const struct dom_sid *user_sid,
2259                            struct wbint_userinfo *info)
2260 {
2261         struct winbind_cache *cache = get_cache(domain);
2262         struct cache_entry *centry = NULL;
2263         NTSTATUS status;
2264         char *sid_string;
2265
2266         if (cache->tdb == NULL) {
2267                 return NT_STATUS_NOT_FOUND;
2268         }
2269
2270         sid_string = sid_string_tos(user_sid);
2271         if (sid_string == NULL) {
2272                 return NT_STATUS_NO_MEMORY;
2273         }
2274
2275         centry = wcache_fetch(cache, domain, "U/%s", sid_string);
2276         TALLOC_FREE(sid_string);
2277         if (centry == NULL) {
2278                 return NT_STATUS_NOT_FOUND;
2279         }
2280
2281         /*
2282          * If we have an access denied cache entry and a cached info3
2283          * in the samlogon cache then do a query.  This will force the
2284          * rpc back end to return the info3 data.
2285          */
2286
2287         if (NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED) &&
2288             netsamlogon_cache_have(user_sid)) {
2289                 DEBUG(10, ("query_user: cached access denied and have cached "
2290                            "info3\n"));
2291                 domain->last_status = NT_STATUS_OK;
2292                 centry_free(centry);
2293                 return NT_STATUS_NOT_FOUND;
2294         }
2295
2296         /* if status is not ok then this is a negative hit
2297            and the rest of the data doesn't matter */
2298         status = centry->status;
2299         if (NT_STATUS_IS_OK(status)) {
2300                 info->acct_name = centry_string(centry, mem_ctx);
2301                 info->full_name = centry_string(centry, mem_ctx);
2302                 info->homedir = centry_string(centry, mem_ctx);
2303                 info->shell = centry_string(centry, mem_ctx);
2304                 info->primary_gid = centry_uint32(centry);
2305                 centry_sid(centry, &info->user_sid);
2306                 centry_sid(centry, &info->group_sid);
2307         }
2308
2309         DEBUG(10,("query_user: [Cached] - cached info for domain %s status: "
2310                   "%s\n", domain->name, nt_errstr(status) ));
2311
2312         centry_free(centry);
2313         return status;
2314 }
2315
2316 /* Lookup user information from a rid */
2317 static NTSTATUS query_user(struct winbindd_domain *domain,
2318                            TALLOC_CTX *mem_ctx,
2319                            const struct dom_sid *user_sid,
2320                            struct wbint_userinfo *info)
2321 {
2322         NTSTATUS status;
2323         bool old_status;
2324
2325         old_status = domain->online;
2326         status = wcache_query_user(domain, mem_ctx, user_sid, info);
2327         if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2328                 return status;
2329         }
2330
2331         ZERO_STRUCTP(info);
2332
2333         /* Return status value returned by seq number check */
2334
2335         if (!NT_STATUS_IS_OK(domain->last_status))
2336                 return domain->last_status;
2337
2338         DEBUG(10,("query_user: [Cached] - doing backend query for info for domain %s\n",
2339                 domain->name ));
2340
2341         status = domain->backend->query_user(domain, mem_ctx, user_sid, info);
2342
2343         if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2344                 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2345                 if (!domain->internal && old_status) {
2346                         set_domain_offline(domain);
2347                 }
2348                 if (!domain->internal &&
2349                         !domain->online &&
2350                         old_status) {
2351                         NTSTATUS cache_status;
2352                         cache_status = wcache_query_user(domain, mem_ctx, user_sid, info);
2353                         return cache_status;
2354                 }
2355         }
2356         /* and save it */
2357         refresh_sequence_number(domain, false);
2358         if (!NT_STATUS_IS_OK(status)) {
2359                 return status;
2360         }
2361         wcache_save_user(domain, status, info);
2362
2363         return status;
2364 }
2365
2366 NTSTATUS wcache_lookup_usergroups(struct winbindd_domain *domain,
2367                                   TALLOC_CTX *mem_ctx,
2368                                   const struct dom_sid *user_sid,
2369                                   uint32_t *pnum_sids,
2370                                   struct dom_sid **psids)
2371 {
2372         struct winbind_cache *cache = get_cache(domain);
2373         struct cache_entry *centry = NULL;
2374         NTSTATUS status;
2375         uint32_t i, num_sids;
2376         struct dom_sid *sids;
2377         fstring sid_string;
2378
2379         if (cache->tdb == NULL) {
2380                 return NT_STATUS_NOT_FOUND;
2381         }
2382
2383         centry = wcache_fetch(cache, domain, "UG/%s",
2384                               sid_to_fstring(sid_string, user_sid));
2385         if (centry == NULL) {
2386                 return NT_STATUS_NOT_FOUND;
2387         }
2388
2389         /* If we have an access denied cache entry and a cached info3 in the
2390            samlogon cache then do a query.  This will force the rpc back end
2391            to return the info3 data. */
2392
2393         if (NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)
2394             && netsamlogon_cache_have(user_sid)) {
2395                 DEBUG(10, ("lookup_usergroups: cached access denied and have "
2396                            "cached info3\n"));
2397                 domain->last_status = NT_STATUS_OK;
2398                 centry_free(centry);
2399                 return NT_STATUS_NOT_FOUND;
2400         }
2401
2402         num_sids = centry_uint32(centry);
2403         sids = talloc_array(mem_ctx, struct dom_sid, num_sids);
2404         if (sids == NULL) {
2405                 centry_free(centry);
2406                 return NT_STATUS_NO_MEMORY;
2407         }
2408
2409         for (i=0; i<num_sids; i++) {
2410                 centry_sid(centry, &sids[i]);
2411         }
2412
2413         status = centry->status;
2414
2415         DEBUG(10,("lookup_usergroups: [Cached] - cached info for domain %s "
2416                   "status: %s\n", domain->name, nt_errstr(status)));
2417
2418         centry_free(centry);
2419
2420         *pnum_sids = num_sids;
2421         *psids = sids;
2422         return status;
2423 }
2424
2425 /* Lookup groups a user is a member of. */
2426 static NTSTATUS lookup_usergroups(struct winbindd_domain *domain,
2427                                   TALLOC_CTX *mem_ctx,
2428                                   const struct dom_sid *user_sid,
2429                                   uint32 *num_groups, struct dom_sid **user_gids)
2430 {
2431         struct cache_entry *centry = NULL;
2432         NTSTATUS status;
2433         unsigned int i;
2434         fstring sid_string;
2435         bool old_status;
2436
2437         old_status = domain->online;
2438         status = wcache_lookup_usergroups(domain, mem_ctx, user_sid,
2439                                           num_groups, user_gids);
2440         if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2441                 return status;
2442         }
2443
2444         (*num_groups) = 0;
2445         (*user_gids) = NULL;
2446
2447         /* Return status value returned by seq number check */
2448
2449         if (!NT_STATUS_IS_OK(domain->last_status))
2450                 return domain->last_status;
2451
2452         DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info for domain %s\n",
2453                 domain->name ));
2454
2455         status = domain->backend->lookup_usergroups(domain, mem_ctx, user_sid, num_groups, user_gids);
2456
2457         if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2458                 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2459                 if (!domain->internal && old_status) {
2460                         set_domain_offline(domain);
2461                 }
2462                 if (!domain->internal &&
2463                         !domain->online &&
2464                         old_status) {
2465                         NTSTATUS cache_status;
2466                         cache_status = wcache_lookup_usergroups(domain, mem_ctx, user_sid,
2467                                                           num_groups, user_gids);
2468                         return cache_status;
2469                 }
2470         }
2471         if ( NT_STATUS_EQUAL(status, NT_STATUS_SYNCHRONIZATION_REQUIRED) )
2472                 goto skip_save;
2473
2474         /* and save it */
2475         refresh_sequence_number(domain, false);
2476         if (!NT_STATUS_IS_OK(status)) {
2477                 return status;
2478         }
2479         centry = centry_start(domain, status);
2480         if (!centry)
2481                 goto skip_save;
2482
2483         centry_put_uint32(centry, *num_groups);
2484         for (i=0; i<(*num_groups); i++) {
2485                 centry_put_sid(centry, &(*user_gids)[i]);
2486         }       
2487
2488         centry_end(centry, "UG/%s", sid_to_fstring(sid_string, user_sid));
2489         centry_free(centry);
2490
2491 skip_save:
2492         return status;
2493 }
2494
2495 static char *wcache_make_sidlist(TALLOC_CTX *mem_ctx, uint32_t num_sids,
2496                                  const struct dom_sid *sids)
2497 {
2498         uint32_t i;
2499         char *sidlist;
2500
2501         sidlist = talloc_strdup(mem_ctx, "");
2502         if (sidlist == NULL) {
2503                 return NULL;
2504         }
2505         for (i=0; i<num_sids; i++) {
2506                 fstring tmp;
2507                 sidlist = talloc_asprintf_append_buffer(
2508                         sidlist, "/%s", sid_to_fstring(tmp, &sids[i]));
2509                 if (sidlist == NULL) {
2510                         return NULL;
2511                 }
2512         }
2513         return sidlist;
2514 }
2515
2516 NTSTATUS wcache_lookup_useraliases(struct winbindd_domain *domain,
2517                                    TALLOC_CTX *mem_ctx, uint32_t num_sids,
2518                                    const struct dom_sid *sids,
2519                                    uint32_t *pnum_aliases, uint32_t **paliases)
2520 {
2521         struct winbind_cache *cache = get_cache(domain);
2522         struct cache_entry *centry = NULL;
2523         uint32_t num_aliases;
2524         uint32_t *aliases;
2525         NTSTATUS status;
2526         char *sidlist;
2527         int i;
2528
2529         if (cache->tdb == NULL) {
2530                 return NT_STATUS_NOT_FOUND;
2531         }
2532
2533         if (num_sids == 0) {
2534                 *pnum_aliases = 0;
2535                 *paliases = NULL;
2536                 return NT_STATUS_OK;
2537         }
2538
2539         /* We need to cache indexed by the whole list of SIDs, the aliases
2540          * resulting might come from any of the SIDs. */
2541
2542         sidlist = wcache_make_sidlist(talloc_tos(), num_sids, sids);
2543         if (sidlist == NULL) {
2544                 return NT_STATUS_NO_MEMORY;
2545         }
2546
2547         centry = wcache_fetch(cache, domain, "UA%s", sidlist);
2548         TALLOC_FREE(sidlist);
2549         if (centry == NULL) {
2550                 return NT_STATUS_NOT_FOUND;
2551         }
2552
2553         num_aliases = centry_uint32(centry);
2554         aliases = talloc_array(mem_ctx, uint32_t, num_aliases);
2555         if (aliases == NULL) {
2556                 centry_free(centry);
2557                 return NT_STATUS_NO_MEMORY;
2558         }
2559
2560         for (i=0; i<num_aliases; i++) {
2561                 aliases[i] = centry_uint32(centry);
2562         }
2563
2564         status = centry->status;
2565
2566         DEBUG(10,("lookup_useraliases: [Cached] - cached info for domain: %s "
2567                   "status %s\n", domain->name, nt_errstr(status)));
2568
2569         centry_free(centry);
2570
2571         *pnum_aliases = num_aliases;
2572         *paliases = aliases;
2573
2574         return status;
2575 }
2576
2577 static NTSTATUS lookup_useraliases(struct winbindd_domain *domain,
2578                                    TALLOC_CTX *mem_ctx,
2579                                    uint32 num_sids, const struct dom_sid *sids,
2580                                    uint32 *num_aliases, uint32 **alias_rids)
2581 {
2582         struct cache_entry *centry = NULL;
2583         NTSTATUS status;
2584         char *sidlist;
2585         int i;
2586         bool old_status;
2587
2588         old_status = domain->online;
2589         status = wcache_lookup_useraliases(domain, mem_ctx, num_sids, sids,
2590                                            num_aliases, alias_rids);
2591         if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2592                 return status;
2593         }
2594
2595         (*num_aliases) = 0;
2596         (*alias_rids) = NULL;
2597
2598         if (!NT_STATUS_IS_OK(domain->last_status))
2599                 return domain->last_status;
2600
2601         DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info "
2602                   "for domain %s\n", domain->name ));
2603
2604         sidlist = wcache_make_sidlist(talloc_tos(), num_sids, sids);
2605         if (sidlist == NULL) {
2606                 return NT_STATUS_NO_MEMORY;
2607         }
2608
2609         status = domain->backend->lookup_useraliases(domain, mem_ctx,
2610                                                      num_sids, sids,
2611                                                      num_aliases, alias_rids);
2612
2613         if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2614                 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2615                 if (!domain->internal && old_status) {
2616                         set_domain_offline(domain);
2617                 }
2618                 if (!domain->internal &&
2619                         !domain->online &&
2620                         old_status) {
2621                         NTSTATUS cache_status;
2622                         cache_status = wcache_lookup_useraliases(domain, mem_ctx, num_sids,
2623                                                                  sids, num_aliases, alias_rids);
2624                         return cache_status;
2625                 }
2626         }
2627         /* and save it */
2628         refresh_sequence_number(domain, false);
2629         if (!NT_STATUS_IS_OK(status)) {
2630                 return status;
2631         }
2632         centry = centry_start(domain, status);
2633         if (!centry)
2634                 goto skip_save;
2635         centry_put_uint32(centry, *num_aliases);
2636         for (i=0; i<(*num_aliases); i++)
2637                 centry_put_uint32(centry, (*alias_rids)[i]);
2638         centry_end(centry, "UA%s", sidlist);
2639         centry_free(centry);
2640
2641  skip_save:
2642         return status;
2643 }
2644
2645 NTSTATUS wcache_lookup_groupmem(struct winbindd_domain *domain,
2646                                 TALLOC_CTX *mem_ctx,
2647                                 const struct dom_sid *group_sid,
2648                                 uint32_t *num_names,
2649                                 struct dom_sid **sid_mem, char ***names,
2650                                 uint32_t **name_types)
2651 {
2652         struct winbind_cache *cache = get_cache(domain);
2653         struct cache_entry *centry = NULL;
2654         NTSTATUS status;
2655         unsigned int i;
2656         char *sid_string;
2657
2658         if (cache->tdb == NULL) {
2659                 return NT_STATUS_NOT_FOUND;
2660         }
2661
2662         sid_string = sid_string_tos(group_sid);
2663         if (sid_string == NULL) {
2664                 return NT_STATUS_NO_MEMORY;
2665         }
2666
2667         centry = wcache_fetch(cache, domain, "GM/%s", sid_string);
2668         TALLOC_FREE(sid_string);
2669         if (centry == NULL) {
2670                 return NT_STATUS_NOT_FOUND;
2671         }
2672
2673         *sid_mem = NULL;
2674         *names = NULL;
2675         *name_types = NULL;
2676
2677         *num_names = centry_uint32(centry);
2678         if (*num_names == 0) {
2679                 centry_free(centry);
2680                 return NT_STATUS_OK;
2681         }
2682
2683         *sid_mem = talloc_array(mem_ctx, struct dom_sid, *num_names);
2684         *names = talloc_array(mem_ctx, char *, *num_names);
2685         *name_types = talloc_array(mem_ctx, uint32, *num_names);
2686
2687         if ((*sid_mem == NULL) || (*names == NULL) || (*name_types == NULL)) {
2688                 TALLOC_FREE(*sid_mem);
2689                 TALLOC_FREE(*names);
2690                 TALLOC_FREE(*name_types);
2691                 centry_free(centry);
2692                 return NT_STATUS_NO_MEMORY;
2693         }
2694
2695         for (i=0; i<(*num_names); i++) {
2696                 centry_sid(centry, &(*sid_mem)[i]);
2697                 (*names)[i] = centry_string(centry, mem_ctx);
2698                 (*name_types)[i] = centry_uint32(centry);
2699         }
2700
2701         status = centry->status;
2702
2703         DEBUG(10,("lookup_groupmem: [Cached] - cached info for domain %s "
2704                   "status: %s\n", domain->name, nt_errstr(status)));
2705
2706         centry_free(centry);
2707         return status;
2708 }
2709
2710 static NTSTATUS lookup_groupmem(struct winbindd_domain *domain,
2711                                 TALLOC_CTX *mem_ctx,
2712                                 const struct dom_sid *group_sid,
2713                                 enum lsa_SidType type,
2714                                 uint32 *num_names,
2715                                 struct dom_sid **sid_mem, char ***names,
2716                                 uint32 **name_types)
2717 {
2718         struct cache_entry *centry = NULL;
2719         NTSTATUS status;
2720         unsigned int i;
2721         fstring sid_string;
2722         bool old_status;
2723
2724         old_status = domain->online;
2725         status = wcache_lookup_groupmem(domain, mem_ctx, group_sid, num_names,
2726                                         sid_mem, names, name_types);
2727         if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2728                 return status;
2729         }
2730
2731         (*num_names) = 0;
2732         (*sid_mem) = NULL;
2733         (*names) = NULL;
2734         (*name_types) = NULL;
2735
2736         /* Return status value returned by seq number check */
2737
2738         if (!NT_STATUS_IS_OK(domain->last_status))
2739                 return domain->last_status;
2740
2741         DEBUG(10,("lookup_groupmem: [Cached] - doing backend query for info for domain %s\n",
2742                 domain->name ));
2743
2744         status = domain->backend->lookup_groupmem(domain, mem_ctx, group_sid,
2745                                                   type, num_names,
2746                                                   sid_mem, names, name_types);
2747
2748         if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2749                 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2750                 if (!domain->internal && old_status) {
2751                         set_domain_offline(domain);
2752                 }
2753                 if (!domain->internal &&
2754                         !domain->online &&
2755                         old_status) {
2756                         NTSTATUS cache_status;
2757                         cache_status = wcache_lookup_groupmem(domain, mem_ctx, group_sid,
2758                                                               num_names, sid_mem, names,
2759                                                               name_types);
2760                         return cache_status;
2761                 }
2762         }
2763         /* and save it */
2764         refresh_sequence_number(domain, false);
2765         if (!NT_STATUS_IS_OK(status)) {
2766                 return status;
2767         }
2768         centry = centry_start(domain, status);
2769         if (!centry)
2770                 goto skip_save;
2771         centry_put_uint32(centry, *num_names);
2772         for (i=0; i<(*num_names); i++) {
2773                 centry_put_sid(centry, &(*sid_mem)[i]);
2774                 centry_put_string(centry, (*names)[i]);
2775                 centry_put_uint32(centry, (*name_types)[i]);
2776         }       
2777         centry_end(centry, "GM/%s", sid_to_fstring(sid_string, group_sid));
2778         centry_free(centry);
2779
2780 skip_save:
2781         return status;
2782 }
2783
2784 /* find the sequence number for a domain */
2785 static NTSTATUS sequence_number(struct winbindd_domain *domain, uint32 *seq)
2786 {
2787         refresh_sequence_number(domain, false);
2788
2789         *seq = domain->sequence_number;
2790
2791         return NT_STATUS_OK;
2792 }
2793
2794 /* enumerate trusted domains 
2795  * (we need to have the list of trustdoms in the cache when we go offline) -
2796  * Guenther */
2797 static NTSTATUS trusted_domains(struct winbindd_domain *domain,
2798                                 TALLOC_CTX *mem_ctx,
2799                                 struct netr_DomainTrustList *trusts)
2800 {
2801         NTSTATUS status;
2802         struct winbind_cache *cache;
2803         struct winbindd_tdc_domain *dom_list = NULL;
2804         size_t num_domains = 0;
2805         bool retval = false;
2806         int i;
2807         bool old_status;
2808
2809         old_status = domain->online;
2810         trusts->count = 0;
2811         trusts->array = NULL;
2812
2813         cache = get_cache(domain);
2814         if (!cache || !cache->tdb) {
2815                 goto do_query;
2816         }
2817
2818         if (domain->online) {
2819                 goto do_query;
2820         }
2821
2822         retval = wcache_tdc_fetch_list(&dom_list, &num_domains);
2823         if (!retval || !num_domains || !dom_list) {
2824                 TALLOC_FREE(dom_list);
2825                 goto do_query;
2826         }
2827
2828 do_fetch_cache:
2829         trusts->array = talloc_zero_array(mem_ctx, struct netr_DomainTrust, num_domains);
2830         if (!trusts->array) {
2831                 TALLOC_FREE(dom_list);
2832                 return NT_STATUS_NO_MEMORY;
2833         }
2834
2835         for (i = 0; i < num_domains; i++) {
2836                 struct netr_DomainTrust *trust;
2837                 struct dom_sid *sid;
2838                 struct winbindd_domain *dom;
2839
2840                 dom = find_domain_from_name_noinit(dom_list[i].domain_name);
2841                 if (dom && dom->internal) {
2842                         continue;
2843                 }
2844
2845                 trust = &trusts->array[trusts->count];
2846                 trust->netbios_name = talloc_strdup(trusts->array, dom_list[i].domain_name);
2847                 trust->dns_name = talloc_strdup(trusts->array, dom_list[i].dns_name);
2848                 sid = talloc(trusts->array, struct dom_sid);
2849                 if (!trust->netbios_name || !trust->dns_name ||
2850                         !sid) {
2851                         TALLOC_FREE(dom_list);
2852                         TALLOC_FREE(trusts->array);
2853                         return NT_STATUS_NO_MEMORY;
2854                 }
2855
2856                 trust->trust_flags = dom_list[i].trust_flags;
2857                 trust->trust_attributes = dom_list[i].trust_attribs;
2858                 trust->trust_type = dom_list[i].trust_type;
2859                 sid_copy(sid, &dom_list[i].sid);
2860                 trust->sid = sid;
2861                 trusts->count++;
2862         }
2863
2864         TALLOC_FREE(dom_list);
2865         return NT_STATUS_OK;
2866
2867 do_query:
2868         /* Return status value returned by seq number check */
2869
2870         if (!NT_STATUS_IS_OK(domain->last_status))
2871                 return domain->last_status;
2872
2873         DEBUG(10,("trusted_domains: [Cached] - doing backend query for info for domain %s\n",
2874                 domain->name ));
2875
2876         status = domain->backend->trusted_domains(domain, mem_ctx, trusts);
2877
2878         if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2879                 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2880                 if (!domain->internal && old_status) {
2881                         set_domain_offline(domain);
2882                 }
2883                 if (!domain->internal &&
2884                         !domain->online &&
2885                         old_status) {
2886                         retval = wcache_tdc_fetch_list(&dom_list, &num_domains);
2887                         if (retval && num_domains && dom_list) {
2888                                 TALLOC_FREE(trusts->array);
2889                                 trusts->count = 0;
2890                                 goto do_fetch_cache;
2891                         }
2892                 }
2893         }
2894         /* no trusts gives NT_STATUS_NO_MORE_ENTRIES resetting to NT_STATUS_OK
2895          * so that the generic centry handling still applies correctly -
2896          * Guenther*/
2897
2898         if (!NT_STATUS_IS_ERR(status)) {
2899                 status = NT_STATUS_OK;
2900         }
2901         return status;
2902 }       
2903
2904 /* get lockout policy */
2905 static NTSTATUS lockout_policy(struct winbindd_domain *domain,
2906                                TALLOC_CTX *mem_ctx,
2907                                struct samr_DomInfo12 *policy)
2908 {
2909         struct winbind_cache *cache = get_cache(domain);
2910         struct cache_entry *centry = NULL;
2911         NTSTATUS status;
2912         bool old_status;
2913
2914         old_status = domain->online;
2915         if (!cache->tdb)
2916                 goto do_query;
2917
2918         centry = wcache_fetch(cache, domain, "LOC_POL/%s", domain->name);
2919
2920         if (!centry)
2921                 goto do_query;
2922
2923 do_fetch_cache:
2924         policy->lockout_duration = centry_nttime(centry);
2925         policy->lockout_window = centry_nttime(centry);
2926         policy->lockout_threshold = centry_uint16(centry);
2927
2928         status = centry->status;
2929
2930         DEBUG(10,("lockout_policy: [Cached] - cached info for domain %s status: %s\n",
2931                 domain->name, nt_errstr(status) ));
2932
2933         centry_free(centry);
2934         return status;
2935
2936 do_query:
2937         ZERO_STRUCTP(policy);
2938
2939         /* Return status value returned by seq number check */
2940
2941         if (!NT_STATUS_IS_OK(domain->last_status))
2942                 return domain->last_status;
2943
2944         DEBUG(10,("lockout_policy: [Cached] - doing backend query for info for domain %s\n",
2945                 domain->name ));
2946
2947         status = domain->backend->lockout_policy(domain, mem_ctx, policy);
2948
2949         if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2950                 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2951                 if (!domain->internal && old_status) {
2952                         set_domain_offline(domain);
2953                 }
2954                 if (cache->tdb &&
2955                         !domain->internal &&
2956                         !domain->online &&
2957                         old_status) {
2958                         centry = wcache_fetch(cache, domain, "LOC_POL/%s", domain->name);
2959                         if (centry) {
2960                                 goto do_fetch_cache;
2961                         }
2962                 }
2963         }
2964         /* and save it */
2965         refresh_sequence_number(domain, false);
2966         if (!NT_STATUS_IS_OK(status)) {
2967                 return status;
2968         }
2969         wcache_save_lockout_policy(domain, status, policy);
2970
2971         return status;
2972 }
2973
2974 /* get password policy */
2975 static NTSTATUS password_policy(struct winbindd_domain *domain,
2976                                 TALLOC_CTX *mem_ctx,
2977                                 struct samr_DomInfo1 *policy)
2978 {
2979         struct winbind_cache *cache = get_cache(domain);
2980         struct cache_entry *centry = NULL;
2981         NTSTATUS status;
2982         bool old_status;
2983
2984         old_status = domain->online;
2985         if (!cache->tdb)
2986                 goto do_query;
2987
2988         centry = wcache_fetch(cache, domain, "PWD_POL/%s", domain->name);
2989
2990         if (!centry)
2991                 goto do_query;
2992
2993 do_fetch_cache:
2994         policy->min_password_length = centry_uint16(centry);
2995         policy->password_history_length = centry_uint16(centry);
2996         policy->password_properties = centry_uint32(centry);
2997         policy->max_password_age = centry_nttime(centry);
2998         policy->min_password_age = centry_nttime(centry);
2999
3000         status = centry->status;
3001
3002         DEBUG(10,("lockout_policy: [Cached] - cached info for domain %s status: %s\n",
3003                 domain->name, nt_errstr(status) ));
3004
3005         centry_free(centry);
3006         return status;
3007
3008 do_query:
3009         ZERO_STRUCTP(policy);
3010
3011         /* Return status value returned by seq number check */
3012
3013         if (!NT_STATUS_IS_OK(domain->last_status))
3014                 return domain->last_status;
3015
3016         DEBUG(10,("password_policy: [Cached] - doing backend query for info for domain %s\n",
3017                 domain->name ));
3018
3019         status = domain->backend->password_policy(domain, mem_ctx, policy);
3020
3021         if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
3022                 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
3023                 if (!domain->internal && old_status) {
3024                         set_domain_offline(domain);
3025                 }
3026                 if (cache->tdb &&
3027                         !domain->internal &&
3028                         !domain->online &&
3029                         old_status) {
3030                         centry = wcache_fetch(cache, domain, "PWD_POL/%s", domain->name);
3031                         if (centry) {
3032                                 goto do_fetch_cache;
3033                         }
3034                 }
3035         }
3036         /* and save it */
3037         refresh_sequence_number(domain, false);
3038         if (!NT_STATUS_IS_OK(status)) {
3039                 return status;
3040         }
3041         wcache_save_password_policy(domain, status, policy);
3042
3043         return status;
3044 }
3045
3046
3047 /* Invalidate cached user and group lists coherently */
3048
3049 static int traverse_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf, 
3050                        void *state)
3051 {
3052         if (strncmp((const char *)kbuf.dptr, "UL/", 3) == 0 ||
3053             strncmp((const char *)kbuf.dptr, "GL/", 3) == 0)
3054                 tdb_delete(the_tdb, kbuf);
3055
3056         return 0;
3057 }
3058
3059 /* Invalidate the getpwnam and getgroups entries for a winbindd domain */
3060
3061 void wcache_invalidate_samlogon(struct winbindd_domain *domain, 
3062                                 const struct dom_sid *sid)
3063 {
3064         fstring key_str, sid_string;
3065         struct winbind_cache *cache;
3066
3067         /* dont clear cached U/SID and UG/SID entries when we want to logon
3068          * offline - gd */
3069
3070         if (lp_winbind_offline_logon()) {
3071                 return;
3072         }
3073
3074         if (!domain)
3075                 return;
3076
3077         cache = get_cache(domain);
3078
3079         if (!cache->tdb) {
3080                 return;
3081         }
3082
3083         /* Clear U/SID cache entry */
3084         fstr_sprintf(key_str, "U/%s", sid_to_fstring(sid_string, sid));
3085         DEBUG(10, ("wcache_invalidate_samlogon: clearing %s\n", key_str));
3086         tdb_delete(cache->tdb, string_tdb_data(key_str));
3087
3088         /* Clear UG/SID cache entry */
3089         fstr_sprintf(key_str, "UG/%s", sid_to_fstring(sid_string, sid));
3090         DEBUG(10, ("wcache_invalidate_samlogon: clearing %s\n", key_str));
3091         tdb_delete(cache->tdb, string_tdb_data(key_str));
3092
3093         /* Samba/winbindd never needs this. */
3094         netsamlogon_clear_cached_user(sid);
3095 }
3096
3097 bool wcache_invalidate_cache(void)
3098 {
3099         struct winbindd_domain *domain;
3100
3101         for (domain = domain_list(); domain; domain = domain->next) {
3102                 struct winbind_cache *cache = get_cache(domain);
3103
3104                 DEBUG(10, ("wcache_invalidate_cache: invalidating cache "
3105                            "entries for %s\n", domain->name));
3106                 if (cache) {
3107                         if (cache->tdb) {
3108                                 tdb_traverse(cache->tdb, traverse_fn, NULL);
3109                         } else {
3110                                 return false;
3111                         }
3112                 }
3113         }
3114         return true;
3115 }
3116
3117 bool wcache_invalidate_cache_noinit(void)
3118 {
3119         struct winbindd_domain *domain;
3120
3121         for (domain = domain_list(); domain; domain = domain->next) {
3122                 struct winbind_cache *cache;
3123
3124                 /* Skip uninitialized domains. */
3125                 if (!domain->initialized && !domain->internal) {
3126                         continue;
3127                 }
3128
3129                 cache = get_cache(domain);
3130
3131                 DEBUG(10, ("wcache_invalidate_cache: invalidating cache "
3132                            "entries for %s\n", domain->name));
3133                 if (cache) {
3134                         if (cache->tdb) {
3135                                 tdb_traverse(cache->tdb, traverse_fn, NULL);
3136                                 /*
3137                                  * Flushing cache has nothing to with domains.
3138                                  * return here if we successfully flushed once.
3139                                  * To avoid unnecessary traversing the cache.
3140                                  */
3141                                 return true;
3142                         } else {
3143                                 return false;
3144                         }
3145                 }
3146         }
3147         return true;
3148 }
3149
3150 bool init_wcache(void)
3151 {
3152         if (wcache == NULL) {
3153                 wcache = SMB_XMALLOC_P(struct winbind_cache);
3154                 ZERO_STRUCTP(wcache);
3155         }
3156
3157         if (wcache->tdb != NULL)
3158                 return true;
3159
3160         /* when working offline we must not clear the cache on restart */
3161         wcache->tdb = tdb_open_log(state_path("winbindd_cache.tdb"),
3162                                 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE, 
3163                                 TDB_INCOMPATIBLE_HASH |
3164                                         (lp_winbind_offline_logon() ? TDB_DEFAULT : (TDB_DEFAULT | TDB_CLEAR_IF_FIRST)),
3165                                 O_RDWR|O_CREAT, 0600);
3166
3167         if (wcache->tdb == NULL) {
3168                 DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
3169                 return false;
3170         }
3171
3172         return true;
3173 }
3174
3175 /************************************************************************
3176  This is called by the parent to initialize the cache file.
3177  We don't need sophisticated locking here as we know we're the
3178  only opener.
3179 ************************************************************************/
3180
3181 bool initialize_winbindd_cache(void)
3182 {
3183         bool cache_bad = true;
3184         uint32 vers;
3185
3186         if (!init_wcache()) {
3187                 DEBUG(0,("initialize_winbindd_cache: init_wcache failed.\n"));
3188                 return false;
3189         }
3190
3191         /* Check version number. */
3192         if (tdb_fetch_uint32(wcache->tdb, WINBINDD_CACHE_VERSION_KEYSTR, &vers) &&
3193                         vers == WINBINDD_CACHE_VERSION) {
3194                 cache_bad = false;
3195         }
3196
3197         if (cache_bad) {
3198                 DEBUG(0,("initialize_winbindd_cache: clearing cache "
3199                         "and re-creating with version number %d\n",
3200                         WINBINDD_CACHE_VERSION ));
3201
3202                 tdb_close(wcache->tdb);
3203                 wcache->tdb = NULL;
3204
3205                 if (unlink(state_path("winbindd_cache.tdb")) == -1) {
3206                         DEBUG(0,("initialize_winbindd_cache: unlink %s failed %s ",
3207                                 state_path("winbindd_cache.tdb"),
3208                                 strerror(errno) ));
3209                         return false;
3210                 }
3211                 if (!init_wcache()) {
3212                         DEBUG(0,("initialize_winbindd_cache: re-initialization "
3213                                         "init_wcache failed.\n"));
3214                         return false;
3215                 }
3216
3217                 /* Write the version. */
3218                 if (!tdb_store_uint32(wcache->tdb, WINBINDD_CACHE_VERSION_KEYSTR, WINBINDD_CACHE_VERSION)) {
3219                         DEBUG(0,("initialize_winbindd_cache: version number store failed %s\n",
3220                                 tdb_errorstr_compat(wcache->tdb) ));
3221                         return false;
3222                 }
3223         }
3224
3225         tdb_close(wcache->tdb);
3226         wcache->tdb = NULL;
3227         return true;
3228 }
3229
3230 void close_winbindd_cache(void)
3231 {
3232         if (!wcache) {
3233                 return;
3234         }
3235         if (wcache->tdb) {
3236                 tdb_close(wcache->tdb);
3237                 wcache->tdb = NULL;
3238         }
3239 }
3240
3241 bool lookup_cached_sid(TALLOC_CTX *mem_ctx, const struct dom_sid *sid,
3242                        char **domain_name, char **name,
3243                        enum lsa_SidType *type)
3244 {
3245         struct winbindd_domain *domain;
3246         NTSTATUS status;
3247
3248         domain = find_lookup_domain_from_sid(sid);
3249         if (domain == NULL) {
3250                 return false;
3251         }
3252         status = wcache_sid_to_name(domain, sid, mem_ctx, domain_name, name,
3253                                     type);
3254         return NT_STATUS_IS_OK(status);
3255 }
3256
3257 bool lookup_cached_name(const char *domain_name,
3258                         const char *name,
3259                         struct dom_sid *sid,
3260                         enum lsa_SidType *type)
3261 {
3262         struct winbindd_domain *domain;
3263         NTSTATUS status;
3264         bool original_online_state;
3265
3266         domain = find_lookup_domain_from_name(domain_name);
3267         if (domain == NULL) {
3268                 return false;
3269         }
3270
3271         /* If we are doing a cached logon, temporarily set the domain
3272            offline so the cache won't expire the entry */
3273
3274         original_online_state = domain->online;
3275         domain->online = false;
3276         status = wcache_name_to_sid(domain, domain_name, name, sid, type);
3277         domain->online = original_online_state;
3278
3279         return NT_STATUS_IS_OK(status);
3280 }
3281
3282 void cache_name2sid(struct winbindd_domain *domain, 
3283                     const char *domain_name, const char *name,
3284                     enum lsa_SidType type, const struct dom_sid *sid)
3285 {
3286         refresh_sequence_number(domain, false);
3287         wcache_save_name_to_sid(domain, NT_STATUS_OK, domain_name, name,
3288                                 sid, type);
3289 }
3290
3291 /*
3292  * The original idea that this cache only contains centries has
3293  * been blurred - now other stuff gets put in here. Ensure we
3294  * ignore these things on cleanup.
3295  */
3296
3297 static int traverse_fn_cleanup(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, 
3298                                TDB_DATA dbuf, void *state)
3299 {
3300         struct cache_entry *centry;
3301
3302         if (is_non_centry_key(kbuf)) {
3303                 return 0;
3304         }
3305
3306         centry = wcache_fetch_raw((char *)kbuf.dptr);
3307         if (!centry) {
3308                 return 0;
3309         }
3310
3311         if (!NT_STATUS_IS_OK(centry->status)) {
3312                 DEBUG(10,("deleting centry %s\n", (const char *)kbuf.dptr));
3313                 tdb_delete(the_tdb, kbuf);
3314         }
3315
3316         centry_free(centry);
3317         return 0;
3318 }
3319
3320 /* flush the cache */
3321 void wcache_flush_cache(void)
3322 {
3323         if (!wcache)
3324                 return;
3325         if (wcache->tdb) {
3326                 tdb_close(wcache->tdb);
3327                 wcache->tdb = NULL;
3328         }
3329         if (!winbindd_use_cache()) {
3330                 return;
3331         }
3332
3333         /* when working offline we must not clear the cache on restart */
3334         wcache->tdb = tdb_open_log(state_path("winbindd_cache.tdb"),
3335                                 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE, 
3336                                 TDB_INCOMPATIBLE_HASH |
3337                                 (lp_winbind_offline_logon() ? TDB_DEFAULT : (TDB_DEFAULT | TDB_CLEAR_IF_FIRST)),
3338                                 O_RDWR|O_CREAT, 0600);
3339
3340         if (!wcache->tdb) {
3341                 DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
3342                 return;
3343         }
3344
3345         tdb_traverse(wcache->tdb, traverse_fn_cleanup, NULL);
3346
3347         DEBUG(10,("wcache_flush_cache success\n"));
3348 }
3349
3350 /* Count cached creds */
3351
3352 static int traverse_fn_cached_creds(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf, 
3353                                     void *state)
3354 {
3355         int *cred_count = (int*)state;
3356  
3357         if (strncmp((const char *)kbuf.dptr, "CRED/", 5) == 0) {
3358                 (*cred_count)++;
3359         }
3360         return 0;
3361 }
3362
3363 NTSTATUS wcache_count_cached_creds(struct winbindd_domain *domain, int *count)
3364 {
3365         struct winbind_cache *cache = get_cache(domain);
3366
3367         *count = 0;
3368
3369         if (!cache->tdb) {
3370                 return NT_STATUS_INTERNAL_DB_ERROR;
3371         }
3372  
3373         tdb_traverse(cache->tdb, traverse_fn_cached_creds, (void *)count);
3374
3375         return NT_STATUS_OK;
3376 }
3377
3378 struct cred_list {
3379         struct cred_list *prev, *next;
3380         TDB_DATA key;
3381         fstring name;
3382         time_t created;
3383 };
3384 static struct cred_list *wcache_cred_list;
3385
3386 static int traverse_fn_get_credlist(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf, 
3387                                     void *state)
3388 {
3389         struct cred_list *cred;
3390
3391         if (strncmp((const char *)kbuf.dptr, "CRED/", 5) == 0) {
3392
3393                 cred = SMB_MALLOC_P(struct cred_list);
3394                 if (cred == NULL) {
3395                         DEBUG(0,("traverse_fn_remove_first_creds: failed to malloc new entry for list\n"));
3396                         return -1;
3397                 }
3398
3399                 ZERO_STRUCTP(cred);
3400
3401                 /* save a copy of the key */
3402
3403                 fstrcpy(cred->name, (const char *)kbuf.dptr);           
3404                 DLIST_ADD(wcache_cred_list, cred);
3405         }
3406
3407         return 0;
3408 }
3409
3410 NTSTATUS wcache_remove_oldest_cached_creds(struct winbindd_domain *domain, const struct dom_sid *sid)
3411 {
3412         struct winbind_cache *cache = get_cache(domain);
3413         NTSTATUS status;
3414         int ret;
3415         struct cred_list *cred, *oldest = NULL;
3416
3417         if (!cache->tdb) {
3418                 return NT_STATUS_INTERNAL_DB_ERROR;
3419         }
3420
3421         /* we possibly already have an entry */
3422         if (sid && NT_STATUS_IS_OK(wcache_cached_creds_exist(domain, sid))) {
3423
3424                 fstring key_str, tmp;
3425
3426                 DEBUG(11,("we already have an entry, deleting that\n"));
3427
3428                 fstr_sprintf(key_str, "CRED/%s", sid_to_fstring(tmp, sid));
3429
3430                 tdb_delete(cache->tdb, string_tdb_data(key_str));
3431
3432                 return NT_STATUS_OK;
3433         }
3434
3435         ret = tdb_traverse(cache->tdb, traverse_fn_get_credlist, NULL);
3436         if (ret == 0) {
3437                 return NT_STATUS_OK;
3438         } else if ((ret < 0) || (wcache_cred_list == NULL)) {
3439                 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
3440         }
3441
3442         ZERO_STRUCTP(oldest);
3443
3444         for (cred = wcache_cred_list; cred; cred = cred->next) {
3445
3446                 TDB_DATA data;
3447                 time_t t;
3448
3449                 data = tdb_fetch_compat(cache->tdb, string_tdb_data(cred->name));
3450                 if (!data.dptr) {
3451                         DEBUG(10,("wcache_remove_oldest_cached_creds: entry for [%s] not found\n", 
3452                                 cred->name));
3453                         status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
3454                         goto done;
3455                 }
3456
3457                 t = IVAL(data.dptr, 0);
3458                 SAFE_FREE(data.dptr);
3459
3460                 if (!oldest) {
3461                         oldest = SMB_MALLOC_P(struct cred_list);
3462                         if (oldest == NULL) {
3463                                 status = NT_STATUS_NO_MEMORY;
3464                                 goto done;
3465                         }
3466
3467                         fstrcpy(oldest->name, cred->name);
3468                         oldest->created = t;
3469                         continue;
3470                 }
3471
3472                 if (t < oldest->created) {
3473                         fstrcpy(oldest->name, cred->name);
3474                         oldest->created = t;
3475                 }
3476         }
3477
3478         if (tdb_delete(cache->tdb, string_tdb_data(oldest->name)) == 0) {
3479                 status = NT_STATUS_OK;
3480         } else {
3481                 status = NT_STATUS_UNSUCCESSFUL;
3482         }
3483 done:
3484         SAFE_FREE(wcache_cred_list);
3485         SAFE_FREE(oldest);
3486
3487         return status;
3488 }
3489
3490 /* Change the global online/offline state. */
3491 bool set_global_winbindd_state_offline(void)
3492 {
3493         TDB_DATA data;
3494
3495         DEBUG(10,("set_global_winbindd_state_offline: offline requested.\n"));
3496
3497         /* Only go offline if someone has created
3498            the key "WINBINDD_OFFLINE" in the cache tdb. */
3499
3500         if (wcache == NULL || wcache->tdb == NULL) {
3501                 DEBUG(10,("set_global_winbindd_state_offline: wcache not open yet.\n"));
3502                 return false;
3503         }
3504
3505         if (!lp_winbind_offline_logon()) {
3506                 DEBUG(10,("set_global_winbindd_state_offline: rejecting.\n"));
3507                 return false;
3508         }
3509
3510         if (global_winbindd_offline_state) {
3511                 /* Already offline. */
3512                 return true;
3513         }
3514
3515         data = tdb_fetch_bystring( wcache->tdb, "WINBINDD_OFFLINE" );
3516
3517         if (!data.dptr || data.dsize != 4) {
3518                 DEBUG(10,("set_global_winbindd_state_offline: offline state not set.\n"));
3519                 SAFE_FREE(data.dptr);
3520                 return false;
3521         } else {
3522                 DEBUG(10,("set_global_winbindd_state_offline: offline state set.\n"));
3523                 global_winbindd_offline_state = true;
3524                 SAFE_FREE(data.dptr);
3525                 return true;
3526         }
3527 }
3528
3529 void set_global_winbindd_state_online(void)
3530 {
3531         DEBUG(10,("set_global_winbindd_state_online: online requested.\n"));
3532
3533         if (!lp_winbind_offline_logon()) {
3534                 DEBUG(10,("set_global_winbindd_state_online: rejecting.\n"));
3535                 return;
3536         }
3537
3538         if (!global_winbindd_offline_state) {
3539                 /* Already online. */
3540                 return;
3541         }
3542         global_winbindd_offline_state = false;
3543
3544         if (!wcache->tdb) {
3545                 return;
3546         }
3547
3548         /* Ensure there is no key "WINBINDD_OFFLINE" in the cache tdb. */
3549         tdb_delete_bystring(wcache->tdb, "WINBINDD_OFFLINE");
3550 }
3551
3552 bool get_global_winbindd_state_offline(void)
3553 {
3554         return global_winbindd_offline_state;
3555 }
3556
3557 /***********************************************************************
3558  Validate functions for all possible cache tdb keys.
3559 ***********************************************************************/
3560
3561 static struct cache_entry *create_centry_validate(const char *kstr, TDB_DATA data, 
3562                                                   struct tdb_validation_status *state)
3563 {
3564         struct cache_entry *centry;
3565
3566         centry = SMB_XMALLOC_P(struct cache_entry);
3567         centry->data = (unsigned char *)smb_memdup(data.dptr, data.dsize);
3568         if (!centry->data) {
3569                 SAFE_FREE(centry);
3570                 return NULL;
3571         }
3572         centry->len = data.dsize;
3573         centry->ofs = 0;
3574
3575         if (centry->len < 16) {
3576                 /* huh? corrupt cache? */
3577                 DEBUG(0,("create_centry_validate: Corrupt cache for key %s "
3578                          "(len < 16) ?\n", kstr));
3579                 centry_free(centry);
3580                 state->bad_entry = true;
3581                 state->success = false;
3582                 return NULL;
3583         }
3584
3585         centry->status = NT_STATUS(centry_uint32(centry));
3586         centry->sequence_number = centry_uint32(centry);
3587         centry->timeout = centry_uint64_t(centry);
3588         return centry;
3589 }
3590
3591 static int validate_seqnum(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3592                            struct tdb_validation_status *state)
3593 {
3594         if (dbuf.dsize != 8) {
3595                 DEBUG(0,("validate_seqnum: Corrupt cache for key %s (len %u != 8) ?\n",
3596                                 keystr, (unsigned int)dbuf.dsize ));
3597                 state->bad_entry = true;
3598                 return 1;
3599         }
3600         return 0;
3601 }
3602
3603 static int validate_ns(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3604                        struct tdb_validation_status *state)
3605 {
3606         struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3607         if (!centry) {
3608                 return 1;
3609         }
3610
3611         (void)centry_uint32(centry);
3612         if (NT_STATUS_IS_OK(centry->status)) {
3613                 struct dom_sid sid;
3614                 (void)centry_sid(centry, &sid);
3615         }
3616
3617         centry_free(centry);
3618
3619         if (!(state->success)) {
3620                 return 1;
3621         }
3622         DEBUG(10,("validate_ns: %s ok\n", keystr));
3623         return 0;
3624 }
3625
3626 static int validate_sn(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3627                        struct tdb_validation_status *state)
3628 {
3629         struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3630         if (!centry) {
3631                 return 1;
3632         }
3633
3634         if (NT_STATUS_IS_OK(centry->status)) {
3635                 (void)centry_uint32(centry);
3636                 (void)centry_string(centry, mem_ctx);
3637                 (void)centry_string(centry, mem_ctx);
3638         }
3639
3640         centry_free(centry);
3641
3642         if (!(state->success)) {
3643                 return 1;
3644         }
3645         DEBUG(10,("validate_sn: %s ok\n", keystr));
3646         return 0;
3647 }
3648
3649 static int validate_u(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3650                       struct tdb_validation_status *state)
3651 {
3652         struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3653         struct dom_sid sid;
3654
3655         if (!centry) {
3656                 return 1;
3657         }
3658
3659         (void)centry_string(centry, mem_ctx);
3660         (void)centry_string(centry, mem_ctx);
3661         (void)centry_string(centry, mem_ctx);
3662         (void)centry_string(centry, mem_ctx);
3663         (void)centry_uint32(centry);
3664         (void)centry_sid(centry, &sid);
3665         (void)centry_sid(centry, &sid);
3666
3667         centry_free(centry);
3668
3669         if (!(state->success)) {
3670                 return 1;
3671         }
3672         DEBUG(10,("validate_u: %s ok\n", keystr));
3673         return 0;
3674 }
3675
3676 static int validate_loc_pol(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3677                             struct tdb_validation_status *state)
3678 {
3679         struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3680
3681         if (!centry) {
3682                 return 1;
3683         }
3684
3685         (void)centry_nttime(centry);
3686         (void)centry_nttime(centry);
3687         (void)centry_uint16(centry);
3688
3689         centry_free(centry);
3690
3691         if (!(state->success)) {
3692                 return 1;
3693         }
3694         DEBUG(10,("validate_loc_pol: %s ok\n", keystr));
3695         return 0;
3696 }
3697
3698 static int validate_pwd_pol(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3699                             struct tdb_validation_status *state)
3700 {
3701         struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3702
3703         if (!centry) {
3704                 return 1;
3705         }
3706
3707         (void)centry_uint16(centry);
3708         (void)centry_uint16(centry);
3709         (void)centry_uint32(centry);
3710         (void)centry_nttime(centry);
3711         (void)centry_nttime(centry);
3712
3713         centry_free(centry);
3714
3715         if (!(state->success)) {
3716                 return 1;
3717         }
3718         DEBUG(10,("validate_pwd_pol: %s ok\n", keystr));
3719         return 0;
3720 }
3721
3722 static int validate_cred(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3723                          struct tdb_validation_status *state)
3724 {
3725         struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3726
3727         if (!centry) {
3728                 return 1;
3729         }
3730
3731         (void)centry_time(centry);
3732         (void)centry_hash16(centry, mem_ctx);
3733
3734         /* We only have 17 bytes more data in the salted cred case. */
3735         if (centry->len - centry->ofs == 17) {
3736                 (void)centry_hash16(centry, mem_ctx);
3737         }
3738
3739         centry_free(centry);
3740
3741         if (!(state->success)) {
3742                 return 1;
3743         }
3744         DEBUG(10,("validate_cred: %s ok\n", keystr));
3745         return 0;
3746 }
3747
3748 static int validate_ul(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3749                        struct tdb_validation_status *state)
3750 {
3751         struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3752         int32 num_entries, i;
3753
3754         if (!centry) {
3755                 return 1;
3756         }
3757
3758         num_entries = (int32)centry_uint32(centry);
3759
3760         for (i=0; i< num_entries; i++) {
3761                 struct dom_sid sid;
3762                 (void)centry_string(centry, mem_ctx);
3763                 (void)centry_string(centry, mem_ctx);
3764                 (void)centry_string(centry, mem_ctx);
3765                 (void)centry_string(centry, mem_ctx);
3766                 (void)centry_sid(centry, &sid);
3767                 (void)centry_sid(centry, &sid);
3768         }
3769
3770         centry_free(centry);
3771
3772         if (!(state->success)) {
3773                 return 1;
3774         }
3775         DEBUG(10,("validate_ul: %s ok\n", keystr));
3776         return 0;
3777 }
3778
3779 static int validate_gl(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3780                        struct tdb_validation_status *state)
3781 {
3782         struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3783         int32 num_entries, i;
3784
3785         if (!centry) {
3786                 return 1;
3787         }
3788
3789         num_entries = centry_uint32(centry);
3790
3791         for (i=0; i< num_entries; i++) {
3792                 (void)centry_string(centry, mem_ctx);
3793                 (void)centry_string(centry, mem_ctx);
3794                 (void)centry_uint32(centry);
3795         }
3796
3797         centry_free(centry);
3798
3799         if (!(state->success)) {
3800                 return 1;
3801         }
3802         DEBUG(10,("validate_gl: %s ok\n", keystr));
3803         return 0;
3804 }
3805
3806 static int validate_ug(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3807                        struct tdb_validation_status *state)
3808 {
3809         struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3810         int32 num_groups, i;
3811
3812         if (!centry) {
3813                 return 1;
3814         }
3815
3816         num_groups = centry_uint32(centry);
3817
3818         for (i=0; i< num_groups; i++) {
3819                 struct dom_sid sid;
3820                 centry_sid(centry, &sid);
3821         }
3822
3823         centry_free(centry);
3824
3825         if (!(state->success)) {
3826                 return 1;
3827         }
3828         DEBUG(10,("validate_ug: %s ok\n", keystr));
3829         return 0;
3830 }
3831
3832 static int validate_ua(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3833                        struct tdb_validation_status *state)
3834 {
3835         struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3836         int32 num_aliases, i;
3837
3838         if (!centry) {
3839                 return 1;
3840         }
3841
3842         num_aliases = centry_uint32(centry);
3843
3844         for (i=0; i < num_aliases; i++) {
3845                 (void)centry_uint32(centry);
3846         }
3847
3848         centry_free(centry);
3849
3850         if (!(state->success)) {
3851                 return 1;
3852         }
3853         DEBUG(10,("validate_ua: %s ok\n", keystr));
3854         return 0;
3855 }
3856
3857 static int validate_gm(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3858                        struct tdb_validation_status *state)
3859 {
3860         struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3861         int32 num_names, i;
3862
3863         if (!centry) {
3864                 return 1;
3865         }
3866
3867         num_names = centry_uint32(centry);
3868
3869         for (i=0; i< num_names; i++) {
3870                 struct dom_sid sid;
3871                 centry_sid(centry, &sid);
3872                 (void)centry_string(centry, mem_ctx);
3873                 (void)centry_uint32(centry);
3874         }
3875
3876         centry_free(centry);
3877
3878         if (!(state->success)) {
3879                 return 1;
3880         }
3881         DEBUG(10,("validate_gm: %s ok\n", keystr));
3882         return 0;
3883 }
3884
3885 static int validate_dr(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3886                        struct tdb_validation_status *state)
3887 {
3888         /* Can't say anything about this other than must be nonzero. */
3889         if (dbuf.dsize == 0) {
3890                 DEBUG(0,("validate_dr: Corrupt cache for key %s (len == 0) ?\n",
3891                                 keystr));
3892                 state->bad_entry = true;
3893                 state->success = false;
3894                 return 1;
3895         }
3896
3897         DEBUG(10,("validate_dr: %s ok\n", keystr));
3898         return 0;
3899 }
3900
3901 static int validate_de(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3902                        struct tdb_validation_status *state)
3903 {
3904         /* Can't say anything about this other than must be nonzero. */
3905         if (dbuf.dsize == 0) {
3906                 DEBUG(0,("validate_de: Corrupt cache for key %s (len == 0) ?\n",
3907                                 keystr));
3908                 state->bad_entry = true;
3909                 state->success = false;
3910                 return 1;
3911         }
3912
3913         DEBUG(10,("validate_de: %s ok\n", keystr));
3914         return 0;
3915 }
3916
3917 static int validate_pwinfo(TALLOC_CTX *mem_ctx, const char *keystr,
3918                            TDB_DATA dbuf, struct tdb_validation_status *state)
3919 {
3920         struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3921
3922         if (!centry) {
3923                 return 1;
3924         }
3925
3926         (void)centry_string(centry, mem_ctx);
3927         (void)centry_string(centry, mem_ctx);
3928         (void)centry_string(centry, mem_ctx);
3929         (void)centry_uint32(centry);
3930
3931         centry_free(centry);
3932
3933         if (!(state->success)) {
3934                 return 1;
3935         }
3936         DEBUG(10,("validate_pwinfo: %s ok\n", keystr));
3937         return 0;
3938 }
3939
3940 static int validate_nss_an(TALLOC_CTX *mem_ctx, const char *keystr,
3941                            TDB_DATA dbuf,
3942                            struct tdb_validation_status *state)
3943 {
3944         struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3945
3946         if (!centry) {
3947                 return 1;
3948         }
3949
3950         (void)centry_string( centry, mem_ctx );
3951
3952         centry_free(centry);
3953
3954         if (!(state->success)) {
3955                 return 1;
3956         }
3957         DEBUG(10,("validate_pwinfo: %s ok\n", keystr));
3958         return 0;
3959 }
3960
3961 static int validate_nss_na(TALLOC_CTX *mem_ctx, const char *keystr,
3962                            TDB_DATA dbuf,
3963                            struct tdb_validation_status *state)
3964 {
3965         struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3966
3967         if (!centry) {
3968                 return 1;
3969         }
3970
3971         (void)centry_string( centry, mem_ctx );
3972
3973         centry_free(centry);
3974
3975         if (!(state->success)) {
3976                 return 1;
3977         }
3978         DEBUG(10,("validate_pwinfo: %s ok\n", keystr));
3979         return 0;
3980 }
3981
3982 static int validate_trustdomcache(TALLOC_CTX *mem_ctx, const char *keystr, 
3983                                   TDB_DATA dbuf,
3984                                   struct tdb_validation_status *state)
3985 {
3986         if (dbuf.dsize == 0) {
3987                 DEBUG(0, ("validate_trustdomcache: Corrupt cache for "
3988                           "key %s (len ==0) ?\n", keystr));
3989                 state->bad_entry = true;
3990                 state->success = false;
3991                 return 1;
3992         }
3993
3994         DEBUG(10,    ("validate_trustdomcache: %s ok\n", keystr));
3995         DEBUGADD(10, ("  Don't trust me, I am a DUMMY!\n"));
3996         return 0;
3997 }
3998
3999 static int validate_offline(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
4000                             struct tdb_validation_status *state)
4001 {
4002         if (dbuf.dsize != 4) {
4003                 DEBUG(0,("validate_offline: Corrupt cache for key %s (len %u != 4) ?\n",
4004                                 keystr, (unsigned int)dbuf.dsize ));
4005                 state->bad_entry = true;
4006                 state->success = false;
4007                 return 1;
4008         }
4009         DEBUG(10,("validate_offline: %s ok\n", keystr));
4010         return 0;
4011 }
4012
4013 static int validate_ndr(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
4014                         struct tdb_validation_status *state)
4015 {
4016         /*
4017          * Ignore validation for now. The proper way to do this is with a
4018          * checksum. Just pure parsing does not really catch much.
4019          */
4020         return 0;
4021 }
4022
4023 static int validate_cache_version(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
4024                                   struct tdb_validation_status *state)
4025 {
4026         if (dbuf.dsize != 4) {
4027                 DEBUG(0, ("validate_cache_version: Corrupt cache for "
4028                           "key %s (len %u != 4) ?\n", 
4029                           keystr, (unsigned int)dbuf.dsize));
4030                 state->bad_entry = true;
4031                 state->success = false;
4032                 return 1;
4033         }
4034
4035         DEBUG(10, ("validate_cache_version: %s ok\n", keystr));
4036         return 0;
4037 }
4038
4039 /***********************************************************************
4040  A list of all possible cache tdb keys with associated validation
4041  functions.
4042 ***********************************************************************/
4043
4044 struct key_val_struct {
4045         const char *keyname;
4046         int (*validate_data_fn)(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf, struct tdb_validation_status* state);
4047 } key_val[] = {
4048         {"SEQNUM/", validate_seqnum},
4049         {"NS/", validate_ns},
4050         {"SN/", validate_sn},
4051         {"U/", validate_u},
4052         {"LOC_POL/", validate_loc_pol},
4053         {"PWD_POL/", validate_pwd_pol},
4054         {"CRED/", validate_cred},
4055         {"UL/", validate_ul},
4056         {"GL/", validate_gl},
4057         {"UG/", validate_ug},
4058         {"UA", validate_ua},
4059         {"GM/", validate_gm},
4060         {"DR/", validate_dr},
4061         {"DE/", validate_de},
4062         {"NSS/PWINFO/", validate_pwinfo},
4063         {"TRUSTDOMCACHE/", validate_trustdomcache},
4064         {"NSS/NA/", validate_nss_na},
4065         {"NSS/AN/", validate_nss_an},
4066         {"WINBINDD_OFFLINE", validate_offline},
4067         {"NDR/", validate_ndr},
4068         {WINBINDD_CACHE_VERSION_KEYSTR, validate_cache_version},
4069         {NULL, NULL}
4070 };
4071
4072 /***********************************************************************
4073  Function to look at every entry in the tdb and validate it as far as
4074  possible.
4075 ***********************************************************************/
4076
4077 static int cache_traverse_validate_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf, void *state)
4078 {
4079         int i;
4080         unsigned int max_key_len = 1024;
4081         struct tdb_validation_status *v_state = (struct tdb_validation_status *)state;
4082
4083         /* Paranoia check. */
4084         if (strncmp("UA/", (const char *)kbuf.dptr, 3) == 0 ||
4085             strncmp("NDR/", (const char *)kbuf.dptr, 4) == 0) {
4086                 max_key_len = 1024 * 1024;
4087         }
4088         if (kbuf.dsize > max_key_len) {
4089                 DEBUG(0, ("cache_traverse_validate_fn: key length too large: "
4090                           "(%u) > (%u)\n\n",
4091                           (unsigned int)kbuf.dsize, (unsigned int)max_key_len));
4092                 return 1;
4093         }
4094
4095         for (i = 0; key_val[i].keyname; i++) {
4096                 size_t namelen = strlen(key_val[i].keyname);
4097                 if (kbuf.dsize >= namelen && (
4098                                 strncmp(key_val[i].keyname, (const char *)kbuf.dptr, namelen)) == 0) {
4099                         TALLOC_CTX *mem_ctx;
4100                         char *keystr;
4101                         int ret;
4102
4103                         keystr = SMB_MALLOC_ARRAY(char, kbuf.dsize+1);
4104                         if (!keystr) {
4105                                 return 1;
4106                         }
4107                         memcpy(keystr, kbuf.dptr, kbuf.dsize);
4108                         keystr[kbuf.dsize] = '\0';
4109
4110                         mem_ctx = talloc_init("validate_ctx");
4111                         if (!mem_ctx) {
4112                                 SAFE_FREE(keystr);
4113                                 return 1;
4114                         }
4115
4116                         ret = key_val[i].validate_data_fn(mem_ctx, keystr, dbuf, 
4117                                                           v_state);
4118
4119                         SAFE_FREE(keystr);
4120                         talloc_destroy(mem_ctx);
4121                         return ret;
4122                 }
4123         }
4124
4125         DEBUG(0,("cache_traverse_validate_fn: unknown cache entry\nkey :\n"));
4126         dump_data(0, (uint8 *)kbuf.dptr, kbuf.dsize);
4127         DEBUG(0,("data :\n"));
4128         dump_data(0, (uint8 *)dbuf.dptr, dbuf.dsize);
4129         v_state->unknown_key = true;
4130         v_state->success = false;
4131         return 1; /* terminate. */
4132 }
4133
4134 static void validate_panic(const char *const why)
4135 {
4136         DEBUG(0,("validating cache: would panic %s\n", why ));
4137         DEBUGADD(0, ("exiting instead (cache validation mode)\n"));
4138         exit(47);
4139 }
4140
4141 static int wbcache_update_centry_fn(TDB_CONTEXT *tdb,
4142                                     TDB_DATA key,
4143                                     TDB_DATA data,
4144                                     void *state)
4145 {
4146         uint64_t ctimeout;
4147         TDB_DATA blob;
4148
4149         if (is_non_centry_key(key)) {
4150                 return 0;
4151         }
4152
4153         if (data.dptr == NULL || data.dsize == 0) {
4154                 if (tdb_delete(tdb, key) < 0) {
4155                         DEBUG(0, ("tdb_delete for [%s] failed!\n",
4156                                   key.dptr));
4157                         return 1;
4158                 }
4159         }
4160
4161         /* add timeout to blob (uint64_t) */
4162         blob.dsize = data.dsize + 8;
4163
4164         blob.dptr = SMB_XMALLOC_ARRAY(uint8_t, blob.dsize);
4165         if (blob.dptr == NULL) {
4166                 return 1;
4167         }
4168         memset(blob.dptr, 0, blob.dsize);
4169
4170         /* copy status and seqnum */
4171         memcpy(blob.dptr, data.dptr, 8);
4172
4173         /* add timeout */
4174         ctimeout = lp_winbind_cache_time() + time(NULL);
4175         SBVAL(blob.dptr, 8, ctimeout);
4176
4177         /* copy the rest */
4178         memcpy(blob.dptr + 16, data.dptr + 8, data.dsize - 8);
4179
4180         if (tdb_store(tdb, key, blob, TDB_REPLACE) < 0) {
4181                 DEBUG(0, ("tdb_store to update [%s] failed!\n",
4182                           key.dptr));
4183                 SAFE_FREE(blob.dptr);
4184                 return 1;
4185         }
4186
4187         SAFE_FREE(blob.dptr);
4188         return 0;
4189 }
4190
4191 static bool wbcache_upgrade_v1_to_v2(TDB_CONTEXT *tdb)
4192 {
4193         int rc;
4194
4195         DEBUG(1, ("Upgrade to version 2 of the winbindd_cache.tdb\n"));
4196
4197         rc = tdb_traverse(tdb, wbcache_update_centry_fn, NULL);
4198         if (rc < 0) {
4199                 return false;
4200         }
4201
4202         return true;
4203 }
4204
4205 /***********************************************************************
4206  Try and validate every entry in the winbindd cache. If we fail here,
4207  delete the cache tdb and return non-zero.
4208 ***********************************************************************/
4209
4210 int winbindd_validate_cache(void)
4211 {
4212         int ret = -1;
4213         const char *tdb_path = state_path("winbindd_cache.tdb");
4214         TDB_CONTEXT *tdb = NULL;
4215         uint32_t vers_id;
4216         bool ok;
4217
4218         DEBUG(10, ("winbindd_validate_cache: replacing panic function\n"));
4219         smb_panic_fn = validate_panic;
4220
4221         tdb = tdb_open_log(tdb_path, 
4222                            WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
4223                            TDB_INCOMPATIBLE_HASH |
4224                            ( lp_winbind_offline_logon() 
4225                              ? TDB_DEFAULT 
4226                              : TDB_DEFAULT | TDB_CLEAR_IF_FIRST ),
4227                            O_RDWR|O_CREAT, 
4228                            0600);
4229         if (!tdb) {
4230                 DEBUG(0, ("winbindd_validate_cache: "
4231                           "error opening/initializing tdb\n"));
4232                 goto done;
4233         }
4234
4235         /* Version check and upgrade code. */
4236         if (!tdb_fetch_uint32(tdb, WINBINDD_CACHE_VERSION_KEYSTR, &vers_id)) {
4237                 DEBUG(10, ("Fresh database\n"));
4238                 tdb_store_uint32(tdb, WINBINDD_CACHE_VERSION_KEYSTR, WINBINDD_CACHE_VERSION);
4239                 vers_id = WINBINDD_CACHE_VERSION;
4240         }
4241
4242         if (vers_id != WINBINDD_CACHE_VERSION) {
4243                 if (vers_id == WINBINDD_CACHE_VER1) {
4244                         ok = wbcache_upgrade_v1_to_v2(tdb);
4245                         if (!ok) {
4246                                 DEBUG(10, ("winbindd_validate_cache: upgrade to version 2 failed.\n"));
4247                                 unlink(tdb_path);
4248                                 goto done;
4249                         }
4250
4251                         tdb_store_uint32(tdb,
4252                                          WINBINDD_CACHE_VERSION_KEYSTR,
4253                                          WINBINDD_CACHE_VERSION);
4254                         vers_id = WINBINDD_CACHE_VER2;
4255                 }
4256         }
4257
4258         tdb_close(tdb);
4259
4260         ret = tdb_validate_and_backup(tdb_path, cache_traverse_validate_fn);
4261
4262         if (ret != 0) {
4263                 DEBUG(10, ("winbindd_validate_cache: validation not successful.\n"));
4264                 DEBUGADD(10, ("removing tdb %s.\n", tdb_path));
4265                 unlink(tdb_path);
4266         }
4267
4268 done:
4269         DEBUG(10, ("winbindd_validate_cache: restoring panic function\n"));
4270         smb_panic_fn = smb_panic;
4271         return ret;
4272 }
4273
4274 /***********************************************************************
4275  Try and validate every entry in the winbindd cache.
4276 ***********************************************************************/
4277
4278 int winbindd_validate_cache_nobackup(void)
4279 {
4280         int ret = -1;
4281         const char *tdb_path = state_path("winbindd_cache.tdb");
4282
4283         DEBUG(10, ("winbindd_validate_cache: replacing panic function\n"));
4284         smb_panic_fn = validate_panic;
4285
4286
4287         if (wcache == NULL || wcache->tdb == NULL) {
4288                 ret = tdb_validate_open(tdb_path, cache_traverse_validate_fn);
4289         } else {
4290                 ret = tdb_validate(wcache->tdb, cache_traverse_validate_fn);
4291         }
4292
4293         if (ret != 0) {
4294                 DEBUG(10, ("winbindd_validate_cache_nobackup: validation not "
4295                            "successful.\n"));
4296         }
4297
4298         DEBUG(10, ("winbindd_validate_cache_nobackup: restoring panic "
4299                    "function\n"));
4300         smb_panic_fn = smb_panic;
4301         return ret;
4302 }
4303
4304 bool winbindd_cache_validate_and_initialize(void)
4305 {
4306         close_winbindd_cache();
4307
4308         if (lp_winbind_offline_logon()) {
4309                 if (winbindd_validate_cache() < 0) {
4310                         DEBUG(0, ("winbindd cache tdb corrupt and no backup "
4311                                   "could be restored.\n"));
4312                 }
4313         }
4314
4315         return initialize_winbindd_cache();
4316 }
4317
4318 /*********************************************************************
4319  ********************************************************************/
4320
4321 static bool add_wbdomain_to_tdc_array( struct winbindd_domain *new_dom,
4322                                        struct winbindd_tdc_domain **domains, 
4323                                        size_t *num_domains )
4324 {
4325         struct winbindd_tdc_domain *list = NULL;
4326         size_t idx;
4327         int i;
4328         bool set_only = false;
4329
4330         /* don't allow duplicates */
4331
4332         idx = *num_domains;
4333         list = *domains;
4334
4335         for ( i=0; i< (*num_domains); i++ ) {
4336                 if ( strequal( new_dom->name, list[i].domain_name ) ) {
4337                         DEBUG(10,("add_wbdomain_to_tdc_array: Found existing record for %s\n",
4338                                   new_dom->name));
4339                         idx = i;
4340                         set_only = true;
4341
4342                         break;
4343                 }
4344         }
4345
4346         if ( !set_only ) {
4347                 if ( !*domains ) {
4348                         list = talloc_array( NULL, struct winbindd_tdc_domain, 1 );
4349                         idx = 0;
4350                 } else {
4351                         list = talloc_realloc( *domains, *domains, 
4352                                                      struct winbindd_tdc_domain,  
4353                                                      (*num_domains)+1);
4354                         idx = *num_domains;             
4355                 }
4356
4357                 ZERO_STRUCT( list[idx] );
4358         }
4359
4360         if ( !list )
4361                 return false;
4362
4363         list[idx].domain_name = talloc_strdup(list, new_dom->name);
4364         if (list[idx].domain_name == NULL) {
4365                 return false;
4366         }
4367         if (new_dom->alt_name != NULL) {
4368                 list[idx].dns_name = talloc_strdup(list, new_dom->alt_name);
4369                 if (list[idx].dns_name == NULL) {
4370                         return false;
4371                 }
4372         }
4373
4374         if ( !is_null_sid( &new_dom->sid ) ) {
4375                 sid_copy( &list[idx].sid, &new_dom->sid );
4376         } else {
4377                 sid_copy(&list[idx].sid, &global_sid_NULL);
4378         }
4379
4380         if ( new_dom->domain_flags != 0x0 )
4381                 list[idx].trust_flags = new_dom->domain_flags;  
4382
4383         if ( new_dom->domain_type != 0x0 )
4384                 list[idx].trust_type = new_dom->domain_type;
4385
4386         if ( new_dom->domain_trust_attribs != 0x0 )
4387                 list[idx].trust_attribs = new_dom->domain_trust_attribs;
4388
4389         if ( !set_only ) {
4390                 *domains = list;
4391                 *num_domains = idx + 1; 
4392         }
4393
4394         return true;
4395 }
4396
4397 /*********************************************************************
4398  ********************************************************************/
4399
4400 static TDB_DATA make_tdc_key( const char *domain_name )
4401 {
4402         char *keystr = NULL;
4403         TDB_DATA key = { NULL, 0 };
4404
4405         if ( !domain_name ) {
4406                 DEBUG(5,("make_tdc_key: Keyname workgroup is NULL!\n"));
4407                 return key;
4408         }
4409
4410         if (asprintf( &keystr, "TRUSTDOMCACHE/%s", domain_name ) == -1) {
4411                 return key;
4412         }
4413         key = string_term_tdb_data(keystr);
4414
4415         return key;     
4416 }
4417
4418 /*********************************************************************
4419  ********************************************************************/
4420
4421 static int pack_tdc_domains( struct winbindd_tdc_domain *domains, 
4422                              size_t num_domains,
4423                              unsigned char **buf )
4424 {
4425         unsigned char *buffer = NULL;
4426         int len = 0;
4427         int buflen = 0;
4428         int i = 0;
4429
4430         DEBUG(10,("pack_tdc_domains: Packing %d trusted domains\n",
4431                   (int)num_domains));
4432
4433         buflen = 0;
4434
4435  again: 
4436         len = 0;
4437
4438         /* Store the number of array items first */
4439         len += tdb_pack( buffer+len, buflen-len, "d", 
4440                          num_domains );
4441
4442         /* now pack each domain trust record */
4443         for ( i=0; i<num_domains; i++ ) {
4444
4445                 fstring tmp;
4446
4447                 if ( buflen > 0 ) {
4448                         DEBUG(10,("pack_tdc_domains: Packing domain %s (%s)\n",
4449                                   domains[i].domain_name,
4450                                   domains[i].dns_name ? domains[i].dns_name : "UNKNOWN" ));
4451                 }
4452
4453                 len += tdb_pack( buffer+len, buflen-len, "fffddd",
4454                                  domains[i].domain_name,
4455                                  domains[i].dns_name ? domains[i].dns_name : "",
4456                                  sid_to_fstring(tmp, &domains[i].sid),
4457                                  domains[i].trust_flags,
4458                                  domains[i].trust_attribs,
4459                                  domains[i].trust_type );
4460         }
4461
4462         if ( buflen < len ) {
4463                 SAFE_FREE(buffer);
4464                 if ( (buffer = SMB_MALLOC_ARRAY(unsigned char, len)) == NULL ) {
4465                         DEBUG(0,("pack_tdc_domains: failed to alloc buffer!\n"));
4466                         buflen = -1;
4467                         goto done;
4468                 }
4469                 buflen = len;
4470                 goto again;
4471         }
4472
4473         *buf = buffer;  
4474
4475  done:  
4476         return buflen;  
4477 }
4478
4479 /*********************************************************************
4480  ********************************************************************/
4481
4482 static size_t unpack_tdc_domains( unsigned char *buf, int buflen, 
4483                                   struct winbindd_tdc_domain **domains )
4484 {
4485         fstring domain_name, dns_name, sid_string;      
4486         uint32 type, attribs, flags;
4487         int num_domains;
4488         int len = 0;
4489         int i;
4490         struct winbindd_tdc_domain *list = NULL;
4491
4492         /* get the number of domains */
4493         len += tdb_unpack( buf+len, buflen-len, "d", &num_domains);
4494         if ( len == -1 ) {
4495                 DEBUG(5,("unpack_tdc_domains: Failed to unpack domain array\n"));               
4496                 return 0;
4497         }
4498
4499         list = talloc_array( NULL, struct winbindd_tdc_domain, num_domains );
4500         if ( !list ) {
4501                 DEBUG(0,("unpack_tdc_domains: Failed to talloc() domain list!\n"));
4502                 return 0;               
4503         }
4504
4505         for ( i=0; i<num_domains; i++ ) {
4506                 int this_len;
4507
4508                 this_len = tdb_unpack( buf+len, buflen-len, "fffddd",
4509                                    domain_name,
4510                                    dns_name,
4511                                    sid_string,
4512                                    &flags,
4513                                    &attribs,
4514                                    &type );
4515
4516                 if ( this_len == -1 ) {
4517                         DEBUG(5,("unpack_tdc_domains: Failed to unpack domain array\n"));
4518                         TALLOC_FREE( list );                    
4519                         return 0;
4520                 }
4521                 len += this_len;
4522
4523                 DEBUG(11,("unpack_tdc_domains: Unpacking domain %s (%s) "
4524                           "SID %s, flags = 0x%x, attribs = 0x%x, type = 0x%x\n",
4525                           domain_name, dns_name, sid_string,
4526                           flags, attribs, type));
4527
4528                 list[i].domain_name = talloc_strdup( list, domain_name );
4529                 list[i].dns_name = NULL;
4530                 if (dns_name[0] != '\0') {
4531                         list[i].dns_name = talloc_strdup(list, dns_name);
4532                 }
4533                 if ( !string_to_sid( &(list[i].sid), sid_string ) ) {                   
4534                         DEBUG(10,("unpack_tdc_domains: no SID for domain %s\n",
4535                                   domain_name));
4536                 }
4537                 list[i].trust_flags = flags;
4538                 list[i].trust_attribs = attribs;
4539                 list[i].trust_type = type;
4540         }
4541
4542         *domains = list;
4543
4544         return num_domains;
4545 }
4546
4547 /*********************************************************************
4548  ********************************************************************/
4549
4550 static bool wcache_tdc_store_list( struct winbindd_tdc_domain *domains, size_t num_domains )
4551 {
4552         TDB_DATA key = make_tdc_key( lp_workgroup() );   
4553         TDB_DATA data = { NULL, 0 };
4554         int ret;
4555
4556         if ( !key.dptr )
4557                 return false;
4558
4559         /* See if we were asked to delete the cache entry */
4560
4561         if ( !domains ) {
4562                 ret = tdb_delete( wcache->tdb, key );
4563                 goto done;
4564         }
4565
4566         data.dsize = pack_tdc_domains( domains, num_domains, &data.dptr );
4567
4568         if ( !data.dptr ) {
4569                 ret = -1;
4570                 goto done;
4571         }
4572
4573         ret = tdb_store( wcache->tdb, key, data, 0 );
4574
4575  done:
4576         SAFE_FREE( data.dptr );
4577         SAFE_FREE( key.dptr );
4578
4579         return ( ret == 0 );
4580 }
4581
4582 /*********************************************************************
4583  ********************************************************************/
4584
4585 bool wcache_tdc_fetch_list( struct winbindd_tdc_domain **domains, size_t *num_domains )
4586 {
4587         TDB_DATA key = make_tdc_key( lp_workgroup() );
4588         TDB_DATA data = { NULL, 0 };
4589
4590         *domains = NULL;        
4591         *num_domains = 0;       
4592
4593         if ( !key.dptr )
4594                 return false;
4595
4596         data = tdb_fetch_compat( wcache->tdb, key );
4597
4598         SAFE_FREE( key.dptr );
4599
4600         if ( !data.dptr ) 
4601                 return false;
4602
4603         *num_domains = unpack_tdc_domains( data.dptr, data.dsize, domains );
4604
4605         SAFE_FREE( data.dptr );
4606
4607         if ( !*domains )
4608                 return false;
4609
4610         return true;
4611 }
4612
4613 /*********************************************************************
4614  ********************************************************************/
4615
4616 bool wcache_tdc_add_domain( struct winbindd_domain *domain )
4617 {
4618         struct winbindd_tdc_domain *dom_list = NULL;
4619         size_t num_domains = 0;
4620         bool ret = false;
4621
4622         DEBUG(10,("wcache_tdc_add_domain: Adding domain %s (%s), SID %s, "
4623                   "flags = 0x%x, attributes = 0x%x, type = 0x%x\n",
4624                   domain->name, domain->alt_name, 
4625                   sid_string_dbg(&domain->sid),
4626                   domain->domain_flags,
4627                   domain->domain_trust_attribs,
4628                   domain->domain_type));        
4629
4630         if ( !init_wcache() ) {
4631                 return false;
4632         }
4633
4634         /* fetch the list */
4635
4636         wcache_tdc_fetch_list( &dom_list, &num_domains );
4637
4638         /* add the new domain */
4639
4640         if ( !add_wbdomain_to_tdc_array( domain, &dom_list, &num_domains ) ) {
4641                 goto done;              
4642         }       
4643
4644         /* pack the domain */
4645
4646         if ( !wcache_tdc_store_list( dom_list, num_domains ) ) {
4647                 goto done;              
4648         }
4649
4650         /* Success */
4651
4652         ret = true;
4653  done:
4654         TALLOC_FREE( dom_list );
4655
4656         return ret;     
4657 }
4658
4659 static struct winbindd_tdc_domain *wcache_tdc_dup_domain(
4660         TALLOC_CTX *mem_ctx, const struct winbindd_tdc_domain *src)
4661 {
4662         struct winbindd_tdc_domain *dst;
4663
4664         dst = talloc(mem_ctx, struct winbindd_tdc_domain);
4665         if (dst == NULL) {
4666                 goto fail;
4667         }
4668         dst->domain_name = talloc_strdup(dst, src->domain_name);
4669         if (dst->domain_name == NULL) {
4670                 goto fail;
4671         }
4672
4673         dst->dns_name = NULL;
4674         if (src->dns_name != NULL) {
4675                 dst->dns_name = talloc_strdup(dst, src->dns_name);
4676                 if (dst->dns_name == NULL) {
4677                         goto fail;
4678                 }
4679         }
4680
4681         sid_copy(&dst->sid, &src->sid);
4682         dst->trust_flags = src->trust_flags;
4683         dst->trust_type = src->trust_type;
4684         dst->trust_attribs = src->trust_attribs;
4685         return dst;
4686 fail:
4687         TALLOC_FREE(dst);
4688         return NULL;
4689 }
4690
4691 /*********************************************************************
4692  ********************************************************************/
4693
4694 struct winbindd_tdc_domain * wcache_tdc_fetch_domain( TALLOC_CTX *ctx, const char *name )
4695 {
4696         struct winbindd_tdc_domain *dom_list = NULL;
4697         size_t num_domains = 0;
4698         int i;
4699         struct winbindd_tdc_domain *d = NULL;   
4700
4701         DEBUG(10,("wcache_tdc_fetch_domain: Searching for domain %s\n", name));
4702
4703         if ( !init_wcache() ) {
4704                 return NULL;
4705         }
4706
4707         /* fetch the list */
4708
4709         wcache_tdc_fetch_list( &dom_list, &num_domains );
4710
4711         for ( i=0; i<num_domains; i++ ) {
4712                 if ( strequal(name, dom_list[i].domain_name) ||
4713                      strequal(name, dom_list[i].dns_name) )
4714                 {
4715                         DEBUG(10,("wcache_tdc_fetch_domain: Found domain %s\n",
4716                                   name));
4717
4718                         d = wcache_tdc_dup_domain(ctx, &dom_list[i]);
4719                         break;
4720                 }
4721         }
4722
4723         TALLOC_FREE( dom_list );
4724
4725         return d;       
4726 }
4727
4728 /*********************************************************************
4729  ********************************************************************/
4730
4731 struct winbindd_tdc_domain*
4732         wcache_tdc_fetch_domainbysid(TALLOC_CTX *ctx,
4733                                      const struct dom_sid *sid)
4734 {
4735         struct winbindd_tdc_domain *dom_list = NULL;
4736         size_t num_domains = 0;
4737         int i;
4738         struct winbindd_tdc_domain *d = NULL;
4739
4740         DEBUG(10,("wcache_tdc_fetch_domainbysid: Searching for domain %s\n",
4741                   sid_string_dbg(sid)));
4742
4743         if (!init_wcache()) {
4744                 return NULL;
4745         }
4746
4747         /* fetch the list */
4748
4749         wcache_tdc_fetch_list(&dom_list, &num_domains);
4750
4751         for (i = 0; i<num_domains; i++) {
4752                 if (dom_sid_equal(sid, &(dom_list[i].sid))) {
4753                         DEBUG(10, ("wcache_tdc_fetch_domainbysid: "
4754                                    "Found domain %s for SID %s\n",
4755                                    dom_list[i].domain_name,
4756                                    sid_string_dbg(sid)));
4757
4758                         d = wcache_tdc_dup_domain(ctx, &dom_list[i]);
4759                         break;
4760                 }
4761         }
4762
4763         TALLOC_FREE(dom_list);
4764
4765         return d;
4766 }
4767
4768
4769 /*********************************************************************
4770  ********************************************************************/
4771
4772 void wcache_tdc_clear( void )
4773 {
4774         if ( !init_wcache() )
4775                 return;
4776
4777         wcache_tdc_store_list( NULL, 0 );
4778
4779         return; 
4780 }
4781
4782
4783 /*********************************************************************
4784  ********************************************************************/
4785
4786 static void wcache_save_user_pwinfo(struct winbindd_domain *domain, 
4787                                     NTSTATUS status,
4788                                     const struct dom_sid *user_sid,
4789                                     const char *homedir,
4790                                     const char *shell,
4791                                     const char *gecos,
4792                                     uint32 gid)
4793 {
4794         struct cache_entry *centry;
4795         fstring tmp;
4796
4797         if ( (centry = centry_start(domain, status)) == NULL )
4798                 return;
4799
4800         centry_put_string( centry, homedir );
4801         centry_put_string( centry, shell );
4802         centry_put_string( centry, gecos );
4803         centry_put_uint32( centry, gid );
4804
4805         centry_end(centry, "NSS/PWINFO/%s", sid_to_fstring(tmp, user_sid) );
4806
4807         DEBUG(10,("wcache_save_user_pwinfo: %s\n", sid_string_dbg(user_sid) ));
4808
4809         centry_free(centry);
4810 }
4811
4812 #ifdef HAVE_ADS
4813
4814 NTSTATUS nss_get_info_cached( struct winbindd_domain *domain, 
4815                               const struct dom_sid *user_sid,
4816                               TALLOC_CTX *ctx,
4817                               const char **homedir, const char **shell,
4818                               const char **gecos, gid_t *p_gid)
4819 {
4820         struct winbind_cache *cache = get_cache(domain);
4821         struct cache_entry *centry = NULL;
4822         NTSTATUS nt_status;
4823         fstring tmp;
4824
4825         if (!cache->tdb)
4826                 goto do_query;
4827
4828         centry = wcache_fetch(cache, domain, "NSS/PWINFO/%s",
4829                               sid_to_fstring(tmp, user_sid));
4830
4831         if (!centry)
4832                 goto do_query;
4833
4834         *homedir = centry_string( centry, ctx );
4835         *shell   = centry_string( centry, ctx );
4836         *gecos   = centry_string( centry, ctx );
4837         *p_gid   = centry_uint32( centry );     
4838
4839         centry_free(centry);
4840
4841         DEBUG(10,("nss_get_info_cached: [Cached] - user_sid %s\n",
4842                   sid_string_dbg(user_sid)));
4843
4844         return NT_STATUS_OK;
4845
4846 do_query:
4847
4848         nt_status = nss_get_info( domain->name, user_sid, ctx,
4849                                   homedir, shell, gecos, p_gid );
4850
4851         DEBUG(10, ("nss_get_info returned %s\n", nt_errstr(nt_status)));
4852
4853         if ( NT_STATUS_IS_OK(nt_status) ) {
4854                 DEBUG(10, ("result:\n\thomedir = '%s'\n", *homedir));
4855                 DEBUGADD(10, ("\tshell = '%s'\n", *shell));
4856                 DEBUGADD(10, ("\tgecos = '%s'\n", *gecos));
4857                 DEBUGADD(10, ("\tgid = '%u'\n", (unsigned int)*p_gid));
4858
4859                 wcache_save_user_pwinfo( domain, nt_status, user_sid,
4860                                          *homedir, *shell, *gecos, *p_gid );
4861         }       
4862
4863         if ( NT_STATUS_EQUAL( nt_status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND ) ) {
4864                 DEBUG(5,("nss_get_info_cached: Setting domain %s offline\n",
4865                          domain->name ));
4866                 set_domain_offline( domain );
4867         }
4868
4869         return nt_status;       
4870 }
4871
4872 #endif
4873
4874 /* the cache backend methods are exposed via this structure */
4875 struct winbindd_methods cache_methods = {
4876         true,
4877         query_user_list,
4878         enum_dom_groups,
4879         enum_local_groups,
4880         name_to_sid,
4881         sid_to_name,
4882         rids_to_names,
4883         query_user,
4884         lookup_usergroups,
4885         lookup_useraliases,
4886         lookup_groupmem,
4887         sequence_number,
4888         lockout_policy,
4889         password_policy,
4890         trusted_domains
4891 };
4892
4893 static bool wcache_ndr_key(TALLOC_CTX *mem_ctx, const char *domain_name,
4894                            uint32_t opnum, const DATA_BLOB *req,
4895                            TDB_DATA *pkey)
4896 {
4897         char *key;
4898         size_t keylen;
4899
4900         key = talloc_asprintf(mem_ctx, "NDR/%s/%d/", domain_name, (int)opnum);
4901         if (key == NULL) {
4902                 return false;
4903         }
4904         keylen = talloc_get_size(key) - 1;
4905
4906         key = talloc_realloc(mem_ctx, key, char, keylen + req->length);
4907         if (key == NULL) {
4908                 return false;
4909         }
4910         memcpy(key + keylen, req->data, req->length);
4911
4912         pkey->dptr = (uint8_t *)key;
4913         pkey->dsize = talloc_get_size(key);
4914         return true;
4915 }
4916
4917 static bool wcache_opnum_cacheable(uint32_t opnum)
4918 {
4919         switch (opnum) {
4920         case NDR_WBINT_PING:
4921         case NDR_WBINT_QUERYSEQUENCENUMBER:
4922         case NDR_WBINT_ALLOCATEUID:
4923         case NDR_WBINT_ALLOCATEGID:
4924         case NDR_WBINT_CHECKMACHINEACCOUNT:
4925         case NDR_WBINT_CHANGEMACHINEACCOUNT:
4926         case NDR_WBINT_PINGDC:
4927                 return false;
4928         }
4929         return true;
4930 }
4931
4932 bool wcache_fetch_ndr(TALLOC_CTX *mem_ctx, struct winbindd_domain *domain,
4933                       uint32_t opnum, const DATA_BLOB *req, DATA_BLOB *resp)
4934 {
4935         TDB_DATA key, data;
4936         bool ret = false;
4937
4938         if (!wcache_opnum_cacheable(opnum) ||
4939             is_my_own_sam_domain(domain) ||
4940             is_builtin_domain(domain)) {
4941                 return false;
4942         }
4943
4944         if (wcache->tdb == NULL) {
4945                 return false;
4946         }
4947
4948         if (!wcache_ndr_key(talloc_tos(), domain->name, opnum, req, &key)) {
4949                 return false;
4950         }
4951         data = tdb_fetch_compat(wcache->tdb, key);
4952         TALLOC_FREE(key.dptr);
4953
4954         if (data.dptr == NULL) {
4955                 return false;
4956         }
4957         if (data.dsize < 12) {
4958                 goto fail;
4959         }
4960
4961         if (!is_domain_offline(domain)) {
4962                 uint32_t entry_seqnum, dom_seqnum, last_check;
4963                 uint64_t entry_timeout;
4964
4965                 if (!wcache_fetch_seqnum(domain->name, &dom_seqnum,
4966                                          &last_check)) {
4967                         goto fail;
4968                 }
4969                 entry_seqnum = IVAL(data.dptr, 0);
4970                 if (entry_seqnum != dom_seqnum) {
4971                         DEBUG(10, ("Entry has wrong sequence number: %d\n",
4972                                    (int)entry_seqnum));
4973                         goto fail;
4974                 }
4975                 entry_timeout = BVAL(data.dptr, 4);
4976                 if (time(NULL) > entry_timeout) {
4977                         DEBUG(10, ("Entry has timed out\n"));
4978                         goto fail;
4979                 }
4980         }
4981
4982         resp->data = (uint8_t *)talloc_memdup(mem_ctx, data.dptr + 12,
4983                                               data.dsize - 12);
4984         if (resp->data == NULL) {
4985                 DEBUG(10, ("talloc failed\n"));
4986                 goto fail;
4987         }
4988         resp->length = data.dsize - 12;
4989
4990         ret = true;
4991 fail:
4992         SAFE_FREE(data.dptr);
4993         return ret;
4994 }
4995
4996 void wcache_store_ndr(struct winbindd_domain *domain, uint32_t opnum,
4997                       const DATA_BLOB *req, const DATA_BLOB *resp)
4998 {
4999         TDB_DATA key, data;
5000         uint32_t dom_seqnum, last_check;
5001         uint64_t timeout;
5002
5003         if (!wcache_opnum_cacheable(opnum) ||
5004             is_my_own_sam_domain(domain) ||
5005             is_builtin_domain(domain)) {
5006                 return;
5007         }
5008
5009         if (wcache->tdb == NULL) {
5010                 return;
5011         }
5012
5013         if (!wcache_fetch_seqnum(domain->name, &dom_seqnum, &last_check)) {
5014                 DEBUG(10, ("could not fetch seqnum for domain %s\n",
5015                            domain->name));
5016                 return;
5017         }
5018
5019         if (!wcache_ndr_key(talloc_tos(), domain->name, opnum, req, &key)) {
5020                 return;
5021         }
5022
5023         timeout = time(NULL) + lp_winbind_cache_time();
5024
5025         data.dsize = resp->length + 12;
5026         data.dptr = talloc_array(key.dptr, uint8_t, data.dsize);
5027         if (data.dptr == NULL) {
5028                 goto done;
5029         }
5030
5031         SIVAL(data.dptr, 0, dom_seqnum);
5032         SBVAL(data.dptr, 4, timeout);
5033         memcpy(data.dptr + 12, resp->data, resp->length);
5034
5035         tdb_store(wcache->tdb, key, data, 0);
5036
5037 done:
5038         TALLOC_FREE(key.dptr);
5039         return;
5040 }