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