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