Merge branch 'master' of ctdb into 'master' of samba
[metze/samba/wip.git] / source3 / winbindd / winbindd_cache.c
1 /* 
2    Unix SMB/CIFS implementation.
3
4    Winbind cache backend functions
5
6    Copyright (C) Andrew Tridgell 2001
7    Copyright (C) Gerald Carter   2003-2007
8    Copyright (C) Volker Lendecke 2005
9    Copyright (C) Guenther Deschner 2005
10    Copyright (C) Michael Adam    2007
11
12    This program is free software; you can redistribute it and/or modify
13    it under the terms of the GNU General Public License as published by
14    the Free Software Foundation; either version 3 of the License, or
15    (at your option) any later version.
16
17    This program is distributed in the hope that it will be useful,
18    but WITHOUT ANY WARRANTY; without even the implied warranty of
19    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20    GNU General Public License for more details.
21
22    You should have received a copy of the GNU General Public License
23    along with this program.  If not, see <http://www.gnu.org/licenses/>.
24 */
25
26 #include "includes.h"
27 #include "system/filesys.h"
28 #include "winbindd.h"
29 #include "tdb_validate.h"
30 #include "../libcli/auth/libcli_auth.h"
31 #include "../librpc/gen_ndr/ndr_wbint.h"
32 #include "ads.h"
33 #include "nss_info.h"
34 #include "../libcli/security/security.h"
35 #include "passdb/machine_sid.h"
36 #include "util_tdb.h"
37
38 #undef DBGC_CLASS
39 #define DBGC_CLASS DBGC_WINBIND
40
41 #define WINBINDD_CACHE_VER1 1 /* initial db version */
42 #define WINBINDD_CACHE_VER2 2 /* second version with timeouts for NDR entries */
43
44 #define WINBINDD_CACHE_VERSION WINBINDD_CACHE_VER2
45 #define WINBINDD_CACHE_VERSION_KEYSTR "WINBINDD_CACHE_VERSION"
46
47 extern struct winbindd_methods reconnect_methods;
48 #ifdef HAVE_ADS
49 extern struct winbindd_methods ads_methods;
50 #endif
51 extern struct winbindd_methods builtin_passdb_methods;
52 extern struct winbindd_methods sam_passdb_methods;
53
54 /*
55  * JRA. KEEP THIS LIST UP TO DATE IF YOU ADD CACHE ENTRIES.
56  * Here are the list of entry types that are *not* stored
57  * as form struct cache_entry in the cache.
58  */
59
60 static const char *non_centry_keys[] = {
61         "SEQNUM/",
62         "WINBINDD_OFFLINE",
63         WINBINDD_CACHE_VERSION_KEYSTR,
64         NULL
65 };
66
67 /************************************************************************
68  Is this key a non-centry type ?
69 ************************************************************************/
70
71 static bool is_non_centry_key(TDB_DATA kbuf)
72 {
73         int i;
74
75         if (kbuf.dptr == NULL || kbuf.dsize == 0) {
76                 return false;
77         }
78         for (i = 0; non_centry_keys[i] != NULL; i++) {
79                 size_t namelen = strlen(non_centry_keys[i]);
80                 if (kbuf.dsize < namelen) {
81                         continue;
82                 }
83                 if (strncmp(non_centry_keys[i], (const char *)kbuf.dptr, namelen) == 0) {
84                         return true;
85                 }
86         }
87         return false;
88 }
89
90 /* Global online/offline state - False when online. winbindd starts up online
91    and sets this to true if the first query fails and there's an entry in
92    the cache tdb telling us to stay offline. */
93
94 static bool global_winbindd_offline_state;
95
96 struct winbind_cache {
97         TDB_CONTEXT *tdb;
98 };
99
100 struct cache_entry {
101         NTSTATUS status;
102         uint32 sequence_number;
103         uint64_t timeout;
104         uint8 *data;
105         uint32 len, ofs;
106 };
107
108 void (*smb_panic_fn)(const char *const why) = smb_panic;
109
110 #define WINBINDD_MAX_CACHE_SIZE (50*1024*1024)
111
112 static struct winbind_cache *wcache;
113
114 /* get the winbind_cache structure */
115 static struct winbind_cache *get_cache(struct winbindd_domain *domain)
116 {
117         struct winbind_cache *ret = wcache;
118
119         /* We have to know what type of domain we are dealing with first. */
120
121         if (domain->internal) {
122                 domain->backend = &builtin_passdb_methods;
123                 domain->initialized = True;
124         }
125
126         if (strequal(domain->name, get_global_sam_name()) &&
127             sid_check_is_our_sam(&domain->sid)) {
128                 domain->backend = &sam_passdb_methods;
129                 domain->initialized = True;
130         }
131
132         if ( !domain->initialized ) {
133                 init_dc_connection( domain );
134         }
135
136         /* 
137            OK.  listen up becasue I'm only going to say this once.
138            We have the following scenarios to consider
139            (a) trusted AD domains on a Samba DC,
140            (b) trusted AD domains and we are joined to a non-kerberos domain
141            (c) trusted AD domains and we are joined to a kerberos (AD) domain
142
143            For (a) we can always contact the trusted domain using krb5 
144            since we have the domain trust account password
145
146            For (b) we can only use RPC since we have no way of 
147            getting a krb5 ticket in our own domain
148
149            For (c) we can always use krb5 since we have a kerberos trust
150
151            --jerry
152          */
153
154         if (!domain->backend) {
155 #ifdef HAVE_ADS
156                 struct winbindd_domain *our_domain = domain;
157
158                 /* find our domain first so we can figure out if we 
159                    are joined to a kerberized domain */
160
161                 if ( !domain->primary )
162                         our_domain = find_our_domain();
163
164                 if ((our_domain->active_directory || IS_DC)
165                     && domain->active_directory
166                     && !lp_winbind_rpc_only()) {
167                         DEBUG(5,("get_cache: Setting ADS methods for domain %s\n", domain->name));
168                         domain->backend = &ads_methods;
169                 } else {
170 #endif  /* HAVE_ADS */
171                         DEBUG(5,("get_cache: Setting MS-RPC methods for domain %s\n", domain->name));
172                         domain->backend = &reconnect_methods;
173 #ifdef HAVE_ADS
174                 }
175 #endif  /* HAVE_ADS */
176         }
177
178         if (ret)
179                 return ret;
180
181         ret = SMB_XMALLOC_P(struct winbind_cache);
182         ZERO_STRUCTP(ret);
183
184         wcache = ret;
185         wcache_flush_cache();
186
187         return ret;
188 }
189
190 /*
191   free a centry structure
192 */
193 static void centry_free(struct cache_entry *centry)
194 {
195         if (!centry)
196                 return;
197         SAFE_FREE(centry->data);
198         free(centry);
199 }
200
201 static bool centry_check_bytes(struct cache_entry *centry, size_t nbytes)
202 {
203         if (centry->len - centry->ofs < nbytes) {
204                 DEBUG(0,("centry corruption? needed %u bytes, have %d\n", 
205                          (unsigned int)nbytes,
206                          centry->len - centry->ofs));
207                 return false;
208         }
209         return true;
210 }
211
212 /*
213   pull a uint64_t from a cache entry
214 */
215 static uint64_t centry_uint64_t(struct cache_entry *centry)
216 {
217         uint64_t ret;
218
219         if (!centry_check_bytes(centry, 8)) {
220                 smb_panic_fn("centry_uint64_t");
221         }
222         ret = BVAL(centry->data, centry->ofs);
223         centry->ofs += 8;
224         return ret;
225 }
226
227 /*
228   pull a uint32 from a cache entry 
229 */
230 static uint32 centry_uint32(struct cache_entry *centry)
231 {
232         uint32 ret;
233
234         if (!centry_check_bytes(centry, 4)) {
235                 smb_panic_fn("centry_uint32");
236         }
237         ret = IVAL(centry->data, centry->ofs);
238         centry->ofs += 4;
239         return ret;
240 }
241
242 /*
243   pull a uint16 from a cache entry 
244 */
245 static uint16 centry_uint16(struct cache_entry *centry)
246 {
247         uint16 ret;
248         if (!centry_check_bytes(centry, 2)) {
249                 smb_panic_fn("centry_uint16");
250         }
251         ret = SVAL(centry->data, centry->ofs);
252         centry->ofs += 2;
253         return ret;
254 }
255
256 /*
257   pull a uint8 from a cache entry 
258 */
259 static uint8 centry_uint8(struct cache_entry *centry)
260 {
261         uint8 ret;
262         if (!centry_check_bytes(centry, 1)) {
263                 smb_panic_fn("centry_uint8");
264         }
265         ret = CVAL(centry->data, centry->ofs);
266         centry->ofs += 1;
267         return ret;
268 }
269
270 /*
271   pull a NTTIME from a cache entry 
272 */
273 static NTTIME centry_nttime(struct cache_entry *centry)
274 {
275         NTTIME ret;
276         if (!centry_check_bytes(centry, 8)) {
277                 smb_panic_fn("centry_nttime");
278         }
279         ret = IVAL(centry->data, centry->ofs);
280         centry->ofs += 4;
281         ret += (uint64)IVAL(centry->data, centry->ofs) << 32;
282         centry->ofs += 4;
283         return ret;
284 }
285
286 /*
287   pull a time_t from a cache entry. time_t stored portably as a 64-bit time.
288 */
289 static time_t centry_time(struct cache_entry *centry)
290 {
291         return (time_t)centry_nttime(centry);
292 }
293
294 /* pull a string from a cache entry, using the supplied
295    talloc context 
296 */
297 static char *centry_string(struct cache_entry *centry, TALLOC_CTX *mem_ctx)
298 {
299         uint32 len;
300         char *ret;
301
302         len = centry_uint8(centry);
303
304         if (len == 0xFF) {
305                 /* a deliberate NULL string */
306                 return NULL;
307         }
308
309         if (!centry_check_bytes(centry, (size_t)len)) {
310                 smb_panic_fn("centry_string");
311         }
312
313         ret = talloc_array(mem_ctx, char, len+1);
314         if (!ret) {
315                 smb_panic_fn("centry_string out of memory\n");
316         }
317         memcpy(ret,centry->data + centry->ofs, len);
318         ret[len] = 0;
319         centry->ofs += len;
320         return ret;
321 }
322
323 /* pull a hash16 from a cache entry, using the supplied
324    talloc context 
325 */
326 static char *centry_hash16(struct cache_entry *centry, TALLOC_CTX *mem_ctx)
327 {
328         uint32 len;
329         char *ret;
330
331         len = centry_uint8(centry);
332
333         if (len != 16) {
334                 DEBUG(0,("centry corruption? hash len (%u) != 16\n", 
335                         len ));
336                 return NULL;
337         }
338
339         if (!centry_check_bytes(centry, 16)) {
340                 return NULL;
341         }
342
343         ret = talloc_array(mem_ctx, char, 16);
344         if (!ret) {
345                 smb_panic_fn("centry_hash out of memory\n");
346         }
347         memcpy(ret,centry->data + centry->ofs, 16);
348         centry->ofs += 16;
349         return ret;
350 }
351
352 /* pull a sid from a cache entry, using the supplied
353    talloc context 
354 */
355 static bool centry_sid(struct cache_entry *centry, struct dom_sid *sid)
356 {
357         char *sid_string;
358         bool ret;
359
360         sid_string = centry_string(centry, talloc_tos());
361         if (sid_string == NULL) {
362                 return false;
363         }
364         ret = string_to_sid(sid, sid_string);
365         TALLOC_FREE(sid_string);
366         return ret;
367 }
368
369
370 /*
371   pull a NTSTATUS from a cache entry
372 */
373 static NTSTATUS centry_ntstatus(struct cache_entry *centry)
374 {
375         NTSTATUS status;
376
377         status = NT_STATUS(centry_uint32(centry));
378         return status;
379 }
380
381
382 /* the server is considered down if it can't give us a sequence number */
383 static bool wcache_server_down(struct winbindd_domain *domain)
384 {
385         bool ret;
386
387         if (!wcache->tdb)
388                 return false;
389
390         ret = (domain->sequence_number == DOM_SEQUENCE_NONE);
391
392         if (ret)
393                 DEBUG(10,("wcache_server_down: server for Domain %s down\n", 
394                         domain->name ));
395         return ret;
396 }
397
398 static bool wcache_fetch_seqnum(const char *domain_name, uint32_t *seqnum,
399                                 uint32_t *last_seq_check)
400 {
401         char *key;
402         TDB_DATA data;
403
404         if (wcache->tdb == NULL) {
405                 DEBUG(10,("wcache_fetch_seqnum: tdb == NULL\n"));
406                 return false;
407         }
408
409         key = talloc_asprintf(talloc_tos(), "SEQNUM/%s", domain_name);
410         if (key == NULL) {
411                 DEBUG(10, ("talloc failed\n"));
412                 return false;
413         }
414
415         data = tdb_fetch_bystring(wcache->tdb, key);
416         TALLOC_FREE(key);
417
418         if (data.dptr == NULL) {
419                 DEBUG(10, ("wcache_fetch_seqnum: %s not found\n",
420                            domain_name));
421                 return false;
422         }
423         if (data.dsize != 8) {
424                 DEBUG(10, ("wcache_fetch_seqnum: invalid data size %d\n",
425                            (int)data.dsize));
426                 SAFE_FREE(data.dptr);
427                 return false;
428         }
429
430         *seqnum = IVAL(data.dptr, 0);
431         *last_seq_check = IVAL(data.dptr, 4);
432         SAFE_FREE(data.dptr);
433
434         return true;
435 }
436
437 static NTSTATUS fetch_cache_seqnum( struct winbindd_domain *domain, time_t now )
438 {
439         uint32 last_check, time_diff;
440
441         if (!wcache_fetch_seqnum(domain->name, &domain->sequence_number,
442                                  &last_check)) {
443                 return NT_STATUS_UNSUCCESSFUL;
444         }
445         domain->last_seq_check = last_check;
446
447         /* have we expired? */
448
449         time_diff = now - domain->last_seq_check;
450         if ( time_diff > lp_winbind_cache_time() ) {
451                 DEBUG(10,("fetch_cache_seqnum: timeout [%s][%u @ %u]\n",
452                         domain->name, domain->sequence_number,
453                         (uint32)domain->last_seq_check));
454                 return NT_STATUS_UNSUCCESSFUL;
455         }
456
457         DEBUG(10,("fetch_cache_seqnum: success [%s][%u @ %u]\n", 
458                 domain->name, domain->sequence_number, 
459                 (uint32)domain->last_seq_check));
460
461         return NT_STATUS_OK;
462 }
463
464 bool wcache_store_seqnum(const char *domain_name, uint32_t seqnum,
465                          time_t last_seq_check)
466 {
467         char *key_str;
468         uint8_t buf[8];
469         int ret;
470
471         if (wcache->tdb == NULL) {
472                 DEBUG(10, ("wcache_store_seqnum: wcache->tdb == NULL\n"));
473                 return false;
474         }
475
476         key_str = talloc_asprintf(talloc_tos(), "SEQNUM/%s", domain_name);
477         if (key_str == NULL) {
478                 DEBUG(10, ("talloc_asprintf failed\n"));
479                 return false;
480         }
481
482         SIVAL(buf, 0, seqnum);
483         SIVAL(buf, 4, last_seq_check);
484
485         ret = tdb_store_bystring(wcache->tdb, key_str,
486                                  make_tdb_data(buf, sizeof(buf)), TDB_REPLACE);
487         TALLOC_FREE(key_str);
488         if (ret != 0) {
489                 DEBUG(10, ("tdb_store_bystring failed: %s\n",
490                            tdb_errorstr_compat(wcache->tdb)));
491                 TALLOC_FREE(key_str);
492                 return false;
493         }
494
495         DEBUG(10, ("wcache_store_seqnum: success [%s][%u @ %u]\n",
496                    domain_name, seqnum, (unsigned)last_seq_check));
497
498         return true;
499 }
500
501 static bool store_cache_seqnum( struct winbindd_domain *domain )
502 {
503         return wcache_store_seqnum(domain->name, domain->sequence_number,
504                                    domain->last_seq_check);
505 }
506
507 /*
508   refresh the domain sequence number. If force is true
509   then always refresh it, no matter how recently we fetched it
510 */
511
512 static void refresh_sequence_number(struct winbindd_domain *domain, bool force)
513 {
514         NTSTATUS status;
515         unsigned time_diff;
516         time_t t = time(NULL);
517         unsigned cache_time = lp_winbind_cache_time();
518
519         if (is_domain_offline(domain)) {
520                 return;
521         }
522
523         get_cache( domain );
524
525 #if 0   /* JERRY -- disable as the default cache time is now 5 minutes */
526         /* trying to reconnect is expensive, don't do it too often */
527         if (domain->sequence_number == DOM_SEQUENCE_NONE) {
528                 cache_time *= 8;
529         }
530 #endif
531
532         time_diff = t - domain->last_seq_check;
533
534         /* see if we have to refetch the domain sequence number */
535         if (!force && (time_diff < cache_time) &&
536                         (domain->sequence_number != DOM_SEQUENCE_NONE) &&
537                         NT_STATUS_IS_OK(domain->last_status)) {
538                 DEBUG(10, ("refresh_sequence_number: %s time ok\n", domain->name));
539                 goto done;
540         }
541
542         /* try to get the sequence number from the tdb cache first */
543         /* this will update the timestamp as well */
544
545         status = fetch_cache_seqnum( domain, t );
546         if (NT_STATUS_IS_OK(status) &&
547                         (domain->sequence_number != DOM_SEQUENCE_NONE) &&
548                         NT_STATUS_IS_OK(domain->last_status)) {
549                 goto done;
550         }
551
552         /* important! make sure that we know if this is a native 
553            mode domain or not.  And that we can contact it. */
554
555         if ( winbindd_can_contact_domain( domain ) ) {          
556                 status = domain->backend->sequence_number(domain, 
557                                                           &domain->sequence_number);
558         } else {
559                 /* just use the current time */
560                 status = NT_STATUS_OK;
561                 domain->sequence_number = time(NULL);
562         }
563
564
565         /* the above call could have set our domain->backend to NULL when
566          * coming from offline to online mode, make sure to reinitialize the
567          * backend - Guenther */
568         get_cache( domain );
569
570         if (!NT_STATUS_IS_OK(status)) {
571                 DEBUG(10,("refresh_sequence_number: failed with %s\n", nt_errstr(status)));
572                 domain->sequence_number = DOM_SEQUENCE_NONE;
573         }
574
575         domain->last_status = status;
576         domain->last_seq_check = time(NULL);
577
578         /* save the new sequence number in the cache */
579         store_cache_seqnum( domain );
580
581 done:
582         DEBUG(10, ("refresh_sequence_number: %s seq number is now %d\n", 
583                    domain->name, domain->sequence_number));
584
585         return;
586 }
587
588 /*
589   decide if a cache entry has expired
590 */
591 static bool centry_expired(struct winbindd_domain *domain, const char *keystr, struct cache_entry *centry)
592 {
593         /* If we've been told to be offline - stay in that state... */
594         if (lp_winbind_offline_logon() && global_winbindd_offline_state) {
595                 DEBUG(10,("centry_expired: Key %s for domain %s valid as winbindd is globally offline.\n",
596                         keystr, domain->name ));
597                 return false;
598         }
599
600         /* when the domain is offline return the cached entry.
601          * This deals with transient offline states... */
602
603         if (!domain->online) {
604                 DEBUG(10,("centry_expired: Key %s for domain %s valid as domain is offline.\n",
605                         keystr, domain->name ));
606                 return false;
607         }
608
609         /* if the server is OK and our cache entry came from when it was down then
610            the entry is invalid */
611         if ((domain->sequence_number != DOM_SEQUENCE_NONE) &&  
612             (centry->sequence_number == DOM_SEQUENCE_NONE)) {
613                 DEBUG(10,("centry_expired: Key %s for domain %s invalid sequence.\n",
614                         keystr, domain->name ));
615                 return true;
616         }
617
618         /* if the server is down or the cache entry is not older than the
619            current sequence number or it did not timeout then it is OK */
620         if (wcache_server_down(domain)
621             || ((centry->sequence_number == domain->sequence_number)
622                 && (centry->timeout > time(NULL)))) {
623                 DEBUG(10,("centry_expired: Key %s for domain %s is good.\n",
624                         keystr, domain->name ));
625                 return false;
626         }
627
628         DEBUG(10,("centry_expired: Key %s for domain %s expired\n",
629                 keystr, domain->name ));
630
631         /* it's expired */
632         return true;
633 }
634
635 static struct cache_entry *wcache_fetch_raw(char *kstr)
636 {
637         TDB_DATA data;
638         struct cache_entry *centry;
639         TDB_DATA key;
640
641         key = string_tdb_data(kstr);
642         data = tdb_fetch_compat(wcache->tdb, key);
643         if (!data.dptr) {
644                 /* a cache miss */
645                 return NULL;
646         }
647
648         centry = SMB_XMALLOC_P(struct cache_entry);
649         centry->data = (unsigned char *)data.dptr;
650         centry->len = data.dsize;
651         centry->ofs = 0;
652
653         if (centry->len < 16) {
654                 /* huh? corrupt cache? */
655                 DEBUG(10,("wcache_fetch_raw: Corrupt cache for key %s "
656                           "(len < 16)?\n", kstr));
657                 centry_free(centry);
658                 return NULL;
659         }
660
661         centry->status = centry_ntstatus(centry);
662         centry->sequence_number = centry_uint32(centry);
663         centry->timeout = centry_uint64_t(centry);
664
665         return centry;
666 }
667
668 static bool is_my_own_sam_domain(struct winbindd_domain *domain)
669 {
670         if (strequal(domain->name, get_global_sam_name()) &&
671             sid_check_is_our_sam(&domain->sid)) {
672                 return true;
673         }
674
675         return false;
676 }
677
678 static bool is_builtin_domain(struct winbindd_domain *domain)
679 {
680         if (strequal(domain->name, "BUILTIN") &&
681             sid_check_is_builtin(&domain->sid)) {
682                 return true;
683         }
684
685         return false;
686 }
687
688 /*
689   fetch an entry from the cache, with a varargs key. auto-fetch the sequence
690   number and return status
691 */
692 static struct cache_entry *wcache_fetch(struct winbind_cache *cache, 
693                                         struct winbindd_domain *domain,
694                                         const char *format, ...) PRINTF_ATTRIBUTE(3,4);
695 static struct cache_entry *wcache_fetch(struct winbind_cache *cache, 
696                                         struct winbindd_domain *domain,
697                                         const char *format, ...)
698 {
699         va_list ap;
700         char *kstr;
701         struct cache_entry *centry;
702
703         if (!winbindd_use_cache() ||
704             is_my_own_sam_domain(domain) ||
705             is_builtin_domain(domain)) {
706                 return NULL;
707         }
708
709         refresh_sequence_number(domain, false);
710
711         va_start(ap, format);
712         smb_xvasprintf(&kstr, format, ap);
713         va_end(ap);
714
715         centry = wcache_fetch_raw(kstr);
716         if (centry == NULL) {
717                 free(kstr);
718                 return NULL;
719         }
720
721         if (centry_expired(domain, kstr, centry)) {
722
723                 DEBUG(10,("wcache_fetch: entry %s expired for domain %s\n",
724                          kstr, domain->name ));
725
726                 centry_free(centry);
727                 free(kstr);
728                 return NULL;
729         }
730
731         DEBUG(10,("wcache_fetch: returning entry %s for domain %s\n",
732                  kstr, domain->name ));
733
734         free(kstr);
735         return centry;
736 }
737
738 static void wcache_delete(const char *format, ...) PRINTF_ATTRIBUTE(1,2);
739 static void wcache_delete(const char *format, ...)
740 {
741         va_list ap;
742         char *kstr;
743         TDB_DATA key;
744
745         va_start(ap, format);
746         smb_xvasprintf(&kstr, format, ap);
747         va_end(ap);
748
749         key = string_tdb_data(kstr);
750
751         tdb_delete(wcache->tdb, key);
752         free(kstr);
753 }
754
755 /*
756   make sure we have at least len bytes available in a centry 
757 */
758 static void centry_expand(struct cache_entry *centry, uint32 len)
759 {
760         if (centry->len - centry->ofs >= len)
761                 return;
762         centry->len *= 2;
763         centry->data = SMB_REALLOC_ARRAY(centry->data, unsigned char,
764                                          centry->len);
765         if (!centry->data) {
766                 DEBUG(0,("out of memory: needed %d bytes in centry_expand\n", centry->len));
767                 smb_panic_fn("out of memory in centry_expand");
768         }
769 }
770
771 /*
772   push a uint64_t into a centry
773 */
774 static void centry_put_uint64_t(struct cache_entry *centry, uint64_t v)
775 {
776         centry_expand(centry, 8);
777         SBVAL(centry->data, centry->ofs, v);
778         centry->ofs += 8;
779 }
780
781 /*
782   push a uint32 into a centry 
783 */
784 static void centry_put_uint32(struct cache_entry *centry, uint32 v)
785 {
786         centry_expand(centry, 4);
787         SIVAL(centry->data, centry->ofs, v);
788         centry->ofs += 4;
789 }
790
791 /*
792   push a uint16 into a centry 
793 */
794 static void centry_put_uint16(struct cache_entry *centry, uint16 v)
795 {
796         centry_expand(centry, 2);
797         SSVAL(centry->data, centry->ofs, v);
798         centry->ofs += 2;
799 }
800
801 /*
802   push a uint8 into a centry 
803 */
804 static void centry_put_uint8(struct cache_entry *centry, uint8 v)
805 {
806         centry_expand(centry, 1);
807         SCVAL(centry->data, centry->ofs, v);
808         centry->ofs += 1;
809 }
810
811 /* 
812    push a string into a centry 
813  */
814 static void centry_put_string(struct cache_entry *centry, const char *s)
815 {
816         int len;
817
818         if (!s) {
819                 /* null strings are marked as len 0xFFFF */
820                 centry_put_uint8(centry, 0xFF);
821                 return;
822         }
823
824         len = strlen(s);
825         /* can't handle more than 254 char strings. Truncating is probably best */
826         if (len > 254) {
827                 DEBUG(10,("centry_put_string: truncating len (%d) to: 254\n", len));
828                 len = 254;
829         }
830         centry_put_uint8(centry, len);
831         centry_expand(centry, len);
832         memcpy(centry->data + centry->ofs, s, len);
833         centry->ofs += len;
834 }
835
836 /* 
837    push a 16 byte hash into a centry - treat as 16 byte string.
838  */
839 static void centry_put_hash16(struct cache_entry *centry, const uint8 val[16])
840 {
841         centry_put_uint8(centry, 16);
842         centry_expand(centry, 16);
843         memcpy(centry->data + centry->ofs, val, 16);
844         centry->ofs += 16;
845 }
846
847 static void centry_put_sid(struct cache_entry *centry, const struct dom_sid *sid)
848 {
849         fstring sid_string;
850         centry_put_string(centry, sid_to_fstring(sid_string, sid));
851 }
852
853
854 /*
855   put NTSTATUS into a centry
856 */
857 static void centry_put_ntstatus(struct cache_entry *centry, NTSTATUS status)
858 {
859         uint32 status_value = NT_STATUS_V(status);
860         centry_put_uint32(centry, status_value);
861 }
862
863
864 /*
865   push a NTTIME into a centry 
866 */
867 static void centry_put_nttime(struct cache_entry *centry, NTTIME nt)
868 {
869         centry_expand(centry, 8);
870         SIVAL(centry->data, centry->ofs, nt & 0xFFFFFFFF);
871         centry->ofs += 4;
872         SIVAL(centry->data, centry->ofs, nt >> 32);
873         centry->ofs += 4;
874 }
875
876 /*
877   push a time_t into a centry - use a 64 bit size.
878   NTTIME here is being used as a convenient 64-bit size.
879 */
880 static void centry_put_time(struct cache_entry *centry, time_t t)
881 {
882         NTTIME nt = (NTTIME)t;
883         centry_put_nttime(centry, nt);
884 }
885
886 /*
887   start a centry for output. When finished, call centry_end()
888 */
889 struct cache_entry *centry_start(struct winbindd_domain *domain, NTSTATUS status)
890 {
891         struct cache_entry *centry;
892
893         if (!wcache->tdb)
894                 return NULL;
895
896         centry = SMB_XMALLOC_P(struct cache_entry);
897
898         centry->len = 8192; /* reasonable default */
899         centry->data = SMB_XMALLOC_ARRAY(uint8, centry->len);
900         centry->ofs = 0;
901         centry->sequence_number = domain->sequence_number;
902         centry->timeout = lp_winbind_cache_time() + time(NULL);
903         centry_put_ntstatus(centry, status);
904         centry_put_uint32(centry, centry->sequence_number);
905         centry_put_uint64_t(centry, centry->timeout);
906         return centry;
907 }
908
909 /*
910   finish a centry and write it to the tdb
911 */
912 static void centry_end(struct cache_entry *centry, const char *format, ...) PRINTF_ATTRIBUTE(2,3);
913 static void centry_end(struct cache_entry *centry, const char *format, ...)
914 {
915         va_list ap;
916         char *kstr;
917         TDB_DATA key, data;
918
919         if (!winbindd_use_cache()) {
920                 return;
921         }
922
923         va_start(ap, format);
924         smb_xvasprintf(&kstr, format, ap);
925         va_end(ap);
926
927         key = string_tdb_data(kstr);
928         data.dptr = centry->data;
929         data.dsize = centry->ofs;
930
931         tdb_store(wcache->tdb, key, data, TDB_REPLACE);
932         free(kstr);
933 }
934
935 static void wcache_save_name_to_sid(struct winbindd_domain *domain, 
936                                     NTSTATUS status, const char *domain_name,
937                                     const char *name, const struct dom_sid *sid,
938                                     enum lsa_SidType type)
939 {
940         struct cache_entry *centry;
941         fstring uname;
942
943         centry = centry_start(domain, status);
944         if (!centry)
945                 return;
946
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                         centry_free(centry);
2094                         goto error;
2095                 }
2096
2097                 centry_free(centry);
2098         }
2099
2100         if (!have_mapped) {
2101                 return NT_STATUS_NONE_MAPPED;
2102         }
2103         if (!have_unmapped) {
2104                 return NT_STATUS_OK;
2105         }
2106         return STATUS_SOME_UNMAPPED;
2107
2108  do_query:
2109
2110         TALLOC_FREE(*names);
2111         TALLOC_FREE(*types);
2112
2113         result = domain->backend->rids_to_names(domain, mem_ctx, domain_sid,
2114                                                 rids, num_rids, domain_name,
2115                                                 names, types);
2116
2117         if (NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
2118                 NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2119                 if (!domain->internal && old_status) {
2120                         set_domain_offline(domain);
2121                 }
2122                 if (cache->tdb &&
2123                         !domain->internal &&
2124                         !domain->online &&
2125                         old_status) {
2126                         have_mapped = have_unmapped = false;
2127
2128                         for (i=0; i<num_rids; i++) {
2129                                 struct dom_sid sid;
2130                                 struct cache_entry *centry;
2131                                 fstring tmp;
2132
2133                                 if (!sid_compose(&sid, domain_sid, rids[i])) {
2134                                         result = NT_STATUS_INTERNAL_ERROR;
2135                                         goto error;
2136                                 }
2137
2138                                 centry = wcache_fetch(cache, domain, "SN/%s",
2139                                                       sid_to_fstring(tmp, &sid));
2140                                 if (!centry) {
2141                                         (*types)[i] = SID_NAME_UNKNOWN;
2142                                         (*names)[i] = talloc_strdup(*names, "");
2143                                         continue;
2144                                 }
2145
2146                                 (*types)[i] = SID_NAME_UNKNOWN;
2147                                 (*names)[i] = talloc_strdup(*names, "");
2148
2149                                 if (NT_STATUS_IS_OK(centry->status)) {
2150                                         char *dom;
2151                                         have_mapped = true;
2152                                         (*types)[i] = (enum lsa_SidType)centry_uint32(centry);
2153
2154                                         dom = centry_string(centry, mem_ctx);
2155                                         if (*domain_name == NULL) {
2156                                                 *domain_name = dom;
2157                                         } else {
2158                                                 talloc_free(dom);
2159                                         }
2160
2161                                         (*names)[i] = centry_string(centry, *names);
2162
2163                                 } else if (NT_STATUS_EQUAL(centry->status, NT_STATUS_NONE_MAPPED)) {
2164                                         have_unmapped = true;
2165
2166                                 } else {
2167                                         /* something's definitely wrong */
2168                                         result = centry->status;
2169                                         centry_free(centry);
2170                                         goto error;
2171                                 }
2172
2173                                 centry_free(centry);
2174                         }
2175
2176                         if (!have_mapped) {
2177                                 return NT_STATUS_NONE_MAPPED;
2178                         }
2179                         if (!have_unmapped) {
2180                                 return NT_STATUS_OK;
2181                         }
2182                         return STATUS_SOME_UNMAPPED;
2183                 }
2184         }
2185         /*
2186           None of the queried rids has been found so save all negative entries
2187         */
2188         if (NT_STATUS_EQUAL(result, NT_STATUS_NONE_MAPPED)) {
2189                 for (i = 0; i < num_rids; i++) {
2190                         struct dom_sid sid;
2191                         const char *name = "";
2192                         const enum lsa_SidType type = SID_NAME_UNKNOWN;
2193                         NTSTATUS status = NT_STATUS_NONE_MAPPED;
2194
2195                         if (!sid_compose(&sid, domain_sid, rids[i])) {
2196                                 return NT_STATUS_INTERNAL_ERROR;
2197                         }
2198
2199                         wcache_save_sid_to_name(domain, status, &sid, *domain_name,
2200                                                 name, type);
2201                 }
2202
2203                 return result;
2204         }
2205
2206         /*
2207           Some or all of the queried rids have been found.
2208         */
2209         if (!NT_STATUS_IS_OK(result) &&
2210             !NT_STATUS_EQUAL(result, STATUS_SOME_UNMAPPED)) {
2211                 return result;
2212         }
2213
2214         refresh_sequence_number(domain, false);
2215
2216         for (i=0; i<num_rids; i++) {
2217                 struct dom_sid sid;
2218                 NTSTATUS status;
2219
2220                 if (!sid_compose(&sid, domain_sid, rids[i])) {
2221                         result = NT_STATUS_INTERNAL_ERROR;
2222                         goto error;
2223                 }
2224
2225                 status = (*types)[i] == SID_NAME_UNKNOWN ?
2226                         NT_STATUS_NONE_MAPPED : NT_STATUS_OK;
2227
2228                 wcache_save_sid_to_name(domain, status, &sid, *domain_name,
2229                                         (*names)[i], (*types)[i]);
2230         }
2231
2232         return result;
2233
2234  error:
2235         TALLOC_FREE(*names);
2236         TALLOC_FREE(*types);
2237         return result;
2238 }
2239
2240 NTSTATUS wcache_query_user(struct winbindd_domain *domain,
2241                            TALLOC_CTX *mem_ctx,
2242                            const struct dom_sid *user_sid,
2243                            struct wbint_userinfo *info)
2244 {
2245         struct winbind_cache *cache = get_cache(domain);
2246         struct cache_entry *centry = NULL;
2247         NTSTATUS status;
2248         char *sid_string;
2249
2250         if (cache->tdb == NULL) {
2251                 return NT_STATUS_NOT_FOUND;
2252         }
2253
2254         sid_string = sid_string_tos(user_sid);
2255         if (sid_string == NULL) {
2256                 return NT_STATUS_NO_MEMORY;
2257         }
2258
2259         centry = wcache_fetch(cache, domain, "U/%s", sid_string);
2260         TALLOC_FREE(sid_string);
2261         if (centry == NULL) {
2262                 return NT_STATUS_NOT_FOUND;
2263         }
2264
2265         /*
2266          * If we have an access denied cache entry and a cached info3
2267          * in the samlogon cache then do a query.  This will force the
2268          * rpc back end to return the info3 data.
2269          */
2270
2271         if (NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED) &&
2272             netsamlogon_cache_have(user_sid)) {
2273                 DEBUG(10, ("query_user: cached access denied and have cached "
2274                            "info3\n"));
2275                 domain->last_status = NT_STATUS_OK;
2276                 centry_free(centry);
2277                 return NT_STATUS_NOT_FOUND;
2278         }
2279
2280         /* if status is not ok then this is a negative hit
2281            and the rest of the data doesn't matter */
2282         status = centry->status;
2283         if (NT_STATUS_IS_OK(status)) {
2284                 info->acct_name = centry_string(centry, mem_ctx);
2285                 info->full_name = centry_string(centry, mem_ctx);
2286                 info->homedir = centry_string(centry, mem_ctx);
2287                 info->shell = centry_string(centry, mem_ctx);
2288                 info->primary_gid = centry_uint32(centry);
2289                 centry_sid(centry, &info->user_sid);
2290                 centry_sid(centry, &info->group_sid);
2291         }
2292
2293         DEBUG(10,("query_user: [Cached] - cached info for domain %s status: "
2294                   "%s\n", domain->name, nt_errstr(status) ));
2295
2296         centry_free(centry);
2297         return status;
2298 }
2299
2300 /* Lookup user information from a rid */
2301 static NTSTATUS query_user(struct winbindd_domain *domain,
2302                            TALLOC_CTX *mem_ctx,
2303                            const struct dom_sid *user_sid,
2304                            struct wbint_userinfo *info)
2305 {
2306         NTSTATUS status;
2307         bool old_status;
2308
2309         old_status = domain->online;
2310         status = wcache_query_user(domain, mem_ctx, user_sid, info);
2311         if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2312                 return status;
2313         }
2314
2315         ZERO_STRUCTP(info);
2316
2317         /* Return status value returned by seq number check */
2318
2319         if (!NT_STATUS_IS_OK(domain->last_status))
2320                 return domain->last_status;
2321
2322         DEBUG(10,("query_user: [Cached] - doing backend query for info for domain %s\n",
2323                 domain->name ));
2324
2325         status = domain->backend->query_user(domain, mem_ctx, user_sid, info);
2326
2327         if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2328                 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2329                 if (!domain->internal && old_status) {
2330                         set_domain_offline(domain);
2331                 }
2332                 if (!domain->internal &&
2333                         !domain->online &&
2334                         old_status) {
2335                         NTSTATUS cache_status;
2336                         cache_status = wcache_query_user(domain, mem_ctx, user_sid, info);
2337                         return cache_status;
2338                 }
2339         }
2340         /* and save it */
2341         refresh_sequence_number(domain, false);
2342         if (!NT_STATUS_IS_OK(status)) {
2343                 return status;
2344         }
2345         wcache_save_user(domain, status, info);
2346
2347         return status;
2348 }
2349
2350 NTSTATUS wcache_lookup_usergroups(struct winbindd_domain *domain,
2351                                   TALLOC_CTX *mem_ctx,
2352                                   const struct dom_sid *user_sid,
2353                                   uint32_t *pnum_sids,
2354                                   struct dom_sid **psids)
2355 {
2356         struct winbind_cache *cache = get_cache(domain);
2357         struct cache_entry *centry = NULL;
2358         NTSTATUS status;
2359         uint32_t i, num_sids;
2360         struct dom_sid *sids;
2361         fstring sid_string;
2362
2363         if (cache->tdb == NULL) {
2364                 return NT_STATUS_NOT_FOUND;
2365         }
2366
2367         centry = wcache_fetch(cache, domain, "UG/%s",
2368                               sid_to_fstring(sid_string, user_sid));
2369         if (centry == NULL) {
2370                 return NT_STATUS_NOT_FOUND;
2371         }
2372
2373         /* If we have an access denied cache entry and a cached info3 in the
2374            samlogon cache then do a query.  This will force the rpc back end
2375            to return the info3 data. */
2376
2377         if (NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)
2378             && netsamlogon_cache_have(user_sid)) {
2379                 DEBUG(10, ("lookup_usergroups: cached access denied and have "
2380                            "cached info3\n"));
2381                 domain->last_status = NT_STATUS_OK;
2382                 centry_free(centry);
2383                 return NT_STATUS_NOT_FOUND;
2384         }
2385
2386         num_sids = centry_uint32(centry);
2387         sids = talloc_array(mem_ctx, struct dom_sid, num_sids);
2388         if (sids == NULL) {
2389                 centry_free(centry);
2390                 return NT_STATUS_NO_MEMORY;
2391         }
2392
2393         for (i=0; i<num_sids; i++) {
2394                 centry_sid(centry, &sids[i]);
2395         }
2396
2397         status = centry->status;
2398
2399         DEBUG(10,("lookup_usergroups: [Cached] - cached info for domain %s "
2400                   "status: %s\n", domain->name, nt_errstr(status)));
2401
2402         centry_free(centry);
2403
2404         *pnum_sids = num_sids;
2405         *psids = sids;
2406         return status;
2407 }
2408
2409 /* Lookup groups a user is a member of. */
2410 static NTSTATUS lookup_usergroups(struct winbindd_domain *domain,
2411                                   TALLOC_CTX *mem_ctx,
2412                                   const struct dom_sid *user_sid,
2413                                   uint32 *num_groups, struct dom_sid **user_gids)
2414 {
2415         struct cache_entry *centry = NULL;
2416         NTSTATUS status;
2417         unsigned int i;
2418         fstring sid_string;
2419         bool old_status;
2420
2421         old_status = domain->online;
2422         status = wcache_lookup_usergroups(domain, mem_ctx, user_sid,
2423                                           num_groups, user_gids);
2424         if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2425                 return status;
2426         }
2427
2428         (*num_groups) = 0;
2429         (*user_gids) = NULL;
2430
2431         /* Return status value returned by seq number check */
2432
2433         if (!NT_STATUS_IS_OK(domain->last_status))
2434                 return domain->last_status;
2435
2436         DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info for domain %s\n",
2437                 domain->name ));
2438
2439         status = domain->backend->lookup_usergroups(domain, mem_ctx, user_sid, num_groups, user_gids);
2440
2441         if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2442                 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2443                 if (!domain->internal && old_status) {
2444                         set_domain_offline(domain);
2445                 }
2446                 if (!domain->internal &&
2447                         !domain->online &&
2448                         old_status) {
2449                         NTSTATUS cache_status;
2450                         cache_status = wcache_lookup_usergroups(domain, mem_ctx, user_sid,
2451                                                           num_groups, user_gids);
2452                         return cache_status;
2453                 }
2454         }
2455         if ( NT_STATUS_EQUAL(status, NT_STATUS_SYNCHRONIZATION_REQUIRED) )
2456                 goto skip_save;
2457
2458         /* and save it */
2459         refresh_sequence_number(domain, false);
2460         if (!NT_STATUS_IS_OK(status)) {
2461                 return status;
2462         }
2463         centry = centry_start(domain, status);
2464         if (!centry)
2465                 goto skip_save;
2466
2467         centry_put_uint32(centry, *num_groups);
2468         for (i=0; i<(*num_groups); i++) {
2469                 centry_put_sid(centry, &(*user_gids)[i]);
2470         }       
2471
2472         centry_end(centry, "UG/%s", sid_to_fstring(sid_string, user_sid));
2473         centry_free(centry);
2474
2475 skip_save:
2476         return status;
2477 }
2478
2479 static char *wcache_make_sidlist(TALLOC_CTX *mem_ctx, uint32_t num_sids,
2480                                  const struct dom_sid *sids)
2481 {
2482         uint32_t i;
2483         char *sidlist;
2484
2485         sidlist = talloc_strdup(mem_ctx, "");
2486         if (sidlist == NULL) {
2487                 return NULL;
2488         }
2489         for (i=0; i<num_sids; i++) {
2490                 fstring tmp;
2491                 sidlist = talloc_asprintf_append_buffer(
2492                         sidlist, "/%s", sid_to_fstring(tmp, &sids[i]));
2493                 if (sidlist == NULL) {
2494                         return NULL;
2495                 }
2496         }
2497         return sidlist;
2498 }
2499
2500 NTSTATUS wcache_lookup_useraliases(struct winbindd_domain *domain,
2501                                    TALLOC_CTX *mem_ctx, uint32_t num_sids,
2502                                    const struct dom_sid *sids,
2503                                    uint32_t *pnum_aliases, uint32_t **paliases)
2504 {
2505         struct winbind_cache *cache = get_cache(domain);
2506         struct cache_entry *centry = NULL;
2507         uint32_t num_aliases;
2508         uint32_t *aliases;
2509         NTSTATUS status;
2510         char *sidlist;
2511         int i;
2512
2513         if (cache->tdb == NULL) {
2514                 return NT_STATUS_NOT_FOUND;
2515         }
2516
2517         if (num_sids == 0) {
2518                 *pnum_aliases = 0;
2519                 *paliases = NULL;
2520                 return NT_STATUS_OK;
2521         }
2522
2523         /* We need to cache indexed by the whole list of SIDs, the aliases
2524          * resulting might come from any of the SIDs. */
2525
2526         sidlist = wcache_make_sidlist(talloc_tos(), num_sids, sids);
2527         if (sidlist == NULL) {
2528                 return NT_STATUS_NO_MEMORY;
2529         }
2530
2531         centry = wcache_fetch(cache, domain, "UA%s", sidlist);
2532         TALLOC_FREE(sidlist);
2533         if (centry == NULL) {
2534                 return NT_STATUS_NOT_FOUND;
2535         }
2536
2537         num_aliases = centry_uint32(centry);
2538         aliases = talloc_array(mem_ctx, uint32_t, num_aliases);
2539         if (aliases == NULL) {
2540                 centry_free(centry);
2541                 return NT_STATUS_NO_MEMORY;
2542         }
2543
2544         for (i=0; i<num_aliases; i++) {
2545                 aliases[i] = centry_uint32(centry);
2546         }
2547
2548         status = centry->status;
2549
2550         DEBUG(10,("lookup_useraliases: [Cached] - cached info for domain: %s "
2551                   "status %s\n", domain->name, nt_errstr(status)));
2552
2553         centry_free(centry);
2554
2555         *pnum_aliases = num_aliases;
2556         *paliases = aliases;
2557
2558         return status;
2559 }
2560
2561 static NTSTATUS lookup_useraliases(struct winbindd_domain *domain,
2562                                    TALLOC_CTX *mem_ctx,
2563                                    uint32 num_sids, const struct dom_sid *sids,
2564                                    uint32 *num_aliases, uint32 **alias_rids)
2565 {
2566         struct cache_entry *centry = NULL;
2567         NTSTATUS status;
2568         char *sidlist;
2569         int i;
2570         bool old_status;
2571
2572         old_status = domain->online;
2573         status = wcache_lookup_useraliases(domain, mem_ctx, num_sids, sids,
2574                                            num_aliases, alias_rids);
2575         if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2576                 return status;
2577         }
2578
2579         (*num_aliases) = 0;
2580         (*alias_rids) = NULL;
2581
2582         if (!NT_STATUS_IS_OK(domain->last_status))
2583                 return domain->last_status;
2584
2585         DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info "
2586                   "for domain %s\n", domain->name ));
2587
2588         sidlist = wcache_make_sidlist(talloc_tos(), num_sids, sids);
2589         if (sidlist == NULL) {
2590                 return NT_STATUS_NO_MEMORY;
2591         }
2592
2593         status = domain->backend->lookup_useraliases(domain, mem_ctx,
2594                                                      num_sids, sids,
2595                                                      num_aliases, alias_rids);
2596
2597         if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2598                 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2599                 if (!domain->internal && old_status) {
2600                         set_domain_offline(domain);
2601                 }
2602                 if (!domain->internal &&
2603                         !domain->online &&
2604                         old_status) {
2605                         NTSTATUS cache_status;
2606                         cache_status = wcache_lookup_useraliases(domain, mem_ctx, num_sids,
2607                                                                  sids, num_aliases, alias_rids);
2608                         return cache_status;
2609                 }
2610         }
2611         /* and save it */
2612         refresh_sequence_number(domain, false);
2613         if (!NT_STATUS_IS_OK(status)) {
2614                 return status;
2615         }
2616         centry = centry_start(domain, status);
2617         if (!centry)
2618                 goto skip_save;
2619         centry_put_uint32(centry, *num_aliases);
2620         for (i=0; i<(*num_aliases); i++)
2621                 centry_put_uint32(centry, (*alias_rids)[i]);
2622         centry_end(centry, "UA%s", sidlist);
2623         centry_free(centry);
2624
2625  skip_save:
2626         return status;
2627 }
2628
2629 NTSTATUS wcache_lookup_groupmem(struct winbindd_domain *domain,
2630                                 TALLOC_CTX *mem_ctx,
2631                                 const struct dom_sid *group_sid,
2632                                 uint32_t *num_names,
2633                                 struct dom_sid **sid_mem, char ***names,
2634                                 uint32_t **name_types)
2635 {
2636         struct winbind_cache *cache = get_cache(domain);
2637         struct cache_entry *centry = NULL;
2638         NTSTATUS status;
2639         unsigned int i;
2640         char *sid_string;
2641
2642         if (cache->tdb == NULL) {
2643                 return NT_STATUS_NOT_FOUND;
2644         }
2645
2646         sid_string = sid_string_tos(group_sid);
2647         if (sid_string == NULL) {
2648                 return NT_STATUS_NO_MEMORY;
2649         }
2650
2651         centry = wcache_fetch(cache, domain, "GM/%s", sid_string);
2652         TALLOC_FREE(sid_string);
2653         if (centry == NULL) {
2654                 return NT_STATUS_NOT_FOUND;
2655         }
2656
2657         *sid_mem = NULL;
2658         *names = NULL;
2659         *name_types = NULL;
2660
2661         *num_names = centry_uint32(centry);
2662         if (*num_names == 0) {
2663                 centry_free(centry);
2664                 return NT_STATUS_OK;
2665         }
2666
2667         *sid_mem = talloc_array(mem_ctx, struct dom_sid, *num_names);
2668         *names = talloc_array(mem_ctx, char *, *num_names);
2669         *name_types = talloc_array(mem_ctx, uint32, *num_names);
2670
2671         if ((*sid_mem == NULL) || (*names == NULL) || (*name_types == NULL)) {
2672                 TALLOC_FREE(*sid_mem);
2673                 TALLOC_FREE(*names);
2674                 TALLOC_FREE(*name_types);
2675                 centry_free(centry);
2676                 return NT_STATUS_NO_MEMORY;
2677         }
2678
2679         for (i=0; i<(*num_names); i++) {
2680                 centry_sid(centry, &(*sid_mem)[i]);
2681                 (*names)[i] = centry_string(centry, mem_ctx);
2682                 (*name_types)[i] = centry_uint32(centry);
2683         }
2684
2685         status = centry->status;
2686
2687         DEBUG(10,("lookup_groupmem: [Cached] - cached info for domain %s "
2688                   "status: %s\n", domain->name, nt_errstr(status)));
2689
2690         centry_free(centry);
2691         return status;
2692 }
2693
2694 static NTSTATUS lookup_groupmem(struct winbindd_domain *domain,
2695                                 TALLOC_CTX *mem_ctx,
2696                                 const struct dom_sid *group_sid,
2697                                 enum lsa_SidType type,
2698                                 uint32 *num_names,
2699                                 struct dom_sid **sid_mem, char ***names,
2700                                 uint32 **name_types)
2701 {
2702         struct cache_entry *centry = NULL;
2703         NTSTATUS status;
2704         unsigned int i;
2705         fstring sid_string;
2706         bool old_status;
2707
2708         old_status = domain->online;
2709         status = wcache_lookup_groupmem(domain, mem_ctx, group_sid, num_names,
2710                                         sid_mem, names, name_types);
2711         if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2712                 return status;
2713         }
2714
2715         (*num_names) = 0;
2716         (*sid_mem) = NULL;
2717         (*names) = NULL;
2718         (*name_types) = NULL;
2719
2720         /* Return status value returned by seq number check */
2721
2722         if (!NT_STATUS_IS_OK(domain->last_status))
2723                 return domain->last_status;
2724
2725         DEBUG(10,("lookup_groupmem: [Cached] - doing backend query for info for domain %s\n",
2726                 domain->name ));
2727
2728         status = domain->backend->lookup_groupmem(domain, mem_ctx, group_sid,
2729                                                   type, num_names,
2730                                                   sid_mem, names, name_types);
2731
2732         if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2733                 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2734                 if (!domain->internal && old_status) {
2735                         set_domain_offline(domain);
2736                 }
2737                 if (!domain->internal &&
2738                         !domain->online &&
2739                         old_status) {
2740                         NTSTATUS cache_status;
2741                         cache_status = wcache_lookup_groupmem(domain, mem_ctx, group_sid,
2742                                                               num_names, sid_mem, names,
2743                                                               name_types);
2744                         return cache_status;
2745                 }
2746         }
2747         /* and save it */
2748         refresh_sequence_number(domain, false);
2749         if (!NT_STATUS_IS_OK(status)) {
2750                 return status;
2751         }
2752         centry = centry_start(domain, status);
2753         if (!centry)
2754                 goto skip_save;
2755         centry_put_uint32(centry, *num_names);
2756         for (i=0; i<(*num_names); i++) {
2757                 centry_put_sid(centry, &(*sid_mem)[i]);
2758                 centry_put_string(centry, (*names)[i]);
2759                 centry_put_uint32(centry, (*name_types)[i]);
2760         }       
2761         centry_end(centry, "GM/%s", sid_to_fstring(sid_string, group_sid));
2762         centry_free(centry);
2763
2764 skip_save:
2765         return status;
2766 }
2767
2768 /* find the sequence number for a domain */
2769 static NTSTATUS sequence_number(struct winbindd_domain *domain, uint32 *seq)
2770 {
2771         refresh_sequence_number(domain, false);
2772
2773         *seq = domain->sequence_number;
2774
2775         return NT_STATUS_OK;
2776 }
2777
2778 /* enumerate trusted domains 
2779  * (we need to have the list of trustdoms in the cache when we go offline) -
2780  * Guenther */
2781 static NTSTATUS trusted_domains(struct winbindd_domain *domain,
2782                                 TALLOC_CTX *mem_ctx,
2783                                 struct netr_DomainTrustList *trusts)
2784 {
2785         NTSTATUS status;
2786         struct winbind_cache *cache;
2787         struct winbindd_tdc_domain *dom_list = NULL;
2788         size_t num_domains = 0;
2789         bool retval = false;
2790         int i;
2791         bool old_status;
2792
2793         old_status = domain->online;
2794         trusts->count = 0;
2795         trusts->array = NULL;
2796
2797         cache = get_cache(domain);
2798         if (!cache || !cache->tdb) {
2799                 goto do_query;
2800         }
2801
2802         if (domain->online) {
2803                 goto do_query;
2804         }
2805
2806         retval = wcache_tdc_fetch_list(&dom_list, &num_domains);
2807         if (!retval || !num_domains || !dom_list) {
2808                 TALLOC_FREE(dom_list);
2809                 goto do_query;
2810         }
2811
2812 do_fetch_cache:
2813         trusts->array = talloc_zero_array(mem_ctx, struct netr_DomainTrust, num_domains);
2814         if (!trusts->array) {
2815                 TALLOC_FREE(dom_list);
2816                 return NT_STATUS_NO_MEMORY;
2817         }
2818
2819         for (i = 0; i < num_domains; i++) {
2820                 struct netr_DomainTrust *trust;
2821                 struct dom_sid *sid;
2822                 struct winbindd_domain *dom;
2823
2824                 dom = find_domain_from_name_noinit(dom_list[i].domain_name);
2825                 if (dom && dom->internal) {
2826                         continue;
2827                 }
2828
2829                 trust = &trusts->array[trusts->count];
2830                 trust->netbios_name = talloc_strdup(trusts->array, dom_list[i].domain_name);
2831                 trust->dns_name = talloc_strdup(trusts->array, dom_list[i].dns_name);
2832                 sid = talloc(trusts->array, struct dom_sid);
2833                 if (!trust->netbios_name || !trust->dns_name ||
2834                         !sid) {
2835                         TALLOC_FREE(dom_list);
2836                         TALLOC_FREE(trusts->array);
2837                         return NT_STATUS_NO_MEMORY;
2838                 }
2839
2840                 trust->trust_flags = dom_list[i].trust_flags;
2841                 trust->trust_attributes = dom_list[i].trust_attribs;
2842                 trust->trust_type = dom_list[i].trust_type;
2843                 sid_copy(sid, &dom_list[i].sid);
2844                 trust->sid = sid;
2845                 trusts->count++;
2846         }
2847
2848         TALLOC_FREE(dom_list);
2849         return NT_STATUS_OK;
2850
2851 do_query:
2852         /* Return status value returned by seq number check */
2853
2854         if (!NT_STATUS_IS_OK(domain->last_status))
2855                 return domain->last_status;
2856
2857         DEBUG(10,("trusted_domains: [Cached] - doing backend query for info for domain %s\n",
2858                 domain->name ));
2859
2860         status = domain->backend->trusted_domains(domain, mem_ctx, trusts);
2861
2862         if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2863                 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2864                 if (!domain->internal && old_status) {
2865                         set_domain_offline(domain);
2866                 }
2867                 if (!domain->internal &&
2868                         !domain->online &&
2869                         old_status) {
2870                         retval = wcache_tdc_fetch_list(&dom_list, &num_domains);
2871                         if (retval && num_domains && dom_list) {
2872                                 TALLOC_FREE(trusts->array);
2873                                 trusts->count = 0;
2874                                 goto do_fetch_cache;
2875                         }
2876                 }
2877         }
2878         /* no trusts gives NT_STATUS_NO_MORE_ENTRIES resetting to NT_STATUS_OK
2879          * so that the generic centry handling still applies correctly -
2880          * Guenther*/
2881
2882         if (!NT_STATUS_IS_ERR(status)) {
2883                 status = NT_STATUS_OK;
2884         }
2885         return status;
2886 }       
2887
2888 /* get lockout policy */
2889 static NTSTATUS lockout_policy(struct winbindd_domain *domain,
2890                                TALLOC_CTX *mem_ctx,
2891                                struct samr_DomInfo12 *policy)
2892 {
2893         struct winbind_cache *cache = get_cache(domain);
2894         struct cache_entry *centry = NULL;
2895         NTSTATUS status;
2896         bool old_status;
2897
2898         old_status = domain->online;
2899         if (!cache->tdb)
2900                 goto do_query;
2901
2902         centry = wcache_fetch(cache, domain, "LOC_POL/%s", domain->name);
2903
2904         if (!centry)
2905                 goto do_query;
2906
2907 do_fetch_cache:
2908         policy->lockout_duration = centry_nttime(centry);
2909         policy->lockout_window = centry_nttime(centry);
2910         policy->lockout_threshold = centry_uint16(centry);
2911
2912         status = centry->status;
2913
2914         DEBUG(10,("lockout_policy: [Cached] - cached info for domain %s status: %s\n",
2915                 domain->name, nt_errstr(status) ));
2916
2917         centry_free(centry);
2918         return status;
2919
2920 do_query:
2921         ZERO_STRUCTP(policy);
2922
2923         /* Return status value returned by seq number check */
2924
2925         if (!NT_STATUS_IS_OK(domain->last_status))
2926                 return domain->last_status;
2927
2928         DEBUG(10,("lockout_policy: [Cached] - doing backend query for info for domain %s\n",
2929                 domain->name ));
2930
2931         status = domain->backend->lockout_policy(domain, mem_ctx, policy);
2932
2933         if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2934                 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2935                 if (!domain->internal && old_status) {
2936                         set_domain_offline(domain);
2937                 }
2938                 if (cache->tdb &&
2939                         !domain->internal &&
2940                         !domain->online &&
2941                         old_status) {
2942                         centry = wcache_fetch(cache, domain, "LOC_POL/%s", domain->name);
2943                         if (centry) {
2944                                 goto do_fetch_cache;
2945                         }
2946                 }
2947         }
2948         /* and save it */
2949         refresh_sequence_number(domain, false);
2950         if (!NT_STATUS_IS_OK(status)) {
2951                 return status;
2952         }
2953         wcache_save_lockout_policy(domain, status, policy);
2954
2955         return status;
2956 }
2957
2958 /* get password policy */
2959 static NTSTATUS password_policy(struct winbindd_domain *domain,
2960                                 TALLOC_CTX *mem_ctx,
2961                                 struct samr_DomInfo1 *policy)
2962 {
2963         struct winbind_cache *cache = get_cache(domain);
2964         struct cache_entry *centry = NULL;
2965         NTSTATUS status;
2966         bool old_status;
2967
2968         old_status = domain->online;
2969         if (!cache->tdb)
2970                 goto do_query;
2971
2972         centry = wcache_fetch(cache, domain, "PWD_POL/%s", domain->name);
2973
2974         if (!centry)
2975                 goto do_query;
2976
2977 do_fetch_cache:
2978         policy->min_password_length = centry_uint16(centry);
2979         policy->password_history_length = centry_uint16(centry);
2980         policy->password_properties = centry_uint32(centry);
2981         policy->max_password_age = centry_nttime(centry);
2982         policy->min_password_age = centry_nttime(centry);
2983
2984         status = centry->status;
2985
2986         DEBUG(10,("lockout_policy: [Cached] - cached info for domain %s status: %s\n",
2987                 domain->name, nt_errstr(status) ));
2988
2989         centry_free(centry);
2990         return status;
2991
2992 do_query:
2993         ZERO_STRUCTP(policy);
2994
2995         /* Return status value returned by seq number check */
2996
2997         if (!NT_STATUS_IS_OK(domain->last_status))
2998                 return domain->last_status;
2999
3000         DEBUG(10,("password_policy: [Cached] - doing backend query for info for domain %s\n",
3001                 domain->name ));
3002
3003         status = domain->backend->password_policy(domain, mem_ctx, policy);
3004
3005         if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
3006                 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
3007                 if (!domain->internal && old_status) {
3008                         set_domain_offline(domain);
3009                 }
3010                 if (cache->tdb &&
3011                         !domain->internal &&
3012                         !domain->online &&
3013                         old_status) {
3014                         centry = wcache_fetch(cache, domain, "PWD_POL/%s", domain->name);
3015                         if (centry) {
3016                                 goto do_fetch_cache;
3017                         }
3018                 }
3019         }
3020         /* and save it */
3021         refresh_sequence_number(domain, false);
3022         if (!NT_STATUS_IS_OK(status)) {
3023                 return status;
3024         }
3025         wcache_save_password_policy(domain, status, policy);
3026
3027         return status;
3028 }
3029
3030
3031 /* Invalidate cached user and group lists coherently */
3032
3033 static int traverse_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf, 
3034                        void *state)
3035 {
3036         if (strncmp((const char *)kbuf.dptr, "UL/", 3) == 0 ||
3037             strncmp((const char *)kbuf.dptr, "GL/", 3) == 0)
3038                 tdb_delete(the_tdb, kbuf);
3039
3040         return 0;
3041 }
3042
3043 /* Invalidate the getpwnam and getgroups entries for a winbindd domain */
3044
3045 void wcache_invalidate_samlogon(struct winbindd_domain *domain, 
3046                                 const struct dom_sid *sid)
3047 {
3048         fstring key_str, sid_string;
3049         struct winbind_cache *cache;
3050
3051         /* dont clear cached U/SID and UG/SID entries when we want to logon
3052          * offline - gd */
3053
3054         if (lp_winbind_offline_logon()) {
3055                 return;
3056         }
3057
3058         if (!domain)
3059                 return;
3060
3061         cache = get_cache(domain);
3062
3063         if (!cache->tdb) {
3064                 return;
3065         }
3066
3067         /* Clear U/SID cache entry */
3068         fstr_sprintf(key_str, "U/%s", sid_to_fstring(sid_string, sid));
3069         DEBUG(10, ("wcache_invalidate_samlogon: clearing %s\n", key_str));
3070         tdb_delete(cache->tdb, string_tdb_data(key_str));
3071
3072         /* Clear UG/SID cache entry */
3073         fstr_sprintf(key_str, "UG/%s", sid_to_fstring(sid_string, sid));
3074         DEBUG(10, ("wcache_invalidate_samlogon: clearing %s\n", key_str));
3075         tdb_delete(cache->tdb, string_tdb_data(key_str));
3076
3077         /* Samba/winbindd never needs this. */
3078         netsamlogon_clear_cached_user(sid);
3079 }
3080
3081 bool wcache_invalidate_cache(void)
3082 {
3083         struct winbindd_domain *domain;
3084
3085         for (domain = domain_list(); domain; domain = domain->next) {
3086                 struct winbind_cache *cache = get_cache(domain);
3087
3088                 DEBUG(10, ("wcache_invalidate_cache: invalidating cache "
3089                            "entries for %s\n", domain->name));
3090                 if (cache) {
3091                         if (cache->tdb) {
3092                                 tdb_traverse(cache->tdb, traverse_fn, NULL);
3093                         } else {
3094                                 return false;
3095                         }
3096                 }
3097         }
3098         return true;
3099 }
3100
3101 bool wcache_invalidate_cache_noinit(void)
3102 {
3103         struct winbindd_domain *domain;
3104
3105         for (domain = domain_list(); domain; domain = domain->next) {
3106                 struct winbind_cache *cache;
3107
3108                 /* Skip uninitialized domains. */
3109                 if (!domain->initialized && !domain->internal) {
3110                         continue;
3111                 }
3112
3113                 cache = get_cache(domain);
3114
3115                 DEBUG(10, ("wcache_invalidate_cache: invalidating cache "
3116                            "entries for %s\n", domain->name));
3117                 if (cache) {
3118                         if (cache->tdb) {
3119                                 tdb_traverse(cache->tdb, traverse_fn, NULL);
3120                                 /*
3121                                  * Flushing cache has nothing to with domains.
3122                                  * return here if we successfully flushed once.
3123                                  * To avoid unnecessary traversing the cache.
3124                                  */
3125                                 return true;
3126                         } else {
3127                                 return false;
3128                         }
3129                 }
3130         }
3131         return true;
3132 }
3133
3134 bool init_wcache(void)
3135 {
3136         if (wcache == NULL) {
3137                 wcache = SMB_XMALLOC_P(struct winbind_cache);
3138                 ZERO_STRUCTP(wcache);
3139         }
3140
3141         if (wcache->tdb != NULL)
3142                 return true;
3143
3144         /* when working offline we must not clear the cache on restart */
3145         wcache->tdb = tdb_open_log(state_path("winbindd_cache.tdb"),
3146                                 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE, 
3147                                 TDB_INCOMPATIBLE_HASH |
3148                                         (lp_winbind_offline_logon() ? TDB_DEFAULT : (TDB_DEFAULT | TDB_CLEAR_IF_FIRST)),
3149                                 O_RDWR|O_CREAT, 0600);
3150
3151         if (wcache->tdb == NULL) {
3152                 DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
3153                 return false;
3154         }
3155
3156         return true;
3157 }
3158
3159 /************************************************************************
3160  This is called by the parent to initialize the cache file.
3161  We don't need sophisticated locking here as we know we're the
3162  only opener.
3163 ************************************************************************/
3164
3165 bool initialize_winbindd_cache(void)
3166 {
3167         bool cache_bad = true;
3168         uint32 vers;
3169
3170         if (!init_wcache()) {
3171                 DEBUG(0,("initialize_winbindd_cache: init_wcache failed.\n"));
3172                 return false;
3173         }
3174
3175         /* Check version number. */
3176         if (tdb_fetch_uint32(wcache->tdb, WINBINDD_CACHE_VERSION_KEYSTR, &vers) &&
3177                         vers == WINBINDD_CACHE_VERSION) {
3178                 cache_bad = false;
3179         }
3180
3181         if (cache_bad) {
3182                 DEBUG(0,("initialize_winbindd_cache: clearing cache "
3183                         "and re-creating with version number %d\n",
3184                         WINBINDD_CACHE_VERSION ));
3185
3186                 tdb_close(wcache->tdb);
3187                 wcache->tdb = NULL;
3188
3189                 if (unlink(state_path("winbindd_cache.tdb")) == -1) {
3190                         DEBUG(0,("initialize_winbindd_cache: unlink %s failed %s ",
3191                                 state_path("winbindd_cache.tdb"),
3192                                 strerror(errno) ));
3193                         return false;
3194                 }
3195                 if (!init_wcache()) {
3196                         DEBUG(0,("initialize_winbindd_cache: re-initialization "
3197                                         "init_wcache failed.\n"));
3198                         return false;
3199                 }
3200
3201                 /* Write the version. */
3202                 if (!tdb_store_uint32(wcache->tdb, WINBINDD_CACHE_VERSION_KEYSTR, WINBINDD_CACHE_VERSION)) {
3203                         DEBUG(0,("initialize_winbindd_cache: version number store failed %s\n",
3204                                 tdb_errorstr_compat(wcache->tdb) ));
3205                         return false;
3206                 }
3207         }
3208
3209         tdb_close(wcache->tdb);
3210         wcache->tdb = NULL;
3211         return true;
3212 }
3213
3214 void close_winbindd_cache(void)
3215 {
3216         if (!wcache) {
3217                 return;
3218         }
3219         if (wcache->tdb) {
3220                 tdb_close(wcache->tdb);
3221                 wcache->tdb = NULL;
3222         }
3223 }
3224
3225 bool lookup_cached_sid(TALLOC_CTX *mem_ctx, const struct dom_sid *sid,
3226                        char **domain_name, char **name,
3227                        enum lsa_SidType *type)
3228 {
3229         struct winbindd_domain *domain;
3230         NTSTATUS status;
3231
3232         domain = find_lookup_domain_from_sid(sid);
3233         if (domain == NULL) {
3234                 return false;
3235         }
3236         status = wcache_sid_to_name(domain, sid, mem_ctx, domain_name, name,
3237                                     type);
3238         return NT_STATUS_IS_OK(status);
3239 }
3240
3241 bool lookup_cached_name(const char *domain_name,
3242                         const char *name,
3243                         struct dom_sid *sid,
3244                         enum lsa_SidType *type)
3245 {
3246         struct winbindd_domain *domain;
3247         NTSTATUS status;
3248         bool original_online_state;
3249
3250         domain = find_lookup_domain_from_name(domain_name);
3251         if (domain == NULL) {
3252                 return false;
3253         }
3254
3255         /* If we are doing a cached logon, temporarily set the domain
3256            offline so the cache won't expire the entry */
3257
3258         original_online_state = domain->online;
3259         domain->online = false;
3260         status = wcache_name_to_sid(domain, domain_name, name, sid, type);
3261         domain->online = original_online_state;
3262
3263         return NT_STATUS_IS_OK(status);
3264 }
3265
3266 void cache_name2sid(struct winbindd_domain *domain, 
3267                     const char *domain_name, const char *name,
3268                     enum lsa_SidType type, const struct dom_sid *sid)
3269 {
3270         refresh_sequence_number(domain, false);
3271         wcache_save_name_to_sid(domain, NT_STATUS_OK, domain_name, name,
3272                                 sid, type);
3273 }
3274
3275 /*
3276  * The original idea that this cache only contains centries has
3277  * been blurred - now other stuff gets put in here. Ensure we
3278  * ignore these things on cleanup.
3279  */
3280
3281 static int traverse_fn_cleanup(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, 
3282                                TDB_DATA dbuf, void *state)
3283 {
3284         struct cache_entry *centry;
3285
3286         if (is_non_centry_key(kbuf)) {
3287                 return 0;
3288         }
3289
3290         centry = wcache_fetch_raw((char *)kbuf.dptr);
3291         if (!centry) {
3292                 return 0;
3293         }
3294
3295         if (!NT_STATUS_IS_OK(centry->status)) {
3296                 DEBUG(10,("deleting centry %s\n", (const char *)kbuf.dptr));
3297                 tdb_delete(the_tdb, kbuf);
3298         }
3299
3300         centry_free(centry);
3301         return 0;
3302 }
3303
3304 /* flush the cache */
3305 void wcache_flush_cache(void)
3306 {
3307         if (!wcache)
3308                 return;
3309         if (wcache->tdb) {
3310                 tdb_close(wcache->tdb);
3311                 wcache->tdb = NULL;
3312         }
3313         if (!winbindd_use_cache()) {
3314                 return;
3315         }
3316
3317         /* when working offline we must not clear the cache on restart */
3318         wcache->tdb = tdb_open_log(state_path("winbindd_cache.tdb"),
3319                                 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE, 
3320                                 TDB_INCOMPATIBLE_HASH |
3321                                 (lp_winbind_offline_logon() ? TDB_DEFAULT : (TDB_DEFAULT | TDB_CLEAR_IF_FIRST)),
3322                                 O_RDWR|O_CREAT, 0600);
3323
3324         if (!wcache->tdb) {
3325                 DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
3326                 return;
3327         }
3328
3329         tdb_traverse(wcache->tdb, traverse_fn_cleanup, NULL);
3330
3331         DEBUG(10,("wcache_flush_cache success\n"));
3332 }
3333
3334 /* Count cached creds */
3335
3336 static int traverse_fn_cached_creds(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf, 
3337                                     void *state)
3338 {
3339         int *cred_count = (int*)state;
3340  
3341         if (strncmp((const char *)kbuf.dptr, "CRED/", 5) == 0) {
3342                 (*cred_count)++;
3343         }
3344         return 0;
3345 }
3346
3347 NTSTATUS wcache_count_cached_creds(struct winbindd_domain *domain, int *count)
3348 {
3349         struct winbind_cache *cache = get_cache(domain);
3350
3351         *count = 0;
3352
3353         if (!cache->tdb) {
3354                 return NT_STATUS_INTERNAL_DB_ERROR;
3355         }
3356  
3357         tdb_traverse(cache->tdb, traverse_fn_cached_creds, (void *)count);
3358
3359         return NT_STATUS_OK;
3360 }
3361
3362 struct cred_list {
3363         struct cred_list *prev, *next;
3364         TDB_DATA key;
3365         fstring name;
3366         time_t created;
3367 };
3368 static struct cred_list *wcache_cred_list;
3369
3370 static int traverse_fn_get_credlist(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf, 
3371                                     void *state)
3372 {
3373         struct cred_list *cred;
3374
3375         if (strncmp((const char *)kbuf.dptr, "CRED/", 5) == 0) {
3376
3377                 cred = SMB_MALLOC_P(struct cred_list);
3378                 if (cred == NULL) {
3379                         DEBUG(0,("traverse_fn_remove_first_creds: failed to malloc new entry for list\n"));
3380                         return -1;
3381                 }
3382
3383                 ZERO_STRUCTP(cred);
3384
3385                 /* save a copy of the key */
3386
3387                 fstrcpy(cred->name, (const char *)kbuf.dptr);           
3388                 DLIST_ADD(wcache_cred_list, cred);
3389         }
3390
3391         return 0;
3392 }
3393
3394 NTSTATUS wcache_remove_oldest_cached_creds(struct winbindd_domain *domain, const struct dom_sid *sid)
3395 {
3396         struct winbind_cache *cache = get_cache(domain);
3397         NTSTATUS status;
3398         int ret;
3399         struct cred_list *cred, *oldest = NULL;
3400
3401         if (!cache->tdb) {
3402                 return NT_STATUS_INTERNAL_DB_ERROR;
3403         }
3404
3405         /* we possibly already have an entry */
3406         if (sid && NT_STATUS_IS_OK(wcache_cached_creds_exist(domain, sid))) {
3407
3408                 fstring key_str, tmp;
3409
3410                 DEBUG(11,("we already have an entry, deleting that\n"));
3411
3412                 fstr_sprintf(key_str, "CRED/%s", sid_to_fstring(tmp, sid));
3413
3414                 tdb_delete(cache->tdb, string_tdb_data(key_str));
3415
3416                 return NT_STATUS_OK;
3417         }
3418
3419         ret = tdb_traverse(cache->tdb, traverse_fn_get_credlist, NULL);
3420         if (ret == 0) {
3421                 return NT_STATUS_OK;
3422         } else if ((ret < 0) || (wcache_cred_list == NULL)) {
3423                 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
3424         }
3425
3426         ZERO_STRUCTP(oldest);
3427
3428         for (cred = wcache_cred_list; cred; cred = cred->next) {
3429
3430                 TDB_DATA data;
3431                 time_t t;
3432
3433                 data = tdb_fetch_compat(cache->tdb, string_tdb_data(cred->name));
3434                 if (!data.dptr) {
3435                         DEBUG(10,("wcache_remove_oldest_cached_creds: entry for [%s] not found\n", 
3436                                 cred->name));
3437                         status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
3438                         goto done;
3439                 }
3440
3441                 t = IVAL(data.dptr, 0);
3442                 SAFE_FREE(data.dptr);
3443
3444                 if (!oldest) {
3445                         oldest = SMB_MALLOC_P(struct cred_list);
3446                         if (oldest == NULL) {
3447                                 status = NT_STATUS_NO_MEMORY;
3448                                 goto done;
3449                         }
3450
3451                         fstrcpy(oldest->name, cred->name);
3452                         oldest->created = t;
3453                         continue;
3454                 }
3455
3456                 if (t < oldest->created) {
3457                         fstrcpy(oldest->name, cred->name);
3458                         oldest->created = t;
3459                 }
3460         }
3461
3462         if (tdb_delete(cache->tdb, string_tdb_data(oldest->name)) == 0) {
3463                 status = NT_STATUS_OK;
3464         } else {
3465                 status = NT_STATUS_UNSUCCESSFUL;
3466         }
3467 done:
3468         SAFE_FREE(wcache_cred_list);
3469         SAFE_FREE(oldest);
3470
3471         return status;
3472 }
3473
3474 /* Change the global online/offline state. */
3475 bool set_global_winbindd_state_offline(void)
3476 {
3477         TDB_DATA data;
3478
3479         DEBUG(10,("set_global_winbindd_state_offline: offline requested.\n"));
3480
3481         /* Only go offline if someone has created
3482            the key "WINBINDD_OFFLINE" in the cache tdb. */
3483
3484         if (wcache == NULL || wcache->tdb == NULL) {
3485                 DEBUG(10,("set_global_winbindd_state_offline: wcache not open yet.\n"));
3486                 return false;
3487         }
3488
3489         if (!lp_winbind_offline_logon()) {
3490                 DEBUG(10,("set_global_winbindd_state_offline: rejecting.\n"));
3491                 return false;
3492         }
3493
3494         if (global_winbindd_offline_state) {
3495                 /* Already offline. */
3496                 return true;
3497         }
3498
3499         data = tdb_fetch_bystring( wcache->tdb, "WINBINDD_OFFLINE" );
3500
3501         if (!data.dptr || data.dsize != 4) {
3502                 DEBUG(10,("set_global_winbindd_state_offline: offline state not set.\n"));
3503                 SAFE_FREE(data.dptr);
3504                 return false;
3505         } else {
3506                 DEBUG(10,("set_global_winbindd_state_offline: offline state set.\n"));
3507                 global_winbindd_offline_state = true;
3508                 SAFE_FREE(data.dptr);
3509                 return true;
3510         }
3511 }
3512
3513 void set_global_winbindd_state_online(void)
3514 {
3515         DEBUG(10,("set_global_winbindd_state_online: online requested.\n"));
3516
3517         if (!lp_winbind_offline_logon()) {
3518                 DEBUG(10,("set_global_winbindd_state_online: rejecting.\n"));
3519                 return;
3520         }
3521
3522         if (!global_winbindd_offline_state) {
3523                 /* Already online. */
3524                 return;
3525         }
3526         global_winbindd_offline_state = false;
3527
3528         if (!wcache->tdb) {
3529                 return;
3530         }
3531
3532         /* Ensure there is no key "WINBINDD_OFFLINE" in the cache tdb. */
3533         tdb_delete_bystring(wcache->tdb, "WINBINDD_OFFLINE");
3534 }
3535
3536 bool get_global_winbindd_state_offline(void)
3537 {
3538         return global_winbindd_offline_state;
3539 }
3540
3541 /***********************************************************************
3542  Validate functions for all possible cache tdb keys.
3543 ***********************************************************************/
3544
3545 static struct cache_entry *create_centry_validate(const char *kstr, TDB_DATA data, 
3546                                                   struct tdb_validation_status *state)
3547 {
3548         struct cache_entry *centry;
3549
3550         centry = SMB_XMALLOC_P(struct cache_entry);
3551         centry->data = (unsigned char *)memdup(data.dptr, data.dsize);
3552         if (!centry->data) {
3553                 SAFE_FREE(centry);
3554                 return NULL;
3555         }
3556         centry->len = data.dsize;
3557         centry->ofs = 0;
3558
3559         if (centry->len < 16) {
3560                 /* huh? corrupt cache? */
3561                 DEBUG(0,("create_centry_validate: Corrupt cache for key %s "
3562                          "(len < 16) ?\n", kstr));
3563                 centry_free(centry);
3564                 state->bad_entry = true;
3565                 state->success = false;
3566                 return NULL;
3567         }
3568
3569         centry->status = NT_STATUS(centry_uint32(centry));
3570         centry->sequence_number = centry_uint32(centry);
3571         centry->timeout = centry_uint64_t(centry);
3572         return centry;
3573 }
3574
3575 static int validate_seqnum(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3576                            struct tdb_validation_status *state)
3577 {
3578         if (dbuf.dsize != 8) {
3579                 DEBUG(0,("validate_seqnum: Corrupt cache for key %s (len %u != 8) ?\n",
3580                                 keystr, (unsigned int)dbuf.dsize ));
3581                 state->bad_entry = true;
3582                 return 1;
3583         }
3584         return 0;
3585 }
3586
3587 static int validate_ns(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3588                        struct tdb_validation_status *state)
3589 {
3590         struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3591         if (!centry) {
3592                 return 1;
3593         }
3594
3595         (void)centry_uint32(centry);
3596         if (NT_STATUS_IS_OK(centry->status)) {
3597                 struct dom_sid sid;
3598                 (void)centry_sid(centry, &sid);
3599         }
3600
3601         centry_free(centry);
3602
3603         if (!(state->success)) {
3604                 return 1;
3605         }
3606         DEBUG(10,("validate_ns: %s ok\n", keystr));
3607         return 0;
3608 }
3609
3610 static int validate_sn(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3611                        struct tdb_validation_status *state)
3612 {
3613         struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3614         if (!centry) {
3615                 return 1;
3616         }
3617
3618         if (NT_STATUS_IS_OK(centry->status)) {
3619                 (void)centry_uint32(centry);
3620                 (void)centry_string(centry, mem_ctx);
3621                 (void)centry_string(centry, mem_ctx);
3622         }
3623
3624         centry_free(centry);
3625
3626         if (!(state->success)) {
3627                 return 1;
3628         }
3629         DEBUG(10,("validate_sn: %s ok\n", keystr));
3630         return 0;
3631 }
3632
3633 static int validate_u(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3634                       struct tdb_validation_status *state)
3635 {
3636         struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3637         struct dom_sid sid;
3638
3639         if (!centry) {
3640                 return 1;
3641         }
3642
3643         (void)centry_string(centry, mem_ctx);
3644         (void)centry_string(centry, mem_ctx);
3645         (void)centry_string(centry, mem_ctx);
3646         (void)centry_string(centry, mem_ctx);
3647         (void)centry_uint32(centry);
3648         (void)centry_sid(centry, &sid);
3649         (void)centry_sid(centry, &sid);
3650
3651         centry_free(centry);
3652
3653         if (!(state->success)) {
3654                 return 1;
3655         }
3656         DEBUG(10,("validate_u: %s ok\n", keystr));
3657         return 0;
3658 }
3659
3660 static int validate_loc_pol(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3661                             struct tdb_validation_status *state)
3662 {
3663         struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3664
3665         if (!centry) {
3666                 return 1;
3667         }
3668
3669         (void)centry_nttime(centry);
3670         (void)centry_nttime(centry);
3671         (void)centry_uint16(centry);
3672
3673         centry_free(centry);
3674
3675         if (!(state->success)) {
3676                 return 1;
3677         }
3678         DEBUG(10,("validate_loc_pol: %s ok\n", keystr));
3679         return 0;
3680 }
3681
3682 static int validate_pwd_pol(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3683                             struct tdb_validation_status *state)
3684 {
3685         struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3686
3687         if (!centry) {
3688                 return 1;
3689         }
3690
3691         (void)centry_uint16(centry);
3692         (void)centry_uint16(centry);
3693         (void)centry_uint32(centry);
3694         (void)centry_nttime(centry);
3695         (void)centry_nttime(centry);
3696
3697         centry_free(centry);
3698
3699         if (!(state->success)) {
3700                 return 1;
3701         }
3702         DEBUG(10,("validate_pwd_pol: %s ok\n", keystr));
3703         return 0;
3704 }
3705
3706 static int validate_cred(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3707                          struct tdb_validation_status *state)
3708 {
3709         struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3710
3711         if (!centry) {
3712                 return 1;
3713         }
3714
3715         (void)centry_time(centry);
3716         (void)centry_hash16(centry, mem_ctx);
3717
3718         /* We only have 17 bytes more data in the salted cred case. */
3719         if (centry->len - centry->ofs == 17) {
3720                 (void)centry_hash16(centry, mem_ctx);
3721         }
3722
3723         centry_free(centry);
3724
3725         if (!(state->success)) {
3726                 return 1;
3727         }
3728         DEBUG(10,("validate_cred: %s ok\n", keystr));
3729         return 0;
3730 }
3731
3732 static int validate_ul(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3733                        struct tdb_validation_status *state)
3734 {
3735         struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3736         int32 num_entries, i;
3737
3738         if (!centry) {
3739                 return 1;
3740         }
3741
3742         num_entries = (int32)centry_uint32(centry);
3743
3744         for (i=0; i< num_entries; i++) {
3745                 struct dom_sid sid;
3746                 (void)centry_string(centry, mem_ctx);
3747                 (void)centry_string(centry, mem_ctx);
3748                 (void)centry_string(centry, mem_ctx);
3749                 (void)centry_string(centry, mem_ctx);
3750                 (void)centry_sid(centry, &sid);
3751                 (void)centry_sid(centry, &sid);
3752         }
3753
3754         centry_free(centry);
3755
3756         if (!(state->success)) {
3757                 return 1;
3758         }
3759         DEBUG(10,("validate_ul: %s ok\n", keystr));
3760         return 0;
3761 }
3762
3763 static int validate_gl(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3764                        struct tdb_validation_status *state)
3765 {
3766         struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3767         int32 num_entries, i;
3768
3769         if (!centry) {
3770                 return 1;
3771         }
3772
3773         num_entries = centry_uint32(centry);
3774
3775         for (i=0; i< num_entries; i++) {
3776                 (void)centry_string(centry, mem_ctx);
3777                 (void)centry_string(centry, mem_ctx);
3778                 (void)centry_uint32(centry);
3779         }
3780
3781         centry_free(centry);
3782
3783         if (!(state->success)) {
3784                 return 1;
3785         }
3786         DEBUG(10,("validate_gl: %s ok\n", keystr));
3787         return 0;
3788 }
3789
3790 static int validate_ug(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3791                        struct tdb_validation_status *state)
3792 {
3793         struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3794         int32 num_groups, i;
3795
3796         if (!centry) {
3797                 return 1;
3798         }
3799
3800         num_groups = centry_uint32(centry);
3801
3802         for (i=0; i< num_groups; i++) {
3803                 struct dom_sid sid;
3804                 centry_sid(centry, &sid);
3805         }
3806
3807         centry_free(centry);
3808
3809         if (!(state->success)) {
3810                 return 1;
3811         }
3812         DEBUG(10,("validate_ug: %s ok\n", keystr));
3813         return 0;
3814 }
3815
3816 static int validate_ua(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3817                        struct tdb_validation_status *state)
3818 {
3819         struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3820         int32 num_aliases, i;
3821
3822         if (!centry) {
3823                 return 1;
3824         }
3825
3826         num_aliases = centry_uint32(centry);
3827
3828         for (i=0; i < num_aliases; i++) {
3829                 (void)centry_uint32(centry);
3830         }
3831
3832         centry_free(centry);
3833
3834         if (!(state->success)) {
3835                 return 1;
3836         }
3837         DEBUG(10,("validate_ua: %s ok\n", keystr));
3838         return 0;
3839 }
3840
3841 static int validate_gm(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3842                        struct tdb_validation_status *state)
3843 {
3844         struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3845         int32 num_names, i;
3846
3847         if (!centry) {
3848                 return 1;
3849         }
3850
3851         num_names = centry_uint32(centry);
3852
3853         for (i=0; i< num_names; i++) {
3854                 struct dom_sid sid;
3855                 centry_sid(centry, &sid);
3856                 (void)centry_string(centry, mem_ctx);
3857                 (void)centry_uint32(centry);
3858         }
3859
3860         centry_free(centry);
3861
3862         if (!(state->success)) {
3863                 return 1;
3864         }
3865         DEBUG(10,("validate_gm: %s ok\n", keystr));
3866         return 0;
3867 }
3868
3869 static int validate_dr(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3870                        struct tdb_validation_status *state)
3871 {
3872         /* Can't say anything about this other than must be nonzero. */
3873         if (dbuf.dsize == 0) {
3874                 DEBUG(0,("validate_dr: Corrupt cache for key %s (len == 0) ?\n",
3875                                 keystr));
3876                 state->bad_entry = true;
3877                 state->success = false;
3878                 return 1;
3879         }
3880
3881         DEBUG(10,("validate_dr: %s ok\n", keystr));
3882         return 0;
3883 }
3884
3885 static int validate_de(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3886                        struct tdb_validation_status *state)
3887 {
3888         /* Can't say anything about this other than must be nonzero. */
3889         if (dbuf.dsize == 0) {
3890                 DEBUG(0,("validate_de: Corrupt cache for key %s (len == 0) ?\n",
3891                                 keystr));
3892                 state->bad_entry = true;
3893                 state->success = false;
3894                 return 1;
3895         }
3896
3897         DEBUG(10,("validate_de: %s ok\n", keystr));
3898         return 0;
3899 }
3900
3901 static int validate_pwinfo(TALLOC_CTX *mem_ctx, const char *keystr,
3902                            TDB_DATA dbuf, struct tdb_validation_status *state)
3903 {
3904         struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3905
3906         if (!centry) {
3907                 return 1;
3908         }
3909
3910         (void)centry_string(centry, mem_ctx);
3911         (void)centry_string(centry, mem_ctx);
3912         (void)centry_string(centry, mem_ctx);
3913         (void)centry_uint32(centry);
3914
3915         centry_free(centry);
3916
3917         if (!(state->success)) {
3918                 return 1;
3919         }
3920         DEBUG(10,("validate_pwinfo: %s ok\n", keystr));
3921         return 0;
3922 }
3923
3924 static int validate_nss_an(TALLOC_CTX *mem_ctx, const char *keystr,
3925                            TDB_DATA dbuf,
3926                            struct tdb_validation_status *state)
3927 {
3928         struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3929
3930         if (!centry) {
3931                 return 1;
3932         }
3933
3934         (void)centry_string( centry, mem_ctx );
3935
3936         centry_free(centry);
3937
3938         if (!(state->success)) {
3939                 return 1;
3940         }
3941         DEBUG(10,("validate_pwinfo: %s ok\n", keystr));
3942         return 0;
3943 }
3944
3945 static int validate_nss_na(TALLOC_CTX *mem_ctx, const char *keystr,
3946                            TDB_DATA dbuf,
3947                            struct tdb_validation_status *state)
3948 {
3949         struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3950
3951         if (!centry) {
3952                 return 1;
3953         }
3954
3955         (void)centry_string( centry, mem_ctx );
3956
3957         centry_free(centry);
3958
3959         if (!(state->success)) {
3960                 return 1;
3961         }
3962         DEBUG(10,("validate_pwinfo: %s ok\n", keystr));
3963         return 0;
3964 }
3965
3966 static int validate_trustdomcache(TALLOC_CTX *mem_ctx, const char *keystr, 
3967                                   TDB_DATA dbuf,
3968                                   struct tdb_validation_status *state)
3969 {
3970         if (dbuf.dsize == 0) {
3971                 DEBUG(0, ("validate_trustdomcache: Corrupt cache for "
3972                           "key %s (len ==0) ?\n", keystr));
3973                 state->bad_entry = true;
3974                 state->success = false;
3975                 return 1;
3976         }
3977
3978         DEBUG(10,    ("validate_trustdomcache: %s ok\n", keystr));
3979         DEBUGADD(10, ("  Don't trust me, I am a DUMMY!\n"));
3980         return 0;
3981 }
3982
3983 static int validate_offline(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3984                             struct tdb_validation_status *state)
3985 {
3986         if (dbuf.dsize != 4) {
3987                 DEBUG(0,("validate_offline: Corrupt cache for key %s (len %u != 4) ?\n",
3988                                 keystr, (unsigned int)dbuf.dsize ));
3989                 state->bad_entry = true;
3990                 state->success = false;
3991                 return 1;
3992         }
3993         DEBUG(10,("validate_offline: %s ok\n", keystr));
3994         return 0;
3995 }
3996
3997 static int validate_ndr(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3998                         struct tdb_validation_status *state)
3999 {
4000         /*
4001          * Ignore validation for now. The proper way to do this is with a
4002          * checksum. Just pure parsing does not really catch much.
4003          */
4004         return 0;
4005 }
4006
4007 static int validate_cache_version(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
4008                                   struct tdb_validation_status *state)
4009 {
4010         if (dbuf.dsize != 4) {
4011                 DEBUG(0, ("validate_cache_version: Corrupt cache for "
4012                           "key %s (len %u != 4) ?\n", 
4013                           keystr, (unsigned int)dbuf.dsize));
4014                 state->bad_entry = true;
4015                 state->success = false;
4016                 return 1;
4017         }
4018
4019         DEBUG(10, ("validate_cache_version: %s ok\n", keystr));
4020         return 0;
4021 }
4022
4023 /***********************************************************************
4024  A list of all possible cache tdb keys with associated validation
4025  functions.
4026 ***********************************************************************/
4027
4028 struct key_val_struct {
4029         const char *keyname;
4030         int (*validate_data_fn)(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf, struct tdb_validation_status* state);
4031 } key_val[] = {
4032         {"SEQNUM/", validate_seqnum},
4033         {"NS/", validate_ns},
4034         {"SN/", validate_sn},
4035         {"U/", validate_u},
4036         {"LOC_POL/", validate_loc_pol},
4037         {"PWD_POL/", validate_pwd_pol},
4038         {"CRED/", validate_cred},
4039         {"UL/", validate_ul},
4040         {"GL/", validate_gl},
4041         {"UG/", validate_ug},
4042         {"UA", validate_ua},
4043         {"GM/", validate_gm},
4044         {"DR/", validate_dr},
4045         {"DE/", validate_de},
4046         {"NSS/PWINFO/", validate_pwinfo},
4047         {"TRUSTDOMCACHE/", validate_trustdomcache},
4048         {"NSS/NA/", validate_nss_na},
4049         {"NSS/AN/", validate_nss_an},
4050         {"WINBINDD_OFFLINE", validate_offline},
4051         {"NDR/", validate_ndr},
4052         {WINBINDD_CACHE_VERSION_KEYSTR, validate_cache_version},
4053         {NULL, NULL}
4054 };
4055
4056 /***********************************************************************
4057  Function to look at every entry in the tdb and validate it as far as
4058  possible.
4059 ***********************************************************************/
4060
4061 static int cache_traverse_validate_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf, void *state)
4062 {
4063         int i;
4064         unsigned int max_key_len = 1024;
4065         struct tdb_validation_status *v_state = (struct tdb_validation_status *)state;
4066
4067         /* Paranoia check. */
4068         if (strncmp("UA/", (const char *)kbuf.dptr, 3) == 0) {
4069                 max_key_len = 1024 * 1024;
4070         }
4071         if (kbuf.dsize > max_key_len) {
4072                 DEBUG(0, ("cache_traverse_validate_fn: key length too large: "
4073                           "(%u) > (%u)\n\n",
4074                           (unsigned int)kbuf.dsize, (unsigned int)max_key_len));
4075                 return 1;
4076         }
4077
4078         for (i = 0; key_val[i].keyname; i++) {
4079                 size_t namelen = strlen(key_val[i].keyname);
4080                 if (kbuf.dsize >= namelen && (
4081                                 strncmp(key_val[i].keyname, (const char *)kbuf.dptr, namelen)) == 0) {
4082                         TALLOC_CTX *mem_ctx;
4083                         char *keystr;
4084                         int ret;
4085
4086                         keystr = SMB_MALLOC_ARRAY(char, kbuf.dsize+1);
4087                         if (!keystr) {
4088                                 return 1;
4089                         }
4090                         memcpy(keystr, kbuf.dptr, kbuf.dsize);
4091                         keystr[kbuf.dsize] = '\0';
4092
4093                         mem_ctx = talloc_init("validate_ctx");
4094                         if (!mem_ctx) {
4095                                 SAFE_FREE(keystr);
4096                                 return 1;
4097                         }
4098
4099                         ret = key_val[i].validate_data_fn(mem_ctx, keystr, dbuf, 
4100                                                           v_state);
4101
4102                         SAFE_FREE(keystr);
4103                         talloc_destroy(mem_ctx);
4104                         return ret;
4105                 }
4106         }
4107
4108         DEBUG(0,("cache_traverse_validate_fn: unknown cache entry\nkey :\n"));
4109         dump_data(0, (uint8 *)kbuf.dptr, kbuf.dsize);
4110         DEBUG(0,("data :\n"));
4111         dump_data(0, (uint8 *)dbuf.dptr, dbuf.dsize);
4112         v_state->unknown_key = true;
4113         v_state->success = false;
4114         return 1; /* terminate. */
4115 }
4116
4117 static void validate_panic(const char *const why)
4118 {
4119         DEBUG(0,("validating cache: would panic %s\n", why ));
4120         DEBUGADD(0, ("exiting instead (cache validation mode)\n"));
4121         exit(47);
4122 }
4123
4124 static int wbcache_update_centry_fn(TDB_CONTEXT *tdb,
4125                                     TDB_DATA key,
4126                                     TDB_DATA data,
4127                                     void *state)
4128 {
4129         uint64_t ctimeout;
4130         TDB_DATA blob;
4131
4132         if (is_non_centry_key(key)) {
4133                 return 0;
4134         }
4135
4136         if (data.dptr == NULL || data.dsize == 0) {
4137                 if (tdb_delete(tdb, key) < 0) {
4138                         DEBUG(0, ("tdb_delete for [%s] failed!\n",
4139                                   key.dptr));
4140                         return 1;
4141                 }
4142         }
4143
4144         /* add timeout to blob (uint64_t) */
4145         blob.dsize = data.dsize + 8;
4146
4147         blob.dptr = SMB_XMALLOC_ARRAY(uint8_t, blob.dsize);
4148         if (blob.dptr == NULL) {
4149                 return 1;
4150         }
4151         memset(blob.dptr, 0, blob.dsize);
4152
4153         /* copy status and seqnum */
4154         memcpy(blob.dptr, data.dptr, 8);
4155
4156         /* add timeout */
4157         ctimeout = lp_winbind_cache_time() + time(NULL);
4158         SBVAL(blob.dptr, 8, ctimeout);
4159
4160         /* copy the rest */
4161         memcpy(blob.dptr + 16, data.dptr + 8, data.dsize - 8);
4162
4163         if (tdb_store(tdb, key, blob, TDB_REPLACE) < 0) {
4164                 DEBUG(0, ("tdb_store to update [%s] failed!\n",
4165                           key.dptr));
4166                 SAFE_FREE(blob.dptr);
4167                 return 1;
4168         }
4169
4170         SAFE_FREE(blob.dptr);
4171         return 0;
4172 }
4173
4174 static bool wbcache_upgrade_v1_to_v2(TDB_CONTEXT *tdb)
4175 {
4176         int rc;
4177
4178         DEBUG(1, ("Upgrade to version 2 of the winbindd_cache.tdb\n"));
4179
4180         rc = tdb_traverse(tdb, wbcache_update_centry_fn, NULL);
4181         if (rc < 0) {
4182                 return false;
4183         }
4184
4185         return true;
4186 }
4187
4188 /***********************************************************************
4189  Try and validate every entry in the winbindd cache. If we fail here,
4190  delete the cache tdb and return non-zero.
4191 ***********************************************************************/
4192
4193 int winbindd_validate_cache(void)
4194 {
4195         int ret = -1;
4196         const char *tdb_path = state_path("winbindd_cache.tdb");
4197         TDB_CONTEXT *tdb = NULL;
4198         uint32_t vers_id;
4199         bool ok;
4200
4201         DEBUG(10, ("winbindd_validate_cache: replacing panic function\n"));
4202         smb_panic_fn = validate_panic;
4203
4204         tdb = tdb_open_log(tdb_path, 
4205                            WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
4206                            TDB_INCOMPATIBLE_HASH |
4207                            ( lp_winbind_offline_logon() 
4208                              ? TDB_DEFAULT 
4209                              : TDB_DEFAULT | TDB_CLEAR_IF_FIRST ),
4210                            O_RDWR|O_CREAT, 
4211                            0600);
4212         if (!tdb) {
4213                 DEBUG(0, ("winbindd_validate_cache: "
4214                           "error opening/initializing tdb\n"));
4215                 goto done;
4216         }
4217
4218         /* Version check and upgrade code. */
4219         if (!tdb_fetch_uint32(tdb, WINBINDD_CACHE_VERSION_KEYSTR, &vers_id)) {
4220                 DEBUG(10, ("Fresh database\n"));
4221                 tdb_store_uint32(tdb, WINBINDD_CACHE_VERSION_KEYSTR, WINBINDD_CACHE_VERSION);
4222                 vers_id = WINBINDD_CACHE_VERSION;
4223         }
4224
4225         if (vers_id != WINBINDD_CACHE_VERSION) {
4226                 if (vers_id == WINBINDD_CACHE_VER1) {
4227                         ok = wbcache_upgrade_v1_to_v2(tdb);
4228                         if (!ok) {
4229                                 DEBUG(10, ("winbindd_validate_cache: upgrade to version 2 failed.\n"));
4230                                 unlink(tdb_path);
4231                                 goto done;
4232                         }
4233
4234                         tdb_store_uint32(tdb,
4235                                          WINBINDD_CACHE_VERSION_KEYSTR,
4236                                          WINBINDD_CACHE_VERSION);
4237                         vers_id = WINBINDD_CACHE_VER2;
4238                 }
4239         }
4240
4241         tdb_close(tdb);
4242
4243         ret = tdb_validate_and_backup(tdb_path, cache_traverse_validate_fn);
4244
4245         if (ret != 0) {
4246                 DEBUG(10, ("winbindd_validate_cache: validation not successful.\n"));
4247                 DEBUGADD(10, ("removing tdb %s.\n", tdb_path));
4248                 unlink(tdb_path);
4249         }
4250
4251 done:
4252         DEBUG(10, ("winbindd_validate_cache: restoring panic function\n"));
4253         smb_panic_fn = smb_panic;
4254         return ret;
4255 }
4256
4257 /***********************************************************************
4258  Try and validate every entry in the winbindd cache.
4259 ***********************************************************************/
4260
4261 int winbindd_validate_cache_nobackup(void)
4262 {
4263         int ret = -1;
4264         const char *tdb_path = state_path("winbindd_cache.tdb");
4265
4266         DEBUG(10, ("winbindd_validate_cache: replacing panic function\n"));
4267         smb_panic_fn = validate_panic;
4268
4269
4270         if (wcache == NULL || wcache->tdb == NULL) {
4271                 ret = tdb_validate_open(tdb_path, cache_traverse_validate_fn);
4272         } else {
4273                 ret = tdb_validate(wcache->tdb, cache_traverse_validate_fn);
4274         }
4275
4276         if (ret != 0) {
4277                 DEBUG(10, ("winbindd_validate_cache_nobackup: validation not "
4278                            "successful.\n"));
4279         }
4280
4281         DEBUG(10, ("winbindd_validate_cache_nobackup: restoring panic "
4282                    "function\n"));
4283         smb_panic_fn = smb_panic;
4284         return ret;
4285 }
4286
4287 bool winbindd_cache_validate_and_initialize(void)
4288 {
4289         close_winbindd_cache();
4290
4291         if (lp_winbind_offline_logon()) {
4292                 if (winbindd_validate_cache() < 0) {
4293                         DEBUG(0, ("winbindd cache tdb corrupt and no backup "
4294                                   "could be restored.\n"));
4295                 }
4296         }
4297
4298         return initialize_winbindd_cache();
4299 }
4300
4301 /*********************************************************************
4302  ********************************************************************/
4303
4304 static bool add_wbdomain_to_tdc_array( struct winbindd_domain *new_dom,
4305                                        struct winbindd_tdc_domain **domains, 
4306                                        size_t *num_domains )
4307 {
4308         struct winbindd_tdc_domain *list = NULL;
4309         size_t idx;
4310         int i;
4311         bool set_only = false;
4312
4313         /* don't allow duplicates */
4314
4315         idx = *num_domains;
4316         list = *domains;
4317
4318         for ( i=0; i< (*num_domains); i++ ) {
4319                 if ( strequal( new_dom->name, list[i].domain_name ) ) {
4320                         DEBUG(10,("add_wbdomain_to_tdc_array: Found existing record for %s\n",
4321                                   new_dom->name));
4322                         idx = i;
4323                         set_only = true;
4324
4325                         break;
4326                 }
4327         }
4328
4329         if ( !set_only ) {
4330                 if ( !*domains ) {
4331                         list = talloc_array( NULL, struct winbindd_tdc_domain, 1 );
4332                         idx = 0;
4333                 } else {
4334                         list = talloc_realloc( *domains, *domains, 
4335                                                      struct winbindd_tdc_domain,  
4336                                                      (*num_domains)+1);
4337                         idx = *num_domains;             
4338                 }
4339
4340                 ZERO_STRUCT( list[idx] );
4341         }
4342
4343         if ( !list )
4344                 return false;
4345
4346         list[idx].domain_name = talloc_strdup(list, new_dom->name);
4347         if (list[idx].domain_name == NULL) {
4348                 return false;
4349         }
4350         if (new_dom->alt_name != NULL) {
4351                 list[idx].dns_name = talloc_strdup(list, new_dom->alt_name);
4352                 if (list[idx].dns_name == NULL) {
4353                         return false;
4354                 }
4355         }
4356
4357         if ( !is_null_sid( &new_dom->sid ) ) {
4358                 sid_copy( &list[idx].sid, &new_dom->sid );
4359         } else {
4360                 sid_copy(&list[idx].sid, &global_sid_NULL);
4361         }
4362
4363         if ( new_dom->domain_flags != 0x0 )
4364                 list[idx].trust_flags = new_dom->domain_flags;  
4365
4366         if ( new_dom->domain_type != 0x0 )
4367                 list[idx].trust_type = new_dom->domain_type;
4368
4369         if ( new_dom->domain_trust_attribs != 0x0 )
4370                 list[idx].trust_attribs = new_dom->domain_trust_attribs;
4371
4372         if ( !set_only ) {
4373                 *domains = list;
4374                 *num_domains = idx + 1; 
4375         }
4376
4377         return true;
4378 }
4379
4380 /*********************************************************************
4381  ********************************************************************/
4382
4383 static TDB_DATA make_tdc_key( const char *domain_name )
4384 {
4385         char *keystr = NULL;
4386         TDB_DATA key = { NULL, 0 };
4387
4388         if ( !domain_name ) {
4389                 DEBUG(5,("make_tdc_key: Keyname workgroup is NULL!\n"));
4390                 return key;
4391         }
4392
4393         if (asprintf( &keystr, "TRUSTDOMCACHE/%s", domain_name ) == -1) {
4394                 return key;
4395         }
4396         key = string_term_tdb_data(keystr);
4397
4398         return key;     
4399 }
4400
4401 /*********************************************************************
4402  ********************************************************************/
4403
4404 static int pack_tdc_domains( struct winbindd_tdc_domain *domains, 
4405                              size_t num_domains,
4406                              unsigned char **buf )
4407 {
4408         unsigned char *buffer = NULL;
4409         int len = 0;
4410         int buflen = 0;
4411         int i = 0;
4412
4413         DEBUG(10,("pack_tdc_domains: Packing %d trusted domains\n",
4414                   (int)num_domains));
4415
4416         buflen = 0;
4417
4418  again: 
4419         len = 0;
4420
4421         /* Store the number of array items first */
4422         len += tdb_pack( buffer+len, buflen-len, "d", 
4423                          num_domains );
4424
4425         /* now pack each domain trust record */
4426         for ( i=0; i<num_domains; i++ ) {
4427
4428                 fstring tmp;
4429
4430                 if ( buflen > 0 ) {
4431                         DEBUG(10,("pack_tdc_domains: Packing domain %s (%s)\n",
4432                                   domains[i].domain_name,
4433                                   domains[i].dns_name ? domains[i].dns_name : "UNKNOWN" ));
4434                 }
4435
4436                 len += tdb_pack( buffer+len, buflen-len, "fffddd",
4437                                  domains[i].domain_name,
4438                                  domains[i].dns_name ? domains[i].dns_name : "",
4439                                  sid_to_fstring(tmp, &domains[i].sid),
4440                                  domains[i].trust_flags,
4441                                  domains[i].trust_attribs,
4442                                  domains[i].trust_type );
4443         }
4444
4445         if ( buflen < len ) {
4446                 SAFE_FREE(buffer);
4447                 if ( (buffer = SMB_MALLOC_ARRAY(unsigned char, len)) == NULL ) {
4448                         DEBUG(0,("pack_tdc_domains: failed to alloc buffer!\n"));
4449                         buflen = -1;
4450                         goto done;
4451                 }
4452                 buflen = len;
4453                 goto again;
4454         }
4455
4456         *buf = buffer;  
4457
4458  done:  
4459         return buflen;  
4460 }
4461
4462 /*********************************************************************
4463  ********************************************************************/
4464
4465 static size_t unpack_tdc_domains( unsigned char *buf, int buflen, 
4466                                   struct winbindd_tdc_domain **domains )
4467 {
4468         fstring domain_name, dns_name, sid_string;      
4469         uint32 type, attribs, flags;
4470         int num_domains;
4471         int len = 0;
4472         int i;
4473         struct winbindd_tdc_domain *list = NULL;
4474
4475         /* get the number of domains */
4476         len += tdb_unpack( buf+len, buflen-len, "d", &num_domains);
4477         if ( len == -1 ) {
4478                 DEBUG(5,("unpack_tdc_domains: Failed to unpack domain array\n"));               
4479                 return 0;
4480         }
4481
4482         list = talloc_array( NULL, struct winbindd_tdc_domain, num_domains );
4483         if ( !list ) {
4484                 DEBUG(0,("unpack_tdc_domains: Failed to talloc() domain list!\n"));
4485                 return 0;               
4486         }
4487
4488         for ( i=0; i<num_domains; i++ ) {
4489                 int this_len;
4490
4491                 this_len = tdb_unpack( buf+len, buflen-len, "fffddd",
4492                                    domain_name,
4493                                    dns_name,
4494                                    sid_string,
4495                                    &flags,
4496                                    &attribs,
4497                                    &type );
4498
4499                 if ( this_len == -1 ) {
4500                         DEBUG(5,("unpack_tdc_domains: Failed to unpack domain array\n"));
4501                         TALLOC_FREE( list );                    
4502                         return 0;
4503                 }
4504                 len += this_len;
4505
4506                 DEBUG(11,("unpack_tdc_domains: Unpacking domain %s (%s) "
4507                           "SID %s, flags = 0x%x, attribs = 0x%x, type = 0x%x\n",
4508                           domain_name, dns_name, sid_string,
4509                           flags, attribs, type));
4510
4511                 list[i].domain_name = talloc_strdup( list, domain_name );
4512                 list[i].dns_name = NULL;
4513                 if (dns_name[0] != '\0') {
4514                         list[i].dns_name = talloc_strdup(list, dns_name);
4515                 }
4516                 if ( !string_to_sid( &(list[i].sid), sid_string ) ) {                   
4517                         DEBUG(10,("unpack_tdc_domains: no SID for domain %s\n",
4518                                   domain_name));
4519                 }
4520                 list[i].trust_flags = flags;
4521                 list[i].trust_attribs = attribs;
4522                 list[i].trust_type = type;
4523         }
4524
4525         *domains = list;
4526
4527         return num_domains;
4528 }
4529
4530 /*********************************************************************
4531  ********************************************************************/
4532
4533 static bool wcache_tdc_store_list( struct winbindd_tdc_domain *domains, size_t num_domains )
4534 {
4535         TDB_DATA key = make_tdc_key( lp_workgroup() );   
4536         TDB_DATA data = { NULL, 0 };
4537         int ret;
4538
4539         if ( !key.dptr )
4540                 return false;
4541
4542         /* See if we were asked to delete the cache entry */
4543
4544         if ( !domains ) {
4545                 ret = tdb_delete( wcache->tdb, key );
4546                 goto done;
4547         }
4548
4549         data.dsize = pack_tdc_domains( domains, num_domains, &data.dptr );
4550
4551         if ( !data.dptr ) {
4552                 ret = -1;
4553                 goto done;
4554         }
4555
4556         ret = tdb_store( wcache->tdb, key, data, 0 );
4557
4558  done:
4559         SAFE_FREE( data.dptr );
4560         SAFE_FREE( key.dptr );
4561
4562         return ( ret == 0 );
4563 }
4564
4565 /*********************************************************************
4566  ********************************************************************/
4567
4568 bool wcache_tdc_fetch_list( struct winbindd_tdc_domain **domains, size_t *num_domains )
4569 {
4570         TDB_DATA key = make_tdc_key( lp_workgroup() );
4571         TDB_DATA data = { NULL, 0 };
4572
4573         *domains = NULL;        
4574         *num_domains = 0;       
4575
4576         if ( !key.dptr )
4577                 return false;
4578
4579         data = tdb_fetch_compat( wcache->tdb, key );
4580
4581         SAFE_FREE( key.dptr );
4582
4583         if ( !data.dptr ) 
4584                 return false;
4585
4586         *num_domains = unpack_tdc_domains( data.dptr, data.dsize, domains );
4587
4588         SAFE_FREE( data.dptr );
4589
4590         if ( !*domains )
4591                 return false;
4592
4593         return true;
4594 }
4595
4596 /*********************************************************************
4597  ********************************************************************/
4598
4599 bool wcache_tdc_add_domain( struct winbindd_domain *domain )
4600 {
4601         struct winbindd_tdc_domain *dom_list = NULL;
4602         size_t num_domains = 0;
4603         bool ret = false;
4604
4605         DEBUG(10,("wcache_tdc_add_domain: Adding domain %s (%s), SID %s, "
4606                   "flags = 0x%x, attributes = 0x%x, type = 0x%x\n",
4607                   domain->name, domain->alt_name, 
4608                   sid_string_dbg(&domain->sid),
4609                   domain->domain_flags,
4610                   domain->domain_trust_attribs,
4611                   domain->domain_type));        
4612
4613         if ( !init_wcache() ) {
4614                 return false;
4615         }
4616
4617         /* fetch the list */
4618
4619         wcache_tdc_fetch_list( &dom_list, &num_domains );
4620
4621         /* add the new domain */
4622
4623         if ( !add_wbdomain_to_tdc_array( domain, &dom_list, &num_domains ) ) {
4624                 goto done;              
4625         }       
4626
4627         /* pack the domain */
4628
4629         if ( !wcache_tdc_store_list( dom_list, num_domains ) ) {
4630                 goto done;              
4631         }
4632
4633         /* Success */
4634
4635         ret = true;
4636  done:
4637         TALLOC_FREE( dom_list );
4638
4639         return ret;     
4640 }
4641
4642 static struct winbindd_tdc_domain *wcache_tdc_dup_domain(
4643         TALLOC_CTX *mem_ctx, const struct winbindd_tdc_domain *src)
4644 {
4645         struct winbindd_tdc_domain *dst;
4646
4647         dst = talloc(mem_ctx, struct winbindd_tdc_domain);
4648         if (dst == NULL) {
4649                 goto fail;
4650         }
4651         dst->domain_name = talloc_strdup(dst, src->domain_name);
4652         if (dst->domain_name == NULL) {
4653                 goto fail;
4654         }
4655
4656         dst->dns_name = NULL;
4657         if (src->dns_name != NULL) {
4658                 dst->dns_name = talloc_strdup(dst, src->dns_name);
4659                 if (dst->dns_name == NULL) {
4660                         goto fail;
4661                 }
4662         }
4663
4664         sid_copy(&dst->sid, &src->sid);
4665         dst->trust_flags = src->trust_flags;
4666         dst->trust_type = src->trust_type;
4667         dst->trust_attribs = src->trust_attribs;
4668         return dst;
4669 fail:
4670         TALLOC_FREE(dst);
4671         return NULL;
4672 }
4673
4674 /*********************************************************************
4675  ********************************************************************/
4676
4677 struct winbindd_tdc_domain * wcache_tdc_fetch_domain( TALLOC_CTX *ctx, const char *name )
4678 {
4679         struct winbindd_tdc_domain *dom_list = NULL;
4680         size_t num_domains = 0;
4681         int i;
4682         struct winbindd_tdc_domain *d = NULL;   
4683
4684         DEBUG(10,("wcache_tdc_fetch_domain: Searching for domain %s\n", name));
4685
4686         if ( !init_wcache() ) {
4687                 return NULL;
4688         }
4689
4690         /* fetch the list */
4691
4692         wcache_tdc_fetch_list( &dom_list, &num_domains );
4693
4694         for ( i=0; i<num_domains; i++ ) {
4695                 if ( strequal(name, dom_list[i].domain_name) ||
4696                      strequal(name, dom_list[i].dns_name) )
4697                 {
4698                         DEBUG(10,("wcache_tdc_fetch_domain: Found domain %s\n",
4699                                   name));
4700
4701                         d = wcache_tdc_dup_domain(ctx, &dom_list[i]);
4702                         break;
4703                 }
4704         }
4705
4706         TALLOC_FREE( dom_list );
4707
4708         return d;       
4709 }
4710
4711 /*********************************************************************
4712  ********************************************************************/
4713
4714 struct winbindd_tdc_domain*
4715         wcache_tdc_fetch_domainbysid(TALLOC_CTX *ctx,
4716                                      const struct dom_sid *sid)
4717 {
4718         struct winbindd_tdc_domain *dom_list = NULL;
4719         size_t num_domains = 0;
4720         int i;
4721         struct winbindd_tdc_domain *d = NULL;
4722
4723         DEBUG(10,("wcache_tdc_fetch_domainbysid: Searching for domain %s\n",
4724                   sid_string_dbg(sid)));
4725
4726         if (!init_wcache()) {
4727                 return NULL;
4728         }
4729
4730         /* fetch the list */
4731
4732         wcache_tdc_fetch_list(&dom_list, &num_domains);
4733
4734         for (i = 0; i<num_domains; i++) {
4735                 if (dom_sid_equal(sid, &(dom_list[i].sid))) {
4736                         DEBUG(10, ("wcache_tdc_fetch_domainbysid: "
4737                                    "Found domain %s for SID %s\n",
4738                                    dom_list[i].domain_name,
4739                                    sid_string_dbg(sid)));
4740
4741                         d = wcache_tdc_dup_domain(ctx, &dom_list[i]);
4742                         break;
4743                 }
4744         }
4745
4746         TALLOC_FREE(dom_list);
4747
4748         return d;
4749 }
4750
4751
4752 /*********************************************************************
4753  ********************************************************************/
4754
4755 void wcache_tdc_clear( void )
4756 {
4757         if ( !init_wcache() )
4758                 return;
4759
4760         wcache_tdc_store_list( NULL, 0 );
4761
4762         return; 
4763 }
4764
4765
4766 /*********************************************************************
4767  ********************************************************************/
4768
4769 static void wcache_save_user_pwinfo(struct winbindd_domain *domain, 
4770                                     NTSTATUS status,
4771                                     const struct dom_sid *user_sid,
4772                                     const char *homedir,
4773                                     const char *shell,
4774                                     const char *gecos,
4775                                     uint32 gid)
4776 {
4777         struct cache_entry *centry;
4778         fstring tmp;
4779
4780         if ( (centry = centry_start(domain, status)) == NULL )
4781                 return;
4782
4783         centry_put_string( centry, homedir );
4784         centry_put_string( centry, shell );
4785         centry_put_string( centry, gecos );
4786         centry_put_uint32( centry, gid );
4787
4788         centry_end(centry, "NSS/PWINFO/%s", sid_to_fstring(tmp, user_sid) );
4789
4790         DEBUG(10,("wcache_save_user_pwinfo: %s\n", sid_string_dbg(user_sid) ));
4791
4792         centry_free(centry);
4793 }
4794
4795 #ifdef HAVE_ADS
4796
4797 NTSTATUS nss_get_info_cached( struct winbindd_domain *domain, 
4798                               const struct dom_sid *user_sid,
4799                               TALLOC_CTX *ctx,
4800                               const char **homedir, const char **shell,
4801                               const char **gecos, gid_t *p_gid)
4802 {
4803         struct winbind_cache *cache = get_cache(domain);
4804         struct cache_entry *centry = NULL;
4805         NTSTATUS nt_status;
4806         fstring tmp;
4807
4808         if (!cache->tdb)
4809                 goto do_query;
4810
4811         centry = wcache_fetch(cache, domain, "NSS/PWINFO/%s",
4812                               sid_to_fstring(tmp, user_sid));
4813
4814         if (!centry)
4815                 goto do_query;
4816
4817         *homedir = centry_string( centry, ctx );
4818         *shell   = centry_string( centry, ctx );
4819         *gecos   = centry_string( centry, ctx );
4820         *p_gid   = centry_uint32( centry );     
4821
4822         centry_free(centry);
4823
4824         DEBUG(10,("nss_get_info_cached: [Cached] - user_sid %s\n",
4825                   sid_string_dbg(user_sid)));
4826
4827         return NT_STATUS_OK;
4828
4829 do_query:
4830
4831         nt_status = nss_get_info( domain->name, user_sid, ctx,
4832                                   homedir, shell, gecos, p_gid );
4833
4834         DEBUG(10, ("nss_get_info returned %s\n", nt_errstr(nt_status)));
4835
4836         if ( NT_STATUS_IS_OK(nt_status) ) {
4837                 DEBUG(10, ("result:\n\thomedir = '%s'\n", *homedir));
4838                 DEBUGADD(10, ("\tshell = '%s'\n", *shell));
4839                 DEBUGADD(10, ("\tgecos = '%s'\n", *gecos));
4840                 DEBUGADD(10, ("\tgid = '%u'\n", (unsigned int)*p_gid));
4841
4842                 wcache_save_user_pwinfo( domain, nt_status, user_sid,
4843                                          *homedir, *shell, *gecos, *p_gid );
4844         }       
4845
4846         if ( NT_STATUS_EQUAL( nt_status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND ) ) {
4847                 DEBUG(5,("nss_get_info_cached: Setting domain %s offline\n",
4848                          domain->name ));
4849                 set_domain_offline( domain );
4850         }
4851
4852         return nt_status;       
4853 }
4854
4855 #endif
4856
4857 /* the cache backend methods are exposed via this structure */
4858 struct winbindd_methods cache_methods = {
4859         true,
4860         query_user_list,
4861         enum_dom_groups,
4862         enum_local_groups,
4863         name_to_sid,
4864         sid_to_name,
4865         rids_to_names,
4866         query_user,
4867         lookup_usergroups,
4868         lookup_useraliases,
4869         lookup_groupmem,
4870         sequence_number,
4871         lockout_policy,
4872         password_policy,
4873         trusted_domains
4874 };
4875
4876 static bool wcache_ndr_key(TALLOC_CTX *mem_ctx, const char *domain_name,
4877                            uint32_t opnum, const DATA_BLOB *req,
4878                            TDB_DATA *pkey)
4879 {
4880         char *key;
4881         size_t keylen;
4882
4883         key = talloc_asprintf(mem_ctx, "NDR/%s/%d/", domain_name, (int)opnum);
4884         if (key == NULL) {
4885                 return false;
4886         }
4887         keylen = talloc_get_size(key) - 1;
4888
4889         key = talloc_realloc(mem_ctx, key, char, keylen + req->length);
4890         if (key == NULL) {
4891                 return false;
4892         }
4893         memcpy(key + keylen, req->data, req->length);
4894
4895         pkey->dptr = (uint8_t *)key;
4896         pkey->dsize = talloc_get_size(key);
4897         return true;
4898 }
4899
4900 static bool wcache_opnum_cacheable(uint32_t opnum)
4901 {
4902         switch (opnum) {
4903         case NDR_WBINT_PING:
4904         case NDR_WBINT_QUERYSEQUENCENUMBER:
4905         case NDR_WBINT_ALLOCATEUID:
4906         case NDR_WBINT_ALLOCATEGID:
4907         case NDR_WBINT_CHECKMACHINEACCOUNT:
4908         case NDR_WBINT_CHANGEMACHINEACCOUNT:
4909         case NDR_WBINT_PINGDC:
4910                 return false;
4911         }
4912         return true;
4913 }
4914
4915 bool wcache_fetch_ndr(TALLOC_CTX *mem_ctx, struct winbindd_domain *domain,
4916                       uint32_t opnum, const DATA_BLOB *req, DATA_BLOB *resp)
4917 {
4918         TDB_DATA key, data;
4919         bool ret = false;
4920
4921         if (!wcache_opnum_cacheable(opnum) ||
4922             is_my_own_sam_domain(domain) ||
4923             is_builtin_domain(domain)) {
4924                 return false;
4925         }
4926
4927         if (wcache->tdb == NULL) {
4928                 return false;
4929         }
4930
4931         if (!wcache_ndr_key(talloc_tos(), domain->name, opnum, req, &key)) {
4932                 return false;
4933         }
4934         data = tdb_fetch_compat(wcache->tdb, key);
4935         TALLOC_FREE(key.dptr);
4936
4937         if (data.dptr == NULL) {
4938                 return false;
4939         }
4940         if (data.dsize < 12) {
4941                 goto fail;
4942         }
4943
4944         if (!is_domain_offline(domain)) {
4945                 uint32_t entry_seqnum, dom_seqnum, last_check;
4946                 uint64_t entry_timeout;
4947
4948                 if (!wcache_fetch_seqnum(domain->name, &dom_seqnum,
4949                                          &last_check)) {
4950                         goto fail;
4951                 }
4952                 entry_seqnum = IVAL(data.dptr, 0);
4953                 if (entry_seqnum != dom_seqnum) {
4954                         DEBUG(10, ("Entry has wrong sequence number: %d\n",
4955                                    (int)entry_seqnum));
4956                         goto fail;
4957                 }
4958                 entry_timeout = BVAL(data.dptr, 4);
4959                 if (time(NULL) > entry_timeout) {
4960                         DEBUG(10, ("Entry has timed out\n"));
4961                         goto fail;
4962                 }
4963         }
4964
4965         resp->data = (uint8_t *)talloc_memdup(mem_ctx, data.dptr + 12,
4966                                               data.dsize - 12);
4967         if (resp->data == NULL) {
4968                 DEBUG(10, ("talloc failed\n"));
4969                 goto fail;
4970         }
4971         resp->length = data.dsize - 12;
4972
4973         ret = true;
4974 fail:
4975         SAFE_FREE(data.dptr);
4976         return ret;
4977 }
4978
4979 void wcache_store_ndr(struct winbindd_domain *domain, uint32_t opnum,
4980                       const DATA_BLOB *req, const DATA_BLOB *resp)
4981 {
4982         TDB_DATA key, data;
4983         uint32_t dom_seqnum, last_check;
4984         uint64_t timeout;
4985
4986         if (!wcache_opnum_cacheable(opnum) ||
4987             is_my_own_sam_domain(domain) ||
4988             is_builtin_domain(domain)) {
4989                 return;
4990         }
4991
4992         if (wcache->tdb == NULL) {
4993                 return;
4994         }
4995
4996         if (!wcache_fetch_seqnum(domain->name, &dom_seqnum, &last_check)) {
4997                 DEBUG(10, ("could not fetch seqnum for domain %s\n",
4998                            domain->name));
4999                 return;
5000         }
5001
5002         if (!wcache_ndr_key(talloc_tos(), domain->name, opnum, req, &key)) {
5003                 return;
5004         }
5005
5006         timeout = time(NULL) + lp_winbind_cache_time();
5007
5008         data.dsize = resp->length + 12;
5009         data.dptr = talloc_array(key.dptr, uint8_t, data.dsize);
5010         if (data.dptr == NULL) {
5011                 goto done;
5012         }
5013
5014         SIVAL(data.dptr, 0, dom_seqnum);
5015         SBVAL(data.dptr, 4, timeout);
5016         memcpy(data.dptr + 12, resp->data, resp->length);
5017
5018         tdb_store(wcache->tdb, key, data, 0);
5019
5020 done:
5021         TALLOC_FREE(key.dptr);
5022         return;
5023 }