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