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